Last active
August 13, 2025 14:43
-
-
Save Fred-Vatin/87281696b171ec09ef97e1200f11c6d4 to your computer and use it in GitHub Desktop.
Backup/Restore PhoenixPE/PEBakery scripts state
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# backup-restore.ps1 | |
# Initial idea from Peter Siering, c't, 11/2022: https://github.com/PhoenixPE/PhoenixPE/issues/13#issuecomment-1371405100 | |
# @Fred-Vatin, rewritten 2025-08-07: https://gist.github.com/Fred-Vatin/87281696b171ec09ef97e1200f11c6d4 | |
# | |
# This script helps to read and write selected state of each script within a PhoenixPE project. | |
# | |
# Put this script in a backup folder at the same level of the Projects folder. | |
# | |
# Should run on every platform with pwsh (not tested). | |
# | |
# If called without parameter it prints the selected variables in console for all script files in projects folder | |
# Example: .\backup-restore.ps1 | |
# | |
# If called with -backcup, it saves the output in "CustomSettings.sel" file at the same level of this script | |
# Example: .\backup-restore.ps1 -backup | |
# | |
# If called with -restore, it writes the selected variables for all scripts mentioned in "CustomSettings.sel" | |
# Example: .\backup-restore.ps1 -restore | |
# | |
# You can saved your settings into another file name if you use -backup -name "my custom file name". | |
# You can restore your settings from a specific file name if you use -restore -name "my custom file name". | |
# The file name will be saved in the same directory as this script with the .sel extension. | |
# This allows to save/restore multiple use cases. | |
# $PSDefaultParameterValues['Out-File:Encoding'] = 'oem' | |
param( | |
[switch]$backup, | |
[switch]$restore, | |
[string]$name, | |
[switch]$test | |
) | |
<#*========================================================================== | |
* ℹ FUNCTIONS | |
===========================================================================#> | |
function Test-ValidWindowsFileName { | |
param ( | |
[string]$name | |
) | |
# Check if the string is empty or null | |
if ([string]::IsNullOrWhiteSpace($name)) { | |
Write-Error "The name is empty or null." | |
return $false | |
} | |
# Check the length (max 255 characters for file name) | |
if ($name.Length -gt 255) { | |
Write-Error "The name exceeds 255 characters." | |
return $false | |
} | |
# Check for forbidden characters (<, >, :, ", /, , |, ?, *) | |
$invalidChars = '[<>:"/\\|?*]' | |
if ($name -match $invalidChars) { | |
Write-Error "The name contains forbidden characters ($invalidChars)." | |
return $false | |
} | |
# Check that it is not a path (no \ or /) | |
if ($name -match '[\\/]') { | |
Write-Error "The name contains path separators (\ or /)." | |
return $false | |
} | |
# Check Windows reserved names (e.g. CON, PRN, AUX, NUL, COM1, LPT1, etc.) | |
$reservedNames = @("CON", "PRN", "AUX", "NUL", | |
"COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", | |
"LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9") | |
if ($reservedNames -contains $name.ToUpper()) { | |
Write-Error "'$name' is a reserved name by Windows." | |
return $false | |
} | |
# If all the checks pass | |
# Write-Host "The '$name' is a valid filename for Windows." -ForegroundColor Green | |
return $true | |
} | |
# Function to get selected variables | |
function Get-SelectedVariables { | |
$result = @("CTMZ") | |
Get-ChildItem "$projects" -Recurse -Filter *.script | | |
ForEach-Object { | |
$output = $null | |
foreach ($line in Get-Content $_.FullName) { | |
if ($line -imatch "^selected=(\w+)") { | |
$output = $matches[1] | |
} | |
} | |
if ($output) { | |
$relativePath = $_.FullName.replace($projects, '').SubString(1) | |
"$relativePath`:Selected=$output" | |
} | |
} | ForEach-Object { $result += $_ } | |
return $result | |
} | |
<#*========================================================================== | |
* ℹ LOGIC | |
===========================================================================#> | |
# Get script folder path | |
$scriptPath = $PSScriptRoot | |
$settingsfile = Join-Path -Path $scriptPath -ChildPath "CustomSettings.sel" | |
# Get path from one level higher and save it in $baseDir | |
$baseDir = Split-Path -Path $scriptPath -Parent | |
# get projects dir path | |
$projects = Join-Path -Path $baseDir -ChildPath "Projects" | |
if (!(Test-Path $projects)) { | |
Write-Error "Projects dir not found in : `"$baseDir`". Nothing can be saved or restored" | |
exit 1 | |
} | |
# Check for parameters | |
$backup = $PSBoundParameters.ContainsKey('backup') | |
$restore = $PSBoundParameters.ContainsKey('restore') | |
$hasname = $PSBoundParameters.ContainsKey('name') | |
$test = $PSBoundParameters.ContainsKey('test') | |
# Check if in valid project directory | |
if (!$test) { | |
if (!((Test-Path (Join-Path -Path $projects -ChildPath "MyApps")) -and (Test-Path (Join-Path -Path $projects -ChildPath "PhoenixPE")))) { | |
Write-Error "`"$projects`" doesn’t contain the required PhoenixPE directory" | |
exit 1 | |
} | |
} | |
if ($hasname) { | |
if (!(Test-ValidWindowsFileName($name))) { | |
Write-Error "$name is not a valid value for -name" | |
exit 1 | |
} | |
# get custom settings file | |
$customSetting = Join-Path -Path $scriptPath -ChildPath "$name.sel" | |
if ($restore) { | |
if (!(Test-Path $customSetting)) { | |
Write-Error "`"$name.sel`" can not be found in `"$scriptPath`"" | |
exit 1 | |
} | |
} | |
$settingsfile = $customSetting | |
} | |
# No parameters: print selected variables to console | |
if (!$backup -and !$restore) { | |
Get-SelectedVariables | |
exit 0 | |
} | |
# Backup mode: save selected variables to CustomSettings.sel | |
if ($backup) { | |
Get-SelectedVariables | Out-File -FilePath $settingsfile | |
Write-Host "`nData saved in: $settingsfile" -ForegroundColor Cyan -NoNewline | |
exit 0 | |
} | |
# Restore mode: apply settings from CustomSettings.sel | |
if ($restore) { | |
if (!(Test-Path "$settingsfile")) { | |
Write-Error "Custom settings file doesn’t not exist. Nothing to restore." | |
exit 1 | |
} | |
# Settings file provided: apply settings from specified file | |
$filecount = 0 | |
$signaturefound = $false | |
foreach ($setting in Get-Content $settingsfile) { | |
if ($setting -eq "CTMZ") { | |
$signaturefound = $true | |
continue | |
} | |
if ($signaturefound) { | |
$filecount++ | |
($path, $state) = $setting.Split(':', 2) | |
$path = Join-Path -Path "$projects" -ChildPath $path | |
($selname, $selval) = $state.Split('=', 2) | |
if (Test-Path $path) { | |
(Get-Content -Raw $path) | | |
ForEach-Object { $_ -replace "$selname=(.+)", "$selname=$selval" } | | |
Set-Content -NoNewline $path | |
Write-Output "Edited: $path" | |
} | |
else { | |
Write-Warning "File not found: $path" | |
} | |
} | |
else { | |
Write-Error "The settings file doesn’t contain a valid signature" | |
exit 1 | |
} | |
} | |
Write-Host "`n$filecount files processed" -ForegroundColor Cyan | |
exit 0 | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I modified the script so that it only modifies PhoenixPE-scripts during settings recovery if the settings are different. Otherwise, all scripts get a new timestamp. It also returns a summary of the modifications.