diff --git a/src/rntuple.cpp b/src/rntuple.cpp index 42ac0d2..0715235 100644 --- a/src/rntuple.cpp +++ b/src/rntuple.cpp @@ -369,7 +369,7 @@ Section find_section(App_State &app, u64 off, i64 hilite_cluster = -1) b8 hilite = false; // TFile start - if (off <= tdata.root_file_header_size) return { Sec_TFile_Header, { 0, tdata.root_file_header_size }, 0, hilite }; + if (off < tdata.root_file_header_size) return { Sec_TFile_Header, { 0, tdata.root_file_header_size }, 0, hilite }; if (tdata.rng_root_file_obj.start <= off && off < tdata.rng_root_file_obj.end()) return { Sec_TFile_Object, tdata.rng_root_file_obj, 0, hilite }; if (tdata.rng_root_file_info.start <= off && off < tdata.rng_root_file_info.end()) return { Sec_TFile_Info, tdata.rng_root_file_info, 0, hilite }; if (tdata.rng_root_file_free.start <= off && off < tdata.rng_root_file_free.end()) return { Sec_TFile_FreeList, tdata.rng_root_file_free, 0, hilite }; @@ -445,51 +445,160 @@ struct Sec_Hover_Info { String8_Node *desc; }; +template void hover_postproc_val(T &) {} +template <> void hover_postproc_val(u16 &x) { x = bswap_16(x); } +template <> void hover_postproc_val(u32 &x) { x = bswap_32(x); } +template <> void hover_postproc_val(u64 &x) { x = bswap_64(x); } + +template +String8_Node *hover_display_val(Arena *arena, String8_Node *prev, const char *fmt, T val) +{ + hover_postproc_val(val); + return push_str8_node(arena, prev, fmt, val); +} + +internal +String8_Node *hover_display_datetime_str(Arena *arena, String8_Node *prev, const char *fmt_pre, u32 datetime) +{ + hover_postproc_val(datetime); + + // datetime: + // year (6b) | month (4b) | day (5b) | hour (5b) | min (6b) | sec (6b) + u32 year = (datetime >> 26) - 1900 + 1995; + u32 month = ((datetime & 0x3ff'ffff) >> 22) - 1; + u32 day = (datetime & 0x3f'ffff) >> 17; + u32 hour = (datetime & 0x1'ffff) >> 12; + u32 min = (datetime & 0xfff) >> 6; + u32 sec = datetime & 0x3f; + return push_str8_node(arena, prev, "%s%u/%02u/%02u %02u:%02u:%02u", fmt_pre, year, month, day, hour, min, sec); +} + +struct Try_Sec_Hover_Fn { + u64 start; + u64 roff; + const u8 *data; + Arena *arena; + Sec_Hover_Info &info; + u64 &cur_field_off; + + template + bool operator()(const char *desc_fmt, + String8_Node *(*display_val)(Arena *, String8_Node *, const char *, TField_Type) = hover_display_val + ) const + { + u64 field_len = sizeof(TField_Type); + if (roff < cur_field_off + field_len) { + info.rng = { start + cur_field_off, field_len }; + TField_Type val; + memcpy(&val, data + info.rng.start, info.rng.len); + display_val(arena, info.desc, desc_fmt, val); + return true; + } + cur_field_off += field_len; + return false; + } +}; + // `off` is the absolute offset into `data`. internal Sec_Hover_Info get_section_hover_info(Arena *arena, Section section, u64 off, const u8 *data) { Sec_Hover_Info info {}; - printf("off: 0x%lX, sec start: 0x%lX\n", off, section.range.start); + // printf("off: 0x%lX, sec start: 0x%lX\n", off, section.range.start); // assert(off >= section.range.start); if (off < section.range.start) { printf("WRONG\n"); // TODO: fix TKey Header case } + info.desc = push_str8_node(arena, nullptr, section_names[section.id].c()); + u64 start = section.range.start; u64 roff = off - start; // offset relative to `section` + u64 cur_field_off = 0; + Try_Sec_Hover_Fn try_sec_hover { start, roff, data, arena, info, cur_field_off }; - if (section.id == Sec_RNTuple_Anchor) { - info.desc = push_str8_node(arena, nullptr, "RNTuple Anchor"); - // TODO: dry - if (roff < sizeof(u32)) { - info.rng = { start, sizeof(u32) }; - u32 val; - memcpy(&val, data + info.rng.start, info.rng.len); - val = bswap_32(val); - val -= 0x40000000; - push_str8_node(arena, info.desc, "Object len: %u", val); - } else if (roff < sizeof(u32) + sizeof(u16)) { - info.rng = { start + sizeof(u32), sizeof(u16) }; - u32 val; - memcpy(&val, data + info.rng.start, info.rng.len); - val = bswap_16(val); - push_str8_node(arena, info.desc, "Class version: %u", val); - } else if (roff < sizeof(u32) + sizeof(u16) + sizeof(u16)) { - info.rng = { start + sizeof(u32) + sizeof(u16), sizeof(u16) }; - u16 val; - memcpy(&val, data + info.rng.start, info.rng.len); - val = bswap_16(val); - push_str8_node(arena, info.desc, "Version Epoch: %u", val); - } else if (roff < sizeof(u32) + sizeof(u16) + sizeof(u16) + sizeof(u16)) { - info.rng = { start + sizeof(u32) + sizeof(u16) + sizeof(u16), sizeof(u16) }; - u16 val; - memcpy(&val, data + info.rng.start, info.rng.len); - val = bswap_16(val); - push_str8_node(arena, info.desc, "Version Major: %u", val); + switch (section.id) { + case Sec_RNTuple_Anchor: { + try_sec_hover.operator()("Object len: %u", [] (Arena *arena, String8_Node *prev, const char *fmt, u32 x) { + x = bswap_32(x); + x -= 0x4000'0000; + return push_str8_node(arena, prev, fmt, x); + }) + || try_sec_hover.operator()("Class version: %u") + || try_sec_hover.operator()("Version Epoch: %u") + || try_sec_hover.operator()("Version Major: %u") + || try_sec_hover.operator()("Version Minor: %u") + || try_sec_hover.operator()("Version Patch: %u") + || try_sec_hover.operator()("Seek Header: %u") + || try_sec_hover.operator()("NBytes Header: %u") + || try_sec_hover.operator()("Len Header: %u") + || try_sec_hover.operator()("Seek Footer: %u") + || try_sec_hover.operator()("NBytes Footer: %u") + || try_sec_hover.operator()("Len Footer: %u") + || try_sec_hover.operator()("Max Key Size: %u") + ; + } break; + + case Sec_TFile_Header: { + u32 root_version_be; + memcpy(&root_version_be, data + start + 4, sizeof(u32)); + u32 root_version = bswap_32(root_version_be); + b32x is_big = root_version > 1000000; + + if (is_big) { + try_sec_hover.operator()("ROOT magic number") + || try_sec_hover.operator()("ROOT version: %u", [] (Arena *arena, String8_Node *prev, const char *fmt, u32 x) { + x = bswap_32(x); + x -= 1000000; + return push_str8_node(arena, prev, fmt, x); + }) + || try_sec_hover.operator()("fBEGIN: 0x%lX") + || try_sec_hover.operator()("fEND: 0x%lX") + || try_sec_hover.operator()("Seek Free: 0x%lX") + || try_sec_hover.operator()("NBytes Free: %u") + || try_sec_hover.operator()("N Free: %u") + || try_sec_hover.operator()("NBytes Name: %u") + || try_sec_hover.operator()("Units: %u") + || try_sec_hover.operator()("Compression: %u") + || try_sec_hover.operator()("Seek Info: 0x%lX") + || try_sec_hover.operator()("NBytes Info: %u") + ; + } else { + try_sec_hover.operator()("ROOT magic number") + || try_sec_hover.operator()("ROOT version: %u") + || try_sec_hover.operator()("fBEGIN: 0x%lX") + || try_sec_hover.operator()("fEND: 0x%lX") + || try_sec_hover.operator()("Seek Free: 0x%lX") + || try_sec_hover.operator()("NBytes Free: %u") + || try_sec_hover.operator()("N Free: %u") + || try_sec_hover.operator()("NBytes Name: %u") + || try_sec_hover.operator()("Units: %u") + || try_sec_hover.operator()("Compression: %u") + || try_sec_hover.operator()("Seek Info: 0x%lX") + || try_sec_hover.operator()("NBytes Info: %u") + ; } + } break; + + case Sec_TFile_Object: { + // FIXME + try_sec_hover.operator()("ROOT magic number") + || try_sec_hover.operator()("Class version: %u", [] (Arena *arena, String8_Node *prev, const char *fmt, u16 x) { + x = bswap_16(x); + x -= (x > 1000) * 1000; + return push_str8_node(arena, prev, fmt, x); + }) + || try_sec_hover.operator()("Created: ", hover_display_datetime_str) + || try_sec_hover.operator()("Modified: ", hover_display_datetime_str) + || try_sec_hover.operator()("NBytes Keys %u") + || try_sec_hover.operator()("NBytes Name %u") + ; + } break; + + default:; } - + + return info; } diff --git a/src/root/RMicroFileReader.cxx b/src/root/RMicroFileReader.cxx index 829877d..d23d143 100644 --- a/src/root/RMicroFileReader.cxx +++ b/src/root/RMicroFileReader.cxx @@ -1072,7 +1072,10 @@ Root_File_Info get_root_file_info(const char *fname, bool is_big_file) { RTFString fileName { fname }; Root_File_Info fileInfo {}; - fileInfo.tfile_header_nbytes = RTFHeader{}.GetSize(); + RTFHeader header; + if (is_big_file) + header.SetBigFile(); + fileInfo.tfile_header_nbytes = header.GetSize(); fileInfo.tfile_obj_seek = 100; // kBEGIN fileInfo.tfile_obj_nbytes = sizeof(RTFFile) + fileName.GetSize() + RTFString{}.GetSize() + RTFUUID{}.GetSize(); fileInfo.version_seek = offsetof(RTFHeader, fVersion);