use std::io::Write; use std::process::{Command, Stdio}; use tempfile::NamedTempFile; // this is where it is on my ubuntu machine, sorry :3 const OBJDUMP_PATH: &'static str = "/usr/powerpc64le-linux-gnu/bin/objdump"; const AS_PATH: &'static str = "/usr/powerpc64le-linux-gnu/bin/as"; const XENIA_DIS: &'static str = "./xenia_dis/target/release/xenia_dis"; const DOLPHIN_DIS: &'static str = "./gekko_dis/target/release/gekko_dis"; const RPCS3_DIS: &'static str = "./rpcs3_dis/target/release/cell_dis"; pub fn cell_ppu_dis_bulk(inputs: &[u8]) -> Result, String> { assert!(inputs.len() % 4 == 0); let mut child = Command::new(RPCS3_DIS) .args(["-"]) .stdin(Stdio::piped()) .stdout(Stdio::piped()) .spawn() .expect("can run"); child.stdin.as_mut().expect("child has stdin") .write_all(&inputs).expect("can fill child stdin"); let out = child.wait_with_output().expect("can wait"); if !out.status.success() { let err_str = String::from_utf8_lossy(&out.stderr); eprintln!("failed to cell_dis: {:?}", err_str); panic!("failed to cell_dis: {:?}", err_str); } let text = std::str::from_utf8(&out.stdout).expect("is utf8"); Ok(text.split("\n").map(|s| s.to_string()).collect()) } pub fn cell_ppu_dis(bytes: &[u8]) -> Result { let out = Command::new(RPCS3_DIS) .args([ format!("{:02x}", bytes[3]), format!("{:02x}", bytes[2]), format!("{:02x}", bytes[1]), format!("{:02x}", bytes[0]), ]) .output() .expect("can run"); if !out.status.success() { eprintln!("failed to cell_dis {bytes:x?}"); panic!("failed to cell_dis {bytes:x?}"); } let cell_dis_out = std::str::from_utf8(out.stdout.as_slice()).expect("valid utf8"); Ok(cell_dis_out.to_string()) } pub fn gekko_dis_bulk(inputs: &[u8]) -> Result, String> { assert!(inputs.len() % 4 == 0); let mut child = Command::new(DOLPHIN_DIS) .args(["-"]) .stdin(Stdio::piped()) .stdout(Stdio::piped()) .spawn() .expect("can run"); child.stdin.as_mut().expect("child has stdin") .write_all(&inputs).expect("can fill child stdin"); let out = child.wait_with_output().expect("can wait"); if !out.status.success() { let err_str = String::from_utf8_lossy(&out.stderr); eprintln!("failed to gekko_dis: {:?}", err_str); panic!("failed to gekko_dis: {:?}", err_str); } let text = std::str::from_utf8(&out.stdout).expect("is utf8"); Ok(text.split("\n").map(|s| s.to_string()).collect()) } pub fn gekko_dis(bytes: &[u8]) -> Result { let out = Command::new(DOLPHIN_DIS) .args([ format!("{:02x}", bytes[3]), format!("{:02x}", bytes[2]), format!("{:02x}", bytes[1]), format!("{:02x}", bytes[0]), ]) .output() .expect("can run"); if !out.status.success() { eprintln!("failed to gekko_dis {bytes:x?}"); panic!("failed to gekko_dis {bytes:x?}"); } let gekko_dis_out = std::str::from_utf8(out.stdout.as_slice()).expect("valid utf8"); Ok(gekko_dis_out.to_string()) } pub fn xenia_dis_bulk(inputs: &[u8]) -> Result, String> { assert!(inputs.len() % 4 == 0); let mut child = Command::new(XENIA_DIS) .args(["-"]) .stdin(Stdio::piped()) .stdout(Stdio::piped()) .spawn() .expect("can run"); child.stdin.as_mut().expect("child has stdin") .write_all(&inputs).expect("can fill child stdin"); let out = child.wait_with_output().expect("can wait"); if !out.status.success() { let err_str = String::from_utf8_lossy(&out.stderr); eprintln!("failed to xenia_dis: {:?}", err_str); panic!("failed to xenia_dis: {:?}", err_str); } let text = std::str::from_utf8(&out.stdout).expect("is utf8"); Ok(text.split("\n").map(|s| s.to_string()).collect()) } pub fn xenia_dis(bytes: &[u8]) -> Result { let out = Command::new(XENIA_DIS) .args([ format!("{:02x}", bytes[3]), format!("{:02x}", bytes[2]), format!("{:02x}", bytes[1]), format!("{:02x}", bytes[0]), ]) .output() .expect("can run"); if !out.status.success() { eprintln!("failed to xenia_dis {bytes:x?}"); panic!("failed to xenia_dis {bytes:x?}"); } let xenia_dis_out = std::str::from_utf8(out.stdout.as_slice()).expect("valid utf8"); Ok(xenia_dis_out.to_string()) } pub fn objdump(bytes: &[u8]) -> Result { let mut source = String::new(); source.push_str(".byte "); let mut printed = false; for byte in bytes { if printed { source.push_str(", "); } source.push_str(&format!("0x{:02x}", byte)); printed = true; } source.push_str("\n"); let mut tempfile = NamedTempFile::new().unwrap(); tempfile.write_all(source.as_bytes()).expect("can write source"); let sourcepath = tempfile.into_temp_path(); let mut objpath = sourcepath.to_path_buf(); objpath.add_extension(".o"); let out = Command::new(AS_PATH) .args(&[sourcepath.display().to_string(), "-o".to_string(), objpath.display().to_string()]) .output() .expect("can run"); if !out.status.success() { eprintln!("failed to assemble {bytes:x?}:"); eprintln!("stdout: {}", std::str::from_utf8(out.stdout.as_slice()).expect("valid utf8")); eprintln!("stderr: {}", std::str::from_utf8(out.stderr.as_slice()).expect("valid utf8")); panic!("failed to assemble"); } let out = Command::new(OBJDUMP_PATH) .args(&["-d".to_string(), objpath.display().to_string()]) .output() .expect("can run"); if !out.status.success() { eprintln!("failed to objdump {bytes:x?}:"); eprintln!("stdout: {}", std::str::from_utf8(out.stdout.as_slice()).expect("valid utf8")); eprintln!("stderr: {}", std::str::from_utf8(out.stderr.as_slice()).expect("valid utf8")); panic!("failed to objdump"); } let objdump_out = std::str::from_utf8(out.stdout.as_slice()).expect("valid utf8"); let (_body, last_line) = objdump_out.trim().rsplit_once("\n").ok_or_else(|| { format!("did not have at least two lines of output in: \"{}\"", objdump_out) })?; Ok(last_line.to_owned()) } #[allow(dead_code)] pub fn gas(_text: &str) -> Result, String> { let _source = String::new(); panic!("TODO") }