summaryrefslogtreecommitdiff
path: root/Source
diff options
context:
space:
mode:
authorspycrab <spycrab@users.noreply.github.com>2017-08-25 20:32:37 +0200
committerspycrab <spycrab@users.noreply.github.com>2017-08-26 14:56:33 +0200
commit8b346b705878ddbdca28565a45daddcbe49b2508 (patch)
tree508482c6d7c9af11797a9457847f2ebab21e91a2 /Source
parent2630fa0ce96e561feb2194fa8e827d7778644976 (diff)
Qt/GameList: Implement "Filesystem" tab
Diffstat (limited to 'Source')
-rw-r--r--Source/Core/DolphinQt2/Config/FilesystemWidget.cpp297
-rw-r--r--Source/Core/DolphinQt2/Config/FilesystemWidget.h31
2 files changed, 327 insertions, 1 deletions
diff --git a/Source/Core/DolphinQt2/Config/FilesystemWidget.cpp b/Source/Core/DolphinQt2/Config/FilesystemWidget.cpp
index 935d0617a2..7db79c9392 100644
--- a/Source/Core/DolphinQt2/Config/FilesystemWidget.cpp
+++ b/Source/Core/DolphinQt2/Config/FilesystemWidget.cpp
@@ -4,6 +4,301 @@
#include "DolphinQt2/Config/FilesystemWidget.h"
-FilesystemWidget::FilesystemWidget(const GameFile& game) : m_game(game)
+#include <QApplication>
+#include <QCoreApplication>
+#include <QFileDialog>
+#include <QHeaderView>
+#include <QMenu>
+#include <QMessageBox>
+#include <QProgressDialog>
+#include <QStandardItemModel>
+#include <QStyleFactory>
+#include <QTreeView>
+#include <QVBoxLayout>
+
+#include <future>
+
+#include "DiscIO/DiscExtractor.h"
+#include "DiscIO/Enums.h"
+#include "DiscIO/Filesystem.h"
+#include "DolphinQt2/Resources.h"
+
+constexpr int ENTRY_PARTITION = Qt::UserRole;
+constexpr int ENTRY_NAME = Qt::UserRole + 1;
+constexpr int ENTRY_TYPE = Qt::UserRole + 2;
+
+enum class EntryType
+{
+ Disc = -2,
+ Partition = -1,
+ File = 0,
+ Dir = 1
+};
+Q_DECLARE_METATYPE(EntryType);
+
+FilesystemWidget::FilesystemWidget(const GameFile& game)
+ : m_game(game), m_volume(DiscIO::CreateVolumeFromFilename(game.GetFilePath().toStdString()))
+{
+ CreateWidgets();
+ ConnectWidgets();
+ PopulateView();
+}
+
+void FilesystemWidget::CreateWidgets()
+{
+ auto* layout = new QVBoxLayout;
+
+ m_tree_model = new QStandardItemModel(0, 1);
+ m_tree_view = new QTreeView(this);
+ m_tree_view->setModel(m_tree_model);
+ m_tree_view->setContextMenuPolicy(Qt::CustomContextMenu);
+ m_tree_view->header()->hide();
+
+// Windows: Set style to (old) windows, which draws branch lines
+#ifdef _WIN32
+ if (QApplication::style()->objectName() == QStringLiteral("windowsvista"))
+ m_tree_view->setStyle(QStyleFactory::create(QStringLiteral("windows")));
+#endif
+
+ layout->addWidget(m_tree_view);
+
+ setLayout(layout);
+}
+
+void FilesystemWidget::ConnectWidgets()
+{
+ connect(m_tree_view, &QTreeView::customContextMenuRequested, this,
+ &FilesystemWidget::ShowContextMenu);
+}
+
+void FilesystemWidget::PopulateView()
+{
+ auto* disc = new QStandardItem(tr("Disc"));
+ disc->setEditable(false);
+ disc->setIcon(Resources::GetScaledIcon("isoproperties_disc"));
+ disc->setData(QVariant::fromValue(EntryType::Disc), ENTRY_TYPE);
+ m_tree_model->appendRow(disc);
+ m_tree_view->expand(disc->index());
+
+ const auto& partitions = m_volume->GetPartitions();
+
+ for (size_t i = 0; i < partitions.size(); i++)
+ {
+ std::unique_ptr<DiscIO::FileSystem> file_system(
+ DiscIO::CreateFileSystem(m_volume.get(), partitions[i]));
+
+ auto* item = new QStandardItem(tr("Partition %1").arg(i));
+ item->setEditable(false);
+
+ item->setIcon(Resources::GetScaledIcon("isoproperties_disc"));
+ item->setData(static_cast<qlonglong>(i), ENTRY_PARTITION);
+ item->setData(QVariant::fromValue(EntryType::Partition), ENTRY_TYPE);
+
+ PopulateDirectory(static_cast<int>(i), item, file_system->GetRoot());
+
+ disc->appendRow(item);
+
+ if (m_volume->GetGamePartition() == partitions[i])
+ m_tree_view->expand(item->index());
+ }
+
+ if (partitions.empty())
+ {
+ PopulateDirectory(-1, disc,
+ DiscIO::CreateFileSystem(m_volume.get(), DiscIO::PARTITION_NONE)->GetRoot());
+ }
+}
+
+void FilesystemWidget::PopulateDirectory(int partition_id, QStandardItem* root,
+ const DiscIO::FileInfo& directory)
+{
+ for (const auto& info : directory)
+ {
+ auto* item = new QStandardItem(QString::fromStdString(info.GetName()));
+ item->setEditable(false);
+ item->setIcon(Resources::GetScaledIcon(info.IsDirectory() ? "isoproperties_folder" :
+ "isoproperties_file"));
+
+ if (info.IsDirectory())
+ PopulateDirectory(partition_id, item, info);
+
+ item->setData(partition_id, ENTRY_PARTITION);
+ item->setData(QString::fromStdString(info.GetPath()), ENTRY_NAME);
+ item->setData(QVariant::fromValue(info.IsDirectory() ? EntryType::Dir : EntryType::File),
+ ENTRY_TYPE);
+ root->appendRow(item);
+ }
+}
+
+static QString SelectFolder()
+{
+ return QFileDialog::getExistingDirectory(nullptr, QObject::tr("Choose the folder to extract to"));
+}
+
+void FilesystemWidget::ShowContextMenu(const QPoint&)
+{
+ auto* selection = m_tree_view->selectionModel();
+ if (!selection->hasSelection())
+ return;
+
+ auto* item = m_tree_model->itemFromIndex(selection->selectedIndexes()[0]);
+
+ QMenu* menu = new QMenu(this);
+
+ DiscIO::Partition partition = GetPartitionFromID(item->data(ENTRY_PARTITION).toInt());
+ QString path = item->data(ENTRY_NAME).toString();
+
+ EntryType type = item->data(ENTRY_TYPE).value<EntryType>();
+
+ if ((type == EntryType::Disc && m_volume->GetPartitions().empty()) ||
+ type == EntryType::Partition)
+ {
+ menu->addAction(tr("Extract System Data..."), this, [this, partition] {
+ auto folder = SelectFolder();
+
+ if (!folder.isEmpty())
+ ExtractSystemData(partition, folder);
+ });
+ }
+
+ switch (type)
+ {
+ case EntryType::Disc:
+ menu->addAction(tr("Extract Entire Disc..."), this, [this, partition, path] {
+ auto folder = SelectFolder();
+
+ if (folder.isEmpty())
+ return;
+
+ if (m_volume->GetPartitions().empty())
+ {
+ ExtractPartition(DiscIO::PARTITION_NONE, folder);
+ }
+ else
+ {
+ for (auto& p : m_volume->GetPartitions())
+ ExtractPartition(p, folder);
+ }
+ });
+ break;
+ case EntryType::Partition:
+ menu->addAction(tr("Extract Entire Partition..."), this, [this, partition] {
+ auto folder = SelectFolder();
+ if (!folder.isEmpty())
+ ExtractPartition(partition, folder);
+ });
+ menu->addSeparator();
+ menu->addAction(tr("Check Partition Integrity"), this,
+ [this, partition] { CheckIntegrity(partition); });
+ break;
+ case EntryType::Dir:
+ menu->addAction(tr("Extract Files..."), this, [this, partition, path] {
+ auto folder = SelectFolder();
+
+ if (!folder.isEmpty())
+ ExtractDirectory(partition, path, folder);
+ });
+ break;
+ case EntryType::File:
+ menu->addAction(tr("Extract File..."), this, [this, partition, path] {
+ auto dest = QFileDialog::getSaveFileName(this, tr("Save File to"));
+
+ if (!dest.isEmpty())
+ ExtractFile(partition, path, dest);
+ });
+ break;
+ }
+
+ menu->exec(QCursor::pos());
+}
+
+DiscIO::Partition FilesystemWidget::GetPartitionFromID(int id)
+{
+ return id == -1 ? DiscIO::PARTITION_NONE : m_volume->GetPartitions()[id];
+}
+
+void FilesystemWidget::ExtractPartition(const DiscIO::Partition& partition, const QString& out)
+{
+ ExtractDirectory(partition, QStringLiteral(""), out);
+ ExtractSystemData(partition, out);
+}
+
+void FilesystemWidget::ExtractSystemData(const DiscIO::Partition& partition, const QString& out)
+{
+ bool success = DiscIO::ExportSystemData(*m_volume, partition, out.toStdString());
+
+ if (success)
+ QMessageBox::information(nullptr, tr("Success"), tr("Successfully extracted system data."));
+ else
+ QMessageBox::critical(nullptr, tr("Error"), tr("Failed to extract system data."));
+}
+
+void FilesystemWidget::ExtractDirectory(const DiscIO::Partition& partition, const QString path,
+ const QString& out)
+{
+ std::unique_ptr<DiscIO::FileSystem> filesystem(
+ DiscIO::CreateFileSystem(m_volume.get(), partition));
+
+ std::unique_ptr<DiscIO::FileInfo> info = filesystem->FindFileInfo(path.toStdString());
+ u32 size = info->GetTotalChildren();
+
+ QProgressDialog* dialog = new QProgressDialog(this);
+ dialog->setMinimum(0);
+ dialog->setMaximum(size);
+ dialog->show();
+
+ bool all = path.isEmpty();
+
+ DiscIO::ExportDirectory(
+ *m_volume, filesystem->GetPartition(), *info, true, path.toStdString(), out.toStdString(),
+ [all, dialog](const std::string& current) {
+ dialog->setLabelText(
+ (all ? QObject::tr("Extracting All Files...") : QObject::tr("Extracting Directory..."))
+ .append(QStringLiteral(" %1").arg(QString::fromStdString(current))));
+ dialog->setValue(dialog->value() + 1);
+
+ QCoreApplication::processEvents();
+ return dialog->wasCanceled();
+ });
+
+ dialog->close();
+}
+
+void FilesystemWidget::ExtractFile(const DiscIO::Partition& partition, const QString& path,
+ const QString& out)
+{
+ std::unique_ptr<DiscIO::FileSystem> filesystem(
+ DiscIO::CreateFileSystem(m_volume.get(), partition));
+ bool success = DiscIO::ExportFile(
+ *m_volume, partition, filesystem->FindFileInfo(path.toStdString()).get(), out.toStdString());
+
+ if (success)
+ QMessageBox::information(nullptr, tr("Success"), tr("Successfully extracted file."));
+ else
+ QMessageBox::critical(nullptr, tr("Error"), tr("Failed to extract file."));
+}
+
+void FilesystemWidget::CheckIntegrity(const DiscIO::Partition& partition)
{
+ QProgressDialog* dialog = new QProgressDialog(this);
+ std::future<bool> is_valid = std::async(
+ std::launch::async, [this, partition] { return m_volume->CheckIntegrity(partition); });
+
+ dialog->setLabelText(tr("Verifying integrity of partition..."));
+ dialog->setMinimum(0);
+ dialog->setMaximum(0);
+ dialog->show();
+
+ while (is_valid.wait_for(std::chrono::milliseconds(50)) != std::future_status::ready)
+ QCoreApplication::processEvents();
+
+ dialog->close();
+
+ if (is_valid.get())
+ QMessageBox::information(nullptr, tr("Success"),
+ tr("Integrity check completed. No errors have been found"));
+ else
+ QMessageBox::critical(nullptr, tr("Error"),
+ tr("Integrity check for partition failed. The disc image is most "
+ "likely corrupted or has been patched incorrectly."));
}
diff --git a/Source/Core/DolphinQt2/Config/FilesystemWidget.h b/Source/Core/DolphinQt2/Config/FilesystemWidget.h
index 8cb58fa236..deabc20442 100644
--- a/Source/Core/DolphinQt2/Config/FilesystemWidget.h
+++ b/Source/Core/DolphinQt2/Config/FilesystemWidget.h
@@ -5,9 +5,21 @@
#pragma once
#include <QDialog>
+#include <memory>
+#include "DiscIO/Volume.h"
#include "DolphinQt2/GameList/GameFile.h"
+class QStandardItem;
+class QStandardItemModel;
+class QTreeView;
+
+namespace DiscIO
+{
+class FileInfo;
+struct Partition;
+};
+
class FilesystemWidget final : public QWidget
{
Q_OBJECT
@@ -15,5 +27,24 @@ public:
explicit FilesystemWidget(const GameFile& game);
private:
+ void CreateWidgets();
+ void ConnectWidgets();
+ void PopulateView();
+ void PopulateDirectory(int partition_id, QStandardItem* root, const DiscIO::FileInfo& directory);
+
+ void ShowContextMenu(const QPoint&);
+
+ void ExtractPartition(const DiscIO::Partition& partition, const QString& out);
+ void ExtractDirectory(const DiscIO::Partition& partition, const QString path, const QString& out);
+ void ExtractFile(const DiscIO::Partition& pratition, const QString& path, const QString& out);
+ void ExtractSystemData(const DiscIO::Partition& partition, const QString& out);
+ void CheckIntegrity(const DiscIO::Partition& partition);
+
+ DiscIO::Partition GetPartitionFromID(int id);
+
+ QStandardItemModel* m_tree_model;
+ QTreeView* m_tree_view;
+
GameFile m_game;
+ std::unique_ptr<DiscIO::Volume> m_volume;
};