#include "fixtures.h" #include "minecraftversion.h" #include #include #include #include #include #include #include #include #include #include QString Fixtures::path(const QString &relative) { return QStringLiteral(BILAUNCHER_FIXTURES_DIR) + u'/' + relative; } QByteArray Fixtures::read(const QString &relative) { QFile file(path(relative)); if (!file.open(QIODevice::ReadOnly)) qFatal("Нет фикстуры %s", qPrintable(file.fileName())); return file.readAll(); } QJsonObject Fixtures::readJson(const QString &relative) { return QJsonDocument::fromJson(read(relative)).object(); } QString Fixtures::otherOsName() { return VersionLoader::osName() == QLatin1String("windows") ? QStringLiteral("linux") : QStringLiteral("windows"); } QByteArray Fixtures::applyTemplate(QByteArray text) { text.replace("%OS%", VersionLoader::osName().toUtf8()); text.replace("%OTHER%", otherOsName().toUtf8()); text.replace("%ARCH%", VersionLoader::osArch().toUtf8()); text.replace("%NATIVE%", VersionLoader::nativeClassifier().toUtf8()); return text; } QByteArray Fixtures::readTemplate(const QString &relative) { return applyTemplate(read(relative)); } bool Fixtures::write(const QString &filePath, const QByteArray &data) { QDir().mkpath(QFileInfo(filePath).absolutePath()); QFile file(filePath); if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) return false; return file.write(data) == data.size(); } bool Fixtures::writeJson(const QString &filePath, const QJsonObject &object) { return write(filePath, QJsonDocument(object).toJson(QJsonDocument::Indented)); } QByteArray Fixtures::readFile(const QString &filePath) { QFile file(filePath); if (!file.open(QIODevice::ReadOnly)) return {}; return file.readAll(); } QJsonObject Fixtures::readJsonFile(const QString &filePath) { return QJsonDocument::fromJson(readFile(filePath)).object(); } QByteArray Fixtures::zipBytes(const QMap &entries) { QBuffer buffer; buffer.open(QIODevice::WriteOnly); { QZipWriter writer(&buffer); writer.setCompressionPolicy(QZipWriter::AutoCompress); for (auto it = entries.cbegin(); it != entries.cend(); ++it) { if (it.key().endsWith(u'/')) writer.addDirectory(it.key()); else writer.addFile(it.key(), it.value()); } writer.close(); } return buffer.data(); } bool Fixtures::makeZip(const QString &zipPath, const QMap &entries) { return write(zipPath, zipBytes(entries)); } QStringList Fixtures::zipEntries(const QString &zipPath) { QZipReader reader(zipPath); QStringList result; for (const QZipReader::FileInfo &info : reader.fileInfoList()) { if (info.isFile) result << info.filePath; } result.sort(); return result; } bool Fixtures::makeTarGz(const QString &archivePath, const QMap &entries) { QTemporaryDir staging; if (!staging.isValid()) return false; QStringList tops; for (auto it = entries.cbegin(); it != entries.cend(); ++it) { if (!write(staging.path() + u'/' + it.key(), it.value())) return false; const QString top = it.key().section(u'/', 0, 0); if (!tops.contains(top)) tops << top; } QDir().mkpath(QFileInfo(archivePath).absolutePath()); QProcess tar; tar.setWorkingDirectory(staging.path()); #if defined(Q_OS_WIN) // Системный bsdtar по полному пути: GNU tar из Git, который бывает раньше в PATH, // принимает «C:» за имя удалённого хоста (та же история в release.yml). const QString program = qEnvironmentVariable("SystemRoot") + QStringLiteral("\\System32\\tar.exe"); #else const QString program = QStringLiteral("tar"); #endif tar.start(program, QStringList{QStringLiteral("-czf"), QDir::toNativeSeparators(archivePath)} + tops); return tar.waitForFinished(30000) && tar.exitStatus() == QProcess::NormalExit && tar.exitCode() == 0; } QString Fixtures::sha1(const QByteArray &data) { return QString::fromLatin1(QCryptographicHash::hash(data, QCryptographicHash::Sha1).toHex()); } QString Fixtures::sha256(const QByteArray &data) { return QString::fromLatin1(QCryptographicHash::hash(data, QCryptographicHash::Sha256).toHex()); } QStringList Fixtures::listTree(const QString &dir) { QStringList result; QDirIterator it(dir, QDir::Files | QDir::Hidden | QDir::NoDotAndDotDot, QDirIterator::Subdirectories); const QDir base(dir); while (it.hasNext()) result << base.relativeFilePath(it.next()); result.sort(); return result; } QString Fixtures::javaExecutableName() { #if defined(Q_OS_WIN) return QStringLiteral("java.exe"); #else return QStringLiteral("java"); #endif } void Fixtures::configureFakeJava(const QString &javaPath, const QString &version, const QStringList &config) { QStringList lines{QStringLiteral("version=") + version}; lines += config; write(QFileInfo(javaPath).absolutePath() + QStringLiteral("/fake-java.cfg"), lines.join(u'\n').toUtf8() + '\n'); } QString Fixtures::installFakeJava(const QString &dir, const QString &version, const QStringList &config) { const QString binDir = dir + QStringLiteral("/bin"); QDir().mkpath(binDir); const QString java = binDir + u'/' + javaExecutableName(); QFile::remove(java); if (!QFile::copy(QStringLiteral(BILAUNCHER_FAKE_JAVA), java)) qFatal("Не удалось скопировать fake_java в %s", qPrintable(java)); QFile::setPermissions(java, QFile::permissions(java) | QFileDevice::ExeOwner | QFileDevice::ExeGroup | QFileDevice::ExeOther); #if defined(Q_OS_WIN) const QString javaw = binDir + QStringLiteral("/javaw.exe"); QFile::remove(javaw); QFile::copy(java, javaw); #endif configureFakeJava(java, version, config); return java; }