1use anyhow::Result;
18use cbm::disk;
19use cbm::disk::file::FileOps;
20use disasm6502;
21use log::debug;
22use std::fs::File;
23use std::io::{self, Read, Write};
24use tempfile::Builder;
25
26use crate::LoadAddress;
27
28fn load_bytes_url(url: &str) -> Result<Vec<u8>> {
30 Ok(reqwest::blocking::get(url)?.bytes()?.to_vec())
31}
32
33pub fn load_bytes(filename: &str) -> Result<Vec<u8>> {
35 let mut bytes = Vec::new();
36 if filename.starts_with("http") {
37 bytes = load_bytes_url(filename)?;
38 } else {
39 File::open(&filename)?.read_to_end(&mut bytes)?;
40 }
41 assert!(bytes.len() < 0xffff);
42 Ok(bytes)
43}
44
45pub fn load_prg(file: &str) -> Result<(LoadAddress, Vec<u8>)> {
50 match std::path::Path::new(&file).extension() {
51 None => load_with_load_address(file),
52 Some(os_str) => match os_str.to_ascii_lowercase().to_str() {
53 Some("prg") => load_with_load_address(file),
54 Some("d81") | Some("d64") => cbm_select_and_load(file),
55 _ => Err(anyhow::Error::msg("invalid file extension")),
56 },
57 }
58}
59
60pub fn purge_load_address(bytes: &mut Vec<u8>) -> Result<LoadAddress> {
74 let address = LoadAddress::from_bytes(bytes)?;
75 *bytes = bytes[2..].to_vec();
76 Ok(address)
77}
78
79pub fn cbm_open(diskimage: &str) -> Result<Box<dyn cbm::disk::Disk>> {
81 debug!("Opening CBM disk {}", diskimage);
82 if diskimage.starts_with("http") {
83 let bytes = load_bytes_url(diskimage)?;
84 let tmp_dir = Builder::new().tempdir()?;
85 let path = tmp_dir.path().join("temp-image");
86 let filename = path.to_str().unwrap_or("");
87 save_binary(filename, &bytes)?;
88 Ok(disk::open(filename, false)?)
89 } else {
90 Ok(disk::open(diskimage, false)?)
91 }
92}
93
94pub fn cbm_load_file(disk: &dyn cbm::disk::Disk, index: usize) -> Result<(LoadAddress, Vec<u8>)> {
96 let dir = disk.directory()?;
97 let entry = dir
98 .get(index)
99 .ok_or_else(|| anyhow::Error::msg("invalid selection"))?;
100 let mut bytes = Vec::<u8>::new();
101 disk.open_file(&entry.filename)?
102 .reader()?
103 .read_to_end(&mut bytes)?;
104 let load_address = purge_load_address(&mut bytes)?;
105 Ok((load_address, bytes))
106}
107
108fn cbm_select_and_load(diskimage: &str) -> Result<(LoadAddress, Vec<u8>)> {
115 let disk = cbm_open(diskimage)?;
116 let dir = disk.directory()?;
117 let prg_files = &mut dir
118 .iter()
119 .filter(|entry| entry.file_attributes.file_type == cbm::disk::directory::FileType::PRG);
120 for (counter, file) in prg_files.clone().enumerate() {
121 println!("[{}] {}.prg", counter, file.filename.to_string());
122 }
123 print!("Select: ");
124 io::stdout().flush()?;
125 let mut selection = String::new();
126 io::stdin().read_line(&mut selection)?;
127 let index = selection.trim_end().parse::<usize>()?;
128
129 let entry = prg_files
130 .nth(index)
131 .ok_or_else(|| anyhow::Error::msg("invalid selection"))?;
132 let mut bytes = Vec::<u8>::new();
133 disk.open_file(&entry.filename)?
134 .reader()?
135 .read_to_end(&mut bytes)?;
136 let load_address = purge_load_address(&mut bytes)?;
137 Ok((load_address, bytes))
138}
139
140pub fn load_with_load_address(filename: &str) -> Result<(LoadAddress, Vec<u8>)> {
142 let mut bytes = load_bytes(filename)?;
143 let load_address = purge_load_address(&mut bytes)?;
144 debug!(
145 "Read {} bytes from {}; detected load address = 0x{:x}",
146 bytes.len() + 2,
147 &filename,
148 load_address.value()
149 );
150 Ok((load_address, bytes.to_vec()))
151}
152
153pub fn save_binary(filename: &str, bytes: &[u8]) -> Result<(), std::io::Error> {
155 debug!("Saving {} bytes to {}", bytes.len(), filename);
156 File::create(filename)?.write_all(bytes)
157}
158
159pub fn hexdump(bytes: &[u8], bytes_per_line: usize) {
161 let to_hex = |i: u8| format!("0x{:02x}", i);
162 bytes.chunks(bytes_per_line).for_each(|line| {
163 for byte in line {
164 print!("{} ", to_hex(*byte));
165 }
166 println!();
167 });
168}
169pub fn disassemble(bytes: &[u8], start_address: u32) {
171 let instructions = disasm6502::from_addr_array(bytes, start_address as u16).unwrap();
172 for i in instructions {
173 println!("{}", i);
174 }
175}