diff --git a/rattletrap.cabal b/rattletrap.cabal
--- a/rattletrap.cabal
+++ b/rattletrap.cabal
@@ -1,7 +1,7 @@
 cabal-version: 2.2
 
 name: rattletrap
-version: 11.2.5
+version: 11.2.6
 synopsis: Parse and generate Rocket League replays.
 description: Rattletrap parses and generates Rocket League replays.
 
@@ -129,8 +129,10 @@
     Rattletrap.Type.Attribute.Product
     Rattletrap.Type.Attribute.ProductValue
     Rattletrap.Type.Attribute.QWord
+    Rattletrap.Type.Attribute.RepStatTitle
     Rattletrap.Type.Attribute.Reservation
     Rattletrap.Type.Attribute.RigidBodyState
+    Rattletrap.Type.Attribute.Rotation
     Rattletrap.Type.Attribute.StatEvent
     Rattletrap.Type.Attribute.String
     Rattletrap.Type.Attribute.TeamPaint
@@ -174,6 +176,7 @@
     Rattletrap.Type.RemoteId.Epic
     Rattletrap.Type.RemoteId.PlayStation
     Rattletrap.Type.RemoteId.PsyNet
+    Rattletrap.Type.RemoteId.QQ
     Rattletrap.Type.RemoteId.Splitscreen
     Rattletrap.Type.RemoteId.Steam
     Rattletrap.Type.RemoteId.Switch
diff --git a/src/lib/Rattletrap/Console/Main.hs b/src/lib/Rattletrap/Console/Main.hs
--- a/src/lib/Rattletrap/Console/Main.hs
+++ b/src/lib/Rattletrap/Console/Main.hs
@@ -46,8 +46,10 @@
 import qualified Rattletrap.Type.Attribute.Product as Attribute.Product
 import qualified Rattletrap.Type.Attribute.ProductValue as Attribute.ProductValue
 import qualified Rattletrap.Type.Attribute.QWord as Attribute.QWord
+import qualified Rattletrap.Type.Attribute.RepStatTitle as Attribute.RepStatTitle
 import qualified Rattletrap.Type.Attribute.Reservation as Attribute.Reservation
 import qualified Rattletrap.Type.Attribute.RigidBodyState as Attribute.RigidBodyState
+import qualified Rattletrap.Type.Attribute.Rotation as Attribute.Rotation
 import qualified Rattletrap.Type.Attribute.StatEvent as Attribute.StatEvent
 import qualified Rattletrap.Type.Attribute.String as Attribute.String
 import qualified Rattletrap.Type.Attribute.TeamPaint as Attribute.TeamPaint
@@ -195,8 +197,10 @@
         , Attribute.Product.schema
         , Attribute.ProductValue.schema
         , Attribute.QWord.schema
+        , Attribute.RepStatTitle.schema
         , Attribute.Reservation.schema
         , Attribute.RigidBodyState.schema
+        , Attribute.Rotation.schema
         , Attribute.StatEvent.schema
         , Attribute.String.schema
         , Attribute.TeamPaint.schema
diff --git a/src/lib/Rattletrap/Data.hs b/src/lib/Rattletrap/Data.hs
--- a/src/lib/Rattletrap/Data.hs
+++ b/src/lib/Rattletrap/Data.hs
@@ -80,6 +80,7 @@
   , "Archetypes.Ball.Ball_Breakout"
   , "Archetypes.Ball.Ball_Default"
   , "Archetypes.Ball.Ball_Puck"
+  , "Archetypes.Ball.Ball_Trajectory"
   , "Archetypes.Ball.CubeBall"
   , "Archetypes.Car.Car_Default"
   , "Archetypes.GameEvent.GameEvent_Season:CarArchetype"
@@ -90,6 +91,7 @@
   , "TAGame.Ball_Haunted_TA"
   , "TAGame.Ball_TA"
   , "TAGame.CameraSettingsActor_TA"
+  , "TAGame.Cannon_TA"
   , "TAGame.Car_Season_TA"
   , "TAGame.Car_TA"
   , "TAGame.CarComponent_Boost_TA"
@@ -105,6 +107,7 @@
   , "TAGame.GameEvent_Soccar_TA"
   , "TAGame.GameEvent_SoccarPrivate_TA"
   , "TAGame.GameEvent_SoccarSplitscreen_TA"
+  , "TAGame.GameEvent_Tutorial_TA"
   , "TAGame.GRI_TA"
   , "TAGame.MaxTimeWarningData_TA"
   , "TAGame.PickupTimer_TA"
@@ -141,6 +144,7 @@
   , "Archetypes.Ball.Ball_Breakout"
   , "Archetypes.Ball.Ball_Default"
   , "Archetypes.Ball.Ball_Puck"
+  , "Archetypes.Ball.Ball_Trajectory"
   , "Archetypes.Ball.CubeBall"
   , "Archetypes.Car.Car_Default"
   , "Archetypes.GameEvent.GameEvent_Season:CarArchetype"
@@ -167,6 +171,8 @@
   , ("Archetypes.Ball.Ball_God", "TAGame.Ball_God_TA")
   , ("Archetypes.Ball.Ball_Haunted", "TAGame.Ball_Haunted_TA")
   , ("Archetypes.Ball.Ball_Puck", "TAGame.Ball_TA")
+  , ("Archetypes.Ball.Ball_Training", "TAGame.Ball_TA")
+  , ("Archetypes.Ball.Ball_Trajectory", "TAGame.Ball_TA")
   , ("Archetypes.Ball.CubeBall", "TAGame.Ball_TA")
   , ("Archetypes.Car.Car_Default", "TAGame.Car_TA")
   , ("Archetypes.CarComponents.CarComponent_Boost", "TAGame.CarComponent_Boost_TA")
@@ -205,6 +211,7 @@
   , ("Archetypes.SpecialPickups.SpecialPickup_Tornado", "TAGame.SpecialPickup_Tornado_TA")
   , ("Archetypes.Teams.Team0", "TAGame.Team_Soccar_TA")
   , ("Archetypes.Teams.Team1", "TAGame.Team_Soccar_TA")
+  , ("Archetypes.Tutorial.Cannon", "TAGame.Cannon_TA")
   , ("GameInfo_Basketball.GameInfo.GameInfo_Basketball:GameReplicationInfoArchetype", "TAGame.GRI_TA")
   , ("GameInfo_Breakout.GameInfo.GameInfo_Breakout:GameReplicationInfoArchetype", "TAGame.GRI_TA")
   , ("GameInfo_FootBall.GameInfo.GameInfo_FootBall:Archetype", "TAGame.GameEvent_Football_TA")
@@ -217,6 +224,8 @@
   , ("GameInfo_Items.GameInfo.GameInfo_Items:GameReplicationInfoArchetype", "TAGame.GRI_TA")
   , ("GameInfo_Season.GameInfo.GameInfo_Season:GameReplicationInfoArchetype", "TAGame.GRI_TA")
   , ("GameInfo_Soccar.GameInfo.GameInfo_Soccar:GameReplicationInfoArchetype", "TAGame.GRI_TA")
+  , ("GameInfo_Tutorial.GameEvent.GameEvent_Tutorial_Aerial", "TAGame.GameEvent_Tutorial_TA")
+  , ("GameInfo_Tutorial.GameInfo.GameInfo_Tutorial:GameReplicationInfoArchetype", "TAGame.GRI_TA")
   , ("Haunted_TrainStation_P.TheWorld:PersistentLevel.HauntedBallTrapTrigger_TA_0", "TAGame.HauntedBallTrapTrigger_TA")
   , ("Haunted_TrainStation_P.TheWorld:PersistentLevel.HauntedBallTrapTrigger_TA_1", "TAGame.HauntedBallTrapTrigger_TA")
   , ("ProjectX.Default__NetModeReplicator_X", "ProjectX.NetModeReplicator")
@@ -238,12 +247,15 @@
   [ ("Engine.Actor:bBlockActors", AttributeType.Boolean)
   , ("Engine.Actor:bCollideActors", AttributeType.Boolean)
   , ("Engine.Actor:bHidden", AttributeType.Boolean)
+  , ("Engine.Actor:bTearOff", AttributeType.Boolean)
   , ("Engine.Actor:DrawScale", AttributeType.Float)
   , ("Engine.Actor:RemoteRole", AttributeType.Enum)
   , ("Engine.Actor:Role", AttributeType.Enum)
+  , ("Engine.Actor:Rotation", AttributeType.Rotation)
   , ("Engine.GameReplicationInfo:bMatchIsOver", AttributeType.Boolean)
   , ("Engine.GameReplicationInfo:GameClass", AttributeType.FlaggedInt)
   , ("Engine.GameReplicationInfo:ServerName", AttributeType.String)
+  , ("Engine.Pawn:HealthMax", AttributeType.Int)
   , ("Engine.Pawn:PlayerReplicationInfo", AttributeType.FlaggedInt)
   , ("Engine.PlayerReplicationInfo:bBot", AttributeType.Boolean)
   , ("Engine.PlayerReplicationInfo:bIsSpectator", AttributeType.Boolean)
@@ -292,10 +304,13 @@
   , ("TAGame.CameraSettingsActor_TA:CameraYaw", AttributeType.Byte)
   , ("TAGame.CameraSettingsActor_TA:PRI", AttributeType.FlaggedInt)
   , ("TAGame.CameraSettingsActor_TA:ProfileSettings", AttributeType.CamSettings)
+  , ("TAGame.Cannon_TA:FireCount", AttributeType.Byte)
+  , ("TAGame.Cannon_TA:Pitch", AttributeType.Float)
   , ("TAGame.Car_TA:AddedBallForceMultiplier", AttributeType.Float)
   , ("TAGame.Car_TA:AddedCarForceMultiplier", AttributeType.Float)
   , ("TAGame.Car_TA:AttachedPickup", AttributeType.FlaggedInt)
   , ("TAGame.Car_TA:ClubColors", AttributeType.ClubColors)
+  , ("TAGame.Car_TA:ReplicatedCarScale", AttributeType.Float)
   , ("TAGame.Car_TA:ReplicatedDemolish_CustomFX", AttributeType.CustomDemolish)
   , ("TAGame.Car_TA:ReplicatedDemolish", AttributeType.Demolish)
   , ("TAGame.Car_TA:ReplicatedDemolishGoalExplosion", AttributeType.CustomDemolish)
@@ -325,10 +340,15 @@
   , ("TAGame.CrowdManager_TA:ReplicatedGlobalOneShotSound", AttributeType.FlaggedInt)
   , ("TAGame.GameEvent_Soccar_TA:bBallHasBeenHit", AttributeType.Boolean)
   , ("TAGame.GameEvent_Soccar_TA:bClubMatch", AttributeType.Boolean)
+  , ("TAGame.GameEvent_Soccar_TA:bMatchEnded", AttributeType.Boolean)
+  , ("TAGame.GameEvent_Soccar_TA:bNoContest", AttributeType.Boolean)
   , ("TAGame.GameEvent_Soccar_TA:bOverTime", AttributeType.Boolean)
   , ("TAGame.GameEvent_Soccar_TA:bUnlimitedTime", AttributeType.Boolean)
   , ("TAGame.GameEvent_Soccar_TA:GameTime", AttributeType.Int)
+  , ("TAGame.GameEvent_Soccar_TA:GameWinner", AttributeType.FlaggedInt)
+  , ("TAGame.GameEvent_Soccar_TA:MatchWinner", AttributeType.FlaggedInt)
   , ("TAGame.GameEvent_Soccar_TA:MaxScore", AttributeType.Int)
+  , ("TAGame.GameEvent_Soccar_TA:MVP", AttributeType.FlaggedInt)
   , ("TAGame.GameEvent_Soccar_TA:ReplicatedMusicStinger", AttributeType.MusicStinger)
   , ("TAGame.GameEvent_Soccar_TA:ReplicatedScoredOnTeam", AttributeType.Byte)
   , ("TAGame.GameEvent_Soccar_TA:ReplicatedServerPerformanceState", AttributeType.Byte)
@@ -338,6 +358,7 @@
   , ("TAGame.GameEvent_Soccar_TA:SeriesLength", AttributeType.Int)
   , ("TAGame.GameEvent_Soccar_TA:SubRulesArchetype", AttributeType.FlaggedInt)
   , ("TAGame.GameEvent_SoccarPrivate_TA:MatchSettings", AttributeType.PrivateMatchSettings)
+  , ("TAGame.GameEvent_TA:bAllowReadyUp", AttributeType.Boolean)
   , ("TAGame.GameEvent_TA:bCanVoteToForfeit", AttributeType.Boolean)
   , ("TAGame.GameEvent_TA:bHasLeaveMatchPenalty", AttributeType.Boolean)
   , ("TAGame.GameEvent_TA:BotSkill", AttributeType.Int)
@@ -385,6 +406,7 @@
   , ("TAGame.PRI_TA:PrimaryTitle", AttributeType.Title)
   , ("TAGame.PRI_TA:ReplicatedGameEvent", AttributeType.FlaggedInt)
   , ("TAGame.PRI_TA:ReplicatedWorstNetQualityBeyondLatency", AttributeType.Byte)
+  , ("TAGame.PRI_TA:RepStatTitles", AttributeType.RepStatTitle)
   , ("TAGame.PRI_TA:SecondaryTitle", AttributeType.Title)
   , ("TAGame.PRI_TA:SkillTier", AttributeType.FlaggedByte)
   , ("TAGame.PRI_TA:SpectatorShortcut", AttributeType.Int)
@@ -416,6 +438,7 @@
   , ("TAGame.Team_TA:GameEvent", AttributeType.FlaggedInt)
   , ("TAGame.Team_TA:LogoData", AttributeType.FlaggedInt)
   , ("TAGame.Vehicle_TA:bDriving", AttributeType.Boolean)
+  , ("TAGame.Vehicle_TA:bPodiumMode", AttributeType.Boolean)
   , ("TAGame.Vehicle_TA:bReplicatedHandbrake", AttributeType.Boolean)
   , ("TAGame.Vehicle_TA:ReplicatedSteer", AttributeType.Byte)
   , ("TAGame.Vehicle_TA:ReplicatedThrottle", AttributeType.Byte)
diff --git a/src/lib/Rattletrap/Type/Attribute/RepStatTitle.hs b/src/lib/Rattletrap/Type/Attribute/RepStatTitle.hs
new file mode 100644
--- /dev/null
+++ b/src/lib/Rattletrap/Type/Attribute/RepStatTitle.hs
@@ -0,0 +1,56 @@
+module Rattletrap.Type.Attribute.RepStatTitle where
+
+import qualified Rattletrap.BitGet as BitGet
+import qualified Rattletrap.BitPut as BitPut
+import qualified Rattletrap.Schema as Schema
+import qualified Rattletrap.Type.Attribute.FlaggedInt as FlaggedInt
+import qualified Rattletrap.Type.Str as Str
+import qualified Rattletrap.Type.U32 as U32
+import qualified Rattletrap.Utility.Json as Json
+
+data RepStatTitle = RepStatTitle
+  { unknown :: Bool
+  , name :: Str.Str
+  , target :: FlaggedInt.FlaggedInt
+  , value :: U32.U32
+  }
+  deriving (Eq, Show)
+
+instance Json.FromJSON RepStatTitle where
+  parseJSON = Json.withObject "RepStatTitle" $ \object -> do
+    unknown <- Json.required object "unknown"
+    name <- Json.required object "name"
+    target <- Json.required object "target"
+    value <- Json.required object "value"
+    pure RepStatTitle { unknown, name, target, value }
+
+instance Json.ToJSON RepStatTitle where
+  toJSON x = Json.object
+    [ Json.pair "unknown" $ unknown x
+    , Json.pair "name" $ name x
+    , Json.pair "target" $ target x
+    , Json.pair "value" $ value x
+    ]
+
+schema :: Schema.Schema
+schema = Schema.named "attribute-rep-stat-title" $ Schema.object
+  [ (Json.pair "unknown" $ Schema.ref Schema.boolean, True)
+  , (Json.pair "name" $ Schema.ref Str.schema, True)
+  , (Json.pair "target" $ Schema.ref FlaggedInt.schema, True)
+  , (Json.pair "value" $ Schema.ref U32.schema, True)
+  ]
+
+bitPut :: RepStatTitle -> BitPut.BitPut
+bitPut x =
+  BitPut.bool (unknown x)
+    <> Str.bitPut (name x)
+    <> FlaggedInt.bitPut (target x)
+    <> U32.bitPut (value x)
+
+bitGet :: BitGet.BitGet RepStatTitle
+bitGet = BitGet.label "RepStatTitle" $ do
+  unknown <- BitGet.label "unknown" BitGet.bool
+  name <- BitGet.label "name" Str.bitGet
+  target <- BitGet.label "target" FlaggedInt.bitGet
+  value <- BitGet.label "value" U32.bitGet
+  pure RepStatTitle { unknown, name, target, value }
diff --git a/src/lib/Rattletrap/Type/Attribute/Rotation.hs b/src/lib/Rattletrap/Type/Attribute/Rotation.hs
new file mode 100644
--- /dev/null
+++ b/src/lib/Rattletrap/Type/Attribute/Rotation.hs
@@ -0,0 +1,28 @@
+module Rattletrap.Type.Attribute.Rotation where
+
+import qualified Rattletrap.BitGet as BitGet
+import qualified Rattletrap.BitPut as BitPut
+import qualified Rattletrap.Schema as Schema
+import qualified Rattletrap.Type.Int8Vector as Int8Vector
+import qualified Rattletrap.Utility.Json as Json
+
+newtype Rotation = Rotation
+  { value :: Int8Vector.Int8Vector
+  } deriving (Eq, Show)
+
+instance Json.FromJSON Rotation where
+  parseJSON = fmap Rotation . Json.parseJSON
+
+instance Json.ToJSON Rotation where
+  toJSON = Json.toJSON . value
+
+schema :: Schema.Schema
+schema = Schema.named "attribute-rotation" $ Schema.ref Int8Vector.schema
+
+bitPut :: Rotation -> BitPut.BitPut
+bitPut = Int8Vector.bitPut . value
+
+bitGet :: BitGet.BitGet Rotation
+bitGet = BitGet.label "Rotation" $ do
+  value <- BitGet.label "value" Int8Vector.bitGet
+  pure Rotation { value }
diff --git a/src/lib/Rattletrap/Type/AttributeType.hs b/src/lib/Rattletrap/Type/AttributeType.hs
--- a/src/lib/Rattletrap/Type/AttributeType.hs
+++ b/src/lib/Rattletrap/Type/AttributeType.hs
@@ -31,8 +31,10 @@
   | PlayerHistoryKey
   | PrivateMatchSettings
   | QWord
+  | RepStatTitle
   | Reservation
   | RigidBodyState
+  | Rotation
   | StatEvent
   | String
   | TeamPaint
diff --git a/src/lib/Rattletrap/Type/AttributeValue.hs b/src/lib/Rattletrap/Type/AttributeValue.hs
--- a/src/lib/Rattletrap/Type/AttributeValue.hs
+++ b/src/lib/Rattletrap/Type/AttributeValue.hs
@@ -37,8 +37,10 @@
 import qualified Rattletrap.Type.Attribute.PlayerHistoryKey as PlayerHistoryKey
 import qualified Rattletrap.Type.Attribute.PrivateMatchSettings as PrivateMatchSettings
 import qualified Rattletrap.Type.Attribute.QWord as QWord
+import qualified Rattletrap.Type.Attribute.RepStatTitle as RepStatTitle
 import qualified Rattletrap.Type.Attribute.Reservation as Reservation
 import qualified Rattletrap.Type.Attribute.RigidBodyState as RigidBodyState
+import qualified Rattletrap.Type.Attribute.Rotation as Rotation
 import qualified Rattletrap.Type.Attribute.StatEvent as StatEvent
 import qualified Rattletrap.Type.Attribute.String as String
 import qualified Rattletrap.Type.Attribute.TeamPaint as TeamPaint
@@ -82,8 +84,10 @@
   | PlayerHistoryKey PlayerHistoryKey.PlayerHistoryKey
   | PrivateMatchSettings PrivateMatchSettings.PrivateMatchSettings
   | QWord QWord.QWord
+  | RepStatTitle RepStatTitle.RepStatTitle
   | Reservation Reservation.Reservation
   | RigidBodyState RigidBodyState.RigidBodyState
+  | Rotation Rotation.Rotation
   | StatEvent StatEvent.StatEvent
   | String String.String
   | TeamPaint TeamPaint.TeamPaint
@@ -124,8 +128,10 @@
     , fmap PlayerHistoryKey $ Json.required object "player_history_key"
     , fmap PrivateMatchSettings $ Json.required object "private_match_settings"
     , fmap QWord $ Json.required object "q_word"
+    , fmap RepStatTitle $ Json.required object "rep_stat_title"
     , fmap Reservation $ Json.required object "reservation"
     , fmap RigidBodyState $ Json.required object "rigid_body_state"
+    , fmap Rotation $ Json.required object "rotation"
     , fmap StatEvent $ Json.required object "stat_event"
     , fmap String $ Json.required object "string"
     , fmap TeamPaint $ Json.required object "team_paint"
@@ -167,8 +173,10 @@
     PrivateMatchSettings y ->
       Json.object [Json.pair "private_match_settings" y]
     QWord y -> Json.object [Json.pair "q_word" y]
+    RepStatTitle y -> Json.object [Json.pair "rep_stat_title" y]
     Reservation y -> Json.object [Json.pair "reservation" y]
     RigidBodyState y -> Json.object [Json.pair "rigid_body_state" y]
+    Rotation y -> Json.object [Json.pair "rotation" y]
     StatEvent y -> Json.object [Json.pair "stat_event" y]
     String y -> Json.object [Json.pair "string" y]
     TeamPaint y -> Json.object [Json.pair "team_paint" y]
@@ -209,8 +217,10 @@
   , ("player_history_key", PlayerHistoryKey.schema)
   , ("private_match_settings", PrivateMatchSettings.schema)
   , ("q_word", QWord.schema)
+  , ("rep_stat_title", RepStatTitle.schema)
   , ("reservation", Reservation.schema)
   , ("rigid_body_state", RigidBodyState.schema)
+  , ("rotation", Rotation.schema)
   , ("stat_event", StatEvent.schema)
   , ("string", String.schema)
   , ("team_paint", TeamPaint.schema)
@@ -251,8 +261,10 @@
   PlayerHistoryKey x -> PlayerHistoryKey.bitPut x
   PrivateMatchSettings x -> PrivateMatchSettings.bitPut x
   QWord x -> QWord.bitPut x
+  RepStatTitle x -> RepStatTitle.bitPut x
   Reservation x -> Reservation.bitPut x
   RigidBodyState x -> RigidBodyState.bitPut x
+  Rotation x -> Rotation.bitPut x
   StatEvent x -> StatEvent.bitPut x
   String x -> String.bitPut x
   TeamPaint x -> TeamPaint.bitPut x
@@ -308,9 +320,11 @@
     AttributeType.PrivateMatchSettings ->
       fmap PrivateMatchSettings PrivateMatchSettings.bitGet
     AttributeType.QWord -> fmap QWord QWord.bitGet
+    AttributeType.RepStatTitle -> fmap RepStatTitle RepStatTitle.bitGet
     AttributeType.Reservation -> fmap Reservation $ Reservation.bitGet version
     AttributeType.RigidBodyState ->
       fmap RigidBodyState $ RigidBodyState.bitGet version
+    AttributeType.Rotation -> fmap Rotation Rotation.bitGet
     AttributeType.StatEvent -> fmap StatEvent StatEvent.bitGet
     AttributeType.String -> fmap String String.bitGet
     AttributeType.TeamPaint -> fmap TeamPaint TeamPaint.bitGet
diff --git a/src/lib/Rattletrap/Type/Property/Byte.hs b/src/lib/Rattletrap/Type/Property/Byte.hs
--- a/src/lib/Rattletrap/Type/Property/Byte.hs
+++ b/src/lib/Rattletrap/Type/Property/Byte.hs
@@ -31,7 +31,9 @@
 byteGet :: ByteGet.ByteGet Byte
 byteGet = ByteGet.label "Byte" $ do
   key <- ByteGet.label "key" Str.byteGet
-  value <- ByteGet.label "value" $ Monad.whenMaybe
-    (Str.toString key /= "OnlinePlatform_Steam")
-    Str.byteGet
+  let
+    isSteam = key == Str.fromString "OnlinePlatform_Steam"
+    isPlayStation = key == Str.fromString "OnlinePlatform_PS4"
+  value <- ByteGet.label "value"
+    $ Monad.whenMaybe (not $ isSteam || isPlayStation) Str.byteGet
   pure Byte { key, value }
diff --git a/src/lib/Rattletrap/Type/RemoteId.hs b/src/lib/Rattletrap/Type/RemoteId.hs
--- a/src/lib/Rattletrap/Type/RemoteId.hs
+++ b/src/lib/Rattletrap/Type/RemoteId.hs
@@ -8,6 +8,7 @@
 import qualified Rattletrap.Type.RemoteId.Epic as Epic
 import qualified Rattletrap.Type.RemoteId.PlayStation as PlayStation
 import qualified Rattletrap.Type.RemoteId.PsyNet as PsyNet
+import qualified Rattletrap.Type.RemoteId.QQ as QQ
 import qualified Rattletrap.Type.RemoteId.Splitscreen as Splitscreen
 import qualified Rattletrap.Type.RemoteId.Steam as Steam
 import qualified Rattletrap.Type.RemoteId.Switch as Switch
@@ -19,6 +20,7 @@
 data RemoteId
   = PlayStation PlayStation.PlayStation
   | PsyNet PsyNet.PsyNet
+  | QQ QQ.QQ
   | Splitscreen Splitscreen.Splitscreen
   -- ^ Really only 24 bits.
   | Steam Steam.Steam
@@ -31,6 +33,7 @@
   parseJSON = Json.withObject "RemoteId" $ \object -> Foldable.asum
     [ fmap PlayStation $ Json.required object "play_station"
     , fmap PsyNet $ Json.required object "psy_net"
+    , fmap QQ $ Json.required object "qq"
     , fmap Splitscreen $ Json.required object "splitscreen"
     , fmap Steam $ Json.required object "steam"
     , fmap Switch $ Json.required object "switch"
@@ -42,6 +45,7 @@
   toJSON x = case x of
     PlayStation y -> Json.object [Json.pair "play_station" y]
     PsyNet y -> Json.object [Json.pair "psy_net" y]
+    QQ y -> Json.object [Json.pair "qq" y]
     Splitscreen y -> Json.object [Json.pair "splitscreen" y]
     Steam y -> Json.object [Json.pair "steam" y]
     Switch y -> Json.object [Json.pair "switch" y]
@@ -53,6 +57,7 @@
   (\(k, v) -> Schema.object [(Json.pair k v, True)])
   [ ("play_station", Schema.ref PlayStation.schema)
   , ("psy_net", Schema.ref PsyNet.schema)
+  , ("qq", Schema.ref QQ.schema)
   , ("splitscreen", Schema.ref Splitscreen.schema)
   , ("steam", Schema.ref Steam.schema)
   , ("switch", Schema.ref Switch.schema)
@@ -64,6 +69,7 @@
 bitPut remoteId = case remoteId of
   PlayStation x -> PlayStation.bitPut x
   PsyNet x -> PsyNet.bitPut x
+  QQ x -> QQ.bitPut x
   Splitscreen x -> Splitscreen.bitPut x
   Steam x -> Steam.bitPut x
   Switch x -> Switch.bitPut x
@@ -76,6 +82,7 @@
   1 -> fmap Steam Steam.bitGet
   2 -> fmap PlayStation $ PlayStation.bitGet version
   4 -> fmap Xbox Xbox.bitGet
+  5 -> fmap QQ QQ.bitGet
   6 -> fmap Switch Switch.bitGet
   7 -> fmap PsyNet $ PsyNet.bitGet version
   11 -> fmap Epic Epic.bitGet
diff --git a/src/lib/Rattletrap/Type/RemoteId/QQ.hs b/src/lib/Rattletrap/Type/RemoteId/QQ.hs
new file mode 100644
--- /dev/null
+++ b/src/lib/Rattletrap/Type/RemoteId/QQ.hs
@@ -0,0 +1,32 @@
+module Rattletrap.Type.RemoteId.QQ where
+
+import qualified Rattletrap.BitGet as BitGet
+import qualified Rattletrap.BitPut as BitPut
+import qualified Rattletrap.Schema as Schema
+import qualified Rattletrap.Type.U64 as U64
+import qualified Rattletrap.Utility.Json as Json
+
+newtype QQ
+  = QQ U64.U64
+  deriving (Eq, Show)
+
+instance Json.FromJSON QQ where
+  parseJSON = fmap fromU64 . Json.parseJSON
+
+instance Json.ToJSON QQ where
+  toJSON = Json.toJSON . toU64
+
+fromU64 :: U64.U64 -> QQ
+fromU64 = QQ
+
+toU64 :: QQ -> U64.U64
+toU64 (QQ x) = x
+
+schema :: Schema.Schema
+schema = U64.schema
+
+bitPut :: QQ -> BitPut.BitPut
+bitPut = U64.bitPut . toU64
+
+bitGet :: BitGet.BitGet QQ
+bitGet = BitGet.label "QQ" $ fmap fromU64 U64.bitGet
diff --git a/src/lib/Rattletrap/Type/Replication/Spawned.hs b/src/lib/Rattletrap/Type/Replication/Spawned.hs
--- a/src/lib/Rattletrap/Type/Replication/Spawned.hs
+++ b/src/lib/Rattletrap/Type/Replication/Spawned.hs
@@ -118,7 +118,9 @@
 
 hasNameIndex :: Maybe Str.Str -> Version.Version -> Bool
 hasNameIndex matchType version =
-  Version.atLeast 868 14 0 version && matchType /= Just (Str.fromString "Lan")
+  Version.atLeast 868 20 0 version
+    || Version.atLeast 868 14 0 version
+    && (matchType /= Just (Str.fromString "Lan"))
 
 lookupName
   :: ClassAttributeMap.ClassAttributeMap
diff --git a/src/lib/Rattletrap/Utility/Json.hs b/src/lib/Rattletrap/Utility/Json.hs
--- a/src/lib/Rattletrap/Utility/Json.hs
+++ b/src/lib/Rattletrap/Utility/Json.hs
@@ -2,7 +2,6 @@
 {-# OPTIONS_GHC -Wno-orphans #-}
 
 {- hlint ignore "Avoid restricted extensions" -}
-{-# LANGUAGE CPP #-}
 {-# LANGUAGE FlexibleInstances #-}
 
 module Rattletrap.Utility.Json
@@ -18,31 +17,16 @@
 
 import qualified Data.Aeson as Aeson
 import qualified Data.Aeson.Encode.Pretty as Aeson
+import qualified Data.Aeson.Key as Key
 import qualified Data.Aeson.Types as Aeson
 import qualified Data.ByteString as ByteString
 import qualified Data.ByteString.Lazy as LazyByteString
 
-# if MIN_VERSION_aeson(2, 0, 0)
-
-import qualified Data.Aeson.Key as Key
-
 toKey :: String -> Key.Key
 toKey = Key.fromString
 
 fromKey :: Key.Key -> String
 fromKey = Key.toString
-
-# else
-
-import qualified Data.Text as Text
-
-toKey :: String -> Text.Text
-toKey = Text.pack
-
-fromKey :: Text.Text -> String
-fromKey = Text.unpack
-
-# endif
 
 instance Aeson.KeyValue (String, Aeson.Value) where
   k .= v = (fromKey k, Aeson.toJSON v)
diff --git a/src/test/Main.hs b/src/test/Main.hs
--- a/src/test/Main.hs
+++ b/src/test/Main.hs
@@ -52,9 +52,11 @@
 replays =
   [ ("0008", "a flip time") -- https://github.com/tfausak/rattletrap/commit/ee7afa0
   , ("000b", "nintendo switch") -- https://github.com/tfausak/rattletrap/pull/60
+  , ("0121", "RLCS 2") -- https://github.com/nickbabcock/boxcars/pull/120
   , ("0416", "v1.78 demolition") -- https://github.com/tfausak/rattletrap/pull/164
   , ("07e9", "a game mode before Neo Tokyo") -- https://github.com/tfausak/rattletrap/commit/b806f9b
   , ("0ad2", "some Latin-1 text") -- https://github.com/tfausak/rattletrap/commit/13a8b2d
+  , ("0ca5", "with QQ remote ID") -- https://github.com/nickbabcock/boxcars/pull/69
   , ("0e76", "v1.95 rumble") -- https://github.com/tfausak/rattletrap/pull/237
   , ("1205", "rumble mode") -- https://github.com/tfausak/rattletrap/commit/5256500
   , ("160c", "a dedicated server IP") -- https://github.com/tfausak/rattletrap/commit/5c64a6d
@@ -87,12 +89,15 @@
   , ("419a", "a club match") -- https://github.com/tfausak/rattletrap/commit/8e35043
   , ("42f0", "reservations after Neo Tokyo") -- https://github.com/tfausak/rattletrap/commit/163684f
   , ("42f2", "anniversary ball") -- https://github.com/tfausak/rattletrap/issues/147
+  , ("43a9", "tutorial") -- https://github.com/nickbabcock/boxcars/pull/70
   , ("4bc3", "with timed out attribute") -- https://github.com/tfausak/rattletrap/pull/98
   , ("504e", "some messages") -- https://github.com/tfausak/rattletrap/commit/1d4a538
+  , ("5123", "rep stat title") -- https://github.com/nickbabcock/boxcars/pull/78
   , ("520e", "no pickup attribute") -- https://github.com/tfausak/rattletrap/pull/38
   , ("524f", "quat edge case") -- https://github.com/tfausak/rattletrap/pull/87
   , ("52aa", "a match-ending attribute") -- https://github.com/tfausak/rattletrap/commit/5c64a6d
   , ("540d", "a demolish attribute") -- https://github.com/tfausak/rattletrap/commit/65ce033
+  , ("54ae", "replicated car scale") -- https://github.com/nickbabcock/boxcars/pull/79
   , ("551c", "private match settings") -- https://github.com/tfausak/rattletrap/commit/5c9ebfc
   , ("5a06", "esports items") -- https://github.com/tfausak/rattletrap/pull/114
   , ("5e0b", "max channels") -- https://github.com/tfausak/rattletrap/issues/254
@@ -105,6 +110,7 @@
   , ("7083", "weird basketball capitalization") -- https://github.com/tfausak/rattletrap/pull/63
   , ("7109", "a boost modifier") -- https://github.com/tfausak/rattletrap/commit/ee7afa0
   , ("7256", "special edition") -- https://github.com/tfausak/rattletrap/pull/103
+  , ("7588", "another malformed byte property") -- https://github.com/nickbabcock/boxcars/pull/68
   , ("75ce", "primary and secondary titles") -- https://github.com/tfausak/rattletrap/pull/69
   , ("7bf6", "an online loadouts attribute") -- https://github.com/tfausak/rattletrap/commit/89d02f7
   , ("81d1", "gridiron") -- https://github.com/tfausak/rattletrap/pull/180
@@ -128,10 +134,12 @@
   , ("a7f0", "a ready attribute") -- https://github.com/tfausak/rattletrap/commit/78af1fd
   , ("a9df", "salty shores patch 1.45") -- https://github.com/tfausak/rattletrap/pull/78
   , ("aa70", "patch 1.50 - TitleID attribute") -- https://github.com/tfausak/rattletrap/pull/93
+  , ("ae46", "mvp") -- https://github.com/nickbabcock/boxcars/pull/80
   , ("afb1", "patch 1.37") -- https://github.com/tfausak/rattletrap/pull/48
   , ("b9f9", "a party leader") -- https://github.com/tfausak/rattletrap/commit/bba2cfd
   , ("c14f", "some mutators") -- https://github.com/tfausak/rattletrap/commit/bba2cfd
   , ("c23b", "new psynet id") -- https://github.com/tfausak/rattletrap/pull/118
+  , ("c62c", "more boolean attributes") -- https://github.com/nickbabcock/boxcars/pull/77
   , ("c837", "a spectator") -- https://github.com/tfausak/rattletrap/commit/bba2cfd
   , ("cc4c", "after Starbase ARC") -- https://github.com/tfausak/rattletrap/pull/20
   , ("d044", "hoops mutators") -- https://github.com/tfausak/rattletrap/pull/34
@@ -140,6 +148,7 @@
   , ("d428", "a private hockey match") -- https://github.com/tfausak/rattletrap/commit/4c104b2
   , ("d44c", "ranked tournament") -- https://github.com/tfausak/rattletrap/pull/167
   , ("d52e", "psynet system id") -- https://github.com/tfausak/rattletrap/pull/99
+  , ("d5d6", "health max") -- https://github.com/nickbabcock/boxcars/pull/80
   , ("d7fb", "an explosion attribute") -- https://github.com/tfausak/rattletrap/commit/c554e3e
   , ("d818", "heatseeker") -- https://github.com/tfausak/rattletrap/pull/160
   , ("db70", "new lag indicator") -- https://github.com/tfausak/rattletrap/pull/69
@@ -147,6 +156,7 @@
   , ("dcb3", "a pawn type attribute") -- https://github.com/tfausak/rattletrap/commit/7d7f438
   , ("dd14", "v1.88") -- https://github.com/tfausak/rattletrap/pull/170
   , ("de56", "a problematic product attribute") -- https://github.com/tfausak/rattletrap/issues/51
+  , ("e2f9", "bTearOff") -- https://github.com/nickbabcock/boxcars/pull/76
   , ("e80d", "unlimited time") -- https://github.com/tfausak/rattletrap/pull/76
   , ("e978", "distracted") -- https://github.com/tfausak/rattletrap/issues/156
   , ("eae3", "an actor/object ID collision") -- https://github.com/tfausak/rattletrap/commit/d8fad06
