feat: add a ModUtils::validate

moves the reading of mod files into `ModUtils` namespace

Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
This commit is contained in:
Rachel Powers 2022-12-10 00:52:50 -07:00
parent 25e23e50ca
commit 878614ff68
9 changed files with 136 additions and 62 deletions

View File

@ -27,8 +27,6 @@
#include "Version.h" #include "Version.h"
#include "minecraft/mod/tasks/LocalDataPackParseTask.h"
// Values taken from: // Values taken from:
// https://minecraft.fandom.com/wiki/Tutorials/Creating_a_data_pack#%22pack_format%22 // https://minecraft.fandom.com/wiki/Tutorials/Creating_a_data_pack#%22pack_format%22
static const QMap<int, std::pair<Version, Version>> s_pack_format_versions = { static const QMap<int, std::pair<Version, Version>> s_pack_format_versions = {

View File

@ -43,6 +43,7 @@
#include "MetadataHandler.h" #include "MetadataHandler.h"
#include "Version.h" #include "Version.h"
#include "minecraft/mod/ModDetails.h"
Mod::Mod(const QFileInfo& file) : Resource(file), m_local_details() Mod::Mod(const QFileInfo& file) : Resource(file), m_local_details()
{ {
@ -68,6 +69,10 @@ void Mod::setMetadata(std::shared_ptr<Metadata::ModStruct>&& metadata)
m_local_details.metadata = metadata; m_local_details.metadata = metadata;
} }
void Mod::setDetails(const ModDetails& details) {
m_local_details = details;
}
std::pair<int, bool> Mod::compare(const Resource& other, SortType type) const std::pair<int, bool> Mod::compare(const Resource& other, SortType type) const
{ {
auto cast_other = dynamic_cast<Mod const*>(&other); auto cast_other = dynamic_cast<Mod const*>(&other);
@ -190,3 +195,8 @@ void Mod::finishResolvingWithDetails(ModDetails&& details)
if (metadata) if (metadata)
setMetadata(std::move(metadata)); setMetadata(std::move(metadata));
} }
bool Mod::valid() const
{
return !m_local_details.mod_id.isEmpty();
}

View File

@ -68,6 +68,9 @@ public:
void setStatus(ModStatus status); void setStatus(ModStatus status);
void setMetadata(std::shared_ptr<Metadata::ModStruct>&& metadata); void setMetadata(std::shared_ptr<Metadata::ModStruct>&& metadata);
void setMetadata(const Metadata::ModStruct& metadata) { setMetadata(std::make_shared<Metadata::ModStruct>(metadata)); } void setMetadata(const Metadata::ModStruct& metadata) { setMetadata(std::make_shared<Metadata::ModStruct>(metadata)); }
void setDetails(const ModDetails& details);
bool valid() const override;
[[nodiscard]] auto compare(Resource const& other, SortType type) const -> std::pair<int, bool> override; [[nodiscard]] auto compare(Resource const& other, SortType type) const -> std::pair<int, bool> override;
[[nodiscard]] bool applyFilter(QRegularExpression filter) const override; [[nodiscard]] bool applyFilter(QRegularExpression filter) const override;

View File

@ -81,7 +81,7 @@ struct ModDetails
ModDetails() = default; ModDetails() = default;
/** Metadata should be handled manually to properly set the mod status. */ /** Metadata should be handled manually to properly set the mod status. */
ModDetails(ModDetails& other) ModDetails(const ModDetails& other)
: mod_id(other.mod_id) : mod_id(other.mod_id)
, name(other.name) , name(other.name)
, version(other.version) , version(other.version)
@ -92,7 +92,7 @@ struct ModDetails
, status(other.status) , status(other.status)
{} {}
ModDetails& operator=(ModDetails& other) ModDetails& operator=(const ModDetails& other)
{ {
this->mod_id = other.mod_id; this->mod_id = other.mod_id;
this->name = other.name; this->name = other.name;
@ -106,7 +106,7 @@ struct ModDetails
return *this; return *this;
} }
ModDetails& operator=(ModDetails&& other) ModDetails& operator=(const ModDetails&& other)
{ {
this->mod_id = other.mod_id; this->mod_id = other.mod_id;
this->name = other.name; this->name = other.name;

View File

@ -39,9 +39,10 @@ bool processFolder(DataPack& pack, ProcessingLevel level = ProcessingLevel::Full
bool processMCMeta(DataPack& pack, QByteArray&& raw_data); bool processMCMeta(DataPack& pack, QByteArray&& raw_data);
/** Checks whether a file is valid as a resource pack or not. */ /** Checks whether a file is valid as a data pack or not. */
bool validate(QFileInfo file); bool validate(QFileInfo file);
} // namespace ResourcePackUtils
} // namespace DataPackUtils
class LocalDataPackParseTask : public Task { class LocalDataPackParseTask : public Task {
Q_OBJECT Q_OBJECT

View File

@ -11,9 +11,10 @@
#include "FileSystem.h" #include "FileSystem.h"
#include "Json.h" #include "Json.h"
#include "minecraft/mod/ModDetails.h"
#include "settings/INIFile.h" #include "settings/INIFile.h"
namespace { namespace ModUtils {
// NEW format // NEW format
// https://github.com/MinecraftForge/FML/wiki/FML-mod-information-file/6f62b37cea040daf350dc253eae6326dd9c822c3 // https://github.com/MinecraftForge/FML/wiki/FML-mod-information-file/6f62b37cea040daf350dc253eae6326dd9c822c3
@ -283,35 +284,45 @@ ModDetails ReadLiteModInfo(QByteArray contents)
return details; return details;
} }
} // namespace bool process(Mod& mod, ProcessingLevel level) {
switch (mod.type()) {
case ResourceType::FOLDER:
return processFolder(mod, level);
case ResourceType::ZIPFILE:
return processZIP(mod, level);
case ResourceType::LITEMOD:
return processLitemod(mod);
default:
qWarning() << "Invalid type for resource pack parse task!";
return false;
}
}
LocalModParseTask::LocalModParseTask(int token, ResourceType type, const QFileInfo& modFile) bool processZIP(Mod& mod, ProcessingLevel level) {
: Task(nullptr, false), m_token(token), m_type(type), m_modFile(modFile), m_result(new Result())
{}
void LocalModParseTask::processAsZip() ModDetails details;
{
QuaZip zip(m_modFile.filePath()); QuaZip zip(mod.fileinfo().filePath());
if (!zip.open(QuaZip::mdUnzip)) if (!zip.open(QuaZip::mdUnzip))
return; return false;
QuaZipFile file(&zip); QuaZipFile file(&zip);
if (zip.setCurrentFile("META-INF/mods.toml")) { if (zip.setCurrentFile("META-INF/mods.toml")) {
if (!file.open(QIODevice::ReadOnly)) { if (!file.open(QIODevice::ReadOnly)) {
zip.close(); zip.close();
return; return false;
} }
m_result->details = ReadMCModTOML(file.readAll()); details = ReadMCModTOML(file.readAll());
file.close(); file.close();
// to replace ${file.jarVersion} with the actual version, as needed // to replace ${file.jarVersion} with the actual version, as needed
if (m_result->details.version == "${file.jarVersion}") { if (details.version == "${file.jarVersion}") {
if (zip.setCurrentFile("META-INF/MANIFEST.MF")) { if (zip.setCurrentFile("META-INF/MANIFEST.MF")) {
if (!file.open(QIODevice::ReadOnly)) { if (!file.open(QIODevice::ReadOnly)) {
zip.close(); zip.close();
return; return false;
} }
// quick and dirty line-by-line parser // quick and dirty line-by-line parser
@ -330,93 +341,134 @@ void LocalModParseTask::processAsZip()
manifestVersion = "NONE"; manifestVersion = "NONE";
} }
m_result->details.version = manifestVersion; details.version = manifestVersion;
file.close(); file.close();
} }
} }
zip.close(); zip.close();
return; mod.setDetails(details);
return true;
} else if (zip.setCurrentFile("mcmod.info")) { } else if (zip.setCurrentFile("mcmod.info")) {
if (!file.open(QIODevice::ReadOnly)) { if (!file.open(QIODevice::ReadOnly)) {
zip.close(); zip.close();
return; return false;
} }
m_result->details = ReadMCModInfo(file.readAll()); details = ReadMCModInfo(file.readAll());
file.close(); file.close();
zip.close(); zip.close();
return;
mod.setDetails(details);
return true;
} else if (zip.setCurrentFile("quilt.mod.json")) { } else if (zip.setCurrentFile("quilt.mod.json")) {
if (!file.open(QIODevice::ReadOnly)) { if (!file.open(QIODevice::ReadOnly)) {
zip.close(); zip.close();
return; return false;
} }
m_result->details = ReadQuiltModInfo(file.readAll()); details = ReadQuiltModInfo(file.readAll());
file.close(); file.close();
zip.close(); zip.close();
return;
mod.setDetails(details);
return true;
} else if (zip.setCurrentFile("fabric.mod.json")) { } else if (zip.setCurrentFile("fabric.mod.json")) {
if (!file.open(QIODevice::ReadOnly)) { if (!file.open(QIODevice::ReadOnly)) {
zip.close(); zip.close();
return; return false;
} }
m_result->details = ReadFabricModInfo(file.readAll()); details = ReadFabricModInfo(file.readAll());
file.close(); file.close();
zip.close(); zip.close();
return;
mod.setDetails(details);
return true;
} else if (zip.setCurrentFile("forgeversion.properties")) { } else if (zip.setCurrentFile("forgeversion.properties")) {
if (!file.open(QIODevice::ReadOnly)) { if (!file.open(QIODevice::ReadOnly)) {
zip.close(); zip.close();
return; return false;
} }
m_result->details = ReadForgeInfo(file.readAll()); details = ReadForgeInfo(file.readAll());
file.close(); file.close();
zip.close(); zip.close();
return;
mod.setDetails(details);
return true;
} }
zip.close(); zip.close();
return false; // no valid mod found in archive
} }
void LocalModParseTask::processAsFolder() bool processFolder(Mod& mod, ProcessingLevel level) {
{
QFileInfo mcmod_info(FS::PathCombine(m_modFile.filePath(), "mcmod.info")); ModDetails details;
if (mcmod_info.isFile()) {
QFileInfo mcmod_info(FS::PathCombine(mod.fileinfo().filePath(), "mcmod.info"));
if (mcmod_info.exists() && mcmod_info.isFile()) {
QFile mcmod(mcmod_info.filePath()); QFile mcmod(mcmod_info.filePath());
if (!mcmod.open(QIODevice::ReadOnly)) if (!mcmod.open(QIODevice::ReadOnly))
return; return false;
auto data = mcmod.readAll(); auto data = mcmod.readAll();
if (data.isEmpty() || data.isNull()) if (data.isEmpty() || data.isNull())
return; return false;
m_result->details = ReadMCModInfo(data); details = ReadMCModInfo(data);
mod.setDetails(details);
return true;
} }
return false; // no valid mcmod.info file found
} }
void LocalModParseTask::processAsLitemod() bool processLitemod(Mod& mod, ProcessingLevel level) {
{
QuaZip zip(m_modFile.filePath()); ModDetails details;
QuaZip zip(mod.fileinfo().filePath());
if (!zip.open(QuaZip::mdUnzip)) if (!zip.open(QuaZip::mdUnzip))
return; return false;
QuaZipFile file(&zip); QuaZipFile file(&zip);
if (zip.setCurrentFile("litemod.json")) { if (zip.setCurrentFile("litemod.json")) {
if (!file.open(QIODevice::ReadOnly)) { if (!file.open(QIODevice::ReadOnly)) {
zip.close(); zip.close();
return; return false;
} }
m_result->details = ReadLiteModInfo(file.readAll()); details = ReadLiteModInfo(file.readAll());
file.close(); file.close();
mod.setDetails(details);
return true;
} }
zip.close(); zip.close();
return false; // no valid litemod.json found in archive
} }
/** Checks whether a file is valid as a mod or not. */
bool validate(QFileInfo file) {
Mod mod{ file };
return ModUtils::process(mod, ProcessingLevel::BasicInfoOnly) && mod.valid();
}
} // namespace ModUtils
LocalModParseTask::LocalModParseTask(int token, ResourceType type, const QFileInfo& modFile)
: Task(nullptr, false), m_token(token), m_type(type), m_modFile(modFile), m_result(new Result())
{}
bool LocalModParseTask::abort() bool LocalModParseTask::abort()
{ {
m_aborted.store(true); m_aborted.store(true);
@ -424,20 +476,11 @@ bool LocalModParseTask::abort()
} }
void LocalModParseTask::executeTask() void LocalModParseTask::executeTask()
{ {
switch (m_type) { Mod mod{ m_modFile };
case ResourceType::ZIPFILE: ModUtils::process(mod, ModUtils::ProcessingLevel::Full);
processAsZip();
break; m_result->details = mod.details();
case ResourceType::FOLDER:
processAsFolder();
break;
case ResourceType::LITEMOD:
processAsLitemod();
break;
default:
break;
}
if (m_aborted) if (m_aborted)
emit finished(); emit finished();

View File

@ -8,6 +8,25 @@
#include "tasks/Task.h" #include "tasks/Task.h"
namespace ModUtils {
ModDetails ReadFabricModInfo(QByteArray contents);
ModDetails ReadQuiltModInfo(QByteArray contents);
ModDetails ReadForgeInfo(QByteArray contents);
ModDetails ReadLiteModInfo(QByteArray contents);
enum class ProcessingLevel { Full, BasicInfoOnly };
bool process(Mod& mod, ProcessingLevel level = ProcessingLevel::Full);
bool processZIP(Mod& mod, ProcessingLevel level = ProcessingLevel::Full);
bool processFolder(Mod& mod, ProcessingLevel level = ProcessingLevel::Full);
bool processLitemod(Mod& mod, ProcessingLevel level = ProcessingLevel::Full);
/** Checks whether a file is valid as a mod or not. */
bool validate(QFileInfo file);
} // namespace ModUtils
class LocalModParseTask : public Task class LocalModParseTask : public Task
{ {
Q_OBJECT Q_OBJECT