mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-11-05 09:33:25 -08:00
[Installer] Split the NSIS template into includes
Pure refactor, no behavior change. cmake/NSIS.template.in is the CPack template, and CPack substitutes the @...@ placeholders in that file only, so everything without a substitution moves into cmake/nsis/ and is included from it: installer-log.nsh the CockLog macro and the two writers behind it process-guard.nsh running instance handling, installer and uninstaller copy user-data.nsh backup/restore around the previous version's uninstaller runtime-purge.nsh stale runtime purge, diagnostics and the holder probe The page and section order, the Vars the included files share, .onInit (it names the package version in the log), AbortInstall (its Sentry offer is substituted), componentsPagePre and the registry bookkeeping stay in the template. The includes resolve through NSIS_SOURCE_PATH, which the template already uses for the bitmaps, and an include does not change the compile directory, so the holder probe keeps its relative File path.
This commit is contained in:
parent
44d5138181
commit
2ea11d4599
5 changed files with 1713 additions and 1623 deletions
492
cmake/nsis/user-data.nsh
Normal file
492
cmake/nsis/user-data.nsh
Normal file
|
|
@ -0,0 +1,492 @@
|
|||
; User data backup and restore around the previous version's uninstaller.
|
||||
;
|
||||
; Kept apart from the upgrade orchestration in the template because all of it is
|
||||
; about the previous version's uninstaller: it waits for that uninstaller's
|
||||
; payload to disappear, removes what it left behind and appends its log, and it
|
||||
; copies the user's data out of $OldInstallDir before the uninstaller runs and
|
||||
; puts it back afterwards. The reasoning for the shared %TEMP% backup directory
|
||||
; and for the copy-not-move relocation is in the comment above each function.
|
||||
;
|
||||
; Registers: $OldUninstallExit/$OldUninstallExe/$OldPayloadRemoved for the
|
||||
; leftover handling, $DataBackupDir, $DataBackupDone, $DataBackupForDir and the
|
||||
; $OldInstallDir/$INSTDIR pair for the backup. Removing leftovers aborts the
|
||||
; install when it cannot free the files; the backup and restore never do.
|
||||
|
||||
; Waits for the previous installation's payload to be gone, so the sections below never
|
||||
; write into a directory an old uninstaller is still emptying.
|
||||
;
|
||||
; ExecWait only waits for as long as the uninstaller runs in place: an NSIS uninstaller
|
||||
; that did not get a usable _?= copies itself to %TEMP%, starts that copy and exits, so
|
||||
; ExecWait returns at once while the copy keeps deleting - and it finishes after this
|
||||
; window has closed, removing the version that was just installed. Waiting for
|
||||
; cockatrice.exe to disappear covers both cases, and stops with a message instead of
|
||||
;
|
||||
; When the payload is still there after the wait, $OldUninstallExit decides: a non-zero
|
||||
; code means the uninstaller was cancelled or failed, and the install aborts saying so.
|
||||
; A zero code means it finished but skipped files someone was holding (NSIS Delete does
|
||||
; that silently), so RemoveOldPayloadLeftovers frees and removes them, and the install
|
||||
; only aborts - naming the holders - when even that fails.
|
||||
; installing over a directory that is still being emptied.
|
||||
Function WaitForOldPayloadToDisappear
|
||||
${If} $OldInstallDir == ""
|
||||
Return
|
||||
${EndIf}
|
||||
; The registered uninstaller could not be run at all (missing, or ExecWait could not
|
||||
; start it): nothing is going to empty the directory, so waiting is pointless and
|
||||
; aborting as "cancelled" was wrong. Remove the previous payload the same way the
|
||||
; skipped-files recovery below does, and abort - naming why - only if that fails.
|
||||
${If} $OldUninstallExit == "missing"
|
||||
${OrIf} $OldUninstallExit == "launch-failed"
|
||||
${IfNot} ${FileExists} "$OldInstallDir\*.*"
|
||||
!insertmacro CockLog "old uninstall: uninstaller $OldUninstallExit and $OldInstallDir is already gone"
|
||||
Return
|
||||
${EndIf}
|
||||
!insertmacro CockLog "old uninstall: uninstaller $OldUninstallExit ('$OldUninstallString'); removing $OldInstallDir payload directly"
|
||||
Call AppendUninstallerLog
|
||||
Call RemoveOldPayloadLeftovers
|
||||
${If} $OldPayloadRemoved != "1"
|
||||
StrCpy $AbortReason \
|
||||
"The previous version in $OldInstallDir could not be removed: its uninstaller ($OldUninstallExe) could not be run ($OldUninstallExit), and removing the files directly failed.$\r$\n$\r$\nStill in use by: $HolderOutput$\r$\n$\r$\nClose anything still using those files and run this installer again."
|
||||
Call RestoreUserDataAfterUninstall
|
||||
Call AbortInstall
|
||||
${EndIf}
|
||||
!insertmacro CockLog "old uninstall: continuing after removing the payload of an uninstaller that could not run"
|
||||
Return
|
||||
${EndIf}
|
||||
StrCpy $R2 0
|
||||
old_uninstall_wait:
|
||||
IfFileExists "$OldInstallDir\cockatrice.exe" 0 old_uninstall_gone
|
||||
IntOp $R2 $R2 + 1
|
||||
${If} $R2 > 60
|
||||
!insertmacro CockLog "old uninstall: $OldInstallDir still holds cockatrice.exe after 30 s (uninstaller exit=$OldUninstallExit)"
|
||||
; The uninstaller's own account of what it skipped or why it stopped lives in its
|
||||
; separate log; copy it here so the report sent from AbortInstall carries it.
|
||||
Call AppendUninstallerLog
|
||||
${If} $OldUninstallExit != "0"
|
||||
; Cancelled or failed with a non-zero exit code (an uninstaller that could not be
|
||||
; started is handled above): the old version was not meant to be removed, so
|
||||
; nothing is deleted on its behalf.
|
||||
StrCpy $AbortReason \
|
||||
"The previous version in $OldInstallDir is still there because its uninstaller was cancelled or failed (exit code '$OldUninstallExit').$\r$\n$\r$\nClose anything still using those files and run this installer again."
|
||||
; The backup was taken before the uninstaller ran; an abort must not leave the
|
||||
; only copy of the user data in %TEMP%, where the next run clears it.
|
||||
Call RestoreUserDataAfterUninstall
|
||||
Call AbortInstall
|
||||
${EndIf}
|
||||
; The uninstaller finished but skipped files - a running instance or a scanner holding
|
||||
; them, which NSIS Delete passes over silently. Free them and remove what is left.
|
||||
Call RemoveOldPayloadLeftovers
|
||||
; The removal reports success only when every payload executable is gone and the
|
||||
; DLL/Plugins purge came back clean; cockatrice.exe alone would hide a locked
|
||||
; oracle.exe or a stale DLL the new payload is about to be written over.
|
||||
${If} $OldPayloadRemoved != "1"
|
||||
StrCpy $AbortReason \
|
||||
"The previous version in $OldInstallDir could not be fully removed after its uninstaller ran.$\r$\n$\r$\nStill in use by: $HolderOutput$\r$\n$\r$\nClose anything still using those files and run this installer again."
|
||||
Call RestoreUserDataAfterUninstall
|
||||
Call AbortInstall
|
||||
${EndIf}
|
||||
; The payload is gone and ExecWait already waited for the uninstaller; keep the
|
||||
; short settle the other paths use, then hand back to the caller's restore.
|
||||
Sleep 1000
|
||||
!insertmacro CockLog "old uninstall: continuing after removing the leftovers the uninstaller skipped"
|
||||
Return
|
||||
${EndIf}
|
||||
Sleep 500
|
||||
Goto old_uninstall_wait
|
||||
old_uninstall_gone:
|
||||
${If} $R2 > 0
|
||||
; ExecWait returned while the old payload was still there: that uninstaller
|
||||
; copied itself to %TEMP%, and the copy is still working through its file list.
|
||||
; cockatrice.exe is gone before the rest of it, so let the copy settle before
|
||||
; the sections below start writing files it would otherwise remove.
|
||||
Sleep 1000
|
||||
!insertmacro CockLog "old uninstall: ExecWait returned early, settled 1 s after $R2 * 500 ms"
|
||||
${Else}
|
||||
!insertmacro CockLog "old uninstall: $OldInstallDir was already clear when ExecWait returned"
|
||||
${EndIf}
|
||||
Return
|
||||
FunctionEnd
|
||||
|
||||
; Called when the old uninstaller exited successfully but left its payload behind, or
|
||||
; could not be run at all (missing or not startable) and the payload is still there.
|
||||
; Frees the files the same way the guard does (close, then offer to end), deletes what
|
||||
; the uninstaller skipped, and on failure records which files survived and who holds
|
||||
; them in $HolderOutput, for the abort message. Sets $OldPayloadRemoved to 1 only when
|
||||
; every payload executable is gone and the DLL/Plugins purge is clean.
|
||||
Function RemoveOldPayloadLeftovers
|
||||
!insertmacro CockLog "old uninstall: removing leftovers of $OldInstallDir"
|
||||
StrCpy $OldPayloadRemoved 0
|
||||
StrCpy $HolderOutput "nothing could be named"
|
||||
StrCpy $GuardDir $OldInstallDir
|
||||
Call GuardAppsNotRunning
|
||||
; The purge helpers work on $INSTDIR; point them at the old directory for the
|
||||
; duration (on /R the two are the same anyway).
|
||||
Push $INSTDIR
|
||||
StrCpy $INSTDIR $OldInstallDir
|
||||
StrCpy $R2 0
|
||||
rop_loop:
|
||||
SetFileAttributes "$INSTDIR\cockatrice.exe" NORMAL
|
||||
SetFileAttributes "$INSTDIR\oracle.exe" NORMAL
|
||||
SetFileAttributes "$INSTDIR\servatrice.exe" NORMAL
|
||||
Delete "$INSTDIR\cockatrice.exe"
|
||||
Delete "$INSTDIR\oracle.exe"
|
||||
Delete "$INSTDIR\servatrice.exe"
|
||||
Delete "$INSTDIR\qt.conf"
|
||||
Call RetryDllPurge
|
||||
; $R0 is RetryDllPurge's verdict (1 = a DLL or the Plugins directory survived).
|
||||
; Every payload executable has to be gone as well: a locked oracle.exe or
|
||||
; servatrice.exe is just as fatal to the install that follows as cockatrice.exe.
|
||||
IfFileExists "$INSTDIR\cockatrice.exe" rop_retry
|
||||
IfFileExists "$INSTDIR\oracle.exe" rop_retry
|
||||
IfFileExists "$INSTDIR\servatrice.exe" rop_retry
|
||||
${If} $R0 == 0
|
||||
Goto rop_done
|
||||
${EndIf}
|
||||
rop_retry:
|
||||
IntOp $R2 $R2 + 1
|
||||
${If} $R2 < 3
|
||||
!insertmacro CockLog "old uninstall: leftovers of $OldInstallDir survived removal attempt $R2, retrying in 2 s"
|
||||
Sleep 2000
|
||||
Goto rop_loop
|
||||
${EndIf}
|
||||
!insertmacro CockLog "old uninstall: leftovers of $OldInstallDir still present after 3 removal attempts"
|
||||
!insertmacro CockLogListing "old-leftovers" "*.*"
|
||||
; Per-file Windows codes for the DLLs plus the holder probe's line ($HolderOutput).
|
||||
Call LogStaleRuntimeDiagnostics
|
||||
Pop $INSTDIR
|
||||
Return
|
||||
rop_done:
|
||||
StrCpy $OldPayloadRemoved 1
|
||||
; Only now that the recovery has succeeded: deleting the old uninstaller on a
|
||||
; failed attempt would leave a directory whose UninstallString points at nothing,
|
||||
; so no later run could remove it. WriteUninstaller recreates it on success anyway.
|
||||
SetFileAttributes "$INSTDIR\uninstall.exe" NORMAL
|
||||
Delete "$INSTDIR\uninstall.exe"
|
||||
!insertmacro CockLog "old uninstall: leftovers of $OldInstallDir removed"
|
||||
!insertmacro CockLogListing "old-leftovers-after-removal" "*.*"
|
||||
Pop $INSTDIR
|
||||
FunctionEnd
|
||||
|
||||
; Copies the old uninstaller's log ($TEMP\Cockatrice-uninstaller.log) into this
|
||||
; installer's log, so the report AbortInstall sends shows what the uninstaller did.
|
||||
; Uninstallers older than this log simply have no file; that is logged too.
|
||||
Function AppendUninstallerLog
|
||||
ClearErrors
|
||||
FileOpen $UninstLogHandle "$TEMP\Cockatrice-uninstaller.log" r
|
||||
${If} ${Errors}
|
||||
${OrIf} $UninstLogHandle == ""
|
||||
!insertmacro CockLog "uninstaller log: not found at $TEMP\Cockatrice-uninstaller.log"
|
||||
Return
|
||||
${EndIf}
|
||||
!insertmacro CockLog "uninstaller log begin ----------------------------------------"
|
||||
aul_loop:
|
||||
ClearErrors
|
||||
FileRead $UninstLogHandle $UninstLogLine
|
||||
IfErrors aul_done
|
||||
; Strip the line break FileRead keeps, so CockLog does not double it.
|
||||
aul_trim:
|
||||
StrCpy $R9 $UninstLogLine 1 -1
|
||||
${If} $R9 == "$\n"
|
||||
${OrIf} $R9 == "$\r"
|
||||
StrCpy $UninstLogLine $UninstLogLine -1
|
||||
Goto aul_trim
|
||||
${EndIf}
|
||||
!insertmacro CockLog "uninstaller: $UninstLogLine"
|
||||
Goto aul_loop
|
||||
aul_done:
|
||||
FileClose $UninstLogHandle
|
||||
!insertmacro CockLog "uninstaller log end ------------------------------------------"
|
||||
FunctionEnd
|
||||
|
||||
; BackupUserDataBeforeUninstall / RestoreUserDataAfterUninstall
|
||||
;
|
||||
; The uninstallers shipped in 3.1.0/3.1.1 run a blanket "RMDir /r $INSTDIR".
|
||||
; For a portable install that deletes $INSTDIR\data (decks, settings, pictures)
|
||||
; along with the payload, and for a normal install it deletes anything the user
|
||||
; pointed into $INSTDIR. The fixed uninstaller preserves foreign files, but the
|
||||
; destructive one is already on disk on affected machines, so stash a copy
|
||||
; outside $INSTDIR before invoking it and put it back afterwards. Best effort
|
||||
; only: a failed backup must never abort the install.
|
||||
;
|
||||
; The backup is left in %TEMP% afterwards on a best-effort basis only: the next
|
||||
; installer run that takes a new backup wipes it first (see below), so it is not
|
||||
; a durable second copy. Anything this backup does not cover - e.g. loose files
|
||||
; with other extensions - relies on the client's startup safety backup
|
||||
; (warnAboutDataPathsInProgramDir), which only exists after the first launch of
|
||||
; the new client.
|
||||
;
|
||||
; The 32-bit and 64-bit uninstall blocks below share $TEMP\CockatriceDataBackup.
|
||||
; When both are registered with different InstallLocations, backing up the second
|
||||
; directory into the first one's backup would restore the first dir's files into
|
||||
; the second one. BackupUserDataBeforeUninstall therefore starts over when
|
||||
; $OldInstallDir changes: the previous pass already restored its own backup, so
|
||||
; only its leftover %TEMP% copy is lost. RelocateRestoredDataToInstDir only sees
|
||||
; the last pass, so with two different old dirs plus a new target dir the first
|
||||
; dir's restored data stays in place instead of moving along - no loss, but the
|
||||
; user has to copy it manually.
|
||||
;
|
||||
; The portable-data branch below is defensive: both callers (AutoUninstallIfNeeded
|
||||
; on /R, componentsPagePre interactively) only run for non-portable installs, and
|
||||
; portable installs never write the registry uninstall keys that trigger them.
|
||||
; It fires only when $OldInstallDir nevertheless contains portable.dat - e.g. a
|
||||
; stale registry InstallLocation pointing at a portable dir, or portable.dat added
|
||||
; to a normal install dir. The loose-decks and install-subdirs branches are the
|
||||
; live ones for normal installs.
|
||||
Function BackupUserDataBeforeUninstall
|
||||
; $OldInstallDir is the directory being uninstalled: on the /R path it equals
|
||||
; $INSTDIR (updater passes /D=<appDir>), on the interactive path componentsPagePre
|
||||
; sets it from the registry InstallLocation via $GuardDir, because $INSTDIR is
|
||||
; still the default there (directory page comes later).
|
||||
${If} $OldInstallDir == ""
|
||||
StrCpy $OldInstallDir $INSTDIR
|
||||
${EndIf}
|
||||
; %TEMP% survives across installer runs, so a previous run's safety copy would
|
||||
; otherwise merge into this run: CopyFiles merges/overwrites, resurrecting files
|
||||
; the user deleted since (e.g. deck A from run 1 reappearing alongside deck B
|
||||
; from run 2) or restoring stale content after a failed copy. Wipe once per
|
||||
; installer run - $DataBackupDir is empty until the first backup of this run,
|
||||
; so the second call in sequence (32-bit then 64-bit uninstall) still merges.
|
||||
${If} $DataBackupDir == ""
|
||||
RMDir /r "$TEMP\CockatriceDataBackup"
|
||||
${EndIf}
|
||||
; The 32-bit and 64-bit uninstall blocks share one backup dir. When the second
|
||||
; block points at a different directory, merging would restore the first dir's
|
||||
; files into the second one, so start over instead. The previous pass already
|
||||
; restored its own backup; only its leftover %TEMP% copy is discarded.
|
||||
; StrCmp is case-sensitive while Windows paths are not, so a casing-only
|
||||
; difference triggers a harmless redundant re-backup of the same content.
|
||||
${If} $DataBackupDir != ""
|
||||
${AndIf} $DataBackupForDir != ""
|
||||
${AndIf} $OldInstallDir != $DataBackupForDir
|
||||
!insertmacro CockLog "pre-uninstall backup: old dir changed ($DataBackupForDir -> $OldInstallDir), starting a fresh backup"
|
||||
RMDir /r "$TEMP\CockatriceDataBackup"
|
||||
StrCpy $DataBackupDir ""
|
||||
StrCpy $DataBackupDone ""
|
||||
StrCpy $LooseDecksBackupDone ""
|
||||
StrCpy $InstallSubdirsBackupDone ""
|
||||
${EndIf}
|
||||
${If} ${FileExists} "$OldInstallDir\portable.dat"
|
||||
${AndIf} ${FileExists} "$OldInstallDir\data"
|
||||
StrCpy $DataBackupDir "$TEMP\CockatriceDataBackup"
|
||||
StrCpy $DataBackupDone "1"
|
||||
!insertmacro CockLog "pre-uninstall backup: copying $OldInstallDir\data (downloaded pics excluded)"
|
||||
DetailPrint "Backing up portable user data before removing previous version..."
|
||||
CreateDirectory "$DataBackupDir\data"
|
||||
; Copy every top-level entry except pics (downloaded pictures are large and
|
||||
; re-downloadable): copying everything first and deleting pics afterwards
|
||||
; would duplicate gigabytes into %TEMP% just to throw them away.
|
||||
FindFirst $DataBackupFindHandle $DataBackupFindName "$OldInstallDir\data\*.*"
|
||||
${If} $DataBackupFindHandle != ""
|
||||
backup_data_loop:
|
||||
${If} $DataBackupFindName != "."
|
||||
${AndIf} $DataBackupFindName != ".."
|
||||
${AndIf} $DataBackupFindName != "pics"
|
||||
${If} ${FileExists} "$OldInstallDir\data\$DataBackupFindName\*.*"
|
||||
CreateDirectory "$DataBackupDir\data\$DataBackupFindName"
|
||||
CopyFiles /SILENT "$OldInstallDir\data\$DataBackupFindName\*.*" "$DataBackupDir\data\$DataBackupFindName\"
|
||||
${Else}
|
||||
CopyFiles /SILENT "$OldInstallDir\data\$DataBackupFindName" "$DataBackupDir\data\"
|
||||
${EndIf}
|
||||
${EndIf}
|
||||
ClearErrors
|
||||
FindNext $DataBackupFindHandle $DataBackupFindName
|
||||
IfErrors 0 backup_data_loop
|
||||
FindClose $DataBackupFindHandle
|
||||
${EndIf}
|
||||
; Keep user-made custom pics (data\pics\CUSTOM).
|
||||
${If} ${FileExists} "$OldInstallDir\data\pics\CUSTOM"
|
||||
CreateDirectory "$DataBackupDir\data\pics\CUSTOM"
|
||||
CopyFiles /SILENT "$OldInstallDir\data\pics\CUSTOM\*.*" "$DataBackupDir\data\pics\CUSTOM\"
|
||||
${EndIf}
|
||||
DetailPrint "Portable user data backup complete."
|
||||
${EndIf}
|
||||
; Deck extensions mirror DeckLoader::ACCEPTED_FILE_EXTENSIONS, minus *.txt: a
|
||||
; blanket *.txt copy would also grab payload-adjacent text files, while the
|
||||
; deck-specific extensions are unambiguous user content.
|
||||
${If} ${FileExists} "$OldInstallDir\*.cod"
|
||||
${OrIf} ${FileExists} "$OldInstallDir\*.dec"
|
||||
${OrIf} ${FileExists} "$OldInstallDir\*.dek"
|
||||
${OrIf} ${FileExists} "$OldInstallDir\*.mwDeck"
|
||||
${If} $DataBackupDir == ""
|
||||
StrCpy $DataBackupDir "$TEMP\CockatriceDataBackup"
|
||||
${EndIf}
|
||||
StrCpy $LooseDecksBackupDone "1"
|
||||
!insertmacro CockLog "pre-uninstall backup: copying loose deck files"
|
||||
DetailPrint "Backing up deck files stored in the install directory..."
|
||||
CreateDirectory "$DataBackupDir\loose_decks"
|
||||
CopyFiles /SILENT "$OldInstallDir\*.cod" "$DataBackupDir\loose_decks\"
|
||||
CopyFiles /SILENT "$OldInstallDir\*.dec" "$DataBackupDir\loose_decks\"
|
||||
CopyFiles /SILENT "$OldInstallDir\*.dek" "$DataBackupDir\loose_decks\"
|
||||
CopyFiles /SILENT "$OldInstallDir\*.mwDeck" "$DataBackupDir\loose_decks\"
|
||||
${EndIf}
|
||||
; Data folders below the top level (e.g. $OldInstallDir\decks) are deleted by the
|
||||
; old "RMDir /r $INSTDIR" just like loose decks, and on a 3.1.1 -> new upgrade the
|
||||
; client's startup backup has never run yet, so stash every top-level directory
|
||||
; that is not part of the payload. Payload dirs stay in place: they are rewritten
|
||||
; by the section below, and restoring them could put stale DLLs next to the new
|
||||
; executables. "data" is skipped once the portable branch above already took it;
|
||||
; without portable.dat it is user content like any other unknown dir.
|
||||
FindFirst $DataBackupFindHandle $DataBackupFindName "$OldInstallDir\*.*"
|
||||
${If} $DataBackupFindHandle != ""
|
||||
backup_subdirs_loop:
|
||||
${If} $DataBackupFindName != "."
|
||||
${AndIf} $DataBackupFindName != ".."
|
||||
${AndIf} $DataBackupFindName != "plugins"
|
||||
${AndIf} $DataBackupFindName != "Plugins"
|
||||
${AndIf} $DataBackupFindName != "translations"
|
||||
${AndIf} $DataBackupFindName != "sounds"
|
||||
${AndIf} $DataBackupFindName != "themes"
|
||||
${If} $DataBackupFindName == "data"
|
||||
${AndIf} $DataBackupDone == "1"
|
||||
Goto backup_subdirs_next
|
||||
${EndIf}
|
||||
${If} ${FileExists} "$OldInstallDir\$DataBackupFindName\*.*"
|
||||
${If} $DataBackupDir == ""
|
||||
StrCpy $DataBackupDir "$TEMP\CockatriceDataBackup"
|
||||
${EndIf}
|
||||
StrCpy $InstallSubdirsBackupDone "1"
|
||||
!insertmacro CockLog "pre-uninstall backup: copying subdir $OldInstallDir\$DataBackupFindName"
|
||||
DetailPrint "Backing up $DataBackupFindName stored in the install directory..."
|
||||
CreateDirectory "$DataBackupDir\install_subdirs\$DataBackupFindName"
|
||||
CopyFiles /SILENT "$OldInstallDir\$DataBackupFindName\*.*" "$DataBackupDir\install_subdirs\$DataBackupFindName\"
|
||||
${EndIf}
|
||||
${EndIf}
|
||||
backup_subdirs_next:
|
||||
ClearErrors
|
||||
FindNext $DataBackupFindHandle $DataBackupFindName
|
||||
IfErrors 0 backup_subdirs_loop
|
||||
FindClose $DataBackupFindHandle
|
||||
${EndIf}
|
||||
; Remember which directory the current backup belongs to, so a later pass for a
|
||||
; different directory starts over instead of merging (see above).
|
||||
${If} $DataBackupDir != ""
|
||||
StrCpy $DataBackupForDir $OldInstallDir
|
||||
${EndIf}
|
||||
FunctionEnd
|
||||
|
||||
Function RestoreUserDataAfterUninstall
|
||||
StrCmp $DataBackupDir "" done
|
||||
${If} $OldInstallDir == ""
|
||||
StrCpy $OldInstallDir $INSTDIR
|
||||
${EndIf}
|
||||
${If} $DataBackupDone == "1"
|
||||
${AndIf} ${FileExists} "$DataBackupDir\data"
|
||||
${If} ${FileExists} "$OldInstallDir\data"
|
||||
; Existence-only: a partially-deleted data dir (locked files during the old
|
||||
; uninstall) looks the same as a survived one, so the backup is always kept.
|
||||
; Merge the backup back over it to repair partial deletes; CopyFiles only
|
||||
; adds/overwrites, so a fully-survived dir is unaffected.
|
||||
!insertmacro CockLog "post-uninstall restore: $OldInstallDir\data still exists, merging backup and keeping it"
|
||||
CopyFiles /SILENT "$DataBackupDir\data\*.*" "$OldInstallDir\data\"
|
||||
DetailPrint "Previous user data is still in place; pre-uninstall backup merged back and kept at $DataBackupDir\data."
|
||||
${Else}
|
||||
!insertmacro CockLog "post-uninstall restore: restoring $OldInstallDir\data"
|
||||
CreateDirectory "$OldInstallDir\data"
|
||||
CopyFiles /SILENT "$DataBackupDir\data\*.*" "$OldInstallDir\data\"
|
||||
DetailPrint "Restored portable user data from pre-uninstall backup."
|
||||
${EndIf}
|
||||
${EndIf}
|
||||
${If} $LooseDecksBackupDone == "1"
|
||||
${AndIf} ${FileExists} "$DataBackupDir\loose_decks"
|
||||
!insertmacro CockLog "post-uninstall restore: restoring loose deck files"
|
||||
CreateDirectory "$OldInstallDir"
|
||||
CopyFiles /SILENT "$DataBackupDir\loose_decks\*.cod" "$OldInstallDir\"
|
||||
CopyFiles /SILENT "$DataBackupDir\loose_decks\*.dec" "$OldInstallDir\"
|
||||
CopyFiles /SILENT "$DataBackupDir\loose_decks\*.dek" "$OldInstallDir\"
|
||||
CopyFiles /SILENT "$DataBackupDir\loose_decks\*.mwDeck" "$OldInstallDir\"
|
||||
DetailPrint "Restored deck files stored in the install directory."
|
||||
${EndIf}
|
||||
${If} $InstallSubdirsBackupDone == "1"
|
||||
${AndIf} ${FileExists} "$DataBackupDir\install_subdirs"
|
||||
!insertmacro CockLog "post-uninstall restore: restoring install subdirectories"
|
||||
CreateDirectory "$OldInstallDir"
|
||||
FindFirst $DataBackupFindHandle $DataBackupFindName "$DataBackupDir\install_subdirs\*.*"
|
||||
${If} $DataBackupFindHandle != ""
|
||||
restore_subdirs_loop:
|
||||
${If} $DataBackupFindName != "."
|
||||
${AndIf} $DataBackupFindName != ".."
|
||||
${AndIf} ${FileExists} "$DataBackupDir\install_subdirs\$DataBackupFindName\*.*"
|
||||
!insertmacro CockLog "post-uninstall restore: restoring subdir $OldInstallDir\$DataBackupFindName"
|
||||
CreateDirectory "$OldInstallDir\$DataBackupFindName"
|
||||
CopyFiles /SILENT "$DataBackupDir\install_subdirs\$DataBackupFindName\*.*" "$OldInstallDir\$DataBackupFindName\"
|
||||
${EndIf}
|
||||
ClearErrors
|
||||
FindNext $DataBackupFindHandle $DataBackupFindName
|
||||
IfErrors 0 restore_subdirs_loop
|
||||
FindClose $DataBackupFindHandle
|
||||
${EndIf}
|
||||
DetailPrint "Restored folders stored in the install directory."
|
||||
${EndIf}
|
||||
; Runs once per uninstall block, and there are two (32-bit, 64-bit): show the
|
||||
; notice a single time per installer run, covering both loose decks and
|
||||
; restored subdirectories.
|
||||
${IfNot} ${Silent}
|
||||
${AndIf} $LooseDeckNoticeShown != "1"
|
||||
${If} $LooseDecksBackupDone == "1"
|
||||
${OrIf} $InstallSubdirsBackupDone == "1"
|
||||
StrCpy $LooseDeckNoticeShown "1"
|
||||
MessageBox MB_OK|MB_ICONINFORMATION "Your decks or data folders were stored inside the Cockatrice install folder ($OldInstallDir), which the previous version's uninstaller deletes.$\n$\nThey have been restored from a backup to that folder (loose deck files ending in .txt are not backed up - move those manually). If you install to a different folder, they are moved there automatically. Please move them out of the install folder afterwards (Settings -> General -> Paths) so a future uninstall cannot delete them again."
|
||||
${EndIf}
|
||||
${EndIf}
|
||||
done:
|
||||
FunctionEnd
|
||||
|
||||
; RelocateRestoredDataToInstDir
|
||||
;
|
||||
; componentsPagePre backs up and restores around the old uninstaller before the
|
||||
; directory page, so $OldInstallDir (registry InstallLocation) is authoritative
|
||||
; there while $INSTDIR is still the default. When the user then picks a different
|
||||
; target directory, the restored user data would otherwise stay orphaned in the
|
||||
; old directory while the new install starts empty. Copy it across when they differ
|
||||
; (a copy, not a move: the source is user data and is deliberately left in place).
|
||||
; Only the last uninstall pass's $OldInstallDir is relocated (see the shared-backup
|
||||
; comment above); with two different old dirs the first one's data stays restored
|
||||
; in place instead of moving along.
|
||||
; StrCmp is case-sensitive while Windows paths are not; a casing-only difference
|
||||
; triggers a harmless redundant copy onto itself.
|
||||
Function RelocateRestoredDataToInstDir
|
||||
StrCmp $DataBackupDir "" done
|
||||
StrCmp $OldInstallDir "" done
|
||||
StrCmp $OldInstallDir $INSTDIR done
|
||||
${If} $DataBackupDone == "1"
|
||||
${AndIf} ${FileExists} "$OldInstallDir\data"
|
||||
!insertmacro CockLog "relocate: copying restored data $OldInstallDir\data -> $INSTDIR\data"
|
||||
CreateDirectory "$INSTDIR\data"
|
||||
CopyFiles /SILENT "$OldInstallDir\data\*.*" "$INSTDIR\data\"
|
||||
DetailPrint "Copied restored user data to the new install folder."
|
||||
${EndIf}
|
||||
${If} $LooseDecksBackupDone == "1"
|
||||
${If} ${FileExists} "$OldInstallDir\*.cod"
|
||||
${OrIf} ${FileExists} "$OldInstallDir\*.dec"
|
||||
${OrIf} ${FileExists} "$OldInstallDir\*.dek"
|
||||
${OrIf} ${FileExists} "$OldInstallDir\*.mwDeck"
|
||||
!insertmacro CockLog "relocate: copying restored decks $OldInstallDir -> $INSTDIR"
|
||||
CreateDirectory "$INSTDIR"
|
||||
CopyFiles /SILENT "$OldInstallDir\*.cod" "$INSTDIR\"
|
||||
CopyFiles /SILENT "$OldInstallDir\*.dec" "$INSTDIR\"
|
||||
CopyFiles /SILENT "$OldInstallDir\*.dek" "$INSTDIR\"
|
||||
CopyFiles /SILENT "$OldInstallDir\*.mwDeck" "$INSTDIR\"
|
||||
DetailPrint "Copied restored deck files to the new install folder."
|
||||
${EndIf}
|
||||
${EndIf}
|
||||
${If} $InstallSubdirsBackupDone == "1"
|
||||
${AndIf} ${FileExists} "$DataBackupDir\install_subdirs"
|
||||
FindFirst $DataBackupFindHandle $DataBackupFindName "$DataBackupDir\install_subdirs\*.*"
|
||||
${If} $DataBackupFindHandle != ""
|
||||
relocate_subdirs_loop:
|
||||
${If} $DataBackupFindName != "."
|
||||
${AndIf} $DataBackupFindName != ".."
|
||||
${AndIf} ${FileExists} "$OldInstallDir\$DataBackupFindName\*.*"
|
||||
!insertmacro CockLog "relocate: copying restored subdir $OldInstallDir\$DataBackupFindName -> $INSTDIR\$DataBackupFindName"
|
||||
CreateDirectory "$INSTDIR\$DataBackupFindName"
|
||||
CopyFiles /SILENT "$OldInstallDir\$DataBackupFindName\*.*" "$INSTDIR\$DataBackupFindName\"
|
||||
DetailPrint "Copied restored folder $DataBackupFindName to the new install folder."
|
||||
${EndIf}
|
||||
ClearErrors
|
||||
FindNext $DataBackupFindHandle $DataBackupFindName
|
||||
IfErrors 0 relocate_subdirs_loop
|
||||
FindClose $DataBackupFindHandle
|
||||
${EndIf}
|
||||
${EndIf}
|
||||
done:
|
||||
FunctionEnd
|
||||
Loading…
Add table
Add a link
Reference in a new issue