This commit is contained in:
Haris-Shahzad 2025-12-12 21:55:49 -07:00 committed by GitHub
commit 6b9af87db5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 121 additions and 8 deletions

View File

@ -58,6 +58,7 @@ set(LIBIMHEX_SOURCES
source/ui/banner.cpp
source/subcommands/subcommands.cpp
source/init/cli.cpp
)
if (APPLE)

View File

@ -88,6 +88,8 @@ EXPORT_MODULE namespace hex {
bool frameRateUnlockRequested();
void resetFrameRateUnlockRequested();
void setReadOnlyMode(bool enabled);
}
/**
@ -186,6 +188,11 @@ EXPORT_MODULE namespace hex {
*/
std::string getInitArgument(const std::string &key);
/**
* @brief Returns whether ImHex is running in read-only mode (no edits/saves)
*/
bool isReadOnlyMode();
/**
* @brief Sets if ImHex should follow the system theme
* @param enabled Whether to follow the system theme

View File

@ -619,6 +619,11 @@ namespace hex {
s_frameRateUnlockRequested = false;
}
static bool s_readOnlyMode = false;
void setReadOnlyMode(bool enabled) {
s_readOnlyMode = enabled;
}
}
bool isMainInstance() {
@ -1092,6 +1097,10 @@ namespace hex {
RequestSetPostProcessingShader::post(vertexShader, fragmentShader);
}
bool isReadOnlyMode() {
return impl::s_readOnlyMode;
}
}

View File

@ -0,0 +1,15 @@
#include <hex.hpp>
#include <hex/api/imhex_api/system.hpp>
namespace hex::init::cli_support {
void applyGlobalFlag(const std::string &arg) {
if (arg == "--readonly" || arg == "-r") {
ImHexApi::System::impl::setReadOnlyMode(true);
}
}
}

View File

@ -68,7 +68,7 @@ namespace hex::prv {
}
void Provider::write(u64 offset, const void *buffer, size_t size) {
if (!this->isWritable())
if (!this->isWritable() || ImHexApi::System::isReadOnlyMode())
return;
EventProviderDataModified::post(this, offset, size, static_cast<const u8*>(buffer));
@ -76,7 +76,7 @@ namespace hex::prv {
}
void Provider::save() {
if (!this->isWritable())
if (!this->isWritable() || ImHexApi::System::isReadOnlyMode())
return;
EventProviderSaved::post(this);
@ -100,6 +100,9 @@ namespace hex::prv {
}
bool Provider::resize(u64 newSize) {
if (ImHexApi::System::isReadOnlyMode()) {
return false;
}
if (newSize >> 63) {
log::error("new provider size is very large ({}). Is it a negative number ?", newSize);
return false;
@ -116,12 +119,14 @@ namespace hex::prv {
}
void Provider::insert(u64 offset, u64 size) {
if (ImHexApi::System::isReadOnlyMode()) return;
EventProviderDataInserted::post(this, offset, size);
this->markDirty();
}
void Provider::remove(u64 offset, u64 size) {
if (ImHexApi::System::isReadOnlyMode()) return;
EventProviderDataRemoved::post(this, offset, size);
this->markDirty();

View File

@ -71,7 +71,24 @@ namespace hex::init {
PluginManager::load(dir);
}
// Process the arguments
// Process our own global flags first
{
std::vector<std::string> remaining;
remaining.reserve(args.size());
for (size_t i = 0; i < args.size(); i += 1) {
const auto &arg = args[i];
if (arg == "--readonly" || arg == "-r") {
ImHexApi::System::impl::setReadOnlyMode(true);
continue;
}
remaining.push_back(arg);
}
args.swap(remaining);
}
// Process the arguments (subcommands) after handling globals
hex::subcommands::processArguments(args);
// Explicitly don't unload plugins again here.

View File

@ -84,7 +84,7 @@ namespace hex::plugin::builtin {
EventWindowClosing::subscribe([](GLFWwindow *window) {
imhexClosing = false;
if (ImHexApi::Provider::isDirty() && !imhexClosing) {
if (!ImHexApi::System::isReadOnlyMode() && ImHexApi::Provider::isDirty() && !imhexClosing) {
glfwSetWindowShouldClose(window, GLFW_FALSE);
ui::PopupQuestion::open("hex.builtin.popup.exit_application.desc"_lang,
[] {
@ -108,7 +108,7 @@ namespace hex::plugin::builtin {
EventCloseButtonPressed::subscribe([]() {
if (ImHexApi::Provider::isValid()) {
if (ImHexApi::Provider::isDirty()) {
if (!ImHexApi::System::isReadOnlyMode() && ImHexApi::Provider::isDirty()) {
ui::PopupQuestion::open("hex.builtin.popup.exit_application.desc"_lang,
[] {
for (const auto &provider : ImHexApi::Provider::getProviders())
@ -134,7 +134,7 @@ namespace hex::plugin::builtin {
});
EventProviderClosing::subscribe([](const prv::Provider *provider, bool *shouldClose) {
if (provider->isDirty()) {
if (!ImHexApi::System::isReadOnlyMode() && provider->isDirty()) {
*shouldClose = false;
PopupUnsavedChanges::open("hex.builtin.popup.close_provider.desc"_lang,
[]{

View File

@ -52,7 +52,7 @@ namespace hex::plugin::builtin {
}
bool noRunningTaskAndWritableProvider() {
return noRunningTasks() && ImHexApi::Provider::isValid() && ImHexApi::Provider::get()->isWritable();
return noRunningTasks() && !ImHexApi::System::isReadOnlyMode() && ImHexApi::Provider::isValid() && ImHexApi::Provider::get()->isWritable();
}
}

View File

@ -6,3 +6,4 @@ target_compile_definitions(tests_common PUBLIC IMHEX_PROJECT_NAME="${PROJECT_NAM
add_subdirectory(helpers)
add_subdirectory(algorithms)
add_subdirectory(plugins)
add_subdirectory(cli)

25
tests/cli/CMakeLists.txt Normal file
View File

@ -0,0 +1,25 @@
cmake_minimum_required(VERSION 3.16)
project(cli_test)
set(TEST_CATEGORY CLI)
set(AVAILABLE_TESTS
ReadOnlyFlagSetsMode
)
add_executable(${PROJECT_NAME}
source/test_cli_readonly.cpp
)
target_include_directories(${PROJECT_NAME} PRIVATE include)
target_link_libraries(${PROJECT_NAME} PRIVATE libimhex tests_common ${FMT_LIBRARIES})
set_target_properties(${PROJECT_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
foreach (test IN LISTS AVAILABLE_TESTS)
add_test(NAME "${TEST_CATEGORY}/${test}" COMMAND ${PROJECT_NAME} "${test}" WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
endforeach ()
add_dependencies(unit_tests ${PROJECT_NAME})

View File

@ -0,0 +1,33 @@
#include <hex/test/tests.hpp>
#include <hex/api/imhex_api/system.hpp>
// We only declare the CLI runner here; it's defined in the GUI main module
namespace hex::init {
void runCommandLine(int argc, char **argv);
}
static int runReadOnlyCLI(const std::vector<const char*> &args) {
int argc = static_cast<int>(args.size());
// const_cast is safe here because CLI layer doesn't mutate program name/args strings
auto argv = const_cast<char**>(reinterpret_cast<char* const*>(args.data()));
hex::init::runCommandLine(argc, argv);
return EXIT_SUCCESS;
}
TEST_SEQUENCE("ReadOnlyFlagSetsMode") {
// Simulate: imhex --readonly somefile
const char *prog = "imhex";
const char *flag = "--readonly";
const char *file = "dummy.bin";
const std::vector<const char*> argv = { prog, flag, file };
(void) runReadOnlyCLI(argv);
// Expect the global read-only mode to be enabled
TEST_ASSERT(hex::ImHexApi::System::isReadOnlyMode());
TEST_SUCCESS();
};