mirror of
https://codeberg.org/demostf/parser.git
synced 2026-06-03 18:24:05 +02:00
entity reading wip
This commit is contained in:
parent
4320f2941c
commit
e90bc53852
11 changed files with 509 additions and 114 deletions
20
Cargo.lock
generated
20
Cargo.lock
generated
|
|
@ -67,20 +67,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitstream_reader"
|
name = "bitstream_reader"
|
||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitstream_reader_derive 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bitstream_reader_derive 0.5.0",
|
||||||
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitstream_reader_derive"
|
name = "bitstream_reader_derive"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
"quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)",
|
"syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"syn_util 0.2.0 (git+https://github.com/icewind1991/syn_util?branch=lit-cast)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -427,6 +426,16 @@ dependencies = [
|
||||||
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn_util"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "git+https://github.com/icewind1991/syn_util?branch=lit-cast#fb81657f975b5a37934b870300112ae6e5528dd2"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "synom"
|
name = "synom"
|
||||||
version = "0.11.3"
|
version = "0.11.3"
|
||||||
|
|
@ -459,7 +468,7 @@ name = "tf-demo-parser"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"better-panic 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"better-panic 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"bitstream_reader 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bitstream_reader 0.5.1",
|
||||||
"enum-primitive-derive 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"enum-primitive-derive 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"enumflags2 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"enumflags2 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"enumflags2_derive 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"enumflags2_derive 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
|
@ -523,8 +532,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
"checksum backtrace-sys 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)" = "82a830b4ef2d1124a711c71d263c5abdc710ef8e907bd508c88be475cebc422b"
|
"checksum backtrace-sys 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)" = "82a830b4ef2d1124a711c71d263c5abdc710ef8e907bd508c88be475cebc422b"
|
||||||
"checksum better-panic 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "64714970eb2081691f85a923b78a490fd7c3e256dcc83c7a5177314586563356"
|
"checksum better-panic 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "64714970eb2081691f85a923b78a490fd7c3e256dcc83c7a5177314586563356"
|
||||||
"checksum bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd"
|
"checksum bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd"
|
||||||
"checksum bitstream_reader 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ce18d8bbe27f4f79e0221775535a640c40760888431a63c22d92e926c5839c77"
|
|
||||||
"checksum bitstream_reader_derive 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7ae1ff36caa3f111504ee053686356bf98bbc1cfe9a5b71e19bbc8294b04bdb1"
|
|
||||||
"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5"
|
"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5"
|
||||||
"checksum cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)" = "ce400c638d48ee0e9ab75aef7997609ec57367ccfe1463f21bf53c3eca67bf46"
|
"checksum cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)" = "ce400c638d48ee0e9ab75aef7997609ec57367ccfe1463f21bf53c3eca67bf46"
|
||||||
"checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33"
|
"checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33"
|
||||||
|
|
@ -569,6 +576,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
"checksum snap 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "95d697d63d44ad8b78b8d235bf85b34022a78af292c8918527c5f0cffdde7f43"
|
"checksum snap 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "95d697d63d44ad8b78b8d235bf85b34022a78af292c8918527c5f0cffdde7f43"
|
||||||
"checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad"
|
"checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad"
|
||||||
"checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5"
|
"checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5"
|
||||||
|
"checksum syn_util 0.2.0 (git+https://github.com/icewind1991/syn_util?branch=lit-cast)" = "<none>"
|
||||||
"checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6"
|
"checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6"
|
||||||
"checksum synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "02353edf96d6e4dc81aea2d8490a7e9db177bf8acb0e951c24940bf866cb313f"
|
"checksum synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "02353edf96d6e4dc81aea2d8490a7e9db177bf8acb0e951c24940bf866cb313f"
|
||||||
"checksum termios 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "72b620c5ea021d75a735c943269bb07d30c9b77d6ac6b236bc8b5c496ef05625"
|
"checksum termios 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "72b620c5ea021d75a735c943269bb07d30c9b77d6ac6b236bc8b5c496ef05625"
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ name = "parse_demo"
|
||||||
path = "src/bin/main.rs"
|
path = "src/bin/main.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bitstream_reader = "0.5"
|
bitstream_reader = { version = "0.5", path = "../../bitbuffer" }
|
||||||
enum-primitive-derive = "0.1"
|
enum-primitive-derive = "0.1"
|
||||||
num-traits = "0.2"
|
num-traits = "0.2"
|
||||||
enumflags2 = "0.5"
|
enumflags2 = "0.5"
|
||||||
|
|
|
||||||
|
|
@ -10846,4 +10846,3 @@ impl GameEvent {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,26 @@
|
||||||
use bitstream_reader::BitRead;
|
use bitstream_reader::{BitRead, BitReadSized, LittleEndian};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::demo::packet::datatable::ServerClass;
|
use crate::demo::message::stringtable::{log_base2, read_var_int};
|
||||||
|
use crate::demo::packet::datatable::{SendTable, SendTableName, ServerClass};
|
||||||
use crate::demo::parser::ParseBitSkip;
|
use crate::demo::parser::ParseBitSkip;
|
||||||
use crate::demo::sendprop::SendProp;
|
use crate::demo::sendprop::{SendProp, SendPropDefinition, SendPropValue};
|
||||||
use crate::{Parse, ParseError, ParserState, Result, Stream};
|
use crate::{MalformedDemoError, Parse, ParseError, ParserState, ReadResult, Result, Stream};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::fmt;
|
||||||
use std::num::ParseIntError;
|
use std::num::ParseIntError;
|
||||||
|
use std::rc::Rc;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
#[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);
|
||||||
|
|
||||||
|
impl fmt::Display for EntityId {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
self.0.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<u32> for EntityId {
|
impl From<u32> for EntityId {
|
||||||
fn from(num: u32) -> Self {
|
fn from(num: u32) -> Self {
|
||||||
EntityId(num)
|
EntityId(num)
|
||||||
|
|
@ -25,8 +35,8 @@ impl FromStr for EntityId {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(BitRead, Clone, Copy, Debug)]
|
#[derive(BitRead, Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
#[discriminant_bits = 3]
|
#[discriminant_bits = 2]
|
||||||
pub enum PVS {
|
pub enum PVS {
|
||||||
Preserve = 0,
|
Preserve = 0,
|
||||||
Leave = 1,
|
Leave = 1,
|
||||||
|
|
@ -36,40 +46,145 @@ pub enum PVS {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct PacketEntity {
|
pub struct PacketEntity {
|
||||||
server_class: ServerClass,
|
pub server_class: Rc<ServerClass>,
|
||||||
entity_index: EntityId,
|
pub entity_index: EntityId,
|
||||||
props: Vec<SendProp>,
|
pub props: Vec<SendProp>,
|
||||||
in_pvs: bool,
|
pub in_pvs: bool,
|
||||||
pvs: PVS,
|
pub pvs: PVS,
|
||||||
serial_number: u32,
|
pub serial_number: u32,
|
||||||
delay: Option<u32>,
|
pub delay: Option<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for PacketEntity {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{}({}) {{\n", self.entity_index, self.server_class.name)?;
|
||||||
|
for child in self.props.iter() {
|
||||||
|
write!(f, "\t{}\n", child)?;
|
||||||
|
}
|
||||||
|
write!(f, "}}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PacketEntity {
|
||||||
|
fn get_prop_by_definition(&mut self, definition: &SendPropDefinition) -> Option<&mut SendProp> {
|
||||||
|
self.props
|
||||||
|
.iter_mut()
|
||||||
|
.find(|prop| prop.definition.eq(definition))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn apply_update(&mut self, props: Vec<SendProp>) {
|
||||||
|
for prop in props {
|
||||||
|
match self.get_prop_by_definition(&prop.definition) {
|
||||||
|
Some(existing_prop) => existing_prop.value = prop.value,
|
||||||
|
None => self.props.push(prop),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_bit_var<T: BitReadSized<LittleEndian>>(stream: &mut Stream) -> ReadResult<T> {
|
||||||
|
let ty: u8 = stream.read_sized(2)?;
|
||||||
|
|
||||||
|
let bits = match ty {
|
||||||
|
0 => 4,
|
||||||
|
1 => 8,
|
||||||
|
2 => 12,
|
||||||
|
3 => 32,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
stream.read_sized(bits)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct PacketEntitiesMessage {
|
pub struct PacketEntitiesMessage {
|
||||||
entities: Vec<PacketEntity>,
|
pub entities: Vec<PacketEntity>,
|
||||||
removed_entities: Vec<EntityId>,
|
pub removed_entities: Vec<EntityId>,
|
||||||
max_entries: u16,
|
pub max_entries: u16,
|
||||||
delta: Option<u32>,
|
pub delta: Option<u32>,
|
||||||
base_line: u8,
|
pub base_line: u8,
|
||||||
updated_base_line: bool,
|
pub updated_base_line: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_send_table<'a>(state: &'a ParserState, table: &SendTableName) -> Result<&'a SendTable> {
|
||||||
|
state
|
||||||
|
.send_tables
|
||||||
|
.get(table)
|
||||||
|
.ok_or_else(|| MalformedDemoError::UnknownSendTable(table.clone()).into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_entity_for_update(
|
||||||
|
state: &ParserState,
|
||||||
|
entity_index: EntityId,
|
||||||
|
pvs: PVS,
|
||||||
|
) -> Result<PacketEntity> {
|
||||||
|
let server_class = state
|
||||||
|
.entity_classes
|
||||||
|
.get(&entity_index)
|
||||||
|
.ok_or_else(|| MalformedDemoError::UnknownEntity(entity_index))?;
|
||||||
|
|
||||||
|
Ok(PacketEntity {
|
||||||
|
server_class: Rc::clone(server_class),
|
||||||
|
entity_index,
|
||||||
|
props: Vec::new(),
|
||||||
|
in_pvs: false,
|
||||||
|
pvs,
|
||||||
|
serial_number: 0,
|
||||||
|
delay: None,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parse for PacketEntitiesMessage {
|
impl Parse for PacketEntitiesMessage {
|
||||||
fn parse(stream: &mut Stream, _state: &ParserState) -> Result<Self> {
|
fn parse(stream: &mut Stream, state: &ParserState) -> Result<Self> {
|
||||||
let max_entries = stream.read_sized(11)?;
|
let max_entries = stream.read_sized(11)?;
|
||||||
let delta = stream.read()?;
|
let delta: Option<u32> = stream.read()?;
|
||||||
let base_line = stream.read_sized(1)?;
|
let base_line = stream.read_sized(1)?;
|
||||||
let _updated_entries: u16 = stream.read_sized(11)?;
|
let updated_entries: u16 = stream.read_sized(11)?;
|
||||||
let length: u32 = stream.read_sized(20)?;
|
let length: u32 = stream.read_sized(20)?;
|
||||||
let updated_base_line = stream.read()?;
|
let updated_base_line = stream.read()?;
|
||||||
let _data = stream.read_bits(length as usize)?;
|
let mut data = stream.read_bits(length as usize)?;
|
||||||
|
|
||||||
// TODO
|
let mut entities = Vec::with_capacity(updated_entries as usize);
|
||||||
|
let mut removed_entities = Vec::new();
|
||||||
|
|
||||||
|
let mut last_index: i32 = -1;
|
||||||
|
|
||||||
|
for _ in 0..updated_entries {
|
||||||
|
let diff: u32 = read_bit_var(&mut data)?;
|
||||||
|
last_index += diff as i32 + 1;
|
||||||
|
let entity_index = EntityId::from(last_index as u32);
|
||||||
|
|
||||||
|
let pvs = data.read()?;
|
||||||
|
if pvs == PVS::Enter {
|
||||||
|
let mut entity =
|
||||||
|
Self::read_enter(&mut data, entity_index, state, base_line as usize)?;
|
||||||
|
let send_table = get_send_table(state, &entity.server_class.data_table)?;
|
||||||
|
let updated_props = Self::read_update(&mut data, send_table)?;
|
||||||
|
entity.apply_update(updated_props);
|
||||||
|
|
||||||
|
entities.push(entity);
|
||||||
|
} else if pvs == PVS::Preserve {
|
||||||
|
let mut entity = get_entity_for_update(state, entity_index, pvs)?;
|
||||||
|
let send_table = get_send_table(state, &entity.server_class.data_table)?;
|
||||||
|
|
||||||
|
let updated_props = Self::read_update(&mut data, send_table)?;
|
||||||
|
entity.props = updated_props;
|
||||||
|
|
||||||
|
entities.push(entity);
|
||||||
|
} else if state.entity_classes.contains_key(&entity_index) {
|
||||||
|
let entity = get_entity_for_update(state, entity_index, pvs)?;
|
||||||
|
entities.push(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if delta.is_some() {
|
||||||
|
while data.read()? {
|
||||||
|
removed_entities.push(data.read_sized::<u32>(11)?.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(PacketEntitiesMessage {
|
Ok(PacketEntitiesMessage {
|
||||||
entities: Vec::new(),
|
entities,
|
||||||
removed_entities: Vec::new(),
|
removed_entities,
|
||||||
max_entries,
|
max_entries,
|
||||||
delta,
|
delta,
|
||||||
base_line,
|
base_line,
|
||||||
|
|
@ -78,6 +193,78 @@ impl Parse for PacketEntitiesMessage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PacketEntitiesMessage {
|
||||||
|
fn read_enter(
|
||||||
|
stream: &mut Stream,
|
||||||
|
entity_index: EntityId,
|
||||||
|
state: &ParserState,
|
||||||
|
baseline_index: usize,
|
||||||
|
) -> Result<PacketEntity> {
|
||||||
|
let bits = log_base2(state.server_classes.len()) + 1;
|
||||||
|
let class_index = stream.read_sized::<u16>(bits as usize)? as usize;
|
||||||
|
let server_class = state
|
||||||
|
.server_classes
|
||||||
|
.get(class_index)
|
||||||
|
.ok_or_else(|| ParseError::from(MalformedDemoError::UnknownServerClass(class_index)))?;
|
||||||
|
|
||||||
|
let serial = stream.read_sized(10)?;
|
||||||
|
let send_table = state
|
||||||
|
.send_tables
|
||||||
|
.get(&server_class.data_table)
|
||||||
|
.ok_or_else(|| MalformedDemoError::UnknownSendTable(server_class.data_table.clone()))?;
|
||||||
|
|
||||||
|
let props = match state.instance_baselines[baseline_index].get(&entity_index) {
|
||||||
|
Some(baseline) => baseline.clone(),
|
||||||
|
None => match state.static_baselines.get(&server_class.id) {
|
||||||
|
Some(static_baseline) => {
|
||||||
|
state.get_static_baseline((class_index as u16).into(), send_table)?
|
||||||
|
}
|
||||||
|
None => Vec::new(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(PacketEntity {
|
||||||
|
server_class: Rc::clone(server_class),
|
||||||
|
entity_index,
|
||||||
|
props,
|
||||||
|
in_pvs: true,
|
||||||
|
pvs: PVS::Enter,
|
||||||
|
serial_number: serial,
|
||||||
|
delay: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_update(stream: &mut Stream, send_table: &SendTable) -> Result<Vec<SendProp>> {
|
||||||
|
let mut index = -1;
|
||||||
|
//let mut props: HashMap<i32, SendProp> = HashMap::new();
|
||||||
|
let mut props = Vec::with_capacity(8);
|
||||||
|
|
||||||
|
while stream.read()? {
|
||||||
|
let diff: u32 = read_bit_var(stream)?;
|
||||||
|
index += (diff as i32) + 1;
|
||||||
|
|
||||||
|
match send_table.flattened_props.get(index as usize) {
|
||||||
|
Some(definition) => {
|
||||||
|
let value = SendPropValue::parse(stream, definition)?;
|
||||||
|
props.push(SendProp {
|
||||||
|
definition: definition.clone(),
|
||||||
|
value,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
return Err(ParseError::from(MalformedDemoError::PropIndexOutOfBounds {
|
||||||
|
index,
|
||||||
|
prop_count: send_table.flattened_props.len(),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(props)
|
||||||
|
//Ok(props.into_iter().map(|(_, prop)| prop).collect())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ParseBitSkip for PacketEntitiesMessage {
|
impl ParseBitSkip for PacketEntitiesMessage {
|
||||||
fn parse_skip(stream: &mut Stream) -> Result<()> {
|
fn parse_skip(stream: &mut Stream) -> Result<()> {
|
||||||
let _: u16 = stream.read_sized(11)?;
|
let _: u16 = stream.read_sized(11)?;
|
||||||
|
|
@ -89,13 +276,3 @@ impl ParseBitSkip for PacketEntitiesMessage {
|
||||||
stream.skip_bits(length as usize).map_err(ParseError::from)
|
stream.skip_bits(length as usize).map_err(ParseError::from)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct EntityUpdate {
|
|
||||||
props: Vec<SendProp>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Parse for EntityUpdate {
|
|
||||||
fn parse(stream: &mut Stream, _state: &ParserState) -> Result<Self> {
|
|
||||||
Ok(EntityUpdate { props: Vec::new() })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -4,19 +4,39 @@ use crate::demo::parser::MalformedSendPropDefinitionError;
|
||||||
use crate::demo::sendprop::{SendPropDefinition, SendPropFlag, SendPropName, SendPropType};
|
use crate::demo::sendprop::{SendPropDefinition, SendPropFlag, SendPropName, SendPropType};
|
||||||
use crate::{MalformedDemoError, Parse, ParseError, ParserState, ReadResult, Result, Stream};
|
use crate::{MalformedDemoError, Parse, ParseError, ParserState, ReadResult, Result, Stream};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::borrow::Borrow;
|
||||||
use std::cell::{Cell, RefCell};
|
use std::cell::{Cell, RefCell};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::num::ParseIntError;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
#[derive(BitRead, Debug)]
|
#[derive(BitRead, Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub struct ServerClass {
|
pub struct ClassId(u16);
|
||||||
pub id: u16,
|
|
||||||
pub name: String,
|
impl FromStr for ClassId {
|
||||||
pub data_table: String,
|
type Err = ParseIntError;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
|
||||||
|
u16::from_str(s).map(|num| ClassId(num))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
|
impl From<u16> for ClassId {
|
||||||
|
fn from(int: u16) -> Self {
|
||||||
|
ClassId(int)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(BitRead, Debug, Clone)]
|
||||||
|
pub struct ServerClass {
|
||||||
|
pub id: ClassId,
|
||||||
|
pub name: String,
|
||||||
|
pub data_table: SendTableName,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(BitRead, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
|
||||||
pub struct SendTableName(Rc<String>);
|
pub struct SendTableName(Rc<String>);
|
||||||
|
|
||||||
impl Clone for SendTableName {
|
impl Clone for SendTableName {
|
||||||
|
|
@ -37,12 +57,6 @@ impl From<String> for SendTableName {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BitRead<LittleEndian> for SendTableName {
|
|
||||||
fn read(stream: &mut Stream) -> ReadResult<Self> {
|
|
||||||
String::read(stream).map(SendTableName::from)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ParseSendTable {
|
pub struct ParseSendTable {
|
||||||
pub name: SendTableName,
|
pub name: SendTableName,
|
||||||
|
|
@ -184,7 +198,7 @@ pub struct SendTable {
|
||||||
pub struct DataTablePacket {
|
pub struct DataTablePacket {
|
||||||
pub tick: u32,
|
pub tick: u32,
|
||||||
pub tables: Vec<SendTable>,
|
pub tables: Vec<SendTable>,
|
||||||
pub server_classes: Vec<ServerClass>,
|
pub server_classes: Vec<Rc<ServerClass>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parse for DataTablePacket {
|
impl Parse for DataTablePacket {
|
||||||
|
|
|
||||||
|
|
@ -233,10 +233,6 @@ impl Analyser {
|
||||||
Self::default()
|
Self::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_message_types(&self) -> Vec<MessageType> {
|
|
||||||
vec![MessageType::GameEvent, MessageType::UserMessage]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_user_message(&mut self, message: UserMessage, tick: u32) {
|
fn handle_user_message(&mut self, message: UserMessage, tick: u32) {
|
||||||
if let UserMessage::SayText2(text_message) = message {
|
if let UserMessage::SayText2(text_message) = message {
|
||||||
if text_message.kind == ChatMessageKind::NameChange {
|
if text_message.kind == ChatMessageKind::NameChange {
|
||||||
|
|
@ -271,7 +267,7 @@ impl Analyser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_user_info(&mut self, text: &String, mut data: Stream) -> ReadResult<()> {
|
fn parse_user_info(&mut self, text: &str, mut data: Stream) -> ReadResult<()> {
|
||||||
let name: String = data.read_sized(32).unwrap_or("Malformed Name".into());
|
let name: String = data.read_sized(32).unwrap_or("Malformed Name".into());
|
||||||
let user_id = data.read::<u32>()?.into();
|
let user_id = data.read::<u32>()?.into();
|
||||||
let steam_id: String = data.read()?;
|
let steam_id: String = data.read()?;
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ use crate::demo::packet::stringtable::{StringTable, StringTableEntry};
|
||||||
use crate::demo::packet::Packet;
|
use crate::demo::packet::Packet;
|
||||||
use crate::demo::parser::analyser::Analyser;
|
use crate::demo::parser::analyser::Analyser;
|
||||||
use crate::ParserState;
|
use crate::ParserState;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub trait MessageHandler {
|
pub trait MessageHandler {
|
||||||
type Output;
|
type Output;
|
||||||
|
|
@ -95,7 +96,11 @@ impl<T: MessageHandler> DemoHandler<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_data_table(&mut self, send_tables: Vec<SendTable>, server_classes: Vec<ServerClass>) {
|
fn handle_data_table(
|
||||||
|
&mut self,
|
||||||
|
send_tables: Vec<SendTable>,
|
||||||
|
server_classes: Vec<Rc<ServerClass>>,
|
||||||
|
) {
|
||||||
self.state_handler
|
self.state_handler
|
||||||
.handle_data_table(send_tables, server_classes);
|
.handle_data_table(send_tables, server_classes);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@ use bitstream_reader::{BitRead, BitSkip, FromUtf8Error, LittleEndian, ReadError}
|
||||||
pub use self::messagetypeanalyser::MessageTypeAnalyser;
|
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::message::packetentities::EntityId;
|
||||||
|
use crate::demo::packet::datatable::SendTableName;
|
||||||
use crate::demo::packet::Packet;
|
use crate::demo::packet::Packet;
|
||||||
use crate::demo::parser::analyser::Analyser;
|
use crate::demo::parser::analyser::Analyser;
|
||||||
pub use crate::demo::parser::analyser::MatchState;
|
pub use crate::demo::parser::analyser::MatchState;
|
||||||
|
|
@ -78,6 +80,18 @@ pub enum MalformedDemoError {
|
||||||
name: &'static str,
|
name: &'static str,
|
||||||
found_type: GameEventValueType,
|
found_type: GameEventValueType,
|
||||||
},
|
},
|
||||||
|
#[error(display = "An entity with an unknown server class({}) was read", _0)]
|
||||||
|
UnknownServerClass(usize),
|
||||||
|
#[error(display = "Unknown send table: {}", _0)]
|
||||||
|
UnknownSendTable(SendTableName),
|
||||||
|
#[error(
|
||||||
|
display = "Property index out of bounds, got {} but only {} props exist",
|
||||||
|
_0,
|
||||||
|
_1
|
||||||
|
)]
|
||||||
|
PropIndexOutOfBounds { index: i32, prop_count: usize },
|
||||||
|
#[error(display = "An attempt was made to update an unknown entity: {}", _0)]
|
||||||
|
UnknownEntity(EntityId),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,17 @@ use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::demo::gamevent::GameEventDefinition;
|
use crate::demo::gamevent::GameEventDefinition;
|
||||||
use crate::demo::message::gameevent::GameEventTypeId;
|
use crate::demo::message::gameevent::GameEventTypeId;
|
||||||
use crate::demo::message::packetentities::EntityId;
|
use crate::demo::message::packetentities::{EntityId, PacketEntitiesMessage, PVS};
|
||||||
use crate::demo::message::stringtable::StringTableMeta;
|
use crate::demo::message::stringtable::StringTableMeta;
|
||||||
use crate::demo::message::{Message, MessageType};
|
use crate::demo::message::{Message, MessageType};
|
||||||
use crate::demo::packet::datatable::{SendTable, SendTableName, ServerClass};
|
use crate::demo::packet::datatable::{ClassId, 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;
|
||||||
use crate::demo::sendprop::SendProp;
|
use crate::demo::sendprop::SendProp;
|
||||||
use crate::Stream;
|
use crate::{Result, Stream};
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct DemoMeta {
|
pub struct DemoMeta {
|
||||||
|
|
@ -20,30 +22,30 @@ pub struct DemoMeta {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ParserState {
|
pub struct ParserState {
|
||||||
pub static_baselines: HashMap<u32, StaticBaseline>,
|
pub static_baselines: HashMap<ClassId, StaticBaseline>,
|
||||||
|
pub parsed_static_baselines: RefCell<HashMap<ClassId, Vec<SendProp>>>,
|
||||||
pub event_definitions: HashMap<GameEventTypeId, GameEventDefinition>,
|
pub event_definitions: HashMap<GameEventTypeId, GameEventDefinition>,
|
||||||
pub string_tables: Vec<StringTableMeta>,
|
pub string_tables: Vec<StringTableMeta>,
|
||||||
pub entity_classes: HashMap<EntityId, ServerClass>,
|
pub entity_classes: HashMap<EntityId, Rc<ServerClass>>,
|
||||||
pub send_tables: HashMap<SendTableName, SendTable>,
|
pub send_tables: HashMap<SendTableName, SendTable>,
|
||||||
pub server_classes: Vec<ServerClass>,
|
pub server_classes: Vec<Rc<ServerClass>>,
|
||||||
pub instance_baselines: [HashMap<EntityId, Vec<SendProp>>; 2],
|
pub instance_baselines: [HashMap<EntityId, Vec<SendProp>>; 2],
|
||||||
pub demo_meta: DemoMeta,
|
pub demo_meta: DemoMeta,
|
||||||
analyser_handles: fn(message_type: MessageType) -> bool,
|
analyser_handles: fn(message_type: MessageType) -> bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct StaticBaseline {
|
pub struct StaticBaseline {
|
||||||
class_id: u32,
|
pub class_id: ClassId,
|
||||||
raw: Stream,
|
pub raw: Stream,
|
||||||
parsed: Option<Vec<SendProp>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StaticBaseline {
|
impl StaticBaseline {
|
||||||
fn new(class_id: u32, raw: Stream) -> Self {
|
fn new(class_id: ClassId, raw: Stream) -> Self {
|
||||||
StaticBaseline {
|
StaticBaseline { class_id, raw }
|
||||||
class_id,
|
}
|
||||||
raw,
|
|
||||||
parsed: None,
|
pub fn parse(&self, send_table: &SendTable) -> Result<Vec<SendProp>> {
|
||||||
}
|
PacketEntitiesMessage::read_update(&mut self.raw.clone(), send_table)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -51,6 +53,7 @@ impl ParserState {
|
||||||
pub fn new(analyser_handles: fn(message_type: MessageType) -> bool) -> Self {
|
pub fn new(analyser_handles: fn(message_type: MessageType) -> bool) -> Self {
|
||||||
ParserState {
|
ParserState {
|
||||||
static_baselines: HashMap::new(),
|
static_baselines: HashMap::new(),
|
||||||
|
parsed_static_baselines: RefCell::new(HashMap::new()),
|
||||||
event_definitions: HashMap::new(),
|
event_definitions: HashMap::new(),
|
||||||
string_tables: Vec::new(),
|
string_tables: Vec::new(),
|
||||||
entity_classes: HashMap::new(),
|
entity_classes: HashMap::new(),
|
||||||
|
|
@ -62,10 +65,28 @@ impl ParserState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_static_baseline(
|
||||||
|
&self,
|
||||||
|
class_id: ClassId,
|
||||||
|
send_table: &SendTable,
|
||||||
|
) -> Result<Vec<SendProp>> {
|
||||||
|
let mut cached = self.parsed_static_baselines.borrow_mut();
|
||||||
|
Ok(match cached.get(&class_id) {
|
||||||
|
Some(props) => props.clone(),
|
||||||
|
None => match self.static_baselines.get(&class_id) {
|
||||||
|
Some(static_baseline) => {
|
||||||
|
let props = static_baseline.parse(send_table)?;
|
||||||
|
cached.entry(class_id).or_insert(props).clone()
|
||||||
|
}
|
||||||
|
None => Vec::new(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn handle_data_table(
|
pub fn handle_data_table(
|
||||||
&mut self,
|
&mut self,
|
||||||
send_tables: Vec<SendTable>,
|
send_tables: Vec<SendTable>,
|
||||||
server_classes: Vec<ServerClass>,
|
server_classes: Vec<Rc<ServerClass>>,
|
||||||
) {
|
) {
|
||||||
for table in send_tables {
|
for table in send_tables {
|
||||||
self.send_tables.insert(table.name.clone(), table);
|
self.send_tables.insert(table.name.clone(), table);
|
||||||
|
|
@ -90,6 +111,7 @@ impl MessageHandler for ParserState {
|
||||||
MessageType::ServerInfo
|
MessageType::ServerInfo
|
||||||
| MessageType::GameEventList
|
| MessageType::GameEventList
|
||||||
| MessageType::CreateStringTable
|
| MessageType::CreateStringTable
|
||||||
|
| MessageType::PacketEntities // TODO entity tracking is only needed when the analyser uses entities
|
||||||
| MessageType::UpdateStringTable => true,
|
| MessageType::UpdateStringTable => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
|
|
@ -105,22 +127,43 @@ impl MessageHandler for ParserState {
|
||||||
Message::GameEventList(message) => {
|
Message::GameEventList(message) => {
|
||||||
self.event_definitions = message.event_list;
|
self.event_definitions = message.event_list;
|
||||||
}
|
}
|
||||||
|
Message::PacketEntities(message) => {
|
||||||
|
if message.updated_base_line {
|
||||||
|
let old_index = message.base_line as usize;
|
||||||
|
let new_index = 1 - old_index;
|
||||||
|
self.instance_baselines.swap(0, 1);
|
||||||
|
//self.instance_baselines[new_index] = self.instance_baselines[new_index].clone();
|
||||||
|
|
||||||
|
for entity in message.entities.iter() {
|
||||||
|
self.instance_baselines[new_index]
|
||||||
|
.insert(entity.entity_index, entity.props.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for removed in message.removed_entities.iter() {
|
||||||
|
self.entity_classes.remove(removed);
|
||||||
|
}
|
||||||
|
|
||||||
|
for entity in message.entities.iter() {
|
||||||
|
if entity.pvs == PVS::Delete {
|
||||||
|
self.entity_classes.remove(&entity.entity_index);
|
||||||
|
}
|
||||||
|
self.entity_classes
|
||||||
|
.insert(entity.entity_index, Rc::clone(&entity.server_class));
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_string_entry(&mut self, table: &String, _index: usize, entry: &StringTableEntry) {
|
fn handle_string_entry(&mut self, table: &String, _index: usize, entry: &StringTableEntry) {
|
||||||
match table.as_str() {
|
match table.as_str() {
|
||||||
"instancebaseline" => match &entry.extra_data {
|
"instancebaseline" => {
|
||||||
Some(extra) => match entry.text().parse::<u32>() {
|
if let (Some(extra), Ok(class_id)) = (&entry.extra_data, entry.text().parse()) {
|
||||||
Ok(class_id) => {
|
let baseline = StaticBaseline::new(class_id, extra.data.clone());
|
||||||
let baseline = StaticBaseline::new(class_id, extra.data.clone());
|
self.static_baselines.insert(class_id, baseline);
|
||||||
self.static_baselines.insert(class_id, baseline);
|
}
|
||||||
}
|
}
|
||||||
Err(_) => {}
|
|
||||||
},
|
|
||||||
_ => {}
|
|
||||||
},
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,9 +13,15 @@ use std::convert::TryInto;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Hash, Debug)]
|
#[derive(BitRead, PartialEq, Eq, Hash, Debug)]
|
||||||
pub struct SendPropName(Rc<String>);
|
pub struct SendPropName(Rc<String>);
|
||||||
|
|
||||||
|
impl PartialEq<&str> for SendPropName {
|
||||||
|
fn eq(&self, other: &&str) -> bool {
|
||||||
|
self.0.as_str() == *other
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Clone for SendPropName {
|
impl Clone for SendPropName {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
SendPropName(Rc::clone(&self.0))
|
SendPropName(Rc::clone(&self.0))
|
||||||
|
|
@ -34,13 +40,7 @@ impl From<String> for SendPropName {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BitRead<LittleEndian> for SendPropName {
|
#[derive(Debug, Clone)]
|
||||||
fn read(stream: &mut Stream) -> ReadResult<Self> {
|
|
||||||
String::read(stream).map(SendPropName::from)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
pub struct SendPropDefinition {
|
pub struct SendPropDefinition {
|
||||||
pub prop_type: SendPropType,
|
pub prop_type: SendPropType,
|
||||||
pub name: SendPropName,
|
pub name: SendPropName,
|
||||||
|
|
@ -54,6 +54,76 @@ pub struct SendPropDefinition {
|
||||||
pub array_property: Option<Box<SendPropDefinition>>,
|
pub array_property: Option<Box<SendPropDefinition>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PartialEq for SendPropDefinition {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.owner_table == other.owner_table && self.name == other.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for SendPropDefinition {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self.prop_type {
|
||||||
|
SendPropType::Vector | SendPropType::VectorXY => write!(
|
||||||
|
f,
|
||||||
|
"{}::{}({})(flags: {}, low: {}, high: {}, bits: {})",
|
||||||
|
self.owner_table,
|
||||||
|
self.name,
|
||||||
|
self.prop_type,
|
||||||
|
self.flags,
|
||||||
|
self.low_value.unwrap_or_default(),
|
||||||
|
self.high_value.unwrap_or_default(),
|
||||||
|
self.bit_count.unwrap_or(96) / 3
|
||||||
|
),
|
||||||
|
SendPropType::Float => write!(
|
||||||
|
f,
|
||||||
|
"{}::{}({})(flags: {}, low: {}, high: {}, bits: {})",
|
||||||
|
self.owner_table,
|
||||||
|
self.name,
|
||||||
|
self.prop_type,
|
||||||
|
self.flags,
|
||||||
|
self.low_value.unwrap_or_default(),
|
||||||
|
self.high_value.unwrap_or_default(),
|
||||||
|
self.bit_count.unwrap_or(32)
|
||||||
|
),
|
||||||
|
SendPropType::Int => write!(
|
||||||
|
f,
|
||||||
|
"{}::{}({})(flags: {}, bits: {})",
|
||||||
|
self.owner_table,
|
||||||
|
self.name,
|
||||||
|
self.prop_type,
|
||||||
|
self.flags,
|
||||||
|
self.bit_count.unwrap_or(32)
|
||||||
|
),
|
||||||
|
SendPropType::String => {
|
||||||
|
write!(f, "{}::{}({})", self.owner_table, self.name, self.prop_type)
|
||||||
|
}
|
||||||
|
SendPropType::Array => match &self.array_property {
|
||||||
|
Some(array_prop) => write!(
|
||||||
|
f,
|
||||||
|
"{}::{}([{}({})] * {})",
|
||||||
|
self.owner_table,
|
||||||
|
self.name,
|
||||||
|
array_prop.prop_type,
|
||||||
|
array_prop.flags,
|
||||||
|
self.element_count.unwrap_or_default(),
|
||||||
|
),
|
||||||
|
None => write!(f, "{}(Malformed array)", self.name),
|
||||||
|
},
|
||||||
|
SendPropType::DataTable => match &self.table_name {
|
||||||
|
Some(sub_table) => write!(
|
||||||
|
f,
|
||||||
|
"{}::{}(DataTable = {})",
|
||||||
|
self.owner_table, self.name, sub_table
|
||||||
|
),
|
||||||
|
None => write!(f, "{}(Malformed DataTable)", self.name),
|
||||||
|
},
|
||||||
|
SendPropType::NumSendPropTypes => {
|
||||||
|
write!(f, "{}::{}(NumSendPropTypes)", self.owner_table, self.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl SendPropDefinition {
|
impl SendPropDefinition {
|
||||||
pub fn with_array_property(self, array_property: Self) -> Self {
|
pub fn with_array_property(self, array_property: Self) -> Self {
|
||||||
SendPropDefinition {
|
SendPropDefinition {
|
||||||
|
|
@ -70,7 +140,7 @@ impl SendPropDefinition {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the refered data table
|
/// Get the referred data table
|
||||||
///
|
///
|
||||||
/// 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> {
|
||||||
|
|
@ -156,6 +226,21 @@ pub enum SendPropType {
|
||||||
NumSendPropTypes = 7,
|
NumSendPropTypes = 7,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for SendPropType {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
SendPropType::Int => write!(f, "Int"),
|
||||||
|
SendPropType::Float => write!(f, "Float"),
|
||||||
|
SendPropType::Vector => write!(f, "Vector"),
|
||||||
|
SendPropType::VectorXY => write!(f, "VectorXY"),
|
||||||
|
SendPropType::String => write!(f, "String"),
|
||||||
|
SendPropType::Array => write!(f, "Array"),
|
||||||
|
SendPropType::DataTable => write!(f, "DataTable"),
|
||||||
|
SendPropType::NumSendPropTypes => write!(f, "NumSendPropTypes"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(EnumFlags, Copy, Clone, PartialEq, Debug)]
|
#[derive(EnumFlags, Copy, Clone, PartialEq, Debug)]
|
||||||
#[repr(u16)]
|
#[repr(u16)]
|
||||||
pub enum SendPropFlag {
|
pub enum SendPropFlag {
|
||||||
|
|
@ -203,6 +288,18 @@ pub enum SendPropFlag {
|
||||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
pub struct SendPropFlags(BitFlags<SendPropFlag>);
|
pub struct SendPropFlags(BitFlags<SendPropFlag>);
|
||||||
|
|
||||||
|
impl fmt::Display for SendPropFlags {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
let debug = format!("{:?}", self.0);
|
||||||
|
let flags: String = debug
|
||||||
|
.chars()
|
||||||
|
.skip_while(|c| *c != '[')
|
||||||
|
.take_while(|c| *c != ')')
|
||||||
|
.collect();
|
||||||
|
write!(f, "{}", flags)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl SendPropFlags {
|
impl SendPropFlags {
|
||||||
pub fn contains(self, other: SendPropFlag) -> bool {
|
pub fn contains(self, other: SendPropFlag) -> bool {
|
||||||
self.0.contains(other)
|
self.0.contains(other)
|
||||||
|
|
@ -226,6 +323,25 @@ pub enum SendPropValue {
|
||||||
Array(Vec<SendPropValue>),
|
Array(Vec<SendPropValue>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for SendPropValue {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
SendPropValue::Vector(vector) => vector.fmt(f),
|
||||||
|
SendPropValue::VectorXY(vector) => vector.fmt(f),
|
||||||
|
SendPropValue::Integer(int) => int.fmt(f),
|
||||||
|
SendPropValue::Float(float) => float.fmt(f),
|
||||||
|
SendPropValue::String(string) => string.fmt(f),
|
||||||
|
SendPropValue::Array(array) => {
|
||||||
|
write!(f, "[")?;
|
||||||
|
for child in array {
|
||||||
|
write!(f, "{}", child)?;
|
||||||
|
}
|
||||||
|
write!(f, "]")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl SendPropValue {
|
impl SendPropValue {
|
||||||
pub fn parse(stream: &mut Stream, definition: &SendPropDefinition) -> Result<Self> {
|
pub fn parse(stream: &mut Stream, definition: &SendPropDefinition) -> Result<Self> {
|
||||||
match definition.prop_type {
|
match definition.prop_type {
|
||||||
|
|
@ -247,12 +363,14 @@ impl SendPropValue {
|
||||||
.map_err(ParseError::from)
|
.map_err(ParseError::from)
|
||||||
} else {
|
} else {
|
||||||
if definition.flags.contains(SendPropFlag::Unsigned) {
|
if definition.flags.contains(SendPropFlag::Unsigned) {
|
||||||
let unsigned: u32 = stream.read()?;
|
let unsigned: u32 =
|
||||||
unsigned
|
stream.read_sized(definition.bit_count.unwrap_or(32) as usize)?;
|
||||||
.try_into()
|
const MAX: u32 = std::i32::MAX as u32;
|
||||||
.map_err(|_| MalformedSendPropDefinitionError::OutOfRange.into())
|
Ok((unsigned & MAX) as i32)
|
||||||
} else {
|
} else {
|
||||||
stream.read().map_err(ParseError::from)
|
stream
|
||||||
|
.read_int(definition.bit_count.unwrap_or(32) as usize)
|
||||||
|
.map_err(ParseError::from)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -265,20 +383,18 @@ impl SendPropValue {
|
||||||
definition
|
definition
|
||||||
.element_count
|
.element_count
|
||||||
.ok_or(MalformedSendPropDefinitionError::UnsizedArray)?,
|
.ok_or(MalformedSendPropDefinitionError::UnsizedArray)?,
|
||||||
);
|
) + 1;
|
||||||
|
|
||||||
let count = stream.read_int(num_bits as usize)?;
|
let count = stream.read_int(num_bits as usize)?;
|
||||||
let mut values = Vec::with_capacity(count);
|
let mut values = Vec::with_capacity(count);
|
||||||
|
|
||||||
|
let child_definition = definition
|
||||||
|
.array_property
|
||||||
|
.as_ref()
|
||||||
|
.ok_or(MalformedSendPropDefinitionError::UntypedArray)?;
|
||||||
|
|
||||||
for _ in 0..count {
|
for _ in 0..count {
|
||||||
let value = Self::parse(
|
values.push(Self::parse(stream, child_definition)?);
|
||||||
stream,
|
|
||||||
definition
|
|
||||||
.array_property
|
|
||||||
.as_ref()
|
|
||||||
.ok_or(MalformedSendPropDefinitionError::UntypedArray)?,
|
|
||||||
)?;
|
|
||||||
values.push(value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(values)
|
Ok(values)
|
||||||
|
|
@ -370,10 +486,20 @@ impl From<Vec<SendPropValue>> for SendPropValue {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct SendProp {
|
pub struct SendProp {
|
||||||
definition: SendPropDefinition,
|
pub definition: SendPropDefinition,
|
||||||
value: SendPropValue,
|
pub value: SendPropValue,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for SendProp {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{}::{} = {}",
|
||||||
|
self.definition.owner_table, self.definition.name, self.value
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_var_int(stream: &mut Stream, signed: bool) -> ReadResult<i32> {
|
pub fn read_var_int(stream: &mut Stream, signed: bool) -> ReadResult<i32> {
|
||||||
|
|
@ -381,7 +507,7 @@ pub fn read_var_int(stream: &mut Stream, signed: bool) -> ReadResult<i32> {
|
||||||
|
|
||||||
for i in (0..35).step_by(7) {
|
for i in (0..35).step_by(7) {
|
||||||
let byte: u8 = stream.read()?;
|
let byte: u8 = stream.read()?;
|
||||||
result |= ((byte & 0x7F) << i) as i32;
|
result |= ((byte & 0x7F) as i32) << i;
|
||||||
|
|
||||||
if (byte >> 7) == 0 {
|
if (byte >> 7) == 0 {
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use bitstream_reader::{BitRead, BitSize};
|
use bitstream_reader::{BitRead, BitSize};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
#[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 {
|
||||||
|
|
@ -8,8 +9,20 @@ pub struct Vector {
|
||||||
pub z: f32,
|
pub z: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Vector {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "({}, {}, {})", self.x, self.y, self.z)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(BitRead, BitSize, Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq)]
|
#[derive(BitRead, BitSize, Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq)]
|
||||||
pub struct VectorXY {
|
pub struct VectorXY {
|
||||||
pub x: f32,
|
pub x: f32,
|
||||||
pub y: f32,
|
pub y: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for VectorXY {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "({}, {})", self.x, self.y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue