diff --git a/library/Octane/Main.hs b/library/Octane/Main.hs
--- a/library/Octane/Main.hs
+++ b/library/Octane/Main.hs
@@ -1,5 +1,6 @@
 module Octane.Main (main) where
 
+import qualified Control.DeepSeq as DeepSeq
 import qualified Control.Monad as Monad
 import qualified Control.Newtype as Newtype
 import qualified Data.Aeson.Encode.Pretty as Aeson
@@ -32,6 +33,7 @@
                 IO.stderr
                 (file ++ " @ byte " ++ show offset ++ " - " ++ message)
         Right replay -> do
+            DeepSeq.deepseq replay (IO.hPutStrLn IO.stderr "evaluated replay")
             let inputSize = contents & BS.length & fromIntegral
             let outputSize = replay & Binary.encode & BSL.length
             Monad.when (inputSize /= outputSize) $
@@ -45,9 +47,14 @@
                     Aeson.defConfig
                     { Aeson.confCompare = compare
                     }
+            putStrLn "{\"meta\":\n"
             replay & Aeson.encodePretty' config & BSL8.putStrLn
 
+            putStrLn ",\"frames\":\n"
             let frames = Parser.parseFrames replay
+            DeepSeq.deepseq frames (IO.hPutStrLn IO.stderr "evaluated frames")
             let expectedFrames = replay & Type.replayProperties & Newtype.unpack & Map.lookup ("NumFrames" & Text.pack & Newtype.pack)
             let actualFrames = frames & length & fromIntegral & Newtype.pack & Type.IntProperty (Newtype.pack 4) & Just
             Monad.when (expectedFrames /= actualFrames) $ IO.hPutStrLn IO.stderr ("expected " ++ show expectedFrames ++ " frames but found " ++ show actualFrames ++ " frames!")
+            frames & Aeson.encodePretty' config & BSL8.putStrLn
+            putStrLn "}"
diff --git a/library/Octane/Parser.hs b/library/Octane/Parser.hs
--- a/library/Octane/Parser.hs
+++ b/library/Octane/Parser.hs
@@ -1,24 +1,28 @@
 {-# LANGUAGE DeriveGeneric #-}
 
-module Octane.Parser (parseFrames) where
+module Octane.Parser where
 
 import Data.Function ((&))
 
+import qualified Control.DeepSeq as DeepSeq
 import qualified Control.Newtype as Newtype
+import qualified Data.Aeson as Aeson
+import qualified Data.Aeson.Types as Aeson
 import qualified Data.Binary.Bits.Get as Bits
 import qualified Data.Binary.IEEE754 as IEEE754
 import qualified Data.Binary.Get as Binary
 import qualified Data.Bits as Bits
 import qualified Data.ByteString as BS
 import qualified Data.ByteString.Lazy as BSL
-import qualified Data.IntMap as IntMap
+import qualified Data.IntMap.Strict as IntMap
 import qualified Data.List as List
-import qualified Data.Map as Map
+import qualified Data.Map.Strict as Map
 import qualified Data.Maybe as Maybe
 import qualified Data.Set as Set
 import qualified Data.Text as Text
 import qualified Data.Text.Encoding as Encoding
 import qualified Data.Word as Word
+import qualified Debug.Trace as Trace
 import qualified GHC.Generics as Generics
 import qualified Octane.Type as Type
 import qualified Text.Regex as Regex
@@ -129,6 +133,7 @@
           { replicationActorId = actorId
           , replicationIsOpen = True
           , replicationIsNew = Just True
+          , replicationClassInit = Just classInit
           , replicationProps = []
           })
 
@@ -144,6 +149,7 @@
         { replicationActorId = actorId
         , replicationIsOpen = True
         , replicationIsNew = Just False
+        , replicationClassInit = Nothing
         , replicationProps = props
         })
 
@@ -159,6 +165,7 @@
           { replicationActorId = actorId
           , replicationIsOpen = False
           , replicationIsNew = Nothing
+          , replicationClassInit = Nothing
           , replicationProps = []
           })
 
@@ -184,7 +191,12 @@
 getProp context thing = do
     let classId = thing & thingClassId
     let props = case context & contextClassPropertyMap & IntMap.lookup classId of
-            Nothing -> error ("could not find property map for class id " ++ show classId)
+            Nothing ->
+                Trace.trace (context & contextObjectMap & IntMap.toAscList & map (\ (k, v) -> "\t" ++ show k ++ "\t=> " ++ show v) & ("OBJECTS:" :) & unlines) $
+                Trace.trace (context & contextArchetypeMap & Map.toList & map (\ (k, v) -> "\t" ++ show k ++ "\t=> " ++ show v) & ("ARCHETYPES:" :) & unlines) $
+                Trace.trace (context & contextClassMap & Map.toList & map (\ (k, v) -> "\t" ++ show k ++ "\t=> " ++ show v) & ("CLASSES:" :) & unlines) $
+                Trace.trace (context & contextClassPropertyMap & IntMap.toAscList & map (\ (k1, v1) -> "\t" ++ show k1 ++ "\t=>\n" ++ (v1 & IntMap.toAscList & map (\ (k2, v2) -> "\t\t" ++ show k2 ++ "\t=>" ++ show v2) & unlines)) & ("CLASS PROPERTIES:" :) & unlines) $
+                error ("could not find property map for class id " ++ show classId)
             Just x -> x
     let maxId = props & IntMap.keys & maximum
     pid <- getInt maxId
@@ -237,6 +249,9 @@
     _ | Set.member name propsWithLocation -> do
         vector <- getVector
         return (PLocation vector)
+    _ | Set.member name propsWithFloat -> do
+        float <- getFloat32
+        return (PFloat float)
     "ProjectX.GRI_X:Reservations" -> do
         -- I think this is the connection order. The first player to connect
         -- gets number 0, and it goes up from there. The maximum is 8, which
@@ -300,6 +315,14 @@
         cue <- getInt32
         trigger <- getInt8
         return (PMusicStinger flag cue trigger)
+    "TAGame.Car_TA:ReplicatedDemolish" -> do
+        hasAtk <- Bits.getBool
+        atk <- if hasAtk then fmap Just getInt32 else return Nothing
+        hasVic <- Bits.getBool
+        vic <- if hasVic then fmap Just getInt32 else return Nothing
+        vec1 <- getVector
+        vec2 <- getVector
+        return (PDemolish hasAtk atk hasVic vic vec1 vec2)
     -- TODO: Parse other prop types.
     _ -> fail ("don't know how to read property " ++ show name)
 
@@ -351,6 +374,10 @@
     , "TAGame.Ball_TA:GameEvent"
     , "TAGame.CameraSettingsActor_TA:PRI"
     , "TAGame.CarComponent_TA:Vehicle"
+    , "TAGame.CrowdActor_TA:GameEvent"
+    , "TAGame.CrowdActor_TA:ReplicatedOneShotSound"
+    , "TAGame.CrowdManager_TA:GameEvent"
+    , "TAGame.CrowdManager_TA:ReplicatedGlobalOneShotSound"
     , "TAGame.PRI_TA:PersistentCamera"
     , "TAGame.PRI_TA:ReplicatedGameEvent"
     , "TAGame.Team_TA:GameEvent"
@@ -364,18 +391,24 @@
 
 propsWithBoolean :: Set.Set Text.Text
 propsWithBoolean =
-    [ "Engine.PlayerReplicationInfo:bReadyToPlay"
+    [ "Engine.Actor:bBlockActors"
+    , "Engine.Actor:bCollideActors"
+    , "Engine.Actor:bHidden"
+    , "Engine.PlayerReplicationInfo:bReadyToPlay"
     , "ProjectX.GRI_X:bGameStarted"
+    , "TAGame.CameraSettingsActor_TA:bUsingBehindView"
     , "TAGame.CameraSettingsActor_TA:bUsingSecondaryCamera"
+    , "TAGame.CarComponent_FlipCar_TA:bFlipRight"
+    , "TAGame.GameEvent_Soccar_TA:bBallHasBeenHit"
+    , "TAGame.GameEvent_Soccar_TA:bOverTime"
     , "TAGame.GameEvent_TA:bHasLeaveMatchPenalty"
     , "TAGame.GameEvent_Team_TA:bDisableMutingOtherTeam"
     , "TAGame.PRI_TA:bOnlineLoadoutSet"
+    , "TAGame.PRI_TA:bUsingBehindView"
+    , "TAGame.PRI_TA:bUsingSecondaryCamera"
+    , "TAGame.RBActor_TA:bReplayActor"
     , "TAGame.Vehicle_TA:bDriving"
-    , "TAGame.GameEvent_Soccar_TA:bBallHasBeenHit"
     , "TAGame.Vehicle_TA:bReplicatedHandbrake"
-    , "Engine.Actor:bCollideActors"
-    , "Engine.Actor:bBlockActors"
-    , "TAGame.CameraSettingsActor_TA:bUsingBehindView"
     ] & map Text.pack & Set.fromList
 
 propsWithQWord :: Set.Set Text.Text
@@ -386,35 +419,39 @@
 propsWithInt :: Set.Set Text.Text
 propsWithInt =
     [ "Engine.PlayerReplicationInfo:PlayerID"
+    , "Engine.PlayerReplicationInfo:Score"
+    , "Engine.TeamInfo:Score"
     , "ProjectX.GRI_X:ReplicatedGamePlaylist"
+    , "TAGame.CrowdActor_TA:ReplicatedCountDownNumber"
+    , "TAGame.GameEvent_Soccar_TA:RoundNum"
     , "TAGame.GameEvent_Soccar_TA:SecondsRemaining"
     , "TAGame.GameEvent_TA:BotSkill"
     , "TAGame.GameEvent_TA:ReplicatedGameStateTimeRemaining"
-    , "TAGame.GameEvent_Soccar_TA:RoundNum"
     , "TAGame.GameEvent_TA:ReplicatedStateName"
     , "TAGame.GameEvent_Team_TA:MaxTeamSize"
-    , "TAGame.PRI_TA:Title"
-    , "TAGame.PRI_TA:TotalXP"
+    , "TAGame.PRI_TA:MatchAssists"
+    , "TAGame.PRI_TA:MatchGoals"
+    , "TAGame.PRI_TA:MatchSaves"
     , "TAGame.PRI_TA:MatchScore"
     , "TAGame.PRI_TA:MatchShots"
-    , "TAGame.PRI_TA:MatchSaves"
-    , "Engine.TeamInfo:Score"
-    , "Engine.PlayerReplicationInfo:Score"
-    , "TAGame.PRI_TA:MatchGoals"
-    , "TAGame.PRI_TA:MatchAssists"
+    , "TAGame.PRI_TA:Title"
+    , "TAGame.PRI_TA:TotalXP"
     ] & map Text.pack & Set.fromList
 
 propsWithByte :: Set.Set Text.Text
 propsWithByte =
     [ "Engine.PlayerReplicationInfo:Ping"
+    , "TAGame.Ball_TA:HitTeamNum"
+    , "TAGame.CameraSettingsActor_TA:CameraPitch"
+    , "TAGame.CameraSettingsActor_TA:CameraYaw"
     , "TAGame.CarComponent_Boost_TA:ReplicatedBoostAmount"
-    , "TAGame.Vehicle_TA:ReplicatedThrottle"
     , "TAGame.CarComponent_TA:ReplicatedActive"
-    , "TAGame.Vehicle_TA:ReplicatedSteer"
-    , "TAGame.Ball_TA:HitTeamNum"
     , "TAGame.GameEvent_Soccar_TA:ReplicatedScoredOnTeam"
-    , "TAGame.CameraSettingsActor_TA:CameraYaw"
-    , "TAGame.CameraSettingsActor_TA:CameraPitch"
+    , "TAGame.GameEvent_TA:ReplicatedStateIndex"
+    , "TAGame.PRI_TA:CameraPitch"
+    , "TAGame.PRI_TA:CameraYaw"
+    , "TAGame.Vehicle_TA:ReplicatedSteer"
+    , "TAGame.Vehicle_TA:ReplicatedThrottle"
     ] & map Text.pack & Set.fromList
 
 propsWithUniqueId :: Set.Set Text.Text
@@ -426,6 +463,7 @@
 propsWithCamSettings :: Set.Set Text.Text
 propsWithCamSettings =
     [ "TAGame.CameraSettingsActor_TA:ProfileSettings"
+    , "TAGame.PRI_TA:CameraSettings"
     ] & map Text.pack & Set.fromList
 
 propsWithLocation :: Set.Set Text.Text
@@ -433,6 +471,13 @@
     [ "TAGame.CarComponent_Dodge_TA:DodgeTorque"
     ] & map Text.pack & Set.fromList
 
+propsWithFloat :: Set.Set Text.Text
+propsWithFloat =
+    [ "Engine.Actor:DrawScale"
+    , "TAGame.CarComponent_FlipCar_TA:FlipCarTime"
+    , "TAGame.CrowdActor_TA:ModifiedNoise"
+    ] & map Text.pack & Set.fromList
+
 type SystemId = Word.Word8
 
 -- This is the number associated with a splitscreen player. So the first player
@@ -442,59 +487,103 @@
 type LocalId = Word.Word8
 
 data RemoteId
-    = SteamId BS.ByteString -- TODO: This is an integer.
-    | PlayStationId BS.ByteString -- TODO: I think this is a string?
-    deriving (Eq, Show)
+    = SteamId !BS.ByteString -- TODO: This is an integer.
+    | PlayStationId !BS.ByteString -- TODO: I think this is a string?
+    deriving (Eq, Generics.Generic, Show)
 
+instance DeepSeq.NFData RemoteId
+
+-- TODO: This is a dumb instance. It's only necessary because there's no ToJSON
+-- instance for ByteString.
+instance Aeson.ToJSON RemoteId where
+    toJSON remoteId = case remoteId of
+        SteamId bytes -> bytes & show & Aeson.toJSON
+        PlayStationId bytes -> bytes & show & Aeson.toJSON
+
 data Prop = Prop
-    { propId :: Int
-    , propValue :: PropValue
-    } deriving (Eq, Show)
+    { propId :: !Int
+    , propValue :: !PropValue
+    } deriving (Eq, Generics.Generic, Show)
 
+instance DeepSeq.NFData Prop
+instance Aeson.ToJSON Prop where
+    toJSON =
+        Aeson.genericToJSON
+            Aeson.defaultOptions
+            { Aeson.fieldLabelModifier = drop 4
+            }
+
 data PropValue
-    = PRigidBodyState Bool (Vector Int) (Vector Float) (Maybe (Vector Int)) (Maybe (Vector Int))
-    | PFlaggedInt Bool Int
-    | PString Text.Text
-    | PBoolean Bool
-    | PQWord Int Int
-    | PReservation Int SystemId RemoteId LocalId (Maybe Text.Text) Bool Bool
-    | PInt Int
-    | PByte Int
-    | PUniqueId SystemId RemoteId LocalId
-    | PLoadoutOnline Int Int Int (Maybe Int)
-    | PLoadout Int Int Int Int Int Int Int Int (Maybe Int)
-    | PCamSettings Float Float Float Float Float Float
-    | PTeamPaint Int Int Int Int Int
-    | PLocation (Vector Int)
-    | PPickup Bool (Maybe Int) Bool
-    | PEnum Word.Word16 -- TODO: This isn't the right data type.
-    | PExplosion Bool (Maybe Int) (Vector Int)
-    | PMusicStinger Bool Int Int
-    deriving (Eq, Show)
+    = PRigidBodyState !Bool !(Vector Int) !(Vector Float) !(Maybe (Vector Int)) !(Maybe (Vector Int))
+    | PFlaggedInt !Bool !Int
+    | PString !Text.Text
+    | PBoolean !Bool
+    | PQWord Int !Int
+    | PReservation !Int !SystemId !RemoteId !LocalId !(Maybe Text.Text) !Bool !Bool
+    | PInt !Int
+    | PByte !Int
+    | PUniqueId !SystemId !RemoteId !LocalId
+    | PLoadoutOnline !Int !Int !Int !(Maybe Int)
+    | PLoadout !Int !Int !Int !Int !Int !Int !Int !Int !(Maybe Int)
+    | PCamSettings !Float !Float !Float !Float !Float !Float
+    | PTeamPaint !Int !Int !Int !Int !Int
+    | PLocation !(Vector Int)
+    | PPickup !Bool !(Maybe Int) !Bool
+    | PEnum !Word.Word16 -- TODO: This isn't the right data type.
+    | PExplosion !Bool !(Maybe Int) !(Vector Int)
+    | PMusicStinger !Bool !Int !Int
+    | PFloat !Float
+    | PDemolish !Bool !(Maybe Int) !Bool !(Maybe Int) !(Vector Int) !(Vector Int)
+    deriving (Eq, Generics.Generic, Show)
 
+instance DeepSeq.NFData PropValue
+instance Aeson.ToJSON PropValue where
+    toJSON =
+        Aeson.genericToJSON
+            Aeson.defaultOptions
+            { Aeson.fieldLabelModifier = drop 9
+            }
+
 -- | A frame in the net stream. Each frame has the time since the beginning of
 -- | the match, the time since the last frame, and a list of replications.
 data Frame = Frame
-    { frameTime :: Float
-    , frameDelta :: Float
-    , frameReplications :: [Replication]
+    { frameTime :: !Float
+    , frameDelta :: !Float
+    , frameReplications :: ![Replication]
     } deriving (Eq,Generics.Generic,Show)
 
+instance DeepSeq.NFData Frame
+instance Aeson.ToJSON Frame where
+    toJSON =
+        Aeson.genericToJSON
+            Aeson.defaultOptions
+            { Aeson.fieldLabelModifier = drop 5
+            }
+
 -- | Replication information about an actor in the net stream.
 data Replication = Replication
-    { replicationActorId :: Int
-    , replicationIsOpen :: Bool
-    , replicationIsNew :: Maybe Bool
-    , replicationProps :: [Prop]
+    { replicationActorId :: !Int
+    , replicationIsOpen :: !Bool
+    , replicationIsNew :: !(Maybe Bool)
+    , replicationClassInit :: !(Maybe ClassInit)
+    , replicationProps :: ![Prop]
     } deriving (Eq,Generics.Generic,Show)
 
+instance DeepSeq.NFData Replication
+instance Aeson.ToJSON Replication where
+    toJSON =
+        Aeson.genericToJSON
+            Aeson.defaultOptions
+            { Aeson.fieldLabelModifier = drop 11
+            }
+
 data Thing = Thing
-    { thingFlag :: Bool
-    , thingObjectId :: Int
-    , thingObjectName :: Text.Text
-    , thingClassId :: Int
-    , thingClassName :: Text.Text
-    , thingClassInit :: ClassInit
+    { thingFlag :: !Bool
+    , thingObjectId :: !Int
+    , thingObjectName :: !Text.Text
+    , thingClassId :: !Int
+    , thingClassName :: !Text.Text
+    , thingClassInit :: !ClassInit
     } deriving (Show)
 
 type Time = Float
@@ -504,16 +593,32 @@
 type ActorId = Int
 
 data Vector a = Vector
-    { vectorX :: a
-    , vectorY :: a
-    , vectorZ :: a
-    } deriving (Eq, Show)
+    { vectorX :: !a
+    , vectorY :: !a
+    , vectorZ :: !a
+    } deriving (Eq, Generics.Generic, Show)
 
+instance (DeepSeq.NFData a) => DeepSeq.NFData (Vector a)
+instance (Aeson.ToJSON a) => Aeson.ToJSON (Vector a) where
+    toJSON =
+        Aeson.genericToJSON
+            Aeson.defaultOptions
+            { Aeson.fieldLabelModifier = drop 6
+            }
+
 data ClassInit = ClassInit
-    { classInitLocation :: Maybe (Vector Int)
-    , classInitRotation :: Maybe (Vector Int)
-    } deriving (Show)
+    { classInitLocation :: !(Maybe (Vector Int))
+    , classInitRotation :: !(Maybe (Vector Int))
+    } deriving (Eq, Generics.Generic, Show)
 
+instance DeepSeq.NFData ClassInit
+instance Aeson.ToJSON ClassInit where
+    toJSON =
+        Aeson.genericToJSON
+            Aeson.defaultOptions
+            { Aeson.fieldLabelModifier = drop 6
+            }
+
 -- { class stream id => { property stream id => name } }
 type ClassPropertyMap = IntMap.IntMap (IntMap.IntMap Text.Text)
 
@@ -527,11 +632,11 @@
 type ClassMap = Map.Map Text.Text Int
 
 data Context = Context
-    { contextObjectMap :: ObjectMap
-    , contextClassPropertyMap :: ClassPropertyMap
-    , contextThings :: IntMap.IntMap Thing
-    , contextArchetypeMap :: ArchetypeMap
-    , contextClassMap :: ClassMap
+    { contextObjectMap :: !ObjectMap
+    , contextClassPropertyMap :: !ClassPropertyMap
+    , contextThings :: !(IntMap.IntMap Thing)
+    , contextArchetypeMap :: !ArchetypeMap
+    , contextClassMap :: !ClassMap
     } deriving (Show)
 
 buildObjectMap :: Type.Replay -> ObjectMap
@@ -565,6 +670,8 @@
     & Newtype.unpack
     & map Newtype.unpack
     & zip [0 ..]
+    & filter (\ (_, objectName) ->
+        not (Text.isInfixOf (Text.pack ":") objectName))
     & map (\ (objectId, objectName) -> let
         k = archetypeToClass objectName
         v = objectId
diff --git a/library/Octane/Type/Actor.hs b/library/Octane/Type/Actor.hs
--- a/library/Octane/Type/Actor.hs
+++ b/library/Octane/Type/Actor.hs
@@ -13,8 +13,8 @@
 -- | A class (like "Core.Object") and it's associated ID in the net stream
 -- | (like 0).
 data Actor = Actor
-    { actorName :: PCString.PCString
-    , actorStreamId :: Word32LE.Word32LE
+    { actorName :: !PCString.PCString
+    , actorStreamId :: !Word32LE.Word32LE
     } deriving (Eq,Generics.Generic,Show)
 
 instance Binary.Binary Actor where
diff --git a/library/Octane/Type/CacheItem.hs b/library/Octane/Type/CacheItem.hs
--- a/library/Octane/Type/CacheItem.hs
+++ b/library/Octane/Type/CacheItem.hs
@@ -13,10 +13,10 @@
 
 -- | An item in the class net cache map.
 data CacheItem = CacheItem
-    { cacheItemClassId :: Word32LE.Word32LE
-    , cacheItemParentCacheId :: Word32LE.Word32LE
-    , cacheItemCacheId :: Word32LE.Word32LE
-    , cacheItemCacheProperties :: List.List CacheProperty.CacheProperty
+    { cacheItemClassId :: !Word32LE.Word32LE
+    , cacheItemParentCacheId :: !Word32LE.Word32LE
+    , cacheItemCacheId :: !Word32LE.Word32LE
+    , cacheItemCacheProperties :: !(List.List CacheProperty.CacheProperty)
     } deriving (Eq,Generics.Generic,Show)
 
 instance Binary.Binary CacheItem where
diff --git a/library/Octane/Type/CacheProperty.hs b/library/Octane/Type/CacheProperty.hs
--- a/library/Octane/Type/CacheProperty.hs
+++ b/library/Octane/Type/CacheProperty.hs
@@ -11,8 +11,8 @@
 
 -- | A property on an item in the class net cache map.
 data CacheProperty = CacheProperty
-    { cachePropertyObjectId :: Word32LE.Word32LE
-    , cachePropertyStreamId :: Word32LE.Word32LE
+    { cachePropertyObjectId :: !Word32LE.Word32LE
+    , cachePropertyStreamId :: !Word32LE.Word32LE
     } deriving (Eq,Generics.Generic,Show)
 
 instance Binary.Binary CacheProperty where
diff --git a/library/Octane/Type/KeyFrame.hs b/library/Octane/Type/KeyFrame.hs
--- a/library/Octane/Type/KeyFrame.hs
+++ b/library/Octane/Type/KeyFrame.hs
@@ -14,9 +14,9 @@
 -- | the frame it corresponds to, and that frame's bit position in the network
 -- | stream.
 data KeyFrame = KeyFrame
-    { keyFrameTime :: Float32LE.Float32LE
-    , keyFrameFrame :: Word32LE.Word32LE
-    , keyFramePosition :: Word32LE.Word32LE
+    { keyFrameTime :: !Float32LE.Float32LE
+    , keyFrameFrame :: !Word32LE.Word32LE
+    , keyFramePosition :: !Word32LE.Word32LE
     } deriving (Eq,Generics.Generic,Show)
 
 instance Binary.Binary KeyFrame where
diff --git a/library/Octane/Type/Mark.hs b/library/Octane/Type/Mark.hs
--- a/library/Octane/Type/Mark.hs
+++ b/library/Octane/Type/Mark.hs
@@ -12,8 +12,8 @@
 
 -- | A tick mark on the replay. Both goals and saves make tick marks.
 data Mark = Mark
-    { markLabel :: PCString.PCString
-    , markFrame :: Word32LE.Word32LE
+    { markLabel :: !PCString.PCString
+    , markFrame :: !Word32LE.Word32LE
     } deriving (Eq,Generics.Generic,Show)
 
 instance Binary.Binary Mark where
diff --git a/library/Octane/Type/Message.hs b/library/Octane/Type/Message.hs
--- a/library/Octane/Type/Message.hs
+++ b/library/Octane/Type/Message.hs
@@ -12,9 +12,9 @@
 
 -- | A debugging message. Replays do not have any of these anymore.
 data Message = Message
-    { messageFrame :: Word32LE.Word32LE
-    , messageName :: PCString.PCString
-    , messageContent :: PCString.PCString
+    { messageFrame :: !Word32LE.Word32LE
+    , messageName :: !PCString.PCString
+    , messageContent :: !PCString.PCString
     } deriving (Eq,Generics.Generic,Show)
 
 instance Binary.Binary Message where
diff --git a/library/Octane/Type/Primitive/Dictionary.hs b/library/Octane/Type/Primitive/Dictionary.hs
--- a/library/Octane/Type/Primitive/Dictionary.hs
+++ b/library/Octane/Type/Primitive/Dictionary.hs
@@ -8,7 +8,7 @@
 import qualified Data.Aeson as Aeson
 import qualified Data.Binary as Binary
 import Data.Function ((&))
-import qualified Data.Map as Map
+import qualified Data.Map.Strict as Map
 import qualified GHC.Generics as Generics
 import qualified Octane.Type.Primitive.PCString as PCString
 
diff --git a/library/Octane/Type/Property.hs b/library/Octane/Type/Property.hs
--- a/library/Octane/Type/Property.hs
+++ b/library/Octane/Type/Property.hs
@@ -21,22 +21,22 @@
 -- | use it. The value stored in the property can be an array, a boolean, and
 -- | so on.
 data Property
-    = ArrayProperty Word64LE.Word64LE
-                    (List.List (Dictionary.Dictionary Property))
-    | BoolProperty Word64LE.Word64LE
-                   Boolean.Boolean
-    | ByteProperty Word64LE.Word64LE
-                   (PCString.PCString, PCString.PCString)
-    | FloatProperty Word64LE.Word64LE
-                    Float32LE.Float32LE
-    | IntProperty Word64LE.Word64LE
-                  Word32LE.Word32LE
-    | NameProperty Word64LE.Word64LE
-                   PCString.PCString
-    | QWordProperty Word64LE.Word64LE
-                    Word64LE.Word64LE
-    | StrProperty Word64LE.Word64LE
-                  PCString.PCString
+    = ArrayProperty !Word64LE.Word64LE
+                    !(List.List (Dictionary.Dictionary Property))
+    | BoolProperty !Word64LE.Word64LE
+                   !Boolean.Boolean
+    | ByteProperty !Word64LE.Word64LE
+                   !(PCString.PCString, PCString.PCString)
+    | FloatProperty !Word64LE.Word64LE
+                    !Float32LE.Float32LE
+    | IntProperty !Word64LE.Word64LE
+                  !Word32LE.Word32LE
+    | NameProperty !Word64LE.Word64LE
+                   !PCString.PCString
+    | QWordProperty !Word64LE.Word64LE
+                    !Word64LE.Word64LE
+    | StrProperty !Word64LE.Word64LE
+                  !PCString.PCString
     deriving (Eq,Generics.Generic,Show)
 
 instance Binary.Binary Property where
diff --git a/library/Octane/Type/Replay.hs b/library/Octane/Type/Replay.hs
--- a/library/Octane/Type/Replay.hs
+++ b/library/Octane/Type/Replay.hs
@@ -23,52 +23,52 @@
 -- | stream has not.
 data Replay = Replay
     -- Number of bytes in the first section.
-    { replaySize1 :: Word32LE.Word32LE
+    { replaySize1 :: !Word32LE.Word32LE
     -- CRC to check the first section.
-    , replayCRC1 :: Word32LE.Word32LE
+    , replayCRC1 :: !Word32LE.Word32LE
     -- Major replay version number.
-    , replayVersion1 :: Word32LE.Word32LE
+    , replayVersion1 :: !Word32LE.Word32LE
     -- Minor replay version number.
-    , replayVersion2 :: Word32LE.Word32LE
+    , replayVersion2 :: !Word32LE.Word32LE
     -- Label, which is always "TAGame.Replay_Soccar_TA".
-    , replayLabel :: PCString.PCString
+    , replayLabel :: !PCString.PCString
     -- High-level metadata about the replay.
-    , replayProperties :: Dictionary.Dictionary Property.Property
+    , replayProperties :: !(Dictionary.Dictionary Property.Property)
     -- Number of bytes in the last section.
-    , replaySize2 :: Word32LE.Word32LE
+    , replaySize2 :: !Word32LE.Word32LE
     -- CRC to check the last section.
-    , replayCRC2 :: Word32LE.Word32LE
+    , replayCRC2 :: !Word32LE.Word32LE
     -- Array of strings for all of the levels that need to be loaded (array
     -- length followed by each string)
-    , replayLevels :: List.List PCString.PCString
+    , replayLevels :: !(List.List PCString.PCString)
     -- Array of Keyframe information used for timeline scrubbing (array
     -- length followed by each keyframe struct) (Time, Frame, File Position)
-    , replayKeyFrames :: List.List KeyFrame.KeyFrame
+    , replayKeyFrames :: !(List.List KeyFrame.KeyFrame)
     -- Array of bytes that is the bulk of the data. This is the raw network
     -- stream. (array length followed by a bunch of bytes)
-    , replayStream :: Stream.Stream
+    , replayStream :: !Stream.Stream
     -- Array of debugging logs (strings). This reminds me that I should
     -- probably turn these off to make the replays smaller. (array length
     -- followed by each string)
-    , replayMessages :: List.List Message.Message
+    , replayMessages :: !(List.List Message.Message)
     -- Array of information used to display the Tick marks in the replay (goal
     -- scores). (array length followed by each tick struct) (Type, Frame)
-    , replayMarks :: List.List Mark.Mark
+    , replayMarks :: !(List.List Mark.Mark)
     -- Array of strings of replicated Packages
-    , replayPackages :: List.List PCString.PCString
+    , replayPackages :: !(List.List PCString.PCString)
     -- Array of strings for the Object table. Whenever a persistent object gets
     -- referenced in the network stream its path gets added to this array. Then
     -- its index in this array is used in the network stream.
-    , replayObjects :: List.List PCString.PCString
+    , replayObjects :: !(List.List PCString.PCString)
     -- Array of strings for the Name table. "Names" are commonly used strings
     -- that get assigned an integer for use in the network stream.
-    , replayNames :: List.List PCString.PCString
+    , replayNames :: !(List.List PCString.PCString)
     -- Map of string, integer pairs for the Class Index Map. Whenever a class
     -- is used in the network stream it is given an integer id by this map.
-    , replayActors :: List.List Actor.Actor
+    , replayActors :: !(List.List Actor.Actor)
     -- "Class Net Cache Map" maps each replicated property in a class to an
     -- integer id used in the network stream.
-    , replayCacheItems :: List.List CacheItem.CacheItem
+    , replayCacheItems :: !(List.List CacheItem.CacheItem)
     } deriving (Eq,Generics.Generic,Show)
 
 instance Binary.Binary Replay where
diff --git a/octane.cabal b/octane.cabal
--- a/octane.cabal
+++ b/octane.cabal
@@ -3,7 +3,7 @@
 -- see: https://github.com/sol/hpack
 
 name:           octane
-version:        0.5.0
+version:        0.5.1
 synopsis:       Parse Rocket League replays.
 description:    Octane parses Rocket League replays.
 category:       Game
diff --git a/package.yaml b/package.yaml
--- a/package.yaml
+++ b/package.yaml
@@ -71,4 +71,4 @@
     - -with-rtsopts=-N
     main: TestSuite.hs
     source-dirs: test-suite
-version: '0.5.0'
+version: '0.5.1'
