#include "IO/ProjectDigest.h" #include #include #include #include #include #include #include QByteArray ProjectDigest::compute(const QString &path) { QCryptographicHash hasher(QCryptographicHash::Sha256); QFile manifest(path + QStringLiteral("/manifest.json")); if (!manifest.open(QIODevice::ReadOnly)) throw std::runtime_error(manifest.errorString().toStdString()); // A failed read comes up short; Swift's Data(contentsOf:) throws. const QByteArray bytes = manifest.readAll(); if (bytes.size() != manifest.size()) throw std::runtime_error("the manifest could not be read whole"); hasher.addData(bytes); // Assets are not read: a save would wait on each. const QDir images(path + QStringLiteral("/images")); QStringList names = images.entryList(QDir::AllEntries | QDir::Hidden | QDir::System | QDir::NoDotAndDotDot, QDir::Unsorted); std::sort(names.begin(), names.end()); for (const QString &name : names) { const QFileInfo info(images.filePath(name)); // Gone mid-listing: half written, as Swift's resourceValues throws. if (!info.exists() && !info.isSymLink()) throw std::runtime_error("an image went while the package was read"); if (info.isSymLink() || !info.isFile()) continue; hasher.addData(name.toUtf8()); const quint64 size = qToLittleEndian(quint64(info.size())); hasher.addData(QByteArrayView(reinterpret_cast(&size), sizeof size)); } return hasher.result(); }