From 64c51a70a3aa110131fb6ad0cabc07ccfdcbb1c0 Mon Sep 17 00:00:00 2001
From: Rachel Powers <508861+Ryex@users.noreply.github.com>
Date: Fri, 9 Dec 2022 20:26:05 -0700
Subject: [PATCH] feat: add initial support for parseing datapacks
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
---
launcher/CMakeLists.txt | 4 +
launcher/minecraft/mod/DataPack.cpp | 110 ++++++++++++
launcher/minecraft/mod/DataPack.h | 73 ++++++++
launcher/minecraft/mod/ResourcePack.cpp | 4 +-
.../mod/tasks/LocalDataPackParseTask.cpp | 169 ++++++++++++++++++
.../mod/tasks/LocalDataPackParseTask.h | 64 +++++++
.../mod/tasks/LocalResourcePackParseTask.cpp | 82 ++++++---
.../mod/tasks/LocalResourcePackParseTask.h | 8 +-
.../mod/tasks/LocalTexturePackParseTask.cpp | 59 ++++--
.../mod/tasks/LocalTexturePackParseTask.h | 8 +-
tests/CMakeLists.txt | 3 +
tests/DataPackParse_test.cpp | 76 ++++++++
.../another_test_folder/pack.mcmeta | Bin 0 -> 104 bytes
.../DataPackParse/test_data_pack_boogaloo.zip | Bin 0 -> 898 bytes
.../DataPackParse/test_folder/pack.mcmeta | Bin 0 -> 86 bytes
15 files changed, 610 insertions(+), 50 deletions(-)
create mode 100644 launcher/minecraft/mod/DataPack.cpp
create mode 100644 launcher/minecraft/mod/DataPack.h
create mode 100644 launcher/minecraft/mod/tasks/LocalDataPackParseTask.cpp
create mode 100644 launcher/minecraft/mod/tasks/LocalDataPackParseTask.h
create mode 100644 tests/DataPackParse_test.cpp
create mode 100644 tests/testdata/DataPackParse/another_test_folder/pack.mcmeta
create mode 100644 tests/testdata/DataPackParse/test_data_pack_boogaloo.zip
create mode 100644 tests/testdata/DataPackParse/test_folder/pack.mcmeta
diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt
index a0d92b6e..c12e6740 100644
--- a/launcher/CMakeLists.txt
+++ b/launcher/CMakeLists.txt
@@ -331,6 +331,8 @@ set(MINECRAFT_SOURCES
minecraft/mod/Resource.cpp
minecraft/mod/ResourceFolderModel.h
minecraft/mod/ResourceFolderModel.cpp
+ minecraft/mod/DataPack.h
+ minecraft/mod/DataPack.cpp
minecraft/mod/ResourcePack.h
minecraft/mod/ResourcePack.cpp
minecraft/mod/ResourcePackFolderModel.h
@@ -347,6 +349,8 @@ set(MINECRAFT_SOURCES
minecraft/mod/tasks/LocalModParseTask.cpp
minecraft/mod/tasks/LocalModUpdateTask.h
minecraft/mod/tasks/LocalModUpdateTask.cpp
+ minecraft/mod/tasks/LocalDataPackParseTask.h
+ minecraft/mod/tasks/LocalDataPackParseTask.cpp
minecraft/mod/tasks/LocalResourcePackParseTask.h
minecraft/mod/tasks/LocalResourcePackParseTask.cpp
minecraft/mod/tasks/LocalTexturePackParseTask.h
diff --git a/launcher/minecraft/mod/DataPack.cpp b/launcher/minecraft/mod/DataPack.cpp
new file mode 100644
index 00000000..3f275160
--- /dev/null
+++ b/launcher/minecraft/mod/DataPack.cpp
@@ -0,0 +1,110 @@
+// SPDX-FileCopyrightText: 2022 Rachel Powers <508861+Ryex@users.noreply.github.com>
+//
+// SPDX-License-Identifier: GPL-3.0-only
+
+/*
+ * Prism Launcher - Minecraft Launcher
+ * Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "DataPack.h"
+
+#include
+#include
+#include
+
+#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> s_pack_format_versions = {
+ { 4, { Version("1.13"), Version("1.14.4") } }, { 5, { Version("1.15"), Version("1.16.1") } },
+ { 6, { Version("1.16.2"), Version("1.16.5") } }, { 7, { Version("1.17"), Version("1.17.1") } },
+ { 8, { Version("1.18"), Version("1.18.1") } }, { 9, { Version("1.18.2"), Version("1.18.2") } },
+ { 10, { Version("1.19"), Version("1.19.3") } },
+};
+
+void DataPack::setPackFormat(int new_format_id)
+{
+ QMutexLocker locker(&m_data_lock);
+
+ if (!s_pack_format_versions.contains(new_format_id)) {
+ qWarning() << "Pack format '%1' is not a recognized resource pack id!";
+ }
+
+ m_pack_format = new_format_id;
+}
+
+void DataPack::setDescription(QString new_description)
+{
+ QMutexLocker locker(&m_data_lock);
+
+ m_description = new_description;
+}
+
+std::pair DataPack::compatibleVersions() const
+{
+ if (!s_pack_format_versions.contains(m_pack_format)) {
+ return { {}, {} };
+ }
+
+ return s_pack_format_versions.constFind(m_pack_format).value();
+}
+
+std::pair DataPack::compare(const Resource& other, SortType type) const
+{
+ auto const& cast_other = static_cast(other);
+
+ switch (type) {
+ default: {
+ auto res = Resource::compare(other, type);
+ if (res.first != 0)
+ return res;
+ }
+ case SortType::PACK_FORMAT: {
+ auto this_ver = packFormat();
+ auto other_ver = cast_other.packFormat();
+
+ if (this_ver > other_ver)
+ return { 1, type == SortType::PACK_FORMAT };
+ if (this_ver < other_ver)
+ return { -1, type == SortType::PACK_FORMAT };
+ }
+ }
+ return { 0, false };
+}
+
+bool DataPack::applyFilter(QRegularExpression filter) const
+{
+ if (filter.match(description()).hasMatch())
+ return true;
+
+ if (filter.match(QString::number(packFormat())).hasMatch())
+ return true;
+
+ if (filter.match(compatibleVersions().first.toString()).hasMatch())
+ return true;
+ if (filter.match(compatibleVersions().second.toString()).hasMatch())
+ return true;
+
+ return Resource::applyFilter(filter);
+}
+
+bool DataPack::valid() const
+{
+ return m_pack_format != 0;
+}
diff --git a/launcher/minecraft/mod/DataPack.h b/launcher/minecraft/mod/DataPack.h
new file mode 100644
index 00000000..17d9b65e
--- /dev/null
+++ b/launcher/minecraft/mod/DataPack.h
@@ -0,0 +1,73 @@
+// SPDX-FileCopyrightText: 2022 Rachel Powers <508861+Ryex@users.noreply.github.com>
+//
+// SPDX-License-Identifier: GPL-3.0-only
+
+/*
+ * Prism Launcher - Minecraft Launcher
+ * Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+
+#include "Resource.h"
+
+#include
+
+class Version;
+
+/* TODO:
+ *
+ * Store localized descriptions
+ * */
+
+class DataPack : public Resource {
+ Q_OBJECT
+ public:
+ using Ptr = shared_qobject_ptr;
+
+ DataPack(QObject* parent = nullptr) : Resource(parent) {}
+ DataPack(QFileInfo file_info) : Resource(file_info) {}
+
+ /** Gets the numerical ID of the pack format. */
+ [[nodiscard]] int packFormat() const { return m_pack_format; }
+ /** Gets, respectively, the lower and upper versions supported by the set pack format. */
+ [[nodiscard]] std::pair compatibleVersions() const;
+
+ /** Gets the description of the resource pack. */
+ [[nodiscard]] QString description() const { return m_description; }
+
+ /** Thread-safe. */
+ void setPackFormat(int new_format_id);
+
+ /** Thread-safe. */
+ void setDescription(QString new_description);
+
+ bool valid() const override;
+
+ [[nodiscard]] auto compare(Resource const& other, SortType type) const -> std::pair override;
+ [[nodiscard]] bool applyFilter(QRegularExpression filter) const override;
+
+ protected:
+ mutable QMutex m_data_lock;
+
+ /* The 'version' of a resource pack, as defined in the pack.mcmeta file.
+ * See https://minecraft.fandom.com/wiki/Tutorials/Creating_a_resource_pack#Formatting_pack.mcmeta
+ */
+ int m_pack_format = 0;
+
+ /** The resource pack's description, as defined in the pack.mcmeta file.
+ */
+ QString m_description;
+};
diff --git a/launcher/minecraft/mod/ResourcePack.cpp b/launcher/minecraft/mod/ResourcePack.cpp
index 3a2fd771..47da4fea 100644
--- a/launcher/minecraft/mod/ResourcePack.cpp
+++ b/launcher/minecraft/mod/ResourcePack.cpp
@@ -17,7 +17,9 @@ static const QMap> s_pack_format_versions = {
{ 3, { Version("1.11"), Version("1.12.2") } }, { 4, { Version("1.13"), Version("1.14.4") } },
{ 5, { Version("1.15"), Version("1.16.1") } }, { 6, { Version("1.16.2"), Version("1.16.5") } },
{ 7, { Version("1.17"), Version("1.17.1") } }, { 8, { Version("1.18"), Version("1.18.2") } },
- { 9, { Version("1.19"), Version("1.19.2") } }, { 11, { Version("1.19.3"), Version("1.19.3") } },
+ { 9, { Version("1.19"), Version("1.19.2") } },
+ // { 11, { Version("22w42a"), Version("22w44a") } }
+ { 12, { Version("1.19.3"), Version("1.19.3") } },
};
void ResourcePack::setPackFormat(int new_format_id)
diff --git a/launcher/minecraft/mod/tasks/LocalDataPackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalDataPackParseTask.cpp
new file mode 100644
index 00000000..8bc8278b
--- /dev/null
+++ b/launcher/minecraft/mod/tasks/LocalDataPackParseTask.cpp
@@ -0,0 +1,169 @@
+// SPDX-FileCopyrightText: 2022 Rachel Powers <508861+Ryex@users.noreply.github.com>
+//
+// SPDX-License-Identifier: GPL-3.0-only
+
+/*
+ * Prism Launcher - Minecraft Launcher
+ * Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "LocalDataPackParseTask.h"
+
+#include "FileSystem.h"
+#include "Json.h"
+
+#include
+#include
+#include
+
+#include
+
+namespace DataPackUtils {
+
+bool process(DataPack& pack, ProcessingLevel level)
+{
+ switch (pack.type()) {
+ case ResourceType::FOLDER:
+ return DataPackUtils::processFolder(pack, level);
+ case ResourceType::ZIPFILE:
+ return DataPackUtils::processZIP(pack, level);
+ default:
+ qWarning() << "Invalid type for resource pack parse task!";
+ return false;
+ }
+}
+
+bool processFolder(DataPack& pack, ProcessingLevel level)
+{
+ Q_ASSERT(pack.type() == ResourceType::FOLDER);
+
+ QFileInfo mcmeta_file_info(FS::PathCombine(pack.fileinfo().filePath(), "pack.mcmeta"));
+ if (mcmeta_file_info.exists() && mcmeta_file_info.isFile()) {
+ QFile mcmeta_file(mcmeta_file_info.filePath());
+ if (!mcmeta_file.open(QIODevice::ReadOnly))
+ return false; // can't open mcmeta file
+
+ auto data = mcmeta_file.readAll();
+
+ bool mcmeta_result = DataPackUtils::processMCMeta(pack, std::move(data));
+
+ mcmeta_file.close();
+ if (!mcmeta_result) {
+ return false; // mcmeta invalid
+ }
+ } else {
+ return false; // mcmeta file isn't a valid file
+ }
+
+ QFileInfo data_dir_info(FS::PathCombine(pack.fileinfo().filePath(), "data"));
+ if (!data_dir_info.exists() || !data_dir_info.isDir()) {
+ return false; // data dir does not exists or isn't valid
+ }
+
+ if (level == ProcessingLevel::BasicInfoOnly) {
+ return true; // only need basic info already checked
+ }
+
+ return true; // all tests passed
+}
+
+bool processZIP(DataPack& pack, ProcessingLevel level)
+{
+ Q_ASSERT(pack.type() == ResourceType::ZIPFILE);
+
+ QuaZip zip(pack.fileinfo().filePath());
+ if (!zip.open(QuaZip::mdUnzip))
+ return false; // can't open zip file
+
+ QuaZipFile file(&zip);
+
+ if (zip.setCurrentFile("pack.mcmeta")) {
+ if (!file.open(QIODevice::ReadOnly)) {
+ qCritical() << "Failed to open file in zip.";
+ zip.close();
+ return false;
+ }
+
+ auto data = file.readAll();
+
+ bool mcmeta_result = DataPackUtils::processMCMeta(pack, std::move(data));
+
+ file.close();
+ if (!mcmeta_result) {
+ return false; // mcmeta invalid
+ }
+ } else {
+ return false; // could not set pack.mcmeta as current file.
+ }
+
+ QuaZipDir zipDir(&zip);
+ if (!zipDir.exists("/data")) {
+ return false; // data dir does not exists at zip root
+ }
+
+ if (level == ProcessingLevel::BasicInfoOnly) {
+ zip.close();
+ return true; // only need basic info already checked
+ }
+
+ zip.close();
+
+ return true;
+}
+
+// https://minecraft.fandom.com/wiki/Tutorials/Creating_a_resource_pack#Formatting_pack.mcmeta
+bool processMCMeta(DataPack& pack, QByteArray&& raw_data)
+{
+ try {
+ auto json_doc = QJsonDocument::fromJson(raw_data);
+ auto pack_obj = Json::requireObject(json_doc.object(), "pack", {});
+
+ pack.setPackFormat(Json::ensureInteger(pack_obj, "pack_format", 0));
+ pack.setDescription(Json::ensureString(pack_obj, "description", ""));
+ } catch (Json::JsonException& e) {
+ qWarning() << "JsonException: " << e.what() << e.cause();
+ return false;
+ }
+ return true;
+}
+
+bool validate(QFileInfo file)
+{
+ DataPack dp{ file };
+ return DataPackUtils::process(dp, ProcessingLevel::BasicInfoOnly) && dp.valid();
+}
+
+} // namespace DataPackUtils
+
+LocalDataPackParseTask::LocalDataPackParseTask(int token, DataPack& dp)
+ : Task(nullptr, false), m_token(token), m_resource_pack(dp)
+{}
+
+bool LocalDataPackParseTask::abort()
+{
+ m_aborted = true;
+ return true;
+}
+
+void LocalDataPackParseTask::executeTask()
+{
+ if (!DataPackUtils::process(m_resource_pack))
+ return;
+
+ if (m_aborted)
+ emitAborted();
+ else
+ emitSucceeded();
+}
diff --git a/launcher/minecraft/mod/tasks/LocalDataPackParseTask.h b/launcher/minecraft/mod/tasks/LocalDataPackParseTask.h
new file mode 100644
index 00000000..ee64df46
--- /dev/null
+++ b/launcher/minecraft/mod/tasks/LocalDataPackParseTask.h
@@ -0,0 +1,64 @@
+// SPDX-FileCopyrightText: 2022 Rachel Powers <508861+Ryex@users.noreply.github.com>
+//
+// SPDX-License-Identifier: GPL-3.0-only
+
+/*
+ * Prism Launcher - Minecraft Launcher
+ * Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+
+#include
+#include
+
+#include "minecraft/mod/DataPack.h"
+
+#include "tasks/Task.h"
+
+namespace DataPackUtils {
+
+enum class ProcessingLevel { Full, BasicInfoOnly };
+
+bool process(DataPack& pack, ProcessingLevel level = ProcessingLevel::Full);
+
+bool processZIP(DataPack& pack, ProcessingLevel level = ProcessingLevel::Full);
+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. */
+bool validate(QFileInfo file);
+} // namespace ResourcePackUtils
+
+class LocalDataPackParseTask : public Task {
+ Q_OBJECT
+ public:
+ LocalDataPackParseTask(int token, DataPack& rp);
+
+ [[nodiscard]] bool canAbort() const override { return true; }
+ bool abort() override;
+
+ void executeTask() override;
+
+ [[nodiscard]] int token() const { return m_token; }
+
+ private:
+ int m_token;
+
+ DataPack& m_resource_pack;
+
+ bool m_aborted = false;
+};
diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp
index 6fd4b024..18d7383d 100644
--- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp
+++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp
@@ -23,6 +23,7 @@
#include
#include
+#include
#include
@@ -32,58 +33,74 @@ bool process(ResourcePack& pack, ProcessingLevel level)
{
switch (pack.type()) {
case ResourceType::FOLDER:
- ResourcePackUtils::processFolder(pack, level);
- return true;
+ return ResourcePackUtils::processFolder(pack, level);
case ResourceType::ZIPFILE:
- ResourcePackUtils::processZIP(pack, level);
- return true;
+ return ResourcePackUtils::processZIP(pack, level);
default:
qWarning() << "Invalid type for resource pack parse task!";
return false;
}
}
-void processFolder(ResourcePack& pack, ProcessingLevel level)
+bool processFolder(ResourcePack& pack, ProcessingLevel level)
{
Q_ASSERT(pack.type() == ResourceType::FOLDER);
QFileInfo mcmeta_file_info(FS::PathCombine(pack.fileinfo().filePath(), "pack.mcmeta"));
- if (mcmeta_file_info.isFile()) {
+ if (mcmeta_file_info.exists() && mcmeta_file_info.isFile()) {
QFile mcmeta_file(mcmeta_file_info.filePath());
if (!mcmeta_file.open(QIODevice::ReadOnly))
- return;
+ return false; // can't open mcmeta file
auto data = mcmeta_file.readAll();
- ResourcePackUtils::processMCMeta(pack, std::move(data));
+ bool mcmeta_result = ResourcePackUtils::processMCMeta(pack, std::move(data));
mcmeta_file.close();
+ if (!mcmeta_result) {
+ return false; // mcmeta invalid
+ }
+ } else {
+ return false; // mcmeta file isn't a valid file
}
- if (level == ProcessingLevel::BasicInfoOnly)
- return;
+ QFileInfo assets_dir_info(FS::PathCombine(pack.fileinfo().filePath(), "assets"));
+ if (!assets_dir_info.exists() || !assets_dir_info.isDir()) {
+ return false; // assets dir does not exists or isn't valid
+ }
+ if (level == ProcessingLevel::BasicInfoOnly) {
+ return true; // only need basic info already checked
+ }
+
QFileInfo image_file_info(FS::PathCombine(pack.fileinfo().filePath(), "pack.png"));
- if (image_file_info.isFile()) {
+ if (image_file_info.exists() && image_file_info.isFile()) {
QFile mcmeta_file(image_file_info.filePath());
if (!mcmeta_file.open(QIODevice::ReadOnly))
- return;
+ return false; // can't open pack.png file
auto data = mcmeta_file.readAll();
- ResourcePackUtils::processPackPNG(pack, std::move(data));
+ bool pack_png_result = ResourcePackUtils::processPackPNG(pack, std::move(data));
mcmeta_file.close();
+ if (!pack_png_result) {
+ return false; // pack.png invalid
+ }
+ } else {
+ return false; // pack.png does not exists or is not a valid file.
}
+
+ return true; // all tests passed
}
-void processZIP(ResourcePack& pack, ProcessingLevel level)
+bool processZIP(ResourcePack& pack, ProcessingLevel level)
{
Q_ASSERT(pack.type() == ResourceType::ZIPFILE);
QuaZip zip(pack.fileinfo().filePath());
if (!zip.open(QuaZip::mdUnzip))
- return;
+ return false; // can't open zip file
QuaZipFile file(&zip);
@@ -91,40 +108,57 @@ void processZIP(ResourcePack& pack, ProcessingLevel level)
if (!file.open(QIODevice::ReadOnly)) {
qCritical() << "Failed to open file in zip.";
zip.close();
- return;
+ return false;
}
auto data = file.readAll();
- ResourcePackUtils::processMCMeta(pack, std::move(data));
+ bool mcmeta_result = ResourcePackUtils::processMCMeta(pack, std::move(data));
file.close();
+ if (!mcmeta_result) {
+ return false; // mcmeta invalid
+ }
+ } else {
+ return false; // could not set pack.mcmeta as current file.
+ }
+
+ QuaZipDir zipDir(&zip);
+ if (!zipDir.exists("/assets")) {
+ return false; // assets dir does not exists at zip root
}
if (level == ProcessingLevel::BasicInfoOnly) {
zip.close();
- return;
+ return true; // only need basic info already checked
}
if (zip.setCurrentFile("pack.png")) {
if (!file.open(QIODevice::ReadOnly)) {
qCritical() << "Failed to open file in zip.";
zip.close();
- return;
+ return false;
}
auto data = file.readAll();
- ResourcePackUtils::processPackPNG(pack, std::move(data));
+ bool pack_png_result = ResourcePackUtils::processPackPNG(pack, std::move(data));
file.close();
+ if (!pack_png_result) {
+ return false; // pack.png invalid
+ }
+ } else {
+ return false; // could not set pack.mcmeta as current file.
}
zip.close();
+
+ return true;
}
// https://minecraft.fandom.com/wiki/Tutorials/Creating_a_resource_pack#Formatting_pack.mcmeta
-void processMCMeta(ResourcePack& pack, QByteArray&& raw_data)
+bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data)
{
try {
auto json_doc = QJsonDocument::fromJson(raw_data);
@@ -134,17 +168,21 @@ void processMCMeta(ResourcePack& pack, QByteArray&& raw_data)
pack.setDescription(Json::ensureString(pack_obj, "description", ""));
} catch (Json::JsonException& e) {
qWarning() << "JsonException: " << e.what() << e.cause();
+ return false;
}
+ return true;
}
-void processPackPNG(ResourcePack& pack, QByteArray&& raw_data)
+bool processPackPNG(ResourcePack& pack, QByteArray&& raw_data)
{
auto img = QImage::fromData(raw_data);
if (!img.isNull()) {
pack.setImage(img);
} else {
qWarning() << "Failed to parse pack.png.";
+ return false;
}
+ return true;
}
bool validate(QFileInfo file)
diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h
index 69dbd6ad..d0c24c2b 100644
--- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h
+++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h
@@ -31,11 +31,11 @@ enum class ProcessingLevel { Full, BasicInfoOnly };
bool process(ResourcePack& pack, ProcessingLevel level = ProcessingLevel::Full);
-void processZIP(ResourcePack& pack, ProcessingLevel level = ProcessingLevel::Full);
-void processFolder(ResourcePack& pack, ProcessingLevel level = ProcessingLevel::Full);
+bool processZIP(ResourcePack& pack, ProcessingLevel level = ProcessingLevel::Full);
+bool processFolder(ResourcePack& pack, ProcessingLevel level = ProcessingLevel::Full);
-void processMCMeta(ResourcePack& pack, QByteArray&& raw_data);
-void processPackPNG(ResourcePack& pack, QByteArray&& raw_data);
+bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data);
+bool processPackPNG(ResourcePack& pack, QByteArray&& raw_data);
/** Checks whether a file is valid as a resource pack or not. */
bool validate(QFileInfo file);
diff --git a/launcher/minecraft/mod/tasks/LocalTexturePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalTexturePackParseTask.cpp
index adb19aca..e4492f12 100644
--- a/launcher/minecraft/mod/tasks/LocalTexturePackParseTask.cpp
+++ b/launcher/minecraft/mod/tasks/LocalTexturePackParseTask.cpp
@@ -32,18 +32,16 @@ bool process(TexturePack& pack, ProcessingLevel level)
{
switch (pack.type()) {
case ResourceType::FOLDER:
- TexturePackUtils::processFolder(pack, level);
- return true;
+ return TexturePackUtils::processFolder(pack, level);
case ResourceType::ZIPFILE:
- TexturePackUtils::processZIP(pack, level);
- return true;
+ return TexturePackUtils::processZIP(pack, level);
default:
qWarning() << "Invalid type for resource pack parse task!";
return false;
}
}
-void processFolder(TexturePack& pack, ProcessingLevel level)
+bool processFolder(TexturePack& pack, ProcessingLevel level)
{
Q_ASSERT(pack.type() == ResourceType::FOLDER);
@@ -51,39 +49,51 @@ void processFolder(TexturePack& pack, ProcessingLevel level)
if (mcmeta_file_info.isFile()) {
QFile mcmeta_file(mcmeta_file_info.filePath());
if (!mcmeta_file.open(QIODevice::ReadOnly))
- return;
+ return false;
auto data = mcmeta_file.readAll();
- TexturePackUtils::processPackTXT(pack, std::move(data));
+ bool packTXT_result = TexturePackUtils::processPackTXT(pack, std::move(data));
mcmeta_file.close();
+ if (!packTXT_result) {
+ return false;
+ }
+ } else {
+ return false;
}
if (level == ProcessingLevel::BasicInfoOnly)
- return;
+ return true;
QFileInfo image_file_info(FS::PathCombine(pack.fileinfo().filePath(), "pack.png"));
if (image_file_info.isFile()) {
QFile mcmeta_file(image_file_info.filePath());
if (!mcmeta_file.open(QIODevice::ReadOnly))
- return;
+ return false;
auto data = mcmeta_file.readAll();
- TexturePackUtils::processPackPNG(pack, std::move(data));
+ bool packPNG_result = TexturePackUtils::processPackPNG(pack, std::move(data));
mcmeta_file.close();
+ if (!packPNG_result) {
+ return false;
+ }
+ } else {
+ return false;
}
+
+ return true;
}
-void processZIP(TexturePack& pack, ProcessingLevel level)
+bool processZIP(TexturePack& pack, ProcessingLevel level)
{
Q_ASSERT(pack.type() == ResourceType::ZIPFILE);
QuaZip zip(pack.fileinfo().filePath());
if (!zip.open(QuaZip::mdUnzip))
- return;
+ return false;
QuaZipFile file(&zip);
@@ -91,51 +101,62 @@ void processZIP(TexturePack& pack, ProcessingLevel level)
if (!file.open(QIODevice::ReadOnly)) {
qCritical() << "Failed to open file in zip.";
zip.close();
- return;
+ return false;
}
auto data = file.readAll();
- TexturePackUtils::processPackTXT(pack, std::move(data));
+ bool packTXT_result = TexturePackUtils::processPackTXT(pack, std::move(data));
file.close();
+ if (!packTXT_result) {
+ return false;
+ }
}
if (level == ProcessingLevel::BasicInfoOnly) {
zip.close();
- return;
+ return false;
}
if (zip.setCurrentFile("pack.png")) {
if (!file.open(QIODevice::ReadOnly)) {
qCritical() << "Failed to open file in zip.";
zip.close();
- return;
+ return false;
}
auto data = file.readAll();
- TexturePackUtils::processPackPNG(pack, std::move(data));
+ bool packPNG_result = TexturePackUtils::processPackPNG(pack, std::move(data));
file.close();
+ if (!packPNG_result) {
+ return false;
+ }
}
zip.close();
+
+ return true;
}
-void processPackTXT(TexturePack& pack, QByteArray&& raw_data)
+bool processPackTXT(TexturePack& pack, QByteArray&& raw_data)
{
pack.setDescription(QString(raw_data));
+ return true;
}
-void processPackPNG(TexturePack& pack, QByteArray&& raw_data)
+bool processPackPNG(TexturePack& pack, QByteArray&& raw_data)
{
auto img = QImage::fromData(raw_data);
if (!img.isNull()) {
pack.setImage(img);
} else {
qWarning() << "Failed to parse pack.png.";
+ return false;
}
+ return true;
}
bool validate(QFileInfo file)
diff --git a/launcher/minecraft/mod/tasks/LocalTexturePackParseTask.h b/launcher/minecraft/mod/tasks/LocalTexturePackParseTask.h
index 9f7aab75..1589f8cb 100644
--- a/launcher/minecraft/mod/tasks/LocalTexturePackParseTask.h
+++ b/launcher/minecraft/mod/tasks/LocalTexturePackParseTask.h
@@ -32,11 +32,11 @@ enum class ProcessingLevel { Full, BasicInfoOnly };
bool process(TexturePack& pack, ProcessingLevel level = ProcessingLevel::Full);
-void processZIP(TexturePack& pack, ProcessingLevel level = ProcessingLevel::Full);
-void processFolder(TexturePack& pack, ProcessingLevel level = ProcessingLevel::Full);
+bool processZIP(TexturePack& pack, ProcessingLevel level = ProcessingLevel::Full);
+bool processFolder(TexturePack& pack, ProcessingLevel level = ProcessingLevel::Full);
-void processPackTXT(TexturePack& pack, QByteArray&& raw_data);
-void processPackPNG(TexturePack& pack, QByteArray&& raw_data);
+bool processPackTXT(TexturePack& pack, QByteArray&& raw_data);
+bool processPackPNG(TexturePack& pack, QByteArray&& raw_data);
/** Checks whether a file is valid as a texture pack or not. */
bool validate(QFileInfo file);
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 630f1200..be33b8db 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -27,6 +27,9 @@ ecm_add_test(ResourcePackParse_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VER
ecm_add_test(TexturePackParse_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test
TEST_NAME TexturePackParse)
+ecm_add_test(DataPackParse_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test
+ TEST_NAME DataPackParse)
+
ecm_add_test(ParseUtils_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test
TEST_NAME ParseUtils)
diff --git a/tests/DataPackParse_test.cpp b/tests/DataPackParse_test.cpp
new file mode 100644
index 00000000..7307035f
--- /dev/null
+++ b/tests/DataPackParse_test.cpp
@@ -0,0 +1,76 @@
+// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * PolyMC - Minecraft Launcher
+ * Copyright (c) 2022 flowln
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+#include
+
+#include
+
+#include
+#include
+
+class DataPackParseTest : public QObject {
+ Q_OBJECT
+
+ private slots:
+ void test_parseZIP()
+ {
+ QString source = QFINDTESTDATA("testdata/DataPackParse");
+
+ QString zip_dp = FS::PathCombine(source, "test_data_pack_boogaloo.zip");
+ DataPack pack { QFileInfo(zip_dp) };
+
+ bool valid = DataPackUtils::processZIP(pack);
+
+ QVERIFY(pack.packFormat() == 4);
+ QVERIFY(pack.description() == "Some data pack 2 boobgaloo");
+ QVERIFY(valid == true);
+ }
+
+ void test_parseFolder()
+ {
+ QString source = QFINDTESTDATA("testdata/DataPackParse");
+
+ QString folder_dp = FS::PathCombine(source, "test_folder");
+ DataPack pack { QFileInfo(folder_dp) };
+
+ bool valid = DataPackUtils::processFolder(pack);
+
+ QVERIFY(pack.packFormat() == 10);
+ QVERIFY(pack.description() == "Some data pack, maybe");
+ QVERIFY(valid == true);
+ }
+
+ void test_parseFolder2()
+ {
+ QString source = QFINDTESTDATA("testdata/DataPackParse");
+
+ QString folder_dp = FS::PathCombine(source, "another_test_folder");
+ DataPack pack { QFileInfo(folder_dp) };
+
+ bool valid = DataPackUtils::process(pack);
+
+ QVERIFY(pack.packFormat() == 6);
+ QVERIFY(pack.description() == "Some data pack three, leaves on the tree");
+ QVERIFY(valid == true);
+ }
+};
+
+QTEST_GUILESS_MAIN(DataPackParseTest)
+
+#include "DataPackParse_test.moc"
diff --git a/tests/testdata/DataPackParse/another_test_folder/pack.mcmeta b/tests/testdata/DataPackParse/another_test_folder/pack.mcmeta
new file mode 100644
index 0000000000000000000000000000000000000000..5509d007bb14d1019563189ba3d5296f545b883d
GIT binary patch
literal 104
zcmXZTI|_g>5QO1Arx;SFw(uGrK$c`h(L6{Nn~=M^qTS2~e>Z?F;B)mtTj@04q!%HUIzs
literal 0
HcmV?d00001
diff --git a/tests/testdata/DataPackParse/test_data_pack_boogaloo.zip b/tests/testdata/DataPackParse/test_data_pack_boogaloo.zip
new file mode 100644
index 0000000000000000000000000000000000000000..cb0b9f3c69fe8722e31224c154edf66c39fa6cbd
GIT binary patch
literal 898
zcmWIWW@Zs#0D;^EouOa`lwbwYDTyVC`T;nVaKn_Ol;-AE;!!Aos<0$6y%>*bQ7o!6
zOHy<3XpzUIB`rTczMv>SKMhIq<+-RnRVS=DDX~Z|t2jRo5*ADh91I5YJ40uf#RT{O
zHAew4C@cyRle6`5lXFu`5?4P93JB2h@HrQ@DQE@TriFWcZ27b3&Jm#n3p)8+OjNg8
z?9|x2K*iXeUt^WXocXg?O_&rhX$3>jx`ZVYrp%u|W!{X*^VhS4#Gej5&B_qq&B$cW
zj60TqHiN-iM-T;#Gu&E0E`@=&j>juRt47_!$
z0y5EcL*p2?5ujLxfwzupflRDMAjdhvFl@07Gi*uYE5e2$(g4IzT&VzIs6Hb~nh5Y_
SWdljF1K|mv)Dj?OU;qF;qQe^i
literal 0
HcmV?d00001
diff --git a/tests/testdata/DataPackParse/test_folder/pack.mcmeta b/tests/testdata/DataPackParse/test_folder/pack.mcmeta
new file mode 100644
index 0000000000000000000000000000000000000000..dbfc7e9b871808e54e2c2bf05e6de02d443fd0a3
GIT binary patch
literal 86
zcmb>CQczGTNKDRFvQnr9vZ1{AwEUvn#1f#Op@9xWI3=|>xhS)sBr`t`D6ABmpPQNJ?
literal 0
HcmV?d00001