Merge pull request #12263 from Dentomologist/realtime_cheatsearch_update
CheatSearch: Automatically update Current Values
This commit is contained in:
commit
731013c316
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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">;
|
||||
|
|
Loading…
Reference in New Issue