Qt: Add missing gamelist contextmenu options

This commit is contained in:
spycrab 2017-05-04 23:01:12 +02:00
parent 54dcd3a89b
commit 2f523afc41
6 changed files with 324 additions and 7 deletions

View File

@ -8,10 +8,14 @@
#include <QImage>
#include <QSharedPointer>
#include "Common/Assert.h"
#include "Common/FileUtil.h"
#include "Common/NandPaths.h"
#include "Core/ConfigManager.h"
#include "Core/HW/WiiSaveCrypted.h"
#include "DiscIO/Blob.h"
#include "DiscIO/Enums.h"
#include "DiscIO/NANDContentLoader.h"
#include "DiscIO/Volume.h"
#include "DiscIO/VolumeCreator.h"
#include "DolphinQt2/GameList/GameFile.h"
@ -144,6 +148,7 @@ bool GameFile::TryLoadVolume()
m_game_id = QString::fromStdString(volume->GetGameID());
std::string maker_id = volume->GetMakerID();
volume->GetTitleID(&m_title_id);
m_maker = QString::fromStdString(DiscIO::GetCompanyFromID(maker_id));
m_maker_id = QString::fromStdString(maker_id);
m_revision = volume->GetRevision();
@ -291,6 +296,53 @@ QString GameFile::GetLanguage(DiscIO::Language lang) const
}
}
bool GameFile::IsInstalled() const
{
_assert_(m_platform == DiscIO::Platform::WII_WAD);
const std::string content_dir =
Common::GetTitleContentPath(m_title_id, Common::FromWhichRoot::FROM_CONFIGURED_ROOT);
if (!File::IsDirectory(content_dir))
return false;
// Since this isn't IOS and we only need a simple way to figure out if a title is installed,
// we make the (reasonable) assumption that having more than just the TMD in the content
// directory means that the title is installed.
const auto entries = File::ScanDirectoryTree(content_dir, false);
return std::any_of(entries.children.begin(), entries.children.end(),
[](const auto& file) { return file.virtualName != "title.tmd"; });
}
bool GameFile::Install()
{
_assert_(m_platform == DiscIO::Platform::WII_WAD);
return DiscIO::CNANDContentManager::Access().Install_WiiWAD(m_path.toStdString());
}
bool GameFile::Uninstall()
{
_assert_(m_platform == DiscIO::Platform::WII_WAD);
return DiscIO::CNANDContentManager::Access().RemoveTitle(m_title_id,
Common::FROM_CONFIGURED_ROOT);
}
bool GameFile::ExportWiiSave()
{
return CWiiSaveCrypted::ExportWiiSave(m_title_id);
}
QString GameFile::GetWiiFSPath() const
{
_assert_(m_platform != DiscIO::Platform::GAMECUBE_DISC);
const std::string path = Common::GetTitleDataPath(m_title_id, Common::FROM_CONFIGURED_ROOT);
return QString::fromStdString(path);
}
// Convert an integer size to a friendly string representation.
QString FormatSize(qint64 size)
{

View File

@ -38,6 +38,7 @@ public:
QString GetGameID() const { return m_game_id; }
QString GetMakerID() const { return m_maker_id; }
QString GetMaker() const { return m_maker; }
u64 GetTitleID() const { return m_title_id; }
u16 GetRevision() const { return m_revision; }
QString GetInternalName() const { return m_internal_name; }
u8 GetDiscNumber() const { return m_disc_number; }
@ -52,6 +53,8 @@ public:
DiscIO::Country GetCountryID() const { return m_country; }
QString GetCountry() const;
DiscIO::BlobType GetBlobType() const { return m_blob_type; }
QString GetWiiFSPath() const;
bool IsInstalled() const;
// Banner details
QString GetLanguage(DiscIO::Language lang) const;
QList<DiscIO::Language> GetAvailableLanguages() const;
@ -65,6 +68,10 @@ public:
QString GetLongName(DiscIO::Language lang) const { return m_long_names[lang]; }
QString GetLongMaker(DiscIO::Language lang) const { return m_long_makers[lang]; }
QString GetDescription(DiscIO::Language lang) const { return m_descriptions[lang]; }
bool Install();
bool Uninstall();
bool ExportWiiSave();
private:
QString GetBannerString(const QMap<DiscIO::Language, QString>& m) const;
@ -90,6 +97,7 @@ private:
QString m_maker;
QString m_maker_id;
u16 m_revision = 0;
u64 m_title_id = 0;
QString m_internal_name;
QMap<DiscIO::Language, QString> m_short_names;
QMap<DiscIO::Language, QString> m_long_names;

View File

@ -3,11 +3,19 @@
// Refer to the license.txt file included.
#include <QDesktopServices>
#include <QDir>
#include <QErrorMessage>
#include <QFileDialog>
#include <QFileInfo>
#include <QHeaderView>
#include <QKeyEvent>
#include <QMenu>
#include <QMessageBox>
#include <QProgressDialog>
#include <QUrl>
#include "Common/FileUtil.h"
#include "DiscIO/Blob.h"
#include "DiscIO/Enums.h"
#include "DolphinQt2/Config/PropertiesDialog.h"
@ -16,6 +24,8 @@
#include "DolphinQt2/GameList/TableDelegate.h"
#include "DolphinQt2/Settings.h"
static bool CompressCB(const std::string&, float, void*);
GameList::GameList(QWidget* parent) : QStackedWidget(parent)
{
m_model = new GameListModel(this);
@ -105,18 +115,47 @@ void GameList::MakeListView()
void GameList::ShowContextMenu(const QPoint&)
{
const auto game = GetSelectedGame();
if (game.isEmpty())
return;
QMenu* menu = new QMenu(this);
DiscIO::Platform platform = GameFile(GetSelectedGame()).GetPlatformID();
DiscIO::Platform platform = GameFile(game).GetPlatformID();
menu->addAction(tr("Properties"), this, SLOT(OpenProperties()));
menu->addAction(tr("Wiki"), this, SLOT(OpenWiki()));
menu->addSeparator();
if (platform == DiscIO::Platform::GAMECUBE_DISC || platform == DiscIO::Platform::WII_DISC)
{
menu->addAction(tr("Properties"), this, SLOT(OpenProperties()));
menu->addAction(tr("Open Wiki Page"), this, SLOT(OpenWiki()));
menu->addAction(tr("Set as Default ISO"), this, SLOT(SetDefaultISO()));
menu->addAction(tr("Default ISO"), this, SLOT(SetDefaultISO()));
const auto blob_type = GameFile(game).GetBlobType();
if (blob_type == DiscIO::BlobType::GCZ)
menu->addAction(tr("Decompress ISO"), this, SLOT(DecompressISO()));
else if (blob_type == DiscIO::BlobType::PLAIN)
menu->addAction(tr("Compress ISO"), this, SLOT(CompressISO()));
menu->addSeparator();
}
else
if (platform == DiscIO::Platform::WII_WAD)
{
return;
menu->addAction(tr("Install to the NAND"), this, SLOT(InstallWAD()));
if (GameFile(game).IsInstalled())
menu->addAction(tr("Uninstall from the NAND"), this, SLOT(UninstallWAD()));
menu->addSeparator();
}
if (platform == DiscIO::Platform::WII_WAD || platform == DiscIO::Platform::WII_DISC)
{
menu->addAction(tr("Open Wii save folder"), this, SLOT(OpenSaveFolder()));
menu->addAction(tr("Export Wii save (Experimental)"), this, SLOT(ExportWiiSave()));
menu->addSeparator();
}
menu->addAction(tr("Open Containing Folder"), this, SLOT(OpenContainingFolder()));
menu->addAction(tr("Remove File"), this, SLOT(DeleteFile()));
menu->exec(QCursor::pos());
}
@ -126,6 +165,18 @@ void GameList::OpenProperties()
properties->show();
}
void GameList::ExportWiiSave()
{
QMessageBox result_dialog(this);
const bool success = GameFile(GetSelectedGame()).ExportWiiSave();
result_dialog.setIcon(success ? QMessageBox::Information : QMessageBox::Critical);
result_dialog.setText(success ? tr("Successfully exported save files") :
tr("Failed to export save files!"));
result_dialog.exec();
}
void GameList::OpenWiki()
{
QString game_id = GameFile(GetSelectedGame()).GetGameID();
@ -133,11 +184,160 @@ void GameList::OpenWiki()
QDesktopServices::openUrl(QUrl(url));
}
void GameList::CompressISO()
{
const auto original_path = GetSelectedGame();
auto file = GameFile(original_path);
const bool compressed = (file.GetBlobType() == DiscIO::BlobType::GCZ);
if (!compressed && file.GetPlatformID() == DiscIO::Platform::WII_DISC)
{
QMessageBox wii_warning(this);
wii_warning.setIcon(QMessageBox::Warning);
wii_warning.setText(tr("Are you sure?"));
wii_warning.setInformativeText(
tr("Compressing a Wii disc image will irreversibly change the compressed copy by removing "
"padding data. Your disc image will still work."));
wii_warning.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
if (wii_warning.exec() == QMessageBox::No)
return;
}
QString dst_path = QFileDialog::getSaveFileName(
this, compressed ? tr("Select where you want to save the decompressed image") :
tr("Select where you want to save the compressed image"),
QFileInfo(GetSelectedGame())
.dir()
.absoluteFilePath(file.GetGameID())
.append(compressed ? QStringLiteral(".gcm") : QStringLiteral(".gcz")),
compressed ? tr("Uncompressed GC/Wii images (*.iso *.gcm") :
tr("Compressed GC/Wii images (*.gcz)"));
if (dst_path.isEmpty())
return;
QProgressDialog progress_dialog(compressed ? tr("Decompressing...") : tr("Compressing..."),
tr("Abort"), 0, 100, this);
progress_dialog.setWindowModality(Qt::WindowModal);
bool good;
if (compressed)
{
good = DiscIO::DecompressBlobToFile(original_path.toStdString(), dst_path.toStdString(),
&CompressCB, &progress_dialog);
}
else
{
good = DiscIO::CompressFileToBlob(original_path.toStdString(), dst_path.toStdString(),
file.GetPlatformID() == DiscIO::Platform::WII_DISC ? 1 : 0,
16384, &CompressCB, &progress_dialog);
}
if (good)
{
QMessageBox(QMessageBox::Information, tr("Success!"), tr("Successfully compressed image."),
QMessageBox::Ok, this)
.exec();
}
else
{
QErrorMessage(this).showMessage(tr("Dolphin failed to complete the requested action."));
}
}
void GameList::InstallWAD()
{
QMessageBox result_dialog(this);
const bool success = GameFile(GetSelectedGame()).Install();
result_dialog.setIcon(success ? QMessageBox::Information : QMessageBox::Critical);
result_dialog.setText(success ? tr("Succesfully installed title to the NAND") :
tr("Failed to install title to the NAND"));
result_dialog.exec();
}
void GameList::UninstallWAD()
{
QMessageBox warning_dialog(this);
warning_dialog.setIcon(QMessageBox::Information);
warning_dialog.setText(tr("Uninstalling the WAD will remove the currently installed version of "
"this title from the NAND without deleting its save data. Continue?"));
warning_dialog.setStandardButtons(QMessageBox::No | QMessageBox::Yes);
if (warning_dialog.exec() == QMessageBox::No)
return;
QMessageBox result_dialog(this);
const bool success = GameFile(GetSelectedGame()).Uninstall();
result_dialog.setIcon(success ? QMessageBox::Information : QMessageBox::Critical);
result_dialog.setText(success ? tr("Succesfully removed title from the NAND") :
tr("Failed to remove title from the NAND"));
result_dialog.exec();
}
void GameList::SetDefaultISO()
{
Settings().SetDefaultGame(GetSelectedGame());
}
void GameList::OpenContainingFolder()
{
QUrl url = QUrl::fromLocalFile(QFileInfo(GetSelectedGame()).dir().absolutePath());
QDesktopServices::openUrl(url);
}
void GameList::OpenSaveFolder()
{
QUrl url = QUrl::fromLocalFile(GameFile(GetSelectedGame()).GetWiiFSPath());
QDesktopServices::openUrl(url);
}
void GameList::DeleteFile()
{
const auto game = GetSelectedGame();
QMessageBox confirm_dialog(this);
confirm_dialog.setIcon(QMessageBox::Warning);
confirm_dialog.setText(tr("Are you sure you want to delete this file?"));
confirm_dialog.setInformativeText(tr("You won't be able to undo this!"));
confirm_dialog.setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel);
if (confirm_dialog.exec() == QMessageBox::Yes)
{
bool deletion_successful = false;
while (!deletion_successful)
{
deletion_successful = File::Delete(game.toStdString());
if (deletion_successful)
{
m_model->RemoveGame(game);
}
else
{
QMessageBox error_dialog(this);
error_dialog.setIcon(QMessageBox::Critical);
error_dialog.setText(tr("Failed to delete the selected file."));
error_dialog.setInformativeText(tr("Check whether you have the permissions required to "
"delete the file or whether it's still in use."));
error_dialog.setStandardButtons(QMessageBox::Retry | QMessageBox::Abort);
if (error_dialog.exec() == QMessageBox::Abort)
break;
}
}
}
}
QString GameList::GetSelectedGame() const
{
QAbstractItemView* view;
@ -189,3 +389,13 @@ void GameList::keyReleaseEvent(QKeyEvent* event)
else
QStackedWidget::keyReleaseEvent(event);
}
static bool CompressCB(const std::string& text, float percent, void* ptr)
{
if (ptr == nullptr)
return false;
auto* progress_dialog = static_cast<QProgressDialog*>(ptr);
progress_dialog->setValue(percent * 100);
return !progress_dialog->wasCanceled();
}

View File

@ -29,9 +29,16 @@ public slots:
void SetViewColumn(int col, bool view) { m_table->setColumnHidden(col, !view); }
private slots:
void ShowContextMenu(const QPoint&);
void OpenContainingFolder();
void OpenProperties();
void OpenSaveFolder();
void OpenWiki();
void SetDefaultISO();
void DeleteFile();
void InstallWAD();
void UninstallWAD();
void ExportWiiSave();
void CompressISO();
signals:
void GameSelected();

View File

@ -4,10 +4,13 @@
#include <QAction>
#include <QDesktopServices>
#include <QFileDialog>
#include <QMessageBox>
#include <QUrl>
#include "Core/State.h"
#include "DolphinQt2/AboutDialog.h"
#include "DolphinQt2/GameList/GameFile.h"
#include "DolphinQt2/MenuBar.h"
#include "DolphinQt2/Settings.h"
@ -17,7 +20,7 @@ MenuBar::MenuBar(QWidget* parent) : QMenuBar(parent)
AddEmulationMenu();
addMenu(tr("Movie"));
addMenu(tr("Options"));
addMenu(tr("Tools"));
AddToolsMenu();
AddViewMenu();
AddHelpMenu();
@ -71,6 +74,12 @@ void MenuBar::AddFileMenu()
m_exit_action = file_menu->addAction(tr("Exit"), this, SIGNAL(Exit()));
}
void MenuBar::AddToolsMenu()
{
QMenu* tools_menu = addMenu(tr("Tools"));
m_wad_install_action = tools_menu->addAction(tr("Install WAD..."), this, SLOT(InstallWAD()));
}
void MenuBar::AddEmulationMenu()
{
QMenu* emu_menu = addMenu(tr("Emulation"));
@ -206,3 +215,27 @@ void MenuBar::AddTableColumnsMenu(QMenu* view_menu)
action->setCheckable(true);
}
}
void MenuBar::InstallWAD()
{
QString wad_file = QFileDialog::getOpenFileName(this, tr("Select a title to install to NAND"),
QString(), tr("WAD files (*.wad)"));
if (wad_file.isEmpty())
return;
QMessageBox result_dialog(this);
if (GameFile(wad_file).Install())
{
result_dialog.setIcon(QMessageBox::Information);
result_dialog.setText(tr("Successfully installed title to the NAND"));
}
else
{
result_dialog.setIcon(QMessageBox::Critical);
result_dialog.setText(tr("Failed to install title to the NAND!"));
}
result_dialog.exec();
}

View File

@ -50,6 +50,9 @@ public slots:
void EmulationStopped();
void UpdateStateSlotMenu();
// Tools
void InstallWAD();
private:
void AddFileMenu();
@ -62,12 +65,16 @@ private:
void AddGameListTypeSection(QMenu* view_menu);
void AddTableColumnsMenu(QMenu* view_menu);
void AddToolsMenu();
void AddHelpMenu();
// File
QAction* m_open_action;
QAction* m_exit_action;
// Tools
QAction* m_wad_install_action;
// Emulation
QAction* m_play_action;
QAction* m_pause_action;