|
# This script automatically backs up the Crashlands save files on the selected interval. |
|
# It also allows the user to restore the latest backup by pressing 'R' and confirming within 5 seconds. |
|
# |
|
# Backups are kept in a rotation, meaning only the latest 5 backups are kept. Older backups are sent to |
|
# the Recycle Bin (not permanently deleted). |
|
# |
|
# The script will run indefinitely until the user presses Ctrl+C to exit, or until the user confirms a restore. |
|
Add-Type -AssemblyName Microsoft.VisualBasic |
|
|
|
# Configuration |
|
# Change these variables to suit your needs |
|
# The source directory is the location of the Crashlands save files. |
|
$source = Join-Path $env:LOCALAPPDATA "Crashlands\Game" |
|
# The backup directory is where the backups will be stored. |
|
# The default is the Downloads folder, but you can change it to any directory you prefer. |
|
$backupRoot = Join-Path $env:USERPROFILE "Downloads" |
|
# The interval in minutes between backups. |
|
$intervalMinutes = 2 |
|
# The maximum number of backups to keep. Older backups will be deleted. |
|
$maxBackups = 5 |
|
|
|
function Send-ToRecycleBin { |
|
param([string]$path) |
|
[Microsoft.VisualBasic.FileIO.FileSystem]::DeleteDirectory($path, 'OnlyErrorDialogs', 'SendToRecycleBin') |
|
} |
|
|
|
function Backup { |
|
$logTimestamp = Get-Date -Format "HH:mm:ss" |
|
$timestamp = Get-Date -Format "yyyyMMdd-HHmmss" |
|
$backupPath = Join-Path $backupRoot "crashlands-$timestamp" |
|
Copy-Item -Path $source -Destination $backupPath -Recurse |
|
Write-Host "[$logTimestamp] Backup created at: $backupPath" |
|
|
|
# Cleanup old backups |
|
$backups = Get-ChildItem -Path $backupRoot -Directory | Where-Object { $_.Name -like "crashlands-*" } | Sort-Object Name -Descending |
|
if ($backups.Count -gt $maxBackups) { |
|
$toDelete = $backups[$maxBackups..($backups.Count - 1)] |
|
foreach ($dir in $toDelete) { |
|
Write-Host "Deleting old backup: $($dir.FullName)" |
|
Send-ToRecycleBin -path $dir.FullName |
|
} |
|
} |
|
} |
|
|
|
function Restore-Latest { |
|
$backups = Get-ChildItem -Path $backupRoot -Directory | Where-Object { $_.Name -like "crashlands-*" } | Sort-Object Name -Descending |
|
if ($backups.Count -eq 0) { |
|
Write-Host "No backups found to restore." |
|
return |
|
} |
|
|
|
$latestBackup = $backups[0].FullName |
|
Write-Host "`nRestoring from backup: $latestBackup..." |
|
|
|
# Delete current contents |
|
Get-ChildItem -Path $source | ForEach-Object { |
|
if ($_.PSIsContainer) { |
|
Send-ToRecycleBin $_.FullName |
|
} else { |
|
[Microsoft.VisualBasic.FileIO.FileSystem]::DeleteFile($_.FullName, 'OnlyErrorDialogs', 'SendToRecycleBin') |
|
} |
|
} |
|
|
|
# Copy from backup |
|
Copy-Item -Path "$latestBackup\*" -Destination $source -Recurse |
|
Write-Host "Restore complete." |
|
} |
|
|
|
# Start backup loop with user input listener |
|
Write-Host "Crashlands backup started. Press 'R' to restore the latest backup. Press Ctrl+C to exit." |
|
|
|
$timer = [System.Diagnostics.Stopwatch]::StartNew() |
|
while ($true) { |
|
if ($timer.Elapsed.TotalMinutes -ge $intervalMinutes) { |
|
Backup |
|
$timer.Restart() |
|
} |
|
|
|
if ($Host.UI.RawUI.KeyAvailable) { |
|
$key = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") |
|
if ($key.Character -eq 'r' -or $key.Character -eq 'R') { |
|
Write-Host "`nRestore latest backup? [y/N] (auto-cancel in 5s): " -NoNewline |
|
|
|
$response = '' |
|
$stopwatch = [System.Diagnostics.Stopwatch]::StartNew() |
|
while ($stopwatch.Elapsed.TotalSeconds -lt 5) { |
|
if ([Console]::KeyAvailable) { |
|
$response = [Console]::ReadKey($true).KeyChar |
|
break |
|
} |
|
Start-Sleep -Milliseconds 100 |
|
} |
|
|
|
if ($response -eq 'y' -or $response -eq 'Y') { |
|
Restore-Latest |
|
Write-Host "`n`nRestore complete. Please restart the program to resume automatic backups." |
|
Start-Sleep -Seconds 3 |
|
break |
|
} else { |
|
Write-Host "`nRestore canceled." |
|
} |
|
} |
|
|
|
# Clear any leftover key presses |
|
while ([Console]::KeyAvailable) { |
|
[Console]::ReadKey($true) | Out-Null |
|
} |
|
} |
|
|
|
Start-Sleep -Milliseconds 500 |
|
} |