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
|
namespace fs = std::filesystem;
class ModDownloader
{
private:
const char* VERIFICATION_FLAG = "-disablemodverification";
const char* CUSTOM_MODS_URL_FLAG = "-customverifiedurl=";
const char* STORE_URL = "https://gcdn.thunderstore.io/live/repository/packages/";
const char* DEFAULT_MODS_LIST_URL = "https://raw.githubusercontent.com/R2Northstar/VerifiedMods/main/verified-mods.json";
char* modsListUrl;
struct VerifiedModVersion
{
std::string checksum;
};
struct VerifiedModDetails
{
std::string dependencyPrefix;
std::unordered_map<std::string, VerifiedModVersion> versions = {};
};
std::unordered_map<std::string, VerifiedModDetails> verifiedMods = {};
/**
* Mod archive download callback.
*
* This function is called by curl as it's downloading the mod archive; this
* will retrieve the current `ModDownloader` instance and update its `modState`
* member accordingly.
*/
static int ModFetchingProgressCallback(
void* ptr, curl_off_t totalDownloadSize, curl_off_t finishedDownloadSize, curl_off_t totalToUpload, curl_off_t nowUploaded);
/**
* Downloads a mod archive from distant store.
*
* This rebuilds the URI of the mod archive using both a predefined store URI
* and the mod dependency string from the `verifiedMods` variable, or using
* input mod name as mod dependency string if bypass flag is set up; fetched
* archive is then stored in a temporary location.
*
* If something went wrong during archive download, this will return an empty
* optional object.
*
* @param modName name of the mod to be downloaded
* @param modVersion version of the mod to be downloaded
* @returns location of the downloaded archive
*/
std::optional<fs::path> FetchModFromDistantStore(std::string_view modName, std::string_view modVersion);
/**
* Tells if a mod archive has not been corrupted.
*
* The mod validation procedure includes computing the SHA256 hash of the final
* archive, which is stored in the verified mods list. This hash is used by this
* very method to ensure the archive downloaded from the Internet is the exact
* same that has been manually verified.
*
* @param modPath path of the archive to check
* @param expectedChecksum checksum the archive should have
* @returns whether archive is legit
*/
bool IsModLegit(fs::path modPath, std::string_view expectedChecksum);
/**
* Extracts a mod archive to the game folder.
*
* This extracts a downloaded mod archive from its original location to the
* current game profile, in the remote mods folder.
*
* @param modPath location of the downloaded archive
* @returns nothing
*/
void ExtractMod(fs::path modPath);
public:
ModDownloader();
/**
* Retrieves the verified mods list from the central authority.
*
* The Northstar auto-downloading feature does NOT allow automatically installing
* all mods for various (notably security) reasons; mods that are candidate to
* auto-downloading are rather listed on a GitHub repository
* (https://raw.githubusercontent.com/R2Northstar/VerifiedMods/main/verified-mods.json),
* which this method gets via a HTTP call to load into local state.
*
* If list fetching fails, local mods list will be initialized as empty, thus
* preventing any mod from being auto-downloaded.
*
* @returns nothing
*/
void FetchModsListFromAPI();
/**
* Checks whether a mod is verified.
*
* A mod is deemed verified/authorized through a manual validation process that is
* described here: https://github.com/R2Northstar/VerifiedMods; in practice, a mod
* is considered authorized if their name AND exact version appear in the
* `verifiedMods` variable.
*
* @param modName name of the mod to be checked
* @param modVersion version of the mod to be checked, must follow semantic versioning
* @returns whether the mod is authorized and can be auto-downloaded
*/
bool IsModAuthorized(std::string_view modName, std::string_view modVersion);
/**
* Downloads a given mod from Thunderstore API to local game profile.
*
* @param modName name of the mod to be downloaded
* @param modVersion version of the mod to be downloaded
* @returns nothing
**/
void DownloadMod(std::string modName, std::string modVersion);
enum ModInstallState
{
MANIFESTO_FETCHING,
// Normal installation process
DOWNLOADING,
CHECKSUMING,
EXTRACTING,
DONE, // Everything went great, mod can be used in-game
// Errors
FAILED, // Generic error message, should be avoided as much as possible
FAILED_READING_ARCHIVE,
FAILED_WRITING_TO_DISK,
MOD_FETCHING_FAILED,
MOD_CORRUPTED, // Downloaded archive checksum does not match verified hash
NO_DISK_SPACE_AVAILABLE,
NOT_FOUND // Mod is not currently being auto-downloaded
};
struct MOD_STATE
{
ModInstallState state;
int progress;
int total;
float ratio;
} modState = {};
/**
* Cancels installation of the mod.
*
* Prevents installation of the mod currently being installed, no matter the install
* progress (downloading, checksuming, extracting), and frees all resources currently
* being used in this purpose.
*
* @returns nothing
*/
void CancelDownload();
};
|