nushell/src/plugins/binaryview.rs

448 lines
13 KiB
Rust
Raw Normal View History

2019-07-05 06:23:28 +02:00
use crossterm::{cursor, terminal, Attribute, RawScreen};
use nu::{
outln, serve_plugin, CallInfo, Plugin, Primitive, ShellError, Signature, UntaggedValue, Value,
};
use nu_source::AnchorLocation;
2019-07-05 09:53:09 +02:00
use pretty_hex::*;
2019-07-04 07:23:05 +02:00
struct BinaryView;
impl BinaryView {
fn new() -> BinaryView {
BinaryView
}
}
impl Plugin for BinaryView {
2019-08-02 21:15:07 +02:00
fn config(&mut self) -> Result<Signature, ShellError> {
Ok(Signature::build("binaryview")
.desc("Autoview of binary data.")
2019-10-28 06:15:35 +01:00
.switch("lores", "use low resolution output mode"))
2019-07-04 07:23:05 +02:00
}
fn sink(&mut self, call_info: CallInfo, input: Vec<Value>) {
2019-07-04 07:23:05 +02:00
for v in input {
2019-09-29 07:13:56 +02:00
let value_anchor = v.anchor();
match &v.value {
UntaggedValue::Primitive(Primitive::Binary(b)) => {
let _ = view_binary(&b, value_anchor.as_ref(), call_info.args.has("lores"));
2019-07-04 07:23:05 +02:00
}
_ => {}
}
}
}
}
fn view_binary(
b: &[u8],
2019-09-29 07:18:59 +02:00
source: Option<&AnchorLocation>,
lores_mode: bool,
) -> Result<(), Box<dyn std::error::Error>> {
if b.len() > 3 {
match (b[0], b[1], b[2]) {
(0x4e, 0x45, 0x53) => {
2019-08-23 05:29:08 +02:00
#[cfg(feature = "rawkey")]
{
view_contents_interactive(b, source, lores_mode)?;
return Ok(());
}
#[cfg(not(feature = "rawkey"))]
{
2019-11-04 16:47:03 +01:00
outln!("Interactive binary viewing currently requires the 'rawkey' feature");
2019-08-23 05:29:08 +02:00
return Ok(());
}
}
_ => {}
}
}
view_contents(b, source, lores_mode)?;
Ok(())
}
2019-07-14 20:38:03 +02:00
pub struct RenderContext {
pub width: usize,
pub height: usize,
2019-07-14 09:54:30 +02:00
pub frame_buffer: Vec<(u8, u8, u8)>,
pub since_last_button: Vec<usize>,
2019-07-16 05:25:36 +02:00
pub lores_mode: bool,
}
2019-07-14 20:38:03 +02:00
impl RenderContext {
2019-07-16 05:25:36 +02:00
pub fn blank(lores_mode: bool) -> RenderContext {
2019-07-14 20:38:03 +02:00
RenderContext {
width: 0,
height: 0,
frame_buffer: vec![],
since_last_button: vec![0; 8],
2019-07-16 05:25:36 +02:00
lores_mode,
}
}
pub fn clear(&mut self) {
2019-07-14 09:54:30 +02:00
self.frame_buffer = vec![(0, 0, 0); self.width * self.height as usize];
}
2019-07-14 20:38:03 +02:00
fn render_to_screen_lores(&mut self) -> Result<(), Box<dyn std::error::Error>> {
let mut prev_color: Option<(u8, u8, u8)> = None;
let mut prev_count = 1;
let cursor = cursor();
cursor.goto(0, 0)?;
2019-07-14 20:38:03 +02:00
for pixel in &self.frame_buffer {
match prev_color {
Some(c) if c == *pixel => {
prev_count += 1;
}
Some(c) => {
print!(
"{}",
ansi_term::Colour::RGB(c.0, c.1, c.2)
.paint((0..prev_count).map(|_| "").collect::<String>())
);
prev_color = Some(*pixel);
prev_count = 1;
}
_ => {
prev_color = Some(*pixel);
prev_count = 1;
}
2019-07-14 09:54:30 +02:00
}
}
2019-07-14 20:38:03 +02:00
if prev_count > 0 {
if let Some(color) = prev_color {
print!(
"{}",
ansi_term::Colour::RGB(color.0, color.1, color.2)
.paint((0..prev_count).map(|_| "").collect::<String>())
);
}
}
2019-11-04 16:47:03 +01:00
outln!("{}", Attribute::Reset);
2019-07-14 20:38:03 +02:00
Ok(())
}
fn render_to_screen_hires(&mut self) -> Result<(), Box<dyn std::error::Error>> {
2019-07-14 09:28:56 +02:00
let mut prev_fg: Option<(u8, u8, u8)> = None;
let mut prev_bg: Option<(u8, u8, u8)> = None;
2019-07-05 06:23:28 +02:00
let mut prev_count = 1;
2019-07-14 09:28:56 +02:00
let mut pos = 0;
let fb_len = self.frame_buffer.len();
2019-07-14 20:38:03 +02:00
let cursor = cursor();
cursor.goto(0, 0)?;
2019-07-14 09:28:56 +02:00
while pos < (fb_len - self.width) {
2019-07-14 09:54:30 +02:00
let top_pixel = self.frame_buffer[pos];
let bottom_pixel = self.frame_buffer[pos + self.width];
2019-07-14 09:28:56 +02:00
match (prev_fg, prev_bg) {
(Some(c), Some(d)) if c == top_pixel && d == bottom_pixel => {
2019-07-05 06:23:28 +02:00
prev_count += 1;
}
2019-07-14 09:28:56 +02:00
(Some(c), Some(d)) => {
print!(
2019-07-05 06:23:28 +02:00
"{}",
ansi_term::Colour::RGB(c.0, c.1, c.2)
2019-07-14 09:28:56 +02:00
.on(ansi_term::Colour::RGB(d.0, d.1, d.2,))
.paint((0..prev_count).map(|_| "").collect::<String>())
2019-07-05 06:23:28 +02:00
);
2019-07-14 09:28:56 +02:00
prev_fg = Some(top_pixel);
prev_bg = Some(bottom_pixel);
2019-07-05 06:23:28 +02:00
prev_count = 1;
}
2019-07-05 06:23:28 +02:00
_ => {
2019-07-14 09:28:56 +02:00
prev_fg = Some(top_pixel);
prev_bg = Some(bottom_pixel);
2019-07-05 06:23:28 +02:00
prev_count = 1;
}
}
2019-07-14 09:28:56 +02:00
pos += 1;
if pos % self.width == 0 {
pos += self.width;
}
2019-07-05 06:23:28 +02:00
}
if prev_count > 0 {
2019-07-14 09:28:56 +02:00
match (prev_fg, prev_bg) {
(Some(c), Some(d)) => {
print!(
"{}",
ansi_term::Colour::RGB(c.0, c.1, c.2)
.on(ansi_term::Colour::RGB(d.0, d.1, d.2,))
.paint((0..prev_count).map(|_| "").collect::<String>())
);
}
_ => {}
}
}
2019-11-04 16:47:03 +01:00
outln!("{}", Attribute::Reset);
Ok(())
}
2019-07-14 20:38:03 +02:00
pub fn flush(&mut self) -> Result<(), Box<dyn std::error::Error>> {
2019-07-16 05:25:36 +02:00
if self.lores_mode {
2019-07-14 20:38:03 +02:00
self.render_to_screen_lores()
2019-07-16 05:25:36 +02:00
} else {
self.render_to_screen_hires()
2019-07-14 20:38:03 +02:00
}
}
pub fn update(&mut self) -> Result<(), Box<dyn std::error::Error>> {
let terminal = terminal();
let terminal_size = terminal.terminal_size();
if (self.width != terminal_size.0 as usize) || (self.height != terminal_size.1 as usize) {
let cursor = cursor();
cursor.hide()?;
2019-07-29 09:46:24 +02:00
self.width = terminal_size.0 as usize;
2019-07-16 05:25:36 +02:00
self.height = if self.lores_mode {
2019-07-29 09:46:24 +02:00
terminal_size.1 as usize - 1
2019-07-16 05:25:36 +02:00
} else {
2019-07-29 09:46:24 +02:00
(terminal_size.1 as usize - 1) * 2
2019-07-14 20:38:03 +02:00
};
}
Ok(())
}
}
2019-07-05 09:53:09 +02:00
#[derive(Debug)]
struct RawImageBuffer {
dimensions: (u64, u64),
colortype: image::ColorType,
buffer: Vec<u8>,
}
2019-11-16 09:23:04 +01:00
fn load_from_png_buffer(buffer: &[u8]) -> Option<RawImageBuffer> {
2019-07-05 09:53:09 +02:00
use image::ImageDecoder;
let decoder = image::png::PNGDecoder::new(buffer);
if decoder.is_err() {
return None;
}
let decoder = decoder.unwrap();
let dimensions = decoder.dimensions();
let colortype = decoder.colortype();
let buffer = decoder.read_image().unwrap();
Some(RawImageBuffer {
dimensions,
colortype,
buffer,
})
}
2019-11-16 09:23:04 +01:00
fn load_from_jpg_buffer(buffer: &[u8]) -> Option<RawImageBuffer> {
2019-07-05 09:53:09 +02:00
use image::ImageDecoder;
let decoder = image::jpeg::JPEGDecoder::new(buffer);
if decoder.is_err() {
return None;
}
let decoder = decoder.unwrap();
let dimensions = decoder.dimensions();
let colortype = decoder.colortype();
let buffer = decoder.read_image().unwrap();
Some(RawImageBuffer {
dimensions,
colortype,
buffer,
})
}
pub fn view_contents(
buffer: &[u8],
2019-09-29 07:18:59 +02:00
_source: Option<&AnchorLocation>,
lores_mode: bool,
) -> Result<(), Box<dyn std::error::Error>> {
2019-07-05 09:53:09 +02:00
let mut raw_image_buffer = load_from_png_buffer(buffer);
if raw_image_buffer.is_none() {
raw_image_buffer = load_from_jpg_buffer(buffer);
}
if raw_image_buffer.is_none() {
//Not yet supported
2019-11-04 16:47:03 +01:00
outln!("{:?}", buffer.hex_dump());
2019-07-05 09:53:09 +02:00
return Ok(());
}
let raw_image_buffer = raw_image_buffer.unwrap();
2019-07-16 05:25:36 +02:00
let mut render_context: RenderContext = RenderContext::blank(lores_mode);
2019-07-14 20:38:03 +02:00
let _ = render_context.update();
render_context.clear();
2019-07-05 09:53:09 +02:00
match raw_image_buffer.colortype {
image::ColorType::RGBA(8) => {
let img = image::ImageBuffer::<image::Rgba<u8>, Vec<u8>>::from_vec(
raw_image_buffer.dimensions.0 as u32,
raw_image_buffer.dimensions.1 as u32,
raw_image_buffer.buffer,
)
.unwrap();
let resized_img = image::imageops::resize(
&img,
2019-07-14 20:38:03 +02:00
render_context.width as u32,
render_context.height as u32,
2019-07-05 09:53:09 +02:00
image::FilterType::Lanczos3,
);
let mut count = 0;
for pixel in resized_img.pixels() {
use image::Pixel;
let rgb = pixel.to_rgb();
2019-07-14 20:38:03 +02:00
render_context.frame_buffer[count] = (rgb[0], rgb[1], rgb[2]);
2019-07-05 09:53:09 +02:00
count += 1;
}
}
image::ColorType::RGB(8) => {
let img = image::ImageBuffer::<image::Rgb<u8>, Vec<u8>>::from_vec(
raw_image_buffer.dimensions.0 as u32,
raw_image_buffer.dimensions.1 as u32,
raw_image_buffer.buffer,
)
.unwrap();
let resized_img = image::imageops::resize(
&img,
2019-07-14 20:38:03 +02:00
render_context.width as u32,
render_context.height as u32,
2019-07-05 09:53:09 +02:00
image::FilterType::Lanczos3,
);
let mut count = 0;
for pixel in resized_img.pixels() {
use image::Pixel;
let rgb = pixel.to_rgb();
2019-07-14 20:38:03 +02:00
render_context.frame_buffer[count] = (rgb[0], rgb[1], rgb[2]);
2019-07-05 09:53:09 +02:00
count += 1;
}
}
_ => {
//Not yet supported
2019-11-04 16:47:03 +01:00
outln!("{:?}", buffer.hex_dump());
2019-07-05 09:53:09 +02:00
return Ok(());
}
}
2019-07-14 20:38:03 +02:00
render_context.flush()?;
2019-07-05 09:53:09 +02:00
let cursor = cursor();
let _ = cursor.show();
2019-09-02 08:11:05 +02:00
let _ = RawScreen::disable_raw_mode();
2019-07-05 09:53:09 +02:00
Ok(())
}
2019-08-23 05:29:08 +02:00
#[cfg(feature = "rawkey")]
2019-07-14 20:38:03 +02:00
pub fn view_contents_interactive(
buffer: &[u8],
2019-09-29 07:18:59 +02:00
source: Option<&AnchorLocation>,
2019-07-16 05:25:36 +02:00
lores_mode: bool,
2019-07-14 20:38:03 +02:00
) -> Result<(), Box<dyn std::error::Error>> {
use rawkey::{KeyCode, RawKey};
2019-09-29 07:18:59 +02:00
let sav_path = if let Some(AnchorLocation::File(f)) = source {
let mut path = std::path::PathBuf::from(f);
path.set_extension("sav");
Some(path)
} else {
None
};
2019-07-27 22:09:25 +02:00
let mut nes = neso::Nes::new(0.0);
let rawkey = RawKey::new();
nes.load_rom(&buffer);
if let Some(ref sav_path) = sav_path {
if let Ok(contents) = std::fs::read(sav_path) {
let _ = nes.load_state(&contents);
}
}
nes.reset();
if let Ok(_raw) = RawScreen::into_raw_mode() {
2019-07-16 05:25:36 +02:00
let mut render_context: RenderContext = RenderContext::blank(lores_mode);
let input = crossterm::input();
let _ = input.read_async();
let cursor = cursor();
let buttons = vec![
KeyCode::Alt,
KeyCode::LeftControl,
KeyCode::Tab,
KeyCode::BackSpace,
KeyCode::UpArrow,
KeyCode::DownArrow,
KeyCode::LeftArrow,
KeyCode::RightArrow,
];
cursor.hide()?;
'gameloop: loop {
2019-07-14 20:38:03 +02:00
let _ = render_context.update();
nes.step_frame();
let image_buffer = nes.image_buffer();
let slice = unsafe { std::slice::from_raw_parts(image_buffer, 256 * 240 * 4) };
2019-07-05 09:53:09 +02:00
let img =
image::ImageBuffer::<image::Rgba<u8>, &[u8]>::from_raw(256, 240, slice).unwrap();
let resized_img = image::imageops::resize(
&img,
2019-07-14 20:38:03 +02:00
render_context.width as u32,
render_context.height as u32,
2019-07-05 09:53:09 +02:00
image::FilterType::Lanczos3,
);
2019-07-14 20:38:03 +02:00
render_context.clear();
2019-07-05 09:53:09 +02:00
let mut count = 0;
for pixel in resized_img.pixels() {
use image::Pixel;
let rgb = pixel.to_rgb();
2019-07-14 20:38:03 +02:00
render_context.frame_buffer[count] = (rgb[0], rgb[1], rgb[2]);
2019-07-05 09:53:09 +02:00
count += 1;
}
2019-07-14 20:38:03 +02:00
render_context.flush()?;
if rawkey.is_pressed(rawkey::KeyCode::Escape) {
break 'gameloop;
} else {
for i in 0..buttons.len() {
if rawkey.is_pressed(buttons[i]) {
nes.press_button(0, i as u8);
} else {
nes.release_button(0, i as u8);
}
}
}
}
}
if let Some(ref sav_path) = sav_path {
let buffer = nes.save_state();
if let Ok(buffer) = buffer {
let _ = std::fs::write(sav_path, buffer);
}
}
let cursor = cursor();
let _ = cursor.show();
2019-09-10 14:00:25 +02:00
let _screen = RawScreen::disable_raw_mode();
Ok(())
2019-07-04 07:23:05 +02:00
}
fn main() {
serve_plugin(&mut BinaryView::new());
}