/* SPDX-FileCopyrightText: 2026 Project Tick
* SPDX-FileContributor: Project Tick
* SPDX-License-Identifier: GPL-3.0-or-later
*
* MeshMC - A Custom Launcher for Minecraft
* 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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 .
*/
#include "Library.h"
#include "MinecraftInstance.h"
#include
#include
#include
#include
void Library::getApplicableFiles(OpSys system, QStringList& jar,
QStringList& native, QStringList& native32,
QStringList& native64,
const QString& overridePath) const
{
bool local = isLocal();
auto actualPath = [&](QString relPath) {
QFileInfo out(FS::PathCombine(storagePrefix(), relPath));
if (local && !overridePath.isEmpty()) {
QString fileName = out.fileName();
return QFileInfo(FS::PathCombine(overridePath, fileName))
.absoluteFilePath();
}
return out.absoluteFilePath();
};
QString raw_storage = storageSuffix(system);
if (isNative()) {
if (raw_storage.contains("${arch}")) {
auto nat32Storage = raw_storage;
nat32Storage.replace("${arch}", "32");
auto nat64Storage = raw_storage;
nat64Storage.replace("${arch}", "64");
native32 += actualPath(nat32Storage);
native64 += actualPath(nat64Storage);
} else {
native += actualPath(raw_storage);
}
} else {
jar += actualPath(raw_storage);
}
}
QList Library::getDownloads(OpSys system,
class HttpMetaCache* cache,
QStringList& failedLocalFiles,
const QString& overridePath) const
{
QList out;
bool stale = isAlwaysStale();
bool local = isLocal();
auto check_local_file = [&](QString storage) {
QFileInfo fileinfo(storage);
QString fileName = fileinfo.fileName();
auto fullPath = FS::PathCombine(overridePath, fileName);
QFileInfo localFileInfo(fullPath);
if (!localFileInfo.exists()) {
failedLocalFiles.append(localFileInfo.filePath());
return false;
}
return true;
};
auto add_download = [&](QString storage, QString url, QString sha1) {
if (local) {
return check_local_file(storage);
}
auto entry = cache->resolveEntry("libraries", storage);
if (stale) {
entry->setStale(true);
}
if (!entry->isStale())
return true;
Net::Download::Options options;
if (stale) {
options |= Net::Download::Option::AcceptLocalFiles;
}
if (sha1.size()) {
auto rawSha1 = QByteArray::fromHex(sha1.toLatin1());
auto dl = Net::Download::makeCached(url, entry, options);
dl->addValidator(
new Net::ChecksumValidator(QCryptographicHash::Sha1, rawSha1));
qDebug() << "Checksummed Download for:" << rawName().serialize()
<< "storage:" << storage << "url:" << url;
out.append(dl);
} else {
out.append(Net::Download::makeCached(url, entry, options));
qDebug() << "Download for:" << rawName().serialize()
<< "storage:" << storage << "url:" << url;
}
return true;
};
QString raw_storage = storageSuffix(system);
if (m_mojangDownloads) {
if (isNative()) {
if (m_nativeClassifiers.contains(system)) {
auto nativeClassifier = m_nativeClassifiers[system];
if (nativeClassifier.contains("${arch}")) {
auto nat32Classifier = nativeClassifier;
nat32Classifier.replace("${arch}", "32");
auto nat64Classifier = nativeClassifier;
nat64Classifier.replace("${arch}", "64");
auto nat32info =
m_mojangDownloads->getDownloadInfo(nat32Classifier);
if (nat32info) {
auto cooked_storage = raw_storage;
cooked_storage.replace("${arch}", "32");
add_download(cooked_storage, nat32info->url,
nat32info->sha1);
}
auto nat64info =
m_mojangDownloads->getDownloadInfo(nat64Classifier);
if (nat64info) {
auto cooked_storage = raw_storage;
cooked_storage.replace("${arch}", "64");
add_download(cooked_storage, nat64info->url,
nat64info->sha1);
}
} else {
auto info =
m_mojangDownloads->getDownloadInfo(nativeClassifier);
if (info) {
add_download(raw_storage, info->url, info->sha1);
}
}
} else {
qDebug() << "Ignoring native library" << m_name.serialize()
<< "because it has no classifier for current OS";
}
} else {
if (m_mojangDownloads->artifact) {
auto artifact = m_mojangDownloads->artifact;
add_download(raw_storage, artifact->url, artifact->sha1);
} else {
qDebug() << "Ignoring java library" << m_name.serialize()
<< "because it has no artifact";
}
}
} else {
auto raw_dl = [&]() {
if (!m_absoluteURL.isEmpty()) {
return m_absoluteURL;
}
if (m_repositoryURL.isEmpty()) {
return BuildConfig.LIBRARY_BASE + raw_storage;
}
if (m_repositoryURL.endsWith('/')) {
return m_repositoryURL + raw_storage;
} else {
return m_repositoryURL + QChar('/') + raw_storage;
}
}();
if (raw_storage.contains("${arch}")) {
QString cooked_storage = raw_storage;
QString cooked_dl = raw_dl;
add_download(cooked_storage.replace("${arch}", "32"),
cooked_dl.replace("${arch}", "32"), QString());
cooked_storage = raw_storage;
cooked_dl = raw_dl;
add_download(cooked_storage.replace("${arch}", "64"),
cooked_dl.replace("${arch}", "64"), QString());
} else {
add_download(raw_storage, raw_dl, QString());
}
}
return out;
}
bool Library::isActive() const
{
bool result = true;
if (m_rules.empty()) {
result = true;
} else {
RuleAction ruleResult = Disallow;
for (auto rule : m_rules) {
RuleAction temp = rule->apply(this);
if (temp != Defer)
ruleResult = temp;
}
result = result && (ruleResult == Allow);
}
if (isNative()) {
result = result && m_nativeClassifiers.contains(currentSystem);
}
return result;
}
bool Library::isLocal() const
{
return m_hint == "local";
}
bool Library::isAlwaysStale() const
{
return m_hint == "always-stale";
}
void Library::setStoragePrefix(QString prefix)
{
m_storagePrefix = prefix;
}
QString Library::defaultStoragePrefix()
{
return "libraries/";
}
QString Library::storagePrefix() const
{
if (m_storagePrefix.isEmpty()) {
return defaultStoragePrefix();
}
return m_storagePrefix;
}
QString Library::filename(OpSys system) const
{
if (!m_filename.isEmpty()) {
return m_filename;
}
// non-native? use only the gradle specifier
if (!isNative()) {
return m_name.getFileName();
}
// otherwise native, override classifiers. Mojang HACK!
GradleSpecifier nativeSpec = m_name;
if (m_nativeClassifiers.contains(system)) {
nativeSpec.setClassifier(m_nativeClassifiers[system]);
} else {
nativeSpec.setClassifier("INVALID");
}
return nativeSpec.getFileName();
}
QString Library::displayName(OpSys system) const
{
if (!m_displayname.isEmpty())
return m_displayname;
return filename(system);
}
QString Library::storageSuffix(OpSys system) const
{
// non-native? use only the gradle specifier
if (!isNative()) {
return m_name.toPath(m_filename);
}
// otherwise native, override classifiers. Mojang HACK!
GradleSpecifier nativeSpec = m_name;
if (m_nativeClassifiers.contains(system)) {
nativeSpec.setClassifier(m_nativeClassifiers[system]);
} else {
nativeSpec.setClassifier("INVALID");
}
return nativeSpec.toPath(m_filename);
}