|
|
|
@ -1,5 +1,5 @@
|
|
|
|
|
/* PCSX2 - PS2 Emulator for PCs
|
|
|
|
|
* Copyright (C) 2002-2022 PCSX2 Dev Team
|
|
|
|
|
* Copyright (C) 2002-2023 PCSX2 Dev Team
|
|
|
|
|
*
|
|
|
|
|
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
|
|
|
|
* of the GNU Lesser General Public License as published by the Free Software Found-
|
|
|
|
@ -24,16 +24,18 @@
|
|
|
|
|
|
|
|
|
|
#include "common/StringUtil.h"
|
|
|
|
|
|
|
|
|
|
#include "MemoryCardSettingsWidget.h"
|
|
|
|
|
#include "CreateMemoryCardDialog.h"
|
|
|
|
|
#include "QtHost.h"
|
|
|
|
|
#include "MemoryCardConvertDialog.h"
|
|
|
|
|
#include "MemoryCardSettingsWidget.h"
|
|
|
|
|
#include "QtHost.h"
|
|
|
|
|
#include "QtUtils.h"
|
|
|
|
|
#include "SettingWidgetBinder.h"
|
|
|
|
|
#include "SettingsDialog.h"
|
|
|
|
|
|
|
|
|
|
#include "pcsx2/MemoryCardFile.h"
|
|
|
|
|
|
|
|
|
|
static constexpr const char* CONFIG_SECTION = "MemoryCards";
|
|
|
|
|
|
|
|
|
|
static std::string getSlotFilenameKey(u32 slot)
|
|
|
|
|
{
|
|
|
|
|
return StringUtil::StdStringFromFormat("Slot%u_Filename", slot + 1);
|
|
|
|
@ -51,7 +53,8 @@ MemoryCardSettingsWidget::MemoryCardSettingsWidget(SettingsDialog* dialog, QWidg
|
|
|
|
|
// since the group box hasn't been resized at that point.
|
|
|
|
|
m_ui.cardGroupBox->installEventFilter(this);
|
|
|
|
|
|
|
|
|
|
SettingWidgetBinder::BindWidgetToFolderSetting(sif, m_ui.directory, m_ui.browse, m_ui.open, m_ui.reset, "Folders", "MemoryCards", Path::Combine(EmuFolders::DataRoot, "memcards"));
|
|
|
|
|
SettingWidgetBinder::BindWidgetToFolderSetting(sif, m_ui.directory, m_ui.browse, m_ui.open, m_ui.reset, "Folders",
|
|
|
|
|
"MemoryCards", Path::Combine(EmuFolders::DataRoot, "memcards"));
|
|
|
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.autoEject, "EmuCore", "McdEnableEjection", true);
|
|
|
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.automaticManagement, "EmuCore", "McdFolderAutoManage", true);
|
|
|
|
|
|
|
|
|
@ -59,8 +62,10 @@ MemoryCardSettingsWidget::MemoryCardSettingsWidget(SettingsDialog* dialog, QWidg
|
|
|
|
|
|
|
|
|
|
connect(m_ui.directory, &QLineEdit::textChanged, this, &MemoryCardSettingsWidget::refresh);
|
|
|
|
|
m_ui.cardList->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
|
|
|
connect(m_ui.cardList, &MemoryCardListWidget::itemSelectionChanged, this, &MemoryCardSettingsWidget::updateCardActions);
|
|
|
|
|
connect(m_ui.cardList, &MemoryCardListWidget::customContextMenuRequested, this, &MemoryCardSettingsWidget::listContextMenuRequested);
|
|
|
|
|
connect(
|
|
|
|
|
m_ui.cardList, &MemoryCardListWidget::itemSelectionChanged, this, &MemoryCardSettingsWidget::updateCardActions);
|
|
|
|
|
connect(m_ui.cardList, &MemoryCardListWidget::customContextMenuRequested, this,
|
|
|
|
|
&MemoryCardSettingsWidget::listContextMenuRequested);
|
|
|
|
|
|
|
|
|
|
connect(m_ui.refreshCard, &QPushButton::clicked, this, &MemoryCardSettingsWidget::refresh);
|
|
|
|
|
connect(m_ui.createCard, &QPushButton::clicked, this, &MemoryCardSettingsWidget::createCard);
|
|
|
|
@ -74,8 +79,10 @@ MemoryCardSettingsWidget::MemoryCardSettingsWidget(SettingsDialog* dialog, QWidg
|
|
|
|
|
dialog->registerWidgetHelp(m_ui.autoEject, tr("Auto-eject Memory Cards when loading save states"), tr("Checked"),
|
|
|
|
|
tr("Avoids broken Memory Card saves. May not work with some games such as Guitar Hero."));
|
|
|
|
|
|
|
|
|
|
dialog->registerWidgetHelp(m_ui.automaticManagement, tr("Automatically manage saves based on running game"), tr("Checked"),
|
|
|
|
|
tr("(Folder type only / Card size: Auto) Loads only the relevant booted game saves, ignoring others. Avoids running out of space for saves."));
|
|
|
|
|
dialog->registerWidgetHelp(m_ui.automaticManagement, tr("Automatically manage saves based on running game"),
|
|
|
|
|
tr("Checked"),
|
|
|
|
|
tr("(Folder type only / Card size: Auto) Loads only the relevant booted game saves, ignoring others. Avoids "
|
|
|
|
|
"running out of space for saves."));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MemoryCardSettingsWidget::~MemoryCardSettingsWidget() = default;
|
|
|
|
@ -110,21 +117,25 @@ void MemoryCardSettingsWidget::setupAdditionalUi()
|
|
|
|
|
|
|
|
|
|
void MemoryCardSettingsWidget::createSlotWidgets(SlotGroup* port, u32 slot)
|
|
|
|
|
{
|
|
|
|
|
const bool perGame = m_dialog->isPerGameSettings();
|
|
|
|
|
|
|
|
|
|
port->root = new QWidget(m_ui.portGroupBox);
|
|
|
|
|
|
|
|
|
|
SettingsInterface* sif = m_dialog->getSettingsInterface();
|
|
|
|
|
port->enable = new QCheckBox(tr("Port %1").arg(slot + 1), port->root);
|
|
|
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, port->enable, "MemoryCards", StringUtil::StdStringFromFormat("Slot%u_Enable", slot + 1), true);
|
|
|
|
|
SettingWidgetBinder::BindWidgetToBoolSetting(
|
|
|
|
|
sif, port->enable, CONFIG_SECTION, StringUtil::StdStringFromFormat("Slot%u_Enable", slot + 1), true);
|
|
|
|
|
connect(port->enable, &QCheckBox::stateChanged, this, &MemoryCardSettingsWidget::refresh);
|
|
|
|
|
|
|
|
|
|
port->eject = new QToolButton(port->root);
|
|
|
|
|
port->eject->setIcon(QIcon::fromTheme("eject-line"));
|
|
|
|
|
port->eject->setIcon(QIcon::fromTheme(perGame ? QStringLiteral("delete-back-2-line") : QStringLiteral("eject-line")));
|
|
|
|
|
port->eject->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
|
|
|
|
|
port->eject->setToolTip(tr("Eject Memory Card"));
|
|
|
|
|
port->eject->setToolTip(perGame ? tr("Reset") : tr("Eject Memory Card"));
|
|
|
|
|
connect(port->eject, &QToolButton::clicked, this, [this, slot]() { ejectSlot(slot); });
|
|
|
|
|
|
|
|
|
|
port->slot = new MemoryCardSlotWidget(port->root);
|
|
|
|
|
connect(port->slot, &MemoryCardSlotWidget::cardDropped, this, [this, slot](const QString& card) { tryInsertCard(slot, card); });
|
|
|
|
|
connect(port->slot, &MemoryCardSlotWidget::cardDropped, this,
|
|
|
|
|
[this, slot](const QString& card) { tryInsertCard(slot, card); });
|
|
|
|
|
|
|
|
|
|
QHBoxLayout* bottom_layout = new QHBoxLayout();
|
|
|
|
|
bottom_layout->setContentsMargins(0, 0, 0, 0);
|
|
|
|
@ -148,25 +159,28 @@ void MemoryCardSettingsWidget::tryInsertCard(u32 slot, const QString& newCard)
|
|
|
|
|
{
|
|
|
|
|
// handle where the card is dragged in from explorer or something
|
|
|
|
|
const int lastSlashPos = std::max(newCard.lastIndexOf('/'), newCard.lastIndexOf('\\'));
|
|
|
|
|
const std::string newCardStr((lastSlashPos >= 0) ? newCard.mid(0, lastSlashPos).toStdString() : newCard.toStdString());
|
|
|
|
|
const std::string newCardStr(
|
|
|
|
|
(lastSlashPos >= 0) ? newCard.mid(0, lastSlashPos).toStdString() : newCard.toStdString());
|
|
|
|
|
if (newCardStr.empty())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// make sure it's a card in the directory
|
|
|
|
|
const std::vector<AvailableMcdInfo> mcds(FileMcd_GetAvailableCards(true));
|
|
|
|
|
if (std::none_of(mcds.begin(), mcds.end(), [&newCardStr](const AvailableMcdInfo& mcd) { return mcd.name == newCardStr; }))
|
|
|
|
|
if (std::none_of(
|
|
|
|
|
mcds.begin(), mcds.end(), [&newCardStr](const AvailableMcdInfo& mcd) { return mcd.name == newCardStr; }))
|
|
|
|
|
{
|
|
|
|
|
QMessageBox::critical(this, tr("Error"), tr("This Memory Card is unknown."));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_dialog->setStringSettingValue("MemoryCards", getSlotFilenameKey(slot).c_str(), newCardStr.c_str());
|
|
|
|
|
m_dialog->setStringSettingValue(CONFIG_SECTION, getSlotFilenameKey(slot).c_str(), newCardStr.c_str());
|
|
|
|
|
refresh();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MemoryCardSettingsWidget::ejectSlot(u32 slot)
|
|
|
|
|
{
|
|
|
|
|
m_dialog->setStringSettingValue("MemoryCards", getSlotFilenameKey(slot).c_str(), m_dialog->isPerGameSettings() ? nullptr : "");
|
|
|
|
|
m_dialog->setStringSettingValue(CONFIG_SECTION, getSlotFilenameKey(slot).c_str(),
|
|
|
|
|
m_dialog->isPerGameSettings() ? std::nullopt : std::optional<const char*>(""));
|
|
|
|
|
refresh();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -241,15 +255,15 @@ void MemoryCardSettingsWidget::renameCard()
|
|
|
|
|
if (selectedCard.isEmpty())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
const QString newName(QInputDialog::getText(QtUtils::GetRootWidget(this),
|
|
|
|
|
tr("Rename Memory Card"), tr("New Card Name"), QLineEdit::Normal, selectedCard));
|
|
|
|
|
const QString newName(QInputDialog::getText(
|
|
|
|
|
QtUtils::GetRootWidget(this), tr("Rename Memory Card"), tr("New Card Name"), QLineEdit::Normal, selectedCard));
|
|
|
|
|
if (newName.isEmpty() || newName == selectedCard)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (!newName.endsWith(QStringLiteral(".ps2")) || newName.length() <= 4)
|
|
|
|
|
{
|
|
|
|
|
QMessageBox::critical(QtUtils::GetRootWidget(this), tr("Rename Memory Card"),
|
|
|
|
|
tr("New name is invalid, it must end with .ps2"));
|
|
|
|
|
QMessageBox::critical(
|
|
|
|
|
QtUtils::GetRootWidget(this), tr("Rename Memory Card"), tr("New name is invalid, it must end with .ps2"));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -293,8 +307,8 @@ void MemoryCardSettingsWidget::listContextMenuRequested(const QPoint& pos)
|
|
|
|
|
{
|
|
|
|
|
for (u32 slot = 0; slot < MAX_SLOTS; slot++)
|
|
|
|
|
{
|
|
|
|
|
connect(menu.addAction(tr("Use for Port %1").arg(slot + 1)), &QAction::triggered,
|
|
|
|
|
this, [this, &selectedCard, slot]() { tryInsertCard(slot, selectedCard); });
|
|
|
|
|
connect(menu.addAction(tr("Use for Port %1").arg(slot + 1)), &QAction::triggered, this,
|
|
|
|
|
[this, &selectedCard, slot]() { tryInsertCard(slot, selectedCard); });
|
|
|
|
|
}
|
|
|
|
|
menu.addSeparator();
|
|
|
|
|
|
|
|
|
@ -312,14 +326,17 @@ void MemoryCardSettingsWidget::listContextMenuRequested(const QPoint& pos)
|
|
|
|
|
|
|
|
|
|
void MemoryCardSettingsWidget::refresh()
|
|
|
|
|
{
|
|
|
|
|
const bool perGame = m_dialog->isPerGameSettings();
|
|
|
|
|
|
|
|
|
|
for (u32 slot = 0; slot < static_cast<u32>(m_slots.size()); slot++)
|
|
|
|
|
{
|
|
|
|
|
const bool enabled = m_slots[slot].enable->isChecked();
|
|
|
|
|
const std::string slotKey = getSlotFilenameKey(slot);
|
|
|
|
|
const std::optional<std::string> name(
|
|
|
|
|
m_dialog->getStringValue("MemoryCards", getSlotFilenameKey(slot).c_str(),
|
|
|
|
|
FileMcd_GetDefaultName(slot).c_str()));
|
|
|
|
|
m_dialog->getEffectiveStringValue(CONFIG_SECTION, slotKey.c_str(), FileMcd_GetDefaultName(slot).c_str()));
|
|
|
|
|
const bool inherited = perGame ? !m_dialog->containsSettingValue(CONFIG_SECTION, slotKey.c_str()) : false;
|
|
|
|
|
|
|
|
|
|
m_slots[slot].slot->setCard(name);
|
|
|
|
|
m_slots[slot].slot->setCard(name, inherited);
|
|
|
|
|
m_slots[slot].slot->setEnabled(enabled);
|
|
|
|
|
m_slots[slot].eject->setEnabled(enabled);
|
|
|
|
|
}
|
|
|
|
@ -330,19 +347,19 @@ void MemoryCardSettingsWidget::refresh()
|
|
|
|
|
|
|
|
|
|
void MemoryCardSettingsWidget::swapCards()
|
|
|
|
|
{
|
|
|
|
|
const std::string card_1_key(getSlotFilenameKey(0));
|
|
|
|
|
const std::string card_2_key(getSlotFilenameKey(1));
|
|
|
|
|
std::optional<std::string> card_1_name(m_dialog->getStringValue("MemoryCards", card_1_key.c_str(), std::nullopt));
|
|
|
|
|
std::optional<std::string> card_2_name(m_dialog->getStringValue("MemoryCards", card_2_key.c_str(), std::nullopt));
|
|
|
|
|
if (!card_1_name.has_value() || card_1_name->empty() ||
|
|
|
|
|
!card_2_name.has_value() || card_2_name->empty())
|
|
|
|
|
const std::string card1Key = getSlotFilenameKey(0);
|
|
|
|
|
const std::string card2Key = getSlotFilenameKey(1);
|
|
|
|
|
std::optional<std::string> card1Name = m_dialog->getStringValue(CONFIG_SECTION, card1Key.c_str(), std::nullopt);
|
|
|
|
|
std::optional<std::string> card2Name = m_dialog->getStringValue(CONFIG_SECTION, card2Key.c_str(), std::nullopt);
|
|
|
|
|
if (!card1Name.has_value() || card1Name->empty() || !card2Name.has_value() || card2Name->empty())
|
|
|
|
|
{
|
|
|
|
|
QMessageBox::critical(QtUtils::GetRootWidget(this), tr("Error"), tr("Both ports must have a card selected to swap."));
|
|
|
|
|
QMessageBox::critical(
|
|
|
|
|
QtUtils::GetRootWidget(this), tr("Error"), tr("Both ports must have a card selected to swap."));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_dialog->setStringSettingValue("MemoryCards", card_1_key.c_str(), card_2_name->c_str());
|
|
|
|
|
m_dialog->setStringSettingValue("MemoryCards", card_2_key.c_str(), card_1_name->c_str());
|
|
|
|
|
m_dialog->setStringSettingValue(CONFIG_SECTION, card1Key.c_str(), card2Name->c_str());
|
|
|
|
|
m_dialog->setStringSettingValue(CONFIG_SECTION, card2Key.c_str(), card1Name->c_str());
|
|
|
|
|
refresh();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -437,8 +454,8 @@ void MemoryCardListWidget::refresh(SettingsDialog* dialog)
|
|
|
|
|
std::array<std::string, MemoryCardSettingsWidget::MAX_SLOTS> currentCards;
|
|
|
|
|
for (u32 i = 0; i < static_cast<u32>(currentCards.size()); i++)
|
|
|
|
|
{
|
|
|
|
|
const std::optional<std::string> filename = dialog->getStringValue("MemoryCards",
|
|
|
|
|
getSlotFilenameKey(i).c_str(), FileMcd_GetDefaultName(i).c_str());
|
|
|
|
|
const std::optional<std::string> filename = dialog->getEffectiveStringValue(
|
|
|
|
|
CONFIG_SECTION, getSlotFilenameKey(i).c_str(), FileMcd_GetDefaultName(i).c_str());
|
|
|
|
|
if (filename.has_value())
|
|
|
|
|
currentCards[i] = std::move(filename.value());
|
|
|
|
|
}
|
|
|
|
@ -492,7 +509,7 @@ void MemoryCardSlotWidget::dropEvent(QDropEvent* event)
|
|
|
|
|
emit cardDropped(text);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MemoryCardSlotWidget::setCard(const std::optional<std::string>& name)
|
|
|
|
|
void MemoryCardSlotWidget::setCard(const std::optional<std::string>& name, bool inherited)
|
|
|
|
|
{
|
|
|
|
|
clear();
|
|
|
|
|
if (!name.has_value() || name->empty())
|
|
|
|
@ -512,4 +529,12 @@ void MemoryCardSlotWidget::setCard(const std::optional<std::string>& name)
|
|
|
|
|
//: Ignore Crowdin's warning for [Missing], the text should be translated.
|
|
|
|
|
item->setText(tr("%1 [Missing]").arg(QString::fromStdString(name.value())));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (inherited)
|
|
|
|
|
{
|
|
|
|
|
QFont font = item->font();
|
|
|
|
|
font.setItalic(true);
|
|
|
|
|
item->setFont(font);
|
|
|
|
|
item->setForeground(palette().brush(QPalette::Disabled, QPalette::Text));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|