diff --git a/CHANGELOG.markdown b/CHANGELOG.markdown
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.markdown
@@ -0,0 +1,7 @@
+# Change log
+
+Rattletrap uses [Semantic Versioning][].
+The change log is available through the [releases on GitHub][].
+
+[Semantic Versioning]: http://semver.org/spec/v2.0.0.html
+[releases on GitHub]: https://github.com/tfausak/rattletrap/releases
diff --git a/README.markdown b/README.markdown
--- a/README.markdown
+++ b/README.markdown
@@ -65,7 +65,7 @@
 
 [Rattletrap]: https://github.com/tfausak/rattletrap
 [Version badge]: https://www.stackage.org/package/rattletrap/badge/nightly?label=version
-[version]: https://www.stackage.org/package/rattletrap
+[version]: https://www.stackage.org/nightly/package/rattletrap
 [Windows build badge]: https://ci.appveyor.com/api/projects/status/github/tfausak/rattletrap?branch=master&svg=true
 [windows build]: https://ci.appveyor.com/project/TaylorFausak/rattletrap
 [Build badge]: https://travis-ci.org/tfausak/rattletrap.svg?branch=master
diff --git a/data/replays/00080014003600090000036E0F65CCEB.replay b/data/replays/00080014003600090000036E0F65CCEB.replay
new file mode 100644
Binary files /dev/null and b/data/replays/00080014003600090000036E0F65CCEB.replay differ
diff --git a/data/replays/07E925B1423653D44CB8B4B2524792C1.replay b/data/replays/07E925B1423653D44CB8B4B2524792C1.replay
new file mode 100644
Binary files /dev/null and b/data/replays/07E925B1423653D44CB8B4B2524792C1.replay differ
diff --git a/data/replays/0AD21FEE4A512906BEB6C98136AAF49A.replay b/data/replays/0AD21FEE4A512906BEB6C98136AAF49A.replay
new file mode 100644
Binary files /dev/null and b/data/replays/0AD21FEE4A512906BEB6C98136AAF49A.replay differ
diff --git a/data/replays/1205D96C4D819800927791820096CD49.replay b/data/replays/1205D96C4D819800927791820096CD49.replay
new file mode 100644
Binary files /dev/null and b/data/replays/1205D96C4D819800927791820096CD49.replay differ
diff --git a/data/replays/160CA83E41083BFD8E6315B4BFCA0561.replay b/data/replays/160CA83E41083BFD8E6315B4BFCA0561.replay
new file mode 100644
Binary files /dev/null and b/data/replays/160CA83E41083BFD8E6315B4BFCA0561.replay differ
diff --git a/data/replays/18D6738D415B70B5BE4C299588D3C141.replay b/data/replays/18D6738D415B70B5BE4C299588D3C141.replay
new file mode 100644
Binary files /dev/null and b/data/replays/18D6738D415B70B5BE4C299588D3C141.replay differ
diff --git a/data/replays/1A126AC24CAA0DB0E98835BD960B8AF8.replay b/data/replays/1A126AC24CAA0DB0E98835BD960B8AF8.replay
new file mode 100644
Binary files /dev/null and b/data/replays/1A126AC24CAA0DB0E98835BD960B8AF8.replay differ
diff --git a/data/replays/1AE415514DFC65DCBF8B8391AD35488D.replay b/data/replays/1AE415514DFC65DCBF8B8391AD35488D.replay
new file mode 100644
Binary files /dev/null and b/data/replays/1AE415514DFC65DCBF8B8391AD35488D.replay differ
diff --git a/data/replays/1BC2D01444ACE577D01E988EADD4DFD0.replay b/data/replays/1BC2D01444ACE577D01E988EADD4DFD0.replay
new file mode 100644
Binary files /dev/null and b/data/replays/1BC2D01444ACE577D01E988EADD4DFD0.replay differ
diff --git a/data/replays/1D1DE97D4941C86E43FE0093563DB621.replay b/data/replays/1D1DE97D4941C86E43FE0093563DB621.replay
new file mode 100644
Binary files /dev/null and b/data/replays/1D1DE97D4941C86E43FE0093563DB621.replay differ
diff --git a/data/replays/1EF90FCC4F719F606A5327B3CDD782A4.replay b/data/replays/1EF90FCC4F719F606A5327B3CDD782A4.replay
new file mode 100644
Binary files /dev/null and b/data/replays/1EF90FCC4F719F606A5327B3CDD782A4.replay differ
diff --git a/data/replays/1F3798E540B0C37A946561ABBB3037F9.replay b/data/replays/1F3798E540B0C37A946561ABBB3037F9.replay
new file mode 100644
Binary files /dev/null and b/data/replays/1F3798E540B0C37A946561ABBB3037F9.replay differ
diff --git a/data/replays/211466D04B983F5A33CC2FA1D5928672.replay b/data/replays/211466D04B983F5A33CC2FA1D5928672.replay
new file mode 100644
Binary files /dev/null and b/data/replays/211466D04B983F5A33CC2FA1D5928672.replay differ
diff --git a/data/replays/22BACD794ABE7B92E50E9CBDBD9C59CE.replay b/data/replays/22BACD794ABE7B92E50E9CBDBD9C59CE.replay
new file mode 100644
Binary files /dev/null and b/data/replays/22BACD794ABE7B92E50E9CBDBD9C59CE.replay differ
diff --git a/data/replays/27B6A7B64553F0F685874584F96BAB1B.replay b/data/replays/27B6A7B64553F0F685874584F96BAB1B.replay
new file mode 100644
Binary files /dev/null and b/data/replays/27B6A7B64553F0F685874584F96BAB1B.replay differ
diff --git a/data/replays/29F582C34A65EB34D358A784CBE3C189.replay b/data/replays/29F582C34A65EB34D358A784CBE3C189.replay
new file mode 100644
Binary files /dev/null and b/data/replays/29F582C34A65EB34D358A784CBE3C189.replay differ
diff --git a/data/replays/372DBFCA4BDB340E4357B6BD43032802.replay b/data/replays/372DBFCA4BDB340E4357B6BD43032802.replay
new file mode 100644
Binary files /dev/null and b/data/replays/372DBFCA4BDB340E4357B6BD43032802.replay differ
diff --git a/data/replays/387F059C47C09E253C875CA990EFD9F2.replay b/data/replays/387F059C47C09E253C875CA990EFD9F2.replay
new file mode 100644
Binary files /dev/null and b/data/replays/387F059C47C09E253C875CA990EFD9F2.replay differ
diff --git a/data/replays/3EA147DD485B8DD24810689A7A989E44.replay b/data/replays/3EA147DD485B8DD24810689A7A989E44.replay
new file mode 100644
Binary files /dev/null and b/data/replays/3EA147DD485B8DD24810689A7A989E44.replay differ
diff --git a/data/replays/4126861E477F4A03DE2A4080374D7908.replay b/data/replays/4126861E477F4A03DE2A4080374D7908.replay
new file mode 100644
Binary files /dev/null and b/data/replays/4126861E477F4A03DE2A4080374D7908.replay differ
diff --git a/data/replays/42F0D8DA4FC89AE7B80FCAB7F637A8EA.replay b/data/replays/42F0D8DA4FC89AE7B80FCAB7F637A8EA.replay
new file mode 100644
Binary files /dev/null and b/data/replays/42F0D8DA4FC89AE7B80FCAB7F637A8EA.replay differ
diff --git a/data/replays/504ED825482186E771FAA9B642CE5CE4.replay b/data/replays/504ED825482186E771FAA9B642CE5CE4.replay
new file mode 100644
Binary files /dev/null and b/data/replays/504ED825482186E771FAA9B642CE5CE4.replay differ
diff --git a/data/replays/52AA67F94090C19D33C5009E54D31FE4.replay b/data/replays/52AA67F94090C19D33C5009E54D31FE4.replay
new file mode 100644
Binary files /dev/null and b/data/replays/52AA67F94090C19D33C5009E54D31FE4.replay differ
diff --git a/data/replays/540DA764423C8FB24EB9D486D982F16F.replay b/data/replays/540DA764423C8FB24EB9D486D982F16F.replay
new file mode 100644
Binary files /dev/null and b/data/replays/540DA764423C8FB24EB9D486D982F16F.replay differ
diff --git a/data/replays/551CA4D44FF2B86015DE44A6B5790D4C.replay b/data/replays/551CA4D44FF2B86015DE44A6B5790D4C.replay
new file mode 100644
Binary files /dev/null and b/data/replays/551CA4D44FF2B86015DE44A6B5790D4C.replay differ
diff --git a/data/replays/6320E51C49066A7C210A2993C2201D5F.replay b/data/replays/6320E51C49066A7C210A2993C2201D5F.replay
new file mode 100644
Binary files /dev/null and b/data/replays/6320E51C49066A7C210A2993C2201D5F.replay differ
diff --git a/data/replays/6688EEE34BFEB3EC3A9E3283098CC712.replay b/data/replays/6688EEE34BFEB3EC3A9E3283098CC712.replay
new file mode 100644
Binary files /dev/null and b/data/replays/6688EEE34BFEB3EC3A9E3283098CC712.replay differ
diff --git a/data/replays/6D1B06D844A5BB91B81FD4B5B28F08BA.replay b/data/replays/6D1B06D844A5BB91B81FD4B5B28F08BA.replay
new file mode 100644
Binary files /dev/null and b/data/replays/6D1B06D844A5BB91B81FD4B5B28F08BA.replay differ
diff --git a/data/replays/6F7CFCD24638F8A6567AB3A8B9958A90.replay b/data/replays/6F7CFCD24638F8A6567AB3A8B9958A90.replay
new file mode 100644
Binary files /dev/null and b/data/replays/6F7CFCD24638F8A6567AB3A8B9958A90.replay differ
diff --git a/data/replays/7109EB9846D303E54B7ACBA792036213.replay b/data/replays/7109EB9846D303E54B7ACBA792036213.replay
new file mode 100644
Binary files /dev/null and b/data/replays/7109EB9846D303E54B7ACBA792036213.replay differ
diff --git a/data/replays/7BF6073F4614CE0A438994B9A260DA6A.replay b/data/replays/7BF6073F4614CE0A438994B9A260DA6A.replay
new file mode 100644
Binary files /dev/null and b/data/replays/7BF6073F4614CE0A438994B9A260DA6A.replay differ
diff --git a/data/replays/89CBA30E46FA5385BDD35DA4285D4D2E.replay b/data/replays/89CBA30E46FA5385BDD35DA4285D4D2E.replay
new file mode 100644
Binary files /dev/null and b/data/replays/89CBA30E46FA5385BDD35DA4285D4D2E.replay differ
diff --git a/data/replays/9704208245D7DD851F2FB2BC7DFD9AC3.replay b/data/replays/9704208245D7DD851F2FB2BC7DFD9AC3.replay
new file mode 100644
Binary files /dev/null and b/data/replays/9704208245D7DD851F2FB2BC7DFD9AC3.replay differ
diff --git a/data/replays/98E58A904D713F2DE202358E8573265D.replay b/data/replays/98E58A904D713F2DE202358E8573265D.replay
new file mode 100644
Binary files /dev/null and b/data/replays/98E58A904D713F2DE202358E8573265D.replay differ
diff --git a/data/replays/A128B3AB45D5A18E3EF9CF93C9576BCE.replay b/data/replays/A128B3AB45D5A18E3EF9CF93C9576BCE.replay
new file mode 100644
Binary files /dev/null and b/data/replays/A128B3AB45D5A18E3EF9CF93C9576BCE.replay differ
diff --git a/data/replays/A52F804845573D8DA65E97BF59026A43.replay b/data/replays/A52F804845573D8DA65E97BF59026A43.replay
new file mode 100644
Binary files /dev/null and b/data/replays/A52F804845573D8DA65E97BF59026A43.replay differ
diff --git a/data/replays/A6711CE74272B2E663DCC9A200A218E3.replay b/data/replays/A6711CE74272B2E663DCC9A200A218E3.replay
new file mode 100644
Binary files /dev/null and b/data/replays/A6711CE74272B2E663DCC9A200A218E3.replay differ
diff --git a/data/replays/A7F001A1417A19BFA8C90990D8F7C2FF.replay b/data/replays/A7F001A1417A19BFA8C90990D8F7C2FF.replay
new file mode 100644
Binary files /dev/null and b/data/replays/A7F001A1417A19BFA8C90990D8F7C2FF.replay differ
diff --git a/data/replays/B9F9B87D4A9D0A3D25D4EC91C0401DE2.replay b/data/replays/B9F9B87D4A9D0A3D25D4EC91C0401DE2.replay
new file mode 100644
Binary files /dev/null and b/data/replays/B9F9B87D4A9D0A3D25D4EC91C0401DE2.replay differ
diff --git a/data/replays/C14F7E0E4D9B5E6BE9AD5D8ED56B174C.replay b/data/replays/C14F7E0E4D9B5E6BE9AD5D8ED56B174C.replay
new file mode 100644
Binary files /dev/null and b/data/replays/C14F7E0E4D9B5E6BE9AD5D8ED56B174C.replay differ
diff --git a/data/replays/C8372B1345B1803DEF039F815DBD802D.replay b/data/replays/C8372B1345B1803DEF039F815DBD802D.replay
new file mode 100644
Binary files /dev/null and b/data/replays/C8372B1345B1803DEF039F815DBD802D.replay differ
diff --git a/data/replays/CC4CA70D4F7A67EBAD0ED9B9923106F7.replay b/data/replays/CC4CA70D4F7A67EBAD0ED9B9923106F7.replay
new file mode 100644
Binary files /dev/null and b/data/replays/CC4CA70D4F7A67EBAD0ED9B9923106F7.replay differ
diff --git a/data/replays/D428F81646A98C25902CE988AE5C14C8.replay b/data/replays/D428F81646A98C25902CE988AE5C14C8.replay
new file mode 100644
Binary files /dev/null and b/data/replays/D428F81646A98C25902CE988AE5C14C8.replay differ
diff --git a/data/replays/D7FB197A451D69075A0C99A2F49A4053.replay b/data/replays/D7FB197A451D69075A0C99A2F49A4053.replay
new file mode 100644
Binary files /dev/null and b/data/replays/D7FB197A451D69075A0C99A2F49A4053.replay differ
diff --git a/data/replays/DCB3A6B94A9DBE46FDE5EAA9B012F6C8.replay b/data/replays/DCB3A6B94A9DBE46FDE5EAA9B012F6C8.replay
new file mode 100644
Binary files /dev/null and b/data/replays/DCB3A6B94A9DBE46FDE5EAA9B012F6C8.replay differ
diff --git a/data/replays/EAE311E84BA35B590A6FDBA6DD4F2FEB.replay b/data/replays/EAE311E84BA35B590A6FDBA6DD4F2FEB.replay
new file mode 100644
Binary files /dev/null and b/data/replays/EAE311E84BA35B590A6FDBA6DD4F2FEB.replay differ
diff --git a/data/replays/EAE8DADA4BB2DC5422792C9B4A67392D.replay b/data/replays/EAE8DADA4BB2DC5422792C9B4A67392D.replay
new file mode 100644
Binary files /dev/null and b/data/replays/EAE8DADA4BB2DC5422792C9B4A67392D.replay differ
diff --git a/data/replays/F299F176491554B11E34AB91CA76B2CE.replay b/data/replays/F299F176491554B11E34AB91CA76B2CE.replay
new file mode 100644
Binary files /dev/null and b/data/replays/F299F176491554B11E34AB91CA76B2CE.replay differ
diff --git a/data/replays/F7B9E14545C7467B89A00895980FCD73.replay b/data/replays/F7B9E14545C7467B89A00895980FCD73.replay
new file mode 100644
Binary files /dev/null and b/data/replays/F7B9E14545C7467B89A00895980FCD73.replay differ
diff --git a/data/replays/F811C1D24888015E23B598AD8628C742.replay b/data/replays/F811C1D24888015E23B598AD8628C742.replay
new file mode 100644
Binary files /dev/null and b/data/replays/F811C1D24888015E23B598AD8628C742.replay differ
diff --git a/data/replays/FDC79DA84DD463D4BCCE6B892829AC88.replay b/data/replays/FDC79DA84DD463D4BCCE6B892829AC88.replay
new file mode 100644
Binary files /dev/null and b/data/replays/FDC79DA84DD463D4BCCE6B892829AC88.replay differ
diff --git a/package.yaml b/package.yaml
new file mode 100644
--- /dev/null
+++ b/package.yaml
@@ -0,0 +1,61 @@
+category: Game
+description: Rattletrap parses and generates Rocket League replays.
+executables:
+  rattletrap:
+    dependencies:
+    - base
+    - rattletrap
+    ghc-options:
+    - -rtsopts
+    - -threaded
+    - -with-rtsopts=-N
+    main: Main.hs
+    source-dirs: source/executables
+extra-source-files:
+- CHANGELOG.markdown
+- data/replays/*.replay
+- package.yaml
+- README.markdown
+- stack.yaml
+ghc-options: -Wall
+github: tfausak/rattletrap
+library:
+  default-extensions:
+  - Strict
+  dependencies:
+  - aeson >=0.11 && <1.1
+  - aeson-casing >=0.1 && <0.2
+  - base >=4.9 && <4.10
+  - bimap >=0.3 && <0.4
+  - binary >=0.8 && <0.9
+  - binary-bits >=0.5 && <0.6
+  - bytestring >=0.10 && <0.11
+  - containers >=0.5 && <0.6
+  - data-binary-ieee754 >=0.4 && <0.5
+  - template-haskell >=2.11 && <2.12
+  - text >=1.2 && <1.3
+  - vector >=0.11 && <0.12
+  other-modules: Paths_rattletrap
+  source-dirs: source/library
+license: MIT
+license-file: LICENSE.markdown
+maintainer: Taylor Fausak
+name: rattletrap
+synopsis: Parse and generate Rocket League replays.
+tests:
+  test:
+    dependencies:
+    - base
+    - bytestring
+    - filepath >=1.4 && <1.5
+    - rattletrap
+    - tasty >=0.11 && <0.12
+    - tasty-hspec >=1.1 && <1.2
+    - temporary >=1.2 && <1.3
+    ghc-options:
+    - -rtsopts
+    - -threaded
+    - -with-rtsopts=-N
+    main: Main.hs
+    source-dirs: source/tests
+version: '2.1.0'
diff --git a/rattletrap.cabal b/rattletrap.cabal
--- a/rattletrap.cabal
+++ b/rattletrap.cabal
@@ -1,198 +1,200 @@
--- This file has been generated from package.yaml by hpack version 0.14.0.
+-- This file has been generated from package.yaml by hpack version 0.15.0.
 --
 -- see: https://github.com/sol/hpack
 
 name:           rattletrap
-version:        2.0.0
-cabal-version:  >= 1.10
-build-type:     Simple
-license:        MIT
-license-file:   LICENSE.markdown
-maintainer:     Taylor Fausak
-homepage:       https://github.com/tfausak/rattletrap#readme
-bug-reports:    https://github.com/tfausak/rattletrap/issues
+version:        2.1.0
 synopsis:       Parse and generate Rocket League replays.
 description:    Rattletrap parses and generates Rocket League replays.
 category:       Game
+homepage:       https://github.com/tfausak/rattletrap#readme
+bug-reports:    https://github.com/tfausak/rattletrap/issues
+maintainer:     Taylor Fausak
+license:        MIT
+license-file:   LICENSE.markdown
+build-type:     Simple
+cabal-version:  >= 1.10
 
 extra-source-files:
+    CHANGELOG.markdown
+    data/replays/00080014003600090000036E0F65CCEB.replay
+    data/replays/07E925B1423653D44CB8B4B2524792C1.replay
+    data/replays/0AD21FEE4A512906BEB6C98136AAF49A.replay
+    data/replays/1205D96C4D819800927791820096CD49.replay
+    data/replays/160CA83E41083BFD8E6315B4BFCA0561.replay
+    data/replays/18D6738D415B70B5BE4C299588D3C141.replay
+    data/replays/1A126AC24CAA0DB0E98835BD960B8AF8.replay
+    data/replays/1AE415514DFC65DCBF8B8391AD35488D.replay
+    data/replays/1BC2D01444ACE577D01E988EADD4DFD0.replay
+    data/replays/1D1DE97D4941C86E43FE0093563DB621.replay
+    data/replays/1EF90FCC4F719F606A5327B3CDD782A4.replay
+    data/replays/1F3798E540B0C37A946561ABBB3037F9.replay
+    data/replays/211466D04B983F5A33CC2FA1D5928672.replay
+    data/replays/22BACD794ABE7B92E50E9CBDBD9C59CE.replay
+    data/replays/27B6A7B64553F0F685874584F96BAB1B.replay
+    data/replays/29F582C34A65EB34D358A784CBE3C189.replay
+    data/replays/372DBFCA4BDB340E4357B6BD43032802.replay
+    data/replays/387F059C47C09E253C875CA990EFD9F2.replay
+    data/replays/3EA147DD485B8DD24810689A7A989E44.replay
+    data/replays/4126861E477F4A03DE2A4080374D7908.replay
+    data/replays/42F0D8DA4FC89AE7B80FCAB7F637A8EA.replay
+    data/replays/504ED825482186E771FAA9B642CE5CE4.replay
+    data/replays/52AA67F94090C19D33C5009E54D31FE4.replay
+    data/replays/540DA764423C8FB24EB9D486D982F16F.replay
+    data/replays/551CA4D44FF2B86015DE44A6B5790D4C.replay
+    data/replays/6320E51C49066A7C210A2993C2201D5F.replay
+    data/replays/6688EEE34BFEB3EC3A9E3283098CC712.replay
+    data/replays/6D1B06D844A5BB91B81FD4B5B28F08BA.replay
+    data/replays/6F7CFCD24638F8A6567AB3A8B9958A90.replay
+    data/replays/7109EB9846D303E54B7ACBA792036213.replay
+    data/replays/7BF6073F4614CE0A438994B9A260DA6A.replay
+    data/replays/89CBA30E46FA5385BDD35DA4285D4D2E.replay
+    data/replays/9704208245D7DD851F2FB2BC7DFD9AC3.replay
+    data/replays/98E58A904D713F2DE202358E8573265D.replay
+    data/replays/A128B3AB45D5A18E3EF9CF93C9576BCE.replay
+    data/replays/A52F804845573D8DA65E97BF59026A43.replay
+    data/replays/A6711CE74272B2E663DCC9A200A218E3.replay
+    data/replays/A7F001A1417A19BFA8C90990D8F7C2FF.replay
+    data/replays/B9F9B87D4A9D0A3D25D4EC91C0401DE2.replay
+    data/replays/C14F7E0E4D9B5E6BE9AD5D8ED56B174C.replay
+    data/replays/C8372B1345B1803DEF039F815DBD802D.replay
+    data/replays/CC4CA70D4F7A67EBAD0ED9B9923106F7.replay
+    data/replays/D428F81646A98C25902CE988AE5C14C8.replay
+    data/replays/D7FB197A451D69075A0C99A2F49A4053.replay
+    data/replays/DCB3A6B94A9DBE46FDE5EAA9B012F6C8.replay
+    data/replays/EAE311E84BA35B590A6FDBA6DD4F2FEB.replay
+    data/replays/EAE8DADA4BB2DC5422792C9B4A67392D.replay
+    data/replays/F299F176491554B11E34AB91CA76B2CE.replay
+    data/replays/F7B9E14545C7467B89A00895980FCD73.replay
+    data/replays/F811C1D24888015E23B598AD8628C742.replay
+    data/replays/FDC79DA84DD463D4BCCE6B892829AC88.replay
+    package.yaml
     README.markdown
-    replays/00080014003600090000036E0F65CCEB.replay
-    replays/07E925B1423653D44CB8B4B2524792C1.replay
-    replays/0AD21FEE4A512906BEB6C98136AAF49A.replay
-    replays/1205D96C4D819800927791820096CD49.replay
-    replays/160CA83E41083BFD8E6315B4BFCA0561.replay
-    replays/18D6738D415B70B5BE4C299588D3C141.replay
-    replays/1A126AC24CAA0DB0E98835BD960B8AF8.replay
-    replays/1AE415514DFC65DCBF8B8391AD35488D.replay
-    replays/1BC2D01444ACE577D01E988EADD4DFD0.replay
-    replays/1D1DE97D4941C86E43FE0093563DB621.replay
-    replays/1EF90FCC4F719F606A5327B3CDD782A4.replay
-    replays/1F3798E540B0C37A946561ABBB3037F9.replay
-    replays/211466D04B983F5A33CC2FA1D5928672.replay
-    replays/22BACD794ABE7B92E50E9CBDBD9C59CE.replay
-    replays/27B6A7B64553F0F685874584F96BAB1B.replay
-    replays/29F582C34A65EB34D358A784CBE3C189.replay
-    replays/372DBFCA4BDB340E4357B6BD43032802.replay
-    replays/387F059C47C09E253C875CA990EFD9F2.replay
-    replays/3EA147DD485B8DD24810689A7A989E44.replay
-    replays/4126861E477F4A03DE2A4080374D7908.replay
-    replays/42F0D8DA4FC89AE7B80FCAB7F637A8EA.replay
-    replays/504ED825482186E771FAA9B642CE5CE4.replay
-    replays/52AA67F94090C19D33C5009E54D31FE4.replay
-    replays/540DA764423C8FB24EB9D486D982F16F.replay
-    replays/551CA4D44FF2B86015DE44A6B5790D4C.replay
-    replays/6320E51C49066A7C210A2993C2201D5F.replay
-    replays/6688EEE34BFEB3EC3A9E3283098CC712.replay
-    replays/6D1B06D844A5BB91B81FD4B5B28F08BA.replay
-    replays/6F7CFCD24638F8A6567AB3A8B9958A90.replay
-    replays/7109EB9846D303E54B7ACBA792036213.replay
-    replays/7BF6073F4614CE0A438994B9A260DA6A.replay
-    replays/89CBA30E46FA5385BDD35DA4285D4D2E.replay
-    replays/9704208245D7DD851F2FB2BC7DFD9AC3.replay
-    replays/98E58A904D713F2DE202358E8573265D.replay
-    replays/A128B3AB45D5A18E3EF9CF93C9576BCE.replay
-    replays/A52F804845573D8DA65E97BF59026A43.replay
-    replays/A6711CE74272B2E663DCC9A200A218E3.replay
-    replays/A7F001A1417A19BFA8C90990D8F7C2FF.replay
-    replays/B9F9B87D4A9D0A3D25D4EC91C0401DE2.replay
-    replays/C14F7E0E4D9B5E6BE9AD5D8ED56B174C.replay
-    replays/C8372B1345B1803DEF039F815DBD802D.replay
-    replays/CC4CA70D4F7A67EBAD0ED9B9923106F7.replay
-    replays/D428F81646A98C25902CE988AE5C14C8.replay
-    replays/D7FB197A451D69075A0C99A2F49A4053.replay
-    replays/DCB3A6B94A9DBE46FDE5EAA9B012F6C8.replay
-    replays/EAE311E84BA35B590A6FDBA6DD4F2FEB.replay
-    replays/EAE8DADA4BB2DC5422792C9B4A67392D.replay
-    replays/F299F176491554B11E34AB91CA76B2CE.replay
-    replays/F7B9E14545C7467B89A00895980FCD73.replay
-    replays/F811C1D24888015E23B598AD8628C742.replay
-    replays/FDC79DA84DD463D4BCCE6B892829AC88.replay
+    stack.yaml
 
 source-repository head
-    type: git
-    location: https://github.com/tfausak/rattletrap
+  type: git
+  location: https://github.com/tfausak/rattletrap
 
 library
-    exposed-modules:
-        Rattletrap
-        Rattletrap.ActorMap
-        Rattletrap.Attribute
-        Rattletrap.Attribute.Boolean
-        Rattletrap.Attribute.Byte
-        Rattletrap.Attribute.CamSettings
-        Rattletrap.Attribute.ClubColors
-        Rattletrap.Attribute.Demolish
-        Rattletrap.Attribute.Enum
-        Rattletrap.Attribute.Explosion
-        Rattletrap.Attribute.FlaggedInt
-        Rattletrap.Attribute.Float
-        Rattletrap.Attribute.GameMode
-        Rattletrap.Attribute.Int
-        Rattletrap.Attribute.Loadout
-        Rattletrap.Attribute.LoadoutOnline
-        Rattletrap.Attribute.Loadouts
-        Rattletrap.Attribute.LoadoutsOnline
-        Rattletrap.Attribute.Location
-        Rattletrap.Attribute.MusicStinger
-        Rattletrap.Attribute.PartyLeader
-        Rattletrap.Attribute.Pickup
-        Rattletrap.Attribute.PrivateMatchSettings
-        Rattletrap.Attribute.QWord
-        Rattletrap.Attribute.Reservation
-        Rattletrap.Attribute.RigidBodyState
-        Rattletrap.Attribute.String
-        Rattletrap.Attribute.TeamPaint
-        Rattletrap.Attribute.UniqueId
-        Rattletrap.Attribute.WeldedInfo
-        Rattletrap.AttributeMapping
-        Rattletrap.AttributeType
-        Rattletrap.AttributeValue
-        Rattletrap.Cache
-        Rattletrap.ClassAttributeMap
-        Rattletrap.ClassMapping
-        Rattletrap.Content
-        Rattletrap.Crc
-        Rattletrap.Data
-        Rattletrap.Frame
-        Rattletrap.Header
-        Rattletrap.Initialization
-        Rattletrap.Json
-        Rattletrap.KeyFrame
-        Rattletrap.Main
-        Rattletrap.Mark
-        Rattletrap.Message
-        Rattletrap.Primitive
-        Rattletrap.Primitive.CompressedWord
-        Rattletrap.Primitive.CompressedWordVector
-        Rattletrap.Primitive.Dictionary
-        Rattletrap.Primitive.Float32
-        Rattletrap.Primitive.Int32
-        Rattletrap.Primitive.Int8
-        Rattletrap.Primitive.Int8Vector
-        Rattletrap.Primitive.List
-        Rattletrap.Primitive.Section
-        Rattletrap.Primitive.Text
-        Rattletrap.Primitive.Vector
-        Rattletrap.Primitive.Word32
-        Rattletrap.Primitive.Word64
-        Rattletrap.Primitive.Word8
-        Rattletrap.Property
-        Rattletrap.PropertyValue
-        Rattletrap.RemoteId
-        Rattletrap.Replay
-        Rattletrap.Replication
-        Rattletrap.Replication.Destroyed
-        Rattletrap.Replication.Spawned
-        Rattletrap.Replication.Updated
-        Rattletrap.ReplicationValue
-        Rattletrap.Utility
-        Rattletrap.Version
-    build-depends:
-        aeson >=0.11 && <1.1,
-        aeson-casing >=0.1 && <0.2,
-        base >=4.9 && <4.10,
-        bimap >=0.3 && <0.4,
-        binary >=0.8 && <0.9,
-        binary-bits >=0.5 && <0.6,
-        bytestring >=0.10 && <0.11,
-        containers >=0.5 && <0.6,
-        data-binary-ieee754 >=0.4 && <0.5,
-        template-haskell >=2.11 && <2.12,
-        text >=1.2 && <1.3,
-        vector >=0.11 && <0.12
-    default-language: Haskell2010
-    default-extensions: Strict
-    hs-source-dirs:
-        source/library
-    other-modules:
-        Paths_rattletrap
-    ghc-options: -Wall
+  hs-source-dirs:
+      source/library
+  default-extensions: Strict
+  ghc-options: -Wall
+  build-depends:
+      aeson >=0.11 && <1.1
+    , aeson-casing >=0.1 && <0.2
+    , base >=4.9 && <4.10
+    , bimap >=0.3 && <0.4
+    , binary >=0.8 && <0.9
+    , binary-bits >=0.5 && <0.6
+    , bytestring >=0.10 && <0.11
+    , containers >=0.5 && <0.6
+    , data-binary-ieee754 >=0.4 && <0.5
+    , template-haskell >=2.11 && <2.12
+    , text >=1.2 && <1.3
+    , vector >=0.11 && <0.12
+  exposed-modules:
+      Rattletrap
+      Rattletrap.ActorMap
+      Rattletrap.Attribute
+      Rattletrap.Attribute.Boolean
+      Rattletrap.Attribute.Byte
+      Rattletrap.Attribute.CamSettings
+      Rattletrap.Attribute.ClubColors
+      Rattletrap.Attribute.Demolish
+      Rattletrap.Attribute.Enum
+      Rattletrap.Attribute.Explosion
+      Rattletrap.Attribute.FlaggedInt
+      Rattletrap.Attribute.Float
+      Rattletrap.Attribute.GameMode
+      Rattletrap.Attribute.Int
+      Rattletrap.Attribute.Loadout
+      Rattletrap.Attribute.LoadoutOnline
+      Rattletrap.Attribute.Loadouts
+      Rattletrap.Attribute.LoadoutsOnline
+      Rattletrap.Attribute.Location
+      Rattletrap.Attribute.MusicStinger
+      Rattletrap.Attribute.PartyLeader
+      Rattletrap.Attribute.Pickup
+      Rattletrap.Attribute.PrivateMatchSettings
+      Rattletrap.Attribute.QWord
+      Rattletrap.Attribute.Reservation
+      Rattletrap.Attribute.RigidBodyState
+      Rattletrap.Attribute.String
+      Rattletrap.Attribute.TeamPaint
+      Rattletrap.Attribute.UniqueId
+      Rattletrap.Attribute.WeldedInfo
+      Rattletrap.AttributeMapping
+      Rattletrap.AttributeType
+      Rattletrap.AttributeValue
+      Rattletrap.Cache
+      Rattletrap.ClassAttributeMap
+      Rattletrap.ClassMapping
+      Rattletrap.Content
+      Rattletrap.Crc
+      Rattletrap.Data
+      Rattletrap.Frame
+      Rattletrap.Header
+      Rattletrap.Helper
+      Rattletrap.Initialization
+      Rattletrap.Json
+      Rattletrap.KeyFrame
+      Rattletrap.Main
+      Rattletrap.Mark
+      Rattletrap.Message
+      Rattletrap.Primitive
+      Rattletrap.Primitive.CompressedWord
+      Rattletrap.Primitive.CompressedWordVector
+      Rattletrap.Primitive.Dictionary
+      Rattletrap.Primitive.Float32
+      Rattletrap.Primitive.Int32
+      Rattletrap.Primitive.Int8
+      Rattletrap.Primitive.Int8Vector
+      Rattletrap.Primitive.List
+      Rattletrap.Primitive.Section
+      Rattletrap.Primitive.Text
+      Rattletrap.Primitive.Vector
+      Rattletrap.Primitive.Word32
+      Rattletrap.Primitive.Word64
+      Rattletrap.Primitive.Word8
+      Rattletrap.Property
+      Rattletrap.PropertyValue
+      Rattletrap.RemoteId
+      Rattletrap.Replay
+      Rattletrap.Replication
+      Rattletrap.Replication.Destroyed
+      Rattletrap.Replication.Spawned
+      Rattletrap.Replication.Updated
+      Rattletrap.ReplicationValue
+      Rattletrap.Utility
+      Rattletrap.Version
+  other-modules:
+      Paths_rattletrap
+  default-language: Haskell2010
 
 executable rattletrap
-    main-is: Main.hs
-    build-depends:
-        base,
-        rattletrap
-    default-language: Haskell2010
-    default-extensions: Strict
-    hs-source-dirs:
-        source/executables
-    ghc-options: -Wall -rtsopts -threaded -with-rtsopts=-N
+  main-is: Main.hs
+  hs-source-dirs:
+      source/executables
+  ghc-options: -Wall -rtsopts -threaded -with-rtsopts=-N
+  build-depends:
+      base
+    , rattletrap
+  default-language: Haskell2010
 
 test-suite test
-    type: exitcode-stdio-1.0
-    main-is: Main.hs
-    build-depends:
-        base,
-        bytestring,
-        filepath >=1.4 && <1.5,
-        rattletrap,
-        tasty >=0.11 && <0.12,
-        tasty-hspec >=1.1 && <1.2,
-        temporary >=1.2 && <1.3
-    default-language: Haskell2010
-    default-extensions: Strict
-    hs-source-dirs:
-        source/tests
-    ghc-options: -Wall -rtsopts -threaded -with-rtsopts=-N
+  type: exitcode-stdio-1.0
+  main-is: Main.hs
+  hs-source-dirs:
+      source/tests
+  ghc-options: -Wall -rtsopts -threaded -with-rtsopts=-N
+  build-depends:
+      base
+    , bytestring
+    , filepath >=1.4 && <1.5
+    , rattletrap
+    , tasty >=0.11 && <0.12
+    , tasty-hspec >=1.1 && <1.2
+    , temporary >=1.2 && <1.3
+  default-language: Haskell2010
diff --git a/replays/00080014003600090000036E0F65CCEB.replay b/replays/00080014003600090000036E0F65CCEB.replay
deleted file mode 100644
Binary files a/replays/00080014003600090000036E0F65CCEB.replay and /dev/null differ
diff --git a/replays/07E925B1423653D44CB8B4B2524792C1.replay b/replays/07E925B1423653D44CB8B4B2524792C1.replay
deleted file mode 100644
Binary files a/replays/07E925B1423653D44CB8B4B2524792C1.replay and /dev/null differ
diff --git a/replays/0AD21FEE4A512906BEB6C98136AAF49A.replay b/replays/0AD21FEE4A512906BEB6C98136AAF49A.replay
deleted file mode 100644
Binary files a/replays/0AD21FEE4A512906BEB6C98136AAF49A.replay and /dev/null differ
diff --git a/replays/1205D96C4D819800927791820096CD49.replay b/replays/1205D96C4D819800927791820096CD49.replay
deleted file mode 100644
Binary files a/replays/1205D96C4D819800927791820096CD49.replay and /dev/null differ
diff --git a/replays/160CA83E41083BFD8E6315B4BFCA0561.replay b/replays/160CA83E41083BFD8E6315B4BFCA0561.replay
deleted file mode 100644
Binary files a/replays/160CA83E41083BFD8E6315B4BFCA0561.replay and /dev/null differ
diff --git a/replays/18D6738D415B70B5BE4C299588D3C141.replay b/replays/18D6738D415B70B5BE4C299588D3C141.replay
deleted file mode 100644
Binary files a/replays/18D6738D415B70B5BE4C299588D3C141.replay and /dev/null differ
diff --git a/replays/1A126AC24CAA0DB0E98835BD960B8AF8.replay b/replays/1A126AC24CAA0DB0E98835BD960B8AF8.replay
deleted file mode 100644
Binary files a/replays/1A126AC24CAA0DB0E98835BD960B8AF8.replay and /dev/null differ
diff --git a/replays/1AE415514DFC65DCBF8B8391AD35488D.replay b/replays/1AE415514DFC65DCBF8B8391AD35488D.replay
deleted file mode 100644
Binary files a/replays/1AE415514DFC65DCBF8B8391AD35488D.replay and /dev/null differ
diff --git a/replays/1BC2D01444ACE577D01E988EADD4DFD0.replay b/replays/1BC2D01444ACE577D01E988EADD4DFD0.replay
deleted file mode 100644
Binary files a/replays/1BC2D01444ACE577D01E988EADD4DFD0.replay and /dev/null differ
diff --git a/replays/1D1DE97D4941C86E43FE0093563DB621.replay b/replays/1D1DE97D4941C86E43FE0093563DB621.replay
deleted file mode 100644
Binary files a/replays/1D1DE97D4941C86E43FE0093563DB621.replay and /dev/null differ
diff --git a/replays/1EF90FCC4F719F606A5327B3CDD782A4.replay b/replays/1EF90FCC4F719F606A5327B3CDD782A4.replay
deleted file mode 100644
Binary files a/replays/1EF90FCC4F719F606A5327B3CDD782A4.replay and /dev/null differ
diff --git a/replays/1F3798E540B0C37A946561ABBB3037F9.replay b/replays/1F3798E540B0C37A946561ABBB3037F9.replay
deleted file mode 100644
Binary files a/replays/1F3798E540B0C37A946561ABBB3037F9.replay and /dev/null differ
diff --git a/replays/211466D04B983F5A33CC2FA1D5928672.replay b/replays/211466D04B983F5A33CC2FA1D5928672.replay
deleted file mode 100644
Binary files a/replays/211466D04B983F5A33CC2FA1D5928672.replay and /dev/null differ
diff --git a/replays/22BACD794ABE7B92E50E9CBDBD9C59CE.replay b/replays/22BACD794ABE7B92E50E9CBDBD9C59CE.replay
deleted file mode 100644
Binary files a/replays/22BACD794ABE7B92E50E9CBDBD9C59CE.replay and /dev/null differ
diff --git a/replays/27B6A7B64553F0F685874584F96BAB1B.replay b/replays/27B6A7B64553F0F685874584F96BAB1B.replay
deleted file mode 100644
Binary files a/replays/27B6A7B64553F0F685874584F96BAB1B.replay and /dev/null differ
diff --git a/replays/29F582C34A65EB34D358A784CBE3C189.replay b/replays/29F582C34A65EB34D358A784CBE3C189.replay
deleted file mode 100644
Binary files a/replays/29F582C34A65EB34D358A784CBE3C189.replay and /dev/null differ
diff --git a/replays/372DBFCA4BDB340E4357B6BD43032802.replay b/replays/372DBFCA4BDB340E4357B6BD43032802.replay
deleted file mode 100644
Binary files a/replays/372DBFCA4BDB340E4357B6BD43032802.replay and /dev/null differ
diff --git a/replays/387F059C47C09E253C875CA990EFD9F2.replay b/replays/387F059C47C09E253C875CA990EFD9F2.replay
deleted file mode 100644
Binary files a/replays/387F059C47C09E253C875CA990EFD9F2.replay and /dev/null differ
diff --git a/replays/3EA147DD485B8DD24810689A7A989E44.replay b/replays/3EA147DD485B8DD24810689A7A989E44.replay
deleted file mode 100644
Binary files a/replays/3EA147DD485B8DD24810689A7A989E44.replay and /dev/null differ
diff --git a/replays/4126861E477F4A03DE2A4080374D7908.replay b/replays/4126861E477F4A03DE2A4080374D7908.replay
deleted file mode 100644
Binary files a/replays/4126861E477F4A03DE2A4080374D7908.replay and /dev/null differ
diff --git a/replays/42F0D8DA4FC89AE7B80FCAB7F637A8EA.replay b/replays/42F0D8DA4FC89AE7B80FCAB7F637A8EA.replay
deleted file mode 100644
Binary files a/replays/42F0D8DA4FC89AE7B80FCAB7F637A8EA.replay and /dev/null differ
diff --git a/replays/504ED825482186E771FAA9B642CE5CE4.replay b/replays/504ED825482186E771FAA9B642CE5CE4.replay
deleted file mode 100644
Binary files a/replays/504ED825482186E771FAA9B642CE5CE4.replay and /dev/null differ
diff --git a/replays/52AA67F94090C19D33C5009E54D31FE4.replay b/replays/52AA67F94090C19D33C5009E54D31FE4.replay
deleted file mode 100644
Binary files a/replays/52AA67F94090C19D33C5009E54D31FE4.replay and /dev/null differ
diff --git a/replays/540DA764423C8FB24EB9D486D982F16F.replay b/replays/540DA764423C8FB24EB9D486D982F16F.replay
deleted file mode 100644
Binary files a/replays/540DA764423C8FB24EB9D486D982F16F.replay and /dev/null differ
diff --git a/replays/551CA4D44FF2B86015DE44A6B5790D4C.replay b/replays/551CA4D44FF2B86015DE44A6B5790D4C.replay
deleted file mode 100644
Binary files a/replays/551CA4D44FF2B86015DE44A6B5790D4C.replay and /dev/null differ
diff --git a/replays/6320E51C49066A7C210A2993C2201D5F.replay b/replays/6320E51C49066A7C210A2993C2201D5F.replay
deleted file mode 100644
Binary files a/replays/6320E51C49066A7C210A2993C2201D5F.replay and /dev/null differ
diff --git a/replays/6688EEE34BFEB3EC3A9E3283098CC712.replay b/replays/6688EEE34BFEB3EC3A9E3283098CC712.replay
deleted file mode 100644
Binary files a/replays/6688EEE34BFEB3EC3A9E3283098CC712.replay and /dev/null differ
diff --git a/replays/6D1B06D844A5BB91B81FD4B5B28F08BA.replay b/replays/6D1B06D844A5BB91B81FD4B5B28F08BA.replay
deleted file mode 100644
Binary files a/replays/6D1B06D844A5BB91B81FD4B5B28F08BA.replay and /dev/null differ
diff --git a/replays/6F7CFCD24638F8A6567AB3A8B9958A90.replay b/replays/6F7CFCD24638F8A6567AB3A8B9958A90.replay
deleted file mode 100644
Binary files a/replays/6F7CFCD24638F8A6567AB3A8B9958A90.replay and /dev/null differ
diff --git a/replays/7109EB9846D303E54B7ACBA792036213.replay b/replays/7109EB9846D303E54B7ACBA792036213.replay
deleted file mode 100644
Binary files a/replays/7109EB9846D303E54B7ACBA792036213.replay and /dev/null differ
diff --git a/replays/7BF6073F4614CE0A438994B9A260DA6A.replay b/replays/7BF6073F4614CE0A438994B9A260DA6A.replay
deleted file mode 100644
Binary files a/replays/7BF6073F4614CE0A438994B9A260DA6A.replay and /dev/null differ
diff --git a/replays/89CBA30E46FA5385BDD35DA4285D4D2E.replay b/replays/89CBA30E46FA5385BDD35DA4285D4D2E.replay
deleted file mode 100644
Binary files a/replays/89CBA30E46FA5385BDD35DA4285D4D2E.replay and /dev/null differ
diff --git a/replays/9704208245D7DD851F2FB2BC7DFD9AC3.replay b/replays/9704208245D7DD851F2FB2BC7DFD9AC3.replay
deleted file mode 100644
Binary files a/replays/9704208245D7DD851F2FB2BC7DFD9AC3.replay and /dev/null differ
diff --git a/replays/98E58A904D713F2DE202358E8573265D.replay b/replays/98E58A904D713F2DE202358E8573265D.replay
deleted file mode 100644
Binary files a/replays/98E58A904D713F2DE202358E8573265D.replay and /dev/null differ
diff --git a/replays/A128B3AB45D5A18E3EF9CF93C9576BCE.replay b/replays/A128B3AB45D5A18E3EF9CF93C9576BCE.replay
deleted file mode 100644
Binary files a/replays/A128B3AB45D5A18E3EF9CF93C9576BCE.replay and /dev/null differ
diff --git a/replays/A52F804845573D8DA65E97BF59026A43.replay b/replays/A52F804845573D8DA65E97BF59026A43.replay
deleted file mode 100644
Binary files a/replays/A52F804845573D8DA65E97BF59026A43.replay and /dev/null differ
diff --git a/replays/A6711CE74272B2E663DCC9A200A218E3.replay b/replays/A6711CE74272B2E663DCC9A200A218E3.replay
deleted file mode 100644
Binary files a/replays/A6711CE74272B2E663DCC9A200A218E3.replay and /dev/null differ
diff --git a/replays/A7F001A1417A19BFA8C90990D8F7C2FF.replay b/replays/A7F001A1417A19BFA8C90990D8F7C2FF.replay
deleted file mode 100644
Binary files a/replays/A7F001A1417A19BFA8C90990D8F7C2FF.replay and /dev/null differ
diff --git a/replays/B9F9B87D4A9D0A3D25D4EC91C0401DE2.replay b/replays/B9F9B87D4A9D0A3D25D4EC91C0401DE2.replay
deleted file mode 100644
Binary files a/replays/B9F9B87D4A9D0A3D25D4EC91C0401DE2.replay and /dev/null differ
diff --git a/replays/C14F7E0E4D9B5E6BE9AD5D8ED56B174C.replay b/replays/C14F7E0E4D9B5E6BE9AD5D8ED56B174C.replay
deleted file mode 100644
Binary files a/replays/C14F7E0E4D9B5E6BE9AD5D8ED56B174C.replay and /dev/null differ
diff --git a/replays/C8372B1345B1803DEF039F815DBD802D.replay b/replays/C8372B1345B1803DEF039F815DBD802D.replay
deleted file mode 100644
Binary files a/replays/C8372B1345B1803DEF039F815DBD802D.replay and /dev/null differ
diff --git a/replays/CC4CA70D4F7A67EBAD0ED9B9923106F7.replay b/replays/CC4CA70D4F7A67EBAD0ED9B9923106F7.replay
deleted file mode 100644
Binary files a/replays/CC4CA70D4F7A67EBAD0ED9B9923106F7.replay and /dev/null differ
diff --git a/replays/D428F81646A98C25902CE988AE5C14C8.replay b/replays/D428F81646A98C25902CE988AE5C14C8.replay
deleted file mode 100644
Binary files a/replays/D428F81646A98C25902CE988AE5C14C8.replay and /dev/null differ
diff --git a/replays/D7FB197A451D69075A0C99A2F49A4053.replay b/replays/D7FB197A451D69075A0C99A2F49A4053.replay
deleted file mode 100644
Binary files a/replays/D7FB197A451D69075A0C99A2F49A4053.replay and /dev/null differ
diff --git a/replays/DCB3A6B94A9DBE46FDE5EAA9B012F6C8.replay b/replays/DCB3A6B94A9DBE46FDE5EAA9B012F6C8.replay
deleted file mode 100644
Binary files a/replays/DCB3A6B94A9DBE46FDE5EAA9B012F6C8.replay and /dev/null differ
diff --git a/replays/EAE311E84BA35B590A6FDBA6DD4F2FEB.replay b/replays/EAE311E84BA35B590A6FDBA6DD4F2FEB.replay
deleted file mode 100644
Binary files a/replays/EAE311E84BA35B590A6FDBA6DD4F2FEB.replay and /dev/null differ
diff --git a/replays/EAE8DADA4BB2DC5422792C9B4A67392D.replay b/replays/EAE8DADA4BB2DC5422792C9B4A67392D.replay
deleted file mode 100644
Binary files a/replays/EAE8DADA4BB2DC5422792C9B4A67392D.replay and /dev/null differ
diff --git a/replays/F299F176491554B11E34AB91CA76B2CE.replay b/replays/F299F176491554B11E34AB91CA76B2CE.replay
deleted file mode 100644
Binary files a/replays/F299F176491554B11E34AB91CA76B2CE.replay and /dev/null differ
diff --git a/replays/F7B9E14545C7467B89A00895980FCD73.replay b/replays/F7B9E14545C7467B89A00895980FCD73.replay
deleted file mode 100644
Binary files a/replays/F7B9E14545C7467B89A00895980FCD73.replay and /dev/null differ
diff --git a/replays/F811C1D24888015E23B598AD8628C742.replay b/replays/F811C1D24888015E23B598AD8628C742.replay
deleted file mode 100644
Binary files a/replays/F811C1D24888015E23B598AD8628C742.replay and /dev/null differ
diff --git a/replays/FDC79DA84DD463D4BCCE6B892829AC88.replay b/replays/FDC79DA84DD463D4BCCE6B892829AC88.replay
deleted file mode 100644
Binary files a/replays/FDC79DA84DD463D4BCCE6B892829AC88.replay and /dev/null differ
diff --git a/source/library/Rattletrap.hs b/source/library/Rattletrap.hs
--- a/source/library/Rattletrap.hs
+++ b/source/library/Rattletrap.hs
@@ -39,6 +39,7 @@
   , module Rattletrap.Data
   , module Rattletrap.Frame
   , module Rattletrap.Header
+  , module Rattletrap.Helper
   , module Rattletrap.Initialization
   , module Rattletrap.KeyFrame
   , module Rattletrap.Main
@@ -68,6 +69,7 @@
 import Rattletrap.Data
 import Rattletrap.Frame
 import Rattletrap.Header
+import Rattletrap.Helper
 import Rattletrap.Initialization
 import Rattletrap.Json ()
 import Rattletrap.KeyFrame
diff --git a/source/library/Rattletrap/ActorMap.hs b/source/library/Rattletrap/ActorMap.hs
--- a/source/library/Rattletrap/ActorMap.hs
+++ b/source/library/Rattletrap/ActorMap.hs
@@ -10,4 +10,4 @@
 makeActorMap = Map.empty
 
 updateActorMap :: CompressedWord -> Word32 -> ActorMap -> ActorMap
-updateActorMap actorId objectId actorMap = Map.insert actorId objectId actorMap
+updateActorMap = Map.insert
diff --git a/source/library/Rattletrap/Helper.hs b/source/library/Rattletrap/Helper.hs
new file mode 100644
--- /dev/null
+++ b/source/library/Rattletrap/Helper.hs
@@ -0,0 +1,72 @@
+-- | This module provides helper functions for converting replays to and from
+-- both their binary format and JSON.
+module Rattletrap.Helper where
+
+import Rattletrap.Json ()
+import Rattletrap.Replay
+
+import qualified Data.Aeson as Aeson
+import qualified Data.Binary.Get as Binary
+import qualified Data.Binary.Put as Binary
+import qualified Data.ByteString.Lazy as ByteString
+import qualified System.IO as IO
+
+-- * Lazy byte strings
+-- | Parses a raw replay.
+decodeReplay :: ByteString.ByteString -> Either String Replay
+decodeReplay contents =
+  case Binary.runGetOrFail getReplay contents of
+    Left (_, _, message) -> fail message
+    Right (_, _, replay) -> pure replay
+
+-- | Encodes a replay as JSON.
+encodeJson :: Replay -> ByteString.ByteString
+encodeJson = Aeson.encode
+
+-- | Parses a JSON replay.
+decodeJson :: ByteString.ByteString -> Either String Replay
+decodeJson = Aeson.eitherDecode
+
+-- | Encodes a raw replay.
+encodeReplay :: Replay -> ByteString.ByteString
+encodeReplay replay = Binary.runPut (putReplay replay)
+
+-- * File paths
+-- | 'decodeReplay'
+decodeReplayFile :: FilePath -> IO (Either String Replay)
+decodeReplayFile file = IO.withBinaryFile file IO.ReadMode decodeReplayHandle
+
+-- | 'encodeJson'
+encodeJsonFile :: Replay -> FilePath -> IO ()
+encodeJsonFile replay file =
+  IO.withBinaryFile file IO.WriteMode (encodeJsonHandle replay)
+
+-- | 'decodeJson'
+decodeJsonFile :: FilePath -> IO (Either String Replay)
+decodeJsonFile file = IO.withBinaryFile file IO.ReadMode decodeJsonHandle
+
+-- | 'encodeReplay'
+encodeReplayFile :: Replay -> FilePath -> IO ()
+encodeReplayFile replay file =
+  IO.withBinaryFile file IO.WriteMode (encodeReplayHandle replay)
+
+-- * Handles
+-- | 'decodeReplay'
+decodeReplayHandle :: IO.Handle -> IO (Either String Replay)
+decodeReplayHandle handle = do
+  contents <- ByteString.hGetContents handle
+  pure (decodeReplay contents)
+
+-- | 'encodeJson'
+encodeJsonHandle :: Replay -> IO.Handle -> IO ()
+encodeJsonHandle replay handle = ByteString.hPut handle (encodeJson replay)
+
+-- | 'decodeJson'
+decodeJsonHandle :: IO.Handle -> IO (Either String Replay)
+decodeJsonHandle handle = do
+  contents <- ByteString.hGetContents handle
+  pure (decodeJson contents)
+
+-- | 'encodeReplay'
+encodeReplayHandle :: Replay -> IO.Handle -> IO ()
+encodeReplayHandle replay handle = ByteString.hPut handle (encodeReplay replay)
diff --git a/source/library/Rattletrap/Primitive/Text.hs b/source/library/Rattletrap/Primitive/Text.hs
--- a/source/library/Rattletrap/Primitive/Text.hs
+++ b/source/library/Rattletrap/Primitive/Text.hs
@@ -99,7 +99,7 @@
     else encodeLatin1 text
 
 dropNull :: Text.Text -> Text.Text
-dropNull text = Text.dropWhileEnd (== '\x00') text
+dropNull = Text.dropWhileEnd (== '\x00')
 
 addNull :: Text.Text -> Text.Text
 addNull text =
diff --git a/source/library/Rattletrap/Utility.hs b/source/library/Rattletrap/Utility.hs
--- a/source/library/Rattletrap/Utility.hs
+++ b/source/library/Rattletrap/Utility.hs
@@ -30,4 +30,4 @@
   Bits.shiftL (byte Bits..&. Bits.bit 0) 7
 
 reverseBytes :: ByteString.ByteString -> ByteString.ByteString
-reverseBytes bytes = ByteString.map reverseByte bytes
+reverseBytes = ByteString.map reverseByte
diff --git a/source/tests/Main.hs b/source/tests/Main.hs
--- a/source/tests/Main.hs
+++ b/source/tests/Main.hs
@@ -12,10 +12,7 @@
   Tasty.defaultMain tests
 
 spec :: Hspec.Spec
-spec =
-  Hspec.describe
-    "Rattletrap"
-    (mapM_ (\(uuid, description) -> itCanRoundTrip uuid description) replays)
+spec = Hspec.describe "Rattletrap" (mapM_ (uncurry itCanRoundTrip) replays)
 
 itCanRoundTrip :: String -> String -> Hspec.Spec
 itCanRoundTrip uuid description =
@@ -42,7 +39,7 @@
 
 pathToReplay :: String -> FilePath
 pathToReplay uuid =
-  FilePath.joinPath ["replays", FilePath.addExtension uuid ".replay"]
+  FilePath.joinPath ["data", "replays", FilePath.addExtension uuid ".replay"]
 
 replays :: [(String, String)]
 replays =
diff --git a/stack.yaml b/stack.yaml
new file mode 100644
--- /dev/null
+++ b/stack.yaml
@@ -0,0 +1,1 @@
+resolver: nightly-2016-12-14
