#include #include #include #include using namespace std::literals::string_literals; std::string getUpdateUrl(std::string_view versionType, std::string_view operatingSystem) { // Get the latest version info from the ImHex API auto response = hex::HttpRequest("GET", ImHexApiURL + fmt::format("/update/{}/{}", versionType, operatingSystem ) ).execute().get(); const auto &data = response.getData(); // Make sure we got a valid response if (!response.isSuccess()) { hex::log::error("Failed to get latest version info: ({}) {}", response.getStatusCode(), data); return { }; } return data; } std::optional downloadUpdate(const std::string &url, const std::string &type) { // Download the update auto response = hex::HttpRequest("GET", url).downloadFile().get(); // Make sure we got a valid response if (!response.isSuccess()) { hex::log::error("Failed to download update"); return std::nullopt; } const auto &data = response.getData(); // Write the update to a file std::fs::path filePath; { constexpr static auto UpdateFileName = "update.hexupd"; // Loop over all available paths wolv::io::File file; for (const auto &path : hex::fs::getDefaultPaths(hex::fs::ImHexPath::Config)) { // Remove any existing update files wolv::io::fs::remove(path / UpdateFileName); // If a valid location hasn't been found already, try to create a new file if (!file.isValid()) file = wolv::io::File(path / UpdateFileName, wolv::io::File::Mode::Create); } // If the update data can't be written to any of the default paths, the update cannot continue if (!file.isValid()) { hex::log::error("Failed to create update file"); return std::nullopt; } // Write the downloaded update data to the file file.writeVector(data); // Save the path to the update file filePath = file.getPath(); } return filePath; } std::string getUpdateType() { #if defined (OS_WINDOWS) if (!hex::ImHexApi::System::isPortableVersion()) return "win-msi"; #elif defined (OS_MACOS) return "mac-dmg"; #elif defined (OS_LINUX) if (hex::executeCommand("lsb_release -a | grep Ubuntu") == 0) { if (hex::executeCommand("lsb_release -a | grep 22.") == 0) return "linux-deb-22.04"; else if (hex::executeCommand("lsb_release -a | grep 23.") == 0) return "linux-deb-23.04"; } #endif return ""; } int installUpdate(const std::string &type, std::fs::path updatePath) { struct UpdateHandler { const char *type; const char *extension; const char *command; }; constexpr static auto UpdateHandlers = { UpdateHandler { "win-msi", ".msi", "msiexec /fa {} /passive" }, UpdateHandler { "macos-dmg", ".dmg", "hdiutil attach {}" }, UpdateHandler { "linux-deb-22.04", ".deb", "sudo apt update && sudo apt install -y --fix-broken {}" }, UpdateHandler { "linux-deb-23.04", ".deb", "sudo apt update && sudo apt install -y --fix-broken {}" }, }; for (const auto &handler : UpdateHandlers) { if (type == handler.type) { // Rename the update file to the correct extension updatePath.replace_extension(handler.extension); std::fs::rename(updatePath, updatePath); // Install the update using the correct command hex::startProgram(hex::format(handler.command, updatePath.string())); return EXIT_SUCCESS; } } // If the installation type isn't handled here, the detected installation type doesn't support updates through the updater hex::log::error("Install type cannot be updated"); // Open the latest release page in the default browser to allow the user to manually update hex::openWebpage("https://github.com/WerWolv/ImHex/releases/latest"); return EXIT_FAILURE; } int main(int argc, char **argv) { hex::log::impl::enableColorPrinting(); // Check we have the correct number of arguments if (argc != 2) { hex::log::error("Failed to start updater: Invalid arguments"); return EXIT_FAILURE; } // Read the version type from the arguments const auto versionType = argv[1]; hex::log::info("Updater started with version type: {}", versionType); // Query the update type const auto updateType = getUpdateType(); hex::log::info("Detected OS String: {}", updateType); // Make sure we got a valid update type if (updateType.empty()) { hex::log::error("Failed to detect installation type"); return EXIT_FAILURE; } // Get the url to the requested update from the ImHex API const auto updateUrl = getUpdateUrl(versionType, updateType); if (updateUrl.empty()) { hex::log::error("Failed to get update URL"); return EXIT_FAILURE; } hex::log::info("Update URL found: {}", updateUrl); // Download the update file auto updatePath = downloadUpdate(updateUrl, updateType); if (!updatePath.has_value()) return EXIT_FAILURE; // Install the update return installUpdate(updateType, *updatePath); }