From f794e49bb6eadd70c52683e60a700a1d7e9cd17b Mon Sep 17 00:00:00 2001
From: Rachel Powers <508861+Ryex@users.noreply.github.com>
Date: Mon, 6 Feb 2023 23:05:06 -0800
Subject: we want to make links!
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
---
launcher/ui/dialogs/CopyInstanceDialog.cpp | 19 +++
launcher/ui/dialogs/CopyInstanceDialog.h | 3 +
launcher/ui/dialogs/CopyInstanceDialog.ui | 200 +++++++++++++++++++----------
3 files changed, 155 insertions(+), 67 deletions(-)
(limited to 'launcher/ui/dialogs')
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.cpp b/launcher/ui/dialogs/CopyInstanceDialog.cpp
index 3f5122f6..981352ae 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.cpp
+++ b/launcher/ui/dialogs/CopyInstanceDialog.cpp
@@ -85,6 +85,10 @@ CopyInstanceDialog::CopyInstanceDialog(InstancePtr original, QWidget *parent)
ui->copyServersCheckbox->setChecked(m_selectedOptions.isCopyServersEnabled());
ui->copyModsCheckbox->setChecked(m_selectedOptions.isCopyModsEnabled());
ui->copyScreenshotsCheckbox->setChecked(m_selectedOptions.isCopyScreenshotsEnabled());
+
+ ui->linkFilesGroup->setChecked(m_selectedOptions.isLinkFilesEnabled());
+ ui->hardLinksCheckbox->setChecked(m_selectedOptions.isUseHardLinksEnabled());
+ ui->linkWorldsCheckbox->setChecked(m_selectedOptions.isLinkWorldsEnabled());
}
CopyInstanceDialog::~CopyInstanceDialog()
@@ -220,3 +224,18 @@ void CopyInstanceDialog::on_copyScreenshotsCheckbox_stateChanged(int state)
m_selectedOptions.enableCopyScreenshots(state == Qt::Checked);
updateSelectAllCheckbox();
}
+
+void CopyInstanceDialog::on_linkFilesGroup_toggled(bool checked)
+{
+ m_selectedOptions.enableLinkFiles(checked);
+}
+
+void CopyInstanceDialog::on_hardLinksCheckbox_stateChanged(int state)
+{
+ m_selectedOptions.enableUseHardLinks(state == Qt::Checked);
+}
+
+void CopyInstanceDialog::on_linkWorldsCheckbox_stateChanged(int state)
+{
+ m_selectedOptions.enableLinkWorlds(state == Qt::Checked);
+}
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.h b/launcher/ui/dialogs/CopyInstanceDialog.h
index 884501d1..a80faab9 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.h
+++ b/launcher/ui/dialogs/CopyInstanceDialog.h
@@ -55,6 +55,9 @@ slots:
void on_copyServersCheckbox_stateChanged(int state);
void on_copyModsCheckbox_stateChanged(int state);
void on_copyScreenshotsCheckbox_stateChanged(int state);
+ void on_linkFilesGroup_toggled(bool checked);
+ void on_hardLinksCheckbox_stateChanged(int state);
+ void on_linkWorldsCheckbox_stateChanged(int state);
private:
void checkAllCheckboxes(const bool& b);
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.ui b/launcher/ui/dialogs/CopyInstanceDialog.ui
index b7828fe3..e41ad526 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.ui
+++ b/launcher/ui/dialogs/CopyInstanceDialog.ui
@@ -9,8 +9,8 @@
0
0
- 341
- 399
+ 525
+ 581
@@ -136,70 +136,126 @@
-
-
-
-
-
-
- Disabling this will still keep the mod loader (ex: Fabric, Quilt, etc.) but erase the mods folder and their configs.
-
-
- Copy mods
-
-
-
- -
-
+
+
+ Instance copy options
+
+
+
-
+
+
+ Disabling this will still keep the mod loader (ex: Fabric, Quilt, etc.) but erase the mods folder and their configs.
+
+
+ Copy mods
+
+
+
+ -
+
+
+ Copy the in-game options like FOV, max framerate, etc.
+
+
+ Copy game options
+
+
+
+ -
+
+
+ Copy saves
+
+
+
+ -
+
+
+ Copy shader packs
+
+
+
+ -
+
+
+ Copy servers
+
+
+
+ -
+
+
+ true
+
+
+ Copy resource packs
+
+
+
+ -
+
+
+ Keep play time
+
+
+
+ -
+
+
+ Copy screenshots
+
+
+
+
+
+
+ -
+
+
-
+
- Copy the in-game options like FOV, max framerate, etc.
+ Use symbolic links instead of copying files.
-
- Copy game options
+
+ Link files instead of copying them
-
-
- -
-
-
- Copy saves
-
-
-
- -
-
-
- Copy shader packs
-
-
-
- -
-
-
- Copy servers
+
+ false
-
-
- -
-
-
+
true
-
- Copy resource packs
-
-
-
- -
-
-
- Keep play time
-
-
-
- -
-
-
- Copy screenshots
+
+ false
+
+
-
+
+
+ Use hard links instead of symbolic links
+
+
+ Use hard links
+
+
+
+ -
+
+
+ World save data will be linked and thus shared between instances.
+
+
+ Link worlds
+
+
+ true
+
+
+ false
+
+
+
+
@@ -210,7 +266,7 @@
Qt::Horizontal
- QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+ QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::Ok
@@ -220,10 +276,20 @@
iconButton
instNameTextBox
groupBox
+ selectAllCheckbox
+ keepPlaytimeCheckbox
+ copyScreenshotsCheckbox
+ copySavesCheckbox
+ copyShaderPacksCheckbox
+ copyGameOptionsCheckbox
+ copyServersCheckbox
+ copyResPacksCheckbox
+ copyModsCheckbox
+ linkFilesGroup
+ hardLinksCheckbox
+ linkWorldsCheckbox
-
-
-
+
buttonBox
@@ -232,8 +298,8 @@
accept()
- 254
- 316
+ 263
+ 571
157
@@ -248,8 +314,8 @@
reject()
- 322
- 316
+ 331
+ 571
286
--
cgit
From 59788823785c186af78d8100fce3bdedbed85c80 Mon Sep 17 00:00:00 2001
From: Rachel Powers <508861+Ryex@users.noreply.github.com>
Date: Wed, 8 Feb 2023 14:30:45 -0800
Subject: feat(symlinks&hardlinks): linkup copy dialog
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
---
launcher/FileSystem.cpp | 5 +-
launcher/FileSystem.h | 9 +++-
launcher/InstanceCopyPrefs.cpp | 8 ++--
launcher/InstanceCopyPrefs.h | 6 +--
launcher/InstanceCopyTask.cpp | 73 ++++++++++++++++++++++++++++--
launcher/InstanceCopyTask.h | 3 ++
launcher/ui/dialogs/CopyInstanceDialog.cpp | 7 +--
launcher/ui/dialogs/CopyInstanceDialog.h | 2 +-
launcher/ui/dialogs/CopyInstanceDialog.ui | 9 ++--
9 files changed, 96 insertions(+), 26 deletions(-)
(limited to 'launcher/ui/dialogs')
diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp
index c48a3bba..c94770ee 100644
--- a/launcher/FileSystem.cpp
+++ b/launcher/FileSystem.cpp
@@ -420,7 +420,7 @@ void create_link::runPrivlaged(const QString& offset)
qint64 byteswritten = clientConnection->write(block);
bool bytesflushed = clientConnection->flush();
qDebug() << "block flushed" << byteswritten << bytesflushed;
- //clientConnection->disconnectFromServer();
+
});
qDebug() << "Listening on pipe" << serverName;
@@ -437,7 +437,6 @@ void create_link::runPrivlaged(const QString& offset)
}
-
void ExternalLinkFileProcess::runLinkFile() {
QString fileLinkExe = PathCombine(QCoreApplication::instance()->applicationDirPath(), BuildConfig.LAUNCHER_APP_BINARY_NAME + "_filelink");
QString params = "-s " + m_server;
@@ -463,7 +462,7 @@ void ExternalLinkFileProcess::runLinkFile() {
ShExecInfo.lpFile = programNameWin;
ShExecInfo.lpParameters = paramsWin;
ShExecInfo.lpDirectory = NULL;
- ShExecInfo.nShow = SW_NORMAL;
+ ShExecInfo.nShow = SW_HIDE;
ShExecInfo.hInstApp = NULL;
ShellExecuteEx(&ShExecInfo);
diff --git a/launcher/FileSystem.h b/launcher/FileSystem.h
index 2e739298..d79096e6 100644
--- a/launcher/FileSystem.h
+++ b/launcher/FileSystem.h
@@ -211,16 +211,21 @@ class create_link : public QObject {
bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); }
+ int totalLinked() { return m_linked; }
+
+
void runPrivlaged() { runPrivlaged(QString()); }
void runPrivlaged(const QString& offset);
- int totalLinked() { return m_linked; }
+ QList getResults() { return m_path_results; }
+
signals:
void fileLinked(const QString& srcName, const QString& dstName);
void linkFailed(const QString& srcName, const QString& dstName, const QString& err_msg, int err_value);
- void finishedPrivlaged(bool gotResults);
void finished();
+ void finishedPrivlaged(bool gotResults);
+
private:
bool operator()(const QString& offset, bool dryRun = false);
diff --git a/launcher/InstanceCopyPrefs.cpp b/launcher/InstanceCopyPrefs.cpp
index 18a6d704..e363d4c6 100644
--- a/launcher/InstanceCopyPrefs.cpp
+++ b/launcher/InstanceCopyPrefs.cpp
@@ -103,9 +103,9 @@ bool InstanceCopyPrefs::isUseHardLinksEnabled() const
return useHardLinks;
}
-bool InstanceCopyPrefs::isLinkWorldsEnabled() const
+bool InstanceCopyPrefs::isDontLinkSavesEnabled() const
{
- return linkWorlds;
+ return dontLinkSaves;
}
// ======= Setters =======
@@ -159,7 +159,7 @@ void InstanceCopyPrefs::enableUseHardLinks(bool b)
useHardLinks = b;
}
-void InstanceCopyPrefs::enableLinkWorlds(bool b)
+void InstanceCopyPrefs::enableDontLinkSaves(bool b)
{
- linkWorlds = b;
+ dontLinkSaves = b;
}
diff --git a/launcher/InstanceCopyPrefs.h b/launcher/InstanceCopyPrefs.h
index 25c0f3fc..61719a06 100644
--- a/launcher/InstanceCopyPrefs.h
+++ b/launcher/InstanceCopyPrefs.h
@@ -21,7 +21,7 @@ struct InstanceCopyPrefs {
[[nodiscard]] bool isCopyScreenshotsEnabled() const;
[[nodiscard]] bool isLinkFilesEnabled() const;
[[nodiscard]] bool isUseHardLinksEnabled() const;
- [[nodiscard]] bool isLinkWorldsEnabled() const;
+ [[nodiscard]] bool isDontLinkSavesEnabled() const;
// Setters
void enableCopySaves(bool b);
void enableKeepPlaytime(bool b);
@@ -33,7 +33,7 @@ struct InstanceCopyPrefs {
void enableCopyScreenshots(bool b);
void enableLinkFiles(bool b);
void enableUseHardLinks(bool b);
- void enableLinkWorlds(bool b);
+ void enableDontLinkSaves(bool b);
protected: // data
bool copySaves = true;
@@ -46,5 +46,5 @@ struct InstanceCopyPrefs {
bool copyScreenshots = true;
bool linkFiles = false;
bool useHardLinks = false;
- bool linkWorlds = true;
+ bool dontLinkSaves = false;
};
diff --git a/launcher/InstanceCopyTask.cpp b/launcher/InstanceCopyTask.cpp
index 188d163b..31c6bdca 100644
--- a/launcher/InstanceCopyTask.cpp
+++ b/launcher/InstanceCopyTask.cpp
@@ -11,6 +11,11 @@ InstanceCopyTask::InstanceCopyTask(InstancePtr origInstance, const InstanceCopyP
m_keepPlaytime = prefs.isKeepPlaytimeEnabled();
QString filters = prefs.getSelectedFiltersAsRegex();
+
+ m_useLinks = prefs.isLinkFilesEnabled();
+ m_useHardLinks = prefs.isUseHardLinksEnabled();
+ m_copySaves = prefs.isDontLinkSavesEnabled() && prefs.isCopySavesEnabled();
+
if (!filters.isEmpty())
{
// Set regex filter:
@@ -25,11 +30,71 @@ void InstanceCopyTask::executeTask()
{
setStatus(tr("Copying instance %1").arg(m_origInstance->name()));
- m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this]{
- FS::copy folderCopy(m_origInstance->instanceRoot(), m_stagingPath);
- folderCopy.followSymlinks(false).matcher(m_matcher.get());
+ auto copySaves = [&](){
+ FS::copy savesCopy(FS::PathCombine(m_origInstance->instanceRoot(), "saves") , FS::PathCombine(m_stagingPath, "saves"));
+ savesCopy.followSymlinks(false);
+
+ return savesCopy();
+ };
+
+ m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this, copySaves]{
+ if (m_useLinks) {
+ FS::create_link folderLink(m_origInstance->instanceRoot(), m_stagingPath);
+ folderLink.linkRecursively(true).useHardLinks(m_useHardLinks).matcher(m_matcher.get());
+
+ bool there_were_errors = false;
+
+ if(!folderLink()){
+#if defined Q_OS_WIN32
+ if (!m_useHardLinks) {
+ qDebug() << "EXPECTED: Link failure, Windows requires permissions for symlinks";
+
+ qDebug() << "atempting to run with privelage";
+
+ QEventLoop loop;
+ bool got_priv_results = false;
+
+ connect(&folderLink, &FS::create_link::finishedPrivlaged, this, [&](bool gotResults){
+ if (!gotResults) {
+ qDebug() << "Privlaged run exited without results!";
+ }
+ got_priv_results = gotResults;
+ loop.quit();
+ });
+ folderLink.runPrivlaged();
+
+ loop.exec(); // wait for the finished signal
+
+ for (auto result : folderLink.getResults()) {
+ if (result.err_value != 0) {
+ there_were_errors = true;
+ }
+ }
+
+ if (m_copySaves) {
+ there_were_errors |= !copySaves();
+ }
+
+ return got_priv_results && !there_were_errors;
+ } else {
+ qDebug() << "Link Failed!" << folderLink.getOSError().value() << folderLink.getOSError().message().c_str();
+ }
+#else
+ qDebug() << "Link Failed!" << folderLink.getOSError().value() << folderLink.getOSError().message().c_str();
+#endif return false;
+ }
+
+ if (m_copySaves) {
+ there_were_errors |= !copySaves();
+ }
+
+ return !there_were_errors;
+ } else {
+ FS::copy folderCopy(m_origInstance->instanceRoot(), m_stagingPath);
+ folderCopy.followSymlinks(false).matcher(m_matcher.get());
- return folderCopy();
+ return folderCopy();
+ }
});
connect(&m_copyFutureWatcher, &QFutureWatcher::finished, this, &InstanceCopyTask::copyFinished);
connect(&m_copyFutureWatcher, &QFutureWatcher::canceled, this, &InstanceCopyTask::copyAborted);
diff --git a/launcher/InstanceCopyTask.h b/launcher/InstanceCopyTask.h
index 1f29b854..d9651b07 100644
--- a/launcher/InstanceCopyTask.h
+++ b/launcher/InstanceCopyTask.h
@@ -30,4 +30,7 @@ private:
QFutureWatcher m_copyFutureWatcher;
std::unique_ptr m_matcher;
bool m_keepPlaytime;
+ bool m_useLinks = false;
+ bool m_useHardLinks = false;
+ bool m_copySaves = true;
};
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.cpp b/launcher/ui/dialogs/CopyInstanceDialog.cpp
index 981352ae..e477b4b3 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.cpp
+++ b/launcher/ui/dialogs/CopyInstanceDialog.cpp
@@ -88,7 +88,7 @@ CopyInstanceDialog::CopyInstanceDialog(InstancePtr original, QWidget *parent)
ui->linkFilesGroup->setChecked(m_selectedOptions.isLinkFilesEnabled());
ui->hardLinksCheckbox->setChecked(m_selectedOptions.isUseHardLinksEnabled());
- ui->linkWorldsCheckbox->setChecked(m_selectedOptions.isLinkWorldsEnabled());
+ ui->dontLinkSavesCheckbox->setChecked(m_selectedOptions.isDontLinkSavesEnabled());
}
CopyInstanceDialog::~CopyInstanceDialog()
@@ -179,6 +179,7 @@ void CopyInstanceDialog::on_selectAllCheckbox_stateChanged(int state)
void CopyInstanceDialog::on_copySavesCheckbox_stateChanged(int state)
{
m_selectedOptions.enableCopySaves(state == Qt::Checked);
+ ui->dontLinkSavesCheckbox->setChecked((state == Qt::Checked) && ui->dontLinkSavesCheckbox->isChecked());
updateSelectAllCheckbox();
}
@@ -235,7 +236,7 @@ void CopyInstanceDialog::on_hardLinksCheckbox_stateChanged(int state)
m_selectedOptions.enableUseHardLinks(state == Qt::Checked);
}
-void CopyInstanceDialog::on_linkWorldsCheckbox_stateChanged(int state)
+void CopyInstanceDialog::on_dontLinkSavesCheckbox_stateChanged(int state)
{
- m_selectedOptions.enableLinkWorlds(state == Qt::Checked);
+ m_selectedOptions.enableDontLinkSaves(state == Qt::Checked);
}
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.h b/launcher/ui/dialogs/CopyInstanceDialog.h
index a80faab9..57775925 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.h
+++ b/launcher/ui/dialogs/CopyInstanceDialog.h
@@ -57,7 +57,7 @@ slots:
void on_copyScreenshotsCheckbox_stateChanged(int state);
void on_linkFilesGroup_toggled(bool checked);
void on_hardLinksCheckbox_stateChanged(int state);
- void on_linkWorldsCheckbox_stateChanged(int state);
+ void on_dontLinkSavesCheckbox_stateChanged(int state);
private:
void checkAllCheckboxes(const bool& b);
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.ui b/launcher/ui/dialogs/CopyInstanceDialog.ui
index e41ad526..d8eb96eb 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.ui
+++ b/launcher/ui/dialogs/CopyInstanceDialog.ui
@@ -240,17 +240,14 @@
-
-
+
- World save data will be linked and thus shared between instances.
+ If "copy saves" is selected world save data will be copied instead of linked and thus not shared between instances.
- Link worlds
+ Don't link saves
- true
-
-
false
--
cgit
From 1bed7754e0bf3c009a38818963fe8d0832b36852 Mon Sep 17 00:00:00 2001
From: Rachel Powers <508861+Ryex@users.noreply.github.com>
Date: Wed, 8 Feb 2023 18:39:17 -0700
Subject: feat(symlinks): make recursive links explicit
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
---
launcher/InstanceCopyPrefs.cpp | 10 ++++++++++
launcher/InstanceCopyPrefs.h | 3 +++
launcher/InstanceCopyTask.cpp | 10 ++++++----
launcher/InstanceCopyTask.h | 3 ++-
launcher/ui/dialogs/CopyInstanceDialog.cpp | 14 ++++++++++++++
launcher/ui/dialogs/CopyInstanceDialog.h | 1 +
launcher/ui/dialogs/CopyInstanceDialog.ui | 25 +++++++++++++++++++++++--
7 files changed, 59 insertions(+), 7 deletions(-)
(limited to 'launcher/ui/dialogs')
diff --git a/launcher/InstanceCopyPrefs.cpp b/launcher/InstanceCopyPrefs.cpp
index e363d4c6..59825ced 100644
--- a/launcher/InstanceCopyPrefs.cpp
+++ b/launcher/InstanceCopyPrefs.cpp
@@ -103,6 +103,11 @@ bool InstanceCopyPrefs::isUseHardLinksEnabled() const
return useHardLinks;
}
+bool InstanceCopyPrefs::isLinkRecursivelyEnabled() const
+{
+ return linkRecursively;
+}
+
bool InstanceCopyPrefs::isDontLinkSavesEnabled() const
{
return dontLinkSaves;
@@ -154,6 +159,11 @@ void InstanceCopyPrefs::enableLinkFiles(bool b)
linkFiles = b;
}
+void InstanceCopyPrefs::enableLinkRecursively(bool b)
+{
+ linkRecursively = b;
+}
+
void InstanceCopyPrefs::enableUseHardLinks(bool b)
{
useHardLinks = b;
diff --git a/launcher/InstanceCopyPrefs.h b/launcher/InstanceCopyPrefs.h
index 61719a06..9fc9dcab 100644
--- a/launcher/InstanceCopyPrefs.h
+++ b/launcher/InstanceCopyPrefs.h
@@ -20,6 +20,7 @@ struct InstanceCopyPrefs {
[[nodiscard]] bool isCopyModsEnabled() const;
[[nodiscard]] bool isCopyScreenshotsEnabled() const;
[[nodiscard]] bool isLinkFilesEnabled() const;
+ [[nodiscard]] bool isLinkRecursivelyEnabled() const;
[[nodiscard]] bool isUseHardLinksEnabled() const;
[[nodiscard]] bool isDontLinkSavesEnabled() const;
// Setters
@@ -32,6 +33,7 @@ struct InstanceCopyPrefs {
void enableCopyMods(bool b);
void enableCopyScreenshots(bool b);
void enableLinkFiles(bool b);
+ void enableLinkRecursively(bool b);
void enableUseHardLinks(bool b);
void enableDontLinkSaves(bool b);
@@ -45,6 +47,7 @@ struct InstanceCopyPrefs {
bool copyMods = true;
bool copyScreenshots = true;
bool linkFiles = false;
+ bool linkRecursively = false;
bool useHardLinks = false;
bool dontLinkSaves = false;
};
diff --git a/launcher/InstanceCopyTask.cpp b/launcher/InstanceCopyTask.cpp
index 31c6bdca..81502d89 100644
--- a/launcher/InstanceCopyTask.cpp
+++ b/launcher/InstanceCopyTask.cpp
@@ -12,9 +12,11 @@ InstanceCopyTask::InstanceCopyTask(InstancePtr origInstance, const InstanceCopyP
QString filters = prefs.getSelectedFiltersAsRegex();
+
m_useLinks = prefs.isLinkFilesEnabled();
- m_useHardLinks = prefs.isUseHardLinksEnabled();
- m_copySaves = prefs.isDontLinkSavesEnabled() && prefs.isCopySavesEnabled();
+ m_linkRecursively = prefs.isLinkRecursivelyEnabled();
+ m_useHardLinks = prefs.isLinkRecursivelyEnabled() && prefs.isUseHardLinksEnabled();
+ m_copySaves = prefs.isLinkRecursivelyEnabled() && prefs.isDontLinkSavesEnabled() && prefs.isCopySavesEnabled();
if (!filters.isEmpty())
{
@@ -32,7 +34,7 @@ void InstanceCopyTask::executeTask()
auto copySaves = [&](){
FS::copy savesCopy(FS::PathCombine(m_origInstance->instanceRoot(), "saves") , FS::PathCombine(m_stagingPath, "saves"));
- savesCopy.followSymlinks(false);
+ savesCopy.followSymlinks(true);
return savesCopy();
};
@@ -40,7 +42,7 @@ void InstanceCopyTask::executeTask()
m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this, copySaves]{
if (m_useLinks) {
FS::create_link folderLink(m_origInstance->instanceRoot(), m_stagingPath);
- folderLink.linkRecursively(true).useHardLinks(m_useHardLinks).matcher(m_matcher.get());
+ folderLink.linkRecursively(m_linkRecursively).useHardLinks(m_useHardLinks).matcher(m_matcher.get());
bool there_were_errors = false;
diff --git a/launcher/InstanceCopyTask.h b/launcher/InstanceCopyTask.h
index d9651b07..3dce1662 100644
--- a/launcher/InstanceCopyTask.h
+++ b/launcher/InstanceCopyTask.h
@@ -32,5 +32,6 @@ private:
bool m_keepPlaytime;
bool m_useLinks = false;
bool m_useHardLinks = false;
- bool m_copySaves = true;
+ bool m_copySaves = false;
+ bool m_linkRecursively = false;
};
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.cpp b/launcher/ui/dialogs/CopyInstanceDialog.cpp
index e477b4b3..55962c5a 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.cpp
+++ b/launcher/ui/dialogs/CopyInstanceDialog.cpp
@@ -87,6 +87,7 @@ CopyInstanceDialog::CopyInstanceDialog(InstancePtr original, QWidget *parent)
ui->copyScreenshotsCheckbox->setChecked(m_selectedOptions.isCopyScreenshotsEnabled());
ui->linkFilesGroup->setChecked(m_selectedOptions.isLinkFilesEnabled());
+ ui->recursiveLinkCheckbox->setChecked(m_selectedOptions.isLinkRecursivelyEnabled());
ui->hardLinksCheckbox->setChecked(m_selectedOptions.isUseHardLinksEnabled());
ui->dontLinkSavesCheckbox->setChecked(m_selectedOptions.isDontLinkSavesEnabled());
}
@@ -231,9 +232,22 @@ void CopyInstanceDialog::on_linkFilesGroup_toggled(bool checked)
m_selectedOptions.enableLinkFiles(checked);
}
+void CopyInstanceDialog::on_recursiveLinkCheckbox_stateChanged(int state)
+{
+ m_selectedOptions.enableLinkRecursively(state == Qt::Checked);
+ if (state != Qt::Checked) {
+ ui->hardLinksCheckbox->setChecked(false);
+ ui->dontLinkSavesCheckbox->setChecked(false);
+ }
+
+}
+
void CopyInstanceDialog::on_hardLinksCheckbox_stateChanged(int state)
{
m_selectedOptions.enableUseHardLinks(state == Qt::Checked);
+ if (state == Qt::Checked && !ui->recursiveLinkCheckbox->isChecked()) {
+ ui->recursiveLinkCheckbox->setChecked(true);
+ }
}
void CopyInstanceDialog::on_dontLinkSavesCheckbox_stateChanged(int state)
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.h b/launcher/ui/dialogs/CopyInstanceDialog.h
index 57775925..2fc6f38a 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.h
+++ b/launcher/ui/dialogs/CopyInstanceDialog.h
@@ -56,6 +56,7 @@ slots:
void on_copyModsCheckbox_stateChanged(int state);
void on_copyScreenshotsCheckbox_stateChanged(int state);
void on_linkFilesGroup_toggled(bool checked);
+ void on_recursiveLinkCheckbox_stateChanged(int state);
void on_hardLinksCheckbox_stateChanged(int state);
void on_dontLinkSavesCheckbox_stateChanged(int state);
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.ui b/launcher/ui/dialogs/CopyInstanceDialog.ui
index d8eb96eb..8df0d3db 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.ui
+++ b/launcher/ui/dialogs/CopyInstanceDialog.ui
@@ -209,6 +209,16 @@
+ -
+
+
+ Advanced Copy Options
+
+
+ Qt::AlignCenter
+
+
+
-
-
@@ -229,8 +239,18 @@
false
+
-
+
+
+ Link files recursively
+
+
+
-
+
+ false
+
Use hard links instead of symbolic links
@@ -242,7 +262,7 @@
-
- If "copy saves" is selected world save data will be copied instead of linked and thus not shared between instances.
+ If "copy saves" is selected world save data will be copied instead of linked and thus not shared between instances.
Don't link saves
@@ -283,8 +303,9 @@
copyResPacksCheckbox
copyModsCheckbox
linkFilesGroup
+ recursiveLinkCheckbox
hardLinksCheckbox
- linkWorldsCheckbox
+ dontLinkSavesCheckbox
--
cgit
From c9105e525e175ee8181ab0a6998d0e21526f116d Mon Sep 17 00:00:00 2001
From: Rachel Powers <508861+Ryex@users.noreply.github.com>
Date: Wed, 8 Feb 2023 18:52:50 -0700
Subject: fix: follow symlinks when exporting
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
---
launcher/FileSystem.h | 2 +-
launcher/MMCZip.cpp | 9 ++++++---
launcher/MMCZip.h | 6 ++++--
launcher/ui/dialogs/ExportInstanceDialog.cpp | 5 ++++-
4 files changed, 15 insertions(+), 7 deletions(-)
(limited to 'launcher/ui/dialogs')
diff --git a/launcher/FileSystem.h b/launcher/FileSystem.h
index d79096e6..782a2f40 100644
--- a/launcher/FileSystem.h
+++ b/launcher/FileSystem.h
@@ -144,7 +144,7 @@ class ExternalLinkFileProcess : public QThread {
Q_OBJECT
public:
ExternalLinkFileProcess(QString server, bool useHardLinks, QObject* parent = nullptr)
- : QThread(parent), m_server(server), m_useHardLinks(useHardLinks)
+ : QThread(parent), m_useHardLinks(useHardLinks), m_server(server)
{}
void run() override
diff --git a/launcher/MMCZip.cpp b/launcher/MMCZip.cpp
index 1eda43fe..b4b663c1 100644
--- a/launcher/MMCZip.cpp
+++ b/launcher/MMCZip.cpp
@@ -94,20 +94,23 @@ bool MMCZip::mergeZipFiles(QuaZip *into, QFileInfo from, QSet &containe
return true;
}
-bool MMCZip::compressDirFiles(QuaZip *zip, QString dir, QFileInfoList files)
+bool MMCZip::compressDirFiles(QuaZip *zip, QString dir, QFileInfoList files, bool followSymlinks)
{
QDir directory(dir);
if (!directory.exists()) return false;
for (auto e : files) {
auto filePath = directory.relativeFilePath(e.absoluteFilePath());
- if( !JlCompress::compressFile(zip, e.absoluteFilePath(), filePath)) return false;
+ auto srcPath = e.absoluteFilePath();
+ if (followSymlinks)
+ srcPath = e.canonicalFilePath();
+ if( !JlCompress::compressFile(zip, srcPath, filePath)) return false;
}
return true;
}
-bool MMCZip::compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files)
+bool MMCZip::compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files, bool followSymlinks)
{
QuaZip zip(fileCompressed);
QDir().mkpath(QFileInfo(fileCompressed).absolutePath());
diff --git a/launcher/MMCZip.h b/launcher/MMCZip.h
index 81f9cb90..2a78f830 100644
--- a/launcher/MMCZip.h
+++ b/launcher/MMCZip.h
@@ -59,18 +59,20 @@ namespace MMCZip
* \param zip target archive
* \param dir directory that will be compressed (to compress with relative paths)
* \param files list of files to compress
+ * \param followSymlinks should follow symlinks when compressing file data
* \return true for success or false for failure
*/
- bool compressDirFiles(QuaZip *zip, QString dir, QFileInfoList files);
+ bool compressDirFiles(QuaZip *zip, QString dir, QFileInfoList files, bool followSymlinks = false);
/**
* Compress directory, by providing a list of files to compress
* \param fileCompressed target archive file
* \param dir directory that will be compressed (to compress with relative paths)
* \param files list of files to compress
+ * \param followSymlinks should follow symlinks when compressing file data
* \return true for success or false for failure
*/
- bool compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files);
+ bool compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files, bool followSymlinks = false);
/**
* take a source jar, add mods to it, resulting in target jar
diff --git a/launcher/ui/dialogs/ExportInstanceDialog.cpp b/launcher/ui/dialogs/ExportInstanceDialog.cpp
index f13e36e8..07ec3c70 100644
--- a/launcher/ui/dialogs/ExportInstanceDialog.cpp
+++ b/launcher/ui/dialogs/ExportInstanceDialog.cpp
@@ -45,6 +45,8 @@
#include
#include
#include
+#include
+
#include "StringUtils.h"
#include "SeparatorPrefixTree.h"
#include "Application.h"
@@ -429,7 +431,8 @@ bool ExportInstanceDialog::doExport()
QMessageBox::warning(this, tr("Error"), tr("Unable to export instance"));
return false;
}
- if (!MMCZip::compressDirFiles(output, m_instance->instanceRoot(), files))
+
+ if (!MMCZip::compressDirFiles(output, m_instance->instanceRoot(), files, true))
{
QMessageBox::warning(this, tr("Error"), tr("Unable to export instance"));
return false;
--
cgit
From 397e7f036339b09569317300423261f2b37d6119 Mon Sep 17 00:00:00 2001
From: Rachel Powers <508861+Ryex@users.noreply.github.com>
Date: Thu, 9 Feb 2023 02:02:40 -0700
Subject: feat(reflink): hook up relink / clone on the copy dialog
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
---
launcher/FileSystem.cpp | 26 ++++++++++++++----
launcher/FileSystem.h | 3 +++
launcher/InstanceCopyPrefs.cpp | 10 +++++++
launcher/InstanceCopyPrefs.h | 3 +++
launcher/InstanceCopyTask.cpp | 11 ++++++--
launcher/InstanceCopyTask.h | 1 +
launcher/ui/dialogs/CopyInstanceDialog.cpp | 28 ++++++++++++++++++-
launcher/ui/dialogs/CopyInstanceDialog.h | 5 ++++
launcher/ui/dialogs/CopyInstanceDialog.ui | 43 +++++++++++++++++++++++++++---
9 files changed, 119 insertions(+), 11 deletions(-)
(limited to 'launcher/ui/dialogs')
diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp
index 7b7fc80b..fd09842f 100644
--- a/launcher/FileSystem.cpp
+++ b/launcher/FileSystem.cpp
@@ -103,6 +103,7 @@ namespace fs = ghc::filesystem;
#include /* Definition of FICLONE* constants */
#include
#include
+#include
#elif defined(Q_OS_MACOS) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
#include
#include
@@ -880,6 +881,9 @@ FilesystemInfo statFS(QString path)
info.name = storage_info.name();
info.rootPath = storage_info.rootPath();
+ qDebug() << "Pulling filesystem info for" << info.rootPath;
+ qDebug() << "Filesystem: " << fsTypeName << "detected" << getFilesystemTypeName(info.fsType);
+
return info;
}
@@ -995,33 +999,45 @@ bool clone_file(const QString& src, const QString& dst, std::error_code& ec)
// https://man7.org/linux/man-pages/man2/ioctl_ficlone.2.html
int src_fd = open(src_path.c_str(), O_RDONLY);
- if(!src_fd) {
+ if(src_fd == -1) {
qWarning() << "Failed to open file:" << src_path.c_str();
qDebug() << "Error:" << strerror(errno);
ec = std::make_error_code(static_cast(errno));
return false;
}
- int dst_fd = open(dst_path.c_str(), O_WRONLY | O_TRUNC);
- if(!dst_fd) {
+ int dst_fd = open(dst_path.c_str(), O_CREAT | O_WRONLY | O_TRUNC, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
+ if(dst_fd == -1) {
qWarning() << "Failed to open file:" << dst_path.c_str();
qDebug() << "Error:" << strerror(errno);
ec = std::make_error_code(static_cast(errno));
+ close(src_fd);
return false;
}
// attempt to clone
- if(!ioctl(dst_fd, FICLONE, src_fd)){
+ if(ioctl(dst_fd, FICLONE, src_fd) == -1){
qWarning() << "Failed to clone file:" << src_path.c_str() << "to" << dst_path.c_str();
qDebug() << "Error:" << strerror(errno);
ec = std::make_error_code(static_cast(errno));
+ close(src_fd);
+ close(dst_fd);
return false;
}
+ if(close(src_fd)) {
+ qWarning() << "Failed to close file:" << src_path.c_str();
+ qDebug() << "Error:" << strerror(errno);
+ }
+ if(close(dst_fd)) {
+ qWarning() << "Failed to close file:" << dst_path.c_str();
+ qDebug() << "Error:" << strerror(errno);
+ }
#elif defined(Q_OS_MACOS) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
// TODO: use clonefile
// clonefile(const char * src, const char * dst, int flags);
// https://www.manpagez.com/man/2/clonefile/
- if (!clonefile(src_path.c_str(), dst_path.c_str(), 0)) {
+ qDebug() << "attempting file clone via clonefile" << src << "to" << dst;
+ if (clonefile(src_path.c_str(), dst_path.c_str(), 0) == -1) {
qWarning() << "Failed to clone file:" << src_path.c_str() << "to" << dst_path.c_str();
qDebug() << "Error:" << strerror(errno);
ec = std::make_error_code(static_cast(errno));
diff --git a/launcher/FileSystem.h b/launcher/FileSystem.h
index 531036dd..aa28de93 100644
--- a/launcher/FileSystem.h
+++ b/launcher/FileSystem.h
@@ -329,6 +329,7 @@ enum class FilesystemType {
HFS,
HFSPLUS,
HFSX,
+ FUSEBLK,
UNKNOWN
};
@@ -346,6 +347,7 @@ static const QMap s_filesystem_type_names = {
{FilesystemType::HFS, QString("HFS")},
{FilesystemType::HFSPLUS, QString("HFSPLUS")},
{FilesystemType::HFSX, QString("HFSX")},
+ {FilesystemType::FUSEBLK, QString("FUSEBLK")},
{FilesystemType::UNKNOWN, QString("UNKNOWN")}
};
@@ -365,6 +367,7 @@ static const QMap s_filesystem_type_names_inverse = {
{QString("HFSPLUS"), FilesystemType::HFSPLUS},
{QString("HFSX"), FilesystemType::HFSX},
{QString("HFS"), FilesystemType::HFS},
+ {QString("FUSEBLK"), FilesystemType::FUSEBLK},
{QString("UNKNOWN"), FilesystemType::UNKNOWN}
};
diff --git a/launcher/InstanceCopyPrefs.cpp b/launcher/InstanceCopyPrefs.cpp
index 59825ced..f2aa5e69 100644
--- a/launcher/InstanceCopyPrefs.cpp
+++ b/launcher/InstanceCopyPrefs.cpp
@@ -113,6 +113,11 @@ bool InstanceCopyPrefs::isDontLinkSavesEnabled() const
return dontLinkSaves;
}
+bool InstanceCopyPrefs::isUseCloneEnabled() const
+{
+ return useClone;
+}
+
// ======= Setters =======
void InstanceCopyPrefs::enableCopySaves(bool b)
{
@@ -173,3 +178,8 @@ void InstanceCopyPrefs::enableDontLinkSaves(bool b)
{
dontLinkSaves = b;
}
+
+void InstanceCopyPrefs::enableUseClone(bool b)
+{
+ useClone = b;
+}
\ No newline at end of file
diff --git a/launcher/InstanceCopyPrefs.h b/launcher/InstanceCopyPrefs.h
index 9fc9dcab..583fdd4c 100644
--- a/launcher/InstanceCopyPrefs.h
+++ b/launcher/InstanceCopyPrefs.h
@@ -23,6 +23,7 @@ struct InstanceCopyPrefs {
[[nodiscard]] bool isLinkRecursivelyEnabled() const;
[[nodiscard]] bool isUseHardLinksEnabled() const;
[[nodiscard]] bool isDontLinkSavesEnabled() const;
+ [[nodiscard]] bool isUseCloneEnabled() const;
// Setters
void enableCopySaves(bool b);
void enableKeepPlaytime(bool b);
@@ -36,6 +37,7 @@ struct InstanceCopyPrefs {
void enableLinkRecursively(bool b);
void enableUseHardLinks(bool b);
void enableDontLinkSaves(bool b);
+ void enableUseClone(bool b);
protected: // data
bool copySaves = true;
@@ -50,4 +52,5 @@ struct InstanceCopyPrefs {
bool linkRecursively = false;
bool useHardLinks = false;
bool dontLinkSaves = false;
+ bool useClone = false;
};
diff --git a/launcher/InstanceCopyTask.cpp b/launcher/InstanceCopyTask.cpp
index 81502d89..b3ea54b0 100644
--- a/launcher/InstanceCopyTask.cpp
+++ b/launcher/InstanceCopyTask.cpp
@@ -17,6 +17,7 @@ InstanceCopyTask::InstanceCopyTask(InstancePtr origInstance, const InstanceCopyP
m_linkRecursively = prefs.isLinkRecursivelyEnabled();
m_useHardLinks = prefs.isLinkRecursivelyEnabled() && prefs.isUseHardLinksEnabled();
m_copySaves = prefs.isLinkRecursivelyEnabled() && prefs.isDontLinkSavesEnabled() && prefs.isCopySavesEnabled();
+ m_useClone = prefs.isUseCloneEnabled();
if (!filters.isEmpty())
{
@@ -40,7 +41,12 @@ void InstanceCopyTask::executeTask()
};
m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this, copySaves]{
- if (m_useLinks) {
+ if (m_useClone) {
+ FS::clone folderClone(m_origInstance->instanceRoot(), m_stagingPath);
+ folderClone.matcher(m_matcher.get());
+
+ return folderClone();
+ } else if (m_useLinks) {
FS::create_link folderLink(m_origInstance->instanceRoot(), m_stagingPath);
folderLink.linkRecursively(m_linkRecursively).useHardLinks(m_useHardLinks).matcher(m_matcher.get());
@@ -83,7 +89,8 @@ void InstanceCopyTask::executeTask()
}
#else
qDebug() << "Link Failed!" << folderLink.getOSError().value() << folderLink.getOSError().message().c_str();
-#endif return false;
+#endif
+ return false;
}
if (m_copySaves) {
diff --git a/launcher/InstanceCopyTask.h b/launcher/InstanceCopyTask.h
index 3dce1662..aea9d99a 100644
--- a/launcher/InstanceCopyTask.h
+++ b/launcher/InstanceCopyTask.h
@@ -34,4 +34,5 @@ private:
bool m_useHardLinks = false;
bool m_copySaves = false;
bool m_linkRecursively = false;
+ bool m_useClone = false;
};
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.cpp b/launcher/ui/dialogs/CopyInstanceDialog.cpp
index 55962c5a..c51bc067 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.cpp
+++ b/launcher/ui/dialogs/CopyInstanceDialog.cpp
@@ -46,6 +46,7 @@
#include "icons/IconList.h"
#include "BaseInstance.h"
#include "InstanceList.h"
+#include "FileSystem.h"
CopyInstanceDialog::CopyInstanceDialog(InstancePtr original, QWidget *parent)
:QDialog(parent), ui(new Ui::CopyInstanceDialog), m_original(original)
@@ -85,11 +86,22 @@ CopyInstanceDialog::CopyInstanceDialog(InstancePtr original, QWidget *parent)
ui->copyServersCheckbox->setChecked(m_selectedOptions.isCopyServersEnabled());
ui->copyModsCheckbox->setChecked(m_selectedOptions.isCopyModsEnabled());
ui->copyScreenshotsCheckbox->setChecked(m_selectedOptions.isCopyScreenshotsEnabled());
-
+
ui->linkFilesGroup->setChecked(m_selectedOptions.isLinkFilesEnabled());
ui->recursiveLinkCheckbox->setChecked(m_selectedOptions.isLinkRecursivelyEnabled());
ui->hardLinksCheckbox->setChecked(m_selectedOptions.isUseHardLinksEnabled());
ui->dontLinkSavesCheckbox->setChecked(m_selectedOptions.isDontLinkSavesEnabled());
+
+ auto detectedOS = FS::statFS(m_original->instanceRoot()).fsType;
+ m_cloneSupported = FS::canCloneOnFS(detectedOS);
+
+ if (m_cloneSupported) {
+ ui->cloneSupportedLabel->setText(tr("Clone / Reflink is supported on (%1)").arg(FS::getFilesystemTypeName(detectedOS)));
+ } else {
+ ui->cloneSupportedLabel->setText(tr("Clone / Reflink not supported on (%1)").arg(FS::getFilesystemTypeName(detectedOS)));
+ }
+
+ updateUseCloneCheckbox();
}
CopyInstanceDialog::~CopyInstanceDialog()
@@ -152,6 +164,12 @@ void CopyInstanceDialog::updateSelectAllCheckbox()
ui->selectAllCheckbox->blockSignals(false);
}
+void CopyInstanceDialog::updateUseCloneCheckbox()
+{
+ ui->useCloneCheckbox->setEnabled(m_cloneSupported && !ui->linkFilesGroup->isChecked());
+ ui->useCloneCheckbox->setChecked(m_cloneSupported && m_selectedOptions.isUseCloneEnabled());
+}
+
void CopyInstanceDialog::on_iconButton_clicked()
{
IconPickerDialog dlg(this);
@@ -230,6 +248,7 @@ void CopyInstanceDialog::on_copyScreenshotsCheckbox_stateChanged(int state)
void CopyInstanceDialog::on_linkFilesGroup_toggled(bool checked)
{
m_selectedOptions.enableLinkFiles(checked);
+ updateUseCloneCheckbox();
}
void CopyInstanceDialog::on_recursiveLinkCheckbox_stateChanged(int state)
@@ -254,3 +273,10 @@ void CopyInstanceDialog::on_dontLinkSavesCheckbox_stateChanged(int state)
{
m_selectedOptions.enableDontLinkSaves(state == Qt::Checked);
}
+
+void CopyInstanceDialog::on_useCloneCheckbox_stateChanged(int state)
+{
+ m_selectedOptions.enableUseClone(m_cloneSupported && (state == Qt::Checked));
+ ui->linkFilesGroup->setEnabled(!m_selectedOptions.isUseCloneEnabled());
+ updateUseCloneCheckbox();
+}
\ No newline at end of file
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.h b/launcher/ui/dialogs/CopyInstanceDialog.h
index 2fc6f38a..859c643c 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.h
+++ b/launcher/ui/dialogs/CopyInstanceDialog.h
@@ -16,6 +16,7 @@
#pragma once
#include
+#include "BaseInstance.h"
#include "BaseVersion.h"
#include "InstanceCopyPrefs.h"
@@ -59,13 +60,17 @@ slots:
void on_recursiveLinkCheckbox_stateChanged(int state);
void on_hardLinksCheckbox_stateChanged(int state);
void on_dontLinkSavesCheckbox_stateChanged(int state);
+ void on_useCloneCheckbox_stateChanged(int state);
private:
void checkAllCheckboxes(const bool& b);
void updateSelectAllCheckbox();
+ void updateUseCloneCheckbox();
+
/* data */
Ui::CopyInstanceDialog *ui;
QString InstIconKey;
InstancePtr m_original;
InstanceCopyPrefs m_selectedOptions;
+ bool m_cloneSupported = false;
};
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.ui b/launcher/ui/dialogs/CopyInstanceDialog.ui
index 8df0d3db..ce8657f6 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.ui
+++ b/launcher/ui/dialogs/CopyInstanceDialog.ui
@@ -9,8 +9,8 @@
0
0
- 525
- 581
+ 531
+ 640
@@ -275,6 +275,44 @@
+ -
+
+
+ Clone / Reflink (Copy On Write) Options
+
+
+
-
+
+
+ false
+
+
+ Use Clone / Reflink
+
+
+
+ -
+
+
+
+ 1
+ 0
+
+
+
+ Clone / Reflink not supported on this filesystem
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+ 4
+
+
+
+
+
+
-
@@ -302,7 +340,6 @@
copyServersCheckbox
copyResPacksCheckbox
copyModsCheckbox
- linkFilesGroup
recursiveLinkCheckbox
hardLinksCheckbox
dontLinkSavesCheckbox
--
cgit
From bc8336a4b115fd190e068f57159d925683ba3930 Mon Sep 17 00:00:00 2001
From: Rachel Powers <508861+Ryex@users.noreply.github.com>
Date: Thu, 9 Feb 2023 16:19:38 -0700
Subject: fix: cleanup UI, detect FAT and turn off links
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
---
launcher/DesktopServices.cpp | 1 -
launcher/FileSystem.cpp | 28 ++++++++
launcher/FileSystem.h | 24 ++++++-
launcher/InstanceCopyPrefs.cpp | 8 +--
launcher/InstanceCopyPrefs.h | 6 +-
launcher/InstanceCopyTask.cpp | 2 +-
launcher/ui/dialogs/CopyInstanceDialog.cpp | 50 +++++++++----
launcher/ui/dialogs/CopyInstanceDialog.h | 6 +-
launcher/ui/dialogs/CopyInstanceDialog.ui | 111 ++++++++++++++++++++---------
9 files changed, 175 insertions(+), 61 deletions(-)
(limited to 'launcher/ui/dialogs')
diff --git a/launcher/DesktopServices.cpp b/launcher/DesktopServices.cpp
index 69770e99..2984a1b4 100644
--- a/launcher/DesktopServices.cpp
+++ b/launcher/DesktopServices.cpp
@@ -37,7 +37,6 @@
#include
#include
#include
-//#include "Application.h"
/**
* This shouldn't exist, but until QTBUG-9328 and other unreported bugs are fixed, it needs to be a thing.
diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp
index fd09842f..c363f6ea 100644
--- a/launcher/FileSystem.cpp
+++ b/launcher/FileSystem.cpp
@@ -1053,4 +1053,32 @@ bool clone_file(const QString& src, const QString& dst, std::error_code& ec)
return true;
}
+
+/**
+ * @brief if the Filesystem is symlink capable
+ *
+ */
+bool canLinkOnFS(const QString& path)
+{
+ FilesystemInfo info = statFS(path);
+ return canLinkOnFS(info);
+}
+bool canLinkOnFS(const FilesystemInfo& info)
+{
+ return canLinkOnFS(info.fsType);
+}
+bool canLinkOnFS(FilesystemType type)
+{
+ return !s_non_link_filesystems.contains(type);
+}
+/**
+ * @brief if the Filesystem is symlink capable on both ends
+ *
+ */
+bool canLink(const QString& src, const QString& dst)
+{
+ return canLinkOnFS(src) && canLinkOnFS(dst);
+}
+
+
}
diff --git a/launcher/FileSystem.h b/launcher/FileSystem.h
index aa28de93..83ff99a4 100644
--- a/launcher/FileSystem.h
+++ b/launcher/FileSystem.h
@@ -330,6 +330,7 @@ enum class FilesystemType {
HFSPLUS,
HFSX,
FUSEBLK,
+ F2FS,
UNKNOWN
};
@@ -348,6 +349,7 @@ static const QMap s_filesystem_type_names = {
{FilesystemType::HFSPLUS, QString("HFSPLUS")},
{FilesystemType::HFSX, QString("HFSX")},
{FilesystemType::FUSEBLK, QString("FUSEBLK")},
+ {FilesystemType::F2FS, QString("F2FS")},
{FilesystemType::UNKNOWN, QString("UNKNOWN")}
};
@@ -368,6 +370,7 @@ static const QMap s_filesystem_type_names_inverse = {
{QString("HFSX"), FilesystemType::HFSX},
{QString("HFS"), FilesystemType::HFS},
{QString("FUSEBLK"), FilesystemType::FUSEBLK},
+ {QString("F2FS"), FilesystemType::F2FS},
{QString("UNKNOWN"), FilesystemType::UNKNOWN}
};
@@ -405,7 +408,7 @@ bool canCloneOnFS(const FilesystemInfo& info);
bool canCloneOnFS(FilesystemType type);
/**
- * @brief if the Filesystem is reflink/clone capable and both are on the same device
+ * @brief if the Filesystems are reflink/clone capable and both are on the same device
*
*/
bool canClone(const QString& src, const QString& dst);
@@ -457,4 +460,23 @@ class clone : public QObject {
*/
bool clone_file(const QString& src, const QString& dst, std::error_code& ec);
+
+static const QList s_non_link_filesystems = {
+ FilesystemType::FAT,
+};
+
+/**
+ * @brief if the Filesystem is symlink capable
+ *
+ */
+bool canLinkOnFS(const QString& path);
+bool canLinkOnFS(const FilesystemInfo& info);
+bool canLinkOnFS(FilesystemType type);
+
+/**
+ * @brief if the Filesystem is symlink capable on both ends
+ *
+ */
+bool canLink(const QString& src, const QString& dst);
+
}
diff --git a/launcher/InstanceCopyPrefs.cpp b/launcher/InstanceCopyPrefs.cpp
index f2aa5e69..c03d0aae 100644
--- a/launcher/InstanceCopyPrefs.cpp
+++ b/launcher/InstanceCopyPrefs.cpp
@@ -93,9 +93,9 @@ bool InstanceCopyPrefs::isCopyScreenshotsEnabled() const
return copyScreenshots;
}
-bool InstanceCopyPrefs::isLinkFilesEnabled() const
+bool InstanceCopyPrefs::isUseSymLinksEnabled() const
{
- return linkFiles;
+ return useSymLinks;
}
bool InstanceCopyPrefs::isUseHardLinksEnabled() const
@@ -159,9 +159,9 @@ void InstanceCopyPrefs::enableCopyScreenshots(bool b)
copyScreenshots = b;
}
-void InstanceCopyPrefs::enableLinkFiles(bool b)
+void InstanceCopyPrefs::enableUseSymLinks(bool b)
{
- linkFiles = b;
+ useSymLinks = b;
}
void InstanceCopyPrefs::enableLinkRecursively(bool b)
diff --git a/launcher/InstanceCopyPrefs.h b/launcher/InstanceCopyPrefs.h
index 583fdd4c..14ae9551 100644
--- a/launcher/InstanceCopyPrefs.h
+++ b/launcher/InstanceCopyPrefs.h
@@ -19,7 +19,7 @@ struct InstanceCopyPrefs {
[[nodiscard]] bool isCopyServersEnabled() const;
[[nodiscard]] bool isCopyModsEnabled() const;
[[nodiscard]] bool isCopyScreenshotsEnabled() const;
- [[nodiscard]] bool isLinkFilesEnabled() const;
+ [[nodiscard]] bool isUseSymLinksEnabled() const;
[[nodiscard]] bool isLinkRecursivelyEnabled() const;
[[nodiscard]] bool isUseHardLinksEnabled() const;
[[nodiscard]] bool isDontLinkSavesEnabled() const;
@@ -33,7 +33,7 @@ struct InstanceCopyPrefs {
void enableCopyServers(bool b);
void enableCopyMods(bool b);
void enableCopyScreenshots(bool b);
- void enableLinkFiles(bool b);
+ void enableUseSymLinks(bool b);
void enableLinkRecursively(bool b);
void enableUseHardLinks(bool b);
void enableDontLinkSaves(bool b);
@@ -48,7 +48,7 @@ struct InstanceCopyPrefs {
bool copyServers = true;
bool copyMods = true;
bool copyScreenshots = true;
- bool linkFiles = false;
+ bool useSymLinks = false;
bool linkRecursively = false;
bool useHardLinks = false;
bool dontLinkSaves = false;
diff --git a/launcher/InstanceCopyTask.cpp b/launcher/InstanceCopyTask.cpp
index b3ea54b0..ba0052fa 100644
--- a/launcher/InstanceCopyTask.cpp
+++ b/launcher/InstanceCopyTask.cpp
@@ -13,7 +13,7 @@ InstanceCopyTask::InstanceCopyTask(InstancePtr origInstance, const InstanceCopyP
QString filters = prefs.getSelectedFiltersAsRegex();
- m_useLinks = prefs.isLinkFilesEnabled();
+ m_useLinks = prefs.isUseSymLinksEnabled();
m_linkRecursively = prefs.isLinkRecursivelyEnabled();
m_useHardLinks = prefs.isLinkRecursivelyEnabled() && prefs.isUseHardLinksEnabled();
m_copySaves = prefs.isLinkRecursivelyEnabled() && prefs.isDontLinkSavesEnabled() && prefs.isCopySavesEnabled();
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.cpp b/launcher/ui/dialogs/CopyInstanceDialog.cpp
index c51bc067..c6cbefcf 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.cpp
+++ b/launcher/ui/dialogs/CopyInstanceDialog.cpp
@@ -87,21 +87,26 @@ CopyInstanceDialog::CopyInstanceDialog(InstancePtr original, QWidget *parent)
ui->copyModsCheckbox->setChecked(m_selectedOptions.isCopyModsEnabled());
ui->copyScreenshotsCheckbox->setChecked(m_selectedOptions.isCopyScreenshotsEnabled());
- ui->linkFilesGroup->setChecked(m_selectedOptions.isLinkFilesEnabled());
- ui->recursiveLinkCheckbox->setChecked(m_selectedOptions.isLinkRecursivelyEnabled());
+ ui->symbolicLinksCheckbox->setChecked(m_selectedOptions.isUseSymLinksEnabled());
ui->hardLinksCheckbox->setChecked(m_selectedOptions.isUseHardLinksEnabled());
+
+ ui->recursiveLinkCheckbox->setChecked(m_selectedOptions.isLinkRecursivelyEnabled());
ui->dontLinkSavesCheckbox->setChecked(m_selectedOptions.isDontLinkSavesEnabled());
auto detectedOS = FS::statFS(m_original->instanceRoot()).fsType;
+
m_cloneSupported = FS::canCloneOnFS(detectedOS);
+ m_linkSupported = FS::canLinkOnFS(detectedOS);
if (m_cloneSupported) {
- ui->cloneSupportedLabel->setText(tr("Clone / Reflink is supported on (%1)").arg(FS::getFilesystemTypeName(detectedOS)));
+ ui->cloneSupportedLabel->setText(tr("Reflinks are supported on %1").arg(FS::getFilesystemTypeName(detectedOS)));
} else {
- ui->cloneSupportedLabel->setText(tr("Clone / Reflink not supported on (%1)").arg(FS::getFilesystemTypeName(detectedOS)));
+ ui->cloneSupportedLabel->setText(tr("Reflinks aren't supported on %1").arg(FS::getFilesystemTypeName(detectedOS)));
}
+ updateLinkOptions();
updateUseCloneCheckbox();
+
}
CopyInstanceDialog::~CopyInstanceDialog()
@@ -170,6 +175,21 @@ void CopyInstanceDialog::updateUseCloneCheckbox()
ui->useCloneCheckbox->setChecked(m_cloneSupported && m_selectedOptions.isUseCloneEnabled());
}
+void CopyInstanceDialog::updateLinkOptions()
+{
+ ui->symbolicLinksCheckbox->setEnabled(m_linkSupported && !ui->hardLinksCheckbox->isChecked());
+ ui->hardLinksCheckbox->setEnabled(m_linkSupported && !ui->symbolicLinksCheckbox->isChecked());
+
+ ui->symbolicLinksCheckbox->setChecked(m_linkSupported && m_selectedOptions.isUseSymLinksEnabled());
+ ui->hardLinksCheckbox->setChecked(m_linkSupported && m_selectedOptions.isUseHardLinksEnabled());
+
+ bool linksInUse = (ui->symbolicLinksCheckbox->isChecked() || ui->hardLinksCheckbox->isChecked());
+ ui->recursiveLinkCheckbox->setEnabled(m_linkSupported && linksInUse && !ui->hardLinksCheckbox->isChecked());
+ ui->dontLinkSavesCheckbox->setEnabled(m_linkSupported && linksInUse);
+ ui->recursiveLinkCheckbox->setChecked(m_linkSupported && linksInUse && m_selectedOptions.isLinkRecursivelyEnabled());
+ ui->dontLinkSavesCheckbox->setChecked(m_linkSupported && linksInUse && m_selectedOptions.isDontLinkSavesEnabled());
+}
+
void CopyInstanceDialog::on_iconButton_clicked()
{
IconPickerDialog dlg(this);
@@ -245,10 +265,20 @@ void CopyInstanceDialog::on_copyScreenshotsCheckbox_stateChanged(int state)
updateSelectAllCheckbox();
}
-void CopyInstanceDialog::on_linkFilesGroup_toggled(bool checked)
+void CopyInstanceDialog::on_symbolicLinksCheckbox_stateChanged(int state)
{
- m_selectedOptions.enableLinkFiles(checked);
+ m_selectedOptions.enableUseSymLinks(state == Qt::Checked);
updateUseCloneCheckbox();
+ updateLinkOptions();
+}
+
+void CopyInstanceDialog::on_hardLinksCheckbox_stateChanged(int state)
+{
+ m_selectedOptions.enableUseHardLinks(state == Qt::Checked);
+ if (state == Qt::Checked && !ui->recursiveLinkCheckbox->isChecked()) {
+ ui->recursiveLinkCheckbox->setChecked(true);
+ }
+ updateLinkOptions();
}
void CopyInstanceDialog::on_recursiveLinkCheckbox_stateChanged(int state)
@@ -261,14 +291,6 @@ void CopyInstanceDialog::on_recursiveLinkCheckbox_stateChanged(int state)
}
-void CopyInstanceDialog::on_hardLinksCheckbox_stateChanged(int state)
-{
- m_selectedOptions.enableUseHardLinks(state == Qt::Checked);
- if (state == Qt::Checked && !ui->recursiveLinkCheckbox->isChecked()) {
- ui->recursiveLinkCheckbox->setChecked(true);
- }
-}
-
void CopyInstanceDialog::on_dontLinkSavesCheckbox_stateChanged(int state)
{
m_selectedOptions.enableDontLinkSaves(state == Qt::Checked);
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.h b/launcher/ui/dialogs/CopyInstanceDialog.h
index 859c643c..2dea3795 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.h
+++ b/launcher/ui/dialogs/CopyInstanceDialog.h
@@ -56,9 +56,9 @@ slots:
void on_copyServersCheckbox_stateChanged(int state);
void on_copyModsCheckbox_stateChanged(int state);
void on_copyScreenshotsCheckbox_stateChanged(int state);
- void on_linkFilesGroup_toggled(bool checked);
- void on_recursiveLinkCheckbox_stateChanged(int state);
+ void on_symbolicLinksCheckbox_stateChanged(int state);
void on_hardLinksCheckbox_stateChanged(int state);
+ void on_recursiveLinkCheckbox_stateChanged(int state);
void on_dontLinkSavesCheckbox_stateChanged(int state);
void on_useCloneCheckbox_stateChanged(int state);
@@ -66,6 +66,7 @@ private:
void checkAllCheckboxes(const bool& b);
void updateSelectAllCheckbox();
void updateUseCloneCheckbox();
+ void updateLinkOptions();
/* data */
Ui::CopyInstanceDialog *ui;
@@ -73,4 +74,5 @@ private:
InstancePtr m_original;
InstanceCopyPrefs m_selectedOptions;
bool m_cloneSupported = false;
+ bool m_linkSupported = false;
};
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.ui b/launcher/ui/dialogs/CopyInstanceDialog.ui
index ce8657f6..7bf75c2d 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.ui
+++ b/launcher/ui/dialogs/CopyInstanceDialog.ui
@@ -9,8 +9,8 @@
0
0
- 531
- 640
+ 527
+ 699
@@ -138,7 +138,7 @@
-
- Instance copy options
+ Instance Copy Options
-
@@ -224,53 +224,92 @@
-
- Use symbolic links instead of copying files.
+ Use symbolic or hard links instead of copying files.
- Link files instead of copying them
+ Symbolic and Hard Link Options
false
- true
+ false
false
-
-
+
- Link files recursively
-
-
-
- -
-
-
- false
+ Links are supported on most filesystems except FAT
-
- Use hard links instead of symbolic links
-
-
- Use hard links
+
+ Qt::AlignCenter
-
-
-
- If "copy saves" is selected world save data will be copied instead of linked and thus not shared between instances.
+
+
+ 6
-
- Don't link saves
+
+ 6
-
- false
+
+ 6
-
+
+ 6
+
+
-
+
+
+ true
+
+
+ Use hard links instead of symbolic links
+
+
+ Use hard links
+
+
+
+ -
+
+
+ false
+
+
+ Link files recursively
+
+
+
+ -
+
+
+ false
+
+
+ If "copy saves" is selected world save data will be copied instead of linked and thus not shared between instances.
+
+
+ Don't link saves
+
+
+ false
+
+
+
+ -
+
+
+ Use symbloic links
+
+
+
+
@@ -278,7 +317,7 @@
-
- Clone / Reflink (Copy On Write) Options
+ CoW (Copy-on-Write) Options
-
@@ -287,7 +326,7 @@
false
- Use Clone / Reflink
+ Clone instead of copying
@@ -300,7 +339,7 @@
- Clone / Reflink not supported on this filesystem
+ Your filesystem and/or OS doesn't support reflinks
Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
@@ -340,9 +379,11 @@
copyServersCheckbox
copyResPacksCheckbox
copyModsCheckbox
+ symbolicLinksCheckbox
recursiveLinkCheckbox
hardLinksCheckbox
dontLinkSavesCheckbox
+ useCloneCheckbox
@@ -353,8 +394,8 @@
accept()
- 263
- 571
+ 269
+ 692
157
@@ -369,8 +410,8 @@
reject()
- 331
- 571
+ 337
+ 692
286
--
cgit
From 2837236d81b882f041a1cefadc86ca9d5f09ceeb Mon Sep 17 00:00:00 2001
From: Rachel Powers <508861+Ryex@users.noreply.github.com>
Date: Thu, 9 Feb 2023 19:48:40 -0700
Subject: fix: intelegent recursive links & symlink follow on export
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
---
launcher/FileSystem.cpp | 66 ++++++++++++-
launcher/FileSystem.h | 28 +++++-
launcher/InstanceCopyPrefs.cpp | 9 ++
launcher/InstanceCopyPrefs.h | 1 +
launcher/InstanceCopyTask.cpp | 16 +++-
launcher/MMCZip.cpp | 11 ++-
launcher/ui/dialogs/CopyInstanceDialog.cpp | 21 ++--
tests/FileSystem_test.cpp | 148 +++++++++++++++++++++++++++++
8 files changed, 278 insertions(+), 22 deletions(-)
(limited to 'launcher/ui/dialogs')
diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp
index c363f6ea..4ee3899b 100644
--- a/launcher/FileSystem.cpp
+++ b/launcher/FileSystem.cpp
@@ -242,6 +242,14 @@ bool copy::operator()(const QString& offset, bool dryRun)
return err.value() == 0;
}
+/// qDebug print support for the LinkPair struct
+QDebug operator<<(QDebug debug, const LinkPair& lp)
+{
+ QDebugStateSaver saver(debug);
+
+ debug.nospace() << "LinkPair{ src: " << lp.src << " , dst: " << lp.dst << " }";
+ return debug;
+}
bool create_link::operator()(const QString& offset, bool dryRun)
{
@@ -265,7 +273,7 @@ bool create_link::operator()(const QString& offset, bool dryRun)
* @param offset subdirectory form src to link to dest
* @return if there was an error during the attempt to link
*/
-void create_link::make_link_list( const QString& offset)
+void create_link::make_link_list(const QString& offset)
{
for (auto pair : m_path_pairs) {
const QString& srcPath = pair.src;
@@ -297,14 +305,26 @@ void create_link::make_link_list( const QString& offset)
link_file(src, "");
} else {
if (m_debug)
- qDebug() << "linking recursivly:" << src << "to" << dst;
+ qDebug() << "linking recursivly:" << src << "to" << dst << "max_depth:" << m_max_depth;
QDir src_dir(src);
QDirIterator source_it(src, QDir::Filter::Files | QDir::Filter::Hidden, QDirIterator::Subdirectories);
+ QStringList linkedPaths;
+
while (source_it.hasNext()) {
auto src_path = source_it.next();
auto relative_path = src_dir.relativeFilePath(src_path);
+ if (m_max_depth >= 0 && PathDepth(relative_path) > m_max_depth){
+ relative_path = PathTruncate(relative_path, m_max_depth);
+ src_path = src_dir.filePath(relative_path);
+ if (linkedPaths.contains(src_path)) {
+ continue;
+ }
+ }
+
+ linkedPaths.append(src_path);
+
link_file(src_path, relative_path);
}
}
@@ -312,7 +332,7 @@ void create_link::make_link_list( const QString& offset)
}
bool create_link::make_links()
-{
+{
for (auto link : m_links_to_make) {
QString src_path = link.src;
@@ -556,11 +576,49 @@ QString PathCombine(const QString& path1, const QString& path2, const QString& p
return PathCombine(PathCombine(path1, path2, path3), path4);
}
-QString AbsolutePath(QString path)
+QString AbsolutePath(const QString& path)
{
return QFileInfo(path).absolutePath();
}
+int PathDepth(const QString& path)
+{
+ if (path.isEmpty()) return 0;
+
+ QFileInfo info(path);
+
+ auto parts = QDir::toNativeSeparators(info.path()).split(QDir::separator(), Qt::SkipEmptyParts);
+
+ int numParts = QDir::toNativeSeparators(info.path()).split(QDir::separator(), Qt::SkipEmptyParts).length();
+ numParts -= parts.count(".");
+ numParts -= parts.count("..") * 2;
+
+ return numParts;
+}
+
+QString PathTruncate(const QString& path, int depth)
+{
+ if (path.isEmpty() || (depth < 0) ) return "";
+
+ QString trunc = QFileInfo(path).path();
+
+ if (PathDepth(trunc) > depth ) {
+ return PathTruncate(trunc, depth);
+ }
+
+ auto parts = QDir::toNativeSeparators(trunc).split(QDir::separator(), Qt::SkipEmptyParts);
+ if (parts.startsWith(".") && !path.startsWith(".")) {
+ parts.removeFirst();
+ }
+ if (path.startsWith(QDir::separator())) {
+ parts.prepend("");
+ }
+
+ trunc = parts.join(QDir::separator());
+
+ return trunc;
+}
+
QString ResolveExecutable(QString path)
{
if (path.isEmpty()) {
diff --git a/launcher/FileSystem.h b/launcher/FileSystem.h
index 83ff99a4..7485206a 100644
--- a/launcher/FileSystem.h
+++ b/launcher/FileSystem.h
@@ -200,6 +200,11 @@ class create_link : public QObject {
m_recursive = recursive;
return *this;
}
+ create_link& setMaxDepth(int depth)
+ {
+ m_max_depth = depth;
+ return *this;
+ }
create_link& debug(bool d)
{
m_debug = d;
@@ -239,6 +244,9 @@ class create_link : public QObject {
bool m_whitelist = false;
bool m_recursive = true;
+ /// @brief >= -1 = infinite, 0 = link files at src/* to dest/*, 1 = link files at src/*/* to dest/*/*, etc.
+ int m_max_depth = -1;
+
QList m_path_pairs;
QList m_path_results;
QList m_links_to_make;
@@ -272,7 +280,25 @@ QString PathCombine(const QString& path1, const QString& path2);
QString PathCombine(const QString& path1, const QString& path2, const QString& path3);
QString PathCombine(const QString& path1, const QString& path2, const QString& path3, const QString& path4);
-QString AbsolutePath(QString path);
+QString AbsolutePath(const QString& path);
+
+/**
+ * @brief depth of path. "foo.txt" -> 0 , "bar/foo.txt" -> 1, /baz/bar/foo.txt -> 2, etc.
+ *
+ * @param path path to measure
+ * @return int number of componants before base path
+ */
+int PathDepth(const QString& path);
+
+
+/**
+ * @brief cut off segments of path untill it is a max of length depth
+ *
+ * @param path path to truncate
+ * @param depth max depth of new path
+ * @return QString truncated path
+ */
+QString PathTruncate(const QString& path, int depth);
/**
* Resolve an executable
diff --git a/launcher/InstanceCopyPrefs.cpp b/launcher/InstanceCopyPrefs.cpp
index c03d0aae..0650002b 100644
--- a/launcher/InstanceCopyPrefs.cpp
+++ b/launcher/InstanceCopyPrefs.cpp
@@ -16,8 +16,13 @@ bool InstanceCopyPrefs::allTrue() const
copyScreenshots;
}
+
// Returns a single RegEx string of the selected folders/files to filter out (ex: ".minecraft/saves|.minecraft/server.dat")
QString InstanceCopyPrefs::getSelectedFiltersAsRegex() const
+{
+ return getSelectedFiltersAsRegex({});
+}
+QString InstanceCopyPrefs::getSelectedFiltersAsRegex(const QStringList& additionalFilters) const
{
QStringList filters;
@@ -42,6 +47,10 @@ QString InstanceCopyPrefs::getSelectedFiltersAsRegex() const
if(!copyScreenshots)
filters << "screenshots";
+ for (auto filter : additionalFilters) {
+ filters << filter;
+ }
+
// If we have any filters to add, join them as a single regex string to return:
if (!filters.isEmpty()) {
const QString MC_ROOT = "[.]?minecraft/";
diff --git a/launcher/InstanceCopyPrefs.h b/launcher/InstanceCopyPrefs.h
index 14ae9551..c7bde068 100644
--- a/launcher/InstanceCopyPrefs.h
+++ b/launcher/InstanceCopyPrefs.h
@@ -10,6 +10,7 @@ struct InstanceCopyPrefs {
public:
[[nodiscard]] bool allTrue() const;
[[nodiscard]] QString getSelectedFiltersAsRegex() const;
+ [[nodiscard]] QString getSelectedFiltersAsRegex(const QStringList& additionalFilters) const;
// Getters
[[nodiscard]] bool isCopySavesEnabled() const;
[[nodiscard]] bool isKeepPlaytimeEnabled() const;
diff --git a/launcher/InstanceCopyTask.cpp b/launcher/InstanceCopyTask.cpp
index ba0052fa..40babd0f 100644
--- a/launcher/InstanceCopyTask.cpp
+++ b/launcher/InstanceCopyTask.cpp
@@ -4,13 +4,14 @@
#include "NullInstance.h"
#include "pathmatcher/RegexpMatcher.h"
#include
+#include
InstanceCopyTask::InstanceCopyTask(InstancePtr origInstance, const InstanceCopyPrefs& prefs)
{
m_origInstance = origInstance;
m_keepPlaytime = prefs.isKeepPlaytimeEnabled();
- QString filters = prefs.getSelectedFiltersAsRegex();
+
m_useLinks = prefs.isUseSymLinksEnabled();
@@ -18,6 +19,14 @@ InstanceCopyTask::InstanceCopyTask(InstancePtr origInstance, const InstanceCopyP
m_useHardLinks = prefs.isLinkRecursivelyEnabled() && prefs.isUseHardLinksEnabled();
m_copySaves = prefs.isLinkRecursivelyEnabled() && prefs.isDontLinkSavesEnabled() && prefs.isCopySavesEnabled();
m_useClone = prefs.isUseCloneEnabled();
+
+ QString filters = prefs.getSelectedFiltersAsRegex();
+ if (m_useLinks || m_useHardLinks) {
+ if (!filters.isEmpty()) filters += "|";
+ filters += "instance.cfg";
+ }
+
+ qDebug() << "CopyFilters:" << filters;
if (!filters.isEmpty())
{
@@ -46,9 +55,10 @@ void InstanceCopyTask::executeTask()
folderClone.matcher(m_matcher.get());
return folderClone();
- } else if (m_useLinks) {
+ } else if (m_useLinks || m_useHardLinks) {
FS::create_link folderLink(m_origInstance->instanceRoot(), m_stagingPath);
- folderLink.linkRecursively(m_linkRecursively).useHardLinks(m_useHardLinks).matcher(m_matcher.get());
+ int depth = m_linkRecursively ? -1 : 0; // we need to at least link the top level instead of the instance folder
+ folderLink.linkRecursively(true).setMaxDepth(depth).useHardLinks(m_useHardLinks).matcher(m_matcher.get());
bool there_were_errors = false;
diff --git a/launcher/MMCZip.cpp b/launcher/MMCZip.cpp
index b4b663c1..1a336375 100644
--- a/launcher/MMCZip.cpp
+++ b/launcher/MMCZip.cpp
@@ -102,8 +102,13 @@ bool MMCZip::compressDirFiles(QuaZip *zip, QString dir, QFileInfoList files, boo
for (auto e : files) {
auto filePath = directory.relativeFilePath(e.absoluteFilePath());
auto srcPath = e.absoluteFilePath();
- if (followSymlinks)
- srcPath = e.canonicalFilePath();
+ if (followSymlinks) {
+ if (e.isSymLink()) {
+ srcPath = e.symLinkTarget();
+ } else {
+ srcPath = e.canonicalFilePath();
+ }
+ }
if( !JlCompress::compressFile(zip, srcPath, filePath)) return false;
}
@@ -119,7 +124,7 @@ bool MMCZip::compressDirFiles(QString fileCompressed, QString dir, QFileInfoList
return false;
}
- auto result = compressDirFiles(&zip, dir, files);
+ auto result = compressDirFiles(&zip, dir, files, followSymlinks);
zip.close();
if(zip.getZipError()!=0) {
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.cpp b/launcher/ui/dialogs/CopyInstanceDialog.cpp
index c6cbefcf..9fe129f1 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.cpp
+++ b/launcher/ui/dialogs/CopyInstanceDialog.cpp
@@ -171,17 +171,18 @@ void CopyInstanceDialog::updateSelectAllCheckbox()
void CopyInstanceDialog::updateUseCloneCheckbox()
{
- ui->useCloneCheckbox->setEnabled(m_cloneSupported && !ui->linkFilesGroup->isChecked());
- ui->useCloneCheckbox->setChecked(m_cloneSupported && m_selectedOptions.isUseCloneEnabled());
+ ui->useCloneCheckbox->setEnabled(m_cloneSupported && !ui->symbolicLinksCheckbox->isChecked() && !ui->hardLinksCheckbox->isChecked());
+ ui->useCloneCheckbox->setChecked(m_cloneSupported && m_selectedOptions.isUseCloneEnabled() && !ui->symbolicLinksCheckbox->isChecked() &&
+ !ui->hardLinksCheckbox->isChecked());
}
void CopyInstanceDialog::updateLinkOptions()
{
- ui->symbolicLinksCheckbox->setEnabled(m_linkSupported && !ui->hardLinksCheckbox->isChecked());
- ui->hardLinksCheckbox->setEnabled(m_linkSupported && !ui->symbolicLinksCheckbox->isChecked());
+ ui->symbolicLinksCheckbox->setEnabled(m_linkSupported && !ui->hardLinksCheckbox->isChecked() && !ui->useCloneCheckbox->isChecked());
+ ui->hardLinksCheckbox->setEnabled(m_linkSupported && !ui->symbolicLinksCheckbox->isChecked() && !ui->useCloneCheckbox->isChecked());
- ui->symbolicLinksCheckbox->setChecked(m_linkSupported && m_selectedOptions.isUseSymLinksEnabled());
- ui->hardLinksCheckbox->setChecked(m_linkSupported && m_selectedOptions.isUseHardLinksEnabled());
+ ui->symbolicLinksCheckbox->setChecked(m_linkSupported && m_selectedOptions.isUseSymLinksEnabled() && !ui->useCloneCheckbox->isChecked());
+ ui->hardLinksCheckbox->setChecked(m_linkSupported && m_selectedOptions.isUseHardLinksEnabled() && !ui->useCloneCheckbox->isChecked());
bool linksInUse = (ui->symbolicLinksCheckbox->isChecked() || ui->hardLinksCheckbox->isChecked());
ui->recursiveLinkCheckbox->setEnabled(m_linkSupported && linksInUse && !ui->hardLinksCheckbox->isChecked());
@@ -278,16 +279,14 @@ void CopyInstanceDialog::on_hardLinksCheckbox_stateChanged(int state)
if (state == Qt::Checked && !ui->recursiveLinkCheckbox->isChecked()) {
ui->recursiveLinkCheckbox->setChecked(true);
}
+ updateUseCloneCheckbox();
updateLinkOptions();
}
void CopyInstanceDialog::on_recursiveLinkCheckbox_stateChanged(int state)
{
m_selectedOptions.enableLinkRecursively(state == Qt::Checked);
- if (state != Qt::Checked) {
- ui->hardLinksCheckbox->setChecked(false);
- ui->dontLinkSavesCheckbox->setChecked(false);
- }
+ updateLinkOptions();
}
@@ -299,6 +298,6 @@ void CopyInstanceDialog::on_dontLinkSavesCheckbox_stateChanged(int state)
void CopyInstanceDialog::on_useCloneCheckbox_stateChanged(int state)
{
m_selectedOptions.enableUseClone(m_cloneSupported && (state == Qt::Checked));
- ui->linkFilesGroup->setEnabled(!m_selectedOptions.isUseCloneEnabled());
updateUseCloneCheckbox();
+ updateLinkOptions();
}
\ No newline at end of file
diff --git a/tests/FileSystem_test.cpp b/tests/FileSystem_test.cpp
index 4ccc4003..4418dd62 100644
--- a/tests/FileSystem_test.cpp
+++ b/tests/FileSystem_test.cpp
@@ -57,6 +57,11 @@ class LinkTask : public Task {
m_lnk->whitelist(b);
}
+ void setMaxDepth(int depth)
+ {
+ m_lnk->setMaxDepth(depth);
+ }
+
private:
void executeTask() override
{
@@ -630,6 +635,149 @@ slots:
QVERIFY(target_dir.entryList(filter).contains("pack.mcmeta"));
}
}
+
+ void test_link_with_max_depth()
+ {
+ QString folder = QFINDTESTDATA("testdata/FileSystem/test_folder");
+ auto f = [&folder, this]()
+ {
+ QTemporaryDir tempDir;
+ tempDir.setAutoRemove(true);
+ qDebug() << "From:" << folder << "To:" << tempDir.path();
+
+ QDir target_dir(FS::PathCombine(tempDir.path(), "test_folder"));
+ qDebug() << tempDir.path();
+ qDebug() << target_dir.path();
+
+ LinkTask lnk_tsk(folder, target_dir.path());
+ lnk_tsk.linkRecursively(true);
+ lnk_tsk.setMaxDepth(0);
+ QObject::connect(&lnk_tsk, &Task::finished, [&]{
+ QVERIFY2(lnk_tsk.wasSuccessful(), "Task finished but was not successful when it should have been.");
+ });
+ lnk_tsk.start();
+
+ QVERIFY2(QTest::qWaitFor([&]() {
+ return lnk_tsk.isFinished();
+ }, 100000), "Task didn't finish as it should.");
+
+ QVERIFY(!QFileInfo(target_dir.path()).isSymLink());
+
+ auto filter = QDir::Filter::Files | QDir::Filter::Dirs | QDir::Filter::Hidden;
+ for(auto entry: target_dir.entryList(filter))
+ {
+ qDebug() << entry;
+ if (entry == "." || entry == "..") continue;
+ QFileInfo entry_lnk_info(target_dir.filePath(entry));
+ QVERIFY(entry_lnk_info.isSymLink());
+ }
+
+ QFileInfo lnk_info(target_dir.path());
+ QVERIFY(lnk_info.exists());
+ QVERIFY(!lnk_info.isSymLink());
+
+ QVERIFY(target_dir.entryList().contains("pack.mcmeta"));
+ QVERIFY(target_dir.entryList().contains("assets"));
+
+
+ };
+
+ // first try variant without trailing /
+ QVERIFY(!folder.endsWith('/'));
+ f();
+
+ // then variant with trailing /
+ folder.append('/');
+ QVERIFY(folder.endsWith('/'));
+ f();
+ }
+
+ void test_link_with_no_max_depth()
+ {
+ QString folder = QFINDTESTDATA("testdata/FileSystem/test_folder");
+ auto f = [&folder]()
+ {
+ QTemporaryDir tempDir;
+ tempDir.setAutoRemove(true);
+ qDebug() << "From:" << folder << "To:" << tempDir.path();
+
+ QDir target_dir(FS::PathCombine(tempDir.path(), "test_folder"));
+ qDebug() << tempDir.path();
+ qDebug() << target_dir.path();
+
+ LinkTask lnk_tsk(folder, target_dir.path());
+ lnk_tsk.linkRecursively(true);
+ lnk_tsk.setMaxDepth(-1);
+ QObject::connect(&lnk_tsk, &Task::finished, [&]{
+ QVERIFY2(lnk_tsk.wasSuccessful(), "Task finished but was not successful when it should have been.");
+ });
+ lnk_tsk.start();
+
+ QVERIFY2(QTest::qWaitFor([&]() {
+ return lnk_tsk.isFinished();
+ }, 100000), "Task didn't finish as it should.");
+
+
+ std::function verify_check = [&](QString check_path) {
+ QDir check_dir(check_path);
+ auto filter = QDir::Filter::Files | QDir::Filter::Dirs | QDir::Filter::Hidden;
+ for(auto entry: check_dir.entryList(filter))
+ {
+ QFileInfo entry_lnk_info(check_dir.filePath(entry));
+ qDebug() << entry << check_dir.filePath(entry);
+ if (!entry_lnk_info.isDir()){
+ QVERIFY(entry_lnk_info.isSymLink());
+ } else if (entry != "." && entry != "..") {
+ qDebug() << "Decending tree to verify symlinks:" << check_dir.filePath(entry);
+ verify_check(entry_lnk_info.filePath());
+ }
+ }
+ };
+
+ verify_check(target_dir.path());
+
+
+ QFileInfo lnk_info(target_dir.path());
+ QVERIFY(lnk_info.exists());
+
+ QVERIFY(target_dir.entryList().contains("pack.mcmeta"));
+ QVERIFY(target_dir.entryList().contains("assets"));
+ };
+
+ // first try variant without trailing /
+ QVERIFY(!folder.endsWith('/'));
+ f();
+
+ // then variant with trailing /
+ folder.append('/');
+ QVERIFY(folder.endsWith('/'));
+ f();
+ }
+
+ void test_path_depth() {
+ QCOMPARE_EQ(FS::PathDepth(""), 0);
+ QCOMPARE_EQ(FS::PathDepth("."), 0);
+ QCOMPARE_EQ(FS::PathDepth("foo.txt"), 0);
+ QCOMPARE_EQ(FS::PathDepth("./foo.txt"), 0);
+ QCOMPARE_EQ(FS::PathDepth("./bar/foo.txt"), 1);
+ QCOMPARE_EQ(FS::PathDepth("../bar/foo.txt"), 0);
+ QCOMPARE_EQ(FS::PathDepth("/bar/foo.txt"), 1);
+ QCOMPARE_EQ(FS::PathDepth("baz/bar/foo.txt"), 2);
+ QCOMPARE_EQ(FS::PathDepth("/baz/bar/foo.txt"), 2);
+ QCOMPARE_EQ(FS::PathDepth("./baz/bar/foo.txt"), 2);
+ QCOMPARE_EQ(FS::PathDepth("/baz/../bar/foo.txt"), 1);
+ }
+
+ void test_path_trunc() {
+ QCOMPARE_EQ(FS::PathTruncate("", 0), "");
+ QCOMPARE_EQ(FS::PathTruncate("foo.txt", 0), "");
+ QCOMPARE_EQ(FS::PathTruncate("foo.txt", 1), "");
+ QCOMPARE_EQ(FS::PathTruncate("./bar/foo.txt", 0), "./bar");
+ QCOMPARE_EQ(FS::PathTruncate("./bar/foo.txt", 1), "./bar");
+ QCOMPARE_EQ(FS::PathTruncate("/bar/foo.txt", 1), "/bar");
+ QCOMPARE_EQ(FS::PathTruncate("bar/foo.txt", 1), "bar");
+ QCOMPARE_EQ(FS::PathTruncate("baz/bar/foo.txt", 2), "baz/bar");
+ }
};
QTEST_GUILESS_MAIN(FileSystemTest)
--
cgit
From 9f441a9678f56c5fb5efbc415b3faff176609b9c Mon Sep 17 00:00:00 2001
From: Rachel Powers <508861+Ryex@users.noreply.github.com>
Date: Sat, 11 Feb 2023 22:51:53 -0800
Subject: feat: Add UAC icon when symlinking on windows.
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
---
launcher/CMakeLists.txt | 8 --------
launcher/ui/dialogs/CopyInstanceDialog.cpp | 23 ++++++++++++++++++-----
launcher/ui/dialogs/CopyInstanceDialog.ui | 5 ++++-
3 files changed, 22 insertions(+), 14 deletions(-)
(limited to 'launcher/ui/dialogs')
diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt
index dd62893c..b47f5746 100644
--- a/launcher/CMakeLists.txt
+++ b/launcher/CMakeLists.txt
@@ -1149,14 +1149,6 @@ if(WIN32)
SET_TARGET_PROPERTIES("${Launcher_Name}_filelink" PROPERTIES INSTALL_RPATH "${Launcher_BINARY_RPATH}")
endif()
- # may be unnessacery with manifest
- if(CMAKE_GENERATOR MATCHES "Visual Studio")
- SET_TARGET_PROPERTIES("${Launcher_Name}_filelink" PROPERTIES LINK_FLAGS "/level='requireAdministrator' /uiAccess='false' /SUBSYSTEM:CONSOLE")
- # else() # link arg /MANIFESTUAC only works with MSVC
- # SET_TARGET_PROPERTIES("${Launcher_Name}_filelink" PROPERTIES LINK_FLAGS "/MANIFESTUAC:\"level='requireAdministrator' uiAccess='false'\" /SUBSYSTEM:CONSOLE")
- endif()
-
-
install(TARGETS "${Launcher_Name}_filelink"
BUNDLE DESTINATION "." COMPONENT Runtime
LIBRARY DESTINATION ${LIBRARY_DEST_DIR} COMPONENT Runtime
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.cpp b/launcher/ui/dialogs/CopyInstanceDialog.cpp
index 9fe129f1..495e98e9 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.cpp
+++ b/launcher/ui/dialogs/CopyInstanceDialog.cpp
@@ -93,17 +93,25 @@ CopyInstanceDialog::CopyInstanceDialog(InstancePtr original, QWidget *parent)
ui->recursiveLinkCheckbox->setChecked(m_selectedOptions.isLinkRecursivelyEnabled());
ui->dontLinkSavesCheckbox->setChecked(m_selectedOptions.isDontLinkSavesEnabled());
- auto detectedOS = FS::statFS(m_original->instanceRoot()).fsType;
+ auto detectedFS = FS::statFS(m_original->instanceRoot()).fsType;
- m_cloneSupported = FS::canCloneOnFS(detectedOS);
- m_linkSupported = FS::canLinkOnFS(detectedOS);
+ m_cloneSupported = FS::canCloneOnFS(detectedFS);
+ m_linkSupported = FS::canLinkOnFS(detectedFS);
if (m_cloneSupported) {
- ui->cloneSupportedLabel->setText(tr("Reflinks are supported on %1").arg(FS::getFilesystemTypeName(detectedOS)));
+ ui->cloneSupportedLabel->setText(tr("Reflinks are supported on %1").arg(FS::getFilesystemTypeName(detectedFS)));
} else {
- ui->cloneSupportedLabel->setText(tr("Reflinks aren't supported on %1").arg(FS::getFilesystemTypeName(detectedOS)));
+ ui->cloneSupportedLabel->setText(tr("Reflinks aren't supported on %1").arg(FS::getFilesystemTypeName(detectedFS)));
}
+#if defined(Q_OS_WIN)
+ ui->symbolicLinksCheckbox->setIcon(style()->standardIcon(QStyle::SP_VistaShield));
+ ui->symbolicLinksCheckbox->setToolTip(
+ tr("Use symbolic links instead of copying files.") +
+ tr("\nOn windows symbolic links may require admin permision to create.")
+ );
+#endif
+
updateLinkOptions();
updateUseCloneCheckbox();
@@ -189,6 +197,11 @@ void CopyInstanceDialog::updateLinkOptions()
ui->dontLinkSavesCheckbox->setEnabled(m_linkSupported && linksInUse);
ui->recursiveLinkCheckbox->setChecked(m_linkSupported && linksInUse && m_selectedOptions.isLinkRecursivelyEnabled());
ui->dontLinkSavesCheckbox->setChecked(m_linkSupported && linksInUse && m_selectedOptions.isDontLinkSavesEnabled());
+
+#if defined(Q_OS_WIN)
+ auto OkButton = ui->buttonBox->button(QDialogButtonBox::Ok);
+ OkButton->setIcon(m_selectedOptions.isUseSymLinksEnabled() ? style()->standardIcon(QStyle::SP_VistaShield) : QIcon());
+#endif
}
void CopyInstanceDialog::on_iconButton_clicked()
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.ui b/launcher/ui/dialogs/CopyInstanceDialog.ui
index 7bf75c2d..58442f73 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.ui
+++ b/launcher/ui/dialogs/CopyInstanceDialog.ui
@@ -269,7 +269,7 @@
true
- Use hard links instead of symbolic links
+ Use hard links instead of symbolic links.
Use hard links
@@ -307,6 +307,9 @@
Use symbloic links
+
+ Use symbolic links instead of copying files.
+
--
cgit
From a0e03c41c034ddbc330789c7639f02a4a8ac1a10 Mon Sep 17 00:00:00 2001
From: Rachel Powers <508861+Ryex@users.noreply.github.com>
Date: Sun, 12 Feb 2023 02:59:52 -0700
Subject: fix: typos
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
---
launcher/minecraft/WorldList.cpp | 6 +++---
launcher/minecraft/mod/ModFolderModel.cpp | 4 ++--
launcher/minecraft/mod/ResourceFolderModel.cpp | 4 ++--
launcher/minecraft/mod/ResourcePackFolderModel.cpp | 4 ++--
launcher/ui/dialogs/CopyInstanceDialog.ui | 2 +-
5 files changed, 10 insertions(+), 10 deletions(-)
(limited to 'launcher/ui/dialogs')
diff --git a/launcher/minecraft/WorldList.cpp b/launcher/minecraft/WorldList.cpp
index 1262fa1d..43733110 100644
--- a/launcher/minecraft/WorldList.cpp
+++ b/launcher/minecraft/WorldList.cpp
@@ -213,7 +213,7 @@ QVariant WorldList::data(const QModelIndex &index, int role) const
case InfoColumn:
if (world.isSymLinkUnder(instDirPath())) {
- return tr("This world is symbolicly linked from elsewhere.");
+ return tr("This world is symbolically linked from elsewhere.");
}
if (world.isMoreThanOneHardLink()) {
return tr("\nThis world is hard linked elsewhere.");
@@ -237,11 +237,11 @@ QVariant WorldList::data(const QModelIndex &index, int role) const
{
if (column == InfoColumn) {
if (world.isSymLinkUnder(instDirPath())) {
- return tr("Warning: This world is symbolicly linked from elsewhere. Editing it will also change the origonal") +
+ return tr("Warning: This world is symbolically linked from elsewhere. Editing it will also change the original") +
tr("\nCanonical Path: %1").arg(world.canonicalFilePath());
}
if (world.isMoreThanOneHardLink()) {
- return tr("Warning: This world is hard linked elsewhere. Editing it will also change the origonal");
+ return tr("Warning: This world is hard linked elsewhere. Editing it will also change the original");
}
}
return world.folderName();
diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp
index e2053f92..943f30ad 100644
--- a/launcher/minecraft/mod/ModFolderModel.cpp
+++ b/launcher/minecraft/mod/ModFolderModel.cpp
@@ -104,12 +104,12 @@ QVariant ModFolderModel::data(const QModelIndex &index, int role) const
if (column == NAME_COLUMN) {
if (at(row)->isSymLinkUnder(instDirPath())) {
return m_resources[row]->internal_id() +
- tr("\nWarning: This resource is symbolicly linked from elsewhere. Editing it will also change the origonal") +
+ tr("\nWarning: This resource is symbolically linked from elsewhere. Editing it will also change the original") +
tr("\nCanonical Path: %1").arg(at(row)->fileinfo().canonicalFilePath());
}
if (at(row)->isMoreThanOneHardLink()) {
return m_resources[row]->internal_id() +
- tr("\nWarning: This resource is hard linked elsewhere. Editing it will also change the origonal");
+ tr("\nWarning: This resource is hard linked elsewhere. Editing it will also change the original");
}
}
return m_resources[row]->internal_id();
diff --git a/launcher/minecraft/mod/ResourceFolderModel.cpp b/launcher/minecraft/mod/ResourceFolderModel.cpp
index d1748c1c..95b7651f 100644
--- a/launcher/minecraft/mod/ResourceFolderModel.cpp
+++ b/launcher/minecraft/mod/ResourceFolderModel.cpp
@@ -424,12 +424,12 @@ QVariant ResourceFolderModel::data(const QModelIndex& index, int role) const
if (column == NAME_COLUMN) {
if (at(row).isSymLinkUnder(instDirPath())) {
return m_resources[row]->internal_id() +
- tr("\nWarning: This resource is symbolicly linked from elsewhere. Editing it will also change the origonal") +
+ tr("\nWarning: This resource is symbolically linked from elsewhere. Editing it will also change the original") +
tr("\nCanonical Path: %1").arg(at(row).fileinfo().canonicalFilePath());;
}
if (at(row).isMoreThanOneHardLink()) {
return m_resources[row]->internal_id() +
- tr("\nWarning: This resource is hard linked elsewhere. Editing it will also change the origonal");
+ tr("\nWarning: This resource is hard linked elsewhere. Editing it will also change the original");
}
}
diff --git a/launcher/minecraft/mod/ResourcePackFolderModel.cpp b/launcher/minecraft/mod/ResourcePackFolderModel.cpp
index 56584a34..1fcfa909 100644
--- a/launcher/minecraft/mod/ResourcePackFolderModel.cpp
+++ b/launcher/minecraft/mod/ResourcePackFolderModel.cpp
@@ -96,12 +96,12 @@ QVariant ResourcePackFolderModel::data(const QModelIndex& index, int role) const
if (column == NAME_COLUMN) {
if (at(row)->isSymLinkUnder(instDirPath())) {
return m_resources[row]->internal_id() +
- tr("\nWarning: This resource is symbolicly linked from elsewhere. Editing it will also change the origonal") +
+ tr("\nWarning: This resource is symbolically linked from elsewhere. Editing it will also change the original") +
tr("\nCanonical Path: %1").arg(at(row)->fileinfo().canonicalFilePath());;
}
if (at(row)->isMoreThanOneHardLink()) {
return m_resources[row]->internal_id() +
- tr("\nWarning: This resource is hard linked elsewhere. Editing it will also change the origonal");
+ tr("\nWarning: This resource is hard linked elsewhere. Editing it will also change the original");
}
}
return m_resources[row]->internal_id();
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.ui b/launcher/ui/dialogs/CopyInstanceDialog.ui
index 58442f73..009f5b88 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.ui
+++ b/launcher/ui/dialogs/CopyInstanceDialog.ui
@@ -305,7 +305,7 @@
-
- Use symbloic links
+ Use symbolic links
Use symbolic links instead of copying files.
--
cgit
From e0ef86340f72ce508034815f1c5f8c695d31140d Mon Sep 17 00:00:00 2001
From: Rachel Powers <508861+Ryex@users.noreply.github.com>
Date: Thu, 16 Feb 2023 03:31:04 -0700
Subject: feat: connect new help button help-pages/instance-copy
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
---
launcher/ui/dialogs/CopyInstanceDialog.cpp | 11 +++++++++++
launcher/ui/dialogs/CopyInstanceDialog.h | 3 +++
2 files changed, 14 insertions(+)
(limited to 'launcher/ui/dialogs')
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.cpp b/launcher/ui/dialogs/CopyInstanceDialog.cpp
index 495e98e9..62c0bb39 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.cpp
+++ b/launcher/ui/dialogs/CopyInstanceDialog.cpp
@@ -37,6 +37,7 @@
#include
#include "Application.h"
+#include "BuildConfig.h"
#include "CopyInstanceDialog.h"
#include "ui_CopyInstanceDialog.h"
@@ -47,6 +48,7 @@
#include "BaseInstance.h"
#include "InstanceList.h"
#include "FileSystem.h"
+#include "DesktopServices.h"
CopyInstanceDialog::CopyInstanceDialog(InstancePtr original, QWidget *parent)
:QDialog(parent), ui(new Ui::CopyInstanceDialog), m_original(original)
@@ -114,6 +116,9 @@ CopyInstanceDialog::CopyInstanceDialog(InstancePtr original, QWidget *parent)
updateLinkOptions();
updateUseCloneCheckbox();
+
+ auto HelpButton = ui->buttonBox->button(QDialogButtonBox::Help);
+ connect(HelpButton, &QPushButton::clicked, this, &CopyInstanceDialog::help);
}
@@ -157,6 +162,12 @@ const InstanceCopyPrefs& CopyInstanceDialog::getChosenOptions() const
return m_selectedOptions;
}
+
+void CopyInstanceDialog::help()
+{
+ DesktopServices::openUrl(QUrl(BuildConfig.HELP_URL.arg("instance-copy")));
+}
+
void CopyInstanceDialog::checkAllCheckboxes(const bool& b)
{
ui->keepPlaytimeCheckbox->setChecked(b);
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.h b/launcher/ui/dialogs/CopyInstanceDialog.h
index 2dea3795..c447bee9 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.h
+++ b/launcher/ui/dialogs/CopyInstanceDialog.h
@@ -42,6 +42,9 @@ public:
QString iconKey() const;
const InstanceCopyPrefs& getChosenOptions() const;
+public slots:
+ void help();
+
private
slots:
void on_iconButton_clicked();
--
cgit
From ae289c923c4f896dca7e6696eef7ca35b10be9bf Mon Sep 17 00:00:00 2001
From: Rachel Powers <508861+Ryex@users.noreply.github.com>
Date: Wed, 22 Feb 2023 17:40:07 -0700
Subject: fix: clean up initial review comments (flowin)
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
---
launcher/FileSystem.cpp | 15 ++--
launcher/FileSystem.h | 6 +-
launcher/InstanceCopyTask.cpp | 6 +-
launcher/ui/dialogs/CopyInstanceDialog.ui | 129 ++++++++++++++++--------------
tests/FileSystem_test.cpp | 6 +-
5 files changed, 82 insertions(+), 80 deletions(-)
(limited to 'launcher/ui/dialogs')
diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp
index 45c73d24..2bd0cf52 100644
--- a/launcher/FileSystem.cpp
+++ b/launcher/FileSystem.cpp
@@ -36,10 +36,6 @@
*/
#include "FileSystem.h"
-#include
-#include
-#include
-#include
#include "BuildConfig.h"
@@ -48,6 +44,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -435,7 +432,7 @@ bool create_link::make_links()
return true;
}
-void create_link::runPrivlaged(const QString& offset)
+void create_link::runPrivileged(const QString& offset)
{
m_linked = 0; // reset counter
m_path_results.clear();
@@ -506,10 +503,10 @@ void create_link::runPrivlaged(const QString& offset)
in >> err_value;
result.err_value = err_value;
if (result.err_value) {
- qDebug() << "privlaged link fail" << result.src << "to" << result.dst << "code" << result.err_value << result.err_msg;
+ qDebug() << "privileged link fail" << result.src << "to" << result.dst << "code" << result.err_value << result.err_msg;
emit linkFailed(result.src, result.dst, result.err_msg, result.err_value);
} else {
- qDebug() << "privlaged link success" << result.src << "to" << result.dst;
+ qDebug() << "privileged link success" << result.src << "to" << result.dst;
m_linked++;
emit fileLinked(result.src, result.dst);
}
@@ -533,7 +530,7 @@ void create_link::runPrivlaged(const QString& offset)
}
ExternalLinkFileProcess* linkFileProcess = new ExternalLinkFileProcess(serverName, m_useHardLinks, this);
- connect(linkFileProcess, &ExternalLinkFileProcess::processExited, this, [&]() { emit finishedPrivlaged(gotResults); });
+ connect(linkFileProcess, &ExternalLinkFileProcess::processExited, this, [&]() { emit finishedPrivileged(gotResults); });
connect(linkFileProcess, &ExternalLinkFileProcess::finished, linkFileProcess, &QObject::deleteLater);
linkFileProcess->start();
@@ -1041,7 +1038,7 @@ FilesystemInfo statFS(const QString& path)
QStorageInfo storage_info(NearestExistentAncestor(path));
- info.fsTypeName = QString::fromStdString(storage_info.fileSystemType().toStdString());
+ info.fsTypeName = storage_info.fileSystemType();
info.fsType = getFilesystemTypeFuzzy(info.fsTypeName);
diff --git a/launcher/FileSystem.h b/launcher/FileSystem.h
index 84526c11..71175bb4 100644
--- a/launcher/FileSystem.h
+++ b/launcher/FileSystem.h
@@ -220,8 +220,8 @@ class create_link : public QObject {
int totalLinked() { return m_linked; }
- void runPrivlaged() { runPrivlaged(QString()); }
- void runPrivlaged(const QString& offset);
+ void runPrivileged() { runPrivileged(QString()); }
+ void runPrivileged(const QString& offset);
QList getResults() { return m_path_results; }
@@ -230,7 +230,7 @@ class create_link : public QObject {
void fileLinked(const QString& srcName, const QString& dstName);
void linkFailed(const QString& srcName, const QString& dstName, const QString& err_msg, int err_value);
void finished();
- void finishedPrivlaged(bool gotResults);
+ void finishedPrivileged(bool gotResults);
private:
diff --git a/launcher/InstanceCopyTask.cpp b/launcher/InstanceCopyTask.cpp
index e0a4de0b..5ef7a7fd 100644
--- a/launcher/InstanceCopyTask.cpp
+++ b/launcher/InstanceCopyTask.cpp
@@ -72,14 +72,14 @@ void InstanceCopyTask::executeTask()
QEventLoop loop;
bool got_priv_results = false;
- connect(&folderLink, &FS::create_link::finishedPrivlaged, this, [&](bool gotResults){
+ connect(&folderLink, &FS::create_link::finishedPrivileged, this, [&](bool gotResults){
if (!gotResults) {
- qDebug() << "Privlaged run exited without results!";
+ qDebug() << "Privileged run exited without results!";
}
got_priv_results = gotResults;
loop.quit();
});
- folderLink.runPrivlaged();
+ folderLink.runPrivileged();
loop.exec(); // wait for the finished signal
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.ui b/launcher/ui/dialogs/CopyInstanceDialog.ui
index 009f5b88..3101acec 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.ui
+++ b/launcher/ui/dialogs/CopyInstanceDialog.ui
@@ -9,8 +9,8 @@
0
0
- 527
- 699
+ 531
+ 653
@@ -112,35 +112,19 @@
- -
-
-
-
-
-
-
- 0
- 0
-
-
-
- Qt::LeftToRight
-
-
- Select all
-
-
- false
-
-
-
-
-
-
Instance Copy Options
+
-
+
+
+ Keep play time
+
+
+
-
@@ -151,6 +135,16 @@
+ -
+
+
+ true
+
+
+ Copy resource packs
+
+
+
-
@@ -161,13 +155,6 @@
- -
-
-
- Copy saves
-
-
-
-
@@ -182,33 +169,49 @@
- -
-
-
- true
-
+
-
+
- Copy resource packs
+ Copy saves
- -
-
+
-
+
- Keep play time
+ Copy screenshots
- -
-
+
-
+
+
+
+ 0
+ 0
+
+
+
+ Qt::LeftToRight
+
- Copy screenshots
+ Select all
+
+
+ false
+ -
+
+
+ Qt::Horizontal
+
+
+
-
@@ -250,7 +253,7 @@
-
-
+
6
@@ -263,24 +266,14 @@
6
-
-
-
-
- true
-
-
- Use hard links instead of symbolic links.
-
-
- Use hard links
-
-
-
-
false
+
+ Link each resource individually instead of linking whole folders at once
+
Link files recursively
@@ -302,14 +295,27 @@
- -
-
+
-
+
+
+ true
+
+
+ Use hard links instead of copying files.
+
- Use symbolic links
+ Use hard links
+
+
+ -
+
Use symbolic links instead of copying files.
+
+ Use symbolic links
+
@@ -373,7 +379,6 @@
iconButton
instNameTextBox
groupBox
- selectAllCheckbox
keepPlaytimeCheckbox
copyScreenshotsCheckbox
copySavesCheckbox
diff --git a/tests/FileSystem_test.cpp b/tests/FileSystem_test.cpp
index 169f0669..19565a99 100644
--- a/tests/FileSystem_test.cpp
+++ b/tests/FileSystem_test.cpp
@@ -72,15 +72,15 @@ class LinkTask : public Task {
qDebug() << "EXPECTED: Link failure, Windows requires permissions for symlinks";
qDebug() << "atempting to run with privelage";
- connect(m_lnk, &FS::create_link::finishedPrivlaged, this, [&](bool gotResults){
+ connect(m_lnk, &FS::create_link::finishedPrivileged, this, [&](bool gotResults){
if (gotResults) {
emitSucceeded();
} else {
- qDebug() << "Privlaged run exited without results!";
+ qDebug() << "Privileged run exited without results!";
emitFailed();
}
});
- m_lnk->runPrivlaged();
+ m_lnk->runPrivileged();
} else {
qDebug() << "Link Failed!" << m_lnk->getOSError().value() << m_lnk->getOSError().message().c_str();
}
--
cgit
From 458c2f38bc8e560832ca09b846edaa9ddc64f58d Mon Sep 17 00:00:00 2001
From: Rachel Powers <508861+Ryex@users.noreply.github.com>
Date: Fri, 3 Mar 2023 06:33:37 -0700
Subject: cleanup: code review sugestions
clean up translation strings
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
---
launcher/BaseInstance.cpp | 24 +++++++++++-----------
launcher/InstanceCopyTask.cpp | 4 ----
launcher/filelink/filelink.exe.manifest | 2 +-
launcher/filelink/main.cpp | 2 +-
launcher/minecraft/WorldList.cpp | 6 +++---
launcher/minecraft/mod/ModFolderModel.cpp | 7 ++++---
launcher/minecraft/mod/ResourceFolderModel.cpp | 7 ++++---
launcher/minecraft/mod/ResourcePackFolderModel.cpp | 7 ++++---
launcher/ui/dialogs/CopyInstanceDialog.cpp | 2 +-
9 files changed, 30 insertions(+), 31 deletions(-)
(limited to 'launcher/ui/dialogs')
diff --git a/launcher/BaseInstance.cpp b/launcher/BaseInstance.cpp
index 6428be43..ad45aa2d 100644
--- a/launcher/BaseInstance.cpp
+++ b/launcher/BaseInstance.cpp
@@ -66,7 +66,7 @@ BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr s
m_settings->registerSetting("totalTimePlayed", 0);
m_settings->registerSetting("lastTimePlayed", 0);
- m_settings->registerSetting("linkedInstancesList", "[]");
+ m_settings->registerSetting("linkedInstances", "[]");
// Game time override
auto gameTimeOverride = m_settings->registerSetting("OverrideGameTime", false);
@@ -188,34 +188,34 @@ bool BaseInstance::shouldStopOnConsoleOverflow() const
QStringList BaseInstance::getLinkedInstances() const
{
- return m_settings->getList("linkedInstancesList");
+ return m_settings->getList("linkedInstances");
}
void BaseInstance::setLinkedInstances(const QStringList& list)
{
- auto linkedInstancesList = m_settings->getList("linkedInstancesList");
- m_settings->setList("linkedInstancesList", list);
+ auto linkedInstances = m_settings->getList("linkedInstances");
+ m_settings->setList("linkedInstances", list);
}
void BaseInstance::addLinkedInstanceId(const QString& id)
{
- auto linkedInstancesList = m_settings->getList("linkedInstancesList");
- linkedInstancesList.append(id);
- setLinkedInstances(linkedInstancesList);
+ auto linkedInstances = m_settings->getList("linkedInstances");
+ linkedInstances.append(id);
+ setLinkedInstances(linkedInstances);
}
bool BaseInstance::removeLinkedInstanceId(const QString& id)
{
- auto linkedInstancesList = m_settings->getList("linkedInstancesList");
- int numRemoved = linkedInstancesList.removeAll(id);
- setLinkedInstances(linkedInstancesList);
+ auto linkedInstances = m_settings->getList("linkedInstances");
+ int numRemoved = linkedInstances.removeAll(id);
+ setLinkedInstances(linkedInstances);
return numRemoved > 0;
}
bool BaseInstance::isLinkedToInstanceId(const QString& id) const
{
- auto linkedInstancesList = m_settings->getList("linkedInstancesList");
- return linkedInstancesList.contains(id);
+ auto linkedInstances = m_settings->getList("linkedInstances");
+ return linkedInstances.contains(id);
}
void BaseInstance::iconUpdated(QString key)
diff --git a/launcher/InstanceCopyTask.cpp b/launcher/InstanceCopyTask.cpp
index 5ef7a7fd..6bd56de3 100644
--- a/launcher/InstanceCopyTask.cpp
+++ b/launcher/InstanceCopyTask.cpp
@@ -10,10 +10,6 @@ InstanceCopyTask::InstanceCopyTask(InstancePtr origInstance, const InstanceCopyP
{
m_origInstance = origInstance;
m_keepPlaytime = prefs.isKeepPlaytimeEnabled();
-
-
-
-
m_useLinks = prefs.isUseSymLinksEnabled();
m_linkRecursively = prefs.isLinkRecursivelyEnabled();
m_useHardLinks = prefs.isLinkRecursivelyEnabled() && prefs.isUseHardLinksEnabled();
diff --git a/launcher/filelink/filelink.exe.manifest b/launcher/filelink/filelink.exe.manifest
index a4e16264..239aa978 100644
--- a/launcher/filelink/filelink.exe.manifest
+++ b/launcher/filelink/filelink.exe.manifest
@@ -25,4 +25,4 @@
-
\ No newline at end of file
+
diff --git a/launcher/filelink/main.cpp b/launcher/filelink/main.cpp
index 7f06795e..4a22ff18 100644
--- a/launcher/filelink/main.cpp
+++ b/launcher/filelink/main.cpp
@@ -28,4 +28,4 @@ int main(int argc, char *argv[])
FileLinkApp ldh(argc, argv);
return ldh.exec();
-}
\ No newline at end of file
+}
diff --git a/launcher/minecraft/WorldList.cpp b/launcher/minecraft/WorldList.cpp
index 43733110..3681bcda 100644
--- a/launcher/minecraft/WorldList.cpp
+++ b/launcher/minecraft/WorldList.cpp
@@ -237,11 +237,11 @@ QVariant WorldList::data(const QModelIndex &index, int role) const
{
if (column == InfoColumn) {
if (world.isSymLinkUnder(instDirPath())) {
- return tr("Warning: This world is symbolically linked from elsewhere. Editing it will also change the original") +
- tr("\nCanonical Path: %1").arg(world.canonicalFilePath());
+ return tr("Warning: This world is symbolically linked from elsewhere. Editing it will also change the original."
+ "\nCanonical Path: %1").arg(world.canonicalFilePath());
}
if (world.isMoreThanOneHardLink()) {
- return tr("Warning: This world is hard linked elsewhere. Editing it will also change the original");
+ return tr("Warning: This world is hard linked elsewhere. Editing it will also change the original.");
}
}
return world.folderName();
diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp
index 943f30ad..91d16175 100644
--- a/launcher/minecraft/mod/ModFolderModel.cpp
+++ b/launcher/minecraft/mod/ModFolderModel.cpp
@@ -104,12 +104,13 @@ QVariant ModFolderModel::data(const QModelIndex &index, int role) const
if (column == NAME_COLUMN) {
if (at(row)->isSymLinkUnder(instDirPath())) {
return m_resources[row]->internal_id() +
- tr("\nWarning: This resource is symbolically linked from elsewhere. Editing it will also change the original") +
- tr("\nCanonical Path: %1").arg(at(row)->fileinfo().canonicalFilePath());
+ tr("\nWarning: This resource is symbolically linked from elsewhere. Editing it will also change the original."
+ "\nCanonical Path: %1")
+ .arg(at(row)->fileinfo().canonicalFilePath());
}
if (at(row)->isMoreThanOneHardLink()) {
return m_resources[row]->internal_id() +
- tr("\nWarning: This resource is hard linked elsewhere. Editing it will also change the original");
+ tr("\nWarning: This resource is hard linked elsewhere. Editing it will also change the original.");
}
}
return m_resources[row]->internal_id();
diff --git a/launcher/minecraft/mod/ResourceFolderModel.cpp b/launcher/minecraft/mod/ResourceFolderModel.cpp
index 95b7651f..29a0c736 100644
--- a/launcher/minecraft/mod/ResourceFolderModel.cpp
+++ b/launcher/minecraft/mod/ResourceFolderModel.cpp
@@ -424,12 +424,13 @@ QVariant ResourceFolderModel::data(const QModelIndex& index, int role) const
if (column == NAME_COLUMN) {
if (at(row).isSymLinkUnder(instDirPath())) {
return m_resources[row]->internal_id() +
- tr("\nWarning: This resource is symbolically linked from elsewhere. Editing it will also change the original") +
- tr("\nCanonical Path: %1").arg(at(row).fileinfo().canonicalFilePath());;
+ tr("\nWarning: This resource is symbolically linked from elsewhere. Editing it will also change the original."
+ "\nCanonical Path: %1")
+ .arg(at(row).fileinfo().canonicalFilePath());;
}
if (at(row).isMoreThanOneHardLink()) {
return m_resources[row]->internal_id() +
- tr("\nWarning: This resource is hard linked elsewhere. Editing it will also change the original");
+ tr("\nWarning: This resource is hard linked elsewhere. Editing it will also change the original.");
}
}
diff --git a/launcher/minecraft/mod/ResourcePackFolderModel.cpp b/launcher/minecraft/mod/ResourcePackFolderModel.cpp
index 1fcfa909..0480d8ba 100644
--- a/launcher/minecraft/mod/ResourcePackFolderModel.cpp
+++ b/launcher/minecraft/mod/ResourcePackFolderModel.cpp
@@ -96,12 +96,13 @@ QVariant ResourcePackFolderModel::data(const QModelIndex& index, int role) const
if (column == NAME_COLUMN) {
if (at(row)->isSymLinkUnder(instDirPath())) {
return m_resources[row]->internal_id() +
- tr("\nWarning: This resource is symbolically linked from elsewhere. Editing it will also change the original") +
- tr("\nCanonical Path: %1").arg(at(row)->fileinfo().canonicalFilePath());;
+ tr("\nWarning: This resource is symbolically linked from elsewhere. Editing it will also change the original."
+ "\nCanonical Path: %1")
+ .arg(at(row)->fileinfo().canonicalFilePath());;
}
if (at(row)->isMoreThanOneHardLink()) {
return m_resources[row]->internal_id() +
- tr("\nWarning: This resource is hard linked elsewhere. Editing it will also change the original");
+ tr("\nWarning: This resource is hard linked elsewhere. Editing it will also change the original.");
}
}
return m_resources[row]->internal_id();
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.cpp b/launcher/ui/dialogs/CopyInstanceDialog.cpp
index 62c0bb39..ced57ae0 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.cpp
+++ b/launcher/ui/dialogs/CopyInstanceDialog.cpp
@@ -324,4 +324,4 @@ void CopyInstanceDialog::on_useCloneCheckbox_stateChanged(int state)
m_selectedOptions.enableUseClone(m_cloneSupported && (state == Qt::Checked));
updateUseCloneCheckbox();
updateLinkOptions();
-}
\ No newline at end of file
+}
--
cgit
From a28193430c3830af294cbba92d68e9bcccd7dfd2 Mon Sep 17 00:00:00 2001
From: Rachel Powers <508861+Ryex@users.noreply.github.com>
Date: Tue, 14 Mar 2023 16:17:43 -0700
Subject: fix: adjust geometry and add missing tooltip
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
---
launcher/ui/dialogs/CopyInstanceDialog.ui | 20 ++++++++++++++++++--
1 file changed, 18 insertions(+), 2 deletions(-)
(limited to 'launcher/ui/dialogs')
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.ui b/launcher/ui/dialogs/CopyInstanceDialog.ui
index 3101acec..5060debc 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.ui
+++ b/launcher/ui/dialogs/CopyInstanceDialog.ui
@@ -9,8 +9,8 @@
0
0
- 531
- 653
+ 575
+ 695
@@ -334,11 +334,27 @@
false
+
+ Files cloned with reflinks take up no extra space until they are modified.
+
Clone instead of copying
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
-
--
cgit
From 0c986ba4d006740947603afb2e18dd9d2ffdfd2f Mon Sep 17 00:00:00 2001
From: Rachel Powers <508861+Ryex@users.noreply.github.com>
Date: Mon, 20 Mar 2023 16:38:40 -0700
Subject: spelling and formatting
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
---
launcher/FileSystem.cpp | 14 +--
launcher/FileSystem.h | 185 +++++++++++++----------------
launcher/InstanceCopyTask.cpp | 45 ++++---
launcher/filelink/FileLink.cpp | 110 +++++++----------
launcher/filelink/FileLink.h | 20 ++--
launcher/filelink/main.cpp | 5 +-
launcher/ui/dialogs/CopyInstanceDialog.cpp | 40 +++----
launcher/ui/dialogs/CopyInstanceDialog.h | 21 ++--
8 files changed, 192 insertions(+), 248 deletions(-)
(limited to 'launcher/ui/dialogs')
diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp
index c913b43e..640cf9be 100644
--- a/launcher/FileSystem.cpp
+++ b/launcher/FileSystem.cpp
@@ -328,7 +328,7 @@ bool create_link::operator()(const QString& offset, bool dryRun)
}
/**
- * @brief make a list off all the links ot make
+ * @brief make a list off all the links to
* @param offset subdirectory form src to link to dest
* @return if there was an error during the attempt to link
*/
@@ -363,7 +363,7 @@ void create_link::make_link_list(const QString& offset)
link_file(src, "");
} else {
if (m_debug)
- qDebug() << "linking recursivly:" << src << "to" << dst << "max_depth:" << m_max_depth;
+ qDebug() << "linking recursively:" << src << "to" << dst << "max_depth:" << m_max_depth;
QDir src_dir(src);
QDirIterator source_it(src, QDir::Filter::Files | QDir::Filter::Hidden, QDirIterator::Subdirectories);
@@ -414,7 +414,7 @@ bool create_link::make_links()
qWarning() << "Failed to link files:" << QString::fromStdString(m_os_err.message());
qDebug() << "Source file:" << src_path;
qDebug() << "Destination file:" << dst_path;
- qDebug() << "Error catagory:" << m_os_err.category().name();
+ qDebug() << "Error category:" << m_os_err.category().name();
qDebug() << "Error code:" << m_os_err.value();
emit linkFailed(src_path, dst_path, QString::fromStdString(m_os_err.message()), m_os_err.value());
} else {
@@ -469,7 +469,7 @@ void create_link::runPrivileged(const QString& offset)
in.setDevice(clientConnection);
in.setVersion(QDataStream::Qt_5_0);
qDebug() << "Reading path results from client";
- qDebug() << "bytes avalible" << clientConnection->bytesAvailable();
+ qDebug() << "bytes available" << clientConnection->bytesAvailable();
// Relies on the fact that QDataStream serializes a quint32 into
// sizeof(quint32) bytes
@@ -479,7 +479,7 @@ void create_link::runPrivileged(const QString& offset)
in >> blockSize;
qDebug() << "blocksize is" << blockSize;
- qDebug() << "bytes avalible" << clientConnection->bytesAvailable();
+ qDebug() << "bytes available" << clientConnection->bytesAvailable();
if (clientConnection->bytesAvailable() < blockSize || in.atEnd())
return;
@@ -506,7 +506,7 @@ void create_link::runPrivileged(const QString& offset)
m_path_results.append(result);
}
gotResults = true;
- qDebug() << "results recieved, closing connection";
+ qDebug() << "results received, closing connection";
clientConnection->close();
});
@@ -981,7 +981,7 @@ FilesystemType getFilesystemType(const QString& name)
}
/**
- * @brief path to the near ancestor that exsists
+ * @brief path to the near ancestor that exists
*
*/
QString NearestExistentAncestor(const QString& path)
diff --git a/launcher/FileSystem.h b/launcher/FileSystem.h
index 71175bb4..673c3563 100644
--- a/launcher/FileSystem.h
+++ b/launcher/FileSystem.h
@@ -44,9 +44,9 @@
#include
#include
+#include
#include
#include
-#include
namespace FS {
@@ -84,7 +84,7 @@ bool ensureFolderPathExists(QString filenamepath);
/**
* @brief Copies a directory and it's contents from src to dest
- */
+ */
class copy : public QObject {
Q_OBJECT
public:
@@ -167,17 +167,14 @@ class ExternalLinkFileProcess : public QThread {
/**
* @brief links (a file / a directory and it's contents) from src to dest
- */
+ */
class create_link : public QObject {
Q_OBJECT
public:
- create_link(const QList path_pairs, QObject* parent = nullptr) : QObject(parent)
- {
- m_path_pairs.append(path_pairs);
- }
+ create_link(const QList path_pairs, QObject* parent = nullptr) : QObject(parent) { m_path_pairs.append(path_pairs); }
create_link(const QString& src, const QString& dst, QObject* parent = nullptr) : QObject(parent)
{
- LinkPair pair = {src, dst};
+ LinkPair pair = { src, dst };
m_path_pairs.append(pair);
}
create_link& useHardLinks(const bool useHard)
@@ -211,28 +208,23 @@ class create_link : public QObject {
return *this;
}
- std::error_code getOSError() {
- return m_os_err;
- }
+ std::error_code getOSError() { return m_os_err; }
bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); }
int totalLinked() { return m_linked; }
-
void runPrivileged() { runPrivileged(QString()); }
void runPrivileged(const QString& offset);
QList getResults() { return m_path_results; }
-
signals:
void fileLinked(const QString& srcName, const QString& dstName);
void linkFailed(const QString& srcName, const QString& dstName, const QString& err_msg, int err_value);
void finished();
void finishedPrivileged(bool gotResults);
-
private:
bool operator()(const QString& offset, bool dryRun = false);
void make_link_list(const QString& offset);
@@ -262,9 +254,9 @@ class create_link : public QObject {
* @brief moves a file by renaming it
* @param source source file path
* @param dest destination filepath
- *
+ *
*/
-bool move(const QString& source, const QString& dest);
+bool move(const QString& source, const QString& dest);
/**
* Delete a folder recursively
@@ -274,7 +266,7 @@ bool deletePath(QString path);
/**
* Trash a folder / file
*/
-bool trash(QString path, QString *pathInTrash = nullptr);
+bool trash(QString path, QString* pathInTrash = nullptr);
QString PathCombine(const QString& path1, const QString& path2);
QString PathCombine(const QString& path1, const QString& path2, const QString& path3);
@@ -284,16 +276,15 @@ QString AbsolutePath(const QString& path);
/**
* @brief depth of path. "foo.txt" -> 0 , "bar/foo.txt" -> 1, /baz/bar/foo.txt -> 2, etc.
- *
+ *
* @param path path to measure
- * @return int number of componants before base path
+ * @return int number of components before base path
*/
int PathDepth(const QString& path);
-
/**
- * @brief cut off segments of path untill it is a max of length depth
- *
+ * @brief cut off segments of path until it is a max of length depth
+ *
* @param path path to truncate
* @param depth max depth of new path
* @return QString truncated path
@@ -363,124 +354,118 @@ enum class FilesystemType {
/**
* @brief Ordered Mapping of enum types to reported filesystem names
- * this maping is non exsaustive, it just attempts to capture the filesystems which could be reasonalbly be in use .
+ * this mapping is non exsaustive, it just attempts to capture the filesystems which could be reasonalbly be in use .
* all string values are in uppercase, use `QString.toUpper()` or equivalent during lookup.
- *
+ *
* QMap is ordered
- *
+ *
*/
-static const QMap s_filesystem_type_names = {
- {FilesystemType::FAT, QString("FAT")},
- {FilesystemType::NTFS, QString("NTFS")},
- {FilesystemType::REFS, QString("REFS")},
- {FilesystemType::EXT, QString("EXT")},
- {FilesystemType::EXT_2_OLD, QString("EXT_2_OLD")},
- {FilesystemType::EXT_2_3_4, QString("EXT2/3/4")},
- {FilesystemType::XFS, QString("XFS")},
- {FilesystemType::BTRFS, QString("BTRFS")},
- {FilesystemType::NFS, QString("NFS")},
- {FilesystemType::ZFS, QString("ZFS")},
- {FilesystemType::APFS, QString("APFS")},
- {FilesystemType::HFS, QString("HFS")},
- {FilesystemType::HFSPLUS, QString("HFSPLUS")},
- {FilesystemType::HFSX, QString("HFSX")},
- {FilesystemType::FUSEBLK, QString("FUSEBLK")},
- {FilesystemType::F2FS, QString("F2FS")},
- {FilesystemType::UNKNOWN, QString("UNKNOWN")}
-};
-
+static const QMap s_filesystem_type_names = { { FilesystemType::FAT, QString("FAT") },
+ { FilesystemType::NTFS, QString("NTFS") },
+ { FilesystemType::REFS, QString("REFS") },
+ { FilesystemType::EXT, QString("EXT") },
+ { FilesystemType::EXT_2_OLD, QString("EXT_2_OLD") },
+ { FilesystemType::EXT_2_3_4, QString("EXT2/3/4") },
+ { FilesystemType::XFS, QString("XFS") },
+ { FilesystemType::BTRFS, QString("BTRFS") },
+ { FilesystemType::NFS, QString("NFS") },
+ { FilesystemType::ZFS, QString("ZFS") },
+ { FilesystemType::APFS, QString("APFS") },
+ { FilesystemType::HFS, QString("HFS") },
+ { FilesystemType::HFSPLUS, QString("HFSPLUS") },
+ { FilesystemType::HFSX, QString("HFSX") },
+ { FilesystemType::FUSEBLK, QString("FUSEBLK") },
+ { FilesystemType::F2FS, QString("F2FS") },
+ { FilesystemType::UNKNOWN, QString("UNKNOWN") } };
/**
* @brief Ordered Mapping of reported filesystem names to enum types
- * this maping is non exsaustive, it just attempts to capture the many way these filesystems could be reported.
+ * this mapping is non exsaustive, it just attempts to capture the many way these filesystems could be reported.
* all keys are in uppercase, use `QString.toUpper()` or equivalent during lookup.
- *
+ *
* QMap is ordered
- *
+ *
*/
static const QMap s_filesystem_type_names_inverse = {
- {QString("FAT"), FilesystemType::FAT},
- {QString("NTFS"), FilesystemType::NTFS},
- {QString("REFS"), FilesystemType::REFS},
- {QString("EXT2_OLD"), FilesystemType::EXT_2_OLD},
- {QString("EXT_2_OLD"), FilesystemType::EXT_2_OLD},
- {QString("EXT2"), FilesystemType::EXT_2_3_4},
- {QString("EXT3"), FilesystemType::EXT_2_3_4},
- {QString("EXT4"), FilesystemType::EXT_2_3_4},
- {QString("EXT2/3/4"), FilesystemType::EXT_2_3_4},
- {QString("EXT_2_3_4"), FilesystemType::EXT_2_3_4},
- {QString("EXT"), FilesystemType::EXT}, // must come after all other EXT variants to prevent greedy detection
- {QString("XFS"), FilesystemType::XFS},
- {QString("BTRFS"), FilesystemType::BTRFS},
- {QString("NFS"), FilesystemType::NFS},
- {QString("ZFS"), FilesystemType::ZFS},
- {QString("APFS"), FilesystemType::APFS},
- {QString("HFSPLUS"), FilesystemType::HFSPLUS},
- {QString("HFSX"), FilesystemType::HFSX},
- {QString("HFS"), FilesystemType::HFS},
- {QString("FUSEBLK"), FilesystemType::FUSEBLK},
- {QString("F2FS"), FilesystemType::F2FS},
- {QString("UNKNOWN"), FilesystemType::UNKNOWN}
+ { QString("FAT"), FilesystemType::FAT },
+ { QString("NTFS"), FilesystemType::NTFS },
+ { QString("REFS"), FilesystemType::REFS },
+ { QString("EXT2_OLD"), FilesystemType::EXT_2_OLD },
+ { QString("EXT_2_OLD"), FilesystemType::EXT_2_OLD },
+ { QString("EXT2"), FilesystemType::EXT_2_3_4 },
+ { QString("EXT3"), FilesystemType::EXT_2_3_4 },
+ { QString("EXT4"), FilesystemType::EXT_2_3_4 },
+ { QString("EXT2/3/4"), FilesystemType::EXT_2_3_4 },
+ { QString("EXT_2_3_4"), FilesystemType::EXT_2_3_4 },
+ { QString("EXT"), FilesystemType::EXT }, // must come after all other EXT variants to prevent greedy detection
+ { QString("XFS"), FilesystemType::XFS },
+ { QString("BTRFS"), FilesystemType::BTRFS },
+ { QString("NFS"), FilesystemType::NFS },
+ { QString("ZFS"), FilesystemType::ZFS },
+ { QString("APFS"), FilesystemType::APFS },
+ { QString("HFSPLUS"), FilesystemType::HFSPLUS },
+ { QString("HFSX"), FilesystemType::HFSX },
+ { QString("HFS"), FilesystemType::HFS },
+ { QString("FUSEBLK"), FilesystemType::FUSEBLK },
+ { QString("F2FS"), FilesystemType::F2FS },
+ { QString("UNKNOWN"), FilesystemType::UNKNOWN }
};
/**
* @brief Get the string name of Filesystem enum object
- *
- * @param type
- * @return QString
+ *
+ * @param type
+ * @return QString
*/
QString getFilesystemTypeName(FilesystemType type);
/**
* @brief Get the Filesystem enum object from a name
- * Does a lookup of the type name and returns an exact match
+ * Does a lookup of the type name and returns an exact match
*
- * @param name
- * @return FilesystemType
+ * @param name
+ * @return FilesystemType
*/
FilesystemType getFilesystemType(const QString& name);
/**
* @brief Get the Filesystem enum object from a name
* Does a fuzzy lookup of the type name and returns an apropreate match
- *
- * @param name
- * @return FilesystemType
+ *
+ * @param name
+ * @return FilesystemType
*/
FilesystemType getFilesystemTypeFuzzy(const QString& name);
-
struct FilesystemInfo {
FilesystemType fsType = FilesystemType::UNKNOWN;
QString fsTypeName;
- int blockSize;
- qint64 bytesAvailable;
- qint64 bytesFree;
- qint64 bytesTotal;
+ int blockSize;
+ qint64 bytesAvailable;
+ qint64 bytesFree;
+ qint64 bytesTotal;
QString name;
QString rootPath;
};
/**
- * @brief path to the near ancestor that exsists
- *
+ * @brief path to the near ancestor that exists
+ *
*/
QString NearestExistentAncestor(const QString& path);
/**
* @brief colect information about the filesystem under a file
- *
+ *
*/
FilesystemInfo statFS(const QString& path);
-static const QList s_clone_filesystems = {
- FilesystemType::BTRFS, FilesystemType::APFS, FilesystemType::ZFS,
- FilesystemType::XFS, FilesystemType::REFS
-};
+static const QList s_clone_filesystems = { FilesystemType::BTRFS, FilesystemType::APFS, FilesystemType::ZFS,
+ FilesystemType::XFS, FilesystemType::REFS };
/**
- * @brief if the Filesystem is reflink/clone capable
- *
+ * @brief if the Filesystem is reflink/clone capable
+ *
*/
bool canCloneOnFS(const QString& path);
bool canCloneOnFS(const FilesystemInfo& info);
@@ -488,13 +473,13 @@ bool canCloneOnFS(FilesystemType type);
/**
* @brief if the Filesystems are reflink/clone capable and both are on the same device
- *
+ *
*/
bool canClone(const QString& src, const QString& dst);
/**
* @brief Copies a directory and it's contents from src to dest
- */
+ */
class clone : public QObject {
Q_OBJECT
public:
@@ -535,7 +520,7 @@ class clone : public QObject {
/**
* @brief clone/reflink file from src to dst
- *
+ *
*/
bool clone_file(const QString& src, const QString& dst, std::error_code& ec);
@@ -552,8 +537,8 @@ static const QList s_non_link_filesystems = {
};
/**
- * @brief if the Filesystem is symlink capable
- *
+ * @brief if the Filesystem is symlink capable
+ *
*/
bool canLinkOnFS(const QString& path);
bool canLinkOnFS(const FilesystemInfo& info);
@@ -561,10 +546,10 @@ bool canLinkOnFS(FilesystemType type);
/**
* @brief if the Filesystem is symlink capable on both ends
- *
+ *
*/
bool canLink(const QString& src, const QString& dst);
uintmax_t hardLinkCount(const QString& path);
-}
+} // namespace FS
diff --git a/launcher/InstanceCopyTask.cpp b/launcher/InstanceCopyTask.cpp
index 6bd56de3..4ac3b51a 100644
--- a/launcher/InstanceCopyTask.cpp
+++ b/launcher/InstanceCopyTask.cpp
@@ -1,10 +1,10 @@
#include "InstanceCopyTask.h"
-#include "settings/INISettingsObject.h"
+#include
+#include
#include "FileSystem.h"
#include "NullInstance.h"
#include "pathmatcher/RegexpMatcher.h"
-#include
-#include
+#include "settings/INISettingsObject.h"
InstanceCopyTask::InstanceCopyTask(InstancePtr origInstance, const InstanceCopyPrefs& prefs)
{
@@ -15,17 +15,17 @@ InstanceCopyTask::InstanceCopyTask(InstancePtr origInstance, const InstanceCopyP
m_useHardLinks = prefs.isLinkRecursivelyEnabled() && prefs.isUseHardLinksEnabled();
m_copySaves = prefs.isLinkRecursivelyEnabled() && prefs.isDontLinkSavesEnabled() && prefs.isCopySavesEnabled();
m_useClone = prefs.isUseCloneEnabled();
-
+
QString filters = prefs.getSelectedFiltersAsRegex();
if (m_useLinks || m_useHardLinks) {
- if (!filters.isEmpty()) filters += "|";
+ if (!filters.isEmpty())
+ filters += "|";
filters += "instance.cfg";
- }
+ }
qDebug() << "CopyFilters:" << filters;
- if (!filters.isEmpty())
- {
+ if (!filters.isEmpty()) {
// Set regex filter:
// FIXME: get this from the original instance type...
auto matcherReal = new RegexpMatcher(filters);
@@ -38,14 +38,14 @@ void InstanceCopyTask::executeTask()
{
setStatus(tr("Copying instance %1").arg(m_origInstance->name()));
- auto copySaves = [&](){
- FS::copy savesCopy(FS::PathCombine(m_origInstance->instanceRoot(), "saves") , FS::PathCombine(m_stagingPath, "saves"));
+ auto copySaves = [&]() {
+ FS::copy savesCopy(FS::PathCombine(m_origInstance->instanceRoot(), "saves"), FS::PathCombine(m_stagingPath, "saves"));
savesCopy.followSymlinks(true);
return savesCopy();
};
- m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this, copySaves]{
+ m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this, copySaves] {
if (m_useClone) {
FS::clone folderClone(m_origInstance->instanceRoot(), m_stagingPath);
folderClone.matcher(m_matcher.get());
@@ -53,22 +53,22 @@ void InstanceCopyTask::executeTask()
return folderClone();
} else if (m_useLinks || m_useHardLinks) {
FS::create_link folderLink(m_origInstance->instanceRoot(), m_stagingPath);
- int depth = m_linkRecursively ? -1 : 0; // we need to at least link the top level instead of the instance folder
+ int depth = m_linkRecursively ? -1 : 0; // we need to at least link the top level instead of the instance folder
folderLink.linkRecursively(true).setMaxDepth(depth).useHardLinks(m_useHardLinks).matcher(m_matcher.get());
bool there_were_errors = false;
- if(!folderLink()){
+ if (!folderLink()) {
#if defined Q_OS_WIN32
if (!m_useHardLinks) {
qDebug() << "EXPECTED: Link failure, Windows requires permissions for symlinks";
- qDebug() << "atempting to run with privelage";
+ qDebug() << "attempting to run with privelage";
QEventLoop loop;
bool got_priv_results = false;
- connect(&folderLink, &FS::create_link::finishedPrivileged, this, [&](bool gotResults){
+ connect(&folderLink, &FS::create_link::finishedPrivileged, this, [&](bool gotResults) {
if (!gotResults) {
qDebug() << "Privileged run exited without results!";
}
@@ -77,7 +77,7 @@ void InstanceCopyTask::executeTask()
});
folderLink.runPrivileged();
- loop.exec(); // wait for the finished signal
+ loop.exec(); // wait for the finished signal
for (auto result : folderLink.getResults()) {
if (result.err_value != 0) {
@@ -88,17 +88,17 @@ void InstanceCopyTask::executeTask()
if (m_copySaves) {
there_were_errors |= !copySaves();
}
-
+
return got_priv_results && !there_were_errors;
} else {
qDebug() << "Link Failed!" << folderLink.getOSError().value() << folderLink.getOSError().message().c_str();
- }
+ }
#else
qDebug() << "Link Failed!" << folderLink.getOSError().value() << folderLink.getOSError().message().c_str();
-#endif
+#endif
return false;
}
-
+
if (m_copySaves) {
there_were_errors |= !copySaves();
}
@@ -119,8 +119,7 @@ void InstanceCopyTask::executeTask()
void InstanceCopyTask::copyFinished()
{
auto successful = m_copyFuture.result();
- if(!successful)
- {
+ if (!successful) {
emitFailed(tr("Instance folder copy failed."));
return;
}
@@ -130,7 +129,7 @@ void InstanceCopyTask::copyFinished()
InstancePtr inst(new NullInstance(m_globalSettings, instanceSettings, m_stagingPath));
inst->setName(name());
inst->setIconKey(m_instIcon);
- if(!m_keepPlaytime) {
+ if (!m_keepPlaytime) {
inst->resetTimePlayed();
}
if (m_useLinks)
diff --git a/launcher/filelink/FileLink.cpp b/launcher/filelink/FileLink.cpp
index a731ecdb..ded46061 100644
--- a/launcher/filelink/FileLink.cpp
+++ b/launcher/filelink/FileLink.cpp
@@ -25,7 +25,6 @@
#include "StringUtils.h"
-
#include
#include
@@ -41,53 +40,47 @@
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
-#include
#include
+#include
#endif
// Snippet from https://github.com/gulrak/filesystem#using-it-as-single-file-header
#ifdef __APPLE__
-#include // for deployment target to support pre-catalina targets without std::fs
-#endif // __APPLE__
+#include // for deployment target to support pre-catalina targets without std::fs
+#endif // __APPLE__
#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || (defined(__cplusplus) && __cplusplus >= 201703L)) && defined(__has_include)
#if __has_include() && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500)
#define GHC_USE_STD_FS
#include
namespace fs = std::filesystem;
-#endif // MacOS min version check
-#endif // Other OSes version check
+#endif // MacOS min version check
+#endif // Other OSes version check
#ifndef GHC_USE_STD_FS
#include
namespace fs = ghc::filesystem;
#endif
-
-
-FileLinkApp::FileLinkApp(int &argc, char **argv) : QCoreApplication(argc, argv), socket(new QLocalSocket(this))
+FileLinkApp::FileLinkApp(int& argc, char** argv) : QCoreApplication(argc, argv), socket(new QLocalSocket(this))
{
#if defined Q_OS_WIN32
// attach the parent console
- if(AttachConsole(ATTACH_PARENT_PROCESS))
- {
+ if (AttachConsole(ATTACH_PARENT_PROCESS)) {
// if attach succeeds, reopen and sync all the i/o
- if(freopen("CON", "w", stdout))
- {
+ if (freopen("CON", "w", stdout)) {
std::cout.sync_with_stdio();
}
- if(freopen("CON", "w", stderr))
- {
+ if (freopen("CON", "w", stderr)) {
std::cerr.sync_with_stdio();
}
- if(freopen("CON", "r", stdin))
- {
+ if (freopen("CON", "r", stdin)) {
std::cin.sync_with_stdio();
}
- auto out = GetStdHandle (STD_OUTPUT_HANDLE);
+ auto out = GetStdHandle(STD_OUTPUT_HANDLE);
DWORD written;
- const char * endline = "\n";
+ const char* endline = "\n";
WriteConsole(out, endline, strlen(endline), &written, NULL);
consoleAttached = true;
}
@@ -101,10 +94,8 @@ FileLinkApp::FileLinkApp(int &argc, char **argv) : QCoreApplication(argc, argv),
QCommandLineParser parser;
parser.setApplicationDescription(QObject::tr("a batch MKLINK program for windows to be used with prismlauncher"));
- parser.addOptions({
- {{"s", "server"}, "Join the specified server on launch", "pipe name"},
- {{"H", "hard"}, "use hard links insted of symbolic", "true/false"}
- });
+ parser.addOptions({ { { "s", "server" }, "Join the specified server on launch", "pipe name" },
+ { { "H", "hard" }, "use hard links instead of symbolic", "true/false" } });
parser.addHelpOption();
parser.addVersionOption();
@@ -122,63 +113,57 @@ FileLinkApp::FileLinkApp(int &argc, char **argv) : QCoreApplication(argc, argv),
qDebug() << "no server to join";
exit();
}
-
}
void FileLinkApp::joinServer(QString server)
{
-
- blockSize = 0;
+ blockSize = 0;
in.setDevice(&socket);
in.setVersion(QDataStream::Qt_5_0);
- connect(&socket, &QLocalSocket::connected, this, [&](){
- qDebug() << "connected to server";
- });
+ connect(&socket, &QLocalSocket::connected, this, [&]() { qDebug() << "connected to server"; });
connect(&socket, &QLocalSocket::readyRead, this, &FileLinkApp::readPathPairs);
- connect(&socket, &QLocalSocket::errorOccurred, this, [&](QLocalSocket::LocalSocketError socketError){
+ connect(&socket, &QLocalSocket::errorOccurred, this, [&](QLocalSocket::LocalSocketError socketError) {
switch (socketError) {
- case QLocalSocket::ServerNotFoundError:
- qDebug() << ("The host was not found. Please make sure "
+ case QLocalSocket::ServerNotFoundError:
+ qDebug()
+ << ("The host was not found. Please make sure "
"that the server is running and that the "
"server name is correct.");
- break;
- case QLocalSocket::ConnectionRefusedError:
- qDebug() << ("The connection was refused by the peer. "
+ break;
+ case QLocalSocket::ConnectionRefusedError:
+ qDebug()
+ << ("The connection was refused by the peer. "
"Make sure the server is running, "
"and check that the server name "
"is correct.");
- break;
- case QLocalSocket::PeerClosedError:
- qDebug() << ("The connection was closed by the peer. ");
- break;
- default:
- qDebug() << "The following error occurred: " << socket.errorString();
+ break;
+ case QLocalSocket::PeerClosedError:
+ qDebug() << ("The connection was closed by the peer. ");
+ break;
+ default:
+ qDebug() << "The following error occurred: " << socket.errorString();
}
});
- connect(&socket, &QLocalSocket::disconnected, this, [&](){
- qDebug() << "dissconnected from server, should exit";
+ connect(&socket, &QLocalSocket::disconnected, this, [&]() {
+ qDebug() << "disconnected from server, should exit";
exit();
});
socket.connectToServer(server);
-
-
}
void FileLinkApp::runLink()
-{
-
+{
std::error_code os_err;
qDebug() << "creating links";
for (auto link : m_links_to_make) {
-
QString src_path = link.src;
QString dst_path = link.dst;
@@ -192,26 +177,25 @@ void FileLinkApp::runLink()
} else {
qDebug() << "making symlink:" << src_path << "to" << dst_path;
fs::create_symlink(StringUtils::toStdString(src_path), StringUtils::toStdString(dst_path), os_err);
- }
+ }
if (os_err) {
qWarning() << "Failed to link files:" << QString::fromStdString(os_err.message());
qDebug() << "Source file:" << src_path;
qDebug() << "Destination file:" << dst_path;
- qDebug() << "Error catagory:" << os_err.category().name();
+ qDebug() << "Error category:" << os_err.category().name();
qDebug() << "Error code:" << os_err.value();
- FS::LinkResult result = {src_path, dst_path, QString::fromStdString(os_err.message()), os_err.value()};
+ FS::LinkResult result = { src_path, dst_path, QString::fromStdString(os_err.message()), os_err.value() };
m_path_results.append(result);
} else {
- FS::LinkResult result = {src_path, dst_path};
+ FS::LinkResult result = { src_path, dst_path };
m_path_results.append(result);
}
}
sendResults();
qDebug() << "done, should exit soon";
-
}
void FileLinkApp::sendResults()
@@ -245,10 +229,10 @@ void FileLinkApp::sendResults()
}
void FileLinkApp::readPathPairs()
-{
+{
m_links_to_make.clear();
qDebug() << "Reading path pairs from server";
- qDebug() << "bytes avalible" << socket.bytesAvailable();
+ qDebug() << "bytes available" << socket.bytesAvailable();
if (blockSize == 0) {
// Relies on the fact that QDataStream serializes a quint32 into
// sizeof(quint32) bytes
@@ -258,15 +242,15 @@ void FileLinkApp::readPathPairs()
in >> blockSize;
}
qDebug() << "blocksize is" << blockSize;
- qDebug() << "bytes avalible" << socket.bytesAvailable();
+ qDebug() << "bytes available" << socket.bytesAvailable();
if (socket.bytesAvailable() < blockSize || in.atEnd())
return;
-
+
quint32 numLinks;
in >> numLinks;
qDebug() << "numLinks" << numLinks;
- for(int i = 0; i < numLinks; i++) {
+ for (int i = 0; i < numLinks; i++) {
FS::LinkPair pair;
in >> pair.src;
in >> pair.dst;
@@ -277,17 +261,15 @@ void FileLinkApp::readPathPairs()
runLink();
}
-
FileLinkApp::~FileLinkApp()
-{
+{
qDebug() << "link program shutting down";
// Shut down logger by setting the logger function to nothing
qInstallMessageHandler(nullptr);
#if defined Q_OS_WIN32
// Detach from Windows console
- if(consoleAttached)
- {
+ if (consoleAttached) {
fclose(stdout);
fclose(stdin);
fclose(stderr);
@@ -295,7 +277,3 @@ FileLinkApp::~FileLinkApp()
}
#endif
}
-
-
-
-
diff --git a/launcher/filelink/FileLink.h b/launcher/filelink/FileLink.h
index d146b8d9..4c47d9bb 100644
--- a/launcher/filelink/FileLink.h
+++ b/launcher/filelink/FileLink.h
@@ -25,30 +25,26 @@
#include
#include
-#include
+#include
+#include
#include
#include
#include
-#include
-#include
-#include
-#include
#include
+#include
+#include
#define PRISM_EXTERNAL_EXE
#include "FileSystem.h"
-class FileLinkApp : public QCoreApplication
-{
+class FileLinkApp : public QCoreApplication {
// friends for the purpose of limiting access to deprecated stuff
Q_OBJECT
-public:
-
- FileLinkApp(int &argc, char **argv);
+ public:
+ FileLinkApp(int& argc, char** argv);
virtual ~FileLinkApp();
-private:
-
+ private:
void joinServer(QString server);
void readPathPairs();
void runLink();
diff --git a/launcher/filelink/main.cpp b/launcher/filelink/main.cpp
index 4a22ff18..83566a3c 100644
--- a/launcher/filelink/main.cpp
+++ b/launcher/filelink/main.cpp
@@ -22,10 +22,9 @@
#include "FileLink.h"
-int main(int argc, char *argv[])
+int main(int argc, char* argv[])
{
-
FileLinkApp ldh(argc, argv);
-
+
return ldh.exec();
}
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.cpp b/launcher/ui/dialogs/CopyInstanceDialog.cpp
index ced57ae0..347bd39f 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.cpp
+++ b/launcher/ui/dialogs/CopyInstanceDialog.cpp
@@ -43,15 +43,15 @@
#include "ui/dialogs/IconPickerDialog.h"
-#include "BaseVersion.h"
-#include "icons/IconList.h"
#include "BaseInstance.h"
-#include "InstanceList.h"
-#include "FileSystem.h"
+#include "BaseVersion.h"
#include "DesktopServices.h"
+#include "FileSystem.h"
+#include "InstanceList.h"
+#include "icons/IconList.h"
-CopyInstanceDialog::CopyInstanceDialog(InstancePtr original, QWidget *parent)
- :QDialog(parent), ui(new Ui::CopyInstanceDialog), m_original(original)
+CopyInstanceDialog::CopyInstanceDialog(InstancePtr original, QWidget* parent)
+ : QDialog(parent), ui(new Ui::CopyInstanceDialog), m_original(original)
{
ui->setupUi(this);
resize(minimumSizeHint());
@@ -74,8 +74,7 @@ CopyInstanceDialog::CopyInstanceDialog(InstancePtr original, QWidget *parent)
groupList.push_front("");
ui->groupBox->addItems(groupList);
int index = groupList.indexOf(APPLICATION->instances()->getInstanceGroup(m_original->id()));
- if(index == -1)
- {
+ if (index == -1) {
index = 0;
}
ui->groupBox->setCurrentIndex(index);
@@ -108,10 +107,8 @@ CopyInstanceDialog::CopyInstanceDialog(InstancePtr original, QWidget *parent)
#if defined(Q_OS_WIN)
ui->symbolicLinksCheckbox->setIcon(style()->standardIcon(QStyle::SP_VistaShield));
- ui->symbolicLinksCheckbox->setToolTip(
- tr("Use symbolic links instead of copying files.") +
- tr("\nOn windows symbolic links may require admin permision to create.")
- );
+ ui->symbolicLinksCheckbox->setToolTip(tr("Use symbolic links instead of copying files.") +
+ tr("\nOn windows symbolic links may require admin permission to create."));
#endif
updateLinkOptions();
@@ -119,7 +116,6 @@ CopyInstanceDialog::CopyInstanceDialog(InstancePtr original, QWidget *parent)
auto HelpButton = ui->buttonBox->button(QDialogButtonBox::Help);
connect(HelpButton, &QPushButton::clicked, this, &CopyInstanceDialog::help);
-
}
CopyInstanceDialog::~CopyInstanceDialog()
@@ -131,8 +127,7 @@ void CopyInstanceDialog::updateDialogState()
{
auto allowOK = !instName().isEmpty();
auto OkButton = ui->buttonBox->button(QDialogButtonBox::Ok);
- if(OkButton->isEnabled() != allowOK)
- {
+ if (OkButton->isEnabled() != allowOK) {
OkButton->setEnabled(allowOK);
}
}
@@ -140,8 +135,7 @@ void CopyInstanceDialog::updateDialogState()
QString CopyInstanceDialog::instName() const
{
auto result = ui->instNameTextBox->text().trimmed();
- if(result.size())
- {
+ if (result.size()) {
return result;
}
return QString();
@@ -162,7 +156,6 @@ const InstanceCopyPrefs& CopyInstanceDialog::getChosenOptions() const
return m_selectedOptions;
}
-
void CopyInstanceDialog::help()
{
DesktopServices::openUrl(QUrl(BuildConfig.HELP_URL.arg("instance-copy")));
@@ -200,7 +193,8 @@ void CopyInstanceDialog::updateLinkOptions()
ui->symbolicLinksCheckbox->setEnabled(m_linkSupported && !ui->hardLinksCheckbox->isChecked() && !ui->useCloneCheckbox->isChecked());
ui->hardLinksCheckbox->setEnabled(m_linkSupported && !ui->symbolicLinksCheckbox->isChecked() && !ui->useCloneCheckbox->isChecked());
- ui->symbolicLinksCheckbox->setChecked(m_linkSupported && m_selectedOptions.isUseSymLinksEnabled() && !ui->useCloneCheckbox->isChecked());
+ ui->symbolicLinksCheckbox->setChecked(m_linkSupported && m_selectedOptions.isUseSymLinksEnabled() &&
+ !ui->useCloneCheckbox->isChecked());
ui->hardLinksCheckbox->setChecked(m_linkSupported && m_selectedOptions.isUseHardLinksEnabled() && !ui->useCloneCheckbox->isChecked());
bool linksInUse = (ui->symbolicLinksCheckbox->isChecked() || ui->hardLinksCheckbox->isChecked());
@@ -220,15 +214,13 @@ void CopyInstanceDialog::on_iconButton_clicked()
IconPickerDialog dlg(this);
dlg.execWithSelection(InstIconKey);
- if (dlg.result() == QDialog::Accepted)
- {
+ if (dlg.result() == QDialog::Accepted) {
InstIconKey = dlg.selectedIconKey;
ui->iconButton->setIcon(APPLICATION->icons()->getIcon(InstIconKey));
}
}
-
-void CopyInstanceDialog::on_instNameTextBox_textChanged(const QString &arg1)
+void CopyInstanceDialog::on_instNameTextBox_textChanged(const QString& arg1)
{
updateDialogState();
}
@@ -247,7 +239,6 @@ void CopyInstanceDialog::on_copySavesCheckbox_stateChanged(int state)
updateSelectAllCheckbox();
}
-
void CopyInstanceDialog::on_keepPlaytimeCheckbox_stateChanged(int state)
{
m_selectedOptions.enableKeepPlaytime(state == Qt::Checked);
@@ -311,7 +302,6 @@ void CopyInstanceDialog::on_recursiveLinkCheckbox_stateChanged(int state)
{
m_selectedOptions.enableLinkRecursively(state == Qt::Checked);
updateLinkOptions();
-
}
void CopyInstanceDialog::on_dontLinkSavesCheckbox_stateChanged(int state)
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.h b/launcher/ui/dialogs/CopyInstanceDialog.h
index c447bee9..698c6e93 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.h
+++ b/launcher/ui/dialogs/CopyInstanceDialog.h
@@ -22,17 +22,15 @@
class BaseInstance;
-namespace Ui
-{
+namespace Ui {
class CopyInstanceDialog;
}
-class CopyInstanceDialog : public QDialog
-{
+class CopyInstanceDialog : public QDialog {
Q_OBJECT
-public:
- explicit CopyInstanceDialog(InstancePtr original, QWidget *parent = 0);
+ public:
+ explicit CopyInstanceDialog(InstancePtr original, QWidget* parent = 0);
~CopyInstanceDialog();
void updateDialogState();
@@ -42,13 +40,12 @@ public:
QString iconKey() const;
const InstanceCopyPrefs& getChosenOptions() const;
-public slots:
+ public slots:
void help();
-private
-slots:
+ private slots:
void on_iconButton_clicked();
- void on_instNameTextBox_textChanged(const QString &arg1);
+ void on_instNameTextBox_textChanged(const QString& arg1);
// Checkboxes
void on_selectAllCheckbox_stateChanged(int state);
void on_copySavesCheckbox_stateChanged(int state);
@@ -65,14 +62,14 @@ slots:
void on_dontLinkSavesCheckbox_stateChanged(int state);
void on_useCloneCheckbox_stateChanged(int state);
-private:
+ private:
void checkAllCheckboxes(const bool& b);
void updateSelectAllCheckbox();
void updateUseCloneCheckbox();
void updateLinkOptions();
/* data */
- Ui::CopyInstanceDialog *ui;
+ Ui::CopyInstanceDialog* ui;
QString InstIconKey;
InstancePtr m_original;
InstanceCopyPrefs m_selectedOptions;
--
cgit
From ff07714e8c955003bb3deec84ec6cce6fc3ef5e6 Mon Sep 17 00:00:00 2001
From: Sefa Eyeoglu
Date: Thu, 27 Apr 2023 16:01:40 +0200
Subject: chore: remove FTB modpack support
We have been contacted by Feed the Beast to drop support for the FTB
modpack browser from Prism Launcher.
Signed-off-by: Sefa Eyeoglu
---
launcher/CMakeLists.txt | 16 -
.../modplatform/modpacksch/FTBPackInstallTask.cpp | 387 ---------------------
.../modplatform/modpacksch/FTBPackInstallTask.h | 101 ------
.../modplatform/modpacksch/FTBPackManifest.cpp | 195 -----------
launcher/modplatform/modpacksch/FTBPackManifest.h | 168 ---------
launcher/ui/dialogs/NewInstanceDialog.cpp | 2 -
.../ui/pages/modplatform/ftb/FtbFilterModel.cpp | 93 -----
launcher/ui/pages/modplatform/ftb/FtbFilterModel.h | 51 ---
launcher/ui/pages/modplatform/ftb/FtbListModel.cpp | 304 ----------------
launcher/ui/pages/modplatform/ftb/FtbListModel.h | 83 -----
launcher/ui/pages/modplatform/ftb/FtbPage.cpp | 199 -----------
launcher/ui/pages/modplatform/ftb/FtbPage.h | 105 ------
launcher/ui/pages/modplatform/ftb/FtbPage.ui | 86 -----
13 files changed, 1790 deletions(-)
delete mode 100644 launcher/modplatform/modpacksch/FTBPackInstallTask.cpp
delete mode 100644 launcher/modplatform/modpacksch/FTBPackInstallTask.h
delete mode 100644 launcher/modplatform/modpacksch/FTBPackManifest.cpp
delete mode 100644 launcher/modplatform/modpacksch/FTBPackManifest.h
delete mode 100644 launcher/ui/pages/modplatform/ftb/FtbFilterModel.cpp
delete mode 100644 launcher/ui/pages/modplatform/ftb/FtbFilterModel.h
delete mode 100644 launcher/ui/pages/modplatform/ftb/FtbListModel.cpp
delete mode 100644 launcher/ui/pages/modplatform/ftb/FtbListModel.h
delete mode 100644 launcher/ui/pages/modplatform/ftb/FtbPage.cpp
delete mode 100644 launcher/ui/pages/modplatform/ftb/FtbPage.h
delete mode 100644 launcher/ui/pages/modplatform/ftb/FtbPage.ui
(limited to 'launcher/ui/dialogs')
diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt
index 074570e3..ee36175f 100644
--- a/launcher/CMakeLists.txt
+++ b/launcher/CMakeLists.txt
@@ -524,13 +524,6 @@ set(MODRINTH_SOURCES
modplatform/modrinth/ModrinthInstanceCreationTask.h
)
-set(MODPACKSCH_SOURCES
- modplatform/modpacksch/FTBPackInstallTask.h
- modplatform/modpacksch/FTBPackInstallTask.cpp
- modplatform/modpacksch/FTBPackManifest.h
- modplatform/modpacksch/FTBPackManifest.cpp
-)
-
set(PACKWIZ_SOURCES
modplatform/packwiz/Packwiz.h
modplatform/packwiz/Packwiz.cpp
@@ -599,7 +592,6 @@ set(LOGIC_SOURCES
${FTB_SOURCES}
${FLAME_SOURCES}
${MODRINTH_SOURCES}
- ${MODPACKSCH_SOURCES}
${PACKWIZ_SOURCES}
${TECHNIC_SOURCES}
${ATLAUNCHER_SOURCES}
@@ -797,13 +789,6 @@ SET(LAUNCHER_SOURCES
ui/pages/modplatform/atlauncher/AtlUserInteractionSupportImpl.cpp
ui/pages/modplatform/atlauncher/AtlUserInteractionSupportImpl.h
- ui/pages/modplatform/ftb/FtbFilterModel.cpp
- ui/pages/modplatform/ftb/FtbFilterModel.h
- ui/pages/modplatform/ftb/FtbListModel.cpp
- ui/pages/modplatform/ftb/FtbListModel.h
- ui/pages/modplatform/ftb/FtbPage.cpp
- ui/pages/modplatform/ftb/FtbPage.h
-
ui/pages/modplatform/legacy_ftb/Page.cpp
ui/pages/modplatform/legacy_ftb/Page.h
ui/pages/modplatform/legacy_ftb/ListModel.h
@@ -978,7 +963,6 @@ qt_wrap_ui(LAUNCHER_UI
ui/pages/modplatform/flame/FlamePage.ui
ui/pages/modplatform/legacy_ftb/Page.ui
ui/pages/modplatform/ImportPage.ui
- ui/pages/modplatform/ftb/FtbPage.ui
ui/pages/modplatform/modrinth/ModrinthPage.ui
ui/pages/modplatform/technic/TechnicPage.ui
ui/widgets/InstanceCardWidget.ui
diff --git a/launcher/modplatform/modpacksch/FTBPackInstallTask.cpp b/launcher/modplatform/modpacksch/FTBPackInstallTask.cpp
deleted file mode 100644
index 68d4751c..00000000
--- a/launcher/modplatform/modpacksch/FTBPackInstallTask.cpp
+++ /dev/null
@@ -1,387 +0,0 @@
-// SPDX-License-Identifier: GPL-3.0-only
-/*
- * Prism Launcher - Minecraft Launcher
- * Copyright (C) 2022 flowln
- * Copyright (c) 2022 Jamie Mansfield
- * Copyright (C) 2022 Sefa Eyeoglu
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, version 3.
- *
- * This program 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 this program. If not, see .
- *
- * This file incorporates work covered by the following copyright and
- * permission notice:
- *
- * Copyright 2020-2021 Jamie Mansfield
- * Copyright 2020-2021 Petr Mrazek
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "FTBPackInstallTask.h"
-
-#include "FileSystem.h"
-#include "Json.h"
-#include "minecraft/MinecraftInstance.h"
-#include "minecraft/PackProfile.h"
-#include "modplatform/flame/PackManifest.h"
-#include "net/ChecksumValidator.h"
-#include "settings/INISettingsObject.h"
-
-#include "Application.h"
-#include "BuildConfig.h"
-#include "ui/dialogs/BlockedModsDialog.h"
-
-namespace ModpacksCH {
-
-PackInstallTask::PackInstallTask(Modpack pack, QString version, QWidget* parent)
- : m_pack(std::move(pack)), m_version_name(std::move(version)), m_parent(parent)
-{}
-
-bool PackInstallTask::abort()
-{
- if (!canAbort())
- return false;
-
- bool aborted = true;
-
- if (m_net_job)
- aborted &= m_net_job->abort();
- if (m_mod_id_resolver_task)
- aborted &= m_mod_id_resolver_task->abort();
-
- return aborted ? InstanceTask::abort() : false;
-}
-
-void PackInstallTask::executeTask()
-{
- setStatus(tr("Getting the manifest..."));
- setAbortable(false);
-
- // Find pack version
- auto version_it = std::find_if(m_pack.versions.constBegin(), m_pack.versions.constEnd(),
- [this](ModpacksCH::VersionInfo const& a) { return a.name == m_version_name; });
-
- if (version_it == m_pack.versions.constEnd()) {
- emitFailed(tr("Failed to find pack version %1").arg(m_version_name));
- return;
- }
-
- auto version = *version_it;
-
- auto netJob = makeShared("ModpacksCH::VersionFetch", APPLICATION->network());
-
- auto searchUrl = QString(BuildConfig.MODPACKSCH_API_BASE_URL + "public/modpack/%1/%2").arg(m_pack.id).arg(version.id);
- netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), &m_response));
-
- QObject::connect(netJob.get(), &NetJob::succeeded, this, &PackInstallTask::onManifestDownloadSucceeded);
- QObject::connect(netJob.get(), &NetJob::failed, this, &PackInstallTask::onManifestDownloadFailed);
- QObject::connect(netJob.get(), &NetJob::aborted, this, &PackInstallTask::abort);
- QObject::connect(netJob.get(), &NetJob::progress, this, &PackInstallTask::setProgress);
-
- m_net_job = netJob;
-
- setAbortable(true);
- netJob->start();
-}
-
-void PackInstallTask::onManifestDownloadSucceeded()
-{
- m_net_job.reset();
-
- QJsonParseError parse_error{};
- QJsonDocument doc = QJsonDocument::fromJson(m_response, &parse_error);
- if (parse_error.error != QJsonParseError::NoError) {
- qWarning() << "Error while parsing JSON response from ModpacksCH at " << parse_error.offset
- << " reason: " << parse_error.errorString();
- qWarning() << m_response;
- return;
- }
-
- ModpacksCH::Version version;
- try {
- auto obj = Json::requireObject(doc);
- ModpacksCH::loadVersion(version, obj);
- } catch (const JSONValidationError& e) {
- emitFailed(tr("Could not understand pack manifest:\n") + e.cause());
- return;
- }
-
- m_version = version;
-
- resolveMods();
-}
-
-void PackInstallTask::resolveMods()
-{
- setStatus(tr("Resolving mods..."));
- setAbortable(false);
- setProgress(0, 100);
-
- m_file_id_map.clear();
-
- Flame::Manifest manifest;
- int index = 0;
-
- for (auto const& file : m_version.files) {
- if (!file.serverOnly && file.url.isEmpty()) {
- if (file.curseforge.file_id <= 0) {
- emitFailed(tr("Invalid manifest: There's no information available to download the file '%1'!").arg(file.name));
- return;
- }
-
- Flame::File flame_file;
- flame_file.projectId = file.curseforge.project_id;
- flame_file.fileId = file.curseforge.file_id;
- flame_file.hash = file.sha1;
-
- manifest.files.insert(flame_file.fileId, flame_file);
- m_file_id_map.append(flame_file.fileId);
- } else {
- m_file_id_map.append(-1);
- }
-
- index++;
- }
-
- m_mod_id_resolver_task.reset(new Flame::FileResolvingTask(APPLICATION->network(), manifest));
-
- connect(m_mod_id_resolver_task.get(), &Flame::FileResolvingTask::succeeded, this, &PackInstallTask::onResolveModsSucceeded);
- connect(m_mod_id_resolver_task.get(), &Flame::FileResolvingTask::failed, this, &PackInstallTask::onResolveModsFailed);
- connect(m_mod_id_resolver_task.get(), &Flame::FileResolvingTask::aborted, this, &PackInstallTask::abort);
- connect(m_mod_id_resolver_task.get(), &Flame::FileResolvingTask::progress, this, &PackInstallTask::setProgress);
-
- setAbortable(true);
-
- m_mod_id_resolver_task->start();
-}
-
-void PackInstallTask::onResolveModsSucceeded()
-{
- auto anyBlocked = false;
-
- Flame::Manifest results = m_mod_id_resolver_task->getResults();
- for (int index = 0; index < m_file_id_map.size(); index++) {
- auto const file_id = m_file_id_map.at(index);
- if (file_id < 0)
- continue;
-
- Flame::File results_file = results.files[file_id];
- VersionFile& local_file = m_version.files[index];
-
- // First check for blocked mods
- if (!results_file.resolved || results_file.url.isEmpty()) {
- BlockedMod blocked_mod;
- blocked_mod.name = local_file.name;
- blocked_mod.websiteUrl = results_file.websiteUrl;
- blocked_mod.hash = results_file.hash;
- blocked_mod.matched = false;
- blocked_mod.localPath = "";
- blocked_mod.targetFolder = results_file.targetFolder;
-
- m_blocked_mods.append(blocked_mod);
-
- anyBlocked = true;
- } else {
- local_file.url = results_file.url.toString();
- }
- }
-
- m_mod_id_resolver_task.reset();
-
- if (anyBlocked) {
- qDebug() << "Blocked files found, displaying file list";
-
- BlockedModsDialog message_dialog(m_parent, tr("Blocked files found"),
- tr("The following files are not available for download in third party launchers.
"
- "You will need to manually download them and add them to the instance."),
- m_blocked_mods);
-
- message_dialog.setModal(true);
-
- if (message_dialog.exec() == QDialog::Accepted) {
- qDebug() << "Post dialog blocked mods list: " << m_blocked_mods;
- createInstance();
- } else {
- abort();
- }
-
- } else {
- createInstance();
- }
-}
-
-void PackInstallTask::createInstance()
-{
- setAbortable(false);
-
- setStatus(tr("Creating the instance..."));
- QCoreApplication::processEvents();
-
- auto instanceConfigPath = FS::PathCombine(m_stagingPath, "instance.cfg");
- auto instanceSettings = std::make_shared(instanceConfigPath);
-
- MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
- auto components = instance.getPackProfile();
- components->buildingFromScratch();
-
- for (auto target : m_version.targets) {
- if (target.type == "game" && target.name == "minecraft") {
- components->setComponentVersion("net.minecraft", target.version, true);
- break;
- }
- }
-
- for (auto target : m_version.targets) {
- if (target.type != "modloader")
- continue;
-
- if (target.name == "forge") {
- components->setComponentVersion("net.minecraftforge", target.version);
- } else if (target.name == "fabric") {
- components->setComponentVersion("net.fabricmc.fabric-loader", target.version);
- }
- }
-
- // install any jar mods
- QDir jarModsDir(FS::PathCombine(m_stagingPath, "minecraft", "jarmods"));
- if (jarModsDir.exists()) {
- QStringList jarMods;
-
- for (const auto& info : jarModsDir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files)) {
- jarMods.push_back(info.absoluteFilePath());
- }
-
- components->installJarMods(jarMods);
- }
-
- components->saveNow();
-
- instance.setName(name());
- instance.setIconKey(m_instIcon);
- instance.setManagedPack("modpacksch", QString::number(m_pack.id), m_pack.name, QString::number(m_version.id), m_version.name);
-
- instance.saveNow();
-
- onCreateInstanceSucceeded();
-}
-
-void PackInstallTask::onCreateInstanceSucceeded()
-{
- downloadPack();
-}
-
-void PackInstallTask::downloadPack()
-{
- setStatus(tr("Downloading mods..."));
- setAbortable(false);
-
- auto jobPtr = makeShared(tr("Mod download"), APPLICATION->network());
- for (auto const& file : m_version.files) {
- if (file.serverOnly || file.url.isEmpty())
- continue;
-
- auto path = FS::PathCombine(m_stagingPath, ".minecraft", file.path, file.name);
- qDebug() << "Will try to download" << file.url << "to" << path;
-
- QFileInfo file_info(file.name);
-
- auto dl = Net::Download::makeFile(file.url, path);
- if (!file.sha1.isEmpty()) {
- auto rawSha1 = QByteArray::fromHex(file.sha1.toLatin1());
- dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawSha1));
- }
-
- jobPtr->addNetAction(dl);
- }
-
- connect(jobPtr.get(), &NetJob::succeeded, this, &PackInstallTask::onModDownloadSucceeded);
- connect(jobPtr.get(), &NetJob::failed, this, &PackInstallTask::onModDownloadFailed);
- connect(jobPtr.get(), &NetJob::aborted, this, &PackInstallTask::abort);
- connect(jobPtr.get(), &NetJob::progress, this, &PackInstallTask::setProgress);
-
- m_net_job = jobPtr;
-
- setAbortable(true);
- jobPtr->start();
-}
-
-void PackInstallTask::onModDownloadSucceeded()
-{
- m_net_job.reset();
- if (!m_blocked_mods.isEmpty()) {
- copyBlockedMods();
- }
- emitSucceeded();
-}
-
-void PackInstallTask::onManifestDownloadFailed(QString reason)
-{
- m_net_job.reset();
- emitFailed(reason);
-}
-void PackInstallTask::onResolveModsFailed(QString reason)
-{
- m_net_job.reset();
- emitFailed(reason);
-}
-void PackInstallTask::onCreateInstanceFailed(QString reason)
-{
- emitFailed(reason);
-}
-void PackInstallTask::onModDownloadFailed(QString reason)
-{
- m_net_job.reset();
- emitFailed(reason);
-}
-
-/// @brief copy the matched blocked mods to the instance staging area
-void PackInstallTask::copyBlockedMods()
-{
- setStatus(tr("Copying Blocked Mods..."));
- setAbortable(false);
- int i = 0;
- int total = m_blocked_mods.length();
- setProgress(i, total);
- for (auto const& mod : m_blocked_mods) {
- if (!mod.matched) {
- qDebug() << mod.name << "was not matched to a local file, skipping copy";
- continue;
- }
-
- auto dest_path = FS::PathCombine(m_stagingPath, ".minecraft", mod.targetFolder, mod.name);
-
- setStatus(tr("Copying Blocked Mods (%1 out of %2 are done)").arg(QString::number(i), QString::number(total)));
-
- qDebug() << "Will try to copy" << mod.localPath << "to" << dest_path;
-
- if (!FS::copy(mod.localPath, dest_path)()) {
- qDebug() << "Copy of" << mod.localPath << "to" << dest_path << "Failed";
- }
-
- i++;
- setProgress(i, total);
- }
-
- setAbortable(true);
-}
-
-} // namespace ModpacksCH
diff --git a/launcher/modplatform/modpacksch/FTBPackInstallTask.h b/launcher/modplatform/modpacksch/FTBPackInstallTask.h
deleted file mode 100644
index 97b1eb0b..00000000
--- a/launcher/modplatform/modpacksch/FTBPackInstallTask.h
+++ /dev/null
@@ -1,101 +0,0 @@
-// SPDX-License-Identifier: GPL-3.0-only
-/*
- * Prism Launcher - Minecraft Launcher
- * Copyright (C) 2022 flowln
- * Copyright (C) 2022 Sefa Eyeoglu
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, version 3.
- *
- * This program 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 this program. If not, see .
- *
- * This file incorporates work covered by the following copyright and
- * permission notice:
- *
- * Copyright 2020-2021 Jamie Mansfield
- * Copyright 2020-2021 Petr Mrazek
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "FTBPackManifest.h"
-
-#include "InstanceTask.h"
-#include "QObjectPtr.h"
-#include "modplatform/flame/FileResolvingTask.h"
-#include "net/NetJob.h"
-#include "ui/dialogs/BlockedModsDialog.h"
-
-#include
-
-namespace ModpacksCH {
-
-class PackInstallTask final : public InstanceTask
-{
- Q_OBJECT
-
-public:
- explicit PackInstallTask(Modpack pack, QString version, QWidget* parent = nullptr);
- ~PackInstallTask() override = default;
-
- bool abort() override;
-
-protected:
- void executeTask() override;
-
-private slots:
- void onManifestDownloadSucceeded();
- void onResolveModsSucceeded();
- void onCreateInstanceSucceeded();
- void onModDownloadSucceeded();
-
- void onManifestDownloadFailed(QString reason);
- void onResolveModsFailed(QString reason);
- void onCreateInstanceFailed(QString reason);
- void onModDownloadFailed(QString reason);
-
-private:
- void resolveMods();
- void createInstance();
- void downloadPack();
- void copyBlockedMods();
-
-private:
- NetJob::Ptr m_net_job = nullptr;
- shared_qobject_ptr m_mod_id_resolver_task = nullptr;
-
- QList m_file_id_map;
-
- QByteArray m_response;
-
- Modpack m_pack;
- QString m_version_name;
- Version m_version;
-
- QMap m_files_to_copy;
- QList m_blocked_mods;
-
- //FIXME: nuke
- QWidget* m_parent;
-};
-
-}
diff --git a/launcher/modplatform/modpacksch/FTBPackManifest.cpp b/launcher/modplatform/modpacksch/FTBPackManifest.cpp
deleted file mode 100644
index 421527ae..00000000
--- a/launcher/modplatform/modpacksch/FTBPackManifest.cpp
+++ /dev/null
@@ -1,195 +0,0 @@
-// SPDX-License-Identifier: GPL-3.0-only
-/*
- * PolyMC - Minecraft Launcher
- * Copyright (C) 2022 Sefa Eyeoglu
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, version 3.
- *
- * This program 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 this program. If not, see .
- *
- * This file incorporates work covered by the following copyright and
- * permission notice:
- *
- * Copyright 2020 Jamie Mansfield
- * Copyright 2020-2021 Petr Mrazek
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "FTBPackManifest.h"
-
-#include "Json.h"
-
-static void loadSpecs(ModpacksCH::Specs & s, QJsonObject & obj)
-{
- s.id = Json::requireInteger(obj, "id");
- s.minimum = Json::requireInteger(obj, "minimum");
- s.recommended = Json::requireInteger(obj, "recommended");
-}
-
-static void loadTag(ModpacksCH::Tag & t, QJsonObject & obj)
-{
- t.id = Json::requireInteger(obj, "id");
- t.name = Json::requireString(obj, "name");
-}
-
-static void loadArt(ModpacksCH::Art & a, QJsonObject & obj)
-{
- a.id = Json::requireInteger(obj, "id");
- a.url = Json::requireString(obj, "url");
- a.type = Json::requireString(obj, "type");
- a.width = Json::requireInteger(obj, "width");
- a.height = Json::requireInteger(obj, "height");
- a.compressed = Json::requireBoolean(obj, "compressed");
- a.sha1 = Json::requireString(obj, "sha1");
- a.size = Json::requireInteger(obj, "size");
- a.updated = Json::requireInteger(obj, "updated");
-}
-
-static void loadAuthor(ModpacksCH::Author & a, QJsonObject & obj)
-{
- a.id = Json::requireInteger(obj, "id");
- a.name = Json::requireString(obj, "name");
- a.type = Json::requireString(obj, "type");
- a.website = Json::requireString(obj, "website");
- a.updated = Json::requireInteger(obj, "updated");
-}
-
-static void loadVersionInfo(ModpacksCH::VersionInfo & v, QJsonObject & obj)
-{
- v.id = Json::requireInteger(obj, "id");
- v.name = Json::requireString(obj, "name");
- v.type = Json::requireString(obj, "type");
- v.updated = Json::requireInteger(obj, "updated");
- auto specs = Json::requireObject(obj, "specs");
- loadSpecs(v.specs, specs);
-}
-
-void ModpacksCH::loadModpack(ModpacksCH::Modpack & m, QJsonObject & obj)
-{
- m.id = Json::requireInteger(obj, "id");
- m.name = Json::requireString(obj, "name");
- m.synopsis = Json::requireString(obj, "synopsis");
- m.description = Json::requireString(obj, "description");
- m.type = Json::requireString(obj, "type");
- m.featured = Json::requireBoolean(obj, "featured");
- m.installs = Json::requireInteger(obj, "installs");
- m.plays = Json::requireInteger(obj, "plays");
- m.updated = Json::requireInteger(obj, "updated");
- m.refreshed = Json::requireInteger(obj, "refreshed");
- auto artArr = Json::requireArray(obj, "art");
- for (QJsonValueRef artRaw : artArr)
- {
- auto artObj = Json::requireObject(artRaw);
- ModpacksCH::Art art;
- loadArt(art, artObj);
- m.art.append(art);
- }
- auto authorArr = Json::requireArray(obj, "authors");
- for (QJsonValueRef authorRaw : authorArr)
- {
- auto authorObj = Json::requireObject(authorRaw);
- ModpacksCH::Author author;
- loadAuthor(author, authorObj);
- m.authors.append(author);
- }
- auto versionArr = Json::requireArray(obj, "versions");
- for (QJsonValueRef versionRaw : versionArr)
- {
- auto versionObj = Json::requireObject(versionRaw);
- ModpacksCH::VersionInfo version;
- loadVersionInfo(version, versionObj);
- m.versions.append(version);
- }
- auto tagArr = Json::requireArray(obj, "tags");
- for (QJsonValueRef tagRaw : tagArr)
- {
- auto tagObj = Json::requireObject(tagRaw);
- ModpacksCH::Tag tag;
- loadTag(tag, tagObj);
- m.tags.append(tag);
- }
- m.updated = Json::requireInteger(obj, "updated");
-}
-
-static void loadVersionTarget(ModpacksCH::VersionTarget & a, QJsonObject & obj)
-{
- a.id = Json::requireInteger(obj, "id");
- a.name = Json::requireString(obj, "name");
- a.type = Json::requireString(obj, "type");
- a.version = Json::requireString(obj, "version");
- a.updated = Json::requireInteger(obj, "updated");
-}
-
-static void loadVersionFile(ModpacksCH::VersionFile & a, QJsonObject & obj)
-{
- a.id = Json::requireInteger(obj, "id");
- a.type = Json::requireString(obj, "type");
- a.path = Json::requireString(obj, "path");
- a.name = Json::requireString(obj, "name");
- a.version = Json::requireString(obj, "version");
- a.url = Json::ensureString(obj, "url"); // optional
- a.sha1 = Json::requireString(obj, "sha1");
- a.size = Json::requireInteger(obj, "size");
- a.clientOnly = Json::requireBoolean(obj, "clientonly");
- a.serverOnly = Json::requireBoolean(obj, "serveronly");
- a.optional = Json::requireBoolean(obj, "optional");
- a.updated = Json::requireInteger(obj, "updated");
- auto curseforgeObj = Json::ensureObject(obj, "curseforge"); // optional
- a.curseforge.project_id = Json::ensureInteger(curseforgeObj, "project");
- a.curseforge.file_id = Json::ensureInteger(curseforgeObj, "file");
-}
-
-void ModpacksCH::loadVersion(ModpacksCH::Version & m, QJsonObject & obj)
-{
- m.id = Json::requireInteger(obj, "id");
- m.parent = Json::requireInteger(obj, "parent");
- m.name = Json::requireString(obj, "name");
- m.type = Json::requireString(obj, "type");
- m.installs = Json::requireInteger(obj, "installs");
- m.plays = Json::requireInteger(obj, "plays");
- m.updated = Json::requireInteger(obj, "updated");
- m.refreshed = Json::requireInteger(obj, "refreshed");
- auto specs = Json::requireObject(obj, "specs");
- loadSpecs(m.specs, specs);
- auto targetArr = Json::requireArray(obj, "targets");
- for (QJsonValueRef targetRaw : targetArr)
- {
- auto versionObj = Json::requireObject(targetRaw);
- ModpacksCH::VersionTarget target;
- loadVersionTarget(target, versionObj);
- m.targets.append(target);
- }
- auto fileArr = Json::requireArray(obj, "files");
- for (QJsonValueRef fileRaw : fileArr)
- {
- auto fileObj = Json::requireObject(fileRaw);
- ModpacksCH::VersionFile file;
- loadVersionFile(file, fileObj);
- m.files.append(file);
- }
-}
-
-//static void loadVersionChangelog(ModpacksCH::VersionChangelog & m, QJsonObject & obj)
-//{
-// m.content = Json::requireString(obj, "content");
-// m.updated = Json::requireInteger(obj, "updated");
-//}
diff --git a/launcher/modplatform/modpacksch/FTBPackManifest.h b/launcher/modplatform/modpacksch/FTBPackManifest.h
deleted file mode 100644
index a8b6f35e..00000000
--- a/launcher/modplatform/modpacksch/FTBPackManifest.h
+++ /dev/null
@@ -1,168 +0,0 @@
-// SPDX-License-Identifier: GPL-3.0-only
-/*
- * PolyMC - Minecraft Launcher
- * Copyright (C) 2022 Sefa Eyeoglu
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, version 3.
- *
- * This program 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 this program. If not, see .
- *
- * This file incorporates work covered by the following copyright and
- * permission notice:
- *
- * Copyright 2020-2021 Jamie Mansfield
- * Copyright 2020 Petr Mrazek
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include
-#include
-#include
-#include
-#include
-
-namespace ModpacksCH
-{
-
-struct Specs
-{
- int id;
- int minimum;
- int recommended;
-};
-
-struct Tag
-{
- int id;
- QString name;
-};
-
-struct Art
-{
- int id;
- QString url;
- QString type;
- int width;
- int height;
- bool compressed;
- QString sha1;
- int size;
- int64_t updated;
-};
-
-struct Author
-{
- int id;
- QString name;
- QString type;
- QString website;
- int64_t updated;
-};
-
-struct VersionInfo
-{
- int id;
- QString name;
- QString type;
- int64_t updated;
- Specs specs;
-};
-
-struct Modpack
-{
- int id;
- QString name;
- QString synopsis;
- QString description;
- QString type;
- bool featured;
- int installs;
- int plays;
- int64_t updated;
- int64_t refreshed;
- QVector art;
- QVector authors;
- QVector versions;
- QVector tags;
-};
-
-struct VersionTarget
-{
- int id;
- QString type;
- QString name;
- QString version;
- int64_t updated;
-};
-
-struct VersionFileCurseForge
-{
- int project_id;
- int file_id;
-};
-
-struct VersionFile
-{
- int id;
- QString type;
- QString path;
- QString name;
- QString version;
- QString url;
- QString sha1;
- int size;
- bool clientOnly;
- bool serverOnly;
- bool optional;
- int64_t updated;
- VersionFileCurseForge curseforge;
-};
-
-struct Version
-{
- int id;
- int parent;
- QString name;
- QString type;
- int installs;
- int plays;
- int64_t updated;
- int64_t refreshed;
- Specs specs;
- QVector targets;
- QVector files;
-};
-
-struct VersionChangelog
-{
- QString content;
- int64_t updated;
-};
-
-void loadModpack(Modpack & m, QJsonObject & obj);
-
-void loadVersion(Version & m, QJsonObject & obj);
-}
-
-Q_DECLARE_METATYPE(ModpacksCH::Modpack)
diff --git a/launcher/ui/dialogs/NewInstanceDialog.cpp b/launcher/ui/dialogs/NewInstanceDialog.cpp
index df182f09..64ed7673 100644
--- a/launcher/ui/dialogs/NewInstanceDialog.cpp
+++ b/launcher/ui/dialogs/NewInstanceDialog.cpp
@@ -56,7 +56,6 @@
#include "ui/widgets/PageContainer.h"
#include "ui/pages/modplatform/VanillaPage.h"
#include "ui/pages/modplatform/atlauncher/AtlPage.h"
-#include "ui/pages/modplatform/ftb/FtbPage.h"
#include "ui/pages/modplatform/legacy_ftb/Page.h"
#include "ui/pages/modplatform/flame/FlamePage.h"
#include "ui/pages/modplatform/ImportPage.h"
@@ -168,7 +167,6 @@ QList NewInstanceDialog::getPages()
pages.append(new AtlPage(this));
if (APPLICATION->capabilities() & Application::SupportsFlame)
pages.append(new FlamePage(this));
- pages.append(new FtbPage(this));
pages.append(new LegacyFTB::Page(this));
pages.append(new ModrinthPage(this));
pages.append(new TechnicPage(this));
diff --git a/launcher/ui/pages/modplatform/ftb/FtbFilterModel.cpp b/launcher/ui/pages/modplatform/ftb/FtbFilterModel.cpp
deleted file mode 100644
index e2b548f2..00000000
--- a/launcher/ui/pages/modplatform/ftb/FtbFilterModel.cpp
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright 2020-2021 Jamie Mansfield
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "FtbFilterModel.h"
-
-#include
-
-#include "modplatform/modpacksch/FTBPackManifest.h"
-
-#include "StringUtils.h"
-
-namespace Ftb {
-
-FilterModel::FilterModel(QObject *parent) : QSortFilterProxyModel(parent)
-{
- currentSorting = Sorting::ByPlays;
- sortings.insert(tr("Sort by Plays"), Sorting::ByPlays);
- sortings.insert(tr("Sort by Installs"), Sorting::ByInstalls);
- sortings.insert(tr("Sort by Name"), Sorting::ByName);
-}
-
-const QMap FilterModel::getAvailableSortings()
-{
- return sortings;
-}
-
-QString FilterModel::translateCurrentSorting()
-{
- return sortings.key(currentSorting);
-}
-
-void FilterModel::setSorting(Sorting sorting)
-{
- currentSorting = sorting;
- invalidate();
-}
-
-FilterModel::Sorting FilterModel::getCurrentSorting()
-{
- return currentSorting;
-}
-
-void FilterModel::setSearchTerm(const QString& term)
-{
- searchTerm = term.trimmed();
- invalidate();
-}
-
-bool FilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
-{
- if (searchTerm.isEmpty()) {
- return true;
- }
-
- auto index = sourceModel()->index(sourceRow, 0, sourceParent);
- auto pack = sourceModel()->data(index, Qt::UserRole).value();
- return pack.name.contains(searchTerm, Qt::CaseInsensitive);
-}
-
-bool FilterModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
-{
- ModpacksCH::Modpack leftPack = sourceModel()->data(left, Qt::UserRole).value();
- ModpacksCH::Modpack rightPack = sourceModel()->data(right, Qt::UserRole).value();
-
- if (currentSorting == ByPlays) {
- return leftPack.plays < rightPack.plays;
- }
- else if (currentSorting == ByInstalls) {
- return leftPack.installs < rightPack.installs;
- }
- else if (currentSorting == ByName) {
- return StringUtils::naturalCompare(leftPack.name, rightPack.name, Qt::CaseSensitive) >= 0;
- }
-
- // Invalid sorting set, somehow...
- qWarning() << "Invalid sorting set!";
- return true;
-}
-
-}
diff --git a/launcher/ui/pages/modplatform/ftb/FtbFilterModel.h b/launcher/ui/pages/modplatform/ftb/FtbFilterModel.h
deleted file mode 100644
index 1be28e99..00000000
--- a/launcher/ui/pages/modplatform/ftb/FtbFilterModel.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright 2020-2021 Jamie Mansfield
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include
-
-namespace Ftb {
-
-class FilterModel : public QSortFilterProxyModel
-{
- Q_OBJECT
-
-public:
- FilterModel(QObject* parent = Q_NULLPTR);
- enum Sorting {
- ByPlays,
- ByInstalls,
- ByName,
- };
- const QMap getAvailableSortings();
- QString translateCurrentSorting();
- void setSorting(Sorting sorting);
- Sorting getCurrentSorting();
- void setSearchTerm(const QString& term);
-
-protected:
- bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
- bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
-
-private:
- QMap sortings;
- Sorting currentSorting;
- QString searchTerm { "" };
-
-};
-
-}
diff --git a/launcher/ui/pages/modplatform/ftb/FtbListModel.cpp b/launcher/ui/pages/modplatform/ftb/FtbListModel.cpp
deleted file mode 100644
index e8065415..00000000
--- a/launcher/ui/pages/modplatform/ftb/FtbListModel.cpp
+++ /dev/null
@@ -1,304 +0,0 @@
-/*
- * Copyright 2020-2021 Jamie Mansfield
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "FtbListModel.h"
-
-#include "BuildConfig.h"
-#include "Application.h"
-#include "Json.h"
-
-#include
-
-namespace Ftb {
-
-ListModel::ListModel(QObject *parent) : QAbstractListModel(parent)
-{
-}
-
-ListModel::~ListModel()
-{
-}
-
-int ListModel::rowCount(const QModelIndex &parent) const
-{
- return parent.isValid() ? 0 : modpacks.size();
-}
-
-int ListModel::columnCount(const QModelIndex &parent) const
-{
- return parent.isValid() ? 0 : 1;
-}
-
-QVariant ListModel::data(const QModelIndex &index, int role) const
-{
- int pos = index.row();
- if(pos >= modpacks.size() || pos < 0 || !index.isValid())
- {
- return QString("INVALID INDEX %1").arg(pos);
- }
-
- ModpacksCH::Modpack pack = modpacks.at(pos);
- if(role == Qt::DisplayRole)
- {
- return pack.name;
- }
- else if (role == Qt::ToolTipRole)
- {
- return pack.synopsis;
- }
- else if(role == Qt::DecorationRole)
- {
- QIcon placeholder = APPLICATION->getThemedIcon("screenshot-placeholder");
-
- auto iter = m_logoMap.find(pack.name);
- if (iter != m_logoMap.end()) {
- auto & logo = *iter;
- if(!logo.result.isNull()) {
- return logo.result;
- }
- return placeholder;
- }
-
- for(auto art : pack.art) {
- if(art.type == "square") {
- ((ListModel *)this)->requestLogo(pack.name, art.url);
- }
- }
- return placeholder;
- }
- else if(role == Qt::UserRole)
- {
- QVariant v;
- v.setValue(pack);
- return v;
- }
-
- return QVariant();
-}
-
-void ListModel::getLogo(const QString &logo, const QString &logoUrl, LogoCallback callback)
-{
- if(m_logoMap.contains(logo))
- {
- callback(APPLICATION->metacache()->resolveEntry("ModpacksCHPacks", QString("logos/%1").arg(logo.section(".", 0, 0)))->getFullPath());
- }
- else
- {
- requestLogo(logo, logoUrl);
- }
-}
-
-void ListModel::request()
-{
- m_aborted = false;
-
- beginResetModel();
- modpacks.clear();
- endResetModel();
-
- auto netJob = makeShared("Ftb::Request", APPLICATION->network());
- auto url = QString(BuildConfig.MODPACKSCH_API_BASE_URL + "public/modpack/all");
- netJob->addNetAction(Net::Download::makeByteArray(QUrl(url), &response));
- jobPtr = netJob;
- jobPtr->start();
-
- QObject::connect(netJob.get(), &NetJob::succeeded, this, &ListModel::requestFinished);
- QObject::connect(netJob.get(), &NetJob::failed, this, &ListModel::requestFailed);
-}
-
-void ListModel::abortRequest()
-{
- m_aborted = jobPtr->abort();
- jobPtr.reset();
-}
-
-void ListModel::requestFinished()
-{
- jobPtr.reset();
- remainingPacks.clear();
-
- QJsonParseError parse_error {};
- QJsonDocument doc = QJsonDocument::fromJson(response, &parse_error);
- if(parse_error.error != QJsonParseError::NoError) {
- qWarning() << "Error while parsing JSON response from ModpacksCH at " << parse_error.offset << " reason: " << parse_error.errorString();
- qWarning() << response;
- return;
- }
-
- auto packs = doc.object().value("packs").toArray();
- for(auto pack : packs) {
- auto packId = pack.toInt();
- remainingPacks.append(packId);
- }
-
- if(!remainingPacks.isEmpty()) {
- currentPack = remainingPacks.at(0);
- requestPack();
- }
-}
-
-void ListModel::requestFailed(QString reason)
-{
- jobPtr.reset();
- remainingPacks.clear();
-}
-
-void ListModel::requestPack()
-{
- auto netJob = makeShared("Ftb::Search", APPLICATION->network());
- auto searchUrl = QString(BuildConfig.MODPACKSCH_API_BASE_URL + "public/modpack/%1").arg(currentPack);
- netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), &response));
- jobPtr = netJob;
- jobPtr->start();
-
- QObject::connect(netJob.get(), &NetJob::succeeded, this, &ListModel::packRequestFinished);
- QObject::connect(netJob.get(), &NetJob::failed, this, &ListModel::packRequestFailed);
-}
-
-void ListModel::packRequestFinished()
-{
- if (!jobPtr || m_aborted)
- return;
-
- jobPtr.reset();
- remainingPacks.removeOne(currentPack);
-
- QJsonParseError parse_error;
- QJsonDocument doc = QJsonDocument::fromJson(response, &parse_error);
-
- if(parse_error.error != QJsonParseError::NoError) {
- qWarning() << "Error while parsing JSON response from ModpacksCH at " << parse_error.offset << " reason: " << parse_error.errorString();
- qWarning() << response;
- return;
- }
-
- auto obj = doc.object();
-
- ModpacksCH::Modpack pack;
- try
- {
- ModpacksCH::loadModpack(pack, obj);
- }
- catch (const JSONValidationError &e)
- {
- qDebug() << QString::fromUtf8(response);
- qWarning() << "Error while reading pack manifest from ModpacksCH: " << e.cause();
- return;
- }
-
- // Since there is no guarantee that packs have a version, this will just
- // ignore those "dud" packs.
- if (pack.versions.empty())
- {
- qWarning() << "ModpacksCH Pack " << pack.id << " ignored. reason: lacking any versions";
- }
- else
- {
- beginInsertRows(QModelIndex(), modpacks.size(), modpacks.size());
- modpacks.append(pack);
- endInsertRows();
- }
-
- if(!remainingPacks.isEmpty()) {
- currentPack = remainingPacks.at(0);
- requestPack();
- }
-}
-
-void ListModel::packRequestFailed(QString reason)
-{
- jobPtr.reset();
- remainingPacks.removeOne(currentPack);
-}
-
-void ListModel::logoLoaded(QString logo, bool stale)
-{
- auto & logoObj = m_logoMap[logo];
- logoObj.downloadJob.reset();
- QString smallPath = logoObj.fullpath + ".small";
-
- QFileInfo smallInfo(smallPath);
-
- if(stale || !smallInfo.exists()) {
- QImage image(logoObj.fullpath);
- if (image.isNull())
- {
- logoObj.failed = true;
- return;
- }
- QImage small;
- if (image.width() > image.height()) {
- small = image.scaledToWidth(512).scaledToWidth(256, Qt::SmoothTransformation);
- }
- else {
- small = image.scaledToHeight(512).scaledToHeight(256, Qt::SmoothTransformation);
- }
- QPoint offset((256 - small.width()) / 2, (256 - small.height()) / 2);
- QImage square(QSize(256, 256), QImage::Format_ARGB32);
- square.fill(Qt::transparent);
-
- QPainter painter(&square);
- painter.drawImage(offset, small);
- painter.end();
-
- square.save(logoObj.fullpath + ".small", "PNG");
- }
-
- logoObj.result = QIcon(logoObj.fullpath + ".small");
- for(int i = 0; i < modpacks.size(); i++) {
- if(modpacks[i].name == logo) {
- emit dataChanged(createIndex(i, 0), createIndex(i, 0), {Qt::DecorationRole});
- }
- }
-}
-
-void ListModel::logoFailed(QString logo)
-{
- m_logoMap[logo].failed = true;
- m_logoMap[logo].downloadJob.reset();
-}
-
-void ListModel::requestLogo(QString logo, QString url)
-{
- if(m_logoMap.contains(logo)) {
- return;
- }
-
- MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("ModpacksCHPacks", QString("logos/%1").arg(logo.section(".", 0, 0)));
-
- bool stale = entry->isStale();
-
- auto job = makeShared(QString("ModpacksCH Icon Download %1").arg(logo), APPLICATION->network());
- job->addNetAction(Net::Download::makeCached(QUrl(url), entry));
-
- auto fullPath = entry->getFullPath();
- QObject::connect(job.get(), &NetJob::finished, this, [this, logo, fullPath, stale]
- {
- logoLoaded(logo, stale);
- });
-
- QObject::connect(job.get(), &NetJob::failed, this, [this, logo]
- {
- logoFailed(logo);
- });
-
- auto &newLogoEntry = m_logoMap[logo];
- newLogoEntry.downloadJob = job;
- newLogoEntry.fullpath = fullPath;
- job->start();
-}
-
-}
diff --git a/launcher/ui/pages/modplatform/ftb/FtbListModel.h b/launcher/ui/pages/modplatform/ftb/FtbListModel.h
deleted file mode 100644
index d7a120f0..00000000
--- a/launcher/ui/pages/modplatform/ftb/FtbListModel.h
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright 2020-2021 Jamie Mansfield
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include
-
-#include "modplatform/modpacksch/FTBPackManifest.h"
-#include "net/NetJob.h"
-#include
-
-namespace Ftb {
-
-struct Logo {
- QString fullpath;
- NetJob::Ptr downloadJob;
- QIcon result;
- bool failed = false;
-};
-
-typedef QMap LogoMap;
-typedef std::function LogoCallback;
-
-class ListModel : public QAbstractListModel
-{
- Q_OBJECT
-
-public:
- ListModel(QObject *parent);
- virtual ~ListModel();
-
- int rowCount(const QModelIndex &parent) const override;
- int columnCount(const QModelIndex &parent) const override;
- QVariant data(const QModelIndex &index, int role) const override;
-
- void request();
- void abortRequest();
-
- void getLogo(const QString &logo, const QString &logoUrl, LogoCallback callback);
-
- [[nodiscard]] bool isMakingRequest() const { return jobPtr.get(); }
- [[nodiscard]] bool wasAborted() const { return m_aborted; }
-
-private slots:
- void requestFinished();
- void requestFailed(QString reason);
-
- void requestPack();
- void packRequestFinished();
- void packRequestFailed(QString reason);
-
- void logoFailed(QString logo);
- void logoLoaded(QString logo, bool stale);
-
-private:
- void requestLogo(QString file, QString url);
-
-private:
- bool m_aborted = false;
-
- QList modpacks;
- LogoMap m_logoMap;
-
- NetJob::Ptr jobPtr;
- int currentPack;
- QList remainingPacks;
- QByteArray response;
-};
-
-}
diff --git a/launcher/ui/pages/modplatform/ftb/FtbPage.cpp b/launcher/ui/pages/modplatform/ftb/FtbPage.cpp
deleted file mode 100644
index 7d59a6ae..00000000
--- a/launcher/ui/pages/modplatform/ftb/FtbPage.cpp
+++ /dev/null
@@ -1,199 +0,0 @@
-// SPDX-License-Identifier: GPL-3.0-only
-/*
- * PolyMC - Minecraft Launcher
- * Copyright (c) 2022 Jamie Mansfield
- * Copyright (C) 2022 Sefa Eyeoglu
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, version 3.
- *
- * This program 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 this program. If not, see .
- *
- * This file incorporates work covered by the following copyright and
- * permission notice:
- *
- * Copyright 2020-2021 Jamie Mansfield
- * Copyright 2021 Philip T
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "FtbPage.h"
-#include "ui_FtbPage.h"
-
-#include
-
-#include "ui/dialogs/NewInstanceDialog.h"
-#include "modplatform/modpacksch/FTBPackInstallTask.h"
-
-#include "Markdown.h"
-
-FtbPage::FtbPage(NewInstanceDialog* dialog, QWidget *parent)
- : QWidget(parent), ui(new Ui::FtbPage), dialog(dialog)
-{
- ui->setupUi(this);
-
- filterModel = new Ftb::FilterModel(this);
- listModel = new Ftb::ListModel(this);
- filterModel->setSourceModel(listModel);
- ui->packView->setModel(filterModel);
- ui->packView->setSortingEnabled(true);
- ui->packView->header()->hide();
- ui->packView->setIndentation(0);
-
- ui->searchEdit->installEventFilter(this);
-
- ui->versionSelectionBox->view()->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
- ui->versionSelectionBox->view()->parentWidget()->setMaximumHeight(300);
-
- for(int i = 0; i < filterModel->getAvailableSortings().size(); i++)
- {
- ui->sortByBox->addItem(filterModel->getAvailableSortings().keys().at(i));
- }
- ui->sortByBox->setCurrentText(filterModel->translateCurrentSorting());
-
- connect(ui->searchEdit, &QLineEdit::textChanged, this, &FtbPage::triggerSearch);
- connect(ui->sortByBox, &QComboBox::currentTextChanged, this, &FtbPage::onSortingSelectionChanged);
- connect(ui->packView->selectionModel(), &QItemSelectionModel::currentChanged, this, &FtbPage::onSelectionChanged);
- connect(ui->versionSelectionBox, &QComboBox::currentTextChanged, this, &FtbPage::onVersionSelectionChanged);
-
- ui->packDescription->setMetaEntry("FTBPacks");
-}
-
-FtbPage::~FtbPage()
-{
- delete ui;
-}
-
-bool FtbPage::eventFilter(QObject* watched, QEvent* event)
-{
- if (watched == ui->searchEdit && event->type() == QEvent::KeyPress) {
- QKeyEvent* keyEvent = static_cast(event);
- if (keyEvent->key() == Qt::Key_Return) {
- triggerSearch();
- keyEvent->accept();
- return true;
- }
- }
- return QWidget::eventFilter(watched, event);
-}
-
-bool FtbPage::shouldDisplay() const
-{
- return true;
-}
-
-void FtbPage::retranslate()
-{
- ui->retranslateUi(this);
-}
-
-void FtbPage::openedImpl()
-{
- if(!initialised || listModel->wasAborted())
- {
- listModel->request();
- initialised = true;
- }
-
- suggestCurrent();
-}
-
-void FtbPage::closedImpl()
-{
- if (listModel->isMakingRequest())
- listModel->abortRequest();
-}
-
-void FtbPage::suggestCurrent()
-{
- if(!isOpened)
- {
- return;
- }
-
- if (selectedVersion.isEmpty())
- {
- dialog->setSuggestedPack();
- return;
- }
-
- dialog->setSuggestedPack(selected.name, selectedVersion, new ModpacksCH::PackInstallTask(selected, selectedVersion, this));
- for(auto art : selected.art) {
- if(art.type == "square") {
- QString editedLogoName;
- editedLogoName = selected.name;
-
- listModel->getLogo(selected.name, art.url, [this, editedLogoName](QString logo)
- {
- dialog->setSuggestedIconFromFile(logo + ".small", editedLogoName);
- });
- }
- }
-}
-
-void FtbPage::triggerSearch()
-{
- filterModel->setSearchTerm(ui->searchEdit->text());
-}
-
-void FtbPage::onSortingSelectionChanged(QString data)
-{
- auto toSet = filterModel->getAvailableSortings().value(data);
- filterModel->setSorting(toSet);
-}
-
-void FtbPage::onSelectionChanged(QModelIndex first, QModelIndex second)
-{
- ui->versionSelectionBox->clear();
-
- if(!first.isValid())
- {
- if(isOpened)
- {
- dialog->setSuggestedPack();
- }
- return;
- }
-
- selected = filterModel->data(first, Qt::UserRole).value();
-
- QString output = markdownToHTML(selected.description.toUtf8());
- ui->packDescription->setHtml(output);
-
- // reverse foreach, so that the newest versions are first
- for (auto i = selected.versions.size(); i--;) {
- ui->versionSelectionBox->addItem(selected.versions.at(i).name);
- }
-
- suggestCurrent();
-}
-
-void FtbPage::onVersionSelectionChanged(QString data)
-{
- if(data.isNull() || data.isEmpty())
- {
- selectedVersion = "";
- return;
- }
-
- selectedVersion = data;
- suggestCurrent();
-}
diff --git a/launcher/ui/pages/modplatform/ftb/FtbPage.h b/launcher/ui/pages/modplatform/ftb/FtbPage.h
deleted file mode 100644
index 631ae7f5..00000000
--- a/launcher/ui/pages/modplatform/ftb/FtbPage.h
+++ /dev/null
@@ -1,105 +0,0 @@
-// SPDX-License-Identifier: GPL-3.0-only
-/*
- * PolyMC - Minecraft Launcher
- * Copyright (c) 2022 Jamie Mansfield
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, version 3.
- *
- * This program 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 this program. If not, see .
- *
- * This file incorporates work covered by the following copyright and
- * permission notice:
- *
- * Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "FtbFilterModel.h"
-#include "FtbListModel.h"
-
-#include
-
-#include "Application.h"
-#include "ui/pages/BasePage.h"
-#include "tasks/Task.h"
-
-namespace Ui
-{
- class FtbPage;
-}
-
-class NewInstanceDialog;
-
-class FtbPage : public QWidget, public BasePage
-{
-Q_OBJECT
-
-public:
- explicit FtbPage(NewInstanceDialog* dialog, QWidget *parent = 0);
- virtual ~FtbPage();
- virtual QString displayName() const override
- {
- return "FTB";
- }
- virtual QIcon icon() const override
- {
- return APPLICATION->getThemedIcon("ftb_logo");
- }
- virtual QString id() const override
- {
- return "ftb";
- }
- virtual QString helpPage() const override
- {
- return "FTB-platform";
- }
- virtual bool shouldDisplay() const override;
- void retranslate() override;
-
- void openedImpl() override;
- void closedImpl() override;
-
- bool eventFilter(QObject * watched, QEvent * event) override;
-
-private:
- void suggestCurrent();
-
-private slots:
- void triggerSearch();
-
- void onSortingSelectionChanged(QString data);
- void onSelectionChanged(QModelIndex first, QModelIndex second);
- void onVersionSelectionChanged(QString data);
-
-private:
- Ui::FtbPage *ui = nullptr;
- NewInstanceDialog* dialog = nullptr;
- Ftb::ListModel* listModel = nullptr;
- Ftb::FilterModel* filterModel = nullptr;
-
- ModpacksCH::Modpack selected;
- QString selectedVersion;
-
- bool initialised { false };
-};
diff --git a/launcher/ui/pages/modplatform/ftb/FtbPage.ui b/launcher/ui/pages/modplatform/ftb/FtbPage.ui
deleted file mode 100644
index 8de0f4e6..00000000
--- a/launcher/ui/pages/modplatform/ftb/FtbPage.ui
+++ /dev/null
@@ -1,86 +0,0 @@
-
-
- FtbPage
-
-
-
- 0
- 0
- 875
- 745
-
-
-
- -
-
-
-
-
-
- -
-
-
- Version selected:
-
-
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
-
-
-
- -
-
-
-
-
- -
-
-
- Search and filter...
-
-
- true
-
-
-
- -
-
-
-
-
-
- true
-
-
-
- 48
- 48
-
-
-
-
- -
-
-
- true
-
-
- true
-
-
-
-
-
-
-
-
-
- ProjectDescriptionPage
- QTextBrowser
- ui/widgets/ProjectDescriptionPage.h
-
-
-
- searchEdit
- versionSelectionBox
-
-
-
-
--
cgit
From d80dee2a54a172fa19c0bc21486ee43ef2e3514d Mon Sep 17 00:00:00 2001
From: Rachel Powers <508861+Ryex@users.noreply.github.com>
Date: Sat, 29 Apr 2023 19:38:51 -0700
Subject: refactor: pass instance ptr to resource models. use it to find
instance root.
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
---
launcher/minecraft/MinecraftInstance.cpp | 14 +++++++-------
launcher/minecraft/WorldList.cpp | 6 +++---
launcher/minecraft/WorldList.h | 4 +++-
launcher/minecraft/mod/ModFolderModel.cpp | 3 ++-
launcher/minecraft/mod/ModFolderModel.h | 2 +-
launcher/minecraft/mod/ResourceFolderModel.cpp | 7 ++++---
launcher/minecraft/mod/ResourceFolderModel.h | 5 ++++-
launcher/minecraft/mod/ResourcePackFolderModel.cpp | 3 ++-
launcher/minecraft/mod/ResourcePackFolderModel.h | 2 +-
launcher/minecraft/mod/ShaderPackFolderModel.h | 4 +++-
launcher/minecraft/mod/TexturePackFolderModel.cpp | 4 +++-
launcher/minecraft/mod/TexturePackFolderModel.h | 2 +-
launcher/ui/MainWindow.cpp | 4 ++--
launcher/ui/dialogs/CopyInstanceDialog.cpp | 2 +-
tests/ResourceFolderModel_test.cpp | 18 ++++++++++++------
15 files changed, 49 insertions(+), 31 deletions(-)
(limited to 'launcher/ui/dialogs')
diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp
index af4da5d0..6b7ca84f 100644
--- a/launcher/minecraft/MinecraftInstance.cpp
+++ b/launcher/minecraft/MinecraftInstance.cpp
@@ -1111,7 +1111,7 @@ std::shared_ptr MinecraftInstance::loaderModList() const
if (!m_loader_mod_list)
{
bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool();
- m_loader_mod_list.reset(new ModFolderModel(modsRoot(), is_indexed));
+ m_loader_mod_list.reset(new ModFolderModel(modsRoot(), shared_from_this(), is_indexed));
m_loader_mod_list->disableInteraction(isRunning());
connect(this, &BaseInstance::runningStatusChanged, m_loader_mod_list.get(), &ModFolderModel::disableInteraction);
}
@@ -1123,7 +1123,7 @@ std::shared_ptr MinecraftInstance::coreModList() const
if (!m_core_mod_list)
{
bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool();
- m_core_mod_list.reset(new ModFolderModel(coreModsDir(), is_indexed));
+ m_core_mod_list.reset(new ModFolderModel(coreModsDir(), shared_from_this(), is_indexed));
m_core_mod_list->disableInteraction(isRunning());
connect(this, &BaseInstance::runningStatusChanged, m_core_mod_list.get(), &ModFolderModel::disableInteraction);
}
@@ -1135,7 +1135,7 @@ std::shared_ptr MinecraftInstance::nilModList() const
if (!m_nil_mod_list)
{
bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool();
- m_nil_mod_list.reset(new ModFolderModel(nilModsDir(), is_indexed, false));
+ m_nil_mod_list.reset(new ModFolderModel(nilModsDir(), shared_from_this(), is_indexed, false));
m_nil_mod_list->disableInteraction(isRunning());
connect(this, &BaseInstance::runningStatusChanged, m_nil_mod_list.get(), &ModFolderModel::disableInteraction);
}
@@ -1146,7 +1146,7 @@ std::shared_ptr MinecraftInstance::resourcePackList() c
{
if (!m_resource_pack_list)
{
- m_resource_pack_list.reset(new ResourcePackFolderModel(resourcePacksDir()));
+ m_resource_pack_list.reset(new ResourcePackFolderModel(resourcePacksDir(), shared_from_this()));
}
return m_resource_pack_list;
}
@@ -1155,7 +1155,7 @@ std::shared_ptr MinecraftInstance::texturePackList() con
{
if (!m_texture_pack_list)
{
- m_texture_pack_list.reset(new TexturePackFolderModel(texturePacksDir()));
+ m_texture_pack_list.reset(new TexturePackFolderModel(texturePacksDir(), shared_from_this()));
}
return m_texture_pack_list;
}
@@ -1164,7 +1164,7 @@ std::shared_ptr MinecraftInstance::shaderPackList() const
{
if (!m_shader_pack_list)
{
- m_shader_pack_list.reset(new ShaderPackFolderModel(shaderPacksDir()));
+ m_shader_pack_list.reset(new ShaderPackFolderModel(shaderPacksDir(), shared_from_this()));
}
return m_shader_pack_list;
}
@@ -1173,7 +1173,7 @@ std::shared_ptr MinecraftInstance::worldList() const
{
if (!m_world_list)
{
- m_world_list.reset(new WorldList(worldDir()));
+ m_world_list.reset(new WorldList(worldDir(), shared_from_this()));
}
return m_world_list;
}
diff --git a/launcher/minecraft/WorldList.cpp b/launcher/minecraft/WorldList.cpp
index 3681bcda..62e55cd4 100644
--- a/launcher/minecraft/WorldList.cpp
+++ b/launcher/minecraft/WorldList.cpp
@@ -45,8 +45,8 @@
#include
#include
-WorldList::WorldList(const QString &dir)
- : QAbstractListModel(), m_dir(dir)
+WorldList::WorldList(const QString &dir, std::shared_ptr instance)
+ : QAbstractListModel(), m_instance(instance), m_dir(dir)
{
FS::ensureFolderPathExists(m_dir.absolutePath());
m_dir.setFilter(QDir::Readable | QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs);
@@ -129,7 +129,7 @@ bool WorldList::isValid()
}
QString WorldList::instDirPath() const {
- return QFileInfo(m_dir.filePath("../..")).absoluteFilePath();
+ return QFileInfo(m_instance->instanceRoot()).absoluteFilePath();
}
bool WorldList::deleteWorld(int index)
diff --git a/launcher/minecraft/WorldList.h b/launcher/minecraft/WorldList.h
index bd32dd4e..10fb4e3c 100644
--- a/launcher/minecraft/WorldList.h
+++ b/launcher/minecraft/WorldList.h
@@ -21,6 +21,7 @@
#include
#include
#include "minecraft/World.h"
+#include "BaseInstance.h"
class QFileSystemWatcher;
@@ -49,7 +50,7 @@ public:
IconFileRole
};
- WorldList(const QString &dir);
+ WorldList(const QString &dir, std::shared_ptr instance);
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
@@ -127,6 +128,7 @@ signals:
void changed();
protected:
+ std::shared_ptr m_instance;
QFileSystemWatcher *m_watcher;
bool is_watching;
QDir m_dir;
diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp
index 91d16175..6ae25d33 100644
--- a/launcher/minecraft/mod/ModFolderModel.cpp
+++ b/launcher/minecraft/mod/ModFolderModel.cpp
@@ -54,7 +54,8 @@
#include "minecraft/mod/tasks/ModFolderLoadTask.h"
#include "modplatform/ModIndex.h"
-ModFolderModel::ModFolderModel(const QString &dir, bool is_indexed, bool create_dir) : ResourceFolderModel(QDir(dir), nullptr, create_dir), m_is_indexed(is_indexed)
+ModFolderModel::ModFolderModel(const QString& dir, std::shared_ptr instance, bool is_indexed, bool create_dir)
+ : ResourceFolderModel(QDir(dir), instance, nullptr, create_dir), m_is_indexed(is_indexed)
{
m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::VERSION, SortType::DATE, SortType::PROVIDER };
}
diff --git a/launcher/minecraft/mod/ModFolderModel.h b/launcher/minecraft/mod/ModFolderModel.h
index 84e70db9..46f5087f 100644
--- a/launcher/minecraft/mod/ModFolderModel.h
+++ b/launcher/minecraft/mod/ModFolderModel.h
@@ -75,7 +75,7 @@ public:
Enable,
Toggle
};
- ModFolderModel(const QString &dir, bool is_indexed = false, bool create_dir = true);
+ ModFolderModel(const QString &dir, std::shared_ptr instance, bool is_indexed = false, bool create_dir = true);
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
diff --git a/launcher/minecraft/mod/ResourceFolderModel.cpp b/launcher/minecraft/mod/ResourceFolderModel.cpp
index 29a0c736..e1973468 100644
--- a/launcher/minecraft/mod/ResourceFolderModel.cpp
+++ b/launcher/minecraft/mod/ResourceFolderModel.cpp
@@ -16,7 +16,8 @@
#include "tasks/Task.h"
-ResourceFolderModel::ResourceFolderModel(QDir dir, QObject* parent, bool create_dir) : QAbstractListModel(parent), m_dir(dir), m_watcher(this)
+ResourceFolderModel::ResourceFolderModel(QDir dir, std::shared_ptr instance, QObject* parent, bool create_dir)
+ : QAbstractListModel(parent), m_dir(dir), m_instance(instance), m_watcher(this)
{
if (create_dir) {
FS::ensureFolderPathExists(m_dir.absolutePath());
@@ -26,7 +27,7 @@ ResourceFolderModel::ResourceFolderModel(QDir dir, QObject* parent, bool create_
m_dir.setSorting(QDir::Name | QDir::IgnoreCase | QDir::LocaleAware);
connect(&m_watcher, &QFileSystemWatcher::directoryChanged, this, &ResourceFolderModel::directoryChanged);
- connect(&m_helper_thread_task, &ConcurrentTask::finished, this, [this]{ m_helper_thread_task.clear(); });
+ connect(&m_helper_thread_task, &ConcurrentTask::finished, this, [this] { m_helper_thread_task.clear(); });
}
ResourceFolderModel::~ResourceFolderModel()
@@ -556,5 +557,5 @@ void ResourceFolderModel::enableInteraction(bool enabled)
}
QString ResourceFolderModel::instDirPath() const {
- return QFileInfo(m_dir.filePath("../..")).absoluteFilePath();
+ return QFileInfo(m_instance->instanceRoot()).absoluteFilePath();
}
diff --git a/launcher/minecraft/mod/ResourceFolderModel.h b/launcher/minecraft/mod/ResourceFolderModel.h
index f840b2de..fdf5f331 100644
--- a/launcher/minecraft/mod/ResourceFolderModel.h
+++ b/launcher/minecraft/mod/ResourceFolderModel.h
@@ -9,6 +9,8 @@
#include "Resource.h"
+#include "BaseInstance.h"
+
#include "tasks/Task.h"
#include "tasks/ConcurrentTask.h"
@@ -24,7 +26,7 @@ class QSortFilterProxyModel;
class ResourceFolderModel : public QAbstractListModel {
Q_OBJECT
public:
- ResourceFolderModel(QDir, QObject* parent = nullptr, bool create_dir = true);
+ ResourceFolderModel(QDir, std::shared_ptr, QObject* parent = nullptr, bool create_dir = true);
~ResourceFolderModel() override;
/** Starts watching the paths for changes.
@@ -189,6 +191,7 @@ class ResourceFolderModel : public QAbstractListModel {
bool m_can_interact = true;
QDir m_dir;
+ std::shared_ptr m_instance;
QFileSystemWatcher m_watcher;
bool m_is_watching = false;
diff --git a/launcher/minecraft/mod/ResourcePackFolderModel.cpp b/launcher/minecraft/mod/ResourcePackFolderModel.cpp
index 0480d8ba..6eba4e2e 100644
--- a/launcher/minecraft/mod/ResourcePackFolderModel.cpp
+++ b/launcher/minecraft/mod/ResourcePackFolderModel.cpp
@@ -45,7 +45,8 @@
#include "minecraft/mod/tasks/BasicFolderLoadTask.h"
#include "minecraft/mod/tasks/LocalResourcePackParseTask.h"
-ResourcePackFolderModel::ResourcePackFolderModel(const QString& dir) : ResourceFolderModel(QDir(dir))
+ResourcePackFolderModel::ResourcePackFolderModel(const QString& dir, std::shared_ptr instance)
+ : ResourceFolderModel(QDir(dir), instance)
{
m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::PACK_FORMAT, SortType::DATE };
}
diff --git a/launcher/minecraft/mod/ResourcePackFolderModel.h b/launcher/minecraft/mod/ResourcePackFolderModel.h
index cb620ce2..66d5a074 100644
--- a/launcher/minecraft/mod/ResourcePackFolderModel.h
+++ b/launcher/minecraft/mod/ResourcePackFolderModel.h
@@ -17,7 +17,7 @@ public:
NUM_COLUMNS
};
- explicit ResourcePackFolderModel(const QString &dir);
+ explicit ResourcePackFolderModel(const QString &dir, std::shared_ptr instance);
[[nodiscard]] QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
diff --git a/launcher/minecraft/mod/ShaderPackFolderModel.h b/launcher/minecraft/mod/ShaderPackFolderModel.h
index a3aa958f..6f3f2811 100644
--- a/launcher/minecraft/mod/ShaderPackFolderModel.h
+++ b/launcher/minecraft/mod/ShaderPackFolderModel.h
@@ -6,5 +6,7 @@ class ShaderPackFolderModel : public ResourceFolderModel {
Q_OBJECT
public:
- explicit ShaderPackFolderModel(const QString& dir) : ResourceFolderModel(QDir(dir)) {}
+ explicit ShaderPackFolderModel(const QString& dir, std::shared_ptr instance)
+ : ResourceFolderModel(QDir(dir), instance)
+ {}
};
diff --git a/launcher/minecraft/mod/TexturePackFolderModel.cpp b/launcher/minecraft/mod/TexturePackFolderModel.cpp
index 5a32cfaf..1e218537 100644
--- a/launcher/minecraft/mod/TexturePackFolderModel.cpp
+++ b/launcher/minecraft/mod/TexturePackFolderModel.cpp
@@ -39,7 +39,9 @@
#include "minecraft/mod/tasks/BasicFolderLoadTask.h"
#include "minecraft/mod/tasks/LocalTexturePackParseTask.h"
-TexturePackFolderModel::TexturePackFolderModel(const QString &dir) : ResourceFolderModel(QDir(dir)) {}
+TexturePackFolderModel::TexturePackFolderModel(const QString& dir, std::shared_ptr instance)
+ : ResourceFolderModel(QDir(dir), instance)
+{}
Task* TexturePackFolderModel::createUpdateTask()
{
diff --git a/launcher/minecraft/mod/TexturePackFolderModel.h b/launcher/minecraft/mod/TexturePackFolderModel.h
index 261f83b4..246997bd 100644
--- a/launcher/minecraft/mod/TexturePackFolderModel.h
+++ b/launcher/minecraft/mod/TexturePackFolderModel.h
@@ -43,7 +43,7 @@ class TexturePackFolderModel : public ResourceFolderModel
Q_OBJECT
public:
- explicit TexturePackFolderModel(const QString &dir);
+ explicit TexturePackFolderModel(const QString &dir, std::shared_ptr instance);
[[nodiscard]] Task* createUpdateTask() override;
[[nodiscard]] Task* createParseTask(Resource&) override;
};
diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp
index e20a7613..72b7db64 100644
--- a/launcher/ui/MainWindow.cpp
+++ b/launcher/ui/MainWindow.cpp
@@ -1341,10 +1341,10 @@ void MainWindow::on_actionDeleteInstance_triggered()
if (!linkedInstances.empty()) {
response = CustomMessageBox::selectable(
this, tr("There are linked instances"),
- tr("The folowing instance(s) might reference files in this instance:\n\n"
+ tr("The following instance(s) might reference files in this instance:\n\n"
"%1\n\n"
"Deleting it could break the other instance(s), \n\n"
- "Do you wish to proceed?").arg(linkedInstances.join("\n")),
+ "Do you wish to proceed?", nullptr, linkedInstances.count()).arg(linkedInstances.join("\n")),
QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No
)->exec();
if (response != QMessageBox::Yes)
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.cpp b/launcher/ui/dialogs/CopyInstanceDialog.cpp
index 347bd39f..d75bb5fe 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.cpp
+++ b/launcher/ui/dialogs/CopyInstanceDialog.cpp
@@ -108,7 +108,7 @@ CopyInstanceDialog::CopyInstanceDialog(InstancePtr original, QWidget* parent)
#if defined(Q_OS_WIN)
ui->symbolicLinksCheckbox->setIcon(style()->standardIcon(QStyle::SP_VistaShield));
ui->symbolicLinksCheckbox->setToolTip(tr("Use symbolic links instead of copying files.") +
- tr("\nOn windows symbolic links may require admin permission to create."));
+ "\n" + tr("On Windows, symbolic links may require admin permission to create."));
#endif
updateLinkOptions();
diff --git a/tests/ResourceFolderModel_test.cpp b/tests/ResourceFolderModel_test.cpp
index e38b8e93..054d81c4 100644
--- a/tests/ResourceFolderModel_test.cpp
+++ b/tests/ResourceFolderModel_test.cpp
@@ -36,6 +36,7 @@
#include
#include
#include
+#include "BaseInstance.h"
#include
@@ -89,7 +90,9 @@ slots:
QEventLoop loop;
- ModFolderModel m(tempDir.path(), true);
+ InstancePtr instance;
+
+ ModFolderModel m(tempDir.path(), instance, true);
connect(&m, &ModFolderModel::updateFinished, &loop, &QEventLoop::quit);
@@ -113,7 +116,8 @@ slots:
QString folder = source + '/';
QTemporaryDir tempDir;
QEventLoop loop;
- ModFolderModel m(tempDir.path(), true);
+ InstancePtr instance;
+ ModFolderModel m(tempDir.path(), instance, true);
connect(&m, &ModFolderModel::updateFinished, &loop, &QEventLoop::quit);
@@ -136,8 +140,8 @@ slots:
void test_addFromWatch()
{
QString source = QFINDTESTDATA("testdata/ResourceFolderModel");
-
- ModFolderModel model(source);
+ InstancePtr instance;
+ ModFolderModel model(source, instance);
QCOMPARE(model.size(), 0);
@@ -157,8 +161,9 @@ slots:
QString file_mod = QFINDTESTDATA("testdata/ResourceFolderModel/supercoolmod.jar");
QTemporaryDir tmp;
+ InstancePtr instance;
- ResourceFolderModel model(QDir(tmp.path()));
+ ResourceFolderModel model(QDir(tmp.path()), instance);
QCOMPARE(model.size(), 0);
@@ -209,7 +214,8 @@ slots:
QString file_mod = QFINDTESTDATA("testdata/ResourceFolderModel/supercoolmod.jar");
QTemporaryDir tmp;
- ResourceFolderModel model(tmp.path());
+ InstancePtr instance;
+ ResourceFolderModel model(tmp.path(), instance);
QCOMPARE(model.size(), 0);
--
cgit