From 529b696fcbce385beb89aeb47bb479eeb15b558c Mon Sep 17 00:00:00 2001 From: Lilly Jade Katrin Date: Fri, 20 Mar 2026 21:37:09 -0400 Subject: [PATCH 1/5] Remove AchievementManager ChangeDisc call from Eject This was causing the achievement runtime to shutdown in the middle of a disc change even if the resulting disc was valid, which was causing achievements like the ones in Resident Evil for completing the entire game in a single session to break midway through. Also adds a log to notify the user when the disc change is invalid (e.g. different game inserted). --- Source/Core/Core/AchievementManager.cpp | 2 ++ Source/Core/Core/HW/DVD/DVDInterface.cpp | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/Core/Core/AchievementManager.cpp b/Source/Core/Core/AchievementManager.cpp index a88fb463e1..ab9974f2cd 100644 --- a/Source/Core/Core/AchievementManager.cpp +++ b/Source/Core/Core/AchievementManager.cpp @@ -168,6 +168,8 @@ void AchievementManager::LoadGame(const DiscIO::Volume* volume) WARN_LOG_FMT(ACHIEVEMENTS, "Software format unsupported by AchievementManager."); if (rc_client_get_game_info(m_client)) { + OSD::AddMessage("Unsupported media change; disabling achievements.", OSD::Duration::VERY_LONG, + OSD::Color::RED); CloseGame(); } else diff --git a/Source/Core/Core/HW/DVD/DVDInterface.cpp b/Source/Core/Core/HW/DVD/DVDInterface.cpp index 376aeb4add..640023344e 100644 --- a/Source/Core/Core/HW/DVD/DVDInterface.cpp +++ b/Source/Core/Core/HW/DVD/DVDInterface.cpp @@ -442,7 +442,6 @@ void DVDInterface::AutoChangeDiscCallback(Core::System& system, u64 userdata, s6 void DVDInterface::EjectDiscCallback(Core::System& system, u64 userdata, s64 cyclesLate) { - AchievementManager::GetInstance().ChangeDisc(nullptr); system.GetDVDInterface().SetDisc(nullptr, {}); } From 69a5e516ed7f1b737b3a7f6dc012b9c7850118bd Mon Sep 17 00:00:00 2001 From: Dentomologist Date: Fri, 3 Apr 2026 16:18:19 -0700 Subject: [PATCH 2/5] BreakpointWidget: Fix drawing of icon when breakpoints are disabled Fix incorrect scaling of the gap drawn inside enabled breakpoints in BreakpointWidget when breakpoints are globally disabled and the font size is changed from the default. As the font size increased the gap would become larger and its center would "migrate" downward and rightward until the gap stopped being visible at all. Additionally, at small font sizes integer truncation issues could result in the gap being offset towards the top left. --- Source/Core/DolphinQt/Debugger/BreakpointWidget.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Source/Core/DolphinQt/Debugger/BreakpointWidget.cpp b/Source/Core/DolphinQt/Debugger/BreakpointWidget.cpp index fbb5c38d9b..06cee537a6 100644 --- a/Source/Core/DolphinQt/Debugger/BreakpointWidget.cpp +++ b/Source/Core/DolphinQt/Debugger/BreakpointWidget.cpp @@ -306,9 +306,14 @@ void BreakpointWidget::Update() painter.setRenderHint(QPainter::Antialiasing, true); painter.setPen(Qt::NoPen); painter.setBrush(Qt::transparent); - // Center and radius - painter.drawEllipse(QPoint(downscale / 2, downscale / 2), downscale / 4, downscale / 4); + + const float icon_radius = static_cast(image.height()) / 2.0f; + const float gap_radius = icon_radius / 2.0f; + const QPointF center(icon_radius, icon_radius); + + painter.drawEllipse(center, gap_radius, gap_radius); painter.end(); + enabled_icon = QPixmap::fromImage(image); } From 7e98245ca47ff2ea0203930049cc725b4c433f72 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sun, 5 Apr 2026 11:27:56 +0200 Subject: [PATCH 3/5] DolphinQt: Remove Presets from GameConfigEdit Back in 2018, a Presets system was added to DolphinQt's GameConfigEdit. Presets was a dropdown menu where you could select a particular setting to add to your custom game INI. Only a small number of settings were made available, with the intent that more would be added over time. 8 years later, the set of available settings hasn't been expanded at all, and I don't know of anyone who uses these presets. On top of this, we have now made good progress in exposing per-game settings graphically. I think the Presets system is best off removed. In place of the Presets menu, we now have "Refresh" and "Open in External Editor" buttons. These more useful actions were previously hidden away in the Presets menu. --- .../Core/DolphinQt/Config/GameConfigEdit.cpp | 109 +++--------------- Source/Core/DolphinQt/Config/GameConfigEdit.h | 9 +- 2 files changed, 16 insertions(+), 102 deletions(-) diff --git a/Source/Core/DolphinQt/Config/GameConfigEdit.cpp b/Source/Core/DolphinQt/Config/GameConfigEdit.cpp index 3c49fcae05..e43c222293 100644 --- a/Source/Core/DolphinQt/Config/GameConfigEdit.cpp +++ b/Source/Core/DolphinQt/Config/GameConfigEdit.cpp @@ -7,9 +7,8 @@ #include #include #include +#include #include -#include -#include #include #include #include @@ -72,7 +71,6 @@ GameConfigEdit::GameConfigEdit(QWidget* parent, QString path, bool read_only) m_completer->setCompletionMode(QCompleter::PopupCompletion); m_completer->setWidget(m_edit); - AddMenubarOptions(); ConnectWidgets(); } @@ -82,17 +80,15 @@ void GameConfigEdit::CreateWidgets() m_edit->setReadOnly(m_read_only); m_edit->setAcceptRichText(false); + m_refresh_button = new QPushButton(tr("Refresh")); + m_external_editor_button = new QPushButton(tr("Open in External Editor")); + + auto* button_layout = new QHBoxLayout; + button_layout->addWidget(m_refresh_button); + button_layout->addWidget(m_external_editor_button); + auto* layout = new QVBoxLayout; - - auto* menu_button = new QPushButton; - - menu_button->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed); - menu_button->setText(tr("Presets")); - - m_menu = new QMenu(menu_button); - menu_button->setMenu(m_menu); - - layout->addWidget(menu_button); + layout->addLayout(button_layout); layout->addWidget(m_edit); setLayout(layout); @@ -138,6 +134,10 @@ void GameConfigEdit::ConnectWidgets() connect(m_edit, &QTextEdit::selectionChanged, this, &GameConfigEdit::OnSelectionChanged); connect(m_completer, qOverload(&QCompleter::activated), this, &GameConfigEdit::OnAutoComplete); + + connect(m_refresh_button, &QPushButton::clicked, this, &GameConfigEdit::LoadFile); + connect(m_external_editor_button, &QPushButton::clicked, this, + &GameConfigEdit::OpenExternalEditor); } void GameConfigEdit::OnSelectionChanged() @@ -148,47 +148,6 @@ void GameConfigEdit::OnSelectionChanged() QWhatsThis::showText(QCursor::pos(), m_keyword_map[keyword], this); } -void GameConfigEdit::AddBoolOption(QMenu* menu, const QString& name, const QString& section, - const QString& key) -{ - auto* option = menu->addMenu(name); - - option->addAction(tr("On"), this, - [this, section, key] { SetOption(section, key, QStringLiteral("True")); }); - option->addAction(tr("Off"), this, - [this, section, key] { SetOption(section, key, QStringLiteral("False")); }); -} - -void GameConfigEdit::SetOption(const QString& section, const QString& key, const QString& value) -{ - auto section_cursor = - m_edit->document()->find(QRegularExpression(QStringLiteral("^\\[%1\\]").arg(section)), 0); - - // Check if the section this belongs in can be found - if (section_cursor.isNull()) - { - m_edit->append(QStringLiteral("[%1]\n\n%2 = %3\n").arg(section).arg(key).arg(value)); - } - else - { - auto value_cursor = m_edit->document()->find( - QRegularExpression(QStringLiteral("^%1 = .*").arg(key)), section_cursor); - - const QString new_line = QStringLiteral("%1 = %2").arg(key).arg(value); - - // Check if the value that has to be set already exists - if (value_cursor.isNull()) - { - section_cursor.clearSelection(); - section_cursor.insertText(QLatin1Char{'\n'} + new_line); - } - else - { - value_cursor.insertText(new_line); - } - } -} - QString GameConfigEdit::GetTextUnderCursor() { QTextCursor tc = m_edit->textCursor(); @@ -196,48 +155,6 @@ QString GameConfigEdit::GetTextUnderCursor() return tc.selectedText(); } -void GameConfigEdit::AddMenubarOptions() -{ - auto* editor = m_menu->addMenu(tr("Editor")); - - editor->addAction(tr("Refresh"), this, &GameConfigEdit::LoadFile); - editor->addAction(tr("Open in External Editor"), this, &GameConfigEdit::OpenExternalEditor); - - if (!m_read_only) - { - m_menu->addSeparator(); - auto* core_menubar = m_menu->addMenu(tr("Core")); - - AddBoolOption(core_menubar, tr("Dual Core"), QStringLiteral("Core"), - QStringLiteral("CPUThread")); - AddBoolOption(core_menubar, tr("MMU"), QStringLiteral("Core"), QStringLiteral("MMU")); - - auto* video_menubar = m_menu->addMenu(tr("Video")); - - AddBoolOption(video_menubar, tr("Store EFB Copies to Texture Only"), - QStringLiteral("Video_Hacks"), QStringLiteral("EFBToTextureEnable")); - - AddBoolOption(video_menubar, tr("Store XFB Copies to Texture Only"), - QStringLiteral("Video_Hacks"), QStringLiteral("XFBToTextureEnable")); - - { - auto* texture_cache = video_menubar->addMenu(tr("Texture Cache")); - texture_cache->addAction(tr("Safe"), this, [this] { - SetOption(QStringLiteral("Video_Settings"), QStringLiteral("SafeTextureCacheColorSamples"), - QStringLiteral("0")); - }); - texture_cache->addAction(tr("Medium"), this, [this] { - SetOption(QStringLiteral("Video_Settings"), QStringLiteral("SafeTextureCacheColorSamples"), - QStringLiteral("512")); - }); - texture_cache->addAction(tr("Fast"), this, [this] { - SetOption(QStringLiteral("Video_Settings"), QStringLiteral("SafeTextureCacheColorSamples"), - QStringLiteral("128")); - }); - } - } -} - void GameConfigEdit::OnAutoComplete(const QString& completion) { QTextCursor cursor = m_edit->textCursor(); diff --git a/Source/Core/DolphinQt/Config/GameConfigEdit.h b/Source/Core/DolphinQt/Config/GameConfigEdit.h index 1fceaedb8a..73184c2b13 100644 --- a/Source/Core/DolphinQt/Config/GameConfigEdit.h +++ b/Source/Core/DolphinQt/Config/GameConfigEdit.h @@ -4,6 +4,7 @@ #pragma once #include +#include #include #include #include @@ -24,7 +25,6 @@ protected: private: void CreateWidgets(); void ConnectWidgets(); - void AddMenubarOptions(); void LoadFile(); void SaveFile(); @@ -35,15 +35,12 @@ private: QString GetTextUnderCursor(); - void AddBoolOption(QMenu* menu, const QString& name, const QString& section, const QString& key); - - void SetOption(const QString& section, const QString& key, const QString& value); - void AddDescription(const QString& keyword, const QString& description); QCompleter* m_completer; QStringList m_completions; - QMenu* m_menu; + QPushButton* m_refresh_button; + QPushButton* m_external_editor_button; QTextEdit* m_edit; const QString m_path; From 5fcdb6f8f9a5a0cb5fbc3e0e201119b832eef03c Mon Sep 17 00:00:00 2001 From: JosJuice Date: Mon, 6 Apr 2026 17:59:45 +0200 Subject: [PATCH 4/5] DolphinQt: Hide buttons for read-only game config The Open in External Editor button makes editing a game INI file easy, and the Refresh button is only useful if you edit the game INI. You're not supposed to edit read-only game INIs, so let's not show these two buttons for read-only game INIs. I implemented this by hiding the buttons instead of not creating them in the first place so we don't have to branch on whether the buttons exist in GameConfigEdit::ConnectWidgets. --- Source/Core/DolphinQt/Config/GameConfigEdit.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Source/Core/DolphinQt/Config/GameConfigEdit.cpp b/Source/Core/DolphinQt/Config/GameConfigEdit.cpp index e43c222293..9d9f79848e 100644 --- a/Source/Core/DolphinQt/Config/GameConfigEdit.cpp +++ b/Source/Core/DolphinQt/Config/GameConfigEdit.cpp @@ -83,6 +83,12 @@ void GameConfigEdit::CreateWidgets() m_refresh_button = new QPushButton(tr("Refresh")); m_external_editor_button = new QPushButton(tr("Open in External Editor")); + if (m_read_only) + { + m_refresh_button->hide(); + m_external_editor_button->hide(); + } + auto* button_layout = new QHBoxLayout; button_layout->addWidget(m_refresh_button); button_layout->addWidget(m_external_editor_button); From f8fead401c0f399ed2f98a62ade600860f8dbd7d Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Mon, 6 Apr 2026 15:05:23 -0500 Subject: [PATCH 5/5] Mixer: Fix WAV file dumping. --- Source/Core/AudioCommon/Mixer.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/Source/Core/AudioCommon/Mixer.cpp b/Source/Core/AudioCommon/Mixer.cpp index daf7e08820..1229d8faa1 100644 --- a/Source/Core/AudioCommon/Mixer.cpp +++ b/Source/Core/AudioCommon/Mixer.cpp @@ -214,10 +214,11 @@ void Mixer::PushSamples(const s16* samples, std::size_t num_samples) { // Big-endian RL-orderered stereo samples. - while (num_samples--) + const s16* ptr = samples; + for (std::size_t i = 0; i != num_samples; ++i) { - m_dma_mixer.PushSample(Common::swap16(samples[1]), Common::swap16(samples[0])); - samples += 2; + m_dma_mixer.PushSample(Common::swap16(ptr[1]), Common::swap16(ptr[0])); + ptr += 2; } } @@ -236,10 +237,11 @@ void Mixer::PushStreamingSamples(const s16* samples, std::size_t num_samples) { // Big-endian RL-orderered stereo samples. - while (num_samples--) + const s16* ptr = samples; + for (std::size_t i = 0; i != num_samples; ++i) { - m_streaming_mixer.PushSample(Common::swap16(samples[1]), Common::swap16(samples[0])); - samples += 2; + m_streaming_mixer.PushSample(Common::swap16(ptr[1]), Common::swap16(ptr[0])); + ptr += 2; } }