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
@@ -164,6 +164,12 @@
             & version
             & Version.versionBranch
             & map Word32.toWord32
+    -- Key frames aren't important for replays. Mark the first frame as a key
+    -- frame and the rest as regular frames.
+    let frames_ = replay
+            & frames
+            & zip [0 :: Int ..]
+            & map (\ (index, frame) -> frame { Frame.isKeyFrame = index == 0 })
 
     pure OptimizedReplay.OptimizedReplay
         { OptimizedReplay.version1 = version1
@@ -171,15 +177,14 @@
         , OptimizedReplay.label = "TAGame.Replay_Soccar_TA"
         , OptimizedReplay.properties = replay & metadata & Map.mapKeys Text.Text & Dictionary.Dictionary
         , OptimizedReplay.levels = replay & levels & map Text.Text & List.List
-        , OptimizedReplay.keyFrames = replay
-            & frames
+        , OptimizedReplay.keyFrames = frames_
             & filter Frame.isKeyFrame
             & map (\ frame -> KeyFrame.KeyFrame
                 (Frame.time frame)
                 (frame & Frame.number & Word32.toWord32)
-                0) -- TODO: This is incorrect
+                0)
             & List.List
-        , OptimizedReplay.frames = replay & frames
+        , OptimizedReplay.frames = frames_
         , OptimizedReplay.messages = replay
             & messages
             & Map.toList
@@ -201,7 +206,9 @@
             & map Text.Text
             & List.List
         , OptimizedReplay.objects = List.List [] -- TODO
-        , OptimizedReplay.names = List.List [] -- TODO
+        -- TODO: This list is usually empty. Also the parser doesn't use it at
+        -- all. Is it safe for it to always be empty?
+        , OptimizedReplay.names = List.List []
         , OptimizedReplay.classes = List.List [] -- TODO
         , OptimizedReplay.cache = List.List [] -- TODO
         }
diff --git a/library/Octane/Utility/Parser.hs b/library/Octane/Utility/Parser.hs
--- a/library/Octane/Utility/Parser.hs
+++ b/library/Octane/Utility/Parser.hs
@@ -274,7 +274,7 @@
         "explosion" -> getExplosionProperty
         "flagged_int" -> getFlaggedIntProperty
         "float" -> getFloatProperty
-        "game_mode" -> getGameModeProperty
+        "game_mode" -> getGameModeProperty context
         "int" -> getIntProperty
         "loadout_online" -> getLoadoutOnlineProperty
         "loadout" -> getLoadoutProperty
@@ -361,9 +361,10 @@
     pure (Value.VFloat float)
 
 
-getGameModeProperty :: Bits.BitGet Value.Value
-getGameModeProperty = do
-    x <- Bits.getWord8 2
+getGameModeProperty :: Context -> Bits.BitGet Value.Value
+getGameModeProperty context = do
+    let numBits = if atLeastNeoTokyo context then 8 else 2
+    x <- Bits.getWord8 numBits
     pure (Value.VGameMode (Word8.toWord8 x))
 
 
@@ -445,6 +446,7 @@
     vector <- Vector.getFloatVector
     pure (Value.VRelativeRotation vector)
 
+
 getReservationProperty :: Context -> Bits.BitGet Value.Value
 getReservationProperty context = do
     -- I think this is the connection order. The first player to connect
@@ -458,9 +460,8 @@
     b <- getBool
 
     -- The Neo Tokyo update added 6 bits to the reservation property that are
-    -- always (as far as I can tell) 0. The only way to know about these bits
-    -- is to check the top-level version number in the replay.
-    Monad.when (contextVersion context >= neoTokyoVersion) (do
+    -- always (as far as I can tell) 0.
+    Monad.when (atLeastNeoTokyo context) (do
         x <- Bits.getWord8 6
         Monad.when (x /= 0b000000) (do
             fail (Printf.printf "Read 6 reservation bits and they weren't all 0! 0b%06b" x)))
@@ -599,6 +600,17 @@
         , replay & ReplayWithoutFrames.version2
         ] & map Word32.fromWord32 & Version.makeVersion
     }
+
+
+-- Helpers
+
+
+-- Some values are parsed differently depending on the version of the game that
+-- saved them. So far, all differences happened with the Neo Tokyo patch. This
+-- function takes a context and returns true if the replay was saved by a game
+-- running at least the Neo Tokyo version.
+atLeastNeoTokyo :: Context -> Bool
+atLeastNeoTokyo context = contextVersion context >= neoTokyoVersion
 
 
 -- Constants
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.13.2
+version:        0.13.3
 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
@@ -79,4 +79,4 @@
     - -with-rtsopts=-N
     main: Main.hs
     source-dirs: test-suite
-version: '0.13.2'
+version: '0.13.3'
