fix: Implement two-stage update system to handle file-in-use errors

Replace direct file overwriting with a staging-based update mechanism to
resolve "file in use" errors during self-updates.

**Changes:**
- Stage updates to temporary directory instead of direct installation
- Apply staged updates on next application startup (before files are loaded)
- Add static methods for startup update detection and application
- Create update manifest to track staged update metadata
- Backup existing files before applying updates
- Update UI messaging to reflect staged update workflow

**Problem solved:**
The previous direct file replacement approach failed when trying to overwrite
DLLs and executables that were loaded into memory by the running process.
This two-stage approach stages files safely, then applies them on restart
when no files are in use.

**Workflow:**
1. Download and extract update to staging directory
2. Create manifest with update metadata
3. Show "Update ready" message to user
4. On next app startup: detect staged update, apply it, show success message
5. Clean up staging directory after successful application

This ensures reliable self-updates without file access conflicts.

Signed-off-by: Zephyron <zephyron@citron-emu.org>
This commit is contained in:
Zephyron
2025-07-11 17:01:37 +10:00
parent 5472256a2d
commit 00c3e7aea5
4 changed files with 142 additions and 16 deletions

View File

@@ -162,6 +162,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
#include "citron/startup_checks.h"
#include "citron/uisettings.h"
#include "citron/updater/updater_dialog.h"
#include "citron/updater/updater_service.h"
#include "citron/util/clickable_label.h"
#include "citron/vk_device_info.h"
@@ -5321,6 +5322,16 @@ int main(int argc, char* argv[]) {
QApplication app(argc, argv);
// Check for and apply staged updates before starting the main application
std::filesystem::path app_dir = std::filesystem::path(QCoreApplication::applicationDirPath().toStdString());
if (Updater::UpdaterService::HasStagedUpdate(app_dir)) {
if (Updater::UpdaterService::ApplyStagedUpdate(app_dir)) {
// Show a simple message that update was applied
QMessageBox::information(nullptr, QObject::tr("Update Applied"),
QObject::tr("Citron has been updated successfully!"));
}
}
#ifdef _WIN32
OverrideWindowsFont();
#endif