1
0
mirror of synced 2024-11-12 10:10:53 +01:00

sys: Added basic editing support to data inspector

This commit is contained in:
WerWolv 2022-02-15 21:50:27 +01:00
parent 227040f82f
commit d6b887b7db
5 changed files with 225 additions and 81 deletions

View File

@ -68,7 +68,8 @@ namespace hex {
/* Command Palette Command Registry. Allows adding of new commands to the command palette */
namespace CommandPaletteCommands {
enum class Type : u32 {
enum class Type : u32
{
SymbolCommand,
KeywordCommand
};
@ -173,7 +174,8 @@ namespace hex {
/* Data Inspector Registry. Allows adding of new types to the data inspector */
namespace DataInspector {
enum class NumberDisplayStyle {
enum class NumberDisplayStyle
{
Decimal,
Hexadecimal,
Octal
@ -182,17 +184,19 @@ namespace hex {
namespace impl {
using DisplayFunction = std::function<std::string()>;
using EditingFunction = std::function<std::vector<u8>(std::string, std::endian)>;
using GeneratorFunction = std::function<DisplayFunction(const std::vector<u8> &, std::endian, NumberDisplayStyle)>;
struct Entry {
std::string unlocalizedName;
size_t requiredSize;
impl::GeneratorFunction generatorFunction;
std::optional<impl::EditingFunction> editingFunction;
};
}
void add(const std::string &unlocalizedName, size_t requiredSize, impl::GeneratorFunction function);
void add(const std::string &unlocalizedName, size_t requiredSize, impl::GeneratorFunction displayGeneratorFunction, std::optional<impl::EditingFunction> editingFunction = std::nullopt);
std::vector<impl::Entry> &getEntries();
}

View File

@ -297,10 +297,10 @@ namespace hex {
namespace ContentRegistry::DataInspector {
void add(const std::string &unlocalizedName, size_t requiredSize, impl::GeneratorFunction function) {
void add(const std::string &unlocalizedName, size_t requiredSize, impl::GeneratorFunction displayGeneratorFunction, std::optional<impl::EditingFunction> editingFunction) {
log::info("Registered new data inspector format: {}", unlocalizedName);
getEntries().push_back({ unlocalizedName, requiredSize, std::move(function) });
getEntries().push_back({ unlocalizedName, requiredSize, std::move(displayGeneratorFunction), std::move(editingFunction) });
}
std::vector<impl::Entry> &getEntries() {

View File

@ -25,6 +25,8 @@ namespace hex::plugin::builtin {
struct InspectorCacheEntry {
std::string unlocalizedName;
ContentRegistry::DataInspector::impl::DisplayFunction displayFunction;
std::optional<ContentRegistry::DataInspector::impl::EditingFunction> editingFunction;
bool editing;
};
bool m_shouldInvalidate = true;
@ -35,6 +37,8 @@ namespace hex::plugin::builtin {
u64 m_startAddress = 0;
size_t m_validBytes = 0;
std::vector<InspectorCacheEntry> m_cachedData;
std::string m_editingValue;
};
}

View File

@ -23,112 +23,218 @@ namespace hex::plugin::builtin {
u8 data4[8];
};
template<std::integral T>
requires(sizeof(T) <= sizeof(u64)) static std::vector<u8> stringToUnsigned(const std::string &value, std::endian endian) {
u64 result = std::strtoull(value.c_str(), nullptr, 0);
if (result > std::numeric_limits<T>::max()) return {};
std::vector<u8> bytes(sizeof(T), 0x00);
std::memcpy(bytes.data(), &result, bytes.size());
if (endian != std::endian::native)
std::reverse(bytes.begin(), bytes.end());
return bytes;
}
template<std::integral T>
requires(sizeof(T) <= sizeof(u64)) static std::vector<u8> stringToSigned(const std::string &value, std::endian endian) {
i64 result = std::strtoull(value.c_str(), nullptr, 0);
if (result > std::numeric_limits<T>::max() || result < std::numeric_limits<T>::min()) return {};
std::vector<u8> bytes(sizeof(T), 0x00);
std::memcpy(bytes.data(), &result, bytes.size());
if (endian != std::endian::native)
std::reverse(bytes.begin(), bytes.end());
return bytes;
}
template<std::floating_point T>
requires(sizeof(T) <= sizeof(long double)) static std::vector<u8> stringToFloat(const std::string &value, std::endian endian) {
auto result = std::strtold(value.c_str(), nullptr);
std::vector<u8> bytes(sizeof(T), 0x00);
std::memcpy(bytes.data(), &result, bytes.size());
if (endian != std::endian::native)
std::reverse(bytes.begin(), bytes.end());
return bytes;
}
template<std::integral T>
requires(sizeof(T) <= sizeof(u64)) static std::vector<u8> stringToInteger(const std::string &value, std::endian endian) {
if constexpr (std::is_unsigned_v<T>)
return stringToUnsigned<T>(value, endian);
else if constexpr (std::is_signed_v<T>)
return stringToSigned<T>(value, endian);
else
return {};
}
void registerDataInspectorEntries() {
using Style = ContentRegistry::DataInspector::NumberDisplayStyle;
ContentRegistry::DataInspector::add("hex.builtin.inspector.binary", sizeof(u8), [](auto buffer, auto endian, auto style) {
std::string binary = "0b";
for (u8 i = 0; i < 8; i++)
binary += ((buffer[0] << i) & 0x80) == 0 ? '0' : '1';
ContentRegistry::DataInspector::add(
"hex.builtin.inspector.binary", sizeof(u8), [](auto buffer, auto endian, auto style) {
std::string binary = "0b";
for (u8 i = 0; i < 8; i++)
binary += ((buffer[0] << i) & 0x80) == 0 ? '0' : '1';
return [binary] {
ImGui::TextUnformatted(binary.c_str());
return binary;
};
});
return [binary] {
ImGui::TextUnformatted(binary.c_str());
return binary;
}; }, [](std::string value, std::endian endian) -> std::vector<u8> {
if (value.starts_with("0b"))
value = value.substr(2);
ContentRegistry::DataInspector::add("hex.builtin.inspector.u8", sizeof(u8), [](auto buffer, auto endian, auto style) {
auto format = (style == Style::Decimal) ? "{0:d}" : ((style == Style::Hexadecimal) ? "0x{0:02X}" : "0o{0:03o}");
if (value.size() > 8) return { };
u8 byte = 0x00;
for (char c : value) {
byte <<= 1;
auto value = hex::format(format, *reinterpret_cast<u8 *>(buffer.data()));
if (c == '1')
byte |= 0b01;
else if (c == '0')
byte |= 0b00;
else
return { };
}
return [value] { ImGui::TextUnformatted(value.c_str()); return value; };
});
return { byte }; });
ContentRegistry::DataInspector::add("hex.builtin.inspector.i8", sizeof(i8), [](auto buffer, auto endian, auto style) {
auto format = (style == Style::Decimal) ? "{0}{1:d}" : ((style == Style::Hexadecimal) ? "{0}0x{1:02X}" : "{0}0o{1:03o}");
ContentRegistry::DataInspector::add(
"hex.builtin.inspector.u8", sizeof(u8), [](auto buffer, auto endian, auto style) {
auto format = (style == Style::Decimal) ? "{0:d}" : ((style == Style::Hexadecimal) ? "0x{0:02X}" : "0o{0:03o}");
auto number = hex::changeEndianess(*reinterpret_cast<i8 *>(buffer.data()), endian);
bool negative = number < 0;
auto value = hex::format(format, negative ? "-" : "", std::abs(number));
auto value = hex::format(format, *reinterpret_cast<u8 *>(buffer.data()));
return [value] { ImGui::TextUnformatted(value.c_str()); return value; };
});
return [value] { ImGui::TextUnformatted(value.c_str()); return value; };
},
stringToInteger<u8>);
ContentRegistry::DataInspector::add("hex.builtin.inspector.u16", sizeof(u16), [](auto buffer, auto endian, auto style) {
auto format = (style == Style::Decimal) ? "{0:d}" : ((style == Style::Hexadecimal) ? "0x{0:04X}" : "0o{0:06o}");
ContentRegistry::DataInspector::add(
"hex.builtin.inspector.i8", sizeof(i8), [](auto buffer, auto endian, auto style) {
auto format = (style == Style::Decimal) ? "{0}{1:d}" : ((style == Style::Hexadecimal) ? "{0}0x{1:02X}" : "{0}0o{1:03o}");
auto value = hex::format(format, hex::changeEndianess(*reinterpret_cast<u16 *>(buffer.data()), endian));
auto number = hex::changeEndianess(*reinterpret_cast<i8 *>(buffer.data()), endian);
bool negative = number < 0;
auto value = hex::format(format, negative ? "-" : "", std::abs(number));
return [value] { ImGui::TextUnformatted(value.c_str()); return value; };
});
return [value] { ImGui::TextUnformatted(value.c_str()); return value; };
},
stringToInteger<i8>);
ContentRegistry::DataInspector::add("hex.builtin.inspector.i16", sizeof(i16), [](auto buffer, auto endian, auto style) {
auto format = (style == Style::Decimal) ? "{0}{1:d}" : ((style == Style::Hexadecimal) ? "{0}0x{1:04X}" : "{0}0o{1:06o}");
ContentRegistry::DataInspector::add(
"hex.builtin.inspector.u16", sizeof(u16), [](auto buffer, auto endian, auto style) {
auto format = (style == Style::Decimal) ? "{0:d}" : ((style == Style::Hexadecimal) ? "0x{0:04X}" : "0o{0:06o}");
auto number = hex::changeEndianess(*reinterpret_cast<i16 *>(buffer.data()), endian);
bool negative = number < 0;
auto value = hex::format(format, negative ? "-" : "", std::abs(number));
auto value = hex::format(format, hex::changeEndianess(*reinterpret_cast<u16 *>(buffer.data()), endian));
return [value] { ImGui::TextUnformatted(value.c_str()); return value; };
});
return [value] { ImGui::TextUnformatted(value.c_str()); return value; };
},
stringToInteger<u16>);
ContentRegistry::DataInspector::add("hex.builtin.inspector.u32", sizeof(u32), [](auto buffer, auto endian, auto style) {
auto format = (style == Style::Decimal) ? "{0:d}" : ((style == Style::Hexadecimal) ? "0x{0:08X}" : "0o{0:011o}");
ContentRegistry::DataInspector::add(
"hex.builtin.inspector.i16", sizeof(i16), [](auto buffer, auto endian, auto style) {
auto format = (style == Style::Decimal) ? "{0}{1:d}" : ((style == Style::Hexadecimal) ? "{0}0x{1:04X}" : "{0}0o{1:06o}");
auto value = hex::format(format, hex::changeEndianess(*reinterpret_cast<u32 *>(buffer.data()), endian));
auto number = hex::changeEndianess(*reinterpret_cast<i16 *>(buffer.data()), endian);
bool negative = number < 0;
auto value = hex::format(format, negative ? "-" : "", std::abs(number));
return [value] { ImGui::TextUnformatted(value.c_str()); return value; };
});
return [value] { ImGui::TextUnformatted(value.c_str()); return value; };
},
stringToInteger<i16>);
ContentRegistry::DataInspector::add("hex.builtin.inspector.i32", sizeof(i32), [](auto buffer, auto endian, auto style) {
auto format = (style == Style::Decimal) ? "{0}{1:d}" : ((style == Style::Hexadecimal) ? "{0}0x{1:08X}" : "{0}0o{1:011o}");
ContentRegistry::DataInspector::add(
"hex.builtin.inspector.u32", sizeof(u32), [](auto buffer, auto endian, auto style) {
auto format = (style == Style::Decimal) ? "{0:d}" : ((style == Style::Hexadecimal) ? "0x{0:08X}" : "0o{0:011o}");
auto number = hex::changeEndianess(*reinterpret_cast<i32 *>(buffer.data()), endian);
bool negative = number < 0;
auto value = hex::format(format, negative ? "-" : "", std::abs(number));
auto value = hex::format(format, hex::changeEndianess(*reinterpret_cast<u32 *>(buffer.data()), endian));
return [value] { ImGui::TextUnformatted(value.c_str()); return value; };
});
return [value] { ImGui::TextUnformatted(value.c_str()); return value; };
},
stringToInteger<u32>);
ContentRegistry::DataInspector::add("hex.builtin.inspector.u64", sizeof(u64), [](auto buffer, auto endian, auto style) {
auto format = (style == Style::Decimal) ? "{0:d}" : ((style == Style::Hexadecimal) ? "0x{0:016X}" : "0o{0:022o}");
ContentRegistry::DataInspector::add(
"hex.builtin.inspector.i32", sizeof(i32), [](auto buffer, auto endian, auto style) {
auto format = (style == Style::Decimal) ? "{0}{1:d}" : ((style == Style::Hexadecimal) ? "{0}0x{1:08X}" : "{0}0o{1:011o}");
auto value = hex::format(format, hex::changeEndianess(*reinterpret_cast<u64 *>(buffer.data()), endian));
auto number = hex::changeEndianess(*reinterpret_cast<i32 *>(buffer.data()), endian);
bool negative = number < 0;
auto value = hex::format(format, negative ? "-" : "", std::abs(number));
return [value] { ImGui::TextUnformatted(value.c_str()); return value; };
});
return [value] { ImGui::TextUnformatted(value.c_str()); return value; };
},
stringToInteger<i32>);
ContentRegistry::DataInspector::add("hex.builtin.inspector.i64", sizeof(i64), [](auto buffer, auto endian, auto style) {
auto format = (style == Style::Decimal) ? "{0}{1:d}" : ((style == Style::Hexadecimal) ? "{0}0x{1:016X}" : "{0}0o{1:022o}");
ContentRegistry::DataInspector::add(
"hex.builtin.inspector.u64", sizeof(u64), [](auto buffer, auto endian, auto style) {
auto format = (style == Style::Decimal) ? "{0:d}" : ((style == Style::Hexadecimal) ? "0x{0:016X}" : "0o{0:022o}");
auto number = hex::changeEndianess(*reinterpret_cast<i64 *>(buffer.data()), endian);
bool negative = number < 0;
auto value = hex::format(format, negative ? "-" : "", std::abs(number));
auto value = hex::format(format, hex::changeEndianess(*reinterpret_cast<u64 *>(buffer.data()), endian));
return [value] { ImGui::TextUnformatted(value.c_str()); return value; };
});
return [value] { ImGui::TextUnformatted(value.c_str()); return value; };
},
stringToInteger<u64>);
ContentRegistry::DataInspector::add(
"hex.builtin.inspector.i64", sizeof(i64), [](auto buffer, auto endian, auto style) {
auto format = (style == Style::Decimal) ? "{0}{1:d}" : ((style == Style::Hexadecimal) ? "{0}0x{1:016X}" : "{0}0o{1:022o}");
auto number = hex::changeEndianess(*reinterpret_cast<i64 *>(buffer.data()), endian);
bool negative = number < 0;
auto value = hex::format(format, negative ? "-" : "", std::abs(number));
return [value] { ImGui::TextUnformatted(value.c_str()); return value; };
},
stringToInteger<i64>);
ContentRegistry::DataInspector::add("hex.builtin.inspector.float16", sizeof(u16), [](auto buffer, auto endian, auto style) {
auto value = hex::format("{0:G}", hex::changeEndianess(float16ToFloat32(*reinterpret_cast<u16 *>(buffer.data())), endian));
return [value] { ImGui::TextUnformatted(value.c_str()); return value; };
});
ContentRegistry::DataInspector::add("hex.builtin.inspector.float", sizeof(float), [](auto buffer, auto endian, auto style) {
auto value = hex::format("{0:G}", hex::changeEndianess(*reinterpret_cast<float *>(buffer.data()), endian));
return [value] { ImGui::TextUnformatted(value.c_str()); return value; };
});
ContentRegistry::DataInspector::add(
"hex.builtin.inspector.float", sizeof(float), [](auto buffer, auto endian, auto style) {
auto value = hex::format("{0:G}", hex::changeEndianess(*reinterpret_cast<float *>(buffer.data()), endian));
return [value] { ImGui::TextUnformatted(value.c_str()); return value; };
},
stringToFloat<float>);
ContentRegistry::DataInspector::add("hex.builtin.inspector.double", sizeof(double), [](auto buffer, auto endian, auto style) {
auto value = hex::format("{0:G}", hex::changeEndianess(*reinterpret_cast<float *>(buffer.data()), endian));
return [value] { ImGui::TextUnformatted(value.c_str()); return value; };
});
ContentRegistry::DataInspector::add(
"hex.builtin.inspector.double", sizeof(double), [](auto buffer, auto endian, auto style) {
auto value = hex::format("{0:G}", hex::changeEndianess(*reinterpret_cast<double *>(buffer.data()), endian));
return [value] { ImGui::TextUnformatted(value.c_str()); return value; };
},
stringToFloat<double>);
ContentRegistry::DataInspector::add("hex.builtin.inspector.ascii", sizeof(char8_t), [](auto buffer, auto endian, auto style) {
auto value = hex::format("'{0}'", makePrintable(*reinterpret_cast<char8_t *>(buffer.data())).c_str());
return [value] { ImGui::TextUnformatted(value.c_str()); return value; };
});
ContentRegistry::DataInspector::add(
"hex.builtin.inspector.long_double", sizeof(long double), [](auto buffer, auto endian, auto style) {
auto value = hex::format("{0:G}", hex::changeEndianess(*reinterpret_cast<long double *>(buffer.data()), endian));
return [value] { ImGui::TextUnformatted(value.c_str()); return value; };
},
stringToFloat<long double>);
ContentRegistry::DataInspector::add(
"hex.builtin.inspector.ascii", sizeof(char8_t), [](auto buffer, auto endian, auto style) {
auto value = hex::format("'{0}'", makePrintable(*reinterpret_cast<char8_t *>(buffer.data())).c_str());
return [value] { ImGui::TextUnformatted(value.c_str()); return value; }; }, [](const std::string &value, std::endian endian) -> std::vector<u8> {
if (value.length() > 3) return { };
u8 character = 0x00;
if (value.length() == 3 && value.starts_with('\'') && value.ends_with('\''))
character = value[1];
else if (value.length() == 1)
character = value[0];
else
return { };
return { character }; });
ContentRegistry::DataInspector::add("hex.builtin.inspector.wide", sizeof(wchar_t), [](auto buffer, auto endian, auto style) {
wchar_t wideChar = '\x00';
@ -250,7 +356,7 @@ namespace hex::plugin::builtin {
auto stringValue = hex::format("(0x{:02X}, 0x{:02X}, 0x{:02X}, 0x{:02X})", u8(0xFF * (value.Value.x)), u8(0xFF * (value.Value.y)), u8(0xFF * (value.Value.z)), u8(0xFF * (value.Value.w)));
return [value, stringValue] {
ImGui::ColorButton("##inspectorColor", value, ImGuiColorEditFlags_None, ImVec2(ImGui::GetColumnWidth(), ImGui::GetTextLineHeight()));
ImGui::ColorButton("##inspectorColor", value, ImGuiColorEditFlags_None, ImVec2(ImGui::GetContentRegionAvail().x, ImGui::GetTextLineHeight()));
return stringValue;
};
});

View File

@ -1,5 +1,7 @@
#include "content/views/view_data_inspector.hpp"
#include <hex/ui/imgui_imhex_extensions.h>
#include <hex/providers/provider.hpp>
#include <cstring>
@ -42,7 +44,7 @@ namespace hex::plugin::builtin {
std::vector<u8> buffer(entry.requiredSize);
provider->read(this->m_startAddress, buffer.data(), buffer.size());
this->m_cachedData.push_back({ entry.unlocalizedName, entry.generatorFunction(buffer, this->m_endian, this->m_numberDisplayStyle) });
this->m_cachedData.push_back({ entry.unlocalizedName, entry.generatorFunction(buffer, this->m_endian, this->m_numberDisplayStyle), entry.editingFunction, false });
}
}
@ -59,16 +61,44 @@ namespace hex::plugin::builtin {
ImGui::TableHeadersRow();
u32 i = 0;
for (const auto &[unlocalizedName, function] : this->m_cachedData) {
for (auto &[unlocalizedName, displayFunction, editingFunction, editing] : this->m_cachedData) {
ImGui::PushID(i);
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextUnformatted(LangEntry(unlocalizedName));
ImGui::TableNextColumn();
const auto &copyValue = function();
ImGui::SameLine();
if (ImGui::Selectable("##InspectorLine", false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowItemOverlap)) {
ImGui::SetClipboardText(copyValue.c_str());
if (!editing) {
const auto &copyValue = displayFunction();
ImGui::SameLine();
if (ImGui::Selectable("##InspectorLine", false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowItemOverlap)) {
ImGui::SetClipboardText(copyValue.c_str());
}
if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left) && editingFunction.has_value()) {
editing = true;
this->m_editingValue = copyValue;
}
} else {
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
ImGui::SetKeyboardFocusHere();
if (ImGui::InputText("##InspectorLineEditing", this->m_editingValue.data(), this->m_editingValue.capacity(), ImGuiInputTextFlags_CallbackResize | ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll, ImGui::UpdateStringSizeCallback, &this->m_editingValue)) {
auto bytes = (*editingFunction)(this->m_editingValue, this->m_endian);
provider->write(this->m_startAddress, bytes.data(), bytes.size());
this->m_editingValue.clear();
editing = false;
this->m_shouldInvalidate = true;
}
ImGui::PopStyleVar();
if (!ImGui::IsItemHovered() && ImGui::IsAnyMouseDown()) {
this->m_editingValue.clear();
editing = false;
}
}
ImGui::PopID();