454 lines
16 KiB
C++
454 lines
16 KiB
C++
struct ExplorerUI {
|
|
u8 search_input[64];
|
|
u8 secondary_input[64];
|
|
};
|
|
|
|
struct Explorer {
|
|
// A bunch of flags?
|
|
// Icon cache?
|
|
// Array<Image_Data> frame_textures;
|
|
};
|
|
|
|
struct Ex1_NTFS_Enumeration {
|
|
b32 initialized;
|
|
b32 threads_started;
|
|
|
|
ArrayView<Thread> threads;
|
|
Array<Thread*> threads_in_flight;
|
|
};
|
|
|
|
// #Ex1_global_state
|
|
global ExplorerUI explorer_ui;
|
|
global Explorer explorer;
|
|
global Ex1_NTFS_Enumeration ex1_ntfs;
|
|
|
|
void ntfs_create_enumeration_threads (s32 thread_count) {
|
|
if (!ex1_ntfs.initialized) { Timed_Block_Print("Thread initialization (ntfs)");
|
|
ex1_ntfs.initialized = true;
|
|
ex1_ntfs.threads = ArrayView<Thread>(thread_count);
|
|
ex1_ntfs.threads_in_flight.allocator = GPAllocator();
|
|
for_each(t, ex1_ntfs.threads) {
|
|
string thread_name = format_string("ntfs_enumeration_thread#%d", t);
|
|
bool success = thread_init(&ex1_ntfs.threads[t], ntfs_enumeration_thread_proc, thread_name);
|
|
Assert(success);
|
|
}
|
|
}
|
|
}
|
|
|
|
#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<string> files_sorted_by_size;
|
|
ArrayView<string> files_sorted_by_modtime;
|
|
};
|
|
|
|
// #TODO: Move all sort stuff to OS_Win32?
|
|
// Make a general version of this that takes two ArrayView<T> and reorders.
|
|
void os_win32_reorder_files_by_radix (RadixSort* r, ArrayView<string>* files, bool reverse_order=false) {
|
|
Timed_Block_Print("os_win32_reorder_files_by_radix");
|
|
// Where are my source files!?
|
|
(*files) = ArrayView<string>(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]);
|
|
}
|
|
}
|
|
|
|
global Ex1_Workspace ex1w;
|
|
|
|
void Ex1_show_enumeration_workspace () { using namespace ImGui;
|
|
push_imgui_window("Enumerated Data Workspace");
|
|
|
|
/*
|
|
SliderInt("Select path index", &ex1w.path_select, 0, count_paths(stfe)-1);
|
|
Text("%s", get_path_copy(stfe, ex1w.path_select).data);
|
|
Text("time modified: %s", format_time_datetime(get_path_modtime(stfe, ex1w.path_select)).data);
|
|
// #TODO: modtime (to indextime)
|
|
SliderInt("Select file index", &ex1w.file_select, 0, count_files(stfe)-1);
|
|
Text("%s", get_file_copy(stfe, ex1w.file_select).data);
|
|
Text("size: %s", format_bytes(get_file_size_bytes(stfe, ex1w.file_select)).data);
|
|
Text("time modified: %s", format_time_datetime(get_file_modtime(stfe, ex1w.file_select)).data);
|
|
*/
|
|
// #TODO: size, modtime
|
|
|
|
if (!ex1w.sort_completed || Button("sort file sizes")) {
|
|
push_allocator(GPAllocator());
|
|
Timed_Block_Print("radix_sort_u64: file sizes, file modtimes, directory modtimes");
|
|
ArrayView<u64> sizes = to_view(*stfe->files.sizes);
|
|
radix_sort_u64(&ex1w.file_size_radix, sizes.data, (u32)sizes.count);
|
|
ArrayView<u64> file_modtimes = to_view(*stfe->files.modtimes);
|
|
radix_sort_u64(&ex1w.file_modtime_radix, file_modtimes.data, (u32)file_modtimes.count);
|
|
ArrayView<u64> dirs_modtimes = to_view(*stfe->dirs.modtimes);
|
|
radix_sort_u64(&ex1w.dir_modtime_radix, dirs_modtimes.data, (u32)dirs_modtimes.count);
|
|
// Create ArrayView<string>, ArrayView<u64> sizes, and ArrayView<u64> 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) {
|
|
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);
|
|
}
|
|
if (ex1w.sort_completed) {
|
|
SeparatorText("Files ordered by size");
|
|
s32 file_count = (s32)ex1w.files_sorted_by_size.count;
|
|
// SliderInt("Select file index", &ex1w.file_select, 0, file_count-1);
|
|
string file_name = copy_string(ex1w.files_sorted_by_size[ex1w.file_select]);
|
|
Text("%s", file_name.data);
|
|
u32 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);
|
|
}
|
|
}
|
|
|
|
void Ex1_Control_Panel () { using namespace ImGui;
|
|
f64 frame_time = GetUnixTimestamp();
|
|
Table<string, OS_Drive*>* 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<OS_Drive*> 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);
|
|
// if (!all_drives_enumerated && file_exists(file_path)) {
|
|
// 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)) {
|
|
push_allocator(GPAllocator());
|
|
thread_deinit(stfe->master_thread, true);
|
|
stfe->thread_completed = true;
|
|
// Delete(GPAllocator(), stfe);
|
|
// stfe = nullptr;
|
|
}
|
|
}
|
|
|
|
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) {
|
|
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();
|
|
// run_multithreaded_enumeration_thread();
|
|
return;
|
|
/*// #NTFS_MFT_RAW
|
|
push_allocator(GPAllocator());
|
|
Array<ArrayView<OS_Drive*>> drive_split;
|
|
drive_split.allocator = temp(); // this is only needed for this frame
|
|
|
|
if (drives.count > os_cpu_physical_core_count()) {
|
|
s32 thread_count = os_cpu_physical_core_count();
|
|
array_resize(drive_split, thread_count);
|
|
ntfs_create_enumeration_threads(thread_count);
|
|
|
|
s32 threads_to_create = thread_count;
|
|
s64 drives_per_thread = (drives.count / thread_count);
|
|
s64 remainder = drives.count % thread_count;
|
|
s64 current_drive = 0;
|
|
|
|
for_each(d, drive_split) {
|
|
if (d == drive_split.count) {
|
|
drive_split[d] = ArrayView<OS_Drive*>(remainder);
|
|
} else {
|
|
drive_split[d] = ArrayView<OS_Drive*>(drives_per_thread);
|
|
}
|
|
|
|
for (s64 i = 0; i < drive_split[d].count; i += 1) {
|
|
drive_split[d][i] = drives[current_drive];
|
|
current_drive += 1;
|
|
}
|
|
}
|
|
|
|
debug_break(); // #TODO: Check that the work has been distributed correctly.
|
|
} else { // more threads than drives, or same amount
|
|
s32 thread_count = (s32)drives.count;
|
|
array_resize(drive_split, drives.count);
|
|
ntfs_create_enumeration_threads(thread_count);
|
|
|
|
for_each(d, drives) {
|
|
auto drive = drives[d];
|
|
drive_split[d] = ArrayView<OS_Drive*>(1); // Arrays of size one are sad :pensive:
|
|
|
|
drive_split[d][0] = drive;
|
|
}
|
|
}
|
|
|
|
s64 active_thread_count = drive_split.count;
|
|
|
|
ex1_ntfs.threads_started = true;
|
|
for (s64 t = 0; t < active_thread_count; t += 1) {
|
|
Thread* thread = &ex1_ntfs.threads[t];
|
|
Arena* thread_arena = next_arena(Arena_Reserve::Size_64K);
|
|
push_arena(thread_arena);
|
|
auto thread_data = New<NTFS_Enumeration_Task>();
|
|
thread_data->pool = thread_arena;
|
|
thread_data->drives = drive_split[t];
|
|
thread_start(thread, thread_data);
|
|
array_add(ex1_ntfs.threads_in_flight, thread);
|
|
}*/
|
|
}
|
|
|
|
/* #NTFS_MFT_RAW
|
|
if (ex1_ntfs.threads_in_flight.count) {
|
|
Text("Threads in flight: %d", ex1_ntfs.threads_in_flight.count);
|
|
|
|
for_each(t, ex1_ntfs.threads_in_flight) {
|
|
if (thread_is_done(ex1_ntfs.threads_in_flight[t])) {
|
|
push_allocator(GPAllocator());
|
|
|
|
Thread* thread = ex1_ntfs.threads_in_flight[t];
|
|
auto task = thread_task(NTFS_Enumeration_Task);
|
|
array_free(task->drives);
|
|
|
|
// make sure to retreive any data you need to from here!
|
|
release_arena(task->pool);
|
|
|
|
thread_deinit(ex1_ntfs.threads_in_flight[t], false);
|
|
array_unordered_remove_by_index(ex1_ntfs.threads_in_flight, t);
|
|
t -= 1; // check this element index again!
|
|
}
|
|
}
|
|
}*/
|
|
|
|
/* #NTFS_MFT_RAW
|
|
if (ex1_ntfs.threads_started && !ex1_ntfs.threads_in_flight.count) {
|
|
// All threads are complete, we're free to clean up remaining memory
|
|
push_allocator(GPAllocator());
|
|
array_free(ex1_ntfs.threads);
|
|
array_free(ex1_ntfs.threads_in_flight);
|
|
|
|
// Instead maybe we should just memset this to zero.
|
|
reset_struct(&ex1_ntfs);
|
|
}
|
|
|
|
// How do I tell when all files are enumerated?
|
|
// check drives[i]->data.paths.wstrings.count count?
|
|
if (all_drives_enumerated && Button("Save drive data")) {
|
|
string file_path = format_string_temp("%s_DriveData.bin", os_get_machine_name().data);
|
|
bool success = Serialize_Win32_Drives(drives, file_path);
|
|
if (!success) { log_error("Failed to save Win32_Drive data"); }
|
|
}
|
|
|
|
if (all_drives_enumerated && Button("Clear all drive data")) {
|
|
os_clear_drive_data();
|
|
}*/
|
|
}
|
|
|
|
void ImGui_Debug_Panel () { using namespace ImGui;
|
|
push_allocator(temp());
|
|
|
|
Begin("Debug Panel");
|
|
/*SeparatorText("ex1_ntfs");
|
|
Text("Threads in flight count: %d", ex1_ntfs.threads_in_flight.count);
|
|
for_each(i, ex1_ntfs.threads) {
|
|
Text(" [%d] initialized: %d, has_context: %d, has_data: %d",
|
|
i, ex1_ntfs.threads[i].proc != nullptr, ex1_ntfs.threads[i].context != nullptr, ex1_ntfs.threads[i].data != nullptr);
|
|
}*/
|
|
// #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<Error*> 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();
|
|
} |