mirror of
https://github.com/WerWolv/ImHex.git
synced 2026-04-02 05:27:41 -05:00
feat: Added file information command line option and fullscreen view
This commit is contained in:
@@ -29,6 +29,7 @@
|
||||
|
||||
#include <content/providers/file_provider.hpp>
|
||||
#include <content/views/fullscreen/view_fullscreen_save_editor.hpp>
|
||||
#include <content/views/fullscreen/view_fullscreen_file_info.hpp>
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
using namespace hex::literals;
|
||||
@@ -521,6 +522,21 @@ namespace hex::plugin::builtin {
|
||||
}
|
||||
}
|
||||
|
||||
void handleFileInfoCommand(const std::vector<std::string> &args) {
|
||||
if (args.size() != 1) {
|
||||
log::println("usage: imhex --file-info <file>");
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
const auto path = std::fs::path(args[0]);
|
||||
if (!wolv::io::fs::exists(path)) {
|
||||
log::println("File '{}' does not exist!", args[0]);
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
ContentRegistry::Views::setFullScreenView<ViewFullScreenFileInfo>(path);
|
||||
}
|
||||
|
||||
|
||||
void registerCommandForwarders() {
|
||||
hex::subcommands::registerSubCommand("open", [](const std::vector<std::string> &args){
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
#include <content/views/fullscreen/view_fullscreen_file_info.hpp>
|
||||
#include <fonts/fonts.hpp>
|
||||
|
||||
#include <hex/api/content_registry/pattern_language.hpp>
|
||||
#include <hex/helpers/magic.hpp>
|
||||
#include <hex/helpers/literals.hpp>
|
||||
|
||||
#include <pl/pattern_language.hpp>
|
||||
#include <pl/core/evaluator.hpp>
|
||||
#include <toasts/toast_notification.hpp>
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
using namespace hex::literals;
|
||||
|
||||
ViewFullScreenFileInfo::ViewFullScreenFileInfo(std::fs::path filePath) : m_filePath(std::move(filePath)) {
|
||||
this->m_provider.setPath(m_filePath);
|
||||
if (!this->m_provider.open()) {
|
||||
ui::ToastError::open("hex.builtin.view.fullscreen.file_info.error.file_not_readable"_lang);
|
||||
}
|
||||
|
||||
m_analysisTask = TaskManager::createBlockingTask("hex.builtin.view.fullscreen.file_info.analyzing", TaskManager::NoProgress, [this] {
|
||||
m_mimeType = magic::getMIMEType(&m_provider);
|
||||
if (!magic::isValidMIMEType(m_mimeType)) {
|
||||
m_mimeType.clear();
|
||||
} else {
|
||||
m_fileDescription = magic::getDescription(&m_provider, 0, 100_KiB, true);
|
||||
}
|
||||
|
||||
m_foundPatterns = magic::findViablePatterns(&m_provider);
|
||||
if (!m_foundPatterns.empty()) {
|
||||
pl::PatternLanguage runtime;
|
||||
ContentRegistry::PatternLanguage::configureRuntime(runtime, &m_provider);
|
||||
|
||||
constexpr static auto DataDescriptionFunction = "get_data_description";
|
||||
if (runtime.executeFile(m_foundPatterns.front().patternFilePath)) {
|
||||
const auto &evaluator = runtime.getInternals().evaluator;
|
||||
const auto &functions = evaluator->getCustomFunctions();
|
||||
if (const auto function = functions.find(DataDescriptionFunction); function != functions.end()) {
|
||||
if (const auto value = function->second.func(evaluator.get(), {}); value.has_value()) {
|
||||
if (value->isString()) {
|
||||
m_fullDescription = ui::Markdown(value->toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
void ViewFullScreenFileInfo::drawContent() {
|
||||
if (!m_provider.isReadable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_analysisTask.isRunning()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bool foundPatterns = !m_foundPatterns.empty();
|
||||
const bool hasMimeType = !m_mimeType.empty();
|
||||
if (!foundPatterns && !hasMimeType) {
|
||||
ImGuiExt::TextFormattedCentered("hex.builtin.view.fullscreen.file_info.error.not_identified"_lang);
|
||||
return;
|
||||
}
|
||||
|
||||
if (foundPatterns) {
|
||||
const auto &firstMatch = m_foundPatterns.front();
|
||||
|
||||
ImGui::BeginGroup();
|
||||
|
||||
fonts::Default().pushBold(1.2F);
|
||||
ImGuiExt::TextFormattedCenteredHorizontal("{}", firstMatch.description);
|
||||
fonts::Default().pop();
|
||||
|
||||
if (hasMimeType)
|
||||
ImGuiExt::TextFormattedCenteredHorizontal("{}", m_mimeType);
|
||||
if (!m_fileDescription.empty())
|
||||
ImGuiExt::TextFormattedCenteredHorizontal("{}", m_fileDescription);
|
||||
|
||||
ImGui::EndGroup();
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::SetCursorPosX(ImGui::GetWindowSize().x - 300_scaled);
|
||||
|
||||
if (ImGuiExt::BeginSubWindow("hex.builtin.view.fullscreen.file_info.match_info"_lang)) {
|
||||
if (firstMatch.mimeType.has_value())
|
||||
ImGuiExt::TextFormattedWrapped("hex.builtin.view.fullscreen.file_info.match_info.mime"_lang);
|
||||
else if (firstMatch.magicOffset.has_value())
|
||||
ImGuiExt::TextFormattedWrapped("hex.builtin.view.fullscreen.file_info.match_info.magic"_lang, *firstMatch.magicOffset);
|
||||
}
|
||||
ImGuiExt::EndSubWindow();
|
||||
} else {
|
||||
fonts::Default().pushBold(1.2F);
|
||||
ImGuiExt::TextFormattedCenteredHorizontal("{}", m_mimeType);
|
||||
fonts::Default().pop();
|
||||
}
|
||||
|
||||
|
||||
ImGui::NewLine();
|
||||
|
||||
if (ImGuiExt::BeginSubWindow("hex.builtin.view.fullscreen.file_info.information"_lang, nullptr, ImGui::GetContentRegionAvail())) {
|
||||
if (m_fullDescription.has_value())
|
||||
m_fullDescription->draw();
|
||||
else
|
||||
ImGuiExt::TextFormattedCentered("hex.builtin.view.fullscreen.file_info.no_information"_lang);
|
||||
}
|
||||
ImGuiExt::EndSubWindow();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -70,7 +70,6 @@ namespace hex::plugin::builtin {
|
||||
if (!this->m_provider.open()) {
|
||||
ui::ToastError::open("The selected file could not be opened. Please ensure the file exists and is readable.");
|
||||
}
|
||||
this->m_provider.convertToMemoryFile();
|
||||
|
||||
ContentRegistry::PatternLanguage::configureRuntime(m_runtime, &m_provider);
|
||||
if (!m_runtime.executeString(this->m_sourceCode)) {
|
||||
|
||||
@@ -70,7 +70,7 @@ namespace hex::plugin::builtin {
|
||||
|
||||
if (ImGui::BeginListBox("##patterns_accept", ImVec2(400_scaled, 0))) {
|
||||
u32 index = 0;
|
||||
for (const auto &[path, author, description] : m_view->m_possiblePatternFiles.get(provider)) {
|
||||
for (const auto &[path, author, description, mimeType, magicOffset] : m_view->m_possiblePatternFiles.get(provider)) {
|
||||
ImGui::PushID(index + 1);
|
||||
auto fileName = wolv::util::toUTF8String(path.filename());
|
||||
|
||||
@@ -105,7 +105,7 @@ namespace hex::plugin::builtin {
|
||||
}
|
||||
|
||||
if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(0))
|
||||
m_view->loadPatternFile(m_view->m_possiblePatternFiles.get(provider)[m_selectedPatternFile].path, provider, false);
|
||||
m_view->loadPatternFile(m_view->m_possiblePatternFiles.get(provider)[m_selectedPatternFile].patternFilePath, provider, false);
|
||||
|
||||
ImGuiExt::InfoTooltip(wolv::util::toUTF8String(path).c_str());
|
||||
|
||||
@@ -127,7 +127,7 @@ namespace hex::plugin::builtin {
|
||||
|
||||
ImGuiExt::ConfirmButtons("hex.ui.common.yes"_lang, "hex.ui.common.no"_lang,
|
||||
[this, provider] {
|
||||
m_view->loadPatternFile(m_view->m_possiblePatternFiles.get(provider)[m_selectedPatternFile].path, provider, false);
|
||||
m_view->loadPatternFile(m_view->m_possiblePatternFiles.get(provider)[m_selectedPatternFile].patternFilePath, provider, false);
|
||||
this->close();
|
||||
},
|
||||
[this] {
|
||||
@@ -1495,129 +1495,17 @@ namespace hex::plugin::builtin {
|
||||
if (m_shouldAnalyze) {
|
||||
m_shouldAnalyze = false;
|
||||
|
||||
m_analysisTask = TaskManager::createBackgroundTask("hex.builtin.task.analyzing_data", [this, provider](const Task &task) {
|
||||
m_analysisTask = TaskManager::createBackgroundTask("hex.builtin.task.analyzing_data", [this, provider] {
|
||||
if (!m_autoLoadPatterns)
|
||||
return;
|
||||
|
||||
pl::PatternLanguage runtime;
|
||||
ContentRegistry::PatternLanguage::configureRuntime(runtime, provider);
|
||||
auto foundPatterns = magic::findViablePatterns(provider);
|
||||
|
||||
bool foundCorrectType = false;
|
||||
if (!foundPatterns.empty()) {
|
||||
std::scoped_lock lock(m_possiblePatternFilesMutex);
|
||||
|
||||
auto mimeType = magic::getMIMEType(provider, 0, 4_KiB, true);
|
||||
|
||||
m_possiblePatternFiles.get(provider).clear();
|
||||
|
||||
bool popupOpen = false;
|
||||
std::error_code errorCode;
|
||||
for (const auto &dir : paths::Patterns.read()) {
|
||||
for (auto &entry : std::fs::recursive_directory_iterator(dir, errorCode)) {
|
||||
task.update();
|
||||
|
||||
foundCorrectType = false;
|
||||
if (!entry.is_regular_file())
|
||||
continue;
|
||||
|
||||
wolv::io::File file(entry.path(), wolv::io::File::Mode::Read);
|
||||
if (!file.isValid())
|
||||
continue;
|
||||
|
||||
std::string author, description;
|
||||
|
||||
const auto pragmaValues = runtime.getPragmaValues(file.readString());
|
||||
if (auto it = pragmaValues.find("author"); it != pragmaValues.end())
|
||||
author = it->second;
|
||||
if (auto it = pragmaValues.find("description"); it != pragmaValues.end())
|
||||
description = it->second;
|
||||
|
||||
// Format: #pragma MIME type/subtype
|
||||
if (auto it = pragmaValues.find("MIME"); it != pragmaValues.end()) {
|
||||
if (magic::isValidMIMEType(it->second) && it->second == mimeType)
|
||||
foundCorrectType = true;
|
||||
}
|
||||
// Format: #pragma magic [ AA BB CC DD ] @ 0x12345678
|
||||
if (auto it = pragmaValues.find("magic"); it != pragmaValues.end()) {
|
||||
const auto pattern = [value = it->second]() mutable -> std::optional<BinaryPattern> {
|
||||
value = wolv::util::trim(value);
|
||||
|
||||
if (value.empty())
|
||||
return std::nullopt;
|
||||
|
||||
if (!value.starts_with('['))
|
||||
return std::nullopt;
|
||||
|
||||
value = value.substr(1);
|
||||
|
||||
const auto end = value.find(']');
|
||||
if (end == std::string::npos)
|
||||
return std::nullopt;
|
||||
value.resize(end);
|
||||
|
||||
value = wolv::util::trim(value);
|
||||
|
||||
return BinaryPattern(value);
|
||||
}();
|
||||
|
||||
const auto address = [value = it->second, provider]() mutable -> std::optional<u64> {
|
||||
value = wolv::util::trim(value);
|
||||
|
||||
if (value.empty())
|
||||
return std::nullopt;
|
||||
|
||||
const auto start = value.find('@');
|
||||
if (start == std::string::npos)
|
||||
return std::nullopt;
|
||||
|
||||
value = value.substr(start + 1);
|
||||
value = wolv::util::trim(value);
|
||||
|
||||
size_t end = 0;
|
||||
auto result = std::stoll(value, &end, 0);
|
||||
if (end != value.length())
|
||||
return std::nullopt;
|
||||
|
||||
if (result < 0) {
|
||||
const auto size = provider->getActualSize();
|
||||
if (u64(-result) > size) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return size + result;
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
}();
|
||||
|
||||
if (address && pattern) {
|
||||
std::vector<u8> bytes(pattern->getSize());
|
||||
if (!bytes.empty()) {
|
||||
provider->read(*address, bytes.data(), bytes.size());
|
||||
|
||||
if (pattern->matches(bytes))
|
||||
foundCorrectType = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (foundCorrectType) {
|
||||
{
|
||||
std::scoped_lock lock(m_possiblePatternFilesMutex);
|
||||
|
||||
m_possiblePatternFiles.get(provider).emplace_back(
|
||||
entry.path(),
|
||||
std::move(author),
|
||||
std::move(description)
|
||||
);
|
||||
}
|
||||
|
||||
if (!popupOpen) {
|
||||
PopupAcceptPattern::open(this);
|
||||
popupOpen = true;
|
||||
}
|
||||
}
|
||||
|
||||
runtime.reset();
|
||||
}
|
||||
m_possiblePatternFiles.get(provider) = std::move(foundPatterns);
|
||||
PopupAcceptPattern::open(this);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1959,6 +1847,9 @@ namespace hex::plugin::builtin {
|
||||
|
||||
RequestSetPatternLanguageCode::subscribe(this, [this](const std::string &code) {
|
||||
auto provider = ImHexApi::Provider::get();
|
||||
if (provider == nullptr)
|
||||
return;
|
||||
|
||||
m_textEditor.get(provider).setText(wolv::util::preprocessText(code));
|
||||
m_sourceCode.get(provider) = code;
|
||||
m_hasUnevaluatedChanges.get(provider) = true;
|
||||
@@ -2363,6 +2254,9 @@ namespace hex::plugin::builtin {
|
||||
ContentRegistry::FileTypeHandler::add({ ".hexpat", ".pat" }, [](const std::fs::path &path) -> bool {
|
||||
wolv::io::File file(path, wolv::io::File::Mode::Read);
|
||||
|
||||
if (!ImHexApi::Provider::isValid())
|
||||
return false;
|
||||
|
||||
if (file.isValid()) {
|
||||
RequestSetPatternLanguageCode::post(wolv::util::preprocessText(file.readString()));
|
||||
return true;
|
||||
|
||||
Reference in New Issue
Block a user