feat: Added Intel Hex and Motorola SREC provider (#670)
* feat: Initial implementation of an Intel Hex provider * fix: Reading of bytes from intel hex files * lang: Added localization for new provider * ui: Only show file name in intel hex provider name * feat: Added Motorola SREC provider
This commit is contained in:
parent
72c4f50871
commit
b580691871
9
lib/external/intervaltree/CMakeLists.txt
vendored
Normal file
9
lib/external/intervaltree/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
project(intervaltree)
|
||||
|
||||
set(CMAKE_CXX_STANDARD20)
|
||||
|
||||
add_library(intervaltree INTERFACE)
|
||||
|
||||
target_include_directories(intervaltree INTERFACE include)
|
||||
target_compile_options(intervaltree INTERFACE "-DUSE_INTERVAL_TREE_NAMESPACE")
|
19
lib/external/intervaltree/LICENSE
vendored
Normal file
19
lib/external/intervaltree/LICENSE
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
Copyright (c) 2011 Erik Garrison
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
37
lib/external/intervaltree/README.md
vendored
Normal file
37
lib/external/intervaltree/README.md
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
# intervaltree
|
||||
|
||||
## Overview
|
||||
|
||||
An interval tree can be used to efficiently find a set of numeric intervals overlapping or containing another interval.
|
||||
|
||||
This library provides a basic implementation of an interval tree using C++ templates, allowing the insertion of arbitrary types into the tree.
|
||||
|
||||
## Usage
|
||||
|
||||
Add `#include "IntervalTree.h"` to the source files in which you will use the interval tree.
|
||||
|
||||
To make an IntervalTree to contain objects of class T, use:
|
||||
|
||||
```c++
|
||||
vector<Interval<T> > intervals;
|
||||
T a, b, c;
|
||||
intervals.push_back(Interval<T>(2, 10, a));
|
||||
intervals.push_back(Interval<T>(3, 4, b));
|
||||
intervals.push_back(Interval<T>(20, 100, c));
|
||||
IntervalTree<T> tree;
|
||||
tree = IntervalTree<T>(intervals);
|
||||
```
|
||||
|
||||
Now, it's possible to query the tree and obtain a set of intervals which are contained within the start and stop coordinates.
|
||||
|
||||
```c++
|
||||
vector<Interval<T> > results;
|
||||
tree.findContained(start, stop, results);
|
||||
cout << "found " << results.size() << " overlapping intervals" << endl;
|
||||
```
|
||||
|
||||
The function IntervalTree::findOverlapping provides a method to find all those intervals which are contained or partially overlap the interval (start, stop).
|
||||
|
||||
### Author: Erik Garrison <erik.garrison@gmail.com>
|
||||
|
||||
### License: MIT
|
325
lib/external/intervaltree/include/IntervalTree.h
vendored
Normal file
325
lib/external/intervaltree/include/IntervalTree.h
vendored
Normal file
@ -0,0 +1,325 @@
|
||||
#ifndef __INTERVAL_TREE_H
|
||||
#define __INTERVAL_TREE_H
|
||||
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <cassert>
|
||||
#include <limits>
|
||||
|
||||
#ifdef USE_INTERVAL_TREE_NAMESPACE
|
||||
namespace interval_tree {
|
||||
#endif
|
||||
template <class Scalar, typename Value>
|
||||
class Interval {
|
||||
public:
|
||||
Scalar start;
|
||||
Scalar stop;
|
||||
Value value;
|
||||
Interval(const Scalar& s, const Scalar& e, const Value& v)
|
||||
: start(std::min(s, e))
|
||||
, stop(std::max(s, e))
|
||||
, value(v)
|
||||
{}
|
||||
};
|
||||
|
||||
template <class Scalar, typename Value>
|
||||
Value intervalStart(const Interval<Scalar,Value>& i) {
|
||||
return i.start;
|
||||
}
|
||||
|
||||
template <class Scalar, typename Value>
|
||||
Value intervalStop(const Interval<Scalar, Value>& i) {
|
||||
return i.stop;
|
||||
}
|
||||
|
||||
template <class Scalar, typename Value>
|
||||
std::ostream& operator<<(std::ostream& out, const Interval<Scalar, Value>& i) {
|
||||
out << "Interval(" << i.start << ", " << i.stop << "): " << i.value;
|
||||
return out;
|
||||
}
|
||||
|
||||
template <class Scalar, class Value>
|
||||
class IntervalTree {
|
||||
public:
|
||||
typedef Interval<Scalar, Value> interval;
|
||||
typedef std::vector<interval> interval_vector;
|
||||
|
||||
|
||||
struct IntervalStartCmp {
|
||||
bool operator()(const interval& a, const interval& b) {
|
||||
return a.start < b.start;
|
||||
}
|
||||
};
|
||||
|
||||
struct IntervalStopCmp {
|
||||
bool operator()(const interval& a, const interval& b) {
|
||||
return a.stop < b.stop;
|
||||
}
|
||||
};
|
||||
|
||||
IntervalTree()
|
||||
: left(nullptr)
|
||||
, right(nullptr)
|
||||
, center(0)
|
||||
{}
|
||||
|
||||
~IntervalTree() = default;
|
||||
|
||||
std::unique_ptr<IntervalTree> clone() const {
|
||||
return std::unique_ptr<IntervalTree>(new IntervalTree(*this));
|
||||
}
|
||||
|
||||
IntervalTree(const IntervalTree& other)
|
||||
: intervals(other.intervals),
|
||||
left(other.left ? other.left->clone() : nullptr),
|
||||
right(other.right ? other.right->clone() : nullptr),
|
||||
center(other.center)
|
||||
{}
|
||||
|
||||
IntervalTree& operator=(IntervalTree&&) = default;
|
||||
IntervalTree(IntervalTree&&) = default;
|
||||
|
||||
IntervalTree& operator=(const IntervalTree& other) {
|
||||
center = other.center;
|
||||
intervals = other.intervals;
|
||||
left = other.left ? other.left->clone() : nullptr;
|
||||
right = other.right ? other.right->clone() : nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
IntervalTree(
|
||||
interval_vector&& ivals,
|
||||
std::size_t depth = 16,
|
||||
std::size_t minbucket = 64,
|
||||
std::size_t maxbucket = 512,
|
||||
Scalar leftextent = 0,
|
||||
Scalar rightextent = 0)
|
||||
: left(nullptr)
|
||||
, right(nullptr)
|
||||
{
|
||||
--depth;
|
||||
const auto minmaxStop = std::minmax_element(ivals.begin(), ivals.end(),
|
||||
IntervalStopCmp());
|
||||
const auto minmaxStart = std::minmax_element(ivals.begin(), ivals.end(),
|
||||
IntervalStartCmp());
|
||||
if (!ivals.empty()) {
|
||||
center = (minmaxStart.first->start + minmaxStop.second->stop) / 2;
|
||||
}
|
||||
if (leftextent == 0 && rightextent == 0) {
|
||||
// sort intervals by start
|
||||
std::sort(ivals.begin(), ivals.end(), IntervalStartCmp());
|
||||
} else {
|
||||
assert(std::is_sorted(ivals.begin(), ivals.end(), IntervalStartCmp()));
|
||||
}
|
||||
if (depth == 0 || (ivals.size() < minbucket && ivals.size() < maxbucket)) {
|
||||
std::sort(ivals.begin(), ivals.end(), IntervalStartCmp());
|
||||
intervals = std::move(ivals);
|
||||
assert(is_valid().first);
|
||||
return;
|
||||
} else {
|
||||
Scalar leftp = 0;
|
||||
Scalar rightp = 0;
|
||||
|
||||
if (leftextent || rightextent) {
|
||||
leftp = leftextent;
|
||||
rightp = rightextent;
|
||||
} else {
|
||||
leftp = ivals.front().start;
|
||||
rightp = std::max_element(ivals.begin(), ivals.end(),
|
||||
IntervalStopCmp())->stop;
|
||||
}
|
||||
|
||||
interval_vector lefts;
|
||||
interval_vector rights;
|
||||
|
||||
for (typename interval_vector::const_iterator i = ivals.begin();
|
||||
i != ivals.end(); ++i) {
|
||||
const interval& interval = *i;
|
||||
if (interval.stop < center) {
|
||||
lefts.push_back(interval);
|
||||
} else if (interval.start > center) {
|
||||
rights.push_back(interval);
|
||||
} else {
|
||||
assert(interval.start <= center);
|
||||
assert(center <= interval.stop);
|
||||
intervals.push_back(interval);
|
||||
}
|
||||
}
|
||||
|
||||
if (!lefts.empty()) {
|
||||
left.reset(new IntervalTree(std::move(lefts),
|
||||
depth, minbucket, maxbucket,
|
||||
leftp, center));
|
||||
}
|
||||
if (!rights.empty()) {
|
||||
right.reset(new IntervalTree(std::move(rights),
|
||||
depth, minbucket, maxbucket,
|
||||
center, rightp));
|
||||
}
|
||||
}
|
||||
assert(is_valid().first);
|
||||
}
|
||||
|
||||
// Call f on all intervals near the range [start, stop]:
|
||||
template <class UnaryFunction>
|
||||
void visit_near(const Scalar& start, const Scalar& stop, UnaryFunction f) const {
|
||||
if (!intervals.empty() && ! (stop < intervals.front().start)) {
|
||||
for (auto & i : intervals) {
|
||||
f(i);
|
||||
}
|
||||
}
|
||||
if (left && start <= center) {
|
||||
left->visit_near(start, stop, f);
|
||||
}
|
||||
if (right && stop >= center) {
|
||||
right->visit_near(start, stop, f);
|
||||
}
|
||||
}
|
||||
|
||||
// Call f on all intervals crossing pos
|
||||
template <class UnaryFunction>
|
||||
void visit_overlapping(const Scalar& pos, UnaryFunction f) const {
|
||||
visit_overlapping(pos, pos, f);
|
||||
}
|
||||
|
||||
// Call f on all intervals overlapping [start, stop]
|
||||
template <class UnaryFunction>
|
||||
void visit_overlapping(const Scalar& start, const Scalar& stop, UnaryFunction f) const {
|
||||
auto filterF = [&](const interval& interval) {
|
||||
if (interval.stop >= start && interval.start <= stop) {
|
||||
// Only apply f if overlapping
|
||||
f(interval);
|
||||
}
|
||||
};
|
||||
visit_near(start, stop, filterF);
|
||||
}
|
||||
|
||||
// Call f on all intervals contained within [start, stop]
|
||||
template <class UnaryFunction>
|
||||
void visit_contained(const Scalar& start, const Scalar& stop, UnaryFunction f) const {
|
||||
auto filterF = [&](const interval& interval) {
|
||||
if (start <= interval.start && interval.stop <= stop) {
|
||||
f(interval);
|
||||
}
|
||||
};
|
||||
visit_near(start, stop, filterF);
|
||||
}
|
||||
|
||||
interval_vector findOverlapping(const Scalar& start, const Scalar& stop) const {
|
||||
interval_vector result;
|
||||
visit_overlapping(start, stop,
|
||||
[&](const interval& interval) {
|
||||
result.emplace_back(interval);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
interval_vector findContained(const Scalar& start, const Scalar& stop) const {
|
||||
interval_vector result;
|
||||
visit_contained(start, stop,
|
||||
[&](const interval& interval) {
|
||||
result.push_back(interval);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
bool empty() const {
|
||||
if (left && !left->empty()) {
|
||||
return false;
|
||||
}
|
||||
if (!intervals.empty()) {
|
||||
return false;
|
||||
}
|
||||
if (right && !right->empty()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class UnaryFunction>
|
||||
void visit_all(UnaryFunction f) const {
|
||||
if (left) {
|
||||
left->visit_all(f);
|
||||
}
|
||||
std::for_each(intervals.begin(), intervals.end(), f);
|
||||
if (right) {
|
||||
right->visit_all(f);
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<Scalar, Scalar> extentBruitForce() const {
|
||||
struct Extent {
|
||||
std::pair<Scalar, Scalar> x = {std::numeric_limits<Scalar>::max(),
|
||||
std::numeric_limits<Scalar>::min() };
|
||||
void operator()(const interval & interval) {
|
||||
x.first = std::min(x.first, interval.start);
|
||||
x.second = std::max(x.second, interval.stop);
|
||||
}
|
||||
};
|
||||
Extent extent;
|
||||
|
||||
visit_all([&](const interval & interval) { extent(interval); });
|
||||
return extent.x;
|
||||
}
|
||||
|
||||
// Check all constraints.
|
||||
// If first is false, second is invalid.
|
||||
std::pair<bool, std::pair<Scalar, Scalar>> is_valid() const {
|
||||
const auto minmaxStop = std::minmax_element(intervals.begin(), intervals.end(),
|
||||
IntervalStopCmp());
|
||||
const auto minmaxStart = std::minmax_element(intervals.begin(), intervals.end(),
|
||||
IntervalStartCmp());
|
||||
|
||||
std::pair<bool, std::pair<Scalar, Scalar>> result = {true, { std::numeric_limits<Scalar>::max(),
|
||||
std::numeric_limits<Scalar>::min() }};
|
||||
if (!intervals.empty()) {
|
||||
result.second.first = std::min(result.second.first, minmaxStart.first->start);
|
||||
result.second.second = std::min(result.second.second, minmaxStop.second->stop);
|
||||
}
|
||||
if (left) {
|
||||
auto valid = left->is_valid();
|
||||
result.first &= valid.first;
|
||||
result.second.first = std::min(result.second.first, valid.second.first);
|
||||
result.second.second = std::min(result.second.second, valid.second.second);
|
||||
if (!result.first) { return result; }
|
||||
if (valid.second.second >= center) {
|
||||
result.first = false;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
if (right) {
|
||||
auto valid = right->is_valid();
|
||||
result.first &= valid.first;
|
||||
result.second.first = std::min(result.second.first, valid.second.first);
|
||||
result.second.second = std::min(result.second.second, valid.second.second);
|
||||
if (!result.first) { return result; }
|
||||
if (valid.second.first <= center) {
|
||||
result.first = false;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
if (!std::is_sorted(intervals.begin(), intervals.end(), IntervalStartCmp())) {
|
||||
result.first = false;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
left.reset();
|
||||
right.reset();
|
||||
intervals.clear();
|
||||
center = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
interval_vector intervals;
|
||||
std::unique_ptr<IntervalTree> left;
|
||||
std::unique_ptr<IntervalTree> right;
|
||||
Scalar center;
|
||||
};
|
||||
#ifdef USE_INTERVAL_TREE_NAMESPACE
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
2
lib/external/pattern_language
vendored
2
lib/external/pattern_language
vendored
@ -1 +1 @@
|
||||
Subproject commit 15f42da03ff501d5e1c66794a508ef9a452236e3
|
||||
Subproject commit 7285b76f8284ea1c6e145ac0e664d8f3e5f537b9
|
@ -18,6 +18,9 @@ set(LIBROMFS_RESOURCE_LOCATION ${IMHEX_BASE_FOLDER}/resources/romfs)
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../external/libromfs ${CMAKE_CURRENT_BINARY_DIR}/external/libromfs EXCLUDE_FROM_ALL)
|
||||
set_target_properties(libromfs PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../external/intervaltree ${CMAKE_CURRENT_BINARY_DIR}/external/intervaltree EXCLUDE_FROM_ALL)
|
||||
set_target_properties(intervaltree PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
set(XDGPP_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/../external/xdgpp")
|
||||
set(CURL_USE_MBEDTLS ON)
|
||||
set(BUILD_CURL_EXE OFF)
|
||||
@ -163,4 +166,4 @@ if (APPLE)
|
||||
target_link_libraries(libimhex PUBLIC ${FOUNDATION})
|
||||
endif ()
|
||||
|
||||
target_link_libraries(libimhex PUBLIC dl imgui nfd magic ${CAPSTONE_LIBRARIES} LLVMDemangle microtar ${NLOHMANN_JSON_LIBRARIES} ${YARA_LIBRARIES} ${LIBCURL_LIBRARIES} ${MBEDTLS_LIBRARIES} ${FMT_LIBRARIES} ${Python_LIBRARIES} libromfs libpl)
|
||||
target_link_libraries(libimhex PUBLIC dl imgui nfd magic ${CAPSTONE_LIBRARIES} LLVMDemangle microtar ${NLOHMANN_JSON_LIBRARIES} ${YARA_LIBRARIES} ${LIBCURL_LIBRARIES} ${MBEDTLS_LIBRARIES} ${FMT_LIBRARIES} ${Python_LIBRARIES} libromfs libpl intervaltree)
|
||||
|
@ -82,6 +82,9 @@ namespace hex::prv {
|
||||
[[nodiscard]] bool canUndo() const;
|
||||
[[nodiscard]] bool canRedo() const;
|
||||
|
||||
[[nodiscard]] virtual bool hasFilePicker() const;
|
||||
virtual bool handleFilePicker();
|
||||
|
||||
[[nodiscard]] virtual bool hasLoadInterface() const;
|
||||
[[nodiscard]] virtual bool hasInterface() const;
|
||||
virtual void drawLoadInterface();
|
||||
|
@ -19,6 +19,7 @@ namespace hex::prv {
|
||||
Provider::~Provider() {
|
||||
for (auto &overlay : this->m_overlays)
|
||||
this->deleteOverlay(overlay);
|
||||
this->close();
|
||||
}
|
||||
|
||||
void Provider::read(u64 offset, void *buffer, size_t size, bool overlays) {
|
||||
@ -227,6 +228,13 @@ namespace hex::prv {
|
||||
return this->m_patchTreeOffset > 0;
|
||||
}
|
||||
|
||||
bool Provider::hasFilePicker() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Provider::handleFilePicker() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Provider::hasLoadInterface() const {
|
||||
return false;
|
||||
|
@ -22,6 +22,8 @@
|
||||
#include <dwmapi.h>
|
||||
#include <windowsx.h>
|
||||
|
||||
#include <csignal>
|
||||
|
||||
#include <imgui_impl_glfw.h>
|
||||
|
||||
namespace hex {
|
||||
@ -263,6 +265,21 @@ namespace hex {
|
||||
::SetWindowPos(hwnd, nullptr, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE);
|
||||
::SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) | WS_OVERLAPPEDWINDOW);
|
||||
}
|
||||
|
||||
// Catch heap corruption
|
||||
{
|
||||
::AddVectoredExceptionHandler(TRUE, [](PEXCEPTION_POINTERS exception) -> LONG {
|
||||
if ((exception->ExceptionRecord->ExceptionCode & 0xF000'0000) == 0xC000'0000) {
|
||||
log::fatal("Exception raised: 0x{:08X}", exception->ExceptionRecord->ExceptionCode);
|
||||
if (exception->ExceptionRecord->ExceptionCode == STATUS_HEAP_CORRUPTION) {
|
||||
log::fatal("Heap corruption detected!");
|
||||
std::raise(SIGABRT);
|
||||
}
|
||||
}
|
||||
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void Window::beginNativeWindowFrame() {
|
||||
|
@ -28,6 +28,8 @@ add_library(${PROJECT_NAME} SHARED
|
||||
source/content/providers/file_provider.cpp
|
||||
source/content/providers/gdb_provider.cpp
|
||||
source/content/providers/disk_provider.cpp
|
||||
source/content/providers/intel_hex_provider.cpp
|
||||
source/content/providers/motorola_srec_provider.cpp
|
||||
|
||||
source/content/views/view_hex_editor.cpp
|
||||
source/content/views/view_pattern_editor.cpp
|
||||
|
@ -15,7 +15,7 @@ namespace hex::plugin::builtin::prv {
|
||||
class DiskProvider : public hex::prv::Provider {
|
||||
public:
|
||||
DiskProvider();
|
||||
~DiskProvider() override;
|
||||
~DiskProvider() override = default;
|
||||
|
||||
[[nodiscard]] bool isAvailable() const override;
|
||||
[[nodiscard]] bool isReadable() const override;
|
||||
|
@ -22,8 +22,8 @@ namespace hex::plugin::builtin::prv {
|
||||
|
||||
class FileProvider : public hex::prv::Provider {
|
||||
public:
|
||||
explicit FileProvider();
|
||||
~FileProvider() override;
|
||||
FileProvider() = default;;
|
||||
~FileProvider() override = default;;
|
||||
|
||||
[[nodiscard]] bool isAvailable() const override;
|
||||
[[nodiscard]] bool isReadable() const override;
|
||||
@ -49,6 +49,9 @@ namespace hex::plugin::builtin::prv {
|
||||
[[nodiscard]] std::string getName() const override;
|
||||
[[nodiscard]] std::vector<std::pair<std::string, std::string>> getDataInformation() const override;
|
||||
|
||||
bool hasFilePicker() const override { return true; }
|
||||
bool handleFilePicker() override;
|
||||
|
||||
void setPath(const std::fs::path &path);
|
||||
|
||||
[[nodiscard]] bool open() override;
|
||||
|
@ -13,7 +13,7 @@ namespace hex::plugin::builtin::prv {
|
||||
class GDBProvider : public hex::prv::Provider {
|
||||
public:
|
||||
GDBProvider();
|
||||
~GDBProvider() override;
|
||||
~GDBProvider() override = default;;
|
||||
|
||||
[[nodiscard]] bool isAvailable() const override;
|
||||
[[nodiscard]] bool isReadable() const override;
|
||||
|
@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/providers/provider.hpp>
|
||||
|
||||
#include <IntervalTree.h>
|
||||
|
||||
namespace hex::plugin::builtin::prv {
|
||||
|
||||
class IntelHexProvider : public hex::prv::Provider {
|
||||
public:
|
||||
IntelHexProvider() = default;
|
||||
~IntelHexProvider() override = default;
|
||||
|
||||
[[nodiscard]] bool isAvailable() const override { return this->m_dataValid; }
|
||||
[[nodiscard]] bool isReadable() const override { return true; }
|
||||
[[nodiscard]] bool isWritable() const override { return false; }
|
||||
[[nodiscard]] bool isResizable() const override { return false; }
|
||||
[[nodiscard]] bool isSavable() const override { return false; }
|
||||
|
||||
void readRaw(u64 offset, void *buffer, size_t size) override;
|
||||
void writeRaw(u64 offset, const void *buffer, size_t size) override;
|
||||
[[nodiscard]] size_t getActualSize() const override;
|
||||
|
||||
bool open() override;
|
||||
void close() override;
|
||||
|
||||
[[nodiscard]] std::string getName() const override;
|
||||
[[nodiscard]] std::vector<std::pair<std::string, std::string>> getDataInformation() const override { return { }; }
|
||||
|
||||
void loadSettings(const nlohmann::json &settings) override { hex::unused(settings); }
|
||||
[[nodiscard]] nlohmann::json storeSettings(nlohmann::json settings) const override { return settings; }
|
||||
|
||||
[[nodiscard]] std::string getTypeName() const override {
|
||||
return "hex.builtin.provider.intel_hex";
|
||||
}
|
||||
|
||||
bool hasFilePicker() const override { return true; }
|
||||
bool handleFilePicker() override;
|
||||
|
||||
std::pair<Region, bool> getRegionValidity(u64 address) const override;
|
||||
|
||||
protected:
|
||||
bool m_dataValid = false;
|
||||
size_t m_dataSize = 0x00;
|
||||
interval_tree::IntervalTree<u64, std::vector<u8>> m_data;
|
||||
|
||||
std::fs::path m_sourceFilePath;
|
||||
};
|
||||
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <content/providers/intel_hex_provider.hpp>
|
||||
|
||||
namespace hex::plugin::builtin::prv {
|
||||
|
||||
class MotorolaSRECProvider : public IntelHexProvider {
|
||||
public:
|
||||
MotorolaSRECProvider() = default;
|
||||
~MotorolaSRECProvider() override = default;
|
||||
|
||||
bool open() override;
|
||||
void close() override;
|
||||
|
||||
[[nodiscard]] std::string getName() const override;
|
||||
|
||||
[[nodiscard]] std::string getTypeName() const override {
|
||||
return "hex.builtin.provider.motorola_srec";
|
||||
}
|
||||
|
||||
bool handleFilePicker() override;
|
||||
|
||||
private:
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -11,38 +11,13 @@
|
||||
#include <imgui.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include "content/providers/file_provider.hpp"
|
||||
#include "provider_extra_data.hpp"
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
static void openFile(const std::fs::path &path) {
|
||||
auto provider = ImHexApi::Provider::createProvider("hex.builtin.provider.file");
|
||||
|
||||
if (auto fileProvider = dynamic_cast<prv::FileProvider *>(provider)) {
|
||||
fileProvider->setPath(path);
|
||||
if (!fileProvider->open()) {
|
||||
View::showErrorPopup("hex.builtin.popup.error.open"_lang);
|
||||
ImHexApi::Provider::remove(provider);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!provider->isWritable()) {
|
||||
View::showErrorPopup("hex.builtin.popup.error.read_only"_lang);
|
||||
}
|
||||
|
||||
if (!provider->isAvailable()) {
|
||||
View::showErrorPopup("hex.builtin.popup.error.open"_lang);
|
||||
ImHexApi::Provider::remove(provider);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
ImHexApi::Provider::createProvider("hex.builtin.provider.file");
|
||||
EventManager::post<EventFileLoaded>(path);
|
||||
EventManager::post<EventDataChanged>();
|
||||
EventManager::post<EventHighlightingChanged>();
|
||||
}
|
||||
|
||||
void registerEventHandlers() {
|
||||
@ -88,9 +63,7 @@ namespace hex::plugin::builtin {
|
||||
EventManager::post<RequestOpenFile>(path);
|
||||
});
|
||||
} else if (name == "Open File") {
|
||||
fs::openFileBrowser(fs::DialogMode::Open, {}, [](const auto &path) {
|
||||
EventManager::post<RequestOpenFile>(path);
|
||||
});
|
||||
ImHexApi::Provider::createProvider("hex.builtin.provider.file");
|
||||
} else if (name == "Open Project") {
|
||||
fs::openFileBrowser(fs::DialogMode::Open, { {"Project File", "hexproj"} },
|
||||
[](const auto &path) {
|
||||
@ -104,8 +77,28 @@ namespace hex::plugin::builtin {
|
||||
});
|
||||
|
||||
EventManager::subscribe<EventProviderCreated>([](hex::prv::Provider *provider) {
|
||||
if (provider->hasLoadInterface())
|
||||
if (provider->hasFilePicker()) {
|
||||
if (!provider->handleFilePicker()) {
|
||||
ImHexApi::Tasks::doLater([provider] { ImHexApi::Provider::remove(provider); });
|
||||
return;
|
||||
}
|
||||
if (!provider->open()) {
|
||||
View::showErrorPopup("hex.builtin.popup.error.open"_lang);
|
||||
ImHexApi::Tasks::doLater([provider] { ImHexApi::Provider::remove(provider); });
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (provider->hasLoadInterface())
|
||||
EventManager::post<RequestOpenPopup>(View::toWindowName("hex.builtin.view.provider_settings.load_popup"));
|
||||
else {
|
||||
if (!provider->open() || !provider->isAvailable()) {
|
||||
View::showErrorPopup("hex.builtin.popup.error.open"_lang);
|
||||
ImHexApi::Tasks::doLater([provider] { ImHexApi::Provider::remove(provider); });
|
||||
}
|
||||
|
||||
if (!provider->isWritable())
|
||||
View::showErrorPopup("hex.builtin.popup.error.read_only"_lang);
|
||||
}
|
||||
});
|
||||
|
||||
EventManager::subscribe<EventProviderDeleted>([](hex::prv::Provider *provider) {
|
||||
|
@ -4,6 +4,8 @@
|
||||
#include "content/providers/file_provider.hpp"
|
||||
#include "content/providers/null_provider.hpp"
|
||||
#include "content/providers/disk_provider.hpp"
|
||||
#include "content/providers/intel_hex_provider.hpp"
|
||||
#include "content/providers/motorola_srec_provider.hpp"
|
||||
|
||||
#include <hex/api/project_file_manager.hpp>
|
||||
#include <nlohmann/json.hpp>
|
||||
@ -17,6 +19,8 @@ namespace hex::plugin::builtin {
|
||||
ContentRegistry::Provider::add<prv::NullProvider>(false);
|
||||
ContentRegistry::Provider::add<prv::DiskProvider>();
|
||||
ContentRegistry::Provider::add<prv::GDBProvider>();
|
||||
ContentRegistry::Provider::add<prv::IntelHexProvider>();
|
||||
ContentRegistry::Provider::add<prv::MotorolaSRECProvider>();
|
||||
|
||||
ProjectFile::registerHandler({
|
||||
.basePath = "providers",
|
||||
|
@ -35,10 +35,6 @@ namespace hex::plugin::builtin::prv {
|
||||
this->reloadDrives();
|
||||
}
|
||||
|
||||
DiskProvider::~DiskProvider() {
|
||||
this->close();
|
||||
}
|
||||
|
||||
bool DiskProvider::isAvailable() const {
|
||||
#if defined(OS_WINDOWS)
|
||||
|
||||
|
@ -12,14 +12,6 @@
|
||||
|
||||
namespace hex::plugin::builtin::prv {
|
||||
|
||||
FileProvider::FileProvider() : Provider() {
|
||||
}
|
||||
|
||||
FileProvider::~FileProvider() {
|
||||
this->close();
|
||||
}
|
||||
|
||||
|
||||
bool FileProvider::isAvailable() const {
|
||||
#if defined(OS_WINDOWS)
|
||||
return this->m_file != INVALID_HANDLE_VALUE && this->m_mapping != INVALID_HANDLE_VALUE && this->m_mappedFile != nullptr;
|
||||
@ -196,6 +188,12 @@ namespace hex::plugin::builtin::prv {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool FileProvider::handleFilePicker() {
|
||||
return fs::openFileBrowser(fs::DialogMode::Open, {}, [this](const auto &path) {
|
||||
this->setPath(path);
|
||||
});
|
||||
}
|
||||
|
||||
void FileProvider::setPath(const std::fs::path &path) {
|
||||
this->m_path = path;
|
||||
}
|
||||
|
@ -121,11 +121,6 @@ namespace hex::plugin::builtin::prv {
|
||||
GDBProvider::GDBProvider() : Provider(), m_size(0xFFFF'FFFF) {
|
||||
}
|
||||
|
||||
GDBProvider::~GDBProvider() {
|
||||
this->close();
|
||||
}
|
||||
|
||||
|
||||
bool GDBProvider::isAvailable() const {
|
||||
return this->m_socket.isConnected();
|
||||
}
|
||||
|
235
plugins/builtin/source/content/providers/intel_hex_provider.cpp
Normal file
235
plugins/builtin/source/content/providers/intel_hex_provider.cpp
Normal file
@ -0,0 +1,235 @@
|
||||
#include "content/providers/intel_hex_provider.hpp"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include <hex/api/imhex_api.hpp>
|
||||
#include <hex/api/localization.hpp>
|
||||
#include <hex/helpers/utils.hpp>
|
||||
#include <hex/helpers/file.hpp>
|
||||
#include <hex/helpers/fmt.hpp>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
namespace hex::plugin::builtin::prv {
|
||||
|
||||
namespace intel_hex {
|
||||
|
||||
u8 parseHexDigit(char c) {
|
||||
if (c >= '0' && c <= '9')
|
||||
return c - '0';
|
||||
else if (c >= 'A' && c <= 'F')
|
||||
return c - 'A' + 10;
|
||||
else if (c >= 'a' && c <= 'f')
|
||||
return c - 'a' + 10;
|
||||
else
|
||||
throw std::runtime_error("Failed to parse hex digit");
|
||||
}
|
||||
|
||||
std::map<u64, std::vector<u8>> parseIntelHex(const std::string &string) {
|
||||
std::map<u64, std::vector<u8>> result;
|
||||
|
||||
u8 checksum = 0x00;
|
||||
u64 offset = 0x00;
|
||||
|
||||
u8 byteCount = 0x00;
|
||||
u32 segmentAddress = 0x0000'0000;
|
||||
u32 extendedLinearAddress = 0x0000'0000;
|
||||
u16 address = 0x0000;
|
||||
std::vector<u8> data;
|
||||
|
||||
enum class RecordType {
|
||||
Data = 0x00,
|
||||
EndOfFile = 0x01,
|
||||
ExtendedSegmentAddress = 0x02,
|
||||
StartSegmentAddress = 0x03,
|
||||
ExtendedLinearAddress = 0x04,
|
||||
StartLinearAddress = 0x05
|
||||
} recordType;
|
||||
|
||||
auto c = [&]() {
|
||||
while (std::isspace(string[offset]) && offset < string.length())
|
||||
offset++;
|
||||
|
||||
if (offset >= string.length())
|
||||
throw std::runtime_error("Unexpected end of file");
|
||||
|
||||
return string[offset++];
|
||||
};
|
||||
|
||||
auto parseValue = [&](u8 byteCount) {
|
||||
u64 value = 0x00;
|
||||
for (u8 i = 0; i < byteCount; i++) {
|
||||
u8 byte = (parseHexDigit(c()) << 4) | parseHexDigit(c());
|
||||
value <<= 8;
|
||||
value |= byte;
|
||||
|
||||
checksum += byte;
|
||||
}
|
||||
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
bool endOfFile = false;
|
||||
try {
|
||||
while (offset < string.length()) {
|
||||
// Parse start code
|
||||
if (c() != ':')
|
||||
return { };
|
||||
|
||||
checksum = 0x00;
|
||||
|
||||
if (endOfFile)
|
||||
throw std::runtime_error("Unexpected end of file");
|
||||
|
||||
// Parse byte count
|
||||
byteCount = parseValue(1);
|
||||
|
||||
// Parse address
|
||||
address = parseValue(2);
|
||||
|
||||
// Parse record type
|
||||
recordType = static_cast<RecordType>(parseValue(1));
|
||||
|
||||
data.clear();
|
||||
for (u32 i = 0; i < byteCount; i++) {
|
||||
data.push_back(parseValue(1));
|
||||
}
|
||||
|
||||
parseValue(1);
|
||||
if (!data.empty() && checksum != 0x00)
|
||||
throw std::runtime_error("Checksum mismatch");
|
||||
|
||||
while (std::isspace(string[offset]) && offset < string.length())
|
||||
offset++;
|
||||
|
||||
// Construct region
|
||||
switch (recordType) {
|
||||
case RecordType::Data: {
|
||||
result[extendedLinearAddress | (segmentAddress + address)] = data;
|
||||
break;
|
||||
}
|
||||
case RecordType::EndOfFile: {
|
||||
endOfFile = true;
|
||||
break;
|
||||
}
|
||||
case RecordType::ExtendedSegmentAddress: {
|
||||
if (byteCount != 2)
|
||||
throw std::runtime_error("Unexpected byte count");
|
||||
|
||||
segmentAddress = (data[0] << 8 | data[1]) * 16;
|
||||
break;
|
||||
}
|
||||
case RecordType::StartSegmentAddress: {
|
||||
if (byteCount != 4)
|
||||
throw std::runtime_error("Unexpected byte count");
|
||||
|
||||
// Can be safely ignored
|
||||
break;
|
||||
}
|
||||
case RecordType::ExtendedLinearAddress: {
|
||||
if (byteCount != 2)
|
||||
throw std::runtime_error("Unexpected byte count");
|
||||
|
||||
extendedLinearAddress = (data[0] << 8 | data[1]) << 16;
|
||||
break;
|
||||
}
|
||||
case RecordType::StartLinearAddress: {
|
||||
if (byteCount != 4)
|
||||
throw std::runtime_error("Unexpected byte count");
|
||||
|
||||
// Can be safely ignored
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (const std::runtime_error &e) {
|
||||
return { };
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void IntelHexProvider::readRaw(u64 offset, void *buffer, size_t size) {
|
||||
auto intervals = this->m_data.findOverlapping(offset, (offset + size) - 1);
|
||||
|
||||
std::memset(buffer, 0x00, size);
|
||||
auto bytes = reinterpret_cast<u8*>(buffer);
|
||||
for (const auto &interval : intervals) {
|
||||
for (u32 i = std::max(interval.start, offset); i <= interval.stop && (i - offset) < size; i++) {
|
||||
bytes[i - offset] = interval.value[i - interval.start];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IntelHexProvider::writeRaw(u64 offset, const void *buffer, size_t size) {
|
||||
hex::unused(offset, buffer, size);
|
||||
}
|
||||
|
||||
size_t IntelHexProvider::getActualSize() const {
|
||||
return this->m_dataSize;
|
||||
}
|
||||
|
||||
bool IntelHexProvider::open() {
|
||||
auto file = fs::File(this->m_sourceFilePath, fs::File::Mode::Read);
|
||||
if (!file.isValid())
|
||||
return false;
|
||||
|
||||
auto data = intel_hex::parseIntelHex(file.readString());
|
||||
if (data.empty())
|
||||
return false;
|
||||
|
||||
u64 maxAddress = 0x00;
|
||||
decltype(this->m_data)::interval_vector intervals;
|
||||
for (auto &[address, bytes] : data) {
|
||||
auto endAddress = (address + bytes.size()) - 1;
|
||||
intervals.emplace_back(address, endAddress, std::move(bytes));
|
||||
|
||||
if (endAddress > maxAddress)
|
||||
maxAddress = endAddress;
|
||||
}
|
||||
this->m_data = std::move(intervals);
|
||||
this->m_dataSize = maxAddress + 1;
|
||||
this->m_dataValid = true;
|
||||
|
||||
return Provider::open();
|
||||
}
|
||||
|
||||
void IntelHexProvider::close() {
|
||||
|
||||
Provider::close();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string IntelHexProvider::getName() const {
|
||||
return hex::format("hex.builtin.provider.intel_hex.name"_lang, this->m_sourceFilePath.filename().string());
|
||||
}
|
||||
|
||||
bool IntelHexProvider::handleFilePicker() {
|
||||
auto picked = fs::openFileBrowser(fs::DialogMode::Open, { { "Intel Hex File", "*" } }, [this](const std::fs::path &path) {
|
||||
this->m_sourceFilePath = path;
|
||||
});
|
||||
if (!picked)
|
||||
return false;
|
||||
if (!fs::isRegularFile(this->m_sourceFilePath))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::pair<Region, bool> IntelHexProvider::getRegionValidity(u64 address) const {
|
||||
auto intervals = this->m_data.findOverlapping(address, address);
|
||||
if (intervals.empty())
|
||||
return { Region::Invalid(), false };
|
||||
|
||||
auto closestInterval = intervals.front();
|
||||
for (const auto &interval : intervals) {
|
||||
if (interval.start < closestInterval.start)
|
||||
closestInterval = interval;
|
||||
}
|
||||
return { Region { closestInterval.start, (closestInterval.stop - closestInterval.start) + 1}, true };
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,216 @@
|
||||
#include "content/providers/motorola_srec_provider.hpp"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include <hex/api/imhex_api.hpp>
|
||||
#include <hex/api/localization.hpp>
|
||||
#include <hex/helpers/utils.hpp>
|
||||
#include <hex/helpers/file.hpp>
|
||||
#include <hex/helpers/fmt.hpp>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
namespace hex::plugin::builtin::prv {
|
||||
|
||||
namespace motorola_srec {
|
||||
|
||||
u8 parseHexDigit(char c) {
|
||||
if (c >= '0' && c <= '9')
|
||||
return c - '0';
|
||||
else if (c >= 'A' && c <= 'F')
|
||||
return c - 'A' + 10;
|
||||
else if (c >= 'a' && c <= 'f')
|
||||
return c - 'a' + 10;
|
||||
else
|
||||
throw std::runtime_error("Failed to parse hex digit");
|
||||
}
|
||||
|
||||
std::map<u64, std::vector<u8>> parseMotorolaSREC(const std::string &string) {
|
||||
std::map<u64, std::vector<u8>> result;
|
||||
|
||||
u64 offset = 0x00;
|
||||
u8 checksum = 0x00;
|
||||
u8 byteCount = 0x00;
|
||||
u32 address = 0x0000'0000;
|
||||
std::vector<u8> data;
|
||||
|
||||
auto c = [&]() {
|
||||
while (std::isspace(string[offset]) && offset < string.length())
|
||||
offset++;
|
||||
|
||||
if (offset >= string.length())
|
||||
throw std::runtime_error("Unexpected end of file");
|
||||
|
||||
return string[offset++];
|
||||
};
|
||||
|
||||
auto parseValue = [&](u8 byteCount) {
|
||||
u64 value = 0x00;
|
||||
for (u8 i = 0; i < byteCount; i++) {
|
||||
u8 byte = (parseHexDigit(c()) << 4) | parseHexDigit(c());
|
||||
value <<= 8;
|
||||
value |= byte;
|
||||
|
||||
checksum += byte;
|
||||
}
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
enum class RecordType {
|
||||
Header = 0x00,
|
||||
Data16 = 0x01,
|
||||
Data24 = 0x02,
|
||||
Data32 = 0x03,
|
||||
Reserved = 0x04,
|
||||
Count16 = 0x05,
|
||||
Count24 = 0x06,
|
||||
StartAddress32 = 0x07,
|
||||
StartAddress24 = 0x08,
|
||||
StartAddress16 = 0x09,
|
||||
} recordType;
|
||||
|
||||
bool endOfFile = false;
|
||||
try {
|
||||
while (offset < string.length()) {
|
||||
// Parse record start
|
||||
if (c() != 'S')
|
||||
return { };
|
||||
|
||||
if (endOfFile)
|
||||
throw std::runtime_error("Unexpected end of file");
|
||||
|
||||
// Parse record type
|
||||
{
|
||||
char typeCharacter = c();
|
||||
if (typeCharacter < '0' || typeCharacter > '9')
|
||||
throw std::runtime_error("Invalid record type");
|
||||
recordType = static_cast<RecordType>(typeCharacter - '0');
|
||||
}
|
||||
|
||||
checksum = 0x00;
|
||||
|
||||
// Parse byte count
|
||||
byteCount = parseValue(1);
|
||||
|
||||
// Parse address
|
||||
switch (recordType) {
|
||||
case RecordType::Reserved:
|
||||
break;
|
||||
case RecordType::Header:
|
||||
case RecordType::Data16:
|
||||
case RecordType::Count16:
|
||||
case RecordType::StartAddress16:
|
||||
byteCount -= 2;
|
||||
address = parseValue(2);
|
||||
break;
|
||||
case RecordType::Data24:
|
||||
case RecordType::Count24:
|
||||
case RecordType::StartAddress24:
|
||||
byteCount -= 3;
|
||||
address = parseValue(3);
|
||||
break;
|
||||
case RecordType::Data32:
|
||||
case RecordType::StartAddress32:
|
||||
byteCount -= 4;
|
||||
address = parseValue(4);
|
||||
break;
|
||||
}
|
||||
|
||||
byteCount -= 1;
|
||||
|
||||
auto readData = [&byteCount, &parseValue]() {
|
||||
std::vector<u8> bytes;
|
||||
bytes.resize(byteCount);
|
||||
for (u8 i = 0; i < byteCount; i++) {
|
||||
bytes[i] = parseValue(1);
|
||||
}
|
||||
|
||||
return bytes;
|
||||
};
|
||||
|
||||
// Parse data
|
||||
data = readData();
|
||||
|
||||
// Parse checksum
|
||||
{
|
||||
auto value = parseValue(1);
|
||||
if (((checksum - value) ^ 0xFF) != value)
|
||||
throw std::runtime_error("Invalid checksum");
|
||||
}
|
||||
|
||||
// Construct region
|
||||
switch (recordType) {
|
||||
case RecordType::Data16:
|
||||
case RecordType::Data24:
|
||||
case RecordType::Data32:
|
||||
result[address] = data;
|
||||
break;
|
||||
case RecordType::Header:
|
||||
case RecordType::Reserved:
|
||||
break;
|
||||
case RecordType::Count16:
|
||||
case RecordType::Count24:
|
||||
break;
|
||||
case RecordType::StartAddress32:
|
||||
case RecordType::StartAddress24:
|
||||
case RecordType::StartAddress16:
|
||||
endOfFile = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (const std::runtime_error &e) {
|
||||
return { };
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool MotorolaSRECProvider::open() {
|
||||
auto file = fs::File(this->m_sourceFilePath, fs::File::Mode::Read);
|
||||
if (!file.isValid())
|
||||
return false;
|
||||
|
||||
auto data = motorola_srec::parseMotorolaSREC(file.readString());
|
||||
if (data.empty())
|
||||
return false;
|
||||
|
||||
u64 maxAddress = 0x00;
|
||||
decltype(this->m_data)::interval_vector intervals;
|
||||
for (auto &[address, bytes] : data) {
|
||||
auto endAddress = (address + bytes.size()) - 1;
|
||||
intervals.emplace_back(address, endAddress, std::move(bytes));
|
||||
|
||||
if (endAddress > maxAddress)
|
||||
maxAddress = endAddress;
|
||||
}
|
||||
this->m_data = std::move(intervals);
|
||||
this->m_dataSize = maxAddress + 1;
|
||||
this->m_dataValid = true;
|
||||
|
||||
return Provider::open();
|
||||
}
|
||||
|
||||
void MotorolaSRECProvider::close() {
|
||||
Provider::close();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string MotorolaSRECProvider::getName() const {
|
||||
return hex::format("hex.builtin.provider.motorola_srec.name"_lang, this->m_sourceFilePath.filename().string());
|
||||
}
|
||||
|
||||
bool MotorolaSRECProvider::handleFilePicker() {
|
||||
auto picked = fs::openFileBrowser(fs::DialogMode::Open, { { "Motorola SREC File", "*" } }, [this](const std::fs::path &path) {
|
||||
this->m_sourceFilePath = path;
|
||||
});
|
||||
if (!picked)
|
||||
return false;
|
||||
if (!fs::isRegularFile(this->m_sourceFilePath))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
@ -28,7 +28,6 @@ namespace hex::plugin::builtin {
|
||||
if (ImGui::BeginPopupModal(View::toWindowName("hex.builtin.view.provider_settings.load_popup").c_str(), nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
|
||||
auto provider = hex::ImHexApi::Provider::get();
|
||||
|
||||
if (provider != nullptr) {
|
||||
provider->drawLoadInterface();
|
||||
|
||||
|
@ -140,9 +140,7 @@ namespace hex::plugin::builtin {
|
||||
|
||||
for (const auto &unlocalizedProviderName : ContentRegistry::Provider::getEntries()) {
|
||||
if (ImGui::Hyperlink(LangEntry(unlocalizedProviderName))) {
|
||||
auto *provider = ImHexApi::Provider::createProvider(unlocalizedProviderName);
|
||||
if (!provider->hasLoadInterface())
|
||||
(void)provider->open();
|
||||
ImHexApi::Provider::createProvider(unlocalizedProviderName);
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
}
|
||||
|
@ -734,19 +734,18 @@ namespace hex::plugin::builtin {
|
||||
{ "hex.builtin.setting.font", "Schriftart" },
|
||||
{ "hex.builtin.setting.font.font_path", "Eigene Schriftart" },
|
||||
{ "hex.builtin.setting.font.font_size", "Schriftgrösse" },
|
||||
//{ "hex.builtin.setting.proxy", "Proxy" },
|
||||
//{ "hex.builtin.setting.proxy.description", "Proxy will take effect on store, wikipedia or any other plugin immediately." },
|
||||
//{ "hex.builtin.setting.proxy.enable", "Enable Proxy" },
|
||||
//{ "hex.builtin.setting.proxy.url", "Proxy URL" },
|
||||
//{ "hex.builtin.setting.proxy.url.tooltip", "http(s):// or socks5:// (e.g., http://127.0.0.1:1080)" },
|
||||
|
||||
{ "hex.builtin.provider.file.path", "Dateipfad" },
|
||||
{ "hex.builtin.provider.file.size", "Größe" },
|
||||
{ "hex.builtin.provider.file.creation", "Erstellungszeit" },
|
||||
{ "hex.builtin.provider.file.access", "Letzte Zugriffszeit" },
|
||||
{ "hex.builtin.provider.file.modification", "Letzte Modifikationszeit" },
|
||||
{ "hex.builtin.setting.proxy", "Proxy" },
|
||||
{ "hex.builtin.setting.proxy.description", "Proxy wird bei allen Netzwerkverbindungen angewendet." },
|
||||
{ "hex.builtin.setting.proxy.enable", "Proxy aktivieren" },
|
||||
{ "hex.builtin.setting.proxy.url", "Proxy URL" },
|
||||
{ "hex.builtin.setting.proxy.url.tooltip", "http(s):// oder socks5:// (z.B, http://127.0.0.1:1080)" },
|
||||
|
||||
{ "hex.builtin.provider.file", "Datei Provider" },
|
||||
{ "hex.builtin.provider.file.path", "Dateipfad" },
|
||||
{ "hex.builtin.provider.file.size", "Größe" },
|
||||
{ "hex.builtin.provider.file.creation", "Erstellungszeit" },
|
||||
{ "hex.builtin.provider.file.access", "Letzte Zugriffszeit" },
|
||||
{ "hex.builtin.provider.file.modification", "Letzte Modifikationszeit" },
|
||||
{ "hex.builtin.provider.gdb", "GDB Server Provider" },
|
||||
{ "hex.builtin.provider.gdb.name", "GDB Server <{0}:{1}>" },
|
||||
{ "hex.builtin.provider.gdb.server", "Server" },
|
||||
@ -757,6 +756,10 @@ namespace hex::plugin::builtin {
|
||||
{ "hex.builtin.provider.disk.disk_size", "Datenträgergrösse" },
|
||||
{ "hex.builtin.provider.disk.sector_size", "Sektorgrösse" },
|
||||
{ "hex.builtin.provider.disk.reload", "Neu laden" },
|
||||
{ "hex.builtin.provider.intel_hex", "Intel Hex Provider" },
|
||||
{ "hex.builtin.provider.intel_hex.name", "Intel Hex {0}" },
|
||||
{ "hex.builtin.provider.motorola_srec", "Motorola SREC Provider" },
|
||||
{ "hex.builtin.provider.motorola_srec.name", "Motorola SREC {0}" },
|
||||
|
||||
{ "hex.builtin.layouts.default", "Standard" },
|
||||
|
||||
|
@ -744,13 +744,12 @@ namespace hex::plugin::builtin {
|
||||
{ "hex.builtin.setting.proxy.url", "Proxy URL" },
|
||||
{ "hex.builtin.setting.proxy.url.tooltip", "http(s):// or socks5:// (e.g., http://127.0.0.1:1080)" },
|
||||
|
||||
{ "hex.builtin.provider.file.path", "File path" },
|
||||
{ "hex.builtin.provider.file.size", "Size" },
|
||||
{ "hex.builtin.provider.file.creation", "Creation time" },
|
||||
{ "hex.builtin.provider.file.access", "Last access time" },
|
||||
{ "hex.builtin.provider.file.modification", "Last modification time" },
|
||||
|
||||
{ "hex.builtin.provider.file", "File Provider" },
|
||||
{ "hex.builtin.provider.file.path", "File path" },
|
||||
{ "hex.builtin.provider.file.size", "Size" },
|
||||
{ "hex.builtin.provider.file.creation", "Creation time" },
|
||||
{ "hex.builtin.provider.file.access", "Last access time" },
|
||||
{ "hex.builtin.provider.file.modification", "Last modification time" },
|
||||
{ "hex.builtin.provider.gdb", "GDB Server Provider" },
|
||||
{ "hex.builtin.provider.gdb.name", "GDB Server <{0}:{1}>" },
|
||||
{ "hex.builtin.provider.gdb.server", "Server" },
|
||||
@ -761,6 +760,10 @@ namespace hex::plugin::builtin {
|
||||
{ "hex.builtin.provider.disk.disk_size", "Disk Size" },
|
||||
{ "hex.builtin.provider.disk.sector_size", "Sector Size" },
|
||||
{ "hex.builtin.provider.disk.reload", "Reload" },
|
||||
{ "hex.builtin.provider.intel_hex", "Intel Hex Provider" },
|
||||
{ "hex.builtin.provider.intel_hex.name", "Intel Hex {0}" },
|
||||
{ "hex.builtin.provider.motorola_srec", "Motorola SREC Provider" },
|
||||
{ "hex.builtin.provider.motorola_srec.name", "Motorola SREC {0}" },
|
||||
|
||||
{ "hex.builtin.layouts.default", "Default" },
|
||||
|
||||
|
@ -748,13 +748,12 @@ namespace hex::plugin::builtin {
|
||||
//{ "hex.builtin.setting.proxy.url", "Proxy URL" },
|
||||
//{ "hex.builtin.setting.proxy.url.tooltip", "http(s):// or socks5:// (e.g., http://127.0.0.1:1080)" },
|
||||
|
||||
{ "hex.builtin.provider.file.path", "Percorso del File" },
|
||||
{ "hex.builtin.provider.file.size", "Dimensione" },
|
||||
{ "hex.builtin.provider.file.creation", "Data di creazione" },
|
||||
{ "hex.builtin.provider.file.access", "Data dell'ultimo accesso" },
|
||||
{ "hex.builtin.provider.file.modification", "Data dell'ultima modifica" },
|
||||
|
||||
{ "hex.builtin.provider.file", "Provider di file" },
|
||||
{ "hex.builtin.provider.file.path", "Percorso del File" },
|
||||
{ "hex.builtin.provider.file.size", "Dimensione" },
|
||||
{ "hex.builtin.provider.file.creation", "Data di creazione" },
|
||||
{ "hex.builtin.provider.file.access", "Data dell'ultimo accesso" },
|
||||
{ "hex.builtin.provider.file.modification", "Data dell'ultima modifica" },
|
||||
{ "hex.builtin.provider.gdb", "Server GDB Provider" },
|
||||
{ "hex.builtin.provider.gdb.name", "Server GDB <{0}:{1}>" },
|
||||
{ "hex.builtin.provider.gdb.server", "Server" },
|
||||
@ -765,6 +764,10 @@ namespace hex::plugin::builtin {
|
||||
{ "hex.builtin.provider.disk.disk_size", "Dimensione disco" },
|
||||
{ "hex.builtin.provider.disk.sector_size", "Dimensione settore" },
|
||||
{ "hex.builtin.provider.disk.reload", "Ricarica" },
|
||||
//{ "hex.builtin.provider.intel_hex", "Intel Hex Provider" },
|
||||
// { "hex.builtin.provider.intel_hex.name", "Intel Hex {0}" },
|
||||
//{ "hex.builtin.provider.motorola_srec", "Motorola SREC Provider" },
|
||||
// { "hex.builtin.provider.motorola_srec.name", "Motorola SREC {0}" },
|
||||
|
||||
{ "hex.builtin.layouts.default", "Default" },
|
||||
|
||||
|
@ -745,13 +745,12 @@ namespace hex::plugin::builtin {
|
||||
{ "hex.builtin.setting.proxy.url", "プロキシURL" },
|
||||
//{ "hex.builtin.setting.proxy.url.tooltip", "http(s):// or socks5:// (e.g., http://127.0.0.1:1080)" },
|
||||
|
||||
{ "hex.builtin.provider.file.path", "ファイルパス" },
|
||||
{ "hex.builtin.provider.file.size", "サイズ" },
|
||||
{ "hex.builtin.provider.file.creation", "作成時刻" },
|
||||
{ "hex.builtin.provider.file.access", "最終アクセス時刻" },
|
||||
{ "hex.builtin.provider.file.modification", "最終編集時刻" },
|
||||
|
||||
{ "hex.builtin.provider.file", "ファイルプロバイダ" },
|
||||
{ "hex.builtin.provider.file.path", "ファイルパス" },
|
||||
{ "hex.builtin.provider.file.size", "サイズ" },
|
||||
{ "hex.builtin.provider.file.creation", "作成時刻" },
|
||||
{ "hex.builtin.provider.file.access", "最終アクセス時刻" },
|
||||
{ "hex.builtin.provider.file.modification", "最終編集時刻" },
|
||||
{ "hex.builtin.provider.gdb", "GDBサーバープロバイダ" },
|
||||
{ "hex.builtin.provider.gdb.name", "GDBサーバー <{0}:{1}>" },
|
||||
{ "hex.builtin.provider.gdb.server", "サーバー" },
|
||||
@ -762,6 +761,10 @@ namespace hex::plugin::builtin {
|
||||
{ "hex.builtin.provider.disk.disk_size", "ディスクサイズ" },
|
||||
{ "hex.builtin.provider.disk.sector_size", "セクタサイズ" },
|
||||
{ "hex.builtin.provider.disk.reload", "リロード" },
|
||||
//{ "hex.builtin.provider.intel_hex", "Intel Hex Provider" },
|
||||
// { "hex.builtin.provider.intel_hex.name", "Intel Hex {0}" },
|
||||
//{ "hex.builtin.provider.motorola_srec", "Motorola SREC Provider" },
|
||||
// { "hex.builtin.provider.motorola_srec.name", "Motorola SREC {0}" },
|
||||
|
||||
{ "hex.builtin.layouts.default", "標準" },
|
||||
|
||||
|
@ -740,13 +740,12 @@ namespace hex::plugin::builtin {
|
||||
//{ "hex.builtin.setting.proxy.url", "Proxy URL" },
|
||||
//{ "hex.builtin.setting.proxy.url.tooltip", "http(s):// or socks5:// (e.g., http://127.0.0.1:1080)" },
|
||||
|
||||
{ "hex.builtin.provider.file.path", "Caminho do Arquivo" },
|
||||
{ "hex.builtin.provider.file.size", "Tamanho" },
|
||||
{ "hex.builtin.provider.file.creation", "Data de Criação" },
|
||||
{ "hex.builtin.provider.file.access", "Ultima vez acessado" },
|
||||
{ "hex.builtin.provider.file.modification", "Ultima vez modificado" },
|
||||
|
||||
{ "hex.builtin.provider.file", "Provedor de arquivo" },
|
||||
{ "hex.builtin.provider.file.path", "Caminho do Arquivo" },
|
||||
{ "hex.builtin.provider.file.size", "Tamanho" },
|
||||
{ "hex.builtin.provider.file.creation", "Data de Criação" },
|
||||
{ "hex.builtin.provider.file.access", "Ultima vez acessado" },
|
||||
{ "hex.builtin.provider.file.modification", "Ultima vez modificado" },
|
||||
{ "hex.builtin.provider.gdb", "GDB Server Provider" },
|
||||
{ "hex.builtin.provider.gdb.name", "GDB Server <{0}:{1}>" },
|
||||
{ "hex.builtin.provider.gdb.server", "Servidor" },
|
||||
@ -757,6 +756,10 @@ namespace hex::plugin::builtin {
|
||||
{ "hex.builtin.provider.disk.disk_size", "Tamanho do Disco" },
|
||||
{ "hex.builtin.provider.disk.sector_size", "Tamanho do Setor" },
|
||||
{ "hex.builtin.provider.disk.reload", "Recarregar" },
|
||||
//{ "hex.builtin.provider.intel_hex", "Intel Hex Provider" },
|
||||
// { "hex.builtin.provider.intel_hex.name", "Intel Hex {0}" },
|
||||
//{ "hex.builtin.provider.motorola_srec", "Motorola SREC Provider" },
|
||||
// { "hex.builtin.provider.motorola_srec.name", "Motorola SREC {0}" },
|
||||
|
||||
{ "hex.builtin.layouts.default", "Default" },
|
||||
|
||||
|
@ -745,13 +745,12 @@ namespace hex::plugin::builtin {
|
||||
{ "hex.builtin.setting.proxy.url", "代理 URL" },
|
||||
{ "hex.builtin.setting.proxy.url.tooltip", "http(s):// 或 socks5://(如 http://127.0.0.1:1080)" },
|
||||
|
||||
{ "hex.builtin.provider.file.path", "路径" },
|
||||
{ "hex.builtin.provider.file.size", "大小" },
|
||||
{ "hex.builtin.provider.file.creation", "创建时间" },
|
||||
{ "hex.builtin.provider.file.access", "最后访问时间" },
|
||||
{ "hex.builtin.provider.file.modification", "最后更改时间" },
|
||||
|
||||
{ "hex.builtin.provider.file", "文件" },
|
||||
{ "hex.builtin.provider.file.path", "路径" },
|
||||
{ "hex.builtin.provider.file.size", "大小" },
|
||||
{ "hex.builtin.provider.file.creation", "创建时间" },
|
||||
{ "hex.builtin.provider.file.access", "最后访问时间" },
|
||||
{ "hex.builtin.provider.file.modification", "最后更改时间" },
|
||||
{ "hex.builtin.provider.gdb", "GDB 服务器" },
|
||||
{ "hex.builtin.provider.gdb.name", "GDB 服务器 <{0}:{1}>" },
|
||||
{ "hex.builtin.provider.gdb.server", "服务器" },
|
||||
@ -762,6 +761,10 @@ namespace hex::plugin::builtin {
|
||||
{ "hex.builtin.provider.disk.disk_size", "磁盘大小" },
|
||||
{ "hex.builtin.provider.disk.sector_size", "扇区大小" },
|
||||
{ "hex.builtin.provider.disk.reload", "刷新" },
|
||||
//{ "hex.builtin.provider.intel_hex", "Intel Hex Provider" },
|
||||
// { "hex.builtin.provider.intel_hex.name", "Intel Hex {0}" },
|
||||
//{ "hex.builtin.provider.motorola_srec", "Motorola SREC Provider" },
|
||||
// { "hex.builtin.provider.motorola_srec.name", "Motorola SREC {0}" },
|
||||
|
||||
{ "hex.builtin.layouts.default", "默认" },
|
||||
|
||||
|
@ -741,13 +741,12 @@ namespace hex::plugin::builtin {
|
||||
{ "hex.builtin.setting.proxy.url", "Proxy 網址" },
|
||||
{ "hex.builtin.setting.proxy.url.tooltip", "http(s):// 或 socks5:// (例如 http://127.0.0.1:1080)" },
|
||||
|
||||
{ "hex.builtin.provider.file.path", "檔案路徑" },
|
||||
{ "hex.builtin.provider.file.size", "大小" },
|
||||
{ "hex.builtin.provider.file.creation", "建立時間" },
|
||||
{ "hex.builtin.provider.file.access", "最後存取時間" },
|
||||
{ "hex.builtin.provider.file.modification", "最後修改時間" },
|
||||
|
||||
{ "hex.builtin.provider.file", "檔案提供者" },
|
||||
{ "hex.builtin.provider.file.path", "檔案路徑" },
|
||||
{ "hex.builtin.provider.file.size", "大小" },
|
||||
{ "hex.builtin.provider.file.creation", "建立時間" },
|
||||
{ "hex.builtin.provider.file.access", "最後存取時間" },
|
||||
{ "hex.builtin.provider.file.modification", "最後修改時間" },
|
||||
{ "hex.builtin.provider.gdb", "GDB 伺服器提供者" },
|
||||
{ "hex.builtin.provider.gdb.name", "GDB 伺服器 <{0}:{1}>" },
|
||||
{ "hex.builtin.provider.gdb.server", "伺服器" },
|
||||
@ -758,6 +757,10 @@ namespace hex::plugin::builtin {
|
||||
{ "hex.builtin.provider.disk.disk_size", "Disk Size" },
|
||||
{ "hex.builtin.provider.disk.sector_size", "Sector Size" },
|
||||
{ "hex.builtin.provider.disk.reload", "Reload" },
|
||||
//{ "hex.builtin.provider.intel_hex", "Intel Hex Provider" },
|
||||
// { "hex.builtin.provider.intel_hex.name", "Intel Hex {0}" },
|
||||
//{ "hex.builtin.provider.motorola_srec", "Motorola SREC Provider" },
|
||||
// { "hex.builtin.provider.motorola_srec.name", "Motorola SREC {0}" },
|
||||
|
||||
{ "hex.builtin.layouts.default", "預設" },
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user