rattletrap 3.1.2 → 4.0.0
raw patch · 359 files changed
+5800/−4173 lines, 359 filesdep +HUnitdep +aeson-prettydep +transformersdep −bimapdep −data-binary-ieee754dep −hspecdep ~aesondep ~basedep ~binaryPVP ok
version bump matches the API change (PVP)
Dependencies added: HUnit, aeson-pretty, transformers
Dependencies removed: bimap, data-binary-ieee754, hspec, vector
Dependency ranges changed: aeson, base, binary, bytestring, containers, filepath, http-client, http-client-tls, template-haskell, temporary, text
API changes (from Hackage documentation)
Files
- LICENSE.markdown +8/−10
- README.markdown +56/−19
- executables/Main.hs +2/−2
- library/Rattletrap.hs +137/−84
- 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/Console/Main.hs +226/−0
- library/Rattletrap/Content.hs +0/−125
- library/Rattletrap/Crc.hs +0/−40
- library/Rattletrap/Data.hs +147/−147
- library/Rattletrap/Decode/AppliedDamageAttribute.hs +17/−0
- library/Rattletrap/Decode/Attribute.hs +74/−0
- library/Rattletrap/Decode/AttributeMapping.hs +11/−0
- library/Rattletrap/Decode/AttributeValue.hs +115/−0
- library/Rattletrap/Decode/Bitstream.hs +11/−0
- library/Rattletrap/Decode/BooleanAttribute.hs +9/−0
- library/Rattletrap/Decode/ByteAttribute.hs +10/−0
- library/Rattletrap/Decode/Cache.hs +17/−0
- library/Rattletrap/Decode/CamSettingsAttribute.hs +19/−0
- library/Rattletrap/Decode/ClassMapping.hs +11/−0
- library/Rattletrap/Decode/ClubColorsAttribute.hs +15/−0
- library/Rattletrap/Decode/Common.hs +55/−0
- library/Rattletrap/Decode/CompressedWord.hs +29/−0
- library/Rattletrap/Decode/CompressedWordVector.hs +17/−0
- library/Rattletrap/Decode/Content.hs +68/−0
- library/Rattletrap/Decode/DamageStateAttribute.hs +19/−0
- library/Rattletrap/Decode/DemolishAttribute.hs +18/−0
- library/Rattletrap/Decode/DestroyedReplication.hs +9/−0
- library/Rattletrap/Decode/Dictionary.hs +16/−0
- library/Rattletrap/Decode/EnumAttribute.hs +9/−0
- library/Rattletrap/Decode/ExplosionAttribute.hs +12/−0
- library/Rattletrap/Decode/ExtendedExplosionAttribute.hs +14/−0
- library/Rattletrap/Decode/FlaggedIntAttribute.hs +11/−0
- library/Rattletrap/Decode/Float32le.hs +13/−0
- library/Rattletrap/Decode/FloatAttribute.hs +10/−0
- library/Rattletrap/Decode/Frame.hs +43/−0
- library/Rattletrap/Decode/GameModeAttribute.hs +14/−0
- library/Rattletrap/Decode/Header.hs +21/−0
- library/Rattletrap/Decode/Initialization.hs +14/−0
- library/Rattletrap/Decode/Int32le.hs +13/−0
- library/Rattletrap/Decode/Int8Vector.hs +17/−0
- library/Rattletrap/Decode/Int8le.hs +13/−0
- library/Rattletrap/Decode/IntAttribute.hs +10/−0
- library/Rattletrap/Decode/KeyFrame.hs +12/−0
- library/Rattletrap/Decode/List.hs +15/−0
- library/Rattletrap/Decode/LoadoutAttribute.hs +26/−0
- library/Rattletrap/Decode/LoadoutOnlineAttribute.hs +24/−0
- library/Rattletrap/Decode/LoadoutsAttribute.hs +13/−0
- library/Rattletrap/Decode/LoadoutsOnlineAttribute.hs +22/−0
- library/Rattletrap/Decode/LocationAttribute.hs +10/−0
- library/Rattletrap/Decode/Mark.hs +11/−0
- library/Rattletrap/Decode/Message.hs +11/−0
- library/Rattletrap/Decode/MusicStingerAttribute.hs +15/−0
- library/Rattletrap/Decode/PartyLeaderAttribute.hs +17/−0
- library/Rattletrap/Decode/PickupAttribute.hs +12/−0
- library/Rattletrap/Decode/PlayerHistoryKeyAttribute.hs +11/−0
- library/Rattletrap/Decode/PrivateMatchSettingsAttribute.hs +19/−0
- library/Rattletrap/Decode/ProductAttribute.hs +57/−0
- library/Rattletrap/Decode/Property.hs +14/−0
- library/Rattletrap/Decode/PropertyValue.hs +31/−0
- library/Rattletrap/Decode/QWordAttribute.hs +10/−0
- library/Rattletrap/Decode/RemoteId.hs +38/−0
- library/Rattletrap/Decode/Replay.hs +48/−0
- library/Rattletrap/Decode/Replication.hs +45/−0
- library/Rattletrap/Decode/ReplicationValue.hs +44/−0
- library/Rattletrap/Decode/ReservationAttribute.hs +24/−0
- library/Rattletrap/Decode/RigidBodyStateAttribute.hs +17/−0
- library/Rattletrap/Decode/Section.hs +25/−0
- library/Rattletrap/Decode/SpawnedReplication.hs +69/−0
- library/Rattletrap/Decode/Str.hs +42/−0
- library/Rattletrap/Decode/StringAttribute.hs +10/−0
- library/Rattletrap/Decode/TeamPaintAttribute.hs +17/−0
- library/Rattletrap/Decode/UniqueIdAttribute.hs +15/−0
- library/Rattletrap/Decode/UpdatedReplication.hs +21/−0
- library/Rattletrap/Decode/Vector.hs +28/−0
- library/Rattletrap/Decode/WeldedInfoAttribute.hs +19/−0
- library/Rattletrap/Decode/Word32le.hs +13/−0
- library/Rattletrap/Decode/Word64le.hs +13/−0
- library/Rattletrap/Decode/Word8le.hs +13/−0
- library/Rattletrap/Encode/AppliedDamageAttribute.hs +17/−0
- library/Rattletrap/Encode/Attribute.hs +21/−0
- library/Rattletrap/Encode/AttributeMapping.hs +13/−0
- library/Rattletrap/Encode/AttributeValue.hs +72/−0
- library/Rattletrap/Encode/Bitstream.hs +10/−0
- library/Rattletrap/Encode/BooleanAttribute.hs +11/−0
- library/Rattletrap/Encode/ByteAttribute.hs +12/−0
- library/Rattletrap/Encode/Cache.hs +17/−0
- library/Rattletrap/Encode/CamSettingsAttribute.hs +20/−0
- library/Rattletrap/Encode/ClassMapping.hs +14/−0
- library/Rattletrap/Encode/ClubColorsAttribute.hs +15/−0
- library/Rattletrap/Encode/CompressedWord.hs +43/−0
- library/Rattletrap/Encode/CompressedWordVector.hs +14/−0
- library/Rattletrap/Encode/Content.hs +39/−0
- library/Rattletrap/Encode/DamageStateAttribute.hs +19/−0
- library/Rattletrap/Encode/DemolishAttribute.hs +18/−0
- library/Rattletrap/Encode/DestroyedReplication.hs +10/−0
- library/Rattletrap/Encode/Dictionary.hs +16/−0
- library/Rattletrap/Encode/EnumAttribute.hs +11/−0
- library/Rattletrap/Encode/ExplosionAttribute.hs +15/−0
- library/Rattletrap/Encode/ExtendedExplosionAttribute.hs +15/−0
- library/Rattletrap/Encode/FlaggedIntAttribute.hs +13/−0
- library/Rattletrap/Encode/Float32le.hs +20/−0
- library/Rattletrap/Encode/FloatAttribute.hs +12/−0
- library/Rattletrap/Encode/Frame.hs +19/−0
- library/Rattletrap/Encode/GameModeAttribute.hs +13/−0
- library/Rattletrap/Encode/Header.hs +21/−0
- library/Rattletrap/Encode/Initialization.hs +18/−0
- library/Rattletrap/Encode/Int32le.hs +20/−0
- library/Rattletrap/Encode/Int8Vector.hs +22/−0
- library/Rattletrap/Encode/Int8le.hs +19/−0
- library/Rattletrap/Encode/IntAttribute.hs +11/−0
- library/Rattletrap/Encode/KeyFrame.hs +15/−0
- library/Rattletrap/Encode/List.hs +15/−0
- library/Rattletrap/Encode/LoadoutAttribute.hs +30/−0
- library/Rattletrap/Encode/LoadoutOnlineAttribute.hs +16/−0
- library/Rattletrap/Encode/LoadoutsAttribute.hs +13/−0
- library/Rattletrap/Encode/LoadoutsOnlineAttribute.hs +17/−0
- library/Rattletrap/Encode/LocationAttribute.hs +12/−0
- library/Rattletrap/Encode/Mark.hs +14/−0
- library/Rattletrap/Encode/Message.hs +15/−0
- library/Rattletrap/Encode/MusicStingerAttribute.hs +15/−0
- library/Rattletrap/Encode/PartyLeaderAttribute.hs +18/−0
- library/Rattletrap/Encode/PickupAttribute.hs +17/−0
- library/Rattletrap/Encode/PlayerHistoryKeyAttribute.hs +12/−0
- library/Rattletrap/Encode/PrivateMatchSettingsAttribute.hs +25/−0
- library/Rattletrap/Encode/ProductAttribute.hs +43/−0
- library/Rattletrap/Encode/Property.hs +16/−0
- library/Rattletrap/Encode/PropertyValue.hs +29/−0
- library/Rattletrap/Encode/QWordAttribute.hs +12/−0
- library/Rattletrap/Encode/RemoteId.hs +24/−0
- library/Rattletrap/Encode/Replay.hs +20/−0
- library/Rattletrap/Encode/Replication.hs +21/−0
- library/Rattletrap/Encode/ReplicationValue.hs +24/−0
- library/Rattletrap/Encode/ReservationAttribute.hs +23/−0
- library/Rattletrap/Encode/RigidBodyStateAttribute.hs +22/−0
- library/Rattletrap/Encode/Section.hs +27/−0
- library/Rattletrap/Encode/SpawnedReplication.hs +18/−0
- library/Rattletrap/Encode/Str.hs +55/−0
- library/Rattletrap/Encode/StringAttribute.hs +12/−0
- library/Rattletrap/Encode/TeamPaintAttribute.hs +17/−0
- library/Rattletrap/Encode/UniqueIdAttribute.hs +15/−0
- library/Rattletrap/Encode/UpdatedReplication.hs +12/−0
- library/Rattletrap/Encode/Vector.hs +26/−0
- library/Rattletrap/Encode/WeldedInfoAttribute.hs +19/−0
- library/Rattletrap/Encode/Word32le.hs +20/−0
- library/Rattletrap/Encode/Word64le.hs +20/−0
- library/Rattletrap/Encode/Word8le.hs +20/−0
- 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/Type/AppliedDamageAttribute.hs +19/−0
- library/Rattletrap/Type/Attribute.hs +20/−0
- library/Rattletrap/Type/AttributeMapping.hs +15/−0
- library/Rattletrap/Type/AttributeType.hs +43/−0
- library/Rattletrap/Type/AttributeValue.hs +74/−0
- library/Rattletrap/Type/Bitstream.hs +28/−0
- library/Rattletrap/Type/BooleanAttribute.hs +13/−0
- library/Rattletrap/Type/ByteAttribute.hs +14/−0
- library/Rattletrap/Type/Cache.hs +19/−0
- library/Rattletrap/Type/CamSettingsAttribute.hs +20/−0
- library/Rattletrap/Type/ClassAttributeMap.hs +326/−0
- library/Rattletrap/Type/ClassMapping.hs +16/−0
- library/Rattletrap/Type/ClubColorsAttribute.hs +17/−0
- library/Rattletrap/Type/Common.hs +55/−0
- library/Rattletrap/Type/CompressedWord.hs +14/−0
- library/Rattletrap/Type/CompressedWordVector.hs +16/−0
- library/Rattletrap/Type/Content.hs +52/−0
- library/Rattletrap/Type/DamageStateAttribute.hs +21/−0
- library/Rattletrap/Type/DemolishAttribute.hs +20/−0
- library/Rattletrap/Type/DestroyedReplication.hs +15/−0
- library/Rattletrap/Type/Dictionary.hs +69/−0
- library/Rattletrap/Type/EnumAttribute.hs +13/−0
- library/Rattletrap/Type/ExplosionAttribute.hs +17/−0
- library/Rattletrap/Type/ExtendedExplosionAttribute.hs +16/−0
- library/Rattletrap/Type/FlaggedIntAttribute.hs +15/−0
- library/Rattletrap/Type/Float32le.hs +13/−0
- library/Rattletrap/Type/FloatAttribute.hs +14/−0
- library/Rattletrap/Type/Frame.hs +20/−0
- library/Rattletrap/Type/GameModeAttribute.hs +14/−0
- library/Rattletrap/Type/Header.hs +60/−0
- library/Rattletrap/Type/Initialization.hs +20/−0
- library/Rattletrap/Type/Int32le.hs +13/−0
- library/Rattletrap/Type/Int8Vector.hs +16/−0
- library/Rattletrap/Type/Int8le.hs +13/−0
- library/Rattletrap/Type/IntAttribute.hs +14/−0
- library/Rattletrap/Type/KeyFrame.hs +20/−0
- library/Rattletrap/Type/List.hs +13/−0
- library/Rattletrap/Type/LoadoutAttribute.hs +28/−0
- library/Rattletrap/Type/LoadoutOnlineAttribute.hs +14/−0
- library/Rattletrap/Type/LoadoutsAttribute.hs +15/−0
- library/Rattletrap/Type/LoadoutsOnlineAttribute.hs +17/−0
- library/Rattletrap/Type/LocationAttribute.hs +14/−0
- library/Rattletrap/Type/Mark.hs +18/−0
- library/Rattletrap/Type/Message.hs +20/−0
- library/Rattletrap/Type/MusicStingerAttribute.hs +17/−0
- library/Rattletrap/Type/PartyLeaderAttribute.hs +16/−0
- library/Rattletrap/Type/PickupAttribute.hs +15/−0
- library/Rattletrap/Type/PlayerHistoryKeyAttribute.hs +14/−0
- library/Rattletrap/Type/PrivateMatchSettingsAttribute.hs +20/−0
- library/Rattletrap/Type/ProductAttribute.hs +20/−0
- library/Rattletrap/Type/Property.hs +19/−0
- library/Rattletrap/Type/PropertyValue.hs +31/−0
- library/Rattletrap/Type/QWordAttribute.hs +14/−0
- library/Rattletrap/Type/RemoteId.hs +19/−0
- library/Rattletrap/Type/Replay.hs +20/−0
- library/Rattletrap/Type/Replication.hs +16/−0
- library/Rattletrap/Type/ReplicationValue.hs +21/−0
- library/Rattletrap/Type/ReservationAttribute.hs +21/−0
- library/Rattletrap/Type/RigidBodyStateAttribute.hs +19/−0
- library/Rattletrap/Type/Section.hs +23/−0
- library/Rattletrap/Type/SpawnedReplication.hs +29/−0
- library/Rattletrap/Type/Str.hs +23/−0
- library/Rattletrap/Type/StringAttribute.hs +14/−0
- library/Rattletrap/Type/TeamPaintAttribute.hs +19/−0
- library/Rattletrap/Type/UniqueIdAttribute.hs +17/−0
- library/Rattletrap/Type/UpdatedReplication.hs +14/−0
- library/Rattletrap/Type/Vector.hs +16/−0
- library/Rattletrap/Type/WeldedInfoAttribute.hs +21/−0
- library/Rattletrap/Type/Word32le.hs +13/−0
- library/Rattletrap/Type/Word64le.hs +13/−0
- library/Rattletrap/Type/Word8le.hs +13/−0
- library/Rattletrap/Utility.hs +0/−30
- library/Rattletrap/Utility/Bytes.hs +30/−0
- library/Rattletrap/Utility/Crc.hs +46/−0
- library/Rattletrap/Utility/Helper.hs +38/−0
- library/Rattletrap/Version.hs +0/−7
- package.yaml +30/−34
- rattletrap.cabal +258/−186
- 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
- stack.yaml +1/−5
- tests/Main.hs +97/−96
@@ -1,13 +1,13 @@-# [The MIT License (MIT)][]+MIT License -Copyright (c) Taylor Fausak+Copyright (c) 2018 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:+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.@@ -19,5 +19,3 @@ 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
@@ -16,45 +16,82 @@ ## Replays -Rocket League saves your replays in a folder that depends on your operating system.+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`+- 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 4.0.0+ -c --compact minify JSON output+ -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+ -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 decode http://example.com/input.replay output.json+> rattletrap --input http://example.com/input.replay --output output.json # or-> rattletrap decode input.replay > output.json+> rattletrap -i input.replay > output.json # or-> rattletrap.decode < input.replay > output.json+> rattletrap < 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.+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 encode http://example.com/input.json output.replay+> rattletrap --input http://example.com/input.json --output output.replay # or-> rattletrap encode input.json > output.replay+> rattletrap -i input.json > output.replay # or-> rattletrap.encode < input.json > output.replay+> 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 resulting replay should be-identical to the original.+If the JSON was generated by Rattletrap, the output replay will be bit-for-bit+identical to the input replay. ## Modify @@ -62,17 +99,17 @@ used to modify replays. ``` sh-> rattletrap decode < original.replay |+> rattletrap -i input.replay | modify-replay-json |- rattletrap encode > modified.replay+ rattletrap -o output.replay ``` [Rattletrap]: https://github.com/tfausak/rattletrap-[Version badge]: https://www.stackage.org/package/rattletrap/badge/nightly?label=version+[Version badge]: https://img.shields.io/hackage/v/rattletrap.svg?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 badge]: https://img.shields.io/appveyor/ci/taylorfausak/rattletrap/master.svg?logo=appveyor [windows build]: https://ci.appveyor.com/project/TaylorFausak/rattletrap-[Build badge]: https://travis-ci.org/tfausak/rattletrap.svg?branch=master+[Build badge]: https://img.shields.io/travis/tfausak/rattletrap/master.svg [build]: https://travis-ci.org/tfausak/rattletrap [Rocket League]: https://www.rocketleaguegame.com [the latest release]: https://github.com/tfausak/rattletrap/releases/latest
@@ -1,5 +1,5 @@ module Main- ( module Rattletrap+ ( main ) where -import Rattletrap (main)+import Rattletrap.Console.Main (main)
@@ -1,87 +1,140 @@--- | 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+ ( Rattletrap.Utility.Helper.decodeReplayFile+ , Rattletrap.Utility.Helper.encodeReplayJson+ , Rattletrap.Utility.Helper.decodeReplayJson+ , Rattletrap.Utility.Helper.encodeReplayFile+ , Rattletrap.Type.Replay.Replay(..)+ , Rattletrap.Type.Section.Section(..)+ , Rattletrap.Type.Header.Header(..)+ , Rattletrap.Type.Word32le.Word32le(..)+ , Rattletrap.Type.Str.Str(..)+ , Rattletrap.Type.Dictionary.Dictionary(..)+ , Rattletrap.Type.Property.Property(..)+ , Rattletrap.Type.Word64le.Word64le(..)+ , Rattletrap.Type.PropertyValue.PropertyValue(..)+ , Rattletrap.Type.List.List(..)+ , Rattletrap.Type.Word8le.Word8le(..)+ , Rattletrap.Type.Float32le.Float32le(..)+ , Rattletrap.Type.Int32le.Int32le(..)+ , Rattletrap.Type.Content.Content(..)+ , Rattletrap.Type.KeyFrame.KeyFrame(..)+ , Rattletrap.Type.Frame.Frame(..)+ , Rattletrap.Type.Replication.Replication(..)+ , Rattletrap.Type.CompressedWord.CompressedWord(..)+ , Rattletrap.Type.ReplicationValue.ReplicationValue(..)+ , Rattletrap.Type.SpawnedReplication.SpawnedReplication(..)+ , Rattletrap.Type.Initialization.Initialization(..)+ , Rattletrap.Type.Vector.Vector(..)+ , Rattletrap.Type.Int8Vector.Int8Vector(..)+ , Rattletrap.Type.Int8le.Int8le(..)+ , Rattletrap.Type.UpdatedReplication.UpdatedReplication(..)+ , Rattletrap.Type.Attribute.Attribute(..)+ , Rattletrap.Type.AttributeValue.AttributeValue(..)+ , Rattletrap.Type.AppliedDamageAttribute.AppliedDamageAttribute(..)+ , Rattletrap.Type.BooleanAttribute.BooleanAttribute(..)+ , Rattletrap.Type.ByteAttribute.ByteAttribute(..)+ , Rattletrap.Type.CamSettingsAttribute.CamSettingsAttribute(..)+ , Rattletrap.Type.ClubColorsAttribute.ClubColorsAttribute(..)+ , Rattletrap.Type.DamageStateAttribute.DamageStateAttribute(..)+ , Rattletrap.Type.DemolishAttribute.DemolishAttribute(..)+ , Rattletrap.Type.EnumAttribute.EnumAttribute(..)+ , Rattletrap.Type.ExplosionAttribute.ExplosionAttribute(..)+ , Rattletrap.Type.ExtendedExplosionAttribute.ExtendedExplosionAttribute(..)+ , Rattletrap.Type.FlaggedIntAttribute.FlaggedIntAttribute(..)+ , Rattletrap.Type.FloatAttribute.FloatAttribute(..)+ , Rattletrap.Type.GameModeAttribute.GameModeAttribute(..)+ , Rattletrap.Type.IntAttribute.IntAttribute(..)+ , Rattletrap.Type.LoadoutAttribute.LoadoutAttribute(..)+ , Rattletrap.Type.LoadoutOnlineAttribute.LoadoutOnlineAttribute(..)+ , Rattletrap.Type.ProductAttribute.ProductAttribute(..)+ , Rattletrap.Type.LoadoutsAttribute.LoadoutsAttribute(..)+ , Rattletrap.Type.LoadoutsOnlineAttribute.LoadoutsOnlineAttribute(..)+ , Rattletrap.Type.LocationAttribute.LocationAttribute(..)+ , Rattletrap.Type.MusicStingerAttribute.MusicStingerAttribute(..)+ , Rattletrap.Type.PartyLeaderAttribute.PartyLeaderAttribute(..)+ , Rattletrap.Type.RemoteId.RemoteId(..)+ , Rattletrap.Type.PickupAttribute.PickupAttribute(..)+ , Rattletrap.Type.PrivateMatchSettingsAttribute.PrivateMatchSettingsAttribute(..)+ , Rattletrap.Type.QWordAttribute.QWordAttribute(..)+ , Rattletrap.Type.ReservationAttribute.ReservationAttribute(..)+ , Rattletrap.Type.UniqueIdAttribute.UniqueIdAttribute(..)+ , Rattletrap.Type.RigidBodyStateAttribute.RigidBodyStateAttribute(..)+ , Rattletrap.Type.CompressedWordVector.CompressedWordVector(..)+ , Rattletrap.Type.StringAttribute.StringAttribute(..)+ , Rattletrap.Type.TeamPaintAttribute.TeamPaintAttribute(..)+ , Rattletrap.Type.WeldedInfoAttribute.WeldedInfoAttribute(..)+ , Rattletrap.Type.DestroyedReplication.DestroyedReplication(..)+ , Rattletrap.Type.Message.Message(..)+ , Rattletrap.Type.Mark.Mark(..)+ , Rattletrap.Type.ClassMapping.ClassMapping(..)+ , Rattletrap.Type.Cache.Cache(..)+ , Rattletrap.Type.AttributeMapping.AttributeMapping(..) ) 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+import qualified Rattletrap.Type.AppliedDamageAttribute+import qualified Rattletrap.Type.Attribute+import qualified Rattletrap.Type.AttributeMapping+import qualified Rattletrap.Type.AttributeValue+import qualified Rattletrap.Type.BooleanAttribute+import qualified Rattletrap.Type.ByteAttribute+import qualified Rattletrap.Type.Cache+import qualified Rattletrap.Type.CamSettingsAttribute+import qualified Rattletrap.Type.ClassMapping+import qualified Rattletrap.Type.ClubColorsAttribute+import qualified Rattletrap.Type.CompressedWord+import qualified Rattletrap.Type.CompressedWordVector+import qualified Rattletrap.Type.Content+import qualified Rattletrap.Type.DamageStateAttribute+import qualified Rattletrap.Type.DemolishAttribute+import qualified Rattletrap.Type.DestroyedReplication+import qualified Rattletrap.Type.Dictionary+import qualified Rattletrap.Type.EnumAttribute+import qualified Rattletrap.Type.ExplosionAttribute+import qualified Rattletrap.Type.ExtendedExplosionAttribute+import qualified Rattletrap.Type.FlaggedIntAttribute+import qualified Rattletrap.Type.Float32le+import qualified Rattletrap.Type.FloatAttribute+import qualified Rattletrap.Type.Frame+import qualified Rattletrap.Type.GameModeAttribute+import qualified Rattletrap.Type.Header+import qualified Rattletrap.Type.Initialization+import qualified Rattletrap.Type.Int32le+import qualified Rattletrap.Type.Int8Vector+import qualified Rattletrap.Type.Int8le+import qualified Rattletrap.Type.IntAttribute+import qualified Rattletrap.Type.KeyFrame+import qualified Rattletrap.Type.List+import qualified Rattletrap.Type.LoadoutAttribute+import qualified Rattletrap.Type.LoadoutOnlineAttribute+import qualified Rattletrap.Type.LoadoutsAttribute+import qualified Rattletrap.Type.LoadoutsOnlineAttribute+import qualified Rattletrap.Type.LocationAttribute+import qualified Rattletrap.Type.Mark+import qualified Rattletrap.Type.Message+import qualified Rattletrap.Type.MusicStingerAttribute+import qualified Rattletrap.Type.PartyLeaderAttribute+import qualified Rattletrap.Type.PickupAttribute+import qualified Rattletrap.Type.PrivateMatchSettingsAttribute+import qualified Rattletrap.Type.ProductAttribute+import qualified Rattletrap.Type.Property+import qualified Rattletrap.Type.PropertyValue+import qualified Rattletrap.Type.QWordAttribute+import qualified Rattletrap.Type.RemoteId+import qualified Rattletrap.Type.Replay+import qualified Rattletrap.Type.Replication+import qualified Rattletrap.Type.ReplicationValue+import qualified Rattletrap.Type.ReservationAttribute+import qualified Rattletrap.Type.RigidBodyStateAttribute+import qualified Rattletrap.Type.Section+import qualified Rattletrap.Type.SpawnedReplication+import qualified Rattletrap.Type.Str+import qualified Rattletrap.Type.StringAttribute+import qualified Rattletrap.Type.TeamPaintAttribute+import qualified Rattletrap.Type.UniqueIdAttribute+import qualified Rattletrap.Type.UpdatedReplication+import qualified Rattletrap.Type.Vector+import qualified Rattletrap.Type.WeldedInfoAttribute+import qualified Rattletrap.Type.Word32le+import qualified Rattletrap.Type.Word64le+import qualified Rattletrap.Type.Word8le+import qualified Rattletrap.Utility.Helper
@@ -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)
@@ -0,0 +1,226 @@+module Rattletrap.Console.Main+ ( main+ , rattletrap+ ) where++import qualified Control.Monad as Monad+import qualified Data.Aeson as Json+import qualified Data.ByteString.Lazy as LazyBytes+import qualified Data.Version as Version+import qualified Network.HTTP.Client as Client+import qualified Network.HTTP.Client.TLS as Client+import qualified Paths_rattletrap as This+import qualified Rattletrap+import qualified System.Console.GetOpt as Console+import qualified System.Environment as Environment+import qualified System.Exit as Exit+import qualified System.FilePath as Path+import qualified System.IO as IO+import qualified Text.Printf as Printf++main :: IO ()+main = do+ name <- Environment.getProgName+ arguments <- Environment.getArgs+ rattletrap name arguments++rattletrap :: String -> [String] -> IO ()+rattletrap name arguments = do+ config <- getConfig arguments+ Monad.when (configHelp config) (printHelp name *> Exit.exitFailure)+ Monad.when (configVersion config) (printVersion *> Exit.exitFailure)+ input <- getInput config+ let decode = getDecoder config+ replay <- either fail pure (decode input)+ let encode = getEncoder config+ putOutput config (encode replay)++getDecoder :: Config -> LazyBytes.ByteString -> Either String Rattletrap.Replay+getDecoder config = case getMode config of+ ModeDecode -> Rattletrap.decodeReplayFile+ ModeEncode -> Rattletrap.decodeReplayJson++getEncoder :: Config -> Rattletrap.Replay -> LazyBytes.ByteString+getEncoder config = case getMode config of+ ModeDecode ->+ if configCompact config then Json.encode else Rattletrap.encodeReplayJson+ ModeEncode -> Rattletrap.encodeReplayFile++getInput :: Config -> IO LazyBytes.ByteString+getInput config = case configInput config of+ Nothing -> LazyBytes.getContents+ Just fileOrUrl -> case Client.parseUrlThrow fileOrUrl of+ Nothing -> LazyBytes.readFile fileOrUrl+ Just request -> do+ manager <- Client.newTlsManager+ response <- Client.httpLbs request manager+ pure (Client.responseBody response)++putOutput :: Config -> LazyBytes.ByteString -> IO ()+putOutput config = case configOutput config of+ Nothing -> LazyBytes.putStr+ Just file -> LazyBytes.writeFile file++getConfig :: [String] -> IO Config+getConfig arguments = do+ let+ (updates, unexpectedArguments, unknownOptions, problems) =+ Console.getOpt' Console.Permute options arguments+ printUnexpectedArguments unexpectedArguments+ printUnknownOptions unknownOptions+ printProblems problems+ Monad.unless (null problems) Exit.exitFailure+ either fail pure (Monad.foldM applyUpdate defaultConfig updates)++type Option = Console.OptDescr Update++type Update = Config -> Either String Config++options :: [Option]+options =+ [ compactOption+ , helpOption+ , inputOption+ , modeOption+ , outputOption+ , versionOption+ ]++compactOption :: Option+compactOption = Console.Option+ ['c']+ ["compact"]+ (Console.NoArg (\config -> pure config { configCompact = True }))+ "minify JSON output"++helpOption :: Option+helpOption = Console.Option+ ['h']+ ["help"]+ (Console.NoArg (\config -> pure config { configHelp = True }))+ "show the help"++inputOption :: Option+inputOption = Console.Option+ ['i']+ ["input"]+ ( Console.ReqArg+ (\input config -> pure config { configInput = Just input })+ "FILE|URL"+ )+ "input file or URL"++modeOption :: Option+modeOption = Console.Option+ ['m']+ ["mode"]+ ( Console.ReqArg+ ( \rawMode config -> do+ mode <- parseMode rawMode+ pure config { configMode = Just mode }+ )+ "MODE"+ )+ "decode or encode"++outputOption :: Option+outputOption = Console.Option+ ['o']+ ["output"]+ ( Console.ReqArg+ (\output config -> pure config { configOutput = Just output })+ "FILE"+ )+ "output file"++versionOption :: Option+versionOption = Console.Option+ ['v']+ ["version"]+ (Console.NoArg (\config -> pure config { configVersion = True }))+ "show the version"++applyUpdate :: Config -> Update -> Either String Config+applyUpdate config update = update config++data Config = Config+ { configCompact :: Bool+ , configHelp :: Bool+ , configInput :: Maybe String+ , configMode :: Maybe Mode+ , configOutput :: Maybe String+ , configVersion :: Bool+ } deriving (Show)++defaultConfig :: Config+defaultConfig = Config+ { configCompact = False+ , configHelp = False+ , configInput = Nothing+ , configMode = Nothing+ , configOutput = Nothing+ , configVersion = False+ }++getMode :: Config -> Mode+getMode config = case getExtension (configInput config) of+ ".json" -> ModeEncode+ ".replay" -> ModeDecode+ _ -> case getExtension (configOutput config) of+ ".json" -> ModeDecode+ ".replay" -> ModeEncode+ _ -> ModeDecode++getExtension :: Maybe String -> String+getExtension = maybe "" Path.takeExtension++data Mode+ = ModeDecode+ | ModeEncode+ deriving (Show)++parseMode :: String -> Either String Mode+parseMode mode = case mode of+ "decode" -> pure ModeDecode+ "encode" -> pure ModeEncode+ _ -> fail (Printf.printf "invalid mode: %s" (show mode))++printUnexpectedArguments :: [String] -> IO ()+printUnexpectedArguments = mapM_ printUnexpectedArgument++printUnexpectedArgument :: String -> IO ()+printUnexpectedArgument =+ warnLn . Printf.printf "WARNING: unexpected argument `%s'"++printUnknownOptions :: [String] -> IO ()+printUnknownOptions = mapM_ printUnknownOption++printUnknownOption :: String -> IO ()+printUnknownOption = warnLn . Printf.printf "WARNING: unknown option `%s'"++printProblems :: [String] -> IO ()+printProblems = mapM_ printProblem++printProblem :: String -> IO ()+printProblem = warn . Printf.printf "ERROR: %s"++printHelp :: String -> IO ()+printHelp = warn . help++help :: String -> String+help name = Console.usageInfo (header name) options++header :: String -> String+header name = unwords [name, "version", version]++version :: String+version = Version.showVersion This.version++printVersion :: IO ()+printVersion = warnLn version++warn :: String -> IO ()+warn = IO.hPutStr IO.stderr++warnLn :: String -> IO ()+warnLn = IO.hPutStrLn IO.stderr
@@ -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,6 +1,6 @@ module Rattletrap.Data where -import Rattletrap.AttributeType+import Rattletrap.Type.AttributeType rawParentClasses :: [(String, String)] rawParentClasses =@@ -78,7 +78,6 @@ , "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"@@ -156,151 +155,152 @@ 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)+ [ ("Engine.Actor:bBlockActors", AttributeTypeBoolean)+ , ("Engine.Actor:bCollideActors", AttributeTypeBoolean)+ , ("Engine.Actor:bHidden", AttributeTypeBoolean)+ , ("Engine.Actor:DrawScale", AttributeTypeFloat)+ , ("Engine.Actor:Role", AttributeTypeEnum)+ , ("Engine.GameReplicationInfo:bMatchIsOver", AttributeTypeBoolean)+ , ("Engine.GameReplicationInfo:GameClass", AttributeTypeFlaggedInt)+ , ("Engine.GameReplicationInfo:ServerName", AttributeTypeString)+ , ("Engine.Pawn:PlayerReplicationInfo", AttributeTypeFlaggedInt)+ , ("Engine.PlayerReplicationInfo:bBot", AttributeTypeBoolean)+ , ("Engine.PlayerReplicationInfo:bIsSpectator", AttributeTypeBoolean)+ , ("Engine.PlayerReplicationInfo:bReadyToPlay", AttributeTypeBoolean)+ , ("Engine.PlayerReplicationInfo:bWaitingPlayer", AttributeTypeBoolean)+ , ("Engine.PlayerReplicationInfo:Ping", AttributeTypeByte)+ , ("Engine.PlayerReplicationInfo:PlayerID", AttributeTypeInt)+ , ("Engine.PlayerReplicationInfo:PlayerName", AttributeTypeString)+ , ("Engine.PlayerReplicationInfo:RemoteUserData", AttributeTypeString)+ , ("Engine.PlayerReplicationInfo:Score", AttributeTypeInt)+ , ("Engine.PlayerReplicationInfo:Team", AttributeTypeFlaggedInt)+ , ("Engine.PlayerReplicationInfo:UniqueId", AttributeTypeUniqueId)+ , ("Engine.TeamInfo:Score", AttributeTypeInt)+ , ("ProjectX.GRI_X:bGameStarted", AttributeTypeBoolean)+ , ("ProjectX.GRI_X:GameServerID", AttributeTypeQWord)+ , ("ProjectX.GRI_X:MatchGUID", AttributeTypeString)+ , ("ProjectX.GRI_X:ReplicatedGameMutatorIndex", AttributeTypeInt)+ , ("ProjectX.GRI_X:ReplicatedGamePlaylist", AttributeTypeInt)+ , ("ProjectX.GRI_X:Reservations", AttributeTypeReservation)+ , ("TAGame.Ball_Breakout_TA:AppliedDamage", AttributeTypeAppliedDamage)+ , ("TAGame.Ball_Breakout_TA:DamageIndex", AttributeTypeInt)+ , ("TAGame.Ball_Breakout_TA:LastTeamTouch", AttributeTypeByte)+ , ("TAGame.Ball_TA:GameEvent", AttributeTypeFlaggedInt)+ , ("TAGame.Ball_TA:HitTeamNum", AttributeTypeByte)+ , ("TAGame.Ball_TA:ReplicatedAddedCarBounceScale", AttributeTypeFloat)+ , ("TAGame.Ball_TA:ReplicatedBallMaxLinearSpeedScale", AttributeTypeFloat)+ , ("TAGame.Ball_TA:ReplicatedBallScale", AttributeTypeFloat)+ , ("TAGame.Ball_TA:ReplicatedExplosionData", AttributeTypeExplosion)+ , ("TAGame.Ball_TA:ReplicatedExplosionDataExtended", AttributeTypeExtendedExplosion)+ , ("TAGame.Ball_TA:ReplicatedWorldBounceScale", AttributeTypeFloat)+ , ("TAGame.BreakOutActor_Platform_TA:DamageState", AttributeTypeDamageState)+ , ("TAGame.CameraSettingsActor_TA:bUsingBehindView", AttributeTypeBoolean)+ , ("TAGame.CameraSettingsActor_TA:bUsingSecondaryCamera", AttributeTypeBoolean)+ , ("TAGame.CameraSettingsActor_TA:CameraPitch", AttributeTypeByte)+ , ("TAGame.CameraSettingsActor_TA:CameraYaw", AttributeTypeByte)+ , ("TAGame.CameraSettingsActor_TA:PRI", AttributeTypeFlaggedInt)+ , ("TAGame.CameraSettingsActor_TA:ProfileSettings", AttributeTypeCamSettings)+ , ("TAGame.Car_TA:AddedBallForceMultiplier", AttributeTypeFloat)+ , ("TAGame.Car_TA:AddedCarForceMultiplier", AttributeTypeFloat)+ , ("TAGame.Car_TA:AttachedPickup", AttributeTypeFlaggedInt)+ , ("TAGame.Car_TA:ClubColors", AttributeTypeClubColors)+ , ("TAGame.Car_TA:ReplicatedDemolish", AttributeTypeDemolish)+ , ("TAGame.Car_TA:TeamPaint", AttributeTypeTeamPaint)+ , ("TAGame.CarComponent_Boost_TA:bNoBoost", AttributeTypeBoolean)+ , ("TAGame.CarComponent_Boost_TA:BoostModifier", AttributeTypeFloat)+ , ("TAGame.CarComponent_Boost_TA:bUnlimitedBoost", AttributeTypeBoolean)+ , ("TAGame.CarComponent_Boost_TA:RechargeDelay", AttributeTypeFloat)+ , ("TAGame.CarComponent_Boost_TA:RechargeRate", AttributeTypeFloat)+ , ("TAGame.CarComponent_Boost_TA:ReplicatedBoostAmount", AttributeTypeByte)+ , ("TAGame.CarComponent_Boost_TA:UnlimitedBoostRefCount", AttributeTypeInt)+ , ("TAGame.CarComponent_Dodge_TA:DodgeTorque", AttributeTypeLocation)+ , ("TAGame.CarComponent_FlipCar_TA:bFlipRight", AttributeTypeBoolean)+ , ("TAGame.CarComponent_FlipCar_TA:FlipCarTime", AttributeTypeFloat)+ , ("TAGame.CarComponent_TA:ReplicatedActive", AttributeTypeByte)+ , ("TAGame.CarComponent_TA:ReplicatedActivityTime", AttributeTypeFloat)+ , ("TAGame.CarComponent_TA:Vehicle", AttributeTypeFlaggedInt)+ , ("TAGame.CrowdActor_TA:GameEvent", AttributeTypeFlaggedInt)+ , ("TAGame.CrowdActor_TA:ModifiedNoise", AttributeTypeFloat)+ , ("TAGame.CrowdActor_TA:ReplicatedCountDownNumber", AttributeTypeInt)+ , ("TAGame.CrowdActor_TA:ReplicatedOneShotSound", AttributeTypeFlaggedInt)+ , ("TAGame.CrowdActor_TA:ReplicatedRoundCountDownNumber", AttributeTypeInt)+ , ("TAGame.CrowdManager_TA:GameEvent", AttributeTypeFlaggedInt)+ , ("TAGame.CrowdManager_TA:ReplicatedGlobalOneShotSound", AttributeTypeFlaggedInt)+ , ("TAGame.GameEvent_Soccar_TA:bBallHasBeenHit", AttributeTypeBoolean)+ , ("TAGame.GameEvent_Soccar_TA:bOverTime", AttributeTypeBoolean)+ , ("TAGame.GameEvent_Soccar_TA:GameTime", AttributeTypeInt)+ , ("TAGame.GameEvent_Soccar_TA:ReplicatedMusicStinger", AttributeTypeMusicStinger)+ , ("TAGame.GameEvent_Soccar_TA:ReplicatedScoredOnTeam", AttributeTypeByte)+ , ("TAGame.GameEvent_Soccar_TA:RoundNum", AttributeTypeInt)+ , ("TAGame.GameEvent_Soccar_TA:SecondsRemaining", AttributeTypeInt)+ , ("TAGame.GameEvent_Soccar_TA:SubRulesArchetype", AttributeTypeFlaggedInt)+ , ("TAGame.GameEvent_SoccarPrivate_TA:MatchSettings", AttributeTypePrivateMatchSettings)+ , ("TAGame.GameEvent_TA:bCanVoteToForfeit", AttributeTypeBoolean)+ , ("TAGame.GameEvent_TA:bHasLeaveMatchPenalty", AttributeTypeBoolean)+ , ("TAGame.GameEvent_TA:BotSkill", AttributeTypeInt)+ , ("TAGame.GameEvent_TA:GameMode", AttributeTypeGameMode)+ , ("TAGame.GameEvent_TA:MatchTypeClass", AttributeTypeFlaggedInt)+ , ("TAGame.GameEvent_TA:ReplicatedGameStateTimeRemaining", AttributeTypeInt)+ , ("TAGame.GameEvent_TA:ReplicatedStateIndex", AttributeTypeByte)+ , ("TAGame.GameEvent_TA:ReplicatedStateName", AttributeTypeInt)+ , ("TAGame.GameEvent_Team_TA:bForfeit", AttributeTypeBoolean)+ , ("TAGame.GameEvent_Team_TA:MaxTeamSize", AttributeTypeInt)+ , ("TAGame.GRI_TA:NewDedicatedServerIP", AttributeTypeString)+ , ("TAGame.PRI_TA:bIsInSplitScreen", AttributeTypeBoolean)+ , ("TAGame.PRI_TA:bMatchMVP", AttributeTypeBoolean)+ , ("TAGame.PRI_TA:bOnlineLoadoutSet", AttributeTypeBoolean)+ , ("TAGame.PRI_TA:bOnlineLoadoutsSet", AttributeTypeBoolean)+ , ("TAGame.PRI_TA:BotProductName", AttributeTypeInt)+ , ("TAGame.PRI_TA:bReady", AttributeTypeBoolean)+ , ("TAGame.PRI_TA:bUsingBehindView", AttributeTypeBoolean)+ , ("TAGame.PRI_TA:bUsingItems", AttributeTypeBoolean)+ , ("TAGame.PRI_TA:bUsingSecondaryCamera", AttributeTypeBoolean)+ , ("TAGame.PRI_TA:CameraPitch", AttributeTypeByte)+ , ("TAGame.PRI_TA:CameraSettings", AttributeTypeCamSettings)+ , ("TAGame.PRI_TA:CameraYaw", AttributeTypeByte)+ , ("TAGame.PRI_TA:ClientLoadout", AttributeTypeLoadout)+ , ("TAGame.PRI_TA:ClientLoadoutOnline", AttributeTypeLoadoutOnline)+ , ("TAGame.PRI_TA:ClientLoadouts", AttributeTypeLoadouts)+ , ("TAGame.PRI_TA:ClientLoadoutsOnline", AttributeTypeLoadoutsOnline)+ , ("TAGame.PRI_TA:MatchAssists", AttributeTypeInt)+ , ("TAGame.PRI_TA:MatchBreakoutDamage", AttributeTypeInt)+ , ("TAGame.PRI_TA:MatchGoals", AttributeTypeInt)+ , ("TAGame.PRI_TA:MatchSaves", AttributeTypeInt)+ , ("TAGame.PRI_TA:MatchScore", AttributeTypeInt)+ , ("TAGame.PRI_TA:MatchShots", AttributeTypeInt)+ , ("TAGame.PRI_TA:MaxTimeTillItem", AttributeTypeInt)+ , ("TAGame.PRI_TA:PartyLeader", AttributeTypePartyLeader)+ , ("TAGame.PRI_TA:PawnType", AttributeTypeByte)+ , ("TAGame.PRI_TA:PersistentCamera", AttributeTypeFlaggedInt)+ , ("TAGame.PRI_TA:PlayerHistoryKey", AttributeTypePlayerHistoryKey)+ , ("TAGame.PRI_TA:PlayerHistoryValid", AttributeTypeBoolean)+ , ("TAGame.PRI_TA:ReplicatedGameEvent", AttributeTypeFlaggedInt)+ , ("TAGame.PRI_TA:SteeringSensitivity", AttributeTypeFloat)+ , ("TAGame.PRI_TA:TimeTillItem", AttributeTypeInt)+ , ("TAGame.PRI_TA:Title", AttributeTypeInt)+ , ("TAGame.PRI_TA:TotalXP", AttributeTypeInt)+ , ("TAGame.RBActor_TA:bFrozen", AttributeTypeBoolean)+ , ("TAGame.RBActor_TA:bIgnoreSyncing", AttributeTypeBoolean)+ , ("TAGame.RBActor_TA:bReplayActor", AttributeTypeBoolean)+ , ("TAGame.RBActor_TA:ReplicatedRBState", AttributeTypeRigidBodyState)+ , ("TAGame.RBActor_TA:WeldedInfo", AttributeTypeWeldedInfo)+ , ("TAGame.SpecialPickup_BallFreeze_TA:RepOrigSpeed", AttributeTypeFloat)+ , ("TAGame.SpecialPickup_BallVelcro_TA:AttachTime", AttributeTypeFloat)+ , ("TAGame.SpecialPickup_BallVelcro_TA:bBroken", AttributeTypeBoolean)+ , ("TAGame.SpecialPickup_BallVelcro_TA:bHit", AttributeTypeBoolean)+ , ("TAGame.SpecialPickup_BallVelcro_TA:BreakTime", AttributeTypeFloat)+ , ("TAGame.SpecialPickup_Targeted_TA:Targeted", AttributeTypeFlaggedInt)+ , ("TAGame.Team_Soccar_TA:GameScore", AttributeTypeInt)+ , ("TAGame.Team_TA:ClubColors", AttributeTypeClubColors)+ , ("TAGame.Team_TA:CustomTeamName", AttributeTypeString)+ , ("TAGame.Team_TA:GameEvent", AttributeTypeFlaggedInt)+ , ("TAGame.Team_TA:LogoData", AttributeTypeFlaggedInt)+ , ("TAGame.Vehicle_TA:bDriving", AttributeTypeBoolean)+ , ("TAGame.Vehicle_TA:bReplicatedHandbrake", AttributeTypeBoolean)+ , ("TAGame.Vehicle_TA:ReplicatedSteer", AttributeTypeByte)+ , ("TAGame.Vehicle_TA:ReplicatedThrottle", AttributeTypeByte)+ , ("TAGame.VehiclePickup_TA:bNoPickup", AttributeTypeBoolean)+ , ("TAGame.VehiclePickup_TA:ReplicatedPickupData", AttributeTypePickup) ] rawCrc32Table :: Integral a => [a]
@@ -0,0 +1,17 @@+module Rattletrap.Decode.AppliedDamageAttribute+ ( decodeAppliedDamageAttributeBits+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Decode.Int32le+import Rattletrap.Decode.Vector+import Rattletrap.Decode.Word8le+import Rattletrap.Type.AppliedDamageAttribute++decodeAppliedDamageAttributeBits :: DecodeBits AppliedDamageAttribute+decodeAppliedDamageAttributeBits =+ AppliedDamageAttribute+ <$> decodeWord8leBits+ <*> decodeVectorBits+ <*> decodeInt32leBits+ <*> decodeInt32leBits
@@ -0,0 +1,74 @@+module Rattletrap.Decode.Attribute+ ( decodeAttributesBits+ , decodeAttributeBits+ ) where++import Data.Semigroup ((<>))+import Rattletrap.Decode.AttributeValue+import Rattletrap.Decode.Common+import Rattletrap.Decode.CompressedWord+import Rattletrap.Type.Attribute+import Rattletrap.Type.ClassAttributeMap+import Rattletrap.Type.Common+import Rattletrap.Type.CompressedWord+import Rattletrap.Type.Str+import Rattletrap.Type.Word32le++decodeAttributesBits+ :: (Int, Int, Int)+ -> ClassAttributeMap+ -> Map CompressedWord Word32le+ -> CompressedWord+ -> DecodeBits [Attribute]+decodeAttributesBits version classes actors actor = do+ hasAttribute <- getBool+ if hasAttribute+ then+ (:)+ <$> decodeAttributeBits version classes actors actor+ <*> decodeAttributesBits version classes actors actor+ else pure []++decodeAttributeBits+ :: (Int, Int, Int)+ -> ClassAttributeMap+ -> Map CompressedWord Word32le+ -> CompressedWord+ -> DecodeBits Attribute+decodeAttributeBits version classes actors actor = do+ attributes <- lookupAttributeMap classes actors actor+ limit <- lookupAttributeIdLimit attributes actor+ attribute <- decodeCompressedWordBits limit+ name <- lookupAttributeName classes attributes attribute+ Attribute attribute name+ <$> decodeAttributeValueBits+ version+ (classAttributeMapObjectMap classes)+ name++lookupAttributeMap+ :: ClassAttributeMap+ -> Map CompressedWord Word32le+ -> CompressedWord+ -> DecodeBits (Map Word32le Word32le)+lookupAttributeMap classes actors actor = fromMaybe+ ("could not get attribute map for " <> show actor)+ (getAttributeMap classes actors actor)++lookupAttributeIdLimit+ :: Map Word32le Word32le -> CompressedWord -> DecodeBits Word+lookupAttributeIdLimit attributes actor = fromMaybe+ ("could not get attribute ID limit for " <> show actor)+ (getAttributeIdLimit attributes)++lookupAttributeName+ :: ClassAttributeMap+ -> Map Word32le Word32le+ -> CompressedWord+ -> DecodeBits Str+lookupAttributeName classes attributes attribute = fromMaybe+ ("could not get attribute name for " <> show attribute)+ (getAttributeName classes attributes attribute)++fromMaybe :: String -> Maybe a -> DecodeBits a+fromMaybe message = maybe (fail message) pure
@@ -0,0 +1,11 @@+module Rattletrap.Decode.AttributeMapping+ ( decodeAttributeMapping+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Decode.Word32le+import Rattletrap.Type.AttributeMapping++decodeAttributeMapping :: Decode AttributeMapping+decodeAttributeMapping =+ AttributeMapping <$> decodeWord32le <*> decodeWord32le
@@ -0,0 +1,115 @@+module Rattletrap.Decode.AttributeValue+ ( decodeAttributeValueBits+ ) where++import Data.Semigroup ((<>))+import Rattletrap.Data+import Rattletrap.Decode.AppliedDamageAttribute+import Rattletrap.Decode.BooleanAttribute+import Rattletrap.Decode.ByteAttribute+import Rattletrap.Decode.CamSettingsAttribute+import Rattletrap.Decode.ClubColorsAttribute+import Rattletrap.Decode.Common+import Rattletrap.Decode.DamageStateAttribute+import Rattletrap.Decode.DemolishAttribute+import Rattletrap.Decode.EnumAttribute+import Rattletrap.Decode.ExplosionAttribute+import Rattletrap.Decode.ExtendedExplosionAttribute+import Rattletrap.Decode.FlaggedIntAttribute+import Rattletrap.Decode.FloatAttribute+import Rattletrap.Decode.GameModeAttribute+import Rattletrap.Decode.IntAttribute+import Rattletrap.Decode.LoadoutAttribute+import Rattletrap.Decode.LoadoutOnlineAttribute+import Rattletrap.Decode.LoadoutsAttribute+import Rattletrap.Decode.LoadoutsOnlineAttribute+import Rattletrap.Decode.LocationAttribute+import Rattletrap.Decode.MusicStingerAttribute+import Rattletrap.Decode.PartyLeaderAttribute+import Rattletrap.Decode.PickupAttribute+import Rattletrap.Decode.PlayerHistoryKeyAttribute+import Rattletrap.Decode.PrivateMatchSettingsAttribute+import Rattletrap.Decode.QWordAttribute+import Rattletrap.Decode.ReservationAttribute+import Rattletrap.Decode.RigidBodyStateAttribute+import Rattletrap.Decode.StringAttribute+import Rattletrap.Decode.TeamPaintAttribute+import Rattletrap.Decode.UniqueIdAttribute+import Rattletrap.Decode.WeldedInfoAttribute+import Rattletrap.Type.AttributeType+import Rattletrap.Type.AttributeValue+import Rattletrap.Type.Common+import Rattletrap.Type.Str+import Rattletrap.Type.Word32le++import qualified Data.Map as Map++decodeAttributeValueBits+ :: (Int, Int, Int) -> Map Word32le Str -> Str -> DecodeBits AttributeValue+decodeAttributeValueBits version objectMap name = do+ constructor <- maybe+ (fail ("don't know how to get attribute value " <> show name))+ pure+ (Map.lookup name attributeTypes)+ case constructor of+ AttributeTypeAppliedDamage ->+ AttributeValueAppliedDamage <$> decodeAppliedDamageAttributeBits+ AttributeTypeBoolean ->+ AttributeValueBoolean <$> decodeBooleanAttributeBits+ AttributeTypeByte -> AttributeValueByte <$> decodeByteAttributeBits+ AttributeTypeCamSettings ->+ AttributeValueCamSettings <$> decodeCamSettingsAttributeBits version+ AttributeTypeClubColors ->+ AttributeValueClubColors <$> decodeClubColorsAttributeBits+ AttributeTypeDamageState ->+ AttributeValueDamageState <$> decodeDamageStateAttributeBits+ AttributeTypeDemolish ->+ AttributeValueDemolish <$> decodeDemolishAttributeBits+ AttributeTypeEnum -> AttributeValueEnum <$> decodeEnumAttributeBits+ AttributeTypeExplosion ->+ AttributeValueExplosion <$> decodeExplosionAttributeBits+ AttributeTypeExtendedExplosion ->+ AttributeValueExtendedExplosion <$> decodeExtendedExplosionAttributeBits+ AttributeTypeFlaggedInt ->+ AttributeValueFlaggedInt <$> decodeFlaggedIntAttributeBits+ AttributeTypeFloat -> AttributeValueFloat <$> decodeFloatAttributeBits+ AttributeTypeGameMode ->+ AttributeValueGameMode <$> decodeGameModeAttributeBits version+ AttributeTypeInt -> AttributeValueInt <$> decodeIntAttributeBits+ AttributeTypeLoadout ->+ AttributeValueLoadout <$> decodeLoadoutAttributeBits+ AttributeTypeLoadoutOnline ->+ AttributeValueLoadoutOnline+ <$> decodeLoadoutOnlineAttributeBits version objectMap+ AttributeTypeLoadouts ->+ AttributeValueLoadouts <$> decodeLoadoutsAttributeBits+ AttributeTypeLoadoutsOnline ->+ AttributeValueLoadoutsOnline+ <$> decodeLoadoutsOnlineAttributeBits version objectMap+ AttributeTypeLocation ->+ AttributeValueLocation <$> decodeLocationAttributeBits+ AttributeTypeMusicStinger ->+ AttributeValueMusicStinger <$> decodeMusicStingerAttributeBits+ AttributeTypePartyLeader ->+ AttributeValuePartyLeader <$> decodePartyLeaderAttributeBits version+ AttributeTypePickup -> AttributeValuePickup <$> decodePickupAttributeBits+ AttributeTypePlayerHistoryKey ->+ AttributeValuePlayerHistoryKey <$> decodePlayerHistoryKeyAttributeBits+ AttributeTypePrivateMatchSettings ->+ AttributeValuePrivateMatchSettings+ <$> decodePrivateMatchSettingsAttributeBits+ AttributeTypeQWord -> AttributeValueQWord <$> decodeQWordAttributeBits+ AttributeTypeReservation ->+ AttributeValueReservation <$> decodeReservationAttributeBits version+ AttributeTypeRigidBodyState ->+ AttributeValueRigidBodyState <$> decodeRigidBodyStateAttributeBits+ AttributeTypeString -> AttributeValueString <$> decodeStringAttributeBits+ AttributeTypeTeamPaint ->+ AttributeValueTeamPaint <$> decodeTeamPaintAttributeBits+ AttributeTypeUniqueId ->+ AttributeValueUniqueId <$> decodeUniqueIdAttributeBits version+ AttributeTypeWeldedInfo ->+ AttributeValueWeldedInfo <$> decodeWeldedInfoAttributeBits++attributeTypes :: Map Str AttributeType+attributeTypes = Map.mapKeys toStr (Map.fromList rawAttributeTypes)
@@ -0,0 +1,11 @@+module Rattletrap.Decode.Bitstream+ ( decodeBitstreamBits+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Type.Bitstream++import qualified Control.Monad as Monad++decodeBitstreamBits :: Int -> DecodeBits Bitstream+decodeBitstreamBits n = Bitstream <$> Monad.replicateM n getBool
@@ -0,0 +1,9 @@+module Rattletrap.Decode.BooleanAttribute+ ( decodeBooleanAttributeBits+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Type.BooleanAttribute++decodeBooleanAttributeBits :: DecodeBits BooleanAttribute+decodeBooleanAttributeBits = BooleanAttribute <$> getBool
@@ -0,0 +1,10 @@+module Rattletrap.Decode.ByteAttribute+ ( decodeByteAttributeBits+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Decode.Word8le+import Rattletrap.Type.ByteAttribute++decodeByteAttributeBits :: DecodeBits ByteAttribute+decodeByteAttributeBits = ByteAttribute <$> decodeWord8leBits
@@ -0,0 +1,17 @@+module Rattletrap.Decode.Cache+ ( decodeCache+ ) where++import Rattletrap.Decode.AttributeMapping+import Rattletrap.Decode.Common+import Rattletrap.Decode.List+import Rattletrap.Decode.Word32le+import Rattletrap.Type.Cache++decodeCache :: Decode Cache+decodeCache =+ Cache+ <$> decodeWord32le+ <*> decodeWord32le+ <*> decodeWord32le+ <*> decodeList decodeAttributeMapping
@@ -0,0 +1,19 @@+module Rattletrap.Decode.CamSettingsAttribute+ ( decodeCamSettingsAttributeBits+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Decode.Float32le+import Rattletrap.Type.CamSettingsAttribute++decodeCamSettingsAttributeBits+ :: (Int, Int, Int) -> DecodeBits CamSettingsAttribute+decodeCamSettingsAttributeBits version =+ CamSettingsAttribute+ <$> decodeFloat32leBits+ <*> decodeFloat32leBits+ <*> decodeFloat32leBits+ <*> decodeFloat32leBits+ <*> decodeFloat32leBits+ <*> decodeFloat32leBits+ <*> decodeWhen (version >= (868, 20, 0)) decodeFloat32leBits
@@ -0,0 +1,11 @@+module Rattletrap.Decode.ClassMapping+ ( decodeClassMapping+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Decode.Str+import Rattletrap.Decode.Word32le+import Rattletrap.Type.ClassMapping++decodeClassMapping :: Decode ClassMapping+decodeClassMapping = ClassMapping <$> decodeStr <*> decodeWord32le
@@ -0,0 +1,15 @@+module Rattletrap.Decode.ClubColorsAttribute+ ( decodeClubColorsAttributeBits+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Decode.Word8le+import Rattletrap.Type.ClubColorsAttribute++decodeClubColorsAttributeBits :: DecodeBits ClubColorsAttribute+decodeClubColorsAttributeBits =+ ClubColorsAttribute+ <$> getBool+ <*> decodeWord8leBits+ <*> getBool+ <*> decodeWord8leBits
@@ -0,0 +1,55 @@+module Rattletrap.Decode.Common+ ( Decode+ , DecodeBits+ , decodeWhen+ , getLazyByteStringBits+ , getWord8Bits+ , runDecode+ , runDecodeBits+ , toBits+ , Binary.getFloatle+ , Binary.getLazyByteString+ , Binary.getInt8+ , Binary.getInt32le+ , Binary.getWord8+ , Binary.getWord32le+ , Binary.getWord64le+ , BinaryBits.getBool+ , BinaryBits.getWord16be+ , BinaryBits.getWord32be+ ) where++import qualified Control.Applicative as Applicative+import qualified Data.Binary as Binary+import qualified Data.Binary.Bits.Get as BinaryBits+import qualified Data.Binary.Get as Binary+import qualified Data.ByteString.Lazy as LazyBytes+import qualified Data.Word as Word+import qualified Rattletrap.Utility.Bytes as Utility++type Decode = Binary.Get++type DecodeBits = BinaryBits.BitGet++decodeWhen+ :: (Applicative m, Applicative.Alternative f) => Bool -> m a -> m (f a)+decodeWhen p f = if p then fmap pure f else pure Applicative.empty++getLazyByteStringBits :: Int -> DecodeBits LazyBytes.ByteString+getLazyByteStringBits = BinaryBits.getLazyByteString++getWord8Bits :: Int -> DecodeBits Word.Word8+getWord8Bits = BinaryBits.getWord8++runDecode :: Decode a -> LazyBytes.ByteString -> Either String a+runDecode decode bytes = case Binary.runGetOrFail decode bytes of+ Left (_, _, x) -> fail x+ Right (_, _, x) -> pure x++runDecodeBits :: DecodeBits a -> LazyBytes.ByteString -> Either String a+runDecodeBits = runDecode . BinaryBits.runBitGet++toBits :: Decode a -> Int -> DecodeBits a+toBits decode =+ fmap (Binary.runGet decode . Utility.reverseBytes)+ . BinaryBits.getLazyByteString
@@ -0,0 +1,29 @@+module Rattletrap.Decode.CompressedWord+ ( decodeCompressedWordBits+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Type.CompressedWord++import qualified Data.Bits as Bits++decodeCompressedWordBits :: Word -> DecodeBits CompressedWord+decodeCompressedWordBits limit =+ CompressedWord limit <$> step limit (getMaxBits limit) 0 0++getMaxBits :: Word -> Word+getMaxBits x = do+ let+ n :: Word+ n = max 1 (ceiling (logBase (2 :: Double) (fromIntegral (max 1 x))))+ if x < 1024 && x == 2 ^ n then n + 1 else n++step :: Word -> Word -> Word -> Word -> DecodeBits Word+step limit maxBits position value = do+ let x = Bits.shiftL 1 (fromIntegral position) :: Word+ if position < maxBits && value + x <= limit+ then do+ bit <- getBool+ let newValue = if bit then value + x else value+ step limit maxBits (position + 1) newValue+ else pure value
@@ -0,0 +1,17 @@+module Rattletrap.Decode.CompressedWordVector+ ( decodeCompressedWordVectorBits+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Decode.CompressedWord+import Rattletrap.Type.CompressedWordVector++decodeCompressedWordVectorBits :: DecodeBits CompressedWordVector+decodeCompressedWordVectorBits =+ CompressedWordVector+ <$> decodeCompressedWordBits limit+ <*> decodeCompressedWordBits limit+ <*> decodeCompressedWordBits limit++limit :: Word+limit = 65536
@@ -0,0 +1,68 @@+module Rattletrap.Decode.Content+ ( decodeContent+ ) where++import Rattletrap.Decode.Cache+import Rattletrap.Decode.ClassMapping+import Rattletrap.Decode.Common+import Rattletrap.Decode.Frame+import Rattletrap.Decode.KeyFrame+import Rattletrap.Decode.List+import Rattletrap.Decode.Mark+import Rattletrap.Decode.Message+import Rattletrap.Decode.Str+import Rattletrap.Decode.Word32le+import Rattletrap.Type.ClassAttributeMap+import Rattletrap.Type.Content+import Rattletrap.Type.Word32le+import Rattletrap.Utility.Bytes++import qualified Control.Monad.Trans.State as State++decodeContent+ :: (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'.+ -> Decode Content+decodeContent version numFrames maxChannels = do+ (levels, keyFrames, streamSize) <-+ (,,)+ <$> decodeList decodeStr+ <*> decodeList decodeKeyFrame+ <*> decodeWord32le+ (stream, messages, marks, packages, objects, names, classMappings, caches) <-+ (,,,,,,,)+ <$> getLazyByteString (fromIntegral (word32leValue streamSize))+ <*> decodeList decodeMessage+ <*> decodeList decodeMark+ <*> decodeList decodeStr+ <*> decodeList decodeStr+ <*> decodeList decodeStr+ <*> decodeList decodeClassMapping+ <*> decodeList decodeCache+ let+ classAttributeMap =+ makeClassAttributeMap objects classMappings caches names+ bitGet = State.evalStateT+ (decodeFramesBits version numFrames maxChannels classAttributeMap)+ mempty+ frames <- either fail pure (runDecodeBits bitGet (reverseBytes stream))+ pure+ ( Content+ levels+ keyFrames+ streamSize+ frames+ messages+ marks+ packages+ objects+ names+ classMappings+ caches+ )
@@ -0,0 +1,19 @@+module Rattletrap.Decode.DamageStateAttribute+ ( decodeDamageStateAttributeBits+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Decode.Int32le+import Rattletrap.Decode.Vector+import Rattletrap.Decode.Word8le+import Rattletrap.Type.DamageStateAttribute++decodeDamageStateAttributeBits :: DecodeBits DamageStateAttribute+decodeDamageStateAttributeBits =+ DamageStateAttribute+ <$> decodeWord8leBits+ <*> getBool+ <*> decodeInt32leBits+ <*> decodeVectorBits+ <*> getBool+ <*> getBool
@@ -0,0 +1,18 @@+module Rattletrap.Decode.DemolishAttribute+ ( decodeDemolishAttributeBits+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Decode.Vector+import Rattletrap.Decode.Word32le+import Rattletrap.Type.DemolishAttribute++decodeDemolishAttributeBits :: DecodeBits DemolishAttribute+decodeDemolishAttributeBits =+ DemolishAttribute+ <$> getBool+ <*> decodeWord32leBits+ <*> getBool+ <*> decodeWord32leBits+ <*> decodeVectorBits+ <*> decodeVectorBits
@@ -0,0 +1,9 @@+module Rattletrap.Decode.DestroyedReplication+ ( decodeDestroyedReplicationBits+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Type.DestroyedReplication++decodeDestroyedReplicationBits :: DecodeBits DestroyedReplication+decodeDestroyedReplicationBits = pure DestroyedReplication
@@ -0,0 +1,16 @@+module Rattletrap.Decode.Dictionary+ ( decodeDictionary+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Decode.Str+import Rattletrap.Type.Dictionary+import Rattletrap.Type.Str++decodeDictionary :: Decode a -> Decode (Dictionary a)+decodeDictionary decodeValue = do+ key <- decodeStr+ case filter (/= '\x00') (fromStr key) of+ "None" -> pure (DictionaryEnd key)+ _ ->+ DictionaryElement key <$> decodeValue <*> decodeDictionary decodeValue
@@ -0,0 +1,9 @@+module Rattletrap.Decode.EnumAttribute+ ( decodeEnumAttributeBits+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Type.EnumAttribute++decodeEnumAttributeBits :: DecodeBits EnumAttribute+decodeEnumAttributeBits = EnumAttribute <$> getWord16be 11
@@ -0,0 +1,12 @@+module Rattletrap.Decode.ExplosionAttribute+ ( decodeExplosionAttributeBits+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Decode.Int32le+import Rattletrap.Decode.Vector+import Rattletrap.Type.ExplosionAttribute++decodeExplosionAttributeBits :: DecodeBits ExplosionAttribute+decodeExplosionAttributeBits =+ ExplosionAttribute <$> getBool <*> decodeInt32leBits <*> decodeVectorBits
@@ -0,0 +1,14 @@+module Rattletrap.Decode.ExtendedExplosionAttribute+ ( decodeExtendedExplosionAttributeBits+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Decode.ExplosionAttribute+import Rattletrap.Decode.FlaggedIntAttribute+import Rattletrap.Type.ExtendedExplosionAttribute++decodeExtendedExplosionAttributeBits :: DecodeBits ExtendedExplosionAttribute+decodeExtendedExplosionAttributeBits =+ ExtendedExplosionAttribute+ <$> decodeExplosionAttributeBits+ <*> decodeFlaggedIntAttributeBits
@@ -0,0 +1,11 @@+module Rattletrap.Decode.FlaggedIntAttribute+ ( decodeFlaggedIntAttributeBits+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Decode.Int32le+import Rattletrap.Type.FlaggedIntAttribute++decodeFlaggedIntAttributeBits :: DecodeBits FlaggedIntAttribute+decodeFlaggedIntAttributeBits =+ FlaggedIntAttribute <$> getBool <*> decodeInt32leBits
@@ -0,0 +1,13 @@+module Rattletrap.Decode.Float32le+ ( decodeFloat32le+ , decodeFloat32leBits+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Type.Float32le++decodeFloat32le :: Decode Float32le+decodeFloat32le = Float32le <$> getFloatle++decodeFloat32leBits :: DecodeBits Float32le+decodeFloat32leBits = toBits decodeFloat32le 4
@@ -0,0 +1,10 @@+module Rattletrap.Decode.FloatAttribute+ ( decodeFloatAttributeBits+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Decode.Float32le+import Rattletrap.Type.FloatAttribute++decodeFloatAttributeBits :: DecodeBits FloatAttribute+decodeFloatAttributeBits = FloatAttribute <$> decodeFloat32leBits
@@ -0,0 +1,43 @@+module Rattletrap.Decode.Frame+ ( decodeFramesBits+ , decodeFrameBits+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Decode.Float32le+import Rattletrap.Decode.Replication+import Rattletrap.Type.ClassAttributeMap+import Rattletrap.Type.CompressedWord+import Rattletrap.Type.Frame+import Rattletrap.Type.Word32le++import qualified Control.Monad.Trans.Class as Trans+import qualified Control.Monad.Trans.State as State+import qualified Data.Map as Map++decodeFramesBits+ :: (Int, Int, Int)+ -> Int+ -> Word+ -> ClassAttributeMap+ -> State.StateT+ (Map.Map CompressedWord Word32le)+ DecodeBits+ [Frame]+decodeFramesBits version count limit classes = if count <= 0+ then pure []+ else+ (:)+ <$> decodeFrameBits version limit classes+ <*> decodeFramesBits version (count - 1) limit classes++decodeFrameBits+ :: (Int, Int, Int)+ -> Word+ -> ClassAttributeMap+ -> State.StateT (Map.Map CompressedWord Word32le) DecodeBits Frame+decodeFrameBits version limit classes =+ Frame+ <$> Trans.lift decodeFloat32leBits+ <*> Trans.lift decodeFloat32leBits+ <*> decodeReplicationsBits version limit classes
@@ -0,0 +1,14 @@+module Rattletrap.Decode.GameModeAttribute+ ( decodeGameModeAttributeBits+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Type.GameModeAttribute++decodeGameModeAttributeBits :: (Int, Int, Int) -> DecodeBits GameModeAttribute+decodeGameModeAttributeBits version =+ GameModeAttribute <$> pure (numBits version) <*> getWord8Bits+ (numBits version)++numBits :: (Int, Int, Int) -> Int+numBits version = if version < (868, 12, 0) then 2 else 8
@@ -0,0 +1,21 @@+module Rattletrap.Decode.Header+ ( decodeHeader+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Decode.Dictionary+import Rattletrap.Decode.Property+import Rattletrap.Decode.Str+import Rattletrap.Decode.Word32le+import Rattletrap.Type.Header+import Rattletrap.Type.Word32le++decodeHeader :: Decode Header+decodeHeader = do+ (major, minor) <- (,) <$> decodeWord32le <*> decodeWord32le+ Header major minor+ <$> decodeWhen+ (major >= Word32le 868 && minor >= Word32le 18)+ decodeWord32le+ <*> decodeStr+ <*> decodeDictionary decodeProperty
@@ -0,0 +1,14 @@+module Rattletrap.Decode.Initialization+ ( decodeInitializationBits+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Decode.Int8Vector+import Rattletrap.Decode.Vector+import Rattletrap.Type.Initialization++decodeInitializationBits :: Bool -> Bool -> DecodeBits Initialization+decodeInitializationBits hasLocation hasRotation =+ Initialization+ <$> decodeWhen hasLocation decodeVectorBits+ <*> decodeWhen hasRotation decodeInt8VectorBits
@@ -0,0 +1,13 @@+module Rattletrap.Decode.Int32le+ ( decodeInt32le+ , decodeInt32leBits+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Type.Int32le++decodeInt32le :: Decode Int32le+decodeInt32le = Int32le <$> getInt32le++decodeInt32leBits :: DecodeBits Int32le+decodeInt32leBits = toBits decodeInt32le 4
@@ -0,0 +1,17 @@+module Rattletrap.Decode.Int8Vector+ ( decodeInt8VectorBits+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Decode.Int8le+import Rattletrap.Type.Int8Vector+import Rattletrap.Type.Int8le++decodeInt8VectorBits :: DecodeBits Int8Vector+decodeInt8VectorBits =+ Int8Vector <$> decodeFieldBits <*> decodeFieldBits <*> decodeFieldBits++decodeFieldBits :: DecodeBits (Maybe Int8le)+decodeFieldBits = do+ hasField <- getBool+ decodeWhen hasField decodeInt8leBits
@@ -0,0 +1,13 @@+module Rattletrap.Decode.Int8le+ ( decodeInt8le+ , decodeInt8leBits+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Type.Int8le++decodeInt8le :: Decode Int8le+decodeInt8le = Int8le <$> getInt8++decodeInt8leBits :: DecodeBits Int8le+decodeInt8leBits = toBits decodeInt8le 1
@@ -0,0 +1,10 @@+module Rattletrap.Decode.IntAttribute+ ( decodeIntAttributeBits+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Decode.Int32le+import Rattletrap.Type.IntAttribute++decodeIntAttributeBits :: DecodeBits IntAttribute+decodeIntAttributeBits = IntAttribute <$> decodeInt32leBits
@@ -0,0 +1,12 @@+module Rattletrap.Decode.KeyFrame+ ( decodeKeyFrame+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Decode.Float32le+import Rattletrap.Decode.Word32le+import Rattletrap.Type.KeyFrame++decodeKeyFrame :: Decode KeyFrame+decodeKeyFrame =+ KeyFrame <$> decodeFloat32le <*> decodeWord32le <*> decodeWord32le
@@ -0,0 +1,15 @@+module Rattletrap.Decode.List+ ( decodeList+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Decode.Word32le+import Rattletrap.Type.List+import Rattletrap.Type.Word32le++import qualified Control.Monad as Monad++decodeList :: Decode a -> Decode (List a)+decodeList decodeElement = do+ size <- decodeWord32le+ List <$> Monad.replicateM (fromIntegral (word32leValue size)) decodeElement
@@ -0,0 +1,26 @@+module Rattletrap.Decode.LoadoutAttribute+ ( decodeLoadoutAttributeBits+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Decode.Word32le+import Rattletrap.Decode.Word8le+import Rattletrap.Type.LoadoutAttribute+import Rattletrap.Type.Word8le++decodeLoadoutAttributeBits :: DecodeBits LoadoutAttribute+decodeLoadoutAttributeBits = do+ version <- decodeWord8leBits+ LoadoutAttribute version+ <$> decodeWord32leBits+ <*> decodeWord32leBits+ <*> decodeWord32leBits+ <*> decodeWord32leBits+ <*> decodeWord32leBits+ <*> decodeWord32leBits+ <*> decodeWord32leBits+ <*> decodeWhen (version > Word8le 10) decodeWord32leBits+ <*> decodeWhen (version >= Word8le 16) decodeWord32leBits+ <*> decodeWhen (version >= Word8le 16) decodeWord32leBits+ <*> decodeWhen (version >= Word8le 16) decodeWord32leBits+ <*> decodeWhen (version >= Word8le 17) decodeWord32leBits
@@ -0,0 +1,24 @@+module Rattletrap.Decode.LoadoutOnlineAttribute+ ( decodeLoadoutOnlineAttributeBits+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Decode.ProductAttribute+import Rattletrap.Decode.Word8le+import Rattletrap.Type.LoadoutOnlineAttribute+import Rattletrap.Type.Str+import Rattletrap.Type.Word32le+import Rattletrap.Type.Word8le++import qualified Control.Monad as Monad+import qualified Data.Map as Map++decodeLoadoutOnlineAttributeBits+ :: (Int, Int, Int)+ -> Map.Map Word32le Str+ -> DecodeBits LoadoutOnlineAttribute+decodeLoadoutOnlineAttributeBits version objectMap = do+ size <- decodeWord8leBits+ LoadoutOnlineAttribute <$> Monad.replicateM+ (fromIntegral (word8leValue size))+ (decodeProductAttributesBits version objectMap)
@@ -0,0 +1,13 @@+module Rattletrap.Decode.LoadoutsAttribute+ ( decodeLoadoutsAttributeBits+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Decode.LoadoutAttribute+import Rattletrap.Type.LoadoutsAttribute++decodeLoadoutsAttributeBits :: DecodeBits LoadoutsAttribute+decodeLoadoutsAttributeBits =+ LoadoutsAttribute+ <$> decodeLoadoutAttributeBits+ <*> decodeLoadoutAttributeBits
@@ -0,0 +1,22 @@+module Rattletrap.Decode.LoadoutsOnlineAttribute+ ( decodeLoadoutsOnlineAttributeBits+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Decode.LoadoutOnlineAttribute+import Rattletrap.Type.LoadoutsOnlineAttribute+import Rattletrap.Type.Str+import Rattletrap.Type.Word32le++import qualified Data.Map as Map++decodeLoadoutsOnlineAttributeBits+ :: (Int, Int, Int)+ -> Map.Map Word32le Str+ -> DecodeBits LoadoutsOnlineAttribute+decodeLoadoutsOnlineAttributeBits version objectMap =+ LoadoutsOnlineAttribute+ <$> decodeLoadoutOnlineAttributeBits version objectMap+ <*> decodeLoadoutOnlineAttributeBits version objectMap+ <*> getBool+ <*> getBool
@@ -0,0 +1,10 @@+module Rattletrap.Decode.LocationAttribute+ ( decodeLocationAttributeBits+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Decode.Vector+import Rattletrap.Type.LocationAttribute++decodeLocationAttributeBits :: DecodeBits LocationAttribute+decodeLocationAttributeBits = LocationAttribute <$> decodeVectorBits
@@ -0,0 +1,11 @@+module Rattletrap.Decode.Mark+ ( decodeMark+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Decode.Str+import Rattletrap.Decode.Word32le+import Rattletrap.Type.Mark++decodeMark :: Decode Mark+decodeMark = Mark <$> decodeStr <*> decodeWord32le
@@ -0,0 +1,11 @@+module Rattletrap.Decode.Message+ ( decodeMessage+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Decode.Str+import Rattletrap.Decode.Word32le+import Rattletrap.Type.Message++decodeMessage :: Decode Message+decodeMessage = Message <$> decodeWord32le <*> decodeStr <*> decodeStr
@@ -0,0 +1,15 @@+module Rattletrap.Decode.MusicStingerAttribute+ ( decodeMusicStingerAttributeBits+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Decode.Word32le+import Rattletrap.Decode.Word8le+import Rattletrap.Type.MusicStingerAttribute++decodeMusicStingerAttributeBits :: DecodeBits MusicStingerAttribute+decodeMusicStingerAttributeBits =+ MusicStingerAttribute+ <$> getBool+ <*> decodeWord32leBits+ <*> decodeWord8leBits
@@ -0,0 +1,17 @@+module Rattletrap.Decode.PartyLeaderAttribute+ ( decodePartyLeaderAttributeBits+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Decode.RemoteId+import Rattletrap.Decode.Word8le+import Rattletrap.Type.PartyLeaderAttribute+import Rattletrap.Type.Word8le++decodePartyLeaderAttributeBits+ :: (Int, Int, Int) -> DecodeBits PartyLeaderAttribute+decodePartyLeaderAttributeBits version = do+ systemId <- decodeWord8leBits+ PartyLeaderAttribute systemId <$> decodeWhen+ (systemId /= Word8le 0)+ ((,) <$> decodeRemoteIdBits version systemId <*> decodeWord8leBits)
@@ -0,0 +1,12 @@+module Rattletrap.Decode.PickupAttribute+ ( decodePickupAttributeBits+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Decode.Word32le+import Rattletrap.Type.PickupAttribute++decodePickupAttributeBits :: DecodeBits PickupAttribute+decodePickupAttributeBits = do+ instigator <- getBool+ PickupAttribute <$> decodeWhen instigator decodeWord32leBits <*> getBool
@@ -0,0 +1,11 @@+module Rattletrap.Decode.PlayerHistoryKeyAttribute+ ( decodePlayerHistoryKeyAttributeBits+ ) where++import Rattletrap.Decode.Bitstream+import Rattletrap.Decode.Common+import Rattletrap.Type.PlayerHistoryKeyAttribute++decodePlayerHistoryKeyAttributeBits :: DecodeBits PlayerHistoryKeyAttribute+decodePlayerHistoryKeyAttributeBits =+ PlayerHistoryKeyAttribute <$> decodeBitstreamBits 1253
@@ -0,0 +1,19 @@+module Rattletrap.Decode.PrivateMatchSettingsAttribute+ ( decodePrivateMatchSettingsAttributeBits+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Decode.Str+import Rattletrap.Decode.Word32le+import Rattletrap.Type.PrivateMatchSettingsAttribute++decodePrivateMatchSettingsAttributeBits+ :: DecodeBits PrivateMatchSettingsAttribute+decodePrivateMatchSettingsAttributeBits =+ PrivateMatchSettingsAttribute+ <$> decodeStrBits+ <*> decodeWord32leBits+ <*> decodeWord32leBits+ <*> decodeStrBits+ <*> decodeStrBits+ <*> getBool
@@ -0,0 +1,57 @@+module Rattletrap.Decode.ProductAttribute+ ( decodeProductAttributesBits+ , decodeProductAttributeBits+ ) where++import Data.Semigroup ((<>))+import Rattletrap.Decode.Common+import Rattletrap.Decode.CompressedWord+import Rattletrap.Decode.Word32le+import Rattletrap.Decode.Word8le+import Rattletrap.Type.Common+import Rattletrap.Type.CompressedWord+import Rattletrap.Type.ProductAttribute+import Rattletrap.Type.Str+import Rattletrap.Type.Word32le+import Rattletrap.Type.Word8le++import qualified Control.Monad as Monad+import qualified Data.Map as Map++decodeProductAttributesBits+ :: (Int, Int, Int) -> Map Word32le Str -> DecodeBits [ProductAttribute]+decodeProductAttributesBits version objectMap = do+ size <- decodeWord8leBits+ Monad.replicateM+ (fromIntegral (word8leValue size))+ (decodeProductAttributeBits version objectMap)++decodeProductAttributeBits+ :: (Int, Int, Int) -> Map Word32le Str -> DecodeBits ProductAttribute+decodeProductAttributeBits version objectMap = do+ flag <- getBool+ objectId <- decodeWord32leBits+ let objectName = Map.lookup objectId objectMap+ value <- case objectName of+ Just name -> case fromStr name of+ "TAGame.ProductAttribute_Painted_TA" -> Just <$> decodePainted version+ "TAGame.ProductAttribute_UserColor_TA" -> decodeColor+ _ ->+ 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)++decodePainted :: (Int, Int, Int) -> DecodeBits (Either CompressedWord Word32)+decodePainted version = if version >= (868, 18, 0)+ then Right <$> getWord32be 31+ else Left <$> decodeCompressedWordBits 13++decodeColor :: DecodeBits (Maybe (Either CompressedWord Word32))+decodeColor = do+ hasValue <- getBool+ decodeWhen hasValue (Right <$> getWord32be 31)
@@ -0,0 +1,14 @@+module Rattletrap.Decode.Property+ ( decodeProperty+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Decode.PropertyValue+import Rattletrap.Decode.Str+import Rattletrap.Decode.Word64le+import Rattletrap.Type.Property++decodeProperty :: Decode Property+decodeProperty = do+ kind <- decodeStr+ Property kind <$> decodeWord64le <*> decodePropertyValue decodeProperty kind
@@ -0,0 +1,31 @@+module Rattletrap.Decode.PropertyValue+ ( decodePropertyValue+ ) where++import Data.Semigroup ((<>))+import Rattletrap.Decode.Common+import Rattletrap.Decode.Dictionary+import Rattletrap.Decode.Float32le+import Rattletrap.Decode.Int32le+import Rattletrap.Decode.List+import Rattletrap.Decode.Str+import Rattletrap.Decode.Word64le+import Rattletrap.Decode.Word8le+import Rattletrap.Type.PropertyValue+import Rattletrap.Type.Str++decodePropertyValue :: Decode a -> Str -> Decode (PropertyValue a)+decodePropertyValue getProperty kind = case fromStr kind of+ "ArrayProperty" ->+ PropertyValueArray <$> decodeList (decodeDictionary getProperty)+ "BoolProperty" -> PropertyValueBool <$> decodeWord8le+ "ByteProperty" -> do+ k <- decodeStr+ PropertyValueByte k+ <$> decodeWhen (fromStr k /= "OnlinePlatform_Steam") decodeStr+ "FloatProperty" -> PropertyValueFloat <$> decodeFloat32le+ "IntProperty" -> PropertyValueInt <$> decodeInt32le+ "NameProperty" -> PropertyValueName <$> decodeStr+ "QWordProperty" -> PropertyValueQWord <$> decodeWord64le+ "StrProperty" -> PropertyValueStr <$> decodeStr+ _ -> fail ("don't know how to read property value " <> show kind)
@@ -0,0 +1,10 @@+module Rattletrap.Decode.QWordAttribute+ ( decodeQWordAttributeBits+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Decode.Word64le+import Rattletrap.Type.QWordAttribute++decodeQWordAttributeBits :: DecodeBits QWordAttribute+decodeQWordAttributeBits = QWordAttribute <$> decodeWord64leBits
@@ -0,0 +1,38 @@+module Rattletrap.Decode.RemoteId+ ( decodeRemoteIdBits+ ) where++import Data.Semigroup ((<>))+import Rattletrap.Decode.Bitstream+import Rattletrap.Decode.Common+import Rattletrap.Decode.Word64le+import Rattletrap.Type.RemoteId+import Rattletrap.Type.Word8le+import Rattletrap.Utility.Bytes++import qualified Data.ByteString.Lazy as LazyBytes+import qualified Data.Text as Text+import qualified Data.Text.Encoding as Text+import qualified Data.Word as Word++decodeRemoteIdBits :: (Int, Int, Int) -> Word8le -> DecodeBits RemoteId+decodeRemoteIdBits (_, _, patch) systemId = case word8leValue systemId of+ 0 -> RemoteIdSplitscreen <$> getWord32be 24+ 1 -> RemoteIdSteam <$> decodeWord64leBits+ 2 -> RemoteIdPlayStation <$> decodePsName <*> decodePsBytes patch+ 4 -> RemoteIdXbox <$> decodeWord64leBits+ 6 -> RemoteIdSwitch <$> decodeBitstreamBits 256+ _ -> fail ("unknown system id " <> show systemId)++decodePsName :: DecodeBits Text.Text+decodePsName = fmap+ ( Text.dropWhileEnd (== '\x00')+ . Text.decodeLatin1+ . LazyBytes.toStrict+ . reverseBytes+ )+ (getLazyByteStringBits 16)++decodePsBytes :: Int -> DecodeBits [Word.Word8]+decodePsBytes patch =+ LazyBytes.unpack <$> getLazyByteStringBits (if patch >= 1 then 24 else 16)
@@ -0,0 +1,48 @@+module Rattletrap.Decode.Replay+ ( decodeReplay+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Decode.Content+import Rattletrap.Decode.Header+import Rattletrap.Decode.Section+import Rattletrap.Type.Dictionary+import Rattletrap.Type.Header+import Rattletrap.Type.Int32le+import Rattletrap.Type.Property+import Rattletrap.Type.PropertyValue+import Rattletrap.Type.Replay+import Rattletrap.Type.Section+import Rattletrap.Type.Str+import Rattletrap.Type.Word32le++decodeReplay :: Decode Replay+decodeReplay = do+ header <- decodeSection decodeHeader+ Replay header <$> decodeSection+ ( decodeContent+ (getVersion (sectionBody header))+ (getNumFrames (sectionBody header))+ (getMaxChannels (sectionBody header))+ )++getVersion :: Header -> (Int, Int, Int)+getVersion header =+ ( fromIntegral (word32leValue (headerEngineVersion header))+ , fromIntegral (word32leValue (headerLicenseeVersion header))+ , maybe 0 (fromIntegral . word32leValue) (headerPatchVersion header)+ )++getNumFrames :: Header -> Int+getNumFrames header =+ case dictionaryLookup (toStr "NumFrames") (headerProperties header) of+ Just (Property _ _ (PropertyValueInt numFrames)) ->+ fromIntegral (int32leValue numFrames)+ _ -> 0++getMaxChannels :: Header -> Word+getMaxChannels header =+ case dictionaryLookup (toStr "MaxChannels") (headerProperties header) of+ Just (Property _ _ (PropertyValueInt numFrames)) ->+ fromIntegral (int32leValue numFrames)+ _ -> 1023
@@ -0,0 +1,45 @@+module Rattletrap.Decode.Replication+ ( decodeReplicationsBits+ , decodeReplicationBits+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Decode.CompressedWord+import Rattletrap.Decode.ReplicationValue+import Rattletrap.Type.ClassAttributeMap+import Rattletrap.Type.CompressedWord+import Rattletrap.Type.Replication+import Rattletrap.Type.Word32le++import qualified Control.Monad.Trans.Class as Trans+import qualified Control.Monad.Trans.State as State+import qualified Data.Map as Map++decodeReplicationsBits+ :: (Int, Int, Int)+ -> Word+ -> ClassAttributeMap+ -> State.StateT+ (Map.Map CompressedWord Word32le)+ DecodeBits+ [Replication]+decodeReplicationsBits version limit classes = do+ hasReplication <- Trans.lift getBool+ if hasReplication+ then+ (:)+ <$> decodeReplicationBits version limit classes+ <*> decodeReplicationsBits version limit classes+ else pure []++decodeReplicationBits+ :: (Int, Int, Int)+ -> Word+ -> ClassAttributeMap+ -> State.StateT+ (Map.Map CompressedWord Word32le)+ DecodeBits+ Replication+decodeReplicationBits version limit classes = do+ actor <- Trans.lift (decodeCompressedWordBits limit)+ Replication actor <$> decodeReplicationValueBits version classes actor
@@ -0,0 +1,44 @@+module Rattletrap.Decode.ReplicationValue+ ( decodeReplicationValueBits+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Decode.DestroyedReplication+import Rattletrap.Decode.SpawnedReplication+import Rattletrap.Decode.UpdatedReplication+import Rattletrap.Type.ClassAttributeMap+import Rattletrap.Type.CompressedWord+import Rattletrap.Type.ReplicationValue+import Rattletrap.Type.Word32le++import qualified Control.Monad.Trans.Class as Trans+import qualified Control.Monad.Trans.State as State+import qualified Data.Map as Map++decodeReplicationValueBits+ :: (Int, Int, Int)+ -> ClassAttributeMap+ -> CompressedWord+ -> State.StateT+ (Map.Map CompressedWord Word32le)+ DecodeBits+ ReplicationValue+decodeReplicationValueBits version classAttributeMap actorId = do+ actorMap <- State.get+ isOpen <- Trans.lift getBool+ if isOpen+ then do+ isNew <- Trans.lift getBool+ if isNew+ then+ ReplicationValueSpawned+ <$> decodeSpawnedReplicationBits version classAttributeMap actorId+ else ReplicationValueUpdated <$> Trans.lift+ ( decodeUpdatedReplicationBits+ version+ classAttributeMap+ actorMap+ actorId+ )+ else ReplicationValueDestroyed+ <$> Trans.lift decodeDestroyedReplicationBits
@@ -0,0 +1,24 @@+module Rattletrap.Decode.ReservationAttribute+ ( decodeReservationAttributeBits+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Decode.CompressedWord+import Rattletrap.Decode.Str+import Rattletrap.Decode.UniqueIdAttribute+import Rattletrap.Type.ReservationAttribute+import Rattletrap.Type.UniqueIdAttribute+import Rattletrap.Type.Word8le++decodeReservationAttributeBits+ :: (Int, Int, Int) -> DecodeBits ReservationAttribute+decodeReservationAttributeBits version = do+ number <- decodeCompressedWordBits 7+ uniqueId <- decodeUniqueIdAttributeBits version+ ReservationAttribute number uniqueId+ <$> decodeWhen+ (uniqueIdAttributeSystemId uniqueId /= Word8le 0)+ decodeStrBits+ <*> getBool+ <*> getBool+ <*> decodeWhen (version >= (868, 12, 0)) (getWord8Bits 6)
@@ -0,0 +1,17 @@+module Rattletrap.Decode.RigidBodyStateAttribute+ ( decodeRigidBodyStateAttributeBits+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Decode.CompressedWordVector+import Rattletrap.Decode.Vector+import Rattletrap.Type.RigidBodyStateAttribute++decodeRigidBodyStateAttributeBits :: DecodeBits RigidBodyStateAttribute+decodeRigidBodyStateAttributeBits = do+ sleeping <- getBool+ RigidBodyStateAttribute sleeping+ <$> decodeVectorBits+ <*> decodeCompressedWordVectorBits+ <*> decodeWhen (not sleeping) decodeVectorBits+ <*> decodeWhen (not sleeping) decodeVectorBits
@@ -0,0 +1,25 @@+module Rattletrap.Decode.Section+ ( decodeSection+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Decode.Word32le+import Rattletrap.Type.Section+import Rattletrap.Type.Word32le+import Rattletrap.Utility.Crc++import qualified Control.Monad as Monad++decodeSection :: Decode a -> Decode (Section a)+decodeSection getBody = do+ size <- decodeWord32le+ crc <- decodeWord32le+ rawBody <- getLazyByteString (fromIntegral (word32leValue size))+ let actualCrc = Word32le (getCrc32 rawBody)+ Monad.when (actualCrc /= crc) (fail (crcMessage actualCrc crc))+ body <- either fail pure (runDecode getBody rawBody)+ pure (Section size crc body)++crcMessage :: Word32le -> Word32le -> String+crcMessage actual expected = unwords+ ["actual CRC", show actual, "does not match expected CRC", show expected]
@@ -0,0 +1,69 @@+module Rattletrap.Decode.SpawnedReplication+ ( decodeSpawnedReplicationBits+ ) where++import Data.Semigroup ((<>))+import Rattletrap.Decode.Common+import Rattletrap.Decode.Initialization+import Rattletrap.Decode.Word32le+import Rattletrap.Type.ClassAttributeMap+import Rattletrap.Type.CompressedWord+import Rattletrap.Type.SpawnedReplication+import Rattletrap.Type.Str+import Rattletrap.Type.Word32le++import qualified Control.Monad.Trans.Class as Trans+import qualified Control.Monad.Trans.State as State+import qualified Data.Map as Map++decodeSpawnedReplicationBits+ :: (Int, Int, Int)+ -> ClassAttributeMap+ -> CompressedWord+ -> State.StateT+ (Map.Map CompressedWord Word32le)+ DecodeBits+ SpawnedReplication+decodeSpawnedReplicationBits version classAttributeMap actorId = do+ flag <- Trans.lift getBool+ nameIndex <- decodeWhen+ (version >= (868, 14, 0))+ (Trans.lift decodeWord32leBits)+ name <- lookupName classAttributeMap nameIndex+ objectId <- Trans.lift decodeWord32leBits+ State.modify (Map.insert actorId objectId)+ objectName <- lookupObjectName classAttributeMap objectId+ className <- lookupClassName objectName+ let hasLocation = classHasLocation className+ let hasRotation = classHasRotation className+ initialization <- Trans.lift+ (decodeInitializationBits hasLocation hasRotation)+ pure+ ( SpawnedReplication+ flag+ nameIndex+ name+ objectId+ objectName+ className+ initialization+ )++lookupName :: Monad m => ClassAttributeMap -> Maybe Word32le -> m (Maybe Str)+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 -> Word32le -> m Str+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 => Str -> m Str+lookupClassName objectName = case getClassName objectName of+ Nothing -> fail ("could not get class name for object " <> show objectName)+ Just className -> pure className
@@ -0,0 +1,42 @@+module Rattletrap.Decode.Str+ ( decodeStr+ , decodeStrBits+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Decode.Int32le+import Rattletrap.Type.Int32le+import Rattletrap.Type.Str+import Rattletrap.Utility.Bytes++import qualified Data.ByteString.Lazy as LazyBytes+import qualified Data.Text as Text+import qualified Data.Text.Encoding as Text++decodeStr :: Decode Str+decodeStr = do+ rawSize <- decodeInt32le+ bytes <- getLazyByteString (normalizeTextSize rawSize)+ pure (Str (dropNull (getTextDecoder rawSize bytes)))++decodeStrBits :: DecodeBits Str+decodeStrBits = do+ rawSize <- decodeInt32leBits+ bytes <- getLazyByteStringBits (normalizeTextSize rawSize)+ pure (Str (dropNull (getTextDecoder rawSize (reverseBytes bytes))))++normalizeTextSize :: Integral a => Int32le -> a+normalizeTextSize size = case int32leValue size of+ 0x05000000 -> 8+ x -> if x < 0 then (-2 * fromIntegral x) else fromIntegral x++getTextDecoder :: Int32le -> LazyBytes.ByteString -> Text.Text+getTextDecoder size bytes =+ let+ decode =+ if size < Int32le 0 then Text.decodeUtf16LE else Text.decodeLatin1+ in+ decode (LazyBytes.toStrict bytes)++dropNull :: Text.Text -> Text.Text+dropNull = Text.dropWhileEnd (== '\x00')
@@ -0,0 +1,10 @@+module Rattletrap.Decode.StringAttribute+ ( decodeStringAttributeBits+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Decode.Str+import Rattletrap.Type.StringAttribute++decodeStringAttributeBits :: DecodeBits StringAttribute+decodeStringAttributeBits = StringAttribute <$> decodeStrBits
@@ -0,0 +1,17 @@+module Rattletrap.Decode.TeamPaintAttribute+ ( decodeTeamPaintAttributeBits+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Decode.Word32le+import Rattletrap.Decode.Word8le+import Rattletrap.Type.TeamPaintAttribute++decodeTeamPaintAttributeBits :: DecodeBits TeamPaintAttribute+decodeTeamPaintAttributeBits =+ TeamPaintAttribute+ <$> decodeWord8leBits+ <*> decodeWord8leBits+ <*> decodeWord8leBits+ <*> decodeWord32leBits+ <*> decodeWord32leBits
@@ -0,0 +1,15 @@+module Rattletrap.Decode.UniqueIdAttribute+ ( decodeUniqueIdAttributeBits+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Decode.RemoteId+import Rattletrap.Decode.Word8le+import Rattletrap.Type.UniqueIdAttribute++decodeUniqueIdAttributeBits :: (Int, Int, Int) -> DecodeBits UniqueIdAttribute+decodeUniqueIdAttributeBits version = do+ systemId <- decodeWord8leBits+ UniqueIdAttribute systemId+ <$> decodeRemoteIdBits version systemId+ <*> decodeWord8leBits
@@ -0,0 +1,21 @@+module Rattletrap.Decode.UpdatedReplication+ ( decodeUpdatedReplicationBits+ ) where++import Rattletrap.Decode.Attribute+import Rattletrap.Decode.Common+import Rattletrap.Type.ClassAttributeMap+import Rattletrap.Type.CompressedWord+import Rattletrap.Type.UpdatedReplication+import Rattletrap.Type.Word32le++import qualified Data.Map as Map++decodeUpdatedReplicationBits+ :: (Int, Int, Int)+ -> ClassAttributeMap+ -> Map.Map CompressedWord Word32le+ -> CompressedWord+ -> DecodeBits UpdatedReplication+decodeUpdatedReplicationBits version classes actors actor =+ UpdatedReplication <$> decodeAttributesBits version classes actors actor
@@ -0,0 +1,28 @@+module Rattletrap.Decode.Vector+ ( decodeVectorBits+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Decode.CompressedWord+import Rattletrap.Type.CompressedWord+import Rattletrap.Type.Vector++decodeVectorBits :: DecodeBits Vector+decodeVectorBits = do+ size <- decodeCompressedWordBits 19+ let+ limit = getLimit size+ bias = getBias size+ Vector bias+ <$> fmap (fromDelta bias) (decodeCompressedWordBits limit)+ <*> fmap (fromDelta bias) (decodeCompressedWordBits limit)+ <*> fmap (fromDelta bias) (decodeCompressedWordBits limit)++getLimit :: CompressedWord -> Word+getLimit = (2 ^) . (+ 2) . compressedWordValue++getBias :: CompressedWord -> Word+getBias = (2 ^) . (+ 1) . compressedWordValue++fromDelta :: Word -> CompressedWord -> Int+fromDelta bias x = fromIntegral (compressedWordValue x) - fromIntegral bias
@@ -0,0 +1,19 @@+module Rattletrap.Decode.WeldedInfoAttribute+ ( decodeWeldedInfoAttributeBits+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Decode.Float32le+import Rattletrap.Decode.Int32le+import Rattletrap.Decode.Int8Vector+import Rattletrap.Decode.Vector+import Rattletrap.Type.WeldedInfoAttribute++decodeWeldedInfoAttributeBits :: DecodeBits WeldedInfoAttribute+decodeWeldedInfoAttributeBits =+ WeldedInfoAttribute+ <$> getBool+ <*> decodeInt32leBits+ <*> decodeVectorBits+ <*> decodeFloat32leBits+ <*> decodeInt8VectorBits
@@ -0,0 +1,13 @@+module Rattletrap.Decode.Word32le+ ( decodeWord32le+ , decodeWord32leBits+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Type.Word32le++decodeWord32le :: Decode Word32le+decodeWord32le = Word32le <$> getWord32le++decodeWord32leBits :: DecodeBits Word32le+decodeWord32leBits = toBits decodeWord32le 4
@@ -0,0 +1,13 @@+module Rattletrap.Decode.Word64le+ ( decodeWord64le+ , decodeWord64leBits+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Type.Word64le++decodeWord64le :: Decode Word64le+decodeWord64le = Word64le <$> getWord64le++decodeWord64leBits :: DecodeBits Word64le+decodeWord64leBits = toBits decodeWord64le 8
@@ -0,0 +1,13 @@+module Rattletrap.Decode.Word8le+ ( decodeWord8le+ , decodeWord8leBits+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Type.Word8le++decodeWord8le :: Decode Word8le+decodeWord8le = Word8le <$> getWord8++decodeWord8leBits :: DecodeBits Word8le+decodeWord8leBits = toBits decodeWord8le 1
@@ -0,0 +1,17 @@+module Rattletrap.Encode.AppliedDamageAttribute+ ( putAppliedDamageAttribute+ ) where++import Rattletrap.Encode.Int32le+import Rattletrap.Encode.Vector+import Rattletrap.Encode.Word8le+import Rattletrap.Type.AppliedDamageAttribute++import qualified Data.Binary.Bits.Put as BinaryBits++putAppliedDamageAttribute :: AppliedDamageAttribute -> BinaryBits.BitPut ()+putAppliedDamageAttribute appliedDamageAttribute = do+ putWord8Bits (appliedDamageAttributeUnknown1 appliedDamageAttribute)+ putVector (appliedDamageAttributeLocation appliedDamageAttribute)+ putInt32Bits (appliedDamageAttributeUnknown3 appliedDamageAttribute)+ putInt32Bits (appliedDamageAttributeUnknown4 appliedDamageAttribute)
@@ -0,0 +1,21 @@+module Rattletrap.Encode.Attribute+ ( putAttributes+ , putAttribute+ ) where++import Rattletrap.Encode.AttributeValue+import Rattletrap.Encode.CompressedWord+import Rattletrap.Type.Attribute++import qualified Data.Binary.Bits.Put as BinaryBits++putAttributes :: [Attribute] -> BinaryBits.BitPut ()+putAttributes attributes = do+ mapM_ putAttribute attributes+ BinaryBits.putBool False++putAttribute :: Attribute -> BinaryBits.BitPut ()+putAttribute attribute = do+ BinaryBits.putBool True+ putCompressedWord (attributeId attribute)+ putAttributeValue (attributeValue attribute)
@@ -0,0 +1,13 @@+module Rattletrap.Encode.AttributeMapping+ ( putAttributeMapping+ ) where++import Rattletrap.Encode.Word32le+import Rattletrap.Type.AttributeMapping++import qualified Data.Binary as Binary++putAttributeMapping :: AttributeMapping -> Binary.Put+putAttributeMapping attributeMapping = do+ putWord32 (attributeMappingObjectId attributeMapping)+ putWord32 (attributeMappingStreamId attributeMapping)
@@ -0,0 +1,72 @@+module Rattletrap.Encode.AttributeValue+ ( putAttributeValue+ ) where++import Rattletrap.Encode.AppliedDamageAttribute+import Rattletrap.Encode.BooleanAttribute+import Rattletrap.Encode.ByteAttribute+import Rattletrap.Encode.CamSettingsAttribute+import Rattletrap.Encode.ClubColorsAttribute+import Rattletrap.Encode.DamageStateAttribute+import Rattletrap.Encode.DemolishAttribute+import Rattletrap.Encode.EnumAttribute+import Rattletrap.Encode.ExplosionAttribute+import Rattletrap.Encode.ExtendedExplosionAttribute+import Rattletrap.Encode.FlaggedIntAttribute+import Rattletrap.Encode.FloatAttribute+import Rattletrap.Encode.GameModeAttribute+import Rattletrap.Encode.IntAttribute+import Rattletrap.Encode.LoadoutAttribute+import Rattletrap.Encode.LoadoutOnlineAttribute+import Rattletrap.Encode.LoadoutsAttribute+import Rattletrap.Encode.LoadoutsOnlineAttribute+import Rattletrap.Encode.LocationAttribute+import Rattletrap.Encode.MusicStingerAttribute+import Rattletrap.Encode.PartyLeaderAttribute+import Rattletrap.Encode.PickupAttribute+import Rattletrap.Encode.PlayerHistoryKeyAttribute+import Rattletrap.Encode.PrivateMatchSettingsAttribute+import Rattletrap.Encode.QWordAttribute+import Rattletrap.Encode.ReservationAttribute+import Rattletrap.Encode.RigidBodyStateAttribute+import Rattletrap.Encode.StringAttribute+import Rattletrap.Encode.TeamPaintAttribute+import Rattletrap.Encode.UniqueIdAttribute+import Rattletrap.Encode.WeldedInfoAttribute+import Rattletrap.Type.AttributeValue++import qualified Data.Binary.Bits.Put as BinaryBits++putAttributeValue :: AttributeValue -> BinaryBits.BitPut ()+putAttributeValue value = case value of+ AttributeValueAppliedDamage x -> putAppliedDamageAttribute x+ AttributeValueBoolean x -> putBooleanAttribute x+ AttributeValueByte x -> putByteAttribute x+ AttributeValueCamSettings x -> putCamSettingsAttribute x+ AttributeValueClubColors x -> putClubColorsAttribute x+ AttributeValueDamageState x -> putDamageStateAttribute x+ AttributeValueDemolish x -> putDemolishAttribute x+ AttributeValueEnum x -> putEnumAttribute x+ AttributeValueExplosion x -> putExplosionAttribute x+ AttributeValueExtendedExplosion x -> putExtendedExplosionAttribute x+ AttributeValueFlaggedInt x -> putFlaggedIntAttribute x+ AttributeValueFloat x -> putFloatAttribute x+ AttributeValueGameMode x -> putGameModeAttribute x+ AttributeValueInt x -> putIntAttribute x+ AttributeValueLoadout x -> putLoadoutAttribute x+ AttributeValueLoadoutOnline x -> putLoadoutOnlineAttribute x+ AttributeValueLoadouts x -> putLoadoutsAttribute x+ AttributeValueLoadoutsOnline x -> putLoadoutsOnlineAttribute x+ AttributeValueLocation x -> putLocationAttribute x+ AttributeValueMusicStinger x -> putMusicStingerAttribute x+ AttributeValuePartyLeader x -> putPartyLeaderAttribute x+ AttributeValuePickup x -> putPickupAttribute x+ AttributeValuePlayerHistoryKey x -> putPlayerHistoryKeyAttribute x+ AttributeValuePrivateMatchSettings x -> putPrivateMatchSettingsAttribute x+ AttributeValueQWord x -> putQWordAttribute x+ AttributeValueReservation x -> putReservationAttribute x+ AttributeValueRigidBodyState x -> putRigidBodyStateAttribute x+ AttributeValueString x -> putStringAttribute x+ AttributeValueTeamPaint x -> putTeamPaintAttribute x+ AttributeValueUniqueId x -> putUniqueIdAttribute x+ AttributeValueWeldedInfo x -> putWeldedInfoAttribute x
@@ -0,0 +1,10 @@+module Rattletrap.Encode.Bitstream+ ( putBitstream+ ) where++import Rattletrap.Type.Bitstream++import qualified Data.Binary.Bits.Put as BinaryBits++putBitstream :: Bitstream -> BinaryBits.BitPut ()+putBitstream = mapM_ BinaryBits.putBool . bitstreamValue
@@ -0,0 +1,11 @@+module Rattletrap.Encode.BooleanAttribute+ ( putBooleanAttribute+ ) where++import Rattletrap.Type.BooleanAttribute++import qualified Data.Binary.Bits.Put as BinaryBits++putBooleanAttribute :: BooleanAttribute -> BinaryBits.BitPut ()+putBooleanAttribute booleanAttribute =+ BinaryBits.putBool (booleanAttributeValue booleanAttribute)
@@ -0,0 +1,12 @@+module Rattletrap.Encode.ByteAttribute+ ( putByteAttribute+ ) where++import Rattletrap.Encode.Word8le+import Rattletrap.Type.ByteAttribute++import qualified Data.Binary.Bits.Put as BinaryBits++putByteAttribute :: ByteAttribute -> BinaryBits.BitPut ()+putByteAttribute byteAttribute =+ putWord8Bits (byteAttributeValue byteAttribute)
@@ -0,0 +1,17 @@+module Rattletrap.Encode.Cache+ ( putCache+ ) where++import Rattletrap.Encode.AttributeMapping+import Rattletrap.Encode.List+import Rattletrap.Encode.Word32le+import Rattletrap.Type.Cache++import qualified Data.Binary as Binary++putCache :: Cache -> Binary.Put+putCache cache = do+ putWord32 (cacheClassId cache)+ putWord32 (cacheParentCacheId cache)+ putWord32 (cacheCacheId cache)+ putList putAttributeMapping (cacheAttributeMappings cache)
@@ -0,0 +1,20 @@+module Rattletrap.Encode.CamSettingsAttribute+ ( putCamSettingsAttribute+ ) where++import Rattletrap.Encode.Float32le+import Rattletrap.Type.CamSettingsAttribute++import qualified Data.Binary.Bits.Put as BinaryBits++putCamSettingsAttribute :: CamSettingsAttribute -> BinaryBits.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
@@ -0,0 +1,14 @@+module Rattletrap.Encode.ClassMapping+ ( putClassMapping+ ) where++import Rattletrap.Encode.Str+import Rattletrap.Encode.Word32le+import Rattletrap.Type.ClassMapping++import qualified Data.Binary as Binary++putClassMapping :: ClassMapping -> Binary.Put+putClassMapping classMapping = do+ putText (classMappingName classMapping)+ putWord32 (classMappingStreamId classMapping)
@@ -0,0 +1,15 @@+module Rattletrap.Encode.ClubColorsAttribute+ ( putClubColorsAttribute+ ) where++import Rattletrap.Encode.Word8le+import Rattletrap.Type.ClubColorsAttribute++import qualified Data.Binary.Bits.Put as BinaryBits++putClubColorsAttribute :: ClubColorsAttribute -> BinaryBits.BitPut ()+putClubColorsAttribute clubColorsAttribute = do+ BinaryBits.putBool (clubColorsAttributeBlueFlag clubColorsAttribute)+ putWord8Bits (clubColorsAttributeBlueColor clubColorsAttribute)+ BinaryBits.putBool (clubColorsAttributeOrangeFlag clubColorsAttribute)+ putWord8Bits (clubColorsAttributeOrangeColor clubColorsAttribute)
@@ -0,0 +1,43 @@+module Rattletrap.Encode.CompressedWord+ ( putCompressedWord+ ) where++import Rattletrap.Type.CompressedWord++import qualified Data.Binary.Bits.Put as BinaryBits+import qualified Data.Bits as Bits++putCompressedWord :: CompressedWord -> BinaryBits.BitPut ()+putCompressedWord compressedWord =+ let+ limit = compressedWordLimit compressedWord+ value = compressedWordValue compressedWord+ maxBits = getMaxBits limit+ in putCompressedWordStep limit value maxBits 0 0++putCompressedWordStep+ :: Word -> Word -> Int -> Int -> Word -> BinaryBits.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 pure ()+ else do+ let bit = Bits.testBit value position+ BinaryBits.putBool bit+ let delta = if bit then x else 0+ putCompressedWordStep+ limit+ value+ maxBits+ (position + 1)+ (soFar + delta)+ else pure ()++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
@@ -0,0 +1,14 @@+module Rattletrap.Encode.CompressedWordVector+ ( putCompressedWordVector+ ) where++import Rattletrap.Encode.CompressedWord+import Rattletrap.Type.CompressedWordVector++import qualified Data.Binary.Bits.Put as BinaryBits++putCompressedWordVector :: CompressedWordVector -> BinaryBits.BitPut ()+putCompressedWordVector compressedWordVector = do+ putCompressedWord (compressedWordVectorX compressedWordVector)+ putCompressedWord (compressedWordVectorY compressedWordVector)+ putCompressedWord (compressedWordVectorZ compressedWordVector)
@@ -0,0 +1,39 @@+module Rattletrap.Encode.Content+ ( putContent+ ) where++import Rattletrap.Encode.Cache+import Rattletrap.Encode.ClassMapping+import Rattletrap.Encode.Frame+import Rattletrap.Encode.KeyFrame+import Rattletrap.Encode.List+import Rattletrap.Encode.Mark+import Rattletrap.Encode.Message+import Rattletrap.Encode.Str+import Rattletrap.Encode.Word32le+import Rattletrap.Type.Content+import Rattletrap.Type.Word32le+import Rattletrap.Utility.Bytes++import qualified Data.Binary as Binary+import qualified Data.Binary.Bits.Put as BinaryBits+import qualified Data.Binary.Put as Binary++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 (BinaryBits.runBitPut (putFrames (contentFrames content)))+ Binary.putLazyByteString+ (reverseBytes (padBytes (word32leValue 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)
@@ -0,0 +1,19 @@+module Rattletrap.Encode.DamageStateAttribute+ ( putDamageStateAttribute+ ) where++import Rattletrap.Encode.Int32le+import Rattletrap.Encode.Vector+import Rattletrap.Encode.Word8le+import Rattletrap.Type.DamageStateAttribute++import qualified Data.Binary.Bits.Put as BinaryBits++putDamageStateAttribute :: DamageStateAttribute -> BinaryBits.BitPut ()+putDamageStateAttribute damageStateAttribute = do+ putWord8Bits (damageStateAttributeUnknown1 damageStateAttribute)+ BinaryBits.putBool (damageStateAttributeUnknown2 damageStateAttribute)+ putInt32Bits (damageStateAttributeUnknown3 damageStateAttribute)+ putVector (damageStateAttributeUnknown4 damageStateAttribute)+ BinaryBits.putBool (damageStateAttributeUnknown5 damageStateAttribute)+ BinaryBits.putBool (damageStateAttributeUnknown6 damageStateAttribute)
@@ -0,0 +1,18 @@+module Rattletrap.Encode.DemolishAttribute+ ( putDemolishAttribute+ ) where++import Rattletrap.Encode.Vector+import Rattletrap.Encode.Word32le+import Rattletrap.Type.DemolishAttribute++import qualified Data.Binary.Bits.Put as BinaryBits++putDemolishAttribute :: DemolishAttribute -> BinaryBits.BitPut ()+putDemolishAttribute demolishAttribute = do+ BinaryBits.putBool (demolishAttributeAttackerFlag demolishAttribute)+ putWord32Bits (demolishAttributeAttackerActorId demolishAttribute)+ BinaryBits.putBool (demolishAttributeVictimFlag demolishAttribute)+ putWord32Bits (demolishAttributeVictimActorId demolishAttribute)+ putVector (demolishAttributeAttackerVelocity demolishAttribute)+ putVector (demolishAttributeVictimVelocity demolishAttribute)
@@ -0,0 +1,10 @@+module Rattletrap.Encode.DestroyedReplication+ ( putDestroyedReplication+ ) where++import Rattletrap.Type.DestroyedReplication++import qualified Data.Binary.Bits.Put as BinaryBits++putDestroyedReplication :: DestroyedReplication -> BinaryBits.BitPut ()+putDestroyedReplication _ = pure ()
@@ -0,0 +1,16 @@+module Rattletrap.Encode.Dictionary+ ( putDictionary+ ) where++import Rattletrap.Encode.Str+import Rattletrap.Type.Dictionary++import qualified Data.Binary as Binary++putDictionary :: (a -> Binary.Put) -> Dictionary a -> Binary.Put+putDictionary f x = case x of+ DictionaryElement k v y -> do+ putText k+ f v+ putDictionary f y+ DictionaryEnd y -> putText y
@@ -0,0 +1,11 @@+module Rattletrap.Encode.EnumAttribute+ ( putEnumAttribute+ ) where++import Rattletrap.Type.EnumAttribute++import qualified Data.Binary.Bits.Put as BinaryBits++putEnumAttribute :: EnumAttribute -> BinaryBits.BitPut ()+putEnumAttribute enumAttribute =+ BinaryBits.putWord16be 11 (enumAttributeValue enumAttribute)
@@ -0,0 +1,15 @@+module Rattletrap.Encode.ExplosionAttribute+ ( putExplosionAttribute+ ) where++import Rattletrap.Encode.Int32le+import Rattletrap.Encode.Vector+import Rattletrap.Type.ExplosionAttribute++import qualified Data.Binary.Bits.Put as BinaryBits++putExplosionAttribute :: ExplosionAttribute -> BinaryBits.BitPut ()+putExplosionAttribute explosionAttribute = do+ BinaryBits.putBool False+ putInt32Bits (explosionAttributeActorId explosionAttribute)+ putVector (explosionAttributeLocation explosionAttribute)
@@ -0,0 +1,15 @@+module Rattletrap.Encode.ExtendedExplosionAttribute+ ( putExtendedExplosionAttribute+ ) where++import Rattletrap.Encode.ExplosionAttribute+import Rattletrap.Encode.FlaggedIntAttribute+import Rattletrap.Type.ExtendedExplosionAttribute++import qualified Data.Binary.Bits.Put as BinaryBits++putExtendedExplosionAttribute+ :: ExtendedExplosionAttribute -> BinaryBits.BitPut ()+putExtendedExplosionAttribute x = do+ putExplosionAttribute (extendedExplosionAttributeExplosion x)+ putFlaggedIntAttribute (extendedExplosionAttributeUnknown x)
@@ -0,0 +1,13 @@+module Rattletrap.Encode.FlaggedIntAttribute+ ( putFlaggedIntAttribute+ ) where++import Rattletrap.Encode.Int32le+import Rattletrap.Type.FlaggedIntAttribute++import qualified Data.Binary.Bits.Put as BinaryBits++putFlaggedIntAttribute :: FlaggedIntAttribute -> BinaryBits.BitPut ()+putFlaggedIntAttribute flaggedIntAttribute = do+ BinaryBits.putBool (flaggedIntAttributeFlag flaggedIntAttribute)+ putInt32Bits (flaggedIntAttributeInt flaggedIntAttribute)
@@ -0,0 +1,20 @@+module Rattletrap.Encode.Float32le+ ( putFloat32+ , putFloat32Bits+ ) where++import Rattletrap.Type.Float32le+import Rattletrap.Utility.Bytes++import qualified Data.Binary as Binary+import qualified Data.Binary.Bits.Put as BinaryBits+import qualified Data.Binary.Put as Binary+import qualified Data.ByteString.Lazy as LazyBytes++putFloat32 :: Float32le -> Binary.Put+putFloat32 = Binary.putFloatle . float32leValue++putFloat32Bits :: Float32le -> BinaryBits.BitPut ()+putFloat32Bits float32 = do+ let bytes = Binary.runPut (putFloat32 float32)+ BinaryBits.putByteString (LazyBytes.toStrict (reverseBytes bytes))
@@ -0,0 +1,12 @@+module Rattletrap.Encode.FloatAttribute+ ( putFloatAttribute+ ) where++import Rattletrap.Encode.Float32le+import Rattletrap.Type.FloatAttribute++import qualified Data.Binary.Bits.Put as BinaryBits++putFloatAttribute :: FloatAttribute -> BinaryBits.BitPut ()+putFloatAttribute floatAttribute =+ putFloat32Bits (floatAttributeValue floatAttribute)
@@ -0,0 +1,19 @@+module Rattletrap.Encode.Frame+ ( putFrames+ , putFrame+ ) where++import Rattletrap.Encode.Float32le+import Rattletrap.Encode.Replication+import Rattletrap.Type.Frame++import qualified Data.Binary.Bits.Put as BinaryBits++putFrames :: [Frame] -> BinaryBits.BitPut ()+putFrames = mapM_ putFrame++putFrame :: Frame -> BinaryBits.BitPut ()+putFrame frame = do+ putFloat32Bits (frameTime frame)+ putFloat32Bits (frameDelta frame)+ putReplications (frameReplications frame)
@@ -0,0 +1,13 @@+module Rattletrap.Encode.GameModeAttribute+ ( putGameModeAttribute+ ) where++import Rattletrap.Type.GameModeAttribute++import qualified Data.Binary.Bits.Put as BinaryBits++putGameModeAttribute :: GameModeAttribute -> BinaryBits.BitPut ()+putGameModeAttribute gameModeAttribute = do+ let numBits = gameModeAttributeNumBits gameModeAttribute+ let word = gameModeAttributeWord gameModeAttribute+ BinaryBits.putWord8 numBits word
@@ -0,0 +1,21 @@+module Rattletrap.Encode.Header+ ( putHeader+ ) where++import Rattletrap.Encode.Dictionary+import Rattletrap.Encode.Property+import Rattletrap.Encode.Str+import Rattletrap.Encode.Word32le+import Rattletrap.Type.Header++import qualified Data.Binary as Binary++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)
@@ -0,0 +1,18 @@+module Rattletrap.Encode.Initialization+ ( putInitialization+ ) where++import Rattletrap.Encode.Int8Vector+import Rattletrap.Encode.Vector+import Rattletrap.Type.Initialization++import qualified Data.Binary.Bits.Put as BinaryBits++putInitialization :: Initialization -> BinaryBits.BitPut ()+putInitialization initialization = do+ case initializationLocation initialization of+ Nothing -> pure ()+ Just location -> putVector location+ case initializationRotation initialization of+ Nothing -> pure ()+ Just rotation -> putInt8Vector rotation
@@ -0,0 +1,20 @@+module Rattletrap.Encode.Int32le+ ( putInt32+ , putInt32Bits+ ) where++import Rattletrap.Type.Int32le+import Rattletrap.Utility.Bytes++import qualified Data.Binary as Binary+import qualified Data.Binary.Bits.Put as BinaryBits+import qualified Data.Binary.Put as Binary+import qualified Data.ByteString.Lazy as LazyBytes++putInt32 :: Int32le -> Binary.Put+putInt32 int32 = Binary.putInt32le (int32leValue int32)++putInt32Bits :: Int32le -> BinaryBits.BitPut ()+putInt32Bits int32 = do+ let bytes = Binary.runPut (putInt32 int32)+ BinaryBits.putByteString (LazyBytes.toStrict (reverseBytes bytes))
@@ -0,0 +1,22 @@+module Rattletrap.Encode.Int8Vector+ ( putInt8Vector+ ) where++import Rattletrap.Encode.Int8le+import Rattletrap.Type.Int8Vector+import Rattletrap.Type.Int8le++import qualified Data.Binary.Bits.Put as BinaryBits++putInt8Vector :: Int8Vector -> BinaryBits.BitPut ()+putInt8Vector int8Vector = do+ putInt8VectorField (int8VectorX int8Vector)+ putInt8VectorField (int8VectorY int8Vector)+ putInt8VectorField (int8VectorZ int8Vector)++putInt8VectorField :: Maybe Int8le -> BinaryBits.BitPut ()+putInt8VectorField maybeField = case maybeField of+ Nothing -> BinaryBits.putBool False+ Just field -> do+ BinaryBits.putBool True+ putInt8Bits field
@@ -0,0 +1,19 @@+module Rattletrap.Encode.Int8le+ ( putInt8+ , putInt8Bits+ ) where++import Rattletrap.Type.Int8le+import Rattletrap.Utility.Bytes++import qualified Data.Binary.Bits.Put as BinaryBits+import qualified Data.Binary.Put as Binary+import qualified Data.ByteString.Lazy as LazyBytes++putInt8 :: Int8le -> Binary.Put+putInt8 int8 = Binary.putInt8 (int8leValue int8)++putInt8Bits :: Int8le -> BinaryBits.BitPut ()+putInt8Bits int8 = do+ let bytes = Binary.runPut (putInt8 int8)+ BinaryBits.putByteString (LazyBytes.toStrict (reverseBytes bytes))
@@ -0,0 +1,11 @@+module Rattletrap.Encode.IntAttribute+ ( putIntAttribute+ ) where++import Rattletrap.Encode.Int32le+import Rattletrap.Type.IntAttribute++import qualified Data.Binary.Bits.Put as BinaryBits++putIntAttribute :: IntAttribute -> BinaryBits.BitPut ()+putIntAttribute intAttribute = putInt32Bits (intAttributeValue intAttribute)
@@ -0,0 +1,15 @@+module Rattletrap.Encode.KeyFrame+ ( putKeyFrame+ ) where++import Rattletrap.Encode.Float32le+import Rattletrap.Encode.Word32le+import Rattletrap.Type.KeyFrame++import qualified Data.Binary as Binary++putKeyFrame :: KeyFrame -> Binary.Put+putKeyFrame keyFrame = do+ putFloat32 (keyFrameTime keyFrame)+ putWord32 (keyFrameFrame keyFrame)+ putWord32 (keyFramePosition keyFrame)
@@ -0,0 +1,15 @@+module Rattletrap.Encode.List+ ( putList+ ) where++import Rattletrap.Encode.Word32le+import Rattletrap.Type.List+import Rattletrap.Type.Word32le++import qualified Data.Binary as Binary++putList :: (a -> Binary.Put) -> List a -> Binary.Put+putList putElement list = do+ let elements = listValue list+ putWord32 (Word32le (fromIntegral (length elements)))+ mapM_ putElement elements
@@ -0,0 +1,30 @@+module Rattletrap.Encode.LoadoutAttribute+ ( putLoadoutAttribute+ ) where++import Rattletrap.Encode.Word32le+import Rattletrap.Encode.Word8le+import Rattletrap.Type.LoadoutAttribute++import qualified Data.Binary.Bits.Put as BinaryBits++putLoadoutAttribute :: LoadoutAttribute -> BinaryBits.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 -> BinaryBits.BitPut ()) -> BinaryBits.BitPut ()+putOptional m f = case m of+ Just x -> f x+ Nothing -> pure ()
@@ -0,0 +1,16 @@+module Rattletrap.Encode.LoadoutOnlineAttribute+ ( putLoadoutOnlineAttribute+ ) where++import Rattletrap.Encode.ProductAttribute+import Rattletrap.Encode.Word8le+import Rattletrap.Type.LoadoutOnlineAttribute+import Rattletrap.Type.Word8le++import qualified Data.Binary.Bits.Put as BinaryBits++putLoadoutOnlineAttribute :: LoadoutOnlineAttribute -> BinaryBits.BitPut ()+putLoadoutOnlineAttribute loadoutAttribute = do+ let attributes = loadoutAttributeValue loadoutAttribute+ putWord8Bits (Word8le (fromIntegral (length attributes)))+ mapM_ putProductAttributes attributes
@@ -0,0 +1,13 @@+module Rattletrap.Encode.LoadoutsAttribute+ ( putLoadoutsAttribute+ ) where++import Rattletrap.Encode.LoadoutAttribute+import Rattletrap.Type.LoadoutsAttribute++import qualified Data.Binary.Bits.Put as BinaryBits++putLoadoutsAttribute :: LoadoutsAttribute -> BinaryBits.BitPut ()+putLoadoutsAttribute loadoutsAttribute = do+ putLoadoutAttribute (loadoutsAttributeBlue loadoutsAttribute)+ putLoadoutAttribute (loadoutsAttributeOrange loadoutsAttribute)
@@ -0,0 +1,17 @@+module Rattletrap.Encode.LoadoutsOnlineAttribute+ ( putLoadoutsOnlineAttribute+ ) where++import Rattletrap.Encode.LoadoutOnlineAttribute+import Rattletrap.Type.LoadoutsOnlineAttribute++import qualified Data.Binary.Bits.Put as BinaryBits++putLoadoutsOnlineAttribute :: LoadoutsOnlineAttribute -> BinaryBits.BitPut ()+putLoadoutsOnlineAttribute loadoutsOnlineAttribute = do+ putLoadoutOnlineAttribute+ (loadoutsOnlineAttributeBlue loadoutsOnlineAttribute)+ putLoadoutOnlineAttribute+ (loadoutsOnlineAttributeOrange loadoutsOnlineAttribute)+ BinaryBits.putBool (loadoutsOnlineAttributeUnknown1 loadoutsOnlineAttribute)+ BinaryBits.putBool (loadoutsOnlineAttributeUnknown2 loadoutsOnlineAttribute)
@@ -0,0 +1,12 @@+module Rattletrap.Encode.LocationAttribute+ ( putLocationAttribute+ ) where++import Rattletrap.Encode.Vector+import Rattletrap.Type.LocationAttribute++import qualified Data.Binary.Bits.Put as BinaryBits++putLocationAttribute :: LocationAttribute -> BinaryBits.BitPut ()+putLocationAttribute locationAttribute =+ putVector (locationAttributeValue locationAttribute)
@@ -0,0 +1,14 @@+module Rattletrap.Encode.Mark+ ( putMark+ ) where++import Rattletrap.Encode.Str+import Rattletrap.Encode.Word32le+import Rattletrap.Type.Mark++import qualified Data.Binary as Binary++putMark :: Mark -> Binary.Put+putMark mark = do+ putText (markValue mark)+ putWord32 (markFrame mark)
@@ -0,0 +1,15 @@+module Rattletrap.Encode.Message+ ( putMessage+ ) where++import Rattletrap.Encode.Str+import Rattletrap.Encode.Word32le+import Rattletrap.Type.Message++import qualified Data.Binary as Binary++putMessage :: Message -> Binary.Put+putMessage message = do+ putWord32 (messageFrame message)+ putText (messageName message)+ putText (messageValue message)
@@ -0,0 +1,15 @@+module Rattletrap.Encode.MusicStingerAttribute+ ( putMusicStingerAttribute+ ) where++import Rattletrap.Encode.Word32le+import Rattletrap.Encode.Word8le+import Rattletrap.Type.MusicStingerAttribute++import qualified Data.Binary.Bits.Put as BinaryBits++putMusicStingerAttribute :: MusicStingerAttribute -> BinaryBits.BitPut ()+putMusicStingerAttribute musicStingerAttribute = do+ BinaryBits.putBool (musicStingerAttributeFlag musicStingerAttribute)+ putWord32Bits (musicStingerAttributeCue musicStingerAttribute)+ putWord8Bits (musicStingerAttributeTrigger musicStingerAttribute)
@@ -0,0 +1,18 @@+module Rattletrap.Encode.PartyLeaderAttribute+ ( putPartyLeaderAttribute+ ) where++import Rattletrap.Encode.RemoteId+import Rattletrap.Encode.Word8le+import Rattletrap.Type.PartyLeaderAttribute++import qualified Data.Binary.Bits.Put as BinaryBits++putPartyLeaderAttribute :: PartyLeaderAttribute -> BinaryBits.BitPut ()+putPartyLeaderAttribute partyLeaderAttribute = do+ putWord8Bits (partyLeaderAttributeSystemId partyLeaderAttribute)+ case partyLeaderAttributeId partyLeaderAttribute of+ Nothing -> pure ()+ Just (remoteId, localId) -> do+ putRemoteId remoteId+ putWord8Bits localId
@@ -0,0 +1,17 @@+module Rattletrap.Encode.PickupAttribute+ ( putPickupAttribute+ ) where++import Rattletrap.Encode.Word32le+import Rattletrap.Type.PickupAttribute++import qualified Data.Binary.Bits.Put as BinaryBits++putPickupAttribute :: PickupAttribute -> BinaryBits.BitPut ()+putPickupAttribute pickupAttribute = do+ case pickupAttributeInstigatorId pickupAttribute of+ Nothing -> BinaryBits.putBool False+ Just instigatorId -> do+ BinaryBits.putBool True+ putWord32Bits instigatorId+ BinaryBits.putBool (pickupAttributePickedUp pickupAttribute)
@@ -0,0 +1,12 @@+module Rattletrap.Encode.PlayerHistoryKeyAttribute+ ( putPlayerHistoryKeyAttribute+ ) where++import Rattletrap.Encode.Bitstream+import Rattletrap.Type.PlayerHistoryKeyAttribute++import qualified Data.Binary.Bits.Put as BinaryBits++putPlayerHistoryKeyAttribute+ :: PlayerHistoryKeyAttribute -> BinaryBits.BitPut ()+putPlayerHistoryKeyAttribute = putBitstream . playerHistoryKeyAttributeUnknown
@@ -0,0 +1,25 @@+module Rattletrap.Encode.PrivateMatchSettingsAttribute+ ( putPrivateMatchSettingsAttribute+ ) where++import Rattletrap.Encode.Str+import Rattletrap.Encode.Word32le+import Rattletrap.Type.PrivateMatchSettingsAttribute++import qualified Data.Binary.Bits.Put as BinaryBits++putPrivateMatchSettingsAttribute+ :: PrivateMatchSettingsAttribute -> BinaryBits.BitPut ()+putPrivateMatchSettingsAttribute privateMatchSettingsAttribute = do+ putTextBits+ (privateMatchSettingsAttributeMutators privateMatchSettingsAttribute)+ putWord32Bits+ (privateMatchSettingsAttributeJoinableBy privateMatchSettingsAttribute)+ putWord32Bits+ (privateMatchSettingsAttributeMaxPlayers privateMatchSettingsAttribute)+ putTextBits+ (privateMatchSettingsAttributeGameName privateMatchSettingsAttribute)+ putTextBits+ (privateMatchSettingsAttributePassword privateMatchSettingsAttribute)+ BinaryBits.putBool+ (privateMatchSettingsAttributeFlag privateMatchSettingsAttribute)
@@ -0,0 +1,43 @@+module Rattletrap.Encode.ProductAttribute+ ( putProductAttributes+ , putProductAttribute+ ) where++import Data.Semigroup ((<>))+import Rattletrap.Encode.CompressedWord+import Rattletrap.Encode.Word32le+import Rattletrap.Encode.Word8le+import Rattletrap.Type.ProductAttribute+import Rattletrap.Type.Str+import Rattletrap.Type.Word8le++import qualified Data.Binary.Bits.Put as BinaryBits++putProductAttributes :: [ProductAttribute] -> BinaryBits.BitPut ()+putProductAttributes attributes = do+ putWord8Bits (Word8le (fromIntegral (length attributes)))+ mapM_ putProductAttribute attributes++putProductAttribute :: ProductAttribute -> BinaryBits.BitPut ()+putProductAttribute attribute = do+ BinaryBits.putBool (productAttributeUnknown attribute)+ putWord32Bits (productAttributeObjectId attribute)+ case productAttributeObjectName attribute of+ Just name -> case fromStr name of+ "TAGame.ProductAttribute_Painted_TA" ->+ case productAttributeValue attribute of+ Nothing -> pure ()+ Just (Left x) -> putCompressedWord x+ Just (Right x) -> BinaryBits.putWord32be 31 x+ "TAGame.ProductAttribute_UserColor_TA" ->+ case productAttributeValue attribute of+ Nothing -> BinaryBits.putBool False+ Just value -> do+ BinaryBits.putBool True+ case value of+ Left x -> putCompressedWord x+ Right x -> BinaryBits.putWord32be 31 x+ _ ->+ fail ("unknown object name for product attribute " <> show attribute)+ Nothing ->+ fail ("missing object name for product attribute " <> show attribute)
@@ -0,0 +1,16 @@+module Rattletrap.Encode.Property+ ( putProperty+ ) where++import Rattletrap.Encode.PropertyValue+import Rattletrap.Encode.Str+import Rattletrap.Encode.Word64le+import Rattletrap.Type.Property++import qualified Data.Binary as Binary++putProperty :: Property -> Binary.Put+putProperty property = do+ putText (propertyKind property)+ putWord64 (propertySize property)+ putPropertyValue putProperty (propertyValue property)
@@ -0,0 +1,29 @@+module Rattletrap.Encode.PropertyValue+ ( putPropertyValue+ ) where++import Rattletrap.Encode.Dictionary+import Rattletrap.Encode.Float32le+import Rattletrap.Encode.Int32le+import Rattletrap.Encode.List+import Rattletrap.Encode.Str+import Rattletrap.Encode.Word64le+import Rattletrap.Encode.Word8le+import Rattletrap.Type.PropertyValue++import qualified Data.Binary as Binary++putPropertyValue :: (a -> Binary.Put) -> PropertyValue a -> Binary.Put+putPropertyValue putProperty value = case value of+ PropertyValueArray list -> putList (putDictionary putProperty) list+ PropertyValueBool word8 -> putWord8 word8+ PropertyValueByte k mv -> do+ putText k+ case mv of+ Nothing -> pure ()+ Just v -> putText v+ PropertyValueFloat float32 -> putFloat32 float32+ PropertyValueInt int32 -> putInt32 int32+ PropertyValueName text -> putText text+ PropertyValueQWord word64 -> putWord64 word64+ PropertyValueStr text -> putText text
@@ -0,0 +1,12 @@+module Rattletrap.Encode.QWordAttribute+ ( putQWordAttribute+ ) where++import Rattletrap.Encode.Word64le+import Rattletrap.Type.QWordAttribute++import qualified Data.Binary.Bits.Put as BinaryBits++putQWordAttribute :: QWordAttribute -> BinaryBits.BitPut ()+putQWordAttribute qWordAttribute =+ putWord64Bits (qWordAttributeValue qWordAttribute)
@@ -0,0 +1,24 @@+module Rattletrap.Encode.RemoteId+ ( putRemoteId+ ) where++import Rattletrap.Encode.Bitstream+import Rattletrap.Encode.Word64le+import Rattletrap.Type.RemoteId+import Rattletrap.Utility.Bytes++import qualified Data.Binary.Bits.Put as BinaryBits+import qualified Data.ByteString.Lazy as LazyBytes++putRemoteId :: RemoteId -> BinaryBits.BitPut ()+putRemoteId remoteId = case remoteId of+ RemoteIdPlayStation name bytes -> do+ let+ rawName = LazyBytes.toStrict+ (reverseBytes (padBytes (16 :: Int) (encodeLatin1 name)))+ BinaryBits.putByteString rawName+ BinaryBits.putByteString (LazyBytes.toStrict (LazyBytes.pack bytes))+ RemoteIdSplitscreen word24 -> BinaryBits.putWord32be 24 word24+ RemoteIdSteam word64 -> putWord64Bits word64+ RemoteIdSwitch x -> putBitstream x+ RemoteIdXbox word64 -> putWord64Bits word64
@@ -0,0 +1,20 @@+module Rattletrap.Encode.Replay+ ( putReplay+ ) where++import Rattletrap.Encode.Content+import Rattletrap.Encode.Header+import Rattletrap.Encode.Section+import Rattletrap.Type.Replay++import qualified Data.Binary as Binary++-- | 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)
@@ -0,0 +1,21 @@+module Rattletrap.Encode.Replication+ ( putReplications+ , putReplication+ ) where++import Rattletrap.Encode.CompressedWord+import Rattletrap.Encode.ReplicationValue+import Rattletrap.Type.Replication++import qualified Data.Binary.Bits.Put as BinaryBits++putReplications :: [Replication] -> BinaryBits.BitPut ()+putReplications replications = do+ mapM_ putReplication replications+ BinaryBits.putBool False++putReplication :: Replication -> BinaryBits.BitPut ()+putReplication replication = do+ BinaryBits.putBool True+ putCompressedWord (replicationActorId replication)+ putReplicationValue (replicationValue replication)
@@ -0,0 +1,24 @@+module Rattletrap.Encode.ReplicationValue+ ( putReplicationValue+ ) where++import Rattletrap.Encode.DestroyedReplication+import Rattletrap.Encode.SpawnedReplication+import Rattletrap.Encode.UpdatedReplication+import Rattletrap.Type.ReplicationValue++import qualified Data.Binary.Bits.Put as BinaryBits++putReplicationValue :: ReplicationValue -> BinaryBits.BitPut ()+putReplicationValue value = case value of+ ReplicationValueSpawned x -> do+ BinaryBits.putBool True+ BinaryBits.putBool True+ putSpawnedReplication x+ ReplicationValueUpdated x -> do+ BinaryBits.putBool True+ BinaryBits.putBool False+ putUpdatedReplication x+ ReplicationValueDestroyed x -> do+ BinaryBits.putBool False+ putDestroyedReplication x
@@ -0,0 +1,23 @@+module Rattletrap.Encode.ReservationAttribute+ ( putReservationAttribute+ ) where++import Rattletrap.Encode.CompressedWord+import Rattletrap.Encode.Str+import Rattletrap.Encode.UniqueIdAttribute+import Rattletrap.Type.ReservationAttribute++import qualified Data.Binary.Bits.Put as BinaryBits++putReservationAttribute :: ReservationAttribute -> BinaryBits.BitPut ()+putReservationAttribute reservationAttribute = do+ putCompressedWord (reservationAttributeNumber reservationAttribute)+ putUniqueIdAttribute (reservationAttributeUniqueId reservationAttribute)+ case reservationAttributeName reservationAttribute of+ Nothing -> pure ()+ Just name -> putTextBits name+ BinaryBits.putBool (reservationAttributeUnknown1 reservationAttribute)+ BinaryBits.putBool (reservationAttributeUnknown2 reservationAttribute)+ case reservationAttributeUnknown3 reservationAttribute of+ Nothing -> pure ()+ Just c -> BinaryBits.putWord8 6 c
@@ -0,0 +1,22 @@+module Rattletrap.Encode.RigidBodyStateAttribute+ ( putRigidBodyStateAttribute+ ) where++import Rattletrap.Encode.CompressedWordVector+import Rattletrap.Encode.Vector+import Rattletrap.Type.RigidBodyStateAttribute++import qualified Data.Binary.Bits.Put as BinaryBits++putRigidBodyStateAttribute :: RigidBodyStateAttribute -> BinaryBits.BitPut ()+putRigidBodyStateAttribute rigidBodyStateAttribute = do+ BinaryBits.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
@@ -0,0 +1,27 @@+module Rattletrap.Encode.Section+ ( putSection+ ) where++import Rattletrap.Encode.Word32le+import Rattletrap.Type.Section+import Rattletrap.Type.Word32le+import Rattletrap.Utility.Crc++import qualified Data.Binary as Binary+import qualified Data.Binary.Put as Binary+import qualified Data.ByteString.Lazy as LazyBytes++-- | 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 = LazyBytes.length rawBody+ let crc = getCrc32 rawBody+ putWord32 (Word32le (fromIntegral size))+ putWord32 (Word32le crc)+ Binary.putLazyByteString rawBody
@@ -0,0 +1,18 @@+module Rattletrap.Encode.SpawnedReplication+ ( putSpawnedReplication+ ) where++import Rattletrap.Encode.Initialization+import Rattletrap.Encode.Word32le+import Rattletrap.Type.SpawnedReplication++import qualified Data.Binary.Bits.Put as BinaryBits++putSpawnedReplication :: SpawnedReplication -> BinaryBits.BitPut ()+putSpawnedReplication spawnedReplication = do+ BinaryBits.putBool (spawnedReplicationFlag spawnedReplication)+ case spawnedReplicationNameIndex spawnedReplication of+ Nothing -> pure ()+ Just nameIndex -> putWord32Bits nameIndex+ putWord32Bits (spawnedReplicationObjectId spawnedReplication)+ putInitialization (spawnedReplicationInitialization spawnedReplication)
@@ -0,0 +1,55 @@+module Rattletrap.Encode.Str+ ( putText+ , putTextBits+ ) where++import Rattletrap.Encode.Int32le+import Rattletrap.Type.Common+import Rattletrap.Type.Int32le+import Rattletrap.Type.Str+import Rattletrap.Utility.Bytes++import qualified Data.Binary as Binary+import qualified Data.Binary.Bits.Put as BinaryBits+import qualified Data.Binary.Put as Binary+import qualified Data.ByteString.Lazy as LazyBytes+import qualified Data.Char as Char+import qualified Data.Text as Text+import qualified Data.Text.Encoding as Text++putText :: Str -> Binary.Put+putText text = do+ let size = getTextSize text+ let encode = getTextEncoder size+ putInt32 size+ Binary.putLazyByteString (encode (addNull (strValue text)))++putTextBits :: Str -> BinaryBits.BitPut ()+putTextBits text = do+ let size = getTextSize text+ let encode = getTextEncoder size+ putInt32Bits size+ BinaryBits.putByteString+ (LazyBytes.toStrict (reverseBytes (encode (addNull (strValue text)))))++getTextSize :: Str -> Int32le+getTextSize text =+ let+ value = strValue text+ scale = if Text.all Char.isLatin1 value then 1 else -1 :: Int32+ rawSize = if Text.null value+ then 0+ else fromIntegral (Text.length value) + 1 :: Int32+ size = if value == Text.pack "\x00\x00\x00None"+ then 0x05000000+ else scale * rawSize :: Int32+ in+ Int32le size++getTextEncoder :: Int32le -> Text.Text -> LazyBytes.ByteString+getTextEncoder size text = if size < Int32le 0+ then LazyBytes.fromStrict (Text.encodeUtf16LE text)+ else encodeLatin1 text++addNull :: Text.Text -> Text.Text+addNull text = if Text.null text then text else Text.snoc text '\x00'
@@ -0,0 +1,12 @@+module Rattletrap.Encode.StringAttribute+ ( putStringAttribute+ ) where++import Rattletrap.Encode.Str+import Rattletrap.Type.StringAttribute++import qualified Data.Binary.Bits.Put as BinaryBits++putStringAttribute :: StringAttribute -> BinaryBits.BitPut ()+putStringAttribute stringAttribute =+ putTextBits (stringAttributeValue stringAttribute)
@@ -0,0 +1,17 @@+module Rattletrap.Encode.TeamPaintAttribute+ ( putTeamPaintAttribute+ ) where++import Rattletrap.Encode.Word32le+import Rattletrap.Encode.Word8le+import Rattletrap.Type.TeamPaintAttribute++import qualified Data.Binary.Bits.Put as BinaryBits++putTeamPaintAttribute :: TeamPaintAttribute -> BinaryBits.BitPut ()+putTeamPaintAttribute teamPaintAttribute = do+ putWord8Bits (teamPaintAttributeTeam teamPaintAttribute)+ putWord8Bits (teamPaintAttributePrimaryColor teamPaintAttribute)+ putWord8Bits (teamPaintAttributeAccentColor teamPaintAttribute)+ putWord32Bits (teamPaintAttributePrimaryFinish teamPaintAttribute)+ putWord32Bits (teamPaintAttributeAccentFinish teamPaintAttribute)
@@ -0,0 +1,15 @@+module Rattletrap.Encode.UniqueIdAttribute+ ( putUniqueIdAttribute+ ) where++import Rattletrap.Encode.RemoteId+import Rattletrap.Encode.Word8le+import Rattletrap.Type.UniqueIdAttribute++import qualified Data.Binary.Bits.Put as BinaryBits++putUniqueIdAttribute :: UniqueIdAttribute -> BinaryBits.BitPut ()+putUniqueIdAttribute uniqueIdAttribute = do+ putWord8Bits (uniqueIdAttributeSystemId uniqueIdAttribute)+ putRemoteId (uniqueIdAttributeRemoteId uniqueIdAttribute)+ putWord8Bits (uniqueIdAttributeLocalId uniqueIdAttribute)
@@ -0,0 +1,12 @@+module Rattletrap.Encode.UpdatedReplication+ ( putUpdatedReplication+ ) where++import Rattletrap.Encode.Attribute+import Rattletrap.Type.UpdatedReplication++import qualified Data.Binary.Bits.Put as BinaryBits++putUpdatedReplication :: UpdatedReplication -> BinaryBits.BitPut ()+putUpdatedReplication updatedReplication =+ putAttributes (updatedReplicationAttributes updatedReplication)
@@ -0,0 +1,26 @@+module Rattletrap.Encode.Vector+ ( putVector+ ) where++import Rattletrap.Encode.CompressedWord+import Rattletrap.Type.CompressedWord+import Rattletrap.Type.Vector++import qualified Data.Binary.Bits.Put as BinaryBits++putVector :: Vector -> BinaryBits.BitPut ()+putVector vector = do+ let+ bitSize =+ round (logBase (2 :: Float) (fromIntegral (vectorBias vector))) - 1 :: Word+ dx =+ fromIntegral (vectorX vector + fromIntegral (vectorBias vector)) :: Word+ dy =+ fromIntegral (vectorY vector + fromIntegral (vectorBias vector)) :: Word+ dz =+ fromIntegral (vectorZ vector + fromIntegral (vectorBias vector)) :: Word+ limit = 2 ^ (bitSize + 2) :: Word+ putCompressedWord (CompressedWord 19 bitSize)+ putCompressedWord (CompressedWord limit dx)+ putCompressedWord (CompressedWord limit dy)+ putCompressedWord (CompressedWord limit dz)
@@ -0,0 +1,19 @@+module Rattletrap.Encode.WeldedInfoAttribute+ ( putWeldedInfoAttribute+ ) where++import Rattletrap.Encode.Float32le+import Rattletrap.Encode.Int32le+import Rattletrap.Encode.Int8Vector+import Rattletrap.Encode.Vector+import Rattletrap.Type.WeldedInfoAttribute++import qualified Data.Binary.Bits.Put as BinaryBits++putWeldedInfoAttribute :: WeldedInfoAttribute -> BinaryBits.BitPut ()+putWeldedInfoAttribute weldedInfoAttribute = do+ BinaryBits.putBool (weldedInfoAttributeActive weldedInfoAttribute)+ putInt32Bits (weldedInfoAttributeActorId weldedInfoAttribute)+ putVector (weldedInfoAttributeOffset weldedInfoAttribute)+ putFloat32Bits (weldedInfoAttributeMass weldedInfoAttribute)+ putInt8Vector (weldedInfoAttributeRotation weldedInfoAttribute)
@@ -0,0 +1,20 @@+module Rattletrap.Encode.Word32le+ ( putWord32+ , putWord32Bits+ ) where++import Rattletrap.Type.Word32le+import Rattletrap.Utility.Bytes++import qualified Data.Binary as Binary+import qualified Data.Binary.Bits.Put as BinaryBits+import qualified Data.Binary.Put as Binary+import qualified Data.ByteString.Lazy as LazyBytes++putWord32 :: Word32le -> Binary.Put+putWord32 word32 = Binary.putWord32le (word32leValue word32)++putWord32Bits :: Word32le -> BinaryBits.BitPut ()+putWord32Bits word32 = do+ let bytes = Binary.runPut (putWord32 word32)+ BinaryBits.putByteString (LazyBytes.toStrict (reverseBytes bytes))
@@ -0,0 +1,20 @@+module Rattletrap.Encode.Word64le+ ( putWord64+ , putWord64Bits+ ) where++import Rattletrap.Type.Word64le+import Rattletrap.Utility.Bytes++import qualified Data.Binary as Binary+import qualified Data.Binary.Bits.Put as BinaryBits+import qualified Data.Binary.Put as Binary+import qualified Data.ByteString.Lazy as LazyBytes++putWord64 :: Word64le -> Binary.Put+putWord64 word64 = Binary.putWord64le (word64leValue word64)++putWord64Bits :: Word64le -> BinaryBits.BitPut ()+putWord64Bits word64 = do+ let bytes = Binary.runPut (putWord64 word64)+ BinaryBits.putByteString (LazyBytes.toStrict (reverseBytes bytes))
@@ -0,0 +1,20 @@+module Rattletrap.Encode.Word8le+ ( putWord8+ , putWord8Bits+ ) where++import Rattletrap.Type.Word8le+import Rattletrap.Utility.Bytes++import qualified Data.Binary as Binary+import qualified Data.Binary.Bits.Put as BinaryBits+import qualified Data.Binary.Put as Binary+import qualified Data.ByteString.Lazy as LazyBytes++putWord8 :: Word8le -> Binary.Put+putWord8 word8 = Binary.putWord8 (word8leValue word8)++putWord8Bits :: Word8le -> BinaryBits.BitPut ()+putWord8Bits word8 = do+ let bytes = Binary.runPut (putWord8 word8)+ BinaryBits.putByteString (LazyBytes.toStrict (reverseBytes bytes))
@@ -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
@@ -0,0 +1,19 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.AppliedDamageAttribute+ ( AppliedDamageAttribute(..)+ ) where++import Rattletrap.Type.Common+import Rattletrap.Type.Int32le+import Rattletrap.Type.Vector+import Rattletrap.Type.Word8le++data AppliedDamageAttribute = AppliedDamageAttribute+ { appliedDamageAttributeUnknown1 :: Word8le+ , appliedDamageAttributeLocation :: Vector+ , appliedDamageAttributeUnknown3 :: Int32le+ , appliedDamageAttributeUnknown4 :: Int32le+ } deriving (Eq, Ord, Show)++$(deriveJson ''AppliedDamageAttribute)
@@ -0,0 +1,20 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.Attribute+ ( Attribute(..)+ ) where++import Rattletrap.Type.AttributeValue+import Rattletrap.Type.Common+import Rattletrap.Type.CompressedWord+import Rattletrap.Type.Str++data Attribute = Attribute+ { attributeId :: CompressedWord+ , attributeName :: Str+ -- ^ Read-only! Changing an attribute's name requires editing the class+ -- attribute map.+ , attributeValue :: AttributeValue+ } deriving (Eq, Ord, Show)++$(deriveJson ''Attribute)
@@ -0,0 +1,15 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.AttributeMapping+ ( AttributeMapping(..)+ ) where++import Rattletrap.Type.Common+import Rattletrap.Type.Word32le++data AttributeMapping = AttributeMapping+ { attributeMappingObjectId :: Word32le+ , attributeMappingStreamId :: Word32le+ } deriving (Eq, Ord, Show)++$(deriveJson ''AttributeMapping)
@@ -0,0 +1,43 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.AttributeType+ ( AttributeType(..)+ ) where++import Rattletrap.Type.Common++data AttributeType+ = AttributeTypeAppliedDamage+ | AttributeTypeBoolean+ | AttributeTypeByte+ | AttributeTypeCamSettings+ | AttributeTypeClubColors+ | AttributeTypeDamageState+ | AttributeTypeDemolish+ | AttributeTypeEnum+ | AttributeTypeExplosion+ | AttributeTypeExtendedExplosion+ | AttributeTypeFlaggedInt+ | AttributeTypeFloat+ | AttributeTypeGameMode+ | AttributeTypeInt+ | AttributeTypeLoadout+ | AttributeTypeLoadoutOnline+ | AttributeTypeLoadouts+ | AttributeTypeLoadoutsOnline+ | AttributeTypeLocation+ | AttributeTypeMusicStinger+ | AttributeTypePartyLeader+ | AttributeTypePickup+ | AttributeTypePlayerHistoryKey+ | AttributeTypePrivateMatchSettings+ | AttributeTypeQWord+ | AttributeTypeReservation+ | AttributeTypeRigidBodyState+ | AttributeTypeString+ | AttributeTypeTeamPaint+ | AttributeTypeUniqueId+ | AttributeTypeWeldedInfo+ deriving (Eq, Ord, Show)++$(deriveJson ''AttributeType)
@@ -0,0 +1,74 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.AttributeValue+ ( AttributeValue(..)+ ) where++import Rattletrap.Type.AppliedDamageAttribute+import Rattletrap.Type.BooleanAttribute+import Rattletrap.Type.ByteAttribute+import Rattletrap.Type.CamSettingsAttribute+import Rattletrap.Type.ClubColorsAttribute+import Rattletrap.Type.Common+import Rattletrap.Type.DamageStateAttribute+import Rattletrap.Type.DemolishAttribute+import Rattletrap.Type.EnumAttribute+import Rattletrap.Type.ExplosionAttribute+import Rattletrap.Type.ExtendedExplosionAttribute+import Rattletrap.Type.FlaggedIntAttribute+import Rattletrap.Type.FloatAttribute+import Rattletrap.Type.GameModeAttribute+import Rattletrap.Type.IntAttribute+import Rattletrap.Type.LoadoutAttribute+import Rattletrap.Type.LoadoutOnlineAttribute+import Rattletrap.Type.LoadoutsAttribute+import Rattletrap.Type.LoadoutsOnlineAttribute+import Rattletrap.Type.LocationAttribute+import Rattletrap.Type.MusicStingerAttribute+import Rattletrap.Type.PartyLeaderAttribute+import Rattletrap.Type.PickupAttribute+import Rattletrap.Type.PlayerHistoryKeyAttribute+import Rattletrap.Type.PrivateMatchSettingsAttribute+import Rattletrap.Type.QWordAttribute+import Rattletrap.Type.ReservationAttribute+import Rattletrap.Type.RigidBodyStateAttribute+import Rattletrap.Type.StringAttribute+import Rattletrap.Type.TeamPaintAttribute+import Rattletrap.Type.UniqueIdAttribute+import Rattletrap.Type.WeldedInfoAttribute++data AttributeValue+ = AttributeValueAppliedDamage AppliedDamageAttribute+ | AttributeValueBoolean BooleanAttribute+ | AttributeValueByte ByteAttribute+ | AttributeValueCamSettings CamSettingsAttribute+ | AttributeValueClubColors ClubColorsAttribute+ | AttributeValueDamageState DamageStateAttribute+ | AttributeValueDemolish DemolishAttribute+ | AttributeValueEnum EnumAttribute+ | AttributeValueExplosion ExplosionAttribute+ | AttributeValueExtendedExplosion ExtendedExplosionAttribute+ | AttributeValueFlaggedInt FlaggedIntAttribute+ | AttributeValueFloat FloatAttribute+ | AttributeValueGameMode GameModeAttribute+ | AttributeValueInt IntAttribute+ | AttributeValueLoadout LoadoutAttribute+ | AttributeValueLoadoutOnline LoadoutOnlineAttribute+ | AttributeValueLoadouts LoadoutsAttribute+ | AttributeValueLoadoutsOnline LoadoutsOnlineAttribute+ | AttributeValueLocation LocationAttribute+ | AttributeValueMusicStinger MusicStingerAttribute+ | AttributeValuePartyLeader PartyLeaderAttribute+ | AttributeValuePickup PickupAttribute+ | AttributeValuePlayerHistoryKey PlayerHistoryKeyAttribute+ | AttributeValuePrivateMatchSettings PrivateMatchSettingsAttribute+ | AttributeValueQWord QWordAttribute+ | AttributeValueReservation ReservationAttribute+ | AttributeValueRigidBodyState RigidBodyStateAttribute+ | AttributeValueString StringAttribute+ | AttributeValueTeamPaint TeamPaintAttribute+ | AttributeValueUniqueId UniqueIdAttribute+ | AttributeValueWeldedInfo WeldedInfoAttribute+ deriving (Eq, Ord, Show)++$(deriveJson ''AttributeValue)
@@ -0,0 +1,28 @@+module Rattletrap.Type.Bitstream+ ( Bitstream(..)+ ) where++import qualified Data.Aeson as Json+import qualified Data.Bool as Bool+import qualified Data.Text as Text++newtype Bitstream = Bitstream+ { bitstreamValue :: [Bool]+ } deriving (Eq, Ord, Show)++instance Json.FromJSON Bitstream where+ parseJSON =+ Json.withText+ "Bitstream"+ (\text ->+ Bitstream <$>+ mapM+ (\char ->+ case char of+ '0' -> pure False+ '1' -> pure True+ _ -> fail ("invalid bit: " ++ show char))+ (Text.unpack text))++instance Json.ToJSON Bitstream where+ toJSON = Json.toJSON . fmap (Bool.bool '0' '1') . bitstreamValue
@@ -0,0 +1,13 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.BooleanAttribute+ ( BooleanAttribute(..)+ ) where++import Rattletrap.Type.Common++newtype BooleanAttribute = BooleanAttribute+ { booleanAttributeValue :: Bool+ } deriving (Eq, Ord, Show)++$(deriveJson ''BooleanAttribute)
@@ -0,0 +1,14 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.ByteAttribute+ ( ByteAttribute(..)+ ) where++import Rattletrap.Type.Common+import Rattletrap.Type.Word8le++newtype ByteAttribute = ByteAttribute+ { byteAttributeValue :: Word8le+ } deriving (Eq, Ord, Show)++$(deriveJson ''ByteAttribute)
@@ -0,0 +1,19 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.Cache+ ( Cache(..)+ ) where++import Rattletrap.Type.AttributeMapping+import Rattletrap.Type.Common+import Rattletrap.Type.List+import Rattletrap.Type.Word32le++data Cache = Cache+ { cacheClassId :: Word32le+ , cacheParentCacheId :: Word32le+ , cacheCacheId :: Word32le+ , cacheAttributeMappings :: List AttributeMapping+ } deriving (Eq, Ord, Show)++$(deriveJson ''Cache)
@@ -0,0 +1,20 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.CamSettingsAttribute+ ( CamSettingsAttribute(..)+ ) where++import Rattletrap.Type.Common+import Rattletrap.Type.Float32le++data CamSettingsAttribute = CamSettingsAttribute+ { camSettingsAttributeFov :: Float32le+ , camSettingsAttributeHeight :: Float32le+ , camSettingsAttributeAngle :: Float32le+ , camSettingsAttributeDistance :: Float32le+ , camSettingsAttributeStiffness :: Float32le+ , camSettingsAttributeSwivelSpeed :: Float32le+ , camSettingsAttributeTransitionSpeed :: Maybe Float32le+ } deriving (Eq, Ord, Show)++$(deriveJson ''CamSettingsAttribute)
@@ -0,0 +1,326 @@+module Rattletrap.Type.ClassAttributeMap+ ( ClassAttributeMap(..)+ , classHasLocation+ , classHasRotation+ , getAttributeIdLimit+ , getAttributeMap+ , getAttributeName+ , getClassName+ , getName+ , getObjectName+ , makeClassAttributeMap+ ) where++import Rattletrap.Data+import Rattletrap.Type.AttributeMapping+import Rattletrap.Type.Cache+import Rattletrap.Type.ClassMapping+import Rattletrap.Type.Common+import Rattletrap.Type.CompressedWord+import Rattletrap.Type.List+import Rattletrap.Type.Str+import Rattletrap.Type.Word32le++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++-- | 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 Word32le Str+ -- ^ A map from object IDs to their names.+ , classAttributeMapObjectClassMap :: Map Word32le Word32le+ -- ^ A map from object IDs to their class IDs.+ , classAttributeMapValue :: Map Word32le (Map Word32le Word32le)+ -- ^ A map from class IDs to a map from attribute stream IDs to attribute+ -- IDs.+ , classAttributeMapNameMap :: IntMap.IntMap Str+ } deriving (Eq, Ord, Show)++type Bimap l r = (Map l r, 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'.+makeClassAttributeMap+ :: List Str+ -- ^ From 'Rattletrap.Content.contentObjects'.+ -> List ClassMapping+ -- ^ From 'Rattletrap.Content.contentClassMappings'.+ -> List Cache+ -- ^ From 'Rattletrap.Content.contentCaches'.+ -> List Str+ -- ^ 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 = 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 :: List Str -> IntMap.IntMap Str+makeNameMap names = IntMap.fromDistinctAscList (zip [0 ..] (listValue names))++getName :: IntMap.IntMap Str -> Word32le -> Maybe Str+getName nameMap nameIndex =+ IntMap.lookup (fromIntegral (word32leValue nameIndex)) nameMap++makeObjectClassMap+ :: Map Word32le Str -> Bimap Word32le Str -> Map Word32le Word32le+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 Word32le Str -> Bimap Word32le Str -> Word32le -> Maybe Word32le+getClassId objectMap classMap objectId = do+ objectName <- getObjectName objectMap objectId+ className <- getClassName objectName+ lookupR className classMap++makeClassCache+ :: Bimap Word32le Str+ -> List Cache+ -> [(Maybe Str, Word32le, Word32le, Word32le)]+makeClassCache classMap caches = fmap+ ( \cache ->+ let+ classId = cacheClassId cache+ in+ ( lookupL classId classMap+ , classId+ , cacheCacheId cache+ , cacheParentCacheId cache+ )+ )+ (listValue caches)++makeClassMap :: List ClassMapping -> Bimap Word32le Str+makeClassMap classMappings = bimap+ ( fmap+ ( \classMapping ->+ (classMappingStreamId classMapping, classMappingName classMapping)+ )+ (listValue classMappings)+ )++makeAttributeMap :: List Cache -> Map Word32le (Map Word32le Word32le)+makeAttributeMap caches = Map.fromList+ ( fmap+ ( \cache ->+ ( cacheClassId cache+ , Map.fromList+ ( fmap+ ( \attributeMapping ->+ ( attributeMappingStreamId attributeMapping+ , attributeMappingObjectId attributeMapping+ )+ )+ (listValue (cacheAttributeMappings cache))+ )+ )+ )+ (listValue caches)+ )++makeShallowParentMap+ :: [(Maybe Str, Word32le, Word32le, Word32le)] -> Map Word32le Word32le+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, Word32le, Word32le, Word32le)] -> Map Word32le [Word32le]+makeParentMap classCache =+ let+ shallowParentMap = makeShallowParentMap classCache+ in+ Map.mapWithKey+ (\classId _ -> getParentClasses shallowParentMap classId)+ shallowParentMap++getParentClasses :: Map Word32le Word32le -> Word32le -> [Word32le]+getParentClasses shallowParentMap classId =+ case Map.lookup classId shallowParentMap of+ Nothing -> []+ Just parentClassId ->+ parentClassId : getParentClasses shallowParentMap parentClassId++getParentClass+ :: Maybe Str+ -> Word32le+ -> [(Maybe Str, Word32le, Word32le, Word32le)]+ -> Maybe Word32le+getParentClass maybeClassName parentCacheId xs = case maybeClassName of+ Nothing -> getParentClassById parentCacheId xs+ Just className -> getParentClassByName className parentCacheId xs++getParentClassById+ :: Word32le -> [(Maybe Str, Word32le, Word32le, Word32le)] -> Maybe Word32le+getParentClassById parentCacheId xs =+ case dropWhile (\(_, _, cacheId, _) -> cacheId /= parentCacheId) xs of+ [] -> if parentCacheId == Word32le 0+ then Nothing+ else getParentClassById (Word32le (word32leValue parentCacheId - 1)) xs+ (_, parentClassId, _, _):_ -> Just parentClassId++getParentClassByName+ :: Str+ -> Word32le+ -> [(Maybe Str, Word32le, Word32le, Word32le)]+ -> Maybe Word32le+getParentClassByName className parentCacheId xs =+ case Map.lookup className 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, _, _, _) ->+ maybeClassName == Just parentClassName+ )+ xs+ )+ )+ )+ )++parentClasses :: Map Str Str+parentClasses =+ Map.map toStr (Map.mapKeys toStr (Map.fromList rawParentClasses))++makeObjectMap :: List Str -> Map Word32le Str+makeObjectMap objects =+ Map.fromAscList (zip (fmap Word32le [0 ..]) (listValue objects))++getObjectName :: Map Word32le Str -> Word32le -> Maybe Str+getObjectName objectMap objectId = Map.lookup objectId objectMap++getClassName :: Str -> Maybe Str+getClassName rawObjectName =+ Map.lookup (normalizeObjectName rawObjectName) objectClasses++normalizeObjectName :: Str -> Str+normalizeObjectName objectName =+ let+ name = strValue 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 crowdActor+ else if Text.isInfixOf crowdManager name+ then Str crowdManager+ else if Text.isInfixOf boostPickup name+ then Str boostPickup+ else if Text.isInfixOf mapScoreboard name+ then Str mapScoreboard+ else if Text.isInfixOf breakout name+ then Str breakout+ else objectName++objectClasses :: Map Str Str+objectClasses =+ Map.map toStr (Map.mapKeys toStr (Map.fromList rawObjectClasses))++classHasLocation :: Str -> Bool+classHasLocation className = Set.member className classesWithLocation++classesWithLocation :: Set.Set Str+classesWithLocation = Set.fromList (fmap toStr rawClassesWithLocation)++classHasRotation :: Str -> Bool+classHasRotation className = Set.member className classesWithRotation++classesWithRotation :: Set.Set Str+classesWithRotation = Set.fromList (fmap toStr rawClassesWithRotation)++getAttributeIdLimit :: Map Word32le Word32le -> Maybe Word+getAttributeIdLimit attributeMap = do+ ((streamId, _), _) <- Map.maxViewWithKey attributeMap+ pure (fromIntegral (word32leValue streamId))++getAttributeName+ :: ClassAttributeMap -> Map Word32le Word32le -> CompressedWord -> Maybe Str+getAttributeName classAttributeMap attributeMap streamId = do+ let key = Word32le (fromIntegral (compressedWordValue streamId))+ attributeId <- Map.lookup key attributeMap+ let objectMap = classAttributeMapObjectMap classAttributeMap+ Map.lookup attributeId objectMap++getAttributeMap+ :: ClassAttributeMap+ -> Map CompressedWord Word32le+ -> CompressedWord+ -> Maybe (Map Word32le Word32le)+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
@@ -0,0 +1,16 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.ClassMapping+ ( ClassMapping(..)+ ) where++import Rattletrap.Type.Common+import Rattletrap.Type.Str+import Rattletrap.Type.Word32le++data ClassMapping = ClassMapping+ { classMappingName :: Str+ , classMappingStreamId :: Word32le+ } deriving (Eq, Ord, Show)++$(deriveJson ''ClassMapping)
@@ -0,0 +1,17 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.ClubColorsAttribute+ ( ClubColorsAttribute(..)+ ) where++import Rattletrap.Type.Common+import Rattletrap.Type.Word8le++data ClubColorsAttribute = ClubColorsAttribute+ { clubColorsAttributeBlueFlag :: Bool+ , clubColorsAttributeBlueColor :: Word8le+ , clubColorsAttributeOrangeFlag :: Bool+ , clubColorsAttributeOrangeColor :: Word8le+ } deriving (Eq, Ord, Show)++$(deriveJson ''ClubColorsAttribute)
@@ -0,0 +1,55 @@+module Rattletrap.Type.Common+ ( Int.Int8+ , Int.Int32+ , Int.Int64+ , Map.Map+ , Text.Text+ , Word.Word8+ , Word.Word16+ , Word.Word32+ , Word.Word64+ , deriveJson+ ) where++import qualified Data.Aeson as Json+import qualified Data.Aeson.TH as Json+import qualified Data.Char as Char+import qualified Data.Int as Int+import qualified Data.Map as Map+import qualified Data.Maybe as Maybe+import qualified Data.Text as Text+import qualified Data.Word as Word+import qualified Language.Haskell.TH as TH++deriveJson :: TH.Name -> TH.Q [TH.Dec]+deriveJson name = Json.deriveJSON (jsonOptions (TH.nameBase name)) name++jsonOptions :: String -> Json.Options+jsonOptions prefix = Json.defaultOptions+ { Json.constructorTagModifier = toSnakeCase . partialDropPrefix prefix+ , Json.fieldLabelModifier = toSnakeCase+ . partialDropPrefix (lowerFirst prefix)+ , Json.omitNothingFields = True+ , Json.sumEncoding = Json.ObjectWithSingleField+ , Json.unwrapUnaryRecords = True+ }++lowerFirst :: String -> String+lowerFirst string = case string of+ "" -> string+ first:rest -> Char.toLower first : rest++toSnakeCase :: String -> String+toSnakeCase = Json.camelTo2 '_'++partialDropPrefix :: (Eq a, Show a) => [a] -> [a] -> [a]+partialDropPrefix prefix list = Maybe.fromMaybe+ (error (unwords [show prefix, "is not a prefix of", show list]))+ (dropPrefix prefix list)++dropPrefix :: Eq a => [a] -> [a] -> Maybe [a]+dropPrefix prefix list = case prefix of+ [] -> Just list+ ph:pt -> case list of+ [] -> Nothing+ lh:lt -> if ph == lh then dropPrefix pt lt else Nothing
@@ -0,0 +1,14 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.CompressedWord+ ( CompressedWord(..)+ ) where++import Rattletrap.Type.Common++data CompressedWord = CompressedWord+ { compressedWordLimit :: Word+ , compressedWordValue :: Word+ } deriving (Eq, Ord, Show)++$(deriveJson ''CompressedWord)
@@ -0,0 +1,16 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.CompressedWordVector+ ( CompressedWordVector(..)+ ) where++import Rattletrap.Type.Common+import Rattletrap.Type.CompressedWord++data CompressedWordVector = CompressedWordVector+ { compressedWordVectorX :: CompressedWord+ , compressedWordVectorY :: CompressedWord+ , compressedWordVectorZ :: CompressedWord+ } deriving (Eq, Ord, Show)++$(deriveJson ''CompressedWordVector)
@@ -0,0 +1,52 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.Content+ ( Content(..)+ ) where++import Rattletrap.Type.Cache+import Rattletrap.Type.ClassMapping+import Rattletrap.Type.Common+import Rattletrap.Type.Frame+import Rattletrap.Type.KeyFrame+import Rattletrap.Type.List+import Rattletrap.Type.Mark+import Rattletrap.Type.Message+import Rattletrap.Type.Str+import Rattletrap.Type.Word32le++-- | Contains low-level game data about a 'Rattletrap.Replay.Replay'.+data Content = Content+ { contentLevels :: List Str+ -- ^ 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 :: Word32le+ -- ^ 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 Str+ -- ^ A list of @.upk@ files to load, like+ -- @..\\..\\TAGame\\CookedPCConsole\\Stadium_P.upk@.+ , contentObjects :: List Str+ -- ^ Objects in the stream. Used for the+ -- 'Rattletrap.Type.ClassAttributeMap.ClassAttributeMap'.+ , contentNames :: List Str+ -- ^ 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.Type.ClassAttributeMap.ClassAttributeMap'.+ , contentCaches :: List Cache+ -- ^ A list of classes along with their parent classes and attributes. Used+ -- for the 'Rattletrap.Type.ClassAttributeMap.ClassAttributeMap'.+ } deriving (Eq, Ord, Show)++$(deriveJson ''Content)
@@ -0,0 +1,21 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.DamageStateAttribute+ ( DamageStateAttribute(..)+ ) where++import Rattletrap.Type.Common+import Rattletrap.Type.Int32le+import Rattletrap.Type.Vector+import Rattletrap.Type.Word8le++data DamageStateAttribute = DamageStateAttribute+ { damageStateAttributeUnknown1 :: Word8le+ , damageStateAttributeUnknown2 :: Bool+ , damageStateAttributeUnknown3 :: Int32le+ , damageStateAttributeUnknown4 :: Vector+ , damageStateAttributeUnknown5 :: Bool+ , damageStateAttributeUnknown6 :: Bool+ } deriving (Eq, Ord, Show)++$(deriveJson ''DamageStateAttribute)
@@ -0,0 +1,20 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.DemolishAttribute+ ( DemolishAttribute(..)+ ) where++import Rattletrap.Type.Common+import Rattletrap.Type.Vector+import Rattletrap.Type.Word32le++data DemolishAttribute = DemolishAttribute+ { demolishAttributeAttackerFlag :: Bool+ , demolishAttributeAttackerActorId :: Word32le+ , demolishAttributeVictimFlag :: Bool+ , demolishAttributeVictimActorId :: Word32le+ , demolishAttributeAttackerVelocity :: Vector+ , demolishAttributeVictimVelocity :: Vector+ } deriving (Eq, Ord, Show)++$(deriveJson ''DemolishAttribute)
@@ -0,0 +1,15 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.DestroyedReplication+ ( DestroyedReplication(..)+ ) where++import Rattletrap.Type.Common++-- | 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)++$(deriveJson ''DestroyedReplication)
@@ -0,0 +1,69 @@+module Rattletrap.Type.Dictionary+ ( Dictionary(..)+ , dictionaryLookup+ ) where++import Rattletrap.Type.Common+import Rattletrap.Type.Str++import qualified Control.Monad as Monad+import qualified Data.Aeson as Json+import qualified Data.Aeson.Types as Json+import qualified Data.Map as Map+import qualified Data.Text as Text++data Dictionary a+ = DictionaryElement Str a (Dictionary a)+ | DictionaryEnd Str+ deriving (Eq, Ord, Show)++instance Json.FromJSON a => Json.FromJSON (Dictionary a) where+ parseJSON =+ Json.withObject+ "Dictionary"+ (\o -> do+ keys <- get o "keys"+ lastKey <- get o "last_key"+ value <- get o "value"+ Monad.foldM+ (\d k ->+ case Map.lookup k value of+ Nothing -> fail (unwords ["missing key", show k])+ Just v -> pure (DictionaryElement (Str k) v d))+ (DictionaryEnd lastKey)+ (reverse keys))++instance Json.ToJSON a => Json.ToJSON (Dictionary a) where+ toJSON d =+ Json.object+ [ pair "keys" (dictionaryKeys d)+ , pair "last_key" (dictionaryLastKey d)+ , pair "value" (dictionaryValue d)+ ]++dictionaryKeys :: Dictionary a -> [Str]+dictionaryKeys = fmap fst . toList++dictionaryLastKey :: Dictionary a -> Str+dictionaryLastKey x = case x of+ DictionaryElement _ _ y -> dictionaryLastKey y+ DictionaryEnd y -> y++dictionaryLookup :: Str -> Dictionary a -> Maybe a+dictionaryLookup k x = case x of+ DictionaryElement j v y -> if k == j then Just v else dictionaryLookup k y+ DictionaryEnd _ -> Nothing++dictionaryValue :: Dictionary a -> Map Text a+dictionaryValue = Map.mapKeys strValue . Map.fromList . toList++get :: Json.FromJSON a => Json.Object -> String -> Json.Parser a+get o k = o Json..: Text.pack k++pair :: Json.ToJSON a => String -> a -> (Text, Json.Value)+pair k v = (Text.pack k, Json.toJSON v)++toList :: Dictionary a -> [(Str, a)]+toList x = case x of+ DictionaryElement k v y -> (k, v) : toList y+ DictionaryEnd _ -> []
@@ -0,0 +1,13 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.EnumAttribute+ ( EnumAttribute(..)+ ) where++import Rattletrap.Type.Common++newtype EnumAttribute = EnumAttribute+ { enumAttributeValue :: Word16+ } deriving (Eq, Ord, Show)++$(deriveJson ''EnumAttribute)
@@ -0,0 +1,17 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.ExplosionAttribute+ ( ExplosionAttribute(..)+ ) where++import Rattletrap.Type.Common+import Rattletrap.Type.Int32le+import Rattletrap.Type.Vector++data ExplosionAttribute = ExplosionAttribute+ { explosionAttributeFlag :: Bool+ , explosionAttributeActorId :: Int32le+ , explosionAttributeLocation :: Vector+ } deriving (Eq, Ord, Show)++$(deriveJson ''ExplosionAttribute)
@@ -0,0 +1,16 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.ExtendedExplosionAttribute+ ( ExtendedExplosionAttribute(..)+ ) where++import Rattletrap.Type.Common+import Rattletrap.Type.ExplosionAttribute+import Rattletrap.Type.FlaggedIntAttribute++data ExtendedExplosionAttribute = ExtendedExplosionAttribute+ { extendedExplosionAttributeExplosion :: ExplosionAttribute+ , extendedExplosionAttributeUnknown :: FlaggedIntAttribute+ } deriving (Eq, Ord, Show)++$(deriveJson ''ExtendedExplosionAttribute)
@@ -0,0 +1,15 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.FlaggedIntAttribute+ ( FlaggedIntAttribute(..)+ ) where++import Rattletrap.Type.Common+import Rattletrap.Type.Int32le++data FlaggedIntAttribute = FlaggedIntAttribute+ { flaggedIntAttributeFlag :: Bool+ , flaggedIntAttributeInt :: Int32le+ } deriving (Eq, Ord, Show)++$(deriveJson ''FlaggedIntAttribute)
@@ -0,0 +1,13 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.Float32le+ ( Float32le(..)+ ) where++import Rattletrap.Type.Common++newtype Float32le = Float32le+ { float32leValue :: Float+ } deriving (Eq, Ord, Show)++$(deriveJson ''Float32le)
@@ -0,0 +1,14 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.FloatAttribute+ ( FloatAttribute(..)+ ) where++import Rattletrap.Type.Common+import Rattletrap.Type.Float32le++newtype FloatAttribute = FloatAttribute+ { floatAttributeValue :: Float32le+ } deriving (Eq, Ord, Show)++$(deriveJson ''FloatAttribute)
@@ -0,0 +1,20 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.Frame+ ( Frame(..)+ ) where++import Rattletrap.Type.Common+import Rattletrap.Type.Float32le+import Rattletrap.Type.Replication++data Frame = Frame+ { frameTime :: Float32le+ -- ^ Time in seconds since the beginning of the match.+ , frameDelta :: Float32le+ -- ^ Time in seconds since the last frame. Usually about 0.03 since there+ -- are 30 frames per second.+ , frameReplications :: [Replication]+ } deriving (Eq, Ord, Show)++$(deriveJson ''Frame)
@@ -0,0 +1,14 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.GameModeAttribute+ ( GameModeAttribute(..)+ ) where++import Rattletrap.Type.Common++data GameModeAttribute = GameModeAttribute+ { gameModeAttributeNumBits :: Int+ , gameModeAttributeWord :: Word8+ } deriving (Eq, Ord, Show)++$(deriveJson ''GameModeAttribute)
@@ -0,0 +1,60 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.Header+ ( Header(..)+ ) where++import Rattletrap.Type.Common+import Rattletrap.Type.Dictionary+import Rattletrap.Type.Property+import Rattletrap.Type.Str+import Rattletrap.Type.Word32le++-- | Contains high-level metadata about a 'Rattletrap.Replay.Replay'.+data Header = Header+ { headerEngineVersion :: Word32le+ -- ^ The "major" ("engine") version number.+ , headerLicenseeVersion :: Word32le+ -- ^ The "minor" ("licensee") version number.+ , headerPatchVersion :: Maybe Word32le+ -- ^ The "patch" ("net") version number.+ , headerLabel :: Str+ -- ^ 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)++$(deriveJson ''Header)
@@ -0,0 +1,20 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.Initialization+ ( Initialization(..)+ ) where++import Rattletrap.Type.Common+import Rattletrap.Type.Int8Vector+import Rattletrap.Type.Vector++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)++$(deriveJson ''Initialization)
@@ -0,0 +1,13 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.Int32le+ ( Int32le(..)+ ) where++import Rattletrap.Type.Common++newtype Int32le = Int32le+ { int32leValue :: Int32+ } deriving (Eq, Ord, Show)++$(deriveJson ''Int32le)
@@ -0,0 +1,16 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.Int8Vector+ ( Int8Vector(..)+ ) where++import Rattletrap.Type.Common+import Rattletrap.Type.Int8le++data Int8Vector = Int8Vector+ { int8VectorX :: Maybe Int8le+ , int8VectorY :: Maybe Int8le+ , int8VectorZ :: Maybe Int8le+ } deriving (Eq, Ord, Show)++$(deriveJson ''Int8Vector)
@@ -0,0 +1,13 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.Int8le+ ( Int8le(..)+ ) where++import Rattletrap.Type.Common++newtype Int8le = Int8le+ { int8leValue :: Int8+ } deriving (Eq, Ord, Show)++$(deriveJson ''Int8le)
@@ -0,0 +1,14 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.IntAttribute+ ( IntAttribute(..)+ ) where++import Rattletrap.Type.Common+import Rattletrap.Type.Int32le++newtype IntAttribute = IntAttribute+ { intAttributeValue :: Int32le+ } deriving (Eq, Ord, Show)++$(deriveJson ''IntAttribute)
@@ -0,0 +1,20 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.KeyFrame+ ( KeyFrame(..)+ ) where++import Rattletrap.Type.Common+import Rattletrap.Type.Float32le+import Rattletrap.Type.Word32le++data KeyFrame = KeyFrame+ { keyFrameTime :: Float32le+ -- ^ When this key frame occurs, in seconds.+ , keyFrameFrame :: Word32le+ -- ^ The frame number of this key frame, starting from 0.+ , keyFramePosition :: Word32le+ -- ^ The bit position of this key frame in the stream.+ } deriving (Eq, Ord, Show)++$(deriveJson ''KeyFrame)
@@ -0,0 +1,13 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.List+ ( List(..)+ ) where++import Rattletrap.Type.Common++newtype List a = List+ { listValue :: [a]+ } deriving (Eq, Ord, Show)++$(deriveJson ''List)
@@ -0,0 +1,28 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.LoadoutAttribute+ ( LoadoutAttribute(..)+ ) where++import Rattletrap.Type.Common+import Rattletrap.Type.Word32le+import Rattletrap.Type.Word8le++data LoadoutAttribute = LoadoutAttribute+ { loadoutAttributeVersion :: Word8le+ , loadoutAttributeBody :: Word32le+ , loadoutAttributeDecal :: Word32le+ , loadoutAttributeWheels :: Word32le+ , loadoutAttributeRocketTrail :: Word32le+ -- ^ Now known as "rocket boost".+ , loadoutAttributeAntenna :: Word32le+ , loadoutAttributeTopper :: Word32le+ , loadoutAttributeUnknown1 :: Word32le+ , loadoutAttributeUnknown2 :: Maybe Word32le+ , loadoutAttributeEngineAudio :: Maybe Word32le+ , loadoutAttributeTrail :: Maybe Word32le+ , loadoutAttributeGoalExplosion :: Maybe Word32le+ , loadoutAttributeBanner :: Maybe Word32le+ } deriving (Eq, Ord, Show)++$(deriveJson ''LoadoutAttribute)
@@ -0,0 +1,14 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.LoadoutOnlineAttribute+ ( LoadoutOnlineAttribute(..)+ ) where++import Rattletrap.Type.Common+import Rattletrap.Type.ProductAttribute++newtype LoadoutOnlineAttribute = LoadoutOnlineAttribute+ { loadoutAttributeValue :: [[ProductAttribute]]+ } deriving (Eq, Ord, Show)++$(deriveJson ''LoadoutOnlineAttribute)
@@ -0,0 +1,15 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.LoadoutsAttribute+ ( LoadoutsAttribute(..)+ ) where++import Rattletrap.Type.Common+import Rattletrap.Type.LoadoutAttribute++data LoadoutsAttribute = LoadoutsAttribute+ { loadoutsAttributeBlue :: LoadoutAttribute+ , loadoutsAttributeOrange :: LoadoutAttribute+ } deriving (Eq, Ord, Show)++$(deriveJson ''LoadoutsAttribute)
@@ -0,0 +1,17 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.LoadoutsOnlineAttribute+ ( LoadoutsOnlineAttribute(..)+ ) where++import Rattletrap.Type.Common+import Rattletrap.Type.LoadoutOnlineAttribute++data LoadoutsOnlineAttribute = LoadoutsOnlineAttribute+ { loadoutsOnlineAttributeBlue :: LoadoutOnlineAttribute+ , loadoutsOnlineAttributeOrange :: LoadoutOnlineAttribute+ , loadoutsOnlineAttributeUnknown1 :: Bool+ , loadoutsOnlineAttributeUnknown2 :: Bool+ } deriving (Eq, Ord, Show)++$(deriveJson ''LoadoutsOnlineAttribute)
@@ -0,0 +1,14 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.LocationAttribute+ ( LocationAttribute(..)+ ) where++import Rattletrap.Type.Common+import Rattletrap.Type.Vector++newtype LocationAttribute = LocationAttribute+ { locationAttributeValue :: Vector+ } deriving (Eq, Ord, Show)++$(deriveJson ''LocationAttribute)
@@ -0,0 +1,18 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.Mark+ ( Mark(..)+ ) where++import Rattletrap.Type.Common+import Rattletrap.Type.Str+import Rattletrap.Type.Word32le++data Mark = Mark+ { markValue :: Str+ -- ^ Which type of mark this is, like @Team0Goal@.+ , markFrame :: Word32le+ -- ^ Which frame this mark belongs to, starting from 0.+ } deriving (Eq, Ord, Show)++$(deriveJson ''Mark)
@@ -0,0 +1,20 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.Message+ ( Message(..)+ ) where++import Rattletrap.Type.Common+import Rattletrap.Type.Str+import Rattletrap.Type.Word32le++data Message = Message+ { messageFrame :: Word32le+ -- ^ Which frame this message belongs to, starting from 0.+ , messageName :: Str+ -- ^ The primary player's name.+ , messageValue :: Str+ -- ^ The content of the message.+ } deriving (Eq, Ord, Show)++$(deriveJson ''Message)
@@ -0,0 +1,17 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.MusicStingerAttribute+ ( MusicStingerAttribute(..)+ ) where++import Rattletrap.Type.Common+import Rattletrap.Type.Word32le+import Rattletrap.Type.Word8le++data MusicStingerAttribute = MusicStingerAttribute+ { musicStingerAttributeFlag :: Bool+ , musicStingerAttributeCue :: Word32le+ , musicStingerAttributeTrigger :: Word8le+ } deriving (Eq, Ord, Show)++$(deriveJson ''MusicStingerAttribute)
@@ -0,0 +1,16 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.PartyLeaderAttribute+ ( PartyLeaderAttribute(..)+ ) where++import Rattletrap.Type.Common+import Rattletrap.Type.RemoteId+import Rattletrap.Type.Word8le++data PartyLeaderAttribute = PartyLeaderAttribute+ { partyLeaderAttributeSystemId :: Word8le+ , partyLeaderAttributeId :: Maybe (RemoteId, Word8le)+ } deriving (Eq, Ord, Show)++$(deriveJson ''PartyLeaderAttribute)
@@ -0,0 +1,15 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.PickupAttribute+ ( PickupAttribute(..)+ ) where++import Rattletrap.Type.Common+import Rattletrap.Type.Word32le++data PickupAttribute = PickupAttribute+ { pickupAttributeInstigatorId :: Maybe Word32le+ , pickupAttributePickedUp :: Bool+ } deriving (Eq, Ord, Show)++$(deriveJson ''PickupAttribute)
@@ -0,0 +1,14 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.PlayerHistoryKeyAttribute+ ( PlayerHistoryKeyAttribute(..)+ ) where++import Rattletrap.Type.Bitstream+import Rattletrap.Type.Common++newtype PlayerHistoryKeyAttribute = PlayerHistoryKeyAttribute+ { playerHistoryKeyAttributeUnknown :: Bitstream+ } deriving (Eq, Ord, Show)++$(deriveJson ''PlayerHistoryKeyAttribute)
@@ -0,0 +1,20 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.PrivateMatchSettingsAttribute+ ( PrivateMatchSettingsAttribute(..)+ ) where++import Rattletrap.Type.Common+import Rattletrap.Type.Str+import Rattletrap.Type.Word32le++data PrivateMatchSettingsAttribute = PrivateMatchSettingsAttribute+ { privateMatchSettingsAttributeMutators :: Str+ , privateMatchSettingsAttributeJoinableBy :: Word32le+ , privateMatchSettingsAttributeMaxPlayers :: Word32le+ , privateMatchSettingsAttributeGameName :: Str+ , privateMatchSettingsAttributePassword :: Str+ , privateMatchSettingsAttributeFlag :: Bool+ } deriving (Eq, Ord, Show)++$(deriveJson ''PrivateMatchSettingsAttribute)
@@ -0,0 +1,20 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.ProductAttribute+ ( ProductAttribute(..)+ ) where++import Rattletrap.Type.Common+import Rattletrap.Type.CompressedWord+import Rattletrap.Type.Str+import Rattletrap.Type.Word32le++data ProductAttribute = ProductAttribute+ { productAttributeUnknown :: Bool+ , productAttributeObjectId :: Word32le+ , productAttributeObjectName :: Maybe Str+ -- ^ read-only+ , productAttributeValue :: Maybe (Either CompressedWord Word32)+ } deriving (Eq, Ord, Show)++$(deriveJson ''ProductAttribute)
@@ -0,0 +1,19 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.Property+ ( Property(..)+ ) where++import Rattletrap.Type.Common+import Rattletrap.Type.PropertyValue+import Rattletrap.Type.Str+import Rattletrap.Type.Word64le++data Property = Property+ { propertyKind :: Str+ , propertySize :: Word64le+ -- ^ Not used.+ , propertyValue :: PropertyValue Property+ } deriving (Eq, Ord, Show)++$(deriveJson ''Property)
@@ -0,0 +1,31 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.PropertyValue+ ( PropertyValue(..)+ ) where++import Rattletrap.Type.Common+import Rattletrap.Type.Dictionary+import Rattletrap.Type.Float32le+import Rattletrap.Type.Int32le+import Rattletrap.Type.List+import Rattletrap.Type.Str+import Rattletrap.Type.Word64le+import Rattletrap.Type.Word8le++data PropertyValue a+ = PropertyValueArray (List (Dictionary a))+ -- ^ Yes, a list of dictionaries. No, it doesn't make sense. These usually+ -- only have one element.+ | PropertyValueBool Word8le+ | PropertyValueByte Str (Maybe Str)+ -- ^ This is a strange name for essentially a key-value pair.+ | PropertyValueFloat Float32le+ | PropertyValueInt Int32le+ | PropertyValueName Str+ -- ^ It's unclear how exactly this is different than a 'StrProperty'.+ | PropertyValueQWord Word64le+ | PropertyValueStr Str+ deriving (Eq, Ord, Show)++$(deriveJson ''PropertyValue)
@@ -0,0 +1,14 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.QWordAttribute+ ( QWordAttribute(..)+ ) where++import Rattletrap.Type.Common+import Rattletrap.Type.Word64le++newtype QWordAttribute = QWordAttribute+ { qWordAttributeValue :: Word64le+ } deriving (Eq, Ord, Show)++$(deriveJson ''QWordAttribute)
@@ -0,0 +1,19 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.RemoteId+ ( RemoteId(..)+ ) where++import Rattletrap.Type.Bitstream+import Rattletrap.Type.Common+import Rattletrap.Type.Word64le++data RemoteId+ = RemoteIdPlayStation Text [Word8]+ | RemoteIdSplitscreen Word32+ | RemoteIdSteam Word64le+ | RemoteIdSwitch Bitstream+ | RemoteIdXbox Word64le+ deriving (Eq, Ord, Show)++$(deriveJson ''RemoteId)
@@ -0,0 +1,20 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.Replay+ ( Replay(..)+ ) where++import Rattletrap.Type.Common+import Rattletrap.Type.Content+import Rattletrap.Type.Header+import Rattletrap.Type.Section++-- | 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)++$(deriveJson ''Replay)
@@ -0,0 +1,16 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.Replication+ ( Replication(..)+ ) where++import Rattletrap.Type.Common+import Rattletrap.Type.CompressedWord+import Rattletrap.Type.ReplicationValue++data Replication = Replication+ { replicationActorId :: CompressedWord+ , replicationValue :: ReplicationValue+ } deriving (Eq, Ord, Show)++$(deriveJson ''Replication)
@@ -0,0 +1,21 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.ReplicationValue+ ( ReplicationValue(..)+ ) where++import Rattletrap.Type.Common+import Rattletrap.Type.DestroyedReplication+import Rattletrap.Type.SpawnedReplication+import Rattletrap.Type.UpdatedReplication++data ReplicationValue+ = ReplicationValueSpawned SpawnedReplication+ -- ^ Creates a new actor.+ | ReplicationValueUpdated UpdatedReplication+ -- ^ Updates an existing actor.+ | ReplicationValueDestroyed DestroyedReplication+ -- ^ Destroys an existing actor.+ deriving (Eq, Ord, Show)++$(deriveJson ''ReplicationValue)
@@ -0,0 +1,21 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.ReservationAttribute+ ( ReservationAttribute(..)+ ) where++import Rattletrap.Type.Common+import Rattletrap.Type.CompressedWord+import Rattletrap.Type.Str+import Rattletrap.Type.UniqueIdAttribute++data ReservationAttribute = ReservationAttribute+ { reservationAttributeNumber :: CompressedWord+ , reservationAttributeUniqueId :: UniqueIdAttribute+ , reservationAttributeName :: Maybe Str+ , reservationAttributeUnknown1 :: Bool+ , reservationAttributeUnknown2 :: Bool+ , reservationAttributeUnknown3 :: Maybe Word8+ } deriving (Eq, Ord, Show)++$(deriveJson ''ReservationAttribute)
@@ -0,0 +1,19 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.RigidBodyStateAttribute+ ( RigidBodyStateAttribute(..)+ ) where++import Rattletrap.Type.Common+import Rattletrap.Type.CompressedWordVector+import Rattletrap.Type.Vector++data RigidBodyStateAttribute = RigidBodyStateAttribute+ { rigidBodyStateAttributeSleeping :: Bool+ , rigidBodyStateAttributeLocation :: Vector+ , rigidBodyStateAttributeRotation :: CompressedWordVector+ , rigidBodyStateAttributeLinearVelocity :: Maybe Vector+ , rigidBodyStateAttributeAngularVelocity :: Maybe Vector+ } deriving (Eq, Ord, Show)++$(deriveJson ''RigidBodyStateAttribute)
@@ -0,0 +1,23 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.Section+ ( Section(..)+ ) where++import Rattletrap.Type.Common+import Rattletrap.Type.Word32le++-- | 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+ { sectionSize :: Word32le+ -- ^ read only+ , sectionCrc :: Word32le+ -- ^ read only+ , sectionBody :: a+ -- ^ The actual content in the section.+ } deriving (Eq, Ord, Show)++$(deriveJson ''Section)
@@ -0,0 +1,29 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.SpawnedReplication+ ( SpawnedReplication(..)+ ) where++import Rattletrap.Type.Common+import Rattletrap.Type.Initialization+import Rattletrap.Type.Str+import Rattletrap.Type.Word32le++data SpawnedReplication = SpawnedReplication+ { spawnedReplicationFlag :: Bool+ -- ^ Unclear what this is.+ , spawnedReplicationNameIndex :: Maybe Word32le+ , spawnedReplicationName :: Maybe Str+ -- ^ Read-only! Changing a replication's name requires editing the+ -- 'spawnedReplicationNameIndex' and maybe the class attribute map.+ , spawnedReplicationObjectId :: Word32le+ , spawnedReplicationObjectName :: Str+ -- ^ Read-only! Changing a replication's object requires editing the class+ -- attribute map.+ , spawnedReplicationClassName :: Str+ -- ^ Read-only! Changing a replication's class requires editing the class+ -- attribute map.+ , spawnedReplicationInitialization :: Initialization+ } deriving (Eq, Ord, Show)++$(deriveJson ''SpawnedReplication)
@@ -0,0 +1,23 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.Str+ ( Str(..)+ , toStr+ , fromStr+ ) where++import Rattletrap.Type.Common++import qualified Data.Text as Text++newtype Str = Str+ { strValue :: Text+ } deriving (Eq, Ord, Show)++$(deriveJson ''Str)++toStr :: String -> Str+toStr string = Str (Text.pack string)++fromStr :: Str -> String+fromStr text = Text.unpack (strValue text)
@@ -0,0 +1,14 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.StringAttribute+ ( StringAttribute(..)+ ) where++import Rattletrap.Type.Common+import Rattletrap.Type.Str++newtype StringAttribute = StringAttribute+ { stringAttributeValue :: Str+ } deriving (Eq, Ord, Show)++$(deriveJson ''StringAttribute)
@@ -0,0 +1,19 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.TeamPaintAttribute+ ( TeamPaintAttribute(..)+ ) where++import Rattletrap.Type.Common+import Rattletrap.Type.Word32le+import Rattletrap.Type.Word8le++data TeamPaintAttribute = TeamPaintAttribute+ { teamPaintAttributeTeam :: Word8le+ , teamPaintAttributePrimaryColor :: Word8le+ , teamPaintAttributeAccentColor :: Word8le+ , teamPaintAttributePrimaryFinish :: Word32le+ , teamPaintAttributeAccentFinish :: Word32le+ } deriving (Eq, Ord, Show)++$(deriveJson ''TeamPaintAttribute)
@@ -0,0 +1,17 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.UniqueIdAttribute+ ( UniqueIdAttribute(..)+ ) where++import Rattletrap.Type.Common+import Rattletrap.Type.RemoteId+import Rattletrap.Type.Word8le++data UniqueIdAttribute = UniqueIdAttribute+ { uniqueIdAttributeSystemId :: Word8le+ , uniqueIdAttributeRemoteId :: RemoteId+ , uniqueIdAttributeLocalId :: Word8le+ } deriving (Eq, Ord, Show)++$(deriveJson ''UniqueIdAttribute)
@@ -0,0 +1,14 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.UpdatedReplication+ ( UpdatedReplication(..)+ ) where++import Rattletrap.Type.Attribute+import Rattletrap.Type.Common++newtype UpdatedReplication = UpdatedReplication+ { updatedReplicationAttributes :: [Attribute]+ } deriving (Eq, Ord, Show)++$(deriveJson ''UpdatedReplication)
@@ -0,0 +1,16 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.Vector+ ( Vector(..)+ ) where++import Rattletrap.Type.Common++data Vector = Vector+ { vectorBias :: Word+ , vectorX :: Int+ , vectorY :: Int+ , vectorZ :: Int+ } deriving (Eq, Ord, Show)++$(deriveJson ''Vector)
@@ -0,0 +1,21 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.WeldedInfoAttribute+ ( WeldedInfoAttribute(..)+ ) where++import Rattletrap.Type.Common+import Rattletrap.Type.Float32le+import Rattletrap.Type.Int32le+import Rattletrap.Type.Int8Vector+import Rattletrap.Type.Vector++data WeldedInfoAttribute = WeldedInfoAttribute+ { weldedInfoAttributeActive :: Bool+ , weldedInfoAttributeActorId :: Int32le+ , weldedInfoAttributeOffset :: Vector+ , weldedInfoAttributeMass :: Float32le+ , weldedInfoAttributeRotation :: Int8Vector+ } deriving (Eq, Ord, Show)++$(deriveJson ''WeldedInfoAttribute)
@@ -0,0 +1,13 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.Word32le+ ( Word32le(..)+ ) where++import Rattletrap.Type.Common++newtype Word32le = Word32le+ { word32leValue :: Word32+ } deriving (Eq, Ord, Show)++$(deriveJson ''Word32le)
@@ -0,0 +1,13 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.Word64le+ ( Word64le(..)+ ) where++import Rattletrap.Type.Common++newtype Word64le = Word64le+ { word64leValue :: Word64+ } deriving (Eq, Ord, Show)++$(deriveJson ''Word64le)
@@ -0,0 +1,13 @@+{-# LANGUAGE TemplateHaskell #-}++module Rattletrap.Type.Word8le+ ( Word8le(..)+ ) where++import Rattletrap.Type.Common++newtype Word8le = Word8le+ { word8leValue :: Word8+ } deriving (Eq, Ord, Show)++$(deriveJson ''Word8le)
@@ -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
@@ -0,0 +1,30 @@+module Rattletrap.Utility.Bytes where++import qualified Data.Bits as Bits+import qualified Data.ByteString.Lazy as LazyBytes+import qualified Data.ByteString.Lazy.Char8 as LazyBytes8+import qualified Data.Text as Text+import qualified Data.Word as Word++encodeLatin1 :: Text.Text -> LazyBytes.ByteString+encodeLatin1 text = LazyBytes8.pack (Text.unpack text)++padBytes :: Integral a => a -> LazyBytes.ByteString -> LazyBytes.ByteString+padBytes size bytes = LazyBytes.concat+ [ bytes+ , LazyBytes.replicate (fromIntegral size - LazyBytes.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 :: LazyBytes.ByteString -> LazyBytes.ByteString+reverseBytes = LazyBytes.map reverseByte
@@ -0,0 +1,46 @@+module Rattletrap.Utility.Crc+ ( getCrc32+ ) where++import Rattletrap.Data++import qualified Data.Bits as Bits+import qualified Data.ByteString.Lazy as LazyBytes+import qualified Data.IntMap as IntMap+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 :: LazyBytes.ByteString -> Word.Word32+getCrc32 bytes = do+ let+ update = crc32Update crc32Table+ initial = Bits.complement crc32Initial+ crc = LazyBytes.foldl update initial bytes+ Bits.complement crc++crc32Update+ :: IntMap.IntMap Word.Word32 -> Word.Word32 -> Word.Word8 -> Word.Word32+crc32Update table crc byte = do+ let+ toWord8 :: (Integral a) => a -> Word.Word8+ toWord8 = fromIntegral+ toInt :: (Integral a) => a -> Int+ toInt = fromIntegral+ index = toInt (Bits.xor byte (toWord8 (Bits.shiftR crc 24)))+ left = table IntMap.! index+ right = Bits.shiftL crc 8+ Bits.xor left right++crc32Initial :: Word.Word32+crc32Initial = 0xefcbf201++crc32Table :: IntMap.IntMap Word.Word32+crc32Table = IntMap.fromDistinctAscList (zip [0 ..] rawCrc32Table)
@@ -0,0 +1,38 @@+-- | This module provides helper functions for converting replays to and from+-- both their binary format and JSON.+module Rattletrap.Utility.Helper+ ( decodeReplayFile+ , encodeReplayJson+ , decodeReplayJson+ , encodeReplayFile+ ) where++import Rattletrap.Decode.Common+import Rattletrap.Decode.Replay+import Rattletrap.Encode.Replay+import Rattletrap.Type.Replay++import qualified Data.Aeson as Json+import qualified Data.Aeson.Encode.Pretty as Json+import qualified Data.Binary.Put as Binary+import qualified Data.ByteString.Lazy as LazyBytes++-- | Parses a raw replay.+decodeReplayFile :: LazyBytes.ByteString -> Either String Replay+decodeReplayFile = runDecode decodeReplay++-- | Encodes a replay as JSON.+encodeReplayJson :: Replay -> LazyBytes.ByteString+encodeReplayJson = Json.encodePretty' Json.defConfig+ { Json.confCompare = compare+ , Json.confIndent = Json.Spaces 2+ , Json.confTrailingNewline = True+ }++-- | Parses a JSON replay.+decodeReplayJson :: LazyBytes.ByteString -> Either String Replay+decodeReplayJson = Json.eitherDecode++-- | Encodes a raw replay.+encodeReplayFile :: Replay -> LazyBytes.ByteString+encodeReplayFile replay = Binary.runPut (putReplay replay)
@@ -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,11 +1,10 @@ name: rattletrap-version: 3.1.2+version: 4.0.0 category: Game description: Rattletrap parses and generates Rocket League replays. extra-source-files: - CHANGELOG.markdown- - replays/*.replay - package.yaml - README.markdown - stack.yaml@@ -16,48 +15,45 @@ 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+ aeson-pretty: == 0.8.5.*+ aeson: == 1.2.3.*+ base: == 4.10.1.*+ binary-bits: == 0.5.*+ binary: == 0.8.5.*+ bytestring: == 0.10.8.*+ containers: == 0.5.10.*+ filepath: == 1.4.1.*+ http-client-tls: == 0.3.5.*+ http-client: == 0.5.7.*+ template-haskell: == 2.12.*+ text: == 1.2.2.*+ transformers: == 0.5.2.*+ghc-options:+ - -Weverything+ - -Wno-implicit-prelude+ - -Wno-missing-import-lists+ - -Wno-safe+ - -Wno-unsafe 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+executable:+ dependencies: rattletrap+ ghc-options:+ - -rtsopts+ - -threaded+ 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+ HUnit: == 1.6.*+ rattletrap:+ temporary: == 1.2.1.* ghc-options: - -rtsopts - -threaded- - -with-rtsopts=-N main: Main.hs source-dirs: tests
@@ -2,10 +2,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: ffc648678c84d8942a93a5168812a9a02db2b7471f0f42a0576f8409235cc6cd+-- hash: db273356d10179b50de43a97e4f71076671b4d2a32bd6d23c8ee9b1cd4e20af4 name: rattletrap-version: 3.1.2+version: 4.0.0 synopsis: Parse and generate Rocket League replays. description: Rattletrap parses and generates Rocket League replays. category: Game@@ -21,68 +21,6 @@ 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 source-repository head@@ -92,101 +30,236 @@ library hs-source-dirs: library- default-extensions: Strict- ghc-options: -Wall+ ghc-options: -Weverything -Wno-implicit-prelude -Wno-missing-import-lists -Wno-safe -Wno-unsafe 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 ==1.2.3.*+ , aeson-pretty ==0.8.5.*+ , base ==4.10.1.*+ , binary ==0.8.5.*+ , binary-bits ==0.5.*+ , bytestring ==0.10.8.*+ , containers ==0.5.10.*+ , filepath ==1.4.1.*+ , http-client ==0.5.7.*+ , http-client-tls ==0.3.5.*+ , template-haskell ==2.12.*+ , text ==1.2.2.*+ , transformers ==0.5.2.* 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.Console.Main 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+ Rattletrap.Decode.AppliedDamageAttribute+ Rattletrap.Decode.Attribute+ Rattletrap.Decode.AttributeMapping+ Rattletrap.Decode.AttributeValue+ Rattletrap.Decode.Bitstream+ Rattletrap.Decode.BooleanAttribute+ Rattletrap.Decode.ByteAttribute+ Rattletrap.Decode.Cache+ Rattletrap.Decode.CamSettingsAttribute+ Rattletrap.Decode.ClassMapping+ Rattletrap.Decode.ClubColorsAttribute+ Rattletrap.Decode.Common+ Rattletrap.Decode.CompressedWord+ Rattletrap.Decode.CompressedWordVector+ Rattletrap.Decode.Content+ Rattletrap.Decode.DamageStateAttribute+ Rattletrap.Decode.DemolishAttribute+ Rattletrap.Decode.DestroyedReplication+ Rattletrap.Decode.Dictionary+ Rattletrap.Decode.EnumAttribute+ Rattletrap.Decode.ExplosionAttribute+ Rattletrap.Decode.ExtendedExplosionAttribute+ Rattletrap.Decode.FlaggedIntAttribute+ Rattletrap.Decode.Float32le+ Rattletrap.Decode.FloatAttribute+ Rattletrap.Decode.Frame+ Rattletrap.Decode.GameModeAttribute+ Rattletrap.Decode.Header+ Rattletrap.Decode.Initialization+ Rattletrap.Decode.Int32le+ Rattletrap.Decode.Int8le+ Rattletrap.Decode.Int8Vector+ Rattletrap.Decode.IntAttribute+ Rattletrap.Decode.KeyFrame+ Rattletrap.Decode.List+ Rattletrap.Decode.LoadoutAttribute+ Rattletrap.Decode.LoadoutOnlineAttribute+ Rattletrap.Decode.LoadoutsAttribute+ Rattletrap.Decode.LoadoutsOnlineAttribute+ Rattletrap.Decode.LocationAttribute+ Rattletrap.Decode.Mark+ Rattletrap.Decode.Message+ Rattletrap.Decode.MusicStingerAttribute+ Rattletrap.Decode.PartyLeaderAttribute+ Rattletrap.Decode.PickupAttribute+ Rattletrap.Decode.PlayerHistoryKeyAttribute+ Rattletrap.Decode.PrivateMatchSettingsAttribute+ Rattletrap.Decode.ProductAttribute+ Rattletrap.Decode.Property+ Rattletrap.Decode.PropertyValue+ Rattletrap.Decode.QWordAttribute+ Rattletrap.Decode.RemoteId+ Rattletrap.Decode.Replay+ Rattletrap.Decode.Replication+ Rattletrap.Decode.ReplicationValue+ Rattletrap.Decode.ReservationAttribute+ Rattletrap.Decode.RigidBodyStateAttribute+ Rattletrap.Decode.Section+ Rattletrap.Decode.SpawnedReplication+ Rattletrap.Decode.Str+ Rattletrap.Decode.StringAttribute+ Rattletrap.Decode.TeamPaintAttribute+ Rattletrap.Decode.UniqueIdAttribute+ Rattletrap.Decode.UpdatedReplication+ Rattletrap.Decode.Vector+ Rattletrap.Decode.WeldedInfoAttribute+ Rattletrap.Decode.Word32le+ Rattletrap.Decode.Word64le+ Rattletrap.Decode.Word8le+ Rattletrap.Encode.AppliedDamageAttribute+ Rattletrap.Encode.Attribute+ Rattletrap.Encode.AttributeMapping+ Rattletrap.Encode.AttributeValue+ Rattletrap.Encode.Bitstream+ Rattletrap.Encode.BooleanAttribute+ Rattletrap.Encode.ByteAttribute+ Rattletrap.Encode.Cache+ Rattletrap.Encode.CamSettingsAttribute+ Rattletrap.Encode.ClassMapping+ Rattletrap.Encode.ClubColorsAttribute+ Rattletrap.Encode.CompressedWord+ Rattletrap.Encode.CompressedWordVector+ Rattletrap.Encode.Content+ Rattletrap.Encode.DamageStateAttribute+ Rattletrap.Encode.DemolishAttribute+ Rattletrap.Encode.DestroyedReplication+ Rattletrap.Encode.Dictionary+ Rattletrap.Encode.EnumAttribute+ Rattletrap.Encode.ExplosionAttribute+ Rattletrap.Encode.ExtendedExplosionAttribute+ Rattletrap.Encode.FlaggedIntAttribute+ Rattletrap.Encode.Float32le+ Rattletrap.Encode.FloatAttribute+ Rattletrap.Encode.Frame+ Rattletrap.Encode.GameModeAttribute+ Rattletrap.Encode.Header+ Rattletrap.Encode.Initialization+ Rattletrap.Encode.Int32le+ Rattletrap.Encode.Int8le+ Rattletrap.Encode.Int8Vector+ Rattletrap.Encode.IntAttribute+ Rattletrap.Encode.KeyFrame+ Rattletrap.Encode.List+ Rattletrap.Encode.LoadoutAttribute+ Rattletrap.Encode.LoadoutOnlineAttribute+ Rattletrap.Encode.LoadoutsAttribute+ Rattletrap.Encode.LoadoutsOnlineAttribute+ Rattletrap.Encode.LocationAttribute+ Rattletrap.Encode.Mark+ Rattletrap.Encode.Message+ Rattletrap.Encode.MusicStingerAttribute+ Rattletrap.Encode.PartyLeaderAttribute+ Rattletrap.Encode.PickupAttribute+ Rattletrap.Encode.PlayerHistoryKeyAttribute+ Rattletrap.Encode.PrivateMatchSettingsAttribute+ Rattletrap.Encode.ProductAttribute+ Rattletrap.Encode.Property+ Rattletrap.Encode.PropertyValue+ Rattletrap.Encode.QWordAttribute+ Rattletrap.Encode.RemoteId+ Rattletrap.Encode.Replay+ Rattletrap.Encode.Replication+ Rattletrap.Encode.ReplicationValue+ Rattletrap.Encode.ReservationAttribute+ Rattletrap.Encode.RigidBodyStateAttribute+ Rattletrap.Encode.Section+ Rattletrap.Encode.SpawnedReplication+ Rattletrap.Encode.Str+ Rattletrap.Encode.StringAttribute+ Rattletrap.Encode.TeamPaintAttribute+ Rattletrap.Encode.UniqueIdAttribute+ Rattletrap.Encode.UpdatedReplication+ Rattletrap.Encode.Vector+ Rattletrap.Encode.WeldedInfoAttribute+ Rattletrap.Encode.Word32le+ Rattletrap.Encode.Word64le+ Rattletrap.Encode.Word8le+ Rattletrap.Type.AppliedDamageAttribute+ Rattletrap.Type.Attribute+ Rattletrap.Type.AttributeMapping+ Rattletrap.Type.AttributeType+ Rattletrap.Type.AttributeValue+ Rattletrap.Type.Bitstream+ Rattletrap.Type.BooleanAttribute+ Rattletrap.Type.ByteAttribute+ Rattletrap.Type.Cache+ Rattletrap.Type.CamSettingsAttribute+ Rattletrap.Type.ClassAttributeMap+ Rattletrap.Type.ClassMapping+ Rattletrap.Type.ClubColorsAttribute+ Rattletrap.Type.Common+ Rattletrap.Type.CompressedWord+ Rattletrap.Type.CompressedWordVector+ Rattletrap.Type.Content+ Rattletrap.Type.DamageStateAttribute+ Rattletrap.Type.DemolishAttribute+ Rattletrap.Type.DestroyedReplication+ Rattletrap.Type.Dictionary+ Rattletrap.Type.EnumAttribute+ Rattletrap.Type.ExplosionAttribute+ Rattletrap.Type.ExtendedExplosionAttribute+ Rattletrap.Type.FlaggedIntAttribute+ Rattletrap.Type.Float32le+ Rattletrap.Type.FloatAttribute+ Rattletrap.Type.Frame+ Rattletrap.Type.GameModeAttribute+ Rattletrap.Type.Header+ Rattletrap.Type.Initialization+ Rattletrap.Type.Int32le+ Rattletrap.Type.Int8le+ Rattletrap.Type.Int8Vector+ Rattletrap.Type.IntAttribute+ Rattletrap.Type.KeyFrame+ Rattletrap.Type.List+ Rattletrap.Type.LoadoutAttribute+ Rattletrap.Type.LoadoutOnlineAttribute+ Rattletrap.Type.LoadoutsAttribute+ Rattletrap.Type.LoadoutsOnlineAttribute+ Rattletrap.Type.LocationAttribute+ Rattletrap.Type.Mark+ Rattletrap.Type.Message+ Rattletrap.Type.MusicStingerAttribute+ Rattletrap.Type.PartyLeaderAttribute+ Rattletrap.Type.PickupAttribute+ Rattletrap.Type.PlayerHistoryKeyAttribute+ Rattletrap.Type.PrivateMatchSettingsAttribute+ Rattletrap.Type.ProductAttribute+ Rattletrap.Type.Property+ Rattletrap.Type.PropertyValue+ Rattletrap.Type.QWordAttribute+ Rattletrap.Type.RemoteId+ Rattletrap.Type.Replay+ Rattletrap.Type.Replication+ Rattletrap.Type.ReplicationValue+ Rattletrap.Type.ReservationAttribute+ Rattletrap.Type.RigidBodyStateAttribute+ Rattletrap.Type.Section+ Rattletrap.Type.SpawnedReplication+ Rattletrap.Type.Str+ Rattletrap.Type.StringAttribute+ Rattletrap.Type.TeamPaintAttribute+ Rattletrap.Type.UniqueIdAttribute+ Rattletrap.Type.UpdatedReplication+ Rattletrap.Type.Vector+ Rattletrap.Type.WeldedInfoAttribute+ Rattletrap.Type.Word32le+ Rattletrap.Type.Word64le+ Rattletrap.Type.Word8le+ Rattletrap.Utility.Bytes+ Rattletrap.Utility.Crc+ Rattletrap.Utility.Helper other-modules: Paths_rattletrap default-language: Haskell2010@@ -195,22 +268,22 @@ main-is: Main.hs hs-source-dirs: executables- ghc-options: -Wall -rtsopts -threaded -with-rtsopts=-N+ ghc-options: -Weverything -Wno-implicit-prelude -Wno-missing-import-lists -Wno-safe -Wno-unsafe -rtsopts -threaded 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+ aeson ==1.2.3.*+ , aeson-pretty ==0.8.5.*+ , base ==4.10.1.*+ , binary ==0.8.5.*+ , binary-bits ==0.5.*+ , bytestring ==0.10.8.*+ , containers ==0.5.10.*+ , filepath ==1.4.1.*+ , http-client ==0.5.7.*+ , http-client-tls ==0.3.5.* , rattletrap- , template-haskell >=2.12.0 && <2.13- , text >=1.2.2 && <1.3- , vector >=0.12.0 && <0.13+ , template-haskell ==2.12.*+ , text ==1.2.2.*+ , transformers ==0.5.2.* other-modules: Paths_rattletrap default-language: Haskell2010@@ -220,25 +293,24 @@ main-is: Main.hs hs-source-dirs: tests- ghc-options: -Wall -rtsopts -threaded -with-rtsopts=-N+ ghc-options: -Weverything -Wno-implicit-prelude -Wno-missing-import-lists -Wno-safe -Wno-unsafe -rtsopts -threaded 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+ HUnit ==1.6.*+ , aeson ==1.2.3.*+ , aeson-pretty ==0.8.5.*+ , base ==4.10.1.*+ , binary ==0.8.5.*+ , binary-bits ==0.5.*+ , bytestring ==0.10.8.*+ , containers ==0.5.10.*+ , filepath ==1.4.1.*+ , http-client ==0.5.7.*+ , http-client-tls ==0.3.5.* , 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+ , template-haskell ==2.12.*+ , temporary ==1.2.1.*+ , text ==1.2.2.*+ , transformers ==0.5.2.* other-modules: Paths_rattletrap default-language: Haskell2010
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)
@@ -1,5 +1,1 @@-resolver: nightly-2017-11-25--packages:- - .- - tools+resolver: lts-10.0
@@ -1,106 +1,107 @@-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+import Control.Exception (displayException, try)+import Control.Monad (unless)+import Data.ByteString.Lazy (readFile)+import Prelude hiding (readFile)+import Rattletrap.Console.Main (rattletrap)+import System.FilePath (addExtension, joinPath)+import System.IO.Temp (withSystemTempDirectory)+import Text.Printf (printf) -main :: IO ()-main = Hspec.hspec spec+import qualified Test.HUnit as Test -spec :: Hspec.Spec-spec = Hspec.describe "Rattletrap" (mapM_ (uncurry itCanRoundTrip) replays)+main :: IO Test.Counts+main = withSystemTempDirectory "rattletrap-" (Test.runTestTT . toTests) -itCanRoundTrip :: String -> String -> Hspec.Spec-itCanRoundTrip uuid description =- Hspec.it (specName uuid description) (specBody uuid)+toTests :: FilePath -> Test.Test+toTests directory = Test.TestList (fmap (toTest directory) replays) -specName :: String -> String -> String-specName uuid description = unwords [take 4 uuid, description]+toTest :: FilePath -> (String, String) -> Test.Test+toTest directory (uuid, name) = Test.TestLabel+ (toLabel uuid name)+ (Test.TestCase (toAssertion directory uuid)) -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")- )+toLabel :: String -> String -> String+toLabel = printf "%s: %s" -pathToReplay :: String -> FilePath-pathToReplay uuid =- FilePath.joinPath ["replays", FilePath.addExtension uuid ".replay"]+toAssertion :: FilePath -> String -> Test.Assertion+toAssertion directory uuid = do+ let inputFile = joinPath ["replays", addExtension uuid ".replay"]+ result <- try (readFile inputFile)+ case result of+ Left problem -> putStrLn (displayException (problem :: IOError))+ Right input -> do+ let jsonFile = joinPath [directory, addExtension uuid ".json"]+ rattletrap "" ["--compact", "--input", inputFile, "--output", jsonFile]+ let outputFile = joinPath [directory, addExtension uuid ".replay"]+ rattletrap "" ["--input", jsonFile, "--output", outputFile]+ output <- readFile outputFile+ unless+ (output == input)+ (Test.assertFailure "output does not match input") 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")+ [ ("0008", "a flip time")+ , ("000b", "nintendo switch")+ , ("07e9", "a game mode before Neo Tokyo")+ , ("0ad2", "some Latin-1 text")+ , ("1205", "rumble mode")+ , ("160c", "a dedicated server IP")+ , ("16d5", "new property types")+ , ("18d6", "an online loadout attribute")+ , ("1a12", "overtime")+ , ("1ae4", "a game time")+ , ("1bc2", "no padding after the frames")+ , ("1d1d", "a camera pitch")+ , ("1ef9", "a private hoops match")+ , ("1f37", "splitscreen players")+ , ("2114", "a match save")+ , ("2266", "dropshot")+ , ("22ba", "a vote to forfeit")+ , ("27b6", "some UTF-16 text")+ , ("29f5", "frames")+ , ("2cfe", "a new playstation id")+ , ("3381", "patch 1.37")+ , ("372d", "a camera yaw attribute")+ , ("387f", "a frozen attribute")+ , ("3ea1", "a custom team name")+ , ("4126", "a game mode after Neo Tokyo")+ , ("42f0", "reservations after Neo Tokyo")+ , ("504e", "some messages")+ , ("520e", "no pickup attribute")+ , ("52aa", "a match-ending attribute")+ , ("540d", "a demolish attribute")+ , ("551c", "private match settings")+ , ("6320", "a forfeit attribute")+ , ("6688", "a malformed byte property")+ , ("6b0d", "patch 1.37")+ , ("6d1b", "a flip right")+ , ("6f7c", "a map with numbers")+ , ("7109", "a boost modifier")+ , ("7bf6", "an online loadouts attribute")+ , ("89cb", "remote user data")+ , ("8ae5", "new painted items")+ , ("9704", "a batarang")+ , ("98e5", "a player using behind view")+ , ("a128", "a round count down")+ , ("a52f", "some more mutators")+ , ("a558", "extended explosion data")+ , ("a671", "a waiting player")+ , ("a7f0", "a ready attribute")+ , ("afb1", "patch 1.37")+ , ("b9f9", "a party leader")+ , ("c14f", "some mutators")+ , ("c837", "a spectator")+ , ("cc4c", "after Starbase ARC")+ , ("d044", "hoops mutators")+ , ("d428", "a private hockey match")+ , ("d7fb", "an explosion attribute")+ , ("dcb3", "a pawn type attribute")+ , ("de56", "a problematic product attribute")+ , ("eae3", "an actor/object ID collision")+ , ("eae8", "custom team colors")+ , ("f299", "a location attribute")+ , ("f7b9", "a hockey game event")+ , ("f811", "no frames")+ , ("fdc7", "an MVP") ]