Загрузка данных


& {
    $ErrorActionPreference = 'Stop'
    $file = Join-Path $env:ProgramData 'NVIDIA Corporation\Drs\nvAppTimestamps'
    $output = $null
    $guard = $null
    $reader = $null
    $temporary = $null

    try {
        [byte[]]$data = [IO.File]::ReadAllBytes($file)

        if ($data.Length -lt 2 -or $data[0] -ne 1 -or $data[1] -ne 0) {
            throw 'Неожиданный заголовок файла. Замена отменена.'
        }

        $encoding = [Text.UnicodeEncoding]::new($false, $false, $true)
        $output = [IO.MemoryStream]::new()
        $removed = [Collections.Generic.List[string]]::new()
        $output.Write($data, 0, 2)
        $pos = 2
        $total = 0

        while ($pos -lt $data.Length) {
            if ($pos + 4 -gt $data.Length) {
                throw "Неполная запись на смещении $pos."
            }

            $size = [int][BitConverter]::ToUInt16($data, $pos + 2)
            $start = $pos + 4
            $next = $start + $size + 8

            if ($size -lt 2 -or ($size % 2) -ne 0 -or $next -gt $data.Length) {
                throw "Некорректная длина записи на смещении $pos."
            }

            if ($data[$start + $size - 2] -ne 0 -or
                $data[$start + $size - 1] -ne 0) {
                throw "Не найден конец строки на смещении $pos."
            }

            $appPath = $encoding.GetString($data, $start, $size - 2)
            if ($appPath.IndexOf([char]0) -ge 0) {
                throw "Неожиданная структура строки на смещении $pos."
            }

            # Проверяем оба вида путей: D:\... и D:/...
            if ($appPath.Replace('\', '/').StartsWith(
                'D:/', [StringComparison]::OrdinalIgnoreCase
            )) {
                $removed.Add($appPath)
            }
            else {
                # Всю оставляемую запись переносим побайтно без изменений.
                $output.Write($data, $pos, $next - $pos)
            }

            $pos = $next
            $total++
        }

        if ($removed.Count -eq 0) {
            Write-Host 'В этом файле записей с D: нет. Файл не изменён.'
            return
        }

        Write-Host "`nИз истории NVIDIA будут удалены:"
        $removed | ForEach-Object { Write-Host $_ }
        Write-Host "`nУдалить: $($removed.Count). Оставить: $($total - $removed.Count)."

        if ((Read-Host 'Введи DELETE для удаления БЕЗ резервной копии') -cne 'DELETE') {
            Write-Host 'Отмена. Файл не изменён.'
            return
        }

        # Проверяем исходный файл ещё раз перед заменой.
        $share = [IO.FileShare]::Read -bor [IO.FileShare]::Delete
        $guard = [IO.File]::Open(
            $file, [IO.FileMode]::Open, [IO.FileAccess]::Read, $share
        )
        $reader = [IO.BinaryReader]::new($guard)
        [byte[]]$current = $reader.ReadBytes($data.Length + 1)

        if ([Convert]::ToBase64String($current) -cne
            [Convert]::ToBase64String($data)) {
            throw 'Файл изменился за время подтверждения. Замена отменена; повтори запуск.'
        }

        # Временный файл содержит только новый результат, не резервную копию.
        $temporary = "$file.$([Guid]::NewGuid().ToString('N')).new"
        [IO.File]::WriteAllBytes($temporary, $output.ToArray())
        [IO.File]::Replace(
            $temporary,
            $file,
            [System.Management.Automation.Language.NullString]::Value
        )
        $temporary = $null

        Write-Host "`nГотово. Удалено записей с D: $($removed.Count)."
        Write-Host "Оставлено остальных записей: $($total - $removed.Count)."
        Write-Host 'Резервная копия не создавалась.'
    }
    catch {
        Write-Warning ("Ошибка: " + $_.Exception.Message)
    }
    finally {
        if ($reader) { $reader.Dispose() }
        if ($guard) { $guard.Dispose() }
        if ($output) { $output.Dispose() }

        if ($temporary -and [IO.File]::Exists($temporary)) {
            try {
                [IO.File]::Delete($temporary)
            }
            catch {
                Write-Warning "Не удалось убрать временный файл: $temporary"
            }
        }
    }
}