From 6d160a7b7e31034c7a657f30003562c20f9b9c21 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Wed, 8 Feb 2023 00:35:03 -0800 Subject: feat: successful process elevation and comunication! Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/StringUtils.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'launcher/StringUtils.h') diff --git a/launcher/StringUtils.h b/launcher/StringUtils.h index 1799605b..1ba19555 100644 --- a/launcher/StringUtils.h +++ b/launcher/StringUtils.h @@ -29,4 +29,6 @@ inline QString fromStdString(string s) #endif int naturalCompare(const QString& s1, const QString& s2, Qt::CaseSensitivity cs); + +QString getRandomAlphaNumeric(const int length); } // namespace StringUtils -- cgit From dc5402349e8c65945d46f1539fd92823a05a391c Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Thu, 23 Feb 2023 19:08:35 -0700 Subject: refactor: use UUID toString mode Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/FileSystem.cpp | 2 +- launcher/InstanceList.cpp | 2 +- launcher/StringUtils.cpp | 14 +++----------- launcher/StringUtils.h | 2 +- 4 files changed, 6 insertions(+), 14 deletions(-) (limited to 'launcher/StringUtils.h') diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp index 2bd0cf52..714af4a0 100644 --- a/launcher/FileSystem.cpp +++ b/launcher/FileSystem.cpp @@ -442,7 +442,7 @@ void create_link::runPrivileged(const QString& offset) make_link_list(offset); - QString serverName = BuildConfig.LAUNCHER_APP_BINARY_NAME + "_filelink_server" + StringUtils::getRandomAlphaNumeric(8); + QString serverName = BuildConfig.LAUNCHER_APP_BINARY_NAME + "_filelink_server" + StringUtils::getRandomAlphaNumeric(); connect(&m_linkServer, &QLocalServer::newConnection, this, [&](){ diff --git a/launcher/InstanceList.cpp b/launcher/InstanceList.cpp index 179bfb9a..5b33d678 100644 --- a/launcher/InstanceList.cpp +++ b/launcher/InstanceList.cpp @@ -875,7 +875,7 @@ Task* InstanceList::wrapInstanceTask(InstanceTask* task) QString InstanceList::getStagedInstancePath() { - QString key = QUuid::createUuid().toString().remove("{").remove("}"); + QString key = QUuid::createUuid().toString(QUuid::WithoutBraces); QString tempDir = ".LAUNCHER_TEMP/"; QString relPath = FS::PathCombine(tempDir, key); QDir rootPath(m_instDir); diff --git a/launcher/StringUtils.cpp b/launcher/StringUtils.cpp index 93a44d4c..2fa56501 100644 --- a/launcher/StringUtils.cpp +++ b/launcher/StringUtils.cpp @@ -1,6 +1,6 @@ #include "StringUtils.h" -#include +#include /// If you're wondering where these came from exactly, then know you're not the only one =D @@ -77,15 +77,7 @@ int StringUtils::naturalCompare(const QString& s1, const QString& s2, Qt::CaseSe return QString::compare(s1, s2, cs); } -QString StringUtils::getRandomAlphaNumeric(const int length) +QString StringUtils::getRandomAlphaNumeric() { - const QString possibleCharacters("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); - QString randomString; - for(int i=0; i < length; ++i) - { - int index = QRandomGenerator::global()->bounded(0, possibleCharacters.length()); - QChar nextChar = possibleCharacters.at(index); - randomString.append(nextChar); - } - return randomString; + return QUuid::createUuid().toString(QUuid::Id128); } diff --git a/launcher/StringUtils.h b/launcher/StringUtils.h index 1ba19555..c4a6ab31 100644 --- a/launcher/StringUtils.h +++ b/launcher/StringUtils.h @@ -30,5 +30,5 @@ inline QString fromStdString(string s) int naturalCompare(const QString& s1, const QString& s2, Qt::CaseSensitivity cs); -QString getRandomAlphaNumeric(const int length); +QString getRandomAlphaNumeric(); } // namespace StringUtils -- cgit From 788fa40c2ae0e9786c070f4593a4e7ff6efc77d3 Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Sat, 29 Apr 2023 18:05:48 -0700 Subject: refactor: Move ini to use QSettings && drop get/setList functions Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/BaseInstance.cpp | 12 +- launcher/CMakeLists.txt | 1 + launcher/FileSystem.cpp | 3 +- launcher/QVariantUtils.h | 70 ++++++++++ launcher/StringUtils.cpp | 36 +++++ launcher/StringUtils.h | 36 +++++ launcher/minecraft/mod/tasks/LocalModParseTask.cpp | 6 +- launcher/settings/INIFile.cpp | 153 ++++----------------- launcher/settings/INIFile.h | 76 +++++----- launcher/settings/SettingsObject.cpp | 13 -- launcher/settings/SettingsObject.h | 39 ------ tests/INIFile_test.cpp | 24 +--- 12 files changed, 219 insertions(+), 250 deletions(-) create mode 100644 launcher/QVariantUtils.h (limited to 'launcher/StringUtils.h') diff --git a/launcher/BaseInstance.cpp b/launcher/BaseInstance.cpp index ad45aa2d..a8fce879 100644 --- a/launcher/BaseInstance.cpp +++ b/launcher/BaseInstance.cpp @@ -188,25 +188,25 @@ bool BaseInstance::shouldStopOnConsoleOverflow() const QStringList BaseInstance::getLinkedInstances() const { - return m_settings->getList("linkedInstances"); + return m_settings->get("linkedInstances").toStringList(); } void BaseInstance::setLinkedInstances(const QStringList& list) { - auto linkedInstances = m_settings->getList("linkedInstances"); - m_settings->setList("linkedInstances", list); + auto linkedInstances = m_settings->get("linkedInstances").toStringList(); + m_settings->set("linkedInstances", list); } void BaseInstance::addLinkedInstanceId(const QString& id) { - auto linkedInstances = m_settings->getList("linkedInstances"); + auto linkedInstances = m_settings->get("linkedInstances").toStringList(); linkedInstances.append(id); setLinkedInstances(linkedInstances); } bool BaseInstance::removeLinkedInstanceId(const QString& id) { - auto linkedInstances = m_settings->getList("linkedInstances"); + auto linkedInstances = m_settings->get("linkedInstances").toStringList(); int numRemoved = linkedInstances.removeAll(id); setLinkedInstances(linkedInstances); return numRemoved > 0; @@ -214,7 +214,7 @@ bool BaseInstance::removeLinkedInstanceId(const QString& id) bool BaseInstance::isLinkedToInstanceId(const QString& id) const { - auto linkedInstances = m_settings->getList("linkedInstances"); + auto linkedInstances = m_settings->get("linkedInstances").toStringList(); return linkedInstances.contains(id); } diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 4de0f73b..def73b85 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -26,6 +26,7 @@ set(CORE_SOURCES MMCZip.cpp StringUtils.h StringUtils.cpp + QVariantUtils.h RuntimeContext.h # Basic instance manipulation tasks (derived from InstanceTask) diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp index bd985c5b..d98526df 100644 --- a/launcher/FileSystem.cpp +++ b/launcher/FileSystem.cpp @@ -329,8 +329,7 @@ bool create_link::operator()(const QString& offset, bool dryRun) /** * @brief Make a list of all the links to make - * @param offset subdirectory form src to link to dest - * @return if there was an error during the attempt to link + * @param offset subdirectory of src to link to dest */ void create_link::make_link_list(const QString& offset) { diff --git a/launcher/QVariantUtils.h b/launcher/QVariantUtils.h new file mode 100644 index 00000000..7e422c3e --- /dev/null +++ b/launcher/QVariantUtils.h @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (C) 2023 flowln + * + * 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 +#include + +namespace QVariantUtils { + +template +inline QList toList(QVariant src) { + QVariantList variantList = src.toList(); + + QList list_t; + list_t.reserve(variantList.size()); + for (const QVariant& v : variantList) + { + list_t.append(v.value()); + } + return list_t; +} + +template +inline QVariant fromList(QList val) { + QVariantList variantList; + variantList.reserve(val.size()); + for (const T& v : val) + { + variantList.append(v); + } + + return variantList; +} + +} \ No newline at end of file diff --git a/launcher/StringUtils.cpp b/launcher/StringUtils.cpp index 2fa56501..d109d63d 100644 --- a/launcher/StringUtils.cpp +++ b/launcher/StringUtils.cpp @@ -1,3 +1,39 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (C) 2023 flowln + * + * 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. + */ + #include "StringUtils.h" #include diff --git a/launcher/StringUtils.h b/launcher/StringUtils.h index c4a6ab31..36c8cf8f 100644 --- a/launcher/StringUtils.h +++ b/launcher/StringUtils.h @@ -1,3 +1,39 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (C) 2023 flowln + * + * 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 diff --git a/launcher/minecraft/mod/tasks/LocalModParseTask.cpp b/launcher/minecraft/mod/tasks/LocalModParseTask.cpp index da27a505..5342d693 100644 --- a/launcher/minecraft/mod/tasks/LocalModParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalModParseTask.cpp @@ -242,7 +242,7 @@ ModDetails ReadQuiltModInfo(QByteArray contents) return details; } -ModDetails ReadForgeInfo(QByteArray contents) +ModDetails ReadForgeInfo(QString fileName) { ModDetails details; // Read the data @@ -250,7 +250,7 @@ ModDetails ReadForgeInfo(QByteArray contents) details.mod_id = "Forge"; details.homeurl = "http://www.minecraftforge.net/forum/"; INIFile ini; - if (!ini.loadFile(contents)) + if (!ini.loadFile(fileName)) return details; QString major = ini.get("forge.major.number", "0").toString(); @@ -422,7 +422,7 @@ bool processZIP(Mod& mod, ProcessingLevel level) return false; } - details = ReadForgeInfo(file.readAll()); + details = ReadForgeInfo(file.getFileName()); file.close(); zip.close(); diff --git a/launcher/settings/INIFile.cpp b/launcher/settings/INIFile.cpp index e48e6f47..f0347cab 100644 --- a/launcher/settings/INIFile.cpp +++ b/launcher/settings/INIFile.cpp @@ -1,7 +1,8 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher + * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (C) 2023 flowln * * 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 @@ -42,132 +43,51 @@ #include #include -INIFile::INIFile() -{ -} - -QString INIFile::unescape(QString orig) -{ - QString out; - QChar prev = QChar::Null; - for(auto c: orig) - { - if(prev == '\\') - { - if(c == 'n') - out += '\n'; - else if(c == 't') - out += '\t'; - else if(c == '#') - out += '#'; - else - out += c; - prev = QChar::Null; - } - else - { - if(c == '\\') - { - prev = c; - continue; - } - out += c; - prev = QChar::Null; - } - } - return out; -} +#include -QString INIFile::escape(QString orig) +INIFile::INIFile() { - QString out; - for(auto c: orig) - { - if(c == '\n') - out += "\\n"; - else if (c == '\t') - out += "\\t"; - else if(c == '\\') - out += "\\\\"; - else if(c == '#') - out += "\\#"; - else - out += c; - } - return out; } bool INIFile::saveFile(QString fileName) { - QByteArray outArray; + QSettings _settings_obj{ fileName, QSettings::Format::IniFormat }; + _settings_obj.setFallbacksEnabled(false); + for (Iterator iter = begin(); iter != end(); iter++) - { - QString value = iter.value().toString(); - value = escape(value); - outArray.append(iter.key().toUtf8()); - outArray.append('='); - outArray.append(value.toUtf8()); - outArray.append('\n'); - } + _settings_obj.setValue(iter.key(), iter.value()); + + _settings_obj.sync(); + + if (auto status = _settings_obj.status(); status != QSettings::Status::NoError) { + // Shouldn't be possible! + Q_ASSERT(status != QSettings::Status::FormatError); + + if (status == QSettings::Status::AccessError) + qCritical() << "An access error occurred (e.g. trying to write to a read-only file)."; - try - { - FS::write(fileName, outArray); - } - catch (const Exception &e) - { - qCritical() << e.what(); return false; } return true; } - bool INIFile::loadFile(QString fileName) { - QFile file(fileName); - if (!file.open(QIODevice::ReadOnly)) + QSettings _settings_obj{ fileName, QSettings::Format::IniFormat }; + _settings_obj.setFallbacksEnabled(false); + + if (auto status = _settings_obj.status(); status != QSettings::Status::NoError) { + if (status == QSettings::Status::AccessError) + qCritical() << "An access error occurred (e.g. trying to write to a read-only file)."; + if (status == QSettings::Status::FormatError) + qCritical() << "A format error occurred (e.g. loading a malformed INI file)."; return false; - bool success = loadFile(file.readAll()); - file.close(); - return success; -} - -bool INIFile::loadFile(QByteArray file) -{ - QTextStream in(file); -#if QT_VERSION <= QT_VERSION_CHECK(6, 0, 0) - in.setCodec("UTF-8"); -#endif - - QStringList lines = in.readAll().split('\n'); - for (int i = 0; i < lines.count(); i++) - { - QString &lineRaw = lines[i]; - // Ignore comments. - int commentIndex = 0; - QString line = lineRaw; - // Search for comments until no more escaped # are available - while((commentIndex = line.indexOf('#', commentIndex + 1)) != -1) { - if(commentIndex > 0 && line.at(commentIndex - 1) == '\\') { - continue; - } - line = line.left(lineRaw.indexOf('#')).trimmed(); - } - - int eqPos = line.indexOf('='); - if (eqPos == -1) - continue; - QString key = line.left(eqPos).trimmed(); - QString valueStr = line.right(line.length() - eqPos - 1).trimmed(); - - valueStr = unescape(valueStr); - - QVariant value(valueStr); - this->operator[](key) = value; } + for (auto&& key : _settings_obj.allKeys()) + insert(key, _settings_obj.value(key)); + return true; } @@ -184,20 +104,3 @@ void INIFile::set(QString key, QVariant val) this->operator[](key) = val; } -void INIFile::setList(QString key, QVariantList val) -{ - QString stringList = QJsonDocument(QVariant(val).toJsonArray()).toJson(QJsonDocument::Compact); - - this->operator[](key) = stringList; -} - -QVariantList INIFile::getList(QString key, QVariantList def) const -{ - if (this->contains(key)) { - auto src = this->operator[](key); - - return QJsonDocument::fromJson(src.toByteArray()).toVariant().toList(); - } - - return def; -} diff --git a/launcher/settings/INIFile.h b/launcher/settings/INIFile.h index 86bf0898..0d5c05eb 100644 --- a/launcher/settings/INIFile.h +++ b/launcher/settings/INIFile.h @@ -1,16 +1,37 @@ -/* Copyright 2013-2021 MultiMC Contributors +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (C) 2023 flowln * - * 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 + * 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. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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. * - * 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. + * 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 @@ -28,44 +49,9 @@ class INIFile : public QMap public: explicit INIFile(); - bool loadFile(QByteArray file); bool loadFile(QString fileName); bool saveFile(QString fileName); QVariant get(QString key, QVariant def) const; void set(QString key, QVariant val); - static QString unescape(QString orig); - static QString escape(QString orig); - - void setList(QString key, QVariantList val); - template void setList(QString key, QList val) - { - QVariantList variantList; - variantList.reserve(val.size()); - for (const T& v : val) - { - variantList.append(v); - } - - this->setList(key, variantList); - } - - QVariantList getList(QString key, QVariantList def) const; - template QList getList(QString key, QList def) const - { - if (this->contains(key)) { - QVariant src = this->operator[](key); - QVariantList variantList = QJsonDocument::fromJson(src.toByteArray()).toVariant().toList(); - - QListTList; - TList.reserve(variantList.size()); - for (const QVariant& v : variantList) - { - TList.append(v.value()); - } - return TList; - } - - return def; - } }; diff --git a/launcher/settings/SettingsObject.cpp b/launcher/settings/SettingsObject.cpp index 4c51d6e9..8a0bc045 100644 --- a/launcher/settings/SettingsObject.cpp +++ b/launcher/settings/SettingsObject.cpp @@ -121,19 +121,6 @@ bool SettingsObject::contains(const QString &id) return m_settings.contains(id); } -bool SettingsObject::setList(const QString &id, QVariantList value) -{ - QString stringList = QJsonDocument(QVariant(value).toJsonArray()).toJson(QJsonDocument::Compact); - - return set(id, stringList); -} - -QVariantList SettingsObject::getList(const QString &id) -{ - QVariant value = this->get(id); - return QJsonDocument::fromJson(value.toByteArray()).toVariant().toList(); -} - bool SettingsObject::reload() { for (auto setting : m_settings.values()) diff --git a/launcher/settings/SettingsObject.h b/launcher/settings/SettingsObject.h index ff430172..4d735511 100644 --- a/launcher/settings/SettingsObject.h +++ b/launcher/settings/SettingsObject.h @@ -144,45 +144,6 @@ public: */ bool contains(const QString &id); - /*! - * \brief Sets the value of the setting with the given ID with a json list. - * If no setting with the given ID exists, returns false - * \param id The ID of the setting to change. - * \param value The new value of the setting. - */ - bool setList(const QString &id, QVariantList value); - template bool setList(const QString &id, QList val) - { - QVariantList variantList; - variantList.reserve(val.size()); - for (const T& v : val) - { - variantList.append(v); - } - - return setList(id, variantList); - } - - /** - * \brief Gets the value of the setting with the given ID as if it were a json list. - * \param id The ID of the setting to change. - * \return The setting's value as a QVariantList. - * If no setting with the given ID exists, returns an empty QVariantList. - */ - QVariantList getList(const QString &id); - template QList getList(const QString &id) - { - QVariantList variantList = this->getList(id); - - QListTList; - TList.reserve(variantList.size()); - for (const QVariant& v : variantList) - { - TList.append(v.value()); - } - return TList; - } - /*! * \brief Reloads the settings and emit signals for changed settings * \return True if reloading was successful diff --git a/tests/INIFile_test.cpp b/tests/INIFile_test.cpp index d13937c0..4be8133c 100644 --- a/tests/INIFile_test.cpp +++ b/tests/INIFile_test.cpp @@ -4,6 +4,7 @@ #include #include +#include class IniFileTest : public QObject { @@ -30,15 +31,6 @@ slots: QTest::newRow("Escape sequences 2") << "\"\n\n\""; QTest::newRow("Hashtags") << "some data#something"; } - void test_Escape() - { - QFETCH(QString, through); - - QString there = INIFile::escape(through); - QString back = INIFile::unescape(there); - - QCOMPARE(back, through); - } void test_SaveLoad() { @@ -61,32 +53,30 @@ slots: void test_SaveLoadLists() { - QString slist_strings = "[\"a\",\"b\",\"c\"]"; + QString slist_strings = "(\"a\",\"b\",\"c\")"; QStringList list_strings = {"a", "b", "c"}; - QString slist_numbers = "[1,2,3,10]"; + QString slist_numbers = "(1,2,3,10)"; QList list_numbers = {1, 2, 3, 10}; QString filename = "test_SaveLoadLists.ini"; INIFile f; - f.setList("list_strings", list_strings); - f.setList("list_numbers", list_numbers); + f.set("list_strings", list_strings); + f.set("list_numbers", QVariantUtils::fromList(list_numbers)); f.saveFile(filename); // load INIFile f2; f2.loadFile(filename); - QStringList out_list_strings = f2.getList("list_strings", QStringList()); + QStringList out_list_strings = f2.get("list_strings", QStringList()).toStringList(); qDebug() << "OutStringList" << out_list_strings; - QList out_list_numbers = f2.getList("list_numbers", QList()); + QList out_list_numbers = QVariantUtils::toList(f2.get("list_numbers", QVariantUtils::fromList(QList()))); qDebug() << "OutNumbersList" << out_list_numbers; - QCOMPARE(f2.get("list_strings","NOT SET").toString(), slist_strings); QCOMPARE(out_list_strings, list_strings); - QCOMPARE(f2.get("list_numbers","NOT SET").toString(), slist_numbers); QCOMPARE(out_list_numbers, list_numbers); } }; -- cgit From 62a402d05aa2b4722201506794fd20e95b05845c Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Sat, 6 May 2023 19:10:58 -0700 Subject: refactor: move functions to utils + code-review fixes Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com> --- launcher/Application.cpp | 8 +- launcher/MMCTime.cpp | 64 +++++++++++ launcher/MMCTime.h | 9 ++ launcher/StringUtils.cpp | 67 ++++++++++++ launcher/StringUtils.h | 12 +++ .../modplatform/atlauncher/ATLPackInstallTask.cpp | 2 +- launcher/net/Download.cpp | 119 +++------------------ launcher/net/NetAction.h | 36 ++----- launcher/qtlogging.ini | 2 + launcher/tasks/ConcurrentTask.h | 21 ++-- 10 files changed, 194 insertions(+), 146 deletions(-) (limited to 'launcher/StringUtils.h') diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 28c2a9ce..1659eb44 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -423,16 +423,16 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) foundLoggingRules = QFile::exists(logRulesPath); // search the dataPath() - + // seach app data standard path if(!foundLoggingRules && !isPortable() && dirParam.isEmpty()) { - logRulesPath = QStandardPaths::locate(QStandardPaths::AppDataLocation, logRulesFile); + logRulesPath = QStandardPaths::locate(QStandardPaths::AppDataLocation, FS::PathCombine("..", logRulesFile)); if(!logRulesPath.isEmpty()) { qDebug() << "Found" << logRulesPath << "..."; foundLoggingRules = true; } } - - if(!QFile::exists(logRulesPath)) { + // seach root path + if(!foundLoggingRules) { logRulesPath = FS::PathCombine(m_rootPath, logRulesFile); qDebug() << "Testing" << logRulesPath << "..."; foundLoggingRules = QFile::exists(logRulesPath); diff --git a/launcher/MMCTime.cpp b/launcher/MMCTime.cpp index 70bc4135..1702ec06 100644 --- a/launcher/MMCTime.cpp +++ b/launcher/MMCTime.cpp @@ -18,6 +18,8 @@ #include #include +#include +#include QString Time::prettifyDuration(int64_t duration) { int seconds = (int) (duration % 60); @@ -36,3 +38,65 @@ QString Time::prettifyDuration(int64_t duration) { } return QObject::tr("%1d %2h %3min").arg(days).arg(hours).arg(minutes); } + +QString Time::humanReadableDuration(double duration, int precision) { + + using days = std::chrono::duration>; + + QString outStr; + QTextStream os(&outStr); + + bool neg = false; + if (duration < 0) { + neg = true; // flag + duration *= -1; // invert + } + + auto std_duration = std::chrono::duration(duration); + auto d = std::chrono::duration_cast(std_duration); + std_duration -= d; + auto h = std::chrono::duration_cast(std_duration); + std_duration -= h; + auto m = std::chrono::duration_cast(std_duration); + std_duration -= m; + auto s = std::chrono::duration_cast(std_duration); + std_duration -= s; + auto ms = std::chrono::duration_cast(std_duration); + + auto dc = d.count(); + auto hc = h.count(); + auto mc = m.count(); + auto sc = s.count(); + auto msc = ms.count(); + + if (neg) { + os << '-'; + } + if (dc) { + os << dc << QObject::tr("days"); + } + if (hc) { + if (dc) + os << " "; + os << qSetFieldWidth(2) << hc << QObject::tr("h"); // hours + } + if (mc) { + if (dc || hc) + os << " "; + os << qSetFieldWidth(2) << mc << QObject::tr("m"); // minutes + } + if (dc || hc || mc || sc) { + if (dc || hc || mc) + os << " "; + os << qSetFieldWidth(2) << sc << QObject::tr("s"); // seconds + } + if ((msc && (precision > 0)) || !(dc || hc || mc || sc)) { + if (dc || hc || mc || sc) + os << " "; + os << qSetFieldWidth(0) << qSetRealNumberPrecision(precision) << msc << QObject::tr("ms"); // miliseconds + } + + os.flush(); + + return outStr; +} \ No newline at end of file diff --git a/launcher/MMCTime.h b/launcher/MMCTime.h index 10ff2ffe..576b837f 100644 --- a/launcher/MMCTime.h +++ b/launcher/MMCTime.h @@ -22,4 +22,13 @@ namespace Time { QString prettifyDuration(int64_t duration); +/** + * @brief Returns a string with short form time duration ie. `2days 1h3m4s56.0ms` + * miliseconds are only included if `percison` is greater than 0 + * + * @param duration a number of seconds as floating point + * @param precision number of decmial points to display on fractons of a second, defualts to 0. + * @return QString + */ +QString humanReadableDuration(double duration, int precision = 0); } diff --git a/launcher/StringUtils.cpp b/launcher/StringUtils.cpp index 0f3c3669..f677ebf6 100644 --- a/launcher/StringUtils.cpp +++ b/launcher/StringUtils.cpp @@ -1,5 +1,8 @@ #include "StringUtils.h" +#include +#include + /// 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 @@ -74,3 +77,67 @@ int StringUtils::naturalCompare(const QString& s1, const QString& s2, Qt::CaseSe // The two strings are the same (02 == 2) so fall back to the normal sort return QString::compare(s1, s2, cs); } + +/// Truncate a url while keeping its readability py placing the `...` in the middle of the path +QString StringUtils::truncateUrlHumanFriendly(QUrl &url, int max_len, bool hard_limit) +{ + auto display_options = QUrl::RemoveUserInfo | QUrl::RemoveFragment | QUrl::NormalizePathSegments; + auto str_url = url.toDisplayString(display_options); + + if (str_url.length() <= max_len) + return str_url; + + auto url_path_parts = url.path().split('/'); + QString last_path_segment = url_path_parts.takeLast(); + + if (url_path_parts.size() >= 1 && url_path_parts.first().isEmpty()) + url_path_parts.removeFirst(); // drop empty first segment (from leading / ) + + if (url_path_parts.size() >= 1) + url_path_parts.removeLast(); // drop the next to last path segment + + auto url_template = QStringLiteral("%1://%2/%3%4"); + + auto url_compact = url_path_parts.isEmpty() + ? url_template.arg(url.scheme(), url.host(), QStringList({ "...", last_path_segment }).join('/'), url.query()) + : url_template.arg(url.scheme(), url.host(), + QStringList({ url_path_parts.join('/'), "...", last_path_segment }).join('/'), url.query()); + + // remove url parts one by one if it's still too long + while (url_compact.length() > max_len && url_path_parts.size() >= 1) { + url_path_parts.removeLast(); // drop the next to last path segment + url_compact = url_path_parts.isEmpty() + ? url_template.arg(url.scheme(), url.host(), QStringList({ "...", last_path_segment }).join('/'), url.query()) + : url_template.arg(url.scheme(), url.host(), + QStringList({ url_path_parts.join('/'), "...", last_path_segment }).join('/'), url.query()); + } + + if ((url_compact.length() >= max_len) && hard_limit) { + // still too long, truncate normaly + url_compact = QString(str_url); + auto to_remove = url_compact.length() - max_len + 3; + url_compact.remove(url_compact.length() - to_remove - 1, to_remove); + url_compact.append("..."); + } + + return url_compact; + +} + +static const QStringList s_units_si {"KB", "MB", "GB", "TB"}; +static const QStringList s_units_kibi {"KiB", "MiB", "Gib", "TiB"}; + +QString StringUtils::humanReadableFileSize(double bytes, bool use_si, int decimal_points) { + const QStringList units = use_si ? s_units_si : s_units_kibi; + const int scale = use_si ? 1000 : 1024; + + int u = -1; + double r = pow(10, decimal_points); + + do { + bytes /= scale; + u++; + } while (round(abs(bytes) * r) / r >= scale && u < units.length() - 1); + + return QString::number(bytes, 'f', 2) + " " + units[u]; +} \ No newline at end of file diff --git a/launcher/StringUtils.h b/launcher/StringUtils.h index 1799605b..271e5099 100644 --- a/launcher/StringUtils.h +++ b/launcher/StringUtils.h @@ -1,6 +1,7 @@ #pragma once #include +#include namespace StringUtils { @@ -29,4 +30,15 @@ inline QString fromStdString(string s) #endif int naturalCompare(const QString& s1, const QString& s2, Qt::CaseSensitivity cs); + +/** + * @brief Truncate a url while keeping its readability py placing the `...` in the middle of the path + * @param url Url to truncate + * @param max_len max lenght of url in charaters + * @param hard_limit if truncating the path can't get the url short enough, truncate it normaly. + */ +QString truncateUrlHumanFriendly(QUrl &url, int max_len, bool hard_limit = false); + +QString humanReadableFileSize(double bytes, bool use_si = false, int decimal_points = 1); + } // namespace StringUtils diff --git a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp index d130914f..96cea7b7 100644 --- a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp +++ b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp @@ -846,7 +846,7 @@ void PackInstallTask::downloadMods() emitFailed(reason); }); connect(jobPtr.get(), &NetJob::progress, [&](qint64 current, qint64 total) - { + { setDetails(tr("%1 out of %2 complete").arg(current).arg(total)); abortable = true; setProgress(current, total); diff --git a/launcher/net/Download.cpp b/launcher/net/Download.cpp index 082f963d..7617b5a0 100644 --- a/launcher/net/Download.cpp +++ b/launcher/net/Download.cpp @@ -37,7 +37,6 @@ */ #include "Download.h" -#include #include #include @@ -45,106 +44,18 @@ #include "ByteArraySink.h" #include "ChecksumValidator.h" -#include "FileSystem.h" #include "MetaCacheSink.h" -#include "BuildConfig.h" #include "Application.h" +#include "BuildConfig.h" #include "net/Logging.h" #include "net/NetAction.h" -namespace Net { - -QString truncateUrlHumanFriendly(QUrl &url, int max_len, bool hard_limit = false) -{ - auto display_options = QUrl::RemoveUserInfo | QUrl::RemoveFragment | QUrl::NormalizePathSegments; - auto str_url = url.toDisplayString(display_options); - if (str_url.length() <= max_len) - return str_url; - - /* this is a PCRE regular expression that splits a URL (given by the display rules above) into 5 capture groups - * the scheme (ie https://) is group 1 - * the host (with trailing /) is group 2 - * the first part of the path (with trailing /) is group 3 - * the last part of the path (with leading /) is group 5 - * the remainder of the URL is in the .* and in group 4 - * - * See: https://regex101.com/r/inHkek/1 - * for an interactive breakdown - */ - QRegularExpression re(R"(^([\w]+:\/\/)([\w._-]+\/)([\w._-]+\/)(.*)(\/[^]+[^]+)$)"); - - auto url_compact = QString(str_url); - url_compact.replace(re, "\\1\\2\\3...\\5"); - if (url_compact.length() >= max_len) { - url_compact = QString(str_url); - url_compact.replace(re, "\\1\\2...\\5"); - } - - - if ((url_compact.length() >= max_len) && hard_limit) { - auto to_remove = url_compact.length() - max_len + 3; - url_compact.remove(url_compact.length() - to_remove - 1, to_remove); - url_compact.append("..."); - } - - return url_compact; - -} - -QString humanReadableDuration(double duration, int precision = 0) { - - using days = std::chrono::duration>; - - QString outStr; - QTextStream os(&outStr); - - auto std_duration = std::chrono::duration(duration); - auto d = std::chrono::duration_cast(std_duration); - std_duration -= d; - auto h = std::chrono::duration_cast(std_duration); - std_duration -= h; - auto m = std::chrono::duration_cast(std_duration); - std_duration -= m; - auto s = std::chrono::duration_cast(std_duration); - std_duration -= s; - auto ms = std::chrono::duration_cast(std_duration); +#include "MMCTime.h" +#include "StringUtils.h" - auto dc = d.count(); - auto hc = h.count(); - auto mc = m.count(); - auto sc = s.count(); - auto msc = ms.count(); - - if (dc) { - os << dc << "days"; - } - if (hc) { - if (dc) - os << " "; - os << qSetFieldWidth(2) << hc << "h"; - } - if (mc) { - if (dc || hc) - os << " "; - os << qSetFieldWidth(2) << mc << "m"; - } - if (dc || hc || mc || sc) { - if (dc || hc || mc) - os << " "; - os << qSetFieldWidth(2) << sc << "s"; - } - if ((msc && (precision > 0)) || !(dc || hc || mc || sc)) { - if (dc || hc || mc || sc) - os << " "; - os << qSetFieldWidth(0) << qSetRealNumberPrecision(precision) << msc << "ms"; - } - - os.flush(); - - return outStr; -} +namespace Net { auto Download::makeCached(QUrl url, MetaEntryPtr entry, Options options) -> Download::Ptr { @@ -185,7 +96,7 @@ void Download::addValidator(Validator* v) void Download::executeTask() { - setStatus(tr("Downloading %1").arg(truncateUrlHumanFriendly(m_url, 100))); + setStatus(tr("Downloading %1").arg(StringUtils::truncateUrlHumanFriendly(m_url, 80))); if (getState() == Task::State::AbortedByUser) { qCWarning(taskDownloadLogC) << getUid().toString() << "Attempt to start an aborted Download:" << m_url.toString(); @@ -222,12 +133,12 @@ void Download::executeTask() if (!token.isNull()) request.setRawHeader("Authorization", token.toUtf8()); } - + m_last_progress_time = m_clock.now(); m_last_progress_bytes = 0; QNetworkReply* rep = m_network->get(request); - + m_reply.reset(rep); connect(rep, &QNetworkReply::downloadProgress, this, &Download::downloadProgress); connect(rep, &QNetworkReply::finished, this, &Download::downloadFinished); @@ -252,16 +163,19 @@ void Download::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) auto remaing_time_s = (bytesTotal - bytesReceived) / dl_speed_bps; //: Current amount of bytes downloaded, out of the total amount of bytes in the download - QString dl_progress = tr("%1 / %2").arg(humanReadableFileSize(bytesReceived)).arg(humanReadableFileSize(bytesTotal)); - + QString dl_progress = + tr("%1 / %2").arg(StringUtils::humanReadableFileSize(bytesReceived)).arg(StringUtils::humanReadableFileSize(bytesTotal)); + QString dl_speed_str; if (elapsed_ms.count() > 0) { + auto str_eta = bytesTotal > 0 ? Time::humanReadableDuration(remaing_time_s) : tr("unknown"); //: Download speed, in bytes per second (remaining download time in parenthesis) - dl_speed_str = tr("%1 /s (%2)").arg(humanReadableFileSize(dl_speed_bps)).arg(humanReadableDuration(remaing_time_s)); + dl_speed_str = + tr("%1 /s (%2)").arg(StringUtils::humanReadableFileSize(dl_speed_bps)).arg(str_eta); } else { - //: Download speed at 0 bytes per second + //: Download speed at 0 bytes per second dl_speed_str = tr("0 B/s"); - } + } setDetails(dl_progress + "\n" + dl_speed_str); @@ -290,7 +204,8 @@ void Download::sslErrors(const QList& errors) { int i = 1; for (auto error : errors) { - qCCritical(taskDownloadLogC) << getUid().toString() << "Download" << m_url.toString() << "SSL Error #" << i << " : " << error.errorString(); + qCCritical(taskDownloadLogC) << getUid().toString() << "Download" << m_url.toString() << "SSL Error #" << i << " : " + << error.errorString(); auto cert = error.certificate(); qCCritical(taskDownloadLogC) << getUid().toString() << "Certificate in question:\n" << cert.toText(); i++; diff --git a/launcher/net/NetAction.h b/launcher/net/NetAction.h index a13d115f..32095041 100644 --- a/launcher/net/NetAction.h +++ b/launcher/net/NetAction.h @@ -36,38 +36,18 @@ #pragma once -#include - #include #include #include "QObjectPtr.h" #include "tasks/Task.h" -static const QStringList s_units_si {"kB", "MB", "GB", "TB"}; -static const QStringList s_units_kibi {"KiB", "MiB", "Gib", "TiB"}; - -inline QString humanReadableFileSize(double bytes, bool use_si = false, int decimal_points = 1) { - const QStringList units = use_si ? s_units_si : s_units_kibi; - const int scale = use_si ? 1000 : 1024; - - int u = -1; - double r = pow(10, decimal_points); - - do { - bytes /= scale; - u++; - } while (round(abs(bytes) * r) / r >= scale && u < units.length() - 1); - - return QString::number(bytes, 'f', 2) + " " + units[u]; -} - class NetAction : public Task { Q_OBJECT -protected: - explicit NetAction() : Task() {}; + protected: + explicit NetAction() : Task(){}; -public: + public: using Ptr = shared_qobject_ptr; virtual ~NetAction() = default; @@ -76,23 +56,23 @@ public: void setNetwork(shared_qobject_ptr network) { m_network = network; } -protected slots: + protected slots: virtual void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) = 0; virtual void downloadError(QNetworkReply::NetworkError error) = 0; virtual void downloadFinished() = 0; virtual void downloadReadyRead() = 0; -public slots: + public slots: void startAction(shared_qobject_ptr network) { m_network = network; executeTask(); } -protected: - void executeTask() override {}; + protected: + void executeTask() override{}; -public: + public: shared_qobject_ptr m_network; /// the network reply diff --git a/launcher/qtlogging.ini b/launcher/qtlogging.ini index 4543378f..27f4e676 100644 --- a/launcher/qtlogging.ini +++ b/launcher/qtlogging.ini @@ -3,6 +3,8 @@ # prevent log spam and strange bugs # qt.qpa.drawing in particular causes theme artifacts on MacOS qt.*.debug=false +# dont log credentials buy defualt +launcher.auth.credentials.debug=false # remove the debug lines, other log levels still get through launcher.task.net.download.debug=false # enable or disable whole catageries diff --git a/launcher/tasks/ConcurrentTask.h b/launcher/tasks/ConcurrentTask.h index aec707bc..6325fc9e 100644 --- a/launcher/tasks/ConcurrentTask.h +++ b/launcher/tasks/ConcurrentTask.h @@ -35,17 +35,17 @@ */ #pragma once -#include #include #include #include +#include #include #include "tasks/Task.h" class ConcurrentTask : public Task { Q_OBJECT -public: + public: using Ptr = shared_qobject_ptr; explicit ConcurrentTask(QObject* parent = nullptr, QString task_name = "", int max_concurrent = 6); @@ -58,7 +58,7 @@ public: void addTask(Task::Ptr task); -public slots: + public slots: bool abort() override; /** Resets the internal state of the task. @@ -66,20 +66,19 @@ public slots: */ void clear(); -protected -slots: + protected slots: void executeTask() override; virtual void startNext(); void subTaskSucceeded(Task::Ptr); - void subTaskFailed(Task::Ptr, const QString &msg); - void subTaskStatus(Task::Ptr task, const QString &msg); - void subTaskDetails(Task::Ptr task, const QString &msg); + void subTaskFailed(Task::Ptr, const QString& msg); + void subTaskStatus(Task::Ptr task, const QString& msg); + void subTaskDetails(Task::Ptr task, const QString& msg); void subTaskProgress(Task::Ptr task, qint64 current, qint64 total); void subTaskStepProgress(Task::Ptr task, TaskStepProgress const& task_step_progress); -protected: + protected: // NOTE: This is not thread-safe. [[nodiscard]] unsigned int totalSize() const { return m_queue.size() + m_doing.size() + m_done.size(); } @@ -88,13 +87,13 @@ protected: virtual void updateState(); -protected: + protected: QString m_name; QString m_step_status; QQueue m_queue; - QHash m_doing; + QHash m_doing; QHash m_done; QHash m_failed; QHash m_succeeded; -- cgit