From 5d5d743f0528a8dc0e4625107d46c2872fa695e2 Mon Sep 17 00:00:00 2001 From: Mateusz Faderewski Date: Sun, 2 Apr 2023 18:34:24 +0200 Subject: [PATCH] Debug/server cleanup --- sw/deployer/src/debug.rs | 298 +++++++++++++++++++-------------- sw/deployer/src/main.rs | 26 +-- sw/deployer/src/sc64/link.rs | 4 +- sw/deployer/src/sc64/mod.rs | 42 ++--- sw/deployer/src/sc64/server.rs | 31 ++-- sw/deployer/src/sc64/types.rs | 13 +- 6 files changed, 224 insertions(+), 190 deletions(-) diff --git a/sw/deployer/src/debug.rs b/sw/deployer/src/debug.rs index 64280fc..1d3e5ef 100644 --- a/sw/deployer/src/debug.rs +++ b/sw/deployer/src/debug.rs @@ -1,6 +1,7 @@ use crate::sc64; use chrono::Local; use colored::Colorize; +use encoding_rs::EUC_JP; use panic_message::panic_message; use std::{ fs::File, @@ -116,74 +117,99 @@ impl TryFrom> for ScreenshotMetadata { } } -const MAX_FILE_LENGTH: u64 = 8 * 1024 * 1024; +macro_rules! success { + ($($a: tt)*) => { + println!("{}", format!($($a)*).bright_blue()); + }; +} + +macro_rules! error { + ($($a: tt)*) => {{ + println!("{}", format!("Error: {}", format!($($a)*)).bright_red()); + }}; +} + +macro_rules! stop { + ($r: expr, $($a: tt)*) => {{ + error!($($a)*); + $r + }}; +} + +const MAX_PACKET_LENGTH: usize = 8 * 1024 * 1024; impl Handler { pub fn process_user_input(&self) -> Option { - if let Ok(line) = self.line_rx.try_recv() { - if line.len() == 0 { - return None; - } - let mut data: Vec = Vec::new(); - if line.matches("@").count() != 2 { - data.append(&mut line.as_bytes().to_vec()); - data.append(&mut [b'\0'].to_vec()); - return Some(sc64::DebugPacket { - datatype: DataType::Text.into(), - data, - }); - } else { - let start = line.find("@").unwrap(); - let end = line.rfind("@").unwrap(); - let path = &line[start + 1..end]; - if path.len() == 0 { - println!("Invalid path provided"); + let line = match self.line_rx.try_recv() { + Ok(line) => { + if line.len() == 0 { return None; - } - let mut file = match File::open(path) { - Ok(file) => file, - Err(error) => { - println!("Couldn't open file: {error}"); - return None; - } - }; - let length = match file.metadata() { - Ok(metadata) => metadata.len(), - Err(error) => { - println!("Couldn't get file length: {error}"); - return None; - } - }; - if length > MAX_FILE_LENGTH { - println!("File too big ({length} bytes), exceeding max size of {MAX_FILE_LENGTH} bytes"); - return None; - } - let mut data = vec![0u8; length as usize]; - if let Err(error) = file.read_exact(&mut data) { - println!("Couldn't read file contents: {error}"); - return None; - } - if line.starts_with("@") && line.ends_with("@") { - return Some(sc64::DebugPacket { - datatype: DataType::RawBinary.into(), - data, - }); } else { - let mut combined_data: Vec = Vec::new(); - combined_data.append(&mut line[0..start].as_bytes().to_vec()); - combined_data.append(&mut [b'@'].to_vec()); - combined_data.append(&mut format!("{length}").into_bytes()); - combined_data.append(&mut [b'@'].to_vec()); - combined_data.append(&mut data); - combined_data.append(&mut [b'\0'].to_vec()); - return Some(sc64::DebugPacket { - datatype: DataType::Text.into(), - data: combined_data, - }); + line } } + Err(_) => return None, + }; + + let token_count = line.matches("@").count(); + + if (token_count % 2) != 0 { + return stop!(None, "Missing closing '@' token"); } - None + + let packet = if token_count == 2 && line.starts_with("@") && line.ends_with("@") { + sc64::DebugPacket { + datatype: DataType::RawBinary.into(), + data: match load_file(line.trim_matches('@')) { + Ok(data) => data, + Err(error) => return stop!(None, "{error}"), + }, + } + } else { + let mut is_text = true; + let mut path = String::new(); + let mut character_buffer = vec![0u8; 4]; + let mut data = vec![0u8; 0]; + for character in line.chars() { + if character == '@' { + if is_text { + is_text = false; + } else { + let mut file = match load_file(&path) { + Ok(data) => data, + Err(error) => return stop!(None, "{error}"), + }; + let length = file.len(); + data.append(&mut format!("@{length}@").into_bytes()); + data.append(&mut file); + is_text = true; + path = String::new(); + } + } else { + if is_text { + let encoded = character.encode_utf8(&mut character_buffer); + data.append(&mut encoded.as_bytes().to_vec()); + } else { + path.push(character); + } + } + } + sc64::DebugPacket { + datatype: DataType::Text.into(), + data, + } + }; + + if packet.data.len() > MAX_PACKET_LENGTH { + return stop!( + None, + "Debug packet size too big ({}), exceeding maximum size of {}", + packet.data.len(), + MAX_PACKET_LENGTH + ); + } + + Some(packet) } pub fn handle_debug_packet(&mut self, debug_packet: sc64::DebugPacket) { @@ -194,9 +220,53 @@ impl Handler { DataType::Header => self.handle_datatype_header(&data), DataType::Screenshot => self.handle_datatype_screenshot(&data), DataType::GDB => self.handle_datatype_gdb(&data), - _ => { - println!("Unknown debug packet datatype: 0x{datatype:02X}"); + _ => error!("Received unknown debug packet datatype: 0x{datatype:02X}"), + } + } + + pub fn handle_is_viewer_64(&self, message: Vec) { + print!("{}", EUC_JP.decode(&message).0) + } + + pub fn handle_save_writeback( + &self, + save_writeback: sc64::SaveWriteback, + path: &Option, + ) { + let filename = &if let Some(path) = path { + path.to_string_lossy().to_string() + } else { + generate_filename( + "save", + match save_writeback.save { + sc64::SaveType::Eeprom4k | sc64::SaveType::Eeprom16k => "eep", + sc64::SaveType::Sram | sc64::SaveType::SramBanked | sc64::SaveType::Sram1m => { + "srm" + } + sc64::SaveType::Flashram => "fla", + _ => "sav", + }, + ) + }; + match File::create(filename) { + Ok(mut file) => { + if let Err(error) = file.write_all(&save_writeback.data) { + error!("Couldn't write save [{filename}]: {error}"); + } + success!("Wrote [{}] save to [{filename}]", save_writeback.save); } + Err(error) => error!("Couldn't create save writeback file [{filename}]: {error}"), + } + } + + pub fn receive_gdb_packet(&self) -> Option { + if let Some(data) = self.gdb_rx.try_recv().ok() { + Some(sc64::DebugPacket { + datatype: DataType::GDB.into(), + data, + }) + } else { + None } } @@ -205,17 +275,15 @@ impl Handler { } fn handle_datatype_raw_binary(&self, data: &[u8]) { - let filename = &self.generate_filename("binaryout", "bin"); + let filename = &generate_filename("binaryout", "bin"); match File::create(filename) { Ok(mut file) => { if let Err(error) = file.write_all(data) { - println!("Error during raw binary write: {error}"); + error!("Couldn't write raw binary [{filename}]: {error}"); } - println!("Wrote [{}] bytes to [{}]", data.len(), filename); - } - Err(error) => { - println!("Error during raw binary file creation: {error}"); + success!("Wrote [{}] bytes to [{filename}]", data.len()); } + Err(error) => error!("Couldn't create raw binary file [{filename}]: {error}"), } } @@ -226,10 +294,7 @@ impl Handler { fn handle_datatype_screenshot(&mut self, data: &[u8]) { let header = match self.header.take() { Some(header) => header, - None => { - println!("Got screenshot packet without header data"); - return; - } + None => return error!("Got screenshot packet without header data"), }; let ScreenshotMetadata { format, @@ -237,15 +302,11 @@ impl Handler { width, } = match header.try_into() { Ok(data) => data, - Err(error) => { - println!("{error}"); - return; - } + Err(error) => return error!("{error}"), }; let format_size: u32 = format.into(); if data.len() as u32 != format_size * width * height { - println!("Data length did not match header data for screenshot datatype"); - return; + return error!("Data length did not match header data for screenshot datatype"); } let mut image = image::RgbaImage::new(width, height); for (x, y, pixel) in image.enumerate_pixels_mut() { @@ -262,68 +323,45 @@ impl Handler { ScreenshotPixelFormat::Rgba32 => [p[0], p[1], p[2], p[3]], } } - let filename = &self.generate_filename("screenshot", "png"); + let filename = &generate_filename("screenshot", "png"); if let Some(error) = image.save(filename).err() { - println!("Error during image save: {error}"); - return; + return error!("Couldn't save screenshot [{filename}]: {error}"); } - println!("Wrote {width}x{height} pixels to [{filename}]"); + success!("Wrote {width}x{height} pixels to [{filename}]"); } fn handle_datatype_gdb(&self, data: &[u8]) { self.gdb_tx.send(data.to_vec()).ok(); } +} - pub fn handle_save_writeback( - &self, - save_writeback: sc64::SaveWriteback, - path: &Option, - ) { - let filename = &if let Some(path) = path { - path.to_string_lossy().to_string() - } else { - self.generate_filename( - "save", - match save_writeback.save { - sc64::SaveType::Eeprom4k | sc64::SaveType::Eeprom16k => "eep", - sc64::SaveType::Sram | sc64::SaveType::SramBanked | sc64::SaveType::Sram1m => { - "srm" - } - sc64::SaveType::Flashram => "fla", - _ => "sav", - }, - ) - }; - match File::create(filename) { - Ok(mut file) => { - if let Err(error) = file.write_all(&save_writeback.data) { - println!("Error during save write: {error}"); - } - println!("Wrote [{}] save to [{}]", save_writeback.save, filename); - } - Err(error) => { - println!("Error during save writeback file creation: {error}"); - } - } +fn load_file(path: &str) -> Result, String> { + if path.len() == 0 { + return Err(format!("Couldn't open file: Specified path is empty")); } + let mut file = match File::open(path) { + Ok(file) => file, + Err(error) => return Err(format!("Couldn't open file [{path}]: {error}")), + }; + let length = match file.metadata() { + Ok(metadata) => metadata.len(), + Err(error) => return Err(format!("Couldn't get file [{path}] length: {error}")), + }; + if length > MAX_PACKET_LENGTH as u64 { + return Err(format!("File [{path}] size too big")); + } + let mut data = vec![0u8; length as usize]; + match file.read_exact(&mut data) { + Ok(()) => Ok(data), + Err(error) => Err(format!("Couldn't read file [{path}] contents: {error}")), + } +} - fn generate_filename(&self, prefix: &str, extension: &str) -> String { - format!( - "{prefix}-{}.{extension}", - Local::now().format("%y%m%d%H%M%S.%f") - ) - } - - pub fn receive_gdb_packet(&self) -> Option { - if let Some(data) = self.gdb_rx.try_recv().ok() { - Some(sc64::DebugPacket { - datatype: DataType::GDB.into(), - data, - }) - } else { - None - } - } +fn generate_filename(prefix: &str, extension: &str) -> String { + format!( + "{prefix}-{}.{extension}", + Local::now().format("%y%m%d%H%M%S.%f") + ) } pub fn new(gdb_port: Option) -> Result { diff --git a/sw/deployer/src/main.rs b/sw/deployer/src/main.rs index 998f888..06cebeb 100644 --- a/sw/deployer/src/main.rs +++ b/sw/deployer/src/main.rs @@ -504,22 +504,22 @@ fn handle_64dd_command(connection: Connection, args: &_64DDArgs) -> Result<(), s while !exit.load(Ordering::Relaxed) { if let Some(data_packet) = sc64.receive_data_packet()? { match data_packet { - sc64::DataPacket::Disk(mut packet) => { - let track = packet.info.track; - let head = packet.info.head; - let block = packet.info.block; + sc64::DataPacket::DiskRequest(mut disk_packet) => { + let track = disk_packet.info.track; + let head = disk_packet.info.head; + let block = disk_packet.info.block; if let Some(ref mut disk) = selected_disk { - let (reply_packet, rw) = match packet.kind { + let (reply_packet, rw) = match disk_packet.kind { sc64::DiskPacketKind::Read => ( disk.read_block(track, head, block)?.map(|data| { - packet.info.set_data(&data); - packet + disk_packet.info.set_data(&data); + disk_packet }), "[R]".bright_blue(), ), sc64::DiskPacketKind::Write => ( - disk.write_block(track, head, block, &packet.info.data)? - .map(|_| packet), + disk.write_block(track, head, block, &disk_packet.info.data)? + .map(|_| disk_packet), "[W]".bright_yellow(), ), }; @@ -595,11 +595,11 @@ fn handle_debug_command(connection: Connection, args: &DebugArgs) -> Result<(), while !exit.load(Ordering::Relaxed) { if let Some(data_packet) = sc64.receive_data_packet()? { match data_packet { - sc64::DataPacket::IsViewer64(message) => { - print!("{message}") + sc64::DataPacket::DebugData(debug_packet) => { + debug_handler.handle_debug_packet(debug_packet); } - sc64::DataPacket::Debug(debug_packet) => { - debug_handler.handle_debug_packet(debug_packet) + sc64::DataPacket::IsViewer64(message) => { + debug_handler.handle_is_viewer_64(message); } sc64::DataPacket::SaveWriteback(save_writeback) => { debug_handler.handle_save_writeback(save_writeback, &args.save); diff --git a/sw/deployer/src/sc64/link.rs b/sw/deployer/src/sc64/link.rs index 15bef2d..0f489be 100644 --- a/sw/deployer/src/sc64/link.rs +++ b/sw/deployer/src/sc64/link.rs @@ -39,10 +39,10 @@ impl TryFrom for DataType { } } -pub struct Command<'a> { +pub struct Command { pub id: u8, pub args: [u32; 2], - pub data: &'a [u8], + pub data: Vec, } pub struct Response { diff --git a/sw/deployer/src/sc64/mod.rs b/sw/deployer/src/sc64/mod.rs index d4c7b13..fce3d3c 100644 --- a/sw/deployer/src/sc64/mod.rs +++ b/sw/deployer/src/sc64/mod.rs @@ -103,7 +103,7 @@ impl SC64 { let data = self.link.execute_command(&Command { id: b'v', args: [0, 0], - data: &[], + data: vec![], })?; if data.len() != 4 { return Err(Error::new( @@ -117,7 +117,7 @@ impl SC64 { let data = self.link.execute_command(&Command { id: b'V', args: [0, 0], - data: &[], + data: vec![], })?; if data.len() != 8 { return Err(Error::new( @@ -134,7 +134,7 @@ impl SC64 { self.link.execute_command(&Command { id: b'R', args: [0, 0], - data: &[], + data: vec![], })?; Ok(()) } @@ -152,7 +152,7 @@ impl SC64 { self.link.execute_command(&Command { id: b'B', args, - data: &[], + data: vec![], })?; Ok(()) } @@ -161,7 +161,7 @@ impl SC64 { let data = self.link.execute_command(&Command { id: b'c', args: [config_id.into(), 0], - data: &[], + data: vec![], })?; if data.len() != 4 { return Err(Error::new( @@ -176,7 +176,7 @@ impl SC64 { self.link.execute_command(&Command { id: b'C', args: config.into(), - data: &[], + data: vec![], })?; Ok(()) } @@ -185,7 +185,7 @@ impl SC64 { let data = self.link.execute_command(&Command { id: b'a', args: [setting_id.into(), 0], - data: &[], + data: vec![], })?; if data.len() != 4 { return Err(Error::new( @@ -200,7 +200,7 @@ impl SC64 { self.link.execute_command(&Command { id: b'A', args: setting.into(), - data: &[], + data: vec![], })?; Ok(()) } @@ -209,7 +209,7 @@ impl SC64 { let data = self.link.execute_command(&Command { id: b't', args: [0, 0], - data: &[], + data: vec![], })?; if data.len() != 8 { return Err(Error::new( @@ -223,7 +223,7 @@ impl SC64 { self.link.execute_command(&Command { id: b'T', args: convert_from_datetime(datetime), - data: &[], + data: vec![], })?; Ok(()) } @@ -232,7 +232,7 @@ impl SC64 { let data = self.link.execute_command(&Command { id: b'm', args: [address, length as u32], - data: &[], + data: vec![], })?; if data.len() != length { return Err(Error::new( @@ -246,7 +246,7 @@ impl SC64 { self.link.execute_command(&Command { id: b'M', args: [address, data.len() as u32], - data, + data: data.to_vec(), })?; Ok(()) } @@ -256,7 +256,7 @@ impl SC64 { &Command { id: b'U', args: [datatype as u32, data.len() as u32], - data, + data: data.to_vec(), }, true, false, @@ -268,7 +268,7 @@ impl SC64 { self.link.execute_command(&Command { id: b'D', args: [error as u32, 0], - data: &[], + data: vec![], })?; Ok(()) } @@ -277,7 +277,7 @@ impl SC64 { self.link.execute_command(&Command { id: b'W', args: [0, 0], - data: &[], + data: vec![], })?; Ok(()) } @@ -286,7 +286,7 @@ impl SC64 { let data = self.link.execute_command(&Command { id: b'p', args: [wait as u32, 0], - data: &[], + data: vec![], })?; if data.len() != 4 { return Err(Error::new( @@ -301,7 +301,7 @@ impl SC64 { self.link.execute_command(&Command { id: b'P', args: [address, 0], - data: &[], + data: vec![], })?; Ok(()) } @@ -311,7 +311,7 @@ impl SC64 { &Command { id: b'f', args: [address, 0], - data: &[], + data: vec![], }, false, true, @@ -335,7 +335,7 @@ impl SC64 { &Command { id: b'F', args: [address, length as u32], - data: &[], + data: vec![], }, false, true, @@ -352,7 +352,7 @@ impl SC64 { let data = self.link.execute_command(&Command { id: b'?', args: [0, 0], - data: &[], + data: vec![], })?; Ok(data.try_into()?) } @@ -361,7 +361,7 @@ impl SC64 { let data = self.link.execute_command(&Command { id: b'%', args: [0, 0], - data: &[], + data: vec![], })?; Ok(data.try_into()?) } diff --git a/sw/deployer/src/sc64/server.rs b/sw/deployer/src/sc64/server.rs index f67e61d..091a479 100644 --- a/sw/deployer/src/sc64/server.rs +++ b/sw/deployer/src/sc64/server.rs @@ -51,7 +51,7 @@ pub fn run_server( } enum Event { - Command((u8, [u32; 2], Vec)), + Command(Command), Response(Response), Packet(Packet), KeepAlive, @@ -114,12 +114,8 @@ fn server_process_events( ) -> Result<(), Error> { for event in event_receiver.into_iter() { match event { - Event::Command((id, args, data)) => { - serial_writer.send_command(&Command { - id, - args, - data: &data, - })?; + Event::Command(command) => { + serial_writer.send_command(&command)?; } Event::Response(response) => { stream_writer.write_all(&u32::to_be_bytes(DataType::Response.into()))?; @@ -188,10 +184,12 @@ fn server_stream_thread( } let mut buffer = [0u8; 4]; - let mut id = [0u8; 1]; + let mut id_buffer = [0u8; 1]; let mut args = [0u32; 2]; - stream_reader.read_exact(&mut id)?; + stream_reader.read_exact(&mut id_buffer)?; + let id = id_buffer[0]; + stream_reader.read_exact(&mut buffer)?; args[0] = u32::from_be_bytes(buffer); stream_reader.read_exact(&mut buffer)?; @@ -202,8 +200,10 @@ fn server_stream_thread( let mut data = vec![0u8; command_data_length]; stream_reader.read_exact(&mut data)?; - let event = Event::Command((id[0], args, data)); - if event_sender.send(event).is_err() { + if event_sender + .send(Event::Command(Command { id, args, data })) + .is_err() + { break; } } @@ -222,15 +222,13 @@ fn server_serial_thread( let response = serial_reader.process_incoming_data(DataType::Packet, &mut packets)?; if let Some(response) = response { - let event = Event::Response(response); - if event_sender.send(event).is_err() { + if event_sender.send(Event::Response(response)).is_err() { break; } } if let Some(packet) = packets.pop_front() { - let event = Event::Packet(packet); - if event_sender.send(event).is_err() { + if event_sender.send(Event::Packet(packet)).is_err() { break; } } @@ -245,8 +243,7 @@ fn server_keepalive_thread(event_sender: Sender, exit_flag: Arc= Duration::from_secs(5) { keepalive = Instant::now(); - let event = Event::KeepAlive; - if event_sender.send(event).is_err() { + if event_sender.send(Event::KeepAlive).is_err() { break; } } else { diff --git a/sw/deployer/src/sc64/types.rs b/sw/deployer/src/sc64/types.rs index 03f6b9a..3bbc354 100644 --- a/sw/deployer/src/sc64/types.rs +++ b/sw/deployer/src/sc64/types.rs @@ -1,5 +1,4 @@ use super::{link::Packet, Error}; -use encoding_rs::EUC_JP; use std::fmt::Display; #[derive(Clone, Copy)] @@ -581,9 +580,9 @@ impl From for [u32; 2] { pub enum DataPacket { Button, - Debug(DebugPacket), - Disk(DiskPacket), - IsViewer64(String), + DebugData(DebugPacket), + DiskRequest(DiskPacket), + IsViewer64(Vec), SaveWriteback(SaveWriteback), UpdateStatus(UpdateStatus), } @@ -593,9 +592,9 @@ impl TryFrom for DataPacket { fn try_from(value: Packet) -> Result { Ok(match value.id { b'B' => Self::Button, - b'U' => Self::Debug(value.data.try_into()?), - b'D' => Self::Disk(value.data.try_into()?), - b'I' => Self::IsViewer64(EUC_JP.decode(&value.data).0.into()), + b'U' => Self::DebugData(value.data.try_into()?), + b'D' => Self::DiskRequest(value.data.try_into()?), + b'I' => Self::IsViewer64(value.data), b'S' => Self::SaveWriteback(value.data.try_into()?), b'F' => { if value.data.len() != 4 {