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

generated code for parsing game events

This commit is contained in:
Robin Appelman 2019-03-02 00:39:22 +01:00
commit cc7f9a48cd
18 changed files with 8518 additions and 64 deletions

View 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,
})
}
}

View file

@ -0,0 +1,7 @@
use bitstream_reader::{BitRead, BitReadSized, LittleEndian};
use crate::{ReadResult, Stream};
pub struct GameEventMessage {
}

View file

@ -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)
}
}

View file

@ -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);
}
}

View 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
View 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,
})
}
}