rattletrap 3.1.2 → 14.1.4
raw patch · 296 files changed
Files
- CHANGELOG.markdown +0/−7
- CHANGELOG.md +7/−0
- LICENSE.markdown +0/−23
- LICENSE.txt +21/−0
- README.markdown +0/−79
- README.md +135/−0
- Setup.hs +0/−4
- executables/Main.hs +0/−5
- library/Rattletrap.hs +0/−87
- library/Rattletrap/ActorMap.hs +0/−21
- library/Rattletrap/Attribute.hs +0/−65
- library/Rattletrap/Attribute/AppliedDamage.hs +0/−28
- library/Rattletrap/Attribute/Boolean.hs +0/−17
- library/Rattletrap/Attribute/Byte.hs +0/−19
- library/Rattletrap/Attribute/CamSettings.hs +0/−52
- library/Rattletrap/Attribute/ClubColors.hs +0/−28
- library/Rattletrap/Attribute/DamageState.hs +0/−35
- library/Rattletrap/Attribute/Demolish.hs +0/−42
- library/Rattletrap/Attribute/Enum.hs +0/−18
- library/Rattletrap/Attribute/Explosion.hs +0/−24
- library/Rattletrap/Attribute/ExtendedExplosion.hs +0/−32
- library/Rattletrap/Attribute/FlaggedInt.hs +0/−22
- library/Rattletrap/Attribute/Float.hs +0/−19
- library/Rattletrap/Attribute/GameMode.hs +0/−22
- library/Rattletrap/Attribute/Int.hs +0/−18
- library/Rattletrap/Attribute/Loadout.hs +0/−83
- library/Rattletrap/Attribute/LoadoutOnline.hs +0/−30
- library/Rattletrap/Attribute/Loadouts.hs +0/−22
- library/Rattletrap/Attribute/LoadoutsOnline.hs +0/−35
- library/Rattletrap/Attribute/Location.hs +0/−19
- library/Rattletrap/Attribute/MusicStinger.hs +0/−25
- library/Rattletrap/Attribute/PartyLeader.hs +0/−32
- library/Rattletrap/Attribute/Pickup.hs +0/−31
- library/Rattletrap/Attribute/PrivateMatchSettings.hs +0/−50
- library/Rattletrap/Attribute/Product.hs +0/−87
- library/Rattletrap/Attribute/QWord.hs +0/−19
- library/Rattletrap/Attribute/Reservation.hs +0/−48
- library/Rattletrap/Attribute/RigidBodyState.hs +0/−51
- library/Rattletrap/Attribute/String.hs +0/−19
- library/Rattletrap/Attribute/TeamPaint.hs +0/−38
- library/Rattletrap/Attribute/UniqueId.hs +0/−26
- library/Rattletrap/Attribute/WeldedInfo.hs +0/−31
- library/Rattletrap/AttributeMapping.hs +0/−21
- library/Rattletrap/AttributeType.hs +0/−34
- library/Rattletrap/AttributeValue.hs +0/−242
- library/Rattletrap/Cache.hs +0/−28
- library/Rattletrap/ClassAttributeMap.hs +0/−300
- library/Rattletrap/ClassMapping.hs +0/−21
- library/Rattletrap/Content.hs +0/−125
- library/Rattletrap/Crc.hs +0/−40
- library/Rattletrap/Data.hs +0/−372
- library/Rattletrap/Frame.hs +0/−67
- library/Rattletrap/Header.hs +0/−112
- library/Rattletrap/Helper.hs +0/−103
- library/Rattletrap/Initialization.hs +0/−38
- library/Rattletrap/Json.hs +0/−112
- library/Rattletrap/KeyFrame.hs +0/−27
- library/Rattletrap/Main.hs +0/−60
- library/Rattletrap/Mark.hs +0/−23
- library/Rattletrap/Message.hs +0/−27
- library/Rattletrap/Primitive.hs +0/−31
- library/Rattletrap/Primitive/CompressedWord.hs +0/−49
- library/Rattletrap/Primitive/CompressedWordVector.hs +0/−26
- library/Rattletrap/Primitive/Dictionary.hs +0/−60
- library/Rattletrap/Primitive/Float32.hs +0/−33
- library/Rattletrap/Primitive/Int32.hs +0/−33
- library/Rattletrap/Primitive/Int8.hs +0/−32
- library/Rattletrap/Primitive/Int8Vector.hs +0/−41
- library/Rattletrap/Primitive/List.hs +0/−22
- library/Rattletrap/Primitive/Section.hs +0/−54
- library/Rattletrap/Primitive/Text.hs +0/−94
- library/Rattletrap/Primitive/Vector.hs +0/−41
- library/Rattletrap/Primitive/Word32.hs +0/−33
- library/Rattletrap/Primitive/Word64.hs +0/−33
- library/Rattletrap/Primitive/Word8.hs +0/−33
- library/Rattletrap/Property.hs +0/−26
- library/Rattletrap/PropertyValue.hs +0/−69
- library/Rattletrap/RemoteId.hs +0/−53
- library/Rattletrap/Replay.hs +0/−39
- library/Rattletrap/Replication.hs +0/−66
- library/Rattletrap/Replication/Destroyed.hs +0/−17
- library/Rattletrap/Replication/Spawned.hs +0/−88
- library/Rattletrap/Replication/Updated.hs +0/−27
- library/Rattletrap/ReplicationValue.hs +0/−65
- library/Rattletrap/Utility.hs +0/−30
- library/Rattletrap/Version.hs +0/−7
- package.yaml +0/−63
- rattletrap.cabal +217/−228
- replays/00080014003600090000036E0F65CCEB.replay binary
- replays/07E925B1423653D44CB8B4B2524792C1.replay binary
- replays/0AD21FEE4A512906BEB6C98136AAF49A.replay binary
- replays/1205D96C4D819800927791820096CD49.replay binary
- replays/160CA83E41083BFD8E6315B4BFCA0561.replay binary
- replays/16D580EF479483E015207C901776F9FB.replay binary
- replays/18D6738D415B70B5BE4C299588D3C141.replay binary
- replays/1A126AC24CAA0DB0E98835BD960B8AF8.replay binary
- replays/1AE415514DFC65DCBF8B8391AD35488D.replay binary
- replays/1BC2D01444ACE577D01E988EADD4DFD0.replay binary
- replays/1D1DE97D4941C86E43FE0093563DB621.replay binary
- replays/1EF90FCC4F719F606A5327B3CDD782A4.replay binary
- replays/1F3798E540B0C37A946561ABBB3037F9.replay binary
- replays/211466D04B983F5A33CC2FA1D5928672.replay binary
- replays/22660E3649FC7971E5653692473D4318.replay binary
- replays/22BACD794ABE7B92E50E9CBDBD9C59CE.replay binary
- replays/27B6A7B64553F0F685874584F96BAB1B.replay binary
- replays/29F582C34A65EB34D358A784CBE3C189.replay binary
- replays/2CFE577044E651D3FA9DBF83ECA8BCC3.replay binary
- replays/338173964F9F71EBDD31058A1936CBB4.replay binary
- replays/372DBFCA4BDB340E4357B6BD43032802.replay binary
- replays/387F059C47C09E253C875CA990EFD9F2.replay binary
- replays/3EA147DD485B8DD24810689A7A989E44.replay binary
- replays/4126861E477F4A03DE2A4080374D7908.replay binary
- replays/42F0D8DA4FC89AE7B80FCAB7F637A8EA.replay binary
- replays/504ED825482186E771FAA9B642CE5CE4.replay binary
- replays/520E1BFF468CF6C3C48D1EA85D9C7909.replay binary
- replays/52AA67F94090C19D33C5009E54D31FE4.replay binary
- replays/540DA764423C8FB24EB9D486D982F16F.replay binary
- replays/551CA4D44FF2B86015DE44A6B5790D4C.replay binary
- replays/6320E51C49066A7C210A2993C2201D5F.replay binary
- replays/6688EEE34BFEB3EC3A9E3283098CC712.replay binary
- replays/6B0D1614417085A7AAD82EAA30D8DABE.replay binary
- replays/6D1B06D844A5BB91B81FD4B5B28F08BA.replay binary
- replays/6F7CFCD24638F8A6567AB3A8B9958A90.replay binary
- replays/7109EB9846D303E54B7ACBA792036213.replay binary
- replays/7BF6073F4614CE0A438994B9A260DA6A.replay binary
- replays/89CBA30E46FA5385BDD35DA4285D4D2E.replay binary
- replays/8AE551FF406D7B82ED853B8C7BFF8CDA.replay binary
- replays/9704208245D7DD851F2FB2BC7DFD9AC3.replay binary
- replays/98E58A904D713F2DE202358E8573265D.replay binary
- replays/A128B3AB45D5A18E3EF9CF93C9576BCE.replay binary
- replays/A52F804845573D8DA65E97BF59026A43.replay binary
- replays/A558B1B44124D6E021640884E8EEC2A7.replay binary
- replays/A6711CE74272B2E663DCC9A200A218E3.replay binary
- replays/A7F001A1417A19BFA8C90990D8F7C2FF.replay binary
- replays/AFB1F46A49737E36928E1EABC6F5B7AD.replay binary
- replays/B9F9B87D4A9D0A3D25D4EC91C0401DE2.replay binary
- replays/C14F7E0E4D9B5E6BE9AD5D8ED56B174C.replay binary
- replays/C8372B1345B1803DEF039F815DBD802D.replay binary
- replays/CC4CA70D4F7A67EBAD0ED9B9923106F7.replay binary
- replays/D0449F5F4AA775B86FFA7DA2B5A3204E.replay binary
- replays/D428F81646A98C25902CE988AE5C14C8.replay binary
- replays/D7FB197A451D69075A0C99A2F49A4053.replay binary
- replays/DCB3A6B94A9DBE46FDE5EAA9B012F6C8.replay binary
- replays/DE56B1B146687EA2E62E31A37A08C8EB.replay binary
- replays/EAE311E84BA35B590A6FDBA6DD4F2FEB.replay binary
- replays/EAE8DADA4BB2DC5422792C9B4A67392D.replay binary
- replays/F299F176491554B11E34AB91CA76B2CE.replay binary
- replays/F7B9E14545C7467B89A00895980FCD73.replay binary
- replays/F811C1D24888015E23B598AD8628C742.replay binary
- replays/FDC79DA84DD463D4BCCE6B892829AC88.replay binary
- src/exe/Main.hs +4/−0
- src/lib/Rattletrap.hs +12/−0
- src/lib/Rattletrap/BitBuilder.hs +33/−0
- src/lib/Rattletrap/BitGet.hs +52/−0
- src/lib/Rattletrap/BitPut.hs +32/−0
- src/lib/Rattletrap/BitString.hs +23/−0
- src/lib/Rattletrap/ByteGet.hs +82/−0
- src/lib/Rattletrap/BytePut.hs +39/−0
- src/lib/Rattletrap/Console/Config.hs +57/−0
- src/lib/Rattletrap/Console/Flag.hs +13/−0
- src/lib/Rattletrap/Console/Main.hs +329/−0
- src/lib/Rattletrap/Console/Mode.hs +12/−0
- src/lib/Rattletrap/Console/Option.hs +68/−0
- src/lib/Rattletrap/Data.hs +469/−0
- src/lib/Rattletrap/Exception/CrcMismatch.hs +11/−0
- src/lib/Rattletrap/Exception/Empty.hs +8/−0
- src/lib/Rattletrap/Exception/Fail.hs +10/−0
- src/lib/Rattletrap/Exception/InvalidComponent.hs +9/−0
- src/lib/Rattletrap/Exception/InvalidJson.hs +9/−0
- src/lib/Rattletrap/Exception/MissingAttributeLimit.hs +9/−0
- src/lib/Rattletrap/Exception/MissingAttributeName.hs +9/−0
- src/lib/Rattletrap/Exception/MissingClassName.hs +9/−0
- src/lib/Rattletrap/Exception/MissingObjectName.hs +10/−0
- src/lib/Rattletrap/Exception/MissingProductName.hs +10/−0
- src/lib/Rattletrap/Exception/NotEnoughInput.hs +8/−0
- src/lib/Rattletrap/Exception/UnknownActor.hs +9/−0
- src/lib/Rattletrap/Exception/UnknownAttribute.hs +9/−0
- src/lib/Rattletrap/Exception/UnknownName.hs +10/−0
- src/lib/Rattletrap/Exception/UnknownProduct.hs +9/−0
- src/lib/Rattletrap/Exception/UnknownProperty.hs +9/−0
- src/lib/Rattletrap/Exception/UnknownSystemId.hs +10/−0
- src/lib/Rattletrap/Get.hs +68/−0
- src/lib/Rattletrap/Schema.hs +68/−0
- src/lib/Rattletrap/Type/Attribute.hs +109/−0
- src/lib/Rattletrap/Type/Attribute/AppliedDamage.hs +60/−0
- src/lib/Rattletrap/Type/Attribute/Boolean.hs +28/−0
- src/lib/Rattletrap/Type/Attribute/Boost.hs +57/−0
- src/lib/Rattletrap/Type/Attribute/Byte.hs +29/−0
- src/lib/Rattletrap/Type/Attribute/CamSettings.hs +99/−0
- src/lib/Rattletrap/Type/Attribute/ClubColors.hs +57/−0
- src/lib/Rattletrap/Type/Attribute/CustomDemolish.hs +58/−0
- src/lib/Rattletrap/Type/Attribute/CustomDemolishExtended.hs +71/−0
- src/lib/Rattletrap/Type/Attribute/DamageState.hs +88/−0
- src/lib/Rattletrap/Type/Attribute/Demolish.hs +87/−0
- src/lib/Rattletrap/Type/Attribute/Enum.hs +30/−0
- src/lib/Rattletrap/Type/Attribute/Explosion.hs +53/−0
- src/lib/Rattletrap/Type/Attribute/ExtendedExplosion.hs +43/−0
- src/lib/Rattletrap/Type/Attribute/FlaggedByte.hs +42/−0
- src/lib/Rattletrap/Type/Attribute/FlaggedInt.hs +41/−0
- src/lib/Rattletrap/Type/Attribute/Float.hs +30/−0
- src/lib/Rattletrap/Type/Attribute/GameMode.hs +46/−0
- src/lib/Rattletrap/Type/Attribute/GameServer.hs +43/−0
- src/lib/Rattletrap/Type/Attribute/Int.hs +30/−0
- src/lib/Rattletrap/Type/Attribute/Int64.hs +29/−0
- src/lib/Rattletrap/Type/Attribute/Loadout.hs +194/−0
- src/lib/Rattletrap/Type/Attribute/LoadoutOnline.hs +47/−0
- src/lib/Rattletrap/Type/Attribute/Loadouts.hs +42/−0
- src/lib/Rattletrap/Type/Attribute/LoadoutsOnline.hs +62/−0
- src/lib/Rattletrap/Type/Attribute/Location.hs +30/−0
- src/lib/Rattletrap/Type/Attribute/MusicStinger.hs +52/−0
- src/lib/Rattletrap/Type/Attribute/PartyLeader.hs +70/−0
- src/lib/Rattletrap/Type/Attribute/Pickup.hs +52/−0
- src/lib/Rattletrap/Type/Attribute/PickupInfo.hs +94/−0
- src/lib/Rattletrap/Type/Attribute/PickupNew.hs +53/−0
- src/lib/Rattletrap/Type/Attribute/PlayerHistoryKey.hs +30/−0
- src/lib/Rattletrap/Type/Attribute/PrivateMatchSettings.hs +86/−0
- src/lib/Rattletrap/Type/Attribute/Product.hs +78/−0
- src/lib/Rattletrap/Type/Attribute/ProductValue.hs +126/−0
- src/lib/Rattletrap/Type/Attribute/QWord.hs +29/−0
- src/lib/Rattletrap/Type/Attribute/RepStatTitle.hs +59/−0
- src/lib/Rattletrap/Type/Attribute/Reservation.hs +82/−0
- src/lib/Rattletrap/Type/Attribute/RigidBodyState.hs +88/−0
- src/lib/Rattletrap/Type/Attribute/Rotation.hs +29/−0
- src/lib/Rattletrap/Type/Attribute/StatEvent.hs +43/−0
- src/lib/Rattletrap/Type/Attribute/String.hs +30/−0
- src/lib/Rattletrap/Type/Attribute/TeamPaint.hs +78/−0
- src/lib/Rattletrap/Type/Attribute/Title.hs +101/−0
- src/lib/Rattletrap/Type/Attribute/UniqueId.hs +53/−0
- src/lib/Rattletrap/Type/Attribute/WeldedInfo.hs +67/−0
- src/lib/Rattletrap/Type/AttributeMapping.hs +41/−0
- src/lib/Rattletrap/Type/AttributeType.hs +47/−0
- src/lib/Rattletrap/Type/AttributeValue.hs +364/−0
- src/lib/Rattletrap/Type/Cache.hs +65/−0
- src/lib/Rattletrap/Type/ClassAttributeMap.hs +323/−0
- src/lib/Rattletrap/Type/ClassMapping.hs +41/−0
- src/lib/Rattletrap/Type/CompressedWord.hs +89/−0
- src/lib/Rattletrap/Type/CompressedWordVector.hs +50/−0
- src/lib/Rattletrap/Type/Content.hs +247/−0
- src/lib/Rattletrap/Type/Dictionary.hs +76/−0
- src/lib/Rattletrap/Type/F32.hs +39/−0
- src/lib/Rattletrap/Type/Frame.hs +140/−0
- src/lib/Rattletrap/Type/Header.hs +113/−0
- src/lib/Rattletrap/Type/I32.hs +46/−0
- src/lib/Rattletrap/Type/I64.hs +48/−0
- src/lib/Rattletrap/Type/I8.hs +46/−0
- src/lib/Rattletrap/Type/Initialization.hs +56/−0
- src/lib/Rattletrap/Type/Int8Vector.hs +58/−0
- src/lib/Rattletrap/Type/Keyframe.hs +53/−0
- src/lib/Rattletrap/Type/List.hs +57/−0
- src/lib/Rattletrap/Type/Mark.hs +43/−0
- src/lib/Rattletrap/Type/Message.hs +53/−0
- src/lib/Rattletrap/Type/ObjectTarget.hs +53/−0
- src/lib/Rattletrap/Type/Property.hs +62/−0
- src/lib/Rattletrap/Type/Property/Array.hs +37/−0
- src/lib/Rattletrap/Type/Property/Bool.hs +33/−0
- src/lib/Rattletrap/Type/Property/Byte.hs +53/−0
- src/lib/Rattletrap/Type/Property/Float.hs +33/−0
- src/lib/Rattletrap/Type/Property/Int.hs +33/−0
- src/lib/Rattletrap/Type/Property/Name.hs +32/−0
- src/lib/Rattletrap/Type/Property/QWord.hs +32/−0
- src/lib/Rattletrap/Type/Property/Str.hs +32/−0
- src/lib/Rattletrap/Type/Property/Struct.hs +46/−0
- src/lib/Rattletrap/Type/PropertyValue.hs +102/−0
- src/lib/Rattletrap/Type/Quaternion.hs +167/−0
- src/lib/Rattletrap/Type/RemoteId.hs +92/−0
- src/lib/Rattletrap/Type/RemoteId/Epic.hs +32/−0
- src/lib/Rattletrap/Type/RemoteId/PlayStation.hs +55/−0
- src/lib/Rattletrap/Type/RemoteId/PsyNet.hs +56/−0
- src/lib/Rattletrap/Type/RemoteId/QQ.hs +32/−0
- src/lib/Rattletrap/Type/RemoteId/Splitscreen.hs +32/−0
- src/lib/Rattletrap/Type/RemoteId/Steam.hs +32/−0
- src/lib/Rattletrap/Type/RemoteId/Switch.hs +41/−0
- src/lib/Rattletrap/Type/RemoteId/Xbox.hs +32/−0
- src/lib/Rattletrap/Type/Replay.hs +134/−0
- src/lib/Rattletrap/Type/Replication.hs +128/−0
- src/lib/Rattletrap/Type/Replication/Destroyed.hs +30/−0
- src/lib/Rattletrap/Type/Replication/Spawned.hs +164/−0
- src/lib/Rattletrap/Type/Replication/Updated.hs +52/−0
- src/lib/Rattletrap/Type/ReplicationValue.hs +95/−0
- src/lib/Rattletrap/Type/Rotation.hs +48/−0
- src/lib/Rattletrap/Type/Section.hs +96/−0
- src/lib/Rattletrap/Type/Str.hs +101/−0
- src/lib/Rattletrap/Type/U32.hs +46/−0
- src/lib/Rattletrap/Type/U64.hs +48/−0
- src/lib/Rattletrap/Type/U8.hs +46/−0
- src/lib/Rattletrap/Type/Vector.hs +93/−0
- src/lib/Rattletrap/Type/Version.hs +38/−0
- src/lib/Rattletrap/Utility/Bytes.hs +13/−0
- src/lib/Rattletrap/Utility/Crc.hs +293/−0
- src/lib/Rattletrap/Utility/Helper.hs +46/−0
- src/lib/Rattletrap/Utility/Json.hs +55/−0
- src/lib/Rattletrap/Utility/Monad.hs +4/−0
- src/lib/Rattletrap/Version.hs +7/−0
- src/test/Main.hs +181/−0
- stack.yaml +0/−5
- tests/Main.hs +0/−106
@@ -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
@@ -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
@@ -1,23 +0,0 @@-# [The MIT License (MIT)][]--Copyright (c) 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.--[The MIT License (MIT)]: http://opensource.org/licenses/MIT
@@ -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.
@@ -1,79 +0,0 @@-# [Rattletrap][]--[![Version badge][]][version]-[![Windows build badge][]][windows build]-[![Build badge][]][build]--Rattletrap parses and generates [Rocket League][] replays.--## Install--Get Rattletrap by downloading and unpacking [the latest release][] for your-platform.--To build Rattletrap from source, install [Stack][]. Then run-`stack --resolver nightly install rattletrap`.--## Replays--Rocket League saves your replays in a folder that depends on your operating system.--- Windows: `%UserProfile%\Documents\My Games\Rocket League\TAGame\Demos`-- macOS: `$HOME/Library/Application Support/Rocket League/TAGame/Demos`-- Linux: `$HOME/.local/share/Rocket League/TAGame/Demos`--## Parse--Rattletrap can parse (decode) Rocket League replays and output them as JSON.--``` sh-> rattletrap decode http://example.com/input.replay output.json-# or-> rattletrap decode input.replay > output.json-# or-> rattletrap.decode < input.replay > output.json-```--The input argument can either be a local path or a URL.--The resulting JSON is minified, but extremely large. The output can be up to 50-times larger than the input.--## Generate--Rattletrap can also generate (encode) Rocket League replays from JSON files.--``` sh-> rattletrap encode http://example.com/input.json output.replay-# or-> rattletrap encode input.json > output.replay-# or-> rattletrap.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 resulting replay should be-identical to the original.--## Modify--By inserting another program between parsing and generating, Rattletrap can be-used to modify replays.--``` sh-> rattletrap decode < original.replay |- modify-replay-json |- rattletrap encode > modified.replay-```--[Rattletrap]: https://github.com/tfausak/rattletrap-[Version badge]: https://www.stackage.org/package/rattletrap/badge/nightly?label=version-[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-[build]: https://travis-ci.org/tfausak/rattletrap-[Rocket League]: https://www.rocketleaguegame.com-[the latest release]: https://github.com/tfausak/rattletrap/releases/latest-[Stack]: https://docs.haskellstack.org/en/stable/README/
@@ -0,0 +1,135 @@+# [Rattletrap](https://github.com/tfausak/rattletrap)++[](https://github.com/tfausak/rattletrap/actions/workflows/ci.yml)+[](https://hackage.haskell.org/package/rattletrap)++:warning: This package is not maintained anymore.++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.46][], which was+released on 2024-12-03. 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++)+- <https://github.com/Drogebot/RocketRP> (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.46]: https://www.rocketleague.com/en/news/patch-notes-v2-46+[Ball Chasing]: https://ballchasing.com+[the latest release]: https://github.com/tfausak/rattletrap/releases/latest
@@ -1,4 +0,0 @@-import qualified Distribution.Simple as Cabal--main :: IO ()-main = Cabal.defaultMain
@@ -1,5 +0,0 @@-module Main- ( module Rattletrap- ) where--import Rattletrap (main)
@@ -1,87 +0,0 @@--- | Rattletrap is a tool for parsing and generating Rocket League replays. It--- is typically used as an executable; see "Rattletrap.Main" for details about--- that interface. Using Rattletrap from Haskell is as easy as importing this--- module. Together with "Data.ByteString.Lazy" and "Data.Binary.Get", you can--- decode a 'Replay' value. Then you can transform it into JSON with--- "Data.Aeson". For example:------ @--- let file = "path-to/some.replay"--- bytes <- 'Data.ByteString.Lazy.readFile' file--- let replay = 'Data.Binary.Get.runGet' 'Rattletrap.Replay.getReplay' bytes--- let json = 'Data.Aeson.encode' replay--- 'Data.ByteString.Lazy.putStr' json--- @------ There are a few unusual aspects of Rattletrap to be aware of:------ - In order to improve memory usage, everything Rattletrap does is strict.--- Don't expect any lazy values or evaluation.--- - Some names, like 'Text', collide with commonly-used modules. Use--- qualified imports to avoid collisions.--- - No instances of 'Data.Binary.Binary' are provided. Use functions like--- 'getReplay' instead. This was done to improve type inference and make--- profiling easier. Also some types require information that could not be--- provided by the 'Data.Binary.Binary' interface.--- - Importing one specific module is not recommended because all JSON--- instances are defined as orphans in "Rattletrap.Json".-module Rattletrap- ( module Rattletrap.ActorMap- , module Rattletrap.Attribute- , module Rattletrap.AttributeMapping- , module Rattletrap.AttributeType- , module Rattletrap.AttributeValue- , module Rattletrap.Cache- , module Rattletrap.ClassAttributeMap- , module Rattletrap.ClassMapping- , module Rattletrap.Content- , module Rattletrap.Crc- , module Rattletrap.Data- , module Rattletrap.Frame- , module Rattletrap.Header- , module Rattletrap.Helper- , module Rattletrap.Initialization- , module Rattletrap.KeyFrame- , module Rattletrap.Main- , module Rattletrap.Mark- , module Rattletrap.Message- , module Rattletrap.Primitive- , module Rattletrap.Property- , module Rattletrap.PropertyValue- , module Rattletrap.RemoteId- , module Rattletrap.Replay- , module Rattletrap.Replication- , module Rattletrap.ReplicationValue- , module Rattletrap.Utility- , module Rattletrap.Version- ) where--import Rattletrap.ActorMap-import Rattletrap.Attribute-import Rattletrap.AttributeMapping-import Rattletrap.AttributeType-import Rattletrap.AttributeValue-import Rattletrap.Cache-import Rattletrap.ClassAttributeMap-import Rattletrap.ClassMapping-import Rattletrap.Content-import Rattletrap.Crc-import Rattletrap.Data-import Rattletrap.Frame-import Rattletrap.Header-import Rattletrap.Helper-import Rattletrap.Initialization-import Rattletrap.Json ()-import Rattletrap.KeyFrame-import Rattletrap.Main-import Rattletrap.Mark-import Rattletrap.Message-import Rattletrap.Primitive-import Rattletrap.Property-import Rattletrap.PropertyValue-import Rattletrap.RemoteId-import Rattletrap.Replay-import Rattletrap.Replication-import Rattletrap.ReplicationValue-import Rattletrap.Utility-import Rattletrap.Version
@@ -1,21 +0,0 @@-module Rattletrap.ActorMap where--import Rattletrap.Primitive--import qualified Data.Map.Strict as Map--type ActorMap = Map.Map CompressedWord Word32--{-# DEPRECATED-makeActorMap "use Data.Map.Strict.empty"- #-}--makeActorMap :: ActorMap-makeActorMap = Map.empty--{-# DEPRECATED-updateActorMap "use Data.Map.Strict.insert"- #-}--updateActorMap :: CompressedWord -> Word32 -> ActorMap -> ActorMap-updateActorMap = Map.insert
@@ -1,65 +0,0 @@-module Rattletrap.Attribute where--import Rattletrap.ActorMap-import Rattletrap.AttributeValue-import Rattletrap.ClassAttributeMap-import Rattletrap.Primitive--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--data Attribute = Attribute- { attributeId :: CompressedWord- , attributeName :: Text- -- ^ Read-only! Changing an attribute's name requires editing the class- -- attribute map.- , attributeValue :: AttributeValue- } deriving (Eq, Ord, Show)--getAttributes- :: (Int, Int, Int)- -> ClassAttributeMap- -> ActorMap- -> CompressedWord- -> BinaryBit.BitGet [Attribute]-getAttributes version classAttributeMap actorMap actorId = do- hasAttribute <- BinaryBit.getBool- if not hasAttribute- then pure []- else do- attribute <- getAttribute version classAttributeMap actorMap actorId- attributes <- getAttributes version classAttributeMap actorMap actorId- pure (attribute : attributes)--putAttributes :: [Attribute] -> BinaryBit.BitPut ()-putAttributes attributes = do- mapM_ putAttribute attributes- BinaryBit.putBool False--getAttribute- :: (Int, Int, Int)- -> ClassAttributeMap- -> ActorMap- -> CompressedWord- -> BinaryBit.BitGet Attribute-getAttribute version classAttributeMap actorMap actorId =- case getAttributeMap classAttributeMap actorMap actorId of- Nothing -> fail ("could not get attribute map for " ++ show actorId)- Just attributeMap -> case getAttributeIdLimit attributeMap of- Nothing -> fail ("could not get attribute ID limit for " ++ show actorId)- Just limit -> do- id_ <- getCompressedWord limit- case getAttributeName classAttributeMap attributeMap id_ of- Nothing -> fail ("could not get attribute name for " ++ show id_)- Just name -> do- value <- getAttributeValue- version- (classAttributeMapObjectMap classAttributeMap)- name- pure (Attribute id_ name value)--putAttribute :: Attribute -> BinaryBit.BitPut ()-putAttribute attribute = do- BinaryBit.putBool True- putCompressedWord (attributeId attribute)- putAttributeValue (attributeValue attribute)
@@ -1,28 +0,0 @@-module Rattletrap.Attribute.AppliedDamage where--import Rattletrap.Primitive--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--data AppliedDamageAttribute = AppliedDamageAttribute- { appliedDamageAttributeUnknown1 :: Word8- , appliedDamageAttributeLocation :: Vector- , appliedDamageAttributeUnknown3 :: Int32- , appliedDamageAttributeUnknown4 :: Int32- } deriving (Eq, Ord, Show)--getAppliedDamageAttribute :: BinaryBit.BitGet AppliedDamageAttribute-getAppliedDamageAttribute = do- unknown1 <- getWord8Bits- location <- getVector- unknown3 <- getInt32Bits- unknown4 <- getInt32Bits- pure (AppliedDamageAttribute unknown1 location unknown3 unknown4)--putAppliedDamageAttribute :: AppliedDamageAttribute -> BinaryBit.BitPut ()-putAppliedDamageAttribute appliedDamageAttribute = do- putWord8Bits (appliedDamageAttributeUnknown1 appliedDamageAttribute)- putVector (appliedDamageAttributeLocation appliedDamageAttribute)- putInt32Bits (appliedDamageAttributeUnknown3 appliedDamageAttribute)- putInt32Bits (appliedDamageAttributeUnknown4 appliedDamageAttribute)
@@ -1,17 +0,0 @@-module Rattletrap.Attribute.Boolean where--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--newtype BooleanAttribute = BooleanAttribute- { booleanAttributeValue :: Bool- } deriving (Eq, Ord, Show)--getBooleanAttribute :: BinaryBit.BitGet BooleanAttribute-getBooleanAttribute = do- value <- BinaryBit.getBool- pure (BooleanAttribute value)--putBooleanAttribute :: BooleanAttribute -> BinaryBit.BitPut ()-putBooleanAttribute booleanAttribute =- BinaryBit.putBool (booleanAttributeValue booleanAttribute)
@@ -1,19 +0,0 @@-module Rattletrap.Attribute.Byte where--import Rattletrap.Primitive--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--newtype ByteAttribute = ByteAttribute- { byteAttributeValue :: Word8- } deriving (Eq, Ord, Show)--getByteAttribute :: BinaryBit.BitGet ByteAttribute-getByteAttribute = do- value <- getWord8Bits- pure (ByteAttribute value)--putByteAttribute :: ByteAttribute -> BinaryBit.BitPut ()-putByteAttribute byteAttribute =- putWord8Bits (byteAttributeValue byteAttribute)
@@ -1,52 +0,0 @@-module Rattletrap.Attribute.CamSettings where--import Rattletrap.Primitive--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--data CamSettingsAttribute = CamSettingsAttribute- { camSettingsAttributeFov :: Float32- , camSettingsAttributeHeight :: Float32- , camSettingsAttributeAngle :: Float32- , camSettingsAttributeDistance :: Float32- , camSettingsAttributeStiffness :: Float32- , camSettingsAttributeSwivelSpeed :: Float32- , camSettingsAttributeTransitionSpeed :: Maybe Float32- } deriving (Eq, Ord, Show)--getCamSettingsAttribute :: (Int, Int, Int) -> BinaryBit.BitGet CamSettingsAttribute-getCamSettingsAttribute version = do- fov <- getFloat32Bits- height <- getFloat32Bits- angle <- getFloat32Bits- distance <- getFloat32Bits- stiffness <- getFloat32Bits- swivelSpeed <- getFloat32Bits- transitionSpeed <- if version >= (868, 20, 0)- then do- x <- getFloat32Bits- pure (Just x)- else pure Nothing- pure- ( CamSettingsAttribute- fov- height- angle- distance- stiffness- swivelSpeed- transitionSpeed- )--putCamSettingsAttribute :: CamSettingsAttribute -> BinaryBit.BitPut ()-putCamSettingsAttribute camSettingsAttribute = do- putFloat32Bits (camSettingsAttributeFov camSettingsAttribute)- putFloat32Bits (camSettingsAttributeHeight camSettingsAttribute)- putFloat32Bits (camSettingsAttributeAngle camSettingsAttribute)- putFloat32Bits (camSettingsAttributeDistance camSettingsAttribute)- putFloat32Bits (camSettingsAttributeStiffness camSettingsAttribute)- putFloat32Bits (camSettingsAttributeSwivelSpeed camSettingsAttribute)- case camSettingsAttributeTransitionSpeed camSettingsAttribute of- Nothing -> pure ()- Just transitionSpeed -> putFloat32Bits transitionSpeed
@@ -1,28 +0,0 @@-module Rattletrap.Attribute.ClubColors where--import Rattletrap.Primitive--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--data ClubColorsAttribute = ClubColorsAttribute- { clubColorsAttributeBlueFlag :: Bool- , clubColorsAttributeBlueColor :: Word8- , clubColorsAttributeOrangeFlag :: Bool- , clubColorsAttributeOrangeColor :: Word8- } deriving (Eq, Ord, Show)--getClubColorsAttribute :: BinaryBit.BitGet ClubColorsAttribute-getClubColorsAttribute = do- blueFlag <- BinaryBit.getBool- blueColor <- getWord8Bits- orangeFlag <- BinaryBit.getBool- orangeColor <- getWord8Bits- pure (ClubColorsAttribute blueFlag blueColor orangeFlag orangeColor)--putClubColorsAttribute :: ClubColorsAttribute -> BinaryBit.BitPut ()-putClubColorsAttribute clubColorsAttribute = do- BinaryBit.putBool (clubColorsAttributeBlueFlag clubColorsAttribute)- putWord8Bits (clubColorsAttributeBlueColor clubColorsAttribute)- BinaryBit.putBool (clubColorsAttributeOrangeFlag clubColorsAttribute)- putWord8Bits (clubColorsAttributeOrangeColor clubColorsAttribute)
@@ -1,35 +0,0 @@-module Rattletrap.Attribute.DamageState where--import Rattletrap.Primitive--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--data DamageStateAttribute = DamageStateAttribute- { damageStateAttributeUnknown1 :: Word8- , damageStateAttributeUnknown2 :: Bool- , damageStateAttributeUnknown3 :: Int32- , damageStateAttributeUnknown4 :: Vector- , damageStateAttributeUnknown5 :: Bool- , damageStateAttributeUnknown6 :: Bool- } deriving (Eq, Ord, Show)--getDamageStateAttribute :: BinaryBit.BitGet DamageStateAttribute-getDamageStateAttribute = do- unknown1 <- getWord8Bits- unknown2 <- BinaryBit.getBool- unknown3 <- getInt32Bits- unknown4 <- getVector- unknown5 <- BinaryBit.getBool- unknown6 <- BinaryBit.getBool- pure- (DamageStateAttribute unknown1 unknown2 unknown3 unknown4 unknown5 unknown6)--putDamageStateAttribute :: DamageStateAttribute -> BinaryBit.BitPut ()-putDamageStateAttribute damageStateAttribute = do- putWord8Bits (damageStateAttributeUnknown1 damageStateAttribute)- BinaryBit.putBool (damageStateAttributeUnknown2 damageStateAttribute)- putInt32Bits (damageStateAttributeUnknown3 damageStateAttribute)- putVector (damageStateAttributeUnknown4 damageStateAttribute)- BinaryBit.putBool (damageStateAttributeUnknown5 damageStateAttribute)- BinaryBit.putBool (damageStateAttributeUnknown6 damageStateAttribute)
@@ -1,42 +0,0 @@-module Rattletrap.Attribute.Demolish where--import Rattletrap.Primitive--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--data DemolishAttribute = DemolishAttribute- { demolishAttributeAttackerFlag :: Bool- , demolishAttributeAttackerActorId :: Word32- , demolishAttributeVictimFlag :: Bool- , demolishAttributeVictimActorId :: Word32- , demolishAttributeAttackerVelocity :: Vector- , demolishAttributeVictimVelocity :: Vector- } deriving (Eq, Ord, Show)--getDemolishAttribute :: BinaryBit.BitGet DemolishAttribute-getDemolishAttribute = do- attackerFlag <- BinaryBit.getBool- attackerActorId <- getWord32Bits- victimFlag <- BinaryBit.getBool- victimActorId <- getWord32Bits- attackerVelocity <- getVector- victimVelocity <- getVector- pure- ( DemolishAttribute- attackerFlag- attackerActorId- victimFlag- victimActorId- attackerVelocity- victimVelocity- )--putDemolishAttribute :: DemolishAttribute -> BinaryBit.BitPut ()-putDemolishAttribute demolishAttribute = do- BinaryBit.putBool (demolishAttributeAttackerFlag demolishAttribute)- putWord32Bits (demolishAttributeAttackerActorId demolishAttribute)- BinaryBit.putBool (demolishAttributeVictimFlag demolishAttribute)- putWord32Bits (demolishAttributeVictimActorId demolishAttribute)- putVector (demolishAttributeAttackerVelocity demolishAttribute)- putVector (demolishAttributeVictimVelocity demolishAttribute)
@@ -1,18 +0,0 @@-module Rattletrap.Attribute.Enum where--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit-import qualified Data.Word as Word--newtype EnumAttribute = EnumAttribute- { enumAttributeValue :: Word.Word16- } deriving (Eq, Ord, Show)--getEnumAttribute :: BinaryBit.BitGet EnumAttribute-getEnumAttribute = do- value <- BinaryBit.getWord16be 11- pure (EnumAttribute value)--putEnumAttribute :: EnumAttribute -> BinaryBit.BitPut ()-putEnumAttribute enumAttribute =- BinaryBit.putWord16be 11 (enumAttributeValue enumAttribute)
@@ -1,24 +0,0 @@-module Rattletrap.Attribute.Explosion where--import Rattletrap.Primitive--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--data ExplosionAttribute = ExplosionAttribute- { explosionAttributeActorId :: Int32- , explosionAttributeLocation :: Vector- } deriving (Eq, Ord, Show)--getExplosionAttribute :: BinaryBit.BitGet ExplosionAttribute-getExplosionAttribute = do- False <- BinaryBit.getBool- actorId <- getInt32Bits- location <- getVector- pure (ExplosionAttribute actorId location)--putExplosionAttribute :: ExplosionAttribute -> BinaryBit.BitPut ()-putExplosionAttribute explosionAttribute = do- BinaryBit.putBool False- putInt32Bits (explosionAttributeActorId explosionAttribute)- putVector (explosionAttributeLocation explosionAttribute)
@@ -1,32 +0,0 @@-module Rattletrap.Attribute.ExtendedExplosion where--import Rattletrap.Primitive--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--data ExtendedExplosionAttribute = ExtendedExplosionAttribute- { extendedExplosionAttributeActorId :: Int32- , extendedExplosionAttributeLocation :: Vector- , extendedExplosionAttributeUnknown1 :: Bool- , extendedExplosionAttributeUnknown2 :: Int32- } deriving (Eq, Ord, Show)--getExtendedExplosionAttribute :: BinaryBit.BitGet ExtendedExplosionAttribute-getExtendedExplosionAttribute = do- False <- BinaryBit.getBool- actorId <- getInt32Bits- location <- getVector- unknown1 <- BinaryBit.getBool- unknown2 <- getInt32Bits- pure (ExtendedExplosionAttribute actorId location unknown1 unknown2)--putExtendedExplosionAttribute- :: ExtendedExplosionAttribute -> BinaryBit.BitPut ()-putExtendedExplosionAttribute extendedExplosionAttribute = do- BinaryBit.putBool False- putInt32Bits (extendedExplosionAttributeActorId extendedExplosionAttribute)- putVector (extendedExplosionAttributeLocation extendedExplosionAttribute)- BinaryBit.putBool- (extendedExplosionAttributeUnknown1 extendedExplosionAttribute)- putInt32Bits (extendedExplosionAttributeUnknown2 extendedExplosionAttribute)
@@ -1,22 +0,0 @@-module Rattletrap.Attribute.FlaggedInt where--import Rattletrap.Primitive--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--data FlaggedIntAttribute = FlaggedIntAttribute- { flaggedIntAttributeFlag :: Bool- , flaggedIntAttributeInt :: Int32- } deriving (Eq, Ord, Show)--getFlaggedIntAttribute :: BinaryBit.BitGet FlaggedIntAttribute-getFlaggedIntAttribute = do- flag <- BinaryBit.getBool- int <- getInt32Bits- pure (FlaggedIntAttribute flag int)--putFlaggedIntAttribute :: FlaggedIntAttribute -> BinaryBit.BitPut ()-putFlaggedIntAttribute flaggedIntAttribute = do- BinaryBit.putBool (flaggedIntAttributeFlag flaggedIntAttribute)- putInt32Bits (flaggedIntAttributeInt flaggedIntAttribute)
@@ -1,19 +0,0 @@-module Rattletrap.Attribute.Float where--import Rattletrap.Primitive--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--newtype FloatAttribute = FloatAttribute- { floatAttributeValue :: Float32- } deriving (Eq, Ord, Show)--getFloatAttribute :: BinaryBit.BitGet FloatAttribute-getFloatAttribute = do- value <- getFloat32Bits- pure (FloatAttribute value)--putFloatAttribute :: FloatAttribute -> BinaryBit.BitPut ()-putFloatAttribute floatAttribute =- putFloat32Bits (floatAttributeValue floatAttribute)
@@ -1,22 +0,0 @@-module Rattletrap.Attribute.GameMode where--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit-import qualified Data.Word as Word--data GameModeAttribute = GameModeAttribute- { gameModeAttributeNumBits :: Int- , gameModeAttributeWord :: Word.Word8- } deriving (Eq, Ord, Show)--getGameModeAttribute :: (Int, Int, Int) -> BinaryBit.BitGet GameModeAttribute-getGameModeAttribute version = do- let numBits = if version < (868, 12, 0) then 2 else 8- word <- BinaryBit.getWord8 numBits- pure (GameModeAttribute numBits word)--putGameModeAttribute :: GameModeAttribute -> BinaryBit.BitPut ()-putGameModeAttribute gameModeAttribute = do- let numBits = gameModeAttributeNumBits gameModeAttribute- let word = gameModeAttributeWord gameModeAttribute- BinaryBit.putWord8 numBits word
@@ -1,18 +0,0 @@-module Rattletrap.Attribute.Int where--import Rattletrap.Primitive--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--newtype IntAttribute = IntAttribute- { intAttributeValue :: Int32- } deriving (Eq, Ord, Show)--getIntAttribute :: BinaryBit.BitGet IntAttribute-getIntAttribute = do- value <- getInt32Bits- pure (IntAttribute value)--putIntAttribute :: IntAttribute -> BinaryBit.BitPut ()-putIntAttribute intAttribute = putInt32Bits (intAttributeValue intAttribute)
@@ -1,83 +0,0 @@-module Rattletrap.Attribute.Loadout where--import Rattletrap.Primitive--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--data LoadoutAttribute = LoadoutAttribute- { loadoutAttributeVersion :: Word8- , loadoutAttributeBody :: Word32- , loadoutAttributeDecal :: Word32- , loadoutAttributeWheels :: Word32- , loadoutAttributeRocketTrail :: Word32- -- ^ Now known as "rocket boost".- , loadoutAttributeAntenna :: Word32- , loadoutAttributeTopper :: Word32- , loadoutAttributeUnknown1 :: Word32- , loadoutAttributeUnknown2 :: Maybe Word32- , loadoutAttributeEngineAudio :: Maybe Word32- , loadoutAttributeTrail :: Maybe Word32- , loadoutAttributeGoalExplosion :: Maybe Word32- , loadoutAttributeBanner :: Maybe Word32- } deriving (Eq, Ord, Show)--getLoadoutAttribute :: BinaryBit.BitGet LoadoutAttribute-getLoadoutAttribute = do- version <- getWord8Bits- body <- getWord32Bits- decal <- getWord32Bits- wheels <- getWord32Bits- rocketTrail <- getWord32Bits- antenna <- getWord32Bits- topper <- getWord32Bits- unknown1 <- getWord32Bits- unknown2 <- getOptional (version > Word8 10) getWord32Bits- engineAudio <- getOptional (version >= Word8 16) getWord32Bits- trail <- getOptional (version >= Word8 16) getWord32Bits- goalExplosion <- getOptional (version >= Word8 16) getWord32Bits- banner <- getOptional (version >= Word8 17) getWord32Bits- pure- ( LoadoutAttribute- version- body- decal- wheels- rocketTrail- antenna- topper- unknown1- unknown2- engineAudio- trail- goalExplosion- banner- )--getOptional :: Bool -> BinaryBit.BitGet a -> BinaryBit.BitGet (Maybe a)-getOptional p f = if p- then do- x <- f- pure (Just x)- else pure Nothing--putLoadoutAttribute :: LoadoutAttribute -> BinaryBit.BitPut ()-putLoadoutAttribute loadoutAttribute = do- putWord8Bits (loadoutAttributeVersion loadoutAttribute)- putWord32Bits (loadoutAttributeBody loadoutAttribute)- putWord32Bits (loadoutAttributeDecal loadoutAttribute)- putWord32Bits (loadoutAttributeWheels loadoutAttribute)- putWord32Bits (loadoutAttributeRocketTrail loadoutAttribute)- putWord32Bits (loadoutAttributeAntenna loadoutAttribute)- putWord32Bits (loadoutAttributeTopper loadoutAttribute)- putWord32Bits (loadoutAttributeUnknown1 loadoutAttribute)- putOptional (loadoutAttributeUnknown2 loadoutAttribute) putWord32Bits- putOptional (loadoutAttributeEngineAudio loadoutAttribute) putWord32Bits- putOptional (loadoutAttributeTrail loadoutAttribute) putWord32Bits- putOptional (loadoutAttributeGoalExplosion loadoutAttribute) putWord32Bits- putOptional (loadoutAttributeBanner loadoutAttribute) putWord32Bits--putOptional :: Maybe a -> (a -> BinaryBit.BitPut ()) -> BinaryBit.BitPut ()-putOptional m f = case m of- Just x -> f x- Nothing -> pure ()
@@ -1,30 +0,0 @@-module Rattletrap.Attribute.LoadoutOnline where--import Rattletrap.Attribute.Product-import Rattletrap.Primitive--import qualified Control.Monad as Monad-import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit-import qualified Data.Map as Map--newtype LoadoutOnlineAttribute = LoadoutOnlineAttribute- { loadoutAttributeValue :: [[ProductAttribute]]- } deriving (Eq, Ord, Show)--getLoadoutOnlineAttribute- :: (Int, Int, Int)- -> Map.Map Word32 Text- -> BinaryBit.BitGet LoadoutOnlineAttribute-getLoadoutOnlineAttribute version objectMap = do- size <- getWord8Bits- values <- Monad.replicateM- (fromIntegral (word8Value size))- (getProductAttributes version objectMap)- pure (LoadoutOnlineAttribute values)--putLoadoutOnlineAttribute :: LoadoutOnlineAttribute -> BinaryBit.BitPut ()-putLoadoutOnlineAttribute loadoutAttribute = do- let attributes = loadoutAttributeValue loadoutAttribute- putWord8Bits (Word8 (fromIntegral (length attributes)))- mapM_ putProductAttributes attributes
@@ -1,22 +0,0 @@-module Rattletrap.Attribute.Loadouts where--import Rattletrap.Attribute.Loadout--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--data LoadoutsAttribute = LoadoutsAttribute- { loadoutsAttributeBlue :: LoadoutAttribute- , loadoutsAttributeOrange :: LoadoutAttribute- } deriving (Eq, Ord, Show)--getLoadoutsAttribute :: BinaryBit.BitGet LoadoutsAttribute-getLoadoutsAttribute = do- blue <- getLoadoutAttribute- orange <- getLoadoutAttribute- pure (LoadoutsAttribute blue orange)--putLoadoutsAttribute :: LoadoutsAttribute -> BinaryBit.BitPut ()-putLoadoutsAttribute loadoutsAttribute = do- putLoadoutAttribute (loadoutsAttributeBlue loadoutsAttribute)- putLoadoutAttribute (loadoutsAttributeOrange loadoutsAttribute)
@@ -1,35 +0,0 @@-module Rattletrap.Attribute.LoadoutsOnline where--import Rattletrap.Attribute.LoadoutOnline-import Rattletrap.Primitive--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit-import qualified Data.Map as Map--data LoadoutsOnlineAttribute = LoadoutsOnlineAttribute- { loadoutsOnlineAttributeBlue :: LoadoutOnlineAttribute- , loadoutsOnlineAttributeOrange :: LoadoutOnlineAttribute- , loadoutsOnlineAttributeUnknown1 :: Bool- , loadoutsOnlineAttributeUnknown2 :: Bool- } deriving (Eq, Ord, Show)--getLoadoutsOnlineAttribute- :: (Int, Int, Int)- -> Map.Map Word32 Text- -> BinaryBit.BitGet LoadoutsOnlineAttribute-getLoadoutsOnlineAttribute version objectMap = do- blueLoadout <- getLoadoutOnlineAttribute version objectMap- orangeLoadout <- getLoadoutOnlineAttribute version objectMap- unknown1 <- BinaryBit.getBool- unknown2 <- BinaryBit.getBool- pure (LoadoutsOnlineAttribute blueLoadout orangeLoadout unknown1 unknown2)--putLoadoutsOnlineAttribute :: LoadoutsOnlineAttribute -> BinaryBit.BitPut ()-putLoadoutsOnlineAttribute loadoutsOnlineAttribute = do- putLoadoutOnlineAttribute- (loadoutsOnlineAttributeBlue loadoutsOnlineAttribute)- putLoadoutOnlineAttribute- (loadoutsOnlineAttributeOrange loadoutsOnlineAttribute)- BinaryBit.putBool (loadoutsOnlineAttributeUnknown1 loadoutsOnlineAttribute)- BinaryBit.putBool (loadoutsOnlineAttributeUnknown2 loadoutsOnlineAttribute)
@@ -1,19 +0,0 @@-module Rattletrap.Attribute.Location where--import Rattletrap.Primitive--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--newtype LocationAttribute = LocationAttribute- { locationAttributeValue :: Vector- } deriving (Eq, Ord, Show)--getLocationAttribute :: BinaryBit.BitGet LocationAttribute-getLocationAttribute = do- value <- getVector- pure (LocationAttribute value)--putLocationAttribute :: LocationAttribute -> BinaryBit.BitPut ()-putLocationAttribute locationAttribute =- putVector (locationAttributeValue locationAttribute)
@@ -1,25 +0,0 @@-module Rattletrap.Attribute.MusicStinger where--import Rattletrap.Primitive--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--data MusicStingerAttribute = MusicStingerAttribute- { musicStingerAttributeFlag :: Bool- , musicStingerAttributeCue :: Word32- , musicStingerAttributeTrigger :: Word8- } deriving (Eq, Ord, Show)--getMusicStingerAttribute :: BinaryBit.BitGet MusicStingerAttribute-getMusicStingerAttribute = do- flag <- BinaryBit.getBool- cue <- getWord32Bits- trigger <- getWord8Bits- pure (MusicStingerAttribute flag cue trigger)--putMusicStingerAttribute :: MusicStingerAttribute -> BinaryBit.BitPut ()-putMusicStingerAttribute musicStingerAttribute = do- BinaryBit.putBool (musicStingerAttributeFlag musicStingerAttribute)- putWord32Bits (musicStingerAttributeCue musicStingerAttribute)- putWord8Bits (musicStingerAttributeTrigger musicStingerAttribute)
@@ -1,32 +0,0 @@-module Rattletrap.Attribute.PartyLeader where--import Rattletrap.Primitive-import Rattletrap.RemoteId--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--data PartyLeaderAttribute = PartyLeaderAttribute- { partyLeaderAttributeSystemId :: Word8- , partyLeaderAttributeId :: Maybe (RemoteId, Word8)- } deriving (Eq, Ord, Show)--getPartyLeaderAttribute :: (Int, Int, Int) -> BinaryBit.BitGet PartyLeaderAttribute-getPartyLeaderAttribute version = do- systemId <- getWord8Bits- maybeRemoteAndLocalId <- if systemId == Word8 0- then pure Nothing- else do- remoteId <- getRemoteId version systemId- localId <- getWord8Bits- pure (Just (remoteId, localId))- pure (PartyLeaderAttribute systemId maybeRemoteAndLocalId)--putPartyLeaderAttribute :: PartyLeaderAttribute -> BinaryBit.BitPut ()-putPartyLeaderAttribute partyLeaderAttribute = do- putWord8Bits (partyLeaderAttributeSystemId partyLeaderAttribute)- case partyLeaderAttributeId partyLeaderAttribute of- Nothing -> pure ()- Just (remoteId, localId) -> do- putRemoteId remoteId- putWord8Bits localId
@@ -1,31 +0,0 @@-module Rattletrap.Attribute.Pickup where--import Rattletrap.Primitive--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--data PickupAttribute = PickupAttribute- { pickupAttributeInstigatorId :: Maybe Word32- , pickupAttributePickedUp :: Bool- } deriving (Eq, Ord, Show)--getPickupAttribute :: BinaryBit.BitGet PickupAttribute-getPickupAttribute = do- instigator <- BinaryBit.getBool- maybeInstigatorId <- if instigator- then do- instigatorId <- getWord32Bits- pure (Just instigatorId)- else pure Nothing- pickedUp <- BinaryBit.getBool- pure (PickupAttribute maybeInstigatorId pickedUp)--putPickupAttribute :: PickupAttribute -> BinaryBit.BitPut ()-putPickupAttribute pickupAttribute = do- case pickupAttributeInstigatorId pickupAttribute of- Nothing -> BinaryBit.putBool False- Just instigatorId -> do- BinaryBit.putBool True- putWord32Bits instigatorId- BinaryBit.putBool (pickupAttributePickedUp pickupAttribute)
@@ -1,50 +0,0 @@-module Rattletrap.Attribute.PrivateMatchSettings where--import Rattletrap.Primitive--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--data PrivateMatchSettingsAttribute = PrivateMatchSettingsAttribute- { privateMatchSettingsAttributeMutators :: Text- , privateMatchSettingsAttributeJoinableBy :: Word32- , privateMatchSettingsAttributeMaxPlayers :: Word32- , privateMatchSettingsAttributeGameName :: Text- , privateMatchSettingsAttributePassword :: Text- , privateMatchSettingsAttributeFlag :: Bool- } deriving (Eq, Ord, Show)--getPrivateMatchSettingsAttribute- :: BinaryBit.BitGet PrivateMatchSettingsAttribute-getPrivateMatchSettingsAttribute = do- mutators <- getTextBits- joinableBy <- getWord32Bits- maxPlayers <- getWord32Bits- gameName <- getTextBits- password <- getTextBits- flag <- BinaryBit.getBool- pure- ( PrivateMatchSettingsAttribute- mutators- joinableBy- maxPlayers- gameName- password- flag- )--putPrivateMatchSettingsAttribute- :: PrivateMatchSettingsAttribute -> BinaryBit.BitPut ()-putPrivateMatchSettingsAttribute privateMatchSettingsAttribute = do- putTextBits- (privateMatchSettingsAttributeMutators privateMatchSettingsAttribute)- putWord32Bits- (privateMatchSettingsAttributeJoinableBy privateMatchSettingsAttribute)- putWord32Bits- (privateMatchSettingsAttributeMaxPlayers privateMatchSettingsAttribute)- putTextBits- (privateMatchSettingsAttributeGameName privateMatchSettingsAttribute)- putTextBits- (privateMatchSettingsAttributePassword privateMatchSettingsAttribute)- BinaryBit.putBool- (privateMatchSettingsAttributeFlag privateMatchSettingsAttribute)
@@ -1,87 +0,0 @@-module Rattletrap.Attribute.Product where--import Rattletrap.Primitive--import qualified Control.Monad as Monad-import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit-import qualified Data.Map as Map-import qualified Data.Word as Word--data ProductAttribute = ProductAttribute- { productAttributeUnknown :: Bool- , productAttributeObjectId :: Word32- , productAttributeObjectName :: Maybe Text- -- ^ read-only- , productAttributeValue :: Maybe (Either CompressedWord Word.Word32)- } deriving (Eq, Ord, Show)--getProductAttributes- :: (Int, Int, Int) -> Map.Map Word32 Text -> BinaryBit.BitGet [ProductAttribute]-getProductAttributes version objectMap = do- size <- getWord8Bits- Monad.replicateM- (fromIntegral (word8Value size))- (getProductAttribute version objectMap)--getProductAttribute- :: (Int, Int, Int) -> Map.Map Word32 Text -> BinaryBit.BitGet ProductAttribute-getProductAttribute version objectMap = do- flag <- BinaryBit.getBool- objectId <- getWord32Bits- let objectName = Map.lookup objectId objectMap- value <- case objectName of- Just name -> case textToString name of- "TAGame.ProductAttribute_Painted_TA" -> if version >= (868, 18, 0)- then do- x <- BinaryBit.getWord32be 31- pure (Just (Right x))- else do- x <- getCompressedWord 13- pure (Just (Left x))- "TAGame.ProductAttribute_UserColor_TA" -> do- hasValue <- BinaryBit.getBool- value <- if hasValue- then do- x <- BinaryBit.getWord32be 31- pure (Just (Right x))- else pure Nothing- pure value- _ ->- fail- ( "unknown object name "- ++ show objectName- ++ " for ID "- ++ show objectId- )- Nothing -> fail ("missing object name for ID " ++ show objectId)- pure (ProductAttribute flag objectId objectName value)--putProductAttributes :: [ProductAttribute] -> BinaryBit.BitPut ()-putProductAttributes attributes = do- putWord8Bits (Word8 (fromIntegral (length attributes)))- mapM_ putProductAttribute attributes--putProductAttribute :: ProductAttribute -> BinaryBit.BitPut ()-putProductAttribute attribute = do- BinaryBit.putBool (productAttributeUnknown attribute)- putWord32Bits (productAttributeObjectId attribute)- case productAttributeObjectName attribute of- Just name -> case textToString name of- "TAGame.ProductAttribute_Painted_TA" ->- case productAttributeValue attribute of- Nothing -> pure ()- Just (Left x) -> putCompressedWord x- Just (Right x) -> BinaryBit.putWord32be 31 x- "TAGame.ProductAttribute_UserColor_TA" ->- case productAttributeValue attribute of- Nothing -> BinaryBit.putBool False- Just value -> do- BinaryBit.putBool True- case value of- Left x -> putCompressedWord x- Right x -> BinaryBit.putWord32be 31 x- _ ->- fail ("unknown object name for product attribute " ++ show attribute)- Nothing ->- fail ("missing object name for product attribute " ++ show attribute)
@@ -1,19 +0,0 @@-module Rattletrap.Attribute.QWord where--import Rattletrap.Primitive--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--newtype QWordAttribute = QWordAttribute- { qWordAttributeValue :: Word64- } deriving (Eq, Ord, Show)--getQWordAttribute :: BinaryBit.BitGet QWordAttribute-getQWordAttribute = do- value <- getWord64Bits- pure (QWordAttribute value)--putQWordAttribute :: QWordAttribute -> BinaryBit.BitPut ()-putQWordAttribute qWordAttribute =- putWord64Bits (qWordAttributeValue qWordAttribute)
@@ -1,48 +0,0 @@-module Rattletrap.Attribute.Reservation where--import Rattletrap.Attribute.UniqueId-import Rattletrap.Primitive--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit-import qualified Data.Word as Word--data ReservationAttribute = ReservationAttribute- { reservationAttributeNumber :: CompressedWord- , reservationAttributeUniqueId :: UniqueIdAttribute- , reservationAttributeName :: Maybe Text- , reservationAttributeUnknown1 :: Bool- , reservationAttributeUnknown2 :: Bool- , reservationAttributeUnknown3 :: Maybe Word.Word8- } deriving (Eq, Ord, Show)--getReservationAttribute :: (Int, Int, Int) -> BinaryBit.BitGet ReservationAttribute-getReservationAttribute version = do- number <- getCompressedWord 7- uniqueId <- getUniqueIdAttribute version- name <- if uniqueIdAttributeSystemId uniqueId == Word8 0- then pure Nothing- else do- name <- getTextBits- pure (Just name)- a <- BinaryBit.getBool- b <- BinaryBit.getBool- mc <- if version < (868, 12, 0)- then pure Nothing- else do- c <- BinaryBit.getWord8 6- pure (Just c)- pure (ReservationAttribute number uniqueId name a b mc)--putReservationAttribute :: ReservationAttribute -> BinaryBit.BitPut ()-putReservationAttribute reservationAttribute = do- putCompressedWord (reservationAttributeNumber reservationAttribute)- putUniqueIdAttribute (reservationAttributeUniqueId reservationAttribute)- case reservationAttributeName reservationAttribute of- Nothing -> pure ()- Just name -> putTextBits name- BinaryBit.putBool (reservationAttributeUnknown1 reservationAttribute)- BinaryBit.putBool (reservationAttributeUnknown2 reservationAttribute)- case reservationAttributeUnknown3 reservationAttribute of- Nothing -> pure ()- Just c -> BinaryBit.putWord8 6 c
@@ -1,51 +0,0 @@-module Rattletrap.Attribute.RigidBodyState where--import Rattletrap.Primitive--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--data RigidBodyStateAttribute = RigidBodyStateAttribute- { rigidBodyStateAttributeSleeping :: Bool- , rigidBodyStateAttributeLocation :: Vector- , rigidBodyStateAttributeRotation :: CompressedWordVector- , rigidBodyStateAttributeLinearVelocity :: Maybe Vector- , rigidBodyStateAttributeAngularVelocity :: Maybe Vector- } deriving (Eq, Ord, Show)--getRigidBodyStateAttribute :: BinaryBit.BitGet RigidBodyStateAttribute-getRigidBodyStateAttribute = do- sleeping <- BinaryBit.getBool- location <- getVector- rotation <- getCompressedWordVector- linearVelocity <- if sleeping- then pure Nothing- else do- linearVelocity <- getVector- pure (Just linearVelocity)- angularVelocity <- if sleeping- then pure Nothing- else do- angularVelocity <- getVector- pure (Just angularVelocity)- pure- ( RigidBodyStateAttribute- sleeping- location- rotation- linearVelocity- angularVelocity- )--putRigidBodyStateAttribute :: RigidBodyStateAttribute -> BinaryBit.BitPut ()-putRigidBodyStateAttribute rigidBodyStateAttribute = do- BinaryBit.putBool (rigidBodyStateAttributeSleeping rigidBodyStateAttribute)- putVector (rigidBodyStateAttributeLocation rigidBodyStateAttribute)- putCompressedWordVector- (rigidBodyStateAttributeRotation rigidBodyStateAttribute)- case rigidBodyStateAttributeLinearVelocity rigidBodyStateAttribute of- Nothing -> pure ()- Just linearVelocity -> putVector linearVelocity- case rigidBodyStateAttributeAngularVelocity rigidBodyStateAttribute of- Nothing -> pure ()- Just angularVelocity -> putVector angularVelocity
@@ -1,19 +0,0 @@-module Rattletrap.Attribute.String where--import Rattletrap.Primitive--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--newtype StringAttribute = StringAttribute- { stringAttributeValue :: Text- } deriving (Eq, Ord, Show)--getStringAttribute :: BinaryBit.BitGet StringAttribute-getStringAttribute = do- value <- getTextBits- pure (StringAttribute value)--putStringAttribute :: StringAttribute -> BinaryBit.BitPut ()-putStringAttribute stringAttribute =- putTextBits (stringAttributeValue stringAttribute)
@@ -1,38 +0,0 @@-module Rattletrap.Attribute.TeamPaint where--import Rattletrap.Primitive--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--data TeamPaintAttribute = TeamPaintAttribute- { teamPaintAttributeTeam :: Word8- , teamPaintAttributePrimaryColor :: Word8- , teamPaintAttributeAccentColor :: Word8- , teamPaintAttributePrimaryFinish :: Word32- , teamPaintAttributeAccentFinish :: Word32- } deriving (Eq, Ord, Show)--getTeamPaintAttribute :: BinaryBit.BitGet TeamPaintAttribute-getTeamPaintAttribute = do- team <- getWord8Bits- primaryColor <- getWord8Bits- accentColor <- getWord8Bits- primaryFinish <- getWord32Bits- accentFinish <- getWord32Bits- pure- ( TeamPaintAttribute- team- primaryColor- accentColor- primaryFinish- accentFinish- )--putTeamPaintAttribute :: TeamPaintAttribute -> BinaryBit.BitPut ()-putTeamPaintAttribute teamPaintAttribute = do- putWord8Bits (teamPaintAttributeTeam teamPaintAttribute)- putWord8Bits (teamPaintAttributePrimaryColor teamPaintAttribute)- putWord8Bits (teamPaintAttributeAccentColor teamPaintAttribute)- putWord32Bits (teamPaintAttributePrimaryFinish teamPaintAttribute)- putWord32Bits (teamPaintAttributeAccentFinish teamPaintAttribute)
@@ -1,26 +0,0 @@-module Rattletrap.Attribute.UniqueId where--import Rattletrap.Primitive-import Rattletrap.RemoteId--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--data UniqueIdAttribute = UniqueIdAttribute- { uniqueIdAttributeSystemId :: Word8- , uniqueIdAttributeRemoteId :: RemoteId- , uniqueIdAttributeLocalId :: Word8- } deriving (Eq, Ord, Show)--getUniqueIdAttribute :: (Int, Int, Int) -> BinaryBit.BitGet UniqueIdAttribute-getUniqueIdAttribute version = do- systemId <- getWord8Bits- remoteId <- getRemoteId version systemId- localId <- getWord8Bits- pure (UniqueIdAttribute systemId remoteId localId)--putUniqueIdAttribute :: UniqueIdAttribute -> BinaryBit.BitPut ()-putUniqueIdAttribute uniqueIdAttribute = do- putWord8Bits (uniqueIdAttributeSystemId uniqueIdAttribute)- putRemoteId (uniqueIdAttributeRemoteId uniqueIdAttribute)- putWord8Bits (uniqueIdAttributeLocalId uniqueIdAttribute)
@@ -1,31 +0,0 @@-module Rattletrap.Attribute.WeldedInfo where--import Rattletrap.Primitive--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--data WeldedInfoAttribute = WeldedInfoAttribute- { weldedInfoAttributeActive :: Bool- , weldedInfoAttributeActorId :: Int32- , weldedInfoAttributeOffset :: Vector- , weldedInfoAttributeMass :: Float32- , weldedInfoAttributeRotation :: Int8Vector- } deriving (Eq, Ord, Show)--getWeldedInfoAttribute :: BinaryBit.BitGet WeldedInfoAttribute-getWeldedInfoAttribute = do- active <- BinaryBit.getBool- actorId <- getInt32Bits- offset <- getVector- mass <- getFloat32Bits- rotation <- getInt8Vector- pure (WeldedInfoAttribute active actorId offset mass rotation)--putWeldedInfoAttribute :: WeldedInfoAttribute -> BinaryBit.BitPut ()-putWeldedInfoAttribute weldedInfoAttribute = do- BinaryBit.putBool (weldedInfoAttributeActive weldedInfoAttribute)- putInt32Bits (weldedInfoAttributeActorId weldedInfoAttribute)- putVector (weldedInfoAttributeOffset weldedInfoAttribute)- putFloat32Bits (weldedInfoAttributeMass weldedInfoAttribute)- putInt8Vector (weldedInfoAttributeRotation weldedInfoAttribute)
@@ -1,21 +0,0 @@-module Rattletrap.AttributeMapping where--import Rattletrap.Primitive--import qualified Data.Binary as Binary--data AttributeMapping = AttributeMapping- { attributeMappingObjectId :: Word32- , attributeMappingStreamId :: Word32- } deriving (Eq, Ord, Show)--getAttributeMapping :: Binary.Get AttributeMapping-getAttributeMapping = do- objectId <- getWord32- streamId <- getWord32- pure (AttributeMapping objectId streamId)--putAttributeMapping :: AttributeMapping -> Binary.Put-putAttributeMapping attributeMapping = do- putWord32 (attributeMappingObjectId attributeMapping)- putWord32 (attributeMappingStreamId attributeMapping)
@@ -1,34 +0,0 @@-module Rattletrap.AttributeType where--data AttributeType- = AppliedDamageAttributeType- | BooleanAttributeType- | ByteAttributeType- | CamSettingsAttributeType- | ClubColorsAttributeType- | DamageStateAttributeType- | DemolishAttributeType- | EnumAttributeType- | ExplosionAttributeType- | ExtendedExplosionAttributeType- | FlaggedIntAttributeType- | FloatAttributeType- | GameModeAttributeType- | IntAttributeType- | LoadoutAttributeType- | LoadoutOnlineAttributeType- | LoadoutsAttributeType- | LoadoutsOnlineAttributeType- | LocationAttributeType- | MusicStingerAttributeType- | PartyLeaderAttributeType- | PickupAttributeType- | PrivateMatchSettingsAttributeType- | QWordAttributeType- | ReservationAttributeType- | RigidBodyStateAttributeType- | StringAttributeType- | TeamPaintAttributeType- | UniqueIdAttributeType- | WeldedInfoAttributeType- deriving (Eq, Ord, Show)
@@ -1,242 +0,0 @@-module Rattletrap.AttributeValue- ( module Rattletrap.AttributeValue- , module Rattletrap.Attribute.AppliedDamage- , module Rattletrap.Attribute.Boolean- , module Rattletrap.Attribute.Byte- , module Rattletrap.Attribute.CamSettings- , module Rattletrap.Attribute.ClubColors- , module Rattletrap.Attribute.DamageState- , module Rattletrap.Attribute.Demolish- , module Rattletrap.Attribute.Enum- , module Rattletrap.Attribute.Explosion- , module Rattletrap.Attribute.ExtendedExplosion- , module Rattletrap.Attribute.FlaggedInt- , module Rattletrap.Attribute.Float- , module Rattletrap.Attribute.GameMode- , module Rattletrap.Attribute.Int- , module Rattletrap.Attribute.Loadout- , module Rattletrap.Attribute.LoadoutOnline- , module Rattletrap.Attribute.Loadouts- , module Rattletrap.Attribute.LoadoutsOnline- , module Rattletrap.Attribute.Location- , module Rattletrap.Attribute.MusicStinger- , module Rattletrap.Attribute.PartyLeader- , module Rattletrap.Attribute.Pickup- , module Rattletrap.Attribute.PrivateMatchSettings- , module Rattletrap.Attribute.Product- , module Rattletrap.Attribute.QWord- , module Rattletrap.Attribute.Reservation- , module Rattletrap.Attribute.RigidBodyState- , module Rattletrap.Attribute.String- , module Rattletrap.Attribute.TeamPaint- , module Rattletrap.Attribute.UniqueId- , module Rattletrap.Attribute.WeldedInfo- ) where--import Rattletrap.Attribute.AppliedDamage-import Rattletrap.Attribute.Boolean-import Rattletrap.Attribute.Byte-import Rattletrap.Attribute.CamSettings-import Rattletrap.Attribute.ClubColors-import Rattletrap.Attribute.DamageState-import Rattletrap.Attribute.Demolish-import Rattletrap.Attribute.Enum-import Rattletrap.Attribute.Explosion-import Rattletrap.Attribute.ExtendedExplosion-import Rattletrap.Attribute.FlaggedInt-import Rattletrap.Attribute.Float-import Rattletrap.Attribute.GameMode-import Rattletrap.Attribute.Int-import Rattletrap.Attribute.Loadout-import Rattletrap.Attribute.LoadoutOnline-import Rattletrap.Attribute.Loadouts-import Rattletrap.Attribute.LoadoutsOnline-import Rattletrap.Attribute.Location-import Rattletrap.Attribute.MusicStinger-import Rattletrap.Attribute.PartyLeader-import Rattletrap.Attribute.Pickup-import Rattletrap.Attribute.PrivateMatchSettings-import Rattletrap.Attribute.Product-import Rattletrap.Attribute.QWord-import Rattletrap.Attribute.Reservation-import Rattletrap.Attribute.RigidBodyState-import Rattletrap.Attribute.String-import Rattletrap.Attribute.TeamPaint-import Rattletrap.Attribute.UniqueId-import Rattletrap.Attribute.WeldedInfo-import Rattletrap.AttributeType-import Rattletrap.Data-import Rattletrap.Primitive--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit-import qualified Data.Map.Strict as Map--data AttributeValue- = AppliedDamageAttributeValue AppliedDamageAttribute- | BooleanAttributeValue BooleanAttribute- | ByteAttributeValue ByteAttribute- | CamSettingsAttributeValue CamSettingsAttribute- | ClubColorsAttributeValue ClubColorsAttribute- | DamageStateAttributeValue DamageStateAttribute- | DemolishAttributeValue DemolishAttribute- | EnumAttributeValue EnumAttribute- | ExplosionAttributeValue ExplosionAttribute- | ExtendedExplosionAttributeValue ExtendedExplosionAttribute- | FlaggedIntAttributeValue FlaggedIntAttribute- | FloatAttributeValue FloatAttribute- | GameModeAttributeValue GameModeAttribute- | IntAttributeValue IntAttribute- | LoadoutAttributeValue LoadoutAttribute- | LoadoutOnlineAttributeValue LoadoutOnlineAttribute- | LoadoutsAttributeValue LoadoutsAttribute- | LoadoutsOnlineAttributeValue LoadoutsOnlineAttribute- | LocationAttributeValue LocationAttribute- | MusicStingerAttributeValue MusicStingerAttribute- | PartyLeaderAttributeValue PartyLeaderAttribute- | PickupAttributeValue PickupAttribute- | PrivateMatchSettingsAttributeValue PrivateMatchSettingsAttribute- | QWordAttributeValue QWordAttribute- | ReservationAttributeValue ReservationAttribute- | RigidBodyStateAttributeValue RigidBodyStateAttribute- | StringAttributeValue StringAttribute- | TeamPaintAttributeValue TeamPaintAttribute- | UniqueIdAttributeValue UniqueIdAttribute- | WeldedInfoAttributeValue WeldedInfoAttribute- deriving (Eq, Ord, Show)--getAttributeValue- :: (Int, Int, Int)- -> Map.Map Word32 Text- -> Text- -> BinaryBit.BitGet AttributeValue-getAttributeValue version objectMap name =- case Map.lookup name attributeTypes of- Just constructor -> case constructor of- AppliedDamageAttributeType -> do- x <- getAppliedDamageAttribute- pure (AppliedDamageAttributeValue x)- BooleanAttributeType -> do- x <- getBooleanAttribute- pure (BooleanAttributeValue x)- ByteAttributeType -> do- x <- getByteAttribute- pure (ByteAttributeValue x)- CamSettingsAttributeType -> do- x <- getCamSettingsAttribute version- pure (CamSettingsAttributeValue x)- ClubColorsAttributeType -> do- x <- getClubColorsAttribute- pure (ClubColorsAttributeValue x)- DamageStateAttributeType -> do- x <- getDamageStateAttribute- pure (DamageStateAttributeValue x)- DemolishAttributeType -> do- x <- getDemolishAttribute- pure (DemolishAttributeValue x)- EnumAttributeType -> do- x <- getEnumAttribute- pure (EnumAttributeValue x)- ExplosionAttributeType -> do- x <- getExplosionAttribute- pure (ExplosionAttributeValue x)- ExtendedExplosionAttributeType -> do- x <- getExtendedExplosionAttribute- pure (ExtendedExplosionAttributeValue x)- FlaggedIntAttributeType -> do- x <- getFlaggedIntAttribute- pure (FlaggedIntAttributeValue x)- FloatAttributeType -> do- x <- getFloatAttribute- pure (FloatAttributeValue x)- GameModeAttributeType -> do- x <- getGameModeAttribute version- pure (GameModeAttributeValue x)- IntAttributeType -> do- x <- getIntAttribute- pure (IntAttributeValue x)- LoadoutAttributeType -> do- x <- getLoadoutAttribute- pure (LoadoutAttributeValue x)- LoadoutOnlineAttributeType -> do- x <- getLoadoutOnlineAttribute version objectMap- pure (LoadoutOnlineAttributeValue x)- LoadoutsAttributeType -> do- x <- getLoadoutsAttribute- pure (LoadoutsAttributeValue x)- LoadoutsOnlineAttributeType -> do- x <- getLoadoutsOnlineAttribute version objectMap- pure (LoadoutsOnlineAttributeValue x)- LocationAttributeType -> do- x <- getLocationAttribute- pure (LocationAttributeValue x)- MusicStingerAttributeType -> do- x <- getMusicStingerAttribute- pure (MusicStingerAttributeValue x)- PartyLeaderAttributeType -> do- x <- getPartyLeaderAttribute version- pure (PartyLeaderAttributeValue x)- PickupAttributeType -> do- x <- getPickupAttribute- pure (PickupAttributeValue x)- PrivateMatchSettingsAttributeType -> do- x <- getPrivateMatchSettingsAttribute- pure (PrivateMatchSettingsAttributeValue x)- QWordAttributeType -> do- x <- getQWordAttribute- pure (QWordAttributeValue x)- ReservationAttributeType -> do- x <- getReservationAttribute version- pure (ReservationAttributeValue x)- RigidBodyStateAttributeType -> do- x <- getRigidBodyStateAttribute- pure (RigidBodyStateAttributeValue x)- StringAttributeType -> do- x <- getStringAttribute- pure (StringAttributeValue x)- TeamPaintAttributeType -> do- x <- getTeamPaintAttribute- pure (TeamPaintAttributeValue x)- UniqueIdAttributeType -> do- x <- getUniqueIdAttribute version- pure (UniqueIdAttributeValue x)- WeldedInfoAttributeType -> do- x <- getWeldedInfoAttribute- pure (WeldedInfoAttributeValue x)- Nothing -> fail ("don't know how to get attribute value " ++ show name)--attributeTypes :: Map.Map Text AttributeType-attributeTypes = Map.mapKeys stringToText (Map.fromList rawAttributeTypes)--putAttributeValue :: AttributeValue -> BinaryBit.BitPut ()-putAttributeValue value = case value of- AppliedDamageAttributeValue x -> putAppliedDamageAttribute x- BooleanAttributeValue x -> putBooleanAttribute x- ByteAttributeValue x -> putByteAttribute x- CamSettingsAttributeValue x -> putCamSettingsAttribute x- ClubColorsAttributeValue x -> putClubColorsAttribute x- DamageStateAttributeValue x -> putDamageStateAttribute x- DemolishAttributeValue x -> putDemolishAttribute x- EnumAttributeValue x -> putEnumAttribute x- ExplosionAttributeValue x -> putExplosionAttribute x- ExtendedExplosionAttributeValue x -> putExtendedExplosionAttribute x- FlaggedIntAttributeValue x -> putFlaggedIntAttribute x- FloatAttributeValue x -> putFloatAttribute x- GameModeAttributeValue x -> putGameModeAttribute x- IntAttributeValue x -> putIntAttribute x- LoadoutAttributeValue x -> putLoadoutAttribute x- LoadoutOnlineAttributeValue x -> putLoadoutOnlineAttribute x- LoadoutsAttributeValue x -> putLoadoutsAttribute x- LoadoutsOnlineAttributeValue x -> putLoadoutsOnlineAttribute x- LocationAttributeValue x -> putLocationAttribute x- MusicStingerAttributeValue x -> putMusicStingerAttribute x- PartyLeaderAttributeValue x -> putPartyLeaderAttribute x- PickupAttributeValue x -> putPickupAttribute x- PrivateMatchSettingsAttributeValue x -> putPrivateMatchSettingsAttribute x- QWordAttributeValue x -> putQWordAttribute x- ReservationAttributeValue x -> putReservationAttribute x- RigidBodyStateAttributeValue x -> putRigidBodyStateAttribute x- StringAttributeValue x -> putStringAttribute x- TeamPaintAttributeValue x -> putTeamPaintAttribute x- UniqueIdAttributeValue x -> putUniqueIdAttribute x- WeldedInfoAttributeValue x -> putWeldedInfoAttribute x
@@ -1,28 +0,0 @@-module Rattletrap.Cache where--import Rattletrap.AttributeMapping-import Rattletrap.Primitive--import qualified Data.Binary as Binary--data Cache = Cache- { cacheClassId :: Word32- , cacheParentCacheId :: Word32- , cacheCacheId :: Word32- , cacheAttributeMappings :: List AttributeMapping- } deriving (Eq, Ord, Show)--getCache :: Binary.Get Cache-getCache = do- classId <- getWord32- parentCacheId <- getWord32- cacheId <- getWord32- attributeMappings <- getList getAttributeMapping- pure (Cache classId parentCacheId cacheId attributeMappings)--putCache :: Cache -> Binary.Put-putCache cache = do- putWord32 (cacheClassId cache)- putWord32 (cacheParentCacheId cache)- putWord32 (cacheCacheId cache)- putList putAttributeMapping (cacheAttributeMappings cache)
@@ -1,300 +0,0 @@-module Rattletrap.ClassAttributeMap where--import Rattletrap.ActorMap-import Rattletrap.AttributeMapping-import Rattletrap.Cache-import Rattletrap.ClassMapping-import Rattletrap.Data-import Rattletrap.Primitive--import qualified Data.Bimap as Bimap-import qualified Data.IntMap.Strict as IntMap-import qualified Data.List as List-import qualified Data.Map.Strict as Map-import qualified Data.Maybe as Maybe-import qualified Data.Set as Set-import qualified Data.Text as Text---- | This data structure holds all the information about classes, objects, and--- attributes in the replay. The class hierarchy is not fixed; it is encoded--- in the 'Rattletrap.Content.Content'. Similarly, the attributes that belong--- to each class are not fixed either. Converting the raw data into a usable--- structure is tedious; see 'makeClassAttributeMap'.-data ClassAttributeMap = ClassAttributeMap- { classAttributeMapObjectMap :: Map.Map Word32 Text- -- ^ A map from object IDs to their names.- , classAttributeMapObjectClassMap :: Map.Map Word32 Word32- -- ^ A map from object IDs to their class IDs.- , classAttributeMapValue :: Map.Map Word32 (Map.Map Word32 Word32)- -- ^ A map from class IDs to a map from attribute stream IDs to attribute- -- IDs.- , classAttributeMapNameMap :: IntMap.IntMap Text- } deriving (Eq, Ord, Show)---- | Makes a 'ClassAttributeMap' given the necessary fields from the--- 'Rattletrap.Content.Content'.-makeClassAttributeMap- :: List Text- -- ^ From 'Rattletrap.Content.contentObjects'.- -> List ClassMapping- -- ^ From 'Rattletrap.Content.contentClassMappings'.- -> List Cache- -- ^ From 'Rattletrap.Content.contentCaches'.- -> List Text- -- ^ From 'Rattletrap.Content.contentNames'.- -> ClassAttributeMap-makeClassAttributeMap objects classMappings caches names =- let- objectMap = makeObjectMap objects- classMap = makeClassMap classMappings- objectClassMap = makeObjectClassMap objectMap classMap- classCache = makeClassCache classMap caches- attributeMap = makeAttributeMap caches- classIds = map (\(_, classId, _, _) -> classId) classCache- parentMap = makeParentMap classCache- value = Map.fromList- ( map- ( \classId ->- let- ownAttributes =- Maybe.fromMaybe Map.empty (Map.lookup classId attributeMap)- parentsAttributes = case Map.lookup classId parentMap of- Nothing -> []- Just parentClassIds -> map- ( \parentClassId -> Maybe.fromMaybe- Map.empty- (Map.lookup parentClassId attributeMap)- )- parentClassIds- attributes = ownAttributes : parentsAttributes- in (classId, Map.fromList (concatMap Map.toList attributes))- )- classIds- )- nameMap = makeNameMap names- in ClassAttributeMap objectMap objectClassMap value nameMap--makeNameMap :: List Text -> IntMap.IntMap Text-makeNameMap names = IntMap.fromDistinctAscList (zip [0 ..] (listValue names))--getName :: IntMap.IntMap Text -> Word32 -> Maybe Text-getName nameMap nameIndex =- IntMap.lookup (fromIntegral (word32Value nameIndex)) nameMap--makeObjectClassMap- :: Map.Map Word32 Text -> Bimap.Bimap Word32 Text -> Map.Map Word32 Word32-makeObjectClassMap objectMap classMap = do- let objectIds = Map.keys objectMap- let classIds = map (getClassId objectMap classMap) objectIds- let rawPairs = zip objectIds classIds- let- pairs = Maybe.mapMaybe- ( \(objectId, maybeClassId) -> case maybeClassId of- Nothing -> Nothing- Just classId -> Just (objectId, classId)- )- rawPairs- Map.fromList pairs--getClassId- :: Map.Map Word32 Text -> Bimap.Bimap Word32 Text -> Word32 -> Maybe Word32-getClassId objectMap classMap objectId = do- objectName <- getObjectName objectMap objectId- className <- getClassName objectName- Bimap.lookupR className classMap--makeClassCache- :: Bimap.Bimap Word32 Text- -> List Cache- -> [(Maybe Text, Word32, Word32, Word32)]-makeClassCache classMap caches = map- ( \cache ->- let- classId = cacheClassId cache- in- ( Bimap.lookup classId classMap- , classId- , cacheCacheId cache- , cacheParentCacheId cache- )- )- (listValue caches)--makeClassMap :: List ClassMapping -> Bimap.Bimap Word32 Text-makeClassMap classMappings = Bimap.fromList- ( map- ( \classMapping ->- (classMappingStreamId classMapping, classMappingName classMapping)- )- (listValue classMappings)- )--makeAttributeMap :: List Cache -> Map.Map Word32 (Map.Map Word32 Word32)-makeAttributeMap caches = Map.fromList- ( map- ( \cache ->- ( cacheClassId cache- , Map.fromList- ( map- ( \attributeMapping ->- ( attributeMappingStreamId attributeMapping- , attributeMappingObjectId attributeMapping- )- )- (listValue (cacheAttributeMappings cache))- )- )- )- (listValue caches)- )--makeShallowParentMap- :: [(Maybe Text, Word32, Word32, Word32)] -> Map.Map Word32 Word32-makeShallowParentMap classCache = Map.fromList- ( Maybe.mapMaybe- ( \xs -> case xs of- [] -> Nothing- (maybeClassName, classId, _, parentCacheId):rest -> do- parentClassId <- getParentClass maybeClassName parentCacheId rest- pure (classId, parentClassId)- )- (List.tails (reverse classCache))- )--makeParentMap- :: [(Maybe Text, Word32, Word32, Word32)] -> Map.Map Word32 [Word32]-makeParentMap classCache =- let- shallowParentMap = makeShallowParentMap classCache- in- Map.mapWithKey- (\classId _ -> getParentClasses shallowParentMap classId)- shallowParentMap--getParentClasses :: Map.Map Word32 Word32 -> Word32 -> [Word32]-getParentClasses shallowParentMap classId =- case Map.lookup classId shallowParentMap of- Nothing -> []- Just parentClassId ->- parentClassId : getParentClasses shallowParentMap parentClassId--getParentClass- :: Maybe Text- -> Word32- -> [(Maybe Text, Word32, Word32, Word32)]- -> Maybe Word32-getParentClass maybeClassName parentCacheId xs = case maybeClassName of- Nothing -> getParentClassById parentCacheId xs- Just className -> getParentClassByName className parentCacheId xs--getParentClassById- :: Word32 -> [(Maybe Text, Word32, Word32, Word32)] -> Maybe Word32-getParentClassById parentCacheId xs =- case dropWhile (\(_, _, cacheId, _) -> cacheId /= parentCacheId) xs of- [] -> if parentCacheId == Word32 0- then Nothing- else getParentClassById (Word32 (word32Value parentCacheId - 1)) xs- (_, parentClassId, _, _):_ -> Just parentClassId--getParentClassByName- :: Text -> Word32 -> [(Maybe Text, Word32, Word32, Word32)] -> Maybe Word32-getParentClassByName className parentCacheId xs =- case Map.lookup className parentClasses of- Nothing -> getParentClassById parentCacheId xs- Just parentClassName -> Maybe.maybe- (getParentClassById parentCacheId xs)- Just- ( Maybe.listToMaybe- ( map- (\(_, parentClassId, _, _) -> parentClassId)- ( filter- (\(_, _, cacheId, _) -> cacheId <= parentCacheId)- ( filter- ( \(maybeClassName, _, _, _) ->- maybeClassName == Just parentClassName- )- xs- )- )- )- )--parentClasses :: Map.Map Text Text-parentClasses = Map.map- stringToText- (Map.mapKeys stringToText (Map.fromList rawParentClasses))--makeObjectMap :: List Text -> Map.Map Word32 Text-makeObjectMap objects =- Map.fromAscList (zip (map Word32 [0 ..]) (listValue objects))--getObjectName :: Map.Map Word32 Text -> Word32 -> Maybe Text-getObjectName objectMap objectId = Map.lookup objectId objectMap--getClassName :: Text -> Maybe Text-getClassName rawObjectName =- Map.lookup (normalizeObjectName rawObjectName) objectClasses--normalizeObjectName :: Text -> Text-normalizeObjectName objectName =- let- name = textValue objectName- crowdActor = Text.pack "TheWorld:PersistentLevel.CrowdActor_TA"- crowdManager = Text.pack "TheWorld:PersistentLevel.CrowdManager_TA"- boostPickup = Text.pack "TheWorld:PersistentLevel.VehiclePickup_Boost_TA"- mapScoreboard = Text.pack "TheWorld:PersistentLevel.InMapScoreboard_TA"- breakout = Text.pack "TheWorld:PersistentLevel.BreakOutActor_Platform_TA"- in if Text.isInfixOf crowdActor name- then Text crowdActor- else if Text.isInfixOf crowdManager name- then Text crowdManager- else if Text.isInfixOf boostPickup name- then Text boostPickup- else if Text.isInfixOf mapScoreboard name- then Text mapScoreboard- else if Text.isInfixOf breakout name- then Text breakout- else objectName--objectClasses :: Map.Map Text Text-objectClasses = Map.map- stringToText- (Map.mapKeys stringToText (Map.fromList rawObjectClasses))--classHasLocation :: Text -> Bool-classHasLocation className = Set.member className classesWithLocation--classesWithLocation :: Set.Set Text-classesWithLocation = Set.fromList (map stringToText rawClassesWithLocation)--classHasRotation :: Text -> Bool-classHasRotation className = Set.member className classesWithRotation--classesWithRotation :: Set.Set Text-classesWithRotation = Set.fromList (map stringToText rawClassesWithRotation)--getAttributeIdLimit :: Map.Map Word32 Word32 -> Maybe Word-getAttributeIdLimit attributeMap = do- ((streamId, _), _) <- Map.maxViewWithKey attributeMap- let limit = fromIntegral (word32Value streamId)- pure limit--getAttributeName- :: ClassAttributeMap -> Map.Map Word32 Word32 -> CompressedWord -> Maybe Text-getAttributeName classAttributeMap attributeMap streamId = do- let key = Word32 (fromIntegral (compressedWordValue streamId))- attributeId <- Map.lookup key attributeMap- let objectMap = classAttributeMapObjectMap classAttributeMap- Map.lookup attributeId objectMap--getAttributeMap- :: ClassAttributeMap- -> ActorMap- -> CompressedWord- -> Maybe (Map.Map Word32 Word32)-getAttributeMap classAttributeMap actorMap actorId = do- objectId <- Map.lookup actorId actorMap- let objectClassMap = classAttributeMapObjectClassMap classAttributeMap- classId <- Map.lookup objectId objectClassMap- let value = classAttributeMapValue classAttributeMap- Map.lookup classId value
@@ -1,21 +0,0 @@-module Rattletrap.ClassMapping where--import Rattletrap.Primitive--import qualified Data.Binary as Binary--data ClassMapping = ClassMapping- { classMappingName :: Text- , classMappingStreamId :: Word32- } deriving (Eq, Ord, Show)--getClassMapping :: Binary.Get ClassMapping-getClassMapping = do- name <- getText- streamId <- getWord32- pure (ClassMapping name streamId)--putClassMapping :: ClassMapping -> Binary.Put-putClassMapping classMapping = do- putText (classMappingName classMapping)- putWord32 (classMappingStreamId classMapping)
@@ -1,125 +0,0 @@-module Rattletrap.Content where--import Rattletrap.Cache-import Rattletrap.ClassAttributeMap-import Rattletrap.ClassMapping-import Rattletrap.Frame-import Rattletrap.KeyFrame-import Rattletrap.Mark-import Rattletrap.Message-import Rattletrap.Primitive-import Rattletrap.Utility--import qualified Data.Binary as Binary-import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit-import qualified Data.Binary.Get as Binary-import qualified Data.Binary.Put as Binary-import qualified Data.Map as Map---- | Contains low-level game data about a 'Rattletrap.Replay.Replay'.-data Content = Content- { contentLevels :: List Text- -- ^ This typically only has one element, like @stadium_oob_audio_map@.- , contentKeyFrames :: List KeyFrame- -- ^ A list of which frames are key frames. Although they aren't necessary- -- for replay, key frames are frames that replicate every actor. They- -- typically happen once every 10 seconds.- , contentStreamSize :: Word32- -- ^ The size of the stream in bytes. This is only really necessary because- -- the stream has some arbitrary amount of padding at the end.- , contentFrames :: [Frame]- -- ^ The actual game data. This is where all the interesting information is.- , contentMessages :: List Message- -- ^ Debugging messages. In newer replays, this is always empty.- , contentMarks :: List Mark- -- ^ Tick marks shown on the scrubber when watching a replay.- , contentPackages :: List Text- -- ^ A list of @.upk@ files to load, like- -- @..\\..\\TAGame\\CookedPCConsole\\Stadium_P.upk@.- , contentObjects :: List Text- -- ^ Objects in the stream. Used for the- -- 'Rattletrap.ClassAttributeMap.ClassAttributeMap'.- , contentNames :: List Text- -- ^ It's not clear what these are used for. This list is usually not empty,- -- but appears unused otherwise.- , contentClassMappings :: List ClassMapping- -- ^ A mapping between classes and their ID in the stream. Used for the- -- 'Rattletrap.ClassAttributeMap.ClassAttributeMap'.- , contentCaches :: List Cache- -- ^ A list of classes along with their parent classes and attributes. Used- -- for the 'Rattletrap.ClassAttributeMap.ClassAttributeMap'.- } deriving (Eq, Ord, Show)--getContent- :: (Int, Int, Int)- -- ^ Version numbers, usually from 'Rattletrap.Header.getVersion'.- -> Int- -- ^ The number of frames in the stream, usually from- -- 'Rattletrap.Header.getNumFrames'.- -> Word- -- ^ The maximum number of channels in the stream, usually from- -- 'Rattletrap.Header.getMaxChannels'.- -> Binary.Get Content-getContent version numFrames maxChannels = do- levels <- getList getText- keyFrames <- getList getKeyFrame- streamSize <- getWord32- stream <- Binary.getLazyByteString (fromIntegral (word32Value streamSize))- messages <- getList getMessage- marks <- getList getMark- packages <- getList getText- objects <- getList getText- names <- getList getText- classMappings <- getList getClassMapping- caches <- getList getCache- let- classAttributeMap =- makeClassAttributeMap objects classMappings caches names- let- frames = Binary.runGet- ( BinaryBit.runBitGet- ( do- (theFrames, _) <- getFrames- version- numFrames- maxChannels- classAttributeMap- Map.empty- pure theFrames- )- )- (reverseBytes stream)- pure- ( Content- levels- keyFrames- streamSize- frames- messages- marks- packages- objects- names- classMappings- caches- )--putContent :: Content -> Binary.Put-putContent content = do- putList putText (contentLevels content)- putList putKeyFrame (contentKeyFrames content)- let streamSize = contentStreamSize content- putWord32 streamSize- let- stream =- Binary.runPut (BinaryBit.runBitPut (putFrames (contentFrames content)))- Binary.putLazyByteString- (reverseBytes (padBytes (word32Value streamSize) stream))- putList putMessage (contentMessages content)- putList putMark (contentMarks content)- putList putText (contentPackages content)- putList putText (contentObjects content)- putList putText (contentNames content)- putList putClassMapping (contentClassMappings content)- putList putCache (contentCaches content)
@@ -1,40 +0,0 @@-module Rattletrap.Crc where--import Rattletrap.Data--import qualified Data.Bits as Bits-import qualified Data.ByteString.Lazy as ByteString-import qualified Data.Vector.Unboxed as Vector-import qualified Data.Word as Word---- | Computes the CRC32 of some bytes. This is done to ensure that the bytes--- are valid before trying to parse them.------ @--- getCrc32 ('Data.ByteString.Lazy.pack' [0x00])--- @------ This CRC uses an initial value of @0xefcbf201@ and a polynomial of--- @0x04c11db7@.-getCrc32 :: ByteString.ByteString -> Word.Word32-getCrc32 bytes = do- let update = crc32Update crc32Table- let initial = Bits.complement crc32Initial- let crc = ByteString.foldl update initial bytes- Bits.complement crc--crc32Update- :: Vector.Vector Word.Word32 -> Word.Word32 -> Word.Word8 -> Word.Word32-crc32Update table crc byte = do- let toWord8 = fromIntegral :: (Integral a) => a -> Word.Word8- let toInt = fromIntegral :: (Integral a) => a -> Int- let index = toInt (Bits.xor byte (toWord8 (Bits.shiftR crc 24)))- let left = Vector.unsafeIndex table index- let right = Bits.shiftL crc 8- Bits.xor left right--crc32Initial :: Word.Word32-crc32Initial = 0xefcbf201--crc32Table :: Vector.Vector Word.Word32-crc32Table = Vector.fromList rawCrc32Table
@@ -1,372 +0,0 @@-module Rattletrap.Data where--import Rattletrap.AttributeType--rawParentClasses :: [(String, String)]-rawParentClasses =- [ ("Engine.Actor", "Core.Object")- , ("Engine.GameReplicationInfo", "Engine.ReplicationInfo")- , ("Engine.Info", "Engine.Actor")- , ("Engine.Pawn", "Engine.Actor")- , ("Engine.PlayerReplicationInfo", "Engine.ReplicationInfo")- , ("Engine.ReplicationInfo", "Engine.Info")- , ("Engine.TeamInfo", "Engine.ReplicationInfo")- , ("ProjectX.GRI_X", "Engine.GameReplicationInfo")- , ("ProjectX.Pawn_X", "Engine.Pawn")- , ("ProjectX.PRI_X", "Engine.PlayerReplicationInfo")- , ("TAGame.Ball_TA", "TAGame.RBActor_TA")- , ("TAGame.CameraSettingsActor_TA", "Engine.ReplicationInfo")- , ("TAGame.Car_Season_TA", "TAGame.PRI_TA")- , ("TAGame.Car_TA", "TAGame.Vehicle_TA")- , ("TAGame.CarComponent_Boost_TA", "TAGame.CarComponent_TA")- , ("TAGame.CarComponent_Dodge_TA", "TAGame.CarComponent_TA")- , ("TAGame.CarComponent_DoubleJump_TA", "TAGame.CarComponent_TA")- , ("TAGame.CarComponent_FlipCar_TA", "TAGame.CarComponent_TA")- , ("TAGame.CarComponent_Jump_TA", "TAGame.CarComponent_TA")- , ("TAGame.CarComponent_TA", "Engine.ReplicationInfo")- , ("TAGame.CrowdActor_TA", "Engine.ReplicationInfo")- , ("TAGame.CrowdManager_TA", "Engine.ReplicationInfo")- , ("TAGame.GameEvent_Season_TA", "TAGame.GameEvent_Soccar_TA")- , ("TAGame.GameEvent_Soccar_TA", "TAGame.GameEvent_Team_TA")- , ("TAGame.GameEvent_SoccarPrivate_TA", "TAGame.GameEvent_Soccar_TA")- , ("TAGame.GameEvent_SoccarSplitscreen_TA", "TAGame.GameEvent_SoccarPrivate_TA")- , ("TAGame.GameEvent_TA", "Engine.ReplicationInfo")- , ("TAGame.GameEvent_Team_TA", "TAGame.GameEvent_TA")- , ("TAGame.GRI_TA", "ProjectX.GRI_X")- , ("TAGame.InMapScoreboard_TA", "Engine.Actor")- , ("TAGame.PRI_TA", "ProjectX.PRI_X")- , ("TAGame.RBActor_TA", "ProjectX.Pawn_X")- , ("TAGame.SpecialPickup_BallCarSpring_TA", "TAGame.SpecialPickup_Spring_TA")- , ("TAGame.SpecialPickup_BallFreeze_TA", "TAGame.SpecialPickup_Targeted_TA")- , ("TAGame.SpecialPickup_BallGravity_TA", "TAGame.SpecialPickup_TA")- , ("TAGame.SpecialPickup_BallLasso_TA", "TAGame.SpecialPickup_GrapplingHook_TA")- , ("TAGame.SpecialPickup_BallVelcro_TA", "TAGame.SpecialPickup_TA")- , ("TAGame.SpecialPickup_Batarang_TA", "TAGame.SpecialPickup_BallLasso_TA")- , ("TAGame.SpecialPickup_BoostOverride_TA", "TAGame.SpecialPickup_Targeted_TA")- , ("TAGame.SpecialPickup_GrapplingHook_TA", "TAGame.SpecialPickup_Targeted_TA")- , ("TAGame.SpecialPickup_HitForce_TA", "TAGame.SpecialPickup_TA")- , ("TAGame.SpecialPickup_Spring_TA", "TAGame.SpecialPickup_Targeted_TA")- , ("TAGame.SpecialPickup_Swapper_TA", "TAGame.SpecialPickup_Targeted_TA")- , ("TAGame.SpecialPickup_TA", "TAGame.CarComponent_TA")- , ("TAGame.SpecialPickup_Targeted_TA", "TAGame.SpecialPickup_TA")- , ("TAGame.SpecialPickup_Tornado_TA", "TAGame.SpecialPickup_TA")- , ("TAGame.Team_Soccar_TA", "TAGame.Team_TA")- , ("TAGame.Team_TA", "Engine.TeamInfo")- , ("TAGame.Vehicle_TA", "TAGame.RBActor_TA")- , ("TAGame.VehiclePickup_Boost_TA", "TAGame.VehiclePickup_TA")- , ("TAGame.VehiclePickup_TA", "Engine.ReplicationInfo")- ]--rawClassesWithLocation :: [String]-rawClassesWithLocation =- [ "TAGame.Ball_Breakout_TA"- , "TAGame.Ball_TA"- , "TAGame.CameraSettingsActor_TA"- , "TAGame.Car_Season_TA"- , "TAGame.Car_TA"- , "TAGame.CarComponent_Boost_TA"- , "TAGame.CarComponent_Dodge_TA"- , "TAGame.CarComponent_DoubleJump_TA"- , "TAGame.CarComponent_FlipCar_TA"- , "TAGame.CarComponent_Jump_TA"- , "TAGame.GameEvent_Season_TA"- , "TAGame.GameEvent_Soccar_TA"- , "TAGame.GameEvent_SoccarPrivate_TA"- , "TAGame.GameEvent_SoccarSplitscreen_TA"- , "TAGame.GRI_TA"- , "TAGame.PRI_TA"- , "TAGame.SpecialPickup_BallCarSpring_TA"- , "TAGame.SpecialPickup_BallFreeze_TA"- , "TAGame.SpecialPickup_BallGravity_TA"- , "TAGame.SpecialPickup_BallGravity_TA"- , "TAGame.SpecialPickup_BallLasso_TA"- , "TAGame.SpecialPickup_BallVelcro_TA"- , "TAGame.SpecialPickup_Batarang_TA"- , "TAGame.SpecialPickup_BoostOverride_TA"- , "TAGame.SpecialPickup_GrapplingHook_TA"- , "TAGame.SpecialPickup_HitForce_TA"- , "TAGame.SpecialPickup_Swapper_TA"- , "TAGame.SpecialPickup_Tornado_TA"- , "TAGame.Team_Soccar_TA"- ]--rawClassesWithRotation :: [String]-rawClassesWithRotation =- [ "TAGame.Ball_Breakout_TA"- , "TAGame.Ball_TA"- , "TAGame.Car_Season_TA"- , "TAGame.Car_TA"- ]--rawObjectClasses :: [(String, String)]-rawObjectClasses =- [ ("Archetypes.Ball.Ball_BasketBall_Mutator", "TAGame.Ball_TA")- , ("Archetypes.Ball.Ball_Basketball", "TAGame.Ball_TA")- , ("Archetypes.Ball.Ball_Breakout", "TAGame.Ball_Breakout_TA")- , ("Archetypes.Ball.Ball_Default", "TAGame.Ball_TA")- , ("Archetypes.Ball.Ball_Puck", "TAGame.Ball_TA")- , ("Archetypes.Ball.CubeBall", "TAGame.Ball_TA")- , ("Archetypes.Car.Car_Default", "TAGame.Car_TA")- , ("Archetypes.CarComponents.CarComponent_Boost", "TAGame.CarComponent_Boost_TA")- , ("Archetypes.CarComponents.CarComponent_Dodge", "TAGame.CarComponent_Dodge_TA")- , ("Archetypes.CarComponents.CarComponent_DoubleJump", "TAGame.CarComponent_DoubleJump_TA")- , ("Archetypes.CarComponents.CarComponent_FlipCar", "TAGame.CarComponent_FlipCar_TA")- , ("Archetypes.CarComponents.CarComponent_Jump", "TAGame.CarComponent_Jump_TA")- , ("Archetypes.GameEvent.GameEvent_Basketball", "TAGame.GameEvent_Soccar_TA")- , ("Archetypes.GameEvent.GameEvent_BasketballPrivate", "TAGame.GameEvent_SoccarPrivate_TA")- , ("Archetypes.GameEvent.GameEvent_BasketballSplitscreen", "TAGame.GameEvent_SoccarSplitscreen_TA")- , ("Archetypes.GameEvent.GameEvent_Breakout", "TAGame.GameEvent_Soccar_TA")- , ("Archetypes.GameEvent.GameEvent_Hockey", "TAGame.GameEvent_Soccar_TA")- , ("Archetypes.GameEvent.GameEvent_HockeyPrivate", "TAGame.GameEvent_SoccarPrivate_TA")- , ("Archetypes.GameEvent.GameEvent_HockeySplitscreen", "TAGame.GameEvent_SoccarSplitscreen_TA")- , ("Archetypes.GameEvent.GameEvent_Items", "TAGame.GameEvent_Soccar_TA")- , ("Archetypes.GameEvent.GameEvent_Season:CarArchetype", "TAGame.Car_TA")- , ("Archetypes.GameEvent.GameEvent_Season", "TAGame.GameEvent_Season_TA")- , ("Archetypes.GameEvent.GameEvent_Soccar", "TAGame.GameEvent_Soccar_TA")- , ("Archetypes.GameEvent.GameEvent_SoccarPrivate", "TAGame.GameEvent_SoccarPrivate_TA")- , ("Archetypes.GameEvent.GameEvent_SoccarSplitscreen", "TAGame.GameEvent_SoccarSplitscreen_TA")- , ("Archetypes.SpecialPickups.SpecialPickup_BallFreeze", "TAGame.SpecialPickup_BallFreeze_TA")- , ("Archetypes.SpecialPickups.SpecialPickup_BallGrapplingHook", "TAGame.SpecialPickup_GrapplingHook_TA")- , ("Archetypes.SpecialPickups.SpecialPickup_BallLasso", "TAGame.SpecialPickup_BallLasso_TA")- , ("Archetypes.SpecialPickups.SpecialPickup_BallSpring", "TAGame.SpecialPickup_BallCarSpring_TA")- , ("Archetypes.SpecialPickups.SpecialPickup_BallVelcro", "TAGame.SpecialPickup_BallVelcro_TA")- , ("Archetypes.SpecialPickups.SpecialPickup_Batarang", "TAGame.SpecialPickup_Batarang_TA")- , ("Archetypes.SpecialPickups.SpecialPickup_BoostOverride", "TAGame.SpecialPickup_BoostOverride_TA")- , ("Archetypes.SpecialPickups.SpecialPickup_CarSpring", "TAGame.SpecialPickup_BallCarSpring_TA")- , ("Archetypes.SpecialPickups.SpecialPickup_GravityWell", "TAGame.SpecialPickup_BallGravity_TA")- , ("Archetypes.SpecialPickups.SpecialPickup_StrongHit", "TAGame.SpecialPickup_HitForce_TA")- , ("Archetypes.SpecialPickups.SpecialPickup_Swapper", "TAGame.SpecialPickup_Swapper_TA")- , ("Archetypes.SpecialPickups.SpecialPickup_Tornado", "TAGame.SpecialPickup_Tornado_TA")- , ("Archetypes.Teams.Team0", "TAGame.Team_Soccar_TA")- , ("Archetypes.Teams.Team1", "TAGame.Team_Soccar_TA")- , ("GameInfo_Basketball.GameInfo.GameInfo_Basketball:GameReplicationInfoArchetype", "TAGame.GRI_TA")- , ("GameInfo_Breakout.GameInfo.GameInfo_Breakout:GameReplicationInfoArchetype", "TAGame.GRI_TA")- , ("Gameinfo_Hockey.GameInfo.Gameinfo_Hockey:GameReplicationInfoArchetype", "TAGame.GRI_TA")- , ("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")- , ("TAGame.Default__CameraSettingsActor_TA", "TAGame.CameraSettingsActor_TA")- , ("TAGame.Default__PRI_TA", "TAGame.PRI_TA")- , ("TheWorld:PersistentLevel.BreakOutActor_Platform_TA", "TAGame.BreakOutActor_Platform_TA")- , ("TheWorld:PersistentLevel.CrowdActor_TA", "TAGame.CrowdActor_TA")- , ("TheWorld:PersistentLevel.CrowdManager_TA", "TAGame.CrowdManager_TA")- , ("TheWorld:PersistentLevel.InMapScoreboard_TA", "TAGame.InMapScoreboard_TA")- , ("TheWorld:PersistentLevel.VehiclePickup_Boost_TA", "TAGame.VehiclePickup_Boost_TA")- ]--rawAttributeTypes :: [(String, AttributeType)]-rawAttributeTypes =- [ ("Engine.Actor:bBlockActors", BooleanAttributeType)- , ("Engine.Actor:bCollideActors", BooleanAttributeType)- , ("Engine.Actor:bHidden", BooleanAttributeType)- , ("Engine.Actor:DrawScale", FloatAttributeType)- , ("Engine.Actor:Role", EnumAttributeType)- , ("Engine.GameReplicationInfo:bMatchIsOver", BooleanAttributeType)- , ("Engine.GameReplicationInfo:GameClass", FlaggedIntAttributeType)- , ("Engine.GameReplicationInfo:ServerName", StringAttributeType)- , ("Engine.Pawn:PlayerReplicationInfo", FlaggedIntAttributeType)- , ("Engine.PlayerReplicationInfo:bBot", BooleanAttributeType)- , ("Engine.PlayerReplicationInfo:bIsSpectator", BooleanAttributeType)- , ("Engine.PlayerReplicationInfo:bReadyToPlay", BooleanAttributeType)- , ("Engine.PlayerReplicationInfo:bWaitingPlayer", BooleanAttributeType)- , ("Engine.PlayerReplicationInfo:Ping", ByteAttributeType)- , ("Engine.PlayerReplicationInfo:PlayerID", IntAttributeType)- , ("Engine.PlayerReplicationInfo:PlayerName", StringAttributeType)- , ("Engine.PlayerReplicationInfo:RemoteUserData", StringAttributeType)- , ("Engine.PlayerReplicationInfo:Score", IntAttributeType)- , ("Engine.PlayerReplicationInfo:Team", FlaggedIntAttributeType)- , ("Engine.PlayerReplicationInfo:UniqueId", UniqueIdAttributeType)- , ("Engine.TeamInfo:Score", IntAttributeType)- , ("ProjectX.GRI_X:bGameStarted", BooleanAttributeType)- , ("ProjectX.GRI_X:GameServerID", QWordAttributeType)- , ("ProjectX.GRI_X:MatchGUID", StringAttributeType)- , ("ProjectX.GRI_X:ReplicatedGameMutatorIndex", IntAttributeType)- , ("ProjectX.GRI_X:ReplicatedGamePlaylist", IntAttributeType)- , ("ProjectX.GRI_X:Reservations", ReservationAttributeType)- , ("TAGame.Ball_Breakout_TA:AppliedDamage", AppliedDamageAttributeType)- , ("TAGame.Ball_Breakout_TA:DamageIndex", IntAttributeType)- , ("TAGame.Ball_Breakout_TA:LastTeamTouch", ByteAttributeType)- , ("TAGame.Ball_TA:GameEvent", FlaggedIntAttributeType)- , ("TAGame.Ball_TA:HitTeamNum", ByteAttributeType)- , ("TAGame.Ball_TA:ReplicatedAddedCarBounceScale", FloatAttributeType)- , ("TAGame.Ball_TA:ReplicatedBallMaxLinearSpeedScale", FloatAttributeType)- , ("TAGame.Ball_TA:ReplicatedBallScale", FloatAttributeType)- , ("TAGame.Ball_TA:ReplicatedExplosionData", ExplosionAttributeType)- , ("TAGame.Ball_TA:ReplicatedExplosionDataExtended", ExtendedExplosionAttributeType)- , ("TAGame.Ball_TA:ReplicatedWorldBounceScale", FloatAttributeType)- , ("TAGame.BreakOutActor_Platform_TA:DamageState", DamageStateAttributeType)- , ("TAGame.CameraSettingsActor_TA:bUsingBehindView", BooleanAttributeType)- , ("TAGame.CameraSettingsActor_TA:bUsingSecondaryCamera", BooleanAttributeType)- , ("TAGame.CameraSettingsActor_TA:CameraPitch", ByteAttributeType)- , ("TAGame.CameraSettingsActor_TA:CameraYaw", ByteAttributeType)- , ("TAGame.CameraSettingsActor_TA:PRI", FlaggedIntAttributeType)- , ("TAGame.CameraSettingsActor_TA:ProfileSettings", CamSettingsAttributeType)- , ("TAGame.Car_TA:AddedBallForceMultiplier", FloatAttributeType)- , ("TAGame.Car_TA:AddedCarForceMultiplier", FloatAttributeType)- , ("TAGame.Car_TA:AttachedPickup", FlaggedIntAttributeType)- , ("TAGame.Car_TA:ClubColors", ClubColorsAttributeType)- , ("TAGame.Car_TA:ReplicatedDemolish", DemolishAttributeType)- , ("TAGame.Car_TA:TeamPaint", TeamPaintAttributeType)- , ("TAGame.CarComponent_Boost_TA:bNoBoost", BooleanAttributeType)- , ("TAGame.CarComponent_Boost_TA:BoostModifier", FloatAttributeType)- , ("TAGame.CarComponent_Boost_TA:bUnlimitedBoost", BooleanAttributeType)- , ("TAGame.CarComponent_Boost_TA:RechargeDelay", FloatAttributeType)- , ("TAGame.CarComponent_Boost_TA:RechargeRate", FloatAttributeType)- , ("TAGame.CarComponent_Boost_TA:ReplicatedBoostAmount", ByteAttributeType)- , ("TAGame.CarComponent_Boost_TA:UnlimitedBoostRefCount", IntAttributeType)- , ("TAGame.CarComponent_Dodge_TA:DodgeTorque", LocationAttributeType)- , ("TAGame.CarComponent_FlipCar_TA:bFlipRight", BooleanAttributeType)- , ("TAGame.CarComponent_FlipCar_TA:FlipCarTime", FloatAttributeType)- , ("TAGame.CarComponent_TA:ReplicatedActive", ByteAttributeType)- , ("TAGame.CarComponent_TA:ReplicatedActivityTime", FloatAttributeType)- , ("TAGame.CarComponent_TA:Vehicle", FlaggedIntAttributeType)- , ("TAGame.CrowdActor_TA:GameEvent", FlaggedIntAttributeType)- , ("TAGame.CrowdActor_TA:ModifiedNoise", FloatAttributeType)- , ("TAGame.CrowdActor_TA:ReplicatedCountDownNumber", IntAttributeType)- , ("TAGame.CrowdActor_TA:ReplicatedOneShotSound", FlaggedIntAttributeType)- , ("TAGame.CrowdActor_TA:ReplicatedRoundCountDownNumber", IntAttributeType)- , ("TAGame.CrowdManager_TA:GameEvent", FlaggedIntAttributeType)- , ("TAGame.CrowdManager_TA:ReplicatedGlobalOneShotSound", FlaggedIntAttributeType)- , ("TAGame.GameEvent_Soccar_TA:bBallHasBeenHit", BooleanAttributeType)- , ("TAGame.GameEvent_Soccar_TA:bOverTime", BooleanAttributeType)- , ("TAGame.GameEvent_Soccar_TA:GameTime", IntAttributeType)- , ("TAGame.GameEvent_Soccar_TA:ReplicatedMusicStinger", MusicStingerAttributeType)- , ("TAGame.GameEvent_Soccar_TA:ReplicatedScoredOnTeam", ByteAttributeType)- , ("TAGame.GameEvent_Soccar_TA:RoundNum", IntAttributeType)- , ("TAGame.GameEvent_Soccar_TA:SecondsRemaining", IntAttributeType)- , ("TAGame.GameEvent_Soccar_TA:SubRulesArchetype", FlaggedIntAttributeType)- , ("TAGame.GameEvent_SoccarPrivate_TA:MatchSettings", PrivateMatchSettingsAttributeType)- , ("TAGame.GameEvent_TA:bCanVoteToForfeit", BooleanAttributeType)- , ("TAGame.GameEvent_TA:bHasLeaveMatchPenalty", BooleanAttributeType)- , ("TAGame.GameEvent_TA:BotSkill", IntAttributeType)- , ("TAGame.GameEvent_TA:GameMode", GameModeAttributeType)- , ("TAGame.GameEvent_TA:MatchTypeClass", FlaggedIntAttributeType)- , ("TAGame.GameEvent_TA:ReplicatedGameStateTimeRemaining", IntAttributeType)- , ("TAGame.GameEvent_TA:ReplicatedStateIndex", ByteAttributeType)- , ("TAGame.GameEvent_TA:ReplicatedStateName", IntAttributeType)- , ("TAGame.GameEvent_Team_TA:bForfeit", BooleanAttributeType)- , ("TAGame.GameEvent_Team_TA:MaxTeamSize", IntAttributeType)- , ("TAGame.GRI_TA:NewDedicatedServerIP", StringAttributeType)- , ("TAGame.PRI_TA:bIsInSplitScreen", BooleanAttributeType)- , ("TAGame.PRI_TA:bMatchMVP", BooleanAttributeType)- , ("TAGame.PRI_TA:bOnlineLoadoutSet", BooleanAttributeType)- , ("TAGame.PRI_TA:bOnlineLoadoutsSet", BooleanAttributeType)- , ("TAGame.PRI_TA:BotProductName", IntAttributeType)- , ("TAGame.PRI_TA:bReady", BooleanAttributeType)- , ("TAGame.PRI_TA:bUsingBehindView", BooleanAttributeType)- , ("TAGame.PRI_TA:bUsingItems", BooleanAttributeType)- , ("TAGame.PRI_TA:bUsingSecondaryCamera", BooleanAttributeType)- , ("TAGame.PRI_TA:CameraPitch", ByteAttributeType)- , ("TAGame.PRI_TA:CameraSettings", CamSettingsAttributeType)- , ("TAGame.PRI_TA:CameraYaw", ByteAttributeType)- , ("TAGame.PRI_TA:ClientLoadout", LoadoutAttributeType)- , ("TAGame.PRI_TA:ClientLoadoutOnline", LoadoutOnlineAttributeType)- , ("TAGame.PRI_TA:ClientLoadouts", LoadoutsAttributeType)- , ("TAGame.PRI_TA:ClientLoadoutsOnline", LoadoutsOnlineAttributeType)- , ("TAGame.PRI_TA:MatchAssists", IntAttributeType)- , ("TAGame.PRI_TA:MatchBreakoutDamage", IntAttributeType)- , ("TAGame.PRI_TA:MatchGoals", IntAttributeType)- , ("TAGame.PRI_TA:MatchSaves", IntAttributeType)- , ("TAGame.PRI_TA:MatchScore", IntAttributeType)- , ("TAGame.PRI_TA:MatchShots", IntAttributeType)- , ("TAGame.PRI_TA:MaxTimeTillItem", IntAttributeType)- , ("TAGame.PRI_TA:PartyLeader", PartyLeaderAttributeType)- , ("TAGame.PRI_TA:PawnType", ByteAttributeType)- , ("TAGame.PRI_TA:PersistentCamera", FlaggedIntAttributeType)- , ("TAGame.PRI_TA:PlayerHistoryValid", BooleanAttributeType)- , ("TAGame.PRI_TA:ReplicatedGameEvent", FlaggedIntAttributeType)- , ("TAGame.PRI_TA:SteeringSensitivity", FloatAttributeType)- , ("TAGame.PRI_TA:TimeTillItem", IntAttributeType)- , ("TAGame.PRI_TA:Title", IntAttributeType)- , ("TAGame.PRI_TA:TotalXP", IntAttributeType)- , ("TAGame.RBActor_TA:bFrozen", BooleanAttributeType)- , ("TAGame.RBActor_TA:bIgnoreSyncing", BooleanAttributeType)- , ("TAGame.RBActor_TA:bReplayActor", BooleanAttributeType)- , ("TAGame.RBActor_TA:ReplicatedRBState", RigidBodyStateAttributeType)- , ("TAGame.RBActor_TA:WeldedInfo", WeldedInfoAttributeType)- , ("TAGame.SpecialPickup_BallFreeze_TA:RepOrigSpeed", FloatAttributeType)- , ("TAGame.SpecialPickup_BallVelcro_TA:AttachTime", FloatAttributeType)- , ("TAGame.SpecialPickup_BallVelcro_TA:bBroken", BooleanAttributeType)- , ("TAGame.SpecialPickup_BallVelcro_TA:bHit", BooleanAttributeType)- , ("TAGame.SpecialPickup_BallVelcro_TA:BreakTime", FloatAttributeType)- , ("TAGame.SpecialPickup_Targeted_TA:Targeted", FlaggedIntAttributeType)- , ("TAGame.Team_Soccar_TA:GameScore", IntAttributeType)- , ("TAGame.Team_TA:ClubColors", ClubColorsAttributeType)- , ("TAGame.Team_TA:CustomTeamName", StringAttributeType)- , ("TAGame.Team_TA:GameEvent", FlaggedIntAttributeType)- , ("TAGame.Team_TA:LogoData", FlaggedIntAttributeType)- , ("TAGame.Vehicle_TA:bDriving", BooleanAttributeType)- , ("TAGame.Vehicle_TA:bReplicatedHandbrake", BooleanAttributeType)- , ("TAGame.Vehicle_TA:ReplicatedSteer", ByteAttributeType)- , ("TAGame.Vehicle_TA:ReplicatedThrottle", ByteAttributeType)- , ("TAGame.VehiclePickup_TA:bNoPickup", BooleanAttributeType)- , ("TAGame.VehiclePickup_TA:ReplicatedPickupData", PickupAttributeType)- ]--rawCrc32Table :: Integral a => [a]-rawCrc32Table =- [ 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9- , 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005- , 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61- , 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd- , 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9- , 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75- , 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011- , 0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd- , 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039- , 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5- , 0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81- , 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d- , 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49- , 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95- , 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1- , 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d- , 0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae- , 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072- , 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16- , 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca- , 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde- , 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02- , 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066- , 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba- , 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e- , 0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692- , 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6- , 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a- , 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e- , 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2- , 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686- , 0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a- , 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637- , 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb- , 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f- , 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53- , 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47- , 0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b- , 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff- , 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623- , 0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7- , 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b- , 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f- , 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3- , 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7- , 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b- , 0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f- , 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3- , 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640- , 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c- , 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8- , 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24- , 0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30- , 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec- , 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088- , 0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654- , 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0- , 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c- , 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18- , 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4- , 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0- , 0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c- , 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668- , 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4- ]
@@ -1,67 +0,0 @@-module Rattletrap.Frame where--import Rattletrap.ActorMap-import Rattletrap.ClassAttributeMap-import Rattletrap.Primitive-import Rattletrap.Replication--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--data Frame = Frame- { frameTime :: Float32- -- ^ Time in seconds since the beginning of the match.- , frameDelta :: Float32- -- ^ Time in seconds since the last frame. Usually about 0.03 since there- -- are 30 frames per second.- , frameReplications :: [Replication]- } deriving (Eq, Ord, Show)--getFrames- :: (Int, Int, Int)- -> Int- -> Word- -> ClassAttributeMap- -> ActorMap- -> BinaryBit.BitGet ([Frame], ActorMap)-getFrames version numFrames maxChannels classAttributeMap actorMap =- if numFrames <= 0- then pure ([], actorMap)- else do- (frame, newActorMap) <- getFrame- version- maxChannels- classAttributeMap- actorMap- (frames, newerActorMap) <- getFrames- version- (numFrames - 1)- maxChannels- classAttributeMap- newActorMap- pure (frame : frames, newerActorMap)--putFrames :: [Frame] -> BinaryBit.BitPut ()-putFrames = mapM_ putFrame--getFrame- :: (Int, Int, Int)- -> Word- -> ClassAttributeMap- -> ActorMap- -> BinaryBit.BitGet (Frame, ActorMap)-getFrame version maxChannels classAttributeMap actorMap = do- time <- getFloat32Bits- delta <- getFloat32Bits- (replications, newActorMap) <- getReplications- version- maxChannels- classAttributeMap- actorMap- pure (Frame time delta replications, newActorMap)--putFrame :: Frame -> BinaryBit.BitPut ()-putFrame frame = do- putFloat32Bits (frameTime frame)- putFloat32Bits (frameDelta frame)- putReplications (frameReplications frame)
@@ -1,112 +0,0 @@-module Rattletrap.Header where--import Rattletrap.Primitive-import Rattletrap.Property-import Rattletrap.PropertyValue--import qualified Data.Binary as Binary-import qualified Data.Map as Map-import qualified Data.Maybe as Maybe---- | Contains high-level metadata about a 'Rattletrap.Replay.Replay'.-data Header = Header- { headerEngineVersion :: Word32- -- ^ The "major" ("engine") version number.- , headerLicenseeVersion :: Word32- -- ^ The "minor" ("licensee") version number.- , headerPatchVersion :: Maybe Word32- -- ^ The "patch" ("net") version number.- , headerLabel :: Text- -- ^ Always @TAGame.Replay_Soccar_TA@.- , headerProperties :: Dictionary Property- -- ^ These properties determine how a replay will look in the list of- -- replays in-game. One element is required for the replay to show up:- --- -- - MapName: This is a 'Rattletrap.PropertyValue.NameProperty' with a- -- case-insensitive map identifier, like @Stadium_P@.- --- -- There are many other properties that affect how the replay looks in the- -- list of replays.- --- -- - Date: A 'Rattletrap.PropertyValue.StrProperty' with the format- -- @YYYY-mm-dd:HH-MM@. Dates are not validated, but the month must be- -- between 1 and 12 to show up. The hour is shown modulo 12 with AM or PM.- -- - MatchType: A 'Rattletrap.PropertyValue.NameProperty'. If this is not- -- one of the expected values, nothing will be shown next to the replay's- -- map. The expected values are: @Online@, @Offline@, @Private@, and- -- @Season@.- -- - NumFrames: This 'Rattletrap.PropertyValue.IntProperty' is used to- -- calculate the length of the match. There are 30 frames per second,- -- a typical 5-minute match has about 9,000 frames.- -- - PrimaryPlayerTeam: This is an 'Rattletrap.PropertyValue.IntProperty'.- -- It is either 0 (blue) or 1 (orange). Any other value is ignored. If- -- this would be 0, you don't have to set it at all.- -- - ReplayName: An optional 'Rattletrap.PropertyValue.StrProperty' with a- -- user-supplied name for the replay.- -- - Team0Score: The blue team's score as an- -- 'Rattletrap.PropertyValue.IntProperty'. Can be omitted if the score is- -- 0.- -- - Team1Score: The orange team's score as an- -- 'Rattletrap.PropertyValue.IntProperty'. Can also be omitted if the- -- score is 0.- -- - TeamSize: An 'Rattletrap.PropertyValue.IntProperty' with the number of- -- players per team. This value is not validated, so you can put absurd- -- values like 99. To get an "unfair" team size like 1v4, you must set the- -- bUnfairBots 'Rattletrap.PropertyValue.BoolProperty' to @True@.- } deriving (Eq, Ord, Show)--getHeader :: Binary.Get Header-getHeader = do- engineVersion <- getWord32- licenseeVersion <- getWord32- patchVersion <- getPatchVersion engineVersion licenseeVersion- label <- getText- properties <- getDictionary getProperty- pure (Header engineVersion licenseeVersion patchVersion label properties)--getPatchVersion :: Word32 -> Word32 -> Binary.Get (Maybe Word32)-getPatchVersion major minor = if hasPatchVersion major minor- then do- patchVersion <- getWord32- pure (Just patchVersion)- else pure Nothing--hasPatchVersion :: Word32 -> Word32 -> Bool-hasPatchVersion major minor = major >= Word32 868 && minor >= Word32 18--putHeader :: Header -> Binary.Put-putHeader header = do- putWord32 (headerEngineVersion header)- putWord32 (headerLicenseeVersion header)- case headerPatchVersion header of- Nothing -> pure ()- Just patchVersion -> putWord32 patchVersion- putText (headerLabel header)- putDictionary putProperty (headerProperties header)--getVersion :: Header -> (Int, Int, Int)-getVersion header =- ( fromIntegral (word32Value (headerEngineVersion header))- , fromIntegral (word32Value (headerLicenseeVersion header))- , Maybe.fromMaybe 0 (fmap (\ v -> fromIntegral (word32Value v)) (headerPatchVersion header))- )--getNumFrames :: Header -> Int-getNumFrames header =- let- key = textValue (stringToText "NumFrames")- properties = dictionaryValue (headerProperties header)- in case Map.lookup key properties of- Just (Property _ _ (IntProperty numFrames)) ->- fromIntegral (int32Value numFrames)- _ -> 0--getMaxChannels :: Header -> Word-getMaxChannels header =- let- key = textValue (stringToText "MaxChannels")- properties = dictionaryValue (headerProperties header)- in case Map.lookup key properties of- Just (Property _ _ (IntProperty numFrames)) ->- fromIntegral (int32Value numFrames)- _ -> 1023
@@ -1,103 +0,0 @@--- | 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'-{-# DEPRECATED-decodeReplayFile "use Data.ByteString.Lazy.readFile"- #-}--decodeReplayFile :: FilePath -> IO (Either String Replay)-decodeReplayFile file = IO.withBinaryFile file IO.ReadMode decodeReplayHandle---- | 'encodeJson'-{-# DEPRECATED-encodeJsonFile "use Data.ByteString.Lazy.writeFile"- #-}--encodeJsonFile :: Replay -> FilePath -> IO ()-encodeJsonFile replay file =- IO.withBinaryFile file IO.WriteMode (encodeJsonHandle replay)---- | 'decodeJson'-{-# DEPRECATED-decodeJsonFile "use Data.ByteString.Lazy.readFile"- #-}--decodeJsonFile :: FilePath -> IO (Either String Replay)-decodeJsonFile file = IO.withBinaryFile file IO.ReadMode decodeJsonHandle---- | 'encodeReplay'-{-# DEPRECATED-encodeReplayFile "use Data.ByteString.Lazy.writeFile"- #-}--encodeReplayFile :: Replay -> FilePath -> IO ()-encodeReplayFile replay file =- IO.withBinaryFile file IO.WriteMode (encodeReplayHandle replay)---- * Handles--- | 'decodeReplay'-{-# DEPRECATED-decodeReplayHandle "use Data.ByteString.Lazy.hGetContents"- #-}--decodeReplayHandle :: IO.Handle -> IO (Either String Replay)-decodeReplayHandle handle = do- contents <- ByteString.hGetContents handle- pure (decodeReplay contents)---- | 'encodeJson'-{-# DEPRECATED-encodeJsonHandle "use Data.ByteString.Lazy.hPut"- #-}--encodeJsonHandle :: Replay -> IO.Handle -> IO ()-encodeJsonHandle replay handle = ByteString.hPut handle (encodeJson replay)---- | 'decodeJson'-{-# DEPRECATED-decodeJsonHandle "use Data.ByteString.Lazy.hGetContents"- #-}--decodeJsonHandle :: IO.Handle -> IO (Either String Replay)-decodeJsonHandle handle = do- contents <- ByteString.hGetContents handle- pure (decodeJson contents)---- | 'encodeReplay'-{-# DEPRECATED-encodeReplayHandle "use Data.ByteString.Lazy.hPut"- #-}--encodeReplayHandle :: Replay -> IO.Handle -> IO ()-encodeReplayHandle replay handle = ByteString.hPut handle (encodeReplay replay)
@@ -1,38 +0,0 @@-module Rattletrap.Initialization where--import Rattletrap.Primitive--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--data Initialization = Initialization- { initializationLocation :: Maybe Vector- -- ^ Not every class has an initial location. See- -- 'Rattletrap.Data.rawClassesWithLocation'.- , initializationRotation :: Maybe Int8Vector- -- ^ Only classes with location can have rotation, but not every one does.- -- See 'Rattletrap.Data.rawClassesWithRotation'.- } deriving (Eq, Ord, Show)--getInitialization :: Bool -> Bool -> BinaryBit.BitGet Initialization-getInitialization hasLocation hasRotation = do- location <- if hasLocation- then do- location <- getVector- pure (Just location)- else pure Nothing- rotation <- if hasRotation- then do- rotation <- getInt8Vector- pure (Just rotation)- else pure Nothing- pure (Initialization location rotation)--putInitialization :: Initialization -> BinaryBit.BitPut ()-putInitialization initialization = do- case initializationLocation initialization of- Nothing -> pure ()- Just location -> putVector location- case initializationRotation initialization of- Nothing -> pure ()- Just rotation -> putInt8Vector rotation
@@ -1,112 +0,0 @@-{-# LANGUAGE TemplateHaskell #-}-{-# OPTIONS_GHC -fno-warn-orphans #-}--module Rattletrap.Json where--import Rattletrap.Attribute-import Rattletrap.AttributeMapping-import Rattletrap.AttributeValue-import Rattletrap.Cache-import Rattletrap.ClassMapping-import Rattletrap.Content-import Rattletrap.Frame-import Rattletrap.Header-import Rattletrap.Initialization-import Rattletrap.KeyFrame-import Rattletrap.Mark-import Rattletrap.Message-import Rattletrap.Primitive-import Rattletrap.Property-import Rattletrap.PropertyValue-import Rattletrap.RemoteId-import Rattletrap.Replay-import Rattletrap.Replication-import Rattletrap.ReplicationValue--import qualified Control.Monad as Monad-import qualified Data.Aeson.TH as Aeson-import qualified Data.Aeson.Types as Aeson-import qualified Language.Haskell.TH as TH--$(let toSnakeCase = Aeson.camelTo2 '_'- dropName name = drop (length (TH.nameBase name))- optionsFor name =- Aeson.defaultOptions- { Aeson.constructorTagModifier = toSnakeCase- , Aeson.fieldLabelModifier = toSnakeCase . dropName name- , Aeson.omitNothingFields = True- , Aeson.sumEncoding = Aeson.ObjectWithSingleField- , Aeson.unwrapUnaryRecords = True- }- deriveJSON declarations name = do- newDeclarations <- Aeson.deriveJSON (optionsFor name) name- pure (newDeclarations ++ declarations)- names =- [ ''AppliedDamageAttribute- , ''Attribute- , ''AttributeMapping- , ''AttributeValue- , ''BooleanAttribute- , ''ByteAttribute- , ''Cache- , ''CamSettingsAttribute- , ''ClassMapping- , ''ClubColorsAttribute- , ''CompressedWord- , ''CompressedWordVector- , ''Content- , ''DamageStateAttribute- , ''DemolishAttribute- , ''DestroyedReplication- , ''Dictionary- , ''EnumAttribute- , ''ExplosionAttribute- , ''ExtendedExplosionAttribute- , ''FlaggedIntAttribute- , ''Float32- , ''FloatAttribute- , ''Frame- , ''GameModeAttribute- , ''Header- , ''Initialization- , ''Int32- , ''Int8- , ''Int8Vector- , ''IntAttribute- , ''KeyFrame- , ''List- , ''LoadoutAttribute- , ''LoadoutOnlineAttribute- , ''LoadoutsAttribute- , ''LoadoutsOnlineAttribute- , ''LocationAttribute- , ''Mark- , ''Message- , ''MusicStingerAttribute- , ''PartyLeaderAttribute- , ''PickupAttribute- , ''PrivateMatchSettingsAttribute- , ''ProductAttribute- , ''Property- , ''PropertyValue- , ''QWordAttribute- , ''RemoteId- , ''Replay- , ''Replication- , ''ReplicationValue- , ''ReservationAttribute- , ''RigidBodyStateAttribute- , ''Section- , ''SpawnedReplication- , ''StringAttribute- , ''TeamPaintAttribute- , ''Text- , ''UniqueIdAttribute- , ''UpdatedReplication- , ''Vector- , ''WeldedInfoAttribute- , ''Word32- , ''Word64- , ''Word8- ]- in Monad.foldM deriveJSON [] names)
@@ -1,27 +0,0 @@-module Rattletrap.KeyFrame where--import Rattletrap.Primitive--import qualified Data.Binary as Binary--data KeyFrame = KeyFrame- { keyFrameTime :: Float32- -- ^ When this key frame occurs, in seconds.- , keyFrameFrame :: Word32- -- ^ The frame number of this key frame, starting from 0.- , keyFramePosition :: Word32- -- ^ The bit position of this key frame in the stream.- } deriving (Eq, Ord, Show)--getKeyFrame :: Binary.Get KeyFrame-getKeyFrame = do- time <- getFloat32- frame <- getWord32- position <- getWord32- pure (KeyFrame time frame position)--putKeyFrame :: KeyFrame -> Binary.Put-putKeyFrame keyFrame = do- putFloat32 (keyFrameTime keyFrame)- putWord32 (keyFrameFrame keyFrame)- putWord32 (keyFramePosition keyFrame)
@@ -1,60 +0,0 @@-module Rattletrap.Main where--import Rattletrap.Helper-import Rattletrap.Version--import qualified Data.ByteString.Lazy as ByteString-import qualified Data.Version as Version-import qualified Network.HTTP.Client as Client-import qualified Network.HTTP.Client.TLS as Client-import qualified System.Environment as Environment---- | Gets command-line arguments and passes them to 'mainWithArgs'.-main :: IO ()-main = do- args <- Environment.getArgs- mainWithArgs args---- | Runs the main command-line interface. Can be used in one of three modes:------ 1. @mainWithArgs ["version"]@: Prints out the version number and exits.--- Mostly useful for debugging.--- 2. @mainWithArgs ["decode"]@: Parses a raw replay into JSON. By default--- this reads from STDIN and writes to STDOUT. To read from a file, pass a--- second argument like @mainWithArgs ["decode", "input.replay"]@. To write--- to a file, pass a third argument like--- @mainWithArgs ["decode", "input.replay", "output.json"]@.--- 3. @mainWithArgs ["encode"]@: Generates a raw replay from JSON. The--- handling of input and output is the same as decoding.-mainWithArgs :: [String] -> IO ()-mainWithArgs args = case args of- ["version"] -> putStrLn (Version.showVersion version)- action:files -> do- (getInput, putOutput) <- getIO files- input <- getInput- output <- case action of- "decode" -> case decodeReplay input of- Left message -> fail message- Right replay -> pure (encodeJson replay)- "encode" -> case decodeJson input of- Left message -> fail message- Right replay -> pure (encodeReplay replay)- _ -> fail ("unknown action: " ++ show action)- putOutput output- _ -> fail ("unknown arguments: " ++ show args)--getIO- :: [FilePath] -> IO (IO ByteString.ByteString, ByteString.ByteString -> IO ())-getIO files = case files of- [] -> pure (ByteString.getContents, ByteString.putStr)- [i] -> pure (readUrlOrFile i, ByteString.putStr)- [i, o] -> pure (readUrlOrFile i, ByteString.writeFile o)- _ -> fail ("unknown files: " ++ show files)--readUrlOrFile :: FilePath -> IO ByteString.ByteString-readUrlOrFile i = case Client.parseUrlThrow i of- Just request -> do- manager <- Client.newTlsManager- response <- Client.httpLbs request manager- pure (Client.responseBody response)- Nothing -> ByteString.readFile i
@@ -1,23 +0,0 @@-module Rattletrap.Mark where--import Rattletrap.Primitive--import qualified Data.Binary as Binary--data Mark = Mark- { markValue :: Text- -- ^ Which type of mark this is, like @Team0Goal@.- , markFrame :: Word32- -- ^ Which frame this mark belongs to, starting from 0.- } deriving (Eq, Ord, Show)--getMark :: Binary.Get Mark-getMark = do- value <- getText- frame <- getWord32- pure (Mark value frame)--putMark :: Mark -> Binary.Put-putMark mark = do- putText (markValue mark)- putWord32 (markFrame mark)
@@ -1,27 +0,0 @@-module Rattletrap.Message where--import Rattletrap.Primitive--import qualified Data.Binary as Binary--data Message = Message- { messageFrame :: Word32- -- ^ Which frame this message belongs to, starting from 0.- , messageName :: Text- -- ^ The primary player's name.- , messageValue :: Text- -- ^ The content of the message.- } deriving (Eq, Ord, Show)--getMessage :: Binary.Get Message-getMessage = do- frame <- getWord32- name <- getText- value <- getText- pure (Message frame name value)--putMessage :: Message -> Binary.Put-putMessage message = do- putWord32 (messageFrame message)- putText (messageName message)- putText (messageValue message)
@@ -1,31 +0,0 @@-module Rattletrap.Primitive- ( module Rattletrap.Primitive.CompressedWord- , module Rattletrap.Primitive.CompressedWordVector- , module Rattletrap.Primitive.Dictionary- , module Rattletrap.Primitive.Float32- , module Rattletrap.Primitive.Int8- , module Rattletrap.Primitive.Int8Vector- , module Rattletrap.Primitive.Int32- , module Rattletrap.Primitive.List- , module Rattletrap.Primitive.Section- , module Rattletrap.Primitive.Text- , module Rattletrap.Primitive.Vector- , module Rattletrap.Primitive.Word8- , module Rattletrap.Primitive.Word32- , module Rattletrap.Primitive.Word64- ) where--import Rattletrap.Primitive.CompressedWord-import Rattletrap.Primitive.CompressedWordVector-import Rattletrap.Primitive.Dictionary-import Rattletrap.Primitive.Float32-import Rattletrap.Primitive.Int32-import Rattletrap.Primitive.Int8-import Rattletrap.Primitive.Int8Vector-import Rattletrap.Primitive.List-import Rattletrap.Primitive.Section-import Rattletrap.Primitive.Text-import Rattletrap.Primitive.Vector-import Rattletrap.Primitive.Word32-import Rattletrap.Primitive.Word64-import Rattletrap.Primitive.Word8
@@ -1,49 +0,0 @@-module Rattletrap.Primitive.CompressedWord where--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit-import qualified Data.Bits as Bits--data CompressedWord = CompressedWord- { compressedWordLimit :: Word- , compressedWordValue :: Word- } deriving (Eq, Ord, Show)--getCompressedWord :: Word -> BinaryBit.BitGet CompressedWord-getCompressedWord limit = do- value <- getCompressedWordStep limit (getMaxBits limit) 0 0- pure (CompressedWord limit value)--putCompressedWord :: CompressedWord -> BinaryBit.BitPut ()-putCompressedWord compressedWord = do- let limit = compressedWordLimit compressedWord- let value = compressedWordValue compressedWord- let maxBits = getMaxBits limit- let- go position soFar = if position < maxBits- then do- let x = Bits.shiftL 1 position- if maxBits > 1 && position == maxBits - 1 && soFar + x > limit- then pure ()- else do- let bit = Bits.testBit value position- BinaryBit.putBool bit- let delta = if bit then x else 0- go (position + 1) (soFar + delta)- else pure ()- go 0 0--getMaxBits :: (Integral a, Integral b) => a -> b-getMaxBits x = do- let n = max 1 (ceiling (logBase (2 :: Double) (fromIntegral (max 1 x))))- if x < 1024 && x == 2 ^ n then n + 1 else n--getCompressedWordStep :: Word -> Word -> Word -> Word -> BinaryBit.BitGet Word-getCompressedWordStep limit maxBits position value = do- let x = Bits.shiftL 1 (fromIntegral position)- if position < maxBits && value + x <= limit- then do- bit <- BinaryBit.getBool- let newValue = if bit then value + x else value- getCompressedWordStep limit maxBits (position + 1) newValue- else pure value
@@ -1,26 +0,0 @@-module Rattletrap.Primitive.CompressedWordVector where--import Rattletrap.Primitive.CompressedWord--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--data CompressedWordVector = CompressedWordVector- { compressedWordVectorX :: CompressedWord- , compressedWordVectorY :: CompressedWord- , compressedWordVectorZ :: CompressedWord- } deriving (Eq, Ord, Show)--getCompressedWordVector :: BinaryBit.BitGet CompressedWordVector-getCompressedWordVector = do- let limit = 65536- x <- getCompressedWord limit- y <- getCompressedWord limit- z <- getCompressedWord limit- pure (CompressedWordVector x y z)--putCompressedWordVector :: CompressedWordVector -> BinaryBit.BitPut ()-putCompressedWordVector compressedWordVector = do- putCompressedWord (compressedWordVectorX compressedWordVector)- putCompressedWord (compressedWordVectorY compressedWordVector)- putCompressedWord (compressedWordVectorZ compressedWordVector)
@@ -1,60 +0,0 @@-module Rattletrap.Primitive.Dictionary where--import Rattletrap.Primitive.Text--import qualified Data.Binary as Binary-import qualified Data.Map as Map-import qualified Data.Text as Text--data Dictionary a = Dictionary- { dictionaryKeys :: [Text]- -- ^ Objects in JSON aren't ordered, so the order of the keys must be stored- -- separately.- , dictionaryLastKey :: Text- -- ^ The last key is usually @None@ but sometimes contains extra null bytes.- , dictionaryValue :: Map.Map Text.Text a- -- ^ Be sure to update 'dictionaryKeys' if you add, change, or remove a key- -- in this map.- } deriving (Eq, Ord, Show)--getDictionary :: Binary.Get a -> Binary.Get (Dictionary a)-getDictionary getValue = do- (elements, lastKey) <- getElements getValue- let keys = map fst elements- let value = Map.mapKeys textValue (Map.fromList elements)- pure (Dictionary keys lastKey value)--getElements :: Binary.Get a -> Binary.Get ([(Text, a)], Text)-getElements getValue = do- (key, maybeValue) <- getElement getValue- case maybeValue of- Nothing -> pure ([], key)- Just value -> do- let element = (key, value)- (elements, lastKey) <- getElements getValue- pure (element : elements, lastKey)--getElement :: Binary.Get a -> Binary.Get (Text, Maybe a)-getElement getValue = do- key <- getText- if isNoneKey key- then pure (key, Nothing)- else do- value <- getValue- pure (key, Just value)--isNoneKey :: Text -> Bool-isNoneKey text = filter (/= '\x00') (textToString text) == "None"--putDictionary :: (a -> Binary.Put) -> Dictionary a -> Binary.Put-putDictionary putValue dictionary = do- let elements = dictionaryValue dictionary- mapM_- ( \key -> do- putText key- case Map.lookup (textValue key) elements of- Nothing -> fail ("could not find key " ++ textToString key)- Just value -> putValue value- )- (dictionaryKeys dictionary)- putText (dictionaryLastKey dictionary)
@@ -1,33 +0,0 @@-module Rattletrap.Primitive.Float32 where--import Rattletrap.Utility--import qualified Data.Binary as Binary-import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit-import qualified Data.Binary.Get as Binary-import qualified Data.Binary.IEEE754 as IEEE754-import qualified Data.Binary.Put as Binary-import qualified Data.ByteString.Lazy as ByteString--newtype Float32 = Float32- { float32Value :: Float- } deriving (Eq, Ord, Show)--getFloat32 :: Binary.Get Float32-getFloat32 = do- float32 <- IEEE754.getFloat32le- pure (Float32 float32)--putFloat32 :: Float32 -> Binary.Put-putFloat32 float32 = IEEE754.putFloat32le (float32Value float32)--getFloat32Bits :: BinaryBit.BitGet Float32-getFloat32Bits = do- bytes <- BinaryBit.getLazyByteString 4- pure (Binary.runGet getFloat32 (reverseBytes bytes))--putFloat32Bits :: Float32 -> BinaryBit.BitPut ()-putFloat32Bits float32 = do- let bytes = Binary.runPut (putFloat32 float32)- BinaryBit.putByteString (ByteString.toStrict (reverseBytes bytes))
@@ -1,33 +0,0 @@-module Rattletrap.Primitive.Int32 where--import Rattletrap.Utility--import qualified Data.Binary as Binary-import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit-import qualified Data.Binary.Get as Binary-import qualified Data.Binary.Put as Binary-import qualified Data.ByteString.Lazy as ByteString-import qualified Data.Int as Int--newtype Int32 = Int32- { int32Value :: Int.Int32- } deriving (Eq, Ord, Show)--getInt32 :: Binary.Get Int32-getInt32 = do- int32 <- Binary.getInt32le- pure (Int32 int32)--putInt32 :: Int32 -> Binary.Put-putInt32 int32 = Binary.putInt32le (int32Value int32)--getInt32Bits :: BinaryBit.BitGet Int32-getInt32Bits = do- bytes <- BinaryBit.getLazyByteString 4- pure (Binary.runGet getInt32 (reverseBytes bytes))--putInt32Bits :: Int32 -> BinaryBit.BitPut ()-putInt32Bits int32 = do- let bytes = Binary.runPut (putInt32 int32)- BinaryBit.putByteString (ByteString.toStrict (reverseBytes bytes))
@@ -1,32 +0,0 @@-module Rattletrap.Primitive.Int8 where--import Rattletrap.Utility--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit-import qualified Data.Binary.Get as Binary-import qualified Data.Binary.Put as Binary-import qualified Data.ByteString.Lazy as ByteString-import qualified Data.Int as Int--newtype Int8 = Int8- { int8Value :: Int.Int8- } deriving (Eq, Ord, Show)--getInt8 :: Binary.Get Int8-getInt8 = do- int8 <- Binary.getInt8- pure (Int8 int8)--putInt8 :: Int8 -> Binary.Put-putInt8 int8 = Binary.putInt8 (int8Value int8)--getInt8Bits :: BinaryBit.BitGet Int8-getInt8Bits = do- bytes <- BinaryBit.getLazyByteString 1- pure (Binary.runGet getInt8 (reverseBytes bytes))--putInt8Bits :: Int8 -> BinaryBit.BitPut ()-putInt8Bits int8 = do- let bytes = Binary.runPut (putInt8 int8)- BinaryBit.putByteString (ByteString.toStrict (reverseBytes bytes))
@@ -1,41 +0,0 @@-module Rattletrap.Primitive.Int8Vector where--import Rattletrap.Primitive.Int8--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--data Int8Vector = Int8Vector- { int8VectorX :: Maybe Int8- , int8VectorY :: Maybe Int8- , int8VectorZ :: Maybe Int8- } deriving (Eq, Ord, Show)--getInt8Vector :: BinaryBit.BitGet Int8Vector-getInt8Vector = do- x <- getInt8VectorField- y <- getInt8VectorField- z <- getInt8VectorField- pure (Int8Vector x y z)--putInt8Vector :: Int8Vector -> BinaryBit.BitPut ()-putInt8Vector int8Vector = do- putInt8VectorField (int8VectorX int8Vector)- putInt8VectorField (int8VectorY int8Vector)- putInt8VectorField (int8VectorZ int8Vector)--getInt8VectorField :: BinaryBit.BitGet (Maybe Int8)-getInt8VectorField = do- hasField <- BinaryBit.getBool- if hasField- then do- field <- getInt8Bits- pure (Just field)- else pure Nothing--putInt8VectorField :: Maybe Int8 -> BinaryBit.BitPut ()-putInt8VectorField maybeField = case maybeField of- Nothing -> BinaryBit.putBool False- Just field -> do- BinaryBit.putBool True- putInt8Bits field
@@ -1,22 +0,0 @@-module Rattletrap.Primitive.List where--import Rattletrap.Primitive.Word32--import qualified Control.Monad as Monad-import qualified Data.Binary as Binary--newtype List a = List- { listValue :: [a]- } deriving (Eq, Ord, Show)--getList :: Binary.Get a -> Binary.Get (List a)-getList getElement = do- size <- getWord32- elements <- Monad.replicateM (fromIntegral (word32Value size)) getElement- pure (List elements)--putList :: (a -> Binary.Put) -> List a -> Binary.Put-putList putElement list = do- let elements = listValue list- putWord32 (Word32 (fromIntegral (length elements)))- mapM_ putElement elements
@@ -1,54 +0,0 @@-module Rattletrap.Primitive.Section where--import Rattletrap.Crc-import Rattletrap.Primitive.Word32--import qualified Control.Monad as Monad-import qualified Data.Binary as Binary-import qualified Data.Binary.Get as Binary-import qualified Data.Binary.Put as Binary-import qualified Data.ByteString.Lazy as ByteString---- | A section is a large piece of a 'Rattletrap.Replay.Replay'. It has a--- 32-bit size (in bytes), a 32-bit CRC (see "Rattletrap.Crc"), and then a--- bunch of data (the body). This interface is provided so that you don't have--- to think about the size and CRC.-newtype Section a = Section- { sectionBody :: a- -- ^ The actual content in the section.- } deriving (Eq, Ord, Show)---- | Given a way to get the 'sectionBody', gets a section. This will fail if--- the section's CRC does not match.------ @--- let header = 'Data.Binary.Get.runGet' ('getSection' 'Rattletrap.Header.getHeader') bytes--- @-getSection :: Binary.Get a -> Binary.Get (Section a)-getSection getBody = do- size <- getWord32- crc <- getWord32- rawBody <- Binary.getLazyByteString (fromIntegral (word32Value size))- let actualCrc = Word32 (getCrc32 rawBody)- Monad.when (actualCrc /= crc) (fail (crcMessage actualCrc crc))- let body = Binary.runGet getBody rawBody- pure (Section body)---- | Given a way to put the 'sectionBody', puts a section. This will also put--- the size and CRC.------ @--- let bytes = 'Data.Binary.Put.runPut' ('putSection' 'Rattletrap.Content.putContent' content)--- @-putSection :: (a -> Binary.Put) -> Section a -> Binary.Put-putSection putBody section = do- let rawBody = Binary.runPut (putBody (sectionBody section))- let size = ByteString.length rawBody- let crc = getCrc32 rawBody- putWord32 (Word32 (fromIntegral size))- putWord32 (Word32 crc)- Binary.putLazyByteString rawBody--crcMessage :: Word32 -> Word32 -> String-crcMessage actual expected = unwords- ["actual CRC", show actual, "does not match expected CRC", show expected]
@@ -1,94 +0,0 @@-module Rattletrap.Primitive.Text where--import Rattletrap.Primitive.Int32-import Rattletrap.Utility--import qualified Data.Binary as Binary-import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit-import qualified Data.Binary.Get as Binary-import qualified Data.Binary.Put as Binary-import qualified Data.ByteString.Lazy as ByteString-import qualified Data.Char as Char-import qualified Data.Text as Text-import qualified Data.Text.Encoding as Encoding--newtype Text = Text- { textValue :: Text.Text- } deriving (Eq, Ord, Show)--getText :: Binary.Get Text-getText = do- rawSize <- getInt32- let decode = getTextDecoder rawSize- let size = normalizeTextSize rawSize- bytes <- Binary.getLazyByteString size- let text = dropNull (decode bytes)- pure (Text text)--putText :: Text -> Binary.Put-putText text = do- let size = getTextSize text- let encode = getTextEncoder size- putInt32 size- Binary.putLazyByteString (encode (addNull (textValue text)))--getTextBits :: BinaryBit.BitGet Text-getTextBits = do- rawSize <- getInt32Bits- let decode = getTextDecoder rawSize- let size = normalizeTextSize rawSize- bytes <- BinaryBit.getLazyByteString size- let text = dropNull (decode (reverseBytes bytes))- pure (Text text)--putTextBits :: Text -> BinaryBit.BitPut ()-putTextBits text = do- let size = getTextSize text- let encode = getTextEncoder size- putInt32Bits size- BinaryBit.putByteString- (ByteString.toStrict (reverseBytes (encode (addNull (textValue text)))))--stringToText :: String -> Text-stringToText string = Text (Text.pack string)--textToString :: Text -> String-textToString text = Text.unpack (textValue text)--getTextSize :: Text -> Int32-getTextSize text =- let- value = textValue text- scale = if Text.all Char.isLatin1 value then 1 else -1- rawSize =- if Text.null value then 0 else fromIntegral (Text.length value) + 1- size = if value == Text.pack "\x00\x00\x00None"- then 0x05000000- else scale * rawSize- in- Int32 size--normalizeTextSize :: Integral a => Int32 -> a-normalizeTextSize size = case int32Value size of- 0x05000000 -> 8- x -> if x < 0 then (-2 * fromIntegral x) else fromIntegral x--getTextDecoder :: Int32 -> ByteString.ByteString -> Text.Text-getTextDecoder size bytes =- let- decode =- if size < Int32 0 then Encoding.decodeUtf16LE else Encoding.decodeLatin1- in- decode (ByteString.toStrict bytes)--getTextEncoder :: Int32 -> Text.Text -> ByteString.ByteString-getTextEncoder size text = if size < Int32 0- then ByteString.fromStrict (Encoding.encodeUtf16LE text)- else encodeLatin1 text--dropNull :: Text.Text -> Text.Text-dropNull = Text.dropWhileEnd (== '\x00')--addNull :: Text.Text -> Text.Text-addNull text = if Text.null text then text else Text.snoc text '\x00'
@@ -1,41 +0,0 @@-module Rattletrap.Primitive.Vector where--import Rattletrap.Primitive.CompressedWord--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--data Vector = Vector- { vectorBias :: Word- , vectorX :: Int- , vectorY :: Int- , vectorZ :: Int- } deriving (Eq, Ord, Show)--getVector :: BinaryBit.BitGet Vector-getVector = do- bitSize <- getCompressedWord 19- let limit = 2 ^ (compressedWordValue bitSize + 2)- dx <- getCompressedWord limit- dy <- getCompressedWord limit- dz <- getCompressedWord limit- let fromCompressedWord x = fromIntegral (compressedWordValue x)- let bias = 2 ^ (fromCompressedWord bitSize + 1 :: Word)- let x = fromCompressedWord dx - fromIntegral bias- let y = fromCompressedWord dy - fromIntegral bias- let z = fromCompressedWord dz - fromIntegral bias- pure (Vector bias x y z)--putVector :: Vector -> BinaryBit.BitPut ()-putVector vector = do- let- bitSize =- round (logBase (2 :: Float) (fromIntegral (vectorBias vector))) - 1- putCompressedWord (CompressedWord 19 bitSize)- let dx = fromIntegral (vectorX vector + fromIntegral (vectorBias vector))- let dy = fromIntegral (vectorY vector + fromIntegral (vectorBias vector))- let dz = fromIntegral (vectorZ vector + fromIntegral (vectorBias vector))- let limit = 2 ^ (bitSize + 2)- putCompressedWord (CompressedWord limit dx)- putCompressedWord (CompressedWord limit dy)- putCompressedWord (CompressedWord limit dz)
@@ -1,33 +0,0 @@-module Rattletrap.Primitive.Word32 where--import Rattletrap.Utility--import qualified Data.Binary as Binary-import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit-import qualified Data.Binary.Get as Binary-import qualified Data.Binary.Put as Binary-import qualified Data.ByteString.Lazy as ByteString-import qualified Data.Word as Word--newtype Word32 = Word32- { word32Value :: Word.Word32- } deriving (Eq, Ord, Show)--getWord32 :: Binary.Get Word32-getWord32 = do- word32 <- Binary.getWord32le- pure (Word32 word32)--putWord32 :: Word32 -> Binary.Put-putWord32 word32 = Binary.putWord32le (word32Value word32)--getWord32Bits :: BinaryBit.BitGet Word32-getWord32Bits = do- bytes <- BinaryBit.getLazyByteString 4- pure (Binary.runGet getWord32 (reverseBytes bytes))--putWord32Bits :: Word32 -> BinaryBit.BitPut ()-putWord32Bits word32 = do- let bytes = Binary.runPut (putWord32 word32)- BinaryBit.putByteString (ByteString.toStrict (reverseBytes bytes))
@@ -1,33 +0,0 @@-module Rattletrap.Primitive.Word64 where--import Rattletrap.Utility--import qualified Data.Binary as Binary-import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit-import qualified Data.Binary.Get as Binary-import qualified Data.Binary.Put as Binary-import qualified Data.ByteString.Lazy as ByteString-import qualified Data.Word as Word--newtype Word64 = Word64- { word64Value :: Word.Word64- } deriving (Eq, Ord, Show)--getWord64 :: Binary.Get Word64-getWord64 = do- word64 <- Binary.getWord64le- pure (Word64 word64)--putWord64 :: Word64 -> Binary.Put-putWord64 word64 = Binary.putWord64le (word64Value word64)--getWord64Bits :: BinaryBit.BitGet Word64-getWord64Bits = do- bytes <- BinaryBit.getLazyByteString 8- pure (Binary.runGet getWord64 (reverseBytes bytes))--putWord64Bits :: Word64 -> BinaryBit.BitPut ()-putWord64Bits word64 = do- let bytes = Binary.runPut (putWord64 word64)- BinaryBit.putByteString (ByteString.toStrict (reverseBytes bytes))
@@ -1,33 +0,0 @@-module Rattletrap.Primitive.Word8 where--import Rattletrap.Utility--import qualified Data.Binary as Binary-import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit-import qualified Data.Binary.Get as Binary-import qualified Data.Binary.Put as Binary-import qualified Data.ByteString.Lazy as ByteString-import qualified Data.Word as Word--newtype Word8 = Word8- { word8Value :: Word.Word8- } deriving (Eq, Ord, Show)--getWord8 :: Binary.Get Word8-getWord8 = do- word8 <- Binary.getWord8- pure (Word8 word8)--putWord8 :: Word8 -> Binary.Put-putWord8 word8 = Binary.putWord8 (word8Value word8)--getWord8Bits :: BinaryBit.BitGet Word8-getWord8Bits = do- bytes <- BinaryBit.getLazyByteString 1- pure (Binary.runGet getWord8 (reverseBytes bytes))--putWord8Bits :: Word8 -> BinaryBit.BitPut ()-putWord8Bits word8 = do- let bytes = Binary.runPut (putWord8 word8)- BinaryBit.putByteString (ByteString.toStrict (reverseBytes bytes))
@@ -1,26 +0,0 @@-module Rattletrap.Property where--import Rattletrap.Primitive-import Rattletrap.PropertyValue--import qualified Data.Binary as Binary--data Property = Property- { propertyKind :: Text- , propertySize :: Word64- -- ^ Not used.- , propertyValue :: PropertyValue Property- } deriving (Eq, Ord, Show)--getProperty :: Binary.Get Property-getProperty = do- kind <- getText- size <- getWord64- value <- getPropertyValue getProperty kind- pure (Property kind size value)--putProperty :: Property -> Binary.Put-putProperty property = do- putText (propertyKind property)- putWord64 (propertySize property)- putPropertyValue putProperty (propertyValue property)
@@ -1,69 +0,0 @@-module Rattletrap.PropertyValue where--import Rattletrap.Primitive--import qualified Data.Binary as Binary--data PropertyValue a- = ArrayProperty (List (Dictionary a))- -- ^ Yes, a list of dictionaries. No, it doesn't make sense. These usually- -- only have one element.- | BoolProperty Word8- | ByteProperty Text- (Maybe Text)- -- ^ This is a strange name for essentially a key-value pair.- | FloatProperty Float32- | IntProperty Int32- | NameProperty Text- -- ^ It's unclear how exactly this is different than a 'StrProperty'.- | QWordProperty Word64- | StrProperty Text- deriving (Eq, Ord, Show)--getPropertyValue :: Binary.Get a -> Text -> Binary.Get (PropertyValue a)-getPropertyValue getProperty kind = case textToString kind of- "ArrayProperty" -> do- list <- getList (getDictionary getProperty)- pure (ArrayProperty list)- "BoolProperty" -> do- word8 <- getWord8- pure (BoolProperty word8)- "ByteProperty" -> do- k <- getText- v <- if textToString k == "OnlinePlatform_Steam"- then pure Nothing- else do- v <- getText- pure (Just v)- pure (ByteProperty k v)- "FloatProperty" -> do- float32 <- getFloat32- pure (FloatProperty float32)- "IntProperty" -> do- int32 <- getInt32- pure (IntProperty int32)- "NameProperty" -> do- text <- getText- pure (NameProperty text)- "QWordProperty" -> do- word64 <- getWord64- pure (QWordProperty word64)- "StrProperty" -> do- text <- getText- pure (StrProperty text)- _ -> fail ("don't know how to read property value " ++ show kind)--putPropertyValue :: (a -> Binary.Put) -> PropertyValue a -> Binary.Put-putPropertyValue putProperty value = case value of- ArrayProperty list -> putList (putDictionary putProperty) list- BoolProperty word8 -> putWord8 word8- ByteProperty k mv -> do- putText k- case mv of- Nothing -> pure ()- Just v -> putText v- FloatProperty float32 -> putFloat32 float32- IntProperty int32 -> putInt32 int32- NameProperty text -> putText text- QWordProperty word64 -> putWord64 word64- StrProperty text -> putText text
@@ -1,53 +0,0 @@-module Rattletrap.RemoteId where--import Rattletrap.Primitive-import Rattletrap.Utility--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit-import qualified Data.ByteString.Lazy as ByteString-import qualified Data.Text as Text-import qualified Data.Text.Encoding as Encoding-import qualified Data.Word as Word--data RemoteId- = PlayStationId Text.Text- [Word.Word8]- | SplitscreenId Word.Word32- | SteamId Word64- | XboxId Word64- deriving (Eq, Ord, Show)--getRemoteId :: (Int, Int, Int) -> Word8 -> BinaryBit.BitGet RemoteId-getRemoteId (_, _, patchVersion) systemId = case word8Value systemId of- 0 -> do- word24 <- BinaryBit.getWord32be 24- pure (SplitscreenId word24)- 1 -> do- word64 <- getWord64Bits- pure (SteamId word64)- 2 -> do- rawName <- BinaryBit.getLazyByteString 16- let- name = Text.dropWhileEnd- (== '\x00')- (Encoding.decodeLatin1 (ByteString.toStrict (reverseBytes rawName)))- numBytes = if patchVersion >= 1 then 24 else 16- bytes <- BinaryBit.getLazyByteString numBytes- pure (PlayStationId name (ByteString.unpack bytes))- 4 -> do- word64 <- getWord64Bits- pure (XboxId word64)- _ -> fail ("unknown system id " ++ show systemId)--putRemoteId :: RemoteId -> BinaryBit.BitPut ()-putRemoteId remoteId = case remoteId of- PlayStationId name bytes -> do- let- rawName = ByteString.toStrict- (reverseBytes (padBytes (16 :: Int) (encodeLatin1 name)))- BinaryBit.putByteString rawName- BinaryBit.putByteString (ByteString.toStrict (ByteString.pack bytes))- SplitscreenId word24 -> BinaryBit.putWord32be 24 word24- SteamId word64 -> putWord64Bits word64- XboxId word64 -> putWord64Bits word64
@@ -1,39 +0,0 @@-module Rattletrap.Replay where--import Rattletrap.Content-import Rattletrap.Header-import Rattletrap.Primitive.Section--import qualified Data.Binary as Binary---- | A Rocket League replay.-data Replay = Replay- { replayHeader :: Section Header- -- ^ This has most of the high-level metadata.- , replayContent :: Section Content- -- ^ This has most of the low-level game data.- } deriving (Eq, Ord, Show)---- | Parses a raw replay. Use this with 'Data.Binary.Get.runGet'.------ @--- let replay = 'Data.Binary.Get.runGet' 'getReplay' bytes--- @-getReplay :: Binary.Get Replay-getReplay = do- header <- getSection getHeader- let version = getVersion (sectionBody header)- let numFrames = getNumFrames (sectionBody header)- let maxChannels = getMaxChannels (sectionBody header)- content <- getSection (getContent version numFrames maxChannels)- pure (Replay header content)---- | Generates a raw replay. Use this with 'Data.Binary.Put.runPut'.------ @--- let bytes = 'Data.Binary.Put.runPut' ('putReplay' replay)--- @-putReplay :: Replay -> Binary.Put-putReplay replay = do- putSection putHeader (replayHeader replay)- putSection putContent (replayContent replay)
@@ -1,66 +0,0 @@-module Rattletrap.Replication where--import Rattletrap.ActorMap-import Rattletrap.ClassAttributeMap-import Rattletrap.Primitive-import Rattletrap.ReplicationValue--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--data Replication = Replication- { replicationActorId :: CompressedWord- , replicationValue :: ReplicationValue- } deriving (Eq, Ord, Show)--getReplications- :: (Int, Int, Int)- -> Word- -> ClassAttributeMap- -> ActorMap- -> BinaryBit.BitGet ([Replication], ActorMap)-getReplications version maxChannels classAttributeMap actorMap = do- maybeReplication <- getReplication- version- maxChannels- classAttributeMap- actorMap- case maybeReplication of- Nothing -> pure ([], actorMap)- Just (replication, newActorMap) -> do- (replications, newerActorMap) <- getReplications- version- maxChannels- classAttributeMap- newActorMap- pure (replication : replications, newerActorMap)--putReplications :: [Replication] -> BinaryBit.BitPut ()-putReplications replications = do- mapM_ putReplication replications- BinaryBit.putBool False--getReplication- :: (Int, Int, Int)- -> Word- -> ClassAttributeMap- -> ActorMap- -> BinaryBit.BitGet (Maybe (Replication, ActorMap))-getReplication version maxChannels classAttributeMap actorMap = do- hasReplication <- BinaryBit.getBool- if not hasReplication- then pure Nothing- else do- actorId <- getCompressedWord maxChannels- (value, newActorMap) <- getReplicationValue- version- classAttributeMap- actorMap- actorId- pure (Just (Replication actorId value, newActorMap))--putReplication :: Replication -> BinaryBit.BitPut ()-putReplication replication = do- BinaryBit.putBool True- putCompressedWord (replicationActorId replication)- putReplicationValue (replicationValue replication)
@@ -1,17 +0,0 @@-module Rattletrap.Replication.Destroyed where--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit---- | Destroyed replications don't actually contain any extra information. All--- you need to know is the actor's ID, which is given by the--- 'Rattletrap.Replication.Replication'.-data DestroyedReplication = DestroyedReplication- {- } deriving (Eq, Ord, Show)--getDestroyedReplication :: BinaryBit.BitGet DestroyedReplication-getDestroyedReplication = pure DestroyedReplication--putDestroyedReplication :: DestroyedReplication -> BinaryBit.BitPut ()-putDestroyedReplication _ = pure ()
@@ -1,88 +0,0 @@-module Rattletrap.Replication.Spawned where--import Rattletrap.ActorMap-import Rattletrap.ClassAttributeMap-import Rattletrap.Initialization-import Rattletrap.Primitive--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit-import qualified Data.Map as Map--data SpawnedReplication = SpawnedReplication- { spawnedReplicationFlag :: Bool- -- ^ Unclear what this is.- , spawnedReplicationNameIndex :: Maybe Word32- , spawnedReplicationName :: Maybe Text- -- ^ Read-only! Changing a replication's name requires editing the- -- 'spawnedReplicationNameIndex' and maybe the class attribute map.- , spawnedReplicationObjectId :: Word32- , spawnedReplicationObjectName :: Text- -- ^ Read-only! Changing a replication's object requires editing the class- -- attribute map.- , spawnedReplicationClassName :: Text- -- ^ Read-only! Changing a replication's class requires editing the class- -- attribute map.- , spawnedReplicationInitialization :: Initialization- } deriving (Eq, Ord, Show)--getSpawnedReplication- :: (Int, Int, Int)- -> ClassAttributeMap- -> ActorMap- -> CompressedWord- -> BinaryBit.BitGet (SpawnedReplication, ActorMap)-getSpawnedReplication version classAttributeMap actorMap actorId = do- flag <- BinaryBit.getBool- nameIndex <- if version < (868, 14, 0)- then pure Nothing- else do- nameIndex <- getWord32Bits- pure (Just nameIndex)- name <- lookupName classAttributeMap nameIndex- objectId <- getWord32Bits- let newActorMap = Map.insert actorId objectId actorMap- objectName <- lookupObjectName classAttributeMap objectId- className <- lookupClassName objectName- let hasLocation = classHasLocation className- let hasRotation = classHasRotation className- initialization <- getInitialization hasLocation hasRotation- pure- ( SpawnedReplication- flag- nameIndex- name- objectId- objectName- className- initialization- , newActorMap- )--putSpawnedReplication :: SpawnedReplication -> BinaryBit.BitPut ()-putSpawnedReplication spawnedReplication = do- BinaryBit.putBool (spawnedReplicationFlag spawnedReplication)- case spawnedReplicationNameIndex spawnedReplication of- Nothing -> pure ()- Just nameIndex -> putWord32Bits nameIndex- putWord32Bits (spawnedReplicationObjectId spawnedReplication)- putInitialization (spawnedReplicationInitialization spawnedReplication)--lookupName :: Monad m => ClassAttributeMap -> Maybe Word32 -> m (Maybe Text)-lookupName classAttributeMap maybeNameIndex = case maybeNameIndex of- Nothing -> pure Nothing- Just nameIndex ->- case getName (classAttributeMapNameMap classAttributeMap) nameIndex of- Nothing -> fail ("could not get name for index " ++ show nameIndex)- Just name -> pure (Just name)--lookupObjectName :: Monad m => ClassAttributeMap -> Word32 -> m Text-lookupObjectName classAttributeMap objectId =- case getObjectName (classAttributeMapObjectMap classAttributeMap) objectId of- Nothing -> fail ("could not get object name for id " ++ show objectId)- Just objectName -> pure objectName--lookupClassName :: Monad m => Text -> m Text-lookupClassName objectName = case getClassName objectName of- Nothing -> fail ("could not get class name for object " ++ show objectName)- Just className -> pure className
@@ -1,27 +0,0 @@-module Rattletrap.Replication.Updated where--import Rattletrap.ActorMap-import Rattletrap.Attribute-import Rattletrap.ClassAttributeMap-import Rattletrap.Primitive--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--newtype UpdatedReplication = UpdatedReplication- { updatedReplicationAttributes :: [Attribute]- } deriving (Eq, Ord, Show)--getUpdatedReplication- :: (Int, Int, Int)- -> ClassAttributeMap- -> ActorMap- -> CompressedWord- -> BinaryBit.BitGet UpdatedReplication-getUpdatedReplication version classAttributeMap actorMap actorId = do- attributes <- getAttributes version classAttributeMap actorMap actorId- pure (UpdatedReplication attributes)--putUpdatedReplication :: UpdatedReplication -> BinaryBit.BitPut ()-putUpdatedReplication updatedReplication =- putAttributes (updatedReplicationAttributes updatedReplication)
@@ -1,65 +0,0 @@-module Rattletrap.ReplicationValue- ( module Rattletrap.ReplicationValue- , module Rattletrap.Replication.Destroyed- , module Rattletrap.Replication.Spawned- , module Rattletrap.Replication.Updated- ) where--import Rattletrap.ActorMap-import Rattletrap.ClassAttributeMap-import Rattletrap.Primitive-import Rattletrap.Replication.Destroyed-import Rattletrap.Replication.Spawned-import Rattletrap.Replication.Updated--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--data ReplicationValue- = SpawnedReplicationValue SpawnedReplication- -- ^ Creates a new actor.- | UpdatedReplicationValue UpdatedReplication- -- ^ Updates an existing actor.- | DestroyedReplicationValue DestroyedReplication- -- ^ Destroys an existing actor.- deriving (Eq, Ord, Show)--getReplicationValue- :: (Int, Int, Int)- -> ClassAttributeMap- -> ActorMap- -> CompressedWord- -> BinaryBit.BitGet (ReplicationValue, ActorMap)-getReplicationValue version classAttributeMap actorMap actorId = do- isOpen <- BinaryBit.getBool- if isOpen- then do- isNew <- BinaryBit.getBool- if isNew- then do- (x, newActorMap) <- getSpawnedReplication- version- classAttributeMap- actorMap- actorId- pure (SpawnedReplicationValue x, newActorMap)- else do- x <- getUpdatedReplication version classAttributeMap actorMap actorId- pure (UpdatedReplicationValue x, actorMap)- else do- x <- getDestroyedReplication- pure (DestroyedReplicationValue x, actorMap)--putReplicationValue :: ReplicationValue -> BinaryBit.BitPut ()-putReplicationValue value = case value of- SpawnedReplicationValue x -> do- BinaryBit.putBool True- BinaryBit.putBool True- putSpawnedReplication x- UpdatedReplicationValue x -> do- BinaryBit.putBool True- BinaryBit.putBool False- putUpdatedReplication x- DestroyedReplicationValue x -> do- BinaryBit.putBool False- putDestroyedReplication x
@@ -1,30 +0,0 @@-module Rattletrap.Utility where--import qualified Data.Bits as Bits-import qualified Data.ByteString.Lazy as ByteString-import qualified Data.ByteString.Lazy.Char8 as ByteString8-import qualified Data.Text as Text-import qualified Data.Word as Word--encodeLatin1 :: Text.Text -> ByteString.ByteString-encodeLatin1 text = ByteString8.pack (Text.unpack text)--padBytes :: Integral a => a -> ByteString.ByteString -> ByteString.ByteString-padBytes size bytes = ByteString.concat- [ bytes- , ByteString.replicate (fromIntegral size - ByteString.length bytes) 0x00- ]--reverseByte :: Word.Word8 -> Word.Word8-reverseByte byte =- Bits.shiftR (byte Bits..&. Bits.bit 7) 7- + Bits.shiftR (byte Bits..&. Bits.bit 6) 5- + Bits.shiftR (byte Bits..&. Bits.bit 5) 3- + Bits.shiftR (byte Bits..&. Bits.bit 4) 1- + Bits.shiftL (byte Bits..&. Bits.bit 3) 1- + Bits.shiftL (byte Bits..&. Bits.bit 2) 3- + Bits.shiftL (byte Bits..&. Bits.bit 1) 5- + Bits.shiftL (byte Bits..&. Bits.bit 0) 7--reverseBytes :: ByteString.ByteString -> ByteString.ByteString-reverseBytes = ByteString.map reverseByte
@@ -1,7 +0,0 @@-module Rattletrap.Version where--import qualified Data.Version as Version-import qualified Paths_rattletrap as This--version :: Version.Version-version = This.version
@@ -1,63 +0,0 @@-name: rattletrap-version: 3.1.2--category: Game-description: Rattletrap parses and generates Rocket League replays.-extra-source-files:- - CHANGELOG.markdown- - replays/*.replay- - package.yaml- - README.markdown- - stack.yaml-github: tfausak/rattletrap-license-file: LICENSE.markdown-license: MIT-maintainer: Taylor Fausak-synopsis: Parse and generate Rocket League replays.--dependencies:- - aeson >= 1.2.3 && < 1.3- - base >= 4.10.1 && < 4.11- - bimap >= 0.3.3 && < 0.4- - binary >= 0.8.5 && < 0.9- - binary-bits >= 0.5 && < 0.6- - bytestring >= 0.10.8 && < 0.11- - containers >= 0.5.10 && < 0.6- - data-binary-ieee754 >= 0.4.4 && < 0.5- - http-client >= 0.5.7 && < 0.6- - http-client-tls >= 0.3.5 && < 0.4- - template-haskell >= 2.12.0 && < 2.13- - text >= 1.2.2 && < 1.3- - vector >= 0.12.0 && < 0.13-ghc-options: -Wall--library:- default-extensions:- - Strict- other-modules: Paths_rattletrap- source-dirs: library--executables:- rattletrap:- dependencies:- - rattletrap- ghc-options:- - -rtsopts- - -threaded- - -with-rtsopts=-N- main: Main.hs- source-dirs: executables--tests:- test:- dependencies:- - filepath >= 1.4.1 && < 1.5- - hspec >= 2.4.4 && < 2.5- - rattletrap- - temporary >= 1.2.1 && < 1.3- ghc-options:- - -rtsopts- - -threaded- - -with-rtsopts=-N- main: Main.hs- source-dirs: tests
@@ -1,244 +1,233 @@--- This file has been generated from package.yaml by hpack version 0.20.0.------ see: https://github.com/sol/hpack------ hash: ffc648678c84d8942a93a5168812a9a02db2b7471f0f42a0576f8409235cc6cd+cabal-version: 2.2+name: rattletrap+version: 14.1.4+synopsis: Parse and generate Rocket League replays.+description:+ Warning: This package is not maintained anymore.+ .+ Rattletrap parses and generates Rocket League replays. -name: rattletrap-version: 3.1.2-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+build-type: Simple+category: Game+extra-doc-files:+ CHANGELOG.md+ README.md -extra-source-files:- CHANGELOG.markdown- package.yaml- README.markdown- replays/00080014003600090000036E0F65CCEB.replay- replays/07E925B1423653D44CB8B4B2524792C1.replay- replays/0AD21FEE4A512906BEB6C98136AAF49A.replay- replays/1205D96C4D819800927791820096CD49.replay- replays/160CA83E41083BFD8E6315B4BFCA0561.replay- replays/16D580EF479483E015207C901776F9FB.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/22660E3649FC7971E5653692473D4318.replay- replays/22BACD794ABE7B92E50E9CBDBD9C59CE.replay- replays/27B6A7B64553F0F685874584F96BAB1B.replay- replays/29F582C34A65EB34D358A784CBE3C189.replay- replays/2CFE577044E651D3FA9DBF83ECA8BCC3.replay- replays/338173964F9F71EBDD31058A1936CBB4.replay- replays/372DBFCA4BDB340E4357B6BD43032802.replay- replays/387F059C47C09E253C875CA990EFD9F2.replay- replays/3EA147DD485B8DD24810689A7A989E44.replay- replays/4126861E477F4A03DE2A4080374D7908.replay- replays/42F0D8DA4FC89AE7B80FCAB7F637A8EA.replay- replays/504ED825482186E771FAA9B642CE5CE4.replay- replays/520E1BFF468CF6C3C48D1EA85D9C7909.replay- replays/52AA67F94090C19D33C5009E54D31FE4.replay- replays/540DA764423C8FB24EB9D486D982F16F.replay- replays/551CA4D44FF2B86015DE44A6B5790D4C.replay- replays/6320E51C49066A7C210A2993C2201D5F.replay- replays/6688EEE34BFEB3EC3A9E3283098CC712.replay- replays/6B0D1614417085A7AAD82EAA30D8DABE.replay- replays/6D1B06D844A5BB91B81FD4B5B28F08BA.replay- replays/6F7CFCD24638F8A6567AB3A8B9958A90.replay- replays/7109EB9846D303E54B7ACBA792036213.replay- replays/7BF6073F4614CE0A438994B9A260DA6A.replay- replays/89CBA30E46FA5385BDD35DA4285D4D2E.replay- replays/8AE551FF406D7B82ED853B8C7BFF8CDA.replay- replays/9704208245D7DD851F2FB2BC7DFD9AC3.replay- replays/98E58A904D713F2DE202358E8573265D.replay- replays/A128B3AB45D5A18E3EF9CF93C9576BCE.replay- replays/A52F804845573D8DA65E97BF59026A43.replay- replays/A558B1B44124D6E021640884E8EEC2A7.replay- replays/A6711CE74272B2E663DCC9A200A218E3.replay- replays/A7F001A1417A19BFA8C90990D8F7C2FF.replay- replays/AFB1F46A49737E36928E1EABC6F5B7AD.replay- replays/B9F9B87D4A9D0A3D25D4EC91C0401DE2.replay- replays/C14F7E0E4D9B5E6BE9AD5D8ED56B174C.replay- replays/C8372B1345B1803DEF039F815DBD802D.replay- replays/CC4CA70D4F7A67EBAD0ED9B9923106F7.replay- replays/D0449F5F4AA775B86FFA7DA2B5A3204E.replay- replays/D428F81646A98C25902CE988AE5C14C8.replay- replays/D7FB197A451D69075A0C99A2F49A4053.replay- replays/DCB3A6B94A9DBE46FDE5EAA9B012F6C8.replay- replays/DE56B1B146687EA2E62E31A37A08C8EB.replay- replays/EAE311E84BA35B590A6FDBA6DD4F2FEB.replay- replays/EAE8DADA4BB2DC5422792C9B4A67392D.replay- replays/F299F176491554B11E34AB91CA76B2CE.replay- replays/F7B9E14545C7467B89A00895980FCD73.replay- replays/F811C1D24888015E23B598AD8628C742.replay- replays/FDC79DA84DD463D4BCCE6B892829AC88.replay- stack.yaml+license-file: LICENSE.txt+license: MIT+maintainer: Taylor Fausak source-repository head type: git location: https://github.com/tfausak/rattletrap +flag pedantic+ default: False+ manual: True++common library+ build-depends: base ^>=4.19.0.0 || ^>=4.20.0.0 || ^>=4.21.0.0+ default-extensions: NamedFieldPuns+ default-language: Haskell2010+ ghc-options:+ -Weverything+ -Wno-all-missed-specialisations+ -Wno-implicit-prelude+ -Wno-missed-specialisations+ -Wno-missing-deriving-strategies+ -Wno-missing-export-lists+ -Wno-missing-exported-signatures+ -Wno-missing-import-lists+ -Wno-missing-kind-signatures+ -Wno-missing-role-annotations+ -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- hs-source-dirs:- library- default-extensions: Strict- ghc-options: -Wall+ import: library+ autogen-modules: Paths_rattletrap build-depends:- aeson >=1.2.3 && <1.3- , base >=4.10.1 && <4.11- , bimap >=0.3.3 && <0.4- , binary >=0.8.5 && <0.9- , binary-bits >=0.5 && <0.6- , bytestring >=0.10.8 && <0.11- , containers >=0.5.10 && <0.6- , data-binary-ieee754 >=0.4.4 && <0.5- , http-client >=0.5.7 && <0.6- , http-client-tls >=0.3.5 && <0.4- , template-haskell >=2.12.0 && <2.13- , text >=1.2.2 && <1.3- , vector >=0.12.0 && <0.13+ 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 exposed-modules:- Rattletrap- Rattletrap.ActorMap- Rattletrap.Attribute- Rattletrap.Attribute.AppliedDamage- Rattletrap.Attribute.Boolean- Rattletrap.Attribute.Byte- Rattletrap.Attribute.CamSettings- Rattletrap.Attribute.ClubColors- Rattletrap.Attribute.DamageState- Rattletrap.Attribute.Demolish- Rattletrap.Attribute.Enum- Rattletrap.Attribute.Explosion- Rattletrap.Attribute.ExtendedExplosion- 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.Product- 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+ Rattletrap+ Rattletrap.BitBuilder+ Rattletrap.BitGet+ Rattletrap.BitPut+ Rattletrap.BitString+ Rattletrap.ByteGet+ Rattletrap.BytePut+ Rattletrap.Console.Config+ Rattletrap.Console.Flag+ Rattletrap.Console.Main+ Rattletrap.Console.Mode+ Rattletrap.Console.Option+ Rattletrap.Data+ Rattletrap.Exception.CrcMismatch+ Rattletrap.Exception.Empty+ Rattletrap.Exception.Fail+ Rattletrap.Exception.InvalidComponent+ Rattletrap.Exception.InvalidJson+ Rattletrap.Exception.MissingAttributeLimit+ Rattletrap.Exception.MissingAttributeName+ Rattletrap.Exception.MissingClassName+ Rattletrap.Exception.MissingObjectName+ Rattletrap.Exception.MissingProductName+ Rattletrap.Exception.NotEnoughInput+ Rattletrap.Exception.UnknownActor+ Rattletrap.Exception.UnknownAttribute+ Rattletrap.Exception.UnknownName+ Rattletrap.Exception.UnknownProduct+ Rattletrap.Exception.UnknownProperty+ Rattletrap.Exception.UnknownSystemId+ Rattletrap.Get+ Rattletrap.Schema+ Rattletrap.Type.Attribute+ Rattletrap.Type.Attribute.AppliedDamage+ Rattletrap.Type.Attribute.Boolean+ Rattletrap.Type.Attribute.Boost+ Rattletrap.Type.Attribute.Byte+ Rattletrap.Type.Attribute.CamSettings+ Rattletrap.Type.Attribute.ClubColors+ Rattletrap.Type.Attribute.CustomDemolish+ Rattletrap.Type.Attribute.CustomDemolishExtended+ Rattletrap.Type.Attribute.DamageState+ Rattletrap.Type.Attribute.Demolish+ Rattletrap.Type.Attribute.Enum+ Rattletrap.Type.Attribute.Explosion+ Rattletrap.Type.Attribute.ExtendedExplosion+ Rattletrap.Type.Attribute.FlaggedByte+ Rattletrap.Type.Attribute.FlaggedInt+ Rattletrap.Type.Attribute.Float+ Rattletrap.Type.Attribute.GameMode+ Rattletrap.Type.Attribute.GameServer+ Rattletrap.Type.Attribute.Int+ Rattletrap.Type.Attribute.Int64+ Rattletrap.Type.Attribute.Loadout+ Rattletrap.Type.Attribute.LoadoutOnline+ Rattletrap.Type.Attribute.Loadouts+ Rattletrap.Type.Attribute.LoadoutsOnline+ Rattletrap.Type.Attribute.Location+ Rattletrap.Type.Attribute.MusicStinger+ Rattletrap.Type.Attribute.PartyLeader+ Rattletrap.Type.Attribute.Pickup+ Rattletrap.Type.Attribute.PickupInfo+ Rattletrap.Type.Attribute.PickupNew+ Rattletrap.Type.Attribute.PlayerHistoryKey+ Rattletrap.Type.Attribute.PrivateMatchSettings+ 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+ Rattletrap.Type.Attribute.Title+ Rattletrap.Type.Attribute.UniqueId+ Rattletrap.Type.Attribute.WeldedInfo+ Rattletrap.Type.AttributeMapping+ Rattletrap.Type.AttributeType+ Rattletrap.Type.AttributeValue+ Rattletrap.Type.Cache+ Rattletrap.Type.ClassAttributeMap+ Rattletrap.Type.ClassMapping+ Rattletrap.Type.CompressedWord+ Rattletrap.Type.CompressedWordVector+ Rattletrap.Type.Content+ Rattletrap.Type.Dictionary+ Rattletrap.Type.F32+ Rattletrap.Type.Frame+ Rattletrap.Type.Header+ Rattletrap.Type.I32+ Rattletrap.Type.I64+ Rattletrap.Type.I8+ Rattletrap.Type.Initialization+ Rattletrap.Type.Int8Vector+ Rattletrap.Type.Keyframe+ Rattletrap.Type.List+ Rattletrap.Type.Mark+ Rattletrap.Type.Message+ Rattletrap.Type.ObjectTarget+ Rattletrap.Type.Property+ Rattletrap.Type.Property.Array+ Rattletrap.Type.Property.Bool+ Rattletrap.Type.Property.Byte+ Rattletrap.Type.Property.Float+ Rattletrap.Type.Property.Int+ Rattletrap.Type.Property.Name+ Rattletrap.Type.Property.QWord+ Rattletrap.Type.Property.Str+ Rattletrap.Type.Property.Struct+ Rattletrap.Type.PropertyValue+ Rattletrap.Type.Quaternion+ Rattletrap.Type.RemoteId+ 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+ Rattletrap.Type.RemoteId.Xbox+ Rattletrap.Type.Replay+ Rattletrap.Type.Replication+ Rattletrap.Type.Replication.Destroyed+ Rattletrap.Type.Replication.Spawned+ Rattletrap.Type.Replication.Updated+ Rattletrap.Type.ReplicationValue+ Rattletrap.Type.Rotation+ Rattletrap.Type.Section+ Rattletrap.Type.Str+ Rattletrap.Type.U32+ Rattletrap.Type.U64+ Rattletrap.Type.U8+ Rattletrap.Type.Vector+ Rattletrap.Type.Version+ Rattletrap.Utility.Bytes+ Rattletrap.Utility.Crc+ Rattletrap.Utility.Helper+ Rattletrap.Utility.Json+ Rattletrap.Utility.Monad+ Rattletrap.Version + hs-source-dirs: src/lib+ executable rattletrap+ import: executable+ hs-source-dirs: src/exe main-is: Main.hs- hs-source-dirs:- executables- ghc-options: -Wall -rtsopts -threaded -with-rtsopts=-N++test-suite rattletrap-test-suite+ import: executable build-depends:- aeson >=1.2.3 && <1.3- , base >=4.10.1 && <4.11- , bimap >=0.3.3 && <0.4- , binary >=0.8.5 && <0.9- , binary-bits >=0.5 && <0.6- , bytestring >=0.10.8 && <0.11- , containers >=0.5.10 && <0.6- , data-binary-ieee754 >=0.4.4 && <0.5- , http-client >=0.5.7 && <0.6- , http-client-tls >=0.3.5 && <0.4- , rattletrap- , template-haskell >=2.12.0 && <2.13- , text >=1.2.2 && <1.3- , vector >=0.12.0 && <0.13- other-modules:- Paths_rattletrap- default-language: Haskell2010+ bytestring,+ filepath, -test-suite test- type: exitcode-stdio-1.0+ hs-source-dirs: src/test main-is: Main.hs- hs-source-dirs:- tests- ghc-options: -Wall -rtsopts -threaded -with-rtsopts=-N- build-depends:- aeson >=1.2.3 && <1.3- , base >=4.10.1 && <4.11- , bimap >=0.3.3 && <0.4- , binary >=0.8.5 && <0.9- , binary-bits >=0.5 && <0.6- , bytestring >=0.10.8 && <0.11- , containers >=0.5.10 && <0.6- , data-binary-ieee754 >=0.4.4 && <0.5- , filepath >=1.4.1 && <1.5- , hspec >=2.4.4 && <2.5- , http-client >=0.5.7 && <0.6- , http-client-tls >=0.3.5 && <0.4- , rattletrap- , template-haskell >=2.12.0 && <2.13- , temporary >=1.2.1 && <1.3- , text >=1.2.2 && <1.3- , vector >=0.12.0 && <0.13- other-modules:- Paths_rattletrap- default-language: Haskell2010+ type: exitcode-stdio-1.0
binary file changed (94347 → absent bytes)
binary file changed (30063 → absent bytes)
binary file changed (27949 → absent bytes)
binary file changed (461470 → absent bytes)
binary file changed (565359 → absent bytes)
binary file changed (30691 → absent bytes)
binary file changed (21996 → absent bytes)
binary file changed (98814 → absent bytes)
binary file changed (25926 → absent bytes)
binary file changed (22739 → absent bytes)
binary file changed (59943 → absent bytes)
binary file changed (737542 → absent bytes)
binary file changed (27316 → absent bytes)
binary file changed (38258 → absent bytes)
binary file changed (1259407 → absent bytes)
binary file changed (315922 → absent bytes)
binary file changed (25332 → absent bytes)
binary file changed (21607 → absent bytes)
binary file changed (1060102 → absent bytes)
binary file changed (1556372 → absent bytes)
binary file changed (28065 → absent bytes)
binary file changed (737279 → absent bytes)
binary file changed (596690 → absent bytes)
binary file changed (26284 → absent bytes)
binary file changed (116440 → absent bytes)
binary file changed (145094 → absent bytes)
binary file changed (68945 → absent bytes)
binary file changed (570657 → absent bytes)
binary file changed (34674 → absent bytes)
binary file changed (754887 → absent bytes)
binary file changed (391424 → absent bytes)
binary file changed (1026 → absent bytes)
binary file changed (1294929 → absent bytes)
binary file changed (91217 → absent bytes)
binary file changed (29715 → absent bytes)
binary file changed (65002 → absent bytes)
binary file changed (22841 → absent bytes)
binary file changed (212429 → absent bytes)
binary file changed (28946 → absent bytes)
binary file changed (36307 → absent bytes)
binary file changed (34047 → absent bytes)
binary file changed (743537 → absent bytes)
binary file changed (73064 → absent bytes)
binary file changed (266615 → absent bytes)
binary file changed (221897 → absent bytes)
binary file changed (396148 → absent bytes)
binary file changed (32310 → absent bytes)
binary file changed (25888 → absent bytes)
binary file changed (25904 → absent bytes)
binary file changed (23625 → absent bytes)
binary file changed (30103 → absent bytes)
binary file changed (640433 → absent bytes)
binary file changed (476589 → absent bytes)
binary file changed (29095 → absent bytes)
binary file changed (1055071 → absent bytes)
binary file changed (368882 → absent bytes)
binary file changed (30151 → absent bytes)
binary file changed (25462 → absent bytes)
binary file changed (22732 → absent bytes)
binary file changed (25580 → absent bytes)
binary file changed (605 → absent bytes)
binary file changed (783619 → absent bytes)
@@ -0,0 +1,4 @@+import qualified Rattletrap++main :: IO ()+main = Rattletrap.main
@@ -0,0 +1,12 @@+module Rattletrap+ ( Rattletrap.Console.Main.main,+ Rattletrap.Console.Main.rattletrap,+ Rattletrap.Utility.Helper.decodeReplayFile,+ Rattletrap.Utility.Helper.encodeReplayJson,+ Rattletrap.Utility.Helper.decodeReplayJson,+ Rattletrap.Utility.Helper.encodeReplayFile,+ )+where++import qualified Rattletrap.Console.Main+import qualified Rattletrap.Utility.Helper
@@ -0,0 +1,33 @@+{- hlint ignore "Avoid restricted extensions" -}+{-# LANGUAGE BangPatterns #-}++module Rattletrap.BitBuilder where++import qualified Data.Bits as Bits+import qualified Data.ByteString.Builder as Builder+import qualified Data.Word as Word++data BitBuilder = BitBuilder+ { buffer :: Word.Word8,+ builder :: Builder.Builder,+ offset :: Int+ }++empty :: BitBuilder+empty = BitBuilder {buffer = 0x00, builder = mempty, offset = 0}++push :: Bool -> BitBuilder -> BitBuilder+push b x =+ let !newBuffer = if b then Bits.setBit (buffer x) (offset x) else buffer x+ in if offset x == 7+ then+ BitBuilder+ { buffer = 0x00,+ builder = builder x <> Builder.word8 newBuffer,+ offset = 0+ }+ else x {buffer = newBuffer, offset = offset x + 1}++toBuilder :: BitBuilder -> Builder.Builder+toBuilder x =+ if offset x == 0 then builder x else builder x <> Builder.word8 (buffer x)
@@ -0,0 +1,52 @@+module Rattletrap.BitGet where++import qualified Control.Exception as Exception+import qualified Control.Monad as Monad+import qualified Data.Bits as Bits+import qualified Data.ByteString as ByteString+import qualified Data.Functor.Identity as Identity+import qualified Rattletrap.BitString as BitString+import qualified Rattletrap.ByteGet as ByteGet+import qualified Rattletrap.Exception.NotEnoughInput as NotEnoughInput+import qualified Rattletrap.Get as Get++type BitGet = Get.Get BitString.BitString Identity.Identity++toByteGet :: BitGet a -> ByteGet.ByteGet a+toByteGet g = do+ s1 <- Get.get+ case Identity.runIdentity . Get.run g $ BitString.fromByteString s1 of+ Left (ls, e) -> Get.labels ls $ ByteGet.throw e+ Right (s2, x) -> do+ Get.put $ BitString.byteString s2+ pure x++fromByteGet :: ByteGet.ByteGet a -> Int -> BitGet a+fromByteGet f n = do+ x <- byteString n+ Get.embed f x++bits :: (Bits.Bits a) => Int -> BitGet a+bits n = do+ let f :: (Bits.Bits a) => Bool -> a -> a+ f bit x = let y = Bits.shiftL x 1 in if bit then Bits.setBit y 0 else y+ xs <- Monad.replicateM n bool+ pure $ foldr f Bits.zeroBits xs++bool :: BitGet Bool+bool = do+ s1 <- Get.get+ case BitString.pop s1 of+ Nothing -> throw NotEnoughInput.NotEnoughInput+ Just (x, s2) -> do+ Get.put s2+ pure x++byteString :: Int -> BitGet ByteString.ByteString+byteString n = fmap ByteString.pack . Monad.replicateM n $ bits 8++throw :: (Exception.Exception e) => e -> BitGet a+throw = Get.throw++label :: String -> BitGet a -> BitGet a+label = Get.label
@@ -0,0 +1,32 @@+module Rattletrap.BitPut where++import qualified Data.Bits as Bits+import qualified Data.ByteString as ByteString+import qualified Rattletrap.BitBuilder as BitBuilder+import qualified Rattletrap.BytePut as BytePut++newtype BitPut = BitPut (BitBuilder.BitBuilder -> BitBuilder.BitBuilder)++instance Semigroup BitPut where+ f1 <> f2 = BitPut $ run f2 . run f1++instance Monoid BitPut where+ mempty = BitPut id++run :: BitPut -> BitBuilder.BitBuilder -> BitBuilder.BitBuilder+run (BitPut f) = f++toBytePut :: BitPut -> BytePut.BytePut+toBytePut b = BitBuilder.toBuilder $ run b BitBuilder.empty++fromBytePut :: BytePut.BytePut -> BitPut+fromBytePut = byteString . BytePut.toByteString++bits :: (Bits.Bits a) => Int -> a -> BitPut+bits n x = foldMap (bool . Bits.testBit x) [0 .. n - 1]++bool :: Bool -> BitPut+bool = BitPut . BitBuilder.push++byteString :: ByteString.ByteString -> BitPut+byteString = foldMap (bits 8) . ByteString.unpack
@@ -0,0 +1,23 @@+module Rattletrap.BitString where++import qualified Data.Bits as Bits+import qualified Data.ByteString as ByteString++data BitString = BitString+ { byteString :: ByteString.ByteString,+ offset :: Int+ }+ deriving (Eq, Show)++fromByteString :: ByteString.ByteString -> BitString+fromByteString byteString = BitString {byteString, offset = 0}++pop :: BitString -> Maybe (Bool, BitString)+pop old = do+ (word, byteString) <- ByteString.uncons $ byteString old+ let bit = Bits.testBit word $ offset old+ new =+ if offset old == 7+ then fromByteString byteString+ else old {offset = offset old + 1}+ pure (bit, new)
@@ -0,0 +1,82 @@+module Rattletrap.ByteGet where++import qualified Control.Exception as Exception+import qualified Data.Bits as Bits+import qualified Data.ByteString as ByteString+import qualified Data.ByteString.Lazy as LazyByteString+import qualified Data.Functor.Identity as Identity+import qualified Data.Int as Int+import qualified Data.Word as Word+import qualified GHC.Float as Float+import qualified Rattletrap.Exception.NotEnoughInput as NotEnoughInput+import qualified Rattletrap.Get as Get++type ByteGet = Get.Get ByteString.ByteString Identity.Identity++run ::+ ByteGet a ->+ ByteString.ByteString ->+ Either ([String], Exception.SomeException) a+run g = fmap snd . Identity.runIdentity . Get.run g++byteString :: Int -> ByteGet ByteString.ByteString+byteString n = do+ s1 <- Get.get+ let (x, s2) = ByteString.splitAt n s1+ if ByteString.length x == n+ then do+ Get.put s2+ pure x+ else throw NotEnoughInput.NotEnoughInput++float :: ByteGet Float+float = fmap Float.castWord32ToFloat word32++int8 :: ByteGet Int.Int8+int8 = fmap fromIntegral word8++int32 :: ByteGet Int.Int32+int32 = fmap fromIntegral word32++int64 :: ByteGet Int.Int64+int64 = fmap fromIntegral word64++remaining :: ByteGet LazyByteString.ByteString+remaining = do+ x <- Get.get+ Get.put ByteString.empty+ pure $ LazyByteString.fromStrict x++word8 :: ByteGet Word.Word8+word8 = fmap ByteString.head $ byteString 1++word32 :: ByteGet Word.Word32+word32 = do+ x <- byteString 4+ pure $+ Bits.shiftL (fromIntegral $ ByteString.index x 0) 0+ + Bits.shiftL (fromIntegral $ ByteString.index x 1) 8+ + Bits.shiftL (fromIntegral $ ByteString.index x 2) 16+ + Bits.shiftL (fromIntegral $ ByteString.index x 3) 24++word64 :: ByteGet Word.Word64+word64 = do+ x <- byteString 8+ pure $+ Bits.shiftL (fromIntegral $ ByteString.index x 0) 0+ + Bits.shiftL (fromIntegral $ ByteString.index x 1) 8+ + Bits.shiftL (fromIntegral $ ByteString.index x 2) 16+ + Bits.shiftL (fromIntegral $ ByteString.index x 3) 24+ + Bits.shiftL (fromIntegral $ ByteString.index x 4) 32+ + Bits.shiftL (fromIntegral $ ByteString.index x 5) 40+ + Bits.shiftL (fromIntegral $ ByteString.index x 6) 48+ + Bits.shiftL (fromIntegral $ ByteString.index x 7) 56++throw :: (Exception.Exception e) => e -> ByteGet a+throw = Get.throw++embed :: ByteGet a -> ByteString.ByteString -> ByteGet a+embed = Get.embed++label :: String -> ByteGet a -> ByteGet a+label = Get.label
@@ -0,0 +1,39 @@+module Rattletrap.BytePut where++import qualified Data.ByteString as ByteString+import qualified Data.ByteString.Builder as Builder+import qualified Data.ByteString.Lazy as LazyByteString+import qualified Data.Int as Int+import qualified Data.Word as Word++type BytePut = Builder.Builder++toByteString :: BytePut -> ByteString.ByteString+toByteString = LazyByteString.toStrict . toLazyByteString++toLazyByteString :: BytePut -> LazyByteString.ByteString+toLazyByteString = Builder.toLazyByteString++byteString :: ByteString.ByteString -> BytePut+byteString = Builder.byteString++float :: Float -> BytePut+float = Builder.floatLE++int8 :: Int.Int8 -> BytePut+int8 = Builder.int8++int32 :: Int.Int32 -> BytePut+int32 = Builder.int32LE++int64 :: Int.Int64 -> BytePut+int64 = Builder.int64LE++word8 :: Word.Word8 -> BytePut+word8 = Builder.word8++word32 :: Word.Word32 -> BytePut+word32 = Builder.word32LE++word64 :: Word.Word64 -> BytePut+word64 = Builder.word64LE
@@ -0,0 +1,57 @@+module Rattletrap.Console.Config where++import qualified Rattletrap.Console.Flag as Flag+import qualified Rattletrap.Console.Mode as Mode+import qualified System.FilePath as FilePath++data Config = Config+ { compact :: Bool,+ fast :: Bool,+ help :: Bool,+ input :: Maybe String,+ mode :: Maybe Mode.Mode,+ output :: Maybe String,+ schema :: Bool,+ skipCrc :: Bool,+ version :: Bool+ }+ deriving (Eq, Show)++initial :: Config+initial =+ Config+ { compact = False,+ fast = False,+ help = False,+ input = Nothing,+ mode = Nothing,+ output = Nothing,+ schema = False,+ skipCrc = False,+ version = False+ }++applyFlag :: Config -> Flag.Flag -> Either String Config+applyFlag config flag = case flag of+ Flag.Compact -> Right config {compact = True}+ Flag.Fast -> Right config {fast = True}+ Flag.Help -> Right config {help = True}+ Flag.Input x -> Right config {input = Just x}+ Flag.Mode x -> do+ y <- Mode.fromString x+ Right config {mode = Just y}+ Flag.Output x -> Right config {output = Just x}+ Flag.Schema -> Right config {schema = True}+ Flag.SkipCrc -> Right config {skipCrc = True}+ Flag.Version -> Right config {version = True}++getMode :: Config -> Mode.Mode+getMode config =+ let i = fmap FilePath.takeExtension $ input config+ o = fmap FilePath.takeExtension $ output config+ in case (i, o) of+ (Just ".json", _) -> Mode.Encode+ (Just ".replay", _) -> Mode.Decode+ (_, Just ".json") -> Mode.Decode+ (_, Just ".replay") -> Mode.Encode+ _ -> Mode.Decode
@@ -0,0 +1,13 @@+module Rattletrap.Console.Flag where++data Flag+ = Compact+ | Fast+ | Help+ | Input FilePath+ | Mode String+ | Output FilePath+ | Schema+ | SkipCrc+ | Version+ deriving (Eq, Show)
@@ -0,0 +1,329 @@+module Rattletrap.Console.Main where++import qualified Control.Exception as Exception+import qualified Control.Monad as Monad+import qualified Data.Bool as Bool+import qualified Data.ByteString as ByteString+import qualified Data.ByteString.Lazy as LazyByteString+import qualified Data.List as List+import qualified Data.Text as Text+import qualified Network.HTTP.Client as Client+import qualified Network.HTTP.Client.TLS as Client+import qualified Rattletrap.Console.Config as Config+import qualified Rattletrap.Console.Mode as Mode+import qualified Rattletrap.Console.Option as Option+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.Attribute as Attribute+import qualified Rattletrap.Type.Attribute.AppliedDamage as Attribute.AppliedDamage+import qualified Rattletrap.Type.Attribute.Boolean as Attribute.Boolean+import qualified Rattletrap.Type.Attribute.Boost as Attribute.Boost+import qualified Rattletrap.Type.Attribute.Byte as Attribute.Byte+import qualified Rattletrap.Type.Attribute.CamSettings as Attribute.CamSettings+import qualified Rattletrap.Type.Attribute.ClubColors as Attribute.ClubColors+import qualified Rattletrap.Type.Attribute.CustomDemolish as Attribute.CustomDemolish+import qualified Rattletrap.Type.Attribute.CustomDemolishExtended as Attribute.CustomDemolishExtended+import qualified Rattletrap.Type.Attribute.DamageState as Attribute.DamageState+import qualified Rattletrap.Type.Attribute.Demolish as Attribute.Demolish+import qualified Rattletrap.Type.Attribute.Enum as Attribute.Enum+import qualified Rattletrap.Type.Attribute.Explosion as Attribute.Explosion+import qualified Rattletrap.Type.Attribute.ExtendedExplosion as Attribute.ExtendedExplosion+import qualified Rattletrap.Type.Attribute.FlaggedByte as Attribute.FlaggedByte+import qualified Rattletrap.Type.Attribute.FlaggedInt as Attribute.FlaggedInt+import qualified Rattletrap.Type.Attribute.Float as Attribute.Float+import qualified Rattletrap.Type.Attribute.GameMode as Attribute.GameMode+import qualified Rattletrap.Type.Attribute.GameServer as Attribute.GameServer+import qualified Rattletrap.Type.Attribute.Int as Attribute.Int+import qualified Rattletrap.Type.Attribute.Int64 as Attribute.Int64+import qualified Rattletrap.Type.Attribute.Loadout as Attribute.Loadout+import qualified Rattletrap.Type.Attribute.LoadoutOnline as Attribute.LoadoutOnline+import qualified Rattletrap.Type.Attribute.Loadouts as Attribute.Loadouts+import qualified Rattletrap.Type.Attribute.LoadoutsOnline as Attribute.LoadoutsOnline+import qualified Rattletrap.Type.Attribute.Location as Attribute.Location+import qualified Rattletrap.Type.Attribute.MusicStinger as Attribute.MusicStinger+import qualified Rattletrap.Type.Attribute.PartyLeader as Attribute.PartyLeader+import qualified Rattletrap.Type.Attribute.Pickup as Attribute.Pickup+import qualified Rattletrap.Type.Attribute.PickupInfo as Attribute.PickupInfo+import qualified Rattletrap.Type.Attribute.PickupNew as Attribute.PickupNew+import qualified Rattletrap.Type.Attribute.PlayerHistoryKey as Attribute.PlayerHistoryKey+import qualified Rattletrap.Type.Attribute.PrivateMatchSettings as Attribute.PrivateMatchSettings+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+import qualified Rattletrap.Type.Attribute.Title as Attribute.Title+import qualified Rattletrap.Type.Attribute.UniqueId as Attribute.UniqueId+import qualified Rattletrap.Type.Attribute.WeldedInfo as Attribute.WeldedInfo+import qualified Rattletrap.Type.AttributeMapping as AttributeMapping+import qualified Rattletrap.Type.AttributeValue as AttributeValue+import qualified Rattletrap.Type.Cache as Cache+import qualified Rattletrap.Type.ClassMapping as ClassMapping+import qualified Rattletrap.Type.CompressedWord as CompressedWord+import qualified Rattletrap.Type.CompressedWordVector as CompressedWordVector+import qualified Rattletrap.Type.Content as Content+import qualified Rattletrap.Type.Dictionary as Dictionary+import qualified Rattletrap.Type.F32 as F32+import qualified Rattletrap.Type.Frame as Frame+import qualified Rattletrap.Type.Header as Header+import qualified Rattletrap.Type.I32 as I32+import qualified Rattletrap.Type.I64 as I64+import qualified Rattletrap.Type.I8 as I8+import qualified Rattletrap.Type.Initialization as Initialization+import qualified Rattletrap.Type.Int8Vector as Int8Vector+import qualified Rattletrap.Type.Keyframe as Keyframe+import qualified Rattletrap.Type.List as RList+import qualified Rattletrap.Type.Mark as Mark+import qualified Rattletrap.Type.Message as Message+import qualified Rattletrap.Type.ObjectTarget as ObjectTarget+import qualified Rattletrap.Type.Property as Property+import qualified Rattletrap.Type.Property.Array as Property.Array+import qualified Rattletrap.Type.Property.Byte as Property.Byte+import qualified Rattletrap.Type.Property.Struct as Property.Struct+import qualified Rattletrap.Type.PropertyValue as PropertyValue+import qualified Rattletrap.Type.Quaternion as Quaternion+import qualified Rattletrap.Type.RemoteId as RemoteId+import qualified Rattletrap.Type.RemoteId.PlayStation as RemoteId.PlayStation+import qualified Rattletrap.Type.RemoteId.PsyNet as RemoteId.PsyNet+import qualified Rattletrap.Type.RemoteId.Switch as RemoteId.Switch+import qualified Rattletrap.Type.Replay as Replay+import qualified Rattletrap.Type.Replication as Replication+import qualified Rattletrap.Type.Replication.Destroyed as Replication.Destroyed+import qualified Rattletrap.Type.Replication.Spawned as Replication.Spawned+import qualified Rattletrap.Type.Replication.Updated as Replication.Updated+import qualified Rattletrap.Type.ReplicationValue as ReplicationValue+import qualified Rattletrap.Type.Rotation as Rotation+import qualified Rattletrap.Type.Section as Section+import qualified Rattletrap.Type.Str as Str+import qualified Rattletrap.Type.U32 as U32+import qualified Rattletrap.Type.U64 as U64+import qualified Rattletrap.Type.U8 as U8+import qualified Rattletrap.Type.Vector as Vector+import qualified Rattletrap.Utility.Helper as Rattletrap+import qualified Rattletrap.Utility.Json as Json+import qualified Rattletrap.Version as Version+import qualified System.Console.GetOpt as Console+import qualified System.Environment as Environment+import qualified System.Exit as Exit+import qualified System.FilePath as FilePath+import qualified System.IO as IO++main :: IO ()+main = do+ name <- Environment.getProgName+ arguments <- Environment.getArgs+ rattletrap name arguments++rattletrap :: String -> [String] -> IO ()+rattletrap name arguments = do+ config <- getConfig arguments+ if Config.help config+ then helpMain name+ else+ if Config.version config+ then versionMain+ else+ if Config.schema config+ then schemaMain config+ else defaultMain name config++helpMain :: String -> IO ()+helpMain name = do+ putStr $+ Console.usageInfo+ (unwords [name, "version", Version.string])+ Option.all++versionMain :: IO ()+versionMain = do+ putStrLn Version.string++schemaMain :: Config.Config -> IO ()+schemaMain config = putOutput config $ encodeJson config schema++defaultMain :: String -> Config.Config -> IO ()+defaultMain name config = do+ input <- getInput name config+ let decode = getDecoder config+ replay <- case decode input of+ Left (ls, e) -> do+ IO.hPutStr IO.stderr $+ unlines+ [ "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+ Right x -> pure x+ let encode = getEncoder config+ putOutput config (encode replay)++schema :: Json.Value+schema =+ let contentSchema = Content.schema $ RList.schema Frame.schema+ in Json.object+ [ Json.pair "$schema" "http://json-schema.org/draft-07/schema",+ Json.pair "$id" Replay.schemaUrl,+ Json.pair "$ref" "#/definitions/replay",+ Json.pair "definitions" . Json.object $+ fmap+ (\s -> Json.pair (Text.unpack $ Schema.name s) $ Schema.json s)+ [ Attribute.schema,+ Attribute.AppliedDamage.schema,+ Attribute.Boolean.schema,+ Attribute.Boost.schema,+ Attribute.Byte.schema,+ Attribute.CamSettings.schema,+ Attribute.ClubColors.schema,+ Attribute.CustomDemolish.schema,+ Attribute.CustomDemolishExtended.schema,+ Attribute.DamageState.schema,+ Attribute.Demolish.schema,+ Attribute.Enum.schema,+ Attribute.Explosion.schema,+ Attribute.ExtendedExplosion.schema,+ Attribute.FlaggedByte.schema,+ Attribute.FlaggedInt.schema,+ Attribute.Float.schema,+ Attribute.GameMode.schema,+ Attribute.GameServer.schema,+ Attribute.Int.schema,+ Attribute.Int64.schema,+ Attribute.Loadout.schema,+ Attribute.LoadoutOnline.schema,+ Attribute.Loadouts.schema,+ Attribute.LoadoutsOnline.schema,+ Attribute.Location.schema,+ Attribute.MusicStinger.schema,+ Attribute.PartyLeader.schema,+ Attribute.Pickup.schema,+ Attribute.PickupInfo.schema,+ Attribute.PickupNew.schema,+ Attribute.PlayerHistoryKey.schema,+ Attribute.PrivateMatchSettings.schema,+ 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,+ Attribute.Title.schema,+ Attribute.UniqueId.schema,+ Attribute.WeldedInfo.schema,+ AttributeMapping.schema,+ AttributeValue.schema,+ Cache.schema,+ ClassMapping.schema,+ CompressedWord.schema,+ CompressedWordVector.schema,+ contentSchema,+ Dictionary.elementSchema Property.schema,+ Dictionary.schema Property.schema,+ F32.schema,+ Frame.schema,+ Header.schema,+ I32.schema,+ I64.schema,+ I8.schema,+ Initialization.schema,+ Int8Vector.schema,+ Keyframe.schema,+ RList.schema Attribute.Product.schema,+ Mark.schema,+ Message.schema,+ ObjectTarget.schema,+ Property.schema,+ Property.Array.schema Property.schema,+ Property.Byte.schema,+ Property.Struct.schema Property.schema,+ PropertyValue.schema Property.schema,+ Quaternion.schema,+ RemoteId.schema,+ RemoteId.PlayStation.schema,+ RemoteId.PsyNet.schema,+ RemoteId.Switch.schema,+ Replay.schema (Section.schema Header.schema)+ . Section.schema+ $ contentSchema,+ Replication.Destroyed.schema,+ Replication.schema,+ Replication.Spawned.schema,+ Replication.Updated.schema,+ ReplicationValue.schema,+ Rotation.schema,+ Schema.boolean,+ Schema.integer,+ Schema.null,+ Schema.number,+ Schema.string,+ Section.schema contentSchema,+ Section.schema Header.schema,+ Str.schema,+ U32.schema,+ U64.schema,+ U8.schema,+ Vector.schema+ ]+ ]++getDecoder ::+ Config.Config ->+ ByteString.ByteString ->+ Either ([String], Exception.SomeException) Replay.Replay+getDecoder config = case Config.getMode config of+ Mode.Decode ->+ Rattletrap.decodeReplayFile (Config.fast config) (Config.skipCrc config)+ Mode.Encode -> Rattletrap.decodeReplayJson++getEncoder :: Config.Config -> Replay.Replay -> LazyByteString.ByteString+getEncoder config = case Config.getMode config of+ Mode.Decode -> encodeJson config+ Mode.Encode -> Rattletrap.encodeReplayFile $ Config.fast config++getInput :: String -> Config.Config -> IO ByteString.ByteString+getInput name config = case Config.input config of+ Nothing -> do+ isTerminalDevice <- IO.hIsTerminalDevice IO.stdin+ Monad.when isTerminalDevice . IO.hPutStr IO.stderr $+ unlines+ [ "-- You did not supply any input, so Rattletrap will read from STDIN.",+ "-- If that is unexpected, try running: "+ <> FilePath.combine "." name+ <> " --help"+ ]+ ByteString.getContents+ Just fileOrUrl -> case Client.parseUrlThrow fileOrUrl of+ Nothing -> ByteString.readFile fileOrUrl+ Just request -> do+ manager <- Client.newTlsManager+ response <- Client.httpLbs request manager+ pure (LazyByteString.toStrict (Client.responseBody response))++putOutput :: Config.Config -> LazyByteString.ByteString -> IO ()+putOutput =+ maybe LazyByteString.putStr LazyByteString.writeFile . Config.output++encodeJson :: (Json.ToJSON a) => Config.Config -> a -> LazyByteString.ByteString+encodeJson = Bool.bool Json.encodePretty Json.encode . Config.compact++getConfig :: [String] -> IO Config.Config+getConfig arguments = do+ let (flags, unexpectedArguments, unknownOptions, problems) =+ Console.getOpt' Console.Permute Option.all arguments+ Monad.forM_ unexpectedArguments $ \x ->+ IO.hPutStrLn IO.stderr $ "WARNING: unexpected argument `" <> x <> "'"+ Monad.forM_ unknownOptions $+ \x -> IO.hPutStrLn IO.stderr $ "WARNING: unknown option `" <> x <> "'"+ Monad.forM_ problems $ \x -> IO.hPutStr IO.stderr $ "ERROR: " <> x+ Monad.unless (null problems) Exit.exitFailure+ either fail pure $ Monad.foldM Config.applyFlag Config.initial flags
@@ -0,0 +1,12 @@+module Rattletrap.Console.Mode where++data Mode+ = Decode+ | Encode+ deriving (Eq, Show)++fromString :: String -> Either String Mode+fromString string = case string of+ "decode" -> Right Decode+ "encode" -> Right Encode+ _ -> Left $ "invalid mode: " <> show string
@@ -0,0 +1,68 @@+module Rattletrap.Console.Option where++import qualified Rattletrap.Console.Flag as Flag+import qualified System.Console.GetOpt as Console++type Option = Console.OptDescr Flag.Flag++all :: [Option]+all = [compact, fast, help, input, mode, output, schema, skipCrc, version]++compact :: Option+compact =+ Console.Option+ ['c']+ ["compact"]+ (Console.NoArg Flag.Compact)+ "minify JSON output"++fast :: Option+fast =+ Console.Option+ ['f']+ ["fast"]+ (Console.NoArg Flag.Fast)+ "only encode or decode the header"++help :: Option+help = Console.Option ['h'] ["help"] (Console.NoArg Flag.Help) "show the help"++input :: Option+input =+ Console.Option+ ['i']+ ["input"]+ (Console.ReqArg Flag.Input "FILE|URL")+ "input file or URL"++mode :: Option+mode =+ Console.Option+ ['m']+ ["mode"]+ (Console.ReqArg Flag.Mode "MODE")+ "decode or encode"++output :: Option+output =+ Console.Option+ ['o']+ ["output"]+ (Console.ReqArg Flag.Output "FILE")+ "output file"++schema :: Option+schema =+ Console.Option [] ["schema"] (Console.NoArg Flag.Schema) "output the schema"++skipCrc :: Option+skipCrc =+ Console.Option [] ["skip-crc"] (Console.NoArg Flag.SkipCrc) "skip the CRC"++version :: Option+version =+ Console.Option+ ['v']+ ["version"]+ (Console.NoArg Flag.Version)+ "show the version"
@@ -0,0 +1,469 @@+-- brittany --columns 120+module Rattletrap.Data where++import qualified Data.Bifunctor as Bifunctor+import qualified Data.Map as Map+import qualified Data.Set as Set+import qualified Data.Text as Text+import qualified Rattletrap.Type.AttributeType as AttributeType++parentClasses :: Map.Map Text.Text Text.Text+parentClasses =+ Map.fromList $+ fmap+ (Bifunctor.bimap Text.pack Text.pack)+ [ ("Engine.Actor", "Core.Object"),+ ("Engine.GameReplicationInfo", "Engine.ReplicationInfo"),+ ("Engine.Info", "Engine.Actor"),+ ("Engine.Pawn", "Engine.Actor"),+ ("Engine.PlayerReplicationInfo", "Engine.ReplicationInfo"),+ ("Engine.ReplicationInfo", "Engine.Info"),+ ("Engine.TeamInfo", "Engine.ReplicationInfo"),+ ("ProjectX.GRI_X", "Engine.GameReplicationInfo"),+ ("ProjectX.Pawn_X", "Engine.Pawn"),+ ("ProjectX.PRI_X", "Engine.PlayerReplicationInfo"),+ ("TAGame.Ball_God_TA", "TAGame.Ball_TA"),+ ("TAGame.Ball_Haunted_TA", "TAGame.Ball_TA"),+ ("TAGame.Ball_TA", "TAGame.RBActor_TA"),+ ("TAGame.CameraSettingsActor_TA", "Engine.ReplicationInfo"),+ ("TAGame.Car_Season_TA", "TAGame.PRI_TA"),+ ("TAGame.Car_TA", "TAGame.Vehicle_TA"),+ ("TAGame.CarComponent_Boost_TA", "TAGame.CarComponent_TA"),+ ("TAGame.CarComponent_Dodge_TA", "TAGame.CarComponent_TA"),+ ("TAGame.CarComponent_DoubleJump_TA", "TAGame.CarComponent_TA"),+ ("TAGame.CarComponent_FlipCar_TA", "TAGame.CarComponent_TA"),+ ("TAGame.CarComponent_Jump_TA", "TAGame.CarComponent_TA"),+ ("TAGame.CarComponent_TA", "Engine.ReplicationInfo"),+ ("TAGame.CrowdActor_TA", "Engine.ReplicationInfo"),+ ("TAGame.CrowdManager_TA", "Engine.ReplicationInfo"),+ ("TAGame.GameEvent_Football_TA", "TAGame.GameEvent_Soccar_TA"),+ ("TAGame.GameEvent_GodBall_TA", "TAGame.GameEvent_Soccar_TA"),+ ("TAGame.GameEvent_Season_TA", "TAGame.GameEvent_Soccar_TA"),+ ("TAGame.GameEvent_Soccar_TA", "TAGame.GameEvent_Team_TA"),+ ("TAGame.GameEvent_SoccarPrivate_TA", "TAGame.GameEvent_Soccar_TA"),+ ("TAGame.GameEvent_SoccarSplitscreen_TA", "TAGame.GameEvent_SoccarPrivate_TA"),+ ("TAGame.GameEvent_TA", "Engine.ReplicationInfo"),+ ("TAGame.GameEvent_Team_TA", "TAGame.GameEvent_TA"),+ ("TAGame.GRI_TA", "ProjectX.GRI_X"),+ ("TAGame.HauntedBallTrapTrigger_TA", "Engine.Actor"),+ ("TAGame.InMapScoreboard_TA", "Engine.Actor"),+ ("TAGame.PRI_TA", "ProjectX.PRI_X"),+ ("TAGame.RBActor_TA", "ProjectX.Pawn_X"),+ ("TAGame.SpecialPickup_BallCarSpring_TA", "TAGame.SpecialPickup_Spring_TA"),+ ("TAGame.SpecialPickup_BallFreeze_TA", "TAGame.SpecialPickup_Targeted_TA"),+ ("TAGame.SpecialPickup_BallGravity_TA", "TAGame.SpecialPickup_TA"),+ ("TAGame.SpecialPickup_BallLasso_TA", "TAGame.SpecialPickup_GrapplingHook_TA"),+ ("TAGame.SpecialPickup_BallVelcro_TA", "TAGame.SpecialPickup_TA"),+ ("TAGame.SpecialPickup_Batarang_TA", "TAGame.SpecialPickup_BallLasso_TA"),+ ("TAGame.SpecialPickup_BoostOverride_TA", "TAGame.SpecialPickup_Targeted_TA"),+ ("TAGame.SpecialPickup_Football_TA", "TAGame.SpecialPickup_TA"),+ ("TAGame.SpecialPickup_GrapplingHook_TA", "TAGame.SpecialPickup_Targeted_TA"),+ ("TAGame.SpecialPickup_HauntedBallBeam_TA", "TAGame.SpecialPickup_TA"),+ ("TAGame.SpecialPickup_HitForce_TA", "TAGame.SpecialPickup_TA"),+ ("TAGame.SpecialPickup_Rugby_TA", "TAGame.SpecialPickup_TA"),+ ("TAGame.SpecialPickup_Spring_TA", "TAGame.SpecialPickup_Targeted_TA"),+ ("TAGame.SpecialPickup_Swapper_TA", "TAGame.SpecialPickup_Targeted_TA"),+ ("TAGame.SpecialPickup_TA", "TAGame.CarComponent_TA"),+ ("TAGame.SpecialPickup_Targeted_TA", "TAGame.SpecialPickup_TA"),+ ("TAGame.SpecialPickup_Tornado_TA", "TAGame.SpecialPickup_TA"),+ ("TAGame.Team_Soccar_TA", "TAGame.Team_TA"),+ ("TAGame.Team_TA", "Engine.TeamInfo"),+ ("TAGame.Vehicle_TA", "TAGame.RBActor_TA"),+ ("TAGame.VehiclePickup_Boost_TA", "TAGame.VehiclePickup_TA"),+ ("TAGame.VehiclePickup_TA", "Engine.ReplicationInfo")+ ]++classesWithLocation :: Set.Set Text.Text+classesWithLocation =+ Set.fromList $+ fmap+ Text.pack+ [ "Archetypes.Ball.Ball_BasketBall_Mutator",+ "Archetypes.Ball.Ball_Basketball",+ "Archetypes.Ball.Ball_BasketBall",+ "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",+ "Archetypes.SpecialPickups.SpecialPickup_Rugby",+ "ProjectX.NetModeReplicator",+ "TAGame.Ball_Breakout_TA",+ "TAGame.Ball_God_TA",+ "TAGame.Ball_Haunted_TA",+ "TAGame.Ball_TA",+ "TAGame.CameraSettingsActor_TA",+ "TAGame.Cannon_TA",+ "TAGame.Car_Season_TA",+ "TAGame.Car_TA",+ "TAGame.CarComponent_Boost_TA",+ "TAGame.CarComponent_Dodge_TA",+ "TAGame.CarComponent_DoubleJump_TA",+ "TAGame.CarComponent_FlipCar_TA",+ "TAGame.CarComponent_Jump_TA",+ "TAGame.Default__CameraSettingsActor_TA",+ "TAGame.Default__PRI_TA",+ "TAGame.GameEvent_Football_TA",+ "TAGame.GameEvent_FTE_TA",+ "TAGame.GameEvent_GodBall_TA",+ "TAGame.GameEvent_Season_TA",+ "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",+ "TAGame.PRI_TA",+ "TAGame.RumblePickups_TA",+ "TAGame.SpecialPickup_BallCarSpring_TA",+ "TAGame.SpecialPickup_BallFreeze_TA",+ "TAGame.SpecialPickup_BallGravity_TA",+ "TAGame.SpecialPickup_BallLasso_TA",+ "TAGame.SpecialPickup_BallVelcro_TA",+ "TAGame.SpecialPickup_Batarang_TA",+ "TAGame.SpecialPickup_BoostOverride_TA",+ "TAGame.SpecialPickup_Football_TA",+ "TAGame.SpecialPickup_GrapplingHook_TA",+ "TAGame.SpecialPickup_HauntedBallBeam_TA",+ "TAGame.SpecialPickup_HitForce_TA",+ "TAGame.SpecialPickup_Rugby_TA",+ "TAGame.SpecialPickup_Swapper_TA",+ "TAGame.SpecialPickup_Tornado_TA",+ "TAGame.Team_Soccar_TA",+ "TheWorld:PersistentLevel.BreakOutActor_Platform_TA",+ "TheWorld:PersistentLevel.CrowdActor_TA",+ "TheWorld:PersistentLevel.CrowdManager_TA",+ "TheWorld:PersistentLevel.InMapScoreboard_TA",+ "TheWorld:PersistentLevel.VehiclePickup_Boost_TA"+ ]++classesWithRotation :: Set.Set Text.Text+classesWithRotation =+ Set.fromList $+ fmap+ Text.pack+ [ "Archetypes.Ball.Ball_BasketBall_Mutator",+ "Archetypes.Ball.Ball_Basketball",+ "Archetypes.Ball.Ball_BasketBall",+ "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",+ "Archetypes.SpecialPickups.SpecialPickup_Rugby",+ "TAGame.Ball_Breakout_TA",+ "TAGame.Ball_God_TA",+ "TAGame.Ball_Haunted_TA",+ "TAGame.Ball_TA",+ "TAGame.Car_Season_TA",+ "TAGame.Car_TA"+ ]++objectClasses :: Map.Map Text.Text Text.Text+objectClasses =+ Map.fromList $+ fmap+ (Bifunctor.bimap Text.pack Text.pack)+ [ ("Archetypes.Ball.Ball_Anniversary", "TAGame.Ball_TA"),+ ("Archetypes.Ball.Ball_BasketBall_Mutator", "TAGame.Ball_TA"),+ ("Archetypes.Ball.Ball_Basketball", "TAGame.Ball_TA"),+ ("Archetypes.Ball.Ball_BasketBall", "TAGame.Ball_TA"),+ ("Archetypes.Ball.Ball_Beachball", "TAGame.Ball_TA"),+ ("Archetypes.Ball.Ball_Breakout", "TAGame.Ball_Breakout_TA"),+ ("Archetypes.Ball.Ball_Default", "TAGame.Ball_TA"),+ ("Archetypes.Ball.Ball_Football", "TAGame.Ball_TA"),+ ("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.Car.Car_PostGameLobby", "TAGame.Car_TA"),+ ("Archetypes.CarComponents.CarComponent_Boost", "TAGame.CarComponent_Boost_TA"),+ ("Archetypes.CarComponents.CarComponent_Dodge", "TAGame.CarComponent_Dodge_TA"),+ ("Archetypes.CarComponents.CarComponent_DoubleJump", "TAGame.CarComponent_DoubleJump_TA"),+ ("Archetypes.CarComponents.CarComponent_FlipCar", "TAGame.CarComponent_FlipCar_TA"),+ ("Archetypes.CarComponents.CarComponent_Jump", "TAGame.CarComponent_Jump_TA"),+ ("Archetypes.GameEvent.GameEvent_Basketball", "TAGame.GameEvent_Soccar_TA"),+ ("Archetypes.GameEvent.GameEvent_BasketballPrivate", "TAGame.GameEvent_SoccarPrivate_TA"),+ ("Archetypes.GameEvent.GameEvent_BasketballSplitscreen", "TAGame.GameEvent_SoccarSplitscreen_TA"),+ ("Archetypes.GameEvent.GameEvent_Breakout", "TAGame.GameEvent_Soccar_TA"),+ ("Archetypes.GameEvent.GameEvent_FTE_Part1_Prime", "TAGame.GameEvent_FTE_TA"),+ ("Archetypes.GameEvent.GameEvent_Hockey", "TAGame.GameEvent_Soccar_TA"),+ ("Archetypes.GameEvent.GameEvent_HockeyPrivate", "TAGame.GameEvent_SoccarPrivate_TA"),+ ("Archetypes.GameEvent.GameEvent_HockeySplitscreen", "TAGame.GameEvent_SoccarSplitscreen_TA"),+ ("Archetypes.GameEvent.GameEvent_Items", "TAGame.GameEvent_Soccar_TA"),+ ("Archetypes.GameEvent.GameEvent_Season:CarArchetype", "TAGame.Car_TA"),+ ("Archetypes.GameEvent.GameEvent_Season", "TAGame.GameEvent_Season_TA"),+ ("Archetypes.GameEvent.GameEvent_Soccar", "TAGame.GameEvent_Soccar_TA"),+ ("Archetypes.GameEvent.GameEvent_SoccarLan", "TAGame.GameEvent_Soccar_TA"),+ ("Archetypes.GameEvent.GameEvent_SoccarPrivate", "TAGame.GameEvent_SoccarPrivate_TA"),+ ("Archetypes.GameEvent.GameEvent_SoccarSplitscreen", "TAGame.GameEvent_SoccarSplitscreen_TA"),+ ("Archetypes.SpecialPickups.SpecialPickup_BallFreeze", "TAGame.SpecialPickup_BallFreeze_TA"),+ ("Archetypes.SpecialPickups.SpecialPickup_BallGrapplingHook", "TAGame.SpecialPickup_GrapplingHook_TA"),+ ("Archetypes.SpecialPickups.SpecialPickup_BallLasso", "TAGame.SpecialPickup_BallLasso_TA"),+ ("Archetypes.SpecialPickups.SpecialPickup_BallSpring", "TAGame.SpecialPickup_BallCarSpring_TA"),+ ("Archetypes.SpecialPickups.SpecialPickup_BallVelcro", "TAGame.SpecialPickup_BallVelcro_TA"),+ ("Archetypes.SpecialPickups.SpecialPickup_Batarang", "TAGame.SpecialPickup_Batarang_TA"),+ ("Archetypes.SpecialPickups.SpecialPickup_BoostOverride", "TAGame.SpecialPickup_BoostOverride_TA"),+ ("Archetypes.SpecialPickups.SpecialPickup_CarSpring", "TAGame.SpecialPickup_BallCarSpring_TA"),+ ("Archetypes.SpecialPickups.SpecialPickup_Football", "TAGame.SpecialPickup_Football_TA"),+ ("Archetypes.SpecialPickups.SpecialPickup_GravityWell", "TAGame.SpecialPickup_BallGravity_TA"),+ ("Archetypes.SpecialPickups.SpecialPickup_HauntedBallBeam", "TAGame.SpecialPickup_HauntedBallBeam_TA"),+ ("Archetypes.SpecialPickups.SpecialPickup_Rugby", "TAGame.SpecialPickup_Rugby_TA"),+ ("Archetypes.SpecialPickups.SpecialPickup_StrongHit", "TAGame.SpecialPickup_HitForce_TA"),+ ("Archetypes.SpecialPickups.SpecialPickup_Swapper", "TAGame.SpecialPickup_Swapper_TA"),+ ("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"),+ ("GameInfo_FootBall.GameInfo.GameInfo_FootBall:GameReplicationInfoArchetype", "TAGame.GRI_TA"),+ ("GameInfo_FTE.GameInfo.GameInfo_FTE:GameReplicationInfoArchetype", "TAGame.GRI_TA"),+ ("gameinfo_godball.GameInfo.gameinfo_godball:Archetype", "TAGame.GameEvent_GodBall_TA"),+ ("GameInfo_GodBall.GameInfo.GameInfo_GodBall:Archetype", "TAGame.GameEvent_GodBall_TA"),+ ("gameinfo_godball.GameInfo.gameinfo_godball:GameReplicationInfoArchetype", "TAGame.GRI_TA"),+ ("GameInfo_GodBall.GameInfo.GameInfo_GodBall:GameReplicationInfoArchetype", "TAGame.GRI_TA"),+ ("Gameinfo_Hockey.GameInfo.Gameinfo_Hockey:GameReplicationInfoArchetype", "TAGame.GRI_TA"),+ ("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"),+ ("TAGame.Default__CameraSettingsActor_TA", "TAGame.CameraSettingsActor_TA"),+ ("TAGame.Default__MaxTimeWarningData_TA", "TAGame.MaxTimeWarningData_TA"),+ ("TAGame.Default__PickupTimer_TA", "TAGame.PickupTimer_TA"),+ ("TAGame.Default__PRI_TA", "TAGame.PRI_TA"),+ ("TAGame.Default__RumblePickups_TA", "TAGame.RumblePickups_TA"),+ ("TheWorld:PersistentLevel.BreakOutActor_Platform_TA", "TAGame.BreakOutActor_Platform_TA"),+ ("TheWorld:PersistentLevel.CrowdActor_TA", "TAGame.CrowdActor_TA"),+ ("TheWorld:PersistentLevel.CrowdManager_TA", "TAGame.CrowdManager_TA"),+ ("TheWorld:PersistentLevel.InMapScoreboard_TA", "TAGame.InMapScoreboard_TA"),+ ("TheWorld:PersistentLevel.VehiclePickup_Boost_TA", "TAGame.VehiclePickup_Boost_TA")+ ]++attributeTypes :: Map.Map Text.Text AttributeType.AttributeType+attributeTypes =+ Map.fromList $+ fmap+ (Bifunctor.first Text.pack)+ [ ("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),+ ("Engine.PlayerReplicationInfo:bReadyToPlay", AttributeType.Boolean),+ ("Engine.PlayerReplicationInfo:bTimedOut", AttributeType.Boolean),+ ("Engine.PlayerReplicationInfo:bWaitingPlayer", AttributeType.Boolean),+ ("Engine.PlayerReplicationInfo:Ping", AttributeType.Byte),+ ("Engine.PlayerReplicationInfo:PlayerID", AttributeType.Int),+ ("Engine.PlayerReplicationInfo:PlayerName", AttributeType.String),+ ("Engine.PlayerReplicationInfo:RemoteUserData", AttributeType.String),+ ("Engine.PlayerReplicationInfo:Score", AttributeType.Int),+ ("Engine.PlayerReplicationInfo:Team", AttributeType.FlaggedInt),+ ("Engine.PlayerReplicationInfo:UniqueId", AttributeType.UniqueId),+ ("Engine.ReplicatedActor_ORS:ReplicatedOwner", AttributeType.FlaggedInt),+ ("Engine.TeamInfo:Score", AttributeType.Int),+ ("ProjectX.GRI_X:bGameStarted", AttributeType.Boolean),+ ("ProjectX.GRI_X:GameServerID", AttributeType.GameServer),+ ("ProjectX.GRI_X:MatchGuid", AttributeType.String),+ ("ProjectX.GRI_X:MatchGUID", AttributeType.String),+ ("ProjectX.GRI_X:ReplicatedGameMutatorIndex", AttributeType.Int),+ ("ProjectX.GRI_X:ReplicatedGamePlaylist", AttributeType.Int),+ ("ProjectX.GRI_X:ReplicatedServerRegion", AttributeType.String),+ ("ProjectX.GRI_X:Reservations", AttributeType.Reservation),+ ("TAGame.Ball_Breakout_TA:AppliedDamage", AttributeType.AppliedDamage),+ ("TAGame.Ball_Breakout_TA:DamageIndex", AttributeType.Int),+ ("TAGame.Ball_Breakout_TA:LastTeamTouch", AttributeType.Byte),+ ("TAGame.Ball_God_TA:TargetSpeed", AttributeType.Float),+ ("TAGame.Ball_Haunted_TA:bIsBallBeamed", AttributeType.Boolean),+ ("TAGame.Ball_Haunted_TA:DeactivatedGoalIndex", AttributeType.Byte),+ ("TAGame.Ball_Haunted_TA:LastTeamTouch", AttributeType.Byte),+ ("TAGame.Ball_Haunted_TA:ReplicatedBeamBrokenValue", AttributeType.Byte),+ ("TAGame.Ball_Haunted_TA:TotalActiveBeams", AttributeType.Byte),+ ("TAGame.Ball_TA:GameEvent", AttributeType.FlaggedInt),+ ("TAGame.Ball_TA:HitTeamNum", AttributeType.Byte),+ ("TAGame.Ball_TA:ReplicatedAddedCarBounceScale", AttributeType.Float),+ ("TAGame.Ball_TA:ReplicatedBallMaxLinearSpeedScale", AttributeType.Float),+ ("TAGame.Ball_TA:ReplicatedBallScale", AttributeType.Float),+ ("TAGame.Ball_TA:ReplicatedExplosionData", AttributeType.Explosion),+ ("TAGame.Ball_TA:ReplicatedExplosionDataExtended", AttributeType.ExtendedExplosion),+ ("TAGame.Ball_TA:ReplicatedPhysMatOverride", AttributeType.FlaggedInt),+ ("TAGame.Ball_TA:ReplicatedWorldBounceScale", AttributeType.Float),+ ("TAGame.BreakOutActor_Platform_TA:DamageState", AttributeType.DamageState),+ ("TAGame.CameraSettingsActor_TA:bMouseCameraToggleEnabled", AttributeType.Boolean),+ ("TAGame.CameraSettingsActor_TA:bUsingBehindView", AttributeType.Boolean),+ ("TAGame.CameraSettingsActor_TA:bUsingSecondaryCamera", AttributeType.Boolean),+ ("TAGame.CameraSettingsActor_TA:bUsingSwivel", AttributeType.Boolean),+ ("TAGame.CameraSettingsActor_TA:CameraPitch", AttributeType.Byte),+ ("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:ReplicatedDemolishExtended", AttributeType.CustomDemolishExtended),+ ("TAGame.Car_TA:ReplicatedDemolishGoalExplosion", AttributeType.CustomDemolish),+ ("TAGame.Car_TA:RumblePickups", AttributeType.FlaggedInt),+ ("TAGame.Car_TA:TeamPaint", AttributeType.TeamPaint),+ ("TAGame.CarComponent_Boost_TA:bNoBoost", AttributeType.Boolean),+ ("TAGame.CarComponent_Boost_TA:BoostModifier", AttributeType.Float),+ ("TAGame.CarComponent_Boost_TA:bUnlimitedBoost", AttributeType.Boolean),+ ("TAGame.CarComponent_Boost_TA:RechargeDelay", AttributeType.Float),+ ("TAGame.CarComponent_Boost_TA:RechargeRate", AttributeType.Float),+ ("TAGame.CarComponent_Boost_TA:ReplicatedBoost", AttributeType.Boost),+ ("TAGame.CarComponent_Boost_TA:ReplicatedBoostAmount", AttributeType.Byte),+ ("TAGame.CarComponent_Boost_TA:UnlimitedBoostRefCount", AttributeType.Int),+ ("TAGame.CarComponent_Dodge_TA:DodgeImpulse", AttributeType.Location),+ ("TAGame.CarComponent_Dodge_TA:DodgeTorque", AttributeType.Location),+ ("TAGame.CarComponent_DoubleJump_TA:DoubleJumpImpulse", AttributeType.Location),+ ("TAGame.CarComponent_FlipCar_TA:bFlipRight", AttributeType.Boolean),+ ("TAGame.CarComponent_FlipCar_TA:FlipCarTime", AttributeType.Float),+ ("TAGame.CarComponent_TA:ReplicatedActive", AttributeType.Byte),+ ("TAGame.CarComponent_TA:ReplicatedActivityTime", AttributeType.Float),+ ("TAGame.CarComponent_TA:Vehicle", AttributeType.FlaggedInt),+ ("TAGame.CrowdActor_TA:GameEvent", AttributeType.FlaggedInt),+ ("TAGame.CrowdActor_TA:ModifiedNoise", AttributeType.Float),+ ("TAGame.CrowdActor_TA:ReplicatedCountDownNumber", AttributeType.Int),+ ("TAGame.CrowdActor_TA:ReplicatedOneShotSound", AttributeType.FlaggedInt),+ ("TAGame.CrowdActor_TA:ReplicatedRoundCountDownNumber", AttributeType.Int),+ ("TAGame.CrowdManager_TA:GameEvent", AttributeType.FlaggedInt),+ ("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),+ ("TAGame.GameEvent_Soccar_TA:ReplicatedStatEvent", AttributeType.StatEvent),+ ("TAGame.GameEvent_Soccar_TA:RoundNum", AttributeType.Int),+ ("TAGame.GameEvent_Soccar_TA:SecondsRemaining", AttributeType.Int),+ ("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),+ ("TAGame.GameEvent_TA:GameMode", AttributeType.GameMode),+ ("TAGame.GameEvent_TA:MatchStartEpoch", AttributeType.Int64),+ ("TAGame.GameEvent_TA:MatchTypeClass", AttributeType.FlaggedInt),+ ("TAGame.GameEvent_TA:ReplicatedGameStateTimeRemaining", AttributeType.Int),+ ("TAGame.GameEvent_TA:ReplicatedRoundCountDownNumber", AttributeType.Int),+ ("TAGame.GameEvent_TA:ReplicatedStateIndex", AttributeType.Byte),+ ("TAGame.GameEvent_TA:ReplicatedStateName", AttributeType.Int),+ ("TAGame.GameEvent_Team_TA:bForfeit", AttributeType.Boolean),+ ("TAGame.GameEvent_Team_TA:MaxTeamSize", AttributeType.Int),+ ("TAGame.GRI_TA:NewDedicatedServerIP", AttributeType.String),+ ("TAGame.MaxTimeWarningData_TA:EndGameEpochTime", AttributeType.Int64),+ ("TAGame.MaxTimeWarningData_TA:EndGameWarningEpochTime", AttributeType.Int64),+ ("TAGame.PRI_TA:bIsDistracted", AttributeType.Boolean),+ ("TAGame.PRI_TA:bIsInSplitScreen", AttributeType.Boolean),+ ("TAGame.PRI_TA:bMatchMVP", AttributeType.Boolean),+ ("TAGame.PRI_TA:bOnlineLoadoutSet", AttributeType.Boolean),+ ("TAGame.PRI_TA:bOnlineLoadoutsSet", AttributeType.Boolean),+ ("TAGame.PRI_TA:BotProductName", AttributeType.Int),+ ("TAGame.PRI_TA:bReady", AttributeType.Boolean),+ ("TAGame.PRI_TA:bUsingBehindView", AttributeType.Boolean),+ ("TAGame.PRI_TA:bUsingItems", AttributeType.Boolean),+ ("TAGame.PRI_TA:bUsingSecondaryCamera", AttributeType.Boolean),+ ("TAGame.PRI_TA:CameraPitch", AttributeType.Byte),+ ("TAGame.PRI_TA:CameraSettings", AttributeType.CamSettings),+ ("TAGame.PRI_TA:CameraYaw", AttributeType.Byte),+ ("TAGame.PRI_TA:ClientLoadout", AttributeType.Loadout),+ ("TAGame.PRI_TA:ClientLoadoutOnline", AttributeType.LoadoutOnline),+ ("TAGame.PRI_TA:ClientLoadouts", AttributeType.Loadouts),+ ("TAGame.PRI_TA:ClientLoadoutsOnline", AttributeType.LoadoutsOnline),+ ("TAGame.PRI_TA:ClubID", AttributeType.Int64),+ ("TAGame.PRI_TA:CurrentVoiceRoom", AttributeType.String),+ ("TAGame.PRI_TA:MatchAssists", AttributeType.Int),+ ("TAGame.PRI_TA:MatchBreakoutDamage", AttributeType.Int),+ ("TAGame.PRI_TA:MatchGoals", AttributeType.Int),+ ("TAGame.PRI_TA:MatchSaves", AttributeType.Int),+ ("TAGame.PRI_TA:MatchScore", AttributeType.Int),+ ("TAGame.PRI_TA:MatchShots", AttributeType.Int),+ ("TAGame.PRI_TA:MaxTimeTillItem", AttributeType.Int),+ ("TAGame.PRI_TA:PartyLeader", AttributeType.PartyLeader),+ ("TAGame.PRI_TA:PawnType", AttributeType.Byte),+ ("TAGame.PRI_TA:PersistentCamera", AttributeType.FlaggedInt),+ ("TAGame.PRI_TA:PlayerHistoryKey", AttributeType.PlayerHistoryKey),+ ("TAGame.PRI_TA:PlayerHistoryValid", AttributeType.Boolean),+ ("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),+ ("TAGame.PRI_TA:SteeringSensitivity", AttributeType.Float),+ ("TAGame.PRI_TA:TimeTillItem", AttributeType.Int),+ ("TAGame.PRI_TA:Title", AttributeType.Int),+ ("TAGame.PRI_TA:TotalXP", AttributeType.Int),+ ("TAGame.RBActor_TA:bFrozen", AttributeType.Boolean),+ ("TAGame.RBActor_TA:bIgnoreSyncing", AttributeType.Boolean),+ ("TAGame.RBActor_TA:bReplayActor", AttributeType.Boolean),+ ("TAGame.RBActor_TA:ReplicatedRBState", AttributeType.RigidBodyState),+ ("TAGame.RBActor_TA:WeldedInfo", AttributeType.WeldedInfo),+ ("TAGame.RumblePickups_TA:AttachedPickup", AttributeType.FlaggedInt),+ ("TAGame.RumblePickups_TA:ConcurrentItemCount", AttributeType.Int),+ ("TAGame.RumblePickups_TA:PickupInfo", AttributeType.PickupInfo),+ ("TAGame.SpecialPickup_BallFreeze_TA:RepOrigSpeed", AttributeType.Float),+ ("TAGame.SpecialPickup_BallVelcro_TA:AttachTime", AttributeType.Float),+ ("TAGame.SpecialPickup_BallVelcro_TA:bBroken", AttributeType.Boolean),+ ("TAGame.SpecialPickup_BallVelcro_TA:bHit", AttributeType.Boolean),+ ("TAGame.SpecialPickup_BallVelcro_TA:BreakTime", AttributeType.Float),+ ("TAGame.SpecialPickup_Football_TA:WeldedBall", AttributeType.FlaggedInt),+ ("TAGame.SpecialPickup_Rugby_TA:bBallWelded", AttributeType.Boolean),+ ("TAGame.SpecialPickup_Targeted_TA:Targeted", AttributeType.FlaggedInt),+ ("TAGame.Team_Soccar_TA:GameScore", AttributeType.Int),+ ("TAGame.Team_TA:ClubColors", AttributeType.ClubColors),+ ("TAGame.Team_TA:ClubID", AttributeType.Int64),+ ("TAGame.Team_TA:CustomTeamName", AttributeType.String),+ ("TAGame.Team_TA:Difficulty", AttributeType.Int),+ ("TAGame.Team_TA:GameEvent", AttributeType.FlaggedInt),+ ("TAGame.Team_TA:LogoData", AttributeType.FlaggedInt),+ ("TAGame.Vehicle_TA:bDriving", AttributeType.Boolean),+ ("TAGame.Vehicle_TA:bHasPostMatchCelebration", 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),+ ("TAGame.VehiclePickup_TA:bNoPickup", AttributeType.Boolean),+ ("TAGame.VehiclePickup_TA:NewReplicatedPickupData", AttributeType.PickupNew),+ ("TAGame.VehiclePickup_TA:ReplicatedPickupData", AttributeType.Pickup)+ ]
@@ -0,0 +1,11 @@+module Rattletrap.Exception.CrcMismatch where++import qualified Control.Exception as Exception+import qualified Data.Word as Word++data CrcMismatch = CrcMismatch Word.Word32 Word.Word32+ deriving (Eq, Show)++instance Exception.Exception CrcMismatch where+ displayException (CrcMismatch x y) =+ unwords ["invalid CRC: expected", show x, "but got", show y]
@@ -0,0 +1,8 @@+module Rattletrap.Exception.Empty where++import qualified Control.Exception as Exception++data Empty = Empty+ deriving (Eq, Show)++instance Exception.Exception Empty
@@ -0,0 +1,10 @@+module Rattletrap.Exception.Fail where++import qualified Control.Exception as Exception++newtype Fail+ = Fail String+ deriving (Eq, Show)++instance Exception.Exception Fail where+ displayException (Fail x) = x
@@ -0,0 +1,9 @@+module Rattletrap.Exception.InvalidComponent where++import qualified Control.Exception as Exception++newtype InvalidComponent+ = InvalidComponent Word+ deriving (Eq, Show)++instance Exception.Exception InvalidComponent
@@ -0,0 +1,9 @@+module Rattletrap.Exception.InvalidJson where++import qualified Control.Exception as Exception++newtype InvalidJson+ = InvalidJson String+ deriving (Eq, Show)++instance Exception.Exception InvalidJson
@@ -0,0 +1,9 @@+module Rattletrap.Exception.MissingAttributeLimit where++import qualified Control.Exception as Exception++newtype MissingAttributeLimit+ = MissingAttributeLimit Word+ deriving (Eq, Show)++instance Exception.Exception MissingAttributeLimit
@@ -0,0 +1,9 @@+module Rattletrap.Exception.MissingAttributeName where++import qualified Control.Exception as Exception++newtype MissingAttributeName+ = MissingAttributeName Word+ deriving (Eq, Show)++instance Exception.Exception MissingAttributeName
@@ -0,0 +1,9 @@+module Rattletrap.Exception.MissingClassName where++import qualified Control.Exception as Exception++newtype MissingClassName+ = MissingClassName String+ deriving (Eq, Show)++instance Exception.Exception MissingClassName
@@ -0,0 +1,10 @@+module Rattletrap.Exception.MissingObjectName where++import qualified Control.Exception as Exception+import qualified Data.Word as Word++newtype MissingObjectName+ = MissingObjectName Word.Word32+ deriving (Eq, Show)++instance Exception.Exception MissingObjectName
@@ -0,0 +1,10 @@+module Rattletrap.Exception.MissingProductName where++import qualified Control.Exception as Exception+import qualified Data.Word as Word++newtype MissingProductName+ = MissingProductName Word.Word32+ deriving (Eq, Show)++instance Exception.Exception MissingProductName
@@ -0,0 +1,8 @@+module Rattletrap.Exception.NotEnoughInput where++import qualified Control.Exception as Exception++data NotEnoughInput = NotEnoughInput+ deriving (Eq, Show)++instance Exception.Exception NotEnoughInput
@@ -0,0 +1,9 @@+module Rattletrap.Exception.UnknownActor where++import qualified Control.Exception as Exception++newtype UnknownActor+ = UnknownActor Word+ deriving (Eq, Show)++instance Exception.Exception UnknownActor
@@ -0,0 +1,9 @@+module Rattletrap.Exception.UnknownAttribute where++import qualified Control.Exception as Exception++newtype UnknownAttribute+ = UnknownAttribute String+ deriving (Eq, Show)++instance Exception.Exception UnknownAttribute
@@ -0,0 +1,10 @@+module Rattletrap.Exception.UnknownName where++import qualified Control.Exception as Exception+import qualified Data.Word as Word++newtype UnknownName+ = UnknownName Word.Word32+ deriving (Eq, Show)++instance Exception.Exception UnknownName
@@ -0,0 +1,9 @@+module Rattletrap.Exception.UnknownProduct where++import qualified Control.Exception as Exception++newtype UnknownProduct+ = UnknownProduct String+ deriving (Eq, Show)++instance Exception.Exception UnknownProduct
@@ -0,0 +1,9 @@+module Rattletrap.Exception.UnknownProperty where++import qualified Control.Exception as Exception++newtype UnknownProperty+ = UnknownProperty String+ deriving (Eq, Show)++instance Exception.Exception UnknownProperty
@@ -0,0 +1,10 @@+module Rattletrap.Exception.UnknownSystemId where++import qualified Control.Exception as Exception+import qualified Data.Word as Word++newtype UnknownSystemId+ = UnknownSystemId Word.Word8+ deriving (Eq, Show)++instance Exception.Exception UnknownSystemId
@@ -0,0 +1,68 @@+module Rattletrap.Get where++import qualified Control.Applicative as Applicative+import qualified Control.Exception as Exception+import qualified Data.Bifunctor as Bifunctor+import qualified Rattletrap.Exception.Empty as Empty+import qualified Rattletrap.Exception.Fail as Fail++newtype Get s m a = Get (s -> m (Either ([String], Exception.SomeException) (s, a)))++instance (Functor m) => Functor (Get s m) where+ fmap f g = Get $ fmap (fmap (fmap f)) . run g++instance (Monad m) => Applicative (Get s m) where+ pure x = Get $ \s -> pure $ Right (s, x)++ gf <*> gx = Get $ \s1 -> do+ r <- run gf s1+ case r of+ Left e -> pure $ Left e+ Right (s2, f) -> run (fmap f gx) s2++instance (Monad m) => Monad (Get s m) where+ g >>= f = Get $ \s1 -> do+ r <- run g s1+ case r of+ Left e -> pure $ Left e+ Right (s2, x) -> run (f x) s2++instance (Monad m) => MonadFail (Get s m) where+ fail = throw . Fail.Fail++instance (Monad m) => Applicative.Alternative (Get s m) where+ empty = throw Empty.Empty++ gx <|> gy = Get $ \s -> do+ r <- run gx s+ case r of+ Left _ -> run gy s+ Right x -> pure $ Right x++run :: Get s m a -> s -> m (Either ([String], Exception.SomeException) (s, a))+run (Get f) = f++get :: (Applicative m) => Get s m s+get = Get $ \s -> pure $ Right (s, s)++put :: (Applicative m) => s -> Get s m ()+put s = Get $ \_ -> pure $ Right (s, ())++lift :: (Functor m) => m a -> Get s m a+lift m = Get $ \s -> fmap (\x -> Right (s, x)) m++throw :: (Exception.Exception e, Applicative m) => e -> Get s m a+throw = Get . const . pure . Left . (,) [] . Exception.toException++embed :: (Monad m) => Get s m a -> s -> Get t m a+embed g s = do+ r <- lift $ run g s+ case r of+ Left (ls, e) -> labels ls $ throw e+ Right (_, x) -> pure x++labels :: (Functor m) => [String] -> Get s m a -> Get s m a+labels ls g = Get $ fmap (Bifunctor.first $ Bifunctor.first (ls <>)) . run g++label :: (Functor m) => String -> Get s m a -> Get s m a+label = labels . pure
@@ -0,0 +1,68 @@+module Rattletrap.Schema where++import qualified Data.Text as Text+import qualified Rattletrap.Utility.Json as Json++data Schema = Schema+ { name :: Text.Text,+ json :: Json.Value+ }+ deriving (Eq, Show)++named :: String -> Json.Value -> Schema+named n j = Schema {name = Text.pack n, json = j}++ref :: Schema -> Json.Value+ref s = Json.object [Json.pair "$ref" $ Text.pack "#/definitions/" <> name s]++object :: [((Json.Key, Json.Value), Bool)] -> Json.Value+object xs =+ Json.object+ [ Json.pair "type" "object",+ Json.pair "properties" . Json.object $+ fmap+ ((\(k, v) -> Json.pair (Json.keyToString k) v) . fst)+ xs,+ Json.pair "required" . fmap (fst . fst) $ filter snd xs+ ]++maybe :: Schema -> Schema+maybe s =+ Schema+ { name = Text.pack "maybe-" <> name s,+ json = oneOf [ref s, json Rattletrap.Schema.null]+ }++oneOf :: [Json.Value] -> Json.Value+oneOf xs = Json.object [Json.pair "oneOf" xs]++tuple :: [Json.Value] -> Json.Value+tuple xs =+ Json.object+ [ Json.pair "type" "array",+ Json.pair "items" xs,+ Json.pair "minItems" $ length xs,+ Json.pair "maxItems" $ length xs+ ]++array :: Schema -> Schema+array s =+ Schema+ { name = Text.pack "array-" <> name s,+ json = Json.object [Json.pair "type" "array", Json.pair "items" $ ref s]+ }++boolean :: Schema+boolean = named "boolean" $ Json.object [Json.pair "type" "boolean"]++integer :: Schema+integer = named "integer" $ Json.object [Json.pair "type" "integer"]++null :: Schema+null = named "null" $ Json.object [Json.pair "type" "null"]++number :: Schema+number = named "number" $ Json.object [Json.pair "type" "number"]++string :: Schema+string = named "string" $ Json.object [Json.pair "type" "string"]
@@ -0,0 +1,109 @@+module Rattletrap.Type.Attribute where++import qualified Control.Exception as Exception+import qualified Data.Map as Map+import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Exception.MissingAttributeLimit as MissingAttributeLimit+import qualified Rattletrap.Exception.MissingAttributeName as MissingAttributeName+import qualified Rattletrap.Exception.UnknownActor as UnknownActor+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.AttributeValue as AttributeValue+import qualified Rattletrap.Type.ClassAttributeMap as ClassAttributeMap+import qualified Rattletrap.Type.CompressedWord as CompressedWord+import qualified Rattletrap.Type.Str as Str+import qualified Rattletrap.Type.U32 as U32+import qualified Rattletrap.Type.Version as Version+import qualified Rattletrap.Utility.Json as Json++data Attribute = Attribute+ { id :: CompressedWord.CompressedWord,+ -- | Read-only! Changing an attribute's name requires editing the class+ -- attribute map.+ name :: Str.Str,+ value :: AttributeValue.AttributeValue+ }+ deriving (Eq, Show)++instance Json.FromJSON Attribute where+ parseJSON = Json.withObject "Attribute" $ \object -> do+ id_ <- Json.required object "id"+ name <- Json.required object "name"+ value <- Json.required object "value"+ pure Attribute {Rattletrap.Type.Attribute.id = id_, name, value}++instance Json.ToJSON Attribute where+ toJSON x =+ Json.object+ [ Json.pair "id" $ Rattletrap.Type.Attribute.id x,+ Json.pair "name" $ name x,+ Json.pair "value" $ value x+ ]++schema :: Schema.Schema+schema =+ Schema.named "attribute" $+ Schema.object+ [ (Json.pair "id" $ Schema.ref CompressedWord.schema, True),+ (Json.pair "name" $ Schema.ref Str.schema, True),+ (Json.pair "value" $ Schema.ref AttributeValue.schema, True)+ ]++bitPut :: Attribute -> BitPut.BitPut+bitPut attribute =+ CompressedWord.bitPut (Rattletrap.Type.Attribute.id attribute)+ <> AttributeValue.bitPut (value attribute)++bitGet ::+ Version.Version ->+ Maybe Str.Str ->+ ClassAttributeMap.ClassAttributeMap ->+ Map.Map CompressedWord.CompressedWord U32.U32 ->+ CompressedWord.CompressedWord ->+ BitGet.BitGet Attribute+bitGet version buildVersion classes actors actor =+ BitGet.label "Attribute" $ do+ attributes <- lookupAttributeMap classes actors actor+ limit <- lookupAttributeIdLimit attributes actor+ id_ <- BitGet.label "id" $ CompressedWord.bitGet limit+ name <- lookupAttributeName classes attributes id_+ value <-+ BitGet.label "value" $+ AttributeValue.bitGet+ version+ buildVersion+ (ClassAttributeMap.objectMap classes)+ name+ pure Attribute {Rattletrap.Type.Attribute.id = id_, name, value}++lookupAttributeMap ::+ ClassAttributeMap.ClassAttributeMap ->+ Map.Map CompressedWord.CompressedWord U32.U32 ->+ CompressedWord.CompressedWord ->+ BitGet.BitGet (Map.Map U32.U32 U32.U32)+lookupAttributeMap classes actors actor =+ fromMaybe+ (UnknownActor.UnknownActor $ CompressedWord.value actor)+ (ClassAttributeMap.getAttributeMap classes actors actor)++lookupAttributeIdLimit ::+ Map.Map U32.U32 U32.U32 ->+ CompressedWord.CompressedWord ->+ BitGet.BitGet Word+lookupAttributeIdLimit attributes actor =+ fromMaybe+ (MissingAttributeLimit.MissingAttributeLimit $ CompressedWord.value actor)+ (ClassAttributeMap.getAttributeIdLimit attributes)++lookupAttributeName ::+ ClassAttributeMap.ClassAttributeMap ->+ Map.Map U32.U32 U32.U32 ->+ CompressedWord.CompressedWord ->+ BitGet.BitGet Str.Str+lookupAttributeName classes attributes attribute =+ fromMaybe+ (MissingAttributeName.MissingAttributeName $ CompressedWord.value attribute)+ (ClassAttributeMap.getAttributeName classes attributes attribute)++fromMaybe :: (Exception.Exception e) => e -> Maybe a -> BitGet.BitGet a+fromMaybe message = maybe (BitGet.throw message) pure
@@ -0,0 +1,60 @@+module Rattletrap.Type.Attribute.AppliedDamage where++import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.I32 as I32+import qualified Rattletrap.Type.U8 as U8+import qualified Rattletrap.Type.Vector as Vector+import qualified Rattletrap.Type.Version as Version+import qualified Rattletrap.Utility.Json as Json++data AppliedDamage = AppliedDamage+ { unknown1 :: U8.U8,+ location :: Vector.Vector,+ unknown3 :: I32.I32,+ unknown4 :: I32.I32+ }+ deriving (Eq, Show)++instance Json.FromJSON AppliedDamage where+ parseJSON = Json.withObject "AppliedDamage" $ \object -> do+ unknown1 <- Json.required object "unknown1"+ location <- Json.required object "location"+ unknown3 <- Json.required object "unknown3"+ unknown4 <- Json.required object "unknown4"+ pure AppliedDamage {unknown1, location, unknown3, unknown4}++instance Json.ToJSON AppliedDamage where+ toJSON x =+ Json.object+ [ Json.pair "unknown1" $ unknown1 x,+ Json.pair "location" $ location x,+ Json.pair "unknown3" $ unknown3 x,+ Json.pair "unknown4" $ unknown4 x+ ]++schema :: Schema.Schema+schema =+ Schema.named "attribute-applied-damage" $+ Schema.object+ [ (Json.pair "unknown1" $ Schema.ref U8.schema, True),+ (Json.pair "location" $ Schema.ref Vector.schema, True),+ (Json.pair "unknown3" $ Schema.ref I32.schema, True),+ (Json.pair "unknown4" $ Schema.ref I32.schema, True)+ ]++bitPut :: AppliedDamage -> BitPut.BitPut+bitPut appliedDamageAttribute =+ U8.bitPut (unknown1 appliedDamageAttribute)+ <> Vector.bitPut (location appliedDamageAttribute)+ <> I32.bitPut (unknown3 appliedDamageAttribute)+ <> I32.bitPut (unknown4 appliedDamageAttribute)++bitGet :: Version.Version -> BitGet.BitGet AppliedDamage+bitGet version = BitGet.label "AppliedDamage" $ do+ unknown1 <- BitGet.label "unknown1" U8.bitGet+ location <- BitGet.label "location" $ Vector.bitGet version+ unknown3 <- BitGet.label "unknown3" I32.bitGet+ unknown4 <- BitGet.label "unknown4" I32.bitGet+ pure AppliedDamage {unknown1, location, unknown3, unknown4}
@@ -0,0 +1,28 @@+module Rattletrap.Type.Attribute.Boolean where++import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Utility.Json as Json++newtype Boolean = Boolean+ { value :: Bool+ }+ deriving (Eq, Show)++instance Json.FromJSON Boolean where+ parseJSON = fmap Boolean . Json.parseJSON++instance Json.ToJSON Boolean where+ toJSON = Json.toJSON . value++schema :: Schema.Schema+schema = Schema.named "attribute-boolean" $ Schema.ref Schema.boolean++bitPut :: Boolean -> BitPut.BitPut+bitPut booleanAttribute = BitPut.bool (value booleanAttribute)++bitGet :: BitGet.BitGet Boolean+bitGet = BitGet.label "Boolean" $ do+ value <- BitGet.label "value" BitGet.bool+ pure Boolean {value}
@@ -0,0 +1,57 @@+module Rattletrap.Type.Attribute.Boost where++import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.U8 as U8+import qualified Rattletrap.Utility.Json as Json++data Boost = Boost+ { grantCount :: U8.U8,+ boostAmount :: U8.U8,+ unused1 :: U8.U8,+ unused2 :: U8.U8+ }+ deriving (Eq, Show)++instance Json.FromJSON Boost where+ parseJSON = Json.withObject "Boost" $ \object -> do+ grantCount <- Json.required object "grantCount"+ boostAmount <- Json.required object "boostAmount"+ unused1 <- Json.required object "unused1"+ unused2 <- Json.required object "unused2"+ pure Boost {grantCount, boostAmount, unused1, unused2}++instance Json.ToJSON Boost where+ toJSON x =+ Json.object+ [ Json.pair "grantCount" $ grantCount x,+ Json.pair "boostAmount" $ boostAmount x,+ Json.pair "unused1" $ unused1 x,+ Json.pair "unused2" $ unused2 x+ ]++schema :: Schema.Schema+schema =+ Schema.named "boost" $+ Schema.object+ [ (Json.pair "grantCount" $ Schema.ref U8.schema, True),+ (Json.pair "boostAmount" $ Schema.ref U8.schema, True),+ (Json.pair "unused1" $ Schema.ref U8.schema, True),+ (Json.pair "unused2" $ Schema.ref U8.schema, True)+ ]++bitPut :: Boost -> BitPut.BitPut+bitPut appliedDamageAttribute =+ U8.bitPut (grantCount appliedDamageAttribute)+ <> U8.bitPut (boostAmount appliedDamageAttribute)+ <> U8.bitPut (unused1 appliedDamageAttribute)+ <> U8.bitPut (unused2 appliedDamageAttribute)++bitGet :: BitGet.BitGet Boost+bitGet = BitGet.label "Boost" $ do+ grantCount <- BitGet.label "grantCount" U8.bitGet+ boostAmount <- BitGet.label "boostAmount" U8.bitGet+ unused1 <- BitGet.label "unused1" U8.bitGet+ unused2 <- BitGet.label "unused2" U8.bitGet+ pure Boost {grantCount, boostAmount, unused1, unused2}
@@ -0,0 +1,29 @@+module Rattletrap.Type.Attribute.Byte where++import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.U8 as U8+import qualified Rattletrap.Utility.Json as Json++newtype Byte = Byte+ { value :: U8.U8+ }+ deriving (Eq, Show)++instance Json.FromJSON Byte where+ parseJSON = fmap Byte . Json.parseJSON++instance Json.ToJSON Byte where+ toJSON = Json.toJSON . value++schema :: Schema.Schema+schema = Schema.named "attribute-byte" $ Schema.ref U8.schema++bitPut :: Byte -> BitPut.BitPut+bitPut byteAttribute = U8.bitPut (value byteAttribute)++bitGet :: BitGet.BitGet Byte+bitGet = BitGet.label "Byte" $ do+ value <- BitGet.label "value" U8.bitGet+ pure Byte {value}
@@ -0,0 +1,99 @@+module Rattletrap.Type.Attribute.CamSettings where++import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.F32 as F32+import qualified Rattletrap.Type.Version as Version+import qualified Rattletrap.Utility.Json as Json+import qualified Rattletrap.Utility.Monad as Monad++data CamSettings = CamSettings+ { fov :: F32.F32,+ height :: F32.F32,+ angle :: F32.F32,+ distance :: F32.F32,+ stiffness :: F32.F32,+ swivelSpeed :: F32.F32,+ transitionSpeed :: Maybe F32.F32+ }+ deriving (Eq, Show)++instance Json.FromJSON CamSettings where+ parseJSON = Json.withObject "CamSettings" $ \object -> do+ fov <- Json.required object "fov"+ height <- Json.required object "height"+ angle <- Json.required object "angle"+ distance <- Json.required object "distance"+ stiffness <- Json.required object "stiffness"+ swivelSpeed <- Json.required object "swivel_speed"+ transitionSpeed <- Json.optional object "transition_speed"+ pure+ CamSettings+ { fov,+ height,+ angle,+ distance,+ stiffness,+ swivelSpeed,+ transitionSpeed+ }++instance Json.ToJSON CamSettings where+ toJSON x =+ Json.object+ [ Json.pair "fov" $ fov x,+ Json.pair "height" $ height x,+ Json.pair "angle" $ angle x,+ Json.pair "distance" $ distance x,+ Json.pair "stiffness" $ stiffness x,+ Json.pair "swivel_speed" $ swivelSpeed x,+ Json.pair "transition_speed" $ transitionSpeed x+ ]++schema :: Schema.Schema+schema =+ Schema.named "attribute-cam-settings" $+ Schema.object+ [ (Json.pair "fov" $ Schema.ref F32.schema, True),+ (Json.pair "height" $ Schema.ref F32.schema, True),+ (Json.pair "angle" $ Schema.ref F32.schema, True),+ (Json.pair "distance" $ Schema.ref F32.schema, True),+ (Json.pair "stiffness" $ Schema.ref F32.schema, True),+ (Json.pair "swivel_speed" $ Schema.ref F32.schema, True),+ ( Json.pair "transition_speed" . Schema.json $ Schema.maybe F32.schema,+ False+ )+ ]++bitPut :: CamSettings -> BitPut.BitPut+bitPut camSettingsAttribute =+ F32.bitPut (fov camSettingsAttribute)+ <> F32.bitPut (height camSettingsAttribute)+ <> F32.bitPut (angle camSettingsAttribute)+ <> F32.bitPut (distance camSettingsAttribute)+ <> F32.bitPut (stiffness camSettingsAttribute)+ <> F32.bitPut (swivelSpeed camSettingsAttribute)+ <> foldMap F32.bitPut (transitionSpeed camSettingsAttribute)++bitGet :: Version.Version -> BitGet.BitGet CamSettings+bitGet version = BitGet.label "CamSettings" $ do+ fov <- BitGet.label "fov" F32.bitGet+ height <- BitGet.label "height" F32.bitGet+ angle <- BitGet.label "angle" F32.bitGet+ distance <- BitGet.label "distance" F32.bitGet+ stiffness <- BitGet.label "stiffness" F32.bitGet+ swivelSpeed <- BitGet.label "swivelSpeed" F32.bitGet+ transitionSpeed <-+ BitGet.label "transitionSpeed" $+ Monad.whenMaybe (Version.atLeast 868 20 0 version) F32.bitGet+ pure+ CamSettings+ { fov,+ height,+ angle,+ distance,+ stiffness,+ swivelSpeed,+ transitionSpeed+ }
@@ -0,0 +1,57 @@+module Rattletrap.Type.Attribute.ClubColors where++import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.U8 as U8+import qualified Rattletrap.Utility.Json as Json++data ClubColors = ClubColors+ { blueFlag :: Bool,+ blueColor :: U8.U8,+ orangeFlag :: Bool,+ orangeColor :: U8.U8+ }+ deriving (Eq, Show)++instance Json.FromJSON ClubColors where+ parseJSON = Json.withObject "ClubColors" $ \object -> do+ blueFlag <- Json.required object "blue_flag"+ blueColor <- Json.required object "blue_color"+ orangeFlag <- Json.required object "orange_flag"+ orangeColor <- Json.required object "orange_color"+ pure ClubColors {blueFlag, blueColor, orangeFlag, orangeColor}++instance Json.ToJSON ClubColors where+ toJSON x =+ Json.object+ [ Json.pair "blue_flag" $ blueFlag x,+ Json.pair "blue_color" $ blueColor x,+ Json.pair "orange_flag" $ orangeFlag x,+ Json.pair "orange_color" $ orangeColor x+ ]++schema :: Schema.Schema+schema =+ Schema.named "attribute-club-colors" $+ Schema.object+ [ (Json.pair "blue_flag" $ Schema.ref Schema.boolean, True),+ (Json.pair "blue_color" $ Schema.ref U8.schema, True),+ (Json.pair "orange_flag" $ Schema.ref Schema.boolean, True),+ (Json.pair "orange_color" $ Schema.ref U8.schema, True)+ ]++bitPut :: ClubColors -> BitPut.BitPut+bitPut clubColorsAttribute =+ BitPut.bool (blueFlag clubColorsAttribute)+ <> U8.bitPut (blueColor clubColorsAttribute)+ <> BitPut.bool (orangeFlag clubColorsAttribute)+ <> U8.bitPut (orangeColor clubColorsAttribute)++bitGet :: BitGet.BitGet ClubColors+bitGet = BitGet.label "ClubColors" $ do+ blueFlag <- BitGet.label "blueFlag" BitGet.bool+ blueColor <- BitGet.label "blueColor" U8.bitGet+ orangeFlag <- BitGet.label "orangeFlag" BitGet.bool+ orangeColor <- BitGet.label "orangeColor" U8.bitGet+ pure ClubColors {blueFlag, blueColor, orangeFlag, orangeColor}
@@ -0,0 +1,58 @@+module Rattletrap.Type.Attribute.CustomDemolish where++import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.Attribute.Demolish as Demolish+import qualified Rattletrap.Type.I32 as I32+import qualified Rattletrap.Type.Version as Version+import qualified Rattletrap.Utility.Json as Json++data CustomDemolish = CustomDemolish+ { flag :: Bool,+ id :: I32.I32,+ demolish :: Demolish.Demolish+ }+ deriving (Eq, Show)++instance Json.FromJSON CustomDemolish where+ parseJSON = Json.withObject "CustomDemolish" $ \object -> do+ flag <- Json.required object "flag"+ id_ <- Json.required object "id"+ demolish <- Json.required object "demolish"+ pure CustomDemolish {flag, Rattletrap.Type.Attribute.CustomDemolish.id = id_, demolish}++instance Json.ToJSON CustomDemolish where+ toJSON x =+ Json.object+ [ Json.pair "flag" $ flag x,+ Json.pair "id" $ Rattletrap.Type.Attribute.CustomDemolish.id x,+ Json.pair "demolish" $ demolish x+ ]++schema :: Schema.Schema+schema =+ Schema.named "attribute-custom-demolish" $+ Schema.object+ [ (Json.pair "flag" $ Schema.ref Schema.boolean, True),+ (Json.pair "id" $ Schema.ref I32.schema, True),+ (Json.pair "demolish" $ Schema.ref Demolish.schema, True)+ ]++bitPut :: CustomDemolish -> BitPut.BitPut+bitPut x =+ BitPut.bool (flag x)+ <> I32.bitPut (Rattletrap.Type.Attribute.CustomDemolish.id x)+ <> Demolish.bitPut (demolish x)++bitGet :: Version.Version -> BitGet.BitGet CustomDemolish+bitGet version = BitGet.label "CustomDemolish" $ do+ flag <- BitGet.label "flag" BitGet.bool+ id_ <- BitGet.label "id" I32.bitGet+ demolish <- BitGet.label "demolish" $ Demolish.bitGet version+ pure+ CustomDemolish+ { flag,+ Rattletrap.Type.Attribute.CustomDemolish.id = id_,+ demolish+ }
@@ -0,0 +1,71 @@+module Rattletrap.Type.Attribute.CustomDemolishExtended where++import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.Attribute.CustomDemolish as CustomDemolish+import qualified Rattletrap.Type.ObjectTarget as ObjectTarget+import qualified Rattletrap.Type.Version as Version+import qualified Rattletrap.Utility.Json as Json++data CustomDemolishExtended = CustomDemolishExtended+ { attackerPri :: ObjectTarget.ObjectTarget,+ selfDemoFx :: ObjectTarget.ObjectTarget,+ selfDemolish :: Bool,+ customDemolish :: CustomDemolish.CustomDemolish+ }+ deriving (Eq, Show)++instance Json.FromJSON CustomDemolishExtended where+ parseJSON = Json.withObject "CustomDemolishExtended" $ \object -> do+ attackerPri <- Json.required object "attacker_pri"+ selfDemoFx <- Json.required object "self_demo_fx"+ selfDemolish <- Json.required object "self_demolish"+ customDemolish <- Json.required object "custom_demolish"+ pure+ CustomDemolishExtended+ { attackerPri,+ selfDemoFx,+ selfDemolish,+ customDemolish+ }++instance Json.ToJSON CustomDemolishExtended where+ toJSON x =+ Json.object+ [ Json.pair "attacker_pri" $ attackerPri x,+ Json.pair "self_demo_fx" $ selfDemoFx x,+ Json.pair "self_demolish" $ selfDemolish x,+ Json.pair "custom_demolish" $ customDemolish x+ ]++schema :: Schema.Schema+schema =+ Schema.named "attribute-custom-demolish-extended" $+ Schema.object+ [ (Json.pair "attacker_pri" $ Schema.ref ObjectTarget.schema, True),+ (Json.pair "self_demo_fx" $ Schema.ref ObjectTarget.schema, True),+ (Json.pair "self_demolish" $ Schema.ref Schema.boolean, True),+ (Json.pair "custom_demolish" $ Schema.ref CustomDemolish.schema, True)+ ]++bitPut :: CustomDemolishExtended -> BitPut.BitPut+bitPut demolishAttribute =+ ObjectTarget.bitPut (attackerPri demolishAttribute)+ <> ObjectTarget.bitPut (selfDemoFx demolishAttribute)+ <> BitPut.bool (selfDemolish demolishAttribute)+ <> CustomDemolish.bitPut (customDemolish demolishAttribute)++bitGet :: Version.Version -> BitGet.BitGet CustomDemolishExtended+bitGet version = BitGet.label "CustomDemolishExtended" $ do+ attackerPri <- BitGet.label "attackerPri" ObjectTarget.bitGet+ selfDemoFx <- BitGet.label "selfDemoFx" ObjectTarget.bitGet+ selfDemolish <- BitGet.label "selfDemolish" BitGet.bool+ customDemolish <- BitGet.label "customDemolish" $ CustomDemolish.bitGet version+ pure+ CustomDemolishExtended+ { attackerPri,+ selfDemoFx,+ selfDemolish,+ customDemolish+ }
@@ -0,0 +1,88 @@+module Rattletrap.Type.Attribute.DamageState where++import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.I32 as I32+import qualified Rattletrap.Type.U8 as U8+import qualified Rattletrap.Type.Vector as Vector+import qualified Rattletrap.Type.Version as Version+import qualified Rattletrap.Utility.Json as Json++data DamageState = DamageState+ { unknown1 :: U8.U8,+ unknown2 :: Bool,+ unknown3 :: I32.I32,+ unknown4 :: Vector.Vector,+ unknown5 :: Bool,+ unknown6 :: Bool+ }+ deriving (Eq, Show)++instance Json.FromJSON DamageState where+ parseJSON = Json.withObject "DamageState" $ \object -> do+ unknown1 <- Json.required object "unknown1"+ unknown2 <- Json.required object "unknown2"+ unknown3 <- Json.required object "unknown3"+ unknown4 <- Json.required object "unknown4"+ unknown5 <- Json.required object "unknown5"+ unknown6 <- Json.required object "unknown6"+ pure+ DamageState+ { unknown1,+ unknown2,+ unknown3,+ unknown4,+ unknown5,+ unknown6+ }++instance Json.ToJSON DamageState where+ toJSON x =+ Json.object+ [ Json.pair "unknown1" $ unknown1 x,+ Json.pair "unknown2" $ unknown2 x,+ Json.pair "unknown3" $ unknown3 x,+ Json.pair "unknown4" $ unknown4 x,+ Json.pair "unknown5" $ unknown5 x,+ Json.pair "unknown6" $ unknown6 x+ ]++schema :: Schema.Schema+schema =+ Schema.named "attribute-damage-state" $+ Schema.object+ [ (Json.pair "unknown1" $ Schema.ref U8.schema, True),+ (Json.pair "unknown2" $ Schema.ref Schema.boolean, True),+ (Json.pair "unknown3" $ Schema.ref I32.schema, True),+ (Json.pair "unknown4" $ Schema.ref Vector.schema, True),+ (Json.pair "unknown5" $ Schema.ref Schema.boolean, True),+ (Json.pair "unknown6" $ Schema.ref Schema.boolean, True)+ ]++bitPut :: DamageState -> BitPut.BitPut+bitPut damageStateAttribute =+ U8.bitPut (unknown1 damageStateAttribute)+ <> BitPut.bool (unknown2 damageStateAttribute)+ <> I32.bitPut (unknown3 damageStateAttribute)+ <> Vector.bitPut (unknown4 damageStateAttribute)+ <> BitPut.bool (unknown5 damageStateAttribute)+ <> BitPut.bool (unknown6 damageStateAttribute)++bitGet :: Version.Version -> BitGet.BitGet DamageState+bitGet version = BitGet.label "DamageState" $ do+ unknown1 <- BitGet.label "unknown1" U8.bitGet+ unknown2 <- BitGet.label "unknown2" BitGet.bool+ unknown3 <- BitGet.label "unknown3" I32.bitGet+ unknown4 <- BitGet.label "unknown4" $ Vector.bitGet version+ unknown5 <- BitGet.label "unknown5" BitGet.bool+ unknown6 <- BitGet.label "unknown6" BitGet.bool+ pure+ DamageState+ { unknown1,+ unknown2,+ unknown3,+ unknown4,+ unknown5,+ unknown6+ }
@@ -0,0 +1,87 @@+module Rattletrap.Type.Attribute.Demolish where++import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.U32 as U32+import qualified Rattletrap.Type.Vector as Vector+import qualified Rattletrap.Type.Version as Version+import qualified Rattletrap.Utility.Json as Json++data Demolish = Demolish+ { attackerFlag :: Bool,+ attackerActorId :: U32.U32,+ victimFlag :: Bool,+ victimActorId :: U32.U32,+ attackerVelocity :: Vector.Vector,+ victimVelocity :: Vector.Vector+ }+ deriving (Eq, Show)++instance Json.FromJSON Demolish where+ parseJSON = Json.withObject "Demolish" $ \object -> do+ attackerFlag <- Json.required object "attacker_flag"+ attackerActorId <- Json.required object "attacker_actor_id"+ victimFlag <- Json.required object "victim_flag"+ victimActorId <- Json.required object "victim_actor_id"+ attackerVelocity <- Json.required object "attacker_velocity"+ victimVelocity <- Json.required object "victim_velocity"+ pure+ Demolish+ { attackerFlag,+ attackerActorId,+ victimFlag,+ victimActorId,+ attackerVelocity,+ victimVelocity+ }++instance Json.ToJSON Demolish where+ toJSON x =+ Json.object+ [ Json.pair "attacker_flag" $ attackerFlag x,+ Json.pair "attacker_actor_id" $ attackerActorId x,+ Json.pair "victim_flag" $ victimFlag x,+ Json.pair "victim_actor_id" $ victimActorId x,+ Json.pair "attacker_velocity" $ attackerVelocity x,+ Json.pair "victim_velocity" $ victimVelocity x+ ]++schema :: Schema.Schema+schema =+ Schema.named "attribute-demolish" $+ Schema.object+ [ (Json.pair "attacker_flag" $ Schema.ref Schema.boolean, True),+ (Json.pair "attacker_actor_id" $ Schema.ref U32.schema, True),+ (Json.pair "victim_flag" $ Schema.ref Schema.boolean, True),+ (Json.pair "victim_actor_id" $ Schema.ref U32.schema, True),+ (Json.pair "attacker_velocity" $ Schema.ref Vector.schema, True),+ (Json.pair "victim_velocity" $ Schema.ref Vector.schema, True)+ ]++bitPut :: Demolish -> BitPut.BitPut+bitPut demolishAttribute =+ BitPut.bool (attackerFlag demolishAttribute)+ <> U32.bitPut (attackerActorId demolishAttribute)+ <> BitPut.bool (victimFlag demolishAttribute)+ <> U32.bitPut (victimActorId demolishAttribute)+ <> Vector.bitPut (attackerVelocity demolishAttribute)+ <> Vector.bitPut (victimVelocity demolishAttribute)++bitGet :: Version.Version -> BitGet.BitGet Demolish+bitGet version = BitGet.label "Demolish" $ do+ attackerFlag <- BitGet.label "attackerFlag" BitGet.bool+ attackerActorId <- BitGet.label "attackerActorId" U32.bitGet+ victimFlag <- BitGet.label "victimFlag" BitGet.bool+ victimActorId <- BitGet.label "victimActorId" U32.bitGet+ attackerVelocity <- BitGet.label "attackerVelocity" $ Vector.bitGet version+ victimVelocity <- BitGet.label "victimVelocity" $ Vector.bitGet version+ pure+ Demolish+ { attackerFlag,+ attackerActorId,+ victimFlag,+ victimActorId,+ attackerVelocity,+ victimVelocity+ }
@@ -0,0 +1,30 @@+module Rattletrap.Type.Attribute.Enum where++import qualified Data.Word as Word+import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Utility.Json as Json+import Prelude hiding (Enum)++newtype Enum = Enum+ { value :: Word.Word16+ }+ deriving (Eq, Show)++instance Json.FromJSON Enum where+ parseJSON = fmap Enum . Json.parseJSON++instance Json.ToJSON Enum where+ toJSON = Json.toJSON . value++schema :: Schema.Schema+schema = Schema.named "attribute-enum" $ Schema.ref Schema.integer++bitPut :: Enum -> BitPut.BitPut+bitPut enumAttribute = BitPut.bits 11 (value enumAttribute)++bitGet :: BitGet.BitGet Enum+bitGet = BitGet.label "Enum" $ do+ value <- BitGet.label "value" $ BitGet.bits 11+ pure Enum {value}
@@ -0,0 +1,53 @@+module Rattletrap.Type.Attribute.Explosion where++import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.I32 as I32+import qualified Rattletrap.Type.Vector as Vector+import qualified Rattletrap.Type.Version as Version+import qualified Rattletrap.Utility.Json as Json++data Explosion = Explosion+ { flag :: Bool,+ actorId :: I32.I32,+ location :: Vector.Vector+ }+ deriving (Eq, Show)++instance Json.FromJSON Explosion where+ parseJSON = Json.withObject "Explosion" $ \object -> do+ flag <- Json.required object "flag"+ actorId <- Json.required object "actor_id"+ location <- Json.required object "location"+ pure Explosion {flag, actorId, location}++instance Json.ToJSON Explosion where+ toJSON x =+ Json.object+ [ Json.pair "flag" $ flag x,+ Json.pair "actor_id" $ actorId x,+ Json.pair "location" $ location x+ ]++schema :: Schema.Schema+schema =+ Schema.named "attribute-explosion" $+ Schema.object+ [ (Json.pair "flag" $ Schema.ref Schema.boolean, True),+ (Json.pair "actor_id" $ Schema.ref I32.schema, True),+ (Json.pair "location" $ Schema.ref Vector.schema, True)+ ]++bitPut :: Explosion -> BitPut.BitPut+bitPut explosionAttribute =+ BitPut.bool (flag explosionAttribute)+ <> I32.bitPut (actorId explosionAttribute)+ <> Vector.bitPut (location explosionAttribute)++bitGet :: Version.Version -> BitGet.BitGet Explosion+bitGet version = BitGet.label "Explosion" $ do+ flag <- BitGet.label "flag" BitGet.bool+ actorId <- BitGet.label "actorId" I32.bitGet+ location <- BitGet.label "location" $ Vector.bitGet version+ pure Explosion {flag, actorId, location}
@@ -0,0 +1,43 @@+module Rattletrap.Type.Attribute.ExtendedExplosion where++import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.Attribute.Explosion as Explosion+import qualified Rattletrap.Type.Attribute.FlaggedInt as FlaggedInt+import qualified Rattletrap.Type.Version as Version+import qualified Rattletrap.Utility.Json as Json++data ExtendedExplosion = ExtendedExplosion+ { explosion :: Explosion.Explosion,+ unknown :: FlaggedInt.FlaggedInt+ }+ deriving (Eq, Show)++instance Json.FromJSON ExtendedExplosion where+ parseJSON = Json.withObject "ExtendedExplosion" $ \object -> do+ explosion <- Json.required object "explosion"+ unknown <- Json.required object "unknown"+ pure ExtendedExplosion {explosion, unknown}++instance Json.ToJSON ExtendedExplosion where+ toJSON x =+ Json.object+ [Json.pair "explosion" $ explosion x, Json.pair "unknown" $ unknown x]++schema :: Schema.Schema+schema =+ Schema.named "attribute-extended-explosion" $+ Schema.object+ [ (Json.pair "explosion" $ Schema.ref Explosion.schema, True),+ (Json.pair "unknown" $ Schema.ref FlaggedInt.schema, True)+ ]++bitPut :: ExtendedExplosion -> BitPut.BitPut+bitPut x = Explosion.bitPut (explosion x) <> FlaggedInt.bitPut (unknown x)++bitGet :: Version.Version -> BitGet.BitGet ExtendedExplosion+bitGet version = BitGet.label "ExtendedExplosion" $ do+ explosion <- BitGet.label "explosion" $ Explosion.bitGet version+ unknown <- BitGet.label "unknown" FlaggedInt.bitGet+ pure ExtendedExplosion {explosion, unknown}
@@ -0,0 +1,42 @@+module Rattletrap.Type.Attribute.FlaggedByte where++import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.U8 as U8+import qualified Rattletrap.Utility.Json as Json++data FlaggedByte = FlaggedByte+ { flag :: Bool,+ byte :: U8.U8+ }+ deriving (Eq, Show)++instance Json.FromJSON FlaggedByte where+ parseJSON = Json.withObject "FlaggedByte" $ \object -> do+ flag <- Json.required object "flag"+ byte <- Json.required object "byte"+ pure FlaggedByte {flag, byte}++instance Json.ToJSON FlaggedByte where+ toJSON x =+ Json.object [Json.pair "flag" $ flag x, Json.pair "byte" $ byte x]++schema :: Schema.Schema+schema =+ Schema.named "attribute-flagged-byte" $+ Schema.object+ [ (Json.pair "flag" $ Schema.ref Schema.boolean, True),+ (Json.pair "byte" $ Schema.ref U8.schema, True)+ ]++bitPut :: FlaggedByte -> BitPut.BitPut+bitPut flaggedByteAttribute =+ BitPut.bool (flag flaggedByteAttribute)+ <> U8.bitPut (byte flaggedByteAttribute)++bitGet :: BitGet.BitGet FlaggedByte+bitGet = BitGet.label "FlaggedByte" $ do+ flag <- BitGet.label "flag" BitGet.bool+ byte <- BitGet.label "byte" U8.bitGet+ pure FlaggedByte {flag, byte}
@@ -0,0 +1,41 @@+module Rattletrap.Type.Attribute.FlaggedInt where++import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.I32 as I32+import qualified Rattletrap.Utility.Json as Json++data FlaggedInt = FlaggedInt+ { flag :: Bool,+ int :: I32.I32+ }+ deriving (Eq, Show)++instance Json.FromJSON FlaggedInt where+ parseJSON = Json.withObject "FlaggedInt" $ \object -> do+ flag <- Json.required object "flag"+ int <- Json.required object "int"+ pure FlaggedInt {flag, int}++instance Json.ToJSON FlaggedInt where+ toJSON x = Json.object [Json.pair "flag" $ flag x, Json.pair "int" $ int x]++schema :: Schema.Schema+schema =+ Schema.named "attribute-flagged-int" $+ Schema.object+ [ (Json.pair "flag" $ Schema.ref Schema.boolean, True),+ (Json.pair "int" $ Schema.ref I32.schema, True)+ ]++bitPut :: FlaggedInt -> BitPut.BitPut+bitPut flaggedIntAttribute =+ BitPut.bool (flag flaggedIntAttribute)+ <> I32.bitPut (int flaggedIntAttribute)++bitGet :: BitGet.BitGet FlaggedInt+bitGet = BitGet.label "FlaggedInt" $ do+ flag <- BitGet.label "flag" BitGet.bool+ int <- BitGet.label "int" I32.bitGet+ pure FlaggedInt {flag, int}
@@ -0,0 +1,30 @@+module Rattletrap.Type.Attribute.Float where++import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.F32 as F32+import qualified Rattletrap.Utility.Json as Json+import Prelude hiding (Float)++newtype Float = Float+ { value :: F32.F32+ }+ deriving (Eq, Show)++instance Json.FromJSON Float where+ parseJSON = fmap Float . Json.parseJSON++instance Json.ToJSON Float where+ toJSON = Json.toJSON . value++schema :: Schema.Schema+schema = Schema.named "attribute-float" $ Schema.ref F32.schema++bitPut :: Float -> BitPut.BitPut+bitPut floatAttribute = F32.bitPut (value floatAttribute)++bitGet :: BitGet.BitGet Float+bitGet = BitGet.label "Float" $ do+ value <- BitGet.label "value" F32.bitGet+ pure Float {value}
@@ -0,0 +1,46 @@+module Rattletrap.Type.Attribute.GameMode where++import qualified Data.Word as Word+import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.Version as Version+import qualified Rattletrap.Utility.Json as Json++data GameMode = GameMode+ { -- | This field is guaranteed to be small. In other words, it won't overflow.+ -- It's stored as a regular 'Int' rather than something more precise like an+ -- 'Int8' because it just gets passed to functions that expect 'Int's.+ -- There's no reason to do a bunch of conversions.+ numBits :: Int,+ word :: Word.Word8+ }+ deriving (Eq, Show)++instance Json.FromJSON GameMode where+ parseJSON = Json.withObject "GameMode" $ \object -> do+ numBits <- Json.required object "num_bits"+ word <- Json.required object "word"+ pure GameMode {numBits, word}++instance Json.ToJSON GameMode where+ toJSON x =+ Json.object [Json.pair "num_bits" $ numBits x, Json.pair "word" $ word x]++schema :: Schema.Schema+schema =+ Schema.named "attribute-game-mode" $+ Schema.object+ [ (Json.pair "num_bits" $ Schema.ref Schema.integer, True),+ (Json.pair "word" $ Schema.ref Schema.integer, True)+ ]++bitPut :: GameMode -> BitPut.BitPut+bitPut gameModeAttribute = do+ BitPut.bits (numBits gameModeAttribute) (word gameModeAttribute)++bitGet :: Version.Version -> BitGet.BitGet GameMode+bitGet version = BitGet.label "GameMode" $ do+ let numBits = if Version.atLeast 868 12 0 version then 8 else 2 :: Int+ word <- BitGet.label "word" $ BitGet.bits numBits+ pure GameMode {numBits, word}
@@ -0,0 +1,43 @@+module Rattletrap.Type.Attribute.GameServer where++import qualified Data.Foldable as Foldable+import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.Attribute.QWord as QWord+import qualified Rattletrap.Type.Str as Str+import qualified Rattletrap.Utility.Json as Json++data GameServer+ = Old QWord.QWord+ | New Str.Str+ deriving (Eq, Show)++instance Json.FromJSON GameServer where+ parseJSON = Json.withObject "GameServer" $ \x ->+ Foldable.asum+ [fmap Old $ Json.required x "old", fmap New $ Json.required x "new"]++instance Json.ToJSON GameServer where+ toJSON x = case x of+ Old y -> Json.object [Json.pair "old" $ Json.toJSON y]+ New y -> Json.object [Json.pair "new" $ Json.toJSON y]++schema :: Schema.Schema+schema =+ Schema.named "attribute-game-server" . Schema.oneOf $+ fmap+ (\(k, v) -> Schema.object [(Json.pair k v, True)])+ [("old", Schema.ref QWord.schema), ("new", Schema.ref Str.schema)]++bitPut :: GameServer -> BitPut.BitPut+bitPut x = case x of+ Old y -> QWord.bitPut y+ New y -> Str.bitPut y++bitGet :: Maybe Str.Str -> BitGet.BitGet GameServer+bitGet buildVersion =+ BitGet.label "GameServer" $+ if buildVersion >= Just (Str.fromString "221120.42953.406184")+ then BitGet.label "New" $ fmap New Str.bitGet+ else BitGet.label "Old" $ fmap Old QWord.bitGet
@@ -0,0 +1,30 @@+module Rattletrap.Type.Attribute.Int where++import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.I32 as I32+import qualified Rattletrap.Utility.Json as Json+import Prelude hiding (Int)++newtype Int = Int+ { value :: I32.I32+ }+ deriving (Eq, Show)++instance Json.FromJSON Int where+ parseJSON = fmap Int . Json.parseJSON++instance Json.ToJSON Int where+ toJSON = Json.toJSON . value++schema :: Schema.Schema+schema = Schema.named "attribute-int" $ Schema.ref I32.schema++bitPut :: Int -> BitPut.BitPut+bitPut intAttribute = I32.bitPut (value intAttribute)++bitGet :: BitGet.BitGet Int+bitGet = BitGet.label "Int" $ do+ value <- BitGet.label "value" I32.bitGet+ pure Int {value}
@@ -0,0 +1,29 @@+module Rattletrap.Type.Attribute.Int64 where++import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.I64 as I64+import qualified Rattletrap.Utility.Json as Json++newtype Int64 = Int64+ { value :: I64.I64+ }+ deriving (Eq, Show)++instance Json.FromJSON Int64 where+ parseJSON = fmap Int64 . Json.parseJSON++instance Json.ToJSON Int64 where+ toJSON = Json.toJSON . value++schema :: Schema.Schema+schema = Schema.named "attribute-int64" $ Schema.ref I64.schema++putInt64Attribute :: Int64 -> BitPut.BitPut+putInt64Attribute int64Attribute = I64.bitPut (value int64Attribute)++bitGet :: BitGet.BitGet Int64+bitGet = BitGet.label "Int64" $ do+ value <- BitGet.label "value" I64.bitGet+ pure Int64 {value}
@@ -0,0 +1,194 @@+module Rattletrap.Type.Attribute.Loadout where++import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.U32 as U32+import qualified Rattletrap.Type.U8 as U8+import qualified Rattletrap.Utility.Json as Json+import qualified Rattletrap.Utility.Monad as Monad++data Loadout = Loadout+ { version :: U8.U8,+ body :: U32.U32,+ decal :: U32.U32,+ wheels :: U32.U32,+ -- | Now known as "rocket boost".+ rocketTrail :: U32.U32,+ antenna :: U32.U32,+ topper :: U32.U32,+ unknown1 :: U32.U32,+ unknown2 :: Maybe U32.U32,+ engineAudio :: Maybe U32.U32,+ trail :: Maybe U32.U32,+ goalExplosion :: Maybe U32.U32,+ banner :: Maybe U32.U32,+ unknown3 :: Maybe U32.U32,+ unknown4 :: Maybe U32.U32,+ unknown5 :: Maybe U32.U32,+ unknown6 :: Maybe U32.U32+ }+ deriving (Eq, Show)++instance Json.FromJSON Loadout where+ parseJSON = Json.withObject "Loadout" $ \object -> do+ version <- Json.required object "version"+ body <- Json.required object "body"+ decal <- Json.required object "decal"+ wheels <- Json.required object "wheels"+ rocketTrail <- Json.required object "rocket_trail"+ antenna <- Json.required object "antenna"+ topper <- Json.required object "topper"+ unknown1 <- Json.required object "unknown1"+ unknown2 <- Json.optional object "unknown2"+ engineAudio <- Json.optional object "engine_audio"+ trail <- Json.optional object "trail"+ goalExplosion <- Json.optional object "goal_explosion"+ banner <- Json.optional object "banner"+ unknown3 <- Json.optional object "unknown3"+ unknown4 <- Json.optional object "unknown4"+ unknown5 <- Json.optional object "unknown5"+ unknown6 <- Json.optional object "unknown6"+ pure+ Loadout+ { version,+ body,+ decal,+ wheels,+ rocketTrail,+ antenna,+ topper,+ unknown1,+ unknown2,+ engineAudio,+ trail,+ goalExplosion,+ banner,+ unknown3,+ unknown4,+ unknown5,+ unknown6+ }++instance Json.ToJSON Loadout where+ toJSON x =+ Json.object+ [ Json.pair "version" $ version x,+ Json.pair "body" $ body x,+ Json.pair "decal" $ decal x,+ Json.pair "wheels" $ wheels x,+ Json.pair "rocket_trail" $ rocketTrail x,+ Json.pair "antenna" $ antenna x,+ Json.pair "topper" $ topper x,+ Json.pair "unknown1" $ unknown1 x,+ Json.pair "unknown2" $ unknown2 x,+ Json.pair "engine_audio" $ engineAudio x,+ Json.pair "trail" $ trail x,+ Json.pair "goal_explosion" $ goalExplosion x,+ Json.pair "banner" $ banner x,+ Json.pair "unknown3" $ unknown3 x,+ Json.pair "unknown4" $ unknown4 x,+ Json.pair "unknown5" $ unknown5 x,+ Json.pair "unknown6" $ unknown6 x+ ]++schema :: Schema.Schema+schema =+ Schema.named "attribute-loadout" $+ Schema.object+ [ (Json.pair "version" $ Schema.ref U8.schema, True),+ (Json.pair "body" $ Schema.ref U32.schema, True),+ (Json.pair "decal" $ Schema.ref U32.schema, True),+ (Json.pair "wheels" $ Schema.ref U32.schema, True),+ (Json.pair "rocket_trail" $ Schema.ref U32.schema, True),+ (Json.pair "antenna" $ Schema.ref U32.schema, True),+ (Json.pair "topper" $ Schema.ref U32.schema, True),+ (Json.pair "unknown1" $ Schema.ref U32.schema, True),+ (Json.pair "unknown2" . Schema.json $ Schema.maybe U32.schema, False),+ (Json.pair "engine_audio" . Schema.json $ Schema.maybe U32.schema, False),+ (Json.pair "trail" . Schema.json $ Schema.maybe U32.schema, False),+ (Json.pair "goal_explosion" . Schema.json $ Schema.maybe U32.schema, False),+ (Json.pair "banner" . Schema.json $ Schema.maybe U32.schema, False),+ (Json.pair "unknown3" . Schema.json $ Schema.maybe U32.schema, False),+ (Json.pair "unknown4" . Schema.json $ Schema.maybe U32.schema, False),+ (Json.pair "unknown5" . Schema.json $ Schema.maybe U32.schema, False),+ (Json.pair "unknown6" . Schema.json $ Schema.maybe U32.schema, False)+ ]++bitPut :: Loadout -> BitPut.BitPut+bitPut loadoutAttribute =+ U8.bitPut (version loadoutAttribute)+ <> U32.bitPut (body loadoutAttribute)+ <> U32.bitPut (decal loadoutAttribute)+ <> U32.bitPut (wheels loadoutAttribute)+ <> U32.bitPut (rocketTrail loadoutAttribute)+ <> U32.bitPut (antenna loadoutAttribute)+ <> U32.bitPut (topper loadoutAttribute)+ <> U32.bitPut (unknown1 loadoutAttribute)+ <> foldMap U32.bitPut (unknown2 loadoutAttribute)+ <> foldMap U32.bitPut (engineAudio loadoutAttribute)+ <> foldMap U32.bitPut (trail loadoutAttribute)+ <> foldMap U32.bitPut (goalExplosion loadoutAttribute)+ <> foldMap U32.bitPut (banner loadoutAttribute)+ <> foldMap U32.bitPut (unknown3 loadoutAttribute)+ <> foldMap U32.bitPut (unknown4 loadoutAttribute)+ <> foldMap U32.bitPut (unknown5 loadoutAttribute)+ <> foldMap U32.bitPut (unknown6 loadoutAttribute)++bitGet :: BitGet.BitGet Loadout+bitGet = BitGet.label "Loadout" $ do+ version <- BitGet.label "version" U8.bitGet+ body <- BitGet.label "body" U32.bitGet+ decal <- BitGet.label "decal" U32.bitGet+ wheels <- BitGet.label "wheels" U32.bitGet+ rocketTrail <- BitGet.label "rocketTrail" U32.bitGet+ antenna <- BitGet.label "antenna" U32.bitGet+ topper <- BitGet.label "topper" U32.bitGet+ unknown1 <- BitGet.label "unknown1" U32.bitGet+ unknown2 <-+ BitGet.label "unknown2" $+ Monad.whenMaybe (U8.toWord8 version >= 11) U32.bitGet+ engineAudio <-+ BitGet.label "engineAudio" $+ Monad.whenMaybe (U8.toWord8 version >= 16) U32.bitGet+ trail <-+ BitGet.label "trail" $+ Monad.whenMaybe (U8.toWord8 version >= 16) U32.bitGet+ goalExplosion <-+ BitGet.label "goalExplosion" $+ Monad.whenMaybe (U8.toWord8 version >= 16) U32.bitGet+ banner <-+ BitGet.label "banner" $+ Monad.whenMaybe (U8.toWord8 version >= 17) U32.bitGet+ unknown3 <-+ BitGet.label "unknown3" $+ Monad.whenMaybe (U8.toWord8 version >= 19) U32.bitGet+ unknown4 <-+ BitGet.label "unknown4" $+ Monad.whenMaybe (U8.toWord8 version >= 22) U32.bitGet+ unknown5 <-+ BitGet.label "unknown5" $+ Monad.whenMaybe (U8.toWord8 version >= 22) U32.bitGet+ unknown6 <-+ BitGet.label "unknown6" $+ Monad.whenMaybe (U8.toWord8 version >= 22) U32.bitGet+ pure+ Loadout+ { version,+ body,+ decal,+ wheels,+ rocketTrail,+ antenna,+ topper,+ unknown1,+ unknown2,+ engineAudio,+ trail,+ goalExplosion,+ banner,+ unknown3,+ unknown4,+ unknown5,+ unknown6+ }
@@ -0,0 +1,47 @@+module Rattletrap.Type.Attribute.LoadoutOnline where++import qualified Data.Map as Map+import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.Attribute.Product as Product+import qualified Rattletrap.Type.List as RList+import qualified Rattletrap.Type.Str as Str+import qualified Rattletrap.Type.U32 as U32+import qualified Rattletrap.Type.U8 as U8+import qualified Rattletrap.Type.Version as Version+import qualified Rattletrap.Utility.Json as Json++newtype LoadoutOnline = LoadoutOnline+ { value :: RList.List (RList.List Product.Product)+ }+ deriving (Eq, Show)++instance Json.FromJSON LoadoutOnline where+ parseJSON = fmap LoadoutOnline . Json.parseJSON++instance Json.ToJSON LoadoutOnline where+ toJSON = Json.toJSON . value++schema :: Schema.Schema+schema =+ Schema.named "attribute-loadout-online"+ . Schema.json+ . RList.schema+ $ RList.schema Product.schema++bitPut :: LoadoutOnline -> BitPut.BitPut+bitPut loadoutAttribute =+ let attributes = RList.toList $ value loadoutAttribute+ in (U8.bitPut . U8.fromWord8 . fromIntegral $ length attributes)+ <> foldMap Product.putProductAttributes attributes++bitGet ::+ Version.Version -> Map.Map U32.U32 Str.Str -> BitGet.BitGet LoadoutOnline+bitGet version objectMap = BitGet.label "LoadoutOnline" $ do+ size <- BitGet.label "size" U8.bitGet+ value <-+ BitGet.label "value"+ . RList.replicateM (fromIntegral $ U8.toWord8 size)+ $ Product.decodeProductAttributesBits version objectMap+ pure LoadoutOnline {value}
@@ -0,0 +1,42 @@+module Rattletrap.Type.Attribute.Loadouts where++import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.Attribute.Loadout as Loadout+import qualified Rattletrap.Utility.Json as Json++data Loadouts = Loadouts+ { blue :: Loadout.Loadout,+ orange :: Loadout.Loadout+ }+ deriving (Eq, Show)++instance Json.FromJSON Loadouts where+ parseJSON = Json.withObject "Loadouts" $ \object -> do+ blue <- Json.required object "blue"+ orange <- Json.required object "orange"+ pure Loadouts {blue, orange}++instance Json.ToJSON Loadouts where+ toJSON x =+ Json.object [Json.pair "blue" $ blue x, Json.pair "orange" $ orange x]++schema :: Schema.Schema+schema =+ Schema.named "attribute-loadouts" $+ Schema.object+ [ (Json.pair "blue" $ Schema.ref Loadout.schema, True),+ (Json.pair "orange" $ Schema.ref Loadout.schema, True)+ ]++bitPut :: Loadouts -> BitPut.BitPut+bitPut loadoutsAttribute =+ Loadout.bitPut (blue loadoutsAttribute)+ <> Loadout.bitPut (orange loadoutsAttribute)++bitGet :: BitGet.BitGet Loadouts+bitGet = BitGet.label "Loadouts" $ do+ blue <- BitGet.label "blue" Loadout.bitGet+ orange <- BitGet.label "orange" Loadout.bitGet+ pure Loadouts {blue, orange}
@@ -0,0 +1,62 @@+module Rattletrap.Type.Attribute.LoadoutsOnline where++import qualified Data.Map as Map+import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.Attribute.LoadoutOnline as LoadoutOnline+import qualified Rattletrap.Type.Str as Str+import qualified Rattletrap.Type.U32 as U32+import qualified Rattletrap.Type.Version as Version+import qualified Rattletrap.Utility.Json as Json++data LoadoutsOnline = LoadoutsOnline+ { blue :: LoadoutOnline.LoadoutOnline,+ orange :: LoadoutOnline.LoadoutOnline,+ unknown1 :: Bool,+ unknown2 :: Bool+ }+ deriving (Eq, Show)++instance Json.FromJSON LoadoutsOnline where+ parseJSON = Json.withObject "LoadoutsOnline" $ \object -> do+ blue <- Json.required object "blue"+ orange <- Json.required object "orange"+ unknown1 <- Json.required object "unknown1"+ unknown2 <- Json.required object "unknown2"+ pure LoadoutsOnline {blue, orange, unknown1, unknown2}++instance Json.ToJSON LoadoutsOnline where+ toJSON x =+ Json.object+ [ Json.pair "blue" $ blue x,+ Json.pair "orange" $ orange x,+ Json.pair "unknown1" $ unknown1 x,+ Json.pair "unknown2" $ unknown2 x+ ]++schema :: Schema.Schema+schema =+ Schema.named "attribute-loadouts-online" $+ Schema.object+ [ (Json.pair "blue" $ Schema.ref LoadoutOnline.schema, True),+ (Json.pair "orange" $ Schema.ref LoadoutOnline.schema, True),+ (Json.pair "unknown1" $ Schema.ref Schema.boolean, True),+ (Json.pair "unknown2" $ Schema.ref Schema.boolean, True)+ ]++bitPut :: LoadoutsOnline -> BitPut.BitPut+bitPut loadoutsOnlineAttribute =+ LoadoutOnline.bitPut (blue loadoutsOnlineAttribute)+ <> LoadoutOnline.bitPut (orange loadoutsOnlineAttribute)+ <> BitPut.bool (unknown1 loadoutsOnlineAttribute)+ <> BitPut.bool (unknown2 loadoutsOnlineAttribute)++bitGet ::+ Version.Version -> Map.Map U32.U32 Str.Str -> BitGet.BitGet LoadoutsOnline+bitGet version objectMap = BitGet.label "LoadoutsOnline" $ do+ blue <- BitGet.label "blue" $ LoadoutOnline.bitGet version objectMap+ orange <- BitGet.label "orange" $ LoadoutOnline.bitGet version objectMap+ unknown1 <- BitGet.label "unknown1" BitGet.bool+ unknown2 <- BitGet.label "unknown2" BitGet.bool+ pure LoadoutsOnline {blue, orange, unknown1, unknown2}
@@ -0,0 +1,30 @@+module Rattletrap.Type.Attribute.Location where++import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.Vector as Vector+import qualified Rattletrap.Type.Version as Version+import qualified Rattletrap.Utility.Json as Json++newtype Location = Location+ { value :: Vector.Vector+ }+ deriving (Eq, Show)++instance Json.FromJSON Location where+ parseJSON = fmap Location . Json.parseJSON++instance Json.ToJSON Location where+ toJSON = Json.toJSON . value++schema :: Schema.Schema+schema = Schema.named "attribute-location" $ Schema.ref Vector.schema++bitPut :: Location -> BitPut.BitPut+bitPut locationAttribute = Vector.bitPut (value locationAttribute)++bitGet :: Version.Version -> BitGet.BitGet Location+bitGet version = BitGet.label "Location" $ do+ value <- BitGet.label "value" $ Vector.bitGet version+ pure Location {value}
@@ -0,0 +1,52 @@+module Rattletrap.Type.Attribute.MusicStinger where++import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.U32 as U32+import qualified Rattletrap.Type.U8 as U8+import qualified Rattletrap.Utility.Json as Json++data MusicStinger = MusicStinger+ { flag :: Bool,+ cue :: U32.U32,+ trigger :: U8.U8+ }+ deriving (Eq, Show)++instance Json.FromJSON MusicStinger where+ parseJSON = Json.withObject "MusicStinger" $ \object -> do+ flag <- Json.required object "flag"+ cue <- Json.required object "cue"+ trigger <- Json.required object "trigger"+ pure MusicStinger {flag, cue, trigger}++instance Json.ToJSON MusicStinger where+ toJSON x =+ Json.object+ [ Json.pair "flag" $ flag x,+ Json.pair "cue" $ cue x,+ Json.pair "trigger" $ trigger x+ ]++schema :: Schema.Schema+schema =+ Schema.named "attribute-music-stinger" $+ Schema.object+ [ (Json.pair "flag" $ Schema.ref Schema.boolean, True),+ (Json.pair "cue" $ Schema.ref U32.schema, True),+ (Json.pair "trigger" $ Schema.ref U8.schema, True)+ ]++bitPut :: MusicStinger -> BitPut.BitPut+bitPut musicStingerAttribute =+ BitPut.bool (flag musicStingerAttribute)+ <> U32.bitPut (cue musicStingerAttribute)+ <> U8.bitPut (trigger musicStingerAttribute)++bitGet :: BitGet.BitGet MusicStinger+bitGet = BitGet.label "MusicStinger" $ do+ flag <- BitGet.label "flag" BitGet.bool+ cue <- BitGet.label "cue" U32.bitGet+ trigger <- BitGet.label "trigger" U8.bitGet+ pure MusicStinger {flag, cue, trigger}
@@ -0,0 +1,70 @@+module Rattletrap.Type.Attribute.PartyLeader where++import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.RemoteId as RemoteId+import qualified Rattletrap.Type.U8 as U8+import qualified Rattletrap.Type.Version as Version+import qualified Rattletrap.Utility.Json as Json++data PartyLeader = PartyLeader+ { systemId :: U8.U8,+ remoteId :: Maybe RemoteId.RemoteId,+ localId :: Maybe U8.U8+ }+ deriving (Eq, Show)++instance Json.FromJSON PartyLeader where+ parseJSON = Json.withObject "PartyLeader" $ \object -> do+ systemId <- Json.required object "system_id"+ maybeId <- Json.optional object "id"+ pure+ PartyLeader+ { systemId,+ remoteId = fmap fst maybeId,+ localId = fmap snd maybeId+ }++instance Json.ToJSON PartyLeader where+ toJSON x =+ Json.object+ [ Json.pair "system_id" $ systemId x,+ Json.pair "id" $ case (remoteId x, localId x) of+ (Just r, Just l) -> Just (r, l)+ _ -> Nothing+ ]++schema :: Schema.Schema+schema =+ Schema.named "attribute-party-leader" $+ Schema.object+ [ (Json.pair "system_id" $ Schema.ref U8.schema, True),+ ( Json.pair "id" $+ Schema.oneOf+ [ Schema.tuple [Schema.ref RemoteId.schema, Schema.ref U8.schema],+ Schema.ref Schema.null+ ],+ False+ )+ ]++bitPut :: PartyLeader -> BitPut.BitPut+bitPut x =+ U8.bitPut (systemId x)+ <> foldMap RemoteId.bitPut (remoteId x)+ <> foldMap+ U8.bitPut+ (localId x)++bitGet :: Version.Version -> BitGet.BitGet PartyLeader+bitGet version = BitGet.label "PartyLeader" $ do+ systemId <- BitGet.label "systemId" U8.bitGet+ (remoteId, localId) <-+ if systemId == U8.fromWord8 0+ then pure (Nothing, Nothing)+ else do+ remoteId <- BitGet.label "remoteId" $ RemoteId.bitGet version systemId+ localId <- BitGet.label "localId" U8.bitGet+ pure (Just remoteId, Just localId)+ pure PartyLeader {systemId, remoteId, localId}
@@ -0,0 +1,52 @@+module Rattletrap.Type.Attribute.Pickup where++import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.U32 as U32+import qualified Rattletrap.Utility.Json as Json+import qualified Rattletrap.Utility.Monad as Monad++data Pickup = Pickup+ { instigatorId :: Maybe U32.U32,+ pickedUp :: Bool+ }+ deriving (Eq, Show)++instance Json.FromJSON Pickup where+ parseJSON = Json.withObject "Pickup" $ \object -> do+ instigatorId <- Json.optional object "instigator_id"+ pickedUp <- Json.required object "picked_up"+ pure Pickup {instigatorId, pickedUp}++instance Json.ToJSON Pickup where+ toJSON x =+ Json.object+ [ Json.pair "instigator_id" $ instigatorId x,+ Json.pair "picked_up" $ pickedUp x+ ]++schema :: Schema.Schema+schema =+ Schema.named "attribute-pickup" $+ Schema.object+ [ (Json.pair "instigator_id" . Schema.json $ Schema.maybe U32.schema, False),+ (Json.pair "picked_up" $ Schema.ref Schema.boolean, True)+ ]++bitPut :: Pickup -> BitPut.BitPut+bitPut x =+ maybe+ (BitPut.bool False)+ (\y -> BitPut.bool True <> U32.bitPut y)+ (instigatorId x)+ <> BitPut.bool (pickedUp x)++bitGet :: BitGet.BitGet Pickup+bitGet = BitGet.label "Pickup" $ do+ instigator <- BitGet.label "instigator" BitGet.bool+ instigatorId <-+ BitGet.label "instigatorId" $+ Monad.whenMaybe instigator U32.bitGet+ pickedUp <- BitGet.label "pickedUp" BitGet.bool+ pure Pickup {instigatorId, pickedUp}
@@ -0,0 +1,94 @@+module Rattletrap.Type.Attribute.PickupInfo where++import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.I32 as I32+import qualified Rattletrap.Type.U32 as U32+import qualified Rattletrap.Utility.Json as Json++data PickupInfo = PickupInfo+ { unknown1 :: Bool,+ unknown2 :: Bool,+ unknown3 :: U32.U32,+ unknown4 :: I32.I32,+ unknown5 :: I32.I32,+ unknown6 :: Bool,+ unknown7 :: Bool+ }+ deriving (Eq, Show)++instance Json.FromJSON PickupInfo where+ parseJSON = Json.withObject "PickupInfo" $ \object -> do+ unknown1 <- Json.required object "unknown1"+ unknown2 <- Json.required object "unknown2"+ unknown3 <- Json.required object "unknown3"+ unknown4 <- Json.required object "unknown4"+ unknown5 <- Json.required object "unknown5"+ unknown6 <- Json.required object "unknown6"+ unknown7 <- Json.required object "unknown7"+ pure+ PickupInfo+ { unknown1,+ unknown2,+ unknown3,+ unknown4,+ unknown5,+ unknown6,+ unknown7+ }++instance Json.ToJSON PickupInfo where+ toJSON x =+ Json.object+ [ Json.pair "unknown1" $ unknown1 x,+ Json.pair "unknown2" $ unknown2 x,+ Json.pair "unknown3" $ unknown3 x,+ Json.pair "unknown4" $ unknown4 x,+ Json.pair "unknown5" $ unknown5 x,+ Json.pair "unknown6" $ unknown6 x,+ Json.pair "unknown7" $ unknown7 x+ ]++schema :: Schema.Schema+schema =+ Schema.named "pickup-info" $+ Schema.object+ [ (Json.pair "unknown1" $ Schema.ref Schema.boolean, True),+ (Json.pair "unknown2" $ Schema.ref Schema.boolean, True),+ (Json.pair "unknown3" $ Schema.ref U32.schema, True),+ (Json.pair "unknown4" $ Schema.ref I32.schema, True),+ (Json.pair "unknown5" $ Schema.ref I32.schema, True),+ (Json.pair "unknown6" $ Schema.ref Schema.boolean, True),+ (Json.pair "unknown7" $ Schema.ref Schema.boolean, True)+ ]++bitPut :: PickupInfo -> BitPut.BitPut+bitPut x =+ BitPut.bool (unknown1 x)+ <> BitPut.bool (unknown2 x)+ <> U32.bitPut (unknown3 x)+ <> I32.bitPut (unknown4 x)+ <> I32.bitPut (unknown5 x)+ <> BitPut.bool (unknown6 x)+ <> BitPut.bool (unknown7 x)++bitGet :: BitGet.BitGet PickupInfo+bitGet = BitGet.label "PickupInfo" $ do+ unknown1 <- BitGet.label "unknown1" BitGet.bool+ unknown2 <- BitGet.label "unknown2" BitGet.bool+ unknown3 <- BitGet.label "unknown3" U32.bitGet+ unknown4 <- BitGet.label "unknown4" I32.bitGet+ unknown5 <- BitGet.label "unknown5" I32.bitGet+ unknown6 <- BitGet.label "unknown6" BitGet.bool+ unknown7 <- BitGet.label "unknown7" BitGet.bool+ pure+ PickupInfo+ { unknown1,+ unknown2,+ unknown3,+ unknown4,+ unknown5,+ unknown6,+ unknown7+ }
@@ -0,0 +1,53 @@+module Rattletrap.Type.Attribute.PickupNew where++import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.U32 as U32+import qualified Rattletrap.Type.U8 as U8+import qualified Rattletrap.Utility.Json as Json+import qualified Rattletrap.Utility.Monad as Monad++data PickupNew = PickupNew+ { instigatorId :: Maybe U32.U32,+ pickedUp :: U8.U8+ }+ deriving (Eq, Show)++instance Json.FromJSON PickupNew where+ parseJSON = Json.withObject "PickupNew" $ \object -> do+ instigatorId <- Json.optional object "instigator_id"+ pickedUp <- Json.required object "picked_up"+ pure PickupNew {instigatorId, pickedUp}++instance Json.ToJSON PickupNew where+ toJSON x =+ Json.object+ [ Json.pair "instigator_id" $ instigatorId x,+ Json.pair "picked_up" $ pickedUp x+ ]++schema :: Schema.Schema+schema =+ Schema.named "attribute-pickup-new" $+ Schema.object+ [ (Json.pair "instigator_id" . Schema.json $ Schema.maybe U32.schema, False),+ (Json.pair "picked_up" $ Schema.ref U8.schema, True)+ ]++bitPut :: PickupNew -> BitPut.BitPut+bitPut x =+ maybe+ (BitPut.bool False)+ (\y -> BitPut.bool True <> U32.bitPut y)+ (instigatorId x)+ <> U8.bitPut (pickedUp x)++bitGet :: BitGet.BitGet PickupNew+bitGet = BitGet.label "PickupNew" $ do+ instigator <- BitGet.label "instigator" BitGet.bool+ instigatorId <-+ BitGet.label "instigatorId" $+ Monad.whenMaybe instigator U32.bitGet+ pickedUp <- BitGet.label "pickedUp" U8.bitGet+ pure PickupNew {instigatorId, pickedUp}
@@ -0,0 +1,30 @@+module Rattletrap.Type.Attribute.PlayerHistoryKey where++import qualified Data.Word as Word+import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Utility.Json as Json++newtype PlayerHistoryKey = PlayerHistoryKey+ { unknown :: Word.Word16+ }+ deriving (Eq, Show)++instance Json.FromJSON PlayerHistoryKey where+ parseJSON = fmap PlayerHistoryKey . Json.parseJSON++instance Json.ToJSON PlayerHistoryKey where+ toJSON = Json.toJSON . unknown++schema :: Schema.Schema+schema =+ Schema.named "attribute-player-history-key" $ Schema.ref Schema.number++bitPut :: PlayerHistoryKey -> BitPut.BitPut+bitPut = BitPut.bits 14 . unknown++bitGet :: BitGet.BitGet PlayerHistoryKey+bitGet = BitGet.label "PlayerHistoryKey" $ do+ unknown <- BitGet.label "unknown" $ BitGet.bits 14+ pure PlayerHistoryKey {unknown}
@@ -0,0 +1,86 @@+module Rattletrap.Type.Attribute.PrivateMatchSettings where++import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.Str as Str+import qualified Rattletrap.Type.U32 as U32+import qualified Rattletrap.Utility.Json as Json++data PrivateMatchSettings = PrivateMatchSettings+ { mutators :: Str.Str,+ joinableBy :: U32.U32,+ maxPlayers :: U32.U32,+ gameName :: Str.Str,+ password :: Str.Str,+ flag :: Bool+ }+ deriving (Eq, Show)++instance Json.FromJSON PrivateMatchSettings where+ parseJSON = Json.withObject "PrivateMatchSettings" $ \object -> do+ mutators <- Json.required object "mutators"+ joinableBy <- Json.required object "joinable_by"+ maxPlayers <- Json.required object "max_players"+ gameName <- Json.required object "game_name"+ password <- Json.required object "password"+ flag <- Json.required object "flag"+ pure+ PrivateMatchSettings+ { mutators,+ joinableBy,+ maxPlayers,+ gameName,+ password,+ flag+ }++instance Json.ToJSON PrivateMatchSettings where+ toJSON x =+ Json.object+ [ Json.pair "mutators" $ mutators x,+ Json.pair "joinable_by" $ joinableBy x,+ Json.pair "max_players" $ maxPlayers x,+ Json.pair "game_name" $ gameName x,+ Json.pair "password" $ password x,+ Json.pair "flag" $ flag x+ ]++schema :: Schema.Schema+schema =+ Schema.named "attribute-private-match-settings" $+ Schema.object+ [ (Json.pair "mutators" $ Schema.ref Str.schema, True),+ (Json.pair "joinable_by" $ Schema.ref U32.schema, True),+ (Json.pair "max_players" $ Schema.ref U32.schema, True),+ (Json.pair "game_name" $ Schema.ref Str.schema, True),+ (Json.pair "password" $ Schema.ref Str.schema, True),+ (Json.pair "flag" $ Schema.ref Schema.boolean, True)+ ]++bitPut :: PrivateMatchSettings -> BitPut.BitPut+bitPut privateMatchSettingsAttribute =+ Str.bitPut (mutators privateMatchSettingsAttribute)+ <> U32.bitPut (joinableBy privateMatchSettingsAttribute)+ <> U32.bitPut (maxPlayers privateMatchSettingsAttribute)+ <> Str.bitPut (gameName privateMatchSettingsAttribute)+ <> Str.bitPut (password privateMatchSettingsAttribute)+ <> BitPut.bool (flag privateMatchSettingsAttribute)++bitGet :: BitGet.BitGet PrivateMatchSettings+bitGet = BitGet.label "PrivateMatchSettings" $ do+ mutators <- BitGet.label "mutators" Str.bitGet+ joinableBy <- BitGet.label "joinableBy" U32.bitGet+ maxPlayers <- BitGet.label "maxPlayers" U32.bitGet+ gameName <- BitGet.label "gameName" Str.bitGet+ password <- BitGet.label "password" Str.bitGet+ flag <- BitGet.label "flag" BitGet.bool+ pure+ PrivateMatchSettings+ { mutators,+ joinableBy,+ maxPlayers,+ gameName,+ password,+ flag+ }
@@ -0,0 +1,78 @@+module Rattletrap.Type.Attribute.Product where++import qualified Data.Map as Map+import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.Attribute.ProductValue as ProductValue+import qualified Rattletrap.Type.List as RList+import qualified Rattletrap.Type.Str as Str+import qualified Rattletrap.Type.U32 as U32+import qualified Rattletrap.Type.U8 as U8+import qualified Rattletrap.Type.Version as Version+import qualified Rattletrap.Utility.Json as Json++data Product = Product+ { unknown :: Bool,+ objectId :: U32.U32,+ -- | read-only+ objectName :: Maybe Str.Str,+ value :: ProductValue.ProductValue+ }+ deriving (Eq, Show)++instance Json.FromJSON Product where+ parseJSON = Json.withObject "Product" $ \object -> do+ unknown <- Json.required object "unknown"+ objectId <- Json.required object "object_id"+ objectName <- Json.optional object "object_name"+ value <- Json.required object "value"+ pure Product {unknown, objectId, objectName, value}++instance Json.ToJSON Product where+ toJSON x =+ Json.object+ [ Json.pair "unknown" $ unknown x,+ Json.pair "object_id" $ objectId x,+ Json.pair "object_name" $ objectName x,+ Json.pair "value" $ value x+ ]++schema :: Schema.Schema+schema =+ Schema.named "attribute-product" $+ Schema.object+ [ (Json.pair "unknown" $ Schema.ref Schema.boolean, True),+ (Json.pair "object_id" $ Schema.ref U32.schema, True),+ (Json.pair "object_name" . Schema.json $ Schema.maybe Str.schema, False),+ (Json.pair "value" $ Schema.ref ProductValue.schema, True)+ ]++putProductAttributes :: RList.List Product -> BitPut.BitPut+putProductAttributes attributes =+ let v = RList.toList attributes+ in (U8.bitPut . U8.fromWord8 . fromIntegral $ length v) <> foldMap bitPut v++bitPut :: Product -> BitPut.BitPut+bitPut attribute =+ BitPut.bool (unknown attribute)+ <> U32.bitPut (objectId attribute)+ <> ProductValue.bitPut (value attribute)++decodeProductAttributesBits ::+ Version.Version ->+ Map.Map U32.U32 Str.Str ->+ BitGet.BitGet (RList.List Product)+decodeProductAttributesBits version objectMap = do+ size <- U8.bitGet+ RList.replicateM (fromIntegral $ U8.toWord8 size) $ bitGet version objectMap++bitGet :: Version.Version -> Map.Map U32.U32 Str.Str -> BitGet.BitGet Product+bitGet version objectMap = BitGet.label "Product" $ do+ unknown <- BitGet.label "unknown" BitGet.bool+ objectId <- BitGet.label "objectId" U32.bitGet+ let objectName = Map.lookup objectId objectMap+ value <-+ BitGet.label "value" $+ ProductValue.bitGet version objectId objectName+ pure Product {unknown, objectId, objectName, value}
@@ -0,0 +1,126 @@+module Rattletrap.Type.Attribute.ProductValue where++import qualified Data.Foldable as Foldable+import qualified Data.Word as Word+import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Exception.MissingProductName as MissingProductName+import qualified Rattletrap.Exception.UnknownProduct as UnknownProduct+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.CompressedWord as CompressedWord+import qualified Rattletrap.Type.Str as Str+import qualified Rattletrap.Type.U32 as U32+import qualified Rattletrap.Type.Version as Version+import qualified Rattletrap.Utility.Json as Json+import qualified Rattletrap.Utility.Monad as Monad++data ProductValue+ = PaintedOld CompressedWord.CompressedWord+ | PaintedNew Word.Word32+ | TeamEditionOld CompressedWord.CompressedWord+ | TeamEditionNew Word.Word32+ | SpecialEdition Word.Word32+ | UserColorOld (Maybe Word.Word32)+ | UserColorNew U32.U32+ | TitleId Str.Str+ deriving (Eq, Show)++instance Json.FromJSON ProductValue where+ parseJSON = Json.withObject "ProductValue" $ \object ->+ Foldable.asum+ [ fmap PaintedOld $ Json.required object "painted_old",+ fmap PaintedNew $ Json.required object "painted_new",+ fmap TeamEditionOld $ Json.required object "team_edition_old",+ fmap TeamEditionNew $ Json.required object "team_edition_new",+ fmap SpecialEdition $ Json.required object "special_edition",+ fmap UserColorOld $ Json.required object "user_color_old",+ fmap UserColorNew $ Json.required object "user_color_new",+ fmap TitleId $ Json.required object "title_id"+ ]++instance Json.ToJSON ProductValue where+ toJSON x = case x of+ PaintedOld y -> Json.object [Json.pair "painted_old" y]+ PaintedNew y -> Json.object [Json.pair "painted_new" y]+ TeamEditionOld y -> Json.object [Json.pair "team_edition_old" y]+ TeamEditionNew y -> Json.object [Json.pair "team_edition_new" y]+ SpecialEdition y -> Json.object [Json.pair "special_edition" y]+ UserColorOld y -> Json.object [Json.pair "user_color_old" y]+ UserColorNew y -> Json.object [Json.pair "user_color_new" y]+ TitleId y -> Json.object [Json.pair "title_id" y]++schema :: Schema.Schema+schema =+ Schema.named "attribute-product-value" . Schema.oneOf $+ fmap+ (\(k, v) -> Schema.object [(Json.pair k v, True)])+ [ ("painted_old", Schema.ref CompressedWord.schema),+ ("painted_new", Schema.ref Schema.integer),+ ("team_edition_old", Schema.ref CompressedWord.schema),+ ("team_edition_new", Schema.ref Schema.integer),+ ("special_edition", Schema.ref Schema.integer),+ ("user_color_old", Schema.json $ Schema.maybe Schema.integer),+ ("user_color_new", Schema.ref U32.schema),+ ("title_id", Schema.ref Str.schema)+ ]++bitPut :: ProductValue -> BitPut.BitPut+bitPut val = case val of+ PaintedOld x -> CompressedWord.bitPut x+ PaintedNew x -> BitPut.bits 31 x+ TeamEditionOld x -> CompressedWord.bitPut x+ TeamEditionNew x -> BitPut.bits 31 x+ SpecialEdition x -> BitPut.bits 31 x+ UserColorOld x -> case x of+ Nothing -> BitPut.bool False+ Just y -> BitPut.bool True <> BitPut.bits 31 y+ UserColorNew x -> U32.bitPut x+ TitleId x -> Str.bitPut x++bitGet ::+ Version.Version -> U32.U32 -> Maybe Str.Str -> BitGet.BitGet ProductValue+bitGet version objectId maybeObjectName =+ BitGet.label "ProductValue" $ case fmap Str.toString maybeObjectName of+ Just "TAGame.ProductAttribute_Painted_TA" -> decodePainted version+ Just "TAGame.ProductAttribute_SpecialEdition_TA" -> decodeSpecialEdition+ Just "TAGame.ProductAttribute_TeamEdition_TA" -> decodeTeamEdition version+ Just "TAGame.ProductAttribute_TitleID_TA" -> decodeTitle+ Just "TAGame.ProductAttribute_UserColor_TA" -> decodeColor version+ Just x -> BitGet.throw $ UnknownProduct.UnknownProduct x+ Nothing ->+ BitGet.throw . MissingProductName.MissingProductName $+ U32.toWord32+ objectId++decodeSpecialEdition :: BitGet.BitGet ProductValue+decodeSpecialEdition =+ BitGet.label "SpecialEdition" . fmap SpecialEdition $ BitGet.bits 31++decodePainted :: Version.Version -> BitGet.BitGet ProductValue+decodePainted version =+ BitGet.label "Painted" $+ if hasNewPainted version+ then fmap PaintedNew $ BitGet.bits 31+ else fmap PaintedOld $ CompressedWord.bitGet 13++decodeTeamEdition :: Version.Version -> BitGet.BitGet ProductValue+decodeTeamEdition version =+ BitGet.label "TeamEdition" $+ if hasNewPainted version+ then fmap TeamEditionNew $ BitGet.bits 31+ else fmap TeamEditionOld $ CompressedWord.bitGet 13++decodeColor :: Version.Version -> BitGet.BitGet ProductValue+decodeColor version =+ BitGet.label "UserColor" $+ if Version.atLeast 868 23 8 version+ then fmap UserColorNew U32.bitGet+ else do+ hasValue <- BitGet.bool+ fmap UserColorOld $ Monad.whenMaybe hasValue (BitGet.bits 31)++hasNewPainted :: Version.Version -> Bool+hasNewPainted = Version.atLeast 868 18 0++decodeTitle :: BitGet.BitGet ProductValue+decodeTitle = BitGet.label "Title" $ fmap TitleId Str.bitGet
@@ -0,0 +1,29 @@+module Rattletrap.Type.Attribute.QWord 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 QWord = QWord+ { value :: U64.U64+ }+ deriving (Eq, Show)++instance Json.FromJSON QWord where+ parseJSON = fmap QWord . Json.parseJSON++instance Json.ToJSON QWord where+ toJSON = Json.toJSON . value++schema :: Schema.Schema+schema = Schema.named "attribute-q-word" $ Schema.ref U64.schema++bitPut :: QWord -> BitPut.BitPut+bitPut qWordAttribute = U64.bitPut (value qWordAttribute)++bitGet :: BitGet.BitGet QWord+bitGet = BitGet.label "QWord" $ do+ value <- BitGet.label "value" U64.bitGet+ pure QWord {value}
@@ -0,0 +1,59 @@+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}
@@ -0,0 +1,82 @@+module Rattletrap.Type.Attribute.Reservation where++import qualified Data.Word as Word+import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.Attribute.UniqueId as UniqueId+import qualified Rattletrap.Type.CompressedWord as CompressedWord+import qualified Rattletrap.Type.Str as Str+import qualified Rattletrap.Type.U8 as U8+import qualified Rattletrap.Type.Version as Version+import qualified Rattletrap.Utility.Json as Json+import qualified Rattletrap.Utility.Monad as Monad++data Reservation = Reservation+ { number :: CompressedWord.CompressedWord,+ uniqueId :: UniqueId.UniqueId,+ name :: Maybe Str.Str,+ unknown1 :: Bool,+ unknown2 :: Bool,+ unknown3 :: Maybe Word.Word8+ }+ deriving (Eq, Show)++instance Json.FromJSON Reservation where+ parseJSON = Json.withObject "Reservation" $ \object -> do+ number <- Json.required object "number"+ uniqueId <- Json.required object "unique_id"+ name <- Json.optional object "name"+ unknown1 <- Json.required object "unknown1"+ unknown2 <- Json.required object "unknown2"+ unknown3 <- Json.optional object "unknown3"+ pure Reservation {number, uniqueId, name, unknown1, unknown2, unknown3}++instance Json.ToJSON Reservation where+ toJSON x =+ Json.object+ [ Json.pair "number" $ number x,+ Json.pair "unique_id" $ uniqueId x,+ Json.pair "name" $ name x,+ Json.pair "unknown1" $ unknown1 x,+ Json.pair "unknown2" $ unknown2 x,+ Json.pair "unknown3" $ unknown3 x+ ]++schema :: Schema.Schema+schema =+ Schema.named "attribute-reservation" $+ Schema.object+ [ (Json.pair "number" $ Schema.ref CompressedWord.schema, True),+ (Json.pair "unique_id" $ Schema.ref UniqueId.schema, True),+ (Json.pair "name" . Schema.json $ Schema.maybe Str.schema, False),+ (Json.pair "unknown1" $ Schema.ref Schema.boolean, True),+ (Json.pair "unknown2" $ Schema.ref Schema.boolean, True),+ (Json.pair "unknown3" . Schema.json $ Schema.maybe Schema.integer, False)+ ]++bitPut :: Reservation -> BitPut.BitPut+bitPut reservationAttribute =+ CompressedWord.bitPut (number reservationAttribute)+ <> UniqueId.bitPut (uniqueId reservationAttribute)+ <> foldMap Str.bitPut (name reservationAttribute)+ <> BitPut.bool (unknown1 reservationAttribute)+ <> BitPut.bool (unknown2 reservationAttribute)+ <> foldMap (BitPut.bits 6) (unknown3 reservationAttribute)++bitGet :: Version.Version -> BitGet.BitGet Reservation+bitGet version = BitGet.label "Reservation" $ do+ number <- BitGet.label "number" $ CompressedWord.bitGet 7+ uniqueId <- BitGet.label "uniqueId" $ UniqueId.bitGet version+ name <-+ BitGet.label "name" $+ Monad.whenMaybe+ (UniqueId.systemId uniqueId /= U8.fromWord8 0)+ Str.bitGet+ unknown1 <- BitGet.label "unknown1" BitGet.bool+ unknown2 <- BitGet.label "unknown2" BitGet.bool+ unknown3 <-+ BitGet.label "unknown3"+ . Monad.whenMaybe (Version.atLeast 868 12 0 version)+ $ BitGet.bits 6+ pure Reservation {number, uniqueId, name, unknown1, unknown2, unknown3}
@@ -0,0 +1,88 @@+module Rattletrap.Type.Attribute.RigidBodyState where++import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.Rotation as Rotation+import qualified Rattletrap.Type.Vector as Vector+import qualified Rattletrap.Type.Version as Version+import qualified Rattletrap.Utility.Json as Json+import qualified Rattletrap.Utility.Monad as Monad++data RigidBodyState = RigidBodyState+ { sleeping :: Bool,+ location :: Vector.Vector,+ rotation :: Rotation.Rotation,+ linearVelocity :: Maybe Vector.Vector,+ angularVelocity :: Maybe Vector.Vector+ }+ deriving (Eq, Show)++instance Json.FromJSON RigidBodyState where+ parseJSON = Json.withObject "RigidBodyState" $ \object -> do+ sleeping <- Json.required object "sleeping"+ location <- Json.required object "location"+ rotation <- Json.required object "rotation"+ linearVelocity <- Json.optional object "linear_velocity"+ angularVelocity <- Json.optional object "angular_velocity"+ pure+ RigidBodyState+ { sleeping,+ location,+ rotation,+ linearVelocity,+ angularVelocity+ }++instance Json.ToJSON RigidBodyState where+ toJSON x =+ Json.object+ [ Json.pair "sleeping" $ sleeping x,+ Json.pair "location" $ location x,+ Json.pair "rotation" $ rotation x,+ Json.pair "linear_velocity" $ linearVelocity x,+ Json.pair "angular_velocity" $ angularVelocity x+ ]++schema :: Schema.Schema+schema =+ Schema.named "attribute-rigid-body-state" $+ Schema.object+ [ (Json.pair "sleeping" $ Schema.ref Schema.boolean, True),+ (Json.pair "location" $ Schema.ref Vector.schema, True),+ (Json.pair "rotation" $ Schema.ref Rotation.schema, True),+ ( Json.pair "linear_velocity" . Schema.json $ Schema.maybe Vector.schema,+ False+ ),+ ( Json.pair "angular_velocity" . Schema.json $ Schema.maybe Vector.schema,+ False+ )+ ]++bitPut :: RigidBodyState -> BitPut.BitPut+bitPut rigidBodyStateAttribute =+ BitPut.bool (sleeping rigidBodyStateAttribute)+ <> Vector.bitPut (location rigidBodyStateAttribute)+ <> Rotation.bitPut (rotation rigidBodyStateAttribute)+ <> foldMap Vector.bitPut (linearVelocity rigidBodyStateAttribute)+ <> foldMap Vector.bitPut (angularVelocity rigidBodyStateAttribute)++bitGet :: Version.Version -> BitGet.BitGet RigidBodyState+bitGet version = BitGet.label "RigidBodyState" $ do+ sleeping <- BitGet.label "sleeping" BitGet.bool+ location <- BitGet.label "location" $ Vector.bitGet version+ rotation <- BitGet.label "rotation" $ Rotation.bitGet version+ linearVelocity <-+ BitGet.label "linearVelocity" $+ Monad.whenMaybe (not sleeping) (Vector.bitGet version)+ angularVelocity <-+ BitGet.label "angularVelocity" $+ Monad.whenMaybe (not sleeping) (Vector.bitGet version)+ pure+ RigidBodyState+ { sleeping,+ location,+ rotation,+ linearVelocity,+ angularVelocity+ }
@@ -0,0 +1,29 @@+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}
@@ -0,0 +1,43 @@+module Rattletrap.Type.Attribute.StatEvent where++import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.I32 as I32+import qualified Rattletrap.Utility.Json as Json++data StatEvent = StatEvent+ { unknown :: Bool,+ objectId :: I32.I32+ }+ deriving (Eq, Show)++instance Json.FromJSON StatEvent where+ parseJSON = Json.withObject "StatEvent" $ \object -> do+ unknown <- Json.required object "unknown"+ objectId <- Json.required object "object_id"+ pure StatEvent {unknown, objectId}++instance Json.ToJSON StatEvent where+ toJSON x =+ Json.object+ [Json.pair "unknown" $ unknown x, Json.pair "object_id" $ objectId x]++schema :: Schema.Schema+schema =+ Schema.named "attribute-stat-event" $+ Schema.object+ [ (Json.pair "unknown" $ Schema.ref Schema.boolean, True),+ (Json.pair "object_id" $ Schema.ref I32.schema, True)+ ]++bitPut :: StatEvent -> BitPut.BitPut+bitPut statEventAttribute =+ BitPut.bool (unknown statEventAttribute)+ <> I32.bitPut (objectId statEventAttribute)++bitGet :: BitGet.BitGet StatEvent+bitGet = BitGet.label "StatEvent" $ do+ unknown <- BitGet.label "unknown" BitGet.bool+ objectId <- BitGet.label "objectId" I32.bitGet+ pure StatEvent {unknown, objectId}
@@ -0,0 +1,30 @@+module Rattletrap.Type.Attribute.String where++import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.Str as Str+import qualified Rattletrap.Utility.Json as Json+import Prelude hiding (String)++newtype String = String+ { value :: Str.Str+ }+ deriving (Eq, Show)++instance Json.FromJSON String where+ parseJSON = fmap String . Json.parseJSON++instance Json.ToJSON String where+ toJSON = Json.toJSON . value++schema :: Schema.Schema+schema = Schema.named "attribute-string" $ Schema.ref Str.schema++bitPut :: String -> BitPut.BitPut+bitPut stringAttribute = Str.bitPut (value stringAttribute)++bitGet :: BitGet.BitGet String+bitGet = BitGet.label "String" $ do+ value <- BitGet.label "value" Str.bitGet+ pure String {value}
@@ -0,0 +1,78 @@+module Rattletrap.Type.Attribute.TeamPaint where++import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.U32 as U32+import qualified Rattletrap.Type.U8 as U8+import qualified Rattletrap.Utility.Json as Json++data TeamPaint = TeamPaint+ { team :: U8.U8,+ primaryColor :: U8.U8,+ accentColor :: U8.U8,+ primaryFinish :: U32.U32,+ accentFinish :: U32.U32+ }+ deriving (Eq, Show)++instance Json.FromJSON TeamPaint where+ parseJSON = Json.withObject "TeamPaint" $ \object -> do+ team <- Json.required object "team"+ primaryColor <- Json.required object "primary_color"+ accentColor <- Json.required object "accent_color"+ primaryFinish <- Json.required object "primary_finish"+ accentFinish <- Json.required object "accent_finish"+ pure+ TeamPaint+ { team,+ primaryColor,+ accentColor,+ primaryFinish,+ accentFinish+ }++instance Json.ToJSON TeamPaint where+ toJSON x =+ Json.object+ [ Json.pair "team" $ team x,+ Json.pair "primary_color" $ primaryColor x,+ Json.pair "accent_color" $ accentColor x,+ Json.pair "primary_finish" $ primaryFinish x,+ Json.pair "accent_finish" $ accentFinish x+ ]++schema :: Schema.Schema+schema =+ Schema.named "attribute-team-paint" $+ Schema.object+ [ (Json.pair "team" $ Schema.ref U8.schema, True),+ (Json.pair "primary_color" $ Schema.ref U8.schema, True),+ (Json.pair "accent_color" $ Schema.ref U8.schema, True),+ (Json.pair "primary_finish" $ Schema.ref U32.schema, True),+ (Json.pair "accent_finish" $ Schema.ref U32.schema, True)+ ]++bitPut :: TeamPaint -> BitPut.BitPut+bitPut teamPaintAttribute =+ U8.bitPut (team teamPaintAttribute)+ <> U8.bitPut (primaryColor teamPaintAttribute)+ <> U8.bitPut (accentColor teamPaintAttribute)+ <> U32.bitPut (primaryFinish teamPaintAttribute)+ <> U32.bitPut (accentFinish teamPaintAttribute)++bitGet :: BitGet.BitGet TeamPaint+bitGet = BitGet.label "TeamPaint" $ do+ team <- BitGet.label "team" U8.bitGet+ primaryColor <- BitGet.label "primaryColor" U8.bitGet+ accentColor <- BitGet.label "accentColor" U8.bitGet+ primaryFinish <- BitGet.label "primaryFinish" U32.bitGet+ accentFinish <- BitGet.label "accentFinish" U32.bitGet+ pure+ TeamPaint+ { team,+ primaryColor,+ accentColor,+ primaryFinish,+ accentFinish+ }
@@ -0,0 +1,101 @@+module Rattletrap.Type.Attribute.Title where++import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.U32 as U32+import qualified Rattletrap.Utility.Json as Json++data Title = Title+ { unknown1 :: Bool,+ unknown2 :: Bool,+ unknown3 :: U32.U32,+ unknown4 :: U32.U32,+ unknown5 :: U32.U32,+ unknown6 :: U32.U32,+ unknown7 :: U32.U32,+ unknown8 :: Bool+ }+ deriving (Eq, Show)++instance Json.FromJSON Title where+ parseJSON = Json.withObject "Title" $ \object -> do+ unknown1 <- Json.required object "unknown1"+ unknown2 <- Json.required object "unknown2"+ unknown3 <- Json.required object "unknown3"+ unknown4 <- Json.required object "unknown4"+ unknown5 <- Json.required object "unknown5"+ unknown6 <- Json.required object "unknown6"+ unknown7 <- Json.required object "unknown7"+ unknown8 <- Json.required object "unknown8"+ pure+ Title+ { unknown1,+ unknown2,+ unknown3,+ unknown4,+ unknown5,+ unknown6,+ unknown7,+ unknown8+ }++instance Json.ToJSON Title where+ toJSON x =+ Json.object+ [ Json.pair "unknown1" $ unknown1 x,+ Json.pair "unknown2" $ unknown2 x,+ Json.pair "unknown3" $ unknown3 x,+ Json.pair "unknown4" $ unknown4 x,+ Json.pair "unknown5" $ unknown5 x,+ Json.pair "unknown6" $ unknown6 x,+ Json.pair "unknown7" $ unknown7 x,+ Json.pair "unknown8" $ unknown8 x+ ]++schema :: Schema.Schema+schema =+ Schema.named "attribute-title" $+ Schema.object+ [ (Json.pair "unknown1" $ Schema.ref Schema.boolean, True),+ (Json.pair "unknown2" $ Schema.ref Schema.boolean, True),+ (Json.pair "unknown3" $ Schema.ref U32.schema, True),+ (Json.pair "unknown4" $ Schema.ref U32.schema, True),+ (Json.pair "unknown5" $ Schema.ref U32.schema, True),+ (Json.pair "unknown6" $ Schema.ref U32.schema, True),+ (Json.pair "unknown7" $ Schema.ref U32.schema, True),+ (Json.pair "unknown8" $ Schema.ref Schema.boolean, True)+ ]++bitPut :: Title -> BitPut.BitPut+bitPut titleAttribute =+ BitPut.bool (unknown1 titleAttribute)+ <> BitPut.bool (unknown2 titleAttribute)+ <> U32.bitPut (unknown3 titleAttribute)+ <> U32.bitPut (unknown4 titleAttribute)+ <> U32.bitPut (unknown5 titleAttribute)+ <> U32.bitPut (unknown6 titleAttribute)+ <> U32.bitPut (unknown7 titleAttribute)+ <> BitPut.bool (unknown8 titleAttribute)++bitGet :: BitGet.BitGet Title+bitGet = BitGet.label "Title" $ do+ unknown1 <- BitGet.label "unknown1" BitGet.bool+ unknown2 <- BitGet.label "unknown2" BitGet.bool+ unknown3 <- BitGet.label "unknown3" U32.bitGet+ unknown4 <- BitGet.label "unknown4" U32.bitGet+ unknown5 <- BitGet.label "unknown5" U32.bitGet+ unknown6 <- BitGet.label "unknown6" U32.bitGet+ unknown7 <- BitGet.label "unknown7" U32.bitGet+ unknown8 <- BitGet.label "unknown8" BitGet.bool+ pure+ Title+ { unknown1,+ unknown2,+ unknown3,+ unknown4,+ unknown5,+ unknown6,+ unknown7,+ unknown8+ }
@@ -0,0 +1,53 @@+module Rattletrap.Type.Attribute.UniqueId where++import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.RemoteId as RemoteId+import qualified Rattletrap.Type.U8 as U8+import qualified Rattletrap.Type.Version as Version+import qualified Rattletrap.Utility.Json as Json++data UniqueId = UniqueId+ { systemId :: U8.U8,+ remoteId :: RemoteId.RemoteId,+ localId :: U8.U8+ }+ deriving (Eq, Show)++instance Json.FromJSON UniqueId where+ parseJSON = Json.withObject "UniqueId" $ \object -> do+ systemId <- Json.required object "system_id"+ remoteId <- Json.required object "remote_id"+ localId <- Json.required object "local_id"+ pure UniqueId {systemId, remoteId, localId}++instance Json.ToJSON UniqueId where+ toJSON x =+ Json.object+ [ Json.pair "system_id" $ systemId x,+ Json.pair "remote_id" $ remoteId x,+ Json.pair "local_id" $ localId x+ ]++schema :: Schema.Schema+schema =+ Schema.named "attribute-unique-id" $+ Schema.object+ [ (Json.pair "system_id" $ Schema.ref U8.schema, True),+ (Json.pair "remote_id" $ Schema.ref RemoteId.schema, True),+ (Json.pair "local_id" $ Schema.ref U8.schema, True)+ ]++bitPut :: UniqueId -> BitPut.BitPut+bitPut uniqueIdAttribute =+ U8.bitPut (systemId uniqueIdAttribute)+ <> RemoteId.bitPut (remoteId uniqueIdAttribute)+ <> U8.bitPut (localId uniqueIdAttribute)++bitGet :: Version.Version -> BitGet.BitGet UniqueId+bitGet version = BitGet.label "UniqueId" $ do+ systemId <- BitGet.label "systemId" U8.bitGet+ remoteId <- BitGet.label "remoteId" $ RemoteId.bitGet version systemId+ localId <- BitGet.label "localId" U8.bitGet+ pure UniqueId {systemId, remoteId, localId}
@@ -0,0 +1,67 @@+module Rattletrap.Type.Attribute.WeldedInfo where++import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.F32 as F32+import qualified Rattletrap.Type.I32 as I32+import qualified Rattletrap.Type.Int8Vector as Int8Vector+import qualified Rattletrap.Type.Vector as Vector+import qualified Rattletrap.Type.Version as Version+import qualified Rattletrap.Utility.Json as Json++data WeldedInfo = WeldedInfo+ { active :: Bool,+ actorId :: I32.I32,+ offset :: Vector.Vector,+ mass :: F32.F32,+ rotation :: Int8Vector.Int8Vector+ }+ deriving (Eq, Show)++instance Json.FromJSON WeldedInfo where+ parseJSON = Json.withObject "WeldedInfo" $ \object -> do+ active <- Json.required object "active"+ actorId <- Json.required object "actor_id"+ offset <- Json.required object "offset"+ mass <- Json.required object "mass"+ rotation <- Json.required object "rotation"+ pure WeldedInfo {active, actorId, offset, mass, rotation}++instance Json.ToJSON WeldedInfo where+ toJSON x =+ Json.object+ [ Json.pair "active" $ active x,+ Json.pair "actor_id" $ actorId x,+ Json.pair "offset" $ offset x,+ Json.pair "mass" $ mass x,+ Json.pair "rotation" $ rotation x+ ]++schema :: Schema.Schema+schema =+ Schema.named "attribute-welded-info" $+ Schema.object+ [ (Json.pair "active" $ Schema.ref Schema.boolean, True),+ (Json.pair "actor_id" $ Schema.ref I32.schema, True),+ (Json.pair "offset" $ Schema.ref Vector.schema, True),+ (Json.pair "mass" $ Schema.ref F32.schema, True),+ (Json.pair "rotation" $ Schema.ref Int8Vector.schema, True)+ ]++bitPut :: WeldedInfo -> BitPut.BitPut+bitPut weldedInfoAttribute =+ BitPut.bool (active weldedInfoAttribute)+ <> I32.bitPut (actorId weldedInfoAttribute)+ <> Vector.bitPut (offset weldedInfoAttribute)+ <> F32.bitPut (mass weldedInfoAttribute)+ <> Int8Vector.bitPut (rotation weldedInfoAttribute)++bitGet :: Version.Version -> BitGet.BitGet WeldedInfo+bitGet version = BitGet.label "WeldedInfo" $ do+ active <- BitGet.label "active" BitGet.bool+ actorId <- BitGet.label "actorId" I32.bitGet+ offset <- BitGet.label "offset" $ Vector.bitGet version+ mass <- BitGet.label "mass" F32.bitGet+ rotation <- BitGet.label "rotation" Int8Vector.bitGet+ pure WeldedInfo {active, actorId, offset, mass, rotation}
@@ -0,0 +1,41 @@+module Rattletrap.Type.AttributeMapping where++import qualified Rattletrap.ByteGet as ByteGet+import qualified Rattletrap.BytePut as BytePut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.U32 as U32+import qualified Rattletrap.Utility.Json as Json++data AttributeMapping = AttributeMapping+ { objectId :: U32.U32,+ streamId :: U32.U32+ }+ deriving (Eq, Show)++instance Json.FromJSON AttributeMapping where+ parseJSON = Json.withObject "AttributeMapping" $ \object -> do+ objectId <- Json.required object "object_id"+ streamId <- Json.required object "stream_id"+ pure AttributeMapping {objectId, streamId}++instance Json.ToJSON AttributeMapping where+ toJSON x =+ Json.object+ [Json.pair "object_id" $ objectId x, Json.pair "stream_id" $ streamId x]++schema :: Schema.Schema+schema =+ Schema.named "attributeMapping" $+ Schema.object+ [ (Json.pair "object_id" $ Schema.ref U32.schema, True),+ (Json.pair "stream_id" $ Schema.ref U32.schema, True)+ ]++bytePut :: AttributeMapping -> BytePut.BytePut+bytePut x = U32.bytePut (objectId x) <> U32.bytePut (streamId x)++byteGet :: ByteGet.ByteGet AttributeMapping+byteGet = ByteGet.label "AttributeMapping" $ do+ objectId <- ByteGet.label "objectId" U32.byteGet+ streamId <- ByteGet.label "streamId" U32.byteGet+ pure AttributeMapping {objectId, streamId}
@@ -0,0 +1,47 @@+module Rattletrap.Type.AttributeType where++data AttributeType+ = AppliedDamage+ | Boolean+ | Boost+ | Byte+ | CamSettings+ | ClubColors+ | CustomDemolish+ | CustomDemolishExtended+ | DamageState+ | Demolish+ | Enum+ | Explosion+ | ExtendedExplosion+ | FlaggedInt+ | FlaggedByte+ | Float+ | GameMode+ | GameServer+ | Int+ | Int64+ | Loadout+ | LoadoutOnline+ | Loadouts+ | LoadoutsOnline+ | Location+ | MusicStinger+ | PartyLeader+ | Pickup+ | PickupInfo+ | PickupNew+ | PlayerHistoryKey+ | PrivateMatchSettings+ | QWord+ | RepStatTitle+ | Reservation+ | RigidBodyState+ | Rotation+ | StatEvent+ | String+ | TeamPaint+ | Title+ | UniqueId+ | WeldedInfo+ deriving (Eq, Show)
@@ -0,0 +1,364 @@+module Rattletrap.Type.AttributeValue where++import qualified Data.Foldable as Foldable+import qualified Data.Map as Map+import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Data as Data+import qualified Rattletrap.Exception.UnknownAttribute as UnknownAttribute+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.Attribute.AppliedDamage as AppliedDamage+import qualified Rattletrap.Type.Attribute.Boolean as Boolean+import qualified Rattletrap.Type.Attribute.Boost as Boost+import qualified Rattletrap.Type.Attribute.Byte as Byte+import qualified Rattletrap.Type.Attribute.CamSettings as CamSettings+import qualified Rattletrap.Type.Attribute.ClubColors as ClubColors+import qualified Rattletrap.Type.Attribute.CustomDemolish as CustomDemolish+import qualified Rattletrap.Type.Attribute.CustomDemolishExtended as CustomDemolishExtended+import qualified Rattletrap.Type.Attribute.DamageState as DamageState+import qualified Rattletrap.Type.Attribute.Demolish as Demolish+import qualified Rattletrap.Type.Attribute.Enum as Enum+import qualified Rattletrap.Type.Attribute.Explosion as Explosion+import qualified Rattletrap.Type.Attribute.ExtendedExplosion as ExtendedExplosion+import qualified Rattletrap.Type.Attribute.FlaggedByte as FlaggedByte+import qualified Rattletrap.Type.Attribute.FlaggedInt as FlaggedInt+import qualified Rattletrap.Type.Attribute.Float as Float+import qualified Rattletrap.Type.Attribute.GameMode as GameMode+import qualified Rattletrap.Type.Attribute.GameServer as GameServer+import qualified Rattletrap.Type.Attribute.Int as Int+import qualified Rattletrap.Type.Attribute.Int64 as Int64+import qualified Rattletrap.Type.Attribute.Loadout as Loadout+import qualified Rattletrap.Type.Attribute.LoadoutOnline as LoadoutOnline+import qualified Rattletrap.Type.Attribute.Loadouts as Loadouts+import qualified Rattletrap.Type.Attribute.LoadoutsOnline as LoadoutsOnline+import qualified Rattletrap.Type.Attribute.Location as Location+import qualified Rattletrap.Type.Attribute.MusicStinger as MusicStinger+import qualified Rattletrap.Type.Attribute.PartyLeader as PartyLeader+import qualified Rattletrap.Type.Attribute.Pickup as Pickup+import qualified Rattletrap.Type.Attribute.PickupInfo as PickupInfo+import qualified Rattletrap.Type.Attribute.PickupNew as PickupNew+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+import qualified Rattletrap.Type.Attribute.Title as Title+import qualified Rattletrap.Type.Attribute.UniqueId as UniqueId+import qualified Rattletrap.Type.Attribute.WeldedInfo as WeldedInfo+import qualified Rattletrap.Type.AttributeType as AttributeType+import qualified Rattletrap.Type.Str as Str+import qualified Rattletrap.Type.U32 as U32+import qualified Rattletrap.Type.Version as Version+import qualified Rattletrap.Utility.Json as Json++data AttributeValue+ = AppliedDamage AppliedDamage.AppliedDamage+ | Boolean Boolean.Boolean+ | Boost Boost.Boost+ | Byte Byte.Byte+ | CamSettings CamSettings.CamSettings+ | ClubColors ClubColors.ClubColors+ | CustomDemolish CustomDemolish.CustomDemolish+ | CustomDemolishExtended CustomDemolishExtended.CustomDemolishExtended+ | DamageState DamageState.DamageState+ | Demolish Demolish.Demolish+ | Enum Enum.Enum+ | Explosion Explosion.Explosion+ | ExtendedExplosion ExtendedExplosion.ExtendedExplosion+ | FlaggedInt FlaggedInt.FlaggedInt+ | FlaggedByte FlaggedByte.FlaggedByte+ | Float Float.Float+ | GameMode GameMode.GameMode+ | GameServer GameServer.GameServer+ | Int Int.Int+ | Int64 Int64.Int64+ | Loadout Loadout.Loadout+ | LoadoutOnline LoadoutOnline.LoadoutOnline+ | Loadouts Loadouts.Loadouts+ | LoadoutsOnline LoadoutsOnline.LoadoutsOnline+ | Location Location.Location+ | MusicStinger MusicStinger.MusicStinger+ | PartyLeader PartyLeader.PartyLeader+ | Pickup Pickup.Pickup+ | PickupInfo PickupInfo.PickupInfo+ | PickupNew PickupNew.PickupNew+ | 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+ | Title Title.Title+ | UniqueId UniqueId.UniqueId+ | WeldedInfo WeldedInfo.WeldedInfo+ deriving (Eq, Show)++instance Json.FromJSON AttributeValue where+ parseJSON = Json.withObject "AttributeValue" $ \object ->+ Foldable.asum+ [ fmap AppliedDamage $ Json.required object "applied_damage",+ fmap Boolean $ Json.required object "boolean",+ fmap Boost $ Json.required object "boost",+ fmap Byte $ Json.required object "byte",+ fmap CamSettings $ Json.required object "cam_settings",+ fmap ClubColors $ Json.required object "club_colors",+ fmap CustomDemolish $ Json.required object "custom_demolish",+ fmap CustomDemolishExtended $ Json.required object "custom_demolish_extended",+ fmap DamageState $ Json.required object "damage_state",+ fmap Demolish $ Json.required object "demolish",+ fmap Enum $ Json.required object "enum",+ fmap Explosion $ Json.required object "explosion",+ fmap ExtendedExplosion $ Json.required object "extended_explosion",+ fmap FlaggedByte $ Json.required object "flagged_byte",+ fmap FlaggedInt $ Json.required object "flagged_int",+ fmap Float $ Json.required object "float",+ fmap GameMode $ Json.required object "game_mode",+ fmap GameServer $ Json.required object "game_server",+ fmap Int $ Json.required object "int",+ fmap Int64 $ Json.required object "int64",+ fmap Loadout $ Json.required object "loadout",+ fmap LoadoutOnline $ Json.required object "loadout_online",+ fmap Loadouts $ Json.required object "loadouts",+ fmap LoadoutsOnline $ Json.required object "loadouts_online",+ fmap Location $ Json.required object "location",+ fmap MusicStinger $ Json.required object "music_stinger",+ fmap PartyLeader $ Json.required object "party_leader",+ fmap Pickup $ Json.required object "pickup",+ fmap PickupInfo $ Json.required object "pickup_info",+ fmap PickupNew $ Json.required object "pickup_new",+ 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",+ fmap Title $ Json.required object "title",+ fmap UniqueId $ Json.required object "unique_id",+ fmap WeldedInfo $ Json.required object "welded_info"+ ]++instance Json.ToJSON AttributeValue where+ toJSON x = case x of+ AppliedDamage y -> Json.object [Json.pair "applied_damage" y]+ Boolean y -> Json.object [Json.pair "boolean" y]+ Boost y -> Json.object [Json.pair "boost" y]+ Byte y -> Json.object [Json.pair "byte" y]+ CamSettings y -> Json.object [Json.pair "cam_settings" y]+ ClubColors y -> Json.object [Json.pair "club_colors" y]+ CustomDemolish y -> Json.object [Json.pair "custom_demolish" y]+ CustomDemolishExtended y -> Json.object [Json.pair "custom_demolish_extended" y]+ DamageState y -> Json.object [Json.pair "damage_state" y]+ Demolish y -> Json.object [Json.pair "demolish" y]+ Enum y -> Json.object [Json.pair "enum" y]+ Explosion y -> Json.object [Json.pair "explosion" y]+ ExtendedExplosion y -> Json.object [Json.pair "extended_explosion" y]+ FlaggedByte y -> Json.object [Json.pair "flagged_byte" y]+ FlaggedInt y -> Json.object [Json.pair "flagged_int" y]+ Float y -> Json.object [Json.pair "float" y]+ GameMode y -> Json.object [Json.pair "game_mode" y]+ GameServer y -> Json.object [Json.pair "game_server" y]+ Int y -> Json.object [Json.pair "int" y]+ Int64 y -> Json.object [Json.pair "int64" y]+ Loadout y -> Json.object [Json.pair "loadout" y]+ LoadoutOnline y -> Json.object [Json.pair "loadout_online" y]+ Loadouts y -> Json.object [Json.pair "loadouts" y]+ LoadoutsOnline y -> Json.object [Json.pair "loadouts_online" y]+ Location y -> Json.object [Json.pair "location" y]+ MusicStinger y -> Json.object [Json.pair "music_stinger" y]+ PartyLeader y -> Json.object [Json.pair "party_leader" y]+ Pickup y -> Json.object [Json.pair "pickup" y]+ PickupInfo y -> Json.object [Json.pair "pickup_info" y]+ PickupNew y -> Json.object [Json.pair "pickup_new" y]+ PlayerHistoryKey y -> Json.object [Json.pair "player_history_key" y]+ 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]+ Title y -> Json.object [Json.pair "title" y]+ UniqueId y -> Json.object [Json.pair "unique_id" y]+ WeldedInfo y -> Json.object [Json.pair "welded_info" y]++schema :: Schema.Schema+schema =+ Schema.named "attribute-value" . Schema.oneOf $+ fmap+ (\(k, v) -> Schema.object [(Json.pair k $ Schema.ref v, True)])+ [ ("applied_damage", AppliedDamage.schema),+ ("boolean", Boolean.schema),+ ("boost", Boost.schema),+ ("byte", Byte.schema),+ ("cam_settings", CamSettings.schema),+ ("club_colors", ClubColors.schema),+ ("custom_demolish", CustomDemolish.schema),+ ("custom_demolish_extended", CustomDemolishExtended.schema),+ ("damage_state", DamageState.schema),+ ("demolish", Demolish.schema),+ ("enum", Enum.schema),+ ("explosion", Explosion.schema),+ ("extended_explosion", ExtendedExplosion.schema),+ ("flagged_byte", FlaggedByte.schema),+ ("flagged_int", FlaggedInt.schema),+ ("float", Float.schema),+ ("game_mode", GameMode.schema),+ ("game_server", GameServer.schema),+ ("int", Int.schema),+ ("int64", Int64.schema),+ ("loadout_online", LoadoutOnline.schema),+ ("loadout", Loadout.schema),+ ("loadouts_online", LoadoutsOnline.schema),+ ("loadouts", Loadouts.schema),+ ("location", Location.schema),+ ("music_stinger", MusicStinger.schema),+ ("party_leader", PartyLeader.schema),+ ("pickup_info", PickupInfo.schema),+ ("pickup_new", PickupNew.schema),+ ("pickup", Pickup.schema),+ ("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),+ ("title", Title.schema),+ ("unique_id", UniqueId.schema),+ ("welded_info", WeldedInfo.schema)+ ]++bitPut :: AttributeValue -> BitPut.BitPut+bitPut value = case value of+ AppliedDamage x -> AppliedDamage.bitPut x+ Boolean x -> Boolean.bitPut x+ Boost x -> Boost.bitPut x+ Byte x -> Byte.bitPut x+ CamSettings x -> CamSettings.bitPut x+ ClubColors x -> ClubColors.bitPut x+ CustomDemolish x -> CustomDemolish.bitPut x+ CustomDemolishExtended x -> CustomDemolishExtended.bitPut x+ DamageState x -> DamageState.bitPut x+ Demolish x -> Demolish.bitPut x+ Enum x -> Enum.bitPut x+ Explosion x -> Explosion.bitPut x+ ExtendedExplosion x -> ExtendedExplosion.bitPut x+ FlaggedInt x -> FlaggedInt.bitPut x+ FlaggedByte x -> FlaggedByte.bitPut x+ Float x -> Float.bitPut x+ GameMode x -> GameMode.bitPut x+ GameServer x -> GameServer.bitPut x+ Int x -> Int.bitPut x+ Int64 x -> Int64.putInt64Attribute x+ Loadout x -> Loadout.bitPut x+ LoadoutOnline x -> LoadoutOnline.bitPut x+ Loadouts x -> Loadouts.bitPut x+ LoadoutsOnline x -> LoadoutsOnline.bitPut x+ Location x -> Location.bitPut x+ MusicStinger x -> MusicStinger.bitPut x+ PartyLeader x -> PartyLeader.bitPut x+ Pickup x -> Pickup.bitPut x+ PickupInfo x -> PickupInfo.bitPut x+ PickupNew x -> PickupNew.bitPut x+ 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+ Title x -> Title.bitPut x+ UniqueId x -> UniqueId.bitPut x+ WeldedInfo x -> WeldedInfo.bitPut x++bitGet ::+ Version.Version ->+ Maybe Str.Str ->+ Map.Map U32.U32 Str.Str ->+ Str.Str ->+ BitGet.BitGet AttributeValue+bitGet version buildVersion objectMap name =+ BitGet.label "AttributeValue" $ do+ constructor <- case Map.lookup (Str.toText name) Data.attributeTypes of+ Nothing ->+ BitGet.throw . UnknownAttribute.UnknownAttribute $ Str.toString name+ Just x -> pure x+ case constructor of+ AttributeType.AppliedDamage ->+ fmap AppliedDamage $ AppliedDamage.bitGet version+ AttributeType.Boolean -> fmap Boolean Boolean.bitGet+ AttributeType.Boost -> fmap Boost Boost.bitGet+ AttributeType.Byte -> fmap Byte Byte.bitGet+ AttributeType.CamSettings ->+ fmap CamSettings $ CamSettings.bitGet version+ AttributeType.ClubColors -> fmap ClubColors ClubColors.bitGet+ AttributeType.CustomDemolish ->+ fmap CustomDemolish $ CustomDemolish.bitGet version+ AttributeType.CustomDemolishExtended -> fmap CustomDemolishExtended $ CustomDemolishExtended.bitGet version+ AttributeType.DamageState ->+ fmap DamageState $ DamageState.bitGet version+ AttributeType.Demolish -> fmap Demolish $ Demolish.bitGet version+ AttributeType.Enum -> fmap Enum Enum.bitGet+ AttributeType.Explosion -> fmap Explosion $ Explosion.bitGet version+ AttributeType.ExtendedExplosion ->+ fmap ExtendedExplosion $ ExtendedExplosion.bitGet version+ AttributeType.FlaggedInt -> fmap FlaggedInt FlaggedInt.bitGet+ AttributeType.FlaggedByte -> fmap FlaggedByte FlaggedByte.bitGet+ AttributeType.Float -> fmap Float Float.bitGet+ AttributeType.GameMode -> fmap GameMode $ GameMode.bitGet version+ AttributeType.GameServer ->+ fmap GameServer $ GameServer.bitGet buildVersion+ AttributeType.Int -> fmap Int Int.bitGet+ AttributeType.Int64 -> fmap Int64 Int64.bitGet+ AttributeType.Loadout -> fmap Loadout Loadout.bitGet+ AttributeType.LoadoutOnline ->+ fmap LoadoutOnline $ LoadoutOnline.bitGet version objectMap+ AttributeType.Loadouts -> fmap Loadouts Loadouts.bitGet+ AttributeType.LoadoutsOnline ->+ fmap LoadoutsOnline $ LoadoutsOnline.bitGet version objectMap+ AttributeType.Location -> fmap Location $ Location.bitGet version+ AttributeType.MusicStinger -> fmap MusicStinger MusicStinger.bitGet+ AttributeType.PartyLeader ->+ fmap PartyLeader $ PartyLeader.bitGet version+ AttributeType.Pickup -> fmap Pickup Pickup.bitGet+ AttributeType.PickupInfo -> fmap PickupInfo PickupInfo.bitGet+ AttributeType.PickupNew -> fmap PickupNew PickupNew.bitGet+ AttributeType.PlayerHistoryKey ->+ fmap PlayerHistoryKey PlayerHistoryKey.bitGet+ 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+ AttributeType.Title -> fmap Title Title.bitGet+ AttributeType.UniqueId -> fmap UniqueId $ UniqueId.bitGet version+ AttributeType.WeldedInfo -> fmap WeldedInfo $ WeldedInfo.bitGet version
@@ -0,0 +1,65 @@+module Rattletrap.Type.Cache where++import qualified Rattletrap.ByteGet as ByteGet+import qualified Rattletrap.BytePut as BytePut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.AttributeMapping as AttributeMapping+import qualified Rattletrap.Type.List as RList+import qualified Rattletrap.Type.U32 as U32+import qualified Rattletrap.Utility.Json as Json++data Cache = Cache+ { classId :: U32.U32,+ parentCacheId :: U32.U32,+ cacheId :: U32.U32,+ attributeMappings :: RList.List AttributeMapping.AttributeMapping+ }+ deriving (Eq, Show)++instance Json.FromJSON Cache where+ parseJSON = Json.withObject "Cache" $ \object -> do+ classId <- Json.required object "class_id"+ parentCacheId <- Json.required object "parent_cache_id"+ cacheId <- Json.required object "cache_id"+ attributeMappings <- Json.required object "attribute_mappings"+ pure Cache {classId, parentCacheId, cacheId, attributeMappings}++instance Json.ToJSON Cache where+ toJSON x =+ Json.object+ [ Json.pair "class_id" $ classId x,+ Json.pair "parent_cache_id" $ parentCacheId x,+ Json.pair "cache_id" $ cacheId x,+ Json.pair "attribute_mappings" $ attributeMappings x+ ]++schema :: Schema.Schema+schema =+ Schema.named "cache" $+ Schema.object+ [ (Json.pair "class_id" $ Schema.ref U32.schema, True),+ (Json.pair "parent_cache_id" $ Schema.ref U32.schema, True),+ (Json.pair "cache_id" $ Schema.ref U32.schema, True),+ ( Json.pair "attribute_mappings" . Schema.json $+ RList.schema+ AttributeMapping.schema,+ True+ )+ ]++bytePut :: Cache -> BytePut.BytePut+bytePut x =+ U32.bytePut (classId x)+ <> U32.bytePut (parentCacheId x)+ <> U32.bytePut (cacheId x)+ <> RList.bytePut AttributeMapping.bytePut (attributeMappings x)++byteGet :: ByteGet.ByteGet Cache+byteGet = ByteGet.label "Cache" $ do+ classId <- ByteGet.label "classId" U32.byteGet+ parentCacheId <- ByteGet.label "parentCacheId" U32.byteGet+ cacheId <- ByteGet.label "cacheId" U32.byteGet+ attributeMappings <-+ ByteGet.label "attributeMappings" $+ RList.byteGet AttributeMapping.byteGet+ pure Cache {classId, parentCacheId, cacheId, attributeMappings}
@@ -0,0 +1,323 @@+module Rattletrap.Type.ClassAttributeMap where++import qualified Data.IntMap as IntMap+import qualified Data.List as List+import qualified Data.Map as Map+import qualified Data.Maybe as Maybe+import qualified Data.Set as Set+import qualified Data.Text as Text+import qualified Data.Tuple as Tuple+import qualified Rattletrap.Data as Data+import qualified Rattletrap.Type.AttributeMapping as AttributeMapping+import qualified Rattletrap.Type.Cache as Cache+import qualified Rattletrap.Type.ClassMapping as ClassMapping+import qualified Rattletrap.Type.CompressedWord as CompressedWord+import qualified Rattletrap.Type.List as RList+import qualified Rattletrap.Type.Str as Str+import qualified Rattletrap.Type.U32 as U32++-- | This data structure holds all the information about classes, objects, and+-- attributes in the replay. The class hierarchy is not fixed; it is encoded+-- in the 'Rattletrap.Content.Content'. Similarly, the attributes that belong+-- to each class are not fixed either. Converting the raw data into a usable+-- structure is tedious; see 'make'.+data ClassAttributeMap = ClassAttributeMap+ { -- | A map from object IDs to their names.+ objectMap :: Map.Map U32.U32 Str.Str,+ -- | A map from object IDs to their class IDs.+ objectClassMap :: Map.Map U32.U32 U32.U32,+ -- | A map from class IDs to a map from attribute stream IDs to attribute+ -- IDs.+ value :: Map.Map U32.U32 (Map.Map U32.U32 U32.U32),+ nameMap :: IntMap.IntMap Str.Str+ }+ deriving (Eq, Show)++type Bimap l r = (Map.Map l r, Map.Map r l)++bimap :: (Ord l, Ord r) => [(l, r)] -> Bimap l r+bimap xs = (Map.fromList xs, Map.fromList (fmap Tuple.swap xs))++lookupL :: (Ord l) => l -> Bimap l r -> Maybe r+lookupL k = Map.lookup k . fst++lookupR :: (Ord r) => r -> Bimap l r -> Maybe l+lookupR k = Map.lookup k . snd++-- | Makes a 'ClassAttributeMap' given the necessary fields from the+-- 'Rattletrap.Content.Content'.+make ::+ -- | From 'Rattletrap.Content.objects'.+ RList.List Str.Str ->+ -- | From 'Rattletrap.Content.classMappings'.+ RList.List ClassMapping.ClassMapping ->+ -- | From 'Rattletrap.Content.caches'.+ RList.List Cache.Cache ->+ -- | From 'Rattletrap.Content.names'.+ RList.List Str.Str ->+ ClassAttributeMap+make objects classMappings caches names =+ let objectMap_ = makeObjectMap objects+ classMap = makeClassMap classMappings+ objectClassMap_ = makeObjectClassMap objectMap_ classMap+ classCache = makeClassCache classMap caches+ attributeMap = makeAttributeMap caches+ classIds = fmap (\(_, classId, _, _) -> classId) classCache+ parentMap = makeParentMap classCache+ value_ =+ Map.fromList+ ( fmap+ ( \classId ->+ let ownAttributes =+ Maybe.fromMaybe Map.empty (Map.lookup classId attributeMap)+ parentsAttributes = case Map.lookup classId parentMap of+ Nothing -> []+ Just parentClassIds ->+ fmap+ ( \parentClassId ->+ Maybe.fromMaybe+ Map.empty+ (Map.lookup parentClassId attributeMap)+ )+ parentClassIds+ attributes = ownAttributes : parentsAttributes+ in (classId, Map.fromList (concatMap Map.toList attributes))+ )+ classIds+ )+ nameMap_ = makeNameMap names+ in ClassAttributeMap objectMap_ objectClassMap_ value_ nameMap_++makeNameMap :: RList.List Str.Str -> IntMap.IntMap Str.Str+makeNameMap names =+ IntMap.fromDistinctAscList (zip [0 ..] (RList.toList names))++getName :: IntMap.IntMap Str.Str -> U32.U32 -> Maybe Str.Str+getName nameMap_ nameIndex =+ IntMap.lookup (fromIntegral (U32.toWord32 nameIndex)) nameMap_++makeObjectClassMap ::+ Map.Map U32.U32 Str.Str ->+ Bimap U32.U32 Str.Str ->+ Map.Map U32.U32 U32.U32+makeObjectClassMap objectMap_ classMap = do+ let objectIds = Map.keys objectMap_+ let classIds = fmap (getClassId objectMap_ classMap) objectIds+ let rawPairs = zip objectIds classIds+ let pairs =+ Maybe.mapMaybe+ ( \(objectId, maybeClassId) -> case maybeClassId of+ Nothing -> Nothing+ Just classId -> Just (objectId, classId)+ )+ rawPairs+ Map.fromList pairs++getClassId ::+ Map.Map U32.U32 Str.Str ->+ Bimap U32.U32 Str.Str ->+ U32.U32 ->+ Maybe U32.U32+getClassId objectMap_ classMap objectId = do+ objectName <- getObjectName objectMap_ objectId+ className <- getClassName objectName+ lookupR className classMap++makeClassCache ::+ Bimap U32.U32 Str.Str ->+ RList.List Cache.Cache ->+ [(Maybe Str.Str, U32.U32, U32.U32, U32.U32)]+makeClassCache classMap caches =+ fmap+ ( \cache ->+ let classId = Cache.classId cache+ in ( lookupL classId classMap,+ classId,+ Cache.cacheId cache,+ Cache.parentCacheId cache+ )+ )+ (RList.toList caches)++makeClassMap :: RList.List ClassMapping.ClassMapping -> Bimap U32.U32 Str.Str+makeClassMap classMappings =+ bimap+ ( fmap+ ( \classMapping ->+ (ClassMapping.streamId classMapping, ClassMapping.name classMapping)+ )+ (RList.toList classMappings)+ )++makeAttributeMap ::+ RList.List Cache.Cache -> Map.Map U32.U32 (Map.Map U32.U32 U32.U32)+makeAttributeMap caches =+ Map.fromList+ ( fmap+ ( \cache ->+ ( Cache.classId cache,+ Map.fromList+ ( fmap+ ( \attributeMapping ->+ ( AttributeMapping.streamId attributeMapping,+ AttributeMapping.objectId attributeMapping+ )+ )+ (RList.toList (Cache.attributeMappings cache))+ )+ )+ )+ (RList.toList caches)+ )++makeShallowParentMap ::+ [(Maybe Str.Str, U32.U32, U32.U32, U32.U32)] -> Map.Map U32.U32 U32.U32+makeShallowParentMap classCache =+ Map.fromList+ ( Maybe.mapMaybe+ ( \xs -> case xs of+ [] -> Nothing+ (maybeClassName, classId, _, parentCacheId) : rest -> do+ parentClassId <- getParentClass maybeClassName parentCacheId rest+ pure (classId, parentClassId)+ )+ (List.tails (reverse classCache))+ )++makeParentMap ::+ [(Maybe Str.Str, U32.U32, U32.U32, U32.U32)] -> Map.Map U32.U32 [U32.U32]+makeParentMap classCache =+ let shallowParentMap = makeShallowParentMap classCache+ in Map.mapWithKey+ (\classId _ -> getParentClasses shallowParentMap classId)+ shallowParentMap++getParentClasses :: Map.Map U32.U32 U32.U32 -> U32.U32 -> [U32.U32]+getParentClasses shallowParentMap classId =+ case Map.lookup classId shallowParentMap of+ Nothing -> []+ Just parentClassId ->+ parentClassId : getParentClasses shallowParentMap parentClassId++getParentClass ::+ Maybe Str.Str ->+ U32.U32 ->+ [(Maybe Str.Str, U32.U32, U32.U32, U32.U32)] ->+ Maybe U32.U32+getParentClass maybeClassName parentCacheId xs = case maybeClassName of+ Nothing -> getParentClassById parentCacheId xs+ Just className -> getParentClassByName className parentCacheId xs++getParentClassById ::+ U32.U32 -> [(Maybe Str.Str, U32.U32, U32.U32, U32.U32)] -> Maybe U32.U32+getParentClassById parentCacheId xs =+ case dropWhile (\(_, _, cacheId, _) -> cacheId /= parentCacheId) xs of+ [] ->+ if parentCacheId == U32.fromWord32 0+ then Nothing+ else+ getParentClassById+ (U32.fromWord32 (U32.toWord32 parentCacheId - 1))+ xs+ (_, parentClassId, _, _) : _ -> Just parentClassId++getParentClassByName ::+ Str.Str ->+ U32.U32 ->+ [(Maybe Str.Str, U32.U32, U32.U32, U32.U32)] ->+ Maybe U32.U32+getParentClassByName className parentCacheId xs =+ case Map.lookup (Str.toText className) Data.parentClasses of+ Nothing -> getParentClassById parentCacheId xs+ Just parentClassName ->+ Maybe.maybe+ (getParentClassById parentCacheId xs)+ Just+ ( Maybe.listToMaybe+ ( fmap+ (\(_, parentClassId, _, _) -> parentClassId)+ ( filter+ (\(_, _, cacheId, _) -> cacheId <= parentCacheId)+ ( filter+ ( \(maybeClassName, _, _, _) ->+ fmap Str.toText maybeClassName == Just parentClassName+ )+ xs+ )+ )+ )+ )++makeObjectMap :: RList.List Str.Str -> Map.Map U32.U32 Str.Str+makeObjectMap objects =+ Map.fromAscList (zip (fmap U32.fromWord32 [0 ..]) (RList.toList objects))++getObjectName :: Map.Map U32.U32 Str.Str -> U32.U32 -> Maybe Str.Str+getObjectName objectMap_ objectId = Map.lookup objectId objectMap_++getClassName :: Str.Str -> Maybe Str.Str+getClassName rawObjectName =+ fmap Str.fromText $+ Map.lookup+ (Str.toText $ normalizeObjectName rawObjectName)+ Data.objectClasses++normalizeObjectName :: Str.Str -> Str.Str+normalizeObjectName objectName =+ let name = Str.toText objectName+ crowdActor = Text.pack "TheWorld:PersistentLevel.CrowdActor_TA"+ crowdManager = Text.pack "TheWorld:PersistentLevel.CrowdManager_TA"+ boostPickup = Text.pack "TheWorld:PersistentLevel.VehiclePickup_Boost_TA"+ mapScoreboard = Text.pack "TheWorld:PersistentLevel.InMapScoreboard_TA"+ breakout = Text.pack "TheWorld:PersistentLevel.BreakOutActor_Platform_TA"+ in if Text.isInfixOf crowdActor name+ then Str.fromText crowdActor+ else+ if Text.isInfixOf crowdManager name+ then Str.fromText crowdManager+ else+ if Text.isInfixOf boostPickup name+ then Str.fromText boostPickup+ else+ if Text.isInfixOf mapScoreboard name+ then Str.fromText mapScoreboard+ else+ if Text.isInfixOf breakout name+ then Str.fromText breakout+ else objectName++classHasLocation :: Str.Str -> Bool+classHasLocation className =+ Set.member (Str.toText className) Data.classesWithLocation++classHasRotation :: Str.Str -> Bool+classHasRotation className =+ Set.member (Str.toText className) Data.classesWithRotation++getAttributeIdLimit :: Map.Map U32.U32 U32.U32 -> Maybe Word+getAttributeIdLimit attributeMap = do+ ((streamId, _), _) <- Map.maxViewWithKey attributeMap+ pure (fromIntegral (U32.toWord32 streamId))++getAttributeName ::+ ClassAttributeMap ->+ Map.Map U32.U32 U32.U32 ->+ CompressedWord.CompressedWord ->+ Maybe Str.Str+getAttributeName classAttributeMap attributeMap streamId = do+ let key = U32.fromWord32 (fromIntegral (CompressedWord.value streamId))+ attributeId <- Map.lookup key attributeMap+ let objectMap_ = objectMap classAttributeMap+ Map.lookup attributeId objectMap_++getAttributeMap ::+ ClassAttributeMap ->+ Map.Map CompressedWord.CompressedWord U32.U32 ->+ CompressedWord.CompressedWord ->+ Maybe (Map.Map U32.U32 U32.U32)+getAttributeMap classAttributeMap actorMap actorId = do+ objectId <- Map.lookup actorId actorMap+ let objectClassMap_ = objectClassMap classAttributeMap+ classId <- Map.lookup objectId objectClassMap_+ let value_ = value classAttributeMap+ Map.lookup classId value_
@@ -0,0 +1,41 @@+module Rattletrap.Type.ClassMapping where++import qualified Rattletrap.ByteGet as ByteGet+import qualified Rattletrap.BytePut as BytePut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.Str as Str+import qualified Rattletrap.Type.U32 as U32+import qualified Rattletrap.Utility.Json as Json++data ClassMapping = ClassMapping+ { name :: Str.Str,+ streamId :: U32.U32+ }+ deriving (Eq, Show)++instance Json.FromJSON ClassMapping where+ parseJSON = Json.withObject "ClassMapping" $ \object -> do+ name <- Json.required object "name"+ streamId <- Json.required object "stream_id"+ pure ClassMapping {name, streamId}++instance Json.ToJSON ClassMapping where+ toJSON x =+ Json.object [Json.pair "name" $ name x, Json.pair "stream_id" $ streamId x]++schema :: Schema.Schema+schema =+ Schema.named "classMapping" $+ Schema.object+ [ (Json.pair "name" $ Schema.ref Str.schema, True),+ (Json.pair "stream_id" $ Schema.ref U32.schema, True)+ ]++bytePut :: ClassMapping -> BytePut.BytePut+bytePut x = Str.bytePut (name x) <> U32.bytePut (streamId x)++byteGet :: ByteGet.ByteGet ClassMapping+byteGet = ByteGet.label "ClassMapping" $ do+ name <- ByteGet.label "name" Str.byteGet+ streamId <- ByteGet.label "streamId" U32.byteGet+ pure ClassMapping {name, streamId}
@@ -0,0 +1,89 @@+module Rattletrap.Type.CompressedWord where++import qualified Data.Bits as Bits+import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Utility.Json as Json++-- | Although there's no guarantee that these values will not overflow, it's+-- exceptionally unlikely. Most 'CompressedWord's are very small.+data CompressedWord = CompressedWord+ { limit :: Word,+ value :: Word+ }+ deriving (Eq, Ord, Show)++instance Json.FromJSON CompressedWord where+ parseJSON = Json.withObject "CompressedWord" $ \object -> do+ limit <- Json.required object "limit"+ value <- Json.required object "value"+ pure CompressedWord {limit, value}++instance Json.ToJSON CompressedWord where+ toJSON x =+ Json.object [Json.pair "limit" $ limit x, Json.pair "value" $ value x]++schema :: Schema.Schema+schema =+ Schema.named "compressedWord" $+ Schema.object+ [ (Json.pair "limit" $ Json.object [Json.pair "type" "integer"], True),+ (Json.pair "value" $ Json.object [Json.pair "type" "integer"], True)+ ]++bitPut :: CompressedWord -> BitPut.BitPut+bitPut compressedWord =+ let limit_ = limit compressedWord+ value_ = value compressedWord+ maxBits = getMaxBits limit_+ in putCompressedWordStep limit_ value_ maxBits 0 0++putCompressedWordStep :: Word -> Word -> Int -> Int -> Word -> BitPut.BitPut+putCompressedWordStep limit_ value_ maxBits position soFar =+ if position < maxBits+ then do+ let x = Bits.shiftL 1 position :: Word+ if maxBits > 1 && position == maxBits - 1 && soFar + x > limit_+ then mempty+ else+ let bit = Bits.testBit value_ position+ delta = if bit then x else 0+ in BitPut.bool bit+ <> putCompressedWordStep+ limit_+ value_+ maxBits+ (position + 1)+ (soFar + delta)+ else mempty++getMaxBits :: Word -> Int+getMaxBits x =+ let n :: Int+ n = max 1 (ceiling (logBase (2 :: Double) (fromIntegral (max 1 x))))+ in if x < 1024 && x == 2 ^ n then n + 1 else n++bitGet :: Word -> BitGet.BitGet CompressedWord+bitGet = bitGetNew++bitGetNew :: Word -> BitGet.BitGet CompressedWord+bitGetNew limit = do+ value <-+ if limit < 1+ then pure 0+ else do+ let numBits =+ max (0 :: Int)+ . subtract 1+ . ceiling+ . logBase (2 :: Double)+ $ fromIntegral limit+ partial <- BitGet.bits numBits+ let next = partial + Bits.shiftL 1 numBits+ if next > limit+ then pure partial+ else do+ x <- BitGet.bool+ pure $ if x then next else partial+ pure CompressedWord {limit, value}
@@ -0,0 +1,50 @@+module Rattletrap.Type.CompressedWordVector where++import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.CompressedWord as CompressedWord+import qualified Rattletrap.Utility.Json as Json++data CompressedWordVector = CompressedWordVector+ { x :: CompressedWord.CompressedWord,+ y :: CompressedWord.CompressedWord,+ z :: CompressedWord.CompressedWord+ }+ deriving (Eq, Show)++instance Json.FromJSON CompressedWordVector where+ parseJSON = Json.withObject "CompressedWordVector" $ \object -> do+ x <- Json.required object "x"+ y <- Json.required object "y"+ z <- Json.required object "z"+ pure CompressedWordVector {x, y, z}++instance Json.ToJSON CompressedWordVector where+ toJSON a =+ Json.object [Json.pair "x" $ x a, Json.pair "y" $ y a, Json.pair "z" $ z a]++schema :: Schema.Schema+schema =+ Schema.named "compressed-word-vector" $+ Schema.object+ [ (Json.pair "x" $ Schema.ref CompressedWord.schema, True),+ (Json.pair "y" $ Schema.ref CompressedWord.schema, True),+ (Json.pair "z" $ Schema.ref CompressedWord.schema, True)+ ]++bitPut :: CompressedWordVector -> BitPut.BitPut+bitPut compressedWordVector =+ CompressedWord.bitPut (x compressedWordVector)+ <> CompressedWord.bitPut (y compressedWordVector)+ <> CompressedWord.bitPut (z compressedWordVector)++bitGet :: BitGet.BitGet CompressedWordVector+bitGet = do+ x <- CompressedWord.bitGet limit+ y <- CompressedWord.bitGet limit+ z <- CompressedWord.bitGet limit+ pure CompressedWordVector {x, y, z}++limit :: Word+limit = 65536
@@ -0,0 +1,247 @@+module Rattletrap.Type.Content where++import qualified Data.ByteString as ByteString+import qualified Data.ByteString.Lazy as LazyByteString+import qualified Data.Word as Word+import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.ByteGet as ByteGet+import qualified Rattletrap.BytePut as BytePut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.Cache as Cache+import qualified Rattletrap.Type.ClassAttributeMap as ClassAttributeMap+import qualified Rattletrap.Type.ClassMapping as ClassMapping+import qualified Rattletrap.Type.Frame as Frame+import qualified Rattletrap.Type.Keyframe as Keyframe+import qualified Rattletrap.Type.List as RList+import qualified Rattletrap.Type.Mark as Mark+import qualified Rattletrap.Type.Message as Message+import qualified Rattletrap.Type.Str as Str+import qualified Rattletrap.Type.U32 as U32+import qualified Rattletrap.Type.U8 as U8+import qualified Rattletrap.Type.Version as Version+import qualified Rattletrap.Utility.Bytes as Bytes+import qualified Rattletrap.Utility.Json as Json++type Content = ContentWith (RList.List Frame.Frame)++-- | Contains low-level game data about a 'Rattletrap.Replay.Replay'.+data ContentWith frames = Content+ { -- | This typically only has one element, like @stadium_oob_audio_map@.+ levels :: RList.List Str.Str,+ -- | A list of which frames are key frames. Although they aren't necessary+ -- for replay, key frames are frames that replicate every actor. They+ -- typically happen once every 10 seconds.+ keyframes :: RList.List Keyframe.Keyframe,+ -- | The size of the stream in bytes. This is only really necessary because+ -- the stream has some arbitrary amount of padding at the end.+ streamSize :: U32.U32,+ -- | The actual game data. This is where all the interesting information is.+ frames :: frames,+ -- | Debugging messages. In newer replays, this is always empty.+ messages :: RList.List Message.Message,+ -- | Tick marks shown on the scrubber when watching a replay.+ marks :: RList.List Mark.Mark,+ -- | A list of @.upk@ files to load, like+ -- @..\\..\\TAGame\\CookedPCConsole\\Stadium_P.upk@.+ packages :: RList.List Str.Str,+ -- | Objects in the stream. Used for the+ -- 'Rattletrap.Type.ClassAttributeMap.ClassAttributeMap'.+ objects :: RList.List Str.Str,+ -- | It's not clear what these are used for. This list is usually not empty,+ -- but appears unused otherwise.+ names :: RList.List Str.Str,+ -- | A mapping between classes and their ID in the stream. Used for the+ -- 'Rattletrap.Type.ClassAttributeMap.ClassAttributeMap'.+ classMappings :: RList.List ClassMapping.ClassMapping,+ -- | A list of classes along with their parent classes and attributes. Used+ -- for the 'Rattletrap.Type.ClassAttributeMap.ClassAttributeMap'.+ caches :: RList.List Cache.Cache,+ unknown :: [Word.Word8]+ }+ deriving (Eq, Show)++instance (Json.FromJSON frames) => Json.FromJSON (ContentWith frames) where+ parseJSON = Json.withObject "Content" $ \object -> do+ levels <- Json.required object "levels"+ keyframes <- Json.required object "key_frames"+ streamSize <- Json.required object "stream_size"+ frames <- Json.required object "frames"+ messages <- Json.required object "messages"+ marks <- Json.required object "marks"+ packages <- Json.required object "packages"+ objects <- Json.required object "objects"+ names <- Json.required object "names"+ classMappings <- Json.required object "class_mappings"+ caches <- Json.required object "caches"+ unknown <- Json.required object "unknown"+ pure+ Content+ { levels,+ keyframes,+ streamSize,+ frames,+ messages,+ marks,+ packages,+ objects,+ names,+ classMappings,+ caches,+ unknown+ }++instance (Json.ToJSON frames) => Json.ToJSON (ContentWith frames) where+ toJSON x =+ Json.object+ [ Json.pair "levels" $ levels x,+ Json.pair "key_frames" $ keyframes x,+ Json.pair "stream_size" $ streamSize x,+ Json.pair "frames" $ frames x,+ Json.pair "messages" $ messages x,+ Json.pair "marks" $ marks x,+ Json.pair "packages" $ packages x,+ Json.pair "objects" $ objects x,+ Json.pair "names" $ names x,+ Json.pair "class_mappings" $ classMappings x,+ Json.pair "caches" $ caches x,+ Json.pair "unknown" $ unknown x+ ]++schema :: Schema.Schema -> Schema.Schema+schema s =+ Schema.named "content" $+ Schema.object+ [ (Json.pair "levels" . Schema.json $ RList.schema Str.schema, True),+ (Json.pair "key_frames" . Schema.json $ RList.schema Keyframe.schema, True),+ (Json.pair "stream_size" $ Schema.ref U32.schema, True),+ (Json.pair "frames" $ Schema.json s, True),+ (Json.pair "messages" . Schema.json $ RList.schema Message.schema, True),+ (Json.pair "marks" . Schema.json $ RList.schema Mark.schema, True),+ (Json.pair "packages" . Schema.json $ RList.schema Str.schema, True),+ (Json.pair "objects" . Schema.json $ RList.schema Str.schema, True),+ (Json.pair "names" . Schema.json $ RList.schema Str.schema, True),+ ( Json.pair "class_mappings" . Schema.json $+ RList.schema+ ClassMapping.schema,+ True+ ),+ (Json.pair "caches" . Schema.json $ RList.schema Cache.schema, True),+ (Json.pair "unknown" . Schema.json $ Schema.array U8.schema, True)+ ]++empty :: Content+empty =+ Content+ { levels = RList.empty,+ keyframes = RList.empty,+ streamSize = U32.fromWord32 0,+ frames = RList.empty,+ messages = RList.empty,+ marks = RList.empty,+ packages = RList.empty,+ objects = RList.empty,+ names = RList.empty,+ classMappings = RList.empty,+ caches = RList.empty,+ unknown = []+ }++bytePut :: Content -> BytePut.BytePut+bytePut x =+ RList.bytePut Str.bytePut (levels x)+ <> RList.bytePut Keyframe.bytePut (keyframes x)+ <> putFrames x+ <> RList.bytePut Message.bytePut (messages x)+ <> RList.bytePut Mark.bytePut (marks x)+ <> RList.bytePut Str.bytePut (packages x)+ <> RList.bytePut Str.bytePut (objects x)+ <> RList.bytePut Str.bytePut (names x)+ <> RList.bytePut ClassMapping.bytePut (classMappings x)+ <> RList.bytePut Cache.bytePut (caches x)+ <> foldMap BytePut.word8 (unknown x)++putFrames :: Content -> BytePut.BytePut+putFrames x =+ let stream =+ BytePut.toByteString . BitPut.toBytePut . Frame.putFrames $ frames x+ -- This is a little strange. When parsing a binary replay, the stream size+ -- is given before the stream itself. When generating the JSON, the stream+ -- size is included. That allows a bit-for-bit identical binary replay to+ -- be generated from the JSON. However if you modify the JSON before+ -- converting it back into binary, the stream size might be different.+ --+ -- If it was possible to know how much padding the stream required without+ -- carrying it along as extra data on the side, this logic could go away.+ -- Unforunately that isn't currently known. See this issue for details:+ -- <https://github.com/tfausak/rattletrap/issues/171>.+ expectedStreamSize = streamSize x+ actualStreamSize =+ U32.fromWord32 . fromIntegral $ ByteString.length stream+ streamSize_ =+ U32.fromWord32 $+ max (U32.toWord32 expectedStreamSize) (U32.toWord32 actualStreamSize)+ in U32.bytePut streamSize_+ <> BytePut.byteString (Bytes.padBytes (U32.toWord32 streamSize_) stream)++byteGet ::+ Maybe Str.Str ->+ -- | Version numbers, usually from 'Rattletrap.Header.getVersion'.+ Version.Version ->+ -- | The number of frames in the stream, usually from+ -- 'Rattletrap.Header.getNumFrames'.+ Int ->+ -- | The maximum number of channels in the stream, usually from+ -- 'Rattletrap.Header.getMaxChannels'.+ Word ->+ -- | 'Rattletrap.Header.getBuildVersion'+ Maybe Str.Str ->+ ByteGet.ByteGet Content+byteGet matchType version numFrames maxChannels buildVersion =+ ByteGet.label "Content" $ do+ levels <- ByteGet.label "levels" $ RList.byteGet Str.byteGet+ keyframes <- ByteGet.label "keyframes" $ RList.byteGet Keyframe.byteGet+ streamSize <- ByteGet.label "streamSize" U32.byteGet+ stream <-+ ByteGet.label "stream" . ByteGet.byteString . fromIntegral $+ U32.toWord32+ streamSize+ messages <- ByteGet.label "messages" $ RList.byteGet Message.byteGet+ marks <- ByteGet.label "marks" $ RList.byteGet Mark.byteGet+ packages <- ByteGet.label "packages" $ RList.byteGet Str.byteGet+ objects <- ByteGet.label "objects" $ RList.byteGet Str.byteGet+ names <- ByteGet.label "names" $ RList.byteGet Str.byteGet+ classMappings <-+ ByteGet.label "classMappings" $+ RList.byteGet ClassMapping.byteGet+ caches <- ByteGet.label "caches" $ RList.byteGet Cache.byteGet+ let classAttributeMap =+ ClassAttributeMap.make objects classMappings caches names+ getFrames =+ BitGet.toByteGet $+ Frame.decodeFramesBits+ matchType+ version+ buildVersion+ numFrames+ maxChannels+ classAttributeMap+ frames <- ByteGet.label "frames" $ ByteGet.embed getFrames stream+ unknown <-+ ByteGet.label "unknown" $+ fmap LazyByteString.unpack ByteGet.remaining+ pure+ Content+ { levels,+ keyframes,+ streamSize,+ frames,+ messages,+ marks,+ packages,+ objects,+ names,+ classMappings,+ caches,+ unknown+ }
@@ -0,0 +1,76 @@+module Rattletrap.Type.Dictionary where++import qualified Data.Text as Text+import qualified Rattletrap.ByteGet as ByteGet+import qualified Rattletrap.BytePut as BytePut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.List as RList+import qualified Rattletrap.Type.Str as Str+import qualified Rattletrap.Utility.Json as Json++data Dictionary a = Dictionary+ { elements :: RList.List (Str.Str, a),+ lastKey :: Str.Str+ }+ deriving (Eq, Show)++instance (Json.FromJSON a) => Json.FromJSON (Dictionary a) where+ parseJSON = Json.withObject "Dictionary" $ \o -> do+ elements <- Json.required o "elements"+ lastKey <- Json.required o "last_key"+ pure Dictionary {elements = elements, lastKey = lastKey}++instance (Json.ToJSON a) => Json.ToJSON (Dictionary a) where+ toJSON x =+ Json.object+ [ Json.pair "elements" . RList.toList $ elements x,+ Json.pair "last_key" $ lastKey x+ ]++schema :: Schema.Schema -> Schema.Schema+schema s =+ Schema.named ("dictionary-" <> Text.unpack (Schema.name s)) $+ Schema.object+ [ (Json.pair "elements" . Schema.json . Schema.array $ elementSchema s, True),+ (Json.pair "last_key" $ Schema.ref Str.schema, True)+ ]++elementSchema :: Schema.Schema -> Schema.Schema+elementSchema s =+ Schema.named ("dictionary-element-" <> Text.unpack (Schema.name s)) $+ Schema.tuple+ [ Schema.ref Str.schema,+ Schema.ref s+ ]++lookup :: Str.Str -> Dictionary a -> Maybe a+lookup k = Prelude.lookup k . RList.toList . elements++bytePut :: (a -> BytePut.BytePut) -> Dictionary a -> BytePut.BytePut+bytePut f x =+ foldMap (\(k, v) -> Str.bytePut k <> f v) (RList.toList $ elements x)+ <> Str.bytePut (lastKey x)++byteGet :: ByteGet.ByteGet a -> ByteGet.ByteGet (Dictionary a)+byteGet = ByteGet.label "Dictionary" . byteGetWith 0 []++byteGetWith ::+ Int ->+ [(Int, (Str.Str, a))] ->+ ByteGet.ByteGet a ->+ ByteGet.ByteGet (Dictionary a)+byteGetWith i xs f = do+ k <- ByteGet.label ("key (" <> show i <> ")") Str.byteGet+ if isNone k+ then+ pure+ Dictionary+ { elements = RList.fromList . reverse $ fmap snd xs,+ lastKey = k+ }+ else do+ v <- ByteGet.label ("value (" <> Str.toString k <> ")") f+ byteGetWith (i + 1) ((i, (k, v)) : xs) f++isNone :: Str.Str -> Bool+isNone = (== Text.pack "None") . Text.filter (/= '\x00') . Str.toText
@@ -0,0 +1,39 @@+module Rattletrap.Type.F32 where++import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.ByteGet as ByteGet+import qualified Rattletrap.BytePut as BytePut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Utility.Json as Json++newtype F32+ = F32 Float+ deriving (Eq, Show)++instance Json.FromJSON F32 where+ parseJSON = fmap fromFloat . Json.parseJSON++instance Json.ToJSON F32 where+ toJSON = Json.toJSON . toFloat++schema :: Schema.Schema+schema = Schema.named "f32" $ Json.object [Json.pair "type" "number"]++fromFloat :: Float -> F32+fromFloat = F32++toFloat :: F32 -> Float+toFloat (F32 x) = x++bytePut :: F32 -> BytePut.BytePut+bytePut = BytePut.float . toFloat++bitPut :: F32 -> BitPut.BitPut+bitPut = BitPut.fromBytePut . bytePut++byteGet :: ByteGet.ByteGet F32+byteGet = ByteGet.label "F32" $ fmap fromFloat ByteGet.float++bitGet :: BitGet.BitGet F32+bitGet = BitGet.fromByteGet byteGet 4
@@ -0,0 +1,140 @@+module Rattletrap.Type.Frame where++import qualified Data.Map as Map+import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.ClassAttributeMap as ClassAttributeMap+import qualified Rattletrap.Type.CompressedWord as CompressedWord+import qualified Rattletrap.Type.F32 as F32+import qualified Rattletrap.Type.List as RList+import qualified Rattletrap.Type.Replication as Replication+import qualified Rattletrap.Type.Str as Str+import qualified Rattletrap.Type.U32 as U32+import qualified Rattletrap.Type.Version as Version+import qualified Rattletrap.Utility.Json as Json++data Frame = Frame+ { -- | Time in seconds since the beginning of the match.+ time :: F32.F32,+ -- | Time in seconds since the last frame. Usually about 0.03 since there+ -- are 30 frames per second.+ delta :: F32.F32,+ replications :: RList.List Replication.Replication+ }+ deriving (Eq, Show)++instance Json.FromJSON Frame where+ parseJSON = Json.withObject "Frame" $ \object -> do+ time <- Json.required object "time"+ delta <- Json.required object "delta"+ replications <- Json.required object "replications"+ pure Frame {time, delta, replications}++instance Json.ToJSON Frame where+ toJSON x =+ Json.object+ [ Json.pair "time" $ time x,+ Json.pair "delta" $ delta x,+ Json.pair "replications" $ replications x+ ]++schema :: Schema.Schema+schema =+ Schema.named "frame" $+ Schema.object+ [ (Json.pair "time" $ Schema.ref F32.schema, True),+ (Json.pair "delta" $ Schema.ref F32.schema, True),+ ( Json.pair "replications" . Schema.json $ RList.schema Replication.schema,+ True+ )+ ]++putFrames :: RList.List Frame -> BitPut.BitPut+putFrames = foldMap bitPut . RList.toList++bitPut :: Frame -> BitPut.BitPut+bitPut frame =+ F32.bitPut (time frame)+ <> F32.bitPut (delta frame)+ <> Replication.putReplications (replications frame)++decodeFramesBits ::+ Maybe Str.Str ->+ Version.Version ->+ Maybe Str.Str ->+ Int ->+ Word ->+ ClassAttributeMap.ClassAttributeMap ->+ BitGet.BitGet (RList.List Frame)+decodeFramesBits matchType version buildVersion count limit classes =+ fmap snd $+ decodeFramesBitsWith+ matchType+ version+ buildVersion+ count+ limit+ classes+ Map.empty+ 0+ []++decodeFramesBitsWith ::+ Maybe Str.Str ->+ Version.Version ->+ Maybe Str.Str ->+ Int ->+ Word ->+ ClassAttributeMap.ClassAttributeMap ->+ Map.Map CompressedWord.CompressedWord U32.U32 ->+ Int ->+ [Frame] ->+ BitGet.BitGet+ ( Map.Map+ CompressedWord.CompressedWord+ U32.U32,+ RList.List Frame+ )+decodeFramesBitsWith matchType version buildVersion count limit classes actorMap index frames =+ if index >= count+ then pure (actorMap, RList.fromList $ reverse frames)+ else do+ (newActorMap, frame) <-+ BitGet.label ("element (" <> show index <> ")") $+ bitGet matchType version buildVersion limit classes actorMap+ decodeFramesBitsWith+ matchType+ version+ buildVersion+ count+ limit+ classes+ newActorMap+ (index + 1)+ $ frame+ : frames++bitGet ::+ Maybe Str.Str ->+ Version.Version ->+ Maybe Str.Str ->+ Word ->+ ClassAttributeMap.ClassAttributeMap ->+ Map.Map CompressedWord.CompressedWord U32.U32 ->+ BitGet.BitGet+ (Map.Map CompressedWord.CompressedWord U32.U32, Frame)+bitGet matchType version buildVersion limit classes actorMap =+ BitGet.label "Frame" $ do+ time <- BitGet.label "time" F32.bitGet+ delta <- BitGet.label "delta" F32.bitGet+ (newActorMap, replications) <-+ BitGet.label "replications" $+ Replication.decodeReplicationsBits+ matchType+ version+ buildVersion+ limit+ classes+ actorMap+ pure (newActorMap, Frame {time, delta, replications})
@@ -0,0 +1,113 @@+module Rattletrap.Type.Header where++import qualified Rattletrap.ByteGet as ByteGet+import qualified Rattletrap.BytePut as BytePut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.Dictionary as Dictionary+import qualified Rattletrap.Type.Property as Property+import qualified Rattletrap.Type.Str as Str+import qualified Rattletrap.Type.U32 as U32+import qualified Rattletrap.Type.Version as Version+import qualified Rattletrap.Utility.Json as Json++-- | Contains high-level metadata about a 'Rattletrap.Replay.Replay'.+data Header = Header+ { version :: Version.Version,+ -- | Always @TAGame.Replay_Soccar_TA@.+ label :: Str.Str,+ -- | These properties determine how a replay will look in the list of+ -- replays in-game. One element is required for the replay to show up:+ --+ -- - MapName: This is a 'Rattletrap.PropertyValue.NameProperty' with a+ -- case-insensitive map identifier, like @Stadium_P@.+ --+ -- There are many other properties that affect how the replay looks in the+ -- list of replays.+ --+ -- - Date: A 'Rattletrap.PropertyValue.StrProperty' with the format+ -- @YYYY-mm-dd:HH-MM@. Dates are not validated, but the month must be+ -- between 1 and 12 to show up. The hour is shown modulo 12 with AM or PM.+ -- - MatchType: A 'Rattletrap.PropertyValue.NameProperty'. If this is not+ -- one of the expected values, nothing will be shown next to the replay's+ -- map. The expected values are: @Online@, @Offline@, @Private@, and+ -- @Season@.+ -- - NumFrames: This 'Rattletrap.PropertyValue.IntProperty' is used to+ -- calculate the length of the match. There are 30 frames per second,+ -- a typical 5-minute match has about 9,000 frames.+ -- - PrimaryPlayerTeam: This is an 'Rattletrap.PropertyValue.IntProperty'.+ -- It is either 0 (blue) or 1 (orange). Any other value is ignored. If+ -- this would be 0, you don't have to set it at all.+ -- - ReplayName: An optional 'Rattletrap.PropertyValue.StrProperty' with a+ -- user-supplied name for the replay.+ -- - Team0Score: The blue team's score as an+ -- 'Rattletrap.PropertyValue.IntProperty'. Can be omitted if the score is+ -- 0.+ -- - Team1Score: The orange team's score as an+ -- 'Rattletrap.PropertyValue.IntProperty'. Can also be omitted if the+ -- score is 0.+ -- - TeamSize: An 'Rattletrap.PropertyValue.IntProperty' with the number of+ -- players per team. This value is not validated, so you can put absurd+ -- values like 99. To get an "unfair" team size like 1v4, you must set the+ -- bUnfairBots 'Rattletrap.PropertyValue.BoolProperty' to @True@.+ properties :: Dictionary.Dictionary Property.Property+ }+ deriving (Eq, Show)++instance Json.FromJSON Header where+ parseJSON = Json.withObject "Header" $ \object -> do+ major <- Json.required object "engine_version"+ minor <- Json.required object "licensee_version"+ patch <- Json.optional object "patch_version"+ label <- Json.required object "label"+ properties <- Json.required object "properties"+ pure+ Header+ { version =+ Version.Version+ { Version.major,+ Version.minor,+ Version.patch+ },+ label,+ properties+ }++instance Json.ToJSON Header where+ toJSON x =+ Json.object+ [ Json.pair "engine_version" . Version.major $ version x,+ Json.pair "licensee_version" . Version.minor $ version x,+ Json.pair "patch_version" . Version.patch $ version x,+ Json.pair "label" $ label x,+ Json.pair "properties" $ properties x+ ]++schema :: Schema.Schema+schema =+ Schema.named "header" $+ Schema.object+ [ (Json.pair "engine_version" $ Schema.ref U32.schema, True),+ (Json.pair "licensee_version" $ Schema.ref U32.schema, True),+ (Json.pair "patch_version" . Schema.json $ Schema.maybe U32.schema, False),+ (Json.pair "label" $ Schema.ref Str.schema, True),+ ( Json.pair "properties" . Schema.json $ Dictionary.schema Property.schema,+ True+ )+ ]++bytePut :: Header -> BytePut.BytePut+bytePut x =+ Version.bytePut (version x)+ <> Str.bytePut (label x)+ <> Dictionary.bytePut+ Property.bytePut+ (properties x)++byteGet :: ByteGet.ByteGet Header+byteGet = ByteGet.label "Header" $ do+ version <- ByteGet.label "version" Version.byteGet+ label <- ByteGet.label "label" Str.byteGet+ properties <-+ ByteGet.label "properties" $+ Dictionary.byteGet Property.byteGet+ pure Header {version, label, properties}
@@ -0,0 +1,46 @@+module Rattletrap.Type.I32 where++import qualified Data.Int as Int+import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.ByteGet as ByteGet+import qualified Rattletrap.BytePut as BytePut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Utility.Json as Json++newtype I32+ = I32 Int.Int32+ deriving (Eq, Show)++instance Json.FromJSON I32 where+ parseJSON = fmap fromInt32 . Json.parseJSON++instance Json.ToJSON I32 where+ toJSON = Json.toJSON . toInt32++schema :: Schema.Schema+schema =+ Schema.named "i32" $+ Json.object+ [ Json.pair "type" "integer",+ Json.pair "minimum" (minBound :: Int.Int32),+ Json.pair "maximum" (maxBound :: Int.Int32)+ ]++fromInt32 :: Int.Int32 -> I32+fromInt32 = I32++toInt32 :: I32 -> Int.Int32+toInt32 (I32 x) = x++bytePut :: I32 -> BytePut.BytePut+bytePut = BytePut.int32 . toInt32++bitPut :: I32 -> BitPut.BitPut+bitPut = BitPut.fromBytePut . bytePut++byteGet :: ByteGet.ByteGet I32+byteGet = ByteGet.label "I32" $ fmap fromInt32 ByteGet.int32++bitGet :: BitGet.BitGet I32+bitGet = BitGet.fromByteGet byteGet 4
@@ -0,0 +1,48 @@+module Rattletrap.Type.I64 where++import qualified Data.Int as Int+import qualified Data.Text as Text+import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.ByteGet as ByteGet+import qualified Rattletrap.BytePut as BytePut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Utility.Json as Json+import qualified Text.Read as Read++newtype I64+ = I64 Int.Int64+ deriving (Eq, Show)++instance Json.FromJSON I64 where+ parseJSON =+ Json.withText "I64" $+ either fail (pure . fromInt64)+ . Read.readEither+ . Text.unpack++instance Json.ToJSON I64 where+ toJSON = Json.toJSON . show . toInt64++schema :: Schema.Schema+schema =+ Schema.named "i64" $+ Json.object [Json.pair "type" "string", Json.pair "pattern" "^-?[0-9]+$"]++fromInt64 :: Int.Int64 -> I64+fromInt64 = I64++toInt64 :: I64 -> Int.Int64+toInt64 (I64 x) = x++bytePut :: I64 -> BytePut.BytePut+bytePut = BytePut.int64 . toInt64++bitPut :: I64 -> BitPut.BitPut+bitPut = BitPut.fromBytePut . bytePut++byteGet :: ByteGet.ByteGet I64+byteGet = fmap fromInt64 ByteGet.int64++bitGet :: BitGet.BitGet I64+bitGet = BitGet.fromByteGet byteGet 8
@@ -0,0 +1,46 @@+module Rattletrap.Type.I8 where++import qualified Data.Int as Int+import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.ByteGet as ByteGet+import qualified Rattletrap.BytePut as BytePut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Utility.Json as Json++newtype I8+ = I8 Int.Int8+ deriving (Eq, Show)++instance Json.FromJSON I8 where+ parseJSON = fmap fromInt8 . Json.parseJSON++instance Json.ToJSON I8 where+ toJSON = Json.toJSON . toInt8++schema :: Schema.Schema+schema =+ Schema.named "i8" $+ Json.object+ [ Json.pair "type" "integer",+ Json.pair "minimum" (minBound :: Int.Int8),+ Json.pair "maximum" (maxBound :: Int.Int8)+ ]++fromInt8 :: Int.Int8 -> I8+fromInt8 = I8++toInt8 :: I8 -> Int.Int8+toInt8 (I8 x) = x++bytePut :: I8 -> BytePut.BytePut+bytePut = BytePut.int8 . toInt8++bitPut :: I8 -> BitPut.BitPut+bitPut = BitPut.fromBytePut . bytePut++byteGet :: ByteGet.ByteGet I8+byteGet = ByteGet.label "I8" $ fmap fromInt8 ByteGet.int8++bitGet :: BitGet.BitGet I8+bitGet = BitGet.fromByteGet byteGet 1
@@ -0,0 +1,56 @@+module Rattletrap.Type.Initialization 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.Type.Vector as Vector+import qualified Rattletrap.Type.Version as Version+import qualified Rattletrap.Utility.Json as Json+import qualified Rattletrap.Utility.Monad as Monad++data Initialization = Initialization+ { -- | Not every class has an initial location. See+ -- 'Rattletrap.Data.classesWithLocation'.+ location :: Maybe Vector.Vector,+ -- | Only classes with location can have rotation, but not every one does.+ -- See 'Rattletrap.Data.classesWithRotation'.+ rotation :: Maybe Int8Vector.Int8Vector+ }+ deriving (Eq, Show)++instance Json.FromJSON Initialization where+ parseJSON = Json.withObject "Initialization" $ \object -> do+ location <- Json.optional object "location"+ rotation <- Json.optional object "rotation"+ pure Initialization {location, rotation}++instance Json.ToJSON Initialization where+ toJSON x =+ Json.object+ [Json.pair "location" $ location x, Json.pair "rotation" $ rotation x]++schema :: Schema.Schema+schema =+ Schema.named "initialization" $+ Schema.object+ [ (Json.pair "location" . Schema.json $ Schema.maybe Vector.schema, False),+ ( Json.pair "rotation" . Schema.json $ Schema.maybe Int8Vector.schema,+ False+ )+ ]++bitPut :: Initialization -> BitPut.BitPut+bitPut initialization =+ foldMap Vector.bitPut (location initialization)+ <> foldMap Int8Vector.bitPut (rotation initialization)++bitGet :: Version.Version -> Bool -> Bool -> BitGet.BitGet Initialization+bitGet version hasLocation hasRotation = BitGet.label "Initialization" $ do+ location <-+ BitGet.label "location" $+ Monad.whenMaybe hasLocation (Vector.bitGet version)+ rotation <-+ BitGet.label "rotation" $+ Monad.whenMaybe hasRotation Int8Vector.bitGet+ pure Initialization {location, rotation}
@@ -0,0 +1,58 @@+module Rattletrap.Type.Int8Vector where++import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.I8 as I8+import qualified Rattletrap.Utility.Json as Json+import qualified Rattletrap.Utility.Monad as Monad++data Int8Vector = Int8Vector+ { x :: Maybe I8.I8,+ y :: Maybe I8.I8,+ z :: Maybe I8.I8+ }+ deriving (Eq, Show)++instance Json.FromJSON Int8Vector where+ parseJSON = Json.withObject "Int8Vector" $ \object -> do+ x <- Json.optional object "x"+ y <- Json.optional object "y"+ z <- Json.optional object "z"+ pure Int8Vector {x, y, z}++instance Json.ToJSON Int8Vector where+ toJSON a =+ Json.object [Json.pair "x" $ x a, Json.pair "y" $ y a, Json.pair "z" $ z a]++schema :: Schema.Schema+schema =+ Schema.named "int8Vector" $+ Schema.object+ [ (Json.pair "x" . Schema.json $ Schema.maybe I8.schema, False),+ (Json.pair "y" . Schema.json $ Schema.maybe I8.schema, False),+ (Json.pair "z" . Schema.json $ Schema.maybe I8.schema, False)+ ]++bitPut :: Int8Vector -> BitPut.BitPut+bitPut int8Vector =+ putInt8VectorField (x int8Vector)+ <> putInt8VectorField (y int8Vector)+ <> putInt8VectorField (z int8Vector)++putInt8VectorField :: Maybe I8.I8 -> BitPut.BitPut+putInt8VectorField maybeField = case maybeField of+ Nothing -> BitPut.bool False+ Just field -> BitPut.bool True <> I8.bitPut field++bitGet :: BitGet.BitGet Int8Vector+bitGet = BitGet.label "Int8Vector" $ do+ x <- BitGet.label "x" decodeFieldBits+ y <- BitGet.label "y" decodeFieldBits+ z <- BitGet.label "z" decodeFieldBits+ pure Int8Vector {x, y, z}++decodeFieldBits :: BitGet.BitGet (Maybe I8.I8)+decodeFieldBits = do+ hasField <- BitGet.bool+ Monad.whenMaybe hasField I8.bitGet
@@ -0,0 +1,53 @@+module Rattletrap.Type.Keyframe where++import qualified Rattletrap.ByteGet as ByteGet+import qualified Rattletrap.BytePut as BytePut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.F32 as F32+import qualified Rattletrap.Type.U32 as U32+import qualified Rattletrap.Utility.Json as Json++data Keyframe = Keyframe+ { -- | When this key frame occurs, in seconds.+ time :: F32.F32,+ -- | The frame number of this key frame, starting from 0.+ frame :: U32.U32,+ -- | The bit position of this key frame in the stream.+ position :: U32.U32+ }+ deriving (Eq, Show)++instance Json.FromJSON Keyframe where+ parseJSON = Json.withObject "Keyframe" $ \object -> do+ time <- Json.required object "time"+ frame <- Json.required object "frame"+ position <- Json.required object "position"+ pure Keyframe {time, frame, position}++instance Json.ToJSON Keyframe where+ toJSON x =+ Json.object+ [ Json.pair "time" $ time x,+ Json.pair "frame" $ frame x,+ Json.pair "position" $ position x+ ]++schema :: Schema.Schema+schema =+ Schema.named "keyframe" $+ Schema.object+ [ (Json.pair "time" $ Schema.ref F32.schema, True),+ (Json.pair "frame" $ Schema.ref U32.schema, True),+ (Json.pair "position" $ Schema.ref U32.schema, True)+ ]++bytePut :: Keyframe -> BytePut.BytePut+bytePut x =+ F32.bytePut (time x) <> U32.bytePut (frame x) <> U32.bytePut (position x)++byteGet :: ByteGet.ByteGet Keyframe+byteGet = ByteGet.label "Keyframe" $ do+ time <- ByteGet.label "time" F32.byteGet+ frame <- ByteGet.label "frame" U32.byteGet+ position <- ByteGet.label "position" U32.byteGet+ pure Keyframe {time, frame, position}
@@ -0,0 +1,57 @@+module Rattletrap.Type.List where++import qualified Control.Monad as Monad+import qualified Rattletrap.ByteGet as ByteGet+import qualified Rattletrap.BytePut as BytePut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.U32 as U32+import qualified Rattletrap.Utility.Json as Json++newtype List a+ = List [a]+ deriving (Eq, Show)++instance (Json.FromJSON a) => Json.FromJSON (List a) where+ parseJSON = fmap fromList . Json.parseJSON++instance (Json.ToJSON a) => Json.ToJSON (List a) where+ toJSON = Json.toJSON . toList++schema :: Schema.Schema -> Schema.Schema+schema = Schema.array++fromList :: [a] -> List a+fromList = List++empty :: List a+empty = fromList []++toList :: List a -> [a]+toList (List x) = x++bytePut :: (a -> BytePut.BytePut) -> List a -> BytePut.BytePut+bytePut f x =+ let v = toList x+ in (U32.bytePut . U32.fromWord32 . fromIntegral $ length v) <> foldMap f v++byteGet :: ByteGet.ByteGet a -> ByteGet.ByteGet (List a)+byteGet f = ByteGet.label "List" $ do+ size <- ByteGet.label "size" U32.byteGet+ generateM (fromIntegral $ U32.toWord32 size) $+ \i -> ByteGet.label ("element (" <> show i <> ")") f++generateM :: (Monad m) => Int -> (Int -> m a) -> m (List a)+generateM n f = fmap fromList $ mapM f [0 .. n - 1]++replicateM :: (Monad m) => Int -> m a -> m (List a)+replicateM n = fmap fromList . Monad.replicateM n++untilM :: (Monad m) => m (Maybe a) -> m (List a)+untilM f = untilMWith f 0 []++untilMWith :: (Monad m) => m (Maybe a) -> Int -> [(Int, a)] -> m (List a)+untilMWith f i xs = do+ m <- f+ case m of+ Nothing -> pure . fromList . reverse $ fmap snd xs+ Just x -> untilMWith f (i + 1) ((i, x) : xs)
@@ -0,0 +1,43 @@+module Rattletrap.Type.Mark where++import qualified Rattletrap.ByteGet as ByteGet+import qualified Rattletrap.BytePut as BytePut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.Str as Str+import qualified Rattletrap.Type.U32 as U32+import qualified Rattletrap.Utility.Json as Json++data Mark = Mark+ { -- | Which type of mark this is, like @Team0Goal@.+ value :: Str.Str,+ -- | Which frame this mark belongs to, starting from 0.+ frame :: U32.U32+ }+ deriving (Eq, Show)++instance Json.FromJSON Mark where+ parseJSON = Json.withObject "Mark" $ \object -> do+ value <- Json.required object "value"+ frame <- Json.required object "frame"+ pure Mark {value, frame}++instance Json.ToJSON Mark where+ toJSON x =+ Json.object [Json.pair "value" $ value x, Json.pair "frame" $ frame x]++schema :: Schema.Schema+schema =+ Schema.named "mark" $+ Schema.object+ [ (Json.pair "value" $ Schema.ref Str.schema, True),+ (Json.pair "frame" $ Schema.ref U32.schema, True)+ ]++bytePut :: Mark -> BytePut.BytePut+bytePut x = Str.bytePut (value x) <> U32.bytePut (frame x)++byteGet :: ByteGet.ByteGet Mark+byteGet = ByteGet.label "Mark" $ do+ value <- ByteGet.label "value" Str.byteGet+ frame <- ByteGet.label "frame" U32.byteGet+ pure Mark {value, frame}
@@ -0,0 +1,53 @@+module Rattletrap.Type.Message where++import qualified Rattletrap.ByteGet as ByteGet+import qualified Rattletrap.BytePut as BytePut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.Str as Str+import qualified Rattletrap.Type.U32 as U32+import qualified Rattletrap.Utility.Json as Json++data Message = Message+ { -- | Which frame this message belongs to, starting from 0.+ frame :: U32.U32,+ -- | The primary player's name.+ name :: Str.Str,+ -- | The content of the message.+ value :: Str.Str+ }+ deriving (Eq, Show)++instance Json.FromJSON Message where+ parseJSON = Json.withObject "Message" $ \object -> do+ frame <- Json.required object "frame"+ name <- Json.required object "name"+ value <- Json.required object "value"+ pure Message {frame, name, value}++instance Json.ToJSON Message where+ toJSON x =+ Json.object+ [ Json.pair "frame" $ frame x,+ Json.pair "name" $ name x,+ Json.pair "value" $ value x+ ]++schema :: Schema.Schema+schema =+ Schema.named "message" $+ Schema.object+ [ (Json.pair "frame" $ Schema.ref U32.schema, True),+ (Json.pair "name" $ Schema.ref Str.schema, True),+ (Json.pair "value" $ Schema.ref Str.schema, True)+ ]++bytePut :: Message -> BytePut.BytePut+bytePut x =+ U32.bytePut (frame x) <> Str.bytePut (name x) <> Str.bytePut (value x)++byteGet :: ByteGet.ByteGet Message+byteGet = ByteGet.label "Message" $ do+ frame <- ByteGet.label "frame" U32.byteGet+ name <- ByteGet.label "name" Str.byteGet+ value <- ByteGet.label "value" Str.byteGet+ pure Message {frame, name, value}
@@ -0,0 +1,53 @@+module Rattletrap.Type.ObjectTarget where++import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.I32 as I32+import qualified Rattletrap.Utility.Json as Json++data ObjectTarget = ObjectTarget+ { isActor :: Bool,+ targetIndex :: I32.I32+ }+ deriving (Eq, Show)++instance Json.FromJSON ObjectTarget where+ parseJSON = Json.withObject "ObjectTarget" $ \object -> do+ isActor <- Json.required object "is_actor"+ targetIndex <- Json.required object "target_index"+ pure+ ObjectTarget+ { isActor,+ targetIndex+ }++instance Json.ToJSON ObjectTarget where+ toJSON x =+ Json.object+ [ Json.pair "is_actor" $ isActor x,+ Json.pair "target_index" $ targetIndex x+ ]++schema :: Schema.Schema+schema =+ Schema.named "object-target" $+ Schema.object+ [ (Json.pair "is_actor" $ Schema.ref Schema.boolean, True),+ (Json.pair "target_index" $ Schema.ref I32.schema, True)+ ]++bitPut :: ObjectTarget -> BitPut.BitPut+bitPut demolishAttribute =+ BitPut.bool (isActor demolishAttribute)+ <> I32.bitPut (targetIndex demolishAttribute)++bitGet :: BitGet.BitGet ObjectTarget+bitGet = BitGet.label "ObjectTarget" $ do+ isActor <- BitGet.label "isActor" BitGet.bool+ targetIndex <- BitGet.label "targetIndex" I32.bitGet+ pure+ ObjectTarget+ { isActor,+ targetIndex+ }
@@ -0,0 +1,62 @@+module Rattletrap.Type.Property where++import qualified Rattletrap.ByteGet as ByteGet+import qualified Rattletrap.BytePut as BytePut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.PropertyValue as PropertyValue+import qualified Rattletrap.Type.Str as Str+import qualified Rattletrap.Type.U32 as U32+import qualified Rattletrap.Utility.Json as Json++data Property = Property+ { kind :: Str.Str,+ -- | Not used.+ size :: U32.U32,+ index :: U32.U32,+ value :: PropertyValue.PropertyValue Property+ }+ deriving (Eq, Show)++instance Json.FromJSON Property where+ parseJSON = Json.withObject "Property" $ \object -> do+ kind <- Json.required object "kind"+ size <- Json.required object "size"+ index <- Json.required object "index"+ value <- Json.required object "value"+ pure Property {kind, size, index, value}++instance Json.ToJSON Property where+ toJSON x =+ Json.object+ [ Json.pair "kind" $ kind x,+ Json.pair "size" $ size x,+ Json.pair "index" $ index x,+ Json.pair "value" $ value x+ ]++schema :: Schema.Schema+schema =+ Schema.named "property" $+ Schema.object+ [ (Json.pair "kind" $ Schema.ref Str.schema, True),+ (Json.pair "size" $ Schema.ref U32.schema, True),+ (Json.pair "index" $ Schema.ref U32.schema, True),+ (Json.pair "value" . Schema.ref $ PropertyValue.schema schema, True)+ ]++bytePut :: Property -> BytePut.BytePut+bytePut x =+ Str.bytePut (kind x)+ <> U32.bytePut (size x)+ <> U32.bytePut (index x)+ <> PropertyValue.bytePut+ bytePut+ (value x)++byteGet :: ByteGet.ByteGet Property+byteGet = ByteGet.label "Property" $ do+ kind <- ByteGet.label "kind" Str.byteGet+ size <- ByteGet.label "size" U32.byteGet+ index <- ByteGet.label "index" U32.byteGet+ value <- ByteGet.label "value" $ PropertyValue.byteGet byteGet kind+ pure Property {kind, size, index, value}
@@ -0,0 +1,37 @@+module Rattletrap.Type.Property.Array where++import qualified Rattletrap.ByteGet as ByteGet+import qualified Rattletrap.BytePut as BytePut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.Dictionary as Dictionary+import qualified Rattletrap.Type.List as RList+import qualified Rattletrap.Utility.Json as Json++newtype Array a+ = Array (RList.List (Dictionary.Dictionary a))+ deriving (Eq, Show)++fromList :: RList.List (Dictionary.Dictionary a) -> Array a+fromList = Array++toList :: Array a -> RList.List (Dictionary.Dictionary a)+toList (Array x) = x++instance (Json.FromJSON a) => Json.FromJSON (Array a) where+ parseJSON = fmap fromList . Json.parseJSON++instance (Json.ToJSON a) => Json.ToJSON (Array a) where+ toJSON = Json.toJSON . toList++schema :: Schema.Schema -> Schema.Schema+schema s =+ Schema.named "property-array" . Schema.json . RList.schema $+ Dictionary.schema+ s++bytePut :: (a -> BytePut.BytePut) -> Array a -> BytePut.BytePut+bytePut f = RList.bytePut (Dictionary.bytePut f) . toList++byteGet :: ByteGet.ByteGet a -> ByteGet.ByteGet (Array a)+byteGet =+ ByteGet.label "Array" . fmap fromList . RList.byteGet . Dictionary.byteGet
@@ -0,0 +1,33 @@+module Rattletrap.Type.Property.Bool where++import qualified Rattletrap.ByteGet as ByteGet+import qualified Rattletrap.BytePut as BytePut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.U8 as U8+import qualified Rattletrap.Utility.Json as Json+import Prelude hiding (Bool)++newtype Bool+ = Bool U8.U8+ deriving (Eq, Show)++fromU8 :: U8.U8 -> Bool+fromU8 = Bool++toU8 :: Bool -> U8.U8+toU8 (Bool x) = x++instance Json.FromJSON Bool where+ parseJSON = fmap fromU8 . Json.parseJSON++instance Json.ToJSON Bool where+ toJSON = Json.toJSON . toU8++schema :: Schema.Schema+schema = U8.schema++bytePut :: Bool -> BytePut.BytePut+bytePut = U8.bytePut . toU8++byteGet :: ByteGet.ByteGet Bool+byteGet = ByteGet.label "Bool" $ fmap fromU8 U8.byteGet
@@ -0,0 +1,53 @@+module Rattletrap.Type.Property.Byte where++import qualified Rattletrap.ByteGet as ByteGet+import qualified Rattletrap.BytePut as BytePut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.Str as Str+import qualified Rattletrap.Type.U8 as U8+import qualified Rattletrap.Utility.Json as Json++data Byte = Byte+ { key :: Str.Str,+ value :: Maybe (Either U8.U8 Str.Str)+ }+ deriving (Eq, Show)++instance Json.FromJSON Byte where+ parseJSON json = do+ (key, value) <- Json.parseJSON json+ pure Byte {key, value}++instance Json.ToJSON Byte where+ toJSON byte = Json.toJSON (key byte, value byte)++schema :: Schema.Schema+schema =+ Schema.named "property-byte" $+ Schema.tuple+ [ Schema.ref Str.schema,+ Schema.oneOf+ [ Schema.ref Schema.null,+ Schema.object [(Json.pair "Left" $ Schema.ref U8.schema, True)],+ Schema.object [(Json.pair "Right" $ Schema.ref Str.schema, True)]+ ]+ ]++bytePut :: Byte -> BytePut.BytePut+bytePut byte = Str.bytePut (key byte) <> foldMap (either U8.bytePut Str.bytePut) (value byte)++byteGet :: ByteGet.ByteGet Byte+byteGet = ByteGet.label "Byte" $ do+ key <- ByteGet.label "key" Str.byteGet+ let isSteam = key == Str.fromString "OnlinePlatform_Steam"+ isPlayStation = key == Str.fromString "OnlinePlatform_PS4"+ isNone = key == Str.fromString "None"+ value <-+ ByteGet.label "value" $+ if isSteam || isPlayStation+ then pure Nothing+ else+ if isNone+ then Just . Left <$> U8.byteGet+ else Just . Right <$> Str.byteGet+ pure Byte {key, value}
@@ -0,0 +1,33 @@+module Rattletrap.Type.Property.Float where++import qualified Rattletrap.ByteGet as ByteGet+import qualified Rattletrap.BytePut as BytePut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.F32 as F32+import qualified Rattletrap.Utility.Json as Json+import Prelude hiding (Float)++newtype Float+ = Float F32.F32+ deriving (Eq, Show)++fromF32 :: F32.F32 -> Float+fromF32 = Float++toF32 :: Float -> F32.F32+toF32 (Float x) = x++instance Json.FromJSON Float where+ parseJSON = fmap fromF32 . Json.parseJSON++instance Json.ToJSON Float where+ toJSON = Json.toJSON . toF32++schema :: Schema.Schema+schema = F32.schema++bytePut :: Float -> BytePut.BytePut+bytePut = F32.bytePut . toF32++byteGet :: ByteGet.ByteGet Float+byteGet = ByteGet.label "Float" $ fmap fromF32 F32.byteGet
@@ -0,0 +1,33 @@+module Rattletrap.Type.Property.Int where++import qualified Rattletrap.ByteGet as ByteGet+import qualified Rattletrap.BytePut as BytePut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.I32 as I32+import qualified Rattletrap.Utility.Json as Json+import Prelude hiding (Int)++newtype Int+ = Int I32.I32+ deriving (Eq, Show)++fromI32 :: I32.I32 -> Int+fromI32 = Int++toI32 :: Int -> I32.I32+toI32 (Int x) = x++instance Json.FromJSON Int where+ parseJSON = fmap fromI32 . Json.parseJSON++instance Json.ToJSON Int where+ toJSON = Json.toJSON . toI32++schema :: Schema.Schema+schema = I32.schema++bytePut :: Int -> BytePut.BytePut+bytePut = I32.bytePut . toI32++byteGet :: ByteGet.ByteGet Int+byteGet = ByteGet.label "I32" $ fmap fromI32 I32.byteGet
@@ -0,0 +1,32 @@+module Rattletrap.Type.Property.Name where++import qualified Rattletrap.ByteGet as ByteGet+import qualified Rattletrap.BytePut as BytePut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.Str as Str+import qualified Rattletrap.Utility.Json as Json++newtype Name+ = Name Str.Str+ deriving (Eq, Show)++fromStr :: Str.Str -> Name+fromStr = Name++toStr :: Name -> Str.Str+toStr (Name x) = x++instance Json.FromJSON Name where+ parseJSON = fmap fromStr . Json.parseJSON++instance Json.ToJSON Name where+ toJSON = Json.toJSON . toStr++schema :: Schema.Schema+schema = Str.schema++bytePut :: Name -> BytePut.BytePut+bytePut = Str.bytePut . toStr++byteGet :: ByteGet.ByteGet Name+byteGet = ByteGet.label "Name" $ fmap fromStr Str.byteGet
@@ -0,0 +1,32 @@+module Rattletrap.Type.Property.QWord where++import qualified Rattletrap.ByteGet as ByteGet+import qualified Rattletrap.BytePut as BytePut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.U64 as U64+import qualified Rattletrap.Utility.Json as Json++newtype QWord+ = QWord U64.U64+ deriving (Eq, Show)++fromU64 :: U64.U64 -> QWord+fromU64 = QWord++toU64 :: QWord -> U64.U64+toU64 (QWord x) = x++instance Json.FromJSON QWord where+ parseJSON = fmap fromU64 . Json.parseJSON++instance Json.ToJSON QWord where+ toJSON = Json.toJSON . toU64++schema :: Schema.Schema+schema = U64.schema++bytePut :: QWord -> BytePut.BytePut+bytePut = U64.bytePut . toU64++byteGet :: ByteGet.ByteGet QWord+byteGet = ByteGet.label "QWord" $ fmap fromU64 U64.byteGet
@@ -0,0 +1,32 @@+module Rattletrap.Type.Property.Str where++import qualified Rattletrap.ByteGet as ByteGet+import qualified Rattletrap.BytePut as BytePut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.Str as Str+import qualified Rattletrap.Utility.Json as Json++newtype Str+ = Str Str.Str+ deriving (Eq, Show)++fromStr :: Str.Str -> Str+fromStr = Str++toStr :: Str -> Str.Str+toStr (Str x) = x++instance Json.FromJSON Str where+ parseJSON = fmap fromStr . Json.parseJSON++instance Json.ToJSON Str where+ toJSON = Json.toJSON . toStr++schema :: Schema.Schema+schema = Str.schema++bytePut :: Str -> BytePut.BytePut+bytePut = Str.bytePut . toStr++byteGet :: ByteGet.ByteGet Str+byteGet = ByteGet.label "Str" $ fmap fromStr Str.byteGet
@@ -0,0 +1,46 @@+module Rattletrap.Type.Property.Struct where++import qualified Rattletrap.ByteGet as ByteGet+import qualified Rattletrap.BytePut as BytePut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.Dictionary as Dictionary+import qualified Rattletrap.Type.Str as Str+import qualified Rattletrap.Utility.Json as Json++data Struct a = Struct+ { name :: Str.Str,+ fields :: Dictionary.Dictionary a+ }+ deriving (Eq, Show)++instance (Json.FromJSON a) => Json.FromJSON (Struct a) where+ parseJSON = Json.withObject "Struct" $ \o -> do+ name <- Json.required o "name"+ fields <- Json.required o "fields"+ pure Struct {name, fields}++instance (Json.ToJSON a) => Json.ToJSON (Struct a) where+ toJSON x =+ Json.object+ [ Json.pair "name" $ name x,+ Json.pair "fields" $ fields x+ ]++schema :: Schema.Schema -> Schema.Schema+schema s =+ Schema.named "property-struct" $+ Schema.object+ [ (Json.pair "name" $ Schema.ref Str.schema, True),+ (Json.pair "fields" $ Schema.ref (Dictionary.schema s), True)+ ]++bytePut :: (a -> BytePut.BytePut) -> Struct a -> BytePut.BytePut+bytePut p x =+ Str.bytePut (name x)+ <> Dictionary.bytePut p (fields x)++byteGet :: ByteGet.ByteGet a -> ByteGet.ByteGet (Struct a)+byteGet g = ByteGet.label "Struct" $ do+ name <- ByteGet.label "name" Str.byteGet+ fields <- ByteGet.label "fields" $ Dictionary.byteGet g+ pure Struct {name, fields}
@@ -0,0 +1,102 @@+module Rattletrap.Type.PropertyValue where++import qualified Data.Foldable as Foldable+import qualified Rattletrap.ByteGet as ByteGet+import qualified Rattletrap.BytePut as BytePut+import qualified Rattletrap.Exception.UnknownProperty as UnknownProperty+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.Property.Array as Property.Array+import qualified Rattletrap.Type.Property.Bool as Property.Bool+import qualified Rattletrap.Type.Property.Byte as Property.Byte+import qualified Rattletrap.Type.Property.Float as Property.Float+import qualified Rattletrap.Type.Property.Int as Property.Int+import qualified Rattletrap.Type.Property.Name as Property.Name+import qualified Rattletrap.Type.Property.QWord as Property.QWord+import qualified Rattletrap.Type.Property.Str as Property.Str+import qualified Rattletrap.Type.Property.Struct as Property.Struct+import qualified Rattletrap.Type.Str as Str+import qualified Rattletrap.Utility.Json as Json++data PropertyValue a+ = -- | Yes, a list of dictionaries. No, it doesn't make sense. These usually+ -- only have one element.+ Array (Property.Array.Array a)+ | Bool Property.Bool.Bool+ | -- | This is a strange name for essentially a key-value pair.+ Byte Property.Byte.Byte+ | Float Property.Float.Float+ | Int Property.Int.Int+ | -- | It's unclear how exactly this is different than a 'StrProperty'.+ Name Property.Name.Name+ | QWord Property.QWord.QWord+ | Str Property.Str.Str+ | Struct (Property.Struct.Struct a)+ deriving (Eq, Show)++instance (Json.FromJSON a) => Json.FromJSON (PropertyValue a) where+ parseJSON = Json.withObject "PropertyValue" $ \object ->+ Foldable.asum+ [ fmap Array $ Json.required object "array",+ fmap Bool $ Json.required object "bool",+ fmap Byte $ Json.required object "byte",+ fmap Float $ Json.required object "float",+ fmap Int $ Json.required object "int",+ fmap Name $ Json.required object "name",+ fmap QWord $ Json.required object "q_word",+ fmap Str $ Json.required object "str",+ fmap Struct $ Json.required object "struct"+ ]++instance (Json.ToJSON a) => Json.ToJSON (PropertyValue a) where+ toJSON x = case x of+ Array y -> Json.object [Json.pair "array" y]+ Bool y -> Json.object [Json.pair "bool" y]+ Byte y -> Json.object [Json.pair "byte" y]+ Float y -> Json.object [Json.pair "float" y]+ Int y -> Json.object [Json.pair "int" y]+ Name y -> Json.object [Json.pair "name" y]+ QWord y -> Json.object [Json.pair "q_word" y]+ Str y -> Json.object [Json.pair "str" y]+ Struct y -> Json.object [Json.pair "struct" y]++schema :: Schema.Schema -> Schema.Schema+schema s =+ Schema.named "property-value" . Schema.oneOf $+ fmap+ (\(k, v) -> Schema.object [(Json.pair k v, True)])+ [ ("array", Schema.ref $ Property.Array.schema s),+ ("bool", Schema.ref Property.Bool.schema),+ ("byte", Schema.ref Property.Byte.schema),+ ("float", Schema.ref Property.Float.schema),+ ("int", Schema.ref Property.Int.schema),+ ("name", Schema.ref Property.Name.schema),+ ("q_word", Schema.ref Property.QWord.schema),+ ("str", Schema.ref Property.Str.schema),+ ("struct", Schema.ref $ Property.Struct.schema s)+ ]++bytePut :: (a -> BytePut.BytePut) -> PropertyValue a -> BytePut.BytePut+bytePut putProperty value = case value of+ Array x -> Property.Array.bytePut putProperty x+ Bool x -> Property.Bool.bytePut x+ Byte x -> Property.Byte.bytePut x+ Float x -> Property.Float.bytePut x+ Int x -> Property.Int.bytePut x+ Name x -> Property.Name.bytePut x+ QWord x -> Property.QWord.bytePut x+ Str x -> Property.Str.bytePut x+ Struct x -> Property.Struct.bytePut putProperty x++byteGet :: ByteGet.ByteGet a -> Str.Str -> ByteGet.ByteGet (PropertyValue a)+byteGet getProperty kind =+ ByteGet.label "PropertyValue" $ case Str.toString kind of+ "ArrayProperty" -> fmap Array $ Property.Array.byteGet getProperty+ "BoolProperty" -> fmap Bool Property.Bool.byteGet+ "ByteProperty" -> fmap Byte Property.Byte.byteGet+ "FloatProperty" -> fmap Float Property.Float.byteGet+ "IntProperty" -> fmap Int Property.Int.byteGet+ "NameProperty" -> fmap Name Property.Name.byteGet+ "QWordProperty" -> fmap QWord Property.QWord.byteGet+ "StrProperty" -> fmap Str Property.Str.byteGet+ "StructProperty" -> fmap Struct $ Property.Struct.byteGet getProperty+ x -> ByteGet.throw $ UnknownProperty.UnknownProperty x
@@ -0,0 +1,167 @@+module Rattletrap.Type.Quaternion where++import qualified Data.List as List+import qualified Data.Maybe as Maybe+import qualified Data.Ord as Ord+import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Exception.InvalidComponent as InvalidComponent+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.CompressedWord as CompressedWord+import qualified Rattletrap.Utility.Json as Json++data Quaternion = Quaternion+ { x :: Double,+ y :: Double,+ z :: Double,+ w :: Double+ }+ deriving (Eq, Show)++instance Json.FromJSON Quaternion where+ parseJSON = Json.withObject "Quaternion" $ \object -> do+ x <- Json.required object "x"+ y <- Json.required object "y"+ z <- Json.required object "z"+ w <- Json.required object "w"+ pure Quaternion {x, y, z, w}++instance Json.ToJSON Quaternion where+ toJSON a =+ Json.object+ [ Json.pair "x" $ x a,+ Json.pair "y" $ y a,+ Json.pair "z" $ z a,+ Json.pair "w" $ w a+ ]++schema :: Schema.Schema+schema =+ Schema.named "quaternion" $+ Schema.object+ [ (Json.pair "x" $ Schema.ref Schema.number, True),+ (Json.pair "y" $ Schema.ref Schema.number, True),+ (Json.pair "z" $ Schema.ref Schema.number, True),+ (Json.pair "w" $ Schema.ref Schema.number, True)+ ]++data Component+ = X+ | Y+ | Z+ | W+ deriving (Eq, Show)++toQuaternion :: Component -> Double -> Double -> Double -> Quaternion+toQuaternion component a b c =+ let d = toPart a b c+ in case component of+ X -> Quaternion d a b c+ Y -> Quaternion a d b c+ Z -> Quaternion a b d c+ W -> Quaternion a b c d++toPart :: Double -> Double -> Double -> Double+toPart a b c = sqrt (1 - (a * a) - (b * b) - (c * c))++compressPart :: Double -> CompressedWord.CompressedWord+compressPart =+ CompressedWord.CompressedWord maxCompressedValue+ . round+ . (* wordToDouble maxCompressedValue)+ . (+ 0.5)+ . (/ 2.0)+ . (/ maxValue)++decompressPart :: CompressedWord.CompressedWord -> Double+decompressPart x_ =+ (* maxValue)+ . (* 2.0)+ . subtract 0.5+ . (/ wordToDouble (CompressedWord.limit x_))+ . wordToDouble+ $ CompressedWord.value x_++maxComponent :: Quaternion -> Component+maxComponent quaternion =+ let x_ = x quaternion+ y_ = y quaternion+ z_ = z quaternion+ w_ = w quaternion+ parts = [(x_, X), (y_, Y), (z_, Z), (w_, W)]+ biggestPart = maximumOn fst parts+ roundTrip = decompressPart . compressPart+ computedPart =+ Maybe.fromMaybe+ biggestPart+ (List.find (\(value, _) -> value /= roundTrip value) parts)+ in snd+ ( if (biggestPart == computedPart)+ || (abs (fst biggestPart - fst computedPart) > 0.00001)+ then biggestPart+ else computedPart+ )++maximumOn :: (Foldable t, Ord b) => (a -> b) -> t a -> a+maximumOn f = List.maximumBy (Ord.comparing f)++numBits :: Word+numBits = 18++wordToDouble :: Word -> Double+wordToDouble = fromIntegral++maxCompressedValue :: Word+maxCompressedValue = (2 ^ numBits) - 1++maxValue :: Double+maxValue = 1.0 / sqrt 2.0++bitPut :: Quaternion -> BitPut.BitPut+bitPut q =+ let c = maxComponent q+ in putComponent c <> case c of+ X -> putParts (y q) (z q) (w q)+ Y -> putParts (x q) (z q) (w q)+ Z -> putParts (x q) (y q) (w q)+ W -> putParts (x q) (y q) (z q)++putComponent :: Component -> BitPut.BitPut+putComponent component =+ CompressedWord.bitPut+ ( CompressedWord.CompressedWord+ 3+ ( case component of+ X -> 0+ Y -> 1+ Z -> 2+ W -> 3+ )+ )++putParts :: Double -> Double -> Double -> BitPut.BitPut+putParts a b c = putPart a <> putPart b <> putPart c++putPart :: Double -> BitPut.BitPut+putPart = CompressedWord.bitPut . compressPart++bitGet :: BitGet.BitGet Quaternion+bitGet = do+ component <- decodeComponent+ a <- decodePart+ b <- decodePart+ c <- decodePart+ pure $ toQuaternion component a b c++decodeComponent :: BitGet.BitGet Component+decodeComponent = do+ x_ <- CompressedWord.bitGet 3+ case CompressedWord.value x_ of+ 0 -> pure X+ 1 -> pure Y+ 2 -> pure Z+ 3 -> pure W+ y_ -> BitGet.throw $ InvalidComponent.InvalidComponent y_++decodePart :: BitGet.BitGet Double+decodePart = fmap decompressPart $ CompressedWord.bitGet maxCompressedValue
@@ -0,0 +1,92 @@+module Rattletrap.Type.RemoteId where++import qualified Data.Foldable as Foldable+import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Exception.UnknownSystemId as UnknownSystemId+import qualified Rattletrap.Schema as Schema+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+import qualified Rattletrap.Type.RemoteId.Xbox as Xbox+import qualified Rattletrap.Type.U8 as U8+import qualified Rattletrap.Type.Version as Version+import qualified Rattletrap.Utility.Json as Json++data RemoteId+ = PlayStation PlayStation.PlayStation+ | PsyNet PsyNet.PsyNet+ | QQ QQ.QQ+ | -- | Really only 24 bits.+ Splitscreen Splitscreen.Splitscreen+ | Steam Steam.Steam+ | Switch Switch.Switch+ | Xbox Xbox.Xbox+ | Epic Epic.Epic+ deriving (Eq, Show)++instance Json.FromJSON RemoteId where+ 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",+ fmap Xbox $ Json.required object "xbox",+ fmap Epic $ Json.required object "epic"+ ]++instance Json.ToJSON RemoteId where+ 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]+ Xbox y -> Json.object [Json.pair "xbox" y]+ Epic y -> Json.object [Json.pair "epic" y]++schema :: Schema.Schema+schema =+ Schema.named "remote-id" . Schema.oneOf $+ fmap+ (\(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),+ ("xbox", Schema.ref Xbox.schema),+ ("epic", Schema.ref Epic.schema)+ ]++bitPut :: RemoteId -> BitPut.BitPut+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+ Xbox x -> Xbox.bitPut x+ Epic x -> Epic.bitPut x++bitGet :: Version.Version -> U8.U8 -> BitGet.BitGet RemoteId+bitGet version systemId = case U8.toWord8 systemId of+ 0 -> fmap Splitscreen Splitscreen.bitGet+ 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+ x -> BitGet.throw $ UnknownSystemId.UnknownSystemId x
@@ -0,0 +1,32 @@+module Rattletrap.Type.RemoteId.Epic where++import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.Str as Str+import qualified Rattletrap.Utility.Json as Json++newtype Epic+ = Epic Str.Str+ deriving (Eq, Show)++instance Json.FromJSON Epic where+ parseJSON = fmap fromStr . Json.parseJSON++instance Json.ToJSON Epic where+ toJSON = Json.toJSON . toStr++fromStr :: Str.Str -> Epic+fromStr = Epic++toStr :: Epic -> Str.Str+toStr (Epic x) = x++schema :: Schema.Schema+schema = Str.schema++bitPut :: Epic -> BitPut.BitPut+bitPut = Str.bitPut . toStr++bitGet :: BitGet.BitGet Epic+bitGet = BitGet.label "Epic" $ fmap fromStr Str.bitGet
@@ -0,0 +1,55 @@+module Rattletrap.Type.RemoteId.PlayStation where++import qualified Data.ByteString as ByteString+import qualified Data.Text as Text+import qualified Data.Text.Encoding as Text+import qualified Data.Word as Word+import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.Version as Version+import qualified Rattletrap.Utility.Bytes as Bytes+import qualified Rattletrap.Utility.Json as Json++data PlayStation = PlayStation+ { name :: Text.Text,+ code :: [Word.Word8]+ }+ deriving (Eq, Show)++instance Json.FromJSON PlayStation where+ parseJSON json = do+ (name, code) <- Json.parseJSON json+ pure PlayStation {name, code}++instance Json.ToJSON PlayStation where+ toJSON x = Json.toJSON (name x, code x)++schema :: Schema.Schema+schema =+ Schema.named "remote-id-play-station" $+ Schema.tuple+ [Schema.ref Schema.string, Schema.json $ Schema.array Schema.number]++bitPut :: PlayStation -> BitPut.BitPut+bitPut x =+ let nameBytes = Bytes.padBytes (16 :: Int) . Bytes.encodeLatin1 $ name x+ codeBytes = ByteString.pack $ code x+ in BitPut.byteString nameBytes <> BitPut.byteString codeBytes++bitGet :: Version.Version -> BitGet.BitGet PlayStation+bitGet version = BitGet.label "PlayStation" $ do+ name <- BitGet.label "name" getCode+ code <- BitGet.label "code" $ getName version+ pure PlayStation {name, code}++getCode :: BitGet.BitGet Text.Text+getCode =+ fmap (Text.dropWhileEnd (== '\x00') . Text.decodeLatin1) $+ BitGet.byteString 16++getName :: Version.Version -> BitGet.BitGet [Word.Word8]+getName version =+ fmap ByteString.unpack+ . BitGet.byteString+ $ if Version.atLeast 868 20 1 version then 24 else 16
@@ -0,0 +1,56 @@+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+import qualified Rattletrap.Type.U64 as U64+import qualified Rattletrap.Type.Version as Version+import qualified Rattletrap.Utility.Json as Json++data PsyNet+ = New U64.U64+ | Old U64.U64 U64.U64 U64.U64 U64.U64+ deriving (Eq, Show)++instance Json.FromJSON PsyNet where+ 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 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+ [ Schema.object [(Json.pair "Left" $ Schema.ref U64.schema, True)],+ Schema.object+ [ ( Json.pair "Right" . Schema.tuple . replicate 4 $ Schema.ref U64.schema,+ True+ )+ ]+ ]++bitPut :: PsyNet -> BitPut.BitPut+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" $+ 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
@@ -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
@@ -0,0 +1,32 @@+module Rattletrap.Type.RemoteId.Splitscreen where++import qualified Data.Word as Word+import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Utility.Json as Json++newtype Splitscreen+ = Splitscreen Word.Word32+ deriving (Eq, Show)++instance Json.FromJSON Splitscreen where+ parseJSON = fmap fromWord32 . Json.parseJSON++instance Json.ToJSON Splitscreen where+ toJSON = Json.toJSON . toWord32++fromWord32 :: Word.Word32 -> Splitscreen+fromWord32 = Splitscreen++toWord32 :: Splitscreen -> Word.Word32+toWord32 (Splitscreen x) = x++schema :: Schema.Schema+schema = Schema.integer++bitPut :: Splitscreen -> BitPut.BitPut+bitPut = BitPut.bits 24 . toWord32++bitGet :: BitGet.BitGet Splitscreen+bitGet = BitGet.label "Splitscreen" . fmap fromWord32 $ BitGet.bits 24
@@ -0,0 +1,32 @@+module Rattletrap.Type.RemoteId.Steam 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 Steam+ = Steam U64.U64+ deriving (Eq, Show)++instance Json.FromJSON Steam where+ parseJSON = fmap fromU64 . Json.parseJSON++instance Json.ToJSON Steam where+ toJSON = Json.toJSON . toU64++fromU64 :: U64.U64 -> Steam+fromU64 = Steam++toU64 :: Steam -> U64.U64+toU64 (Steam x) = x++schema :: Schema.Schema+schema = U64.schema++bitPut :: Steam -> BitPut.BitPut+bitPut = U64.bitPut . toU64++bitGet :: BitGet.BitGet Steam+bitGet = BitGet.label "Steam" $ fmap fromU64 U64.bitGet
@@ -0,0 +1,41 @@+module Rattletrap.Type.RemoteId.Switch 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++data Switch = Switch+ { a :: U64.U64,+ b :: U64.U64,+ c :: U64.U64,+ d :: U64.U64+ }+ deriving (Eq, Show)++instance Json.FromJSON Switch where+ parseJSON json = do+ (a, b, c, d) <- Json.parseJSON json+ pure Switch {a, b, c, d}++instance Json.ToJSON Switch where+ toJSON x = Json.toJSON (a x, b x, c x, d x)++schema :: Schema.Schema+schema =+ Schema.named "remote-id-switch" . Schema.tuple . replicate 4 $+ Schema.ref+ U64.schema++bitPut :: Switch -> BitPut.BitPut+bitPut x =+ U64.bitPut (a x) <> U64.bitPut (b x) <> U64.bitPut (c x) <> U64.bitPut (d x)++bitGet :: BitGet.BitGet Switch+bitGet = BitGet.label "Switch" $ do+ a <- U64.bitGet+ b <- U64.bitGet+ c <- U64.bitGet+ d <- U64.bitGet+ pure Switch {a, b, c, d}
@@ -0,0 +1,32 @@+module Rattletrap.Type.RemoteId.Xbox 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 Xbox+ = Xbox U64.U64+ deriving (Eq, Show)++instance Json.FromJSON Xbox where+ parseJSON = fmap fromU64 . Json.parseJSON++instance Json.ToJSON Xbox where+ toJSON = Json.toJSON . toU64++fromU64 :: U64.U64 -> Xbox+fromU64 = Xbox++toU64 :: Xbox -> U64.U64+toU64 (Xbox x) = x++schema :: Schema.Schema+schema = U64.schema++bitPut :: Xbox -> BitPut.BitPut+bitPut = U64.bitPut . toU64++bitGet :: BitGet.BitGet Xbox+bitGet = BitGet.label "Xbox" $ fmap fromU64 U64.bitGet
@@ -0,0 +1,134 @@+module Rattletrap.Type.Replay where++import qualified Rattletrap.ByteGet as ByteGet+import qualified Rattletrap.BytePut as BytePut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.Content as Content+import qualified Rattletrap.Type.Dictionary as Dictionary+import qualified Rattletrap.Type.Header as Header+import qualified Rattletrap.Type.I32 as I32+import qualified Rattletrap.Type.Property as Property+import qualified Rattletrap.Type.Property.Int as Property.Int+import qualified Rattletrap.Type.Property.Name as Property.Name+import qualified Rattletrap.Type.Property.Str as Property.Str+import qualified Rattletrap.Type.PropertyValue as PropertyValue+import qualified Rattletrap.Type.Section as Section+import qualified Rattletrap.Type.Str as Str+import qualified Rattletrap.Type.U32 as U32+import qualified Rattletrap.Utility.Json as Json+import qualified Rattletrap.Version as Version++type Replay =+ ReplayWith+ (Section.Section Header.Header)+ (Section.Section Content.Content)++-- | A Rocket League replay.+data ReplayWith header content = Replay+ { -- | This has most of the high-level metadata.+ header :: header,+ -- | This has most of the low-level game data.+ content :: content+ }+ deriving (Eq, Show)++instance (Json.FromJSON h, Json.FromJSON c) => Json.FromJSON (ReplayWith h c) where+ parseJSON = Json.withObject "Replay" $ \object -> do+ header <- Json.required object "header"+ content <- Json.required object "content"+ pure Replay {header, content}++instance (Json.ToJSON h, Json.ToJSON c) => Json.ToJSON (ReplayWith h c) where+ toJSON x =+ Json.object+ [ Json.pair "$schema" schemaUrl,+ Json.pair "header" $ header x,+ Json.pair "content" $ content x+ ]++schema :: Schema.Schema -> Schema.Schema -> Schema.Schema+schema h c =+ Schema.named "replay" $+ Schema.object+ [ (Json.pair "header" $ Schema.ref h, True),+ (Json.pair "content" $ Schema.ref c, True)+ ]++schemaUrl :: String+schemaUrl =+ mconcat+ [ "https://github.com/tfausak/rattletrap/releases/download/",+ Version.string,+ "/rattletrap-",+ Version.string,+ "-schema.json"+ ]++bytePut :: Replay -> BytePut.BytePut+bytePut x =+ Section.bytePut Header.bytePut (header x)+ <> Section.bytePut Content.bytePut (content x)++byteGet :: Bool -> Bool -> ByteGet.ByteGet Replay+byteGet fast skip = ByteGet.label "Replay" $ do+ header <- ByteGet.label "header" $ do+ section <-+ Section.byteGet skip $ ByteGet.byteString . fromIntegral . U32.toWord32+ body <- ByteGet.embed Header.byteGet $ Section.body section+ pure section {Section.body}+ content <- ByteGet.label "content" $ do+ section <-+ Section.byteGet skip $ ByteGet.byteString . fromIntegral . U32.toWord32+ body <-+ if fast+ then pure Content.empty+ else+ ByteGet.embed (getContent $ Section.body header) $+ Section.body section+ pure section {Section.body}+ pure Replay {header, content}++getContent :: Header.Header -> ByteGet.ByteGet Content.Content+getContent h =+ Content.byteGet+ (getMatchType h)+ (Header.version h)+ (getNumFrames h)+ (getMaxChannels h)+ (getBuildVersion h)++getMatchType :: Header.Header -> Maybe Str.Str+getMatchType header = do+ Property.Property {Property.value} <-+ Dictionary.lookup (Str.fromString "MatchType") $ Header.properties header+ case value of+ PropertyValue.Name x -> Just $ Property.Name.toStr x+ _ -> Nothing++getNumFrames :: Header.Header -> Int+getNumFrames header_ =+ case Dictionary.lookup+ (Str.fromString "NumFrames")+ (Header.properties header_) of+ Just (Property.Property _ _ _ (PropertyValue.Int numFrames)) ->+ fromIntegral (I32.toInt32 (Property.Int.toI32 numFrames))+ _ -> 0++getMaxChannels :: Header.Header -> Word+getMaxChannels header_ =+ subtract 1 $+ case Dictionary.lookup+ (Str.fromString "MaxChannels")+ (Header.properties header_) of+ Just (Property.Property _ _ _ (PropertyValue.Int maxChannels)) ->+ fromIntegral (I32.toInt32 (Property.Int.toI32 maxChannels))+ _ -> 1023++getBuildVersion :: Header.Header -> Maybe Str.Str+getBuildVersion header = do+ property <-+ Dictionary.lookup (Str.fromString "BuildVersion") $+ Header.properties header+ case Property.value property of+ PropertyValue.Str x -> Just $ Property.Str.toStr x+ _ -> Nothing
@@ -0,0 +1,128 @@+module Rattletrap.Type.Replication where++import qualified Data.Map as Map+import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.ClassAttributeMap as ClassAttributeMap+import qualified Rattletrap.Type.CompressedWord as CompressedWord+import qualified Rattletrap.Type.List as RList+import qualified Rattletrap.Type.ReplicationValue as ReplicationValue+import qualified Rattletrap.Type.Str as Str+import qualified Rattletrap.Type.U32 as U32+import qualified Rattletrap.Type.Version as Version+import qualified Rattletrap.Utility.Json as Json++data Replication = Replication+ { actorId :: CompressedWord.CompressedWord,+ value :: ReplicationValue.ReplicationValue+ }+ deriving (Eq, Show)++instance Json.FromJSON Replication where+ parseJSON = Json.withObject "Replication" $ \object -> do+ actorId <- Json.required object "actor_id"+ value <- Json.required object "value"+ pure Replication {actorId, value}++instance Json.ToJSON Replication where+ toJSON x =+ Json.object [Json.pair "actor_id" $ actorId x, Json.pair "value" $ value x]++schema :: Schema.Schema+schema =+ Schema.named "replication" $+ Schema.object+ [ (Json.pair "actor_id" $ Schema.ref CompressedWord.schema, True),+ (Json.pair "value" $ Schema.ref ReplicationValue.schema, True)+ ]++putReplications :: RList.List Replication -> BitPut.BitPut+putReplications xs =+ foldMap (\x -> BitPut.bool True <> bitPut x) (RList.toList xs)+ <> BitPut.bool False++bitPut :: Replication -> BitPut.BitPut+bitPut replication =+ CompressedWord.bitPut (actorId replication)+ <> ReplicationValue.bitPut (value replication)++decodeReplicationsBits ::+ Maybe Str.Str ->+ Version.Version ->+ Maybe Str.Str ->+ Word ->+ ClassAttributeMap.ClassAttributeMap ->+ Map.Map CompressedWord.CompressedWord U32.U32 ->+ BitGet.BitGet+ ( Map.Map CompressedWord.CompressedWord U32.U32,+ RList.List Replication+ )+decodeReplicationsBits matchType version buildVersion limit classes actorMap =+ decodeReplicationsBitsWith+ matchType+ version+ buildVersion+ limit+ classes+ actorMap+ 0+ []++decodeReplicationsBitsWith ::+ Maybe Str.Str ->+ Version.Version ->+ Maybe Str.Str ->+ Word ->+ ClassAttributeMap.ClassAttributeMap ->+ Map.Map CompressedWord.CompressedWord U32.U32 ->+ Int ->+ [Replication] ->+ BitGet.BitGet+ ( Map.Map CompressedWord.CompressedWord U32.U32,+ RList.List Replication+ )+decodeReplicationsBitsWith matchType version buildVersion limit classes actorMap index replications =+ do+ hasReplication <- BitGet.bool+ if hasReplication+ then do+ (newActorMap, replication) <-+ BitGet.label ("element (" <> show index <> ")") $+ bitGet matchType version buildVersion limit classes actorMap+ decodeReplicationsBitsWith+ matchType+ version+ buildVersion+ limit+ classes+ newActorMap+ (index + 1)+ $ replication+ : replications+ else pure (actorMap, RList.fromList $ reverse replications)++bitGet ::+ Maybe Str.Str ->+ Version.Version ->+ Maybe Str.Str ->+ Word ->+ ClassAttributeMap.ClassAttributeMap ->+ Map.Map CompressedWord.CompressedWord U32.U32 ->+ BitGet.BitGet+ ( Map.Map CompressedWord.CompressedWord U32.U32,+ Replication+ )+bitGet matchType version buildVersion limit classes actorMap =+ BitGet.label "Replication" $ do+ actorId <- BitGet.label "actorId" $ CompressedWord.bitGet limit+ (newActorMap, value) <-+ BitGet.label "value" $+ ReplicationValue.bitGet+ matchType+ version+ buildVersion+ classes+ actorId+ actorMap+ pure (newActorMap, Replication {actorId, value})
@@ -0,0 +1,30 @@+module Rattletrap.Type.Replication.Destroyed where++import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Utility.Json as Json++-- | Destroyed replications don't actually contain any extra information. All+-- you need to know is the actor's ID, which is given by the+-- 'Rattletrap.Replication.Replication'.+data Destroyed = Destroyed+ deriving (Eq, Show)++instance Json.FromJSON Destroyed where+ parseJSON json = do+ () <- Json.parseJSON json+ pure Destroyed++instance Json.ToJSON Destroyed where+ toJSON = const $ Json.toJSON ()++schema :: Schema.Schema+schema =+ Schema.named "replication-destroyed" $ Json.object [Json.pair "type" "array"]++bitPut :: Destroyed -> BitPut.BitPut+bitPut _ = mempty++bitGet :: BitGet.BitGet Destroyed+bitGet = BitGet.label "Destroyed" $ pure Destroyed
@@ -0,0 +1,164 @@+module Rattletrap.Type.Replication.Spawned where++import qualified Data.Map as Map+import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Exception.MissingClassName as MissingClassName+import qualified Rattletrap.Exception.MissingObjectName as MissingObjectName+import qualified Rattletrap.Exception.UnknownName as UnknownName+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.ClassAttributeMap as ClassAttributeMap+import qualified Rattletrap.Type.CompressedWord as CompressedWord+import qualified Rattletrap.Type.Initialization as Initialization+import qualified Rattletrap.Type.Str as Str+import qualified Rattletrap.Type.U32 as U32+import qualified Rattletrap.Type.Version as Version+import qualified Rattletrap.Utility.Json as Json+import qualified Rattletrap.Utility.Monad as Monad++data Spawned = Spawned+ { -- | Unclear what this is.+ flag :: Bool,+ nameIndex :: Maybe U32.U32,+ -- | Read-only! Changing a replication's name requires editing the+ -- 'nameIndex' and maybe the class attribute map.+ name :: Maybe Str.Str,+ objectId :: U32.U32,+ -- | Read-only! Changing a replication's object requires editing the class+ -- attribute map.+ objectName :: Str.Str,+ -- | Read-only! Changing a replication's class requires editing the class+ -- attribute map.+ className :: Str.Str,+ initialization :: Initialization.Initialization+ }+ deriving (Eq, Show)++instance Json.FromJSON Spawned where+ parseJSON = Json.withObject "Spawned" $ \object -> do+ flag <- Json.required object "flag"+ nameIndex <- Json.optional object "name_index"+ name <- Json.optional object "name"+ objectId <- Json.required object "object_id"+ objectName <- Json.required object "object_name"+ className <- Json.required object "class_name"+ initialization <- Json.required object "initialization"+ pure+ Spawned+ { flag,+ nameIndex,+ name,+ objectId,+ objectName,+ className,+ initialization+ }++instance Json.ToJSON Spawned where+ toJSON x =+ Json.object+ [ Json.pair "flag" $ flag x,+ Json.pair "name_index" $ nameIndex x,+ Json.pair "name" $ name x,+ Json.pair "object_id" $ objectId x,+ Json.pair "object_name" $ objectName x,+ Json.pair "class_name" $ className x,+ Json.pair "initialization" $ initialization x+ ]++schema :: Schema.Schema+schema =+ Schema.named "replication-spawned" $+ Schema.object+ [ (Json.pair "flag" $ Schema.ref Schema.boolean, True),+ (Json.pair "name_index" . Schema.json $ Schema.maybe U32.schema, False),+ (Json.pair "name" . Schema.json $ Schema.maybe Str.schema, False),+ (Json.pair "object_id" $ Schema.ref U32.schema, True),+ (Json.pair "object_name" $ Schema.ref Str.schema, True),+ (Json.pair "class_name" $ Schema.ref Str.schema, True),+ (Json.pair "initialization" $ Schema.ref Initialization.schema, True)+ ]++bitPut :: Spawned -> BitPut.BitPut+bitPut spawnedReplication =+ BitPut.bool (flag spawnedReplication)+ <> foldMap U32.bitPut (nameIndex spawnedReplication)+ <> U32.bitPut (objectId spawnedReplication)+ <> Initialization.bitPut (initialization spawnedReplication)++bitGet ::+ Maybe Str.Str ->+ Version.Version ->+ ClassAttributeMap.ClassAttributeMap ->+ CompressedWord.CompressedWord ->+ Map.Map CompressedWord.CompressedWord U32.U32 ->+ BitGet.BitGet+ (Map.Map CompressedWord.CompressedWord U32.U32, Spawned)+bitGet matchType version classAttributeMap actorId actorMap =+ BitGet.label "Spawned" $ do+ flag <- BitGet.label "flag" BitGet.bool+ nameIndex <-+ BitGet.label "nameIndex" $+ Monad.whenMaybe (hasNameIndex matchType version) U32.bitGet+ name <- lookupName classAttributeMap nameIndex+ objectId <- BitGet.label "objectId" U32.bitGet+ objectName <- lookupObjectName classAttributeMap objectId+ className <- lookupClassName objectName+ let hasLocation = ClassAttributeMap.classHasLocation className+ let hasRotation = ClassAttributeMap.classHasRotation className+ initialization <-+ BitGet.label "initialization" $+ Initialization.bitGet version hasLocation hasRotation+ pure+ ( Map.insert actorId objectId actorMap,+ Spawned+ { flag,+ nameIndex,+ name,+ objectId,+ objectName,+ className,+ initialization+ }+ )++hasNameIndex :: Maybe Str.Str -> Version.Version -> Bool+hasNameIndex matchType version =+ Version.atLeast 868 20 0 version+ || Version.atLeast 868 14 0 version+ && (matchType /= Just (Str.fromString "Lan"))++lookupName ::+ ClassAttributeMap.ClassAttributeMap ->+ Maybe U32.U32 ->+ BitGet.BitGet (Maybe Str.Str)+lookupName classAttributeMap maybeNameIndex = case maybeNameIndex of+ Nothing -> pure Nothing+ Just nameIndex_ ->+ case ClassAttributeMap.getName+ (ClassAttributeMap.nameMap classAttributeMap)+ nameIndex_ of+ Nothing ->+ BitGet.throw . UnknownName.UnknownName $ U32.toWord32 nameIndex_+ Just name_ -> pure (Just name_)++lookupObjectName ::+ ClassAttributeMap.ClassAttributeMap -> U32.U32 -> BitGet.BitGet Str.Str+lookupObjectName classAttributeMap objectId_ =+ case ClassAttributeMap.getObjectName+ (ClassAttributeMap.objectMap classAttributeMap)+ objectId_ of+ Nothing ->+ BitGet.throw . MissingObjectName.MissingObjectName $+ U32.toWord32+ objectId_+ Just objectName_ -> pure objectName_++lookupClassName :: Str.Str -> BitGet.BitGet Str.Str+lookupClassName objectName_ =+ case ClassAttributeMap.getClassName objectName_ of+ Nothing ->+ BitGet.throw . MissingClassName.MissingClassName $+ Str.toString+ objectName_+ Just className_ -> pure className_
@@ -0,0 +1,52 @@+module Rattletrap.Type.Replication.Updated where++import qualified Data.Map as Map+import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.Attribute as Attribute+import qualified Rattletrap.Type.ClassAttributeMap as ClassAttributeMap+import qualified Rattletrap.Type.CompressedWord as CompressedWord+import qualified Rattletrap.Type.List as RList+import qualified Rattletrap.Type.Str as Str+import qualified Rattletrap.Type.U32 as U32+import qualified Rattletrap.Type.Version as Version+import qualified Rattletrap.Utility.Json as Json+import qualified Rattletrap.Utility.Monad as Monad++newtype Updated = Updated+ { attributes :: RList.List Attribute.Attribute+ }+ deriving (Eq, Show)++instance Json.FromJSON Updated where+ parseJSON = fmap Updated . Json.parseJSON++instance Json.ToJSON Updated where+ toJSON = Json.toJSON . attributes++schema :: Schema.Schema+schema =+ Schema.named "replication-updated" . Schema.json $+ RList.schema+ Attribute.schema++bitPut :: Updated -> BitPut.BitPut+bitPut x =+ foldMap+ (\y -> BitPut.bool True <> Attribute.bitPut y)+ (RList.toList $ attributes x)+ <> BitPut.bool False++bitGet ::+ Version.Version ->+ Maybe Str.Str ->+ ClassAttributeMap.ClassAttributeMap ->+ Map.Map CompressedWord.CompressedWord U32.U32 ->+ CompressedWord.CompressedWord ->+ BitGet.BitGet Updated+bitGet version buildVersion classes actors actor =+ BitGet.label "Updated" . fmap Updated . RList.untilM $ do+ p <- BitGet.bool+ Monad.whenMaybe p $+ Attribute.bitGet version buildVersion classes actors actor
@@ -0,0 +1,95 @@+module Rattletrap.Type.ReplicationValue where++import qualified Data.Foldable as Foldable+import qualified Data.Map as Map+import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.ClassAttributeMap as ClassAttributeMap+import qualified Rattletrap.Type.CompressedWord as CompressedWord+import qualified Rattletrap.Type.Replication.Destroyed as Destroyed+import qualified Rattletrap.Type.Replication.Spawned as Spawned+import qualified Rattletrap.Type.Replication.Updated as Updated+import qualified Rattletrap.Type.Str as Str+import qualified Rattletrap.Type.U32 as U32+import qualified Rattletrap.Type.Version as Version+import qualified Rattletrap.Utility.Json as Json++data ReplicationValue+ = -- | Creates a new actor.+ Spawned Spawned.Spawned+ | -- | Updates an existing actor.+ Updated Updated.Updated+ | -- | Destroys an existing actor.+ Destroyed Destroyed.Destroyed+ deriving (Eq, Show)++instance Json.FromJSON ReplicationValue where+ parseJSON = Json.withObject "ReplicationValue" $ \object ->+ Foldable.asum+ [ fmap Spawned $ Json.required object "spawned",+ fmap Updated $ Json.required object "updated",+ fmap Destroyed $ Json.required object "destroyed"+ ]++instance Json.ToJSON ReplicationValue where+ toJSON x = case x of+ Spawned y -> Json.object [Json.pair "spawned" y]+ Updated y -> Json.object [Json.pair "updated" y]+ Destroyed y -> Json.object [Json.pair "destroyed" y]++schema :: Schema.Schema+schema =+ Schema.named "replicationValue" . Schema.oneOf $+ fmap+ (\(k, v) -> Schema.object [(Json.pair k $ Schema.ref v, True)])+ [ ("spawned", Spawned.schema),+ ("updated", Updated.schema),+ ("destroyed", Destroyed.schema)+ ]++bitPut :: ReplicationValue -> BitPut.BitPut+bitPut value = case value of+ Spawned x -> BitPut.bool True <> BitPut.bool True <> Spawned.bitPut x+ Updated x -> BitPut.bool True <> BitPut.bool False <> Updated.bitPut x+ Destroyed x -> BitPut.bool False <> Destroyed.bitPut x++bitGet ::+ Maybe Str.Str ->+ Version.Version ->+ Maybe Str.Str ->+ ClassAttributeMap.ClassAttributeMap ->+ CompressedWord.CompressedWord ->+ Map.Map CompressedWord.CompressedWord U32.U32 ->+ BitGet.BitGet+ ( Map.Map CompressedWord.CompressedWord U32.U32,+ ReplicationValue+ )+bitGet matchType version buildVersion classAttributeMap actorId actorMap =+ BitGet.label "ReplicationValue" $ do+ isOpen <- BitGet.bool+ if isOpen+ then do+ isNew <- BitGet.bool+ if isNew+ then do+ (newActorMap, spawned) <-+ Spawned.bitGet+ matchType+ version+ classAttributeMap+ actorId+ actorMap+ pure (newActorMap, Spawned spawned)+ else do+ updated <-+ Updated.bitGet+ version+ buildVersion+ classAttributeMap+ actorMap+ actorId+ pure (actorMap, Updated updated)+ else do+ destroyed <- Destroyed.bitGet+ pure (actorMap, Destroyed destroyed)
@@ -0,0 +1,48 @@+module Rattletrap.Type.Rotation where++import qualified Data.Foldable as Foldable+import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.CompressedWordVector as CompressedWordVector+import qualified Rattletrap.Type.Quaternion as Quaternion+import qualified Rattletrap.Type.Version as Version+import qualified Rattletrap.Utility.Json as Json++data Rotation+ = CompressedWordVector CompressedWordVector.CompressedWordVector+ | Quaternion Quaternion.Quaternion+ deriving (Eq, Show)++instance Json.FromJSON Rotation where+ parseJSON = Json.withObject "Rotation" $ \object ->+ Foldable.asum+ [ fmap CompressedWordVector $ Json.required object "compressed_word_vector",+ fmap Quaternion $ Json.required object "quaternion"+ ]++instance Json.ToJSON Rotation where+ toJSON x = case x of+ CompressedWordVector y ->+ Json.object [Json.pair "compressed_word_vector" y]+ Quaternion y -> Json.object [Json.pair "quaternion" y]++schema :: Schema.Schema+schema =+ Schema.named "rotation" . Schema.oneOf $+ fmap+ (\(k, v) -> Schema.object [(Json.pair k $ Schema.ref v, True)])+ [ ("compressed_word_vector", CompressedWordVector.schema),+ ("quaternion", Quaternion.schema)+ ]++bitPut :: Rotation -> BitPut.BitPut+bitPut r = case r of+ CompressedWordVector cwv -> CompressedWordVector.bitPut cwv+ Quaternion q -> Quaternion.bitPut q++bitGet :: Version.Version -> BitGet.BitGet Rotation+bitGet version =+ if Version.atLeast 868 22 7 version+ then fmap Quaternion Quaternion.bitGet+ else fmap CompressedWordVector CompressedWordVector.bitGet
@@ -0,0 +1,96 @@+module Rattletrap.Type.Section where++import qualified Control.Monad as Monad+import qualified Data.ByteString as ByteString+import qualified Data.Text as Text+import qualified Rattletrap.ByteGet as ByteGet+import qualified Rattletrap.BytePut as BytePut+import qualified Rattletrap.Exception.CrcMismatch as CrcMismatch+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.U32 as U32+import qualified Rattletrap.Utility.Crc as Crc+import qualified Rattletrap.Utility.Json as Json++-- | A section is a large piece of a 'Rattletrap.Replay.Replay'. It has a+-- 32-bit size (in bytes), a 32-bit CRC (see "Rattletrap.Utility.Crc"), and then a+-- bunch of data (the body). This interface is provided so that you don't have+-- to think about the size and CRC.+data Section a = Section+ { -- | read only+ size :: U32.U32,+ -- | read only+ crc :: U32.U32,+ -- | The actual content in the section.+ body :: a+ }+ deriving (Eq, Show)++instance (Json.FromJSON a) => Json.FromJSON (Section a) where+ parseJSON = Json.withObject "Section" $ \object -> do+ size <- Json.required object "size"+ crc <- Json.required object "crc"+ body <- Json.required object "body"+ pure Section {size, crc, body}++instance (Json.ToJSON a) => Json.ToJSON (Section a) where+ toJSON x =+ Json.object+ [ Json.pair "size" $ size x,+ Json.pair "crc" $ crc x,+ Json.pair "body" $ body x+ ]++schema :: Schema.Schema -> Schema.Schema+schema s =+ Schema.named ("section-" <> Text.unpack (Schema.name s)) $+ Schema.object+ [ (Json.pair "size" $ Schema.ref U32.schema, True),+ (Json.pair "crc" $ Schema.ref U32.schema, True),+ (Json.pair "body" $ Schema.ref s, True)+ ]++create :: (a -> BytePut.BytePut) -> a -> Section a+create encode body_ =+ let bytes = BytePut.toByteString $ encode body_+ in Section+ { size = U32.fromWord32 . fromIntegral $ ByteString.length bytes,+ crc = U32.fromWord32 $ Crc.compute bytes,+ body = body_+ }++-- | Given a way to put the 'body', puts a section. This will also put+-- the size and CRC.+bytePut :: (a -> BytePut.BytePut) -> Section a -> BytePut.BytePut+bytePut putBody section =+ let rawBody = BytePut.toByteString . putBody $ body section+ size_ = ByteString.length rawBody+ crc_ = Crc.compute rawBody+ in U32.bytePut (U32.fromWord32 (fromIntegral size_))+ <> U32.bytePut (U32.fromWord32 crc_)+ <> BytePut.byteString rawBody++byteGet ::+ Bool -> (U32.U32 -> ByteGet.ByteGet a) -> ByteGet.ByteGet (Section a)+byteGet skip getBody = ByteGet.label "Section" $ do+ size <- ByteGet.label "size" U32.byteGet+ crc <- ByteGet.label "crc" U32.byteGet+ body <- ByteGet.label "body" $ do+ rawBody <- ByteGet.byteString . fromIntegral $ U32.toWord32 size+ Monad.unless skip $ do+ let expected = U32.toWord32 crc+ actual = Crc.compute rawBody+ Monad.when (actual /= expected) . ByteGet.throw $+ CrcMismatch.CrcMismatch+ expected+ actual+ ByteGet.embed (getBody size) rawBody+ pure Section {size, crc, body}++crcMessage :: U32.U32 -> U32.U32 -> String+crcMessage actual expected =+ unwords+ [ "[RT10] actual CRC",+ show actual,+ "does not match expected CRC",+ show expected+ ]
@@ -0,0 +1,101 @@+module Rattletrap.Type.Str where++import qualified Data.ByteString as ByteString+import qualified Data.Char as Char+import qualified Data.Int as Int+import qualified Data.Text as Text+import qualified Data.Text.Encoding as Text+import qualified Data.Text.Encoding.Error as Text+import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.ByteGet as ByteGet+import qualified Rattletrap.BytePut as BytePut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.I32 as I32+import qualified Rattletrap.Utility.Bytes as Bytes+import qualified Rattletrap.Utility.Json as Json++newtype Str+ = Str Text.Text+ deriving (Eq, Ord, Show)++instance Json.FromJSON Str where+ parseJSON = fmap fromText . Json.parseJSON++instance Json.ToJSON Str where+ toJSON = Json.toJSON . toText++schema :: Schema.Schema+schema = Schema.named "str" $ Json.object [Json.pair "type" "string"]++fromText :: Text.Text -> Str+fromText = Str++toText :: Str -> Text.Text+toText (Str x) = x++fromString :: String -> Str+fromString = fromText . Text.pack++toString :: Str -> String+toString = Text.unpack . toText++bytePut :: Str -> BytePut.BytePut+bytePut text =+ let size = getTextSize text+ encode = getTextEncoder size+ in I32.bytePut size <> (BytePut.byteString . encode . addNull $ toText text)++bitPut :: Str -> BitPut.BitPut+bitPut = BitPut.fromBytePut . bytePut++getTextSize :: Str -> I32.I32+getTextSize text =+ let value = toText text+ scale = if Text.all Char.isLatin1 value then 1 else -1 :: Int.Int32+ rawSize =+ if Text.null value+ then 0+ else fromIntegral (Text.length value) + 1 :: Int.Int32+ size =+ if value == Text.pack "\x00\x00\x00None"+ then 0x05000000+ else scale * rawSize :: Int.Int32+ in I32.fromInt32 size++getTextEncoder :: I32.I32 -> Text.Text -> ByteString.ByteString+getTextEncoder size text =+ if I32.toInt32 size < 0+ then Text.encodeUtf16LE text+ else Bytes.encodeLatin1 text++addNull :: Text.Text -> Text.Text+addNull text = if Text.null text then text else Text.snoc text '\x00'++byteGet :: ByteGet.ByteGet Str+byteGet = ByteGet.label "Str" $ do+ size <- ByteGet.label "size" I32.byteGet+ bytes <- ByteGet.label "value" . ByteGet.byteString $ normalizeTextSize size+ pure . fromText . dropNull $ getTextDecoder size bytes++bitGet :: BitGet.BitGet Str+bitGet = do+ rawSize <- I32.bitGet+ bytes <- BitGet.byteString (normalizeTextSize rawSize)+ pure (fromText (dropNull (getTextDecoder rawSize bytes)))++normalizeTextSize :: (Integral a) => I32.I32 -> a+normalizeTextSize size = case I32.toInt32 size of+ 0x05000000 -> 8+ x -> (if x < 0 then negate . (*) 2 else id) $ fromIntegral x++getTextDecoder :: I32.I32 -> ByteString.ByteString -> Text.Text+getTextDecoder size bytes =+ let decode =+ if I32.toInt32 size < 0+ then Text.decodeUtf16LEWith Text.lenientDecode+ else Text.decodeLatin1+ in decode bytes++dropNull :: Text.Text -> Text.Text+dropNull = Text.dropWhileEnd (== '\x00')
@@ -0,0 +1,46 @@+module Rattletrap.Type.U32 where++import qualified Data.Word as Word+import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.ByteGet as ByteGet+import qualified Rattletrap.BytePut as BytePut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Utility.Json as Json++newtype U32+ = U32 Word.Word32+ deriving (Eq, Ord, Show)++instance Json.FromJSON U32 where+ parseJSON = fmap fromWord32 . Json.parseJSON++instance Json.ToJSON U32 where+ toJSON = Json.toJSON . toWord32++schema :: Schema.Schema+schema =+ Schema.named "u32" $+ Json.object+ [ Json.pair "type" "integer",+ Json.pair "minimum" (minBound :: Word.Word32),+ Json.pair "maximum" (maxBound :: Word.Word32)+ ]++fromWord32 :: Word.Word32 -> U32+fromWord32 = U32++toWord32 :: U32 -> Word.Word32+toWord32 (U32 x) = x++bytePut :: U32 -> BytePut.BytePut+bytePut = BytePut.word32 . toWord32++bitPut :: U32 -> BitPut.BitPut+bitPut = BitPut.fromBytePut . bytePut++byteGet :: ByteGet.ByteGet U32+byteGet = ByteGet.label "U32" $ fmap fromWord32 ByteGet.word32++bitGet :: BitGet.BitGet U32+bitGet = BitGet.fromByteGet byteGet 4
@@ -0,0 +1,48 @@+module Rattletrap.Type.U64 where++import qualified Data.Text as Text+import qualified Data.Word as Word+import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.ByteGet as ByteGet+import qualified Rattletrap.BytePut as BytePut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Utility.Json as Json+import qualified Text.Read as Read++newtype U64+ = U64 Word.Word64+ deriving (Eq, Show)++instance Json.FromJSON U64 where+ parseJSON =+ Json.withText "U64" $+ either fail (pure . fromWord64)+ . Read.readEither+ . Text.unpack++instance Json.ToJSON U64 where+ toJSON = Json.toJSON . show . toWord64++schema :: Schema.Schema+schema =+ Schema.named "u64" $+ Json.object [Json.pair "type" "string", Json.pair "pattern" "^[0-9]+$"]++fromWord64 :: Word.Word64 -> U64+fromWord64 = U64++toWord64 :: U64 -> Word.Word64+toWord64 (U64 x) = x++bytePut :: U64 -> BytePut.BytePut+bytePut = BytePut.word64 . toWord64++bitPut :: U64 -> BitPut.BitPut+bitPut = BitPut.fromBytePut . bytePut++byteGet :: ByteGet.ByteGet U64+byteGet = ByteGet.label "U64" $ fmap fromWord64 ByteGet.word64++bitGet :: BitGet.BitGet U64+bitGet = BitGet.fromByteGet byteGet 8
@@ -0,0 +1,46 @@+module Rattletrap.Type.U8 where++import qualified Data.Word as Word+import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.ByteGet as ByteGet+import qualified Rattletrap.BytePut as BytePut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Utility.Json as Json++newtype U8+ = U8 Word.Word8+ deriving (Eq, Show)++instance Json.FromJSON U8 where+ parseJSON = fmap fromWord8 . Json.parseJSON++instance Json.ToJSON U8 where+ toJSON = Json.toJSON . toWord8++schema :: Schema.Schema+schema =+ Schema.named "u8" $+ Json.object+ [ Json.pair "type" "integer",+ Json.pair "minimum" (minBound :: Word.Word8),+ Json.pair "maximum" (maxBound :: Word.Word8)+ ]++fromWord8 :: Word.Word8 -> U8+fromWord8 = U8++toWord8 :: U8 -> Word.Word8+toWord8 (U8 x) = x++bytePut :: U8 -> BytePut.BytePut+bytePut = BytePut.word8 . toWord8++bitPut :: U8 -> BitPut.BitPut+bitPut = BitPut.fromBytePut . bytePut++byteGet :: ByteGet.ByteGet U8+byteGet = ByteGet.label "U8" $ fmap fromWord8 ByteGet.word8++bitGet :: BitGet.BitGet U8+bitGet = BitGet.fromByteGet byteGet 1
@@ -0,0 +1,93 @@+module Rattletrap.Type.Vector where++import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.CompressedWord as CompressedWord+import qualified Rattletrap.Type.Version as Version+import qualified Rattletrap.Utility.Json as Json++data Vector = Vector+ { size :: CompressedWord.CompressedWord,+ -- | This field is guaranteed to be small. In other words, it won't overflow.+ -- It's stored as a regular 'Word' rather than something more precise like a+ -- 'Word8' because it just gets passed to a functions that expect 'Word's.+ -- There's no reason to do a bunch of conversions.+ bias :: Word,+ -- | See 'bias'.+ x :: Int,+ -- | See 'bias'.+ y :: Int,+ -- | See 'bias'.+ z :: Int+ }+ deriving (Eq, Show)++instance Json.FromJSON Vector where+ parseJSON = Json.withObject "Vector" $ \object -> do+ size <- Json.required object "size"+ bias <- Json.required object "bias"+ x <- Json.required object "x"+ y <- Json.required object "y"+ z <- Json.required object "z"+ pure Vector {size, bias, x, y, z}++instance Json.ToJSON Vector where+ toJSON a =+ Json.object+ [ Json.pair "size" $ size a,+ Json.pair "bias" $ bias a,+ Json.pair "x" $ x a,+ Json.pair "y" $ y a,+ Json.pair "z" $ z a+ ]++schema :: Schema.Schema+schema =+ Schema.named "vector" $+ Schema.object+ [ (Json.pair "size" $ Schema.ref CompressedWord.schema, True),+ (Json.pair "bias" $ Schema.ref Schema.integer, True),+ (Json.pair "x" $ Schema.ref Schema.integer, True),+ (Json.pair "y" $ Schema.ref Schema.integer, True),+ (Json.pair "z" $ Schema.ref Schema.integer, True)+ ]++bitPut :: Vector -> BitPut.BitPut+bitPut vector =+ let bitSize =+ round (logBase (2 :: Float) (fromIntegral (bias vector))) - 1 :: Word+ dx = fromIntegral (x vector + fromIntegral (bias vector)) :: Word+ dy = fromIntegral (y vector + fromIntegral (bias vector)) :: Word+ dz = fromIntegral (z vector + fromIntegral (bias vector)) :: Word+ limit = 2 ^ (bitSize + 2) :: Word+ in CompressedWord.bitPut (size vector)+ <> CompressedWord.bitPut (CompressedWord.CompressedWord limit dx)+ <> CompressedWord.bitPut (CompressedWord.CompressedWord limit dy)+ <> CompressedWord.bitPut (CompressedWord.CompressedWord limit dz)++bitGet :: Version.Version -> BitGet.BitGet Vector+bitGet version = BitGet.label "Vector" $ do+ size <-+ BitGet.label "size"+ . CompressedWord.bitGet+ $ if Version.atLeast 868 22 7 version then 21 else 19+ let limit = getLimit size+ bias = getBias size+ x <- BitGet.label "x" $ getPart limit bias+ y <- BitGet.label "y" $ getPart limit bias+ z <- BitGet.label "z" $ getPart limit bias+ pure Vector {size, bias, x, y, z}++getPart :: Word -> Word -> BitGet.BitGet Int+getPart limit bias = fmap (fromDelta bias) (CompressedWord.bitGet limit)++getLimit :: CompressedWord.CompressedWord -> Word+getLimit = (2 ^) . (+ 2) . CompressedWord.value++getBias :: CompressedWord.CompressedWord -> Word+getBias = (2 ^) . (+ 1) . CompressedWord.value++fromDelta :: Word -> CompressedWord.CompressedWord -> Int+fromDelta bias_ x_ =+ fromIntegral (CompressedWord.value x_) - fromIntegral bias_
@@ -0,0 +1,38 @@+module Rattletrap.Type.Version where++import qualified Rattletrap.ByteGet as ByteGet+import qualified Rattletrap.BytePut as BytePut+import qualified Rattletrap.Type.U32 as U32+import qualified Rattletrap.Utility.Monad as Monad++data Version = Version+ { major :: U32.U32,+ minor :: U32.U32,+ patch :: Maybe U32.U32+ }+ deriving (Eq, Show)++atLeast :: Int -> Int -> Int -> Version -> Bool+atLeast m n p v =+ (U32.toWord32 (major v) >= fromIntegral m)+ && (U32.toWord32 (minor v) >= fromIntegral n)+ && (maybe 0 U32.toWord32 (patch v) >= fromIntegral p)++bytePut :: Version -> BytePut.BytePut+bytePut x =+ U32.bytePut (major x)+ <> U32.bytePut (minor x)+ <> foldMap+ U32.bytePut+ (patch x)++byteGet :: ByteGet.ByteGet Version+byteGet = ByteGet.label "Version" $ do+ major <- ByteGet.label "major" U32.byteGet+ minor <- ByteGet.label "minor" U32.byteGet+ patch <-+ ByteGet.label "patch" $+ Monad.whenMaybe+ (U32.toWord32 major >= 868 && U32.toWord32 minor >= 18)+ U32.byteGet+ pure Version {major, minor, patch}
@@ -0,0 +1,13 @@+module Rattletrap.Utility.Bytes where++import qualified Data.ByteString as ByteString+import qualified Data.ByteString.Char8 as Latin1+import qualified Data.Text as Text++encodeLatin1 :: Text.Text -> ByteString.ByteString+encodeLatin1 text = Latin1.pack (Text.unpack text)++padBytes :: (Integral a) => a -> ByteString.ByteString -> ByteString.ByteString+padBytes size bytes =+ bytes+ <> ByteString.replicate (fromIntegral size - ByteString.length bytes) 0x00
@@ -0,0 +1,293 @@+module Rattletrap.Utility.Crc where++import qualified Data.Array.Unboxed as Array+import qualified Data.Bits as Bits+import qualified Data.ByteString as ByteString+import qualified Data.Word as Word++-- | Computes the CRC32 of some bytes. This is done to ensure that the bytes+-- are valid before trying to parse them.+--+-- @+-- compute ('Data.ByteString.Lazy.pack' [0x00])+-- @+--+-- This CRC uses an initial value of @0xefcbf201@ and a polynomial of+-- @0x04c11db7@.+compute :: ByteString.ByteString -> Word.Word32+compute = Bits.complement . ByteString.foldl' update initial++update :: Word.Word32 -> Word.Word8 -> Word.Word32+update crc byte =+ let index = Bits.xor byte . unsafeWord32ToWord8 $ Bits.shiftR crc 24+ left = table Array.! index+ right = Bits.shiftL crc 8+ in Bits.xor left right++unsafeWord32ToWord8 :: Word.Word32 -> Word.Word8+unsafeWord32ToWord8 = fromIntegral++initial :: Word.Word32+initial = Bits.complement 0xefcbf201++table :: Array.Array Word.Word8 Word.Word32+table =+ Array.listArray+ (0, 255)+ [ 0x00000000,+ 0x04c11db7,+ 0x09823b6e,+ 0x0d4326d9,+ 0x130476dc,+ 0x17c56b6b,+ 0x1a864db2,+ 0x1e475005,+ 0x2608edb8,+ 0x22c9f00f,+ 0x2f8ad6d6,+ 0x2b4bcb61,+ 0x350c9b64,+ 0x31cd86d3,+ 0x3c8ea00a,+ 0x384fbdbd,+ 0x4c11db70,+ 0x48d0c6c7,+ 0x4593e01e,+ 0x4152fda9,+ 0x5f15adac,+ 0x5bd4b01b,+ 0x569796c2,+ 0x52568b75,+ 0x6a1936c8,+ 0x6ed82b7f,+ 0x639b0da6,+ 0x675a1011,+ 0x791d4014,+ 0x7ddc5da3,+ 0x709f7b7a,+ 0x745e66cd,+ 0x9823b6e0,+ 0x9ce2ab57,+ 0x91a18d8e,+ 0x95609039,+ 0x8b27c03c,+ 0x8fe6dd8b,+ 0x82a5fb52,+ 0x8664e6e5,+ 0xbe2b5b58,+ 0xbaea46ef,+ 0xb7a96036,+ 0xb3687d81,+ 0xad2f2d84,+ 0xa9ee3033,+ 0xa4ad16ea,+ 0xa06c0b5d,+ 0xd4326d90,+ 0xd0f37027,+ 0xddb056fe,+ 0xd9714b49,+ 0xc7361b4c,+ 0xc3f706fb,+ 0xceb42022,+ 0xca753d95,+ 0xf23a8028,+ 0xf6fb9d9f,+ 0xfbb8bb46,+ 0xff79a6f1,+ 0xe13ef6f4,+ 0xe5ffeb43,+ 0xe8bccd9a,+ 0xec7dd02d,+ 0x34867077,+ 0x30476dc0,+ 0x3d044b19,+ 0x39c556ae,+ 0x278206ab,+ 0x23431b1c,+ 0x2e003dc5,+ 0x2ac12072,+ 0x128e9dcf,+ 0x164f8078,+ 0x1b0ca6a1,+ 0x1fcdbb16,+ 0x018aeb13,+ 0x054bf6a4,+ 0x0808d07d,+ 0x0cc9cdca,+ 0x7897ab07,+ 0x7c56b6b0,+ 0x71159069,+ 0x75d48dde,+ 0x6b93dddb,+ 0x6f52c06c,+ 0x6211e6b5,+ 0x66d0fb02,+ 0x5e9f46bf,+ 0x5a5e5b08,+ 0x571d7dd1,+ 0x53dc6066,+ 0x4d9b3063,+ 0x495a2dd4,+ 0x44190b0d,+ 0x40d816ba,+ 0xaca5c697,+ 0xa864db20,+ 0xa527fdf9,+ 0xa1e6e04e,+ 0xbfa1b04b,+ 0xbb60adfc,+ 0xb6238b25,+ 0xb2e29692,+ 0x8aad2b2f,+ 0x8e6c3698,+ 0x832f1041,+ 0x87ee0df6,+ 0x99a95df3,+ 0x9d684044,+ 0x902b669d,+ 0x94ea7b2a,+ 0xe0b41de7,+ 0xe4750050,+ 0xe9362689,+ 0xedf73b3e,+ 0xf3b06b3b,+ 0xf771768c,+ 0xfa325055,+ 0xfef34de2,+ 0xc6bcf05f,+ 0xc27dede8,+ 0xcf3ecb31,+ 0xcbffd686,+ 0xd5b88683,+ 0xd1799b34,+ 0xdc3abded,+ 0xd8fba05a,+ 0x690ce0ee,+ 0x6dcdfd59,+ 0x608edb80,+ 0x644fc637,+ 0x7a089632,+ 0x7ec98b85,+ 0x738aad5c,+ 0x774bb0eb,+ 0x4f040d56,+ 0x4bc510e1,+ 0x46863638,+ 0x42472b8f,+ 0x5c007b8a,+ 0x58c1663d,+ 0x558240e4,+ 0x51435d53,+ 0x251d3b9e,+ 0x21dc2629,+ 0x2c9f00f0,+ 0x285e1d47,+ 0x36194d42,+ 0x32d850f5,+ 0x3f9b762c,+ 0x3b5a6b9b,+ 0x0315d626,+ 0x07d4cb91,+ 0x0a97ed48,+ 0x0e56f0ff,+ 0x1011a0fa,+ 0x14d0bd4d,+ 0x19939b94,+ 0x1d528623,+ 0xf12f560e,+ 0xf5ee4bb9,+ 0xf8ad6d60,+ 0xfc6c70d7,+ 0xe22b20d2,+ 0xe6ea3d65,+ 0xeba91bbc,+ 0xef68060b,+ 0xd727bbb6,+ 0xd3e6a601,+ 0xdea580d8,+ 0xda649d6f,+ 0xc423cd6a,+ 0xc0e2d0dd,+ 0xcda1f604,+ 0xc960ebb3,+ 0xbd3e8d7e,+ 0xb9ff90c9,+ 0xb4bcb610,+ 0xb07daba7,+ 0xae3afba2,+ 0xaafbe615,+ 0xa7b8c0cc,+ 0xa379dd7b,+ 0x9b3660c6,+ 0x9ff77d71,+ 0x92b45ba8,+ 0x9675461f,+ 0x8832161a,+ 0x8cf30bad,+ 0x81b02d74,+ 0x857130c3,+ 0x5d8a9099,+ 0x594b8d2e,+ 0x5408abf7,+ 0x50c9b640,+ 0x4e8ee645,+ 0x4a4ffbf2,+ 0x470cdd2b,+ 0x43cdc09c,+ 0x7b827d21,+ 0x7f436096,+ 0x7200464f,+ 0x76c15bf8,+ 0x68860bfd,+ 0x6c47164a,+ 0x61043093,+ 0x65c52d24,+ 0x119b4be9,+ 0x155a565e,+ 0x18197087,+ 0x1cd86d30,+ 0x029f3d35,+ 0x065e2082,+ 0x0b1d065b,+ 0x0fdc1bec,+ 0x3793a651,+ 0x3352bbe6,+ 0x3e119d3f,+ 0x3ad08088,+ 0x2497d08d,+ 0x2056cd3a,+ 0x2d15ebe3,+ 0x29d4f654,+ 0xc5a92679,+ 0xc1683bce,+ 0xcc2b1d17,+ 0xc8ea00a0,+ 0xd6ad50a5,+ 0xd26c4d12,+ 0xdf2f6bcb,+ 0xdbee767c,+ 0xe3a1cbc1,+ 0xe760d676,+ 0xea23f0af,+ 0xeee2ed18,+ 0xf0a5bd1d,+ 0xf464a0aa,+ 0xf9278673,+ 0xfde69bc4,+ 0x89b8fd09,+ 0x8d79e0be,+ 0x803ac667,+ 0x84fbdbd0,+ 0x9abc8bd5,+ 0x9e7d9662,+ 0x933eb0bb,+ 0x97ffad0c,+ 0xafb010b1,+ 0xab710d06,+ 0xa6322bdf,+ 0xa2f33668,+ 0xbcb4666d,+ 0xb8757bda,+ 0xb5365d03,+ 0xb1f740b4+ ]
@@ -0,0 +1,46 @@+-- | This module provides helper functions for converting replays to and from+-- both their binary format and JSON.+module Rattletrap.Utility.Helper where++import qualified Control.Exception as Exception+import qualified Data.Bifunctor as Bifunctor+import qualified Data.ByteString as ByteString+import qualified Data.ByteString.Lazy as LazyByteString+import qualified Rattletrap.ByteGet as ByteGet+import qualified Rattletrap.BytePut as BytePut+import qualified Rattletrap.Exception.InvalidJson as InvalidJson+import qualified Rattletrap.Type.Content as Content+import qualified Rattletrap.Type.Replay as Replay+import qualified Rattletrap.Type.Section as Section+import qualified Rattletrap.Utility.Json as Json++-- | Parses a raw replay.+decodeReplayFile ::+ Bool ->+ Bool ->+ ByteString.ByteString ->+ Either ([String], Exception.SomeException) Replay.Replay+decodeReplayFile fast = ByteGet.run . Replay.byteGet fast++-- | Encodes a replay as JSON.+encodeReplayJson :: Replay.Replay -> LazyByteString.ByteString+encodeReplayJson = Json.encodePretty++-- | Parses a JSON replay.+decodeReplayJson ::+ ByteString.ByteString ->+ Either ([String], Exception.SomeException) Replay.Replay+decodeReplayJson =+ Bifunctor.first ((,) [] . Exception.toException . InvalidJson.InvalidJson)+ . Json.decode++-- | Encodes a raw replay.+encodeReplayFile :: Bool -> Replay.Replay -> LazyByteString.ByteString+encodeReplayFile fast replay =+ BytePut.toLazyByteString . Replay.bytePut $+ if fast+ then+ replay+ { Replay.content = Section.create Content.bytePut Content.empty+ }+ else replay
@@ -0,0 +1,55 @@+{- hlint ignore "Avoid restricted extensions" -}+{-# LANGUAGE CPP #-}++module Rattletrap.Utility.Json+ ( module Rattletrap.Utility.Json,+ Aeson.FromJSON (parseJSON),+ Key.Key,+ Aeson.ToJSON (toJSON),+ Aeson.Value,+ Aeson.encode,+ Aeson.object,+ Aeson.withObject,+ Aeson.withText,+ )+where++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++keyToString :: Key.Key -> String+keyToString = Key.toString++required ::+ (Aeson.FromJSON value) => Aeson.Object -> String -> Aeson.Parser value+required object key = object Aeson..: Key.fromString key++optional ::+ (Aeson.FromJSON value) =>+ Aeson.Object ->+ String ->+ Aeson.Parser (Maybe value)+optional object key = object Aeson..:? Key.fromString key++# if MIN_VERSION_aeson(2, 2, 0)+pair :: (Aeson.ToJSON value, Aeson.KeyValue e p) => String -> value -> p+# else+pair :: (Aeson.ToJSON value, Aeson.KeyValue p) => String -> value -> p+# endif+pair key value = Key.fromString key Aeson..= value++decode :: (Aeson.FromJSON a) => ByteString.ByteString -> Either String a+decode = Aeson.eitherDecodeStrict'++encodePretty :: (Aeson.ToJSON a) => a -> LazyByteString.ByteString+encodePretty =+ Aeson.encodePretty'+ Aeson.defConfig+ { Aeson.confCompare = compare,+ Aeson.confIndent = Aeson.Tab,+ Aeson.confTrailingNewline = True+ }
@@ -0,0 +1,4 @@+module Rattletrap.Utility.Monad where++whenMaybe :: (Applicative m) => Bool -> m a -> m (Maybe a)+whenMaybe p f = if p then fmap Just f else pure Nothing
@@ -0,0 +1,7 @@+module Rattletrap.Version where++import qualified Data.Version as Version+import qualified Paths_rattletrap as Package++string :: String+string = Version.showVersion Package.version
@@ -0,0 +1,181 @@+import qualified Control.Monad as Monad+import qualified Data.ByteString as ByteString+import qualified GHC.Clock as Clock+import qualified Rattletrap+import qualified System.FilePath as FilePath+import qualified Text.Printf as Printf++main :: IO ()+main = do+ let directory = "output"+ generateSchema directory+ mapM_ (testReplay directory) replays++generateSchema :: FilePath -> IO ()+generateSchema directory =+ Rattletrap.rattletrap+ ""+ ["--schema", "--output", FilePath.combine directory "schema.json"]++testReplay :: FilePath -> (FilePath, String) -> IO ()+testReplay directory (uuid, name) = do+ let inputFile = FilePath.combine "replays" $ uuid <> ".replay"+ input <- ByteString.readFile inputFile+ 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) $ 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]++encode :: FilePath -> FilePath -> IO ()+encode input output =+ Rattletrap.rattletrap "" ["--input", input, "--output", output]++replays :: [(String, String)]+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+ ("0ae9", "fte"), -- https://github.com/jjbott/RocketLeagueReplayParser/issues/50+ ("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+ ("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+ ("1d25", "bumblebee"), -- https://github.com/tfausak/rattletrap/issues/280+ ("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+ ("2066", "replicated boost"), -- https://github.com/tfausak/rattletrap/issues/296+ ("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+ ("3bd0", "v2.45"), -- https://github.com/tfausak/rattletrap/issues/311+ ("3ea1", "a custom team name"), -- https://github.com/tfausak/rattletrap/commit/cf4d145+ ("4050", "v2.08 dodge impulse"), -- https://github.com/tfausak/rattletrap/issues/247+ ("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+ ("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+ ("59d3", "v2.23"), -- https://github.com/tfausak/rattletrap/issues/274+ ("5a06", "esports items"), -- https://github.com/tfausak/rattletrap/pull/114+ ("5e0b", "max channels"), -- https://github.com/tfausak/rattletrap/issues/254+ ("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+ ("717a", "v2.43"), -- https://github.com/tfausak/rattletrap/issues/307+ ("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+ ("79ae", "voice update replay"), -- https://github.com/tfausak/rattletrap/pull/265+ ("7bf6", "an online loadouts attribute"), -- https://github.com/tfausak/rattletrap/commit/89d02f7+ ("81d1", "gridiron"), -- https://github.com/tfausak/rattletrap/pull/180+ ("83aa", "v2.46 replicated demolish extended"), -- https://github.com/tfausak/rattletrap/issues/316+ ("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+ ("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+ ("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+ ("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+ ("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+ ("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+ ("eae8", "custom team colors"), -- https://github.com/tfausak/rattletrap/commit/809240f+ ("ecd5", "new match guid attribute"), -- https://github.com/tfausak/rattletrap/issues/270+ ("edbb", "remote role"), -- https://github.com/tfausak/rattletrap/pull/106+ ("f08e", "post game lobby"), -- https://github.com/jjbott/RocketLeagueReplayParser/issues/46+ ("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+ ]
@@ -1,5 +0,0 @@-resolver: nightly-2017-11-25--packages:- - .- - tools
@@ -1,106 +0,0 @@-import qualified Control.Monad as Monad-import qualified Data.ByteString.Lazy as ByteString-import qualified Rattletrap-import qualified System.FilePath as FilePath-import qualified System.IO.Temp as Temp-import qualified Test.Hspec as Hspec--main :: IO ()-main = Hspec.hspec spec--spec :: Hspec.Spec-spec = Hspec.describe "Rattletrap" (mapM_ (uncurry itCanRoundTrip) replays)--itCanRoundTrip :: String -> String -> Hspec.Spec-itCanRoundTrip uuid description =- Hspec.it (specName uuid description) (specBody uuid)--specName :: String -> String -> String-specName uuid description = unwords [take 4 uuid, description]--specBody :: String -> IO ()-specBody uuid = do- let inputFile = pathToReplay uuid- input <- ByteString.readFile inputFile- Temp.withSystemTempDirectory- "replay-"- ( \directory -> do- let jsonFile = FilePath.combine directory "replay.json"- Rattletrap.mainWithArgs ["decode", inputFile, jsonFile]- let outputFile = FilePath.combine directory "output.replay"- Rattletrap.mainWithArgs ["encode", jsonFile, outputFile]- output <- ByteString.readFile outputFile- Monad.unless- (output == input)- (Hspec.expectationFailure "output does not match input")- )--pathToReplay :: String -> FilePath-pathToReplay uuid =- FilePath.joinPath ["replays", FilePath.addExtension uuid ".replay"]--replays :: [(String, String)]-replays =- [ ("00080014003600090000036E0F65CCEB", "a flip time")- , ("07E925B1423653D44CB8B4B2524792C1", "a game mode before Neo Tokyo")- , ("0AD21FEE4A512906BEB6C98136AAF49A", "some Latin-1 text")- , ("1205D96C4D819800927791820096CD49", "rumble mode")- , ("160CA83E41083BFD8E6315B4BFCA0561", "a dedicated server IP")- , ("16D580EF479483E015207C901776F9FB", "new property types")- , ("18D6738D415B70B5BE4C299588D3C141", "an online loadout attribute")- , ("1A126AC24CAA0DB0E98835BD960B8AF8", "overtime")- , ("1AE415514DFC65DCBF8B8391AD35488D", "a game time")- , ("1BC2D01444ACE577D01E988EADD4DFD0", "no padding after the frames")- , ("1D1DE97D4941C86E43FE0093563DB621", "a camera pitch")- , ("1EF90FCC4F719F606A5327B3CDD782A4", "a private hoops match")- , ("1F3798E540B0C37A946561ABBB3037F9", "splitscreen players")- , ("211466D04B983F5A33CC2FA1D5928672", "a match save")- , ("22660E3649FC7971E5653692473D4318", "dropshot")- , ("22BACD794ABE7B92E50E9CBDBD9C59CE", "a vote to forfeit")- , ("27B6A7B64553F0F685874584F96BAB1B", "some UTF-16 text")- , ("29F582C34A65EB34D358A784CBE3C189", "frames")- , ("2CFE577044E651D3FA9DBF83ECA8BCC3", "a new playstation id")- , ("338173964F9F71EBDD31058A1936CBB4", "patch 1.37")- , ("372DBFCA4BDB340E4357B6BD43032802", "a camera yaw attribute")- , ("387F059C47C09E253C875CA990EFD9F2", "a frozen attribute")- , ("3EA147DD485B8DD24810689A7A989E44", "a custom team name")- , ("4126861E477F4A03DE2A4080374D7908", "a game mode after Neo Tokyo")- , ("42F0D8DA4FC89AE7B80FCAB7F637A8EA", "reservations after Neo Tokyo")- , ("504ED825482186E771FAA9B642CE5CE4", "some messages")- , ("520E1BFF468CF6C3C48D1EA85D9C7909", "no pickup attribute")- , ("52AA67F94090C19D33C5009E54D31FE4", "a match-ending attribute")- , ("540DA764423C8FB24EB9D486D982F16F", "a demolish attribute")- , ("551CA4D44FF2B86015DE44A6B5790D4C", "private match settings")- , ("6320E51C49066A7C210A2993C2201D5F", "a forfeit attribute")- , ("6688EEE34BFEB3EC3A9E3283098CC712", "a malformed byte property")- , ("6B0D1614417085A7AAD82EAA30D8DABE", "patch 1.37")- , ("6D1B06D844A5BB91B81FD4B5B28F08BA", "a flip right")- , ("6F7CFCD24638F8A6567AB3A8B9958A90", "a map with numbers")- , ("7109EB9846D303E54B7ACBA792036213", "a boost modifier")- , ("7BF6073F4614CE0A438994B9A260DA6A", "an online loadouts attribute")- , ("89CBA30E46FA5385BDD35DA4285D4D2E", "remote user data")- , ("8AE551FF406D7B82ED853B8C7BFF8CDA", "new painted items")- , ("9704208245D7DD851F2FB2BC7DFD9AC3", "a batarang")- , ("98E58A904D713F2DE202358E8573265D", "a player using behind view")- , ("A128B3AB45D5A18E3EF9CF93C9576BCE", "a round count down")- , ("A52F804845573D8DA65E97BF59026A43", "some more mutators")- , ("A558B1B44124D6E021640884E8EEC2A7", "extended explosion data")- , ("A6711CE74272B2E663DCC9A200A218E3", "a waiting player")- , ("A7F001A1417A19BFA8C90990D8F7C2FF", "a ready attribute")- , ("AFB1F46A49737E36928E1EABC6F5B7AD", "patch 1.37")- , ("B9F9B87D4A9D0A3D25D4EC91C0401DE2", "a party leader")- , ("C14F7E0E4D9B5E6BE9AD5D8ED56B174C", "some mutators")- , ("C8372B1345B1803DEF039F815DBD802D", "a spectator")- , ("CC4CA70D4F7A67EBAD0ED9B9923106F7", "after Starbase ARC")- , ("D0449F5F4AA775B86FFA7DA2B5A3204E", "hoops mutators")- , ("D428F81646A98C25902CE988AE5C14C8", "a private hockey match")- , ("D7FB197A451D69075A0C99A2F49A4053", "an explosion attribute")- , ("DCB3A6B94A9DBE46FDE5EAA9B012F6C8", "a pawn type attribute")- , ("DE56B1B146687EA2E62E31A37A08C8EB", "a problematic product attribute")- , ("EAE311E84BA35B590A6FDBA6DD4F2FEB", "an actor/object ID collision")- , ("EAE8DADA4BB2DC5422792C9B4A67392D", "custom team colors")- , ("F299F176491554B11E34AB91CA76B2CE", "a location attribute")- , ("F7B9E14545C7467B89A00895980FCD73", "a hockey game event")- , ("F811C1D24888015E23B598AD8628C742", "no frames")- , ("FDC79DA84DD463D4BCCE6B892829AC88", "an MVP")- ]