mirror of
https://github.com/ZuneDev/ZuneUIXTools.git
synced 2026-07-27 13:11:59 -07:00
Begin work on UIXC debug command
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
using Microsoft.Iris.Debug;
|
||||
using Microsoft.Iris.Debug.SystemNet;
|
||||
using Spectre.Console;
|
||||
using Spectre.Console.Cli;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
|
||||
namespace UIXC.Commands;
|
||||
|
||||
public class DebugCommand : Command<DebugCommand.Settings>
|
||||
{
|
||||
private bool isRunning = true;
|
||||
|
||||
public override int Execute(CommandContext context, Settings settings)
|
||||
{
|
||||
if (settings.ServerUri is null)
|
||||
{
|
||||
AnsiConsole.MarkupLine("[red]Missing argument: must specify a debug server to connect to.[/]");
|
||||
return -1;
|
||||
}
|
||||
|
||||
DebugSymbolResolver? symbolResolver = null;
|
||||
|
||||
if (settings.SymbolDir is not null)
|
||||
{
|
||||
Directory.CreateDirectory(settings.SymbolDir);
|
||||
symbolResolver = new(settings.SymbolDir);
|
||||
}
|
||||
|
||||
AnsiConsole.MarkupLineInterpolated($"Connecting to '{settings.ServerUri}'...");
|
||||
|
||||
var c = new NetDebuggerClient(settings.ServerUri);
|
||||
c.Connected += (s, e) =>
|
||||
{
|
||||
AnsiConsole.MarkupLine("[green]Connected[/]");
|
||||
Thread consoleThread = new(() => DebugConsole(c, symbolResolver));
|
||||
consoleThread.Start();
|
||||
};
|
||||
|
||||
c.Start();
|
||||
|
||||
while (isRunning) ;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int DebugConsole(IDebuggerClient client, DebugSymbolResolver? symbolResolver)
|
||||
{
|
||||
client.InterpreterStateChanged += (cmd) =>
|
||||
{
|
||||
AnsiConsole.MarkupLineInterpolated($"[yellow]Application in '{cmd}' state[/]");
|
||||
};
|
||||
|
||||
while (true)
|
||||
{
|
||||
var input = AnsiConsole.Prompt(new TextPrompt<string>("> "));
|
||||
var inputParts = input.Split(' ', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
var command = inputParts[0].ToUpperInvariant();
|
||||
switch (command)
|
||||
{
|
||||
case "BREAK" or "B":
|
||||
string path = inputParts[1];
|
||||
var fileName = Path.GetFileName(path).Split('!')[^1];
|
||||
|
||||
uint breakOffset;
|
||||
|
||||
if (inputParts.Length >= 3 && inputParts[2].StartsWith("0x"))
|
||||
{
|
||||
breakOffset = uint.Parse(inputParts[2][2..], NumberStyles.HexNumber);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (symbolResolver is null)
|
||||
{
|
||||
AnsiConsole.MarkupLine("[red]Setting breakpoints via source code requires debug symbols. Use the --symbols argument to specify a directory.[/]");
|
||||
break;
|
||||
}
|
||||
|
||||
var fsym = symbolResolver.GetForFile(fileName);
|
||||
if (fsym is null)
|
||||
{
|
||||
AnsiConsole.MarkupLineInterpolated($"[red]No symbols loaded for '{fileName}'.[/]");
|
||||
break;
|
||||
}
|
||||
|
||||
var line = int.Parse(inputParts[2]);
|
||||
var column = int.Parse(inputParts[3]);
|
||||
breakOffset = fsym!.OffsetByLineAndColumn(line, column);
|
||||
}
|
||||
|
||||
client.UpdateBreakpoint(new(path, breakOffset));
|
||||
AnsiConsole.MarkupLineInterpolated($"[green]Breakpoint set in '{fileName}' at offset 0x{breakOffset:X4}.[/]");
|
||||
|
||||
break;
|
||||
|
||||
case "CONTINUE" or "C":
|
||||
client.DebuggerCommand = Microsoft.Iris.Debug.Data.InterpreterCommand.Continue;
|
||||
break;
|
||||
|
||||
case "CLEAR":
|
||||
// Not implemented yet, should clear all breakpoints
|
||||
break;
|
||||
|
||||
case "EXIT":
|
||||
isRunning = false;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class Settings : CommandSettings
|
||||
{
|
||||
[Description("The directory containing pre-generated symbols.")]
|
||||
[CommandOption("-s|--symbols <symbolDir>")]
|
||||
public string? SymbolDir { get; init; }
|
||||
|
||||
[Description("Whether to decompile the current file when a breakpoint is hit. Generated symbols will be written to the symbol directory if specified.")]
|
||||
[CommandOption("-d|--decompile")]
|
||||
public bool Decompile { get; init; }
|
||||
|
||||
[Description("The URI of the debug server to connect to.")]
|
||||
[CommandOption("-u|--server <serverUri>")]
|
||||
public Uri ServerUri { get; init; } = DebugRemoting.DEFAULT_TCP_URI;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
using Microsoft.Iris.Asm;
|
||||
using Microsoft.Iris.Data;
|
||||
using Microsoft.Iris.Debug;
|
||||
using Microsoft.Iris.Debug.Symbols;
|
||||
using Microsoft.Iris.DecompXml;
|
||||
using Microsoft.Iris.Markup;
|
||||
using Spectre.Console;
|
||||
@@ -59,7 +61,7 @@ public class DecompileCommand : CompilerCommandBase<DecompileCommand.Settings>
|
||||
foreach (var redirectOption in settings.ImportRedirects ?? [])
|
||||
{
|
||||
var parts = redirectOption.Split('>');
|
||||
MarkupSystem.AddImportRedirect(parts[0], parts[1]);
|
||||
ResourceManager.Instance.AddUriRedirect(parts[0], parts[1]);
|
||||
}
|
||||
|
||||
MarkupSystem.Startup(true);
|
||||
@@ -90,10 +92,23 @@ public class DecompileCommand : CompilerCommandBase<DecompileCommand.Settings>
|
||||
}
|
||||
else if (settings.Language == SourceLanguage.Xml)
|
||||
{
|
||||
var saveDebugSymbols = settings.SymbolDir is not null;
|
||||
|
||||
decompilerMethod = loadResult =>
|
||||
{
|
||||
var decompiler = Decompiler.Load(loadResult);
|
||||
return decompiler.DecompileToSource();
|
||||
var source = decompiler.DecompileToSource(saveDebugSymbols);
|
||||
|
||||
if (settings.SymbolDir is not null && decompiler.DebugSymbols is not null)
|
||||
{
|
||||
var fsymJson = DebugSymbolsJsonParser.Serialize(decompiler.DebugSymbols);
|
||||
var sourceName = Path.GetFileName(decompiler.DebugSymbols.CompiledFileName);
|
||||
var fsymPath = Path.Combine(settings.SymbolDir, $"{sourceName}.fsym.json");
|
||||
|
||||
File.WriteAllText(fsymPath, fsymJson);
|
||||
}
|
||||
|
||||
return source;
|
||||
};
|
||||
}
|
||||
else
|
||||
@@ -179,5 +194,9 @@ public class DecompileCommand : CompilerCommandBase<DecompileCommand.Settings>
|
||||
[Description("The language to decompile to.")]
|
||||
[CommandOption("-l|--lang <lang>")]
|
||||
public SourceLanguage Language { get; init; } = SourceLanguage.Asm;
|
||||
|
||||
[Description("The directory to write file symbols to.")]
|
||||
[CommandOption("--symbols <symbolOutDir>")]
|
||||
public string? SymbolDir { get; init; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
using Microsoft.Iris.Debug.Symbols;
|
||||
|
||||
namespace UIXC;
|
||||
|
||||
public class DebugSymbolResolver(string symbolDir)
|
||||
{
|
||||
private readonly DirectoryInfo _symbolDir = new(symbolDir);
|
||||
|
||||
public ApplicationDebugSymbols? GetForApplication(string application)
|
||||
{
|
||||
var fileName = $"{application}.asym.json";
|
||||
var asymFile = _symbolDir.EnumerateFiles()
|
||||
.FirstOrDefault(f => f.Name == fileName);
|
||||
|
||||
if (asymFile is null)
|
||||
return null;
|
||||
|
||||
using var stream = asymFile.OpenRead();
|
||||
using var reader = new StreamReader(stream);
|
||||
return DebugSymbolsJsonParser.ParseForApplication(reader.ReadToEnd());
|
||||
}
|
||||
|
||||
public FileDebugSymbols? GetForFile(string file, string? application = null)
|
||||
{
|
||||
var fileName = $"{file}.fsym.json";
|
||||
var fsymFile = FindFile(fileName);
|
||||
|
||||
if (fsymFile is not null)
|
||||
{
|
||||
using var stream = fsymFile.OpenRead();
|
||||
using var reader = new StreamReader(stream);
|
||||
return DebugSymbolsJsonParser.ParseForFile(reader.ReadToEnd());
|
||||
}
|
||||
|
||||
if (application is null)
|
||||
return null;
|
||||
|
||||
var asym = GetForApplication(application);
|
||||
if (asym is null)
|
||||
return null;
|
||||
|
||||
return asym.Files.FirstOrDefault(f => f.CompiledFileName == file || f.SourceFileName == file);
|
||||
}
|
||||
|
||||
private FileInfo? FindFile(string fileName)
|
||||
{
|
||||
return _symbolDir
|
||||
.EnumerateFiles()
|
||||
.FirstOrDefault(f => AreEquivalentFileNames(f.Name, fileName));
|
||||
}
|
||||
|
||||
private static bool AreEquivalentFileNames(string fileName1, string fileName2)
|
||||
{
|
||||
// NOTE: Assumes Windows. Should be trivial to support other filesystems later.
|
||||
return fileName1.Equals(fileName2, StringComparison.InvariantCultureIgnoreCase);
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,10 @@ internal class Program
|
||||
.WithAlias("d")
|
||||
.WithDescription("Decompiles the given compiled UIX to a source language.");
|
||||
|
||||
config.AddCommand<DebugCommand>("debug")
|
||||
.WithAlias("dbg")
|
||||
.WithDescription("Starts an Iris debugger client.");
|
||||
|
||||
config.AddCommand<ResxCommand>("resx")
|
||||
.WithDescription("Generates a RESX file compatible with the CLR DLL resource loader.");
|
||||
|
||||
|
||||
@@ -3,8 +3,11 @@
|
||||
"UIXC": {
|
||||
"commandName": "Project",
|
||||
|
||||
"commandLineArgs": "debug --symbols E:\\Repos\\ZuneDev\\ZuneUIXTools\\test\\syms",
|
||||
|
||||
//"commandLineArgs": "decompile E:\\Documents\\REProj\\Zune\\Resources\\ZuneMarketplaceResources48\\MarketplaceData.schema.xml -l xml -A ZuneShell.dll -A UIXControls.dll -A ZuneDBApi.dll -o E:\\Repos\\ZuneDev\\ZuneUIXTools\\test",
|
||||
"commandLineArgs": "decompile E:\\Documents\\REProj\\Zune\\Resources\\ZuneShellResources48\\NowPlayingLand.uix -l xml -A ZuneShell.dll -A UIXControls.dll -A ZuneDBApi.dll -o E:\\Repos\\ZuneDev\\ZuneUIXTools\\test",
|
||||
//"commandLineArgs": "decompile E:\\Documents\\REProj\\Zune\\Resources\\ZuneShellResources48\\DeviceLandElements.uix -l xml -A ZuneShell.dll -A UIXControls.dll -A ZuneDBApi.dll -o E:\\Repos\\ZuneDev\\ZuneUIXTools\\test",
|
||||
//"commandLineArgs": "decompile E:\\Documents\\REProj\\Zune\\Resources\\ZuneShellResources48\\NOWPLAYINGMUSICBACKGROUND.UIX -l xml -A ZuneShell.dll -A UIXControls.dll -A ZuneDBApi.dll -o E:\\Repos\\ZuneDev\\ZuneUIXTools\\test --symbols E:\\Repos\\ZuneDev\\ZuneUIXTools\\test\\syms",
|
||||
//"commandLineArgs": "decompile E:\\Repos\\ZuneDev\\ZuneShell.dll\\libs\\MicrosoftIris\\UIXcontrols\\Resources\\RCDATA\\STYLES.UIX -l xml -A ZuneShell.dll -A UIXControls.dll -A ZuneDBApi.dll -o E:\\Repos\\ZuneDev\\ZuneShell.dll\\ZuneShell\\Resources\\RCDATA\\Dark\\Controls",
|
||||
//"commandLineArgs": "compile E:\\Repos\\ZuneDev\\ZuneUIXTools\\test\\ADDTOCOLLECTION.31_48.UIX -A ZuneShell.dll -A UIXControls.dll -A ZuneDBApi.dll -o E:\\Repos\\ZuneDev\\ZuneUIXTools\\test",
|
||||
//"commandLineArgs": "compile E:\\Documents\\REProj\\Zune\\Resources\\ZuneMarketplaceResources31\\MARKETPLACEDATA.SCHEMA.31_48.XML -A ZuneShell.dll -A UIXControls.dll -A ZuneDBApi.dll -o E:\\Repos\\ZuneDev\\ZuneUIXTools\\test",
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Errata" Version="0.13.0" />
|
||||
<PackageReference Include="OwlCore" Version="0.6.1" />
|
||||
<PackageReference Include="Spectre.Console" Version="0.49.1" />
|
||||
<PackageReference Include="Spectre.Console.Cli" Version="0.49.1" />
|
||||
<ProjectReference Include="..\libs\MicrosoftIris\UIX\UIX.csproj" />
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user