diff --git a/library/Octane/Analyzer.hs b/library/Octane/Analyzer.hs
--- a/library/Octane/Analyzer.hs
+++ b/library/Octane/Analyzer.hs
@@ -5,6 +5,7 @@
 import qualified Data.Binary as Binary
 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 Octane.Parser as Parser
 import qualified System.Environment as Environment
@@ -14,25 +15,37 @@
 type Frames = [Parser.Frame]
 type ActorId = Int
 
-find :: (a -> Bool) -> [a] -> Maybe a
-find p xs = xs & filter p & Maybe.listToMaybe
-
-getActorId :: (Parser.Replication -> Bool) -> (Parser.Replication -> ActorId) -> Frames -> Maybe ActorId
-getActorId p f frames = frames
-    & Maybe.mapMaybe (\ frame -> frame
-        & Parser.frameReplications
-        & find p
-        & fmap f)
-    & Maybe.listToMaybe
+-- { car actor id => player actor id }
+getCarActorIds :: Frames -> Map.Map ActorId ActorId
+getCarActorIds frames = frames
+    & concatMap Parser.frameReplications
+    & filter (\ replication -> replication
+        & Parser.replicationClassName
+        & flip Set.member carClassNames)
+    & Maybe.mapMaybe (\ replication -> let
+        carActorId = replication
+            & Parser.replicationActorId
+        maybeProperty = replication
+            & Parser.replicationProperties
+            & Map.lookup playerReplicationInfoPropertyName
+        in case maybeProperty of
+            Nothing -> Nothing
+            Just property -> case property of
+                Parser.PFlaggedInt _ playerActorId -> Just (carActorId, playerActorId)
+                _ -> Nothing)
+    & Map.fromList
 
-getBallActorId :: Frames -> Maybe ActorId
-getBallActorId frames = getActorId
-    (\ replication -> replication
+getBallActorIds :: Frames -> Set.Set ActorId
+getBallActorIds frames = frames
+    & concatMap Parser.frameReplications
+    & filter (\ replication -> replication
         & Parser.replicationClassName
         & (== ballClassName))
-    Parser.replicationActorId
-    frames
+    & map (\ replication -> replication
+        & Parser.replicationActorId)
+    & Set.fromList
 
+-- { player actor id => player name }
 getPlayerActorIds :: Frames -> Map.Map ActorId Text.Text
 getPlayerActorIds frames = frames
     & concatMap Parser.frameReplications
@@ -52,8 +65,8 @@
                 _ -> Nothing)
     & Map.fromList
 
-getRigidBodyStatesForActorId :: ActorId -> Frames -> [(Parser.Time, Point)]
-getRigidBodyStatesForActorId actorId frames = frames
+getRigidBodyStatesForActorId :: Frames -> ActorId -> [(Parser.Time, Point)]
+getRigidBodyStatesForActorId frames actorId = frames
     & concatMap (\ frame -> let
         time = Parser.frameTime frame
         in frame
@@ -70,22 +83,34 @@
             & map (\ (Parser.Vector x y z) -> (x, y, z))
             & map (\ point -> (time, point)))
 
+getBallRigidBodyStates :: Frames -> [[(Parser.Time, Point)]]
+getBallRigidBodyStates frames = frames
+    & getBallActorIds
+    & Set.toAscList
+    & map (\ actorId -> actorId
+        & getRigidBodyStatesForActorId frames)
+
 getBallLocations :: Frames -> Points
-getBallLocations frames = let
-    ballActorId = frames
-        & getBallActorId
-        & Maybe.fromJust
-    rigidBodyStates = frames
-        & getRigidBodyStatesForActorId ballActorId
-    in rigidBodyStates
-        & map snd
+getBallLocations frames = frames
+    & getBallRigidBodyStates
+    & concat
+    & map snd
 
+carClassNames :: Set.Set Text.Text
+carClassNames =
+    [ "TAGame.Car_Season_TA"
+    , "TAGame.Car_TA"
+    ] & map Text.pack & Set.fromList
+
 ballClassName :: Text.Text
 ballClassName = Text.pack "TAGame.Ball_TA"
 
 playerClassName :: Text.Text
 playerClassName = Text.pack "TAGame.PRI_TA"
 
+playerReplicationInfoPropertyName :: Text.Text
+playerReplicationInfoPropertyName = Text.pack "Engine.Pawn:PlayerReplicationInfo"
+
 rbsPropertyName :: Text.Text
 rbsPropertyName = Text.pack "TAGame.RBActor_TA:ReplicatedRBState"
 
@@ -196,6 +221,10 @@
     let playerActorIds = getPlayerActorIds frames
     putStrLn "Players:"
     playerActorIds & Map.toList & mapM_ print
+
+    let carActorIds = getCarActorIds frames
+    putStrLn "Cars:"
+    carActorIds & Map.toList & mapM_ print
 
 main :: IO ()
 main = do
diff --git a/library/Octane/Parser.hs b/library/Octane/Parser.hs
--- a/library/Octane/Parser.hs
+++ b/library/Octane/Parser.hs
@@ -77,7 +77,7 @@
             , frameDelta = delta
             , frameReplications = replications
             }
-    return (newContext, frame)
+    (newContext, frame) & DeepSeq.force & return
 
 getReplications :: Context -> Bits.BitGet (Context, [Replication])
 getReplications context = do
@@ -272,8 +272,8 @@
 
 getByteProperty :: Bits.BitGet PropValue
 getByteProperty = do
-    int <- getInt8
-    return (PByte int)
+    word <- getWord8
+    return (PByte word)
 
 getCamSettingsProperty :: Bits.BitGet PropValue
 getCamSettingsProperty = do
@@ -552,7 +552,7 @@
 
 data PropValue
     = PBoolean !Bool
-    | PByte !Int
+    | PByte !Word.Word8
     | PCamSettings !Float !Float !Float !Float !Float !Float
     | PDemolish !Bool !Int !Bool !Int !(Vector Int) !(Vector Int)
     | PEnum !Word.Word16 !Bool
@@ -626,8 +626,10 @@
     , thingClassId :: !Int
     , thingClassName :: !Text.Text
     , thingClassInit :: !ClassInit
-    } deriving (Show)
+    } deriving (Eq, Generics.Generic, Show)
 
+instance DeepSeq.NFData Thing
+
 type Time = Float
 
 type Delta = Float
@@ -671,8 +673,10 @@
     , contextClassPropertyMap :: !ClassPropertyMap
     , contextThings :: !(IntMap.IntMap Thing)
     , contextClassMap :: !ClassMap
-    } deriving (Show)
+    } deriving (Eq, Generics.Generic, Show)
 
+instance DeepSeq.NFData Context
+
 extractContext :: Type.Replay -> Context
 extractContext replay =
     Context
@@ -825,6 +829,14 @@
             Binary.getWord8
             (byte & BSL.fromStrict & BSL.map Type.reverseBits)
     word & fromIntegral & (\ x -> x :: Int.Int8) & fromIntegral & return
+
+getWord8 :: Bits.BitGet Word.Word8
+getWord8 = do
+    byte <- Bits.getByteString 1
+    let word = Binary.runGet
+            Binary.getWord8
+            (byte & BSL.fromStrict & BSL.map Type.reverseBits)
+    return word
 
 getActorId :: Bits.BitGet Int
 getActorId = getInt 1024
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.6.1
+version:        0.6.2
 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
@@ -68,4 +68,4 @@
     - -with-rtsopts=-N
     main: TestSuite.hs
     source-dirs: test-suite
-version: '0.6.1'
+version: '0.6.2'
diff --git a/stack.yaml b/stack.yaml
--- a/stack.yaml
+++ b/stack.yaml
@@ -1,1 +1,1 @@
-resolver: nightly-2016-05-30
+resolver: nightly-2016-06-04
