Implemented crude support for custom encodings via thingy files
Relevant issue: #26
This commit is contained in:
parent
424bba71f7
commit
b4c2f7d371
@ -42,6 +42,7 @@ add_executable(imhex ${application_type}
|
|||||||
source/helpers/project_file_handler.cpp
|
source/helpers/project_file_handler.cpp
|
||||||
source/helpers/loader_script_handler.cpp
|
source/helpers/loader_script_handler.cpp
|
||||||
source/helpers/plugin_handler.cpp
|
source/helpers/plugin_handler.cpp
|
||||||
|
source/helpers/encoding_file.cpp
|
||||||
|
|
||||||
source/providers/file_provider.cpp
|
source/providers/file_provider.cpp
|
||||||
|
|
||||||
|
125
external/ImGui/include/imgui_memory_editor.h
vendored
125
external/ImGui/include/imgui_memory_editor.h
vendored
@ -50,6 +50,8 @@
|
|||||||
|
|
||||||
#include <hex/views/view.hpp>
|
#include <hex/views/view.hpp>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
#define _PRISizeT "I"
|
#define _PRISizeT "I"
|
||||||
#define ImSnprintf _snprintf
|
#define ImSnprintf _snprintf
|
||||||
@ -75,12 +77,19 @@ struct MemoryEditor
|
|||||||
DataFormat_COUNT
|
DataFormat_COUNT
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct DecodeData {
|
||||||
|
std::string data;
|
||||||
|
size_t advance;
|
||||||
|
ImColor color;
|
||||||
|
};
|
||||||
|
|
||||||
// Settings
|
// Settings
|
||||||
bool ReadOnly; // = false // disable any editing.
|
bool ReadOnly; // = false // disable any editing.
|
||||||
int Cols; // = 16 // number of columns to display.
|
int Cols; // = 16 // number of columns to display.
|
||||||
bool OptShowOptions; // = true // display options button/context menu. when disabled, options will be locked unless you provide your own UI for them.
|
bool OptShowOptions; // = true // display options button/context menu. when disabled, options will be locked unless you provide your own UI for them.
|
||||||
bool OptShowHexII; // = false // display values in HexII representation instead of regular hexadecimal: hide null/zero bytes, ascii values as ".X".
|
bool OptShowHexII; // = false // display values in HexII representation instead of regular hexadecimal: hide null/zero bytes, ascii values as ".X".
|
||||||
bool OptShowAscii; // = true // display ASCII representation on the right side.
|
bool OptShowAscii; // = true // display ASCII representation on the right side.
|
||||||
|
bool OptShowAdvancedDecoding; // = true // display advanced decoding data on the right side.
|
||||||
bool OptGreyOutZeroes; // = true // display null/zero bytes using the TextDisabled color.
|
bool OptGreyOutZeroes; // = true // display null/zero bytes using the TextDisabled color.
|
||||||
bool OptUpperCaseHex; // = true // display hexadecimal values as "FF" instead of "ff".
|
bool OptUpperCaseHex; // = true // display hexadecimal values as "FF" instead of "ff".
|
||||||
int OptMidColsCount; // = 8 // set to 0 to disable extra spacing between every mid-cols.
|
int OptMidColsCount; // = 8 // set to 0 to disable extra spacing between every mid-cols.
|
||||||
@ -90,6 +99,7 @@ struct MemoryEditor
|
|||||||
void (*WriteFn)(ImU8* data, size_t off, ImU8 d); // = 0 // optional handler to write bytes.
|
void (*WriteFn)(ImU8* data, size_t off, ImU8 d); // = 0 // optional handler to write bytes.
|
||||||
bool (*HighlightFn)(const ImU8* data, size_t off, bool next);//= 0 // optional handler to return Highlight property (to support non-contiguous highlighting).
|
bool (*HighlightFn)(const ImU8* data, size_t off, bool next);//= 0 // optional handler to return Highlight property (to support non-contiguous highlighting).
|
||||||
void (*HoverFn)(const ImU8 *data, size_t off);
|
void (*HoverFn)(const ImU8 *data, size_t off);
|
||||||
|
DecodeData (*DecodeFn)(const ImU8 *data, size_t off);
|
||||||
|
|
||||||
// [Internal State]
|
// [Internal State]
|
||||||
bool ContentsWidthChanged;
|
bool ContentsWidthChanged;
|
||||||
@ -114,6 +124,7 @@ struct MemoryEditor
|
|||||||
OptShowOptions = true;
|
OptShowOptions = true;
|
||||||
OptShowHexII = false;
|
OptShowHexII = false;
|
||||||
OptShowAscii = true;
|
OptShowAscii = true;
|
||||||
|
OptShowAdvancedDecoding = true;
|
||||||
OptGreyOutZeroes = true;
|
OptGreyOutZeroes = true;
|
||||||
OptUpperCaseHex = true;
|
OptUpperCaseHex = true;
|
||||||
OptMidColsCount = 8;
|
OptMidColsCount = 8;
|
||||||
@ -123,6 +134,7 @@ struct MemoryEditor
|
|||||||
WriteFn = NULL;
|
WriteFn = NULL;
|
||||||
HighlightFn = NULL;
|
HighlightFn = NULL;
|
||||||
HoverFn = NULL;
|
HoverFn = NULL;
|
||||||
|
DecodeFn = NULL;
|
||||||
|
|
||||||
// State/Internals
|
// State/Internals
|
||||||
ContentsWidthChanged = false;
|
ContentsWidthChanged = false;
|
||||||
@ -155,6 +167,8 @@ struct MemoryEditor
|
|||||||
float PosHexEnd;
|
float PosHexEnd;
|
||||||
float PosAsciiStart;
|
float PosAsciiStart;
|
||||||
float PosAsciiEnd;
|
float PosAsciiEnd;
|
||||||
|
float PosDecodingStart;
|
||||||
|
float PosDecodingEnd;
|
||||||
float WindowWidth;
|
float WindowWidth;
|
||||||
|
|
||||||
Sizes() { memset(this, 0, sizeof(*this)); }
|
Sizes() { memset(this, 0, sizeof(*this)); }
|
||||||
@ -174,12 +188,27 @@ struct MemoryEditor
|
|||||||
s.PosHexStart = (s.AddrDigitsCount + 2) * s.GlyphWidth;
|
s.PosHexStart = (s.AddrDigitsCount + 2) * s.GlyphWidth;
|
||||||
s.PosHexEnd = s.PosHexStart + (s.HexCellWidth * Cols);
|
s.PosHexEnd = s.PosHexStart + (s.HexCellWidth * Cols);
|
||||||
s.PosAsciiStart = s.PosAsciiEnd = s.PosHexEnd;
|
s.PosAsciiStart = s.PosAsciiEnd = s.PosHexEnd;
|
||||||
if (OptShowAscii)
|
|
||||||
{
|
if (OptShowAscii && OptShowAdvancedDecoding) {
|
||||||
s.PosAsciiStart = s.PosHexEnd + s.GlyphWidth * 1;
|
s.PosAsciiStart = s.PosHexEnd + s.GlyphWidth * 1;
|
||||||
if (OptMidColsCount > 0)
|
if (OptMidColsCount > 0)
|
||||||
s.PosAsciiStart += (float)((Cols + OptMidColsCount - 1) / OptMidColsCount) * s.SpacingBetweenMidCols;
|
s.PosAsciiStart += (float)((Cols + OptMidColsCount - 1) / OptMidColsCount) * s.SpacingBetweenMidCols;
|
||||||
s.PosAsciiEnd = s.PosAsciiStart + Cols * s.GlyphWidth;
|
s.PosAsciiEnd = s.PosAsciiStart + Cols * s.GlyphWidth;
|
||||||
|
|
||||||
|
s.PosDecodingStart = s.PosAsciiEnd + s.GlyphWidth * 1;
|
||||||
|
if (OptMidColsCount > 0)
|
||||||
|
s.PosDecodingStart += (float)((Cols + OptMidColsCount - 1) / OptMidColsCount) * s.SpacingBetweenMidCols;
|
||||||
|
s.PosDecodingEnd = s.PosDecodingStart + Cols * s.GlyphWidth;
|
||||||
|
} else if (OptShowAscii) {
|
||||||
|
s.PosAsciiStart = s.PosHexEnd + s.GlyphWidth * 1;
|
||||||
|
if (OptMidColsCount > 0)
|
||||||
|
s.PosAsciiStart += (float)((Cols + OptMidColsCount - 1) / OptMidColsCount) * s.SpacingBetweenMidCols;
|
||||||
|
s.PosAsciiEnd = s.PosAsciiStart + Cols * s.GlyphWidth;
|
||||||
|
} else if (OptShowAdvancedDecoding) {
|
||||||
|
s.PosDecodingStart = s.PosHexEnd + s.GlyphWidth * 1;
|
||||||
|
if (OptMidColsCount > 0)
|
||||||
|
s.PosDecodingStart += (float)((Cols + OptMidColsCount - 1) / OptMidColsCount) * s.SpacingBetweenMidCols;
|
||||||
|
s.PosDecodingEnd = s.PosDecodingStart + Cols * s.GlyphWidth;
|
||||||
}
|
}
|
||||||
s.WindowWidth = s.PosAsciiEnd + style.ScrollbarSize + style.WindowPadding.x * 2 + s.GlyphWidth;
|
s.WindowWidth = s.PosAsciiEnd + style.ScrollbarSize + style.WindowPadding.x * 2 + s.GlyphWidth;
|
||||||
}
|
}
|
||||||
@ -222,15 +251,6 @@ struct MemoryEditor
|
|||||||
CalcSizes(s, mem_size, base_display_addr);
|
CalcSizes(s, mem_size, base_display_addr);
|
||||||
ImGuiStyle& style = ImGui::GetStyle();
|
ImGuiStyle& style = ImGui::GetStyle();
|
||||||
|
|
||||||
if (mem_size == 0x00) {
|
|
||||||
constexpr const char *noDataString = "No data loaded!";
|
|
||||||
|
|
||||||
auto pos = ImGui::GetCursorScreenPos();
|
|
||||||
pos.x += (ImGui::GetWindowWidth() - (ImGui::CalcTextSize(noDataString).x)) / 2;
|
|
||||||
ImGui::GetWindowDrawList()->AddText(pos, 0xFFFFFFFF, noDataString);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We begin into our scrolling region with the 'ImGuiWindowFlags_NoMove' in order to prevent click from moving the window.
|
// We begin into our scrolling region with the 'ImGuiWindowFlags_NoMove' in order to prevent click from moving the window.
|
||||||
// This is used as a facility since our main click detection code doesn't assign an ActiveId so the click would normally be caught as a window-move.
|
// This is used as a facility since our main click detection code doesn't assign an ActiveId so the click would normally be caught as a window-move.
|
||||||
const float height_separator = style.ItemSpacing.y;
|
const float height_separator = style.ItemSpacing.y;
|
||||||
@ -327,6 +347,8 @@ struct MemoryEditor
|
|||||||
ImVec2 window_pos = ImGui::GetWindowPos();
|
ImVec2 window_pos = ImGui::GetWindowPos();
|
||||||
if (OptShowAscii)
|
if (OptShowAscii)
|
||||||
draw_list->AddLine(ImVec2(window_pos.x + s.PosAsciiStart - s.GlyphWidth, window_pos.y), ImVec2(window_pos.x + s.PosAsciiStart - s.GlyphWidth, window_pos.y + 9999), ImGui::GetColorU32(ImGuiCol_Border));
|
draw_list->AddLine(ImVec2(window_pos.x + s.PosAsciiStart - s.GlyphWidth, window_pos.y), ImVec2(window_pos.x + s.PosAsciiStart - s.GlyphWidth, window_pos.y + 9999), ImGui::GetColorU32(ImGuiCol_Border));
|
||||||
|
if (OptShowAdvancedDecoding)
|
||||||
|
draw_list->AddLine(ImVec2(window_pos.x + s.PosDecodingStart - s.GlyphWidth, window_pos.y), ImVec2(window_pos.x + s.PosDecodingStart - s.GlyphWidth, window_pos.y + 9999), ImGui::GetColorU32(ImGuiCol_Border));
|
||||||
|
|
||||||
const ImU32 color_text = ImGui::GetColorU32(ImGuiCol_Text);
|
const ImU32 color_text = ImGui::GetColorU32(ImGuiCol_Text);
|
||||||
const ImU32 color_disabled = OptGreyOutZeroes ? ImGui::GetColorU32(ImGuiCol_TextDisabled) : color_text;
|
const ImU32 color_disabled = OptGreyOutZeroes ? ImGui::GetColorU32(ImGuiCol_TextDisabled) : color_text;
|
||||||
@ -541,6 +563,86 @@ struct MemoryEditor
|
|||||||
|
|
||||||
pos.x += s.GlyphWidth;
|
pos.x += s.GlyphWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImGui::PushID(-1);
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::Dummy(ImVec2(s.GlyphWidth, s.LineHeight));
|
||||||
|
|
||||||
|
ImGui::PopID();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (OptShowAdvancedDecoding && DecodeFn) {
|
||||||
|
// Draw decoded bytes
|
||||||
|
ImGui::SameLine(s.PosDecodingStart);
|
||||||
|
ImVec2 pos = ImGui::GetCursorScreenPos();
|
||||||
|
addr = line_i * Cols;
|
||||||
|
|
||||||
|
ImGui::PushID(-1);
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::Dummy(ImVec2(s.GlyphWidth, s.LineHeight));
|
||||||
|
|
||||||
|
ImGui::PopID();
|
||||||
|
|
||||||
|
for (int n = 0; n < Cols && addr < mem_size;)
|
||||||
|
{
|
||||||
|
auto decodedData = DecodeFn(mem_data, addr);
|
||||||
|
|
||||||
|
auto displayData = decodedData.data;
|
||||||
|
auto decodedDataLength = displayData.length();
|
||||||
|
|
||||||
|
if (addr == DataEditingAddr)
|
||||||
|
{
|
||||||
|
draw_list->AddRectFilled(pos, ImVec2(pos.x + s.GlyphWidth * decodedDataLength, pos.y + s.LineHeight), ImGui::GetColorU32(ImGuiCol_FrameBg));
|
||||||
|
draw_list->AddRectFilled(pos, ImVec2(pos.x + s.GlyphWidth * decodedDataLength, pos.y + s.LineHeight), ImGui::GetColorU32(ImGuiCol_TextSelectedBg));
|
||||||
|
}
|
||||||
|
|
||||||
|
draw_list->AddText(pos, decodedData.color, displayData.c_str(), displayData.c_str() + decodedDataLength);
|
||||||
|
|
||||||
|
// Draw highlight
|
||||||
|
bool is_highlight_from_user_range = (addr >= HighlightMin && addr < HighlightMax);
|
||||||
|
bool is_highlight_from_user_func = (HighlightFn && HighlightFn(mem_data, addr, false));
|
||||||
|
bool is_highlight_from_preview = (addr >= DataPreviewAddr && addr <= DataPreviewAddrEnd) || (addr >= DataPreviewAddrEnd && addr <= DataPreviewAddr);
|
||||||
|
if (is_highlight_from_user_range || is_highlight_from_user_func || is_highlight_from_preview)
|
||||||
|
{
|
||||||
|
ImU32 color = HighlightColor;
|
||||||
|
if ((is_highlight_from_user_range + is_highlight_from_user_func + is_highlight_from_preview) > 1)
|
||||||
|
color = (ImAlphaBlendColors(HighlightColor, 0x60C08080) & 0x00FFFFFF) | 0x90000000;
|
||||||
|
|
||||||
|
draw_list->AddRectFilled(pos, ImVec2(pos.x + s.GlyphWidth * decodedDataLength, pos.y + s.LineHeight), color);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ImGui::PushID(line_i * Cols + n);
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::Dummy(ImVec2(s.GlyphWidth * decodedDataLength, s.LineHeight));
|
||||||
|
|
||||||
|
ImGui::PopID();
|
||||||
|
|
||||||
|
if (ImGui::IsItemHovered() && ImGui::IsMouseClicked(0) && !ImGui::GetIO().KeyShift)
|
||||||
|
{
|
||||||
|
if (!ReadOnly && ImGui::IsMouseDoubleClicked(0)) {
|
||||||
|
DataEditingTakeFocus = true;
|
||||||
|
data_editing_addr_next = addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
DataPreviewAddr = addr;
|
||||||
|
DataPreviewAddrEnd = addr;
|
||||||
|
|
||||||
|
}
|
||||||
|
if (ImGui::IsItemHovered() && ((ImGui::IsMouseClicked(0) && ImGui::GetIO().KeyShift) || ImGui::IsMouseDragging(0))) {
|
||||||
|
DataPreviewAddrEnd = addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
pos.x += s.GlyphWidth * decodedDataLength;
|
||||||
|
|
||||||
|
if (addr <= 1) {
|
||||||
|
n++;
|
||||||
|
addr++;
|
||||||
|
} else {
|
||||||
|
n += decodedData.advance;
|
||||||
|
addr += decodedData.advance;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
IM_ASSERT(clipper.Step() == false);
|
IM_ASSERT(clipper.Step() == false);
|
||||||
@ -585,6 +687,7 @@ struct MemoryEditor
|
|||||||
ImGui::PopItemWidth();
|
ImGui::PopItemWidth();
|
||||||
ImGui::Checkbox("Show HexII", &OptShowHexII);
|
ImGui::Checkbox("Show HexII", &OptShowHexII);
|
||||||
if (ImGui::Checkbox("Show Ascii", &OptShowAscii)) { ContentsWidthChanged = true; }
|
if (ImGui::Checkbox("Show Ascii", &OptShowAscii)) { ContentsWidthChanged = true; }
|
||||||
|
if (ImGui::Checkbox("Show Advanced Decoding", &OptShowAdvancedDecoding)) { ContentsWidthChanged = true; }
|
||||||
ImGui::Checkbox("Grey out zeroes", &OptGreyOutZeroes);
|
ImGui::Checkbox("Grey out zeroes", &OptGreyOutZeroes);
|
||||||
ImGui::Checkbox("Uppercase Hex", &OptUpperCaseHex);
|
ImGui::Checkbox("Uppercase Hex", &OptUpperCaseHex);
|
||||||
|
|
||||||
|
37
include/helpers/encoding_file.hpp
Normal file
37
include/helpers/encoding_file.hpp
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <hex.hpp>
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace hex {
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct SizeSorter {
|
||||||
|
bool operator() (const T& lhs, const T& rhs) const {
|
||||||
|
return lhs.size() < rhs.size();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class EncodingFile {
|
||||||
|
public:
|
||||||
|
enum class Type {
|
||||||
|
Thingy,
|
||||||
|
CSV
|
||||||
|
};
|
||||||
|
|
||||||
|
EncodingFile() = default;
|
||||||
|
EncodingFile(Type type, std::string_view path);
|
||||||
|
|
||||||
|
std::pair<std::string_view, size_t> getEncodingFor(const std::vector<u8> &buffer) const;
|
||||||
|
size_t getLongestSequence() const { return this->m_longestSequence; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
void parseThingyFile(std::ifstream &content);
|
||||||
|
|
||||||
|
std::map<u32, std::map<std::vector<u8>, std::string>> m_mapping;
|
||||||
|
size_t m_longestSequence = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <hex/helpers/utils.hpp>
|
#include <hex/helpers/utils.hpp>
|
||||||
#include <hex/views/view.hpp>
|
#include <hex/views/view.hpp>
|
||||||
|
#include "helpers/encoding_file.hpp"
|
||||||
|
|
||||||
#include <imgui_memory_editor.h>
|
#include <imgui_memory_editor.h>
|
||||||
#include <ImGuiFileBrowser.h>
|
#include <ImGuiFileBrowser.h>
|
||||||
@ -54,6 +55,8 @@ namespace hex {
|
|||||||
std::string m_loaderScriptScriptPath;
|
std::string m_loaderScriptScriptPath;
|
||||||
std::string m_loaderScriptFilePath;
|
std::string m_loaderScriptFilePath;
|
||||||
|
|
||||||
|
hex::EncodingFile m_currEncodingFile;
|
||||||
|
|
||||||
void drawSearchPopup();
|
void drawSearchPopup();
|
||||||
void drawGotoPopup();
|
void drawGotoPopup();
|
||||||
void drawEditPopup();
|
void drawEditPopup();
|
||||||
|
@ -130,6 +130,7 @@ namespace hex::plugin::builtin {
|
|||||||
{ "hex.view.hexeditor.save_project", "Save Project" },
|
{ "hex.view.hexeditor.save_project", "Save Project" },
|
||||||
{ "hex.view.hexeditor.save_data", "Save Data" },
|
{ "hex.view.hexeditor.save_data", "Save Data" },
|
||||||
{ "hex.view.hexeditor.open_base64", "Open Base64 File" },
|
{ "hex.view.hexeditor.open_base64", "Open Base64 File" },
|
||||||
|
{ "hex.view.hexeditor.load_enconding_file", "Load custom encoding File" },
|
||||||
{ "hex.view.hexeditor.page", "Page %d / %d" },
|
{ "hex.view.hexeditor.page", "Page %d / %d" },
|
||||||
{ "hex.view.hexeditor.save_as", "Save As" },
|
{ "hex.view.hexeditor.save_as", "Save As" },
|
||||||
{ "hex.view.hexeditor.save_changes.title", "Save Changes" },
|
{ "hex.view.hexeditor.save_changes.title", "Save Changes" },
|
||||||
@ -146,6 +147,7 @@ namespace hex::plugin::builtin {
|
|||||||
{ "hex.view.hexeditor.menu.file.save_as", "Save As..." },
|
{ "hex.view.hexeditor.menu.file.save_as", "Save As..." },
|
||||||
{ "hex.view.hexeditor.menu.file.open_project", "Open Project..." },
|
{ "hex.view.hexeditor.menu.file.open_project", "Open Project..." },
|
||||||
{ "hex.view.hexeditor.menu.file.save_project", "Save Project..." },
|
{ "hex.view.hexeditor.menu.file.save_project", "Save Project..." },
|
||||||
|
{ "hex.view.hexeditor.menu.file.load_encoding_file", "Load custom encoding..." },
|
||||||
{ "hex.view.hexeditor.menu.file.import", "Import..." },
|
{ "hex.view.hexeditor.menu.file.import", "Import..." },
|
||||||
{ "hex.view.hexeditor.menu.file.import.base64", "Base64 File" },
|
{ "hex.view.hexeditor.menu.file.import.base64", "Base64 File" },
|
||||||
{ "hex.view.hexeditor.base64.import_error", "File is not in a valid Base64 format!" },
|
{ "hex.view.hexeditor.base64.import_error", "File is not in a valid Base64 format!" },
|
||||||
|
@ -180,6 +180,23 @@ namespace hex {
|
|||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline std::vector<u8> parseByteString(std::string_view string) {
|
||||||
|
auto byteString = std::string(string);
|
||||||
|
byteString.erase(std::remove(byteString.begin(), byteString.end(), ' '), byteString.end());
|
||||||
|
|
||||||
|
if ((byteString.length() % 2) != 0) return { };
|
||||||
|
|
||||||
|
std::vector<u8> result;
|
||||||
|
for (u32 i = 0; i < byteString.length(); i += 2) {
|
||||||
|
if (!std::isxdigit(byteString[i]) || !std::isxdigit(byteString[i + 1]))
|
||||||
|
return { };
|
||||||
|
|
||||||
|
result.push_back(std::strtoul(byteString.substr(i, 2).c_str(), nullptr, 16));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
inline std::string toBinaryString(hex::integral auto number) {
|
inline std::string toBinaryString(hex::integral auto number) {
|
||||||
if (number == 0) return "0";
|
if (number == 0) return "0";
|
||||||
|
|
||||||
@ -190,6 +207,23 @@ namespace hex {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void trimLeft(std::string &s) {
|
||||||
|
s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) {
|
||||||
|
return !std::isspace(ch);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void trimRight(std::string &s) {
|
||||||
|
s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) {
|
||||||
|
return !std::isspace(ch);
|
||||||
|
}).base(), s.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void trim(std::string &s) {
|
||||||
|
trimLeft(s);
|
||||||
|
trimRight(s);
|
||||||
|
}
|
||||||
|
|
||||||
#define SCOPE_EXIT(func) ScopeExit TOKEN_CONCAT(scopeGuard, __COUNTER__)([&] { func })
|
#define SCOPE_EXIT(func) ScopeExit TOKEN_CONCAT(scopeGuard, __COUNTER__)([&] { func })
|
||||||
class ScopeExit {
|
class ScopeExit {
|
||||||
public:
|
public:
|
||||||
|
@ -30,8 +30,6 @@ namespace hex {
|
|||||||
json[unlocalizedCategory.data()] = nlohmann::json::object();
|
json[unlocalizedCategory.data()] = nlohmann::json::object();
|
||||||
if (!json[unlocalizedCategory.data()].contains(unlocalizedName.data()))
|
if (!json[unlocalizedCategory.data()].contains(unlocalizedName.data()))
|
||||||
json[unlocalizedCategory.data()][unlocalizedName.data()] = defaultValue;
|
json[unlocalizedCategory.data()][unlocalizedName.data()] = defaultValue;
|
||||||
|
|
||||||
Settings::store();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ContentRegistry::Settings::add(std::string_view unlocalizedCategory, std::string_view unlocalizedName, std::string_view defaultValue, const std::function<bool(std::string_view, nlohmann::json&)> &callback) {
|
void ContentRegistry::Settings::add(std::string_view unlocalizedCategory, std::string_view unlocalizedName, std::string_view defaultValue, const std::function<bool(std::string_view, nlohmann::json&)> &callback) {
|
||||||
@ -43,8 +41,6 @@ namespace hex {
|
|||||||
json[unlocalizedCategory.data()] = nlohmann::json::object();
|
json[unlocalizedCategory.data()] = nlohmann::json::object();
|
||||||
if (!json[unlocalizedCategory.data()].contains(unlocalizedName.data()))
|
if (!json[unlocalizedCategory.data()].contains(unlocalizedName.data()))
|
||||||
json[unlocalizedCategory.data()][unlocalizedName.data()] = defaultValue;
|
json[unlocalizedCategory.data()][unlocalizedName.data()] = defaultValue;
|
||||||
|
|
||||||
Settings::store();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ContentRegistry::Settings::write(std::string_view unlocalizedCategory, std::string_view unlocalizedName, s64 value) {
|
void ContentRegistry::Settings::write(std::string_view unlocalizedCategory, std::string_view unlocalizedName, s64 value) {
|
||||||
|
53
source/helpers/encoding_file.cpp
Normal file
53
source/helpers/encoding_file.cpp
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
#include "helpers/encoding_file.hpp"
|
||||||
|
|
||||||
|
#include <hex/helpers/utils.hpp>
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
namespace hex {
|
||||||
|
|
||||||
|
EncodingFile::EncodingFile(Type type, std::string_view path) {
|
||||||
|
std::ifstream encodingFile(path.data());
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case Type::Thingy: parseThingyFile(encodingFile); break;
|
||||||
|
default: throw std::runtime_error("Invalid encoding file type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<std::string_view, size_t> EncodingFile::getEncodingFor(const std::vector<u8> &buffer) const {
|
||||||
|
for (auto iter = this->m_mapping.rbegin(); iter != this->m_mapping.rend(); iter++) {
|
||||||
|
auto &[size, mapping] = *iter;
|
||||||
|
|
||||||
|
if (size > buffer.size()) continue;
|
||||||
|
|
||||||
|
auto key = std::vector<u8>(buffer.begin(), buffer.begin() + size);
|
||||||
|
if (mapping.contains(key))
|
||||||
|
return { mapping.at(key), size };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { ".", 1 };
|
||||||
|
}
|
||||||
|
|
||||||
|
void EncodingFile::parseThingyFile(std::ifstream &content) {
|
||||||
|
for (std::string line; std::getline(content, line);) {
|
||||||
|
auto entry = hex::splitString(line, "=");
|
||||||
|
|
||||||
|
if (entry.size() != 2) return;
|
||||||
|
|
||||||
|
auto &from = entry[0];
|
||||||
|
auto &to = entry[1];
|
||||||
|
|
||||||
|
hex::trim(from);
|
||||||
|
hex::trim(to);
|
||||||
|
|
||||||
|
auto fromBytes = hex::parseByteString(from);
|
||||||
|
if (!this->m_mapping.contains(fromBytes.size()))
|
||||||
|
this->m_mapping.insert({ fromBytes.size(), { } });
|
||||||
|
this->m_mapping[fromBytes.size()].insert({ fromBytes, to });
|
||||||
|
|
||||||
|
this->m_longestSequence = std::max(this->m_longestSequence, fromBytes.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -99,6 +99,30 @@ namespace hex {
|
|||||||
ImGui::EndTooltip();
|
ImGui::EndTooltip();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this->m_memoryEditor.DecodeFn = [](const ImU8 *data, size_t addr) -> MemoryEditor::DecodeData {
|
||||||
|
ViewHexEditor *_this = (ViewHexEditor *) data;
|
||||||
|
|
||||||
|
if (_this->m_currEncodingFile.getLongestSequence() == 0)
|
||||||
|
return { ".", 1, 0xFFFF8000 };
|
||||||
|
|
||||||
|
auto &provider = SharedData::currentProvider;
|
||||||
|
size_t size = std::min<size_t>(_this->m_currEncodingFile.getLongestSequence(), provider->getActualSize() - addr);
|
||||||
|
|
||||||
|
std::vector<u8> buffer(size);
|
||||||
|
provider->read(addr, buffer.data(), size);
|
||||||
|
|
||||||
|
auto [decoded, advance] = _this->m_currEncodingFile.getEncodingFor(buffer);
|
||||||
|
|
||||||
|
ImColor color;
|
||||||
|
if (decoded.length() == 1 && std::isalnum(decoded[0])) color = 0xFFFF8000;
|
||||||
|
else if (decoded.length() == 1 && advance == 1) color = 0xFF0000FF;
|
||||||
|
else if (decoded.length() > 1 && advance == 1) color = 0xFF00FFFF;
|
||||||
|
else if (advance > 1) color = 0xFFFFFFFF;
|
||||||
|
else color = 0xFFFF8000;
|
||||||
|
|
||||||
|
return { std::string(decoded), advance, color };
|
||||||
|
};
|
||||||
|
|
||||||
View::subscribeEvent(Events::FileDropped, [this](auto userData) {
|
View::subscribeEvent(Events::FileDropped, [this](auto userData) {
|
||||||
auto filePath = std::any_cast<const char*>(userData);
|
auto filePath = std::any_cast<const char*>(userData);
|
||||||
|
|
||||||
@ -358,6 +382,12 @@ namespace hex {
|
|||||||
ProjectFile::store();
|
ProjectFile::store();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ImGui::MenuItem("hex.view.hexeditor.menu.file.load_encoding_file"_lang)) {
|
||||||
|
View::openFileBrowser("hex.view.hexeditor.load_enconding_file"_lang, imgui_addons::ImGuiFileBrowser::DialogMode::OPEN, "*.*", [this](auto path) {
|
||||||
|
this->m_currEncodingFile = EncodingFile(EncodingFile::Type::Thingy, path);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
|
|
||||||
if (ImGui::BeginMenu("hex.view.hexeditor.menu.file.import"_lang)) {
|
if (ImGui::BeginMenu("hex.view.hexeditor.menu.file.import"_lang)) {
|
||||||
@ -515,6 +545,9 @@ namespace hex {
|
|||||||
|
|
||||||
if (!provider->isAvailable()) {
|
if (!provider->isAvailable()) {
|
||||||
View::showErrorPopup("hex.view.hexeditor.error.open"_lang);
|
View::showErrorPopup("hex.view.hexeditor.error.open"_lang);
|
||||||
|
delete provider;
|
||||||
|
provider = nullptr;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user