PrismLauncher/launcher/minecraft/mod/tasks/LocalWorldSaveParseTask.cpp
Rachel Powers a7c9b2f172 feat: validate world saves
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
2022-12-24 20:43:17 -07:00

181 lines
4.6 KiB
C++

// 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 <https://www.gnu.org/licenses/>.
*/
#include "LocalWorldSaveParseTask.h"
#include "FileSystem.h"
#include <qdir.h>
#include <qfileinfo.h>
#include <quazip/quazip.h>
#include <quazip/quazipfile.h>
#include <quazip/quazipdir.h>
#include <utility>
namespace WorldSaveUtils {
bool process(WorldSave& pack, ProcessingLevel level)
{
switch (pack.type()) {
case ResourceType::FOLDER:
return WorldSaveUtils::processFolder(pack, level);
case ResourceType::ZIPFILE:
return WorldSaveUtils::processZIP(pack, level);
default:
qWarning() << "Invalid type for shader pack parse task!";
return false;
}
}
static std::tuple<bool, QString, bool> contains_level_dat(QDir dir, bool saves = false)
{
for(auto const& entry : dir.entryInfoList()) {
if (!entry.isDir()) {
continue;
}
if (!saves && entry.fileName() == "saves") {
return contains_level_dat(QDir(entry.filePath()), true);
}
QFileInfo level_dat(FS::PathCombine(entry.filePath(), "level.dat"));
if (level_dat.exists() && level_dat.isFile()) {
return std::make_tuple(true, entry.fileName(), saves);
}
}
return std::make_tuple(false, "", saves);
}
bool processFolder(WorldSave& save, ProcessingLevel level)
{
Q_ASSERT(save.type() == ResourceType::FOLDER);
auto [ found, save_dir_name, found_saves_dir ] = contains_level_dat(QDir(save.fileinfo().filePath()));
if (!found) {
return false;
}
save.setSaveDirName(save_dir_name);
if (found_saves_dir) {
save.setSaveFormat(WorldSaveFormat::MULTI);
} else {
save.setSaveFormat(WorldSaveFormat::SINGLE);
}
if (level == ProcessingLevel::BasicInfoOnly) {
return true; // only need basic info already checked
}
// resurved for more intensive processing
return true; // all tests passed
}
static std::tuple<bool, QString, bool> contains_level_dat(QuaZip& zip)
{
bool saves = false;
QuaZipDir zipDir(&zip);
if (zipDir.exists("/saves")) {
saves = true;
zipDir.cd("/saves");
}
for (auto const& entry : zipDir.entryList()) {
zipDir.cd(entry);
if (zipDir.exists("level.dat")) {
return std::make_tuple(true, entry, saves);
}
zipDir.cd("..");
}
return std::make_tuple(false, "", saves);
}
bool processZIP(WorldSave& save, ProcessingLevel level)
{
Q_ASSERT(save.type() == ResourceType::ZIPFILE);
QuaZip zip(save.fileinfo().filePath());
if (!zip.open(QuaZip::mdUnzip))
return false; // can't open zip file
auto [ found, save_dir_name, found_saves_dir ] = contains_level_dat(zip);
if (save_dir_name.endsWith("/")) {
save_dir_name.chop(1);
}
if (!found) {
return false;
}
save.setSaveDirName(save_dir_name);
if (found_saves_dir) {
save.setSaveFormat(WorldSaveFormat::MULTI);
} else {
save.setSaveFormat(WorldSaveFormat::SINGLE);
}
if (level == ProcessingLevel::BasicInfoOnly) {
zip.close();
return true; // only need basic info already checked
}
// resurved for more intensive processing
zip.close();
return true;
}
bool validate(QFileInfo file)
{
WorldSave sp{ file };
return WorldSaveUtils::process(sp, ProcessingLevel::BasicInfoOnly) && sp.valid();
}
} // namespace WorldSaveUtils
LocalWorldSaveParseTask::LocalWorldSaveParseTask(int token, WorldSave& save)
: Task(nullptr, false), m_token(token), m_save(save)
{}
bool LocalWorldSaveParseTask::abort()
{
m_aborted = true;
return true;
}
void LocalWorldSaveParseTask::executeTask()
{
if (!WorldSaveUtils::process(m_save))
return;
if (m_aborted)
emitAborted();
else
emitSucceeded();
}