# IODD TRIM Tool - Version History

## 0.2.0.28

- Fixed remaining **multilingual UI gaps** in the Real Test area: the target-drive hint, Speed Chart button, chart window title/buttons/info text, dynamic status prefixes (`Status`, `Speed`, `Elapsed`, `Step`), and **FS policy** messages are now connected to the translation table
- Shortened the German **Speed drop wait** label to **Speed-Drop (Min)** so it no longer overlaps the adjacent input/control area
- Corrected Japanese and Simplified Chinese default status labels that still displayed English `Status: Idle` / `Status: Running`

## 0.2.0.27

- Adjusted the TRIM Execute tab layout so long option captions fit better: reduced the **Windows ReTrim only**, **serial masking**, and **flush volumes** control widths, tightened the advanced settings panel height, and moved the status/log group upward
- Fixed the advanced-options tab order after the layout adjustment

## 0.2.0.26

- Removed the visible **Expert ATA Pass-Through retry after ReTrim failure** option; auto mode now enables the guarded retry path internally, so after a ReTrim 43022 failure the existing warning/confirmation prompt can still offer ATA Pass-Through without an extra checkbox
- Updated the USB disconnect guidance in all 8 UI languages: users are now told **not to close the tool** and **not to use Windows “Safely Remove Hardware”**, but to physically unplug/reconnect first and wait for Windows re-detection
- Fixed the German advanced-options layout where **serial-number masking** text could overlap the adjacent **flush volumes** checkbox
- Added USB 3.0+ requirement wording to the USB guide; disk list/detail text now shows a USB requirement marker when speed clues are available (`USB2 - unsupported`, `USB3+`, `USB3+ likely`, or `USB3+ required`)
- Completed missing localized Expert ATA retry strings in Japanese, Simplified Chinese, Spanish, German, French, and Russian so no fallback English text remains if the string is referenced internally

## 0.2.0.24

- **ASM1153E regression guardrail:** fixed the UAS+NTFS dead-end where Pinned Volume guard skipped ATA Pass-Through, then `Optimize-Volume -ReTrim` failed with `StorageWMI 43022` and the run ended with `0` trimmed batches
  - Added an optional **Expert retry** path: when ReTrim fails with 43022 under Pin guard, the tool can prompt once and run lock/dismount + ATA Pass-Through as a second chance
- **UI / mode selection:** fixed a **repeated “Restriction” (Full TRIM blocked)** dialog loop when a disk had mapped volumes but **free-space TRIM** was not allowed (for example **FAT12/16** reported as `FAT`): the mode handler no longer flips the radio back to **Full TRIM**, which re-triggered the same handler. Added `FRgModeUpdating` to avoid re-entrancy from programmatic `TRadioGroup` changes; **FS policy** label now matches the refresh path (`free-space mode disabled - …`) and **plain FAT** volumes get an explicit reason instead of the misleading “no NTFS/exFAT/FAT32 volume mapped” text
- **Disk list:** USB candidates with reported capacity **≤ 1 GiB** (1024³ bytes) are **omitted** from enumeration (small firmware / dongle volumes)

## 0.2.0.22

- **SSK exFAT post-failure hang fix:** after a SCSI UNMAP **bridge hang** is detected, the executor no longer runs the delayed **PhysicalDrive** `CreateFileW` reopen probe (that probe could block for minutes during USB reset/removal). Failure path now skips straight to USB reconnect / NTFS guidance
- SCSI UNMAP bridge-hang dead-ends now record a clear batch message: `SCSI UNMAP bridge hang detected; skipped unsafe fallback paths` (fixes empty `Batch %d failed:` log lines)

## 0.2.0.21

- Reduced the long **Starting from PC** dead-end on fragile UAS + non-NTFS bridges by capping the first SCSI UNMAP timeout to 2.5 s before skipping ATA Pass-Through on a detected bridge hang
- Added a short delayed USB disconnect probe after failed runs: the executor now polls `\\.\PhysicalDriveN` for up to 3 s before choosing between the USB-disconnect guidance and the non-NTFS guidance, covering Windows' delayed device-removal notification

## 0.2.0.20

- Updated the **USB bus disconnect guidance** to explicitly include Martin's requested recovery path: unplug/reconnect the drive, wait for Windows to detect it again, then **format as NTFS to use Windows ReTrim** and retry

## 0.2.0.19

- **USB bus disconnect guidance:** after a run, if a batch recorded `winerr=55` (`ERROR_DEV_NOT_EXIST`) or reopening `\\.\PhysicalDriveN` fails with `55` / `1167` (`ERROR_DEVICE_NOT_CONNECTED`), the tool shows a single localized notice (unplug/reconnect, wait for re-enumeration, optional NTFS ReTrim). When this notice is shown, the existing **UAS + non-NTFS** “reformat as NTFS” popup is **suppressed** so the user is not buried in two dialogs

## 0.2.0.18

- **TRIM compatibility warning** (`NeedsUserConfirm`): if the disk has at least one **NTFS** volume, the warning dialog is **skipped automatically** (same as the user clicking Yes). Rationale: `Optimize-Volume -ReTrim` remains a viable fallback after hardware paths fail, so the popup added little value for NTFS users
- Non-NTFS disks (exFAT/FAT32 only) still see the warning; declining still shows the informational message about NTFS being required for Windows ReTrim

## 0.2.0.17

- After the **TRIM compatibility warning**, if the user clicks **No** (decline hardware TRIM): **offer Windows ReTrim-only mode**
  - When at least one **NTFS** volume exists on the disk, a second prompt asks whether to continue with **Optimize-Volume -ReTrim only** (automatically enables the "Windows ReTrim only" option for this run)
  - When there is **no NTFS** volume (e.g. exFAT only), an informational message explains that ReTrim requires NTFS and suggests reformatting or enabling the checkbox manually after switching to NTFS — avoids users exiting with no path forward

## 0.2.0.16

- **Preflight TRIM warning no longer shown when ATA IDENTIFY reports TRIM** (`TrimCapable`, Word 169 — same basis as tools showing **TRIM: YES**)
  - Windows `StorageDeviceTrimProperty` reflects what the **USB bridge tells storport**, which often fails (`winerr=24`) or reports `TrimEnabled=0` even when the **SSD behind SAT** correctly advertises DSM/TRIM in IDENTIFY
  - In that common mismatch case the popup was misleading and only annoyed users; the executor still performs runtime validation as before

## 0.2.0.15

- Added **Path 3 fallback CDB configs: ATA Pass-Through(12)** — opcode `0xA1`, 12-byte CDB
  - Some older / cheaper USB-SATA bridges only recognize the 12-byte ATA Pass-Through form and reject the standard 16-byte form (`0x85`) with sense `5/32/0` (INVALID OPCODE)
  - The Path 3 CDB rotation now tries 5 configs in order: PT16 DMA+EXTEND → PT16 DMA → PT16 PIO → **PT12 DMA → PT12 PIO**
  - PT12 cannot express LBA48 / EXTEND, but the DSM TRIM payload carries LBAs in the data-out buffer, not the CDB, so this is fine. PT12 sector count is 8-bit (max 255 sectors); we skip PT12 when payload exceeds 255 sectors to avoid silent truncation
  - `SendTrimBatch` now auto-detects the CDB length from the opcode (`0xA1` → 12 bytes, `0x85` → 16 bytes) so storport sees the correct `CdbLength` field
- Added **preflight TRIM compatibility warning popup** (Method F)
  - When `IOCTL_STORAGE_QUERY_PROPERTY (StorageDeviceTrimProperty)` fails or reports `TrimEnabled=0`, the user is now shown a non-blocking warning popup before TRIM starts: *"This USB-SATA bridge reports that TRIM is NOT supported. All TRIM paths (DSM / SCSI UNMAP / WRITE SAME / ATA Pass-Through) are likely to fail. Continue anyway?"* (translated to 8 languages)
  - Avoids the previous experience where users on TRIM-incapable bridges (e.g. ROG ESD-S1C, certain VANSUNY / SSK exFAT setups) only discovered the dead-end after 30+ seconds of failed attempts
  - The check is only a warning — users can still proceed if they want to verify the bridge state experimentally; the previous "hard block" on `TrimEnabled=0` has been relaxed because some bridges under-report TRIM capability while still honoring ATA Pass-Through

## 0.2.0.14

- Added new fallback path **Path 2.5: WRITE SAME(16) with UNMAP=1** (SBC-3 §5.41) for USB-SATA bridges that decline DSM TRIM and SCSI UNMAP at the IOCTL layer
  - Some cheap UAS bridges (typically shipped with **VANSUNY / SSK / Hiksemi / Move Speed / KEXIN / ORICO / Netac / Fanxiang** drives in exFAT) report `TRIM not supported` via `StorageDeviceTrimProperty`. Storport then silently rejects `IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES` (DSM TRIM) and the SCSI UNMAP IOCTL with `winerr=5, scsi_status=0` even after dismount
  - `WRITE SAME(16)` is a regular block-write opcode that bypasses the storport TRIM filter — only the bridge's own SAT layer decides whether to honor the `UNMAP=1` bit. Some bridges that decline the dedicated `UNMAP (0x42)` opcode still honor `WRITE SAME (0x93)` because they share the same write data path
- **Probe-first safety:** SBC-3 mandates that a device unable to unmap must instead write the data block (zeros) to every LBA in the range. To avoid silently zero-filling 100 GiB of "free" space, the executor first issues one small probe (max 8 sectors = 4 KiB) on the first range. If the probe fails, the run aborts WRITE SAME entirely and falls through to ATA Pass-Through
- Skip conditions for Path 2.5: Pinned Volume guard active, bridge hang detected on UNMAP, or probe already failed earlier in this run
- Updated UAS preflight transport message to advertise the new path: `UASP (DSM TRIM incl. FS-cooperative retry / SCSI UNMAP / WRITE SAME(16)+UNMAP fallback / ATA Pass-Through under Pinned Volume guard / Optimize-Volume -ReTrim fallback)`

## 0.2.0.13

- Fixed bus disconnection on **SSK / VANSUNY / Hiksemi / Move Speed / KEXIN / Netac / Fanxiang / ORICO** UAS USB-SATA bridges (typical Amazon-shipped China-made USB SSDs, all formatted in **exFAT** by the vendor)
  - Root cause: these bridges hang for ~5–6 s on the first SCSI UNMAP, then the bus is reset by the host. The previous flow then issued ATA Pass-Through against the (now disappearing) device, which caused `winerr=55` "device no longer available" and full disconnection from the OS
  - **Bridge-hang detection:** the executor now measures the elapsed time of the first SCSI UNMAP. If it returns `winerr=1117` (ERROR_IO_DEVICE) after `TimeoutMs - 500 ms` or longer, the bridge is treated as hung
  - **Path 3 ATA Pass-Through is automatically skipped** when a bridge hang is detected, so the device stays enumerated and the run ends cleanly instead of crashing the bus
- Added **user-facing guidance popup** when the run dead-ends on a UAS bridge with **no NTFS volume** (so the `Optimize-Volume -ReTrim` fallback does not apply): "This USB-SATA bridge does not support TRIM on exFAT/FAT32. Please reformat the drive as NTFS to use Windows ReTrim, then try again." (translated to 8 languages)

## 0.2.0.12

- Fixed regression on **BOT (USB Mass Storage)** devices with NTFS volumes introduced by 0.2.0.10's Pinned Volume guard
  - Affected case: IODD External HDD (BOT bridge, NTFS + FAT32 + exFAT volumes) that previously trimmed via ATA Pass-Through (worked in 0.2.0.9, failed in 0.2.0.10 / 0.2.0.11)
  - Root cause: Pinned Volume guard was applied to BOT as well, but BOT bridges typically report "TRIM unsupported" to Windows storage stack, which makes the `Optimize-Volume -ReTrim` fallback fail with `StorageWMI 43022` ("not supported by the hardware backing the volume"). The guard skipped Path 3 (ATA Pass-Through), which is the only working hardware path on these bridges
- **Pinned Volume guard now applies to UAS transport only** (was UAS + BOT)
  - BOT devices revert to 0.2.0.9 behavior: lock + dismount + Path 3 ATA Pass-Through (the only path that actually trims on bridges that decline TRIM at the SAT layer)
  - UAS NTFS devices keep the 0.2.0.11 protection unchanged (Vansuny U52, SSK USB 3.2 SSD, ROG ESD-S1C, ...)
- Updated BOT preflight transport message to reflect that Pinned Volume guard is intentionally not applicable

## 0.2.0.11

- Fixed regression on **exFAT / FAT32** USB SSDs introduced by 0.2.0.10's Pinned Volume guard
  - Affected case: JMicron Generic USB-SATA bridge with exFAT volume that previously trimmed via SCSI UNMAP single-descriptor mode (worked in 0.2.0.9, failed in 0.2.0.10)
  - Root cause: Pinned Volume guard blocked the SCSI UNMAP path with `ERROR_ACCESS_DENIED`, but the `Optimize-Volume -ReTrim` fallback returns `StorageWMI 43025` ("This volume cannot be optimized") on non-NTFS volumes, so the run hit a dead end
- **Pinned Volume guard now activates only when at least one volume on the disk is NTFS**
  - exFAT / FAT32 disks fall back to the legacy lock + dismount path (where the SCSI UNMAP single-descriptor retry can actually reach the bridge)
  - NTFS disks (Vansuny U52, SSK, ROG ESD-S1C, etc.) keep the 0.2.0.10 protection unchanged
- Skipped Path 3 (ATA Pass-Through) entirely when Pinned Volume guard is active
  - Previously, ATA PT was attempted after SCSI UNMAP was rejected; it was not actually blocked by the OS (ATA PT is wrapped in SCSI), so it stressed the bridge and wasted ~22 s on retries before reaching the PowerShell fallback
- Tightened `Optimize-Volume -ReTrim` fallback eligibility to **NTFS only** (previously NTFS / exFAT / FAT32) to match Microsoft's actual support matrix and avoid false positives that block working hardware paths

## 0.2.0.10

- Added **Pinned Volume guard** (Option A) for auto-mode UAS/BOT executions on disks with at least one mounted volume
  - Volume handles are opened with `FILE_SHARE_READ|WRITE` but **never locked or dismounted**, so NTFS keeps owning the volume
  - Any raw SCSI UNMAP / ATA Pass-Through targeting a mounted volume's LBA range is rejected by Windows storport with `ERROR_ACCESS_DENIED` **before reaching fragile USB-SATA bridges** (Vansuny U52, SSK USB 3.2 SSD, ROG ESD-S1C, ...)
  - Tool then falls through cleanly to the existing `Optimize-Volume -ReTrim` fallback (Path 4), which uses the cooperative defrag stack and does not crash these bridges
  - DSM TRIM (Path 1) still runs first and benefits compatible UASP devices (e.g. HP 911 Pro) at full hardware speed
- Legacy lock + dismount path is preserved only for the explicit DEBUG-forced **SCSI UNMAP** / **ATA Pass-Through** modes, where the user intentionally bypasses the Pinned Volume guard
- UASP / BOT preflight transport message now mentions the Pinned Volume guard

## 0.2.0.9

- Added user-selectable **"Windows ReTrim only (skip DSM / UNMAP / ATA Pass-Through)"** checkbox in the Advanced options panel
  - Targets USB-SATA bridges that hang or disconnect during SCSI UNMAP / ATA Pass-Through (observed on **Vansuny U52**, **SSK USB 3.2 SSD**)
  - When enabled, all hardware TRIM paths and the preflight Lock/Dismount step are skipped, and `Optimize-Volume -ReTrim` is invoked directly (without the confirmation popup, since the user explicitly selected this mode)

## 0.2.0.8

- Fixed `Optimize-Volume -ReTrim` fallback failing on bridges where SCSI UNMAP / ATA Pass-Through dismount the volume before the fallback runs (observed on **ROG ESD-S1C UASP**)
  - Volume handles held by the executor are now released before invoking PowerShell so Windows can auto-remount the drive letter
  - Added drive-letter mount polling (`GetVolumeInformationW`, up to 8 s) before each `Optimize-Volume` call; a warning is logged and the volume is skipped if it does not remount in time
- Forced UTF-8 console output for the `powershell.exe` child (`[Console]::OutputEncoding = [Text.Encoding]::UTF8`) and decoded captured stdout/stderr as UTF-8 so non-ASCII PowerShell error messages survive intact in the log

## 0.2.0.7

- Improved compatibility with USB-SATA bridges that decline standard TRIM IOCTLs (e.g. **Vansuny U52 USB 3.2 Gen 2**, **SSK USB 3.2 SSD**)
  - Added **FS-cooperative DSM TRIM** retry: when the first DSM TRIM call returns `ERROR_INVALID_FUNCTION` (typical for bridges that don't advertise `StorageDeviceTrimProperty`), the tool re-issues the same batch without the `DEVICE_DSM_FLAG_TRIM_NOT_FS_ALLOCATED` flag so NTFS/exFAT can dispatch the trim on its behalf
  - Added **`Optimize-Volume -ReTrim` PowerShell fallback** (free-space mode + mounted NTFS/exFAT/FAT32) when DSM, SCSI UNMAP, and ATA Pass-Through all fail; a confirmation popup is shown before invocation, child stdout/stderr is logged, and remaining batches are skipped after success since `-ReTrim` covers all free space at once
  - Updated UASP transport message to reflect the extended path set: `DSM TRIM (incl. FS-cooperative retry) / SCSI UNMAP / ATA Pass-Through / Optimize-Volume -ReTrim fallback`

## 0.2.0.6

- Fixed incorrect "Last used LBA" calculation for NTFS and exFAT in Drive Inspection tab
  - NTFS: replaced free-range inference with backward bitmap scan via `FSCTL_GET_NTFS_VOLUME_DATA` for accurate highest-used-cluster detection
  - exFAT: replaced FAT table scan with allocation bitmap backward scan (correctly handles "No FAT Chain" contiguous files that have no FAT entries)
  - FAT32: unchanged (FAT table backward scan)
- Partition last-used-byte logic shared as `GetUsedBytesForVolume` in `D:\data\lazarus_lib\uDiskLastSector.pas` (used by this project and `iodd_HDD_4`)

## 0.2.0.4

- Added pre-TRIM step before file creation in Real Test STEP1 to optimize SSD write performance
- Added TRIM result logging with batch/sector statistics after each TRIM operation
- Added write speed chart: 2-second interval time-series data recorded during test, viewable via "Speed Chart" button after completion
- Speed chart supports Save CSV export and displays step boundary markers
- Fixed speed drop detection: replaced fixed-window average with Exponential Moving Average (EMA) for accurate detection
- Fixed speed drop wait not triggering due to 60-second cooldown blocking legitimate detections across steps
- Removed cooldown mechanism (unnecessary with EMA-based averaging)
- Display total disk sectors alongside GiB in Drive Inspection summary
- Changed "Last free start LBA" to "Last used LBA" for clearer disk usage display
- DSM TRIM initial failure and multi-descriptor SCSI UNMAP rejection now logged as WARN instead of ERROR
- Deferred volume Lock/Dismount to preserve mounted state for DSM TRIM (fixes ACCESS_DENIED on first attempt)

## 0.2.0.3

- Implemented 3-stage UASP TRIM strategy: DSM TRIM → SCSI UNMAP → ATA Pass-Through
- Added SCSI UNMAP single-descriptor fallback when multi-descriptor batch is rejected
- Volume Lock/Dismount now performed once before the batch loop and held for the entire operation (UASP and BOT)
- Improved cancel responsiveness: added cancellation checks inside UNMAP and ATA PT inner loops
- Changed default SCSI timeout from 20s to 5s (minimum 1s)
- Added UASP TRIM method selector in DEBUG_MODE builds (Auto / SCSI UNMAP / ATA Pass-Through)

## 0.2.0.2

- Added "Size (GB)" column after "Total Sectors" in the Inspect tab grid
- Added percentage display to "Free Sectors" and "Used Sectors" columns (e.g. `920664 (1.5%)`)
- Added auto-size columns after inspection to prevent data from being clipped

## 0.2.0.1

- Bug fixes across 7 source files (25 issues resolved)
- **Critical**: Fixed use-after-free in logger destructor when queued UI callbacks reference freed object
- **High**: Fixed SCSI sense data parsing to distinguish fixed-format (0x70/0x71) vs descriptor-format (0x72/0x73)
- **High**: Fixed worker thread accessing UI controls directly — TRIM options now captured on main thread before execution
- **High**: Fixed shared `TVolumeBitmapReader` concurrent access from worker thread — now uses dedicated instance
- **High**: Fixed `FreeOnTerminate=True` threads causing race condition in `FormDestroy` — properly stopped and freed
- **High**: Fixed Debug-build range-check crash on flexible array (`array[0..0]`) access in disk extent enumeration
- **High**: Added `{$packrecords 8}` for `LARGE_INTEGER`-containing records to prevent 32-bit build field misalignment
- **Medium**: Fixed `GetLastError` being clobbered before capture in `ProbeIdentifyDevice`
- **Medium**: Added minimum size guard (36 bytes) for `TStorageDeviceDescriptor` before accessing fields
- **Medium**: Added null terminator to device descriptor buffer to prevent unbounded string reads
- **Medium**: Fixed all early exits in real test `Execute` to set `FResultMessage` (previously silent failures)
- **Medium**: Fixed `FillFiles` batch directory logic — files 1000+ now correctly placed in `batch_N/` subdirectories
- **Medium**: Moved `CloseFile` into `finally` blocks to prevent TextFile handle leaks on exceptions
- **Medium**: Fixed logger deadlock potential — UI callback now invoked outside critical section lock
- **Medium**: Wrapped `AppendLineToFile` in `try..except` to prevent I/O exceptions from crashing worker threads
- **Low**: Fixed `FIsWriting` flag not being reset on early exit from file fill operations
- **Low**: Fixed `FileCrc` returning partial CRC on cancellation, causing false verification failures
- **Low**: Removed duplicate `FreeStatsSummary` append in disk selection change handler
- **Low**: Fixed `FlushVolumesIfRequested` using wrong field (`VolumePath` → `DriveLetter`) for device path
- **Low**: Fixed `GetFileSize` mishandling exact 4GB-1 byte files — added `GetLastError = NO_ERROR` check
- **Low**: Added thread safety to `SetUiSink` and `DisableFileOutput` (lock acquisition)
- **Low**: Fixed Chinese Traditional users incorrectly mapped to Simplified Chinese
- **Low**: Added `ERROR_INVALID_FUNCTION` as permanent ATA failure code to avoid useless retries
- **Low**: Fixed empty `MessageText` when all ATA retries exhausted without hitting permanent error
- **Low**: Capped retry backoff delay at 30 seconds to prevent `Cardinal` overflow
- **Low**: Fixed `JsonEscape` to handle control characters (`\n`, `\r`, `\t`, etc.)
- **Low**: Replaced O(n²) bubble sort with QuickSort in `MergeRanges` for fragmented volume performance
- **Low**: Protocol detection heuristic now scans only raw device properties, skipping binary header

## 0.1.0.5

- Added UASP (USB Attached SCSI) support with dual-path TRIM strategy
  - UASP devices: SCSI UNMAP first, automatic fallback to ATA Pass-Through if UNMAP fails
  - BOT devices: ATA Pass-Through (unchanged)

## 0.1.0.4

- Changed "Flush Volumes before TRIM" checkbox default to checked

## 0.1.0.3

- Changed Dry Run checkbox default to unchecked

## 0.1.0.2

- Added logical drive selector in the Real Test tab, allowing users to choose which volume to use for testing
- Added read-only drive detection: log and CSV files are no longer created when running from a read-only drive

## 0.1.0.1

- Initial release
- USB-SATA bridge ATA Pass-Through support for SSD TRIM
- TRIM execution with Full Device and Free Space Only modes
- Drive inspection tab with volume bitmap analysis and free-range CSV export
- Real test tab with 4-step write/delete/TRIM/verify cycle
- Multi-language support: Korean, English, Japanese, Chinese, Spanish, German, French, Russian
- Automatic OS language detection with manual language selection
- TRIM and ATA Pass-Through capability detection displayed in disk info
- Partition file system type shown in disk selection combo box
- GiB display alongside raw byte values in disk info
- Top toolbar buttons: IODD Product, IODD Manual, How to Use, TRIM Info, License
- Success/failure popup notifications for TRIM execution and real test verification
- Stop button support during Verify Only mode
- Administrator privilege requirement via application manifest
