diff --git a/Source/Core/DolphinQt/Updater.cpp b/Source/Core/DolphinQt/Updater.cpp index fa13d9a961..88294c12ff 100644 --- a/Source/Core/DolphinQt/Updater.cpp +++ b/Source/Core/DolphinQt/Updater.cpp @@ -17,6 +17,8 @@ #include "DolphinQt/QtUtils/RunOnObject.h" #include "DolphinQt/Settings.h" +// Refer to docs/autoupdate_overview.md for a detailed overview of the autoupdate process + Updater::Updater(QWidget* parent) : m_parent(parent) { connect(this, &QThread::finished, this, &QObject::deleteLater); diff --git a/Source/Core/DolphinQt/Updater.h b/Source/Core/DolphinQt/Updater.h index 462424ea89..13125e2ce4 100644 --- a/Source/Core/DolphinQt/Updater.h +++ b/Source/Core/DolphinQt/Updater.h @@ -8,6 +8,8 @@ #include "UICommon/AutoUpdate.h" +// Refer to docs/autoupdate_overview.md for a detailed overview of the autoupdate process + class QWidget; class Updater : public QThread, public AutoUpdateChecker diff --git a/Source/Core/MacUpdater/AppDelegate.mm b/Source/Core/MacUpdater/AppDelegate.mm index 95b03322a0..a61ebdc7d0 100644 --- a/Source/Core/MacUpdater/AppDelegate.mm +++ b/Source/Core/MacUpdater/AppDelegate.mm @@ -10,13 +10,16 @@ #include #include +// Refer to docs/autoupdate_overview.md for a detailed overview of the autoupdate process + @interface AppDelegate () @end @implementation AppDelegate -- (void)applicationDidFinishLaunching:(NSNotification*)aNotification { +- (void)applicationDidFinishLaunching:(NSNotification*)aNotification +{ NSArray* arguments = [[NSProcessInfo processInfo] arguments]; __block std::vector args; @@ -32,7 +35,8 @@ }); } -- (void)applicationWillTerminate:(NSNotification*)aNotification { +- (void)applicationWillTerminate:(NSNotification*)aNotification +{ } @end diff --git a/Source/Core/MacUpdater/main.m b/Source/Core/MacUpdater/main.m index b2e372139f..f027f1b00d 100644 --- a/Source/Core/MacUpdater/main.m +++ b/Source/Core/MacUpdater/main.m @@ -4,6 +4,8 @@ #include +// Refer to docs/autoupdate_overview.md for a detailed overview of the autoupdate process + int main(int argc, const char** argv) { if (argc == 1) diff --git a/Source/Core/UICommon/AutoUpdate.cpp b/Source/Core/UICommon/AutoUpdate.cpp index 5ad5e38495..846c9deb0f 100644 --- a/Source/Core/UICommon/AutoUpdate.cpp +++ b/Source/Core/UICommon/AutoUpdate.cpp @@ -29,6 +29,8 @@ #define OS_SUPPORTS_UPDATER #endif +// Refer to docs/autoupdate_overview.md for a detailed overview of the autoupdate process + namespace { bool s_update_triggered = false; diff --git a/Source/Core/UICommon/AutoUpdate.h b/Source/Core/UICommon/AutoUpdate.h index 7c75100433..a712584db8 100644 --- a/Source/Core/UICommon/AutoUpdate.h +++ b/Source/Core/UICommon/AutoUpdate.h @@ -6,6 +6,8 @@ #include +// Refer to docs/autoupdate_overview.md for a detailed overview of the autoupdate process + // This class defines all the logic for Dolphin auto-update checking. UI-specific elements have to // be defined in a backend specific subclass. class AutoUpdateChecker diff --git a/Source/Core/UpdaterCommon/UpdaterCommon.cpp b/Source/Core/UpdaterCommon/UpdaterCommon.cpp index 7eadf14a19..f52755aeb5 100644 --- a/Source/Core/UpdaterCommon/UpdaterCommon.cpp +++ b/Source/Core/UpdaterCommon/UpdaterCommon.cpp @@ -25,6 +25,8 @@ #include #endif +// Refer to docs/autoupdate_overview.md for a detailed overview of the autoupdate process + namespace { // Where to log updater output. diff --git a/Source/Core/UpdaterCommon/UpdaterCommon.h b/Source/Core/UpdaterCommon/UpdaterCommon.h index 53f37c2424..53fe9a145e 100644 --- a/Source/Core/UpdaterCommon/UpdaterCommon.h +++ b/Source/Core/UpdaterCommon/UpdaterCommon.h @@ -13,4 +13,6 @@ #include "Common/CommonTypes.h" +// Refer to docs/autoupdate_overview.md for a detailed overview of the autoupdate process + bool RunUpdater(std::vector args); diff --git a/Source/Core/WinUpdater/Main.cpp b/Source/Core/WinUpdater/Main.cpp index 567486f00a..910a4cfd10 100644 --- a/Source/Core/WinUpdater/Main.cpp +++ b/Source/Core/WinUpdater/Main.cpp @@ -16,6 +16,8 @@ #include "UpdaterCommon/UI.h" #include "UpdaterCommon/UpdaterCommon.h" +// Refer to docs/autoupdate_overview.md for a detailed overview of the autoupdate process + int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) { if (lstrlenW(pCmdLine) == 0) diff --git a/docs/autoupdate_overview.md b/docs/autoupdate_overview.md new file mode 100644 index 0000000000..2335e869ea --- /dev/null +++ b/docs/autoupdate_overview.md @@ -0,0 +1,93 @@ +# AutoUpdate Overview +Dolphin's autoupdate procedure is spread through a number of files; this overview describes the +update flow. + +## General notes: +* The updater is only supported on Windows and MacOS. +* There are four update frequency tracks: Dev (updated every commit), Beta (a few times a year), + Stable (very rarely), and Disabled. +* Applications can't overwrite themselves so a separate application is responsible for actually + updating the Dolphin executable and other files. +* The updater application needs to be able to also update itself, so it creates a copy which + updates both Dolphin and the original updater. Every time Dolphin launches it deletes the + updater copy (if it exists). + +## Class and file responsibilities: +* AutoUpdateChecker (UICommon/AutoUpdate.h): Checks if an update is available. + * Verifies the updater is supported on the user's platform and hasn't been disabled. + * Retrieves new version information from the update server. + * Copies the updater application and launches the copy. +* Updater (DolphinQt/Updater.h): Serves as the interface between AutoUpdateChecker and Qt. + * Spawns a background thread when Dolphin launches that calls AutoUpdateChecker. + * Creates the update prompt window when an update is available. + * If the user wants to update now, closes Dolphin. +* MacUpdater/main.m and MacUpdater/AppDelegate.mm: Entry point to MacOS updater. + * Converts command line arguments to vector\ and passes them to + UpdaterCommon::RunUpdate(). +* WinUpdater/main.cpp: Entry point to Windows updater. + * Converts command line arguments to vector\. + * Checks if updater has write access to the Dolphin executable directory. If not, attempts + to relaunch itself with admin privileges (creating a User Account Control prompt). + * Passes argument vector to UpdaterCommon::RunUpdate(). +* UpdaterCommon/UpdaterCommon.cpp: Performs the actual update process. + * Manages updater UI. + * Fetches file manifests from update server for current and new versions. + * Calculates file diff between versions. + * Downloads and adds/replaces changed files and deletes removed files. + * Verifies file hashes. + * If the user updated immediately (rather than waiting for Dolphin to close before starting + the update), starts Dolphin again when the update is complete. + +## Update flow: +* An update check is started in one of two ways: + * When Dolphin is launched (unless in NoGUI or batch mode): + * In main.cpp an instance of Updater is created and invokes start(), which is inherited + from QThread and creates a new thread which performs the check off the main thread. + * QThread::start() calls run() which is overridden in Updater and calls + AutoUpdateChecker::CheckForUpdate(). + * When the user selects Help -> "Check for Updates..." in the main menu: + * The menu option runs a callback to MenuBar::InstallUpdateManually(). + * The Config value for the update track is backed up to a temporary variable, then + overwritten with "dev" to force a check for the latest version. After the check + completes the original Config value is restored. + * Updater::CheckForUpdate() is called, which calls AutoUpdateChecker::CheckForUpdate(). +* AutoUpdateChecker::CheckForUpdate() checks if the selected track has a new version available. + * If not the check ends. If the user started it, returns to Updater::CheckForUpdate() which + tells the user they're up to date. +* Information about the update is passed to OnUpdateAvailable(), which is overridden by Updater. +* OnUpdateAvailable() creates a window displaying the update changelog and asks the user if they + want to update now, update after Dolphin closes, not update, or never auto-update. +* If the user wants to update AutoUpdateChecker::TriggerUpdate() is called. +* TriggerUpdate() builds the command line arguments for the updater process, creates a copy of + the updater executable, then runs the copy in a new process. +* TriggerUpdate() returns to OnUpdateAvailable(). If the user chose to update now, Dolphin's + main window is closed which results in the Dolphin process ending. +* The updater process begins. + * On MacOS (starts main() in MacUpdater/main.m): + * Checks that the process received command line arguments. If not it tells the user the + updater can't be launched directly and quits. + * Calls NSApplicationMain(), which passes control to the AppDelegate defined in + MacUpdater/AppDelegate.mm. + * The command line arguments are converted to a vector\ + and passed to RunUpdater() in UpdaterCommon.h. + * On Windows (starts wWinMain() in WinUpdater/Main.cpp): + * Checks that the process received command line arguments. If not it tells the user the + updater can't be launched directly and quits. + * Attempts to open Updater.log in the same directory as Dolphin.exe. If this fails, + checks to see if the process has admin privileges. + * If not, attempts to relaunch the updater as admin. This will spawn a User Account + Control prompt. + * If the user declines the UAC prompt, or if the updater already has admin status, + the update aborts. + * Converts the command line arguments to a vector\ and passes them to RunUpdater() + in UpdaterCommon.h. +* RunUpdater() parses and validates the command line arguments, hides the updater UI, then waits + for the Dolphin process to quit. +* RunUpdater() begins the actual update. + * Fetches file manifests from update server for current and new versions. + * Decompresses manifests and verifies their signatures. + * Downloads and adds/replaces changed files and deletes removed files. + * Verifies downloaded files match manifest hashes. +* If the user updated immediately (rather than waiting for Dolphin to close before starting + the update), Dolphin restarts. +* As part of Dolphin's normal startup process, the Updater copy is deleted.