use std::borrow::Cow;
use scroll::Pread;
use crate::common::*;
use crate::msf::Stream;
const PDB_NMT_HDR: u32 = 0xEFFE_EFFE;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum StringTableHashVersion {
LongHash = 1,
LongHashV2 = 2,
}
impl StringTableHashVersion {
fn parse_u32(value: u32) -> Result<Self> {
match value {
1 => Ok(StringTableHashVersion::LongHash),
2 => Ok(StringTableHashVersion::LongHashV2),
_ => Err(Error::UnimplementedFeature(
"unknown string table hash version",
)),
}
}
}
#[repr(C, packed)]
#[derive(Clone, Copy, Debug, Pread)]
struct StringTableHeader {
magic: u32,
hash_version: u32,
names_size: u32,
}
impl StringTableHeader {
fn names_start(self) -> usize {
std::mem::size_of::<Self>()
}
fn names_end(self) -> usize {
self.names_start() + self.names_size as usize
}
}
#[derive(Debug)]
pub struct StringTable<'s> {
header: StringTableHeader,
hash_version: StringTableHashVersion,
stream: Stream<'s>,
}
impl<'s> StringTable<'s> {
pub(crate) fn parse(stream: Stream<'s>) -> Result<Self> {
let mut buf = stream.parse_buffer();
let header = buf.parse::<StringTableHeader>()?;
if header.magic != PDB_NMT_HDR {
return Err(Error::UnimplementedFeature(
"invalid string table signature",
));
}
if buf.len() < header.names_end() {
return Err(Error::UnexpectedEof);
}
let hash_version = StringTableHashVersion::parse_u32(header.hash_version)?;
Ok(StringTable {
header,
hash_version,
stream,
})
}
}
impl<'s> StringTable<'s> {
pub fn get(&self, offset: StringRef) -> Result<RawString<'_>> {
if offset.0 >= self.header.names_size {
return Err(Error::UnexpectedEof);
}
let string_offset = self.header.names_start() + offset.0 as usize;
let data = &self.stream.as_slice()[string_offset..self.header.names_end()];
ParseBuffer::from(data).parse_cstring()
}
}
impl StringRef {
pub fn to_raw_string<'s>(self, strings: &'s StringTable<'_>) -> Result<RawString<'s>> {
strings.get(self)
}
pub fn to_string_lossy<'s>(self, strings: &'s StringTable<'_>) -> Result<Cow<'s, str>> {
strings.get(self).map(|r| r.to_string())
}
}