From c444ad928020f7b4c5ab5c6494ead2b4a4f199e5 Mon Sep 17 00:00:00 2001 From: WerWolv Date: Sun, 29 Oct 2023 19:43:40 +0100 Subject: [PATCH] impr: Bundle default magic file with application --- cmake/modules/ImHexPlugin.cmake | 8 ++- lib/external/libromfs | 2 +- .../include/hex/providers/provider_cache.hpp | 0 lib/libimhex/source/helpers/fs.cpp | 50 ++++++++++++++++--- lib/libimhex/source/helpers/magic.cpp | 18 +++++-- plugins/builtin/CMakeLists.txt | 6 +++ .../source/content/file_extraction.cpp | 26 ++++++++++ plugins/builtin/source/plugin_builtin.cpp | 4 ++ 8 files changed, 100 insertions(+), 14 deletions(-) delete mode 100644 lib/libimhex/include/hex/providers/provider_cache.hpp create mode 100644 plugins/builtin/source/content/file_extraction.cpp diff --git a/cmake/modules/ImHexPlugin.cmake b/cmake/modules/ImHexPlugin.cmake index 4e4051f2d..4244bd64f 100644 --- a/cmake/modules/ImHexPlugin.cmake +++ b/cmake/modules/ImHexPlugin.cmake @@ -45,7 +45,7 @@ macro(add_imhex_plugin) ) # Setup a romfs for the plugin - set(LIBROMFS_RESOURCE_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/romfs) + list(APPEND LIBROMFS_RESOURCE_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/romfs) set(LIBROMFS_PROJECT_NAME ${IMHEX_PLUGIN_NAME}) add_subdirectory(${IMHEX_BASE_FOLDER}/lib/external/libromfs ${CMAKE_CURRENT_BINARY_DIR}/libromfs) set_target_properties(${LIBROMFS_LIBRARY} PROPERTIES POSITION_INDEPENDENT_CODE ON) @@ -54,3 +54,9 @@ macro(add_imhex_plugin) # Add the new plugin to the main dependency list so it gets built by default add_dependencies(imhex_all ${IMHEX_PLUGIN_NAME}) endmacro() + +macro(add_romfs_resource input output) + configure_file(${input} ${CMAKE_CURRENT_BINARY_DIR}/romfs/${output} COPYONLY) + + list(APPEND LIBROMFS_RESOURCE_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/romfs) +endmacro() \ No newline at end of file diff --git a/lib/external/libromfs b/lib/external/libromfs index 03365d8c5..fd79e8689 160000 --- a/lib/external/libromfs +++ b/lib/external/libromfs @@ -1 +1 @@ -Subproject commit 03365d8c5a43baa22cc6bbd5725db15a3038d035 +Subproject commit fd79e86893e1b71b8d53cd59310a95748388555b diff --git a/lib/libimhex/include/hex/providers/provider_cache.hpp b/lib/libimhex/include/hex/providers/provider_cache.hpp deleted file mode 100644 index e69de29bb..000000000 diff --git a/lib/libimhex/source/helpers/fs.cpp b/lib/libimhex/source/helpers/fs.cpp index 736f0866f..a29900436 100644 --- a/lib/libimhex/source/helpers/fs.cpp +++ b/lib/libimhex/source/helpers/fs.cpp @@ -38,8 +38,8 @@ namespace hex::fs { } // With help from https://github.com/owncloud/client/blob/cba22aa34b3677406e0499aadd126ce1d94637a2/src/gui/openfilemanager.cpp - void openFileExternal(const std::fs::path &filePath) { + // Make sure the file exists before trying to open it if (!wolv::io::fs::exists(filePath)) return; @@ -57,6 +57,7 @@ namespace hex::fs { } void openFolderExternal(const std::fs::path &dirPath) { + // Make sure the folder exists before trying to open it if (!wolv::io::fs::exists(dirPath)) return; @@ -74,6 +75,7 @@ namespace hex::fs { } void openFolderWithSelectionExternal(const std::fs::path &selectedFilePath) { + // Make sure the file exists before trying to open it if (!wolv::io::fs::exists(selectedFilePath)) return; @@ -198,13 +200,18 @@ namespace hex::fs { #else bool openFileBrowser(DialogMode mode, const std::vector &validExtensions, const std::function &callback, const std::string &defaultPath, bool multiple) { + // Turn the content of the ItemFilter objects into something NFD understands std::vector validExtensionsNfd; for (const auto &extension : validExtensions) { validExtensionsNfd.emplace_back(nfdfilteritem_t{ extension.name.c_str(), extension.spec.c_str() }); } + + // Clear errors from previous runs NFD::ClearError(); + // Try to initialize NFD if (NFD::Init() != NFD_OKAY) { + // Handle errors if initialization failed log::error("NFD init returned an error: {}", NFD::GetError()); if (s_fileBrowserErrorCallback != nullptr) { auto error = NFD::GetError(); @@ -216,7 +223,9 @@ namespace hex::fs { NFD::UniquePathU8 outPath; NFD::UniquePathSet outPaths; - nfdresult_t result; + nfdresult_t result = NFD_ERROR; + + // Open the correct file dialog based on the mode switch (mode) { case DialogMode::Open: if (multiple) @@ -230,17 +239,20 @@ namespace hex::fs { case DialogMode::Folder: result = NFD::PickFolder(outPath, defaultPath.empty() ? nullptr : defaultPath.c_str()); break; - default: - std::unreachable(); } if (result == NFD_OKAY){ - if(outPath != nullptr) { + // Handle the path if the dialog was opened in single mode + if (outPath != nullptr) { + // Call the provided callback with the path callback(reinterpret_cast(outPath.get())); } + + // Handle multiple paths if the dialog was opened in multiple mode if (outPaths != nullptr) { nfdpathsetsize_t numPaths = 0; if (NFD::PathSet::Count(outPaths, numPaths) == NFD_OKAY) { + // Loop over all returned paths and call the callback with each of them for (size_t i = 0; i < numPaths; i++) { NFD::UniquePathSetPath path; if (NFD::PathSet::GetPath(outPaths, i, path) == NFD_OKAY) @@ -249,9 +261,14 @@ namespace hex::fs { } } } else if (result == NFD_ERROR) { + // Handle errors that occurred during the file dialog call + log::error("Requested file dialog returned an error: {}", NFD::GetError()); - if (s_fileBrowserErrorCallback != nullptr) - s_fileBrowserErrorCallback(NFD::GetError() ? NFD::GetError() : "No details"); + + if (s_fileBrowserErrorCallback != nullptr) { + auto error = NFD::GetError(); + s_fileBrowserErrorCallback(error != nullptr ? error : "No details"); + } } NFD::Quit(); @@ -266,6 +283,8 @@ namespace hex::fs { #if defined(OS_WINDOWS) + // In the portable Windows version, we just use the executable directory + // Prevent the use of the AppData folder here if (!ImHexApi::System::isPortableVersion()) { PWSTR wAppDataPath = nullptr; if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_CREATE, nullptr, &wAppDataPath))) { @@ -303,9 +322,11 @@ namespace hex::fs { #endif + // Add additional data directories to the path auto additionalDirs = ImHexApi::System::getAdditionalFolderPaths(); std::copy(additionalDirs.begin(), additionalDirs.end(), std::back_inserter(paths)); + // Add the project file directory to the path, if one is loaded if (ProjectFile::hasPath()) { paths.push_back(ProjectFile::getPath().parent_path()); } @@ -332,9 +353,12 @@ namespace hex::fs { std::vector getPluginPaths() { std::vector paths = getDataPaths(); + + // Add the system plugin directory to the path if one was provided at compile time #if defined(OS_LINUX) && defined(SYSTEM_PLUGINS_LOCATION) - paths.push_back(SYSTEM_PLUGINS_LOCATION); + paths.push_back(SYSTEM_PLUGINS_LOCATION); #endif + return paths; } @@ -342,6 +366,7 @@ namespace hex::fs { std::vector getDefaultPaths(ImHexPath path, bool listNonExisting) { std::vector result; + // Return the correct path based on the ImHexPath enum switch (path) { case ImHexPath::END: return { }; @@ -398,6 +423,7 @@ namespace hex::fs { break; } + // Remove all paths that don't exist if requested if (!listNonExisting) { result.erase(std::remove_if(result.begin(), result.end(), [](const auto &path) { return !wolv::io::fs::isDirectory(path); @@ -409,6 +435,9 @@ namespace hex::fs { bool isPathWritable(const std::fs::path &path) { constexpr static auto TestFileName = "__imhex__tmp__"; + + // Try to open the __imhex__tmp__ file in the given path + // If one does exist already, try to delete it { wolv::io::File file(path / TestFileName, wolv::io::File::Mode::Read); if (file.isValid()) { @@ -417,6 +446,8 @@ namespace hex::fs { } } + // Try to create a new file in the given path + // If that fails, or the file cannot be deleted anymore afterward; the path is not writable wolv::io::File file(path / TestFileName, wolv::io::File::Mode::Create); bool result = file.isValid(); if (!file.remove()) @@ -427,16 +458,19 @@ namespace hex::fs { std::fs::path toShortPath(const std::fs::path &path) { #if defined(OS_WINDOWS) + // Get the size of the short path size_t size = GetShortPathNameW(path.c_str(), nullptr, 0); if (size == 0) return path; + // Get the short path std::wstring newPath(size, 0x00); GetShortPathNameW(path.c_str(), newPath.data(), newPath.size()); newPath.pop_back(); return newPath; #else + // Other supported platforms don't have short paths return path; #endif } diff --git a/lib/libimhex/source/helpers/magic.cpp b/lib/libimhex/source/helpers/magic.cpp index 26c0396a6..c953ff469 100644 --- a/lib/libimhex/source/helpers/magic.cpp +++ b/lib/libimhex/source/helpers/magic.cpp @@ -30,15 +30,22 @@ namespace hex::magic { std::error_code error; for (const auto &dir : fs::getDefaultPaths(fs::ImHexPath::Magic)) { - for (const auto &entry : std::fs::recursive_directory_iterator(dir, error)) { + for (const auto &entry : std::fs::directory_iterator(dir, error)) { auto path = std::fs::absolute(entry.path()); - if (entry.is_regular_file() && ((sourceFiles && path.extension().empty()) || (!sourceFiles && path.extension() == ".mgc"))) { - magicFiles += wolv::util::toUTF8String(wolv::io::fs::toShortPath(path)) + MAGIC_PATH_SEPARATOR; + if (sourceFiles) { + if (path.extension().empty() || entry.is_directory()) + magicFiles += wolv::util::toUTF8String(path) + MAGIC_PATH_SEPARATOR; + } else { + if (path.extension() == ".mgc") + magicFiles += wolv::util::toUTF8String(path) + MAGIC_PATH_SEPARATOR; } } } + if (!magicFiles.empty()) + magicFiles.pop_back(); + if (error) return std::nullopt; else @@ -46,7 +53,7 @@ namespace hex::magic { } bool compile() { - magic_t ctx = magic_open(MAGIC_NONE); + magic_t ctx = magic_open(MAGIC_CHECK); ON_SCOPE_EXIT { magic_close(ctx); }; auto magicFiles = getMagicFiles(true); @@ -75,6 +82,9 @@ namespace hex::magic { return false; auto result = magic_compile(ctx, magicFiles->c_str()) == 0; + if (!result) { + log::error("Failed to compile magic files \"{}\": {}", *magicFiles, magic_error(ctx)); + } if (chdir(cwd.data()) != 0) return false; diff --git a/plugins/builtin/CMakeLists.txt b/plugins/builtin/CMakeLists.txt index cf66c9570..a654a2d19 100644 --- a/plugins/builtin/CMakeLists.txt +++ b/plugins/builtin/CMakeLists.txt @@ -2,6 +2,11 @@ cmake_minimum_required(VERSION 3.16) include(ImHexPlugin) +find_file(DEFAULT_MAGIC_FILE_PATH magic.mgc HINTS ${MAGIC_INCLUDE_DIRS}/../share/misc) +if (DEFAULT_MAGIC_FILE_PATH) + add_romfs_resource(${DEFAULT_MAGIC_FILE_PATH} auto_extract/magic/magic.mgc) +endif () + add_imhex_plugin( NAME builtin @@ -36,6 +41,7 @@ add_imhex_plugin( source/content/file_handlers.cpp source/content/project.cpp source/content/achievements.cpp + source/content/file_extraction.cpp source/content/providers/file_provider.cpp source/content/providers/gdb_provider.cpp diff --git a/plugins/builtin/source/content/file_extraction.cpp b/plugins/builtin/source/content/file_extraction.cpp new file mode 100644 index 000000000..7fd628710 --- /dev/null +++ b/plugins/builtin/source/content/file_extraction.cpp @@ -0,0 +1,26 @@ +#include + +#include +#include + +namespace hex::plugin::builtin { + + void extractBundledFiles() { + for (const auto &romfsPath : romfs::list("auto_extract")) { + for (const auto &imhexPath : fs::getDataPaths()) { + wolv::io::File file(imhexPath, wolv::io::File::Mode::Create); + + if (!file.isValid()) + continue; + + auto data = romfs::get(romfsPath).span(); + file.writeBuffer(data.data(), data.size()); + + if (file.getSize() == data.size()) + break; + } + + } + } + +} \ No newline at end of file diff --git a/plugins/builtin/source/plugin_builtin.cpp b/plugins/builtin/source/plugin_builtin.cpp index e604cfdd7..ebaca528e 100644 --- a/plugins/builtin/source/plugin_builtin.cpp +++ b/plugins/builtin/source/plugin_builtin.cpp @@ -47,6 +47,8 @@ namespace hex::plugin::builtin { void handleBorderlessWindowMode(); + void extractBundledFiles(); + } IMHEX_PLUGIN_SUBCOMMANDS() { @@ -107,6 +109,8 @@ IMHEX_PLUGIN_SETUP("Built-in", "WerWolv", "Default ImHex functionality") { registerMainMenuEntries(); handleBorderlessWindowMode(); + + extractBundledFiles(); } // This is the default plugin