Debug/server cleanup

This commit is contained in:
Mateusz Faderewski 2023-04-02 18:34:24 +02:00
parent d9456bcd14
commit 5d5d743f05
6 changed files with 224 additions and 190 deletions

View File

@ -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<Vec<u8>> 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<sc64::DebugPacket> {
if let Ok(line) = self.line_rx.try_recv() {
if line.len() == 0 {
return None;
}
let mut data: Vec<u8> = 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<u8> = 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<u8>) {
print!("{}", EUC_JP.decode(&message).0)
}
pub fn handle_save_writeback(
&self,
save_writeback: sc64::SaveWriteback,
path: &Option<PathBuf>,
) {
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<sc64::DebugPacket> {
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<PathBuf>,
) {
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<Vec<u8>, 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<sc64::DebugPacket> {
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<u16>) -> Result<Handler, sc64::Error> {

View File

@ -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);

View File

@ -39,10 +39,10 @@ impl TryFrom<u32> for DataType {
}
}
pub struct Command<'a> {
pub struct Command {
pub id: u8,
pub args: [u32; 2],
pub data: &'a [u8],
pub data: Vec<u8>,
}
pub struct Response {

View File

@ -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()?)
}

View File

@ -51,7 +51,7 @@ pub fn run_server(
}
enum Event {
Command((u8, [u32; 2], Vec<u8>)),
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<Event>, exit_flag: Arc<AtomicBoo
while !exit_flag.load(Ordering::Relaxed) {
if keepalive.elapsed() >= 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 {

View File

@ -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<Setting> for [u32; 2] {
pub enum DataPacket {
Button,
Debug(DebugPacket),
Disk(DiskPacket),
IsViewer64(String),
DebugData(DebugPacket),
DiskRequest(DiskPacket),
IsViewer64(Vec<u8>),
SaveWriteback(SaveWriteback),
UpdateStatus(UpdateStatus),
}
@ -593,9 +592,9 @@ impl TryFrom<Packet> for DataPacket {
fn try_from(value: Packet) -> Result<Self, Self::Error> {
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 {