Initial version

This commit is contained in:
Lol3rrr
2024-09-15 05:09:15 +02:00
commit c7aa4dbe8c
489 changed files with 124650 additions and 0 deletions

229
src/parser.rs Normal file
View File

@@ -0,0 +1,229 @@
use crate::{packet::DemoEvent, DemoCommand, Frame};
#[derive(Debug)]
pub enum FirstPassError {
DecompressFrame,
NoDataFrame,
DecodeProtobuf(prost::DecodeError),
MissingFileHeader,
MissingFileInfo,
Bitreader(crate::bitreader::BitReadError),
ParseGameEventError(crate::game_event::ParseGameEventError)
}
impl From<prost::DecodeError> for FirstPassError {
fn from(value: prost::DecodeError) -> Self {
Self::DecodeProtobuf(value)
}
}
impl From<crate::bitreader::BitReadError> for FirstPassError {
fn from(value: crate::bitreader::BitReadError) -> Self {
Self::Bitreader(value)
}
}
impl From<crate::game_event::ParseGameEventError> for FirstPassError {
fn from(value: crate::game_event::ParseGameEventError) -> Self {
Self::ParseGameEventError(value)
}
}
#[derive(Debug)]
pub struct FirstPassOutput {
pub header: crate::csgo_proto::CDemoFileHeader,
pub info: crate::csgo_proto::CDemoFileInfo,
pub events: Vec<DemoEvent>,
}
#[derive(Debug)]
struct GameEventMapping {
mapping: std::collections::HashMap<i32, (String, Vec<crate::csgo_proto::csvc_msg_game_event_list::KeyT>)>,
}
pub fn parse<'b, FI>(frames: FI) -> Result<FirstPassOutput, FirstPassError>
where
FI: IntoIterator<Item = Frame<'b>>,
{
let mut header = None;
let mut file_info = None;
let mut events = Vec::new();
let mut event_mapping = GameEventMapping {
mapping: std::collections::HashMap::new(),
};
for mut frame in frames.into_iter() {
frame
.decompress()
.map_err(|e| FirstPassError::DecompressFrame)?;
let data = frame.data().ok_or(FirstPassError::NoDataFrame)?;
match frame.cmd {
DemoCommand::FileHeader => {
let raw: crate::csgo_proto::CDemoFileHeader = prost::Message::decode(data)?;
header = Some(raw);
}
DemoCommand::FileInfo => {
let raw: crate::csgo_proto::CDemoFileInfo = prost::Message::decode(data)?;
file_info = Some(raw);
}
DemoCommand::SignonPacket | DemoCommand::Packet => {
parse_packet(data, &mut events, &mut event_mapping)?;
}
DemoCommand::FullPacket => {
parse_fullpacket(data, &mut events, &mut event_mapping)?;
}
_ => {}
}
}
let header = header.ok_or(FirstPassError::MissingFileHeader)?;
let info = file_info.ok_or(FirstPassError::MissingFileInfo)?;
Ok(FirstPassOutput { header, info, events })
}
fn parse_fullpacket(
data: &[u8],
events: &mut Vec<DemoEvent>,
event_mapper: &mut GameEventMapping,
) -> Result<(), FirstPassError> {
let raw: crate::csgo_proto::CDemoFullPacket = prost::Message::decode(data)?;
// TODO
// Handle string table stuff
match raw.packet {
Some(packet) => {
inner_parse_packet(&packet, events, event_mapper)?;
Ok(())
}
None => Ok(()),
}
}
fn parse_packet(
data: &[u8],
events: &mut Vec<DemoEvent>,
event_mapper: &mut GameEventMapping,
) -> Result<(), FirstPassError> {
let raw: crate::csgo_proto::CDemoPacket = prost::Message::decode(data)?;
inner_parse_packet(&raw, events, event_mapper)?;
Ok(())
}
fn inner_parse_packet(
raw: &crate::csgo_proto::CDemoPacket,
events: &mut Vec<DemoEvent>,
event_mapper: &mut GameEventMapping,
) -> Result<(), FirstPassError> {
let mut bitreader = crate::bitreader::Bitreader::new(raw.data());
while bitreader.bits_remaining().unwrap_or(0) > 8 {
let msg_type = bitreader.read_u_bit_var()?;
let size = bitreader.read_varint()?;
let msg_bytes = bitreader.read_n_bytes(size as usize)?;
let net_msg_type = match crate::netmessagetypes::NetmessageType::try_from(msg_type as i32) {
Ok(v) => v,
Err(e) => {
dbg!(e);
continue;
}
};
match net_msg_type {
crate::netmessagetypes::NetmessageType::svc_ClearAllStringTables => {}
crate::netmessagetypes::NetmessageType::svc_CreateStringTable => {}
crate::netmessagetypes::NetmessageType::svc_UpdateStringTable => {}
crate::netmessagetypes::NetmessageType::GE_Source1LegacyGameEventList => {
let event_list: crate::csgo_proto::CsvcMsgGameEventList =
prost::Message::decode(msg_bytes.as_slice())?;
event_mapper.mapping.clear();
for event in event_list.descriptors {
event_mapper
.mapping
.insert(event.eventid(), (event.name().to_owned(), event.keys));
}
}
crate::netmessagetypes::NetmessageType::svc_ServerInfo => {
let raw: crate::csgo_proto::CsvcMsgServerInfo =
prost::Message::decode(msg_bytes.as_slice())?;
events.push(DemoEvent::ServerInfo(raw));
}
crate::netmessagetypes::NetmessageType::net_SignonState => {}
crate::netmessagetypes::NetmessageType::net_Tick => {
let raw: crate::csgo_proto::CnetMsgTick =
prost::Message::decode(msg_bytes.as_slice())?;
events.push(DemoEvent::Tick(raw));
}
crate::netmessagetypes::NetmessageType::net_SetConVar => {}
crate::netmessagetypes::NetmessageType::svc_ClassInfo => {}
crate::netmessagetypes::NetmessageType::svc_VoiceInit => {}
crate::netmessagetypes::NetmessageType::svc_PacketEntities => {}
crate::netmessagetypes::NetmessageType::svc_UserCmds => {}
crate::netmessagetypes::NetmessageType::GE_SosStartSoundEvent => {}
crate::netmessagetypes::NetmessageType::GE_SosStopSoundEvent => {}
crate::netmessagetypes::NetmessageType::CS_GE_PlayerAnimationEvent => {}
crate::netmessagetypes::NetmessageType::CS_GE_RadioIconEvent => {}
crate::netmessagetypes::NetmessageType::CS_GE_FireBullets => {}
crate::netmessagetypes::NetmessageType::GE_Source1LegacyGameEvent => {
let raw: crate::csgo_proto::CMsgSource1LegacyGameEvent =
prost::Message::decode(msg_bytes.as_slice())?;
match event_mapper.mapping.get(&raw.eventid()) {
Some((name, keys)) => {
match crate::game_event::EVENT_PARSERS.get(&name) {
Some(parser) => {
let parsed = parser.parse(keys.as_slice(), raw.clone())?;
events.push(DemoEvent::GameEvent(parsed));
}
None => {
println!("No parser for {:?}", name);
}
};
}
None => {
println!("Unknown Event - ID: {}", raw.eventid());
}
};
}
crate::netmessagetypes::NetmessageType::UM_SayText2 => {}
crate::netmessagetypes::NetmessageType::CS_UM_XpUpdate => {}
crate::netmessagetypes::NetmessageType::CS_UM_ServerRankUpdate => {
let raw: crate::csgo_proto::CcsUsrMsgServerRankUpdate =
prost::Message::decode(msg_bytes.as_slice())?;
events.push(DemoEvent::RankUpdate(raw));
}
crate::netmessagetypes::NetmessageType::CS_UM_ServerRankRevealAll => {
let raw: crate::csgo_proto::CcsUsrMsgServerRankRevealAll =
prost::Message::decode(msg_bytes.as_slice())?;
events.push(DemoEvent::RankReveal(raw));
}
crate::netmessagetypes::NetmessageType::CS_UM_WeaponSound => {}
crate::netmessagetypes::NetmessageType::CS_UM_RadioText => {}
crate::netmessagetypes::NetmessageType::TE_WorldDecal => {}
crate::netmessagetypes::NetmessageType::TE_EffectDispatch => {}
crate::netmessagetypes::NetmessageType::CS_UM_PlayerStatsUpdate => {}
crate::netmessagetypes::NetmessageType::CS_UM_EndOfMatchAllPlayersData => {}
crate::netmessagetypes::NetmessageType::TE_PhysicsProp => {}
crate::netmessagetypes::NetmessageType::UM_TextMsg => {}
crate::netmessagetypes::NetmessageType::CS_UM_VoteFailed => {}
crate::netmessagetypes::NetmessageType::net_SpawnGroup_Load => {}
crate::netmessagetypes::NetmessageType::CS_UM_MatchEndConditions => {}
crate::netmessagetypes::NetmessageType::TE_Explosion => {}
unknown => {
dbg!(unknown);
}
};
}
Ok(())
}