1
0
mirror of synced 2025-01-18 09:04:52 +01:00

Added proper data inspector view

This commit is contained in:
WerWolv 2020-11-21 00:12:58 +01:00
parent 9c0a270d90
commit bf6ed3d540
7 changed files with 219 additions and 209 deletions

View File

@ -36,6 +36,7 @@ add_executable(ImHex
source/views/view_help.cpp
source/views/view_tools.cpp
source/views/view_strings.cpp
source/views/view_data_inspector.cpp
libs/glad/source/glad.c

View File

@ -8,7 +8,8 @@ namespace hex {
enum class Events {
DataChanged,
PatternChanged,
FileDropped
FileDropped,
ByteSelected
};
struct EventHandler {

View File

@ -0,0 +1,57 @@
#pragma once
#include "views/view.hpp"
#include <cstdio>
#include <ctime>
#include <string>
namespace hex {
namespace prv { class Provider; }
struct GUID {
u32 data1;
u16 data2;
u16 data3;
u8 data4[8];
};
union PreviewData {
u8 unsigned8;
s8 signed8;
u16 unsigned16;
s16 signed16;
u32 unsigned32;
s32 signed32;
u64 unsigned64;
s64 signed64;
char8_t ansiChar;
char16_t wideChar;
u8 utf8Char[4];
float float32;
double float64;
__time32_t time32;
__time64_t time64;
GUID guid;
};
class ViewDataInspector : public View {
public:
explicit ViewDataInspector(prv::Provider* &dataProvider);
~ViewDataInspector() override;
void createView() override;
void createMenu() override;
private:
prv::Provider* &m_dataProvider;
bool m_windowOpen = true;
bool m_shouldInvalidate = true;
PreviewData m_previewData = { 0 };
size_t m_validBytes = 0;
std::vector<std::pair<std::string, std::string>> m_cachedData;
};
}

View File

@ -47,6 +47,8 @@
#include <stdio.h> // sprintf, scanf
#include <stdint.h> // uint8_t, etc.
#include "views/view.hpp"
#ifdef _MSC_VER
#define _PRISizeT "I"
#define ImSnprintf _snprintf
@ -77,7 +79,6 @@ struct MemoryEditor
bool ReadOnly; // = false // disable any editing.
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 OptShowDataPreview; // = false // display a footer previewing the decimal/binary/hex/float representation of the currently selected bytes.
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 OptGreyOutZeroes; // = true // display null/zero bytes using the TextDisabled color.
@ -109,7 +110,6 @@ struct MemoryEditor
ReadOnly = false;
Cols = 16;
OptShowOptions = true;
OptShowDataPreview = false;
OptShowHexII = false;
OptShowAscii = true;
OptGreyOutZeroes = true;
@ -189,8 +189,6 @@ struct MemoryEditor
if (ImGui::Begin(title, &Open, ImGuiWindowFlags_NoScrollbar))
{
if (ImGui::IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows) && ImGui::IsMouseReleased(ImGuiMouseButton_Right))
ImGui::OpenPopup("context");
DrawContents(mem_data, mem_size, base_display_addr);
if (ContentsWidthChanged)
{
@ -229,8 +227,6 @@ struct MemoryEditor
float footer_height = 0;
if (OptShowOptions)
footer_height += height_separator + ImGui::GetFrameHeightWithSpacing() * 1;
if (OptShowDataPreview)
footer_height += height_separator + ImGui::GetFrameHeightWithSpacing() * 1 + ImGui::GetTextLineHeightWithSpacing() * 3;
ImGui::BeginChild("offset", ImVec2(0, s.LineHeight), false, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav);
ImGui::Text("% *c ", s.AddrDigitsCount, ' ');
@ -267,8 +263,6 @@ struct MemoryEditor
if (DataPreviewAddrEnd >= mem_size)
DataPreviewAddrEnd = (size_t)-1;
size_t preview_data_type_size = OptShowDataPreview ? DataTypeGetSize(PreviewDataType) : 0;
size_t data_editing_addr_backup = DataEditingAddr;
size_t data_editing_addr_next = (size_t)-1;
if (DataEditingAddr != (size_t)-1)
@ -428,9 +422,15 @@ struct MemoryEditor
DataPreviewAddr = addr;
DataPreviewAddrEnd = addr;
hex::View::postEvent(hex::Events::ByteSelected, &DataPreviewAddr);
}
if (!ReadOnly && ImGui::IsItemHovered() && ((ImGui::IsMouseClicked(0) && ImGui::GetIO().KeyShift) || ImGui::IsMouseDragging(0))) {
DataPreviewAddrEnd = addr;
size_t dataPreviewStart = std::min(DataPreviewAddr, DataPreviewAddrEnd);
hex::View::postEvent(hex::Events::ByteSelected, &dataPreviewStart);
}
}
}
@ -513,19 +513,12 @@ struct MemoryEditor
DataEditingAddr = DataPreviewAddr = data_editing_addr_next;
}
const bool lock_show_data_preview = OptShowDataPreview;
if (OptShowOptions)
{
ImGui::Separator();
DrawOptionsLine(s, mem_data, mem_size, base_display_addr);
}
if (lock_show_data_preview)
{
ImGui::Separator();
DrawPreviewLine(s, mem_data, mem_size, base_display_addr);
}
// Notify the main window of our ideal child content size (FIXME: we are missing an API to get the contents size from the child)
ImGui::SetCursorPosX(s.WindowWidth);
}
@ -538,13 +531,12 @@ struct MemoryEditor
// Options menu
if (ImGui::Button("Options"))
ImGui::OpenPopup("context");
if (ImGui::BeginPopup("context"))
{
ImGui::OpenPopup("options");
if (ImGui::BeginPopup("options")) {
ImGui::PushItemWidth(56);
if (ImGui::DragInt("##cols", &Cols, 0.2f, 4, 32, "%d cols")) { ContentsWidthChanged = true; if (Cols < 1) Cols = 1; }
ImGui::PopItemWidth();
ImGui::Checkbox("Show Data Preview", &OptShowDataPreview);
ImGui::Checkbox("Show HexII", &OptShowHexII);
if (ImGui::Checkbox("Show Ascii", &OptShowAscii)) { ContentsWidthChanged = true; }
ImGui::Checkbox("Grey out zeroes", &OptGreyOutZeroes);
@ -582,66 +574,7 @@ struct MemoryEditor
}
}
void DrawPreviewLine(const Sizes& s, void* mem_data_void, size_t mem_size, size_t base_display_addr)
{
IM_UNUSED(base_display_addr);
ImU8* mem_data = (ImU8*)mem_data_void;
ImGuiStyle& style = ImGui::GetStyle();
ImGui::AlignTextToFramePadding();
ImGui::Text("Preview as:");
ImGui::SameLine();
ImGui::PushItemWidth((s.GlyphWidth * 10.0f) + style.FramePadding.x * 2.0f + style.ItemInnerSpacing.x);
if (ImGui::BeginCombo("##combo_type", DataTypeGetDesc(PreviewDataType), ImGuiComboFlags_HeightLargest))
{
for (int n = 0; n < ImGuiDataType_COUNT; n++)
if (ImGui::Selectable(DataTypeGetDesc((ImGuiDataType)n), PreviewDataType == n))
PreviewDataType = (ImGuiDataType)n;
ImGui::EndCombo();
}
ImGui::PopItemWidth();
ImGui::SameLine();
ImGui::PushItemWidth((s.GlyphWidth * 6.0f) + style.FramePadding.x * 2.0f + style.ItemInnerSpacing.x);
ImGui::Combo("##combo_endianess", &PreviewEndianess, "LE\0BE\0\0");
ImGui::PopItemWidth();
char buf[128] = "";
float x = s.GlyphWidth * 6.0f;
bool has_value = DataPreviewAddr != (size_t)-1;
if (has_value)
DrawPreviewData(DataPreviewAddr, mem_data, mem_size, PreviewDataType, DataFormat_Dec, buf, (size_t)IM_ARRAYSIZE(buf));
ImGui::Text("Dec"); ImGui::SameLine(x); ImGui::TextUnformatted(has_value ? buf : "N/A");
if (has_value)
DrawPreviewData(DataPreviewAddr, mem_data, mem_size, PreviewDataType, DataFormat_Hex, buf, (size_t)IM_ARRAYSIZE(buf));
ImGui::Text("Hex"); ImGui::SameLine(x); ImGui::TextUnformatted(has_value ? buf : "N/A");
if (has_value)
DrawPreviewData(DataPreviewAddr, mem_data, mem_size, PreviewDataType, DataFormat_Bin, buf, (size_t)IM_ARRAYSIZE(buf));
buf[IM_ARRAYSIZE(buf) - 1] = 0;
ImGui::Text("Bin"); ImGui::SameLine(x); ImGui::TextUnformatted(has_value ? buf : "N/A");
}
// Utilities for Data Preview
const char* DataTypeGetDesc(ImGuiDataType data_type) const
{
const char* descs[] = { "Int8", "Uint8", "Int16", "Uint16", "Int32", "Uint32", "Int64", "Uint64", "Float", "Double" };
IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT);
return descs[data_type];
}
size_t DataTypeGetSize(ImGuiDataType data_type) const
{
const size_t sizes[] = { 1, 1, 2, 2, 4, 4, 8, 8, sizeof(float), sizeof(double) };
IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT);
return sizes[data_type];
}
const char* DataFormatGetDesc(DataFormat data_format) const
{
const char* descs[] = { "Bin", "Dec", "Hex" };
IM_ASSERT(data_format >= 0 && data_format < DataFormat_COUNT);
return descs[data_format];
}
bool IsBigEndian() const
static bool IsBigEndian()
{
uint16_t x = 1;
char c[2];
@ -688,132 +621,6 @@ struct MemoryEditor
fp = IsBigEndian() ? EndianessCopyBigEndian : EndianessCopyLittleEndian;
return fp(dst, src, size, PreviewEndianess);
}
const char* FormatBinary(const uint8_t* buf, int width) const
{
IM_ASSERT(width <= 64);
size_t out_n = 0;
static char out_buf[64 + 8 + 1];
int n = width / 8;
for (int j = n - 1; j >= 0; --j)
{
for (int i = 0; i < 8; ++i)
out_buf[out_n++] = (buf[j] & (1 << (7 - i))) ? '1' : '0';
out_buf[out_n++] = ' ';
}
IM_ASSERT(out_n < IM_ARRAYSIZE(out_buf));
out_buf[out_n] = 0;
return out_buf;
}
// [Internal]
void DrawPreviewData(size_t addr, const ImU8* mem_data, size_t mem_size, ImGuiDataType data_type, DataFormat data_format, char* out_buf, size_t out_buf_size) const
{
uint8_t buf[8];
size_t elem_size = DataTypeGetSize(data_type);
size_t size = addr + elem_size > mem_size ? mem_size - addr : elem_size;
if (ReadFn)
for (int i = 0, n = (int)size; i < n; ++i)
buf[i] = ReadFn(mem_data, addr + i);
else
memcpy(buf, mem_data + addr, size);
if (data_format == DataFormat_Bin)
{
uint8_t binbuf[8];
EndianessCopy(binbuf, buf, size);
ImSnprintf(out_buf, out_buf_size, "%s", FormatBinary(binbuf, (int)size * 8));
return;
}
out_buf[0] = 0;
switch (data_type)
{
case ImGuiDataType_S8:
{
int8_t int8 = 0;
EndianessCopy(&int8, buf, size);
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hhd", int8); return; }
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%02x", int8 & 0xFF); return; }
break;
}
case ImGuiDataType_U8:
{
uint8_t uint8 = 0;
EndianessCopy(&uint8, buf, size);
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hhu", uint8); return; }
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%02x", uint8 & 0XFF); return; }
break;
}
case ImGuiDataType_S16:
{
int16_t int16 = 0;
EndianessCopy(&int16, buf, size);
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hd", int16); return; }
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%04x", int16 & 0xFFFF); return; }
break;
}
case ImGuiDataType_U16:
{
uint16_t uint16 = 0;
EndianessCopy(&uint16, buf, size);
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hu", uint16); return; }
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%04x", uint16 & 0xFFFF); return; }
break;
}
case ImGuiDataType_S32:
{
int32_t int32 = 0;
EndianessCopy(&int32, buf, size);
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%d", int32); return; }
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%08x", int32); return; }
break;
}
case ImGuiDataType_U32:
{
uint32_t uint32 = 0;
EndianessCopy(&uint32, buf, size);
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%u", uint32); return; }
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%08x", uint32); return; }
break;
}
case ImGuiDataType_S64:
{
int64_t int64 = 0;
EndianessCopy(&int64, buf, size);
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%lld", (long long)int64); return; }
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%016llx", (long long)int64); return; }
break;
}
case ImGuiDataType_U64:
{
uint64_t uint64 = 0;
EndianessCopy(&uint64, buf, size);
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%llu", (long long)uint64); return; }
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%016llx", (long long)uint64); return; }
break;
}
case ImGuiDataType_Float:
{
float float32 = 0.0f;
EndianessCopy(&float32, buf, size);
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%f", float32); return; }
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "%a", float32); return; }
break;
}
case ImGuiDataType_Double:
{
double float64 = 0.0;
EndianessCopy(&float64, buf, size);
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%f", float64); return; }
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "%a", float64); return; }
break;
}
case ImGuiDataType_COUNT:
break;
} // Switch
IM_ASSERT(0); // Shouldn't reach
}
};
#undef _PRISizeT

View File

@ -9,6 +9,7 @@
#include "views/view_help.hpp"
#include "views/view_tools.hpp"
#include "views/view_strings.hpp"
#include "views/view_data_inspector.hpp"
#include "providers/provider.hpp"
@ -25,6 +26,7 @@ int main() {
window.addView<hex::ViewHexEditor>(dataProvider, patternData);
window.addView<hex::ViewPattern>(dataProvider, patternData);
window.addView<hex::ViewPatternData>(dataProvider, patternData);
window.addView<hex::ViewDataInspector>(dataProvider);
window.addView<hex::ViewHashes>(dataProvider);
window.addView<hex::ViewInformation>(dataProvider);
window.addView<hex::ViewStrings>(dataProvider);

View File

@ -0,0 +1,140 @@
#include "views/view_data_inspector.hpp"
#include "providers/provider.hpp"
#include "utils.hpp"
#include <cstring>
extern int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end);
namespace hex {
ViewDataInspector::ViewDataInspector(prv::Provider* &dataProvider) : View(), m_dataProvider(dataProvider) {
View::subscribeEvent(Events::ByteSelected, [this](const void* userData){
size_t offset = *static_cast<const size_t*>(userData);
this->m_validBytes = std::min(this->m_dataProvider->getSize() - offset, sizeof(PreviewData));
std::memset(&this->m_previewData, 0x00, sizeof(PreviewData));
this->m_dataProvider->read(offset, &this->m_previewData, sizeof(PreviewData));
this->m_shouldInvalidate = true;
});
}
ViewDataInspector::~ViewDataInspector() {
View::unsubscribeEvent(Events::ByteSelected);
}
void ViewDataInspector::createView() {
if (!this->m_windowOpen)
return;
if (this->m_shouldInvalidate) {
this->m_shouldInvalidate = false;
this->m_cachedData.clear();
{
std::string binary;
for (u8 i = 0; i < 8; i++)
binary += ((this->m_previewData.unsigned8 << i) & 0x80) == 0 ? '0' : '1';
this->m_cachedData.emplace_back("Binary (8 bit)", binary);
}
this->m_cachedData.emplace_back("uint8_t", hex::format("%u", this->m_previewData.unsigned8));
this->m_cachedData.emplace_back("int8_t", hex::format("%d", this->m_previewData.unsigned8));
this->m_cachedData.emplace_back("uint16_t", hex::format("%u", this->m_previewData.unsigned16));
this->m_cachedData.emplace_back("int16_t", hex::format("%d", this->m_previewData.unsigned16));
this->m_cachedData.emplace_back("uint32_t", hex::format("%lu", this->m_previewData.unsigned32));
this->m_cachedData.emplace_back("int32_t", hex::format("%ld", this->m_previewData.unsigned32));
this->m_cachedData.emplace_back("uint64_t", hex::format("%llu", this->m_previewData.unsigned64));
this->m_cachedData.emplace_back("int64_t", hex::format("%lld", this->m_previewData.unsigned64));
this->m_cachedData.emplace_back("ANSI Character / char8_t", hex::format("%c", this->m_previewData.ansiChar));
this->m_cachedData.emplace_back("Wide Character / char16_t", hex::format("%lc", this->m_previewData.wideChar));
{
char buffer[5] = { 0 };
std::memcpy(buffer, &this->m_previewData.utf8Char, 4);
u32 utf8 = 0;
ImTextCharFromUtf8(&utf8, buffer, buffer + 4);
this->m_cachedData.emplace_back("UTF-8 code point", hex::format("U+%08lx", utf8));
}
this->m_cachedData.emplace_back("float (32 bit)", hex::format("%e", this->m_previewData.float32));
this->m_cachedData.emplace_back("double (64 bit)", hex::format("%e", this->m_previewData.float64));
{
std::tm * ptm = _localtime32(&this->m_previewData.time32);
char buffer[32];
if (std::strftime(buffer, 32, "%a, %d.%m.%Y %H:%M:%S", ptm))
this->m_cachedData.emplace_back("__time32_t", buffer);
else
this->m_cachedData.emplace_back("__time32_t", "Invalid");
}
{
std::tm * ptm = _localtime64(&this->m_previewData.time64);
char buffer[64];
if (std::strftime(buffer, 64, "%a, %d.%m.%Y %H:%M:%S", ptm) != 0)
this->m_cachedData.emplace_back("__time64_t", buffer);
else
this->m_cachedData.emplace_back("__time64_t", "Invalid");
}
this->m_cachedData.emplace_back("GUID", hex::format("{%08lX-%04hX-%04hX-%02hhX%02hhX-%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX}",
this->m_previewData.guid.data1, this->m_previewData.guid.data2, this->m_previewData.guid.data3,
this->m_previewData.guid.data4[0], this->m_previewData.guid.data4[1], this->m_previewData.guid.data4[2], this->m_previewData.guid.data4[3],
this->m_previewData.guid.data4[4], this->m_previewData.guid.data4[5], this->m_previewData.guid.data4[6], this->m_previewData.guid.data4[7]));
}
if (ImGui::Begin("Data Preview", &this->m_windowOpen)) {
if (this->m_dataProvider != nullptr && this->m_dataProvider->isReadable()) {
if (ImGui::BeginChild("##scrolling")) {
if (ImGui::BeginTable("##datainspector", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg)) {
ImGui::TableSetupColumn("Name");
ImGui::TableSetupColumn("Value");
ImGui::TableHeadersRow();
u32 rowCount = 0;
for (const auto &[name, value] : this->m_cachedData) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextUnformatted(name.c_str());
ImGui::TableNextColumn();
ImGui::TextUnformatted(value.c_str());
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0,
((rowCount % 2) == 0) ? 0xFF101010 : 0xFF303030);
rowCount++;
}
{
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextUnformatted("RGBA Color");
ImGui::TableNextColumn();
ImGui::ColorButton("##nolabel", ImColor(this->m_previewData.unsigned32),
ImGuiColorEditFlags_None, ImVec2(ImGui::GetColumnWidth(), 15));
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0,
((rowCount % 2) == 0) ? 0xFF101010 : 0xFF303030);
rowCount++;
}
ImGui::EndTable();
}
}
ImGui::EndChild();
}
}
ImGui::End();
}
void ViewDataInspector::createMenu() {
if (ImGui::BeginMenu("View")) {
ImGui::MenuItem("Data Preview View", "", &this->m_windowOpen);
ImGui::EndMenu();
}
}
}

View File

@ -96,10 +96,12 @@ namespace hex {
return;
if (ImGui::Begin("Pattern", &this->m_windowOpen, ImGuiWindowFlags_None)) {
this->m_textEditor.Render("Pattern");
if (this->m_dataProvider != nullptr && this->m_dataProvider->isAvailable()) {
this->m_textEditor.Render("Pattern");
if (this->m_textEditor.IsTextChanged()) {
this->parsePattern(this->m_textEditor.GetText().data());
if (this->m_textEditor.IsTextChanged()) {
this->parsePattern(this->m_textEditor.GetText().data());
}
}
}
ImGui::End();