Merge pull request #397 from flowln/windows_mod_updater_fixes_maybe

Fixes https://github.com/PrismLauncher/PrismLauncher/issues/226
This commit is contained in:
Sefa Eyeoglu 2022-11-10 14:34:15 +01:00
parent 79617d0c50
commit 0120d170b7
No known key found for this signature in database
GPG Key ID: C10411294912A422
21 changed files with 102 additions and 90 deletions

View File

@ -24,8 +24,8 @@ set(CORE_SOURCES
NullInstance.h NullInstance.h
MMCZip.h MMCZip.h
MMCZip.cpp MMCZip.cpp
MMCStrings.h StringUtils.h
MMCStrings.cpp StringUtils.cpp
RuntimeContext.h RuntimeContext.h
# Basic instance manipulation tasks (derived from InstanceTask) # Basic instance manipulation tasks (derived from InstanceTask)

View File

@ -44,7 +44,9 @@
#include <QStandardPaths> #include <QStandardPaths>
#include <QTextStream> #include <QTextStream>
#include <QUrl> #include <QUrl>
#include "DesktopServices.h" #include "DesktopServices.h"
#include "StringUtils.h"
#if defined Q_OS_WIN32 #if defined Q_OS_WIN32
#include <objbase.h> #include <objbase.h>
@ -79,22 +81,6 @@ namespace fs = std::filesystem;
namespace fs = ghc::filesystem; namespace fs = ghc::filesystem;
#endif #endif
#if defined Q_OS_WIN32
std::wstring toStdString(QString s)
{
return s.toStdWString();
}
#else
std::string toStdString(QString s)
{
return s.toStdString();
}
#endif
namespace FS { namespace FS {
void ensureExists(const QDir& dir) void ensureExists(const QDir& dir)
@ -191,7 +177,7 @@ bool copy::operator()(const QString& offset)
auto dst_path = PathCombine(dst, relative_dst_path); auto dst_path = PathCombine(dst, relative_dst_path);
ensureFilePathExists(dst_path); ensureFilePathExists(dst_path);
fs::copy(toStdString(src_path), toStdString(dst_path), opt, err); fs::copy(StringUtils::toStdString(src_path), StringUtils::toStdString(dst_path), opt, err);
if (err) { if (err) {
qWarning() << "Failed to copy files:" << QString::fromStdString(err.message()); qWarning() << "Failed to copy files:" << QString::fromStdString(err.message());
qDebug() << "Source file:" << src_path; qDebug() << "Source file:" << src_path;
@ -213,7 +199,7 @@ bool copy::operator()(const QString& offset)
} }
// If the root src is not a directory, the previous iterator won't run. // If the root src is not a directory, the previous iterator won't run.
if (!fs::is_directory(toStdString(src))) if (!fs::is_directory(StringUtils::toStdString(src)))
copy_file(src, ""); copy_file(src, "");
return err.value() == 0; return err.value() == 0;
@ -223,7 +209,7 @@ bool deletePath(QString path)
{ {
std::error_code err; std::error_code err;
fs::remove_all(toStdString(path), err); fs::remove_all(StringUtils::toStdString(path), err);
if (err) { if (err) {
qWarning() << "Failed to remove files:" << QString::fromStdString(err.message()); qWarning() << "Failed to remove files:" << QString::fromStdString(err.message());
@ -414,7 +400,7 @@ bool overrideFolder(QString overwritten_path, QString override_path)
fs::copy_options opt = copy_opts::recursive | copy_opts::overwrite_existing; fs::copy_options opt = copy_opts::recursive | copy_opts::overwrite_existing;
// FIXME: hello traveller! Apparently std::copy does NOT overwrite existing files on GNU libstdc++ on Windows? // FIXME: hello traveller! Apparently std::copy does NOT overwrite existing files on GNU libstdc++ on Windows?
fs::copy(toStdString(override_path), toStdString(overwritten_path), opt, err); fs::copy(StringUtils::toStdString(override_path), StringUtils::toStdString(overwritten_path), opt, err);
if (err) { if (err) {
qCritical() << QString("Failed to apply override from %1 to %2").arg(override_path, overwritten_path); qCritical() << QString("Failed to apply override from %1 to %2").arg(override_path, overwritten_path);

View File

@ -36,7 +36,7 @@
#include "JavaCommon.h" #include "JavaCommon.h"
#include "java/JavaUtils.h" #include "java/JavaUtils.h"
#include "ui/dialogs/CustomMessageBox.h" #include "ui/dialogs/CustomMessageBox.h"
#include <MMCStrings.h>
#include <QRegularExpression> #include <QRegularExpression>
bool JavaCommon::checkJVMArgs(QString jvmargs, QWidget *parent) bool JavaCommon::checkJVMArgs(QString jvmargs, QWidget *parent)

View File

@ -1,8 +0,0 @@
#pragma once
#include <QString>
namespace Strings
{
int naturalCompare(const QString &s1, const QString &s2, Qt::CaseSensitivity cs);
}

View File

@ -1,4 +1,6 @@
#include "MMCStrings.h" #include "StringUtils.h"
/// If you're wondering where these came from exactly, then know you're not the only one =D
/// TAKEN FROM Qt, because it doesn't expose it intelligently /// TAKEN FROM Qt, because it doesn't expose it intelligently
static inline QChar getNextChar(const QString& s, int location) static inline QChar getNextChar(const QString& s, int location)
@ -7,20 +9,20 @@ static inline QChar getNextChar(const QString &s, int location)
} }
/// TAKEN FROM Qt, because it doesn't expose it intelligently /// TAKEN FROM Qt, because it doesn't expose it intelligently
int Strings::naturalCompare(const QString &s1, const QString &s2, Qt::CaseSensitivity cs) int StringUtils::naturalCompare(const QString& s1, const QString& s2, Qt::CaseSensitivity cs)
{
for (int l1 = 0, l2 = 0; l1 <= s1.count() && l2 <= s2.count(); ++l1, ++l2)
{ {
int l1 = 0, l2 = 0;
while (l1 <= s1.count() && l2 <= s2.count()) {
// skip spaces, tabs and 0's // skip spaces, tabs and 0's
QChar c1 = getNextChar(s1, l1); QChar c1 = getNextChar(s1, l1);
while (c1.isSpace()) while (c1.isSpace())
c1 = getNextChar(s1, ++l1); c1 = getNextChar(s1, ++l1);
QChar c2 = getNextChar(s2, l2); QChar c2 = getNextChar(s2, l2);
while (c2.isSpace()) while (c2.isSpace())
c2 = getNextChar(s2, ++l2); c2 = getNextChar(s2, ++l2);
if (c1.isDigit() && c2.isDigit()) if (c1.isDigit() && c2.isDigit()) {
{
while (c1.digitValue() == 0) while (c1.digitValue() == 0)
c1 = getNextChar(s1, ++l1); c1 = getNextChar(s1, ++l1);
while (c2.digitValue() == 0) while (c2.digitValue() == 0)
@ -30,11 +32,8 @@ int Strings::naturalCompare(const QString &s1, const QString &s2, Qt::CaseSensit
int lookAheadLocation2 = l2; int lookAheadLocation2 = l2;
int currentReturnValue = 0; int currentReturnValue = 0;
// find the last digit, setting currentReturnValue as we go if it isn't equal // find the last digit, setting currentReturnValue as we go if it isn't equal
for (QChar lookAhead1 = c1, lookAhead2 = c2; for (QChar lookAhead1 = c1, lookAhead2 = c2; (lookAheadLocation1 <= s1.length() && lookAheadLocation2 <= s2.length());
(lookAheadLocation1 <= s1.length() && lookAheadLocation2 <= s2.length()); lookAhead1 = getNextChar(s1, ++lookAheadLocation1), lookAhead2 = getNextChar(s2, ++lookAheadLocation2)) {
lookAhead1 = getNextChar(s1, ++lookAheadLocation1),
lookAhead2 = getNextChar(s2, ++lookAheadLocation2))
{
bool is1ADigit = !lookAhead1.isNull() && lookAhead1.isDigit(); bool is1ADigit = !lookAhead1.isNull() && lookAhead1.isDigit();
bool is2ADigit = !lookAhead2.isNull() && lookAhead2.isDigit(); bool is2ADigit = !lookAhead2.isNull() && lookAhead2.isDigit();
if (!is1ADigit && !is2ADigit) if (!is1ADigit && !is2ADigit)
@ -43,14 +42,10 @@ int Strings::naturalCompare(const QString &s1, const QString &s2, Qt::CaseSensit
return -1; return -1;
if (!is2ADigit) if (!is2ADigit)
return 1; return 1;
if (currentReturnValue == 0) if (currentReturnValue == 0) {
{ if (lookAhead1 < lookAhead2) {
if (lookAhead1 < lookAhead2)
{
currentReturnValue = -1; currentReturnValue = -1;
} } else if (lookAhead1 > lookAhead2) {
else if (lookAhead1 > lookAhead2)
{
currentReturnValue = 1; currentReturnValue = 1;
} }
} }
@ -58,19 +53,24 @@ int Strings::naturalCompare(const QString &s1, const QString &s2, Qt::CaseSensit
if (currentReturnValue != 0) if (currentReturnValue != 0)
return currentReturnValue; return currentReturnValue;
} }
if (cs == Qt::CaseInsensitive)
{ if (cs == Qt::CaseInsensitive) {
if (!c1.isLower()) if (!c1.isLower())
c1 = c1.toLower(); c1 = c1.toLower();
if (!c2.isLower()) if (!c2.isLower())
c2 = c2.toLower(); c2 = c2.toLower();
} }
int r = QString::localeAwareCompare(c1, c2); int r = QString::localeAwareCompare(c1, c2);
if (r < 0) if (r < 0)
return -1; return -1;
if (r > 0) if (r > 0)
return 1; return 1;
l1 += 1;
l2 += 1;
} }
// The two strings are the same (02 == 2) so fall back to the normal sort // The two strings are the same (02 == 2) so fall back to the normal sort
return QString::compare(s1, s2, cs); return QString::compare(s1, s2, cs);
} }

32
launcher/StringUtils.h Normal file
View File

@ -0,0 +1,32 @@
#pragma once
#include <QString>
namespace StringUtils {
#if defined Q_OS_WIN32
using string = std::wstring;
inline string toStdString(QString s)
{
return s.toStdWString();
}
inline QString fromStdString(string s)
{
return QString::fromStdWString(s);
}
#else
using string = std::string;
inline string toStdString(QString s)
{
return s.toStdString();
}
inline QString fromStdString(string s)
{
return QString::fromStdString(s);
}
#endif
int naturalCompare(const QString& s1, const QString& s2, Qt::CaseSensitivity cs);
} // namespace StringUtils

View File

@ -1,9 +1,10 @@
#include "JavaInstall.h" #include "JavaInstall.h"
#include <MMCStrings.h>
#include "StringUtils.h"
bool JavaInstall::operator<(const JavaInstall &rhs) bool JavaInstall::operator<(const JavaInstall &rhs)
{ {
auto archCompare = Strings::naturalCompare(arch, rhs.arch, Qt::CaseInsensitive); auto archCompare = StringUtils::naturalCompare(arch, rhs.arch, Qt::CaseInsensitive);
if(archCompare != 0) if(archCompare != 0)
return archCompare < 0; return archCompare < 0;
if(id < rhs.id) if(id < rhs.id)
@ -14,7 +15,7 @@ bool JavaInstall::operator<(const JavaInstall &rhs)
{ {
return false; return false;
} }
return Strings::naturalCompare(path, rhs.path, Qt::CaseInsensitive) < 0; return StringUtils::naturalCompare(path, rhs.path, Qt::CaseInsensitive) < 0;
} }
bool JavaInstall::operator==(const JavaInstall &rhs) bool JavaInstall::operator==(const JavaInstall &rhs)

View File

@ -41,7 +41,6 @@
#include "java/JavaInstallList.h" #include "java/JavaInstallList.h"
#include "java/JavaCheckerJob.h" #include "java/JavaCheckerJob.h"
#include "java/JavaUtils.h" #include "java/JavaUtils.h"
#include "MMCStrings.h"
#include "minecraft/VersionFilterData.h" #include "minecraft/VersionFilterData.h"
JavaInstallList::JavaInstallList(QObject *parent) : BaseVersionList(parent) JavaInstallList::JavaInstallList(QObject *parent) : BaseVersionList(parent)

View File

@ -1,5 +1,6 @@
#include "JavaVersion.h" #include "JavaVersion.h"
#include <MMCStrings.h>
#include "StringUtils.h"
#include <QRegularExpression> #include <QRegularExpression>
#include <QString> #include <QString>
@ -98,12 +99,12 @@ bool JavaVersion::operator<(const JavaVersion &rhs)
else if(thisPre && rhsPre) else if(thisPre && rhsPre)
{ {
// both are prereleases - use natural compare... // both are prereleases - use natural compare...
return Strings::naturalCompare(m_prerelease, rhs.m_prerelease, Qt::CaseSensitive) < 0; return StringUtils::naturalCompare(m_prerelease, rhs.m_prerelease, Qt::CaseSensitive) < 0;
} }
// neither is prerelease, so they are the same -> this cannot be less than rhs // neither is prerelease, so they are the same -> this cannot be less than rhs
return false; return false;
} }
else return Strings::naturalCompare(m_string, rhs.m_string, Qt::CaseSensitive) < 0; else return StringUtils::naturalCompare(m_string, rhs.m_string, Qt::CaseSensitive) < 0;
} }
bool JavaVersion::operator==(const JavaVersion &rhs) bool JavaVersion::operator==(const JavaVersion &rhs)

View File

@ -37,7 +37,6 @@
#include "launch/LaunchTask.h" #include "launch/LaunchTask.h"
#include "MessageLevel.h" #include "MessageLevel.h"
#include "MMCStrings.h"
#include "java/JavaChecker.h" #include "java/JavaChecker.h"
#include "tasks/Task.h" #include "tasks/Task.h"
#include <QDebug> #include <QDebug>

View File

@ -43,7 +43,6 @@
#include "settings/SettingsObject.h" #include "settings/SettingsObject.h"
#include "Application.h" #include "Application.h"
#include "MMCStrings.h"
#include "pathmatcher/RegexpMatcher.h" #include "pathmatcher/RegexpMatcher.h"
#include "pathmatcher/MultiMatcher.h" #include "pathmatcher/MultiMatcher.h"
#include "FileSystem.h" #include "FileSystem.h"

View File

@ -36,7 +36,7 @@ LocalModUpdateTask::LocalModUpdateTask(QDir index_dir, ModPlatform::IndexedPack&
} }
#ifdef Q_OS_WIN32 #ifdef Q_OS_WIN32
SetFileAttributesA(index_dir.path().toStdString().c_str(), FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED); SetFileAttributesW(index_dir.path().toStdWString().c_str(), FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED);
#endif #endif
} }

View File

@ -4,6 +4,7 @@
#include <QFile> #include <QFile>
#include "FileSystem.h" #include "FileSystem.h"
#include "StringUtils.h"
#include <MurmurHash2.h> #include <MurmurHash2.h>
@ -66,7 +67,7 @@ void FlameHasher::executeTask()
// CF-specific // CF-specific
auto should_filter_out = [](char c) { return (c == 9 || c == 10 || c == 13 || c == 32); }; auto should_filter_out = [](char c) { return (c == 9 || c == 10 || c == 13 || c == 32); };
std::ifstream file_stream(m_path.toStdString(), std::ifstream::binary); std::ifstream file_stream(StringUtils::toStdString(m_path), std::ifstream::binary);
// TODO: This is very heavy work, but apparently QtConcurrent can't use move semantics, so we can't boop this to another thread. // TODO: This is very heavy work, but apparently QtConcurrent can't use move semantics, so we can't boop this to another thread.
// How do we make this non-blocking then? // How do we make this non-blocking then?
m_hash = QString::number(MurmurHash2(std::move(file_stream), 4 * MiB, should_filter_out)); m_hash = QString::number(MurmurHash2(std::move(file_stream), 4 * MiB, should_filter_out));

View File

@ -22,10 +22,14 @@
#include <QDir> #include <QDir>
#include <QObject> #include <QObject>
#include <toml++/toml.h> #include "FileSystem.h"
#include "StringUtils.h"
#include "minecraft/mod/Mod.h" #include "minecraft/mod/Mod.h"
#include "modplatform/ModIndex.h" #include "modplatform/ModIndex.h"
#include <toml++/toml.h>
namespace Packwiz { namespace Packwiz {
auto getRealIndexName(QDir& index_dir, QString normalized_fname, bool should_find_match) -> QString auto getRealIndexName(QDir& index_dir, QString normalized_fname, bool should_find_match) -> QString
@ -63,22 +67,22 @@ static inline auto indexFileName(QString const& mod_slug) -> QString
static ModPlatform::ProviderCapabilities ProviderCaps; static ModPlatform::ProviderCapabilities ProviderCaps;
// Helper functions for extracting data from the TOML file // Helper functions for extracting data from the TOML file
auto stringEntry(toml::table table, const std::string entry_name) -> QString auto stringEntry(toml::table table, QString entry_name) -> QString
{ {
auto node = table[entry_name]; auto node = table[StringUtils::toStdString(entry_name)];
if (!node) { if (!node) {
qCritical() << QString::fromStdString("Failed to read str property '" + entry_name + "' in mod metadata."); qCritical() << "Failed to read str property '" + entry_name + "' in mod metadata.";
return {}; return {};
} }
return QString::fromStdString(node.value_or("")); return node.value_or("");
} }
auto intEntry(toml::table table, const std::string entry_name) -> int auto intEntry(toml::table table, QString entry_name) -> int
{ {
auto node = table[entry_name]; auto node = table[StringUtils::toStdString(entry_name)];
if (!node) { if (!node) {
qCritical() << QString::fromStdString("Failed to read int property '" + entry_name + "' in mod metadata."); qCritical() << "Failed to read int property '" + entry_name + "' in mod metadata.";
return {}; return {};
} }
@ -145,6 +149,8 @@ void V1::updateModIndex(QDir& index_dir, Mod& mod)
// they want to do! // they want to do!
if (index_file.exists()) { if (index_file.exists()) {
index_file.remove(); index_file.remove();
} else {
FS::ensureFilePathExists(index_file.fileName());
} }
if (!index_file.open(QIODevice::ReadWrite)) { if (!index_file.open(QIODevice::ReadWrite)) {
@ -228,14 +234,14 @@ auto V1::getIndexForMod(QDir& index_dir, QString slug) -> Mod
toml::table table; toml::table table;
#if TOML_EXCEPTIONS #if TOML_EXCEPTIONS
try { try {
table = toml::parse_file(index_dir.absoluteFilePath(real_fname).toStdString()); table = toml::parse_file(StringUtils::toStdString(index_dir.absoluteFilePath(real_fname)));
} catch (const toml::parse_error& err) { } catch (const toml::parse_error& err) {
qWarning() << QString("Could not open file %1!").arg(normalized_fname); qWarning() << QString("Could not open file %1!").arg(normalized_fname);
qWarning() << "Reason: " << QString(err.what()); qWarning() << "Reason: " << QString(err.what());
return {}; return {};
} }
#else #else
table = toml::parse_file(index_dir.absoluteFilePath(real_fname).toStdString()); table = toml::parse_file(StringUtils::toStdString(index_dir.absoluteFilePath(real_fname)));
if (!table) { if (!table) {
qWarning() << QString("Could not open file %1!").arg(normalized_fname); qWarning() << QString("Could not open file %1!").arg(normalized_fname);
qWarning() << "Reason: " << QString(table.error().what()); qWarning() << "Reason: " << QString(table.error().what());

View File

@ -24,7 +24,6 @@
#include <QUrl> #include <QUrl>
#include <QVariant> #include <QVariant>
struct toml_table_t;
class QDir; class QDir;
// Mod from launcher/minecraft/mod/Mod.h // Mod from launcher/minecraft/mod/Mod.h
@ -34,9 +33,6 @@ namespace Packwiz {
auto getRealIndexName(QDir& index_dir, QString normalized_index_name, bool should_match = false) -> QString; auto getRealIndexName(QDir& index_dir, QString normalized_index_name, bool should_match = false) -> QString;
auto stringEntry(toml_table_t* parent, const char* entry_name) -> QString;
auto intEntry(toml_table_t* parent, const char* entry_name) -> int;
class V1 { class V1 {
public: public:
struct Mod { struct Mod {

View File

@ -39,13 +39,12 @@
#include <MMCZip.h> #include <MMCZip.h>
#include <QFileDialog> #include <QFileDialog>
#include <QMessageBox> #include <QMessageBox>
#include <qfilesystemmodel.h> #include <QFileSystemModel>
#include <QSortFilterProxyModel> #include <QSortFilterProxyModel>
#include <QDebug> #include <QDebug>
#include <qstack.h>
#include <QSaveFile> #include <QSaveFile>
#include "MMCStrings.h" #include "StringUtils.h"
#include "SeparatorPrefixTree.h" #include "SeparatorPrefixTree.h"
#include "Application.h" #include "Application.h"
#include <icons/IconList.h> #include <icons/IconList.h>
@ -85,7 +84,7 @@ public:
// sort and proxy model breaks the original model... // sort and proxy model breaks the original model...
if (sortColumn() == 0) if (sortColumn() == 0)
{ {
return Strings::naturalCompare(leftFileInfo.fileName(), rightFileInfo.fileName(), return StringUtils::naturalCompare(leftFileInfo.fileName(), rightFileInfo.fileName(),
Qt::CaseInsensitive) < 0; Qt::CaseInsensitive) < 0;
} }
if (sortColumn() == 1) if (sortColumn() == 1)
@ -94,7 +93,7 @@ public:
auto rightSize = rightFileInfo.size(); auto rightSize = rightFileInfo.size();
if ((leftSize == rightSize) || (leftFileInfo.isDir() && rightFileInfo.isDir())) if ((leftSize == rightSize) || (leftFileInfo.isDir() && rightFileInfo.isDir()))
{ {
return Strings::naturalCompare(leftFileInfo.fileName(), return StringUtils::naturalCompare(leftFileInfo.fileName(),
rightFileInfo.fileName(), rightFileInfo.fileName(),
Qt::CaseInsensitive) < 0 Qt::CaseInsensitive) < 0
? asc ? asc

View File

@ -20,7 +20,8 @@
#include <modplatform/atlauncher/ATLPackIndex.h> #include <modplatform/atlauncher/ATLPackIndex.h>
#include <Version.h> #include <Version.h>
#include <MMCStrings.h>
#include "StringUtils.h"
namespace Atl { namespace Atl {
@ -86,7 +87,7 @@ bool FilterModel::lessThan(const QModelIndex &left, const QModelIndex &right) co
return lv < rv; return lv < rv;
} }
else if (currentSorting == ByName) { else if (currentSorting == ByName) {
return Strings::naturalCompare(leftPack.name, rightPack.name, Qt::CaseSensitive) >= 0; return StringUtils::naturalCompare(leftPack.name, rightPack.name, Qt::CaseSensitive) >= 0;
} }
// Invalid sorting set, somehow... // Invalid sorting set, somehow...

View File

@ -2,7 +2,6 @@
#include <Json.h> #include <Json.h>
#include "Application.h" #include "Application.h"
#include <MMCStrings.h>
#include <Version.h> #include <Version.h>
#include <QtMath> #include <QtMath>

View File

@ -19,7 +19,8 @@
#include <QDebug> #include <QDebug>
#include "modplatform/modpacksch/FTBPackManifest.h" #include "modplatform/modpacksch/FTBPackManifest.h"
#include <MMCStrings.h>
#include "StringUtils.h"
namespace Ftb { namespace Ftb {
@ -81,7 +82,7 @@ bool FilterModel::lessThan(const QModelIndex &left, const QModelIndex &right) co
return leftPack.installs < rightPack.installs; return leftPack.installs < rightPack.installs;
} }
else if (currentSorting == ByName) { else if (currentSorting == ByName) {
return Strings::naturalCompare(leftPack.name, rightPack.name, Qt::CaseSensitive) >= 0; return StringUtils::naturalCompare(leftPack.name, rightPack.name, Qt::CaseSensitive) >= 0;
} }
// Invalid sorting set, somehow... // Invalid sorting set, somehow...

View File

@ -36,7 +36,7 @@
#include "ListModel.h" #include "ListModel.h"
#include "Application.h" #include "Application.h"
#include <MMCStrings.h> #include "StringUtils.h"
#include <Version.h> #include <Version.h>
#include <QtMath> #include <QtMath>
@ -66,7 +66,7 @@ bool FilterModel::lessThan(const QModelIndex &left, const QModelIndex &right) co
return lv < rv; return lv < rv;
} else if(currentSorting == Sorting::ByName) { } else if(currentSorting == Sorting::ByName) {
return Strings::naturalCompare(leftPack.name, rightPack.name, Qt::CaseSensitive) >= 0; return StringUtils::naturalCompare(leftPack.name, rightPack.name, Qt::CaseSensitive) >= 0;
} }
//UHM, some inavlid value set?! //UHM, some inavlid value set?!

@ -1 +1 @@
Subproject commit 4b166b69f28e70a416a1a04a98f365d2aeb90de8 Subproject commit cc741c9f5f2a62856a2a2e9e275f61eb0591c09c