mirror of
https://codeberg.org/demostf/parser.git
synced 2026-06-03 18:24:05 +02:00
sendprop decoding
This commit is contained in:
parent
164f88a32a
commit
ff18680a02
25 changed files with 389 additions and 135 deletions
|
|
@ -5,7 +5,7 @@ extern crate test;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
|
||||||
use test::Bencher;
|
use test::Bencher;
|
||||||
use tf_demo_parser::{Demo, Stream, DemoParser};
|
use tf_demo_parser::{Demo, DemoParser, Stream};
|
||||||
|
|
||||||
fn bench_file(input_file: &str, b: &mut Bencher) {
|
fn bench_file(input_file: &str, b: &mut Bencher) {
|
||||||
let file = fs::read(input_file).expect("Unable to read file");
|
let file = fs::read(input_file).expect("Unable to read file");
|
||||||
|
|
|
||||||
|
|
@ -5,17 +5,17 @@
|
||||||
|
|
||||||
extern crate test;
|
extern crate test;
|
||||||
|
|
||||||
use std::fs;
|
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
|
use std::fs;
|
||||||
|
|
||||||
use tf_demo_parser::{Demo, DemoParser, MatchState, MessageTypeAnalyser, MessageType, ParserState};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
use test::Bencher;
|
||||||
|
use tf_demo_parser::demo::message::Message;
|
||||||
use tf_demo_parser::demo::packet::datatable::{ParseSendTable, SendTableName};
|
use tf_demo_parser::demo::packet::datatable::{ParseSendTable, SendTableName};
|
||||||
use tf_demo_parser::demo::packet::stringtable::StringTableEntry;
|
use tf_demo_parser::demo::packet::stringtable::StringTableEntry;
|
||||||
use tf_demo_parser::demo::message::Message;
|
|
||||||
use tf_demo_parser::demo::sendprop::SendPropDefinition;
|
|
||||||
use std::collections::{HashMap, HashSet};
|
|
||||||
use tf_demo_parser::demo::parser::MessageHandler;
|
use tf_demo_parser::demo::parser::MessageHandler;
|
||||||
use test::Bencher;
|
use tf_demo_parser::demo::sendprop::SendPropDefinition;
|
||||||
|
use tf_demo_parser::{Demo, DemoParser, MatchState, MessageType, MessageTypeAnalyser, ParserState};
|
||||||
|
|
||||||
pub struct SendPropAnalyser;
|
pub struct SendPropAnalyser;
|
||||||
|
|
||||||
|
|
@ -31,11 +31,15 @@ impl MessageHandler for SendPropAnalyser {
|
||||||
fn handle_string_entry(&mut self, table: &String, _index: usize, entry: &StringTableEntry) {}
|
fn handle_string_entry(&mut self, table: &String, _index: usize, entry: &StringTableEntry) {}
|
||||||
|
|
||||||
fn get_output(self, state: ParserState) -> Self::Output {
|
fn get_output(self, state: ParserState) -> Self::Output {
|
||||||
state.send_tables.into_iter().map(|(_k, v)| ParseSendTable {
|
state
|
||||||
|
.send_tables
|
||||||
|
.into_iter()
|
||||||
|
.map(|(_k, v)| ParseSendTable {
|
||||||
name: v.name,
|
name: v.name,
|
||||||
props: v.props,
|
props: v.props,
|
||||||
needs_decoder: v.needs_decoder
|
needs_decoder: v.needs_decoder,
|
||||||
}).collect()
|
})
|
||||||
|
.collect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -43,9 +47,13 @@ fn flatten_bench(input_file: &str, b: &mut Bencher) {
|
||||||
let file = fs::read(input_file).expect("Unable to read file");
|
let file = fs::read(input_file).expect("Unable to read file");
|
||||||
let demo = Demo::new(file);
|
let demo = Demo::new(file);
|
||||||
let stream = demo.get_stream();
|
let stream = demo.get_stream();
|
||||||
let (_, send_tables) = DemoParser::parse_with_analyser(stream.clone(), SendPropAnalyser).unwrap();
|
let (_, send_tables) =
|
||||||
|
DemoParser::parse_with_analyser(stream.clone(), SendPropAnalyser).unwrap();
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
let flat: Vec<_> = send_tables.iter().map(|table| table.flatten_props(&send_tables)).collect();
|
let flat: Vec<_> = send_tables
|
||||||
|
.iter()
|
||||||
|
.map(|table| table.flatten_props(&send_tables))
|
||||||
|
.collect();
|
||||||
test::black_box(flat);
|
test::black_box(flat);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use bitstream_reader::{BitRead, LittleEndian, BitSkip};
|
use bitstream_reader::{BitRead, BitSkip, LittleEndian};
|
||||||
|
|
||||||
use crate::demo::sendprop::read_bit_coord;
|
use crate::demo::sendprop::read_bit_coord;
|
||||||
use crate::demo::vector::Vector;
|
use crate::demo::vector::Vector;
|
||||||
|
|
@ -45,4 +45,4 @@ impl BitRead<LittleEndian> for BSPDecalMessage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BitSkip<LittleEndian> for BSPDecalMessage{}
|
impl BitSkip<LittleEndian> for BSPDecalMessage {}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use bitstream_reader::{BitRead, BitReadSized, LittleEndian, BitSkip};
|
use bitstream_reader::{BitRead, BitReadSized, BitSkip, LittleEndian};
|
||||||
|
|
||||||
use crate::demo::message::stringtable::log_base2;
|
use crate::demo::message::stringtable::log_base2;
|
||||||
use crate::{ReadResult, Stream};
|
use crate::{ReadResult, Stream};
|
||||||
|
|
@ -41,4 +41,4 @@ impl BitRead<LittleEndian> for ClassInfoMessage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BitSkip<LittleEndian> for ClassInfoMessage{}
|
impl BitSkip<LittleEndian> for ClassInfoMessage {}
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::iter::FromIterator;
|
use std::iter::FromIterator;
|
||||||
|
|
||||||
use bitstream_reader::{BitRead, LittleEndian, BitSkip};
|
use bitstream_reader::{BitRead, BitSkip, LittleEndian};
|
||||||
|
|
||||||
use crate::demo::gameevent_gen::GameEventType;
|
use crate::demo::gameevent_gen::GameEventType;
|
||||||
use crate::demo::gamevent::{
|
use crate::demo::gamevent::{
|
||||||
GameEvent, GameEventDefinition, GameEventEntry, GameEventValue, GameEventValueType,
|
GameEvent, GameEventDefinition, GameEventEntry, GameEventValue, GameEventValueType,
|
||||||
RawGameEvent,
|
RawGameEvent,
|
||||||
};
|
};
|
||||||
use crate::{Parse, ParserState, ReadResult, Result, Stream, ParseError};
|
|
||||||
use crate::demo::parser::ParseBitSkip;
|
use crate::demo::parser::ParseBitSkip;
|
||||||
|
use crate::{Parse, ParseError, ParserState, ReadResult, Result, Stream};
|
||||||
|
|
||||||
fn read_event_value(stream: &mut Stream, definition: &GameEventEntry) -> Result<GameEventValue> {
|
fn read_event_value(stream: &mut Stream, definition: &GameEventEntry) -> Result<GameEventValue> {
|
||||||
Ok(match definition.kind {
|
Ok(match definition.kind {
|
||||||
|
|
@ -111,4 +111,4 @@ impl BitRead<LittleEndian> for GameEventListMessage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BitSkip<LittleEndian> for GameEventListMessage{}
|
impl BitSkip<LittleEndian> for GameEventListMessage {}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::Stream;
|
use crate::Stream;
|
||||||
/// Messages that consists only of primitives and string and can be derived
|
/// Messages that consists only of primitives and string and can be derived
|
||||||
use bitstream_reader::{BitRead, LittleEndian, BitSize, BitSkip};
|
use bitstream_reader::{BitRead, BitSize, BitSkip, LittleEndian};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
#[derive(BitRead, Debug)]
|
#[derive(BitRead, Debug)]
|
||||||
|
|
@ -10,7 +10,7 @@ pub struct FileMessage {
|
||||||
pub requested: bool,
|
pub requested: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BitSkip<LittleEndian> for FileMessage{}
|
impl BitSkip<LittleEndian> for FileMessage {}
|
||||||
|
|
||||||
#[derive(BitRead, BitSize, Debug)]
|
#[derive(BitRead, BitSize, Debug)]
|
||||||
pub struct NetTickMessage {
|
pub struct NetTickMessage {
|
||||||
|
|
@ -24,7 +24,7 @@ pub struct StringCmdMessage {
|
||||||
pub command: String,
|
pub command: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BitSkip<LittleEndian> for StringCmdMessage{}
|
impl BitSkip<LittleEndian> for StringCmdMessage {}
|
||||||
|
|
||||||
#[derive(BitRead, BitSize, Debug)]
|
#[derive(BitRead, BitSize, Debug)]
|
||||||
pub struct SigOnStateMessage {
|
pub struct SigOnStateMessage {
|
||||||
|
|
@ -37,7 +37,7 @@ pub struct PrintMessage {
|
||||||
pub value: String,
|
pub value: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BitSkip<LittleEndian> for PrintMessage{}
|
impl BitSkip<LittleEndian> for PrintMessage {}
|
||||||
|
|
||||||
#[derive(BitRead, Debug)]
|
#[derive(BitRead, Debug)]
|
||||||
pub struct ServerInfoMessage {
|
pub struct ServerInfoMessage {
|
||||||
|
|
@ -60,7 +60,7 @@ pub struct ServerInfoMessage {
|
||||||
pub replay: bool,
|
pub replay: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BitSkip<LittleEndian> for ServerInfoMessage{}
|
impl BitSkip<LittleEndian> for ServerInfoMessage {}
|
||||||
|
|
||||||
#[derive(BitRead, BitSize, Debug)]
|
#[derive(BitRead, BitSize, Debug)]
|
||||||
pub struct SetPauseMessage {
|
pub struct SetPauseMessage {
|
||||||
|
|
@ -94,7 +94,7 @@ pub struct EntityMessage {
|
||||||
pub data: Stream,
|
pub data: Stream,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BitSkip<LittleEndian> for EntityMessage{}
|
impl BitSkip<LittleEndian> for EntityMessage {}
|
||||||
|
|
||||||
#[derive(BitRead, BitSize, Debug)]
|
#[derive(BitRead, BitSize, Debug)]
|
||||||
pub struct PreFetchMessage {
|
pub struct PreFetchMessage {
|
||||||
|
|
@ -111,7 +111,7 @@ pub struct MenuMessage {
|
||||||
pub index: Stream,
|
pub index: Stream,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BitSkip<LittleEndian> for MenuMessage{}
|
impl BitSkip<LittleEndian> for MenuMessage {}
|
||||||
|
|
||||||
#[derive(BitRead, Debug)]
|
#[derive(BitRead, Debug)]
|
||||||
pub struct GetCvarValueMessage {
|
pub struct GetCvarValueMessage {
|
||||||
|
|
@ -119,7 +119,7 @@ pub struct GetCvarValueMessage {
|
||||||
pub value: String,
|
pub value: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BitSkip<LittleEndian> for GetCvarValueMessage{}
|
impl BitSkip<LittleEndian> for GetCvarValueMessage {}
|
||||||
|
|
||||||
#[derive(BitRead, Debug)]
|
#[derive(BitRead, Debug)]
|
||||||
#[endianness = "LittleEndian"]
|
#[endianness = "LittleEndian"]
|
||||||
|
|
@ -129,4 +129,4 @@ pub struct CmdKeyValuesMessage {
|
||||||
pub data: Stream,
|
pub data: Stream,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BitSkip<LittleEndian> for CmdKeyValuesMessage{}
|
impl BitSkip<LittleEndian> for CmdKeyValuesMessage {}
|
||||||
|
|
|
||||||
|
|
@ -5,28 +5,28 @@ pub use generated::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_repr::{Deserialize_repr, Serialize_repr};
|
use serde_repr::{Deserialize_repr, Serialize_repr};
|
||||||
|
|
||||||
use crate::{Parse, ParseError, ParserState, Result, Stream};
|
|
||||||
use crate::demo::message::bspdecal::*;
|
use crate::demo::message::bspdecal::*;
|
||||||
use crate::demo::message::classinfo::*;
|
use crate::demo::message::classinfo::*;
|
||||||
use crate::demo::message::gameevent::*;
|
use crate::demo::message::gameevent::*;
|
||||||
use crate::demo::message::packetentities::*;
|
use crate::demo::message::packetentities::*;
|
||||||
|
use crate::demo::message::setconvar::*;
|
||||||
use crate::demo::message::stringtable::*;
|
use crate::demo::message::stringtable::*;
|
||||||
use crate::demo::message::tempentities::*;
|
use crate::demo::message::tempentities::*;
|
||||||
use crate::demo::message::usermessage::*;
|
use crate::demo::message::usermessage::*;
|
||||||
use crate::demo::message::voice::*;
|
use crate::demo::message::voice::*;
|
||||||
use crate::demo::message::setconvar::*;
|
|
||||||
use crate::demo::parser::ParseBitSkip;
|
use crate::demo::parser::ParseBitSkip;
|
||||||
|
use crate::{Parse, ParseError, ParserState, Result, Stream};
|
||||||
|
|
||||||
pub mod bspdecal;
|
pub mod bspdecal;
|
||||||
pub mod classinfo;
|
pub mod classinfo;
|
||||||
pub mod gameevent;
|
pub mod gameevent;
|
||||||
pub mod generated;
|
pub mod generated;
|
||||||
pub mod packetentities;
|
pub mod packetentities;
|
||||||
|
pub mod setconvar;
|
||||||
pub mod stringtable;
|
pub mod stringtable;
|
||||||
pub mod tempentities;
|
pub mod tempentities;
|
||||||
pub mod usermessage;
|
pub mod usermessage;
|
||||||
pub mod voice;
|
pub mod voice;
|
||||||
pub mod setconvar;
|
|
||||||
|
|
||||||
#[derive(Primitive, Debug, Clone, Copy, PartialEq, Eq, Serialize_repr, Deserialize_repr)]
|
#[derive(Primitive, Debug, Clone, Copy, PartialEq, Eq, Serialize_repr, Deserialize_repr)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
|
|
@ -142,7 +142,11 @@ impl Message {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_type(message_type: MessageType, stream: &mut Stream, state: &ParserState) -> Result<Self> {
|
pub fn from_type(
|
||||||
|
message_type: MessageType,
|
||||||
|
stream: &mut Stream,
|
||||||
|
state: &ParserState,
|
||||||
|
) -> Result<Self> {
|
||||||
Ok(match message_type {
|
Ok(match message_type {
|
||||||
MessageType::Empty => Message::Empty,
|
MessageType::Empty => Message::Empty,
|
||||||
MessageType::File => Message::File(FileMessage::parse(stream, state)?),
|
MessageType::File => Message::File(FileMessage::parse(stream, state)?),
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
use bitstream_reader::BitRead;
|
use bitstream_reader::BitRead;
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{Parse, ParserState, Result, Stream, ParseError};
|
|
||||||
use crate::demo::packet::datatable::ServerClass;
|
use crate::demo::packet::datatable::ServerClass;
|
||||||
use crate::demo::sendprop::SendProp;
|
|
||||||
use crate::demo::parser::ParseBitSkip;
|
use crate::demo::parser::ParseBitSkip;
|
||||||
|
use crate::demo::sendprop::SendProp;
|
||||||
|
use crate::{Parse, ParseError, ParserState, Result, Stream};
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
pub struct EntityId(u32);
|
pub struct EntityId(u32);
|
||||||
|
|
@ -81,13 +81,11 @@ impl ParseBitSkip for PacketEntitiesMessage {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct EntityUpdate {
|
pub struct EntityUpdate {
|
||||||
props: Vec<SendProp>
|
props: Vec<SendProp>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parse for EntityUpdate {
|
impl Parse for EntityUpdate {
|
||||||
fn parse(stream: &mut Stream, _state: &ParserState) -> Result<Self> {
|
fn parse(stream: &mut Stream, _state: &ParserState) -> Result<Self> {
|
||||||
Ok(EntityUpdate {
|
Ok(EntityUpdate { props: Vec::new() })
|
||||||
props: Vec::new()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
use bitstream_reader::{BitRead, BitReadSized, LittleEndian, BitSkip};
|
use bitstream_reader::{BitRead, BitReadSized, BitSkip, LittleEndian};
|
||||||
|
|
||||||
use crate::demo::message::stringtable::log_base2;
|
use crate::demo::message::stringtable::log_base2;
|
||||||
use crate::{ReadResult, Stream};
|
use crate::{ReadResult, Stream};
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct SetConVarMessage {
|
pub struct SetConVarMessage {
|
||||||
vars: Vec<(String, String)>,
|
vars: Vec<(String, String)>,
|
||||||
|
|
@ -14,13 +13,15 @@ impl BitRead<LittleEndian> for SetConVarMessage {
|
||||||
let count: u8 = stream.read()?;
|
let count: u8 = stream.read()?;
|
||||||
let mut vars: Vec<(String, String)> = Vec::with_capacity(count as usize);
|
let mut vars: Vec<(String, String)> = Vec::with_capacity(count as usize);
|
||||||
for _ in 0..count {
|
for _ in 0..count {
|
||||||
let key = stream.read().unwrap_or_else(|_| "Malformed cvar name".to_string());
|
let key = stream
|
||||||
let value = stream.read().unwrap_or_else(|_| "Malformed cvar value".to_string());
|
.read()
|
||||||
|
.unwrap_or_else(|_| "Malformed cvar name".to_string());
|
||||||
|
let value = stream
|
||||||
|
.read()
|
||||||
|
.unwrap_or_else(|_| "Malformed cvar value".to_string());
|
||||||
vars.push((key, value));
|
vars.push((key, value));
|
||||||
}
|
}
|
||||||
Ok(SetConVarMessage {
|
Ok(SetConVarMessage { vars })
|
||||||
vars
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,11 @@ use bitstream_reader::{BitBuffer, BitStream, LittleEndian};
|
||||||
use num_traits::{PrimInt, Unsigned};
|
use num_traits::{PrimInt, Unsigned};
|
||||||
use snap::Decoder;
|
use snap::Decoder;
|
||||||
|
|
||||||
use crate::{Parse, ParseError, ParserState, ReadResult, Result, Stream};
|
|
||||||
use crate::demo::packet::stringtable::{
|
use crate::demo::packet::stringtable::{
|
||||||
ExtraData, FixedUserdataSize, StringTable, StringTableEntry,
|
ExtraData, FixedUserdataSize, StringTable, StringTableEntry,
|
||||||
};
|
};
|
||||||
use crate::demo::parser::ParseBitSkip;
|
use crate::demo::parser::ParseBitSkip;
|
||||||
|
use crate::{Parse, ParseError, ParserState, ReadResult, Result, Stream};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct CreateStringTableMessage {
|
pub struct CreateStringTableMessage {
|
||||||
|
|
@ -105,7 +105,6 @@ impl ParseBitSkip for CreateStringTableMessage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct UpdateStringTableMessage {
|
pub struct UpdateStringTableMessage {
|
||||||
pub entries: Vec<(u16, StringTableEntry)>,
|
pub entries: Vec<(u16, StringTableEntry)>,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::{Parse, ParserState, Result, Stream, ParseError};
|
use crate::{Parse, ParseError, ParserState, Result, Stream};
|
||||||
|
|
||||||
use super::packetentities::PacketEntity;
|
use super::packetentities::PacketEntity;
|
||||||
use super::stringtable::read_var_int;
|
use super::stringtable::read_var_int;
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
use bitstream_reader::{BitRead, LittleEndian};
|
use bitstream_reader::{BitRead, LittleEndian};
|
||||||
use enum_primitive_derive::Primitive;
|
use enum_primitive_derive::Primitive;
|
||||||
use num_traits::FromPrimitive;
|
use num_traits::FromPrimitive;
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{ReadResult, Stream, Result, ParseError};
|
|
||||||
use crate::demo::parser::ParseBitSkip;
|
use crate::demo::parser::ParseBitSkip;
|
||||||
|
use crate::{ParseError, ReadResult, Result, Stream};
|
||||||
|
|
||||||
#[derive(Primitive, Clone, Copy, Debug)]
|
#[derive(Primitive, Clone, Copy, Debug)]
|
||||||
pub enum UserMessageType {
|
pub enum UserMessageType {
|
||||||
|
|
@ -102,12 +102,11 @@ impl BitRead<LittleEndian> for UserMessage {
|
||||||
impl ParseBitSkip for UserMessage {
|
impl ParseBitSkip for UserMessage {
|
||||||
fn parse_skip(stream: &mut Stream) -> Result<()> {
|
fn parse_skip(stream: &mut Stream) -> Result<()> {
|
||||||
let _ = stream.skip_bits(8)?;
|
let _ = stream.skip_bits(8)?;
|
||||||
let length:u32 = stream.read_int(11)?;
|
let length: u32 = stream.read_int(11)?;
|
||||||
stream.skip_bits(length as usize).map_err(ParseError::from)
|
stream.skip_bits(length as usize).map_err(ParseError::from)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||||
pub enum ChatMessageKind {
|
pub enum ChatMessageKind {
|
||||||
#[serde(rename = "TF_Chat_All")]
|
#[serde(rename = "TF_Chat_All")]
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use bitstream_reader::{BitRead, LittleEndian, BitSkip};
|
use bitstream_reader::{BitRead, BitSkip, LittleEndian};
|
||||||
|
|
||||||
use crate::{ReadResult, Stream};
|
use crate::{ReadResult, Stream};
|
||||||
|
|
||||||
|
|
@ -30,7 +30,7 @@ impl BitRead<LittleEndian> for VoiceInitMessage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BitSkip<LittleEndian> for VoiceInitMessage{}
|
impl BitSkip<LittleEndian> for VoiceInitMessage {}
|
||||||
|
|
||||||
#[derive(BitRead, Debug, Clone)]
|
#[derive(BitRead, Debug, Clone)]
|
||||||
#[endianness = "LittleEndian"]
|
#[endianness = "LittleEndian"]
|
||||||
|
|
@ -42,7 +42,7 @@ pub struct VoiceDataMessage {
|
||||||
data: Stream,
|
data: Stream,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BitSkip<LittleEndian> for VoiceDataMessage{}
|
impl BitSkip<LittleEndian> for VoiceDataMessage {}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ParseSoundsMessage {
|
pub struct ParseSoundsMessage {
|
||||||
|
|
@ -72,4 +72,4 @@ impl BitRead<LittleEndian> for ParseSoundsMessage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BitSkip<LittleEndian> for ParseSoundsMessage{}
|
impl BitSkip<LittleEndian> for ParseSoundsMessage {}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
use bitstream_reader::{BitRead, LittleEndian};
|
use bitstream_reader::{BitRead, LittleEndian};
|
||||||
|
|
||||||
use crate::demo::sendprop::{SendPropDefinition, SendPropFlag, SendPropType};
|
use crate::demo::sendprop::{SendPropDefinition, SendPropFlag, SendPropType};
|
||||||
use crate::{Parse, ParseError, ParserState, Result, Stream, ReadResult};
|
use crate::{Parse, ParseError, ParserState, ReadResult, Result, Stream};
|
||||||
use std::fmt;
|
use serde::{Deserialize, Serialize};
|
||||||
use serde::{Serialize, Deserialize};
|
|
||||||
use std::rc::Rc;
|
|
||||||
use std::cell::{Cell, RefCell};
|
use std::cell::{Cell, RefCell};
|
||||||
|
use std::fmt;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
#[derive(BitRead, Debug)]
|
#[derive(BitRead, Debug)]
|
||||||
pub struct ServerClass {
|
pub struct ServerClass {
|
||||||
|
|
@ -60,20 +60,17 @@ impl Parse for ParseSendTable {
|
||||||
let mut props = Vec::with_capacity(prop_count);
|
let mut props = Vec::with_capacity(prop_count);
|
||||||
|
|
||||||
for _ in 0..prop_count {
|
for _ in 0..prop_count {
|
||||||
let prop: SendPropDefinition =
|
let prop: SendPropDefinition = SendPropDefinition::read(stream, name.clone())?;
|
||||||
SendPropDefinition::read(stream, name.clone())?;
|
|
||||||
if prop.flags.contains(SendPropFlag::InsideArray) {
|
if prop.flags.contains(SendPropFlag::InsideArray) {
|
||||||
if array_element_prop.is_some()
|
if array_element_prop.is_some() || prop.flags.contains(SendPropFlag::ChangesOften) {
|
||||||
|| prop.flags.contains(SendPropFlag::ChangesOften)
|
return Err(ParseError::InvalidSendProp(
|
||||||
{
|
|
||||||
return Err(ParseError::InvalidSendPropArray(
|
|
||||||
"Array contents can't have the 'ChangesOften' flag".to_owned(),
|
"Array contents can't have the 'ChangesOften' flag".to_owned(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
array_element_prop = Some(prop);
|
array_element_prop = Some(prop);
|
||||||
} else if let Some(array_element) = array_element_prop {
|
} else if let Some(array_element) = array_element_prop {
|
||||||
if prop.prop_type != SendPropType::Array {
|
if prop.prop_type != SendPropType::Array {
|
||||||
return Err(ParseError::InvalidSendPropArray(
|
return Err(ParseError::InvalidSendProp(
|
||||||
"Array contents can without array".to_owned(),
|
"Array contents can without array".to_owned(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
@ -131,14 +128,25 @@ impl ParseSendTable {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: below is a direct port from the js which is a direct port from C++ and not very optimal
|
// TODO: below is a direct port from the js which is a direct port from C++ and not very optimal
|
||||||
fn get_all_props<'a>(&'a self, tables: &'a [ParseSendTable], excludes: &[Exclude], props: &mut Vec<&'a SendPropDefinition>) {
|
fn get_all_props<'a>(
|
||||||
|
&'a self,
|
||||||
|
tables: &'a [ParseSendTable],
|
||||||
|
excludes: &[Exclude],
|
||||||
|
props: &mut Vec<&'a SendPropDefinition>,
|
||||||
|
) {
|
||||||
let mut local_props = Vec::new();
|
let mut local_props = Vec::new();
|
||||||
|
|
||||||
self.get_all_props_iterator_props(tables, excludes, &mut local_props, props);
|
self.get_all_props_iterator_props(tables, excludes, &mut local_props, props);
|
||||||
props.extend_from_slice(&local_props);
|
props.extend_from_slice(&local_props);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_all_props_iterator_props<'a>(&'a self, tables: &'a [ParseSendTable], excludes: &[Exclude], local_props: &mut Vec<&'a SendPropDefinition>, props: &mut Vec<&'a SendPropDefinition>) {
|
fn get_all_props_iterator_props<'a>(
|
||||||
|
&'a self,
|
||||||
|
tables: &'a [ParseSendTable],
|
||||||
|
excludes: &[Exclude],
|
||||||
|
local_props: &mut Vec<&'a SendPropDefinition>,
|
||||||
|
props: &mut Vec<&'a SendPropDefinition>,
|
||||||
|
) {
|
||||||
for prop in self.props.iter() {
|
for prop in self.props.iter() {
|
||||||
if prop.is_exclude() {
|
if prop.is_exclude() {
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -202,24 +210,22 @@ impl Parse for DataTablePacket {
|
||||||
parse_tables.push(table);
|
parse_tables.push(table);
|
||||||
}
|
}
|
||||||
|
|
||||||
let flat_props: Vec<_> = parse_tables.iter()
|
let flat_props: Vec<_> = parse_tables
|
||||||
|
.iter()
|
||||||
.map(|table| table.flatten_props(&parse_tables))
|
.map(|table| table.flatten_props(&parse_tables))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let tables = parse_tables.into_iter()
|
let tables = parse_tables
|
||||||
|
.into_iter()
|
||||||
.zip(flat_props.into_iter())
|
.zip(flat_props.into_iter())
|
||||||
.map(|(parse_table, flat)| {
|
.map(|(parse_table, flat)| SendTable {
|
||||||
SendTable {
|
|
||||||
name: parse_table.name,
|
name: parse_table.name,
|
||||||
props: parse_table.props,
|
props: parse_table.props,
|
||||||
needs_decoder: parse_table.needs_decoder,
|
needs_decoder: parse_table.needs_decoder,
|
||||||
flattened_props: flat,
|
flattened_props: flat,
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
// TODO linked tables?
|
|
||||||
|
|
||||||
let server_class_count = packet_data.read_int(16)?;
|
let server_class_count = packet_data.read_int(16)?;
|
||||||
let server_classes = packet_data.read_sized(server_class_count)?;
|
let server_classes = packet_data.read_sized(server_class_count)?;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
use bitstream_reader::{BitRead, BitSize, LazyBitRead, LittleEndian};
|
use bitstream_reader::{BitRead, BitSize, LazyBitRead, LittleEndian};
|
||||||
|
|
||||||
use crate::{Parse, ParserState, ReadResult, Result, Stream};
|
|
||||||
use crate::demo::message::{Message, MessageType};
|
use crate::demo::message::{Message, MessageType};
|
||||||
use crate::demo::vector::Vector;
|
use crate::demo::vector::Vector;
|
||||||
|
use crate::{Parse, ParserState, ReadResult, Result, Stream};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct MessagePacket {
|
pub struct MessagePacket {
|
||||||
|
|
|
||||||
|
|
@ -3,16 +3,16 @@ use std::collections::HashMap;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_repr::{Deserialize_repr, Serialize_repr};
|
use serde_repr::{Deserialize_repr, Serialize_repr};
|
||||||
|
|
||||||
use crate::{ParserState, ReadResult, Stream};
|
|
||||||
use crate::demo::gameevent_gen::{
|
use crate::demo::gameevent_gen::{
|
||||||
GameEvent, PlayerDeathEvent, PlayerSpawnEvent, TeamPlayRoundWinEvent,
|
GameEvent, PlayerDeathEvent, PlayerSpawnEvent, TeamPlayRoundWinEvent,
|
||||||
};
|
};
|
||||||
use crate::demo::message::{Message, MessageType};
|
|
||||||
use crate::demo::message::packetentities::EntityId;
|
use crate::demo::message::packetentities::EntityId;
|
||||||
use crate::demo::message::usermessage::{ChatMessageKind, SayText2Message, UserMessage};
|
use crate::demo::message::usermessage::{ChatMessageKind, SayText2Message, UserMessage};
|
||||||
|
use crate::demo::message::{Message, MessageType};
|
||||||
use crate::demo::packet::stringtable::StringTableEntry;
|
use crate::demo::packet::stringtable::StringTableEntry;
|
||||||
use crate::demo::parser::handler::MessageHandler;
|
use crate::demo::parser::handler::MessageHandler;
|
||||||
use crate::demo::vector::Vector;
|
use crate::demo::vector::Vector;
|
||||||
|
use crate::{ParserState, ReadResult, Stream};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||||
pub struct ChatMassage {
|
pub struct ChatMassage {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::demo::message::{Message, MessageType};
|
use crate::demo::message::{Message, MessageType};
|
||||||
use crate::demo::packet::datatable::{SendTable, ServerClass};
|
use crate::demo::packet::datatable::{SendTable, ServerClass};
|
||||||
use crate::demo::packet::Packet;
|
|
||||||
use crate::demo::packet::stringtable::{StringTable, StringTableEntry};
|
use crate::demo::packet::stringtable::{StringTable, StringTableEntry};
|
||||||
|
use crate::demo::packet::Packet;
|
||||||
use crate::demo::parser::analyser::Analyser;
|
use crate::demo::parser::analyser::Analyser;
|
||||||
use crate::ParserState;
|
use crate::ParserState;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,20 +3,20 @@ use std::collections::HashMap;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_repr::{Deserialize_repr, Serialize_repr};
|
use serde_repr::{Deserialize_repr, Serialize_repr};
|
||||||
|
|
||||||
use crate::{ParserState, ReadResult, Stream};
|
|
||||||
use crate::demo::gameevent_gen::{
|
use crate::demo::gameevent_gen::{
|
||||||
GameEvent, PlayerDeathEvent, PlayerSpawnEvent, TeamPlayRoundWinEvent,
|
GameEvent, PlayerDeathEvent, PlayerSpawnEvent, TeamPlayRoundWinEvent,
|
||||||
};
|
};
|
||||||
use crate::demo::message::{Message, MessageType};
|
|
||||||
use crate::demo::message::packetentities::EntityId;
|
use crate::demo::message::packetentities::EntityId;
|
||||||
use crate::demo::message::usermessage::{ChatMessageKind, SayText2Message, UserMessage};
|
use crate::demo::message::usermessage::{ChatMessageKind, SayText2Message, UserMessage};
|
||||||
use crate::demo::packet::PacketType;
|
use crate::demo::message::{Message, MessageType};
|
||||||
use crate::demo::packet::stringtable::StringTableEntry;
|
use crate::demo::packet::stringtable::StringTableEntry;
|
||||||
|
use crate::demo::packet::PacketType;
|
||||||
use crate::demo::parser::handler::MessageHandler;
|
use crate::demo::parser::handler::MessageHandler;
|
||||||
use crate::demo::vector::Vector;
|
use crate::demo::vector::Vector;
|
||||||
|
use crate::{ParserState, ReadResult, Stream};
|
||||||
|
|
||||||
pub struct MessageTypeAnalyser {
|
pub struct MessageTypeAnalyser {
|
||||||
packet_types: Vec<MessageType>
|
packet_types: Vec<MessageType>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MessageHandler for MessageTypeAnalyser {
|
impl MessageHandler for MessageTypeAnalyser {
|
||||||
|
|
@ -40,7 +40,7 @@ impl MessageHandler for MessageTypeAnalyser {
|
||||||
impl MessageTypeAnalyser {
|
impl MessageTypeAnalyser {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
MessageTypeAnalyser {
|
MessageTypeAnalyser {
|
||||||
packet_types: Vec::with_capacity(1024)
|
packet_types: Vec::with_capacity(1024),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use bitstream_reader::{BitRead, BitSkip, LittleEndian, ReadError};
|
use bitstream_reader::{BitRead, BitSkip, LittleEndian, ReadError};
|
||||||
|
|
||||||
|
pub use self::messagetypeanalyser::MessageTypeAnalyser;
|
||||||
use crate::demo::gamevent::{GameEventValue, GameEventValueType};
|
use crate::demo::gamevent::{GameEventValue, GameEventValueType};
|
||||||
use crate::demo::header::Header;
|
use crate::demo::header::Header;
|
||||||
use crate::demo::packet::Packet;
|
use crate::demo::packet::Packet;
|
||||||
|
|
@ -8,12 +9,11 @@ pub use crate::demo::parser::analyser::MatchState;
|
||||||
pub use crate::demo::parser::handler::{DemoHandler, MessageHandler};
|
pub use crate::demo::parser::handler::{DemoHandler, MessageHandler};
|
||||||
pub use crate::demo::parser::state::ParserState;
|
pub use crate::demo::parser::state::ParserState;
|
||||||
use crate::Stream;
|
use crate::Stream;
|
||||||
pub use self::messagetypeanalyser::MessageTypeAnalyser;
|
|
||||||
|
|
||||||
mod analyser;
|
mod analyser;
|
||||||
mod handler;
|
mod handler;
|
||||||
mod state;
|
|
||||||
mod messagetypeanalyser;
|
mod messagetypeanalyser;
|
||||||
|
mod state;
|
||||||
|
|
||||||
/// Errors that can occur during parsing
|
/// Errors that can occur during parsing
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -26,8 +26,8 @@ pub enum ParseError {
|
||||||
InvalidMessageType(u8),
|
InvalidMessageType(u8),
|
||||||
/// SendProp type is invalid
|
/// SendProp type is invalid
|
||||||
InvalidSendPropType(u8),
|
InvalidSendPropType(u8),
|
||||||
/// Invalid structure found while creating array SendProp
|
/// Invalid SendProp
|
||||||
InvalidSendPropArray(String),
|
InvalidSendProp(String),
|
||||||
/// Expected amount of data left after parsing an object
|
/// Expected amount of data left after parsing an object
|
||||||
DataRemaining(usize),
|
DataRemaining(usize),
|
||||||
/// String table that was send for update doesn't exist
|
/// String table that was send for update doesn't exist
|
||||||
|
|
@ -95,7 +95,10 @@ impl DemoParser {
|
||||||
Self::parse_with_analyser(stream, Analyser::new())
|
Self::parse_with_analyser(stream, Analyser::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_with_analyser<T: MessageHandler>(mut stream: Stream, analyser: T) -> Result<(Header, T::Output)> {
|
pub fn parse_with_analyser<T: MessageHandler>(
|
||||||
|
mut stream: Stream,
|
||||||
|
analyser: T,
|
||||||
|
) -> Result<(Header, T::Output)> {
|
||||||
let mut handler = DemoHandler::with_analyser(analyser);
|
let mut handler = DemoHandler::with_analyser(analyser);
|
||||||
let header = Header::read(&mut stream)?;
|
let header = Header::read(&mut stream)?;
|
||||||
loop {
|
loop {
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::demo::gamevent::GameEventDefinition;
|
use crate::demo::gamevent::GameEventDefinition;
|
||||||
use crate::demo::message::{Message, MessageType};
|
|
||||||
use crate::demo::message::gameevent::GameEventTypeId;
|
use crate::demo::message::gameevent::GameEventTypeId;
|
||||||
use crate::demo::message::packetentities::EntityId;
|
use crate::demo::message::packetentities::EntityId;
|
||||||
use crate::demo::message::stringtable::StringTableMeta;
|
use crate::demo::message::stringtable::StringTableMeta;
|
||||||
use crate::demo::packet::datatable::{SendTable, ServerClass, SendTableName};
|
use crate::demo::message::{Message, MessageType};
|
||||||
|
use crate::demo::packet::datatable::{SendTable, SendTableName, ServerClass};
|
||||||
use crate::demo::packet::stringtable::StringTableEntry;
|
use crate::demo::packet::stringtable::StringTableEntry;
|
||||||
use crate::demo::parser::analyser::Analyser;
|
use crate::demo::parser::analyser::Analyser;
|
||||||
use crate::demo::parser::handler::MessageHandler;
|
use crate::demo::parser::handler::MessageHandler;
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,13 @@ use bitstream_reader::{BitRead, LittleEndian};
|
||||||
use enumflags2::BitFlags;
|
use enumflags2::BitFlags;
|
||||||
use enumflags2_derive::EnumFlags;
|
use enumflags2_derive::EnumFlags;
|
||||||
|
|
||||||
use crate::{ReadResult, Result, Stream, Parse};
|
use crate::{Parse, ParseError, ReadResult, Result, Stream};
|
||||||
|
|
||||||
use super::packet::datatable::ParseSendTable;
|
use super::packet::datatable::ParseSendTable;
|
||||||
use super::vector::{Vector, VectorXY};
|
use super::vector::{Vector, VectorXY};
|
||||||
|
use crate::demo::message::stringtable::log_base2;
|
||||||
use crate::demo::packet::datatable::SendTableName;
|
use crate::demo::packet::datatable::SendTableName;
|
||||||
|
use std::convert::TryInto;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct SendPropDefinition {
|
pub struct SendPropDefinition {
|
||||||
|
|
@ -42,7 +44,8 @@ impl SendPropDefinition {
|
||||||
///
|
///
|
||||||
/// Note that this is not the owner table
|
/// Note that this is not the owner table
|
||||||
pub fn get_data_table<'a>(&self, tables: &'a [ParseSendTable]) -> Option<&'a ParseSendTable> {
|
pub fn get_data_table<'a>(&self, tables: &'a [ParseSendTable]) -> Option<&'a ParseSendTable> {
|
||||||
self.table_name.as_ref()
|
self.table_name
|
||||||
|
.as_ref()
|
||||||
.and_then(|name| tables.iter().find(|table| table.name == *name))
|
.and_then(|name| tables.iter().find(|table| table.name == *name))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -200,12 +203,173 @@ pub enum SendPropValue {
|
||||||
Array(Vec<SendPropValue>),
|
Array(Vec<SendPropValue>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SendPropValue {
|
||||||
|
pub fn parse(stream: &mut Stream, definition: &SendPropDefinition) -> Result<Self> {
|
||||||
|
match definition.prop_type {
|
||||||
|
SendPropType::Int => Self::read_int(stream, definition).map(SendPropValue::from),
|
||||||
|
SendPropType::Float => Self::read_float(stream, definition).map(SendPropValue::from),
|
||||||
|
SendPropType::String => Self::read_string(stream, definition).map(SendPropValue::from),
|
||||||
|
SendPropType::Vector => Self::read_vector(stream, definition).map(SendPropValue::from),
|
||||||
|
SendPropType::VectorXY => {
|
||||||
|
Self::read_vector_xy(stream, definition).map(SendPropValue::from)
|
||||||
|
}
|
||||||
|
SendPropType::Array => Self::read_array(stream, definition).map(SendPropValue::from),
|
||||||
|
_ => Err(ParseError::InvalidSendProp(
|
||||||
|
"Prop type not allowed in entity".to_string(),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_int(stream: &mut Stream, definition: &SendPropDefinition) -> Result<i32> {
|
||||||
|
if definition.flags.contains(SendPropFlag::NormalVarInt) {
|
||||||
|
read_var_int(stream, !definition.flags.contains(SendPropFlag::Unsigned))
|
||||||
|
.map_err(ParseError::from)
|
||||||
|
} else {
|
||||||
|
if definition.flags.contains(SendPropFlag::Unsigned) {
|
||||||
|
let unsigned: u32 = stream.read()?;
|
||||||
|
unsigned.try_into().map_err(|_| {
|
||||||
|
ParseError::InvalidSendProp("SendProp value out of range".to_string())
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
stream.read().map_err(ParseError::from)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_array(
|
||||||
|
stream: &mut Stream,
|
||||||
|
definition: &SendPropDefinition,
|
||||||
|
) -> Result<Vec<SendPropValue>> {
|
||||||
|
let num_bits = log_base2(definition.element_count.unwrap_or_default());
|
||||||
|
|
||||||
|
let count = stream.read_int(num_bits as usize)?;
|
||||||
|
let mut values = Vec::with_capacity(count);
|
||||||
|
|
||||||
|
for _ in 0..count {
|
||||||
|
let value = Self::parse(
|
||||||
|
stream,
|
||||||
|
definition
|
||||||
|
.array_property
|
||||||
|
.as_ref()
|
||||||
|
.ok_or_else(|| ParseError::InvalidSendProp("Untyped array".to_string()))?,
|
||||||
|
)?;
|
||||||
|
values.push(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(values)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_string(stream: &mut Stream, definition: &SendPropDefinition) -> Result<String> {
|
||||||
|
let length = stream.read_int(9)?;
|
||||||
|
stream.read_sized(length).map_err(ParseError::from)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_vector(stream: &mut Stream, definition: &SendPropDefinition) -> Result<Vector> {
|
||||||
|
Ok(Vector {
|
||||||
|
x: Self::read_float(stream, definition)?,
|
||||||
|
y: Self::read_float(stream, definition)?,
|
||||||
|
z: Self::read_float(stream, definition)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_vector_xy(stream: &mut Stream, definition: &SendPropDefinition) -> Result<VectorXY> {
|
||||||
|
Ok(VectorXY {
|
||||||
|
x: Self::read_float(stream, definition)?,
|
||||||
|
y: Self::read_float(stream, definition)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_float(stream: &mut Stream, definition: &SendPropDefinition) -> Result<f32> {
|
||||||
|
if definition.flags.contains(SendPropFlag::Coord) {
|
||||||
|
read_bit_coord(stream).map_err(ParseError::from)
|
||||||
|
} else if definition.flags.contains(SendPropFlag::CoordMP) {
|
||||||
|
read_bit_coord_mp(stream, false, false).map_err(ParseError::from)
|
||||||
|
} else if definition.flags.contains(SendPropFlag::CoordMPLowPercision) {
|
||||||
|
read_bit_coord_mp(stream, false, true).map_err(ParseError::from)
|
||||||
|
} else if definition.flags.contains(SendPropFlag::CoordMPIntegral) {
|
||||||
|
read_bit_coord_mp(stream, true, false).map_err(ParseError::from)
|
||||||
|
} else if definition.flags.contains(SendPropFlag::NoScale) {
|
||||||
|
stream.read().map_err(ParseError::from)
|
||||||
|
} else if definition.flags.contains(SendPropFlag::NormalVarInt) {
|
||||||
|
read_bit_normal(stream).map_err(ParseError::from)
|
||||||
|
} else {
|
||||||
|
let bit_count = definition
|
||||||
|
.bit_count
|
||||||
|
.ok_or_else(|| ParseError::InvalidSendProp("Unsized float".to_string()))?;
|
||||||
|
let high = definition
|
||||||
|
.high_value
|
||||||
|
.ok_or_else(|| ParseError::InvalidSendProp("Unsized float".to_string()))?;
|
||||||
|
let low = definition
|
||||||
|
.low_value
|
||||||
|
.ok_or_else(|| ParseError::InvalidSendProp("Unsized float".to_string()))?;
|
||||||
|
let raw: u32 = stream.read_int(bit_count as usize)?;
|
||||||
|
let percentage = (raw as f32) * get_frac_factor(bit_count as usize);
|
||||||
|
Ok(low + ((high - low) * percentage))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<i32> for SendPropValue {
|
||||||
|
fn from(value: i32) -> Self {
|
||||||
|
SendPropValue::Integer(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Vector> for SendPropValue {
|
||||||
|
fn from(value: Vector) -> Self {
|
||||||
|
SendPropValue::Vector(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<VectorXY> for SendPropValue {
|
||||||
|
fn from(value: VectorXY) -> Self {
|
||||||
|
SendPropValue::VectorXY(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<f32> for SendPropValue {
|
||||||
|
fn from(value: f32) -> Self {
|
||||||
|
SendPropValue::Float(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<String> for SendPropValue {
|
||||||
|
fn from(value: String) -> Self {
|
||||||
|
SendPropValue::String(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Vec<SendPropValue>> for SendPropValue {
|
||||||
|
fn from(value: Vec<SendPropValue>) -> Self {
|
||||||
|
SendPropValue::Array(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct SendProp {
|
pub struct SendProp {
|
||||||
definition: SendPropDefinition,
|
definition: SendPropDefinition,
|
||||||
value: SendPropValue,
|
value: SendPropValue,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn read_var_int(stream: &mut Stream, signed: bool) -> ReadResult<i32> {
|
||||||
|
let mut result: i32 = 0;
|
||||||
|
|
||||||
|
for i in (0..35).step_by(7) {
|
||||||
|
let byte: u8 = stream.read()?;
|
||||||
|
result |= ((byte & 0x7F) << i) as i32;
|
||||||
|
|
||||||
|
if (byte >> 7) == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if signed {
|
||||||
|
Ok((result >> 1) ^ -(result & 1))
|
||||||
|
} else {
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn read_bit_coord(stream: &mut Stream) -> ReadResult<f32> {
|
pub fn read_bit_coord(stream: &mut Stream) -> ReadResult<f32> {
|
||||||
let has_int = stream.read()?;
|
let has_int = stream.read()?;
|
||||||
let has_frac = stream.read()?;
|
let has_frac = stream.read()?;
|
||||||
|
|
@ -224,3 +388,54 @@ pub fn read_bit_coord(stream: &mut Stream) -> ReadResult<f32> {
|
||||||
0f32
|
0f32
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_frac_factor(bits: usize) -> f32 {
|
||||||
|
1.0 / ((1 << bits) as f32)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_bit_coord_mp(
|
||||||
|
stream: &mut Stream,
|
||||||
|
is_integral: bool,
|
||||||
|
low_precision: bool,
|
||||||
|
) -> ReadResult<f32> {
|
||||||
|
let mut value = 0.0;
|
||||||
|
let mut is_negative = false;
|
||||||
|
|
||||||
|
let in_bounds = stream.read()?;
|
||||||
|
let has_int_val = stream.read()?;
|
||||||
|
|
||||||
|
if is_integral {
|
||||||
|
if has_int_val {
|
||||||
|
is_negative = stream.read()?;
|
||||||
|
|
||||||
|
let int_val = stream.read_sized::<u32>(if in_bounds { 11 } else { 14 })? + 1;
|
||||||
|
value = int_val as f32;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
is_negative = stream.read()?;
|
||||||
|
if has_int_val {
|
||||||
|
let int_val = stream.read_sized::<u32>(if in_bounds { 11 } else { 14 })? + 1;
|
||||||
|
value = int_val as f32;
|
||||||
|
}
|
||||||
|
let frac_bits = if low_precision { 3 } else { 5 };
|
||||||
|
let frac_val: u32 = stream.read_sized(frac_bits)?;
|
||||||
|
value += (frac_val as f32) * get_frac_factor(frac_bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_negative {
|
||||||
|
value = -value;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_bit_normal(stream: &mut Stream) -> ReadResult<f32> {
|
||||||
|
let is_negative = stream.read()?;
|
||||||
|
let frac_val: u16 = stream.read_sized(11)?;
|
||||||
|
let value = (frac_val as f32) * get_frac_factor(11);
|
||||||
|
if is_negative {
|
||||||
|
Ok(-value)
|
||||||
|
} else {
|
||||||
|
Ok(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use bitstream_reader::{BitRead, BitSize};
|
use bitstream_reader::{BitRead, BitSize};
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(BitRead, BitSize, Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq)]
|
#[derive(BitRead, BitSize, Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq)]
|
||||||
pub struct Vector {
|
pub struct Vector {
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,9 @@
|
||||||
pub use bitstream_reader::Result as ReadResult;
|
pub use bitstream_reader::Result as ReadResult;
|
||||||
|
|
||||||
pub use crate::demo::{
|
pub use crate::demo::{
|
||||||
Demo,
|
message::MessageType,
|
||||||
message::MessageType, parser::{
|
parser::{DemoParser, MatchState, MessageTypeAnalyser, Parse, ParseError, ParserState, Result},
|
||||||
DemoParser, MatchState, MessageTypeAnalyser, Parse, ParseError, ParserState, Result,
|
Demo, Stream,
|
||||||
},
|
|
||||||
Stream,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod demo;
|
pub mod demo;
|
||||||
|
|
|
||||||
|
|
@ -2,16 +2,16 @@
|
||||||
#![allow(unused_imports)]
|
#![allow(unused_imports)]
|
||||||
#![allow(unused_variables)]
|
#![allow(unused_variables)]
|
||||||
|
|
||||||
use std::fs;
|
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
|
use std::fs;
|
||||||
|
|
||||||
use tf_demo_parser::{Demo, DemoParser, MatchState, MessageTypeAnalyser, MessageType, ParserState};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
use tf_demo_parser::demo::message::Message;
|
||||||
use tf_demo_parser::demo::packet::datatable::{ParseSendTable, SendTableName};
|
use tf_demo_parser::demo::packet::datatable::{ParseSendTable, SendTableName};
|
||||||
use tf_demo_parser::demo::packet::stringtable::StringTableEntry;
|
use tf_demo_parser::demo::packet::stringtable::StringTableEntry;
|
||||||
use tf_demo_parser::demo::message::Message;
|
|
||||||
use tf_demo_parser::demo::sendprop::SendPropDefinition;
|
|
||||||
use std::collections::{HashMap, HashSet};
|
|
||||||
use tf_demo_parser::demo::parser::MessageHandler;
|
use tf_demo_parser::demo::parser::MessageHandler;
|
||||||
|
use tf_demo_parser::demo::sendprop::SendPropDefinition;
|
||||||
|
use tf_demo_parser::{Demo, DemoParser, MatchState, MessageType, MessageTypeAnalyser, ParserState};
|
||||||
|
|
||||||
pub struct SendPropAnalyser;
|
pub struct SendPropAnalyser;
|
||||||
|
|
||||||
|
|
@ -27,31 +27,43 @@ impl MessageHandler for SendPropAnalyser {
|
||||||
fn handle_string_entry(&mut self, table: &String, _index: usize, entry: &StringTableEntry) {}
|
fn handle_string_entry(&mut self, table: &String, _index: usize, entry: &StringTableEntry) {}
|
||||||
|
|
||||||
fn get_output(self, state: ParserState) -> Self::Output {
|
fn get_output(self, state: ParserState) -> Self::Output {
|
||||||
state.send_tables.into_iter().map(|(_k, v)| ParseSendTable {
|
state
|
||||||
|
.send_tables
|
||||||
|
.into_iter()
|
||||||
|
.map(|(_k, v)| ParseSendTable {
|
||||||
name: v.name,
|
name: v.name,
|
||||||
props: v.props,
|
props: v.props,
|
||||||
needs_decoder: v.needs_decoder
|
needs_decoder: v.needs_decoder,
|
||||||
}).collect()
|
})
|
||||||
|
.collect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn flatten_test(input_file: &str, snapshot_file: &str) {
|
fn flatten_test(input_file: &str, snapshot_file: &str) {
|
||||||
let file = fs::read(input_file).expect("Unable to read file");
|
let file = fs::read(input_file).expect("Unable to read file");
|
||||||
let demo = Demo::new(file);
|
let demo = Demo::new(file);
|
||||||
let (_, send_tables) = DemoParser::parse_with_analyser(demo.get_stream(), SendPropAnalyser).unwrap();
|
let (_, send_tables) =
|
||||||
let flat_props: HashMap<SendTableName, Vec<String>> = send_tables.iter()
|
DemoParser::parse_with_analyser(demo.get_stream(), SendPropAnalyser).unwrap();
|
||||||
|
let flat_props: HashMap<SendTableName, Vec<String>> = send_tables
|
||||||
|
.iter()
|
||||||
.map(|table| {
|
.map(|table| {
|
||||||
(
|
(
|
||||||
table.name.clone(),
|
table.name.clone(),
|
||||||
table.flatten_props(&send_tables)
|
table
|
||||||
|
.flatten_props(&send_tables)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|prop| format!("{}.{}", prop.owner_table, prop.name))
|
.map(|prop| format!("{}.{}", prop.owner_table, prop.name))
|
||||||
.collect()
|
.collect(),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let expected: HashMap<SendTableName, Vec<String>> = serde_json::from_slice(fs::read(snapshot_file).expect("Unable to read file").as_slice()).unwrap();
|
let expected: HashMap<SendTableName, Vec<String>> = serde_json::from_slice(
|
||||||
|
fs::read(snapshot_file)
|
||||||
|
.expect("Unable to read file")
|
||||||
|
.as_slice(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let expected_tables: HashSet<_> = expected.keys().collect();
|
let expected_tables: HashSet<_> = expected.keys().collect();
|
||||||
let actual_tables: HashSet<_> = flat_props.keys().collect();
|
let actual_tables: HashSet<_> = flat_props.keys().collect();
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,42 @@
|
||||||
use std::fs;
|
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
|
use std::fs;
|
||||||
|
|
||||||
use tf_demo_parser::{Demo, DemoParser, MatchState, MessageTypeAnalyser, MessageType};
|
use tf_demo_parser::{Demo, DemoParser, MatchState, MessageType, MessageTypeAnalyser};
|
||||||
|
|
||||||
fn snapshot_test(input_file: &str, snapshot_file: &str) {
|
fn snapshot_test(input_file: &str, snapshot_file: &str) {
|
||||||
let file = fs::read(input_file).expect("Unable to read file");
|
let file = fs::read(input_file).expect("Unable to read file");
|
||||||
let demo = Demo::new(file);
|
let demo = Demo::new(file);
|
||||||
let (_, state) = DemoParser::parse_demo(demo.get_stream()).unwrap();
|
let (_, state) = DemoParser::parse_demo(demo.get_stream()).unwrap();
|
||||||
|
|
||||||
let expected: MatchState = serde_json::from_slice(fs::read(snapshot_file).expect("Unable to read file").as_slice()).unwrap();
|
let expected: MatchState = serde_json::from_slice(
|
||||||
|
fs::read(snapshot_file)
|
||||||
|
.expect("Unable to read file")
|
||||||
|
.as_slice(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
assert_eq!(expected, state);
|
assert_eq!(expected, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_message_types(input_file: &str, snapshot_file: &str) {
|
fn test_message_types(input_file: &str, snapshot_file: &str) {
|
||||||
let file = fs::read(input_file).expect("Unable to read file");
|
let file = fs::read(input_file).expect("Unable to read file");
|
||||||
let demo = Demo::new(file);
|
let demo = Demo::new(file);
|
||||||
let (_, message_types) = DemoParser::parse_with_analyser(demo.get_stream(), MessageTypeAnalyser::new()).unwrap();
|
let (_, message_types) =
|
||||||
|
DemoParser::parse_with_analyser(demo.get_stream(), MessageTypeAnalyser::new()).unwrap();
|
||||||
|
|
||||||
let expected: Vec<MessageType> = serde_json::from_slice(fs::read(snapshot_file).expect("Unable to read file").as_slice()).unwrap();
|
let expected: Vec<MessageType> = serde_json::from_slice(
|
||||||
|
fs::read(snapshot_file)
|
||||||
|
.expect("Unable to read file")
|
||||||
|
.as_slice(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
assert_eq!(expected, message_types);
|
assert_eq!(expected, message_types);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dump_message_types(input_file: &str, snapshot_file: &str) {
|
fn dump_message_types(input_file: &str, snapshot_file: &str) {
|
||||||
let file = fs::read(input_file).expect("Unable to read file");
|
let file = fs::read(input_file).expect("Unable to read file");
|
||||||
let demo = Demo::new(file);
|
let demo = Demo::new(file);
|
||||||
let (_, message_types) = DemoParser::parse_with_analyser(demo.get_stream(), MessageTypeAnalyser::new()).unwrap();
|
let (_, message_types) =
|
||||||
|
DemoParser::parse_with_analyser(demo.get_stream(), MessageTypeAnalyser::new()).unwrap();
|
||||||
|
|
||||||
fs::write(snapshot_file, serde_json::to_vec(&message_types).unwrap()).unwrap();
|
fs::write(snapshot_file, serde_json::to_vec(&message_types).unwrap()).unwrap();
|
||||||
}
|
}
|
||||||
|
|
@ -44,7 +56,6 @@ fn snapshot_test_comp() {
|
||||||
snapshot_test("data/comp.dem", "data/comp.json");
|
snapshot_test("data/comp.dem", "data/comp.json");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn snapshot_test_malformed_cvar() {
|
fn snapshot_test_malformed_cvar() {
|
||||||
snapshot_test("data/malformed_cvar.dem", "data/malformed_cvar.json");
|
snapshot_test("data/malformed_cvar.dem", "data/malformed_cvar.json");
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue