@@ -272,9 +272,12 @@ public IEnumerable<Player> PlayingParticipants {
272272 /// <summary>
273273 /// The stream of the demo - all the information go here
274274 /// </summary>
275- private readonly IBitStream BitStream ;
276-
275+ private IBitStream BitStream ;
277276
277+ /// <summary>
278+ /// The file stream read by BitStream
279+ /// </summary>
280+ private Stream Input ;
278281
279282 /// <summary>
280283 /// A parser for DataTables. This contains the ServerClasses and DataTables.
@@ -432,15 +435,21 @@ public string TFlag
432435 /// </summary>
433436 /// <value>The tick rate.</value>
434437 public float TickRate {
435- get { return 1 / ServerInfo . TickInterval ; }
438+ get { return this . Header . PlaybackFrames / this . Header . PlaybackTime ; }
436439 }
437440
441+ /// <summary>
442+ /// Used to impute header time values when they're corrupted
443+ /// TickInterval = 1/TickRate
444+ /// </summary>
445+ internal float TickInterval ;
446+
438447 /// <summary>
439448 /// How long a tick of the demo is in s^-1
440449 /// </summary>
441450 /// <value>The tick time.</value>
442451 public float TickTime {
443- get { return ServerInfo . TickInterval ; }
452+ get { return this . Header . PlaybackTime / this . Header . PlaybackFrames ; }
444453 }
445454
446455 /// <summary>
@@ -463,6 +472,11 @@ public float ParsingProgess {
463472 /// <value>The current tick.</value>
464473 public int CurrentTick { get ; private set ; }
465474
475+ /// <summary>
476+ /// The CurrentTick for the last tick where IngameTick == 0
477+ /// </summary>
478+ private int TickAtStart ;
479+
466480 /// <summary>
467481 /// The current ingame-tick as reported by the demo-file.
468482 /// </summary>
@@ -488,6 +502,8 @@ public float ParsingProgess {
488502 /// <param name="input">An input-stream.</param>
489503 public DemoParser ( Stream input )
490504 {
505+ Input = input ;
506+
491507 BitStream = BitStreamUtil . Create ( input ) ;
492508
493509 for ( int i = 0 ; i < MAXPLAYERS ; i ++ ) {
@@ -512,10 +528,143 @@ public void ParseHeader()
512528
513529 Header = header ;
514530
515-
516531 if ( HeaderParsed != null )
517532 HeaderParsed ( this , new HeaderParsedEventArgs ( Header ) ) ;
518- }
533+
534+ if ( header . PlaybackTime == 0 ) //header is corrupted
535+ FixHeader ( ) ;
536+ }
537+
538+ void FixHeader ( )
539+ {
540+ ExpressParseToEnd ( ) ;
541+ Header . PlaybackTicks = IngameTick ;
542+ Header . PlaybackFrames = CurrentTick - ( TickAtStart + 1 ) ;
543+ Header . PlaybackTime = TickInterval * IngameTick ;
544+
545+ CurrentTick = 0 ;
546+
547+ //the ol' switcheroo
548+ //It's probably safest and easiest to
549+ //swap out for a fresh bitstream.
550+ Input . Seek ( 0 , System . IO . SeekOrigin . Begin ) ;
551+ BitStream = BitStreamUtil . Create ( Input ) ;
552+ //Put the new stream back at the end of the header
553+ ExpressParseHeader ( BitStream ) ;
554+ }
555+
556+ //These express functions mirror functions without the express prefix,
557+ //but all they do is read ticks and get ServerInfo. They do not parse
558+ //data from any other packets.
559+ void ExpressParseToEnd ( )
560+ {
561+ while ( true )
562+ {
563+ try
564+ {
565+ if ( ! ExpressParseTick ( ) )
566+ break ;
567+ }
568+ catch ( System . IO . EndOfStreamException )
569+ {
570+ break ;
571+ }
572+ }
573+ }
574+
575+
576+ bool ExpressParseTick ( )
577+ {
578+ DemoCommand command = ( DemoCommand ) BitStream . ReadByte ( ) ;
579+
580+ IngameTick = ( int ) BitStream . ReadInt ( 32 ) ; // tick number
581+
582+ if ( IngameTick == 0 )
583+ TickAtStart = CurrentTick ;
584+
585+ BitStream . ReadByte ( ) ; // player slot
586+
587+ this . CurrentTick ++ ; // = TickNum;
588+
589+ switch ( command )
590+ {
591+ case DemoCommand . Synctick :
592+ break ;
593+ case DemoCommand . Stop :
594+ return false ;
595+ case DemoCommand . ConsoleCommand :
596+ BitStream . BeginChunk ( BitStream . ReadSignedInt ( 32 ) * 8 ) ;
597+ BitStream . EndChunk ( ) ;
598+ break ;
599+ case DemoCommand . DataTables :
600+ BitStream . BeginChunk ( BitStream . ReadSignedInt ( 32 ) * 8 ) ;
601+ BitStream . EndChunk ( ) ;
602+ break ;
603+ case DemoCommand . StringTables :
604+ BitStream . BeginChunk ( BitStream . ReadSignedInt ( 32 ) * 8 ) ;
605+ BitStream . EndChunk ( ) ;
606+ break ;
607+ case DemoCommand . UserCommand :
608+ BitStream . ReadInt ( 32 ) ;
609+ BitStream . BeginChunk ( BitStream . ReadSignedInt ( 32 ) * 8 ) ;
610+ BitStream . EndChunk ( ) ;
611+ break ;
612+ case DemoCommand . Signon :
613+ case DemoCommand . Packet :
614+ ExpressParseDemoPacket ( ) ;
615+ break ;
616+ default :
617+ throw new Exception ( "Can't handle Demo-Command " + command ) ;
618+ }
619+
620+ return true ;
621+ }
622+
623+ void ExpressParseDemoPacket ( )
624+ {
625+ //Read a command-info. Contains no really useful information afaik.
626+ CommandInfo . Parse ( BitStream ) ;
627+ BitStream . ReadInt ( 32 ) ; // SeqNrIn
628+ BitStream . ReadInt ( 32 ) ; // SeqNrOut
629+
630+ BitStream . BeginChunk ( BitStream . ReadSignedInt ( 32 ) * 8 ) ;
631+ ExpressParsePacket ( BitStream , this ) ;
632+ BitStream . EndChunk ( ) ;
633+ }
634+
635+ void ExpressParsePacket ( IBitStream bitstream , DemoParser demo )
636+ {
637+ while ( ! bitstream . ChunkFinished )
638+ {
639+ int cmd = bitstream . ReadProtobufVarInt ( ) ; //What type of packet is this?
640+ int length = bitstream . ReadProtobufVarInt ( ) ; //And how long is it?
641+ bitstream . BeginChunk ( length * 8 ) ; //read length bytes
642+ if ( cmd == ( int ) SVC_Messages . svc_ServerInfo )
643+ {
644+ new ServerInfo ( ) . Parse ( bitstream , demo ) ;
645+ }
646+ bitstream . EndChunk ( ) ;
647+ }
648+ }
649+
650+ void ExpressParseHeader ( IBitStream reader )
651+ {
652+ var MAX_OSPATH = DemoHeader . MAX_OSPATH ;
653+
654+ reader . ReadCString ( 8 ) ;
655+ reader . ReadSignedInt ( 32 ) ;
656+ reader . ReadSignedInt ( 32 ) ;
657+ reader . ReadCString ( MAX_OSPATH ) ;
658+
659+ reader . ReadCString ( MAX_OSPATH ) ;
660+ reader . ReadCString ( MAX_OSPATH ) ;
661+ reader . ReadCString ( MAX_OSPATH ) ;
662+ reader . ReadFloat ( ) ;
663+
664+ reader . ReadSignedInt ( 32 ) ;
665+ reader . ReadSignedInt ( 32 ) ;
666+ reader . ReadSignedInt ( 32 ) ;
667+ }
519668
520669 /// <summary>
521670 /// Parses this file until the end of the demo is reached.
@@ -599,6 +748,7 @@ private bool ParseTick()
599748 DemoCommand command = ( DemoCommand ) BitStream . ReadByte ( ) ;
600749
601750 IngameTick = ( int ) BitStream . ReadInt ( 32 ) ; // tick number
751+
602752 BitStream . ReadByte ( ) ; // player slot
603753
604754 this . CurrentTick ++ ; // = TickNum;
@@ -652,7 +802,7 @@ private void ParseDemoPacket()
652802 {
653803 //Read a command-info. Contains no really useful information afaik.
654804 CommandInfo . Parse ( BitStream ) ;
655- BitStream . ReadInt ( 32 ) ; // SeqNrIn
805+ uint seqin = BitStream . ReadInt ( 32 ) ; // SeqNrIn
656806 BitStream . ReadInt ( 32 ) ; // SeqNrOut
657807
658808 BitStream . BeginChunk ( BitStream . ReadSignedInt ( 32 ) * 8 ) ;
0 commit comments