mirror of
https://codeberg.org/demostf/parser.git
synced 2026-06-03 18:24:05 +02:00
some wip
This commit is contained in:
commit
4ac2a3cfc6
20 changed files with 907 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
/target
|
||||||
|
**/*.rs.bk
|
||||||
11
Cargo.toml
Normal file
11
Cargo.toml
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
[package]
|
||||||
|
name = "tf-demo-demo"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Robin Appelman <robin@icewind.nl>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
bitstream_reader = { path = "../../bitbuffer" }
|
||||||
|
rental = "0.5"
|
||||||
|
enum-primitive-derive = "0.1.2"
|
||||||
|
num-traits = "0.2"
|
||||||
BIN
data/small.dem
Normal file
BIN
data/small.dem
Normal file
Binary file not shown.
416
src/demo/gamevent.rs
Normal file
416
src/demo/gamevent.rs
Normal file
|
|
@ -0,0 +1,416 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
pub struct GameEventDefinition {
|
||||||
|
id: u32,
|
||||||
|
name: String,
|
||||||
|
entries: Vec<GameEventEntry>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct GameEventEntry {
|
||||||
|
name: String,
|
||||||
|
kind: GameEventValueType,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum GameEventValueType {
|
||||||
|
String,
|
||||||
|
Float,
|
||||||
|
Long,
|
||||||
|
Short,
|
||||||
|
Byte,
|
||||||
|
Boolean,
|
||||||
|
Local,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct GameEvent {
|
||||||
|
kind: GameEventType,
|
||||||
|
values: HashMap<String, GameEventValue>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum GameEventValue {
|
||||||
|
String(String),
|
||||||
|
Float(f32),
|
||||||
|
Long(u32),
|
||||||
|
Short(u16),
|
||||||
|
Byte(u8),
|
||||||
|
Boolean(bool),
|
||||||
|
Local,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum GameEventType {
|
||||||
|
ServerSpawnEvent,
|
||||||
|
ServerChangelevelFailedEvent,
|
||||||
|
ServerShutdownEvent,
|
||||||
|
ServerCvarEvent,
|
||||||
|
ServerMessageEvent,
|
||||||
|
ServerAddBanEvent,
|
||||||
|
ServerRemoveBanEvent,
|
||||||
|
PlayerConnectEvent,
|
||||||
|
PlayerConnectClientEvent,
|
||||||
|
PlayerInfoEvent,
|
||||||
|
PlayerDisconnectEvent,
|
||||||
|
PlayerActivateEvent,
|
||||||
|
PlayerSayEvent,
|
||||||
|
ClientDisconnectEvent,
|
||||||
|
ClientBeginConnectEvent,
|
||||||
|
ClientConnectedEvent,
|
||||||
|
ClientFullConnectEvent,
|
||||||
|
HostQuitEvent,
|
||||||
|
TeamInfoEvent,
|
||||||
|
TeamScoreEvent,
|
||||||
|
TeamPlayBroadcastAudioEvent,
|
||||||
|
PlayerTeamEvent,
|
||||||
|
PlayerClassEvent,
|
||||||
|
PlayerDeathEvent,
|
||||||
|
PlayerHurtEvent,
|
||||||
|
PlayerChatEvent,
|
||||||
|
PlayerScoreEvent,
|
||||||
|
PlayerSpawnEvent,
|
||||||
|
PlayerShootEvent,
|
||||||
|
PlayerUseEvent,
|
||||||
|
PlayerChangeNameEvent,
|
||||||
|
PlayerHintMessageEvent,
|
||||||
|
BasePlayerTeleportedEvent,
|
||||||
|
GameInitEvent,
|
||||||
|
GameNewMapEvent,
|
||||||
|
GameStartEvent,
|
||||||
|
GameEndEvent,
|
||||||
|
RoundStartEvent,
|
||||||
|
RoundEndEvent,
|
||||||
|
GameMessageEvent,
|
||||||
|
BreakBreakableEvent,
|
||||||
|
BreakPropEvent,
|
||||||
|
EntityKilledEvent,
|
||||||
|
BonusUpdatedEvent,
|
||||||
|
AchievementEventEvent,
|
||||||
|
AchievementIncrementEvent,
|
||||||
|
PhysgunPickupEvent,
|
||||||
|
FlareIgniteNpcEvent,
|
||||||
|
HelicopterGrenadePuntMissEvent,
|
||||||
|
UserDataDownloadedEvent,
|
||||||
|
RagdollDissolvedEvent,
|
||||||
|
HLTVChangedModeEvent,
|
||||||
|
HLTVChangedTargetEvent,
|
||||||
|
VoteEndedEvent,
|
||||||
|
VoteStartedEvent,
|
||||||
|
VoteChangedEvent,
|
||||||
|
VotePassedEvent,
|
||||||
|
VoteFailedEvent,
|
||||||
|
VoteCastEvent,
|
||||||
|
VoteOptionsEvent,
|
||||||
|
ReplaySavedEvent,
|
||||||
|
EnteredPerformanceModeEvent,
|
||||||
|
BrowseReplaysEvent,
|
||||||
|
ReplayYoutubeStatsEvent,
|
||||||
|
InventoryUpdatedEvent,
|
||||||
|
CartUpdatedEvent,
|
||||||
|
StorePricesheetUpdatedEvent,
|
||||||
|
GcConnectedEvent,
|
||||||
|
ItemSchemaInitializedEvent,
|
||||||
|
IntroFinishEvent,
|
||||||
|
IntroNextCameraEvent,
|
||||||
|
MmLobbyChatEvent,
|
||||||
|
MmLobbyMemberJoinEvent,
|
||||||
|
MmLobbyMemberLeaveEvent,
|
||||||
|
PlayerChangeClassEvent,
|
||||||
|
TfMapTimeRemainingEvent,
|
||||||
|
TfGameOverEvent,
|
||||||
|
CtfFlagCapturedEvent,
|
||||||
|
ControlPointInitializedEvent,
|
||||||
|
ControlPointUpdateImagesEvent,
|
||||||
|
ControlPointUpdateLayoutEvent,
|
||||||
|
ControlPointUpdateCappingEvent,
|
||||||
|
ControlPointUpdateOwnerEvent,
|
||||||
|
ControlPointStartTouchEvent,
|
||||||
|
ControlPointEndTouchEvent,
|
||||||
|
ControlPointPulseElementEvent,
|
||||||
|
ControlPointFakeCaptureEvent,
|
||||||
|
ControlPointFakeCaptureMultEvent,
|
||||||
|
TeamPlayRoundSelectedEvent,
|
||||||
|
TeamPlayRoundStartEvent,
|
||||||
|
TeamPlayRoundActiveEvent,
|
||||||
|
TeamPlayWaitingBeginsEvent,
|
||||||
|
TeamPlayWaitingEndsEvent,
|
||||||
|
TeamPlayWaitingAboutToEndEvent,
|
||||||
|
TeamPlayRestartRoundEvent,
|
||||||
|
TeamPlayReadyRestartEvent,
|
||||||
|
TeamPlayRoundRestartSecondsEvent,
|
||||||
|
TeamPlayTeamReadyEvent,
|
||||||
|
TeamPlayRoundWinEvent,
|
||||||
|
TeamPlayUpdateTimerEvent,
|
||||||
|
TeamPlayRoundStalemateEvent,
|
||||||
|
TeamPlayOvertimeBeginEvent,
|
||||||
|
TeamPlayOvertimeEndEvent,
|
||||||
|
TeamPlaySuddenDeathBeginEvent,
|
||||||
|
TeamPlaySuddenDeathEndEvent,
|
||||||
|
TeamPlayGameOverEvent,
|
||||||
|
TeamPlayMapTimeRemainingEvent,
|
||||||
|
TeamPlayTimerFlashEvent,
|
||||||
|
TeamPlayTimerTimeAddedEvent,
|
||||||
|
TeamPlayPointStartCaptureEvent,
|
||||||
|
TeamPlayPointCapturedEvent,
|
||||||
|
TeamPlayPointLockedEvent,
|
||||||
|
TeamPlayPointUnlockedEvent,
|
||||||
|
TeamPlayCaptureBrokenEvent,
|
||||||
|
TeamPlayCaptureBlockedEvent,
|
||||||
|
TeamPlayFlagEventEvent,
|
||||||
|
TeamPlayWinPanelEvent,
|
||||||
|
TeamPlayTeambalancedPlayerEvent,
|
||||||
|
TeamPlaySetupFinishedEvent,
|
||||||
|
TeamPlayAlertEvent,
|
||||||
|
TrainingCompleteEvent,
|
||||||
|
ShowFreezePanelEvent,
|
||||||
|
HideFreezePanelEvent,
|
||||||
|
FreezeCamStartedEvent,
|
||||||
|
LocalPlayerChangeTeamEvent,
|
||||||
|
LocalPlayerScoreChangedEvent,
|
||||||
|
LocalPlayerChangeClassEvent,
|
||||||
|
LocalPlayerRespawnEvent,
|
||||||
|
BuildingInfoChangedEvent,
|
||||||
|
LocalPlayerChangeDisguiseEvent,
|
||||||
|
PlayerAccountChangedEvent,
|
||||||
|
SpyPdaResetEvent,
|
||||||
|
FlagStatusUpdateEvent,
|
||||||
|
PlayerStatsUpdatedEvent,
|
||||||
|
PlayingCommentaryEvent,
|
||||||
|
PlayerChargedeployedEvent,
|
||||||
|
PlayerBuiltObjectEvent,
|
||||||
|
PlayerUpgradedObjectEvent,
|
||||||
|
PlayerCarryObjectEvent,
|
||||||
|
PlayerDropObjectEvent,
|
||||||
|
ObjectRemovedEvent,
|
||||||
|
ObjectDestroyedEvent,
|
||||||
|
ObjectDetonatedEvent,
|
||||||
|
AchievementEarnedEvent,
|
||||||
|
SpecTargetUpdatedEvent,
|
||||||
|
TournamentStateUpdateEvent,
|
||||||
|
TournamentEnableCountdownEvent,
|
||||||
|
PlayerCalledForMedicEvent,
|
||||||
|
PlayerAskedForBallEvent,
|
||||||
|
LocalPlayerBecameObserverEvent,
|
||||||
|
PlayerIgnitedInvEvent,
|
||||||
|
PlayerIgnitedEvent,
|
||||||
|
PlayerExtinguishedEvent,
|
||||||
|
PlayerTeleportedEvent,
|
||||||
|
PlayerHealedMedicCallEvent,
|
||||||
|
LocalPlayerChargeReadyEvent,
|
||||||
|
LocalPlayerWinddownEvent,
|
||||||
|
PlayerInvulnedEvent,
|
||||||
|
EscortSpeedEvent,
|
||||||
|
EscortProgressEvent,
|
||||||
|
EscortRecedeEvent,
|
||||||
|
GameUIActivatedEvent,
|
||||||
|
GameUIHiddenEvent,
|
||||||
|
PlayerEscortScoreEvent,
|
||||||
|
PlayerHealOnHitEvent,
|
||||||
|
PlayerStealsandvichEvent,
|
||||||
|
ShowClassLayoutEvent,
|
||||||
|
ShowVsPanelEvent,
|
||||||
|
PlayerDamagedEvent,
|
||||||
|
ArenaPlayerNotificationEvent,
|
||||||
|
ArenaMatchMaxStreakEvent,
|
||||||
|
ArenaRoundStartEvent,
|
||||||
|
ArenaWinPanelEvent,
|
||||||
|
PveWinPanelEvent,
|
||||||
|
AirDashEvent,
|
||||||
|
LandedEvent,
|
||||||
|
PlayerDamageDodgedEvent,
|
||||||
|
PlayerStunnedEvent,
|
||||||
|
ScoutGrandSlamEvent,
|
||||||
|
ScoutSlamdollLandedEvent,
|
||||||
|
ArrowImpactEvent,
|
||||||
|
PlayerJaratedEvent,
|
||||||
|
PlayerJaratedFadeEvent,
|
||||||
|
PlayerShieldBlockedEvent,
|
||||||
|
PlayerPinnedEvent,
|
||||||
|
PlayerHealedByMedicEvent,
|
||||||
|
PlayerSappedObjectEvent,
|
||||||
|
ItemFoundEvent,
|
||||||
|
ShowAnnotationEvent,
|
||||||
|
HideAnnotationEvent,
|
||||||
|
PostInventoryApplicationEvent,
|
||||||
|
ControlPointUnlockUpdatedEvent,
|
||||||
|
DeployBuffBannerEvent,
|
||||||
|
PlayerBuffEvent,
|
||||||
|
MedicDeathEvent,
|
||||||
|
OvertimeNagEvent,
|
||||||
|
TeamsChangedEvent,
|
||||||
|
HalloweenPumpkinGrabEvent,
|
||||||
|
RocketJumpEvent,
|
||||||
|
RocketJumpLandedEvent,
|
||||||
|
StickyJumpEvent,
|
||||||
|
StickyJumpLandedEvent,
|
||||||
|
MedicDefendedEvent,
|
||||||
|
LocalPlayerHealedEvent,
|
||||||
|
PlayerDestroyedPipeBombEvent,
|
||||||
|
ObjectDeflectedEvent,
|
||||||
|
PlayerMvpEvent,
|
||||||
|
RaidSpawnMobEvent,
|
||||||
|
RaidSpawnSquadEvent,
|
||||||
|
NavBlockedEvent,
|
||||||
|
PathTrackPassedEvent,
|
||||||
|
NumCappersChangedEvent,
|
||||||
|
PlayerRegenerateEvent,
|
||||||
|
UpdateStatusItemEvent,
|
||||||
|
StatsResetRoundEvent,
|
||||||
|
ScoreStatsAccumulatedUpdateEvent,
|
||||||
|
ScoreStatsAccumulatedResetEvent,
|
||||||
|
AchievementEarnedLocalEvent,
|
||||||
|
PlayerHealedEvent,
|
||||||
|
BuildingHealedEvent,
|
||||||
|
ItemPickupEvent,
|
||||||
|
DuelStatusEvent,
|
||||||
|
FishNoticeEvent,
|
||||||
|
FishNoticeArmEvent,
|
||||||
|
ThrowableHitEvent,
|
||||||
|
PumpkinLordSummonedEvent,
|
||||||
|
PumpkinLordKilledEvent,
|
||||||
|
MerasmusSummonedEvent,
|
||||||
|
MerasmusKilledEvent,
|
||||||
|
MerasmusEscapeWarningEvent,
|
||||||
|
MerasmusEscapedEvent,
|
||||||
|
EyeballBossSummonedEvent,
|
||||||
|
EyeballBossStunnedEvent,
|
||||||
|
EyeballBossKilledEvent,
|
||||||
|
EyeballBossKillerEvent,
|
||||||
|
EyeballBossEscapeImminentEvent,
|
||||||
|
EyeballBossEscapedEvent,
|
||||||
|
NpcHurtEvent,
|
||||||
|
ControlPointTimerUpdatedEvent,
|
||||||
|
PlayerHighfiveStartEvent,
|
||||||
|
PlayerHighfiveCancelEvent,
|
||||||
|
PlayerHighfiveSuccessEvent,
|
||||||
|
PlayerBonusPointsEvent,
|
||||||
|
PlayerUpgradedEvent,
|
||||||
|
PlayerBuybackEvent,
|
||||||
|
PlayerUsedPowerUpBottleEvent,
|
||||||
|
ChristmasGiftGrabEvent,
|
||||||
|
PlayerKilledAchievementZoneEvent,
|
||||||
|
PartyUpdatedEvent,
|
||||||
|
LobbyUpdatedEvent,
|
||||||
|
MvmMissionUpdateEvent,
|
||||||
|
RecalculateHolidaysEvent,
|
||||||
|
PlayerCurrencyChangedEvent,
|
||||||
|
DoomsdayRocketOpenEvent,
|
||||||
|
RemoveNemesisRelationshipsEvent,
|
||||||
|
MvmCreditBonusWaveEvent,
|
||||||
|
MvmCreditBonusAllEvent,
|
||||||
|
MvmCreditBonusAllAdvancedEvent,
|
||||||
|
MvmQuickSentryUpgradeEvent,
|
||||||
|
MvmTankDestroyedByPlayersEvent,
|
||||||
|
MvmKillRobotDeliveringBombEvent,
|
||||||
|
MvmPickupCurrencyEvent,
|
||||||
|
MvmBombCarrierKilledEvent,
|
||||||
|
MvmSentryBusterDetonateEvent,
|
||||||
|
MvmScoutMarkedForDeathEvent,
|
||||||
|
MvmMedicPowerupSharedEvent,
|
||||||
|
MvmBeginWaveEvent,
|
||||||
|
MvmWaveCompleteEvent,
|
||||||
|
MvmMissionCompleteEvent,
|
||||||
|
MvmBombResetByPlayerEvent,
|
||||||
|
MvmBombAlarmTriggeredEvent,
|
||||||
|
MvmBombDeployResetByPlayerEvent,
|
||||||
|
MvmWaveFailedEvent,
|
||||||
|
MvmResetStatsEvent,
|
||||||
|
DamageResistedEvent,
|
||||||
|
RevivePlayerNotifyEvent,
|
||||||
|
RevivePlayerStoppedEvent,
|
||||||
|
RevivePlayerCompleteEvent,
|
||||||
|
PlayerTurnedToGhostEvent,
|
||||||
|
MedigunShieldBlockedDamageEvent,
|
||||||
|
MvmAdvWaveCompleteNoGatesEvent,
|
||||||
|
MvmSniperHeadshotCurrencyEvent,
|
||||||
|
MvmMannhattanPitEvent,
|
||||||
|
FlagCarriedInDetectionZoneEvent,
|
||||||
|
MvmAdvWaveKilledStunRadioEvent,
|
||||||
|
PlayerDirecthitStunEvent,
|
||||||
|
MvmSentryBusterKilledEvent,
|
||||||
|
UpgradesFileChangedEvent,
|
||||||
|
RdTeamPointsChangedEvent,
|
||||||
|
RdRulesStateChangedEvent,
|
||||||
|
RdRobotKilledEvent,
|
||||||
|
RdRobotImpactEvent,
|
||||||
|
TeamPlayPreRoundTimeLeftEvent,
|
||||||
|
ParachuteDeployEvent,
|
||||||
|
ParachuteHolsterEvent,
|
||||||
|
KillRefillsMeterEvent,
|
||||||
|
RpsTauntEventEvent,
|
||||||
|
CongaKillEvent,
|
||||||
|
PlayerInitialSpawnEvent,
|
||||||
|
CompetitiveVictoryEvent,
|
||||||
|
CompetitiveStatsUpdateEvent,
|
||||||
|
MiniGameWinEvent,
|
||||||
|
SentryOnGoActiveEvent,
|
||||||
|
DuckXpLevelUpEvent,
|
||||||
|
QuestLogOpenedEvent,
|
||||||
|
SchemaUpdatedEvent,
|
||||||
|
LocalPlayerPickupWeaponEvent,
|
||||||
|
RdPlayerScorePointsEvent,
|
||||||
|
DemomanDetStickiesEvent,
|
||||||
|
QuestObjectiveCompletedEvent,
|
||||||
|
PlayerScoreChangedEvent,
|
||||||
|
KilledCappingPlayerEvent,
|
||||||
|
EnvironmentalDeathEvent,
|
||||||
|
ProjectileDirectHitEvent,
|
||||||
|
PassGetEvent,
|
||||||
|
PassScoreEvent,
|
||||||
|
PassFreeEvent,
|
||||||
|
PassPassCaughtEvent,
|
||||||
|
PassBallStolenEvent,
|
||||||
|
PassBallBlockedEvent,
|
||||||
|
DamagePreventedEvent,
|
||||||
|
HalloweenBossKilledEvent,
|
||||||
|
EscapedLootIslandEvent,
|
||||||
|
TaggedPlayerAsItEvent,
|
||||||
|
MerasmusStunnedEvent,
|
||||||
|
MerasmusPropFoundEvent,
|
||||||
|
HalloweenSkeletonKilledEvent,
|
||||||
|
EscapeHellEvent,
|
||||||
|
CrossSpectralBridgeEvent,
|
||||||
|
MiniGameWonEvent,
|
||||||
|
RespawnGhostEvent,
|
||||||
|
KillInHellEvent,
|
||||||
|
HalloweenDuckCollectedEvent,
|
||||||
|
SpecialScoreEvent,
|
||||||
|
TeamLeaderKilledEvent,
|
||||||
|
HalloweenSoulCollectedEvent,
|
||||||
|
RecalculateTruceEvent,
|
||||||
|
DeadringerCheatDeathEvent,
|
||||||
|
CrossbowHealEvent,
|
||||||
|
DamageMitigatedEvent,
|
||||||
|
PayloadPushedEvent,
|
||||||
|
PlayerAbandonedMatchEvent,
|
||||||
|
ClDrawlineEvent,
|
||||||
|
RestartTimerTimeEvent,
|
||||||
|
WinLimitChangedEvent,
|
||||||
|
WinPanelShowScoresEvent,
|
||||||
|
TopStreamsRequestFinishedEvent,
|
||||||
|
CompetitiveStateChangedEvent,
|
||||||
|
GlobalWarDataUpdatedEvent,
|
||||||
|
StopWatchChangedEvent,
|
||||||
|
DsStopEvent,
|
||||||
|
DsScreenshotEvent,
|
||||||
|
ShowMatchSummaryEvent,
|
||||||
|
ExperienceChangedEvent,
|
||||||
|
BeginXpLerpEvent,
|
||||||
|
MatchmakerStatsUpdatedEvent,
|
||||||
|
RematchVotePeriodOverEvent,
|
||||||
|
RematchFailedToCreateEvent,
|
||||||
|
PlayerRematchChangeEvent,
|
||||||
|
PingUpdatedEvent,
|
||||||
|
PlayerNextMapVoteChangeEvent,
|
||||||
|
VoteMapsChangedEvent,
|
||||||
|
HLTVStatusEvent,
|
||||||
|
HLTVCameramanEvent,
|
||||||
|
HLTVRankCameraEvent,
|
||||||
|
HLTVRankEntityEvent,
|
||||||
|
HLTVFixedEvent,
|
||||||
|
HLTVChaseEvent,
|
||||||
|
HLTVMessageEvent,
|
||||||
|
HLTVTitleEvent,
|
||||||
|
HLTVChatEvent,
|
||||||
|
ReplayStartRecordEvent,
|
||||||
|
ReplaySessionInfoEvent,
|
||||||
|
ReplayEndRecordEvent,
|
||||||
|
ReplayReplaysAvailableEvent,
|
||||||
|
ReplayServerErrorEvent,
|
||||||
|
}
|
||||||
62
src/demo/header.rs
Normal file
62
src/demo/header.rs
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
use crate::{ParseError, Parse, Result, Stream, ParserState};
|
||||||
|
//use crate::test::test_parse;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub struct Header {
|
||||||
|
pub demo_type: String,
|
||||||
|
pub version: u32,
|
||||||
|
pub protocol: u32,
|
||||||
|
pub server: String,
|
||||||
|
pub nick: String,
|
||||||
|
pub map: String,
|
||||||
|
pub game: String,
|
||||||
|
pub duration: f32,
|
||||||
|
pub ticks: u32,
|
||||||
|
pub frames: u32,
|
||||||
|
pub sigon: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Parse<'a> for Header {
|
||||||
|
fn parse(stream: &mut Stream, _state: &ParserState) -> Result<Header> {
|
||||||
|
Ok(Header {
|
||||||
|
demo_type: stream.read_string(Some(8))?,
|
||||||
|
version: stream.read(32)?,
|
||||||
|
protocol: stream.read(32)?,
|
||||||
|
server: stream.read_string(Some(260))?,
|
||||||
|
nick: stream.read_string(Some(260))?,
|
||||||
|
map: stream.read_string(Some(260))?,
|
||||||
|
game: stream.read_string(Some(260))?,
|
||||||
|
duration: stream.read_float()?,
|
||||||
|
ticks: stream.read(32)?,
|
||||||
|
frames: stream.read(32)?,
|
||||||
|
sigon: stream.read(32)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn skip(stream: &mut Stream) -> Result<()> {
|
||||||
|
stream
|
||||||
|
.skip(8 * 8 + (32 * 2) + (260 * 8 * 4) + (32 * 4))
|
||||||
|
.map_err(ParseError::from)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//#[test]
|
||||||
|
//fn test_header() {
|
||||||
|
// test_parse(
|
||||||
|
// "data/small.dem",
|
||||||
|
// Header {
|
||||||
|
// demo_type: "HL2DEMO".to_owned(),
|
||||||
|
// version: 3,
|
||||||
|
// protocol: 24,
|
||||||
|
// server: "localhost:27015".to_owned(),
|
||||||
|
// nick: "Icewind | demos.tf".to_owned(),
|
||||||
|
// map: "cp_gullywash".to_owned(),
|
||||||
|
// game: "tf".to_owned(),
|
||||||
|
// duration: 1.7249999,
|
||||||
|
// ticks: 115,
|
||||||
|
// frames: 111,
|
||||||
|
// sigon: 218908,
|
||||||
|
// },
|
||||||
|
// 0,
|
||||||
|
// );
|
||||||
|
//}
|
||||||
40
src/demo/mod.rs
Normal file
40
src/demo/mod.rs
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
use bitstream_reader::{BitBuffer, BitStream, LittleEndian, NonPadded};
|
||||||
|
use rentals::OwnedBuffer;
|
||||||
|
|
||||||
|
pub mod gamevent;
|
||||||
|
pub mod header;
|
||||||
|
pub mod sendprop;
|
||||||
|
pub mod vector;
|
||||||
|
pub mod parser;
|
||||||
|
pub mod packet;
|
||||||
|
|
||||||
|
pub type Buffer<'a> = BitBuffer<'a, LittleEndian, NonPadded>;
|
||||||
|
pub type Stream<'a> = BitStream<'a, LittleEndian, NonPadded>;
|
||||||
|
|
||||||
|
rental! {
|
||||||
|
mod rentals {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[rental_mut(covariant)]
|
||||||
|
pub struct OwnedBuffer {
|
||||||
|
source_vec: Vec<u8>,
|
||||||
|
stream: Buffer<'source_vec>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Demo {
|
||||||
|
data: OwnedBuffer,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Demo {
|
||||||
|
pub fn new(vec: Vec<u8>) -> Self {
|
||||||
|
let data = OwnedBuffer::new(vec, |bytes| BitBuffer::new(bytes, LittleEndian));
|
||||||
|
Demo { data }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a new stream with the data of the demo
|
||||||
|
pub fn get_stream(&self) -> Stream {
|
||||||
|
BitStream::new(self.data.suffix().clone(), None, None)
|
||||||
|
}
|
||||||
|
}
|
||||||
23
src/demo/packet/consolecmd.rs
Normal file
23
src/demo/packet/consolecmd.rs
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
use crate::{Parse, ParseError, ParserState, Result, Stream};
|
||||||
|
|
||||||
|
pub struct ConsoleCmdPacket {
|
||||||
|
tick: u32,
|
||||||
|
command: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Parse<'a> for ConsoleCmdPacket {
|
||||||
|
fn parse(stream: &mut Stream, _state: &ParserState) -> Result<Self> {
|
||||||
|
let tick = stream.read(32)?;
|
||||||
|
let len = stream.read(32)?;
|
||||||
|
Ok(ConsoleCmdPacket {
|
||||||
|
tick,
|
||||||
|
command: stream.read_string(Some(len))?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn skip(stream: &mut Stream) -> Result<()> {
|
||||||
|
let _ = stream.skip(32)?;
|
||||||
|
let len = stream.read(32)?;
|
||||||
|
stream.skip(len).map_err(ParseError::from)
|
||||||
|
}
|
||||||
|
}
|
||||||
46
src/demo/packet/mod.rs
Normal file
46
src/demo/packet/mod.rs
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
use crate::{Parse, ParseError, ParserState, Result, Stream};
|
||||||
|
use num_traits::FromPrimitive;
|
||||||
|
use self::stop::Stop;
|
||||||
|
|
||||||
|
mod stop;
|
||||||
|
mod synctick;
|
||||||
|
mod consolecmd;
|
||||||
|
mod usercmd;
|
||||||
|
mod stringtable;
|
||||||
|
|
||||||
|
pub enum Packet {
|
||||||
|
Stop(Stop)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Primitive)]
|
||||||
|
pub enum PacketType {
|
||||||
|
Sigon = 1,
|
||||||
|
Packet = 2,
|
||||||
|
SyncTick = 3,
|
||||||
|
ConsoleCmd = 4,
|
||||||
|
UserCmd = 5,
|
||||||
|
DataTables = 6,
|
||||||
|
Stop = 7,
|
||||||
|
StringTables = 8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Packet {
|
||||||
|
fn read_type(stream: &mut Stream) -> Result<PacketType> {
|
||||||
|
let raw = stream.read(8)?;
|
||||||
|
let packet_type: Option<PacketType> = PacketType::from_u8(raw);
|
||||||
|
packet_type.ok_or(ParseError::InvalidPacketType(raw))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//impl Parse for Packet {
|
||||||
|
// fn parse(stream: &mut Stream, _state: &ParserState) -> Result<Self> {
|
||||||
|
// let packet_type = Packet::read_type(stream);
|
||||||
|
// match packet_type {
|
||||||
|
// Sigon => {}
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// fn skip(stream: &mut Stream) -> Result<()> {
|
||||||
|
// Ok(())
|
||||||
|
// }
|
||||||
|
//}
|
||||||
13
src/demo/packet/stop.rs
Normal file
13
src/demo/packet/stop.rs
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
use crate::{Parse, ParserState, Result, Stream};
|
||||||
|
|
||||||
|
pub struct Stop;
|
||||||
|
|
||||||
|
impl<'a> Parse<'a> for Stop {
|
||||||
|
fn parse(_stream: &mut Stream, _state: &ParserState) -> Result<Self> {
|
||||||
|
Ok(Stop)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn skip(_stream: &mut Stream) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
91
src/demo/packet/stringtable.rs
Normal file
91
src/demo/packet/stringtable.rs
Normal file
|
|
@ -0,0 +1,91 @@
|
||||||
|
use crate::{Parse, ParseError, ParserState, Result, Stream};
|
||||||
|
|
||||||
|
pub struct StringTable<'a> {
|
||||||
|
name: String,
|
||||||
|
entries: Vec<StringTableEntry<'a>>,
|
||||||
|
max_entries: u32,
|
||||||
|
fixed_userdata_size: Option<u32>,
|
||||||
|
fixed_userdata_size_bits: Option<u32>,
|
||||||
|
client_entries: Option<Vec<StringTableEntry<'a>>>,
|
||||||
|
compressed: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> StringTable<'a> {
|
||||||
|
fn parse(stream: &mut Stream<'a>) -> Result<Self> {
|
||||||
|
let name = stream.read_string(None)?;
|
||||||
|
let entry_count: u32 = stream.read(16)?;
|
||||||
|
let mut entries = Vec::with_capacity(entry_count as usize);
|
||||||
|
for _ in 0..entry_count {
|
||||||
|
entries.push(StringTableEntry::parse(stream)?);
|
||||||
|
}
|
||||||
|
|
||||||
|
let client_entries = if stream.read_bool()? {
|
||||||
|
let count = stream.read(16)?;
|
||||||
|
let mut vec = Vec::with_capacity(count);
|
||||||
|
for _ in 0..count {
|
||||||
|
vec.push(StringTableEntry::parse(stream)?);
|
||||||
|
}
|
||||||
|
Some(vec)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(StringTable {
|
||||||
|
name,
|
||||||
|
entries,
|
||||||
|
max_entries: entry_count,
|
||||||
|
fixed_userdata_size: None,
|
||||||
|
fixed_userdata_size_bits: None,
|
||||||
|
client_entries,
|
||||||
|
compressed: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct StringTableEntry<'a> {
|
||||||
|
text: String,
|
||||||
|
extra_data: Option<Stream<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> StringTableEntry<'a> {
|
||||||
|
fn parse(stream: &mut Stream<'a>) -> Result<Self> {
|
||||||
|
let text = stream.read_string(None)?;
|
||||||
|
let extra_data = if stream.read_bool()? {
|
||||||
|
let byte_len: usize = stream.read(16)?;
|
||||||
|
Some(stream.read_bits(byte_len * 8)?)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
Ok(StringTableEntry {
|
||||||
|
text,
|
||||||
|
extra_data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct StringTablePacket<'a> {
|
||||||
|
tick: u32,
|
||||||
|
tables: Vec<StringTable<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Parse<'a> for StringTablePacket<'a> {
|
||||||
|
fn parse(stream: &mut Stream<'a>, _state: &ParserState<'a>) -> Result<Self> {
|
||||||
|
let tick = stream.read(32)?;
|
||||||
|
let length: usize = stream.read(32)?;
|
||||||
|
let count: u32 = stream.read(8)?;
|
||||||
|
let mut tables = Vec::with_capacity(count as usize);
|
||||||
|
for _ in 0..count {
|
||||||
|
tables.push(StringTable::parse(stream)?);
|
||||||
|
}
|
||||||
|
Ok(StringTablePacket {
|
||||||
|
tick,
|
||||||
|
tables,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn skip(stream: &mut Stream) -> Result<()> {
|
||||||
|
let _ = stream.skip(32)?;
|
||||||
|
let length = stream.read(32)?;
|
||||||
|
stream.skip(length).map_err(ParseError::from)
|
||||||
|
}
|
||||||
|
}
|
||||||
17
src/demo/packet/synctick.rs
Normal file
17
src/demo/packet/synctick.rs
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
use crate::{Parse, ParseError, ParserState, Result, Stream};
|
||||||
|
|
||||||
|
pub struct SyncTickPacket {
|
||||||
|
tick: u32
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Parse<'a> for SyncTickPacket {
|
||||||
|
fn parse(stream: &mut Stream, _state: &ParserState) -> Result<Self> {
|
||||||
|
Ok(SyncTickPacket {
|
||||||
|
tick: stream.read(32)?
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn skip(stream: &mut Stream) -> Result<()> {
|
||||||
|
stream.skip(32).map_err(ParseError::from)
|
||||||
|
}
|
||||||
|
}
|
||||||
26
src/demo/packet/usercmd.rs
Normal file
26
src/demo/packet/usercmd.rs
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
use crate::{Parse, ParseError, ParserState, Result, Stream};
|
||||||
|
|
||||||
|
pub struct UserCmdPacket {
|
||||||
|
tick: u32,
|
||||||
|
command: String,
|
||||||
|
sequence_out: u32
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Parse<'a> for UserCmdPacket {
|
||||||
|
fn parse(stream: &mut Stream, _state: &ParserState) -> Result<Self> {
|
||||||
|
let tick = stream.read(32)?;
|
||||||
|
let sequence_out = stream.read(32)?;
|
||||||
|
let len = stream.read(32)?;
|
||||||
|
Ok(UserCmdPacket {
|
||||||
|
tick,
|
||||||
|
sequence_out,
|
||||||
|
command: stream.read_string(Some(len))?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn skip(stream: &mut Stream) -> Result<()> {
|
||||||
|
let _ = stream.skip(64)?;
|
||||||
|
let len = stream.read(32)?;
|
||||||
|
stream.skip(len).map_err(ParseError::from)
|
||||||
|
}
|
||||||
|
}
|
||||||
59
src/demo/parser/mod.rs
Normal file
59
src/demo/parser/mod.rs
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
use bitstream_reader::ReadError;
|
||||||
|
pub use crate::demo::parser::state::ParserState;
|
||||||
|
use crate::Stream;
|
||||||
|
|
||||||
|
mod state;
|
||||||
|
|
||||||
|
/// Errors that can occur during parsing
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum ParseError {
|
||||||
|
/// Error while reading bits from stream
|
||||||
|
ReadError(ReadError),
|
||||||
|
/// Packet identifier is invalid
|
||||||
|
InvalidPacketType(u8),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ReadError> for ParseError {
|
||||||
|
fn from(err: ReadError) -> ParseError {
|
||||||
|
ParseError::ReadError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Result<T> = std::result::Result<T, ParseError>;
|
||||||
|
|
||||||
|
pub trait Parse<'a>: Sized {
|
||||||
|
fn parse(stream: &mut Stream<'a>, state: &ParserState<'a>) -> Result<Self>;
|
||||||
|
fn skip(stream: &mut Stream) -> Result<()>;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub struct DemoParser<'a> {
|
||||||
|
stream: Stream<'a>,
|
||||||
|
state: ParserState<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> DemoParser<'a> {
|
||||||
|
pub fn new(stream: Stream<'a>) -> Self {
|
||||||
|
DemoParser {
|
||||||
|
state: ParserState::new(&stream),
|
||||||
|
stream,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read<T: Parse<'a>>(&mut self) -> Result<T> {
|
||||||
|
T::parse(&mut self.stream, &self.state)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn skip<T: Parse<'a>>(&mut self) -> Result<()> {
|
||||||
|
T::skip(&mut self.stream)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stream_pos(&self) -> usize {
|
||||||
|
self.stream.pos()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_stream_pos(&mut self, pos: usize) -> Result<()> {
|
||||||
|
self.stream.set_pos(pos)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
26
src/demo/parser/state.rs
Normal file
26
src/demo/parser/state.rs
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
use crate::demo::gamevent::GameEventDefinition;
|
||||||
|
use crate::demo::sendprop::SendProp;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use crate::Stream;
|
||||||
|
|
||||||
|
pub struct ParserState<'a> {
|
||||||
|
pub version: u32,
|
||||||
|
pub static_baselines: HashMap<u32, StaticBaseline<'a>>,
|
||||||
|
pub event_definitions: HashMap<u32, GameEventDefinition>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct StaticBaseline<'a> {
|
||||||
|
class_id: u32,
|
||||||
|
raw: Stream<'a>,
|
||||||
|
parsed: Option<Vec<SendProp>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ParserState<'a> {
|
||||||
|
pub fn new(_stream: &Stream<'a>) -> Self {
|
||||||
|
ParserState {
|
||||||
|
version: 0,
|
||||||
|
static_baselines: HashMap::new(),
|
||||||
|
event_definitions: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
17
src/demo/sendprop.rs
Normal file
17
src/demo/sendprop.rs
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
use super::vector::{Vector, VectorXY};
|
||||||
|
|
||||||
|
pub struct SendPropDefinition {}
|
||||||
|
|
||||||
|
pub enum SendPropType {
|
||||||
|
Vector(Vector),
|
||||||
|
VectorXY(VectorXY),
|
||||||
|
Integer(i32),
|
||||||
|
Float(f32),
|
||||||
|
String(String),
|
||||||
|
Array(Vec<SendPropType>),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SendProp {
|
||||||
|
definition: SendPropDefinition,
|
||||||
|
value: SendPropType,
|
||||||
|
}
|
||||||
10
src/demo/vector.rs
Normal file
10
src/demo/vector.rs
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
pub struct Vector {
|
||||||
|
pub x: f32,
|
||||||
|
pub y: f32,
|
||||||
|
pub z: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct VectorXY {
|
||||||
|
pub x: f32,
|
||||||
|
pub y: f32,
|
||||||
|
}
|
||||||
16
src/main.rs
Normal file
16
src/main.rs
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
#[macro_use]
|
||||||
|
extern crate rental;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate enum_primitive_derive;
|
||||||
|
extern crate num_traits;
|
||||||
|
|
||||||
|
pub use demo::{Demo, parser::{Parse, ParseError, ParserState, Result, DemoParser}, Stream};
|
||||||
|
|
||||||
|
mod demo;
|
||||||
|
mod state;
|
||||||
|
mod test;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println!("Hello, world!");
|
||||||
|
}
|
||||||
|
|
||||||
0
src/state.rs
Normal file
0
src/state.rs
Normal file
17
src/test.rs
Normal file
17
src/test.rs
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
use crate::*;
|
||||||
|
use std::fmt::Debug;
|
||||||
|
use std::fs;
|
||||||
|
|
||||||
|
//pub fn test_parse<'a, T: Parse<'a> + Debug + PartialEq>(path: &str, expected: T, offset: usize) {
|
||||||
|
// let file = fs::read(path).expect("Unable to read file");
|
||||||
|
// let demo = Demo::new(file);
|
||||||
|
// let stream: Stream = demo.get_stream();
|
||||||
|
// let mut parser = DemoParser::new(stream);
|
||||||
|
// let _ = parser.set_stream_pos(offset);
|
||||||
|
// let result = parser.read().unwrap();
|
||||||
|
// assert_eq!(expected, result);
|
||||||
|
// let end_pos = parser.stream_pos();
|
||||||
|
// let _ = parser.set_stream_pos(offset);
|
||||||
|
// let _ = parser.skip::<T>();
|
||||||
|
// assert_eq!(parser.stream_pos(), end_pos);
|
||||||
|
//}
|
||||||
15
tf-demo-parser.iml
Normal file
15
tf-demo-parser.iml
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="RUST_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||||
|
<exclude-output />
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/examples" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/benches" isTestSource="true" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue