Merge pull request #3735 from kumquat-ir/develop

NOISSUE Parse META-INF/mods.toml for Forge 1.14+ mod metadata
This commit is contained in:
Petr Mrázek
2021-05-15 00:36:46 +02:00
committed by GitHub
10 changed files with 2905 additions and 2 deletions

View File

@ -540,7 +540,7 @@ set_target_properties(MultiMC_logic PROPERTIES CXX_VISIBILITY_PRESET hidden VISI
generate_export_header(MultiMC_logic)
# Link
target_link_libraries(MultiMC_logic systeminfo MultiMC_quazip MultiMC_classparser ${NBT_NAME} ${ZLIB_LIBRARIES} optional-bare BuildConfig)
target_link_libraries(MultiMC_logic systeminfo MultiMC_quazip MultiMC_classparser ${NBT_NAME} ${ZLIB_LIBRARIES} optional-bare tomlc99 BuildConfig)
target_link_libraries(MultiMC_logic Qt5::Core Qt5::Xml Qt5::Network Qt5::Concurrent)
# Mark and export headers

View File

@ -6,6 +6,7 @@
#include <QJsonValue>
#include <quazip.h>
#include <quazipfile.h>
#include <toml.h>
#include "settings/INIFile.h"
#include "FileSystem.h"
@ -90,6 +91,124 @@ std::shared_ptr<ModDetails> ReadMCModInfo(QByteArray contents)
return nullptr;
}
// https://github.com/MinecraftForge/Documentation/blob/5ab4ba6cf9abc0ac4c0abd96ad187461aefd72af/docs/gettingstarted/structuring.md
std::shared_ptr<ModDetails> ReadMCModTOML(QByteArray contents)
{
std::shared_ptr<ModDetails> details = std::make_shared<ModDetails>();
char errbuf[200];
// top-level table
toml_table_t* tomlData = toml_parse(contents.data(), errbuf, sizeof(errbuf));
if(!tomlData)
{
return nullptr;
}
// array defined by [[mods]]
toml_array_t* tomlModsArr = toml_array_in(tomlData, "mods");
// we only really care about the first element, since multiple mods in one file is not supported by us at the moment
toml_table_t* tomlModsTable0 = toml_table_at(tomlModsArr, 0);
// mandatory properties - always in [[mods]]
toml_datum_t modIdDatum = toml_string_in(tomlModsTable0, "modId");
if(modIdDatum.ok)
{
details->mod_id = modIdDatum.u.s;
// library says this is required for strings
free(modIdDatum.u.s);
}
toml_datum_t versionDatum = toml_string_in(tomlModsTable0, "version");
if(versionDatum.ok)
{
details->version = versionDatum.u.s;
free(versionDatum.u.s);
}
toml_datum_t displayNameDatum = toml_string_in(tomlModsTable0, "displayName");
if(displayNameDatum.ok)
{
details->name = displayNameDatum.u.s;
free(displayNameDatum.u.s);
}
toml_datum_t descriptionDatum = toml_string_in(tomlModsTable0, "description");
if(descriptionDatum.ok)
{
details->description = descriptionDatum.u.s;
free(descriptionDatum.u.s);
}
// optional properties - can be in the root table or [[mods]]
toml_datum_t authorsDatum = toml_string_in(tomlData, "authors");
QString authors = "";
if(authorsDatum.ok)
{
authors = authorsDatum.u.s;
free(authorsDatum.u.s);
}
else
{
authorsDatum = toml_string_in(tomlModsTable0, "authors");
if(authorsDatum.ok)
{
authors = authorsDatum.u.s;
free(authorsDatum.u.s);
}
}
if(!authors.isEmpty())
{
// author information is stored as a string now, not a list
details->authors.append(authors);
}
// is credits even used anywhere? including this for completion/parity with old data version
toml_datum_t creditsDatum = toml_string_in(tomlData, "credits");
QString credits = "";
if(creditsDatum.ok)
{
authors = creditsDatum.u.s;
free(creditsDatum.u.s);
}
else
{
creditsDatum = toml_string_in(tomlModsTable0, "credits");
if(creditsDatum.ok)
{
credits = creditsDatum.u.s;
free(creditsDatum.u.s);
}
}
details->credits = credits;
toml_datum_t homeurlDatum = toml_string_in(tomlData, "displayURL");
QString homeurl = "";
if(homeurlDatum.ok)
{
homeurl = homeurlDatum.u.s;
free(homeurlDatum.u.s);
}
else
{
homeurlDatum = toml_string_in(tomlModsTable0, "displayURL");
if(homeurlDatum.ok)
{
homeurl = homeurlDatum.u.s;
free(homeurlDatum.u.s);
}
}
if(!homeurl.isEmpty())
{
// fix up url.
if (!homeurl.startsWith("http://") && !homeurl.startsWith("https://") && !homeurl.startsWith("ftp://"))
{
homeurl.prepend("http://");
}
}
details->homeurl = homeurl;
// this seems to be recursive, so it should free everything
toml_free(tomlData);
return details;
}
// https://fabricmc.net/wiki/documentation:fabric_mod_json
std::shared_ptr<ModDetails> ReadFabricModInfo(QByteArray contents)
{
@ -198,7 +317,57 @@ void LocalModParseTask::processAsZip()
QuaZipFile file(&zip);
if (zip.setCurrentFile("mcmod.info"))
if (zip.setCurrentFile("META-INF/mods.toml"))
{
if (!file.open(QIODevice::ReadOnly))
{
zip.close();
return;
}
m_result->details = ReadMCModTOML(file.readAll());
file.close();
// to replace ${file.jarVersion} with the actual version, as needed
if (m_result->details && m_result->details->version == "${file.jarVersion}")
{
if (zip.setCurrentFile("META-INF/MANIFEST.MF"))
{
if (!file.open(QIODevice::ReadOnly))
{
zip.close();
return;
}
// quick and dirty line-by-line parser
auto manifestLines = file.readAll().split('\n');
QString manifestVersion = "";
for (auto &line : manifestLines)
{
if (QString(line).startsWith("Implementation-Version: "))
{
manifestVersion = QString(line).remove("Implementation-Version: ");
break;
}
}
// some mods use ${projectversion} in their build.gradle, causing this mess to show up in MANIFEST.MF
// also keep with forge's behavior of setting the version to "NONE" if none is found
if (manifestVersion.contains("task ':jar' property 'archiveVersion'") || manifestVersion == "")
{
manifestVersion = "NONE";
}
m_result->details->version = manifestVersion;
file.close();
}
}
zip.close();
return;
}
else if (zip.setCurrentFile("mcmod.info"))
{
if (!file.open(QIODevice::ReadOnly))
{