Skip to content

Commit dcfe0a8

Browse files
committed
Mitigate the possibility of "exehash.txt" still being locked (because we opened it) when we try to delete the patch folder immediately afterwards (possible cause for recent remove_all exceptions, since this would cause that exact error)
- Read the hash using an ifstream that immediately gets released - Wrap attempts to delete the folder in try-catch, allow retries just in case - Always delete the patch zip when we are done with it - Add more logs
1 parent b4efaca commit dcfe0a8

1 file changed

Lines changed: 53 additions & 56 deletions

File tree

src/isaac_installation.cpp

Lines changed: 53 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,14 @@
66
#include <filesystem>
77
#include <regex>
88
#include <stdexcept>
9+
#include <string>
10+
#include <algorithm>
11+
#include <cctype>
12+
#include <chrono>
13+
#include <ctime>
14+
#include <cstdio>
15+
#include <vector>
16+
#include <curl/curl.h>
917

1018
#include "launcher/isaac_installation.h"
1119
#include "launcher/repentogon_installation.h"
@@ -20,22 +28,6 @@
2028
#include "launcher/standalone_rgon_folder.h"
2129
#include "steam_api.h"
2230

23-
24-
#include <io.h>
25-
#include <fstream>
26-
#include <filesystem>
27-
#include <string>
28-
#include <stdexcept>
29-
#include <curl/curl.h>
30-
#include <algorithm>
31-
#include <cctype>
32-
#include <chrono>
33-
#include <ctime>
34-
#include <string>
35-
#include <cstdio>
36-
#include <vector>
37-
#include <curl/curl.h>
38-
3931
static constexpr const char* __versionNeedle = "Binding of Isaac: Repentance+ v";
4032
static constexpr const char* __launcherDataFolder = "launcher-data";
4133
static constexpr const char* __patchFolder = "launcher-data/patch";
@@ -358,27 +350,37 @@ static size_t patchwriteresponse(void* contents, size_t size, size_t nmemb, void
358350
return fwrite(contents, size, nmemb, fp);
359351
}
360352

361-
static void RemoveOldPatchFolder()
353+
static bool RemoveOldPatchFolder()
362354
{
363-
namespace fs = std::filesystem;
364-
if (fs::exists(__patchFolder))
365-
fs::remove_all(__patchFolder);
355+
int attempts = 0;
356+
while (fs::exists(__patchFolder) && attempts++ <= 5) {
357+
try {
358+
fs::remove_all(__patchFolder);
359+
} catch (std::filesystem::filesystem_error& err) {
360+
Logger::Error("Failed to delete patch folder: %s (attempt #%d)", err.what(), attempts);
361+
std::this_thread::sleep_for(std::chrono::milliseconds(200));
362+
}
363+
}
364+
return !fs::exists(__patchFolder);
366365
}
367366

368-
bool CheckIfAvailableOnlineNGet(const std::string& currexehash,
369-
const std::string& targetversion)
367+
bool CheckIfAvailableOnlineNGet(const std::string& currexehash, const std::string& targetversion)
370368
{
371369
const std::string url =
372370
"https://gitlab.com/repentogon/versiontracker/-/raw/main/Patches/_" +
373371
currexehash + "_-_" + targetversion + "_.zip?inline=false";
374372

375373
CURL* curl = curl_easy_init();
376374
if (!curl)
375+
{
376+
Logger::Error("Failed to initialize curl!!!\n");
377377
return false;
378+
}
378379

379380
FILE* fp = fopen(__patchZip, "wb");
380381
if (!fp)
381382
{
383+
Logger::Error("Failed to fopen %s (%d)\n", __patchZip, errno);
382384
curl_easy_cleanup(curl);
383385
return false;
384386
}
@@ -402,24 +404,41 @@ bool CheckIfAvailableOnlineNGet(const std::string& currexehash,
402404
curl_slist_free_all(headers);
403405
curl_easy_cleanup(curl);
404406

405-
if (res != CURLE_OK || http_code < 200 || http_code >= 300)
407+
bool patchAvailable = false;
408+
409+
if (res != CURLE_OK)
406410
{
407-
return false;
411+
Logger::Error("Curl error: %d\n", res);
412+
}
413+
else if (http_code < 200 || http_code >= 300)
414+
{
415+
Logger::Info("Curl HTTP code: %d\n", http_code);
416+
}
417+
else if (!RemoveOldPatchFolder())
418+
{
419+
Logger::Error("Failed to remove existing patch folder!\n");
408420
}
409-
RemoveOldPatchFolder();
410-
if (!Zip::ExtractAllToFolder(__patchZip, __launcherDataFolder))
421+
else if (!Zip::ExtractAllToFolder(__patchZip, __launcherDataFolder))
411422
{
423+
Logger::Error("Failed to extract downloaded patch!\n");
424+
}
425+
else
426+
{
427+
// Patch successfully found, downloaded and extracted.
428+
patchAvailable = true;
429+
}
430+
431+
try {
412432
std::filesystem::remove(__patchZip);
413-
return false;
433+
} catch (std::filesystem::filesystem_error& err) {
434+
Logger::Error("Failed to delete patch zip: %s", err.what());
414435
}
415-
std::filesystem::remove(__patchZip);
416-
return true;
436+
return patchAvailable;
417437
}
418438

419439

420440

421441
bool InstallationData::PatchIsAvailable(const bool skipOnlineCheck) {
422-
namespace fs = std::filesystem;
423442
if (_vanillaexeispatchable > 0) {
424443
return _vanillaexeispatchable == 1; //so it doesnt do the whole check and we can call this a shitton of times without worrying, the vanilla exe shouldnt change while the launcher is open anyway, since the launcher doesnt update it and...if it does, just fucking restart the launcher, dude
425444
}
@@ -430,33 +449,11 @@ bool InstallationData::PatchIsAvailable(const bool skipOnlineCheck) {
430449
fs::path fullPath = fs::current_path() / __patchFolder / "exehash.txt";
431450
std::string s = fullPath.string();
432451
if (fs::exists(fullPath)) {
433-
ScopedFile file(fopen(s.c_str(), "r"));
434-
if (!file) {
435-
Logger::Error("InstallationData::PatchIsAvailable: failed to open patch file %s\n", s.c_str());
436-
return false;
437-
}
438-
439-
long begin = ftell(file);
440-
fseek(file, 0, SEEK_END);
441-
442-
if (ferror(file)) {
443-
Logger::Error("InstallationData::PatchIsAvailable: failed to fseek\n");
444-
return false;
445-
}
446-
447-
long end = ftell(file);
448-
rewind(file);
449-
450-
std::unique_ptr<char[]> content = std::make_unique<char[]>(end - begin + 1);
451-
if (!content) {
452-
Logger::Error("InstallationData::PatchIsAvailable: unable to allocate memory\n");
453-
return false;
454-
}
455-
456-
fread(content.get(), end - begin, 1, file);
457-
content.get()[end - begin] = '\0';
452+
// Read the exe hash and compare it.
453+
std::stringstream buffer;
454+
buffer << std::ifstream(fullPath).rdbuf();
455+
std::string currexehash = buffer.str();
458456

459-
std::string currexehash = content.get();
460457
std::transform(currexehash.begin(), currexehash.end(), currexehash.begin(),
461458
[](unsigned char c) { return std::tolower(c); });
462459

0 commit comments

Comments
 (0)