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/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; } diff --git a/Source/Core/Core/WiiUtils.cpp b/Source/Core/Core/WiiUtils.cpp new file mode 100644 index 0000000000..0e8a8fd253 --- /dev/null +++ b/Source/Core/Core/WiiUtils.cpp @@ -0,0 +1,498 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#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" + +namespace WiiUtils +{ +bool InstallWAD(const std::string& wad_path) +{ + const DiscIO::WiiWAD wad{wad_path}; + if (!wad.IsValid()) + { + PanicAlertT("WAD installation failed: The selected file is not a valid WAD."); + return false; + } + + const auto tmd = wad.GetTMD(); + IOS::HLE::Kernel ios; + const auto es = ios.GetES(); + + IOS::HLE::Device::ES::Context context; + IOS::HLE::ReturnCode ret; + const bool checks_enabled = SConfig::GetInstance().m_enable_signature_checks; + while ((ret = es->ImportTicket(wad.GetTicket().GetBytes(), wad.GetCertificateChain())) < 0 || + (ret = es->ImportTitleInit(context, tmd.GetBytes(), wad.GetCertificateChain())) < 0) + { + if (checks_enabled && ret == IOS::HLE::IOSC_FAIL_CHECKVALUE && + AskYesNoT("This WAD has not been signed by Nintendo. Continue to import?")) + { + SConfig::GetInstance().m_enable_signature_checks = false; + continue; + } + + SConfig::GetInstance().m_enable_signature_checks = checks_enabled; + PanicAlertT("WAD installation failed: Could not initialise title import."); + return false; + } + SConfig::GetInstance().m_enable_signature_checks = checks_enabled; + + const bool contents_imported = [&]() { + const u64 title_id = tmd.GetTitleId(); + for (const IOS::ES::Content& content : tmd.GetContents()) + { + const std::vector data = wad.GetContent(content.index); + + if (es->ImportContentBegin(context, title_id, content.id) < 0 || + es->ImportContentData(context, 0, data.data(), static_cast(data.size())) < 0 || + es->ImportContentEnd(context, 0) < 0) + { + PanicAlertT("WAD installation failed: Could not import content %08x.", content.id); + return false; + } + } + return true; + }(); + + if ((contents_imported && es->ImportTitleDone(context) < 0) || + (!contents_imported && es->ImportTitleCancel(context) < 0)) + { + PanicAlertT("WAD installation failed: Could not finalise title import."); + return false; + } + + 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 new file mode 100644 index 0000000000..3d0eff6b9a --- /dev/null +++ b/Source/Core/Core/WiiUtils.h @@ -0,0 +1,40 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#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); +} 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/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/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/UICommon/WiiUtils.h b/Source/Core/DolphinQt2/WiiUpdate.h similarity index 51% rename from Source/Core/UICommon/WiiUtils.h rename to Source/Core/DolphinQt2/WiiUpdate.h index 8419e01b9b..ea7428ebc2 100644 --- a/Source/Core/UICommon/WiiUtils.h +++ b/Source/Core/DolphinQt2/WiiUpdate.h @@ -6,9 +6,9 @@ #include -// Small utility functions for common Wii related tasks. +class QWidget; -namespace WiiUtils +namespace WiiUpdate { -bool InstallWAD(const std::string& wad_path); -} +void PerformOnlineUpdate(const std::string& region, QWidget* parent = nullptr); +}; // namespace WiiUpdate 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 b33ed2cf2f..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 @@ -54,6 +56,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 +90,6 @@ #include "InputCommon/ControllerInterface/ControllerInterface.h" #include "UICommon/UICommon.h" -#include "UICommon/WiiUtils.h" #include "VideoCommon/RenderBase.h" #include "VideoCommon/VideoBackendBase.h" @@ -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; 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 + diff --git a/Source/Core/UICommon/WiiUtils.cpp b/Source/Core/UICommon/WiiUtils.cpp deleted file mode 100644 index 994079f623..0000000000 --- a/Source/Core/UICommon/WiiUtils.cpp +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2017 Dolphin Emulator Project -// Licensed under GPLv2+ -// Refer to the license.txt file included. - -#include "UICommon/WiiUtils.h" -#include "Common/CommonTypes.h" -#include "Common/MsgHandler.h" -#include "Core/ConfigManager.h" -#include "Core/IOS/ES/ES.h" -#include "Core/IOS/ES/Formats.h" -#include "Core/IOS/IOS.h" -#include "DiscIO/NANDContentLoader.h" -#include "DiscIO/WiiWad.h" - -namespace WiiUtils -{ -bool InstallWAD(const std::string& wad_path) -{ - const DiscIO::WiiWAD wad{wad_path}; - if (!wad.IsValid()) - { - PanicAlertT("WAD installation failed: The selected file is not a valid WAD."); - return false; - } - - const auto tmd = wad.GetTMD(); - IOS::HLE::Kernel ios; - const auto es = ios.GetES(); - - IOS::HLE::Device::ES::Context context; - IOS::HLE::ReturnCode ret; - const bool checks_enabled = SConfig::GetInstance().m_enable_signature_checks; - while ((ret = es->ImportTicket(wad.GetTicket().GetBytes(), wad.GetCertificateChain())) < 0 || - (ret = es->ImportTitleInit(context, tmd.GetBytes(), wad.GetCertificateChain())) < 0) - { - if (checks_enabled && ret == IOS::HLE::IOSC_FAIL_CHECKVALUE && - AskYesNoT("This WAD has not been signed by Nintendo. Continue to import?")) - { - SConfig::GetInstance().m_enable_signature_checks = false; - continue; - } - - SConfig::GetInstance().m_enable_signature_checks = checks_enabled; - PanicAlertT("WAD installation failed: Could not initialise title import."); - return false; - } - SConfig::GetInstance().m_enable_signature_checks = checks_enabled; - - const bool contents_imported = [&]() { - const u64 title_id = tmd.GetTitleId(); - for (const IOS::ES::Content& content : tmd.GetContents()) - { - const std::vector data = wad.GetContent(content.index); - - if (es->ImportContentBegin(context, title_id, content.id) < 0 || - es->ImportContentData(context, 0, data.data(), static_cast(data.size())) < 0 || - es->ImportContentEnd(context, 0) < 0) - { - PanicAlertT("WAD installation failed: Could not import content %08x.", content.id); - return false; - } - } - return true; - }(); - - if ((contents_imported && es->ImportTitleDone(context) < 0) || - (!contents_imported && es->ImportTitleCancel(context) < 0)) - { - PanicAlertT("WAD installation failed: Could not finalise title import."); - return false; - } - - DiscIO::NANDContentManager::Access().ClearCache(); - return true; -} -}