diff --git a/Cargo.lock b/Cargo.lock index 4bcd2e6..5c45a4f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3337,6 +3337,7 @@ dependencies = [ "cpp_demangle", "cwdemangle", "cwextab", + "encoding_rs", "filetime", "flagset", "gimli", diff --git a/objdiff-core/Cargo.toml b/objdiff-core/Cargo.toml index e78816b..919eefa 100644 --- a/objdiff-core/Cargo.toml +++ b/objdiff-core/Cargo.toml @@ -167,6 +167,7 @@ notify-debouncer-full = { version = "0.5.0", optional = true } shell-escape = { version = "0.1", optional = true } tempfile = { version = "3.19", optional = true } time = { version = "0.3", optional = true } +encoding_rs = "0.8.35" [target.'cfg(windows)'.dependencies] winapi = { version = "0.3", optional = true } diff --git a/objdiff-core/src/arch/mod.rs b/objdiff-core/src/arch/mod.rs index c033ef3..f7adb18 100644 --- a/objdiff-core/src/arch/mod.rs +++ b/objdiff-core/src/arch/mod.rs @@ -2,6 +2,7 @@ use alloc::{borrow::Cow, boxed::Box, format, string::String, vec::Vec}; use core::{ffi::CStr, fmt, fmt::Debug}; use anyhow::{Result, bail}; +use encoding_rs::SHIFT_JIS; use object::Endian as _; use crate::{ @@ -57,13 +58,18 @@ impl fmt::Display for DataType { impl DataType { pub fn display_labels(&self, endian: object::Endianness, bytes: &[u8]) -> Vec { let mut strs = Vec::new(); - for literal in self.display_literals(endian, bytes) { - strs.push(format!("{}: {}", self, literal)) + for (literal, label_override) in self.display_literals(endian, bytes) { + let label = label_override.unwrap_or_else(|| format!("{}", self)); + strs.push(format!("{}: {}", label, literal)) } strs } - pub fn display_literals(&self, endian: object::Endianness, bytes: &[u8]) -> Vec { + pub fn display_literals( + &self, + endian: object::Endianness, + bytes: &[u8], + ) -> Vec<(String, Option)> { let mut strs = Vec::new(); if self.required_len().is_some_and(|l| bytes.len() < l) { log::warn!( @@ -87,56 +93,72 @@ impl DataType { match self { DataType::Int8 => { let i = i8::from_ne_bytes(bytes.try_into().unwrap()); - strs.push(format!("{:#x}", i)); + strs.push((format!("{:#x}", i), None)); if i < 0 { - strs.push(format!("{:#x}", ReallySigned(i))); + strs.push((format!("{:#x}", ReallySigned(i)), None)); } } DataType::Int16 => { let i = endian.read_i16_bytes(bytes.try_into().unwrap()); - strs.push(format!("{:#x}", i)); + strs.push((format!("{:#x}", i), None)); if i < 0 { - strs.push(format!("{:#x}", ReallySigned(i))); + strs.push((format!("{:#x}", ReallySigned(i)), None)); } } DataType::Int32 => { let i = endian.read_i32_bytes(bytes.try_into().unwrap()); - strs.push(format!("{:#x}", i)); + strs.push((format!("{:#x}", i), None)); if i < 0 { - strs.push(format!("{:#x}", ReallySigned(i))); + strs.push((format!("{:#x}", ReallySigned(i)), None)); } } DataType::Int64 => { let i = endian.read_i64_bytes(bytes.try_into().unwrap()); - strs.push(format!("{:#x}", i)); + strs.push((format!("{:#x}", i), None)); if i < 0 { - strs.push(format!("{:#x}", ReallySigned(i))); + strs.push((format!("{:#x}", ReallySigned(i)), None)); } } DataType::Float => { let bytes: [u8; 4] = bytes.try_into().unwrap(); - strs.push(format!("{:?}f", match endian { - object::Endianness::Little => f32::from_le_bytes(bytes), - object::Endianness::Big => f32::from_be_bytes(bytes), - })); + strs.push(( + format!("{:?}f", match endian { + object::Endianness::Little => f32::from_le_bytes(bytes), + object::Endianness::Big => f32::from_be_bytes(bytes), + }), + None, + )); } DataType::Double => { let bytes: [u8; 8] = bytes.try_into().unwrap(); - strs.push(format!("{:?}", match endian { - object::Endianness::Little => f64::from_le_bytes(bytes), - object::Endianness::Big => f64::from_be_bytes(bytes), - })); + strs.push(( + format!("{:?}", match endian { + object::Endianness::Little => f64::from_le_bytes(bytes), + object::Endianness::Big => f64::from_be_bytes(bytes), + }), + None, + )); } DataType::Bytes => { - strs.push(format!("{:#?}", bytes)); + strs.push((format!("{:#?}", bytes), None)); } DataType::String => { if let Ok(cstr) = CStr::from_bytes_until_nul(bytes) { - strs.push(format!("{:?}", cstr)); + strs.push((format!("{:?}", cstr), None)); + } + if let Some(nul_idx) = bytes.iter().position(|&c| c == b'\0') { + let (cow, _, had_errors) = SHIFT_JIS.decode(&bytes[..nul_idx]); + if !had_errors { + let str = format!("{:?}", cow); + // Only add the Shift JIS string if it's different from the ASCII string. + if !strs.iter().any(|x| x.0 == str) { + strs.push((str, Some("Shift JIS".into()))); + } + } } } } diff --git a/objdiff-core/src/diff/display.rs b/objdiff-core/src/diff/display.rs index 790b458..d5a5305 100644 --- a/objdiff-core/src/diff/display.rs +++ b/objdiff-core/src/diff/display.rs @@ -444,8 +444,8 @@ pub fn relocation_context( let literals = display_ins_data_literals(obj, ins); if !literals.is_empty() { out.push(ContextItem::Separator); - for literal in literals { - out.push(ContextItem::Copy { value: literal, label: None }); + for (literal, label_override) in literals { + out.push(ContextItem::Copy { value: literal, label: label_override }); } } } @@ -569,9 +569,9 @@ pub fn instruction_hover( let literals = display_ins_data_literals(obj, resolved); if !literals.is_empty() { out.push(HoverItem::Separator); - for literal in literals { + for (literal, label_override) in literals { out.push(HoverItem::Text { - label: format!("{}", ty), + label: label_override.unwrap_or_else(|| format!("{}", ty)), value: literal, color: HoverItemColor::Normal, }); @@ -764,7 +764,10 @@ pub fn display_ins_data_labels(obj: &Object, resolved: ResolvedInstructionRef) - .unwrap_or_default() } -pub fn display_ins_data_literals(obj: &Object, resolved: ResolvedInstructionRef) -> Vec { +pub fn display_ins_data_literals( + obj: &Object, + resolved: ResolvedInstructionRef, +) -> Vec<(String, Option)> { let Some(reloc) = resolved.relocation else { return Vec::new(); };