2022-08-12 15:11:27 +02:00
|
|
|
#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>
|
|
|
|
|
2022-11-07 00:04:47 +01:00
|
|
|
namespace hex::plugin::builtin {
|
2022-08-12 15:11:27 +02:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2022-08-17 16:28:44 +02:00
|
|
|
void IntelHexProvider::setBaseAddress(u64 address) {
|
|
|
|
auto oldBase = this->getBaseAddress();
|
|
|
|
|
|
|
|
auto intervals = this->m_data.findOverlapping(oldBase, oldBase + this->getActualSize());
|
|
|
|
|
|
|
|
for (auto &interval : intervals) {
|
|
|
|
interval.start = (interval.start - oldBase) + address;
|
|
|
|
interval.stop = (interval.stop - oldBase) + address;
|
|
|
|
}
|
|
|
|
|
|
|
|
this->m_data = std::move(intervals);
|
|
|
|
|
|
|
|
Provider::setBaseAddress(address);
|
|
|
|
}
|
|
|
|
|
2022-08-12 15:11:27 +02:00
|
|
|
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;
|
|
|
|
|
2022-09-04 11:16:20 +02:00
|
|
|
return true;
|
2022-08-12 15:11:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void IntelHexProvider::close() {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
[[nodiscard]] std::string IntelHexProvider::getName() const {
|
2022-10-04 09:10:58 +02:00
|
|
|
return hex::format("hex.builtin.provider.intel_hex.name"_lang, hex::toUTF8String(this->m_sourceFilePath.filename()));
|
2022-08-12 15:11:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool IntelHexProvider::handleFilePicker() {
|
2023-01-12 08:20:15 +01:00
|
|
|
auto picked = fs::openFileBrowser(fs::DialogMode::Open, {
|
|
|
|
{ "Intel Hex File", "hex" },
|
|
|
|
{ "Intel Hex File", "h86" },
|
|
|
|
{ "Intel Hex File", "hxl" },
|
|
|
|
{ "Intel Hex File", "hxh" },
|
|
|
|
{ "Intel Hex File", "obl" },
|
|
|
|
{ "Intel Hex File", "obh" },
|
|
|
|
{ "Intel Hex File", "mcs" },
|
|
|
|
{ "Intel Hex File", "ihex" },
|
|
|
|
{ "Intel Hex File", "ihe" },
|
|
|
|
{ "Intel Hex File", "ihx" },
|
|
|
|
{ "Intel Hex File", "a43" },
|
|
|
|
{ "Intel Hex File", "a90" }
|
|
|
|
}, [this](const std::fs::path &path) {
|
|
|
|
this->m_sourceFilePath = path;
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
2022-08-12 15:11:27 +02:00
|
|
|
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);
|
2022-08-14 14:45:18 +02:00
|
|
|
if (intervals.empty()) {
|
|
|
|
return Provider::getRegionValidity(address);
|
|
|
|
}
|
2022-08-12 15:11:27 +02:00
|
|
|
|
|
|
|
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 };
|
|
|
|
}
|
|
|
|
|
2022-08-14 10:07:45 +02:00
|
|
|
void IntelHexProvider::loadSettings(const nlohmann::json &settings) {
|
|
|
|
Provider::loadSettings(settings);
|
|
|
|
|
2022-10-04 09:10:58 +02:00
|
|
|
auto path = settings["path"].get<std::string>();
|
|
|
|
this->m_sourceFilePath = std::u8string(path.begin(), path.end());
|
2022-08-14 10:07:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
nlohmann::json IntelHexProvider::storeSettings(nlohmann::json settings) const {
|
2022-10-04 09:10:58 +02:00
|
|
|
settings["path"] = hex::toUTF8String(this->m_sourceFilePath);
|
2022-08-14 10:07:45 +02:00
|
|
|
|
|
|
|
return Provider::storeSettings(settings);
|
|
|
|
}
|
|
|
|
|
2022-08-12 15:11:27 +02:00
|
|
|
}
|