diff options
Diffstat (limited to 'launcher')
| -rw-r--r-- | launcher/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | launcher/mojang/PackageManifest.cpp | 427 | ||||
| -rw-r--r-- | launcher/mojang/PackageManifest.h | 171 | 
3 files changed, 0 insertions, 600 deletions
| diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 9bad2a67..6ca31d46 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -377,8 +377,6 @@ set(MINECRAFT_SOURCES      minecraft/services/SkinDelete.cpp      minecraft/services/SkinDelete.h -    mojang/PackageManifest.h -    mojang/PackageManifest.cpp      minecraft/Agent.h)  # the screenshots feature diff --git a/launcher/mojang/PackageManifest.cpp b/launcher/mojang/PackageManifest.cpp deleted file mode 100644 index b3dfd7fc..00000000 --- a/launcher/mojang/PackageManifest.cpp +++ /dev/null @@ -1,427 +0,0 @@ -#include "PackageManifest.h" -#include <Json.h> -#include <QDir> -#include <QDirIterator> -#include <QCryptographicHash> -#include <QDebug> - -#ifndef Q_OS_WIN32 -#include <unistd.h> -#include <sys/types.h> -#include <sys/stat.h> -#endif - -namespace mojang_files { - -const Hash hash_of_empty_string = "da39a3ee5e6b4b0d3255bfef95601890afd80709"; - -int Path::compare(const Path& rhs) const -{ -    auto left_cursor = begin(); -    auto left_end = end(); -    auto right_cursor = rhs.begin(); -    auto right_end = rhs.end(); - -    while (left_cursor != left_end && right_cursor != right_end) -    { -        if(*left_cursor < *right_cursor) -        { -            return -1; -        } -        else if(*left_cursor > *right_cursor) -        { -            return 1; -        } -        left_cursor++; -        right_cursor++; -    } - -    if(left_cursor == left_end) -    { -        if(right_cursor == right_end) -        { -            return 0; -        } -        return -1; -    } -    return 1; -} - -void Package::addFile(const Path& path, const File& file) { -    addFolder(path.parent_path()); -    files[path] = file; -} - -void Package::addFolder(Path folder) { -    if(!folder.has_parent_path()) { -        return; -    } -    do { -        folders.insert(folder); -        folder = folder.parent_path(); -    } while(folder.has_parent_path()); -} - -void Package::addLink(const Path& path, const Path& target) { -    addFolder(path.parent_path()); -    symlinks[path] = target; -} - -void Package::addSource(const FileSource& source) { -    sources[source.hash] = source; -} - - -namespace { -void fromJson(QJsonDocument & doc, Package & out) { -    std::set<Path> seen_paths; -    if (!doc.isObject()) -    { -        throw JSONValidationError("file manifest is not an object"); -    } -    QJsonObject root = doc.object(); - -    auto filesObj = Json::ensureObject(root, "files"); -    auto iter = filesObj.begin(); -    while (iter != filesObj.end()) -    { -        Path objectPath = Path(iter.key()); -        auto value = iter.value(); -        iter++; -        if(seen_paths.count(objectPath)) { -            throw JSONValidationError("duplicate path inside manifest, the manifest is invalid"); -        } -        if (!value.isObject()) -        { -            throw JSONValidationError("file entry inside manifest is not an an object"); -        } -        seen_paths.insert(objectPath); - -        auto fileObject = value.toObject(); -        auto type = Json::requireString(fileObject, "type"); -        if(type == "directory") { -            out.addFolder(objectPath); -            continue; -        } -        else if(type == "file") { -            FileSource bestSource; -            File file; -            file.executable = Json::ensureBoolean(fileObject, QString("executable"), false); -            auto downloads = Json::requireObject(fileObject, "downloads"); -            for(auto iter2 = downloads.begin(); iter2 != downloads.end(); iter2++) { -                FileSource source; - -                auto downloadObject = Json::requireObject(iter2.value()); -                source.hash = Json::requireString(downloadObject, "sha1"); -                source.size = Json::requireInteger(downloadObject, "size"); -                source.url = Json::requireString(downloadObject, "url"); - -                auto compression = iter2.key(); -                if(compression == "raw") { -                    file.hash = source.hash; -                    file.size = source.size; -                    source.compression = Compression::Raw; -                } -                else if (compression == "lzma") { -                    source.compression = Compression::Lzma; -                } -                else { -                    continue; -                } -                bestSource.upgrade(source); -            } -            if(bestSource.isBad()) { -                throw JSONValidationError("No valid compression method for file " + iter.key()); -            } -            out.addFile(objectPath, file); -            out.addSource(bestSource); -        } -        else if(type == "link") { -            auto target = Json::requireString(fileObject, "target"); -            out.symlinks[objectPath] = target; -            out.addLink(objectPath, target); -        } -        else { -            throw JSONValidationError("Invalid item type in manifest: " + type); -        } -    } -    // make sure the containing folder exists -    out.folders.insert(Path()); -} -} - -Package Package::fromManifestContents(const QByteArray& contents) -{ -    Package out; -    try -    { -        auto doc = Json::requireDocument(contents, "Manifest"); -        fromJson(doc, out); -        return out; -    } -    catch (const Exception &e) -    { -        qDebug() << QString("Unable to parse manifest: %1").arg(e.cause()); -        out.valid = false; -        return out; -    } -} - -Package Package::fromManifestFile(const QString & filename) { -    Package out; -    try -    { -        auto doc = Json::requireDocument(filename, filename); -        fromJson(doc, out); -        return out; -    } -    catch (const Exception &e) -    { -        qDebug() << QString("Unable to parse manifest file %1: %2").arg(filename, e.cause()); -        out.valid = false; -        return out; -    } -} - -#ifndef Q_OS_WIN32 - -#include <unistd.h> -#include <sys/types.h> -#include <sys/stat.h> - -namespace { -// FIXME: Qt obscures symlink targets by making them absolute. that is useless. this is the workaround - we do it ourselves -bool actually_read_symlink_target(const QString & filepath, Path & out) -{ -    struct ::stat st; -    // FIXME: here, we assume the native filesystem encoding. May the Gods have mercy upon our Souls. -    QByteArray nativePath = filepath.toUtf8(); -    const char * filepath_cstr = nativePath.data(); - -    if (lstat(filepath_cstr, &st) != 0) -    { -        return false; -    } - -    auto size = st.st_size ? st.st_size + 1 : PATH_MAX; -    std::string temp(size, '\0'); -    // because we don't realiably know how long the damn thing actually is, we loop and expand. POSIX is naff -    do -    { -        auto link_length = ::readlink(filepath_cstr, &temp[0], temp.size()); -        if(link_length == -1) -        { -            return false; -        } -        if(std::string::size_type(link_length) < temp.size()) -        { -            // buffer was long enough and we managed to read the link target. RETURN here. -            temp.resize(link_length); -            out = Path(QString::fromUtf8(temp.c_str())); -            return true; -        } -        temp.resize(temp.size() * 2); -    } while (true); -} -} -#endif - -// FIXME: Qt filesystem abstraction is bad, but ... let's hope it doesn't break too much? -// FIXME: The error handling is just DEFICIENT -Package Package::fromInspectedFolder(const QString& folderPath) -{ -    QDir root(folderPath); - -    Package out; -    QDirIterator iterator(folderPath, QDir::NoDotAndDotDot | QDir::AllEntries | QDir::System | QDir::Hidden, QDirIterator::Subdirectories); -    while(iterator.hasNext()) { -        iterator.next(); - -        auto fileInfo = iterator.fileInfo(); -        auto relPath = root.relativeFilePath(fileInfo.filePath()); -        // FIXME: this is probably completely busted on Windows anyway, so just disable it. -        // Qt makes shit up and doesn't understand the platform details -        // TODO: Actually use a filesystem library that isn't terrible and has decen license. -        //       I only know one, and I wrote it. Sadly, currently proprietary. PAIN. -#ifndef Q_OS_WIN32 -        if(fileInfo.isSymLink()) { -            Path targetPath; -            if(!actually_read_symlink_target(fileInfo.filePath(), targetPath)) { -                qCritical() << "Folder inspection: Unknown filesystem object:" << fileInfo.absoluteFilePath(); -                out.valid = false; -            } -            out.addLink(relPath, targetPath); -        } -        else -#endif -        if(fileInfo.isDir()) { -            out.addFolder(relPath); -        } -        else if(fileInfo.isFile()) { -            File f; -            f.executable = fileInfo.isExecutable(); -            f.size = fileInfo.size(); -            // FIXME: async / optimize the hashing -            QFile input(fileInfo.absoluteFilePath()); -            if(!input.open(QIODevice::ReadOnly)) { -                qCritical() << "Folder inspection: Failed to open file:" << fileInfo.absoluteFilePath(); -                out.valid = false; -                break; -            } -            f.hash = QCryptographicHash::hash(input.readAll(), QCryptographicHash::Sha1).toHex().constData(); -            out.addFile(relPath, f); -        } -        else { -            // Something else... oh my -            qCritical() << "Folder inspection: Unknown filesystem object:" << fileInfo.absoluteFilePath(); -            out.valid = false; -            break; -        } -    } -    out.folders.insert(Path(".")); -    out.valid = true; -    return out; -} - -namespace { -struct shallow_first_sort -{ -    bool operator()(const Path &lhs, const Path &rhs) const -    { -        auto lhs_depth = lhs.length(); -        auto rhs_depth = rhs.length(); -        if(lhs_depth < rhs_depth) -        { -            return true; -        } -        else if(lhs_depth == rhs_depth) -        { -            if(lhs < rhs) -            { -                return true; -            } -        } -        return false; -    } -}; - -struct deep_first_sort -{ -    bool operator()(const Path &lhs, const Path &rhs) const -    { -        auto lhs_depth = lhs.length(); -        auto rhs_depth = rhs.length(); -        if(lhs_depth > rhs_depth) -        { -            return true; -        } -        else if(lhs_depth == rhs_depth) -        { -            if(lhs < rhs) -            { -                return true; -            } -        } -        return false; -    } -}; -} - -UpdateOperations UpdateOperations::resolve(const Package& from, const Package& to) -{ -    UpdateOperations out; - -    if(!from.valid || !to.valid) { -        out.valid = false; -        return out; -    } - -    // Files -    for(auto iter = from.files.begin(); iter != from.files.end(); iter++) { -        const auto ¤t_hash = iter->second.hash; -        const auto ¤t_executable = iter->second.executable; -        const auto &path = iter->first; - -        auto iter2 = to.files.find(path); -        if(iter2 == to.files.end()) { -            // removed -            out.deletes.push_back(path); -            continue; -        } -        auto new_hash = iter2->second.hash; -        auto new_executable = iter2->second.executable; -        if (current_hash != new_hash) { -            out.deletes.push_back(path); -            out.downloads.emplace( -                std::pair<Path, FileDownload>{ -                    path, -                    FileDownload(to.sources.at(iter2->second.hash), iter2->second.executable) -                } -            ); -        } -        else if (current_executable != new_executable) { -            out.executable_fixes[path] = new_executable; -        } -    } -    for(auto iter = to.files.begin(); iter != to.files.end(); iter++) { -        auto path = iter->first; -        if(!from.files.count(path)) { -            out.downloads.emplace( -                std::pair<Path, FileDownload>{ -                    path, -                    FileDownload(to.sources.at(iter->second.hash), iter->second.executable) -                } -            ); -        } -    } - -    // Folders -    std::set<Path, deep_first_sort> remove_folders; -    std::set<Path, shallow_first_sort> make_folders; -    for(auto from_path: from.folders) { -        auto iter = to.folders.find(from_path); -        if(iter == to.folders.end()) { -            remove_folders.insert(from_path); -        } -    } -    for(auto & rmdir: remove_folders) { -        out.rmdirs.push_back(rmdir); -    } -    for(auto to_path: to.folders) { -        auto iter = from.folders.find(to_path); -        if(iter == from.folders.end()) { -            make_folders.insert(to_path); -        } -    } -    for(auto & mkdir: make_folders) { -        out.mkdirs.push_back(mkdir); -    } - -    // Symlinks -    for(auto iter = from.symlinks.begin(); iter != from.symlinks.end(); iter++) { -        const auto ¤t_target = iter->second; -        const auto &path = iter->first; - -        auto iter2 = to.symlinks.find(path); -        if(iter2 == to.symlinks.end()) { -            // removed -            out.deletes.push_back(path); -            continue; -        } -        const auto &new_target = iter2->second; -        if (current_target != new_target) { -            out.deletes.push_back(path); -            out.mklinks[path] = iter2->second; -        } -    } -    for(auto iter = to.symlinks.begin(); iter != to.symlinks.end(); iter++) { -        auto path = iter->first; -        if(!from.symlinks.count(path)) { -            out.mklinks[path] = iter->second; -        } -    } -    out.valid = true; -    return out; -} - -} diff --git a/launcher/mojang/PackageManifest.h b/launcher/mojang/PackageManifest.h deleted file mode 100644 index fd7ab0ad..00000000 --- a/launcher/mojang/PackageManifest.h +++ /dev/null @@ -1,171 +0,0 @@ -#pragma once - -#include <QString> -#include <map> -#include <set> -#include <QStringList> -#include "tasks/Task.h" - -namespace mojang_files { - -using Hash = QString; -extern const Hash empty_hash; - -// simple-ish path implementation. assumes always relative and does not allow '..' entries -class Path -{ -public: -    using parts_type = QStringList; - -    Path() = default; -    Path(QString string) { -        auto parts_in = string.split('/'); -        for(auto & part: parts_in) { -            if(part.isEmpty() || part == ".") { -                continue; -            } -            if(part == "..") { -                if(parts.size()) { -                    parts.pop_back(); -                } -                continue; -            } -            parts.push_back(part); -        } -    } - -    bool has_parent_path() const -    { -        return parts.size() > 0; -    } - -    Path parent_path() const -    { -        if (parts.empty()) -            return Path(); -        return Path(parts.begin(), std::prev(parts.end())); -    } - -    bool empty() const -    { -        return parts.empty(); -    } - -    int length() const -    { -        return parts.length(); -    } - -    bool operator==(const Path & rhs) const { -        return parts == rhs.parts; -    } - -    bool operator!=(const Path & rhs) const { -        return parts != rhs.parts; -    } - -    inline bool operator<(const Path& rhs) const -    { -        return compare(rhs) < 0; -    } - -    parts_type::const_iterator begin() const -    { -        return parts.begin(); -    } - -    parts_type::const_iterator end() const -    { -        return parts.end(); -    } - -    QString toString() const { -        return parts.join("/"); -    } - -private: -    Path(const parts_type::const_iterator & start, const parts_type::const_iterator & end) { -        auto cursor = start; -        while(cursor != end) { -            parts.push_back(*cursor); -            cursor++; -        } -    } -    int compare(const Path& p) const; - -    parts_type parts; -}; - - -enum class Compression { -    Raw, -    Lzma, -    Unknown -}; - - -struct FileSource -{ -    Compression compression = Compression::Unknown; -    Hash hash; -    QString url; -    std::size_t size = 0; -    void upgrade(const FileSource & other) { -        if(compression == Compression::Unknown || other.size < size) { -            *this = other; -        } -    } -    bool isBad() const { -        return compression == Compression::Unknown; -    } -}; - -struct File -{ -    Hash hash; -    bool executable; -    std::uint64_t size = 0; -}; - -struct Package { -    static Package fromInspectedFolder(const QString &folderPath); -    static Package fromManifestFile(const QString &path); -    static Package fromManifestContents(const QByteArray& contents); - -    explicit operator bool() const -    { -        return valid; -    } -    void addFolder(Path folder); -    void addFile(const Path & path, const File & file); -    void addLink(const Path & path, const Path & target); -    void addSource(const FileSource & source); - -    std::map<Hash, FileSource> sources; -    bool valid = true; -    std::set<Path> folders; -    std::map<Path, File> files; -    std::map<Path, Path> symlinks; -}; - -struct FileDownload : FileSource -{ -    FileDownload(const FileSource& source, bool executable) { -        static_cast<FileSource &> (*this) = source; -        this->executable = executable; -    } -    bool executable = false; -}; - -struct UpdateOperations { -    static UpdateOperations resolve(const Package & from, const Package & to); -    bool valid = false; -    std::vector<Path> deletes; -    std::vector<Path> rmdirs; -    std::vector<Path> mkdirs; -    std::map<Path, FileDownload> downloads; -    std::map<Path, Path> mklinks; -    std::map<Path, bool> executable_fixes; -}; - -} | 
