From 7156e086f6ba6993db87396e133b8158bed882c8 Mon Sep 17 00:00:00 2001 From: kumquat-ir <66188216+kumquat-ir@users.noreply.github.com> Date: Fri, 16 Apr 2021 13:33:56 -0700 Subject: parse META-INF/mods.toml for metadata --- api/logic/CMakeLists.txt | 2 +- api/logic/minecraft/mod/LocalModParseTask.cpp | 74 ++++++++++++++++++++++++++- 2 files changed, 74 insertions(+), 2 deletions(-) (limited to 'api/logic') diff --git a/api/logic/CMakeLists.txt b/api/logic/CMakeLists.txt index beaa0c00..23bf848d 100644 --- a/api/logic/CMakeLists.txt +++ b/api/logic/CMakeLists.txt @@ -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 toml11 BuildConfig) target_link_libraries(MultiMC_logic Qt5::Core Qt5::Xml Qt5::Network Qt5::Concurrent) # Mark and export headers diff --git a/api/logic/minecraft/mod/LocalModParseTask.cpp b/api/logic/minecraft/mod/LocalModParseTask.cpp index 22ebd7d4..cb35dca6 100644 --- a/api/logic/minecraft/mod/LocalModParseTask.cpp +++ b/api/logic/minecraft/mod/LocalModParseTask.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include "settings/INIFile.h" #include "FileSystem.h" @@ -90,6 +91,64 @@ std::shared_ptr ReadMCModInfo(QByteArray contents) return nullptr; } + +std::shared_ptr ReadMCModTOML(QByteArray contents) +{ + std::shared_ptr details = std::make_shared(); + + // toml11 throws an error when something goes wrong in parsing, so we need to catch that + try { + // data under the root table + auto tomlData = toml::parse(contents); + // data under [[mods]] + auto tomlModsArr = toml::find>(tomlData, "mods"); + + // these properties are required in this location by forge, and thus can be assumed to exist + // forge supports multiple mods in one file, details of which are accessable here from tomlModsArr[n] + details->mod_id = QString::fromStdString(toml::find(tomlModsArr[0], "modId")); + details->version = QString::fromStdString(toml::find(tomlModsArr[0], "version")); + details->name = QString::fromStdString(toml::find(tomlModsArr[0], "displayName")); + // known issue: sometimes overflows the description box for some reason + details->description = QString::fromStdString(toml::find(tomlModsArr[0], "description")); + + // optional properties - can be stored either in the root table or in [[mods]] + auto authors = QString::fromStdString(toml::find_or(tomlData, "authors", "")); + if(authors.isEmpty()) + { + authors = QString::fromStdString(toml::find_or(tomlModsArr[0], "authors", "")); + } + 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 + auto credits = QString::fromStdString(toml::find_or(tomlData, "credits", "")); + if(credits.isEmpty()) + { + credits = QString::fromStdString(toml::find_or(tomlModsArr[0], "credits", "")); + } + details->credits = credits; + auto homeurl = QString::fromStdString(toml::find_or(tomlData, "displayURL", "")); + if(homeurl.isEmpty()) + { + homeurl = QString::fromStdString(toml::find_or(tomlModsArr[0], "displayURL", "")); + } + if(!homeurl.isEmpty()) + { + // fix up url. + if (!homeurl.startsWith("http://") && !homeurl.startsWith("https://") && !homeurl.startsWith("ftp://")) + { + homeurl.prepend("http://"); + } + } + details->homeurl = homeurl; + } catch (toml::syntax_error&) { + return nullptr; + } + return details; +} + // https://fabricmc.net/wiki/documentation:fabric_mod_json std::shared_ptr ReadFabricModInfo(QByteArray contents) { @@ -198,7 +257,20 @@ 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(); + zip.close(); + return; + } + else if (zip.setCurrentFile("mcmod.info")) { if (!file.open(QIODevice::ReadOnly)) { -- cgit From 13afad80fb9b17503621dc63509113203af8887c Mon Sep 17 00:00:00 2001 From: kumquat-ir <66188216+kumquat-ir@users.noreply.github.com> Date: Fri, 16 Apr 2021 17:45:55 -0700 Subject: replace ${file.jarVersion} with something useful --- api/logic/minecraft/mod/LocalModParseTask.cpp | 37 +++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) (limited to 'api/logic') diff --git a/api/logic/minecraft/mod/LocalModParseTask.cpp b/api/logic/minecraft/mod/LocalModParseTask.cpp index cb35dca6..9f3da5a7 100644 --- a/api/logic/minecraft/mod/LocalModParseTask.cpp +++ b/api/logic/minecraft/mod/LocalModParseTask.cpp @@ -267,6 +267,43 @@ void LocalModParseTask::processAsZip() 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; } -- cgit From e668aa0f95f4f56a1ac4320f014662d279c22708 Mon Sep 17 00:00:00 2001 From: kumquat-ir <66188216+kumquat-ir@users.noreply.github.com> Date: Sat, 17 Apr 2021 09:46:11 -0700 Subject: switch to new toml library --- api/logic/CMakeLists.txt | 2 +- api/logic/minecraft/mod/LocalModParseTask.cpp | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'api/logic') diff --git a/api/logic/CMakeLists.txt b/api/logic/CMakeLists.txt index 23bf848d..91155ea7 100644 --- a/api/logic/CMakeLists.txt +++ b/api/logic/CMakeLists.txt @@ -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 toml11 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 diff --git a/api/logic/minecraft/mod/LocalModParseTask.cpp b/api/logic/minecraft/mod/LocalModParseTask.cpp index 9f3da5a7..d8475fe2 100644 --- a/api/logic/minecraft/mod/LocalModParseTask.cpp +++ b/api/logic/minecraft/mod/LocalModParseTask.cpp @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include "settings/INIFile.h" #include "FileSystem.h" @@ -91,7 +91,7 @@ std::shared_ptr ReadMCModInfo(QByteArray contents) return nullptr; } - +/* std::shared_ptr ReadMCModTOML(QByteArray contents) { std::shared_ptr details = std::make_shared(); @@ -148,7 +148,7 @@ std::shared_ptr ReadMCModTOML(QByteArray contents) } return details; } - +*/ // https://fabricmc.net/wiki/documentation:fabric_mod_json std::shared_ptr ReadFabricModInfo(QByteArray contents) { @@ -257,7 +257,7 @@ void LocalModParseTask::processAsZip() QuaZipFile file(&zip); - if (zip.setCurrentFile("META-INF/mods.toml")) + /*if (zip.setCurrentFile("META-INF/mods.toml")) { if (!file.open(QIODevice::ReadOnly)) { @@ -307,7 +307,7 @@ void LocalModParseTask::processAsZip() zip.close(); return; } - else if (zip.setCurrentFile("mcmod.info")) + else*/ if (zip.setCurrentFile("mcmod.info")) { if (!file.open(QIODevice::ReadOnly)) { -- cgit From e2e5294fb9930024c18f8def55ce79c7f7767f2c Mon Sep 17 00:00:00 2001 From: kumquat-ir <66188216+kumquat-ir@users.noreply.github.com> Date: Sat, 17 Apr 2021 10:33:45 -0700 Subject: reimplement parsing logic for tomlc99 --- api/logic/minecraft/mod/LocalModParseTask.cpp | 150 ++++++++++++++++++-------- 1 file changed, 105 insertions(+), 45 deletions(-) (limited to 'api/logic') diff --git a/api/logic/minecraft/mod/LocalModParseTask.cpp b/api/logic/minecraft/mod/LocalModParseTask.cpp index d8475fe2..0d6972fb 100644 --- a/api/logic/minecraft/mod/LocalModParseTask.cpp +++ b/api/logic/minecraft/mod/LocalModParseTask.cpp @@ -91,64 +91,124 @@ std::shared_ptr ReadMCModInfo(QByteArray contents) return nullptr; } -/* +// https://github.com/MinecraftForge/Documentation/blob/5ab4ba6cf9abc0ac4c0abd96ad187461aefd72af/docs/gettingstarted/structuring.md std::shared_ptr ReadMCModTOML(QByteArray contents) { std::shared_ptr details = std::make_shared(); - // toml11 throws an error when something goes wrong in parsing, so we need to catch that - try { - // data under the root table - auto tomlData = toml::parse(contents); - // data under [[mods]] - auto tomlModsArr = toml::find>(tomlData, "mods"); - - // these properties are required in this location by forge, and thus can be assumed to exist - // forge supports multiple mods in one file, details of which are accessable here from tomlModsArr[n] - details->mod_id = QString::fromStdString(toml::find(tomlModsArr[0], "modId")); - details->version = QString::fromStdString(toml::find(tomlModsArr[0], "version")); - details->name = QString::fromStdString(toml::find(tomlModsArr[0], "displayName")); - // known issue: sometimes overflows the description box for some reason - details->description = QString::fromStdString(toml::find(tomlModsArr[0], "description")); - - // optional properties - can be stored either in the root table or in [[mods]] - auto authors = QString::fromStdString(toml::find_or(tomlData, "authors", "")); - if(authors.isEmpty()) - { - authors = QString::fromStdString(toml::find_or(tomlModsArr[0], "authors", "")); - } - if(!authors.isEmpty()) + 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) { - // author information is stored as a string now, not a list - details->authors.append(authors); + authors = authorsDatum.u.s; + free(authorsDatum.u.s); } - // is credits even used anywhere? including this for completion/parity with old data version - auto credits = QString::fromStdString(toml::find_or(tomlData, "credits", "")); - if(credits.isEmpty()) + } + 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 = QString::fromStdString(toml::find_or(tomlModsArr[0], "credits", "")); + credits = creditsDatum.u.s; + free(creditsDatum.u.s); } - details->credits = credits; - auto homeurl = QString::fromStdString(toml::find_or(tomlData, "displayURL", "")); - if(homeurl.isEmpty()) + } + 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 = QString::fromStdString(toml::find_or(tomlModsArr[0], "displayURL", "")); + homeurl = homeurlDatum.u.s; + free(homeurlDatum.u.s); } - if(!homeurl.isEmpty()) + } + if(!homeurl.isEmpty()) + { + // fix up url. + if (!homeurl.startsWith("http://") && !homeurl.startsWith("https://") && !homeurl.startsWith("ftp://")) { - // fix up url. - if (!homeurl.startsWith("http://") && !homeurl.startsWith("https://") && !homeurl.startsWith("ftp://")) - { - homeurl.prepend("http://"); - } + homeurl.prepend("http://"); } - details->homeurl = homeurl; - } catch (toml::syntax_error&) { - return nullptr; } + 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 ReadFabricModInfo(QByteArray contents) { @@ -257,7 +317,7 @@ void LocalModParseTask::processAsZip() QuaZipFile file(&zip); - /*if (zip.setCurrentFile("META-INF/mods.toml")) + if (zip.setCurrentFile("META-INF/mods.toml")) { if (!file.open(QIODevice::ReadOnly)) { @@ -307,7 +367,7 @@ void LocalModParseTask::processAsZip() zip.close(); return; } - else*/ if (zip.setCurrentFile("mcmod.info")) + else if (zip.setCurrentFile("mcmod.info")) { if (!file.open(QIODevice::ReadOnly)) { -- cgit