mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-11-05 09:33:25 -08:00
[Installer][Client] Back up user data before old uninstallers can delete it (#7503)
* [Installer][Client] Back up user data before old uninstallers can delete it * [Installer][Client] Fix stale backup resurrection, batch session backups, extend coverage NSIS: clear stale %TEMP% backup and only arm restore when something was copied, directory-existence checks, back up loose .dec decks, drop downloaded pics but keep pics\CUSTOM. Helper: single session dir per run (ms timestamps), same-basename disambiguation, partial copies still report their location. * [Installer][Client] Fix review findings: throttle backups, NSIS old dir, portable messaging * [Installer][Client] Fix review findings: backup markers, NSIS relocate, size cap * [Installer][Client] Fix review findings: sounds, session coverage, NSIS wording Client: back up data/sounds in portable mode and mention it in the warning; reuse a recent session only when complete and covering the current sources (source manifest), estimate size only for fresh copies, normalize dedup via isInsideDir. Helper: source manifest per session with sessionCoversSources(), clear out-params on all failure paths. NSIS: document defensive portable branch, merge backup over surviving data/ to repair partial deletes, copy (not move) wording in relocate, note .txt exclusion in deck notice. * [Installer][Client] Fix review findings: stale backup, empty reuse, relocate, portable warning NSIS: wipe TEMP backup once per run to avoid resurrecting deleted files, check data dir existence in relocate so dir-only trees are moved. Client: never reuse a recent session for an empty source set, only fall back to a covering session on copy failure, warn fresh portable installs instead of staying silent. * [Installer][Client] Fix review findings: usable session reuse, loose decks, NSIS pics, symlinks * [Installer][Client] Fix portable backup exclusions in offender merge * [Client] Split at-risk data warning into focused helpers Ricky's nit on the 200+ line warnAboutDataPathsInProgramDir: extract collection (portable/installed), backup, message building and dialog into named helpers. No behavior change, all tr() strings untouched. * [Client] Read path settings inside isPortableBackupExcluded * [Client] Make stateless at-risk helpers static * [Client] Extract at-risk data warning out of MainWindow * [Installer][Client] Fix review findings: subdir backup, portable warning, backup API NSIS: - Back up unknown top-level install subdirs (e.g. <installdir>\decks), not just loose deck files, so the 3.1.1 -> new upgrade no longer loses data folders the old RMDir /r deletes before the client ever runs. Payload dirs (plugins/translations/sounds/themes) stay in place to avoid restoring stale DLLs; restore + relocate handle the new backup set and the one-time notice covers both decks and folders. - Restart the %TEMP% backup when the 32/64-bit passes point at different dirs instead of merging (which restored one dir's files into the other); document that only the last pass relocates and the %TEMP% copy is not a durable second copy. Client: - Drop the portable at-risk warning: portable installs never shipped an uninstaller and portable updates preserve data/, so every portable user got a spurious dialog plus daily host-machine copies. - AtRiskDataWarning is now a plain class with Q_DECLARE_TR_FUNCTIONS instead of QObject+Q_OBJECT; logging category renamed to at_risk_data_warning; MainWindow wrapper inlined into startupConfigCheck. - Lower the synchronous startup backup cap 500 -> 200 MB. Utility/tests: - Remove test-only backupAtRiskPath()/findRecentBackupSession() and the no-marker/no-manifest legacy paths; isBackupSessionComplete() now requires the complete marker; tests updated to the plural API. * [Client] Restore collectInstalledAtRiskPaths per reviewa23e02b5removed the definition along with the portable helpers, breaking the build (undefined reference at link time, and isInsideAnyDir unused under -Werror on Arch/Debian 13). Restores the202c0d65version (pathsInsideDir plus loose top-level deck files) as suggested. --------- Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
This commit is contained in:
parent
711386574d
commit
6c660d6b21
11 changed files with 1734 additions and 43 deletions
|
|
@ -15,8 +15,17 @@ Var ReinstallMode
|
|||
Var AllowForceClose
|
||||
Var GuardDir
|
||||
Var OldUninstallString
|
||||
Var OldInstallDir
|
||||
Var DataBackupDir
|
||||
Var DataBackupDone
|
||||
Var DataBackupForDir
|
||||
Var LooseDecksBackupDone
|
||||
Var InstallSubdirsBackupDone
|
||||
Var LooseDeckNoticeShown
|
||||
Var StaleFindHandle
|
||||
Var StaleFindName
|
||||
Var DataBackupFindHandle
|
||||
Var DataBackupFindName
|
||||
Var IsUpgrade
|
||||
Var AbortReason
|
||||
Var WaitPid
|
||||
|
|
@ -125,6 +134,17 @@ FunctionEnd
|
|||
Function .onInit
|
||||
|
||||
StrCpy $InstallLogPath "$TEMP\Cockatrice-installer.log"
|
||||
; No backup has been taken yet; RestoreUserDataAfterUninstall must be a no-op
|
||||
; until BackupUserDataBeforeUninstall sets this after actually copying something.
|
||||
StrCpy $DataBackupDir ""
|
||||
StrCpy $DataBackupDone ""
|
||||
StrCpy $DataBackupForDir ""
|
||||
StrCpy $LooseDecksBackupDone ""
|
||||
StrCpy $InstallSubdirsBackupDone ""
|
||||
StrCpy $LooseDeckNoticeShown ""
|
||||
; No old install location resolved yet; Backup/Restore fall back to $INSTDIR
|
||||
; when this is empty (the /R path where $INSTDIR already is the old dir).
|
||||
StrCpy $OldInstallDir ""
|
||||
; Start each run from a clean file so the log that gets sent back is the last
|
||||
; run, not every past run glued together.
|
||||
FileOpen $LogHandle "$InstallLogPath" w
|
||||
|
|
@ -314,10 +334,15 @@ SetShellVarContext all
|
|||
SetRegView 32
|
||||
ReadRegStr $R0 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "QuietUninstallString"
|
||||
|
||||
!insertmacro CockLog "uninstall (32-bit): '$R0 _?=$INSTDIR'"
|
||||
; On the /R path the updater passes /D=<appDir>, so $INSTDIR already is the old
|
||||
; install dir. Publish it for the backup/restore pair below.
|
||||
StrCpy $OldInstallDir $INSTDIR
|
||||
!insertmacro CockLog "uninstall (32-bit): '$R0 _?=$OldInstallDir'"
|
||||
StrCmp $R0 "" done32
|
||||
DetailPrint "Removing previous version (32-bit)..."
|
||||
ExecWait '$R0 _?=$INSTDIR'
|
||||
Call BackupUserDataBeforeUninstall
|
||||
ExecWait '$R0 _?=$OldInstallDir'
|
||||
Call RestoreUserDataAfterUninstall
|
||||
|
||||
done32:
|
||||
|
||||
|
|
@ -326,16 +351,312 @@ ${If} ${RunningX64}
|
|||
SetRegView 64
|
||||
ReadRegStr $R0 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "QuietUninstallString"
|
||||
|
||||
!insertmacro CockLog "uninstall (64-bit): '$R0 _?=$INSTDIR'"
|
||||
StrCpy $OldInstallDir $INSTDIR
|
||||
!insertmacro CockLog "uninstall (64-bit): '$R0 _?=$OldInstallDir'"
|
||||
StrCmp $R0 "" done64
|
||||
DetailPrint "Removing previous version (64-bit)..."
|
||||
ExecWait '$R0 _?=$INSTDIR'
|
||||
Call BackupUserDataBeforeUninstall
|
||||
ExecWait '$R0 _?=$OldInstallDir'
|
||||
Call RestoreUserDataAfterUninstall
|
||||
|
||||
done64:
|
||||
${EndIf}
|
||||
|
||||
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
|
||||
|
||||
; --- Running instance handling ---
|
||||
; Cockatrice, Oracle and Servatrice must not be running while files are
|
||||
; replaced or deleted. A still-running process holds locks on its .exe and
|
||||
|
|
@ -1037,11 +1358,17 @@ ${If} $PortableMode = 0
|
|||
; instance of the application is still running out of the *old* install
|
||||
; directory before letting it delete anything. The uninstall string is
|
||||
; stashed away first, the check works on $R0.
|
||||
; $INSTDIR is still the default here (directory page comes later), so the
|
||||
; backup/restore dir comes from the registry InstallLocation via $GuardDir,
|
||||
; not from $INSTDIR - otherwise a custom-location install is unprotected.
|
||||
StrCpy $OldUninstallString $R0
|
||||
!insertmacro CockLog "componentsPagePre: old 32-bit uninstall '$OldUninstallString _?=$INSTDIR'"
|
||||
Call EnsureOldInstallNotRunning
|
||||
StrCpy $OldInstallDir $GuardDir
|
||||
!insertmacro CockLog "componentsPagePre: old 32-bit uninstall '$OldUninstallString _?=$OldInstallDir' (oldDir=$OldInstallDir InstDir=$INSTDIR)"
|
||||
ClearErrors
|
||||
ExecWait "$OldUninstallString _?=$INSTDIR"
|
||||
Call BackupUserDataBeforeUninstall
|
||||
ExecWait "$OldUninstallString _?=$OldInstallDir"
|
||||
Call RestoreUserDataAfterUninstall
|
||||
|
||||
done32:
|
||||
|
||||
|
|
@ -1061,10 +1388,13 @@ ${If} $PortableMode = 0
|
|||
|
||||
uninst64:
|
||||
StrCpy $OldUninstallString $R0
|
||||
!insertmacro CockLog "componentsPagePre: old 64-bit uninstall '$OldUninstallString _?=$INSTDIR'"
|
||||
Call EnsureOldInstallNotRunning
|
||||
StrCpy $OldInstallDir $GuardDir
|
||||
!insertmacro CockLog "componentsPagePre: old 64-bit uninstall '$OldUninstallString _?=$OldInstallDir' (oldDir=$OldInstallDir InstDir=$INSTDIR)"
|
||||
ClearErrors
|
||||
ExecWait "$OldUninstallString _?=$INSTDIR"
|
||||
Call BackupUserDataBeforeUninstall
|
||||
ExecWait "$OldUninstallString _?=$OldInstallDir"
|
||||
Call RestoreUserDataAfterUninstall
|
||||
|
||||
done64:
|
||||
${EndIf}
|
||||
|
|
@ -1188,6 +1518,11 @@ ${EndIf}
|
|||
!insertmacro CockLog "section: writing payload to $INSTDIR"
|
||||
@CPACK_NSIS_FULL_INSTALL@
|
||||
!insertmacro CockLog "section: payload written"
|
||||
; The pre-uninstall backup/restore above runs before the directory page, so a
|
||||
; user picking a different target folder would otherwise leave the restored data
|
||||
; orphaned in the old directory. Move it across when they differ (no-op on the
|
||||
; /R path where they are equal).
|
||||
Call RelocateRestoredDataToInstDir
|
||||
!insertmacro CockLogListing "final" "Qt6*.dll"
|
||||
|
||||
${If} $PortableMode = 0
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue