diff options
Diffstat (limited to 'archived/projt-launcher/launcher/minecraft/mod/Resource.cpp')
| -rw-r--r-- | archived/projt-launcher/launcher/minecraft/mod/Resource.cpp | 343 |
1 files changed, 343 insertions, 0 deletions
diff --git a/archived/projt-launcher/launcher/minecraft/mod/Resource.cpp b/archived/projt-launcher/launcher/minecraft/mod/Resource.cpp new file mode 100644 index 0000000000..3f70a24f41 --- /dev/null +++ b/archived/projt-launcher/launcher/minecraft/mod/Resource.cpp @@ -0,0 +1,343 @@ +// SPDX-License-Identifier: GPL-3.0-only +// SPDX-FileCopyrightText: 2026 Project Tick +// SPDX-FileContributor: Project Tick Team +/* + * ProjT Launcher - Minecraft Launcher + * Copyright (C) 2026 Project Tick + * + * 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, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "Resource.hpp" + +#include <QDirIterator> +#include <QFileInfo> +#include <QRegularExpression> +#include <tuple> + +#include "FileSystem.h" +#include "StringUtils.h" +#include "minecraft/MinecraftInstance.h" +#include "minecraft/PackProfile.h" + +Resource::Resource(QObject* parent) : QObject(parent) +{} + +Resource::Resource(QFileInfo file_info) : QObject() +{ + setFile(file_info); +} + +void Resource::setFile(QFileInfo file_info) +{ + m_file_info = file_info; + parseFile(); +} + +static std::tuple<QString, qint64> calculateFileSize(const QFileInfo& file) +{ + if (file.isDir()) + { + auto dir = QDir(file.absoluteFilePath()); + dir.setFilter(QDir::AllEntries | QDir::NoDotAndDotDot); + auto count = dir.count(); + auto str = QObject::tr("item"); + if (count != 1) + str = QObject::tr("items"); + return { QString("%1 %2").arg(QString::number(count), str), count }; + } + return { StringUtils::humanReadableFileSize(file.size(), true), file.size() }; +} + +void Resource::parseFile() +{ + QString file_name{ m_file_info.fileName() }; + + m_type = ResourceType::UNKNOWN; + + m_internal_id = file_name; + + std::tie(m_size_str, m_size_info) = calculateFileSize(m_file_info); + if (m_file_info.isDir()) + { + m_type = ResourceType::FOLDER; + m_name = file_name; + } + else if (m_file_info.isFile()) + { + if (file_name.endsWith(".disabled")) + { + file_name.chop(9); + m_enabled = false; + } + + if (file_name.endsWith(".zip") || file_name.endsWith(".jar")) + { + m_type = ResourceType::ZIPFILE; + file_name.chop(4); + } + else if (file_name.endsWith(".nilmod")) + { + m_type = ResourceType::ZIPFILE; + file_name.chop(7); + } + else if (file_name.endsWith(".litemod")) + { + m_type = ResourceType::LITEMOD; + file_name.chop(8); + } + else + { + m_type = ResourceType::SINGLEFILE; + } + + m_name = file_name; + } + + m_changed_date_time = m_file_info.lastModified(); +} + +auto Resource::name() const -> QString +{ + if (metadata()) + return metadata()->name; + + return m_name; +} + +static void removeThePrefix(QString& string) +{ + static const QRegularExpression s_regex(QStringLiteral("^(?:the|teh) +"), + QRegularExpression::CaseInsensitiveOption); + string.remove(s_regex); + string = string.trimmed(); +} + +auto Resource::provider() const -> QString +{ + if (metadata()) + return ModPlatform::ProviderCapabilities::readableName(metadata()->provider); + + return tr("Unknown"); +} + +auto Resource::homepage() const -> QString +{ + if (metadata()) + return ModPlatform::getMetaURL(metadata()->provider, metadata()->project_id); + + return {}; +} + +void Resource::setMetadata(std::shared_ptr<Metadata::ModStruct>&& metadata) +{ + if (status() == ResourceStatus::NO_METADATA) + setStatus(ResourceStatus::INSTALLED); + + m_metadata = metadata; +} + +QStringList Resource::issues() const +{ + QStringList result; + result.reserve(m_issues.length()); + + for (const char* issue : m_issues) { + result.append(tr(issue)); + } + + return result; +} + +void Resource::updateIssues(const BaseInstance* inst) +{ + m_issues.clear(); + + if (m_metadata == nullptr) { + return; + } + + auto mcInst = dynamic_cast<const MinecraftInstance*>(inst); + if (mcInst == nullptr) { + return; + } + + auto profile = mcInst->getPackProfile(); + QString mcVersion = profile->getComponentVersion("net.minecraft"); + + if (!m_metadata->mcVersions.empty() && !m_metadata->mcVersions.contains(mcVersion)) { + // delay translation until issues() is called + m_issues.append(QT_TR_NOOP("Not marked as compatible with the instance's game version.")); + } +} + +int Resource::compare(const Resource& other, SortType type) const +{ + switch (type) + { + default: + case SortType::ENABLED: + if (enabled() && !other.enabled()) + return 1; + if (!enabled() && other.enabled()) + return -1; + break; + case SortType::NAME: + { + QString this_name{ name() }; + QString other_name{ other.name() }; + + // Remove common prefixes like "The" for better alphabetical sorting + removeThePrefix(this_name); + removeThePrefix(other_name); + + return QString::compare(this_name, other_name, Qt::CaseInsensitive); + } + case SortType::DATE: + if (dateTimeChanged() > other.dateTimeChanged()) + return 1; + if (dateTimeChanged() < other.dateTimeChanged()) + return -1; + break; + case SortType::SIZE: + { + if (this->type() != other.type()) + { + if (this->type() == ResourceType::FOLDER) + return -1; + if (other.type() == ResourceType::FOLDER) + return 1; + } + + if (sizeInfo() > other.sizeInfo()) + return 1; + if (sizeInfo() < other.sizeInfo()) + return -1; + break; + } + case SortType::PROVIDER: + { + auto compare_result = QString::compare(provider(), other.provider(), Qt::CaseInsensitive); + if (compare_result != 0) + return compare_result; + break; + } + } + + return 0; +} + +bool Resource::applyFilter(QRegularExpression filter) const +{ + return filter.match(name()).hasMatch(); +} + +bool Resource::enable(EnableAction action) +{ + if (m_type == ResourceType::UNKNOWN || m_type == ResourceType::FOLDER) + return false; + + QString path = m_file_info.absoluteFilePath(); + QFile file(path); + + bool enable = true; + switch (action) + { + case EnableAction::ENABLE: enable = true; break; + case EnableAction::DISABLE: enable = false; break; + case EnableAction::TOGGLE: + default: enable = !enabled(); break; + } + + if (m_enabled == enable) + return false; + + if (enable) + { + // m_enabled is false, but there's no '.disabled' suffix. + if (!path.endsWith(".disabled")) + { + qWarning() << "Cannot enable resource" << name() << ": file does not have .disabled suffix"; + return false; + } + path.chop(9); + } + else + { + path += ".disabled"; + if (QFile::exists(path)) + { + path = FS::getUniqueResourceName(path); + } + } + if (!file.rename(path)) + return false; + + setFile(QFileInfo(path)); + + m_enabled = enable; + return true; +} + +auto Resource::destroy(const QDir& index_dir, bool preserve_metadata, bool attempt_trash) -> bool +{ + m_type = ResourceType::UNKNOWN; + + if (!preserve_metadata) + { + qDebug() << QString("Destroying metadata for '%1' on purpose").arg(name()); + destroyMetadata(index_dir); + } + + return (attempt_trash && FS::trash(m_file_info.filePath())) || FS::deletePath(m_file_info.filePath()); +} + +auto Resource::destroyMetadata(const QDir& index_dir) -> void +{ + if (metadata()) + { + Metadata::remove(index_dir, metadata()->slug); + } + else + { + auto n = name(); + Metadata::remove(index_dir, n); + } + m_metadata = nullptr; +} + +bool Resource::isSymLinkUnder(const QString& instPath) const +{ + if (isSymLink()) + return true; + + auto instDir = QDir(instPath); + + auto relAbsPath = instDir.relativeFilePath(m_file_info.absoluteFilePath()); + auto relCanonPath = instDir.relativeFilePath(m_file_info.canonicalFilePath()); + + return relAbsPath != relCanonPath; +} + +bool Resource::isMoreThanOneHardLink() const +{ + return FS::hardLinkCount(m_file_info.absoluteFilePath()) > 1; +} + +auto Resource::getOriginalFileName() const -> QString +{ + auto fileName = m_file_info.fileName(); + if (!m_enabled) + fileName.chop(9); + return fileName; +}
\ No newline at end of file |
