public static DebugString Deserialize(BinaryReader br)
 {
     var ds = new DebugString();
     ds.FrameNumber = br.ReadInt32();
     ds.Username = br.ReadString2();
     ds.Text = br.ReadString2();
     return ds;
 }
 public static EnumPropertyValue Deserialize(BinaryReader br)
 {
     var epv = new EnumPropertyValue()
     {
         Type = br.ReadString2(),
         Value = br.ReadString2()
     };
     return epv;
 }
 public static ClassIndex Deserialize(BinaryReader bs)
 {
     var classIndex = new ClassIndex();
     classIndex.Class = bs.ReadString2();
     classIndex.Index = bs.ReadInt32();
     return classIndex;
 }
 public static TickMark Deserialize(BinaryReader bs)
 {
     var tm = new TickMark();
     tm.Type = bs.ReadString2();
     tm.Frame = bs.ReadInt32();
     return tm;
 }
        public static Replay DeserializeHeader(BinaryReader br)
        {
            var replay = new Replay();

            replay.Part1Length = br.ReadInt32();
            replay.Part1Crc = br.ReadUInt32();
            replay.VersionMajor = br.ReadUInt32();
            replay.VersionMinor = br.ReadUInt32();
            replay.Unknown5 = br.ReadString2();

            replay.Properties = PropertyDictionary.Deserialize(br);

            return replay;
        }
        public static Replay Deserialize(BinaryReader br)
        {
            var replay = DeserializeHeader(br);

            try
            {
                replay.Part2Length = br.ReadInt32();
                replay.Part2Crc = br.ReadUInt32();

                replay.LevelLength = br.ReadInt32();
                // looks like sfx data, not level data. shrug
                replay.Levels = new List<Level>();
                for (int i = 0; i < replay.LevelLength; i++)
                {
                    replay.Levels.Add(Level.Deserialize(br));
                }

                replay.KeyFrameLength = br.ReadInt32();
                replay.KeyFrames = new List<KeyFrame>();
                for (int i = 0; i < replay.KeyFrameLength; i++)
                {
                    replay.KeyFrames.Add(KeyFrame.Deserialize(br));
                }

                replay.NetworkStreamLength = br.ReadInt32();
                replay.NetworkStream = new List<byte>();
                for (int i = 0; i < replay.NetworkStreamLength; ++i)
                {
                    replay.NetworkStream.Add(br.ReadByte());
                }

                replay.DebugStringLength = br.ReadInt32();
                replay.DebugStrings = new List<DebugString>();
                for (int i = 0; i < replay.DebugStringLength; i++)
                {
                    replay.DebugStrings.Add(DebugString.Deserialize(br));
                }

                replay.TickMarkLength = br.ReadInt32();
                replay.TickMarks = new List<TickMark>();
                for (int i = 0; i < replay.TickMarkLength; i++)
                {
                    replay.TickMarks.Add(TickMark.Deserialize(br));
                }

                replay.PackagesLength = br.ReadInt32();
                replay.Packages = new List<string>();
                for (int i = 0; i < replay.PackagesLength; i++)
                {
                    replay.Packages.Add(br.ReadString2());
                }

                replay.ObjectLength = br.ReadInt32();
                replay.Objects = new string[replay.ObjectLength];
                for (int i = 0; i < replay.ObjectLength; i++)
                {
                    replay.Objects[i] = br.ReadString2();
                }

                replay.NamesLength = br.ReadInt32();
                replay.Names = new string[replay.NamesLength];
                for (int i = 0; i < replay.NamesLength; i++)
                {
                    replay.Names[i] = br.ReadString2();
                }

                replay.ClassIndexLength = br.ReadInt32();
                replay.ClassIndexes = new List<ClassIndex>();
                for (int i = 0; i < replay.ClassIndexLength; i++)
                {
                    replay.ClassIndexes.Add(ClassIndex.Deserialize(br));
                }

                replay.ClassNetCacheLength = br.ReadInt32();
                replay.ClassNetCaches = new ClassNetCache[replay.ClassNetCacheLength];
                for (int i = 0; i < replay.ClassNetCacheLength; i++)
                {
                    var classNetCache = ClassNetCache.Deserialize(br);
                    replay.ClassNetCaches[i] = classNetCache;

                    for (int j = i - 1; j >= 0; --j)
                    {
                        if (classNetCache.ParentId == replay.ClassNetCaches[j].Id)
                        {
                            classNetCache.Parent = replay.ClassNetCaches[j];
                            replay.ClassNetCaches[j].Children.Add(classNetCache);
                            break;
                        }
                    }

                    if (replay.ClassNetCaches[i].Parent == null)
                    {
                        replay.ClassNetCaches[i].Root = true;
                    }

                }

                // 2016/02/10 patch replays have TAGame.PRI_TA classes with no parent.
                // Deserialization may have failed somehow, but for now manually fix it up.
                replay.FixClassParent("ProjectX.PRI_X", "Engine.PlayerReplicationInfo");
                replay.FixClassParent("TAGame.PRI_TA", "ProjectX.PRI_X");

                // A lot of replays have messed up class hierarchies, commonly giving
                // both Engine.TeamInfo, TAGame.CarComponent_TA, and others the same id.
                // Some ambiguities may have become more common since the 2016-06-20 patch,
                // but there have always been issues.
                //
                // For example, from E8B66F8A4561A2DAACC61FA9FBB710CD:
                //    Index 26(TAGame.CarComponent_TA) ParentId 21 Id 24
                //        Index 28(TAGame.CarComponent_Dodge_TA) ParentId 24 Id 25
                //        Index 188(TAGame.CarComponent_Jump_TA) ParentId 24 Id 24
                //            Index 190(TAGame.CarComponent_DoubleJump_TA) ParentId 24 Id 24
                //    Index 30(Engine.Info) ParentId 21 Id 21
                //        Index 31(Engine.ReplicationInfo) ParentId 21 Id 21
                //            Index 195(Engine.TeamInfo) ParentId 21 Id 24
                //                Index 214(TAGame.CarComponent_Boost_TA) ParentId 24 Id 31
                //                Index 237(TAGame.CarComponent_FlipCar_TA) ParentId 24 Id 26
                // Problems:
                //     TAGame.CarComponent_Jump_TA's parent id and id are both 24 (happens to work fine in this case)
                //     TAGame.CarComponent_DoubleJump_TA's parent id and id are both 24 (incorrectly picks CarComponent_Jump_TA as parent)
                //     Engine.TeamInfo's ID is 24, even though there are 3 other classes with that id
                //     TAGame.CarComponent_Boost_TA's parent is 24 (Incorrectly picks Engine.TeamInfo, since it's ambiguous)
                //     TAGame.CarComponent_FlipCar_TA's parent is 24 (Incorrectly picks Engine.TeamInfo, since it's ambiguous)
                //     Engine.ReplicationInfo and Engine.Info have the same parent id and id (no ill effects so far)
                //
                // Note: The heirarchy problems do not always cause parsing errors! But they can if you're unlucky.

                replay.FixClassParent("TAGame.CarComponent_Boost_TA", "TAGame.CarComponent_TA");
                replay.FixClassParent("TAGame.CarComponent_FlipCar_TA", "TAGame.CarComponent_TA");
                replay.FixClassParent("TAGame.CarComponent_Jump_TA", "TAGame.CarComponent_TA");
                replay.FixClassParent("TAGame.CarComponent_Dodge_TA", "TAGame.CarComponent_TA");
                replay.FixClassParent("TAGame.CarComponent_DoubleJump_TA", "TAGame.CarComponent_TA");
                replay.FixClassParent("TAGame.GameEvent_TA", "Engine.Actor");
                replay.FixClassParent("TAGame.SpecialPickup_TA", "TAGame.CarComponent_TA");
                replay.FixClassParent("TAGame.SpecialPickup_BallVelcro_TA", "TAGame.SpecialPickup_TA");
                replay.FixClassParent("TAGame.SpecialPickup_Targeted_TA", "TAGame.SpecialPickup_TA");
                replay.FixClassParent("TAGame.SpecialPickup_Spring_TA", "TAGame.SpecialPickup_Targeted_TA");
                replay.FixClassParent("TAGame.SpecialPickup_BallLasso_TA", "TAGame.SpecialPickup_Spring_TA");
                replay.FixClassParent("TAGame.SpecialPickup_BoostOverride_TA", "TAGame.SpecialPickup_Targeted_TA");
                replay.FixClassParent("TAGame.SpecialPickup_BallCarSpring_TA", "TAGame.SpecialPickup_Spring_TA");
                replay.FixClassParent("TAGame.SpecialPickup_BallFreeze_TA", "TAGame.SpecialPickup_Targeted_TA");
                replay.FixClassParent("TAGame.SpecialPickup_Swapper_TA", "TAGame.SpecialPickup_Targeted_TA");
                replay.FixClassParent("TAGame.SpecialPickup_GrapplingHook_TA", "TAGame.SpecialPickup_Targeted_TA");
                replay.FixClassParent("TAGame.SpecialPickup_BallGravity_TA", "TAGame.SpecialPickup_TA");
                replay.FixClassParent("TAGame.SpecialPickup_HitForce_TA", "TAGame.SpecialPickup_TA");
                replay.FixClassParent("TAGame.SpecialPickup_Tornado_TA", "TAGame.SpecialPickup_TA");

                // Havent had problems with these yet. They (among others) can be ambiguous,
                // but I havent found a replay yet where my parent choosing algorithm
                // (which picks the matching class that was most recently read) picks the wrong class.
                // Just a safeguard for now.
                replay.FixClassParent("Engine.TeamInfo", "Engine.ReplicationInfo");
                replay.FixClassParent("TAGame.Team_TA", "Engine.TeamInfo");

                replay.Frames = ExtractFrames(replay.MaxChannels(), replay.NetworkStream, replay.KeyFrames.Select(x => x.FilePosition), replay.Objects, replay.ClassNetCaches, replay.VersionMajor, replay.VersionMinor);

                if (br.BaseStream.Position != br.BaseStream.Length)
                {
                    throw new Exception("Extra data somewhere!");
                }

                return replay;
            }
            catch(Exception)
            {
            #if DEBUG
                return replay;
            #else
                throw;
            #endif

            }
        }
 public static Level Deserialize(BinaryReader bs)
 {
     var level = new Level();
     level.Name = bs.ReadString2();
     return level;
 }