use object::{Bytes, LittleEndian as LE, U16};
pub struct VersionInfo {
pub key: Vec<u8>,
pub value: Vec<u8>,
}
pub fn read_version_info(mem: &[u8], offset: usize) -> Option<Vec<VersionInfo>> {
const VS_VERSION_INFO_KEY: &[u8] = b"V\0S\0_\0V\0E\0R\0S\0I\0O\0N\0_\0I\0N\0F\0O\0\0\0";
let mut bytes = Bytes(mem);
bytes.skip(offset).ok()?;
let header = read_header(&mut bytes)?;
let version_info_length = usize::from(header.length);
let end = offset + version_info_length;
let key = bytes.read_bytes(VS_VERSION_INFO_KEY.len()).ok()?;
if key.0 != VS_VERSION_INFO_KEY {
return None;
}
let offset = align32(offset + HEADER_SIZE + VS_VERSION_INFO_KEY.len());
let mut offset = align32(offset + usize::from(header.value_length));
while let Some(length) = read_var_file_info(mem, offset) {
offset = align32(offset + length);
}
let mut infos = Vec::new();
while offset < end {
match read_string_file_info(mem, offset, &mut infos) {
Some(length) => offset = align32(offset + length),
None => break,
}
}
Some(infos)
}
fn read_var_file_info(mem: &[u8], offset: usize) -> Option<usize> {
const VAR_FILE_INFO_KEY: &[u8] = b"V\0a\0r\0F\0i\0l\0e\0I\0n\0f\0o\0\0\0";
let mut bytes = Bytes(mem);
bytes.skip(offset).ok()?;
let header = read_header(&mut bytes)?;
let key = bytes.read_bytes(VAR_FILE_INFO_KEY.len()).ok()?;
if key.0 != VAR_FILE_INFO_KEY {
return None;
}
Some(usize::from(header.length))
}
fn read_string_file_info(mem: &[u8], offset: usize, out: &mut Vec<VersionInfo>) -> Option<usize> {
const STRING_FILE_INFO_KEY: &[u8] = b"S\0t\0r\0i\0n\0g\0F\0i\0l\0e\0I\0n\0f\0o\0\0\0";
let mut bytes = Bytes(mem);
bytes.skip(offset).ok()?;
let header = read_header(&mut bytes)?;
let key = bytes.read_bytes(STRING_FILE_INFO_KEY.len()).ok()?;
if key.0 != STRING_FILE_INFO_KEY {
return None;
}
let length = usize::from(header.length);
let end = offset + length;
let mut offset = align32(offset + HEADER_SIZE + STRING_FILE_INFO_KEY.len());
while offset < end {
match read_string_table(mem, offset, out) {
Some(length) => offset = align32(offset + length),
None => break,
}
}
Some(length)
}
fn read_string_table(mem: &[u8], offset: usize, out: &mut Vec<VersionInfo>) -> Option<usize> {
let mut bytes = Bytes(mem);
bytes.skip(offset).ok()?;
let header = read_header(&mut bytes)?;
let length = usize::from(header.length);
let end = offset + length;
let mut offset = align32(offset + HEADER_SIZE + 2 * 9);
while offset < end {
match read_string(mem, offset, out) {
Some(length) => offset = align32(offset + length),
None => break,
}
}
Some(length)
}
fn read_string(mem: &[u8], offset: usize, out: &mut Vec<VersionInfo>) -> Option<usize> {
let mut bytes = Bytes(mem);
bytes.skip(offset).ok()?;
let header = read_header(&mut bytes)?;
let length = usize::from(header.length);
let end = offset + length;
let value_length = usize::from(header.value_length);
let value_length = value_length * 2;
if value_length + 6 > length {
return None;
}
let value_start = end - value_length;
let key_start = offset + HEADER_SIZE;
let key_end = value_start;
if key_end <= key_start {
return None;
}
let key = unwide(&mem[key_start..key_end]);
let value = unwide(&mem[value_start..end]);
out.push(VersionInfo { key, value });
Some(length)
}
fn unwide(mem: &[u8]) -> Vec<u8> {
let mut res = Vec::new();
let mut i = 0;
while i < mem.len() {
res.push(mem[i]);
i += 2;
}
i = res.len();
while i > 0 && res[i - 1] == 0 {
i -= 1;
}
res.truncate(i);
res
}
const fn align32(offset: usize) -> usize {
(offset + 3) & !3
}
#[derive(Copy, Clone)]
#[repr(C)]
struct Header {
length: u16,
value_length: u16,
typ: u16,
}
const HEADER_SIZE: usize = std::mem::size_of::<Header>();
fn read_header(data: &mut Bytes) -> Option<Header> {
Some(Header {
length: data.read::<U16<LE>>().ok()?.get(LE),
value_length: data.read::<U16<LE>>().ok()?.get(LE),
typ: data.read::<U16<LE>>().ok()?.get(LE),
})
}