2024-07-30 11:42:36 +00:00
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
2023-05-25 16:24:01 +00:00
# include "MainWindow.h"
# include "QtHost.h"
# include "QtUtils.h"
# include "SettingWidgetBinder.h"
# include "Settings/GameCheatSettingsWidget.h"
2023-10-14 08:45:09 +00:00
# include "Settings/SettingsWindow.h"
2023-05-25 16:24:01 +00:00
# include "pcsx2/GameList.h"
# include "pcsx2/Patch.h"
# include "common/HeterogeneousContainers.h"
2024-10-20 15:39:09 +00:00
# include <QtCore/QSortFilterProxyModel>
2024-10-20 15:25:59 +00:00
# include <QtGui/QStandardItemModel>
2023-10-14 08:45:09 +00:00
GameCheatSettingsWidget : : GameCheatSettingsWidget ( SettingsWindow * dialog , QWidget * parent )
2023-05-25 16:24:01 +00:00
: m_dialog ( dialog )
{
m_ui . setupUi ( this ) ;
2024-10-20 15:25:59 +00:00
m_model = new QStandardItemModel ( this ) ;
QStringList headers ;
headers . push_back ( tr ( " Name " ) ) ;
headers . push_back ( tr ( " Author " ) ) ;
headers . push_back ( tr ( " Description " ) ) ;
m_model - > setHorizontalHeaderLabels ( headers ) ;
2024-10-20 15:39:09 +00:00
m_model_proxy = new QSortFilterProxyModel ( this ) ;
m_model_proxy - > setSourceModel ( m_model ) ;
m_model_proxy - > setRecursiveFilteringEnabled ( true ) ;
m_model_proxy - > setAutoAcceptChildRows ( true ) ;
m_model_proxy - > setFilterCaseSensitivity ( Qt : : CaseInsensitive ) ;
m_ui . cheatList - > setModel ( m_model_proxy ) ;
2023-05-25 16:24:01 +00:00
reloadList ( ) ;
2024-10-20 15:39:09 +00:00
m_ui . cheatList - > expandAll ( ) ;
2023-05-25 16:24:01 +00:00
SettingsInterface * sif = m_dialog - > getSettingsInterface ( ) ;
SettingWidgetBinder : : BindWidgetToBoolSetting ( sif , m_ui . enableCheats , " EmuCore " , " EnableCheats " , false ) ;
2024-01-01 00:23:27 +00:00
SettingWidgetBinder : : BindWidgetToBoolSetting ( sif , m_ui . allCRCsCheckbox , " EmuCore " , " ShowCheatsForAllCRCs " , false ) ;
2023-05-25 16:24:01 +00:00
updateListEnabled ( ) ;
2024-04-12 12:22:58 +00:00
connect ( m_ui . enableCheats , & QCheckBox : : checkStateChanged , this , & GameCheatSettingsWidget : : updateListEnabled ) ;
2024-10-20 15:25:59 +00:00
connect ( m_ui . cheatList , & QTreeView : : doubleClicked , this , & GameCheatSettingsWidget : : onCheatListItemDoubleClicked ) ;
connect ( m_model , & QStandardItemModel : : itemChanged , this , & GameCheatSettingsWidget : : onCheatListItemChanged ) ;
2023-11-17 01:33:24 +00:00
connect ( m_ui . reloadCheats , & QPushButton : : clicked , this , & GameCheatSettingsWidget : : onReloadClicked ) ;
2023-05-25 16:24:01 +00:00
connect ( m_ui . enableAll , & QPushButton : : clicked , this , [ this ] ( ) { setStateForAll ( true ) ; } ) ;
connect ( m_ui . disableAll , & QPushButton : : clicked , this , [ this ] ( ) { setStateForAll ( false ) ; } ) ;
2024-04-12 12:22:58 +00:00
connect ( m_ui . allCRCsCheckbox , & QCheckBox : : checkStateChanged , this , & GameCheatSettingsWidget : : onReloadClicked ) ;
2024-10-20 15:39:09 +00:00
connect ( m_ui . searchText , & QLineEdit : : textChanged , this , [ this ] ( const QString & text ) {
m_model_proxy - > setFilterFixedString ( text ) ;
m_ui . cheatList - > expandAll ( ) ;
} ) ;
2024-01-09 23:52:24 +00:00
dialog - > registerWidgetHelp ( m_ui . allCRCsCheckbox , tr ( " Show Cheats For All CRCs " ) , tr ( " Checked " ) ,
tr ( " Toggles scanning patch files for all CRCs of the game. With this enabled available patches for the game serial with different CRCs will also be loaded. " ) ) ;
2023-05-25 16:24:01 +00:00
}
GameCheatSettingsWidget : : ~ GameCheatSettingsWidget ( ) = default ;
2024-10-20 15:25:59 +00:00
void GameCheatSettingsWidget : : onCheatListItemDoubleClicked ( const QModelIndex & index )
2023-05-25 16:24:01 +00:00
{
2024-10-20 15:39:09 +00:00
const QModelIndex source_index = m_model_proxy - > mapToSource ( index ) ;
const QModelIndex sibling_index = source_index . sibling ( source_index . row ( ) , 0 ) ;
2024-10-20 15:25:59 +00:00
QStandardItem * item = m_model - > itemFromIndex ( sibling_index ) ;
if ( ! item )
return ;
if ( item - > hasChildren ( ) & & index . column ( ) ! = 0 )
{
2024-10-20 15:39:09 +00:00
const QModelIndex view_sibling_index = index . sibling ( index . row ( ) , 0 ) ;
const bool isExpanded = m_ui . cheatList - > isExpanded ( view_sibling_index ) ;
2024-10-20 15:25:59 +00:00
if ( isExpanded )
2024-10-20 15:39:09 +00:00
m_ui . cheatList - > collapse ( view_sibling_index ) ;
2024-10-20 15:25:59 +00:00
else
2024-10-20 15:39:09 +00:00
m_ui . cheatList - > expand ( view_sibling_index ) ;
2024-10-20 15:25:59 +00:00
return ;
}
QVariant data = item - > data ( Qt : : UserRole ) ;
2023-05-25 16:24:01 +00:00
if ( ! data . isValid ( ) )
return ;
std : : string cheat_name = data . toString ( ) . toStdString ( ) ;
2024-10-20 15:25:59 +00:00
const bool new_state = ! ( item - > checkState ( ) = = Qt : : Checked ) ;
item - > setCheckState ( new_state ? Qt : : Checked : Qt : : Unchecked ) ;
2023-05-25 16:24:01 +00:00
setCheatEnabled ( std : : move ( cheat_name ) , new_state , true ) ;
}
2024-10-20 15:25:59 +00:00
void GameCheatSettingsWidget : : onCheatListItemChanged ( QStandardItem * item )
2023-05-25 16:24:01 +00:00
{
2024-10-20 15:25:59 +00:00
QVariant data = item - > data ( Qt : : UserRole ) ;
2023-05-25 16:24:01 +00:00
if ( ! data . isValid ( ) )
return ;
std : : string cheat_name = data . toString ( ) . toStdString ( ) ;
const bool current_enabled =
( std : : find ( m_enabled_patches . begin ( ) , m_enabled_patches . end ( ) , cheat_name ) ! = m_enabled_patches . end ( ) ) ;
2024-10-20 15:25:59 +00:00
const bool current_checked = ( item - > checkState ( ) = = Qt : : Checked ) ;
2023-05-25 16:24:01 +00:00
if ( current_enabled = = current_checked )
return ;
setCheatEnabled ( std : : move ( cheat_name ) , current_checked , true ) ;
}
void GameCheatSettingsWidget : : onReloadClicked ( )
{
reloadList ( ) ;
2024-10-20 15:39:09 +00:00
m_ui . cheatList - > expandAll ( ) ;
2023-05-25 16:24:01 +00:00
// reload it on the emu thread too, so it picks up any changes
g_emu_thread - > reloadPatches ( ) ;
}
void GameCheatSettingsWidget : : updateListEnabled ( )
{
const bool cheats_enabled = m_dialog - > getEffectiveBoolValue ( " EmuCore " , " EnableCheats " , false ) ;
m_ui . cheatList - > setEnabled ( cheats_enabled ) ;
m_ui . enableAll - > setEnabled ( cheats_enabled ) ;
m_ui . disableAll - > setEnabled ( cheats_enabled ) ;
m_ui . reloadCheats - > setEnabled ( cheats_enabled ) ;
2024-01-01 00:23:27 +00:00
m_ui . allCRCsCheckbox - > setEnabled ( cheats_enabled ) ;
2024-10-20 15:39:09 +00:00
m_ui . searchText - > setEnabled ( cheats_enabled ) ;
2023-05-25 16:24:01 +00:00
}
2024-01-01 02:40:14 +00:00
void GameCheatSettingsWidget : : disableAllCheats ( )
{
SettingsInterface * si = m_dialog - > getSettingsInterface ( ) ;
si - > ClearSection ( Patch : : CHEATS_CONFIG_SECTION ) ;
si - > Save ( ) ;
}
2024-01-07 03:05:09 +00:00
void GameCheatSettingsWidget : : resizeEvent ( QResizeEvent * event )
{
QWidget : : resizeEvent ( event ) ;
QtUtils : : ResizeColumnsForTreeView ( m_ui . cheatList , { 320 , 100 , - 1 } ) ;
}
2023-05-25 16:24:01 +00:00
void GameCheatSettingsWidget : : setCheatEnabled ( std : : string name , bool enabled , bool save_and_reload_settings )
{
SettingsInterface * si = m_dialog - > getSettingsInterface ( ) ;
auto it = std : : find ( m_enabled_patches . begin ( ) , m_enabled_patches . end ( ) , name ) ;
if ( enabled )
{
si - > AddToStringList ( Patch : : CHEATS_CONFIG_SECTION , Patch : : PATCH_ENABLE_CONFIG_KEY , name . c_str ( ) ) ;
if ( it = = m_enabled_patches . end ( ) )
m_enabled_patches . push_back ( std : : move ( name ) ) ;
}
else
{
si - > RemoveFromStringList ( Patch : : CHEATS_CONFIG_SECTION , Patch : : PATCH_ENABLE_CONFIG_KEY , name . c_str ( ) ) ;
if ( it ! = m_enabled_patches . end ( ) )
m_enabled_patches . erase ( it ) ;
}
if ( save_and_reload_settings )
{
si - > Save ( ) ;
g_emu_thread - > reloadGameSettings ( ) ;
}
}
void GameCheatSettingsWidget : : setStateForAll ( bool enabled )
{
2024-10-20 15:25:59 +00:00
// Temporarily disconnect from itemChanged to prevent redundant saves
disconnect ( m_model , & QStandardItemModel : : itemChanged , this , & GameCheatSettingsWidget : : onCheatListItemChanged ) ;
2023-05-25 16:24:01 +00:00
setStateRecursively ( nullptr , enabled ) ;
m_dialog - > getSettingsInterface ( ) - > Save ( ) ;
g_emu_thread - > reloadGameSettings ( ) ;
2024-10-20 15:25:59 +00:00
connect ( m_model , & QStandardItemModel : : itemChanged , this , & GameCheatSettingsWidget : : onCheatListItemChanged ) ;
2023-05-25 16:24:01 +00:00
}
2024-10-20 15:25:59 +00:00
void GameCheatSettingsWidget : : setStateRecursively ( QStandardItem * parent , bool enabled )
2023-05-25 16:24:01 +00:00
{
2024-10-20 15:25:59 +00:00
const int count = parent ? parent - > rowCount ( ) : m_model - > rowCount ( ) ;
2023-05-25 16:24:01 +00:00
for ( int i = 0 ; i < count ; i + + )
{
2024-10-20 15:25:59 +00:00
QStandardItem * item = parent ? parent - > child ( i , 0 ) : m_model - > item ( i , 0 ) ;
QVariant data = item - > data ( Qt : : UserRole ) ;
2023-05-25 16:24:01 +00:00
if ( data . isValid ( ) )
{
2024-10-20 15:25:59 +00:00
if ( ( item - > checkState ( ) = = Qt : : Checked ) ! = enabled )
2023-05-25 16:24:01 +00:00
{
2024-10-20 15:25:59 +00:00
item - > setCheckState ( enabled ? Qt : : Checked : Qt : : Unchecked ) ;
2023-05-25 16:24:01 +00:00
setCheatEnabled ( data . toString ( ) . toStdString ( ) , enabled , false ) ;
}
}
else
{
setStateRecursively ( item , enabled ) ;
}
}
}
void GameCheatSettingsWidget : : reloadList ( )
{
u32 num_unlabelled_codes = 0 ;
2024-01-01 00:23:27 +00:00
bool showAllCRCS = m_ui . allCRCsCheckbox - > isChecked ( ) ;
m_patches = Patch : : GetPatchInfo ( m_dialog - > getSerial ( ) , m_dialog - > getDiscCRC ( ) , true , showAllCRCS , & num_unlabelled_codes ) ;
2023-05-25 16:24:01 +00:00
m_enabled_patches =
m_dialog - > getSettingsInterface ( ) - > GetStringList ( Patch : : CHEATS_CONFIG_SECTION , Patch : : PATCH_ENABLE_CONFIG_KEY ) ;
m_parent_map . clear ( ) ;
2024-10-20 15:25:59 +00:00
m_model - > removeRows ( 0 , m_model - > rowCount ( ) ) ;
2023-05-25 16:24:01 +00:00
for ( const Patch : : PatchInfo & pi : m_patches )
{
const bool enabled =
( std : : find ( m_enabled_patches . begin ( ) , m_enabled_patches . end ( ) , pi . name ) ! = m_enabled_patches . end ( ) ) ;
const std : : string_view parent_part = pi . GetNameParentPart ( ) ;
2024-10-20 15:25:59 +00:00
QStandardItem * parent = getTreeViewParent ( parent_part ) ;
QList < QStandardItem * > items = populateTreeViewRow ( pi , enabled ) ;
2023-05-25 16:24:01 +00:00
if ( parent )
2024-10-20 15:25:59 +00:00
parent - > appendRow ( items ) ;
2023-05-25 16:24:01 +00:00
else
2024-10-20 15:25:59 +00:00
m_model - > appendRow ( items ) ;
2023-05-25 16:24:01 +00:00
}
// Hide root indicator when there's no groups, frees up some whitespace.
m_ui . cheatList - > setRootIsDecorated ( ! m_parent_map . empty ( ) ) ;
if ( num_unlabelled_codes > 0 )
{
2024-10-20 15:25:59 +00:00
QStandardItem * item = new QStandardItem ( ) ;
item - > setText ( tr ( " %1 unlabelled patch codes will automatically activate. " ) . arg ( num_unlabelled_codes ) ) ;
m_model - > appendRow ( item ) ;
2023-05-25 16:24:01 +00:00
}
}
2024-10-20 15:25:59 +00:00
QStandardItem * GameCheatSettingsWidget : : getTreeViewParent ( const std : : string_view parent )
2023-05-25 16:24:01 +00:00
{
if ( parent . empty ( ) )
return nullptr ;
2023-07-22 03:15:26 +00:00
auto it = m_parent_map . find ( parent ) ;
2023-05-25 16:24:01 +00:00
if ( it ! = m_parent_map . end ( ) )
return it - > second ;
std : : string_view this_part = parent ;
2024-10-20 15:25:59 +00:00
QStandardItem * parent_to_this = nullptr ;
2023-05-25 16:24:01 +00:00
const std : : string_view : : size_type pos = parent . rfind ( ' \\ ' ) ;
if ( pos ! = std : : string : : npos & & pos ! = ( parent . size ( ) - 1 ) )
{
// go up the chain until we find the real parent, then back down
2024-10-20 15:25:59 +00:00
parent_to_this = getTreeViewParent ( parent . substr ( 0 , pos ) ) ;
2023-05-25 16:24:01 +00:00
this_part = parent . substr ( pos + 1 ) ;
}
2024-10-20 15:25:59 +00:00
QStandardItem * item = new QStandardItem ( ) ;
item - > setText ( QString : : fromUtf8 ( this_part . data ( ) , this_part . length ( ) ) ) ;
2023-05-25 16:24:01 +00:00
if ( parent_to_this )
2024-10-20 15:25:59 +00:00
parent_to_this - > appendRow ( item ) ;
2023-05-25 16:24:01 +00:00
else
2024-10-20 15:25:59 +00:00
m_model - > appendRow ( item ) ;
2023-05-25 16:24:01 +00:00
m_parent_map . emplace ( parent , item ) ;
return item ;
}
2024-10-20 15:25:59 +00:00
QList < QStandardItem * > GameCheatSettingsWidget : : populateTreeViewRow ( const Patch : : PatchInfo & pi , bool enabled )
2023-05-25 16:24:01 +00:00
{
2024-10-20 15:25:59 +00:00
QList < QStandardItem * > items ;
QStandardItem * nameItem = new QStandardItem ( ) ;
2023-05-25 16:24:01 +00:00
const std : : string_view name_part = pi . GetNamePart ( ) ;
2024-10-20 15:25:59 +00:00
nameItem - > setFlags ( Qt : : ItemIsUserCheckable | Qt : : ItemNeverHasChildren | Qt : : ItemIsEnabled ) ;
nameItem - > setCheckState ( enabled ? Qt : : Checked : Qt : : Unchecked ) ;
nameItem - > setData ( QString : : fromStdString ( pi . name ) , Qt : : UserRole ) ;
2023-05-25 16:24:01 +00:00
if ( ! name_part . empty ( ) )
2024-10-20 15:25:59 +00:00
nameItem - > setText ( QString : : fromUtf8 ( name_part . data ( ) , name_part . length ( ) ) ) ;
QStandardItem * authorItem = new QStandardItem ( QString : : fromStdString ( pi . author ) ) ;
QStandardItem * descriptionItem = new QStandardItem ( QString : : fromStdString ( pi . description ) ) ;
items . push_back ( nameItem ) ;
items . push_back ( authorItem ) ;
items . push_back ( descriptionItem ) ;
return items ;
2023-05-25 16:24:01 +00:00
}