Localize UI text and add copy progress bar
This commit is contained in:
@@ -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;
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ CloseHandle
|
||||
VirtualAlloc
|
||||
VirtualFree
|
||||
SetVolumeLabelW
|
||||
GUID
|
||||
DISK_GEOMETRY
|
||||
PARTITION_INFORMATION
|
||||
PARTITION_INFORMATION_EX
|
||||
@@ -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>(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;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace BadBuilder
|
||||
|
||||
return AnsiConsole.Prompt(
|
||||
new SelectionPrompt<string>()
|
||||
.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<bool>($"[#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<bool>($"[#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)
|
||||
|
||||
@@ -9,6 +9,23 @@ namespace BadBuilder
|
||||
{
|
||||
static async Task<List<ArchiveItem>> DownloadRequiredFiles()
|
||||
{
|
||||
bool hasUpdatedDashboard = AnsiConsole.Prompt(
|
||||
new TextPrompt<bool>("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<DownloadItem> items = new()
|
||||
{
|
||||
("ABadAvatar", "https://cdn.niklascfw.de/files/xbox360/ABadAvatar-publicbeta1.0.zip"),
|
||||
@@ -25,11 +42,11 @@ namespace BadBuilder
|
||||
|
||||
List<string> 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<string>()
|
||||
.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<DownloadItem> 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<string>($"Enter the path to the [bold]{selectedItem}[/] archive:")
|
||||
new TextPrompt<string>($"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<ArchiveItem> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 });
|
||||
|
||||
@@ -7,13 +7,13 @@ namespace BadBuilder
|
||||
static bool PromptAddHomebrew()
|
||||
{
|
||||
return AnsiConsole.Prompt(
|
||||
new TextPrompt<bool>("Would you like to add homebrew programs?")
|
||||
new TextPrompt<bool>("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<string>()
|
||||
.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<bool>("Save and exit?")
|
||||
new TextPrompt<bool>("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<string>("[#FFA500]Enter the folder path for the app:[/]");
|
||||
AnsiConsole.MarkupLine("[bold #76B900]Neue Homebrew-App hinzufügen[/]\n");
|
||||
string folderPath = AnsiConsole.Ask<string>("[#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<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"))
|
||||
new TextPrompt<string>("[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<string?>()
|
||||
.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<string>()
|
||||
.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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,8 @@ namespace BadBuilder.Helpers
|
||||
|
||||
task.Increment(1);
|
||||
}
|
||||
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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}[/]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<DiskInfo> 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);
|
||||
}
|
||||
|
||||
@@ -304,8 +330,8 @@ namespace BadBuilder
|
||||
new SelectionPrompt<string>()
|
||||
.HighlightStyle(GreenStyle)
|
||||
.AddChoices(
|
||||
"Build exploit USB",
|
||||
"Exit"
|
||||
"Exploit-USB erstellen",
|
||||
"Beenden"
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
namespace BadBuilder.Utilities
|
||||
using Spectre.Console;
|
||||
using System.Linq;
|
||||
|
||||
namespace BadBuilder.Utilities
|
||||
{
|
||||
internal class ActionQueue
|
||||
{
|
||||
private SortedDictionary<int, Queue<Func<Task>>> priorityQueue = new SortedDictionary<int, Queue<Func<Task>>>();
|
||||
private readonly SortedDictionary<int, Queue<Func<Task>>> priorityQueue = new();
|
||||
|
||||
internal void EnqueueAction(Func<Task> action, int priority)
|
||||
{
|
||||
@@ -15,6 +18,26 @@
|
||||
|
||||
internal async Task ExecuteActionsAsync()
|
||||
{
|
||||
int totalActions = priorityQueue.Values.Sum(queue => queue.Count);
|
||||
if (totalActions == 0)
|
||||
{
|
||||
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];
|
||||
@@ -22,8 +45,12 @@
|
||||
{
|
||||
var action = actions.Dequeue();
|
||||
await action();
|
||||
copyTask.Increment(1);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
priorityQueue.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user