1use crossterm::{style::Attribute, ExecutableCommand};
2use nu_pretty_hex::*;
3use nu_protocol::outln;
4use nu_source::AnchorLocation;
5
6#[derive(Default)]
7pub struct BinaryView;
8
9impl BinaryView {
10 pub fn new() -> BinaryView {
11 BinaryView
12 }
13}
14
15pub fn view_binary(
16 b: &[u8],
17 source: Option<&AnchorLocation>,
18 lores_mode: bool,
19) -> Result<(), Box<dyn std::error::Error>> {
20 if b.len() > 3 {
21 if let (0x4e, 0x45, 0x53) = (b[0], b[1], b[2]) {
22 view_contents_interactive(b, source, lores_mode)?;
23 return Ok(());
24 }
25 }
26
27 view_contents(b, source, lores_mode)?;
28 Ok(())
29}
30
31pub struct RenderContext {
32 pub width: usize,
33 pub height: usize,
34 pub frame_buffer: Vec<(u8, u8, u8)>,
35 pub since_last_button: Vec<usize>,
36 pub lores_mode: bool,
37}
38
39impl RenderContext {
40 pub fn blank(lores_mode: bool) -> RenderContext {
41 RenderContext {
42 width: 0,
43 height: 0,
44 frame_buffer: vec![],
45 since_last_button: vec![0; 8],
46 lores_mode,
47 }
48 }
49 pub fn clear(&mut self) {
50 self.frame_buffer = vec![(0, 0, 0); self.width * self.height as usize];
51 }
52
53 fn render_to_screen_lores(&mut self) {
54 let mut prev_color: Option<(u8, u8, u8)> = None;
55 let mut prev_count = 1;
56
57 let _ = std::io::stdout().execute(crossterm::cursor::MoveTo(0, 0));
58
59 for pixel in &self.frame_buffer {
60 match prev_color {
61 Some(c) if c == *pixel => {
62 prev_count += 1;
63 }
64 Some(c) => {
65 print!(
66 "{}",
67 nu_ansi_term::Color::Rgb(c.0, c.1, c.2)
68 .paint((0..prev_count).map(|_| "█").collect::<String>())
69 );
70 prev_color = Some(*pixel);
71 prev_count = 1;
72 }
73 _ => {
74 prev_color = Some(*pixel);
75 prev_count = 1;
76 }
77 }
78 }
79
80 if prev_count > 0 {
81 if let Some(color) = prev_color {
82 print!(
83 "{}",
84 nu_ansi_term::Color::Rgb(color.0, color.1, color.2)
85 .paint((0..prev_count).map(|_| "█").collect::<String>())
86 );
87 }
88 }
89 outln!("{}", Attribute::Reset);
90 }
91 fn render_to_screen_hires(&mut self) {
92 let mut prev_fg: Option<(u8, u8, u8)> = None;
93 let mut prev_bg: Option<(u8, u8, u8)> = None;
94 let mut prev_count = 1;
95
96 let mut pos = 0;
97 let fb_len = self.frame_buffer.len();
98
99 let _ = std::io::stdout().execute(crossterm::cursor::MoveTo(0, 0));
100
101 while pos < (fb_len - self.width) {
102 let top_pixel = self.frame_buffer[pos];
103 let bottom_pixel = self.frame_buffer[pos + self.width];
104
105 match (prev_fg, prev_bg) {
106 (Some(c), Some(d)) if c == top_pixel && d == bottom_pixel => {
107 prev_count += 1;
108 }
109 (Some(c), Some(d)) => {
110 print!(
111 "{}",
112 nu_ansi_term::Color::Rgb(c.0, c.1, c.2)
113 .on(nu_ansi_term::Color::Rgb(d.0, d.1, d.2,))
114 .paint((0..prev_count).map(|_| "▀").collect::<String>())
115 );
116 prev_fg = Some(top_pixel);
117 prev_bg = Some(bottom_pixel);
118 prev_count = 1;
119 }
120 _ => {
121 prev_fg = Some(top_pixel);
122 prev_bg = Some(bottom_pixel);
123 prev_count = 1;
124 }
125 }
126 pos += 1;
127 if pos % self.width == 0 {
128 pos += self.width;
129 }
130 }
131 if prev_count > 0 {
132 if let (Some(c), Some(d)) = (prev_fg, prev_bg) {
133 print!(
134 "{}",
135 nu_ansi_term::Color::Rgb(c.0, c.1, c.2)
136 .on(nu_ansi_term::Color::Rgb(d.0, d.1, d.2,))
137 .paint((0..prev_count).map(|_| "▀").collect::<String>())
138 );
139 }
140 }
141 outln!("{}", Attribute::Reset);
142 }
143 pub fn flush(&mut self) {
144 if self.lores_mode {
145 self.render_to_screen_lores()
146 } else {
147 self.render_to_screen_hires()
148 }
149 }
150 pub fn update(&mut self) -> Result<(), Box<dyn std::error::Error>> {
151 let terminal_size = crossterm::terminal::size().unwrap_or((80, 24));
152
153 if (self.width != terminal_size.0 as usize) || (self.height != terminal_size.1 as usize) {
154 let _ = std::io::stdout().execute(crossterm::cursor::Hide);
155
156 self.width = terminal_size.0 as usize;
157 self.height = if self.lores_mode {
158 terminal_size.1 as usize - 1
159 } else {
160 (terminal_size.1 as usize - 1) * 2
161 };
162 }
163
164 Ok(())
165 }
166}
167
168#[derive(Debug)]
169struct RawImageBuffer {
170 dimensions: (u32, u32),
171 colortype: image::ColorType,
172 buffer: Vec<u8>,
173}
174
175fn load_from_png_buffer(buffer: &[u8]) -> Result<RawImageBuffer, Box<dyn std::error::Error>> {
176 use image::ImageDecoder;
177
178 let decoder = image::codecs::png::PngDecoder::new(buffer)?;
179
180 let dimensions = decoder.dimensions();
181 let colortype = decoder.color_type();
182 let mut buffer: Vec<u8> = vec![0; decoder.total_bytes() as usize];
183 decoder.read_image(&mut buffer)?;
184
185 Ok(RawImageBuffer {
186 dimensions,
187 colortype,
188 buffer,
189 })
190}
191
192fn load_from_jpg_buffer(buffer: &[u8]) -> Result<RawImageBuffer, Box<dyn std::error::Error>> {
193 use image::ImageDecoder;
194
195 let decoder = image::codecs::jpeg::JpegDecoder::new(buffer)?;
196
197 let dimensions = decoder.dimensions();
198 let colortype = decoder.color_type();
199 let mut buffer: Vec<u8> = vec![0; decoder.total_bytes() as usize];
200 decoder.read_image(&mut buffer)?;
201
202 Ok(RawImageBuffer {
203 dimensions,
204 colortype,
205 buffer,
206 })
207}
208
209pub fn view_contents(
210 buffer: &[u8],
211 _source: Option<&AnchorLocation>,
212 lores_mode: bool,
213) -> Result<(), Box<dyn std::error::Error>> {
214 #[cfg(windows)]
217 {
218 let _ = nu_ansi_term::enable_ansi_support();
219 }
220
221 let hex_config = HexConfig {
223 title: true,
224 ascii: true,
225 width: 16,
226 group: 4,
227 chunk: 1,
228 skip: None,
229 length: None,
230 };
231
232 let mut raw_image_buffer = load_from_png_buffer(buffer);
233
234 if raw_image_buffer.is_err() {
235 raw_image_buffer = load_from_jpg_buffer(buffer);
236 }
237
238 if raw_image_buffer.is_err() {
239 outln!("{}", config_hex(&buffer, hex_config));
241 return Ok(());
242 }
243 let raw_image_buffer = raw_image_buffer?;
244
245 let mut render_context: RenderContext = RenderContext::blank(lores_mode);
246 let _ = render_context.update();
247 render_context.clear();
248
249 match raw_image_buffer.colortype {
250 image::ColorType::Rgba8 => {
251 let img = image::ImageBuffer::<image::Rgba<u8>, Vec<u8>>::from_vec(
252 raw_image_buffer.dimensions.0 as u32,
253 raw_image_buffer.dimensions.1 as u32,
254 raw_image_buffer.buffer,
255 )
256 .ok_or("Cannot convert image data")?;
257
258 let resized_img = image::imageops::resize(
259 &img,
260 render_context.width as u32,
261 render_context.height as u32,
262 image::imageops::FilterType::Lanczos3,
263 );
264
265 for (count, pixel) in resized_img.pixels().enumerate() {
266 use image::Pixel;
267 let rgb = pixel.to_rgb();
268 render_context.frame_buffer[count] = (rgb[0], rgb[1], rgb[2]);
269 }
270 }
271 image::ColorType::Rgb8 => {
272 let img = image::ImageBuffer::<image::Rgb<u8>, Vec<u8>>::from_vec(
273 raw_image_buffer.dimensions.0 as u32,
274 raw_image_buffer.dimensions.1 as u32,
275 raw_image_buffer.buffer,
276 )
277 .ok_or("Cannot convert image data")?;
278
279 let resized_img = image::imageops::resize(
280 &img,
281 render_context.width as u32,
282 render_context.height as u32,
283 image::imageops::FilterType::Lanczos3,
284 );
285
286 for (count, pixel) in resized_img.pixels().enumerate() {
287 use image::Pixel;
288 let rgb = pixel.to_rgb();
289 render_context.frame_buffer[count] = (rgb[0], rgb[1], rgb[2]);
290 }
291 }
292 _ => {
293 outln!("{}", config_hex(&buffer, hex_config));
295 return Ok(());
296 }
297 }
298
299 render_context.flush();
300
301 let _ = std::io::stdout().execute(crossterm::cursor::Show);
302
303 let _ = crossterm::terminal::disable_raw_mode();
304
305 Ok(())
306}
307
308pub fn view_contents_interactive(
309 buffer: &[u8],
310 source: Option<&AnchorLocation>,
311 lores_mode: bool,
312) -> Result<(), Box<dyn std::error::Error>> {
313 use rawkey::{KeyCode, RawKey};
314
315 let sav_path = if let Some(AnchorLocation::File(f)) = source {
316 let mut path = std::path::PathBuf::from(f);
317 path.set_extension("sav");
318 Some(path)
319 } else {
320 None
321 };
322
323 let mut nes = neso::Nes::new(0.0);
324 let rawkey = RawKey::new();
325 nes.load_rom(buffer);
326
327 if let Some(ref sav_path) = sav_path {
328 if let Ok(contents) = std::fs::read(sav_path) {
329 let _ = nes.load_state(&contents);
330 }
331 }
332
333 nes.reset();
334
335 if let Ok(_raw) = crossterm::terminal::enable_raw_mode() {
336 let mut render_context: RenderContext = RenderContext::blank(lores_mode);
337
338 let buttons = vec![
339 KeyCode::Alt,
340 KeyCode::LeftControl,
341 KeyCode::Tab,
342 KeyCode::BackSpace,
343 KeyCode::UpArrow,
344 KeyCode::DownArrow,
345 KeyCode::LeftArrow,
346 KeyCode::RightArrow,
347 ];
348
349 let _ = std::io::stdout().execute(crossterm::cursor::Hide);
350
351 'gameloop: loop {
352 let _ = render_context.update();
353 nes.step_frame();
354
355 let image_buffer = nes.image_buffer();
356
357 let slice = unsafe { std::slice::from_raw_parts(image_buffer, 256 * 240 * 4) };
358 let img = image::ImageBuffer::<image::Rgba<u8>, &[u8]>::from_raw(256, 240, slice)
359 .ok_or("Cannot convert image data")?;
360 let resized_img = image::imageops::resize(
361 &img,
362 render_context.width as u32,
363 render_context.height as u32,
364 image::imageops::FilterType::Lanczos3,
365 );
366
367 render_context.clear();
368
369 for (count, pixel) in resized_img.pixels().enumerate() {
370 use image::Pixel;
371 let rgb = pixel.to_rgb();
372
373 render_context.frame_buffer[count] = (rgb[0], rgb[1], rgb[2]);
374 }
375 render_context.flush();
376
377 if rawkey.is_pressed(rawkey::KeyCode::Escape) {
378 break 'gameloop;
379 } else {
380 for (idx, button) in buttons.iter().enumerate() {
381 if rawkey.is_pressed(*button) {
382 nes.press_button(0, idx as u8);
383 } else {
384 nes.release_button(0, idx as u8);
385 }
386 }
387 loop {
388 let x = crossterm::event::poll(std::time::Duration::from_secs(0));
389 match x {
390 Ok(true) => {
391 let _ = crossterm::event::read();
393 }
394 _ => {
395 break;
396 }
397 }
398 }
399 }
400 }
401 }
402
403 if let Some(ref sav_path) = sav_path {
404 let buffer = nes.save_state();
405 if let Ok(buffer) = buffer {
406 let _ = std::fs::write(sav_path, buffer);
407 }
408 }
409
410 let _ = std::io::stdout().execute(crossterm::cursor::Show);
411
412 let _screen = crossterm::terminal::disable_raw_mode();
413
414 Ok(())
415}