use anyhow::Result;
use cbm::disk;
use cbm::disk::file::FileOps;
use disasm6502;
use log::debug;
use std::fs::File;
use std::io::{self, Read, Write};
use tempfile::Builder;
use crate::LoadAddress;
fn load_bytes_url(url: &str) -> Result<Vec<u8>> {
Ok(reqwest::blocking::get(url)?.bytes()?.to_vec())
}
pub fn load_bytes(filename: &str) -> Result<Vec<u8>> {
let mut bytes = Vec::new();
if filename.starts_with("http") {
bytes = load_bytes_url(filename)?;
} else {
File::open(&filename)?.read_to_end(&mut bytes)?;
}
assert!(bytes.len() < 0xffff);
Ok(bytes)
}
pub fn load_prg(file: &str) -> Result<(LoadAddress, Vec<u8>)> {
match std::path::Path::new(&file).extension() {
None => load_with_load_address(file),
Some(os_str) => match os_str.to_ascii_lowercase().to_str() {
Some("prg") => load_with_load_address(file),
Some("d81") | Some("d64") => cbm_select_and_load(file),
_ => Err(anyhow::Error::msg("invalid file extension")),
},
}
}
pub fn purge_load_address(bytes: &mut Vec<u8>) -> Result<LoadAddress> {
let address = LoadAddress::from_bytes(bytes)?;
*bytes = bytes[2..].to_vec();
Ok(address)
}
pub fn cbm_open(diskimage: &str) -> Result<Box<dyn cbm::disk::Disk>> {
debug!("Opening CBM disk {}", diskimage);
if diskimage.starts_with("http") {
let bytes = load_bytes_url(diskimage)?;
let tmp_dir = Builder::new().tempdir()?;
let path = tmp_dir.path().join("temp-image");
let filename = path.to_str().unwrap_or("");
save_binary(filename, &bytes)?;
Ok(disk::open(filename, false)?)
} else {
Ok(disk::open(diskimage, false)?)
}
}
pub fn cbm_load_file(disk: &dyn cbm::disk::Disk, index: usize) -> Result<(LoadAddress, Vec<u8>)> {
let dir = disk.directory()?;
let entry = dir
.get(index)
.ok_or_else(|| anyhow::Error::msg("invalid selection"))?;
let mut bytes = Vec::<u8>::new();
disk.open_file(&entry.filename)?
.reader()?
.read_to_end(&mut bytes)?;
let load_address = purge_load_address(&mut bytes)?;
Ok((load_address, bytes))
}
fn cbm_select_and_load(diskimage: &str) -> Result<(LoadAddress, Vec<u8>)> {
let disk = cbm_open(diskimage)?;
let dir = disk.directory()?;
let prg_files = &mut dir
.iter()
.filter(|entry| entry.file_attributes.file_type == cbm::disk::directory::FileType::PRG);
for (counter, file) in prg_files.clone().enumerate() {
println!("[{}] {}.prg", counter, file.filename.to_string());
}
print!("Select: ");
io::stdout().flush()?;
let mut selection = String::new();
io::stdin().read_line(&mut selection)?;
let index = selection.trim_end().parse::<usize>()?;
let entry = prg_files
.nth(index)
.ok_or_else(|| anyhow::Error::msg("invalid selection"))?;
let mut bytes = Vec::<u8>::new();
disk.open_file(&entry.filename)?
.reader()?
.read_to_end(&mut bytes)?;
let load_address = purge_load_address(&mut bytes)?;
Ok((load_address, bytes))
}
pub fn load_with_load_address(filename: &str) -> Result<(LoadAddress, Vec<u8>)> {
let mut bytes = load_bytes(filename)?;
let load_address = purge_load_address(&mut bytes)?;
debug!(
"Read {} bytes from {}; detected load address = 0x{:x}",
bytes.len() + 2,
&filename,
load_address.value()
);
Ok((load_address, bytes.to_vec()))
}
pub fn save_binary(filename: &str, bytes: &[u8]) -> Result<(), std::io::Error> {
debug!("Saving {} bytes to {}", bytes.len(), filename);
File::create(filename)?.write_all(bytes)
}
pub fn hexdump(bytes: &[u8], bytes_per_line: usize) {
let to_hex = |i: u8| format!("0x{:02x}", i);
bytes.chunks(bytes_per_line).for_each(|line| {
for byte in line {
print!("{} ", to_hex(*byte));
}
println!();
});
}
pub fn disassemble(bytes: &[u8], start_address: u32) {
let instructions = disasm6502::from_addr_array(bytes, start_address as u16).unwrap();
for i in instructions {
println!("{}", i);
}
}