1
0
mirror of synced 2024-11-12 02:00:52 +01:00

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:
WerWolv 2022-08-12 15:11:27 +02:00 committed by GitHub
parent 72c4f50871
commit b580691871
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 1062 additions and 104 deletions

View 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
View 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
View 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

View 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

@ -1 +1 @@
Subproject commit 15f42da03ff501d5e1c66794a508ef9a452236e3
Subproject commit 7285b76f8284ea1c6e145ac0e664d8f3e5f537b9

View File

@ -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)

View File

@ -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();

View File

@ -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;

View File

@ -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() {

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;
};
}

View File

@ -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:
};
}

View File

@ -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) {

View File

@ -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",

View File

@ -35,10 +35,6 @@ namespace hex::plugin::builtin::prv {
this->reloadDrives();
}
DiskProvider::~DiskProvider() {
this->close();
}
bool DiskProvider::isAvailable() const {
#if defined(OS_WINDOWS)

View File

@ -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;
}

View File

@ -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();
}

View 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 };
}
}

View File

@ -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;
}
}

View File

@ -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();

View File

@ -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();
}
}

View File

@ -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" },

View File

@ -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" },

View File

@ -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" },

View File

@ -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", "標準" },

View File

@ -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" },

View File

@ -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", "默认" },

View File

@ -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", "預設" },