@@ -17,16 +17,6 @@ static StringMap<async::TaskHolder<web::WebResponse>> RUNNING_REQUESTS {};
1717
1818bool s_isNewUpdateDownloaded = false ;
1919
20- namespace {
21- inline std::string formatDownloadUrl (std::string_view tag) {
22- return fmt::format (" https://github.com/geode-sdk/geode/releases/download/{0}/geode-{0}-{1}.zip" , tag, GEODE_PLATFORM_SHORT_IDENTIFIER_NOARCH );
23- }
24-
25- inline std::string formatResourcesUrl (std::string_view tag) {
26- return fmt::format (" https://github.com/geode-sdk/geode/releases/download/{}/resources.zip" , tag);
27- }
28- }
29-
3020void updater::downloadLatestLoaderResources () {
3121 log::debug (" Downloading latest resources" );
3222
@@ -36,10 +26,7 @@ void updater::downloadLatestLoaderResources() {
3626 if (res.ok ()) {
3727 auto & release = res.unwrap ();
3828
39- updater::tryDownloadLoaderResources (
40- formatResourcesUrl (release.tag ),
41- false
42- );
29+ updater::tryDownloadLoaderResources (release.resources .url , release.resources .hash , false );
4330 } else {
4431 ResourceDownloadEvent ().send (
4532 UpdateFailed (" Unable to download resources: " + res.unwrapErr ().details )
@@ -49,7 +36,13 @@ void updater::downloadLatestLoaderResources() {
4936 );
5037}
5138
52- Result<> updater::extractLoaderResources (ByteSpan data) {
39+ Result<> updater::extractLoaderResources (ByteSpan data, std::string_view expectedHash) {
40+ auto actualHash = geode::sha256 (data).toString ();
41+ if (actualHash != expectedHash) {
42+ log::error (" Hash mismatch in downloaded resources: expected {} but got {}" , expectedHash, actualHash);
43+ return Err (" Hash mismatch in downloaded resources" );
44+ }
45+
5346 auto tempDir = dirs::getGeodeResourcesDir () / fmt::format (" {}_tmp" , Mod::get ()->getID ());
5447 auto resourcesDir = dirs::getGeodeResourcesDir () / Mod::get ()->getID ();
5548
@@ -90,7 +83,7 @@ Result<> updater::extractLoaderResources(ByteSpan data) {
9083 return Ok ();
9184}
9285
93- void updater::tryDownloadLoaderResources (std::string url, bool tryLatestOnError) {
86+ void updater::tryDownloadLoaderResources (std::string url, std::string hash, bool tryLatestOnError) {
9487 if (RUNNING_REQUESTS .contains (url)) return ;
9588
9689 auto progress = [](const web::WebProgress& prog) {
@@ -106,10 +99,10 @@ void updater::tryDownloadLoaderResources(std::string url, bool tryLatestOnError)
10699 holder.spawn (
107100 " Geode resources download" ,
108101 web::WebRequest{}.onProgress (std::move (progress)).get (url),
109- [url](auto response) {
102+ [url, hash = std::move (hash) ](auto response) {
110103 if (response.ok ()) {
111104 auto data = std::move (response).data ();
112- if (GEODE_UNWRAP_IF_ERR (e, updater::extractLoaderResources (data))) {
105+ if (GEODE_UNWRAP_IF_ERR (e, updater::extractLoaderResources (data, hash ))) {
113106 ResourceDownloadEvent ().send (UpdateFailed (e));
114107 } else {
115108 ResourceDownloadEvent ().send (UpdateFinished ());
@@ -151,9 +144,7 @@ void updater::downloadLoaderResources(bool useLatestRelease) {
151144 if (res.ok ()) {
152145 auto & release = res.unwrap ();
153146
154- updater::tryDownloadLoaderResources (
155- formatResourcesUrl (release.tag ), false
156- );
147+ updater::tryDownloadLoaderResources (release.resources .url , release.resources .hash , false );
157148
158149 DOWNLOADING_LOADER_RESOURCES = false ;
159150 return ;
@@ -233,7 +224,7 @@ bool updater::verifyLoaderResources() {
233224 return true ;
234225}
235226
236- void updater::downloadLoaderUpdate (std::string url) {
227+ void updater::downloadLoaderUpdate (std::string url, std::string hash ) {
237228 if (RUNNING_REQUESTS .contains (" @downloadLoaderUpdate" )) return ;
238229
239230 auto req = web::WebRequest ();
@@ -249,49 +240,53 @@ void updater::downloadLoaderUpdate(std::string url) {
249240 auto & holder = RUNNING_REQUESTS [" @downloadLoaderUpdate" ];
250241 holder.spawn (
251242 req.get (std::move (url)),
252- [](web::WebResponse response) {
243+ [hash = std::move (hash) ](web::WebResponse response) {
253244 RUNNING_REQUESTS .erase (" @downloadLoaderUpdate" );
254245
255- auto updateZip = dirs::getTempDir () / " loader-update.zip" ;
256- auto targetDir = dirs::getGeodeDir () / " update" ;
257-
258- if (response.ok ()) {
259- // unzip resources zip
260- auto data = std::move (response).data ();
261- auto unzip = file::Unzip::create (data);
262- if (unzip) {
263- auto ok = unzip.unwrap ().extractAllTo (targetDir);
264- if (ok) {
265- s_isNewUpdateDownloaded = true ;
266- LoaderUpdateEvent ().send (UpdateFinished ());
267- }
268- else {
269- LoaderUpdateEvent ().send (
270- UpdateFailed (" Unable to unzip update: " + ok.unwrapErr ())
271- );
272- Mod::get ()->setSavedValue (" last-modified-auto-update-check" , std::string ());
273- }
274- }
275- else {
276- LoaderUpdateEvent ().send (
277- UpdateFailed (" Unable to unzip update: " + unzip.unwrapErr ())
278- );
279- Mod::get ()->setSavedValue (" last-modified-auto-update-check" , std::string ());
280- }
281- }
282- else {
283- auto info = response.string ().unwrapOr (" Unknown error" );
284- log::error (" Failed to download latest update {}" , info);
246+ auto result = installLoaderUpdate (std::move (response), hash);
247+ if (!result) {
248+ log::error (" Failed to install latest update: {}" , result.unwrapErr ());
285249 LoaderUpdateEvent ().send (
286- UpdateFailed (" Unable to download update: " + info )
250+ UpdateFailed (fmt::format ( " Unable to install loader update: {} " , result. unwrapErr ()) )
287251 );
288-
289252 Mod::get ()->setSavedValue (" last-modified-auto-update-check" , std::string ());
290253 }
291254 }
292255 );
293256}
294257
258+ Result<> updater::installLoaderUpdate (utils::web::WebResponse response, std::string_view expectedHash) {
259+ auto targetDir = dirs::getGeodeDir () / " update" ;
260+
261+ if (!response.ok ()) {
262+ auto info = response.string ().unwrapOr (" Unknown error" );
263+ return Err (" Download failed: {}" , info);
264+ }
265+
266+ // validate hash
267+ auto data = std::move (response).data ();
268+ auto actualHash = geode::sha256 (data).toString ();
269+ if (actualHash != expectedHash) {
270+ log::error (" Hash mismatch in downloaded loader update, we expected {}, but got {}" , expectedHash, actualHash);
271+ return Err (" Hash mismatch in downloaded loader update" );
272+ }
273+
274+ // unzip resources zip
275+ auto unzip = file::Unzip::create (data);
276+ if (!unzip) {
277+ return Err (" Unable to unzip update: {}" , unzip.unwrapErr ());
278+ }
279+
280+ auto ok = unzip.unwrap ().extractAllTo (targetDir);
281+ if (!ok) {
282+ return Err (" Unable to extract update: {}" , ok.unwrapErr ());
283+ }
284+
285+ s_isNewUpdateDownloaded = true ;
286+ LoaderUpdateEvent ().send (UpdateFinished ());
287+ return Ok ();
288+ }
289+
295290void updater::checkForLoaderUpdates () {
296291 // Check for updates in the background
297292 async::spawn (
@@ -314,8 +309,7 @@ void updater::checkForLoaderUpdates() {
314309 return ;
315310 }
316311
317- // find release asset
318- updater::downloadLoaderUpdate (formatDownloadUrl (release.tag ));
312+ updater::downloadLoaderUpdate (release.download .url , release.download .hash );
319313 } else {
320314 auto info = res.unwrapErr ().details ;
321315 log::error (" Failed to fetch updates {}" , info);
0 commit comments