struct ExplorerUI { // u8 search_input[64]; // u8 secondary_input[64]; }; struct Explorer { // A bunch of flags? // Icon cache? // Array frame_textures; }; struct Ex1_NTFS_Enumeration { b32 initialized; b32 threads_started; ArrayView threads; Array threads_in_flight; }; // #Ex1_global_state global ExplorerUI explorer_ui; global Explorer explorer; global Ex1_NTFS_Enumeration ex1_ntfs; #define HOTKEY_ID_BRING_TO_FOREGROUND 1 #define VK_SPACE_KEY_CODE 0x20 // #define HOTKEY_ID_HIDE_TITLEBAR void Ex1_Unregister_Global_Hotkeys () { UnregisterHotKey(nullptr, HOTKEY_ID_BRING_TO_FOREGROUND); } bool Ex1_Register_Global_Hotkeys () { if (!RegisterHotKey(nullptr, HOTKEY_ID_BRING_TO_FOREGROUND, MOD_CONTROL|MOD_SHIFT|MOD_ALT, VK_SPACE_KEY_CODE)) { Assert(false); log_error("Failed to register global hotkey Ctrl-Shift-Alt-Space"); return false; } return true; } #define USE_CTRL true #define USE_SHIFT true #define USE_ALT true #define TRIGGER_ONCE true #define TRIGGER_EVERY_FRAME false #define KEY_UNUSED false // #program_hotkeys (Not specific to Ex1) global Key_Combination program_exit_hotkey { ImGuiKey_W, USE_CTRL, USE_SHIFT, KEY_UNUSED, TRIGGER_ONCE }; global Key_Combination program_minimize_hotkey { ImGuiKey_W, USE_CTRL, KEY_UNUSED, KEY_UNUSED, TRIGGER_ONCE }; bool Ex1_check_key_combinations() { update_global_keyboard_state(); // #program_hotkeys if (check_key_combination(&program_exit_hotkey)) { return true; } if (check_key_combination(&program_minimize_hotkey)) { Win32_Minimize_Window_To_Tray(get_main_window_pointer()); } // #Ex1_hotkeys return false; } // #Workspaces are FOR DEVELOPMENT ONLY. struct Ex1_Workspace { s32 path_select; s32 file_select; RadixSort file_size_radix; RadixSort file_modtime_radix; RadixSort dir_modtime_radix; bool sort_completed; // Reordered strings: ArrayView files_sorted_by_size; ArrayView files_sorted_by_modtime; }; global Ex1_Workspace ex1w; void free_ex1_workspace_and_reset () { if (ex1w.sort_completed) { push_allocator(GPAllocator()); radix_sort_free(&ex1w.file_size_radix); radix_sort_free(&ex1w.file_modtime_radix); radix_sort_free(&ex1w.dir_modtime_radix); array_free(ex1w.files_sorted_by_size); array_free(ex1w.files_sorted_by_modtime); zero_struct(&ex1w); } } // #TODO: Move all sort stuff to OS_Win32? // Make a general version of this that takes two ArrayView and reorders. // There's no need to do this until we have the filtered results. void os_win32_reorder_files_by_radix (RadixSort* r, ArrayView* files, bool reverse_order=false) { Timed_Block_Print("os_win32_reorder_files_by_radix"); // Where are my source files!? (*files) = ArrayView(r->ranks.count); for_each(f, (*files)) { // (*files)[f] = get_file_copy(stfe, r->ranks[f]); (*files)[f] = get_file_string_view(stfe, r->ranks[f]); } } void Ex1_show_enumeration_workspace () { using namespace ImGui; push_imgui_window("Enumerated Data Workspace"); if (!ex1w.sort_completed) { push_allocator(GPAllocator()); Timed_Block_Print("radix_sort_u64: file sizes, file modtimes, directory modtimes"); ArrayView sizes = to_view(*stfe->files.sizes); radix_sort_u64(&ex1w.file_size_radix, sizes.data, (u32)sizes.count); ArrayView file_modtimes = to_view(*stfe->files.modtimes); radix_sort_u64(&ex1w.file_modtime_radix, file_modtimes.data, (u32)file_modtimes.count); ArrayView dirs_modtimes = to_view(*stfe->dirs.modtimes); radix_sort_u64(&ex1w.dir_modtime_radix, dirs_modtimes.data, (u32)dirs_modtimes.count); // Create ArrayView, ArrayView sizes, and ArrayView modtimes os_win32_reorder_files_by_radix(&ex1w.file_size_radix, &ex1w.files_sorted_by_size); os_win32_reorder_files_by_radix(&ex1w.file_modtime_radix, &ex1w.files_sorted_by_modtime); // reordering by the rank permutations generated by RadixSort. ex1w.sort_completed = true; } if (!ex1w.sort_completed) { return; } SeparatorText("Files ordered by modtime"); s32 file_count = (s32)ex1w.files_sorted_by_modtime.count; SliderInt("Select file index", &ex1w.file_select, 0, file_count-1); string file_name = copy_string(ex1w.files_sorted_by_modtime[ex1w.file_select]); Text("%s", file_name.data); u32 radix_index = rank(&ex1w.file_modtime_radix, ex1w.file_select); Text("date modified: %s", format_time_datetime(get_file_modtime(stfe, radix_index)).data); SeparatorText("Files ordered by size"); file_count = (s32)ex1w.files_sorted_by_size.count; // SliderInt("Select file index", &ex1w.file_select, 0, file_count-1); file_name = copy_string(ex1w.files_sorted_by_size[ex1w.file_select]); Text("%s", file_name.data); radix_index = rank(&ex1w.file_size_radix, ex1w.file_select); Text("size: %s", format_bytes(get_file_size_bytes(stfe, radix_index)).data); // Text("date modified: %s", format_time_datetime(get_file_modtime(stfe, radix_index)).data); if (Button("Count unique UTF-8 characters")) { count_unique_utf8_chars(); } Text("unique_codepoints_utf32.count: %", unique_codepoints_utf32.count); for_each(u, unique_codepoints_utf32) { Text("[%d] Code point as hex: 0x%X", u, unique_codepoints_utf32[u]); } Text("files_sorted_by_size size in bytes: %lld", ex1w.files_sorted_by_size.count * sizeof(string)); Text("files_sorted_by_modtime size in bytes: %lld", ex1w.files_sorted_by_modtime.count * sizeof(string)); for (s64 i = 1; i < 128; i += 1) { u8 cstring[2] = {}; cstring[0] = (u8)i; cstring[1] = 0; Text("codepoint[0x%X]: %s, count: %lld", i, cstring, count_ascii_codepoints[i]); } } void Ex1_Control_Panel () { using namespace ImGui; f64 frame_time = GetUnixTimestamp(); Table* drive_table = get_drive_table(); push_imgui_window("Control Panel"); // if (Button("Debug break")) { debug_break(); } if (/*Button("Discover drives") ||*/!table_is_valid(drive_table)) { Win32_Discover_Drives(); } // Text("ntfs_workspace_files_loaded: %s", ntfs_workspace_files_loaded()? "true": "false"); // if (ntfs_workspace_files_loaded()) { // Ex1_show_ntfs_workspace(); // return; // } bool all_drives_enumerated = stfe && stfe->thread_completed; push_allocator(temp()); ArrayView drives = os_get_available_drives(); // only includes drives that are ready. if (!USN_Journal_Monitoring_Ready(drives[0]) && Button("Enable USN Monitoring for all drives")) { Win32_Enable_USN_Journal_Monitoring(drives); } if (USN_Journal_Monitoring_Ready(drives[0]) && Button("Query USN Journal")) { Query_USN_Journal(drives); } if (!all_drives_enumerated) { // Text("drive_table is valid: %d", table_is_valid(drive_table)); for_each(i, drives) { OS_Drive* drive = drives[i]; Text(" > [%d] drive letter: %s (is_present: %d)", i + 1, drive->label.data, drive->is_present); if (drive->time_to_enumerate != 0) { SameLine(); Text("Enumerated in %.2f seconds", drive->time_to_enumerate); } // SameLine(); // if (Button(format_cstring("Read NTFS MFT Raw##%s", drive->label.data))) { // push_arena(thread_context()->arena); // Error* error = NTFS_MFT_read_raw(drive); // } } } s32 drives_enumerated = 0; for_each(d, drives) { OS_Drive* drive = drives[d]; if (!drives[d]->time_to_enumerate) { continue; } if (!drives[d]->data) { continue; } if (drives[d]->data->paths.offsets->count > 0) { drives_enumerated += 1; } } // bool all_drives_enumerated = !ex1_ntfs.threads_in_flight.count // && (drives_enumerated == drives.count); // string file_path = format_string_temp("%s_DriveData.bin", os_get_machine_name().data); string file_path = "D:/Projects/Cpp/Musa-Cpp-Lib-V2/bin/MUSA-PC3_DriveData.bin";// FIXED path. Text("fixed file_path: %s", file_path.data); if (!all_drives_enumerated && file_exists(file_path)) { // #autoload Deserialize_ST_File_Enumeration(file_path); } if (drives.count > 0 && !all_drives_enumerated && file_exists(file_path) && Button("Load from file (this machine)")) { Deserialize_ST_File_Enumeration(file_path); // Deserialize_Win32_Drives(file_path); } // if (file_enum_multithreading_started()) { // if (thread_is_done(drive_enumeration->master_thread)) { // push_allocator(GPAllocator()); // // Thread* thread = drive_enumeration->master_thread; // // auto task = thread_task(Drive_Enumeration); // // Nothing to free? // thread_deinit(drive_enumeration->master_thread, true); // } // } // #FileEnumerationST if (stfe && stfe->thread_started) { // print dirs enumerated, etc if (!stfe->thread_completed) Text("Enumeration Thread Active (elapsed: %s)", format_time_seconds(frame_time-stfe->start_time).data); if (stfe->dirs.offsets) { s64 dirs_enumerated = stfe->dirs.offsets->count; Text("Dirs enumerated: %lld", dirs_enumerated); // if (!stfe->thread_completed) Text("Current dir: %s", get_last_path_copy(stfe).data); } if (stfe->files.offsets) { s64 files_enumerated = stfe->files.offsets->count; Text("Files enumerated: %lld", files_enumerated); // if (!stfe->thread_completed) Text("Current file: %s", get_last_file_copy(stfe).data); } if (stfe->thread_completed) { Text("String bytes stored: %s", format_bytes(stfe->dirs.strings->count + stfe->files.strings->count).data); Text("Elapsed time: %s", format_time_seconds(stfe->end_time-stfe->start_time).data); } } if (stfe && stfe->thread_started && !stfe->thread_completed) { Assert(stfe->master_thread != nullptr); if (thread_is_done(stfe->master_thread)) { thread_deinit(stfe->master_thread, true); stfe->thread_completed = true; } } if (all_drives_enumerated && Button("Save enumerated data")) { if (!Serialize_ST_File_Enumeration(file_path)) { log_error("Failed to write enumerated files"); os_log_error(); } } if (all_drives_enumerated && Button("Reset State")) { free_ex1_workspace_and_reset(); free_stfe_and_reset(); return; } if (all_drives_enumerated) { Ex1_show_enumeration_workspace(); } if (drives.count > 0 && !all_drives_enumerated && Button("Enumerate all NTFS drives")) { // && ex1_ntfs.initialized os_run_file_enumeration_single_threaded(); // os_run_file_enumeration_multithreaded(); // #disabled for now return; } } void ImGui_Debug_Panel () { using namespace ImGui; push_allocator(temp()); Begin("Debug Panel"); // #cpuid Text("[cpus] physical: %d, logical: %d, primary: %d, secondary: %d", os_cpu_physical_core_count(), os_cpu_logical_core_count(), os_cpu_primary_core_count(), os_cpu_secondary_core_count()); { SeparatorText("Arena In-Use List"); lock_guard(&arena_free_list->mutex); for (u8 i = 0; i < Arena_Reserve_Count; i += 1) { #if ARENA_DEBUG auto t = format_cstring( " [%s] in_use: %d, committed_bytes: %s", format_bytes(Arena_Sizes[i], 0).data, arena_free_list->in_flight_count[i], format_bytes(committed_bytes(arena_free_list->in_flight[i])).data ); #else auto t = format_cstring( " [%s] in_use: %d, committed_bytes: %s", format_bytes(Arena_Sizes[i], 0).data, arena_free_list->in_flight_count[i], "disabled in release mode" ); #endif Text(t); } SeparatorText("Arena Free List"); for (u8 i = 0; i < Arena_Reserve_Count; i += 1) { auto t = format_cstring( " [%s] free: %d, committed_bytes: %s", format_bytes(Arena_Sizes[i], 0).data, (s32)arena_free_list->free_table[i].count, format_bytes(committed_bytes(arena_free_list->free_table[i])).data ); Text(t); } } SeparatorText("Child Threads"); SeparatorText("Errors"); ArrayView errors = get_all_errors(thread_context()); if (errors.count && Button("Clear all errors")) { clear_errors(thread_context()); errors.count = 0; } for_each(e, errors) { auto button_label = format_cstring("Clear##%d", e); if (Button(button_label)) { clear_error(thread_context(), errors[e]); continue; } SameLine(); Text(" [%d] %s", e, to_string(errors[e]).data); } Spacing(); if (Button("Push some error")) { log_warning("This is a warning."); log_error("... and this is an error."); } End(); }