diff --git a/rattletrap.cabal b/rattletrap.cabal
--- a/rattletrap.cabal
+++ b/rattletrap.cabal
@@ -1,7 +1,7 @@
-cabal-version: 2.0
+cabal-version: 2.2
 
 name: rattletrap
-version: 11.2.1
+version: 11.2.3
 synopsis: Parse and generate Rocket League replays.
 description: Rattletrap parses and generates Rocket League replays.
 
@@ -16,21 +16,50 @@
   type: git
   location: https://github.com/tfausak/rattletrap
 
+flag pedantic
+  default: False
+  manual: True
+
+common library
+  build-depends: base ^>= 4.15.0
+  default-extensions: NamedFieldPuns
+  default-language: Haskell2010
+  ghc-options:
+    -Weverything
+    -Wno-all-missed-specialisations
+    -Wno-implicit-prelude
+    -Wno-missing-deriving-strategies
+    -Wno-missing-export-lists
+    -Wno-missing-exported-signatures
+    -Wno-missing-import-lists
+    -Wno-missing-safe-haskell-mode
+    -Wno-prepositive-qualified-module
+    -Wno-safe
+    -Wno-unsafe
+
+  if flag(pedantic)
+    ghc-options: -Werror
+
+common executable
+  import: library
+
+  build-depends: rattletrap
+  ghc-options: -rtsopts -threaded
+
 library
+  import: library
+
   autogen-modules: Paths_rattletrap
   build-depends:
-    base >= 4.15.0 && < 4.16
-    , aeson >= 1.5.6 && < 1.6
-    , aeson-pretty >= 0.8.8 && < 0.9
-    , array >= 0.5.4 && < 0.6
-    , bytestring >= 0.10.12 && < 0.12
-    , containers >= 0.6.4 && < 0.7
-    , filepath >= 1.4.2 && < 1.5
-    , http-client >= 0.7.8 && < 0.8
-    , http-client-tls >= 0.3.5 && < 0.4
-    , text >= 1.2.4 && < 1.3
-  default-extensions: NamedFieldPuns
-  default-language: Haskell2010
+    , aeson ^>= 1.5.6 || ^>= 2.0.0
+    , aeson-pretty ^>= 0.8.8
+    , array ^>= 0.5.4
+    , bytestring ^>= 0.10.12
+    , containers ^>= 0.6.4
+    , filepath ^>= 1.4.2
+    , http-client ^>= 0.7.8
+    , http-client-tls ^>= 0.3.5
+    , text ^>= 1.2.4
   exposed-modules:
     Paths_rattletrap
     Rattletrap
@@ -167,36 +196,18 @@
     Rattletrap.Utility.Json
     Rattletrap.Utility.Monad
     Rattletrap.Version
-  ghc-options:
-    -Weverything
-    -Wno-all-missed-specialisations
-    -Wno-implicit-prelude
-    -Wno-missing-deriving-strategies
-    -Wno-missing-export-lists
-    -Wno-missing-exported-signatures
-    -Wno-missing-import-lists
-    -Wno-missing-safe-haskell-mode
-    -Wno-prepositive-qualified-module
-    -Wno-safe
-    -Wno-unsafe
   hs-source-dirs: src/lib
 
 executable rattletrap
-  build-depends: base, rattletrap
-  default-language: Haskell2010
-  ghc-options: -rtsopts -threaded
+  import: executable
+
   hs-source-dirs: src/exe
   main-is: Main.hs
 
 test-suite test
-  build-depends:
-    base
-    , bytestring
-    , filepath
-    , HUnit >= 1.6.2 && < 1.7
-    , rattletrap
-  default-language: Haskell2010
-  ghc-options: -rtsopts -threaded
+  import: executable
+
+  build-depends: bytestring, filepath
   hs-source-dirs: src/test
   main-is: Main.hs
   type: exitcode-stdio-1.0
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
@@ -144,6 +144,7 @@
         [ "ERROR: " <> Exception.displayException e
         , "-- Context: " <> List.intercalate ", " ls
         , "-- You are using Rattletrap version " <> Version.string
+        , "-- " <> show config
         , "-- Please report this problem at https://github.com/tfausak/rattletrap/issues/new"
         ]
       Exit.exitFailure
diff --git a/src/lib/Rattletrap/Schema.hs b/src/lib/Rattletrap/Schema.hs
--- a/src/lib/Rattletrap/Schema.hs
+++ b/src/lib/Rattletrap/Schema.hs
@@ -15,10 +15,10 @@
 ref :: Schema -> Json.Value
 ref s = Json.object [Json.pair "$ref" $ Text.pack "#/definitions/" <> name s]
 
-object :: [((Text.Text, Json.Value), Bool)] -> Json.Value
+object :: [((String, Json.Value), Bool)] -> Json.Value
 object xs = Json.object
   [ Json.pair "type" "object"
-  , Json.pair "properties" . Json.object $ fmap fst xs
+  , Json.pair "properties" . Json.object $ fmap (uncurry Json.pair . fst) xs
   , Json.pair "required" . fmap (fst . fst) $ filter snd xs
   ]
 
diff --git a/src/lib/Rattletrap/Type/RemoteId/PsyNet.hs b/src/lib/Rattletrap/Type/RemoteId/PsyNet.hs
--- a/src/lib/Rattletrap/Type/RemoteId/PsyNet.hs
+++ b/src/lib/Rattletrap/Type/RemoteId/PsyNet.hs
@@ -1,5 +1,6 @@
 module Rattletrap.Type.RemoteId.PsyNet where
 
+import qualified Control.Applicative as Applicative
 import qualified Rattletrap.BitGet as BitGet
 import qualified Rattletrap.BitPut as BitPut
 import qualified Rattletrap.Schema as Schema
@@ -7,21 +8,24 @@
 import qualified Rattletrap.Type.Version as Version
 import qualified Rattletrap.Utility.Json as Json
 
-newtype PsyNet
-  = PsyNet (Either U64.U64 (U64.U64, U64.U64, U64.U64, U64.U64))
+data PsyNet
+  = New U64.U64
+  | Old U64.U64 U64.U64 U64.U64 U64.U64
   deriving (Eq, Show)
 
 instance Json.FromJSON PsyNet where
-  parseJSON = fmap fromEither . Json.parseJSON
+  parseJSON = Json.withObject "PsyNet" $ \object -> do
+    let
+      new = fmap New $ Json.required object "Left"
+      old = do
+        (a, b, c, d) <- Json.required object "Right"
+        pure $ Old a b c d
+    new Applicative.<|> old
 
 instance Json.ToJSON PsyNet where
-  toJSON = Json.toJSON . toEither
-
-fromEither :: Either U64.U64 (U64.U64, U64.U64, U64.U64, U64.U64) -> PsyNet
-fromEither = PsyNet
-
-toEither :: PsyNet -> Either U64.U64 (U64.U64, U64.U64, U64.U64, U64.U64)
-toEither (PsyNet x) = x
+  toJSON x = case x of
+    New a -> Json.object [Json.pair "Left" a]
+    Old a b c d -> Json.object [Json.pair "Right" (a, b, c, d)]
 
 schema :: Schema.Schema
 schema = Schema.named "remote-id-psy-net" $ Schema.oneOf
@@ -34,20 +38,16 @@
   ]
 
 bitPut :: PsyNet -> BitPut.BitPut
-bitPut x = case toEither x of
-  Left l -> U64.bitPut l
-  Right (a, b, c, d) ->
-    U64.bitPut a <> U64.bitPut b <> U64.bitPut c <> U64.bitPut d
+bitPut x = case x of
+  New l -> U64.bitPut l
+  Old a b c d -> U64.bitPut a <> U64.bitPut b <> U64.bitPut c <> U64.bitPut d
 
 bitGet :: Version.Version -> BitGet.BitGet PsyNet
-bitGet version =
-  BitGet.label "PsyNet"
-    . fmap fromEither
-    $ if Version.atLeast 868 24 10 version
-        then BitGet.label "new" $ fmap Left U64.bitGet
-        else BitGet.label "old" . fmap Right $ do
-          a <- U64.bitGet
-          b <- U64.bitGet
-          c <- U64.bitGet
-          d <- U64.bitGet
-          pure (a, b, c, d)
+bitGet version = BitGet.label "PsyNet" $ if Version.atLeast 868 24 10 version
+  then BitGet.label "New" $ fmap New U64.bitGet
+  else BitGet.label "Old" $ do
+    a <- U64.bitGet
+    b <- U64.bitGet
+    c <- U64.bitGet
+    d <- U64.bitGet
+    pure $ Old a b c d
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
@@ -1,3 +1,10 @@
+{- hlint ignore "Avoid restricted flags" -}
+{-# OPTIONS_GHC -Wno-orphans #-}
+
+{- hlint ignore "Avoid restricted extensions" -}
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE FlexibleInstances #-}
+
 module Rattletrap.Utility.Json
   ( module Rattletrap.Utility.Json
   , Aeson.FromJSON(parseJSON)
@@ -14,21 +21,45 @@
 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)
+
 required
   :: Aeson.FromJSON value => Aeson.Object -> String -> Aeson.Parser value
-required object key = object Aeson..: Text.pack key
+required object key = object Aeson..: toKey key
 
 optional
   :: Aeson.FromJSON value
   => Aeson.Object
   -> String
   -> Aeson.Parser (Maybe value)
-optional object key = object Aeson..:? Text.pack key
+optional object key = object Aeson..:? toKey key
 
 pair :: (Aeson.ToJSON value, Aeson.KeyValue pair) => String -> value -> pair
-pair key value = Text.pack key Aeson..= value
+pair key value = toKey key Aeson..= value
 
 decode :: Aeson.FromJSON a => ByteString.ByteString -> Either String a
 decode = Aeson.eitherDecodeStrict'
diff --git a/src/test/Main.hs b/src/test/Main.hs
--- a/src/test/Main.hs
+++ b/src/test/Main.hs
@@ -2,48 +2,44 @@
 import qualified Data.ByteString as ByteString
 import qualified GHC.Clock as Clock
 import qualified Rattletrap
-import qualified System.Exit as Exit
 import qualified System.FilePath as FilePath
-import qualified Test.HUnit as Test
 import qualified Text.Printf as Printf
 
 main :: IO ()
-main = runTests makeTests
-
-runTests :: Test.Test -> IO ()
-runTests test = do
-  Rattletrap.rattletrap
-    ""
-    ["--schema", "--output", FilePath.combine directory "schema.json"]
-  (result, elapsed) <- withElapsed $ Test.runTestTT test
-  Printf.printf "Total time: %.3f seconds\n" elapsed
-  Monad.when
-    (Test.errors result > 0 || Test.failures result > 0)
-    Exit.exitFailure
-
-makeTests :: Test.Test
-makeTests = Test.TestList $ fmap toTest replays
-
-toTest :: (String, String) -> Test.Test
-toTest (uuid, name) =
-  Test.TestLabel (toLabel uuid name) . Test.TestCase $ toAssertion uuid
+main = do
+  let directory = "output"
+  generateSchema directory
+  mapM_ (testReplay directory) replays
 
-toLabel :: String -> String -> String
-toLabel uuid name = uuid <> ": " <> name
+generateSchema :: FilePath -> IO ()
+generateSchema directory = Rattletrap.rattletrap
+  ""
+  ["--schema", "--output", FilePath.combine directory "schema.json"]
 
-toAssertion :: String -> Test.Assertion
-toAssertion uuid = do
-  let
-    inputFile = FilePath.combine "replays" $ uuid <> ".replay"
-    jsonFile = FilePath.combine directory $ uuid <> ".json"
-    outputFile = FilePath.combine directory $ uuid <> ".replay"
+testReplay :: FilePath -> (FilePath, String) -> IO ()
+testReplay directory (uuid, name) = do
+  let inputFile = FilePath.combine "replays" $ uuid <> ".replay"
   input <- ByteString.readFile inputFile
-  decode inputFile jsonFile
-  encode jsonFile outputFile
+  let mb = fromIntegral (ByteString.length input) / (1024 * 1024 :: Double)
+  Printf.printf "- replay: %s %s (%.3f mb)\n" uuid name mb
+  let jsonFile = FilePath.combine directory $ uuid <> ".json"
+  do
+    (s, ()) <- withDuration $ decode inputFile jsonFile
+    Printf.printf "  decode: %.3f s @ %.3f mb/s\n" s (mb / s)
+  let outputFile = FilePath.combine directory $ uuid <> ".replay"
+  do
+    (s, ()) <- withDuration $ encode jsonFile outputFile
+    Printf.printf "  encode: %.3f s @ %.3f mb/s\n" s (mb / s)
   output <- ByteString.readFile outputFile
-  Monad.when (output /= input)
-    $ Test.assertFailure "output does not match input"
+  Monad.when (output /= input) $ fail "output does not match input"
 
+withDuration :: IO a -> IO (Double, a)
+withDuration action = do
+  before <- Clock.getMonotonicTime
+  result <- action
+  after <- Clock.getMonotonicTime
+  pure (after - before, result)
+
 decode :: FilePath -> FilePath -> IO ()
 decode input output =
   Rattletrap.rattletrap "" ["--compact", "--input", input, "--output", output]
@@ -52,121 +48,111 @@
 encode input output =
   Rattletrap.rattletrap "" ["--input", input, "--output", output]
 
-withElapsed :: IO a -> IO (a, Double)
-withElapsed action = do
-  before <- Clock.getMonotonicTime
-  result <- action
-  after <- Clock.getMonotonicTime
-  pure (result, after - before)
-
-directory :: FilePath
-directory = "output"
-
 replays :: [(String, String)]
 replays =
-  [ ("0008", "a flip time")
-  , ("000b", "nintendo switch")
-  , ("0416", "v1.78 demolition")
-  , ("07e9", "a game mode before Neo Tokyo")
-  , ("0ad2", "some Latin-1 text")
-  , ("0e76", "v1.95 rumble")
-  , ("1205", "rumble mode")
-  , ("160c", "a dedicated server IP")
-  , ("16d5", "new property types")
-  , ("18d6", "an online loadout attribute")
-  , ("1a12", "overtime")
-  , ("1ae4", "a game time")
-  , ("1bc2", "no padding after the frames")
-  , ("1d1d", "a camera pitch")
-  , ("1ec9", "a V1.63 match")
-  , ("1ef9", "a private hoops match")
-  , ("1f37", "splitscreen players")
-  , ("2114", "a match save")
-  , ("21a8", "v1.66")
-  , ("2266", "dropshot")
-  , ("22ba", "a vote to forfeit")
-  , ("27b6", "some UTF-16 text")
-  , ("29f5", "frames")
-  , ("2cf8", "demo v2.01")
-  , ("2cfe", "a new playstation id")
-  , ("3381", "patch 1.37")
-  , ("35f6", "heatseeker v2.01")
-  , ("372d", "a camera yaw attribute")
-  , ("383e", "older unknown content field")
-  , ("387f", "a frozen attribute")
-  , ("3abd", "rlcs")
-  , ("3ea1", "a custom team name")
-  , ("4126", "a game mode after Neo Tokyo")
-  , ("419a", "a club match")
-  , ("42f0", "reservations after Neo Tokyo")
-  , ("42f2", "anniversary ball")
-  , ("4bc3", "with timed out attribute")
-  , ("504e", "some messages")
-  , ("520e", "no pickup attribute")
-  , ("524f", "quat edge case")
-  , ("52aa", "a match-ending attribute")
-  , ("540d", "a demolish attribute")
-  , ("551c", "private match settings")
-  , ("5a06", "esports items")
-  , ("6210", "different player history key")
-  , ("6320", "a forfeit attribute")
-  , ("6688", "a malformed byte property")
-  , ("6b0d", "patch 1.37")
-  , ("6d1b", "a flip right")
-  , ("6f7c", "a map with numbers")
-  , ("7083", "weird basketball capitalization")
-  , ("7109", "a boost modifier")
-  , ("7256", "special edition")
-  , ("75ce", "primary and secondary titles")
-  , ("7bf6", "an online loadouts attribute")
-  , ("81d1", "gridiron")
-  , ("89cb", "remote user data")
-  , ("8ae5", "new painted items")
-  , ("92a6", "with server performance state")
-  , ("946f", "patch 1.43")
-  , ("9704", "a batarang")
-  , ("98e5", "a player using behind view")
-  , ("9a2c", "ghost hunt")
-  , ("9e35", "spike rush")
-  , ("9eaa", "newer replay without trailing bytes")
-  , ("a09e", "a tournament")
-  , ("a128", "a round count down")
-  , ("a184", "max score")
-  , ("a1c0", "epic system id")
-  , ("a52f", "some more mutators")
-  , ("a558", "extended explosion data")
-  , ("a671", "a waiting player")
-  , ("a676", "new user color")
-  , ("a7f0", "a ready attribute")
-  , ("a9df", "salty shores patch 1.45")
-  , ("aa70", "patch 1.50 - TitleID attribute")
-  , ("afb1", "patch 1.37")
-  , ("b9f9", "a party leader")
-  , ("c14f", "some mutators")
-  , ("c23b", "new psynet id")
-  , ("c837", "a spectator")
-  , ("cc4c", "after Starbase ARC")
-  , ("d044", "hoops mutators")
-  , ("d1d5", "v1.68")
-  , ("d236", "rlcs s2")
-  , ("d428", "a private hockey match")
-  , ("d44c", "ranked tournament")
-  , ("d52e", "psynet system id")
-  , ("d7fb", "an explosion attribute")
-  , ("d818", "heatseeker")
-  , ("db70", "new lag indicator")
-  , ("dcab", "weird ball attribute value")
-  , ("dcb3", "a pawn type attribute")
-  , ("dd14", "v1.88")
-  , ("de56", "a problematic product attribute")
-  , ("e80d", "unlimited time")
-  , ("e978", "distracted")
-  , ("eae3", "an actor/object ID collision")
-  , ("eae8", "custom team colors")
-  , ("edbb", "remote role")
-  , ("f299", "a location attribute")
-  , ("f7b9", "a hockey game event")
-  , ("f811", "no frames")
-  , ("fdc7", "an MVP")
-  , ("ffb7", "with difficulty")
+  [ ("0008", "a flip time") -- https://github.com/tfausak/rattletrap/commit/ee7afa0
+  , ("000b", "nintendo switch") -- https://github.com/tfausak/rattletrap/pull/60
+  , ("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
+  , ("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
+  , ("16d5", "new property types") -- https://github.com/tfausak/rattletrap/pull/41
+  , ("18d6", "an online loadout attribute") -- https://github.com/tfausak/rattletrap/commit/a9900e7
+  , ("1a12", "overtime") -- https://github.com/tfausak/rattletrap/commit/ada6053
+  , ("1ae4", "a game time") -- https://github.com/tfausak/rattletrap/commit/d08176e
+  , ("1bc2", "no padding after the frames") -- https://github.com/tfausak/rattletrap/commit/c9a2dd8
+  , ("1d1d", "a camera pitch") -- https://github.com/tfausak/rattletrap/commit/ee7afa0
+  , ("1ec9", "a V1.63 match") -- https://github.com/tfausak/rattletrap/pull/132
+  , ("1ef9", "a private hoops match") -- https://github.com/tfausak/rattletrap/commit/5570839
+  , ("1f37", "splitscreen players") -- https://github.com/tfausak/rattletrap/commit/c4d2f32
+  , ("2114", "a match save") -- https://github.com/tfausak/rattletrap/commit/ee7afa0
+  , ("21a8", "v1.66") -- https://github.com/tfausak/rattletrap/pull/142
+  , ("2266", "dropshot") -- https://github.com/tfausak/rattletrap/pull/36
+  , ("22ba", "a vote to forfeit") -- https://github.com/tfausak/rattletrap/commit/86656bb
+  , ("27b6", "some UTF-16 text") -- https://github.com/tfausak/rattletrap/commit/c4d2f32
+  , ("29f5", "frames") -- https://github.com/tfausak/rattletrap/commit/bf4b6af
+  , ("2cf8", "demo v2.01") -- https://github.com/tfausak/rattletrap/pull/243
+  , ("2cfe", "a new playstation id") -- https://github.com/tfausak/rattletrap/issues/51
+  , ("3381", "patch 1.37") -- https://github.com/tfausak/rattletrap/pull/48
+  , ("35f6", "heatseeker v2.01") -- https://github.com/tfausak/rattletrap/commit/57d86c2
+  , ("372d", "a camera yaw attribute") -- https://github.com/tfausak/rattletrap/commit/9c1516c
+  , ("383e", "older unknown content field") -- https://github.com/tfausak/rattletrap/pull/123
+  , ("387f", "a frozen attribute") -- https://github.com/tfausak/rattletrap/commit/93ce196
+  , ("3abd", "rlcs") -- https://github.com/tfausak/rattletrap/pull/86
+  , ("3ea1", "a custom team name") -- https://github.com/tfausak/rattletrap/commit/cf4d145
+  , ("4126", "a game mode after Neo Tokyo") -- https://github.com/tfausak/rattletrap/commit/a1cf21e
+  , ("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
+  , ("4bc3", "with timed out attribute") -- https://github.com/tfausak/rattletrap/pull/98
+  , ("504e", "some messages") -- https://github.com/tfausak/rattletrap/commit/1d4a538
+  , ("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
+  , ("551c", "private match settings") -- https://github.com/tfausak/rattletrap/commit/5c9ebfc
+  , ("5a06", "esports items") -- https://github.com/tfausak/rattletrap/pull/114
+  , ("6210", "different player history key") -- https://github.com/tfausak/rattletrap/pull/63
+  , ("6320", "a forfeit attribute") -- https://github.com/tfausak/rattletrap/pull/20
+  , ("6688", "a malformed byte property") -- https://github.com/tfausak/rattletrap/commit/b1ec18b
+  , ("6b0d", "patch 1.37") -- https://github.com/tfausak/rattletrap/pull/48
+  , ("6d1b", "a flip right") -- https://github.com/tfausak/rattletrap/commit/ee7afa0
+  , ("6f7c", "a map with numbers") -- https://github.com/tfausak/rattletrap/commit/2629511
+  , ("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
+  , ("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
+  , ("89cb", "remote user data") -- https://github.com/tfausak/rattletrap/commit/163684f
+  , ("8ae5", "new painted items") -- https://github.com/tfausak/rattletrap/pull/43
+  , ("92a6", "with server performance state") -- https://github.com/tfausak/rattletrap/pull/93
+  , ("946f", "patch 1.43") -- https://github.com/tfausak/rattletrap/pull/69
+  , ("9704", "a batarang") -- https://github.com/tfausak/rattletrap/commit/5958e5c
+  , ("98e5", "a player using behind view") -- https://github.com/tfausak/rattletrap/commit/163684f
+  , ("9a2c", "ghost hunt") -- https://github.com/tfausak/rattletrap/pull/160
+  , ("9e35", "spike rush") -- https://github.com/tfausak/rattletrap/pull/160
+  , ("9eaa", "newer replay without trailing bytes") -- https://github.com/tfausak/rattletrap/pull/126
+  , ("a09e", "a tournament") -- https://github.com/tfausak/rattletrap/pull/69
+  , ("a128", "a round count down") -- https://github.com/tfausak/rattletrap/commit/8bb778d
+  , ("a184", "max score") -- https://github.com/tfausak/rattletrap/pull/158
+  , ("a1c0", "epic system id") -- https://github.com/tfausak/rattletrap/pull/167
+  , ("a52f", "some more mutators") -- https://github.com/tfausak/rattletrap/commit/ee7afa0
+  , ("a558", "extended explosion data") -- https://github.com/tfausak/rattletrap/pull/44
+  , ("a671", "a waiting player") -- https://github.com/tfausak/rattletrap/commit/163684f
+  , ("a676", "new user color") -- https://github.com/tfausak/rattletrap/pull/93
+  , ("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
+  , ("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
+  , ("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
+  , ("d1d5", "v1.68") -- https://github.com/tfausak/rattletrap/pull/146
+  , ("d236", "rlcs s2") -- https://github.com/tfausak/rattletrap/pull/88
+  , ("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
+  , ("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
+  , ("dcab", "weird ball attribute value") -- https://github.com/tfausak/rattletrap/issues/149
+  , ("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
+  , ("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
+  , ("eae8", "custom team colors") -- https://github.com/tfausak/rattletrap/commit/809240f
+  , ("edbb", "remote role") -- https://github.com/tfausak/rattletrap/pull/106
+  , ("f299", "a location attribute") -- https://github.com/tfausak/rattletrap/commit/21b09c5
+  , ("f7b9", "a hockey game event") -- https://github.com/tfausak/rattletrap/commit/3e16d7f
+  , ("f811", "no frames") -- https://github.com/tfausak/rattletrap/commit/bf4b6af
+  , ("fdc7", "an MVP") -- https://github.com/tfausak/rattletrap/commit/65019d2
+  , ("ffb7", "with difficulty") -- https://github.com/tfausak/rattletrap/pull/167
   ]
