Qt: Add dialog for memory card conversion

This commit is contained in:
RedPanda4552 2022-08-28 17:31:19 -04:00 committed by refractionpcsx2
parent ab295f0f10
commit fb9c38b75f
10 changed files with 815 additions and 3 deletions

View File

@ -96,6 +96,11 @@ target_sources(pcsx2-qt PRIVATE
Settings/InterfaceSettingsWidget.cpp
Settings/InterfaceSettingsWidget.h
Settings/InterfaceSettingsWidget.ui
Settings/MemoryCardConvertDialog.cpp
Settings/MemoryCardConvertDialog.h
Settings/MemoryCardConvertDialog.ui
Settings/MemoryCardConvertWorker.cpp
Settings/MemoryCardConvertWorker.h
Settings/MemoryCardSettingsWidget.cpp
Settings/MemoryCardSettingsWidget.h
Settings/MemoryCardSettingsWidget.ui

View File

@ -0,0 +1,310 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2022 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-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "PrecompiledHeader.h"
#include "MemoryCardConvertDialog.h"
#include <QtWidgets/QMessageBox>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QProgressDialog>
#include "common/Path.h"
#include "common/StringUtil.h"
#include "pcsx2/System.h"
MemoryCardConvertDialog::MemoryCardConvertDialog(QWidget* parent, QString selectedCard)
: QDialog(parent)
{
m_ui.setupUi(this);
m_selectedCard = selectedCard;
std::optional<AvailableMcdInfo> srcCardInfo = FileMcd_GetCardInfo(m_selectedCard.toStdString());
if (srcCardInfo.has_value())
{
m_srcCardInfo = srcCardInfo.value();
}
isSetup = SetupPicklist();
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
m_ui.progressBar->setRange(0, 100);
m_ui.progressBar->setValue(0);
connect(m_ui.conversionTypeSelect, &QComboBox::currentIndexChanged, this, [this]()
{
switch (m_srcCardInfo.type)
{
case MemoryCardType::File:
SetType(MemoryCardType::Folder, MemoryCardFileType::Unknown, "Uses a folder on your PC filesystem, instead of a file. Infinite capacity, while keeping the same compatibility as an 8 MB memory card.");
break;
case MemoryCardType::Folder:
switch (m_ui.conversionTypeSelect->currentData().toInt())
{
case 8:
SetType(MemoryCardType::File, MemoryCardFileType::PS2_8MB, "A standard, 8 MB memory card. Most compatible, but smallest capacity.");
break;
case 16:
SetType(MemoryCardType::File, MemoryCardFileType::PS2_16MB, "2x larger as a standard memory card. May have some compatibility issues.");
break;
case 32:
SetType(MemoryCardType::File, MemoryCardFileType::PS2_32MB, "4x larger than a standard memory card. Likely to have compatibility issues.");
break;
case 64:
SetType(MemoryCardType::File, MemoryCardFileType::PS2_64MB, "8x larger than a standard memory card. Likely to have compatibility issues.");
break;
default:
QMessageBox::critical(this, tr("Convert Memory Card Failed"), tr("Invalid MemoryCardType"));
return;
}
break;
default:
QMessageBox::critical(this, tr("Convert Memory Card Failed"), tr("Invalid MemoryCardType"));
return;
}
}
);
disconnect(m_ui.buttonBox, &QDialogButtonBox::accepted, this, nullptr);
connect(m_ui.buttonBox->button(QDialogButtonBox::Ok), &QPushButton::clicked, this, &MemoryCardConvertDialog::ConvertCard);
connect(m_ui.buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, this, &MemoryCardConvertDialog::close);
}
MemoryCardConvertDialog::~MemoryCardConvertDialog() = default;
bool MemoryCardConvertDialog::IsSetup()
{
return isSetup;
}
void MemoryCardConvertDialog::onStatusUpdated()
{
}
void MemoryCardConvertDialog::onProgressUpdated(int value, int range)
{
m_ui.progressBar->setRange(0, range);
m_ui.progressBar->setValue(value);
}
void MemoryCardConvertDialog::onThreadFinished()
{
QMessageBox::information(this, tr("Conversion Complete"), tr("Memory card \"%1\" converted to \"%2\"").arg(m_selectedCard).arg(m_destCardName));
accept();
}
void MemoryCardConvertDialog::StartThread()
{
m_thread = std::make_unique<MemoryCardConvertWorker>(this, m_srcCardInfo.type, m_fileType, m_selectedCard.toStdString(), m_destCardName.toStdString());
connect(m_thread.get(), &MemoryCardConvertWorker::statusUpdated, this, &MemoryCardConvertDialog::onStatusUpdated);
connect(m_thread.get(), &MemoryCardConvertWorker::progressUpdated, this, &MemoryCardConvertDialog::onProgressUpdated);
connect(m_thread.get(), &MemoryCardConvertWorker::threadFinished, this, &MemoryCardConvertDialog::onThreadFinished);
m_thread->start();
UpdateEnabled();
}
void MemoryCardConvertDialog::CancelThread()
{
if (!m_thread)
{
return;
}
m_thread->requestInterruption();
m_thread->join();
m_thread.reset();
}
void MemoryCardConvertDialog::UpdateEnabled()
{
if (m_thread)
{
m_ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
m_ui.buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(false);
}
}
bool MemoryCardConvertDialog::SetupPicklist()
{
FileSystem::FindResultsArray rootDir;
size_t sizeBytes = 0;
bool typeSet = false;
m_ui.conversionTypeSelect->clear();
switch (m_srcCardInfo.type)
{
case MemoryCardType::File:
m_ui.conversionTypeSelect->addItems({"Folder"});
SetType(MemoryCardType::Folder, MemoryCardFileType::Unknown, "Uses a folder on your PC filesystem, instead of a file. Infinite capacity, while keeping the same compatibility as an 8 MB memory card.");
break;
case MemoryCardType::Folder:
// Compute which file types should be allowed.
FileSystem::FindFiles(m_srcCardInfo.path.c_str(), "*", FLAGS, &rootDir);
for (auto dirEntry : rootDir)
{
const std::string_view fileName = Path::GetFileName(dirEntry.FileName);
if (fileName.size() >= 7 && fileName.substr(0, 7).compare("_pcsx2_") == 0)
{
continue;
}
else if (dirEntry.Attributes & FILESYSTEM_FILE_ATTRIBUTE_DIRECTORY)
{
sizeBytes += 512;
}
else
{
size_t toAdd = static_cast<size_t>(dirEntry.Size + (1024 - (dirEntry.Size % 1024)));
sizeBytes += toAdd + 512; // The file content needs to be added, PLUS a directory entry
}
}
// Finally, round up to the nearest erase block.
sizeBytes += (512 * 16) - (sizeBytes % (512 * 16));
if (sizeBytes < CardCapacity::_8_MB)
{
m_ui.conversionTypeSelect->addItem("8 MB File", 8);
if (!typeSet)
{
SetType_8();
typeSet = true;
}
}
if (sizeBytes < CardCapacity::_16_MB)
{
m_ui.conversionTypeSelect->addItem("16 MB File", 16);
if (!typeSet)
{
SetType_16();
typeSet = true;
}
}
if (sizeBytes < CardCapacity::_32_MB)
{
m_ui.conversionTypeSelect->addItem("32 MB File", 32);
if (!typeSet)
{
SetType_32();
typeSet = true;
}
}
if (sizeBytes < CardCapacity::_64_MB)
{
m_ui.conversionTypeSelect->addItem("64 MB File", 64);
if (!typeSet)
{
SetType_64();
typeSet = true;
}
}
if (!typeSet)
{
QMessageBox::critical(this, tr("Cannot Convert Memory Card"), tr("Your folder memory card has too much data inside it to be converted to a file memory card. The largest supported file memory card has a capacity of 64 MB. To convert your folder memory card, you must remove game folders until its size is 64 MB or less."));
return false;
}
break;
default:
QMessageBox::critical(this, tr("Convert Memory Card Failed"), tr("Invalid MemoryCardType"));
return false;
}
return true;
}
void MemoryCardConvertDialog::ConvertCard()
{
if (m_thread)
{
CancelThread();
}
else
{
QString baseName = m_selectedCard;
// Get our destination file name
size_t extensionPos = baseName.lastIndexOf(".ps2", -1);
// Strip the extension off of it
baseName.replace(extensionPos, 4, "");
// Add _converted to the end of it
baseName.append("_converted");
size_t num = 0;
QString destName = baseName;
destName.append(".ps2");
// If a match is found, revert back to the base name, add a number and the extension, and try again.
// Keep incrementing the number until we get a unique result.
while (m_srcCardInfo.type == MemoryCardType::File ? FileSystem::DirectoryExists(Path::Combine(EmuFolders::MemoryCards, destName.toStdString()).c_str()) : FileSystem::FileExists(Path::Combine(EmuFolders::MemoryCards, destName.toStdString()).c_str()))
{
destName = baseName;
destName.append(StringUtil::StdStringFromFormat("_%02d.ps2", ++num).c_str());
}
m_destCardName = destName;
StartThread();
}
}
void MemoryCardConvertDialog::ConvertCallback()
{
Console.WriteLn("%s() Finished", __FUNCTION__);
}
void MemoryCardConvertDialog::SetType(MemoryCardType type, MemoryCardFileType fileType, const QString& description)
{
m_type = type;
m_fileType = fileType;
m_ui.conversionTypeDescription->setPlainText(description);
}
void MemoryCardConvertDialog::SetType_8()
{
SetType(MemoryCardType::File, MemoryCardFileType::PS2_8MB, "A standard, 8 MB memory card. Most compatible, but smallest capacity.");
}
void MemoryCardConvertDialog::SetType_16()
{
SetType(MemoryCardType::File, MemoryCardFileType::PS2_16MB, "2x larger as a standard memory card. May have some compatibility issues.");
}
void MemoryCardConvertDialog::SetType_32()
{
SetType(MemoryCardType::File, MemoryCardFileType::PS2_32MB, "4x larger than a standard memory card. Likely to have compatibility issues.");
}
void MemoryCardConvertDialog::SetType_64()
{
SetType(MemoryCardType::File, MemoryCardFileType::PS2_64MB, "8x larger than a standard memory card. Likely to have compatibility issues.");
}
void MemoryCardConvertDialog::SetType_Folder()
{
SetType(MemoryCardType::Folder, MemoryCardFileType::Unknown, "Uses a folder on your PC filesystem, instead of a file. Infinite capacity, while keeping the same compatibility as an 8 MB memory card.");
}

View File

@ -0,0 +1,78 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2022 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-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <QtWidgets/QDialog>
#include "common/FileSystem.h"
#include "ui_MemoryCardConvertDialog.h"
#include "MemoryCardConvertWorker.h"
#include "pcsx2/MemoryCardFile.h"
class MemoryCardConvertDialog final : public QDialog
{
Q_OBJECT
public:
explicit MemoryCardConvertDialog(QWidget* parent, QString selectedCard);
~MemoryCardConvertDialog();
bool IsSetup();
void onStatusUpdated();
void onProgressUpdated(int value, int range);
void onThreadFinished();
private Q_SLOTS:
void ConvertCard();
void ConvertCallback();
private:
void StartThread();
void CancelThread();
void UpdateEnabled();
bool SetupPicklist();
void SetType(MemoryCardType type, MemoryCardFileType fileType, const QString& description);
void SetType_8();
void SetType_16();
void SetType_32();
void SetType_64();
void SetType_Folder();
Ui::MemoryCardConvertDialog m_ui;
bool isSetup = false;
AvailableMcdInfo m_srcCardInfo;
QString m_selectedCard;
QString m_destCardName;
MemoryCardType m_type = MemoryCardType::File;
MemoryCardFileType m_fileType = MemoryCardFileType::PS2_8MB;
std::unique_ptr<MemoryCardConvertWorker> m_thread;
static constexpr u32 FLAGS = FILESYSTEM_FIND_RECURSIVE | FILESYSTEM_FIND_FOLDERS | FILESYSTEM_FIND_FILES;
};
// Card capacities computed from freshly formatted superblocks.
namespace CardCapacity
{
static constexpr size_t _8_MB = 0x1f40 * 512 * 2; //(0x1fc7 - 0x29) * 2 * 512;
static constexpr size_t _16_MB = 0x3e80 * 512 * 2; //(0x3fa7 - 0x49) * 2 * 512;
static constexpr size_t _32_MB = 0x7d00 * 512 * 2; //(0x7f67 - 0x89) * 2 * 512;
static constexpr size_t _64_MB = 0xfde8 * 512 * 2; //(0xfee7 - 0x0109) * 2 * 512;
}

View File

@ -0,0 +1,181 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MemoryCardConvertDialog</class>
<widget class="QDialog" name="MemoryCardConvertDialog">
<property name="enabled">
<bool>true</bool>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>440</width>
<height>320</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>440</width>
<height>320</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>440</width>
<height>320</height>
</size>
</property>
<property name="windowTitle">
<string>Convert Memory Card</string>
</property>
<property name="autoFillBackground">
<bool>false</bool>
</property>
<property name="modal">
<bool>false</bool>
</property>
<widget class="QGroupBox" name="conversionTypeGroup">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>421</width>
<height>61</height>
</rect>
</property>
<property name="title">
<string>Conversion Type</string>
</property>
<widget class="QComboBox" name="conversionTypeSelect">
<property name="geometry">
<rect>
<x>10</x>
<y>30</y>
<width>401</width>
<height>22</height>
</rect>
</property>
<item>
<property name="text">
<string>8 MB File</string>
</property>
</item>
<item>
<property name="text">
<string>16 MB File</string>
</property>
</item>
<item>
<property name="text">
<string>32 MB File</string>
</property>
</item>
<item>
<property name="text">
<string>64 MB File</string>
</property>
</item>
<item>
<property name="text">
<string>Folder</string>
</property>
</item>
</widget>
</widget>
<widget class="QTextBrowser" name="note">
<property name="geometry">
<rect>
<x>10</x>
<y>150</y>
<width>421</width>
<height>61</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="html">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;meta charset=&quot;utf-8&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
hr { height: 1px; border-width: 0; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Segoe UI'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Note: Converting a memory card creates a COPY of your existing memory card. It does NOT delete, modify, or replace your existing memory card.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
<widget class="QTextBrowser" name="conversionTypeDescription">
<property name="geometry">
<rect>
<x>10</x>
<y>80</y>
<width>421</width>
<height>61</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="html">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;meta charset=&quot;utf-8&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
hr { height: 1px; border-width: 0; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Segoe UI'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p align=&quot;center&quot; style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="geometry">
<rect>
<x>270</x>
<y>290</y>
<width>156</width>
<height>24</height>
</rect>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
<widget class="QGroupBox" name="progressGroup">
<property name="geometry">
<rect>
<x>10</x>
<y>220</y>
<width>421</width>
<height>61</height>
</rect>
</property>
<property name="title">
<string>Progress</string>
</property>
<widget class="QProgressBar" name="progressBar">
<property name="geometry">
<rect>
<x>10</x>
<y>30</y>
<width>401</width>
<height>23</height>
</rect>
</property>
<property name="value">
<number>24</number>
</property>
</widget>
</widget>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,165 @@
#include "PrecompiledHeader.h"
#include "MemoryCardConvertWorker.h"
#include "common/Path.h"
#include "common/FileSystem.h"
MemoryCardConvertWorker::MemoryCardConvertWorker(QWidget* parent, MemoryCardType type, MemoryCardFileType fileType, const std::string& srcFileName, const std::string& destFileName)
: QtAsyncProgressThread(parent)
{
this->type = type;
this->fileType = fileType;
this->srcFileName = srcFileName;
this->destFileName = destFileName;
}
MemoryCardConvertWorker::~MemoryCardConvertWorker() = default;
void MemoryCardConvertWorker::runAsync()
{
switch (type)
{
case MemoryCardType::File:
ConvertToFolder(srcFileName, destFileName, fileType);
break;
case MemoryCardType::Folder:
ConvertToFile(srcFileName, destFileName, fileType);
break;
default:
break;
}
}
bool MemoryCardConvertWorker::ConvertToFile(const std::string& srcFolderName, const std::string& destFileName, const MemoryCardFileType type)
{
const std::string srcPath(Path::Combine(EmuFolders::MemoryCards, srcFolderName));
const std::string destPath(Path::Combine(EmuFolders::MemoryCards, destFileName));
size_t sizeInMB = 0;
switch (type)
{
case MemoryCardFileType::PS2_8MB:
sizeInMB = 8;
break;
case MemoryCardFileType::PS2_16MB:
sizeInMB = 16;
break;
case MemoryCardFileType::PS2_32MB:
sizeInMB = 32;
break;
case MemoryCardFileType::PS2_64MB:
sizeInMB = 64;
break;
default:
Console.Error("%s(%s, %s, %d) Received invalid MemoryCardFileType, aborting", __FUNCTION__, srcPath.c_str(), destPath.c_str(), type);
return false;
}
FolderMemoryCard sourceFolderMemoryCard;
Pcsx2Config::McdOptions config;
config.Enabled = true;
config.Type = MemoryCardType::Folder;
sourceFolderMemoryCard.Open(srcPath, config, (sizeInMB * 1024 * 1024) / FolderMemoryCard::ClusterSize, false, "");
const size_t capacity = sourceFolderMemoryCard.GetSizeInClusters() * FolderMemoryCard::ClusterSizeRaw;
std::vector<u8> sourceBuffer;
sourceBuffer.resize(capacity);
size_t address = 0;
this->SetProgressRange(capacity);
this->SetProgressValue(0);
while (address < capacity)
{
sourceFolderMemoryCard.Read(sourceBuffer.data() + address, address, FolderMemoryCard::PageSizeRaw);
address += FolderMemoryCard::PageSizeRaw;
// Only report progress every 16 pages. Substantially speeds up the conversion.
if (address % (FolderMemoryCard::PageSizeRaw * 16) == 0)
this->SetProgressValue(address);
}
bool writeResult = FileSystem::WriteBinaryFile(destPath.c_str(), sourceBuffer.data(), sourceBuffer.size());
if (!writeResult)
{
Console.Error("%s(%s, %s, %d) Failed to write memory card contents to file", __FUNCTION__, srcPath.c_str(), destPath.c_str(), type);
return false;
}
#ifdef _WIN32
else
{
FileSystem::SetPathCompression(destPath.c_str(), true);
}
#endif
sourceFolderMemoryCard.Close(false);
return true;
}
bool MemoryCardConvertWorker::ConvertToFolder(const std::string& srcFileName, const std::string& destFolderName, const MemoryCardFileType type)
{
const std::string srcPath(Path::Combine(EmuFolders::MemoryCards, srcFileName));
const std::string destPath(Path::Combine(EmuFolders::MemoryCards, destFolderName));
u8 buffer[FolderMemoryCard::PageSizeRaw];
FolderMemoryCard targetFolderMemoryCard;
Pcsx2Config::McdOptions config;
config.Enabled = true;
config.Type = MemoryCardType::Folder;
std::optional<std::vector<u8>> sourceBufferOpt = FileSystem::ReadBinaryFile(srcPath.c_str());
if (!sourceBufferOpt.has_value())
{
Console.Error("%s(%s, %s, %d) Failed to open file memory card!", __FUNCTION__, srcFileName.c_str(), destFolderName.c_str(), type);
return false;
}
std::vector<u8> sourceBuffer = sourceBufferOpt.value();
// Set progress bar to the literal number of bytes in the memcard.
// Plus two because there is a lag period after the Save calls complete
// where the progress bar stalls out; this lets us stop the progress bar
// just shy of 50 and 100% so it seems like its still doing some work.
this->SetProgressRange((sourceBuffer.size() * 2) + 2);
this->SetProgressValue(0);
// Attempt the write twice. Once with writes being simulated rather than truly committed.
// Again with actual writes. If a file memcard has a corrupted page or something which would
// cause the conversion to fail, it will fail on the simulated run, with no files committed
// to the filesystem yet.
for (int i = 0; i < 2; i++)
{
bool simulateWrites = (i == 0);
targetFolderMemoryCard.Open(destPath, config, 0, false, "", simulateWrites);
size_t address = 0;
while (address < sourceBuffer.size())
{
targetFolderMemoryCard.Save(sourceBuffer.data() + address, address, FolderMemoryCard::PageSizeRaw);
address += FolderMemoryCard::PageSizeRaw;
// Only report progress every 16 pages. Substantially speeds up the conversion.
if (address % (FolderMemoryCard::PageSizeRaw * 16) == 0)
this->SetProgressValue(address + (i * sourceBuffer.size()));
}
targetFolderMemoryCard.Close();
// If the source file memory card was larger than 8 MB, the raw copy will have also made the superblock of
// the destination folder memory card larger than 8 MB. For compatibility, we always want folder memory cards
// to report 8 MB, so we'll override that here. Don't do this on the simulated run, only the actual.
if (!simulateWrites && sourceBuffer.size() != FolderMemoryCard::TotalSizeRaw)
{
targetFolderMemoryCard.Open(destPath, config, 0, false, "", simulateWrites);
targetFolderMemoryCard.SetSizeInMB(8);
targetFolderMemoryCard.Close();
}
this->IncrementProgressValue();
}
return true;
}

View File

@ -0,0 +1,31 @@
#pragma once
#include <QtCore/QtCore>
#include <QtWidgets/QMessageBox>
#include <QtWidgets/QProgressDialog>
#include "QtProgressCallback.h"
#include "pcsx2/MemoryCardFile.h"
#include "pcsx2/MemoryCardFolder.h"
class MemoryCardConvertWorker : public QtAsyncProgressThread
{
public:
MemoryCardConvertWorker(QWidget* parent, MemoryCardType type, MemoryCardFileType fileType, const std::string& srcFileName, const std::string& destFileName);
~MemoryCardConvertWorker();
protected:
void runAsync() override;
private:
MemoryCardType type;
MemoryCardFileType fileType;
std::string srcFileName;
std::string destFileName;
bool ConvertToFile(const std::string& srcFolderName, const std::string& destFileName, const MemoryCardFileType type);
bool ConvertToFolder(const std::string& srcFolderName, const std::string& destFileName, const MemoryCardFileType type);
};

View File

@ -27,6 +27,7 @@
#include "MemoryCardSettingsWidget.h"
#include "CreateMemoryCardDialog.h"
#include "QtHost.h"
#include "MemoryCardConvertDialog.h"
#include "QtUtils.h"
#include "SettingWidgetBinder.h"
#include "SettingsDialog.h"
@ -183,9 +184,13 @@ QString MemoryCardSettingsWidget::getSelectedCard() const
void MemoryCardSettingsWidget::updateCardActions()
{
const bool hasSelection = !getSelectedCard().isEmpty();
QString selectedCard = getSelectedCard();
const bool hasSelection = !selectedCard.isEmpty();
std::optional<AvailableMcdInfo> cardInfo = FileMcd_GetCardInfo(selectedCard.toStdString());
bool isPS1 = (cardInfo.has_value() ? cardInfo.value().file_type == MemoryCardFileType::PS1 : false);
m_ui.convertCard->setEnabled(hasSelection);
m_ui.convertCard->setEnabled(hasSelection && !isPS1);
m_ui.duplicateCard->setEnabled(hasSelection);
m_ui.renameCard->setEnabled(hasSelection);
m_ui.deleteCard->setEnabled(hasSelection);
@ -263,10 +268,14 @@ void MemoryCardSettingsWidget::renameCard()
void MemoryCardSettingsWidget::convertCard()
{
const QString selectedCard(getSelectedCard());
if (selectedCard.isEmpty())
return;
QMessageBox::critical(this, tr("Error"), tr("Not yet implemented."));
MemoryCardConvertDialog dialog(QtUtils::GetRootWidget(this), selectedCard);
if (dialog.IsSetup() && dialog.exec() == QDialog::Accepted)
refresh();
}
void MemoryCardSettingsWidget::listContextMenuRequested(const QPoint& pos)

View File

@ -135,6 +135,7 @@
<ClCompile Include="EarlyHardwareCheck.cpp" />
<ClCompile Include="QtProgressCallback.cpp" />
<ClCompile Include="Settings\FolderSettingsWidget.cpp" />
<ClCompile Include="Settings\MemoryCardConvertWorker.cpp" />
<ClCompile Include="Tools\InputRecording\NewInputRecordingDlg.cpp" />
<ClCompile Include="Settings\BIOSSettingsWidget.cpp" />
<ClCompile Include="Settings\ControllerBindingWidgets.cpp" />
@ -153,6 +154,7 @@
<ClCompile Include="Settings\InputBindingWidget.cpp" />
<ClCompile Include="Settings\ControllerSettingsDialog.cpp" />
<ClCompile Include="Settings\AudioSettingsWidget.cpp" />
<ClCompile Include="Settings\MemoryCardConvertDialog.cpp" />
<ClCompile Include="Settings\MemoryCardSettingsWidget.cpp" />
<ClCompile Include="Settings\DEV9DnsHostDialog.cpp" />
<ClCompile Include="Settings\DEV9SettingsWidget.cpp" />
@ -191,6 +193,7 @@
<QtMoc Include="Settings\InputBindingWidget.h" />
<QtMoc Include="Settings\ControllerSettingsDialog.h" />
<QtMoc Include="Settings\AudioSettingsWidget.h" />
<QtMoc Include="Settings\MemoryCardConvertDialog.h" />
<QtMoc Include="Settings\MemoryCardSettingsWidget.h" />
<QtMoc Include="Settings\DEV9DnsHostDialog.h" />
<QtMoc Include="Settings\DEV9SettingsWidget.h" />
@ -210,6 +213,7 @@
<ClInclude Include="QtUtils.h" />
<QtMoc Include="Settings\ControllerBindingWidgets.h" />
<QtMoc Include="Settings\ControllerGlobalSettingsWidget.h" />
<ClInclude Include="Settings\MemoryCardConvertWorker.h" />
<ClInclude Include="SettingWidgetBinder.h" />
<QtMoc Include="QtHost.h" />
<ClInclude Include="PrecompiledHeader.h" />
@ -242,6 +246,7 @@
<ClCompile Include="$(IntDir)Settings\moc_InputBindingDialog.cpp" />
<ClCompile Include="$(IntDir)Settings\moc_InputBindingWidget.cpp" />
<ClCompile Include="$(IntDir)Settings\moc_AudioSettingsWidget.cpp" />
<ClCompile Include="$(IntDir)Settings\moc_MemoryCardConvertDialog.cpp" />
<ClCompile Include="$(IntDir)Settings\moc_MemoryCardSettingsWidget.cpp" />
<ClCompile Include="$(IntDir)Settings\moc_FolderSettingsWidget.cpp" />
<ClCompile Include="$(IntDir)Settings\moc_DEV9DnsHostDialog.cpp" />
@ -323,6 +328,9 @@
<QtUi Include="Settings\AudioSettingsWidget.ui">
<FileType>Document</FileType>
</QtUi>
<QtUi Include="Settings\MemoryCardConvertDialog.ui">
<FileType>Document</FileType>
</QtUi>
<QtUi Include="Settings\MemoryCardSettingsWidget.ui">
<FileType>Document</FileType>
</QtUi>

View File

@ -156,6 +156,9 @@
<ClCompile Include="Settings\ControllerGlobalSettingsWidget.cpp">
<Filter>Settings</Filter>
</ClCompile>
<ClCompile Include="$(IntDir)Settings\moc_MemoryCardConvertDialog.cpp">
<Filter>moc</Filter>
</ClCompile>
<ClCompile Include="$(IntDir)Settings\moc_MemoryCardSettingsWidget.cpp">
<Filter>moc</Filter>
</ClCompile>
@ -180,6 +183,9 @@
<ClCompile Include="Settings\GameSummaryWidget.cpp">
<Filter>Settings</Filter>
</ClCompile>
<ClCompile Include="Settings\MemoryCardConvertDialog.cpp">
<Filter>Settings</Filter>
</ClCompile>
<ClCompile Include="Settings\MemoryCardSettingsWidget.cpp">
<Filter>Settings</Filter>
</ClCompile>
@ -241,6 +247,9 @@
<ClCompile Include="$(IntDir)Settings\moc_AchievementLoginDialog.cpp">
<Filter>moc</Filter>
</ClCompile>
<ClCompile Include="Settings\MemoryCardConvertWorker.cpp">
<Filter>Settings</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Manifest Include="..\pcsx2\windows\PCSX2.manifest">
@ -257,6 +266,9 @@
<ClInclude Include="Settings\ControllerSettingWidgetBinder.h">
<Filter>Settings</Filter>
</ClInclude>
<ClInclude Include="Settings\MemoryCardConvertWorker.h">
<Filter>Settings</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<QtMoc Include="MainWindow.h" />
@ -319,6 +331,9 @@
<QtMoc Include="Settings\GameSummaryWidget.h">
<Filter>Settings</Filter>
</QtMoc>
<QtMoc Include="Settings\MemoryCardConvertDialog.h">
<Filter>Settings</Filter>
</QtMoc>
<QtMoc Include="Settings\MemoryCardSettingsWidget.h">
<Filter>Settings</Filter>
</QtMoc>
@ -406,6 +421,9 @@
<QtUi Include="Settings\GameSummaryWidget.ui">
<Filter>Settings</Filter>
</QtUi>
<QtUi Include="Settings\MemoryCardConvertDialog.ui">
<Filter>Settings</Filter>
</QtUi>
<QtUi Include="Settings\MemoryCardSettingsWidget.ui">
<Filter>Settings</Filter>
</QtUi>

View File

@ -290,6 +290,7 @@ void FolderMemoryCard::LoadMemoryCardData(const u32 sizeInClusters, const bool e
MemoryCardFileEntry* const rootDirEntry = &m_fileEntryDict[m_superBlock.data.rootdir_cluster].entries[0];
AddFolder(rootDirEntry, m_folderName, nullptr, enableFiltering, filter);
#ifdef DEBUG_WRITE_FOLDER_CARD_IN_MEMORY_TO_FILE_ON_CHANGE
WriteToFile(m_folderName.GetFullPath().RemoveLast() + L"-debug_" + wxDateTime::Now().Format(L"%Y-%m-%d-%H-%M-%S") + L"_load.ps2");
#endif
@ -2281,6 +2282,12 @@ void MemoryCardFileMetadataReference::GetInternalPath(std::string* fileName) con
FolderMemoryCardAggregator::FolderMemoryCardAggregator()
{
#ifdef _WIN32
// Override Windows' default allowance for open files. Folder memory cards with more than 32 MB of content are likely to contain more than 512 individual files.
// Unix platforms seem to use 1024 by default.
_setmaxstdio(1024);
#endif
for (uint i = 0; i < TotalCardSlots; ++i)
{
m_cards[i].SetSlot(i);