module solar_map import os import time import render pub type SpaceBody = render.SpaceBody // Convert a SpaceBody into a 64-byte Little-Endian array fn body_to_bytes(b SpaceBody) []u8 { mut bytes := []u8{cap: 64} bytes << u32_to_bytes(b.id) bytes << u32_to_bytes(b.parent_id) bytes << i64_to_bytes(b.rel_x) bytes << i64_to_bytes(b.rel_y) bytes << i64_to_bytes(b.rel_z) bytes << u32_to_bytes(b.radius) bytes << b.body_type bytes << b.flags[0] bytes << b.flags[1] bytes << b.flags[2] for i in 0 .. 24 { bytes << b.comment[i] } return bytes } // Reconstruct a SpaceBody from a 64-byte slice fn body_from_bytes(data []u8) SpaceBody { mut flags_arr := [3]u8{} flags_arr[0] = data[37] flags_arr[1] = data[38] flags_arr[2] = data[39] mut comment_arr := [24]u8{} for i in 0 .. 24 { comment_arr[i] = data[40 + i] } return SpaceBody{ id: bytes_to_u32(data[0..4]) parent_id: bytes_to_u32(data[4..8]) rel_x: bytes_to_i64(data[8..16]) rel_y: bytes_to_i64(data[16..24]) rel_z: bytes_to_i64(data[24..32]) radius: bytes_to_u32(data[32..36]) body_type: data[36] flags: flags_arr comment: comment_arr } } // Public API function to process a CSV file, output binary data, and optionally render a BMP image pub fn process_galaxy(csv_path string, generate_image bool) ! { struct_size := int(sizeof(SpaceBody)) if !os.exists(csv_path) { return error('File not found: ${csv_path}') } // 1. Parse CSV sw_parse := time.new_stopwatch() lines := os.read_lines(csv_path)! mut bodies := []SpaceBody{cap: lines.len - 1} for i := 1; i < lines.len; i++ { line := lines[i].trim_space() if line == '' { continue } parts := line.split(',') if parts.len < 7 { continue } mut flag_val := u8(0) if parts.len >= 8 { flag_val = parts[7].u8() } mut body := SpaceBody{ id: parts[0].u32() parent_id: parts[1].u32() rel_x: parts[2].i64() rel_y: parts[3].i64() rel_z: parts[4].i64() radius: parts[5].u32() body_type: parts[6].u8() flags: [flag_val, u8(0), u8(0)]! } if parts.len >= 9 { comment_str := parts[8].trim_space() mut bytes_to_copy := comment_str.len if bytes_to_copy > 24 { bytes_to_copy = 24 } for j := 0; j < bytes_to_copy; j++ { body.comment[j] = comment_str[j] } } bodies << body } println('CSV parsed in: ${sw_parse.elapsed().microseconds()} µs') // 2. Write binary data bin_path := csv_path.replace('.csv', '.bin') mut bytes_to_write := []u8{cap: bodies.len * struct_size} for body in bodies { bytes_to_write << body_to_bytes(body) } os.write_file(bin_path, bytes_to_write.bytestr())! // 3. Load binary data back (Fast Load validation) sw_load := time.new_stopwatch() bin_bytes := os.read_bytes(bin_path)! body_count := bin_bytes.len / struct_size mut loaded_bodies := []SpaceBody{cap: body_count} for i in 0 .. body_count { offset := i * struct_size loaded_bodies << body_from_bytes(bin_bytes[offset .. offset + struct_size]) } println('Binary loaded in: ${sw_load.elapsed().microseconds()} µs') // 4. Render BMP image if requested if generate_image { bmp_path := csv_path.replace('.csv', '.bmp') sw_render := time.new_stopwatch() render.draw_bodies_to_bmp(loaded_bodies, bmp_path, 1920, 600)! println('Image successfully rendered to "${bmp_path}" in ${sw_render.elapsed().microseconds()} µs') } } // Helper functions for Little-Endian conversions fn u32_to_bytes(v u32) []u8 { return [u8(v), u8(v >> 8), u8(v >> 16), u8(v >> 24)] } fn i64_to_bytes(v i64) []u8 { u := u64(v) return [ u8(u), u8(u >> 8), u8(u >> 16), u8(u >> 24), u8(u >> 32), u8(u >> 40), u8(u >> 48), u8(u >> 56), ] } fn bytes_to_u32(b []u8) u32 { return u32(b[0]) | (u32(b[1]) << 8) | (u32(b[2]) << 16) | (u32(b[3]) << 24) } fn bytes_to_i64(b []u8) i64 { u := u64(b[0]) | (u64(b[1]) << 8) | (u64(b[2]) << 16) | (u64(b[3]) << 24) | (u64(b[4]) << 32) | (u64(b[5]) << 40) | (u64(b[6]) << 48) | (u64(b[7]) << 56) return i64(u) }