diff --git a/CHANGELOG.markdown b/CHANGELOG.markdown
deleted file mode 100644
--- a/CHANGELOG.markdown
+++ /dev/null
@@ -1,7 +0,0 @@
-# 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/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -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/LICENSE.markdown b/LICENSE.markdown
deleted file mode 100644
--- a/LICENSE.markdown
+++ /dev/null
@@ -1,21 +0,0 @@
-MIT License
-
-Copyright (c) 2024 Taylor Fausak
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2024 Taylor Fausak
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.markdown b/README.markdown
deleted file mode 100644
--- a/README.markdown
+++ /dev/null
@@ -1,133 +0,0 @@
-# [Rattletrap](https://github.com/tfausak/rattletrap)
-
-[![Workflow](https://github.com/tfausak/rattletrap/actions/workflows/workflow.yaml/badge.svg)](https://github.com/tfausak/rattletrap/actions/workflows/workflow.yaml)
-[![Hackage](https://badgen.net/hackage/v/rattletrap)](https://hackage.haskell.org/package/rattletrap)
-[![Stackage](https://www.stackage.org/package/rattletrap/badge/nightly?label=stackage)](https://www.stackage.org/package/rattletrap)
-
-Rattletrap parses and generates [Rocket League][] replays. Parsing replays can
-be used to analyze data in order to collect high-level statistics like players
-and points, or low-level details like positions and cameras. Generating replays
-can be used to modify replays in order to force everyone into the same car or
-change the map a game was played on.
-
-Rattletrap supports every version of Rocket League up to [2.38][], which was
-released on 2024-04-16. If a replay can be played by the Rocket League client,
-it can be parsed by Rattletrap. (If not, that's a bug. Please report it!)
-
-## Install
-
-Rattletrap is a command-line application. You should only use it if you're
-comfortable running things in terminals or command prompts. Otherwise consider
-using another tool like [Ball Chasing][].
-
-The best way to get Rattletrap is by downloading [the latest release][] for
-your platform.
-
-Rattletrap is written in [Haskell][]. If you'd like to use a program written in
-a different language, consider one of the following:
-
-- <https://github.com/jjbott/RocketLeagueReplayParser> (C#)
-- <https://github.com/nickbabcock/rrrocket> (Rust)
-- <https://github.com/Bakkes/CPPRP> (C++)
-
-## Replays
-
-Rocket League saves your replays in a folder that depends on your operating
-system.
-
-- Windows:
-  - `%UserProfile%/Documents/My Games/Rocket League/TAGame/Demos`
-  - For example: `C:/Users/Taylor/Documents/My Games/Rocket League/TAGame/Demos`
-- macOS:
-  - `$HOME/Library/Application Support/Rocket League/TAGame/Demos`
-  - For example: `/Users/taylor/Library/Application Support/Rocket League/TAGame/Demos`
-- Linux:
-  - `$HOME/.local/share/Rocket League/TAGame/Demos`
-  - For example: `/home/taylor/.local/share/Rocket League/TAGame/Demos`
-
-## Interface
-
-Rattletrap is a command line application.
-
-``` sh
-$ rattletrap --help
-```
-
-```
-rattletrap version 12.0.0
-  -c           --compact         minify JSON output
-  -f           --fast            only encode or decode the header
-  -h           --help            show the help
-  -i FILE|URL  --input=FILE|URL  input file or URL
-  -m MODE      --mode=MODE       decode or encode
-  -o FILE      --output=FILE     output file
-               --schema          output the schema
-               --skip-crc        skip the CRC
-  -v           --version         show the version
-```
-
-By default Rattletrap will try to determine the appropriate mode (either decode
-or encode) based on the file extensions of the input or output. You can
-override this behavior by passing `--mode` (or `-m`) with either `decode` or
-`encode`.
-
-Input extension | Output extension | Mode
----             | ---              | ---
-`.replay`       | anything         | `decode` (parse)
-`.json`         | anything         | `encode` (generate)
-anything        | `.replay`        | `encode` (generate)
-anything        | `.json`          | `decode` (parse)
-anything        | anything         | `decode` (parse)
-
-## Parse
-
-Rattletrap can parse (decode) Rocket League replays and output them as JSON.
-
-``` sh
-$ rattletrap --input http://example.com/input.replay --output output.json
-# or
-$ rattletrap -i input.replay -o output.json
-# or
-$ rattletrap < input.replay > output.json
-```
-
-The input argument can either be a local path or a URL.
-
-By default the JSON is pretty-printed. To minify the JSON, pass `--compact` (or
-`-c`) to Rattletrap. Even when the JSON is minified, it's extremely large. The
-output can be up to 100 times larger than the input. For example, a 1.5 MB
-replay turns into 31 MB of minified JSON or 159 MB of pretty-printed JSON.
-
-## Generate
-
-Rattletrap can also generate (encode) Rocket League replays from JSON files.
-
-``` sh
-$ rattletrap --input http://example.com/input.json --output output.replay
-# or
-$ rattletrap -i input.json -o output.replay
-# or
-$ rattletrap --mode encode < input.json > output.replay
-```
-
-The input argument can either be a local path or a URL.
-
-If the JSON was generated by Rattletrap, the output replay will be bit-for-bit
-identical to the input replay.
-
-## Modify
-
-By inserting another program between parsing and generating, Rattletrap can be
-used to modify replays.
-
-``` sh
-$ rattletrap -i input.replay |
-  modify-replay-json |
-  rattletrap -o output.replay
-```
-
-[Rattletrap]: https://github.com/tfausak/rattletrap
-[Rocket League]: https://www.rocketleague.com
-[2.38]: https://www.rocketleague.com/en/news/patch-notes-v2-38
-[Ball Chasing]: https://ballchasing.com
-[the latest release]: https://github.com/tfausak/rattletrap/releases/latest
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,132 @@
+# [Rattletrap](https://github.com/tfausak/rattletrap)
+
+[![CI](https://github.com/tfausak/rattletrap/actions/workflows/ci.yml/badge.svg)](https://github.com/tfausak/rattletrap/actions/workflows/ci.yml)
+[![Hackage](https://badgen.net/hackage/v/rattletrap)](https://hackage.haskell.org/package/rattletrap)
+
+Rattletrap parses and generates [Rocket League][] replays. Parsing replays can
+be used to analyze data in order to collect high-level statistics like players
+and points, or low-level details like positions and cameras. Generating replays
+can be used to modify replays in order to force everyone into the same car or
+change the map a game was played on.
+
+Rattletrap supports every version of Rocket League up to [2.38][], which was
+released on 2024-04-16. If a replay can be played by the Rocket League client,
+it can be parsed by Rattletrap. (If not, that's a bug. Please report it!)
+
+## Install
+
+Rattletrap is a command-line application. You should only use it if you're
+comfortable running things in terminals or command prompts. Otherwise consider
+using another tool like [Ball Chasing][].
+
+The best way to get Rattletrap is by downloading [the latest release][] for
+your platform.
+
+Rattletrap is written in [Haskell][]. If you'd like to use a program written in
+a different language, consider one of the following:
+
+- <https://github.com/jjbott/RocketLeagueReplayParser> (C#)
+- <https://github.com/nickbabcock/rrrocket> (Rust)
+- <https://github.com/Bakkes/CPPRP> (C++)
+
+## Replays
+
+Rocket League saves your replays in a folder that depends on your operating
+system.
+
+- Windows:
+  - `%UserProfile%/Documents/My Games/Rocket League/TAGame/Demos`
+  - For example: `C:/Users/Taylor/Documents/My Games/Rocket League/TAGame/Demos`
+- macOS:
+  - `$HOME/Library/Application Support/Rocket League/TAGame/Demos`
+  - For example: `/Users/taylor/Library/Application Support/Rocket League/TAGame/Demos`
+- Linux:
+  - `$HOME/.local/share/Rocket League/TAGame/Demos`
+  - For example: `/home/taylor/.local/share/Rocket League/TAGame/Demos`
+
+## Interface
+
+Rattletrap is a command line application.
+
+``` sh
+$ rattletrap --help
+```
+
+```
+rattletrap version 12.0.0
+  -c           --compact         minify JSON output
+  -f           --fast            only encode or decode the header
+  -h           --help            show the help
+  -i FILE|URL  --input=FILE|URL  input file or URL
+  -m MODE      --mode=MODE       decode or encode
+  -o FILE      --output=FILE     output file
+               --schema          output the schema
+               --skip-crc        skip the CRC
+  -v           --version         show the version
+```
+
+By default Rattletrap will try to determine the appropriate mode (either decode
+or encode) based on the file extensions of the input or output. You can
+override this behavior by passing `--mode` (or `-m`) with either `decode` or
+`encode`.
+
+Input extension | Output extension | Mode
+---             | ---              | ---
+`.replay`       | anything         | `decode` (parse)
+`.json`         | anything         | `encode` (generate)
+anything        | `.replay`        | `encode` (generate)
+anything        | `.json`          | `decode` (parse)
+anything        | anything         | `decode` (parse)
+
+## Parse
+
+Rattletrap can parse (decode) Rocket League replays and output them as JSON.
+
+``` sh
+$ rattletrap --input http://example.com/input.replay --output output.json
+# or
+$ rattletrap -i input.replay -o output.json
+# or
+$ rattletrap < input.replay > output.json
+```
+
+The input argument can either be a local path or a URL.
+
+By default the JSON is pretty-printed. To minify the JSON, pass `--compact` (or
+`-c`) to Rattletrap. Even when the JSON is minified, it's extremely large. The
+output can be up to 100 times larger than the input. For example, a 1.5 MB
+replay turns into 31 MB of minified JSON or 159 MB of pretty-printed JSON.
+
+## Generate
+
+Rattletrap can also generate (encode) Rocket League replays from JSON files.
+
+``` sh
+$ rattletrap --input http://example.com/input.json --output output.replay
+# or
+$ rattletrap -i input.json -o output.replay
+# or
+$ rattletrap --mode encode < input.json > output.replay
+```
+
+The input argument can either be a local path or a URL.
+
+If the JSON was generated by Rattletrap, the output replay will be bit-for-bit
+identical to the input replay.
+
+## Modify
+
+By inserting another program between parsing and generating, Rattletrap can be
+used to modify replays.
+
+``` sh
+$ rattletrap -i input.replay |
+  modify-replay-json |
+  rattletrap -o output.replay
+```
+
+[Rattletrap]: https://github.com/tfausak/rattletrap
+[Rocket League]: https://www.rocketleague.com
+[2.38]: https://www.rocketleague.com/en/news/patch-notes-v2-38
+[Ball Chasing]: https://ballchasing.com
+[the latest release]: https://github.com/tfausak/rattletrap/releases/latest
diff --git a/rattletrap.cabal b/rattletrap.cabal
--- a/rattletrap.cabal
+++ b/rattletrap.cabal
@@ -1,15 +1,15 @@
 cabal-version: 2.2
 name: rattletrap
-version: 13.0.2
+version: 13.0.3
 synopsis: Parse and generate Rocket League replays.
 description: Rattletrap parses and generates Rocket League replays.
 build-type: Simple
 category: Game
-extra-source-files:
-  CHANGELOG.markdown
-  README.markdown
+extra-doc-files:
+  CHANGELOG.md
+  README.md
 
-license-file: LICENSE.markdown
+license-file: LICENSE.txt
 license: MIT
 maintainer: Taylor Fausak
 
@@ -19,11 +19,10 @@
 
 flag pedantic
   default: False
-  description: Enables @-Werror@, which turns warnings into errors.
   manual: True
 
 common library
-  build-depends: base >=4.16 && <4.20
+  build-depends: base ^>=4.18.0.0 || ^>=4.19.0.0 || ^>=4.20.0.0
   default-extensions: NamedFieldPuns
   default-language: Haskell2010
   ghc-options:
@@ -58,15 +57,15 @@
   import: library
   autogen-modules: Paths_rattletrap
   build-depends:
-    aeson >=2.0.3 && <2.3,
-    aeson-pretty >=0.8.9 && <0.9,
-    array >=0.5.4 && <0.6,
-    bytestring >=0.11.3 && <0.13,
-    containers >=0.6.5 && <0.8,
-    filepath >=1.4.2 && <1.5,
-    http-client >=0.7.13 && <0.8,
-    http-client-tls >=0.3.6 && <0.4,
-    text >=1.2.5 && <1.3 || >=2.0 && <2.2,
+    aeson ^>=2.1.2.1 || ^>=2.2.2.0,
+    aeson-pretty ^>=0.8.10,
+    array ^>=0.5.5.0,
+    bytestring ^>=0.11.4.0 || ^>=0.12.0.2,
+    containers ^>=0.6.7 || ^>=0.7,
+    filepath ^>=1.4.100.1 || ^>=1.5.2.0,
+    http-client ^>=0.7.17,
+    http-client-tls ^>=0.3.6.3,
+    text ^>=2.0.2 || ^>=2.1,
 
   exposed-modules: Paths_rattletrap
   -- cabal-gild: discover src/lib
diff --git a/src/lib/Rattletrap/Type/Str.hs b/src/lib/Rattletrap/Type/Str.hs
--- a/src/lib/Rattletrap/Type/Str.hs
+++ b/src/lib/Rattletrap/Type/Str.hs
@@ -87,7 +87,7 @@
 normalizeTextSize :: (Integral a) => I32.I32 -> a
 normalizeTextSize size = case I32.toInt32 size of
   0x05000000 -> 8
-  x -> if x < 0 then (-2 * fromIntegral x) else fromIntegral x
+  x -> (if x < 0 then negate . (*) 2 else id) $ fromIntegral x
 
 getTextDecoder :: I32.I32 -> ByteString.ByteString -> Text.Text
 getTextDecoder size bytes =
