1
0
Fork 0
mirror of https://codeberg.org/demostf/parser.git synced 2026-06-03 18:24:05 +02:00
This commit is contained in:
Robin Appelman 2019-03-02 22:33:22 +01:00
commit f2650dc32f
19 changed files with 3614 additions and 2210 deletions

View file

@ -1,5 +1,4 @@
#[allow(unused_variables)]
use std::error::Error;
use std::fs;

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,5 @@
use bitstream_reader::BitRead;
use std::collections::HashMap;
use bitstream_reader::{BitRead};
use crate::{ParseError, Result};
@ -53,7 +53,7 @@ impl FromGameEventValue for String {
_ => Err(ParseError::InvalidGameEvent {
name: name.to_string(),
value,
})
}),
}
}
}
@ -65,7 +65,7 @@ impl FromGameEventValue for f32 {
_ => Err(ParseError::InvalidGameEvent {
name: name.to_string(),
value,
})
}),
}
}
}
@ -77,7 +77,7 @@ impl FromGameEventValue for u32 {
_ => Err(ParseError::InvalidGameEvent {
name: name.to_string(),
value,
})
}),
}
}
}
@ -89,7 +89,7 @@ impl FromGameEventValue for u16 {
_ => Err(ParseError::InvalidGameEvent {
name: name.to_string(),
value,
})
}),
}
}
}
@ -101,7 +101,7 @@ impl FromGameEventValue for u8 {
_ => Err(ParseError::InvalidGameEvent {
name: name.to_string(),
value,
})
}),
}
}
}
@ -113,7 +113,7 @@ impl FromGameEventValue for bool {
_ => Err(ParseError::InvalidGameEvent {
name: name.to_string(),
value,
})
}),
}
}
}
@ -125,7 +125,7 @@ impl FromGameEventValue for () {
_ => Err(ParseError::InvalidGameEvent {
name: name.to_string(),
value,
})
}),
}
}
}

View file

@ -1,8 +1,8 @@
use bitstream_reader::{BitRead, BitReadSized, LittleEndian};
use crate::{ReadResult, Stream};
use crate::demo::sendprop::read_bit_coord;
use crate::demo::vector::Vector;
use crate::{ReadResult, Stream};
#[derive(Debug, Clone)]
pub struct BSPDecalMessage {
@ -44,4 +44,3 @@ impl BitRead<LittleEndian> for BSPDecalMessage {
})
}
}

View file

@ -1,7 +1,7 @@
use bitstream_reader::{BitRead, BitReadSized, LittleEndian};
use crate::{ReadResult, Stream};
use crate::demo::message::stringtable::log_base2;
use crate::{ReadResult, Stream};
#[derive(BitReadSized, Debug)]
pub struct ClassInfoEntry {

View file

@ -3,9 +3,12 @@ use std::iter::FromIterator;
use bitstream_reader::{BitRead, BitReadSized, LittleEndian};
use crate::{Parse, ParseError, ParserState, ReadResult, Result, Stream};
use crate::demo::gameevent_gen::GameEventType;
use crate::demo::gamevent::{GameEvent, GameEventDefinition, GameEventEntry, GameEventValue, GameEventValueType, RawGameEvent};
use crate::demo::gamevent::{
GameEvent, GameEventDefinition, GameEventEntry, GameEventValue, GameEventValueType,
RawGameEvent,
};
use crate::{Parse, ParseError, ParserState, ReadResult, Result, Stream};
fn read_event_value(stream: &mut Stream, definition: &GameEventEntry) -> Result<GameEventValue> {
Ok(match definition.kind {
@ -16,13 +19,13 @@ fn read_event_value(stream: &mut Stream, definition: &GameEventEntry) -> Result<
GameEventValueType::Byte => GameEventValue::Byte(stream.read()?),
GameEventValueType::Boolean => GameEventValue::Boolean(stream.read()?),
GameEventValueType::Local => GameEventValue::Local,
GameEventValueType::None => unreachable!()
GameEventValueType::None => unreachable!(),
})
}
#[derive(Debug)]
pub struct GameEventMessage {
pub event: GameEvent
pub event: GameEvent,
}
impl Parse for GameEventMessage {
@ -37,17 +40,12 @@ impl Parse for GameEventMessage {
values.push(read_event_value(&mut data, &entry)?);
}
RawGameEvent {
event_type,
values,
RawGameEvent { event_type, values }
}
}
None => unreachable!()
None => unreachable!(),
};
let event = GameEvent::from_raw_event(raw_event)?;
Ok(GameEventMessage {
event
})
Ok(GameEventMessage { event })
}
}
@ -86,12 +84,8 @@ impl BitRead<LittleEndian> for GameEventListMessage {
let length: u32 = stream.read_sized(20)?;
let mut data = stream.read_bits(length as usize)?;
let event_list_vec: Vec<GameEventDefinition> = data.read_sized(count as usize)?;
let event_list = HashMap::from_iter(
event_list_vec.into_iter().map(|def| (def.id, def))
);
let event_list = HashMap::from_iter(event_list_vec.into_iter().map(|def| (def.id, def)));
Ok(GameEventListMessage {
event_list
})
Ok(GameEventListMessage { event_list })
}
}

View file

@ -1,7 +1,7 @@
use crate::Stream;
/// Messages that consists only of primitives and string and can be derived
use bitstream_reader::{BitRead, BitStream, LittleEndian};
use std::collections::HashMap;
use crate::Stream;
#[derive(BitRead, Debug)]
pub struct FileMessage {

View file

@ -3,7 +3,6 @@ use num_traits::FromPrimitive;
pub use generated::*;
use crate::{Parse, ParseError, ParserState, Result, Stream, ReadResult};
use crate::demo::message::bspdecal::*;
use crate::demo::message::classinfo::*;
use crate::demo::message::gameevent::*;
@ -13,17 +12,18 @@ use crate::demo::message::stringtable::*;
use crate::demo::message::tempentities::*;
use crate::demo::message::usermessage::*;
use crate::demo::message::voice::*;
use crate::{Parse, ParseError, ParserState, ReadResult, Result, Stream};
use bitstream_reader::{BitRead, LittleEndian};
pub mod classinfo;
pub mod generated;
pub mod stringtable;
pub mod voice;
pub mod bspdecal;
pub mod usermessage;
pub mod classinfo;
pub mod gameevent;
pub mod generated;
pub mod packetentities;
pub mod stringtable;
pub mod tempentities;
pub mod usermessage;
pub mod voice;
#[derive(Primitive, Debug, Clone, Copy)]
pub enum MessageType {
@ -106,29 +106,51 @@ impl Parse for Message {
MessageType::NetTick => Message::NetTick(NetTickMessage::parse(stream, state)?),
MessageType::StringCmd => Message::StringCmd(StringCmdMessage::parse(stream, state)?),
MessageType::SetConVar => Message::SetConVar(SetConVarMessage::parse(stream, state)?),
MessageType::SigOnState => Message::SigOnState(SigOnStateMessage::parse(stream, state)?),
MessageType::SigOnState => {
Message::SigOnState(SigOnStateMessage::parse(stream, state)?)
}
MessageType::Print => Message::Print(PrintMessage::parse(stream, state)?),
MessageType::ServerInfo => Message::ServerInfo(ServerInfoMessage::parse(stream, state)?),
MessageType::ServerInfo => {
Message::ServerInfo(ServerInfoMessage::parse(stream, state)?)
}
MessageType::ClassInfo => Message::ClassInfo(ClassInfoMessage::parse(stream, state)?),
MessageType::SetPause => Message::SetPause(SetPauseMessage::parse(stream, state)?),
MessageType::CreateStringTable => Message::CreateStringTable(CreateStringTableMessage::parse(stream, state)?),
MessageType::UpdateStringTable => Message::UpdateStringTable(UpdateStringTableMessage::parse(stream, state)?),
MessageType::CreateStringTable => {
Message::CreateStringTable(CreateStringTableMessage::parse(stream, state)?)
}
MessageType::UpdateStringTable => {
Message::UpdateStringTable(UpdateStringTableMessage::parse(stream, state)?)
}
MessageType::VoiceInit => Message::VoiceInit(VoiceInitMessage::parse(stream, state)?),
MessageType::VoiceData => Message::VoiceData(VoiceDataMessage::parse(stream, state)?),
MessageType::ParseSounds => Message::ParseSounds(ParseSoundsMessage::parse(stream, state)?),
MessageType::ParseSounds => {
Message::ParseSounds(ParseSoundsMessage::parse(stream, state)?)
}
MessageType::SetView => Message::SetView(SetViewMessage::parse(stream, state)?),
MessageType::FixAngle => Message::FixAngle(FixAngleMessage::parse(stream, state)?),
MessageType::BspDecal => Message::BspDecal(BSPDecalMessage::parse(stream, state)?),
MessageType::UserMessage => Message::UserMessage(UserMessage::parse(stream, state)?),
MessageType::EntityMessage => Message::EntityMessage(EntityMessage::parse(stream, state)?),
MessageType::EntityMessage => {
Message::EntityMessage(EntityMessage::parse(stream, state)?)
}
MessageType::GameEvent => Message::GameEvent(GameEventMessage::parse(stream, state)?),
MessageType::PacketEntities => Message::PacketEntities(PacketEntitiesMessage::parse(stream, state)?),
MessageType::TempEntities => Message::TempEntities(TempEntitiesMessage::parse(stream, state)?),
MessageType::PacketEntities => {
Message::PacketEntities(PacketEntitiesMessage::parse(stream, state)?)
}
MessageType::TempEntities => {
Message::TempEntities(TempEntitiesMessage::parse(stream, state)?)
}
MessageType::PreFetch => Message::PreFetch(PreFetchMessage::parse(stream, state)?),
MessageType::Menu => Message::Menu(MenuMessage::parse(stream, state)?),
MessageType::GameEventList => Message::GameEventList(GameEventListMessage::parse(stream, state)?),
MessageType::GetCvarValue => Message::GetCvarValue(GetCvarValueMessage::parse(stream, state)?),
MessageType::CmdKeyValues => Message::CmdKeyValues(CmdKeyValuesMessage::parse(stream, state)?),
MessageType::GameEventList => {
Message::GameEventList(GameEventListMessage::parse(stream, state)?)
}
MessageType::GetCvarValue => {
Message::GetCvarValue(GetCvarValueMessage::parse(stream, state)?)
}
MessageType::CmdKeyValues => {
Message::CmdKeyValues(CmdKeyValuesMessage::parse(stream, state)?)
}
})
}
}

View file

@ -1,8 +1,8 @@
use bitstream_reader::{BitRead, BitReadSized, LittleEndian};
use crate::{Parse, ParseError, ParserState, ReadResult, Result, Stream};
use crate::demo::packet::datatable::ServerClass;
use crate::demo::sendprop::SendProp;
use crate::{Parse, ParseError, ParserState, ReadResult, Result, Stream};
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct EntityId(u32);

View file

@ -1,11 +1,13 @@
use std::collections::HashMap;
use arraydeque::{ArrayDeque, Wrapping};
use bitstream_reader::{BitRead, BitReadSized, BitStream, LittleEndian, BitBuffer};
use bitstream_reader::{BitBuffer, BitRead, BitReadSized, BitStream, LittleEndian};
use snap::Decoder;
use crate::demo::packet::stringtable::{
ExtraData, FixedUserdataSize, StringTable, StringTableEntry,
};
use crate::{Parse, ParseError, ParserState, ReadResult, Result, Stream};
use crate::demo::packet::stringtable::{ExtraData, FixedUserdataSize, StringTable, StringTableEntry};
use num_traits::{PrimInt, Unsigned};
#[derive(Debug)]
@ -55,12 +57,14 @@ impl Parse for CreateStringTableMessage {
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)?;
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
size: decompressed_data.len() as u32,
});
}
@ -73,7 +77,8 @@ impl Parse for CreateStringTableMessage {
fixed_userdata_size,
};
let entries = parse_string_table_entries(&mut table_data, &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
@ -93,9 +98,7 @@ impl Parse for CreateStringTableMessage {
compressed,
name,
};
Ok(CreateStringTableMessage {
table
})
Ok(CreateStringTableMessage { table })
}
}
@ -115,14 +118,16 @@ impl Parse for UpdateStringTableMessage {
let mut data = stream.read_bits(len)?;
let entries = match state.string_tables.get(table_id as usize) {
Some(table) => parse_string_table_entries(&mut data, &table.get_table_meta(), changed, &table.entries),
None => return Err(ParseError::StringTableNotFound(table_id))
Some(table) => parse_string_table_entries(
&mut data,
&table.get_table_meta(),
changed,
&table.entries,
),
None => return Err(ParseError::StringTableNotFound(table_id)),
}?;
Ok(UpdateStringTableMessage {
table_id,
entries,
})
Ok(UpdateStringTableMessage { table_id, entries })
}
}
@ -147,17 +152,22 @@ fn parse_string_table_entries(
last_entry = index as i16;
let value = if stream.read()? { // set value
if stream.read()? { // reuse from history
let value = if stream.read()? {
// set value
if stream.read()? {
// reuse from history
let index: u16 = stream.read_sized(5)?;
let bytes_to_copy: u32 = stream.read_sized(5)?;
let rest_of_string: String = stream.read()?;
Some(match history.get(index as usize) {
Some(text) => String::from_utf8({
text.bytes().take(bytes_to_copy as usize).chain(rest_of_string.bytes()).collect()
text.bytes()
.take(bytes_to_copy as usize)
.chain(rest_of_string.bytes())
.collect()
})?,
None => rest_of_string // best guess, happens in some pov demos but only for unimportant tables it seems
None => rest_of_string, // best guess, happens in some pov demos but only for unimportant tables it seems
})
} else {
Some(stream.read()?)
@ -176,7 +186,8 @@ fn parse_string_table_entries(
})
} else {
None
}.map(|stream| ExtraData {
}
.map(|stream| ExtraData {
len: stream.bit_len() as u16 / 8,
data: stream,
});
@ -193,7 +204,7 @@ fn parse_string_table_entries(
None => StringTableEntry {
text: value.unwrap_or_default(),
extra_data: user_data,
}
},
};
// optimize: any way to get rid of the clone here?
// `entries` always outlives `history` without reallocation

View file

@ -7,7 +7,7 @@ use super::stringtable::read_var_int;
#[derive(Debug)]
pub struct TempEntitiesMessage {
pub entities: Vec<PacketEntity>
pub entities: Vec<PacketEntity>,
}
impl Parse for TempEntitiesMessage {
@ -17,7 +17,7 @@ impl Parse for TempEntitiesMessage {
let data = stream.read_bits(length as usize)?;
Ok(TempEntitiesMessage {
entities: Vec::new()
entities: Vec::new(),
})
}
}

View file

@ -2,8 +2,8 @@ use bitstream_reader::{BitRead, BitReadSized, LittleEndian};
use enum_primitive_derive::Primitive;
use num_traits::{FromPrimitive, ToPrimitive};
use crate::{ReadResult, Stream};
use crate::demo::message::usermessage::UserMessage::SayText2;
use crate::{ReadResult, Stream};
#[derive(Primitive, Clone, Copy, Debug)]
pub enum UserMessageType {
@ -81,8 +81,8 @@ pub enum UserMessage {
impl BitRead<LittleEndian> for UserMessage {
fn read(stream: &mut Stream) -> ReadResult<Self> {
let message_type = UserMessageType::from_u8(stream.read()?)
.unwrap_or(UserMessageType::Unknown);
let message_type =
UserMessageType::from_u8(stream.read()?).unwrap_or(UserMessageType::Unknown);
let length = stream.read_int(11)?;
let mut data = stream.read_bits(length)?;
let message = match message_type {
@ -113,7 +113,7 @@ impl BitRead<LittleEndian> for SayText2Kind {
"TF_Chat_Team" => SayText2Kind::ChatTeam,
"TF_Chat_AllDead" => SayText2Kind::ChatAllDead,
"#TF_Name_Change" => SayText2Kind::NameChange,
_ => SayText2Kind::ChatAll
_ => SayText2Kind::ChatAll,
})
}
}
@ -144,7 +144,9 @@ impl BitRead<LittleEndian> for SayText2Message {
// grave talk is in the format '*DEAD* \u0003$from\u0001: $text'b
let start = text.find(char::from(3)).unwrap_or(0);
let end = text.find(char::from(1)).unwrap_or(0);
let from: String = String::from_utf8(text.bytes().skip(start + 1).take(end - start - 1).collect())?;
let from: String = String::from_utf8(
text.bytes().skip(start + 1).take(end - start - 1).collect(),
)?;
let text: String = String::from_utf8(text.bytes().skip(end + 5).collect())?;
let kind = SayText2Kind::ChatAllDead;
(kind, from, text)
@ -162,11 +164,14 @@ impl BitRead<LittleEndian> for SayText2Message {
};
// cleanup color codes
let mut text = text
.replace(char::from(1), "")
.replace(char::from(3), "");
let mut text = text.replace(char::from(1), "").replace(char::from(3), "");
while let Some(pos) = text.find(char::from(7)) {
text = String::from_utf8(text.bytes().take(pos).chain(text.bytes().skip(pos + 7)).collect())?;
text = String::from_utf8(
text.bytes()
.take(pos)
.chain(text.bytes().skip(pos + 7))
.collect(),
)?;
}
Ok(SayText2Message {
@ -198,12 +203,12 @@ pub struct TextMessage {
#[derive(BitRead, Debug, Clone)]
pub struct ResetHudMessage {
pub data: u8
pub data: u8,
}
#[derive(BitRead, Debug, Clone)]
pub struct TrainMessage {
pub data: u8
pub data: u8,
}
#[derive(BitRead, Debug, Clone)]
@ -223,13 +228,13 @@ pub struct ShakeMessage {
#[derive(Debug, Clone)]
pub struct UnknownUserMessage {
data: Stream
data: Stream,
}
impl BitRead<LittleEndian> for UnknownUserMessage {
fn read(stream: &mut Stream) -> ReadResult<Self> {
Ok(UnknownUserMessage {
data: stream.read_bits(stream.bits_left())?
data: stream.read_bits(stream.bits_left())?,
})
}
}

View file

@ -52,7 +52,11 @@ impl BitRead<LittleEndian> for ParseSoundsMessage {
fn read(stream: &mut Stream) -> ReadResult<Self> {
let reliable = stream.read()?;
let num = if reliable { 1u8 } else { stream.read()? };
let length = if reliable { stream.read_sized::<u16>(8)? } else { stream.read()? };
let length = if reliable {
stream.read_sized::<u16>(8)?
} else {
stream.read()?
};
let data = stream.read_sized(length as usize)?;
Ok(ParseSoundsMessage {

View file

@ -1,7 +1,7 @@
use bitstream_reader::{BitBuffer, BitStream, LittleEndian};
pub mod gamevent;
pub mod gameevent_gen;
pub mod gamevent;
pub mod header;
pub mod message;
pub mod packet;

View file

@ -1,6 +1,6 @@
use crate::{Parse, ParseError, ParserState, Result, Stream};
use crate::demo::message::Message;
use crate::demo::vector::Vector;
use crate::{Parse, ParseError, ParserState, Result, Stream};
#[derive(Debug)]
pub struct MessagePacket {
@ -36,7 +36,7 @@ impl Parse for MessagePacket {
let message = Message::parse(&mut packet_data, state)?;
match message {
Message::Empty => {}
_ => messages.push(message)
_ => messages.push(message),
}
}

View file

@ -2,9 +2,9 @@ use std::fmt;
use bitstream_reader::{BitRead, LittleEndian};
use crate::{Parse, ParseError, ParserState, ReadResult, Result, Stream};
use crate::demo::message::stringtable::StringTableMeta;
use crate::demo::sendprop::SendPropFlag::Exclude;
use crate::{Parse, ParseError, ParserState, ReadResult, Result, Stream};
#[derive(BitRead, Clone, Copy, Debug)]
pub struct FixedUserdataSize {

View file

@ -30,10 +30,7 @@ pub enum ParseError {
/// A unknown game event type was read
UnknownGameEvent(String),
/// A read game event doesn't contain the expected values
InvalidGameEvent {
name: String,
value: GameEventValue,
},
InvalidGameEvent { name: String, value: GameEventValue },
/// Unexpected type of compressed data
UnexpectedCompressionType(String),
/// Error while decompressing SNAP compressed string table
@ -43,8 +40,8 @@ pub enum ParseError {
/// Expected decompressed size
expected: u32,
/// Actual decompressed size
size: u32
}
size: u32,
},
}
impl From<ReadError> for ParseError {

View file

@ -2,11 +2,11 @@ use std::collections::HashMap;
use crate::demo::gameevent_gen::GameEventType;
use crate::demo::gamevent::GameEventDefinition;
use crate::demo::message::Message;
use crate::demo::message::packetentities::EntityId;
use crate::demo::message::Message;
use crate::demo::packet::datatable::{SendTable, ServerClass};
use crate::demo::packet::Packet;
use crate::demo::packet::stringtable::{StringTable, StringTableEntry};
use crate::demo::packet::Packet;
use crate::demo::sendprop::SendProp;
use crate::Stream;

View file

@ -206,7 +206,11 @@ pub fn read_bit_coord(stream: &mut Stream) -> ReadResult<f32> {
Ok(if has_int || has_frac {
let sign = if stream.read()? { -1f32 } else { 1f32 };
let int_val: u16 = if has_int { stream.read_sized::<u16>(14)? + 1 } else { 0 };
let int_val: u16 = if has_int {
stream.read_sized::<u16>(14)? + 1
} else {
0
};
let frac_val: u8 = if has_frac { stream.read_sized(5)? } else { 0 };
let value = int_val as f32 + (frac_val as f32 * (1f32 / 32f32));
value * sign