Add 'unsupported' homebrew support & 'XeUnshackle'
This commit is contained in:
@@ -9,7 +9,6 @@ using Windows.Win32.Storage.FileSystem;
|
|||||||
using static Windows.Win32.PInvoke;
|
using static Windows.Win32.PInvoke;
|
||||||
using static BadBuilder.Formatter.Constants;
|
using static BadBuilder.Formatter.Constants;
|
||||||
using static BadBuilder.Formatter.Utilities;
|
using static BadBuilder.Formatter.Utilities;
|
||||||
using Windows.Win32.Foundation;
|
|
||||||
|
|
||||||
namespace BadBuilder.Formatter
|
namespace BadBuilder.Formatter
|
||||||
{
|
{
|
||||||
@@ -17,7 +16,7 @@ namespace BadBuilder.Formatter
|
|||||||
{
|
{
|
||||||
public static unsafe string FormatVolume(char driveLetter, long diskSize)
|
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
|
Process process = new Process
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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;
|
bool ret = true;
|
||||||
string output = string.Empty;
|
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();
|
ClearConsole();
|
||||||
output = DiskHelper.FormatDisk(disks[diskIndex]);
|
output = DiskHelper.FormatDisk(disk);
|
||||||
if (output != string.Empty) ret = false;
|
if (output != string.Empty) ret = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -24,9 +24,6 @@ namespace BadBuilder
|
|||||||
await ArchiveHelper.ExtractFileAsync(item.name, item.path, task);
|
await ArchiveHelper.ExtractFileAsync(item.name, item.path, task);
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
string status = "[+]";
|
|
||||||
AnsiConsole.MarkupInterpolated($"[#76B900]{status}[/] [bold]{filesToExtract.Count()}[/] files extracted.");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -17,7 +17,7 @@ namespace BadBuilder
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<HomebrewApp> ManageHomebrewApps()
|
internal static List<HomebrewApp> ManageHomebrewApps()
|
||||||
{
|
{
|
||||||
var homebrewApps = new List<HomebrewApp>();
|
var homebrewApps = new List<HomebrewApp>();
|
||||||
|
|
||||||
@@ -37,11 +37,7 @@ namespace BadBuilder
|
|||||||
|
|
||||||
var newApp = AddHomebrewApp();
|
var newApp = AddHomebrewApp();
|
||||||
if (newApp != null)
|
if (newApp != null)
|
||||||
{
|
|
||||||
homebrewApps.Add(newApp.Value);
|
homebrewApps.Add(newApp.Value);
|
||||||
AnsiConsole.Status()
|
|
||||||
.Start("Adding...", ctx => ctx.Spinner(Spinner.Known.Dots2));
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "View Added Apps":
|
case "View Added Apps":
|
||||||
@@ -85,31 +81,30 @@ namespace BadBuilder
|
|||||||
string folderPath = AnsiConsole.Ask<string>("[#FFA500]Enter the folder path for the app:[/]");
|
string folderPath = AnsiConsole.Ask<string>("[#FFA500]Enter the folder path for the app:[/]");
|
||||||
if (!Directory.Exists(folderPath))
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
string[] xexFiles = Directory.GetFiles(folderPath, "*.xex");
|
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
|
string entryPoint = xexFiles.Length switch
|
||||||
{
|
{
|
||||||
0 => AnsiConsole.Prompt(
|
0 => AnsiConsole.Prompt(
|
||||||
new TextPrompt<string>("[#ffac4d]No .xex files found.[/] Enter entry point:")
|
new TextPrompt<string>("[grey]No .xex files found in this folder.[/] [#FFA500]Enter the path to the entry point:[/]")
|
||||||
.Validate(path => File.Exists(path) ? ValidationResult.Success() : ValidationResult.Error("File not found"))
|
.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],
|
1 => xexFiles[0],
|
||||||
_ => AnsiConsole.Prompt(
|
_ => AnsiConsole.Prompt(
|
||||||
new SelectionPrompt<string>()
|
new SelectionPrompt<string?>()
|
||||||
.Title("[#FFA500]Select entry point:[/]")
|
.Title("[#FFA500]Select entry point:[/]")
|
||||||
.HighlightStyle(PeachStyle)
|
.HighlightStyle(PeachStyle)
|
||||||
.AddChoices(xexFiles.Select(Path.GetFileName))
|
.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();
|
ClearConsole();
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ namespace BadBuilder.Helpers
|
|||||||
{
|
{
|
||||||
internal static class DiskHelper
|
internal static class DiskHelper
|
||||||
{
|
{
|
||||||
public static List<DiskInfo> GetDisks()
|
internal static List<DiskInfo> GetDisks()
|
||||||
{
|
{
|
||||||
var disks = new List<DiskInfo>();
|
var disks = new List<DiskInfo>();
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ namespace BadBuilder.Helpers
|
|||||||
List<string> repos =
|
List<string> repos =
|
||||||
[
|
[
|
||||||
"grimdoomer/Xbox360BadUpdate",
|
"grimdoomer/Xbox360BadUpdate",
|
||||||
|
"Byrom90/XeUnshackle",
|
||||||
"FreeMyXe/FreeMyXe"
|
"FreeMyXe/FreeMyXe"
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -28,6 +29,7 @@ namespace BadBuilder.Helpers
|
|||||||
var name when name.Contains("Free", StringComparison.OrdinalIgnoreCase) => "FreeMyXe",
|
var name when name.Contains("Free", StringComparison.OrdinalIgnoreCase) => "FreeMyXe",
|
||||||
var name when name.Contains("Tools", StringComparison.OrdinalIgnoreCase) => "BadUpdate Tools",
|
var name when name.Contains("Tools", StringComparison.OrdinalIgnoreCase) => "BadUpdate Tools",
|
||||||
var name when name.Contains("BadUpdate", StringComparison.OrdinalIgnoreCase) => "BadUpdate",
|
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)
|
_ => asset.Name.Substring(0, asset.Name.Length - 4)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Diagnostics;
|
using Spectre.Console;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
namespace BadBuilder.Helpers
|
namespace BadBuilder.Helpers
|
||||||
{
|
{
|
||||||
@@ -12,8 +13,8 @@ namespace BadBuilder.Helpers
|
|||||||
{
|
{
|
||||||
FileName = xexToolPath,
|
FileName = xexToolPath,
|
||||||
Arguments = $"-m r -r a \"{xexPath}\"",
|
Arguments = $"-m r -r a \"{xexPath}\"",
|
||||||
RedirectStandardOutput = false,
|
RedirectStandardOutput = true,
|
||||||
RedirectStandardError = false,
|
RedirectStandardError = true,
|
||||||
UseShellExecute = false,
|
UseShellExecute = false,
|
||||||
CreateNoWindow = true
|
CreateNoWindow = true
|
||||||
}
|
}
|
||||||
@@ -21,6 +22,13 @@ namespace BadBuilder.Helpers
|
|||||||
|
|
||||||
process.Start();
|
process.Start();
|
||||||
await process.WaitForExitAsync();
|
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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -25,6 +25,8 @@ namespace BadBuilder
|
|||||||
|
|
||||||
static ActionQueue actionQueue = new();
|
static ActionQueue actionQueue = new();
|
||||||
|
|
||||||
|
static DiskInfo targetDisk = new("Z:\\", "Fixed", 0, "", 0, int.MaxValue); // Default values just incase.
|
||||||
|
|
||||||
static void Main(string[] args)
|
static void Main(string[] args)
|
||||||
{
|
{
|
||||||
ShowWelcomeMessage();
|
ShowWelcomeMessage();
|
||||||
@@ -40,10 +42,13 @@ namespace BadBuilder
|
|||||||
string selectedDisk = PromptDiskSelection(disks);
|
string selectedDisk = PromptDiskSelection(disks);
|
||||||
TargetDriveLetter = selectedDisk.Substring(0, 3);
|
TargetDriveLetter = selectedDisk.Substring(0, 3);
|
||||||
|
|
||||||
|
int diskIndex = disks.FindIndex(disk => $"{disk.DriveLetter} ({disk.SizeFormatted}) - {disk.Type}" == selectedDisk);
|
||||||
|
targetDisk = disks[diskIndex];
|
||||||
|
|
||||||
bool confirmation = PromptFormatConfirmation(selectedDisk);
|
bool confirmation = PromptFormatConfirmation(selectedDisk);
|
||||||
if (confirmation)
|
if (confirmation)
|
||||||
{
|
{
|
||||||
if (!FormatDisk(disks, selectedDisk)) continue;
|
if (!FormatDisk(targetDisk)) continue;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -51,13 +56,21 @@ namespace BadBuilder
|
|||||||
List<ArchiveItem> downloadedFiles = DownloadRequiredFiles().Result;
|
List<ArchiveItem> downloadedFiles = DownloadRequiredFiles().Result;
|
||||||
ExtractFiles(downloadedFiles).Wait();
|
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}"))
|
foreach (var folder in Directory.GetDirectories($@"{EXTRACTED_DIR}"))
|
||||||
{
|
{
|
||||||
var folderName = folder.Split("\\").Last();
|
switch (folder.Split("\\").Last())
|
||||||
|
|
||||||
switch (folderName)
|
|
||||||
{
|
{
|
||||||
case "XEXMenu":
|
case "XEXMenu":
|
||||||
EnqueueMirrorDirectory(
|
EnqueueMirrorDirectory(
|
||||||
@@ -68,6 +81,7 @@ namespace BadBuilder
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case "FreeMyXe":
|
case "FreeMyXe":
|
||||||
|
if (selectedDefaultApp != "FreeMyXe") break;
|
||||||
EnqueueFileCopy(
|
EnqueueFileCopy(
|
||||||
Path.Combine(folder, "FreeMyXe.xex"),
|
Path.Combine(folder, "FreeMyXe.xex"),
|
||||||
Path.Combine(TargetDriveLetter, "BadUpdatePayload", "default.xex"),
|
Path.Combine(TargetDriveLetter, "BadUpdatePayload", "default.xex"),
|
||||||
@@ -75,6 +89,17 @@ namespace BadBuilder
|
|||||||
);
|
);
|
||||||
break;
|
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":
|
case "BadUpdate":
|
||||||
actionQueue.EnqueueAction(async () =>
|
actionQueue.EnqueueAction(async () =>
|
||||||
{
|
{
|
||||||
@@ -82,7 +107,7 @@ namespace BadBuilder
|
|||||||
writer.WriteLine("USB Storage Device");
|
writer.WriteLine("USB Storage Device");
|
||||||
|
|
||||||
using (StreamWriter writer = new(Path.Combine(TargetDriveLetter, "info.txt")))
|
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"));
|
Directory.CreateDirectory(Path.Combine(TargetDriveLetter, "Apps"));
|
||||||
await FileSystemHelper.MirrorDirectoryAsync(Path.Combine(folder, "Rock Band Blitz"), TargetDriveLetter);
|
await FileSystemHelper.MirrorDirectoryAsync(Path.Combine(folder, "Rock Band Blitz"), TargetDriveLetter);
|
||||||
@@ -109,14 +134,18 @@ namespace BadBuilder
|
|||||||
}, 6);
|
}, 6);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default: throw new Exception($"Unexpected directory in working folder: {folder}");
|
default: throw new Exception($"[-] Unexpected directory in working folder: {folder}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
actionQueue.ExecuteActionsAsync().Wait();
|
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();
|
ClearConsole();
|
||||||
if (!PromptAddHomebrew())
|
if (!PromptAddHomebrew())
|
||||||
{
|
{
|
||||||
|
WriteHomebrewLog(1);
|
||||||
AnsiConsole.MarkupLine("\n[#76B900]{0}[/] Your USB drive is ready to go.", Markup.Escape("[+]"));
|
AnsiConsole.MarkupLine("\n[#76B900]{0}[/] Your USB drive is ready to go.", Markup.Escape("[+]"));
|
||||||
Console.Write("\nPress any key to exit...");
|
Console.Write("\nPress any key to exit...");
|
||||||
Console.ReadKey();
|
Console.ReadKey();
|
||||||
@@ -134,12 +163,14 @@ namespace BadBuilder
|
|||||||
await Task.WhenAll(homebrewApps.Select(async item =>
|
await Task.WhenAll(homebrewApps.Select(async item =>
|
||||||
{
|
{
|
||||||
await FileSystemHelper.MirrorDirectoryAsync(item.folder, Path.Combine(TargetDriveLetter, "Apps", item.name));
|
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();
|
}).Wait();
|
||||||
|
|
||||||
|
WriteHomebrewLog(homebrewApps.Count + 1);
|
||||||
|
|
||||||
string status = "[+]";
|
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("[+]"));
|
AnsiConsole.MarkupLine("\n[#76B900]{0}[/] Your USB drive is ready to go.", Markup.Escape("[+]"));
|
||||||
|
|
||||||
@@ -163,6 +194,13 @@ namespace BadBuilder
|
|||||||
}, priority);
|
}, 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(
|
static void ShowWelcomeMessage() => AnsiConsole.Markup(
|
||||||
"""
|
"""
|
||||||
@@ -173,12 +211,12 @@ namespace BadBuilder
|
|||||||
[#CCE388]██████╔╝██║ ██║██████╔╝██████╔╝╚██████╔╝██║███████╗██████╔╝███████╗██║ ██║[/]
|
[#CCE388]██████╔╝██║ ██║██████╔╝██████╔╝╚██████╔╝██║███████╗██████╔╝███████╗██║ ██║[/]
|
||||||
[#CCE388]╚═════╝ ╚═╝ ╚═╝╚═════╝ ╚═════╝ ╚═════╝ ╚═╝╚══════╝╚═════╝ ╚══════╝╚═╝ ╚═╝[/]
|
[#CCE388]╚═════╝ ╚═╝ ╚═╝╚═════╝ ╚═════╝ ╚═════╝ ╚═╝╚══════╝╚═════╝ ╚══════╝╚═╝ ╚═╝[/]
|
||||||
|
|
||||||
[#76B900]──────────────────────────────────────────────────────────────────────v0.21a[/]
|
[#76B900]───────────────────────────────────────────────────────────────────────v0.30[/]
|
||||||
───────────────────────Xbox 360 [#FF7200]BadUpdate[/] USB Builder───────────────────────
|
───────────────────────Xbox 360 [#FF7200]BadUpdate[/] USB Builder───────────────────────
|
||||||
[#848589]Created by Pdawg[/]
|
[#848589]Created by Pdawg[/]
|
||||||
[#76B900]────────────────────────────────────────────────────────────────────────────[/]
|
[#76B900]────────────────────────────────────────────────────────────────────────────[/]
|
||||||
|
|
||||||
""");
|
""");
|
||||||
|
|
||||||
static string PromptForAction() => AnsiConsole.Prompt(
|
static string PromptForAction() => AnsiConsole.Prompt(
|
||||||
new SelectionPrompt<string>()
|
new SelectionPrompt<string>()
|
||||||
|
|||||||
@@ -7,5 +7,10 @@
|
|||||||
internal const string EXTRACTED_DIR = $@"{WORKING_DIR}\Extract";
|
internal const string EXTRACTED_DIR = $@"{WORKING_DIR}\Extract";
|
||||||
|
|
||||||
internal const string ContentFolder = "Content\\0000000000000000\\";
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user