Duckstation/src/updater/cocoa_main.mm

133 lines
3.8 KiB
Plaintext

// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
#include "cocoa_progress_callback.h"
#include "updater.h"
#include "common/file_system.h"
#include "common/log.h"
#include "common/path.h"
#include "common/scoped_guard.h"
#include "common/string_util.h"
#include "common/timer.h"
#include <cstdlib>
#include <thread>
static void LaunchApplication(const char* path)
{
@autoreleasepool
{
NSTask* task = [[[NSTask alloc] init] autorelease];
[task setLaunchPath:[NSString stringWithUTF8String:path]];
[task launch];
}
}
int main(int argc, char* argv[])
{
[NSApplication sharedApplication];
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
// Needed for keyboard in put.
const ProcessSerialNumber psn = {0, kCurrentProcess};
TransformProcessType(&psn, kProcessTransformToForegroundApplication);
CocoaProgressCallback progress;
if (argc != 4)
{
progress.ModalError("Expected 3 arguments: update zip, staging directory, output directory.\n\nThis program is not "
"intended to be run manually, please use the Qt frontend and click Help->Check for Updates.");
return EXIT_FAILURE;
}
std::string zip_path = argv[1];
std::string staging_directory = argv[2];
std::string destination_directory = argv[3];
if (zip_path.empty() || staging_directory.empty() || destination_directory.empty())
{
progress.ModalError("One or more parameters is empty.");
return EXIT_FAILURE;
}
if (const char* home_dir = getenv("HOME"))
{
static constexpr char log_file[] = "Library/Application Support/DuckStation/updater.log";
std::string log_path = Path::Combine(home_dir, log_file);
Log::SetFileOutputParams(true, log_path.c_str());
}
std::string program_to_launch = Path::Combine(destination_directory, "Contents/MacOS/DuckStation");
int result = EXIT_SUCCESS;
std::thread worker([&progress, zip_path = std::move(zip_path),
destination_directory = std::move(destination_directory),
staging_directory = std::move(staging_directory), &result]() {
ScopedGuard app_stopper([]() { dispatch_async(dispatch_get_main_queue(), []() { [NSApp stop:nil]; }); });
Updater updater(&progress);
if (!updater.Initialize(std::move(staging_directory), std::move(destination_directory)))
{
progress.ModalError("Failed to initialize updater.");
result = EXIT_FAILURE;
return;
}
if (!updater.OpenUpdateZip(zip_path.c_str()))
{
progress.FormatModalError("Could not open update zip '{}'. Update not installed.", zip_path);
result = EXIT_FAILURE;
return;
}
if (!updater.PrepareStagingDirectory())
{
progress.ModalError("Failed to prepare staging directory. Update not installed.");
result = EXIT_FAILURE;
return;
}
if (!updater.StageUpdate())
{
progress.ModalError("Failed to stage update. Update not installed.");
result = EXIT_FAILURE;
return;
}
if (!updater.ClearDestinationDirectory())
{
progress.ModalError("Failed to clear destination directory. Your installation may be corrupted, please "
"re-download a fresh version from GitHub.");
result = EXIT_FAILURE;
return;
}
if (!updater.CommitUpdate())
{
progress.ModalError(
"Failed to commit update. Your installation may be corrupted, please re-download a fresh version from GitHub.");
result = EXIT_FAILURE;
return;
}
updater.CleanupStagingDirectory();
updater.RemoveUpdateZip();
result = EXIT_SUCCESS;
});
[NSApp run];
worker.join();
if (result == EXIT_SUCCESS)
{
progress.FormatInformation("Launching '{}'...", program_to_launch);
LaunchApplication(program_to_launch.c_str());
}
return result;
}