# IODD TRIM Tool - Version History

## 0.3.0.2

- **Fixed unresponsive/uncancelable runs on flaky USB bridges:** the ATA Pass-Through TRIM path called `DeviceIoControl` (and the per-batch `CreateFileW`) synchronously on the executor thread, so a stuck bridge (e.g. some INITIO INIC security enclosures) could block it for minutes — the cancel button did nothing and the app appeared frozen. Each ATA batch now runs on a bounded worker thread: the executor polls for cancel every ~250 ms and enforces a wall-clock cap, so a user cancel is honored promptly and a genuinely hung bridge is abandoned (marked as a bridge hang) instead of pinning the run. The wall-clock cap is set well above the per-command timeout so slow-but-working bridges that need ~20 s just to open the handle per batch are not aborted
- **Stale disk selection guard on Start:** pressing Start used to run against the disk snapshot from the last refresh, with no check that reality still matched — a replugged device can take over the same PhysicalDrive number (TRIM would hit the wrong disk), and reformatting a volume (NTFS ↔ exFAT) silently invalidated the cached file-system list that drives the mode decisions (free-space eligibility, Pinned Volume guard, ReTrim fallback). Start now re-probes the selected disk (serial/size via a new lightweight `ProbeDiskIdentity`) and re-enumerates its volumes (letters, file system, partition offsets/lengths); on any mismatch it shows a localized "disk configuration changed — refreshing the list" warning, logs the specific reason, auto-refreshes the disk list, and aborts the run
- **Post-TRIM disk initialization is now conditional on the run outcome:** the pending GPT/MBR initialization after a full-device TRIM used to run unconditionally — even after a completely failed run, a user cancel, a detected bridge hang, or a **dry run** (which must not modify the disk at all). It now runs only for an executed run with at least one successful batch and no cancel/bridge-hang, and logs a localized "initialization skipped" notice otherwise. Partial success still initializes, since the leading LBAs (old partition table area) may already be trimmed
- **Fixed cancel being ignored during the Optimize-Volume -ReTrim fallback:** after the user cancelled, the run still entered the PowerShell ReTrim fallback and its multi-second remount waits, forcing a second cancel. The fallback is now skipped entirely when a cancel is pending, and the per-volume ReTrim loop breaks out promptly on cancel
- **Truthful ReTrim fallback prompt for the Pinned Volume guard:** when the tool itself blocked the raw paths (SCSI UNMAP / ATA Pass-Through) to keep a mounted NTFS volume online, the confirmation popup still claimed "hardware TRIM paths were all rejected by this USB bridge", which was misleading. The guard case now shows its own localized message explaining that the hardware paths were skipped on purpose to protect the mounted NTFS volume and that Windows "Optimize-Volume -ReTrim" is the safe, native path for NTFS; the original "rejected by the bridge" text remains for genuine hardware-path exhaustion
- The abandoned ATA worker owns its own pass-through engine and runs it **without any logger reference**: a worker still blocked in `DeviceIoControl` can outlive the executor thread and even the main form's logger, so it must not touch any external object. The executor now emits the per-command `scsi_status` diagnostic line itself after joining the worker (`SendTrimBatch` exposes the SCSI status as an out parameter), keeping log output identical to before

## 0.3.0.1

- **Fixed DSM TRIM false success:** the UASP DSM TRIM path could report a batch as **succeeded** (and even log "trimmed" sectors) when no volume range was actually trimmed — e.g. an unformatted UAS SSD with no mapped volume, or a batch whose ranges fell outside every mapped volume. `SendDsmTrimBatch` now returns the number of ranges actually applied, and the executor only treats DSM as successful when at least one range was trimmed; otherwise it falls through to the lower fallback paths (SCSI UNMAP / WRITE SAME(16) / ATA Pass-Through)
- Initialized the DSM `winerr` accumulator before the per-volume loop so the failure report no longer shows a stale error code when the loop does not run
- **Corrected Full-device TRIM on UASP:** DSM TRIM is issued on **volume handles with volume-relative offsets**, which is only valid for **free-space** mode. In **full-device** mode the ranges are absolute whole-disk LBAs, so DSM is now disabled for that mode and the run uses the physical-handle paths (SCSI UNMAP / WRITE SAME(16) / ATA Pass-Through) which address absolute LBAs correctly
- The **Pinned Volume guard** is now limited to free-space mode as well: full-device mode has no Optimize-Volume -ReTrim fallback, so pinning would dead-end every raw path; full-device now uses lock+dismount + raw paths as intended. BOT transport and the free-space + UASP path are unchanged
- **Robustness:** guarded `FindLastUsedLbaForVolume` against an empty drive letter (prevented a possible range-check crash in debug builds)
- **Performance:** the logger now keeps the log file open for the session instead of opening/closing it on every line, removing heavy per-line file I/O during large full-device runs
- **Performance:** the DSM entry-limit splitter now pre-allocates its output in a single pass, replacing tens of thousands of incremental reallocations when planning a full-device TRIM on a large disk
- **Diagnostics:** free-space planning now reports a specific reason when no ranges are produced — "no eligible volume mapped", "no free space (volumes appear full)", or "bitmap could not be read" — instead of a single ambiguous message
- **Admin check:** elevation is now determined from the process token (`TokenElevation`), correctly reflecting UAC, with the previous PhysicalDrive0 RW-open probe kept only as a fallback
- **Multilingual logs:** migrated the remaining hard-coded Korean log/status strings (CSV create/save failures, physical-address lookup failure, STEP4 verify status, post-TRIM GPT/MBR initialize messages, "no files in test folder") into the `uLang` translation table with full Korean/English/Japanese/Chinese/Spanish/German/French/Russian entries, so non-Korean UIs no longer show Korean text in these paths
- **Strict bridge probe:** `CheckBridgePassThroughProbe` now actively issues a read-only SAT (ATA Pass-Through) **IDENTIFY DEVICE** command in addition to the raw-handle open check; on a BOT bridge a failed SAT probe is surfaced as a warning (ATA TRIM likely unsupported), while UAS bridges remain non-blocking since they TRIM via SCSI UNMAP / DSM
- **Robustness:** `SaveFailureReport` now builds its JSON with `fpjson` instead of hand-rolled string concatenation, so quotes/backslashes/control characters in the device path and batch message text are always escaped correctly; the JSON and CSV reports also share a single timestamp instead of calling `Now` twice
- **Robustness:** added an explicit upper-bound guard on the per-batch ATA payload sector count — a batch exceeding the 16-bit PASS-THROUGH(16) block-count field now fails with a clear "reduce batch entries" message instead of silently truncating the `Word` and under-trimming

## 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
