From 7c0fb0d416540fa6327a775000f0c01d22d65bb6 Mon Sep 17 00:00:00 2001 From: Pdawg11239 <83825746+Pdawg-bytes@users.noreply.github.com> Date: Mon, 17 Mar 2025 03:11:11 -0400 Subject: [PATCH] Finish formatter --- .../BadBuilder.Formatter.csproj | 7 + BadBuilder.Formatter/Constants.cs | 33 +++ BadBuilder.Formatter/DiskFormatter.cs | 274 ++++++++++++------ BadBuilder.Formatter/FAT32BootSector.cs | 38 +-- BadBuilder.Formatter/FAT32FsInfoSector.cs | 10 +- BadBuilder.Formatter/NativeMethods.txt | 12 + .../{FAT32Utilities.cs => Utilities.cs} | 82 +++--- BadBuilder.Formatter/Win32.cs | 238 --------------- BadBuilder/BadBuilder.csproj | 4 - .../ConsoleExperiences/DiskExperience.cs | 24 +- .../ConsoleExperiences/DownloadExperience.cs | 2 +- .../ConsoleExperiences/ExtractExperience.cs | 2 +- .../ConsoleExperiences/HomebrewExperience.cs | 7 +- BadBuilder/Helpers/ArchiveHelper.cs | 11 +- BadBuilder/Helpers/DiskHelper.cs | 24 +- BadBuilder/Helpers/DownloadHelper.cs | 2 +- BadBuilder/Helpers/FileSystemHelper.cs | 4 +- BadBuilder/Helpers/PatchHelper.cs | 7 +- BadBuilder/Helpers/ResourceHelper.cs | 21 -- BadBuilder/Models/DiskInfo.cs | 2 +- BadBuilder/Program.cs | 6 +- BadBuilder/Resources/fat32format.exe | Bin 49218 -> 0 bytes .../{Models => Utilities}/ActionQueue.cs | 2 +- BadBuilder/{ => Utilities}/Constants.cs | 2 +- 24 files changed, 332 insertions(+), 482 deletions(-) create mode 100644 BadBuilder.Formatter/NativeMethods.txt rename BadBuilder.Formatter/{FAT32Utilities.cs => Utilities.cs} (50%) delete mode 100644 BadBuilder.Formatter/Win32.cs delete mode 100644 BadBuilder/Helpers/ResourceHelper.cs delete mode 100644 BadBuilder/Resources/fat32format.exe rename BadBuilder/{Models => Utilities}/ActionQueue.cs (96%) rename BadBuilder/{ => Utilities}/Constants.cs (90%) diff --git a/BadBuilder.Formatter/BadBuilder.Formatter.csproj b/BadBuilder.Formatter/BadBuilder.Formatter.csproj index bd44ee5..2d6a66a 100644 --- a/BadBuilder.Formatter/BadBuilder.Formatter.csproj +++ b/BadBuilder.Formatter/BadBuilder.Formatter.csproj @@ -7,4 +7,11 @@ true + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + diff --git a/BadBuilder.Formatter/Constants.cs b/BadBuilder.Formatter/Constants.cs index 99a3530..474a431 100644 --- a/BadBuilder.Formatter/Constants.cs +++ b/BadBuilder.Formatter/Constants.cs @@ -5,5 +5,38 @@ internal const string ORANGE = "\u001b[38;2;255;114;0m"; internal const string ANSI_RESET = "\u001b[0m"; + + + internal const uint GENERIC_READ = 0x80000000; + internal const uint GENERIC_WRITE = 0x40000000; + internal const uint OPEN_EXISTING = 3; + internal const uint FILE_SHARE_READ = 1; + internal const uint FILE_BEGIN = 0; + internal const uint FILE_FLAG_NO_BUFFERING = 0x20000000; + + internal const uint IOCTL_DISK_GET_DRIVE_GEOMETRY = 0x00070000; + internal const uint IOCTL_DISK_GET_PARTITION_INFO_EX = 0x00070048; + internal const uint IOCTL_DISK_GET_PARTITION_INFO = 0x00074004; + internal const uint IOCTL_DISK_SET_PARTITION_INFO = 0x0007C008; + internal const uint FSCTL_LOCK_VOLUME = 0x00090018; + internal const uint FSCTL_DISMOUNT_VOLUME = 0x00090020; + internal const uint FSCTL_UNLOCK_VOLUME = 0x0009001C; + internal const uint FSCTL_QUERY_RETRIEVAL_POINTERS = 0x0009003B; + internal const uint FSCTL_GET_COMPRESSION = 0x0009003C; + internal const uint FSCTL_SET_COMPRESSION = 0x0009C040; + internal const uint FSCTL_SET_BOOTLOADER_ACCESSED = 0x0009004F; + internal const uint FSCTL_MARK_AS_SYSTEM_HIVE = 0x0009004F; + internal const uint FSCTL_OPLOCK_BREAK_ACK_NO_2 = 0x00090050; + internal const uint FSCTL_INVALIDATE_VOLUMES = 0x00090054; + internal const uint FSCTL_QUERY_FAT_BPB = 0x00090058; + internal const uint FSCTL_REQUEST_FILTER_OPLOCK = 0x0009005C; + internal const uint FSCTL_FILESYSTEM_GET_STATISTICS = 0x00090060; + internal const uint FSCTL_GET_NTFS_VOLUME_DATA = 0x00090064; + internal const uint FSCTL_GET_NTFS_FILE_RECORD = 0x00090068; + internal const uint FSCTL_GET_VOLUME_BITMAP = 0x0009006F; + internal const uint FSCTL_GET_RETRIEVAL_POINTERS = 0x00090073; + internal const uint FSCTL_MOVE_FILE = 0x00090074; + internal const uint FSCTL_IS_VOLUME_DIRTY = 0x00090078; + internal const uint FSCTL_ALLOW_EXTENDED_DASD_IO = 0x00090083; } } \ No newline at end of file diff --git a/BadBuilder.Formatter/DiskFormatter.cs b/BadBuilder.Formatter/DiskFormatter.cs index 81b7b6a..b6638e8 100644 --- a/BadBuilder.Formatter/DiskFormatter.cs +++ b/BadBuilder.Formatter/DiskFormatter.cs @@ -1,114 +1,220 @@ -using static BadBuilder.Formatter.Win32; -using static BadBuilder.Formatter.Constants; -using static BadBuilder.Formatter.FAT32Utilities; +#pragma warning disable CA1416 +using System.Text; +using Windows.Win32.System.Ioctl; +using Microsoft.Win32.SafeHandles; using System.Runtime.InteropServices; +using Windows.Win32.Storage.FileSystem; + +using static Windows.Win32.PInvoke; +using static BadBuilder.Formatter.Constants; +using static BadBuilder.Formatter.Utilities; namespace BadBuilder.Formatter { public static class DiskFormatter { - public static unsafe (int, string) FormatVolume(char driveLetter) + public static unsafe string FormatVolume(char driveLetter) { - uint cbRet; - - DISK_GEOMETRY diskGeometry; - PARTITION_INFORMATION diskPartInfo; - PARTITION_INFORMATION_EX exDiskPartInfo; - bool isGPT = false; - uint bytesPerSector = 0; - uint totalSectors; - uint fatSize; - string devicePath = $"\\\\.\\{driveLetter}:"; uint volumeID = GetVolumeID(); + using SafeFileHandle driveHandle = OpenDeviceHandle(devicePath); + if (driveHandle.IsInvalid) return Error("Unable to open device. GetLastError: " + Marshal.GetLastWin32Error()); - IntPtr driveHandle = CreateFileW( - devicePath, - GENERIC_READ | GENERIC_WRITE, - 0, - 0, - OPEN_EXISTING, - FILE_FLAG_NO_BUFFERING, - 0); + if (!EnableExtendedDASDIO(driveHandle) || !LockDevice(driveHandle)) + return Error("Failed to initialize device access."); - if (driveHandle == -1) return (-1, Error("Unable to open device - close all open programs or windows that may have a handle lock on the drive.")); + DISK_GEOMETRY diskGeometry; + if (!TryGetDiskGeometry(driveHandle, out diskGeometry)) + return Error("Failed to get drive geometry."); - if (!DeviceIoControl( driveHandle, FSCTL_ALLOW_EXTENDED_DASD_IO, 0, 0, 0, 0, out cbRet, 0)) - return (-1, Error("Failed to enable extended DASD IO on the device.")); + PARTITION_INFORMATION partitionInfo = new(); + bool isGPT = false; + if (!TryGetPartitionInfo(driveHandle, ref diskGeometry, out partitionInfo, out isGPT)) + return Error("Failed to get partition information."); - if (!DeviceIoControl(driveHandle, FSCTL_LOCK_VOLUME, 0, 0, 0, 0, out cbRet, 0)) - return (-1, Error("Failed to lock the device.")); + uint totalSectors = (uint)(partitionInfo.PartitionLength / diskGeometry.BytesPerSector); + if (!IsValidFAT32Size(totalSectors)) + return Error("Invalid drive size for FAT32."); - - using (var pDiskGeometry = NativePointer.Allocate()) + FAT32BootSector bootSector = InitializeBootSector(diskGeometry, partitionInfo, totalSectors, volumeID); + FAT32FsInfoSector fsInfo = InitializeFsInfo(); + uint[] firstFATSector = InitializeFirstFATSector(diskGeometry.BytesPerSector); + + string formatOutput = FormatVolumeData(driveHandle, diskGeometry, bootSector, fsInfo, firstFATSector, isGPT, partitionInfo); + if (formatOutput != string.Empty) + return formatOutput; + + if (!UnlockDevice(driveHandle) || !DismountVolume(driveHandle)) + return Error($"Failed to release the device. GetLastError: {Marshal.GetLastWin32Error()}"); + + + return string.Empty; + } + + + private static SafeFileHandle OpenDeviceHandle(string devicePath) => + CreateFile(devicePath, GENERIC_READ | GENERIC_WRITE, 0, null, FILE_CREATION_DISPOSITION.OPEN_EXISTING, FILE_FLAGS_AND_ATTRIBUTES.FILE_FLAG_NO_BUFFERING, null); + + private static unsafe bool EnableExtendedDASDIO(SafeFileHandle handle) => + DeviceIoControl(handle, FSCTL_ALLOW_EXTENDED_DASD_IO, null, 0, null, 0, null, null); + + private static unsafe bool LockDevice(SafeFileHandle handle) => + DeviceIoControl(handle, FSCTL_LOCK_VOLUME, null, 0, null, 0, null, null); + + private static unsafe bool UnlockDevice(SafeFileHandle handle) => + DeviceIoControl(handle, FSCTL_UNLOCK_VOLUME, null, 0, null, 0, null, null); + + private static unsafe bool DismountVolume(SafeFileHandle handle) => + DeviceIoControl(handle, FSCTL_DISMOUNT_VOLUME, null, 0, null, 0, null, null); + + private static unsafe bool TryGetDiskGeometry(SafeFileHandle handle, out DISK_GEOMETRY diskGeometry) + { + diskGeometry = new DISK_GEOMETRY(); + fixed (DISK_GEOMETRY* pDiskGeometry = &diskGeometry) { - if (!DeviceIoControl(driveHandle, IOCTL_DISK_GET_DRIVE_GEOMETRY, 0, 0, pDiskGeometry.Pointer, pDiskGeometry.Size, out cbRet, 0)) - return (-1, Error("Failed to get the drive geometry.")); - - diskGeometry = Marshal.PtrToStructure(pDiskGeometry.Pointer); + return DeviceIoControl(handle, IOCTL_DISK_GET_DRIVE_GEOMETRY, null, 0, pDiskGeometry, (uint)sizeof(DISK_GEOMETRY), null, null); } - bytesPerSector = diskGeometry.BytesPerSector; + } - using (var pDrivePartInfo = NativePointer.Allocate()) + private static unsafe bool TryGetPartitionInfo(SafeFileHandle handle, ref DISK_GEOMETRY diskGeometry, out PARTITION_INFORMATION partitionInfo, out bool isGPT) + { + partitionInfo = new PARTITION_INFORMATION(); + PARTITION_INFORMATION_EX diskExPartInfo = new(); + isGPT = false; + + fixed (PARTITION_INFORMATION* pPartitionInfo = &partitionInfo) { - if (!DeviceIoControl(driveHandle, IOCTL_DISK_GET_PARTITION_INFO, 0, 0, pDrivePartInfo.Pointer, pDrivePartInfo.Size, out cbRet, 0)) - return (-1, Error("Failed to get the drive partition information.")); + if (!DeviceIoControl(handle, IOCTL_DISK_GET_PARTITION_INFO, null, 0, pPartitionInfo, (uint)sizeof(PARTITION_INFORMATION), null, null)) + { + if (DeviceIoControl(handle, IOCTL_DISK_GET_PARTITION_INFO_EX, null, 0, &diskExPartInfo, (uint)sizeof(PARTITION_INFORMATION_EX), null, null)) + { + partitionInfo = new PARTITION_INFORMATION + { + StartingOffset = diskExPartInfo.StartingOffset, + PartitionLength = diskExPartInfo.PartitionLength, + HiddenSectors = (uint)(diskExPartInfo.StartingOffset / diskGeometry.BytesPerSector) + }; + isGPT = (diskExPartInfo.PartitionStyle == PARTITION_STYLE.PARTITION_STYLE_GPT); + return true; + } + return false; + } + return true; + } + } - diskPartInfo = Marshal.PtrToStructure(pDrivePartInfo.Pointer); + private static bool IsValidFAT32Size(uint totalSectors) => + totalSectors >= 65536 && totalSectors < 0xFFFFFFFF; + + private static unsafe FAT32BootSector InitializeBootSector(DISK_GEOMETRY diskGeometry, PARTITION_INFORMATION partitionInfo, uint totalSectors, uint volumeID) + { + byte sectorsPerCluster = CalculateSectorsPerCluster((ulong)partitionInfo.PartitionLength, diskGeometry.BytesPerSector); + uint fatSize = CalculateFATSize(totalSectors, 32, sectorsPerCluster, 2, diskGeometry.BytesPerSector); + + FAT32BootSector bootSector = new FAT32BootSector + { + BytesPerSector = (ushort)diskGeometry.BytesPerSector, + SectorsPerCluster = sectorsPerCluster, + ReservedSectorCount = 32, + NumberOfFATs = 2, + MediaDescriptor = 0xF8, + SectorsPerTrack = (ushort)diskGeometry.SectorsPerTrack, + NumberOfHeads = (ushort)diskGeometry.TracksPerCylinder, + HiddenSectors = partitionInfo.HiddenSectors, + TotalSectors = totalSectors, + SectorsPerFAT = fatSize, + RootCluster = 2, + FSInfoSector = 1, + BackupBootSector = 6, + DriveNumber = 0x80, + BootSignature = 0x29, + VolumeID = volumeID, + Signature = 0x55AA + }; + + Span rawBytes = MemoryMarshal.AsBytes(new Span(ref bootSector)); + + if (bootSector.BytesPerSector != 512) + { + rawBytes[bootSector.BytesPerSector - 2] = 0x55; + rawBytes[bootSector.BytesPerSector - 1] = 0xAA; } - using (var pDriveExPartInfo = NativePointer.Allocate()) + rawBytes[0] = 0xEB; + rawBytes[1] = 0x58; + rawBytes[2] = 0x90; + + string oemName = "MSWIN4.1"; + Encoding.ASCII.GetBytes(oemName).CopyTo(rawBytes.Slice(3, 8)); + + string volumeLabel = "BADUPDATE "; + Encoding.ASCII.GetBytes(volumeLabel).CopyTo(rawBytes.Slice(71, 11)); + + string fileSystemType = "FAT32 "; + Encoding.ASCII.GetBytes(fileSystemType).CopyTo(rawBytes.Slice(82, 8)); + + return bootSector; + } + + private static FAT32FsInfoSector InitializeFsInfo() => new FAT32FsInfoSector + { + LeadSignature = 0x41615252, + StructureSignature = 0x61417272, + TrailSignature = 0xAA550000, + FreeClusterCount = 0, + NextFreeCluster = 3 + }; + + private static uint[] InitializeFirstFATSector(uint bytesPerSector) + { + uint[] sector = new uint[bytesPerSector / 4]; + sector[0] = 0x0FFFFFF8; + sector[1] = 0x0FFFFFFF; + sector[2] = 0x0FFFFFFF; + return sector; + } + + private static unsafe string FormatVolumeData(SafeFileHandle driveHandle, DISK_GEOMETRY geometry, FAT32BootSector bootSector, FAT32FsInfoSector fsInfo, uint[] firstFATSector, bool isGPT, PARTITION_INFORMATION partitionInfo) + { + uint bytesPerSector = geometry.BytesPerSector; + uint totalSectors = (uint)(partitionInfo.PartitionLength / geometry.BytesPerSector); + uint userAreaSize = totalSectors - bootSector.ReservedSectorCount - (bootSector.NumberOfFATs * bootSector.SectorsPerFAT); + uint systemAreaSize = bootSector.ReservedSectorCount + (bootSector.NumberOfFATs * bootSector.SectorsPerFAT) + bootSector.SectorsPerCluster; + uint clusterCount = userAreaSize / bootSector.SectorsPerCluster; + + if (clusterCount < 65536 || clusterCount > 0x0FFFFFFF) + return Error("The drives cluster count is out of range (65536 < clusterCount < 0x0FFFFFFF)"); + + fsInfo.FreeClusterCount = (userAreaSize / bootSector.SectorsPerCluster) - 1; + + ZeroOutSectors(driveHandle, 0, systemAreaSize, bytesPerSector); + + for (int i = 0; i < 2; i++) { - if (!DeviceIoControl(driveHandle, IOCTL_DISK_GET_PARTITION_INFO_EX, 0, 0, pDriveExPartInfo.Pointer, pDriveExPartInfo.Size, out cbRet, 0)) - return (-1, Error("Failed to get the drive extended partition information.")); - - exDiskPartInfo = Marshal.PtrToStructure(pDriveExPartInfo.Pointer); + uint sectorStart = (i == 0) ? 0 : (uint)bootSector.BackupBootSector; + WriteSector(driveHandle, sectorStart, 1, bytesPerSector, StructToBytes(bootSector)); + WriteSector(driveHandle, sectorStart + 1, 1, bytesPerSector, StructToBytes(fsInfo)); } - isGPT = (exDiskPartInfo.PartitionStyle == PARTITION_STYLE.GPT); - totalSectors = (uint)(diskPartInfo.PartitionLength / diskGeometry.BytesPerSector); - if (totalSectors < 65536 || totalSectors >= 0xffffffff) - return (-1, Error("Invalid drive size for FAT32 - either too small (less than 64K clusters) or too large (greater than 2TB).")); + for (int i = 0; i < bootSector.NumberOfFATs; i++) + { + uint sectorStart = bootSector.ReservedSectorCount + ((uint)i * bootSector.SectorsPerFAT); + WriteSector(driveHandle, sectorStart, 1, bytesPerSector, UintArrayToBytes(firstFATSector)); + } - FAT32BootSector bootSector; - FAT32FsInfoSector fsInfo; + if (!isGPT) + { + SET_PARTITION_INFORMATION setPartInfo = new SET_PARTITION_INFORMATION + { + PartitionType = 0x0C + }; - bootSector.JumpCode = [0xEB, 0x58, 0x90]; - bootSector.OEMName = "MSWIN4.1".ToCharArray(); - bootSector.BytesPerSector = (ushort)bytesPerSector; - bootSector.SectorsPerCluster = CalculateSectorsPerCluster((ulong)diskPartInfo.PartitionLength, bytesPerSector); - bootSector.ReservedSectorCount = 32; - bootSector.NumberOfFATs = 2; - bootSector.MaxRootEntries = 0; - bootSector.TotalSectors16 = 0; - bootSector.MediaDescriptor = 0xF8; - bootSector.SectorsPerFAT16 = 0; - bootSector.SectorsPerTrack = (ushort)diskGeometry.SectorsPerTrack; - bootSector.NumberOfHeads = (ushort)diskGeometry.TracksPerCylinder; - bootSector.HiddenSectors = diskPartInfo.HiddenSectors; - bootSector.TotalSectors = totalSectors; + if (!DeviceIoControl(driveHandle, IOCTL_DISK_SET_PARTITION_INFO, &setPartInfo, (uint)sizeof(SET_PARTITION_INFORMATION), null, 0, null, null)) + return Error($"Failed to set the drive partition information. GetLastError: {Marshal.GetLastWin32Error()}"); + } - fatSize = CalculateFATSize(bootSector.TotalSectors, bootSector.ReservedSectorCount, bootSector.SectorsPerCluster, bootSector.NumberOfFATs, bytesPerSector); - - bootSector.SectorsPerFAT = fatSize; - bootSector.FATFlags = 0; - bootSector.FileSystemVersion = 0; - bootSector.RootCluster = 2; - bootSector.FSInfoSector = 1; - bootSector.BackupBootSector = 6; - bootSector.DriveNumber = 0x80; - bootSector.Reserved1 = 0; - bootSector.BootSignature = 0x29; - bootSector.VolumeID = volumeID; - bootSector.VolumeLabel = "BADUPDATE ".ToCharArray(); - bootSector.FileSystemType = "FAT32 ".ToCharArray(); - bootSector.Signature = 0x55AA; - - if (!DeviceIoControl(driveHandle, FSCTL_UNLOCK_VOLUME, 0, 0, 0, 0, out cbRet, 0)) - return (-1, Error("Failed to unlock the device.")); - - return (0, ""); + return ""; } } -} +} \ No newline at end of file diff --git a/BadBuilder.Formatter/FAT32BootSector.cs b/BadBuilder.Formatter/FAT32BootSector.cs index ea5d3c6..5f1351e 100644 --- a/BadBuilder.Formatter/FAT32BootSector.cs +++ b/BadBuilder.Formatter/FAT32BootSector.cs @@ -5,15 +5,10 @@ namespace BadBuilder.Formatter // Reference: https://cscie92.dce.harvard.edu/spring2024/K70F120M/bootSector.h [StructLayout(LayoutKind.Explicit, Pack = 1, Size = 512)] - internal struct FAT32BootSector + internal unsafe struct FAT32BootSector { - [FieldOffset(0)] - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] - internal byte[] JumpCode; - - [FieldOffset(3)] - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - internal char[] OEMName; + [FieldOffset(0)] public fixed byte JumpCode[3]; + [FieldOffset(3)] public fixed char OEMName[8]; [FieldOffset(11)] internal ushort BytesPerSector; [FieldOffset(13)] internal byte SectorsPerCluster; @@ -36,26 +31,15 @@ namespace BadBuilder.Formatter [FieldOffset(48)] internal ushort FSInfoSector; [FieldOffset(50)] internal ushort BackupBootSector; - [FieldOffset(52)] - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)] - internal byte[] Reserved; + [FieldOffset(52)] public fixed byte Reserved[12]; - [FieldOffset(64)] internal byte DriveNumber; - [FieldOffset(65)] internal byte Reserved1; - [FieldOffset(66)] internal byte BootSignature; - [FieldOffset(67)] internal uint VolumeID; - - [FieldOffset(71)] - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 11)] - internal char[] VolumeLabel; - - [FieldOffset(82)] - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - internal char[] FileSystemType; - - [FieldOffset(90)] - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 420)] - internal byte[] Reserved2; + [FieldOffset(64)] public byte DriveNumber; + [FieldOffset(65)] public byte Reserved1; + [FieldOffset(66)] public byte BootSignature; + [FieldOffset(67)] public uint VolumeID; + [FieldOffset(71)] public fixed char VolumeLabel[11]; + [FieldOffset(82)] public fixed char FileSystemType[8]; + [FieldOffset(90)] public fixed byte Reserved2[420]; [FieldOffset(510)] internal ushort Signature; } diff --git a/BadBuilder.Formatter/FAT32FsInfoSector.cs b/BadBuilder.Formatter/FAT32FsInfoSector.cs index bfdb1ef..2daac5e 100644 --- a/BadBuilder.Formatter/FAT32FsInfoSector.cs +++ b/BadBuilder.Formatter/FAT32FsInfoSector.cs @@ -5,21 +5,17 @@ namespace BadBuilder.Formatter // Reference: https://cscie92.dce.harvard.edu/spring2024/K70F120M/fsInfo.h [StructLayout(LayoutKind.Explicit, Pack = 1, Size = 512)] - internal struct FAT32FsInfoSector + internal unsafe struct FAT32FsInfoSector { [FieldOffset(0)] public uint LeadSignature; - [FieldOffset(4)] - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 480)] - public byte[] Reserved1; // Zeros + [FieldOffset(4)] public fixed byte Reserved1[480]; // Zeros [FieldOffset(484)] public uint StructureSignature; [FieldOffset(488)] public uint FreeClusterCount; [FieldOffset(492)] public uint NextFreeCluster; - [FieldOffset(496)] - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)] - public byte[] Reserved2; + [FieldOffset(496)] public fixed byte Reserved2[12]; [FieldOffset(508)] public uint TrailSignature; } diff --git a/BadBuilder.Formatter/NativeMethods.txt b/BadBuilder.Formatter/NativeMethods.txt new file mode 100644 index 0000000..fd019bf --- /dev/null +++ b/BadBuilder.Formatter/NativeMethods.txt @@ -0,0 +1,12 @@ +CreateFileW +WriteFile +SetFilePointer +DeviceIoControl +CloseHandle +VirtualAlloc +VirtualFree +GUID +DISK_GEOMETRY +PARTITION_INFORMATION +PARTITION_INFORMATION_EX +SET_PARTITION_INFORMATION \ No newline at end of file diff --git a/BadBuilder.Formatter/FAT32Utilities.cs b/BadBuilder.Formatter/Utilities.cs similarity index 50% rename from BadBuilder.Formatter/FAT32Utilities.cs rename to BadBuilder.Formatter/Utilities.cs index ab1c011..23fe006 100644 --- a/BadBuilder.Formatter/FAT32Utilities.cs +++ b/BadBuilder.Formatter/Utilities.cs @@ -1,39 +1,24 @@ -using static BadBuilder.Formatter.Win32; -using static BadBuilder.Formatter.Constants; +using Windows.Win32.Foundation; +using Windows.Win32.System.Memory; using System.Runtime.InteropServices; +using static Windows.Win32.PInvoke; +using static BadBuilder.Formatter.Constants; + namespace BadBuilder.Formatter { - static class FAT32Utilities + static class Utilities { - internal struct NativePointer : IDisposable + internal static byte[] StructToBytes(T @struct) where T : struct { - internal IntPtr Pointer; - internal uint Size; - - internal NativePointer(Type type) - { - uint size = (uint)Marshal.SizeOf(type); - Pointer = Marshal.AllocHGlobal((int)size); - Size = size; - } - - public void Dispose() - { - if (Pointer != IntPtr.Zero) - { - Marshal.FreeHGlobal(Pointer); - Pointer = IntPtr.Zero; - Size = 0; - } - } - - internal static NativePointer Allocate() where T : struct - { - return new NativePointer(typeof(T)); - } + Span structSpan = MemoryMarshal.CreateSpan(ref @struct, 1); + Span byteSpan = MemoryMarshal.AsBytes(structSpan); + return byteSpan.ToArray(); } + internal static byte[] UintArrayToBytes(uint[] array) => MemoryMarshal.AsBytes(array.AsSpan()).ToArray(); + + internal static string Error(string error) => $"{ORANGE}[-]{ANSI_RESET} {error}"; internal static void ExitWithError(string error) { @@ -74,41 +59,54 @@ namespace BadBuilder.Formatter }; - internal static void SeekTo(IntPtr hDevice, uint sector, uint bytesPerSector) + internal static unsafe void SeekTo(SafeHandle hDevice, uint sector, uint bytesPerSector) { long offset = sector * bytesPerSector; int lowOffset = (int)(offset & 0xFFFFFFFF); int highOffset = (int)(offset >> 32); - SetFilePointer(hDevice, lowOffset, ref highOffset, FILE_BEGIN); + SetFilePointer(hDevice, lowOffset, &highOffset, Windows.Win32.Storage.FileSystem.SET_FILE_POINTER_MOVE_METHOD.FILE_BEGIN); } - internal static void WriteSector(IntPtr hDevice, uint sector, uint numberOfSectors, uint bytesPerSector, byte[] data) + internal static unsafe void WriteSector(SafeHandle hDevice, uint sector, uint numberOfSectors, uint bytesPerSector, byte[] data) { uint bytesWritten; SeekTo(hDevice, sector, bytesPerSector); - if (!WriteFile(hDevice, data, numberOfSectors * bytesPerSector, out bytesWritten, 0)) - ExitWithError("Unable to write to write sectors to FAT32 device, exiting."); + fixed (byte* pData = &data[0]) + { + if (!WriteFile(new HANDLE(hDevice.DangerousGetHandle()), pData, numberOfSectors * bytesPerSector, &bytesWritten, null)) + ExitWithError($"Unable to write sectors to FAT32 device, exiting. GetLastError: {Marshal.GetLastWin32Error()}"); + } } - internal static void ZeroOutSectors(IntPtr hDevice, uint sector, uint numberOfSectors, uint bytesPerSector) + internal static unsafe void ZeroOutSectors(SafeHandle hDevice, uint sector, uint numberOfSectors, uint bytesPerSector) { const uint burstSize = 128; uint writeSize; + uint bytesWritten; - byte[] zeroBuffer = new byte[bytesPerSector * burstSize]; - Array.Clear(zeroBuffer); + byte* pZeroSector = (byte*)VirtualAlloc(null, bytesPerSector * burstSize, VIRTUAL_ALLOCATION_TYPE.MEM_COMMIT | VIRTUAL_ALLOCATION_TYPE.MEM_RESERVE, PAGE_PROTECTION_FLAGS.PAGE_READWRITE); - SeekTo(hDevice, sector, bytesPerSector); - - while (numberOfSectors > 0) + try { - writeSize = (numberOfSectors > burstSize) ? burstSize : numberOfSectors; - WriteSector(hDevice, sector, numberOfSectors, bytesPerSector, zeroBuffer); - numberOfSectors -= writeSize; + SeekTo(hDevice, sector, bytesPerSector); + + while (numberOfSectors > 0) + { + writeSize = (numberOfSectors > burstSize) ? burstSize : numberOfSectors; + + if (!WriteFile(new HANDLE(hDevice.DangerousGetHandle()), pZeroSector, writeSize * bytesPerSector, &bytesWritten, null)) + ExitWithError($"Unable to write sectors to FAT32 device, exiting. GetLastError: {Marshal.GetLastWin32Error()}"); + + numberOfSectors -= writeSize; + } + } + finally + { + VirtualFree(pZeroSector, bytesPerSector * burstSize, VIRTUAL_FREE_TYPE.MEM_RELEASE); } } } diff --git a/BadBuilder.Formatter/Win32.cs b/BadBuilder.Formatter/Win32.cs deleted file mode 100644 index 91154d3..0000000 --- a/BadBuilder.Formatter/Win32.cs +++ /dev/null @@ -1,238 +0,0 @@ -using System.Runtime.InteropServices; - -namespace BadBuilder.Formatter -{ - static partial class Win32 - { - internal const uint GENERIC_READ = 0x80000000; - internal const uint GENERIC_WRITE = 0x40000000; - internal const uint OPEN_EXISTING = 3; - internal const uint FILE_SHARE_READ = 1; - internal const uint FILE_BEGIN = 0; - internal const uint FILE_FLAG_NO_BUFFERING = 0x20000000; - - internal const uint IOCTL_DISK_GET_DRIVE_GEOMETRY = 0x00070000; - internal const uint IOCTL_DISK_GET_PARTITION_INFO_EX = 0x00070048; - internal const uint IOCTL_DISK_GET_PARTITION_INFO = 0x00074004; - internal const uint FSCTL_LOCK_VOLUME = 0x00090018; - internal const uint FSCTL_UNLOCK_VOLUME = 0x0009001C; - internal const uint FSCTL_QUERY_RETRIEVAL_POINTERS = 0x0009003B; - internal const uint FSCTL_GET_COMPRESSION = 0x0009003C; - internal const uint FSCTL_SET_COMPRESSION = 0x0009C040; - internal const uint FSCTL_SET_BOOTLOADER_ACCESSED = 0x0009004F; - internal const uint FSCTL_MARK_AS_SYSTEM_HIVE = 0x0009004F; - internal const uint FSCTL_OPLOCK_BREAK_ACK_NO_2 = 0x00090050; - internal const uint FSCTL_INVALIDATE_VOLUMES = 0x00090054; - internal const uint FSCTL_QUERY_FAT_BPB = 0x00090058; - internal const uint FSCTL_REQUEST_FILTER_OPLOCK = 0x0009005C; - internal const uint FSCTL_FILESYSTEM_GET_STATISTICS = 0x00090060; - internal const uint FSCTL_GET_NTFS_VOLUME_DATA = 0x00090064; - internal const uint FSCTL_GET_NTFS_FILE_RECORD = 0x00090068; - internal const uint FSCTL_GET_VOLUME_BITMAP = 0x0009006F; - internal const uint FSCTL_GET_RETRIEVAL_POINTERS = 0x00090073; - internal const uint FSCTL_MOVE_FILE = 0x00090074; - internal const uint FSCTL_IS_VOLUME_DIRTY = 0x00090078; - internal const uint FSCTL_ALLOW_EXTENDED_DASD_IO = 0x00090083; - - - private const string Kernel32 = "kernel32.dll"; - - [LibraryImport(Kernel32, SetLastError = true)] - internal static partial IntPtr CreateFileW( - [MarshalAs(UnmanagedType.LPWStr)] string lpFileName, - uint dwDesiredAccess, - uint dwShareMode, - IntPtr lpSecurityAttributes, - uint dwCreationDisposition, - uint dwFlagsAndAttributes, - IntPtr hTemplateFile); - - - [LibraryImport(Kernel32, SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - internal static partial bool WriteFile( - IntPtr hFile, - byte[] lpBuffer, - uint nNumberOfBytesToWrite, - out uint lpNumberOfBytesWritten, - IntPtr lpOverlapped); - - [LibraryImport(Kernel32, SetLastError = true)] - internal static partial uint SetFilePointer( - IntPtr hFile, - int lDistanceToMove, - ref int lpDistanceToMoveHigh, - uint dwMoveMethod); - - [LibraryImport(Kernel32, SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - internal static partial bool DeviceIoControl( - IntPtr hDevice, - uint dwIoControlCode, - IntPtr lpInBuffer, - uint nInBufferSize, - IntPtr lpOutBuffer, - uint nOutBufferSize, - out uint lpBytesReturned, - IntPtr lpOverlapped); - - [LibraryImport(Kernel32, SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - internal static partial bool CloseHandle(IntPtr hObject); - - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - internal struct DISK_GEOMETRY - { - internal long Cylinders; - internal MediaType MediaType; - internal uint TracksPerCylinder; - internal uint SectorsPerTrack; - internal uint BytesPerSector; - } - - internal enum MediaType // from winioctl.h - { - Unknown, // Format is unknown - F5_1Pt2_512, // 5.25", 1.2MB, 512 bytes/sector - F3_1Pt44_512, // 3.5", 1.44MB, 512 bytes/sector - F3_2Pt88_512, // 3.5", 2.88MB, 512 bytes/sector - F3_20Pt8_512, // 3.5", 20.8MB, 512 bytes/sector - F3_720_512, // 3.5", 720KB, 512 bytes/sector - F5_360_512, // 5.25", 360KB, 512 bytes/sector - F5_320_512, // 5.25", 320KB, 512 bytes/sector - F5_320_1024, // 5.25", 320KB, 1024 bytes/sector - F5_180_512, // 5.25", 180KB, 512 bytes/sector - F5_160_512, // 5.25", 160KB, 512 bytes/sector - RemovableMedia, // Removable media other than floppy - FixedMedia, // Fixed hard disk media - F3_120M_512, // 3.5", 120M Floppy - F3_640_512, // 3.5" , 640KB, 512 bytes/sector - F5_640_512, // 5.25", 640KB, 512 bytes/sector - F5_720_512, // 5.25", 720KB, 512 bytes/sector - F3_1Pt2_512, // 3.5" , 1.2Mb, 512 bytes/sector - F3_1Pt23_1024, // 3.5" , 1.23Mb, 1024 bytes/sector - F5_1Pt23_1024, // 5.25", 1.23MB, 1024 bytes/sector - F3_128Mb_512, // 3.5" MO 128Mb 512 bytes/sector - F3_230Mb_512, // 3.5" MO 230Mb 512 bytes/sector - F8_256_128, // 8", 256KB, 128 bytes/sector - F3_200Mb_512, // 3.5", 200M Floppy (HiFD) - F3_240M_512, // 3.5", 240Mb Floppy (HiFD) - F3_32M_512 // 3.5", 32Mb Floppy - } - - [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 16)] - internal struct GUID - { - internal uint Data1; - internal ushort Data2; - internal ushort Data3; - - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - internal byte[] Data4; - } - - [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 32)] - internal struct PARTITION_INFORMATION - { - internal long StartingOffset; - internal long PartitionLength; - internal uint HiddenSectors; - internal uint PartitionNumber; - internal byte PartitionType; - internal byte BootIndicator; - internal byte RecognizedPattern; - internal byte RewritePartition; - } - - [StructLayout(LayoutKind.Explicit, Size = 144)] - internal struct PARTITION_INFORMATION_EX - { - [FieldOffset(0)] - internal PARTITION_STYLE PartitionStyle; - - [FieldOffset(8)] - internal long StartingOffset; - - [FieldOffset(16)] - internal long PartitionLength; - - [FieldOffset(24)] - internal uint PartitionNumber; - - [FieldOffset(28)] - internal byte RewritePartition; - - [FieldOffset(29)] - internal byte IsServicePartition; - - [FieldOffset(32)] - internal unsafe fixed byte Union[112]; - - public unsafe PARTITION_INFORMATION_MBR Mbr - { - get - { - fixed (byte* p = Union) - { - return *(PARTITION_INFORMATION_MBR*)p; - } - } - set - { - fixed (byte* p = Union) - { - *(PARTITION_INFORMATION_MBR*)p = value; - } - } - } - - public unsafe PARTITION_INFORMATION_GPT Gpt - { - get - { - fixed (byte* p = Union) - { - return *(PARTITION_INFORMATION_GPT*)p; - } - } - set - { - fixed (byte* p = Union) - { - *(PARTITION_INFORMATION_GPT*)p = value; - } - } - } - } - - [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 24)] - internal struct PARTITION_INFORMATION_MBR - { - internal byte PartitionType; - internal byte BootIndicator; - internal byte RecognizedPartition; - private byte _padding1; - internal uint HiddenSectors; - internal GUID PartitionId; - } - - [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 112)] - internal struct PARTITION_INFORMATION_GPT - { - internal GUID PartitionType; - internal GUID PartitionId; - internal ulong Attributes; - - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 36)] - internal ushort[] Name; - } - - internal enum PARTITION_STYLE : uint - { - MBR = 0, - GPT = 1, - RAW = 2 - } - } -} \ No newline at end of file diff --git a/BadBuilder/BadBuilder.csproj b/BadBuilder/BadBuilder.csproj index 9032e08..558c64d 100644 --- a/BadBuilder/BadBuilder.csproj +++ b/BadBuilder/BadBuilder.csproj @@ -13,10 +13,6 @@ - - - - diff --git a/BadBuilder/ConsoleExperiences/DiskExperience.cs b/BadBuilder/ConsoleExperiences/DiskExperience.cs index bebf627..66b0ee4 100644 --- a/BadBuilder/ConsoleExperiences/DiskExperience.cs +++ b/BadBuilder/ConsoleExperiences/DiskExperience.cs @@ -1,11 +1,6 @@ using Spectre.Console; using BadBuilder.Models; using BadBuilder.Helpers; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace BadBuilder { @@ -38,15 +33,28 @@ namespace BadBuilder ); } - static void FormatDisk(List disks, string selectedDisk) + static bool FormatDisk(List disks, string selectedDisk) { 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}", ctx => { if (diskIndex == -1) return; - DiskHelper.FormatDisk(disks[diskIndex]).Wait(); + + output = DiskHelper.FormatDisk(disks[diskIndex]); + if (output != string.Empty) ret = false; }); + + if (!ret) + { + AnsiConsole.Clear(); + ShowWelcomeMessage(); + Console.Write("\n" + output + "\n"); + } + + return ret; } } -} +} \ No newline at end of file diff --git a/BadBuilder/ConsoleExperiences/DownloadExperience.cs b/BadBuilder/ConsoleExperiences/DownloadExperience.cs index c216de6..aad3fe0 100644 --- a/BadBuilder/ConsoleExperiences/DownloadExperience.cs +++ b/BadBuilder/ConsoleExperiences/DownloadExperience.cs @@ -1,7 +1,7 @@ using Spectre.Console; using BadBuilder.Helpers; -using static BadBuilder.Constants; +using static BadBuilder.Utilities.Constants; namespace BadBuilder { diff --git a/BadBuilder/ConsoleExperiences/ExtractExperience.cs b/BadBuilder/ConsoleExperiences/ExtractExperience.cs index 7a469b3..2671e7e 100644 --- a/BadBuilder/ConsoleExperiences/ExtractExperience.cs +++ b/BadBuilder/ConsoleExperiences/ExtractExperience.cs @@ -30,4 +30,4 @@ namespace BadBuilder 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 b098e81..df9904a 100644 --- a/BadBuilder/ConsoleExperiences/HomebrewExperience.cs +++ b/BadBuilder/ConsoleExperiences/HomebrewExperience.cs @@ -1,9 +1,4 @@ using Spectre.Console; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace BadBuilder { @@ -167,4 +162,4 @@ namespace BadBuilder AnsiConsole.MarkupLine($"[#ffac4d]Removed:[/] {selectedApp.folder.Split('\\').Last()}\n"); } } -} +} \ No newline at end of file diff --git a/BadBuilder/Helpers/ArchiveHelper.cs b/BadBuilder/Helpers/ArchiveHelper.cs index ef731a0..0021406 100644 --- a/BadBuilder/Helpers/ArchiveHelper.cs +++ b/BadBuilder/Helpers/ArchiveHelper.cs @@ -1,8 +1,9 @@ -using SharpCompress.Archives; -using SharpCompress.Common; -using Spectre.Console; +using Spectre.Console; using System.Diagnostics; -using static BadBuilder.Constants; +using SharpCompress.Common; +using SharpCompress.Archives; + +using static BadBuilder.Utilities.Constants; namespace BadBuilder.Helpers { @@ -27,7 +28,7 @@ namespace BadBuilder.Helpers } catch (Exception ex) { - // Sorry, but these exceptions are invalid. SharpCompress is mad because there's nested ZIPs in xexmenu. + // Sorry, but these exceptions are invalid. The files extract just fine. Debug.WriteLine(ex); } } diff --git a/BadBuilder/Helpers/DiskHelper.cs b/BadBuilder/Helpers/DiskHelper.cs index 784c299..9403dfa 100644 --- a/BadBuilder/Helpers/DiskHelper.cs +++ b/BadBuilder/Helpers/DiskHelper.cs @@ -1,6 +1,5 @@ using BadBuilder.Models; -using System.Diagnostics; -using System.Management; +using BadBuilder.Formatter; namespace BadBuilder.Helpers { @@ -28,25 +27,6 @@ namespace BadBuilder.Helpers return disks; } - internal static async Task FormatDisk(DiskInfo disk) - { - ResourceHelper.ExtractEmbeddedBinary("fat32format.exe"); - - Process process = new Process - { - StartInfo = new ProcessStartInfo - { - FileName = @"fat32format.exe", - Arguments = $"-c64 {disk.DriveLetter}", - RedirectStandardOutput = false, - RedirectStandardError = false, - UseShellExecute = false, - CreateNoWindow = true - } - }; - - process.Start(); - await process.WaitForExitAsync(); - } + internal static string FormatDisk(DiskInfo disk) => DiskFormatter.FormatVolume(disk.DriveLetter.ToCharArray()[0]); } } \ No newline at end of file diff --git a/BadBuilder/Helpers/DownloadHelper.cs b/BadBuilder/Helpers/DownloadHelper.cs index f19ded5..3265531 100644 --- a/BadBuilder/Helpers/DownloadHelper.cs +++ b/BadBuilder/Helpers/DownloadHelper.cs @@ -1,7 +1,7 @@ using Octokit; using Spectre.Console; -using static BadBuilder.Constants; +using static BadBuilder.Utilities.Constants; namespace BadBuilder.Helpers { diff --git a/BadBuilder/Helpers/FileSystemHelper.cs b/BadBuilder/Helpers/FileSystemHelper.cs index 38b90a7..e5c0630 100644 --- a/BadBuilder/Helpers/FileSystemHelper.cs +++ b/BadBuilder/Helpers/FileSystemHelper.cs @@ -1,6 +1,4 @@ -using BadBuilder.Models; - -namespace BadBuilder.Helpers +namespace BadBuilder.Helpers { internal static class FileSystemHelper { diff --git a/BadBuilder/Helpers/PatchHelper.cs b/BadBuilder/Helpers/PatchHelper.cs index 2aff559..16c6858 100644 --- a/BadBuilder/Helpers/PatchHelper.cs +++ b/BadBuilder/Helpers/PatchHelper.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Diagnostics; namespace BadBuilder.Helpers { diff --git a/BadBuilder/Helpers/ResourceHelper.cs b/BadBuilder/Helpers/ResourceHelper.cs deleted file mode 100644 index 92bc13e..0000000 --- a/BadBuilder/Helpers/ResourceHelper.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Reflection; - -namespace BadBuilder.Helpers -{ - internal static class ResourceHelper - { - internal static void ExtractEmbeddedBinary(string resourceName) - { - var assembly = Assembly.GetExecutingAssembly(); - string fullResourceName = $"BadBuilder.Resources.{resourceName}"; - - using (Stream resourceStream = assembly.GetManifestResourceStream(fullResourceName)) - { - if (resourceStream == null) return; - - using (FileStream fileStream = new FileStream($@"{resourceName}", FileMode.Create, FileAccess.Write)) - resourceStream.CopyTo(fileStream); - } - } - } -} \ No newline at end of file diff --git a/BadBuilder/Models/DiskInfo.cs b/BadBuilder/Models/DiskInfo.cs index f385250..e9bbb49 100644 --- a/BadBuilder/Models/DiskInfo.cs +++ b/BadBuilder/Models/DiskInfo.cs @@ -37,4 +37,4 @@ return $"{bytes} bytes"; } } -} +} \ No newline at end of file diff --git a/BadBuilder/Program.cs b/BadBuilder/Program.cs index d0967cc..6c2cd7c 100644 --- a/BadBuilder/Program.cs +++ b/BadBuilder/Program.cs @@ -6,7 +6,8 @@ using Spectre.Console; using BadBuilder.Models; using BadBuilder.Helpers; -using static BadBuilder.Constants; +using static BadBuilder.Utilities.Constants; +using BadBuilder.Utilities; namespace BadBuilder { @@ -26,7 +27,6 @@ namespace BadBuilder static void Main(string[] args) { - BadBuilder.Formatter.DiskFormatter.FormatVolume('E'); ShowWelcomeMessage(); while (true) @@ -43,7 +43,7 @@ namespace BadBuilder bool confirmation = PromptFormatConfirmation(selectedDisk); if (confirmation) { - FormatDisk(disks, selectedDisk); + if (!FormatDisk(disks, selectedDisk)) continue; break; } } diff --git a/BadBuilder/Resources/fat32format.exe b/BadBuilder/Resources/fat32format.exe deleted file mode 100644 index 40baa541c77a52da2cb4c9ed4778559323a869f9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 49218 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 WXKKt@HQiPz^cqx&Ew+W^diHNdEVU2- diff --git a/BadBuilder/Models/ActionQueue.cs b/BadBuilder/Utilities/ActionQueue.cs similarity index 96% rename from BadBuilder/Models/ActionQueue.cs rename to BadBuilder/Utilities/ActionQueue.cs index 7bb263c..7146615 100644 --- a/BadBuilder/Models/ActionQueue.cs +++ b/BadBuilder/Utilities/ActionQueue.cs @@ -1,4 +1,4 @@ -namespace BadBuilder.Models +namespace BadBuilder.Utilities { internal class ActionQueue { diff --git a/BadBuilder/Constants.cs b/BadBuilder/Utilities/Constants.cs similarity index 90% rename from BadBuilder/Constants.cs rename to BadBuilder/Utilities/Constants.cs index 1d0aa23..4ddd874 100644 --- a/BadBuilder/Constants.cs +++ b/BadBuilder/Utilities/Constants.cs @@ -1,4 +1,4 @@ -namespace BadBuilder +namespace BadBuilder.Utilities { internal static class Constants {