1
0
Fork 0
mirror of https://codeberg.org/demostf/parser.git synced 2026-06-03 18:24:05 +02:00

string table fixes

This commit is contained in:
Robin Appelman 2019-03-02 18:48:30 +01:00
commit d7f14a70aa
6 changed files with 103 additions and 30 deletions

View file

@ -1,10 +1,12 @@
use std::collections::HashMap;
use arraydeque::{ArrayDeque, Wrapping};
use bitstream_reader::{BitRead, BitReadSized, BitStream, LittleEndian};
use bitstream_reader::{BitRead, BitReadSized, BitStream, LittleEndian, BitBuffer};
use snap::Decoder;
use crate::{Parse, ParseError, ParserState, ReadResult, Stream, Result};
use crate::{Parse, ParseError, ParserState, ReadResult, Result, Stream};
use crate::demo::packet::stringtable::{ExtraData, FixedUserdataSize, StringTable, StringTableEntry};
use num_traits::{PrimInt, Unsigned};
#[derive(Debug)]
pub struct CreateStringTableMessage {
@ -26,11 +28,11 @@ impl From<&StringTable> for StringTableMeta {
}
}
impl BitRead<LittleEndian> for CreateStringTableMessage {
fn read(stream: &mut Stream) -> ReadResult<Self> {
impl Parse for CreateStringTableMessage {
fn parse(stream: &mut Stream, state: &ParserState) -> Result<Self> {
let name = stream.read()?;
let max_entries: u16 = stream.read()?;
let encode_bits = 16 - max_entries.leading_zeros();
let encode_bits = log_base2(max_entries);
let entity_count: u16 = stream.read_sized(encode_bits as usize + 1)?;
let bit_count = read_var_int(stream)?;
@ -38,10 +40,32 @@ impl BitRead<LittleEndian> for CreateStringTableMessage {
let compressed = stream.read()?;
let table_data = stream.read_bits(bit_count as usize)?;
let mut table_data = stream.read_bits(bit_count as usize)?;
if compressed {
unimplemented!("TODO: SNAP decoding");
let decompressed_size: u32 = table_data.read()?;
let compressed_size: u32 = table_data.read()?;
let magic = table_data.read_string(Some(4))?;
if magic != "SNAP" {
return Err(ParseError::UnexpectedCompressionType(magic));
}
let compressed_data = table_data.read_bytes(compressed_size as usize - 4)?;
let mut decoder = Decoder::new();
let decompressed_data = decoder.decompress_vec(&compressed_data).map_err(ParseError::from)?;
if decompressed_data.len() != decompressed_size as usize {
return Err(ParseError::UnexpectedDecompressedSize {
expected: decompressed_size,
size: decompressed_data.len() as u32
});
}
let buffer = BitBuffer::new(decompressed_data, LittleEndian);
table_data = BitStream::new(buffer);
}
let table_meta = StringTableMeta {
@ -49,14 +73,16 @@ impl BitRead<LittleEndian> for CreateStringTableMessage {
fixed_userdata_size,
};
let entries = parse_string_table_entries(stream, &table_meta, entity_count, &Vec::new())?;
let entries = parse_string_table_entries(&mut table_data, &table_meta, entity_count, &Vec::new())?;
let mut entries: Vec<(u16, StringTableEntry)> = entries.into_iter().collect();
// verify that there are no holes in our indexes
entries.sort_unstable_by(|(a, _), (b, _)| a.cmp(b));
let last_index = entries.last().map(|(index, _)| *index).unwrap_or(0u16) as usize;
if last_index != entries.len() {
panic!("there should be no holes when reading CreateStringTable message");
if entries.len() > 0 {
let last_index = entries.last().map(|(index, _)| *index).unwrap_or(0u16) as usize;
if last_index != entries.len() - 1 {
panic!("there should be no holes when reading CreateStringTable message");
}
}
let table_entries = entries.into_iter().map(|(_, entry)| entry).collect();
let table = StringTable {
@ -106,11 +132,11 @@ fn parse_string_table_entries(
entry_count: u16,
existing_entries: &Vec<StringTableEntry>,
) -> ReadResult<HashMap<u16, StringTableEntry>> {
let entry_bits = 16 - table_meta.max_entries.leading_zeros();
let entry_bits = log_base2(table_meta.max_entries);
let mut entries = HashMap::with_capacity(entry_count as usize);
let mut last_entry: i16 = -1;
let mut history: ArrayDeque<[String; 32], Wrapping> = ArrayDeque::new();
let mut history: Vec<String> = Vec::new();
for i in 0..entry_count {
let index = if stream.read()? {
@ -119,6 +145,8 @@ fn parse_string_table_entries(
stream.read_sized(entry_bits as usize)?
};
last_entry = index as i16;
let value = if stream.read()? { // set value
if stream.read()? { // reuse from history
let index: u16 = stream.read_sized(5)?;
@ -143,7 +171,7 @@ fn parse_string_table_entries(
Some(size) => stream.read_bits(size.bits as usize)?,
None => {
let bytes: u16 = stream.read_sized(14)?;
stream.read_bits(bytes as usize)?
stream.read_bits(bytes as usize * 8)?
}
})
} else {
@ -171,9 +199,11 @@ fn parse_string_table_entries(
// `entries` always outlives `history` without reallocation
let text = entry.text.clone();
entries.insert(index, entry);
unsafe {
// not 100% sure we should be pushing front here, and not appending
history.push_front(text);
// not 100% sure we should be pushing front here, and not appending
history.push(text);
if history.len() > 32 {
history.remove(0);
}
}
@ -192,3 +222,7 @@ pub fn read_var_int(stream: &mut Stream) -> ReadResult<u32> {
}
Ok(result)
}
pub fn log_base2<T: PrimInt + Unsigned>(num: T) -> u32 {
(std::mem::size_of::<T>() as u32 * 8 - 1) - num.leading_zeros()
}

View file

@ -16,8 +16,8 @@ pub struct MessagePacket {
impl Parse for MessagePacket {
fn parse(stream: &mut Stream, state: &ParserState) -> Result<Self> {
let tick = stream.read_int(32)?;
let flags = stream.read_int(32)?;
let tick = stream.read()?;
let flags = stream.read()?;
let view_origin_1 = Vector::parse(stream, state)?;
let view_angle_1 = Vector::parse(stream, state)?;
@ -26,16 +26,14 @@ impl Parse for MessagePacket {
let view_angles = (Vector::parse(stream, state)?, view_angle_1);
let local_view_angles = (Vector::parse(stream, state)?, local_view_angle_1);
let sequence_in = stream.read_int(32)?;
let sequence_out = stream.read_int(32)?;
let length: usize = stream.read_int(32)?;
let mut packet_data = stream.read_bits(length * 8)?;
let sequence_in = stream.read()?;
let sequence_out = stream.read()?;
let length: u32 = stream.read()?;
let mut packet_data = stream.read_bits(length as usize * 8)?;
let mut messages = Vec::new();
dbg!(&packet_data);
let mut messages: Vec<Message> = Vec::new();
while packet_data.bits_left() > 6 {
let message = Message::parse(stream, state)?;
dbg!(&message);
let message = Message::parse(&mut packet_data, state)?;
messages.push(message);
}

View file

@ -75,10 +75,10 @@ pub struct StringTableEntry {
impl fmt::Debug for StringTableEntry {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match &self.extra_data {
None => write!(f, "Table Entry: '{}'", self.text),
None => write!(f, "StringTableEntry {{ text: {} }}", self.text),
Some(extra_data) => write!(
f,
"StringTableEntry{{ '{}' with {} bits of extra data }}",
"StringTableEntry{{ text: {} extra_data: {} bits }}",
self.text, extra_data.len
),
}

View file

@ -34,6 +34,17 @@ pub enum ParseError {
name: String,
value: GameEventValue,
},
/// Unexpected type of compressed data
UnexpectedCompressionType(String),
/// Error while decompressing SNAP compressed string table
SnapError(snap::Error),
/// Unexpected size after decompressing SNAP data
UnexpectedDecompressedSize {
/// Expected decompressed size
expected: u32,
/// Actual decompressed size
size: u32
}
}
impl From<ReadError> for ParseError {
@ -42,6 +53,12 @@ impl From<ReadError> for ParseError {
}
}
impl From<snap::Error> for ParseError {
fn from(err: snap::Error) -> ParseError {
ParseError::SnapError(err)
}
}
pub type Result<T> = std::result::Result<T, ParseError>;
pub trait Parse: Sized {