From 299007a8e3e8691073fefc12fb7229c237ee6de4 Mon Sep 17 00:00:00 2001 From: niklascfw Date: Thu, 13 Nov 2025 19:20:28 +0100 Subject: [PATCH] Localize UI text and add copy progress bar --- BadBuilder.Formatter/DiskFormatter.cs | 20 ++++--- BadBuilder.Formatter/NativeMethods.txt | 1 - BadBuilder.Formatter/Utilities.cs | 9 ++- .../ConsoleExperiences/DiskExperience.cs | 10 ++-- .../ConsoleExperiences/DownloadExperience.cs | 58 +++++++++++++++---- .../ConsoleExperiences/ExtractExperience.cs | 2 +- .../ConsoleExperiences/HomebrewExperience.cs | 56 +++++++++--------- BadBuilder/Helpers/ArchiveHelper.cs | 2 + BadBuilder/Helpers/DiskHelper.cs | 2 +- BadBuilder/Helpers/DownloadHelper.cs | 2 +- BadBuilder/Helpers/FileSystemHelper.cs | 2 +- BadBuilder/Helpers/PatchHelper.cs | 2 +- BadBuilder/Program.cs | 54 ++++++++++++----- BadBuilder/Utilities/ActionQueue.cs | 47 +++++++++++---- 14 files changed, 183 insertions(+), 84 deletions(-) diff --git a/BadBuilder.Formatter/DiskFormatter.cs b/BadBuilder.Formatter/DiskFormatter.cs index ead7ff4..a27d9c3 100644 --- a/BadBuilder.Formatter/DiskFormatter.cs +++ b/BadBuilder.Formatter/DiskFormatter.cs @@ -5,6 +5,7 @@ using Windows.Win32.System.Ioctl; using Microsoft.Win32.SafeHandles; using System.Runtime.InteropServices; using Windows.Win32.Storage.FileSystem; +using System.Runtime.Versioning; using static Windows.Win32.PInvoke; using static BadBuilder.Formatter.Constants; @@ -12,6 +13,7 @@ using static BadBuilder.Formatter.Utilities; namespace BadBuilder.Formatter { + [SupportedOSPlatform("windows")] public static class DiskFormatter { public static unsafe string FormatVolume(char driveLetter, long diskSize) @@ -35,30 +37,30 @@ namespace BadBuilder.Formatter process.WaitForExit(); if (process.ExitCode == 0) return ""; - else return Error($"Native format failed with exit code: {process.ExitCode}"); + else return Error($"Die native Formatierung ist mit dem Exitcode {process.ExitCode} fehlgeschlagen."); } string devicePath = $"\\\\.\\{driveLetter}:"; uint volumeID = GetVolumeID(); SafeFileHandle driveHandle = OpenDeviceHandle(devicePath); - if (driveHandle.IsInvalid) return Error("Unable to open device. GetLastError: " + Marshal.GetLastWin32Error()); + if (driveHandle.IsInvalid) return Error("Gerät konnte nicht geöffnet werden. GetLastError: " + Marshal.GetLastWin32Error()); if (!EnableExtendedDASDIO(driveHandle) || !LockDevice(driveHandle)) - return Error($"Failed to initialize device access. GetLastError: {Marshal.GetLastWin32Error()}"); + return Error($"Gerätezugriff konnte nicht initialisiert werden. GetLastError: {Marshal.GetLastWin32Error()}"); DISK_GEOMETRY diskGeometry; if (!TryGetDiskGeometry(driveHandle, out diskGeometry)) - return Error($"Failed to get disk geometry. GetLastError: {Marshal.GetLastWin32Error()}"); + return Error($"Datenträgergeometrie konnte nicht ermittelt werden. GetLastError: {Marshal.GetLastWin32Error()}"); PARTITION_INFORMATION partitionInfo; bool isGPT = false; if (!TryGetPartitionInfo(driveHandle, ref diskGeometry, out partitionInfo, out isGPT)) - return Error($"Failed to get partition information. GetLastError: {Marshal.GetLastWin32Error()}"); + return Error($"Partitionsinformationen konnten nicht ermittelt werden. GetLastError: {Marshal.GetLastWin32Error()}"); uint totalSectors = (uint)(partitionInfo.PartitionLength / diskGeometry.BytesPerSector); if (!IsValidFAT32Size(totalSectors)) - return Error("Invalid drive size for FAT32."); + return Error("Ungültige Laufwerksgröße für FAT32."); FAT32BootSector bootSector = InitializeBootSector(diskGeometry, partitionInfo, totalSectors, volumeID); FAT32FsInfoSector fsInfo = InitializeFsInfo(); @@ -69,12 +71,12 @@ namespace BadBuilder.Formatter return formatOutput; if (!UnlockDevice(driveHandle) || !DismountVolume(driveHandle)) - return Error($"Failed to release the device. GetLastError: {Marshal.GetLastWin32Error()}"); + return Error($"Gerät konnte nicht freigegeben werden. GetLastError: {Marshal.GetLastWin32Error()}"); driveHandle.Dispose(); if (!SetVolumeLabel($"{driveLetter}:", "BADUPDATE")) - return Error($"Unable to set volume label. GetLastError: {Marshal.GetLastWin32Error()}"); + return Error($"Volumenbezeichnung konnte nicht gesetzt werden. GetLastError: {Marshal.GetLastWin32Error()}"); return string.Empty; } @@ -222,7 +224,7 @@ namespace BadBuilder.Formatter uint clusterCount = userAreaSize / bootSector.SectorsPerCluster; if (clusterCount < 65536 || clusterCount > 0x0FFFFFFF) - return Error("The drive's cluster count is out of range (65536 < clusterCount < 0x0FFFFFFF)"); + return Error("Die Clusteranzahl des Laufwerks liegt außerhalb des gültigen Bereichs (65536 < clusterCount < 0x0FFFFFFF)."); fsInfo.FreeClusterCount = clusterCount - 1; diff --git a/BadBuilder.Formatter/NativeMethods.txt b/BadBuilder.Formatter/NativeMethods.txt index bce018a..598401d 100644 --- a/BadBuilder.Formatter/NativeMethods.txt +++ b/BadBuilder.Formatter/NativeMethods.txt @@ -6,7 +6,6 @@ CloseHandle VirtualAlloc VirtualFree SetVolumeLabelW -GUID DISK_GEOMETRY PARTITION_INFORMATION PARTITION_INFORMATION_EX \ No newline at end of file diff --git a/BadBuilder.Formatter/Utilities.cs b/BadBuilder.Formatter/Utilities.cs index 448ad18..259e077 100644 --- a/BadBuilder.Formatter/Utilities.cs +++ b/BadBuilder.Formatter/Utilities.cs @@ -1,12 +1,15 @@ -using Windows.Win32.Foundation; +#pragma warning disable CA1416 +using Windows.Win32.Foundation; using Windows.Win32.System.Memory; using System.Runtime.InteropServices; +using System.Runtime.Versioning; using static Windows.Win32.PInvoke; using static BadBuilder.Formatter.Constants; namespace BadBuilder.Formatter { + [SupportedOSPlatform("windows")] static class Utilities { internal static byte[] StructToBytes(T @struct) where T : struct @@ -80,7 +83,7 @@ namespace BadBuilder.Formatter fixed (byte* pData = &data[0]) { if (!WriteFile(new HANDLE(hDevice.DangerousGetHandle()), pData, numberOfSectors * bytesPerSector, null, null)) - ExitWithError($"Unable to write sectors to FAT32 device, exiting. GetLastError: {Marshal.GetLastWin32Error()}"); + ExitWithError($"Sektoren konnten nicht auf das FAT32-Gerät geschrieben werden. Beende mit Fehler. GetLastError: {Marshal.GetLastWin32Error()}"); } } @@ -100,7 +103,7 @@ namespace BadBuilder.Formatter writeSize = (numberOfSectors > burstSize) ? burstSize : numberOfSectors; if (!WriteFile(new HANDLE(hDevice.DangerousGetHandle()), pZeroSector, writeSize * bytesPerSector, null, null)) - ExitWithError($"Unable to write sectors to FAT32 device, exiting. GetLastError: {Marshal.GetLastWin32Error()}"); + ExitWithError($"Sektoren konnten nicht auf das FAT32-Gerät geschrieben werden. Beende mit Fehler. GetLastError: {Marshal.GetLastWin32Error()}"); numberOfSectors -= writeSize; } diff --git a/BadBuilder/ConsoleExperiences/DiskExperience.cs b/BadBuilder/ConsoleExperiences/DiskExperience.cs index 5ca4364..7f5e838 100644 --- a/BadBuilder/ConsoleExperiences/DiskExperience.cs +++ b/BadBuilder/ConsoleExperiences/DiskExperience.cs @@ -14,7 +14,7 @@ namespace BadBuilder return AnsiConsole.Prompt( new SelectionPrompt() - .Title("Select a disk to format:") + .Title("Wähle ein Laufwerk zum Formatieren aus:") .HighlightStyle(GreenStyle) .AddChoices(choices) ); @@ -23,13 +23,13 @@ namespace BadBuilder static bool PromptFormatConfirmation(string selectedDisk) { return AnsiConsole.Prompt( - new TextPrompt($"[#FF7200 bold]WARNING: [/]Are you sure you would like to format [bold]{selectedDisk.Substring(0, 3)}[/]? All data on this drive will be lost.") + new TextPrompt($"[#FF7200 bold]WARNUNG: [/]Möchtest du [bold]{selectedDisk.Substring(0, 3)}[/] wirklich formatieren? Alle Daten auf diesem Laufwerk gehen verloren.") .AddChoice(true) .AddChoice(false) .DefaultValue(false) .ChoicesStyle(GreenStyle) .DefaultValueStyle(OrangeStyle) - .WithConverter(choice => choice ? "y" : "n") + .WithConverter(choice => choice ? "j" : "n") ); } @@ -38,11 +38,13 @@ namespace BadBuilder bool ret = true; string output = string.Empty; - AnsiConsole.Status().SpinnerStyle(LightOrangeStyle).Start($"[#76B900]Formatting disk[/] {disk.DriveLetter} ({disk.SizeFormatted}) - {disk.Type}", async ctx => + AnsiConsole.Status().SpinnerStyle(LightOrangeStyle).Start($"[#76B900]Formatiere Laufwerk[/] {disk.DriveLetter} ({disk.SizeFormatted}) - {disk.Type}", async ctx => { ClearConsole(); output = DiskHelper.FormatDisk(disk); if (output != string.Empty) ret = false; + + await Task.CompletedTask; }); if (!ret) diff --git a/BadBuilder/ConsoleExperiences/DownloadExperience.cs b/BadBuilder/ConsoleExperiences/DownloadExperience.cs index 8a08e46..fed91b8 100644 --- a/BadBuilder/ConsoleExperiences/DownloadExperience.cs +++ b/BadBuilder/ConsoleExperiences/DownloadExperience.cs @@ -9,6 +9,23 @@ namespace BadBuilder { static async Task> DownloadRequiredFiles() { + bool hasUpdatedDashboard = AnsiConsole.Prompt( + new TextPrompt("Hast du bereits auf Dashboard-Version [bold]17559[/] aktualisiert?") + .AddChoice(true) + .AddChoice(false) + .DefaultValue(true) + .ChoicesStyle(GreenStyle) + .DefaultValueStyle(OrangeStyle) + .WithConverter(choice => choice ? "j" : "n") + ); + + ClearConsole(); + + RequiresDashboardUpdate = !hasUpdatedDashboard; + DownloadItem? dashboardUpdateItem = RequiresDashboardUpdate + ? ("Dashboard Update 17559", "https://cdn.niklascfw.de/files/xbox360/SystemUpdate_17559.zip") + : null; + List items = new() { ("ABadAvatar", "https://cdn.niklascfw.de/files/xbox360/ABadAvatar-publicbeta1.0.zip"), @@ -25,11 +42,11 @@ namespace BadBuilder List choices = items.Select(item => existingFiles.Any(e => e.name == item.name) - ? $"{item.name} [italic gray](already exists)[/]" + ? $"{item.name} [italic gray](bereits vorhanden)[/]" : item.name).ToList(); var prompt = new MultiSelectionPrompt() - .Title("Which files do you already have? [gray](Select all that apply)[/]") + .Title("Welche Dateien hast du bereits? [gray](Mehrfachauswahl möglich)[/]") .PageSize(10) .NotRequired() .HighlightStyle(GreenStyle) @@ -48,6 +65,17 @@ namespace BadBuilder List itemsToDownload = items.Where(item => !selectedItems.Contains(item.name)).ToList(); + if (dashboardUpdateItem.HasValue) + { + string updateFileName = dashboardUpdateItem.Value.url.Split('/').Last(); + string updateDestination = Path.Combine(DOWNLOAD_DIR, updateFileName); + + if (!File.Exists(updateDestination)) + { + itemsToDownload.Add(dashboardUpdateItem.Value); + } + } + itemsToDownload.Sort((a, b) => b.name.Length.CompareTo(a.name.Length)); if (!Directory.Exists($"{DOWNLOAD_DIR}")) @@ -67,7 +95,7 @@ namespace BadBuilder ) .StartAsync(async ctx => { - AnsiConsole.MarkupLine("[#76B900]{0}[/] Downloading required files.", Markup.Escape("[*]")); + AnsiConsole.MarkupLine("[#76B900]{0}[/] Lade erforderliche Dateien herunter.", Markup.Escape("[*]")); await Task.WhenAll(itemsToDownload.Select(async item => { var task = ctx.AddTask(item.name, new ProgressTaskSettings { AutoStart = false }); @@ -76,11 +104,11 @@ namespace BadBuilder }); string status = "[+]"; - AnsiConsole.MarkupInterpolated($"[#76B900]{status}[/] [bold]{itemsToDownload.Count()}[/] download(s) completed.\n"); + AnsiConsole.MarkupInterpolated($"[#76B900]{status}[/] [bold]{itemsToDownload.Count()}[/] Downloads abgeschlossen.\n"); } else { - AnsiConsole.MarkupLine("[italic #76B900]No downloads required. All files already exist.[/]"); + AnsiConsole.MarkupLine("[italic #76B900]Keine Downloads erforderlich. Alle Dateien sind bereits vorhanden.[/]"); } @@ -93,29 +121,39 @@ namespace BadBuilder if (File.Exists(destinationPath)) continue; string existingPath = AnsiConsole.Prompt( - new TextPrompt($"Enter the path to the [bold]{selectedItem}[/] archive:") + new TextPrompt($"Gib den Pfad zum Archiv [bold]{selectedItem}[/] ein:") .PromptStyle(LightOrangeStyle) .Validate(path => { return File.Exists(path.Trim().Trim('"')) ? ValidationResult.Success() - : ValidationResult.Error("[red]File does not exist.[/]"); + : ValidationResult.Error("[red]Datei wurde nicht gefunden.[/]"); }) ).Trim().Trim('"'); try { File.Copy(existingPath, destinationPath, overwrite: true); - AnsiConsole.MarkupLine($"[italic #76B900]Successfully copied [bold]{selectedItem}[/] to the working directory.[/]\n"); + AnsiConsole.MarkupLine($"[italic #76B900][bold]{selectedItem}[/] wurde erfolgreich in das Arbeitsverzeichnis kopiert.[/]\n"); } catch (Exception ex) { - AnsiConsole.MarkupLine($"[italic red]Failed to copy [bold]{selectedItem}[/] to the working directory. Exception: {ex.Message}[/]\n"); + AnsiConsole.MarkupLine($"[italic red]Fehler beim Kopieren von [bold]{selectedItem}[/] in das Arbeitsverzeichnis. Ausnahme: {ex.Message}[/]\n"); } } - return items.Select(item => new ArchiveItem(item.name, Path.Combine(DOWNLOAD_DIR, item.url.Split('/').Last()))).ToList(); + List archives = items + .Select(item => new ArchiveItem(item.name, Path.Combine(DOWNLOAD_DIR, item.url.Split('/').Last()))) + .ToList(); + + if (dashboardUpdateItem.HasValue) + { + string updatePath = Path.Combine(DOWNLOAD_DIR, dashboardUpdateItem.Value.url.Split('/').Last()); + archives.Add(new ArchiveItem(dashboardUpdateItem.Value.name, updatePath)); + } + + return archives; } } } \ No newline at end of file diff --git a/BadBuilder/ConsoleExperiences/ExtractExperience.cs b/BadBuilder/ConsoleExperiences/ExtractExperience.cs index 9468984..d6dea45 100644 --- a/BadBuilder/ConsoleExperiences/ExtractExperience.cs +++ b/BadBuilder/ConsoleExperiences/ExtractExperience.cs @@ -17,7 +17,7 @@ namespace BadBuilder ) .StartAsync(async ctx => { - AnsiConsole.MarkupLine("[#76B900]{0}[/] Extracting files.", Markup.Escape("[*]")); + AnsiConsole.MarkupLine("[#76B900]{0}[/] Entpacke Dateien.", Markup.Escape("[*]")); await Task.WhenAll(filesToExtract.Select(async item => { var task = ctx.AddTask(item.name, new ProgressTaskSettings { AutoStart = false }); diff --git a/BadBuilder/ConsoleExperiences/HomebrewExperience.cs b/BadBuilder/ConsoleExperiences/HomebrewExperience.cs index 6c8e39d..6002147 100644 --- a/BadBuilder/ConsoleExperiences/HomebrewExperience.cs +++ b/BadBuilder/ConsoleExperiences/HomebrewExperience.cs @@ -7,13 +7,13 @@ namespace BadBuilder static bool PromptAddHomebrew() { return AnsiConsole.Prompt( - new TextPrompt("Would you like to add homebrew programs?") + new TextPrompt("Möchtest du Homebrew-Programme hinzufügen?") .AddChoice(true) .AddChoice(false) .DefaultValue(false) .ChoicesStyle(GreenStyle) .DefaultValueStyle(OrangeStyle) - .WithConverter(choice => choice ? "y" : "n") + .WithConverter(choice => choice ? "j" : "n") ); } @@ -27,12 +27,12 @@ namespace BadBuilder new SelectionPrompt() .PageSize(4) .HighlightStyle(GreenStyle) - .AddChoices("Add Homebrew App", "View Added Apps", "Remove App", "Finish & Save") + .AddChoices("Homebrew-App hinzufügen", "Hinzugefügte Apps anzeigen", "App entfernen", "Fertig & Speichern") ); switch (choice) { - case "Add Homebrew App": + case "Homebrew-App hinzufügen": ClearConsole(); var newApp = AddHomebrewApp(); @@ -40,34 +40,34 @@ namespace BadBuilder homebrewApps.Add(newApp.Value); break; - case "View Added Apps": + case "Hinzugefügte Apps anzeigen": ClearConsole(); DisplayApps(homebrewApps); break; - case "Remove App": + case "App entfernen": ClearConsole(); RemoveHomebrewApp(homebrewApps); break; - case "Finish & Save": + case "Fertig & Speichern": if (homebrewApps.Count == 0) { - AnsiConsole.MarkupLine("[#ffac4d]No apps added.[/]"); + AnsiConsole.MarkupLine("[#ffac4d]Keine Apps hinzugefügt.[/]"); return homebrewApps; } if (AnsiConsole.Prompt( - new TextPrompt("Save and exit?") + new TextPrompt("Speichern und beenden?") .AddChoice(true) .AddChoice(false) .DefaultValue(true) .ChoicesStyle(GreenStyle) .DefaultValueStyle(OrangeStyle) - .WithConverter(choice => choice ? "y" : "n"))) + .WithConverter(choice => choice ? "j" : "n"))) { string status = "[+]"; - AnsiConsole.MarkupInterpolated($"[#76B900]{status}[/] Saved [bold]{homebrewApps.Count}[/] app(s).\n"); + AnsiConsole.MarkupInterpolated($"[#76B900]{status}[/] [bold]{homebrewApps.Count}[/] App(s) gespeichert.\n"); return homebrewApps; } break; @@ -77,11 +77,11 @@ namespace BadBuilder private static HomebrewApp? AddHomebrewApp() { - AnsiConsole.MarkupLine("[bold #76B900]Add a new homebrew app[/]\n"); - string folderPath = AnsiConsole.Ask("[#FFA500]Enter the folder path for the app:[/]"); + AnsiConsole.MarkupLine("[bold #76B900]Neue Homebrew-App hinzufügen[/]\n"); + string folderPath = AnsiConsole.Ask("[#FFA500]Gib den Ordnerpfad für die App ein:[/]"); if (!Directory.Exists(folderPath)) { - AnsiConsole.MarkupLine("[#ffac4d]Invalid folder path. Please try again.\n[/]"); + AnsiConsole.MarkupLine("[#ffac4d]Ungültiger Ordnerpfad. Bitte erneut versuchen.\n[/]"); return null; } @@ -92,23 +92,23 @@ namespace BadBuilder string entryPoint = xexFiles.Length switch { 0 => AnsiConsole.Prompt( - 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")) + new TextPrompt("[grey]Keine .xex-Dateien in diesem Ordner gefunden.[/] [#FFA500]Gib den Pfad zum Einstiegspunkt ein:[/]") + .Validate(path => File.Exists(path.Trim().Trim('"')) && Path.GetExtension(path.Trim().Trim('"')) == ".xex" ? ValidationResult.Success() : ValidationResult.Error("[#ffac4d]Datei nicht gefunden oder keine XEX-Datei.[/]\n")) ).Trim().Trim('"'), 1 => xexFiles[0], _ => AnsiConsole.Prompt( new SelectionPrompt() - .Title("[#FFA500]Select entry point:[/]") + .Title("[#FFA500]Einstiegspunkt auswählen:[/]") .HighlightStyle(PeachStyle) - .AddChoices(xexFiles.Select(Path.GetFileName)) - ) + .AddChoices(xexFiles.Select(file => Path.GetFileName(file) ?? file)) + ) ?? throw new InvalidOperationException("Die Auswahl des Einstiegspunkts war null.") }; #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(); - AnsiConsole.MarkupLine($"[#76B900]Added:[/] {folderPath.Split('\\').Last()} -> [#ffac4d]{Path.GetFileName(entryPoint)}[/]\n"); + AnsiConsole.MarkupLine($"[#76B900]Hinzugefügt:[/] {folderPath.Split('\\').Last()} -> [#ffac4d]{Path.GetFileName(entryPoint)}[/]\n"); return (folderPath.Split('\\').Last(), folderPath, Path.Combine(folderPath, entryPoint)); } @@ -118,14 +118,14 @@ namespace BadBuilder { if (apps.Count == 0) { - AnsiConsole.MarkupLine("[#ffac4d]No homebrew apps added.[/]\n"); + AnsiConsole.MarkupLine("[#ffac4d]Keine Homebrew-Apps hinzugefügt.[/]\n"); return; } var table = new Table() - .Title("[bold #76B900]Added Homebrew Apps[/]") - .AddColumn("[#4D8C00]Folder[/]") - .AddColumn("[#ff7200]Entry Point[/]"); + .Title("[bold #76B900]Hinzugefügte Homebrew-Apps[/]") + .AddColumn("[#4D8C00]Ordner[/]") + .AddColumn("[#ff7200]Einstiegspunkt[/]"); foreach (var app in apps) table.AddRow($"[#A1CF3E]{app.folder}[/]", $"[#ffac4d]{Path.GetFileName(app.entryPoint)}[/]"); @@ -138,23 +138,23 @@ namespace BadBuilder { if (apps.Count == 0) { - AnsiConsole.MarkupLine("[#ffac4d]No apps to remove.[/]\n"); + AnsiConsole.MarkupLine("[#ffac4d]Keine Apps zum Entfernen.[/]\n"); return; } var appToRemove = AnsiConsole.Prompt( new SelectionPrompt() - .Title("[bold #76B900]Select an app to remove:[/]") + .Title("[bold #76B900]Wähle eine App zum Entfernen:[/]") .PageSize(5) .HighlightStyle(LightOrangeStyle) - .MoreChoicesText("[grey](Move up/down to scroll)[/]") + .MoreChoicesText("[grey](Mit Hoch/Runter blättern)[/]") .AddChoices(apps.Select(app => $"{Path.GetFileName(app.folder)}")) ); var selectedApp = apps.First(app => $"{Path.GetFileName(app.folder)}" == appToRemove); apps.Remove(selectedApp); - AnsiConsole.MarkupLine($"[#ffac4d]Removed:[/] {selectedApp.folder.Split('\\').Last()}\n"); + AnsiConsole.MarkupLine($"[#ffac4d]Entfernt:[/] {selectedApp.folder.Split('\\').Last()}\n"); } } } \ No newline at end of file diff --git a/BadBuilder/Helpers/ArchiveHelper.cs b/BadBuilder/Helpers/ArchiveHelper.cs index 0021406..c5ec8bc 100644 --- a/BadBuilder/Helpers/ArchiveHelper.cs +++ b/BadBuilder/Helpers/ArchiveHelper.cs @@ -25,6 +25,8 @@ namespace BadBuilder.Helpers task.Increment(1); } + + await Task.CompletedTask; } catch (Exception ex) { diff --git a/BadBuilder/Helpers/DiskHelper.cs b/BadBuilder/Helpers/DiskHelper.cs index 83d27d5..b46bdb8 100644 --- a/BadBuilder/Helpers/DiskHelper.cs +++ b/BadBuilder/Helpers/DiskHelper.cs @@ -32,7 +32,7 @@ namespace BadBuilder.Helpers internal static string FormatDisk(DiskInfo disk) { if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - return "\u001b[38;2;255;114;0m[-]\u001b[0m Formatting is currently only supported on Windows. Please format your drive manually and try again."; + return "\u001b[38;2;255;114;0m[-]\u001b[0m Das Formatieren wird derzeit nur unter Windows unterstützt. Bitte formatiere dein Laufwerk manuell und versuche es erneut."; return DiskFormatter.FormatVolume(disk.DriveLetter[0], disk.TotalSize); } diff --git a/BadBuilder/Helpers/DownloadHelper.cs b/BadBuilder/Helpers/DownloadHelper.cs index 31ec6d0..5e4ae17 100644 --- a/BadBuilder/Helpers/DownloadHelper.cs +++ b/BadBuilder/Helpers/DownloadHelper.cs @@ -67,7 +67,7 @@ namespace BadBuilder.Helpers } catch (Exception ex) { - AnsiConsole.MarkupLine($"[red]Error downloading file from [bold]{url}[/]: {ex}[/]"); + AnsiConsole.MarkupLine($"[red]Fehler beim Herunterladen der Datei von [bold]{url}[/]: {ex}[/]"); } } } diff --git a/BadBuilder/Helpers/FileSystemHelper.cs b/BadBuilder/Helpers/FileSystemHelper.cs index e5c0630..2cb753e 100644 --- a/BadBuilder/Helpers/FileSystemHelper.cs +++ b/BadBuilder/Helpers/FileSystemHelper.cs @@ -12,7 +12,7 @@ string relativePath = Path.GetRelativePath(sourceDir, file); string destFile = Path.Combine(destDir, relativePath); - Directory.CreateDirectory(Path.GetDirectoryName(destFile)); + Directory.CreateDirectory(Path.GetDirectoryName(destFile)!); await CopyFileAsync(file, destFile); } diff --git a/BadBuilder/Helpers/PatchHelper.cs b/BadBuilder/Helpers/PatchHelper.cs index 7eaaeba..b5b0eee 100644 --- a/BadBuilder/Helpers/PatchHelper.cs +++ b/BadBuilder/Helpers/PatchHelper.cs @@ -26,7 +26,7 @@ namespace BadBuilder.Helpers if (process.ExitCode != 0) { string status = "[-]"; - AnsiConsole.MarkupLineInterpolated($"\n[#FF7200]{status}[/] The program {Path.GetFileNameWithoutExtension(xexPath)} was unable to be patched. XexTool output:"); + AnsiConsole.MarkupLineInterpolated($"\n[#FF7200]{status}[/] Das Programm {Path.GetFileNameWithoutExtension(xexPath)} konnte nicht gepatcht werden. XexTool-Ausgabe:"); Console.WriteLine(process.StandardError.ReadToEnd()); } } diff --git a/BadBuilder/Program.cs b/BadBuilder/Program.cs index 898c46f..181e82a 100644 --- a/BadBuilder/Program.cs +++ b/BadBuilder/Program.cs @@ -23,6 +23,7 @@ namespace BadBuilder static string XexToolPath = string.Empty; static string TargetDriveLetter = string.Empty; + static bool RequiresDashboardUpdate = false; static ActionQueue actionQueue = new(); @@ -37,7 +38,7 @@ namespace BadBuilder Console.WriteLine(); string action = PromptForAction(); - if (action == "Exit") Environment.Exit(0); + if (action == "Beenden") Environment.Exit(0); List disks = DiskHelper.GetDisks(); string selectedDisk = PromptDiskSelection(disks); @@ -59,13 +60,38 @@ namespace BadBuilder ClearConsole(); string selectedDefaultApp = "XeUnshackle"; - AnsiConsole.MarkupLine("[#76B900]{0}[/] Default payload set to [bold]XeUnshackle[/].", Markup.Escape("[*]")); + AnsiConsole.MarkupLine("[#76B900]{0}[/] Standard-Payload auf [bold]XeUnshackle[/] gesetzt.", Markup.Escape("[*]")); - AnsiConsole.MarkupLine("[#76B900]{0}[/] Copying requried files and folders.", Markup.Escape("[*]")); + AnsiConsole.MarkupLine("[#76B900]{0}[/] Kopiere erforderliche Dateien und Ordner.", Markup.Escape("[*]")); foreach (var folder in Directory.GetDirectories($@"{EXTRACTED_DIR}")) { switch (folder.Split("\\").Last()) { + case "Dashboard Update 17559": + if (!RequiresDashboardUpdate) + { + break; + } + + foreach (string subDirectory in Directory.GetDirectories(folder)) + { + EnqueueMirrorDirectory( + subDirectory, + Path.Combine(TargetDriveLetter, Path.GetFileName(subDirectory)), + 1 + ); + } + + foreach (string file in Directory.GetFiles(folder)) + { + EnqueueFileCopy( + file, + Path.Combine(TargetDriveLetter, Path.GetFileName(file)), + 1 + ); + } + break; + case "ABadAvatar": string avatarPayloadPath = Path.Combine(folder, "BadUpdatePayload"); if (Directory.Exists(avatarPayloadPath)) @@ -182,10 +208,10 @@ namespace BadBuilder actionQueue.EnqueueAction(async () => { using (StreamWriter writer = new(Path.Combine(TargetDriveLetter, "name.txt"))) - writer.WriteLine("USB Storage Device"); + writer.WriteLine("USB-Speichergerät"); 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\nConfiguration: \n- BadUpdate target binary: {selectedDefaultApp}"); + writer.WriteLine($"Dieses Laufwerk wurde mit BadBuilder von Pdawg erstellt.\nWeitere Informationen: https://github.com/Pdawg-bytes/BadBuilder\nKonfiguration: \n- BadUpdate-Ziel-Binary: {selectedDefaultApp}"); Directory.CreateDirectory(Path.Combine(TargetDriveLetter, "Apps")); await FileSystemHelper.MirrorDirectoryAsync(Path.Combine(folder, "Rock Band Blitz"), TargetDriveLetter); @@ -212,7 +238,7 @@ namespace BadBuilder }, 6); break; - default: throw new Exception($"[-] Unexpected directory in working folder: {folder}"); + default: throw new Exception($"[-] Unerwartetes Verzeichnis im Arbeitsordner: {folder}"); } } actionQueue.ExecuteActionsAsync().Wait(); @@ -249,14 +275,14 @@ namespace BadBuilder File.WriteAllLines(launchIniPath, launchIniLines); } - 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"); + File.AppendAllText(Path.Combine(TargetDriveLetter, "info.txt"), $"- Laufwerk formatiert mit {(targetDisk.TotalSize < 31 * GB ? "Windows \"format.com\"" : "BadBuilder Large FAT32 formatter")}\n"); + File.AppendAllText(Path.Combine(TargetDriveLetter, "info.txt"), $"- Gesamtkapazität des Laufwerks: {targetDisk.TotalSize} Bytes\n"); ClearConsole(); WriteHomebrewLog(1); - AnsiConsole.MarkupLine("\n[#76B900]{0}[/] Your USB drive is ready to go.", Markup.Escape("[+]")); + AnsiConsole.MarkupLine("\n[#76B900]{0}[/] Dein USB-Laufwerk ist einsatzbereit.", Markup.Escape("[+]")); - Console.Write("\nPress any key to exit..."); + Console.Write("\nBeliebige Taste zum Beenden drücken..."); Console.ReadKey(); } @@ -279,7 +305,7 @@ namespace BadBuilder 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"; + string logEntry = $"- {count} Homebrew-App(s) hinzugefügt (einschließlich Simple 360 NAND Flasher)\n"; File.AppendAllText(logPath, logEntry); } @@ -295,7 +321,7 @@ namespace BadBuilder [#76B900]───────────────────────────────────────────────────────────────────────v0.31[/] ───────────────────────Xbox 360 [#FF7200]BadUpdate[/] USB Builder─────────────────────── - [#848589]Created by Pdawg | Forked by NiklasCFW[/] + [#848589]Created by Pdawg | Forked by NiklasCFW[/] [#76B900]────────────────────────────────────────────────────────────────────────────[/] """); @@ -304,8 +330,8 @@ namespace BadBuilder new SelectionPrompt() .HighlightStyle(GreenStyle) .AddChoices( - "Build exploit USB", - "Exit" + "Exploit-USB erstellen", + "Beenden" ) ); diff --git a/BadBuilder/Utilities/ActionQueue.cs b/BadBuilder/Utilities/ActionQueue.cs index 7146615..edc79f1 100644 --- a/BadBuilder/Utilities/ActionQueue.cs +++ b/BadBuilder/Utilities/ActionQueue.cs @@ -1,8 +1,11 @@ -namespace BadBuilder.Utilities +using Spectre.Console; +using System.Linq; + +namespace BadBuilder.Utilities { internal class ActionQueue { - private SortedDictionary>> priorityQueue = new SortedDictionary>>(); + private readonly SortedDictionary>> priorityQueue = new(); internal void EnqueueAction(Func action, int priority) { @@ -15,15 +18,39 @@ internal async Task ExecuteActionsAsync() { - foreach (var priority in priorityQueue.Keys.OrderByDescending(p => p)) + int totalActions = priorityQueue.Values.Sum(queue => queue.Count); + if (totalActions == 0) { - var actions = priorityQueue[priority]; - while (actions.Count > 0) - { - var action = actions.Dequeue(); - await action(); - } + return; } + + await AnsiConsole.Progress() + .Columns( + new TaskDescriptionColumn(), + new ProgressBarColumn(), + new PercentageColumn() + ) + .StartAsync(async ctx => + { + var copyTask = ctx.AddTask("Dateien auf das Laufwerk kopieren", new ProgressTaskSettings + { + AutoStart = true, + MaxValue = totalActions + }); + + foreach (var priority in priorityQueue.Keys.OrderByDescending(p => p)) + { + var actions = priorityQueue[priority]; + while (actions.Count > 0) + { + var action = actions.Dequeue(); + await action(); + copyTask.Increment(1); + } + } + }); + + priorityQueue.Clear(); } } -} \ No newline at end of file +}