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
{