add terminal mode for rntviewer
This commit is contained in:
parent
d00db42d7a
commit
79787d6c2d
9 changed files with 358 additions and 39 deletions
|
@ -17,6 +17,8 @@ struct Inspected_File {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct App_State {
|
struct App_State {
|
||||||
|
u8 should_quit;
|
||||||
|
|
||||||
Window_Data win_data;
|
Window_Data win_data;
|
||||||
User_Input user_input;
|
User_Input user_input;
|
||||||
RNTuple_Data rndata;
|
RNTuple_Data rndata;
|
||||||
|
|
|
@ -52,8 +52,7 @@ void run_main_loop(GLFWwindow *window, Arena *arena, App_State &app)
|
||||||
app.delta_time_accum.max = 100;
|
app.delta_time_accum.max = 100;
|
||||||
app.delta_time_accum.base = arena_push_array<f32>(arena, app.delta_time_accum.max);
|
app.delta_time_accum.base = arena_push_array<f32>(arena, app.delta_time_accum.max);
|
||||||
|
|
||||||
b8 running = true;
|
while (!app.should_quit) {
|
||||||
while (running) {
|
|
||||||
chr::time_point frame_start = chr::high_resolution_clock::now();
|
chr::time_point frame_start = chr::high_resolution_clock::now();
|
||||||
u64 time_since_prev_frame_us = chr::duration_cast<chr::microseconds>(frame_start - last_saved_time).count();
|
u64 time_since_prev_frame_us = chr::duration_cast<chr::microseconds>(frame_start - last_saved_time).count();
|
||||||
delta_time_ms = time_since_prev_frame_us * 0.001f;
|
delta_time_ms = time_since_prev_frame_us * 0.001f;
|
||||||
|
@ -86,7 +85,7 @@ void run_main_loop(GLFWwindow *window, Arena *arena, App_State &app)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS || glfwWindowShouldClose(window)) {
|
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS || glfwWindowShouldClose(window)) {
|
||||||
running = false;
|
app.should_quit = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,11 +11,11 @@ i32 os_page_size()
|
||||||
}
|
}
|
||||||
|
|
||||||
internal
|
internal
|
||||||
bool os_open_and_map_file(const char *fname, App_State &app)
|
bool os_open_and_map_file(String8 fname, App_State &app)
|
||||||
{
|
{
|
||||||
FILE *file = fopen(fname, "rb");
|
FILE *file = fopen(fname.c(), "rb");
|
||||||
if (!file) {
|
if (!file) {
|
||||||
fprintf(stderr, "Failed to open file '%s' for reading: %s (%d)\n", fname, strerror(errno), errno);
|
fprintf(stderr, "Failed to open file '%s' for reading: %s (%d)\n", fname.c(), strerror(errno), errno);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
int fd = fileno(file);
|
int fd = fileno(file);
|
||||||
|
@ -23,11 +23,11 @@ bool os_open_and_map_file(const char *fname, App_State &app)
|
||||||
|
|
||||||
void *fmem = mmap(0, fsize, PROT_READ, MAP_SHARED_VALIDATE, fd, 0);
|
void *fmem = mmap(0, fsize, PROT_READ, MAP_SHARED_VALIDATE, fd, 0);
|
||||||
if (!fmem) {
|
if (!fmem) {
|
||||||
fprintf(stderr, "Failed to open file %s\n", fname);
|
fprintf(stderr, "Failed to open file %s\n", fname.c());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
app.inspected_file.name = str8(fname);
|
app.inspected_file.name = fname;
|
||||||
app.inspected_file.stream = file;
|
app.inspected_file.stream = file;
|
||||||
app.inspected_file.size = fsize;
|
app.inspected_file.size = fsize;
|
||||||
app.inspected_file.mem = reinterpret_cast<u8*>(fmem);
|
app.inspected_file.mem = reinterpret_cast<u8*>(fmem);
|
||||||
|
@ -43,12 +43,12 @@ void os_unmap_file(u8 *&mem, u64 size)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal
|
internal
|
||||||
void os_start_file_watch(const char *fname, App_State &app)
|
void os_start_file_watch(String8 fname, App_State &app)
|
||||||
{
|
{
|
||||||
int inot = inotify_init1(IN_NONBLOCK);
|
int inot = inotify_init1(IN_NONBLOCK);
|
||||||
if (inot == -1)
|
if (inot == -1)
|
||||||
fprintf(stderr, "Failed to init inotify: %s (%d)\n", strerror(errno), errno);
|
fprintf(stderr, "Failed to init inotify: %s (%d)\n", strerror(errno), errno);
|
||||||
if (inotify_add_watch(inot, fname, IN_MODIFY) == -1)
|
if (inotify_add_watch(inot, fname.c(), IN_MODIFY) == -1)
|
||||||
fprintf(stderr, "Failed to add inotify watch: %s (%d)\n", strerror(errno), errno);
|
fprintf(stderr, "Failed to add inotify watch: %s (%d)\n", strerror(errno), errno);
|
||||||
|
|
||||||
app.inspected_file.inot = inot;
|
app.inspected_file.inot = inot;
|
||||||
|
|
178
src/render.cpp
178
src/render.cpp
|
@ -1,8 +1,8 @@
|
||||||
internal
|
internal
|
||||||
std::optional<Byte_Range> get_section_range(const App_State &app, Section_Id sec)
|
Byte_Range get_section_range(const App_State &app, Section_Id sec)
|
||||||
{
|
{
|
||||||
switch (sec) {
|
switch (sec) {
|
||||||
default: return {};
|
default: return { 0, 0 };
|
||||||
case Sec_TFile_Header: return Byte_Range { 0, app.tfile_data.root_file_header_size };
|
case Sec_TFile_Header: return Byte_Range { 0, app.tfile_data.root_file_header_size };
|
||||||
case Sec_TFile_Object: return app.tfile_data.rng_root_file_obj;
|
case Sec_TFile_Object: return app.tfile_data.rng_root_file_obj;
|
||||||
case Sec_TFile_Info: return app.tfile_data.rng_root_file_info;
|
case Sec_TFile_Info: return app.tfile_data.rng_root_file_info;
|
||||||
|
@ -127,6 +127,12 @@ Section find_section(App_State &app, u64 off)
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal
|
||||||
|
ImColor imcol(const f32 col[3])
|
||||||
|
{
|
||||||
|
return ImColor(col[0], col[1], col[2]);
|
||||||
|
}
|
||||||
|
|
||||||
internal
|
internal
|
||||||
u32 mem_edit_bg_color_fn(const u8 *, u64 off, void *user_data)
|
u32 mem_edit_bg_color_fn(const u8 *, u64 off, void *user_data)
|
||||||
{
|
{
|
||||||
|
@ -135,13 +141,11 @@ u32 mem_edit_bg_color_fn(const u8 *, u64 off, void *user_data)
|
||||||
|
|
||||||
Section section = find_section(*app, off);
|
Section section = find_section(*app, off);
|
||||||
|
|
||||||
#define COL(c) (ImColor((c)[0], (c)[1], (c)[2]))
|
if (section.highlighted) return imcol(app->viewer.col_highlight);
|
||||||
if (section.highlighted) return COL(app->viewer.col_highlight);
|
if (section.id == Sec_Page && off == section.range.start) return imcol(app->viewer.col_page_start);
|
||||||
if (section.id == Sec_Page && off == section.range.start) return COL(app->viewer.col_page_start);
|
if (off < section.range.start) return imcol(app->viewer.col_key);
|
||||||
if (off < section.range.start) return COL(app->viewer.col_key);
|
if (section.range.end() - section.post_size <= off && off < section.range.end()) return imcol(app->viewer.col_checksum);
|
||||||
if (section.range.end() - section.post_size <= off && off < section.range.end()) return COL(app->viewer.col_checksum);
|
return imcol(app->viewer.col_section[section.id]);
|
||||||
return COL(app->viewer.col_section[section.id]);
|
|
||||||
#undef COL
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal
|
internal
|
||||||
|
@ -150,10 +154,10 @@ void mem_edit_interact_fn(const u8 *, u64 off, void *user_data)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal
|
internal
|
||||||
MemoryEditor make_memory_editor(App_State &app)
|
MemoryEditor make_memory_editor(App_State &app, i32 n_cols)
|
||||||
{
|
{
|
||||||
MemoryEditor mem_edit;
|
MemoryEditor mem_edit;
|
||||||
mem_edit.Cols = 32;
|
mem_edit.Cols = n_cols ? n_cols : 32;
|
||||||
mem_edit.OptShowDataPreview = true;
|
mem_edit.OptShowDataPreview = true;
|
||||||
// Do nothing on write.
|
// Do nothing on write.
|
||||||
// Note that we don't use ReadOnly = true because that disables selecting bytes
|
// Note that we don't use ReadOnly = true because that disables selecting bytes
|
||||||
|
@ -166,10 +170,10 @@ MemoryEditor make_memory_editor(App_State &app)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal
|
internal
|
||||||
void make_viewer(App_State &app)
|
void make_viewer(App_State &app, u16 n_cols)
|
||||||
{
|
{
|
||||||
Viewer viewer {};
|
Viewer viewer {};
|
||||||
viewer.mem_edit = make_memory_editor(app);
|
viewer.mem_edit = make_memory_editor(app, (i32)n_cols);
|
||||||
|
|
||||||
#define COL(c, r, g, b) viewer.c[0] = r/255.0, viewer.c[1] = g/255.0, viewer.c[2] = b/255.0
|
#define COL(c, r, g, b) viewer.c[0] = r/255.0, viewer.c[1] = g/255.0, viewer.c[2] = b/255.0
|
||||||
COL(col_key, 0, 100, 50);
|
COL(col_key, 0, 100, 50);
|
||||||
|
@ -306,17 +310,17 @@ void update_and_render(Arena *arena, App_State &app, f32 delta_time_ms)
|
||||||
|
|
||||||
// Unique sections: just display a button that jumps to the start of it and show their size
|
// Unique sections: just display a button that jumps to the start of it and show their size
|
||||||
for (u32 i = 0; i < Sec_COUNT; ++i) {
|
for (u32 i = 0; i < Sec_COUNT; ++i) {
|
||||||
std::optional<Byte_Range> range = get_section_range(app, static_cast<Section_Id>(i));
|
Byte_Range range = get_section_range(app, static_cast<Section_Id>(i));
|
||||||
if (!range) continue;
|
if (!range.len) continue;
|
||||||
|
|
||||||
String8 sec_name = section_names[i];
|
String8 sec_name = section_names[i];
|
||||||
String8 col_label = push_str8f(scratch.arena, "_%s", sec_name.c());
|
String8 col_label = push_str8f(scratch.arena, "_%s", sec_name.c());
|
||||||
ImGui::ColorEdit3(col_label.c(), app.viewer.col_section[i], flags);
|
ImGui::ColorEdit3(col_label.c(), app.viewer.col_section[i], flags);
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
if (ImGui::Button(sec_name.c()))
|
if (ImGui::Button(sec_name.c()))
|
||||||
viewer_jump_to(app.viewer, range->start);
|
viewer_jump_to(app.viewer, range.start);
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::Text("%s", to_pretty_size(scratch.arena, range->len).c());
|
ImGui::Text("%s", to_pretty_size(scratch.arena, range.len).c());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Repeated sections: allow jumping to the N-th
|
// Repeated sections: allow jumping to the N-th
|
||||||
|
@ -389,3 +393,143 @@ void update_and_render(Arena *arena, App_State &app, f32 delta_time_ms)
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal
|
||||||
|
Term_Viewer make_term_viewer(u16 n_cols)
|
||||||
|
{
|
||||||
|
Term_Viewer viewer {};
|
||||||
|
viewer.max_cols = n_cols ? n_cols : 32;
|
||||||
|
|
||||||
|
viewer.col_key = ACol_Green;
|
||||||
|
viewer.col_page_start = ACol_Bright_Magenta;
|
||||||
|
viewer.col_checksum = ACol_Yellow;
|
||||||
|
viewer.col_highlight = ACol_Bright_White;
|
||||||
|
viewer.col_section[Sec_RNTuple_Anchor] = ACol_Bright_Yellow;
|
||||||
|
viewer.col_section[Sec_RNTuple_Header] = ACol_Cyan;
|
||||||
|
viewer.col_section[Sec_RNTuple_Footer] = ACol_Blue;
|
||||||
|
viewer.col_section[Sec_TFile_Header] = ACol_White;
|
||||||
|
viewer.col_section[Sec_TFile_Object] = ACol_Grey;
|
||||||
|
viewer.col_section[Sec_TFile_Info] = ACol_Red;
|
||||||
|
viewer.col_section[Sec_TFile_FreeList] = ACol_Bright_Yellow;
|
||||||
|
viewer.col_section[Sec_Page] = ACol_Magenta;
|
||||||
|
viewer.col_section[Sec_Page_List] = ACol_Bright_Cyan;
|
||||||
|
viewer.col_section[Sec_TKey_List] = ACol_Bright_Green;
|
||||||
|
return viewer;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal
|
||||||
|
Ansi_Color viewer_to_ansi_color(const Viewer &viewer, const Term_Viewer &tviewer, u32 col)
|
||||||
|
{
|
||||||
|
if (col == imcol(viewer.col_key)) return tviewer.col_key;
|
||||||
|
if (col == imcol(viewer.col_page_start)) return tviewer.col_page_start;
|
||||||
|
if (col == imcol(viewer.col_checksum)) return tviewer.col_checksum;
|
||||||
|
if (col == imcol(viewer.col_highlight)) return tviewer.col_highlight;
|
||||||
|
for (u64 section = 0; section < Sec_COUNT; ++section)
|
||||||
|
if (col == imcol(viewer.col_section[section]))
|
||||||
|
return tviewer.col_section[section];
|
||||||
|
return ACol_None;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal
|
||||||
|
String8 render_legend_to_string(Arena *arena, const Term_Viewer &viewer)
|
||||||
|
{
|
||||||
|
Temp scratch = scratch_begin(&arena, 1);
|
||||||
|
defer { scratch_end(scratch); };
|
||||||
|
|
||||||
|
struct String8_Node {
|
||||||
|
String8_Node *next;
|
||||||
|
String8 str;
|
||||||
|
} *head = nullptr, *tail = nullptr;
|
||||||
|
|
||||||
|
u64 tot_len = 0;
|
||||||
|
for (u64 section = 0; section < Sec_COUNT; ++section) {
|
||||||
|
Ansi_Color color = viewer.col_section[section];
|
||||||
|
String8 color_str = ansi_color_table[color];
|
||||||
|
String8 sec_name = section_names[section];
|
||||||
|
String8 s = push_str8f(scratch.arena, "%s%s ", color_str.c(), sec_name.c());
|
||||||
|
String8_Node *node = arena_push<String8_Node>(scratch.arena);
|
||||||
|
node->str = s;
|
||||||
|
if (!tail) {
|
||||||
|
head = node;
|
||||||
|
} else {
|
||||||
|
tail->next = node;
|
||||||
|
}
|
||||||
|
tail = node;
|
||||||
|
tot_len += s.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
String8 legend { arena_push_array_nozero<u8>(arena, tot_len + 1), tot_len + 1 };
|
||||||
|
legend.str[tot_len] = 0;
|
||||||
|
u64 cur_size = 0;
|
||||||
|
|
||||||
|
for (String8_Node *node = head; node; node = node->next) {
|
||||||
|
memcpy(legend.str + cur_size, node->str.str, node->str.size);
|
||||||
|
cur_size += node->str.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return legend;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal
|
||||||
|
String8 render_range_to_string(Arena *arena, App_State &app, u64 len, u64 n_cols)
|
||||||
|
{
|
||||||
|
Term_Viewer viewer = make_term_viewer(n_cols);
|
||||||
|
|
||||||
|
String8 legend = render_legend_to_string(arena, viewer);
|
||||||
|
|
||||||
|
// NOTE: +3 because we need 2 chars for each byte + 1 whitespace.
|
||||||
|
u64 buf_size = (ACOL_MAX_LEN + 3) * len;
|
||||||
|
u64 n_newlines = len / viewer.max_cols;
|
||||||
|
buf_size += n_newlines;
|
||||||
|
buf_size += 2; // two newlines
|
||||||
|
buf_size += legend.size;
|
||||||
|
buf_size += 1; // trailing zero
|
||||||
|
u8 *buf = arena_push_array_nozero<u8>(arena, buf_size);
|
||||||
|
buf[len] = 0;
|
||||||
|
|
||||||
|
u64 start = app.viewer.base_display_addr;
|
||||||
|
|
||||||
|
// We need to properly initialize this before calling mem_edit_bg_color_fn!
|
||||||
|
app.last_pinfo = &invalid_pinfo;
|
||||||
|
|
||||||
|
const u8 *data = app.inspected_file.mem;
|
||||||
|
u64 max_addr = app.inspected_file.size;
|
||||||
|
assert(start <= max_addr);
|
||||||
|
len = min(len, max_addr - start);
|
||||||
|
|
||||||
|
Temp scratch = scratch_begin(&arena, 1);
|
||||||
|
defer { scratch_end(scratch); };
|
||||||
|
|
||||||
|
u8 *cur = buf;
|
||||||
|
for (u64 i = 0; i < len; ++i) {
|
||||||
|
u64 off = start + i;
|
||||||
|
|
||||||
|
/// Select color
|
||||||
|
u32 byte_col = mem_edit_bg_color_fn(data, off, &app);
|
||||||
|
// piggyback off mem_edit_bg_color_fn instead of rewriting the color-choosing logic.
|
||||||
|
// Kinda dumb code but we don't need speed here since we're printing this one-off.
|
||||||
|
Ansi_Color acol = viewer_to_ansi_color(app.viewer, viewer, byte_col);
|
||||||
|
String8 acol_str = ansi_color_table[acol];
|
||||||
|
memcpy(cur, acol_str.str, acol_str.size);
|
||||||
|
cur += acol_str.size;
|
||||||
|
|
||||||
|
/// Write the human-readable byte
|
||||||
|
String8 byte_str = push_str8f(scratch.arena, "%02X ", data[off]);
|
||||||
|
memcpy(cur, byte_str.str, byte_str.size);
|
||||||
|
cur += byte_str.size;
|
||||||
|
|
||||||
|
if ((i + 1) % viewer.max_cols == 0)
|
||||||
|
*cur++ = '\n';
|
||||||
|
|
||||||
|
assert((u64)(cur - buf) < (u64)buf_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
*cur++ = '\n';
|
||||||
|
*cur++ = '\n';
|
||||||
|
memcpy(cur, legend.str, legend.size);
|
||||||
|
cur += legend.size;
|
||||||
|
|
||||||
|
*cur = 0;
|
||||||
|
|
||||||
|
return { buf, (u64)(cur - buf) };
|
||||||
|
}
|
||||||
|
|
54
src/render.h
54
src/render.h
|
@ -1,6 +1,7 @@
|
||||||
struct Viewer {
|
struct Viewer {
|
||||||
MemoryEditor mem_edit;
|
MemoryEditor mem_edit;
|
||||||
|
|
||||||
|
// Imgui colors
|
||||||
f32 col_section[Sec_COUNT][3];
|
f32 col_section[Sec_COUNT][3];
|
||||||
f32 col_key[3];
|
f32 col_key[3];
|
||||||
f32 col_checksum[3];
|
f32 col_checksum[3];
|
||||||
|
@ -23,3 +24,56 @@ struct Edit_Bg_Color_Data {
|
||||||
Viewer viewer;
|
Viewer viewer;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------------------------------------
|
||||||
|
// TERMINAL VIEW
|
||||||
|
// ----------------------------------------------------
|
||||||
|
enum Ansi_Color {
|
||||||
|
ACol_None,
|
||||||
|
ACol_Red,
|
||||||
|
ACol_Green,
|
||||||
|
ACol_Yellow,
|
||||||
|
ACol_Blue,
|
||||||
|
ACol_Magenta,
|
||||||
|
ACol_Cyan,
|
||||||
|
ACol_White,
|
||||||
|
ACol_Grey,
|
||||||
|
ACol_Bright_Red,
|
||||||
|
ACol_Bright_Green,
|
||||||
|
ACol_Bright_Yellow,
|
||||||
|
ACol_Bright_Blue,
|
||||||
|
ACol_Bright_Magenta,
|
||||||
|
ACol_Bright_Cyan,
|
||||||
|
ACol_Bright_White,
|
||||||
|
ACol_COUNT
|
||||||
|
};
|
||||||
|
const u64 ACOL_MAX_LEN = 6;
|
||||||
|
internal String8 ansi_color_table[ACol_COUNT] = {
|
||||||
|
str8("\033[0m"),
|
||||||
|
str8("\033[31m"),
|
||||||
|
str8("\033[32m"),
|
||||||
|
str8("\033[33m"),
|
||||||
|
str8("\033[34m"),
|
||||||
|
str8("\033[35m"),
|
||||||
|
str8("\033[36m"),
|
||||||
|
str8("\033[37m"),
|
||||||
|
str8("\033[90m"),
|
||||||
|
str8("\033[91m"),
|
||||||
|
str8("\033[92m"),
|
||||||
|
str8("\033[93m"),
|
||||||
|
str8("\033[94m"),
|
||||||
|
str8("\033[95m"),
|
||||||
|
str8("\033[96m"),
|
||||||
|
str8("\033[97m")
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Term_Viewer {
|
||||||
|
Ansi_Color col_section[Sec_COUNT];
|
||||||
|
Ansi_Color col_key;
|
||||||
|
Ansi_Color col_checksum;
|
||||||
|
Ansi_Color col_highlight;
|
||||||
|
Ansi_Color col_page_start;
|
||||||
|
|
||||||
|
// How many bytes to render per line
|
||||||
|
u16 max_cols;
|
||||||
|
};
|
||||||
|
|
|
@ -110,7 +110,7 @@ struct Section {
|
||||||
b8 highlighted;
|
b8 highlighted;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline const String8 section_names[Sec_COUNT] = {
|
internal const String8 section_names[Sec_COUNT] = {
|
||||||
str8("None"),
|
str8("None"),
|
||||||
str8("TFile Header"),
|
str8("TFile Header"),
|
||||||
str8("TFile Object"),
|
str8("TFile Object"),
|
||||||
|
|
|
@ -75,21 +75,124 @@ void app_cleanup(App_State &app)
|
||||||
if (app.inspected_file.stream) fclose(app.inspected_file.stream);
|
if (app.inspected_file.stream) fclose(app.inspected_file.stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal
|
||||||
|
void print_help(const char *argv0)
|
||||||
|
{
|
||||||
|
fprintf(stderr,
|
||||||
|
"Usage: %s [-t] [-s START] [-l LEN] [-w WIDTH] <ntuple_name> <ntuple_file.root>"
|
||||||
|
"\n"
|
||||||
|
"\n\t-t: no graphics, output to terminal"
|
||||||
|
"\n\t-s: set first displayed byte to START"
|
||||||
|
"\n\t-l: display LEN bytes (only in terminal mode)"
|
||||||
|
"\n\t-w: display WIDTH bytes per column"
|
||||||
|
"\n"
|
||||||
|
, argv0);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Cmdline_Args {
|
||||||
|
b8 print_to_terminal;
|
||||||
|
b8 show_help_and_exit;
|
||||||
|
u64 start_addr;
|
||||||
|
u64 nbytes_displayed;
|
||||||
|
u16 n_cols;
|
||||||
|
|
||||||
|
String8 ntpl_name;
|
||||||
|
String8 file_name;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Conv_Res {
|
||||||
|
u64 num;
|
||||||
|
b8 error;
|
||||||
|
};
|
||||||
|
|
||||||
|
internal
|
||||||
|
Conv_Res str_to_u64(String8 s)
|
||||||
|
{
|
||||||
|
Conv_Res res {};
|
||||||
|
for (u64 i = 0; i < s.size; ++i) {
|
||||||
|
u8 c = s.str[i];
|
||||||
|
if (c >= '0' && c <= '9') {
|
||||||
|
res.num *= 10;
|
||||||
|
res.num += c - '0';
|
||||||
|
} else {
|
||||||
|
res.error = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal
|
||||||
|
void parse_int_arg(i32 &cur_arg_idx, i32 argc, char **argv, u64 &out)
|
||||||
|
{
|
||||||
|
const char *arg = argv[cur_arg_idx];
|
||||||
|
if (cur_arg_idx < argc - 1) {
|
||||||
|
String8 nxt_arg = str8_from_c(argv[++cur_arg_idx]);
|
||||||
|
Conv_Res res = str_to_u64(nxt_arg);
|
||||||
|
if (res.error)
|
||||||
|
fprintf(stderr, "Invalid integer after %s flag.\n", arg);
|
||||||
|
else
|
||||||
|
out = res.num;
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Argument required after %s flag.\n", arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal
|
||||||
|
Cmdline_Args parse_args(i32 argc, char **argv)
|
||||||
|
{
|
||||||
|
Cmdline_Args args {};
|
||||||
|
if (argc < 3) {
|
||||||
|
args.show_help_and_exit = true;
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: refactor this probably
|
||||||
|
for (i32 i = 1; i < argc; ++i) {
|
||||||
|
String8 arg = str8_from_c(argv[i]);
|
||||||
|
if (arg.str[0] == '-') {
|
||||||
|
if (arg.size == 2) {
|
||||||
|
if (arg.str[1] == 't')
|
||||||
|
args.print_to_terminal = true;
|
||||||
|
else if (arg.str[1] == 's')
|
||||||
|
parse_int_arg(i, argc, argv, args.start_addr);
|
||||||
|
else if (arg.str[1] == 'l')
|
||||||
|
parse_int_arg(i, argc, argv, args.nbytes_displayed);
|
||||||
|
else if (arg.str[1] == 'w') {
|
||||||
|
u64 n_cols = 0;
|
||||||
|
parse_int_arg(i, argc, argv, n_cols);
|
||||||
|
args.n_cols = (u16)n_cols;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
args.show_help_and_exit = true;
|
||||||
|
} else {
|
||||||
|
args.show_help_and_exit = true;
|
||||||
|
}
|
||||||
|
} else if (args.ntpl_name.size) {
|
||||||
|
if (args.file_name.size)
|
||||||
|
args.show_help_and_exit = true;
|
||||||
|
else
|
||||||
|
args.file_name = arg;
|
||||||
|
} else {
|
||||||
|
args.ntpl_name = arg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
Thread_Ctx tctx;
|
Thread_Ctx tctx;
|
||||||
tctx_init(tctx);
|
tctx_init(tctx);
|
||||||
defer { tctx_release(); };
|
defer { tctx_release(); };
|
||||||
|
|
||||||
if ((argc > 1 && argv[1][0] == '-') || argc < 3) {
|
// Parse cmdline
|
||||||
fprintf(stderr, "Usage: %s <ntuple_name> <ntuple_file.root>\n", argv[0]);
|
Cmdline_Args args = parse_args(argc, argv);
|
||||||
|
if (args.show_help_and_exit || !args.ntpl_name.size || !args.file_name.size) {
|
||||||
|
print_help(argv[0]);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collect arguments
|
|
||||||
const char *ntpl_name = argc > 1 ? argv[1] : nullptr;
|
|
||||||
const char *fname = argc > 2 ? argv[2] : nullptr;
|
|
||||||
|
|
||||||
// Allocate program memory
|
// Allocate program memory
|
||||||
Arena *arena = arena_alloc();
|
Arena *arena = arena_alloc();
|
||||||
if (!arena) {
|
if (!arena) {
|
||||||
|
@ -115,19 +218,29 @@ int main(int argc, char **argv)
|
||||||
defer { app_cleanup(app); };
|
defer { app_cleanup(app); };
|
||||||
|
|
||||||
// Open and map the file
|
// Open and map the file
|
||||||
if (fname) {
|
if (args.file_name.size) {
|
||||||
if (!os_open_and_map_file(fname, app))
|
if (!os_open_and_map_file(args.file_name, app))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
// Watch file for changes (to adapt the displayed file size - otherwise
|
// Watch file for changes (to adapt the displayed file size - otherwise
|
||||||
// we may try to access invalid memory when the file gets shrunk)
|
// we may try to access invalid memory when the file gets shrunk)
|
||||||
os_start_file_watch(fname, app);
|
os_start_file_watch(args.file_name, app);
|
||||||
}
|
}
|
||||||
|
|
||||||
app.ntpl_name = str8(ntpl_name);
|
app.ntpl_name = args.ntpl_name;
|
||||||
app.tfile_data = get_tfile_data(app.inspected_file);
|
app.tfile_data = get_tfile_data(app.inspected_file);
|
||||||
app.rndata = get_rntuple_data(arena, app.inspected_file, app.ntpl_name);
|
app.rndata = get_rntuple_data(arena, app.inspected_file, app.ntpl_name);
|
||||||
make_viewer(app);
|
make_viewer(app, args.n_cols);
|
||||||
|
app.viewer.base_display_addr = args.start_addr;
|
||||||
|
|
||||||
|
if (args.print_to_terminal) {
|
||||||
|
u64 nbytes_displayed = args.nbytes_displayed;
|
||||||
|
if (!nbytes_displayed)
|
||||||
|
nbytes_displayed = 1000;
|
||||||
|
String8 rendered = render_range_to_string(arena, app, nbytes_displayed, args.n_cols);
|
||||||
|
printf("%s\n", rendered.c());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// Start main loop
|
// Start main loop
|
||||||
run_main_loop(window, arena, app);
|
run_main_loop(window, arena, app);
|
||||||
|
|
|
@ -20,3 +20,9 @@ String8 push_str8f(Arena *arena, const char *fmt, ...)
|
||||||
va_end(args);
|
va_end(args);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String8 str8_from_c(const char *str)
|
||||||
|
{
|
||||||
|
u64 size = strlen(str);
|
||||||
|
return String8 { (u8*)str, size };
|
||||||
|
}
|
||||||
|
|
|
@ -7,4 +7,5 @@ struct String8 {
|
||||||
|
|
||||||
#define str8(s) String8 { (u8*)(s), sizeof(s) - 1 }
|
#define str8(s) String8 { (u8*)(s), sizeof(s) - 1 }
|
||||||
|
|
||||||
|
String8 str8_from_c(const char *str);
|
||||||
String8 push_str8f(Arena *arena, char *fmt, ...);
|
String8 push_str8f(Arena *arena, char *fmt, ...);
|
||||||
|
|
Loading…
Reference in a new issue