use byteorder::{BigEndian, LittleEndian, ReadBytesExt};
use colored::Colorize;
use std::{fmt::Write, io::Cursor, str::FromStr};
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum DisplayMode {
Hex,
Ascii,
Int16,
Int32,
Int64,
}
impl FromStr for DisplayMode {
type Err = &'static str;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"hex" => Ok(DisplayMode::Hex),
"ascii" => Ok(DisplayMode::Ascii),
"int16" => Ok(DisplayMode::Int16),
"int32" => Ok(DisplayMode::Int32),
"int64" => Ok(DisplayMode::Int64),
_ => Err("Unknown display mode"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Endianness {
Little,
Big,
}
impl FromStr for Endianness {
type Err = &'static str;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"little" => Ok(Endianness::Little),
"big" => Ok(Endianness::Big),
_ => Err("Unknown endianness"),
}
}
}
pub fn format_memory_display(
data: &[u8],
address: u64,
mode: DisplayMode,
endianness: Endianness,
) -> String {
match mode {
DisplayMode::Hex => format_hex(data, address),
DisplayMode::Ascii => format_ascii(data, address),
DisplayMode::Int16 => format_int::<u16>(data, address, endianness),
DisplayMode::Int32 => format_int::<u32>(data, address, endianness),
DisplayMode::Int64 => format_int::<u64>(data, address, endianness),
}
}
fn format_hex(data: &[u8], address: u64) -> String {
let mut result = String::new();
let bytes_per_line = 16;
for (i, chunk) in data.chunks(bytes_per_line).enumerate() {
let line_addr = address + (i * bytes_per_line) as u64;
write!(result, "{:016x}: ", line_addr).unwrap();
for (j, byte) in chunk.iter().enumerate() {
write!(result, "{:02x} ", byte).unwrap();
if j == 7 {
result.push(' ');
}
}
if chunk.len() < bytes_per_line {
let padding = (bytes_per_line - chunk.len()) * 3 + if chunk.len() <= 8 { 1 } else { 0 };
for _ in 0..padding {
result.push(' ');
}
}
result.push_str(" |");
for &byte in chunk {
if byte >= 32 && byte <= 126 {
result.push(byte as char);
} else {
result.push('.');
}
}
for _ in chunk.len()..bytes_per_line {
result.push(' ');
}
result.push_str("|\n");
}
result
}
fn format_ascii(data: &[u8], address: u64) -> String {
let mut result = String::new();
write!(result, "ASCII string at address {:016x}:\n\"", address).unwrap();
for &byte in data {
if byte == 0 {
break;
} else if byte >= 32 && byte <= 126 {
result.push(byte as char);
} else {
write!(result, "\\x{:02x}", byte).unwrap();
}
}
result.push_str("\"\n");
result
}
fn format_int<T: std::fmt::Display>(data: &[u8], address: u64, endianness: Endianness) -> String {
let type_size = std::mem::size_of::<T>();
let mut result = String::new();
let type_name = match type_size {
2 => "int16",
4 => "int32",
8 => "int64",
_ => "unknown",
};
write!(
result,
"{} values at address {:016x}:\n",
type_name, address
)
.unwrap();
for (i, chunk) in data.chunks(type_size).enumerate() {
if chunk.len() == type_size {
let value = read_int::<T>(chunk, endianness);
write!(
result,
"[{:04x}] {:016x} = {}\n",
i * type_size,
address + (i * type_size) as u64,
value
)
.unwrap();
}
}
result
}
fn read_int<T>(data: &[u8], endianness: Endianness) -> u64 {
let mut cursor = Cursor::new(data);
match std::mem::size_of::<T>() {
2 => match endianness {
Endianness::Little => cursor.read_u16::<LittleEndian>().unwrap_or(0) as u64,
Endianness::Big => cursor.read_u16::<BigEndian>().unwrap_or(0) as u64,
},
4 => match endianness {
Endianness::Little => cursor.read_u32::<LittleEndian>().unwrap_or(0) as u64,
Endianness::Big => cursor.read_u32::<BigEndian>().unwrap_or(0) as u64,
},
8 => match endianness {
Endianness::Little => cursor.read_u64::<LittleEndian>().unwrap_or(0),
Endianness::Big => cursor.read_u64::<BigEndian>().unwrap_or(0),
},
_ => 0,
}
}
pub fn colorize_output(text: &str, use_colors: bool) -> String {
if !use_colors {
return text.to_string();
}
let mut result = String::new();
for line in text.lines() {
if line.contains("Error") || line.contains("error") {
result.push_str(&line.red().to_string());
} else if line.contains("Warning") || line.contains("warning") {
result.push_str(&line.yellow().to_string());
} else if line.starts_with("0x") || line.contains(":") && line.contains("|") {
result.push_str(&colorize_memory_line(line));
} else {
result.push_str(line);
}
result.push('\n');
}
result
}
fn colorize_memory_line(line: &str) -> String {
let parts: Vec<&str> = line.splitn(2, ":").collect();
if parts.len() == 2 {
return format!("{}: {}", parts[0].cyan().to_string(), parts[1].to_string());
}
line.to_string()
}