rattletrap 1.0.0 → 2.0.0
raw patch · 252 files changed
+3944/−3674 lines, 252 filesbinary-addedPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
Files
- HLint.hs +0/−12
- executable/Main.hs +0/−5
- library/Rattletrap.hs +0/−58
- library/Rattletrap/ActorMap.hs +0/−13
- library/Rattletrap/Attribute.hs +0/−62
- library/Rattletrap/Attribute/Boolean.hs +0/−17
- library/Rattletrap/Attribute/Byte.hs +0/−19
- library/Rattletrap/Attribute/CamSettings.hs +0/−36
- library/Rattletrap/Attribute/ClubColors.hs +0/−29
- library/Rattletrap/Attribute/Demolish.hs +0/−41
- library/Rattletrap/Attribute/Enum.hs +0/−18
- library/Rattletrap/Attribute/Explosion.hs +0/−24
- library/Rattletrap/Attribute/FlaggedInt.hs +0/−22
- library/Rattletrap/Attribute/Float.hs +0/−19
- library/Rattletrap/Attribute/GameMode.hs +0/−26
- library/Rattletrap/Attribute/Int.hs +0/−19
- library/Rattletrap/Attribute/Loadout.hs +0/−60
- library/Rattletrap/Attribute/LoadoutOnline.hs +0/−39
- library/Rattletrap/Attribute/Loadouts.hs +0/−22
- library/Rattletrap/Attribute/LoadoutsOnline.hs +0/−34
- library/Rattletrap/Attribute/Location.hs +0/−19
- library/Rattletrap/Attribute/MusicStinger.hs +0/−26
- library/Rattletrap/Attribute/PartyLeader.hs +0/−33
- library/Rattletrap/Attribute/Pickup.hs +0/−32
- library/Rattletrap/Attribute/PrivateMatchSettings.hs +0/−53
- library/Rattletrap/Attribute/QWord.hs +0/−19
- library/Rattletrap/Attribute/Reservation.hs +0/−54
- library/Rattletrap/Attribute/RigidBodyState.hs +0/−54
- library/Rattletrap/Attribute/String.hs +0/−19
- library/Rattletrap/Attribute/TeamPaint.hs +0/−37
- 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/−31
- library/Rattletrap/AttributeValue.hs +0/−217
- library/Rattletrap/Cache.hs +0/−28
- library/Rattletrap/ClassAttributeMap.hs +0/−269
- library/Rattletrap/ClassMapping.hs +0/−21
- library/Rattletrap/Content.hs +0/−92
- library/Rattletrap/Crc.hs +0/−37
- library/Rattletrap/Data.hs +0/−351
- library/Rattletrap/Frame.hs +0/−51
- library/Rattletrap/Header.hs +0/−59
- library/Rattletrap/Initialization.hs +0/−36
- library/Rattletrap/Json.hs +0/−105
- library/Rattletrap/KeyFrame.hs +0/−24
- library/Rattletrap/Main.hs +0/−47
- library/Rattletrap/Mark.hs +0/−21
- library/Rattletrap/Message.hs +0/−24
- library/Rattletrap/Primitive.hs +0/−31
- library/Rattletrap/Primitive/CompressedWord.hs +0/−59
- library/Rattletrap/Primitive/CompressedWordVector.hs +0/−26
- library/Rattletrap/Primitive/Dictionary.hs +0/−33
- 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/−42
- library/Rattletrap/Primitive/List.hs +0/−22
- library/Rattletrap/Primitive/Section.hs +0/−38
- library/Rattletrap/Primitive/Text.hs +0/−88
- library/Rattletrap/Primitive/Vector.hs +0/−29
- 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/−25
- library/Rattletrap/PropertyValue.hs +0/−68
- library/Rattletrap/RemoteId.hs +0/−42
- library/Rattletrap/Replay.hs +0/−25
- library/Rattletrap/Replication.hs +0/−57
- library/Rattletrap/Replication/Destroyed.hs +0/−14
- library/Rattletrap/Replication/Spawned.hs +0/−57
- library/Rattletrap/Replication/Updated.hs +0/−27
- library/Rattletrap/ReplicationValue.hs +0/−60
- library/Rattletrap/Utility.hs +0/−39
- library/Rattletrap/Version.hs +0/−7
- rattletrap.cabal +179/−178
- replays/00080014003600090000036E0F65CCEB.replay binary
- replays/07E925B1423653D44CB8B4B2524792C1.replay binary
- replays/0AD21FEE4A512906BEB6C98136AAF49A.replay binary
- replays/1205D96C4D819800927791820096CD49.replay binary
- replays/160CA83E41083BFD8E6315B4BFCA0561.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/22BACD794ABE7B92E50E9CBDBD9C59CE.replay binary
- replays/27B6A7B64553F0F685874584F96BAB1B.replay binary
- replays/29F582C34A65EB34D358A784CBE3C189.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/52AA67F94090C19D33C5009E54D31FE4.replay binary
- replays/540DA764423C8FB24EB9D486D982F16F.replay binary
- replays/551CA4D44FF2B86015DE44A6B5790D4C.replay binary
- replays/6320E51C49066A7C210A2993C2201D5F.replay binary
- replays/6688EEE34BFEB3EC3A9E3283098CC712.replay binary
- replays/6D1B06D844A5BB91B81FD4B5B28F08BA.replay binary
- replays/6F7CFCD24638F8A6567AB3A8B9958A90.replay binary
- replays/7109EB9846D303E54B7ACBA792036213.replay binary
- replays/7BF6073F4614CE0A438994B9A260DA6A.replay binary
- replays/89CBA30E46FA5385BDD35DA4285D4D2E.replay binary
- replays/9704208245D7DD851F2FB2BC7DFD9AC3.replay binary
- replays/98E58A904D713F2DE202358E8573265D.replay binary
- replays/A128B3AB45D5A18E3EF9CF93C9576BCE.replay binary
- replays/A52F804845573D8DA65E97BF59026A43.replay binary
- replays/A6711CE74272B2E663DCC9A200A218E3.replay binary
- replays/A7F001A1417A19BFA8C90990D8F7C2FF.replay binary
- replays/B9F9B87D4A9D0A3D25D4EC91C0401DE2.replay binary
- replays/C14F7E0E4D9B5E6BE9AD5D8ED56B174C.replay binary
- replays/C8372B1345B1803DEF039F815DBD802D.replay binary
- replays/CC4CA70D4F7A67EBAD0ED9B9923106F7.replay binary
- replays/D428F81646A98C25902CE988AE5C14C8.replay binary
- replays/D7FB197A451D69075A0C99A2F49A4053.replay binary
- replays/DCB3A6B94A9DBE46FDE5EAA9B012F6C8.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
- source/executables/Main.hs +5/−0
- source/library/Rattletrap.hs +85/−0
- source/library/Rattletrap/ActorMap.hs +13/−0
- source/library/Rattletrap/Attribute.hs +64/−0
- source/library/Rattletrap/Attribute/Boolean.hs +17/−0
- source/library/Rattletrap/Attribute/Byte.hs +19/−0
- source/library/Rattletrap/Attribute/CamSettings.hs +34/−0
- source/library/Rattletrap/Attribute/ClubColors.hs +28/−0
- source/library/Rattletrap/Attribute/Demolish.hs +41/−0
- source/library/Rattletrap/Attribute/Enum.hs +18/−0
- source/library/Rattletrap/Attribute/Explosion.hs +24/−0
- source/library/Rattletrap/Attribute/FlaggedInt.hs +22/−0
- source/library/Rattletrap/Attribute/Float.hs +19/−0
- source/library/Rattletrap/Attribute/GameMode.hs +25/−0
- source/library/Rattletrap/Attribute/Int.hs +18/−0
- source/library/Rattletrap/Attribute/Loadout.hs +51/−0
- source/library/Rattletrap/Attribute/LoadoutOnline.hs +39/−0
- source/library/Rattletrap/Attribute/Loadouts.hs +22/−0
- source/library/Rattletrap/Attribute/LoadoutsOnline.hs +30/−0
- source/library/Rattletrap/Attribute/Location.hs +19/−0
- source/library/Rattletrap/Attribute/MusicStinger.hs +25/−0
- source/library/Rattletrap/Attribute/PartyLeader.hs +33/−0
- source/library/Rattletrap/Attribute/Pickup.hs +32/−0
- source/library/Rattletrap/Attribute/PrivateMatchSettings.hs +48/−0
- source/library/Rattletrap/Attribute/QWord.hs +19/−0
- source/library/Rattletrap/Attribute/Reservation.hs +50/−0
- source/library/Rattletrap/Attribute/RigidBodyState.hs +52/−0
- source/library/Rattletrap/Attribute/String.hs +19/−0
- source/library/Rattletrap/Attribute/TeamPaint.hs +37/−0
- source/library/Rattletrap/Attribute/UniqueId.hs +26/−0
- source/library/Rattletrap/Attribute/WeldedInfo.hs +31/−0
- source/library/Rattletrap/AttributeMapping.hs +21/−0
- source/library/Rattletrap/AttributeType.hs +31/−0
- source/library/Rattletrap/AttributeValue.hs +217/−0
- source/library/Rattletrap/Cache.hs +28/−0
- source/library/Rattletrap/ClassAttributeMap.hs +292/−0
- source/library/Rattletrap/ClassMapping.hs +21/−0
- source/library/Rattletrap/Content.hs +121/−0
- source/library/Rattletrap/Crc.hs +46/−0
- source/library/Rattletrap/Data.hs +351/−0
- source/library/Rattletrap/Frame.hs +62/−0
- source/library/Rattletrap/Header.hs +100/−0
- source/library/Rattletrap/Initialization.hs +40/−0
- source/library/Rattletrap/Json.hs +105/−0
- source/library/Rattletrap/KeyFrame.hs +27/−0
- source/library/Rattletrap/Main.hs +58/−0
- source/library/Rattletrap/Mark.hs +23/−0
- source/library/Rattletrap/Message.hs +27/−0
- source/library/Rattletrap/Primitive.hs +31/−0
- source/library/Rattletrap/Primitive/CompressedWord.hs +59/−0
- source/library/Rattletrap/Primitive/CompressedWordVector.hs +26/−0
- source/library/Rattletrap/Primitive/Dictionary.hs +59/−0
- source/library/Rattletrap/Primitive/Float32.hs +33/−0
- source/library/Rattletrap/Primitive/Int32.hs +33/−0
- source/library/Rattletrap/Primitive/Int8.hs +32/−0
- source/library/Rattletrap/Primitive/Int8Vector.hs +42/−0
- source/library/Rattletrap/Primitive/List.hs +22/−0
- source/library/Rattletrap/Primitive/Section.hs +55/−0
- source/library/Rattletrap/Primitive/Text.hs +108/−0
- source/library/Rattletrap/Primitive/Vector.hs +40/−0
- source/library/Rattletrap/Primitive/Word32.hs +33/−0
- source/library/Rattletrap/Primitive/Word64.hs +33/−0
- source/library/Rattletrap/Primitive/Word8.hs +33/−0
- source/library/Rattletrap/Property.hs +26/−0
- source/library/Rattletrap/PropertyValue.hs +72/−0
- source/library/Rattletrap/RemoteId.hs +55/−0
- source/library/Rattletrap/Replay.hs +39/−0
- source/library/Rattletrap/Replication.hs +57/−0
- source/library/Rattletrap/Replication/Destroyed.hs +17/−0
- source/library/Rattletrap/Replication/Spawned.hs +95/−0
- source/library/Rattletrap/Replication/Updated.hs +27/−0
- source/library/Rattletrap/ReplicationValue.hs +63/−0
- source/library/Rattletrap/Utility.hs +33/−0
- source/library/Rattletrap/Version.hs +7/−0
- source/tests/Main.hs +100/−0
- test/Test.hs +0/−98
- test/replays/00080014003600090000036E0F65CCEB.replay binary
- test/replays/07E925B1423653D44CB8B4B2524792C1.replay binary
- test/replays/0AD21FEE4A512906BEB6C98136AAF49A.replay binary
- test/replays/1205D96C4D819800927791820096CD49.replay binary
- test/replays/160CA83E41083BFD8E6315B4BFCA0561.replay binary
- test/replays/18D6738D415B70B5BE4C299588D3C141.replay binary
- test/replays/1A126AC24CAA0DB0E98835BD960B8AF8.replay binary
- test/replays/1BC2D01444ACE577D01E988EADD4DFD0.replay binary
- test/replays/1D1DE97D4941C86E43FE0093563DB621.replay binary
- test/replays/1EF90FCC4F719F606A5327B3CDD782A4.replay binary
- test/replays/1F3798E540B0C37A946561ABBB3037F9.replay binary
- test/replays/211466D04B983F5A33CC2FA1D5928672.replay binary
- test/replays/22BACD794ABE7B92E50E9CBDBD9C59CE.replay binary
- test/replays/27B6A7B64553F0F685874584F96BAB1B.replay binary
- test/replays/29F582C34A65EB34D358A784CBE3C189.replay binary
- test/replays/372DBFCA4BDB340E4357B6BD43032802.replay binary
- test/replays/387F059C47C09E253C875CA990EFD9F2.replay binary
- test/replays/3EA147DD485B8DD24810689A7A989E44.replay binary
- test/replays/4126861E477F4A03DE2A4080374D7908.replay binary
- test/replays/42F0D8DA4FC89AE7B80FCAB7F637A8EA.replay binary
- test/replays/504ED825482186E771FAA9B642CE5CE4.replay binary
- test/replays/52AA67F94090C19D33C5009E54D31FE4.replay binary
- test/replays/540DA764423C8FB24EB9D486D982F16F.replay binary
- test/replays/551CA4D44FF2B86015DE44A6B5790D4C.replay binary
- test/replays/6688EEE34BFEB3EC3A9E3283098CC712.replay binary
- test/replays/6D1B06D844A5BB91B81FD4B5B28F08BA.replay binary
- test/replays/6F7CFCD24638F8A6567AB3A8B9958A90.replay binary
- test/replays/7109EB9846D303E54B7ACBA792036213.replay binary
- test/replays/7BF6073F4614CE0A438994B9A260DA6A.replay binary
- test/replays/89CBA30E46FA5385BDD35DA4285D4D2E.replay binary
- test/replays/9704208245D7DD851F2FB2BC7DFD9AC3.replay binary
- test/replays/98E58A904D713F2DE202358E8573265D.replay binary
- test/replays/A128B3AB45D5A18E3EF9CF93C9576BCE.replay binary
- test/replays/A52F804845573D8DA65E97BF59026A43.replay binary
- test/replays/A6711CE74272B2E663DCC9A200A218E3.replay binary
- test/replays/A7F001A1417A19BFA8C90990D8F7C2FF.replay binary
- test/replays/B82DDB624C393A4A425E68AB40DC2450.replay binary
- test/replays/B9F9B87D4A9D0A3D25D4EC91C0401DE2.replay binary
- test/replays/C14F7E0E4D9B5E6BE9AD5D8ED56B174C.replay binary
- test/replays/C8372B1345B1803DEF039F815DBD802D.replay binary
- test/replays/D428F81646A98C25902CE988AE5C14C8.replay binary
- test/replays/D7FB197A451D69075A0C99A2F49A4053.replay binary
- test/replays/DCB3A6B94A9DBE46FDE5EAA9B012F6C8.replay binary
- test/replays/EAE311E84BA35B590A6FDBA6DD4F2FEB.replay binary
- test/replays/EAE8DADA4BB2DC5422792C9B4A67392D.replay binary
- test/replays/F299F176491554B11E34AB91CA76B2CE.replay binary
- test/replays/F7B9E14545C7467B89A00895980FCD73.replay binary
- test/replays/F811C1D24888015E23B598AD8628C742.replay binary
- test/replays/FDC79DA84DD463D4BCCE6B892829AC88.replay binary
@@ -1,12 +0,0 @@-module HLint where--import "hint" HLint.Default-import "hint" HLint.Dollar--ignore "Eta reduce"--ignore "Use &&&"--ignore "Use <|>"--ignore "Use uncurry"
@@ -1,5 +0,0 @@-module Main- ( module Rattletrap- ) where--import Rattletrap (main)
@@ -1,58 +0,0 @@-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.Initialization- , module Rattletrap.KeyFrame- , module Rattletrap.Main- , module Rattletrap.Mark- , module Rattletrap.Message- , module Rattletrap.Primitive- , module Rattletrap.Property- , module Rattletrap.PropertyValue- , module Rattletrap.RemoteId- , module Rattletrap.Replay- , module Rattletrap.Replication- , module Rattletrap.ReplicationValue- , module Rattletrap.Utility- , module Rattletrap.Version- ) where--import Rattletrap.ActorMap-import Rattletrap.Attribute-import Rattletrap.AttributeMapping-import Rattletrap.AttributeType-import Rattletrap.AttributeValue-import Rattletrap.Cache-import Rattletrap.ClassAttributeMap-import Rattletrap.ClassMapping-import Rattletrap.Content-import Rattletrap.Crc-import Rattletrap.Data-import Rattletrap.Frame-import Rattletrap.Header-import Rattletrap.Initialization-import Rattletrap.Json ()-import Rattletrap.KeyFrame-import Rattletrap.Main-import Rattletrap.Mark-import Rattletrap.Message-import Rattletrap.Primitive-import Rattletrap.Property-import Rattletrap.PropertyValue-import Rattletrap.RemoteId-import Rattletrap.Replay-import Rattletrap.Replication-import Rattletrap.ReplicationValue-import Rattletrap.Utility-import Rattletrap.Version
@@ -1,13 +0,0 @@-module Rattletrap.ActorMap where--import Rattletrap.Primitive--import qualified Data.Map.Strict as Map--type ActorMap = Map.Map CompressedWord Word32--makeActorMap :: ActorMap-makeActorMap = Map.empty--updateActorMap :: CompressedWord -> Word32 -> ActorMap -> ActorMap-updateActorMap actorId objectId actorMap = Map.insert actorId objectId actorMap
@@ -1,62 +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- , attribute_name :: Text- , attributeValue :: AttributeValue- } deriving (Eq, Ord, Show)--getAttributes- :: (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)- -> 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 name- pure (Attribute id_ name value)--putAttribute :: Attribute -> BinaryBit.BitPut ()-putAttribute attribute = do- BinaryBit.putBool True- putCompressedWord (attributeId attribute)- putAttributeValue (attributeValue attribute)
@@ -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,36 +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- } deriving (Eq, Ord, Show)--getCamSettingsAttribute :: BinaryBit.BitGet CamSettingsAttribute-getCamSettingsAttribute = do- fov <- getFloat32Bits- height <- getFloat32Bits- angle <- getFloat32Bits- distance <- getFloat32Bits- stiffness <- getFloat32Bits- swivelSpeed <- getFloat32Bits- pure- (CamSettingsAttribute fov height angle distance stiffness swivelSpeed)--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)
@@ -1,29 +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,41 +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,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,26 +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)- -> BinaryBit.BitGet GameModeAttribute-getGameModeAttribute version = do- let numBits =- if version < (868, 12)- 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,19 +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,60 +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- , loadoutAttributeAntenna :: Word32- , loadoutAttributeTopper :: Word32- , loadoutAttributeUnknown1 :: Word32- , loadoutAttributeUnknown2 :: 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- g <- getWord32Bits- h <-- if version > Word8 10- then do- h <- getWord32Bits- pure (Just h)- else pure Nothing- pure- (LoadoutAttribute- version- body- decal- wheels- rocketTrail- antenna- topper- g- h)--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)- case loadoutAttributeUnknown2 loadoutAttribute of- Nothing -> pure ()- Just x -> putWord32Bits x
@@ -1,39 +0,0 @@-module Rattletrap.Attribute.LoadoutOnline 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--newtype LoadoutOnlineAttribute = LoadoutOnlineAttribute- { loadoutAttributeValue :: [[(Word32, CompressedWord)]]- } deriving (Eq, Ord, Show)--getLoadoutOnlineAttribute :: BinaryBit.BitGet LoadoutOnlineAttribute-getLoadoutOnlineAttribute = do- size <- getWord8Bits- values <-- Monad.replicateM- (fromIntegral (word8Value size))- (do innerSize <- getWord8Bits- Monad.replicateM- (fromIntegral (word8Value innerSize))- (do x <- getWord32Bits- y <- getCompressedWord 27- pure (x, y)))- pure (LoadoutOnlineAttribute values)--putLoadoutOnlineAttribute :: LoadoutOnlineAttribute -> BinaryBit.BitPut ()-putLoadoutOnlineAttribute loadoutAttribute = do- let values = loadoutAttributeValue loadoutAttribute- putWord8Bits (Word8 (fromIntegral (length values)))- mapM_- (\xs -> do- putWord8Bits (Word8 (fromIntegral (length xs)))- mapM_- (\(x, y) -> do- putWord32Bits x- putCompressedWord y)- xs)- values
@@ -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,34 +0,0 @@-module Rattletrap.Attribute.LoadoutsOnline where--import Rattletrap.Attribute.LoadoutOnline--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--data LoadoutsOnlineAttribute = LoadoutsOnlineAttribute- { loadoutsOnlineAttributeBlue :: LoadoutOnlineAttribute- , loadoutsOnlineAttributeOrange :: LoadoutOnlineAttribute- , loadoutsOnlineAttributeUnknown1 :: Bool- , loadoutsOnlineAttributeUnknown2 :: Bool- } deriving (Eq, Ord, Show)--getLoadoutsOnlineAttribute :: BinaryBit.BitGet LoadoutsOnlineAttribute-getLoadoutsOnlineAttribute = do- blueLoadout <- getLoadoutOnlineAttribute- orangeLoadout <- getLoadoutOnlineAttribute- 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,26 +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,33 +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 :: BinaryBit.BitGet PartyLeaderAttribute-getPartyLeaderAttribute = do- systemId <- getWord8Bits- maybeRemoteAndLocalId <-- if systemId == Word8 0- then pure Nothing- else do- remoteId <- getRemoteId 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,32 +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,53 +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,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,54 +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)- -> BinaryBit.BitGet ReservationAttribute-getReservationAttribute version = do- number <- getCompressedWord 7- uniqueId <- getUniqueIdAttribute- 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)- 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,54 +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,37 +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 :: BinaryBit.BitGet UniqueIdAttribute-getUniqueIdAttribute = do- systemId <- getWord8Bits- remoteId <- getRemoteId 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,31 +0,0 @@-module Rattletrap.AttributeType where--data AttributeType- = BooleanAttributeType- | ByteAttributeType- | CamSettingsAttributeType- | ClubColorsAttributeType- | DemolishAttributeType- | EnumAttributeType- | ExplosionAttributeType- | 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,217 +0,0 @@-module Rattletrap.AttributeValue- ( module Rattletrap.AttributeValue- , module Rattletrap.Attribute.Boolean- , module Rattletrap.Attribute.Byte- , module Rattletrap.Attribute.CamSettings- , module Rattletrap.Attribute.ClubColors- , module Rattletrap.Attribute.Demolish- , module Rattletrap.Attribute.Enum- , module Rattletrap.Attribute.Explosion- , 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.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.Boolean-import Rattletrap.Attribute.Byte-import Rattletrap.Attribute.CamSettings-import Rattletrap.Attribute.ClubColors-import Rattletrap.Attribute.Demolish-import Rattletrap.Attribute.Enum-import Rattletrap.Attribute.Explosion-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.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- = BooleanAttributeValue BooleanAttribute- | ByteAttributeValue ByteAttribute- | CamSettingsAttributeValue CamSettingsAttribute- | ClubColorsAttributeValue ClubColorsAttribute- | DemolishAttributeValue DemolishAttribute- | EnumAttributeValue EnumAttribute- | ExplosionAttributeValue ExplosionAttribute- | 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) -> Text -> BinaryBit.BitGet AttributeValue-getAttributeValue version name =- case Map.lookup name attributeTypes of- Just constructor ->- case constructor of- BooleanAttributeType -> do- x <- getBooleanAttribute- pure (BooleanAttributeValue x)- ByteAttributeType -> do- x <- getByteAttribute- pure (ByteAttributeValue x)- CamSettingsAttributeType -> do- x <- getCamSettingsAttribute- pure (CamSettingsAttributeValue x)- ClubColorsAttributeType -> do- x <- getClubColorsAttribute- pure (ClubColorsAttributeValue x)- DemolishAttributeType -> do- x <- getDemolishAttribute- pure (DemolishAttributeValue x)- EnumAttributeType -> do- x <- getEnumAttribute- pure (EnumAttributeValue x)- ExplosionAttributeType -> do- x <- getExplosionAttribute- pure (ExplosionAttributeValue 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- pure (LoadoutOnlineAttributeValue x)- LoadoutsAttributeType -> do- x <- getLoadoutsAttribute- pure (LoadoutsAttributeValue x)- LoadoutsOnlineAttributeType -> do- x <- getLoadoutsOnlineAttribute- pure (LoadoutsOnlineAttributeValue x)- LocationAttributeType -> do- x <- getLocationAttribute- pure (LocationAttributeValue x)- MusicStingerAttributeType -> do- x <- getMusicStingerAttribute- pure (MusicStingerAttributeValue x)- PartyLeaderAttributeType -> do- x <- getPartyLeaderAttribute- 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- 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- BooleanAttributeValue x -> putBooleanAttribute x- ByteAttributeValue x -> putByteAttribute x- CamSettingsAttributeValue x -> putCamSettingsAttribute x- ClubColorsAttributeValue x -> putClubColorsAttribute x- DemolishAttributeValue x -> putDemolishAttribute x- EnumAttributeValue x -> putEnumAttribute x- ExplosionAttributeValue x -> putExplosionAttribute 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,269 +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.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--data ClassAttributeMap = ClassAttributeMap- { classAttributeMapObjectMap :: Map.Map Word32 Text- , classAttributeMapClassMap :: Bimap.Bimap Word32 Text- , classAttributeMapObjectClassMap :: Map.Map Word32 Word32- , classAttributeMapValue :: Map.Map Word32 (Map.Map Word32 Word32)- } deriving (Eq, Show)--makeClassAttributeMap :: List Text- -> List ClassMapping- -> List Cache- -> ClassAttributeMap-makeClassAttributeMap objects classMappings caches =- 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)- in ClassAttributeMap objectMap classMap objectClassMap value--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"- toText text =- Text- (Int32 (fromIntegral (Text.length text + 1)))- (Text.snoc text '\x00')- in if Text.isInfixOf crowdActor name- then toText crowdActor- else if Text.isInfixOf crowdManager name- then toText crowdManager- else if Text.isInfixOf boostPickup name- then toText boostPickup- else if Text.isInfixOf mapScoreboard name- then toText mapScoreboard- else objectName--objectClasses :: Map.Map Text Text-objectClasses =- Map.map- stringToText- (Map.mapKeys stringToText (Map.fromList rawObjectClasses))--classHasLocation :: Text -> Bool-classHasLocation className = Set.member className classesWithLocation--classesWithLocation :: Set.Set Text-classesWithLocation = Set.fromList (map stringToText rawClassesWithLocation)--classHasRotation :: Text -> Bool-classHasRotation className = Set.member className classesWithRotation--classesWithRotation :: Set.Set Text-classesWithRotation = Set.fromList (map stringToText rawClassesWithRotation)--getAttributeIdLimit :: Map.Map Word32 Word32 -> Maybe Word-getAttributeIdLimit attributeMap = do- ((streamId, _), _) <- Map.maxViewWithKey attributeMap- let limit = fromIntegral (word32Value streamId)- pure limit--getAttributeName :: ClassAttributeMap- -> Map.Map Word32 Word32- -> CompressedWord- -> Maybe Text-getAttributeName classAttributeMap attributeMap streamId = do- let key = Word32 (fromIntegral (compressedWordValue streamId))- attributeId <- Map.lookup key attributeMap- let objectMap = classAttributeMapObjectMap classAttributeMap- Map.lookup attributeId objectMap--getAttributeMap :: ClassAttributeMap- -> ActorMap- -> CompressedWord- -> Maybe (Map.Map Word32 Word32)-getAttributeMap classAttributeMap actorMap actorId = do- objectId <- Map.lookup actorId actorMap- let objectClassMap = classAttributeMapObjectClassMap classAttributeMap- classId <- Map.lookup objectId objectClassMap- let value = classAttributeMapValue classAttributeMap- Map.lookup classId value
@@ -1,21 +0,0 @@-module Rattletrap.ClassMapping where--import Rattletrap.Primitive--import qualified Data.Binary as Binary--data ClassMapping = ClassMapping- { classMappingName :: Text- , classMappingStreamId :: Word32- } deriving (Eq, Ord, Show)--getClassMapping :: Binary.Get ClassMapping-getClassMapping = do- name <- getText- streamId <- getWord32- pure (ClassMapping name streamId)--putClassMapping :: ClassMapping -> Binary.Put-putClassMapping classMapping = do- putText (classMappingName classMapping)- putWord32 (classMappingStreamId classMapping)
@@ -1,92 +0,0 @@-module Rattletrap.Content where--import Rattletrap.ActorMap-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--data Content = Content- { contentLevels :: List Text- , contentKeyFrames :: List KeyFrame- , contentStreamSize :: Word32- , contentFrames :: [Frame]- , contentTrailingBits :: [Bool]- , contentMessages :: List Message- , contentMarks :: List Mark- , contentPackages :: List Text- , contentObjects :: List Text- , contentNames :: List Text- , contentClassMappings :: List ClassMapping- , contentCaches :: List Cache- } deriving (Eq, Ord, Show)--getContent :: (Int, Int) -> Int -> Binary.Get Content-getContent version numFrames = 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- let (frames, remainingBits) =- Binary.runGet- (BinaryBit.runBitGet- (do (theFrames, _) <-- getFrames version numFrames classAttributeMap makeActorMap- theRemainingBits <- getRemainingBits- pure (theFrames, theRemainingBits)))- (reverseBytes stream)- let trailingBits = reverse (dropWhile not (reverse remainingBits))- pure- (Content- levels- keyFrames- streamSize- frames- trailingBits- 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- (do putFrames (contentFrames content)- mapM_ BinaryBit.putBool (contentTrailingBits 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,37 +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--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,351 +0,0 @@-module Rattletrap.Data where--import Rattletrap.AttributeType--rawParentClasses :: [(String, String)]-rawParentClasses =- [ ("Engine.Actor", "Core.Object")- , ("Engine.GameReplicationInfo", "Engine.ReplicationInfo")- , ("Engine.Info", "Engine.Actor")- , ("Engine.Pawn", "Engine.Actor")- , ("Engine.PlayerReplicationInfo", "Engine.ReplicationInfo")- , ("Engine.ReplicationInfo", "Engine.Info")- , ("Engine.TeamInfo", "Engine.ReplicationInfo")- , ("ProjectX.GRI_X", "Engine.GameReplicationInfo")- , ("ProjectX.PRI_X", "Engine.PlayerReplicationInfo")- , ("ProjectX.Pawn_X", "Engine.Pawn")- , ("TAGame.Ball_TA", "TAGame.RBActor_TA")- , ("TAGame.CameraSettingsActor_TA", "Engine.ReplicationInfo")- , ("TAGame.CarComponent_Boost_TA", "TAGame.CarComponent_TA")- , ("TAGame.CarComponent_Dodge_TA", "TAGame.CarComponent_TA")- , ("TAGame.CarComponent_DoubleJump_TA", "TAGame.CarComponent_TA")- , ("TAGame.CarComponent_FlipCar_TA", "TAGame.CarComponent_TA")- , ("TAGame.CarComponent_Jump_TA", "TAGame.CarComponent_TA")- , ("TAGame.CarComponent_TA", "Engine.ReplicationInfo")- , ("TAGame.Car_Season_TA", "TAGame.PRI_TA")- , ("TAGame.Car_TA", "TAGame.Vehicle_TA")- , ("TAGame.CrowdActor_TA", "Engine.ReplicationInfo")- , ("TAGame.CrowdManager_TA", "Engine.ReplicationInfo")- , ("TAGame.GRI_TA", "ProjectX.GRI_X")- , ("TAGame.GameEvent_Season_TA", "TAGame.GameEvent_Soccar_TA")- , ("TAGame.GameEvent_SoccarPrivate_TA", "TAGame.GameEvent_Soccar_TA")- , ("TAGame.GameEvent_SoccarSplitscreen_TA", "TAGame.GameEvent_SoccarPrivate_TA")- , ("TAGame.GameEvent_Soccar_TA", "TAGame.GameEvent_Team_TA")- , ("TAGame.GameEvent_TA", "Engine.ReplicationInfo")- , ("TAGame.GameEvent_Team_TA", "TAGame.GameEvent_TA")- , ("TAGame.InMapScoreboard_TA", "Engine.Actor")- , ("TAGame.PRI_TA", "ProjectX.PRI_X")- , ("TAGame.RBActor_TA", "ProjectX.Pawn_X")- , ("TAGame.SpecialPickup_BallCarSpring_TA", "TAGame.SpecialPickup_Spring_TA")- , ("TAGame.SpecialPickup_BallFreeze_TA", "TAGame.SpecialPickup_Targeted_TA")- , ("TAGame.SpecialPickup_BallGravity_TA", "TAGame.SpecialPickup_TA")- , ("TAGame.SpecialPickup_BallLasso_TA", "TAGame.SpecialPickup_GrapplingHook_TA")- , ("TAGame.SpecialPickup_BallVelcro_TA", "TAGame.SpecialPickup_TA")- , ("TAGame.SpecialPickup_Batarang_TA", "TAGame.SpecialPickup_BallLasso_TA")- , ("TAGame.SpecialPickup_BoostOverride_TA", "TAGame.SpecialPickup_Targeted_TA")- , ("TAGame.SpecialPickup_GrapplingHook_TA", "TAGame.SpecialPickup_Targeted_TA")- , ("TAGame.SpecialPickup_HitForce_TA", "TAGame.SpecialPickup_TA")- , ("TAGame.SpecialPickup_Spring_TA", "TAGame.SpecialPickup_Targeted_TA")- , ("TAGame.SpecialPickup_Swapper_TA", "TAGame.SpecialPickup_Targeted_TA")- , ("TAGame.SpecialPickup_TA", "TAGame.CarComponent_TA")- , ("TAGame.SpecialPickup_Targeted_TA", "TAGame.SpecialPickup_TA")- , ("TAGame.SpecialPickup_Tornado_TA", "TAGame.SpecialPickup_TA")- , ("TAGame.Team_Soccar_TA", "TAGame.Team_TA")- , ("TAGame.Team_TA", "Engine.TeamInfo")- , ("TAGame.VehiclePickup_Boost_TA", "TAGame.VehiclePickup_TA")- , ("TAGame.VehiclePickup_TA", "Engine.ReplicationInfo")- , ("TAGame.Vehicle_TA", "TAGame.RBActor_TA")- , ("TAGame.VoteActor_TA", "Engine.Actor")- ]--rawClassesWithLocation :: [String]-rawClassesWithLocation =- [ "TAGame.Ball_TA"- , "TAGame.CameraSettingsActor_TA"- , "TAGame.CarComponent_Boost_TA"- , "TAGame.CarComponent_Dodge_TA"- , "TAGame.CarComponent_DoubleJump_TA"- , "TAGame.CarComponent_FlipCar_TA"- , "TAGame.CarComponent_Jump_TA"- , "TAGame.Car_Season_TA"- , "TAGame.Car_TA"- , "TAGame.GRI_TA"- , "TAGame.GameEvent_Season_TA"- , "TAGame.GameEvent_SoccarPrivate_TA"- , "TAGame.GameEvent_SoccarSplitscreen_TA"- , "TAGame.GameEvent_Soccar_TA"- , "TAGame.PRI_TA"- , "TAGame.SpecialPickup_BallCarSpring_TA"- , "TAGame.SpecialPickup_BallFreeze_TA"- , "TAGame.SpecialPickup_BallGravity_TA"- , "TAGame.SpecialPickup_BallGravity_TA"- , "TAGame.SpecialPickup_BallLasso_TA"- , "TAGame.SpecialPickup_BallVelcro_TA"- , "TAGame.SpecialPickup_Batarang_TA"- , "TAGame.SpecialPickup_BoostOverride_TA"- , "TAGame.SpecialPickup_GrapplingHook_TA"- , "TAGame.SpecialPickup_HitForce_TA"- , "TAGame.SpecialPickup_Swapper_TA"- , "TAGame.SpecialPickup_Tornado_TA"- , "TAGame.Team_Soccar_TA"- ]--rawClassesWithRotation :: [String]-rawClassesWithRotation =- [ "TAGame.Ball_TA"- , "TAGame.Car_Season_TA"- , "TAGame.Car_TA"- ]--rawObjectClasses :: [(String, String)]-rawObjectClasses =- [ ("Archetypes.Ball.Ball_Basketball", "TAGame.Ball_TA")- , ("Archetypes.Ball.Ball_Default", "TAGame.Ball_TA")- , ("Archetypes.Ball.Ball_Puck", "TAGame.Ball_TA")- , ("Archetypes.Ball.CubeBall", "TAGame.Ball_TA")- , ("Archetypes.Car.Car_Default", "TAGame.Car_TA")- , ("Archetypes.CarComponents.CarComponent_Boost", "TAGame.CarComponent_Boost_TA")- , ("Archetypes.CarComponents.CarComponent_Dodge", "TAGame.CarComponent_Dodge_TA")- , ("Archetypes.CarComponents.CarComponent_DoubleJump", "TAGame.CarComponent_DoubleJump_TA")- , ("Archetypes.CarComponents.CarComponent_FlipCar", "TAGame.CarComponent_FlipCar_TA")- , ("Archetypes.CarComponents.CarComponent_Jump", "TAGame.CarComponent_Jump_TA")- , ("Archetypes.GameEvent.GameEvent_Basketball", "TAGame.GameEvent_Soccar_TA")- , ("Archetypes.GameEvent.GameEvent_BasketballPrivate", "TAGame.GameEvent_SoccarPrivate_TA")- , ("Archetypes.GameEvent.GameEvent_BasketballSplitscreen", "TAGame.GameEvent_SoccarSplitscreen_TA")- , ("Archetypes.GameEvent.GameEvent_Hockey", "TAGame.GameEvent_Soccar_TA")- , ("Archetypes.GameEvent.GameEvent_HockeyPrivate", "TAGame.GameEvent_SoccarPrivate_TA")- , ("Archetypes.GameEvent.GameEvent_HockeySplitscreen", "TAGame.GameEvent_SoccarSplitscreen_TA")- , ("Archetypes.GameEvent.GameEvent_Items", "TAGame.GameEvent_Soccar_TA")- , ("Archetypes.GameEvent.GameEvent_Season", "TAGame.GameEvent_Season_TA")- , ("Archetypes.GameEvent.GameEvent_Season:CarArchetype", "TAGame.Car_TA")- , ("Archetypes.GameEvent.GameEvent_Soccar", "TAGame.GameEvent_Soccar_TA")- , ("Archetypes.GameEvent.GameEvent_SoccarPrivate", "TAGame.GameEvent_SoccarPrivate_TA")- , ("Archetypes.GameEvent.GameEvent_SoccarSplitscreen", "TAGame.GameEvent_SoccarSplitscreen_TA")- , ("Archetypes.SpecialPickups.SpecialPickup_BallFreeze", "TAGame.SpecialPickup_BallFreeze_TA")- , ("Archetypes.SpecialPickups.SpecialPickup_BallGrapplingHook", "TAGame.SpecialPickup_GrapplingHook_TA")- , ("Archetypes.SpecialPickups.SpecialPickup_BallLasso", "TAGame.SpecialPickup_BallLasso_TA")- , ("Archetypes.SpecialPickups.SpecialPickup_BallSpring", "TAGame.SpecialPickup_BallCarSpring_TA")- , ("Archetypes.SpecialPickups.SpecialPickup_BallVelcro", "TAGame.SpecialPickup_BallVelcro_TA")- , ("Archetypes.SpecialPickups.SpecialPickup_Batarang", "TAGame.SpecialPickup_Batarang_TA")- , ("Archetypes.SpecialPickups.SpecialPickup_BoostOverride", "TAGame.SpecialPickup_BoostOverride_TA")- , ("Archetypes.SpecialPickups.SpecialPickup_CarSpring", "TAGame.SpecialPickup_BallCarSpring_TA")- , ("Archetypes.SpecialPickups.SpecialPickup_GravityWell", "TAGame.SpecialPickup_BallGravity_TA")- , ("Archetypes.SpecialPickups.SpecialPickup_StrongHit", "TAGame.SpecialPickup_HitForce_TA")- , ("Archetypes.SpecialPickups.SpecialPickup_Swapper", "TAGame.SpecialPickup_Swapper_TA")- , ("Archetypes.SpecialPickups.SpecialPickup_Tornado", "TAGame.SpecialPickup_Tornado_TA")- , ("Archetypes.Teams.Team0", "TAGame.Team_Soccar_TA")- , ("Archetypes.Teams.Team1", "TAGame.Team_Soccar_TA")- , ("GameInfo_Basketball.GameInfo.GameInfo_Basketball:GameReplicationInfoArchetype", "TAGame.GRI_TA")- , ("GameInfo_Items.GameInfo.GameInfo_Items:GameReplicationInfoArchetype", "TAGame.GRI_TA")- , ("GameInfo_Season.GameInfo.GameInfo_Season:GameReplicationInfoArchetype", "TAGame.GRI_TA")- , ("GameInfo_Soccar.GameInfo.GameInfo_Soccar:GameReplicationInfoArchetype", "TAGame.GRI_TA")- , ("Gameinfo_Hockey.GameInfo.Gameinfo_Hockey:GameReplicationInfoArchetype", "TAGame.GRI_TA")- , ("TAGame.Default__CameraSettingsActor_TA", "TAGame.CameraSettingsActor_TA")- , ("TAGame.Default__PRI_TA", "TAGame.PRI_TA")- , ("TAGame.Default__VoteActor_TA", "TAGame.VoteActor_TA")- , ("TheWorld:PersistentLevel.CrowdActor_TA", "TAGame.CrowdActor_TA")- , ("TheWorld:PersistentLevel.CrowdManager_TA", "TAGame.CrowdManager_TA")- , ("TheWorld:PersistentLevel.InMapScoreboard_TA", "TAGame.InMapScoreboard_TA")- , ("TheWorld:PersistentLevel.VehiclePickup_Boost_TA", "TAGame.VehiclePickup_Boost_TA")- ]--rawAttributeTypes :: [(String, AttributeType)]-rawAttributeTypes =- [ ("Engine.Actor:DrawScale", FloatAttributeType)- , ("Engine.Actor:Role", EnumAttributeType)- , ("Engine.Actor:bBlockActors", BooleanAttributeType)- , ("Engine.Actor:bCollideActors", BooleanAttributeType)- , ("Engine.Actor:bHidden", BooleanAttributeType)- , ("Engine.GameReplicationInfo:GameClass", FlaggedIntAttributeType)- , ("Engine.GameReplicationInfo:ServerName", StringAttributeType)- , ("Engine.GameReplicationInfo:bMatchIsOver", BooleanAttributeType)- , ("Engine.Pawn:PlayerReplicationInfo", FlaggedIntAttributeType)- , ("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.PlayerReplicationInfo:bBot", BooleanAttributeType)- , ("Engine.PlayerReplicationInfo:bIsSpectator", BooleanAttributeType)- , ("Engine.PlayerReplicationInfo:bReadyToPlay", BooleanAttributeType)- , ("Engine.PlayerReplicationInfo:bWaitingPlayer", BooleanAttributeType)- , ("Engine.TeamInfo:Score", IntAttributeType)- , ("ProjectX.GRI_X:GameServerID", QWordAttributeType)- , ("ProjectX.GRI_X:ReplicatedGameMutatorIndex", IntAttributeType)- , ("ProjectX.GRI_X:ReplicatedGamePlaylist", IntAttributeType)- , ("ProjectX.GRI_X:Reservations", ReservationAttributeType)- , ("ProjectX.GRI_X:bGameStarted", BooleanAttributeType)- , ("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:ReplicatedWorldBounceScale", FloatAttributeType)- , ("TAGame.CameraSettingsActor_TA:CameraPitch", ByteAttributeType)- , ("TAGame.CameraSettingsActor_TA:CameraYaw", ByteAttributeType)- , ("TAGame.CameraSettingsActor_TA:PRI", FlaggedIntAttributeType)- , ("TAGame.CameraSettingsActor_TA:ProfileSettings", CamSettingsAttributeType)- , ("TAGame.CameraSettingsActor_TA:bUsingBehindView", BooleanAttributeType)- , ("TAGame.CameraSettingsActor_TA:bUsingSecondaryCamera", BooleanAttributeType)- , ("TAGame.CarComponent_Boost_TA:BoostModifier", FloatAttributeType)- , ("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_Boost_TA:bNoBoost", BooleanAttributeType)- , ("TAGame.CarComponent_Boost_TA:bUnlimitedBoost", BooleanAttributeType)- , ("TAGame.CarComponent_Dodge_TA:DodgeTorque", LocationAttributeType)- , ("TAGame.CarComponent_FlipCar_TA:FlipCarTime", FloatAttributeType)- , ("TAGame.CarComponent_FlipCar_TA:bFlipRight", BooleanAttributeType)- , ("TAGame.CarComponent_TA:ReplicatedActive", ByteAttributeType)- , ("TAGame.CarComponent_TA:ReplicatedActivityTime", FloatAttributeType)- , ("TAGame.CarComponent_TA:Vehicle", FlaggedIntAttributeType)- , ("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.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.GRI_TA:NewDedicatedServerIP", StringAttributeType)- , ("TAGame.GameEvent_SoccarPrivate_TA:MatchSettings", PrivateMatchSettingsAttributeType)- , ("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_Soccar_TA:bBallHasBeenHit", BooleanAttributeType)- , ("TAGame.GameEvent_Soccar_TA:bOverTime", 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_TA:bCanVoteToForfeit", BooleanAttributeType)- , ("TAGame.GameEvent_TA:bHasLeaveMatchPenalty", BooleanAttributeType)- , ("TAGame.GameEvent_Team_TA:MaxTeamSize", IntAttributeType)- , ("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:MatchGoals", IntAttributeType)- , ("TAGame.PRI_TA:MatchSaves", IntAttributeType)- , ("TAGame.PRI_TA:MatchScore", IntAttributeType)- , ("TAGame.PRI_TA:MatchShots", IntAttributeType)- , ("TAGame.PRI_TA:PartyLeader", PartyLeaderAttributeType)- , ("TAGame.PRI_TA:PawnType", ByteAttributeType)- , ("TAGame.PRI_TA:PersistentCamera", FlaggedIntAttributeType)- , ("TAGame.PRI_TA:ReplicatedGameEvent", FlaggedIntAttributeType)- , ("TAGame.PRI_TA:Title", IntAttributeType)- , ("TAGame.PRI_TA:TotalXP", IntAttributeType)- , ("TAGame.PRI_TA:bIsInSplitScreen", BooleanAttributeType)- , ("TAGame.PRI_TA:bMatchMVP", BooleanAttributeType)- , ("TAGame.PRI_TA:bOnlineLoadoutSet", BooleanAttributeType)- , ("TAGame.PRI_TA:bOnlineLoadoutsSet", BooleanAttributeType)- , ("TAGame.PRI_TA:bReady", BooleanAttributeType)- , ("TAGame.PRI_TA:bUsingBehindView", BooleanAttributeType)- , ("TAGame.PRI_TA:bUsingSecondaryCamera", BooleanAttributeType)- , ("TAGame.RBActor_TA:ReplicatedRBState", RigidBodyStateAttributeType)- , ("TAGame.RBActor_TA:WeldedInfo", WeldedInfoAttributeType)- , ("TAGame.RBActor_TA:bFrozen", BooleanAttributeType)- , ("TAGame.RBActor_TA:bIgnoreSyncing", BooleanAttributeType)- , ("TAGame.RBActor_TA:bReplayActor", BooleanAttributeType)- , ("TAGame.SpecialPickup_BallFreeze_TA:RepOrigSpeed", FloatAttributeType)- , ("TAGame.SpecialPickup_BallVelcro_TA:AttachTime", FloatAttributeType)- , ("TAGame.SpecialPickup_BallVelcro_TA:BreakTime", FloatAttributeType)- , ("TAGame.SpecialPickup_BallVelcro_TA:bBroken", BooleanAttributeType)- , ("TAGame.SpecialPickup_BallVelcro_TA:bHit", BooleanAttributeType)- , ("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.VehiclePickup_TA:ReplicatedPickupData", PickupAttributeType)- , ("TAGame.Vehicle_TA:ReplicatedSteer", ByteAttributeType)- , ("TAGame.Vehicle_TA:ReplicatedThrottle", ByteAttributeType)- , ("TAGame.Vehicle_TA:bDriving", BooleanAttributeType)- , ("TAGame.Vehicle_TA:bReplicatedHandbrake", BooleanAttributeType)- ]--rawCrc32Table :: Integral a => [a]-rawCrc32Table =- [ 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9- , 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005- , 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61- , 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd- , 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9- , 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75- , 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011- , 0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd- , 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039- , 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5- , 0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81- , 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d- , 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49- , 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95- , 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1- , 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d- , 0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae- , 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072- , 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16- , 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca- , 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde- , 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02- , 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066- , 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba- , 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e- , 0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692- , 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6- , 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a- , 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e- , 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2- , 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686- , 0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a- , 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637- , 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb- , 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f- , 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53- , 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47- , 0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b- , 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff- , 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623- , 0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7- , 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b- , 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f- , 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3- , 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7- , 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b- , 0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f- , 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3- , 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640- , 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c- , 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8- , 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24- , 0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30- , 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec- , 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088- , 0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654- , 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0- , 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c- , 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18- , 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4- , 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0- , 0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c- , 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668- , 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4- ]
@@ -1,51 +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- , frameDelta :: Float32- , frameReplications :: [Replication]- } deriving (Eq, Ord, Show)--getFrames- :: (Int, Int)- -> Int- -> ClassAttributeMap- -> ActorMap- -> BinaryBit.BitGet ([Frame], ActorMap)-getFrames version numFrames classAttributeMap actorMap =- if numFrames <= 0- then pure ([], actorMap)- else do- (frame, newActorMap) <- getFrame version classAttributeMap actorMap- (frames, newerActorMap) <-- getFrames version (numFrames - 1) classAttributeMap newActorMap- pure (frame : frames, newerActorMap)--putFrames :: [Frame] -> BinaryBit.BitPut ()-putFrames = mapM_ putFrame--getFrame- :: (Int, Int)- -> ClassAttributeMap- -> ActorMap- -> BinaryBit.BitGet (Frame, ActorMap)-getFrame version classAttributeMap actorMap = do- time <- getFloat32Bits- delta <- getFloat32Bits- (replications, newActorMap) <-- getReplications version 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,59 +0,0 @@-module Rattletrap.Header where--import Rattletrap.Primitive-import Rattletrap.Property-import Rattletrap.PropertyValue--import qualified Data.Binary as Binary--data Header = Header- { headerEngineVersion :: Word32- , headerLicenseeVersion :: Word32- , headerLabel :: Text- , headerProperties :: Dictionary Property- } deriving (Eq, Ord, Show)--getHeader :: Binary.Get Header-getHeader = do- engineVersion <- getWord32- licenseeVersion <- getWord32- label <- getText- properties <- getDictionary getProperty- pure (Header engineVersion licenseeVersion label properties)--putHeader :: Header -> Binary.Put-putHeader header = do- putWord32 (headerEngineVersion header)- putWord32 (headerLicenseeVersion header)- putText (headerLabel header)- putDictionary putProperty (headerProperties header)--getVersion- :: (Integral a, Integral b)- => Header -> (a, b)-getVersion header =- let major = getMajorVersion header- minor = getMinorVersion header- in (major, minor)--getMajorVersion- :: (Integral a)- => Header -> a-getMajorVersion header = fromIntegral (word32Value (headerEngineVersion header))--getMinorVersion- :: (Integral a)- => Header -> a-getMinorVersion header =- fromIntegral (word32Value (headerLicenseeVersion header))--getNumFrames- :: (Integral a)- => Header -> a-getNumFrames header =- let key = stringToText "NumFrames"- properties = dictionaryValue (headerProperties header)- in case lookup key properties of- Just (Just (Property _ _ (IntProperty numFrames))) ->- fromIntegral (int32Value numFrames)- _ -> 0
@@ -1,36 +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- , initializationRotation :: Maybe Int8Vector- } 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,105 +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.Casing as Casing-import qualified Data.Aeson.TH as Aeson-import qualified Language.Haskell.TH as TH--$(let optionsFor name =- (Casing.aesonDrop (length (TH.nameBase name)) Casing.snakeCase)- { Aeson.constructorTagModifier = Casing.snakeCase- , Aeson.omitNothingFields = True- , Aeson.sumEncoding = Aeson.ObjectWithSingleField- , Aeson.unwrapUnaryRecords = True- }- deriveJSON declarations name = do- newDeclarations <- Aeson.deriveJSON (optionsFor name) name- pure (newDeclarations ++ declarations)- names =- [ ''Attribute- , ''AttributeMapping- , ''AttributeValue- , ''BooleanAttribute- , ''ByteAttribute- , ''Cache- , ''CamSettingsAttribute- , ''ClassMapping- , ''ClubColorsAttribute- , ''CompressedWord- , ''CompressedWordVector- , ''Content- , ''DemolishAttribute- , ''DestroyedReplication- , ''Dictionary- , ''EnumAttribute- , ''ExplosionAttribute- , ''FlaggedIntAttribute- , ''Float32- , ''FloatAttribute- , ''Frame- , ''GameModeAttribute- , ''Header- , ''Initialization- , ''Int32- , ''Int8- , ''Int8Vector- , ''IntAttribute- , ''KeyFrame- , ''List- , ''LoadoutAttribute- , ''LoadoutOnlineAttribute- , ''LoadoutsAttribute- , ''LoadoutsOnlineAttribute- , ''LocationAttribute- , ''Mark- , ''Message- , ''MusicStingerAttribute- , ''PartyLeaderAttribute- , ''PickupAttribute- , ''PrivateMatchSettingsAttribute- , ''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,24 +0,0 @@-module Rattletrap.KeyFrame where--import Rattletrap.Primitive--import qualified Data.Binary as Binary--data KeyFrame = KeyFrame- { keyFrameTime :: Float32- , keyFrameFrame :: Word32- , keyFramePosition :: Word32- } 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,47 +0,0 @@-module Rattletrap.Main where--import Rattletrap.Json ()-import Rattletrap.Replay-import Rattletrap.Version--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 Data.Version as Version-import qualified System.Environment as Environment--main :: IO ()-main = do- args <- Environment.getArgs- mainWithArgs args--mainWithArgs :: [String] -> IO ()-mainWithArgs args =- case args of- ["version"] -> putStrLn (Version.showVersion version)- "decode":files -> do- (getInput, putOutput) <- getIO files- input <- getInput- let replay = Binary.runGet getReplay input- let output = Aeson.encode replay- putOutput output- "encode":files -> do- (getInput, putOutput) <- getIO files- input <- getInput- case Aeson.eitherDecode input of- Left message -> fail ("could not parse JSON: " ++ message)- Right replay -> do- let output = Binary.runPut (putReplay replay)- putOutput output- _ -> fail ("unexpected arguments " ++ show args)--getIO- :: Monad m- => [FilePath] -> m (IO ByteString.ByteString, ByteString.ByteString -> IO ())-getIO files =- case files of- [] -> pure (ByteString.getContents, ByteString.putStr)- [i] -> pure (ByteString.readFile i, ByteString.putStr)- [i, o] -> pure (ByteString.readFile i, ByteString.writeFile o)- _ -> fail ("unexpected arguments " ++ show files)
@@ -1,21 +0,0 @@-module Rattletrap.Mark where--import Rattletrap.Primitive--import qualified Data.Binary as Binary--data Mark = Mark- { markValue :: Text- , markFrame :: Word32- } 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,24 +0,0 @@-module Rattletrap.Message where--import Rattletrap.Primitive--import qualified Data.Binary as Binary--data Message = Message- { messageFrame :: Word32- , messageName :: Text- , messageValue :: Text- } 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.Int8-import Rattletrap.Primitive.Int8Vector-import Rattletrap.Primitive.Int32-import Rattletrap.Primitive.List-import Rattletrap.Primitive.Section-import Rattletrap.Primitive.Text-import Rattletrap.Primitive.Vector-import Rattletrap.Primitive.Word8-import Rattletrap.Primitive.Word32-import Rattletrap.Primitive.Word64
@@ -1,59 +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,33 +0,0 @@-module Rattletrap.Primitive.Dictionary where--import Rattletrap.Primitive.Text--import qualified Data.Binary as Binary--newtype Dictionary a = Dictionary- { dictionaryValue :: [(Text, Maybe a)]- } deriving (Eq, Ord, Show)--getDictionary :: Binary.Get a -> Binary.Get (Dictionary a)-getDictionary getValue = do- key <- getText- if isNoneKey key- then pure (Dictionary [(key, Nothing)])- else do- value <- getValue- let element = (key, Just value)- Dictionary elements <- getDictionary getValue- pure (Dictionary (element : elements))--putDictionary :: (a -> Binary.Put) -> Dictionary a -> Binary.Put-putDictionary putValue dictionary =- mapM_- (\(key, maybeValue) -> do- putText key- case maybeValue of- Nothing -> pure ()- Just value -> putValue value)- (dictionaryValue dictionary)--isNoneKey :: Text -> Bool-isNoneKey text = filter (/= '\x00') (textToString text) == "None"
@@ -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,42 +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,38 +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--newtype Section a = Section- { sectionBody :: a- } deriving (Eq, Ord, Show)--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)--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,88 +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.Char8 as ByteString-import qualified Data.Text as Text-import qualified Data.Text.Encoding as Encoding--data Text = Text- { textSize :: Int32- , textValue :: Text.Text- } deriving (Eq, Ord, Show)--getText :: Binary.Get Text-getText = do- rawSize <- getInt32- let decode = getTextDecoder rawSize- let size = getTextSize rawSize- bytes <- Binary.getLazyByteString size- let text = decode bytes- pure (Text rawSize text)--putText :: Text -> Binary.Put-putText text = do- let size = textSize text- let encode = getTextEncoder size- putInt32 size- Binary.putLazyByteString (encode (textValue text))--getTextBits :: BinaryBit.BitGet Text-getTextBits = do- rawSize <- getInt32Bits- let decode = getTextDecoder rawSize- let size = getTextSize rawSize- bytes <- BinaryBit.getLazyByteString size- let text = decode (reverseBytes bytes)- pure (Text rawSize text)--putTextBits :: Text -> BinaryBit.BitPut ()-putTextBits text = do- let size = textSize text- let encode = getTextEncoder size- putInt32Bits size- BinaryBit.putByteString- (ByteString.toStrict (reverseBytes (encode (textValue text))))--stringToText :: String -> Text-stringToText string =- let value = Text.snoc (Text.pack string) '\x00'- size = Int32 (fromIntegral (Text.length value))- in Text size value--textToString :: Text -> String-textToString text = Text.unpack (Text.dropWhileEnd (== '\x00') (textValue text))--getTextSize- :: Integral a- => Int32 -> a-getTextSize 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--encodeLatin1 :: Text.Text -> ByteString.ByteString-encodeLatin1 text = ByteString.pack (Text.unpack text)
@@ -1,29 +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- { vectorBitSize :: CompressedWord- , vectorDx :: CompressedWord- , vectorDy :: CompressedWord- , vectorDz :: CompressedWord- } 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- pure (Vector bitSize dx dy dz)--putVector :: Vector -> BinaryBit.BitPut ()-putVector vector = do- putCompressedWord (vectorBitSize vector)- putCompressedWord (vectorDx vector)- putCompressedWord (vectorDy vector)- putCompressedWord (vectorDz vector)
@@ -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,25 +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- , 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,68 +0,0 @@-module Rattletrap.PropertyValue where--import Rattletrap.Primitive--import qualified Data.Binary as Binary--data PropertyValue a- = ArrayProperty (List (Dictionary a))- | BoolProperty Word8- | ByteProperty Text- (Maybe Text)- | FloatProperty Float32- | IntProperty Int32- | NameProperty Text- | 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,42 +0,0 @@-module Rattletrap.RemoteId where--import Rattletrap.Primitive--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.Word as Word--data RemoteId- = PlayStationId [Word.Word8]- | SplitscreenId [Word.Word8]- | SteamId Word64- | XboxId Word64- deriving (Eq, Ord, Show)--getRemoteId :: Word8 -> BinaryBit.BitGet RemoteId-getRemoteId systemId =- case word8Value systemId of- 0 -> do- bytes <- BinaryBit.getLazyByteString 3- pure (SplitscreenId (ByteString.unpack bytes))- 1 -> do- word64 <- getWord64Bits- pure (SteamId word64)- 2 -> do- bytes <- BinaryBit.getLazyByteString 32- pure (PlayStationId (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 bytes ->- BinaryBit.putByteString (ByteString.toStrict (ByteString.pack bytes))- SplitscreenId bytes ->- BinaryBit.putByteString (ByteString.toStrict (ByteString.pack bytes))- SteamId word64 -> putWord64Bits word64- XboxId word64 -> putWord64Bits word64
@@ -1,25 +0,0 @@-module Rattletrap.Replay where--import Rattletrap.Content-import Rattletrap.Header-import Rattletrap.Primitive.Section--import qualified Data.Binary as Binary--data Replay = Replay- { replayHeader :: Section Header- , replayContent :: Section Content- } deriving (Eq, Ord, Show)--getReplay :: Binary.Get Replay-getReplay = do- header <- getSection getHeader- let version = getVersion (sectionBody header)- let numFrames = getNumFrames (sectionBody header)- content <- getSection (getContent version numFrames)- pure (Replay header content)--putReplay :: Replay -> Binary.Put-putReplay replay = do- putSection putHeader (replayHeader replay)- putSection putContent (replayContent replay)
@@ -1,57 +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)- -> ClassAttributeMap- -> ActorMap- -> BinaryBit.BitGet ([Replication], ActorMap)-getReplications version classAttributeMap actorMap = do- maybeReplication <- getReplication version classAttributeMap actorMap- case maybeReplication of- Nothing -> pure ([], actorMap)- Just (replication, newActorMap) -> do- (replications, newerActorMap) <-- getReplications version classAttributeMap newActorMap- pure (replication : replications, newerActorMap)--putReplications :: [Replication] -> BinaryBit.BitPut ()-putReplications replications = do- mapM_ putReplication replications- BinaryBit.putBool False--getReplication- :: (Int, Int)- -> ClassAttributeMap- -> ActorMap- -> BinaryBit.BitGet (Maybe (Replication, ActorMap))-getReplication version classAttributeMap actorMap = do- hasReplication <- BinaryBit.getBool- if not hasReplication- then pure Nothing- else do- actorId <- getCompressedWord maxActorId- (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)--maxActorId :: Word-maxActorId = 1023
@@ -1,14 +0,0 @@-module Rattletrap.Replication.Destroyed where--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--data DestroyedReplication = DestroyedReplication- {- } deriving (Eq, Ord, Show)--getDestroyedReplication :: BinaryBit.BitGet DestroyedReplication-getDestroyedReplication = pure DestroyedReplication--putDestroyedReplication :: DestroyedReplication -> BinaryBit.BitPut ()-putDestroyedReplication _ = pure ()
@@ -1,57 +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--data SpawnedReplication = SpawnedReplication- { spawnedReplicationFlag :: Bool- , spawnedReplicationObjectId :: Word32- , spawnedReplication_objectName :: Text- , spawnedReplication_className :: Text- , spawnedReplicationInitialization :: Initialization- } deriving (Eq, Ord, Show)--getSpawnedReplication- :: ClassAttributeMap- -> ActorMap- -> CompressedWord- -> BinaryBit.BitGet (SpawnedReplication, ActorMap)-getSpawnedReplication classAttributeMap actorMap actorId = do- flag <- BinaryBit.getBool- objectId <- getWord32Bits- let newActorMap = updateActorMap 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 objectId objectName className initialization- , newActorMap)--putSpawnedReplication :: SpawnedReplication -> BinaryBit.BitPut ()-putSpawnedReplication spawnedReplication = do- BinaryBit.putBool (spawnedReplicationFlag spawnedReplication)- putWord32Bits (spawnedReplicationObjectId spawnedReplication)- putInitialization (spawnedReplicationInitialization spawnedReplication)--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)- -> 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,60 +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- | UpdatedReplicationValue UpdatedReplication- | DestroyedReplicationValue DestroyedReplication- deriving (Eq, Ord, Show)--getReplicationValue- :: (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 classAttributeMap actorMap actorId- pure (SpawnedReplicationValue x, newActorMap)- else do- x <- getUpdatedReplication version classAttributeMap actorMap actorId- pure (UpdatedReplicationValue x, actorMap)- else do- x <- getDestroyedReplication- pure (DestroyedReplicationValue x, actorMap)--putReplicationValue :: ReplicationValue -> BinaryBit.BitPut ()-putReplicationValue value =- case value of- SpawnedReplicationValue x -> do- BinaryBit.putBool True- BinaryBit.putBool True- putSpawnedReplication x- UpdatedReplicationValue x -> do- BinaryBit.putBool True- BinaryBit.putBool False- putUpdatedReplication x- DestroyedReplicationValue x -> do- BinaryBit.putBool False- putDestroyedReplication x
@@ -1,39 +0,0 @@-module Rattletrap.Utility where--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Bits as Bits-import qualified Data.ByteString.Lazy as ByteString-import qualified Data.Word as Word--getRemainingBits :: BinaryBit.BitGet [Bool]-getRemainingBits = do- isEmpty <- BinaryBit.isEmpty- if isEmpty- then pure []- else do- bit <- BinaryBit.getBool- bits <- getRemainingBits- pure (bit : bits)--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 bytes = ByteString.map reverseByte bytes
@@ -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
@@ -3,195 +3,196 @@ -- see: https://github.com/sol/hpack name: rattletrap-version: 1.0.0+version: 2.0.0+cabal-version: >= 1.10+build-type: Simple+license: MIT+license-file: LICENSE.markdown+maintainer: Taylor Fausak+homepage: https://github.com/tfausak/rattletrap#readme+bug-reports: https://github.com/tfausak/rattletrap/issues synopsis: Parse and generate Rocket League replays. description: Rattletrap parses and generates Rocket League replays. category: Game-homepage: https://github.com/tfausak/rattletrap#readme-bug-reports: https://github.com/tfausak/rattletrap/issues-maintainer: Taylor Fausak-license: MIT-license-file: LICENSE.markdown-build-type: Simple-cabal-version: >= 1.10 extra-source-files:- HLint.hs README.markdown- test/replays/00080014003600090000036E0F65CCEB.replay- test/replays/07E925B1423653D44CB8B4B2524792C1.replay- test/replays/0AD21FEE4A512906BEB6C98136AAF49A.replay- test/replays/1205D96C4D819800927791820096CD49.replay- test/replays/160CA83E41083BFD8E6315B4BFCA0561.replay- test/replays/18D6738D415B70B5BE4C299588D3C141.replay- test/replays/1A126AC24CAA0DB0E98835BD960B8AF8.replay- test/replays/1BC2D01444ACE577D01E988EADD4DFD0.replay- test/replays/1D1DE97D4941C86E43FE0093563DB621.replay- test/replays/1EF90FCC4F719F606A5327B3CDD782A4.replay- test/replays/1F3798E540B0C37A946561ABBB3037F9.replay- test/replays/211466D04B983F5A33CC2FA1D5928672.replay- test/replays/22BACD794ABE7B92E50E9CBDBD9C59CE.replay- test/replays/27B6A7B64553F0F685874584F96BAB1B.replay- test/replays/29F582C34A65EB34D358A784CBE3C189.replay- test/replays/372DBFCA4BDB340E4357B6BD43032802.replay- test/replays/387F059C47C09E253C875CA990EFD9F2.replay- test/replays/3EA147DD485B8DD24810689A7A989E44.replay- test/replays/4126861E477F4A03DE2A4080374D7908.replay- test/replays/42F0D8DA4FC89AE7B80FCAB7F637A8EA.replay- test/replays/504ED825482186E771FAA9B642CE5CE4.replay- test/replays/52AA67F94090C19D33C5009E54D31FE4.replay- test/replays/540DA764423C8FB24EB9D486D982F16F.replay- test/replays/551CA4D44FF2B86015DE44A6B5790D4C.replay- test/replays/6688EEE34BFEB3EC3A9E3283098CC712.replay- test/replays/6D1B06D844A5BB91B81FD4B5B28F08BA.replay- test/replays/6F7CFCD24638F8A6567AB3A8B9958A90.replay- test/replays/7109EB9846D303E54B7ACBA792036213.replay- test/replays/7BF6073F4614CE0A438994B9A260DA6A.replay- test/replays/89CBA30E46FA5385BDD35DA4285D4D2E.replay- test/replays/9704208245D7DD851F2FB2BC7DFD9AC3.replay- test/replays/98E58A904D713F2DE202358E8573265D.replay- test/replays/A128B3AB45D5A18E3EF9CF93C9576BCE.replay- test/replays/A52F804845573D8DA65E97BF59026A43.replay- test/replays/A6711CE74272B2E663DCC9A200A218E3.replay- test/replays/A7F001A1417A19BFA8C90990D8F7C2FF.replay- test/replays/B82DDB624C393A4A425E68AB40DC2450.replay- test/replays/B9F9B87D4A9D0A3D25D4EC91C0401DE2.replay- test/replays/C14F7E0E4D9B5E6BE9AD5D8ED56B174C.replay- test/replays/C8372B1345B1803DEF039F815DBD802D.replay- test/replays/D428F81646A98C25902CE988AE5C14C8.replay- test/replays/D7FB197A451D69075A0C99A2F49A4053.replay- test/replays/DCB3A6B94A9DBE46FDE5EAA9B012F6C8.replay- test/replays/EAE311E84BA35B590A6FDBA6DD4F2FEB.replay- test/replays/EAE8DADA4BB2DC5422792C9B4A67392D.replay- test/replays/F299F176491554B11E34AB91CA76B2CE.replay- test/replays/F7B9E14545C7467B89A00895980FCD73.replay- test/replays/F811C1D24888015E23B598AD8628C742.replay- test/replays/FDC79DA84DD463D4BCCE6B892829AC88.replay+ replays/00080014003600090000036E0F65CCEB.replay+ replays/07E925B1423653D44CB8B4B2524792C1.replay+ replays/0AD21FEE4A512906BEB6C98136AAF49A.replay+ replays/1205D96C4D819800927791820096CD49.replay+ replays/160CA83E41083BFD8E6315B4BFCA0561.replay+ replays/18D6738D415B70B5BE4C299588D3C141.replay+ replays/1A126AC24CAA0DB0E98835BD960B8AF8.replay+ replays/1AE415514DFC65DCBF8B8391AD35488D.replay+ replays/1BC2D01444ACE577D01E988EADD4DFD0.replay+ replays/1D1DE97D4941C86E43FE0093563DB621.replay+ replays/1EF90FCC4F719F606A5327B3CDD782A4.replay+ replays/1F3798E540B0C37A946561ABBB3037F9.replay+ replays/211466D04B983F5A33CC2FA1D5928672.replay+ replays/22BACD794ABE7B92E50E9CBDBD9C59CE.replay+ replays/27B6A7B64553F0F685874584F96BAB1B.replay+ replays/29F582C34A65EB34D358A784CBE3C189.replay+ replays/372DBFCA4BDB340E4357B6BD43032802.replay+ replays/387F059C47C09E253C875CA990EFD9F2.replay+ replays/3EA147DD485B8DD24810689A7A989E44.replay+ replays/4126861E477F4A03DE2A4080374D7908.replay+ replays/42F0D8DA4FC89AE7B80FCAB7F637A8EA.replay+ replays/504ED825482186E771FAA9B642CE5CE4.replay+ replays/52AA67F94090C19D33C5009E54D31FE4.replay+ replays/540DA764423C8FB24EB9D486D982F16F.replay+ replays/551CA4D44FF2B86015DE44A6B5790D4C.replay+ replays/6320E51C49066A7C210A2993C2201D5F.replay+ replays/6688EEE34BFEB3EC3A9E3283098CC712.replay+ replays/6D1B06D844A5BB91B81FD4B5B28F08BA.replay+ replays/6F7CFCD24638F8A6567AB3A8B9958A90.replay+ replays/7109EB9846D303E54B7ACBA792036213.replay+ replays/7BF6073F4614CE0A438994B9A260DA6A.replay+ replays/89CBA30E46FA5385BDD35DA4285D4D2E.replay+ replays/9704208245D7DD851F2FB2BC7DFD9AC3.replay+ replays/98E58A904D713F2DE202358E8573265D.replay+ replays/A128B3AB45D5A18E3EF9CF93C9576BCE.replay+ replays/A52F804845573D8DA65E97BF59026A43.replay+ replays/A6711CE74272B2E663DCC9A200A218E3.replay+ replays/A7F001A1417A19BFA8C90990D8F7C2FF.replay+ replays/B9F9B87D4A9D0A3D25D4EC91C0401DE2.replay+ replays/C14F7E0E4D9B5E6BE9AD5D8ED56B174C.replay+ replays/C8372B1345B1803DEF039F815DBD802D.replay+ replays/CC4CA70D4F7A67EBAD0ED9B9923106F7.replay+ replays/D428F81646A98C25902CE988AE5C14C8.replay+ replays/D7FB197A451D69075A0C99A2F49A4053.replay+ replays/DCB3A6B94A9DBE46FDE5EAA9B012F6C8.replay+ replays/EAE311E84BA35B590A6FDBA6DD4F2FEB.replay+ replays/EAE8DADA4BB2DC5422792C9B4A67392D.replay+ replays/F299F176491554B11E34AB91CA76B2CE.replay+ replays/F7B9E14545C7467B89A00895980FCD73.replay+ replays/F811C1D24888015E23B598AD8628C742.replay+ replays/FDC79DA84DD463D4BCCE6B892829AC88.replay source-repository head- type: git- location: https://github.com/tfausak/rattletrap+ type: git+ location: https://github.com/tfausak/rattletrap library- hs-source-dirs:- library- default-extensions: Strict- ghc-options: -Wall- build-depends:- aeson >=0.11 && <1.1- , aeson-casing >=0.1 && <0.2- , base >=4.9 && <4.10- , bimap >=0.3 && <0.4- , binary >=0.8 && <0.9- , binary-bits >=0.5 && <0.6- , bytestring >=0.10 && <0.11- , containers >=0.5 && <0.6- , data-binary-ieee754 >=0.4 && <0.5- , template-haskell >=2.11 && <2.12- , text >=1.2 && <1.3- , vector >=0.11 && <0.12- exposed-modules:- Rattletrap- Rattletrap.ActorMap- Rattletrap.Attribute- Rattletrap.Attribute.Boolean- Rattletrap.Attribute.Byte- Rattletrap.Attribute.CamSettings- Rattletrap.Attribute.ClubColors- Rattletrap.Attribute.Demolish- Rattletrap.Attribute.Enum- Rattletrap.Attribute.Explosion- Rattletrap.Attribute.FlaggedInt- Rattletrap.Attribute.Float- Rattletrap.Attribute.GameMode- Rattletrap.Attribute.Int- Rattletrap.Attribute.Loadout- Rattletrap.Attribute.LoadoutOnline- Rattletrap.Attribute.Loadouts- Rattletrap.Attribute.LoadoutsOnline- Rattletrap.Attribute.Location- Rattletrap.Attribute.MusicStinger- Rattletrap.Attribute.PartyLeader- Rattletrap.Attribute.Pickup- Rattletrap.Attribute.PrivateMatchSettings- Rattletrap.Attribute.QWord- Rattletrap.Attribute.Reservation- Rattletrap.Attribute.RigidBodyState- Rattletrap.Attribute.String- Rattletrap.Attribute.TeamPaint- Rattletrap.Attribute.UniqueId- Rattletrap.Attribute.WeldedInfo- Rattletrap.AttributeMapping- Rattletrap.AttributeType- Rattletrap.AttributeValue- Rattletrap.Cache- Rattletrap.ClassAttributeMap- Rattletrap.ClassMapping- Rattletrap.Content- Rattletrap.Crc- Rattletrap.Data- Rattletrap.Frame- Rattletrap.Header- Rattletrap.Initialization- Rattletrap.Json- Rattletrap.KeyFrame- Rattletrap.Main- Rattletrap.Mark- Rattletrap.Message- Rattletrap.Primitive- Rattletrap.Primitive.CompressedWord- Rattletrap.Primitive.CompressedWordVector- Rattletrap.Primitive.Dictionary- Rattletrap.Primitive.Float32- Rattletrap.Primitive.Int32- Rattletrap.Primitive.Int8- Rattletrap.Primitive.Int8Vector- Rattletrap.Primitive.List- Rattletrap.Primitive.Section- Rattletrap.Primitive.Text- Rattletrap.Primitive.Vector- Rattletrap.Primitive.Word32- Rattletrap.Primitive.Word64- Rattletrap.Primitive.Word8- Rattletrap.Property- Rattletrap.PropertyValue- Rattletrap.RemoteId- Rattletrap.Replay- Rattletrap.Replication- Rattletrap.Replication.Destroyed- Rattletrap.Replication.Spawned- Rattletrap.Replication.Updated- Rattletrap.ReplicationValue- Rattletrap.Utility- Rattletrap.Version- other-modules:- Paths_rattletrap- default-language: Haskell2010+ exposed-modules:+ Rattletrap+ Rattletrap.ActorMap+ Rattletrap.Attribute+ Rattletrap.Attribute.Boolean+ Rattletrap.Attribute.Byte+ Rattletrap.Attribute.CamSettings+ Rattletrap.Attribute.ClubColors+ Rattletrap.Attribute.Demolish+ Rattletrap.Attribute.Enum+ Rattletrap.Attribute.Explosion+ Rattletrap.Attribute.FlaggedInt+ Rattletrap.Attribute.Float+ Rattletrap.Attribute.GameMode+ Rattletrap.Attribute.Int+ Rattletrap.Attribute.Loadout+ Rattletrap.Attribute.LoadoutOnline+ Rattletrap.Attribute.Loadouts+ Rattletrap.Attribute.LoadoutsOnline+ Rattletrap.Attribute.Location+ Rattletrap.Attribute.MusicStinger+ Rattletrap.Attribute.PartyLeader+ Rattletrap.Attribute.Pickup+ Rattletrap.Attribute.PrivateMatchSettings+ Rattletrap.Attribute.QWord+ Rattletrap.Attribute.Reservation+ Rattletrap.Attribute.RigidBodyState+ Rattletrap.Attribute.String+ Rattletrap.Attribute.TeamPaint+ Rattletrap.Attribute.UniqueId+ Rattletrap.Attribute.WeldedInfo+ Rattletrap.AttributeMapping+ Rattletrap.AttributeType+ Rattletrap.AttributeValue+ Rattletrap.Cache+ Rattletrap.ClassAttributeMap+ Rattletrap.ClassMapping+ Rattletrap.Content+ Rattletrap.Crc+ Rattletrap.Data+ Rattletrap.Frame+ Rattletrap.Header+ Rattletrap.Initialization+ Rattletrap.Json+ Rattletrap.KeyFrame+ Rattletrap.Main+ Rattletrap.Mark+ Rattletrap.Message+ Rattletrap.Primitive+ Rattletrap.Primitive.CompressedWord+ Rattletrap.Primitive.CompressedWordVector+ Rattletrap.Primitive.Dictionary+ Rattletrap.Primitive.Float32+ Rattletrap.Primitive.Int32+ Rattletrap.Primitive.Int8+ Rattletrap.Primitive.Int8Vector+ Rattletrap.Primitive.List+ Rattletrap.Primitive.Section+ Rattletrap.Primitive.Text+ Rattletrap.Primitive.Vector+ Rattletrap.Primitive.Word32+ Rattletrap.Primitive.Word64+ Rattletrap.Primitive.Word8+ Rattletrap.Property+ Rattletrap.PropertyValue+ Rattletrap.RemoteId+ Rattletrap.Replay+ Rattletrap.Replication+ Rattletrap.Replication.Destroyed+ Rattletrap.Replication.Spawned+ Rattletrap.Replication.Updated+ Rattletrap.ReplicationValue+ Rattletrap.Utility+ Rattletrap.Version+ build-depends:+ aeson >=0.11 && <1.1,+ aeson-casing >=0.1 && <0.2,+ base >=4.9 && <4.10,+ bimap >=0.3 && <0.4,+ binary >=0.8 && <0.9,+ binary-bits >=0.5 && <0.6,+ bytestring >=0.10 && <0.11,+ containers >=0.5 && <0.6,+ data-binary-ieee754 >=0.4 && <0.5,+ template-haskell >=2.11 && <2.12,+ text >=1.2 && <1.3,+ vector >=0.11 && <0.12+ default-language: Haskell2010+ default-extensions: Strict+ hs-source-dirs:+ source/library+ other-modules:+ Paths_rattletrap+ ghc-options: -Wall executable rattletrap- main-is: Main.hs- hs-source-dirs:- executable- default-extensions: Strict- ghc-options: -Wall -rtsopts -threaded -with-rtsopts=-N- build-depends:- base- , rattletrap- default-language: Haskell2010+ main-is: Main.hs+ build-depends:+ base,+ rattletrap+ default-language: Haskell2010+ default-extensions: Strict+ hs-source-dirs:+ source/executables+ ghc-options: -Wall -rtsopts -threaded -with-rtsopts=-N test-suite test- type: exitcode-stdio-1.0- main-is: Test.hs- hs-source-dirs:- test- default-extensions: Strict- ghc-options: -Wall -rtsopts -threaded -with-rtsopts=-N- build-depends:- base- , bytestring- , filepath >=1.4 && <1.5- , rattletrap- , tasty >=0.11 && <0.12- , tasty-hspec >=1.1 && <1.2- , temporary >=1.2 && <1.3- default-language: Haskell2010+ type: exitcode-stdio-1.0+ main-is: Main.hs+ build-depends:+ base,+ bytestring,+ filepath >=1.4 && <1.5,+ rattletrap,+ tasty >=0.11 && <0.12,+ tasty-hspec >=1.1 && <1.2,+ temporary >=1.2 && <1.3+ default-language: Haskell2010+ default-extensions: Strict+ hs-source-dirs:+ source/tests+ ghc-options: -Wall -rtsopts -threaded -with-rtsopts=-N
binary file changed (absent → 94347 bytes)
binary file changed (absent → 30063 bytes)
binary file changed (absent → 27949 bytes)
binary file changed (absent → 461470 bytes)
binary file changed (absent → 565359 bytes)
binary file changed (absent → 21996 bytes)
binary file changed (absent → 98814 bytes)
binary file changed (absent → 25926 bytes)
binary file changed (absent → 22739 bytes)
binary file changed (absent → 59943 bytes)
binary file changed (absent → 737542 bytes)
binary file changed (absent → 27316 bytes)
binary file changed (absent → 38258 bytes)
binary file changed (absent → 315922 bytes)
binary file changed (absent → 25332 bytes)
binary file changed (absent → 21607 bytes)
binary file changed (absent → 28065 bytes)
binary file changed (absent → 737279 bytes)
binary file changed (absent → 596690 bytes)
binary file changed (absent → 26284 bytes)
binary file changed (absent → 116440 bytes)
binary file changed (absent → 145094 bytes)
binary file changed (absent → 570657 bytes)
binary file changed (absent → 34674 bytes)
binary file changed (absent → 754887 bytes)
binary file changed (absent → 391424 bytes)
binary file changed (absent → 1026 bytes)
binary file changed (absent → 91217 bytes)
binary file changed (absent → 29715 bytes)
binary file changed (absent → 65002 bytes)
binary file changed (absent → 22841 bytes)
binary file changed (absent → 212429 bytes)
binary file changed (absent → 36307 bytes)
binary file changed (absent → 34047 bytes)
binary file changed (absent → 743537 bytes)
binary file changed (absent → 73064 bytes)
binary file changed (absent → 221897 bytes)
binary file changed (absent → 396148 bytes)
binary file changed (absent → 25888 bytes)
binary file changed (absent → 25904 bytes)
binary file changed (absent → 23625 bytes)
binary file changed (absent → 30103 bytes)
binary file changed (absent → 476589 bytes)
binary file changed (absent → 29095 bytes)
binary file changed (absent → 1055071 bytes)
binary file changed (absent → 30151 bytes)
binary file changed (absent → 25462 bytes)
binary file changed (absent → 22732 bytes)
binary file changed (absent → 25580 bytes)
binary file changed (absent → 605 bytes)
binary file changed (absent → 783619 bytes)
@@ -0,0 +1,5 @@+module Main+ ( module Rattletrap+ ) where++import Rattletrap (main)
@@ -0,0 +1,85 @@+-- | 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.Initialization+ , module Rattletrap.KeyFrame+ , module Rattletrap.Main+ , module Rattletrap.Mark+ , module Rattletrap.Message+ , module Rattletrap.Primitive+ , module Rattletrap.Property+ , module Rattletrap.PropertyValue+ , module Rattletrap.RemoteId+ , module Rattletrap.Replay+ , module Rattletrap.Replication+ , module Rattletrap.ReplicationValue+ , module Rattletrap.Utility+ , module Rattletrap.Version+ ) where++import Rattletrap.ActorMap+import Rattletrap.Attribute+import Rattletrap.AttributeMapping+import Rattletrap.AttributeType+import Rattletrap.AttributeValue+import Rattletrap.Cache+import Rattletrap.ClassAttributeMap+import Rattletrap.ClassMapping+import Rattletrap.Content+import Rattletrap.Crc+import Rattletrap.Data+import Rattletrap.Frame+import Rattletrap.Header+import Rattletrap.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
@@ -0,0 +1,13 @@+module Rattletrap.ActorMap where++import Rattletrap.Primitive++import qualified Data.Map.Strict as Map++type ActorMap = Map.Map CompressedWord Word32++makeActorMap :: ActorMap+makeActorMap = Map.empty++updateActorMap :: CompressedWord -> Word32 -> ActorMap -> ActorMap+updateActorMap actorId objectId actorMap = Map.insert actorId objectId actorMap
@@ -0,0 +1,64 @@+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)+ -> 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)+ -> 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 name+ pure (Attribute id_ name value)++putAttribute :: Attribute -> BinaryBit.BitPut ()+putAttribute attribute = do+ BinaryBit.putBool True+ putCompressedWord (attributeId attribute)+ putAttributeValue (attributeValue attribute)
@@ -0,0 +1,17 @@+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)
@@ -0,0 +1,19 @@+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)
@@ -0,0 +1,34 @@+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+ } deriving (Eq, Ord, Show)++getCamSettingsAttribute :: BinaryBit.BitGet CamSettingsAttribute+getCamSettingsAttribute = do+ fov <- getFloat32Bits+ height <- getFloat32Bits+ angle <- getFloat32Bits+ distance <- getFloat32Bits+ stiffness <- getFloat32Bits+ swivelSpeed <- getFloat32Bits+ pure (CamSettingsAttribute fov height angle distance stiffness swivelSpeed)++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)
@@ -0,0 +1,28 @@+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)
@@ -0,0 +1,41 @@+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)
@@ -0,0 +1,18 @@+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)
@@ -0,0 +1,24 @@+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)
@@ -0,0 +1,22 @@+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)
@@ -0,0 +1,19 @@+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)
@@ -0,0 +1,25 @@+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) -> BinaryBit.BitGet GameModeAttribute+getGameModeAttribute version = do+ let numBits =+ if version < (868, 12)+ 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
@@ -0,0 +1,18 @@+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)
@@ -0,0 +1,51 @@+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+ , loadoutAttributeAntenna :: Word32+ , loadoutAttributeTopper :: Word32+ , loadoutAttributeUnknown1 :: Word32+ , loadoutAttributeUnknown2 :: 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+ g <- getWord32Bits+ h <-+ if version > Word8 10+ then do+ h <- getWord32Bits+ pure (Just h)+ else pure Nothing+ pure+ (LoadoutAttribute version body decal wheels rocketTrail antenna topper g h)++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)+ case loadoutAttributeUnknown2 loadoutAttribute of+ Nothing -> pure ()+ Just x -> putWord32Bits x
@@ -0,0 +1,39 @@+module Rattletrap.Attribute.LoadoutOnline 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++newtype LoadoutOnlineAttribute = LoadoutOnlineAttribute+ { loadoutAttributeValue :: [[(Word32, CompressedWord)]]+ } deriving (Eq, Ord, Show)++getLoadoutOnlineAttribute :: BinaryBit.BitGet LoadoutOnlineAttribute+getLoadoutOnlineAttribute = do+ size <- getWord8Bits+ values <-+ Monad.replicateM+ (fromIntegral (word8Value size))+ (do innerSize <- getWord8Bits+ Monad.replicateM+ (fromIntegral (word8Value innerSize))+ (do x <- getWord32Bits+ y <- getCompressedWord 27+ pure (x, y)))+ pure (LoadoutOnlineAttribute values)++putLoadoutOnlineAttribute :: LoadoutOnlineAttribute -> BinaryBit.BitPut ()+putLoadoutOnlineAttribute loadoutAttribute = do+ let values = loadoutAttributeValue loadoutAttribute+ putWord8Bits (Word8 (fromIntegral (length values)))+ mapM_+ (\xs -> do+ putWord8Bits (Word8 (fromIntegral (length xs)))+ mapM_+ (\(x, y) -> do+ putWord32Bits x+ putCompressedWord y)+ xs)+ values
@@ -0,0 +1,22 @@+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)
@@ -0,0 +1,30 @@+module Rattletrap.Attribute.LoadoutsOnline where++import Rattletrap.Attribute.LoadoutOnline++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++data LoadoutsOnlineAttribute = LoadoutsOnlineAttribute+ { loadoutsOnlineAttributeBlue :: LoadoutOnlineAttribute+ , loadoutsOnlineAttributeOrange :: LoadoutOnlineAttribute+ , loadoutsOnlineAttributeUnknown1 :: Bool+ , loadoutsOnlineAttributeUnknown2 :: Bool+ } deriving (Eq, Ord, Show)++getLoadoutsOnlineAttribute :: BinaryBit.BitGet LoadoutsOnlineAttribute+getLoadoutsOnlineAttribute = do+ blueLoadout <- getLoadoutOnlineAttribute+ orangeLoadout <- getLoadoutOnlineAttribute+ 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)
@@ -0,0 +1,19 @@+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)
@@ -0,0 +1,25 @@+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)
@@ -0,0 +1,33 @@+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 :: BinaryBit.BitGet PartyLeaderAttribute+getPartyLeaderAttribute = do+ systemId <- getWord8Bits+ maybeRemoteAndLocalId <-+ if systemId == Word8 0+ then pure Nothing+ else do+ remoteId <- getRemoteId 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
@@ -0,0 +1,32 @@+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)
@@ -0,0 +1,48 @@+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)
@@ -0,0 +1,19 @@+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)
@@ -0,0 +1,50 @@+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) -> BinaryBit.BitGet ReservationAttribute+getReservationAttribute version = do+ number <- getCompressedWord 7+ uniqueId <- getUniqueIdAttribute+ 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)+ 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
@@ -0,0 +1,52 @@+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
@@ -0,0 +1,19 @@+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)
@@ -0,0 +1,37 @@+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)
@@ -0,0 +1,26 @@+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 :: BinaryBit.BitGet UniqueIdAttribute+getUniqueIdAttribute = do+ systemId <- getWord8Bits+ remoteId <- getRemoteId systemId+ localId <- getWord8Bits+ pure (UniqueIdAttribute systemId remoteId localId)++putUniqueIdAttribute :: UniqueIdAttribute -> BinaryBit.BitPut ()+putUniqueIdAttribute uniqueIdAttribute = do+ putWord8Bits (uniqueIdAttributeSystemId uniqueIdAttribute)+ putRemoteId (uniqueIdAttributeRemoteId uniqueIdAttribute)+ putWord8Bits (uniqueIdAttributeLocalId uniqueIdAttribute)
@@ -0,0 +1,31 @@+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)
@@ -0,0 +1,21 @@+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)
@@ -0,0 +1,31 @@+module Rattletrap.AttributeType where++data AttributeType+ = BooleanAttributeType+ | ByteAttributeType+ | CamSettingsAttributeType+ | ClubColorsAttributeType+ | DemolishAttributeType+ | EnumAttributeType+ | ExplosionAttributeType+ | FlaggedIntAttributeType+ | FloatAttributeType+ | GameModeAttributeType+ | IntAttributeType+ | LoadoutAttributeType+ | LoadoutOnlineAttributeType+ | LoadoutsAttributeType+ | LoadoutsOnlineAttributeType+ | LocationAttributeType+ | MusicStingerAttributeType+ | PartyLeaderAttributeType+ | PickupAttributeType+ | PrivateMatchSettingsAttributeType+ | QWordAttributeType+ | ReservationAttributeType+ | RigidBodyStateAttributeType+ | StringAttributeType+ | TeamPaintAttributeType+ | UniqueIdAttributeType+ | WeldedInfoAttributeType+ deriving (Eq, Ord, Show)
@@ -0,0 +1,217 @@+module Rattletrap.AttributeValue+ ( module Rattletrap.AttributeValue+ , module Rattletrap.Attribute.Boolean+ , module Rattletrap.Attribute.Byte+ , module Rattletrap.Attribute.CamSettings+ , module Rattletrap.Attribute.ClubColors+ , module Rattletrap.Attribute.Demolish+ , module Rattletrap.Attribute.Enum+ , module Rattletrap.Attribute.Explosion+ , 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.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.Boolean+import Rattletrap.Attribute.Byte+import Rattletrap.Attribute.CamSettings+import Rattletrap.Attribute.ClubColors+import Rattletrap.Attribute.Demolish+import Rattletrap.Attribute.Enum+import Rattletrap.Attribute.Explosion+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.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+ = BooleanAttributeValue BooleanAttribute+ | ByteAttributeValue ByteAttribute+ | CamSettingsAttributeValue CamSettingsAttribute+ | ClubColorsAttributeValue ClubColorsAttribute+ | DemolishAttributeValue DemolishAttribute+ | EnumAttributeValue EnumAttribute+ | ExplosionAttributeValue ExplosionAttribute+ | 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) -> Text -> BinaryBit.BitGet AttributeValue+getAttributeValue version name =+ case Map.lookup name attributeTypes of+ Just constructor ->+ case constructor of+ BooleanAttributeType -> do+ x <- getBooleanAttribute+ pure (BooleanAttributeValue x)+ ByteAttributeType -> do+ x <- getByteAttribute+ pure (ByteAttributeValue x)+ CamSettingsAttributeType -> do+ x <- getCamSettingsAttribute+ pure (CamSettingsAttributeValue x)+ ClubColorsAttributeType -> do+ x <- getClubColorsAttribute+ pure (ClubColorsAttributeValue x)+ DemolishAttributeType -> do+ x <- getDemolishAttribute+ pure (DemolishAttributeValue x)+ EnumAttributeType -> do+ x <- getEnumAttribute+ pure (EnumAttributeValue x)+ ExplosionAttributeType -> do+ x <- getExplosionAttribute+ pure (ExplosionAttributeValue 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+ pure (LoadoutOnlineAttributeValue x)+ LoadoutsAttributeType -> do+ x <- getLoadoutsAttribute+ pure (LoadoutsAttributeValue x)+ LoadoutsOnlineAttributeType -> do+ x <- getLoadoutsOnlineAttribute+ pure (LoadoutsOnlineAttributeValue x)+ LocationAttributeType -> do+ x <- getLocationAttribute+ pure (LocationAttributeValue x)+ MusicStingerAttributeType -> do+ x <- getMusicStingerAttribute+ pure (MusicStingerAttributeValue x)+ PartyLeaderAttributeType -> do+ x <- getPartyLeaderAttribute+ 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+ 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+ BooleanAttributeValue x -> putBooleanAttribute x+ ByteAttributeValue x -> putByteAttribute x+ CamSettingsAttributeValue x -> putCamSettingsAttribute x+ ClubColorsAttributeValue x -> putClubColorsAttribute x+ DemolishAttributeValue x -> putDemolishAttribute x+ EnumAttributeValue x -> putEnumAttribute x+ ExplosionAttributeValue x -> putExplosionAttribute 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
@@ -0,0 +1,28 @@+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)
@@ -0,0 +1,292 @@+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, 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"+ 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 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
@@ -0,0 +1,21 @@+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,121 @@+module Rattletrap.Content where++import Rattletrap.ActorMap+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++-- | 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)+ -- ^ Major and minor 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+ makeActorMap+ 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)
@@ -0,0 +1,46 @@+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
@@ -0,0 +1,351 @@+module Rattletrap.Data where++import Rattletrap.AttributeType++rawParentClasses :: [(String, String)]+rawParentClasses =+ [ ("Engine.Actor", "Core.Object")+ , ("Engine.GameReplicationInfo", "Engine.ReplicationInfo")+ , ("Engine.Info", "Engine.Actor")+ , ("Engine.Pawn", "Engine.Actor")+ , ("Engine.PlayerReplicationInfo", "Engine.ReplicationInfo")+ , ("Engine.ReplicationInfo", "Engine.Info")+ , ("Engine.TeamInfo", "Engine.ReplicationInfo")+ , ("ProjectX.GRI_X", "Engine.GameReplicationInfo")+ , ("ProjectX.PRI_X", "Engine.PlayerReplicationInfo")+ , ("ProjectX.Pawn_X", "Engine.Pawn")+ , ("TAGame.Ball_TA", "TAGame.RBActor_TA")+ , ("TAGame.CameraSettingsActor_TA", "Engine.ReplicationInfo")+ , ("TAGame.CarComponent_Boost_TA", "TAGame.CarComponent_TA")+ , ("TAGame.CarComponent_Dodge_TA", "TAGame.CarComponent_TA")+ , ("TAGame.CarComponent_DoubleJump_TA", "TAGame.CarComponent_TA")+ , ("TAGame.CarComponent_FlipCar_TA", "TAGame.CarComponent_TA")+ , ("TAGame.CarComponent_Jump_TA", "TAGame.CarComponent_TA")+ , ("TAGame.CarComponent_TA", "Engine.ReplicationInfo")+ , ("TAGame.Car_Season_TA", "TAGame.PRI_TA")+ , ("TAGame.Car_TA", "TAGame.Vehicle_TA")+ , ("TAGame.CrowdActor_TA", "Engine.ReplicationInfo")+ , ("TAGame.CrowdManager_TA", "Engine.ReplicationInfo")+ , ("TAGame.GRI_TA", "ProjectX.GRI_X")+ , ("TAGame.GameEvent_Season_TA", "TAGame.GameEvent_Soccar_TA")+ , ("TAGame.GameEvent_SoccarPrivate_TA", "TAGame.GameEvent_Soccar_TA")+ , ("TAGame.GameEvent_SoccarSplitscreen_TA", "TAGame.GameEvent_SoccarPrivate_TA")+ , ("TAGame.GameEvent_Soccar_TA", "TAGame.GameEvent_Team_TA")+ , ("TAGame.GameEvent_TA", "Engine.ReplicationInfo")+ , ("TAGame.GameEvent_Team_TA", "TAGame.GameEvent_TA")+ , ("TAGame.InMapScoreboard_TA", "Engine.Actor")+ , ("TAGame.PRI_TA", "ProjectX.PRI_X")+ , ("TAGame.RBActor_TA", "ProjectX.Pawn_X")+ , ("TAGame.SpecialPickup_BallCarSpring_TA", "TAGame.SpecialPickup_Spring_TA")+ , ("TAGame.SpecialPickup_BallFreeze_TA", "TAGame.SpecialPickup_Targeted_TA")+ , ("TAGame.SpecialPickup_BallGravity_TA", "TAGame.SpecialPickup_TA")+ , ("TAGame.SpecialPickup_BallLasso_TA", "TAGame.SpecialPickup_GrapplingHook_TA")+ , ("TAGame.SpecialPickup_BallVelcro_TA", "TAGame.SpecialPickup_TA")+ , ("TAGame.SpecialPickup_Batarang_TA", "TAGame.SpecialPickup_BallLasso_TA")+ , ("TAGame.SpecialPickup_BoostOverride_TA", "TAGame.SpecialPickup_Targeted_TA")+ , ("TAGame.SpecialPickup_GrapplingHook_TA", "TAGame.SpecialPickup_Targeted_TA")+ , ("TAGame.SpecialPickup_HitForce_TA", "TAGame.SpecialPickup_TA")+ , ("TAGame.SpecialPickup_Spring_TA", "TAGame.SpecialPickup_Targeted_TA")+ , ("TAGame.SpecialPickup_Swapper_TA", "TAGame.SpecialPickup_Targeted_TA")+ , ("TAGame.SpecialPickup_TA", "TAGame.CarComponent_TA")+ , ("TAGame.SpecialPickup_Targeted_TA", "TAGame.SpecialPickup_TA")+ , ("TAGame.SpecialPickup_Tornado_TA", "TAGame.SpecialPickup_TA")+ , ("TAGame.Team_Soccar_TA", "TAGame.Team_TA")+ , ("TAGame.Team_TA", "Engine.TeamInfo")+ , ("TAGame.VehiclePickup_Boost_TA", "TAGame.VehiclePickup_TA")+ , ("TAGame.VehiclePickup_TA", "Engine.ReplicationInfo")+ , ("TAGame.Vehicle_TA", "TAGame.RBActor_TA")+ ]++rawClassesWithLocation :: [String]+rawClassesWithLocation =+ [ "TAGame.Ball_TA"+ , "TAGame.CameraSettingsActor_TA"+ , "TAGame.CarComponent_Boost_TA"+ , "TAGame.CarComponent_Dodge_TA"+ , "TAGame.CarComponent_DoubleJump_TA"+ , "TAGame.CarComponent_FlipCar_TA"+ , "TAGame.CarComponent_Jump_TA"+ , "TAGame.Car_Season_TA"+ , "TAGame.Car_TA"+ , "TAGame.GRI_TA"+ , "TAGame.GameEvent_Season_TA"+ , "TAGame.GameEvent_SoccarPrivate_TA"+ , "TAGame.GameEvent_SoccarSplitscreen_TA"+ , "TAGame.GameEvent_Soccar_TA"+ , "TAGame.PRI_TA"+ , "TAGame.SpecialPickup_BallCarSpring_TA"+ , "TAGame.SpecialPickup_BallFreeze_TA"+ , "TAGame.SpecialPickup_BallGravity_TA"+ , "TAGame.SpecialPickup_BallGravity_TA"+ , "TAGame.SpecialPickup_BallLasso_TA"+ , "TAGame.SpecialPickup_BallVelcro_TA"+ , "TAGame.SpecialPickup_Batarang_TA"+ , "TAGame.SpecialPickup_BoostOverride_TA"+ , "TAGame.SpecialPickup_GrapplingHook_TA"+ , "TAGame.SpecialPickup_HitForce_TA"+ , "TAGame.SpecialPickup_Swapper_TA"+ , "TAGame.SpecialPickup_Tornado_TA"+ , "TAGame.Team_Soccar_TA"+ ]++rawClassesWithRotation :: [String]+rawClassesWithRotation =+ [ "TAGame.Ball_TA"+ , "TAGame.Car_Season_TA"+ , "TAGame.Car_TA"+ ]++rawObjectClasses :: [(String, String)]+rawObjectClasses =+ [ ("Archetypes.Ball.Ball_Basketball", "TAGame.Ball_TA")+ , ("Archetypes.Ball.Ball_Default", "TAGame.Ball_TA")+ , ("Archetypes.Ball.Ball_Puck", "TAGame.Ball_TA")+ , ("Archetypes.Ball.CubeBall", "TAGame.Ball_TA")+ , ("Archetypes.Car.Car_Default", "TAGame.Car_TA")+ , ("Archetypes.CarComponents.CarComponent_Boost", "TAGame.CarComponent_Boost_TA")+ , ("Archetypes.CarComponents.CarComponent_Dodge", "TAGame.CarComponent_Dodge_TA")+ , ("Archetypes.CarComponents.CarComponent_DoubleJump", "TAGame.CarComponent_DoubleJump_TA")+ , ("Archetypes.CarComponents.CarComponent_FlipCar", "TAGame.CarComponent_FlipCar_TA")+ , ("Archetypes.CarComponents.CarComponent_Jump", "TAGame.CarComponent_Jump_TA")+ , ("Archetypes.GameEvent.GameEvent_Basketball", "TAGame.GameEvent_Soccar_TA")+ , ("Archetypes.GameEvent.GameEvent_BasketballPrivate", "TAGame.GameEvent_SoccarPrivate_TA")+ , ("Archetypes.GameEvent.GameEvent_BasketballSplitscreen", "TAGame.GameEvent_SoccarSplitscreen_TA")+ , ("Archetypes.GameEvent.GameEvent_Hockey", "TAGame.GameEvent_Soccar_TA")+ , ("Archetypes.GameEvent.GameEvent_HockeyPrivate", "TAGame.GameEvent_SoccarPrivate_TA")+ , ("Archetypes.GameEvent.GameEvent_HockeySplitscreen", "TAGame.GameEvent_SoccarSplitscreen_TA")+ , ("Archetypes.GameEvent.GameEvent_Items", "TAGame.GameEvent_Soccar_TA")+ , ("Archetypes.GameEvent.GameEvent_Season", "TAGame.GameEvent_Season_TA")+ , ("Archetypes.GameEvent.GameEvent_Season:CarArchetype", "TAGame.Car_TA")+ , ("Archetypes.GameEvent.GameEvent_Soccar", "TAGame.GameEvent_Soccar_TA")+ , ("Archetypes.GameEvent.GameEvent_SoccarPrivate", "TAGame.GameEvent_SoccarPrivate_TA")+ , ("Archetypes.GameEvent.GameEvent_SoccarSplitscreen", "TAGame.GameEvent_SoccarSplitscreen_TA")+ , ("Archetypes.SpecialPickups.SpecialPickup_BallFreeze", "TAGame.SpecialPickup_BallFreeze_TA")+ , ("Archetypes.SpecialPickups.SpecialPickup_BallGrapplingHook", "TAGame.SpecialPickup_GrapplingHook_TA")+ , ("Archetypes.SpecialPickups.SpecialPickup_BallLasso", "TAGame.SpecialPickup_BallLasso_TA")+ , ("Archetypes.SpecialPickups.SpecialPickup_BallSpring", "TAGame.SpecialPickup_BallCarSpring_TA")+ , ("Archetypes.SpecialPickups.SpecialPickup_BallVelcro", "TAGame.SpecialPickup_BallVelcro_TA")+ , ("Archetypes.SpecialPickups.SpecialPickup_Batarang", "TAGame.SpecialPickup_Batarang_TA")+ , ("Archetypes.SpecialPickups.SpecialPickup_BoostOverride", "TAGame.SpecialPickup_BoostOverride_TA")+ , ("Archetypes.SpecialPickups.SpecialPickup_CarSpring", "TAGame.SpecialPickup_BallCarSpring_TA")+ , ("Archetypes.SpecialPickups.SpecialPickup_GravityWell", "TAGame.SpecialPickup_BallGravity_TA")+ , ("Archetypes.SpecialPickups.SpecialPickup_StrongHit", "TAGame.SpecialPickup_HitForce_TA")+ , ("Archetypes.SpecialPickups.SpecialPickup_Swapper", "TAGame.SpecialPickup_Swapper_TA")+ , ("Archetypes.SpecialPickups.SpecialPickup_Tornado", "TAGame.SpecialPickup_Tornado_TA")+ , ("Archetypes.Teams.Team0", "TAGame.Team_Soccar_TA")+ , ("Archetypes.Teams.Team1", "TAGame.Team_Soccar_TA")+ , ("GameInfo_Basketball.GameInfo.GameInfo_Basketball:GameReplicationInfoArchetype", "TAGame.GRI_TA")+ , ("GameInfo_Items.GameInfo.GameInfo_Items:GameReplicationInfoArchetype", "TAGame.GRI_TA")+ , ("GameInfo_Season.GameInfo.GameInfo_Season:GameReplicationInfoArchetype", "TAGame.GRI_TA")+ , ("GameInfo_Soccar.GameInfo.GameInfo_Soccar:GameReplicationInfoArchetype", "TAGame.GRI_TA")+ , ("Gameinfo_Hockey.GameInfo.Gameinfo_Hockey:GameReplicationInfoArchetype", "TAGame.GRI_TA")+ , ("TAGame.Default__CameraSettingsActor_TA", "TAGame.CameraSettingsActor_TA")+ , ("TAGame.Default__PRI_TA", "TAGame.PRI_TA")+ , ("TheWorld:PersistentLevel.CrowdActor_TA", "TAGame.CrowdActor_TA")+ , ("TheWorld:PersistentLevel.CrowdManager_TA", "TAGame.CrowdManager_TA")+ , ("TheWorld:PersistentLevel.InMapScoreboard_TA", "TAGame.InMapScoreboard_TA")+ , ("TheWorld:PersistentLevel.VehiclePickup_Boost_TA", "TAGame.VehiclePickup_Boost_TA")+ ]++rawAttributeTypes :: [(String, AttributeType)]+rawAttributeTypes =+ [ ("Engine.Actor:DrawScale", FloatAttributeType)+ , ("Engine.Actor:Role", EnumAttributeType)+ , ("Engine.Actor:bBlockActors", BooleanAttributeType)+ , ("Engine.Actor:bCollideActors", BooleanAttributeType)+ , ("Engine.Actor:bHidden", BooleanAttributeType)+ , ("Engine.GameReplicationInfo:GameClass", FlaggedIntAttributeType)+ , ("Engine.GameReplicationInfo:ServerName", StringAttributeType)+ , ("Engine.GameReplicationInfo:bMatchIsOver", BooleanAttributeType)+ , ("Engine.Pawn:PlayerReplicationInfo", FlaggedIntAttributeType)+ , ("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.PlayerReplicationInfo:bBot", BooleanAttributeType)+ , ("Engine.PlayerReplicationInfo:bIsSpectator", BooleanAttributeType)+ , ("Engine.PlayerReplicationInfo:bReadyToPlay", BooleanAttributeType)+ , ("Engine.PlayerReplicationInfo:bWaitingPlayer", BooleanAttributeType)+ , ("Engine.TeamInfo:Score", IntAttributeType)+ , ("ProjectX.GRI_X:GameServerID", QWordAttributeType)+ , ("ProjectX.GRI_X:ReplicatedGameMutatorIndex", IntAttributeType)+ , ("ProjectX.GRI_X:ReplicatedGamePlaylist", IntAttributeType)+ , ("ProjectX.GRI_X:Reservations", ReservationAttributeType)+ , ("ProjectX.GRI_X:bGameStarted", BooleanAttributeType)+ , ("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:ReplicatedWorldBounceScale", FloatAttributeType)+ , ("TAGame.CameraSettingsActor_TA:CameraPitch", ByteAttributeType)+ , ("TAGame.CameraSettingsActor_TA:CameraYaw", ByteAttributeType)+ , ("TAGame.CameraSettingsActor_TA:PRI", FlaggedIntAttributeType)+ , ("TAGame.CameraSettingsActor_TA:ProfileSettings", CamSettingsAttributeType)+ , ("TAGame.CameraSettingsActor_TA:bUsingBehindView", BooleanAttributeType)+ , ("TAGame.CameraSettingsActor_TA:bUsingSecondaryCamera", BooleanAttributeType)+ , ("TAGame.CarComponent_Boost_TA:BoostModifier", FloatAttributeType)+ , ("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_Boost_TA:bNoBoost", BooleanAttributeType)+ , ("TAGame.CarComponent_Boost_TA:bUnlimitedBoost", BooleanAttributeType)+ , ("TAGame.CarComponent_Dodge_TA:DodgeTorque", LocationAttributeType)+ , ("TAGame.CarComponent_FlipCar_TA:FlipCarTime", FloatAttributeType)+ , ("TAGame.CarComponent_FlipCar_TA:bFlipRight", BooleanAttributeType)+ , ("TAGame.CarComponent_TA:ReplicatedActive", ByteAttributeType)+ , ("TAGame.CarComponent_TA:ReplicatedActivityTime", FloatAttributeType)+ , ("TAGame.CarComponent_TA:Vehicle", FlaggedIntAttributeType)+ , ("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.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.GRI_TA:NewDedicatedServerIP", StringAttributeType)+ , ("TAGame.GameEvent_SoccarPrivate_TA:MatchSettings", PrivateMatchSettingsAttributeType)+ , ("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_Soccar_TA:bBallHasBeenHit", BooleanAttributeType)+ , ("TAGame.GameEvent_Soccar_TA:bOverTime", 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_TA:bCanVoteToForfeit", BooleanAttributeType)+ , ("TAGame.GameEvent_TA:bHasLeaveMatchPenalty", BooleanAttributeType)+ , ("TAGame.GameEvent_Team_TA:bForfeit", BooleanAttributeType)+ , ("TAGame.GameEvent_Team_TA:MaxTeamSize", IntAttributeType)+ , ("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:MatchGoals", IntAttributeType)+ , ("TAGame.PRI_TA:MatchSaves", IntAttributeType)+ , ("TAGame.PRI_TA:MatchScore", IntAttributeType)+ , ("TAGame.PRI_TA:MatchShots", IntAttributeType)+ , ("TAGame.PRI_TA:PartyLeader", PartyLeaderAttributeType)+ , ("TAGame.PRI_TA:PawnType", ByteAttributeType)+ , ("TAGame.PRI_TA:PersistentCamera", FlaggedIntAttributeType)+ , ("TAGame.PRI_TA:ReplicatedGameEvent", FlaggedIntAttributeType)+ , ("TAGame.PRI_TA:Title", IntAttributeType)+ , ("TAGame.PRI_TA:TotalXP", IntAttributeType)+ , ("TAGame.PRI_TA:bIsInSplitScreen", BooleanAttributeType)+ , ("TAGame.PRI_TA:bMatchMVP", BooleanAttributeType)+ , ("TAGame.PRI_TA:bOnlineLoadoutSet", BooleanAttributeType)+ , ("TAGame.PRI_TA:bOnlineLoadoutsSet", BooleanAttributeType)+ , ("TAGame.PRI_TA:bReady", BooleanAttributeType)+ , ("TAGame.PRI_TA:bUsingBehindView", BooleanAttributeType)+ , ("TAGame.PRI_TA:bUsingSecondaryCamera", BooleanAttributeType)+ , ("TAGame.RBActor_TA:ReplicatedRBState", RigidBodyStateAttributeType)+ , ("TAGame.RBActor_TA:WeldedInfo", WeldedInfoAttributeType)+ , ("TAGame.RBActor_TA:bFrozen", BooleanAttributeType)+ , ("TAGame.RBActor_TA:bIgnoreSyncing", BooleanAttributeType)+ , ("TAGame.RBActor_TA:bReplayActor", BooleanAttributeType)+ , ("TAGame.SpecialPickup_BallFreeze_TA:RepOrigSpeed", FloatAttributeType)+ , ("TAGame.SpecialPickup_BallVelcro_TA:AttachTime", FloatAttributeType)+ , ("TAGame.SpecialPickup_BallVelcro_TA:BreakTime", FloatAttributeType)+ , ("TAGame.SpecialPickup_BallVelcro_TA:bBroken", BooleanAttributeType)+ , ("TAGame.SpecialPickup_BallVelcro_TA:bHit", BooleanAttributeType)+ , ("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.VehiclePickup_TA:ReplicatedPickupData", PickupAttributeType)+ , ("TAGame.Vehicle_TA:ReplicatedSteer", ByteAttributeType)+ , ("TAGame.Vehicle_TA:ReplicatedThrottle", ByteAttributeType)+ , ("TAGame.Vehicle_TA:bDriving", BooleanAttributeType)+ , ("TAGame.Vehicle_TA:bReplicatedHandbrake", BooleanAttributeType)+ ]++rawCrc32Table :: Integral a => [a]+rawCrc32Table =+ [ 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9+ , 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005+ , 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61+ , 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd+ , 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9+ , 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75+ , 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011+ , 0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd+ , 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039+ , 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5+ , 0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81+ , 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d+ , 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49+ , 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95+ , 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1+ , 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d+ , 0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae+ , 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072+ , 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16+ , 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca+ , 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde+ , 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02+ , 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066+ , 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba+ , 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e+ , 0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692+ , 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6+ , 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a+ , 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e+ , 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2+ , 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686+ , 0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a+ , 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637+ , 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb+ , 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f+ , 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53+ , 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47+ , 0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b+ , 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff+ , 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623+ , 0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7+ , 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b+ , 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f+ , 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3+ , 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7+ , 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b+ , 0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f+ , 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3+ , 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640+ , 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c+ , 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8+ , 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24+ , 0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30+ , 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec+ , 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088+ , 0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654+ , 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0+ , 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c+ , 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18+ , 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4+ , 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0+ , 0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c+ , 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668+ , 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4+ ]
@@ -0,0 +1,62 @@+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+ -> 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)+ -> 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)
@@ -0,0 +1,100 @@+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++-- | Contains high-level metadata about a 'Rattletrap.Replay.Replay'.+data Header = Header+ { headerEngineVersion :: Word32+ -- ^ The "major" version number.+ , headerLicenseeVersion :: Word32+ -- ^ The "minor" 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+ label <- getText+ properties <- getDictionary getProperty+ pure (Header engineVersion licenseeVersion label properties)++putHeader :: Header -> Binary.Put+putHeader header = do+ putWord32 (headerEngineVersion header)+ putWord32 (headerLicenseeVersion header)+ putText (headerLabel header)+ putDictionary putProperty (headerProperties header)++getVersion :: Header -> (Int, Int)+getVersion header =+ let major = getMajorVersion header+ minor = getMinorVersion header+ in (major, minor)++getMajorVersion :: Header -> Int+getMajorVersion header =+ fromIntegral (word32Value (headerEngineVersion header))++getMinorVersion :: Header -> Int+getMinorVersion header =+ fromIntegral (word32Value (headerLicenseeVersion 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
@@ -0,0 +1,40 @@+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
@@ -0,0 +1,105 @@+{-# 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.Casing as Casing+import qualified Data.Aeson.TH as Aeson+import qualified Language.Haskell.TH as TH++$(let optionsFor name =+ (Casing.aesonDrop (length (TH.nameBase name)) Casing.snakeCase)+ { Aeson.constructorTagModifier = Casing.snakeCase+ , Aeson.omitNothingFields = True+ , Aeson.sumEncoding = Aeson.ObjectWithSingleField+ , Aeson.unwrapUnaryRecords = True+ }+ deriveJSON declarations name = do+ newDeclarations <- Aeson.deriveJSON (optionsFor name) name+ pure (newDeclarations ++ declarations)+ names =+ [ ''Attribute+ , ''AttributeMapping+ , ''AttributeValue+ , ''BooleanAttribute+ , ''ByteAttribute+ , ''Cache+ , ''CamSettingsAttribute+ , ''ClassMapping+ , ''ClubColorsAttribute+ , ''CompressedWord+ , ''CompressedWordVector+ , ''Content+ , ''DemolishAttribute+ , ''DestroyedReplication+ , ''Dictionary+ , ''EnumAttribute+ , ''ExplosionAttribute+ , ''FlaggedIntAttribute+ , ''Float32+ , ''FloatAttribute+ , ''Frame+ , ''GameModeAttribute+ , ''Header+ , ''Initialization+ , ''Int32+ , ''Int8+ , ''Int8Vector+ , ''IntAttribute+ , ''KeyFrame+ , ''List+ , ''LoadoutAttribute+ , ''LoadoutOnlineAttribute+ , ''LoadoutsAttribute+ , ''LoadoutsOnlineAttribute+ , ''LocationAttribute+ , ''Mark+ , ''Message+ , ''MusicStingerAttribute+ , ''PartyLeaderAttribute+ , ''PickupAttribute+ , ''PrivateMatchSettingsAttribute+ , ''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)
@@ -0,0 +1,27 @@+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)
@@ -0,0 +1,58 @@+module Rattletrap.Main where++import Rattletrap.Json ()+import Rattletrap.Replay+import Rattletrap.Version++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 Data.Version as Version+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)+ "decode":files -> do+ (getInput, putOutput) <- getIO files+ input <- getInput+ let replay = Binary.runGet getReplay input+ let output = Aeson.encode replay+ putOutput output+ "encode":files -> do+ (getInput, putOutput) <- getIO files+ input <- getInput+ case Aeson.eitherDecode input of+ Left message -> fail ("could not parse JSON: " ++ message)+ Right replay -> do+ let output = Binary.runPut (putReplay replay)+ putOutput output+ _ -> fail ("unexpected arguments " ++ show args)++getIO :: [FilePath]+ -> IO (IO ByteString.ByteString, ByteString.ByteString -> IO ())+getIO files =+ case files of+ [] -> pure (ByteString.getContents, ByteString.putStr)+ [i] -> pure (ByteString.readFile i, ByteString.putStr)+ [i, o] -> pure (ByteString.readFile i, ByteString.writeFile o)+ _ -> fail ("unexpected arguments " ++ show files)
@@ -0,0 +1,23 @@+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)
@@ -0,0 +1,27 @@+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)
@@ -0,0 +1,31 @@+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
@@ -0,0 +1,59 @@+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
@@ -0,0 +1,26 @@+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)
@@ -0,0 +1,59 @@+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)
@@ -0,0 +1,33 @@+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))
@@ -0,0 +1,33 @@+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))
@@ -0,0 +1,32 @@+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))
@@ -0,0 +1,42 @@+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
@@ -0,0 +1,22 @@+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
@@ -0,0 +1,55 @@+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]
@@ -0,0 +1,108 @@+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.Char8 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 = Text.dropWhileEnd (== '\x00') text++addNull :: Text.Text -> Text.Text+addNull text =+ if Text.null text+ then text+ else Text.snoc text '\x00'
@@ -0,0 +1,40 @@+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)
@@ -0,0 +1,33 @@+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))
@@ -0,0 +1,33 @@+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))
@@ -0,0 +1,33 @@+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))
@@ -0,0 +1,26 @@+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)
@@ -0,0 +1,72 @@+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
@@ -0,0 +1,55 @@+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 :: Word8 -> BinaryBit.BitGet RemoteId+getRemoteId 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)))+ bytes <- BinaryBit.getLazyByteString 16+ 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
@@ -0,0 +1,39 @@+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)
@@ -0,0 +1,57 @@+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)+ -> 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)+ -> 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)
@@ -0,0 +1,17 @@+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 ()
@@ -0,0 +1,95 @@+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++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)+ -> ClassAttributeMap+ -> ActorMap+ -> CompressedWord+ -> BinaryBit.BitGet (SpawnedReplication, ActorMap)+getSpawnedReplication version classAttributeMap actorMap actorId = do+ flag <- BinaryBit.getBool+ nameIndex <-+ if version < (868, 14)+ then pure Nothing+ else do+ nameIndex <- getWord32Bits+ pure (Just nameIndex)+ name <- lookupName classAttributeMap nameIndex+ objectId <- getWord32Bits+ let newActorMap = updateActorMap 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
@@ -0,0 +1,27 @@+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)+ -> 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)
@@ -0,0 +1,63 @@+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)+ -> 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,33 @@+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 bytes = ByteString.map reverseByte bytes
@@ -0,0 +1,7 @@+module Rattletrap.Version where++import qualified Data.Version as Version+import qualified Paths_rattletrap as This++version :: Version.Version+version = This.version
@@ -0,0 +1,100 @@+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.Tasty as Tasty+import qualified Test.Tasty.Hspec as Hspec++main :: IO ()+main = do+ tests <- Hspec.testSpec "rattletrap" spec+ Tasty.defaultMain tests++spec :: Hspec.Spec+spec =+ Hspec.describe+ "Rattletrap"+ (mapM_ (\(uuid, description) -> itCanRoundTrip uuid description) replays)++itCanRoundTrip :: String -> String -> Hspec.Spec+itCanRoundTrip uuid description =+ Hspec.it (specName uuid description) (specBody uuid)++specName :: String -> String -> String+specName uuid description = unwords [take 4 uuid, description]++specBody :: String -> IO ()+specBody uuid = do+ let inputFile = pathToReplay uuid+ input <- ByteString.readFile inputFile+ Temp.withSystemTempDirectory+ "replay-"+ (\directory -> do+ let jsonFile = FilePath.combine directory "replay.json"+ Rattletrap.mainWithArgs ["decode", inputFile, jsonFile]+ let outputFile = FilePath.combine directory "output.replay"+ Rattletrap.mainWithArgs ["encode", jsonFile, outputFile]+ output <- ByteString.readFile outputFile+ Monad.unless+ (output == input)+ (Hspec.expectationFailure "output does not match input"))++pathToReplay :: String -> FilePath+pathToReplay uuid =+ FilePath.joinPath ["replays", FilePath.addExtension uuid ".replay"]++replays :: [(String, String)]+replays =+ [ ("00080014003600090000036E0F65CCEB", "a flip time")+ , ("07E925B1423653D44CB8B4B2524792C1", "a game mode before Neo Tokyo")+ , ("0AD21FEE4A512906BEB6C98136AAF49A", "some Latin-1 text")+ , ("1205D96C4D819800927791820096CD49", "rumble mode")+ , ("160CA83E41083BFD8E6315B4BFCA0561", "a dedicated server IP")+ , ("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")+ , ("22BACD794ABE7B92E50E9CBDBD9C59CE", "a vote to forfeit")+ , ("27B6A7B64553F0F685874584F96BAB1B", "some UTF-16 text")+ , ("29F582C34A65EB34D358A784CBE3C189", "frames")+ , ("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")+ , ("52AA67F94090C19D33C5009E54D31FE4", "a match-ending attribute")+ , ("540DA764423C8FB24EB9D486D982F16F", "a demolish attribute")+ , ("551CA4D44FF2B86015DE44A6B5790D4C", "private match settings")+ , ("6320E51C49066A7C210A2993C2201D5F", "a forfeit attribute")+ , ("6688EEE34BFEB3EC3A9E3283098CC712", "a malformed byte property")+ , ("6D1B06D844A5BB91B81FD4B5B28F08BA", "a flip right")+ , ("6F7CFCD24638F8A6567AB3A8B9958A90", "a map with numbers")+ , ("7109EB9846D303E54B7ACBA792036213", "a boost modifier")+ , ("7BF6073F4614CE0A438994B9A260DA6A", "an online loadouts attribute")+ , ("89CBA30E46FA5385BDD35DA4285D4D2E", "remote user data")+ , ("9704208245D7DD851F2FB2BC7DFD9AC3", "a batarang")+ , ("98E58A904D713F2DE202358E8573265D", "a player using behind view")+ , ("A128B3AB45D5A18E3EF9CF93C9576BCE", "a round count down")+ , ("A52F804845573D8DA65E97BF59026A43", "some more mutators")+ , ("A6711CE74272B2E663DCC9A200A218E3", "a waiting player")+ , ("A7F001A1417A19BFA8C90990D8F7C2FF", "a ready attribute")+ , ("B9F9B87D4A9D0A3D25D4EC91C0401DE2", "a party leader")+ , ("C14F7E0E4D9B5E6BE9AD5D8ED56B174C", "some mutators")+ , ("C8372B1345B1803DEF039F815DBD802D", "a spectator")+ , ("CC4CA70D4F7A67EBAD0ED9B9923106F7", "after Starbase ARC")+ , ("D428F81646A98C25902CE988AE5C14C8", "a private hockey match")+ , ("D7FB197A451D69075A0C99A2F49A4053", "an explosion attribute")+ , ("DCB3A6B94A9DBE46FDE5EAA9B012F6C8", "a pawn type 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")+ ]
@@ -1,98 +0,0 @@-import qualified Control.Monad as Monad-import qualified Data.ByteString.Lazy as ByteString-import qualified Rattletrap-import qualified System.FilePath as FilePath-import qualified System.IO.Temp as Temp-import qualified Test.Tasty as Tasty-import qualified Test.Tasty.Hspec as Hspec--main :: IO ()-main = do- tests <- Hspec.testSpec "rattletrap" spec- Tasty.defaultMain tests--spec :: Hspec.Spec-spec =- Hspec.describe- "Rattletrap"- (mapM_ (\(uuid, description) -> itCanRoundTrip uuid description) replays)--itCanRoundTrip :: String -> String -> Hspec.Spec-itCanRoundTrip uuid description =- Hspec.it (specName uuid description) (specBody uuid)--specName :: String -> String -> String-specName uuid description = unwords [take 4 uuid, description]--specBody :: String -> IO ()-specBody uuid = do- let inputFile = pathToReplay uuid- input <- ByteString.readFile inputFile- Temp.withSystemTempDirectory- "replay-"- (\directory -> do- let jsonFile = FilePath.combine directory "replay.json"- Rattletrap.mainWithArgs ["decode", inputFile, jsonFile]- let outputFile = FilePath.combine directory "output.replay"- Rattletrap.mainWithArgs ["encode", jsonFile, outputFile]- output <- ByteString.readFile outputFile- Monad.unless- (output == input)- (Hspec.expectationFailure "output does not match input"))--pathToReplay :: String -> FilePath-pathToReplay uuid =- FilePath.joinPath ["test", "replays", FilePath.addExtension uuid ".replay"]--replays :: [(String, String)]-replays =- [ ("00080014003600090000036E0F65CCEB", "a flip time")- , ("07E925B1423653D44CB8B4B2524792C1", "a game mode before Neo Tokyo")- , ("0AD21FEE4A512906BEB6C98136AAF49A", "some Latin-1 text")- , ("1205D96C4D819800927791820096CD49", "rumble mode")- , ("160CA83E41083BFD8E6315B4BFCA0561", "a dedicated server IP")- , ("18D6738D415B70B5BE4C299588D3C141", "an online loadout attribute")- , ("1A126AC24CAA0DB0E98835BD960B8AF8", "overtime")- , ("1BC2D01444ACE577D01E988EADD4DFD0", "no padding after the frames")- , ("1D1DE97D4941C86E43FE0093563DB621", "a camera pitch")- , ("1EF90FCC4F719F606A5327B3CDD782A4", "a private hoops match")- , ("1F3798E540B0C37A946561ABBB3037F9", "splitscreen players")- , ("211466D04B983F5A33CC2FA1D5928672", "a match save")- , ("22BACD794ABE7B92E50E9CBDBD9C59CE", "a vote to forfeit")- , ("27B6A7B64553F0F685874584F96BAB1B", "some UTF-16 text")- , ("29F582C34A65EB34D358A784CBE3C189", "frames")- , ("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")- , ("52AA67F94090C19D33C5009E54D31FE4", "a match-ending attribute")- , ("540DA764423C8FB24EB9D486D982F16F", "a demolish attribute")- , ("551CA4D44FF2B86015DE44A6B5790D4C", "private match settings")- , ("6688EEE34BFEB3EC3A9E3283098CC712", "a malformed byte property")- , ("6D1B06D844A5BB91B81FD4B5B28F08BA", "a flip right")- , ("6F7CFCD24638F8A6567AB3A8B9958A90", "a map with numbers")- , ("7109EB9846D303E54B7ACBA792036213", "a boost modifier")- , ("7BF6073F4614CE0A438994B9A260DA6A", "an online loadouts attribute")- , ("89CBA30E46FA5385BDD35DA4285D4D2E", "remote user data")- , ("9704208245D7DD851F2FB2BC7DFD9AC3", "a batarang")- , ("98E58A904D713F2DE202358E8573265D", "a player using behind view")- , ("A128B3AB45D5A18E3EF9CF93C9576BCE", "a round count down")- , ("A52F804845573D8DA65E97BF59026A43", "some more mutators")- , ("A6711CE74272B2E663DCC9A200A218E3", "a waiting player")- , ("A7F001A1417A19BFA8C90990D8F7C2FF", "a ready attribute")- , ("B82DDB624C393A4A425E68AB40DC2450", "a vote actor and trailing bits")- , ("B9F9B87D4A9D0A3D25D4EC91C0401DE2", "a party leader")- , ("C14F7E0E4D9B5E6BE9AD5D8ED56B174C", "some mutators")- , ("C8372B1345B1803DEF039F815DBD802D", "a spectator")- , ("D428F81646A98C25902CE988AE5C14C8", "a private hockey match")- , ("D7FB197A451D69075A0C99A2F49A4053", "an explosion attribute")- , ("DCB3A6B94A9DBE46FDE5EAA9B012F6C8", "a pawn type 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")- ]
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 (21996 → absent bytes)
binary file changed (98814 → 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 (315922 → absent bytes)
binary file changed (25332 → absent bytes)
binary file changed (21607 → 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 (570657 → absent bytes)
binary file changed (34674 → absent bytes)
binary file changed (754887 → absent bytes)
binary file changed (1026 → 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 (36307 → absent bytes)
binary file changed (34047 → absent bytes)
binary file changed (743537 → absent bytes)
binary file changed (73064 → absent bytes)
binary file changed (221897 → absent bytes)
binary file changed (396148 → absent bytes)
binary file changed (1068865 → absent bytes)
binary file changed (25888 → absent bytes)
binary file changed (25904 → absent bytes)
binary file changed (23625 → absent bytes)
binary file changed (476589 → absent bytes)
binary file changed (29095 → absent bytes)
binary file changed (1055071 → 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)