rattletrap (empty) → 0.1.0
raw patch · 75 files changed
+3565/−0 lines, 75 filesdep +aesondep +aeson-casingdep +basesetup-changed
Dependencies added: aeson, aeson-casing, base, bimap, binary, binary-bits, bytestring, containers, data-binary-ieee754, filepath, hlint, rattletrap, regex-compat, tasty, tasty-hspec, template-haskell, text, vector
Files
- HLint.hs +10/−0
- LICENSE.markdown +23/−0
- README.markdown +66/−0
- Setup.hs +8/−0
- executable/Main.hs +121/−0
- library/Rattletrap.hs +41/−0
- library/Rattletrap/ActorMap.hs +14/−0
- library/Rattletrap/Attribute.hs +57/−0
- library/Rattletrap/AttributeMapping.hs +23/−0
- library/Rattletrap/AttributeValue.hs +187/−0
- library/Rattletrap/AttributeValue/Boolean.hs +17/−0
- library/Rattletrap/AttributeValue/Byte.hs +19/−0
- library/Rattletrap/AttributeValue/CamSettings.hs +36/−0
- library/Rattletrap/AttributeValue/Demolish.hs +42/−0
- library/Rattletrap/AttributeValue/Enum.hs +18/−0
- library/Rattletrap/AttributeValue/Explosion.hs +25/−0
- library/Rattletrap/AttributeValue/FlaggedInt.hs +22/−0
- library/Rattletrap/AttributeValue/Float.hs +19/−0
- library/Rattletrap/AttributeValue/GameMode.hs +26/−0
- library/Rattletrap/AttributeValue/Int.hs +19/−0
- library/Rattletrap/AttributeValue/Loadout.hs +61/−0
- library/Rattletrap/AttributeValue/LoadoutOnline.hs +42/−0
- library/Rattletrap/AttributeValue/Loadouts.hs +22/−0
- library/Rattletrap/AttributeValue/LoadoutsOnline.hs +34/−0
- library/Rattletrap/AttributeValue/Location.hs +19/−0
- library/Rattletrap/AttributeValue/MusicStinger.hs +27/−0
- library/Rattletrap/AttributeValue/PartyLeader.hs +33/−0
- library/Rattletrap/AttributeValue/Pickup.hs +32/−0
- library/Rattletrap/AttributeValue/PrivateMatchSettings.hs +54/−0
- library/Rattletrap/AttributeValue/QWord.hs +19/−0
- library/Rattletrap/AttributeValue/Reservation.hs +56/−0
- library/Rattletrap/AttributeValue/RigidBodyState.hs +55/−0
- library/Rattletrap/AttributeValue/String.hs +19/−0
- library/Rattletrap/AttributeValue/TeamPaint.hs +38/−0
- library/Rattletrap/AttributeValue/UniqueId.hs +26/−0
- library/Rattletrap/AttributeValue/WeldedInfo.hs +34/−0
- library/Rattletrap/AttributeValueType.hs +30/−0
- library/Rattletrap/Cache.hs +35/−0
- library/Rattletrap/ClassAttributeMap.hs +247/−0
- library/Rattletrap/ClassMapping.hs +22/−0
- library/Rattletrap/CompressedWord.hs +59/−0
- library/Rattletrap/CompressedWordVector.hs +31/−0
- library/Rattletrap/Content.hs +86/−0
- library/Rattletrap/Crc.hs +37/−0
- library/Rattletrap/Data.hs +349/−0
- library/Rattletrap/Dictionary.hs +33/−0
- library/Rattletrap/Float32.hs +33/−0
- library/Rattletrap/Frame.hs +61/−0
- library/Rattletrap/Header.hs +36/−0
- library/Rattletrap/Initialization.hs +39/−0
- library/Rattletrap/Int32.hs +33/−0
- library/Rattletrap/Int8.hs +32/−0
- library/Rattletrap/Int8Vector.hs +42/−0
- library/Rattletrap/KeyFrame.hs +27/−0
- library/Rattletrap/List.hs +21/−0
- library/Rattletrap/Mark.hs +22/−0
- library/Rattletrap/Message.hs +25/−0
- library/Rattletrap/Property.hs +27/−0
- library/Rattletrap/PropertyValue.hs +74/−0
- library/Rattletrap/RemoteId.hs +43/−0
- library/Rattletrap/Replay.hs +55/−0
- library/Rattletrap/Replication.hs +61/−0
- library/Rattletrap/ReplicationValue.hs +64/−0
- library/Rattletrap/ReplicationValue/Destroyed.hs +14/−0
- library/Rattletrap/ReplicationValue/Spawned.hs +45/−0
- library/Rattletrap/ReplicationValue/Updated.hs +27/−0
- library/Rattletrap/Text.hs +87/−0
- library/Rattletrap/Utility.hs +33/−0
- library/Rattletrap/Vector.hs +31/−0
- library/Rattletrap/Word32.hs +33/−0
- library/Rattletrap/Word64.hs +33/−0
- library/Rattletrap/Word8.hs +33/−0
- rattletrap.cabal +140/−0
- test/Lint.hs +13/−0
- test/Test.hs +88/−0
+ HLint.hs view
@@ -0,0 +1,10 @@+module HLint+ (+ ) where++import "hint" HLint.Default+import "hint" HLint.Dollar++ignore "Use &&&"++ignore "Use <|>"
+ LICENSE.markdown view
@@ -0,0 +1,23 @@+# [The MIT License (MIT)][]++Copyright (c) 2016 Taylor Fausak <taylor@fausak.me>++Permission is hereby granted, free of charge, to any person obtaining a copy of+this software and associated documentation files (the "Software"), to deal in+the Software without restriction, including without limitation the rights to+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies+of the Software, and to permit persons to whom the Software is furnished to do+so, subject to the following conditions:++The above copyright notice and this permission notice shall be included in all+copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+SOFTWARE.++[The MIT License (MIT)]: http://opensource.org/licenses/MIT
+ README.markdown view
@@ -0,0 +1,66 @@+# [Rattletrap][]++[![Windows build badge][]][windows build]+[![Build badge][]][build]++Rattletrap parses and generates [Rocket League][] replays.++## Install++Rattletrap does not yet offer compiled binaries for download. To install it,+first install [Stack][]. Then download or clone this repository and run+`stack --install-ghc install`.++## Parse++Rattletrap can parse (decode) Rocket League replays and output them as JSON.++``` sh+> rattletrap decode input.replay output.json+# or+> rattletrap decode input.replay > output.json+# or+> rattletrap.decode < input.replay > output.json+```++The resulting JSON is minified, but extremely large. A 4.3 MB replay file turns+into a 123 MB JSON file. To easily view the file, use a JSON pretty printer and+a pager. For example:++``` sh+> cat output.json | python -m json.tool | less+```++## Generate++Rattletrap can also generate (encode) Rocket League replays from JSON files.++``` sh+> rattletrap encode input.json output.replay+# or+> rattletrap encode input.json > output.replay+# or+> rattletrap.encode < input.json > output.replay+```++If the JSON was generated by Rattletrap, the resulting replay should be+identical to the original.++## Modify++By inserting another program between parsing and generating, Rattletrap can be+used to modify replays.++``` sh+> rattletrap decode < original.replay |+ modify-replay-json |+ rattletrap encode > modified.replay+```++[Rattletrap]: https://github.com/tfausak/rattletrap+[Windows build badge]: https://ci.appveyor.com/api/projects/status/github/tfausak/rattletrap?branch=master&svg=true+[windows build]: https://ci.appveyor.com/project/TaylorFausak/rattletrap+[Build badge]: https://travis-ci.org/tfausak/rattletrap.svg?branch=master+[build]: https://travis-ci.org/tfausak/rattletrap+[Rocket League]: https://www.rocketleaguegame.com+[Stack]: https://docs.haskellstack.org/en/stable/README/
+ Setup.hs view
@@ -0,0 +1,8 @@+module Main+ ( main+ ) where++import qualified Distribution.Simple++main :: IO ()+main = Distribution.Simple.defaultMain
+ executable/Main.hs view
@@ -0,0 +1,121 @@+{-# LANGUAGE TemplateHaskell #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}++module Main+ ( main+ ) where++import Rattletrap++import qualified Control.Monad as Monad+import qualified Data.Aeson as Aeson+import qualified Data.Aeson.Casing as Casing+import qualified Data.Aeson.TH 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 Language.Haskell.TH as TH+import qualified System.Environment as Environment++main :: IO ()+main = do+ args <- Environment.getArgs+ mainWithArgs args++mainWithArgs :: [String] -> IO ()+mainWithArgs args =+ case args of+ "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)++$(Monad.foldM+ (\declarations name -> do+ let options =+ Casing.aesonDrop (length (TH.nameBase name)) Casing.snakeCase+ newDeclarations <- Aeson.deriveJSON options name+ pure (newDeclarations ++ declarations))+ []+ [ ''Attribute+ , ''AttributeMapping+ , ''AttributeValue+ , ''BooleanAttributeValue+ , ''ByteAttributeValue+ , ''Cache+ , ''CamSettingsAttributeValue+ , ''ClassMapping+ , ''CompressedWord+ , ''CompressedWordVector+ , ''Content+ , ''DemolishAttributeValue+ , ''DestroyedReplicationValue+ , ''Dictionary+ , ''EnumAttributeValue+ , ''ExplosionAttributeValue+ , ''FlaggedIntAttributeValue+ , ''Float32+ , ''FloatAttributeValue+ , ''Frame+ , ''GameModeAttributeValue+ , ''Header+ , ''Initialization+ , ''Int32+ , ''Int8+ , ''Int8Vector+ , ''IntAttributeValue+ , ''KeyFrame+ , ''List+ , ''LoadoutAttributeValue+ , ''LoadoutOnlineAttributeValue+ , ''LoadoutsAttributeValue+ , ''LoadoutsOnlineAttributeValue+ , ''LocationAttributeValue+ , ''Mark+ , ''Message+ , ''MusicStingerAttributeValue+ , ''PartyLeaderAttributeValue+ , ''PickupAttributeValue+ , ''PrivateMatchSettingsAttributeValue+ , ''Property+ , ''PropertyValue+ , ''QWordAttributeValue+ , ''RemoteId+ , ''Replay+ , ''Replication+ , ''ReplicationValue+ , ''ReservationAttributeValue+ , ''RigidBodyStateAttributeValue+ , ''SpawnedReplicationValue+ , ''StringAttributeValue+ , ''TeamPaintAttributeValue+ , ''Text+ , ''UniqueIdAttributeValue+ , ''UpdatedReplicationValue+ , ''Vector+ , ''WeldedInfoAttributeValue+ , ''Word32+ , ''Word64+ , ''Word8+ ])
+ library/Rattletrap.hs view
@@ -0,0 +1,41 @@+module Rattletrap+ ( module Export+ ) where++import Rattletrap.ActorMap as Export+import Rattletrap.Attribute as Export+import Rattletrap.AttributeMapping as Export+import Rattletrap.AttributeValue as Export+import Rattletrap.AttributeValueType as Export+import Rattletrap.Cache as Export+import Rattletrap.ClassAttributeMap as Export+import Rattletrap.ClassMapping as Export+import Rattletrap.CompressedWord as Export+import Rattletrap.CompressedWordVector as Export+import Rattletrap.Content as Export+import Rattletrap.Crc as Export+import Rattletrap.Data as Export+import Rattletrap.Dictionary as Export+import Rattletrap.Float32 as Export+import Rattletrap.Frame as Export+import Rattletrap.Header as Export+import Rattletrap.Initialization as Export+import Rattletrap.Int32 as Export+import Rattletrap.Int8 as Export+import Rattletrap.Int8Vector as Export+import Rattletrap.KeyFrame as Export+import Rattletrap.List as Export+import Rattletrap.Mark as Export+import Rattletrap.Message as Export+import Rattletrap.Property as Export+import Rattletrap.PropertyValue as Export+import Rattletrap.RemoteId as Export+import Rattletrap.Replay as Export+import Rattletrap.Replication as Export+import Rattletrap.ReplicationValue as Export+import Rattletrap.Text as Export+import Rattletrap.Utility as Export+import Rattletrap.Vector as Export+import Rattletrap.Word32 as Export+import Rattletrap.Word64 as Export+import Rattletrap.Word8 as Export
+ library/Rattletrap/ActorMap.hs view
@@ -0,0 +1,14 @@+module Rattletrap.ActorMap where++import Rattletrap.CompressedWord+import Rattletrap.Word32++import qualified Data.Map as Map++type ActorMap = Map.Map CompressedWord Word32++makeActorMap :: ActorMap+makeActorMap = Map.empty++updateActorMap :: CompressedWord -> Word32 -> ActorMap -> ActorMap+updateActorMap = Map.insert
+ library/Rattletrap/Attribute.hs view
@@ -0,0 +1,57 @@+module Rattletrap.Attribute where++import Rattletrap.ActorMap+import Rattletrap.AttributeValue+import Rattletrap.ClassAttributeMap+import Rattletrap.CompressedWord++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++data Attribute = Attribute+ { attributeId :: CompressedWord+ , 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 getAttributeIdLimit classAttributeMap actorMap actorId of+ Nothing -> fail ("could not get attribute ID limit for " ++ show actorId)+ Just limit -> do+ id_ <- getCompressedWord limit+ case getAttributeName classAttributeMap actorMap actorId id_ of+ Nothing -> fail ("could not get attribute name for " ++ show id_)+ Just name -> do+ value <- getAttributeValue version name+ pure Attribute {attributeId = id_, attributeValue = value}++putAttribute :: Attribute -> BinaryBit.BitPut ()+putAttribute attribute = do+ BinaryBit.putBool True+ putCompressedWord (attributeId attribute)+ putAttributeValue (attributeValue attribute)
+ library/Rattletrap/AttributeMapping.hs view
@@ -0,0 +1,23 @@+module Rattletrap.AttributeMapping where++import Rattletrap.Word32++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+ {attributeMappingObjectId = objectId, attributeMappingStreamId = streamId}++putAttributeMapping :: AttributeMapping -> Binary.Put+putAttributeMapping attributeMapping = do+ putWord32 (attributeMappingObjectId attributeMapping)+ putWord32 (attributeMappingStreamId attributeMapping)
+ library/Rattletrap/AttributeValue.hs view
@@ -0,0 +1,187 @@+module Rattletrap.AttributeValue+ ( module Rattletrap.AttributeValue+ , module Export+ ) where++import Rattletrap.AttributeValue.Boolean as Export+import Rattletrap.AttributeValue.Byte as Export+import Rattletrap.AttributeValue.CamSettings as Export+import Rattletrap.AttributeValue.Demolish as Export+import Rattletrap.AttributeValue.Enum as Export+import Rattletrap.AttributeValue.Explosion as Export+import Rattletrap.AttributeValue.FlaggedInt as Export+import Rattletrap.AttributeValue.Float as Export+import Rattletrap.AttributeValue.GameMode as Export+import Rattletrap.AttributeValue.Int as Export+import Rattletrap.AttributeValue.Loadout as Export+import Rattletrap.AttributeValue.LoadoutOnline as Export+import Rattletrap.AttributeValue.Loadouts as Export+import Rattletrap.AttributeValue.LoadoutsOnline as Export+import Rattletrap.AttributeValue.Location as Export+import Rattletrap.AttributeValue.MusicStinger as Export+import Rattletrap.AttributeValue.PartyLeader as Export+import Rattletrap.AttributeValue.Pickup as Export+import Rattletrap.AttributeValue.PrivateMatchSettings as Export+import Rattletrap.AttributeValue.QWord as Export+import Rattletrap.AttributeValue.Reservation as Export+import Rattletrap.AttributeValue.RigidBodyState as Export+import Rattletrap.AttributeValue.String as Export+import Rattletrap.AttributeValue.TeamPaint as Export+import Rattletrap.AttributeValue.UniqueId as Export+import Rattletrap.AttributeValue.WeldedInfo as Export++import Rattletrap.AttributeValueType+import Rattletrap.Data+import Rattletrap.Text++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit+import qualified Data.Map as Map++data AttributeValue+ = BooleanAttribute BooleanAttributeValue+ | ByteAttribute ByteAttributeValue+ | CamSettingsAttribute CamSettingsAttributeValue+ | DemolishAttribute DemolishAttributeValue+ | EnumAttribute EnumAttributeValue+ | ExplosionAttribute ExplosionAttributeValue+ | FlaggedIntAttribute FlaggedIntAttributeValue+ | FloatAttribute FloatAttributeValue+ | GameModeAttribute GameModeAttributeValue+ | IntAttribute IntAttributeValue+ | LoadoutAttribute LoadoutAttributeValue+ | LoadoutOnlineAttribute LoadoutOnlineAttributeValue+ | LoadoutsAttribute LoadoutsAttributeValue+ | LoadoutsOnlineAttribute LoadoutsOnlineAttributeValue+ | LocationAttribute LocationAttributeValue+ | MusicStingerAttribute MusicStingerAttributeValue+ | PartyLeaderAttribute PartyLeaderAttributeValue+ | PickupAttribute PickupAttributeValue+ | PrivateMatchSettingsAttribute PrivateMatchSettingsAttributeValue+ | QWordAttribute QWordAttributeValue+ | ReservationAttribute ReservationAttributeValue+ | RigidBodyStateAttribute RigidBodyStateAttributeValue+ | StringAttribute StringAttributeValue+ | TeamPaintAttribute TeamPaintAttributeValue+ | UniqueIdAttribute UniqueIdAttributeValue+ | WeldedInfoAttribute WeldedInfoAttributeValue+ deriving (Eq, Ord, Show)++getAttributeValue :: (Int, Int) -> Text -> BinaryBit.BitGet AttributeValue+getAttributeValue version name =+ case Map.lookup name attributeValueTypes of+ Just constructor ->+ case constructor of+ AVBoolean -> do+ x <- getBooleanAttributeValue+ pure (BooleanAttribute x)+ AVByte -> do+ x <- getByteAttributeValue+ pure (ByteAttribute x)+ AVCamSettings -> do+ x <- getCamSettingsAttributeValue+ pure (CamSettingsAttribute x)+ AVDemolish -> do+ x <- getDemolishAttributeValue+ pure (DemolishAttribute x)+ AVEnum -> do+ x <- getEnumAttributeValue+ pure (EnumAttribute x)+ AVExplosion -> do+ x <- getExplosionAttributeValue+ pure (ExplosionAttribute x)+ AVFlaggedInt -> do+ x <- getFlaggedIntAttributeValue+ pure (FlaggedIntAttribute x)+ AVFloat -> do+ x <- getFloatAttributeValue+ pure (FloatAttribute x)+ AVGameMode -> do+ x <- getGameModeAttributeValue version+ pure (GameModeAttribute x)+ AVInt -> do+ x <- getIntAttributeValue+ pure (IntAttribute x)+ AVLoadout -> do+ x <- getLoadoutAttributeValue+ pure (LoadoutAttribute x)+ AVLoadoutOnline -> do+ x <- getLoadoutOnlineAttributeValue+ pure (LoadoutOnlineAttribute x)+ AVLoadouts -> do+ x <- getLoadoutsAttributeValue+ pure (LoadoutsAttribute x)+ AVLoadoutsOnline -> do+ x <- getLoadoutsOnlineAttributeValue+ pure (LoadoutsOnlineAttribute x)+ AVLocation -> do+ x <- getLocationAttributeValue+ pure (LocationAttribute x)+ AVMusicStinger -> do+ x <- getMusicStingerAttributeValue+ pure (MusicStingerAttribute x)+ AVPartyLeader -> do+ x <- getPartyLeaderAttributeValue+ pure (PartyLeaderAttribute x)+ AVPickup -> do+ x <- getPickupAttributeValue+ pure (PickupAttribute x)+ AVPrivateMatchSettings -> do+ x <- getPrivateMatchSettingsAttributeValue+ pure (PrivateMatchSettingsAttribute x)+ AVQWord -> do+ x <- getQWordAttributeValue+ pure (QWordAttribute x)+ AVReservation -> do+ x <- getReservationAttributeValue version+ pure (ReservationAttribute x)+ AVRigidBodyState -> do+ x <- getRigidBodyStateAttributeValue+ pure (RigidBodyStateAttribute x)+ AVString -> do+ x <- getStringAttributeValue+ pure (StringAttribute x)+ AVTeamPaint -> do+ x <- getTeamPaintAttributeValue+ pure (TeamPaintAttribute x)+ AVUniqueId -> do+ x <- getUniqueIdAttributeValue+ pure (UniqueIdAttribute x)+ AVWeldedInfo -> do+ x <- getWeldedInfoAttributeValue+ pure (WeldedInfoAttribute x)+ Nothing -> fail ("don't know how to get attribute value " ++ show name)++attributeValueTypes :: Map.Map Text AttributeValueType+attributeValueTypes =+ Map.mapKeys stringToText (Map.fromList rawAttributeValueTypes)++putAttributeValue :: AttributeValue -> BinaryBit.BitPut ()+putAttributeValue value =+ case value of+ BooleanAttribute x -> putBooleanAttributeValue x+ ByteAttribute x -> putByteAttributeValue x+ CamSettingsAttribute x -> putCamSettingsAttributeValue x+ DemolishAttribute x -> putDemolishAttributeValue x+ EnumAttribute x -> putEnumAttributeValue x+ ExplosionAttribute x -> putExplosionAttributeValue x+ FlaggedIntAttribute x -> putFlaggedIntAttributeValue x+ FloatAttribute x -> putFloatAttributeValue x+ GameModeAttribute x -> putGameModeAttributeValue x+ IntAttribute x -> putIntAttributeValue x+ LoadoutAttribute x -> putLoadoutAttributeValue x+ LoadoutOnlineAttribute x -> putLoadoutOnlineAttributeValue x+ LoadoutsAttribute x -> putLoadoutsAttributeValue x+ LoadoutsOnlineAttribute x -> putLoadoutsOnlineAttributeValue x+ LocationAttribute x -> putLocationAttributeValue x+ MusicStingerAttribute x -> putMusicStingerAttributeValue x+ PartyLeaderAttribute x -> putPartyLeaderAttributeValue x+ PickupAttribute x -> putPickupAttributeValue x+ PrivateMatchSettingsAttribute x -> putPrivateMatchSettingsAttributeValue x+ QWordAttribute x -> putQWordAttributeValue x+ ReservationAttribute x -> putReservationAttributeValue x+ RigidBodyStateAttribute x -> putRigidBodyStateAttributeValue x+ StringAttribute x -> putStringAttributeValue x+ TeamPaintAttribute x -> putTeamPaintAttributeValue x+ UniqueIdAttribute x -> putUniqueIdAttributeValue x+ WeldedInfoAttribute x -> putWeldedInfoAttributeValue x
+ library/Rattletrap/AttributeValue/Boolean.hs view
@@ -0,0 +1,17 @@+module Rattletrap.AttributeValue.Boolean where++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++newtype BooleanAttributeValue = BooleanAttributeValue+ { booleanAttributeValueValue :: Bool+ } deriving (Eq, Ord, Show)++getBooleanAttributeValue :: BinaryBit.BitGet BooleanAttributeValue+getBooleanAttributeValue = do+ value <- BinaryBit.getBool+ pure (BooleanAttributeValue value)++putBooleanAttributeValue :: BooleanAttributeValue -> BinaryBit.BitPut ()+putBooleanAttributeValue booleanAttributeValue =+ BinaryBit.putBool (booleanAttributeValueValue booleanAttributeValue)
+ library/Rattletrap/AttributeValue/Byte.hs view
@@ -0,0 +1,19 @@+module Rattletrap.AttributeValue.Byte where++import Rattletrap.Word8++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++newtype ByteAttributeValue = ByteAttributeValue+ { byteAttributeValueValue :: Word8+ } deriving (Eq, Ord, Show)++getByteAttributeValue :: BinaryBit.BitGet ByteAttributeValue+getByteAttributeValue = do+ value <- getWord8Bits+ pure (ByteAttributeValue value)++putByteAttributeValue :: ByteAttributeValue -> BinaryBit.BitPut ()+putByteAttributeValue byteAttributeValue =+ putWord8Bits (byteAttributeValueValue byteAttributeValue)
+ library/Rattletrap/AttributeValue/CamSettings.hs view
@@ -0,0 +1,36 @@+module Rattletrap.AttributeValue.CamSettings where++import Rattletrap.Float32++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++data CamSettingsAttributeValue = CamSettingsAttributeValue+ { camSettingsAttributeValueFov :: Float32+ , camSettingsAttributeValueHeight :: Float32+ , camSettingsAttributeValueAngle :: Float32+ , camSettingsAttributeValueDistance :: Float32+ , camSettingsAttributeValueStiffness :: Float32+ , camSettingsAttributeValueSwivelSpeed :: Float32+ } deriving (Eq, Ord, Show)++getCamSettingsAttributeValue :: BinaryBit.BitGet CamSettingsAttributeValue+getCamSettingsAttributeValue = do+ fov <- getFloat32Bits+ height <- getFloat32Bits+ angle <- getFloat32Bits+ distance <- getFloat32Bits+ stiffness <- getFloat32Bits+ swivelSpeed <- getFloat32Bits+ pure+ (CamSettingsAttributeValue fov height angle distance stiffness swivelSpeed)++putCamSettingsAttributeValue :: CamSettingsAttributeValue -> BinaryBit.BitPut ()+putCamSettingsAttributeValue camSettingsAttributeValue = do+ putFloat32Bits (camSettingsAttributeValueFov camSettingsAttributeValue)+ putFloat32Bits (camSettingsAttributeValueHeight camSettingsAttributeValue)+ putFloat32Bits (camSettingsAttributeValueAngle camSettingsAttributeValue)+ putFloat32Bits (camSettingsAttributeValueDistance camSettingsAttributeValue)+ putFloat32Bits (camSettingsAttributeValueStiffness camSettingsAttributeValue)+ putFloat32Bits+ (camSettingsAttributeValueSwivelSpeed camSettingsAttributeValue)
+ library/Rattletrap/AttributeValue/Demolish.hs view
@@ -0,0 +1,42 @@+module Rattletrap.AttributeValue.Demolish where++import Rattletrap.Vector+import Rattletrap.Word32++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++data DemolishAttributeValue = DemolishAttributeValue+ { demolishAttributeValueAttackerFlag :: Bool+ , demolishAttributeValueAttackerActorId :: Word32+ , demolishAttributeValueVictimFlag :: Bool+ , demolishAttributeValueVictimActorId :: Word32+ , demolishAttributeValueAttackerVelocity :: Vector+ , demolishAttributeValueVictimVelocity :: Vector+ } deriving (Eq, Ord, Show)++getDemolishAttributeValue :: BinaryBit.BitGet DemolishAttributeValue+getDemolishAttributeValue = do+ attackerFlag <- BinaryBit.getBool+ attackerActorId <- getWord32Bits+ victimFlag <- BinaryBit.getBool+ victimActorId <- getWord32Bits+ attackerVelocity <- getVector+ victimVelocity <- getVector+ pure+ (DemolishAttributeValue+ attackerFlag+ attackerActorId+ victimFlag+ victimActorId+ attackerVelocity+ victimVelocity)++putDemolishAttributeValue :: DemolishAttributeValue -> BinaryBit.BitPut ()+putDemolishAttributeValue demolishAttributeValue = do+ BinaryBit.putBool (demolishAttributeValueAttackerFlag demolishAttributeValue)+ putWord32Bits (demolishAttributeValueAttackerActorId demolishAttributeValue)+ BinaryBit.putBool (demolishAttributeValueVictimFlag demolishAttributeValue)+ putWord32Bits (demolishAttributeValueVictimActorId demolishAttributeValue)+ putVector (demolishAttributeValueAttackerVelocity demolishAttributeValue)+ putVector (demolishAttributeValueVictimVelocity demolishAttributeValue)
+ library/Rattletrap/AttributeValue/Enum.hs view
@@ -0,0 +1,18 @@+module Rattletrap.AttributeValue.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 EnumAttributeValue = EnumAttributeValue+ { enumAttributeValueValue :: Word.Word16+ } deriving (Eq, Ord, Show)++getEnumAttributeValue :: BinaryBit.BitGet EnumAttributeValue+getEnumAttributeValue = do+ value <- BinaryBit.getWord16be 11+ pure (EnumAttributeValue value)++putEnumAttributeValue :: EnumAttributeValue -> BinaryBit.BitPut ()+putEnumAttributeValue enumAttributeValue =+ BinaryBit.putWord16be 11 (enumAttributeValueValue enumAttributeValue)
+ library/Rattletrap/AttributeValue/Explosion.hs view
@@ -0,0 +1,25 @@+module Rattletrap.AttributeValue.Explosion where++import Rattletrap.Int32+import Rattletrap.Vector++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++data ExplosionAttributeValue = ExplosionAttributeValue+ { explosionAttributeValueActorId :: Int32+ , explosionAttributeValueLocation :: Vector+ } deriving (Eq, Ord, Show)++getExplosionAttributeValue :: BinaryBit.BitGet ExplosionAttributeValue+getExplosionAttributeValue = do+ False <- BinaryBit.getBool+ actorId <- getInt32Bits+ location <- getVector+ pure (ExplosionAttributeValue actorId location)++putExplosionAttributeValue :: ExplosionAttributeValue -> BinaryBit.BitPut ()+putExplosionAttributeValue explosionAttributeValue = do+ BinaryBit.putBool False+ putInt32Bits (explosionAttributeValueActorId explosionAttributeValue)+ putVector (explosionAttributeValueLocation explosionAttributeValue)
+ library/Rattletrap/AttributeValue/FlaggedInt.hs view
@@ -0,0 +1,22 @@+module Rattletrap.AttributeValue.FlaggedInt where++import Rattletrap.Int32++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++data FlaggedIntAttributeValue = FlaggedIntAttributeValue+ { flaggedIntAttributeValueFlag :: Bool+ , flaggedIntAttributeValueInt :: Int32+ } deriving (Eq, Ord, Show)++getFlaggedIntAttributeValue :: BinaryBit.BitGet FlaggedIntAttributeValue+getFlaggedIntAttributeValue = do+ flag <- BinaryBit.getBool+ int <- getInt32Bits+ pure (FlaggedIntAttributeValue flag int)++putFlaggedIntAttributeValue :: FlaggedIntAttributeValue -> BinaryBit.BitPut ()+putFlaggedIntAttributeValue flaggedIntAttributeValue = do+ BinaryBit.putBool (flaggedIntAttributeValueFlag flaggedIntAttributeValue)+ putInt32Bits (flaggedIntAttributeValueInt flaggedIntAttributeValue)
+ library/Rattletrap/AttributeValue/Float.hs view
@@ -0,0 +1,19 @@+module Rattletrap.AttributeValue.Float where++import Rattletrap.Float32++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++newtype FloatAttributeValue = FloatAttributeValue+ { floatAttributeValueValue :: Float32+ } deriving (Eq, Ord, Show)++getFloatAttributeValue :: BinaryBit.BitGet FloatAttributeValue+getFloatAttributeValue = do+ value <- getFloat32Bits+ pure (FloatAttributeValue value)++putFloatAttributeValue :: FloatAttributeValue -> BinaryBit.BitPut ()+putFloatAttributeValue floatAttributeValue =+ putFloat32Bits (floatAttributeValueValue floatAttributeValue)
+ library/Rattletrap/AttributeValue/GameMode.hs view
@@ -0,0 +1,26 @@+module Rattletrap.AttributeValue.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 GameModeAttributeValue = GameModeAttributeValue+ { gameModeAttributeValueNumBits :: Int+ , gameModeAttributeValueWord :: Word.Word8+ } deriving (Eq, Ord, Show)++getGameModeAttributeValue :: (Int, Int)+ -> BinaryBit.BitGet GameModeAttributeValue+getGameModeAttributeValue version = do+ let numBits =+ if version < (868, 12)+ then 2+ else 8+ word <- BinaryBit.getWord8 numBits+ pure (GameModeAttributeValue numBits word)++putGameModeAttributeValue :: GameModeAttributeValue -> BinaryBit.BitPut ()+putGameModeAttributeValue gameModeAttributeValue = do+ let numBits = gameModeAttributeValueNumBits gameModeAttributeValue+ let word = gameModeAttributeValueWord gameModeAttributeValue+ BinaryBit.putWord8 numBits word
+ library/Rattletrap/AttributeValue/Int.hs view
@@ -0,0 +1,19 @@+module Rattletrap.AttributeValue.Int where++import Rattletrap.Int32++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++newtype IntAttributeValue = IntAttributeValue+ { intAttributeValueValue :: Int32+ } deriving (Eq, Ord, Show)++getIntAttributeValue :: BinaryBit.BitGet IntAttributeValue+getIntAttributeValue = do+ value <- getInt32Bits+ pure (IntAttributeValue value)++putIntAttributeValue :: IntAttributeValue -> BinaryBit.BitPut ()+putIntAttributeValue intAttributeValue =+ putInt32Bits (intAttributeValueValue intAttributeValue)
+ library/Rattletrap/AttributeValue/Loadout.hs view
@@ -0,0 +1,61 @@+module Rattletrap.AttributeValue.Loadout where++import Rattletrap.Word32+import Rattletrap.Word8++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++data LoadoutAttributeValue = LoadoutAttributeValue+ { loadoutAttributeValueVersion :: Word8+ , loadoutAttributeValueBody :: Word32+ , loadoutAttributeValueDecal :: Word32+ , loadoutAttributeValueWheels :: Word32+ , loadoutAttributeValueRocketTrail :: Word32+ , loadoutAttributeValueAntenna :: Word32+ , loadoutAttributeValueTopper :: Word32+ , loadoutAttributeValueUnknown1 :: Word32+ , loadoutAttributeValueUnknown2 :: Maybe Word32+ } deriving (Eq, Ord, Show)++getLoadoutAttributeValue :: BinaryBit.BitGet LoadoutAttributeValue+getLoadoutAttributeValue = 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+ (LoadoutAttributeValue+ version+ body+ decal+ wheels+ rocketTrail+ antenna+ topper+ g+ h)++putLoadoutAttributeValue :: LoadoutAttributeValue -> BinaryBit.BitPut ()+putLoadoutAttributeValue loadoutAttributeValue = do+ putWord8Bits (loadoutAttributeValueVersion loadoutAttributeValue)+ putWord32Bits (loadoutAttributeValueBody loadoutAttributeValue)+ putWord32Bits (loadoutAttributeValueDecal loadoutAttributeValue)+ putWord32Bits (loadoutAttributeValueWheels loadoutAttributeValue)+ putWord32Bits (loadoutAttributeValueRocketTrail loadoutAttributeValue)+ putWord32Bits (loadoutAttributeValueAntenna loadoutAttributeValue)+ putWord32Bits (loadoutAttributeValueTopper loadoutAttributeValue)+ putWord32Bits (loadoutAttributeValueUnknown1 loadoutAttributeValue)+ case loadoutAttributeValueUnknown2 loadoutAttributeValue of+ Nothing -> pure ()+ Just x -> putWord32Bits x
+ library/Rattletrap/AttributeValue/LoadoutOnline.hs view
@@ -0,0 +1,42 @@+module Rattletrap.AttributeValue.LoadoutOnline where++import Rattletrap.CompressedWord+import Rattletrap.Word32+import Rattletrap.Word8++import qualified Control.Monad as Monad+import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++newtype LoadoutOnlineAttributeValue = LoadoutOnlineAttributeValue+ { loadoutAttributeValueValue :: [[(Word32, CompressedWord)]]+ } deriving (Eq, Ord, Show)++getLoadoutOnlineAttributeValue :: BinaryBit.BitGet LoadoutOnlineAttributeValue+getLoadoutOnlineAttributeValue = 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 (LoadoutOnlineAttributeValue values)++putLoadoutOnlineAttributeValue :: LoadoutOnlineAttributeValue+ -> BinaryBit.BitPut ()+putLoadoutOnlineAttributeValue loadoutAttributeValue = do+ let values = loadoutAttributeValueValue loadoutAttributeValue+ putWord8Bits (Word8 (fromIntegral (length values)))+ mapM_+ (\xs -> do+ putWord8Bits (Word8 (fromIntegral (length xs)))+ mapM_+ (\(x, y) -> do+ putWord32Bits x+ putCompressedWord y)+ xs)+ values
+ library/Rattletrap/AttributeValue/Loadouts.hs view
@@ -0,0 +1,22 @@+module Rattletrap.AttributeValue.Loadouts where++import Rattletrap.AttributeValue.Loadout++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++data LoadoutsAttributeValue = LoadoutsAttributeValue+ { loadoutsAttributeValueBlue :: LoadoutAttributeValue+ , loadoutsAttributeValueOrange :: LoadoutAttributeValue+ } deriving (Eq, Ord, Show)++getLoadoutsAttributeValue :: BinaryBit.BitGet LoadoutsAttributeValue+getLoadoutsAttributeValue = do+ blue <- getLoadoutAttributeValue+ orange <- getLoadoutAttributeValue+ pure (LoadoutsAttributeValue blue orange)++putLoadoutsAttributeValue :: LoadoutsAttributeValue -> BinaryBit.BitPut ()+putLoadoutsAttributeValue loadoutsAttributeValue = do+ putLoadoutAttributeValue (loadoutsAttributeValueBlue loadoutsAttributeValue)+ putLoadoutAttributeValue (loadoutsAttributeValueOrange loadoutsAttributeValue)
+ library/Rattletrap/AttributeValue/LoadoutsOnline.hs view
@@ -0,0 +1,34 @@+module Rattletrap.AttributeValue.LoadoutsOnline where++import Rattletrap.AttributeValue.LoadoutOnline++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++data LoadoutsOnlineAttributeValue = LoadoutsOnlineAttributeValue+ { loadoutsOnlineAttributeValueBlue :: LoadoutOnlineAttributeValue+ , loadoutsOnlineAttributeValueOrange :: LoadoutOnlineAttributeValue+ , loadoutsOnlineAttributeValueUnknown1 :: Bool+ , loadoutsOnlineAttributeValueUnknown2 :: Bool+ } deriving (Eq, Ord, Show)++getLoadoutsOnlineAttributeValue :: BinaryBit.BitGet LoadoutsOnlineAttributeValue+getLoadoutsOnlineAttributeValue = do+ blueLoadout <- getLoadoutOnlineAttributeValue+ orangeLoadout <- getLoadoutOnlineAttributeValue+ unknown1 <- BinaryBit.getBool+ unknown2 <- BinaryBit.getBool+ pure+ (LoadoutsOnlineAttributeValue blueLoadout orangeLoadout unknown1 unknown2)++putLoadoutsOnlineAttributeValue :: LoadoutsOnlineAttributeValue+ -> BinaryBit.BitPut ()+putLoadoutsOnlineAttributeValue loadoutsOnlineAttributeValue = do+ putLoadoutOnlineAttributeValue+ (loadoutsOnlineAttributeValueBlue loadoutsOnlineAttributeValue)+ putLoadoutOnlineAttributeValue+ (loadoutsOnlineAttributeValueOrange loadoutsOnlineAttributeValue)+ BinaryBit.putBool+ (loadoutsOnlineAttributeValueUnknown1 loadoutsOnlineAttributeValue)+ BinaryBit.putBool+ (loadoutsOnlineAttributeValueUnknown2 loadoutsOnlineAttributeValue)
+ library/Rattletrap/AttributeValue/Location.hs view
@@ -0,0 +1,19 @@+module Rattletrap.AttributeValue.Location where++import Rattletrap.Vector++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++newtype LocationAttributeValue = LocationAttributeValue+ { locationAttributeValueValue :: Vector+ } deriving (Eq, Ord, Show)++getLocationAttributeValue :: BinaryBit.BitGet LocationAttributeValue+getLocationAttributeValue = do+ value <- getVector+ pure (LocationAttributeValue value)++putLocationAttributeValue :: LocationAttributeValue -> BinaryBit.BitPut ()+putLocationAttributeValue locationAttributeValue =+ putVector (locationAttributeValueValue locationAttributeValue)
+ library/Rattletrap/AttributeValue/MusicStinger.hs view
@@ -0,0 +1,27 @@+module Rattletrap.AttributeValue.MusicStinger where++import Rattletrap.Word32+import Rattletrap.Word8++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++data MusicStingerAttributeValue = MusicStingerAttributeValue+ { musicStingerAttributeValueFlag :: Bool+ , musicStingerAttributeValueCue :: Word32+ , musicStingerAttributeValueTrigger :: Word8+ } deriving (Eq, Ord, Show)++getMusicStingerAttributeValue :: BinaryBit.BitGet MusicStingerAttributeValue+getMusicStingerAttributeValue = do+ flag <- BinaryBit.getBool+ cue <- getWord32Bits+ trigger <- getWord8Bits+ pure (MusicStingerAttributeValue flag cue trigger)++putMusicStingerAttributeValue :: MusicStingerAttributeValue+ -> BinaryBit.BitPut ()+putMusicStingerAttributeValue musicStingerAttributeValue = do+ BinaryBit.putBool (musicStingerAttributeValueFlag musicStingerAttributeValue)+ putWord32Bits (musicStingerAttributeValueCue musicStingerAttributeValue)+ putWord8Bits (musicStingerAttributeValueTrigger musicStingerAttributeValue)
+ library/Rattletrap/AttributeValue/PartyLeader.hs view
@@ -0,0 +1,33 @@+module Rattletrap.AttributeValue.PartyLeader where++import Rattletrap.RemoteId+import Rattletrap.Word8++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++data PartyLeaderAttributeValue = PartyLeaderAttributeValue+ { partyLeaderAttributeValueSystemId :: Word8+ , partyLeaderAttributeValueId :: Maybe (RemoteId, Word8)+ } deriving (Eq, Ord, Show)++getPartyLeaderAttributeValue :: BinaryBit.BitGet PartyLeaderAttributeValue+getPartyLeaderAttributeValue = do+ systemId <- getWord8Bits+ maybeRemoteAndLocalId <-+ if systemId == Word8 0+ then pure Nothing+ else do+ remoteId <- getRemoteId systemId+ localId <- getWord8Bits+ pure (Just (remoteId, localId))+ pure (PartyLeaderAttributeValue systemId maybeRemoteAndLocalId)++putPartyLeaderAttributeValue :: PartyLeaderAttributeValue -> BinaryBit.BitPut ()+putPartyLeaderAttributeValue partyLeaderAttributeValue = do+ putWord8Bits (partyLeaderAttributeValueSystemId partyLeaderAttributeValue)+ case partyLeaderAttributeValueId partyLeaderAttributeValue of+ Nothing -> pure ()+ Just (remoteId, localId) -> do+ putRemoteId remoteId+ putWord8Bits localId
+ library/Rattletrap/AttributeValue/Pickup.hs view
@@ -0,0 +1,32 @@+module Rattletrap.AttributeValue.Pickup where++import Rattletrap.Word32++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++data PickupAttributeValue = PickupAttributeValue+ { pickupAttributeValueInstigatorId :: Maybe Word32+ , pickupAttributeValuePickedUp :: Bool+ } deriving (Eq, Ord, Show)++getPickupAttributeValue :: BinaryBit.BitGet PickupAttributeValue+getPickupAttributeValue = do+ instigator <- BinaryBit.getBool+ maybeInstigatorId <-+ if instigator+ then do+ instigatorId <- getWord32Bits+ pure (Just instigatorId)+ else pure Nothing+ pickedUp <- BinaryBit.getBool+ pure (PickupAttributeValue maybeInstigatorId pickedUp)++putPickupAttributeValue :: PickupAttributeValue -> BinaryBit.BitPut ()+putPickupAttributeValue pickupAttributeValue = do+ case pickupAttributeValueInstigatorId pickupAttributeValue of+ Nothing -> BinaryBit.putBool False+ Just instigatorId -> do+ BinaryBit.putBool True+ putWord32Bits instigatorId+ BinaryBit.putBool (pickupAttributeValuePickedUp pickupAttributeValue)
+ library/Rattletrap/AttributeValue/PrivateMatchSettings.hs view
@@ -0,0 +1,54 @@+module Rattletrap.AttributeValue.PrivateMatchSettings where++import Rattletrap.Text+import Rattletrap.Word32++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++data PrivateMatchSettingsAttributeValue = PrivateMatchSettingsAttributeValue+ { privateMatchSettingsAttributeValueMutators :: Text+ , privateMatchSettingsAttributeValueJoinableBy :: Word32+ , privateMatchSettingsAttributeValueMaxPlayers :: Word32+ , privateMatchSettingsAttributeValueGameName :: Text+ , privateMatchSettingsAttributeValuePassword :: Text+ , privateMatchSettingsAttributeValueFlag :: Bool+ } deriving (Eq, Ord, Show)++getPrivateMatchSettingsAttributeValue :: BinaryBit.BitGet PrivateMatchSettingsAttributeValue+getPrivateMatchSettingsAttributeValue = do+ mutators <- getTextBits+ joinableBy <- getWord32Bits+ maxPlayers <- getWord32Bits+ gameName <- getTextBits+ password <- getTextBits+ flag <- BinaryBit.getBool+ pure+ (PrivateMatchSettingsAttributeValue+ mutators+ joinableBy+ maxPlayers+ gameName+ password+ flag)++putPrivateMatchSettingsAttributeValue :: PrivateMatchSettingsAttributeValue+ -> BinaryBit.BitPut ()+putPrivateMatchSettingsAttributeValue privateMatchSettingsAttributeValue = do+ putTextBits+ (privateMatchSettingsAttributeValueMutators+ privateMatchSettingsAttributeValue)+ putWord32Bits+ (privateMatchSettingsAttributeValueJoinableBy+ privateMatchSettingsAttributeValue)+ putWord32Bits+ (privateMatchSettingsAttributeValueMaxPlayers+ privateMatchSettingsAttributeValue)+ putTextBits+ (privateMatchSettingsAttributeValueGameName+ privateMatchSettingsAttributeValue)+ putTextBits+ (privateMatchSettingsAttributeValuePassword+ privateMatchSettingsAttributeValue)+ BinaryBit.putBool+ (privateMatchSettingsAttributeValueFlag privateMatchSettingsAttributeValue)
+ library/Rattletrap/AttributeValue/QWord.hs view
@@ -0,0 +1,19 @@+module Rattletrap.AttributeValue.QWord where++import Rattletrap.Word64++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++newtype QWordAttributeValue = QWordAttributeValue+ { qWordAttributeValueValue :: Word64+ } deriving (Eq, Ord, Show)++getQWordAttributeValue :: BinaryBit.BitGet QWordAttributeValue+getQWordAttributeValue = do+ value <- getWord64Bits+ pure (QWordAttributeValue value)++putQWordAttributeValue :: QWordAttributeValue -> BinaryBit.BitPut ()+putQWordAttributeValue qWordAttributeValue =+ putWord64Bits (qWordAttributeValueValue qWordAttributeValue)
+ library/Rattletrap/AttributeValue/Reservation.hs view
@@ -0,0 +1,56 @@+module Rattletrap.AttributeValue.Reservation where++import Rattletrap.AttributeValue.UniqueId+import Rattletrap.CompressedWord+import Rattletrap.Text+import Rattletrap.Word8++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit+import qualified Data.Word as Word++data ReservationAttributeValue = ReservationAttributeValue+ { reservationAttributeValueNumber :: CompressedWord+ , reservationAttributeValueUniqueId :: UniqueIdAttributeValue+ , reservationAttributeValueName :: Maybe Text+ , reservationAttributeValueUnknown1 :: Bool+ , reservationAttributeValueUnknown2 :: Bool+ , reservationAttributeValueUnknown3 :: Maybe Word.Word8+ } deriving (Eq, Ord, Show)++getReservationAttributeValue :: (Int, Int)+ -> BinaryBit.BitGet ReservationAttributeValue+getReservationAttributeValue version = do+ number <- getCompressedWord 7+ uniqueId <- getUniqueIdAttributeValue+ name <-+ if uniqueIdAttributeValueSystemId 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 (ReservationAttributeValue number uniqueId name a b mc)++putReservationAttributeValue :: ReservationAttributeValue -> BinaryBit.BitPut ()+putReservationAttributeValue reservationAttributeValue = do+ putCompressedWord (reservationAttributeValueNumber reservationAttributeValue)+ putUniqueIdAttributeValue+ (reservationAttributeValueUniqueId reservationAttributeValue)+ case reservationAttributeValueName reservationAttributeValue of+ Nothing -> pure ()+ Just name -> putTextBits name+ BinaryBit.putBool+ (reservationAttributeValueUnknown1 reservationAttributeValue)+ BinaryBit.putBool+ (reservationAttributeValueUnknown2 reservationAttributeValue)+ case reservationAttributeValueUnknown3 reservationAttributeValue of+ Nothing -> pure ()+ Just c -> BinaryBit.putWord8 6 c
+ library/Rattletrap/AttributeValue/RigidBodyState.hs view
@@ -0,0 +1,55 @@+module Rattletrap.AttributeValue.RigidBodyState where++import Rattletrap.CompressedWordVector+import Rattletrap.Vector++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++data RigidBodyStateAttributeValue = RigidBodyStateAttributeValue+ { rigidBodyStateAttributeValueSleeping :: Bool+ , rigidBodyStateAttributeValueLocation :: Vector+ , rigidBodyStateAttributeValueRotation :: CompressedWordVector+ , rigidBodyStateAttributeValueLinearVelocity :: Maybe Vector+ , rigidBodyStateAttributeValueAngularVelocity :: Maybe Vector+ } deriving (Eq, Ord, Show)++getRigidBodyStateAttributeValue :: BinaryBit.BitGet RigidBodyStateAttributeValue+getRigidBodyStateAttributeValue = 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+ (RigidBodyStateAttributeValue+ sleeping+ location+ rotation+ linearVelocity+ angularVelocity)++putRigidBodyStateAttributeValue :: RigidBodyStateAttributeValue+ -> BinaryBit.BitPut ()+putRigidBodyStateAttributeValue rigidBodyStateAttributeValue = do+ BinaryBit.putBool+ (rigidBodyStateAttributeValueSleeping rigidBodyStateAttributeValue)+ putVector (rigidBodyStateAttributeValueLocation rigidBodyStateAttributeValue)+ putCompressedWordVector+ (rigidBodyStateAttributeValueRotation rigidBodyStateAttributeValue)+ case rigidBodyStateAttributeValueLinearVelocity rigidBodyStateAttributeValue of+ Nothing -> pure ()+ Just linearVelocity -> putVector linearVelocity+ case rigidBodyStateAttributeValueAngularVelocity rigidBodyStateAttributeValue of+ Nothing -> pure ()+ Just angularVelocity -> putVector angularVelocity
+ library/Rattletrap/AttributeValue/String.hs view
@@ -0,0 +1,19 @@+module Rattletrap.AttributeValue.String where++import Rattletrap.Text++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++newtype StringAttributeValue = StringAttributeValue+ { stringAttributeValueValue :: Text+ } deriving (Eq, Ord, Show)++getStringAttributeValue :: BinaryBit.BitGet StringAttributeValue+getStringAttributeValue = do+ value <- getTextBits+ pure (StringAttributeValue value)++putStringAttributeValue :: StringAttributeValue -> BinaryBit.BitPut ()+putStringAttributeValue stringAttributeValue =+ putTextBits (stringAttributeValueValue stringAttributeValue)
+ library/Rattletrap/AttributeValue/TeamPaint.hs view
@@ -0,0 +1,38 @@+module Rattletrap.AttributeValue.TeamPaint where++import Rattletrap.Word32+import Rattletrap.Word8++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++data TeamPaintAttributeValue = TeamPaintAttributeValue+ { teamPaintAttributeValueTeam :: Word8+ , teamPaintAttributeValuePrimaryColor :: Word8+ , teamPaintAttributeValueAccentColor :: Word8+ , teamPaintAttributeValuePrimaryFinish :: Word32+ , teamPaintAttributeValueAccentFinish :: Word32+ } deriving (Eq, Ord, Show)++getTeamPaintAttributeValue :: BinaryBit.BitGet TeamPaintAttributeValue+getTeamPaintAttributeValue = do+ team <- getWord8Bits+ primaryColor <- getWord8Bits+ accentColor <- getWord8Bits+ primaryFinish <- getWord32Bits+ accentFinish <- getWord32Bits+ pure+ (TeamPaintAttributeValue+ team+ primaryColor+ accentColor+ primaryFinish+ accentFinish)++putTeamPaintAttributeValue :: TeamPaintAttributeValue -> BinaryBit.BitPut ()+putTeamPaintAttributeValue teamPaintAttributeValue = do+ putWord8Bits (teamPaintAttributeValueTeam teamPaintAttributeValue)+ putWord8Bits (teamPaintAttributeValuePrimaryColor teamPaintAttributeValue)+ putWord8Bits (teamPaintAttributeValueAccentColor teamPaintAttributeValue)+ putWord32Bits (teamPaintAttributeValuePrimaryFinish teamPaintAttributeValue)+ putWord32Bits (teamPaintAttributeValueAccentFinish teamPaintAttributeValue)
+ library/Rattletrap/AttributeValue/UniqueId.hs view
@@ -0,0 +1,26 @@+module Rattletrap.AttributeValue.UniqueId where++import Rattletrap.RemoteId+import Rattletrap.Word8++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++data UniqueIdAttributeValue = UniqueIdAttributeValue+ { uniqueIdAttributeValueSystemId :: Word8+ , uniqueIdAttributeValueRemoteId :: RemoteId+ , uniqueIdAttributeValueLocalId :: Word8+ } deriving (Eq, Ord, Show)++getUniqueIdAttributeValue :: BinaryBit.BitGet UniqueIdAttributeValue+getUniqueIdAttributeValue = do+ systemId <- getWord8Bits+ remoteId <- getRemoteId systemId+ localId <- getWord8Bits+ pure (UniqueIdAttributeValue systemId remoteId localId)++putUniqueIdAttributeValue :: UniqueIdAttributeValue -> BinaryBit.BitPut ()+putUniqueIdAttributeValue uniqueIdAttributeValue = do+ putWord8Bits (uniqueIdAttributeValueSystemId uniqueIdAttributeValue)+ putRemoteId (uniqueIdAttributeValueRemoteId uniqueIdAttributeValue)+ putWord8Bits (uniqueIdAttributeValueLocalId uniqueIdAttributeValue)
+ library/Rattletrap/AttributeValue/WeldedInfo.hs view
@@ -0,0 +1,34 @@+module Rattletrap.AttributeValue.WeldedInfo where++import Rattletrap.Float32+import Rattletrap.Int32+import Rattletrap.Int8Vector+import Rattletrap.Vector++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++data WeldedInfoAttributeValue = WeldedInfoAttributeValue+ { weldedInfoAttributeValueActive :: Bool+ , weldedInfoAttributeValueActorId :: Int32+ , weldedInfoAttributeValueOffset :: Vector+ , weldedInfoAttributeValueMass :: Float32+ , weldedInfoAttributeValueRotation :: Int8Vector+ } deriving (Eq, Ord, Show)++getWeldedInfoAttributeValue :: BinaryBit.BitGet WeldedInfoAttributeValue+getWeldedInfoAttributeValue = do+ active <- BinaryBit.getBool+ actorId <- getInt32Bits+ offset <- getVector+ mass <- getFloat32Bits+ rotation <- getInt8Vector+ pure (WeldedInfoAttributeValue active actorId offset mass rotation)++putWeldedInfoAttributeValue :: WeldedInfoAttributeValue -> BinaryBit.BitPut ()+putWeldedInfoAttributeValue weldedInfoAttributeValue = do+ BinaryBit.putBool (weldedInfoAttributeValueActive weldedInfoAttributeValue)+ putInt32Bits (weldedInfoAttributeValueActorId weldedInfoAttributeValue)+ putVector (weldedInfoAttributeValueOffset weldedInfoAttributeValue)+ putFloat32Bits (weldedInfoAttributeValueMass weldedInfoAttributeValue)+ putInt8Vector (weldedInfoAttributeValueRotation weldedInfoAttributeValue)
+ library/Rattletrap/AttributeValueType.hs view
@@ -0,0 +1,30 @@+module Rattletrap.AttributeValueType where++data AttributeValueType+ = AVBoolean+ | AVByte+ | AVCamSettings+ | AVDemolish+ | AVEnum+ | AVExplosion+ | AVFlaggedInt+ | AVFloat+ | AVGameMode+ | AVInt+ | AVLoadout+ | AVLoadoutOnline+ | AVLoadouts+ | AVLoadoutsOnline+ | AVLocation+ | AVMusicStinger+ | AVPartyLeader+ | AVPickup+ | AVPrivateMatchSettings+ | AVQWord+ | AVReservation+ | AVRigidBodyState+ | AVString+ | AVTeamPaint+ | AVUniqueId+ | AVWeldedInfo+ deriving (Eq, Ord, Show)
+ library/Rattletrap/Cache.hs view
@@ -0,0 +1,35 @@+module Rattletrap.Cache where++import Rattletrap.AttributeMapping+import Rattletrap.List+import Rattletrap.Word32++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+ { cacheClassId = classId+ , cacheParentCacheId = parentCacheId+ , cacheCacheId = cacheId+ , cacheAttributeMappings = attributeMappings+ }++putCache :: Cache -> Binary.Put+putCache cache = do+ putWord32 (cacheClassId cache)+ putWord32 (cacheParentCacheId cache)+ putWord32 (cacheCacheId cache)+ putList putAttributeMapping (cacheAttributeMappings cache)
+ library/Rattletrap/ClassAttributeMap.hs view
@@ -0,0 +1,247 @@+module Rattletrap.ClassAttributeMap where++import Rattletrap.ActorMap+import Rattletrap.AttributeMapping+import Rattletrap.Cache+import Rattletrap.ClassMapping+import Rattletrap.CompressedWord+import Rattletrap.Data+import Rattletrap.List+import Rattletrap.Text+import Rattletrap.Utility+import Rattletrap.Word32++import qualified Data.Bimap as Bimap+import qualified Data.List as List+import qualified Data.Map as Map+import qualified Data.Maybe as Maybe+import qualified Data.Set as Set++data ClassAttributeMap = ClassAttributeMap+ { classAttributeMapObjectMap :: Bimap.Bimap Word32 Text+ , classAttributeMapClassMap :: Bimap.Bimap Word32 Text+ , classAttributeMapValue :: Map.Map Word32 (Bimap.Bimap Word32 Word32)+ } deriving (Eq, Show)++makeClassAttributeMap :: List Text+ -> List ClassMapping+ -> List Cache+ -> ClassAttributeMap+makeClassAttributeMap objects classMappings caches =+ let objectMap = makeObjectMap objects+ classCache = makeClassCache classMappings caches+ attributeMap = makeAttributeMap caches+ classIds = map (\(_, classId, _, _) -> classId) classCache+ parentMap = makeParentMap classMappings caches+ value =+ Map.fromList+ (map+ (\classId ->+ let ownAttributes =+ Maybe.fromMaybe+ Bimap.empty+ (Map.lookup classId attributeMap)+ parentsAttributes =+ case Map.lookup classId parentMap of+ Nothing -> []+ Just parentClassIds ->+ map+ (\parentClassId ->+ Maybe.fromMaybe+ Bimap.empty+ (Map.lookup parentClassId attributeMap))+ parentClassIds+ attributes = ownAttributes : parentsAttributes+ in (classId, Bimap.fromList (concatMap Bimap.toList attributes)))+ classIds)+ in ClassAttributeMap+ { classAttributeMapObjectMap = objectMap+ , classAttributeMapClassMap = makeClassMap classMappings+ , classAttributeMapValue = value+ }++makeClassCache :: List ClassMapping+ -> List Cache+ -> [(Maybe Text, Word32, Word32, Word32)]+makeClassCache classMappings caches =+ let classMap = makeClassMap classMappings+ in 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 (Bimap.Bimap Word32 Word32)+makeAttributeMap caches =+ Map.fromList+ (map+ (\cache ->+ ( cacheClassId cache+ , Bimap.fromList+ (map+ (\attributeMapping ->+ ( attributeMappingStreamId attributeMapping+ , attributeMappingObjectId attributeMapping))+ (listValue (cacheAttributeMappings cache)))))+ (listValue caches))++makeShallowParentMap :: List ClassMapping -> List Cache -> Map.Map Word32 Word32+makeShallowParentMap classMappings caches =+ let classCache = makeClassCache classMappings caches+ in 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 :: List ClassMapping -> List Cache -> Map.Map Word32 [Word32]+makeParentMap classMappings caches =+ let shallowParentMap = makeShallowParentMap classMappings caches+ 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 -> Bimap.Bimap Word32 Text+makeObjectMap objects =+ Bimap.fromList (zip (map Word32 [0 ..]) (listValue objects))++getObjectName :: ClassAttributeMap -> Word32 -> Maybe Text+getObjectName classAttributeMap objectId =+ Bimap.lookup objectId (classAttributeMapObjectMap classAttributeMap)++getClassName :: Text -> Maybe Text+getClassName rawObjectName =+ Map.lookup (normalizeObjectName rawObjectName) objectClasses++normalizeObjectName :: Text -> Text+normalizeObjectName objectName =+ stringToText+ (replace+ "_[0-9]+$"+ ""+ (replace "^[A-Z_a-z]+[.]TheWorld:" "TheWorld:" (textToString 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 :: ClassAttributeMap+ -> ActorMap+ -> CompressedWord+ -> Maybe Word+getAttributeIdLimit classAttributeMap actorMap actorId = do+ attributeMap <- getAttributeMap classAttributeMap actorMap actorId+ let streamIds = Bimap.keys attributeMap+ let maxStreamId = maximum (Word32 0 : streamIds)+ let limit = fromIntegral (word32Value maxStreamId)+ pure limit++getAttributeName :: ClassAttributeMap+ -> ActorMap+ -> CompressedWord+ -> CompressedWord+ -> Maybe Text+getAttributeName classAttributeMap actorMap actorId streamId = do+ attributeMap <- getAttributeMap classAttributeMap actorMap actorId+ let key = Word32 (fromIntegral (compressedWordValue streamId))+ attributeId <- Bimap.lookup key attributeMap+ let objectMap = classAttributeMapObjectMap classAttributeMap+ Bimap.lookup attributeId objectMap++getAttributeMap+ :: ClassAttributeMap+ -> ActorMap+ -> CompressedWord+ -> Maybe (Bimap.Bimap Word32 Word32)+getAttributeMap classAttributeMap actorMap actorId = do+ objectId <- Map.lookup actorId actorMap+ objectName <- getObjectName classAttributeMap objectId+ className <- getClassName objectName+ let classMap = classAttributeMapClassMap classAttributeMap+ classId <- Bimap.lookupR className classMap+ let value = classAttributeMapValue classAttributeMap+ Map.lookup classId value
+ library/Rattletrap/ClassMapping.hs view
@@ -0,0 +1,22 @@+module Rattletrap.ClassMapping where++import Rattletrap.Text+import Rattletrap.Word32++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 {classMappingName = name, classMappingStreamId = streamId}++putClassMapping :: ClassMapping -> Binary.Put+putClassMapping classMapping = do+ putText (classMappingName classMapping)+ putWord32 (classMappingStreamId classMapping)
+ library/Rattletrap/CompressedWord.hs view
@@ -0,0 +1,59 @@+module Rattletrap.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
+ library/Rattletrap/CompressedWordVector.hs view
@@ -0,0 +1,31 @@+module Rattletrap.CompressedWordVector where++import Rattletrap.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+ { compressedWordVectorX = x+ , compressedWordVectorY = y+ , compressedWordVectorZ = z+ }++putCompressedWordVector :: CompressedWordVector -> BinaryBit.BitPut ()+putCompressedWordVector compressedWordVector = do+ putCompressedWord (compressedWordVectorX compressedWordVector)+ putCompressedWord (compressedWordVectorY compressedWordVector)+ putCompressedWord (compressedWordVectorZ compressedWordVector)
+ library/Rattletrap/Content.hs view
@@ -0,0 +1,86 @@+module Rattletrap.Content where++import Rattletrap.ActorMap+import Rattletrap.Cache+import Rattletrap.ClassAttributeMap+import Rattletrap.ClassMapping+import Rattletrap.Frame+import Rattletrap.KeyFrame+import Rattletrap.List+import Rattletrap.Mark+import Rattletrap.Message+import Rattletrap.Text+import Rattletrap.Utility+import Rattletrap.Word32++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]+ , 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, _) =+ Binary.runGet+ (BinaryBit.runBitGet+ (getFrames version numFrames classAttributeMap makeActorMap))+ (reverseBytes stream)+ pure+ Content+ { contentLevels = levels+ , contentKeyFrames = keyFrames+ , contentStreamSize = streamSize+ , contentFrames = frames+ , contentMessages = messages+ , contentMarks = marks+ , contentPackages = packages+ , contentObjects = objects+ , contentNames = names+ , contentClassMappings = classMappings+ , contentCaches = 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)
+ library/Rattletrap/Crc.hs view
@@ -0,0 +1,37 @@+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
+ library/Rattletrap/Data.hs view
@@ -0,0 +1,349 @@+module Rattletrap.Data where++import Rattletrap.AttributeValueType++rawParentClasses :: [(String, String)]+rawParentClasses =+ [ ("Engine.Actor", "Core.Object")+ , ("Engine.GameReplicationInfo", "Engine.ReplicationInfo")+ , ("Engine.Info", "Engine.Actor")+ , ("Engine.Pawn", "Engine.Actor")+ , ("Engine.PlayerReplicationInfo", "Engine.ReplicationInfo")+ , ("Engine.ReplicationInfo", "Engine.Info")+ , ("Engine.TeamInfo", "Engine.ReplicationInfo")+ , ("ProjectX.GRI_X", "Engine.GameReplicationInfo")+ , ("ProjectX.Pawn_X", "Engine.Pawn")+ , ("ProjectX.PRI_X", "Engine.PlayerReplicationInfo")+ , ("TAGame.Ball_TA", "TAGame.RBActor_TA")+ , ("TAGame.CameraSettingsActor_TA", "Engine.ReplicationInfo")+ , ("TAGame.Car_Season_TA", "TAGame.PRI_TA")+ , ("TAGame.Car_TA", "TAGame.Vehicle_TA")+ , ("TAGame.CarComponent_Boost_TA", "TAGame.CarComponent_TA")+ , ("TAGame.CarComponent_Dodge_TA", "TAGame.CarComponent_TA")+ , ("TAGame.CarComponent_DoubleJump_TA", "TAGame.CarComponent_TA")+ , ("TAGame.CarComponent_FlipCar_TA", "TAGame.CarComponent_TA")+ , ("TAGame.CarComponent_Jump_TA", "TAGame.CarComponent_TA")+ , ("TAGame.CarComponent_TA", "Engine.ReplicationInfo")+ , ("TAGame.CrowdActor_TA", "Engine.ReplicationInfo")+ , ("TAGame.CrowdManager_TA", "Engine.ReplicationInfo")+ , ("TAGame.GameEvent_Season_TA", "TAGame.GameEvent_Soccar_TA")+ , ("TAGame.GameEvent_Soccar_TA", "TAGame.GameEvent_Team_TA")+ , ("TAGame.GameEvent_SoccarPrivate_TA", "TAGame.GameEvent_Soccar_TA")+ , ("TAGame.GameEvent_SoccarSplitscreen_TA", "TAGame.GameEvent_SoccarPrivate_TA")+ , ("TAGame.GameEvent_TA", "Engine.ReplicationInfo")+ , ("TAGame.GameEvent_Team_TA", "TAGame.GameEvent_TA")+ , ("TAGame.GRI_TA", "ProjectX.GRI_X")+ , ("TAGame.InMapScoreboard_TA", "Engine.Actor")+ , ("TAGame.PRI_TA", "ProjectX.PRI_X")+ , ("TAGame.RBActor_TA", "ProjectX.Pawn_X")+ , ("TAGame.SpecialPickup_BallCarSpring_TA", "TAGame.SpecialPickup_Spring_TA")+ , ("TAGame.SpecialPickup_BallFreeze_TA", "TAGame.SpecialPickup_Targeted_TA")+ , ("TAGame.SpecialPickup_BallGravity_TA", "TAGame.SpecialPickup_TA")+ , ("TAGame.SpecialPickup_BallLasso_TA", "TAGame.SpecialPickup_GrapplingHook_TA")+ , ("TAGame.SpecialPickup_BallVelcro_TA", "TAGame.SpecialPickup_TA")+ -- , ("TAGame.SpecialPickup_Batarang_TA", "TAGame.SpecialPickup_BallLasso_TA")+ , ("TAGame.SpecialPickup_BoostOverride_TA", "TAGame.SpecialPickup_Targeted_TA")+ , ("TAGame.SpecialPickup_GrapplingHook_TA", "TAGame.SpecialPickup_Targeted_TA")+ , ("TAGame.SpecialPickup_HitForce_TA", "TAGame.SpecialPickup_TA")+ , ("TAGame.SpecialPickup_Spring_TA", "TAGame.SpecialPickup_Targeted_TA")+ , ("TAGame.SpecialPickup_Swapper_TA", "TAGame.SpecialPickup_Targeted_TA")+ , ("TAGame.SpecialPickup_TA", "TAGame.CarComponent_TA")+ , ("TAGame.SpecialPickup_Targeted_TA", "TAGame.SpecialPickup_TA")+ , ("TAGame.SpecialPickup_Tornado_TA", "TAGame.SpecialPickup_TA")+ , ("TAGame.Team_Soccar_TA", "TAGame.Team_TA")+ , ("TAGame.Team_TA", "Engine.TeamInfo")+ , ("TAGame.Vehicle_TA", "TAGame.RBActor_TA")+ , ("TAGame.VehiclePickup_Boost_TA", "TAGame.VehiclePickup_TA")+ , ("TAGame.VehiclePickup_TA", "Engine.ReplicationInfo")+ -- , ("TAGame.VoteActor_TA", "Engine.ReplicationInfo")+ ]++rawClassesWithLocation :: [String]+rawClassesWithLocation =+ [ "TAGame.Ball_TA"+ , "TAGame.CameraSettingsActor_TA"+ , "TAGame.Car_Season_TA"+ , "TAGame.Car_TA"+ , "TAGame.CarComponent_Boost_TA"+ , "TAGame.CarComponent_Dodge_TA"+ , "TAGame.CarComponent_DoubleJump_TA"+ , "TAGame.CarComponent_FlipCar_TA"+ , "TAGame.CarComponent_Jump_TA"+ , "TAGame.GameEvent_Season_TA"+ , "TAGame.GameEvent_Soccar_TA"+ , "TAGame.GameEvent_SoccarPrivate_TA"+ , "TAGame.GameEvent_SoccarSplitscreen_TA"+ , "TAGame.GRI_TA"+ , "TAGame.PRI_TA"+ , "TAGame.SpecialPickup_BallCarSpring_TA"+ , "TAGame.SpecialPickup_BallFreeze_TA"+ , "TAGame.SpecialPickup_BallGravity_TA"+ , "TAGame.SpecialPickup_BallGravity_TA"+ , "TAGame.SpecialPickup_BallLasso_TA"+ , "TAGame.SpecialPickup_BallVelcro_TA"+ , "TAGame.SpecialPickup_Batarang_TA"+ , "TAGame.SpecialPickup_BoostOverride_TA"+ , "TAGame.SpecialPickup_GrapplingHook_TA"+ , "TAGame.SpecialPickup_HitForce_TA"+ , "TAGame.SpecialPickup_Swapper_TA"+ , "TAGame.SpecialPickup_Tornado_TA"+ , "TAGame.Team_Soccar_TA"+ ]++rawClassesWithRotation :: [String]+rawClassesWithRotation =+ [ "TAGame.Ball_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_HockeyPrivate", "TAGame.GameEvent_SoccarPrivate_TA")+ , ("Archetypes.GameEvent.GameEvent_HockeySplitscreen", "TAGame.GameEvent_SoccarSplitscreen_TA")+ , ("Archetypes.GameEvent.GameEvent_Items", "TAGame.GameEvent_Soccar_TA")+ , ("Archetypes.GameEvent.GameEvent_Season:CarArchetype", "TAGame.Car_TA")+ , ("Archetypes.GameEvent.GameEvent_Season", "TAGame.GameEvent_Season_TA")+ , ("Archetypes.GameEvent.GameEvent_Soccar", "TAGame.GameEvent_Soccar_TA")+ , ("Archetypes.GameEvent.GameEvent_SoccarPrivate", "TAGame.GameEvent_SoccarPrivate_TA")+ , ("Archetypes.GameEvent.GameEvent_SoccarSplitscreen", "TAGame.GameEvent_SoccarSplitscreen_TA")+ , ("Archetypes.SpecialPickups.SpecialPickup_BallFreeze", "TAGame.SpecialPickup_BallFreeze_TA")+ , ("Archetypes.SpecialPickups.SpecialPickup_BallGrapplingHook", "TAGame.SpecialPickup_GrapplingHook_TA")+ , ("Archetypes.SpecialPickups.SpecialPickup_BallLasso", "TAGame.SpecialPickup_BallLasso_TA")+ , ("Archetypes.SpecialPickups.SpecialPickup_BallSpring", "TAGame.SpecialPickup_BallCarSpring_TA")+ , ("Archetypes.SpecialPickups.SpecialPickup_BallVelcro", "TAGame.SpecialPickup_BallVelcro_TA")+ -- , ("Archetypes.SpecialPickups.SpecialPickup_Batarang", "TAGame.SpecialPickup_Batarang_TA")+ , ("Archetypes.SpecialPickups.SpecialPickup_BoostOverride", "TAGame.SpecialPickup_BoostOverride_TA")+ , ("Archetypes.SpecialPickups.SpecialPickup_CarSpring", "TAGame.SpecialPickup_BallCarSpring_TA")+ , ("Archetypes.SpecialPickups.SpecialPickup_GravityWell", "TAGame.SpecialPickup_BallGravity_TA")+ , ("Archetypes.SpecialPickups.SpecialPickup_StrongHit", "TAGame.SpecialPickup_HitForce_TA")+ , ("Archetypes.SpecialPickups.SpecialPickup_Swapper", "TAGame.SpecialPickup_Swapper_TA")+ , ("Archetypes.SpecialPickups.SpecialPickup_Tornado", "TAGame.SpecialPickup_Tornado_TA")+ , ("Archetypes.Teams.Team0", "TAGame.Team_Soccar_TA")+ , ("Archetypes.Teams.Team1", "TAGame.Team_Soccar_TA")+ , ("GameInfo_Basketball.GameInfo.GameInfo_Basketball:GameReplicationInfoArchetype", "TAGame.GRI_TA")+ , ("Gameinfo_Hockey.GameInfo.Gameinfo_Hockey:GameReplicationInfoArchetype", "TAGame.GRI_TA")+ , ("GameInfo_Items.GameInfo.GameInfo_Items:GameReplicationInfoArchetype", "TAGame.GRI_TA")+ , ("GameInfo_Season.GameInfo.GameInfo_Season:GameReplicationInfoArchetype", "TAGame.GRI_TA")+ , ("GameInfo_Soccar.GameInfo.GameInfo_Soccar:GameReplicationInfoArchetype", "TAGame.GRI_TA")+ -- , ("TAGame.CameraSettingsActor_TA:PRI", "TAGame.CameraSettingsActor_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")+ ]++rawAttributeValueTypes :: [(String, AttributeValueType)]+rawAttributeValueTypes =+ [ ("Engine.Actor:bBlockActors", AVBoolean)+ , ("Engine.Actor:DrawScale", AVFloat)+ , ("Engine.Actor:Role", AVEnum)+ , ("Engine.Actor:bCollideActors", AVBoolean)+ , ("Engine.Actor:bHidden", AVBoolean)+ , ("Engine.GameReplicationInfo:GameClass", AVFlaggedInt)+ , ("Engine.GameReplicationInfo:ServerName", AVString)+ , ("Engine.GameReplicationInfo:bMatchIsOver", AVBoolean)+ , ("Engine.Pawn:PlayerReplicationInfo", AVFlaggedInt)+ , ("Engine.PlayerReplicationInfo:Ping", AVByte)+ , ("Engine.PlayerReplicationInfo:PlayerID", AVInt)+ , ("Engine.PlayerReplicationInfo:PlayerName", AVString)+ , ("Engine.PlayerReplicationInfo:RemoteUserData", AVString)+ , ("Engine.PlayerReplicationInfo:Score", AVInt)+ , ("Engine.PlayerReplicationInfo:Team", AVFlaggedInt)+ , ("Engine.PlayerReplicationInfo:UniqueId", AVUniqueId)+ , ("Engine.PlayerReplicationInfo:bBot", AVBoolean)+ , ("Engine.PlayerReplicationInfo:bIsSpectator", AVBoolean)+ , ("Engine.PlayerReplicationInfo:bReadyToPlay", AVBoolean)+ , ("Engine.PlayerReplicationInfo:bWaitingPlayer", AVBoolean)+ , ("Engine.TeamInfo:Score", AVInt)+ , ("ProjectX.GRI_X:GameServerID", AVQWord)+ , ("ProjectX.GRI_X:ReplicatedGameMutatorIndex", AVInt)+ , ("ProjectX.GRI_X:ReplicatedGamePlaylist", AVInt)+ , ("ProjectX.GRI_X:Reservations", AVReservation)+ , ("ProjectX.GRI_X:bGameStarted", AVBoolean)+ , ("TAGame.Ball_TA:GameEvent", AVFlaggedInt)+ , ("TAGame.Ball_TA:HitTeamNum", AVByte)+ , ("TAGame.Ball_TA:ReplicatedAddedCarBounceScale", AVFloat)+ , ("TAGame.Ball_TA:ReplicatedBallMaxLinearSpeedScale", AVFloat)+ , ("TAGame.Ball_TA:ReplicatedBallScale", AVFloat)+ , ("TAGame.Ball_TA:ReplicatedExplosionData", AVExplosion)+ , ("TAGame.Ball_TA:ReplicatedWorldBounceScale", AVFloat)+ , ("TAGame.CameraSettingsActor_TA:CameraPitch", AVByte)+ , ("TAGame.CameraSettingsActor_TA:CameraYaw", AVByte)+ , ("TAGame.CameraSettingsActor_TA:PRI", AVFlaggedInt)+ , ("TAGame.CameraSettingsActor_TA:ProfileSettings", AVCamSettings)+ , ("TAGame.CameraSettingsActor_TA:bUsingBehindView", AVBoolean)+ , ("TAGame.CameraSettingsActor_TA:bUsingSecondaryCamera", AVBoolean)+ , ("TAGame.CarComponent_Boost_TA:BoostModifier", AVFloat)+ , ("TAGame.CarComponent_Boost_TA:RechargeDelay", AVFloat)+ , ("TAGame.CarComponent_Boost_TA:RechargeRate", AVFloat)+ , ("TAGame.CarComponent_Boost_TA:ReplicatedBoostAmount", AVByte)+ , ("TAGame.CarComponent_Boost_TA:UnlimitedBoostRefCount", AVInt)+ , ("TAGame.CarComponent_Boost_TA:bNoBoost", AVBoolean)+ , ("TAGame.CarComponent_Boost_TA:bUnlimitedBoost", AVBoolean)+ , ("TAGame.CarComponent_Dodge_TA:DodgeTorque", AVLocation)+ , ("TAGame.CarComponent_FlipCar_TA:FlipCarTime", AVFloat)+ , ("TAGame.CarComponent_FlipCar_TA:bFlipRight", AVBoolean)+ , ("TAGame.CarComponent_TA:ReplicatedActive", AVByte)+ , ("TAGame.CarComponent_TA:ReplicatedActivityTime", AVFloat)+ , ("TAGame.CarComponent_TA:Vehicle", AVFlaggedInt)+ , ("TAGame.Car_TA:AddedBallForceMultiplier", AVFloat)+ , ("TAGame.Car_TA:AddedCarForceMultiplier", AVFloat)+ , ("TAGame.Car_TA:AttachedPickup", AVFlaggedInt)+ , ("TAGame.Car_TA:ReplicatedDemolish", AVDemolish)+ , ("TAGame.Car_TA:TeamPaint", AVTeamPaint)+ , ("TAGame.CrowdActor_TA:GameEvent", AVFlaggedInt)+ , ("TAGame.CrowdActor_TA:ModifiedNoise", AVFloat)+ , ("TAGame.CrowdActor_TA:ReplicatedCountDownNumber", AVInt)+ , ("TAGame.CrowdActor_TA:ReplicatedOneShotSound", AVFlaggedInt)+ , ("TAGame.CrowdActor_TA:ReplicatedRoundCountDownNumber", AVInt)+ , ("TAGame.CrowdManager_TA:GameEvent", AVFlaggedInt)+ , ("TAGame.CrowdManager_TA:ReplicatedGlobalOneShotSound", AVFlaggedInt)+ , ("TAGame.GRI_TA:NewDedicatedServerIP", AVString)+ , ("TAGame.GameEvent_SoccarPrivate_TA:MatchSettings", AVPrivateMatchSettings)+ , ("TAGame.GameEvent_Soccar_TA:ReplicatedMusicStinger", AVMusicStinger)+ , ("TAGame.GameEvent_Soccar_TA:ReplicatedScoredOnTeam", AVByte)+ , ("TAGame.GameEvent_Soccar_TA:RoundNum", AVInt)+ , ("TAGame.GameEvent_Soccar_TA:SecondsRemaining", AVInt)+ , ("TAGame.GameEvent_Soccar_TA:SubRulesArchetype", AVFlaggedInt)+ , ("TAGame.GameEvent_Soccar_TA:bBallHasBeenHit", AVBoolean)+ , ("TAGame.GameEvent_Soccar_TA:bOverTime", AVBoolean)+ , ("TAGame.GameEvent_TA:BotSkill", AVInt)+ , ("TAGame.GameEvent_TA:GameMode", AVGameMode)+ , ("TAGame.GameEvent_TA:MatchTypeClass", AVFlaggedInt)+ , ("TAGame.GameEvent_TA:ReplicatedGameStateTimeRemaining", AVInt)+ , ("TAGame.GameEvent_TA:ReplicatedStateIndex", AVByte)+ , ("TAGame.GameEvent_TA:ReplicatedStateName", AVInt)+ , ("TAGame.GameEvent_TA:bCanVoteToForfeit", AVBoolean)+ , ("TAGame.GameEvent_TA:bHasLeaveMatchPenalty", AVBoolean)+ , ("TAGame.GameEvent_Team_TA:MaxTeamSize", AVInt)+ , ("TAGame.PRI_TA:CameraPitch", AVByte)+ , ("TAGame.PRI_TA:CameraSettings", AVCamSettings)+ , ("TAGame.PRI_TA:CameraYaw", AVByte)+ , ("TAGame.PRI_TA:ClientLoadout", AVLoadout)+ , ("TAGame.PRI_TA:ClientLoadoutOnline", AVLoadoutOnline)+ , ("TAGame.PRI_TA:ClientLoadouts", AVLoadouts)+ , ("TAGame.PRI_TA:ClientLoadoutsOnline", AVLoadoutsOnline)+ , ("TAGame.PRI_TA:MatchAssists", AVInt)+ , ("TAGame.PRI_TA:MatchGoals", AVInt)+ , ("TAGame.PRI_TA:MatchSaves", AVInt)+ , ("TAGame.PRI_TA:MatchScore", AVInt)+ , ("TAGame.PRI_TA:MatchShots", AVInt)+ , ("TAGame.PRI_TA:PartyLeader", AVPartyLeader)+ , ("TAGame.PRI_TA:PawnType", AVByte)+ , ("TAGame.PRI_TA:PersistentCamera", AVFlaggedInt)+ , ("TAGame.PRI_TA:ReplicatedGameEvent", AVFlaggedInt)+ , ("TAGame.PRI_TA:Title", AVInt)+ , ("TAGame.PRI_TA:TotalXP", AVInt)+ , ("TAGame.PRI_TA:bIsInSplitScreen", AVBoolean)+ , ("TAGame.PRI_TA:bMatchMVP", AVBoolean)+ , ("TAGame.PRI_TA:bOnlineLoadoutSet", AVBoolean)+ , ("TAGame.PRI_TA:bOnlineLoadoutsSet", AVBoolean)+ , ("TAGame.PRI_TA:bReady", AVBoolean)+ , ("TAGame.PRI_TA:bUsingBehindView", AVBoolean)+ , ("TAGame.PRI_TA:bUsingSecondaryCamera", AVBoolean)+ , ("TAGame.RBActor_TA:ReplicatedRBState", AVRigidBodyState)+ , ("TAGame.RBActor_TA:WeldedInfo", AVWeldedInfo)+ , ("TAGame.RBActor_TA:bFrozen", AVBoolean)+ , ("TAGame.RBActor_TA:bIgnoreSyncing", AVBoolean)+ , ("TAGame.RBActor_TA:bReplayActor", AVBoolean)+ , ("TAGame.SpecialPickup_BallFreeze_TA:RepOrigSpeed", AVFloat)+ , ("TAGame.SpecialPickup_BallVelcro_TA:AttachTime", AVFloat)+ , ("TAGame.SpecialPickup_BallVelcro_TA:BreakTime", AVFloat)+ , ("TAGame.SpecialPickup_BallVelcro_TA:bBroken", AVBoolean)+ , ("TAGame.SpecialPickup_BallVelcro_TA:bHit", AVBoolean)+ , ("TAGame.SpecialPickup_Targeted_TA:Targeted", AVFlaggedInt)+ , ("TAGame.Team_Soccar_TA:GameScore", AVInt)+ , ("TAGame.Team_TA:CustomTeamName", AVString)+ , ("TAGame.Team_TA:GameEvent", AVFlaggedInt)+ , ("TAGame.Team_TA:LogoData", AVFlaggedInt)+ , ("TAGame.VehiclePickup_TA:ReplicatedPickupData", AVPickup)+ , ("TAGame.Vehicle_TA:ReplicatedSteer", AVByte)+ , ("TAGame.Vehicle_TA:ReplicatedThrottle", AVByte)+ , ("TAGame.Vehicle_TA:bDriving", AVBoolean)+ , ("TAGame.Vehicle_TA:bReplicatedHandbrake", AVBoolean)+ ]++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+ ]
+ library/Rattletrap/Dictionary.hs view
@@ -0,0 +1,33 @@+module Rattletrap.Dictionary where++import Rattletrap.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 elements) =+ mapM_+ (\(key, maybeValue) -> do+ putText key+ case maybeValue of+ Nothing -> pure ()+ Just value -> putValue value)+ elements++isNoneKey :: Text -> Bool+isNoneKey text = filter (/= '\x00') (textToString text) == "None"
+ library/Rattletrap/Float32.hs view
@@ -0,0 +1,33 @@+module Rattletrap.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))
+ library/Rattletrap/Frame.hs view
@@ -0,0 +1,61 @@+module Rattletrap.Frame where++import Rattletrap.ActorMap+import Rattletrap.ClassAttributeMap+import Rattletrap.Float32+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 = do+ maybeFrame <-+ if numFrames > 0+ then getFrame version classAttributeMap actorMap+ else pure Nothing+ case maybeFrame of+ Nothing -> pure ([], actorMap)+ Just (frame, newActorMap) -> do+ (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 (Maybe (Frame, ActorMap))+getFrame version classAttributeMap actorMap = do+ time <- getFloat32Bits+ delta <- getFloat32Bits+ (replications, newActorMap) <-+ getReplications version classAttributeMap actorMap+ pure+ (Just+ ( Frame+ { frameTime = time+ , frameDelta = delta+ , frameReplications = replications+ }+ , newActorMap))++putFrame :: Frame -> BinaryBit.BitPut ()+putFrame frame = do+ putFloat32Bits (frameTime frame)+ putFloat32Bits (frameDelta frame)+ putReplications (frameReplications frame)
+ library/Rattletrap/Header.hs view
@@ -0,0 +1,36 @@+module Rattletrap.Header where++import Rattletrap.Dictionary+import Rattletrap.Property+import Rattletrap.Text+import Rattletrap.Word32++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+ { headerEngineVersion = engineVersion+ , headerLicenseeVersion = licenseeVersion+ , headerLabel = label+ , headerProperties = properties+ }++putHeader :: Header -> Binary.Put+putHeader header = do+ putWord32 (headerEngineVersion header)+ putWord32 (headerLicenseeVersion header)+ putText (headerLabel header)+ putDictionary putProperty (headerProperties header)
+ library/Rattletrap/Initialization.hs view
@@ -0,0 +1,39 @@+module Rattletrap.Initialization where++import Rattletrap.Int8Vector+import Rattletrap.Vector++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+ {initializationLocation = location, initializationRotation = 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
+ library/Rattletrap/Int32.hs view
@@ -0,0 +1,33 @@+module Rattletrap.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 int32) = Binary.putInt32le 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))
+ library/Rattletrap/Int8.hs view
@@ -0,0 +1,32 @@+module Rattletrap.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))
+ library/Rattletrap/Int8Vector.hs view
@@ -0,0 +1,42 @@+module Rattletrap.Int8Vector where++import Rattletrap.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 {int8VectorX = x, int8VectorY = y, int8VectorZ = 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
+ library/Rattletrap/KeyFrame.hs view
@@ -0,0 +1,27 @@+module Rattletrap.KeyFrame where++import Rattletrap.Float32+import Rattletrap.Word32++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+ {keyFrameTime = time, keyFrameFrame = frame, keyFramePosition = position}++putKeyFrame :: KeyFrame -> Binary.Put+putKeyFrame keyFrame = do+ putFloat32 (keyFrameTime keyFrame)+ putWord32 (keyFrameFrame keyFrame)+ putWord32 (keyFramePosition keyFrame)
+ library/Rattletrap/List.hs view
@@ -0,0 +1,21 @@+module Rattletrap.List where++import Rattletrap.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 elements) = do+ putWord32 (Word32 (fromIntegral (length elements)))+ mapM_ putElement elements
+ library/Rattletrap/Mark.hs view
@@ -0,0 +1,22 @@+module Rattletrap.Mark where++import Rattletrap.Text+import Rattletrap.Word32++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 {markValue = value, markFrame = frame}++putMark :: Mark -> Binary.Put+putMark mark = do+ putText (markValue mark)+ putWord32 (markFrame mark)
+ library/Rattletrap/Message.hs view
@@ -0,0 +1,25 @@+module Rattletrap.Message where++import Rattletrap.Text+import Rattletrap.Word32++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 {messageFrame = frame, messageName = name, messageValue = value}++putMessage :: Message -> Binary.Put+putMessage message = do+ putWord32 (messageFrame message)+ putText (messageName message)+ putText (messageValue message)
+ library/Rattletrap/Property.hs view
@@ -0,0 +1,27 @@+module Rattletrap.Property where++import Rattletrap.PropertyValue+import Rattletrap.Text+import Rattletrap.Word64++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 {propertyKind = kind, propertySize = size, propertyValue = value}++putProperty :: Property -> Binary.Put+putProperty property = do+ putText (propertyKind property)+ putWord64 (propertySize property)+ putPropertyValue putProperty (propertyValue property)
+ library/Rattletrap/PropertyValue.hs view
@@ -0,0 +1,74 @@+module Rattletrap.PropertyValue where++import Rattletrap.Dictionary+import Rattletrap.Float32+import Rattletrap.Int32+import Rattletrap.List+import Rattletrap.Text+import Rattletrap.Word64+import Rattletrap.Word8++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
+ library/Rattletrap/RemoteId.hs view
@@ -0,0 +1,43 @@+module Rattletrap.RemoteId where++import Rattletrap.Word64+import Rattletrap.Word8++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
+ library/Rattletrap/Replay.hs view
@@ -0,0 +1,55 @@+module Rattletrap.Replay where++import Rattletrap.Content+import Rattletrap.Crc+import Rattletrap.Dictionary+import Rattletrap.Header+import Rattletrap.Int32+import Rattletrap.Property+import Rattletrap.PropertyValue+import Rattletrap.Text+import Rattletrap.Word32++import qualified Data.Binary as Binary+import qualified Data.Binary.Put as Binary+import qualified Data.ByteString.Lazy as ByteString++data Replay = Replay+ { replayHeader :: Header+ , replayContent :: Content+ } deriving (Eq, Ord, Show)++getReplay :: Binary.Get Replay+getReplay = do+ _headerSize <- getWord32+ _headerCrc <- getWord32+ header <- getHeader+ _contentSize <- getWord32+ _contentCrc <- getWord32+ let majorVersion = fromIntegral (word32Value (headerEngineVersion header))+ let minorVersion = fromIntegral (word32Value (headerLicenseeVersion header))+ let version = (majorVersion, minorVersion)+ let numFrames =+ case lookup+ (stringToText "NumFrames")+ (dictionaryValue (headerProperties header)) of+ Just (Just (Property _ _ (IntProperty int32))) ->+ fromIntegral (int32Value int32)+ _ -> 0+ content <- getContent version numFrames+ pure Replay {replayHeader = header, replayContent = content}++putReplay :: Replay -> Binary.Put+putReplay replay = do+ let header = Binary.runPut (putHeader (replayHeader replay))+ let headerSize = ByteString.length header+ let headerCrc = getCrc32 header+ putWord32 (Word32 (fromIntegral headerSize))+ putWord32 (Word32 headerCrc)+ Binary.putLazyByteString header+ let content = Binary.runPut (putContent (replayContent replay))+ let contentSize = ByteString.length content+ let contentCrc = getCrc32 content+ putWord32 (Word32 (fromIntegral contentSize))+ putWord32 (Word32 contentCrc)+ Binary.putLazyByteString content
+ library/Rattletrap/Replication.hs view
@@ -0,0 +1,61 @@+module Rattletrap.Replication where++import Rattletrap.ActorMap+import Rattletrap.ClassAttributeMap+import Rattletrap.CompressedWord+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+ {replicationActorId = actorId, replicationValue = value}+ , newActorMap))++putReplication :: Replication -> BinaryBit.BitPut ()+putReplication replication = do+ BinaryBit.putBool True+ putCompressedWord (replicationActorId replication)+ putReplicationValue (replicationValue replication)++maxActorId :: Word+maxActorId = 1023
+ library/Rattletrap/ReplicationValue.hs view
@@ -0,0 +1,64 @@+module Rattletrap.ReplicationValue+ ( module Rattletrap.ReplicationValue+ , module Export+ ) where++import Rattletrap.ReplicationValue.Destroyed as Export+import Rattletrap.ReplicationValue.Spawned as Export+import Rattletrap.ReplicationValue.Updated as Export++import Rattletrap.ActorMap+import Rattletrap.ClassAttributeMap+import Rattletrap.CompressedWord++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++data ReplicationValue+ = SpawnedReplication SpawnedReplicationValue+ | UpdatedReplication UpdatedReplicationValue+ | DestroyedReplication DestroyedReplicationValue+ 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) <-+ getSpawnedReplicationValue classAttributeMap actorMap actorId+ pure (SpawnedReplication x, newActorMap)+ else do+ x <-+ getUpdatedReplicationValue+ version+ classAttributeMap+ actorMap+ actorId+ pure (UpdatedReplication x, actorMap)+ else do+ x <- getDestroyedReplicationValue+ pure (DestroyedReplication x, actorMap)++putReplicationValue :: ReplicationValue -> BinaryBit.BitPut ()+putReplicationValue value =+ case value of+ SpawnedReplication x -> do+ BinaryBit.putBool True+ BinaryBit.putBool True+ putSpawnedReplicationValue x+ UpdatedReplication x -> do+ BinaryBit.putBool True+ BinaryBit.putBool False+ putUpdatedReplicationValue x+ DestroyedReplication x -> do+ BinaryBit.putBool False+ putDestroyedReplicationValue x
+ library/Rattletrap/ReplicationValue/Destroyed.hs view
@@ -0,0 +1,14 @@+module Rattletrap.ReplicationValue.Destroyed where++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++data DestroyedReplicationValue =+ DestroyedReplicationValue+ deriving (Eq, Ord, Show)++getDestroyedReplicationValue :: BinaryBit.BitGet DestroyedReplicationValue+getDestroyedReplicationValue = pure DestroyedReplicationValue++putDestroyedReplicationValue :: DestroyedReplicationValue -> BinaryBit.BitPut ()+putDestroyedReplicationValue DestroyedReplicationValue = pure ()
+ library/Rattletrap/ReplicationValue/Spawned.hs view
@@ -0,0 +1,45 @@+module Rattletrap.ReplicationValue.Spawned where++import Rattletrap.ActorMap+import Rattletrap.ClassAttributeMap+import Rattletrap.CompressedWord+import Rattletrap.Initialization+import Rattletrap.Word32++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++data SpawnedReplicationValue = SpawnedReplicationValue+ { spawnedReplicationValueFlag :: Bool+ , spawnedReplicationValueObjectId :: Word32+ , spawnedReplicationValueInitialization :: Initialization+ } deriving (Eq, Ord, Show)++getSpawnedReplicationValue+ :: ClassAttributeMap+ -> ActorMap+ -> CompressedWord+ -> BinaryBit.BitGet (SpawnedReplicationValue, ActorMap)+getSpawnedReplicationValue classAttributeMap actorMap actorId = do+ flag <- BinaryBit.getBool+ objectId <- getWord32Bits+ let newActorMap = updateActorMap actorId objectId actorMap+ case getObjectName classAttributeMap objectId of+ Nothing -> fail ("could not get object name for id " ++ show objectId)+ Just objectName ->+ case getClassName objectName of+ Nothing ->+ fail ("could not get class name for object " ++ show objectName)+ Just className -> do+ let hasLocation = classHasLocation className+ let hasRotation = classHasRotation className+ initialization <- getInitialization hasLocation hasRotation+ pure+ (SpawnedReplicationValue flag objectId initialization, newActorMap)++putSpawnedReplicationValue :: SpawnedReplicationValue -> BinaryBit.BitPut ()+putSpawnedReplicationValue spawnedReplicationValue = do+ BinaryBit.putBool (spawnedReplicationValueFlag spawnedReplicationValue)+ putWord32Bits (spawnedReplicationValueObjectId spawnedReplicationValue)+ putInitialization+ (spawnedReplicationValueInitialization spawnedReplicationValue)
+ library/Rattletrap/ReplicationValue/Updated.hs view
@@ -0,0 +1,27 @@+module Rattletrap.ReplicationValue.Updated where++import Rattletrap.ActorMap+import Rattletrap.Attribute+import Rattletrap.ClassAttributeMap+import Rattletrap.CompressedWord++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++data UpdatedReplicationValue = UpdatedReplicationValue+ { updatedReplicationValueAttributes :: [Attribute]+ } deriving (Eq, Ord, Show)++getUpdatedReplicationValue+ :: (Int, Int)+ -> ClassAttributeMap+ -> ActorMap+ -> CompressedWord+ -> BinaryBit.BitGet UpdatedReplicationValue+getUpdatedReplicationValue version classAttributeMap actorMap actorId = do+ attributes <- getAttributes version classAttributeMap actorMap actorId+ pure (UpdatedReplicationValue attributes)++putUpdatedReplicationValue :: UpdatedReplicationValue -> BinaryBit.BitPut ()+putUpdatedReplicationValue updatedReplicationValue =+ putAttributes (updatedReplicationValueAttributes updatedReplicationValue)
+ library/Rattletrap/Text.hs view
@@ -0,0 +1,87 @@+module Rattletrap.Text where++import Rattletrap.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 (Int32 size) =+ if size == 0x05000000+ then 8+ else if size < 0+ then (-2 * fromIntegral size)+ else fromIntegral size++getTextDecoder :: Int32 -> ByteString.ByteString -> Text.Text+getTextDecoder (Int32 size) bytes =+ let decode =+ if size < 0+ then Encoding.decodeUtf16LE+ else Encoding.decodeLatin1+ in decode (ByteString.toStrict bytes)++getTextEncoder :: Int32 -> Text.Text -> ByteString.ByteString+getTextEncoder (Int32 size) text =+ if size < 0+ then ByteString.fromStrict (Encoding.encodeUtf16LE text)+ else encodeLatin1 text++encodeLatin1 :: Text.Text -> ByteString.ByteString+encodeLatin1 text = ByteString.pack (Text.unpack text)
+ library/Rattletrap/Utility.hs view
@@ -0,0 +1,33 @@+module Rattletrap.Utility where++import qualified Data.Bits as Bits+import qualified Data.ByteString.Lazy as ByteString+import qualified Data.Word as Word+import qualified Text.Regex as Regex++padBytes+ :: Integral a+ => a -> ByteString.ByteString -> ByteString.ByteString+padBytes size bytes =+ ByteString.concat+ [ bytes+ , ByteString.replicate (fromIntegral size - ByteString.length bytes) 0x00+ ]++replace :: String -> String -> String -> String+replace needle replacement haystack =+ Regex.subRegex (Regex.mkRegex needle) haystack replacement++reverseByte :: Word.Word8 -> Word.Word8+reverseByte byte =+ Bits.shiftR (byte Bits..&. Bits.bit 7) 7 ++ Bits.shiftR (byte Bits..&. Bits.bit 6) 5 ++ Bits.shiftR (byte Bits..&. Bits.bit 5) 3 ++ Bits.shiftR (byte Bits..&. Bits.bit 4) 1 ++ Bits.shiftL (byte Bits..&. Bits.bit 3) 1 ++ Bits.shiftL (byte Bits..&. Bits.bit 2) 3 ++ Bits.shiftL (byte Bits..&. Bits.bit 1) 5 ++ Bits.shiftL (byte Bits..&. Bits.bit 0) 7++reverseBytes :: ByteString.ByteString -> ByteString.ByteString+reverseBytes = ByteString.map reverseByte
+ library/Rattletrap/Vector.hs view
@@ -0,0 +1,31 @@+module Rattletrap.Vector where++import Rattletrap.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+ {vectorBitSize = bitSize, vectorDx = dx, vectorDy = dy, vectorDz = dz}++putVector :: Vector -> BinaryBit.BitPut ()+putVector vector = do+ putCompressedWord (vectorBitSize vector)+ putCompressedWord (vectorDx vector)+ putCompressedWord (vectorDy vector)+ putCompressedWord (vectorDz vector)
+ library/Rattletrap/Word32.hs view
@@ -0,0 +1,33 @@+module Rattletrap.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 word32) = Binary.putWord32le 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))
+ library/Rattletrap/Word64.hs view
@@ -0,0 +1,33 @@+module Rattletrap.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))
+ library/Rattletrap/Word8.hs view
@@ -0,0 +1,33 @@+module Rattletrap.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 word8) = Binary.putWord8 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))
+ rattletrap.cabal view
@@ -0,0 +1,140 @@+name: rattletrap+version: 0.1.0+cabal-version: >=1.10+build-type: Simple+license: MIT+license-file: LICENSE.markdown+maintainer: Taylor Fausak+synopsis: Parse and generate Rocket League replays.+description:+ Rattletrap parses and generates Rocket League replays.+category: Game+extra-source-files:+ HLint.hs+ README.markdown++library+ exposed-modules:+ Rattletrap+ Rattletrap.ActorMap+ Rattletrap.Attribute+ Rattletrap.AttributeMapping+ Rattletrap.AttributeValue+ Rattletrap.AttributeValue.Boolean+ Rattletrap.AttributeValue.Byte+ Rattletrap.AttributeValue.CamSettings+ Rattletrap.AttributeValue.Demolish+ Rattletrap.AttributeValue.Enum+ Rattletrap.AttributeValue.Explosion+ Rattletrap.AttributeValue.FlaggedInt+ Rattletrap.AttributeValue.Float+ Rattletrap.AttributeValue.GameMode+ Rattletrap.AttributeValue.Int+ Rattletrap.AttributeValue.Loadout+ Rattletrap.AttributeValue.LoadoutOnline+ Rattletrap.AttributeValue.Loadouts+ Rattletrap.AttributeValue.LoadoutsOnline+ Rattletrap.AttributeValue.Location+ Rattletrap.AttributeValue.MusicStinger+ Rattletrap.AttributeValue.PartyLeader+ Rattletrap.AttributeValue.Pickup+ Rattletrap.AttributeValue.PrivateMatchSettings+ Rattletrap.AttributeValue.QWord+ Rattletrap.AttributeValue.Reservation+ Rattletrap.AttributeValue.RigidBodyState+ Rattletrap.AttributeValue.String+ Rattletrap.AttributeValue.TeamPaint+ Rattletrap.AttributeValue.UniqueId+ Rattletrap.AttributeValue.WeldedInfo+ Rattletrap.AttributeValueType+ Rattletrap.Cache+ Rattletrap.ClassAttributeMap+ Rattletrap.ClassMapping+ Rattletrap.CompressedWord+ Rattletrap.CompressedWordVector+ Rattletrap.Content+ Rattletrap.Crc+ Rattletrap.Data+ Rattletrap.Dictionary+ Rattletrap.Float32+ Rattletrap.Frame+ Rattletrap.Header+ Rattletrap.Initialization+ Rattletrap.Int32+ Rattletrap.Int8+ Rattletrap.Int8Vector+ Rattletrap.KeyFrame+ Rattletrap.List+ Rattletrap.Mark+ Rattletrap.Message+ Rattletrap.Property+ Rattletrap.PropertyValue+ Rattletrap.RemoteId+ Rattletrap.Replay+ Rattletrap.Replication+ Rattletrap.ReplicationValue+ Rattletrap.ReplicationValue.Destroyed+ Rattletrap.ReplicationValue.Spawned+ Rattletrap.ReplicationValue.Updated+ Rattletrap.Text+ Rattletrap.Utility+ Rattletrap.Vector+ Rattletrap.Word32+ Rattletrap.Word64+ Rattletrap.Word8+ build-depends:+ base >=4.9.0.0 && <4.10,+ bimap >=0.3.2 && <0.4,+ binary >=0.8.3.0 && <0.9,+ binary-bits ==0.5.*,+ bytestring >=0.10.8.1 && <0.11,+ containers >=0.5.7.1 && <0.6,+ data-binary-ieee754 >=0.4.4 && <0.5,+ regex-compat >=0.95.1 && <0.96,+ text >=1.2.2.1 && <1.3,+ vector >=0.11.0.0 && <0.12+ default-language: Haskell2010+ hs-source-dirs: library+ ghc-options: -Wall++executable rattletrap+ main-is: Main.hs+ build-depends:+ aeson >=0.11.2.1 && <0.12,+ aeson-casing >=0.1.0.5 && <0.2,+ base >=4.9.0.0 && <4.10,+ binary >=0.8.3.0 && <0.9,+ bytestring >=0.10.8.1 && <0.11,+ rattletrap >=0.1.0 && <0.2,+ template-haskell >=2.11.0.0 && <2.12+ default-language: Haskell2010+ hs-source-dirs: executable+ ghc-options: -Wall -rtsopts -threaded -with-rtsopts=-N++test-suite lint+ type: exitcode-stdio-1.0+ main-is: Lint.hs+ build-depends:+ base >=4.9.0.0 && <4.10,+ hlint >=1.9.35 && <1.10+ default-language: Haskell2010+ hs-source-dirs: test+ other-modules:+ Test+ ghc-options: -Wall -rtsopts -threaded -with-rtsopts=-N+test-suite test+ type: exitcode-stdio-1.0+ main-is: Test.hs+ build-depends:+ base >=4.9.0.0 && <4.10,+ binary >=0.8.3.0 && <0.9,+ bytestring >=0.10.8.1 && <0.11,+ filepath >=1.4.1.0 && <1.5,+ rattletrap >=0.1.0 && <0.2,+ tasty >=0.11.0.4 && <0.12,+ tasty-hspec >=1.1.3 && <1.2+ default-language: Haskell2010+ hs-source-dirs: test+ other-modules:+ Lint+ ghc-options: -Wall -rtsopts -threaded -with-rtsopts=-N
+ test/Lint.hs view
@@ -0,0 +1,13 @@+module Main+ ( main+ ) where++import qualified Language.Haskell.HLint3 as HLint+import qualified System.Exit as Exit++main :: IO ()+main = do+ ideas <- HLint.hlint ["lint", "--cross", "."]+ if null ideas+ then Exit.exitSuccess+ else Exit.exitFailure
+ test/Test.hs view
@@ -0,0 +1,88 @@+module Main+ ( main+ ) where++import qualified Data.Binary.Get as Binary+import qualified Data.Binary.Put as Binary+import qualified Data.ByteString.Lazy as ByteString+import qualified Rattletrap+import qualified System.FilePath as FilePath+import qualified Test.Tasty as Tasty+import qualified Test.Tasty.Hspec as Hspec++main :: IO ()+main = do+ tests <- Hspec.testSpec "rattletrap" (Hspec.parallel spec)+ Tasty.defaultMain tests++spec :: Hspec.Spec+spec = Hspec.describe "Rattletrap" (mapM_ (uncurry itCanGetAndPut) replays)++itCanGetAndPut :: String -> String -> Hspec.Spec+itCanGetAndPut uuid description =+ Hspec.it+ (uuid ++ ": a replay with " ++ description)+ (do let file = pathToReplay uuid+ (input, _, output) <- getAndPut file+ Hspec.shouldBe (output == input) True)++pathToReplay :: String -> FilePath+pathToReplay uuid =+ FilePath.joinPath ["test", "replays", FilePath.addExtension uuid ".replay"]++getAndPut :: FilePath+ -> IO (ByteString.ByteString, Rattletrap.Replay, ByteString.ByteString)+getAndPut file = do+ input <- ByteString.readFile file+ let replay = Binary.runGet Rattletrap.getReplay input+ let output = Binary.runPut (Rattletrap.putReplay replay)+ pure (input, replay, output)++replays :: [(String, String)]+replays =+ [ ("00080014003600090000036E0F65CCEB", "a flip time")+ , ("07E925B1423653D44CB8B4B2524792C1", "a game mode before Neo Tokyo")+ , ("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")+ , ("211466D04B983F5A33CC2FA1D5928672", "a match save")+ , ("22BACD794ABE7B92E50E9CBDBD9C59CE", "a vote to forfeit")+ , ("29F582C34A65EB34D358A784CBE3C189", "frames")+ , ("2F817C8C44859C762980AE85B1862A31", "splitscreen players")+ , ("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")+ , ("7109EB9846D303E54B7ACBA792036213", "a boost modifier")+ , ("7BF6073F4614CE0A438994B9A260DA6A", "an online loadouts attribute")+ , ("89CBA30E46FA5385BDD35DA4285D4D2E", "remote user data")+ , ("A128B3AB45D5A18E3EF9CF93C9576BCE", "a round count down")+ , ("A52F804845573D8DA65E97BF59026A43", "some more mutators")+ , ("A6711CE74272B2E663DCC9A200A218E3", "a waiting player")+ , ("A7F001A1417A19BFA8C90990D8F7C2FF", "a ready attribute")+ -- , ("B82DDB624C393A4A425E68AB40DC2450", "TODO")+ , ("B9F9B87D4A9D0A3D25D4EC91C0401DE2", "a party leader")+ , ("C14F7E0E4D9B5E6BE9AD5D8ED56B174C", "some mutators")+ , ("C375E0EC4971B506C51678B465D35AE9", "some UTF-16 text")+ , ("C80A9959479CFE51673B3A889D717554", "some Latin-1 text")+ , ("C8372B1345B1803DEF039F815DBD802D", "a spectator")+ , ("CB5B422A4424262B2877E9A121329EF2", "a player using behind view")+ , ("D428F81646A98C25902CE988AE5C14C8", "a private hockey match")+ , ("D7FB197A451D69075A0C99A2F49A4053", "an explosion attribute")+ , ("DCB3A6B94A9DBE46FDE5EAA9B012F6C8", "a pawn type attribute")+ , ("EAE311E84BA35B590A6FDBA6DD4F2FEB", "an actor/object ID collision")+ , ("F299F176491554B11E34AB91CA76B2CE", "a location attribute")+ , ("F811C1D24888015E23B598AD8628C742", "no frames")+ , ("FDC79DA84DD463D4BCCE6B892829AC88", "an MVP")+ ]