verison deletion feature

This commit is contained in:
2026-08-27 21:05:37 +03:00
parent dddc0261d4
commit af41017446
5 changed files with 365 additions and 8 deletions
+84
View File
@@ -1,6 +1,7 @@
#include "minecraftversion.h"
#include <QDir>
#include <QDirIterator>
#include <QFile>
#include <QFileInfo>
#include <QHash>
@@ -27,6 +28,27 @@ QString versionJarPath(const QString &gameDir, const QString &id)
return gameDir + "/versions/" + id + "/" + id + ".jar";
}
QString versionsRoot(const QString &gameDir)
{
return gameDir + QStringLiteral("/versions");
}
// Идентификатор версии — это имя папки, поэтому «чинить» его как sanitizeId()
// нельзя: подстановка символов увела бы нас не в ту версию. Только проверяем.
bool isSafeVersionId(const QString &id)
{
return !id.isEmpty() && !id.startsWith(u'.') && !id.contains(u'/') && !id.contains(u'\\');
}
// Верхний объект <id>.json без разворачивания цепочки: нужен только inheritsFrom.
QJsonObject readVersionObject(const QString &gameDir, const QString &id)
{
QFile file(versionJsonPath(gameDir, id));
if (!file.open(QIODevice::ReadOnly))
return {};
return QJsonDocument::fromJson(file.readAll()).object();
}
// org.lwjgl:lwjgl:3.3.1:natives-windows -> org/lwjgl/lwjgl/3.3.1/lwjgl-3.3.1-natives-windows.jar
QString mavenToPath(const QString &name)
{
@@ -311,6 +333,68 @@ QStringList VersionLoader::installedVersions(const QString &gameDir)
return result;
}
QStringList VersionLoader::dependentsOf(const QString &gameDir, const QString &versionId)
{
if (versionId.isEmpty())
return {};
// Одного уровня хватает: профили модлоадеров наследуются от ванильных версий,
// а не друг от друга.
QStringList result;
const QStringList installed = installedVersions(gameDir);
for (const QString &id : installed) {
if (id == versionId)
continue;
const QJsonObject object = readVersionObject(gameDir, id);
if (object.value(QStringLiteral("inheritsFrom")).toString() == versionId)
result << id;
}
return result;
}
qint64 VersionLoader::installedSize(const QString &gameDir, const QString &versionId)
{
if (!isSafeVersionId(versionId))
return 0;
qint64 total = 0;
QDirIterator it(versionsRoot(gameDir) + u'/' + versionId,
QDir::Files | QDir::Hidden | QDir::System | QDir::NoSymLinks,
QDirIterator::Subdirectories);
while (it.hasNext()) {
it.next();
total += it.fileInfo().size();
}
return total;
}
bool VersionLoader::remove(const QString &gameDir, const QString &versionId, QString *error)
{
const QString root = versionsRoot(gameDir);
if (gameDir.isEmpty() || !isSafeVersionId(versionId)) {
if (error)
*error = QObject::tr("недопустимый идентификатор версии");
return false;
}
// Удаляем только внутри <gameDir>/versions: проверка id уже не пропустит «..»,
// но цена ошибки — чужая папка, а проверка стоит одной строки.
const QString dir = QDir::cleanPath(root + u'/' + versionId);
if (!dir.startsWith(root + u'/')) {
if (error)
*error = QObject::tr("недопустимый идентификатор версии");
return false;
}
if (!QDir(dir).exists())
return true;
if (!QDir(dir).removeRecursively()) {
if (error)
*error = QObject::tr("не удалось удалить %1").arg(QDir::toNativeSeparators(dir));
return false;
}
return true;
}
MinecraftVersion VersionLoader::load(const QString &gameDir,
const QString &versionId,
const QSet<QString> &features,