This commit is contained in:
Nayla Hanegan 2024-05-12 02:17:59 -04:00
commit 98c174edc4
520 changed files with 74815 additions and 58942 deletions

View file

@ -17,6 +17,16 @@ AboutDialog::AboutDialog(QWidget* parent) : QDialog(parent)
setWindowTitle(tr("About Dolphin"));
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
QString branch_str = QString::fromStdString(Common::GetScmBranchStr());
const int commits_ahead = Common::GetScmCommitsAheadMaster();
if (commits_ahead > 0)
{
branch_str = tr("%1 (%2)").arg(
branch_str,
// i18n: A positive number of version control commits made compared to some named branch
tr("%1 commit(s) ahead of %2").arg(commits_ahead).arg(QStringLiteral("master")));
}
const QString text =
QStringLiteral(R"(
<p style='font-size:38pt; font-weight:400;'>Dolphin MPN</p>
@ -50,7 +60,7 @@ AboutDialog::AboutDialog(QWidget* parent) : QDialog(parent)
QString::fromUtf8(Common::GetScmDescStr().c_str()))
.replace(QStringLiteral("%BRANCH%"),
// i18n: "Branch" means the version control term, not a literal tree branch.
tr("Branch: %1").arg(QString::fromUtf8(Common::GetScmBranchStr().c_str())))
tr("Branch: %1").arg(branch_str))
.replace(QStringLiteral("%REVISION%"),
tr("Revision: %1").arg(QString::fromUtf8(Common::GetScmRevGitStr().c_str())))
.replace(QStringLiteral("%QT_VERSION%"),

View file

@ -0,0 +1,104 @@
// Copyright 2024 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#ifdef USE_RETRO_ACHIEVEMENTS
#include "DolphinQt/Achievements/AchievementBox.h"
#include <QDateTime>
#include <QHBoxLayout>
#include <QLabel>
#include <QProgressBar>
#include <QVBoxLayout>
#include <QWidget>
#include <rcheevos/include/rc_api_runtime.h>
#include "Core/AchievementManager.h"
#include "Core/Config/AchievementSettings.h"
#include "DolphinQt/QtUtils/FromStdString.h"
AchievementBox::AchievementBox(QWidget* parent, rc_client_achievement_t* achievement)
: QGroupBox(parent), m_achievement(achievement)
{
const auto& instance = AchievementManager::GetInstance();
if (!instance.IsGameLoaded())
return;
m_badge = new QLabel();
QLabel* title = new QLabel(QString::fromUtf8(achievement->title, strlen(achievement->title)));
QLabel* description =
new QLabel(QString::fromUtf8(achievement->description, strlen(achievement->description)));
QLabel* points = new QLabel(tr("%1 points").arg(achievement->points));
m_status = new QLabel();
m_progress_bar = new QProgressBar();
QSizePolicy sp_retain = m_progress_bar->sizePolicy();
sp_retain.setRetainSizeWhenHidden(true);
m_progress_bar->setSizePolicy(sp_retain);
QVBoxLayout* a_col_right = new QVBoxLayout();
a_col_right->addWidget(title);
a_col_right->addWidget(description);
a_col_right->addWidget(points);
a_col_right->addWidget(m_status);
a_col_right->addWidget(m_progress_bar);
QHBoxLayout* a_total = new QHBoxLayout();
a_total->addWidget(m_badge);
a_total->addLayout(a_col_right);
setLayout(a_total);
UpdateData();
}
void AchievementBox::UpdateData()
{
std::lock_guard lg{AchievementManager::GetInstance().GetLock()};
const auto& badge = AchievementManager::GetInstance().GetAchievementBadge(
m_achievement->id, m_achievement->state != RC_CLIENT_ACHIEVEMENT_STATE_UNLOCKED);
std::string_view color = AchievementManager::GRAY;
if (m_achievement->unlocked & RC_CLIENT_ACHIEVEMENT_UNLOCKED_HARDCORE)
color = AchievementManager::GOLD;
else if (m_achievement->unlocked & RC_CLIENT_ACHIEVEMENT_UNLOCKED_SOFTCORE)
color = AchievementManager::BLUE;
if (Config::Get(Config::RA_BADGES_ENABLED) && badge.name != "")
{
QImage i_badge{};
if (i_badge.loadFromData(&badge.badge.front(), static_cast<int>(badge.badge.size())))
{
m_badge->setPixmap(QPixmap::fromImage(i_badge).scaled(64, 64, Qt::KeepAspectRatio,
Qt::SmoothTransformation));
m_badge->adjustSize();
m_badge->setStyleSheet(
QStringLiteral("border: 4px solid %1").arg(QtUtils::FromStdString(color)));
}
}
else
{
m_badge->setText({});
}
if (m_achievement->state == RC_CLIENT_ACHIEVEMENT_STATE_UNLOCKED)
{
m_status->setText(
tr("Unlocked at %1")
.arg(QDateTime::fromSecsSinceEpoch(m_achievement->unlock_time).toString()));
}
else
{
m_status->setText(tr("Locked"));
}
if (m_achievement->measured_percent > 0.000)
{
m_progress_bar->setRange(0, 100);
m_progress_bar->setValue(m_achievement->measured_percent);
m_progress_bar->setVisible(true);
}
else
{
m_progress_bar->setVisible(false);
}
}
#endif // USE_RETRO_ACHIEVEMENTS

View file

@ -0,0 +1,32 @@
// Copyright 2024 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#ifdef USE_RETRO_ACHIEVEMENTS
#include <QGroupBox>
#include "Core/AchievementManager.h"
class QLabel;
class QProgressBar;
class QWidget;
struct rc_api_achievement_definition_t;
class AchievementBox final : public QGroupBox
{
Q_OBJECT
public:
explicit AchievementBox(QWidget* parent, rc_client_achievement_t* achievement);
void UpdateData();
private:
QLabel* m_badge;
QLabel* m_status;
QProgressBar* m_progress_bar;
rc_client_achievement_t* m_achievement;
};
#endif // USE_RETRO_ACHIEVEMENTS

View file

@ -11,10 +11,13 @@
#include <QString>
#include <QVBoxLayout>
#include <rcheevos/include/rc_client.h>
#include "Core/AchievementManager.h"
#include "Core/Config/AchievementSettings.h"
#include "Core/Core.h"
#include "DolphinQt/QtUtils/FromStdString.h"
#include "DolphinQt/Settings.h"
AchievementHeaderWidget::AchievementHeaderWidget(QWidget* parent) : QWidget(parent)
@ -23,21 +26,12 @@ AchievementHeaderWidget::AchievementHeaderWidget(QWidget* parent) : QWidget(pare
m_game_icon = new QLabel();
m_name = new QLabel();
m_points = new QLabel();
m_game_progress_hard = new QProgressBar();
m_game_progress_soft = new QProgressBar();
m_game_progress = new QProgressBar();
m_rich_presence = new QLabel();
m_locked_warning = new QLabel();
m_locked_warning->setText(tr("Achievements have been disabled.<br>Please close all running "
"games to re-enable achievements."));
m_locked_warning->setStyleSheet(QStringLiteral("QLabel { color : red; }"));
QSizePolicy sp_retain = m_game_progress_hard->sizePolicy();
QSizePolicy sp_retain = m_game_progress->sizePolicy();
sp_retain.setRetainSizeWhenHidden(true);
m_game_progress_hard->setSizePolicy(sp_retain);
sp_retain = m_game_progress_soft->sizePolicy();
sp_retain.setRetainSizeWhenHidden(true);
m_game_progress_soft->setSizePolicy(sp_retain);
m_game_progress->setSizePolicy(sp_retain);
QVBoxLayout* icon_col = new QVBoxLayout();
icon_col->addWidget(m_user_icon);
@ -45,10 +39,8 @@ AchievementHeaderWidget::AchievementHeaderWidget(QWidget* parent) : QWidget(pare
QVBoxLayout* text_col = new QVBoxLayout();
text_col->addWidget(m_name);
text_col->addWidget(m_points);
text_col->addWidget(m_game_progress_hard);
text_col->addWidget(m_game_progress_soft);
text_col->addWidget(m_game_progress);
text_col->addWidget(m_rich_presence);
text_col->addWidget(m_locked_warning);
QHBoxLayout* header_layout = new QHBoxLayout();
header_layout->addLayout(icon_col);
header_layout->addLayout(text_col);
@ -61,50 +53,49 @@ AchievementHeaderWidget::AchievementHeaderWidget(QWidget* parent) : QWidget(pare
m_total->setContentsMargins(0, 0, 0, 0);
m_total->setAlignment(Qt::AlignTop);
setLayout(m_total);
std::lock_guard lg{AchievementManager::GetInstance().GetLock()};
UpdateData();
}
void AchievementHeaderWidget::UpdateData()
{
std::lock_guard lg{AchievementManager::GetInstance().GetLock()};
auto& instance = AchievementManager::GetInstance();
if (!instance.IsLoggedIn())
if (!Config::Get(Config::RA_ENABLED) || !instance.HasAPIToken())
{
m_header_box->setVisible(false);
return;
}
m_header_box->setVisible(true);
AchievementManager::PointSpread point_spread = instance.TallyScore();
QString user_name = QString::fromStdString(instance.GetPlayerDisplayName());
QString game_name = QString::fromStdString(instance.GetGameDisplayName());
QString user_name = QtUtils::FromStdString(instance.GetPlayerDisplayName());
QString game_name = QtUtils::FromStdString(instance.GetGameDisplayName());
AchievementManager::BadgeStatus player_badge = instance.GetPlayerBadge();
AchievementManager::BadgeStatus game_badge = instance.GetGameBadge();
m_user_icon->setVisible(false);
m_user_icon->clear();
m_user_icon->setText({});
if (Config::Get(Config::RA_BADGES_ENABLED))
if (Config::Get(Config::RA_BADGES_ENABLED) && !player_badge.name.empty())
{
if (!player_badge.name.empty())
QImage i_user_icon{};
if (i_user_icon.loadFromData(&player_badge.badge.front(), (int)player_badge.badge.size()))
{
QImage i_user_icon{};
if (i_user_icon.loadFromData(&player_badge.badge.front(), (int)player_badge.badge.size()))
{
m_user_icon->setPixmap(QPixmap::fromImage(i_user_icon)
.scaled(64, 64, Qt::KeepAspectRatio, Qt::SmoothTransformation));
m_user_icon->adjustSize();
m_user_icon->setStyleSheet(QStringLiteral("border: 4px solid transparent"));
m_user_icon->setVisible(true);
}
m_user_icon->setPixmap(QPixmap::fromImage(i_user_icon)
.scaled(64, 64, Qt::KeepAspectRatio, Qt::SmoothTransformation));
m_user_icon->adjustSize();
m_user_icon->setStyleSheet(QStringLiteral("border: 4px solid transparent"));
m_user_icon->setVisible(true);
}
}
m_game_icon->setVisible(false);
m_game_icon->clear();
m_game_icon->setText({});
if (Config::Get(Config::RA_BADGES_ENABLED))
if (instance.IsGameLoaded())
{
if (!game_badge.name.empty())
rc_client_user_game_summary_t game_summary;
rc_client_get_user_game_summary(instance.GetClient(), &game_summary);
if (Config::Get(Config::RA_BADGES_ENABLED) && !game_badge.name.empty())
{
QImage i_game_icon{};
if (i_game_icon.loadFromData(&game_badge.badge.front(), (int)game_badge.badge.size()))
@ -113,70 +104,39 @@ void AchievementHeaderWidget::UpdateData()
.scaled(64, 64, Qt::KeepAspectRatio, Qt::SmoothTransformation));
m_game_icon->adjustSize();
std::string_view color = AchievementManager::GRAY;
if (point_spread.hard_unlocks == point_spread.total_count)
color = AchievementManager::GOLD;
else if (point_spread.hard_unlocks + point_spread.soft_unlocks == point_spread.total_count)
color = AchievementManager::BLUE;
if (game_summary.num_core_achievements == game_summary.num_unlocked_achievements)
{
color =
instance.IsHardcoreModeActive() ? AchievementManager::GOLD : AchievementManager::BLUE;
}
m_game_icon->setStyleSheet(
QStringLiteral("border: 4px solid %1").arg(QString::fromStdString(std::string(color))));
QStringLiteral("border: 4px solid %1").arg(QtUtils::FromStdString(color)));
m_game_icon->setVisible(true);
}
}
}
if (!game_name.isEmpty())
{
m_name->setText(tr("%1 is playing %2").arg(user_name).arg(game_name));
m_points->setText(GetPointsString(user_name, point_spread));
m_points->setText(tr("%1 has unlocked %2/%3 achievements worth %4/%5 points")
.arg(user_name)
.arg(game_summary.num_unlocked_achievements)
.arg(game_summary.num_core_achievements)
.arg(game_summary.points_unlocked)
.arg(game_summary.points_core));
m_game_progress_hard->setRange(0, point_spread.total_count);
if (!m_game_progress_hard->isVisible())
m_game_progress_hard->setVisible(true);
m_game_progress_hard->setValue(point_spread.hard_unlocks);
m_game_progress_soft->setRange(0, point_spread.total_count);
m_game_progress_soft->setValue(point_spread.hard_unlocks + point_spread.soft_unlocks);
if (!m_game_progress_soft->isVisible())
m_game_progress_soft->setVisible(true);
m_game_progress->setRange(0, game_summary.num_core_achievements);
if (!m_game_progress->isVisible())
m_game_progress->setVisible(true);
m_game_progress->setValue(game_summary.num_unlocked_achievements);
m_rich_presence->setText(QString::fromUtf8(instance.GetRichPresence().data()));
if (!m_rich_presence->isVisible())
m_rich_presence->setVisible(Config::Get(Config::RA_RICH_PRESENCE_ENABLED));
m_locked_warning->setVisible(false);
m_rich_presence->setVisible(true);
}
else
{
m_name->setText(user_name);
m_points->setText(tr("%1 points").arg(instance.GetPlayerScore()));
m_game_progress_hard->setVisible(false);
m_game_progress_soft->setVisible(false);
m_game_progress->setVisible(false);
m_rich_presence->setVisible(false);
m_locked_warning->setVisible(instance.IsDisabled());
}
}
QString
AchievementHeaderWidget::GetPointsString(const QString& user_name,
const AchievementManager::PointSpread& point_spread) const
{
if (point_spread.soft_points > 0)
{
return tr("%1 has unlocked %2/%3 achievements (%4 hardcore) worth %5/%6 points (%7 hardcore)")
.arg(user_name)
.arg(point_spread.hard_unlocks + point_spread.soft_unlocks)
.arg(point_spread.total_count)
.arg(point_spread.hard_unlocks)
.arg(point_spread.hard_points + point_spread.soft_points)
.arg(point_spread.total_points)
.arg(point_spread.hard_points);
}
else
{
return tr("%1 has unlocked %2/%3 achievements worth %4/%5 points")
.arg(user_name)
.arg(point_spread.hard_unlocks)
.arg(point_spread.total_count)
.arg(point_spread.hard_points)
.arg(point_spread.total_points);
}
}

View file

@ -20,17 +20,12 @@ public:
void UpdateData();
private:
QString GetPointsString(const QString& user_name,
const AchievementManager::PointSpread& point_spread) const;
QLabel* m_user_icon;
QLabel* m_game_icon;
QLabel* m_name;
QLabel* m_points;
QProgressBar* m_game_progress_hard;
QProgressBar* m_game_progress_soft;
QProgressBar* m_game_progress;
QLabel* m_rich_presence;
QLabel* m_locked_warning;
QGroupBox* m_header_box;
};

View file

@ -24,11 +24,6 @@ AchievementLeaderboardWidget::AchievementLeaderboardWidget(QWidget* parent) : QW
m_common_box = new QGroupBox();
m_common_layout = new QGridLayout();
{
std::lock_guard lg{AchievementManager::GetInstance().GetLock()};
UpdateData();
}
m_common_box->setLayout(m_common_layout);
auto* layout = new QVBoxLayout;
@ -38,77 +33,126 @@ AchievementLeaderboardWidget::AchievementLeaderboardWidget(QWidget* parent) : QW
setLayout(layout);
}
void AchievementLeaderboardWidget::UpdateData()
void AchievementLeaderboardWidget::UpdateData(bool clean_all)
{
ClearLayoutRecursively(m_common_layout);
if (!AchievementManager::GetInstance().IsGameLoaded())
return;
const auto& leaderboards = AchievementManager::GetInstance().GetLeaderboardsInfo();
int row = 0;
for (const auto& board_row : leaderboards)
if (clean_all)
{
const AchievementManager::LeaderboardStatus& board = board_row.second;
QLabel* a_title = new QLabel(QString::fromStdString(board.name));
QLabel* a_description = new QLabel(QString::fromStdString(board.description));
QVBoxLayout* a_col_left = new QVBoxLayout();
a_col_left->addWidget(a_title);
a_col_left->addWidget(a_description);
if (row > 0)
ClearLayoutRecursively(m_common_layout);
auto& instance = AchievementManager::GetInstance();
if (!instance.IsGameLoaded())
return;
auto* client = instance.GetClient();
auto* leaderboard_list =
rc_client_create_leaderboard_list(client, RC_CLIENT_LEADERBOARD_LIST_GROUPING_NONE);
u32 row = 0;
for (u32 bucket = 0; bucket < leaderboard_list->num_buckets; bucket++)
{
QFrame* a_divider = new QFrame();
a_divider->setFrameShape(QFrame::HLine);
m_common_layout->addWidget(a_divider, row - 1, 0);
}
m_common_layout->addLayout(a_col_left, row, 0);
// Each leaderboard entry is displayed with four values. These are *generally* intended to be,
// in order, the first place entry, the entry one above the player, the player's entry, and
// the entry one below the player.
// Edge cases:
// * If there are fewer than four entries in the leaderboard, all entries will be shown in
// order and the remainder of the list will be padded with empty values.
// * If the player does not currently have a score in the leaderboard, or is in the top 3,
// the four slots will be the top four players in order.
// * If the player is last place, the player will be in the fourth slot, and the second and
// third slots will be the two players above them. The first slot will always be first place.
std::array<u32, 4> to_display{1, 2, 3, 4};
if (board.player_index > to_display.size() - 1)
{
// If the rank one below than the player is found, offset = 1.
u32 offset = static_cast<u32>(board.entries.count(board.player_index + 1));
// Example: player is 10th place but not last
// to_display = {1, 10-3+1+1, 10-3+1+2, 10-3+1+3} = {1, 9, 10, 11}
// Example: player is 15th place and is last
// to_display = {1, 15-3+0+1, 15-3+0+2, 15-3+0+3} = {1, 13, 14, 15}
for (size_t i = 1; i < to_display.size(); ++i)
to_display[i] = board.player_index - 3 + offset + static_cast<u32>(i);
}
for (size_t i = 0; i < to_display.size(); ++i)
{
u32 index = to_display[i];
QLabel* a_rank = new QLabel(QStringLiteral("---"));
QLabel* a_username = new QLabel(QStringLiteral("---"));
QLabel* a_score = new QLabel(QStringLiteral("---"));
const auto it = board.entries.find(index);
if (it != board.entries.end())
const auto& leaderboard_bucket = leaderboard_list->buckets[bucket];
for (u32 board = 0; board < leaderboard_bucket.num_leaderboards; board++)
{
a_rank->setText(tr("Rank %1").arg(it->second.rank));
a_username->setText(QString::fromStdString(it->second.username));
a_score->setText(QString::fromUtf8(it->second.score.data()));
const auto* leaderboard = leaderboard_bucket.leaderboards[board];
m_leaderboard_order[leaderboard->id] = row;
QLabel* a_title = new QLabel(QString::fromUtf8(leaderboard->title));
QLabel* a_description = new QLabel(QString::fromUtf8(leaderboard->description));
QVBoxLayout* a_col_left = new QVBoxLayout();
a_col_left->addWidget(a_title);
a_col_left->addWidget(a_description);
if (row > 0)
{
QFrame* a_divider = new QFrame();
a_divider->setFrameShape(QFrame::HLine);
m_common_layout->addWidget(a_divider, row - 1, 0);
}
m_common_layout->addLayout(a_col_left, row, 0);
for (size_t ix = 0; ix < 4; ix++)
{
QVBoxLayout* a_col = new QVBoxLayout();
for (size_t jx = 0; jx < 3; jx++)
a_col->addWidget(new QLabel(QStringLiteral("---")));
if (row > 0)
{
QFrame* a_divider = new QFrame();
a_divider->setFrameShape(QFrame::HLine);
m_common_layout->addWidget(a_divider, row - 1, static_cast<int>(ix) + 1);
}
m_common_layout->addLayout(a_col, row, static_cast<int>(ix) + 1);
}
row += 2;
}
}
rc_client_destroy_leaderboard_list(leaderboard_list);
}
for (auto row : m_leaderboard_order)
{
UpdateRow(row.second);
}
}
void AchievementLeaderboardWidget::UpdateData(
const std::set<AchievementManager::AchievementId>& update_ids)
{
for (auto row : m_leaderboard_order)
{
if (update_ids.contains(row.first))
{
UpdateRow(row.second);
}
}
}
void AchievementLeaderboardWidget::UpdateRow(AchievementManager::AchievementId leaderboard_id)
{
const auto leaderboard_itr = m_leaderboard_order.find(leaderboard_id);
if (leaderboard_itr == m_leaderboard_order.end())
return;
const int row = leaderboard_itr->second;
const AchievementManager::LeaderboardStatus* board;
{
std::lock_guard lg{AchievementManager::GetInstance().GetLock()};
board = AchievementManager::GetInstance().GetLeaderboardInfo(leaderboard_id);
}
if (!board)
return;
// Each leaderboard entry is displayed with four values. These are *generally* intended to be,
// in order, the first place entry, the entry one above the player, the player's entry, and
// the entry one below the player.
// Edge cases:
// * If there are fewer than four entries in the leaderboard, all entries will be shown in
// order and the remainder of the list will be padded with empty values.
// * If the player does not currently have a score in the leaderboard, or is in the top 3,
// the four slots will be the top four players in order.
// * If the player is last place, the player will be in the fourth slot, and the second and
// third slots will be the two players above them. The first slot will always be first place.
std::array<u32, 4> to_display{1, 2, 3, 4};
if (board->player_index > to_display.size() - 1)
{
// If the rank one below than the player is found, offset = 1.
u32 offset = static_cast<u32>(board->entries.count(board->player_index + 1));
// Example: player is 10th place but not last
// to_display = {1, 10-3+1+1, 10-3+1+2, 10-3+1+3} = {1, 9, 10, 11}
// Example: player is 15th place and is last
// to_display = {1, 15-3+0+1, 15-3+0+2, 15-3+0+3} = {1, 13, 14, 15}
for (size_t ix = 1; ix < to_display.size(); ++ix)
to_display[ix] = board->player_index - 3 + offset + static_cast<u32>(ix);
}
for (size_t ix = 0; ix < to_display.size(); ++ix)
{
const auto it = board->entries.find(to_display[ix]);
if (it != board->entries.end())
{
QVBoxLayout* a_col = new QVBoxLayout();
a_col->addWidget(a_rank);
a_col->addWidget(a_username);
a_col->addWidget(a_score);
if (row > 0)
{
QFrame* a_divider = new QFrame();
a_divider->setFrameShape(QFrame::HLine);
m_common_layout->addWidget(a_divider, row - 1, static_cast<int>(i) + 1);
}
m_common_layout->addLayout(a_col, row, static_cast<int>(i) + 1);
a_col->addWidget(new QLabel(tr("Rank %1").arg(it->second.rank)));
a_col->addWidget(new QLabel(QString::fromStdString(it->second.username)));
a_col->addWidget(new QLabel(QString::fromUtf8(it->second.score.data())));
auto old_item = m_common_layout->itemAtPosition(row, static_cast<int>(ix) + 1);
m_common_layout->removeItem(old_item);
ClearLayoutRecursively(static_cast<QLayout*>(old_item));
m_common_layout->addLayout(a_col, row, static_cast<int>(ix) + 1);
}
row += 2;
}
}

View file

@ -6,6 +6,8 @@
#ifdef USE_RETRO_ACHIEVEMENTS
#include <QWidget>
#include "Core/AchievementManager.h"
class QGroupBox;
class QGridLayout;
@ -14,11 +16,14 @@ class AchievementLeaderboardWidget final : public QWidget
Q_OBJECT
public:
explicit AchievementLeaderboardWidget(QWidget* parent);
void UpdateData();
void UpdateData(bool clean_all);
void UpdateData(const std::set<AchievementManager::AchievementId>& update_ids);
void UpdateRow(AchievementManager::AchievementId leaderboard_id);
private:
QGroupBox* m_common_box;
QGridLayout* m_common_layout;
std::map<AchievementManager::AchievementId, int> m_leaderboard_order;
};
#endif // USE_RETRO_ACHIEVEMENTS

View file

@ -18,21 +18,15 @@
#include "Core/Config/MainSettings.h"
#include "Core/Core.h"
#include "DolphinQt/Achievements/AchievementBox.h"
#include "DolphinQt/QtUtils/ClearLayoutRecursively.h"
#include "DolphinQt/Settings.h"
static constexpr bool hardcore_mode_enabled = false;
AchievementProgressWidget::AchievementProgressWidget(QWidget* parent) : QWidget(parent)
{
m_common_box = new QGroupBox();
m_common_layout = new QVBoxLayout();
{
std::lock_guard lg{AchievementManager::GetInstance().GetLock()};
UpdateData();
}
m_common_box->setLayout(m_common_layout);
auto* layout = new QVBoxLayout;
@ -42,124 +36,50 @@ AchievementProgressWidget::AchievementProgressWidget(QWidget* parent) : QWidget(
setLayout(layout);
}
QGroupBox*
AchievementProgressWidget::CreateAchievementBox(const rc_api_achievement_definition_t* achievement)
void AchievementProgressWidget::UpdateData(bool clean_all)
{
const auto& instance = AchievementManager::GetInstance();
if (!instance.IsGameLoaded())
return new QGroupBox();
if (clean_all)
{
m_achievement_boxes.clear();
ClearLayoutRecursively(m_common_layout);
QLabel* a_badge = new QLabel();
const auto unlock_status = instance.GetUnlockStatus(achievement->id);
const AchievementManager::BadgeStatus* badge = &unlock_status.locked_badge;
std::string_view color = AchievementManager::GRAY;
if (unlock_status.remote_unlock_status == AchievementManager::UnlockStatus::UnlockType::HARDCORE)
{
badge = &unlock_status.unlocked_badge;
color = AchievementManager::GOLD;
}
else if (hardcore_mode_enabled && unlock_status.session_unlock_count > 1)
{
badge = &unlock_status.unlocked_badge;
color = AchievementManager::GOLD;
}
else if (unlock_status.remote_unlock_status ==
AchievementManager::UnlockStatus::UnlockType::SOFTCORE)
{
badge = &unlock_status.unlocked_badge;
color = AchievementManager::BLUE;
}
else if (unlock_status.session_unlock_count > 1)
{
badge = &unlock_status.unlocked_badge;
color = AchievementManager::BLUE;
}
if (Config::Get(Config::RA_BADGES_ENABLED) && badge->name != "")
{
QImage i_badge{};
if (i_badge.loadFromData(&badge->badge.front(), (int)badge->badge.size()))
auto& instance = AchievementManager::GetInstance();
if (!instance.IsGameLoaded())
return;
auto* client = instance.GetClient();
auto* achievement_list = rc_client_create_achievement_list(
client, RC_CLIENT_ACHIEVEMENT_CATEGORY_CORE_AND_UNOFFICIAL,
RC_CLIENT_ACHIEVEMENT_LIST_GROUPING_LOCK_STATE);
for (u32 ix = 0; ix < achievement_list->num_buckets; ix++)
{
a_badge->setPixmap(QPixmap::fromImage(i_badge).scaled(64, 64, Qt::KeepAspectRatio,
Qt::SmoothTransformation));
a_badge->adjustSize();
a_badge->setStyleSheet(
QStringLiteral("border: 4px solid %1").arg(QString::fromStdString(std::string(color))));
for (u32 jx = 0; jx < achievement_list->buckets[ix].num_achievements; jx++)
{
auto* achievement = achievement_list->buckets[ix].achievements[jx];
m_achievement_boxes[achievement->id] = std::make_shared<AchievementBox>(this, achievement);
m_common_layout->addWidget(m_achievement_boxes[achievement->id].get());
}
}
}
QLabel* a_title = new QLabel(QString::fromUtf8(achievement->title, strlen(achievement->title)));
QLabel* a_description =
new QLabel(QString::fromUtf8(achievement->description, strlen(achievement->description)));
QLabel* a_points = new QLabel(tr("%1 points").arg(achievement->points));
QLabel* a_status = new QLabel(GetStatusString(achievement->id));
QProgressBar* a_progress_bar = new QProgressBar();
QSizePolicy sp_retain = a_progress_bar->sizePolicy();
sp_retain.setRetainSizeWhenHidden(true);
a_progress_bar->setSizePolicy(sp_retain);
unsigned int value = 0;
unsigned int target = 0;
if (AchievementManager::GetInstance().GetAchievementProgress(achievement->id, &value, &target) ==
AchievementManager::ResponseType::SUCCESS &&
target > 0)
{
a_progress_bar->setRange(0, target);
a_progress_bar->setValue(value);
rc_client_destroy_achievement_list(achievement_list);
}
else
{
a_progress_bar->setVisible(false);
}
QVBoxLayout* a_col_right = new QVBoxLayout();
a_col_right->addWidget(a_title);
a_col_right->addWidget(a_description);
a_col_right->addWidget(a_points);
a_col_right->addWidget(a_status);
a_col_right->addWidget(a_progress_bar);
QHBoxLayout* a_total = new QHBoxLayout();
a_total->addWidget(a_badge);
a_total->addLayout(a_col_right);
QGroupBox* a_group_box = new QGroupBox();
a_group_box->setLayout(a_total);
return a_group_box;
}
void AchievementProgressWidget::UpdateData()
{
ClearLayoutRecursively(m_common_layout);
auto& instance = AchievementManager::GetInstance();
if (!instance.IsGameLoaded())
return;
const auto* game_data = instance.GetGameData();
for (u32 ix = 0; ix < game_data->num_achievements; ix++)
{
m_common_layout->addWidget(CreateAchievementBox(game_data->achievements + ix));
}
}
QString AchievementProgressWidget::GetStatusString(u32 achievement_id) const
{
const auto unlock_status = AchievementManager::GetInstance().GetUnlockStatus(achievement_id);
if (unlock_status.session_unlock_count > 0)
{
if (Config::Get(Config::RA_ENCORE_ENABLED))
for (auto box : m_achievement_boxes)
{
return tr("Unlocked %1 times this session").arg(unlock_status.session_unlock_count);
box.second->UpdateData();
}
return tr("Unlocked this session");
}
switch (unlock_status.remote_unlock_status)
}
void AchievementProgressWidget::UpdateData(
const std::set<AchievementManager::AchievementId>& update_ids)
{
for (auto& [id, box] : m_achievement_boxes)
{
case AchievementManager::UnlockStatus::UnlockType::LOCKED:
return tr("Locked");
case AchievementManager::UnlockStatus::UnlockType::SOFTCORE:
return tr("Unlocked (Casual)");
case AchievementManager::UnlockStatus::UnlockType::HARDCORE:
return tr("Unlocked");
if (update_ids.contains(id))
{
box->UpdateData();
}
}
return {};
}
#endif // USE_RETRO_ACHIEVEMENTS

View file

@ -7,7 +7,9 @@
#include <QWidget>
#include "Common/CommonTypes.h"
#include "Core/AchievementManager.h"
class AchievementBox;
class QCheckBox;
class QGroupBox;
class QLineEdit;
@ -21,14 +23,13 @@ class AchievementProgressWidget final : public QWidget
Q_OBJECT
public:
explicit AchievementProgressWidget(QWidget* parent);
void UpdateData();
void UpdateData(bool clean_all);
void UpdateData(const std::set<AchievementManager::AchievementId>& update_ids);
private:
QGroupBox* CreateAchievementBox(const rc_api_achievement_definition_t* achievement);
QString GetStatusString(u32 achievement_id) const;
QGroupBox* m_common_box;
QVBoxLayout* m_common_layout;
std::map<AchievementManager::AchievementId, std::shared_ptr<AchievementBox>> m_achievement_boxes;
};
#endif // USE_RETRO_ACHIEVEMENTS

View file

@ -61,24 +61,6 @@ void AchievementSettingsWidget::CreateLayout()
m_common_login_failed = new QLabel(tr("Login Failed"));
m_common_login_failed->setStyleSheet(QStringLiteral("QLabel { color : red; }"));
m_common_login_failed->setVisible(false);
m_common_achievements_enabled_input = new ToolTipCheckBox(tr("Enable Achievements"));
m_common_achievements_enabled_input->SetDescription(tr("Enable unlocking achievements.<br>"));
m_common_leaderboards_enabled_input = new ToolTipCheckBox(tr("Enable Leaderboards"));
m_common_leaderboards_enabled_input->SetDescription(
tr("Enable competing in RetroAchievements leaderboards.<br><br>Hardcore Mode must be enabled "
"to use."));
m_common_rich_presence_enabled_input = new ToolTipCheckBox(tr("Enable Rich Presence"));
m_common_rich_presence_enabled_input->SetDescription(
tr("Enable detailed rich presence on the RetroAchievements website.<br><br>This provides a "
"detailed description of what the player is doing in game to the website. If this is "
"disabled, the website will only report what game is being played.<br><br>This has no "
"bearing on Discord rich presence."));
m_common_unofficial_enabled_input = new ToolTipCheckBox(tr("Enable Unofficial Achievements"));
m_common_unofficial_enabled_input->SetDescription(
tr("Enable unlocking unofficial achievements as well as official "
"achievements.<br><br>Unofficial achievements may be optional or unfinished achievements "
"that have not been deemed official by RetroAchievements and may be useful for testing or "
"simply for fun."));
m_common_hardcore_enabled_input = new ToolTipCheckBox(tr("Enable Hardcore Mode"));
m_common_hardcore_enabled_input->SetDescription(
tr("Enable Hardcore Mode on RetroAchievements.<br><br>Hardcore Mode is intended to provide "
@ -93,6 +75,25 @@ void AchievementSettingsWidget::CreateLayout()
"playing.</dolphin_emphasis><br>Close your current game before enabling.<br>Be aware that "
"turning Hardcore Mode off while a game is running requires the game to be closed before "
"re-enabling."));
m_common_unofficial_enabled_input = new ToolTipCheckBox(tr("Enable Unofficial Achievements"));
m_common_unofficial_enabled_input->SetDescription(
tr("Enable unlocking unofficial achievements as well as official "
"achievements.<br><br>Unofficial achievements may be optional or unfinished achievements "
"that have not been deemed official by RetroAchievements and may be useful for testing or "
"simply for fun.<br><br>Setting takes effect on next game load."));
m_common_encore_enabled_input = new ToolTipCheckBox(tr("Enable Encore Achievements"));
m_common_encore_enabled_input->SetDescription(
tr("Enable unlocking achievements in Encore Mode.<br><br>Encore Mode re-enables achievements "
"the player has already unlocked on the site so that the player will be notified if they "
"meet the unlock conditions again, useful for custom speedrun criteria or simply for fun."
"<br><br>Setting takes effect on next game load."));
m_common_spectator_enabled_input = new ToolTipCheckBox(tr("Enable Spectator Mode"));
m_common_spectator_enabled_input->SetDescription(
tr("Enable unlocking achievements in Spectator Mode.<br><br>While in Spectator Mode, "
"achievements and leaderboards will be processed and displayed on screen, but will not be "
"submitted to the server.<br><br>If this is on at game launch, it will not be turned off "
"until game close, because a RetroAchievements session will not be created.<br><br>If "
"this is off at game launch, it can be toggled freely while the game is running."));
m_common_progress_enabled_input = new ToolTipCheckBox(tr("Enable Progress Notifications"));
m_common_progress_enabled_input->SetDescription(
tr("Enable progress notifications on achievements.<br><br>Displays a brief popup message "
@ -103,11 +104,6 @@ void AchievementSettingsWidget::CreateLayout()
tr("Enable achievement badges.<br><br>Displays icons for the player, game, and achievements. "
"Simple visual option, but will require a small amount of extra memory and time to "
"download the images."));
m_common_encore_enabled_input = new ToolTipCheckBox(tr("Enable Encore Achievements"));
m_common_encore_enabled_input->SetDescription(tr(
"Enable unlocking achievements in Encore Mode.<br><br>Encore Mode re-enables achievements "
"the player has already unlocked on the site so that the player will be notified if they "
"meet the unlock conditions again, useful for custom speedrun criteria or simply for fun."));
m_common_layout->addWidget(m_common_integration_enabled_input);
m_common_layout->addWidget(m_common_username_label);
@ -117,14 +113,14 @@ void AchievementSettingsWidget::CreateLayout()
m_common_layout->addWidget(m_common_login_button);
m_common_layout->addWidget(m_common_logout_button);
m_common_layout->addWidget(m_common_login_failed);
m_common_layout->addWidget(m_common_achievements_enabled_input);
m_common_layout->addWidget(m_common_leaderboards_enabled_input);
m_common_layout->addWidget(m_common_rich_presence_enabled_input);
m_common_layout->addWidget(new QLabel(tr("Function Settings")));
m_common_layout->addWidget(m_common_hardcore_enabled_input);
m_common_layout->addWidget(m_common_progress_enabled_input);
m_common_layout->addWidget(m_common_badges_enabled_input);
m_common_layout->addWidget(m_common_unofficial_enabled_input);
m_common_layout->addWidget(m_common_encore_enabled_input);
m_common_layout->addWidget(m_common_spectator_enabled_input);
m_common_layout->addWidget(new QLabel(tr("Display Settings")));
m_common_layout->addWidget(m_common_progress_enabled_input);
m_common_layout->addWidget(m_common_badges_enabled_input);
m_common_layout->setAlignment(Qt::AlignTop);
setLayout(m_common_layout);
@ -136,22 +132,18 @@ void AchievementSettingsWidget::ConnectWidgets()
&AchievementSettingsWidget::ToggleRAIntegration);
connect(m_common_login_button, &QPushButton::pressed, this, &AchievementSettingsWidget::Login);
connect(m_common_logout_button, &QPushButton::pressed, this, &AchievementSettingsWidget::Logout);
connect(m_common_achievements_enabled_input, &QCheckBox::toggled, this,
&AchievementSettingsWidget::ToggleAchievements);
connect(m_common_leaderboards_enabled_input, &QCheckBox::toggled, this,
&AchievementSettingsWidget::ToggleLeaderboards);
connect(m_common_rich_presence_enabled_input, &QCheckBox::toggled, this,
&AchievementSettingsWidget::ToggleRichPresence);
connect(m_common_hardcore_enabled_input, &QCheckBox::toggled, this,
&AchievementSettingsWidget::ToggleHardcore);
connect(m_common_progress_enabled_input, &QCheckBox::toggled, this,
&AchievementSettingsWidget::ToggleProgress);
connect(m_common_badges_enabled_input, &QCheckBox::toggled, this,
&AchievementSettingsWidget::ToggleBadges);
connect(m_common_unofficial_enabled_input, &QCheckBox::toggled, this,
&AchievementSettingsWidget::ToggleUnofficial);
connect(m_common_encore_enabled_input, &QCheckBox::toggled, this,
&AchievementSettingsWidget::ToggleEncore);
connect(m_common_spectator_enabled_input, &QCheckBox::toggled, this,
&AchievementSettingsWidget::ToggleSpectator);
connect(m_common_progress_enabled_input, &QCheckBox::toggled, this,
&AchievementSettingsWidget::ToggleProgress);
connect(m_common_badges_enabled_input, &QCheckBox::toggled, this,
&AchievementSettingsWidget::ToggleBadges);
}
void AchievementSettingsWidget::OnControllerInterfaceConfigure()
@ -165,7 +157,6 @@ void AchievementSettingsWidget::OnControllerInterfaceConfigure()
void AchievementSettingsWidget::LoadSettings()
{
bool enabled = Config::Get(Config::RA_ENABLED);
bool achievements_enabled = Config::Get(Config::RA_ACHIEVEMENTS_ENABLED);
bool hardcore_enabled = Config::Get(Config::RA_HARDCORE_ENABLED);
bool logged_out = Config::Get(Config::RA_API_TOKEN).empty();
std::string username = Config::Get(Config::RA_USERNAME);
@ -180,41 +171,36 @@ void AchievementSettingsWidget::LoadSettings()
SignalBlocking(m_common_password_input)->setVisible(logged_out);
SignalBlocking(m_common_password_input)->setEnabled(enabled);
SignalBlocking(m_common_login_button)->setVisible(logged_out);
SignalBlocking(m_common_login_button)->setEnabled(enabled && !Core::IsRunning());
SignalBlocking(m_common_login_button)
->setEnabled(enabled && !Core::IsRunning(Core::System::GetInstance()));
SignalBlocking(m_common_logout_button)->setVisible(!logged_out);
SignalBlocking(m_common_logout_button)->setEnabled(enabled);
SignalBlocking(m_common_achievements_enabled_input)->setChecked(achievements_enabled);
SignalBlocking(m_common_achievements_enabled_input)->setEnabled(enabled);
SignalBlocking(m_common_leaderboards_enabled_input)
->setChecked(Config::Get(Config::RA_LEADERBOARDS_ENABLED));
SignalBlocking(m_common_leaderboards_enabled_input)->setEnabled(enabled && hardcore_enabled);
SignalBlocking(m_common_rich_presence_enabled_input)
->setChecked(Config::Get(Config::RA_RICH_PRESENCE_ENABLED));
SignalBlocking(m_common_rich_presence_enabled_input)->setEnabled(enabled);
SignalBlocking(m_common_hardcore_enabled_input)
->setChecked(Config::Get(Config::RA_HARDCORE_ENABLED));
auto& system = Core::System::GetInstance();
SignalBlocking(m_common_hardcore_enabled_input)
->setEnabled(enabled && (hardcore_enabled ||
(Core::GetState() == Core::State::Uninitialized &&
!Core::System::GetInstance().GetMovie().IsPlayingInput())));
SignalBlocking(m_common_progress_enabled_input)
->setChecked(Config::Get(Config::RA_PROGRESS_ENABLED));
SignalBlocking(m_common_progress_enabled_input)->setEnabled(enabled && achievements_enabled);
SignalBlocking(m_common_badges_enabled_input)->setChecked(Config::Get(Config::RA_BADGES_ENABLED));
SignalBlocking(m_common_badges_enabled_input)->setEnabled(enabled);
->setEnabled(enabled &&
(hardcore_enabled || (Core::GetState(system) == Core::State::Uninitialized &&
!system.GetMovie().IsPlayingInput())));
SignalBlocking(m_common_unofficial_enabled_input)
->setChecked(Config::Get(Config::RA_UNOFFICIAL_ENABLED));
SignalBlocking(m_common_unofficial_enabled_input)->setEnabled(enabled && achievements_enabled);
SignalBlocking(m_common_unofficial_enabled_input)->setEnabled(enabled);
SignalBlocking(m_common_encore_enabled_input)->setChecked(Config::Get(Config::RA_ENCORE_ENABLED));
SignalBlocking(m_common_encore_enabled_input)->setEnabled(enabled && achievements_enabled);
SignalBlocking(m_common_encore_enabled_input)->setEnabled(enabled);
SignalBlocking(m_common_spectator_enabled_input)
->setChecked(Config::Get(Config::RA_SPECTATOR_ENABLED));
SignalBlocking(m_common_spectator_enabled_input)->setEnabled(enabled);
SignalBlocking(m_common_progress_enabled_input)
->setChecked(Config::Get(Config::RA_PROGRESS_ENABLED));
SignalBlocking(m_common_progress_enabled_input)->setEnabled(enabled);
SignalBlocking(m_common_badges_enabled_input)->setChecked(Config::Get(Config::RA_BADGES_ENABLED));
SignalBlocking(m_common_badges_enabled_input)->setEnabled(enabled);
}
void AchievementSettingsWidget::SaveSettings()
@ -222,20 +208,16 @@ void AchievementSettingsWidget::SaveSettings()
Config::ConfigChangeCallbackGuard config_guard;
Config::SetBaseOrCurrent(Config::RA_ENABLED, m_common_integration_enabled_input->isChecked());
Config::SetBaseOrCurrent(Config::RA_ACHIEVEMENTS_ENABLED,
m_common_achievements_enabled_input->isChecked());
Config::SetBaseOrCurrent(Config::RA_LEADERBOARDS_ENABLED,
m_common_leaderboards_enabled_input->isChecked());
Config::SetBaseOrCurrent(Config::RA_RICH_PRESENCE_ENABLED,
m_common_rich_presence_enabled_input->isChecked());
Config::SetBaseOrCurrent(Config::RA_HARDCORE_ENABLED,
m_common_hardcore_enabled_input->isChecked());
Config::SetBaseOrCurrent(Config::RA_PROGRESS_ENABLED,
m_common_unofficial_enabled_input->isChecked());
Config::SetBaseOrCurrent(Config::RA_BADGES_ENABLED, m_common_badges_enabled_input->isChecked());
Config::SetBaseOrCurrent(Config::RA_UNOFFICIAL_ENABLED,
m_common_unofficial_enabled_input->isChecked());
Config::SetBaseOrCurrent(Config::RA_ENCORE_ENABLED, m_common_encore_enabled_input->isChecked());
Config::SetBaseOrCurrent(Config::RA_SPECTATOR_ENABLED,
m_common_spectator_enabled_input->isChecked());
Config::SetBaseOrCurrent(Config::RA_PROGRESS_ENABLED,
m_common_progress_enabled_input->isChecked());
Config::SetBaseOrCurrent(Config::RA_BADGES_ENABLED, m_common_badges_enabled_input->isChecked());
Config::Save();
}
@ -248,6 +230,8 @@ void AchievementSettingsWidget::ToggleRAIntegration()
instance.Init();
else
instance.Shutdown();
if (Config::Get(Config::RA_HARDCORE_ENABLED))
emit Settings::Instance().EmulationStateChanged(Core::GetState(Core::System::GetInstance()));
}
void AchievementSettingsWidget::Login()
@ -255,7 +239,6 @@ void AchievementSettingsWidget::Login()
Config::SetBaseOrCurrent(Config::RA_USERNAME, m_common_username_input->text().toStdString());
AchievementManager::GetInstance().Login(m_common_password_input->text().toStdString());
m_common_password_input->setText(QString());
m_common_login_failed->setVisible(Config::Get(Config::RA_API_TOKEN).empty());
SaveSettings();
}
@ -265,27 +248,10 @@ void AchievementSettingsWidget::Logout()
SaveSettings();
}
void AchievementSettingsWidget::ToggleAchievements()
{
SaveSettings();
AchievementManager::GetInstance().ActivateDeactivateAchievements();
}
void AchievementSettingsWidget::ToggleLeaderboards()
{
SaveSettings();
AchievementManager::GetInstance().ActivateDeactivateLeaderboards();
}
void AchievementSettingsWidget::ToggleRichPresence()
{
SaveSettings();
AchievementManager::GetInstance().ActivateDeactivateRichPresence();
}
void AchievementSettingsWidget::ToggleHardcore()
{
SaveSettings();
AchievementManager::GetInstance().SetHardcoreMode();
if (Config::Get(Config::RA_HARDCORE_ENABLED))
{
if (Config::Get(Config::MAIN_EMULATION_SPEED) < 1.0f)
@ -294,7 +260,23 @@ void AchievementSettingsWidget::ToggleHardcore()
Settings::Instance().SetCheatsEnabled(false);
Settings::Instance().SetDebugModeEnabled(false);
}
emit Settings::Instance().EmulationStateChanged(Core::GetState());
emit Settings::Instance().EmulationStateChanged(Core::GetState(Core::System::GetInstance()));
}
void AchievementSettingsWidget::ToggleUnofficial()
{
SaveSettings();
}
void AchievementSettingsWidget::ToggleEncore()
{
SaveSettings();
}
void AchievementSettingsWidget::ToggleSpectator()
{
SaveSettings();
AchievementManager::GetInstance().SetSpectatorMode();
}
void AchievementSettingsWidget::ToggleProgress()
@ -305,19 +287,8 @@ void AchievementSettingsWidget::ToggleProgress()
void AchievementSettingsWidget::ToggleBadges()
{
SaveSettings();
AchievementManager::GetInstance().FetchBadges();
}
void AchievementSettingsWidget::ToggleUnofficial()
{
SaveSettings();
AchievementManager::GetInstance().ActivateDeactivateAchievements();
}
void AchievementSettingsWidget::ToggleEncore()
{
SaveSettings();
AchievementManager::GetInstance().ActivateDeactivateAchievements();
AchievementManager::GetInstance().FetchPlayerBadge();
AchievementManager::GetInstance().FetchGameBadges();
}
#endif // USE_RETRO_ACHIEVEMENTS

View file

@ -32,14 +32,12 @@ private:
void ToggleRAIntegration();
void Login();
void Logout();
void ToggleAchievements();
void ToggleLeaderboards();
void ToggleRichPresence();
void ToggleHardcore();
void ToggleProgress();
void ToggleBadges();
void ToggleUnofficial();
void ToggleEncore();
void ToggleSpectator();
void ToggleProgress();
void ToggleBadges();
QGroupBox* m_common_box;
QVBoxLayout* m_common_layout;
@ -51,14 +49,12 @@ private:
QLineEdit* m_common_password_input;
QPushButton* m_common_login_button;
QPushButton* m_common_logout_button;
ToolTipCheckBox* m_common_achievements_enabled_input;
ToolTipCheckBox* m_common_leaderboards_enabled_input;
ToolTipCheckBox* m_common_rich_presence_enabled_input;
ToolTipCheckBox* m_common_hardcore_enabled_input;
ToolTipCheckBox* m_common_progress_enabled_input;
ToolTipCheckBox* m_common_badges_enabled_input;
ToolTipCheckBox* m_common_unofficial_enabled_input;
ToolTipCheckBox* m_common_encore_enabled_input;
ToolTipCheckBox* m_common_spectator_enabled_input;
ToolTipCheckBox* m_common_progress_enabled_input;
ToolTipCheckBox* m_common_badges_enabled_input;
};
#endif // USE_RETRO_ACHIEVEMENTS

View file

@ -28,11 +28,13 @@ AchievementsWindow::AchievementsWindow(QWidget* parent) : QDialog(parent)
CreateMainLayout();
ConnectWidgets();
AchievementManager::GetInstance().SetUpdateCallback(
[this] { QueueOnObject(this, &AchievementsWindow::UpdateData); });
[this](AchievementManager::UpdatedItems updated_items) {
QueueOnObject(this, [this, updated_items = std::move(updated_items)] {
AchievementsWindow::UpdateData(std::move(updated_items));
});
});
connect(&Settings::Instance(), &Settings::EmulationStateChanged, this,
&AchievementsWindow::UpdateData);
UpdateData();
[this] { AchievementsWindow::UpdateData({.all = true}); });
}
void AchievementsWindow::showEvent(QShowEvent* event)
@ -71,19 +73,38 @@ void AchievementsWindow::ConnectWidgets()
connect(m_button_box, &QDialogButtonBox::rejected, this, &QDialog::reject);
}
void AchievementsWindow::UpdateData()
void AchievementsWindow::UpdateData(AchievementManager::UpdatedItems updated_items)
{
m_settings_widget->UpdateData();
if (updated_items.all)
{
m_header_widget->UpdateData();
m_progress_widget->UpdateData(true);
m_leaderboard_widget->UpdateData(true);
}
else
{
if (updated_items.player_icon || updated_items.game_icon || updated_items.rich_presence ||
updated_items.all_achievements || updated_items.achievements.size() > 0)
{
m_header_widget->UpdateData();
}
if (updated_items.all_achievements)
m_progress_widget->UpdateData(false);
else if (updated_items.achievements.size() > 0)
m_progress_widget->UpdateData(updated_items.achievements);
if (updated_items.all_leaderboards)
m_leaderboard_widget->UpdateData(false);
else if (updated_items.leaderboards.size() > 0)
m_leaderboard_widget->UpdateData(updated_items.leaderboards);
}
{
auto& instance = AchievementManager::GetInstance();
std::lock_guard lg{instance.GetLock()};
const bool is_game_loaded = instance.IsGameLoaded();
m_header_widget->UpdateData();
m_header_widget->setVisible(instance.IsLoggedIn());
m_settings_widget->UpdateData();
m_progress_widget->UpdateData();
m_header_widget->setVisible(instance.HasAPIToken());
m_tab_widget->setTabVisible(1, is_game_loaded);
m_leaderboard_widget->UpdateData();
m_tab_widget->setTabVisible(2, is_game_loaded);
}
update();

View file

@ -6,6 +6,8 @@
#ifdef USE_RETRO_ACHIEVEMENTS
#include <QDialog>
#include "Core/AchievementManager.h"
class AchievementHeaderWidget;
class AchievementLeaderboardWidget;
class AchievementSettingsWidget;
@ -19,7 +21,7 @@ class AchievementsWindow : public QDialog
Q_OBJECT
public:
explicit AchievementsWindow(QWidget* parent);
void UpdateData();
void UpdateData(AchievementManager::UpdatedItems updated_items);
void ForceSettingsTab();
private:

View file

@ -28,6 +28,8 @@ add_executable(dolphin-mpn
CheatSearchWidget.h
CheatsManager.cpp
CheatsManager.h
Achievements/AchievementBox.cpp
Achievements/AchievementBox.h
Achievements/AchievementHeaderWidget.cpp
Achievements/AchievementHeaderWidget.h
Achievements/AchievementLeaderboardWidget.cpp
@ -303,6 +305,7 @@ add_executable(dolphin-mpn
QtUtils/ElidedButton.h
QtUtils/FileOpenEventFilter.cpp
QtUtils/FileOpenEventFilter.h
QtUtils/FromStdString.h
QtUtils/ImageConverter.cpp
QtUtils/ImageConverter.h
QtUtils/ModalMessageBox.cpp

View file

@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "DolphinQt/CheatSearchFactoryWidget.h"
#include "DolphinQt/QtUtils/WrapInScrollArea.h"
#include <string>
#include <vector>
@ -124,7 +125,7 @@ void CheatSearchFactoryWidget::CreateWidgets()
layout->addStretch();
setLayout(layout);
WrapInScrollArea(this, layout);
}
void CheatSearchFactoryWidget::ConnectWidgets()
@ -157,7 +158,8 @@ void CheatSearchFactoryWidget::OnNewSearchClicked()
PowerPC::RequestedAddressSpace address_space;
if (m_standard_address_space->isChecked())
{
const Core::State core_state = Core::GetState();
auto& system = Core::System::GetInstance();
const Core::State core_state = Core::GetState(system);
if (core_state != Core::State::Running && core_state != Core::State::Paused)
{
ModalMessageBox::warning(
@ -166,7 +168,6 @@ void CheatSearchFactoryWidget::OnNewSearchClicked()
return;
}
auto& system = Core::System::GetInstance();
auto& memory = system.GetMemory();
memory_ranges.emplace_back(0x80000000, memory.GetRamSizeReal());
if (system.IsWii())

View file

@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "DolphinQt/CheatSearchWidget.h"
#include "DolphinQt/QtUtils/WrapInScrollArea.h"
#include <functional>
#include <optional>
@ -54,8 +55,10 @@ constexpr int ADDRESS_TABLE_COLUMN_INDEX_ADDRESS = 1;
constexpr int ADDRESS_TABLE_COLUMN_INDEX_LAST_VALUE = 2;
constexpr int ADDRESS_TABLE_COLUMN_INDEX_CURRENT_VALUE = 3;
CheatSearchWidget::CheatSearchWidget(std::unique_ptr<Cheats::CheatSearchSessionBase> session)
: m_session(std::move(session))
CheatSearchWidget::CheatSearchWidget(Core::System& system,
std::unique_ptr<Cheats::CheatSearchSessionBase> session,
QWidget* parent)
: QWidget(parent), m_system(system), m_session(std::move(session))
{
setAttribute(Qt::WA_DeleteOnClose);
CreateWidgets();
@ -172,6 +175,7 @@ void CheatSearchWidget::CreateWidgets()
}
QString aligned = m_session->GetAligned() ? tr("aligned") : tr("unaligned");
session_info_label->setText(tr("%1, %2, %3, %4").arg(ranges).arg(space).arg(type).arg(aligned));
session_info_label->setWordWrap(true);
}
// i18n: This label is followed by a dropdown where the user can select things like "is equal to"
@ -254,7 +258,8 @@ void CheatSearchWidget::CreateWidgets()
layout->addWidget(m_info_label_1);
layout->addWidget(m_info_label_2);
layout->addWidget(m_address_table);
setLayout(layout);
WrapInScrollArea(this, layout);
}
void CheatSearchWidget::ConnectWidgets()
@ -275,13 +280,10 @@ void CheatSearchWidget::ConnectWidgets()
void CheatSearchWidget::OnNextScanClicked()
{
Core::CPUThreadGuard guard(Core::System::GetInstance());
const bool had_old_results = m_session->WasFirstSearchDone();
const size_t old_count = m_session->GetResultCount();
const auto filter_type = m_value_source_dropdown->currentData().value<Cheats::FilterType>();
if (filter_type == Cheats::FilterType::CompareAgainstLastValue &&
!m_session->WasFirstSearchDone())
if (filter_type == Cheats::FilterType::CompareAgainstLastValue && !had_old_results)
{
m_info_label_1->setText(tr("Cannot compare against last value on first search."));
return;
@ -301,7 +303,8 @@ void CheatSearchWidget::OnNextScanClicked()
}
}
const Cheats::SearchErrorCode error_code = m_session->RunSearch(guard);
const size_t old_count = m_session->GetResultCount();
const Cheats::SearchErrorCode error_code = m_session->RunSearch(Core::CPUThreadGuard{m_system});
if (error_code == Cheats::SearchErrorCode::Success)
{
@ -416,11 +419,11 @@ void CheatSearchWidget::UpdateTableVisibleCurrentValues(const UpdateSource sourc
if (source == UpdateSource::Auto && !m_autoupdate_current_values->isChecked())
return;
Core::CPUThreadGuard guard(Core::System::GetInstance());
if (m_address_table->rowCount() == 0)
return;
UpdateTableRows(guard, GetVisibleRowsBeginIndex(), GetVisibleRowsEndIndex(), source);
UpdateTableRows(Core::CPUThreadGuard{m_system}, GetVisibleRowsBeginIndex(),
GetVisibleRowsEndIndex(), source);
}
bool CheatSearchWidget::UpdateTableAllCurrentValues(const UpdateSource source)
@ -428,7 +431,6 @@ bool CheatSearchWidget::UpdateTableAllCurrentValues(const UpdateSource source)
if (source == UpdateSource::Auto && !m_autoupdate_current_values->isChecked())
return false;
Core::CPUThreadGuard guard(Core::System::GetInstance());
const size_t result_count = m_address_table->rowCount();
if (result_count == 0)
{
@ -437,7 +439,7 @@ bool CheatSearchWidget::UpdateTableAllCurrentValues(const UpdateSource source)
return false;
}
return UpdateTableRows(guard, 0, result_count, source);
return UpdateTableRows(Core::CPUThreadGuard{m_system}, 0, result_count, source);
}
void CheatSearchWidget::OnRefreshClicked()
@ -447,7 +449,6 @@ void CheatSearchWidget::OnRefreshClicked()
void CheatSearchWidget::OnResetClicked()
{
Core::CPUThreadGuard guard(Core::System::GetInstance());
m_session->ResetResults();
m_address_table_current_values.clear();
@ -494,13 +495,14 @@ void CheatSearchWidget::OnAddressTableContextMenu()
const u32 address = item->data(ADDRESS_TABLE_ADDRESS_ROLE).toUInt();
QMenu* menu = new QMenu(this);
menu->setAttribute(Qt::WA_DeleteOnClose, true);
menu->addAction(tr("Show in memory"), [this, address] { emit ShowMemory(address); });
menu->addAction(tr("Add to watch"), this, [this, address] {
const QString name = QStringLiteral("mem_%1").arg(address, 8, 16, QLatin1Char('0'));
emit RequestWatch(name, address);
});
menu->addAction(tr("Generate Action Replay Code"), this, &CheatSearchWidget::GenerateARCode);
menu->addAction(tr("Generate Action Replay Code(s)"), this, &CheatSearchWidget::GenerateARCodes);
menu->exec(QCursor::pos());
}
@ -519,41 +521,70 @@ void CheatSearchWidget::OnDisplayHexCheckboxStateChanged()
return;
// If the game is running CheatsManager::OnFrameEnd will update values automatically.
if (Core::GetState() != Core::State::Running)
if (Core::GetState(m_system) != Core::State::Running)
UpdateTableAllCurrentValues(UpdateSource::User);
}
void CheatSearchWidget::GenerateARCode()
void CheatSearchWidget::GenerateARCodes()
{
Core::CPUThreadGuard guard(Core::System::GetInstance());
if (m_address_table->selectedItems().isEmpty())
return;
auto* item = m_address_table->selectedItems()[0];
if (!item)
return;
bool had_success = false;
bool had_error = false;
std::optional<Cheats::GenerateActionReplayCodeErrorCode> error_code;
const u32 index = item->data(ADDRESS_TABLE_RESULT_INDEX_ROLE).toUInt();
auto result = Cheats::GenerateActionReplayCode(*m_session, index);
if (result)
for (auto* const item : m_address_table->selectedItems())
{
emit ActionReplayCodeGenerated(*result);
m_info_label_1->setText(tr("Generated AR code."));
}
else
{
switch (result.Error())
const u32 index = item->data(ADDRESS_TABLE_RESULT_INDEX_ROLE).toUInt();
auto result = Cheats::GenerateActionReplayCode(*m_session, index);
if (result)
{
case Cheats::GenerateActionReplayCodeErrorCode::NotVirtualMemory:
m_info_label_1->setText(tr("Can only generate AR code for values in virtual memory."));
break;
case Cheats::GenerateActionReplayCodeErrorCode::InvalidAddress:
m_info_label_1->setText(tr("Cannot generate AR code for this address."));
break;
default:
m_info_label_1->setText(tr("Internal error while generating AR code."));
break;
emit ActionReplayCodeGenerated(*result);
had_success = true;
}
else
{
const auto new_error_code = result.Error();
if (!had_error)
{
error_code = new_error_code;
}
else if (error_code != new_error_code)
{
// If we have a different error code signify multiple errors with an empty optional<>.
error_code.reset();
}
had_error = true;
}
}
if (had_error)
{
if (error_code.has_value())
{
switch (*error_code)
{
case Cheats::GenerateActionReplayCodeErrorCode::NotVirtualMemory:
m_info_label_1->setText(tr("Can only generate AR code for values in virtual memory."));
break;
case Cheats::GenerateActionReplayCodeErrorCode::InvalidAddress:
m_info_label_1->setText(tr("Cannot generate AR code for this address."));
break;
default:
m_info_label_1->setText(tr("Internal error while generating AR code."));
break;
}
}
else
{
m_info_label_1->setText(tr("Multiple errors while generating AR codes."));
}
}
else if (had_success)
{
m_info_label_1->setText(tr("Generated AR code(s)."));
}
}

View file

@ -18,6 +18,10 @@ namespace ActionReplay
{
struct ARCode;
}
namespace Core
{
class System;
}
class QCheckBox;
class QComboBox;
@ -36,7 +40,9 @@ class CheatSearchWidget : public QWidget
{
Q_OBJECT
public:
explicit CheatSearchWidget(std::unique_ptr<Cheats::CheatSearchSessionBase> session);
explicit CheatSearchWidget(Core::System& system,
std::unique_ptr<Cheats::CheatSearchSessionBase> session,
QWidget* parent = nullptr);
~CheatSearchWidget() override;
enum class UpdateSource
@ -70,10 +76,12 @@ private:
bool UpdateTableRows(const Core::CPUThreadGuard& guard, size_t begin_index, size_t end_index,
UpdateSource source);
void RecreateGUITable();
void GenerateARCode();
void GenerateARCodes();
int GetVisibleRowsBeginIndex() const;
int GetVisibleRowsEndIndex() const;
Core::System& m_system;
std::unique_ptr<Cheats::CheatSearchSessionBase> m_session;
// storage for the 'Current Value' column's data

View file

@ -24,7 +24,8 @@
#include "VideoCommon/VideoEvents.h"
CheatsManager::CheatsManager(QWidget* parent) : QDialog(parent)
CheatsManager::CheatsManager(Core::System& system, QWidget* parent)
: QDialog(parent), m_system(system)
{
setWindowTitle(tr("Cheats Manager"));
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
@ -35,7 +36,7 @@ CheatsManager::CheatsManager(QWidget* parent) : QDialog(parent)
CreateWidgets();
ConnectWidgets();
RefreshCodeTabs(Core::GetState(), true);
RefreshCodeTabs(Core::GetState(m_system), true);
auto& settings = Settings::GetQSettings();
restoreGeometry(settings.value(QStringLiteral("cheatsmanager/geometry")).toByteArray());
@ -169,7 +170,7 @@ void CheatsManager::CreateWidgets()
void CheatsManager::OnNewSessionCreated(const Cheats::CheatSearchSessionBase& session)
{
auto* w = new CheatSearchWidget(session.Clone());
auto* w = new CheatSearchWidget(m_system, session.Clone());
const int tab_index = m_tab_widget->addTab(w, tr("Cheat Search"));
w->connect(w, &CheatSearchWidget::ActionReplayCodeGenerated, this,
[this](const ActionReplay::ARCode& ar_code) {

View file

@ -26,13 +26,14 @@ class PartiallyClosableTabWidget;
namespace Core
{
enum class State;
}
class System;
} // namespace Core
class CheatsManager : public QDialog
{
Q_OBJECT
public:
explicit CheatsManager(QWidget* parent = nullptr);
explicit CheatsManager(Core::System& system, QWidget* parent = nullptr);
~CheatsManager();
signals:
@ -64,6 +65,8 @@ private:
std::string m_game_tdb_id;
u16 m_revision = 0;
Core::System& m_system;
QDialogButtonBox* m_button_box;
PartiallyClosableTabWidget* m_tab_widget = nullptr;

View file

@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "DolphinQt/Config/ARCodeWidget.h"
#include "DolphinQt/QtUtils/WrapInScrollArea.h"
#include <algorithm>
#include <utility>
@ -86,7 +87,7 @@ void ARCodeWidget::CreateWidgets()
layout->addWidget(m_code_list);
layout->addLayout(button_layout);
setLayout(layout);
WrapInScrollArea(this, layout);
}
void ARCodeWidget::ConnectWidgets()

View file

@ -11,6 +11,7 @@
#include "Core/ConfigManager.h"
#include "Core/Core.h"
#include "Core/System.h"
#include "DolphinQt/Settings.h"
@ -22,11 +23,11 @@ CheatWarningWidget::CheatWarningWidget(const std::string& game_id, bool restart_
ConnectWidgets();
connect(&Settings::Instance(), &Settings::EnableCheatsChanged, this,
[this] { Update(Core::IsRunning()); });
[this] { Update(Core::IsRunning(Core::System::GetInstance())); });
connect(&Settings::Instance(), &Settings::EmulationStateChanged, this,
[this](Core::State state) { Update(state == Core::State::Running); });
Update(Core::IsRunning());
Update(Core::IsRunning(Core::System::GetInstance()));
}
void CheatWarningWidget::CreateWidgets()

View file

@ -226,6 +226,7 @@ void FilesystemWidget::ShowContextMenu(const QPoint&)
auto* item = m_tree_model->itemFromIndex(selection->selectedIndexes()[0]);
QMenu* menu = new QMenu(this);
menu->setAttribute(Qt::WA_DeleteOnClose, true);
EntryType type = item->data(ENTRY_TYPE).value<EntryType>();

View file

@ -66,10 +66,10 @@ GamecubeControllersWidget::GamecubeControllersWidget(QWidget* parent) : QWidget(
ConnectWidgets();
connect(&Settings::Instance(), &Settings::ConfigChanged, this,
[this] { LoadSettings(Core::GetState()); });
[this] { LoadSettings(Core::GetState(Core::System::GetInstance())); });
connect(&Settings::Instance(), &Settings::EmulationStateChanged, this,
[this](Core::State state) { LoadSettings(state); });
LoadSettings(Core::GetState());
LoadSettings(Core::GetState(Core::System::GetInstance()));
}
void GamecubeControllersWidget::CreateLayout()
@ -191,16 +191,16 @@ void GamecubeControllersWidget::SaveSettings()
{
Config::ConfigChangeCallbackGuard config_guard;
auto& system = Core::System::GetInstance();
for (size_t i = 0; i < m_gc_groups.size(); ++i)
{
const SerialInterface::SIDevices si_device =
FromGCMenuIndex(m_gc_controller_boxes[i]->currentIndex());
Config::SetBaseOrCurrent(Config::GetInfoForSIDevice(static_cast<int>(i)), si_device);
if (Core::IsRunning())
if (Core::IsRunning(system))
{
Core::System::GetInstance().GetSerialInterface().ChangeDevice(si_device,
static_cast<s32>(i));
system.GetSerialInterface().ChangeDevice(si_device, static_cast<s32>(i));
}
}
}

View file

@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "DolphinQt/Config/GeckoCodeWidget.h"
#include "DolphinQt/QtUtils/WrapInScrollArea.h"
#include <algorithm>
#include <utility>
@ -138,7 +139,7 @@ void GeckoCodeWidget::CreateWidgets()
layout->addLayout(btn_layout);
setLayout(layout);
WrapInScrollArea(this, layout);
}
void GeckoCodeWidget::ConnectWidgets()

View file

@ -14,6 +14,7 @@
#include "Core/Config/SYSCONFSettings.h"
#include "Core/ConfigManager.h"
#include "Core/Core.h"
#include "Core/System.h"
#include "DolphinQt/Config/ConfigControls/ConfigBool.h"
#include "DolphinQt/Config/ConfigControls/ConfigChoice.h"
@ -42,7 +43,8 @@ AdvancedWidget::AdvancedWidget(GraphicsWindow* parent)
});
OnBackendChanged();
OnEmulationStateChanged(Core::GetState() != Core::State::Uninitialized);
OnEmulationStateChanged(Core::GetState(Core::System::GetInstance()) !=
Core::State::Uninitialized);
}
void AdvancedWidget::CreateWidgets()
@ -84,6 +86,8 @@ void AdvancedWidget::CreateWidgets()
m_enable_wireframe = new ConfigBool(tr("Enable Wireframe"), Config::GFX_ENABLE_WIREFRAME);
m_show_statistics = new ConfigBool(tr("Show Statistics"), Config::GFX_OVERLAY_STATS);
m_show_proj_statistics =
new ConfigBool(tr("Show Projection Statistics"), Config::GFX_OVERLAY_PROJ_STATS);
m_enable_format_overlay =
new ConfigBool(tr("Texture Format Overlay"), Config::GFX_TEXFMT_OVERLAY_ENABLE);
m_enable_api_validation =
@ -92,7 +96,8 @@ void AdvancedWidget::CreateWidgets()
debugging_layout->addWidget(m_enable_wireframe, 0, 0);
debugging_layout->addWidget(m_show_statistics, 0, 1);
debugging_layout->addWidget(m_enable_format_overlay, 1, 0);
debugging_layout->addWidget(m_enable_api_validation, 1, 1);
debugging_layout->addWidget(m_show_proj_statistics, 1, 1);
debugging_layout->addWidget(m_enable_api_validation, 2, 0);
// Utility
auto* utility_box = new QGroupBox(tr("Utility"));
@ -135,21 +140,24 @@ void AdvancedWidget::CreateWidgets()
auto* dump_layout = new QGridLayout();
dump_box->setLayout(dump_layout);
m_use_fullres_framedumps = new ConfigBool(tr("Dump at Internal Resolution"),
Config::GFX_INTERNAL_RESOLUTION_FRAME_DUMPS);
m_frame_dumps_resolution_type =
new ConfigChoice({tr("Window Resolution"), tr("Aspect Ratio Corrected Internal Resolution"),
tr("Raw Internal Resolution")},
Config::GFX_FRAME_DUMPS_RESOLUTION_TYPE);
m_dump_use_ffv1 = new ConfigBool(tr("Use Lossless Codec (FFV1)"), Config::GFX_USE_FFV1);
m_dump_bitrate = new ConfigInteger(0, 1000000, Config::GFX_BITRATE_KBPS, 1000);
m_png_compression_level = new ConfigInteger(0, 9, Config::GFX_PNG_COMPRESSION_LEVEL);
dump_layout->addWidget(m_use_fullres_framedumps, 0, 0);
dump_layout->addWidget(new QLabel(tr("Resolution Type:")), 0, 0);
dump_layout->addWidget(m_frame_dumps_resolution_type, 0, 1);
#if defined(HAVE_FFMPEG)
dump_layout->addWidget(m_dump_use_ffv1, 0, 1);
dump_layout->addWidget(new QLabel(tr("Bitrate (kbps):")), 1, 0);
dump_layout->addWidget(m_dump_bitrate, 1, 1);
dump_layout->addWidget(m_dump_use_ffv1, 1, 0);
dump_layout->addWidget(new QLabel(tr("Bitrate (kbps):")), 2, 0);
dump_layout->addWidget(m_dump_bitrate, 2, 1);
#endif
dump_layout->addWidget(new QLabel(tr("PNG Compression Level:")), 2, 0);
dump_layout->addWidget(new QLabel(tr("PNG Compression Level:")), 3, 0);
m_png_compression_level->SetTitle(tr("PNG Compression Level"));
dump_layout->addWidget(m_png_compression_level, 2, 1);
dump_layout->addWidget(m_png_compression_level, 3, 1);
// Misc.
auto* misc_box = new QGroupBox(tr("Misc"));
@ -294,6 +302,9 @@ void AdvancedWidget::AddDescriptions()
static const char TR_SHOW_STATS_DESCRIPTION[] =
QT_TR_NOOP("Shows various rendering statistics.<br><br><dolphin_emphasis>If unsure, "
"leave this unchecked.</dolphin_emphasis>");
static const char TR_SHOW_PROJ_STATS_DESCRIPTION[] =
QT_TR_NOOP("Shows various projection statistics.<br><br><dolphin_emphasis>If unsure, "
"leave this unchecked.</dolphin_emphasis>");
static const char TR_TEXTURE_FORMAT_DESCRIPTION[] =
QT_TR_NOOP("Modifies textures to show the format they're encoded in.<br><br>May require "
"an emulation reset to apply.<br><br><dolphin_emphasis>If unsure, leave this "
@ -338,11 +349,21 @@ void AdvancedWidget::AddDescriptions()
static const char TR_LOAD_GRAPHICS_MODS_DESCRIPTION[] =
QT_TR_NOOP("Loads graphics mods from User/Load/GraphicsMods/.<br><br><dolphin_emphasis>If "
"unsure, leave this unchecked.</dolphin_emphasis>");
static const char TR_INTERNAL_RESOLUTION_FRAME_DUMPING_DESCRIPTION[] = QT_TR_NOOP(
"Creates frame dumps and screenshots at the internal resolution of the renderer, rather than "
"the size of the window it is displayed within.<br><br>If the aspect ratio is widescreen, "
"the output image will be scaled horizontally to preserve the vertical resolution.<br><br>"
"<dolphin_emphasis>If unsure, leave this unchecked.</dolphin_emphasis>");
static const char TR_FRAME_DUMPS_RESOLUTION_TYPE_DESCRIPTION[] = QT_TR_NOOP(
"Selects how frame dumps (videos) and screenshots are going to be captured.<br>If the game "
"or window resolution change during a recording, multiple video files might be created.<br>"
"Note that color correction and cropping are always ignored by the captures."
"<br><br><b>Window Resolution</b>: Uses the output window resolution (without black bars)."
"<br>This is a simple dumping option that will capture the image more or less as you see it."
"<br><b>Aspect Ratio Corrected Internal Resolution</b>: "
"Uses the Internal Resolution (XFB size), and corrects it by the target aspect ratio.<br>"
"This option will consistently dump at the specified Internal Resolution "
"regardless of how the image is displayed during recording."
"<br><b>Raw Internal Resolution</b>: Uses the Internal Resolution (XFB size) "
"without correcting it with the target aspect ratio.<br>"
"This will provide a clean dump without any aspect ratio correction so users have as raw as "
"possible input for external editing software.<br><br><dolphin_emphasis>If unsure, leave "
"this at \"Aspect Ratio Corrected Internal Resolution\".</dolphin_emphasis>");
#if defined(HAVE_FFMPEG)
static const char TR_USE_FFV1_DESCRIPTION[] =
QT_TR_NOOP("Encodes frame dumps using the FFV1 codec.<br><br><dolphin_emphasis>If "
@ -421,6 +442,7 @@ void AdvancedWidget::AddDescriptions()
m_enable_wireframe->SetDescription(tr(TR_WIREFRAME_DESCRIPTION));
m_show_statistics->SetDescription(tr(TR_SHOW_STATS_DESCRIPTION));
m_show_proj_statistics->SetDescription(tr(TR_SHOW_PROJ_STATS_DESCRIPTION));
m_enable_format_overlay->SetDescription(tr(TR_TEXTURE_FORMAT_DESCRIPTION));
m_enable_api_validation->SetDescription(tr(TR_VALIDATION_LAYER_DESCRIPTION));
m_perf_samp_window->SetDescription(tr(TR_PERF_SAMP_WINDOW_DESCRIPTION));
@ -433,7 +455,7 @@ void AdvancedWidget::AddDescriptions()
m_dump_xfb_target->SetDescription(tr(TR_DUMP_XFB_DESCRIPTION));
m_disable_vram_copies->SetDescription(tr(TR_DISABLE_VRAM_COPIES_DESCRIPTION));
m_enable_graphics_mods->SetDescription(tr(TR_LOAD_GRAPHICS_MODS_DESCRIPTION));
m_use_fullres_framedumps->SetDescription(tr(TR_INTERNAL_RESOLUTION_FRAME_DUMPING_DESCRIPTION));
m_frame_dumps_resolution_type->SetDescription(tr(TR_FRAME_DUMPS_RESOLUTION_TYPE_DESCRIPTION));
#ifdef HAVE_FFMPEG
m_dump_use_ffv1->SetDescription(tr(TR_USE_FFV1_DESCRIPTION));
#endif

View file

@ -33,6 +33,7 @@ private:
// Debugging
ConfigBool* m_enable_wireframe;
ConfigBool* m_show_statistics;
ConfigBool* m_show_proj_statistics;
ConfigBool* m_enable_format_overlay;
ConfigBool* m_enable_api_validation;
ConfigBool* m_show_fps;
@ -60,7 +61,7 @@ private:
// Frame dumping
ConfigBool* m_dump_use_ffv1;
ConfigBool* m_use_fullres_framedumps;
ConfigChoice* m_frame_dumps_resolution_type;
ConfigInteger* m_dump_bitrate;
ConfigInteger* m_png_compression_level;

View file

@ -372,7 +372,8 @@ void EnhancementsWidget::LoadSettings()
// Resampling
const OutputResamplingMode output_resampling_mode =
Config::Get(Config::GFX_ENHANCE_OUTPUT_RESAMPLING);
m_output_resampling_combo->setCurrentIndex(static_cast<int>(output_resampling_mode));
m_output_resampling_combo->setCurrentIndex(
m_output_resampling_combo->findData(static_cast<int>(output_resampling_mode)));
m_output_resampling_combo->setEnabled(g_Config.backend_info.bSupportsPostProcessing);
@ -527,20 +528,20 @@ void EnhancementsWidget::AddDescriptions()
"<br><br><b>Bicubic</b> - [16 samples]"
"<br>Gamma corrected cubic interpolation between pixels."
"<br>Good when rescaling between close resolutions. i.e 1080p and 1440p."
"<br>Good when rescaling between close resolutions, e.g. 1080p and 1440p."
"<br>Comes in various flavors:"
"<br><b>B-Spline</b>: Blurry, but avoids all lobing artifacts"
"<br><b>Mitchell-Netravali</b>: Good middle ground between blurry and lobing"
"<br><b>Catmull-Rom</b>: Sharper, but can cause lobing artifacts"
"<br><br><b>Sharp Bilinear</b> - [1-4 samples]"
"<br>Similarly to \"Nearest Neighbor\", it maintains a sharp look,"
"<br>Similar to \"Nearest Neighbor\", it maintains a sharp look,"
"<br>but also does some blending to avoid shimmering."
"<br>Works best with 2D games at low resolutions."
"<br><br><b>Area Sampling</b> - [up to 324 samples]"
"<br>Weights pixels by the percentage of area they occupy. Gamma corrected."
"<br>Best for down scaling by more than 2x."
"<br>Weighs pixels by the percentage of area they occupy. Gamma corrected."
"<br>Best for downscaling by more than 2x."
"<br><br><dolphin_emphasis>If unsure, select 'Default'.</dolphin_emphasis>");
static const char TR_COLOR_CORRECTION_DESCRIPTION[] =

View file

@ -17,6 +17,7 @@
#include "Core/Config/MainSettings.h"
#include "Core/ConfigManager.h"
#include "Core/Core.h"
#include "Core/System.h"
#include "DolphinQt/Config/ConfigControls/ConfigBool.h"
#include "DolphinQt/Config/ConfigControls/ConfigChoice.h"
@ -43,7 +44,8 @@ GeneralWidget::GeneralWidget(GraphicsWindow* parent)
connect(&Settings::Instance(), &Settings::EmulationStateChanged, this, [this](Core::State state) {
OnEmulationStateChanged(state != Core::State::Uninitialized);
});
OnEmulationStateChanged(Core::GetState() != Core::State::Uninitialized);
OnEmulationStateChanged(Core::GetState(Core::System::GetInstance()) !=
Core::State::Uninitialized);
}
void GeneralWidget::CreateWidgets()
@ -253,17 +255,23 @@ void GeneralWidget::AddDescriptions()
"a separate render window.<br><br><dolphin_emphasis>If unsure, leave "
"this unchecked.</dolphin_emphasis>");
static const char TR_ASPECT_RATIO_DESCRIPTION[] = QT_TR_NOOP(
"Selects which aspect ratio to use when drawing on the render window.<br>"
"Each game can have a slightly different native aspect ratio.<br>They can vary by "
"scene and settings and rarely ever exactly match 4:3 or 16:9."
"<br><br><b>Auto</b>: Uses the native aspect ratio"
"<br><br><b>Force 16:9</b>: Mimics an analog TV with a widescreen aspect ratio."
"<br><br><b>Force 4:3</b>: Mimics a standard 4:3 analog TV."
"<br><br><b>Stretch to Window</b>: Stretches the picture to the window size."
"<br><br><b>Custom</b>: Forces the specified aspect ratio."
"<br>This is mostly intended to be used with aspect ratio cheats/mods."
"<br><br><b>Custom (Stretch)</b>: Similar to `Custom` but not relative to the "
"title's native aspect ratio.<br>This is not meant to be used under normal circumstances."
"Selects which aspect ratio to use for displaying the game."
"<br><br>The aspect ratio of the image sent out by the original consoles varied depending on "
"the game and rarely exactly matched 4:3 or 16:9. Some of the image would be cut off by the "
"edges of the TV, or the image wouldn't fill the TV entirely. By default, Dolphin shows the "
"whole image without distorting its proportions, which means it's normal for the image to "
"not entirely fill your display."
"<br><br><b>Auto</b>: Mimics a TV with either a 4:3 or 16:9 aspect ratio, depending on which "
"type of TV the game seems to be targeting."
"<br><br><b>Force 16:9</b>: Mimics a TV with a 16:9 (widescreen) aspect ratio."
"<br><br><b>Force 4:3</b>: Mimics a TV with a 4:3 aspect ratio."
"<br><br><b>Stretch to Window</b>: Stretches the image to the window size. "
"This will usually distort the image's proportions."
"<br><br><b>Custom</b>: Mimics a TV with the specified aspect ratio. "
"This is mostly intended to be used with aspect ratio cheats/mods."
"<br><br><b>Custom (Stretch)</b>: Similar to `Custom`, but stretches the image to the "
"specified aspect ratio. This will usually distort the image's proportions, and should not "
"be used under normal circumstances."
"<br><br><dolphin_emphasis>If unsure, select Auto.</dolphin_emphasis>");
static const char TR_VSYNC_DESCRIPTION[] = QT_TR_NOOP(
"Waits for vertical blanks in order to prevent tearing.<br><br>Decreases performance "
@ -355,7 +363,7 @@ void GeneralWidget::OnBackendChanged(const QString& backend_name)
const bool supports_adapters = !adapters.empty();
m_adapter_combo->setCurrentIndex(g_Config.iAdapter);
m_adapter_combo->setEnabled(supports_adapters && !Core::IsRunning());
m_adapter_combo->setEnabled(supports_adapters && !Core::IsRunning(Core::System::GetInstance()));
static constexpr char TR_ADAPTER_AVAILABLE_DESCRIPTION[] =
QT_TR_NOOP("Selects a hardware adapter to use.<br><br>"

View file

@ -18,6 +18,7 @@
#include "Common/FileUtil.h"
#include "Core/ConfigManager.h"
#include "Core/Core.h"
#include "Core/System.h"
#include "DolphinQt/Config/GraphicsModWarningWidget.h"
#include "DolphinQt/QtUtils/ClearLayoutRecursively.h"
#include "DolphinQt/Settings.h"
@ -28,7 +29,7 @@
GraphicsModListWidget::GraphicsModListWidget(const UICommon::GameFile& game)
: m_game_id(game.GetGameID()), m_mod_group(m_game_id)
{
CalculateGameRunning(Core::GetState());
CalculateGameRunning(Core::GetState(Core::System::GetInstance()));
if (m_loaded_game_is_running && g_Config.graphics_mod_config)
{
m_mod_group.SetChangeCount(g_Config.graphics_mod_config->GetChangeCount());

View file

@ -10,7 +10,7 @@
#include <QPushButton>
#include <QStyle>
#include "Core/Config/AchievementSettings.h"
#include "Core/AchievementManager.h"
#include "Core/ConfigManager.h"
#include "Core/Core.h"
@ -57,6 +57,6 @@ void HardcoreWarningWidget::ConnectWidgets()
void HardcoreWarningWidget::Update()
{
setHidden(!Config::Get(Config::RA_HARDCORE_ENABLED));
setHidden(!AchievementManager::GetInstance().IsHardcoreModeActive());
}
#endif // USE_RETRO_ACHIEVEMENTS

View file

@ -587,28 +587,25 @@ void IOWindow::UpdateOptionList()
if (m_selected_device == nullptr)
return;
const auto add_rows = [this](auto& container) {
int row = 0;
for (ciface::Core::Device::Control* control : container)
{
m_option_list->insertRow(row);
if (control->IsHidden())
m_option_list->hideRow(row);
m_option_list->setItem(row, 0,
new QTableWidgetItem(QString::fromStdString(control->GetName())));
++row;
}
};
if (m_reference->IsInput())
{
int row = 0;
for (const auto* input : m_selected_device->Inputs())
{
m_option_list->insertRow(row);
m_option_list->setItem(row, 0,
new QTableWidgetItem(QString::fromStdString(input->GetName())));
++row;
}
}
add_rows(m_selected_device->Inputs());
else
{
int row = 0;
for (const auto* output : m_selected_device->Outputs())
{
m_option_list->insertRow(row);
m_option_list->setItem(row, 0,
new QTableWidgetItem(QString::fromStdString(output->GetName())));
++row;
}
}
add_rows(m_selected_device->Outputs());
}
void IOWindow::UpdateDeviceList()

View file

@ -17,6 +17,8 @@
#include "Common/MathUtil.h"
#include "Core/HW/WiimoteEmu/Camera.h"
#include "InputCommon/ControlReference/ControlReference.h"
#include "InputCommon/ControllerEmu/Control/Control.h"
#include "InputCommon/ControllerEmu/ControlGroup/Cursor.h"
@ -823,6 +825,35 @@ void GyroMappingIndicator::Draw()
p.drawEllipse(QPointF{}, INPUT_DOT_RADIUS, INPUT_DOT_RADIUS);
}
void IRPassthroughMappingIndicator::Draw()
{
QPainter p(this);
DrawBoundingBox(p);
TransformPainter(p);
p.scale(1.0, -1.0);
auto pen = GetInputDotPen(m_ir_group.enabled ? GetAdjustedInputColor() : GetRawInputColor());
for (std::size_t i = 0; i != WiimoteEmu::CameraLogic::NUM_POINTS; ++i)
{
const auto size = m_ir_group.GetObjectSize(i);
const bool is_visible = size > 0;
if (!is_visible)
continue;
const auto point =
(QPointF{m_ir_group.GetObjectPositionX(i), m_ir_group.GetObjectPositionY(i)} -
QPointF{0.5, 0.5}) *
2.0;
pen.setWidth(size * NORMAL_INDICATOR_WIDTH / 2);
p.setPen(pen);
p.drawPoint(point);
}
}
void ReshapableInputIndicator::DrawCalibration(QPainter& p, Common::DVec2 point)
{
const auto center = m_calibration_widget->GetCenter();

View file

@ -9,6 +9,7 @@
#include <deque>
#include "Core/HW/WiimoteEmu/Dynamics.h"
#include "InputCommon/ControllerEmu/ControlGroup/IRPassthrough.h"
#include "InputCommon/ControllerEmu/StickGate.h"
namespace ControllerEmu
@ -180,6 +181,19 @@ private:
u32 m_stable_steps = 0;
};
class IRPassthroughMappingIndicator : public SquareIndicator
{
public:
explicit IRPassthroughMappingIndicator(ControllerEmu::IRPassthrough& ir_group)
: m_ir_group(ir_group)
{
}
private:
void Draw() override;
ControllerEmu::IRPassthrough& m_ir_group;
};
class CalibrationWidget : public QToolButton
{
public:

View file

@ -95,6 +95,11 @@ QGroupBox* MappingWidget::CreateGroupBox(const QString& name, ControllerEmu::Con
indicator = new AnalogStickIndicator(*static_cast<ControllerEmu::ReshapableInput*>(group));
break;
case ControllerEmu::GroupType::IRPassthrough:
indicator =
new IRPassthroughMappingIndicator(*static_cast<ControllerEmu::IRPassthrough*>(group));
break;
default:
break;
}
@ -167,7 +172,9 @@ QGroupBox* MappingWidget::CreateGroupBox(const QString& name, ControllerEmu::Con
{
QPushButton* mouse_button = new QPushButton(tr("Use Mouse Controlled Pointing"));
form_layout->insertRow(2, mouse_button);
connect(mouse_button, &QCheckBox::clicked, [this, group] {
using ControllerEmu::Cursor;
connect(mouse_button, &QCheckBox::clicked, [this, group = static_cast<Cursor*>(group)] {
std::string default_device = g_controller_interface.GetDefaultDeviceString() + ":";
const std::string controller_device = GetController()->GetDefaultDevice().ToString() + ":";
if (default_device == controller_device)
@ -178,6 +185,9 @@ QGroupBox* MappingWidget::CreateGroupBox(const QString& name, ControllerEmu::Con
group->SetControlExpression(1, fmt::format("`{}Cursor Y+`", default_device));
group->SetControlExpression(2, fmt::format("`{}Cursor X-`", default_device));
group->SetControlExpression(3, fmt::format("`{}Cursor X+`", default_device));
group->SetRelativeInput(false);
emit ConfigChanged();
GetController()->UpdateReferences(g_controller_interface);
});
@ -327,6 +337,9 @@ MappingWidget::CreateSettingAdvancedMappingButton(ControllerEmu::NumericSettingB
if (setting.IsSimpleValue())
setting.SetExpressionFromValue();
// Ensure the UI has the game-controller indicator while editing the expression.
ConfigChanged();
IOWindow io(this, GetController(), &setting.GetInputReference(), IOWindow::Type::Input);
SetQWidgetWindowDecorations(&io);
io.exec();

View file

@ -48,6 +48,8 @@ void WiimoteEmuMotionControlIMU::CreateMainLayout()
auto* groups_layout = new QHBoxLayout();
groups_layout->addWidget(
CreateGroupBox(Wiimote::GetWiimoteGroup(GetPort(), WiimoteEmu::WiimoteGroup::IMUPoint)));
groups_layout->addWidget(
CreateGroupBox(Wiimote::GetWiimoteGroup(GetPort(), WiimoteEmu::WiimoteGroup::IRPassthrough)));
groups_layout->addWidget(CreateGroupBox(
Wiimote::GetWiimoteGroup(GetPort(), WiimoteEmu::WiimoteGroup::IMUAccelerometer)));
groups_layout->addWidget(

View file

@ -315,10 +315,12 @@ void BalloonTip::UpdateBoundsAndRedraw(const QPoint& target_arrow_tip_position,
// Place the arrow tip at the target position whether the arrow tip is drawn or not
const int target_balloontip_global_x =
target_arrow_tip_position.x() - static_cast<int>(arrow_tip_x);
const int rightmost_valid_balloontip_global_x = screen_rect.width() - size_hint.width();
const int rightmost_valid_balloontip_global_x =
screen_rect.left() + screen_rect.width() - size_hint.width();
// If the balloon would extend off the screen, push it left or right until it's not
const int actual_balloontip_global_x =
std::max(0, std::min(rightmost_valid_balloontip_global_x, target_balloontip_global_x));
std::max(screen_rect.left(),
std::min(rightmost_valid_balloontip_global_x, target_balloontip_global_x));
// The tip pixel should be in the middle of the control, and arrow_tip_exterior_y is at the bottom
// of that pixel. When arrow_at_bottom is true the arrow is above arrow_tip_exterior_y and so the
// tip pixel is in the right place, but when it's false the arrow is below arrow_tip_exterior_y

View file

@ -17,6 +17,7 @@
#include "Common/CommonTypes.h"
#include "Core/Core.h"
#include "Core/System.h"
#include "DiscIO/Volume.h"
#include "DiscIO/VolumeVerifier.h"
#include "DolphinQt/QtUtils/ParallelProgressDialog.h"
@ -44,12 +45,12 @@ VerifyWidget::VerifyWidget(std::shared_ptr<DiscIO::Volume> volume) : m_volume(st
connect(&Settings::Instance(), &Settings::EmulationStateChanged, this,
&VerifyWidget::OnEmulationStateChanged);
OnEmulationStateChanged();
OnEmulationStateChanged(Core::GetState(Core::System::GetInstance()));
}
void VerifyWidget::OnEmulationStateChanged()
void VerifyWidget::OnEmulationStateChanged(Core::State state)
{
const bool running = Core::GetState() != Core::State::Uninitialized;
const bool running = state != Core::State::Uninitialized;
// Verifying a Wii game while emulation is running doesn't work correctly
// due to verification of a Wii game creating an instance of IOS

View file

@ -15,6 +15,10 @@
#include <QTextEdit>
#include <QWidget>
namespace Core
{
enum class State;
}
namespace DiscIO
{
class Volume;
@ -26,10 +30,8 @@ class VerifyWidget final : public QWidget
public:
explicit VerifyWidget(std::shared_ptr<DiscIO::Volume> volume);
private slots:
void OnEmulationStateChanged();
private:
void OnEmulationStateChanged(Core::State state);
void CreateWidgets();
std::pair<QCheckBox*, QLineEdit*> AddHashLine(QFormLayout* layout, QString text);
void ConnectWidgets();

View file

@ -46,10 +46,10 @@ WiimoteControllersWidget::WiimoteControllersWidget(QWidget* parent) : QWidget(pa
ConnectWidgets();
connect(&Settings::Instance(), &Settings::ConfigChanged, this,
[this] { LoadSettings(Core::GetState()); });
[this] { LoadSettings(Core::GetState(Core::System::GetInstance())); });
connect(&Settings::Instance(), &Settings::EmulationStateChanged, this,
[this](Core::State state) { LoadSettings(state); });
LoadSettings(Core::GetState());
LoadSettings(Core::GetState(Core::System::GetInstance()));
}
void WiimoteControllersWidget::UpdateBluetoothAvailableStatus()
@ -173,16 +173,16 @@ void WiimoteControllersWidget::ConnectWidgets()
{
connect(m_wiimote_passthrough, &QRadioButton::toggled, this, [this] {
SaveSettings();
LoadSettings(Core::GetState());
LoadSettings(Core::GetState(Core::System::GetInstance()));
});
connect(m_wiimote_ciface, &QCheckBox::toggled, this, [this] {
SaveSettings();
LoadSettings(Core::GetState());
LoadSettings(Core::GetState(Core::System::GetInstance()));
WiimoteReal::HandleWiimotesInControllerInterfaceSettingChange();
});
connect(m_wiimote_continuous_scanning, &QCheckBox::toggled, this, [this] {
SaveSettings();
LoadSettings(Core::GetState());
LoadSettings(Core::GetState(Core::System::GetInstance()));
});
connect(m_wiimote_real_balance_board, &QCheckBox::toggled, this,
@ -200,7 +200,7 @@ void WiimoteControllersWidget::ConnectWidgets()
{
connect(m_wiimote_boxes[i], &QComboBox::currentIndexChanged, this, [this] {
SaveSettings();
LoadSettings(Core::GetState());
LoadSettings(Core::GetState(Core::System::GetInstance()));
});
connect(m_wiimote_buttons[i], &QPushButton::clicked, this,
[this, i] { OnWiimoteConfigure(i); });

View file

@ -729,9 +729,7 @@ void AssemblerWidget::NewEditor(const QString& path)
if (!path.isEmpty() && !new_editor->LoadFromPath())
{
ModalMessageBox::warning(this, tr("Failed to open file"),
tr("Failed to read the contents of file\n\n"
"\"%1\"")
.arg(path));
tr("Failed to read the contents of file:\n%1").arg(path));
delete new_editor;
return;
}

View file

@ -42,6 +42,7 @@
#include "Core/System.h"
#include "DolphinQt/Debugger/BranchWatchTableModel.h"
#include "DolphinQt/Debugger/CodeWidget.h"
#include "DolphinQt/Host.h"
#include "DolphinQt/QtUtils/DolphinFileDialog.h"
#include "DolphinQt/QtUtils/ModalMessageBox.h"
#include "DolphinQt/QtUtils/SetWindowDecorations.h"
@ -194,30 +195,33 @@ void BranchWatchProxyModel::SetInspected(const QModelIndex& index)
}
BranchWatchDialog::BranchWatchDialog(Core::System& system, Core::BranchWatch& branch_watch,
CodeWidget* code_widget, QWidget* parent)
PPCSymbolDB& ppc_symbol_db, CodeWidget* code_widget,
QWidget* parent)
: QDialog(parent), m_system(system), m_branch_watch(branch_watch), m_code_widget(code_widget)
{
setWindowTitle(tr("Branch Watch Tool"));
setWindowFlags((windowFlags() | Qt::WindowMinMaxButtonsHint) & ~Qt::WindowContextHelpButtonHint);
SetQWidgetWindowDecorations(this);
setLayout([this]() {
auto* layout = new QVBoxLayout;
setLayout([this, &ppc_symbol_db]() {
auto* main_layout = new QVBoxLayout;
// Controls Toolbar (widgets are added later)
layout->addWidget(m_control_toolbar = new QToolBar);
main_layout->addWidget(m_control_toolbar = new QToolBar);
// Branch Watch Table
layout->addWidget(m_table_view = [this]() {
main_layout->addWidget(m_table_view = [this, &ppc_symbol_db]() {
const auto& ui_settings = Settings::Instance();
m_table_proxy = new BranchWatchProxyModel(m_branch_watch);
m_table_proxy->setSourceModel(m_table_model =
new BranchWatchTableModel(m_system, m_branch_watch));
m_table_proxy->setSourceModel(
m_table_model = new BranchWatchTableModel(m_system, m_branch_watch, ppc_symbol_db));
m_table_proxy->setSortRole(UserRole::SortRole);
m_table_model->setFont(ui_settings.GetDebugFont());
connect(&ui_settings, &Settings::DebugFontChanged, m_table_model,
&BranchWatchTableModel::setFont);
connect(Host::GetInstance(), &Host::PPCSymbolsChanged, m_table_model,
&BranchWatchTableModel::UpdateSymbols);
auto* const table_view = new QTableView;
table_view->setModel(m_table_proxy);
@ -271,7 +275,7 @@ BranchWatchDialog::BranchWatchDialog(Core::System& system, Core::BranchWatch& br
}();
// Menu Bar
layout->setMenuBar([this]() {
main_layout->setMenuBar([this]() {
QMenuBar* const menu_bar = new QMenuBar;
menu_bar->setNativeMenuBar(false);
@ -308,7 +312,7 @@ BranchWatchDialog::BranchWatchDialog(Core::System& system, Core::BranchWatch& br
}());
// Status Bar
layout->addWidget(m_status_bar = []() {
main_layout->addWidget(m_status_bar = []() {
auto* const status_bar = new QStatusBar;
status_bar->setSizeGripEnabled(false);
return status_bar;
@ -394,7 +398,8 @@ BranchWatchDialog::BranchWatchDialog(Core::System& system, Core::BranchWatch& br
m_control_toolbar->addWidget([this]() {
auto* const layout = new QGridLayout;
const auto routine = [this, layout](const QString& text, int row, int column, int width,
const auto routine = [this, layout](const QString& placeholder_text, int row, int column,
int width,
void (BranchWatchProxyModel::*slot)(const QString&)) {
QLineEdit* const line_edit = new QLineEdit;
layout->addWidget(line_edit, row, column, 1, width);
@ -402,7 +407,7 @@ BranchWatchDialog::BranchWatchDialog(Core::System& system, Core::BranchWatch& br
(m_table_proxy->*slot)(text);
UpdateStatus();
});
line_edit->setPlaceholderText(text);
line_edit->setPlaceholderText(placeholder_text);
return line_edit;
};
@ -487,7 +492,7 @@ BranchWatchDialog::BranchWatchDialog(Core::System& system, Core::BranchWatch& br
connect(m_table_proxy, &BranchWatchProxyModel::layoutChanged, this,
&BranchWatchDialog::UpdateStatus);
return layout;
return main_layout;
}());
// FIXME: On Linux, Qt6 has recently been resetting column widths to their defaults in many
@ -504,15 +509,9 @@ BranchWatchDialog::BranchWatchDialog(Core::System& system, Core::BranchWatch& br
restoreGeometry(settings.value(QStringLiteral("branchwatchdialog/geometry")).toByteArray());
}
void BranchWatchDialog::done(int r)
BranchWatchDialog::~BranchWatchDialog()
{
if (m_timer->isActive())
m_timer->stop();
auto& settings = Settings::GetQSettings();
settings.setValue(QStringLiteral("branchwatchdialog/geometry"), saveGeometry());
settings.setValue(QStringLiteral("branchwatchdialog/tableheader/state"),
m_table_view->horizontalHeader()->saveState());
QDialog::done(r);
SaveSettings();
}
static constexpr int BRANCH_WATCH_TOOL_TIMER_DELAY_MS = 100;
@ -523,18 +522,18 @@ static bool TimerCondition(const Core::BranchWatch& branch_watch, Core::State st
return branch_watch.GetRecordingActive() && state > Core::State::Paused;
}
int BranchWatchDialog::exec()
void BranchWatchDialog::hideEvent(QHideEvent* event)
{
if (TimerCondition(m_branch_watch, Core::GetState()))
m_timer->start(BRANCH_WATCH_TOOL_TIMER_DELAY_MS);
return QDialog::exec();
if (m_timer->isActive())
m_timer->stop();
QDialog::hideEvent(event);
}
void BranchWatchDialog::open()
void BranchWatchDialog::showEvent(QShowEvent* event)
{
if (TimerCondition(m_branch_watch, Core::GetState()))
if (TimerCondition(m_branch_watch, Core::GetState(m_system)))
m_timer->start(BRANCH_WATCH_TOOL_TIMER_DELAY_MS);
QDialog::open();
QDialog::showEvent(event);
}
void BranchWatchDialog::OnStartPause(bool checked)
@ -545,7 +544,7 @@ void BranchWatchDialog::OnStartPause(bool checked)
m_btn_start_pause->setText(tr("Pause Branch Watch"));
// Restart the timer if the situation calls for it, but always turn off single-shot.
m_timer->setSingleShot(false);
if (Core::GetState() > Core::State::Paused)
if (Core::GetState(m_system) > Core::State::Paused)
m_timer->start(BRANCH_WATCH_TOOL_TIMER_DELAY_MS);
}
else
@ -553,7 +552,7 @@ void BranchWatchDialog::OnStartPause(bool checked)
m_branch_watch.Pause();
m_btn_start_pause->setText(tr("Start Branch Watch"));
// Schedule one last update in the future in case Branch Watch is in the middle of a hit.
if (Core::GetState() > Core::State::Paused)
if (Core::GetState(m_system) > Core::State::Paused)
m_timer->setInterval(BRANCH_WATCH_TOOL_TIMER_PAUSE_ONESHOT_MS);
m_timer->setSingleShot(true);
}
@ -646,7 +645,7 @@ void BranchWatchDialog::OnCodePathNotTaken()
void BranchWatchDialog::OnBranchWasOverwritten()
{
if (Core::GetState() == Core::State::Uninitialized)
if (Core::GetState(m_system) == Core::State::Uninitialized)
{
ModalMessageBox::warning(this, tr("Error"), tr("Core is uninitialized."));
return;
@ -661,7 +660,7 @@ void BranchWatchDialog::OnBranchWasOverwritten()
void BranchWatchDialog::OnBranchNotOverwritten()
{
if (Core::GetState() == Core::State::Uninitialized)
if (Core::GetState(m_system) == Core::State::Uninitialized)
{
ModalMessageBox::warning(this, tr("Error"), tr("Core is uninitialized."));
return;
@ -758,6 +757,8 @@ void BranchWatchDialog::OnToggleAutoSave(bool checked)
return;
const QString filepath = DolphinFileDialog::getSaveFileName(
// i18n: If the user selects a file, Branch Watch will save to that file.
// If the user presses Cancel, Branch Watch will save to a file in the user folder.
this, tr("Select Branch Watch snapshot auto-save file (for user folder location, cancel)"),
QString::fromStdString(File::GetUserPath(D_DUMPDEBUG_BRANCHWATCH_IDX)),
tr("Text file (*.txt);;All Files (*)"));
@ -805,13 +806,15 @@ void BranchWatchDialog::OnTableContextMenu(const QPoint& pos)
QModelIndexList index_list = m_table_view->selectionModel()->selectedRows(index.column());
QMenu* const menu = new QMenu;
menu->setAttribute(Qt::WA_DeleteOnClose, true);
menu->addAction(tr("&Delete"), [this, index_list]() { OnTableDelete(std::move(index_list)); });
switch (index.column())
{
case Column::Origin:
{
QAction* const action = menu->addAction(tr("Insert &NOP"));
if (Core::GetState() != Core::State::Uninitialized)
if (Core::GetState(m_system) != Core::State::Uninitialized)
connect(action, &QAction::triggered,
[this, index_list]() { OnTableSetNOP(std::move(index_list)); });
else
@ -825,9 +828,9 @@ void BranchWatchDialog::OnTableContextMenu(const QPoint& pos)
{
QAction* const action = menu->addAction(tr("Insert &BLR"));
const bool enable_action =
Core::GetState() != Core::State::Uninitialized &&
std::all_of(index_list.begin(), index_list.end(), [this](const QModelIndex& index) {
const QModelIndex sibling = index.siblingAtColumn(Column::Instruction);
Core::GetState(m_system) != Core::State::Uninitialized &&
std::all_of(index_list.begin(), index_list.end(), [this](const QModelIndex& idx) {
const QModelIndex sibling = idx.siblingAtColumn(Column::Instruction);
return BranchSavesLR(m_table_proxy->data(sibling, UserRole::ClickRole).value<u32>());
});
if (enable_action)
@ -845,9 +848,9 @@ void BranchWatchDialog::OnTableContextMenu(const QPoint& pos)
{
QAction* const action = menu->addAction(tr("Insert &BLR at start"));
const bool enable_action =
Core::GetState() != Core::State::Uninitialized &&
std::all_of(index_list.begin(), index_list.end(), [this](const QModelIndex& index) {
return m_table_proxy->data(index, UserRole::ClickRole).isValid();
Core::GetState(m_system) != Core::State::Uninitialized &&
std::all_of(index_list.begin(), index_list.end(), [this](const QModelIndex& idx) {
return m_table_proxy->data(idx, UserRole::ClickRole).isValid();
});
if (enable_action)
connect(action, &QAction::triggered, [this, index_list = std::move(index_list)]() {
@ -927,6 +930,14 @@ void BranchWatchDialog::OnTableCopyAddress(QModelIndexList index_list)
QApplication::clipboard()->setText(text);
}
void BranchWatchDialog::SaveSettings()
{
auto& settings = Settings::GetQSettings();
settings.setValue(QStringLiteral("branchwatchdialog/geometry"), saveGeometry());
settings.setValue(QStringLiteral("branchwatchdialog/tableheader/state"),
m_table_view->horizontalHeader()->saveState());
}
void BranchWatchDialog::Update()
{
if (m_branch_watch.GetRecordingPhase() == Core::BranchWatch::Phase::Blacklist)
@ -934,11 +945,6 @@ void BranchWatchDialog::Update()
m_table_model->UpdateHits();
}
void BranchWatchDialog::UpdateSymbols()
{
m_table_model->UpdateSymbols();
}
void BranchWatchDialog::UpdateStatus()
{
switch (m_branch_watch.GetRecordingPhase())

View file

@ -17,6 +17,8 @@ class BranchWatch;
class CPUThreadGuard;
class System;
} // namespace Core
class PPCSymbolDB;
class BranchWatchProxyModel;
class BranchWatchTableModel;
class CodeWidget;
@ -48,10 +50,13 @@ class BranchWatchDialog : public QDialog
public:
explicit BranchWatchDialog(Core::System& system, Core::BranchWatch& branch_watch,
CodeWidget* code_widget, QWidget* parent = nullptr);
void done(int r) override;
int exec() override;
void open() override;
PPCSymbolDB& ppc_symbol_db, CodeWidget* code_widget,
QWidget* parent = nullptr);
~BranchWatchDialog() override;
protected:
void hideEvent(QHideEvent* event) override;
void showEvent(QShowEvent* event) override;
private:
void OnStartPause(bool checked);
@ -82,11 +87,11 @@ private:
void OnTableSetNOP(QModelIndexList index_list);
void OnTableCopyAddress(QModelIndexList index_list);
void SaveSettings();
public:
// TODO: Step doesn't cause EmulationStateChanged to be emitted, so it has to call this manually.
void Update();
// TODO: There seems to be a lack of a ubiquitous signal for when symbols change.
void UpdateSymbols();
private:
void UpdateStatus();

View file

@ -285,8 +285,8 @@ void BranchWatchTableModel::PrefetchSymbols()
for (const Core::BranchWatch::Selection::value_type& value : selection)
{
const Core::BranchWatch::Collection::value_type* const kv = value.collection_ptr;
m_symbol_list.emplace_back(g_symbolDB.GetSymbolFromAddr(kv->first.origin_addr),
g_symbolDB.GetSymbolFromAddr(kv->first.destin_addr));
m_symbol_list.emplace_back(m_ppc_symbol_db.GetSymbolFromAddr(kv->first.origin_addr),
m_ppc_symbol_db.GetSymbolFromAddr(kv->first.destin_addr));
}
}

View file

@ -18,6 +18,7 @@ class BranchWatch;
class CPUThreadGuard;
class System;
} // namespace Core
class PPCSymbolDB;
namespace BranchWatchTableModelColumn
{
@ -69,8 +70,9 @@ public:
using SymbolList = QList<SymbolListValueType>;
explicit BranchWatchTableModel(Core::System& system, Core::BranchWatch& branch_watch,
QObject* parent = nullptr)
: QAbstractTableModel(parent), m_system(system), m_branch_watch(branch_watch)
PPCSymbolDB& ppc_symbol_db, QObject* parent = nullptr)
: QAbstractTableModel(parent), m_system(system), m_branch_watch(branch_watch),
m_ppc_symbol_db(ppc_symbol_db)
{
}
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
@ -113,6 +115,7 @@ private:
Core::System& m_system;
Core::BranchWatch& m_branch_watch;
PPCSymbolDB& m_ppc_symbol_db;
SymbolList m_symbol_list;
mutable QFont m_font;

View file

@ -331,7 +331,12 @@ void BreakpointDialog::ShowConditionHelp()
"\n"
"Registers that can be referenced:\n"
"GPRs : r0..r31\n"
"FPRs : f0..f31\n LR, CTR, PC\n"
"FPRs : f0..f31\n"
"SPRs : xer, lr, ctr, dsisr, dar, dec, sdr1, srr0, srr1, tbl, tbu, pvr, sprg0..sprg3, ear, "
"ibat0u..ibat7u, ibat0l..ibat7l, dbat0u..dbat7u, dbat0l..dbat07, gqr0..gqr7, hid0, hid1, "
"hid2, hid4, iabr, dabr, wpar, dmau, dmal, ecid_u, ecid_m, ecid_l, upmc1..upmc4, usia, sia, "
"l2cr, ictc, mmcr0, mmcr1, pmc1..pmc4, thrm1..thrm3\n"
"Other : pc, msr\n"
"\n"
"Functions:\n"
"Set a register: r1 = 8\n"

View file

@ -150,7 +150,7 @@ void BreakpointWidget::UpdateButtonsEnabled()
if (!isVisible())
return;
const bool is_initialised = Core::GetState() != Core::State::Uninitialized;
const bool is_initialised = Core::GetState(m_system) != Core::State::Uninitialized;
m_new->setEnabled(is_initialised);
m_load->setEnabled(is_initialised);
m_save->setEnabled(is_initialised);
@ -178,6 +178,7 @@ void BreakpointWidget::Update()
auto& power_pc = m_system.GetPowerPC();
auto& breakpoints = power_pc.GetBreakPoints();
auto& memchecks = power_pc.GetMemChecks();
auto& ppc_symbol_db = power_pc.GetSymbolDB();
// Breakpoints
for (const auto& bp : breakpoints.GetBreakPoints())
@ -192,11 +193,8 @@ void BreakpointWidget::Update()
m_table->setItem(i, 0, active);
m_table->setItem(i, 1, create_item(QStringLiteral("BP")));
if (g_symbolDB.GetSymbolFromAddr(bp.address))
{
m_table->setItem(i, 2,
create_item(QString::fromStdString(g_symbolDB.GetDescription(bp.address))));
}
if (const Common::Symbol* const symbol = ppc_symbol_db.GetSymbolFromAddr(bp.address))
m_table->setItem(i, 2, create_item(QString::fromStdString(symbol->name)));
m_table->setItem(i, 3,
create_item(QStringLiteral("%1").arg(bp.address, 8, 16, QLatin1Char('0'))));
@ -233,11 +231,8 @@ void BreakpointWidget::Update()
m_table->setItem(i, 0, active);
m_table->setItem(i, 1, create_item(QStringLiteral("MBP")));
if (g_symbolDB.GetSymbolFromAddr(mbp.start_address))
{
m_table->setItem(
i, 2, create_item(QString::fromStdString(g_symbolDB.GetDescription(mbp.start_address))));
}
if (const Common::Symbol* const symbol = ppc_symbol_db.GetSymbolFromAddr(mbp.start_address))
m_table->setItem(i, 2, create_item(QString::fromStdString(symbol->name)));
if (mbp.is_ranged)
{
@ -314,6 +309,7 @@ void BreakpointWidget::OnClear()
void BreakpointWidget::OnNewBreakpoint()
{
BreakpointDialog* dialog = new BreakpointDialog(this);
dialog->setAttribute(Qt::WA_DeleteOnClose, true);
SetQWidgetWindowDecorations(dialog);
dialog->exec();
}
@ -324,6 +320,7 @@ void BreakpointWidget::OnEditBreakpoint(u32 address, bool is_instruction_bp)
{
auto* dialog =
new BreakpointDialog(this, m_system.GetPowerPC().GetBreakPoints().GetBreakpoint(address));
dialog->setAttribute(Qt::WA_DeleteOnClose, true);
SetQWidgetWindowDecorations(dialog);
dialog->exec();
}
@ -331,6 +328,7 @@ void BreakpointWidget::OnEditBreakpoint(u32 address, bool is_instruction_bp)
{
auto* dialog =
new BreakpointDialog(this, m_system.GetPowerPC().GetMemChecks().GetMemCheck(address));
dialog->setAttribute(Qt::WA_DeleteOnClose, true);
SetQWidgetWindowDecorations(dialog);
dialog->exec();
}
@ -392,6 +390,7 @@ void BreakpointWidget::OnContextMenu()
const auto is_memory_breakpoint = selected_item->data(IS_MEMCHECK_ROLE).toBool();
auto* menu = new QMenu(this);
menu->setAttribute(Qt::WA_DeleteOnClose, true);
if (!is_memory_breakpoint)
{

View file

@ -38,6 +38,7 @@
#include "DolphinQt/Debugger/AssembleInstructionDialog.h"
#include "DolphinQt/Debugger/PatchInstructionDialog.h"
#include "DolphinQt/Host.h"
#include "DolphinQt/QtUtils/FromStdString.h"
#include "DolphinQt/QtUtils/SetWindowDecorations.h"
#include "DolphinQt/Resources.h"
#include "DolphinQt/Settings.h"
@ -138,7 +139,8 @@ constexpr int CODE_VIEW_COLUMN_DESCRIPTION = 4;
constexpr int CODE_VIEW_COLUMN_BRANCH_ARROWS = 5;
constexpr int CODE_VIEW_COLUMNCOUNT = 6;
CodeViewWidget::CodeViewWidget() : m_system(Core::System::GetInstance())
CodeViewWidget::CodeViewWidget()
: m_system(Core::System::GetInstance()), m_ppc_symbol_db(m_system.GetPPCSymbolDB())
{
setColumnCount(CODE_VIEW_COLUMNCOUNT);
setShowGrid(false);
@ -171,9 +173,8 @@ CodeViewWidget::CodeViewWidget() : m_system(Core::System::GetInstance())
connect(this, &CodeViewWidget::customContextMenuRequested, this, &CodeViewWidget::OnContextMenu);
connect(this, &CodeViewWidget::itemSelectionChanged, this, &CodeViewWidget::OnSelectionChanged);
connect(&Settings::Instance(), &Settings::DebugFontChanged, this, &QWidget::setFont);
connect(&Settings::Instance(), &Settings::DebugFontChanged, this,
&CodeViewWidget::FontBasedSizing);
&CodeViewWidget::OnDebugFontChanged);
connect(&Settings::Instance(), &Settings::EmulationStateChanged, this, [this] {
m_address = m_system.GetPPCState().pc;
@ -183,6 +184,8 @@ CodeViewWidget::CodeViewWidget() : m_system(Core::System::GetInstance())
m_address = m_system.GetPPCState().pc;
Update();
});
connect(Host::GetInstance(), &Host::PPCSymbolsChanged, this,
qOverload<>(&CodeViewWidget::Update));
connect(&Settings::Instance(), &Settings::ThemeChanged, this,
qOverload<>(&CodeViewWidget::Update));
@ -207,7 +210,7 @@ void CodeViewWidget::FontBasedSizing()
// just text width is too small with some fonts, so increase by a bit
constexpr int extra_text_width = 8;
const QFontMetrics fm(Settings::Instance().GetDebugFont());
const QFontMetrics fm(font());
const int rowh = fm.height() + 1;
verticalHeader()->setMaximumSectionSize(rowh);
@ -265,7 +268,7 @@ void CodeViewWidget::Update()
if (m_updating)
return;
if (Core::GetState() == Core::State::Paused)
if (Core::GetState(m_system) == Core::State::Paused)
{
Core::CPUThreadGuard guard(m_system);
Update(&guard);
@ -324,7 +327,7 @@ void CodeViewWidget::Update(const Core::CPUThreadGuard* guard)
std::string ins = (split == std::string::npos ? disas : disas.substr(0, split));
std::string param = (split == std::string::npos ? "" : disas.substr(split + 1));
std::string desc = debug_interface.GetDescription(addr);
const std::string_view desc = debug_interface.GetDescription(addr);
// Adds whitespace and a minimum size to ins and param. Helps to prevent frequent resizing while
// scrolling.
@ -332,7 +335,7 @@ void CodeViewWidget::Update(const Core::CPUThreadGuard* guard)
QStringLiteral("%1").arg(QString::fromStdString(ins), -7, QLatin1Char(' '));
const QString param_formatted =
QStringLiteral("%1").arg(QString::fromStdString(param), -19, QLatin1Char(' '));
const QString desc_formatted = QStringLiteral("%1 ").arg(QString::fromStdString(desc));
const QString desc_formatted = QStringLiteral("%1 ").arg(QtUtils::FromStdString(desc));
auto* ins_item = new QTableWidgetItem(ins_formatted);
auto* param_item = new QTableWidgetItem(param_formatted);
@ -372,7 +375,7 @@ void CodeViewWidget::Update(const Core::CPUThreadGuard* guard)
branch.is_link = IsBranchInstructionWithLink(ins);
description_item->setText(
tr("--> %1").arg(QString::fromStdString(debug_interface.GetDescription(branch_addr))));
tr("--> %1").arg(QtUtils::FromStdString(debug_interface.GetDescription(branch_addr))));
param_item->setForeground(dark_theme ? QColor(255, 135, 255) : Qt::magenta);
}
@ -382,7 +385,7 @@ void CodeViewWidget::Update(const Core::CPUThreadGuard* guard)
if (debug_interface.IsBreakpoint(addr))
{
auto icon = Resources::GetThemeIcon("debugger_breakpoint").pixmap(QSize(rowh - 2, rowh - 2));
if (!m_system.GetPowerPC().GetBreakPoints().IsBreakPointEnable(addr))
if (!power_pc.GetBreakPoints().IsBreakPointEnable(addr))
{
QPixmap disabled_icon(icon.size());
disabled_icon.fill(Qt::transparent);
@ -410,7 +413,7 @@ void CodeViewWidget::Update(const Core::CPUThreadGuard* guard)
CalculateBranchIndentation();
g_symbolDB.FillInCallers();
m_ppc_symbol_db.FillInCallers();
repaint();
m_updating = false;
@ -555,13 +558,14 @@ void CodeViewWidget::ReplaceAddress(u32 address, ReplaceWith replace)
void CodeViewWidget::OnContextMenu()
{
QMenu* menu = new QMenu(this);
menu->setAttribute(Qt::WA_DeleteOnClose, true);
const bool running = Core::GetState() != Core::State::Uninitialized;
const bool paused = Core::GetState() == Core::State::Paused;
const bool running = Core::GetState(m_system) != Core::State::Uninitialized;
const bool paused = Core::GetState(m_system) == Core::State::Paused;
const u32 addr = GetContextAddress();
bool has_symbol = g_symbolDB.GetSymbolFromAddr(addr);
const bool has_symbol = m_ppc_symbol_db.GetSymbolFromAddr(addr);
auto* follow_branch_action =
menu->addAction(tr("Follow &branch"), this, &CodeViewWidget::OnFollowBranch);
@ -744,6 +748,12 @@ void CodeViewWidget::AutoStep(CodeTrace::AutoStop option)
} while (msgbox.clickedButton() == (QAbstractButton*)run_button);
}
void CodeViewWidget::OnDebugFontChanged(const QFont& font)
{
setFont(font);
FontBasedSizing();
}
void CodeViewWidget::OnCopyAddress()
{
const u32 addr = GetContextAddress();
@ -753,7 +763,7 @@ void CodeViewWidget::OnCopyAddress()
void CodeViewWidget::OnCopyTargetAddress()
{
if (Core::GetState() != Core::State::Paused)
if (Core::GetState(m_system) != Core::State::Paused)
return;
const u32 addr = GetContextAddress();
@ -783,7 +793,7 @@ void CodeViewWidget::OnShowInMemory()
void CodeViewWidget::OnShowTargetInMemory()
{
if (Core::GetState() != Core::State::Paused)
if (Core::GetState(m_system) != Core::State::Paused)
return;
const u32 addr = GetContextAddress();
@ -819,7 +829,7 @@ void CodeViewWidget::OnCopyFunction()
{
const u32 address = GetContextAddress();
const Common::Symbol* symbol = g_symbolDB.GetSymbolFromAddr(address);
const Common::Symbol* const symbol = m_ppc_symbol_db.GetSymbolFromAddr(address);
if (!symbol)
return;
@ -877,9 +887,8 @@ void CodeViewWidget::OnAddFunction()
Core::CPUThreadGuard guard(m_system);
g_symbolDB.AddFunction(guard, addr);
emit SymbolsChanged();
Update(&guard);
m_ppc_symbol_db.AddFunction(guard, addr);
emit Host::GetInstance()->PPCSymbolsChanged();
}
void CodeViewWidget::OnInsertBLR()
@ -915,7 +924,7 @@ void CodeViewWidget::OnRenameSymbol()
{
const u32 addr = GetContextAddress();
Common::Symbol* const symbol = g_symbolDB.GetSymbolFromAddr(addr);
Common::Symbol* const symbol = m_ppc_symbol_db.GetSymbolFromAddr(addr);
if (!symbol)
return;
@ -928,8 +937,7 @@ void CodeViewWidget::OnRenameSymbol()
if (good && !name.isEmpty())
{
symbol->Rename(name.toStdString());
emit SymbolsChanged();
Update();
emit Host::GetInstance()->PPCSymbolsChanged();
}
}
@ -950,7 +958,7 @@ void CodeViewWidget::OnSetSymbolSize()
{
const u32 addr = GetContextAddress();
Common::Symbol* const symbol = g_symbolDB.GetSymbolFromAddr(addr);
Common::Symbol* const symbol = m_ppc_symbol_db.GetSymbolFromAddr(addr);
if (!symbol)
return;
@ -967,15 +975,14 @@ void CodeViewWidget::OnSetSymbolSize()
Core::CPUThreadGuard guard(m_system);
PPCAnalyst::ReanalyzeFunction(guard, symbol->address, *symbol, size);
emit SymbolsChanged();
Update(&guard);
emit Host::GetInstance()->PPCSymbolsChanged();
}
void CodeViewWidget::OnSetSymbolEndAddress()
{
const u32 addr = GetContextAddress();
Common::Symbol* const symbol = g_symbolDB.GetSymbolFromAddr(addr);
Common::Symbol* const symbol = m_ppc_symbol_db.GetSymbolFromAddr(addr);
if (!symbol)
return;
@ -995,8 +1002,7 @@ void CodeViewWidget::OnSetSymbolEndAddress()
Core::CPUThreadGuard guard(m_system);
PPCAnalyst::ReanalyzeFunction(guard, symbol->address, *symbol, address - symbol->address);
emit SymbolsChanged();
Update(&guard);
emit Host::GetInstance()->PPCSymbolsChanged();
}
void CodeViewWidget::OnReplaceInstruction()

View file

@ -10,6 +10,7 @@
#include "Common/CommonTypes.h"
#include "Core/Debugger/CodeTrace.h"
class QFont;
class QKeyEvent;
class QMouseEvent;
class QResizeEvent;
@ -23,6 +24,7 @@ class System;
struct CodeViewBranch;
class BranchDisplayDelegate;
class PPCSymbolDB;
class CodeViewWidget : public QTableWidget
{
@ -55,7 +57,6 @@ public:
signals:
void RequestPPCComparison(u32 addr);
void ShowMemory(u32 address);
void SymbolsChanged();
void BreakpointsChanged();
void UpdateCodeWidget();
@ -77,6 +78,7 @@ private:
void OnContextMenu();
void AutoStep(CodeTrace::AutoStop option = CodeTrace::AutoStop::Always);
void OnDebugFontChanged(const QFont& font);
void OnFollowBranch();
void OnCopyAddress();
void OnCopyTargetAddress();
@ -102,6 +104,7 @@ private:
void CalculateBranchIndentation();
Core::System& m_system;
PPCSymbolDB& m_ppc_symbol_db;
bool m_updating = false;

View file

@ -38,8 +38,7 @@ static const QString BOX_SPLITTER_STYLESHEET = QStringLiteral(
CodeWidget::CodeWidget(QWidget* parent)
: QDockWidget(parent), m_system(Core::System::GetInstance()),
m_branch_watch_dialog(
new BranchWatchDialog(m_system, m_system.GetPowerPC().GetBranchWatch(), this))
m_ppc_symbol_db(m_system.GetPPCSymbolDB())
{
setWindowTitle(tr("Code"));
setObjectName(QStringLiteral("code"));
@ -61,13 +60,11 @@ CodeWidget::CodeWidget(QWidget* parent)
[this](bool visible) { setHidden(!visible); });
connect(Host::GetInstance(), &Host::UpdateDisasmDialog, this, [this] {
if (Core::GetState() == Core::State::Paused)
if (Core::GetState(m_system) == Core::State::Paused)
SetAddress(m_system.GetPPCState().pc, CodeViewWidget::SetAddressUpdate::WithoutUpdate);
Update();
});
connect(Host::GetInstance(), &Host::NotifyMapLoaded, this, &CodeWidget::UpdateSymbols);
connect(&Settings::Instance(), &Settings::DebugModeToggled, this,
[this](bool enabled) { setHidden(!enabled || !Settings::Instance().IsCodeVisible()); });
@ -174,13 +171,11 @@ void CodeWidget::ConnectWidgets()
connect(m_search_address, &QLineEdit::returnPressed, this, &CodeWidget::OnSearchAddress);
connect(m_search_symbols, &QLineEdit::textChanged, this, &CodeWidget::OnSearchSymbols);
connect(m_search_calls, &QLineEdit::textChanged, this, [this]() {
const Common::Symbol* symbol = g_symbolDB.GetSymbolFromAddr(m_code_view->GetAddress());
if (symbol)
if (const Common::Symbol* symbol = m_ppc_symbol_db.GetSymbolFromAddr(m_code_view->GetAddress()))
UpdateFunctionCalls(symbol);
});
connect(m_search_callers, &QLineEdit::textChanged, this, [this]() {
const Common::Symbol* symbol = g_symbolDB.GetSymbolFromAddr(m_code_view->GetAddress());
if (symbol)
if (const Common::Symbol* symbol = m_ppc_symbol_db.GetSymbolFromAddr(m_code_view->GetAddress()))
UpdateFunctionCallers(symbol);
});
connect(m_search_callstack, &QLineEdit::textChanged, this, &CodeWidget::UpdateCallstack);
@ -194,16 +189,7 @@ void CodeWidget::ConnectWidgets()
connect(m_function_callers_list, &QListWidget::itemPressed, this,
&CodeWidget::OnSelectFunctionCallers);
connect(m_code_view, &CodeViewWidget::SymbolsChanged, this, [this]() {
UpdateCallstack();
UpdateSymbols();
const Common::Symbol* symbol = g_symbolDB.GetSymbolFromAddr(m_code_view->GetAddress());
if (symbol)
{
UpdateFunctionCalls(symbol);
UpdateFunctionCallers(symbol);
}
});
connect(Host::GetInstance(), &Host::PPCSymbolsChanged, this, &CodeWidget::OnPPCSymbolsChanged);
connect(m_code_view, &CodeViewWidget::BreakpointsChanged, this,
[this] { emit BreakpointsChanged(); });
connect(m_code_view, &CodeViewWidget::UpdateCodeWidget, this, &CodeWidget::Update);
@ -215,11 +201,27 @@ void CodeWidget::ConnectWidgets()
void CodeWidget::OnBranchWatchDialog()
{
m_branch_watch_dialog->open();
if (m_branch_watch_dialog == nullptr)
{
m_branch_watch_dialog = new BranchWatchDialog(m_system, m_system.GetPowerPC().GetBranchWatch(),
m_ppc_symbol_db, this, this);
}
m_branch_watch_dialog->show();
m_branch_watch_dialog->raise();
m_branch_watch_dialog->activateWindow();
}
void CodeWidget::OnPPCSymbolsChanged()
{
UpdateSymbols();
UpdateCallstack();
if (const Common::Symbol* symbol = m_ppc_symbol_db.GetSymbolFromAddr(m_code_view->GetAddress()))
{
UpdateFunctionCalls(symbol);
UpdateFunctionCallers(symbol);
}
}
void CodeWidget::OnSearchAddress()
{
bool good = true;
@ -258,7 +260,7 @@ void CodeWidget::OnSelectSymbol()
return;
const u32 address = items[0]->data(Qt::UserRole).toUInt();
const Common::Symbol* symbol = g_symbolDB.GetSymbolFromAddr(address);
const Common::Symbol* const symbol = m_ppc_symbol_db.GetSymbolFromAddr(address);
m_code_view->SetAddress(address, CodeViewWidget::SetAddressUpdate::WithUpdate);
UpdateCallstack();
@ -319,7 +321,7 @@ void CodeWidget::Update()
if (!isVisible())
return;
const Common::Symbol* symbol = g_symbolDB.GetSymbolFromAddr(m_code_view->GetAddress());
const Common::Symbol* const symbol = m_ppc_symbol_db.GetSymbolFromAddr(m_code_view->GetAddress());
UpdateCallstack();
@ -337,17 +339,11 @@ void CodeWidget::UpdateCallstack()
{
m_callstack_list->clear();
if (Core::GetState() != Core::State::Paused)
if (Core::GetState(m_system) != Core::State::Paused)
return;
std::vector<Dolphin_Debugger::CallstackEntry> stack;
const bool success = [this, &stack] {
Core::CPUThreadGuard guard(m_system);
return Dolphin_Debugger::GetCallstack(guard, stack);
}();
if (!success)
if (!Dolphin_Debugger::GetCallstack(Core::CPUThreadGuard{m_system}, stack))
{
m_callstack_list->addItem(tr("Invalid callstack"));
return;
@ -375,7 +371,7 @@ void CodeWidget::UpdateSymbols()
m_symbols_list->selectedItems()[0]->text();
m_symbols_list->clear();
for (const auto& symbol : g_symbolDB.Symbols())
for (const auto& symbol : m_ppc_symbol_db.Symbols())
{
QString name = QString::fromStdString(symbol.second.name);
@ -394,10 +390,6 @@ void CodeWidget::UpdateSymbols()
}
m_symbols_list->sortItems();
// TODO: There seems to be a lack of a ubiquitous signal for when symbols change.
// This is the best location to catch the signals from MenuBar and CodeViewWidget.
m_branch_watch_dialog->UpdateSymbols();
}
void CodeWidget::UpdateFunctionCalls(const Common::Symbol* symbol)
@ -408,7 +400,7 @@ void CodeWidget::UpdateFunctionCalls(const Common::Symbol* symbol)
for (const auto& call : symbol->calls)
{
const u32 addr = call.function;
const Common::Symbol* call_symbol = g_symbolDB.GetSymbolFromAddr(addr);
const Common::Symbol* const call_symbol = m_ppc_symbol_db.GetSymbolFromAddr(addr);
if (call_symbol)
{
@ -433,7 +425,7 @@ void CodeWidget::UpdateFunctionCallers(const Common::Symbol* symbol)
for (const auto& caller : symbol->callers)
{
const u32 addr = caller.call_address;
const Common::Symbol* caller_symbol = g_symbolDB.GetSymbolFromAddr(addr);
const Common::Symbol* const caller_symbol = m_ppc_symbol_db.GetSymbolFromAddr(addr);
if (caller_symbol)
{
@ -470,7 +462,8 @@ void CodeWidget::Step()
// Will get a UpdateDisasmDialog(), don't update the GUI here.
// TODO: Step doesn't cause EmulationStateChanged to be emitted, so it has to call this manually.
m_branch_watch_dialog->Update();
if (m_branch_watch_dialog != nullptr)
m_branch_watch_dialog->Update();
}
void CodeWidget::StepOver()

View file

@ -26,6 +26,7 @@ namespace Core
{
class System;
}
class PPCSymbolDB;
class CodeWidget : public QDockWidget
{
@ -60,6 +61,7 @@ private:
void UpdateFunctionCalls(const Common::Symbol* symbol);
void UpdateFunctionCallers(const Common::Symbol* symbol);
void OnPPCSymbolsChanged();
void OnSearchAddress();
void OnSearchSymbols();
void OnSelectSymbol();
@ -71,8 +73,9 @@ private:
void showEvent(QShowEvent* event) override;
Core::System& m_system;
PPCSymbolDB& m_ppc_symbol_db;
BranchWatchDialog* m_branch_watch_dialog;
BranchWatchDialog* m_branch_watch_dialog = nullptr;
QLineEdit* m_search_address;
QPushButton* m_branch_watch;

View file

@ -14,6 +14,7 @@
#include "Common/GekkoDisassembler.h"
#include "Core/Core.h"
#include "Core/PowerPC/PPCAnalyst.h"
#include "Core/System.h"
#include "UICommon/Disassembler.h"
#include "DolphinQt/Host.h"
@ -136,7 +137,7 @@ void JITWidget::Update()
if (!isVisible())
return;
if (!m_address || (Core::GetState() != Core::State::Paused))
if (!m_address || (Core::GetState(Core::System::GetInstance()) != Core::State::Paused))
{
m_ppc_asm_widget->setHtml(QStringLiteral("<i>%1</i>").arg(tr("(ppc)")));
m_host_asm_widget->setHtml(QStringLiteral("<i>%1</i>").arg(tr("(host)")));

View file

@ -3,6 +3,9 @@
#include "DolphinQt/Debugger/MemoryViewWidget.h"
#include <bit>
#include <cmath>
#include <QApplication>
#include <QClipboard>
#include <QHBoxLayout>
@ -14,10 +17,10 @@
#include <QTableWidget>
#include <QtGlobal>
#include <cmath>
#include <fmt/printf.h>
#include "Common/Align.h"
#include "Common/BitUtils.h"
#include "Common/FloatUtils.h"
#include "Common/StringUtil.h"
#include "Common/Swap.h"
@ -154,7 +157,7 @@ public:
u32 end_address = address + static_cast<u32>(bytes.size()) - 1;
AddressSpace::Accessors* accessors = AddressSpace::GetAccessors(m_view->GetAddressSpace());
Core::CPUThreadGuard guard(Core::System::GetInstance());
const Core::CPUThreadGuard guard(m_view->m_system);
if (!bytes.empty() && accessors->IsValidAddress(guard, address) &&
accessors->IsValidAddress(guard, end_address))
@ -170,8 +173,8 @@ private:
MemoryViewWidget* m_view;
};
MemoryViewWidget::MemoryViewWidget(QWidget* parent)
: QWidget(parent), m_system(Core::System::GetInstance())
MemoryViewWidget::MemoryViewWidget(Core::System& system, QWidget* parent)
: QWidget(parent), m_system(system)
{
auto* layout = new QHBoxLayout();
layout->setContentsMargins(0, 0, 0, 0);
@ -202,18 +205,18 @@ MemoryViewWidget::MemoryViewWidget(QWidget* parent)
connect(&Settings::Instance(), &Settings::ThemeChanged, this, &MemoryViewWidget::Update);
// Also calls create table.
UpdateFont();
UpdateFont(Settings::Instance().GetDebugFont());
}
void MemoryViewWidget::UpdateFont()
void MemoryViewWidget::UpdateFont(const QFont& font)
{
const QFontMetrics fm(Settings::Instance().GetDebugFont());
const QFontMetrics fm(font);
m_font_vspace = fm.lineSpacing() + 4;
// BoundingRect is too unpredictable, a custom one would be needed for each view type. Different
// fonts have wildly different spacing between two characters and horizontalAdvance includes
// spacing.
m_font_width = fm.horizontalAdvance(QLatin1Char('0'));
m_table->setFont(Settings::Instance().GetDebugFont());
m_table->setFont(font);
CreateTable();
}
@ -441,9 +444,9 @@ void MemoryViewWidget::UpdateColumns()
if (m_table->item(1, 1) == nullptr)
return;
if (Core::GetState() == Core::State::Paused)
if (Core::GetState(m_system) == Core::State::Paused)
{
Core::CPUThreadGuard guard(Core::System::GetInstance());
const Core::CPUThreadGuard guard(m_system);
UpdateColumns(&guard);
}
else
@ -520,11 +523,11 @@ QString MemoryViewWidget::ValueToString(const Core::CPUThreadGuard& guard, u32 a
case Type::Unsigned32:
return QString::number(accessors->ReadU32(guard, address));
case Type::Signed8:
return QString::number(Common::BitCast<s8>(accessors->ReadU8(guard, address)));
return QString::number(std::bit_cast<s8>(accessors->ReadU8(guard, address)));
case Type::Signed16:
return QString::number(Common::BitCast<s16>(accessors->ReadU16(guard, address)));
return QString::number(std::bit_cast<s16>(accessors->ReadU16(guard, address)));
case Type::Signed32:
return QString::number(Common::BitCast<s32>(accessors->ReadU32(guard, address)));
return QString::number(std::bit_cast<s32>(accessors->ReadU32(guard, address)));
case Type::Float32:
{
QString string = QString::number(accessors->ReadF32(guard, address), 'g', 4);
@ -537,7 +540,7 @@ QString MemoryViewWidget::ValueToString(const Core::CPUThreadGuard& guard, u32 a
case Type::Double:
{
QString string =
QString::number(Common::BitCast<double>(accessors->ReadU64(guard, address)), 'g', 4);
QString::number(std::bit_cast<double>(accessors->ReadU64(guard, address)), 'g', 4);
// Align to first digit.
if (!string.startsWith(QLatin1Char('-')))
string.prepend(QLatin1Char(' '));
@ -635,7 +638,7 @@ std::vector<u8> MemoryViewWidget::ConvertTextToBytes(Type type, QStringView inpu
if (good)
{
const u32 value = Common::BitCast<u32>(float_value);
const u32 value = std::bit_cast<u32>(float_value);
auto std_array = Common::BitCastToArray<u8>(Common::swap32(value));
return std::vector<u8>(std_array.begin(), std_array.end());
}
@ -647,7 +650,7 @@ std::vector<u8> MemoryViewWidget::ConvertTextToBytes(Type type, QStringView inpu
if (good)
{
const u64 value = Common::BitCast<u64>(double_value);
const u64 value = std::bit_cast<u64>(double_value);
auto std_array = Common::BitCastToArray<u8>(Common::swap64(value));
return std::vector<u8>(std_array.begin(), std_array.end());
}
@ -850,12 +853,8 @@ void MemoryViewWidget::OnCopyHex(u32 addr)
{
const auto length = GetTypeSize(m_type);
const AddressSpace::Accessors* accessors = AddressSpace::GetAccessors(m_address_space);
const u64 value = [addr, accessors] {
Core::CPUThreadGuard guard(Core::System::GetInstance());
return accessors->ReadU64(guard, addr);
}();
const u64 value =
AddressSpace::GetAccessors(m_address_space)->ReadU64(Core::CPUThreadGuard{m_system}, addr);
QApplication::clipboard()->setText(
QStringLiteral("%1").arg(value, sizeof(u64) * 2, 16, QLatin1Char('0')).left(length * 2));
@ -873,14 +872,11 @@ void MemoryViewWidget::OnContextMenu(const QPoint& pos)
const u32 addr = item_selected->data(USER_ROLE_CELL_ADDRESS).toUInt();
const bool item_has_value =
item_selected->data(USER_ROLE_VALUE_TYPE).toInt() != static_cast<int>(Type::Null) &&
[this, addr] {
const AddressSpace::Accessors* accessors = AddressSpace::GetAccessors(m_address_space);
Core::CPUThreadGuard guard(Core::System::GetInstance());
return accessors->IsValidAddress(guard, addr);
}();
AddressSpace::GetAccessors(m_address_space)
->IsValidAddress(Core::CPUThreadGuard{m_system}, addr);
auto* menu = new QMenu(this);
menu->setAttribute(Qt::WA_DeleteOnClose, true);
menu->addAction(tr("Copy Address"), this, [this, addr] { OnCopyAddress(addr); });

View file

@ -7,6 +7,7 @@
#include "Common/CommonTypes.h"
class QFont;
class QPoint;
class QScrollBar;
@ -53,11 +54,11 @@ public:
WriteOnly
};
explicit MemoryViewWidget(QWidget* parent = nullptr);
explicit MemoryViewWidget(Core::System& system, QWidget* parent = nullptr);
void CreateTable();
void Update();
void UpdateFont();
void UpdateFont(const QFont& font);
void ToggleBreakpoint(u32 addr, bool row);
std::vector<u8> ConvertTextToBytes(Type type, QStringView input_text) const;

View file

@ -40,7 +40,8 @@
using Type = MemoryViewWidget::Type;
MemoryWidget::MemoryWidget(QWidget* parent) : QDockWidget(parent)
MemoryWidget::MemoryWidget(Core::System& system, QWidget* parent)
: QDockWidget(parent), m_system(system)
{
setWindowTitle(tr("Memory"));
setObjectName(QStringLiteral("memory"));
@ -199,6 +200,7 @@ void MemoryWidget::CreateWidgets()
m_display_combo->addItem(tr("Double"), int(Type::Double));
m_align_combo = new QComboBox;
// i18n: "Fixed" here means that the alignment is always the same
m_align_combo->addItem(tr("Fixed Alignment"));
m_align_combo->addItem(tr("Type-based Alignment"), 0);
m_align_combo->addItem(tr("No Alignment"), 1);
@ -287,7 +289,7 @@ void MemoryWidget::CreateWidgets()
sidebar_scroll->setWidget(sidebar);
sidebar_scroll->setWidgetResizable(true);
m_memory_view = new MemoryViewWidget(this);
m_memory_view = new MemoryViewWidget(m_system, this);
m_splitter->addWidget(m_memory_view);
m_splitter->addWidget(sidebar_scroll);
@ -496,7 +498,7 @@ void MemoryWidget::SetAddress(u32 address)
AddressSpace::Accessors* accessors =
AddressSpace::GetAccessors(m_memory_view->GetAddressSpace());
Core::CPUThreadGuard guard(Core::System::GetInstance());
const Core::CPUThreadGuard guard(m_system);
good = accessors->IsValidAddress(guard, current_addr);
}
@ -627,7 +629,7 @@ QByteArray MemoryWidget::GetInputData() const
void MemoryWidget::OnSetValue()
{
if (!Core::IsRunning())
if (!Core::IsRunning(m_system))
return;
auto target_addr = GetTargetAddress();
@ -653,7 +655,7 @@ void MemoryWidget::OnSetValue()
return;
}
Core::CPUThreadGuard guard(Core::System::GetInstance());
const Core::CPUThreadGuard guard(m_system);
AddressSpace::Accessors* accessors = AddressSpace::GetAccessors(m_memory_view->GetAddressSpace());
u32 end_address = target_addr.address + static_cast<u32>(bytes.size()) - 1;
@ -673,7 +675,7 @@ void MemoryWidget::OnSetValue()
void MemoryWidget::OnSetValueFromFile()
{
if (!Core::IsRunning())
if (!Core::IsRunning(m_system))
return;
auto target_addr = GetTargetAddress();
@ -715,7 +717,7 @@ void MemoryWidget::OnSetValueFromFile()
AddressSpace::Accessors* accessors = AddressSpace::GetAccessors(m_memory_view->GetAddressSpace());
Core::CPUThreadGuard guard(Core::System::GetInstance());
const Core::CPUThreadGuard guard(m_system);
for (u8 b : file_contents)
accessors->WriteU8(guard, target_addr.address++, b);
@ -833,7 +835,7 @@ void MemoryWidget::FindValue(bool next)
AddressSpace::Accessors* accessors =
AddressSpace::GetAccessors(m_memory_view->GetAddressSpace());
Core::CPUThreadGuard guard(Core::System::GetInstance());
const Core::CPUThreadGuard guard(m_system);
return accessors->Search(guard, target_addr.address,
reinterpret_cast<const u8*>(search_for.data()),
static_cast<u32>(search_for.size()), next);

View file

@ -22,14 +22,15 @@ class QSplitter;
namespace Core
{
class System;
class CPUThreadGuard;
}
} // namespace Core
class MemoryWidget : public QDockWidget
{
Q_OBJECT
public:
explicit MemoryWidget(QWidget* parent = nullptr);
explicit MemoryWidget(Core::System& system, QWidget* parent = nullptr);
~MemoryWidget();
void SetAddress(u32 address);
@ -78,6 +79,8 @@ private:
void closeEvent(QCloseEvent*) override;
void showEvent(QShowEvent* event) override;
Core::System& m_system;
MemoryViewWidget* m_memory_view;
QSplitter* m_splitter;
QComboBox* m_search_address;

View file

@ -239,7 +239,8 @@ void NetworkWidget::Update()
if (!isVisible())
return;
if (Core::GetState() != Core::State::Paused)
auto& system = Core::System::GetInstance();
if (Core::GetState(system) != Core::State::Paused)
{
m_socket_table->setDisabled(true);
m_ssl_table->setDisabled(true);
@ -250,9 +251,9 @@ void NetworkWidget::Update()
m_ssl_table->setDisabled(false);
// needed because there's a race condition on the IOS instance otherwise
Core::CPUThreadGuard guard(Core::System::GetInstance());
const Core::CPUThreadGuard guard(system);
auto* ios = guard.GetSystem().GetIOS();
auto* ios = system.GetIOS();
if (!ios)
return;

View file

@ -106,7 +106,7 @@ void RegisterWidget::ConnectWidgets()
connect(m_table, &QTableWidget::customContextMenuRequested, this,
&RegisterWidget::ShowContextMenu);
connect(m_table, &QTableWidget::itemChanged, this, &RegisterWidget::OnItemChanged);
connect(&Settings::Instance(), &Settings::DebugFontChanged, m_table, &QWidget::setFont);
connect(&Settings::Instance(), &Settings::DebugFontChanged, m_table, &RegisterWidget::setFont);
}
void RegisterWidget::OnItemChanged(QTableWidgetItem* item)
@ -118,6 +118,7 @@ void RegisterWidget::OnItemChanged(QTableWidgetItem* item)
void RegisterWidget::ShowContextMenu()
{
QMenu* menu = new QMenu(this);
menu->setAttribute(Qt::WA_DeleteOnClose, true);
auto* raw_item = m_table->currentItem();
@ -533,7 +534,7 @@ void RegisterWidget::AddRegister(int row, int column, RegisterType type, std::st
void RegisterWidget::Update()
{
if (isVisible() && Core::GetState() == Core::State::Paused)
if (isVisible() && Core::GetState(m_system) == Core::State::Paused)
{
m_updating = true;
emit UpdateTable();

View file

@ -3,6 +3,8 @@
#include "DolphinQt/Debugger/ThreadWidget.h"
#include <bit>
#include <QGroupBox>
#include <QHeaderView>
#include <QLabel>
@ -11,12 +13,12 @@
#include <QTableWidget>
#include <QVBoxLayout>
#include "Common/BitUtils.h"
#include "Core/Core.h"
#include "Core/PowerPC/MMU.h"
#include "Core/PowerPC/PowerPC.h"
#include "Core/System.h"
#include "DolphinQt/Host.h"
#include "DolphinQt/QtUtils/FromStdString.h"
#include "DolphinQt/Settings.h"
ThreadWidget::ThreadWidget(QWidget* parent) : QDockWidget(parent)
@ -118,6 +120,8 @@ void ThreadWidget::ShowContextMenu(QTableWidget* table)
return;
QMenu* menu = new QMenu(this);
menu->setAttribute(Qt::WA_DeleteOnClose, true);
const QString watch_name = QStringLiteral("thread_context_%1").arg(addr, 8, 16, QLatin1Char('0'));
menu->addAction(tr("Add &breakpoint"), this, [this, addr] { emit RequestBreakpoint(addr); });
menu->addAction(tr("Add memory breakpoint"), this,
@ -252,13 +256,14 @@ void ThreadWidget::Update()
if (!isVisible())
return;
const auto emu_state = Core::GetState();
auto& system = Core::System::GetInstance();
const auto emu_state = Core::GetState(system);
if (emu_state == Core::State::Stopping)
{
m_thread_table->setRowCount(0);
UpdateThreadContext({});
Core::CPUThreadGuard guard(Core::System::GetInstance());
const Core::CPUThreadGuard guard(system);
UpdateThreadCallstack(guard, {});
}
if (emu_state != Core::State::Paused)
@ -303,7 +308,7 @@ void ThreadWidget::Update()
};
{
Core::CPUThreadGuard guard(Core::System::GetInstance());
const Core::CPUThreadGuard guard(system);
// YAGCD - Section 4.2.1.4 Dolphin OS Globals
m_current_context->setText(format_hex_from(guard, 0x800000D4));
@ -359,8 +364,7 @@ void ThreadWidget::UpdateThreadContext(const Common::Debug::PartialContext& cont
const auto format_f64_as_u64_idx = [](const auto& table, std::size_t index) {
if (!table || index >= table->size())
return QString{};
return QStringLiteral("%1").arg(Common::BitCast<u64>(table->at(index)), 16, 16,
QLatin1Char('0'));
return QStringLiteral("%1").arg(std::bit_cast<u64>(table->at(index)), 16, 16, QLatin1Char('0'));
};
m_context_table->setRowCount(0);
@ -460,7 +464,7 @@ void ThreadWidget::UpdateThreadCallstack(const Core::CPUThreadGuard& guard,
m_callstack_table->setItem(i, 2, new QTableWidgetItem(format_hex(lr_save)));
m_callstack_table->setItem(
i, 3,
new QTableWidgetItem(QString::fromStdString(
new QTableWidgetItem(QtUtils::FromStdString(
guard.GetSystem().GetPowerPC().GetDebugInterface().GetDescription(lr_save))));
}
else

View file

@ -143,7 +143,7 @@ void WatchWidget::UpdateButtonsEnabled()
if (!isVisible())
return;
const bool is_enabled = Core::IsRunning();
const bool is_enabled = Core::IsRunning(m_system);
m_new->setEnabled(is_enabled);
m_delete->setEnabled(is_enabled);
m_clear->setEnabled(is_enabled);
@ -158,7 +158,7 @@ void WatchWidget::Update()
m_updating = true;
if (Core::GetState() != Core::State::Paused)
if (Core::GetState(m_system) != Core::State::Paused)
{
m_table->setDisabled(true);
m_updating = false;
@ -195,10 +195,10 @@ void WatchWidget::Update()
QBrush brush = QPalette().brush(QPalette::Text);
if (!Core::IsRunning() || !PowerPC::MMU::HostIsRAMAddress(guard, entry.address))
if (!Core::IsRunning(m_system) || !PowerPC::MMU::HostIsRAMAddress(guard, entry.address))
brush.setColor(Qt::red);
if (Core::IsRunning())
if (Core::IsRunning(m_system))
{
if (PowerPC::MMU::HostIsRAMAddress(guard, entry.address))
{
@ -327,6 +327,7 @@ void WatchWidget::OnSave()
void WatchWidget::ShowContextMenu()
{
QMenu* menu = new QMenu(this);
menu->setAttribute(Qt::WA_DeleteOnClose, true);
if (!m_table->selectedItems().empty())
{

View file

@ -55,6 +55,7 @@
<ClCompile Include="CheatSearchFactoryWidget.cpp" />
<ClCompile Include="CheatSearchWidget.cpp" />
<ClCompile Include="CheatsManager.cpp" />
<ClCompile Include="Achievements\AchievementBox.cpp" />
<ClCompile Include="Achievements\AchievementHeaderWidget.cpp" />
<ClCompile Include="Achievements\AchievementLeaderboardWidget.cpp" />
<ClCompile Include="Achievements\AchievementProgressWidget.cpp" />
@ -274,6 +275,7 @@
<QtMoc Include="CheatSearchFactoryWidget.h" />
<QtMoc Include="CheatSearchWidget.h" />
<QtMoc Include="CheatsManager.h" />
<QtMoc Include="Achievements\AchievementBox.h" />
<QtMoc Include="Achievements\AchievementHeaderWidget.h" />
<QtMoc Include="Achievements\AchievementLeaderboardWidget.h" />
<QtMoc Include="Achievements\AchievementProgressWidget.h" />
@ -403,6 +405,7 @@
<QtMoc Include="QtUtils\DoubleClickEventFilter.h" />
<QtMoc Include="QtUtils\ElidedButton.h" />
<QtMoc Include="QtUtils\FileOpenEventFilter.h" />
<ClInclude Include="QtUtils\FromStdString.h" />
<QtMoc Include="QtUtils\ParallelProgressDialog.h" />
<QtMoc Include="QtUtils\PartiallyClosableTabWidget.h" />
<ClInclude Include="QtUtils\SetWindowDecorations.h" />

View file

@ -4,6 +4,7 @@
#include "DolphinQt/FIFO/FIFOAnalyzer.h"
#include <algorithm>
#include <bit>
#include <QGroupBox>
#include <QHBoxLayout>
@ -51,13 +52,9 @@ FIFOAnalyzer::FIFOAnalyzer(FifoPlayer& fifo_player) : m_fifo_player(fifo_player)
m_search_splitter->restoreState(
settings.value(QStringLiteral("fifoanalyzer/searchsplitter")).toByteArray());
m_detail_list->setFont(Settings::Instance().GetDebugFont());
m_entry_detail_browser->setFont(Settings::Instance().GetDebugFont());
connect(&Settings::Instance(), &Settings::DebugFontChanged, this, [this] {
m_detail_list->setFont(Settings::Instance().GetDebugFont());
m_entry_detail_browser->setFont(Settings::Instance().GetDebugFont());
});
OnDebugFontChanged(Settings::Instance().GetDebugFont());
connect(&Settings::Instance(), &Settings::DebugFontChanged, this,
&FIFOAnalyzer::OnDebugFontChanged);
}
FIFOAnalyzer::~FIFOAnalyzer()
@ -650,7 +647,7 @@ public:
}
if (format == ComponentFormat::Float)
{
const float value = Common::BitCast<float>(Common::swap32(&vertex_data[i]));
const float value = std::bit_cast<float>(Common::swap32(&vertex_data[i]));
text += QStringLiteral(" (%1)").arg(value);
}
i += component_size;
@ -779,3 +776,9 @@ void FIFOAnalyzer::UpdateDescription()
object_size - entry_start, callback);
m_entry_detail_browser->setText(callback.text);
}
void FIFOAnalyzer::OnDebugFontChanged(const QFont& font)
{
m_detail_list->setFont(font);
m_entry_detail_browser->setFont(font);
}

View file

@ -10,6 +10,7 @@
#include "Common/CommonTypes.h"
class FifoPlayer;
class QFont;
class QGroupBox;
class QLabel;
class QLineEdit;
@ -43,6 +44,8 @@ private:
void UpdateDetails();
void UpdateDescription();
void OnDebugFontChanged(const QFont& font);
FifoPlayer& m_fifo_player;
QTreeWidget* m_tree_widget;

View file

@ -23,6 +23,7 @@
#include "Core/FifoPlayer/FifoDataFile.h"
#include "Core/FifoPlayer/FifoPlayer.h"
#include "Core/FifoPlayer/FifoRecorder.h"
#include "Core/System.h"
#include "DolphinQt/Config/ToolTipControls/ToolTipCheckBox.h"
#include "DolphinQt/FIFO/FIFOAnalyzer.h"
@ -316,7 +317,7 @@ void FIFOPlayerWindow::UpdateInfo()
return;
}
if (Core::IsRunning() && m_fifo_recorder.IsRecording())
if (Core::IsRunning(Core::System::GetInstance()) && m_fifo_recorder.IsRecording())
{
m_info_label->setText(tr("Recording..."));
return;
@ -375,7 +376,7 @@ void FIFOPlayerWindow::UpdateLimits()
void FIFOPlayerWindow::UpdateControls()
{
bool running = Core::IsRunning();
bool running = Core::IsRunning(Core::System::GetInstance());
bool is_recording = m_fifo_recorder.IsRecording();
bool is_playing = m_fifo_player.IsPlaying();

View file

@ -37,7 +37,8 @@
static void RestartCore(const std::weak_ptr<HW::GBA::Core>& core, std::string_view rom_path = {})
{
Core::RunOnCPUThread(
[core, rom_path = std::string(rom_path)] {
Core::System::GetInstance(),
[core, rom_path = std::string(rom_path)]() {
if (auto core_ptr = core.lock())
{
auto& info = Config::MAIN_GBA_ROM_PATHS[core_ptr->GetCoreInfo().device_number];
@ -57,7 +58,8 @@ static void RestartCore(const std::weak_ptr<HW::GBA::Core>& core, std::string_vi
static void QueueEReaderCard(const std::weak_ptr<HW::GBA::Core>& core, std::string_view card_path)
{
Core::RunOnCPUThread(
[core, card_path = std::string(card_path)] {
Core::System::GetInstance(),
[core, card_path = std::string(card_path)]() {
if (auto core_ptr = core.lock())
core_ptr->EReaderQueueCard(card_path);
},
@ -159,7 +161,8 @@ void GBAWidget::ToggleDisconnect()
m_force_disconnect = !m_force_disconnect;
Core::RunOnCPUThread(
[core = m_core, force_disconnect = m_force_disconnect] {
Core::System::GetInstance(),
[core = m_core, force_disconnect = m_force_disconnect]() {
if (auto core_ptr = core.lock())
core_ptr->SetForceDisconnect(force_disconnect);
},
@ -221,7 +224,8 @@ void GBAWidget::DoState(bool export_state)
return;
Core::RunOnCPUThread(
[export_state, core = m_core, state_path = state_path.toStdString()] {
Core::System::GetInstance(),
[export_state, core = m_core, state_path = state_path.toStdString()]() {
if (auto core_ptr = core.lock())
{
if (export_state)
@ -251,7 +255,8 @@ void GBAWidget::ImportExportSave(bool export_save)
return;
Core::RunOnCPUThread(
[export_save, core = m_core, save_path = save_path.toStdString()] {
Core::System::GetInstance(),
[export_save, core = m_core, save_path = save_path.toStdString()]() {
if (auto core_ptr = core.lock())
{
if (export_save)

View file

@ -370,8 +370,10 @@ void GameList::ShowContextMenu(const QPoint&)
{
if (!GetSelectedGame())
return;
auto& system = Core::System::GetInstance();
QMenu* menu = new QMenu(this);
menu->setAttribute(Qt::WA_DeleteOnClose, true);
if (HasMultipleSelected())
{
@ -421,8 +423,8 @@ void GameList::ShowContextMenu(const QPoint&)
QAction* change_disc = menu->addAction(tr("Change &Disc"), this, &GameList::ChangeDisc);
connect(&Settings::Instance(), &Settings::EmulationStateChanged, change_disc,
[change_disc] { change_disc->setEnabled(Core::IsRunning()); });
change_disc->setEnabled(Core::IsRunning());
[&system, change_disc] { change_disc->setEnabled(Core::IsRunning(system)); });
change_disc->setEnabled(Core::IsRunning(system));
menu->addSeparator();
}
@ -436,7 +438,7 @@ void GameList::ShowContextMenu(const QPoint&)
// system menu, trigger a refresh.
Settings::Instance().NANDRefresh();
});
perform_disc_update->setEnabled(!Core::IsRunning() || !Core::System::GetInstance().IsWii());
perform_disc_update->setEnabled(!Core::IsRunning(system) || !system.IsWii());
}
if (!is_mod_descriptor && platform == DiscIO::Platform::WiiWAD)
@ -449,10 +451,10 @@ void GameList::ShowContextMenu(const QPoint&)
for (QAction* a : {wad_install_action, wad_uninstall_action})
{
a->setEnabled(!Core::IsRunning());
a->setEnabled(!Core::IsRunning(system));
menu->addAction(a);
}
if (!Core::IsRunning())
if (!Core::IsRunning(system))
wad_uninstall_action->setEnabled(WiiUtils::IsTitleInstalled(game->GetTitleID()));
connect(&Settings::Instance(), &Settings::EmulationStateChanged, menu,
@ -473,8 +475,8 @@ void GameList::ShowContextMenu(const QPoint&)
QAction* export_wii_save =
menu->addAction(tr("Export Wii Save"), this, &GameList::ExportWiiSave);
open_wii_save_folder->setEnabled(!Core::IsRunning());
export_wii_save->setEnabled(!Core::IsRunning());
open_wii_save_folder->setEnabled(!Core::IsRunning(system));
export_wii_save->setEnabled(!Core::IsRunning(system));
menu->addSeparator();
}
@ -531,7 +533,7 @@ void GameList::ShowContextMenu(const QPoint&)
connect(&Settings::Instance(), &Settings::EmulationStateChanged, menu, [=](Core::State state) {
netplay_host->setEnabled(state == Core::State::Uninitialized);
});
netplay_host->setEnabled(!Core::IsRunning());
netplay_host->setEnabled(!Core::IsRunning(system));
menu->addAction(netplay_host);
}
@ -879,9 +881,8 @@ void GameList::ChangeDisc()
if (!game)
return;
Core::RunAsCPUThread([file_path = game->GetFilePath()] {
Core::System::GetInstance().GetDVDInterface().ChangeDisc(file_path);
});
auto& system = Core::System::GetInstance();
system.GetDVDInterface().ChangeDisc(Core::CPUThreadGuard{system}, game->GetFilePath());
}
QAbstractItemView* GameList::GetActiveView() const

View file

@ -92,14 +92,14 @@ QVariant GameListModel::data(const QModelIndex& index, int role) const
{
QString name = QString::fromStdString(game.GetName(m_title_database));
// Add disc numbers > 1 to title if not present.
const int disc_nr = game.GetDiscNumber() + 1;
if (disc_nr > 1)
const int disc_number = game.GetDiscNumber() + 1;
if (disc_number > 1 || game.IsTwoDiscGame())
{
if (!name.contains(QRegularExpression(QStringLiteral("disc ?%1").arg(disc_nr),
// Add disc number to title if not present.
if (!name.contains(QRegularExpression(QStringLiteral("disc ?%1").arg(disc_number),
QRegularExpression::CaseInsensitiveOption)))
{
name.append(tr(" (Disc %1)").arg(disc_nr));
name.append(tr(" (Disc %1)").arg(disc_number));
}
}

View file

@ -103,8 +103,8 @@ static void RunWithGPUThreadInactive(std::function<void()> f)
// (Note that this case cannot be reached in single core mode, because in single core mode,
// the CPU and GPU threads are the same thread, and we already checked for the GPU thread.)
const bool was_running = Core::GetState() == Core::State::Running;
auto& system = Core::System::GetInstance();
const bool was_running = Core::GetState(system) == Core::State::Running;
auto& fifo = system.GetFifo();
fifo.PauseAndLock(true, was_running);
f();
@ -112,9 +112,9 @@ static void RunWithGPUThreadInactive(std::function<void()> f)
}
else
{
// If we reach here, we can call Core::PauseAndLock (which we do using RunAsCPUThread).
Core::RunAsCPUThread(std::move(f));
// If we reach here, we can call Core::PauseAndLock (which we do using a CPUThreadGuard).
const Core::CPUThreadGuard guard(Core::System::GetInstance());
f();
}
}
@ -238,14 +238,9 @@ void Host_UpdateDisasmDialog()
QueueOnObject(QApplication::instance(), [] { emit Host::GetInstance()->UpdateDisasmDialog(); });
}
void Host::RequestNotifyMapLoaded()
void Host_PPCSymbolsChanged()
{
QueueOnObject(QApplication::instance(), [this] { emit NotifyMapLoaded(); });
}
void Host_NotifyMapLoaded()
{
Host::GetInstance()->RequestNotifyMapLoaded();
QueueOnObject(QApplication::instance(), [] { emit Host::GetInstance()->PPCSymbolsChanged(); });
}
// We ignore these, and their purpose should be questioned individually.

View file

@ -32,14 +32,13 @@ public:
void SetRenderFullFocus(bool focus);
void SetRenderFullscreen(bool fullscreen);
void ResizeSurface(int new_width, int new_height);
void RequestNotifyMapLoaded();
signals:
void RequestTitle(const QString& title);
void RequestStop();
void RequestRenderSize(int w, int h);
void UpdateDisasmDialog();
void NotifyMapLoaded();
void PPCSymbolsChanged();
private:
Host();

View file

@ -113,7 +113,7 @@ static void HandleFrameStepHotkeys()
if ((frame_step_count == 0 || frame_step_count == FRAME_STEP_DELAY) && !frame_step_hold)
{
Core::DoFrameStep();
Core::DoFrameStep(Core::System::GetInstance());
frame_step_hold = true;
}
@ -159,7 +159,7 @@ void HotkeyScheduler::Run()
if (!HotkeyManagerEmu::IsEnabled())
continue;
if (Core::GetState() != Core::State::Stopping)
if (Core::GetState(Core::System::GetInstance()) != Core::State::Stopping)
{
// Obey window focus (config permitting) before checking hotkeys.
Core::UpdateInputGate(Config::Get(Config::MAIN_FOCUSED_HOTKEYS));

View file

@ -35,6 +35,7 @@ static QString s_last_figure_path;
InfinityBaseWindow::InfinityBaseWindow(QWidget* parent) : QWidget(parent)
{
// i18n: Window for managing Disney Infinity figures
setWindowTitle(tr("Infinity Manager"));
setObjectName(QStringLiteral("infinity_manager"));
setMinimumSize(QSize(700, 200));
@ -46,7 +47,7 @@ InfinityBaseWindow::InfinityBaseWindow(QWidget* parent) : QWidget(parent)
installEventFilter(this);
OnEmulationStateChanged(Core::GetState());
OnEmulationStateChanged(Core::GetState(Core::System::GetInstance()));
};
InfinityBaseWindow::~InfinityBaseWindow() = default;
@ -164,7 +165,7 @@ void InfinityBaseWindow::LoadFigurePath(u8 slot, const QString& path)
{
QMessageBox::warning(
this, tr("Failed to open the Infinity file!"),
tr("Failed to open the Infinity file(%1)!\nFile may already be in use on the base.")
tr("Failed to open the Infinity file:\n%1\n\nThe file may already be in use on the base.")
.arg(path),
QMessageBox::Ok);
return;
@ -172,9 +173,10 @@ void InfinityBaseWindow::LoadFigurePath(u8 slot, const QString& path)
std::array<u8, IOS::HLE::USB::INFINITY_NUM_BLOCKS * IOS::HLE::USB::INFINITY_BLOCK_SIZE> file_data;
if (!inf_file.ReadBytes(file_data.data(), file_data.size()))
{
QMessageBox::warning(this, tr("Failed to read the Infinity file!"),
tr("Failed to read the Infinity file(%1)!\nFile was too small.").arg(path),
QMessageBox::Ok);
QMessageBox::warning(
this, tr("Failed to read the Infinity file!"),
tr("Failed to read the Infinity file(%1):\n%1\n\nThe file was too small.").arg(path),
QMessageBox::Ok);
return;
}
@ -274,6 +276,7 @@ CreateFigureDialog::CreateFigureDialog(QWidget* parent, u8 slot) : QDialog(paren
}
else
{
// i18n: This is used to create a file name. The string must end in ".bin".
QString str = tr("Unknown(%1).bin");
predef_name += str.arg(char_number);
}
@ -289,7 +292,7 @@ CreateFigureDialog::CreateFigureDialog(QWidget* parent, u8 slot) : QDialog(paren
{
QMessageBox::warning(
this, tr("Failed to create Infinity file"),
tr("Blank figure creation failed at:\n%1, try again with a different character")
tr("Blank figure creation failed at:\n%1\n\nTry again with a different character.")
.arg(m_file_path),
QMessageBox::Ok);
return;

View file

@ -30,6 +30,7 @@
#include "Core/Config/MainSettings.h"
#include "Core/Core.h"
#include "Core/DolphinAnalytics.h"
#include "Core/System.h"
#include "DolphinQt/Host.h"
#include "DolphinQt/MainWindow.h"
@ -54,7 +55,7 @@ static bool QtMsgAlertHandler(const char* caption, const char* text, bool yes_no
std::optional<bool> r = RunOnObject(QApplication::instance(), [&] {
// If we were called from the CPU/GPU thread, set us as the CPU/GPU thread.
// This information is used in order to avoid deadlocks when calling e.g.
// Host::SetRenderFocus or Core::RunAsCPUThread. (Host::SetRenderFocus
// Host::SetRenderFocus or Core::CPUThreadGuard. (Host::SetRenderFocus
// can get called automatically when a dialog steals the focus.)
Common::ScopeGuard cpu_scope_guard(&Core::UndeclareAsCPUThread);
@ -187,7 +188,7 @@ int main(int argc, char* argv[])
// Whenever the event loop is about to go to sleep, dispatch the jobs
// queued in the Core first.
QObject::connect(QAbstractEventDispatcher::instance(), &QAbstractEventDispatcher::aboutToBlock,
&app, &Core::HostDispatchJobs);
&app, [] { Core::HostDispatchJobs(Core::System::GetInstance()); });
std::optional<std::string> save_state_path;
if (options.is_set("save_state"))
@ -303,7 +304,7 @@ int main(int argc, char* argv[])
retval = app.exec();
}
Core::Shutdown();
Core::Shutdown(Core::System::GetInstance());
UICommon::Shutdown();
Host::GetInstance()->deleteLater();

View file

@ -451,14 +451,14 @@ void MainWindow::CreateComponents()
m_jit_widget = new JITWidget(this);
m_log_widget = new LogWidget(this);
m_log_config_widget = new LogConfigWidget(this);
m_memory_widget = new MemoryWidget(this);
m_memory_widget = new MemoryWidget(Core::System::GetInstance(), this);
m_network_widget = new NetworkWidget(this);
m_register_widget = new RegisterWidget(this);
m_thread_widget = new ThreadWidget(this);
m_watch_widget = new WatchWidget(this);
m_breakpoint_widget = new BreakpointWidget(this);
m_code_widget = new CodeWidget(this);
m_cheats_manager = new CheatsManager(this);
m_cheats_manager = new CheatsManager(Core::System::GetInstance(), this);
m_assembler_widget = new AssemblerWidget(this);
const auto request_watch = [this](QString name, u32 addr) {
@ -501,7 +501,7 @@ void MainWindow::CreateComponents()
connect(m_breakpoint_widget, &BreakpointWidget::BreakpointsChanged, m_memory_widget,
&MemoryWidget::Update);
connect(m_breakpoint_widget, &BreakpointWidget::ShowCode, [this](u32 address) {
if (Core::GetState() == Core::State::Paused)
if (Core::GetState(Core::System::GetInstance()) == Core::State::Paused)
m_code_widget->SetAddress(address, CodeViewWidget::SetAddressUpdate::WithDetailedUpdate);
});
connect(m_breakpoint_widget, &BreakpointWidget::ShowMemory, m_memory_widget,
@ -591,12 +591,6 @@ void MainWindow::ConnectMenuBar()
connect(m_game_list, &GameList::SelectionChanged, m_menu_bar, &MenuBar::SelectionChanged);
connect(this, &MainWindow::ReadOnlyModeChanged, m_menu_bar, &MenuBar::ReadOnlyModeChanged);
connect(this, &MainWindow::RecordingStatusChanged, m_menu_bar, &MenuBar::RecordingStatusChanged);
// Symbols
connect(m_menu_bar, &MenuBar::NotifySymbolsUpdated, [this] {
m_code_widget->UpdateSymbols();
m_code_widget->Update();
});
}
void MainWindow::ConnectHotkeys()
@ -796,15 +790,17 @@ void MainWindow::ChangeDisc()
{
std::vector<std::string> paths = StringListToStdVector(PromptFileNames());
if (!paths.empty())
Core::RunAsCPUThread(
[&paths] { Core::System::GetInstance().GetDVDInterface().ChangeDisc(paths); });
if (paths.empty())
return;
auto& system = Core::System::GetInstance();
system.GetDVDInterface().ChangeDisc(Core::CPUThreadGuard{system}, paths);
}
void MainWindow::EjectDisc()
{
Core::RunAsCPUThread(
[] { Core::System::GetInstance().GetDVDInterface().EjectDisc(DVD::EjectCause::User); });
auto& system = Core::System::GetInstance();
system.GetDVDInterface().EjectDisc(Core::CPUThreadGuard{system}, DVD::EjectCause::User);
}
void MainWindow::OpenUserFolder()
@ -829,9 +825,9 @@ void MainWindow::Play(const std::optional<std::string>& savestate_path)
// Otherwise, play the default game.
// Otherwise, play the last played game, if there is one.
// Otherwise, prompt for a new game.
if (Core::GetState() == Core::State::Paused)
if (Core::GetState(Core::System::GetInstance()) == Core::State::Paused)
{
Core::SetState(Core::State::Running);
Core::SetState(Core::System::GetInstance(), Core::State::Running);
}
else
{
@ -859,12 +855,12 @@ void MainWindow::Play(const std::optional<std::string>& savestate_path)
void MainWindow::Pause()
{
Core::SetState(Core::State::Paused);
Core::SetState(Core::System::GetInstance(), Core::State::Paused);
}
void MainWindow::TogglePause()
{
if (Core::GetState() == Core::State::Paused)
if (Core::GetState(Core::System::GetInstance()) == Core::State::Paused)
{
Play();
}
@ -907,9 +903,9 @@ void MainWindow::OnStopComplete()
bool MainWindow::RequestStop()
{
if (!Core::IsRunning())
if (!Core::IsRunning(Core::System::GetInstance()))
{
Core::QueueHostJob([this] { OnStopComplete(); }, true);
Core::QueueHostJob([this](Core::System&) { OnStopComplete(); }, true);
return true;
}
@ -932,13 +928,13 @@ bool MainWindow::RequestStop()
Common::ScopeGuard confirm_lock([this] { m_stop_confirm_showing = false; });
const Core::State state = Core::GetState();
const Core::State state = Core::GetState(Core::System::GetInstance());
// Only pause the game, if NetPlay is not running
bool pause = !Settings::Instance().GetNetPlayClient();
if (pause)
Core::SetState(Core::State::Paused);
Core::SetState(Core::System::GetInstance(), Core::State::Paused);
if (rendered_widget_was_active)
{
@ -968,7 +964,7 @@ bool MainWindow::RequestStop()
m_render_widget->SetWaitingForMessageBox(false);
if (pause)
Core::SetState(state);
Core::SetState(Core::System::GetInstance(), state);
return false;
}
@ -989,8 +985,8 @@ bool MainWindow::RequestStop()
// Unpause because gracefully shutting down needs the game to actually request a shutdown.
// TODO: Do not unpause in debug mode to allow debugging until the complete shutdown.
if (Core::GetState() == Core::State::Paused)
Core::SetState(Core::State::Running);
if (Core::GetState(Core::System::GetInstance()) == Core::State::Paused)
Core::SetState(Core::System::GetInstance(), Core::State::Running);
// Tell NetPlay about the power event
if (NetPlay::IsNetPlayRunning())
@ -1009,9 +1005,9 @@ bool MainWindow::RequestStop()
bool MainWindow::RequestStopNetplay()
{
if (!Core::IsRunning())
if (!Core::IsRunning(Core::System::GetInstance()))
{
Core::QueueHostJob([this] { OnStopComplete(); }, true);
Core::QueueHostJob([this](Core::System&) { OnStopComplete(); }, true);
return true;
}
@ -1034,13 +1030,13 @@ bool MainWindow::RequestStopNetplay()
Common::ScopeGuard confirm_lock([this] { m_stop_confirm_showing = false; });
const Core::State state = Core::GetState();
const Core::State state = Core::GetState(Core::System::GetInstance());
// Only pause the game, if NetPlay is not running
bool pause = !Settings::Instance().GetNetPlayClient();
if (pause)
Core::SetState(Core::State::Paused);
Core::SetState(Core::System::GetInstance(), Core::State::Paused);
if (rendered_widget_was_active)
{
@ -1072,7 +1068,7 @@ bool MainWindow::RequestStopNetplay()
m_render_widget->SetWaitingForMessageBox(false);
if (pause)
Core::SetState(state);
Core::SetState(Core::System::GetInstance(), state);
return false;
}
@ -1093,8 +1089,8 @@ bool MainWindow::RequestStopNetplay()
// Unpause because gracefully shutting down needs the game to actually request a shutdown.
// TODO: Do not unpause in debug mode to allow debugging until the complete shutdown.
if (Core::GetState() == Core::State::Paused)
Core::SetState(Core::State::Running);
if (Core::GetState(Core::System::GetInstance()) == Core::State::Paused)
Core::SetState(Core::System::GetInstance(), Core::State::Running);
// Tell NetPlay about the power event
if (NetPlay::IsNetPlayRunning())
@ -1113,7 +1109,7 @@ bool MainWindow::RequestStopNetplay()
void MainWindow::ForceStop()
{
Core::Stop();
Core::Stop(Core::System::GetInstance());
}
void MainWindow::Reset()
@ -1127,7 +1123,7 @@ void MainWindow::Reset()
void MainWindow::FrameAdvance()
{
Core::DoFrameStep();
Core::DoFrameStep(Core::System::GetInstance());
}
void MainWindow::FullScreen()
@ -1218,7 +1214,7 @@ void MainWindow::StartGame(std::unique_ptr<BootParameters>&& parameters)
}
// If we're running, only start a new game once we've stopped the last.
if (Core::GetState() != Core::State::Uninitialized)
if (Core::GetState(Core::System::GetInstance()) != Core::State::Uninitialized)
{
if (!RequestStop())
return;
@ -1232,7 +1228,7 @@ void MainWindow::StartGame(std::unique_ptr<BootParameters>&& parameters)
ShowRenderWidget();
// Boot up, show an error if it fails to load the game.
if (!BootManager::BootCore(std::move(parameters),
if (!BootManager::BootCore(Core::System::GetInstance(), std::move(parameters),
::GetWindowSystemInfo(m_render_widget->windowHandle())))
{
ModalMessageBox::critical(this, tr("Error"), tr("Failed to init core"), QMessageBox::Ok);
@ -1518,7 +1514,7 @@ void MainWindow::StateLoad()
this, tr("Select a File"), dialog_path, tr("All Save States (*.sav *.s##);; All Files (*)"));
Config::SetBase(Config::MAIN_CURRENT_STATE_PATH, QFileInfo(path).dir().path().toStdString());
if (!path.isEmpty())
State::LoadAs(path.toStdString());
State::LoadAs(Core::System::GetInstance(), path.toStdString());
}
void MainWindow::StateSave()
@ -1530,47 +1526,47 @@ void MainWindow::StateSave()
this, tr("Select a File"), dialog_path, tr("All Save States (*.sav *.s##);; All Files (*)"));
Config::SetBase(Config::MAIN_CURRENT_STATE_PATH, QFileInfo(path).dir().path().toStdString());
if (!path.isEmpty())
State::SaveAs(path.toStdString());
State::SaveAs(Core::System::GetInstance(), path.toStdString());
}
void MainWindow::StateLoadSlot()
{
State::Load(m_state_slot);
State::Load(Core::System::GetInstance(), m_state_slot);
}
void MainWindow::StateSaveSlot()
{
State::Save(m_state_slot);
State::Save(Core::System::GetInstance(), m_state_slot);
}
void MainWindow::StateLoadSlotAt(int slot)
{
State::Load(slot);
State::Load(Core::System::GetInstance(), slot);
}
void MainWindow::StateLoadLastSavedAt(int slot)
{
State::LoadLastSaved(slot);
State::LoadLastSaved(Core::System::GetInstance(), slot);
}
void MainWindow::StateSaveSlotAt(int slot)
{
State::Save(slot);
State::Save(Core::System::GetInstance(), slot);
}
void MainWindow::StateLoadUndo()
{
State::UndoLoadState();
State::UndoLoadState(Core::System::GetInstance());
}
void MainWindow::StateSaveUndo()
{
State::UndoSaveState();
State::UndoSaveState(Core::System::GetInstance());
}
void MainWindow::StateSaveOldest()
{
State::SaveFirstSaved();
State::SaveFirstSaved(Core::System::GetInstance());
}
void MainWindow::SetStateSlot(int slot)
@ -1643,7 +1639,7 @@ void MainWindow::NetPlayInit()
bool MainWindow::NetPlayJoin()
{
if (Core::IsRunning())
if (Core::IsRunning(Core::System::GetInstance()))
{
ModalMessageBox::critical(nullptr, tr("Error"),
tr("Can't start a NetPlay Session while a game is still running!"));
@ -1710,7 +1706,7 @@ bool MainWindow::NetPlayJoin()
bool MainWindow::NetPlayHost(const UICommon::GameFile& game)
{
if (Core::IsRunning())
if (Core::IsRunning(Core::System::GetInstance()))
{
ModalMessageBox::critical(nullptr, tr("Error"),
tr("Can't start a NetPlay Session while a game is still running!"));
@ -1771,8 +1767,8 @@ void MainWindow::NetPlayQuit()
void MainWindow::UpdateScreenSaverInhibition()
{
const bool inhibit =
Config::Get(Config::MAIN_DISABLE_SCREENSAVER) && (Core::GetState() == Core::State::Running);
const bool inhibit = Config::Get(Config::MAIN_DISABLE_SCREENSAVER) &&
(Core::GetState(Core::System::GetInstance()) == Core::State::Running);
if (inhibit == m_is_screensaver_inhibited)
return;
@ -1957,7 +1953,7 @@ void MainWindow::OnImportNANDBackup()
result.wait();
m_menu_bar->UpdateToolsMenu(Core::IsRunning());
m_menu_bar->UpdateToolsMenu(Core::IsRunning(Core::System::GetInstance()));
}
void MainWindow::OnPlayRecording()
@ -1987,8 +1983,9 @@ void MainWindow::OnPlayRecording()
void MainWindow::OnStartRecording()
{
auto& movie = Core::System::GetInstance().GetMovie();
if ((!Core::IsRunningAndStarted() && Core::IsRunning()) || movie.IsRecordingInput() ||
auto& system = Core::System::GetInstance();
auto& movie = system.GetMovie();
if ((!Core::IsRunningAndStarted() && Core::IsRunning(system)) || movie.IsRecordingInput() ||
movie.IsPlayingInput())
{
return;
@ -2020,7 +2017,7 @@ void MainWindow::OnStartRecording()
{
emit RecordingStatusChanged(true);
if (!Core::IsRunning())
if (!Core::IsRunning(system))
Play();
}
}
@ -2037,12 +2034,13 @@ void MainWindow::OnStopRecording()
void MainWindow::OnExportRecording()
{
Core::RunAsCPUThread([this] {
QString dtm_file = DolphinFileDialog::getSaveFileName(
this, tr("Save Recording File As"), QString(), tr("Dolphin TAS Movies (*.dtm)"));
if (!dtm_file.isEmpty())
Core::System::GetInstance().GetMovie().SaveRecording(dtm_file.toStdString());
});
auto& system = Core::System::GetInstance();
const Core::CPUThreadGuard guard(system);
QString dtm_file = DolphinFileDialog::getSaveFileName(
this, tr("Save Recording File As"), QString(), tr("Dolphin TAS Movies (*.dtm)"));
if (!dtm_file.isEmpty())
system.GetMovie().SaveRecording(dtm_file.toStdString());
}
void MainWindow::OnActivateChat()
@ -2080,10 +2078,11 @@ void MainWindow::ShowTASInput()
}
}
auto& system = Core::System::GetInstance();
for (int i = 0; i < num_wii_controllers; i++)
{
if (Config::Get(Config::GetInfoForWiimoteSource(i)) == WiimoteSource::Emulated &&
(!Core::IsRunning() || Core::System::GetInstance().IsWii()))
(!Core::IsRunning(system) || system.IsWii()))
{
SetQWidgetWindowDecorations(m_wii_tas_input_windows[i]);
m_wii_tas_input_windows[i]->show();
@ -2095,13 +2094,12 @@ void MainWindow::ShowTASInput()
void MainWindow::OnConnectWiiRemote(int id)
{
Core::RunAsCPUThread([&] {
if (const auto bt = WiiUtils::GetBluetoothEmuDevice())
{
const auto wm = bt->AccessWiimoteByIndex(id);
wm->Activate(!wm->IsConnected());
}
});
const Core::CPUThreadGuard guard(Core::System::GetInstance());
if (const auto bt = WiiUtils::GetBluetoothEmuDevice())
{
const auto wm = bt->AccessWiimoteByIndex(id);
wm->Activate(!wm->IsConnected());
}
}
#ifdef USE_RETRO_ACHIEVEMENTS
@ -2116,6 +2114,7 @@ void MainWindow::ShowAchievementsWindow()
m_achievements_window->show();
m_achievements_window->raise();
m_achievements_window->activateWindow();
m_achievements_window->UpdateData(AchievementManager::UpdatedItems{.all = true});
}
void MainWindow::ShowAchievementSettings()

View file

@ -15,9 +15,12 @@
#include <QMap>
#include <QUrl>
#include <fmt/format.h>
#include "Common/Align.h"
#include "Common/CommonPaths.h"
#include "Common/FileUtil.h"
#include "Common/IOFile.h"
#include "Common/StringUtil.h"
#include "Core/AchievementManager.h"
@ -94,9 +97,9 @@ MenuBar::MenuBar(QWidget* parent) : QMenuBar(parent)
connect(&Settings::Instance(), &Settings::EmulationStateChanged, this,
[=, this](Core::State state) { OnEmulationStateChanged(state); });
connect(Host::GetInstance(), &Host::UpdateDisasmDialog, this,
[this] { OnEmulationStateChanged(Core::GetState()); });
[this] { OnEmulationStateChanged(Core::GetState(Core::System::GetInstance())); });
OnEmulationStateChanged(Core::GetState());
OnEmulationStateChanged(Core::GetState(Core::System::GetInstance()));
connect(&Settings::Instance(), &Settings::DebugModeToggled, this, &MenuBar::OnDebugModeToggled);
connect(this, &MenuBar::SelectionChanged, this, &MenuBar::OnSelectionChanged);
@ -151,6 +154,7 @@ void MenuBar::OnEmulationStateChanged(Core::State state)
!Core::System::GetInstance().GetMovie().IsPlayingInput());
// JIT
const bool jit_exists = Core::System::GetInstance().GetJitInterface().GetCore() != nullptr;
m_jit_interpreter_core->setEnabled(running);
m_jit_block_linking->setEnabled(!running);
m_jit_disable_cache->setEnabled(!running);
@ -159,6 +163,7 @@ void MenuBar::OnEmulationStateChanged(Core::State state)
m_jit_clear_cache->setEnabled(running);
m_jit_log_coverage->setEnabled(!running);
m_jit_search_instruction->setEnabled(running);
m_jit_write_cache_log_dump->setEnabled(running && jit_exists);
// Symbols
m_symbols->setEnabled(running);
@ -199,6 +204,30 @@ void MenuBar::OnDebugModeToggled(bool enabled)
}
}
void MenuBar::OnWriteJitBlockLogDump()
{
const std::string filename = fmt::format("{}{}.txt", File::GetUserPath(D_DUMPDEBUG_JITBLOCKS_IDX),
SConfig::GetInstance().GetGameID());
File::IOFile f(filename, "w");
if (!f)
{
ModalMessageBox::warning(
this, tr("Error"),
tr("Failed to open \"%1\" for writing.").arg(QString::fromStdString(filename)));
return;
}
auto& system = Core::System::GetInstance();
system.GetJitInterface().JitBlockLogDump(Core::CPUThreadGuard{system}, f.GetHandle());
if (static bool ignore = false; ignore == false)
{
const int button_pressed = ModalMessageBox::information(
this, tr("Success"), tr("Wrote to \"%1\".").arg(QString::fromStdString(filename)),
QMessageBox::Ok | QMessageBox::Ignore);
if (button_pressed == QMessageBox::Ignore)
ignore = true;
}
}
void MenuBar::AddFileMenu()
{
QMenu* file_menu = addMenu(tr("&File"));
@ -871,6 +900,17 @@ void MenuBar::AddJITMenu()
m_jit->addSeparator();
m_jit_profile_blocks = m_jit->addAction(tr("Enable JIT Block Profiling"));
m_jit_profile_blocks->setCheckable(true);
m_jit_profile_blocks->setChecked(Config::Get(Config::MAIN_DEBUG_JIT_ENABLE_PROFILING));
connect(m_jit_profile_blocks, &QAction::toggled, [](bool enabled) {
Config::SetBaseOrCurrent(Config::MAIN_DEBUG_JIT_ENABLE_PROFILING, enabled);
});
m_jit_write_cache_log_dump =
m_jit->addAction(tr("Write JIT Block Log Dump"), this, &MenuBar::OnWriteJitBlockLogDump);
m_jit->addSeparator();
m_jit_off = m_jit->addAction(tr("JIT Off (JIT Core)"));
m_jit_off->setCheckable(true);
m_jit_off->setChecked(Config::Get(Config::MAIN_DEBUG_JIT_OFF));
@ -1204,14 +1244,17 @@ void MenuBar::OnSelectionChanged(std::shared_ptr<const UICommon::GameFile> game_
{
m_game_selected = !!game_file;
m_recording_play->setEnabled(m_game_selected && !Core::IsRunning());
m_recording_start->setEnabled((m_game_selected || Core::IsRunning()) &&
!Core::System::GetInstance().GetMovie().IsPlayingInput());
auto& system = Core::System::GetInstance();
const bool core_is_running = Core::IsRunning(system);
m_recording_play->setEnabled(m_game_selected && !core_is_running);
m_recording_start->setEnabled((m_game_selected || core_is_running) &&
!system.GetMovie().IsPlayingInput());
}
void MenuBar::OnRecordingStatusChanged(bool recording)
{
m_recording_start->setEnabled(!recording && (m_game_selected || Core::IsRunning()));
auto& system = Core::System::GetInstance();
m_recording_start->setEnabled(!recording && (m_game_selected || Core::IsRunning(system)));
m_recording_stop->setEnabled(recording);
m_recording_export->setEnabled(recording);
}
@ -1240,35 +1283,37 @@ void MenuBar::ClearSymbols()
if (result == QMessageBox::Cancel)
return;
g_symbolDB.Clear();
emit NotifySymbolsUpdated();
Core::System::GetInstance().GetPPCSymbolDB().Clear();
emit Host::GetInstance()->PPCSymbolsChanged();
}
void MenuBar::GenerateSymbolsFromAddress()
{
Core::CPUThreadGuard guard(Core::System::GetInstance());
auto& system = Core::System::GetInstance();
auto& memory = system.GetMemory();
auto& ppc_symbol_db = system.GetPPCSymbolDB();
const Core::CPUThreadGuard guard(system);
PPCAnalyst::FindFunctions(guard, Memory::MEM1_BASE_ADDR,
Memory::MEM1_BASE_ADDR + memory.GetRamSizeReal(), &g_symbolDB);
emit NotifySymbolsUpdated();
Memory::MEM1_BASE_ADDR + memory.GetRamSizeReal(), &ppc_symbol_db);
emit Host::GetInstance()->PPCSymbolsChanged();
}
void MenuBar::GenerateSymbolsFromSignatureDB()
{
Core::CPUThreadGuard guard(Core::System::GetInstance());
auto& system = Core::System::GetInstance();
auto& memory = system.GetMemory();
auto& ppc_symbol_db = system.GetPPCSymbolDB();
const Core::CPUThreadGuard guard(system);
PPCAnalyst::FindFunctions(guard, Memory::MEM1_BASE_ADDR,
Memory::MEM1_BASE_ADDR + memory.GetRamSizeReal(), &g_symbolDB);
Memory::MEM1_BASE_ADDR + memory.GetRamSizeReal(), &ppc_symbol_db);
SignatureDB db(SignatureDB::HandlerType::DSY);
if (db.Load(File::GetSysDirectory() + TOTALDB))
{
db.Apply(guard, &g_symbolDB);
db.Apply(guard, &ppc_symbol_db);
ModalMessageBox::information(
this, tr("Information"),
tr("Generated symbol names from '%1'").arg(QString::fromStdString(TOTALDB)));
@ -1281,7 +1326,7 @@ void MenuBar::GenerateSymbolsFromSignatureDB()
tr("'%1' not found, no symbol names generated").arg(QString::fromStdString(TOTALDB)));
}
emit NotifySymbolsUpdated();
emit Host::GetInstance()->PPCSymbolsChanged();
}
void MenuBar::GenerateSymbolsFromRSO()
@ -1304,13 +1349,14 @@ void MenuBar::GenerateSymbolsFromRSO()
return;
}
Core::CPUThreadGuard guard(Core::System::GetInstance());
auto& system = Core::System::GetInstance();
const Core::CPUThreadGuard guard(system);
RSOChainView rso_chain;
if (rso_chain.Load(guard, static_cast<u32>(address)))
{
rso_chain.Apply(guard, &g_symbolDB);
emit NotifySymbolsUpdated();
rso_chain.Apply(guard, &system.GetPPCSymbolDB());
emit Host::GetInstance()->PPCSymbolsChanged();
}
else
{
@ -1361,12 +1407,13 @@ void MenuBar::GenerateSymbolsFromRSOAuto()
RSOChainView rso_chain;
const u32 address = item.mid(0, item.indexOf(QLatin1Char(' '))).toUInt(nullptr, 16);
Core::CPUThreadGuard guard(Core::System::GetInstance());
auto& system = Core::System::GetInstance();
const Core::CPUThreadGuard guard(system);
if (rso_chain.Load(guard, address))
{
rso_chain.Apply(guard, &g_symbolDB);
emit NotifySymbolsUpdated();
rso_chain.Apply(guard, &system.GetPPCSymbolDB());
emit Host::GetInstance()->PPCSymbolsChanged();
}
else
{
@ -1481,22 +1528,23 @@ void MenuBar::LoadSymbolMap()
{
auto& system = Core::System::GetInstance();
auto& memory = system.GetMemory();
auto& ppc_symbol_db = system.GetPPCSymbolDB();
std::string existing_map_file, writable_map_file;
bool map_exists = CBoot::FindMapFile(&existing_map_file, &writable_map_file);
if (!map_exists)
{
g_symbolDB.Clear();
ppc_symbol_db.Clear();
{
Core::CPUThreadGuard guard(Core::System::GetInstance());
const Core::CPUThreadGuard guard(system);
PPCAnalyst::FindFunctions(guard, Memory::MEM1_BASE_ADDR + 0x1300000,
Memory::MEM1_BASE_ADDR + memory.GetRamSizeReal(), &g_symbolDB);
Memory::MEM1_BASE_ADDR + memory.GetRamSizeReal(), &ppc_symbol_db);
SignatureDB db(SignatureDB::HandlerType::DSY);
if (db.Load(File::GetSysDirectory() + TOTALDB))
db.Apply(guard, &g_symbolDB);
db.Apply(guard, &ppc_symbol_db);
}
ModalMessageBox::warning(this, tr("Warning"),
@ -1515,7 +1563,7 @@ void MenuBar::LoadSymbolMap()
}
HLE::PatchFunctions(system);
emit NotifySymbolsUpdated();
emit Host::GetInstance()->PPCSymbolsChanged();
}
void MenuBar::SaveSymbolMap()
@ -1540,7 +1588,7 @@ void MenuBar::LoadOtherSymbolMap()
auto& system = Core::System::GetInstance();
HLE::PatchFunctions(system);
emit NotifySymbolsUpdated();
emit Host::GetInstance()->PPCSymbolsChanged();
}
void MenuBar::LoadBadSymbolMap()
@ -1557,7 +1605,7 @@ void MenuBar::LoadBadSymbolMap()
auto& system = Core::System::GetInstance();
HLE::PatchFunctions(system);
emit NotifySymbolsUpdated();
emit Host::GetInstance()->PPCSymbolsChanged();
}
void MenuBar::SaveSymbolMapAs()
@ -1582,13 +1630,8 @@ void MenuBar::SaveCode()
const std::string path =
writable_map_file.substr(0, writable_map_file.find_last_of('.')) + "_code.map";
bool success;
{
Core::CPUThreadGuard guard(Core::System::GetInstance());
success = g_symbolDB.SaveCodeMap(guard, path);
}
if (!success)
auto& system = Core::System::GetInstance();
if (!system.GetPPCSymbolDB().SaveCodeMap(Core::CPUThreadGuard{system}, path))
{
ModalMessageBox::warning(
this, tr("Error"),
@ -1598,9 +1641,10 @@ void MenuBar::SaveCode()
bool MenuBar::TryLoadMapFile(const QString& path, const bool bad)
{
Core::CPUThreadGuard guard(Core::System::GetInstance());
auto& system = Core::System::GetInstance();
auto& ppc_symbol_db = system.GetPPCSymbolDB();
if (!g_symbolDB.LoadMap(guard, path.toStdString(), bad))
if (!ppc_symbol_db.LoadMap(Core::CPUThreadGuard{system}, path.toStdString(), bad))
{
ModalMessageBox::warning(this, tr("Error"), tr("Failed to load map file '%1'").arg(path));
return false;
@ -1611,7 +1655,7 @@ bool MenuBar::TryLoadMapFile(const QString& path, const bool bad)
void MenuBar::TrySaveSymbolMap(const QString& path)
{
if (g_symbolDB.SaveSymbolMap(path.toStdString()))
if (Core::System::GetInstance().GetPPCSymbolDB().SaveSymbolMap(path.toStdString()))
return;
ModalMessageBox::warning(this, tr("Error"),
@ -1632,7 +1676,7 @@ void MenuBar::CreateSignatureFile()
const std::string prefix = text.toStdString();
const std::string save_path = file.toStdString();
SignatureDB db(save_path);
db.Populate(&g_symbolDB, prefix);
db.Populate(&Core::System::GetInstance().GetPPCSymbolDB(), prefix);
if (!db.Save(save_path))
{
@ -1657,7 +1701,7 @@ void MenuBar::AppendSignatureFile()
const std::string prefix = text.toStdString();
const std::string signature_path = file.toStdString();
SignatureDB db(signature_path);
db.Populate(&g_symbolDB, prefix);
db.Populate(&Core::System::GetInstance().GetPPCSymbolDB(), prefix);
db.List();
db.Load(signature_path);
if (!db.Save(signature_path))
@ -1678,17 +1722,15 @@ void MenuBar::ApplySignatureFile()
if (file.isEmpty())
return;
auto& system = Core::System::GetInstance();
const std::string load_path = file.toStdString();
SignatureDB db(load_path);
db.Load(load_path);
{
Core::CPUThreadGuard guard(Core::System::GetInstance());
db.Apply(guard, &g_symbolDB);
}
db.Apply(Core::CPUThreadGuard{system}, &system.GetPPCSymbolDB());
db.List();
auto& system = Core::System::GetInstance();
HLE::PatchFunctions(system);
emit NotifySymbolsUpdated();
emit Host::GetInstance()->PPCSymbolsChanged();
}
void MenuBar::CombineSignatureFiles()
@ -1732,7 +1774,8 @@ void MenuBar::PatchHLEFunctions()
void MenuBar::ClearCache()
{
Core::RunAsCPUThread([] { Core::System::GetInstance().GetJitInterface().ClearCache(); });
auto& system = Core::System::GetInstance();
system.GetJitInterface().ClearCache(Core::CPUThreadGuard{system});
}
void MenuBar::LogInstructions()
@ -1753,20 +1796,19 @@ void MenuBar::SearchInstruction()
auto& system = Core::System::GetInstance();
auto& memory = system.GetMemory();
Core::CPUThreadGuard guard(system);
const std::string op_std = op.toStdString();
const Core::CPUThreadGuard guard(system);
bool found = false;
for (u32 addr = Memory::MEM1_BASE_ADDR; addr < Memory::MEM1_BASE_ADDR + memory.GetRamSizeReal();
addr += 4)
{
const auto ins_name = QString::fromStdString(
PPCTables::GetInstructionName(PowerPC::MMU::HostRead_U32(guard, addr), addr));
if (op == ins_name)
if (op_std == PPCTables::GetInstructionName(PowerPC::MMU::HostRead_U32(guard, addr), addr))
{
NOTICE_LOG_FMT(POWERPC, "Found {} at {:08x}", op.toStdString(), addr);
NOTICE_LOG_FMT(POWERPC, "Found {} at {:08x}", op_std, addr);
found = true;
}
}
if (!found)
NOTICE_LOG_FMT(POWERPC, "Opcode {} not found", op.toStdString());
NOTICE_LOG_FMT(POWERPC, "Opcode {} not found", op_std);
}

View file

@ -125,9 +125,6 @@ signals:
void RecordingStatusChanged(bool recording);
void ReadOnlyModeChanged(bool read_only);
// Synbols
void NotifySymbolsUpdated();
private:
void OnEmulationStateChanged(Core::State state);
@ -188,6 +185,7 @@ private:
void OnRecordingStatusChanged(bool recording);
void OnReadOnlyModeChanged(bool read_only);
void OnDebugModeToggled(bool enabled);
void OnWriteJitBlockLogDump();
QString GetSignatureSelector() const;
@ -271,6 +269,8 @@ private:
QAction* m_jit_clear_cache;
QAction* m_jit_log_coverage;
QAction* m_jit_search_instruction;
QAction* m_jit_profile_blocks;
QAction* m_jit_write_cache_log_dump;
QAction* m_jit_off;
QAction* m_jit_loadstore_off;
QAction* m_jit_loadstore_lbzx_off;

View file

@ -235,19 +235,19 @@ void NetPlayBrowser::accept()
if (m_sessions[index].has_password)
{
auto* dialog = new QInputDialog(this);
QInputDialog dialog(this);
dialog->setWindowFlags(dialog->windowFlags() & ~Qt::WindowContextHelpButtonHint);
dialog->setWindowTitle(tr("Enter password"));
dialog->setLabelText(tr("This session requires a password:"));
dialog->setWindowModality(Qt::WindowModal);
dialog->setTextEchoMode(QLineEdit::Password);
dialog.setWindowFlags(dialog.windowFlags() & ~Qt::WindowContextHelpButtonHint);
dialog.setWindowTitle(tr("Enter password"));
dialog.setLabelText(tr("This session requires a password:"));
dialog.setWindowModality(Qt::WindowModal);
dialog.setTextEchoMode(QLineEdit::Password);
SetQWidgetWindowDecorations(dialog);
if (dialog->exec() != QDialog::Accepted)
SetQWidgetWindowDecorations(&dialog);
if (dialog.exec() != QDialog::Accepted)
return;
const std::string password = dialog->textValue().toStdString();
const std::string password = dialog.textValue().toStdString();
auto decrypted_id = session.DecryptID(password);

View file

@ -43,6 +43,7 @@
#include "Core/IOS/FS/FileSystem.h"
#include "Core/NetPlayServer.h"
#include "Core/SyncIdentifier.h"
#include "Core/System.h"
#include "DolphinQt/NetPlay/ChunkedProgressDialog.h"
#include "DolphinQt/NetPlay/GameDigestDialog.h"
@ -584,7 +585,7 @@ void NetPlayDialog::UpdateDiscordPresence()
m_current_game_name);
};
if (Core::IsRunning())
if (Core::IsRunning(Core::System::GetInstance()))
return use_default();
if (IsHosting())
@ -808,7 +809,7 @@ void NetPlayDialog::DisplayMessage(const QString& msg, const std::string& color,
QColor c(color.empty() ? QStringLiteral("white") : QString::fromStdString(color));
if (g_ActiveConfig.bShowNetPlayMessages && Core::IsRunning())
if (g_ActiveConfig.bShowNetPlayMessages && Core::IsRunning(Core::System::GetInstance()))
g_netplay_chat_ui->AppendChat(msg.toStdString(),
{static_cast<float>(c.redF()), static_cast<float>(c.greenF()),
static_cast<float>(c.blueF())});
@ -908,7 +909,7 @@ void NetPlayDialog::OnMsgStopGame()
void NetPlayDialog::OnMsgPowerButton()
{
if (!Core::IsRunning())
if (!Core::IsRunning(Core::System::GetInstance()))
return;
QueueOnObject(this, [] { UICommon::TriggerSTMPowerEvent(); });
}
@ -972,9 +973,13 @@ void NetPlayDialog::OnHostInputAuthorityChanged(bool enabled)
void NetPlayDialog::OnDesync(u32 frame, const std::string& player)
{
DisplayMessage(tr("Possible desync detected: %1 might have desynced at frame %2")
DisplayMessage(tr("Possible desync detected: %1 might have desynced at frame %2. Game restart advised.")
.arg(QString::fromStdString(player), QString::number(frame)),
"red", OSD::Duration::VERY_LONG);
OSD::AddTypedMessage(OSD::MessageType::NetPlayDesync,
"Possible desync detected. Game restart advised.",
OSD::Duration::VERY_LONG, OSD::Color::RED);
}
void NetPlayDialog::OnConnectionLost()
@ -1283,3 +1288,13 @@ void NetPlayDialog::SetHostWiiSyncData(std::vector<u64> titles, std::string redi
if (client)
client->SetWiiSyncData(nullptr, std::move(titles), std::move(redirect_folder));
}
void NetPlayDialog::OnActiveGeckoCodes(std::string codeStr)
{
DisplayMessage(QString::fromStdString(codeStr), "cornflowerblue");
}
void NetPlayDialog::OnActiveARCodes(std::string codeStr)
{
DisplayMessage(QString::fromStdString(codeStr), "cornflowerblue");
}

View file

@ -118,6 +118,9 @@ private:
void SendMessage(const std::string& message);
void OnActiveGeckoCodes(std::string codeStr);
void OnActiveARCodes(std::string codeStr);
// Chat
QGroupBox* m_chat_box;
QTextEdit* m_chat_edit;

View file

@ -0,0 +1,23 @@
// Copyright 2024 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <QString>
#include <string_view>
namespace QtUtils
{
inline QString FromStdString(std::string_view s)
{
return QString::fromUtf8(s.data(), s.size());
}
inline QString FromStdString(std::u8string_view s)
{
return QString::fromUtf8(s.data(), s.size());
}
inline QString FromStdString(std::u16string_view s)
{
return QString::fromUtf16(s.data(), s.size());
}
} // namespace QtUtils

View file

@ -22,6 +22,7 @@
#include "Core/Config/MainSettings.h"
#include "Core/Core.h"
#include "Core/State.h"
#include "Core/System.h"
#include "DolphinQt/Host.h"
#include "DolphinQt/QtUtils/ModalMessageBox.h"
@ -36,6 +37,7 @@
#ifdef _WIN32
#include <Windows.h>
#include <dwmapi.h>
#endif
RenderWidget::RenderWidget(QWidget* parent) : QWidget(parent)
@ -68,7 +70,7 @@ RenderWidget::RenderWidget(QWidget* parent) : QWidget(parent)
// (which results in them not getting called)
connect(this, &RenderWidget::StateChanged, Host::GetInstance(), &Host::SetRenderFullscreen,
Qt::DirectConnection);
connect(this, &RenderWidget::HandleChanged, Host::GetInstance(), &Host::SetRenderHandle,
connect(this, &RenderWidget::HandleChanged, this, &RenderWidget::OnHandleChanged,
Qt::DirectConnection);
connect(this, &RenderWidget::SizeChanged, Host::GetInstance(), &Host::ResizeSurface,
Qt::DirectConnection);
@ -129,7 +131,21 @@ void RenderWidget::dropEvent(QDropEvent* event)
return;
}
State::LoadAs(path.toStdString());
State::LoadAs(Core::System::GetInstance(), path.toStdString());
}
void RenderWidget::OnHandleChanged(void* handle)
{
if (handle)
{
#ifdef _WIN32
// Remove rounded corners from the render window on Windows 11
const DWM_WINDOW_CORNER_PREFERENCE corner_preference = DWMWCP_DONOTROUND;
DwmSetWindowAttribute(reinterpret_cast<HWND>(handle), DWMWA_WINDOW_CORNER_PREFERENCE,
&corner_preference, sizeof(corner_preference));
#endif
}
Host::GetInstance()->SetRenderHandle(handle);
}
void RenderWidget::OnHideCursorChanged()
@ -405,8 +421,11 @@ bool RenderWidget::event(QEvent* event)
// Note that this event in Windows is not always aligned to the window that is highlighted,
// it's the window that has keyboard and mouse focus
case QEvent::WindowActivate:
if (m_should_unpause_on_focus && Core::GetState() == Core::State::Paused)
Core::SetState(Core::State::Running);
if (m_should_unpause_on_focus &&
Core::GetState(Core::System::GetInstance()) == Core::State::Paused)
{
Core::SetState(Core::System::GetInstance(), Core::State::Running);
}
m_should_unpause_on_focus = false;
@ -429,7 +448,8 @@ bool RenderWidget::event(QEvent* event)
UpdateCursor();
if (Config::Get(Config::MAIN_PAUSE_ON_FOCUS_LOST) && Core::GetState() == Core::State::Running)
if (Config::Get(Config::MAIN_PAUSE_ON_FOCUS_LOST) &&
Core::GetState(Core::System::GetInstance()) == Core::State::Running)
{
// If we are declared as the CPU or GPU thread, it means that the real CPU or GPU thread
// is waiting for us to finish showing a panic alert (with that panic alert likely being
@ -437,7 +457,7 @@ bool RenderWidget::event(QEvent* event)
if (!Core::IsCPUThread() && !Core::IsGPUThread())
{
m_should_unpause_on_focus = true;
Core::SetState(Core::State::Paused);
Core::SetState(Core::System::GetInstance(), Core::State::Paused);
}
}

View file

@ -34,6 +34,7 @@ signals:
private:
void HandleCursorTimer();
void OnHandleChanged(void* handle);
void OnHideCursorChanged();
void OnNeverHideCursorChanged();
void OnLockCursorChanged();

View file

@ -33,7 +33,7 @@
#include "Common/FileUtil.h"
#include "Common/StringUtil.h"
#include "Core/Config/AchievementSettings.h"
#include "Core/AchievementManager.h"
#include "Core/Config/GraphicsSettings.h"
#include "Core/Config/MainSettings.h"
#include "Core/ConfigManager.h"
@ -565,7 +565,7 @@ void Settings::SetCheatsEnabled(bool enabled)
void Settings::SetDebugModeEnabled(bool enabled)
{
#ifdef USE_RETRO_ACHIEVEMENTS
if (Config::Get(Config::RA_HARDCORE_ENABLED))
if (AchievementManager::GetInstance().IsHardcoreModeActive())
enabled = false;
#endif // USE_RETRO_ACHIEVEMENTS
if (IsDebugModeEnabled() != enabled)

View file

@ -217,7 +217,7 @@ signals:
void JITVisibilityChanged(bool visible);
void AssemblerVisibilityChanged(bool visible);
void DebugModeToggled(bool enabled);
void DebugFontChanged(QFont font);
void DebugFontChanged(const QFont& font);
void AutoUpdateTrackChanged(const QString& mode);
void FallbackRegionChanged(const DiscIO::Region& region);
void AnalyticsToggled(bool enabled);

View file

@ -240,7 +240,7 @@ void AdvancedPane::ConnectLayout()
void AdvancedPane::Update()
{
const bool running = Core::GetState() != Core::State::Uninitialized;
const bool running = Core::GetState(Core::System::GetInstance()) != Core::State::Uninitialized;
const bool enable_cpu_clock_override_widgets = Config::Get(Config::MAIN_OVERCLOCK_ENABLE);
const bool enable_ram_override_widgets = Config::Get(Config::MAIN_RAM_OVERRIDE_ENABLE);
const bool enable_custom_rtc_widgets = Config::Get(Config::MAIN_CUSTOM_RTC_ENABLE) && !running;

View file

@ -40,7 +40,8 @@ AudioPane::AudioPane()
OnEmulationStateChanged(state != Core::State::Uninitialized);
});
OnEmulationStateChanged(Core::GetState() != Core::State::Uninitialized);
OnEmulationStateChanged(Core::GetState(Core::System::GetInstance()) !=
Core::State::Uninitialized);
}
void AudioPane::CreateWidgets()

View file

@ -48,6 +48,32 @@ void BroadbandAdapterSettingsDialog::InitControls()
window_title = tr("Broadband Adapter MAC Address");
break;
case Type::TapServer:
case Type::ModemTapServer:
{
const bool is_modem = (m_bba_type == Type::ModemTapServer);
current_address =
QString::fromStdString(Config::Get(is_modem ? Config::MAIN_MODEM_TAPSERVER_DESTINATION :
Config::MAIN_BBA_TAPSERVER_DESTINATION));
#ifdef _WIN32
address_label = new QLabel(tr("Destination (address:port):"));
address_placeholder = QStringLiteral("");
description = new QLabel(
tr("Enter the IP address and port of the tapserver instance you want to connect to."));
#else
address_label = new QLabel(tr("Destination (UNIX socket path or address:port):"));
address_placeholder =
is_modem ? QStringLiteral(u"/tmp/dolphin-modem-tap") : QStringLiteral(u"/tmp/dolphin-tap");
description =
new QLabel(tr("The default value \"%1\" will work with a local tapserver and newserv."
" You can also enter a network location (address:port) to connect to a "
"remote tapserver.")
.arg(address_placeholder));
#endif
window_title = tr("BBA destination address");
break;
}
case Type::BuiltIn:
address_label = new QLabel(tr("Enter the DNS server to use:"));
address_placeholder = QStringLiteral("8.8.8.8");
@ -114,6 +140,12 @@ void BroadbandAdapterSettingsDialog::SaveAddress()
Config::SetBaseOrCurrent(Config::MAIN_BBA_MAC, bba_new_address);
break;
}
case Type::TapServer:
Config::SetBaseOrCurrent(Config::MAIN_BBA_TAPSERVER_DESTINATION, bba_new_address);
break;
case Type::ModemTapServer:
Config::SetBaseOrCurrent(Config::MAIN_MODEM_TAPSERVER_DESTINATION, bba_new_address);
break;
case Type::BuiltIn:
Config::SetBaseOrCurrent(Config::MAIN_BBA_BUILTIN_DNS, bba_new_address);
break;

View file

@ -15,7 +15,9 @@ public:
{
Ethernet,
XLinkKai,
BuiltIn
TapServer,
BuiltIn,
ModemTapServer
};
explicit BroadbandAdapterSettingsDialog(QWidget* target, Type bba_type);

View file

@ -149,10 +149,9 @@ void GameCubePane::CreateWidgets()
EXIDeviceType::Dummy,
EXIDeviceType::Ethernet,
EXIDeviceType::EthernetXLink,
#ifdef __APPLE__
EXIDeviceType::EthernetTapServer,
#endif
EXIDeviceType::EthernetBuiltIn,
EXIDeviceType::ModemTapServer,
})
{
m_slot_combos[ExpansionInterface::Slot::SP1]->addItem(tr(fmt::format("{:n}", device).c_str()),
@ -355,7 +354,9 @@ void GameCubePane::UpdateButton(ExpansionInterface::Slot slot)
case ExpansionInterface::Slot::SP1:
has_config = (device == ExpansionInterface::EXIDeviceType::Ethernet ||
device == ExpansionInterface::EXIDeviceType::EthernetXLink ||
device == ExpansionInterface::EXIDeviceType::EthernetBuiltIn);
device == ExpansionInterface::EXIDeviceType::EthernetTapServer ||
device == ExpansionInterface::EXIDeviceType::EthernetBuiltIn ||
device == ExpansionInterface::EXIDeviceType::ModemTapServer);
break;
}
@ -400,6 +401,21 @@ void GameCubePane::OnConfigPressed(ExpansionInterface::Slot slot)
dialog.exec();
return;
}
case ExpansionInterface::EXIDeviceType::EthernetTapServer:
{
BroadbandAdapterSettingsDialog dialog(this, BroadbandAdapterSettingsDialog::Type::TapServer);
SetQWidgetWindowDecorations(&dialog);
dialog.exec();
return;
}
case ExpansionInterface::EXIDeviceType::ModemTapServer:
{
BroadbandAdapterSettingsDialog dialog(this,
BroadbandAdapterSettingsDialog::Type::ModemTapServer);
SetQWidgetWindowDecorations(&dialog);
dialog.exec();
return;
}
case ExpansionInterface::EXIDeviceType::EthernetBuiltIn:
{
BroadbandAdapterSettingsDialog dialog(this, BroadbandAdapterSettingsDialog::Type::BuiltIn);
@ -498,7 +514,8 @@ bool GameCubePane::SetMemcard(ExpansionInterface::Slot slot, const QString& file
const std::string old_eu_path = Config::GetMemcardPath(slot, DiscIO::Region::PAL);
Config::SetBase(Config::GetInfoForMemcardPath(slot), raw_path);
if (Core::IsRunning())
auto& system = Core::System::GetInstance();
if (Core::IsRunning(system))
{
// If emulation is running and the new card is different from the old one, notify the system to
// eject the old and insert the new card.
@ -507,8 +524,8 @@ bool GameCubePane::SetMemcard(ExpansionInterface::Slot slot, const QString& file
{
// ChangeDevice unplugs the device for 1 second, which means that games should notice that
// the path has changed and thus the memory card contents have changed
Core::System::GetInstance().GetExpansionInterface().ChangeDevice(
slot, ExpansionInterface::EXIDeviceType::MemoryCard);
system.GetExpansionInterface().ChangeDevice(slot,
ExpansionInterface::EXIDeviceType::MemoryCard);
}
}
@ -604,7 +621,8 @@ bool GameCubePane::SetGCIFolder(ExpansionInterface::Slot slot, const QString& pa
Config::SetBase(Config::GetInfoForGCIPath(slot), raw_path);
if (Core::IsRunning())
auto& system = Core::System::GetInstance();
if (Core::IsRunning(system))
{
// If emulation is running and the new card is different from the old one, notify the system to
// eject the old and insert the new card.
@ -613,7 +631,7 @@ bool GameCubePane::SetGCIFolder(ExpansionInterface::Slot slot, const QString& pa
{
// ChangeDevice unplugs the device for 1 second, which means that games should notice that
// the path has changed and thus the memory card contents have changed
Core::System::GetInstance().GetExpansionInterface().ChangeDevice(
system.GetExpansionInterface().ChangeDevice(
slot, ExpansionInterface::EXIDeviceType::MemoryCardFolder);
}
}
@ -644,14 +662,14 @@ void GameCubePane::SetAGPRom(ExpansionInterface::Slot slot, const QString& filen
Config::SetBase(Config::GetInfoForAGPCartPath(slot), path_abs.toStdString());
if (Core::IsRunning() && path_abs != path_old)
auto& system = Core::System::GetInstance();
if (Core::IsRunning(system) && path_abs != path_old)
{
// ChangeDevice unplugs the device for 1 second. For an actual AGP, you can remove the
// cartridge without unplugging it, and it's not clear if the AGP software actually notices
// that it's been unplugged or the cartridge has changed, but this was done for memcards so
// we might as well do it for the AGP too.
Core::System::GetInstance().GetExpansionInterface().ChangeDevice(
slot, ExpansionInterface::EXIDeviceType::AGP);
system.GetExpansionInterface().ChangeDevice(slot, ExpansionInterface::EXIDeviceType::AGP);
}
LoadSettings();
@ -765,6 +783,7 @@ void GameCubePane::SaveSettings()
Config::SetBaseOrCurrent(Config::MAIN_SKIP_IPL, m_skip_main_menu->isChecked());
Config::SetBaseOrCurrent(Config::MAIN_GC_LANGUAGE, m_language_combo->currentData().toInt());
auto& system = Core::System::GetInstance();
// Device Settings
for (ExpansionInterface::Slot slot : ExpansionInterface::SLOTS)
{
@ -773,9 +792,9 @@ void GameCubePane::SaveSettings()
const ExpansionInterface::EXIDeviceType current_exi_device =
Config::Get(Config::GetInfoForEXIDevice(slot));
if (Core::IsRunning() && current_exi_device != dev)
if (Core::IsRunning(system) && current_exi_device != dev)
{
Core::System::GetInstance().GetExpansionInterface().ChangeDevice(slot, dev);
system.GetExpansionInterface().ChangeDevice(slot, dev);
}
Config::SetBaseOrCurrent(Config::GetInfoForEXIDevice(slot), dev);

View file

@ -15,13 +15,14 @@
#include <QVBoxLayout>
#include <QWidget>
#include "Core/Config/AchievementSettings.h"
#include "Core/AchievementManager.h"
#include "Core/Config/MainSettings.h"
#include "Core/Config/UISettings.h"
#include "Core/ConfigManager.h"
#include "Core/Core.h"
#include "Core/DolphinAnalytics.h"
#include "Core/PowerPC/PowerPC.h"
#include "Core/System.h"
#include "DolphinQt/QtUtils/ModalMessageBox.h"
#include "DolphinQt/QtUtils/NonDefaultQPushButton.h"
@ -58,7 +59,7 @@ GeneralPane::GeneralPane(QWidget* parent) : QWidget(parent)
&GeneralPane::OnEmulationStateChanged);
connect(&Settings::Instance(), &Settings::ConfigChanged, this, &GeneralPane::LoadConfig);
OnEmulationStateChanged(Core::GetState());
OnEmulationStateChanged(Core::GetState(Core::System::GetInstance()));
}
void GeneralPane::CreateLayout()
@ -83,7 +84,7 @@ void GeneralPane::OnEmulationStateChanged(Core::State state)
m_checkbox_dualcore->setEnabled(!running);
#ifdef USE_RETRO_ACHIEVEMENTS
bool hardcore = Config::Get(Config::RA_HARDCORE_ENABLED);
bool hardcore = AchievementManager::GetInstance().IsHardcoreModeActive();
m_checkbox_cheats->setEnabled(!running && !hardcore);
#else // USE_RETRO_ACHIEVEMENTS
m_checkbox_cheats->setEnabled(!running);

View file

@ -19,7 +19,7 @@
#include "Common/MsgHandler.h"
#include "Common/StringUtil.h"
#include "Core/Config/AchievementSettings.h"
#include "Core/AchievementManager.h"
#include "Core/Config/MainSettings.h"
#include "Core/Config/UISettings.h"
@ -256,7 +256,7 @@ void InterfacePane::LoadConfig()
->setChecked(Settings::Instance().IsDebugModeEnabled());
#ifdef USE_RETRO_ACHIEVEMENTS
bool hardcore = Config::Get(Config::RA_HARDCORE_ENABLED);
bool hardcore = AchievementManager::GetInstance().IsHardcoreModeActive();
SignalBlocking(m_checkbox_show_debugging_ui)->setEnabled(!hardcore);
if (hardcore)
{

View file

@ -91,6 +91,7 @@ void USBDeviceAddToWhitelistDialog::InitControls()
&QPushButton::clicked);
connect(m_refresh_devices_timer, &QTimer::timeout, this,
&USBDeviceAddToWhitelistDialog::RefreshDeviceList);
RefreshDeviceList();
m_refresh_devices_timer->start(1000);
main_layout->addWidget(usb_inserted_devices_list);

View file

@ -30,6 +30,7 @@
#include "Core/Config/SYSCONFSettings.h"
#include "Core/ConfigManager.h"
#include "Core/Core.h"
#include "Core/System.h"
#include "DolphinQt/QtUtils/DolphinFileDialog.h"
#include "DolphinQt/QtUtils/ModalMessageBox.h"
@ -92,7 +93,8 @@ WiiPane::WiiPane(QWidget* parent) : QWidget(parent)
LoadConfig();
ConnectLayout();
ValidateSelectionState();
OnEmulationStateChanged(Core::GetState() != Core::State::Uninitialized);
OnEmulationStateChanged(Core::GetState(Core::System::GetInstance()) !=
Core::State::Uninitialized);
}
void WiiPane::CreateLayout()

View file

@ -53,10 +53,12 @@ SkylanderModifyDialog::SkylanderModifyDialog(QWidget* parent, u8 slot)
{
// Should never be able to happen. Still good to have
name =
// i18n: "Var" is short for "variant"
tr("Unknown (Id:%1 Var:%2)").arg(m_figure_data.figure_id).arg(m_figure_data.variant_id);
}
}
// i18n: %1 is a name
auto* label_name = new QLabel(tr("Modifying Skylander: %1").arg(name));
hbox_name->addWidget(label_name);
@ -136,11 +138,13 @@ void SkylanderModifyDialog::PopulateSkylanderOptions(QVBoxLayout* layout)
reinterpret_cast<char16_t*>(m_figure_data.skylander_data.nickname.data())));
auto* hbox_playtime = new QHBoxLayout();
// i18n: The total amount of time the Skylander has been used for
auto* label_playtime = new QLabel(tr("Playtime:"));
auto* edit_playtime =
new QLineEdit(QStringLiteral("%1").arg(m_figure_data.skylander_data.playtime));
auto* hbox_last_reset = new QHBoxLayout();
// i18n: A timestamp for when the Skylander was most recently reset
auto* label_last_reset = new QLabel(tr("Last reset:"));
auto* edit_last_reset =
new QDateTimeEdit(QDateTime(QDate(m_figure_data.skylander_data.last_reset.year,
@ -150,6 +154,7 @@ void SkylanderModifyDialog::PopulateSkylanderOptions(QVBoxLayout* layout)
m_figure_data.skylander_data.last_reset.minute)));
auto* hbox_last_placed = new QHBoxLayout();
// i18n: A timestamp for when the Skylander was most recently used
auto* label_last_placed = new QLabel(tr("Last placed:"));
auto* edit_last_placed =
new QDateTimeEdit(QDateTime(QDate(m_figure_data.skylander_data.last_placed.year,
@ -167,10 +172,10 @@ void SkylanderModifyDialog::PopulateSkylanderOptions(QVBoxLayout* layout)
edit_last_placed->setDisplayFormat(QStringLiteral("dd/MM/yyyy hh:mm"));
edit_toy_code->setToolTip(tr("The toy code for this figure. Only available for real figures."));
edit_money->setToolTip(tr("The amount of money this skylander should have. Between 0 and 65000"));
edit_hero->setToolTip(tr("The hero level of this skylander. Only seen in Skylanders: Spyro's "
edit_money->setToolTip(tr("The amount of money this Skylander has. Between 0 and 65000"));
edit_hero->setToolTip(tr("The hero level of this Skylander. Only seen in Skylanders: Spyro's "
"Adventures. Between 0 and 100"));
edit_nick->setToolTip(tr("The nickname for this skylander. Limited to 15 characters"));
edit_nick->setToolTip(tr("The nickname for this Skylander. Limited to 15 characters"));
edit_playtime->setToolTip(
tr("The total time this figure has been used inside a game in seconds"));
edit_last_reset->setToolTip(tr("The last time the figure has been reset. If the figure has never "
@ -309,6 +314,8 @@ bool SkylanderModifyDialog::PopulateTrophyOptions(QVBoxLayout* layout)
edit_villains[i] = new QCheckBox();
edit_villains[i]->setChecked(static_cast<bool>(m_figure_data.trophy_data.unlocked_villains &
(0b1 << shift_distances[i])));
// i18n: "Captured" is a participle here. This string is used when listing villains, not when a
// villain was just captured
auto* const label = new QLabel(tr("Captured villain %1:").arg(i + 1));
auto* const hbox = new QHBoxLayout();
hbox->addWidget(label);

View file

@ -56,7 +56,7 @@ SkylanderPortalWindow::SkylanderPortalWindow(QWidget* parent) : QWidget(parent)
installEventFilter(this);
OnEmulationStateChanged(Core::GetState());
OnEmulationStateChanged(Core::GetState(Core::System::GetInstance()));
connect(m_skylander_list, &QListWidget::itemSelectionChanged, this,
&SkylanderPortalWindow::UpdateCurrentIDs);
@ -75,21 +75,8 @@ SkylanderPortalWindow::SkylanderPortalWindow(QWidget* parent) : QWidget(parent)
{
skylanders_folder = QDir(QString::fromStdString(Config::Get(Config::MAIN_SKYLANDERS_PATH)));
}
// prompt folder creation if path invalid
if (!skylanders_folder.exists())
{
QMessageBox::StandardButton create_folder_response;
create_folder_response =
QMessageBox::question(this, tr("Create Skylander Folder"),
tr("Skylanders folder not found for this user. Create new folder?"),
QMessageBox::Yes | QMessageBox::No);
if (create_folder_response == QMessageBox::Yes)
{
skylanders_folder = QDir(user_path);
Config::SetBase(Config::MAIN_SKYLANDERS_PATH, user_path.toStdString());
skylanders_folder.mkdir(skylanders_folder.path());
}
}
skylanders_folder.mkdir(skylanders_folder.path());
m_collection_path = QDir::toNativeSeparators(skylanders_folder.path()) + QDir::separator();
m_last_skylander_path = m_collection_path;
@ -546,6 +533,7 @@ void SkylanderPortalWindow::LoadSelected()
}
else
{
// i18n: This is used to create a file name. The string must end in ".sky".
const QString str = tr("Unknown(%1 %2).sky");
predef_name += str.arg(m_sky_id, m_sky_var);
}
@ -629,6 +617,7 @@ void SkylanderPortalWindow::CreateSkylanderAdvanced()
}
else
{
// i18n: This is used to create a file name. The string must end in ".sky".
QString str = tr("Unknown(%1 %2).sky");
predef_name += str.arg(m_sky_id, m_sky_var);
}
@ -673,7 +662,7 @@ void SkylanderPortalWindow::ClearSlot(u8 slot)
if (!system.GetSkylanderPortal().RemoveSkylander(slot_infos->portal_slot))
{
QMessageBox::warning(this, tr("Failed to clear Skylander!"),
tr("Failed to clear the Skylander from slot(%1)!").arg(slot),
tr("Failed to clear the Skylander from slot %1!").arg(slot),
QMessageBox::Ok);
return;
}
@ -795,7 +784,7 @@ void SkylanderPortalWindow::CreateSkyfile(const QString& path, bool load_after)
{
QMessageBox::warning(
this, tr("Failed to create Skylander file!"),
tr("Failed to create Skylander file:\n%1\n(Skylander may already be on the portal)")
tr("Failed to create Skylander file:\n%1\n\nThe Skylander may already be on the portal.")
.arg(path),
QMessageBox::Ok);
return;
@ -813,11 +802,11 @@ void SkylanderPortalWindow::LoadSkyfilePath(u8 slot, const QString& path)
File::IOFile sky_file(path.toStdString(), "r+b");
if (!sky_file)
{
QMessageBox::warning(
this, tr("Failed to open the Skylander file!"),
tr("Failed to open the Skylander file(%1)!\nFile may already be in use on the portal.")
.arg(path),
QMessageBox::Ok);
QMessageBox::warning(this, tr("Failed to open the Skylander file!"),
tr("Failed to open the Skylander file:\n%1\n\nThe file may already be in "
"use on the portal.")
.arg(path),
QMessageBox::Ok);
return;
}
std::array<u8, 0x40 * 0x10> file_data;
@ -825,7 +814,7 @@ void SkylanderPortalWindow::LoadSkyfilePath(u8 slot, const QString& path)
{
QMessageBox::warning(
this, tr("Failed to read the Skylander file!"),
tr("Failed to read the Skylander file(%1)!\nFile was too small.").arg(path),
tr("Failed to read the Skylander file:\n%1\n\nThe file was too small.").arg(path),
QMessageBox::Ok);
return;
}
@ -839,7 +828,7 @@ void SkylanderPortalWindow::LoadSkyfilePath(u8 slot, const QString& path)
if (portal_slot == 0xFF)
{
QMessageBox::warning(this, tr("Failed to load the Skylander file!"),
tr("Failed to load the Skylander file(%1)!\n").arg(path), QMessageBox::Ok);
tr("Failed to load the Skylander file:\n%1").arg(path), QMessageBox::Ok);
return;
}
m_sky_slots[slot] = {portal_slot, id_var.first, id_var.second};
@ -862,6 +851,7 @@ void SkylanderPortalWindow::UpdateSlotNames()
}
else
{
// i18n: "Var" is short for "variant"
display_string = tr("Unknown (Id:%1 Var:%2)").arg(sd->m_sky_id).arg(sd->m_sky_var);
}
}

View file

@ -25,6 +25,7 @@
#include "Core/HW/WiimoteEmu/MotionPlus.h"
#include "Core/HW/WiimoteEmu/WiimoteEmu.h"
#include "Core/HW/WiimoteReal/WiimoteReal.h"
#include "Core/System.h"
#include "DolphinQt/QtUtils/AspectRatioWidget.h"
#include "DolphinQt/QtUtils/QueueOnObject.h"
@ -347,7 +348,7 @@ WiiTASInputWindow::WiiTASInputWindow(QWidget* parent, int num) : TASInputWindow(
setLayout(layout);
if (Core::IsRunning())
if (Core::IsRunning(Core::System::GetInstance()))
{
m_active_extension = GetWiimote()->GetActiveExtensionNumber();
m_is_motion_plus_attached = GetWiimote()->IsMotionPlusAttached();

View file

@ -11,6 +11,7 @@
#include "Core/Core.h"
#include "Core/NetPlayProto.h"
#include "Core/System.h"
#include "DolphinQt/Host.h"
#include "DolphinQt/Resources.h"
#include "DolphinQt/Settings.h"
@ -36,7 +37,7 @@ ToolBar::ToolBar(QWidget* parent) : QToolBar(parent)
[this](Core::State state) { OnEmulationStateChanged(state); });
connect(Host::GetInstance(), &Host::UpdateDisasmDialog, this,
[this] { OnEmulationStateChanged(Core::GetState()); });
[this] { OnEmulationStateChanged(Core::GetState(Core::System::GetInstance())); });
connect(&Settings::Instance(), &Settings::DebugModeToggled, this, &ToolBar::OnDebugModeToggled);
@ -51,7 +52,7 @@ ToolBar::ToolBar(QWidget* parent) : QToolBar(parent)
connect(&Settings::Instance(), &Settings::GameListRefreshStarted, this,
[this] { m_refresh_action->setEnabled(true); });
OnEmulationStateChanged(Core::GetState());
OnEmulationStateChanged(Core::GetState(Core::System::GetInstance()));
OnDebugModeToggled(Settings::Instance().IsDebugModeEnabled());
}
@ -65,7 +66,7 @@ void ToolBar::OnEmulationStateChanged(Core::State state)
bool playing = running && state != Core::State::Paused;
UpdatePausePlayButtonState(playing);
bool paused = Core::GetState() == Core::State::Paused;
const bool paused = Core::GetState(Core::System::GetInstance()) == Core::State::Paused;
m_step_action->setEnabled(paused);
m_step_over_action->setEnabled(paused);
m_step_out_action->setEnabled(paused);
@ -87,7 +88,7 @@ void ToolBar::OnDebugModeToggled(bool enabled)
m_show_pc_action->setVisible(enabled);
m_set_pc_action->setVisible(enabled);
bool paused = Core::GetState() == Core::State::Paused;
const bool paused = Core::GetState(Core::System::GetInstance()) == Core::State::Paused;
m_step_action->setEnabled(paused);
m_step_over_action->setEnabled(paused);
m_step_out_action->setEnabled(paused);
@ -183,7 +184,7 @@ void ToolBar::UpdateIcons()
m_open_action->setIcon(Resources::GetThemeIcon("open"));
m_refresh_action->setIcon(Resources::GetThemeIcon("refresh"));
const Core::State state = Core::GetState();
const Core::State state = Core::GetState(Core::System::GetInstance());
const bool playing = state != Core::State::Uninitialized && state != Core::State::Paused;
if (!playing)
m_pause_play_action->setIcon(Resources::GetThemeIcon("play"));

View file

@ -57,6 +57,7 @@ void Updater::OnUpdateAvailable(const NewVersionInformation& info)
std::optional<int> choice = RunOnObject(m_parent, [&] {
QDialog* dialog = new QDialog(m_parent);
dialog->setAttribute(Qt::WA_DeleteOnClose, true);
dialog->setWindowTitle(tr("Update available"));
dialog->setWindowFlags(dialog->windowFlags() & ~Qt::WindowContextHelpButtonHint);