From 428f69b38765c18b6f9fa322f84dba90857f122a Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 18 Jul 2025 18:24:54 +0300 Subject: [PATCH] Improve mod parsing Signed-off-by: Trial97 --- launcher/minecraft/World.cpp | 38 ++-- .../minecraft/mod/tasks/LocalModParseTask.cpp | 166 ++++++++---------- .../mod/tasks/LocalTexturePackParseTask.cpp | 40 ++--- 3 files changed, 103 insertions(+), 141 deletions(-) diff --git a/launcher/minecraft/World.cpp b/launcher/minecraft/World.cpp index 8f7174a07..bdbe721e3 100644 --- a/launcher/minecraft/World.cpp +++ b/launcher/minecraft/World.cpp @@ -244,35 +244,23 @@ void World::readFromZip(const QFileInfo& file) { MMCZip::ArchiveReader r(file.absoluteFilePath()); - if (m_isValid = r.collectFiles(); !m_isValid) { - return; - } - - QString path; - const QString levelDat = "level.dat"; - for (auto filePath : r.getFiles()) { + m_isValid = false; + r.parse([this](MMCZip::ArchiveReader::File* file, bool& stop) { + const QString levelDat = "level.dat"; + auto filePath = file->filename(); QFileInfo fi(filePath); if (fi.fileName().compare(levelDat, Qt::CaseInsensitive) == 0) { m_containerOffsetPath = filePath.chopped(levelDat.length()); - path = filePath; - break; + if (!m_containerOffsetPath.isEmpty()) { + return false; + } + m_levelDatTime = file->dateTime(); + loadFromLevelDat(file->readAll()); + m_isValid = true; + stop = true; } - } - - if (m_isValid = !m_containerOffsetPath.isEmpty(); !m_isValid) { - return; - } - - auto zippedFile = r.goToFile(path); - if (m_isValid = !!zippedFile; !m_isValid) { - return; - } - // read the install profile - m_levelDatTime = zippedFile->dateTime(); - if (!m_isValid) { - return; - } - loadFromLevelDat(zippedFile->readAll()); + return true; + }); } bool World::install(const QString& to, const QString& name) diff --git a/launcher/minecraft/mod/tasks/LocalModParseTask.cpp b/launcher/minecraft/mod/tasks/LocalModParseTask.cpp index 151f2d955..52d8206c1 100644 --- a/launcher/minecraft/mod/tasks/LocalModParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalModParseTask.cpp @@ -1,6 +1,7 @@ #include "LocalModParseTask.h" #include +#include #include #include #include @@ -13,6 +14,7 @@ #include "Json.h" #include "archive/ArchiveReader.h" #include "minecraft/mod/ModDetails.h" +#include "modplatform/ModIndex.h" #include "settings/INIFile.h" static const QRegularExpression s_newlineRegex("\r\n|\n|\r"); @@ -470,35 +472,32 @@ bool processZIP(Mod& mod, [[maybe_unused]] ProcessingLevel level) ModDetails details; MMCZip::ArchiveReader zip(mod.fileinfo().filePath()); - if (!zip.collectFiles()) - return false; - auto isForge = zip.exists("META-INF/mods.toml"); - if (isForge || zip.exists("META-INF/neoforge.mods.toml")) { - { - std::unique_ptr file; - if (isForge) { - file = zip.goToFile("META-INF/mods.toml"); - } else { - file = zip.goToFile("META-INF/neoforge.mods.toml"); - } - if (!file) { - return false; - } + bool baseForgePopulated = false; + bool isNilMod = false; + bool isValid = false; + QString manifestVersion = {}; + QByteArray nilData = {}; + QString nilFilePath = {}; - details = ReadMCModTOML(file->readAll()); - } + if (!zip.parse([&details, &baseForgePopulated, &manifestVersion, &isValid, &nilData, &isNilMod, &nilFilePath]( + MMCZip::ArchiveReader::File* file, bool& stop) { + auto filePath = file->filename(); - // to replace ${file.jarVersion} with the actual version, as needed - if (details.version == "${file.jarVersion}") { - if (zip.exists("META-INF/MANIFEST.MF")) { - auto file = zip.goToFile("META-INF/MANIFEST.MF"); - if (!file) { - return false; + if (filePath == "META-INF/mods.toml" || filePath == "META-INF/neoforge.mods.toml") { + details = ReadMCModTOML(file->readAll()); + isValid = true; + if (details.version == "${file.jarVersion}" && !manifestVersion.isEmpty()) { + details.version = manifestVersion; } + stop = details.version != "${file.jarVersion}"; + baseForgePopulated = true; + return true; + } + if (filePath == "META-INF/MANIFEST.MF") { // quick and dirty line-by-line parser auto manifestLines = QString(file->readAll()).split(s_newlineRegex); - QString manifestVersion = ""; + manifestVersion = ""; for (auto& line : manifestLines) { if (line.startsWith("Implementation-Version: ", Qt::CaseInsensitive)) { manifestVersion = line.remove("Implementation-Version: ", Qt::CaseInsensitive); @@ -511,79 +510,64 @@ bool processZIP(Mod& mod, [[maybe_unused]] ProcessingLevel level) if (manifestVersion.contains("task ':jar' property 'archiveVersion'") || manifestVersion == "") { manifestVersion = "NONE"; } - - details.version = manifestVersion; + if (baseForgePopulated) { + details.version = manifestVersion; + stop = true; + } + return true; + } + if (filePath == "mcmod.info") { + details = ReadMCModInfo(file->readAll()); + isValid = true; + stop = true; + return true; + } + if (filePath == "quilt.mod.json") { + details = ReadQuiltModInfo(file->readAll()); + isValid = true; + stop = true; + return true; + } + if (filePath == "fabric.mod.json") { + details = ReadFabricModInfo(file->readAll()); + isValid = true; + stop = true; + return true; + } + if (filePath == "forgeversion.properties") { + details = ReadForgeInfo(file->readAll()); + isValid = true; + stop = true; + return true; + } + if (filePath == "META-INF/nil/mappings.json") { + // nilloader uses the filename of the metadata file for the modid, so we can't know the exact filename + // thankfully, there is a good file to use as a canary so we don't look for nil meta all the time + isNilMod = true; + stop = !nilFilePath.isEmpty(); + file->skip(); + return true; } - } - - mod.setDetails(details); - - return true; - } else if (zip.exists("mcmod.info")) { - auto file = zip.goToFile("mcmod.info"); - if (!file) { - return false; - } - - details = ReadMCModInfo(file->readAll()); - - mod.setDetails(details); - return true; - } else if (zip.exists("quilt.mod.json")) { - auto file = zip.goToFile("quilt.mod.json"); - if (!file) { - return false; - } - - details = ReadQuiltModInfo(file->readAll()); - - mod.setDetails(details); - return true; - } else if (zip.exists("fabric.mod.json")) { - auto file = zip.goToFile("fabric.mod.json"); - if (!file) { - return false; - } - - details = ReadFabricModInfo(file->readAll()); - - mod.setDetails(details); - return true; - } else if (zip.exists("forgeversion.properties")) { - auto file = zip.goToFile("forgeversion.properties"); - if (!file) { - return false; - } - - details = ReadForgeInfo(file->readAll()); - - mod.setDetails(details); - return true; - } else if (zip.exists("META-INF/nil/mappings.json")) { - // nilloader uses the filename of the metadata file for the modid, so we can't know the exact filename - // thankfully, there is a good file to use as a canary so we don't look for nil meta all the time - - QString foundNilMeta; - for (auto& fname : zip.getFiles()) { // nilmods can shade nilloader to be able to run as a standalone agent - which includes nilloader's own meta file - if (fname.endsWith(".nilmod.css") && fname != "nilloader.nilmod.css") { - foundNilMeta = fname; - break; + if (filePath.endsWith(".nilmod.css") && filePath != "nilloader.nilmod.css") { + nilData = file->readAll(); + nilFilePath = filePath; + stop = isNilMod; + return true; } - } - - if (zip.exists(foundNilMeta)) { - auto file = zip.goToFile(foundNilMeta); - if (!file) { - return false; - } - details = ReadNilModInfo(file->readAll(), foundNilMeta); - - mod.setDetails(details); + file->skip(); return true; - } + })) { + return false; + } + if (isNilMod) { + details = ReadNilModInfo(nilData, nilFilePath); + isValid = true; + } + if (isValid) { + mod.setDetails(details); + return true; } - return false; // no valid mod found in archive } diff --git a/launcher/minecraft/mod/tasks/LocalTexturePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalTexturePackParseTask.cpp index 921b6ce09..106d7c323 100644 --- a/launcher/minecraft/mod/tasks/LocalTexturePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalTexturePackParseTask.cpp @@ -90,35 +90,25 @@ bool processZIP(TexturePack& pack, ProcessingLevel level) Q_ASSERT(pack.type() == ResourceType::ZIPFILE); MMCZip::ArchiveReader zip(pack.fileinfo().filePath()); + bool packProcessed = false; + bool iconProcessed = false; - { - auto file = zip.goToFile("pack.txt"); - if (file) { + return zip.parse([&packProcessed, &iconProcessed, &pack, level](MMCZip::ArchiveReader::File* file, bool& stop) { + if (!packProcessed && file->filename() == "pack.txt") { + packProcessed = true; auto data = file->readAll(); - - bool packTXT_result = TexturePackUtils::processPackTXT(pack, std::move(data)); - - if (!packTXT_result) { - return false; - } + stop = packProcessed && (iconProcessed || level == ProcessingLevel::BasicInfoOnly); + return TexturePackUtils::processPackTXT(pack, std::move(data)); } - } - - if (level == ProcessingLevel::BasicInfoOnly) { + if (!iconProcessed && file->filename() == "pack.png") { + iconProcessed = true; + auto data = file->readAll(); + stop = packProcessed && iconProcessed; + return TexturePackUtils::processPackPNG(pack, std::move(data)); + } + file->skip(); return true; - } - - auto file = zip.goToFile("pack.png"); - if (file) { - auto data = file->readAll(); - - bool packPNG_result = TexturePackUtils::processPackPNG(pack, std::move(data)); - - if (!packPNG_result) { - return false; - } - } - return true; + }); } bool processPackTXT(TexturePack& pack, QByteArray&& raw_data)