package main import "core:fmt" import "core:os" import "core:path/filepath" import "core:sys/windows" import "core:strings" /* TODO: Move info that stays the same throughout the table up to the info header */ VERBOSE :: false PART_ONE :: #load("parts/start.html", string) PART_TWO :: #load("parts/start2.html", string) PART_END :: #load("parts/end.html", string) Device :: enum { UNSET, ZOOM, SOUND_DEVICES, } Stages :: enum { TITLE, INFO, HEADER, BODY, } Info_Line :: struct { field : string, entry : string, } Report :: struct { title : string, info_lines : []Info_Line, header : []string, table : [][]string, column_count : int, row_count : int, tc_column_index : int, } main :: proc() { when ODIN_OS == .Windows { windows.SetConsoleOutputCP(windows.CODEPAGE.UTF8) } input_file_name : string if len(os.args) < 2 { fmt.println("ERROR: No file submitted.") return } else { fmt.printf("Input path: {}\n", os.args[1]) input_file_name = os.args[1] } path_info, error := os.stat(input_file_name) if error == os.ERROR_NONE { file_list : [dynamic]string file_count := 0 if(path_info.is_dir) { fmt.println("Directory submitted! Walking directory...\n") fmt.printf("📁 {}\n", path_info.name) walk_directory(path_info.fullpath, &file_count, &file_list, 1) fmt.println("") } else { append(&file_list, strings.clone(path_info.fullpath)) } fmt.println(file_list) for file in file_list { file_info, _ := os.stat(file) parsed, ok_parse := parse(file_info.fullpath) if !ok_parse { fmt.printf("Parse failed: {}\n", file_info.fullpath) continue } output_name := fmt.aprintf("{}_Knekt.html", file_info.fullpath[:len(file_info.fullpath)-4], allocator=context.temp_allocator) render(parsed, output_name) free_all(context.temp_allocator) } } else { fmt.printf("ERROR could not get path info for: {}\n", input_file_name) } } parse :: proc(path : string, device : Device = .UNSET) -> (Report, bool) { device := device output : Report = {} data, ok := os.read_entire_file(path, context.temp_allocator) if !ok { fmt.printf("ERROR: Could not read file: {}\n", path) return {}, false } file_info, _ := os.lstat(path, context.temp_allocator) // STAGE 1 -------------------------------------------------------------- // First, we detect what kind of sound report this is line_number := 0 it := string(data) for line in strings.split_lines_iterator(&it) { if (device!=.UNSET) { break } if line == "\"SOUND REPORT\"," { device = .ZOOM if VERBOSE do fmt.printf("Detected ZOOM from quotes and comma on line index {}\n", line_number) } if line == "\"ZOOM F8\"," { device = .ZOOM if VERBOSE do fmt.printf("Detected ZOOM from \"ZOOM F8\" on line index {}\n", line_number) } if line == "SOUND REPORT" { device = .SOUND_DEVICES if VERBOSE do fmt.printf("Detected SOUND_DEVICES from unquoted SOUND REPORT line index {}\n", line_number) } line_number += 1 } if device == .UNSET { fmt.printf("ERROR: Unable to detect sound report type!\n") return {}, false } // STAGE 2 -------------------------------------------------------------- // Measuring content for allocation if device == .ZOOM { output.column_count = 21 // Ugly magic number, could be fucked by firmware update output.info_lines = make([]Info_Line, 2, context.temp_allocator) output.row_count = strings.count(string(data), "\n") - 7 // Ugly magic number, could be fucked by firmware update output.table = make([][]string, output.row_count, context.temp_allocator) output.header = make([]string, output.column_count, context.temp_allocator) for &row in output.table { row = make([]string, output.column_count, context.temp_allocator) } } // STAGE 3 -------------------------------------------------------------- // Filling with data if VERBOSE do fmt.printf("Struct before main parse:\n%#v\n", output) first_channel_index := -1 last_channel_index := -1 stage : Stages = .TITLE #partial switch device { case .SOUND_DEVICES: fmt.printf("ERROR parsing {}: Sound Devices reports are not yet supported in this version.\n", path) return {}, false case .ZOOM: fmt.printf("Parsing [{}] as ZOOM report, ", file_info.name) // Getting title if file_info.name[:8] == "F8n Pro_" { output.title = file_info.name[8:len(file_info.name)-4] } else if file_info.name[:4] == "F8n_" { // I don't own one, so I don't know if this is what the F8n does output.title = file_info.name[4:len(file_info.name)-4] } else if file_info.name[:3] == "F8_" { // TODO: Verify this is what the original F8 does output.title = file_info.name[4:len(file_info.name)-4] } fmt.printf("titled \"{}\".\n", output.title) line_index := 0 info_line_index := 0 body_line_index := 0 it := string(data) for line in strings.split_lines_iterator(&it) { switch stage { case .TITLE: if line_index == 1 { // Ugly magic number, could be fucked by firmware update stage = .INFO } case .INFO: if line == "" { stage = .HEADER continue } line_elements := strings.split(line, ",") if VERBOSE do fmt.printf(".INFO {}: {}\n", line_index, line_elements) field_raw := line_elements[0] entry_raw := line_elements[1] field := line_elements[0][1:len(field_raw)-1] entry := line_elements[1][1:len(entry_raw)-1] output.info_lines[info_line_index].field = field output.info_lines[info_line_index].entry = entry info_line_index += 1 case .HEADER: if VERBOSE do fmt.printf(".HEADER {}:", line_index) // to skip empty entry after trailing comma we do a silly slice for element, e in strings.split(line, ",")[:output.column_count] { if VERBOSE do fmt.printf(" {}", element) output.header[e] = element[1:len(element)-1] if element[:4] == "\"Tr " { 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) } if element == "\"Start TC\"" { output.tc_column_index = e } } if VERBOSE do fmt.printf("\n") stage = .BODY case .BODY: if VERBOSE do fmt.printf("first_channel_index: {}\n", first_channel_index) if VERBOSE do fmt.printf("last_channel_index: {}\n", last_channel_index) if line == "" do break if VERBOSE do fmt.printf(".BODY {}:", line_index) // to skip empty entry after trailing comma we do a silly slice for element, e in strings.split(line, ",")[:output.column_count] { if VERBOSE do fmt.printf(" {}", element) output.table[body_line_index][e] = element[1:len(element)-1] } if VERBOSE do fmt.printf("\n") body_line_index += 1 } line_index += 1 } } // STAGE 4 -------------------------------------------------------------- // Cleanup! if VERBOSE do fmt.printf("Struct before cleanup:\n%#v\n", output) // Stacking tracks to the left for &line, l in output.table { stacking_index := first_channel_index for &field, f in line[first_channel_index:last_channel_index+1] { if field != "" { line[stacking_index] = field stacking_index += 1 } } for &field, f in line[stacking_index:last_channel_index+1] { field = "" } } // Cleaning out unused columns touched := make([]bool, output.column_count, context.temp_allocator) for line, l in output.table { for field, f in line { if touched[f] do continue if field != "" { touched[f] = true } } } for &line, l in output.table { stacking_index := 0 for &field, f in line { if touched[f] { line[stacking_index] = field stacking_index += 1 } } for &field, f in line[stacking_index:] { field = "" } } stacking_index := 0 for &field, f in output.header { if touched[f] { output.header[stacking_index] = field stacking_index += 1 } } for &field, f in output.header[stacking_index:] { field = "" } new_column_count := 0 for b in touched { if b do new_column_count+=1 } output.column_count = new_column_count if VERBOSE do fmt.printf("Struct before output:\n%#v\n", output) return output, true } render :: proc(report : Report, path : string) { // Now we output the HTML. builder := strings.builder_make(context.temp_allocator) strings.write_string(&builder, PART_ONE) strings.write_string(&builder, report.title) strings.write_string(&builder, " - Lydrapport") strings.write_string(&builder, PART_TWO) for line, l in report.info_lines { strings.write_string(&builder, "

") strings.write_string(&builder, line.field) strings.write_string(&builder, " ") strings.write_string(&builder, line.entry) strings.write_string(&builder, "

\n") } strings.write_string(&builder, " \n \n \n \n \n") for field, f in report.header[:report.column_count] { if f != report.tc_column_index { strings.write_string(&builder, " \n") } strings.write_string(&builder, " \n \n \n") for line, l in report.table { strings.write_string(&builder, " \n") for field, f in line[:report.column_count] { strings.write_string(&builder, " \n") } strings.write_string(&builder, " \n") } strings.write_string(&builder, PART_END) output_text := strings.to_string(builder) os.write_entire_file(path, transmute([]u8)output_text) fmt.printf("Output: {}\n", path) } indent_by :: proc(i : int) { for x in 0..
") } else { strings.write_string(&builder, " ") } strings.write_string(&builder, field) strings.write_string(&builder, "
") strings.write_string(&builder, field) strings.write_string(&builder, "