private IDictionary <string, object> SerializePretty(Frame frame, JavaScriptSerializer serializer)
        {
            List <UInt32> deletedActorStateIds = new List <UInt32>();
            Dictionary <UInt32, ActorStateJson> updatedActorStates = new Dictionary <UInt32, ActorStateJson>();

            foreach (var a in frame.ActorStates.Where(x => x.State == ActorStateState.Deleted))
            {
                _existingActorStates.Remove(a.Id);
                deletedActorStateIds.Add(a.Id);
            }

            foreach (var a in frame.ActorStates.Where(x => x.State == ActorStateState.New))
            {
                // Skip anything thats not truly new. Dont want keyframes in our json (for now)
                // (If there are property changes for a "new" actor we already have, they'll be caught below)
                if (!_existingActorStates.ContainsKey(a.Id))
                {
                    var actorState = new RocketLeagueReplayParser.Serializers.JsonSerializer.ActorStateJson();
                    actorState.Id              = a.Id;
                    actorState.UnknownBit      = a.Unknown1;
                    actorState.TypeName        = a.TypeName;
                    actorState.ClassName       = a.ClassName;
                    actorState.InitialPosition = a.Position;
                    actorState.Properties      = new List <ActorStateProperty>();

                    _existingActorStates[a.Id] = actorState;
                    updatedActorStates[a.Id]   = actorState;
                }
            }

            foreach (var a in frame.ActorStates.Where(x => x.State == ActorStateState.Existing))
            {
                var existingActorState = _existingActorStates[a.Id];
                RocketLeagueReplayParser.Serializers.JsonSerializer.ActorStateJson actorState = null;
                if (updatedActorStates.ContainsKey(a.Id))
                {
                    // Actor that's new as of this frame. Use the object we created above.

                    actorState = updatedActorStates[a.Id];
                }
                else
                {
                    // Existing actor. Start a new state object to track changes

                    actorState            = new RocketLeagueReplayParser.Serializers.JsonSerializer.ActorStateJson();
                    actorState.Id         = a.Id;
                    actorState.Properties = new List <RocketLeagueReplayParser.Serializers.JsonSerializer.ActorStateProperty>();

                    // Maybe skip some of the following for existing actors
                    //actorState.UnknownBit = existingActorState.UnknownBit;
                    //actorState.TypeName = existingActorState.TypeName;
                    //actorState.ClassName = existingActorState.ClassName;
                    //actorState.InitialPosition = existingActorState.InitialPosition;
                }

                foreach (var p in a.Properties)
                {
                    var property = new RocketLeagueReplayParser.Serializers.JsonSerializer.ActorStateProperty
                    {
                        Name = p.PropertyName,
                        Data = p.Data
                    };

                    var existingProperty = existingActorState.Properties.Where(ep => ep.Name == property.Name).FirstOrDefault();
                    if (existingProperty == null)
                    {
                        // new property

                        actorState.Properties.Add(property);
                        existingActorState.Properties.Add(property);
                    }
                    else
                    {
                        // Existing property.
                        if (/* property.Name == "TAGame.Ball_TA:HitTeamNum" // Keep "Event" properties. TODO: Check if keyframes have this no matter what
                            || property.Name.Contains("Music") // Kind of guessing at some of these event properties. We'll see how they turn out.
                            || property.Name.Contains("Sound")
                            || property.Name.Contains("Event")
                            || */!existingProperty.IsDeepEqual(property))   // Only keep if it is truly different
                        {
                            actorState.Properties.Add(property);

                            // replace the property in our main set of data, so we have it for the next frame
                            existingActorState.Properties = existingActorState.Properties.Where(ep => ep.Name != property.Name).ToList();
                            existingActorState.Properties.Add(property);
                        }
                    }
                }

                // Dont keep this state if we havent found any properties worth keeping
                if (actorState.Properties.Any())
                {
                    updatedActorStates[a.Id] = actorState;
                }
            }

            if (deletedActorStateIds.Any() || updatedActorStates.Any())
            {
                Dictionary <string, object> result = new Dictionary <string, object>();
                result["Time"]            = frame.Time;
                result["DeletedActorIds"] = deletedActorStateIds;
                result["ActorUpdates"]    = updatedActorStates.Values;
                return(result);
            }
            else
            {
                return(null);
            }
        }
        private IDictionary<string, object> SerializePretty(Frame frame, JavaScriptSerializer serializer)
        {
            List<UInt32> deletedActorStateIds = new List<UInt32>();
            Dictionary<UInt32, ActorStateJson> updatedActorStates = new Dictionary<UInt32, ActorStateJson>();

            foreach (var a in frame.ActorStates.Where(x => x.State == ActorStateState.Deleted))
            {
                _existingActorStates.Remove(a.Id);
                deletedActorStateIds.Add(a.Id);
            }

            foreach (var a in frame.ActorStates.Where(x => x.State == ActorStateState.New))
            {
                // Skip anything thats not truly new. Dont want keyframes in our json (for now)
                // (If there are property changes for a "new" actor we already have, they'll be caught below)
                if (!_existingActorStates.ContainsKey(a.Id))
                {
                    var actorState = new RocketLeagueReplayParser.Serializers.JsonSerializer.ActorStateJson();
                    actorState.Id = a.Id;
                    actorState.UnknownBit = a.Unknown1;
                    actorState.TypeName = a.TypeName;
                    actorState.ClassName = a.ClassName;
                    actorState.InitialPosition = a.Position;
                    actorState.Properties = new List<ActorStateProperty>();

                    _existingActorStates[a.Id] = actorState;
                    updatedActorStates[a.Id] = actorState;
                }
            }

            foreach (var a in frame.ActorStates.Where(x => x.State == ActorStateState.Existing))
            {
                var existingActorState = _existingActorStates[a.Id];
                RocketLeagueReplayParser.Serializers.JsonSerializer.ActorStateJson actorState = null;
                if (updatedActorStates.ContainsKey(a.Id))
                {
                    // Actor that's new as of this frame. Use the object we created above.

                    actorState = updatedActorStates[a.Id];
                }
                else
                {
                    // Existing actor. Start a new state object to track changes

                    actorState = new RocketLeagueReplayParser.Serializers.JsonSerializer.ActorStateJson();
                    actorState.Id = a.Id;
                    actorState.Properties = new List<RocketLeagueReplayParser.Serializers.JsonSerializer.ActorStateProperty>();

                    // Maybe skip some of the following for existing actors
                    //actorState.UnknownBit = existingActorState.UnknownBit;
                    //actorState.TypeName = existingActorState.TypeName;
                    //actorState.ClassName = existingActorState.ClassName;
                    //actorState.InitialPosition = existingActorState.InitialPosition;
                }

                foreach (var p in a.Properties)
                {
                    var property = new RocketLeagueReplayParser.Serializers.JsonSerializer.ActorStateProperty
                    {
                        Name = p.PropertyName,
                        Data = p.Data
                    };

                    var existingProperty = existingActorState.Properties.Where(ep => ep.Name == property.Name).FirstOrDefault();
                    if (existingProperty == null)
                    {
                        // new property

                        actorState.Properties.Add(property);
                        existingActorState.Properties.Add(property);
                    }
                    else
                    {
                        // Existing property.
                        if (/* property.Name == "TAGame.Ball_TA:HitTeamNum" // Keep "Event" properties. TODO: Check if keyframes have this no matter what
                            || property.Name.Contains("Music") // Kind of guessing at some of these event properties. We'll see how they turn out.
                            || property.Name.Contains("Sound")
                            || property.Name.Contains("Event")
                            || */ !existingProperty.IsDeepEqual(property))  // Only keep if it is truly different
                        {
                            actorState.Properties.Add(property);

                            // replace the property in our main set of data, so we have it for the next frame
                            existingActorState.Properties = existingActorState.Properties.Where(ep => ep.Name != property.Name).ToList();
                            existingActorState.Properties.Add(property);
                        }
                    }
                }

                // Dont keep this state if we havent found any properties worth keeping
                if (actorState.Properties.Any())
                {
                    updatedActorStates[a.Id] = actorState;
                }
            }

            if (deletedActorStateIds.Any() || updatedActorStates.Any())
            {
                Dictionary<string, object> result = new Dictionary<string, object>();
                result["Time"] = frame.Time;
                result["DeletedActorIds"] = deletedActorStateIds;
                result["ActorUpdates"] = updatedActorStates.Values;
                return result;
            }
            else
            {
                return null;
            }
        }