1
0
Fork 0
mirror of https://codeberg.org/demostf/parser.git synced 2026-06-03 10:14:06 +02:00

prepare for 0.6

This commit is contained in:
Robin Appelman 2025-05-05 19:23:56 +02:00
commit 57096ab170
4 changed files with 23 additions and 222 deletions

View file

@ -1,161 +0,0 @@
use std::env;
use std::fs;
use bitbuffer::{BigEndian, BitRead, BitReadStream, LittleEndian};
use hound::{SampleFormat, WavSpec, WavWriter};
use main_error::MainError;
use opus::{Channels, Decoder};
use opus::packet::parse;
use steamid_ng::SteamID;
use tf_demo_parser::demo::parser::MessageHandler;
use tf_demo_parser::MessageType;
pub use tf_demo_parser::{Demo, DemoParser, Parse, ParserState};
use tf_demo_parser::demo::data::DemoTick;
use tf_demo_parser::demo::message::Message;
use tf_demo_parser::demo::message::voice::{VoiceInitMessage};
#[cfg(feature = "jemallocator")]
#[global_allocator]
static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
fn main() -> Result<(), MainError> {
#[cfg(feature = "better-panic")]
better_panic::install();
#[cfg(feature = "trace")]
tracing_subscriber::fmt::init();
let args: Vec<_> = env::args().collect();
if args.len() < 2 {
println!("1 argument required");
return Ok(());
}
let path = args[1].clone();
let file = fs::read(path)?;
let demo = Demo::new(&file);
let parser = DemoParser::new_with_analyser(demo.get_stream(), Voice::default());
let (_header, samples) = parser.parse()?;
let mut sample_buf = vec![0; 16 * 1024];
let spec = WavSpec {
channels: 1,
sample_rate: 44100,
bits_per_sample: 16,
sample_format: SampleFormat::Int,
};
let mut writer = WavWriter::create("out.wav", spec).unwrap();
for sample in samples {
let packet = decode_steam_voice_sample(&sample.data);
let mut decoder = Decoder::new(packet.sample_rate as u32, Channels::Mono).unwrap();
dbg!(&packet.data[0..8]);
dbg!(parse(&packet.data));
let samples = decoder.decode(&packet.data, &mut sample_buf, false).unwrap();
dbg!(samples);
for sample in &sample_buf[0..samples] {
writer.write_sample(*sample).unwrap();
}
}
Ok(())
}
#[derive(Default)]
struct Voice {
pub samples: Vec<VoiceSample>,
pub last_init: Option<VoiceInitMessage>,
}
#[derive(Debug)]
pub enum VoiceCodec {
Steam,
Speex,
Celt,
CeltHigh
}
#[derive(Debug)]
struct VoiceSample {
tick: DemoTick,
codec: VoiceCodec,
sampling_rate: u16,
client: u8,
data: Vec<u8>,
}
impl MessageHandler for Voice {
type Output = Vec<VoiceSample>;
fn does_handle(message_type: MessageType) -> bool {
matches!(
message_type,
MessageType::VoiceInit | MessageType::VoiceData
)
}
fn handle_message(&mut self, message: &Message, tick: DemoTick, _parser_state: &ParserState) {
match message {
Message::VoiceInit(init) => {
self.last_init = Some(init.clone());
}
Message::VoiceData(data) => {
if let Some(init) = self.last_init.as_ref() {
let codec = match init.codec.as_str() {
"steam" => VoiceCodec::Steam,
"vaudio_speex" => VoiceCodec::Speex,
"vaudio_celt" => VoiceCodec::Celt,
"vaudio_celt_high" => VoiceCodec::CeltHigh,
_ => {
eprintln!("unknown voice codex: {}", init.codec);
return;
}
};
self.samples.push(VoiceSample {
tick,
codec,
sampling_rate: init.sampling_rate,
data: data.data.clone().read_sized::<Vec<u8>>(data.length as usize / 8).unwrap(),
client: data.client,
})
} else {
eprintln!("Voice data without init");
}
}
_ => {}
}
}
fn into_output(self, _state: &ParserState) -> Self::Output {
self.samples
}
}
#[derive(BitRead, Debug)]
struct SteamVoiceHeader {
steam_id: u64,
ty: u8,
sample_rate: u16,
payload_type: u8,
length: u16,
}
#[derive(Debug)]
struct SteamVoicePacket {
steam_id: SteamID,
sample_rate: u16,
data: Vec<u8>,
}
fn decode_steam_voice_sample(data: &[u8]) -> SteamVoicePacket {
let mut reader = BitReadStream::<LittleEndian>::from(data);
let header = reader.read::<SteamVoiceHeader>().unwrap();
assert_eq!(header.ty, 11);
let data: Vec<u8> = if header.payload_type == 6 {
reader.read_sized(header.length as usize).unwrap()
} else {
dbg!(header.length, data.len());
Vec::new()
};
SteamVoicePacket {
steam_id: SteamID::from(header.steam_id),
sample_rate: header.sample_rate,
data,
}
}

View file

@ -13,6 +13,7 @@ use std::collections::{BTreeMap, HashMap};
pub struct Handle(pub i64);
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Default)]
#[non_exhaustive]
pub enum PlayerState {
#[default]
Alive = 0,
@ -54,6 +55,7 @@ impl Box {
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
#[non_exhaustive]
pub struct Player {
pub entity: EntityId,
pub position: Vector,
@ -109,6 +111,7 @@ impl Player {
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
#[non_exhaustive]
pub struct Sentry {
pub entity: EntityId,
pub builder: UserId,
@ -128,6 +131,7 @@ pub struct Sentry {
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
#[non_exhaustive]
pub struct Dispenser {
pub entity: EntityId,
pub builder: UserId,
@ -144,6 +148,7 @@ pub struct Dispenser {
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
#[non_exhaustive]
pub struct Teleporter {
pub entity: EntityId,
pub builder: UserId,
@ -164,6 +169,7 @@ pub struct Teleporter {
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[non_exhaustive]
pub enum Building {
Sentry(Sentry),
Dispenser(Dispenser),
@ -269,6 +275,7 @@ impl Building {
}
}
#[non_exhaustive]
pub enum BuildingClass {
Sentry,
Dispenser,
@ -276,6 +283,7 @@ pub enum BuildingClass {
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct Projectile {
pub id: EntityId,
pub team: Team,
@ -305,6 +313,7 @@ impl Projectile {
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub enum PipeType {
Regular = 0,
Sticky = 1,
@ -376,6 +385,7 @@ impl From<u8> for ProjectileType {
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct Collision {
pub tick: DemoTick,
pub target: EntityId,
@ -383,12 +393,14 @@ pub struct Collision {
}
#[derive(Default, Debug, Serialize, Deserialize, PartialEq, Clone)]
#[non_exhaustive]
pub struct World {
pub boundary_min: Vector,
pub boundary_max: Vector,
}
#[derive(Default, Debug, Serialize, Deserialize, PartialEq, Clone)]
#[non_exhaustive]
pub struct Kill {
pub attacker_id: u16,
pub assister_id: u16,
@ -410,6 +422,7 @@ impl Kill {
}
#[derive(Default, Debug, Serialize, Deserialize, PartialEq)]
#[non_exhaustive]
pub struct GameState {
pub players: Vec<Player>,
pub buildings: BTreeMap<EntityId, Building>,