From 2e8c5b4521d7a483d9895cd4294acd51636046d4 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Mon, 15 Jun 2020 13:16:01 +0200 Subject: [PATCH] DolphinQt: Show a warning when launching an NKit disc image It is my opinion that nobody should use NKit disc images without being aware of the drawbacks of them. Since it seems like almost nobody who is using NKit disc images knows what NKit is (hmm, now how could that have happened...?), I am adding a warning to Dolphin so that you can't run NKit disc images without finding out about the drawbacks. In case someone really does want to use NKit disc images, the warning has a "Don't show this again" option. Unfortunately, I can't retroactively add the warning where it's most needed: in Dolphin 5.0, which does not support Wii NKit disc images. --- Source/Core/Core/Config/MainSettings.cpp | 5 ++ Source/Core/Core/Config/MainSettings.h | 4 + .../Core/ConfigLoaders/IsSettingSaveable.cpp | 6 +- Source/Core/DiscIO/Volume.h | 1 + Source/Core/DiscIO/VolumeDisc.cpp | 6 ++ Source/Core/DiscIO/VolumeDisc.h | 1 + Source/Core/DiscIO/VolumeVerifier.cpp | 24 +++-- Source/Core/DiscIO/VolumeWad.cpp | 5 ++ Source/Core/DiscIO/VolumeWad.h | 1 + Source/Core/DolphinQt/CMakeLists.txt | 2 + Source/Core/DolphinQt/DolphinQt.vcxproj | 3 + Source/Core/DolphinQt/MainWindow.cpp | 11 +++ Source/Core/DolphinQt/NKitWarningDialog.cpp | 87 +++++++++++++++++++ Source/Core/DolphinQt/NKitWarningDialog.h | 19 ++++ 14 files changed, 160 insertions(+), 15 deletions(-) create mode 100644 Source/Core/DolphinQt/NKitWarningDialog.cpp create mode 100644 Source/Core/DolphinQt/NKitWarningDialog.h diff --git a/Source/Core/Core/Config/MainSettings.cpp b/Source/Core/Core/Config/MainSettings.cpp index 1c97e21cdbc..21e8efaec58 100644 --- a/Source/Core/Core/Config/MainSettings.cpp +++ b/Source/Core/Core/Config/MainSettings.cpp @@ -142,6 +142,7 @@ const Info MAIN_FS_PATH{{System::Main, "General", "NANDRootPath"}, const Info MAIN_SD_PATH{{System::Main, "General", "WiiSDCardPath"}, ""}; // Main.Network + const Info MAIN_NETWORK_SSL_DUMP_READ{{System::Main, "Network", "SSLDumpRead"}, false}; const Info MAIN_NETWORK_SSL_DUMP_WRITE{{System::Main, "Network", "SSLDumpWrite"}, false}; const Info MAIN_NETWORK_SSL_VERIFY_CERTIFICATES{ @@ -149,4 +150,8 @@ const Info MAIN_NETWORK_SSL_VERIFY_CERTIFICATES{ const Info MAIN_NETWORK_SSL_DUMP_ROOT_CA{{System::Main, "Network", "SSLDumpRootCA"}, false}; const Info MAIN_NETWORK_SSL_DUMP_PEER_CERT{{System::Main, "Network", "SSLDumpPeerCert"}, false}; + +// Main.Interface + +const Info MAIN_SKIP_NKIT_WARNING{{System::Main, "Interface", "SkipNKitWarning"}, false}; } // namespace Config diff --git a/Source/Core/Core/Config/MainSettings.h b/Source/Core/Core/Config/MainSettings.h index 61f59069ece..086e18419a7 100644 --- a/Source/Core/Core/Config/MainSettings.h +++ b/Source/Core/Core/Config/MainSettings.h @@ -126,4 +126,8 @@ extern const Info MAIN_NETWORK_SSL_DUMP_WRITE; extern const Info MAIN_NETWORK_SSL_VERIFY_CERTIFICATES; extern const Info MAIN_NETWORK_SSL_DUMP_ROOT_CA; extern const Info MAIN_NETWORK_SSL_DUMP_PEER_CERT; + +// Main.Interface + +extern const Info MAIN_SKIP_NKIT_WARNING; } // namespace Config diff --git a/Source/Core/Core/ConfigLoaders/IsSettingSaveable.cpp b/Source/Core/Core/ConfigLoaders/IsSettingSaveable.cpp index dff293772c4..dcf608ac615 100644 --- a/Source/Core/Core/ConfigLoaders/IsSettingSaveable.cpp +++ b/Source/Core/Core/ConfigLoaders/IsSettingSaveable.cpp @@ -32,7 +32,7 @@ bool IsSettingSaveable(const Config::Location& config_location) } } - static constexpr std::array s_setting_saveable = { + static constexpr std::array s_setting_saveable = { // Main.Core &Config::MAIN_DEFAULT_ISO.location, @@ -47,6 +47,10 @@ bool IsSettingSaveable(const Config::Location& config_location) &Config::MAIN_MEM2_SIZE.location, &Config::MAIN_GFX_BACKEND.location, + // Main.Interface + + &Config::MAIN_SKIP_NKIT_WARNING.location, + // UI.General &Config::MAIN_USE_DISCORD_PRESENCE.location, diff --git a/Source/Core/DiscIO/Volume.h b/Source/Core/DiscIO/Volume.h index a5c4e6be14e..e176c5d72a1 100644 --- a/Source/Core/DiscIO/Volume.h +++ b/Source/Core/DiscIO/Volume.h @@ -116,6 +116,7 @@ public: } virtual Platform GetVolumeType() const = 0; virtual bool IsDatelDisc() const = 0; + virtual bool IsNKit() const = 0; virtual bool SupportsIntegrityCheck() const { return false; } virtual bool CheckH3TableIntegrity(const Partition& partition) const { return false; } virtual bool CheckBlockIntegrity(u64 block_index, const std::vector& encrypted_data, diff --git a/Source/Core/DiscIO/VolumeDisc.cpp b/Source/Core/DiscIO/VolumeDisc.cpp index bf622bb8fb4..fed82a8067f 100644 --- a/Source/Core/DiscIO/VolumeDisc.cpp +++ b/Source/Core/DiscIO/VolumeDisc.cpp @@ -84,4 +84,10 @@ std::optional VolumeDisc::GetDiscNumber(const Partition& partition) const return ReadSwapped(6, partition); } +bool VolumeDisc::IsNKit() const +{ + constexpr u32 NKIT_MAGIC = 0x4E4B4954; // "NKIT" + return ReadSwapped(0x200, PARTITION_NONE) == NKIT_MAGIC; +} + } // namespace DiscIO diff --git a/Source/Core/DiscIO/VolumeDisc.h b/Source/Core/DiscIO/VolumeDisc.h index 333ea7d4aaf..680bbf83b05 100644 --- a/Source/Core/DiscIO/VolumeDisc.h +++ b/Source/Core/DiscIO/VolumeDisc.h @@ -22,6 +22,7 @@ public: std::string GetInternalName(const Partition& partition = PARTITION_NONE) const override; std::string GetApploaderDate(const Partition& partition) const override; std::optional GetDiscNumber(const Partition& partition = PARTITION_NONE) const override; + bool IsNKit() const override; protected: Region RegionCodeToRegion(std::optional region_code) const; diff --git a/Source/Core/DiscIO/VolumeVerifier.cpp b/Source/Core/DiscIO/VolumeVerifier.cpp index 00e850071ab..c1a2bcbd90c 100644 --- a/Source/Core/DiscIO/VolumeVerifier.cpp +++ b/Source/Core/DiscIO/VolumeVerifier.cpp @@ -988,22 +988,18 @@ void VolumeVerifier::CheckMisc() } } - if (IsDisc(m_volume.GetVolumeType())) + if (m_volume.IsNKit()) { - constexpr u32 NKIT_MAGIC = 0x4E4B4954; // "NKIT" - if (m_volume.ReadSwapped(0x200, PARTITION_NONE) == NKIT_MAGIC) - { - AddProblem( - Severity::Low, - Common::GetStringT("This disc image is in the NKit format. It is not a good dump in its " - "current form, but it might become a good dump if converted back. " - "The CRC32 of this file might match the CRC32 of a good dump even " - "though the files are not identical.")); - } - - if (StringBeginsWith(game_id_unencrypted, "R8P")) - CheckSuperPaperMario(); + AddProblem( + Severity::Low, + Common::GetStringT("This disc image is in the NKit format. It is not a good dump in its " + "current form, but it might become a good dump if converted back. " + "The CRC32 of this file might match the CRC32 of a good dump even " + "though the files are not identical.")); } + + if (IsDisc(m_volume.GetVolumeType()) && StringBeginsWith(game_id_unencrypted, "R8P")) + CheckSuperPaperMario(); } void VolumeVerifier::CheckSuperPaperMario() diff --git a/Source/Core/DiscIO/VolumeWad.cpp b/Source/Core/DiscIO/VolumeWad.cpp index 61b772ec315..b7b8fa35cdc 100644 --- a/Source/Core/DiscIO/VolumeWad.cpp +++ b/Source/Core/DiscIO/VolumeWad.cpp @@ -289,6 +289,11 @@ bool VolumeWAD::IsDatelDisc() const return false; } +bool VolumeWAD::IsNKit() const +{ + return false; +} + std::map VolumeWAD::GetLongNames() const { if (!m_tmd.IsValid() || !IOS::ES::IsChannel(m_tmd.GetTitleId())) diff --git a/Source/Core/DiscIO/VolumeWad.h b/Source/Core/DiscIO/VolumeWad.h index d76b8853ecd..a4c311da78f 100644 --- a/Source/Core/DiscIO/VolumeWad.h +++ b/Source/Core/DiscIO/VolumeWad.h @@ -60,6 +60,7 @@ public: } Platform GetVolumeType() const override; bool IsDatelDisc() const override; + bool IsNKit() const override; Region GetRegion() const override; Country GetCountry(const Partition& partition = PARTITION_NONE) const override; diff --git a/Source/Core/DolphinQt/CMakeLists.txt b/Source/Core/DolphinQt/CMakeLists.txt index 269083c4dc2..8388f7bb6d4 100644 --- a/Source/Core/DolphinQt/CMakeLists.txt +++ b/Source/Core/DolphinQt/CMakeLists.txt @@ -37,6 +37,8 @@ add_executable(dolphin-emu MainWindow.h MenuBar.cpp MenuBar.h + NKitWarningDialog.cpp + NKitWarningDialog.h RenderWidget.cpp RenderWidget.h Resources.cpp diff --git a/Source/Core/DolphinQt/DolphinQt.vcxproj b/Source/Core/DolphinQt/DolphinQt.vcxproj index 1e4fd3b0b20..6fe7dfb10be 100644 --- a/Source/Core/DolphinQt/DolphinQt.vcxproj +++ b/Source/Core/DolphinQt/DolphinQt.vcxproj @@ -178,6 +178,7 @@ + @@ -290,6 +291,7 @@ + @@ -394,6 +396,7 @@ + diff --git a/Source/Core/DolphinQt/MainWindow.cpp b/Source/Core/DolphinQt/MainWindow.cpp index 1ecb875986d..bff3a6422cd 100644 --- a/Source/Core/DolphinQt/MainWindow.cpp +++ b/Source/Core/DolphinQt/MainWindow.cpp @@ -18,6 +18,7 @@ #include #include +#include #if defined(__unix__) || defined(__unix) || defined(__APPLE__) #include @@ -83,6 +84,7 @@ #include "DolphinQt/HotkeyScheduler.h" #include "DolphinQt/MainWindow.h" #include "DolphinQt/MenuBar.h" +#include "DolphinQt/NKitWarningDialog.h" #include "DolphinQt/NetPlay/NetPlayBrowser.h" #include "DolphinQt/NetPlay/NetPlayDialog.h" #include "DolphinQt/NetPlay/NetPlaySetupDialog.h" @@ -952,6 +954,15 @@ void MainWindow::StartGame(const std::vector& paths, void MainWindow::StartGame(std::unique_ptr&& parameters) { + if (std::holds_alternative(parameters->parameters)) + { + if (std::get(parameters->parameters).volume->IsNKit()) + { + if (!NKitWarningDialog::ShowUnlessDisabled()) + return; + } + } + // If we're running, only start a new game once we've stopped the last. if (Core::GetState() != Core::State::Uninitialized) { diff --git a/Source/Core/DolphinQt/NKitWarningDialog.cpp b/Source/Core/DolphinQt/NKitWarningDialog.cpp new file mode 100644 index 00000000000..3b8a821b8a6 --- /dev/null +++ b/Source/Core/DolphinQt/NKitWarningDialog.cpp @@ -0,0 +1,87 @@ +// Copyright 2020 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "DolphinQt/NKitWarningDialog.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Common/Config/Config.h" +#include "Core/Config/MainSettings.h" +#include "DolphinQt/Resources.h" + +bool NKitWarningDialog::ShowUnlessDisabled(QWidget* parent) +{ + if (Config::Get(Config::MAIN_SKIP_NKIT_WARNING)) + return true; + else + return NKitWarningDialog(parent).exec() == QDialog::Accepted; +} + +NKitWarningDialog::NKitWarningDialog(QWidget* parent) : QDialog(parent) +{ + setWindowTitle(tr("NKit Warning")); + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + setWindowIcon(Resources::GetAppIcon()); + + QVBoxLayout* main_layout = new QVBoxLayout; + + QLabel* warning = new QLabel( + tr("You are about to run an NKit disc image. NKit disc images cause problems that don't " + "happen with normal disc images. These problems include:\n" + "\n" + "• The emulated loading times are longer\n" + "• You can't use NetPlay with people who have normal disc images\n" + "• Input recordings are not compatible between NKit disc images and normal disc images\n" + "• Savestates are not compatible between NKit disc images and normal disc images\n" + "• Some games crash, such as Super Paper Mario\n" + "• Wii games don't work at all in older versions of Dolphin and in many other programs\n" + "\n" + "Are you sure you want to continue anyway?\n")); + warning->setWordWrap(true); + main_layout->addWidget(warning); + + QCheckBox* checkbox_accept = new QCheckBox(tr("I am aware of the risks and want to continue")); + main_layout->addWidget(checkbox_accept); + + QCheckBox* checkbox_skip = new QCheckBox(tr("Don't show this again")); + main_layout->addWidget(checkbox_skip); + + QHBoxLayout* button_layout = new QHBoxLayout; + QPushButton* ok = new QPushButton(tr("OK")); + button_layout->addWidget(ok); + QPushButton* cancel = new QPushButton(tr("Cancel")); + button_layout->addWidget(cancel); + main_layout->addLayout(button_layout); + + QHBoxLayout* top_layout = new QHBoxLayout; + + QIcon icon = QApplication::style()->standardIcon(QStyle::SP_MessageBoxWarning); + QLabel* icon_label = new QLabel; + icon_label->setPixmap(icon.pixmap(100)); + icon_label->setAlignment(Qt::AlignTop); + top_layout->addWidget(icon_label); + top_layout->addSpacing(10); + + top_layout->addLayout(main_layout); + + setLayout(top_layout); + + connect(ok, &QPushButton::clicked, this, &QDialog::accept); + connect(cancel, &QPushButton::clicked, this, &QDialog::reject); + + ok->setEnabled(false); + connect(checkbox_accept, &QCheckBox::stateChanged, + [&ok](int state) { ok->setEnabled(state == Qt::Checked); }); + + connect(this, &QDialog::accepted, [checkbox_skip] { + Config::SetBase(Config::MAIN_SKIP_NKIT_WARNING, checkbox_skip->isChecked()); + }); +} diff --git a/Source/Core/DolphinQt/NKitWarningDialog.h b/Source/Core/DolphinQt/NKitWarningDialog.h new file mode 100644 index 00000000000..df82ab5332b --- /dev/null +++ b/Source/Core/DolphinQt/NKitWarningDialog.h @@ -0,0 +1,19 @@ +// Copyright 2020 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include +#include + +class NKitWarningDialog final : public QDialog +{ + Q_OBJECT + +public: + static bool ShowUnlessDisabled(QWidget* parent = nullptr); + +private: + explicit NKitWarningDialog(QWidget* parent = nullptr); +};