2022-04-13 23:16:36 +01:00
|
|
|
#include "Packwiz.h"
|
|
|
|
|
|
|
|
#include <QDebug>
|
|
|
|
#include <QDir>
|
|
|
|
#include <QObject>
|
|
|
|
|
2022-04-16 02:37:10 +01:00
|
|
|
#include "toml.h"
|
|
|
|
|
|
|
|
#include "minecraft/mod/Mod.h"
|
2022-04-20 00:19:51 +01:00
|
|
|
#include "modplatform/ModIndex.h"
|
2022-04-16 02:37:10 +01:00
|
|
|
|
2022-04-16 17:27:29 +01:00
|
|
|
namespace Packwiz {
|
|
|
|
|
2022-04-15 02:02:41 +01:00
|
|
|
// Helpers
|
2022-04-20 00:19:51 +01:00
|
|
|
static inline auto indexFileName(QString const& mod_name) -> QString
|
2022-04-15 02:02:41 +01:00
|
|
|
{
|
2022-04-17 15:40:41 +01:00
|
|
|
if(mod_name.endsWith(".toml"))
|
|
|
|
return mod_name;
|
2022-04-15 02:02:41 +01:00
|
|
|
return QString("%1.toml").arg(mod_name);
|
|
|
|
}
|
|
|
|
|
2022-04-20 01:10:12 +01:00
|
|
|
static ModPlatform::ProviderCapabilities ProviderCaps;
|
|
|
|
|
2022-04-16 17:27:29 +01:00
|
|
|
auto V1::createModFormat(QDir& index_dir, ModPlatform::IndexedPack& mod_pack, ModPlatform::IndexedVersion& mod_version) -> Mod
|
2022-04-13 23:16:36 +01:00
|
|
|
{
|
|
|
|
Mod mod;
|
|
|
|
|
|
|
|
mod.name = mod_pack.name;
|
|
|
|
mod.filename = mod_version.fileName;
|
|
|
|
|
|
|
|
mod.url = mod_version.downloadUrl;
|
2022-04-20 01:10:12 +01:00
|
|
|
mod.hash_format = ProviderCaps.hashType(mod_pack.provider);
|
2022-04-13 23:16:36 +01:00
|
|
|
mod.hash = ""; // FIXME
|
|
|
|
|
|
|
|
mod.provider = mod_pack.provider;
|
|
|
|
mod.file_id = mod_pack.addonId;
|
|
|
|
mod.project_id = mod_version.fileId;
|
|
|
|
|
|
|
|
return mod;
|
|
|
|
}
|
|
|
|
|
2022-04-16 17:27:29 +01:00
|
|
|
auto V1::createModFormat(QDir& index_dir, ::Mod& internal_mod) -> Mod
|
2022-04-16 02:37:10 +01:00
|
|
|
{
|
|
|
|
auto mod_name = internal_mod.name();
|
|
|
|
|
|
|
|
// Try getting metadata if it exists
|
|
|
|
Mod mod { getIndexForMod(index_dir, mod_name) };
|
|
|
|
if(mod.isValid())
|
|
|
|
return mod;
|
|
|
|
|
|
|
|
// Manually construct packwiz mod
|
|
|
|
mod.name = internal_mod.name();
|
2022-04-16 17:27:29 +01:00
|
|
|
mod.filename = internal_mod.fileinfo().fileName();
|
2022-04-16 02:37:10 +01:00
|
|
|
|
|
|
|
// TODO: Have a mechanism for telling the UI subsystem that we want to gather user information
|
|
|
|
// (i.e. which mod provider we want to use). Maybe an object parameter with a signal for that?
|
|
|
|
|
|
|
|
return mod;
|
|
|
|
}
|
|
|
|
|
2022-04-16 17:27:29 +01:00
|
|
|
void V1::updateModIndex(QDir& index_dir, Mod& mod)
|
2022-04-13 23:16:36 +01:00
|
|
|
{
|
2022-04-16 02:37:10 +01:00
|
|
|
if(!mod.isValid()){
|
|
|
|
qCritical() << QString("Tried to update metadata of an invalid mod!");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-04-13 23:16:36 +01:00
|
|
|
// Ensure the corresponding mod's info exists, and create it if not
|
2022-04-15 02:02:41 +01:00
|
|
|
QFile index_file(index_dir.absoluteFilePath(indexFileName(mod.name)));
|
2022-04-13 23:16:36 +01:00
|
|
|
|
|
|
|
// There's already data on there!
|
2022-04-16 02:37:10 +01:00
|
|
|
// TODO: We should do more stuff here, as the user is likely trying to
|
|
|
|
// override a file. In this case, check versions and ask the user what
|
|
|
|
// they want to do!
|
2022-04-13 23:16:36 +01:00
|
|
|
if (index_file.exists()) { index_file.remove(); }
|
|
|
|
|
|
|
|
if (!index_file.open(QIODevice::ReadWrite)) {
|
2022-04-15 02:02:41 +01:00
|
|
|
qCritical() << QString("Could not open file %1!").arg(indexFileName(mod.name));
|
2022-04-13 23:16:36 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Put TOML data into the file
|
|
|
|
QTextStream in_stream(&index_file);
|
|
|
|
auto addToStream = [&in_stream](QString&& key, QString value) { in_stream << QString("%1 = \"%2\"\n").arg(key, value); };
|
|
|
|
|
|
|
|
{
|
|
|
|
addToStream("name", mod.name);
|
|
|
|
addToStream("filename", mod.filename);
|
|
|
|
addToStream("side", mod.side);
|
|
|
|
|
|
|
|
in_stream << QString("\n[download]\n");
|
|
|
|
addToStream("url", mod.url.toString());
|
|
|
|
addToStream("hash-format", mod.hash_format);
|
|
|
|
addToStream("hash", mod.hash);
|
|
|
|
|
|
|
|
in_stream << QString("\n[update]\n");
|
2022-04-20 01:10:12 +01:00
|
|
|
in_stream << QString("[update.%1]\n").arg(ProviderCaps.name(mod.provider));
|
2022-04-17 15:40:41 +01:00
|
|
|
switch(mod.provider){
|
|
|
|
case(ModPlatform::Provider::FLAME):
|
|
|
|
in_stream << QString("file-id = %1\n").arg(mod.file_id.toString());
|
|
|
|
in_stream << QString("project-id = %1\n").arg(mod.project_id.toString());
|
|
|
|
break;
|
|
|
|
case(ModPlatform::Provider::MODRINTH):
|
|
|
|
addToStream("mod-id", mod.mod_id().toString());
|
|
|
|
addToStream("version", mod.version().toString());
|
|
|
|
break;
|
|
|
|
}
|
2022-04-13 23:16:36 +01:00
|
|
|
}
|
|
|
|
}
|
2022-04-14 01:25:08 +01:00
|
|
|
|
2022-04-16 17:27:29 +01:00
|
|
|
void V1::deleteModIndex(QDir& index_dir, QString& mod_name)
|
2022-04-15 02:02:41 +01:00
|
|
|
{
|
|
|
|
QFile index_file(index_dir.absoluteFilePath(indexFileName(mod_name)));
|
|
|
|
|
|
|
|
if(!index_file.exists()){
|
|
|
|
qWarning() << QString("Tried to delete non-existent mod metadata for %1!").arg(mod_name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!index_file.remove()){
|
|
|
|
qWarning() << QString("Failed to remove metadata for mod %1!").arg(mod_name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-17 15:40:41 +01:00
|
|
|
// Helper functions for extracting data from the TOML file
|
|
|
|
static auto stringEntry(toml_table_t* parent, const char* entry_name) -> QString
|
|
|
|
{
|
|
|
|
toml_datum_t var = toml_string_in(parent, entry_name);
|
|
|
|
if (!var.ok) {
|
|
|
|
qCritical() << QString("Failed to read str property '%1' in mod metadata.").arg(entry_name);
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
QString tmp = var.u.s;
|
|
|
|
free(var.u.s);
|
|
|
|
|
|
|
|
return tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
static auto intEntry(toml_table_t* parent, const char* entry_name) -> int
|
|
|
|
{
|
|
|
|
toml_datum_t var = toml_int_in(parent, entry_name);
|
|
|
|
if (!var.ok) {
|
|
|
|
qCritical() << QString("Failed to read int property '%1' in mod metadata.").arg(entry_name);
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
return var.u.i;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto V1::getIndexForMod(QDir& index_dir, QString& index_file_name) -> Mod
|
2022-04-14 01:25:08 +01:00
|
|
|
{
|
|
|
|
Mod mod;
|
|
|
|
|
2022-04-17 15:40:41 +01:00
|
|
|
QFile index_file(index_dir.absoluteFilePath(indexFileName(index_file_name)));
|
2022-04-14 01:25:08 +01:00
|
|
|
|
2022-04-15 02:02:41 +01:00
|
|
|
if (!index_file.exists()) {
|
2022-04-17 15:40:41 +01:00
|
|
|
qWarning() << QString("Tried to get a non-existent mod metadata for %1").arg(index_file_name);
|
2022-04-16 02:37:10 +01:00
|
|
|
return {};
|
2022-04-15 02:02:41 +01:00
|
|
|
}
|
|
|
|
if (!index_file.open(QIODevice::ReadOnly)) {
|
2022-04-17 15:40:41 +01:00
|
|
|
qWarning() << QString("Failed to open mod metadata for %1").arg(index_file_name);
|
2022-04-16 02:37:10 +01:00
|
|
|
return {};
|
2022-04-15 02:02:41 +01:00
|
|
|
}
|
2022-04-14 01:25:08 +01:00
|
|
|
|
2022-04-20 00:19:51 +01:00
|
|
|
toml_table_t* table = nullptr;
|
2022-04-14 01:25:08 +01:00
|
|
|
|
2022-04-20 00:19:51 +01:00
|
|
|
// NOLINTNEXTLINE(modernize-avoid-c-arrays)
|
2022-04-14 01:25:08 +01:00
|
|
|
char errbuf[200];
|
|
|
|
table = toml_parse(index_file.readAll().data(), errbuf, sizeof(errbuf));
|
|
|
|
|
|
|
|
index_file.close();
|
|
|
|
|
|
|
|
if (!table) {
|
2022-04-15 02:02:41 +01:00
|
|
|
qCritical() << QString("Could not open file %1!").arg(indexFileName(mod.name));
|
2022-04-16 02:37:10 +01:00
|
|
|
return {};
|
2022-04-14 01:25:08 +01:00
|
|
|
}
|
2022-04-17 15:40:41 +01:00
|
|
|
|
2022-04-14 01:25:08 +01:00
|
|
|
{ // Basic info
|
|
|
|
mod.name = stringEntry(table, "name");
|
|
|
|
mod.filename = stringEntry(table, "filename");
|
|
|
|
mod.side = stringEntry(table, "side");
|
|
|
|
}
|
|
|
|
|
|
|
|
{ // [download] info
|
|
|
|
toml_table_t* download_table = toml_table_in(table, "download");
|
|
|
|
if (!download_table) {
|
|
|
|
qCritical() << QString("No [download] section found on mod metadata!");
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
mod.url = stringEntry(download_table, "url");
|
|
|
|
mod.hash_format = stringEntry(download_table, "hash-format");
|
|
|
|
mod.hash = stringEntry(download_table, "hash");
|
|
|
|
}
|
|
|
|
|
|
|
|
{ // [update] info
|
|
|
|
using Provider = ModPlatform::Provider;
|
|
|
|
|
|
|
|
toml_table_t* update_table = toml_table_in(table, "update");
|
|
|
|
if (!update_table) {
|
|
|
|
qCritical() << QString("No [update] section found on mod metadata!");
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2022-04-20 00:19:51 +01:00
|
|
|
toml_table_t* mod_provider_table = nullptr;
|
2022-04-20 01:10:12 +01:00
|
|
|
if ((mod_provider_table = toml_table_in(update_table, ProviderCaps.name(Provider::FLAME)))) {
|
2022-04-14 01:25:08 +01:00
|
|
|
mod.provider = Provider::FLAME;
|
2022-04-17 15:40:41 +01:00
|
|
|
mod.file_id = intEntry(mod_provider_table, "file-id");
|
|
|
|
mod.project_id = intEntry(mod_provider_table, "project-id");
|
2022-04-20 01:10:12 +01:00
|
|
|
} else if ((mod_provider_table = toml_table_in(update_table, ProviderCaps.name(Provider::MODRINTH)))) {
|
2022-04-14 01:25:08 +01:00
|
|
|
mod.provider = Provider::MODRINTH;
|
2022-04-17 15:40:41 +01:00
|
|
|
mod.mod_id() = stringEntry(mod_provider_table, "mod-id");
|
|
|
|
mod.version() = stringEntry(mod_provider_table, "version");
|
2022-04-14 01:25:08 +01:00
|
|
|
} else {
|
2022-04-15 02:02:41 +01:00
|
|
|
qCritical() << QString("No mod provider on mod metadata!");
|
2022-04-14 01:25:08 +01:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
toml_free(table);
|
|
|
|
|
|
|
|
return mod;
|
|
|
|
}
|
2022-04-16 17:27:29 +01:00
|
|
|
|
|
|
|
} // namespace Packwiz
|