Native Windows WPF tool for formatting drives as FAT32 above Windows built-in 32 GB limit. Includes a CLI mode for scripting.
  • C# 89.3%
  • PowerShell 5.4%
  • NSIS 5.3%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
arelas 90cd5f76f7 Wrap the About dialog's project URL so the version stays visible
A release URL is longer than the field, so it clipped at the right edge -
and what fell off was the tail, which is where the version number lives.
After an update check swaps in the release URL, someone glancing at the
field could not see which version the link pointed to. The value and the
Copy link button were always correct; this is presentation only.

Wrapping shows the whole URL. At the dialog's width a release URL runs to
about 1.3 lines, so it costs one line of height in the update case and
none otherwise.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01CyLBbc6HbVEwm9siBWANnF
2026-09-03 17:06:38 -04:00
.forgejo/workflows Drop the permissions block Forgejo does not support 2026-09-03 14:37:27 -04:00
docs Update README screenshot to show full-format option 2026-06-29 16:47:02 -04:00
installer Write the MSI's DisplayIcon explicitly instead of relying on ARPPRODUCTICON 2026-09-03 16:39:20 -04:00
Phat32 Wrap the About dialog's project URL so the version stays visible 2026-09-03 17:06:38 -04:00
.gitignore Add local package.ps1 script for building release zips 2026-06-29 14:23:02 -04:00
global.json Initial commit 2026-06-29 13:42:15 -04:00
icon_preview.png Initial commit 2026-06-29 13:42:15 -04:00
LICENSE Initial commit 2026-06-29 13:42:15 -04:00
package.ps1 Fix icon legibility on light themes and stamp the binary's version 2026-09-03 14:13:17 -04:00
Phat32.sln Initial commit 2026-06-29 13:42:15 -04:00
README.md Redraw the app icon so it survives small sizes 2026-09-03 13:46:26 -04:00
USER_GUIDE.txt Add full-format bad-sector scan (GUI checkbox + --full CLI flag, quick format stays default) 2026-06-29 15:36:38 -04:00

PHAT32

The FAT32 formatter that ignores the 32 GB limit.

A native Windows WPF (.NET 10) tool that formats drives as FAT32 — including drives larger than 32 GB, which Windows' built-in formatter refuses to do.

PHAT32 screenshot

License: MIT .NET Platform

Looking for instructions on how to use the app rather than build it? See USER_GUIDE.txt.

Why this exists

The 32 GB FAT32 cap in format.com / Disk Management is an artificial restriction Microsoft added — the FAT32 format itself supports volumes up to 2 TB (with 512-byte sectors). This is handy for SD cards, USB drives, or external SSDs that need to stay FAT32 for compatibility with cameras, game consoles, car stereos, smart TVs, etc. — devices that often can't read exFAT or NTFS.

This tool calculates the BPB (BIOS Parameter Block) fields, FAT size, and cluster size using the same formulas Windows itself uses internally, then writes the FAT32 structures directly to the volume, bypassing the size check.

Features

  • Lists removable and fixed drives, with size/label/filesystem shown
  • Shows only removable (USB/SD) drives by default — fixed/internal drives are hidden until you explicitly check "Show internal/fixed drives too"
  • Blocks formatting your Windows system drive
  • Advisory warnings for unusual drives (a "removable" drive reporting multiple terabytes, a very small volume, or a fixed drive once you've opted into seeing them) — these don't block you, they just make you look twice
  • Custom volume label and selectable allocation unit (cluster) size, or "Default (recommended)" to match Windows' own sizing logic
  • Confirmation checkbox + a final "this will permanently erase..." dialog before anything is written
  • Reads back the boot sector, FSInfo sector, and FAT entries after writing to confirm they actually landed correctly, rather than just assuming success
  • Optional full-format mode that scans every cluster for bad sectors and marks any found in the FAT (quick format is the default either way)
  • Remembers your last-used cluster size and "show fixed drives" choice between launches
  • Live log and progress bar while formatting
  • Dark, native UI (including a themed confirm/result dialog) that matches Windows' dark mode title bar
  • A headless CLI mode for scripting/automation — see Command-line usage
  • Native builds for both x64 and ARM64 (Windows-on-ARM) Windows devices

⚠️ Before you use it

  • This permanently erases all data on the target drive. There is no undo.
  • It requires Administrator privileges (the app prompts for UAC elevation automatically) because it locks/dismounts the volume and writes raw sectors directly to disk.
  • Quick format is the default — it lays down a clean, empty FAT32 filesystem without scanning for bad sectors. There's also a full format option (a checkbox in the GUI, --full on the CLI) that scans every cluster for bad sectors and is much slower, proportional to the drive's size.
  • This software is provided as-is (see License) with no warranty. Test on a spare/cheap drive first before trusting it with anything that matters.

Requirements

  • Windows 10 (2004+) or Windows 11 — x64 or ARM64 (Windows-on-ARM)
  • .NET 10 Desktop Runtime if running a framework-dependent build, or nothing extra if using a self-contained published build

Building

You'll need Visual Studio 2022 (Community is fine) with the ".NET desktop development" workload, or just the .NET 10 SDK for the command line.

cd Phat32
dotnet build -c Release

Or open Phat32.sln in Visual Studio and press F5. The app will prompt for UAC elevation each time it launches (required for raw disk access).

Publishing a standalone .exe

dotnet publish -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true

This bundles the .NET runtime into a single portable .exe that runs on a bare Windows install with nothing pre-installed. The project is configured with IncludeNativeLibrariesForSelfExtract so WPF's native rendering libraries (e.g. PresentationNative_cor3.dll, wpfgfx_cor3.dll) get bundled into the exe and self-extracted to a temp folder at startup — without that setting, single-file WPF publishes can launch and exit instantly with no window and no visible error, since the native renderer can't be found.

For Windows-on-ARM devices, swap the RID:

dotnet publish -c Release -r win-arm64 --self-contained true -p:PublishSingleFile=true

This cross-publishes fine from an x64 machine (no ARM64 hardware needed to build it) — but that also means the ARM64 build has only ever been cross-compiled, never actually run on real ARM64 hardware. Treat it as unverified until it's been tested on an actual Windows-on-ARM device.

Or just run package.ps1 from the repo root — it does the above publish step and also bundles the resulting exe with USER_GUIDE.txt and LICENSE into a ready-to-share zip under dist/ (the same contents the release workflow produces, but without needing to push a tag):

.\package.ps1                         # builds and zips BOTH win-x64 and win-arm64
.\package.ps1 -Arch x64                # just win-x64
.\package.ps1 -Arch arm64              # just win-arm64
.\package.ps1 -Version 1.2.0           # name the zip(s) explicitly
.\package.ps1 -SkipBuild               # re-zip without rebuilding

If Windows blocks the script from running (...cannot be loaded because running scripts is disabled...), either run it via powershell -ExecutionPolicy Bypass -File package.ps1, or allow local scripts for your account once with Set-ExecutionPolicy -Scope CurrentUser RemoteSigned.

If dotnet publish fails with NU1100 errors about Microsoft.NETCore.App.Runtime.win-x64 or similar, that's a NuGet restore problem (usually a corporate proxy blocking nuget.org), not a project issue — see Troubleshooting.

Code signing

The build is unsigned by default. On a machine that's never run it before, expect a SmartScreen "Windows protected your PC" prompt, and possibly a flag from behavior-based EDR/AV (the disk-locking + raw-sector-write pattern overlaps with how wiper tools behave, regardless of signing). If you're distributing this beyond your own machine, signing with a code-signing certificate or Microsoft Trusted Signing is worth doing:

signtool sign /fd SHA256 /a Phat32.exe

Troubleshooting

dotnet publish fails with NU1100 errors — NuGet can't download the runtime packs needed for a self-contained build. Check your sources:

dotnet nuget list source

Make sure nuget.org is listed and enabled; on a corporate network it may need to be added or proxied through an internal feed. As a quick workaround, drop --self-contained true -r win-x64 and just run dotnet build — the app will run fine with the .NET 10 Desktop Runtime installed instead.

Command-line usage

PHAT32 can also run headlessly for scripting, RMM deployment, or imaging workflows instead of using the GUI:

Phat32.exe --drive E --yes
Phat32.exe --drive F --yes --label BACKUP --cluster 4096
Phat32.exe --drive G --yes --allow-fixed
Phat32.exe --help
Flag Meaning
--drive <Letter> Required. Drive letter to format, e.g. E or E:
--yes Required. Confirms permanent erasure — there's no interactive prompt in CLI mode
--label <Label> Volume label, up to 11 characters
--cluster <bytes|default> Allocation unit size: 4096, 8192, 16384, 32768, 65536, or default
--allow-fixed Required to target a non-removable (fixed/internal) drive
--full Full format: scan every cluster for bad sectors instead of a quick format

Exit codes: 0 success, 1 bad arguments, 2 system drive refused, 3 fixed drive refused (use --allow-fixed), 4 drive not found, 5 format failed, 99 unexpected error.

Note: the app's manifest always requires Administrator, so even CLI invocations trigger UAC unless the calling shell is already elevated. If you run it from an already-elevated PowerShell/cmd, output prints inline in that same window. If not, Windows elevates a new process that can't share the original console, so PHAT32 opens its own console window for output instead — the exit code is still the most reliable thing to check from a calling script either way.

Releases

Pushing a version tag triggers .forgejo/workflows/release.yml, which builds a self-contained single-file exe for both win-x64 and win-arm64, creates the Forgejo release for that tag, and attaches four assets — a portable zip and an MSI installer per architecture:

Asset What it is
Phat32-<tag>-win-x64-setup.exe Setup wizard (x64)
Phat32-<tag>-win-arm64-setup.exe Setup wizard (ARM64)
Phat32-<tag>-win-x64.msi MSI installer (x64)
Phat32-<tag>-win-arm64.msi MSI installer (ARM64)
Phat32-<tag>-win-x64.zip Portable: exe + USER_GUIDE.txt + LICENSE
Phat32-<tag>-win-arm64.zip Portable: exe + USER_GUIDE.txt + LICENSE
git tag v1.3.0
git push origin v1.3.0

Running the workflow manually from the Actions tab builds and uploads the same zips as workflow artifacts without creating a release — useful for a test run.

The workflow runs on the shared EAS-WINRUNNER runner (runs-on: [self-hosted, Windows]). That's an instance-level runner, so it serves this repo without anything registered locally — but the labels must match exactly; windows-latest will not match it. Since it's a self-hosted host-mode runner, it also needs Node available for JavaScript actions, and setup-dotnet installs the .NET 10 SDK on each run.

Installers

Two installers are built per architecture, from sources in installer/. Both are per-machine: they put Phat32 under Program Files\Phat32, add a Start Menu shortcut, and register an Add/Remove Programs entry.

  • -setup.exe — NSIS wizard built from Phat32.nsi. The friendlier option for most people. NSIS comes from the NSIS-Tool NuGet package, which is just a zip of binaries.
  • .msi — WiX package built from Phat32.wxs. Use this for managed or scripted deployment (Group Policy, Intune, msiexec /qn). WiX installs as a dotnet tool, so the runner needs nothing beyond the .NET SDK.

The MSI upgrades an existing install in place, which depends on the UpgradeCode GUID in Phat32.wxs staying fixed forever — changing it makes every future release install alongside the old one instead of replacing it.

Don't install both at once: they write the same files and the same Add/Remove Programs key, so whichever is uninstalled second will find its entry already gone.

The zips remain the portable option: no install, no admin needed to unpack, just run the exe (which prompts for elevation itself).

Signing

Releases are not code-signed. The exe ships as Phat32-unsigned.exe and Windows SmartScreen will warn users on first run.

Signing previously went through SignPath Foundation, which signs qualifying open-source projects for free. That route is GitHub-only — its action fetches binaries from GitHub's artifact API, and the Foundation program itself requires a GitHub-hosted repo — so it did not survive the move to Forgejo. Turning signing back on means either a paid certificate (an OV cert or Azure Trusted Signing) held as a Forgejo secret, or a signing service that supports Forgejo natively.

How it works

It writes, per Microsoft's published FAT32 File System specification:

  1. Boot sector (sector 0) + backup boot sector (sector 6)
  2. FSInfo sector (sector 1)
  3. Two FAT copies, each sized via the standard FAT32 FAT-size formula
  4. An empty root directory (with a volume-label entry, if one is set)

It locks and dismounts the volume first (FSCTL_LOCK_VOLUME / FSCTL_DISMOUNT_VOLUME), writes everything with direct sector-aligned WriteFile calls, then unlocks it so Windows remounts the new filesystem.

Project layout

.forgejo/workflows/release.yml (repo root) handles the build/release pipeline described above, and package.ps1 (repo root) does the same build-and-bundle step locally without needing a tag/release. Everything else lives under the project folder:

Phat32/
  Phat32.csproj              .NET 10 WPF project, requireAdministrator manifest
  app.manifest               Forces UAC elevation prompt
  icon.ico                   App icon - exe, window/taskbar, both installers,
                             and the Add/Remove Programs entry
  App.xaml / App.xaml.cs     App entry point; branches into CLI mode or the GUI
  MainWindow.xaml / .cs      The UI: drive picker, label, cluster size, confirm/format
  Models/DriveItem.cs        UI-facing drive info
  Dialogs/
    AppDialog.xaml / .cs     Themed confirm/info/error dialog (replaces MessageBox)
  Services/
    NativeMethods.cs         P/Invoke: CreateFile, DeviceIoControl, WriteFile, console interop, etc.
    DriveEnumerator.cs       Lists candidate drives, flags the system drive
    Fat32Parameters.cs       BPB field math (cluster size table, FAT size formula)
    Fat32Formatter.cs        Builds, writes, and verifies boot sector / FSInfo / FATs / root dir
    DarkTitleBar.cs          Shared dark-title-bar helper for both windows
    AppSettingsStore.cs      Persists last-used UI choices to %AppData%
    CliRunner.cs             Headless command-line mode

Possible follow-ups

  • Add an "Eject" / safe-remove step after formatting removable media.
  • Add exFAT as an alternative for very large drives where 32 KB FAT32 clusters start to feel wasteful (exFAT has no such size cap at all).

Contributing

Issues and pull requests are welcome.

License

MIT — see LICENSE.