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 "minecraft/mod/tasks/LocalDataPackParseTask.h"
// Values taken from:
// 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 = {

View File

@ -43,6 +43,7 @@
#include "MetadataHandler.h"
#include "Version.h"
#include "minecraft/mod/ModDetails.h"
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;
}
void Mod::setDetails(const ModDetails& details) {
m_local_details = details;
}
std::pair<int, bool> Mod::compare(const Resource& other, SortType type) const
{
auto cast_other = dynamic_cast<Mod const*>(&other);
@ -190,3 +195,8 @@ void Mod::finishResolvingWithDetails(ModDetails&& details)
if (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 setMetadata(std::shared_ptr<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]] bool applyFilter(QRegularExpression filter) const override;

View File

@ -81,7 +81,7 @@ struct ModDetails
ModDetails() = default;
/** Metadata should be handled manually to properly set the mod status. */
ModDetails(ModDetails& other)
ModDetails(const ModDetails& other)
: mod_id(other.mod_id)
, name(other.name)
, version(other.version)
@ -92,7 +92,7 @@ struct ModDetails
, status(other.status)
{}
ModDetails& operator=(ModDetails& other)
ModDetails& operator=(const ModDetails& other)
{
this->mod_id = other.mod_id;
this->name = other.name;
@ -106,7 +106,7 @@ struct ModDetails
return *this;
}
ModDetails& operator=(ModDetails&& other)
ModDetails& operator=(const ModDetails&& other)
{
this->mod_id = other.mod_id;
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);
/** 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);
} // namespace ResourcePackUtils
} // namespace DataPackUtils
class LocalDataPackParseTask : public Task {
Q_OBJECT

View File

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

View File

@ -8,6 +8,25 @@
#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
{
Q_OBJECT