Add 'unsupported' homebrew support & 'XeUnshackle'

This commit is contained in:
Pdawg11239
2025-04-02 01:40:06 -04:00
parent ed41cf84d4
commit 55cfed148a
9 changed files with 83 additions and 42 deletions

View File

@@ -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
{

View File

@@ -33,18 +33,15 @@ namespace BadBuilder
);
}
static bool FormatDisk(List<DiskInfo> 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;
});

View File

@@ -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.");
}
}
}

View File

@@ -17,7 +17,7 @@ namespace BadBuilder
);
}
public static List<HomebrewApp> ManageHomebrewApps()
internal static List<HomebrewApp> ManageHomebrewApps()
{
var homebrewApps = new List<HomebrewApp>();
@@ -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<string>("[#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;
}
#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<string>("[#ffac4d]No .xex files found.[/] Enter entry point:")
.Validate(path => File.Exists(path) ? ValidationResult.Success() : ValidationResult.Error("File not found"))
),
new TextPrompt<string>("[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<string>()
new SelectionPrompt<string?>()
.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();

View File

@@ -7,7 +7,7 @@ namespace BadBuilder.Helpers
{
internal static class DiskHelper
{
public static List<DiskInfo> GetDisks()
internal static List<DiskInfo> GetDisks()
{
var disks = new List<DiskInfo>();

View File

@@ -13,6 +13,7 @@ namespace BadBuilder.Helpers
List<string> 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)
};

View File

@@ -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());
}
}
}
}

View File

@@ -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<ArchiveItem> downloadedFiles = DownloadRequiredFiles().Result;
ExtractFiles(downloadedFiles).Wait();
ClearConsole();
string selectedDefaultApp = AnsiConsole.Prompt(
new SelectionPrompt<string>()
.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<string>()

View File

@@ -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;
}
}