DecompileContext CreateDecompileContext(IShowContext ctx) { var decompileContext = new DecompileContext(); var decompilationContext = new DecompilationContext(); decompilationContext.GetDisableAssemblyLoad = () => decompileFileTabContentFactory.FileManager.DisableAssemblyLoad(); decompilationContext.IsBodyModified = m => decompileFileTabContentFactory.MethodAnnotations.IsBodyModified(m); var output = new AvalonEditTextOutput(); var dispatcher = Dispatcher.CurrentDispatcher; decompileContext.DecompileNodeContext = new DecompileNodeContext(decompilationContext, language, output, dispatcher); if (ctx.IsRefresh) { decompileContext.SavedRefPos = ((ITextEditorUIContext)ctx.UIContext).SaveReferencePosition(); if (decompileContext.SavedRefPos != null) { ctx.OnShown = e => { if (e.Success && !e.HasMovedCaret) { e.HasMovedCaret = ((ITextEditorUIContext)ctx.UIContext).RestoreReferencePosition(decompileContext.SavedRefPos); if (!e.HasMovedCaret) { ((ITextEditorUIContext)ctx.UIContext).ScrollAndMoveCaretTo(1, 1); e.HasMovedCaret = true; } } }; } } return(decompileContext); }
public static void ResolveFunction(DecompileContext ctx, ASTFunction func) { // Handle code-entry-specific types if (ctx.CodeAssetTypes.HasValue) { if (ctx.CodeAssetTypes.Value.FunctionArgs != null && ctx.CodeAssetTypes.Value.FunctionArgs.TryGetValue(func.Function.Name.Content, out AssetType[] types))
DecompileContext CreateDecompileContext(IShowContext ctx) { var decompileContext = new DecompileContext(); var decompilationContext = new DecompilationContext(); decompilationContext.CalculateBinSpans = true; decompilationContext.GetDisableAssemblyLoad = () => decompileDocumentTabContentFactory.DocumentService.DisableAssemblyLoad(); decompilationContext.IsBodyModified = m => decompileDocumentTabContentFactory.MethodAnnotations.IsBodyModified(m); var dispatcher = Dispatcher.CurrentDispatcher; decompileContext.DocumentViewerContentFactory = decompileDocumentTabContentFactory.DocumentViewerContentFactoryProvider.Create(); decompileContext.DecompileNodeContext = new DecompileNodeContext(decompilationContext, Decompiler, decompileContext.DocumentViewerContentFactory.Output, dispatcher); if (ctx.IsRefresh) { decompileContext.SavedRefPos = ((IDocumentViewer)ctx.UIContext).SaveReferencePosition(); if (decompileContext.SavedRefPos != null) { ctx.OnShown = e => { if (e.Success && !e.HasMovedCaret) { e.HasMovedCaret = ((IDocumentViewer)ctx.UIContext).RestoreReferencePosition(decompileContext.SavedRefPos); if (!e.HasMovedCaret) { ((IDocumentViewer)ctx.UIContext).MoveCaretToPosition(0); e.HasMovedCaret = true; } } }; } } return(decompileContext); }
// Processes "isstaticok" jumps (by removing them), and marks the block public static void ProcessStatic(DecompileContext ctx) { foreach (Block b in ctx.Blocks.List) { if (b.Instructions.Count >= 2) { if (b.Instructions[^ 1].Kind == Instruction.Opcode.Bt)
public string Decompile() { StringBuilder sb = new StringBuilder(); StringWriter sw = new StringWriter(sb); DecompileContext context = new DecompileContext(sw, this.ShaderIR); context.Write(); return(sb.ToString()); }
public BlockStatement Process(DecompileContext context, BlockStatement statement) { foreach (var b in context.Blocks) { ExpressionPropagation(b); } return(statement); }
public bool Evaluate(DecompileContext ctx, ASTNode node, ASTNode parent) { foreach (var cond in Conditions) { if (cond.Evaluate(ctx, node, parent)) { return(true); } } return(false); }
public bool Evaluate(DecompileContext ctx, ASTNode node, ASTNode parent) { if (node.Kind != NodeKind) { return(true); } if (node.ToString() != NodeValue) { return(true); } return(false); }
public bool Evaluate(DecompileContext ctx, ASTNode node, ASTNode parent) { if (node.Kind == ASTNode.StatementKind.Int16) { if ((node as ASTInt16).Value == 0) { return(false); } } // todo? other types like int32/64? return(true); }
DecompileContext CreateDecompileContext() { var decompileContext = new DecompileContext(); var decompilationContext = new DecompilationContext(); decompilationContext.GetDisableAssemblyLoad = () => decompileFileTabContentFactory.FileManager.DisableAssemblyLoad(); decompilationContext.IsBodyModified = m => decompileFileTabContentFactory.MethodAnnotations.IsBodyModified(m); var output = new AvalonEditTextOutput(); var dispatcher = Dispatcher.CurrentDispatcher; decompileContext.DecompileNodeContext = new DecompileNodeContext(decompilationContext, language, output, dispatcher); return(decompileContext); }
public void TestDecompileBlock() { var path = "..\\..\\Res\\Initialize.tjs.comp"; Module md = new Module(path); var mt = md.TopLevel.ResolveMethod(); mt.Compact(); DecompileContext context = new DecompileContext(md.TopLevel); context.ScanBlocks(mt.Instructions); context.ComputeDominators(); context.ComputeNaturalLoops(); context.FillInBlocks(mt.Instructions); var pass1 = new RegMemberPass(); var entry = pass1.Process(context, new BlockStatement()); var pass2 = new ExpressionPass(); entry = pass2.Process(context, entry); var b = context.Blocks[1]; var s1 = b.Statements.FirstOrDefault(); var pass3 = new ControlFlowPass(); entry = pass3.Process(context, entry); var c = entry.Statements.Count; foreach (var st in entry.Statements) { var s = st; } var pass4 = new StatementCollectPass(); entry = pass4.Process(context, entry); foreach (var statement in entry.Statements) { var s = statement; } var sWriter = new StringWriter(); TjsWriter writer = new TjsWriter(sWriter); writer.WriteBlock(entry); sWriter.Flush(); var result = sWriter.ToString(); }
/// Finds all the if statements within a list of blocks public static List <IfStatement> Find(DecompileContext ctx) { List <IfStatement> res = new List <IfStatement>(); // Reusable data structures for later List <Node> visited = new List <Node>(); Stack <Node> work = new Stack <Node>(); List <Node> otherVisited = new List <Node>(); foreach (Block b in ctx.Blocks.List) { if (b.Instructions.Count >= 1) { var instr = b.Instructions[^ 1];
DecompileContext CreateDecompileContext(string filename) { var decompileContext = new DecompileContext(); try { decompileContext.Writer = new StreamWriter(filename); decompileContext.Output = new TextWriterDecompilerOutput(decompileContext.Writer); return(decompileContext); } catch { decompileContext.Dispose(); throw; } }
public BlockStatement Process(DecompileContext context, BlockStatement statement) { Dictionary <Block, List <IAstNode> > blockStmts = new Dictionary <Block, List <IAstNode> >(); foreach (var block in context.Blocks) { var newStmts = new List <IAstNode>(); var loop = context.LoopSet.FirstOrDefault(l => l.Header == block); if (loop != null) { newStmts.Add(loop.LoopLogic.ToStatement()); block.Hidden = false; //TODO: temp fix } else { foreach (var node in block.Statements) { switch (node) { case GotoExpression _: case ConditionExpression _: break; case Expression expr: newStmts.Add(new ExpressionStatement(expr)); break; case Statement stmt: newStmts.Add(stmt); break; } } } blockStmts[block] = newStmts; } foreach (var block in context.Blocks) { if (block.Hidden) { continue; } statement.Statements.AddRange(blockStmts[block]); } return(statement); }
public BlockStatement Process(DecompileContext context, BlockStatement statement) { var entry = context.EntryBlock; if (context.Object.ContextType == TjsContextType.TopLevel) { This.HideInstance = true; ThisProxy.HideInstance = true; } else { This.HideInstance = false; ThisProxy.HideInstance = false; } //Add global var exps = new Dictionary <int, Expression> { { Const.ThisReg, This }, { Const.ThisProxyReg, ThisProxy }, }; //Add params var argCount = context.Object.FuncDeclArgCount; for (short i = 0; i < argCount; i++) { short slot = (short)(-i - 3); var v = new Variable(slot) { IsParameter = true }; context.Vars.Add(slot, v); exps.Add(slot, new LocalExpression(v)); } BlockProcess(context, entry, exps); //foreach (var variable in exps.Where(exp => exp.Value.Type == AstNodeType.LocalExpression).Select(exp => //{ // var l = (LocalExpression)exp.Value; // return new Variable(exp.Key) {VarType = l.DataType, IsParameter = l.IsParameter, Name = l.ToString()}; //})) //{ // context.Vars.Add(variable.Slot, variable); //} return(statement); }
/// Finds all the try statements within a list of blocks, and processes public static List <TryStatement> FindAndClean(DecompileContext ctx) { List <TryStatement> res = new List <TryStatement>(); foreach (Block b in ctx.Blocks.List) { if (b.ControlFlow == Block.ControlFlowType.TryHook) { int finallyAddress = b.Branches[0].Address; int catchAddress = b.Branches.Count == 3 ? b.Branches[1].Address : -1; res.Add(new TryStatement(b, finallyAddress, catchAddress)); } else if (b.Instructions.Count >= 3 && b.Instructions[^ 1].Kind == Instruction.Opcode.B && b.Instructions[^ 2].Kind == Instruction.Opcode.Popz) { Instruction call = b.Instructions[^ 3];
DecompileContext CreateDecompileContext(string filename) { var decompileContext = new DecompileContext(); try { var decompilationContext = new DecompilationContext(); decompileContext.Writer = new StreamWriter(filename); var output = new PlainTextOutput(decompileContext.Writer); var dispatcher = Dispatcher.CurrentDispatcher; decompileContext.DecompileNodeContext = new DecompileNodeContext(decompilationContext, language, output, dispatcher); return(decompileContext); } catch { decompileContext.Dispose(); throw; } }
public void DecompileAllScripts() { DecompileContext context = new DecompileContext(data, true); Parallel.ForEach(data.Code, (code) => { //Console.WriteLine(code.Name.Content); try { Decompiler.Decompile(code, context); } catch (Exception e) { throw new Exception("Failed to decompile script " + code.Name.Content, e); } }); }
DecompileContext CreateDecompileContext(string filename) { var decompileContext = new DecompileContext(); try { var decompilationContext = new DecompilationContext(); decompileContext.Writer = new StreamWriter(filename); var output = new TextWriterDecompilerOutput(decompileContext.Writer); var dispatcher = Dispatcher.CurrentDispatcher; decompileContext.DecompileNodeContext = new DecompileNodeContext(decompilationContext, decompiler, output, NullDocumentWriterService.Instance, dispatcher); return(decompileContext); } catch { decompileContext.Dispose(); throw; } }
public static void InsertNodes(DecompileContext ctx) { ctx.PredecessorsToClear = new List <Node>(); // Process basic branch statements in nested order List <Node> toProcess = new List <Node>(ctx.SwitchStatementNodes.Count + ctx.IfStatementNodes.Count + ctx.TryStatementNodes.Count); toProcess.AddRange(ctx.SwitchStatementNodes); toProcess.AddRange(ctx.IfStatementNodes); toProcess.AddRange(ctx.TryStatementNodes); toProcess = toProcess.OrderBy(s => s.EndAddress).ThenByDescending(s => s.Address).ToList(); foreach (var node in toProcess) { switch (node.Kind) { case Node.NodeType.IfStatement: IfStatements.InsertNode(ctx, node as IfStatement); break; case Node.NodeType.SwitchStatement: SwitchStatements.InsertNode(ctx, node as SwitchStatement); break; case Node.NodeType.TryStatement: TryStatements.InsertNode(ctx, node as TryStatement); break; } } // Clear predecessors after the fact foreach (var node in ctx.PredecessorsToClear) { foreach (var pred in node.Predecessors) { for (int i = pred.Branches.Count - 1; i >= 0; i--) { if (pred.Branches[i] == node) { pred.Branches[i] = new Block(-1, -1); // Don't actually remove: causes problems writing AST } } } } }
public BlockStatement Process(DecompileContext context, BlockStatement statement) { _context = context; _context.LoopSetSort(); IntervalAnalysisDoWhilePass(); foreach (var b in _context.Blocks) { if (b.Hidden) { continue; } if (StructureIfElse(b, out var logic)) { if (logic.Else.IsBreak) { //can be while! var loop = context.LoopSet.FirstOrDefault(l => l.Header == b); if (loop != null && loop.LoopLogic is DoWhileLogic dw) { dw.IsWhile = true; dw.Condition = logic.Condition; dw.Body = logic.Then.Blocks; } else { b.Statements.Replace(logic.Condition, logic.Simplify().ToStatement()); } } else { b.Statements.Replace(logic.Condition, logic.Simplify().ToStatement()); } } } return(statement); }
public bool Evaluate(DecompileContext ctx, ASTNode node, ASTNode parent) { if (parent.Kind != ASTNode.StatementKind.Function) { return(false); } ASTFunction func = parent as ASTFunction; if (Index >= func.Children.Count) { return(false); } ASTNode arg = func.Children[Index]; if (arg.Kind != NodeKind) { return(false); } if (arg.ToString() != NodeValue) { return(false); } return(true); }
DecompileContext CreateDecompileContext(IShowContext ctx) { var decompileContext = new DecompileContext(); var decompilationContext = new DecompilationContext(); decompilationContext.CalculateILRanges = true; decompilationContext.GetDisableAssemblyLoad = () => decompileFileTabContentFactory.FileManager.DisableAssemblyLoad(); decompilationContext.IsBodyModified = m => decompileFileTabContentFactory.MethodAnnotations.IsBodyModified(m); var output = new AvalonEditTextOutput(); var dispatcher = Dispatcher.CurrentDispatcher; decompileContext.DecompileNodeContext = new DecompileNodeContext(decompilationContext, language, output, dispatcher); if (ctx.IsRefresh) { decompileContext.SavedRefPos = ((ITextEditorUIContext)ctx.UIContext).SaveReferencePosition(); if (decompileContext.SavedRefPos != null) { ctx.OnShown = e => { if (e.Success && !e.HasMovedCaret) { e.HasMovedCaret = ((ITextEditorUIContext)ctx.UIContext).RestoreReferencePosition(decompileContext.SavedRefPos); if (!e.HasMovedCaret) { ((ITextEditorUIContext)ctx.UIContext).ScrollAndMoveCaretTo(1, 1); e.HasMovedCaret = true; } } }; } } return decompileContext; }
public abstract void Create(DecompileContext ctx);
private async void DecompileCode(UndertaleCode code) { LoaderDialog dialog = new LoaderDialog("Decompiling", "Decompiling, please wait... This can take a while on complex scripts"); dialog.Owner = Window.GetWindow(this); FlowDocument document = new FlowDocument(); document.PagePadding = new Thickness(0); document.PageWidth = 2048; // Speed-up. document.FontFamily = new FontFamily("Lucida Console"); Paragraph par = new Paragraph(); par.Margin = new Thickness(0); UndertaleCode gettextCode = null; if (gettext == null) { gettextCode = (Application.Current.MainWindow as MainWindow).Data.Code.ByName("gml_Script_textdata_en"); } string dataPath = System.IO.Path.GetDirectoryName((Application.Current.MainWindow as MainWindow).FilePath); string gettextJsonPath = (dataPath != null) ? System.IO.Path.Combine(dataPath, "lang/lang_en.json") : null; var dataa = (Application.Current.MainWindow as MainWindow).Data; Task t = Task.Run(() => { int estimatedLineCount = (int)Math.Round(code.Length * .056D); bool skipFormatting = (estimatedLineCount > 5000); DecompileContext context = new DecompileContext(dataa, !skipFormatting); string decompiled = null; Exception e = null; try { decompiled = Decompiler.Decompile(code, context).Replace("\r\n", "\n"); } catch (Exception ex) { e = ex; } if (gettextCode != null) { UpdateGettext(gettextCode); } if (gettextJSON == null && gettextJsonPath != null && File.Exists(gettextJsonPath)) { string err = UpdateGettextJSON(File.ReadAllText(gettextJsonPath)); if (err != null) { e = new Exception(err); } } Dispatcher.Invoke(() => { if (e != null) { Brush exceptionBrush = new SolidColorBrush(Color.FromRgb(255, 0, 0)); par.Inlines.Add(new Run("EXCEPTION!\n") { Foreground = exceptionBrush, FontWeight = FontWeights.Bold }); par.Inlines.Add(new Run(e.ToString()) { Foreground = exceptionBrush }); } else if (decompiled != null) { string[] lines = decompiled.Split('\n'); if (skipFormatting) { for (var i = 0; i < lines.Length; i++) { string toWrite = lines[i]; if (((i + 1) % 100) != 0 && lines.Length > i + 1) { toWrite += "\n"; // Write a new-line if we're not making a new paragraph. } if (i > 0 && i % 100 == 0) { // Splitting into different paragraphs significantly increases selection performance. document.Blocks.Add(par); par = new Paragraph(); par.Margin = new Thickness(0); } par.Inlines.Add(toWrite); } } else { Brush keywordBrush = new SolidColorBrush(Color.FromRgb(0, 0, 150)); Brush constBrush = new SolidColorBrush(Color.FromRgb(0, 100, 150)); Brush stringBrush = new SolidColorBrush(Color.FromRgb(0, 0, 200)); Brush commentBrush = new SolidColorBrush(Color.FromRgb(0, 150, 0)); Brush funcBrush = new SolidColorBrush(Color.FromRgb(100, 100, 0)); Brush assetBrush = new SolidColorBrush(Color.FromRgb(0, 150, 100)); Brush argumentBrush = new SolidColorBrush(Color.FromRgb(80, 131, 80)); Dictionary <string, UndertaleFunction> funcs = new Dictionary <string, UndertaleFunction>(); foreach (var x in (Application.Current.MainWindow as MainWindow).Data.Functions) { funcs.Add(x.Name.Content, x); } string storedStrTok = ""; foreach (var line in lines) { char[] special = { '.', ',', ')', '(', '[', ']', '>', '<', ':', ';', '=', '"', '!' }; Func <char, bool> IsSpecial = (c) => Char.IsWhiteSpace(c) || special.Contains(c); List <string> split = new List <string>(); string tok = storedStrTok; storedStrTok = ""; bool readingString = (tok != ""); bool escaped = false; for (int i = 0; i < line.Length; i++) { if (tok == "//") { tok += line.Substring(i); break; } if (!readingString && tok.Length > 0 && ( (Char.IsWhiteSpace(line[i]) != Char.IsWhiteSpace(tok[tok.Length - 1])) || (special.Contains(line[i]) != special.Contains(tok[tok.Length - 1])) || (special.Contains(line[i]) && special.Contains(tok[tok.Length - 1])) || line[i] == '"' )) { split.Add(tok); tok = ""; } if (readingString && context.isGameMaker2) { if (escaped) { escaped = false; if (line[i] == '"') { tok += line[i]; continue; } } else if (line[i] == '\\') { escaped = true; } } tok += line[i]; if (line[i] == '"') { if (readingString) { split.Add(tok); tok = ""; } readingString = !readingString; } } if (tok != "") { if (readingString) { storedStrTok = tok + "\n"; } else { split.Add(tok); } } Dictionary <string, object> usedObjects = new Dictionary <string, object>(); for (int i = 0; i < split.Count; i++) { int?val = null; string token = split[i]; if (token == "if" || token == "else" || token == "return" || token == "break" || token == "continue" || token == "while" || token == "for" || token == "repeat" || token == "with" || token == "switch" || token == "case" || token == "default" || token == "exit" || token == "var" || token == "do" || token == "until") { par.Inlines.Add(new Run(token) { Foreground = keywordBrush, FontWeight = FontWeights.Bold }); } else if (token == "self" || token == "global" || token == "local" || token == "other" || token == "noone" || token == "true" || token == "false" || token == "undefined" || token == "all") { par.Inlines.Add(new Run(token) { Foreground = keywordBrush }); } else if (token.StartsWith("argument")) { par.Inlines.Add(new Run(token) { Foreground = argumentBrush }); } else if ((val = AssetTypeResolver.FindConstValue(token)) != null) { par.Inlines.Add(new Run(token) { Foreground = constBrush, FontStyle = FontStyles.Italic, ToolTip = val.ToString() }); } else if (token.StartsWith("\"")) { par.Inlines.Add(new Run(token) { Foreground = stringBrush }); } else if (token.StartsWith("//")) { par.Inlines.Add(new Run(token) { Foreground = commentBrush }); } else if (token.StartsWith("@") && split[i - 1][0] == '"' && split[i - 1][split[i - 1].Length - 1] == '"') { par.Inlines.LastInline.Cursor = Cursors.Hand; par.Inlines.LastInline.MouseDown += (sender, ev) => { MainWindow mw = Application.Current.MainWindow as MainWindow; mw.ChangeSelection(mw.Data.Strings[Int32.Parse(token.Substring(1))]); }; } else if (dataa.ByName(token) != null) { par.Inlines.Add(new Run(token) { Foreground = assetBrush, Cursor = Cursors.Hand }); par.Inlines.LastInline.MouseDown += (sender, ev) => (Application.Current.MainWindow as MainWindow).ChangeSelection(dataa.ByName(token)); if (token == "scr_gettext" && gettext != null) { if (split[i + 1] == "(" && split[i + 2].StartsWith("\"") && split[i + 3].StartsWith("@") && split[i + 4] == ")") { string id = split[i + 2].Substring(1, split[i + 2].Length - 2); if (!usedObjects.ContainsKey(id) && gettext.ContainsKey(id)) { usedObjects.Add(id, (Application.Current.MainWindow as MainWindow).Data.Strings[gettext[id]]); } } } if (token == "scr_84_get_lang_string" && gettextJSON != null) { if (split[i + 1] == "(" && split[i + 2].StartsWith("\"") && split[i + 3].StartsWith("@") && split[i + 4] == ")") { string id = split[i + 2].Substring(1, split[i + 2].Length - 2); if (!usedObjects.ContainsKey(id) && gettextJSON.ContainsKey(id)) { usedObjects.Add(id, gettextJSON[id]); } } } } else if (funcs.ContainsKey(token)) { par.Inlines.Add(new Run(token) { Foreground = funcBrush, Cursor = Cursors.Hand }); par.Inlines.LastInline.MouseDown += (sender, ev) => (Application.Current.MainWindow as MainWindow).ChangeSelection(funcs[token]); } else if (char.IsDigit(token[0])) { par.Inlines.Add(new Run(token) { Cursor = Cursors.Hand }); par.Inlines.LastInline.MouseDown += (sender, ev) => { if (token.Length > 2 && token[0] == '0' && token[1] == 'x') { ev.Handled = true; return; // Hex numbers aren't objects. } UndertaleData data = (Application.Current.MainWindow as MainWindow).Data; int id; if (int.TryParse(token, out id)) { List <UndertaleObject> possibleObjects = new List <UndertaleObject>(); if (id < data.Sprites.Count) { possibleObjects.Add(data.Sprites[id]); } if (id < data.Rooms.Count) { possibleObjects.Add(data.Rooms[id]); } if (id < data.GameObjects.Count) { possibleObjects.Add(data.GameObjects[id]); } if (id < data.Backgrounds.Count) { possibleObjects.Add(data.Backgrounds[id]); } if (id < data.Scripts.Count) { possibleObjects.Add(data.Scripts[id]); } if (id < data.Paths.Count) { possibleObjects.Add(data.Paths[id]); } if (id < data.Fonts.Count) { possibleObjects.Add(data.Fonts[id]); } if (id < data.Sounds.Count) { possibleObjects.Add(data.Sounds[id]); } if (id < data.Shaders.Count) { possibleObjects.Add(data.Shaders[id]); } if (id < data.Timelines.Count) { possibleObjects.Add(data.Timelines[id]); } ContextMenu contextMenu = new ContextMenu(); foreach (UndertaleObject obj in possibleObjects) { MenuItem item = new MenuItem(); item.Header = obj.ToString().Replace("_", "__"); item.Click += (sender2, ev2) => (Application.Current.MainWindow as MainWindow).ChangeSelection(obj); contextMenu.Items.Add(item); } if (id > 0x00050000) { contextMenu.Items.Add(new MenuItem() { Header = "#" + id.ToString("X6") + " (color)", IsEnabled = false }); } contextMenu.Items.Add(new MenuItem() { Header = id + " (number)", IsEnabled = false }); (sender as Run).ContextMenu = contextMenu; contextMenu.IsOpen = true; } ev.Handled = true; }; } else { par.Inlines.Add(token); } if (token == "." && (Char.IsLetter(split[i + 1][0]) || split[i + 1][0] == '_')) { int id; if (Int32.TryParse(split[i - 1], out id)) { var gos = (Application.Current.MainWindow as MainWindow).Data.GameObjects; if (!usedObjects.ContainsKey(split[i - 1]) && id >= 0 && id < gos.Count) { usedObjects.Add(split[i - 1], gos[id]); } } } } // Add used object comments. foreach (var gt in usedObjects) { par.Inlines.Add(new Run(" // " + gt.Key + " = ") { Foreground = commentBrush }); par.Inlines.Add(new Run(gt.Value is string? "\"" + (string)gt.Value + "\"" : gt.Value.ToString()) { Foreground = commentBrush, Cursor = Cursors.Hand }); if (gt.Value is UndertaleObject) { par.Inlines.LastInline.MouseDown += (sender, ev) => (Application.Current.MainWindow as MainWindow).ChangeSelection(gt.Value); } } if (par.Inlines.Count >= 250) { // Splitting into different paragraphs significantly increases selection performance. document.Blocks.Add(par); par = new Paragraph(); par.Margin = new Thickness(0); } else if (!readingString) { par.Inlines.Add(new Run("\n")); } } } } document.Blocks.Add(par); DecompiledView.Document = document; CurrentDecompiled = code; dialog.Hide(); }); }); dialog.ShowDialog(); await t; }
/// Inserts short-circuit nodes into the graph, and compile list of conditions public static void InsertNodes(DecompileContext ctx) { foreach (var s in ctx.ShortCircuitNodes) { Node header = ctx.Blocks.AddressToBlock[s.Address]; (header as Block).BelongingTo = s; if ((header as Block).Instructions.Count == 1 || header.Predecessors[0].Kind == Node.NodeType.ShortCircuit) { // The header is actually another short circuit, need to adjust up header = header.Predecessors[0]; s.Address = header.Address; } // Find all the conditions Node curr = header; Node prev = header; bool skip = false; while (curr != s.Tail) { if (curr.Kind == Node.NodeType.Block) { Block b = curr as Block; if (b.LastInstr.Kind == Instruction.Opcode.B) { // This is the last condition if (!skip) { if (b.Instructions.Count != 1) { s.Conditions.Add(b); // This has the full condition in the block } else { s.Conditions.Add(prev); // Another short-circuit node } } if (b.Instructions.LastOrDefault()?.Kind == Instruction.Opcode.B) // It might not be inside a loop! { b.Instructions.RemoveAt(b.Instructions.Count - 1); } b.ControlFlow = Block.ControlFlowType.None; break; } else // (assuming either Bf or Bt) { // This is a new condition if (!skip) { if (b.Instructions.Count != 1) { s.Conditions.Add(b); // This has the full condition in the block } else { s.Conditions.Add(prev); // Another short-circuit node } } else { skip = false; } b.Instructions.RemoveAt(b.Instructions.Count - 1); // Continue onwards prev = curr; curr = curr.Branches[1]; } } else { // Continue onwards prev = curr; s.Conditions.Add(curr); curr = curr.Branches[0]; skip = true; } } // Change header predecessors to point to this node instead foreach (var node in header.Predecessors) { for (int i = 0; i < node.Branches.Count; i++) { if (node.Branches[i] != node && node.Branches[i] == header) { node.Branches[i] = s; break; } } } // Change tail branches to come from this node instead foreach (var node in s.Tail.Branches) { node.Predecessors.Clear(); node.Predecessors.Add(s); } // Remove necessary branches from conditions foreach (var cond in s.Conditions) { for (int i = cond.Branches.Count - 1; i >= 0; i--) { if (cond.Kind == Node.NodeType.ShortCircuit && cond.Branches[i].Address >= s.EndAddress) { cond.Branches.RemoveAt(i); } else if (cond.Branches[i].Address >= s.Tail.Address || s.Conditions.Contains(cond.Branches[i])) { cond.Branches.RemoveAt(i); } else { // Need to cut off the branch eventually Stack <Node> work = new Stack <Node>(); work.Push(cond.Branches[i]); while (work.Count != 0) { Node currBranch = work.Pop(); for (int j = currBranch.Branches.Count - 1; j >= 0; j--) { if (currBranch.Branches[j].Address >= cond.EndAddress) { currBranch.Branches.RemoveAt(j); } else if (currBranch.Branches[j].Address > s.Address && currBranch.Branches[j] != s.Tail) { work.Push(currBranch.Branches[j]); } } } } } } // Transfer predecessors and branches s.Predecessors = header.Predecessors; s.Branches.AddRange(s.Tail.Branches); s.Tail.Branches.Clear(); } }
public static ASTNode ResolveAny(DecompileContext ctx, ASTNode node, ASTNode parent, ConditionalAssetType type) { // Check if this type has a condition that needs to be satisfied if (type.Condition != null) { if (type.Condition.Evaluate(ctx, node, parent)) { if (type.Condition.EvaluateOnce) { type = new(type); // Make a new type without the condition (it has been satisfied) } } else { // Check if there's any valid alternative type if (type.Alternatives != null) { bool evaluated = false; for (int i = 0; i < type.Alternatives.Length; i++) { var curr = type.Alternatives[i]; if (curr.Condition == null || curr.Condition.Evaluate(ctx, node, parent)) { evaluated = true; if (curr.Condition != null && curr.Condition.EvaluateOnce) { type = new(curr); // Make a new type without the condition (it has been satisfied) } else { type = curr; } break; } } if (!evaluated) { return(node); } } else { return(node); } } } switch (node.Kind) { case ASTNode.StatementKind.Int16: case ASTNode.StatementKind.Int32: return(ResolveInt(ctx, node, type)); case ASTNode.StatementKind.IfStatement: if (node.Children.Count == 5) { node.Children[3] = ResolveAny(ctx, node.Children[3], node, type); node.Children[4] = ResolveAny(ctx, node.Children[4], node, type); } break; case ASTNode.StatementKind.Binary: node.Children[0] = ResolveAny(ctx, node.Children[0], node, type); node.Children[1] = ResolveAny(ctx, node.Children[1], node, type); break; case ASTNode.StatementKind.Function: if ((node as ASTFunction).Function.Name.Content == "@@NewGMLArray@@") { for (int i = 0; i < node.Children.Count; i++) { node.Children[i] = ResolveAny(ctx, node.Children[i], node, type); } } break; } return(node); }
/// Inserts loop nodes into the graph, and resolves break/continue public static void InsertNodes(DecompileContext ctx) { foreach (var loop in ctx.LoopNodes) { // Change header predecessors to point to the loop instead foreach (var node in loop.Header.Predecessors) { for (int i = 0; i < node.Branches.Count; i++) { if (node.Branches[i] != node && node.Branches[i] == loop.Header) { if (loop.LoopKind == Loop.LoopType.With || loop.LoopKind == Loop.LoopType.Repeat) { if (node.Kind != Node.NodeType.Loop || (node.Branches[i].Kind == Node.NodeType.Loop && node.Branches[i].Address != loop.Address)) // Special case when a with or repeat statement starts with a loop { node.Branches[i] = loop; break; } } else { node.Branches[i] = loop; break; } } } } // Initialize predecessors/branches if they're outside of the loop bounds foreach (var pred in loop.Header.Predecessors) { if ((pred.Address < loop.Address && pred.EndAddress <= loop.Address) || pred.Address >= loop.EndAddress) { loop.Predecessors.Add(pred); if (!pred.Branches.Contains(loop)) // Fix for certain empty with statements followed by another loop { pred.Branches.Insert(0, loop); } } } if (loop.LoopKind != Loop.LoopType.Repeat) // Hacky(?) fix to prevent too many loop branches { foreach (var branch in loop.Header.Branches) { if ((branch.Address < loop.Address && branch.EndAddress <= loop.Address) || branch.Address >= loop.EndAddress) { loop.Branches.Add(branch); } } } if (loop.LoopKind == Loop.LoopType.With) { // Need to make a new block at the end of the with statement so other control flow detection doesn't explode Block withEndBlock = new Block(loop.Tail.Address, loop.Tail.Address); foreach (var pred in loop.Tail.Predecessors) { for (int i = pred.Branches.Count - 1; i >= 0; i--) { if (pred.Branches[i] == loop.Tail) { pred.Branches[i] = withEndBlock; withEndBlock.Predecessors.Add(pred); } } } loop.Tail.Predecessors.Clear(); loop.Branches.Add(loop.Tail); loop.Tail.Predecessors.Add(loop); } else { foreach (var branch in loop.Tail.Branches) { if ((branch.Address < loop.Address && branch.EndAddress <= loop.Address) || branch.Address >= loop.EndAddress) { if (!loop.Branches.Contains(branch)) { loop.Branches.Add(branch); } } } } // Change any nodes jumped outbound to be marked as jumped from this loop Stack <Node> work = new Stack <Node>(); List <Node> visited = new List <Node>(); work.Push(loop.Header); visited.Add(loop.Header); while (work.Count != 0) { Node curr = work.Pop(); for (int i = 0; i < curr.Branches.Count; i++) { var branch = curr.Branches[i]; if ((branch.Address < loop.Address && branch.EndAddress <= loop.Address) || branch.Address >= loop.EndAddress) { // This node branches outside of the loop. if (curr.Kind == Node.NodeType.Block && (curr as Block).LastInstr?.Kind == Instruction.Opcode.B && branch.Address >= loop.EndAddress && curr.EndAddress < loop.EndAddress) { // This is actually a break statement // Remove `b` instruction, mark the block as a "break" block Block currBlock = (curr as Block); currBlock.Instructions.RemoveAt(currBlock.Instructions.Count - 1); currBlock.ControlFlow = Block.ControlFlowType.Break; } else { // Make the branch come from the loop instead if (loop.LoopKind != Loop.LoopType.With && !loop.Branches.Contains(branch)) { loop.Branches.Add(branch); } var preds = branch.Predecessors; for (int j = 0; j < preds.Count; j++) { if (preds[j] == curr) { preds[j] = loop; break; } } } } else if (branch == loop || branch == loop.Tail) { if (curr != loop.Tail && curr.Kind == Node.NodeType.Block && (curr as Block).LastInstr?.Kind == Instruction.Opcode.B) { if (branch == loop.Tail && branch is Block b && b.Instructions.FirstOrDefault()?.Kind == Instruction.Opcode.Popz) { // This is actually a break statement inside of a switch, inside of a loop // Don't do anything to this here, because it should be processed later? (TODO?) } else { // This is a continue statement // Remove `b` instruction, mark the block as a "continue" block Block currBlock = (curr as Block); currBlock.Instructions.RemoveAt(currBlock.Instructions.Count - 1); currBlock.ControlFlow = Block.ControlFlowType.Continue; } } // (otherwise just ignore the jump) } if (branch.Address >= loop.Address && branch.EndAddress <= loop.EndAddress && !visited.Contains(branch)) { work.Push(branch); visited.Add(branch); } } } // Remove unnecessary predecessors from the header (inside the loop) for (int i = loop.Header.Predecessors.Count - 1; i >= 0; i--) { var pred = loop.Header.Predecessors[i]; if (pred.Address >= loop.Address || pred.EndAddress > loop.Address) { loop.Header.Predecessors.RemoveAt(i); } } loop.Header.Predecessors.Add(loop); // Set it up so that the last branch signifies REAL header/body (not just the block) loop.Branches.Add(loop.Header); // Remove unnecessary instructions and deal with references for the loop switch (loop.LoopKind) { case Loop.LoopType.While: // Remove `b` loop.Tail.Instructions.RemoveAt(loop.Tail.Instructions.Count - 1); // Find the end of the condition: // - Branches out from loop.Header (or is loop.Header) // - Ends in a `bf` instruction // - First branch goes out of loop Block conditionBlock = null; work.Push(loop.Header); while (work.Count != 0) { Node curr = work.Pop(); if (curr.Kind == Node.NodeType.Block && (curr as Block).LastInstr?.Kind == Instruction.Opcode.Bf) { if (curr.Branches[0].Address >= loop.EndAddress) { // We found the condition! conditionBlock = curr as Block; break; } } foreach (var branch in curr.Branches) { work.Push(branch); } } conditionBlock.Branches.RemoveAt(0); conditionBlock.Instructions.RemoveAt(conditionBlock.Instructions.Count - 1); // Remove `bf` conditionBlock.ControlFlow = Block.ControlFlowType.LoopCondition; break; case Loop.LoopType.Repeat: { // Remove initial condition in block before loop, and its branch Block prev = loop.Predecessors[0] as Block; prev.Branches.RemoveAt(0); prev.Instructions.RemoveRange(prev.Instructions.Count - 4, 4); prev.ControlFlow = Block.ControlFlowType.RepeatExpression; // Mark this for later reference // Handle edge cases similar to "with" for (int i = prev.Branches.Count - 1; i >= 0; i--) { if (prev.Branches[i] != loop) { if (prev.Branches[i].Kind == Node.NodeType.Loop && prev.Branches[i].Address == loop.Address) { // Edge case when a loop begins with another loop Node innerLoop = prev.Branches[i]; prev.Branches[i] = loop; foreach (var pred2 in innerLoop.Predecessors) { for (int j = pred2.Branches.Count - 1; j >= 0; j--) { if (pred2.Branches[j] == innerLoop) { pred2.Branches[j] = loop; } } } innerLoop.Predecessors.Clear(); innerLoop.Predecessors.Add(loop); loop.Branches[1] = innerLoop; loop.Header.Predecessors[1] = innerLoop; } } } // Remove unnecessary branches from block previous to this loop for (int i = prev.Branches.Count - 1; i >= 0; i--) { if (prev.Branches[i] != loop) { prev.Branches.RemoveAt(i); } } // Remove decrement and branch loop.Tail.Instructions.RemoveRange(loop.Tail.Instructions.Count - 5, 5); // Remove popz in branch (loop.Branches[0] as Block).Instructions.RemoveAt(0); } break; case Loop.LoopType.DoUntil: // Remove `bf` loop.Tail.Instructions.RemoveAt(loop.Tail.Instructions.Count - 1); loop.Tail.ControlFlow = Block.ControlFlowType.LoopCondition; break; case Loop.LoopType.With: { // Mark block before loop as a with expression (pushenv and popenv don't need to be removed; they're unique) Node prev = loop.Predecessors[0]; if (prev.Kind == Node.NodeType.Block) { (prev as Block).ControlFlow = Block.ControlFlowType.WithExpression; // Mark this for later reference } // Remove unnecessary branches from the block before for (int i = prev.Branches.Count - 1; i >= 0; i--) { if (prev.Branches[i] != loop) { if (prev.Branches[i].Kind == Node.NodeType.Loop && prev.Branches[i].Address == loop.Address) { // Edge case when a loop begins a with statement Node innerLoop = prev.Branches[i]; prev.Branches[i] = loop; foreach (var pred2 in innerLoop.Predecessors) { for (int j = pred2.Branches.Count - 1; j >= 0; j--) { if (pred2.Branches[j] == innerLoop) { pred2.Branches[j] = loop; } } } innerLoop.Predecessors.Clear(); innerLoop.Predecessors.Add(loop); loop.Branches[1] = innerLoop; loop.Header.Predecessors[1] = innerLoop; } else { prev.Branches[i].Predecessors.Remove(prev.Branches[i]); prev.Branches.RemoveAt(i); } } } // Check for a "break"/popenv drop block at the end, and remove the `b` instruction if (loop.Tail.Instructions.Count == 2 && loop.Tail.Instructions[1].Kind == Instruction.Opcode.B && loop.Tail.Index + 1 < ctx.Blocks.List.Count) { Block nextBlock = ctx.Blocks.List[loop.Tail.Index + 1]; if (loop.Tail.Branches[0].Address == nextBlock.EndAddress && nextBlock.Instructions.Count == 1 && nextBlock.Instructions[0].Kind == Instruction.Opcode.PopEnv && nextBlock.Instructions[0].PopenvExitMagic) { loop.Tail.Instructions.RemoveAt(1); } } } break; } }
public void BlockProcess(DecompileContext context, Block block, Dictionary <int, Expression> exps) { if (block.Statements != null) { return; } if (block.From.Count > 1) { //get from.Output && from.Def var commonInput = block.From.Select(b => b.Output).Union(block.From.Select(b => b.Def)).GetIntersection(); commonInput.IntersectWith(block.Input); //flag can be phi if (commonInput.Count > 0) { foreach (var inSlot in commonInput) { //Generate Phi var phi = new PhiExpression(inSlot); //From must be sorted since we need first condition if (block.From[0].Statements?.Last() is ConditionExpression condition) { phi.Condition = condition; //var thenBlock = context.BlockTable[condition.JumpTo]; var elseBlock = context.BlockTable[condition.ElseTo]; //phi.ThenBranch = context.BlockFinalStates[trueBlock][inSlot]; phi.ThenBranch = context.BlockFinalStates[block.From[0]] [inSlot]; //if jump, use the state from the jump-from block phi.ElseBranch = context.BlockFinalStates[elseBlock][inSlot]; //Next: Merge condition: if (v1) then v1 else v2 => v1 || v2 (infer v1 is bool) if (phi.ThenBranch != phi.ElseBranch) { exps[inSlot] = phi; } } } } } Expression retExp = null; var ex = new Dictionary <int, Expression>(exps); var flag = ex.ContainsKey(Const.FlagReg) ? ex[Const.FlagReg] : null; var expList = new List <IAstNode>(); block.Statements = expList; InstructionData insData = null; for (var i = 0; i < block.Instructions.Count; i++) { ex[0] = Void; var ins = block.Instructions[i]; insData = block.InstructionDatas[i]; switch (ins.OpCode) { case OpCode.NOP: break; case OpCode.CONST: { var data = (OperandData)ins.Data; var constExp = new ConstantExpression(data.Variant); ex[ins.GetRegisterSlot(0)] = constExp; } break; case OpCode.CL: { ex[ins.GetRegisterSlot(0)] = null; } break; case OpCode.CCL: break; case OpCode.CEQ: case OpCode.CDEQ: case OpCode.CLT: case OpCode.CGT: { var left = ex[ins.GetRegisterSlot(0)]; var right = ex[ins.GetRegisterSlot(1)]; BinaryOp op = BinaryOp.Unknown; switch (ins.OpCode) { case OpCode.CEQ: op = BinaryOp.Equal; break; case OpCode.CDEQ: op = BinaryOp.Congruent; break; case OpCode.CLT: op = BinaryOp.LessThan; break; case OpCode.CGT: op = BinaryOp.GreaterThan; break; } var b = new BinaryExpression(left, right, op); flag = b; } break; case OpCode.SETF: case OpCode.SETNF: { var dst = ins.GetRegisterSlot(0); switch (ins.OpCode) { case OpCode.SETF: ex[dst] = flag; break; case OpCode.SETNF: ex[dst] = flag.Invert(); break; } } break; case OpCode.TT: { flag = ex[ins.GetRegisterSlot(0)]; } break; case OpCode.TF: { flag = ex[ins.GetRegisterSlot(0)].Invert(); } break; case OpCode.NF: { flag = flag.Invert(); } break; case OpCode.JF: case OpCode.JNF: { bool jmpFlag = ins.OpCode == OpCode.JF; expList.Add(new ConditionExpression(flag, jmpFlag) { JumpTo = ((JumpData)ins.Data).Goto.Line, ElseTo = ins.Line + 1 }); } break; case OpCode.JMP: { expList.Add(new GotoExpression { JumpTo = ((JumpData)ins.Data).Goto.Line }); } break; case OpCode.CHS: case OpCode.INT: case OpCode.REAL: case OpCode.STR: case OpCode.NUM: case OpCode.OCTET: case OpCode.LNOT: case OpCode.INC: case OpCode.DEC: case OpCode.BNOT: case OpCode.TYPEOF: case OpCode.INV: { var dstSlot = ins.GetRegisterSlot(0); var dst = ex[dstSlot]; var op = UnaryOp.Unknown; switch (ins.OpCode) { case OpCode.INC: op = UnaryOp.Inc; break; case OpCode.DEC: op = UnaryOp.Dec; break; case OpCode.CHS: op = UnaryOp.InvertSign; break; case OpCode.INT: op = UnaryOp.ToInt; break; case OpCode.REAL: op = UnaryOp.ToReal; break; case OpCode.STR: op = UnaryOp.ToString; break; case OpCode.NUM: op = UnaryOp.ToNumber; break; case OpCode.BNOT: op = UnaryOp.BitNot; break; case OpCode.OCTET: op = UnaryOp.ToByteArray; break; case OpCode.LNOT: op = UnaryOp.Not; break; case OpCode.TYPEOF: op = UnaryOp.TypeOf; break; case OpCode.INV: op = UnaryOp.Invalidate; break; } var u = new UnaryExpression(dst, op); //ex[dstSlot] = u; expList.Add(u); } break; case OpCode.INCPD: case OpCode.DECPD: case OpCode.TYPEOFD: { var res = ins.GetRegisterSlot(0); var obj = ins.GetRegisterSlot(1); var name = ins.Data.AsString(); var op = UnaryOp.Unknown; switch (ins.OpCode) { case OpCode.INCPI: op = UnaryOp.Inc; break; case OpCode.DECPI: op = UnaryOp.Dec; break; case OpCode.TYPEOFD: op = UnaryOp.TypeOf; break; } //var u = new UnaryExpression(new IdentifierExpression(name), op) {Instance = ex[obj]}; var u = new UnaryExpression(new IdentifierExpression(name) { Instance = ex[obj] }, op); if (res != 0) //copy to %res { ex[res] = u; } expList.Add(u); } break; case OpCode.INCPI: case OpCode.DECPI: case OpCode.TYPEOFI: { var res = ins.GetRegisterSlot(0); var obj = ins.GetRegisterSlot(1); var name = ins.GetRegisterSlot(2); var op = UnaryOp.Unknown; switch (ins.OpCode) { case OpCode.INCPI: op = UnaryOp.Inc; break; case OpCode.DECPI: op = UnaryOp.Dec; break; case OpCode.TYPEOFI: op = UnaryOp.TypeOf; break; } var u = new UnaryExpression(new PropertyAccessExpression(ex[name], ex[obj]), op); if (res != 0) //copy to %res { ex[res] = u; } expList.Add(u); } break; case OpCode.INCP: case OpCode.DECP: break; case OpCode.LORP: break; case OpCode.LANDP: break; case OpCode.BORP: break; case OpCode.BXORP: break; case OpCode.BANDP: break; case OpCode.SARP: break; case OpCode.SALP: break; case OpCode.SRP: break; case OpCode.CP: { var dstSlot = ins.GetRegisterSlot(0); var srcSlot = ins.GetRegisterSlot(1); Expression src; if (ex.ContainsKey(srcSlot)) { src = ex[srcSlot]; } else { src = new LocalExpression(context.Object, srcSlot); } Expression dst = null; if (ex.ContainsKey(dstSlot)) { //dst = ex[dstSlot]; ex[dstSlot] = src; } else if (dstSlot < -2) { var l = new LocalExpression(context.Object, dstSlot); //if (!l.IsParameter) //{ // expList.Add(l); //} dst = l; ex[dstSlot] = l; //assignment -> statements, local -> expressions BinaryExpression b = new BinaryExpression(dst, src, BinaryOp.Assign) { IsDeclaration = true }; //ex[dstSlot] = b; expList.Add(b); } else if (dstSlot != 0) { ex[dstSlot] = src; } } break; //Binary Operation case OpCode.ADD: case OpCode.SUB: case OpCode.MOD: case OpCode.DIV: case OpCode.IDIV: case OpCode.MUL: case OpCode.BAND: case OpCode.BOR: case OpCode.BXOR: case OpCode.LAND: case OpCode.LOR: case OpCode.SAR: case OpCode.SAL: case OpCode.SR: case OpCode.CHKINS: { var dstSlot = ins.GetRegisterSlot(0); var srcSlot = ins.GetRegisterSlot(1); var store = false; //Set to Expression var declare = false; //Is declaration Expression dst = null; if (ex.ContainsKey(dstSlot)) { dst = ex[dstSlot]; } else if (dstSlot < -2) { var l = new LocalExpression(context.Object, dstSlot); //if (!l.IsParameter) //{ // expList.Add(l); //} dst = l; ex[dstSlot] = l; store = false; declare = true; } Expression src; if (ex.ContainsKey(srcSlot)) { src = ex[srcSlot]; } else { src = new LocalExpression(context.Object, srcSlot); } var op = BinaryOp.Unknown; switch (ins.OpCode) { case OpCode.ADD: op = BinaryOp.Add; break; case OpCode.SUB: op = BinaryOp.Sub; break; case OpCode.MOD: op = BinaryOp.Mod; break; case OpCode.DIV: op = BinaryOp.Div; break; case OpCode.IDIV: op = BinaryOp.Idiv; break; case OpCode.MUL: op = BinaryOp.Mul; break; case OpCode.BAND: op = BinaryOp.BitAnd; break; case OpCode.BOR: op = BinaryOp.BitOr; break; case OpCode.BXOR: op = BinaryOp.BitXor; break; case OpCode.LAND: op = BinaryOp.LogicAnd; break; case OpCode.LOR: op = BinaryOp.LogicOr; break; case OpCode.SAR: op = BinaryOp.NumberShiftRight; break; case OpCode.SAL: op = BinaryOp.NumberShiftLeft; break; case OpCode.SR: op = BinaryOp.BitShiftRight; break; //case OpCode.CP: //moved! // op = BinaryOp.Assign; // push = true; //break; case OpCode.CHKINS: op = BinaryOp.InstanceOf; break; } BinaryExpression b = new BinaryExpression(dst, src, op) { IsDeclaration = declare }; if (store) { ex[dstSlot] = b; } expList.Add(b); } break; case OpCode.ADDPD: case OpCode.SUBPD: case OpCode.MODPD: case OpCode.DIVPD: case OpCode.IDIVPD: case OpCode.MULPD: case OpCode.BANDPD: case OpCode.BORPD: case OpCode.BXORPD: case OpCode.LANDPD: case OpCode.LORPD: case OpCode.SARPD: case OpCode.SALPD: case OpCode.SRPD: { var res = ins.GetRegisterSlot(0); var obj = ins.GetRegisterSlot(1); var name = ins.Data.AsString(); var op = BinaryOp.Unknown; var src = ex[ins.GetRegisterSlot(3)]; switch (ins.OpCode) { case OpCode.ADDPD: op = BinaryOp.Add; break; case OpCode.SUBPD: op = BinaryOp.Sub; break; case OpCode.MODPD: op = BinaryOp.Mod; break; case OpCode.DIVPD: op = BinaryOp.Div; break; case OpCode.IDIVPD: op = BinaryOp.Idiv; break; case OpCode.MULPD: op = BinaryOp.Mul; break; case OpCode.BANDPD: op = BinaryOp.BitAnd; break; case OpCode.BORPD: op = BinaryOp.BitOr; break; case OpCode.BXORPD: op = BinaryOp.BitXor; break; case OpCode.LANDPD: op = BinaryOp.LogicAnd; break; case OpCode.LORPD: op = BinaryOp.LogicOr; break; case OpCode.SARPD: op = BinaryOp.NumberShiftRight; break; case OpCode.SALPD: op = BinaryOp.NumberShiftLeft; break; case OpCode.SRPD: op = BinaryOp.BitShiftRight; break; } BinaryExpression b = new BinaryExpression(new IdentifierExpression(name) { Instance = ex[obj] }, src, op); if (res != 0) { ex[res] = b; } expList.Add(b); } break; case OpCode.ADDPI: case OpCode.SUBPI: case OpCode.MODPI: case OpCode.DIVPI: case OpCode.IDIVPI: case OpCode.MULPI: case OpCode.BANDPI: case OpCode.BORPI: case OpCode.BXORPI: case OpCode.LANDPI: case OpCode.LORPI: case OpCode.SARPI: case OpCode.SALPI: case OpCode.SRPI: { var res = ins.GetRegisterSlot(0); var obj = ins.GetRegisterSlot(1); var name = ins.GetRegisterSlot(2); var op = BinaryOp.Unknown; var src = ex[ins.GetRegisterSlot(3)]; switch (ins.OpCode) { case OpCode.ADDPI: op = BinaryOp.Add; break; case OpCode.SUBPI: op = BinaryOp.Sub; break; case OpCode.MODPI: op = BinaryOp.Mod; break; case OpCode.DIVPI: op = BinaryOp.Div; break; case OpCode.IDIVPI: op = BinaryOp.Idiv; break; case OpCode.MULPI: op = BinaryOp.Mul; break; case OpCode.BANDPI: op = BinaryOp.BitAnd; break; case OpCode.BORPI: op = BinaryOp.BitOr; break; case OpCode.BXORPI: op = BinaryOp.BitXor; break; case OpCode.LANDPI: op = BinaryOp.LogicAnd; break; case OpCode.LORPI: op = BinaryOp.LogicOr; break; case OpCode.SARPI: op = BinaryOp.NumberShiftRight; break; case OpCode.SALPI: op = BinaryOp.NumberShiftLeft; break; case OpCode.SRPI: op = BinaryOp.BitShiftRight; break; } BinaryExpression b = new BinaryExpression(new PropertyAccessExpression(ex[name], ex[obj]), src, op); if (res != 0) { ex[res] = b; } expList.Add(b); } break; case OpCode.ADDP: break; case OpCode.SUBP: break; case OpCode.MODP: break; case OpCode.DIVP: break; case OpCode.IDIVP: break; case OpCode.MULP: break; case OpCode.EVAL: break; case OpCode.EEXP: break; case OpCode.ASC: break; case OpCode.CHR: break; case OpCode.CHKINV: break; //Invoke case OpCode.CALL: { var call = new InvokeExpression(((OperandData)ins.Data).Variant as TjsCodeObject); var dst = ins.GetRegisterSlot(0); call.Instance = null; var paramCount = ins.GetRegisterSlot(2); if (paramCount == -1) { //... //do nothing } else { for (int j = 0; j < paramCount; j++) { var pSlot = ins.GetRegisterSlot(3 + j); call.Parameters.Add(ex[pSlot]); } } ex[dst] = call; //if (dst == 0) //just execute and discard result //{ // expList.Add(call); //} expList.Add(call); } break; case OpCode.CALLD: { var callMethodName = ins.Data.AsString(); var call = new InvokeExpression(callMethodName); var dst = ins.GetRegisterSlot(0); var callerSlot = ins.GetRegisterSlot(1); call.Instance = ex[callerSlot]; var paramCount = ins.GetRegisterSlot(3); if (paramCount == -1) { //... //do nothing } else { for (int j = 0; j < paramCount; j++) { var pSlot = ins.GetRegisterSlot(4 + j); ex[pSlot].Parent = call; call.Parameters.Add(ex[pSlot]); } } ex[dst] = call; if (dst == 0) //just execute and discard result { //Handle RegExp()._compile("//g/[^A-Za-z]") if (callMethodName == Const.RegExpCompile) { if (call.Instance is InvokeExpression invoke && invoke.Method == Const.RegExp) { call.InvokeType = InvokeType.RegExpCompile; ex[callerSlot] = call; break; } } expList.Add(call); } } break; case OpCode.CALLI: { //InvokeExpression call = null; //var operand = ((OperandData) ins.Data).Variant; //if (operand is TjsString str) //{ // call = new InvokeExpression(str.StringValue); //} //else //{ // call = new InvokeExpression(operand as TjsCodeObject); //} InvokeExpression call = new InvokeExpression(ex[ins.GetRegisterSlot(2)]); var dst = ins.GetRegisterSlot(0); var callerSlot = ins.GetRegisterSlot(1); call.Instance = ex[callerSlot]; var paramCount = ins.GetRegisterSlot(3); if (paramCount == -1) { //... //do nothing } else { for (int j = 0; j < paramCount; j++) { var pSlot = ins.GetRegisterSlot(4 + j); ex[pSlot].Parent = call; call.Parameters.Add(ex[pSlot]); } } ex[dst] = call; //if (dst == 0) //just execute and discard result //{ // expList.Add(call); //} expList.Add(call); } break; case OpCode.NEW: { InvokeExpression call = new InvokeExpression(ex[ins.GetRegisterSlot(1)]) { InvokeType = InvokeType.Ctor }; var dst = ins.GetRegisterSlot(0); call.Instance = null; var paramCount = ins.GetRegisterSlot(2); if (paramCount == -1) { //... //do nothing } else { for (int j = 0; j < paramCount; j++) { var pSlot = ins.GetRegisterSlot(3 + j); ex[pSlot].Parent = call; call.Parameters.Add(ex[pSlot]); } } ex[dst] = call; //if (dst == 0) //just execute and discard result //{ // expList.Add(call); //} expList.Add(call); } break; case OpCode.GPD: case OpCode.GPDS: { var dst = ins.GetRegisterSlot(0); var slot = ins.GetRegisterSlot(1); var instance = ex[slot]; var name = ins.Data.AsString(); var newId = new IdentifierExpression(name) { Instance = instance }; ex[dst] = newId; } break; case OpCode.GPI: case OpCode.GPIS: { var dst = ins.GetRegisterSlot(0); var obj = ins.GetRegisterSlot(1); var name = ins.GetRegisterSlot(2); PropertyAccessExpression p = new PropertyAccessExpression(ex[name], ex[obj]); ex[dst] = p; } break; case OpCode.SPI: case OpCode.SPIE: case OpCode.SPIS: { var obj = ins.GetRegisterSlot(0); var name = ins.GetRegisterSlot(1); var src = ins.GetRegisterSlot(2); BinaryExpression b = new BinaryExpression(new PropertyAccessExpression(ex[name], ex[obj]), ex[src], BinaryOp.Assign); expList.Add(b); //there is no other way to find this expression } break; //Set case OpCode.SPD: case OpCode.SPDE: case OpCode.SPDEH: case OpCode.SPDS: { var left = new IdentifierExpression(ins.Data.AsString()) { Instance = ex[ins.GetRegisterSlot(0)] }; var right = ex[ins.GetRegisterSlot(2)]; BinaryExpression b = new BinaryExpression(left, right, BinaryOp.Assign); //check declare if (context.Object.ContextType == TjsContextType.TopLevel) { if (!context.RegisteredMembers.ContainsKey(left.Name)) { b.IsDeclaration = true; var stub = new TjsStub(); if (right is ConstantExpression con) //TODO: better type check { stub.Type = con.DataType; } context.RegisteredMembers[left.Name] = stub; } } expList.Add(b); } break; case OpCode.SETP: { } break; case OpCode.GETP: { } break; //Delete case OpCode.DELD: DeleteExpression d = new DeleteExpression(ins.Data.AsString()); d.Instance = ex[ins.GetRegisterSlot(1)]; expList.Add(d); break; case OpCode.DELI: DeleteExpression d2 = new DeleteExpression(ex[ins.GetRegisterSlot(2)]); d2.Instance = ex[ins.GetRegisterSlot(1)]; //Check declare if (d2.Instance is IdentifierExpression toDel) { if (context.RegisteredMembers.ContainsKey(toDel.Name)) { context.RegisteredMembers.Remove(toDel.Name); } } expList.Add(d2); break; case OpCode.SRV: { var srv = ins.GetRegisterSlot(0); retExp = srv == 0 ? null : ex[srv]; } break; case OpCode.RET: { expList.Add(new ReturnExpression(retExp)); } break; case OpCode.ENTRY: break; case OpCode.EXTRY: break; case OpCode.THROW: { var th = new ThrowExpression(ex[ins.GetRegisterSlot(0)]); expList.Add(th); } break; case OpCode.CHGTHIS: break; case OpCode.GLOBAL: { ex[ins.GetRegisterSlot(0)] = Global; } break; case OpCode.ADDCI: break; case OpCode.REGMEMBER: break; case OpCode.DEBUGGER: break; case OpCode.LAST: break; case OpCode.PreDec: break; case OpCode.PostInc: break; case OpCode.PostDec: break; case OpCode.Delete: break; case OpCode.FuncCall: break; case OpCode.IgnorePropGet: break; case OpCode.IgnorePropSet: break; default: throw new ArgumentOutOfRangeException(); } } expList.RemoveAll(node => node is Expression exp && exp.Parent != null); //Save states ex[Const.FlagReg] = flag; context.BlockFinalStates[block] = ex; //Process next foreach (var succ in block.To) { BlockProcess(context, succ, ex); //TODO: deep copy flag? } }
DecompileContext CreateDecompileContext() { var decompileContext = new DecompileContext(); var decompilationContext = new DecompilationContext(); decompilationContext.GetDisableAssemblyLoad = () => decompileFileTabContentFactory.FileManager.DisableAssemblyLoad(); decompilationContext.IsBodyModified = m => decompileFileTabContentFactory.MethodAnnotations.IsBodyModified(m); var output = new AvalonEditTextOutput(); var dispatcher = Dispatcher.CurrentDispatcher; decompileContext.DecompileNodeContext = new DecompileNodeContext(decompilationContext, language, output, dispatcher); return decompileContext; }
DecompileContext CreateDecompileContext(string filename) { var decompileContext = new DecompileContext(); try { var decompilationContext = new DecompilationContext(); decompileContext.Writer = new StreamWriter(filename); var output = new TextWriterDecompilerOutput(decompileContext.Writer); var dispatcher = Dispatcher.CurrentDispatcher; decompileContext.DecompileNodeContext = new DecompileNodeContext(decompilationContext, decompiler, output, NullDocumentWriterService.Instance, dispatcher); return decompileContext; } catch { decompileContext.Dispose(); throw; } }
DecompileContext CreateDecompileContext(string filename) { var decompileContext = new DecompileContext(); try { decompileContext.Writer = new StreamWriter(filename); decompileContext.Output = new PlainTextOutput(decompileContext.Writer); return decompileContext; } catch { decompileContext.Dispose(); throw; } }
DecompileContext CreateDecompileContext(string filename) { var decompileContext = new DecompileContext(); try { var decompilationContext = new DecompilationContext(); decompileContext.Writer = new StreamWriter(filename); var output = new PlainTextOutput(decompileContext.Writer); var dispatcher = Dispatcher.CurrentDispatcher; decompileContext.DecompileNodeContext = new DecompileNodeContext(decompilationContext, language, output, dispatcher); return decompileContext; } catch { decompileContext.Dispose(); throw; } }
//TODO: more robust RegMember detect public BlockStatement Process(DecompileContext context, BlockStatement statement) { context.RegisteredMembers.Clear(); var entry = context.EntryBlock; var codes = entry.Instructions; int i = 0; if (codes.Count < i + 3) { return(statement); } while (codes.Count >= i + 3) { if (codes[i].ToString() == "cl %1") { codes.RemoveAt(i); break; } if (codes[i].ToString().StartsWith("const %1,") && codes[i + 1].ToString() == "chgthis %1, %-1" && codes[i + 2].ToString().StartsWith("spds %-1.") && codes[i + 2].Registers[2].GetSlot() == 1) { var data = codes[i].Data as OperandData; if (data == null) { i += 3; continue; } var func = data.Variant as TjsCodeObject; if (func == null) { i += 3; continue; } var memberData = codes[i + 2].Data as OperandData; if (memberData == null) { i += 3; continue; } var memberName = memberData.Variant as TjsString; if (memberName == null) { i += 3; continue; } context.RegisteredMembers[memberName] = func; i += 3; } else { break; } } codes.RemoveRange(0, i); return(statement); }
public static ASTNode ResolveInt(DecompileContext ctx, ASTNode intNode, ConditionalAssetType type) { int value = (intNode.Kind == ASTNode.StatementKind.Int16) ? (intNode as ASTInt16).Value : (intNode as ASTInt32).Value; switch (type.Kind) { case AssetType.Boolean: if (value == 0) { return(new ASTBoolean(false)); } else if (value == 1) { return(new ASTBoolean(true)); } break; case AssetType.Object: if (intNode.Kind == ASTNode.StatementKind.Int16) { return(ResolveObject(ctx, intNode as ASTInt16)); } break; case AssetType.Sprite: if (value >= 0 && value < ctx.Project.Sprites.Count) { return(new ASTAsset(ctx.Project.Sprites[value].Name)); } else if (value == -4) { return(new ASTAsset("noone")); } break; case AssetType.Room: if (value >= 0 && value < ctx.Project.Rooms.Count) { return(new ASTAsset(ctx.Project.Rooms[value].Name)); } else if (value == -4) { return(new ASTAsset("noone")); } break; case AssetType.Font: if (value >= 0 && value < ctx.Project.Fonts.Count) { return(new ASTAsset(ctx.Project.Fonts[value].Name)); } else if (value == -4) { return(new ASTAsset("noone")); } break; case AssetType.Sound: if (value >= 0 && value < ctx.Project.Sounds.Count) { return(new ASTAsset(ctx.Project.Sounds[value].Name)); } else if (value == -4) { return(new ASTAsset("noone")); } break; case AssetType.Path: if (value >= 0 && value < ctx.Project.Paths.Count) { return(new ASTAsset(ctx.Project.Paths[value].Name)); } else if (value == -4) { return(new ASTAsset("noone")); } break; case AssetType.Color: if (value >= 0) { if (ctx.Cache.Types.ColorMacros.TryGetValue(value, out string color)) { return(new ASTAsset(color)); } return(new ASTAsset((ctx.Data.VersionInfo.IsNumberAtLeast(2) ? "0x" : "$") + value.ToString("X6", CultureInfo.InvariantCulture))); } break; case AssetType.Keyboard: if (value >= 0) { if (ctx.Cache.Types.KeyboardMacros.TryGetValue(value, out string keyboard)) { return(new ASTAsset(keyboard)); } if (value >= '0' && value <= 'Z') { return(new ASTAsset("ord(\"" + (char)value + "\")")); } } break; case AssetType.Macro_PathEndAction: { if (ctx.Cache.Types.PathEndActionMacros.TryGetValue(value, out string macro)) { return(new ASTAsset(macro)); } } break; } return(intNode); }
DecompileContext CreateDecompileContext(IShowContext ctx) { var decompileContext = new DecompileContext(); var decompilationContext = new DecompilationContext(); decompilationContext.CalculateBinSpans = true; decompilationContext.GetDisableAssemblyLoad = () => decompileDocumentTabContentFactory.DocumentService.DisableAssemblyLoad(); decompilationContext.IsBodyModified = m => decompileDocumentTabContentFactory.MethodAnnotations.IsBodyModified(m); var dispatcher = Dispatcher.CurrentDispatcher; decompileContext.DocumentViewerContentFactory = decompileDocumentTabContentFactory.DocumentViewerContentFactoryProvider.Create(); decompileContext.DecompileNodeContext = new DecompileNodeContext(decompilationContext, Decompiler, decompileContext.DocumentViewerContentFactory.Output, decompileDocumentTabContentFactory.DocumentWriterService, dispatcher); if (ctx.IsRefresh) { decompileContext.SavedRefPos = ((IDocumentViewer)ctx.UIContext).SaveReferencePosition(); if (decompileContext.SavedRefPos != null) { ctx.OnShown = e => { if (e.Success && !e.HasMovedCaret) { e.HasMovedCaret = ((IDocumentViewer)ctx.UIContext).RestoreReferencePosition(decompileContext.SavedRefPos); if (!e.HasMovedCaret) { ((IDocumentViewer)ctx.UIContext).MoveCaretToPosition(0); e.HasMovedCaret = true; } } }; } } return decompileContext; }