Merge pull request #12263 from Dentomologist/realtime_cheatsearch_update

CheatSearch: Automatically update Current Values
This commit is contained in:
Mai 2023-11-28 04:17:06 +01:00 committed by GitHub
commit 731013c316
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 194 additions and 59 deletions

View File

@ -614,17 +614,15 @@ std::unique_ptr<Cheats::CheatSearchSessionBase> Cheats::CheatSearchSession<T>::C
template <typename T>
std::unique_ptr<Cheats::CheatSearchSessionBase>
Cheats::CheatSearchSession<T>::ClonePartial(const std::vector<size_t>& result_indices) const
Cheats::CheatSearchSession<T>::ClonePartial(const size_t begin_index, const size_t end_index) const
{
const auto& results = m_search_results;
std::vector<SearchResult<T>> partial_results;
partial_results.reserve(result_indices.size());
for (size_t idx : result_indices)
partial_results.push_back(results[idx]);
if (begin_index == 0 && end_index >= m_search_results.size())
return Clone();
auto c =
std::make_unique<Cheats::CheatSearchSession<T>>(m_memory_ranges, m_address_space, m_aligned);
c->m_search_results = std::move(partial_results);
c->m_search_results.assign(m_search_results.begin() + begin_index,
m_search_results.begin() + end_index);
c->m_compare_type = this->m_compare_type;
c->m_filter_type = this->m_filter_type;
c->m_value = this->m_value;

View File

@ -165,11 +165,11 @@ public:
// Create a complete copy of this search session.
virtual std::unique_ptr<CheatSearchSessionBase> Clone() const = 0;
// Create a partial copy of this search session. Only the results with the passed indices are
// copied. This is useful if you want to run a next search on only partial result data of a
// Create a partial copy of this search session. Only the results with indices in the given range
// are copied. This is useful if you want to run a next search on only partial result data of a
// previous search.
virtual std::unique_ptr<CheatSearchSessionBase>
ClonePartial(const std::vector<size_t>& result_indices) const = 0;
virtual std::unique_ptr<CheatSearchSessionBase> ClonePartial(size_t begin_index,
size_t end_index) const = 0;
};
template <typename T>
@ -210,8 +210,8 @@ public:
bool WasFirstSearchDone() const override;
std::unique_ptr<CheatSearchSessionBase> Clone() const override;
std::unique_ptr<CheatSearchSessionBase>
ClonePartial(const std::vector<size_t>& result_indices) const override;
std::unique_ptr<CheatSearchSessionBase> ClonePartial(size_t begin_index,
size_t end_index) const override;
private:
std::vector<SearchResult<T>> m_search_results;

View File

@ -33,6 +33,7 @@
#include "VideoCommon/VideoBackendBase.h"
#include "VideoCommon/VideoConfig.h"
#include "VideoCommon/VideoEvents.h"
namespace VideoInterface
{
@ -839,6 +840,7 @@ void VideoInterfaceManager::EndField(FieldType field, u64 ticks)
OutputField(field, ticks);
g_perf_metrics.CountVBlank();
VIEndFieldEvent::Trigger();
Core::OnFrameEnd();
}

View File

@ -61,7 +61,7 @@ CheatSearchWidget::CheatSearchWidget(std::unique_ptr<Cheats::CheatSearchSessionB
CreateWidgets();
ConnectWidgets();
OnValueSourceChanged();
UpdateGuiTable();
RecreateGUITable();
}
CheatSearchWidget::~CheatSearchWidget()
@ -69,6 +69,9 @@ CheatSearchWidget::~CheatSearchWidget()
auto& settings = Settings::GetQSettings();
settings.setValue(QStringLiteral("cheatsearchwidget/displayhex"),
m_display_values_in_hex_checkbox->isChecked());
settings.setValue(QStringLiteral("cheatsearchwidget/autoupdatecurrentvalues"),
m_autoupdate_current_values->isChecked());
if (m_session->IsIntegerType())
{
settings.setValue(QStringLiteral("cheatsearchwidget/parsehex"),
@ -229,16 +232,25 @@ void CheatSearchWidget::CreateWidgets()
m_info_label_1 = new QLabel(tr("Waiting for first scan..."));
m_info_label_2 = new QLabel();
auto* const checkboxes_layout = new QHBoxLayout();
m_display_values_in_hex_checkbox = new QCheckBox(tr("Display values in Hex"));
m_display_values_in_hex_checkbox->setChecked(
settings.value(QStringLiteral("cheatsearchwidget/displayhex")).toBool());
checkboxes_layout->addWidget(m_display_values_in_hex_checkbox);
checkboxes_layout->setStretchFactor(m_display_values_in_hex_checkbox, 1);
m_autoupdate_current_values = new QCheckBox(tr("Automatically update Current Values"));
m_autoupdate_current_values->setChecked(
settings.value(QStringLiteral("cheatsearchwidget/autoupdatecurrentvalues"), true).toBool());
checkboxes_layout->addWidget(m_autoupdate_current_values);
checkboxes_layout->setStretchFactor(m_autoupdate_current_values, 2);
QVBoxLayout* layout = new QVBoxLayout();
layout->addWidget(session_info_label);
layout->addWidget(instructions_label);
layout->addLayout(value_layout);
layout->addLayout(button_layout);
layout->addWidget(m_display_values_in_hex_checkbox);
layout->addLayout(checkboxes_layout);
layout->addWidget(m_info_label_1);
layout->addWidget(m_info_label_2);
layout->addWidget(m_address_table);
@ -263,6 +275,7 @@ 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();
@ -288,8 +301,7 @@ void CheatSearchWidget::OnNextScanClicked()
}
}
const Cheats::SearchErrorCode error_code = [this] {
Core::CPUThreadGuard guard(Core::System::GetInstance());
const Cheats::SearchErrorCode error_code = [this, &guard] {
return m_session->RunSearch(guard);
}();
@ -347,7 +359,7 @@ void CheatSearchWidget::OnNextScanClicked()
m_session->GetResultValueAsString(i, show_in_hex);
}
UpdateGuiTable();
RecreateGUITable();
}
else
{
@ -371,30 +383,13 @@ void CheatSearchWidget::OnNextScanClicked()
}
}
bool CheatSearchWidget::RefreshValues()
bool CheatSearchWidget::UpdateTableRows(const Core::CPUThreadGuard& guard, const size_t begin_index,
const size_t end_index, const UpdateSource source)
{
const size_t result_count = m_session->GetResultCount();
if (result_count == 0)
{
m_info_label_1->setText(tr("Cannot refresh without results."));
return false;
}
const bool too_many_results = result_count > TABLE_MAX_ROWS;
std::unique_ptr<Cheats::CheatSearchSessionBase> tmp;
if (too_many_results)
{
std::vector<size_t> value_indices;
value_indices.reserve(TABLE_MAX_ROWS);
for (size_t i = 0; i < TABLE_MAX_ROWS; ++i)
value_indices.push_back(i);
tmp = m_session->ClonePartial(value_indices);
}
else
{
tmp = m_session->Clone();
}
const bool update_status_text = source == UpdateSource::User;
std::unique_ptr<Cheats::CheatSearchSessionBase> tmp =
m_session->ClonePartial(begin_index, end_index);
tmp->SetFilterType(Cheats::FilterType::DoNotFilter);
const Cheats::SearchErrorCode error_code = [&tmp] {
@ -404,11 +399,11 @@ bool CheatSearchWidget::RefreshValues()
if (error_code != Cheats::SearchErrorCode::Success)
{
if (update_status_text)
m_info_label_1->setText(tr("Refresh failed. Please run the game for a bit and try again."));
return false;
}
m_address_table_current_values.clear();
const bool show_in_hex = m_display_values_in_hex_checkbox->isChecked();
const size_t result_count_to_display = tmp->GetResultCount();
for (size_t i = 0; i < result_count_to_display; ++i)
@ -417,22 +412,53 @@ bool CheatSearchWidget::RefreshValues()
tmp->GetResultValueAsString(i, show_in_hex);
}
UpdateGuiTable();
RefreshGUICurrentValues(begin_index, end_index);
if (update_status_text)
m_info_label_1->setText(tr("Refreshed current values."));
return true;
}
void CheatSearchWidget::UpdateTableVisibleCurrentValues(const UpdateSource source)
{
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);
}
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)
{
if (source == UpdateSource::User)
m_info_label_1->setText(tr("Cannot refresh without results."));
return false;
}
return UpdateTableRows(guard, 0, result_count, source);
}
void CheatSearchWidget::OnRefreshClicked()
{
RefreshValues();
UpdateTableAllCurrentValues(UpdateSource::User);
}
void CheatSearchWidget::OnResetClicked()
{
Core::CPUThreadGuard guard(Core::System::GetInstance());
m_session->ResetResults();
m_address_table_current_values.clear();
UpdateGuiTable();
RecreateGUITable();
m_info_label_1->setText(tr("Waiting for first scan..."));
m_info_label_2->clear();
}
@ -454,6 +480,18 @@ void CheatSearchWidget::OnAddressTableItemChanged(QTableWidgetItem* item)
}
}
int CheatSearchWidget::GetVisibleRowsBeginIndex() const
{
return m_address_table->rowAt(0);
}
int CheatSearchWidget::GetVisibleRowsEndIndex() const
{
const int row_at_bottom_of_viewport = m_address_table->rowAt(m_address_table->height());
const int end_index = m_address_table->rowCount();
return row_at_bottom_of_viewport == -1 ? end_index : row_at_bottom_of_viewport + 1;
}
void CheatSearchWidget::OnAddressTableContextMenu()
{
if (m_address_table->selectedItems().isEmpty())
@ -487,12 +525,14 @@ void CheatSearchWidget::OnDisplayHexCheckboxStateChanged()
if (!m_session->WasFirstSearchDone())
return;
if (!RefreshValues())
UpdateGuiTable();
// If the game is running CheatsManager::OnFrameEnd will update values automatically.
if (Core::GetState() != Core::State::Running)
UpdateTableAllCurrentValues(UpdateSource::User);
}
void CheatSearchWidget::GenerateARCode()
{
Core::CPUThreadGuard guard(Core::System::GetInstance());
if (m_address_table->selectedItems().isEmpty())
return;
@ -524,7 +564,28 @@ void CheatSearchWidget::GenerateARCode()
}
}
void CheatSearchWidget::UpdateGuiTable()
void CheatSearchWidget::RefreshCurrentValueTableItem(
QTableWidgetItem* const current_value_table_item)
{
const auto address = current_value_table_item->data(ADDRESS_TABLE_ADDRESS_ROLE).toUInt();
const auto curr_val_iter = m_address_table_current_values.find(address);
if (curr_val_iter != m_address_table_current_values.end())
current_value_table_item->setText(QString::fromStdString(curr_val_iter->second));
else
current_value_table_item->setText(QStringLiteral("---"));
}
void CheatSearchWidget::RefreshGUICurrentValues(const size_t begin_index, const size_t end_index)
{
for (size_t i = begin_index; i < end_index; ++i)
{
QTableWidgetItem* const current_value_table_item =
m_address_table->item(static_cast<int>(i), ADDRESS_TABLE_COLUMN_INDEX_CURRENT_VALUE);
RefreshCurrentValueTableItem(current_value_table_item);
}
}
void CheatSearchWidget::RecreateGUITable()
{
const QSignalBlocker blocker(m_address_table);
@ -570,13 +631,9 @@ void CheatSearchWidget::UpdateGuiTable()
auto* current_value_item = new QTableWidgetItem();
current_value_item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
const auto curr_val_it = m_address_table_current_values.find(address);
if (curr_val_it != m_address_table_current_values.end())
current_value_item->setText(QString::fromStdString(curr_val_it->second));
else
current_value_item->setText(QStringLiteral("---"));
current_value_item->setData(ADDRESS_TABLE_ADDRESS_ROLE, address);
current_value_item->setData(ADDRESS_TABLE_RESULT_INDEX_ROLE, static_cast<u32>(i));
m_address_table->setItem(i, ADDRESS_TABLE_COLUMN_INDEX_CURRENT_VALUE, current_value_item);
RefreshCurrentValueTableItem(current_value_item);
}
}

View File

@ -39,6 +39,15 @@ public:
explicit CheatSearchWidget(std::unique_ptr<Cheats::CheatSearchSessionBase> session);
~CheatSearchWidget() override;
enum class UpdateSource
{
User,
Auto,
};
bool UpdateTableAllCurrentValues(UpdateSource source);
void UpdateTableVisibleCurrentValues(UpdateSource source);
signals:
void ActionReplayCodeGenerated(const ActionReplay::ARCode& ar_code);
void RequestWatch(QString name, u32 address);
@ -56,9 +65,14 @@ private:
void OnValueSourceChanged();
void OnDisplayHexCheckboxStateChanged();
bool RefreshValues();
void UpdateGuiTable();
void RefreshCurrentValueTableItem(QTableWidgetItem* current_value_table_item);
void RefreshGUICurrentValues(size_t begin_index, size_t end_index);
bool UpdateTableRows(const Core::CPUThreadGuard& guard, size_t begin_index, size_t end_index,
UpdateSource source);
void RecreateGUITable();
void GenerateARCode();
int GetVisibleRowsBeginIndex() const;
int GetVisibleRowsEndIndex() const;
std::unique_ptr<Cheats::CheatSearchSessionBase> m_session;
@ -79,5 +93,6 @@ private:
QPushButton* m_reset_button;
QCheckBox* m_parse_values_as_hex_checkbox;
QCheckBox* m_display_values_in_hex_checkbox;
QCheckBox* m_autoupdate_current_values;
QTableWidget* m_address_table;
};

View File

@ -19,8 +19,10 @@
#include "DolphinQt/CheatSearchWidget.h"
#include "DolphinQt/Config/ARCodeWidget.h"
#include "DolphinQt/Config/GeckoCodeWidget.h"
#include "DolphinQt/QtUtils/PartiallyClosableTabWidget.h"
#include "DolphinQt/Settings.h"
#include "QtUtils/PartiallyClosableTabWidget.h"
#include "VideoCommon/VideoEvents.h"
CheatsManager::CheatsManager(QWidget* parent) : QDialog(parent)
{
@ -48,6 +50,52 @@ CheatsManager::~CheatsManager()
void CheatsManager::OnStateChanged(Core::State state)
{
RefreshCodeTabs(state, false);
if (state == Core::State::Paused)
UpdateAllCheatSearchWidgetCurrentValues();
}
void CheatsManager::OnFrameEnd()
{
if (!isVisible())
return;
auto* const selected_cheat_search_widget =
qobject_cast<CheatSearchWidget*>(m_tab_widget->currentWidget());
if (selected_cheat_search_widget != nullptr)
{
selected_cheat_search_widget->UpdateTableVisibleCurrentValues(
CheatSearchWidget::UpdateSource::Auto);
}
}
void CheatsManager::UpdateAllCheatSearchWidgetCurrentValues()
{
for (int i = 0; i < m_tab_widget->count(); ++i)
{
auto* const cheat_search_widget = qobject_cast<CheatSearchWidget*>(m_tab_widget->widget(i));
if (cheat_search_widget != nullptr)
cheat_search_widget->UpdateTableAllCurrentValues(CheatSearchWidget::UpdateSource::Auto);
}
}
void CheatsManager::RegisterAfterFrameEventCallback()
{
m_VI_end_field_event = VIEndFieldEvent::Register([this] { OnFrameEnd(); }, "CheatsManager");
}
void CheatsManager::RemoveAfterFrameEventCallback()
{
m_VI_end_field_event.reset();
}
void CheatsManager::hideEvent(QHideEvent* event)
{
RemoveAfterFrameEventCallback();
}
void CheatsManager::showEvent(QShowEvent* event)
{
RegisterAfterFrameEventCallback();
}
void CheatsManager::RefreshCodeTabs(Core::State state, bool force)

View File

@ -11,14 +11,16 @@
#include <QDialog>
#include "Common/CommonTypes.h"
#include "DolphinQt/GameList/GameListModel.h"
#include "Core/CheatSearch.h"
#include "DolphinQt/GameList/GameListModel.h"
#include "VideoCommon/VideoEvents.h"
class ARCodeWidget;
class GeckoCodeWidget;
class CheatSearchFactoryWidget;
class QDialogButtonBox;
class QHideEvent;
class QShowEvent;
class PartiallyClosableTabWidget;
namespace Core
@ -38,14 +40,22 @@ signals:
void ShowMemory(u32 address);
void RequestWatch(QString name, u32 address);
protected:
void hideEvent(QHideEvent* event) override;
void showEvent(QShowEvent* event) override;
private:
void CreateWidgets();
void ConnectWidgets();
void OnStateChanged(Core::State state);
void OnFrameEnd();
void RegisterAfterFrameEventCallback();
void RemoveAfterFrameEventCallback();
void OnNewSessionCreated(const Cheats::CheatSearchSessionBase& session);
void OnTabCloseRequested(int index);
void RefreshCodeTabs(Core::State state, bool force);
void UpdateAllCheatSearchWidgetCurrentValues();
std::string m_game_id;
std::string m_game_tdb_id;
@ -57,4 +67,6 @@ private:
ARCodeWidget* m_ar_code = nullptr;
GeckoCodeWidget* m_gecko_code = nullptr;
CheatSearchFactoryWidget* m_cheat_search_new = nullptr;
Common::EventHook m_VI_end_field_event;
};

View File

@ -81,3 +81,6 @@ using BeforePresentEvent = Common::HookableEvent<"BeforePresent", PresentInfo&>;
// An event that is triggered after a frame is presented.
// The exact timing of this event depends on backend/driver support.
using AfterPresentEvent = Common::HookableEvent<"AfterPresent", PresentInfo&>;
// An end of frame event that runs on the CPU thread
using VIEndFieldEvent = Common::HookableEvent<"VIEndField">;