feat: Added Auto Updater for Windows, macOS and Ubuntu (#1377)
This commit is contained in:
parent
c46e445a04
commit
f114239f51
@ -98,7 +98,7 @@ macro(configurePackingResources)
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
set(APPLICATION_TYPE)
|
||||
set(APPLICATION_TYPE WIN32)
|
||||
set(IMHEX_ICON "${IMHEX_BASE_FOLDER}/resources/resource.rc")
|
||||
|
||||
if (CREATE_PACKAGE)
|
||||
|
@ -536,6 +536,13 @@ namespace hex {
|
||||
* @return Git commit branch
|
||||
*/
|
||||
std::string getCommitBranch();
|
||||
|
||||
enum class UpdateType {
|
||||
Stable,
|
||||
Nightly
|
||||
};
|
||||
|
||||
bool updateImHex(UpdateType updateType);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -11,15 +11,4 @@ namespace hex {
|
||||
return fmt::format(fmt::runtime(format), args...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
inline void print(std::string_view format, Args... args) {
|
||||
fmt::print(fmt::runtime(format), args...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
inline void println(std::string_view format, Args... args) {
|
||||
fmt::print(fmt::runtime(format), args...);
|
||||
fmt::print("\n");
|
||||
}
|
||||
|
||||
}
|
@ -19,6 +19,7 @@ namespace hex::log {
|
||||
wolv::io::File& getFile();
|
||||
bool isRedirected();
|
||||
[[maybe_unused]] void redirectToFile();
|
||||
[[maybe_unused]] void enableColorPrinting();
|
||||
|
||||
extern std::mutex g_loggerMutex;
|
||||
|
||||
@ -50,8 +51,7 @@ namespace hex::log {
|
||||
fmt::print(dest, "{}", std::string(ProjectNameLength > 10 ? 0 : 10 - ProjectNameLength, ' '));
|
||||
}
|
||||
|
||||
template<typename... T>
|
||||
[[maybe_unused]] void print(const fmt::text_style &ts, const std::string &level, const std::string &fmt, auto... args) {
|
||||
[[maybe_unused]] void print(const fmt::text_style &ts, const std::string &level, const std::string &fmt, auto && ... args) {
|
||||
std::scoped_lock lock(impl::g_loggerMutex);
|
||||
|
||||
auto dest = impl::getDestination();
|
||||
@ -66,7 +66,7 @@ namespace hex::log {
|
||||
|
||||
}
|
||||
|
||||
[[maybe_unused]] void debug(const std::string &fmt, auto &&...args) {
|
||||
[[maybe_unused]] void debug(const std::string &fmt, auto && ... args) {
|
||||
#if defined(DEBUG)
|
||||
hex::log::print(fg(fmt::color::light_green) | fmt::emphasis::bold, "[DEBUG]", fmt, args...);
|
||||
#else
|
||||
@ -74,20 +74,39 @@ namespace hex::log {
|
||||
#endif
|
||||
}
|
||||
|
||||
[[maybe_unused]] void info(const std::string &fmt, auto &&...args) {
|
||||
[[maybe_unused]] void info(const std::string &fmt, auto && ... args) {
|
||||
hex::log::print(fg(fmt::color::cadet_blue) | fmt::emphasis::bold, "[INFO] ", fmt, args...);
|
||||
}
|
||||
|
||||
[[maybe_unused]] void warn(const std::string &fmt, auto &&...args) {
|
||||
[[maybe_unused]] void warn(const std::string &fmt, auto && ... args) {
|
||||
hex::log::print(fg(fmt::color::orange) | fmt::emphasis::bold, "[WARN] ", fmt, args...);
|
||||
}
|
||||
|
||||
[[maybe_unused]] void error(const std::string &fmt, auto &&...args) {
|
||||
[[maybe_unused]] void error(const std::string &fmt, auto && ... args) {
|
||||
hex::log::print(fg(fmt::color::red) | fmt::emphasis::bold, "[ERROR]", fmt, args...);
|
||||
}
|
||||
|
||||
[[maybe_unused]] void fatal(const std::string &fmt, auto &&...args) {
|
||||
[[maybe_unused]] void fatal(const std::string &fmt, auto && ... args) {
|
||||
hex::log::print(fg(fmt::color::purple) | fmt::emphasis::bold, "[FATAL]", fmt, args...);
|
||||
}
|
||||
|
||||
|
||||
[[maybe_unused]] void print(const std::string &fmt, auto && ... args) {
|
||||
std::scoped_lock lock(impl::g_loggerMutex);
|
||||
|
||||
auto dest = impl::getDestination();
|
||||
auto message = fmt::format(fmt::runtime(fmt), args...);
|
||||
fmt::print(dest, "{}", message);
|
||||
fflush(dest);
|
||||
}
|
||||
|
||||
[[maybe_unused]] void println(const std::string &fmt, auto && ... args) {
|
||||
std::scoped_lock lock(impl::g_loggerMutex);
|
||||
|
||||
auto dest = impl::getDestination();
|
||||
auto message = fmt::format(fmt::runtime(fmt), args...);
|
||||
fmt::print(dest, "{}\n", message);
|
||||
fflush(dest);
|
||||
}
|
||||
|
||||
}
|
@ -59,7 +59,8 @@ namespace hex {
|
||||
[[nodiscard]] std::string toByteString(u64 bytes);
|
||||
[[nodiscard]] std::string makePrintable(u8 c);
|
||||
|
||||
void runCommand(const std::string &command);
|
||||
void startProgram(const std::string &command);
|
||||
int executeCommand(const std::string &command);
|
||||
void openWebpage(std::string url);
|
||||
|
||||
[[nodiscard]] std::string encodeByteString(const std::vector<u8> &bytes);
|
||||
|
@ -5,8 +5,10 @@
|
||||
#include <hex/api/task.hpp>
|
||||
#include <hex/providers/provider.hpp>
|
||||
#include <hex/helpers/fmt.hpp>
|
||||
#include <hex/helpers/utils.hpp>
|
||||
|
||||
#include <wolv/io/file.hpp>
|
||||
#include <wolv/utils/string.hpp>
|
||||
|
||||
#include <utility>
|
||||
#include <unistd.h>
|
||||
@ -639,6 +641,44 @@ namespace hex {
|
||||
#endif
|
||||
}
|
||||
|
||||
bool updateImHex(UpdateType updateType) {
|
||||
// Get the path of the updater executable
|
||||
std::fs::path executablePath;
|
||||
|
||||
for (const auto &entry : std::fs::directory_iterator(wolv::io::fs::getExecutablePath()->parent_path())) {
|
||||
if (entry.path().filename().string().starts_with("imhex-updater")) {
|
||||
executablePath = entry.path();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (executablePath.empty() || !wolv::io::fs::exists(executablePath))
|
||||
return false;
|
||||
|
||||
std::string updateTypeString;
|
||||
switch (updateType) {
|
||||
case UpdateType::Stable:
|
||||
updateTypeString = "latest";
|
||||
break;
|
||||
case UpdateType::Nightly:
|
||||
updateTypeString = "nightly";
|
||||
break;
|
||||
}
|
||||
|
||||
EventManager::subscribe<EventImHexClosing>([executablePath, updateTypeString] {
|
||||
hex::executeCommand(
|
||||
hex::format("{} {}",
|
||||
wolv::util::toUTF8String(executablePath),
|
||||
updateTypeString
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
ImHexApi::System::closeImHex();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace ImHexApi::Messaging {
|
||||
|
@ -4,6 +4,10 @@
|
||||
|
||||
#include <wolv/io/file.hpp>
|
||||
|
||||
#if defined(OS_WINDOWS)
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
namespace hex::log::impl {
|
||||
|
||||
static wolv::io::File s_loggerFile;
|
||||
@ -36,6 +40,19 @@ namespace hex::log::impl {
|
||||
}
|
||||
}
|
||||
|
||||
void enableColorPrinting() {
|
||||
#if defined(OS_WINDOWS)
|
||||
auto hConsole = ::GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
if (hConsole != INVALID_HANDLE_VALUE) {
|
||||
DWORD mode = 0;
|
||||
if (::GetConsoleMode(hConsole, &mode) == TRUE) {
|
||||
mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING | ENABLE_PROCESSED_OUTPUT;
|
||||
::SetConsoleMode(hConsole, mode);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
std::vector<LogEntry>& getLogEntries() {
|
||||
static std::vector<LogEntry> logEntries;
|
||||
|
@ -313,7 +313,7 @@ namespace hex {
|
||||
return std::to_string(value).substr(0, 5) + Suffixes[suffixIndex];
|
||||
}
|
||||
|
||||
void runCommand(const std::string &command) {
|
||||
void startProgram(const std::string &command) {
|
||||
|
||||
#if defined(OS_WINDOWS)
|
||||
hex::unused(system(hex::format("start {0}", command).c_str()));
|
||||
@ -326,6 +326,10 @@ namespace hex {
|
||||
#endif
|
||||
}
|
||||
|
||||
int executeCommand(const std::string &command) {
|
||||
return ::system(command.c_str());
|
||||
}
|
||||
|
||||
void openWebpage(std::string url) {
|
||||
if (!url.contains("://"))
|
||||
url = "https://" + url;
|
||||
|
@ -8,3 +8,7 @@ add_subdirectory(gui)
|
||||
if (WIN32)
|
||||
add_subdirectory(forwarder)
|
||||
endif ()
|
||||
|
||||
if (NOT EMSCRIPTEN)
|
||||
add_subdirectory(updater)
|
||||
endif ()
|
@ -28,8 +28,11 @@
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
void handleConsoleWindow() {
|
||||
void setupConsoleWindow() {
|
||||
// Get the handle of the console window
|
||||
HWND consoleWindow = ::GetConsoleWindow();
|
||||
|
||||
// Get console process ID
|
||||
DWORD processId = 0;
|
||||
::GetWindowThreadProcessId(consoleWindow, &processId);
|
||||
|
||||
@ -48,6 +51,11 @@ void handleConsoleWindow() {
|
||||
::SetConsoleMode(hConsole, mode);
|
||||
}
|
||||
}
|
||||
|
||||
// Set the __IMHEX_FORWARD_CONSOLE__ environment variable,
|
||||
// to let ImHex know that it was launched from the forwarder
|
||||
// and that it should forward it's console output to us
|
||||
::SetEnvironmentVariableA("__IMHEX_FORWARD_CONSOLE__", "1");
|
||||
}
|
||||
}
|
||||
|
||||
@ -57,23 +65,26 @@ int launchExecutable() {
|
||||
auto executableFullPath = executablePath->parent_path() / "imhex-gui.exe";
|
||||
|
||||
::PROCESS_INFORMATION process = { };
|
||||
::STARTUPINFOW startupInfo = { };
|
||||
startupInfo.cb = sizeof(STARTUPINFOW);
|
||||
::STARTUPINFOW startupInfo = { .cb = sizeof(::STARTUPINFOW) };
|
||||
|
||||
// Create a new process for imhex-gui.exe with the same command line as the current process
|
||||
if (::CreateProcessW(executableFullPath.wstring().c_str(), ::GetCommandLineW(), nullptr, nullptr, FALSE, 0, nullptr, nullptr, &startupInfo, &process) == FALSE) {
|
||||
// Handle error if the process could not be created
|
||||
|
||||
// Get formatted error message from the OS
|
||||
auto errorCode = ::GetLastError();
|
||||
auto errorMessageString = std::system_category().message(errorCode);
|
||||
|
||||
// Generate error message
|
||||
auto errorMessage = fmt::format("Failed to start ImHex:\n\nError code: 0x{:08X}\n\n{}", errorCode, errorMessageString);
|
||||
|
||||
// Display a message box with the error
|
||||
::MessageBoxA(nullptr, errorMessage.c_str(), "ImHex Forwarder", MB_OK | MB_ICONERROR);
|
||||
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Wait for the main ImHex process to exit
|
||||
::WaitForSingleObject(process.hProcess, INFINITE);
|
||||
::CloseHandle(process.hProcess);
|
||||
|
||||
@ -81,8 +92,7 @@ int launchExecutable() {
|
||||
}
|
||||
|
||||
int main() {
|
||||
handleConsoleWindow();
|
||||
auto result = launchExecutable();
|
||||
setupConsoleWindow();
|
||||
|
||||
return result;
|
||||
return launchExecutable();
|
||||
}
|
@ -32,7 +32,7 @@ namespace hex::crash {
|
||||
void resetCrashHandlers();
|
||||
|
||||
static void sendNativeMessage(const std::string& message) {
|
||||
hex::nativeErrorMessage(hex::format("ImHex crashed during its loading.\nError: {}", message));
|
||||
hex::nativeErrorMessage(hex::format("ImHex crashed during initial setup!\nError: {}", message));
|
||||
}
|
||||
|
||||
// Function that decides what should happen on a crash
|
||||
@ -136,15 +136,14 @@ namespace hex::crash {
|
||||
// Reset crash handlers, so we can't have a recursion if this code crashes
|
||||
resetCrashHandlers();
|
||||
|
||||
handleCrash("Uncaught exception!");
|
||||
|
||||
// Print the current exception info
|
||||
try {
|
||||
std::rethrow_exception(std::current_exception());
|
||||
} catch (std::exception &ex) {
|
||||
std::string exceptionStr = hex::format("{}()::what() -> {}", llvm::itaniumDemangle(typeid(ex).name(), nullptr, nullptr, nullptr), ex.what());
|
||||
log::fatal("Program terminated with uncaught exception: {}", exceptionStr);
|
||||
|
||||
handleCrash(exceptionStr);
|
||||
log::fatal("Program terminated with uncaught exception: {}", exceptionStr);
|
||||
}
|
||||
|
||||
triggerSafeShutdown();
|
||||
|
@ -18,6 +18,7 @@
|
||||
|
||||
#include <wolv/io/fs.hpp>
|
||||
#include <wolv/utils/guards.hpp>
|
||||
#include <fcntl.h>
|
||||
|
||||
#if defined(OS_WEB)
|
||||
#include <emscripten.h>
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <windowsx.h>
|
||||
#include <shobjidl.h>
|
||||
#include <wrl/client.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <csignal>
|
||||
#include <cstdio>
|
||||
@ -231,22 +232,52 @@ namespace hex {
|
||||
}
|
||||
|
||||
|
||||
static void reopenConsoleHandle(u32 stdHandleNumber, i32 stdFileDescriptor, FILE *stdStream) {
|
||||
// Get the Windows handle for the standard stream
|
||||
HANDLE handle = ::GetStdHandle(stdHandleNumber);
|
||||
|
||||
// Redirect the standard stream to the relevant console stream
|
||||
if (stdFileDescriptor == STDIN_FILENO)
|
||||
freopen("CONIN$", "rt", stdStream);
|
||||
else
|
||||
freopen("CONOUT$", "wt", stdStream);
|
||||
|
||||
// Disable buffering
|
||||
setvbuf(stdStream, nullptr, _IONBF, 0);
|
||||
|
||||
// Reopen the standard stream as a file descriptor
|
||||
auto unboundFd = [stdFileDescriptor, handle]{
|
||||
if (stdFileDescriptor == STDIN_FILENO)
|
||||
return _open_osfhandle(intptr_t(handle), _O_RDONLY);
|
||||
else
|
||||
return _open_osfhandle(intptr_t(handle), _O_WRONLY);
|
||||
}();
|
||||
|
||||
// Duplicate the file descriptor to the standard stream
|
||||
dup2(unboundFd, stdFileDescriptor);
|
||||
}
|
||||
|
||||
|
||||
void Window::initNative() {
|
||||
HWND consoleWindow = ::GetConsoleWindow();
|
||||
DWORD processId = 0;
|
||||
::GetWindowThreadProcessId(consoleWindow, &processId);
|
||||
if (GetCurrentProcessId() == processId) {
|
||||
ShowWindow(consoleWindow, SW_HIDE);
|
||||
log::impl::redirectToFile();
|
||||
// Check for the __IMHEX_FORWARD_CONSOLE__ environment variable that was set by the forwarder application
|
||||
// If it's present attach to its console window
|
||||
if (hex::getEnvironmentVariable("__IMHEX_FORWARD_CONSOLE__") == "1") {
|
||||
::AttachConsole(ATTACH_PARENT_PROCESS);
|
||||
|
||||
// Reopen stdin, stdout and stderr to the console
|
||||
reopenConsoleHandle(STD_INPUT_HANDLE, STDIN_FILENO, stdin);
|
||||
reopenConsoleHandle(STD_OUTPUT_HANDLE, STDOUT_FILENO, stdout);
|
||||
|
||||
// Explicitly don't forward stderr because some libraries like to write to it
|
||||
// with no way to disable it (e.g., libmagic)
|
||||
/*
|
||||
reopenConsoleHandle(STD_ERROR_HANDLE, STDERR_FILENO, stderr);
|
||||
*/
|
||||
|
||||
// Enable ANSI colors in the console
|
||||
log::impl::enableColorPrinting();
|
||||
} else {
|
||||
auto hConsole = ::GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
if (hConsole != INVALID_HANDLE_VALUE) {
|
||||
DWORD mode = 0;
|
||||
if (::GetConsoleMode(hConsole, &mode) == TRUE) {
|
||||
mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING | ENABLE_PROCESSED_OUTPUT;
|
||||
::SetConsoleMode(hConsole, mode);
|
||||
}
|
||||
}
|
||||
log::impl::redirectToFile();
|
||||
}
|
||||
|
||||
ImHexApi::System::impl::setBorderlessWindowMode(true);
|
||||
@ -256,11 +287,6 @@ namespace hex {
|
||||
if (std::fs::exists(path))
|
||||
AddDllDirectory(path.c_str());
|
||||
}
|
||||
|
||||
// Various libraries sadly directly print to stderr with no way to disable it
|
||||
// We redirect stderr to NUL to prevent this
|
||||
freopen("NUL:", "w", stderr);
|
||||
setvbuf(stderr, nullptr, _IONBF, 0);
|
||||
}
|
||||
|
||||
void Window::setupNativeWindow() {
|
||||
|
15
main/updater/CMakeLists.txt
Normal file
15
main/updater/CMakeLists.txt
Normal file
@ -0,0 +1,15 @@
|
||||
project(updater)
|
||||
|
||||
add_executable(updater ${APPLICATION_TYPE}
|
||||
source/main.cpp
|
||||
)
|
||||
|
||||
add_compile_definitions(IMHEX_PROJECT_NAME="${PROJECT_NAME}")
|
||||
target_link_libraries(updater PRIVATE libimhex libwolv-io ${FMT_LIBRARIES})
|
||||
add_dependencies(imhex_all updater)
|
||||
set_target_properties(updater PROPERTIES
|
||||
OUTPUT_NAME "imhex-updater"
|
||||
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../..
|
||||
CXX_VISIBILITY_PRESET hidden
|
||||
POSITION_INDEPENDENT_CODE ON
|
||||
)
|
167
main/updater/source/main.cpp
Normal file
167
main/updater/source/main.cpp
Normal file
@ -0,0 +1,167 @@
|
||||
#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;
|
||||
}
|
||||
|
||||
std::optional<std::fs::path> 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);
|
||||
}
|
@ -26,7 +26,7 @@ namespace hex::plugin::builtin {
|
||||
void handleVersionCommand(const std::vector<std::string> &args) {
|
||||
hex::unused(args);
|
||||
|
||||
hex::print(romfs::get("logo.ans").string(),
|
||||
hex::log::print(std::string(romfs::get("logo.ans").string()),
|
||||
ImHexApi::System::getImHexVersion(),
|
||||
ImHexApi::System::getCommitBranch(), ImHexApi::System::getCommitHash(),
|
||||
__DATE__, __TIME__,
|
||||
@ -38,7 +38,7 @@ namespace hex::plugin::builtin {
|
||||
void handleHelpCommand(const std::vector<std::string> &args) {
|
||||
hex::unused(args);
|
||||
|
||||
hex::print(
|
||||
hex::log::print(
|
||||
"ImHex - A Hex Editor for Reverse Engineers, Programmers and people who value their retinas when working at 3 AM.\n"
|
||||
"\n"
|
||||
"usage: imhex [subcommand] [options]\n"
|
||||
@ -54,7 +54,7 @@ namespace hex::plugin::builtin {
|
||||
|
||||
for (const auto &plugin : PluginManager::getPlugins()) {
|
||||
for (const auto &subCommand : plugin.getSubCommands()) {
|
||||
hex::println(" --{}{: <{}} {}", subCommand.commandKey, "", longestCommand - subCommand.commandKey.size(), subCommand.commandDesc);
|
||||
hex::log::println(" --{}{: <{}} {}", subCommand.commandKey, "", longestCommand - subCommand.commandKey.size(), subCommand.commandDesc);
|
||||
}
|
||||
}
|
||||
|
||||
@ -63,7 +63,7 @@ namespace hex::plugin::builtin {
|
||||
|
||||
void handleOpenCommand(const std::vector<std::string> &args) {
|
||||
if (args.empty()) {
|
||||
hex::println("No files provided to open.");
|
||||
hex::log::println("No files provided to open.");
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
@ -89,8 +89,8 @@ namespace hex::plugin::builtin {
|
||||
|
||||
void handleCalcCommand(const std::vector<std::string> &args) {
|
||||
if (args.empty()) {
|
||||
hex::println("No expression provided!");
|
||||
hex::println("Example: imhex --calc \"5 * 7\"");
|
||||
hex::log::println("No expression provided!");
|
||||
hex::log::println("Example: imhex --calc \"5 * 7\"");
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
@ -100,9 +100,9 @@ namespace hex::plugin::builtin {
|
||||
auto result = evaluator.evaluate(input);
|
||||
|
||||
if (!result.has_value())
|
||||
hex::println("{}\n> '{}'", evaluator.getLastError().value(), input);
|
||||
hex::log::println("{}\n> '{}'", evaluator.getLastError().value(), input);
|
||||
else
|
||||
hex::println("{}", result.value());
|
||||
hex::log::println("{}", result.value());
|
||||
|
||||
std::exit(EXIT_SUCCESS);
|
||||
}
|
||||
@ -110,19 +110,19 @@ namespace hex::plugin::builtin {
|
||||
void handlePluginsCommand(const std::vector<std::string> &args) {
|
||||
hex::unused(args);
|
||||
|
||||
hex::println("Loaded plugins:");
|
||||
hex::log::println("Loaded plugins:");
|
||||
|
||||
for (const auto &plugin : PluginManager::getPlugins()) {
|
||||
hex::print("- ");
|
||||
hex::log::print("- ");
|
||||
|
||||
if (plugin.isBuiltinPlugin())
|
||||
hex::print("\033[1;43m{}\033[0m", plugin.getPluginName());
|
||||
hex::log::print("\033[1;43m{}\033[0m", plugin.getPluginName());
|
||||
else
|
||||
hex::print("\033[1m{}\033[0m", plugin.getPluginName());
|
||||
hex::log::print("\033[1m{}\033[0m", plugin.getPluginName());
|
||||
|
||||
hex::println(" by {}", plugin.getPluginAuthor());
|
||||
hex::log::println(" by {}", plugin.getPluginAuthor());
|
||||
|
||||
hex::println(" \033[2;3m{}\033[0m", plugin.getPluginDescription());
|
||||
hex::log::println(" \033[2;3m{}\033[0m", plugin.getPluginDescription());
|
||||
}
|
||||
|
||||
std::exit(EXIT_SUCCESS);
|
||||
@ -130,8 +130,8 @@ namespace hex::plugin::builtin {
|
||||
|
||||
void handleHashCommand(const std::vector<std::string> &args) {
|
||||
if (args.size() != 2) {
|
||||
hex::println("usage: imhex --hash <algorithm> <file>");
|
||||
hex::println("Available algorithms: md5, sha1, sha224, sha256, sha384, sha512");
|
||||
hex::log::println("usage: imhex --hash <algorithm> <file>");
|
||||
hex::log::println("Available algorithms: md5, sha1, sha224, sha256, sha384, sha512");
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
@ -140,7 +140,7 @@ namespace hex::plugin::builtin {
|
||||
|
||||
wolv::io::File file(filePath, wolv::io::File::Mode::Read);
|
||||
if (!file.isValid()) {
|
||||
hex::println("Failed to open file: {}", wolv::util::toUTF8String(filePath));
|
||||
hex::log::println("Failed to open file: {}", wolv::util::toUTF8String(filePath));
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
@ -162,20 +162,20 @@ namespace hex::plugin::builtin {
|
||||
} else if (algorithm == "sha512") {
|
||||
result = toVector(hex::crypt::sha512(file.readVector()));
|
||||
} else {
|
||||
hex::println("Unknown algorithm: {}", algorithm);
|
||||
hex::println("Available algorithms: md5, sha1, sha224, sha256, sha384, sha512");
|
||||
hex::log::println("Unknown algorithm: {}", algorithm);
|
||||
hex::log::println("Available algorithms: md5, sha1, sha224, sha256, sha384, sha512");
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
hex::println("{}({}) = {}", algorithm, wolv::util::toUTF8String(filePath.filename()), hex::crypt::encode16(result));
|
||||
hex::log::println("{}({}) = {}", algorithm, wolv::util::toUTF8String(filePath.filename()), hex::crypt::encode16(result));
|
||||
|
||||
std::exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
void handleEncodeCommand(const std::vector<std::string> &args) {
|
||||
if (args.size() != 2) {
|
||||
hex::println("usage: imhex --encode <algorithm> <string>");
|
||||
hex::println("Available algorithms: base64, hex");
|
||||
hex::log::println("usage: imhex --encode <algorithm> <string>");
|
||||
hex::log::println("Available algorithms: base64, hex");
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
@ -189,19 +189,19 @@ namespace hex::plugin::builtin {
|
||||
} else if (algorithm == "hex") {
|
||||
result = hex::crypt::encode16(data);
|
||||
} else {
|
||||
hex::println("Unknown algorithm: {}", algorithm);
|
||||
hex::println("Available algorithms: base64, hex");
|
||||
hex::log::println("Unknown algorithm: {}", algorithm);
|
||||
hex::log::println("Available algorithms: base64, hex");
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
hex::println("encode_{}({}) = {}", algorithm, args[1], result);
|
||||
hex::log::println("encode_{}({}) = {}", algorithm, args[1], result);
|
||||
std::exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
void handleDecodeCommand(const std::vector<std::string> &args) {
|
||||
if (args.size() != 2) {
|
||||
hex::println("usage: imhex --decode <algorithm> <string>");
|
||||
hex::println("Available algorithms: base64, hex");
|
||||
hex::log::println("usage: imhex --decode <algorithm> <string>");
|
||||
hex::log::println("Available algorithms: base64, hex");
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
@ -216,24 +216,24 @@ namespace hex::plugin::builtin {
|
||||
auto base16 = hex::crypt::decode16(std::string(data.begin(), data.end()));
|
||||
result = std::string(base16.begin(), base16.end());
|
||||
} else {
|
||||
hex::println("Unknown algorithm: {}", algorithm);
|
||||
hex::println("Available algorithms: base64, hex");
|
||||
hex::log::println("Unknown algorithm: {}", algorithm);
|
||||
hex::log::println("Available algorithms: base64, hex");
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
hex::print("decode_{}({}) = {}", algorithm, args[1], result);
|
||||
hex::log::print("decode_{}({}) = {}", algorithm, args[1], result);
|
||||
std::exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
void handleMagicCommand(const std::vector<std::string> &args) {
|
||||
if (args.size() != 2) {
|
||||
hex::println("usage: imhex --magic <operation> <file>");
|
||||
hex::println("Available operations: mime, desc");
|
||||
hex::log::println("usage: imhex --magic <operation> <file>");
|
||||
hex::log::println("Available operations: mime, desc");
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (!magic::compile()) {
|
||||
hex::print("Failed to compile magic database!");
|
||||
hex::log::print("Failed to compile magic database!");
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
@ -242,7 +242,7 @@ namespace hex::plugin::builtin {
|
||||
|
||||
wolv::io::File file(filePath, wolv::io::File::Mode::Read);
|
||||
if (!file.isValid()) {
|
||||
hex::println("Failed to open file: {}", wolv::util::toUTF8String(filePath));
|
||||
hex::log::println("Failed to open file: {}", wolv::util::toUTF8String(filePath));
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
@ -250,13 +250,13 @@ namespace hex::plugin::builtin {
|
||||
|
||||
if (operation == "mime") {
|
||||
auto result = magic::getMIMEType(data);
|
||||
hex::println("{}", result);
|
||||
hex::log::println("{}", result);
|
||||
} else if (operation == "desc") {
|
||||
auto result = magic::getDescription(data);
|
||||
hex::println("{}", result);
|
||||
hex::log::println("{}", result);
|
||||
} else {
|
||||
hex::println("Unknown operation: {}", operation);
|
||||
hex::println("Available operations: mime, desc");
|
||||
hex::log::println("Unknown operation: {}", operation);
|
||||
hex::log::println("Available operations: mime, desc");
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
@ -52,7 +52,7 @@ namespace hex::plugin::builtin {
|
||||
return hex::format("hex.builtin.command.cmd.result"_lang, input.data());
|
||||
},
|
||||
[](auto input) {
|
||||
hex::runCommand(input);
|
||||
hex::executeCommand(input);
|
||||
});
|
||||
|
||||
ContentRegistry::CommandPaletteCommands::addHandler(
|
||||
|
@ -211,7 +211,7 @@ namespace hex::plugin::builtin {
|
||||
ImGui::UnderlinedText("hex.builtin.welcome.header.update"_lang);
|
||||
{
|
||||
if (ImGui::DescriptionButton("hex.builtin.welcome.update.title"_lang, hex::format("hex.builtin.welcome.update.desc"_lang, ImHexApi::System::getInitArguments()["update-available"]).c_str(), ImVec2(ImGui::GetContentRegionAvail().x * 0.8F, 0)))
|
||||
hex::openWebpage("hex.builtin.welcome.update.link"_lang);
|
||||
ImHexApi::System::updateImHex(ImHexApi::System::UpdateType::Stable);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,9 +19,8 @@ GLFW_ICON ICON dist/windows/icon.ico
|
||||
BEGIN
|
||||
BLOCK "080904b0"
|
||||
BEGIN
|
||||
VALUE "FileDescription", "🔍 A Hex Editor for Reverse Engineers, Programmers and people who value their retinas when working at 3 AM."
|
||||
VALUE "CompanyName", "WerWolv"
|
||||
VALUE "FileDescription", "ImHex Hex Editor"
|
||||
VALUE "FileDescription", "ImHex Hex Editor 🔍"
|
||||
VALUE "LegalCopyright", "WerWolv 2020-2023"
|
||||
VALUE "OriginalFilename", "imhex.exe"
|
||||
VALUE "ProductName", "ImHex"
|
||||
|
Loading…
Reference in New Issue
Block a user