2023-10-21 20:40:24 +02:00
|
|
|
#include <hex/api_urls.hpp>
|
|
|
|
#include <hex/api/imhex_api.hpp>
|
|
|
|
|
|
|
|
#include <hex/helpers/http_requests.hpp>
|
|
|
|
#include <hex/helpers/utils.hpp>
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2023-11-10 20:47:08 +01:00
|
|
|
std::optional<std::fs::path> downloadUpdate(const std::string &url) {
|
2023-10-21 20:40:24 +02:00
|
|
|
// 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
|
2023-11-10 20:47:08 +01:00
|
|
|
auto updatePath = downloadUpdate(updateUrl);
|
2023-10-21 20:40:24 +02:00
|
|
|
if (!updatePath.has_value())
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
|
|
|
|
// Install the update
|
|
|
|
return installUpdate(updateType, *updatePath);
|
|
|
|
}
|