From 0c7b9570b4a7fe3f934fdefb05455a7b8990b68e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Mon, 12 Jun 2017 13:24:03 +0200 Subject: [PATCH 1/5] Move WiiUtils to Core Allows reusing the WAD import logic more easily, whereas UICommon code can only be used from UICommon and UI. And managing what's on the NAND is the Core's responsability, not UI. --- Source/Core/Core/CMakeLists.txt | 1 + Source/Core/Core/Core.vcxproj | 2 ++ Source/Core/Core/Core.vcxproj.filters | 4 +++- Source/Core/{UICommon => Core}/WiiUtils.cpp | 3 ++- Source/Core/{UICommon => Core}/WiiUtils.h | 0 Source/Core/DolphinQt2/GameList/GameFile.cpp | 2 +- Source/Core/DolphinWX/FrameTools.cpp | 2 +- Source/Core/UICommon/CMakeLists.txt | 1 - Source/Core/UICommon/UICommon.vcxproj | 4 +--- 9 files changed, 11 insertions(+), 8 deletions(-) rename Source/Core/{UICommon => Core}/WiiUtils.cpp (98%) rename Source/Core/{UICommon => Core}/WiiUtils.h (100%) diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index da907b4477..498cb8e9f2 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -19,6 +19,7 @@ set(SRCS State.cpp TitleDatabase.cpp WiiRoot.cpp + WiiUtils.cpp Boot/Boot_BS2Emu.cpp Boot/Boot.cpp Boot/Boot_WiiWAD.cpp diff --git a/Source/Core/Core/Core.vcxproj b/Source/Core/Core/Core.vcxproj index 193c73ae98..cee0beb8df 100644 --- a/Source/Core/Core/Core.vcxproj +++ b/Source/Core/Core/Core.vcxproj @@ -290,6 +290,7 @@ + @@ -524,6 +525,7 @@ + diff --git a/Source/Core/Core/Core.vcxproj.filters b/Source/Core/Core/Core.vcxproj.filters index bd15438132..76c5fcff0d 100644 --- a/Source/Core/Core/Core.vcxproj.filters +++ b/Source/Core/Core/Core.vcxproj.filters @@ -179,6 +179,7 @@ + ActionReplay @@ -898,6 +899,7 @@ + ActionReplay @@ -1543,4 +1545,4 @@ - \ No newline at end of file + diff --git a/Source/Core/UICommon/WiiUtils.cpp b/Source/Core/Core/WiiUtils.cpp similarity index 98% rename from Source/Core/UICommon/WiiUtils.cpp rename to Source/Core/Core/WiiUtils.cpp index 994079f623..9a61a25a59 100644 --- a/Source/Core/UICommon/WiiUtils.cpp +++ b/Source/Core/Core/WiiUtils.cpp @@ -2,7 +2,8 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. -#include "UICommon/WiiUtils.h" +#include "Core/WiiUtils.h" + #include "Common/CommonTypes.h" #include "Common/MsgHandler.h" #include "Core/ConfigManager.h" diff --git a/Source/Core/UICommon/WiiUtils.h b/Source/Core/Core/WiiUtils.h similarity index 100% rename from Source/Core/UICommon/WiiUtils.h rename to Source/Core/Core/WiiUtils.h diff --git a/Source/Core/DolphinQt2/GameList/GameFile.cpp b/Source/Core/DolphinQt2/GameList/GameFile.cpp index 508265c883..900faab711 100644 --- a/Source/Core/DolphinQt2/GameList/GameFile.cpp +++ b/Source/Core/DolphinQt2/GameList/GameFile.cpp @@ -15,6 +15,7 @@ #include "Core/HW/WiiSaveCrypted.h" #include "Core/IOS/ES/ES.h" #include "Core/IOS/IOS.h" +#include "Core/WiiUtils.h" #include "DiscIO/Blob.h" #include "DiscIO/Enums.h" #include "DiscIO/NANDContentLoader.h" @@ -22,7 +23,6 @@ #include "DolphinQt2/GameList/GameFile.h" #include "DolphinQt2/Resources.h" #include "DolphinQt2/Settings.h" -#include "UICommon/WiiUtils.h" static const int CACHE_VERSION = 13; // Last changed in PR #3261 static const int DATASTREAM_VERSION = QDataStream::Qt_5_5; diff --git a/Source/Core/DolphinWX/FrameTools.cpp b/Source/Core/DolphinWX/FrameTools.cpp index b33ed2cf2f..bb7c28a1c5 100644 --- a/Source/Core/DolphinWX/FrameTools.cpp +++ b/Source/Core/DolphinWX/FrameTools.cpp @@ -54,6 +54,7 @@ #include "Core/PowerPC/PPCSymbolDB.h" #include "Core/PowerPC/PowerPC.h" #include "Core/State.h" +#include "Core/WiiUtils.h" #include "DiscIO/Enums.h" #include "DiscIO/NANDContentLoader.h" @@ -87,7 +88,6 @@ #include "InputCommon/ControllerInterface/ControllerInterface.h" #include "UICommon/UICommon.h" -#include "UICommon/WiiUtils.h" #include "VideoCommon/RenderBase.h" #include "VideoCommon/VideoBackendBase.h" diff --git a/Source/Core/UICommon/CMakeLists.txt b/Source/Core/UICommon/CMakeLists.txt index df1f3fd157..36648bbe7c 100644 --- a/Source/Core/UICommon/CMakeLists.txt +++ b/Source/Core/UICommon/CMakeLists.txt @@ -3,7 +3,6 @@ set(SRCS Disassembler.cpp UICommon.cpp USBUtils.cpp - WiiUtils.cpp ) if(USE_X11) diff --git a/Source/Core/UICommon/UICommon.vcxproj b/Source/Core/UICommon/UICommon.vcxproj index 8b65ab9c25..1b5f3521d4 100644 --- a/Source/Core/UICommon/UICommon.vcxproj +++ b/Source/Core/UICommon/UICommon.vcxproj @@ -50,14 +50,12 @@ 4200;%(DisableSpecificWarnings) - - @@ -67,4 +65,4 @@ - \ No newline at end of file + From 42d217b9a8c6090f7af2743537fecb4747d63797 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Mon, 12 Jun 2017 16:45:01 +0200 Subject: [PATCH 2/5] WiiUtils: Add an online updater While setting up a proper NAND for Wii emulation has become much easier now that disc and online system updates work, they still require users to have a recent disc game, certificates extracted from IOS or a NAND dump for online updates to work and to really get all system titles. This commit adds the ability to do an online update right from Dolphin itself, which solves that usability issue. --- Source/Core/Core/WiiUtils.cpp | 421 ++++++++++++++++++++++++++++++++++ Source/Core/Core/WiiUtils.h | 26 +++ 2 files changed, 447 insertions(+) diff --git a/Source/Core/Core/WiiUtils.cpp b/Source/Core/Core/WiiUtils.cpp index 9a61a25a59..0e8a8fd253 100644 --- a/Source/Core/Core/WiiUtils.cpp +++ b/Source/Core/Core/WiiUtils.cpp @@ -4,12 +4,35 @@ #include "Core/WiiUtils.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "Common/Assert.h" +#include "Common/CommonPaths.h" #include "Common/CommonTypes.h" +#include "Common/FileUtil.h" +#include "Common/HttpRequest.h" +#include "Common/Logging/Log.h" #include "Common/MsgHandler.h" +#include "Common/NandPaths.h" +#include "Common/StringUtil.h" +#include "Common/Swap.h" +#include "Core/CommonTitles.h" #include "Core/ConfigManager.h" +#include "Core/IOS/Device.h" #include "Core/IOS/ES/ES.h" #include "Core/IOS/ES/Formats.h" #include "Core/IOS/IOS.h" +#include "DiscIO/Enums.h" #include "DiscIO/NANDContentLoader.h" #include "DiscIO/WiiWad.h" @@ -74,4 +97,402 @@ bool InstallWAD(const std::string& wad_path) DiscIO::NANDContentManager::Access().ClearCache(); return true; } + +// Common functionality for system updaters. +class SystemUpdater +{ +public: + virtual ~SystemUpdater() = default; + +protected: + struct TitleInfo + { + u64 id; + u16 version; + }; + + std::string GetDeviceRegion(); + std::string GetDeviceId(); + bool ShouldInstallTitle(const TitleInfo& title); + + IOS::HLE::Kernel m_ios; +}; + +std::string SystemUpdater::GetDeviceRegion() +{ + // Try to determine the region from an installed system menu. + const auto tmd = m_ios.GetES()->FindInstalledTMD(Titles::SYSTEM_MENU); + if (tmd.IsValid()) + { + const DiscIO::Region region = tmd.GetRegion(); + static const std::map regions = { + {DiscIO::Region::NTSC_J, "JPN"}, + {DiscIO::Region::NTSC_U, "USA"}, + {DiscIO::Region::PAL, "EUR"}, + {DiscIO::Region::NTSC_K, "KOR"}, + {DiscIO::Region::UNKNOWN_REGION, "EUR"}}; + return regions.at(region); + } + return ""; +} + +std::string SystemUpdater::GetDeviceId() +{ + u32 ios_device_id; + if (m_ios.GetES()->GetDeviceId(&ios_device_id) < 0) + return ""; + return StringFromFormat("%" PRIu64, (u64(1) << 32) | ios_device_id); +} + +bool SystemUpdater::ShouldInstallTitle(const TitleInfo& title) +{ + const auto es = m_ios.GetES(); + const auto installed_tmd = es->FindInstalledTMD(title.id); + return !(installed_tmd.IsValid() && installed_tmd.GetTitleVersion() >= title.version && + es->GetStoredContentsFromTMD(installed_tmd).size() == installed_tmd.GetNumContents()); +} + +class OnlineSystemUpdater final : public SystemUpdater +{ +public: + OnlineSystemUpdater(UpdateCallback update_callback, const std::string& region); + UpdateResult DoOnlineUpdate(); + +private: + struct Response + { + std::string content_prefix_url; + std::vector titles; + }; + + Response GetSystemTitles(); + Response ParseTitlesResponse(const std::vector& response) const; + + UpdateResult InstallTitleFromNUS(const std::string& prefix_url, const TitleInfo& title, + std::unordered_set* updated_titles); + + // Helper functions to download contents from NUS. + std::pair> DownloadTMD(const std::string& prefix_url, + const TitleInfo& title); + std::pair, std::vector> DownloadTicket(const std::string& prefix_url, + const TitleInfo& title); + std::optional> DownloadContent(const std::string& prefix_url, + const TitleInfo& title, u32 cid); + + UpdateCallback m_update_callback; + std::string m_requested_region; + Common::HttpRequest m_http{std::chrono::minutes{3}}; +}; + +OnlineSystemUpdater::OnlineSystemUpdater(UpdateCallback update_callback, const std::string& region) + : m_update_callback(std::move(update_callback)), m_requested_region(region) +{ +} + +OnlineSystemUpdater::Response +OnlineSystemUpdater::ParseTitlesResponse(const std::vector& response) const +{ + pugi::xml_document doc; + pugi::xml_parse_result result = doc.load_buffer(response.data(), response.size()); + if (!result) + { + ERROR_LOG(CORE, "ParseTitlesResponse: Could not parse response"); + return {}; + } + + // pugixml doesn't fully support namespaces and ignores them. + const pugi::xml_node node = doc.select_node("//GetSystemUpdateResponse").node(); + if (!node) + { + ERROR_LOG(CORE, "ParseTitlesResponse: Could not find response node"); + return {}; + } + + const int code = node.child("ErrorCode").text().as_int(); + if (code != 0) + { + ERROR_LOG(CORE, "ParseTitlesResponse: Non-zero error code (%d)", code); + return {}; + } + + // libnup uses the uncached URL, not the cached one. However, that one is way, way too slow, + // so let's use the cached endpoint. + Response info; + info.content_prefix_url = node.child("ContentPrefixURL").text().as_string(); + // Disable HTTPS because we can't use it without a device certificate. + info.content_prefix_url = ReplaceAll(info.content_prefix_url, "https://", "http://"); + if (info.content_prefix_url.empty()) + { + ERROR_LOG(CORE, "ParseTitlesResponse: Empty content prefix URL"); + return {}; + } + + for (const pugi::xml_node& title_node : node.children("TitleVersion")) + { + const u64 title_id = std::stoull(title_node.child("TitleId").text().as_string(), nullptr, 16); + const u16 title_version = static_cast(title_node.child("Version").text().as_uint()); + info.titles.push_back({title_id, title_version}); + } + return info; +} + +constexpr const char* GET_SYSTEM_TITLES_REQUEST_PAYLOAD = R"( + + + + 1.0 + 0 + + + + + +)"; + +OnlineSystemUpdater::Response OnlineSystemUpdater::GetSystemTitles() +{ + // Construct the request by loading the template first, then updating some fields. + pugi::xml_document doc; + pugi::xml_parse_result result = doc.load_string(GET_SYSTEM_TITLES_REQUEST_PAYLOAD); + _assert_(result); + + // Nintendo does not really care about the device ID or verify that we *are* that device, + // as long as it is a valid Wii device ID. + const std::string device_id = GetDeviceId(); + _assert_(doc.select_node("//DeviceId").node().text().set(device_id.c_str())); + + // Write the correct device region. + const std::string region = m_requested_region.empty() ? GetDeviceRegion() : m_requested_region; + _assert_(doc.select_node("//RegionId").node().text().set(region.c_str())); + + std::ostringstream stream; + doc.save(stream); + const std::string request = stream.str(); + + // Note: We don't use HTTPS because that would require the user to have + // a device certificate which cannot be redistributed with Dolphin. + // This is fine, because IOS has signature checks. + const Common::HttpRequest::Response response = + m_http.Post("http://nus.shop.wii.com/nus/services/NetUpdateSOAP", request, + { + {"SOAPAction", "urn:nus.wsapi.broadon.com/GetSystemUpdate"}, + {"User-Agent", "wii libnup/1.0"}, + {"Content-Type", "text/xml; charset=utf-8"}, + }); + + if (!response) + return {}; + return ParseTitlesResponse(*response); +} + +UpdateResult OnlineSystemUpdater::DoOnlineUpdate() +{ + const Response info = GetSystemTitles(); + if (info.titles.empty()) + return UpdateResult::ServerFailed; + + // Download and install any title that is older than the NUS version. + // The order is determined by the server response, which is: boot2, System Menu, IOSes, channels. + // As we install any IOS required by titles, the real order is boot2, SM IOS, SM, IOSes, channels. + std::unordered_set updated_titles; + size_t processed = 0; + for (const TitleInfo& title : info.titles) + { + if (!m_update_callback(processed++, info.titles.size(), title.id)) + return UpdateResult::Cancelled; + + const UpdateResult res = InstallTitleFromNUS(info.content_prefix_url, title, &updated_titles); + if (res != UpdateResult::Succeeded) + { + ERROR_LOG(CORE, "Failed to update %016" PRIx64 " -- aborting update", title.id); + return res; + } + + m_update_callback(processed, info.titles.size(), title.id); + } + + if (updated_titles.empty()) + { + NOTICE_LOG(CORE, "Update finished - Already up-to-date"); + return UpdateResult::AlreadyUpToDate; + } + NOTICE_LOG(CORE, "Update finished - %zu updates installed", updated_titles.size()); + return UpdateResult::Succeeded; +} + +UpdateResult OnlineSystemUpdater::InstallTitleFromNUS(const std::string& prefix_url, + const TitleInfo& title, + std::unordered_set* updated_titles) +{ + // We currently don't support boot2 updates at all, so ignore any attempt to install it. + if (title.id == Titles::BOOT2) + return UpdateResult::Succeeded; + + if (!ShouldInstallTitle(title) || updated_titles->find(title.id) != updated_titles->end()) + return UpdateResult::Succeeded; + + NOTICE_LOG(CORE, "Updating title %016" PRIx64, title.id); + + // Download the ticket and certificates. + const auto ticket = DownloadTicket(prefix_url, title); + if (ticket.first.empty() || ticket.second.empty()) + { + ERROR_LOG(CORE, "Failed to download ticket and certs"); + return UpdateResult::DownloadFailed; + } + + // Import the ticket. + IOS::HLE::ReturnCode ret = IOS::HLE::IPC_SUCCESS; + const auto es = m_ios.GetES(); + if ((ret = es->ImportTicket(ticket.first, ticket.second)) < 0) + { + ERROR_LOG(CORE, "Failed to import ticket: error %d", ret); + return UpdateResult::ImportFailed; + } + + // Download the TMD. + const auto tmd = DownloadTMD(prefix_url, title); + if (!tmd.first.IsValid()) + { + ERROR_LOG(CORE, "Failed to download TMD"); + return UpdateResult::DownloadFailed; + } + + // Download and import any required system title first. + const u64 ios_id = tmd.first.GetIOSId(); + if (ios_id != 0 && IOS::ES::IsTitleType(ios_id, IOS::ES::TitleType::System)) + { + if (!es->FindInstalledTMD(ios_id).IsValid()) + { + WARN_LOG(CORE, "Importing required system title %016" PRIx64 " first", ios_id); + const UpdateResult res = InstallTitleFromNUS(prefix_url, {ios_id, 0}, updated_titles); + if (res != UpdateResult::Succeeded) + { + ERROR_LOG(CORE, "Failed to import required system title %016" PRIx64, ios_id); + return res; + } + } + } + + // Initialise the title import. + IOS::HLE::Device::ES::Context context; + if ((ret = es->ImportTitleInit(context, tmd.first.GetBytes(), tmd.second)) < 0) + { + ERROR_LOG(CORE, "Failed to initialise title import: error %d", ret); + return UpdateResult::ImportFailed; + } + + // Now download and install contents listed in the TMD. + const std::vector stored_contents = es->GetStoredContentsFromTMD(tmd.first); + const UpdateResult import_result = [&]() { + for (const IOS::ES::Content& content : tmd.first.GetContents()) + { + const bool is_already_installed = std::find_if(stored_contents.begin(), stored_contents.end(), + [&content](const auto& stored_content) { + return stored_content.id == content.id; + }) != stored_contents.end(); + + // Do skip what is already installed on the NAND. + if (is_already_installed) + continue; + + if ((ret = es->ImportContentBegin(context, title.id, content.id)) < 0) + { + ERROR_LOG(CORE, "Failed to initialise import for content %08x: error %d", content.id, ret); + return UpdateResult::ImportFailed; + } + + const std::optional> data = DownloadContent(prefix_url, title, content.id); + if (!data) + { + ERROR_LOG(CORE, "Failed to download content %08x", content.id); + return UpdateResult::DownloadFailed; + } + + if (es->ImportContentData(context, 0, data->data(), static_cast(data->size())) < 0 || + es->ImportContentEnd(context, 0) < 0) + { + ERROR_LOG(CORE, "Failed to import content %08x", content.id); + return UpdateResult::ImportFailed; + } + } + return UpdateResult::Succeeded; + }(); + const bool all_contents_imported = import_result == UpdateResult::Succeeded; + + if ((all_contents_imported && (ret = es->ImportTitleDone(context)) < 0) || + (!all_contents_imported && (ret = es->ImportTitleCancel(context)) < 0)) + { + ERROR_LOG(CORE, "Failed to finalise title import: error %d", ret); + return UpdateResult::ImportFailed; + } + + if (!all_contents_imported) + return import_result; + + updated_titles->emplace(title.id); + return UpdateResult::Succeeded; +} + +std::pair> +OnlineSystemUpdater::DownloadTMD(const std::string& prefix_url, const TitleInfo& title) +{ + const std::string url = + (title.version == 0) ? + prefix_url + StringFromFormat("/%016" PRIx64 "/tmd", title.id) : + prefix_url + StringFromFormat("/%016" PRIx64 "/tmd.%u", title.id, title.version); + const Common::HttpRequest::Response response = m_http.Get(url); + if (!response) + return {}; + + // Too small to contain both the TMD and a cert chain. + if (response->size() <= sizeof(IOS::ES::TMDHeader)) + return {}; + const size_t tmd_size = + sizeof(IOS::ES::TMDHeader) + + sizeof(IOS::ES::Content) * + Common::swap16(response->data() + offsetof(IOS::ES::TMDHeader, num_contents)); + if (response->size() <= tmd_size) + return {}; + + const auto tmd_begin = response->begin(); + const auto tmd_end = tmd_begin + tmd_size; + + return {IOS::ES::TMDReader(std::vector(tmd_begin, tmd_end)), + std::vector(tmd_end, response->end())}; +} + +std::pair, std::vector> +OnlineSystemUpdater::DownloadTicket(const std::string& prefix_url, const TitleInfo& title) +{ + const std::string url = prefix_url + StringFromFormat("/%016" PRIx64 "/cetk", title.id); + const Common::HttpRequest::Response response = m_http.Get(url); + if (!response) + return {}; + + // Too small to contain both the ticket and a cert chain. + if (response->size() <= sizeof(IOS::ES::Ticket)) + return {}; + + const auto ticket_begin = response->begin(); + const auto ticket_end = ticket_begin + sizeof(IOS::ES::Ticket); + return {std::vector(ticket_begin, ticket_end), std::vector(ticket_end, response->end())}; +} + +std::optional> OnlineSystemUpdater::DownloadContent(const std::string& prefix_url, + const TitleInfo& title, u32 cid) +{ + const std::string url = prefix_url + StringFromFormat("/%016" PRIx64 "/%08x", title.id, cid); + return m_http.Get(url); +} + +UpdateResult DoOnlineUpdate(UpdateCallback update_callback, const std::string& region) +{ + OnlineSystemUpdater updater{std::move(update_callback), region}; + const UpdateResult result = updater.DoOnlineUpdate(); + DiscIO::NANDContentManager::Access().ClearCache(); + return result; +} } diff --git a/Source/Core/Core/WiiUtils.h b/Source/Core/Core/WiiUtils.h index 8419e01b9b..3d0eff6b9a 100644 --- a/Source/Core/Core/WiiUtils.h +++ b/Source/Core/Core/WiiUtils.h @@ -4,11 +4,37 @@ #pragma once +#include +#include #include +#include "Common/CommonTypes.h" + // Small utility functions for common Wii related tasks. namespace WiiUtils { bool InstallWAD(const std::string& wad_path); + +enum class UpdateResult +{ + Succeeded, + AlreadyUpToDate, + + // NUS errors and failures. + ServerFailed, + // General download failures. + DownloadFailed, + // Import failures. + ImportFailed, + // Update was cancelled. + Cancelled, +}; + +// Return false to cancel the update as soon as the current title has finished updating. +using UpdateCallback = std::function; +// Download and install the latest version of all titles (if missing) from NUS. +// If no region is specified, the region of the installed System Menu will be used. +// If no region is specified and no system menu is installed, the update will fail. +UpdateResult DoOnlineUpdate(UpdateCallback update_callback, const std::string& region); } From f06367febcc6be391bf68b5db41ad4253f60689c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Wed, 14 Jun 2017 01:22:03 +0200 Subject: [PATCH 3/5] WX: Add menu item to perform online update --- Source/Core/DolphinWX/Frame.h | 1 + Source/Core/DolphinWX/FrameTools.cpp | 101 ++++++++++++++++++++++++-- Source/Core/DolphinWX/Globals.h | 5 ++ Source/Core/DolphinWX/MainMenuBar.cpp | 36 +++++++-- Source/Core/DolphinWX/MainMenuBar.h | 6 ++ 5 files changed, 139 insertions(+), 10 deletions(-) diff --git a/Source/Core/DolphinWX/Frame.h b/Source/Core/DolphinWX/Frame.h index 26fdf74455..a4489b83da 100644 --- a/Source/Core/DolphinWX/Frame.h +++ b/Source/Core/DolphinWX/Frame.h @@ -345,6 +345,7 @@ private: void OnUninstallWAD(wxCommandEvent& event); void OnImportBootMiiBackup(wxCommandEvent& event); void OnExtractCertificates(wxCommandEvent& event); + void OnPerformOnlineWiiUpdate(wxCommandEvent& event); void OnFifoPlayer(wxCommandEvent& event); void OnConnectWiimote(wxCommandEvent& event); void GameListChanged(wxCommandEvent& event); diff --git a/Source/Core/DolphinWX/FrameTools.cpp b/Source/Core/DolphinWX/FrameTools.cpp index bb7c28a1c5..a403118000 100644 --- a/Source/Core/DolphinWX/FrameTools.cpp +++ b/Source/Core/DolphinWX/FrameTools.cpp @@ -3,8 +3,11 @@ // Refer to the license.txt file included. #include +#include +#include #include #include +#include #include #include #include @@ -18,7 +21,6 @@ #include #include #include -#include #include #include @@ -182,6 +184,12 @@ void CFrame::BindMenuBarEvents() Bind(wxEVT_MENU, &CFrame::OnLoadWiiMenu, this, IDM_LOAD_WII_MENU); Bind(wxEVT_MENU, &CFrame::OnImportBootMiiBackup, this, IDM_IMPORT_NAND); Bind(wxEVT_MENU, &CFrame::OnExtractCertificates, this, IDM_EXTRACT_CERTIFICATES); + for (const int idm : {IDM_PERFORM_ONLINE_UPDATE_CURRENT, IDM_PERFORM_ONLINE_UPDATE_EUR, + IDM_PERFORM_ONLINE_UPDATE_JPN, IDM_PERFORM_ONLINE_UPDATE_KOR, + IDM_PERFORM_ONLINE_UPDATE_USA}) + { + Bind(wxEVT_MENU, &CFrame::OnPerformOnlineWiiUpdate, this, idm); + } Bind(wxEVT_MENU, &CFrame::OnFifoPlayer, this, IDM_FIFOPLAYER); Bind(wxEVT_MENU, &CFrame::OnConnectWiimote, this, IDM_CONNECT_WIIMOTE1, IDM_CONNECT_BALANCEBOARD); @@ -1293,6 +1301,93 @@ void CFrame::OnExtractCertificates(wxCommandEvent& WXUNUSED(event)) DiscIO::NANDImporter().ExtractCertificates(File::GetUserPath(D_WIIROOT_IDX)); } +static std::string GetUpdateRegionFromIdm(int idm) +{ + switch (idm) + { + case IDM_PERFORM_ONLINE_UPDATE_EUR: + return "EUR"; + case IDM_PERFORM_ONLINE_UPDATE_JPN: + return "JPN"; + case IDM_PERFORM_ONLINE_UPDATE_KOR: + return "KOR"; + case IDM_PERFORM_ONLINE_UPDATE_USA: + return "USA"; + case IDM_PERFORM_ONLINE_UPDATE_CURRENT: + default: + return ""; + } +} + +void CFrame::OnPerformOnlineWiiUpdate(wxCommandEvent& event) +{ + int confirm = wxMessageBox(_("Connect to the Internet and perform an online system update?"), + _("System Update"), wxYES_NO, this); + if (confirm != wxYES) + return; + + wxProgressDialog dialog(_("Updating"), _("Preparing to update...\nThis can take a while."), 1, + this, wxPD_APP_MODAL | wxPD_AUTO_HIDE | wxPD_SMOOTH | wxPD_CAN_ABORT); + + const std::string region = GetUpdateRegionFromIdm(event.GetId()); + std::future result = std::async(std::launch::async, [&] { + const WiiUtils::UpdateResult res = WiiUtils::DoOnlineUpdate( + [&](size_t processed, size_t total, u64 title_id) { + Core::QueueHostJob( + [&dialog, processed, total, title_id] { + dialog.SetRange(total); + dialog.Update(processed, wxString::Format(_("Updating title %016" PRIx64 "...\n" + "This can take a while."), + title_id)); + dialog.Fit(); + }, + true); + return !dialog.WasCancelled(); + }, + region); + Core::QueueHostJob([&dialog] { dialog.EndModal(0); }, true); + return res; + }); + + dialog.ShowModal(); + + switch (result.get()) + { + case WiiUtils::UpdateResult::Succeeded: + wxMessageBox(_("The emulated Wii console has been updated."), _("Update completed"), + wxOK | wxICON_INFORMATION); + DiscIO::NANDImporter().ExtractCertificates(File::GetUserPath(D_WIIROOT_IDX)); + break; + case WiiUtils::UpdateResult::AlreadyUpToDate: + wxMessageBox(_("The emulated Wii console is already up-to-date."), _("Update completed"), + wxOK | wxICON_INFORMATION); + DiscIO::NANDImporter().ExtractCertificates(File::GetUserPath(D_WIIROOT_IDX)); + break; + case WiiUtils::UpdateResult::ServerFailed: + wxMessageBox(_("Could not download update information from Nintendo. " + "Please check your Internet connection and try again."), + _("Update failed"), wxOK | wxICON_ERROR); + break; + case WiiUtils::UpdateResult::DownloadFailed: + wxMessageBox(_("Could not download update files from Nintendo. " + "Please check your Internet connection and try again."), + _("Update failed"), wxOK | wxICON_ERROR); + break; + case WiiUtils::UpdateResult::ImportFailed: + wxMessageBox(_("Could not install an update to the Wii system memory. " + "Please refer to logs for more information."), + _("Update failed"), wxOK | wxICON_ERROR); + break; + case WiiUtils::UpdateResult::Cancelled: + wxMessageBox(_("The update has been cancelled. It is strongly recommended to " + "finish it in order to avoid inconsistent system software versions."), + _("Update cancelled"), wxOK | wxICON_WARNING); + break; + } + + UpdateLoadWiiMenuItem(); +} + void CFrame::UpdateLoadWiiMenuItem() const { GetMenuBar()->Refresh(true, nullptr); @@ -1492,10 +1587,6 @@ void CFrame::UpdateGUI() GetMenuBar() ->FindItem(IDM_LOAD_GC_IPL_EUR) ->Enable(!Initialized && File::Exists(SConfig::GetInstance().GetBootROMPath(EUR_DIR))); - if (DiscIO::NANDContentManager::Access() - .GetNANDLoader(Titles::SYSTEM_MENU, Common::FROM_CONFIGURED_ROOT) - .IsValid()) - GetMenuBar()->FindItem(IDM_LOAD_WII_MENU)->Enable(!Initialized); // Tools GetMenuBar()->FindItem(IDM_CHEATS)->Enable(SConfig::GetInstance().bEnableCheats); diff --git a/Source/Core/DolphinWX/Globals.h b/Source/Core/DolphinWX/Globals.h index ac066872ee..3abcec98f3 100644 --- a/Source/Core/DolphinWX/Globals.h +++ b/Source/Core/DolphinWX/Globals.h @@ -104,6 +104,11 @@ enum IDM_LIST_UNINSTALL_WAD, IDM_IMPORT_NAND, IDM_EXTRACT_CERTIFICATES, + IDM_PERFORM_ONLINE_UPDATE_CURRENT, + IDM_PERFORM_ONLINE_UPDATE_EUR, + IDM_PERFORM_ONLINE_UPDATE_JPN, + IDM_PERFORM_ONLINE_UPDATE_KOR, + IDM_PERFORM_ONLINE_UPDATE_USA, IDM_FIFOPLAYER, IDM_LOAD_GC_IPL_JAP, IDM_LOAD_GC_IPL_USA, diff --git a/Source/Core/DolphinWX/MainMenuBar.cpp b/Source/Core/DolphinWX/MainMenuBar.cpp index c9bdb25289..a9324565d1 100644 --- a/Source/Core/DolphinWX/MainMenuBar.cpp +++ b/Source/Core/DolphinWX/MainMenuBar.cpp @@ -235,6 +235,16 @@ wxMenu* MainMenuBar::CreateToolsMenu() const tools_menu->Append(IDM_LOAD_WII_MENU, dummy_string); tools_menu->Append(IDM_IMPORT_NAND, _("Import BootMii NAND Backup...")); tools_menu->Append(IDM_EXTRACT_CERTIFICATES, _("Extract Certificates from NAND")); + auto* const online_update_menu = new wxMenu; + online_update_menu->Append(IDM_PERFORM_ONLINE_UPDATE_CURRENT, _("Current Region")); + online_update_menu->AppendSeparator(); + online_update_menu->Append(IDM_PERFORM_ONLINE_UPDATE_EUR, _("Europe")); + online_update_menu->Append(IDM_PERFORM_ONLINE_UPDATE_JPN, _("Japan")); + online_update_menu->Append(IDM_PERFORM_ONLINE_UPDATE_KOR, _("Korean")); + online_update_menu->Append(IDM_PERFORM_ONLINE_UPDATE_USA, _("United States")); + tools_menu->AppendSubMenu( + online_update_menu, _("Perform Online System Update"), + _("Update the Wii system software to the latest version from Nintendo.")); tools_menu->AppendSeparator(); tools_menu->AppendSubMenu(wiimote_menu, _("Connect Wii Remotes")); @@ -562,8 +572,6 @@ void MainMenuBar::RefreshSaveStateMenuLabels() const void MainMenuBar::RefreshWiiToolsLabels() const { - RefreshWiiSystemMenuLabel(); - // The Install WAD option should not be enabled while emulation is running, because // having unexpected title changes can confuse emulated software; and of course, this is // not possible on a real Wii and won't be if we have IOS LLE (or simply more accurate IOS HLE). @@ -571,10 +579,26 @@ void MainMenuBar::RefreshWiiToolsLabels() const // For similar reasons, it should not be possible to export or import saves, because this can // result in the emulated software being confused, or even worse, exported saves having // inconsistent data. - for (const int index : {IDM_MENU_INSTALL_WAD, IDM_EXPORT_ALL_SAVE, IDM_IMPORT_SAVE, - IDM_IMPORT_NAND, IDM_EXTRACT_CERTIFICATES}) + const bool enable_wii_tools = !Core::IsRunning() || !SConfig::GetInstance().bWii; + for (const int index : + {IDM_MENU_INSTALL_WAD, IDM_EXPORT_ALL_SAVE, IDM_IMPORT_SAVE, IDM_IMPORT_NAND, + IDM_EXTRACT_CERTIFICATES, IDM_LOAD_WII_MENU, IDM_PERFORM_ONLINE_UPDATE_CURRENT, + IDM_PERFORM_ONLINE_UPDATE_EUR, IDM_PERFORM_ONLINE_UPDATE_JPN, IDM_PERFORM_ONLINE_UPDATE_KOR, + IDM_PERFORM_ONLINE_UPDATE_USA}) { - FindItem(index)->Enable(!Core::IsRunning() || !SConfig::GetInstance().bWii); + FindItem(index)->Enable(enable_wii_tools); + } + if (enable_wii_tools) + RefreshWiiSystemMenuLabel(); +} + +void MainMenuBar::EnableUpdateMenu(UpdateMenuMode mode) const +{ + FindItem(IDM_PERFORM_ONLINE_UPDATE_CURRENT)->Enable(mode == UpdateMenuMode::CurrentRegionOnly); + for (const int idm : {IDM_PERFORM_ONLINE_UPDATE_EUR, IDM_PERFORM_ONLINE_UPDATE_JPN, + IDM_PERFORM_ONLINE_UPDATE_KOR, IDM_PERFORM_ONLINE_UPDATE_USA}) + { + FindItem(idm)->Enable(mode == UpdateMenuMode::SpecificRegionsOnly); } } @@ -591,11 +615,13 @@ void MainMenuBar::RefreshWiiSystemMenuLabel() const const wxString version_string = StrToWxStr(DiscIO::GetSysMenuVersionString(version_number)); item->Enable(); item->SetItemLabel(wxString::Format(_("Load Wii System Menu %s"), version_string)); + EnableUpdateMenu(UpdateMenuMode::CurrentRegionOnly); } else { item->Enable(false); item->SetItemLabel(_("Load Wii System Menu")); + EnableUpdateMenu(UpdateMenuMode::SpecificRegionsOnly); } } diff --git a/Source/Core/DolphinWX/MainMenuBar.h b/Source/Core/DolphinWX/MainMenuBar.h index ca005640cb..d7e3d692dc 100644 --- a/Source/Core/DolphinWX/MainMenuBar.h +++ b/Source/Core/DolphinWX/MainMenuBar.h @@ -48,6 +48,12 @@ private: void RefreshSaveStateMenuLabels() const; void RefreshWiiToolsLabels() const; void RefreshWiiSystemMenuLabel() const; + enum class UpdateMenuMode + { + CurrentRegionOnly, + SpecificRegionsOnly, + }; + void EnableUpdateMenu(UpdateMenuMode mode) const; void ClearSavedPerspectivesMenu() const; void PopulateSavedPerspectivesMenu(const std::vector& label_names) const; From 1a6b5ca36addfa7b9392b261574e29b8f1fcc649 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Wed, 14 Jun 2017 11:58:11 +0200 Subject: [PATCH 4/5] Qt: Add menu item to perform online update --- Source/Core/DolphinQt2/CMakeLists.txt | 2 + Source/Core/DolphinQt2/DolphinQt2.vcxproj | 2 + Source/Core/DolphinQt2/MainWindow.cpp | 12 ++- Source/Core/DolphinQt2/MainWindow.h | 2 + Source/Core/DolphinQt2/MenuBar.cpp | 30 ++++++ Source/Core/DolphinQt2/MenuBar.h | 7 ++ Source/Core/DolphinQt2/WiiUpdate.cpp | 119 ++++++++++++++++++++++ Source/Core/DolphinQt2/WiiUpdate.h | 14 +++ 8 files changed, 187 insertions(+), 1 deletion(-) create mode 100644 Source/Core/DolphinQt2/WiiUpdate.cpp create mode 100644 Source/Core/DolphinQt2/WiiUpdate.h diff --git a/Source/Core/DolphinQt2/CMakeLists.txt b/Source/Core/DolphinQt2/CMakeLists.txt index 10b26d5fe4..35bbb61284 100644 --- a/Source/Core/DolphinQt2/CMakeLists.txt +++ b/Source/Core/DolphinQt2/CMakeLists.txt @@ -19,6 +19,8 @@ set(SRCS Resources.cpp Settings.cpp ToolBar.cpp + WiiUpdate.cpp + WiiUpdate.h Config/ControllersWindow.cpp Config/FilesystemWidget.cpp Config/InfoWidget.cpp diff --git a/Source/Core/DolphinQt2/DolphinQt2.vcxproj b/Source/Core/DolphinQt2/DolphinQt2.vcxproj index 34d79d5228..20e251a3b0 100644 --- a/Source/Core/DolphinQt2/DolphinQt2.vcxproj +++ b/Source/Core/DolphinQt2/DolphinQt2.vcxproj @@ -164,6 +164,7 @@ + @@ -185,6 +186,7 @@ + diff --git a/Source/Core/DolphinQt2/MainWindow.cpp b/Source/Core/DolphinQt2/MainWindow.cpp index b57ac4f134..de38910faa 100644 --- a/Source/Core/DolphinQt2/MainWindow.cpp +++ b/Source/Core/DolphinQt2/MainWindow.cpp @@ -30,7 +30,6 @@ #include "DolphinQt2/AboutDialog.h" #include "DolphinQt2/Config/ControllersWindow.h" - #include "DolphinQt2/Config/Mapping/MappingWindow.h" #include "DolphinQt2/Config/SettingsWindow.h" #include "DolphinQt2/Host.h" @@ -39,6 +38,7 @@ #include "DolphinQt2/QtUtils/WindowActivationEventFilter.h" #include "DolphinQt2/Resources.h" #include "DolphinQt2/Settings.h" +#include "DolphinQt2/WiiUpdate.h" #include "InputCommon/ControllerInterface/ControllerInterface.h" @@ -165,6 +165,9 @@ void MainWindow::ConnectMenuBar() // Options connect(m_menu_bar, &MenuBar::ConfigureHotkeys, this, &MainWindow::ShowHotkeyDialog); + // Tools + connect(m_menu_bar, &MenuBar::PerformOnlineUpdate, this, &MainWindow::PerformOnlineUpdate); + // View connect(m_menu_bar, &MenuBar::ShowTable, m_game_list, &GameList::SetTableView); connect(m_menu_bar, &MenuBar::ShowList, m_game_list, &GameList::SetListView); @@ -530,6 +533,13 @@ void MainWindow::SetStateSlot(int slot) m_state_slot = slot; } +void MainWindow::PerformOnlineUpdate(const std::string& region) +{ + WiiUpdate::PerformOnlineUpdate(region, this); + // Since the update may have installed a newer system menu, refresh the tools menu. + m_menu_bar->UpdateToolsMenu(false); +} + bool MainWindow::eventFilter(QObject* object, QEvent* event) { if (event->type() == QEvent::Close && !Stop()) diff --git a/Source/Core/DolphinQt2/MainWindow.h b/Source/Core/DolphinQt2/MainWindow.h index 7a69972ec9..0cd572790d 100644 --- a/Source/Core/DolphinQt2/MainWindow.h +++ b/Source/Core/DolphinQt2/MainWindow.h @@ -56,6 +56,8 @@ private slots: void StateSaveOldest(); void SetStateSlot(int slot); + void PerformOnlineUpdate(const std::string& region); + void FullScreen(); void ScreenShot(); diff --git a/Source/Core/DolphinQt2/MenuBar.cpp b/Source/Core/DolphinQt2/MenuBar.cpp index 011649e0e1..888a5d8400 100644 --- a/Source/Core/DolphinQt2/MenuBar.cpp +++ b/Source/Core/DolphinQt2/MenuBar.cpp @@ -9,6 +9,9 @@ #include #include +#include "Core/CommonTitles.h" +#include "Core/IOS/ES/ES.h" +#include "Core/IOS/IOS.h" #include "Core/State.h" #include "DolphinQt2/AboutDialog.h" #include "DolphinQt2/GameList/GameFile.h" @@ -43,6 +46,7 @@ void MenuBar::EmulationStarted() m_state_load_menu->setEnabled(true); m_state_save_menu->setEnabled(true); UpdateStateSlotMenu(); + UpdateToolsMenu(true); } void MenuBar::EmulationPaused() { @@ -66,6 +70,7 @@ void MenuBar::EmulationStopped() m_state_load_menu->setEnabled(false); m_state_save_menu->setEnabled(false); UpdateStateSlotMenu(); + UpdateToolsMenu(false); } void MenuBar::AddFileMenu() @@ -79,6 +84,17 @@ void MenuBar::AddToolsMenu() { QMenu* tools_menu = addMenu(tr("Tools")); m_wad_install_action = tools_menu->addAction(tr("Install WAD..."), this, SLOT(InstallWAD())); + + m_perform_online_update_menu = tools_menu->addMenu(tr("Perform Online System Update")); + m_perform_online_update_for_current_region = m_perform_online_update_menu->addAction( + tr("Current Region"), [this] { emit PerformOnlineUpdate(""); }); + m_perform_online_update_menu->addSeparator(); + m_perform_online_update_menu->addAction(tr("Europe"), + [this] { emit PerformOnlineUpdate("EUR"); }); + m_perform_online_update_menu->addAction(tr("Japan"), [this] { emit PerformOnlineUpdate("JPN"); }); + m_perform_online_update_menu->addAction(tr("Korea"), [this] { emit PerformOnlineUpdate("KOR"); }); + m_perform_online_update_menu->addAction(tr("United States"), + [this] { emit PerformOnlineUpdate("USA"); }); } void MenuBar::AddEmulationMenu() @@ -248,6 +264,20 @@ void MenuBar::AddTableColumnsMenu(QMenu* view_menu) } } +void MenuBar::UpdateToolsMenu(bool emulation_started) +{ + const bool enable_wii_tools = !emulation_started || !Settings::Instance().IsWiiGameRunning(); + m_perform_online_update_menu->setEnabled(enable_wii_tools); + if (enable_wii_tools) + { + IOS::HLE::Kernel ios; + const auto tmd = ios.GetES()->FindInstalledTMD(Titles::SYSTEM_MENU); + for (QAction* action : m_perform_online_update_menu->actions()) + action->setEnabled(!tmd.IsValid()); + m_perform_online_update_for_current_region->setEnabled(tmd.IsValid()); + } +} + void MenuBar::InstallWAD() { QString wad_file = QFileDialog::getOpenFileName(this, tr("Select a title to install to NAND"), diff --git a/Source/Core/DolphinQt2/MenuBar.h b/Source/Core/DolphinQt2/MenuBar.h index a0346f4277..426c7748d6 100644 --- a/Source/Core/DolphinQt2/MenuBar.h +++ b/Source/Core/DolphinQt2/MenuBar.h @@ -4,6 +4,8 @@ #pragma once +#include + #include #include @@ -38,6 +40,8 @@ signals: void StateSaveOldest(); void SetStateSlot(int slot); + void PerformOnlineUpdate(const std::string& region); + // Options void ConfigureHotkeys(); @@ -53,6 +57,7 @@ public slots: void EmulationPaused(); void EmulationStopped(); void UpdateStateSlotMenu(); + void UpdateToolsMenu(bool emulation_started); // Tools void InstallWAD(); @@ -79,6 +84,8 @@ private: // Tools QAction* m_wad_install_action; + QMenu* m_perform_online_update_menu; + QAction* m_perform_online_update_for_current_region; // Emulation QAction* m_play_action; diff --git a/Source/Core/DolphinQt2/WiiUpdate.cpp b/Source/Core/DolphinQt2/WiiUpdate.cpp new file mode 100644 index 0000000000..d709e44b50 --- /dev/null +++ b/Source/Core/DolphinQt2/WiiUpdate.cpp @@ -0,0 +1,119 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "DolphinQt2/WiiUpdate.h" + +#include +#include + +#include +#include +#include +#include +#include + +#include "Common/FileUtil.h" +#include "Common/Flag.h" +#include "Core/Core.h" +#include "Core/WiiUtils.h" +#include "DiscIO/NANDImporter.h" + +namespace WiiUpdate +{ +void PerformOnlineUpdate(const std::string& region, QWidget* parent) +{ + const int confirm = QMessageBox::question( + parent, QObject::tr("Confirm"), + QObject::tr("Connect to the Internet and perform an online system update?")); + if (confirm != QMessageBox::Yes) + return; + + // Do not allow the user to close the dialog. Instead, wait until the update is finished + // or cancelled. + class UpdateProgressDialog final : public QProgressDialog + { + public: + using QProgressDialog::QProgressDialog; + + protected: + void reject() override {} + }; + + UpdateProgressDialog dialog{parent}; + dialog.setLabelText(QObject::tr("Preparing to update...\nThis can take a while.")); + dialog.setWindowTitle(QObject::tr("Updating")); + // QProgressDialog doesn't set its minimum size correctly. + dialog.setMinimumSize(360, 150); + + // QProgressDialog doesn't allow us to disable the cancel button when it's pressed, + // so we have to pass it our own push button. Note: the dialog takes ownership of it. + auto* cancel_button = new QPushButton(QObject::tr("&Cancel"), parent); + dialog.setCancelButton(cancel_button); + Common::Flag was_cancelled; + QObject::disconnect(&dialog, &QProgressDialog::canceled, &dialog, &QProgressDialog::cancel); + QObject::connect(&dialog, &QProgressDialog::canceled, [&] { + dialog.setLabelText(QObject::tr("Finishing the update...\nThis can take a while.")); + cancel_button->setEnabled(false); + was_cancelled.Set(); + }); + + std::future result = std::async(std::launch::async, [&] { + const WiiUtils::UpdateResult res = WiiUtils::DoOnlineUpdate( + [&](size_t processed, size_t total, u64 title_id) { + Core::QueueHostJob( + [&dialog, &was_cancelled, processed, total, title_id]() { + if (was_cancelled.IsSet()) + return; + + dialog.setRange(0, static_cast(total)); + dialog.setValue(static_cast(processed)); + dialog.setLabelText(QObject::tr("Updating title %1...\nThis can take a while.") + .arg(title_id, 16, 16, QLatin1Char('0'))); + }, + true); + return !was_cancelled.IsSet(); + }, + region); + Core::QueueHostJob([&dialog] { dialog.close(); }, true); + return res; + }); + + dialog.exec(); + + switch (result.get()) + { + case WiiUtils::UpdateResult::Succeeded: + QMessageBox::information(parent, QObject::tr("Update completed"), + QObject::tr("The emulated Wii console has been updated.")); + DiscIO::NANDImporter().ExtractCertificates(File::GetUserPath(D_WIIROOT_IDX)); + break; + case WiiUtils::UpdateResult::AlreadyUpToDate: + QMessageBox::information(parent, QObject::tr("Update completed"), + QObject::tr("The emulated Wii console is already up-to-date.")); + DiscIO::NANDImporter().ExtractCertificates(File::GetUserPath(D_WIIROOT_IDX)); + break; + case WiiUtils::UpdateResult::ServerFailed: + QMessageBox::critical(parent, QObject::tr("Update failed"), + QObject::tr("Could not download update information from Nintendo. " + "Please check your Internet connection and try again.")); + break; + case WiiUtils::UpdateResult::DownloadFailed: + QMessageBox::critical(parent, QObject::tr("Update failed"), + QObject::tr("Could not download update files from Nintendo. " + "Please check your Internet connection and try again.")); + break; + case WiiUtils::UpdateResult::ImportFailed: + QMessageBox::critical(parent, QObject::tr("Update failed"), + QObject::tr("Could not install an update to the Wii system memory. " + "Please refer to logs for more information.")); + break; + case WiiUtils::UpdateResult::Cancelled: + QMessageBox::warning( + parent, QObject::tr("Update cancelled"), + QObject::tr("The update has been cancelled. It is strongly recommended to " + "finish it in order to avoid inconsistent system software versions.")); + break; + } +} +}; // namespace WiiUpdate diff --git a/Source/Core/DolphinQt2/WiiUpdate.h b/Source/Core/DolphinQt2/WiiUpdate.h new file mode 100644 index 0000000000..ea7428ebc2 --- /dev/null +++ b/Source/Core/DolphinQt2/WiiUpdate.h @@ -0,0 +1,14 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include + +class QWidget; + +namespace WiiUpdate +{ +void PerformOnlineUpdate(const std::string& region, QWidget* parent = nullptr); +}; // namespace WiiUpdate From 4dcfa5371a38e19db9195c360b3fe38cd4a6bfea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Sat, 17 Jun 2017 12:41:34 +0200 Subject: [PATCH 5/5] IOS/ES: Don't open store in rw mode in WriteNewCertToStore Causes different issues on different platforms. r+b is broken on Windows for some reason. a+b is broken on macOS. --- Source/Core/Core/IOS/ES/ES.cpp | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/Source/Core/Core/IOS/ES/ES.cpp b/Source/Core/Core/IOS/ES/ES.cpp index 88aa3e28c0..a0f3241e85 100644 --- a/Source/Core/Core/IOS/ES/ES.cpp +++ b/Source/Core/Core/IOS/ES/ES.cpp @@ -843,30 +843,21 @@ ReturnCode ES::ReadCertStore(std::vector* buffer) const ReturnCode ES::WriteNewCertToStore(const IOS::ES::CertReader& cert) { - const std::string store_path = Common::RootUserPath(Common::FROM_SESSION_ROOT) + "/sys/cert.sys"; - // The certificate store file may not exist, so we use a+b and not r+b here. - File::IOFile store_file{store_path, "a+b"}; - if (!store_file) - return ES_EIO; - // Read the current store to determine if the new cert needs to be written. - const u64 file_size = store_file.GetSize(); - if (file_size != 0) + std::vector current_store; + const ReturnCode ret = ReadCertStore(¤t_store); + if (ret == IPC_SUCCESS) { - std::vector certs_bytes(file_size); - if (!store_file.ReadBytes(certs_bytes.data(), certs_bytes.size())) - return ES_SHORT_READ; - - const std::map certs = IOS::ES::ParseCertChain(certs_bytes); + const std::map certs = IOS::ES::ParseCertChain(current_store); // The cert is already present in the store. Nothing to do. if (certs.find(cert.GetName()) != certs.end()) return IPC_SUCCESS; } // Otherwise, write the new cert at the end of the store. - // When opening a file in read-write mode, a seek is required before a write. - store_file.Seek(0, SEEK_END); - if (!store_file.WriteBytes(cert.GetBytes().data(), cert.GetBytes().size())) + const std::string store_path = Common::RootUserPath(Common::FROM_SESSION_ROOT) + "/sys/cert.sys"; + File::IOFile store_file{store_path, "ab"}; + if (!store_file || !store_file.WriteBytes(cert.GetBytes().data(), cert.GetBytes().size())) return ES_EIO; return IPC_SUCCESS; }