use crate::{error::Error, util::read::Reader, version::Version};
pub(crate) fn read_utf16_string(
reader: &mut Reader<'_>,
what: &'static str,
) -> Result<String, Error> {
let len_bytes = reader.u32_le(what)? as usize;
if len_bytes == 0 {
return Ok(String::new());
}
if len_bytes.checked_rem(2).ok_or(Error::Overflow { what })? != 0 {
return Err(Error::InvalidUtf16 { what });
}
let bytes = reader.take(len_bytes, what)?;
let pairs = len_bytes.checked_div(2).ok_or(Error::Overflow { what })?;
let mut units = Vec::with_capacity(pairs);
for chunk in bytes.chunks_exact(2) {
let mut arr = [0u8; 2];
arr.copy_from_slice(chunk);
units.push(u16::from_le_bytes(arr));
}
String::from_utf16(&units).map_err(|_| Error::InvalidUtf16 { what })
}
pub(crate) fn read_ansi_bytes(
reader: &mut Reader<'_>,
what: &'static str,
) -> Result<Vec<u8>, Error> {
let len = reader.u32_le(what)? as usize;
let bytes = reader.take(len, what)?;
Ok(bytes.to_vec())
}
pub(crate) fn read_setup_string(
reader: &mut Reader<'_>,
version: &Version,
what: &'static str,
) -> Result<String, Error> {
if is_unicode_for_version(version) {
read_utf16_string(reader, what)
} else {
let raw = read_ansi_bytes(reader, what)?;
Ok(decode_windows_1252(&raw))
}
}
pub(crate) fn is_unicode_for_version(version: &Version) -> bool {
if version.is_unicode() {
return true;
}
version.at_least(5, 6, 0)
}
pub(crate) fn decode_windows_1252(bytes: &[u8]) -> String {
let mut out = String::with_capacity(bytes.len());
for &b in bytes {
out.push(byte_to_char_1252(b));
}
out
}
fn byte_to_char_1252(b: u8) -> char {
match b {
0x80 => '\u{20AC}',
0x82 => '\u{201A}',
0x83 => '\u{0192}',
0x84 => '\u{201E}',
0x85 => '\u{2026}',
0x86 => '\u{2020}',
0x87 => '\u{2021}',
0x88 => '\u{02C6}',
0x89 => '\u{2030}',
0x8A => '\u{0160}',
0x8B => '\u{2039}',
0x8C => '\u{0152}',
0x8E => '\u{017D}',
0x91 => '\u{2018}',
0x92 => '\u{2019}',
0x93 => '\u{201C}',
0x94 => '\u{201D}',
0x95 => '\u{2022}',
0x96 => '\u{2013}',
0x97 => '\u{2014}',
0x98 => '\u{02DC}',
0x99 => '\u{2122}',
0x9A => '\u{0161}',
0x9B => '\u{203A}',
0x9C => '\u{0153}',
0x9E => '\u{017E}',
0x9F => '\u{0178}',
0x81 | 0x8D | 0x8F | 0x90 | 0x9D => '\u{FFFD}',
_ => char::from(b),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn windows_1252_ascii_passes_through() {
assert_eq!(decode_windows_1252(b"Hello, World!"), "Hello, World!");
}
#[test]
fn windows_1252_extended_chars() {
assert_eq!(decode_windows_1252(&[0x80, 0xA9]), "۩");
}
#[test]
fn read_utf16_string_decodes_inno_app_name() {
let mut bytes = Vec::new();
bytes.extend_from_slice(&16u32.to_le_bytes()); for c in "HeidiSQL".chars() {
let u = c as u16;
bytes.extend_from_slice(&u.to_le_bytes());
}
let mut reader = Reader::new(&bytes);
let s = read_utf16_string(&mut reader, "AppName").unwrap();
assert_eq!(s, "HeidiSQL");
}
#[test]
fn read_utf16_empty_string() {
let bytes = 0u32.to_le_bytes();
let mut reader = Reader::new(&bytes);
let s = read_utf16_string(&mut reader, "AppName").unwrap();
assert_eq!(s, "");
}
#[test]
fn read_utf16_rejects_odd_byte_count() {
let mut bytes = Vec::new();
bytes.extend_from_slice(&3u32.to_le_bytes());
bytes.extend_from_slice(&[b'H', 0, 0]);
let mut reader = Reader::new(&bytes);
let err = read_utf16_string(&mut reader, "AppName").unwrap_err();
assert!(matches!(err, Error::InvalidUtf16 { .. }));
}
}