// SPDX-FileCopyrightText: 2022-2023 Sefa Eyeoglu <contact@scrumplex.net>
//
// SPDX-License-Identifier: GPL-3.0-only AND Apache-2.0
/*
* Prism Launcher - Minecraft Launcher
* Copyright (C) 2022-2023 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
*
* 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 <https://www.gnu.org/licenses/>.
*
* 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 <Version.h>
#include <QCryptographicHash>
#include <QDebug>
#include <QDir>
#include <QFile>
#include <QJsonArray>
#include <QJsonDocument>
#include <QSaveFile>
#include <QTimer>
#include <QUuid>
#include "Exception.h"
#include "FileSystem.h"
#include "Json.h"
#include "minecraft/MinecraftInstance.h"
#include "minecraft/OneSixVersionFormat.h"
#include "minecraft/ProfileUtils.h"
#include "ComponentUpdateTask.h"
#include "PackProfile.h"
#include "PackProfile_p.h"
#include "minecraft/mod/Mod.h"
#include "modplatform/ModIndex.h"
static const QMap<QString, ModPlatform::ModLoaderType> modloaderMapping{ { "net.neoforged", ModPlatform::NeoForge },
{ "net.minecraftforge", ModPlatform::Forge },
{ "net.fabricmc.fabric-loader", ModPlatform::Fabric },
{ "org.quiltmc.quilt-loader", ModPlatform::Quilt },
{ "com.mumfrey.liteloader", ModPlatform::LiteLoader } };
PackProfile::PackProfile(MinecraftInstance* instance) : QAbstractListModel()
{
d.reset(new PackProfileData);
d->m_instance = instance;
d->m_saveTimer.setSingleShot(true);
d->m_saveTimer.setInterval(5000);
d->interactionDisabled = instance->isRunning();
connect(d->m_instance, &BaseInstance::runningStatusChanged, this, &PackProfile::disableInteraction);
connect(&d->m_saveTimer, &QTimer::timeout, this, &PackProfile::save_internal);
}
PackProfile::~PackProfile()
{
saveNow();
}
// BEGIN: component file format
static const int currentComponentsFileVersion = 1;
static QJsonObject componentToJsonV1(ComponentPtr component)
{
QJsonObject obj;
// critical
obj.insert("uid", component->m_uid);
if (!component->m_version.isEmpty()) {
obj.insert("version", component->m_version);
}
if (component->m_dependencyOnly) {
obj.insert("dependencyOnly", true);
}
if (component->m_important) {
obj.insert("important", true);
}
if (component->m_disabled) {
obj.insert("disabled", true);
}
// cached
if (!component->m_cachedVersion.isEmpty()) {
obj.insert("cachedVersion", component->m_cachedVersion);
}
if (!component->m_cachedName.isEmpty()) {
obj.insert("cachedName", component->m_cachedName);
}
Meta::serializeRequires(obj, &component->m_cachedRequires, "cachedRequires");
Meta::serializeRequires(obj, &component->m_cachedConflicts, "cachedConflicts");
if (component->m_cachedVolatile) {
obj.insert("cachedVolatile", true);
}
return obj;
}
static ComponentPtr componentFromJsonV1(PackProfile* parent, const QString& componentJsonPattern, const QJsonObject& obj)
{
// critical
auto uid = Json::requireString(obj.value("uid"));
auto filePath = componentJsonPattern.arg(uid);
auto component = makeShared<Component>(parent, uid);
component->m_version = Json::ensureString(obj.value("version"));
component->m_dependencyOnly = Json::ensureBoolean(obj.value("dependencyOnly"), false);
component->m_important = Json::ensureBoolean(obj.value("important"), false);
// cached
// TODO @RESILIENCE: ignore invalid values/structure here?
component->m_cachedVersion = Json::ensureString(obj.value("cachedVersion"));
component->m_cachedName = Json::ensureString(obj.value