|
<# |
|
.SYNOPSIS |
|
Unofficial UE 5.8.x clean-install metadata finalization workaround. |
|
.DESCRIPTION |
|
For ONE narrow situation: UE 5.8.x was completely downloaded and verified, |
|
UnrealEditor.exe starts directly, but Epic Launcher stays at Resume because |
|
the pending .item/.egi/.manifest was never committed and there is NO |
|
UE_5.8 registration in LauncherInstalled.dat. |
|
|
|
Discovers the installation ID, install location, and build from existing |
|
metadata. No installation-specific identifiers are embedded in this script. |
|
Runs a READ-ONLY diagnostic by default. -Apply requires administrator, |
|
fresh backup, confirmation of independent file verification, and APPLY. |
|
|
|
Not for upgrades where an older completed UE_5.8 record already exists, |
|
partially downloaded/failed installs, other UE releases, or other .egi |
|
formats. Not an official Epic Games fix. Test carefully at your own risk. |
|
|
|
.EXAMPLE |
|
.\UE58-CleanInstall-Metadata-Repair.ps1 |
|
.EXAMPLE |
|
.\UE58-CleanInstall-Metadata-Repair.ps1 -Apply |
|
#> |
|
[CmdletBinding()] |
|
param([switch]$Apply) |
|
|
|
$ErrorActionPreference = 'Stop' |
|
Set-StrictMode -Version Latest |
|
|
|
$AppName = 'UE_5.8' |
|
$Utf8 = New-Object System.Text.UTF8Encoding($false) |
|
$ProgramDataPath = [Environment]::GetFolderPath('CommonApplicationData') |
|
$ManifestRoot = Join-Path $ProgramDataPath 'Epic\EpicGamesLauncher\Data\Manifests' |
|
$PendingRoot = Join-Path $ManifestRoot 'Pending' |
|
$ItemsRoot = Join-Path $ProgramDataPath 'Epic\EpicOnlineServicesShared\InstallHelper\InstalledItems' |
|
$LauncherDatPath = Join-Path $ProgramDataPath 'Epic\UnrealEngineLauncher\LauncherInstalled.dat' |
|
|
|
function Assert-Condition([bool]$Condition, [string]$Reason) { |
|
if (-not $Condition) { throw ('STOP: ' + $Reason) } |
|
} |
|
function Read-JsonFile([string]$Path) { |
|
Assert-Condition (Test-Path -LiteralPath $Path -PathType Leaf) "File not found: $Path" |
|
return (Get-Content -LiteralPath $Path -Raw | ConvertFrom-Json) |
|
} |
|
function CanonicalPath([string]$Path) { |
|
Assert-Condition (-not [string]::IsNullOrWhiteSpace($Path)) 'An installation path is blank.' |
|
return [System.IO.Path]::GetFullPath($Path).TrimEnd([char]'\') |
|
} |
|
function PathsMatch([string]$Left, [string]$Right) { |
|
return ((CanonicalPath $Left) -ieq (CanonicalPath $Right)) |
|
} |
|
function EnsureClosed { |
|
$Open = @(Get-Process -Name EpicGamesLauncher,EpicWebHelper,EpicOnlineServicesInstallHelper,InstallHelper,UnrealEditor -ErrorAction SilentlyContinue) |
|
Assert-Condition ($Open.Count -eq 0) ('Close these processes before continuing: ' + (($Open | Select-Object -ExpandProperty ProcessName -Unique) -join ', ')) |
|
} |
|
function Write-JsonAtomic([string]$Path, $Object) { |
|
# A temporary file on the same volume avoids leaving partly-written JSON. |
|
$Temp = "$Path.$([guid]::NewGuid().ToString('N')).tmp" |
|
try { |
|
$Json = ConvertTo-Json -InputObject $Object -Depth 40 |
|
[System.IO.File]::WriteAllText($Temp, $Json + "`r`n", $Utf8) |
|
if (Test-Path -LiteralPath $Path) { |
|
[System.IO.File]::Replace($Temp, $Path, $null) |
|
} else { |
|
[System.IO.File]::Move($Temp, $Path) |
|
} |
|
} |
|
finally { |
|
if (Test-Path -LiteralPath $Temp) { Remove-Item -LiteralPath $Temp -Force -ErrorAction SilentlyContinue } |
|
} |
|
} |
|
function VerifyBackup([string]$Original, [string]$Copy) { |
|
Copy-Item -LiteralPath $Original -Destination $Copy -ErrorAction Stop |
|
$Before = (Get-FileHash -LiteralPath $Original -Algorithm SHA256).Hash |
|
$After = (Get-FileHash -LiteralPath $Copy -Algorithm SHA256).Hash |
|
Assert-Condition ($Before -eq $After) "Backup verification failed for $Original" |
|
} |
|
function Read-InstallationState { |
|
EnsureClosed |
|
Assert-Condition (Test-Path -LiteralPath $PendingRoot -PathType Container) 'Epic pending manifest folder not found.' |
|
Assert-Condition (Test-Path -LiteralPath $ItemsRoot -PathType Container) 'Epic InstallHelper database folder not found.' |
|
|
|
# Do not guess when multiple pending UE_5.8 transactions are present. |
|
$Matches = @( |
|
Get-ChildItem -LiteralPath $PendingRoot -Filter '*.item' -File | ForEach-Object { |
|
$CandidatePath = $_.FullName |
|
try { |
|
$Candidate = Read-JsonFile $CandidatePath |
|
if ($Candidate.AppName -eq $AppName) { |
|
[PSCustomObject]@{ Path = $CandidatePath; Data = $Candidate } |
|
} |
|
} |
|
catch { |
|
# Other apps' .item files are irrelevant. |
|
} |
|
} |
|
) |
|
Assert-Condition ($Matches.Count -eq 1) "Expected exactly one pending $AppName .item, found $($Matches.Count). This tool cannot handle an already committed install or ambiguous transactions." |
|
|
|
$ItemPath = $Matches[0].Path |
|
$Item = $Matches[0].Data |
|
$Id = [string]$Item.InstallationGuid |
|
Assert-Condition ($Id -cmatch '^[A-Fa-f0-9]{32}$') 'Unexpected installation GUID format.' |
|
Assert-Condition (([IO.Path]::GetFileNameWithoutExtension($ItemPath)) -ieq $Id) 'Pending .item filename and installation GUID disagree.' |
|
Assert-Condition ($Item.AppName -eq $AppName) 'Pending .item app mismatch.' |
|
Assert-Condition ($Item.bIsIncompleteInstall -eq $true) 'Pending .item is not marked incomplete.' |
|
Assert-Condition ($Item.CatalogNamespace -eq 'ue') 'Unexpected Epic catalog namespace.' |
|
Assert-Condition ($Item.CatalogItemId -cmatch '^[A-Fa-f0-9]{32}$') 'Unexpected Epic catalog item ID.' |
|
Assert-Condition ($Item.EoshRevision -cmatch '^[A-Fa-f0-9]{32}$') 'Unexpected helper revision format.' |
|
Assert-Condition ($Item.PrereqSHA1Hash -match '^[A-Fa-f0-9]{40}$') 'Unexpected prerequisite hash format.' |
|
Assert-Condition ($Item.LaunchExecutable -eq 'Engine/Binaries/Win64/UnrealEditor.exe') 'Unexpected editor executable path.' |
|
|
|
# Only UE 5.8.x build/version strings in the known Windows format. |
|
$Version = [string]$Item.AppVersionString |
|
$VersionMatch = [regex]::Match($Version, '^5\.8\.(?<patch>\d+)-(?<cl>\d+)\+\+\+UE5\+Release-5\.8-Windows$') |
|
Assert-Condition $VersionMatch.Success "Not a recognized UE 5.8.x Windows version: $Version" |
|
$Patch = [int]$VersionMatch.Groups['patch'].Value |
|
$CL = [int64]$VersionMatch.Groups['cl'].Value |
|
$EngineDir = CanonicalPath ([string]$Item.InstallLocation) |
|
$EgStore = Join-Path $EngineDir '.egstore' |
|
$PendingManifestPath = Join-Path (Join-Path $EgStore 'Pending') ($Id + '.manifest') |
|
$CompleteManifestPath = Join-Path $EgStore ($Id + '.manifest') |
|
$CompleteItemPath = Join-Path $ManifestRoot ($Id + '.item') |
|
$EgiPath = Join-Path $ItemsRoot ($Id + '.egi') |
|
$BuildPath = Join-Path $EngineDir 'Engine\Build\Build.version' |
|
$EditorPath = Join-Path $EngineDir 'Engine\Binaries\Win64\UnrealEditor.exe' |
|
|
|
Assert-Condition (PathsMatch $Item.ManifestLocation $EgStore) 'Unexpected .item manifest directory.' |
|
Assert-Condition (PathsMatch $Item.PendingManifestPath $PendingManifestPath) 'Unexpected pending manifest path in .item.' |
|
Assert-Condition (PathsMatch $Item.CompleteManifestPath $CompleteManifestPath) 'Unexpected completed manifest path in .item.' |
|
Assert-Condition (-not (Test-Path -LiteralPath $CompleteItemPath)) 'Completed .item already exists; this is not the clean-install case.' |
|
Assert-Condition (-not (Test-Path -LiteralPath $CompleteManifestPath)) 'Completed manifest already exists; this is not the clean-install case.' |
|
Assert-Condition (Test-Path -LiteralPath $PendingManifestPath -PathType Leaf) 'Pending engine manifest missing.' |
|
Assert-Condition (Test-Path -LiteralPath $EditorPath -PathType Leaf) 'UnrealEditor.exe missing.' |
|
|
|
$Build = Read-JsonFile $BuildPath |
|
Assert-Condition ([int]$Build.MajorVersion -eq 5 -and [int]$Build.MinorVersion -eq 8 -and [int]$Build.PatchVersion -eq $Patch) 'Physical Engine Build.version does not match pending version.' |
|
Assert-Condition ([int64]$Build.Changelist -eq $CL) 'Physical Engine changelist does not match pending version.' |
|
$ExeVersion = (Get-Item -LiteralPath $EditorPath).VersionInfo |
|
Assert-Condition ($ExeVersion.ProductVersion -match ('CL-' + $CL + '(\D|$)')) 'UnrealEditor.exe changelist does not match pending version.' |
|
if ($ExeVersion.FileMajorPart -gt 0) { |
|
Assert-Condition ($ExeVersion.FileMajorPart -eq 5 -and $ExeVersion.FileMinorPart -eq 8 -and $ExeVersion.FileBuildPart -eq $Patch) 'UnrealEditor.exe numeric version does not match pending version.' |
|
} |
|
|
|
$Egi = Read-JsonFile $EgiPath |
|
Assert-Condition ($null -ne $Egi.v4) 'Unsupported helper .egi schema (expected v4).' |
|
Assert-Condition ($Egi.v4.installationId -ieq $Id) '.egi installation GUID mismatch.' |
|
Assert-Condition ($Egi.v4.state -eq 'Incomplete') '.egi is not in the expected Incomplete state.' |
|
Assert-Condition ($Egi.v4.artifactId -eq $AppName) '.egi artifact mismatch.' |
|
Assert-Condition ($Egi.v4.platform -eq 'Windows') '.egi platform mismatch.' |
|
Assert-Condition ($Egi.v4.sandboxId -eq $Item.CatalogNamespace) '.egi namespace mismatch.' |
|
Assert-Condition ($Egi.v4.itemId -eq $Item.CatalogItemId) '.egi catalog item mismatch.' |
|
Assert-Condition ($Egi.v4.revision -eq $Item.EoshRevision) '.egi helper revision mismatch.' |
|
Assert-Condition (PathsMatch $Egi.v4.dir $EngineDir) '.egi install directory mismatch.' |
|
Assert-Condition (PathsMatch $Egi.v4.metaDir $EgStore) '.egi metadata directory mismatch.' |
|
Assert-Condition (PathsMatch $Egi.v4.pendingManifestPath $PendingManifestPath) '.egi pending manifest path mismatch.' |
|
Assert-Condition (PathsMatch $Egi.v4.manifestPath $CompleteManifestPath) '.egi completed manifest path mismatch.' |
|
Assert-Condition ($Egi.v4.pendingManifestData.version -eq $Version) '.egi pending version mismatch.' |
|
Assert-Condition ($Egi.v4.manifestData.version -eq '') '.egi already has a completed manifest version.' |
|
Assert-Condition ($Egi.v4.pendingManifestData.prereqSHA1Hash -eq $Item.PrereqSHA1Hash) '.egi prerequisite hash mismatch.' |
|
Assert-Condition ($Egi.v4.pendingManifestData.launchExe -eq $Item.LaunchExecutable) '.egi editor path mismatch.' |
|
Assert-Condition ([int64]$Egi.v4.pendingManifestData.buildSize -eq [int64]$Item.InstallSize) '.egi build size mismatch.' |
|
Assert-Condition ([int64]$Item.InstallSize -gt 1000000000) 'Reported engine installation size is unexpectedly small.' |
|
Assert-Condition (@($Egi.v4.pendingTags).Count -gt 0) 'No helper pending tags; unexpected state.' |
|
Assert-Condition (@($Egi.v4.tags).Count -eq 0) 'Helper already has installed tags.' |
|
|
|
$LauncherDat = Read-JsonFile $LauncherDatPath |
|
Assert-Condition ($null -ne $LauncherDat.InstallationList) 'LauncherInstalled.dat lacks InstallationList.' |
|
Assert-Condition (@($LauncherDat.InstallationList | Where-Object { $_.AppName -eq $AppName }).Count -eq 0) 'LauncherInstalled.dat already has UE_5.8 registration; use a different workaround.' |
|
# A completed UE_5.8 item with some OTHER GUID is also a different case. |
|
$OtherCompleted = @( |
|
Get-ChildItem -LiteralPath $ManifestRoot -Filter '*.item' -File | ForEach-Object { |
|
try { |
|
$Candidate = Read-JsonFile $_.FullName |
|
if ($Candidate.AppName -eq $AppName) { $_.FullName } |
|
} |
|
catch { } |
|
} |
|
) |
|
Assert-Condition ($OtherCompleted.Count -eq 0) 'An existing completed UE_5.8 item was found; refusing to overwrite an older installation.' |
|
|
|
$ManifestLength = (Get-Item -LiteralPath $PendingManifestPath).Length |
|
Assert-Condition ($ManifestLength -gt 1000000) 'Pending manifest is unusually small.' |
|
$ManifestHash = (Get-FileHash -LiteralPath $PendingManifestPath -Algorithm SHA256).Hash |
|
|
|
return [PSCustomObject]@{ |
|
AppName = $AppName; Version = $Version; CL = $CL; Patch = $Patch |
|
Id = $Id; EngineDir = $EngineDir; EgStore = $EgStore |
|
ItemPath = $ItemPath; CompleteItemPath = $CompleteItemPath |
|
EgiPath = $EgiPath; LauncherDatPath = $LauncherDatPath |
|
PendingManifestPath = $PendingManifestPath; CompleteManifestPath = $CompleteManifestPath |
|
BuildPath = $BuildPath; ManifestLength = $ManifestLength; ManifestHash = $ManifestHash |
|
Item = $Item; Egi = $Egi; LauncherDat = $LauncherDat |
|
} |
|
} |
|
|
|
Write-Host '' |
|
Write-Host 'UE 5.8.x CLEAN-INSTALL METADATA WORKAROUND (unofficial)' |
|
Write-Host ('Mode: ' + $(if ($Apply) { 'APPLY / confirmation required' } else { 'READ-ONLY / DRY RUN' })) |
|
Write-Host '' |
|
$State = Read-InstallationState |
|
Write-Host ('Installation: ' + $State.EngineDir) |
|
Write-Host ('Version: ' + $State.Version) |
|
Write-Host ('GUID: ' + $State.Id) |
|
Write-Host ('Manifest: {0:N0} bytes' -f $State.ManifestLength) |
|
Write-Host ('SHA256: ' + $State.ManifestHash) |
|
Write-Host '' |
|
Write-Host 'MATCH: One uncommitted UE_5.8 installation; matching .item/.egi records;' |
|
Write-Host ' engine executable and Build.version match; no completed registration.' |
|
Write-Host 'NOTE: These checks DO NOT verify every engine file. Inspect InstallHelper logs' |
|
Write-Host ' for successful BuildPatch/BPS completion, and launch UnrealEditor.exe' |
|
Write-Host ' directly before deciding to apply. Do NOT apply to partial downloads.' |
|
Write-Host '' |
|
Write-Host 'Would commit pending manifest, .item and .egi, register UE_5.8, and remove' |
|
Write-Host 'ONLY the corresponding pending .item and manifest. No Engine binaries change.' |
|
|
|
if (-not $Apply) { |
|
Write-Host '' |
|
Write-Host 'DRY RUN PASSED. Nothing was changed.' -ForegroundColor Green |
|
return |
|
} |
|
|
|
$Identity = [Security.Principal.WindowsIdentity]::GetCurrent() |
|
$Principal = New-Object Security.Principal.WindowsPrincipal($Identity) |
|
Assert-Condition ($Principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) 'Apply mode requires an administrator PowerShell window.' |
|
|
|
$BackupRoot = [Environment]::GetFolderPath('Desktop') |
|
Assert-Condition (-not [string]::IsNullOrWhiteSpace($BackupRoot)) 'Could not locate Desktop backup folder.' |
|
$BackupDir = Join-Path $BackupRoot ("UE58_MetadataBackup_{0}_{1}" -f (Get-Date -Format 'yyyyMMdd_HHmmss'), ([guid]::NewGuid().ToString('N').Substring(0,8))) |
|
New-Item -ItemType Directory -Path $BackupDir -ErrorAction Stop | Out-Null |
|
VerifyBackup $State.ItemPath (Join-Path $BackupDir 'OriginalPending.item') |
|
VerifyBackup $State.EgiPath (Join-Path $BackupDir 'Original.egi') |
|
VerifyBackup $State.LauncherDatPath (Join-Path $BackupDir 'OriginalLauncherInstalled.dat') |
|
VerifyBackup $State.PendingManifestPath (Join-Path $BackupDir 'OriginalPending.manifest') |
|
VerifyBackup $State.BuildPath (Join-Path $BackupDir 'OriginalBuild.version') |
|
|
|
# Keep exact restore paths in the backup, for inspection / manual recovery. |
|
@( |
|
'ORIGINAL FILES (keep this entire folder; Launcher must be closed to restore):' |
|
"OriginalPending.item => $($State.ItemPath)" |
|
"Original.egi => $($State.EgiPath)" |
|
"OriginalLauncherInstalled.dat => $($State.LauncherDatPath)" |
|
"OriginalPending.manifest => $($State.PendingManifestPath)" |
|
"OriginalBuild.version => $($State.BuildPath) (reference only; never changed)" |
|
'' |
|
'The script creates these new files, absent at start:' |
|
"COMPLETED .item => $($State.CompleteItemPath)" |
|
"COMPLETED manifest => $($State.CompleteManifestPath)" |
|
'' |
|
'Do not manually restore this backup after a later update without reviewing changed state.' |
|
) | Set-Content -LiteralPath (Join-Path $BackupDir 'RESTORE_PATHS.txt') -Encoding UTF8 |
|
|
|
Write-Host '' |
|
Write-Host ('Verified backup created: ' + $BackupDir) -ForegroundColor Yellow |
|
Write-Host 'WARNING: The script cannot itself prove a complete BuildPatch installation.' -ForegroundColor Yellow |
|
$Verified = Read-Host 'If InstallHelper log confirms BPS success AND the editor opens directly, type VERIFIED' |
|
if ($Verified -cne 'VERIFIED') { Write-Host 'Cancelled. Installation untouched.'; return } |
|
$Confirmation = Read-Host 'Type APPLY to commit the metadata, anything else to cancel' |
|
if ($Confirmation -cne 'APPLY') { Write-Host 'Cancelled. Installation untouched.'; return } |
|
|
|
$Final = Read-InstallationState |
|
Assert-Condition ($Final.Id -eq $State.Id -and $Final.Version -eq $State.Version -and $Final.ManifestHash -eq $State.ManifestHash) 'Metadata or manifest changed while waiting; refusing to apply.' |
|
# Detect edits that occurred after our backup, including edits to helper metadata. |
|
$RecheckSources = @( |
|
[PSCustomObject]@{ Original = $Final.ItemPath; Backup = (Join-Path $BackupDir 'OriginalPending.item') } |
|
[PSCustomObject]@{ Original = $Final.EgiPath; Backup = (Join-Path $BackupDir 'Original.egi') } |
|
[PSCustomObject]@{ Original = $Final.LauncherDatPath; Backup = (Join-Path $BackupDir 'OriginalLauncherInstalled.dat') } |
|
[PSCustomObject]@{ Original = $Final.PendingManifestPath; Backup = (Join-Path $BackupDir 'OriginalPending.manifest') } |
|
[PSCustomObject]@{ Original = $Final.BuildPath; Backup = (Join-Path $BackupDir 'OriginalBuild.version') } |
|
) |
|
foreach ($Pair in $RecheckSources) { |
|
$LiveHash = (Get-FileHash -LiteralPath $Pair.Original -Algorithm SHA256).Hash |
|
$BackupHash = (Get-FileHash -LiteralPath $Pair.Backup -Algorithm SHA256).Hash |
|
Assert-Condition ($LiveHash -eq $BackupHash) ("File changed after backup: " + $Pair.Original) |
|
} |
|
EnsureClosed |
|
|
|
$ChangesStarted = $false |
|
try { |
|
$ChangesStarted = $true |
|
Copy-Item -LiteralPath $Final.PendingManifestPath -Destination $Final.CompleteManifestPath -ErrorAction Stop |
|
Assert-Condition ((Get-FileHash -LiteralPath $Final.CompleteManifestPath -Algorithm SHA256).Hash -eq $Final.ManifestHash) 'Completed manifest copy failed SHA256 check.' |
|
|
|
$Egi = $Final.Egi |
|
$Egi.v4.state = 'Installed' |
|
$Egi.v4.tags = @($Egi.v4.pendingTags) |
|
$Egi.v4.pendingTags = @() |
|
$Egi.v4.manifestData = $Egi.v4.pendingManifestData |
|
$Egi.v4.pendingManifestData = [PSCustomObject]@{ |
|
version = ''; launchCommand = ''; launchExe = ''; prereqSHA1Hash = ''; buildSize = 0 |
|
} |
|
Write-JsonAtomic $Final.EgiPath $Egi |
|
|
|
$Item = $Final.Item |
|
$Item.bIsIncompleteInstall = $false |
|
$Item.InstallTags = @($Egi.v4.tags) |
|
Write-JsonAtomic $Final.CompleteItemPath $Item |
|
|
|
$Registration = [PSCustomObject]@{ |
|
InstallLocation = $Final.EngineDir |
|
NamespaceId = 'ue' |
|
ItemId = $Item.CatalogItemId |
|
ArtifactId = $Final.AppName |
|
AppVersion = $Final.Version |
|
AppName = $Final.AppName |
|
} |
|
$LauncherDat = $Final.LauncherDat |
|
$LauncherDat.InstallationList = @($LauncherDat.InstallationList) + @($Registration) |
|
Write-JsonAtomic $Final.LauncherDatPath $LauncherDat |
|
|
|
# Commit checks before removing the original pending files. |
|
$NewItem = Read-JsonFile $Final.CompleteItemPath |
|
$NewEgi = Read-JsonFile $Final.EgiPath |
|
$NewLauncher = Read-JsonFile $Final.LauncherDatPath |
|
$NewRegistrations = @($NewLauncher.InstallationList | Where-Object { $_.AppName -eq $Final.AppName }) |
|
Assert-Condition ($NewItem.bIsIncompleteInstall -eq $false -and $NewItem.AppVersionString -eq $Final.Version) 'Committed .item validation failed.' |
|
Assert-Condition ($NewEgi.v4.state -eq 'Installed' -and $NewEgi.v4.manifestData.version -eq $Final.Version) 'Committed .egi validation failed.' |
|
Assert-Condition (@($NewEgi.v4.pendingTags).Count -eq 0) 'Committed .egi still has pending tags.' |
|
Assert-Condition ($NewRegistrations.Count -eq 1 -and $NewRegistrations[0].AppVersion -eq $Final.Version) 'LauncherInstalled.dat registration validation failed.' |
|
Assert-Condition ((Get-FileHash -LiteralPath $Final.CompleteManifestPath -Algorithm SHA256).Hash -eq $Final.ManifestHash) 'Committed manifest hash mismatch.' |
|
|
|
Remove-Item -LiteralPath $Final.ItemPath -ErrorAction Stop |
|
Remove-Item -LiteralPath $Final.PendingManifestPath -ErrorAction Stop |
|
$ChangesStarted = $false |
|
Write-Host '' |
|
Write-Host 'METADATA REPAIR WRITTEN AND VERIFIED.' -ForegroundColor Green |
|
Write-Host ('Backup: ' + $BackupDir) |
|
Write-Host 'Restart Epic Games Launcher and check for the Launch button.' |
|
Write-Host 'Keep your backup. This workaround does not fix Epic InstallHelper itself.' |
|
} |
|
catch { |
|
Write-Warning ('Repair failed: ' + $_.Exception.Message) |
|
if ($ChangesStarted) { |
|
Write-Warning 'Attempting to restore pre-repair metadata. Keep Epic Launcher closed.' |
|
try { |
|
$PendingDir = Split-Path -Parent $Final.ItemPath |
|
$ManifestPendingDir = Split-Path -Parent $Final.PendingManifestPath |
|
if (-not (Test-Path -LiteralPath $PendingDir)) { New-Item -ItemType Directory -Path $PendingDir | Out-Null } |
|
if (-not (Test-Path -LiteralPath $ManifestPendingDir)) { New-Item -ItemType Directory -Path $ManifestPendingDir | Out-Null } |
|
Copy-Item -LiteralPath (Join-Path $BackupDir 'OriginalPending.item') -Destination $Final.ItemPath -Force |
|
Copy-Item -LiteralPath (Join-Path $BackupDir 'OriginalPending.manifest') -Destination $Final.PendingManifestPath -Force |
|
Copy-Item -LiteralPath (Join-Path $BackupDir 'Original.egi') -Destination $Final.EgiPath -Force |
|
Copy-Item -LiteralPath (Join-Path $BackupDir 'OriginalLauncherInstalled.dat') -Destination $Final.LauncherDatPath -Force |
|
Remove-Item -LiteralPath $Final.CompleteItemPath -Force -ErrorAction SilentlyContinue |
|
Remove-Item -LiteralPath $Final.CompleteManifestPath -Force -ErrorAction SilentlyContinue |
|
Write-Warning 'Rollback attempted. Confirm the original files using RESTORE_PATHS.txt.' |
|
} |
|
catch { |
|
Write-Warning ('ROLLBACK FAILED: ' + $_.Exception.Message) |
|
Write-Warning ('Do not restart the launcher until reconciled. Backup: ' + $BackupDir) |
|
} |
|
} |
|
throw |
|
} |