bingrep 0.12.1

Cross-platform binary parser and colorizer
use anyhow::Error;
use hexplay::{self, HexViewBuilder, CODEPAGE_ASCII};
use metagoblin::{Object, Tag};
use prettytable::{row, Cell, Row};

use crate::format::*;
use crate::Opt;

const SCALE: [char; 100] = [' '; 100];

fn min_max_scale(n: u64, min: u64, max: u64) -> u64 {
    let range = max as f64 - min as f64;
    let offset = n as f64 - min as f64;
    (SCALE.len() as f64 * offset / range).floor() as u64
}

pub struct Meta<'bytes> {
    analysis: metagoblin::Analysis,
    bytes: &'bytes [u8],
    args: Opt,
}

impl<'bytes> Meta<'bytes> {
    pub fn new(object: &Object<'bytes>, bytes: &'bytes [u8], args: Opt) -> Self {
        let analysis = metagoblin::Analysis::new(object);
        Meta {
            analysis,
            bytes,
            args,
        }
    }
    pub fn print_ranges(&self) -> Result<(), Error> {
        let buffer = &self.bytes;
        let mut franges = self.analysis.franges.iter().collect::<Vec<_>>();
        franges.sort_by(|&(r1, _), &(r2, _)| r2.len().cmp(&r1.len()));
        let spaces = (0_usize..100)
            .map(|e| if e % 2 == 0 { "0" } else { "1" })
            .collect::<String>();
        let mut table =
            new_table(row![b->"Name", b->"Tag", b->"Range", b->"Percent", b->"Size", b->spaces]);

        for (range, data) in franges {
            if let Tag::Zero = data.tag {
                continue;
            }
            let size = range.len().saturating_sub(1);
            if size == 0 {
                continue;
            }
            let buffer_length = u64::try_from(buffer.len())?;
            let scaled_min = usize::try_from(min_max_scale(range.min, 0, buffer_length))?;
            let scaled_max = usize::try_from(min_max_scale(range.max, 0, buffer_length))?;
            let scaled_size = min_max_scale(size, 0, buffer_length);
            let mut chars = SCALE;
            if let Some(range) = chars.get_mut(scaled_min..scaled_max) {
                range.fill('-');
            }
            if let Some(c) = chars.get_mut(scaled_min) {
                *c = '|';
            }
            if scaled_max != 0 {
                if let Some(c) = chars.get_mut(scaled_max - 1) {
                    *c = '|';
                }
            }
            let chars = chars.into_iter().collect::<String>();
            let name_cell = string_cell(&self.args, data.name().unwrap_or("None"));
            let range_cell = Cell::new(&format!("{:#x}..{:#x}", range.min, range.max));
            let size_cell = Cell::new(&format!("{}", size));
            table.add_row(Row::new(vec![
                name_cell,
                Cell::new(&format!("{:?}", data.tag)),
                range_cell,
                Cell::new(&format!("{}%", scaled_size)),
                size_cell,
                Cell::new(&chars),
            ]));
        }

        print_table_to_stdout(&table, true).map_err(Into::into)
    }

    pub fn print_hex(&self) -> Result<(), Error> {
        let analysis = &self.analysis;
        let table = HexViewBuilder::new(self.bytes)
            .row_width(16)
            .codepage(CODEPAGE_ASCII);

        let mut colors = Vec::new();
        let franges = analysis.franges.iter().collect::<Vec<_>>();
        for window in franges.windows(2) {
            if let (Some((range, metadata)), Some((_next, _))) = (window.get(0), window.get(1)) {
                let color = match metadata.tag {
                    Tag::Code => hexplay::color::red(),
                    Tag::ASCII => hexplay::color::yellow_bold(),
                    Tag::SymbolTable => hexplay::color::blue_bold(),
                    Tag::Relocation => hexplay::color::green(),
                    Tag::Zero => hexplay::color::white_bold(),
                    Tag::Meta => hexplay::color::magenta(),
                    Tag::Unknown => hexplay::color::black(),
                    _ => hexplay::color::white(),
                };
                let color_range = usize::try_from(range.min)?..usize::try_from(range.max)?;
                colors.push((color, color_range));
            }
        }

        let table = table.add_colors(colors);

        let view = if self.args.color {
            table.force_color().finish()
        } else {
            table.finish()
        };

        view.print()?;
        println!();
        Ok(())
    }
}