diff --git a/BadBuilder.Formatter/DiskFormatter.cs b/BadBuilder.Formatter/DiskFormatter.cs index 7aa1d75..ead7ff4 100644 --- a/BadBuilder.Formatter/DiskFormatter.cs +++ b/BadBuilder.Formatter/DiskFormatter.cs @@ -9,7 +9,6 @@ using Windows.Win32.Storage.FileSystem; using static Windows.Win32.PInvoke; using static BadBuilder.Formatter.Constants; using static BadBuilder.Formatter.Utilities; -using Windows.Win32.Foundation; namespace BadBuilder.Formatter { @@ -17,7 +16,7 @@ namespace BadBuilder.Formatter { public static unsafe string FormatVolume(char driveLetter, long diskSize) { - if (diskSize < 32 * GB) + if (diskSize < 31 * GB) // Just a safeguard to ensure that we never run into close calls with disk size. { Process process = new Process { diff --git a/BadBuilder/ConsoleExperiences/DiskExperience.cs b/BadBuilder/ConsoleExperiences/DiskExperience.cs index 3d5c98e..5ca4364 100644 --- a/BadBuilder/ConsoleExperiences/DiskExperience.cs +++ b/BadBuilder/ConsoleExperiences/DiskExperience.cs @@ -33,18 +33,15 @@ namespace BadBuilder ); } - static bool FormatDisk(List disks, string selectedDisk) + static bool FormatDisk(DiskInfo disk) { - int diskIndex = disks.FindIndex(disk => $"{disk.DriveLetter} ({disk.SizeFormatted}) - {disk.Type}" == selectedDisk); bool ret = true; string output = string.Empty; - AnsiConsole.Status().SpinnerStyle(LightOrangeStyle).Start($"[#76B900]Formatting disk[/] {selectedDisk}", async ctx => + AnsiConsole.Status().SpinnerStyle(LightOrangeStyle).Start($"[#76B900]Formatting disk[/] {disk.DriveLetter} ({disk.SizeFormatted}) - {disk.Type}", async ctx => { - if (diskIndex == -1) return; - ClearConsole(); - output = DiskHelper.FormatDisk(disks[diskIndex]); + output = DiskHelper.FormatDisk(disk); if (output != string.Empty) ret = false; }); diff --git a/BadBuilder/ConsoleExperiences/ExtractExperience.cs b/BadBuilder/ConsoleExperiences/ExtractExperience.cs index b06d441..9468984 100644 --- a/BadBuilder/ConsoleExperiences/ExtractExperience.cs +++ b/BadBuilder/ConsoleExperiences/ExtractExperience.cs @@ -24,9 +24,6 @@ namespace BadBuilder await ArchiveHelper.ExtractFileAsync(item.name, item.path, task); })); }); - - string status = "[+]"; - AnsiConsole.MarkupInterpolated($"[#76B900]{status}[/] [bold]{filesToExtract.Count()}[/] files extracted."); } } } \ No newline at end of file diff --git a/BadBuilder/ConsoleExperiences/HomebrewExperience.cs b/BadBuilder/ConsoleExperiences/HomebrewExperience.cs index df9904a..6c8e39d 100644 --- a/BadBuilder/ConsoleExperiences/HomebrewExperience.cs +++ b/BadBuilder/ConsoleExperiences/HomebrewExperience.cs @@ -17,7 +17,7 @@ namespace BadBuilder ); } - public static List ManageHomebrewApps() + internal static List ManageHomebrewApps() { var homebrewApps = new List(); @@ -37,11 +37,7 @@ namespace BadBuilder var newApp = AddHomebrewApp(); if (newApp != null) - { homebrewApps.Add(newApp.Value); - AnsiConsole.Status() - .Start("Adding...", ctx => ctx.Spinner(Spinner.Known.Dots2)); - } break; case "View Added Apps": @@ -85,31 +81,30 @@ namespace BadBuilder string folderPath = AnsiConsole.Ask("[#FFA500]Enter the folder path for the app:[/]"); if (!Directory.Exists(folderPath)) { - AnsiConsole.MarkupLine("[#ffac4d]Invalid folder path. Please try again.[/]"); + AnsiConsole.MarkupLine("[#ffac4d]Invalid folder path. Please try again.\n[/]"); return null; } - string[] xexFiles = Directory.GetFiles(folderPath, "*.xex"); - if (xexFiles.Length == 0) - { - AnsiConsole.MarkupLine("[#ffac4d]No XEX files found in this folder.[/]"); - return null; - } + string[] xexFiles = Directory.GetFiles(folderPath, "*.xex"); +#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type. +#pragma warning disable CS8714 // The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match 'notnull' constraint. string entryPoint = xexFiles.Length switch { 0 => AnsiConsole.Prompt( - new TextPrompt("[#ffac4d]No .xex files found.[/] Enter entry point:") - .Validate(path => File.Exists(path) ? ValidationResult.Success() : ValidationResult.Error("File not found")) - ), + new TextPrompt("[grey]No .xex files found in this folder.[/] [#FFA500]Enter the path to the entry point:[/]") + .Validate(path => File.Exists(path.Trim().Trim('"')) && Path.GetExtension(path.Trim().Trim('"')) == ".xex" ? ValidationResult.Success() : ValidationResult.Error("[#ffac4d]File not found or is not an XEX file.[/]\n")) + ).Trim().Trim('"'), 1 => xexFiles[0], _ => AnsiConsole.Prompt( - new SelectionPrompt() + new SelectionPrompt() .Title("[#FFA500]Select entry point:[/]") .HighlightStyle(PeachStyle) .AddChoices(xexFiles.Select(Path.GetFileName)) ) }; +#pragma warning restore CS8714 // The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match 'notnull' constraint. +#pragma warning restore CS8600 // Converting null literal or possible null value to non-nullable type. ClearConsole(); diff --git a/BadBuilder/Helpers/DiskHelper.cs b/BadBuilder/Helpers/DiskHelper.cs index f7e48e3..83d27d5 100644 --- a/BadBuilder/Helpers/DiskHelper.cs +++ b/BadBuilder/Helpers/DiskHelper.cs @@ -7,7 +7,7 @@ namespace BadBuilder.Helpers { internal static class DiskHelper { - public static List GetDisks() + internal static List GetDisks() { var disks = new List(); diff --git a/BadBuilder/Helpers/DownloadHelper.cs b/BadBuilder/Helpers/DownloadHelper.cs index b516150..466ece5 100644 --- a/BadBuilder/Helpers/DownloadHelper.cs +++ b/BadBuilder/Helpers/DownloadHelper.cs @@ -13,6 +13,7 @@ namespace BadBuilder.Helpers List repos = [ "grimdoomer/Xbox360BadUpdate", + "Byrom90/XeUnshackle", "FreeMyXe/FreeMyXe" ]; @@ -28,6 +29,7 @@ namespace BadBuilder.Helpers var name when name.Contains("Free", StringComparison.OrdinalIgnoreCase) => "FreeMyXe", var name when name.Contains("Tools", StringComparison.OrdinalIgnoreCase) => "BadUpdate Tools", var name when name.Contains("BadUpdate", StringComparison.OrdinalIgnoreCase) => "BadUpdate", + var name when name.Contains("XeUnshackle", StringComparison.OrdinalIgnoreCase) => "XeUnshackle", _ => asset.Name.Substring(0, asset.Name.Length - 4) }; diff --git a/BadBuilder/Helpers/PatchHelper.cs b/BadBuilder/Helpers/PatchHelper.cs index 16c6858..7eaaeba 100644 --- a/BadBuilder/Helpers/PatchHelper.cs +++ b/BadBuilder/Helpers/PatchHelper.cs @@ -1,4 +1,5 @@ -using System.Diagnostics; +using Spectre.Console; +using System.Diagnostics; namespace BadBuilder.Helpers { @@ -12,8 +13,8 @@ namespace BadBuilder.Helpers { FileName = xexToolPath, Arguments = $"-m r -r a \"{xexPath}\"", - RedirectStandardOutput = false, - RedirectStandardError = false, + RedirectStandardOutput = true, + RedirectStandardError = true, UseShellExecute = false, CreateNoWindow = true } @@ -21,6 +22,13 @@ namespace BadBuilder.Helpers process.Start(); await process.WaitForExitAsync(); + + if (process.ExitCode != 0) + { + string status = "[-]"; + AnsiConsole.MarkupLineInterpolated($"\n[#FF7200]{status}[/] The program {Path.GetFileNameWithoutExtension(xexPath)} was unable to be patched. XexTool output:"); + Console.WriteLine(process.StandardError.ReadToEnd()); + } } } } \ No newline at end of file diff --git a/BadBuilder/Program.cs b/BadBuilder/Program.cs index 2fd8d5c..91605ab 100644 --- a/BadBuilder/Program.cs +++ b/BadBuilder/Program.cs @@ -25,6 +25,8 @@ namespace BadBuilder static ActionQueue actionQueue = new(); + static DiskInfo targetDisk = new("Z:\\", "Fixed", 0, "", 0, int.MaxValue); // Default values just incase. + static void Main(string[] args) { ShowWelcomeMessage(); @@ -40,10 +42,13 @@ namespace BadBuilder string selectedDisk = PromptDiskSelection(disks); TargetDriveLetter = selectedDisk.Substring(0, 3); + int diskIndex = disks.FindIndex(disk => $"{disk.DriveLetter} ({disk.SizeFormatted}) - {disk.Type}" == selectedDisk); + targetDisk = disks[diskIndex]; + bool confirmation = PromptFormatConfirmation(selectedDisk); if (confirmation) { - if (!FormatDisk(disks, selectedDisk)) continue; + if (!FormatDisk(targetDisk)) continue; break; } } @@ -51,13 +56,21 @@ namespace BadBuilder List downloadedFiles = DownloadRequiredFiles().Result; ExtractFiles(downloadedFiles).Wait(); + ClearConsole(); + string selectedDefaultApp = AnsiConsole.Prompt( + new SelectionPrompt() + .Title("Which program should be launched by BadUpdate?") + .HighlightStyle(GreenStyle) + .AddChoices( + "FreeMyXe", + "XeUnshackle" + ) + ); - AnsiConsole.MarkupLine("\n\n[#76B900]{0}[/] Copying requried files and folders.", Markup.Escape("[*]")); + AnsiConsole.MarkupLine("[#76B900]{0}[/] Copying requried files and folders.", Markup.Escape("[*]")); foreach (var folder in Directory.GetDirectories($@"{EXTRACTED_DIR}")) { - var folderName = folder.Split("\\").Last(); - - switch (folderName) + switch (folder.Split("\\").Last()) { case "XEXMenu": EnqueueMirrorDirectory( @@ -68,6 +81,7 @@ namespace BadBuilder break; case "FreeMyXe": + if (selectedDefaultApp != "FreeMyXe") break; EnqueueFileCopy( Path.Combine(folder, "FreeMyXe.xex"), Path.Combine(TargetDriveLetter, "BadUpdatePayload", "default.xex"), @@ -75,6 +89,17 @@ namespace BadBuilder ); break; + case "XeUnshackle": + if (selectedDefaultApp != "XeUnshackle") break; + string subFolderPath = Directory.GetDirectories(folder).FirstOrDefault(); + File.Delete(Path.Combine(subFolderPath, "README - IMPORTANT.txt")); + EnqueueMirrorDirectory( + subFolderPath, + TargetDriveLetter, + 9 + ); + break; + case "BadUpdate": actionQueue.EnqueueAction(async () => { @@ -82,7 +107,7 @@ namespace BadBuilder writer.WriteLine("USB Storage Device"); using (StreamWriter writer = new(Path.Combine(TargetDriveLetter, "info.txt"))) - writer.WriteLine("This drive was created with BadBuilder by Pdawg.\nFind more info here: https://github.com/Pdawg-bytes/BadBuilder"); + writer.WriteLine($"This drive was created with BadBuilder by Pdawg.\nFind more info here: https://github.com/Pdawg-bytes/BadBuilder\nConfiguration: \n- BadUpdate target binary: {selectedDefaultApp}"); Directory.CreateDirectory(Path.Combine(TargetDriveLetter, "Apps")); await FileSystemHelper.MirrorDirectoryAsync(Path.Combine(folder, "Rock Band Blitz"), TargetDriveLetter); @@ -109,14 +134,18 @@ namespace BadBuilder }, 6); break; - default: throw new Exception($"Unexpected directory in working folder: {folder}"); + default: throw new Exception($"[-] Unexpected directory in working folder: {folder}"); } } actionQueue.ExecuteActionsAsync().Wait(); + File.AppendAllText(Path.Combine(TargetDriveLetter, "info.txt"), $"- Disk formatted using {(targetDisk.TotalSize < 31 * GB ? "Windows \"format.com\"" : "BadBuilder Large FAT32 formatter")}\n"); + File.AppendAllText(Path.Combine(TargetDriveLetter, "info.txt"), $"- Disk total size: {targetDisk.TotalSize} bytes\n"); + ClearConsole(); if (!PromptAddHomebrew()) { + WriteHomebrewLog(1); AnsiConsole.MarkupLine("\n[#76B900]{0}[/] Your USB drive is ready to go.", Markup.Escape("[+]")); Console.Write("\nPress any key to exit..."); Console.ReadKey(); @@ -134,12 +163,14 @@ namespace BadBuilder await Task.WhenAll(homebrewApps.Select(async item => { await FileSystemHelper.MirrorDirectoryAsync(item.folder, Path.Combine(TargetDriveLetter, "Apps", item.name)); - await PatchHelper.PatchXexAsync(Path.Combine(TargetDriveLetter, "Apps", item.name, Path.GetFileName(item.entryPoint)), XexToolPath); + await PatchHelper.PatchXexAsync(item.entryPoint, XexToolPath); })); }).Wait(); + WriteHomebrewLog(homebrewApps.Count + 1); + string status = "[+]"; - AnsiConsole.MarkupInterpolated($"\n[#76B900]{status}[/] [bold]{homebrewApps.Count()}[/] apps copied.\n"); + AnsiConsole.MarkupLineInterpolated($"\n[#76B900]{status}[/] [bold]{homebrewApps.Count}[/] apps copied."); AnsiConsole.MarkupLine("\n[#76B900]{0}[/] Your USB drive is ready to go.", Markup.Escape("[+]")); @@ -163,6 +194,13 @@ namespace BadBuilder }, priority); } + static void WriteHomebrewLog(int count) + { + string logPath = Path.Combine(TargetDriveLetter, "info.txt"); + string logEntry = $"- {count} homebrew app(s) added (including Simple 360 NAND Flasher)\n"; + File.AppendAllText(logPath, logEntry); + } + static void ShowWelcomeMessage() => AnsiConsole.Markup( """ @@ -173,12 +211,12 @@ namespace BadBuilder [#CCE388]██████╔╝██║ ██║██████╔╝██████╔╝╚██████╔╝██║███████╗██████╔╝███████╗██║ ██║[/] [#CCE388]╚═════╝ ╚═╝ ╚═╝╚═════╝ ╚═════╝ ╚═════╝ ╚═╝╚══════╝╚═════╝ ╚══════╝╚═╝ ╚═╝[/] - [#76B900]──────────────────────────────────────────────────────────────────────v0.21a[/] + [#76B900]───────────────────────────────────────────────────────────────────────v0.30[/] ───────────────────────Xbox 360 [#FF7200]BadUpdate[/] USB Builder─────────────────────── [#848589]Created by Pdawg[/] [#76B900]────────────────────────────────────────────────────────────────────────────[/] - """); + """); static string PromptForAction() => AnsiConsole.Prompt( new SelectionPrompt() diff --git a/BadBuilder/Utilities/Constants.cs b/BadBuilder/Utilities/Constants.cs index fe500ab..8029c04 100644 --- a/BadBuilder/Utilities/Constants.cs +++ b/BadBuilder/Utilities/Constants.cs @@ -7,5 +7,10 @@ internal const string EXTRACTED_DIR = $@"{WORKING_DIR}\Extract"; internal const string ContentFolder = "Content\\0000000000000000\\"; + + internal const long KB = 1024L; + internal const long MB = 1048576L; + internal const long GB = 1073741824L; + internal const long TB = 1099511627776L; } } \ No newline at end of file