mirror of https://github.com/PCSX2/pcsx2.git
Qt: Add dialog for memory card conversion
This commit is contained in:
parent
ab295f0f10
commit
fb9c38b75f
|
@ -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
|
||||
|
|
|
@ -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.");
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||
<html><head><meta name="qrichtext" content="1" /><meta charset="utf-8" /><style type="text/css">
|
||||
p, li { white-space: pre-wrap; }
|
||||
hr { height: 1px; border-width: 0; }
|
||||
</style></head><body style=" font-family:'Segoe UI'; font-size:9pt; font-weight:400; font-style:normal;">
|
||||
<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Note: Converting a memory card creates a COPY of your existing memory card. It does NOT delete, modify, or replace your existing memory card.</p></body></html></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><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||
<html><head><meta name="qrichtext" content="1" /><meta charset="utf-8" /><style type="text/css">
|
||||
p, li { white-space: pre-wrap; }
|
||||
hr { height: 1px; border-width: 0; }
|
||||
</style></head><body style=" font-family:'Segoe UI'; font-size:9pt; font-weight:400; font-style:normal;">
|
||||
<p align="center" style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html></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>
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
};
|
|
@ -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)
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue