use std::mem::size_of;
use object::{Bytes, LittleEndian as LE, U16};
use super::MAX_NB_VERSION_INFOS;
pub struct VersionInfo {
pub key: Vec<u8>,
pub value: Vec<u8>,
}
pub fn read_version_info(mem: &[u8], mut offset: usize, infos: &mut 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);
if bytes.skip(offset).is_err() {
return;
}
let Some(header) = read_header(&mut bytes) else {
return;
};
let version_info_length = usize::from(header.length);
let end = offset + version_info_length;
let Ok(key) = bytes.read_bytes(VS_VERSION_INFO_KEY.len()) else {
return;
};
if key.0 != VS_VERSION_INFO_KEY {
return;
}
offset += align32(HEADER_SIZE + 86);
while let Some(length) = read_var_file_info(mem, offset) {
offset += align32(length);
}
while offset < end {
if infos.len() >= MAX_NB_VERSION_INFOS {
break;
}
match read_string_file_info(mem, offset, infos) {
Some(length) => offset += length,
None => break,
}
}
}
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],
mut 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 = align32(usize::from(header.length));
let end = offset + length;
offset += align32(HEADER_SIZE + STRING_FILE_INFO_KEY.len());
while offset < end {
if out.len() >= MAX_NB_VERSION_INFOS {
break;
}
match read_string_table(mem, offset, out) {
Some(length) => offset += length,
None => break,
}
}
Some(length)
}
fn read_string_table(mem: &[u8], mut 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 = align32(usize::from(header.length));
let end = offset + length;
let key_len = find_wide_nul(bytes.0);
offset += align32(HEADER_SIZE + key_len + 2);
while offset < end {
if out.len() >= MAX_NB_VERSION_INFOS {
break;
}
match read_string(mem, offset, out) {
Some(length) => 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 = align32(usize::from(header.length));
let end = offset + length;
let mem = mem.get(offset..end)?;
let key_start = HEADER_SIZE;
if key_start > mem.len() {
return None;
}
let key_end = key_start + find_wide_nul(mem.get(key_start..)?);
let value_start = align32(key_end + 2);
if value_start > end {
return None;
}
let value_end = value_start + find_wide_nul(mem.get(value_start..)?);
let key = unwide(&mem[key_start..key_end], 63);
let value = unwide(&mem[value_start..value_end], 255);
out.push(VersionInfo { key, value });
Some(length)
}
fn find_wide_nul(mem: &[u8]) -> usize {
let mut i = 0;
while i + 1 < mem.len() {
if mem[i] == b'\0' && mem[i + 1] == b'\0' {
return i;
}
i += 2;
}
mem.len()
}
fn unwide(mem: &[u8], max_size: usize) -> Vec<u8> {
let mut res = Vec::new();
let mut i = 0;
while i < mem.len() && res.len() < max_size {
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
}
#[repr(C)]
struct Header {
length: u16,
value_length: u16,
typ: u16,
}
const HEADER_SIZE: usize = 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),
})
}