- C# 89.3%
- PowerShell 5.4%
- NSIS 5.3%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
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 |
||
| .forgejo/workflows | ||
| docs | ||
| installer | ||
| Phat32 | ||
| .gitignore | ||
| global.json | ||
| icon_preview.png | ||
| LICENSE | ||
| package.ps1 | ||
| Phat32.sln | ||
| README.md | ||
| USER_GUIDE.txt | ||
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.
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,
--fullon 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 publishfails withNU1100errors aboutMicrosoft.NETCore.App.Runtime.win-x64or 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-WINRUNNERrunner (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-latestwill not match it. Since it's a self-hosted host-mode runner, it also needs Node available for JavaScript actions, andsetup-dotnetinstalls 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 fromPhat32.nsi. The friendlier option for most people. NSIS comes from theNSIS-ToolNuGet package, which is just a zip of binaries..msi— WiX package built fromPhat32.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:
- Boot sector (sector 0) + backup boot sector (sector 6)
- FSInfo sector (sector 1)
- Two FAT copies, each sized via the standard FAT32 FAT-size formula
- 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.