1 // Copyright 2017 Dolphin Emulator Project
2 // Licensed under GPLv2+
3 // Refer to the license.txt file included.
4 
5 #include "DolphinQt/WiiUpdate.h"
6 
7 #include <cinttypes>
8 #include <future>
9 
10 #include <QCloseEvent>
11 #include <QObject>
12 #include <QProgressDialog>
13 #include <QPushButton>
14 
15 #include "Common/FileUtil.h"
16 #include "Common/Flag.h"
17 
18 #include "Core/Core.h"
19 #include "Core/WiiUtils.h"
20 
21 #include "DiscIO/NANDImporter.h"
22 
23 #include "DolphinQt/QtUtils/ModalMessageBox.h"
24 #include "DolphinQt/QtUtils/QueueOnObject.h"
25 
26 namespace WiiUpdate
27 {
ShowResult(QWidget * parent,WiiUtils::UpdateResult result)28 static void ShowResult(QWidget* parent, WiiUtils::UpdateResult result)
29 {
30   switch (result)
31   {
32   case WiiUtils::UpdateResult::Succeeded:
33     ModalMessageBox::information(parent, QObject::tr("Update completed"),
34                                  QObject::tr("The emulated Wii console has been updated."));
35     DiscIO::NANDImporter().ExtractCertificates(File::GetUserPath(D_WIIROOT_IDX));
36     break;
37   case WiiUtils::UpdateResult::AlreadyUpToDate:
38     ModalMessageBox::information(parent, QObject::tr("Update completed"),
39                                  QObject::tr("The emulated Wii console is already up-to-date."));
40     DiscIO::NANDImporter().ExtractCertificates(File::GetUserPath(D_WIIROOT_IDX));
41     break;
42   case WiiUtils::UpdateResult::ServerFailed:
43     ModalMessageBox::critical(parent, QObject::tr("Update failed"),
44                               QObject::tr("Could not download update information from Nintendo. "
45                                           "Please check your Internet connection and try again."));
46     break;
47   case WiiUtils::UpdateResult::DownloadFailed:
48     ModalMessageBox::critical(parent, QObject::tr("Update failed"),
49                               QObject::tr("Could not download update files from Nintendo. "
50                                           "Please check your Internet connection and try again."));
51     break;
52   case WiiUtils::UpdateResult::ImportFailed:
53     ModalMessageBox::critical(parent, QObject::tr("Update failed"),
54                               QObject::tr("Could not install an update to the Wii system memory. "
55                                           "Please refer to logs for more information."));
56     break;
57   case WiiUtils::UpdateResult::Cancelled:
58     ModalMessageBox::warning(
59         parent, QObject::tr("Update cancelled"),
60         QObject::tr("The update has been cancelled. It is strongly recommended to "
61                     "finish it in order to avoid inconsistent system software versions."));
62     break;
63   case WiiUtils::UpdateResult::RegionMismatch:
64     ModalMessageBox::critical(
65         parent, QObject::tr("Update failed"),
66         QObject::tr("The game's region does not match your console's. "
67                     "To avoid issues with the system menu, it is not possible "
68                     "to update the emulated console using this disc."));
69     break;
70   case WiiUtils::UpdateResult::MissingUpdatePartition:
71   case WiiUtils::UpdateResult::DiscReadFailed:
72     ModalMessageBox::critical(parent, QObject::tr("Update failed"),
73                               QObject::tr("The game disc does not contain any usable "
74                                           "update information."));
75     break;
76   }
77 }
78 
79 template <typename Callable, typename... Args>
ShowProgress(QWidget * parent,Callable function,Args &&...args)80 static WiiUtils::UpdateResult ShowProgress(QWidget* parent, Callable function, Args&&... args)
81 {
82   // Do not allow the user to close the dialog. Instead, wait until the update is finished
83   // or cancelled.
84   class UpdateProgressDialog final : public QProgressDialog
85   {
86   public:
87     using QProgressDialog::QProgressDialog;
88 
89   protected:
90     void reject() override {}
91   };
92 
93   UpdateProgressDialog dialog{parent};
94   dialog.setLabelText(QObject::tr("Preparing to update...\nThis can take a while."));
95   dialog.setWindowTitle(QObject::tr("Updating"));
96   dialog.setWindowFlags(dialog.windowFlags() & ~Qt::WindowContextHelpButtonHint);
97   // QProgressDialog doesn't set its minimum size correctly.
98   dialog.setMinimumSize(360, 150);
99 
100   // QProgressDialog doesn't allow us to disable the cancel button when it's pressed,
101   // so we have to pass it our own push button. Note: the dialog takes ownership of it.
102   auto* cancel_button = new QPushButton(QObject::tr("&Cancel"), parent);
103   dialog.setCancelButton(cancel_button);
104   Common::Flag was_cancelled;
105   QObject::disconnect(&dialog, &QProgressDialog::canceled, nullptr, nullptr);
106   QObject::connect(&dialog, &QProgressDialog::canceled, [&] {
107     dialog.setLabelText(QObject::tr("Finishing the update...\nThis can take a while."));
108     cancel_button->setEnabled(false);
109     was_cancelled.Set();
110   });
111 
112   std::future<WiiUtils::UpdateResult> result = std::async(std::launch::async, [&] {
113     const WiiUtils::UpdateResult res = function(
114         [&](size_t processed, size_t total, u64 title_id) {
115           QueueOnObject(&dialog, [&dialog, &was_cancelled, processed, total, title_id] {
116             if (was_cancelled.IsSet())
117               return;
118 
119             dialog.setRange(0, static_cast<int>(total));
120             dialog.setValue(static_cast<int>(processed));
121             dialog.setLabelText(QObject::tr("Updating title %1...\nThis can take a while.")
122                                     .arg(title_id, 16, 16, QLatin1Char('0')));
123           });
124           return !was_cancelled.IsSet();
125         },
126         std::forward<Args>(args)...);
127     QueueOnObject(&dialog, [&dialog] { dialog.done(0); });
128     return res;
129   });
130 
131   dialog.exec();
132   return result.get();
133 }
134 
PerformOnlineUpdate(const std::string & region,QWidget * parent)135 void PerformOnlineUpdate(const std::string& region, QWidget* parent)
136 {
137   const int confirm = ModalMessageBox::question(
138       parent, QObject::tr("Confirm"),
139       QObject::tr("Connect to the Internet and perform an online system update?"));
140   if (confirm != QMessageBox::Yes)
141     return;
142 
143   const WiiUtils::UpdateResult result = ShowProgress(parent, WiiUtils::DoOnlineUpdate, region);
144   ShowResult(parent, result);
145 }
146 
PerformDiscUpdate(const std::string & file_path,QWidget * parent)147 void PerformDiscUpdate(const std::string& file_path, QWidget* parent)
148 {
149   const WiiUtils::UpdateResult result = ShowProgress(parent, WiiUtils::DoDiscUpdate, file_path);
150   ShowResult(parent, result);
151 }
152 }  // namespace WiiUpdate
153