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 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;
        }
        // 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;
        }