aboutsummaryrefslogtreecommitdiff
path: root/launcher/modplatform/packwiz/Packwiz.cpp
blob: 8fd74a3e7478357a5c9b2745d971ea222000874c (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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
#include "Packwiz.h"

#include <QDebug>
#include <QDir>
#include <QObject>

#include "toml.h"

#include "modplatform/ModIndex.h"
#include "minecraft/mod/Mod.h"

namespace Packwiz {

// Helpers
static inline QString indexFileName(QString const& mod_name)
{
    if(mod_name.endsWith(".toml"))
        return mod_name;
    return QString("%1.toml").arg(mod_name);
}

auto V1::createModFormat(QDir& index_dir, ModPlatform::IndexedPack& mod_pack, ModPlatform::IndexedVersion& mod_version) -> Mod
{
    Mod mod;

    mod.name = mod_pack.name;
    mod.filename = mod_version.fileName;

    mod.url = mod_version.downloadUrl;
    mod.hash_format = ModPlatform::ProviderCapabilities::hashType(mod_pack.provider);
    mod.hash = "";  // FIXME

    mod.provider = mod_pack.provider;
    mod.file_id = mod_pack.addonId;
    mod.project_id = mod_version.fileId;

    return mod;
}

auto V1::createModFormat(QDir& index_dir, ::Mod& internal_mod) -> Mod
{
    auto mod_name = internal_mod.name();

    // Try getting metadata if it exists
    Mod mod { getIndexForMod(index_dir, mod_name) };
    if(mod.isValid())
        return mod;

    // Manually construct packwiz mod
    mod.name = internal_mod.name();
    mod.filename = internal_mod.fileinfo().fileName();

    // TODO: Have a mechanism for telling the UI subsystem that we want to gather user information
    // (i.e. which mod provider we want to use). Maybe an object parameter with a signal for that?

    return mod;
}

void V1::updateModIndex(QDir& index_dir, Mod& mod)
{
    if(!mod.isValid()){
        qCritical() << QString("Tried to update metadata of an invalid mod!");
        return;
    }

    // Ensure the corresponding mod's info exists, and create it if not
    QFile index_file(index_dir.absoluteFilePath(indexFileName(mod.name)));

    // There's already data on there!
    // TODO: We should do more stuff here, as the user is likely trying to
    // override a file. In this case, check versions and ask the user what
    // they want to do!
    if (index_file.exists()) { index_file.remove(); }

    if (!index_file.open(QIODevice::ReadWrite)) {
        qCritical() << QString("Could not open file %1!").arg(indexFileName(mod.name));
        return;
    }

    // Put TOML data into the file
    QTextStream in_stream(&index_file);
    auto addToStream = [&in_stream](QString&& key, QString value) { in_stream << QString("%1 = \"%2\"\n").arg(key, value); };

    {
        addToStream("name", mod.name);
        addToStream("filename", mod.filename);
        addToStream("side", mod.side);

        in_stream << QString("\n[download]\n");
        addToStream("url", mod.url.toString());
        addToStream("hash-format", mod.hash_format);
        addToStream("hash", mod.hash);

        in_stream << QString("\n[update]\n");
        in_stream << QString("[update.%1]\n").arg(ModPlatform::ProviderCapabilities::providerName(mod.provider));
        switch(mod.provider){
        case(ModPlatform::Provider::FLAME):
            in_stream << QString("file-id = %1\n").arg(mod.file_id.toString());
            in_stream << QString("project-id = %1\n").arg(mod.project_id.toString());
            break;
        case(ModPlatform::Provider::MODRINTH):
            addToStream("mod-id", mod.mod_id().toString());
            addToStream("version", mod.version().toString());
            break;
        }
    }
}

void V1::deleteModIndex(QDir& index_dir, QString& mod_name)
{
    QFile index_file(index_dir.absoluteFilePath(indexFileName(mod_name)));

    if(!index_file.exists()){
        qWarning() << QString("Tried to delete non-existent mod metadata for %1!").arg(mod_name);
        return;
    }

    if(!index_file.remove()){
        qWarning() << QString("Failed to remove metadata for mod %1!").arg(mod_name);
    }
}

// Helper functions for extracting data from the TOML file
static auto stringEntry(toml_table_t* parent, const char* entry_name) -> QString
{
    toml_datum_t var = toml_string_in(parent, entry_name);
    if (!var.ok) {
        qCritical() << QString("Failed to read str property '%1' in mod metadata.").arg(entry_name);
        return {};
    }

    QString tmp = var.u.s;
    free(var.u.s);

    return tmp;
}

static auto intEntry(toml_table_t* parent, const char* entry_name) -> int
{
    toml_datum_t var = toml_int_in(parent, entry_name);
    if (!var.ok) {
        qCritical() << QString("Failed to read int property '%1' in mod metadata.").arg(entry_name);
        return {};
    }

    return var.u.i;
}

auto V1::getIndexForMod(QDir& index_dir, QString& index_file_name) -> Mod
{
    Mod mod;

    QFile index_file(index_dir.absoluteFilePath(indexFileName(index_file_name)));

    if (!index_file.exists()) {
        qWarning() << QString("Tried to get a non-existent mod metadata for %1").arg(index_file_name);
        return {};
    }
    if (!index_file.open(QIODevice::ReadOnly)) {
        qWarning() << QString("Failed to open mod metadata for %1").arg(index_file_name);
        return {};
    }

    toml_table_t* table;

    char errbuf[200];
    table = toml_parse(index_file.readAll().data(), errbuf, sizeof(errbuf));

    index_file.close();

    if (!table) {
        qCritical() << QString("Could not open file %1!").arg(indexFileName(mod.name));
        return {};
    }
    
    { // Basic info
        mod.name = stringEntry(table, "name");
        mod.filename = stringEntry(table, "filename");
        mod.side = stringEntry(table, "side");
    }

    { // [download] info
        toml_table_t* download_table = toml_table_in(table, "download");
        if (!download_table) {
            qCritical() << QString("No [download] section found on mod metadata!");
            return {};
        }

        mod.url = stringEntry(download_table, "url");
        mod.hash_format = stringEntry(download_table, "hash-format");
        mod.hash = stringEntry(download_table, "hash");
    }

    { // [update] info
        using ProviderCaps = ModPlatform::ProviderCapabilities;
        using Provider = ModPlatform::Provider;

        toml_table_t* update_table = toml_table_in(table, "update");
        if (!update_table) {
            qCritical() << QString("No [update] section found on mod metadata!");
            return {};
        }

        toml_table_t* mod_provider_table;
        if ((mod_provider_table = toml_table_in(update_table, ProviderCaps::providerName(Provider::FLAME)))) {
            mod.provider = Provider::FLAME;
            mod.file_id = intEntry(mod_provider_table, "file-id");
            mod.project_id = intEntry(mod_provider_table, "project-id");
        } else if ((mod_provider_table = toml_table_in(update_table, ProviderCaps::providerName(Provider::MODRINTH)))) {
            mod.provider = Provider::MODRINTH;
            mod.mod_id() = stringEntry(mod_provider_table, "mod-id");
            mod.version() = stringEntry(mod_provider_table, "version");
        } else {
            qCritical() << QString("No mod provider on mod metadata!");
            return {};
        }
        
    }

    toml_free(table);

    return mod;
}

} // namespace Packwiz