mirror of
https://codeberg.org/demostf/parser.git
synced 2026-06-03 18:24:05 +02:00
generated code for parsing game events
This commit is contained in:
parent
cd552319ac
commit
cc7f9a48cd
18 changed files with 8518 additions and 64 deletions
7969
src/demo/gameevent_gen.rs
Normal file
7969
src/demo/gameevent_gen.rs
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -1,16 +1,21 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use crate::{ParseError, Result};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct GameEventDefinition {
|
||||
id: u32,
|
||||
name: String,
|
||||
entries: Vec<GameEventEntry>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct GameEventEntry {
|
||||
name: String,
|
||||
kind: GameEventValueType,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum GameEventValueType {
|
||||
String,
|
||||
Float,
|
||||
|
|
@ -21,11 +26,13 @@ pub enum GameEventValueType {
|
|||
Local,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct GameEvent {
|
||||
kind: GameEventType,
|
||||
values: HashMap<String, GameEventValue>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum GameEventValue {
|
||||
String(String),
|
||||
Float(f32),
|
||||
|
|
@ -36,6 +43,95 @@ pub enum GameEventValue {
|
|||
Local,
|
||||
}
|
||||
|
||||
pub trait FromGameEventValue: Sized {
|
||||
fn from_value(value: GameEventValue, name: &str) -> Result<Self>;
|
||||
}
|
||||
|
||||
impl FromGameEventValue for String {
|
||||
fn from_value(value: GameEventValue, name: &str) -> Result<Self> {
|
||||
match value {
|
||||
GameEventValue::String(val) => Ok(val),
|
||||
_ => Err(ParseError::InvalidGameEvent {
|
||||
name: name.to_string(),
|
||||
value,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromGameEventValue for f32 {
|
||||
fn from_value(value: GameEventValue, name: &str) -> Result<Self> {
|
||||
match value {
|
||||
GameEventValue::Float(val) => Ok(val),
|
||||
_ => Err(ParseError::InvalidGameEvent {
|
||||
name: name.to_string(),
|
||||
value,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromGameEventValue for u32 {
|
||||
fn from_value(value: GameEventValue, name: &str) -> Result<Self> {
|
||||
match value {
|
||||
GameEventValue::Long(val) => Ok(val),
|
||||
_ => Err(ParseError::InvalidGameEvent {
|
||||
name: name.to_string(),
|
||||
value,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromGameEventValue for u16 {
|
||||
fn from_value(value: GameEventValue, name: &str) -> Result<Self> {
|
||||
match value {
|
||||
GameEventValue::Short(val) => Ok(val),
|
||||
_ => Err(ParseError::InvalidGameEvent {
|
||||
name: name.to_string(),
|
||||
value,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromGameEventValue for u8 {
|
||||
fn from_value(value: GameEventValue, name: &str) -> Result<Self> {
|
||||
match value {
|
||||
GameEventValue::Byte(val) => Ok(val),
|
||||
_ => Err(ParseError::InvalidGameEvent {
|
||||
name: name.to_string(),
|
||||
value,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromGameEventValue for bool {
|
||||
fn from_value(value: GameEventValue, name: &str) -> Result<Self> {
|
||||
match value {
|
||||
GameEventValue::Boolean(val) => Ok(val),
|
||||
_ => Err(ParseError::InvalidGameEvent {
|
||||
name: name.to_string(),
|
||||
value,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromGameEventValue for () {
|
||||
fn from_value(value: GameEventValue, name: &str) -> Result<Self> {
|
||||
match value {
|
||||
GameEventValue::Local => Ok(()),
|
||||
_ => Err(ParseError::InvalidGameEvent {
|
||||
name: name.to_string(),
|
||||
value,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum GameEventType {
|
||||
ServerSpawnEvent,
|
||||
ServerChangelevelFailedEvent,
|
||||
|
|
@ -414,3 +510,12 @@ pub enum GameEventType {
|
|||
ReplayReplaysAvailableEvent,
|
||||
ReplayServerErrorEvent,
|
||||
}
|
||||
|
||||
pub struct RawGameEvent {
|
||||
pub name: String,
|
||||
pub values: HashMap<String, GameEventValue>,
|
||||
}
|
||||
|
||||
pub trait FromRawGameEvent: Sized {
|
||||
fn from_raw_event(values: HashMap<String, GameEventValue>) -> Result<Self>;
|
||||
}
|
||||
47
src/demo/message/bspdecal.rs
Normal file
47
src/demo/message/bspdecal.rs
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
use bitstream_reader::{BitRead, BitReadSized, LittleEndian};
|
||||
|
||||
use crate::{ReadResult, Stream};
|
||||
use crate::demo::sendprop::read_bit_coord;
|
||||
use crate::demo::vector::Vector;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct BSPDecalMessage {
|
||||
pub position: Vector,
|
||||
pub texture_index: u16,
|
||||
pub ent_index: u16,
|
||||
pub model_index: u16,
|
||||
pub low_priority: bool,
|
||||
}
|
||||
|
||||
impl BitRead<LittleEndian> for BSPDecalMessage {
|
||||
fn read(stream: &mut Stream) -> ReadResult<Self> {
|
||||
let position = {
|
||||
let has_x = stream.read()?;
|
||||
let has_y = stream.read()?;
|
||||
let has_z = stream.read()?;
|
||||
|
||||
Vector {
|
||||
x: if has_x { read_bit_coord(stream)? } else { 0f32 },
|
||||
y: if has_y { read_bit_coord(stream)? } else { 0f32 },
|
||||
z: if has_z { read_bit_coord(stream)? } else { 0f32 },
|
||||
}
|
||||
};
|
||||
|
||||
let texture_index = stream.read_sized(9)?;
|
||||
let (ent_index, model_index): (u16, u16) = if stream.read()? {
|
||||
(stream.read_sized(11)?, stream.read_sized(12)?)
|
||||
} else {
|
||||
(0, 0)
|
||||
};
|
||||
let low_priority = stream.read()?;
|
||||
|
||||
Ok(BSPDecalMessage {
|
||||
position,
|
||||
texture_index,
|
||||
ent_index,
|
||||
model_index,
|
||||
low_priority,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
7
src/demo/message/gameevent.rs
Normal file
7
src/demo/message/gameevent.rs
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
use bitstream_reader::{BitRead, BitReadSized, LittleEndian};
|
||||
|
||||
use crate::{ReadResult, Stream};
|
||||
|
||||
pub struct GameEventMessage {
|
||||
|
||||
}
|
||||
|
|
@ -5,9 +5,12 @@ pub use generated::*;
|
|||
|
||||
use crate::{Parse, ParseError, ParserState, Result, Stream};
|
||||
|
||||
mod classinfo;
|
||||
mod generated;
|
||||
mod stringtable;
|
||||
pub mod classinfo;
|
||||
pub mod generated;
|
||||
pub mod stringtable;
|
||||
pub mod voice;
|
||||
pub mod bspdecal;
|
||||
pub mod usermessage;
|
||||
|
||||
#[derive(Primitive, Debug)]
|
||||
pub enum MessageType {
|
||||
|
|
@ -46,8 +49,4 @@ impl Parse for MessageType {
|
|||
let prop_type: Option<MessageType> = MessageType::from_u8(raw);
|
||||
prop_type.ok_or(ParseError::InvalidMessageType(raw))
|
||||
}
|
||||
|
||||
fn skip(stream: &mut Stream) -> Result<()> {
|
||||
stream.skip(6).map_err(ParseError::from)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use arraydeque::{ArrayDeque, Wrapping};
|
||||
use bitstream_reader::{BitRead, BitReadSized, LittleEndian};
|
||||
use bitstream_reader::{BitRead, BitReadSized, BitStream, LittleEndian};
|
||||
|
||||
use crate::{ReadResult, Stream};
|
||||
use crate::{Parse, ParseError, ParserState, ReadResult, Stream, Result};
|
||||
use crate::demo::packet::stringtable::{ExtraData, FixedUserdataSize, StringTable, StringTableEntry};
|
||||
|
||||
pub struct CreateStringTableMessage {
|
||||
pub table: StringTable,
|
||||
}
|
||||
|
||||
struct StringTableMeta {
|
||||
pub struct StringTableMeta {
|
||||
pub max_entries: u16,
|
||||
pub fixed_userdata_size: Option<FixedUserdataSize>,
|
||||
}
|
||||
|
|
@ -47,7 +47,7 @@ impl BitRead<LittleEndian> for CreateStringTableMessage {
|
|||
fixed_userdata_size,
|
||||
};
|
||||
|
||||
let entries = parse_string_table_entries(stream, &table_meta, entity_count, Vec::new())?;
|
||||
let entries = parse_string_table_entries(stream, &table_meta, entity_count, &Vec::new())?;
|
||||
let mut entries: Vec<(u16, StringTableEntry)> = entries.into_iter().collect();
|
||||
|
||||
// verify that there are no holes in our indexes
|
||||
|
|
@ -71,14 +71,40 @@ impl BitRead<LittleEndian> for CreateStringTableMessage {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct UpdateStringTableMessage {
|
||||
pub entries: HashMap<u16, StringTableEntry>,
|
||||
pub table_id: u8,
|
||||
}
|
||||
|
||||
impl Parse for UpdateStringTableMessage {
|
||||
fn parse(stream: &mut Stream, state: &ParserState) -> Result<Self> {
|
||||
let table_id = stream.read_sized(5)?;
|
||||
|
||||
let changed: u16 = if stream.read()? { stream.read()? } else { 1 };
|
||||
let len = stream.read_int(20)?;
|
||||
|
||||
let mut data = stream.read_bits(len)?;
|
||||
|
||||
let entries = match state.string_tables.get(table_id as usize) {
|
||||
Some(table) => parse_string_table_entries(&mut data, &table.get_table_meta(), changed, &table.entries),
|
||||
None => return Err(ParseError::StringTableNotFound(table_id))
|
||||
}?;
|
||||
|
||||
Ok(UpdateStringTableMessage {
|
||||
table_id,
|
||||
entries,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_string_table_entries(
|
||||
stream: &mut Stream,
|
||||
table_meta: &StringTableMeta,
|
||||
entry_count: u16,
|
||||
existing_entries: Vec<StringTableEntry>,
|
||||
existing_entries: &Vec<StringTableEntry>,
|
||||
) -> ReadResult<HashMap<u16, StringTableEntry>> {
|
||||
let entry_bits = 16 - table_meta.max_entries.leading_zeros();
|
||||
let mut entries = HashMap::new();
|
||||
let mut entries = HashMap::with_capacity(entry_count as usize);
|
||||
|
||||
let mut last_entry: i16 = -1;
|
||||
let mut history: ArrayDeque<[String; 32], Wrapping> = ArrayDeque::new();
|
||||
|
|
@ -138,10 +164,13 @@ fn parse_string_table_entries(
|
|||
extra_data: user_data,
|
||||
}
|
||||
};
|
||||
// optimize: any way to get rid of the clone here?
|
||||
// `entries` always outlives `history` without reallocation
|
||||
let text = entry.text.clone();
|
||||
entries.insert(index, entry);
|
||||
unsafe {
|
||||
history.push_back(text);
|
||||
// not 100% sure we should be pushing front here, and not appending
|
||||
history.push_front(text);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
233
src/demo/message/usermessage.rs
Normal file
233
src/demo/message/usermessage.rs
Normal file
|
|
@ -0,0 +1,233 @@
|
|||
use bitstream_reader::{BitRead, BitReadSized, LittleEndian};
|
||||
use enum_primitive_derive::Primitive;
|
||||
use num_traits::{FromPrimitive, ToPrimitive};
|
||||
|
||||
use crate::{ReadResult, Stream};
|
||||
use crate::demo::message::usermessage::UserMessage::SayText2;
|
||||
|
||||
#[derive(Primitive, Clone, Copy, Debug)]
|
||||
pub enum UserMessageType {
|
||||
Geiger = 0,
|
||||
Train = 1,
|
||||
HudText = 2,
|
||||
SayText = 3,
|
||||
SayText2 = 4,
|
||||
TextMsg = 5,
|
||||
ResetHUD = 6,
|
||||
GameTitle = 7,
|
||||
ItemPickup = 8,
|
||||
ShowMenu = 9,
|
||||
Shake = 10,
|
||||
Fade = 11,
|
||||
VGUIMenu = 12,
|
||||
Rumble = 13,
|
||||
CloseCaption = 14,
|
||||
SendAudio = 15,
|
||||
VoiceMask = 16,
|
||||
RequestState = 17,
|
||||
Damage = 18,
|
||||
HintText = 19,
|
||||
KeyHintText = 20,
|
||||
HudMsg = 21,
|
||||
AmmoDenied = 22,
|
||||
AchievementEvent = 23,
|
||||
UpdateRadar = 24,
|
||||
VoiceSubtitle = 25,
|
||||
HudNotify = 26,
|
||||
HudNotifyCustom = 27,
|
||||
PlayerStatsUpdate = 28,
|
||||
PlayerIgnited = 29,
|
||||
PlayerIgnitedInv = 30,
|
||||
HudArenaNotify = 31,
|
||||
UpdateAchievement = 32,
|
||||
TrainingMsg = 33,
|
||||
TrainingObjective = 34,
|
||||
DamageDodged = 35,
|
||||
PlayerJarated = 36,
|
||||
PlayerExtinguished = 37,
|
||||
PlayerJaratedFade = 38,
|
||||
PlayerShieldBlocked = 39,
|
||||
BreakModel = 40,
|
||||
CheapBreakModel = 41,
|
||||
BreakModelPumpkin = 42,
|
||||
BreakModelRocketDud = 43,
|
||||
CallVoteFailed = 44,
|
||||
VoteStart = 45,
|
||||
VotePass = 46,
|
||||
VoteFailed = 47,
|
||||
VoteSetup = 48,
|
||||
PlayerBonusPoints = 49,
|
||||
SpawnFlyingBird = 50,
|
||||
PlayerGodRayEffect = 51,
|
||||
SPHapWeapEvent = 52,
|
||||
HapDmg = 53,
|
||||
HapPunch = 54,
|
||||
HapSetDrag = 55,
|
||||
HapSet = 56,
|
||||
HapMeleeContact = 57,
|
||||
Unknown = 255,
|
||||
}
|
||||
|
||||
pub enum UserMessage {
|
||||
SayText2(SayText2Message),
|
||||
Text(TextMessage),
|
||||
ResetHUD(ResetHudMessage),
|
||||
Train(TrainMessage),
|
||||
VoiceSubtitle(VoiceSubtitleMessage),
|
||||
Shake(ShakeMessage),
|
||||
Unknown(UnknownUserMessage),
|
||||
}
|
||||
|
||||
impl BitRead<LittleEndian> for UserMessage {
|
||||
fn read(stream: &mut Stream) -> ReadResult<Self> {
|
||||
let message_type_opt: Option<UserMessageType> = UserMessageType::from_u8(stream.read()?);
|
||||
let message_type = message_type_opt.unwrap_or(UserMessageType::Unknown);
|
||||
let length = stream.read_int(11)?;
|
||||
let data = stream.read_bits(length)?;
|
||||
Ok(match message_type {
|
||||
UserMessageType::SayText2 => UserMessage::SayText2(stream.read()?),
|
||||
UserMessageType::TextMsg => UserMessage::Text(stream.read()?),
|
||||
UserMessageType::ResetHUD => UserMessage::ResetHUD(stream.read()?),
|
||||
UserMessageType::Train => UserMessage::Train(stream.read()?),
|
||||
UserMessageType::VoiceSubtitle => UserMessage::VoiceSubtitle(stream.read()?),
|
||||
UserMessageType::Shake => UserMessage::Shake(stream.read()?),
|
||||
_ => UserMessage::Unknown(stream.read()?),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum SayText2Kind {
|
||||
ChatAll,
|
||||
ChatTeam,
|
||||
ChatAllDead,
|
||||
NameChange,
|
||||
}
|
||||
|
||||
impl BitRead<LittleEndian> for SayText2Kind {
|
||||
fn read(stream: &mut Stream) -> ReadResult<Self> {
|
||||
let raw: String = stream.read()?;
|
||||
Ok(match raw.as_str() {
|
||||
"TF_Chat_Team" => SayText2Kind::ChatTeam,
|
||||
"TF_Chat_AllDead" => SayText2Kind::ChatAllDead,
|
||||
"#TF_Name_Change" => SayText2Kind::NameChange,
|
||||
_ => SayText2Kind::ChatAll
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SayText2Message {
|
||||
client: u8,
|
||||
raw: u8,
|
||||
kind: SayText2Kind,
|
||||
from: String,
|
||||
text: String,
|
||||
}
|
||||
|
||||
impl BitRead<LittleEndian> for SayText2Message {
|
||||
fn read(stream: &mut Stream) -> ReadResult<Self> {
|
||||
let client = stream.read()?;
|
||||
let raw = stream.read()?;
|
||||
let (kind, from, text): (SayText2Kind, String, String) = if stream.read::<u8>()? == 1 {
|
||||
let first = stream.read::<u8>()?;
|
||||
if first == 7 {
|
||||
let _color = stream.read_string(Some(6))?;
|
||||
} else {
|
||||
let _ = stream.skip(8)?;
|
||||
}
|
||||
|
||||
let text: String = stream.read()?;
|
||||
if text.starts_with("*DEAD*") {
|
||||
// grave talk is in the format '*DEAD* \u0003$from\u0001: $text'b
|
||||
let start = text.find(char::from(3)).unwrap_or(0);
|
||||
let end = text.find(char::from(1)).unwrap_or(0);
|
||||
let from: String = String::from_utf8(text.bytes().skip(start + 1).take(end - start - 1).collect())?;
|
||||
let text: String = String::from_utf8(text.bytes().skip(end + 5).collect())?;
|
||||
let kind = SayText2Kind::ChatAllDead;
|
||||
(kind, from, text)
|
||||
} else {
|
||||
(SayText2Kind::ChatAll, "".to_owned(), text)
|
||||
}
|
||||
} else {
|
||||
let _ = stream.set_pos(stream.pos() - 8)?;
|
||||
|
||||
let kind = stream.read()?;
|
||||
let from = stream.read()?;
|
||||
let text = stream.read()?;
|
||||
let _ = stream.skip(16)?;
|
||||
(kind, from, text)
|
||||
};
|
||||
|
||||
// cleanup color codes
|
||||
let mut text = text
|
||||
.replace(char::from(1), "")
|
||||
.replace(char::from(3), "");
|
||||
while let Some(pos) = text.find(char::from(7)) {
|
||||
text = String::from_utf8(text.bytes().take(pos).chain(text.bytes().skip(pos + 7)).collect())?;
|
||||
}
|
||||
|
||||
Ok(SayText2Message {
|
||||
client,
|
||||
raw,
|
||||
kind,
|
||||
from,
|
||||
text,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(BitRead, Debug, Clone)]
|
||||
#[discriminant_bits = 8]
|
||||
pub enum HudTextLocation {
|
||||
PrintNotify = 1,
|
||||
PrintConsole,
|
||||
PrintTalk,
|
||||
PrintCenter,
|
||||
}
|
||||
|
||||
#[derive(BitRead, Debug, Clone)]
|
||||
pub struct TextMessage {
|
||||
pub location: HudTextLocation,
|
||||
pub text: String,
|
||||
#[size = 4]
|
||||
pub substitute: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(BitRead, Debug, Clone)]
|
||||
pub struct ResetHudMessage {
|
||||
pub data: u8
|
||||
}
|
||||
|
||||
#[derive(BitRead, Debug, Clone)]
|
||||
pub struct TrainMessage {
|
||||
pub data: u8
|
||||
}
|
||||
|
||||
#[derive(BitRead, Debug, Clone)]
|
||||
pub struct VoiceSubtitleMessage {
|
||||
client: u8,
|
||||
menu: u8,
|
||||
item: u8,
|
||||
}
|
||||
|
||||
#[derive(BitRead, Debug, Clone)]
|
||||
pub struct ShakeMessage {
|
||||
command: u8,
|
||||
amplitude: f32,
|
||||
frequency: f32,
|
||||
duration: f32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct UnknownUserMessage {
|
||||
data: Stream
|
||||
}
|
||||
|
||||
impl BitRead<LittleEndian> for UnknownUserMessage {
|
||||
fn read(stream: &mut Stream) -> ReadResult<Self> {
|
||||
Ok(UnknownUserMessage {
|
||||
data: stream.read_bits(stream.bits_left())?
|
||||
})
|
||||
}
|
||||
}
|
||||
65
src/demo/message/voice.rs
Normal file
65
src/demo/message/voice.rs
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
use bitstream_reader::{BitRead, BitStream, LittleEndian};
|
||||
|
||||
use crate::{Parse, ParseError, ParserState, ReadResult, Result, Stream};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct VoiceInitMessage {
|
||||
codec: String,
|
||||
quality: u8,
|
||||
extra_data: u16,
|
||||
}
|
||||
|
||||
impl BitRead<LittleEndian> for VoiceInitMessage {
|
||||
fn read(stream: &mut Stream) -> ReadResult<Self> {
|
||||
let codec = stream.read()?;
|
||||
let quality = stream.read()?;
|
||||
|
||||
let extra_data = if quality == 255 {
|
||||
stream.read()?
|
||||
} else if codec == "vaudio_celt" {
|
||||
11025
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
Ok(VoiceInitMessage {
|
||||
codec,
|
||||
quality,
|
||||
extra_data,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(BitRead, Debug, Clone)]
|
||||
#[endianness = "LittleEndian"]
|
||||
pub struct VoiceDataMessage {
|
||||
client: u8,
|
||||
proximity: u8,
|
||||
length: u16,
|
||||
#[size = "length"]
|
||||
data: Stream,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ParseSoundsMessage {
|
||||
pub reliable: bool,
|
||||
pub num: u8,
|
||||
pub length: u16,
|
||||
pub data: Stream,
|
||||
}
|
||||
|
||||
impl BitRead<LittleEndian> for ParseSoundsMessage {
|
||||
fn read(stream: &mut Stream) -> ReadResult<Self> {
|
||||
let reliable = stream.read()?;
|
||||
let num = if reliable { 1u8 } else { stream.read()? };
|
||||
let length = if reliable { stream.read_sized::<u16>(8)? } else { stream.read()? };
|
||||
let data = stream.read_sized(length as usize)?;
|
||||
|
||||
Ok(ParseSoundsMessage {
|
||||
reliable,
|
||||
num,
|
||||
length,
|
||||
data,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
use bitstream_reader::{BitBuffer, BitStream, LittleEndian};
|
||||
|
||||
pub mod gamevent;
|
||||
pub mod gameevent_gen;
|
||||
pub mod header;
|
||||
pub mod message;
|
||||
pub mod packet;
|
||||
|
|
|
|||
|
|
@ -47,16 +47,4 @@ impl Parse for MessagePacket {
|
|||
};
|
||||
Ok(packet)
|
||||
}
|
||||
|
||||
fn skip(stream: &mut Stream) -> Result<()> {
|
||||
let _ = stream.skip(32 * 2)?;
|
||||
|
||||
for i in 0..6 {
|
||||
Vector::skip(stream)?;
|
||||
}
|
||||
|
||||
let _ = stream.skip(32 * 2)?;
|
||||
let length: usize = stream.read_int::<usize>(32)?;
|
||||
stream.skip(length * 8).map_err(ParseError::from)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,9 @@ use std::fmt;
|
|||
|
||||
use bitstream_reader::{BitRead, LittleEndian};
|
||||
|
||||
use crate::demo::sendprop::SendPropFlag::Exclude;
|
||||
use crate::{Parse, ParseError, ParserState, ReadResult, Result, Stream};
|
||||
use crate::demo::message::stringtable::StringTableMeta;
|
||||
use crate::demo::sendprop::SendPropFlag::Exclude;
|
||||
|
||||
#[derive(BitRead, Clone, Copy, Debug)]
|
||||
pub struct FixedUserdataSize {
|
||||
|
|
@ -23,6 +24,15 @@ pub struct StringTable {
|
|||
pub compressed: bool,
|
||||
}
|
||||
|
||||
impl StringTable {
|
||||
pub fn get_table_meta(&self) -> StringTableMeta {
|
||||
StringTableMeta {
|
||||
fixed_userdata_size: self.fixed_userdata_size,
|
||||
max_entries: self.max_entries,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BitRead<LittleEndian> for StringTable {
|
||||
fn read(stream: &mut Stream) -> ReadResult<Self> {
|
||||
let name = stream.read()?;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use bitstream_reader::{BitRead, LittleEndian, ReadError};
|
||||
|
||||
use crate::demo::gamevent::GameEventValue;
|
||||
use crate::demo::header::Header;
|
||||
use crate::demo::packet::Packet;
|
||||
pub use crate::demo::parser::state::ParserState;
|
||||
|
|
@ -22,6 +25,15 @@ pub enum ParseError {
|
|||
InvalidSendPropArray(String),
|
||||
/// Expected amount of data left after parsing an object
|
||||
DataRemaining(usize),
|
||||
/// String table that was send for update doesn't exist
|
||||
StringTableNotFound(u8),
|
||||
/// A unknown game event type was read
|
||||
UnknownGameEvent(String),
|
||||
/// A read game event doesn't contain the expected values
|
||||
InvalidGameEvent {
|
||||
name: String,
|
||||
value: GameEventValue,
|
||||
},
|
||||
}
|
||||
|
||||
impl From<ReadError> for ParseError {
|
||||
|
|
@ -34,21 +46,12 @@ pub type Result<T> = std::result::Result<T, ParseError>;
|
|||
|
||||
pub trait Parse: Sized {
|
||||
fn parse(stream: &mut Stream, state: &ParserState) -> Result<Self>;
|
||||
fn skip(stream: &mut Stream) -> Result<()> {
|
||||
let _ = Self::parse(stream, &ParserState::new())?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: BitRead<LittleEndian>> Parse for T {
|
||||
fn parse(stream: &mut Stream, state: &ParserState) -> Result<Self> {
|
||||
Self::read(stream).map_err(ParseError::from)
|
||||
}
|
||||
|
||||
fn skip(stream: &mut Stream) -> Result<()> {
|
||||
let _ = Self::parse(stream, &ParserState::new())?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DemoParser {
|
||||
|
|
@ -68,10 +71,6 @@ impl DemoParser {
|
|||
T::parse(&mut self.stream, &self.state)
|
||||
}
|
||||
|
||||
pub fn skip<T: Parse>(&mut self) -> Result<()> {
|
||||
T::skip(&mut self.stream)
|
||||
}
|
||||
|
||||
pub fn stream_pos(&self) -> usize {
|
||||
self.stream.pos()
|
||||
}
|
||||
|
|
@ -81,19 +80,6 @@ impl DemoParser {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn split_packets(mut self) -> Result<Vec<Stream>> {
|
||||
let _ = self.skip::<Header>()?;
|
||||
let mut streams = vec![];
|
||||
while self.stream.bits_left() > 7 {
|
||||
let start = self.stream.pos();
|
||||
let _ = self.skip::<Packet>()?;
|
||||
let end = self.stream.pos();
|
||||
let _ = self.stream.set_pos(start);
|
||||
streams.push(self.stream.read_bits(end - start)?);
|
||||
}
|
||||
Ok(streams)
|
||||
}
|
||||
|
||||
pub fn parse_demo(mut self) -> Result<(Header, Vec<Packet>)> {
|
||||
let header = self.read::<Header>()?;
|
||||
let mut packets = vec![];
|
||||
|
|
|
|||
|
|
@ -2,13 +2,17 @@ use crate::demo::gamevent::GameEventDefinition;
|
|||
use crate::demo::sendprop::SendProp;
|
||||
use crate::Stream;
|
||||
use std::collections::HashMap;
|
||||
use crate::demo::packet::stringtable::StringTable;
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct ParserState {
|
||||
pub version: u32,
|
||||
pub static_baselines: HashMap<u32, StaticBaseline>,
|
||||
pub event_definitions: HashMap<u32, GameEventDefinition>,
|
||||
pub string_tables: Vec<StringTable>
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct StaticBaseline {
|
||||
class_id: u32,
|
||||
raw: Stream,
|
||||
|
|
@ -17,10 +21,6 @@ pub struct StaticBaseline {
|
|||
|
||||
impl ParserState {
|
||||
pub fn new() -> Self {
|
||||
ParserState {
|
||||
version: 0,
|
||||
static_baselines: HashMap::new(),
|
||||
event_definitions: HashMap::new(),
|
||||
}
|
||||
ParserState::default()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -184,6 +184,7 @@ impl BitRead<LittleEndian> for SendPropFlags {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum SendPropValue {
|
||||
Vector(Vector),
|
||||
VectorXY(VectorXY),
|
||||
|
|
@ -193,7 +194,23 @@ pub enum SendPropValue {
|
|||
Array(Vec<SendPropValue>),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SendProp {
|
||||
definition: SendPropDefinition,
|
||||
value: SendPropValue,
|
||||
}
|
||||
|
||||
pub fn read_bit_coord(stream: &mut Stream) -> ReadResult<f32> {
|
||||
let has_int = stream.read()?;
|
||||
let has_frac = stream.read()?;
|
||||
|
||||
Ok(if has_int || has_frac {
|
||||
let sign = if stream.read()? { -1f32 } else { 1f32 };
|
||||
let int_val: u16 = if has_int { stream.read_sized::<u16>(14)? + 1 } else { 0 };
|
||||
let frac_val: u8 = if has_frac { stream.read_sized(5)? } else { 0 };
|
||||
let value = int_val as f32 + (frac_val as f32 * (1f32 / 32f32));
|
||||
value * sign
|
||||
} else {
|
||||
0f32
|
||||
})
|
||||
}
|
||||
|
|
@ -1,13 +1,13 @@
|
|||
use bitstream_reader::BitRead;
|
||||
|
||||
#[derive(BitRead, Debug)]
|
||||
#[derive(BitRead, Debug, Clone, Copy)]
|
||||
pub struct Vector {
|
||||
pub x: f32,
|
||||
pub y: f32,
|
||||
pub z: f32,
|
||||
}
|
||||
|
||||
#[derive(BitRead, Debug)]
|
||||
#[derive(BitRead, Debug, Clone, Copy)]
|
||||
pub struct VectorXY {
|
||||
pub x: f32,
|
||||
pub y: f32,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue