diff options
Diffstat (limited to 'meshmc/launcher/Json.cpp')
| -rw-r--r-- | meshmc/launcher/Json.cpp | 298 |
1 files changed, 298 insertions, 0 deletions
diff --git a/meshmc/launcher/Json.cpp b/meshmc/launcher/Json.cpp new file mode 100644 index 0000000000..ad09c73d85 --- /dev/null +++ b/meshmc/launcher/Json.cpp @@ -0,0 +1,298 @@ +/* SPDX-FileCopyrightText: 2026 Project Tick + * SPDX-FileContributor: Project Tick + * SPDX-License-Identifier: GPL-3.0-or-later + * + * MeshMC - A Custom Launcher for Minecraft + * Copyright (C) 2026 Project Tick + * + * 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, either version 3 of the License, or + * (at your option) any later version. + * + * 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/>. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Json.h" + +#include <QFile> + +#include "FileSystem.h" +#include <math.h> + +namespace Json +{ + void write(const QJsonDocument& doc, const QString& filename) + { + FS::write(filename, doc.toJson()); + } + void write(const QJsonObject& object, const QString& filename) + { + write(QJsonDocument(object), filename); + } + void write(const QJsonArray& array, const QString& filename) + { + write(QJsonDocument(array), filename); + } + + QByteArray toBinary(const QJsonObject& obj) + { + return QJsonDocument(obj).toJson(QJsonDocument::Compact); + } + QByteArray toBinary(const QJsonArray& array) + { + return QJsonDocument(array).toJson(QJsonDocument::Compact); + } + QByteArray toText(const QJsonObject& obj) + { + return QJsonDocument(obj).toJson(QJsonDocument::Compact); + } + QByteArray toText(const QJsonArray& array) + { + return QJsonDocument(array).toJson(QJsonDocument::Compact); + } + + static bool isBinaryJson(const QByteArray& data) + { + // Binary JSON is no longer supported in Qt6 + Q_UNUSED(data); + return false; + } + QJsonDocument requireDocument(const QByteArray& data, const QString& what) + { + if (isBinaryJson(data)) { + throw JsonException(what + + ": Binary JSON format is no longer supported"); + } else { + QJsonParseError error; + QJsonDocument doc = QJsonDocument::fromJson(data, &error); + if (error.error != QJsonParseError::NoError) { + throw JsonException( + what + ": Error parsing JSON: " + error.errorString()); + } + return doc; + } + } + QJsonDocument requireDocument(const QString& filename, const QString& what) + { + return requireDocument(FS::read(filename), what); + } + QJsonObject requireObject(const QJsonDocument& doc, const QString& what) + { + if (!doc.isObject()) { + throw JsonException(what + " is not an object"); + } + return doc.object(); + } + QJsonArray requireArray(const QJsonDocument& doc, const QString& what) + { + if (!doc.isArray()) { + throw JsonException(what + " is not an array"); + } + return doc.array(); + } + + void writeString(QJsonObject& to, const QString& key, const QString& value) + { + if (!value.isEmpty()) { + to.insert(key, value); + } + } + + void writeStringList(QJsonObject& to, const QString& key, + const QStringList& values) + { + if (!values.isEmpty()) { + QJsonArray array; + for (auto value : values) { + array.append(value); + } + to.insert(key, array); + } + } + + template <> QJsonValue toJson<QUrl>(const QUrl& url) + { + return QJsonValue(url.toString(QUrl::FullyEncoded)); + } + template <> QJsonValue toJson<QByteArray>(const QByteArray& data) + { + return QJsonValue(QString::fromLatin1(data.toHex())); + } + template <> QJsonValue toJson<QDateTime>(const QDateTime& datetime) + { + return QJsonValue(datetime.toString(Qt::ISODate)); + } + template <> QJsonValue toJson<QDir>(const QDir& dir) + { + return QDir::current().relativeFilePath(dir.absolutePath()); + } + template <> QJsonValue toJson<QUuid>(const QUuid& uuid) + { + return uuid.toString(); + } + template <> QJsonValue toJson<QVariant>(const QVariant& variant) + { + return QJsonValue::fromVariant(variant); + } + + template <> + QByteArray requireIsType<QByteArray>(const QJsonValue& value, + const QString& what) + { + const QString string = ensureIsType<QString>(value, what); + // ensure that the string can be safely cast to Latin1 + if (string != QString::fromLatin1(string.toLatin1())) { + throw JsonException(what + " is not encodable as Latin1"); + } + return QByteArray::fromHex(string.toLatin1()); + } + + template <> + QJsonArray requireIsType<QJsonArray>(const QJsonValue& value, + const QString& what) + { + if (!value.isArray()) { + throw JsonException(what + " is not an array"); + } + return value.toArray(); + } + + template <> + QString requireIsType<QString>(const QJsonValue& value, const QString& what) + { + if (!value.isString()) { + throw JsonException(what + " is not a string"); + } + return value.toString(); + } + + template <> + bool requireIsType<bool>(const QJsonValue& value, const QString& what) + { + if (!value.isBool()) { + throw JsonException(what + " is not a bool"); + } + return value.toBool(); + } + + template <> + double requireIsType<double>(const QJsonValue& value, const QString& what) + { + if (!value.isDouble()) { + throw JsonException(what + " is not a double"); + } + return value.toDouble(); + } + + template <> + int requireIsType<int>(const QJsonValue& value, const QString& what) + { + const double doubl = requireIsType<double>(value, what); + if (fmod(doubl, 1) != 0) { + throw JsonException(what + " is not an integer"); + } + return int(doubl); + } + + template <> + QDateTime requireIsType<QDateTime>(const QJsonValue& value, + const QString& what) + { + const QString string = requireIsType<QString>(value, what); + const QDateTime datetime = QDateTime::fromString(string, Qt::ISODate); + if (!datetime.isValid()) { + throw JsonException(what + + " is not a ISO formatted date/time value"); + } + return datetime; + } + + template <> + QUrl requireIsType<QUrl>(const QJsonValue& value, const QString& what) + { + const QString string = ensureIsType<QString>(value, what); + if (string.isEmpty()) { + return QUrl(); + } + const QUrl url = QUrl(string, QUrl::StrictMode); + if (!url.isValid()) { + throw JsonException(what + " is not a correctly formatted URL"); + } + return url; + } + + template <> + QDir requireIsType<QDir>(const QJsonValue& value, const QString& what) + { + const QString string = requireIsType<QString>(value, what); + // FIXME: does not handle invalid characters! + return QDir::current().absoluteFilePath(string); + } + + template <> + QUuid requireIsType<QUuid>(const QJsonValue& value, const QString& what) + { + const QString string = requireIsType<QString>(value, what); + const QUuid uuid = QUuid(string); + if (uuid.toString() != string) // converts back => valid + { + throw JsonException(what + " is not a valid UUID"); + } + return uuid; + } + + template <> + QJsonObject requireIsType<QJsonObject>(const QJsonValue& value, + const QString& what) + { + if (!value.isObject()) { + throw JsonException(what + " is not an object"); + } + return value.toObject(); + } + + template <> + QVariant requireIsType<QVariant>(const QJsonValue& value, + const QString& what) + { + if (value.isNull() || value.isUndefined()) { + throw JsonException(what + " is null or undefined"); + } + return value.toVariant(); + } + + template <> + QJsonValue requireIsType<QJsonValue>(const QJsonValue& value, + const QString& what) + { + if (value.isNull() || value.isUndefined()) { + throw JsonException(what + " is null or undefined"); + } + return value; + } + +} // namespace Json |
