diff options
| author | San Jacobs | 2025-12-21 06:50:16 +0100 |
|---|---|---|
| committer | San Jacobs | 2025-12-21 06:50:16 +0100 |
| commit | 7c5a4bd34e286608d61735bd77f5635dc6bc227e (patch) | |
| tree | 41378b766ecec3a758f1dcb7c26165d174352f06 /src/main.odin | |
| parent | eeb72b021c5c8d6ccafbcdbce398d848f49b053e (diff) | |
| download | better-report-7c5a4bd34e286608d61735bd77f5635dc6bc227e.tar.gz better-report-7c5a4bd34e286608d61735bd77f5635dc6bc227e.tar.bz2 better-report-7c5a4bd34e286608d61735bd77f5635dc6bc227e.zip | |
Major restructuring, GUI, icons, MacOS compatibility, fixes
Diffstat (limited to 'src/main.odin')
| -rw-r--r-- | src/main.odin | 197 |
1 files changed, 160 insertions, 37 deletions
diff --git a/src/main.odin b/src/main.odin index bc198b2..037d91d 100644 --- a/src/main.odin +++ b/src/main.odin @@ -7,6 +7,9 @@ import "core:path/filepath" import "core:sys/windows" import "core:strings" import "core:math" +import "core:slice" +import "core:mem" +import rl "vendor:raylib" import "wav" /* @@ -23,7 +26,7 @@ when ODIN_OS == .Windows { VERSION :: "1.6" -KNEKT := false +BRANDING : Brand = .NONE VERBOSE :: false INCLUDE_DATE :: false // By default I delete the retarded date field that says what day the report was generated. @@ -37,6 +40,9 @@ HEADER_TEMPLATE :: #load("header_template.txt", string) HEADER_FIELDS_FILENAME :: "info.txt" header_fields_file : string +BG_PNG :: #load("graphics/bg.png", []u8) +LOGO_PNG :: #load("graphics/logo.png", []u8) + Device :: enum { UNSET, ZOOM, @@ -51,6 +57,11 @@ Stages :: enum { BODY, } +Brand :: enum { + NONE, + KNEKT, +} + Info_Line :: struct { field : string, entry : string, @@ -79,41 +90,148 @@ job_list : [dynamic]Job // TODO: Changing file_list to job_list, so the Directory jobs can contain a list of all the relevant .wav files before being sent to parse_folder() main :: proc() { + when ODIN_DEBUG { + track: mem.Tracking_Allocator + mem.tracking_allocator_init(&track, context.allocator) + context.allocator = mem.tracking_allocator(&track) + + defer { + if len(track.allocation_map) > 0 { + fmt.eprintf("=== %v allocations not freed: ===\n", len(track.allocation_map)) + for _, entry in track.allocation_map { + fmt.eprintf("- %v bytes @ %v\n", entry.size, entry.location) + } + } + if len(track.bad_free_array) > 0 { + fmt.eprintf("=== %v incorrect frees: ===\n", len(track.bad_free_array)) + for entry in track.bad_free_array { + fmt.eprintf("- %p @ %v\n", entry.memory, entry.location) + } + } + mem.tracking_allocator_destroy(&track) + } + } else { + rl.SetTraceLogLevel(.ERROR) + } + when ODIN_OS == .Windows { windows.SetConsoleOutputCP(windows.CODEPAGE.UTF8) } - input_file_name : string + fmt.printfln("Report v{} - Copyright (C) 2025 Sander J. Skjegstad\n", VERSION) + + exe_path, _ := os2.get_executable_directory(context.allocator) + exe_local_fields_file := fmt.aprint(exe_path, HEADER_FIELDS_FILENAME, sep=SEPARATOR) + + instant_run_paths : [dynamic]string if len(os.args) < 2 { - fmt.println("No paths submitted.") - if os.is_file(HEADER_FIELDS_FILENAME) { - fmt.printfln("\"%v\" already exists.", HEADER_FIELDS_FILENAME) + if os.is_file(exe_local_fields_file) { + when ODIN_DEBUG do fmt.printfln("\"%v\" exists.", exe_local_fields_file) } else { - os.write_entire_file(HEADER_FIELDS_FILENAME, transmute([]u8)HEADER_TEMPLATE) - fmt.printfln("Created \"%v\".", HEADER_FIELDS_FILENAME) + os.write_entire_file(exe_local_fields_file, transmute([]u8)HEADER_TEMPLATE) + fmt.printfln("Created \"%v\".", exe_local_fields_file) } - return - } else if len(os.args) > 2 { - for arg in os.args[2:] { - if arg=="-knekt" { - KNEKT = true + } else { + for arg in os.args[1:] { + switch arg { + case "-knekt": + BRANDING = .KNEKT + case: + append(&instant_run_paths, arg) } } } - exe_path, _ := os2.get_executable_directory(context.allocator) - exe_local_fields_file := fmt.aprint(exe_path, HEADER_FIELDS_FILENAME, sep=SEPARATOR) - fmt.println(exe_local_fields_file) + input_file_name : string + if os.is_file(exe_local_fields_file) { header_fields_file = exe_local_fields_file } - fmt.printf("Input path: {}\n", os.args[1]) - input_file_name = os.args[1] - path_info, error := os.stat(input_file_name) + if len(instant_run_paths)>0 { + // Basically, if user passed something in the command line, don't do the UI. + + for path in instant_run_paths { + scan(path) + free_all(context.temp_allocator) + } + return + } + + // Setting up raylib stuff + + cosmos_color : rl.Color = {30, 30, 30, 255} + current_color : rl.Color = cosmos_color + flash_color : rl.Color = {70, 50, 60, 255} + + width : i32 = 640 + height : i32 = 480 + bg_image := rl.LoadImageFromMemory(".png", raw_data(BG_PNG), i32(len(BG_PNG))) + logo_image := rl.LoadImageFromMemory(".png", raw_data(LOGO_PNG), i32(len(LOGO_PNG))) + + rl.InitWindow(width, height, "Better Report") + rl.SetWindowIcon(logo_image) + rl.SetWindowState({.WINDOW_RESIZABLE}) + rl.SetWindowMinSize(400, 300) + rl.SetTargetFPS(60) + rl.SetExitKey(.KEY_NULL) + bg := rl.LoadTextureFromImage(bg_image) + + for !rl.WindowShouldClose() { + + delta := rl.GetFrameTime() + // Input + //---------------------------------------------------------------------------------- + if rl.IsWindowResized() { + height = rl.GetScreenHeight() + width = rl.GetScreenWidth() + when ODIN_DEBUG { + fmt.println("Resized!", height, "x", width) + } + } + dropped_files := rl.IsFileDropped() + if dropped_files { + current_color = flash_color + } + + logo_left := (width-bg.width)/2 + logo_top := (height-bg.height)/2 + + + rl.BeginDrawing() + rl.ClearBackground(current_color) + rl.DrawTexture(bg, logo_left, logo_top, {255,255,255,20}) + rl.EndDrawing() + + current_color = rl.ColorLerp(current_color, cosmos_color, 0.05) + + if dropped_files { + files := rl.LoadDroppedFiles() + defer rl.UnloadDroppedFiles(files) + for i in 0..<files.count { + path := string(files.paths[i]) + scan(path) + free_all(context.temp_allocator) + clear(&job_list) + } + } + + } + delete(job_list) + rl.CloseWindow() +} + + +scan :: proc(input_path : string) { + fmt.printf("Input path: {}\n", input_path) + path_info, error := os.stat(input_path) + + previous_header_file := header_fields_file + defer header_fields_file = previous_header_file file_count := 1 files_done := 0 + if error == os.ERROR_NONE { if(path_info.is_dir) { @@ -162,18 +280,15 @@ main :: proc() { } } render(parsed) - free_all(context.temp_allocator) files_done += 1 } fmt.printf("\nCompleted {}/{} job(s).\n\n", files_done, len(job_list)) } else { - fmt.printf("ERROR could not get path info for: {}\n", input_file_name) + fmt.printf("ERROR could not get path info for: {}\n", input_path) } - } - parse_folder :: proc(paths : Directory) -> (Report, bool) { // 888 888 888 8888b. 888 888 @@ -185,18 +300,18 @@ parse_folder :: proc(paths : Directory) -> (Report, bool) { output : Report = {} output.dir = filepath.dir(paths[0]) - wavs : [dynamic]wav.Wav + wavs := make([dynamic]wav.Wav, allocator=context.temp_allocator) max_channels := 0 for path, i in paths { - w, ok := wav.read(path) + w, ok := wav.read(path, allocator=context.temp_allocator) if ok { append(&wavs, w) max_channels = max(max_channels, w.channels) } } - header_build : [dynamic]string + header_build := make([dynamic]string, allocator=context.temp_allocator) append(&header_build, "Circled") append(&header_build, "File Name") append(&header_build, "Scene") @@ -231,9 +346,11 @@ parse_folder :: proc(paths : Directory) -> (Report, bool) { output.info_lines = make([]Info_Line, 64, context.temp_allocator) - // Header fields + previous_header_file := header_fields_file + defer header_fields_file = previous_header_file + header_file := header_fields_file local_header_file_path := fmt.tprint(output.dir, HEADER_FIELDS_FILENAME, sep=SEPARATOR) if os.is_file(local_header_file_path) { @@ -252,6 +369,11 @@ parse_folder :: proc(paths : Directory) -> (Report, bool) { if len(line)<2 { continue } + if len(line)==len("KNEKT") { + if line=="KNEKT" { + BRANDING=.KNEKT + } + } colon := strings.index_rune(line, ':') if colon==-1 { continue @@ -379,7 +501,7 @@ parse_folder :: proc(paths : Directory) -> (Report, bool) { } for did_change, i in changed { if (!did_change) && touched[i] { - field := fmt.aprintf("{}: ", output.header[i], allocator=context.temp_allocator) + field := fmt.tprintf("{}: ", output.header[i]) entry := prev_line[i] output.info_lines[output.info_line_count] = {field=field, entry=entry} output.info_line_count += 1 @@ -609,7 +731,7 @@ parse_file :: proc(path : CSV, device : Device = .UNSET) -> (Report, bool) { line_elements := strings.split(line, ",") when VERBOSE do fmt.printf(".INFO {}: {}\n", line_index, line_elements) - field := fmt.aprintf("{}:", line_elements[1], allocator=context.temp_allocator) + field := fmt.tprintf("{}:", line_elements[1]) entry := line_elements[2] output.info_lines[info_line_index].field = field output.info_lines[info_line_index].entry = entry @@ -628,7 +750,7 @@ parse_file :: proc(path : CSV, device : Device = .UNSET) -> (Report, bool) { output.info_line_count -= 1 continue } - field := fmt.aprintf("{}:", line_elements[0], allocator=context.temp_allocator) + field := fmt.tprintf("{}:", line_elements[0]) entry := line_elements[1] output.info_lines[info_line_index].field = field output.info_lines[info_line_index].entry = entry @@ -648,7 +770,7 @@ parse_file :: proc(path : CSV, device : Device = .UNSET) -> (Report, bool) { if element[:3] == "Trk" { if first_channel_index == -1 do first_channel_index = e last_channel_index = e - output.header[e] = fmt.aprintf("Trk {}", e-first_channel_index+1, allocator=context.temp_allocator) + output.header[e] = fmt.tprintf("Trk {}", e-first_channel_index+1) } if element == "Start TC" { output.tc_column_index = e @@ -954,13 +1076,13 @@ render :: proc(report : Report) { strings.write_string(&builder, PART_START) title := fmt.tprint(report.title, "- Sound Report") - logo := "" - if KNEKT { + bg := "" + if BRANDING==.KNEKT { title = fmt.tprint(report.title, "- Lydrapport") - logo = PART_KNEKT + bg = PART_KNEKT } strings.builder_replace_all(&builder, "¤¤¤TITLE¤¤¤", title) - strings.builder_replace_all(&builder, "¤¤¤LOGO¤¤¤", logo) + strings.builder_replace_all(&builder, "¤¤¤LOGO¤¤¤", bg) title_section := fmt.tprintf("<h1 class=\"day-title\">{}</h1>", report.title) strings.write_string(&builder, title_section) @@ -1000,7 +1122,7 @@ render :: proc(report : Report) { strings.write_string(&builder, PART_END) footer := PART_FOOTER - if KNEKT { + if BRANDING == .KNEKT { footer = "" } strings.builder_replace_all(&builder, "¤¤¤FOOTER¤¤¤", footer) @@ -1013,7 +1135,7 @@ render :: proc(report : Report) { } output_path := "" - if KNEKT { + if BRANDING == .KNEKT { output_path = fmt.tprintf("{}{}{}_Knekt_Lydrapport.html", report.dir, SEPARATOR, report.title) } else { output_path = fmt.tprintf("{}{}{}_Sound_Report.html", report.dir, SEPARATOR, report.title) @@ -1033,12 +1155,12 @@ indent_by :: proc(i : int) { walk_directory :: proc(path : string, file_number : ^int, depth : int = 0) -> bool { handle, ok := os.open(path) + defer os.close(handle) if ok != os.ERROR_NONE { indent_by(depth) fmt.printf("ERROR opening dir: %s\n", path) return false } - defer os.close(handle) files, okr := os.read_dir(handle, -1, context.temp_allocator) if okr != os.ERROR_NONE { @@ -1051,6 +1173,7 @@ walk_directory :: proc(path : string, file_number : ^int, depth : int = 0) -> bo } wav_files : [dynamic]string + clear(&wav_files) has_csv := false for file in files { |