jabyengine/src/Tools/psxreadmap/src/main.rs

106 lines
3.5 KiB
Rust

use clap::Parser;
use crossterm::{event::{self, Event as CEvent, KeyboardEnhancementFlags, KeyEventKind, PushKeyboardEnhancementFlags}, execute, terminal};
use psxreadmap::{ConsoleUI, ConsoleUIData, Event, EventReceiver, Terminal, load_memory_map, UIState};
use std::{io, path::PathBuf, sync::mpsc, thread, time::{Duration, Instant}};
use tool_helper::{Error, exit_with_error};
use ratatui::backend::CrosstermBackend;
#[derive(Parser)]
#[clap(about = "Opens and scans a MAP file to print extended information", long_about = None)]
struct CommandLine {
#[clap(value_parser, help="Input MAP file for scannning")]
input: PathBuf,
#[clap(long="wsl", default_value_t=false)]
use_wsl: bool,
#[clap(short='o')]
output: Option<PathBuf>
}
pub fn main() {
match CommandLine::try_parse() {
Ok(cmd_line) => {
match run_main(cmd_line) {
Ok(_) => (),
Err(error) => exit_with_error(error)
}
},
Err(error) => {
println!("{}", error)
}
}
}
fn run_main(cmd: CommandLine) -> Result<(), Error> {
let memory_map = load_memory_map(cmd.use_wsl, cmd.input)?; dump_memory_map(cmd.output, &memory_map)?;
let rx = start_event_loop();
let terminal = setup_console()?;
let ui_data = ConsoleUIData::new(&memory_map)?;
let mut console_ui = ConsoleUI::new(rx, terminal);
loop {
match console_ui.update()? {
UIState::Alive => (),
UIState::Render => {console_ui.render(&ui_data)?;}
UIState::Terminated => break
}
}
console_ui.close()
}
fn dump_memory_map(output: Option<PathBuf>, memory_map: &readmap::types::MemoryMap) -> Result<(), Error> {
if let Some(output) = output {
let output = tool_helper::open_output(Some(output))?;
readmap::dump::write(output, &memory_map)?;
}
Ok(())
}
fn start_event_loop() -> EventReceiver {
// Set up a mpsc (multiproducer, single consumer) channel to communicate between the input handler and the rendering loop.
let (tx, rx) = mpsc::channel();
let tick_rate = Duration::from_millis(200);
thread::spawn(move || {
let mut last_tick = Instant::now();
tx.send(Event::ForceRender).expect("Send ForceRender command!");
loop {
let timeout = tick_rate.checked_sub(last_tick.elapsed()).unwrap_or_else(|| Duration::from_secs(0));
if event::poll(timeout).expect("Event poll working") {
if let CEvent::Key(key) = event::read().expect("Can read key input") {
if key.kind == KeyEventKind::Release {
tx.send(Event::Input(key)).expect("Can send KeyInput");
}
}
}
if last_tick.elapsed() >= tick_rate {
if let Ok(_) = tx.send(Event::Tick) {
last_tick = Instant::now();
}
}
}
});
rx
}
fn setup_console() -> Result<Terminal, Error> {
terminal::enable_raw_mode()?;
// Setup Crossterm for the Terminal
let stdout = {
let mut stdout = io::stdout();
if let Err(_) = execute!(stdout, PushKeyboardEnhancementFlags(KeyboardEnhancementFlags::REPORT_EVENT_TYPES)) {
// This fails under Windows but is required for Linux
}
stdout
};
let backend = CrosstermBackend::new(stdout);
let mut terminal = Terminal::new(backend)?;
terminal.clear()?;
Ok(terminal)
}