use clap::{App, Arg, ArgMatches};
#[cfg(feature = "sound")]
use rodio::{source::SineWave, OutputStream, Sink};
use std::io;
use std::{fs::File, io::Read};
fn get_matches() -> ArgMatches<'static> {
App::new("ChipRust Emulator CLI")
.version("1.0")
.author("Maxim K. <maximxlssoff@gmail.com>")
.about("CLI for ChipRust Emulator")
.arg(Arg::with_name("tone")
.long("tone")
.value_name("frequency")
.help("Sets a custom audio tone")
.default_value("900")
.takes_value(true))
.arg(Arg::with_name("cpu_freq")
.short("c")
.long("cpu")
.value_name("frequency")
.help("Sets a custom cpu frequency. If zero, instructions would be executed ASAP.")
.default_value("840")
.takes_value(true))
.arg(Arg::with_name("draw_freq")
.short("d")
.long("draw")
.value_name("frequency")
.help("Sets a custom draw frequency. Recommended to keep equal to the monitor refresh rate.")
.default_value("60")
.takes_value(true))
.arg(Arg::with_name("speed")
.short("s")
.long("speed")
.value_name("frequency")
.help("Sets a custom speed (actually timers' tick frequency). If zero, timers would be decremented ASAP.")
.default_value("60")
.takes_value(true))
.arg(Arg::with_name("transparent")
.short("t")
.long("transparent")
.takes_value(false)
.help("Makes background transparent."))
.arg(Arg::with_name("source")
.help("Sets the rom file to execute. Default: included tetris rom")
.index(1))
.get_matches()
}
pub struct Config {
pub draw_freq: u32,
pub cpu_freq: u32,
pub timers_freq: u32,
#[cfg(feature = "sound")]
pub sink: Option<Sink>,
pub program: Vec<u8>,
}
impl Config {
pub fn load_args() -> Result<Config, String> {
let matches = get_matches();
#[cfg(feature = "sound")]
let sound_freq = matches.value_of("tone").unwrap();
#[cfg(feature = "sound")]
let sound_freq = match sound_freq.parse::<u32>() {
Ok(v) => v,
Err(_) => {
return Err(format!(
"Can't parse {} to an unsigned integer.",
sound_freq
))
}
};
let draw_freq = matches.value_of("draw_freq").unwrap();
let draw_freq = match draw_freq.parse::<u32>() {
Ok(v) => v,
Err(_) => return Err(format!("Can't parse {} to an unsigned integer.", draw_freq)),
};
let cpu_freq = matches.value_of("cpu_freq").unwrap();
let cpu_freq = match cpu_freq.parse::<u32>() {
Ok(v) => v,
Err(_) => return Err(format!("Can't parse {} to an unsigned integer.", cpu_freq)),
};
let timers_freq = matches.value_of("speed").unwrap();
let timers_freq = match timers_freq.parse::<u32>() {
Ok(v) => v,
Err(_) => {
return Err(format!(
"Can't parse {} to an unsigned integer.",
timers_freq
))
}
};
if matches.is_present("transparent") {
unsafe { crate::BACKGROUND_COLOR[3] = 0x00 }
}
let buf = if let Some(source) = matches.value_of("source") {
let mut f = match File::open(source) {
io::Result::Ok(f) => f,
io::Result::Err(e) => return Err(format!("{}", e)),
};
let mut buf: Vec<u8> = Vec::with_capacity(3583);
match f.read_to_end(&mut buf) {
Ok(v) => {
if v >= 3583 {
return Err(format!("Source file is too big! Got {} bytes, while free memory is only 3583 bytes.", v));
}
}
Err(e) => return Err(format!("{}", e)),
}
buf
} else {
include_bytes!("default.ch8").to_vec()
};
#[cfg(feature = "sound")]
let sink = {
let (_stream, stream_handle) = OutputStream::try_default().unwrap();
let sink = Sink::try_new(&stream_handle).unwrap();
sink.pause();
let source = SineWave::new(sound_freq);
sink.append(source);
Some(sink)
};
Ok(Config {
draw_freq,
cpu_freq,
timers_freq,
#[cfg(feature = "sound")]
sink,
program: buf,
})
}
}