mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-30 13:20:03 +00:00 
			
		
		
		
	cheats: Use global cheat engine (#7291)
* cheats: Use global cheat engine * cheats: Prevent wasted double-load of cheat file. * android: Fix for cheat engine updates. --------- Co-authored-by: GPUCode <geoster3d@gmail.com>
This commit is contained in:
		
							parent
							
								
									5a7f615da1
								
							
						
					
					
						commit
						7dd9174d31
					
				
					 14 changed files with 120 additions and 125 deletions
				
			
		|  | @ -7,25 +7,13 @@ package org.citra.citra_emu.features.cheats.model | ||||||
| import androidx.annotation.Keep | import androidx.annotation.Keep | ||||||
| 
 | 
 | ||||||
| @Keep | @Keep | ||||||
| class CheatEngine(titleId: Long) { | object CheatEngine { | ||||||
|     @Keep |     external fun loadCheatFile(titleId: Long) | ||||||
|     private val mPointer: Long |     external fun saveCheatFile(titleId: Long) | ||||||
| 
 |  | ||||||
|     init { |  | ||||||
|         mPointer = initialize(titleId) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     protected external fun finalize() |  | ||||||
| 
 | 
 | ||||||
|     external fun getCheats(): Array<Cheat> |     external fun getCheats(): Array<Cheat> | ||||||
| 
 | 
 | ||||||
|     external fun addCheat(cheat: Cheat?) |     external fun addCheat(cheat: Cheat?) | ||||||
|     external fun removeCheat(index: Int) |     external fun removeCheat(index: Int) | ||||||
|     external fun updateCheat(index: Int, newCheat: Cheat?) |     external fun updateCheat(index: Int, newCheat: Cheat?) | ||||||
|     external fun saveCheatFile() |  | ||||||
| 
 |  | ||||||
|     companion object { |  | ||||||
|         @JvmStatic |  | ||||||
|         private external fun initialize(titleId: Long): Long |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -47,18 +47,19 @@ class CheatsViewModel : ViewModel() { | ||||||
|     val detailsViewFocusChange get() = _detailsViewFocusChange.asStateFlow() |     val detailsViewFocusChange get() = _detailsViewFocusChange.asStateFlow() | ||||||
|     private val _detailsViewFocusChange = MutableStateFlow(false) |     private val _detailsViewFocusChange = MutableStateFlow(false) | ||||||
| 
 | 
 | ||||||
|     private var cheatEngine: CheatEngine? = null |     private var titleId: Long = 0 | ||||||
|     lateinit var cheats: Array<Cheat> |     lateinit var cheats: Array<Cheat> | ||||||
|     private var cheatsNeedSaving = false |     private var cheatsNeedSaving = false | ||||||
|     private var selectedCheatPosition = -1 |     private var selectedCheatPosition = -1 | ||||||
| 
 | 
 | ||||||
|     fun initialize(titleId: Long) { |     fun initialize(titleId_: Long) { | ||||||
|         cheatEngine = CheatEngine(titleId) |         titleId = titleId_; | ||||||
|         load() |         load() | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private fun load() { |     private fun load() { | ||||||
|         cheats = cheatEngine!!.getCheats() |         CheatEngine.loadCheatFile(titleId) | ||||||
|  |         cheats = CheatEngine.getCheats() | ||||||
|         for (i in cheats.indices) { |         for (i in cheats.indices) { | ||||||
|             cheats[i].setEnabledChangedCallback { |             cheats[i].setEnabledChangedCallback { | ||||||
|                 cheatsNeedSaving = true |                 cheatsNeedSaving = true | ||||||
|  | @ -69,7 +70,7 @@ class CheatsViewModel : ViewModel() { | ||||||
| 
 | 
 | ||||||
|     fun saveIfNeeded() { |     fun saveIfNeeded() { | ||||||
|         if (cheatsNeedSaving) { |         if (cheatsNeedSaving) { | ||||||
|             cheatEngine!!.saveCheatFile() |             CheatEngine.saveCheatFile(titleId) | ||||||
|             cheatsNeedSaving = false |             cheatsNeedSaving = false | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | @ -107,7 +108,7 @@ class CheatsViewModel : ViewModel() { | ||||||
|         _isAdding.value = false |         _isAdding.value = false | ||||||
|         _isEditing.value = false |         _isEditing.value = false | ||||||
|         val position = cheats.size |         val position = cheats.size | ||||||
|         cheatEngine!!.addCheat(cheat) |         CheatEngine.addCheat(cheat) | ||||||
|         cheatsNeedSaving = true |         cheatsNeedSaving = true | ||||||
|         load() |         load() | ||||||
|         notifyCheatAdded(position) |         notifyCheatAdded(position) | ||||||
|  | @ -123,7 +124,7 @@ class CheatsViewModel : ViewModel() { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fun updateSelectedCheat(newCheat: Cheat?) { |     fun updateSelectedCheat(newCheat: Cheat?) { | ||||||
|         cheatEngine!!.updateCheat(selectedCheatPosition, newCheat) |         CheatEngine.updateCheat(selectedCheatPosition, newCheat) | ||||||
|         cheatsNeedSaving = true |         cheatsNeedSaving = true | ||||||
|         load() |         load() | ||||||
|         notifyCheatUpdated(selectedCheatPosition) |         notifyCheatUpdated(selectedCheatPosition) | ||||||
|  | @ -141,7 +142,7 @@ class CheatsViewModel : ViewModel() { | ||||||
|     fun deleteSelectedCheat() { |     fun deleteSelectedCheat() { | ||||||
|         val position = selectedCheatPosition |         val position = selectedCheatPosition | ||||||
|         setSelectedCheat(null, -1) |         setSelectedCheat(null, -1) | ||||||
|         cheatEngine!!.removeCheat(position) |         CheatEngine.removeCheat(position) | ||||||
|         cheatsNeedSaving = true |         cheatsNeedSaving = true | ||||||
|         load() |         load() | ||||||
|         notifyCheatDeleted(position) |         notifyCheatDeleted(position) | ||||||
|  |  | ||||||
|  | @ -15,24 +15,24 @@ | ||||||
| 
 | 
 | ||||||
| extern "C" { | extern "C" { | ||||||
| 
 | 
 | ||||||
| static Cheats::CheatEngine* GetPointer(JNIEnv* env, jobject obj) { | static Cheats::CheatEngine& GetEngine() { | ||||||
|     return reinterpret_cast<Cheats::CheatEngine*>( |     Core::System& system{Core::System::GetInstance()}; | ||||||
|         env->GetLongField(obj, IDCache::GetCheatEnginePointer())); |     return system.CheatEngine(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| JNIEXPORT jlong JNICALL Java_org_citra_citra_1emu_features_cheats_model_CheatEngine_initialize( | JNIEXPORT void JNICALL Java_org_citra_citra_1emu_features_cheats_model_CheatEngine_loadCheatFile( | ||||||
|     JNIEnv* env, jclass, jlong title_id) { |     JNIEnv* env, jclass, jlong title_id) { | ||||||
|     return reinterpret_cast<jlong>(new Cheats::CheatEngine(title_id, Core::System::GetInstance())); |     GetEngine().LoadCheatFile(title_id); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| JNIEXPORT void JNICALL | JNIEXPORT void JNICALL Java_org_citra_citra_1emu_features_cheats_model_CheatEngine_saveCheatFile( | ||||||
| Java_org_citra_citra_1emu_features_cheats_model_CheatEngine_finalize(JNIEnv* env, jobject obj) { |     JNIEnv* env, jclass, jlong title_id) { | ||||||
|     delete GetPointer(env, obj); |     GetEngine().SaveCheatFile(title_id); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| JNIEXPORT jobjectArray JNICALL | JNIEXPORT jobjectArray JNICALL | ||||||
| Java_org_citra_citra_1emu_features_cheats_model_CheatEngine_getCheats(JNIEnv* env, jobject obj) { | Java_org_citra_citra_1emu_features_cheats_model_CheatEngine_getCheats(JNIEnv* env, jclass) { | ||||||
|     auto cheats = GetPointer(env, obj)->GetCheats(); |     auto cheats = GetEngine().GetCheats(); | ||||||
| 
 | 
 | ||||||
|     const jobjectArray array = |     const jobjectArray array = | ||||||
|         env->NewObjectArray(static_cast<jsize>(cheats.size()), IDCache::GetCheatClass(), nullptr); |         env->NewObjectArray(static_cast<jsize>(cheats.size()), IDCache::GetCheatClass(), nullptr); | ||||||
|  | @ -45,22 +45,19 @@ Java_org_citra_citra_1emu_features_cheats_model_CheatEngine_getCheats(JNIEnv* en | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| JNIEXPORT void JNICALL Java_org_citra_citra_1emu_features_cheats_model_CheatEngine_addCheat( | JNIEXPORT void JNICALL Java_org_citra_citra_1emu_features_cheats_model_CheatEngine_addCheat( | ||||||
|     JNIEnv* env, jobject obj, jobject j_cheat) { |     JNIEnv* env, jclass, jobject j_cheat) { | ||||||
|     GetPointer(env, obj)->AddCheat(*CheatFromJava(env, j_cheat)); |     auto cheat = *CheatFromJava(env, j_cheat); | ||||||
|  |     GetEngine().AddCheat(std::move(cheat)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| JNIEXPORT void JNICALL Java_org_citra_citra_1emu_features_cheats_model_CheatEngine_removeCheat( | JNIEXPORT void JNICALL Java_org_citra_citra_1emu_features_cheats_model_CheatEngine_removeCheat( | ||||||
|     JNIEnv* env, jobject obj, jint index) { |     JNIEnv* env, jclass, jint index) { | ||||||
|     GetPointer(env, obj)->RemoveCheat(index); |     GetEngine().RemoveCheat(index); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| JNIEXPORT void JNICALL Java_org_citra_citra_1emu_features_cheats_model_CheatEngine_updateCheat( | JNIEXPORT void JNICALL Java_org_citra_citra_1emu_features_cheats_model_CheatEngine_updateCheat( | ||||||
|     JNIEnv* env, jobject obj, jint index, jobject j_new_cheat) { |     JNIEnv* env, jclass, jint index, jobject j_new_cheat) { | ||||||
|     GetPointer(env, obj)->UpdateCheat(index, *CheatFromJava(env, j_new_cheat)); |     auto cheat = *CheatFromJava(env, j_new_cheat); | ||||||
| } |     GetEngine().UpdateCheat(index, std::move(cheat)); | ||||||
| 
 |  | ||||||
| JNIEXPORT void JNICALL Java_org_citra_citra_1emu_features_cheats_model_CheatEngine_saveCheatFile( |  | ||||||
|     JNIEnv* env, jobject obj) { |  | ||||||
|     GetPointer(env, obj)->SaveCheatFile(); |  | ||||||
| } | } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -35,8 +35,6 @@ static jclass s_cheat_class; | ||||||
| static jfieldID s_cheat_pointer; | static jfieldID s_cheat_pointer; | ||||||
| static jmethodID s_cheat_constructor; | static jmethodID s_cheat_constructor; | ||||||
| 
 | 
 | ||||||
| static jfieldID s_cheat_engine_pointer; |  | ||||||
| 
 |  | ||||||
| static jfieldID s_game_info_pointer; | static jfieldID s_game_info_pointer; | ||||||
| 
 | 
 | ||||||
| static jclass s_disk_cache_progress_class; | static jclass s_disk_cache_progress_class; | ||||||
|  | @ -116,10 +114,6 @@ jmethodID GetCheatConstructor() { | ||||||
|     return s_cheat_constructor; |     return s_cheat_constructor; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| jfieldID GetCheatEnginePointer() { |  | ||||||
|     return s_cheat_engine_pointer; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| jfieldID GetGameInfoPointer() { | jfieldID GetGameInfoPointer() { | ||||||
|     return s_game_info_pointer; |     return s_game_info_pointer; | ||||||
| } | } | ||||||
|  | @ -195,12 +189,6 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) { | ||||||
|     s_cheat_constructor = env->GetMethodID(cheat_class, "<init>", "(J)V"); |     s_cheat_constructor = env->GetMethodID(cheat_class, "<init>", "(J)V"); | ||||||
|     env->DeleteLocalRef(cheat_class); |     env->DeleteLocalRef(cheat_class); | ||||||
| 
 | 
 | ||||||
|     // Initialize CheatEngine
 |  | ||||||
|     const jclass cheat_engine_class = |  | ||||||
|         env->FindClass("org/citra/citra_emu/features/cheats/model/CheatEngine"); |  | ||||||
|     s_cheat_engine_pointer = env->GetFieldID(cheat_engine_class, "mPointer", "J"); |  | ||||||
|     env->DeleteLocalRef(cheat_engine_class); |  | ||||||
| 
 |  | ||||||
|     // Initialize GameInfo
 |     // Initialize GameInfo
 | ||||||
|     const jclass game_info_class = env->FindClass("org/citra/citra_emu/model/GameInfo"); |     const jclass game_info_class = env->FindClass("org/citra/citra_emu/model/GameInfo"); | ||||||
|     s_game_info_pointer = env->GetFieldID(game_info_class, "pointer", "J"); |     s_game_info_pointer = env->GetFieldID(game_info_class, "pointer", "J"); | ||||||
|  |  | ||||||
|  | @ -35,8 +35,6 @@ jclass GetCheatClass(); | ||||||
| jfieldID GetCheatPointer(); | jfieldID GetCheatPointer(); | ||||||
| jmethodID GetCheatConstructor(); | jmethodID GetCheatConstructor(); | ||||||
| 
 | 
 | ||||||
| jfieldID GetCheatEnginePointer(); |  | ||||||
| 
 |  | ||||||
| jfieldID GetGameInfoPointer(); | jfieldID GetGameInfoPointer(); | ||||||
| 
 | 
 | ||||||
| jclass GetDiskCacheProgressClass(); | jclass GetDiskCacheProgressClass(); | ||||||
|  |  | ||||||
|  | @ -11,8 +11,10 @@ | ||||||
| #include "core/cheats/gateway_cheat.h" | #include "core/cheats/gateway_cheat.h" | ||||||
| #include "ui_configure_cheats.h" | #include "ui_configure_cheats.h" | ||||||
| 
 | 
 | ||||||
| ConfigureCheats::ConfigureCheats(Core::System& system, u64 title_id_, QWidget* parent) | ConfigureCheats::ConfigureCheats(Cheats::CheatEngine& cheat_engine_, u64 title_id_, QWidget* parent) | ||||||
|     : QWidget(parent), ui(std::make_unique<Ui::ConfigureCheats>()), title_id{title_id_} { |     : QWidget(parent), | ||||||
|  |       ui(std::make_unique<Ui::ConfigureCheats>()), cheat_engine{cheat_engine_}, title_id{ | ||||||
|  |                                                                                     title_id_} { | ||||||
|     // Setup gui control settings
 |     // Setup gui control settings
 | ||||||
|     ui->setupUi(this); |     ui->setupUi(this); | ||||||
|     ui->tableCheats->setColumnWidth(0, 30); |     ui->tableCheats->setColumnWidth(0, 30); | ||||||
|  | @ -34,15 +36,14 @@ ConfigureCheats::ConfigureCheats(Core::System& system, u64 title_id_, QWidget* p | ||||||
|             [this] { SaveCheat(ui->tableCheats->currentRow()); }); |             [this] { SaveCheat(ui->tableCheats->currentRow()); }); | ||||||
|     connect(ui->buttonDelete, &QPushButton::clicked, this, &ConfigureCheats::OnDeleteCheat); |     connect(ui->buttonDelete, &QPushButton::clicked, this, &ConfigureCheats::OnDeleteCheat); | ||||||
| 
 | 
 | ||||||
|     cheat_engine = std::make_unique<Cheats::CheatEngine>(title_id, system); |     cheat_engine.LoadCheatFile(title_id); | ||||||
| 
 |  | ||||||
|     LoadCheats(); |     LoadCheats(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ConfigureCheats::~ConfigureCheats() = default; | ConfigureCheats::~ConfigureCheats() = default; | ||||||
| 
 | 
 | ||||||
| void ConfigureCheats::LoadCheats() { | void ConfigureCheats::LoadCheats() { | ||||||
|     cheats = cheat_engine->GetCheats(); |     cheats = cheat_engine.GetCheats(); | ||||||
|     const int cheats_count = static_cast<int>(cheats.size()); |     const int cheats_count = static_cast<int>(cheats.size()); | ||||||
| 
 | 
 | ||||||
|     ui->tableCheats->setRowCount(cheats_count); |     ui->tableCheats->setRowCount(cheats_count); | ||||||
|  | @ -106,12 +107,12 @@ bool ConfigureCheats::SaveCheat(int row) { | ||||||
|                                                         ui->textNotes->toPlainText().toStdString()); |                                                         ui->textNotes->toPlainText().toStdString()); | ||||||
| 
 | 
 | ||||||
|     if (newly_created) { |     if (newly_created) { | ||||||
|         cheat_engine->AddCheat(cheat); |         cheat_engine.AddCheat(std::move(cheat)); | ||||||
|         newly_created = false; |         newly_created = false; | ||||||
|     } else { |     } else { | ||||||
|         cheat_engine->UpdateCheat(row, cheat); |         cheat_engine.UpdateCheat(row, std::move(cheat)); | ||||||
|     } |     } | ||||||
|     cheat_engine->SaveCheatFile(); |     cheat_engine.SaveCheatFile(title_id); | ||||||
| 
 | 
 | ||||||
|     int previous_row = ui->tableCheats->currentRow(); |     int previous_row = ui->tableCheats->currentRow(); | ||||||
|     int previous_col = ui->tableCheats->currentColumn(); |     int previous_col = ui->tableCheats->currentColumn(); | ||||||
|  | @ -161,7 +162,7 @@ void ConfigureCheats::OnCheckChanged(int state) { | ||||||
|     const QCheckBox* checkbox = qobject_cast<QCheckBox*>(sender()); |     const QCheckBox* checkbox = qobject_cast<QCheckBox*>(sender()); | ||||||
|     int row = static_cast<int>(checkbox->property("row").toInt()); |     int row = static_cast<int>(checkbox->property("row").toInt()); | ||||||
|     cheats[row]->SetEnabled(state); |     cheats[row]->SetEnabled(state); | ||||||
|     cheat_engine->SaveCheatFile(); |     cheat_engine.SaveCheatFile(title_id); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ConfigureCheats::OnTextEdited() { | void ConfigureCheats::OnTextEdited() { | ||||||
|  | @ -173,8 +174,8 @@ void ConfigureCheats::OnDeleteCheat() { | ||||||
|     if (newly_created) { |     if (newly_created) { | ||||||
|         newly_created = false; |         newly_created = false; | ||||||
|     } else { |     } else { | ||||||
|         cheat_engine->RemoveCheat(ui->tableCheats->currentRow()); |         cheat_engine.RemoveCheat(ui->tableCheats->currentRow()); | ||||||
|         cheat_engine->SaveCheatFile(); |         cheat_engine.SaveCheatFile(title_id); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     LoadCheats(); |     LoadCheats(); | ||||||
|  |  | ||||||
|  | @ -5,6 +5,7 @@ | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <memory> | #include <memory> | ||||||
|  | #include <span> | ||||||
| #include <QWidget> | #include <QWidget> | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| 
 | 
 | ||||||
|  | @ -25,7 +26,8 @@ class ConfigureCheats : public QWidget { | ||||||
|     Q_OBJECT |     Q_OBJECT | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
|     explicit ConfigureCheats(Core::System& system, u64 title_id, QWidget* parent = nullptr); |     explicit ConfigureCheats(Cheats::CheatEngine& cheat_engine, u64 title_id_, | ||||||
|  |                              QWidget* parent = nullptr); | ||||||
|     ~ConfigureCheats(); |     ~ConfigureCheats(); | ||||||
|     bool ApplyConfiguration(); |     bool ApplyConfiguration(); | ||||||
| 
 | 
 | ||||||
|  | @ -58,9 +60,9 @@ private slots: | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     std::unique_ptr<Ui::ConfigureCheats> ui; |     std::unique_ptr<Ui::ConfigureCheats> ui; | ||||||
|     std::vector<std::shared_ptr<Cheats::CheatBase>> cheats; |     Cheats::CheatEngine& cheat_engine; | ||||||
|  |     std::span<const std::shared_ptr<Cheats::CheatBase>> cheats; | ||||||
|     bool edited = false, newly_created = false; |     bool edited = false, newly_created = false; | ||||||
|     int last_row = -1, last_col = -1; |     int last_row = -1, last_col = -1; | ||||||
|     u64 title_id; |     u64 title_id; | ||||||
|     std::unique_ptr<Cheats::CheatEngine> cheat_engine; |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -38,7 +38,7 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const QString | ||||||
|     graphics_tab = std::make_unique<ConfigureGraphics>(physical_devices, is_powered_on, this); |     graphics_tab = std::make_unique<ConfigureGraphics>(physical_devices, is_powered_on, this); | ||||||
|     system_tab = std::make_unique<ConfigureSystem>(system, this); |     system_tab = std::make_unique<ConfigureSystem>(system, this); | ||||||
|     debug_tab = std::make_unique<ConfigureDebug>(is_powered_on, this); |     debug_tab = std::make_unique<ConfigureDebug>(is_powered_on, this); | ||||||
|     cheat_tab = std::make_unique<ConfigureCheats>(system, title_id, this); |     cheat_tab = std::make_unique<ConfigureCheats>(system.CheatEngine(), title_id, this); | ||||||
| 
 | 
 | ||||||
|     ui->setupUi(this); |     ui->setupUi(this); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -10,7 +10,6 @@ | ||||||
| #include "core/cheats/gateway_cheat.h" | #include "core/cheats/gateway_cheat.h" | ||||||
| #include "core/core.h" | #include "core/core.h" | ||||||
| #include "core/core_timing.h" | #include "core/core_timing.h" | ||||||
| #include "core/hle/kernel/process.h" |  | ||||||
| 
 | 
 | ||||||
| namespace Cheats { | namespace Cheats { | ||||||
| 
 | 
 | ||||||
|  | @ -18,11 +17,11 @@ namespace Cheats { | ||||||
| // we use the same value
 | // we use the same value
 | ||||||
| constexpr u64 run_interval_ticks = 50'000'000; | constexpr u64 run_interval_ticks = 50'000'000; | ||||||
| 
 | 
 | ||||||
| CheatEngine::CheatEngine(u64 title_id_, Core::System& system_) | CheatEngine::CheatEngine(Core::System& system_) : system{system_} {} | ||||||
|     : system(system_), title_id{title_id_} { | 
 | ||||||
|     LoadCheatFile(); | CheatEngine::~CheatEngine() { | ||||||
|     if (system.IsPoweredOn()) { |     if (system.IsPoweredOn()) { | ||||||
|         Connect(); |         system.CoreTiming().UnscheduleEvent(event, 0); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -33,24 +32,18 @@ void CheatEngine::Connect() { | ||||||
|     system.CoreTiming().ScheduleEvent(run_interval_ticks, event); |     system.CoreTiming().ScheduleEvent(run_interval_ticks, event); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| CheatEngine::~CheatEngine() { | std::span<const std::shared_ptr<CheatBase>> CheatEngine::GetCheats() const { | ||||||
|     if (system.IsPoweredOn()) { |     std::shared_lock lock{cheats_list_mutex}; | ||||||
|         system.CoreTiming().UnscheduleEvent(event, 0); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| std::vector<std::shared_ptr<CheatBase>> CheatEngine::GetCheats() const { |  | ||||||
|     std::shared_lock<std::shared_mutex> lock(cheats_list_mutex); |  | ||||||
|     return cheats_list; |     return cheats_list; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void CheatEngine::AddCheat(const std::shared_ptr<CheatBase>& cheat) { | void CheatEngine::AddCheat(std::shared_ptr<CheatBase>&& cheat) { | ||||||
|     std::unique_lock<std::shared_mutex> lock(cheats_list_mutex); |     std::unique_lock lock{cheats_list_mutex}; | ||||||
|     cheats_list.push_back(cheat); |     cheats_list.push_back(std::move(cheat)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void CheatEngine::RemoveCheat(std::size_t index) { | void CheatEngine::RemoveCheat(std::size_t index) { | ||||||
|     std::unique_lock<std::shared_mutex> lock(cheats_list_mutex); |     std::unique_lock lock{cheats_list_mutex}; | ||||||
|     if (index < 0 || index >= cheats_list.size()) { |     if (index < 0 || index >= cheats_list.size()) { | ||||||
|         LOG_ERROR(Core_Cheats, "Invalid index {}", index); |         LOG_ERROR(Core_Cheats, "Invalid index {}", index); | ||||||
|         return; |         return; | ||||||
|  | @ -58,16 +51,16 @@ void CheatEngine::RemoveCheat(std::size_t index) { | ||||||
|     cheats_list.erase(cheats_list.begin() + index); |     cheats_list.erase(cheats_list.begin() + index); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void CheatEngine::UpdateCheat(std::size_t index, const std::shared_ptr<CheatBase>& new_cheat) { | void CheatEngine::UpdateCheat(std::size_t index, std::shared_ptr<CheatBase>&& new_cheat) { | ||||||
|     std::unique_lock<std::shared_mutex> lock(cheats_list_mutex); |     std::unique_lock lock{cheats_list_mutex}; | ||||||
|     if (index < 0 || index >= cheats_list.size()) { |     if (index < 0 || index >= cheats_list.size()) { | ||||||
|         LOG_ERROR(Core_Cheats, "Invalid index {}", index); |         LOG_ERROR(Core_Cheats, "Invalid index {}", index); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|     cheats_list[index] = new_cheat; |     cheats_list[index] = std::move(new_cheat); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void CheatEngine::SaveCheatFile() const { | void CheatEngine::SaveCheatFile(u64 title_id) const { | ||||||
|     const std::string cheat_dir = FileUtil::GetUserPath(FileUtil::UserPath::CheatsDir); |     const std::string cheat_dir = FileUtil::GetUserPath(FileUtil::UserPath::CheatsDir); | ||||||
|     const std::string filepath = fmt::format("{}{:016X}.txt", cheat_dir, title_id); |     const std::string filepath = fmt::format("{}{:016X}.txt", cheat_dir, title_id); | ||||||
| 
 | 
 | ||||||
|  | @ -82,7 +75,14 @@ void CheatEngine::SaveCheatFile() const { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void CheatEngine::LoadCheatFile() { | void CheatEngine::LoadCheatFile(u64 title_id) { | ||||||
|  |     { | ||||||
|  |         std::unique_lock lock{cheats_list_mutex}; | ||||||
|  |         if (loaded_title_id.has_value() && loaded_title_id == title_id) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     const std::string cheat_dir = FileUtil::GetUserPath(FileUtil::UserPath::CheatsDir); |     const std::string cheat_dir = FileUtil::GetUserPath(FileUtil::UserPath::CheatsDir); | ||||||
|     const std::string filepath = fmt::format("{}{:016X}.txt", cheat_dir, title_id); |     const std::string filepath = fmt::format("{}{:016X}.txt", cheat_dir, title_id); | ||||||
| 
 | 
 | ||||||
|  | @ -90,20 +90,22 @@ void CheatEngine::LoadCheatFile() { | ||||||
|         FileUtil::CreateDir(cheat_dir); |         FileUtil::CreateDir(cheat_dir); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (!FileUtil::Exists(filepath)) |     if (!FileUtil::Exists(filepath)) { | ||||||
|         return; |         return; | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     auto gateway_cheats = GatewayCheat::LoadFile(filepath); |     auto gateway_cheats = GatewayCheat::LoadFile(filepath); | ||||||
|     { |     { | ||||||
|         std::unique_lock<std::shared_mutex> lock(cheats_list_mutex); |         std::unique_lock lock{cheats_list_mutex}; | ||||||
|         std::move(gateway_cheats.begin(), gateway_cheats.end(), std::back_inserter(cheats_list)); |         loaded_title_id = title_id; | ||||||
|  |         cheats_list = std::move(gateway_cheats); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void CheatEngine::RunCallback([[maybe_unused]] std::uintptr_t user_data, s64 cycles_late) { | void CheatEngine::RunCallback([[maybe_unused]] std::uintptr_t user_data, s64 cycles_late) { | ||||||
|     { |     { | ||||||
|         std::shared_lock<std::shared_mutex> lock(cheats_list_mutex); |         std::shared_lock lock{cheats_list_mutex}; | ||||||
|         for (auto& cheat : cheats_list) { |         for (const auto& cheat : cheats_list) { | ||||||
|             if (cheat->IsEnabled()) { |             if (cheat->IsEnabled()) { | ||||||
|                 cheat->Execute(system); |                 cheat->Execute(system); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|  | @ -5,7 +5,9 @@ | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <memory> | #include <memory> | ||||||
|  | #include <optional> | ||||||
| #include <shared_mutex> | #include <shared_mutex> | ||||||
|  | #include <span> | ||||||
| #include <vector> | #include <vector> | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| 
 | 
 | ||||||
|  | @ -24,22 +26,39 @@ class CheatBase; | ||||||
| 
 | 
 | ||||||
| class CheatEngine { | class CheatEngine { | ||||||
| public: | public: | ||||||
|     explicit CheatEngine(u64 title_id_, Core::System& system); |     explicit CheatEngine(Core::System& system); | ||||||
|     ~CheatEngine(); |     ~CheatEngine(); | ||||||
|  | 
 | ||||||
|  |     /// Registers the cheat execution callback.
 | ||||||
|     void Connect(); |     void Connect(); | ||||||
|     std::vector<std::shared_ptr<CheatBase>> GetCheats() const; | 
 | ||||||
|     void AddCheat(const std::shared_ptr<CheatBase>& cheat); |     /// Returns a span of the currently active cheats.
 | ||||||
|  |     std::span<const std::shared_ptr<CheatBase>> GetCheats() const; | ||||||
|  | 
 | ||||||
|  |     /// Adds a cheat to the cheat engine.
 | ||||||
|  |     void AddCheat(std::shared_ptr<CheatBase>&& cheat); | ||||||
|  | 
 | ||||||
|  |     /// Removes a cheat at the specified index in the cheats list.
 | ||||||
|     void RemoveCheat(std::size_t index); |     void RemoveCheat(std::size_t index); | ||||||
|     void UpdateCheat(std::size_t index, const std::shared_ptr<CheatBase>& new_cheat); | 
 | ||||||
|     void SaveCheatFile() const; |     /// Updates a cheat at the specified index in the cheats list.
 | ||||||
|  |     void UpdateCheat(std::size_t index, std::shared_ptr<CheatBase>&& new_cheat); | ||||||
|  | 
 | ||||||
|  |     /// Loads the cheat file from disk for the specified title id.
 | ||||||
|  |     void LoadCheatFile(u64 title_id); | ||||||
|  | 
 | ||||||
|  |     /// Saves currently active cheats to file for the specified title id.
 | ||||||
|  |     void SaveCheatFile(u64 title_id) const; | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     void LoadCheatFile(); |     /// The cheat execution callback.
 | ||||||
|     void RunCallback(std::uintptr_t user_data, s64 cycles_late); |     void RunCallback(std::uintptr_t user_data, s64 cycles_late); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     Core::System& system; | ||||||
|  |     Core::TimingEventType* event; | ||||||
|  |     std::optional<u64> loaded_title_id; | ||||||
|     std::vector<std::shared_ptr<CheatBase>> cheats_list; |     std::vector<std::shared_ptr<CheatBase>> cheats_list; | ||||||
|     mutable std::shared_mutex cheats_list_mutex; |     mutable std::shared_mutex cheats_list_mutex; | ||||||
|     Core::TimingEventType* event; |  | ||||||
|     Core::System& system; |  | ||||||
|     u64 title_id; |  | ||||||
| }; | }; | ||||||
| } // namespace Cheats
 | } // namespace Cheats
 | ||||||
|  |  | ||||||
|  | @ -472,8 +472,8 @@ std::string GatewayCheat::ToString() const { | ||||||
|     return result; |     return result; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| std::vector<std::unique_ptr<CheatBase>> GatewayCheat::LoadFile(const std::string& filepath) { | std::vector<std::shared_ptr<CheatBase>> GatewayCheat::LoadFile(const std::string& filepath) { | ||||||
|     std::vector<std::unique_ptr<CheatBase>> cheats; |     std::vector<std::shared_ptr<CheatBase>> cheats; | ||||||
| 
 | 
 | ||||||
|     boost::iostreams::stream<boost::iostreams::file_descriptor_source> file; |     boost::iostreams::stream<boost::iostreams::file_descriptor_source> file; | ||||||
|     FileUtil::OpenFStream<std::ios_base::in>(file, filepath); |     FileUtil::OpenFStream<std::ios_base::in>(file, filepath); | ||||||
|  | @ -493,7 +493,7 @@ std::vector<std::unique_ptr<CheatBase>> GatewayCheat::LoadFile(const std::string | ||||||
|         line = Common::StripSpaces(line); // remove spaces at front and end
 |         line = Common::StripSpaces(line); // remove spaces at front and end
 | ||||||
|         if (line.length() >= 2 && line.front() == '[') { |         if (line.length() >= 2 && line.front() == '[') { | ||||||
|             if (!cheat_lines.empty()) { |             if (!cheat_lines.empty()) { | ||||||
|                 cheats.push_back(std::make_unique<GatewayCheat>(name, cheat_lines, comments)); |                 cheats.push_back(std::make_shared<GatewayCheat>(name, cheat_lines, comments)); | ||||||
|                 cheats.back()->SetEnabled(enabled); |                 cheats.back()->SetEnabled(enabled); | ||||||
|                 enabled = false; |                 enabled = false; | ||||||
|             } |             } | ||||||
|  | @ -511,7 +511,7 @@ std::vector<std::unique_ptr<CheatBase>> GatewayCheat::LoadFile(const std::string | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     if (!cheat_lines.empty()) { |     if (!cheat_lines.empty()) { | ||||||
|         cheats.push_back(std::make_unique<GatewayCheat>(name, cheat_lines, comments)); |         cheats.push_back(std::make_shared<GatewayCheat>(name, cheat_lines, comments)); | ||||||
|         cheats.back()->SetEnabled(enabled); |         cheats.back()->SetEnabled(enabled); | ||||||
|     } |     } | ||||||
|     return cheats; |     return cheats; | ||||||
|  |  | ||||||
|  | @ -77,7 +77,7 @@ public: | ||||||
|     ///     (there might be multiple lines of those hex numbers)
 |     ///     (there might be multiple lines of those hex numbers)
 | ||||||
|     ///     Comment lines start with a '*'
 |     ///     Comment lines start with a '*'
 | ||||||
|     /// This function will pares the file for such structures
 |     /// This function will pares the file for such structures
 | ||||||
|     static std::vector<std::unique_ptr<CheatBase>> LoadFile(const std::string& filepath); |     static std::vector<std::shared_ptr<CheatBase>> LoadFile(const std::string& filepath); | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     std::atomic<bool> enabled = false; |     std::atomic<bool> enabled = false; | ||||||
|  |  | ||||||
|  | @ -72,7 +72,7 @@ Core::Timing& Global() { | ||||||
|     return System::GetInstance().CoreTiming(); |     return System::GetInstance().CoreTiming(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| System::System() : movie{*this} {} | System::System() : movie{*this}, cheat_engine{*this} {} | ||||||
| 
 | 
 | ||||||
| System::~System() = default; | System::~System() = default; | ||||||
| 
 | 
 | ||||||
|  | @ -320,7 +320,10 @@ System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::st | ||||||
|         LOG_ERROR(Core, "Failed to find title id for ROM (Error {})", |         LOG_ERROR(Core, "Failed to find title id for ROM (Error {})", | ||||||
|                   static_cast<u32>(load_result)); |                   static_cast<u32>(load_result)); | ||||||
|     } |     } | ||||||
|     cheat_engine = std::make_unique<Cheats::CheatEngine>(title_id, *this); | 
 | ||||||
|  |     cheat_engine.LoadCheatFile(title_id); | ||||||
|  |     cheat_engine.Connect(); | ||||||
|  | 
 | ||||||
|     perf_stats = std::make_unique<PerfStats>(title_id); |     perf_stats = std::make_unique<PerfStats>(title_id); | ||||||
| 
 | 
 | ||||||
|     if (Settings::values.dump_textures) { |     if (Settings::values.dump_textures) { | ||||||
|  | @ -502,11 +505,11 @@ const Memory::MemorySystem& System::Memory() const { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| Cheats::CheatEngine& System::CheatEngine() { | Cheats::CheatEngine& System::CheatEngine() { | ||||||
|     return *cheat_engine; |     return cheat_engine; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const Cheats::CheatEngine& System::CheatEngine() const { | const Cheats::CheatEngine& System::CheatEngine() const { | ||||||
|     return *cheat_engine; |     return cheat_engine; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void System::RegisterVideoDumper(std::shared_ptr<VideoDumper::Backend> dumper) { | void System::RegisterVideoDumper(std::shared_ptr<VideoDumper::Backend> dumper) { | ||||||
|  | @ -560,7 +563,6 @@ void System::Shutdown(bool is_deserializing) { | ||||||
|     if (!is_deserializing) { |     if (!is_deserializing) { | ||||||
|         GDBStub::Shutdown(); |         GDBStub::Shutdown(); | ||||||
|         perf_stats.reset(); |         perf_stats.reset(); | ||||||
|         cheat_engine.reset(); |  | ||||||
|         app_loader.reset(); |         app_loader.reset(); | ||||||
|     } |     } | ||||||
|     custom_tex_manager.reset(); |     custom_tex_manager.reset(); | ||||||
|  | @ -718,7 +720,7 @@ void System::serialize(Archive& ar, const unsigned int file_version) { | ||||||
|     if (Archive::is_loading::value) { |     if (Archive::is_loading::value) { | ||||||
|         timing->UnlockEventQueue(); |         timing->UnlockEventQueue(); | ||||||
|         memory->SetDSP(*dsp_core); |         memory->SetDSP(*dsp_core); | ||||||
|         cheat_engine->Connect(); |         cheat_engine.Connect(); | ||||||
|         gpu->Sync(); |         gpu->Sync(); | ||||||
| 
 | 
 | ||||||
|         // Re-register gpu callback, because gsp service changed after service_manager got
 |         // Re-register gpu callback, because gsp service changed after service_manager got
 | ||||||
|  |  | ||||||
|  | @ -11,6 +11,7 @@ | ||||||
| #include <boost/serialization/version.hpp> | #include <boost/serialization/version.hpp> | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| #include "core/arm/arm_interface.h" | #include "core/arm/arm_interface.h" | ||||||
|  | #include "core/cheats/cheats.h" | ||||||
| #include "core/movie.h" | #include "core/movie.h" | ||||||
| #include "core/perf_stats.h" | #include "core/perf_stats.h" | ||||||
| 
 | 
 | ||||||
|  | @ -48,10 +49,6 @@ struct New3dsHwCapabilities; | ||||||
| enum class MemoryMode : u8; | enum class MemoryMode : u8; | ||||||
| } // namespace Kernel
 | } // namespace Kernel
 | ||||||
| 
 | 
 | ||||||
| namespace Cheats { |  | ||||||
| class CheatEngine; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| namespace VideoDumper { | namespace VideoDumper { | ||||||
| class Backend; | class Backend; | ||||||
| } | } | ||||||
|  | @ -401,7 +398,7 @@ private: | ||||||
|     Core::Movie movie; |     Core::Movie movie; | ||||||
| 
 | 
 | ||||||
|     /// Cheats manager
 |     /// Cheats manager
 | ||||||
|     std::unique_ptr<Cheats::CheatEngine> cheat_engine; |     Cheats::CheatEngine cheat_engine; | ||||||
| 
 | 
 | ||||||
|     /// Video dumper backend
 |     /// Video dumper backend
 | ||||||
|     std::shared_ptr<VideoDumper::Backend> video_dumper; |     std::shared_ptr<VideoDumper::Backend> video_dumper; | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue