public static Vector3D Deserialize2(int maxBits, BitReader br)
        {
            var v = new Vector3D();

            // From ReadPackedVector

            v.NumBits = br.ReadInt32Max(maxBits);

            Int32 Bias = 1 << (v.NumBits + 1);
            Int32 Max = v.NumBits + 2;

            v.DX = br.ReadInt32FromBits(Max);
            v.DY = br.ReadInt32FromBits(Max);
            v.DZ = br.ReadInt32FromBits(Max);

            //float fact = 1; //(float)ScaleFactor; // 1 in our case, doesnt matter

            //v.X = (float)(static_cast<int32>(DX)-Bias) / fact; // Why bother with the static_cast? Why not make DX an int32 instead of uint32 in the first place?
            // always integers, hey?
            v.X = v.DX-Bias;
            v.Y = v.DY-Bias;
            v.Z = v.DZ-Bias;

            return v;
        }
        public static string Deserialize(BitReader br)
        {
            var length = br.ReadInt32();
            if (length > 0)
            {
                var bytes = br.ReadBytes(length);
                return Encoding.GetEncoding(1252).GetString(bytes, 0, length - 1);
            }
            else if (length < 0)
            {
                var bytes = br.ReadBytes(length * -2);
                return Encoding.Unicode.GetString(bytes, 0, (length * -2) - 2);
            }

            return "";
        }
        public static ActorState Deserialize(List<ActorState> existingActorStates, List<ActorState> frameActorStates, IDictionary<int, string> objectIndexToName, IEnumerable<ClassNetCache> classNetCache, BitReader br)
        {
            //var a = new ActorState();
            var startPosition = br.Position;

            var actorId = br.ReadInt32FromBits(10);

            ActorState a = new ActorState();
            a.Id = actorId;

            try
            {
               /* var maxId = existingActorStates.Any() ? existingActorStates.Max(x => x.Id) : -1;
                if (actorId > (maxId + 20))
                {
                    // we're probably lost. Awwww.
                    a.KnownBits = br.GetBits(startPosition, br.Position - startPosition);
                    return a;
                }
                */
                frameActorStates.Add(a);

                if (br.ReadBit())
                {
                    if (br.ReadBit())
                    {
                        a.State = "New";
                        a.Unknown1 = br.ReadBit();

                        a.TypeId = br.ReadInt32();

                        a.TypeName = objectIndexToName[(int)a.TypeId.Value];
                        var classMap = ObjectNameToClassNetCache(a.TypeName, objectIndexToName, classNetCache);
                        a.ClassName = objectIndexToName[classMap.ObjectIndex];

                        // Come up with something more foolproof for this
                        /*
                        if ( a.TypeName.Contains(":"))
                        {
                            // Nope, we think we're a property type. We're lost!
                            a.Failed = true;
                            return a;
                        }
                         * */

                        if (a.ClassName == "TAGame.CrowdActor_TA"
                            || a.ClassName == "TAGame.CrowdManager_TA"
                            || a.ClassName == "TAGame.VehiclePickup_Boost_TA"
                            || a.ClassName == "Core.Object")
                        {
                            a.KnownBits = br.GetBits(startPosition, br.Position - startPosition);
                            a.Complete = true;
                            return a;
                        }

                        a.Position = Vector3D.Deserialize(br);

                        if (a.ClassName == "Engine.GameReplicationInfo"
                            || a.ClassName == "TAGame.GameEvent_SoccarSplitscreen_TA"
                            || a.ClassName == "TAGame.CarComponent_Boost_TA"
                            || a.ClassName == "TAGame.CarComponent_Jump_TA"
                            || a.ClassName == "TAGame.CarComponent_DoubleJump_TA"
                            || a.ClassName == "TAGame.CarComponent_Dodge_TA"
                            || a.ClassName == "TAGame.CarComponent_FlipCar_TA"
                            || a.ClassName == "TAGame.Team_TA"
                            || a.ClassName == "TAGame.PRI_TA"
                            || a.ClassName == "TAGame.GameEvent_Soccar_TA")
                        {
                            a.Complete = true;
                        }
                        else if (a.ClassName == "TAGame.Ball_TA"
                            || a.ClassName == "TAGame.Car_TA")
                        {
                            if (br.ReadBit())
                            {
                                br.ReadByte();
                            }
                            if (br.ReadBit())
                            {
                                br.ReadByte();
                            }
                            if (br.ReadBit())
                            {
                                br.ReadByte();
                            }

                            a.Complete = true;
                        }
                    }
                    else
                    {
                        a.State = "Existing";
                        a.TypeId = existingActorStates.Where(x => x.Id == a.Id).Single().TypeId;
                        a.TypeName = objectIndexToName[(int)a.TypeId.Value];
                        var classMap = ObjectNameToClassNetCache(a.TypeName, objectIndexToName, classNetCache);
                        a.ClassName = objectIndexToName[classMap.ObjectIndex];

                        a.Properties = new List<ActorStateProperty>();
                        ActorStateProperty lastProp = null;
                        while ((lastProp == null || lastProp.IsComplete) && br.ReadBit())
                        {
                            lastProp = ActorStateProperty.Deserialize(classMap, objectIndexToName, br);
                            a.Properties.Add(lastProp);
                        }
                        a.Complete = lastProp.IsComplete;
                        if ( lastProp.Data.Count > 0 && lastProp.Data.Last().ToString() == "FAILED")
                        {
                            a.Failed = true;
                        }
                        var endPosition = br.Position;
                    }
                }
                else
                {
                    a.State = "Deleted";

                    var actor = existingActorStates.Where(x => x.Id == a.Id).SingleOrDefault();
                    if (actor != null) // TODO remove this someday. Only here because we might be deleting objects we havent figured out how to parse yet
                    {
                        a.TypeId = actor.TypeId;
                        a.TypeName = objectIndexToName[(int)a.TypeId.Value];
                        var classMap = ObjectNameToClassNetCache(a.TypeName, objectIndexToName, classNetCache);
                        a.ClassName = objectIndexToName[classMap.ObjectIndex];
                    }
                    a.Complete = true;
                    var endPosition = br.Position;
                }
            }
            catch(Exception)
            {
                // eat exceptions for now
                int g = 56;
                a.Failed = true;
            }
            finally
            {
                a.KnownBits = br.GetBits(startPosition, br.Position - startPosition);
            }

            return a;
        }
        public static Frame Deserialize(ref List<ActorState> existingActorStates, IDictionary<int, string> objectIdToName, IEnumerable<ClassNetCache> classNetCache, int filePosition, bool[] bits)
        {
            var f = new Frame();
            f.Position = filePosition;
            f.BitLength = bits.Length;

            f.RawData = new byte[(int)Math.Ceiling(f.BitLength / 8.0)];
            var ba = new BitArray(bits);
            ba.CopyTo(f.RawData, 0);

            f.Time = BitConverter.ToSingle(f.RawData, 0);
            f.Delta = BitConverter.ToSingle(f.RawData, 4);

            var br = new BitReader(bits);
            br.ReadBitsAsBytes(64); // we already read the time and delta

            f.ActorStates = new List<ActorState>();

            try
            {

                ActorState lastActorState = null;
                while ((lastActorState == null || lastActorState.Complete || lastActorState.ForcedComplete) && br.ReadBit())
                {
                    lastActorState = ActorState.Deserialize(existingActorStates, f.ActorStates, objectIdToName, classNetCache, br);

                    var existingActor = existingActorStates.Where(x => x.Id == lastActorState.Id).SingleOrDefault();
                    if (lastActorState.State != "Deleted")
                    {
                        if (existingActor == null)
                        {
                            existingActorStates.Add(lastActorState);
                        }
                    }
                    else
                    {
                        existingActorStates = existingActorStates.Where(x => x.Id != lastActorState.Id).ToList();
                    }

                    if( !lastActorState.Complete
                        || (!br.PeekBit() && ((br.Length - br.Position) > 1) ) ) // 0 bit, signalling end of frame, but too many bits left to be truly complete
                    {
                        lastActorState.Complete = false;

                        // Try to find the next ActorState
                        // Always deleted first, then new, then existing
                        // Within those states id always increases
                        //     ^ Nope thats not true
                        // Will be stuck in New or Existing land, since Deleted states are easy.
                        // If last was New, next ActorState will look like 1XXXXXXXXXX1
                        // If last was Existing, next ActorState will look like 01XXXXXXXXXX1 (0 prefix from the end of last state (no more properties)
                        var startPosition = br.Position;
                        for(var p = br.Position; p < br.Length - 20; p++ ) // I think the smallest non-deleted actor state will be 20 bits (1 0000000000 10 1 0000 0 0)
                        {
                            br.Seek(p);
                            Int32 potentialId = -1;
                            if (br.ReadBit())
                            {
                                potentialId = br.ReadInt32FromBits(10);

                                if (br.ReadBit()) // 1 for New/Existing
                                {
                                    var newActor = br.ReadBit();

                                    if (newActor && lastActorState.State == "Existing")
                                    {
                                        // Nope, cant have a new actor after an existing actor
                                        continue;
                                    }

                                    if (newActor
                                        && (lastActorState.State == "New")
                                        && (potentialId > lastActorState.Id) // id is increasing
                                        && (potentialId < lastActorState.Id + 100)  // id isnt crazy
                                        && ((br.Length - br.Position) > 33) // have enough bits left for the typeid and unknown bit
                                        )
                                    {
                                        // Heck, lets give it a shot.
                                        lastActorState.ForcedComplete = true;
                                        lastActorState.UnknownBits = br.GetBits(startPosition, p - startPosition);
                                        br.Seek(p);
                                        break;
                                    }

                                    if (!newActor
                                        && (lastActorState.State == "New")
                                        //&& (potentialId <= lastActorState.Id) // Potentially first existing actor after a new actor. Id can be at most equal to last actor id
                                        // I think... We could get lucky enough to have only a New actor that doesnt need extra data, and then Existing data for something else...
                                        && (existingActorStates.Where(x => x.Id == potentialId).Any())  // We have a match in our list
                                        && br.ReadBit()  // Must have a 1 here for "new property
                                        && ((br.Length - br.Position) > 6) // have enough bits lefts for the smallest prop (4 bits for id, 1 for bool value, 1 bit for 'no more properties'
                                        )
                                    {
                                        // Heck, lets give it a shot.
                                        lastActorState.ForcedComplete = true;
                                        lastActorState.UnknownBits = br.GetBits(startPosition, p - startPosition);
                                        br.Seek(p);
                                        break;
                                    }

                                    if (!newActor
                                        && (lastActorState.State == "Existing")
                                        //&& (potentialId > lastActorState.Id) // Id is increasing
                                        && (existingActorStates.Where(x => x.Id == potentialId).Any())  // We have a match in our list
                                        && br.ReadBit()  // Must have a 1 here for "new property
                                        && ((br.Length - br.Position) > 6) // have enough bits lefts for the smallest prop (4 bits for id, 1 for bool value, 1 bit for 'no more properties'
                                        )
                                    {
                                        // Heck, lets give it a shot.
                                        // This same code is in here 3 times, silly. Make it better.
                                        lastActorState.ForcedComplete = true;
                                        lastActorState.UnknownBits = br.GetBits(startPosition, p - startPosition);
                                        br.Seek(p);
                                        break;
                                    }
                                }
                            }
                        }

                        if ( !lastActorState.Complete && !lastActorState.ForcedComplete )
                        {
                            // Didnt find anything. Reset position so unknown bits get recorded properly
                            br.Seek(startPosition);
                        }
                    }

                    //f.ActorStates.Add(lastActorState);
                }

                if (br.EndOfStream && (lastActorState == null || lastActorState.Complete))
                {
                    f.Complete = true;
                }

                if ( lastActorState != null &&lastActorState.Failed )
                {
                    f.Failed = true;
                }
            }
            catch (Exception) { }

            while (!br.EndOfStream)
            {
                f.UnknownBits.Add(br.ReadBit());
            }

            return f;
        }
        public static ActorStateProperty Deserialize(IClassNetCache classMap, IDictionary<int, string> objectIndexToName, BitReader br)
        {
            var asp = new ActorStateProperty();
            var startPosition = br.Position;

            var maxPropId = classMap.MaxPropertyId;
            //var idBitLen = Math.Floor(Math.Log10(maxPropId) / Math.Log10(2)) + 1;

            var className = objectIndexToName[classMap.ObjectIndex];
            asp.PropertyId = br.ReadInt32Max(maxPropId + 1);// br.ReadInt32FromBits((int)idBitLen);
            asp.MaxPropertyId = maxPropId;
            asp.PropertyName = objectIndexToName[classMap.GetProperty(asp.PropertyId).Index];
            asp.Data = new List<object>();
            try
            {
                switch (asp.PropertyName)
                {
                    case "TAGame.GameEvent_TA:ReplicatedStateIndex":
                        asp.Data.Add(br.ReadInt32Max(140)); // number is made up, I dont know the max yet
                        asp.IsComplete = true;
                        break;
                    case "TAGame.RBActor_TA:ReplicatedRBState":

                        asp.Data.Add(br.ReadBit());
                        asp.Data.Add(Vector3D.Deserialize2(20, br));

                        var rot = Vector3D.DeserializeFixed(br);
                        asp.Data.Add(rot);
                        // Sometimes these two vectors are missing?
                        // Wild guess: They're momentum vectors, and only there when moving?
                        // Well, how do I know they're moving without momentum vectors... hm.
                        if (!(rot.X < -1 && rot.Y < -1 && rot.Z < -1))
                        {
                            asp.Data.Add(Vector3D.Deserialize2(20, br));
                            asp.Data.Add(Vector3D.Deserialize2(20, br));
                        }

                        asp.IsComplete = true;
                        break;
                    case "TAGame.Team_TA:GameEvent":
                    case "TAGame.CrowdActor_TA:ReplicatedOneShotSound":
                    case "TAGame.CrowdManager_TA:ReplicatedGlobalOneShotSound":
                    case "Engine.Actor:Owner":
                    case "TAGame.GameEvent_Soccar_TA:RoundNum":
                    case "Engine.GameReplicationInfo:GameClass":
                    case "TAGame.GameEvent_TA:BotSkill":
                    case "Engine.PlayerReplicationInfo:Team":
                    case "TAGame.CrowdManager_TA:GameEvent":
                    case "Engine.Pawn:PlayerReplicationInfo":
                    //case "TAGame.VehiclePickup_TA:ReplicatedPickupData":
                        asp.Data.Add(br.ReadBit());
                        asp.Data.Add(br.ReadInt32());
                        asp.IsComplete = true;
                        break;
                    case "TAGame.CarComponent_TA:Vehicle":
                        // 110101111 // TAGame.CarComponent_Jump_TA
                        // 100111111 // TAGame.CarComponent_FlipCar_TA
                        asp.Data.Add(br.ReadBit());
                        if (className == "TAGame.CarComponent_Jump_TA"
                            || className == "TAGame.CarComponent_FlipCar_TA"
                            || className == "TAGame.CarComponent_Boost_TA"
                            || className == "TAGame.CarComponent_Dodge_TA"
                            || className == "TAGame.CarComponent_DoubleJump_TA")
                        {
                            asp.Data.Add(br.ReadInt32());
                        }
                        else
                        {
                            asp.Data.Add(br.ReadByte());
                        }
                        asp.IsComplete = true;
                        break;
                    case "Engine.PlayerReplicationInfo:PlayerName":
                    case "Engine.GameReplicationInfo:ServerName":
                        asp.Data.Add(br.ReadString());
                        asp.IsComplete = true;
                        break;
                    case "TAGame.GameEvent_Soccar_TA:SecondsRemaining":
                    case "TAGame.GameEvent_TA:ReplicatedGameStateTimeRemaining":
                    case "TAGame.CrowdActor_TA:ReplicatedCountDownNumber":
                    case "TAGame.CrowdActor_TA:ModifiedNoise":
                    case "TAGame.GameEvent_Team_TA:MaxTeamSize":
                        asp.Data.Add(br.ReadInt32());
                        asp.IsComplete = true;
                        break;
                    case "TAGame.VehiclePickup_TA:ReplicatedPickupData":
                        // 1011101000000000000000000000000001
                        // 0111111111111111111111111111111110
                        // 1111001000000000000000000000000001
                        // 1000001000000000000000000000000001
                        // 1111110000000000000000000000000001
                        // 1101110000000000000000000000000001
                        // 111111111
                        // 100000001
                        // 101001111

                        var bit1 = br.ReadBit();
                        var byt = br.ReadByte();
                        var bit2 = (byt & 0x80) > 0;
                        if (bit1 == bit2)
                        {
                            asp.Data.Add(bit1);
                            asp.Data.Add(byt);
                        }
                        else
                        {
                            asp.Data.Add(bit1);
                            var bytes = new byte[4];
                            bytes[0] = byt;
                            bytes[1] = br.ReadByte();
                            bytes[2] = br.ReadByte();
                            bytes[3] = br.ReadByte();
                            asp.Data.Add(BitConverter.ToInt32(bytes, 0));
                            asp.Data.Add(br.ReadBit());
                        }
                        asp.IsComplete = true;
                        break;
                    case "Engine.Actor:bNetOwner":
                    case "Engine.Actor:bBlockActors":
                        // this doesnt look right...
                        asp.Data.Add(br.ReadBit());
                        asp.Data.Add(br.ReadBit());
                        asp.Data.Add(br.ReadBit());
                        asp.IsComplete = true;
                        break;
                    case "Engine.Pawn:DrivenVehicle":
                        asp.Data.Add(br.ReadInt32());
                        asp.Data.Add(br.ReadBit());
                        asp.Data.Add(br.ReadBit());
                        asp.IsComplete = true;
                        break;
                    case "Engine.Actor:DrawScale":

                        // Might be more properties in this sample data
                        // 100011000000000000010000000001001110010100000001000000000000000000000000000000110000000000000000000100000000010000000001000110000000000010101
                        // 1011010000000100010000000011001110010100000010000000000000000000000000000011100000000000000001001000000000010000000000100110101100000000101010
                        // 1011010000000011110000000001111111000100000010000000000000000000000000000001100000000000000000101000000000001000000000001110111111100000000101010
                        break;
                    case "Engine.PlayerReplicationInfo:Ping":
                    case "TAGame.Vehicle_TA:ReplicatedSteer":
                    case "TAGame.Vehicle_TA:ReplicatedThrottle":
                    case "TAGame.PRI_TA:CameraYaw":
                    case "TAGame.PRI_TA:CameraPitch":
                        asp.Data.Add(br.ReadByte());
                        asp.IsComplete = true;
                        break;
                    case "Engine.Actor:Location":
                    case "TAGame.CarComponent_Dodge_TA:DodgeTorque":
                        asp.Data.Add(Vector3D.Deserialize(br));
                        asp.IsComplete = true;
                        break;

                    case "Engine.Actor:bCollideWorld":
                    case "Engine.PlayerReplicationInfo:bReadyToPlay":
                    case "TAGame.Vehicle_TA:bReplicatedHandbrake":
                    case "TAGame.Vehicle_TA:bDriving":
                        //asp.Data.Add(Vector3D.Deserialize(5, br));
                        asp.Data.Add(br.ReadBit());
                        asp.IsComplete = true;
                        break;
                    case "TAGame.PRI_TA:bUsingBehindView":
                    case "TAGame.PRI_TA:bUsingSecondaryCamera":
                        asp.Data.Add(br.ReadBit());
                        asp.IsComplete = true;
                        break;
                    case "TAGame.CarComponent_TA:ReplicatedActive":
                        // example data
                        // 0111111111111111111111111111111110

                        asp.Data.Add(br.ReadByte());
                        /*
                        asp.Data.Add(br.ReadBit());
                        if ( (bool)asp.Data[0])
                        {
                            asp.Data.Add(br.ReadInt32FromBits(7));
                        }*/
                        asp.IsComplete = true;
                        break;
                    case "Engine.Actor:Role":
                        asp.Data.Add(br.ReadInt32FromBits(11));
                        asp.IsComplete = true;
                        break;
                    /*case "Engine.Actor:RelativeRotation": //SWAG
                        asp.Data.Add(br.ReadBit());
                        asp.Data.Add(br.ReadByte());
                        asp.Data.Add(br.ReadInt32());
                        asp.IsComplete = true;
                        break;*/
                    case "Engine.PlayerReplicationInfo:UniqueId":
                        asp.Data.Add(br.ReadBit());
                        asp.Data.Add(br.ReadByte());
                        asp.IsComplete = true;
                        break;
                }
            }
            catch(Exception)
            {
                asp.Data.Add("FAILED");
            }
            finally
            {
                asp.KnownBits = br.GetBits(startPosition, br.Position - startPosition);
            }

            return asp;
        }
 private static List<object> ReadData(int numBytes, int numBits, BitReader br)
 {
     List<object> data = new List<object>();
     for (int i = 0; i < numBytes; ++i)
     {
         data.Add(br.ReadByte());
     }
     for (int i = 0; i < numBits; ++i)
     {
         data.Add(br.ReadBit());
     }
     return data;
 }
        public static Vector3D DeserializeFixed(BitReader br)
        {
            var v = new Vector3D();

            v.X = ReadFixedCompressedFloat(1, 16, br);
            v.Y = ReadFixedCompressedFloat(1, 16, br);
            v.Z = ReadFixedCompressedFloat(1, 16, br);

            return v;
        }
 public static Vector3D Deserialize(BitReader br)
 {
     return Deserialize2(20, br);
 }
        // This probably belongs in BitReader. This is the only calss that uses it though.
        static float ReadFixedCompressedFloat(Int32 maxValue, Int32 numBits, BitReader br)
        {
            float value = 0;
                                                        // NumBits = 8:
            var maxBitValue	= (1 << (numBits - 1)) - 1;	//   0111 1111 - Max abs value we will serialize
            var bias        = (1 << (numBits - 1)) ;    //   1000 0000 - Bias to pivot around (in order to support signed values)
            var serIntMax	= (1 << (numBits - 0)) ;	// 1 0000 0000 - What we pass into SerializeInt
            var maxDelta	= (1 << (numBits - 0)) - 1;	//   1111 1111 - Max delta is

            Int32 delta = br.ReadInt32Max(serIntMax); // Could just read 16 bits always, since numBits will always be 16
            float unscaledValue = delta - bias;

            if ( maxValue > maxBitValue )
            {
                // We have to scale down, scale needs to be a float:
                float invScale = maxValue / (float)maxBitValue;
                value = unscaledValue * invScale;
            }
            else
            {
                var scale = maxBitValue / maxValue;
                float invScale = 1.0f / (float)scale;

                value = unscaledValue * invScale;
            }

            return value;
        }