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:
		@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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))
 | 
			
		||||
        {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user