From 1bc6477ea7ca5905e0eb22ceebe4dc6575fc127a Mon Sep 17 00:00:00 2001 From: Roman Tarasov Date: Sat, 21 Feb 2026 06:16:56 +0300 Subject: [PATCH] Fixing exFAT image builds in Windows --- New-OsfExfatImage.ps1 | 47 +++++++++++++------------------------------ README.md | 14 +++++++------ 2 files changed, 22 insertions(+), 39 deletions(-) diff --git a/New-OsfExfatImage.ps1 b/New-OsfExfatImage.ps1 index 792da1b..01c1f8e 100644 --- a/New-OsfExfatImage.ps1 +++ b/New-OsfExfatImage.ps1 @@ -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 diff --git a/README.md b/README.md index 1020929..c30a436 100644 --- a/README.md +++ b/README.md @@ -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