public static void ReplaceInGML(string str1, string str2, UndertaleCode code, UndertaleData data) { string decomp = Decompiler.Decompile(code, new DecompileContext(data, false)); decomp = decomp.Replace(str1, str2); code.ReplaceGML(decomp, data); code.UpdateAddresses(); }
private async void GraphCode(UndertaleCode code) { if (code.DuplicateEntry) { GraphView.Source = null; CurrentGraphed = code; return; } LoaderDialog dialog = new LoaderDialog("Generating graph", "Generating graph, please wait..."); dialog.Owner = Window.GetWindow(this); Task t = Task.Run(() => { ImageSource image = null; try { code.UpdateAddresses(); var blocks = Decompiler.DecompileFlowGraph(code); string dot = Decompiler.ExportFlowGraph(blocks); try { var getStartProcessQuery = new GetStartProcessQuery(); var getProcessStartInfoQuery = new GetProcessStartInfoQuery(); var registerLayoutPluginCommand = new RegisterLayoutPluginCommand(getProcessStartInfoQuery, getStartProcessQuery); var wrapper = new GraphGeneration(getStartProcessQuery, getProcessStartInfoQuery, registerLayoutPluginCommand); byte[] output = wrapper.GenerateGraph(dot, Enums.GraphReturnType.Png); // TODO: Use SVG instead image = new ImageSourceConverter().ConvertFrom(output) as ImageSource; } catch (Exception e) { Debug.WriteLine(e.ToString()); if (MessageBox.Show("Unable to execute GraphViz: " + e.Message + "\nMake sure you have downloaded it and set the path in settings.\nDo you want to open the download page now?", "Graph generation failed", MessageBoxButton.YesNo, MessageBoxImage.Error) == MessageBoxResult.Yes) { Process.Start("https://graphviz.gitlab.io/_pages/Download/Download_windows.html"); } } } catch (Exception e) { Debug.WriteLine(e.ToString()); MessageBox.Show(e.Message, "Graph generation failed", MessageBoxButton.OK, MessageBoxImage.Error); } Dispatcher.Invoke(() => { GraphView.Source = image; CurrentGraphed = code; dialog.Hide(); }); }); dialog.ShowDialog(); await t; }
public static void DebugPatches(UndertaleData data) { UndertaleCode ee = data.Code.ByName("gml_Object_obj_menus_Create_0"); ReplaceInGML("1, 2], [\"\", 9],", "1, 2], [\"EXTRAS\", 1, 9], ", ee, data); ee.UpdateAddresses(); data.Code.ByName("gml_Object_obj_constant_Draw_64").AppendGML(RabbitRunCode.constBruhwer, data); }
private void DisassembleCode(UndertaleCode code) { code.UpdateAddresses(); string text; if (code.DuplicateEntry) { DisassemblyEditor.IsReadOnly = true; text = "; Duplicate code entry; cannot edit here."; } else { DisassemblyEditor.IsReadOnly = false; var data = (Application.Current.MainWindow as MainWindow).Data; text = code.Disassemble(data.Variables, data.CodeLocals.For(code)); } DisassemblyEditor.Text = text; CurrentDisassembled = code; DisassemblyChanged = false; }
private void DisassembleCode(UndertaleCode code) { code.UpdateAddresses(); 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); if (code.Instructions.Count > 5000) { // Disable syntax highlighting. Loading it can take a few MINUTES on large scripts. var data = (Application.Current.MainWindow as MainWindow).Data; string[] split = code.Disassemble(data.Variables, data.CodeLocals.For(code)).Split('\n'); for (var i = 0; i < split.Length; i++) { // Makes it possible to select text. if (i > 0 && (i % 100) == 0) { document.Blocks.Add(par); par = new Paragraph(); par.Margin = new Thickness(0); } par.Inlines.Add(split[i] + (split.Length > i + 1 && ((i + 1) % 100) != 0 ? "\n" : "")); } } else { Brush addressBrush = new SolidColorBrush(Color.FromRgb(50, 50, 50)); Brush opcodeBrush = new SolidColorBrush(Color.FromRgb(0, 100, 0)); Brush argBrush = new SolidColorBrush(Color.FromRgb(0, 0, 150)); Brush typeBrush = new SolidColorBrush(Color.FromRgb(0, 0, 50)); var data = (Application.Current.MainWindow as MainWindow).Data; par.Inlines.Add(new Run(code.GenerateLocalVarDefinitions(data.Variables, data.CodeLocals.For(code))) { Foreground = addressBrush }); foreach (var instr in code.Instructions) { par.Inlines.Add(new Run(instr.Address.ToString("D5") + ": ") { Foreground = addressBrush }); par.Inlines.Add(new Run(instr.Kind.ToString().ToLower()) { Foreground = opcodeBrush, FontWeight = FontWeights.Bold }); switch (UndertaleInstruction.GetInstructionType(instr.Kind)) { case UndertaleInstruction.InstructionType.SingleTypeInstruction: par.Inlines.Add(new Run("." + instr.Type1.ToOpcodeParam()) { Foreground = typeBrush }); if (instr.Kind == UndertaleInstruction.Opcode.Dup || instr.Kind == UndertaleInstruction.Opcode.CallV) { par.Inlines.Add(new Run(" ")); par.Inlines.Add(new Run(instr.Extra.ToString()) { Foreground = argBrush }); } break; case UndertaleInstruction.InstructionType.DoubleTypeInstruction: par.Inlines.Add(new Run("." + instr.Type1.ToOpcodeParam()) { Foreground = typeBrush }); par.Inlines.Add(new Run("." + instr.Type2.ToOpcodeParam()) { Foreground = typeBrush }); break; case UndertaleInstruction.InstructionType.ComparisonInstruction: par.Inlines.Add(new Run("." + instr.Type1.ToOpcodeParam()) { Foreground = typeBrush }); par.Inlines.Add(new Run("." + instr.Type2.ToOpcodeParam()) { Foreground = typeBrush }); par.Inlines.Add(new Run(" ")); par.Inlines.Add(new Run(instr.ComparisonKind.ToString()) { Foreground = opcodeBrush }); break; case UndertaleInstruction.InstructionType.GotoInstruction: par.Inlines.Add(new Run(" ")); string tgt = (instr.Address + instr.JumpOffset).ToString("D5"); if (instr.Address + instr.JumpOffset == code.Length / 4) { tgt = "func_end"; } if (instr.JumpOffsetPopenvExitMagic) { tgt = "[drop]"; } par.Inlines.Add(new Run(tgt) { Foreground = argBrush, ToolTip = "$" + instr.JumpOffset.ToString("+#;-#;0") }); break; case UndertaleInstruction.InstructionType.PopInstruction: par.Inlines.Add(new Run("." + instr.Type1.ToOpcodeParam()) { Foreground = typeBrush }); par.Inlines.Add(new Run("." + instr.Type2.ToOpcodeParam()) { Foreground = typeBrush }); par.Inlines.Add(new Run(" ")); if (instr.Type1 == UndertaleInstruction.DataType.Int16) { // Special scenario - the swap instruction // TODO: Figure out the proper syntax, see #129 Run runType = new Run(instr.SwapExtra.ToString().ToLower()) { Foreground = argBrush }; par.Inlines.Add(runType); } else { if (instr.Type1 == UndertaleInstruction.DataType.Variable && instr.TypeInst != UndertaleInstruction.InstanceType.Undefined) { par.Inlines.Add(new Run(instr.TypeInst.ToString().ToLower()) { Foreground = typeBrush }); par.Inlines.Add(new Run(".")); } Run runDest = new Run(instr.Destination.ToString()) { Foreground = argBrush, Cursor = Cursors.Hand }; runDest.MouseDown += (sender, e) => { (Application.Current.MainWindow as MainWindow).ChangeSelection(instr.Destination); }; par.Inlines.Add(runDest); } break; case UndertaleInstruction.InstructionType.PushInstruction: par.Inlines.Add(new Run("." + instr.Type1.ToOpcodeParam()) { Foreground = typeBrush }); par.Inlines.Add(new Run(" ")); if (instr.Type1 == UndertaleInstruction.DataType.Variable && instr.TypeInst != UndertaleInstruction.InstanceType.Undefined) { par.Inlines.Add(new Run(instr.TypeInst.ToString().ToLower()) { Foreground = typeBrush }); par.Inlines.Add(new Run(".")); } Run valueRun = new Run((instr.Value as IFormattable)?.ToString(null, CultureInfo.InvariantCulture) ?? instr.Value.ToString()) { Foreground = argBrush, Cursor = (instr.Value is UndertaleObject || instr.Value is UndertaleResourceRef) ? Cursors.Hand : Cursors.Arrow }; if (instr.Value is UndertaleResourceRef) { valueRun.MouseDown += (sender, e) => { (Application.Current.MainWindow as MainWindow).ChangeSelection((instr.Value as UndertaleResourceRef).Resource); }; } else if (instr.Value is UndertaleObject) { valueRun.MouseDown += (sender, e) => { (Application.Current.MainWindow as MainWindow).ChangeSelection(instr.Value); }; } par.Inlines.Add(valueRun); break; case UndertaleInstruction.InstructionType.CallInstruction: par.Inlines.Add(new Run("." + instr.Type1.ToOpcodeParam()) { Foreground = typeBrush }); par.Inlines.Add(new Run(" ")); par.Inlines.Add(new Run(instr.Function.ToString()) { Foreground = argBrush }); par.Inlines.Add(new Run("(argc=")); par.Inlines.Add(new Run(instr.ArgumentsCount.ToString()) { Foreground = argBrush }); par.Inlines.Add(new Run(")")); break; case UndertaleInstruction.InstructionType.BreakInstruction: par.Inlines.Add(new Run("." + instr.Type1.ToOpcodeParam()) { Foreground = typeBrush }); par.Inlines.Add(new Run(" ")); par.Inlines.Add(new Run(instr.Value.ToString()) { Foreground = argBrush }); break; } if (par.Inlines.Count >= 250) { // Makes selecting text possible. document.Blocks.Add(par); par = new Paragraph(); par.Margin = new Thickness(0); } else { par.Inlines.Add(new Run("\n")); } } } document.Blocks.Add(par); DisassemblyView.Document = document; CurrentDisassembled = code; }
public static void SpeedrunPatches(UndertaleData data) { UndertaleScript setboi = new UndertaleScript(); setboi.Name = data.Strings.MakeString("set_speedrun_category"); setboi.Code = new UndertaleCode(); setboi.Code.Name = data.Strings.MakeString("gml_Script_set_speedrun_category"); setboi.Code.ReplaceGML(RabbitRunCode.set_speedrun_category, data); data.Code.Add(setboi.Code); data.Scripts.Add(setboi); UndertaleScript sprun = new UndertaleScript(); sprun.Name = new UndertaleString("menu_speedrun_script"); data.Strings.Add(sprun.Name); sprun.Code = new UndertaleCode(); data.Code.Add(sprun.Code); sprun.Code.Name = new UndertaleString("gml_Script_menu_speedrun_script"); data.Strings.Add(sprun.Code.Name); sprun.Code.ReplaceGML(RabbitRunCode.menu_speedrun_script, data); sprun.Code.UpdateAddresses(); data.Scripts.Add(sprun); UndertaleCode ae = data.Code.ByName("gml_Script_setfile"); ae.AppendGML(RabbitRunCode.gml_Script_setfile, data); ae.UpdateAddresses(); UndertaleCode ee = data.Code.ByName("gml_Object_obj_mainmenus_Create_0"); ReplaceInGML("GAME\", 1, 8],", "GAME\", 1, 8], [\"SPEEDBUN\", 1, 19], ", ee, data); ee.UpdateAddresses(); ReplaceInGML("i = 0", RabbitRunCode.speedrunMenuInit, ee, data); UndertaleCode ie = data.Code.ByName("gml_Script_cKeys_beginstep"); ie.AppendGML(RabbitRunCode.tasBeginStepInput, data); UndertaleCode oe = data.Code.ByName("gml_Object_obj_init_Create_0"); oe.AppendGML(@"global.playRun = false; global.watchRun = false; global.speedrunning = true; global.inrun = false; global.onehun = false;//one hundred percent global.allbun = false;//all cuties global.anyper = false;//any percent", data); UndertaleCode ue = data.Code.ByName("gml_Script_SaveStringToFile"); ue.ReplaceGML(RabbitRunCode.saveStringFile, data); UndertaleSprite mico = data.Sprites.ByName("spr_menuicons"); UndertaleSprite.TextureEntry te = new UndertaleSprite.TextureEntry(); UndertaleTexturePageItem ti = mico.Textures[1].Texture; UndertaleTexturePageItem to = data.Sprites.ByName("spr_antibunidle").Textures[0].Texture; te.Texture = new UndertaleTexturePageItem(); te.Texture.TargetX = ti.TargetX; te.Texture.TargetY = ti.TargetY; te.Texture.SourceX = to.SourceX; te.Texture.SourceY = to.SourceY; te.Texture.BoundingHeight = ti.BoundingHeight; te.Texture.BoundingWidth = ti.BoundingWidth; te.Texture.SourceWidth = 16; te.Texture.TargetWidth = 16; te.Texture.SourceHeight = 15; te.Texture.TargetHeight = 15; te.Texture.TexturePage = to.TexturePage; data.TexturePageItems.Add(te.Texture); mico.Textures.Insert(2, te); }