From c68fb6fc1f54cfd50769be13b5ae4bedc7ec78d5 Mon Sep 17 00:00:00 2001 From: "Joshua \"Yoshi\" Askharoun" Date: Fri, 28 Nov 2025 18:06:42 -0600 Subject: [PATCH] Begin work on UIXC debug command --- UIXC/Commands/DebugCommand.cs | 126 ++ UIXC/Commands/DecompileCommand.cs | 23 +- UIXC/DebugSymbolResolver.cs | 57 + UIXC/Program.cs | 4 + UIXC/Properties/launchSettings.json | 5 +- UIXC/UIXC.csproj | 1 + test/NowPlayingMusicBackground_decomp.uix | 1040 ++++++++--------- .../NOWPLAYINGMUSICBACKGROUND.UIX.fsym.json | 751 ++++++++++++ 8 files changed, 1474 insertions(+), 533 deletions(-) create mode 100644 UIXC/Commands/DebugCommand.cs create mode 100644 UIXC/DebugSymbolResolver.cs create mode 100644 test/syms/NOWPLAYINGMUSICBACKGROUND.UIX.fsym.json diff --git a/UIXC/Commands/DebugCommand.cs b/UIXC/Commands/DebugCommand.cs new file mode 100644 index 0000000..09ea5a9 --- /dev/null +++ b/UIXC/Commands/DebugCommand.cs @@ -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 +{ + 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("> ")); + 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 ")] + 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 ")] + public Uri ServerUri { get; init; } = DebugRemoting.DEFAULT_TCP_URI; + } +} diff --git a/UIXC/Commands/DecompileCommand.cs b/UIXC/Commands/DecompileCommand.cs index bf0e6f0..61a5632 100644 --- a/UIXC/Commands/DecompileCommand.cs +++ b/UIXC/Commands/DecompileCommand.cs @@ -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 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 } 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 [Description("The language to decompile to.")] [CommandOption("-l|--lang ")] public SourceLanguage Language { get; init; } = SourceLanguage.Asm; + + [Description("The directory to write file symbols to.")] + [CommandOption("--symbols ")] + public string? SymbolDir { get; init; } } } diff --git a/UIXC/DebugSymbolResolver.cs b/UIXC/DebugSymbolResolver.cs new file mode 100644 index 0000000..3220a2f --- /dev/null +++ b/UIXC/DebugSymbolResolver.cs @@ -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); + } +} diff --git a/UIXC/Program.cs b/UIXC/Program.cs index 1bd73bc..93bc656 100644 --- a/UIXC/Program.cs +++ b/UIXC/Program.cs @@ -19,6 +19,10 @@ internal class Program .WithAlias("d") .WithDescription("Decompiles the given compiled UIX to a source language."); + config.AddCommand("debug") + .WithAlias("dbg") + .WithDescription("Starts an Iris debugger client."); + config.AddCommand("resx") .WithDescription("Generates a RESX file compatible with the CLR DLL resource loader."); diff --git a/UIXC/Properties/launchSettings.json b/UIXC/Properties/launchSettings.json index efeca68..2b7d99b 100644 --- a/UIXC/Properties/launchSettings.json +++ b/UIXC/Properties/launchSettings.json @@ -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", diff --git a/UIXC/UIXC.csproj b/UIXC/UIXC.csproj index 870377e..fc19f54 100644 --- a/UIXC/UIXC.csproj +++ b/UIXC/UIXC.csproj @@ -9,6 +9,7 @@ + diff --git a/test/NowPlayingMusicBackground_decomp.uix b/test/NowPlayingMusicBackground_decomp.uix index f7fccd3..afba633 100644 --- a/test/NowPlayingMusicBackground_decomp.uix +++ b/test/NowPlayingMusicBackground_decomp.uix @@ -12,7 +12,7 @@ - + @@ -24,133 +24,119 @@ - - - - - - - - - + + + + + + + + - +GridDisplayed = true; +]]> + @@ -178,18 +164,24 @@ Page.ShowingArtistBio = false; - - + + @@ -243,112 +235,114 @@ GridInfo.PlayAnimation(animations:Animations.MixClientRestore); - - - - - - - - - - - - + + + + + + + + + + - +]]> + + @@ -377,59 +371,77 @@ OnAnimationClipComplete.Invoke(); - - - - - - - - - - - + + + + + + + + + + +if (IsFaded) +{ + if (WindowState.InputActive || ShowState.ChosenValue != "Faded") + { + fadeIn = true; + } +} +else +{ + if (ShowState.ChosenValue == "Faded" && !WindowState.InputActive) + { + fadeOut = true; + } +} + +if (fadeOut) +{ + IsFaded = true; + Form.PlayAnimation(animations:Animations.NowPlayingGridFadeOut); +} +else +{ + if (fadeIn) + { + IsFaded = false; + Form.PlayAnimation(animations:Animations.NowPlayingGridFadeIn); + } +} +]]> @@ -525,24 +537,29 @@ Form.PlayAnimation(animations:Animations.NowPlayingGridFadeIn); - - - - + + + + @@ -560,22 +577,24 @@ zuneUI:ZuneShell.DefaultInstance.CurrentPage.OverlayUI = null; - - - + + + @@ -601,7 +620,7 @@ MoreInfoActions.ZuneMediaId = zuneUI:GuidHelper.Empty; - + @@ -622,123 +641,76 @@ MoreInfoActions.ZuneMediaId = zuneUI:GuidHelper.Empty; - - + - - - - - +if (roundedFitHorizontalBlocks > DesiredHorizontalBlocks) +{ + DesiredHorizontalBlocks = roundedFitHorizontalBlocks; +} + +if (roundedFitVerticalBlocks > DesiredVerticalBlocks) +{ + DesiredVerticalBlocks = roundedFitVerticalBlocks; +} + +if (DesiredHorizontalBlocks > C.MaximumBlocks.Width) +{ + DesiredHorizontalBlocks = C.MaximumBlocks.Width; +} + +if (DesiredVerticalBlocks > C.MaximumBlocks.Height) +{ + DesiredVerticalBlocks = C.MaximumBlocks.Height; +} +]]> + + + + - + @@ -821,70 +793,79 @@ PalettePicker.ChangeMood.Invoke(); - - - - - + + + + + - + - + = LocalAlbumArt.Count) + { + Index = 0; + } + + album = (schema:Album)LocalAlbumArt[Index]; + TransportControls.DisableSlowDataThumbnailExtraction(album); + Index = Index + 1; + } + return album; } ]]> diff --git a/test/syms/NOWPLAYINGMUSICBACKGROUND.UIX.fsym.json b/test/syms/NOWPLAYINGMUSICBACKGROUND.UIX.fsym.json new file mode 100644 index 0000000..f5d879d --- /dev/null +++ b/test/syms/NOWPLAYINGMUSICBACKGROUND.UIX.fsym.json @@ -0,0 +1,751 @@ +{ + "OriginalFileName": null, + "CompiledFileName": "file://E:\\Documents\\REProj\\Zune\\Resources\\ZuneShellResources48\\NOWPLAYINGMUSICBACKGROUND.UIX", + "SourceFileName": null, + "SourceMap": { + "9": [ + "5:8", + "5:72" + ], + "1253": [ + "10:8", + "10:100" + ], + "1267": [ + "11:8", + "11:86" + ], + "1428": [ + "14:8", + "14:122" + ], + "1439": [ + "15:8", + "15:152" + ], + "1444": [ + "16:8", + "16:131" + ], + "1470": [ + "17:8", + "17:78" + ], + "1479": [ + "18:8", + "18:101" + ], + "1485": [ + "19:8", + "19:100" + ], + "1490": [ + "20:8", + "20:90" + ], + "1498": [ + "21:8", + "21:74" + ], + "1506": [ + "22:8", + "22:74" + ], + "1514": [ + "23:8", + "23:88" + ], + "1522": [ + "24:8", + "24:179" + ], + "1424": [ + "141:6", + "153:11" + ], + "1276": [ + "142:8", + "152:9" + ], + "1302": [ + "144:12", + "144:68" + ], + "1305": [ + "147:12", + "147:193" + ], + "1326": [ + "148:12", + "148:307" + ], + "1376": [ + "149:12", + "149:187" + ], + "1406": [ + "150:12", + "150:92" + ], + "1631": [ + "157:8", + "163:15" + ], + "1655": [ + "158:10", + "162:11" + ], + "1675": [ + "164:8", + "164:102" + ], + "1768": [ + "186:6", + "197:11" + ], + "1681": [ + "187:8", + "196:9" + ], + "1690": [ + "189:12", + "193:43" + ], + "1738": [ + "190:14", + "192:15" + ], + "1711": [ + "191:16", + "191:129" + ], + "1747": [ + "194:12", + "194:170" + ], + "1785": [ + "204:8", + "204:73" + ], + "2909": [ + "213:8", + "213:80" + ], + "2924": [ + "214:8", + "214:83" + ], + "2939": [ + "215:8", + "215:89" + ], + "2954": [ + "216:8", + "216:95" + ], + "2968": [ + "217:8", + "217:86" + ], + "3029": [ + "220:8", + "230:25" + ], + "3266": [ + "221:10", + "229:10" + ], + "3277": [ + "231:8", + "231:91" + ], + "3282": [ + "232:8", + "232:70" + ], + "3290": [ + "233:8", + "233:103" + ], + "3310": [ + "234:8", + "234:120" + ], + "3321": [ + "235:8", + "235:131" + ], + "3025": [ + "347:6", + "359:11" + ], + "2977": [ + "348:8", + "358:9" + ], + "2980": [ + "350:12", + "350:110" + ], + "3001": [ + "351:12", + "356:8" + ], + "3013": [ + "353:16", + "353:125" + ], + "3019": [ + "354:16", + "354:127" + ], + "3956": [ + "364:8", + "364:102" + ], + "4701": [ + "367:8", + "367:118" + ], + "4709": [ + "368:8", + "368:122" + ], + "4718": [ + "369:8", + "369:110" + ], + "4723": [ + "370:8", + "370:72" + ], + "4731": [ + "371:8", + "371:154" + ], + "4697": [ + "446:6", + "524:11" + ], + "3962": [ + "447:8", + "523:9" + ], + "3983": [ + "449:12", + "468:9" + ], + "4011": [ + "450:14", + "452:15" + ], + "3992": [ + "451:16", + "451:103" + ], + "4014": [ + "454:16", + "466:11" + ], + "4050": [ + "456:20", + "456:136" + ], + "4056": [ + "457:20", + "457:125" + ], + "4059": [ + "460:20", + "464:9" + ], + "4074": [ + "462:24", + "462:268" + ], + "4137": [ + "469:12", + "521:9" + ], + "4155": [ + "471:16", + "471:136" + ], + "4161": [ + "472:16", + "472:140" + ], + "4167": [ + "473:16", + "473:122" + ], + "4173": [ + "474:16", + "474:103" + ], + "4203": [ + "476:14", + "478:15" + ], + "4176": [ + "477:16", + "477:137" + ], + "4206": [ + "480:16", + "484:15" + ], + "4246": [ + "481:18", + "483:15" + ], + "4227": [ + "482:20", + "482:96" + ], + "4255": [ + "485:16", + "489:15" + ], + "4295": [ + "486:18", + "488:15" + ], + "4276": [ + "487:20", + "487:103" + ], + "4304": [ + "490:16", + "494:15" + ], + "4344": [ + "491:18", + "493:15" + ], + "4325": [ + "492:20", + "492:103" + ], + "4353": [ + "495:16", + "499:11" + ], + "4427": [ + "496:18", + "498:15" + ], + "4380": [ + "497:20", + "497:210" + ], + "4433": [ + "500:16", + "504:11" + ], + "4507": [ + "501:18", + "503:15" + ], + "4460": [ + "502:20", + "502:210" + ], + "4513": [ + "505:16", + "509:11" + ], + "4587": [ + "506:18", + "508:15" + ], + "4540": [ + "507:20", + "507:210" + ], + "4593": [ + "510:16", + "519:9" + ], + "4631": [ + "511:18", + "513:15" + ], + "4612": [ + "512:20", + "512:105" + ], + "4634": [ + "515:20", + "515:175" + ], + "4655": [ + "516:20", + "516:177" + ], + "4676": [ + "517:20", + "517:151" + ], + "4958": [ + "528:8", + "528:155" + ], + "4972": [ + "529:8", + "529:72" + ], + "4992": [ + "530:8", + "530:121" + ], + "5009": [ + "531:8", + "531:128" + ], + "5026": [ + "532:8", + "532:124" + ], + "5043": [ + "533:8", + "533:92" + ], + "5060": [ + "534:8", + "534:96" + ], + "5066": [ + "537:8", + "537:154" + ], + "5257": [ + "567:8", + "567:163" + ], + "5271": [ + "568:8", + "568:72" + ], + "5291": [ + "569:8", + "569:122" + ], + "5308": [ + "570:8", + "570:129" + ], + "5325": [ + "571:8", + "571:125" + ], + "5342": [ + "572:8", + "572:112" + ], + "5359": [ + "573:8", + "573:96" + ], + "5366": [ + "576:8", + "576:110" + ], + "5371": [ + "577:8", + "577:132" + ], + "5398": [ + "602:8", + "602:96" + ], + "5415": [ + "603:8", + "603:120" + ], + "5432": [ + "604:8", + "604:130" + ], + "5449": [ + "605:8", + "605:134" + ], + "5466": [ + "606:8", + "606:118" + ], + "5483": [ + "607:8", + "607:128" + ], + "5500": [ + "608:8", + "608:132" + ], + "5517": [ + "609:8", + "609:118" + ], + "5534": [ + "610:8", + "610:128" + ], + "5551": [ + "611:8", + "611:132" + ], + "6903": [ + "617:8", + "617:160" + ], + "7011": [ + "620:8", + "620:70" + ], + "7019": [ + "621:8", + "621:130" + ], + "7027": [ + "622:8", + "622:152" + ], + "7041": [ + "623:8", + "623:152" + ], + "7046": [ + "624:8", + "624:86" + ], + "7054": [ + "625:8", + "625:84" + ], + "7062": [ + "626:8", + "626:70" + ], + "7070": [ + "627:8", + "627:78" + ], + "7078": [ + "628:8", + "633:22" + ], + "7096": [ + "629:10", + "632:11" + ], + "7104": [ + "634:8", + "634:70" + ], + "7115": [ + "635:8", + "635:96" + ], + "7149": [ + "636:8", + "636:229" + ], + "7154": [ + "637:8", + "637:72" + ], + "7162": [ + "638:8", + "638:126" + ], + "7188": [ + "639:8", + "639:81" + ], + "7199": [ + "640:8", + "640:90" + ], + "7207": [ + "641:8", + "641:93" + ], + "7007": [ + "738:6", + "749:11" + ], + "6944": [ + "739:8", + "748:9" + ], + "6947": [ + "741:12", + "741:73" + ], + "6959": [ + "742:12", + "746:12" + ], + "6995": [ + "743:14", + "745:11" + ], + "6974": [ + "744:16", + "744:119" + ], + "7393": [ + "753:8", + "753:196" + ], + "7416": [ + "754:8", + "754:180" + ], + "7389": [ + "762:6", + "769:11" + ], + "7350": [ + "763:8", + "768:11" + ], + "7380": [ + "765:12", + "765:116" + ], + "7386": [ + "766:12", + "766:117" + ], + "8105": [ + "778:8", + "778:98" + ], + "8329": [ + "781:8", + "781:130" + ], + "8337": [ + "782:8", + "782:286" + ], + "8385": [ + "783:8", + "783:285" + ], + "8433": [ + "784:8", + "784:287" + ], + "8481": [ + "785:8", + "790:13" + ], + "8490": [ + "787:12", + "787:82" + ], + "8508": [ + "788:12", + "788:82" + ], + "8531": [ + "791:8", + "791:72" + ], + "8540": [ + "792:8", + "792:107" + ], + "8545": [ + "793:8", + "793:127" + ], + "8325": [ + "878:6", + "911:11" + ], + "8111": [ + "879:8", + "910:9" + ], + "8145": [ + "881:12", + "886:19" + ], + "8163": [ + "883:16", + "883:123" + ], + "8171": [ + "884:16", + "884:105" + ], + "8179": [ + "887:12", + "891:19" + ], + "8197": [ + "889:16", + "889:134" + ], + "8205": [ + "894:12", + "894:140" + ], + "8241": [ + "895:12", + "908:11" + ], + "8270": [ + "897:16", + "901:19" + ], + "8288": [ + "899:20", + "899:150" + ], + "8296": [ + "902:16", + "906:19" + ], + "8314": [ + "904:20", + "904:148" + ], + "8941": [ + "915:8", + "915:92" + ], + "8958": [ + "916:8", + "916:95" + ], + "8975": [ + "917:8", + "917:93" + ], + "8992": [ + "918:8", + "918:86" + ], + "9001": [ + "921:8", + "921:78" + ], + "9006": [ + "922:8", + "922:109" + ], + "9018": [ + "923:8", + "923:89" + ], + "9026": [ + "924:8", + "924:152" + ] + } +} \ No newline at end of file