Fixing exFAT image builds in Windows

This commit is contained in:
Roman Tarasov committed 2026-02-21 06:16:56 +03:00
1 parent eb2a4c1036
commit 1bc6477ea7
2 files changed
+22 -39

No files matched your search

+14 -33
View File
@@ -3,7 +3,7 @@
PURPOSE
-------
Create a RAW image file, mount it via OSFMount as a logical volume,
auto-select FAT32/exFAT by image size, and either:
format it as exFAT, and either:
- format + copy + dismount (default), or
- create + mount only for manual steps.
@@ -45,11 +45,10 @@
NOTES
-----
- This script does NOT auto-elevate. Start PowerShell as Administrator.
- FS selection is automatic by image size:
* < 4 GB -> FAT32, cluster 65536
* >= 4 GB -> exFAT, cluster auto-selected:
- large-file sets: 65536
- small/mixed-file sets: 32768
- Filesystem is always exFAT.
- Cluster size is auto-selected:
- large-file sets: 65536
- small/mixed-file sets: 32768
#>
[CmdletBinding()]
@@ -245,11 +244,9 @@ function Dismount-OsfVolume([string]$osfPath, [string]$mountPoint, [int]$maxAtte
return $false
}
function Invoke-FormatVolume([string]$driveLetter, [string]$fileSystem, [int]$clusterSize, [string]$label) {
function Invoke-FormatVolume([string]$driveLetter, [int]$clusterSize, [string]$label) {
$target = "${driveLetter}:"
if ($fileSystem -notin @("FAT32", "exFAT")) {
throw "Unsupported file system '$fileSystem'. Expected FAT32 or exFAT."
}
$fileSystem = "exFAT"
$clusterArg = Format-AllocationUnitArg -clusterSize $clusterSize
$attempts = @(
@{ Name = "$fileSystem quick with requested allocation unit"; Args = @($target, "/FS:$fileSystem", "/A:$clusterArg", "/Q", "/V:$label", "/X", "/Y") }
@@ -311,40 +308,23 @@ if (Test-Path $ImagePath) {
[Int64]$expectedBytes = 0
[string]$osfSizeArg = $null
[Int64]$FsSwitchThresholdBytes = 4GB
[int]$Fat32ClusterSize = 65536
[int]$ExfatClusterSize = 65536
[int]$ExfatClusterSize = Get-OptimalExfatClusterSize -dir $SourceDir
[bool]$sizeProvided = -not [string]::IsNullOrWhiteSpace($Size)
[string]$TargetFs = ""
[int]$TargetClusterSize = 0
[int]$TargetClusterSize = $ExfatClusterSize
if (-not $sizeProvided) {
Write-Host "[Info] Size not provided. Computing an optimal image size from '$SourceDir'..."
$fat32Candidate = Get-OptimalImageSizeBytes -dir $SourceDir -clusterBytes $Fat32ClusterSize
if ($fat32Candidate -lt $FsSwitchThresholdBytes) {
$expectedBytes = $fat32Candidate
} else {
$ExfatClusterSize = Get-OptimalExfatClusterSize -dir $SourceDir
$expectedBytes = Get-OptimalImageSizeBytes -dir $SourceDir -clusterBytes $ExfatClusterSize
}
$expectedBytes = Get-OptimalImageSizeBytes -dir $SourceDir -clusterBytes $TargetClusterSize
$osfSizeArg = "$expectedBytes"
} else {
$expectedBytes = Parse-SizeToBytes $Size
$osfSizeArg = $Size
}
if ($expectedBytes -lt $FsSwitchThresholdBytes) {
$TargetFs = "FAT32"
$TargetClusterSize = $Fat32ClusterSize
} else {
$TargetFs = "exFAT"
$ExfatClusterSize = Get-OptimalExfatClusterSize -dir $SourceDir
$TargetClusterSize = $ExfatClusterSize
}
if (-not $sizeProvided) {
Write-Host "[Info] Computed image size: $(Format-Bytes $expectedBytes) ($expectedBytes bytes)."
}
Write-Host "[Info] Selected filesystem: $TargetFs (cluster=$TargetClusterSize) for image size $(Format-Bytes $expectedBytes)."
Write-Host "[Info] Selected filesystem: exFAT (cluster=$TargetClusterSize) for image size $(Format-Bytes $expectedBytes)."
$osf = Find-OSFMountCom
@@ -380,8 +360,8 @@ try {
return
}
Write-Host "[2/4] Formatting $dest as $TargetFs (cluster=$TargetClusterSize, label='$Label') via format.com ..."
Invoke-FormatVolume -driveLetter $DriveLetter -fileSystem $TargetFs -clusterSize $TargetClusterSize -label $Label
Write-Host "[2/4] Formatting $dest as exFAT (cluster=$TargetClusterSize, label='$Label') via format.com ..."
Invoke-FormatVolume -driveLetter $DriveLetter -clusterSize $TargetClusterSize -label $Label
if (-not (Test-Path $dest)) { throw "Drive $dest is not accessible after formatting." }
@@ -390,6 +370,7 @@ try {
$SourceDir, $dest,
"/E", "/COPY:DAT", "/DCOPY:DAT",
"/R:1", "/W:1",
"/NFL", "/NDL",
"/ETA"
)
& robocopy.exe @roboArgs
+8 -6
View File
@@ -17,19 +17,19 @@
| Extension | Mounted FS | Attach backend | Status |
| --- | --- | --- | --- |
| `.exfat` | `exfatfs` | `LVD` or `MD` (configurable) | Stable |
| `.ffpkg` | `ufs` | `LVD` or `MD` (configurable) | High performance |
| `.exfat` | `exfatfs` | `LVD` or `MD` (configurable) | Optimal |
| `.ffpkg` | `ufs` | `LVD` or `MD` (configurable) | Legacy |
| `.ffpfs` | `pfs` | `LVD` | Experimental |
Notes:
- Backend, read-only mode, and sector size can be configured via `/data/shadowmount/config.ini`.
- Debug logging is enabled by default (`debug=1`) and writes to console plus `/data/shadowmount/debug.log` (set `debug=0` to disable).
- **On 4.xx firmware, shutting down the console may not work correctly when using image files.**
- **exFAT is the preferred image filesystem, including on 4.xx firmware, with no known reboot/shutdown issues in typical use.**
## Recommended FS choice
- Prefer **UFS (`.ffpkg`)** for games that work correctly on UFS: it is generally more performant (~1.5-2.5 x exFAT).
- Use **exFAT (`.exfat`)** for games that only work from external-disk style layouts, because exFAT is case-insensitive.
- Prefer **exFAT (`.exfat`)** in most cases: it is generally more performant, case-insensitive, and does not have reboot/shutdown issues in typical use.
- Use **UFS (`.ffpkg`)** only when specifically needed for compatibility with your game/setup.
## Runtime config (`/data/shadowmount/config.ini`)
@@ -148,7 +148,9 @@ FreeBSD:
Windows:
- You can create UFS2 images with **UFS2Tool** https://github.com/SvenGDK/UFS2Tool.
- Recommended output format for this project is `.ffpkg`.
- Example:
- `UFS2Tool.exe newfs -O 2 -b 32768 -f 4096 -m 0 -i 262144 -D ./APPXXXX ./PPSA12345.ffpkg`
## Installation and usage