From 294f6333b6c166cb5d786f3cae0ccc4d1c22c25f Mon Sep 17 00:00:00 2001 From: aperezro Date: Mon, 8 Jun 2026 17:38:30 -0600 Subject: [PATCH] Validate iOS installs before launch --- UnleashedRecomp/install/installer.cpp | 54 +++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/UnleashedRecomp/install/installer.cpp b/UnleashedRecomp/install/installer.cpp index c06ea571..35465bbd 100644 --- a/UnleashedRecomp/install/installer.cpp +++ b/UnleashedRecomp/install/installer.cpp @@ -31,6 +31,9 @@ static const std::string UpdateExecutablePatchFile = "default.xexp"; static const std::string ISOExtension = ".iso"; static const std::string OldExtension = ".old"; static const std::string TempExtension = ".tmp"; +#ifdef UNLEASHED_RECOMP_IOS +static const std::string InstallValidationFile = "install.validated"; +#endif static std::string fromU8(const std::u8string &str) { @@ -351,6 +354,11 @@ bool Installer::checkGameInstall(const std::filesystem::path &baseDirectory, std if (!std::filesystem::exists(baseDirectory / GameDirectory / GameExecutableFile)) return false; +#ifdef UNLEASHED_RECOMP_IOS + if (!std::filesystem::exists(baseDirectory / InstallValidationFile)) + return false; +#endif + return true; } @@ -597,6 +605,21 @@ bool Installer::install(Sources &sources, const std::filesystem::path &targetDir // Install files in reverse order of importance. In case of a process crash or power outage, this will increase the likelihood of the installation // missing critical files required for the game to run. These files are used as the way to detect if the game is installed. +#ifdef UNLEASHED_RECOMP_IOS + const bool isInstallingGameData = sources.game != nullptr || sources.update != nullptr; + if (isInstallingGameData) + { + std::error_code ec; + std::filesystem::remove(targetDirectory / InstallValidationFile, ec); + if (ec) + { + journal.lastResult = Journal::Result::FileWriteFailed; + journal.lastErrorMessage = fmt::format("Failed to remove validation file at {}.", fromPath(targetDirectory / InstallValidationFile)); + return false; + } + } +#endif + // Install the DLC. if (!sources.dlc.empty()) { @@ -668,6 +691,37 @@ bool Installer::install(Sources &sources, const std::filesystem::path &targetDir // Update the progress with the artificial amount attributed to the patching. journal.progressCounter += PatcherContribution; + +#ifdef UNLEASHED_RECOMP_IOS + Journal validationJournal; + if (!checkInstallIntegrity(targetDirectory, validationJournal, progressCallback)) + { + journal.lastResult = validationJournal.lastResult; + journal.lastPatcherResult = validationJournal.lastPatcherResult; + journal.lastErrorMessage = validationJournal.lastErrorMessage; + return false; + } + + std::filesystem::path validationPath = targetDirectory / InstallValidationFile; + std::ofstream validationStream(validationPath, std::ios::binary); + if (!validationStream.is_open()) + { + journal.lastResult = Journal::Result::FileCreationFailed; + journal.lastErrorMessage = fmt::format("Failed to create file at {}.", fromPath(validationPath)); + return false; + } + + validationStream << "ok\n"; + validationStream.flush(); + if (!validationStream.good()) + { + journal.lastResult = Journal::Result::FileWriteFailed; + journal.lastErrorMessage = fmt::format("Failed to write file at {}.", fromPath(validationPath)); + return false; + } + + journal.createdFiles.push_back(validationPath); +#endif for (uint32_t i = 0; i < 2; i++) {