aboutsummaryrefslogtreecommitdiff
path: root/libs/installer_fomod_plus/scanner/archiveparser.h
blob: 8819a8486529e786a153f2e52532b56489688090 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#pragma once
#include "stringutil.h"

#include <QDir>
#include <QString>
#include <archive.h>
#include <iostream>
#include <ostream>

inline std::ostream& operator<<(std::ostream& os, const Archive::Error& error)
{
    switch (error) {
    case Archive::Error::ERROR_NONE:
        os << "No error";
        break;
    case Archive::Error::ERROR_ARCHIVE_NOT_FOUND:
        os << "File not found";
        break;
    case Archive::Error::ERROR_FAILED_TO_OPEN_ARCHIVE:
        os << "Failed to open file";
        break;
    case Archive::Error::ERROR_INVALID_ARCHIVE_FORMAT:
        os << "Invalid archive format";
        break;
    default:
        os << "Unknown error??";
    }
    return os;
}

inline bool hasFomodFiles(const std::vector<FileData*>& files)
{
    bool hasModuleXml = false;
    bool hasInfoXml   = false;

    for (const auto* file : files) {
        if (endsWithCaseInsensitive(file->getArchiveFilePath(), StringConstants::FomodFiles::W_MODULE_CONFIG.data())) {
            hasModuleXml = true;
        }
        if (endsWithCaseInsensitive(file->getArchiveFilePath(), StringConstants::FomodFiles::W_INFO_XML.data())) {
            hasInfoXml = true;
        }
    }
    return hasModuleXml && hasInfoXml;
}

/* This class can do the following:
 * 1.) Detects if an archive has FOMOD files in it (without extracting)
 *      - This is used by the FOMOD scanner to set content flags
 *
 * 2.) It may support extracting ESPs and FOMODs, but this might be better served
 *     by a separate class. Maybe it could return a ModuleConfiguration to use like
 *     the installer.
 */

enum class ScanResult {
    HAS_FOMOD,
    NO_FOMOD,
    NO_ARCHIVE
};

class ArchiveParser {
public:
    static ScanResult scanForFomodFiles(const QString& downloadsPath, const QString& installationFilePath,
        const QString& modName)
    {
        if (installationFilePath.isEmpty()) {
            return ScanResult::NO_ARCHIVE;
        }
        const auto qualifiedInstallerPath = QDir(installationFilePath).isAbsolute()
            ? installationFilePath
            : downloadsPath + "/" + installationFilePath;

        const auto archive = CreateArchive();

        if (!archive->isValid()) {
            logErrorForMod(modName, "Failed to load the archive module ", archive);
            return ScanResult::NO_ARCHIVE;
        }
        if (!archive->open(qualifiedInstallerPath.toStdWString(), nullptr)) {
            logErrorForMod(modName, "Failed to open archive [" + qualifiedInstallerPath + "]", archive);
            return ScanResult::NO_ARCHIVE;
        }

        if (hasFomodFiles(archive->getFileList())) {
            return ScanResult::HAS_FOMOD;
        }
        return ScanResult::NO_FOMOD;
    }

private:
    static void logErrorForMod(const QString& modName, const QString& message, const std::unique_ptr<Archive>& archive)
    {
        std::cerr << "[" << modName.toStdString() << "] " << message.toStdString() << " (" << archive->getLastError() <<
            ")" << std::endl;
    }
};