packages feed

rattletrap 1.0.0 → 2.0.0

raw patch · 252 files changed

+3944/−3674 lines, 252 filesbinary-addedPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Rattletrap.Attribute: [attribute_name] :: Attribute -> Text
- Rattletrap.ClassAttributeMap: [classAttributeMapClassMap] :: ClassAttributeMap -> Bimap Word32 Text
- Rattletrap.Content: [contentTrailingBits] :: Content -> [Bool]
- Rattletrap.Primitive.Dictionary: newtype Dictionary a
- Rattletrap.Primitive.Text: [textSize] :: Text -> Int32
- Rattletrap.Primitive.Text: data Text
- Rattletrap.Primitive.Text: encodeLatin1 :: Text -> ByteString
- Rattletrap.Primitive.Vector: [vectorBitSize] :: Vector -> CompressedWord
- Rattletrap.Primitive.Vector: [vectorDx] :: Vector -> CompressedWord
- Rattletrap.Primitive.Vector: [vectorDy] :: Vector -> CompressedWord
- Rattletrap.Primitive.Vector: [vectorDz] :: Vector -> CompressedWord
- Rattletrap.Replication: maxActorId :: Word
- Rattletrap.Replication.Spawned: [spawnedReplication_className] :: SpawnedReplication -> Text
- Rattletrap.Replication.Spawned: [spawnedReplication_objectName] :: SpawnedReplication -> Text
- Rattletrap.Utility: getRemainingBits :: BitGet [Bool]
+ Rattletrap.Attribute: [attributeName] :: Attribute -> Text
+ Rattletrap.ClassAttributeMap: [classAttributeMapNameMap] :: ClassAttributeMap -> IntMap Text
+ Rattletrap.ClassAttributeMap: getName :: IntMap Text -> Word32 -> Maybe Text
+ Rattletrap.ClassAttributeMap: makeNameMap :: List Text -> IntMap Text
+ Rattletrap.Header: getMaxChannels :: Header -> Word
+ Rattletrap.Primitive.Dictionary: [dictionaryKeys] :: Dictionary a -> [Text]
+ Rattletrap.Primitive.Dictionary: [dictionaryLastKey] :: Dictionary a -> Text
+ Rattletrap.Primitive.Dictionary: data Dictionary a
+ Rattletrap.Primitive.Dictionary: getElement :: Get a -> Get (Text, Maybe a)
+ Rattletrap.Primitive.Dictionary: getElements :: Get a -> Get ([(Text, a)], Text)
+ Rattletrap.Primitive.Text: addNull :: Text -> Text
+ Rattletrap.Primitive.Text: dropNull :: Text -> Text
+ Rattletrap.Primitive.Text: newtype Text
+ Rattletrap.Primitive.Text: normalizeTextSize :: Integral a => Int32 -> a
+ Rattletrap.Primitive.Vector: [vectorBias] :: Vector -> Word
+ Rattletrap.Primitive.Vector: [vectorX] :: Vector -> Int
+ Rattletrap.Primitive.Vector: [vectorY] :: Vector -> Int
+ Rattletrap.Primitive.Vector: [vectorZ] :: Vector -> Int
+ Rattletrap.Replication.Spawned: [spawnedReplicationClassName] :: SpawnedReplication -> Text
+ Rattletrap.Replication.Spawned: [spawnedReplicationNameIndex] :: SpawnedReplication -> Maybe Word32
+ Rattletrap.Replication.Spawned: [spawnedReplicationName] :: SpawnedReplication -> Maybe Text
+ Rattletrap.Replication.Spawned: [spawnedReplicationObjectName] :: SpawnedReplication -> Text
+ Rattletrap.Replication.Spawned: lookupName :: Monad m => ClassAttributeMap -> Maybe Word32 -> m (Maybe Text)
+ Rattletrap.Utility: encodeLatin1 :: Text -> ByteString
- Rattletrap.ClassAttributeMap: ClassAttributeMap :: Map Word32 Text -> Bimap Word32 Text -> Map Word32 Word32 -> Map Word32 (Map Word32 Word32) -> ClassAttributeMap
+ Rattletrap.ClassAttributeMap: ClassAttributeMap :: Map Word32 Text -> Map Word32 Word32 -> Map Word32 (Map Word32 Word32) -> IntMap Text -> ClassAttributeMap
- Rattletrap.ClassAttributeMap: makeClassAttributeMap :: List Text -> List ClassMapping -> List Cache -> ClassAttributeMap
+ Rattletrap.ClassAttributeMap: makeClassAttributeMap :: List Text -> List ClassMapping -> List Cache -> List Text -> ClassAttributeMap
- Rattletrap.Content: Content :: List Text -> List KeyFrame -> Word32 -> [Frame] -> [Bool] -> List Message -> List Mark -> List Text -> List Text -> List Text -> List ClassMapping -> List Cache -> Content
+ Rattletrap.Content: Content :: List Text -> List KeyFrame -> Word32 -> [Frame] -> List Message -> List Mark -> List Text -> List Text -> List Text -> List ClassMapping -> List Cache -> Content
- Rattletrap.Content: getContent :: (Int, Int) -> Int -> Get Content
+ Rattletrap.Content: getContent :: (Int, Int) -> Int -> Word -> Get Content
- Rattletrap.Frame: getFrame :: (Int, Int) -> ClassAttributeMap -> ActorMap -> BitGet (Frame, ActorMap)
+ Rattletrap.Frame: getFrame :: (Int, Int) -> Word -> ClassAttributeMap -> ActorMap -> BitGet (Frame, ActorMap)
- Rattletrap.Frame: getFrames :: (Int, Int) -> Int -> ClassAttributeMap -> ActorMap -> BitGet ([Frame], ActorMap)
+ Rattletrap.Frame: getFrames :: (Int, Int) -> Int -> Word -> ClassAttributeMap -> ActorMap -> BitGet ([Frame], ActorMap)
- Rattletrap.Header: getMajorVersion :: (Integral a) => Header -> a
+ Rattletrap.Header: getMajorVersion :: Header -> Int
- Rattletrap.Header: getMinorVersion :: (Integral a) => Header -> a
+ Rattletrap.Header: getMinorVersion :: Header -> Int
- Rattletrap.Header: getNumFrames :: (Integral a) => Header -> a
+ Rattletrap.Header: getNumFrames :: Header -> Int
- Rattletrap.Header: getVersion :: (Integral a, Integral b) => Header -> (a, b)
+ Rattletrap.Header: getVersion :: Header -> (Int, Int)
- Rattletrap.Main: getIO :: Monad m => [FilePath] -> m (IO ByteString, ByteString -> IO ())
+ Rattletrap.Main: getIO :: [FilePath] -> IO (IO ByteString, ByteString -> IO ())
- Rattletrap.Primitive.Dictionary: Dictionary :: [(Text, Maybe a)] -> Dictionary a
+ Rattletrap.Primitive.Dictionary: Dictionary :: [Text] -> Text -> Map Text a -> Dictionary a
- Rattletrap.Primitive.Dictionary: [dictionaryValue] :: Dictionary a -> [(Text, Maybe a)]
+ Rattletrap.Primitive.Dictionary: [dictionaryValue] :: Dictionary a -> Map Text a
- Rattletrap.Primitive.Text: Text :: Int32 -> Text -> Text
+ Rattletrap.Primitive.Text: Text :: Text -> Text
- Rattletrap.Primitive.Text: getTextSize :: Integral a => Int32 -> a
+ Rattletrap.Primitive.Text: getTextSize :: Text -> Int32
- Rattletrap.Primitive.Vector: Vector :: CompressedWord -> CompressedWord -> CompressedWord -> CompressedWord -> Vector
+ Rattletrap.Primitive.Vector: Vector :: Word -> Int -> Int -> Int -> Vector
- Rattletrap.RemoteId: PlayStationId :: [Word8] -> RemoteId
+ Rattletrap.RemoteId: PlayStationId :: Text -> [Word8] -> RemoteId
- Rattletrap.RemoteId: SplitscreenId :: [Word8] -> RemoteId
+ Rattletrap.RemoteId: SplitscreenId :: Word32 -> RemoteId
- Rattletrap.Replication: getReplication :: (Int, Int) -> ClassAttributeMap -> ActorMap -> BitGet (Maybe (Replication, ActorMap))
+ Rattletrap.Replication: getReplication :: (Int, Int) -> Word -> ClassAttributeMap -> ActorMap -> BitGet (Maybe (Replication, ActorMap))
- Rattletrap.Replication: getReplications :: (Int, Int) -> ClassAttributeMap -> ActorMap -> BitGet ([Replication], ActorMap)
+ Rattletrap.Replication: getReplications :: (Int, Int) -> Word -> ClassAttributeMap -> ActorMap -> BitGet ([Replication], ActorMap)
- Rattletrap.Replication.Spawned: SpawnedReplication :: Bool -> Word32 -> Text -> Text -> Initialization -> SpawnedReplication
+ Rattletrap.Replication.Spawned: SpawnedReplication :: Bool -> Maybe Word32 -> Maybe Text -> Word32 -> Text -> Text -> Initialization -> SpawnedReplication
- Rattletrap.Replication.Spawned: getSpawnedReplication :: ClassAttributeMap -> ActorMap -> CompressedWord -> BitGet (SpawnedReplication, ActorMap)
+ Rattletrap.Replication.Spawned: getSpawnedReplication :: (Int, Int) -> ClassAttributeMap -> ActorMap -> CompressedWord -> BitGet (SpawnedReplication, ActorMap)

Files

− HLint.hs
@@ -1,12 +0,0 @@-module HLint where--import "hint" HLint.Default-import "hint" HLint.Dollar--ignore "Eta reduce"--ignore "Use &&&"--ignore "Use <|>"--ignore "Use uncurry"
− executable/Main.hs
@@ -1,5 +0,0 @@-module Main-  ( module Rattletrap-  ) where--import Rattletrap (main)
− library/Rattletrap.hs
@@ -1,58 +0,0 @@-module Rattletrap-  ( module Rattletrap.ActorMap-  , module Rattletrap.Attribute-  , module Rattletrap.AttributeMapping-  , module Rattletrap.AttributeType-  , module Rattletrap.AttributeValue-  , module Rattletrap.Cache-  , module Rattletrap.ClassAttributeMap-  , module Rattletrap.ClassMapping-  , module Rattletrap.Content-  , module Rattletrap.Crc-  , module Rattletrap.Data-  , module Rattletrap.Frame-  , module Rattletrap.Header-  , module Rattletrap.Initialization-  , module Rattletrap.KeyFrame-  , module Rattletrap.Main-  , module Rattletrap.Mark-  , module Rattletrap.Message-  , module Rattletrap.Primitive-  , module Rattletrap.Property-  , module Rattletrap.PropertyValue-  , module Rattletrap.RemoteId-  , module Rattletrap.Replay-  , module Rattletrap.Replication-  , module Rattletrap.ReplicationValue-  , module Rattletrap.Utility-  , module Rattletrap.Version-  ) where--import Rattletrap.ActorMap-import Rattletrap.Attribute-import Rattletrap.AttributeMapping-import Rattletrap.AttributeType-import Rattletrap.AttributeValue-import Rattletrap.Cache-import Rattletrap.ClassAttributeMap-import Rattletrap.ClassMapping-import Rattletrap.Content-import Rattletrap.Crc-import Rattletrap.Data-import Rattletrap.Frame-import Rattletrap.Header-import Rattletrap.Initialization-import Rattletrap.Json ()-import Rattletrap.KeyFrame-import Rattletrap.Main-import Rattletrap.Mark-import Rattletrap.Message-import Rattletrap.Primitive-import Rattletrap.Property-import Rattletrap.PropertyValue-import Rattletrap.RemoteId-import Rattletrap.Replay-import Rattletrap.Replication-import Rattletrap.ReplicationValue-import Rattletrap.Utility-import Rattletrap.Version
− library/Rattletrap/ActorMap.hs
@@ -1,13 +0,0 @@-module Rattletrap.ActorMap where--import Rattletrap.Primitive--import qualified Data.Map.Strict as Map--type ActorMap = Map.Map CompressedWord Word32--makeActorMap :: ActorMap-makeActorMap = Map.empty--updateActorMap :: CompressedWord -> Word32 -> ActorMap -> ActorMap-updateActorMap actorId objectId actorMap = Map.insert actorId objectId actorMap
− library/Rattletrap/Attribute.hs
@@ -1,62 +0,0 @@-module Rattletrap.Attribute where--import Rattletrap.ActorMap-import Rattletrap.AttributeValue-import Rattletrap.ClassAttributeMap-import Rattletrap.Primitive--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--data Attribute = Attribute-  { attributeId :: CompressedWord-  , attribute_name :: Text-  , attributeValue :: AttributeValue-  } deriving (Eq, Ord, Show)--getAttributes-  :: (Int, Int)-  -> ClassAttributeMap-  -> ActorMap-  -> CompressedWord-  -> BinaryBit.BitGet [Attribute]-getAttributes version classAttributeMap actorMap actorId = do-  hasAttribute <- BinaryBit.getBool-  if not hasAttribute-    then pure []-    else do-      attribute <- getAttribute version classAttributeMap actorMap actorId-      attributes <- getAttributes version classAttributeMap actorMap actorId-      pure (attribute : attributes)--putAttributes :: [Attribute] -> BinaryBit.BitPut ()-putAttributes attributes = do-  mapM_ putAttribute attributes-  BinaryBit.putBool False--getAttribute-  :: (Int, Int)-  -> ClassAttributeMap-  -> ActorMap-  -> CompressedWord-  -> BinaryBit.BitGet Attribute-getAttribute version classAttributeMap actorMap actorId =-  case getAttributeMap classAttributeMap actorMap actorId of-    Nothing -> fail ("could not get attribute map for " ++ show actorId)-    Just attributeMap ->-      case getAttributeIdLimit attributeMap of-        Nothing ->-          fail ("could not get attribute ID limit for " ++ show actorId)-        Just limit -> do-          id_ <- getCompressedWord limit-          case getAttributeName classAttributeMap attributeMap id_ of-            Nothing -> fail ("could not get attribute name for " ++ show id_)-            Just name -> do-              value <- getAttributeValue version name-              pure (Attribute id_ name value)--putAttribute :: Attribute -> BinaryBit.BitPut ()-putAttribute attribute = do-  BinaryBit.putBool True-  putCompressedWord (attributeId attribute)-  putAttributeValue (attributeValue attribute)
− library/Rattletrap/Attribute/Boolean.hs
@@ -1,17 +0,0 @@-module Rattletrap.Attribute.Boolean where--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--newtype BooleanAttribute = BooleanAttribute-  { booleanAttributeValue :: Bool-  } deriving (Eq, Ord, Show)--getBooleanAttribute :: BinaryBit.BitGet BooleanAttribute-getBooleanAttribute = do-  value <- BinaryBit.getBool-  pure (BooleanAttribute value)--putBooleanAttribute :: BooleanAttribute -> BinaryBit.BitPut ()-putBooleanAttribute booleanAttribute =-  BinaryBit.putBool (booleanAttributeValue booleanAttribute)
− library/Rattletrap/Attribute/Byte.hs
@@ -1,19 +0,0 @@-module Rattletrap.Attribute.Byte where--import Rattletrap.Primitive--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--newtype ByteAttribute = ByteAttribute-  { byteAttributeValue :: Word8-  } deriving (Eq, Ord, Show)--getByteAttribute :: BinaryBit.BitGet ByteAttribute-getByteAttribute = do-  value <- getWord8Bits-  pure (ByteAttribute value)--putByteAttribute :: ByteAttribute -> BinaryBit.BitPut ()-putByteAttribute byteAttribute =-  putWord8Bits (byteAttributeValue byteAttribute)
− library/Rattletrap/Attribute/CamSettings.hs
@@ -1,36 +0,0 @@-module Rattletrap.Attribute.CamSettings where--import Rattletrap.Primitive--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--data CamSettingsAttribute = CamSettingsAttribute-  { camSettingsAttributeFov :: Float32-  , camSettingsAttributeHeight :: Float32-  , camSettingsAttributeAngle :: Float32-  , camSettingsAttributeDistance :: Float32-  , camSettingsAttributeStiffness :: Float32-  , camSettingsAttributeSwivelSpeed :: Float32-  } deriving (Eq, Ord, Show)--getCamSettingsAttribute :: BinaryBit.BitGet CamSettingsAttribute-getCamSettingsAttribute = do-  fov <- getFloat32Bits-  height <- getFloat32Bits-  angle <- getFloat32Bits-  distance <- getFloat32Bits-  stiffness <- getFloat32Bits-  swivelSpeed <- getFloat32Bits-  pure-    (CamSettingsAttribute fov height angle distance stiffness swivelSpeed)--putCamSettingsAttribute :: CamSettingsAttribute -> BinaryBit.BitPut ()-putCamSettingsAttribute camSettingsAttribute = do-  putFloat32Bits (camSettingsAttributeFov camSettingsAttribute)-  putFloat32Bits (camSettingsAttributeHeight camSettingsAttribute)-  putFloat32Bits (camSettingsAttributeAngle camSettingsAttribute)-  putFloat32Bits (camSettingsAttributeDistance camSettingsAttribute)-  putFloat32Bits (camSettingsAttributeStiffness camSettingsAttribute)-  putFloat32Bits-    (camSettingsAttributeSwivelSpeed camSettingsAttribute)
− library/Rattletrap/Attribute/ClubColors.hs
@@ -1,29 +0,0 @@-module Rattletrap.Attribute.ClubColors where--import Rattletrap.Primitive--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--data ClubColorsAttribute = ClubColorsAttribute-  { clubColorsAttributeBlueFlag :: Bool-  , clubColorsAttributeBlueColor :: Word8-  , clubColorsAttributeOrangeFlag :: Bool-  , clubColorsAttributeOrangeColor :: Word8-  } deriving (Eq, Ord, Show)--getClubColorsAttribute :: BinaryBit.BitGet ClubColorsAttribute-getClubColorsAttribute = do-  blueFlag <- BinaryBit.getBool-  blueColor <- getWord8Bits-  orangeFlag <- BinaryBit.getBool-  orangeColor <- getWord8Bits-  pure (ClubColorsAttribute blueFlag blueColor orangeFlag orangeColor)--putClubColorsAttribute :: ClubColorsAttribute -> BinaryBit.BitPut ()-putClubColorsAttribute clubColorsAttribute = do-  BinaryBit.putBool (clubColorsAttributeBlueFlag clubColorsAttribute)-  putWord8Bits (clubColorsAttributeBlueColor clubColorsAttribute)-  BinaryBit.putBool-    (clubColorsAttributeOrangeFlag clubColorsAttribute)-  putWord8Bits (clubColorsAttributeOrangeColor clubColorsAttribute)
− library/Rattletrap/Attribute/Demolish.hs
@@ -1,41 +0,0 @@-module Rattletrap.Attribute.Demolish where--import Rattletrap.Primitive--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--data DemolishAttribute = DemolishAttribute-  { demolishAttributeAttackerFlag :: Bool-  , demolishAttributeAttackerActorId :: Word32-  , demolishAttributeVictimFlag :: Bool-  , demolishAttributeVictimActorId :: Word32-  , demolishAttributeAttackerVelocity :: Vector-  , demolishAttributeVictimVelocity :: Vector-  } deriving (Eq, Ord, Show)--getDemolishAttribute :: BinaryBit.BitGet DemolishAttribute-getDemolishAttribute = do-  attackerFlag <- BinaryBit.getBool-  attackerActorId <- getWord32Bits-  victimFlag <- BinaryBit.getBool-  victimActorId <- getWord32Bits-  attackerVelocity <- getVector-  victimVelocity <- getVector-  pure-    (DemolishAttribute-       attackerFlag-       attackerActorId-       victimFlag-       victimActorId-       attackerVelocity-       victimVelocity)--putDemolishAttribute :: DemolishAttribute -> BinaryBit.BitPut ()-putDemolishAttribute demolishAttribute = do-  BinaryBit.putBool (demolishAttributeAttackerFlag demolishAttribute)-  putWord32Bits (demolishAttributeAttackerActorId demolishAttribute)-  BinaryBit.putBool (demolishAttributeVictimFlag demolishAttribute)-  putWord32Bits (demolishAttributeVictimActorId demolishAttribute)-  putVector (demolishAttributeAttackerVelocity demolishAttribute)-  putVector (demolishAttributeVictimVelocity demolishAttribute)
− library/Rattletrap/Attribute/Enum.hs
@@ -1,18 +0,0 @@-module Rattletrap.Attribute.Enum where--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit-import qualified Data.Word as Word--newtype EnumAttribute = EnumAttribute-  { enumAttributeValue :: Word.Word16-  } deriving (Eq, Ord, Show)--getEnumAttribute :: BinaryBit.BitGet EnumAttribute-getEnumAttribute = do-  value <- BinaryBit.getWord16be 11-  pure (EnumAttribute value)--putEnumAttribute :: EnumAttribute -> BinaryBit.BitPut ()-putEnumAttribute enumAttribute =-  BinaryBit.putWord16be 11 (enumAttributeValue enumAttribute)
− library/Rattletrap/Attribute/Explosion.hs
@@ -1,24 +0,0 @@-module Rattletrap.Attribute.Explosion where--import Rattletrap.Primitive--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--data ExplosionAttribute = ExplosionAttribute-  { explosionAttributeActorId :: Int32-  , explosionAttributeLocation :: Vector-  } deriving (Eq, Ord, Show)--getExplosionAttribute :: BinaryBit.BitGet ExplosionAttribute-getExplosionAttribute = do-  False <- BinaryBit.getBool-  actorId <- getInt32Bits-  location <- getVector-  pure (ExplosionAttribute actorId location)--putExplosionAttribute :: ExplosionAttribute -> BinaryBit.BitPut ()-putExplosionAttribute explosionAttribute = do-  BinaryBit.putBool False-  putInt32Bits (explosionAttributeActorId explosionAttribute)-  putVector (explosionAttributeLocation explosionAttribute)
− library/Rattletrap/Attribute/FlaggedInt.hs
@@ -1,22 +0,0 @@-module Rattletrap.Attribute.FlaggedInt where--import Rattletrap.Primitive--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--data FlaggedIntAttribute = FlaggedIntAttribute-  { flaggedIntAttributeFlag :: Bool-  , flaggedIntAttributeInt :: Int32-  } deriving (Eq, Ord, Show)--getFlaggedIntAttribute :: BinaryBit.BitGet FlaggedIntAttribute-getFlaggedIntAttribute = do-  flag <- BinaryBit.getBool-  int <- getInt32Bits-  pure (FlaggedIntAttribute flag int)--putFlaggedIntAttribute :: FlaggedIntAttribute -> BinaryBit.BitPut ()-putFlaggedIntAttribute flaggedIntAttribute = do-  BinaryBit.putBool (flaggedIntAttributeFlag flaggedIntAttribute)-  putInt32Bits (flaggedIntAttributeInt flaggedIntAttribute)
− library/Rattletrap/Attribute/Float.hs
@@ -1,19 +0,0 @@-module Rattletrap.Attribute.Float where--import Rattletrap.Primitive--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--newtype FloatAttribute = FloatAttribute-  { floatAttributeValue :: Float32-  } deriving (Eq, Ord, Show)--getFloatAttribute :: BinaryBit.BitGet FloatAttribute-getFloatAttribute = do-  value <- getFloat32Bits-  pure (FloatAttribute value)--putFloatAttribute :: FloatAttribute -> BinaryBit.BitPut ()-putFloatAttribute floatAttribute =-  putFloat32Bits (floatAttributeValue floatAttribute)
− library/Rattletrap/Attribute/GameMode.hs
@@ -1,26 +0,0 @@-module Rattletrap.Attribute.GameMode where--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit-import qualified Data.Word as Word--data GameModeAttribute = GameModeAttribute-  { gameModeAttributeNumBits :: Int-  , gameModeAttributeWord :: Word.Word8-  } deriving (Eq, Ord, Show)--getGameModeAttribute :: (Int, Int)-                          -> BinaryBit.BitGet GameModeAttribute-getGameModeAttribute version = do-  let numBits =-        if version < (868, 12)-          then 2-          else 8-  word <- BinaryBit.getWord8 numBits-  pure (GameModeAttribute numBits word)--putGameModeAttribute :: GameModeAttribute -> BinaryBit.BitPut ()-putGameModeAttribute gameModeAttribute = do-  let numBits = gameModeAttributeNumBits gameModeAttribute-  let word = gameModeAttributeWord gameModeAttribute-  BinaryBit.putWord8 numBits word
− library/Rattletrap/Attribute/Int.hs
@@ -1,19 +0,0 @@-module Rattletrap.Attribute.Int where--import Rattletrap.Primitive--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--newtype IntAttribute = IntAttribute-  { intAttributeValue :: Int32-  } deriving (Eq, Ord, Show)--getIntAttribute :: BinaryBit.BitGet IntAttribute-getIntAttribute = do-  value <- getInt32Bits-  pure (IntAttribute value)--putIntAttribute :: IntAttribute -> BinaryBit.BitPut ()-putIntAttribute intAttribute =-  putInt32Bits (intAttributeValue intAttribute)
− library/Rattletrap/Attribute/Loadout.hs
@@ -1,60 +0,0 @@-module Rattletrap.Attribute.Loadout where--import Rattletrap.Primitive--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--data LoadoutAttribute = LoadoutAttribute-  { loadoutAttributeVersion :: Word8-  , loadoutAttributeBody :: Word32-  , loadoutAttributeDecal :: Word32-  , loadoutAttributeWheels :: Word32-  , loadoutAttributeRocketTrail :: Word32-  , loadoutAttributeAntenna :: Word32-  , loadoutAttributeTopper :: Word32-  , loadoutAttributeUnknown1 :: Word32-  , loadoutAttributeUnknown2 :: Maybe Word32-  } deriving (Eq, Ord, Show)--getLoadoutAttribute :: BinaryBit.BitGet LoadoutAttribute-getLoadoutAttribute = do-  version <- getWord8Bits-  body <- getWord32Bits-  decal <- getWord32Bits-  wheels <- getWord32Bits-  rocketTrail <- getWord32Bits-  antenna <- getWord32Bits-  topper <- getWord32Bits-  g <- getWord32Bits-  h <--    if version > Word8 10-      then do-        h <- getWord32Bits-        pure (Just h)-      else pure Nothing-  pure-    (LoadoutAttribute-       version-       body-       decal-       wheels-       rocketTrail-       antenna-       topper-       g-       h)--putLoadoutAttribute :: LoadoutAttribute -> BinaryBit.BitPut ()-putLoadoutAttribute loadoutAttribute = do-  putWord8Bits (loadoutAttributeVersion loadoutAttribute)-  putWord32Bits (loadoutAttributeBody loadoutAttribute)-  putWord32Bits (loadoutAttributeDecal loadoutAttribute)-  putWord32Bits (loadoutAttributeWheels loadoutAttribute)-  putWord32Bits (loadoutAttributeRocketTrail loadoutAttribute)-  putWord32Bits (loadoutAttributeAntenna loadoutAttribute)-  putWord32Bits (loadoutAttributeTopper loadoutAttribute)-  putWord32Bits (loadoutAttributeUnknown1 loadoutAttribute)-  case loadoutAttributeUnknown2 loadoutAttribute of-    Nothing -> pure ()-    Just x -> putWord32Bits x
− library/Rattletrap/Attribute/LoadoutOnline.hs
@@ -1,39 +0,0 @@-module Rattletrap.Attribute.LoadoutOnline where--import Rattletrap.Primitive--import qualified Control.Monad as Monad-import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--newtype LoadoutOnlineAttribute = LoadoutOnlineAttribute-  { loadoutAttributeValue :: [[(Word32, CompressedWord)]]-  } deriving (Eq, Ord, Show)--getLoadoutOnlineAttribute :: BinaryBit.BitGet LoadoutOnlineAttribute-getLoadoutOnlineAttribute = do-  size <- getWord8Bits-  values <--    Monad.replicateM-      (fromIntegral (word8Value size))-      (do innerSize <- getWord8Bits-          Monad.replicateM-            (fromIntegral (word8Value innerSize))-            (do x <- getWord32Bits-                y <- getCompressedWord 27-                pure (x, y)))-  pure (LoadoutOnlineAttribute values)--putLoadoutOnlineAttribute :: LoadoutOnlineAttribute -> BinaryBit.BitPut ()-putLoadoutOnlineAttribute loadoutAttribute = do-  let values = loadoutAttributeValue loadoutAttribute-  putWord8Bits (Word8 (fromIntegral (length values)))-  mapM_-    (\xs -> do-       putWord8Bits (Word8 (fromIntegral (length xs)))-       mapM_-         (\(x, y) -> do-            putWord32Bits x-            putCompressedWord y)-         xs)-    values
− library/Rattletrap/Attribute/Loadouts.hs
@@ -1,22 +0,0 @@-module Rattletrap.Attribute.Loadouts where--import Rattletrap.Attribute.Loadout--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--data LoadoutsAttribute = LoadoutsAttribute-  { loadoutsAttributeBlue :: LoadoutAttribute-  , loadoutsAttributeOrange :: LoadoutAttribute-  } deriving (Eq, Ord, Show)--getLoadoutsAttribute :: BinaryBit.BitGet LoadoutsAttribute-getLoadoutsAttribute = do-  blue <- getLoadoutAttribute-  orange <- getLoadoutAttribute-  pure (LoadoutsAttribute blue orange)--putLoadoutsAttribute :: LoadoutsAttribute -> BinaryBit.BitPut ()-putLoadoutsAttribute loadoutsAttribute = do-  putLoadoutAttribute (loadoutsAttributeBlue loadoutsAttribute)-  putLoadoutAttribute (loadoutsAttributeOrange loadoutsAttribute)
− library/Rattletrap/Attribute/LoadoutsOnline.hs
@@ -1,34 +0,0 @@-module Rattletrap.Attribute.LoadoutsOnline where--import Rattletrap.Attribute.LoadoutOnline--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--data LoadoutsOnlineAttribute = LoadoutsOnlineAttribute-  { loadoutsOnlineAttributeBlue :: LoadoutOnlineAttribute-  , loadoutsOnlineAttributeOrange :: LoadoutOnlineAttribute-  , loadoutsOnlineAttributeUnknown1 :: Bool-  , loadoutsOnlineAttributeUnknown2 :: Bool-  } deriving (Eq, Ord, Show)--getLoadoutsOnlineAttribute :: BinaryBit.BitGet LoadoutsOnlineAttribute-getLoadoutsOnlineAttribute = do-  blueLoadout <- getLoadoutOnlineAttribute-  orangeLoadout <- getLoadoutOnlineAttribute-  unknown1 <- BinaryBit.getBool-  unknown2 <- BinaryBit.getBool-  pure-    (LoadoutsOnlineAttribute blueLoadout orangeLoadout unknown1 unknown2)--putLoadoutsOnlineAttribute :: LoadoutsOnlineAttribute-                                -> BinaryBit.BitPut ()-putLoadoutsOnlineAttribute loadoutsOnlineAttribute = do-  putLoadoutOnlineAttribute-    (loadoutsOnlineAttributeBlue loadoutsOnlineAttribute)-  putLoadoutOnlineAttribute-    (loadoutsOnlineAttributeOrange loadoutsOnlineAttribute)-  BinaryBit.putBool-    (loadoutsOnlineAttributeUnknown1 loadoutsOnlineAttribute)-  BinaryBit.putBool-    (loadoutsOnlineAttributeUnknown2 loadoutsOnlineAttribute)
− library/Rattletrap/Attribute/Location.hs
@@ -1,19 +0,0 @@-module Rattletrap.Attribute.Location where--import Rattletrap.Primitive--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--newtype LocationAttribute = LocationAttribute-  { locationAttributeValue :: Vector-  } deriving (Eq, Ord, Show)--getLocationAttribute :: BinaryBit.BitGet LocationAttribute-getLocationAttribute = do-  value <- getVector-  pure (LocationAttribute value)--putLocationAttribute :: LocationAttribute -> BinaryBit.BitPut ()-putLocationAttribute locationAttribute =-  putVector (locationAttributeValue locationAttribute)
− library/Rattletrap/Attribute/MusicStinger.hs
@@ -1,26 +0,0 @@-module Rattletrap.Attribute.MusicStinger where--import Rattletrap.Primitive--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--data MusicStingerAttribute = MusicStingerAttribute-  { musicStingerAttributeFlag :: Bool-  , musicStingerAttributeCue :: Word32-  , musicStingerAttributeTrigger :: Word8-  } deriving (Eq, Ord, Show)--getMusicStingerAttribute :: BinaryBit.BitGet MusicStingerAttribute-getMusicStingerAttribute = do-  flag <- BinaryBit.getBool-  cue <- getWord32Bits-  trigger <- getWord8Bits-  pure (MusicStingerAttribute flag cue trigger)--putMusicStingerAttribute :: MusicStingerAttribute-                              -> BinaryBit.BitPut ()-putMusicStingerAttribute musicStingerAttribute = do-  BinaryBit.putBool (musicStingerAttributeFlag musicStingerAttribute)-  putWord32Bits (musicStingerAttributeCue musicStingerAttribute)-  putWord8Bits (musicStingerAttributeTrigger musicStingerAttribute)
− library/Rattletrap/Attribute/PartyLeader.hs
@@ -1,33 +0,0 @@-module Rattletrap.Attribute.PartyLeader where--import Rattletrap.Primitive-import Rattletrap.RemoteId--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--data PartyLeaderAttribute = PartyLeaderAttribute-  { partyLeaderAttributeSystemId :: Word8-  , partyLeaderAttributeId :: Maybe (RemoteId, Word8)-  } deriving (Eq, Ord, Show)--getPartyLeaderAttribute :: BinaryBit.BitGet PartyLeaderAttribute-getPartyLeaderAttribute = do-  systemId <- getWord8Bits-  maybeRemoteAndLocalId <--    if systemId == Word8 0-      then pure Nothing-      else do-        remoteId <- getRemoteId systemId-        localId <- getWord8Bits-        pure (Just (remoteId, localId))-  pure (PartyLeaderAttribute systemId maybeRemoteAndLocalId)--putPartyLeaderAttribute :: PartyLeaderAttribute -> BinaryBit.BitPut ()-putPartyLeaderAttribute partyLeaderAttribute = do-  putWord8Bits (partyLeaderAttributeSystemId partyLeaderAttribute)-  case partyLeaderAttributeId partyLeaderAttribute of-    Nothing -> pure ()-    Just (remoteId, localId) -> do-      putRemoteId remoteId-      putWord8Bits localId
− library/Rattletrap/Attribute/Pickup.hs
@@ -1,32 +0,0 @@-module Rattletrap.Attribute.Pickup where--import Rattletrap.Primitive--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--data PickupAttribute = PickupAttribute-  { pickupAttributeInstigatorId :: Maybe Word32-  , pickupAttributePickedUp :: Bool-  } deriving (Eq, Ord, Show)--getPickupAttribute :: BinaryBit.BitGet PickupAttribute-getPickupAttribute = do-  instigator <- BinaryBit.getBool-  maybeInstigatorId <--    if instigator-      then do-        instigatorId <- getWord32Bits-        pure (Just instigatorId)-      else pure Nothing-  pickedUp <- BinaryBit.getBool-  pure (PickupAttribute maybeInstigatorId pickedUp)--putPickupAttribute :: PickupAttribute -> BinaryBit.BitPut ()-putPickupAttribute pickupAttribute = do-  case pickupAttributeInstigatorId pickupAttribute of-    Nothing -> BinaryBit.putBool False-    Just instigatorId -> do-      BinaryBit.putBool True-      putWord32Bits instigatorId-  BinaryBit.putBool (pickupAttributePickedUp pickupAttribute)
− library/Rattletrap/Attribute/PrivateMatchSettings.hs
@@ -1,53 +0,0 @@-module Rattletrap.Attribute.PrivateMatchSettings where--import Rattletrap.Primitive--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--data PrivateMatchSettingsAttribute = PrivateMatchSettingsAttribute-  { privateMatchSettingsAttributeMutators :: Text-  , privateMatchSettingsAttributeJoinableBy :: Word32-  , privateMatchSettingsAttributeMaxPlayers :: Word32-  , privateMatchSettingsAttributeGameName :: Text-  , privateMatchSettingsAttributePassword :: Text-  , privateMatchSettingsAttributeFlag :: Bool-  } deriving (Eq, Ord, Show)--getPrivateMatchSettingsAttribute :: BinaryBit.BitGet PrivateMatchSettingsAttribute-getPrivateMatchSettingsAttribute = do-  mutators <- getTextBits-  joinableBy <- getWord32Bits-  maxPlayers <- getWord32Bits-  gameName <- getTextBits-  password <- getTextBits-  flag <- BinaryBit.getBool-  pure-    (PrivateMatchSettingsAttribute-       mutators-       joinableBy-       maxPlayers-       gameName-       password-       flag)--putPrivateMatchSettingsAttribute :: PrivateMatchSettingsAttribute-                                      -> BinaryBit.BitPut ()-putPrivateMatchSettingsAttribute privateMatchSettingsAttribute = do-  putTextBits-    (privateMatchSettingsAttributeMutators-       privateMatchSettingsAttribute)-  putWord32Bits-    (privateMatchSettingsAttributeJoinableBy-       privateMatchSettingsAttribute)-  putWord32Bits-    (privateMatchSettingsAttributeMaxPlayers-       privateMatchSettingsAttribute)-  putTextBits-    (privateMatchSettingsAttributeGameName-       privateMatchSettingsAttribute)-  putTextBits-    (privateMatchSettingsAttributePassword-       privateMatchSettingsAttribute)-  BinaryBit.putBool-    (privateMatchSettingsAttributeFlag privateMatchSettingsAttribute)
− library/Rattletrap/Attribute/QWord.hs
@@ -1,19 +0,0 @@-module Rattletrap.Attribute.QWord where--import Rattletrap.Primitive--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--newtype QWordAttribute = QWordAttribute-  { qWordAttributeValue :: Word64-  } deriving (Eq, Ord, Show)--getQWordAttribute :: BinaryBit.BitGet QWordAttribute-getQWordAttribute = do-  value <- getWord64Bits-  pure (QWordAttribute value)--putQWordAttribute :: QWordAttribute -> BinaryBit.BitPut ()-putQWordAttribute qWordAttribute =-  putWord64Bits (qWordAttributeValue qWordAttribute)
− library/Rattletrap/Attribute/Reservation.hs
@@ -1,54 +0,0 @@-module Rattletrap.Attribute.Reservation where--import Rattletrap.Attribute.UniqueId-import Rattletrap.Primitive--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit-import qualified Data.Word as Word--data ReservationAttribute = ReservationAttribute-  { reservationAttributeNumber :: CompressedWord-  , reservationAttributeUniqueId :: UniqueIdAttribute-  , reservationAttributeName :: Maybe Text-  , reservationAttributeUnknown1 :: Bool-  , reservationAttributeUnknown2 :: Bool-  , reservationAttributeUnknown3 :: Maybe Word.Word8-  } deriving (Eq, Ord, Show)--getReservationAttribute :: (Int, Int)-                             -> BinaryBit.BitGet ReservationAttribute-getReservationAttribute version = do-  number <- getCompressedWord 7-  uniqueId <- getUniqueIdAttribute-  name <--    if uniqueIdAttributeSystemId uniqueId == Word8 0-      then pure Nothing-      else do-        name <- getTextBits-        pure (Just name)-  a <- BinaryBit.getBool-  b <- BinaryBit.getBool-  mc <--    if version < (868, 12)-      then pure Nothing-      else do-        c <- BinaryBit.getWord8 6-        pure (Just c)-  pure (ReservationAttribute number uniqueId name a b mc)--putReservationAttribute :: ReservationAttribute -> BinaryBit.BitPut ()-putReservationAttribute reservationAttribute = do-  putCompressedWord (reservationAttributeNumber reservationAttribute)-  putUniqueIdAttribute-    (reservationAttributeUniqueId reservationAttribute)-  case reservationAttributeName reservationAttribute of-    Nothing -> pure ()-    Just name -> putTextBits name-  BinaryBit.putBool-    (reservationAttributeUnknown1 reservationAttribute)-  BinaryBit.putBool-    (reservationAttributeUnknown2 reservationAttribute)-  case reservationAttributeUnknown3 reservationAttribute of-    Nothing -> pure ()-    Just c -> BinaryBit.putWord8 6 c
− library/Rattletrap/Attribute/RigidBodyState.hs
@@ -1,54 +0,0 @@-module Rattletrap.Attribute.RigidBodyState where--import Rattletrap.Primitive--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--data RigidBodyStateAttribute = RigidBodyStateAttribute-  { rigidBodyStateAttributeSleeping :: Bool-  , rigidBodyStateAttributeLocation :: Vector-  , rigidBodyStateAttributeRotation :: CompressedWordVector-  , rigidBodyStateAttributeLinearVelocity :: Maybe Vector-  , rigidBodyStateAttributeAngularVelocity :: Maybe Vector-  } deriving (Eq, Ord, Show)--getRigidBodyStateAttribute :: BinaryBit.BitGet RigidBodyStateAttribute-getRigidBodyStateAttribute = do-  sleeping <- BinaryBit.getBool-  location <- getVector-  rotation <- getCompressedWordVector-  linearVelocity <--    if sleeping-      then pure Nothing-      else do-        linearVelocity <- getVector-        pure (Just linearVelocity)-  angularVelocity <--    if sleeping-      then pure Nothing-      else do-        angularVelocity <- getVector-        pure (Just angularVelocity)-  pure-    (RigidBodyStateAttribute-       sleeping-       location-       rotation-       linearVelocity-       angularVelocity)--putRigidBodyStateAttribute :: RigidBodyStateAttribute-                                -> BinaryBit.BitPut ()-putRigidBodyStateAttribute rigidBodyStateAttribute = do-  BinaryBit.putBool-    (rigidBodyStateAttributeSleeping rigidBodyStateAttribute)-  putVector (rigidBodyStateAttributeLocation rigidBodyStateAttribute)-  putCompressedWordVector-    (rigidBodyStateAttributeRotation rigidBodyStateAttribute)-  case rigidBodyStateAttributeLinearVelocity rigidBodyStateAttribute of-    Nothing -> pure ()-    Just linearVelocity -> putVector linearVelocity-  case rigidBodyStateAttributeAngularVelocity rigidBodyStateAttribute of-    Nothing -> pure ()-    Just angularVelocity -> putVector angularVelocity
− library/Rattletrap/Attribute/String.hs
@@ -1,19 +0,0 @@-module Rattletrap.Attribute.String where--import Rattletrap.Primitive--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--newtype StringAttribute = StringAttribute-  { stringAttributeValue :: Text-  } deriving (Eq, Ord, Show)--getStringAttribute :: BinaryBit.BitGet StringAttribute-getStringAttribute = do-  value <- getTextBits-  pure (StringAttribute value)--putStringAttribute :: StringAttribute -> BinaryBit.BitPut ()-putStringAttribute stringAttribute =-  putTextBits (stringAttributeValue stringAttribute)
− library/Rattletrap/Attribute/TeamPaint.hs
@@ -1,37 +0,0 @@-module Rattletrap.Attribute.TeamPaint where--import Rattletrap.Primitive--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--data TeamPaintAttribute = TeamPaintAttribute-  { teamPaintAttributeTeam :: Word8-  , teamPaintAttributePrimaryColor :: Word8-  , teamPaintAttributeAccentColor :: Word8-  , teamPaintAttributePrimaryFinish :: Word32-  , teamPaintAttributeAccentFinish :: Word32-  } deriving (Eq, Ord, Show)--getTeamPaintAttribute :: BinaryBit.BitGet TeamPaintAttribute-getTeamPaintAttribute = do-  team <- getWord8Bits-  primaryColor <- getWord8Bits-  accentColor <- getWord8Bits-  primaryFinish <- getWord32Bits-  accentFinish <- getWord32Bits-  pure-    (TeamPaintAttribute-       team-       primaryColor-       accentColor-       primaryFinish-       accentFinish)--putTeamPaintAttribute :: TeamPaintAttribute -> BinaryBit.BitPut ()-putTeamPaintAttribute teamPaintAttribute = do-  putWord8Bits (teamPaintAttributeTeam teamPaintAttribute)-  putWord8Bits (teamPaintAttributePrimaryColor teamPaintAttribute)-  putWord8Bits (teamPaintAttributeAccentColor teamPaintAttribute)-  putWord32Bits (teamPaintAttributePrimaryFinish teamPaintAttribute)-  putWord32Bits (teamPaintAttributeAccentFinish teamPaintAttribute)
− library/Rattletrap/Attribute/UniqueId.hs
@@ -1,26 +0,0 @@-module Rattletrap.Attribute.UniqueId where--import Rattletrap.Primitive-import Rattletrap.RemoteId--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--data UniqueIdAttribute = UniqueIdAttribute-  { uniqueIdAttributeSystemId :: Word8-  , uniqueIdAttributeRemoteId :: RemoteId-  , uniqueIdAttributeLocalId :: Word8-  } deriving (Eq, Ord, Show)--getUniqueIdAttribute :: BinaryBit.BitGet UniqueIdAttribute-getUniqueIdAttribute = do-  systemId <- getWord8Bits-  remoteId <- getRemoteId systemId-  localId <- getWord8Bits-  pure (UniqueIdAttribute systemId remoteId localId)--putUniqueIdAttribute :: UniqueIdAttribute -> BinaryBit.BitPut ()-putUniqueIdAttribute uniqueIdAttribute = do-  putWord8Bits (uniqueIdAttributeSystemId uniqueIdAttribute)-  putRemoteId (uniqueIdAttributeRemoteId uniqueIdAttribute)-  putWord8Bits (uniqueIdAttributeLocalId uniqueIdAttribute)
− library/Rattletrap/Attribute/WeldedInfo.hs
@@ -1,31 +0,0 @@-module Rattletrap.Attribute.WeldedInfo where--import Rattletrap.Primitive--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--data WeldedInfoAttribute = WeldedInfoAttribute-  { weldedInfoAttributeActive :: Bool-  , weldedInfoAttributeActorId :: Int32-  , weldedInfoAttributeOffset :: Vector-  , weldedInfoAttributeMass :: Float32-  , weldedInfoAttributeRotation :: Int8Vector-  } deriving (Eq, Ord, Show)--getWeldedInfoAttribute :: BinaryBit.BitGet WeldedInfoAttribute-getWeldedInfoAttribute = do-  active <- BinaryBit.getBool-  actorId <- getInt32Bits-  offset <- getVector-  mass <- getFloat32Bits-  rotation <- getInt8Vector-  pure (WeldedInfoAttribute active actorId offset mass rotation)--putWeldedInfoAttribute :: WeldedInfoAttribute -> BinaryBit.BitPut ()-putWeldedInfoAttribute weldedInfoAttribute = do-  BinaryBit.putBool (weldedInfoAttributeActive weldedInfoAttribute)-  putInt32Bits (weldedInfoAttributeActorId weldedInfoAttribute)-  putVector (weldedInfoAttributeOffset weldedInfoAttribute)-  putFloat32Bits (weldedInfoAttributeMass weldedInfoAttribute)-  putInt8Vector (weldedInfoAttributeRotation weldedInfoAttribute)
− library/Rattletrap/AttributeMapping.hs
@@ -1,21 +0,0 @@-module Rattletrap.AttributeMapping where--import Rattletrap.Primitive--import qualified Data.Binary as Binary--data AttributeMapping = AttributeMapping-  { attributeMappingObjectId :: Word32-  , attributeMappingStreamId :: Word32-  } deriving (Eq, Ord, Show)--getAttributeMapping :: Binary.Get AttributeMapping-getAttributeMapping = do-  objectId <- getWord32-  streamId <- getWord32-  pure (AttributeMapping objectId streamId)--putAttributeMapping :: AttributeMapping -> Binary.Put-putAttributeMapping attributeMapping = do-  putWord32 (attributeMappingObjectId attributeMapping)-  putWord32 (attributeMappingStreamId attributeMapping)
− library/Rattletrap/AttributeType.hs
@@ -1,31 +0,0 @@-module Rattletrap.AttributeType where--data AttributeType-  = BooleanAttributeType-  | ByteAttributeType-  | CamSettingsAttributeType-  | ClubColorsAttributeType-  | DemolishAttributeType-  | EnumAttributeType-  | ExplosionAttributeType-  | FlaggedIntAttributeType-  | FloatAttributeType-  | GameModeAttributeType-  | IntAttributeType-  | LoadoutAttributeType-  | LoadoutOnlineAttributeType-  | LoadoutsAttributeType-  | LoadoutsOnlineAttributeType-  | LocationAttributeType-  | MusicStingerAttributeType-  | PartyLeaderAttributeType-  | PickupAttributeType-  | PrivateMatchSettingsAttributeType-  | QWordAttributeType-  | ReservationAttributeType-  | RigidBodyStateAttributeType-  | StringAttributeType-  | TeamPaintAttributeType-  | UniqueIdAttributeType-  | WeldedInfoAttributeType-  deriving (Eq, Ord, Show)
− library/Rattletrap/AttributeValue.hs
@@ -1,217 +0,0 @@-module Rattletrap.AttributeValue-  ( module Rattletrap.AttributeValue-  , module Rattletrap.Attribute.Boolean-  , module Rattletrap.Attribute.Byte-  , module Rattletrap.Attribute.CamSettings-  , module Rattletrap.Attribute.ClubColors-  , module Rattletrap.Attribute.Demolish-  , module Rattletrap.Attribute.Enum-  , module Rattletrap.Attribute.Explosion-  , module Rattletrap.Attribute.FlaggedInt-  , module Rattletrap.Attribute.Float-  , module Rattletrap.Attribute.GameMode-  , module Rattletrap.Attribute.Int-  , module Rattletrap.Attribute.Loadout-  , module Rattletrap.Attribute.LoadoutOnline-  , module Rattletrap.Attribute.Loadouts-  , module Rattletrap.Attribute.LoadoutsOnline-  , module Rattletrap.Attribute.Location-  , module Rattletrap.Attribute.MusicStinger-  , module Rattletrap.Attribute.PartyLeader-  , module Rattletrap.Attribute.Pickup-  , module Rattletrap.Attribute.PrivateMatchSettings-  , module Rattletrap.Attribute.QWord-  , module Rattletrap.Attribute.Reservation-  , module Rattletrap.Attribute.RigidBodyState-  , module Rattletrap.Attribute.String-  , module Rattletrap.Attribute.TeamPaint-  , module Rattletrap.Attribute.UniqueId-  , module Rattletrap.Attribute.WeldedInfo-  ) where--import Rattletrap.Attribute.Boolean-import Rattletrap.Attribute.Byte-import Rattletrap.Attribute.CamSettings-import Rattletrap.Attribute.ClubColors-import Rattletrap.Attribute.Demolish-import Rattletrap.Attribute.Enum-import Rattletrap.Attribute.Explosion-import Rattletrap.Attribute.FlaggedInt-import Rattletrap.Attribute.Float-import Rattletrap.Attribute.GameMode-import Rattletrap.Attribute.Int-import Rattletrap.Attribute.Loadout-import Rattletrap.Attribute.LoadoutOnline-import Rattletrap.Attribute.Loadouts-import Rattletrap.Attribute.LoadoutsOnline-import Rattletrap.Attribute.Location-import Rattletrap.Attribute.MusicStinger-import Rattletrap.Attribute.PartyLeader-import Rattletrap.Attribute.Pickup-import Rattletrap.Attribute.PrivateMatchSettings-import Rattletrap.Attribute.QWord-import Rattletrap.Attribute.Reservation-import Rattletrap.Attribute.RigidBodyState-import Rattletrap.Attribute.String-import Rattletrap.Attribute.TeamPaint-import Rattletrap.Attribute.UniqueId-import Rattletrap.Attribute.WeldedInfo-import Rattletrap.AttributeType-import Rattletrap.Data-import Rattletrap.Primitive--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit-import qualified Data.Map.Strict as Map--data AttributeValue-  = BooleanAttributeValue BooleanAttribute-  | ByteAttributeValue ByteAttribute-  | CamSettingsAttributeValue CamSettingsAttribute-  | ClubColorsAttributeValue ClubColorsAttribute-  | DemolishAttributeValue DemolishAttribute-  | EnumAttributeValue EnumAttribute-  | ExplosionAttributeValue ExplosionAttribute-  | FlaggedIntAttributeValue FlaggedIntAttribute-  | FloatAttributeValue FloatAttribute-  | GameModeAttributeValue GameModeAttribute-  | IntAttributeValue IntAttribute-  | LoadoutAttributeValue LoadoutAttribute-  | LoadoutOnlineAttributeValue LoadoutOnlineAttribute-  | LoadoutsAttributeValue LoadoutsAttribute-  | LoadoutsOnlineAttributeValue LoadoutsOnlineAttribute-  | LocationAttributeValue LocationAttribute-  | MusicStingerAttributeValue MusicStingerAttribute-  | PartyLeaderAttributeValue PartyLeaderAttribute-  | PickupAttributeValue PickupAttribute-  | PrivateMatchSettingsAttributeValue PrivateMatchSettingsAttribute-  | QWordAttributeValue QWordAttribute-  | ReservationAttributeValue ReservationAttribute-  | RigidBodyStateAttributeValue RigidBodyStateAttribute-  | StringAttributeValue StringAttribute-  | TeamPaintAttributeValue TeamPaintAttribute-  | UniqueIdAttributeValue UniqueIdAttribute-  | WeldedInfoAttributeValue WeldedInfoAttribute-  deriving (Eq, Ord, Show)--getAttributeValue :: (Int, Int) -> Text -> BinaryBit.BitGet AttributeValue-getAttributeValue version name =-  case Map.lookup name attributeTypes of-    Just constructor ->-      case constructor of-        BooleanAttributeType -> do-          x <- getBooleanAttribute-          pure (BooleanAttributeValue x)-        ByteAttributeType -> do-          x <- getByteAttribute-          pure (ByteAttributeValue x)-        CamSettingsAttributeType -> do-          x <- getCamSettingsAttribute-          pure (CamSettingsAttributeValue x)-        ClubColorsAttributeType -> do-          x <- getClubColorsAttribute-          pure (ClubColorsAttributeValue x)-        DemolishAttributeType -> do-          x <- getDemolishAttribute-          pure (DemolishAttributeValue x)-        EnumAttributeType -> do-          x <- getEnumAttribute-          pure (EnumAttributeValue x)-        ExplosionAttributeType -> do-          x <- getExplosionAttribute-          pure (ExplosionAttributeValue x)-        FlaggedIntAttributeType -> do-          x <- getFlaggedIntAttribute-          pure (FlaggedIntAttributeValue x)-        FloatAttributeType -> do-          x <- getFloatAttribute-          pure (FloatAttributeValue x)-        GameModeAttributeType -> do-          x <- getGameModeAttribute version-          pure (GameModeAttributeValue x)-        IntAttributeType -> do-          x <- getIntAttribute-          pure (IntAttributeValue x)-        LoadoutAttributeType -> do-          x <- getLoadoutAttribute-          pure (LoadoutAttributeValue x)-        LoadoutOnlineAttributeType -> do-          x <- getLoadoutOnlineAttribute-          pure (LoadoutOnlineAttributeValue x)-        LoadoutsAttributeType -> do-          x <- getLoadoutsAttribute-          pure (LoadoutsAttributeValue x)-        LoadoutsOnlineAttributeType -> do-          x <- getLoadoutsOnlineAttribute-          pure (LoadoutsOnlineAttributeValue x)-        LocationAttributeType -> do-          x <- getLocationAttribute-          pure (LocationAttributeValue x)-        MusicStingerAttributeType -> do-          x <- getMusicStingerAttribute-          pure (MusicStingerAttributeValue x)-        PartyLeaderAttributeType -> do-          x <- getPartyLeaderAttribute-          pure (PartyLeaderAttributeValue x)-        PickupAttributeType -> do-          x <- getPickupAttribute-          pure (PickupAttributeValue x)-        PrivateMatchSettingsAttributeType -> do-          x <- getPrivateMatchSettingsAttribute-          pure (PrivateMatchSettingsAttributeValue x)-        QWordAttributeType -> do-          x <- getQWordAttribute-          pure (QWordAttributeValue x)-        ReservationAttributeType -> do-          x <- getReservationAttribute version-          pure (ReservationAttributeValue x)-        RigidBodyStateAttributeType -> do-          x <- getRigidBodyStateAttribute-          pure (RigidBodyStateAttributeValue x)-        StringAttributeType -> do-          x <- getStringAttribute-          pure (StringAttributeValue x)-        TeamPaintAttributeType -> do-          x <- getTeamPaintAttribute-          pure (TeamPaintAttributeValue x)-        UniqueIdAttributeType -> do-          x <- getUniqueIdAttribute-          pure (UniqueIdAttributeValue x)-        WeldedInfoAttributeType -> do-          x <- getWeldedInfoAttribute-          pure (WeldedInfoAttributeValue x)-    Nothing -> fail ("don't know how to get attribute value " ++ show name)--attributeTypes :: Map.Map Text AttributeType-attributeTypes = Map.mapKeys stringToText (Map.fromList rawAttributeTypes)--putAttributeValue :: AttributeValue -> BinaryBit.BitPut ()-putAttributeValue value =-  case value of-    BooleanAttributeValue x -> putBooleanAttribute x-    ByteAttributeValue x -> putByteAttribute x-    CamSettingsAttributeValue x -> putCamSettingsAttribute x-    ClubColorsAttributeValue x -> putClubColorsAttribute x-    DemolishAttributeValue x -> putDemolishAttribute x-    EnumAttributeValue x -> putEnumAttribute x-    ExplosionAttributeValue x -> putExplosionAttribute x-    FlaggedIntAttributeValue x -> putFlaggedIntAttribute x-    FloatAttributeValue x -> putFloatAttribute x-    GameModeAttributeValue x -> putGameModeAttribute x-    IntAttributeValue x -> putIntAttribute x-    LoadoutAttributeValue x -> putLoadoutAttribute x-    LoadoutOnlineAttributeValue x -> putLoadoutOnlineAttribute x-    LoadoutsAttributeValue x -> putLoadoutsAttribute x-    LoadoutsOnlineAttributeValue x -> putLoadoutsOnlineAttribute x-    LocationAttributeValue x -> putLocationAttribute x-    MusicStingerAttributeValue x -> putMusicStingerAttribute x-    PartyLeaderAttributeValue x -> putPartyLeaderAttribute x-    PickupAttributeValue x -> putPickupAttribute x-    PrivateMatchSettingsAttributeValue x -> putPrivateMatchSettingsAttribute x-    QWordAttributeValue x -> putQWordAttribute x-    ReservationAttributeValue x -> putReservationAttribute x-    RigidBodyStateAttributeValue x -> putRigidBodyStateAttribute x-    StringAttributeValue x -> putStringAttribute x-    TeamPaintAttributeValue x -> putTeamPaintAttribute x-    UniqueIdAttributeValue x -> putUniqueIdAttribute x-    WeldedInfoAttributeValue x -> putWeldedInfoAttribute x
− library/Rattletrap/Cache.hs
@@ -1,28 +0,0 @@-module Rattletrap.Cache where--import Rattletrap.AttributeMapping-import Rattletrap.Primitive--import qualified Data.Binary as Binary--data Cache = Cache-  { cacheClassId :: Word32-  , cacheParentCacheId :: Word32-  , cacheCacheId :: Word32-  , cacheAttributeMappings :: List AttributeMapping-  } deriving (Eq, Ord, Show)--getCache :: Binary.Get Cache-getCache = do-  classId <- getWord32-  parentCacheId <- getWord32-  cacheId <- getWord32-  attributeMappings <- getList getAttributeMapping-  pure (Cache classId parentCacheId cacheId attributeMappings)--putCache :: Cache -> Binary.Put-putCache cache = do-  putWord32 (cacheClassId cache)-  putWord32 (cacheParentCacheId cache)-  putWord32 (cacheCacheId cache)-  putList putAttributeMapping (cacheAttributeMappings cache)
− library/Rattletrap/ClassAttributeMap.hs
@@ -1,269 +0,0 @@-module Rattletrap.ClassAttributeMap where--import Rattletrap.ActorMap-import Rattletrap.AttributeMapping-import Rattletrap.Cache-import Rattletrap.ClassMapping-import Rattletrap.Data-import Rattletrap.Primitive--import qualified Data.Bimap as Bimap-import qualified Data.List as List-import qualified Data.Map.Strict as Map-import qualified Data.Maybe as Maybe-import qualified Data.Set as Set-import qualified Data.Text as Text--data ClassAttributeMap = ClassAttributeMap-  { classAttributeMapObjectMap :: Map.Map Word32 Text-  , classAttributeMapClassMap :: Bimap.Bimap Word32 Text-  , classAttributeMapObjectClassMap :: Map.Map Word32 Word32-  , classAttributeMapValue :: Map.Map Word32 (Map.Map Word32 Word32)-  } deriving (Eq, Show)--makeClassAttributeMap :: List Text-                      -> List ClassMapping-                      -> List Cache-                      -> ClassAttributeMap-makeClassAttributeMap objects classMappings caches =-  let objectMap = makeObjectMap objects-      classMap = makeClassMap classMappings-      objectClassMap = makeObjectClassMap objectMap classMap-      classCache = makeClassCache classMap caches-      attributeMap = makeAttributeMap caches-      classIds = map (\(_, classId, _, _) -> classId) classCache-      parentMap = makeParentMap classCache-      value =-        Map.fromList-          (map-             (\classId ->-                let ownAttributes =-                      Maybe.fromMaybe-                        Map.empty-                        (Map.lookup classId attributeMap)-                    parentsAttributes =-                      case Map.lookup classId parentMap of-                        Nothing -> []-                        Just parentClassIds ->-                          map-                            (\parentClassId ->-                               Maybe.fromMaybe-                                 Map.empty-                                 (Map.lookup parentClassId attributeMap))-                            parentClassIds-                    attributes = ownAttributes : parentsAttributes-                in (classId, Map.fromList (concatMap Map.toList attributes)))-             classIds)-  in ClassAttributeMap objectMap classMap objectClassMap value--makeObjectClassMap :: Map.Map Word32 Text-                   -> Bimap.Bimap Word32 Text-                   -> Map.Map Word32 Word32-makeObjectClassMap objectMap classMap = do-  let objectIds = Map.keys objectMap-  let classIds = map (getClassId objectMap classMap) objectIds-  let rawPairs = zip objectIds classIds-  let pairs =-        Maybe.mapMaybe-          (\(objectId, maybeClassId) ->-             case maybeClassId of-               Nothing -> Nothing-               Just classId -> Just (objectId, classId))-          rawPairs-  Map.fromList pairs--getClassId :: Map.Map Word32 Text-           -> Bimap.Bimap Word32 Text-           -> Word32-           -> Maybe Word32-getClassId objectMap classMap objectId = do-  objectName <- getObjectName objectMap objectId-  className <- getClassName objectName-  Bimap.lookupR className classMap--makeClassCache :: Bimap.Bimap Word32 Text-               -> List Cache-               -> [(Maybe Text, Word32, Word32, Word32)]-makeClassCache classMap caches =-  map-    (\cache ->-       let classId = cacheClassId cache-       in ( Bimap.lookup classId classMap-          , classId-          , cacheCacheId cache-          , cacheParentCacheId cache))-    (listValue caches)--makeClassMap :: List ClassMapping -> Bimap.Bimap Word32 Text-makeClassMap classMappings =-  Bimap.fromList-    (map-       (\classMapping ->-          (classMappingStreamId classMapping, classMappingName classMapping))-       (listValue classMappings))--makeAttributeMap :: List Cache -> Map.Map Word32 (Map.Map Word32 Word32)-makeAttributeMap caches =-  Map.fromList-    (map-       (\cache ->-          ( cacheClassId cache-          , Map.fromList-              (map-                 (\attributeMapping ->-                    ( attributeMappingStreamId attributeMapping-                    , attributeMappingObjectId attributeMapping))-                 (listValue (cacheAttributeMappings cache)))))-       (listValue caches))--makeShallowParentMap :: [(Maybe Text, Word32, Word32, Word32)]-                     -> Map.Map Word32 Word32-makeShallowParentMap classCache =-  Map.fromList-    (Maybe.mapMaybe-       (\xs ->-          case xs of-            [] -> Nothing-            (maybeClassName, classId, _, parentCacheId):rest -> do-              parentClassId <- getParentClass maybeClassName parentCacheId rest-              pure (classId, parentClassId))-       (List.tails (reverse classCache)))--makeParentMap :: [(Maybe Text, Word32, Word32, Word32)]-              -> Map.Map Word32 [Word32]-makeParentMap classCache =-  let shallowParentMap = makeShallowParentMap classCache-  in Map.mapWithKey-       (\classId _ -> getParentClasses shallowParentMap classId)-       shallowParentMap--getParentClasses :: Map.Map Word32 Word32 -> Word32 -> [Word32]-getParentClasses shallowParentMap classId =-  case Map.lookup classId shallowParentMap of-    Nothing -> []-    Just parentClassId ->-      parentClassId : getParentClasses shallowParentMap parentClassId--getParentClass :: Maybe Text-               -> Word32-               -> [(Maybe Text, Word32, Word32, Word32)]-               -> Maybe Word32-getParentClass maybeClassName parentCacheId xs =-  case maybeClassName of-    Nothing -> getParentClassById parentCacheId xs-    Just className -> getParentClassByName className parentCacheId xs--getParentClassById :: Word32-                   -> [(Maybe Text, Word32, Word32, Word32)]-                   -> Maybe Word32-getParentClassById parentCacheId xs =-  case dropWhile (\(_, _, cacheId, _) -> cacheId /= parentCacheId) xs of-    [] ->-      if parentCacheId == Word32 0-        then Nothing-        else getParentClassById (Word32 (word32Value parentCacheId - 1)) xs-    (_, parentClassId, _, _):_ -> Just parentClassId--getParentClassByName :: Text-                     -> Word32-                     -> [(Maybe Text, Word32, Word32, Word32)]-                     -> Maybe Word32-getParentClassByName className parentCacheId xs =-  case Map.lookup className parentClasses of-    Nothing -> getParentClassById parentCacheId xs-    Just parentClassName ->-      Maybe.maybe-        (getParentClassById parentCacheId xs)-        Just-        (Maybe.listToMaybe-           (map-              (\(_, parentClassId, _, _) -> parentClassId)-              (filter-                 (\(_, _, cacheId, _) -> cacheId == parentCacheId)-                 (filter-                    (\(maybeClassName, _, _, _) ->-                       maybeClassName == Just parentClassName)-                    xs))))--parentClasses :: Map.Map Text Text-parentClasses =-  Map.map-    stringToText-    (Map.mapKeys stringToText (Map.fromList rawParentClasses))--makeObjectMap :: List Text -> Map.Map Word32 Text-makeObjectMap objects =-  Map.fromAscList (zip (map Word32 [0 ..]) (listValue objects))--getObjectName :: Map.Map Word32 Text -> Word32 -> Maybe Text-getObjectName objectMap objectId = Map.lookup objectId objectMap--getClassName :: Text -> Maybe Text-getClassName rawObjectName =-  Map.lookup (normalizeObjectName rawObjectName) objectClasses--normalizeObjectName :: Text -> Text-normalizeObjectName objectName =-  let name = textValue objectName-      crowdActor = Text.pack "TheWorld:PersistentLevel.CrowdActor_TA"-      crowdManager = Text.pack "TheWorld:PersistentLevel.CrowdManager_TA"-      boostPickup = Text.pack "TheWorld:PersistentLevel.VehiclePickup_Boost_TA"-      mapScoreboard = Text.pack "TheWorld:PersistentLevel.InMapScoreboard_TA"-      toText text =-        Text-          (Int32 (fromIntegral (Text.length text + 1)))-          (Text.snoc text '\x00')-  in if Text.isInfixOf crowdActor name-       then toText crowdActor-       else if Text.isInfixOf crowdManager name-              then toText crowdManager-              else if Text.isInfixOf boostPickup name-                     then toText boostPickup-                     else if Text.isInfixOf mapScoreboard name-                            then toText mapScoreboard-                            else objectName--objectClasses :: Map.Map Text Text-objectClasses =-  Map.map-    stringToText-    (Map.mapKeys stringToText (Map.fromList rawObjectClasses))--classHasLocation :: Text -> Bool-classHasLocation className = Set.member className classesWithLocation--classesWithLocation :: Set.Set Text-classesWithLocation = Set.fromList (map stringToText rawClassesWithLocation)--classHasRotation :: Text -> Bool-classHasRotation className = Set.member className classesWithRotation--classesWithRotation :: Set.Set Text-classesWithRotation = Set.fromList (map stringToText rawClassesWithRotation)--getAttributeIdLimit :: Map.Map Word32 Word32 -> Maybe Word-getAttributeIdLimit attributeMap = do-  ((streamId, _), _) <- Map.maxViewWithKey attributeMap-  let limit = fromIntegral (word32Value streamId)-  pure limit--getAttributeName :: ClassAttributeMap-                 -> Map.Map Word32 Word32-                 -> CompressedWord-                 -> Maybe Text-getAttributeName classAttributeMap attributeMap streamId = do-  let key = Word32 (fromIntegral (compressedWordValue streamId))-  attributeId <- Map.lookup key attributeMap-  let objectMap = classAttributeMapObjectMap classAttributeMap-  Map.lookup attributeId objectMap--getAttributeMap :: ClassAttributeMap-                -> ActorMap-                -> CompressedWord-                -> Maybe (Map.Map Word32 Word32)-getAttributeMap classAttributeMap actorMap actorId = do-  objectId <- Map.lookup actorId actorMap-  let objectClassMap = classAttributeMapObjectClassMap classAttributeMap-  classId <- Map.lookup objectId objectClassMap-  let value = classAttributeMapValue classAttributeMap-  Map.lookup classId value
− library/Rattletrap/ClassMapping.hs
@@ -1,21 +0,0 @@-module Rattletrap.ClassMapping where--import Rattletrap.Primitive--import qualified Data.Binary as Binary--data ClassMapping = ClassMapping-  { classMappingName :: Text-  , classMappingStreamId :: Word32-  } deriving (Eq, Ord, Show)--getClassMapping :: Binary.Get ClassMapping-getClassMapping = do-  name <- getText-  streamId <- getWord32-  pure (ClassMapping name streamId)--putClassMapping :: ClassMapping -> Binary.Put-putClassMapping classMapping = do-  putText (classMappingName classMapping)-  putWord32 (classMappingStreamId classMapping)
− library/Rattletrap/Content.hs
@@ -1,92 +0,0 @@-module Rattletrap.Content where--import Rattletrap.ActorMap-import Rattletrap.Cache-import Rattletrap.ClassAttributeMap-import Rattletrap.ClassMapping-import Rattletrap.Frame-import Rattletrap.KeyFrame-import Rattletrap.Mark-import Rattletrap.Message-import Rattletrap.Primitive-import Rattletrap.Utility--import qualified Data.Binary as Binary-import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit-import qualified Data.Binary.Get as Binary-import qualified Data.Binary.Put as Binary--data Content = Content-  { contentLevels :: List Text-  , contentKeyFrames :: List KeyFrame-  , contentStreamSize :: Word32-  , contentFrames :: [Frame]-  , contentTrailingBits :: [Bool]-  , contentMessages :: List Message-  , contentMarks :: List Mark-  , contentPackages :: List Text-  , contentObjects :: List Text-  , contentNames :: List Text-  , contentClassMappings :: List ClassMapping-  , contentCaches :: List Cache-  } deriving (Eq, Ord, Show)--getContent :: (Int, Int) -> Int -> Binary.Get Content-getContent version numFrames = do-  levels <- getList getText-  keyFrames <- getList getKeyFrame-  streamSize <- getWord32-  stream <- Binary.getLazyByteString (fromIntegral (word32Value streamSize))-  messages <- getList getMessage-  marks <- getList getMark-  packages <- getList getText-  objects <- getList getText-  names <- getList getText-  classMappings <- getList getClassMapping-  caches <- getList getCache-  let classAttributeMap = makeClassAttributeMap objects classMappings caches-  let (frames, remainingBits) =-        Binary.runGet-          (BinaryBit.runBitGet-             (do (theFrames, _) <--                   getFrames version numFrames classAttributeMap makeActorMap-                 theRemainingBits <- getRemainingBits-                 pure (theFrames, theRemainingBits)))-          (reverseBytes stream)-  let trailingBits = reverse (dropWhile not (reverse remainingBits))-  pure-    (Content-       levels-       keyFrames-       streamSize-       frames-       trailingBits-       messages-       marks-       packages-       objects-       names-       classMappings-       caches)--putContent :: Content -> Binary.Put-putContent content = do-  putList putText (contentLevels content)-  putList putKeyFrame (contentKeyFrames content)-  let streamSize = contentStreamSize content-  putWord32 streamSize-  let stream =-        Binary.runPut-          (BinaryBit.runBitPut-             (do putFrames (contentFrames content)-                 mapM_ BinaryBit.putBool (contentTrailingBits content)))-  Binary.putLazyByteString-    (reverseBytes (padBytes (word32Value streamSize) stream))-  putList putMessage (contentMessages content)-  putList putMark (contentMarks content)-  putList putText (contentPackages content)-  putList putText (contentObjects content)-  putList putText (contentNames content)-  putList putClassMapping (contentClassMappings content)-  putList putCache (contentCaches content)
− library/Rattletrap/Crc.hs
@@ -1,37 +0,0 @@-module Rattletrap.Crc where--import Rattletrap.Data--import qualified Data.Bits as Bits-import qualified Data.ByteString.Lazy as ByteString-import qualified Data.Vector.Unboxed as Vector-import qualified Data.Word as Word--getCrc32 :: ByteString.ByteString -> Word.Word32-getCrc32 bytes = do-  let update = crc32Update crc32Table-  let initial = Bits.complement crc32Initial-  let crc = ByteString.foldl update initial bytes-  Bits.complement crc--crc32Update :: Vector.Vector Word.Word32-            -> Word.Word32-            -> Word.Word8-            -> Word.Word32-crc32Update table crc byte = do-  let toWord8 =-        fromIntegral :: (Integral a) =>-                          a -> Word.Word8-  let toInt =-        fromIntegral :: (Integral a) =>-                          a -> Int-  let index = toInt (Bits.xor byte (toWord8 (Bits.shiftR crc 24)))-  let left = Vector.unsafeIndex table index-  let right = Bits.shiftL crc 8-  Bits.xor left right--crc32Initial :: Word.Word32-crc32Initial = 0xefcbf201--crc32Table :: Vector.Vector Word.Word32-crc32Table = Vector.fromList rawCrc32Table
− library/Rattletrap/Data.hs
@@ -1,351 +0,0 @@-module Rattletrap.Data where--import Rattletrap.AttributeType--rawParentClasses :: [(String, String)]-rawParentClasses =-  [ ("Engine.Actor", "Core.Object")-  , ("Engine.GameReplicationInfo", "Engine.ReplicationInfo")-  , ("Engine.Info", "Engine.Actor")-  , ("Engine.Pawn", "Engine.Actor")-  , ("Engine.PlayerReplicationInfo", "Engine.ReplicationInfo")-  , ("Engine.ReplicationInfo", "Engine.Info")-  , ("Engine.TeamInfo", "Engine.ReplicationInfo")-  , ("ProjectX.GRI_X", "Engine.GameReplicationInfo")-  , ("ProjectX.PRI_X", "Engine.PlayerReplicationInfo")-  , ("ProjectX.Pawn_X", "Engine.Pawn")-  , ("TAGame.Ball_TA", "TAGame.RBActor_TA")-  , ("TAGame.CameraSettingsActor_TA", "Engine.ReplicationInfo")-  , ("TAGame.CarComponent_Boost_TA", "TAGame.CarComponent_TA")-  , ("TAGame.CarComponent_Dodge_TA", "TAGame.CarComponent_TA")-  , ("TAGame.CarComponent_DoubleJump_TA", "TAGame.CarComponent_TA")-  , ("TAGame.CarComponent_FlipCar_TA", "TAGame.CarComponent_TA")-  , ("TAGame.CarComponent_Jump_TA", "TAGame.CarComponent_TA")-  , ("TAGame.CarComponent_TA", "Engine.ReplicationInfo")-  , ("TAGame.Car_Season_TA", "TAGame.PRI_TA")-  , ("TAGame.Car_TA", "TAGame.Vehicle_TA")-  , ("TAGame.CrowdActor_TA", "Engine.ReplicationInfo")-  , ("TAGame.CrowdManager_TA", "Engine.ReplicationInfo")-  , ("TAGame.GRI_TA", "ProjectX.GRI_X")-  , ("TAGame.GameEvent_Season_TA", "TAGame.GameEvent_Soccar_TA")-  , ("TAGame.GameEvent_SoccarPrivate_TA", "TAGame.GameEvent_Soccar_TA")-  , ("TAGame.GameEvent_SoccarSplitscreen_TA", "TAGame.GameEvent_SoccarPrivate_TA")-  , ("TAGame.GameEvent_Soccar_TA", "TAGame.GameEvent_Team_TA")-  , ("TAGame.GameEvent_TA", "Engine.ReplicationInfo")-  , ("TAGame.GameEvent_Team_TA", "TAGame.GameEvent_TA")-  , ("TAGame.InMapScoreboard_TA", "Engine.Actor")-  , ("TAGame.PRI_TA", "ProjectX.PRI_X")-  , ("TAGame.RBActor_TA", "ProjectX.Pawn_X")-  , ("TAGame.SpecialPickup_BallCarSpring_TA", "TAGame.SpecialPickup_Spring_TA")-  , ("TAGame.SpecialPickup_BallFreeze_TA", "TAGame.SpecialPickup_Targeted_TA")-  , ("TAGame.SpecialPickup_BallGravity_TA", "TAGame.SpecialPickup_TA")-  , ("TAGame.SpecialPickup_BallLasso_TA", "TAGame.SpecialPickup_GrapplingHook_TA")-  , ("TAGame.SpecialPickup_BallVelcro_TA", "TAGame.SpecialPickup_TA")-  , ("TAGame.SpecialPickup_Batarang_TA", "TAGame.SpecialPickup_BallLasso_TA")-  , ("TAGame.SpecialPickup_BoostOverride_TA", "TAGame.SpecialPickup_Targeted_TA")-  , ("TAGame.SpecialPickup_GrapplingHook_TA", "TAGame.SpecialPickup_Targeted_TA")-  , ("TAGame.SpecialPickup_HitForce_TA", "TAGame.SpecialPickup_TA")-  , ("TAGame.SpecialPickup_Spring_TA", "TAGame.SpecialPickup_Targeted_TA")-  , ("TAGame.SpecialPickup_Swapper_TA", "TAGame.SpecialPickup_Targeted_TA")-  , ("TAGame.SpecialPickup_TA", "TAGame.CarComponent_TA")-  , ("TAGame.SpecialPickup_Targeted_TA", "TAGame.SpecialPickup_TA")-  , ("TAGame.SpecialPickup_Tornado_TA", "TAGame.SpecialPickup_TA")-  , ("TAGame.Team_Soccar_TA", "TAGame.Team_TA")-  , ("TAGame.Team_TA", "Engine.TeamInfo")-  , ("TAGame.VehiclePickup_Boost_TA", "TAGame.VehiclePickup_TA")-  , ("TAGame.VehiclePickup_TA", "Engine.ReplicationInfo")-  , ("TAGame.Vehicle_TA", "TAGame.RBActor_TA")-  , ("TAGame.VoteActor_TA", "Engine.Actor")-  ]--rawClassesWithLocation :: [String]-rawClassesWithLocation =-  [ "TAGame.Ball_TA"-  , "TAGame.CameraSettingsActor_TA"-  , "TAGame.CarComponent_Boost_TA"-  , "TAGame.CarComponent_Dodge_TA"-  , "TAGame.CarComponent_DoubleJump_TA"-  , "TAGame.CarComponent_FlipCar_TA"-  , "TAGame.CarComponent_Jump_TA"-  , "TAGame.Car_Season_TA"-  , "TAGame.Car_TA"-  , "TAGame.GRI_TA"-  , "TAGame.GameEvent_Season_TA"-  , "TAGame.GameEvent_SoccarPrivate_TA"-  , "TAGame.GameEvent_SoccarSplitscreen_TA"-  , "TAGame.GameEvent_Soccar_TA"-  , "TAGame.PRI_TA"-  , "TAGame.SpecialPickup_BallCarSpring_TA"-  , "TAGame.SpecialPickup_BallFreeze_TA"-  , "TAGame.SpecialPickup_BallGravity_TA"-  , "TAGame.SpecialPickup_BallGravity_TA"-  , "TAGame.SpecialPickup_BallLasso_TA"-  , "TAGame.SpecialPickup_BallVelcro_TA"-  , "TAGame.SpecialPickup_Batarang_TA"-  , "TAGame.SpecialPickup_BoostOverride_TA"-  , "TAGame.SpecialPickup_GrapplingHook_TA"-  , "TAGame.SpecialPickup_HitForce_TA"-  , "TAGame.SpecialPickup_Swapper_TA"-  , "TAGame.SpecialPickup_Tornado_TA"-  , "TAGame.Team_Soccar_TA"-  ]--rawClassesWithRotation :: [String]-rawClassesWithRotation =-  [ "TAGame.Ball_TA"-  , "TAGame.Car_Season_TA"-  , "TAGame.Car_TA"-  ]--rawObjectClasses :: [(String, String)]-rawObjectClasses =-  [ ("Archetypes.Ball.Ball_Basketball", "TAGame.Ball_TA")-  , ("Archetypes.Ball.Ball_Default", "TAGame.Ball_TA")-  , ("Archetypes.Ball.Ball_Puck", "TAGame.Ball_TA")-  , ("Archetypes.Ball.CubeBall", "TAGame.Ball_TA")-  , ("Archetypes.Car.Car_Default", "TAGame.Car_TA")-  , ("Archetypes.CarComponents.CarComponent_Boost", "TAGame.CarComponent_Boost_TA")-  , ("Archetypes.CarComponents.CarComponent_Dodge", "TAGame.CarComponent_Dodge_TA")-  , ("Archetypes.CarComponents.CarComponent_DoubleJump", "TAGame.CarComponent_DoubleJump_TA")-  , ("Archetypes.CarComponents.CarComponent_FlipCar", "TAGame.CarComponent_FlipCar_TA")-  , ("Archetypes.CarComponents.CarComponent_Jump", "TAGame.CarComponent_Jump_TA")-  , ("Archetypes.GameEvent.GameEvent_Basketball", "TAGame.GameEvent_Soccar_TA")-  , ("Archetypes.GameEvent.GameEvent_BasketballPrivate", "TAGame.GameEvent_SoccarPrivate_TA")-  , ("Archetypes.GameEvent.GameEvent_BasketballSplitscreen", "TAGame.GameEvent_SoccarSplitscreen_TA")-  , ("Archetypes.GameEvent.GameEvent_Hockey", "TAGame.GameEvent_Soccar_TA")-  , ("Archetypes.GameEvent.GameEvent_HockeyPrivate", "TAGame.GameEvent_SoccarPrivate_TA")-  , ("Archetypes.GameEvent.GameEvent_HockeySplitscreen", "TAGame.GameEvent_SoccarSplitscreen_TA")-  , ("Archetypes.GameEvent.GameEvent_Items", "TAGame.GameEvent_Soccar_TA")-  , ("Archetypes.GameEvent.GameEvent_Season", "TAGame.GameEvent_Season_TA")-  , ("Archetypes.GameEvent.GameEvent_Season:CarArchetype", "TAGame.Car_TA")-  , ("Archetypes.GameEvent.GameEvent_Soccar", "TAGame.GameEvent_Soccar_TA")-  , ("Archetypes.GameEvent.GameEvent_SoccarPrivate", "TAGame.GameEvent_SoccarPrivate_TA")-  , ("Archetypes.GameEvent.GameEvent_SoccarSplitscreen", "TAGame.GameEvent_SoccarSplitscreen_TA")-  , ("Archetypes.SpecialPickups.SpecialPickup_BallFreeze", "TAGame.SpecialPickup_BallFreeze_TA")-  , ("Archetypes.SpecialPickups.SpecialPickup_BallGrapplingHook", "TAGame.SpecialPickup_GrapplingHook_TA")-  , ("Archetypes.SpecialPickups.SpecialPickup_BallLasso", "TAGame.SpecialPickup_BallLasso_TA")-  , ("Archetypes.SpecialPickups.SpecialPickup_BallSpring", "TAGame.SpecialPickup_BallCarSpring_TA")-  , ("Archetypes.SpecialPickups.SpecialPickup_BallVelcro", "TAGame.SpecialPickup_BallVelcro_TA")-  , ("Archetypes.SpecialPickups.SpecialPickup_Batarang", "TAGame.SpecialPickup_Batarang_TA")-  , ("Archetypes.SpecialPickups.SpecialPickup_BoostOverride", "TAGame.SpecialPickup_BoostOverride_TA")-  , ("Archetypes.SpecialPickups.SpecialPickup_CarSpring", "TAGame.SpecialPickup_BallCarSpring_TA")-  , ("Archetypes.SpecialPickups.SpecialPickup_GravityWell", "TAGame.SpecialPickup_BallGravity_TA")-  , ("Archetypes.SpecialPickups.SpecialPickup_StrongHit", "TAGame.SpecialPickup_HitForce_TA")-  , ("Archetypes.SpecialPickups.SpecialPickup_Swapper", "TAGame.SpecialPickup_Swapper_TA")-  , ("Archetypes.SpecialPickups.SpecialPickup_Tornado", "TAGame.SpecialPickup_Tornado_TA")-  , ("Archetypes.Teams.Team0", "TAGame.Team_Soccar_TA")-  , ("Archetypes.Teams.Team1", "TAGame.Team_Soccar_TA")-  , ("GameInfo_Basketball.GameInfo.GameInfo_Basketball:GameReplicationInfoArchetype", "TAGame.GRI_TA")-  , ("GameInfo_Items.GameInfo.GameInfo_Items:GameReplicationInfoArchetype", "TAGame.GRI_TA")-  , ("GameInfo_Season.GameInfo.GameInfo_Season:GameReplicationInfoArchetype", "TAGame.GRI_TA")-  , ("GameInfo_Soccar.GameInfo.GameInfo_Soccar:GameReplicationInfoArchetype", "TAGame.GRI_TA")-  , ("Gameinfo_Hockey.GameInfo.Gameinfo_Hockey:GameReplicationInfoArchetype", "TAGame.GRI_TA")-  , ("TAGame.Default__CameraSettingsActor_TA", "TAGame.CameraSettingsActor_TA")-  , ("TAGame.Default__PRI_TA", "TAGame.PRI_TA")-  , ("TAGame.Default__VoteActor_TA", "TAGame.VoteActor_TA")-  , ("TheWorld:PersistentLevel.CrowdActor_TA", "TAGame.CrowdActor_TA")-  , ("TheWorld:PersistentLevel.CrowdManager_TA", "TAGame.CrowdManager_TA")-  , ("TheWorld:PersistentLevel.InMapScoreboard_TA", "TAGame.InMapScoreboard_TA")-  , ("TheWorld:PersistentLevel.VehiclePickup_Boost_TA", "TAGame.VehiclePickup_Boost_TA")-  ]--rawAttributeTypes :: [(String, AttributeType)]-rawAttributeTypes =-  [ ("Engine.Actor:DrawScale", FloatAttributeType)-  , ("Engine.Actor:Role", EnumAttributeType)-  , ("Engine.Actor:bBlockActors", BooleanAttributeType)-  , ("Engine.Actor:bCollideActors", BooleanAttributeType)-  , ("Engine.Actor:bHidden", BooleanAttributeType)-  , ("Engine.GameReplicationInfo:GameClass", FlaggedIntAttributeType)-  , ("Engine.GameReplicationInfo:ServerName", StringAttributeType)-  , ("Engine.GameReplicationInfo:bMatchIsOver", BooleanAttributeType)-  , ("Engine.Pawn:PlayerReplicationInfo", FlaggedIntAttributeType)-  , ("Engine.PlayerReplicationInfo:Ping", ByteAttributeType)-  , ("Engine.PlayerReplicationInfo:PlayerID", IntAttributeType)-  , ("Engine.PlayerReplicationInfo:PlayerName", StringAttributeType)-  , ("Engine.PlayerReplicationInfo:RemoteUserData", StringAttributeType)-  , ("Engine.PlayerReplicationInfo:Score", IntAttributeType)-  , ("Engine.PlayerReplicationInfo:Team", FlaggedIntAttributeType)-  , ("Engine.PlayerReplicationInfo:UniqueId", UniqueIdAttributeType)-  , ("Engine.PlayerReplicationInfo:bBot", BooleanAttributeType)-  , ("Engine.PlayerReplicationInfo:bIsSpectator", BooleanAttributeType)-  , ("Engine.PlayerReplicationInfo:bReadyToPlay", BooleanAttributeType)-  , ("Engine.PlayerReplicationInfo:bWaitingPlayer", BooleanAttributeType)-  , ("Engine.TeamInfo:Score", IntAttributeType)-  , ("ProjectX.GRI_X:GameServerID", QWordAttributeType)-  , ("ProjectX.GRI_X:ReplicatedGameMutatorIndex", IntAttributeType)-  , ("ProjectX.GRI_X:ReplicatedGamePlaylist", IntAttributeType)-  , ("ProjectX.GRI_X:Reservations", ReservationAttributeType)-  , ("ProjectX.GRI_X:bGameStarted", BooleanAttributeType)-  , ("TAGame.Ball_TA:GameEvent", FlaggedIntAttributeType)-  , ("TAGame.Ball_TA:HitTeamNum", ByteAttributeType)-  , ("TAGame.Ball_TA:ReplicatedAddedCarBounceScale", FloatAttributeType)-  , ("TAGame.Ball_TA:ReplicatedBallMaxLinearSpeedScale", FloatAttributeType)-  , ("TAGame.Ball_TA:ReplicatedBallScale", FloatAttributeType)-  , ("TAGame.Ball_TA:ReplicatedExplosionData", ExplosionAttributeType)-  , ("TAGame.Ball_TA:ReplicatedWorldBounceScale", FloatAttributeType)-  , ("TAGame.CameraSettingsActor_TA:CameraPitch", ByteAttributeType)-  , ("TAGame.CameraSettingsActor_TA:CameraYaw", ByteAttributeType)-  , ("TAGame.CameraSettingsActor_TA:PRI", FlaggedIntAttributeType)-  , ("TAGame.CameraSettingsActor_TA:ProfileSettings", CamSettingsAttributeType)-  , ("TAGame.CameraSettingsActor_TA:bUsingBehindView", BooleanAttributeType)-  , ("TAGame.CameraSettingsActor_TA:bUsingSecondaryCamera", BooleanAttributeType)-  , ("TAGame.CarComponent_Boost_TA:BoostModifier", FloatAttributeType)-  , ("TAGame.CarComponent_Boost_TA:RechargeDelay", FloatAttributeType)-  , ("TAGame.CarComponent_Boost_TA:RechargeRate", FloatAttributeType)-  , ("TAGame.CarComponent_Boost_TA:ReplicatedBoostAmount", ByteAttributeType)-  , ("TAGame.CarComponent_Boost_TA:UnlimitedBoostRefCount", IntAttributeType)-  , ("TAGame.CarComponent_Boost_TA:bNoBoost", BooleanAttributeType)-  , ("TAGame.CarComponent_Boost_TA:bUnlimitedBoost", BooleanAttributeType)-  , ("TAGame.CarComponent_Dodge_TA:DodgeTorque", LocationAttributeType)-  , ("TAGame.CarComponent_FlipCar_TA:FlipCarTime", FloatAttributeType)-  , ("TAGame.CarComponent_FlipCar_TA:bFlipRight", BooleanAttributeType)-  , ("TAGame.CarComponent_TA:ReplicatedActive", ByteAttributeType)-  , ("TAGame.CarComponent_TA:ReplicatedActivityTime", FloatAttributeType)-  , ("TAGame.CarComponent_TA:Vehicle", FlaggedIntAttributeType)-  , ("TAGame.Car_TA:AddedBallForceMultiplier", FloatAttributeType)-  , ("TAGame.Car_TA:AddedCarForceMultiplier", FloatAttributeType)-  , ("TAGame.Car_TA:AttachedPickup", FlaggedIntAttributeType)-  , ("TAGame.Car_TA:ClubColors", ClubColorsAttributeType)-  , ("TAGame.Car_TA:ReplicatedDemolish", DemolishAttributeType)-  , ("TAGame.Car_TA:TeamPaint", TeamPaintAttributeType)-  , ("TAGame.CrowdActor_TA:GameEvent", FlaggedIntAttributeType)-  , ("TAGame.CrowdActor_TA:ModifiedNoise", FloatAttributeType)-  , ("TAGame.CrowdActor_TA:ReplicatedCountDownNumber", IntAttributeType)-  , ("TAGame.CrowdActor_TA:ReplicatedOneShotSound", FlaggedIntAttributeType)-  , ("TAGame.CrowdActor_TA:ReplicatedRoundCountDownNumber", IntAttributeType)-  , ("TAGame.CrowdManager_TA:GameEvent", FlaggedIntAttributeType)-  , ("TAGame.CrowdManager_TA:ReplicatedGlobalOneShotSound", FlaggedIntAttributeType)-  , ("TAGame.GRI_TA:NewDedicatedServerIP", StringAttributeType)-  , ("TAGame.GameEvent_SoccarPrivate_TA:MatchSettings", PrivateMatchSettingsAttributeType)-  , ("TAGame.GameEvent_Soccar_TA:ReplicatedMusicStinger", MusicStingerAttributeType)-  , ("TAGame.GameEvent_Soccar_TA:ReplicatedScoredOnTeam", ByteAttributeType)-  , ("TAGame.GameEvent_Soccar_TA:RoundNum", IntAttributeType)-  , ("TAGame.GameEvent_Soccar_TA:SecondsRemaining", IntAttributeType)-  , ("TAGame.GameEvent_Soccar_TA:SubRulesArchetype", FlaggedIntAttributeType)-  , ("TAGame.GameEvent_Soccar_TA:bBallHasBeenHit", BooleanAttributeType)-  , ("TAGame.GameEvent_Soccar_TA:bOverTime", BooleanAttributeType)-  , ("TAGame.GameEvent_TA:BotSkill", IntAttributeType)-  , ("TAGame.GameEvent_TA:GameMode", GameModeAttributeType)-  , ("TAGame.GameEvent_TA:MatchTypeClass", FlaggedIntAttributeType)-  , ("TAGame.GameEvent_TA:ReplicatedGameStateTimeRemaining", IntAttributeType)-  , ("TAGame.GameEvent_TA:ReplicatedStateIndex", ByteAttributeType)-  , ("TAGame.GameEvent_TA:ReplicatedStateName", IntAttributeType)-  , ("TAGame.GameEvent_TA:bCanVoteToForfeit", BooleanAttributeType)-  , ("TAGame.GameEvent_TA:bHasLeaveMatchPenalty", BooleanAttributeType)-  , ("TAGame.GameEvent_Team_TA:MaxTeamSize", IntAttributeType)-  , ("TAGame.PRI_TA:CameraPitch", ByteAttributeType)-  , ("TAGame.PRI_TA:CameraSettings", CamSettingsAttributeType)-  , ("TAGame.PRI_TA:CameraYaw", ByteAttributeType)-  , ("TAGame.PRI_TA:ClientLoadout", LoadoutAttributeType)-  , ("TAGame.PRI_TA:ClientLoadoutOnline", LoadoutOnlineAttributeType)-  , ("TAGame.PRI_TA:ClientLoadouts", LoadoutsAttributeType)-  , ("TAGame.PRI_TA:ClientLoadoutsOnline", LoadoutsOnlineAttributeType)-  , ("TAGame.PRI_TA:MatchAssists", IntAttributeType)-  , ("TAGame.PRI_TA:MatchGoals", IntAttributeType)-  , ("TAGame.PRI_TA:MatchSaves", IntAttributeType)-  , ("TAGame.PRI_TA:MatchScore", IntAttributeType)-  , ("TAGame.PRI_TA:MatchShots", IntAttributeType)-  , ("TAGame.PRI_TA:PartyLeader", PartyLeaderAttributeType)-  , ("TAGame.PRI_TA:PawnType", ByteAttributeType)-  , ("TAGame.PRI_TA:PersistentCamera", FlaggedIntAttributeType)-  , ("TAGame.PRI_TA:ReplicatedGameEvent", FlaggedIntAttributeType)-  , ("TAGame.PRI_TA:Title", IntAttributeType)-  , ("TAGame.PRI_TA:TotalXP", IntAttributeType)-  , ("TAGame.PRI_TA:bIsInSplitScreen", BooleanAttributeType)-  , ("TAGame.PRI_TA:bMatchMVP", BooleanAttributeType)-  , ("TAGame.PRI_TA:bOnlineLoadoutSet", BooleanAttributeType)-  , ("TAGame.PRI_TA:bOnlineLoadoutsSet", BooleanAttributeType)-  , ("TAGame.PRI_TA:bReady", BooleanAttributeType)-  , ("TAGame.PRI_TA:bUsingBehindView", BooleanAttributeType)-  , ("TAGame.PRI_TA:bUsingSecondaryCamera", BooleanAttributeType)-  , ("TAGame.RBActor_TA:ReplicatedRBState", RigidBodyStateAttributeType)-  , ("TAGame.RBActor_TA:WeldedInfo", WeldedInfoAttributeType)-  , ("TAGame.RBActor_TA:bFrozen", BooleanAttributeType)-  , ("TAGame.RBActor_TA:bIgnoreSyncing", BooleanAttributeType)-  , ("TAGame.RBActor_TA:bReplayActor", BooleanAttributeType)-  , ("TAGame.SpecialPickup_BallFreeze_TA:RepOrigSpeed", FloatAttributeType)-  , ("TAGame.SpecialPickup_BallVelcro_TA:AttachTime", FloatAttributeType)-  , ("TAGame.SpecialPickup_BallVelcro_TA:BreakTime", FloatAttributeType)-  , ("TAGame.SpecialPickup_BallVelcro_TA:bBroken", BooleanAttributeType)-  , ("TAGame.SpecialPickup_BallVelcro_TA:bHit", BooleanAttributeType)-  , ("TAGame.SpecialPickup_Targeted_TA:Targeted", FlaggedIntAttributeType)-  , ("TAGame.Team_Soccar_TA:GameScore", IntAttributeType)-  , ("TAGame.Team_TA:ClubColors", ClubColorsAttributeType)-  , ("TAGame.Team_TA:CustomTeamName", StringAttributeType)-  , ("TAGame.Team_TA:GameEvent", FlaggedIntAttributeType)-  , ("TAGame.Team_TA:LogoData", FlaggedIntAttributeType)-  , ("TAGame.VehiclePickup_TA:ReplicatedPickupData", PickupAttributeType)-  , ("TAGame.Vehicle_TA:ReplicatedSteer", ByteAttributeType)-  , ("TAGame.Vehicle_TA:ReplicatedThrottle", ByteAttributeType)-  , ("TAGame.Vehicle_TA:bDriving", BooleanAttributeType)-  , ("TAGame.Vehicle_TA:bReplicatedHandbrake", BooleanAttributeType)-  ]--rawCrc32Table :: Integral a => [a]-rawCrc32Table =-  [ 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9-  , 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005-  , 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61-  , 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd-  , 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9-  , 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75-  , 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011-  , 0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd-  , 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039-  , 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5-  , 0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81-  , 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d-  , 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49-  , 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95-  , 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1-  , 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d-  , 0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae-  , 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072-  , 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16-  , 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca-  , 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde-  , 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02-  , 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066-  , 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba-  , 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e-  , 0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692-  , 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6-  , 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a-  , 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e-  , 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2-  , 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686-  , 0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a-  , 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637-  , 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb-  , 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f-  , 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53-  , 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47-  , 0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b-  , 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff-  , 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623-  , 0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7-  , 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b-  , 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f-  , 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3-  , 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7-  , 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b-  , 0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f-  , 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3-  , 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640-  , 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c-  , 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8-  , 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24-  , 0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30-  , 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec-  , 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088-  , 0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654-  , 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0-  , 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c-  , 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18-  , 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4-  , 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0-  , 0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c-  , 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668-  , 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4-  ]
− library/Rattletrap/Frame.hs
@@ -1,51 +0,0 @@-module Rattletrap.Frame where--import Rattletrap.ActorMap-import Rattletrap.ClassAttributeMap-import Rattletrap.Primitive-import Rattletrap.Replication--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--data Frame = Frame-  { frameTime :: Float32-  , frameDelta :: Float32-  , frameReplications :: [Replication]-  } deriving (Eq, Ord, Show)--getFrames-  :: (Int, Int)-  -> Int-  -> ClassAttributeMap-  -> ActorMap-  -> BinaryBit.BitGet ([Frame], ActorMap)-getFrames version numFrames classAttributeMap actorMap =-  if numFrames <= 0-    then pure ([], actorMap)-    else do-      (frame, newActorMap) <- getFrame version classAttributeMap actorMap-      (frames, newerActorMap) <--        getFrames version (numFrames - 1) classAttributeMap newActorMap-      pure (frame : frames, newerActorMap)--putFrames :: [Frame] -> BinaryBit.BitPut ()-putFrames = mapM_ putFrame--getFrame-  :: (Int, Int)-  -> ClassAttributeMap-  -> ActorMap-  -> BinaryBit.BitGet (Frame, ActorMap)-getFrame version classAttributeMap actorMap = do-  time <- getFloat32Bits-  delta <- getFloat32Bits-  (replications, newActorMap) <--    getReplications version classAttributeMap actorMap-  pure (Frame time delta replications, newActorMap)--putFrame :: Frame -> BinaryBit.BitPut ()-putFrame frame = do-  putFloat32Bits (frameTime frame)-  putFloat32Bits (frameDelta frame)-  putReplications (frameReplications frame)
− library/Rattletrap/Header.hs
@@ -1,59 +0,0 @@-module Rattletrap.Header where--import Rattletrap.Primitive-import Rattletrap.Property-import Rattletrap.PropertyValue--import qualified Data.Binary as Binary--data Header = Header-  { headerEngineVersion :: Word32-  , headerLicenseeVersion :: Word32-  , headerLabel :: Text-  , headerProperties :: Dictionary Property-  } deriving (Eq, Ord, Show)--getHeader :: Binary.Get Header-getHeader = do-  engineVersion <- getWord32-  licenseeVersion <- getWord32-  label <- getText-  properties <- getDictionary getProperty-  pure (Header engineVersion licenseeVersion label properties)--putHeader :: Header -> Binary.Put-putHeader header = do-  putWord32 (headerEngineVersion header)-  putWord32 (headerLicenseeVersion header)-  putText (headerLabel header)-  putDictionary putProperty (headerProperties header)--getVersion-  :: (Integral a, Integral b)-  => Header -> (a, b)-getVersion header =-  let major = getMajorVersion header-      minor = getMinorVersion header-  in (major, minor)--getMajorVersion-  :: (Integral a)-  => Header -> a-getMajorVersion header = fromIntegral (word32Value (headerEngineVersion header))--getMinorVersion-  :: (Integral a)-  => Header -> a-getMinorVersion header =-  fromIntegral (word32Value (headerLicenseeVersion header))--getNumFrames-  :: (Integral a)-  => Header -> a-getNumFrames header =-  let key = stringToText "NumFrames"-      properties = dictionaryValue (headerProperties header)-  in case lookup key properties of-       Just (Just (Property _ _ (IntProperty numFrames))) ->-         fromIntegral (int32Value numFrames)-       _ -> 0
− library/Rattletrap/Initialization.hs
@@ -1,36 +0,0 @@-module Rattletrap.Initialization where--import Rattletrap.Primitive--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--data Initialization = Initialization-  { initializationLocation :: Maybe Vector-  , initializationRotation :: Maybe Int8Vector-  } deriving (Eq, Ord, Show)--getInitialization :: Bool -> Bool -> BinaryBit.BitGet Initialization-getInitialization hasLocation hasRotation = do-  location <--    if hasLocation-      then do-        location <- getVector-        pure (Just location)-      else pure Nothing-  rotation <--    if hasRotation-      then do-        rotation <- getInt8Vector-        pure (Just rotation)-      else pure Nothing-  pure (Initialization location rotation)--putInitialization :: Initialization -> BinaryBit.BitPut ()-putInitialization initialization = do-  case initializationLocation initialization of-    Nothing -> pure ()-    Just location -> putVector location-  case initializationRotation initialization of-    Nothing -> pure ()-    Just rotation -> putInt8Vector rotation
− library/Rattletrap/Json.hs
@@ -1,105 +0,0 @@-{-# LANGUAGE TemplateHaskell #-}-{-# OPTIONS_GHC -fno-warn-orphans #-}--module Rattletrap.Json where--import Rattletrap.Attribute-import Rattletrap.AttributeMapping-import Rattletrap.AttributeValue-import Rattletrap.Cache-import Rattletrap.ClassMapping-import Rattletrap.Content-import Rattletrap.Frame-import Rattletrap.Header-import Rattletrap.Initialization-import Rattletrap.KeyFrame-import Rattletrap.Mark-import Rattletrap.Message-import Rattletrap.Primitive-import Rattletrap.Property-import Rattletrap.PropertyValue-import Rattletrap.RemoteId-import Rattletrap.Replay-import Rattletrap.Replication-import Rattletrap.ReplicationValue--import qualified Control.Monad as Monad-import qualified Data.Aeson.Casing as Casing-import qualified Data.Aeson.TH as Aeson-import qualified Language.Haskell.TH as TH--$(let optionsFor name =-        (Casing.aesonDrop (length (TH.nameBase name)) Casing.snakeCase)-        { Aeson.constructorTagModifier = Casing.snakeCase-        , Aeson.omitNothingFields = True-        , Aeson.sumEncoding = Aeson.ObjectWithSingleField-        , Aeson.unwrapUnaryRecords = True-        }-      deriveJSON declarations name = do-        newDeclarations <- Aeson.deriveJSON (optionsFor name) name-        pure (newDeclarations ++ declarations)-      names =-        [ ''Attribute-        , ''AttributeMapping-        , ''AttributeValue-        , ''BooleanAttribute-        , ''ByteAttribute-        , ''Cache-        , ''CamSettingsAttribute-        , ''ClassMapping-        , ''ClubColorsAttribute-        , ''CompressedWord-        , ''CompressedWordVector-        , ''Content-        , ''DemolishAttribute-        , ''DestroyedReplication-        , ''Dictionary-        , ''EnumAttribute-        , ''ExplosionAttribute-        , ''FlaggedIntAttribute-        , ''Float32-        , ''FloatAttribute-        , ''Frame-        , ''GameModeAttribute-        , ''Header-        , ''Initialization-        , ''Int32-        , ''Int8-        , ''Int8Vector-        , ''IntAttribute-        , ''KeyFrame-        , ''List-        , ''LoadoutAttribute-        , ''LoadoutOnlineAttribute-        , ''LoadoutsAttribute-        , ''LoadoutsOnlineAttribute-        , ''LocationAttribute-        , ''Mark-        , ''Message-        , ''MusicStingerAttribute-        , ''PartyLeaderAttribute-        , ''PickupAttribute-        , ''PrivateMatchSettingsAttribute-        , ''Property-        , ''PropertyValue-        , ''QWordAttribute-        , ''RemoteId-        , ''Replay-        , ''Replication-        , ''ReplicationValue-        , ''ReservationAttribute-        , ''RigidBodyStateAttribute-        , ''Section-        , ''SpawnedReplication-        , ''StringAttribute-        , ''TeamPaintAttribute-        , ''Text-        , ''UniqueIdAttribute-        , ''UpdatedReplication-        , ''Vector-        , ''WeldedInfoAttribute-        , ''Word32-        , ''Word64-        , ''Word8-        ]-  in Monad.foldM deriveJSON [] names)
− library/Rattletrap/KeyFrame.hs
@@ -1,24 +0,0 @@-module Rattletrap.KeyFrame where--import Rattletrap.Primitive--import qualified Data.Binary as Binary--data KeyFrame = KeyFrame-  { keyFrameTime :: Float32-  , keyFrameFrame :: Word32-  , keyFramePosition :: Word32-  } deriving (Eq, Ord, Show)--getKeyFrame :: Binary.Get KeyFrame-getKeyFrame = do-  time <- getFloat32-  frame <- getWord32-  position <- getWord32-  pure (KeyFrame time frame position)--putKeyFrame :: KeyFrame -> Binary.Put-putKeyFrame keyFrame = do-  putFloat32 (keyFrameTime keyFrame)-  putWord32 (keyFrameFrame keyFrame)-  putWord32 (keyFramePosition keyFrame)
− library/Rattletrap/Main.hs
@@ -1,47 +0,0 @@-module Rattletrap.Main where--import Rattletrap.Json ()-import Rattletrap.Replay-import Rattletrap.Version--import qualified Data.Aeson as Aeson-import qualified Data.Binary.Get as Binary-import qualified Data.Binary.Put as Binary-import qualified Data.ByteString.Lazy as ByteString-import qualified Data.Version as Version-import qualified System.Environment as Environment--main :: IO ()-main = do-  args <- Environment.getArgs-  mainWithArgs args--mainWithArgs :: [String] -> IO ()-mainWithArgs args =-  case args of-    ["version"] -> putStrLn (Version.showVersion version)-    "decode":files -> do-      (getInput, putOutput) <- getIO files-      input <- getInput-      let replay = Binary.runGet getReplay input-      let output = Aeson.encode replay-      putOutput output-    "encode":files -> do-      (getInput, putOutput) <- getIO files-      input <- getInput-      case Aeson.eitherDecode input of-        Left message -> fail ("could not parse JSON: " ++ message)-        Right replay -> do-          let output = Binary.runPut (putReplay replay)-          putOutput output-    _ -> fail ("unexpected arguments " ++ show args)--getIO-  :: Monad m-  => [FilePath] -> m (IO ByteString.ByteString, ByteString.ByteString -> IO ())-getIO files =-  case files of-    [] -> pure (ByteString.getContents, ByteString.putStr)-    [i] -> pure (ByteString.readFile i, ByteString.putStr)-    [i, o] -> pure (ByteString.readFile i, ByteString.writeFile o)-    _ -> fail ("unexpected arguments " ++ show files)
− library/Rattletrap/Mark.hs
@@ -1,21 +0,0 @@-module Rattletrap.Mark where--import Rattletrap.Primitive--import qualified Data.Binary as Binary--data Mark = Mark-  { markValue :: Text-  , markFrame :: Word32-  } deriving (Eq, Ord, Show)--getMark :: Binary.Get Mark-getMark = do-  value <- getText-  frame <- getWord32-  pure (Mark value frame)--putMark :: Mark -> Binary.Put-putMark mark = do-  putText (markValue mark)-  putWord32 (markFrame mark)
− library/Rattletrap/Message.hs
@@ -1,24 +0,0 @@-module Rattletrap.Message where--import Rattletrap.Primitive--import qualified Data.Binary as Binary--data Message = Message-  { messageFrame :: Word32-  , messageName :: Text-  , messageValue :: Text-  } deriving (Eq, Ord, Show)--getMessage :: Binary.Get Message-getMessage = do-  frame <- getWord32-  name <- getText-  value <- getText-  pure (Message frame name value)--putMessage :: Message -> Binary.Put-putMessage message = do-  putWord32 (messageFrame message)-  putText (messageName message)-  putText (messageValue message)
− library/Rattletrap/Primitive.hs
@@ -1,31 +0,0 @@-module Rattletrap.Primitive-  ( module Rattletrap.Primitive.CompressedWord-  , module Rattletrap.Primitive.CompressedWordVector-  , module Rattletrap.Primitive.Dictionary-  , module Rattletrap.Primitive.Float32-  , module Rattletrap.Primitive.Int8-  , module Rattletrap.Primitive.Int8Vector-  , module Rattletrap.Primitive.Int32-  , module Rattletrap.Primitive.List-  , module Rattletrap.Primitive.Section-  , module Rattletrap.Primitive.Text-  , module Rattletrap.Primitive.Vector-  , module Rattletrap.Primitive.Word8-  , module Rattletrap.Primitive.Word32-  , module Rattletrap.Primitive.Word64-  ) where--import Rattletrap.Primitive.CompressedWord-import Rattletrap.Primitive.CompressedWordVector-import Rattletrap.Primitive.Dictionary-import Rattletrap.Primitive.Float32-import Rattletrap.Primitive.Int8-import Rattletrap.Primitive.Int8Vector-import Rattletrap.Primitive.Int32-import Rattletrap.Primitive.List-import Rattletrap.Primitive.Section-import Rattletrap.Primitive.Text-import Rattletrap.Primitive.Vector-import Rattletrap.Primitive.Word8-import Rattletrap.Primitive.Word32-import Rattletrap.Primitive.Word64
− library/Rattletrap/Primitive/CompressedWord.hs
@@ -1,59 +0,0 @@-module Rattletrap.Primitive.CompressedWord where--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit-import qualified Data.Bits as Bits--data CompressedWord = CompressedWord-  { compressedWordLimit :: Word-  , compressedWordValue :: Word-  } deriving (Eq, Ord, Show)--getCompressedWord :: Word -> BinaryBit.BitGet CompressedWord-getCompressedWord limit = do-  value <- getCompressedWordStep limit (getMaxBits limit) 0 0-  pure (CompressedWord limit value)--putCompressedWord :: CompressedWord -> BinaryBit.BitPut ()-putCompressedWord compressedWord = do-  let limit = compressedWordLimit compressedWord-  let value = compressedWordValue compressedWord-  let maxBits = getMaxBits limit-  let go position soFar =-        if position < maxBits-          then do-            let x = Bits.shiftL 1 position-            if maxBits > 1 && position == maxBits - 1 && soFar + x > limit-              then pure ()-              else do-                let bit = Bits.testBit value position-                BinaryBit.putBool bit-                let delta =-                      if bit-                        then x-                        else 0-                go (position + 1) (soFar + delta)-          else pure ()-  go 0 0--getMaxBits-  :: (Integral a, Integral b)-  => a -> b-getMaxBits x = do-  let n = max 1 (ceiling (logBase (2 :: Double) (fromIntegral (max 1 x))))-  if x < 1024 && x == 2 ^ n-    then n + 1-    else n--getCompressedWordStep :: Word -> Word -> Word -> Word -> BinaryBit.BitGet Word-getCompressedWordStep limit maxBits position value = do-  let x = Bits.shiftL 1 (fromIntegral position)-  if position < maxBits && value + x <= limit-    then do-      bit <- BinaryBit.getBool-      let newValue =-            if bit-              then value + x-              else value-      getCompressedWordStep limit maxBits (position + 1) newValue-    else pure value
− library/Rattletrap/Primitive/CompressedWordVector.hs
@@ -1,26 +0,0 @@-module Rattletrap.Primitive.CompressedWordVector where--import Rattletrap.Primitive.CompressedWord--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--data CompressedWordVector = CompressedWordVector-  { compressedWordVectorX :: CompressedWord-  , compressedWordVectorY :: CompressedWord-  , compressedWordVectorZ :: CompressedWord-  } deriving (Eq, Ord, Show)--getCompressedWordVector :: BinaryBit.BitGet CompressedWordVector-getCompressedWordVector = do-  let limit = 65536-  x <- getCompressedWord limit-  y <- getCompressedWord limit-  z <- getCompressedWord limit-  pure (CompressedWordVector x y z)--putCompressedWordVector :: CompressedWordVector -> BinaryBit.BitPut ()-putCompressedWordVector compressedWordVector = do-  putCompressedWord (compressedWordVectorX compressedWordVector)-  putCompressedWord (compressedWordVectorY compressedWordVector)-  putCompressedWord (compressedWordVectorZ compressedWordVector)
− library/Rattletrap/Primitive/Dictionary.hs
@@ -1,33 +0,0 @@-module Rattletrap.Primitive.Dictionary where--import Rattletrap.Primitive.Text--import qualified Data.Binary as Binary--newtype Dictionary a = Dictionary-  { dictionaryValue :: [(Text, Maybe a)]-  } deriving (Eq, Ord, Show)--getDictionary :: Binary.Get a -> Binary.Get (Dictionary a)-getDictionary getValue = do-  key <- getText-  if isNoneKey key-    then pure (Dictionary [(key, Nothing)])-    else do-      value <- getValue-      let element = (key, Just value)-      Dictionary elements <- getDictionary getValue-      pure (Dictionary (element : elements))--putDictionary :: (a -> Binary.Put) -> Dictionary a -> Binary.Put-putDictionary putValue dictionary =-  mapM_-    (\(key, maybeValue) -> do-       putText key-       case maybeValue of-         Nothing -> pure ()-         Just value -> putValue value)-    (dictionaryValue dictionary)--isNoneKey :: Text -> Bool-isNoneKey text = filter (/= '\x00') (textToString text) == "None"
− library/Rattletrap/Primitive/Float32.hs
@@ -1,33 +0,0 @@-module Rattletrap.Primitive.Float32 where--import Rattletrap.Utility--import qualified Data.Binary as Binary-import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit-import qualified Data.Binary.Get as Binary-import qualified Data.Binary.IEEE754 as IEEE754-import qualified Data.Binary.Put as Binary-import qualified Data.ByteString.Lazy as ByteString--newtype Float32 = Float32-  { float32Value :: Float-  } deriving (Eq, Ord, Show)--getFloat32 :: Binary.Get Float32-getFloat32 = do-  float32 <- IEEE754.getFloat32le-  pure (Float32 float32)--putFloat32 :: Float32 -> Binary.Put-putFloat32 float32 = IEEE754.putFloat32le (float32Value float32)--getFloat32Bits :: BinaryBit.BitGet Float32-getFloat32Bits = do-  bytes <- BinaryBit.getLazyByteString 4-  pure (Binary.runGet getFloat32 (reverseBytes bytes))--putFloat32Bits :: Float32 -> BinaryBit.BitPut ()-putFloat32Bits float32 = do-  let bytes = Binary.runPut (putFloat32 float32)-  BinaryBit.putByteString (ByteString.toStrict (reverseBytes bytes))
− library/Rattletrap/Primitive/Int32.hs
@@ -1,33 +0,0 @@-module Rattletrap.Primitive.Int32 where--import Rattletrap.Utility--import qualified Data.Binary as Binary-import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit-import qualified Data.Binary.Get as Binary-import qualified Data.Binary.Put as Binary-import qualified Data.ByteString.Lazy as ByteString-import qualified Data.Int as Int--newtype Int32 = Int32-  { int32Value :: Int.Int32-  } deriving (Eq, Ord, Show)--getInt32 :: Binary.Get Int32-getInt32 = do-  int32 <- Binary.getInt32le-  pure (Int32 int32)--putInt32 :: Int32 -> Binary.Put-putInt32 int32 = Binary.putInt32le (int32Value int32)--getInt32Bits :: BinaryBit.BitGet Int32-getInt32Bits = do-  bytes <- BinaryBit.getLazyByteString 4-  pure (Binary.runGet getInt32 (reverseBytes bytes))--putInt32Bits :: Int32 -> BinaryBit.BitPut ()-putInt32Bits int32 = do-  let bytes = Binary.runPut (putInt32 int32)-  BinaryBit.putByteString (ByteString.toStrict (reverseBytes bytes))
− library/Rattletrap/Primitive/Int8.hs
@@ -1,32 +0,0 @@-module Rattletrap.Primitive.Int8 where--import Rattletrap.Utility--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit-import qualified Data.Binary.Get as Binary-import qualified Data.Binary.Put as Binary-import qualified Data.ByteString.Lazy as ByteString-import qualified Data.Int as Int--newtype Int8 = Int8-  { int8Value :: Int.Int8-  } deriving (Eq, Ord, Show)--getInt8 :: Binary.Get Int8-getInt8 = do-  int8 <- Binary.getInt8-  pure (Int8 int8)--putInt8 :: Int8 -> Binary.Put-putInt8 int8 = Binary.putInt8 (int8Value int8)--getInt8Bits :: BinaryBit.BitGet Int8-getInt8Bits = do-  bytes <- BinaryBit.getLazyByteString 1-  pure (Binary.runGet getInt8 (reverseBytes bytes))--putInt8Bits :: Int8 -> BinaryBit.BitPut ()-putInt8Bits int8 = do-  let bytes = Binary.runPut (putInt8 int8)-  BinaryBit.putByteString (ByteString.toStrict (reverseBytes bytes))
− library/Rattletrap/Primitive/Int8Vector.hs
@@ -1,42 +0,0 @@-module Rattletrap.Primitive.Int8Vector where--import Rattletrap.Primitive.Int8--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--data Int8Vector = Int8Vector-  { int8VectorX :: Maybe Int8-  , int8VectorY :: Maybe Int8-  , int8VectorZ :: Maybe Int8-  } deriving (Eq, Ord, Show)--getInt8Vector :: BinaryBit.BitGet Int8Vector-getInt8Vector = do-  x <- getInt8VectorField-  y <- getInt8VectorField-  z <- getInt8VectorField-  pure (Int8Vector x y z)--putInt8Vector :: Int8Vector -> BinaryBit.BitPut ()-putInt8Vector int8Vector = do-  putInt8VectorField (int8VectorX int8Vector)-  putInt8VectorField (int8VectorY int8Vector)-  putInt8VectorField (int8VectorZ int8Vector)--getInt8VectorField :: BinaryBit.BitGet (Maybe Int8)-getInt8VectorField = do-  hasField <- BinaryBit.getBool-  if hasField-    then do-      field <- getInt8Bits-      pure (Just field)-    else pure Nothing--putInt8VectorField :: Maybe Int8 -> BinaryBit.BitPut ()-putInt8VectorField maybeField =-  case maybeField of-    Nothing -> BinaryBit.putBool False-    Just field -> do-      BinaryBit.putBool True-      putInt8Bits field
− library/Rattletrap/Primitive/List.hs
@@ -1,22 +0,0 @@-module Rattletrap.Primitive.List where--import Rattletrap.Primitive.Word32--import qualified Control.Monad as Monad-import qualified Data.Binary as Binary--newtype List a = List-  { listValue :: [a]-  } deriving (Eq, Ord, Show)--getList :: Binary.Get a -> Binary.Get (List a)-getList getElement = do-  size <- getWord32-  elements <- Monad.replicateM (fromIntegral (word32Value size)) getElement-  pure (List elements)--putList :: (a -> Binary.Put) -> List a -> Binary.Put-putList putElement list = do-  let elements = listValue list-  putWord32 (Word32 (fromIntegral (length elements)))-  mapM_ putElement elements
− library/Rattletrap/Primitive/Section.hs
@@ -1,38 +0,0 @@-module Rattletrap.Primitive.Section where--import Rattletrap.Crc-import Rattletrap.Primitive.Word32--import qualified Control.Monad as Monad-import qualified Data.Binary as Binary-import qualified Data.Binary.Get as Binary-import qualified Data.Binary.Put as Binary-import qualified Data.ByteString.Lazy as ByteString--newtype Section a = Section-  { sectionBody :: a-  } deriving (Eq, Ord, Show)--getSection :: Binary.Get a -> Binary.Get (Section a)-getSection getBody = do-  size <- getWord32-  crc <- getWord32-  rawBody <- Binary.getLazyByteString (fromIntegral (word32Value size))-  let actualCrc = Word32 (getCrc32 rawBody)-  Monad.when (actualCrc /= crc) (fail (crcMessage actualCrc crc))-  let body = Binary.runGet getBody rawBody-  pure (Section body)--putSection :: (a -> Binary.Put) -> Section a -> Binary.Put-putSection putBody section = do-  let rawBody = Binary.runPut (putBody (sectionBody section))-  let size = ByteString.length rawBody-  let crc = getCrc32 rawBody-  putWord32 (Word32 (fromIntegral size))-  putWord32 (Word32 crc)-  Binary.putLazyByteString rawBody--crcMessage :: Word32 -> Word32 -> String-crcMessage actual expected =-  unwords-    ["actual CRC", show actual, "does not match expected CRC", show expected]
− library/Rattletrap/Primitive/Text.hs
@@ -1,88 +0,0 @@-module Rattletrap.Primitive.Text where--import Rattletrap.Primitive.Int32-import Rattletrap.Utility--import qualified Data.Binary as Binary-import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit-import qualified Data.Binary.Get as Binary-import qualified Data.Binary.Put as Binary-import qualified Data.ByteString.Lazy.Char8 as ByteString-import qualified Data.Text as Text-import qualified Data.Text.Encoding as Encoding--data Text = Text-  { textSize :: Int32-  , textValue :: Text.Text-  } deriving (Eq, Ord, Show)--getText :: Binary.Get Text-getText = do-  rawSize <- getInt32-  let decode = getTextDecoder rawSize-  let size = getTextSize rawSize-  bytes <- Binary.getLazyByteString size-  let text = decode bytes-  pure (Text rawSize text)--putText :: Text -> Binary.Put-putText text = do-  let size = textSize text-  let encode = getTextEncoder size-  putInt32 size-  Binary.putLazyByteString (encode (textValue text))--getTextBits :: BinaryBit.BitGet Text-getTextBits = do-  rawSize <- getInt32Bits-  let decode = getTextDecoder rawSize-  let size = getTextSize rawSize-  bytes <- BinaryBit.getLazyByteString size-  let text = decode (reverseBytes bytes)-  pure (Text rawSize text)--putTextBits :: Text -> BinaryBit.BitPut ()-putTextBits text = do-  let size = textSize text-  let encode = getTextEncoder size-  putInt32Bits size-  BinaryBit.putByteString-    (ByteString.toStrict (reverseBytes (encode (textValue text))))--stringToText :: String -> Text-stringToText string =-  let value = Text.snoc (Text.pack string) '\x00'-      size = Int32 (fromIntegral (Text.length value))-  in Text size value--textToString :: Text -> String-textToString text = Text.unpack (Text.dropWhileEnd (== '\x00') (textValue text))--getTextSize-  :: Integral a-  => Int32 -> a-getTextSize size =-  case int32Value size of-    0x05000000 -> 8-    x ->-      if x < 0-        then (-2 * fromIntegral x)-        else fromIntegral x--getTextDecoder :: Int32 -> ByteString.ByteString -> Text.Text-getTextDecoder size bytes =-  let decode =-        if size < Int32 0-          then Encoding.decodeUtf16LE-          else Encoding.decodeLatin1-  in decode (ByteString.toStrict bytes)--getTextEncoder :: Int32 -> Text.Text -> ByteString.ByteString-getTextEncoder size text =-  if size < Int32 0-    then ByteString.fromStrict (Encoding.encodeUtf16LE text)-    else encodeLatin1 text--encodeLatin1 :: Text.Text -> ByteString.ByteString-encodeLatin1 text = ByteString.pack (Text.unpack text)
− library/Rattletrap/Primitive/Vector.hs
@@ -1,29 +0,0 @@-module Rattletrap.Primitive.Vector where--import Rattletrap.Primitive.CompressedWord--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--data Vector = Vector-  { vectorBitSize :: CompressedWord-  , vectorDx :: CompressedWord-  , vectorDy :: CompressedWord-  , vectorDz :: CompressedWord-  } deriving (Eq, Ord, Show)--getVector :: BinaryBit.BitGet Vector-getVector = do-  bitSize <- getCompressedWord 19-  let limit = 2 ^ (compressedWordValue bitSize + 2)-  dx <- getCompressedWord limit-  dy <- getCompressedWord limit-  dz <- getCompressedWord limit-  pure (Vector bitSize dx dy dz)--putVector :: Vector -> BinaryBit.BitPut ()-putVector vector = do-  putCompressedWord (vectorBitSize vector)-  putCompressedWord (vectorDx vector)-  putCompressedWord (vectorDy vector)-  putCompressedWord (vectorDz vector)
− library/Rattletrap/Primitive/Word32.hs
@@ -1,33 +0,0 @@-module Rattletrap.Primitive.Word32 where--import Rattletrap.Utility--import qualified Data.Binary as Binary-import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit-import qualified Data.Binary.Get as Binary-import qualified Data.Binary.Put as Binary-import qualified Data.ByteString.Lazy as ByteString-import qualified Data.Word as Word--newtype Word32 = Word32-  { word32Value :: Word.Word32-  } deriving (Eq, Ord, Show)--getWord32 :: Binary.Get Word32-getWord32 = do-  word32 <- Binary.getWord32le-  pure (Word32 word32)--putWord32 :: Word32 -> Binary.Put-putWord32 word32 = Binary.putWord32le (word32Value word32)--getWord32Bits :: BinaryBit.BitGet Word32-getWord32Bits = do-  bytes <- BinaryBit.getLazyByteString 4-  pure (Binary.runGet getWord32 (reverseBytes bytes))--putWord32Bits :: Word32 -> BinaryBit.BitPut ()-putWord32Bits word32 = do-  let bytes = Binary.runPut (putWord32 word32)-  BinaryBit.putByteString (ByteString.toStrict (reverseBytes bytes))
− library/Rattletrap/Primitive/Word64.hs
@@ -1,33 +0,0 @@-module Rattletrap.Primitive.Word64 where--import Rattletrap.Utility--import qualified Data.Binary as Binary-import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit-import qualified Data.Binary.Get as Binary-import qualified Data.Binary.Put as Binary-import qualified Data.ByteString.Lazy as ByteString-import qualified Data.Word as Word--newtype Word64 = Word64-  { word64Value :: Word.Word64-  } deriving (Eq, Ord, Show)--getWord64 :: Binary.Get Word64-getWord64 = do-  word64 <- Binary.getWord64le-  pure (Word64 word64)--putWord64 :: Word64 -> Binary.Put-putWord64 word64 = Binary.putWord64le (word64Value word64)--getWord64Bits :: BinaryBit.BitGet Word64-getWord64Bits = do-  bytes <- BinaryBit.getLazyByteString 8-  pure (Binary.runGet getWord64 (reverseBytes bytes))--putWord64Bits :: Word64 -> BinaryBit.BitPut ()-putWord64Bits word64 = do-  let bytes = Binary.runPut (putWord64 word64)-  BinaryBit.putByteString (ByteString.toStrict (reverseBytes bytes))
− library/Rattletrap/Primitive/Word8.hs
@@ -1,33 +0,0 @@-module Rattletrap.Primitive.Word8 where--import Rattletrap.Utility--import qualified Data.Binary as Binary-import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit-import qualified Data.Binary.Get as Binary-import qualified Data.Binary.Put as Binary-import qualified Data.ByteString.Lazy as ByteString-import qualified Data.Word as Word--newtype Word8 = Word8-  { word8Value :: Word.Word8-  } deriving (Eq, Ord, Show)--getWord8 :: Binary.Get Word8-getWord8 = do-  word8 <- Binary.getWord8-  pure (Word8 word8)--putWord8 :: Word8 -> Binary.Put-putWord8 word8 = Binary.putWord8 (word8Value word8)--getWord8Bits :: BinaryBit.BitGet Word8-getWord8Bits = do-  bytes <- BinaryBit.getLazyByteString 1-  pure (Binary.runGet getWord8 (reverseBytes bytes))--putWord8Bits :: Word8 -> BinaryBit.BitPut ()-putWord8Bits word8 = do-  let bytes = Binary.runPut (putWord8 word8)-  BinaryBit.putByteString (ByteString.toStrict (reverseBytes bytes))
− library/Rattletrap/Property.hs
@@ -1,25 +0,0 @@-module Rattletrap.Property where--import Rattletrap.Primitive-import Rattletrap.PropertyValue--import qualified Data.Binary as Binary--data Property = Property-  { propertyKind :: Text-  , propertySize :: Word64-  , propertyValue :: PropertyValue Property-  } deriving (Eq, Ord, Show)--getProperty :: Binary.Get Property-getProperty = do-  kind <- getText-  size <- getWord64-  value <- getPropertyValue getProperty kind-  pure (Property kind size value)--putProperty :: Property -> Binary.Put-putProperty property = do-  putText (propertyKind property)-  putWord64 (propertySize property)-  putPropertyValue putProperty (propertyValue property)
− library/Rattletrap/PropertyValue.hs
@@ -1,68 +0,0 @@-module Rattletrap.PropertyValue where--import Rattletrap.Primitive--import qualified Data.Binary as Binary--data PropertyValue a-  = ArrayProperty (List (Dictionary a))-  | BoolProperty Word8-  | ByteProperty Text-                 (Maybe Text)-  | FloatProperty Float32-  | IntProperty Int32-  | NameProperty Text-  | QWordProperty Word64-  | StrProperty Text-  deriving (Eq, Ord, Show)--getPropertyValue :: Binary.Get a -> Text -> Binary.Get (PropertyValue a)-getPropertyValue getProperty kind =-  case textToString kind of-    "ArrayProperty" -> do-      list <- getList (getDictionary getProperty)-      pure (ArrayProperty list)-    "BoolProperty" -> do-      word8 <- getWord8-      pure (BoolProperty word8)-    "ByteProperty" -> do-      k <- getText-      v <--        if textToString k == "OnlinePlatform_Steam"-          then pure Nothing-          else do-            v <- getText-            pure (Just v)-      pure (ByteProperty k v)-    "FloatProperty" -> do-      float32 <- getFloat32-      pure (FloatProperty float32)-    "IntProperty" -> do-      int32 <- getInt32-      pure (IntProperty int32)-    "NameProperty" -> do-      text <- getText-      pure (NameProperty text)-    "QWordProperty" -> do-      word64 <- getWord64-      pure (QWordProperty word64)-    "StrProperty" -> do-      text <- getText-      pure (StrProperty text)-    _ -> fail ("don't know how to read property value " ++ show kind)--putPropertyValue :: (a -> Binary.Put) -> PropertyValue a -> Binary.Put-putPropertyValue putProperty value =-  case value of-    ArrayProperty list -> putList (putDictionary putProperty) list-    BoolProperty word8 -> putWord8 word8-    ByteProperty k mv -> do-      putText k-      case mv of-        Nothing -> pure ()-        Just v -> putText v-    FloatProperty float32 -> putFloat32 float32-    IntProperty int32 -> putInt32 int32-    NameProperty text -> putText text-    QWordProperty word64 -> putWord64 word64-    StrProperty text -> putText text
− library/Rattletrap/RemoteId.hs
@@ -1,42 +0,0 @@-module Rattletrap.RemoteId where--import Rattletrap.Primitive--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit-import qualified Data.ByteString.Lazy as ByteString-import qualified Data.Word as Word--data RemoteId-  = PlayStationId [Word.Word8]-  | SplitscreenId [Word.Word8]-  | SteamId Word64-  | XboxId Word64-  deriving (Eq, Ord, Show)--getRemoteId :: Word8 -> BinaryBit.BitGet RemoteId-getRemoteId systemId =-  case word8Value systemId of-    0 -> do-      bytes <- BinaryBit.getLazyByteString 3-      pure (SplitscreenId (ByteString.unpack bytes))-    1 -> do-      word64 <- getWord64Bits-      pure (SteamId word64)-    2 -> do-      bytes <- BinaryBit.getLazyByteString 32-      pure (PlayStationId (ByteString.unpack bytes))-    4 -> do-      word64 <- getWord64Bits-      pure (XboxId word64)-    _ -> fail ("unknown system id " ++ show systemId)--putRemoteId :: RemoteId -> BinaryBit.BitPut ()-putRemoteId remoteId =-  case remoteId of-    PlayStationId bytes ->-      BinaryBit.putByteString (ByteString.toStrict (ByteString.pack bytes))-    SplitscreenId bytes ->-      BinaryBit.putByteString (ByteString.toStrict (ByteString.pack bytes))-    SteamId word64 -> putWord64Bits word64-    XboxId word64 -> putWord64Bits word64
− library/Rattletrap/Replay.hs
@@ -1,25 +0,0 @@-module Rattletrap.Replay where--import Rattletrap.Content-import Rattletrap.Header-import Rattletrap.Primitive.Section--import qualified Data.Binary as Binary--data Replay = Replay-  { replayHeader :: Section Header-  , replayContent :: Section Content-  } deriving (Eq, Ord, Show)--getReplay :: Binary.Get Replay-getReplay = do-  header <- getSection getHeader-  let version = getVersion (sectionBody header)-  let numFrames = getNumFrames (sectionBody header)-  content <- getSection (getContent version numFrames)-  pure (Replay header content)--putReplay :: Replay -> Binary.Put-putReplay replay = do-  putSection putHeader (replayHeader replay)-  putSection putContent (replayContent replay)
− library/Rattletrap/Replication.hs
@@ -1,57 +0,0 @@-module Rattletrap.Replication where--import Rattletrap.ActorMap-import Rattletrap.ClassAttributeMap-import Rattletrap.Primitive-import Rattletrap.ReplicationValue--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--data Replication = Replication-  { replicationActorId :: CompressedWord-  , replicationValue :: ReplicationValue-  } deriving (Eq, Ord, Show)--getReplications-  :: (Int, Int)-  -> ClassAttributeMap-  -> ActorMap-  -> BinaryBit.BitGet ([Replication], ActorMap)-getReplications version classAttributeMap actorMap = do-  maybeReplication <- getReplication version classAttributeMap actorMap-  case maybeReplication of-    Nothing -> pure ([], actorMap)-    Just (replication, newActorMap) -> do-      (replications, newerActorMap) <--        getReplications version classAttributeMap newActorMap-      pure (replication : replications, newerActorMap)--putReplications :: [Replication] -> BinaryBit.BitPut ()-putReplications replications = do-  mapM_ putReplication replications-  BinaryBit.putBool False--getReplication-  :: (Int, Int)-  -> ClassAttributeMap-  -> ActorMap-  -> BinaryBit.BitGet (Maybe (Replication, ActorMap))-getReplication version classAttributeMap actorMap = do-  hasReplication <- BinaryBit.getBool-  if not hasReplication-    then pure Nothing-    else do-      actorId <- getCompressedWord maxActorId-      (value, newActorMap) <--        getReplicationValue version classAttributeMap actorMap actorId-      pure (Just (Replication actorId value, newActorMap))--putReplication :: Replication -> BinaryBit.BitPut ()-putReplication replication = do-  BinaryBit.putBool True-  putCompressedWord (replicationActorId replication)-  putReplicationValue (replicationValue replication)--maxActorId :: Word-maxActorId = 1023
− library/Rattletrap/Replication/Destroyed.hs
@@ -1,14 +0,0 @@-module Rattletrap.Replication.Destroyed where--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--data DestroyedReplication = DestroyedReplication-  {-  } deriving (Eq, Ord, Show)--getDestroyedReplication :: BinaryBit.BitGet DestroyedReplication-getDestroyedReplication = pure DestroyedReplication--putDestroyedReplication :: DestroyedReplication -> BinaryBit.BitPut ()-putDestroyedReplication _ = pure ()
− library/Rattletrap/Replication/Spawned.hs
@@ -1,57 +0,0 @@-module Rattletrap.Replication.Spawned where--import Rattletrap.ActorMap-import Rattletrap.ClassAttributeMap-import Rattletrap.Initialization-import Rattletrap.Primitive--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--data SpawnedReplication = SpawnedReplication-  { spawnedReplicationFlag :: Bool-  , spawnedReplicationObjectId :: Word32-  , spawnedReplication_objectName :: Text-  , spawnedReplication_className :: Text-  , spawnedReplicationInitialization :: Initialization-  } deriving (Eq, Ord, Show)--getSpawnedReplication-  :: ClassAttributeMap-  -> ActorMap-  -> CompressedWord-  -> BinaryBit.BitGet (SpawnedReplication, ActorMap)-getSpawnedReplication classAttributeMap actorMap actorId = do-  flag <- BinaryBit.getBool-  objectId <- getWord32Bits-  let newActorMap = updateActorMap actorId objectId actorMap-  objectName <- lookupObjectName classAttributeMap objectId-  className <- lookupClassName objectName-  let hasLocation = classHasLocation className-  let hasRotation = classHasRotation className-  initialization <- getInitialization hasLocation hasRotation-  pure-    ( SpawnedReplication flag objectId objectName className initialization-    , newActorMap)--putSpawnedReplication :: SpawnedReplication -> BinaryBit.BitPut ()-putSpawnedReplication spawnedReplication = do-  BinaryBit.putBool (spawnedReplicationFlag spawnedReplication)-  putWord32Bits (spawnedReplicationObjectId spawnedReplication)-  putInitialization (spawnedReplicationInitialization spawnedReplication)--lookupObjectName-  :: Monad m-  => ClassAttributeMap -> Word32 -> m Text-lookupObjectName classAttributeMap objectId =-  case getObjectName (classAttributeMapObjectMap classAttributeMap) objectId of-    Nothing -> fail ("could not get object name for id " ++ show objectId)-    Just objectName -> pure objectName--lookupClassName-  :: Monad m-  => Text -> m Text-lookupClassName objectName =-  case getClassName objectName of-    Nothing -> fail ("could not get class name for object " ++ show objectName)-    Just className -> pure className
− library/Rattletrap/Replication/Updated.hs
@@ -1,27 +0,0 @@-module Rattletrap.Replication.Updated where--import Rattletrap.ActorMap-import Rattletrap.Attribute-import Rattletrap.ClassAttributeMap-import Rattletrap.Primitive--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--newtype UpdatedReplication = UpdatedReplication-  { updatedReplicationAttributes :: [Attribute]-  } deriving (Eq, Ord, Show)--getUpdatedReplication-  :: (Int, Int)-  -> ClassAttributeMap-  -> ActorMap-  -> CompressedWord-  -> BinaryBit.BitGet UpdatedReplication-getUpdatedReplication version classAttributeMap actorMap actorId = do-  attributes <- getAttributes version classAttributeMap actorMap actorId-  pure (UpdatedReplication attributes)--putUpdatedReplication :: UpdatedReplication -> BinaryBit.BitPut ()-putUpdatedReplication updatedReplication =-  putAttributes (updatedReplicationAttributes updatedReplication)
− library/Rattletrap/ReplicationValue.hs
@@ -1,60 +0,0 @@-module Rattletrap.ReplicationValue-  ( module Rattletrap.ReplicationValue-  , module Rattletrap.Replication.Destroyed-  , module Rattletrap.Replication.Spawned-  , module Rattletrap.Replication.Updated-  ) where--import Rattletrap.ActorMap-import Rattletrap.ClassAttributeMap-import Rattletrap.Primitive-import Rattletrap.Replication.Destroyed-import Rattletrap.Replication.Spawned-import Rattletrap.Replication.Updated--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Binary.Bits.Put as BinaryBit--data ReplicationValue-  = SpawnedReplicationValue SpawnedReplication-  | UpdatedReplicationValue UpdatedReplication-  | DestroyedReplicationValue DestroyedReplication-  deriving (Eq, Ord, Show)--getReplicationValue-  :: (Int, Int)-  -> ClassAttributeMap-  -> ActorMap-  -> CompressedWord-  -> BinaryBit.BitGet (ReplicationValue, ActorMap)-getReplicationValue version classAttributeMap actorMap actorId = do-  isOpen <- BinaryBit.getBool-  if isOpen-    then do-      isNew <- BinaryBit.getBool-      if isNew-        then do-          (x, newActorMap) <--            getSpawnedReplication classAttributeMap actorMap actorId-          pure (SpawnedReplicationValue x, newActorMap)-        else do-          x <- getUpdatedReplication version classAttributeMap actorMap actorId-          pure (UpdatedReplicationValue x, actorMap)-    else do-      x <- getDestroyedReplication-      pure (DestroyedReplicationValue x, actorMap)--putReplicationValue :: ReplicationValue -> BinaryBit.BitPut ()-putReplicationValue value =-  case value of-    SpawnedReplicationValue x -> do-      BinaryBit.putBool True-      BinaryBit.putBool True-      putSpawnedReplication x-    UpdatedReplicationValue x -> do-      BinaryBit.putBool True-      BinaryBit.putBool False-      putUpdatedReplication x-    DestroyedReplicationValue x -> do-      BinaryBit.putBool False-      putDestroyedReplication x
− library/Rattletrap/Utility.hs
@@ -1,39 +0,0 @@-module Rattletrap.Utility where--import qualified Data.Binary.Bits.Get as BinaryBit-import qualified Data.Bits as Bits-import qualified Data.ByteString.Lazy as ByteString-import qualified Data.Word as Word--getRemainingBits :: BinaryBit.BitGet [Bool]-getRemainingBits = do-  isEmpty <- BinaryBit.isEmpty-  if isEmpty-    then pure []-    else do-      bit <- BinaryBit.getBool-      bits <- getRemainingBits-      pure (bit : bits)--padBytes-  :: Integral a-  => a -> ByteString.ByteString -> ByteString.ByteString-padBytes size bytes =-  ByteString.concat-    [ bytes-    , ByteString.replicate (fromIntegral size - ByteString.length bytes) 0x00-    ]--reverseByte :: Word.Word8 -> Word.Word8-reverseByte byte =-  Bits.shiftR (byte Bits..&. Bits.bit 7) 7 +-  Bits.shiftR (byte Bits..&. Bits.bit 6) 5 +-  Bits.shiftR (byte Bits..&. Bits.bit 5) 3 +-  Bits.shiftR (byte Bits..&. Bits.bit 4) 1 +-  Bits.shiftL (byte Bits..&. Bits.bit 3) 1 +-  Bits.shiftL (byte Bits..&. Bits.bit 2) 3 +-  Bits.shiftL (byte Bits..&. Bits.bit 1) 5 +-  Bits.shiftL (byte Bits..&. Bits.bit 0) 7--reverseBytes :: ByteString.ByteString -> ByteString.ByteString-reverseBytes bytes = ByteString.map reverseByte bytes
− library/Rattletrap/Version.hs
@@ -1,7 +0,0 @@-module Rattletrap.Version where--import qualified Data.Version as Version-import qualified Paths_rattletrap as This--version :: Version.Version-version = This.version
rattletrap.cabal view
@@ -3,195 +3,196 @@ -- see: https://github.com/sol/hpack  name:           rattletrap-version:        1.0.0+version:        2.0.0+cabal-version:  >= 1.10+build-type:     Simple+license:        MIT+license-file:   LICENSE.markdown+maintainer:     Taylor Fausak+homepage:       https://github.com/tfausak/rattletrap#readme+bug-reports:    https://github.com/tfausak/rattletrap/issues synopsis:       Parse and generate Rocket League replays. description:    Rattletrap parses and generates Rocket League replays. category:       Game-homepage:       https://github.com/tfausak/rattletrap#readme-bug-reports:    https://github.com/tfausak/rattletrap/issues-maintainer:     Taylor Fausak-license:        MIT-license-file:   LICENSE.markdown-build-type:     Simple-cabal-version:  >= 1.10  extra-source-files:-    HLint.hs     README.markdown-    test/replays/00080014003600090000036E0F65CCEB.replay-    test/replays/07E925B1423653D44CB8B4B2524792C1.replay-    test/replays/0AD21FEE4A512906BEB6C98136AAF49A.replay-    test/replays/1205D96C4D819800927791820096CD49.replay-    test/replays/160CA83E41083BFD8E6315B4BFCA0561.replay-    test/replays/18D6738D415B70B5BE4C299588D3C141.replay-    test/replays/1A126AC24CAA0DB0E98835BD960B8AF8.replay-    test/replays/1BC2D01444ACE577D01E988EADD4DFD0.replay-    test/replays/1D1DE97D4941C86E43FE0093563DB621.replay-    test/replays/1EF90FCC4F719F606A5327B3CDD782A4.replay-    test/replays/1F3798E540B0C37A946561ABBB3037F9.replay-    test/replays/211466D04B983F5A33CC2FA1D5928672.replay-    test/replays/22BACD794ABE7B92E50E9CBDBD9C59CE.replay-    test/replays/27B6A7B64553F0F685874584F96BAB1B.replay-    test/replays/29F582C34A65EB34D358A784CBE3C189.replay-    test/replays/372DBFCA4BDB340E4357B6BD43032802.replay-    test/replays/387F059C47C09E253C875CA990EFD9F2.replay-    test/replays/3EA147DD485B8DD24810689A7A989E44.replay-    test/replays/4126861E477F4A03DE2A4080374D7908.replay-    test/replays/42F0D8DA4FC89AE7B80FCAB7F637A8EA.replay-    test/replays/504ED825482186E771FAA9B642CE5CE4.replay-    test/replays/52AA67F94090C19D33C5009E54D31FE4.replay-    test/replays/540DA764423C8FB24EB9D486D982F16F.replay-    test/replays/551CA4D44FF2B86015DE44A6B5790D4C.replay-    test/replays/6688EEE34BFEB3EC3A9E3283098CC712.replay-    test/replays/6D1B06D844A5BB91B81FD4B5B28F08BA.replay-    test/replays/6F7CFCD24638F8A6567AB3A8B9958A90.replay-    test/replays/7109EB9846D303E54B7ACBA792036213.replay-    test/replays/7BF6073F4614CE0A438994B9A260DA6A.replay-    test/replays/89CBA30E46FA5385BDD35DA4285D4D2E.replay-    test/replays/9704208245D7DD851F2FB2BC7DFD9AC3.replay-    test/replays/98E58A904D713F2DE202358E8573265D.replay-    test/replays/A128B3AB45D5A18E3EF9CF93C9576BCE.replay-    test/replays/A52F804845573D8DA65E97BF59026A43.replay-    test/replays/A6711CE74272B2E663DCC9A200A218E3.replay-    test/replays/A7F001A1417A19BFA8C90990D8F7C2FF.replay-    test/replays/B82DDB624C393A4A425E68AB40DC2450.replay-    test/replays/B9F9B87D4A9D0A3D25D4EC91C0401DE2.replay-    test/replays/C14F7E0E4D9B5E6BE9AD5D8ED56B174C.replay-    test/replays/C8372B1345B1803DEF039F815DBD802D.replay-    test/replays/D428F81646A98C25902CE988AE5C14C8.replay-    test/replays/D7FB197A451D69075A0C99A2F49A4053.replay-    test/replays/DCB3A6B94A9DBE46FDE5EAA9B012F6C8.replay-    test/replays/EAE311E84BA35B590A6FDBA6DD4F2FEB.replay-    test/replays/EAE8DADA4BB2DC5422792C9B4A67392D.replay-    test/replays/F299F176491554B11E34AB91CA76B2CE.replay-    test/replays/F7B9E14545C7467B89A00895980FCD73.replay-    test/replays/F811C1D24888015E23B598AD8628C742.replay-    test/replays/FDC79DA84DD463D4BCCE6B892829AC88.replay+    replays/00080014003600090000036E0F65CCEB.replay+    replays/07E925B1423653D44CB8B4B2524792C1.replay+    replays/0AD21FEE4A512906BEB6C98136AAF49A.replay+    replays/1205D96C4D819800927791820096CD49.replay+    replays/160CA83E41083BFD8E6315B4BFCA0561.replay+    replays/18D6738D415B70B5BE4C299588D3C141.replay+    replays/1A126AC24CAA0DB0E98835BD960B8AF8.replay+    replays/1AE415514DFC65DCBF8B8391AD35488D.replay+    replays/1BC2D01444ACE577D01E988EADD4DFD0.replay+    replays/1D1DE97D4941C86E43FE0093563DB621.replay+    replays/1EF90FCC4F719F606A5327B3CDD782A4.replay+    replays/1F3798E540B0C37A946561ABBB3037F9.replay+    replays/211466D04B983F5A33CC2FA1D5928672.replay+    replays/22BACD794ABE7B92E50E9CBDBD9C59CE.replay+    replays/27B6A7B64553F0F685874584F96BAB1B.replay+    replays/29F582C34A65EB34D358A784CBE3C189.replay+    replays/372DBFCA4BDB340E4357B6BD43032802.replay+    replays/387F059C47C09E253C875CA990EFD9F2.replay+    replays/3EA147DD485B8DD24810689A7A989E44.replay+    replays/4126861E477F4A03DE2A4080374D7908.replay+    replays/42F0D8DA4FC89AE7B80FCAB7F637A8EA.replay+    replays/504ED825482186E771FAA9B642CE5CE4.replay+    replays/52AA67F94090C19D33C5009E54D31FE4.replay+    replays/540DA764423C8FB24EB9D486D982F16F.replay+    replays/551CA4D44FF2B86015DE44A6B5790D4C.replay+    replays/6320E51C49066A7C210A2993C2201D5F.replay+    replays/6688EEE34BFEB3EC3A9E3283098CC712.replay+    replays/6D1B06D844A5BB91B81FD4B5B28F08BA.replay+    replays/6F7CFCD24638F8A6567AB3A8B9958A90.replay+    replays/7109EB9846D303E54B7ACBA792036213.replay+    replays/7BF6073F4614CE0A438994B9A260DA6A.replay+    replays/89CBA30E46FA5385BDD35DA4285D4D2E.replay+    replays/9704208245D7DD851F2FB2BC7DFD9AC3.replay+    replays/98E58A904D713F2DE202358E8573265D.replay+    replays/A128B3AB45D5A18E3EF9CF93C9576BCE.replay+    replays/A52F804845573D8DA65E97BF59026A43.replay+    replays/A6711CE74272B2E663DCC9A200A218E3.replay+    replays/A7F001A1417A19BFA8C90990D8F7C2FF.replay+    replays/B9F9B87D4A9D0A3D25D4EC91C0401DE2.replay+    replays/C14F7E0E4D9B5E6BE9AD5D8ED56B174C.replay+    replays/C8372B1345B1803DEF039F815DBD802D.replay+    replays/CC4CA70D4F7A67EBAD0ED9B9923106F7.replay+    replays/D428F81646A98C25902CE988AE5C14C8.replay+    replays/D7FB197A451D69075A0C99A2F49A4053.replay+    replays/DCB3A6B94A9DBE46FDE5EAA9B012F6C8.replay+    replays/EAE311E84BA35B590A6FDBA6DD4F2FEB.replay+    replays/EAE8DADA4BB2DC5422792C9B4A67392D.replay+    replays/F299F176491554B11E34AB91CA76B2CE.replay+    replays/F7B9E14545C7467B89A00895980FCD73.replay+    replays/F811C1D24888015E23B598AD8628C742.replay+    replays/FDC79DA84DD463D4BCCE6B892829AC88.replay  source-repository head-  type: git-  location: https://github.com/tfausak/rattletrap+    type: git+    location: https://github.com/tfausak/rattletrap  library-  hs-source-dirs:-      library-  default-extensions: Strict-  ghc-options: -Wall-  build-depends:-      aeson >=0.11 && <1.1-    , aeson-casing >=0.1 && <0.2-    , base >=4.9 && <4.10-    , bimap >=0.3 && <0.4-    , binary >=0.8 && <0.9-    , binary-bits >=0.5 && <0.6-    , bytestring >=0.10 && <0.11-    , containers >=0.5 && <0.6-    , data-binary-ieee754 >=0.4 && <0.5-    , template-haskell >=2.11 && <2.12-    , text >=1.2 && <1.3-    , vector >=0.11 && <0.12-  exposed-modules:-      Rattletrap-      Rattletrap.ActorMap-      Rattletrap.Attribute-      Rattletrap.Attribute.Boolean-      Rattletrap.Attribute.Byte-      Rattletrap.Attribute.CamSettings-      Rattletrap.Attribute.ClubColors-      Rattletrap.Attribute.Demolish-      Rattletrap.Attribute.Enum-      Rattletrap.Attribute.Explosion-      Rattletrap.Attribute.FlaggedInt-      Rattletrap.Attribute.Float-      Rattletrap.Attribute.GameMode-      Rattletrap.Attribute.Int-      Rattletrap.Attribute.Loadout-      Rattletrap.Attribute.LoadoutOnline-      Rattletrap.Attribute.Loadouts-      Rattletrap.Attribute.LoadoutsOnline-      Rattletrap.Attribute.Location-      Rattletrap.Attribute.MusicStinger-      Rattletrap.Attribute.PartyLeader-      Rattletrap.Attribute.Pickup-      Rattletrap.Attribute.PrivateMatchSettings-      Rattletrap.Attribute.QWord-      Rattletrap.Attribute.Reservation-      Rattletrap.Attribute.RigidBodyState-      Rattletrap.Attribute.String-      Rattletrap.Attribute.TeamPaint-      Rattletrap.Attribute.UniqueId-      Rattletrap.Attribute.WeldedInfo-      Rattletrap.AttributeMapping-      Rattletrap.AttributeType-      Rattletrap.AttributeValue-      Rattletrap.Cache-      Rattletrap.ClassAttributeMap-      Rattletrap.ClassMapping-      Rattletrap.Content-      Rattletrap.Crc-      Rattletrap.Data-      Rattletrap.Frame-      Rattletrap.Header-      Rattletrap.Initialization-      Rattletrap.Json-      Rattletrap.KeyFrame-      Rattletrap.Main-      Rattletrap.Mark-      Rattletrap.Message-      Rattletrap.Primitive-      Rattletrap.Primitive.CompressedWord-      Rattletrap.Primitive.CompressedWordVector-      Rattletrap.Primitive.Dictionary-      Rattletrap.Primitive.Float32-      Rattletrap.Primitive.Int32-      Rattletrap.Primitive.Int8-      Rattletrap.Primitive.Int8Vector-      Rattletrap.Primitive.List-      Rattletrap.Primitive.Section-      Rattletrap.Primitive.Text-      Rattletrap.Primitive.Vector-      Rattletrap.Primitive.Word32-      Rattletrap.Primitive.Word64-      Rattletrap.Primitive.Word8-      Rattletrap.Property-      Rattletrap.PropertyValue-      Rattletrap.RemoteId-      Rattletrap.Replay-      Rattletrap.Replication-      Rattletrap.Replication.Destroyed-      Rattletrap.Replication.Spawned-      Rattletrap.Replication.Updated-      Rattletrap.ReplicationValue-      Rattletrap.Utility-      Rattletrap.Version-  other-modules:-      Paths_rattletrap-  default-language: Haskell2010+    exposed-modules:+        Rattletrap+        Rattletrap.ActorMap+        Rattletrap.Attribute+        Rattletrap.Attribute.Boolean+        Rattletrap.Attribute.Byte+        Rattletrap.Attribute.CamSettings+        Rattletrap.Attribute.ClubColors+        Rattletrap.Attribute.Demolish+        Rattletrap.Attribute.Enum+        Rattletrap.Attribute.Explosion+        Rattletrap.Attribute.FlaggedInt+        Rattletrap.Attribute.Float+        Rattletrap.Attribute.GameMode+        Rattletrap.Attribute.Int+        Rattletrap.Attribute.Loadout+        Rattletrap.Attribute.LoadoutOnline+        Rattletrap.Attribute.Loadouts+        Rattletrap.Attribute.LoadoutsOnline+        Rattletrap.Attribute.Location+        Rattletrap.Attribute.MusicStinger+        Rattletrap.Attribute.PartyLeader+        Rattletrap.Attribute.Pickup+        Rattletrap.Attribute.PrivateMatchSettings+        Rattletrap.Attribute.QWord+        Rattletrap.Attribute.Reservation+        Rattletrap.Attribute.RigidBodyState+        Rattletrap.Attribute.String+        Rattletrap.Attribute.TeamPaint+        Rattletrap.Attribute.UniqueId+        Rattletrap.Attribute.WeldedInfo+        Rattletrap.AttributeMapping+        Rattletrap.AttributeType+        Rattletrap.AttributeValue+        Rattletrap.Cache+        Rattletrap.ClassAttributeMap+        Rattletrap.ClassMapping+        Rattletrap.Content+        Rattletrap.Crc+        Rattletrap.Data+        Rattletrap.Frame+        Rattletrap.Header+        Rattletrap.Initialization+        Rattletrap.Json+        Rattletrap.KeyFrame+        Rattletrap.Main+        Rattletrap.Mark+        Rattletrap.Message+        Rattletrap.Primitive+        Rattletrap.Primitive.CompressedWord+        Rattletrap.Primitive.CompressedWordVector+        Rattletrap.Primitive.Dictionary+        Rattletrap.Primitive.Float32+        Rattletrap.Primitive.Int32+        Rattletrap.Primitive.Int8+        Rattletrap.Primitive.Int8Vector+        Rattletrap.Primitive.List+        Rattletrap.Primitive.Section+        Rattletrap.Primitive.Text+        Rattletrap.Primitive.Vector+        Rattletrap.Primitive.Word32+        Rattletrap.Primitive.Word64+        Rattletrap.Primitive.Word8+        Rattletrap.Property+        Rattletrap.PropertyValue+        Rattletrap.RemoteId+        Rattletrap.Replay+        Rattletrap.Replication+        Rattletrap.Replication.Destroyed+        Rattletrap.Replication.Spawned+        Rattletrap.Replication.Updated+        Rattletrap.ReplicationValue+        Rattletrap.Utility+        Rattletrap.Version+    build-depends:+        aeson >=0.11 && <1.1,+        aeson-casing >=0.1 && <0.2,+        base >=4.9 && <4.10,+        bimap >=0.3 && <0.4,+        binary >=0.8 && <0.9,+        binary-bits >=0.5 && <0.6,+        bytestring >=0.10 && <0.11,+        containers >=0.5 && <0.6,+        data-binary-ieee754 >=0.4 && <0.5,+        template-haskell >=2.11 && <2.12,+        text >=1.2 && <1.3,+        vector >=0.11 && <0.12+    default-language: Haskell2010+    default-extensions: Strict+    hs-source-dirs:+        source/library+    other-modules:+        Paths_rattletrap+    ghc-options: -Wall  executable rattletrap-  main-is: Main.hs-  hs-source-dirs:-      executable-  default-extensions: Strict-  ghc-options: -Wall -rtsopts -threaded -with-rtsopts=-N-  build-depends:-      base-    , rattletrap-  default-language: Haskell2010+    main-is: Main.hs+    build-depends:+        base,+        rattletrap+    default-language: Haskell2010+    default-extensions: Strict+    hs-source-dirs:+        source/executables+    ghc-options: -Wall -rtsopts -threaded -with-rtsopts=-N  test-suite test-  type: exitcode-stdio-1.0-  main-is: Test.hs-  hs-source-dirs:-      test-  default-extensions: Strict-  ghc-options: -Wall -rtsopts -threaded -with-rtsopts=-N-  build-depends:-      base-    , bytestring-    , filepath >=1.4 && <1.5-    , rattletrap-    , tasty >=0.11 && <0.12-    , tasty-hspec >=1.1 && <1.2-    , temporary >=1.2 && <1.3-  default-language: Haskell2010+    type: exitcode-stdio-1.0+    main-is: Main.hs+    build-depends:+        base,+        bytestring,+        filepath >=1.4 && <1.5,+        rattletrap,+        tasty >=0.11 && <0.12,+        tasty-hspec >=1.1 && <1.2,+        temporary >=1.2 && <1.3+    default-language: Haskell2010+    default-extensions: Strict+    hs-source-dirs:+        source/tests+    ghc-options: -Wall -rtsopts -threaded -with-rtsopts=-N
+ replays/00080014003600090000036E0F65CCEB.replay view

binary file changed (absent → 94347 bytes)

+ replays/07E925B1423653D44CB8B4B2524792C1.replay view

binary file changed (absent → 30063 bytes)

+ replays/0AD21FEE4A512906BEB6C98136AAF49A.replay view

binary file changed (absent → 27949 bytes)

+ replays/1205D96C4D819800927791820096CD49.replay view

binary file changed (absent → 461470 bytes)

+ replays/160CA83E41083BFD8E6315B4BFCA0561.replay view

binary file changed (absent → 565359 bytes)

+ replays/18D6738D415B70B5BE4C299588D3C141.replay view

binary file changed (absent → 21996 bytes)

+ replays/1A126AC24CAA0DB0E98835BD960B8AF8.replay view

binary file changed (absent → 98814 bytes)

+ replays/1AE415514DFC65DCBF8B8391AD35488D.replay view

binary file changed (absent → 25926 bytes)

+ replays/1BC2D01444ACE577D01E988EADD4DFD0.replay view

binary file changed (absent → 22739 bytes)

+ replays/1D1DE97D4941C86E43FE0093563DB621.replay view

binary file changed (absent → 59943 bytes)

+ replays/1EF90FCC4F719F606A5327B3CDD782A4.replay view

binary file changed (absent → 737542 bytes)

+ replays/1F3798E540B0C37A946561ABBB3037F9.replay view

binary file changed (absent → 27316 bytes)

+ replays/211466D04B983F5A33CC2FA1D5928672.replay view

binary file changed (absent → 38258 bytes)

+ replays/22BACD794ABE7B92E50E9CBDBD9C59CE.replay view

binary file changed (absent → 315922 bytes)

+ replays/27B6A7B64553F0F685874584F96BAB1B.replay view

binary file changed (absent → 25332 bytes)

+ replays/29F582C34A65EB34D358A784CBE3C189.replay view

binary file changed (absent → 21607 bytes)

+ replays/372DBFCA4BDB340E4357B6BD43032802.replay view

binary file changed (absent → 28065 bytes)

+ replays/387F059C47C09E253C875CA990EFD9F2.replay view

binary file changed (absent → 737279 bytes)

+ replays/3EA147DD485B8DD24810689A7A989E44.replay view

binary file changed (absent → 596690 bytes)

+ replays/4126861E477F4A03DE2A4080374D7908.replay view

binary file changed (absent → 26284 bytes)

+ replays/42F0D8DA4FC89AE7B80FCAB7F637A8EA.replay view

binary file changed (absent → 116440 bytes)

+ replays/504ED825482186E771FAA9B642CE5CE4.replay view

binary file changed (absent → 145094 bytes)

+ replays/52AA67F94090C19D33C5009E54D31FE4.replay view

binary file changed (absent → 570657 bytes)

+ replays/540DA764423C8FB24EB9D486D982F16F.replay view

binary file changed (absent → 34674 bytes)

+ replays/551CA4D44FF2B86015DE44A6B5790D4C.replay view

binary file changed (absent → 754887 bytes)

+ replays/6320E51C49066A7C210A2993C2201D5F.replay view

binary file changed (absent → 391424 bytes)

+ replays/6688EEE34BFEB3EC3A9E3283098CC712.replay view

binary file changed (absent → 1026 bytes)

+ replays/6D1B06D844A5BB91B81FD4B5B28F08BA.replay view

binary file changed (absent → 91217 bytes)

+ replays/6F7CFCD24638F8A6567AB3A8B9958A90.replay view

binary file changed (absent → 29715 bytes)

+ replays/7109EB9846D303E54B7ACBA792036213.replay view

binary file changed (absent → 65002 bytes)

+ replays/7BF6073F4614CE0A438994B9A260DA6A.replay view

binary file changed (absent → 22841 bytes)

+ replays/89CBA30E46FA5385BDD35DA4285D4D2E.replay view

binary file changed (absent → 212429 bytes)

+ replays/9704208245D7DD851F2FB2BC7DFD9AC3.replay view

binary file changed (absent → 36307 bytes)

+ replays/98E58A904D713F2DE202358E8573265D.replay view

binary file changed (absent → 34047 bytes)

+ replays/A128B3AB45D5A18E3EF9CF93C9576BCE.replay view

binary file changed (absent → 743537 bytes)

+ replays/A52F804845573D8DA65E97BF59026A43.replay view

binary file changed (absent → 73064 bytes)

+ replays/A6711CE74272B2E663DCC9A200A218E3.replay view

binary file changed (absent → 221897 bytes)

+ replays/A7F001A1417A19BFA8C90990D8F7C2FF.replay view

binary file changed (absent → 396148 bytes)

+ replays/B9F9B87D4A9D0A3D25D4EC91C0401DE2.replay view

binary file changed (absent → 25888 bytes)

+ replays/C14F7E0E4D9B5E6BE9AD5D8ED56B174C.replay view

binary file changed (absent → 25904 bytes)

+ replays/C8372B1345B1803DEF039F815DBD802D.replay view

binary file changed (absent → 23625 bytes)

+ replays/CC4CA70D4F7A67EBAD0ED9B9923106F7.replay view

binary file changed (absent → 30103 bytes)

+ replays/D428F81646A98C25902CE988AE5C14C8.replay view

binary file changed (absent → 476589 bytes)

+ replays/D7FB197A451D69075A0C99A2F49A4053.replay view

binary file changed (absent → 29095 bytes)

+ replays/DCB3A6B94A9DBE46FDE5EAA9B012F6C8.replay view

binary file changed (absent → 1055071 bytes)

+ replays/EAE311E84BA35B590A6FDBA6DD4F2FEB.replay view

binary file changed (absent → 30151 bytes)

+ replays/EAE8DADA4BB2DC5422792C9B4A67392D.replay view

binary file changed (absent → 25462 bytes)

+ replays/F299F176491554B11E34AB91CA76B2CE.replay view

binary file changed (absent → 22732 bytes)

+ replays/F7B9E14545C7467B89A00895980FCD73.replay view

binary file changed (absent → 25580 bytes)

+ replays/F811C1D24888015E23B598AD8628C742.replay view

binary file changed (absent → 605 bytes)

+ replays/FDC79DA84DD463D4BCCE6B892829AC88.replay view

binary file changed (absent → 783619 bytes)

+ source/executables/Main.hs view
@@ -0,0 +1,5 @@+module Main+  ( module Rattletrap+  ) where++import Rattletrap (main)
+ source/library/Rattletrap.hs view
@@ -0,0 +1,85 @@+-- | Rattletrap is a tool for parsing and generating Rocket League replays. It+-- is typically used as an executable; see "Rattletrap.Main" for details about+-- that interface. Using Rattletrap from Haskell is as easy as importing this+-- module. Together with "Data.ByteString.Lazy" and "Data.Binary.Get", you can+-- decode a 'Replay' value. Then you can transform it into JSON with+-- "Data.Aeson". For example:+--+-- @+-- let file = "path-to/some.replay"+-- bytes <- 'Data.ByteString.Lazy.readFile' file+-- let replay = 'Data.Binary.Get.runGet' 'Rattletrap.Replay.getReplay' bytes+-- let json = 'Data.Aeson.encode' replay+-- 'Data.ByteString.Lazy.putStr' json+-- @+--+-- There are a few unusual aspects of Rattletrap to be aware of:+--+-- - In order to improve memory usage, everything Rattletrap does is strict.+--   Don't expect any lazy values or evaluation.+-- - Some names, like 'Text', collide with commonly-used modules. Use+--   qualified imports to avoid collisions.+-- - No instances of 'Data.Binary.Binary' are provided. Use functions like+--   'getReplay' instead. This was done to improve type inference and make+--   profiling easier. Also some types require information that could not be+--   provided by the 'Data.Binary.Binary' interface.+-- - Importing one specific module is not recommended because all JSON+--   instances are defined as orphans in "Rattletrap.Json".+module Rattletrap+  ( module Rattletrap.ActorMap+  , module Rattletrap.Attribute+  , module Rattletrap.AttributeMapping+  , module Rattletrap.AttributeType+  , module Rattletrap.AttributeValue+  , module Rattletrap.Cache+  , module Rattletrap.ClassAttributeMap+  , module Rattletrap.ClassMapping+  , module Rattletrap.Content+  , module Rattletrap.Crc+  , module Rattletrap.Data+  , module Rattletrap.Frame+  , module Rattletrap.Header+  , module Rattletrap.Initialization+  , module Rattletrap.KeyFrame+  , module Rattletrap.Main+  , module Rattletrap.Mark+  , module Rattletrap.Message+  , module Rattletrap.Primitive+  , module Rattletrap.Property+  , module Rattletrap.PropertyValue+  , module Rattletrap.RemoteId+  , module Rattletrap.Replay+  , module Rattletrap.Replication+  , module Rattletrap.ReplicationValue+  , module Rattletrap.Utility+  , module Rattletrap.Version+  ) where++import Rattletrap.ActorMap+import Rattletrap.Attribute+import Rattletrap.AttributeMapping+import Rattletrap.AttributeType+import Rattletrap.AttributeValue+import Rattletrap.Cache+import Rattletrap.ClassAttributeMap+import Rattletrap.ClassMapping+import Rattletrap.Content+import Rattletrap.Crc+import Rattletrap.Data+import Rattletrap.Frame+import Rattletrap.Header+import Rattletrap.Initialization+import Rattletrap.Json ()+import Rattletrap.KeyFrame+import Rattletrap.Main+import Rattletrap.Mark+import Rattletrap.Message+import Rattletrap.Primitive+import Rattletrap.Property+import Rattletrap.PropertyValue+import Rattletrap.RemoteId+import Rattletrap.Replay+import Rattletrap.Replication+import Rattletrap.ReplicationValue+import Rattletrap.Utility+import Rattletrap.Version
+ source/library/Rattletrap/ActorMap.hs view
@@ -0,0 +1,13 @@+module Rattletrap.ActorMap where++import Rattletrap.Primitive++import qualified Data.Map.Strict as Map++type ActorMap = Map.Map CompressedWord Word32++makeActorMap :: ActorMap+makeActorMap = Map.empty++updateActorMap :: CompressedWord -> Word32 -> ActorMap -> ActorMap+updateActorMap actorId objectId actorMap = Map.insert actorId objectId actorMap
+ source/library/Rattletrap/Attribute.hs view
@@ -0,0 +1,64 @@+module Rattletrap.Attribute where++import Rattletrap.ActorMap+import Rattletrap.AttributeValue+import Rattletrap.ClassAttributeMap+import Rattletrap.Primitive++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++data Attribute = Attribute+  { attributeId :: CompressedWord+  , attributeName :: Text+  -- ^ Read-only! Changing an attribute's name requires editing the class+  -- attribute map.+  , attributeValue :: AttributeValue+  } deriving (Eq, Ord, Show)++getAttributes+  :: (Int, Int)+  -> ClassAttributeMap+  -> ActorMap+  -> CompressedWord+  -> BinaryBit.BitGet [Attribute]+getAttributes version classAttributeMap actorMap actorId = do+  hasAttribute <- BinaryBit.getBool+  if not hasAttribute+    then pure []+    else do+      attribute <- getAttribute version classAttributeMap actorMap actorId+      attributes <- getAttributes version classAttributeMap actorMap actorId+      pure (attribute : attributes)++putAttributes :: [Attribute] -> BinaryBit.BitPut ()+putAttributes attributes = do+  mapM_ putAttribute attributes+  BinaryBit.putBool False++getAttribute+  :: (Int, Int)+  -> ClassAttributeMap+  -> ActorMap+  -> CompressedWord+  -> BinaryBit.BitGet Attribute+getAttribute version classAttributeMap actorMap actorId =+  case getAttributeMap classAttributeMap actorMap actorId of+    Nothing -> fail ("could not get attribute map for " ++ show actorId)+    Just attributeMap ->+      case getAttributeIdLimit attributeMap of+        Nothing ->+          fail ("could not get attribute ID limit for " ++ show actorId)+        Just limit -> do+          id_ <- getCompressedWord limit+          case getAttributeName classAttributeMap attributeMap id_ of+            Nothing -> fail ("could not get attribute name for " ++ show id_)+            Just name -> do+              value <- getAttributeValue version name+              pure (Attribute id_ name value)++putAttribute :: Attribute -> BinaryBit.BitPut ()+putAttribute attribute = do+  BinaryBit.putBool True+  putCompressedWord (attributeId attribute)+  putAttributeValue (attributeValue attribute)
+ source/library/Rattletrap/Attribute/Boolean.hs view
@@ -0,0 +1,17 @@+module Rattletrap.Attribute.Boolean where++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++newtype BooleanAttribute = BooleanAttribute+  { booleanAttributeValue :: Bool+  } deriving (Eq, Ord, Show)++getBooleanAttribute :: BinaryBit.BitGet BooleanAttribute+getBooleanAttribute = do+  value <- BinaryBit.getBool+  pure (BooleanAttribute value)++putBooleanAttribute :: BooleanAttribute -> BinaryBit.BitPut ()+putBooleanAttribute booleanAttribute =+  BinaryBit.putBool (booleanAttributeValue booleanAttribute)
+ source/library/Rattletrap/Attribute/Byte.hs view
@@ -0,0 +1,19 @@+module Rattletrap.Attribute.Byte where++import Rattletrap.Primitive++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++newtype ByteAttribute = ByteAttribute+  { byteAttributeValue :: Word8+  } deriving (Eq, Ord, Show)++getByteAttribute :: BinaryBit.BitGet ByteAttribute+getByteAttribute = do+  value <- getWord8Bits+  pure (ByteAttribute value)++putByteAttribute :: ByteAttribute -> BinaryBit.BitPut ()+putByteAttribute byteAttribute =+  putWord8Bits (byteAttributeValue byteAttribute)
+ source/library/Rattletrap/Attribute/CamSettings.hs view
@@ -0,0 +1,34 @@+module Rattletrap.Attribute.CamSettings where++import Rattletrap.Primitive++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++data CamSettingsAttribute = CamSettingsAttribute+  { camSettingsAttributeFov :: Float32+  , camSettingsAttributeHeight :: Float32+  , camSettingsAttributeAngle :: Float32+  , camSettingsAttributeDistance :: Float32+  , camSettingsAttributeStiffness :: Float32+  , camSettingsAttributeSwivelSpeed :: Float32+  } deriving (Eq, Ord, Show)++getCamSettingsAttribute :: BinaryBit.BitGet CamSettingsAttribute+getCamSettingsAttribute = do+  fov <- getFloat32Bits+  height <- getFloat32Bits+  angle <- getFloat32Bits+  distance <- getFloat32Bits+  stiffness <- getFloat32Bits+  swivelSpeed <- getFloat32Bits+  pure (CamSettingsAttribute fov height angle distance stiffness swivelSpeed)++putCamSettingsAttribute :: CamSettingsAttribute -> BinaryBit.BitPut ()+putCamSettingsAttribute camSettingsAttribute = do+  putFloat32Bits (camSettingsAttributeFov camSettingsAttribute)+  putFloat32Bits (camSettingsAttributeHeight camSettingsAttribute)+  putFloat32Bits (camSettingsAttributeAngle camSettingsAttribute)+  putFloat32Bits (camSettingsAttributeDistance camSettingsAttribute)+  putFloat32Bits (camSettingsAttributeStiffness camSettingsAttribute)+  putFloat32Bits (camSettingsAttributeSwivelSpeed camSettingsAttribute)
+ source/library/Rattletrap/Attribute/ClubColors.hs view
@@ -0,0 +1,28 @@+module Rattletrap.Attribute.ClubColors where++import Rattletrap.Primitive++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++data ClubColorsAttribute = ClubColorsAttribute+  { clubColorsAttributeBlueFlag :: Bool+  , clubColorsAttributeBlueColor :: Word8+  , clubColorsAttributeOrangeFlag :: Bool+  , clubColorsAttributeOrangeColor :: Word8+  } deriving (Eq, Ord, Show)++getClubColorsAttribute :: BinaryBit.BitGet ClubColorsAttribute+getClubColorsAttribute = do+  blueFlag <- BinaryBit.getBool+  blueColor <- getWord8Bits+  orangeFlag <- BinaryBit.getBool+  orangeColor <- getWord8Bits+  pure (ClubColorsAttribute blueFlag blueColor orangeFlag orangeColor)++putClubColorsAttribute :: ClubColorsAttribute -> BinaryBit.BitPut ()+putClubColorsAttribute clubColorsAttribute = do+  BinaryBit.putBool (clubColorsAttributeBlueFlag clubColorsAttribute)+  putWord8Bits (clubColorsAttributeBlueColor clubColorsAttribute)+  BinaryBit.putBool (clubColorsAttributeOrangeFlag clubColorsAttribute)+  putWord8Bits (clubColorsAttributeOrangeColor clubColorsAttribute)
+ source/library/Rattletrap/Attribute/Demolish.hs view
@@ -0,0 +1,41 @@+module Rattletrap.Attribute.Demolish where++import Rattletrap.Primitive++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++data DemolishAttribute = DemolishAttribute+  { demolishAttributeAttackerFlag :: Bool+  , demolishAttributeAttackerActorId :: Word32+  , demolishAttributeVictimFlag :: Bool+  , demolishAttributeVictimActorId :: Word32+  , demolishAttributeAttackerVelocity :: Vector+  , demolishAttributeVictimVelocity :: Vector+  } deriving (Eq, Ord, Show)++getDemolishAttribute :: BinaryBit.BitGet DemolishAttribute+getDemolishAttribute = do+  attackerFlag <- BinaryBit.getBool+  attackerActorId <- getWord32Bits+  victimFlag <- BinaryBit.getBool+  victimActorId <- getWord32Bits+  attackerVelocity <- getVector+  victimVelocity <- getVector+  pure+    (DemolishAttribute+       attackerFlag+       attackerActorId+       victimFlag+       victimActorId+       attackerVelocity+       victimVelocity)++putDemolishAttribute :: DemolishAttribute -> BinaryBit.BitPut ()+putDemolishAttribute demolishAttribute = do+  BinaryBit.putBool (demolishAttributeAttackerFlag demolishAttribute)+  putWord32Bits (demolishAttributeAttackerActorId demolishAttribute)+  BinaryBit.putBool (demolishAttributeVictimFlag demolishAttribute)+  putWord32Bits (demolishAttributeVictimActorId demolishAttribute)+  putVector (demolishAttributeAttackerVelocity demolishAttribute)+  putVector (demolishAttributeVictimVelocity demolishAttribute)
+ source/library/Rattletrap/Attribute/Enum.hs view
@@ -0,0 +1,18 @@+module Rattletrap.Attribute.Enum where++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit+import qualified Data.Word as Word++newtype EnumAttribute = EnumAttribute+  { enumAttributeValue :: Word.Word16+  } deriving (Eq, Ord, Show)++getEnumAttribute :: BinaryBit.BitGet EnumAttribute+getEnumAttribute = do+  value <- BinaryBit.getWord16be 11+  pure (EnumAttribute value)++putEnumAttribute :: EnumAttribute -> BinaryBit.BitPut ()+putEnumAttribute enumAttribute =+  BinaryBit.putWord16be 11 (enumAttributeValue enumAttribute)
+ source/library/Rattletrap/Attribute/Explosion.hs view
@@ -0,0 +1,24 @@+module Rattletrap.Attribute.Explosion where++import Rattletrap.Primitive++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++data ExplosionAttribute = ExplosionAttribute+  { explosionAttributeActorId :: Int32+  , explosionAttributeLocation :: Vector+  } deriving (Eq, Ord, Show)++getExplosionAttribute :: BinaryBit.BitGet ExplosionAttribute+getExplosionAttribute = do+  False <- BinaryBit.getBool+  actorId <- getInt32Bits+  location <- getVector+  pure (ExplosionAttribute actorId location)++putExplosionAttribute :: ExplosionAttribute -> BinaryBit.BitPut ()+putExplosionAttribute explosionAttribute = do+  BinaryBit.putBool False+  putInt32Bits (explosionAttributeActorId explosionAttribute)+  putVector (explosionAttributeLocation explosionAttribute)
+ source/library/Rattletrap/Attribute/FlaggedInt.hs view
@@ -0,0 +1,22 @@+module Rattletrap.Attribute.FlaggedInt where++import Rattletrap.Primitive++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++data FlaggedIntAttribute = FlaggedIntAttribute+  { flaggedIntAttributeFlag :: Bool+  , flaggedIntAttributeInt :: Int32+  } deriving (Eq, Ord, Show)++getFlaggedIntAttribute :: BinaryBit.BitGet FlaggedIntAttribute+getFlaggedIntAttribute = do+  flag <- BinaryBit.getBool+  int <- getInt32Bits+  pure (FlaggedIntAttribute flag int)++putFlaggedIntAttribute :: FlaggedIntAttribute -> BinaryBit.BitPut ()+putFlaggedIntAttribute flaggedIntAttribute = do+  BinaryBit.putBool (flaggedIntAttributeFlag flaggedIntAttribute)+  putInt32Bits (flaggedIntAttributeInt flaggedIntAttribute)
+ source/library/Rattletrap/Attribute/Float.hs view
@@ -0,0 +1,19 @@+module Rattletrap.Attribute.Float where++import Rattletrap.Primitive++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++newtype FloatAttribute = FloatAttribute+  { floatAttributeValue :: Float32+  } deriving (Eq, Ord, Show)++getFloatAttribute :: BinaryBit.BitGet FloatAttribute+getFloatAttribute = do+  value <- getFloat32Bits+  pure (FloatAttribute value)++putFloatAttribute :: FloatAttribute -> BinaryBit.BitPut ()+putFloatAttribute floatAttribute =+  putFloat32Bits (floatAttributeValue floatAttribute)
+ source/library/Rattletrap/Attribute/GameMode.hs view
@@ -0,0 +1,25 @@+module Rattletrap.Attribute.GameMode where++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit+import qualified Data.Word as Word++data GameModeAttribute = GameModeAttribute+  { gameModeAttributeNumBits :: Int+  , gameModeAttributeWord :: Word.Word8+  } deriving (Eq, Ord, Show)++getGameModeAttribute :: (Int, Int) -> BinaryBit.BitGet GameModeAttribute+getGameModeAttribute version = do+  let numBits =+        if version < (868, 12)+          then 2+          else 8+  word <- BinaryBit.getWord8 numBits+  pure (GameModeAttribute numBits word)++putGameModeAttribute :: GameModeAttribute -> BinaryBit.BitPut ()+putGameModeAttribute gameModeAttribute = do+  let numBits = gameModeAttributeNumBits gameModeAttribute+  let word = gameModeAttributeWord gameModeAttribute+  BinaryBit.putWord8 numBits word
+ source/library/Rattletrap/Attribute/Int.hs view
@@ -0,0 +1,18 @@+module Rattletrap.Attribute.Int where++import Rattletrap.Primitive++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++newtype IntAttribute = IntAttribute+  { intAttributeValue :: Int32+  } deriving (Eq, Ord, Show)++getIntAttribute :: BinaryBit.BitGet IntAttribute+getIntAttribute = do+  value <- getInt32Bits+  pure (IntAttribute value)++putIntAttribute :: IntAttribute -> BinaryBit.BitPut ()+putIntAttribute intAttribute = putInt32Bits (intAttributeValue intAttribute)
+ source/library/Rattletrap/Attribute/Loadout.hs view
@@ -0,0 +1,51 @@+module Rattletrap.Attribute.Loadout where++import Rattletrap.Primitive++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++data LoadoutAttribute = LoadoutAttribute+  { loadoutAttributeVersion :: Word8+  , loadoutAttributeBody :: Word32+  , loadoutAttributeDecal :: Word32+  , loadoutAttributeWheels :: Word32+  , loadoutAttributeRocketTrail :: Word32+  , loadoutAttributeAntenna :: Word32+  , loadoutAttributeTopper :: Word32+  , loadoutAttributeUnknown1 :: Word32+  , loadoutAttributeUnknown2 :: Maybe Word32+  } deriving (Eq, Ord, Show)++getLoadoutAttribute :: BinaryBit.BitGet LoadoutAttribute+getLoadoutAttribute = do+  version <- getWord8Bits+  body <- getWord32Bits+  decal <- getWord32Bits+  wheels <- getWord32Bits+  rocketTrail <- getWord32Bits+  antenna <- getWord32Bits+  topper <- getWord32Bits+  g <- getWord32Bits+  h <-+    if version > Word8 10+      then do+        h <- getWord32Bits+        pure (Just h)+      else pure Nothing+  pure+    (LoadoutAttribute version body decal wheels rocketTrail antenna topper g h)++putLoadoutAttribute :: LoadoutAttribute -> BinaryBit.BitPut ()+putLoadoutAttribute loadoutAttribute = do+  putWord8Bits (loadoutAttributeVersion loadoutAttribute)+  putWord32Bits (loadoutAttributeBody loadoutAttribute)+  putWord32Bits (loadoutAttributeDecal loadoutAttribute)+  putWord32Bits (loadoutAttributeWheels loadoutAttribute)+  putWord32Bits (loadoutAttributeRocketTrail loadoutAttribute)+  putWord32Bits (loadoutAttributeAntenna loadoutAttribute)+  putWord32Bits (loadoutAttributeTopper loadoutAttribute)+  putWord32Bits (loadoutAttributeUnknown1 loadoutAttribute)+  case loadoutAttributeUnknown2 loadoutAttribute of+    Nothing -> pure ()+    Just x -> putWord32Bits x
+ source/library/Rattletrap/Attribute/LoadoutOnline.hs view
@@ -0,0 +1,39 @@+module Rattletrap.Attribute.LoadoutOnline where++import Rattletrap.Primitive++import qualified Control.Monad as Monad+import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++newtype LoadoutOnlineAttribute = LoadoutOnlineAttribute+  { loadoutAttributeValue :: [[(Word32, CompressedWord)]]+  } deriving (Eq, Ord, Show)++getLoadoutOnlineAttribute :: BinaryBit.BitGet LoadoutOnlineAttribute+getLoadoutOnlineAttribute = do+  size <- getWord8Bits+  values <-+    Monad.replicateM+      (fromIntegral (word8Value size))+      (do innerSize <- getWord8Bits+          Monad.replicateM+            (fromIntegral (word8Value innerSize))+            (do x <- getWord32Bits+                y <- getCompressedWord 27+                pure (x, y)))+  pure (LoadoutOnlineAttribute values)++putLoadoutOnlineAttribute :: LoadoutOnlineAttribute -> BinaryBit.BitPut ()+putLoadoutOnlineAttribute loadoutAttribute = do+  let values = loadoutAttributeValue loadoutAttribute+  putWord8Bits (Word8 (fromIntegral (length values)))+  mapM_+    (\xs -> do+       putWord8Bits (Word8 (fromIntegral (length xs)))+       mapM_+         (\(x, y) -> do+            putWord32Bits x+            putCompressedWord y)+         xs)+    values
+ source/library/Rattletrap/Attribute/Loadouts.hs view
@@ -0,0 +1,22 @@+module Rattletrap.Attribute.Loadouts where++import Rattletrap.Attribute.Loadout++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++data LoadoutsAttribute = LoadoutsAttribute+  { loadoutsAttributeBlue :: LoadoutAttribute+  , loadoutsAttributeOrange :: LoadoutAttribute+  } deriving (Eq, Ord, Show)++getLoadoutsAttribute :: BinaryBit.BitGet LoadoutsAttribute+getLoadoutsAttribute = do+  blue <- getLoadoutAttribute+  orange <- getLoadoutAttribute+  pure (LoadoutsAttribute blue orange)++putLoadoutsAttribute :: LoadoutsAttribute -> BinaryBit.BitPut ()+putLoadoutsAttribute loadoutsAttribute = do+  putLoadoutAttribute (loadoutsAttributeBlue loadoutsAttribute)+  putLoadoutAttribute (loadoutsAttributeOrange loadoutsAttribute)
+ source/library/Rattletrap/Attribute/LoadoutsOnline.hs view
@@ -0,0 +1,30 @@+module Rattletrap.Attribute.LoadoutsOnline where++import Rattletrap.Attribute.LoadoutOnline++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++data LoadoutsOnlineAttribute = LoadoutsOnlineAttribute+  { loadoutsOnlineAttributeBlue :: LoadoutOnlineAttribute+  , loadoutsOnlineAttributeOrange :: LoadoutOnlineAttribute+  , loadoutsOnlineAttributeUnknown1 :: Bool+  , loadoutsOnlineAttributeUnknown2 :: Bool+  } deriving (Eq, Ord, Show)++getLoadoutsOnlineAttribute :: BinaryBit.BitGet LoadoutsOnlineAttribute+getLoadoutsOnlineAttribute = do+  blueLoadout <- getLoadoutOnlineAttribute+  orangeLoadout <- getLoadoutOnlineAttribute+  unknown1 <- BinaryBit.getBool+  unknown2 <- BinaryBit.getBool+  pure (LoadoutsOnlineAttribute blueLoadout orangeLoadout unknown1 unknown2)++putLoadoutsOnlineAttribute :: LoadoutsOnlineAttribute -> BinaryBit.BitPut ()+putLoadoutsOnlineAttribute loadoutsOnlineAttribute = do+  putLoadoutOnlineAttribute+    (loadoutsOnlineAttributeBlue loadoutsOnlineAttribute)+  putLoadoutOnlineAttribute+    (loadoutsOnlineAttributeOrange loadoutsOnlineAttribute)+  BinaryBit.putBool (loadoutsOnlineAttributeUnknown1 loadoutsOnlineAttribute)+  BinaryBit.putBool (loadoutsOnlineAttributeUnknown2 loadoutsOnlineAttribute)
+ source/library/Rattletrap/Attribute/Location.hs view
@@ -0,0 +1,19 @@+module Rattletrap.Attribute.Location where++import Rattletrap.Primitive++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++newtype LocationAttribute = LocationAttribute+  { locationAttributeValue :: Vector+  } deriving (Eq, Ord, Show)++getLocationAttribute :: BinaryBit.BitGet LocationAttribute+getLocationAttribute = do+  value <- getVector+  pure (LocationAttribute value)++putLocationAttribute :: LocationAttribute -> BinaryBit.BitPut ()+putLocationAttribute locationAttribute =+  putVector (locationAttributeValue locationAttribute)
+ source/library/Rattletrap/Attribute/MusicStinger.hs view
@@ -0,0 +1,25 @@+module Rattletrap.Attribute.MusicStinger where++import Rattletrap.Primitive++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++data MusicStingerAttribute = MusicStingerAttribute+  { musicStingerAttributeFlag :: Bool+  , musicStingerAttributeCue :: Word32+  , musicStingerAttributeTrigger :: Word8+  } deriving (Eq, Ord, Show)++getMusicStingerAttribute :: BinaryBit.BitGet MusicStingerAttribute+getMusicStingerAttribute = do+  flag <- BinaryBit.getBool+  cue <- getWord32Bits+  trigger <- getWord8Bits+  pure (MusicStingerAttribute flag cue trigger)++putMusicStingerAttribute :: MusicStingerAttribute -> BinaryBit.BitPut ()+putMusicStingerAttribute musicStingerAttribute = do+  BinaryBit.putBool (musicStingerAttributeFlag musicStingerAttribute)+  putWord32Bits (musicStingerAttributeCue musicStingerAttribute)+  putWord8Bits (musicStingerAttributeTrigger musicStingerAttribute)
+ source/library/Rattletrap/Attribute/PartyLeader.hs view
@@ -0,0 +1,33 @@+module Rattletrap.Attribute.PartyLeader where++import Rattletrap.Primitive+import Rattletrap.RemoteId++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++data PartyLeaderAttribute = PartyLeaderAttribute+  { partyLeaderAttributeSystemId :: Word8+  , partyLeaderAttributeId :: Maybe (RemoteId, Word8)+  } deriving (Eq, Ord, Show)++getPartyLeaderAttribute :: BinaryBit.BitGet PartyLeaderAttribute+getPartyLeaderAttribute = do+  systemId <- getWord8Bits+  maybeRemoteAndLocalId <-+    if systemId == Word8 0+      then pure Nothing+      else do+        remoteId <- getRemoteId systemId+        localId <- getWord8Bits+        pure (Just (remoteId, localId))+  pure (PartyLeaderAttribute systemId maybeRemoteAndLocalId)++putPartyLeaderAttribute :: PartyLeaderAttribute -> BinaryBit.BitPut ()+putPartyLeaderAttribute partyLeaderAttribute = do+  putWord8Bits (partyLeaderAttributeSystemId partyLeaderAttribute)+  case partyLeaderAttributeId partyLeaderAttribute of+    Nothing -> pure ()+    Just (remoteId, localId) -> do+      putRemoteId remoteId+      putWord8Bits localId
+ source/library/Rattletrap/Attribute/Pickup.hs view
@@ -0,0 +1,32 @@+module Rattletrap.Attribute.Pickup where++import Rattletrap.Primitive++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++data PickupAttribute = PickupAttribute+  { pickupAttributeInstigatorId :: Maybe Word32+  , pickupAttributePickedUp :: Bool+  } deriving (Eq, Ord, Show)++getPickupAttribute :: BinaryBit.BitGet PickupAttribute+getPickupAttribute = do+  instigator <- BinaryBit.getBool+  maybeInstigatorId <-+    if instigator+      then do+        instigatorId <- getWord32Bits+        pure (Just instigatorId)+      else pure Nothing+  pickedUp <- BinaryBit.getBool+  pure (PickupAttribute maybeInstigatorId pickedUp)++putPickupAttribute :: PickupAttribute -> BinaryBit.BitPut ()+putPickupAttribute pickupAttribute = do+  case pickupAttributeInstigatorId pickupAttribute of+    Nothing -> BinaryBit.putBool False+    Just instigatorId -> do+      BinaryBit.putBool True+      putWord32Bits instigatorId+  BinaryBit.putBool (pickupAttributePickedUp pickupAttribute)
+ source/library/Rattletrap/Attribute/PrivateMatchSettings.hs view
@@ -0,0 +1,48 @@+module Rattletrap.Attribute.PrivateMatchSettings where++import Rattletrap.Primitive++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++data PrivateMatchSettingsAttribute = PrivateMatchSettingsAttribute+  { privateMatchSettingsAttributeMutators :: Text+  , privateMatchSettingsAttributeJoinableBy :: Word32+  , privateMatchSettingsAttributeMaxPlayers :: Word32+  , privateMatchSettingsAttributeGameName :: Text+  , privateMatchSettingsAttributePassword :: Text+  , privateMatchSettingsAttributeFlag :: Bool+  } deriving (Eq, Ord, Show)++getPrivateMatchSettingsAttribute :: BinaryBit.BitGet PrivateMatchSettingsAttribute+getPrivateMatchSettingsAttribute = do+  mutators <- getTextBits+  joinableBy <- getWord32Bits+  maxPlayers <- getWord32Bits+  gameName <- getTextBits+  password <- getTextBits+  flag <- BinaryBit.getBool+  pure+    (PrivateMatchSettingsAttribute+       mutators+       joinableBy+       maxPlayers+       gameName+       password+       flag)++putPrivateMatchSettingsAttribute :: PrivateMatchSettingsAttribute+                                 -> BinaryBit.BitPut ()+putPrivateMatchSettingsAttribute privateMatchSettingsAttribute = do+  putTextBits+    (privateMatchSettingsAttributeMutators privateMatchSettingsAttribute)+  putWord32Bits+    (privateMatchSettingsAttributeJoinableBy privateMatchSettingsAttribute)+  putWord32Bits+    (privateMatchSettingsAttributeMaxPlayers privateMatchSettingsAttribute)+  putTextBits+    (privateMatchSettingsAttributeGameName privateMatchSettingsAttribute)+  putTextBits+    (privateMatchSettingsAttributePassword privateMatchSettingsAttribute)+  BinaryBit.putBool+    (privateMatchSettingsAttributeFlag privateMatchSettingsAttribute)
+ source/library/Rattletrap/Attribute/QWord.hs view
@@ -0,0 +1,19 @@+module Rattletrap.Attribute.QWord where++import Rattletrap.Primitive++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++newtype QWordAttribute = QWordAttribute+  { qWordAttributeValue :: Word64+  } deriving (Eq, Ord, Show)++getQWordAttribute :: BinaryBit.BitGet QWordAttribute+getQWordAttribute = do+  value <- getWord64Bits+  pure (QWordAttribute value)++putQWordAttribute :: QWordAttribute -> BinaryBit.BitPut ()+putQWordAttribute qWordAttribute =+  putWord64Bits (qWordAttributeValue qWordAttribute)
+ source/library/Rattletrap/Attribute/Reservation.hs view
@@ -0,0 +1,50 @@+module Rattletrap.Attribute.Reservation where++import Rattletrap.Attribute.UniqueId+import Rattletrap.Primitive++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit+import qualified Data.Word as Word++data ReservationAttribute = ReservationAttribute+  { reservationAttributeNumber :: CompressedWord+  , reservationAttributeUniqueId :: UniqueIdAttribute+  , reservationAttributeName :: Maybe Text+  , reservationAttributeUnknown1 :: Bool+  , reservationAttributeUnknown2 :: Bool+  , reservationAttributeUnknown3 :: Maybe Word.Word8+  } deriving (Eq, Ord, Show)++getReservationAttribute :: (Int, Int) -> BinaryBit.BitGet ReservationAttribute+getReservationAttribute version = do+  number <- getCompressedWord 7+  uniqueId <- getUniqueIdAttribute+  name <-+    if uniqueIdAttributeSystemId uniqueId == Word8 0+      then pure Nothing+      else do+        name <- getTextBits+        pure (Just name)+  a <- BinaryBit.getBool+  b <- BinaryBit.getBool+  mc <-+    if version < (868, 12)+      then pure Nothing+      else do+        c <- BinaryBit.getWord8 6+        pure (Just c)+  pure (ReservationAttribute number uniqueId name a b mc)++putReservationAttribute :: ReservationAttribute -> BinaryBit.BitPut ()+putReservationAttribute reservationAttribute = do+  putCompressedWord (reservationAttributeNumber reservationAttribute)+  putUniqueIdAttribute (reservationAttributeUniqueId reservationAttribute)+  case reservationAttributeName reservationAttribute of+    Nothing -> pure ()+    Just name -> putTextBits name+  BinaryBit.putBool (reservationAttributeUnknown1 reservationAttribute)+  BinaryBit.putBool (reservationAttributeUnknown2 reservationAttribute)+  case reservationAttributeUnknown3 reservationAttribute of+    Nothing -> pure ()+    Just c -> BinaryBit.putWord8 6 c
+ source/library/Rattletrap/Attribute/RigidBodyState.hs view
@@ -0,0 +1,52 @@+module Rattletrap.Attribute.RigidBodyState where++import Rattletrap.Primitive++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++data RigidBodyStateAttribute = RigidBodyStateAttribute+  { rigidBodyStateAttributeSleeping :: Bool+  , rigidBodyStateAttributeLocation :: Vector+  , rigidBodyStateAttributeRotation :: CompressedWordVector+  , rigidBodyStateAttributeLinearVelocity :: Maybe Vector+  , rigidBodyStateAttributeAngularVelocity :: Maybe Vector+  } deriving (Eq, Ord, Show)++getRigidBodyStateAttribute :: BinaryBit.BitGet RigidBodyStateAttribute+getRigidBodyStateAttribute = do+  sleeping <- BinaryBit.getBool+  location <- getVector+  rotation <- getCompressedWordVector+  linearVelocity <-+    if sleeping+      then pure Nothing+      else do+        linearVelocity <- getVector+        pure (Just linearVelocity)+  angularVelocity <-+    if sleeping+      then pure Nothing+      else do+        angularVelocity <- getVector+        pure (Just angularVelocity)+  pure+    (RigidBodyStateAttribute+       sleeping+       location+       rotation+       linearVelocity+       angularVelocity)++putRigidBodyStateAttribute :: RigidBodyStateAttribute -> BinaryBit.BitPut ()+putRigidBodyStateAttribute rigidBodyStateAttribute = do+  BinaryBit.putBool (rigidBodyStateAttributeSleeping rigidBodyStateAttribute)+  putVector (rigidBodyStateAttributeLocation rigidBodyStateAttribute)+  putCompressedWordVector+    (rigidBodyStateAttributeRotation rigidBodyStateAttribute)+  case rigidBodyStateAttributeLinearVelocity rigidBodyStateAttribute of+    Nothing -> pure ()+    Just linearVelocity -> putVector linearVelocity+  case rigidBodyStateAttributeAngularVelocity rigidBodyStateAttribute of+    Nothing -> pure ()+    Just angularVelocity -> putVector angularVelocity
+ source/library/Rattletrap/Attribute/String.hs view
@@ -0,0 +1,19 @@+module Rattletrap.Attribute.String where++import Rattletrap.Primitive++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++newtype StringAttribute = StringAttribute+  { stringAttributeValue :: Text+  } deriving (Eq, Ord, Show)++getStringAttribute :: BinaryBit.BitGet StringAttribute+getStringAttribute = do+  value <- getTextBits+  pure (StringAttribute value)++putStringAttribute :: StringAttribute -> BinaryBit.BitPut ()+putStringAttribute stringAttribute =+  putTextBits (stringAttributeValue stringAttribute)
+ source/library/Rattletrap/Attribute/TeamPaint.hs view
@@ -0,0 +1,37 @@+module Rattletrap.Attribute.TeamPaint where++import Rattletrap.Primitive++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++data TeamPaintAttribute = TeamPaintAttribute+  { teamPaintAttributeTeam :: Word8+  , teamPaintAttributePrimaryColor :: Word8+  , teamPaintAttributeAccentColor :: Word8+  , teamPaintAttributePrimaryFinish :: Word32+  , teamPaintAttributeAccentFinish :: Word32+  } deriving (Eq, Ord, Show)++getTeamPaintAttribute :: BinaryBit.BitGet TeamPaintAttribute+getTeamPaintAttribute = do+  team <- getWord8Bits+  primaryColor <- getWord8Bits+  accentColor <- getWord8Bits+  primaryFinish <- getWord32Bits+  accentFinish <- getWord32Bits+  pure+    (TeamPaintAttribute+       team+       primaryColor+       accentColor+       primaryFinish+       accentFinish)++putTeamPaintAttribute :: TeamPaintAttribute -> BinaryBit.BitPut ()+putTeamPaintAttribute teamPaintAttribute = do+  putWord8Bits (teamPaintAttributeTeam teamPaintAttribute)+  putWord8Bits (teamPaintAttributePrimaryColor teamPaintAttribute)+  putWord8Bits (teamPaintAttributeAccentColor teamPaintAttribute)+  putWord32Bits (teamPaintAttributePrimaryFinish teamPaintAttribute)+  putWord32Bits (teamPaintAttributeAccentFinish teamPaintAttribute)
+ source/library/Rattletrap/Attribute/UniqueId.hs view
@@ -0,0 +1,26 @@+module Rattletrap.Attribute.UniqueId where++import Rattletrap.Primitive+import Rattletrap.RemoteId++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++data UniqueIdAttribute = UniqueIdAttribute+  { uniqueIdAttributeSystemId :: Word8+  , uniqueIdAttributeRemoteId :: RemoteId+  , uniqueIdAttributeLocalId :: Word8+  } deriving (Eq, Ord, Show)++getUniqueIdAttribute :: BinaryBit.BitGet UniqueIdAttribute+getUniqueIdAttribute = do+  systemId <- getWord8Bits+  remoteId <- getRemoteId systemId+  localId <- getWord8Bits+  pure (UniqueIdAttribute systemId remoteId localId)++putUniqueIdAttribute :: UniqueIdAttribute -> BinaryBit.BitPut ()+putUniqueIdAttribute uniqueIdAttribute = do+  putWord8Bits (uniqueIdAttributeSystemId uniqueIdAttribute)+  putRemoteId (uniqueIdAttributeRemoteId uniqueIdAttribute)+  putWord8Bits (uniqueIdAttributeLocalId uniqueIdAttribute)
+ source/library/Rattletrap/Attribute/WeldedInfo.hs view
@@ -0,0 +1,31 @@+module Rattletrap.Attribute.WeldedInfo where++import Rattletrap.Primitive++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++data WeldedInfoAttribute = WeldedInfoAttribute+  { weldedInfoAttributeActive :: Bool+  , weldedInfoAttributeActorId :: Int32+  , weldedInfoAttributeOffset :: Vector+  , weldedInfoAttributeMass :: Float32+  , weldedInfoAttributeRotation :: Int8Vector+  } deriving (Eq, Ord, Show)++getWeldedInfoAttribute :: BinaryBit.BitGet WeldedInfoAttribute+getWeldedInfoAttribute = do+  active <- BinaryBit.getBool+  actorId <- getInt32Bits+  offset <- getVector+  mass <- getFloat32Bits+  rotation <- getInt8Vector+  pure (WeldedInfoAttribute active actorId offset mass rotation)++putWeldedInfoAttribute :: WeldedInfoAttribute -> BinaryBit.BitPut ()+putWeldedInfoAttribute weldedInfoAttribute = do+  BinaryBit.putBool (weldedInfoAttributeActive weldedInfoAttribute)+  putInt32Bits (weldedInfoAttributeActorId weldedInfoAttribute)+  putVector (weldedInfoAttributeOffset weldedInfoAttribute)+  putFloat32Bits (weldedInfoAttributeMass weldedInfoAttribute)+  putInt8Vector (weldedInfoAttributeRotation weldedInfoAttribute)
+ source/library/Rattletrap/AttributeMapping.hs view
@@ -0,0 +1,21 @@+module Rattletrap.AttributeMapping where++import Rattletrap.Primitive++import qualified Data.Binary as Binary++data AttributeMapping = AttributeMapping+  { attributeMappingObjectId :: Word32+  , attributeMappingStreamId :: Word32+  } deriving (Eq, Ord, Show)++getAttributeMapping :: Binary.Get AttributeMapping+getAttributeMapping = do+  objectId <- getWord32+  streamId <- getWord32+  pure (AttributeMapping objectId streamId)++putAttributeMapping :: AttributeMapping -> Binary.Put+putAttributeMapping attributeMapping = do+  putWord32 (attributeMappingObjectId attributeMapping)+  putWord32 (attributeMappingStreamId attributeMapping)
+ source/library/Rattletrap/AttributeType.hs view
@@ -0,0 +1,31 @@+module Rattletrap.AttributeType where++data AttributeType+  = BooleanAttributeType+  | ByteAttributeType+  | CamSettingsAttributeType+  | ClubColorsAttributeType+  | DemolishAttributeType+  | EnumAttributeType+  | ExplosionAttributeType+  | FlaggedIntAttributeType+  | FloatAttributeType+  | GameModeAttributeType+  | IntAttributeType+  | LoadoutAttributeType+  | LoadoutOnlineAttributeType+  | LoadoutsAttributeType+  | LoadoutsOnlineAttributeType+  | LocationAttributeType+  | MusicStingerAttributeType+  | PartyLeaderAttributeType+  | PickupAttributeType+  | PrivateMatchSettingsAttributeType+  | QWordAttributeType+  | ReservationAttributeType+  | RigidBodyStateAttributeType+  | StringAttributeType+  | TeamPaintAttributeType+  | UniqueIdAttributeType+  | WeldedInfoAttributeType+  deriving (Eq, Ord, Show)
+ source/library/Rattletrap/AttributeValue.hs view
@@ -0,0 +1,217 @@+module Rattletrap.AttributeValue+  ( module Rattletrap.AttributeValue+  , module Rattletrap.Attribute.Boolean+  , module Rattletrap.Attribute.Byte+  , module Rattletrap.Attribute.CamSettings+  , module Rattletrap.Attribute.ClubColors+  , module Rattletrap.Attribute.Demolish+  , module Rattletrap.Attribute.Enum+  , module Rattletrap.Attribute.Explosion+  , module Rattletrap.Attribute.FlaggedInt+  , module Rattletrap.Attribute.Float+  , module Rattletrap.Attribute.GameMode+  , module Rattletrap.Attribute.Int+  , module Rattletrap.Attribute.Loadout+  , module Rattletrap.Attribute.LoadoutOnline+  , module Rattletrap.Attribute.Loadouts+  , module Rattletrap.Attribute.LoadoutsOnline+  , module Rattletrap.Attribute.Location+  , module Rattletrap.Attribute.MusicStinger+  , module Rattletrap.Attribute.PartyLeader+  , module Rattletrap.Attribute.Pickup+  , module Rattletrap.Attribute.PrivateMatchSettings+  , module Rattletrap.Attribute.QWord+  , module Rattletrap.Attribute.Reservation+  , module Rattletrap.Attribute.RigidBodyState+  , module Rattletrap.Attribute.String+  , module Rattletrap.Attribute.TeamPaint+  , module Rattletrap.Attribute.UniqueId+  , module Rattletrap.Attribute.WeldedInfo+  ) where++import Rattletrap.Attribute.Boolean+import Rattletrap.Attribute.Byte+import Rattletrap.Attribute.CamSettings+import Rattletrap.Attribute.ClubColors+import Rattletrap.Attribute.Demolish+import Rattletrap.Attribute.Enum+import Rattletrap.Attribute.Explosion+import Rattletrap.Attribute.FlaggedInt+import Rattletrap.Attribute.Float+import Rattletrap.Attribute.GameMode+import Rattletrap.Attribute.Int+import Rattletrap.Attribute.Loadout+import Rattletrap.Attribute.LoadoutOnline+import Rattletrap.Attribute.Loadouts+import Rattletrap.Attribute.LoadoutsOnline+import Rattletrap.Attribute.Location+import Rattletrap.Attribute.MusicStinger+import Rattletrap.Attribute.PartyLeader+import Rattletrap.Attribute.Pickup+import Rattletrap.Attribute.PrivateMatchSettings+import Rattletrap.Attribute.QWord+import Rattletrap.Attribute.Reservation+import Rattletrap.Attribute.RigidBodyState+import Rattletrap.Attribute.String+import Rattletrap.Attribute.TeamPaint+import Rattletrap.Attribute.UniqueId+import Rattletrap.Attribute.WeldedInfo+import Rattletrap.AttributeType+import Rattletrap.Data+import Rattletrap.Primitive++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit+import qualified Data.Map.Strict as Map++data AttributeValue+  = BooleanAttributeValue BooleanAttribute+  | ByteAttributeValue ByteAttribute+  | CamSettingsAttributeValue CamSettingsAttribute+  | ClubColorsAttributeValue ClubColorsAttribute+  | DemolishAttributeValue DemolishAttribute+  | EnumAttributeValue EnumAttribute+  | ExplosionAttributeValue ExplosionAttribute+  | FlaggedIntAttributeValue FlaggedIntAttribute+  | FloatAttributeValue FloatAttribute+  | GameModeAttributeValue GameModeAttribute+  | IntAttributeValue IntAttribute+  | LoadoutAttributeValue LoadoutAttribute+  | LoadoutOnlineAttributeValue LoadoutOnlineAttribute+  | LoadoutsAttributeValue LoadoutsAttribute+  | LoadoutsOnlineAttributeValue LoadoutsOnlineAttribute+  | LocationAttributeValue LocationAttribute+  | MusicStingerAttributeValue MusicStingerAttribute+  | PartyLeaderAttributeValue PartyLeaderAttribute+  | PickupAttributeValue PickupAttribute+  | PrivateMatchSettingsAttributeValue PrivateMatchSettingsAttribute+  | QWordAttributeValue QWordAttribute+  | ReservationAttributeValue ReservationAttribute+  | RigidBodyStateAttributeValue RigidBodyStateAttribute+  | StringAttributeValue StringAttribute+  | TeamPaintAttributeValue TeamPaintAttribute+  | UniqueIdAttributeValue UniqueIdAttribute+  | WeldedInfoAttributeValue WeldedInfoAttribute+  deriving (Eq, Ord, Show)++getAttributeValue :: (Int, Int) -> Text -> BinaryBit.BitGet AttributeValue+getAttributeValue version name =+  case Map.lookup name attributeTypes of+    Just constructor ->+      case constructor of+        BooleanAttributeType -> do+          x <- getBooleanAttribute+          pure (BooleanAttributeValue x)+        ByteAttributeType -> do+          x <- getByteAttribute+          pure (ByteAttributeValue x)+        CamSettingsAttributeType -> do+          x <- getCamSettingsAttribute+          pure (CamSettingsAttributeValue x)+        ClubColorsAttributeType -> do+          x <- getClubColorsAttribute+          pure (ClubColorsAttributeValue x)+        DemolishAttributeType -> do+          x <- getDemolishAttribute+          pure (DemolishAttributeValue x)+        EnumAttributeType -> do+          x <- getEnumAttribute+          pure (EnumAttributeValue x)+        ExplosionAttributeType -> do+          x <- getExplosionAttribute+          pure (ExplosionAttributeValue x)+        FlaggedIntAttributeType -> do+          x <- getFlaggedIntAttribute+          pure (FlaggedIntAttributeValue x)+        FloatAttributeType -> do+          x <- getFloatAttribute+          pure (FloatAttributeValue x)+        GameModeAttributeType -> do+          x <- getGameModeAttribute version+          pure (GameModeAttributeValue x)+        IntAttributeType -> do+          x <- getIntAttribute+          pure (IntAttributeValue x)+        LoadoutAttributeType -> do+          x <- getLoadoutAttribute+          pure (LoadoutAttributeValue x)+        LoadoutOnlineAttributeType -> do+          x <- getLoadoutOnlineAttribute+          pure (LoadoutOnlineAttributeValue x)+        LoadoutsAttributeType -> do+          x <- getLoadoutsAttribute+          pure (LoadoutsAttributeValue x)+        LoadoutsOnlineAttributeType -> do+          x <- getLoadoutsOnlineAttribute+          pure (LoadoutsOnlineAttributeValue x)+        LocationAttributeType -> do+          x <- getLocationAttribute+          pure (LocationAttributeValue x)+        MusicStingerAttributeType -> do+          x <- getMusicStingerAttribute+          pure (MusicStingerAttributeValue x)+        PartyLeaderAttributeType -> do+          x <- getPartyLeaderAttribute+          pure (PartyLeaderAttributeValue x)+        PickupAttributeType -> do+          x <- getPickupAttribute+          pure (PickupAttributeValue x)+        PrivateMatchSettingsAttributeType -> do+          x <- getPrivateMatchSettingsAttribute+          pure (PrivateMatchSettingsAttributeValue x)+        QWordAttributeType -> do+          x <- getQWordAttribute+          pure (QWordAttributeValue x)+        ReservationAttributeType -> do+          x <- getReservationAttribute version+          pure (ReservationAttributeValue x)+        RigidBodyStateAttributeType -> do+          x <- getRigidBodyStateAttribute+          pure (RigidBodyStateAttributeValue x)+        StringAttributeType -> do+          x <- getStringAttribute+          pure (StringAttributeValue x)+        TeamPaintAttributeType -> do+          x <- getTeamPaintAttribute+          pure (TeamPaintAttributeValue x)+        UniqueIdAttributeType -> do+          x <- getUniqueIdAttribute+          pure (UniqueIdAttributeValue x)+        WeldedInfoAttributeType -> do+          x <- getWeldedInfoAttribute+          pure (WeldedInfoAttributeValue x)+    Nothing -> fail ("don't know how to get attribute value " ++ show name)++attributeTypes :: Map.Map Text AttributeType+attributeTypes = Map.mapKeys stringToText (Map.fromList rawAttributeTypes)++putAttributeValue :: AttributeValue -> BinaryBit.BitPut ()+putAttributeValue value =+  case value of+    BooleanAttributeValue x -> putBooleanAttribute x+    ByteAttributeValue x -> putByteAttribute x+    CamSettingsAttributeValue x -> putCamSettingsAttribute x+    ClubColorsAttributeValue x -> putClubColorsAttribute x+    DemolishAttributeValue x -> putDemolishAttribute x+    EnumAttributeValue x -> putEnumAttribute x+    ExplosionAttributeValue x -> putExplosionAttribute x+    FlaggedIntAttributeValue x -> putFlaggedIntAttribute x+    FloatAttributeValue x -> putFloatAttribute x+    GameModeAttributeValue x -> putGameModeAttribute x+    IntAttributeValue x -> putIntAttribute x+    LoadoutAttributeValue x -> putLoadoutAttribute x+    LoadoutOnlineAttributeValue x -> putLoadoutOnlineAttribute x+    LoadoutsAttributeValue x -> putLoadoutsAttribute x+    LoadoutsOnlineAttributeValue x -> putLoadoutsOnlineAttribute x+    LocationAttributeValue x -> putLocationAttribute x+    MusicStingerAttributeValue x -> putMusicStingerAttribute x+    PartyLeaderAttributeValue x -> putPartyLeaderAttribute x+    PickupAttributeValue x -> putPickupAttribute x+    PrivateMatchSettingsAttributeValue x -> putPrivateMatchSettingsAttribute x+    QWordAttributeValue x -> putQWordAttribute x+    ReservationAttributeValue x -> putReservationAttribute x+    RigidBodyStateAttributeValue x -> putRigidBodyStateAttribute x+    StringAttributeValue x -> putStringAttribute x+    TeamPaintAttributeValue x -> putTeamPaintAttribute x+    UniqueIdAttributeValue x -> putUniqueIdAttribute x+    WeldedInfoAttributeValue x -> putWeldedInfoAttribute x
+ source/library/Rattletrap/Cache.hs view
@@ -0,0 +1,28 @@+module Rattletrap.Cache where++import Rattletrap.AttributeMapping+import Rattletrap.Primitive++import qualified Data.Binary as Binary++data Cache = Cache+  { cacheClassId :: Word32+  , cacheParentCacheId :: Word32+  , cacheCacheId :: Word32+  , cacheAttributeMappings :: List AttributeMapping+  } deriving (Eq, Ord, Show)++getCache :: Binary.Get Cache+getCache = do+  classId <- getWord32+  parentCacheId <- getWord32+  cacheId <- getWord32+  attributeMappings <- getList getAttributeMapping+  pure (Cache classId parentCacheId cacheId attributeMappings)++putCache :: Cache -> Binary.Put+putCache cache = do+  putWord32 (cacheClassId cache)+  putWord32 (cacheParentCacheId cache)+  putWord32 (cacheCacheId cache)+  putList putAttributeMapping (cacheAttributeMappings cache)
+ source/library/Rattletrap/ClassAttributeMap.hs view
@@ -0,0 +1,292 @@+module Rattletrap.ClassAttributeMap where++import Rattletrap.ActorMap+import Rattletrap.AttributeMapping+import Rattletrap.Cache+import Rattletrap.ClassMapping+import Rattletrap.Data+import Rattletrap.Primitive++import qualified Data.Bimap as Bimap+import qualified Data.IntMap.Strict as IntMap+import qualified Data.List as List+import qualified Data.Map.Strict as Map+import qualified Data.Maybe as Maybe+import qualified Data.Set as Set+import qualified Data.Text as Text++-- | This data structure holds all the information about classes, objects, and+-- attributes in the replay. The class hierarchy is not fixed; it is encoded+-- in the 'Rattletrap.Content.Content'. Similarly, the attributes that belong+-- to each class are not fixed either. Converting the raw data into a usable+-- structure is tedious; see 'makeClassAttributeMap'.+data ClassAttributeMap = ClassAttributeMap+  { classAttributeMapObjectMap :: Map.Map Word32 Text+  -- ^ A map from object IDs to their names.+  , classAttributeMapObjectClassMap :: Map.Map Word32 Word32+  -- ^ A map from object IDs to their class IDs.+  , classAttributeMapValue :: Map.Map Word32 (Map.Map Word32 Word32)+  -- ^ A map from class IDs to a map from attribute stream IDs to attribute+  -- IDs.+  , classAttributeMapNameMap :: IntMap.IntMap Text+  } deriving (Eq, Show)++-- | Makes a 'ClassAttributeMap' given the necessary fields from the+-- 'Rattletrap.Content.Content'.+makeClassAttributeMap+  :: List Text+  -- ^ From 'Rattletrap.Content.contentObjects'.+  -> List ClassMapping+  -- ^ From 'Rattletrap.Content.contentClassMappings'.+  -> List Cache+  -- ^ From 'Rattletrap.Content.contentCaches'.+  -> List Text+  -- ^ From 'Rattletrap.Content.contentNames'.+  -> ClassAttributeMap+makeClassAttributeMap objects classMappings caches names =+  let objectMap = makeObjectMap objects+      classMap = makeClassMap classMappings+      objectClassMap = makeObjectClassMap objectMap classMap+      classCache = makeClassCache classMap caches+      attributeMap = makeAttributeMap caches+      classIds = map (\(_, classId, _, _) -> classId) classCache+      parentMap = makeParentMap classCache+      value =+        Map.fromList+          (map+             (\classId ->+                let ownAttributes =+                      Maybe.fromMaybe+                        Map.empty+                        (Map.lookup classId attributeMap)+                    parentsAttributes =+                      case Map.lookup classId parentMap of+                        Nothing -> []+                        Just parentClassIds ->+                          map+                            (\parentClassId ->+                               Maybe.fromMaybe+                                 Map.empty+                                 (Map.lookup parentClassId attributeMap))+                            parentClassIds+                    attributes = ownAttributes : parentsAttributes+                in (classId, Map.fromList (concatMap Map.toList attributes)))+             classIds)+      nameMap = makeNameMap names+  in ClassAttributeMap objectMap objectClassMap value nameMap++makeNameMap :: List Text -> IntMap.IntMap Text+makeNameMap names = IntMap.fromDistinctAscList (zip [0 ..] (listValue names))++getName :: IntMap.IntMap Text -> Word32 -> Maybe Text+getName nameMap nameIndex =+  IntMap.lookup (fromIntegral (word32Value nameIndex)) nameMap++makeObjectClassMap :: Map.Map Word32 Text+                   -> Bimap.Bimap Word32 Text+                   -> Map.Map Word32 Word32+makeObjectClassMap objectMap classMap = do+  let objectIds = Map.keys objectMap+  let classIds = map (getClassId objectMap classMap) objectIds+  let rawPairs = zip objectIds classIds+  let pairs =+        Maybe.mapMaybe+          (\(objectId, maybeClassId) ->+             case maybeClassId of+               Nothing -> Nothing+               Just classId -> Just (objectId, classId))+          rawPairs+  Map.fromList pairs++getClassId :: Map.Map Word32 Text+           -> Bimap.Bimap Word32 Text+           -> Word32+           -> Maybe Word32+getClassId objectMap classMap objectId = do+  objectName <- getObjectName objectMap objectId+  className <- getClassName objectName+  Bimap.lookupR className classMap++makeClassCache :: Bimap.Bimap Word32 Text+               -> List Cache+               -> [(Maybe Text, Word32, Word32, Word32)]+makeClassCache classMap caches =+  map+    (\cache ->+       let classId = cacheClassId cache+       in ( Bimap.lookup classId classMap+          , classId+          , cacheCacheId cache+          , cacheParentCacheId cache))+    (listValue caches)++makeClassMap :: List ClassMapping -> Bimap.Bimap Word32 Text+makeClassMap classMappings =+  Bimap.fromList+    (map+       (\classMapping ->+          (classMappingStreamId classMapping, classMappingName classMapping))+       (listValue classMappings))++makeAttributeMap :: List Cache -> Map.Map Word32 (Map.Map Word32 Word32)+makeAttributeMap caches =+  Map.fromList+    (map+       (\cache ->+          ( cacheClassId cache+          , Map.fromList+              (map+                 (\attributeMapping ->+                    ( attributeMappingStreamId attributeMapping+                    , attributeMappingObjectId attributeMapping))+                 (listValue (cacheAttributeMappings cache)))))+       (listValue caches))++makeShallowParentMap :: [(Maybe Text, Word32, Word32, Word32)]+                     -> Map.Map Word32 Word32+makeShallowParentMap classCache =+  Map.fromList+    (Maybe.mapMaybe+       (\xs ->+          case xs of+            [] -> Nothing+            (maybeClassName, classId, _, parentCacheId):rest -> do+              parentClassId <- getParentClass maybeClassName parentCacheId rest+              pure (classId, parentClassId))+       (List.tails (reverse classCache)))++makeParentMap :: [(Maybe Text, Word32, Word32, Word32)]+              -> Map.Map Word32 [Word32]+makeParentMap classCache =+  let shallowParentMap = makeShallowParentMap classCache+  in Map.mapWithKey+       (\classId _ -> getParentClasses shallowParentMap classId)+       shallowParentMap++getParentClasses :: Map.Map Word32 Word32 -> Word32 -> [Word32]+getParentClasses shallowParentMap classId =+  case Map.lookup classId shallowParentMap of+    Nothing -> []+    Just parentClassId ->+      parentClassId : getParentClasses shallowParentMap parentClassId++getParentClass :: Maybe Text+               -> Word32+               -> [(Maybe Text, Word32, Word32, Word32)]+               -> Maybe Word32+getParentClass maybeClassName parentCacheId xs =+  case maybeClassName of+    Nothing -> getParentClassById parentCacheId xs+    Just className -> getParentClassByName className parentCacheId xs++getParentClassById :: Word32+                   -> [(Maybe Text, Word32, Word32, Word32)]+                   -> Maybe Word32+getParentClassById parentCacheId xs =+  case dropWhile (\(_, _, cacheId, _) -> cacheId /= parentCacheId) xs of+    [] ->+      if parentCacheId == Word32 0+        then Nothing+        else getParentClassById (Word32 (word32Value parentCacheId - 1)) xs+    (_, parentClassId, _, _):_ -> Just parentClassId++getParentClassByName :: Text+                     -> Word32+                     -> [(Maybe Text, Word32, Word32, Word32)]+                     -> Maybe Word32+getParentClassByName className parentCacheId xs =+  case Map.lookup className parentClasses of+    Nothing -> getParentClassById parentCacheId xs+    Just parentClassName ->+      Maybe.maybe+        (getParentClassById parentCacheId xs)+        Just+        (Maybe.listToMaybe+           (map+              (\(_, parentClassId, _, _) -> parentClassId)+              (filter+                 (\(_, _, cacheId, _) -> cacheId == parentCacheId)+                 (filter+                    (\(maybeClassName, _, _, _) ->+                       maybeClassName == Just parentClassName)+                    xs))))++parentClasses :: Map.Map Text Text+parentClasses =+  Map.map+    stringToText+    (Map.mapKeys stringToText (Map.fromList rawParentClasses))++makeObjectMap :: List Text -> Map.Map Word32 Text+makeObjectMap objects =+  Map.fromAscList (zip (map Word32 [0 ..]) (listValue objects))++getObjectName :: Map.Map Word32 Text -> Word32 -> Maybe Text+getObjectName objectMap objectId = Map.lookup objectId objectMap++getClassName :: Text -> Maybe Text+getClassName rawObjectName =+  Map.lookup (normalizeObjectName rawObjectName) objectClasses++normalizeObjectName :: Text -> Text+normalizeObjectName objectName =+  let name = textValue objectName+      crowdActor = Text.pack "TheWorld:PersistentLevel.CrowdActor_TA"+      crowdManager = Text.pack "TheWorld:PersistentLevel.CrowdManager_TA"+      boostPickup = Text.pack "TheWorld:PersistentLevel.VehiclePickup_Boost_TA"+      mapScoreboard = Text.pack "TheWorld:PersistentLevel.InMapScoreboard_TA"+  in if Text.isInfixOf crowdActor name+       then Text crowdActor+       else if Text.isInfixOf crowdManager name+              then Text crowdManager+              else if Text.isInfixOf boostPickup name+                     then Text boostPickup+                     else if Text.isInfixOf mapScoreboard name+                            then Text mapScoreboard+                            else objectName++objectClasses :: Map.Map Text Text+objectClasses =+  Map.map+    stringToText+    (Map.mapKeys stringToText (Map.fromList rawObjectClasses))++classHasLocation :: Text -> Bool+classHasLocation className = Set.member className classesWithLocation++classesWithLocation :: Set.Set Text+classesWithLocation = Set.fromList (map stringToText rawClassesWithLocation)++classHasRotation :: Text -> Bool+classHasRotation className = Set.member className classesWithRotation++classesWithRotation :: Set.Set Text+classesWithRotation = Set.fromList (map stringToText rawClassesWithRotation)++getAttributeIdLimit :: Map.Map Word32 Word32 -> Maybe Word+getAttributeIdLimit attributeMap = do+  ((streamId, _), _) <- Map.maxViewWithKey attributeMap+  let limit = fromIntegral (word32Value streamId)+  pure limit++getAttributeName :: ClassAttributeMap+                 -> Map.Map Word32 Word32+                 -> CompressedWord+                 -> Maybe Text+getAttributeName classAttributeMap attributeMap streamId = do+  let key = Word32 (fromIntegral (compressedWordValue streamId))+  attributeId <- Map.lookup key attributeMap+  let objectMap = classAttributeMapObjectMap classAttributeMap+  Map.lookup attributeId objectMap++getAttributeMap+  :: ClassAttributeMap+  -> ActorMap+  -> CompressedWord+  -> Maybe (Map.Map Word32 Word32)+getAttributeMap classAttributeMap actorMap actorId = do+  objectId <- Map.lookup actorId actorMap+  let objectClassMap = classAttributeMapObjectClassMap classAttributeMap+  classId <- Map.lookup objectId objectClassMap+  let value = classAttributeMapValue classAttributeMap+  Map.lookup classId value
+ source/library/Rattletrap/ClassMapping.hs view
@@ -0,0 +1,21 @@+module Rattletrap.ClassMapping where++import Rattletrap.Primitive++import qualified Data.Binary as Binary++data ClassMapping = ClassMapping+  { classMappingName :: Text+  , classMappingStreamId :: Word32+  } deriving (Eq, Ord, Show)++getClassMapping :: Binary.Get ClassMapping+getClassMapping = do+  name <- getText+  streamId <- getWord32+  pure (ClassMapping name streamId)++putClassMapping :: ClassMapping -> Binary.Put+putClassMapping classMapping = do+  putText (classMappingName classMapping)+  putWord32 (classMappingStreamId classMapping)
+ source/library/Rattletrap/Content.hs view
@@ -0,0 +1,121 @@+module Rattletrap.Content where++import Rattletrap.ActorMap+import Rattletrap.Cache+import Rattletrap.ClassAttributeMap+import Rattletrap.ClassMapping+import Rattletrap.Frame+import Rattletrap.KeyFrame+import Rattletrap.Mark+import Rattletrap.Message+import Rattletrap.Primitive+import Rattletrap.Utility++import qualified Data.Binary as Binary+import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit+import qualified Data.Binary.Get as Binary+import qualified Data.Binary.Put as Binary++-- | Contains low-level game data about a 'Rattletrap.Replay.Replay'.+data Content = Content+  { contentLevels :: List Text+  -- ^ This typically only has one element, like @stadium_oob_audio_map@.+  , contentKeyFrames :: List KeyFrame+  -- ^ A list of which frames are key frames. Although they aren't necessary+  -- for replay, key frames are frames that replicate every actor. They+  -- typically happen once every 10 seconds.+  , contentStreamSize :: Word32+  -- ^ The size of the stream in bytes. This is only really necessary because+  -- the stream has some arbitrary amount of padding at the end.+  , contentFrames :: [Frame]+  -- ^ The actual game data. This is where all the interesting information is.+  , contentMessages :: List Message+  -- ^ Debugging messages. In newer replays, this is always empty.+  , contentMarks :: List Mark+  -- ^ Tick marks shown on the scrubber when watching a replay.+  , contentPackages :: List Text+  -- ^ A list of @.upk@ files to load, like+  -- @..\\..\\TAGame\\CookedPCConsole\\Stadium_P.upk@.+  , contentObjects :: List Text+  -- ^ Objects in the stream. Used for the+  -- 'Rattletrap.ClassAttributeMap.ClassAttributeMap'.+  , contentNames :: List Text+  -- ^ It's not clear what these are used for. This list is usually not empty,+  -- but appears unused otherwise.+  , contentClassMappings :: List ClassMapping+  -- ^ A mapping between classes and their ID in the stream. Used for the+  -- 'Rattletrap.ClassAttributeMap.ClassAttributeMap'.+  , contentCaches :: List Cache+  -- ^ A list of classes along with their parent classes and attributes. Used+  -- for the 'Rattletrap.ClassAttributeMap.ClassAttributeMap'.+  } deriving (Eq, Ord, Show)++getContent+  :: (Int, Int)+  -- ^ Major and minor version numbers, usually from+  -- 'Rattletrap.Header.getVersion'.+  -> Int+  -- ^ The number of frames in the stream, usually from+  -- 'Rattletrap.Header.getNumFrames'.+  -> Word+  -- ^ The maximum number of channels in the stream, usually from+  -- 'Rattletrap.Header.getMaxChannels'.+  -> Binary.Get Content+getContent version numFrames maxChannels = do+  levels <- getList getText+  keyFrames <- getList getKeyFrame+  streamSize <- getWord32+  stream <- Binary.getLazyByteString (fromIntegral (word32Value streamSize))+  messages <- getList getMessage+  marks <- getList getMark+  packages <- getList getText+  objects <- getList getText+  names <- getList getText+  classMappings <- getList getClassMapping+  caches <- getList getCache+  let classAttributeMap =+        makeClassAttributeMap objects classMappings caches names+  let frames =+        Binary.runGet+          (BinaryBit.runBitGet+             (do (theFrames, _) <-+                   getFrames+                     version+                     numFrames+                     maxChannels+                     classAttributeMap+                     makeActorMap+                 pure theFrames))+          (reverseBytes stream)+  pure+    (Content+       levels+       keyFrames+       streamSize+       frames+       messages+       marks+       packages+       objects+       names+       classMappings+       caches)++putContent :: Content -> Binary.Put+putContent content = do+  putList putText (contentLevels content)+  putList putKeyFrame (contentKeyFrames content)+  let streamSize = contentStreamSize content+  putWord32 streamSize+  let stream =+        Binary.runPut (BinaryBit.runBitPut (putFrames (contentFrames content)))+  Binary.putLazyByteString+    (reverseBytes (padBytes (word32Value streamSize) stream))+  putList putMessage (contentMessages content)+  putList putMark (contentMarks content)+  putList putText (contentPackages content)+  putList putText (contentObjects content)+  putList putText (contentNames content)+  putList putClassMapping (contentClassMappings content)+  putList putCache (contentCaches content)
+ source/library/Rattletrap/Crc.hs view
@@ -0,0 +1,46 @@+module Rattletrap.Crc where++import Rattletrap.Data++import qualified Data.Bits as Bits+import qualified Data.ByteString.Lazy as ByteString+import qualified Data.Vector.Unboxed as Vector+import qualified Data.Word as Word++-- | Computes the CRC32 of some bytes. This is done to ensure that the bytes+-- are valid before trying to parse them.+--+-- @+-- getCrc32 ('Data.ByteString.Lazy.pack' [0x00])+-- @+--+-- This CRC uses an initial value of @0xefcbf201@ and a polynomial of+-- @0x04c11db7@.+getCrc32 :: ByteString.ByteString -> Word.Word32+getCrc32 bytes = do+  let update = crc32Update crc32Table+  let initial = Bits.complement crc32Initial+  let crc = ByteString.foldl update initial bytes+  Bits.complement crc++crc32Update :: Vector.Vector Word.Word32+            -> Word.Word32+            -> Word.Word8+            -> Word.Word32+crc32Update table crc byte = do+  let toWord8 =+        fromIntegral :: (Integral a) =>+                          a -> Word.Word8+  let toInt =+        fromIntegral :: (Integral a) =>+                          a -> Int+  let index = toInt (Bits.xor byte (toWord8 (Bits.shiftR crc 24)))+  let left = Vector.unsafeIndex table index+  let right = Bits.shiftL crc 8+  Bits.xor left right++crc32Initial :: Word.Word32+crc32Initial = 0xefcbf201++crc32Table :: Vector.Vector Word.Word32+crc32Table = Vector.fromList rawCrc32Table
+ source/library/Rattletrap/Data.hs view
@@ -0,0 +1,351 @@+module Rattletrap.Data where++import Rattletrap.AttributeType++rawParentClasses :: [(String, String)]+rawParentClasses =+  [ ("Engine.Actor", "Core.Object")+  , ("Engine.GameReplicationInfo", "Engine.ReplicationInfo")+  , ("Engine.Info", "Engine.Actor")+  , ("Engine.Pawn", "Engine.Actor")+  , ("Engine.PlayerReplicationInfo", "Engine.ReplicationInfo")+  , ("Engine.ReplicationInfo", "Engine.Info")+  , ("Engine.TeamInfo", "Engine.ReplicationInfo")+  , ("ProjectX.GRI_X", "Engine.GameReplicationInfo")+  , ("ProjectX.PRI_X", "Engine.PlayerReplicationInfo")+  , ("ProjectX.Pawn_X", "Engine.Pawn")+  , ("TAGame.Ball_TA", "TAGame.RBActor_TA")+  , ("TAGame.CameraSettingsActor_TA", "Engine.ReplicationInfo")+  , ("TAGame.CarComponent_Boost_TA", "TAGame.CarComponent_TA")+  , ("TAGame.CarComponent_Dodge_TA", "TAGame.CarComponent_TA")+  , ("TAGame.CarComponent_DoubleJump_TA", "TAGame.CarComponent_TA")+  , ("TAGame.CarComponent_FlipCar_TA", "TAGame.CarComponent_TA")+  , ("TAGame.CarComponent_Jump_TA", "TAGame.CarComponent_TA")+  , ("TAGame.CarComponent_TA", "Engine.ReplicationInfo")+  , ("TAGame.Car_Season_TA", "TAGame.PRI_TA")+  , ("TAGame.Car_TA", "TAGame.Vehicle_TA")+  , ("TAGame.CrowdActor_TA", "Engine.ReplicationInfo")+  , ("TAGame.CrowdManager_TA", "Engine.ReplicationInfo")+  , ("TAGame.GRI_TA", "ProjectX.GRI_X")+  , ("TAGame.GameEvent_Season_TA", "TAGame.GameEvent_Soccar_TA")+  , ("TAGame.GameEvent_SoccarPrivate_TA", "TAGame.GameEvent_Soccar_TA")+  , ("TAGame.GameEvent_SoccarSplitscreen_TA", "TAGame.GameEvent_SoccarPrivate_TA")+  , ("TAGame.GameEvent_Soccar_TA", "TAGame.GameEvent_Team_TA")+  , ("TAGame.GameEvent_TA", "Engine.ReplicationInfo")+  , ("TAGame.GameEvent_Team_TA", "TAGame.GameEvent_TA")+  , ("TAGame.InMapScoreboard_TA", "Engine.Actor")+  , ("TAGame.PRI_TA", "ProjectX.PRI_X")+  , ("TAGame.RBActor_TA", "ProjectX.Pawn_X")+  , ("TAGame.SpecialPickup_BallCarSpring_TA", "TAGame.SpecialPickup_Spring_TA")+  , ("TAGame.SpecialPickup_BallFreeze_TA", "TAGame.SpecialPickup_Targeted_TA")+  , ("TAGame.SpecialPickup_BallGravity_TA", "TAGame.SpecialPickup_TA")+  , ("TAGame.SpecialPickup_BallLasso_TA", "TAGame.SpecialPickup_GrapplingHook_TA")+  , ("TAGame.SpecialPickup_BallVelcro_TA", "TAGame.SpecialPickup_TA")+  , ("TAGame.SpecialPickup_Batarang_TA", "TAGame.SpecialPickup_BallLasso_TA")+  , ("TAGame.SpecialPickup_BoostOverride_TA", "TAGame.SpecialPickup_Targeted_TA")+  , ("TAGame.SpecialPickup_GrapplingHook_TA", "TAGame.SpecialPickup_Targeted_TA")+  , ("TAGame.SpecialPickup_HitForce_TA", "TAGame.SpecialPickup_TA")+  , ("TAGame.SpecialPickup_Spring_TA", "TAGame.SpecialPickup_Targeted_TA")+  , ("TAGame.SpecialPickup_Swapper_TA", "TAGame.SpecialPickup_Targeted_TA")+  , ("TAGame.SpecialPickup_TA", "TAGame.CarComponent_TA")+  , ("TAGame.SpecialPickup_Targeted_TA", "TAGame.SpecialPickup_TA")+  , ("TAGame.SpecialPickup_Tornado_TA", "TAGame.SpecialPickup_TA")+  , ("TAGame.Team_Soccar_TA", "TAGame.Team_TA")+  , ("TAGame.Team_TA", "Engine.TeamInfo")+  , ("TAGame.VehiclePickup_Boost_TA", "TAGame.VehiclePickup_TA")+  , ("TAGame.VehiclePickup_TA", "Engine.ReplicationInfo")+  , ("TAGame.Vehicle_TA", "TAGame.RBActor_TA")+  ]++rawClassesWithLocation :: [String]+rawClassesWithLocation =+  [ "TAGame.Ball_TA"+  , "TAGame.CameraSettingsActor_TA"+  , "TAGame.CarComponent_Boost_TA"+  , "TAGame.CarComponent_Dodge_TA"+  , "TAGame.CarComponent_DoubleJump_TA"+  , "TAGame.CarComponent_FlipCar_TA"+  , "TAGame.CarComponent_Jump_TA"+  , "TAGame.Car_Season_TA"+  , "TAGame.Car_TA"+  , "TAGame.GRI_TA"+  , "TAGame.GameEvent_Season_TA"+  , "TAGame.GameEvent_SoccarPrivate_TA"+  , "TAGame.GameEvent_SoccarSplitscreen_TA"+  , "TAGame.GameEvent_Soccar_TA"+  , "TAGame.PRI_TA"+  , "TAGame.SpecialPickup_BallCarSpring_TA"+  , "TAGame.SpecialPickup_BallFreeze_TA"+  , "TAGame.SpecialPickup_BallGravity_TA"+  , "TAGame.SpecialPickup_BallGravity_TA"+  , "TAGame.SpecialPickup_BallLasso_TA"+  , "TAGame.SpecialPickup_BallVelcro_TA"+  , "TAGame.SpecialPickup_Batarang_TA"+  , "TAGame.SpecialPickup_BoostOverride_TA"+  , "TAGame.SpecialPickup_GrapplingHook_TA"+  , "TAGame.SpecialPickup_HitForce_TA"+  , "TAGame.SpecialPickup_Swapper_TA"+  , "TAGame.SpecialPickup_Tornado_TA"+  , "TAGame.Team_Soccar_TA"+  ]++rawClassesWithRotation :: [String]+rawClassesWithRotation =+  [ "TAGame.Ball_TA"+  , "TAGame.Car_Season_TA"+  , "TAGame.Car_TA"+  ]++rawObjectClasses :: [(String, String)]+rawObjectClasses =+  [ ("Archetypes.Ball.Ball_Basketball", "TAGame.Ball_TA")+  , ("Archetypes.Ball.Ball_Default", "TAGame.Ball_TA")+  , ("Archetypes.Ball.Ball_Puck", "TAGame.Ball_TA")+  , ("Archetypes.Ball.CubeBall", "TAGame.Ball_TA")+  , ("Archetypes.Car.Car_Default", "TAGame.Car_TA")+  , ("Archetypes.CarComponents.CarComponent_Boost", "TAGame.CarComponent_Boost_TA")+  , ("Archetypes.CarComponents.CarComponent_Dodge", "TAGame.CarComponent_Dodge_TA")+  , ("Archetypes.CarComponents.CarComponent_DoubleJump", "TAGame.CarComponent_DoubleJump_TA")+  , ("Archetypes.CarComponents.CarComponent_FlipCar", "TAGame.CarComponent_FlipCar_TA")+  , ("Archetypes.CarComponents.CarComponent_Jump", "TAGame.CarComponent_Jump_TA")+  , ("Archetypes.GameEvent.GameEvent_Basketball", "TAGame.GameEvent_Soccar_TA")+  , ("Archetypes.GameEvent.GameEvent_BasketballPrivate", "TAGame.GameEvent_SoccarPrivate_TA")+  , ("Archetypes.GameEvent.GameEvent_BasketballSplitscreen", "TAGame.GameEvent_SoccarSplitscreen_TA")+  , ("Archetypes.GameEvent.GameEvent_Hockey", "TAGame.GameEvent_Soccar_TA")+  , ("Archetypes.GameEvent.GameEvent_HockeyPrivate", "TAGame.GameEvent_SoccarPrivate_TA")+  , ("Archetypes.GameEvent.GameEvent_HockeySplitscreen", "TAGame.GameEvent_SoccarSplitscreen_TA")+  , ("Archetypes.GameEvent.GameEvent_Items", "TAGame.GameEvent_Soccar_TA")+  , ("Archetypes.GameEvent.GameEvent_Season", "TAGame.GameEvent_Season_TA")+  , ("Archetypes.GameEvent.GameEvent_Season:CarArchetype", "TAGame.Car_TA")+  , ("Archetypes.GameEvent.GameEvent_Soccar", "TAGame.GameEvent_Soccar_TA")+  , ("Archetypes.GameEvent.GameEvent_SoccarPrivate", "TAGame.GameEvent_SoccarPrivate_TA")+  , ("Archetypes.GameEvent.GameEvent_SoccarSplitscreen", "TAGame.GameEvent_SoccarSplitscreen_TA")+  , ("Archetypes.SpecialPickups.SpecialPickup_BallFreeze", "TAGame.SpecialPickup_BallFreeze_TA")+  , ("Archetypes.SpecialPickups.SpecialPickup_BallGrapplingHook", "TAGame.SpecialPickup_GrapplingHook_TA")+  , ("Archetypes.SpecialPickups.SpecialPickup_BallLasso", "TAGame.SpecialPickup_BallLasso_TA")+  , ("Archetypes.SpecialPickups.SpecialPickup_BallSpring", "TAGame.SpecialPickup_BallCarSpring_TA")+  , ("Archetypes.SpecialPickups.SpecialPickup_BallVelcro", "TAGame.SpecialPickup_BallVelcro_TA")+  , ("Archetypes.SpecialPickups.SpecialPickup_Batarang", "TAGame.SpecialPickup_Batarang_TA")+  , ("Archetypes.SpecialPickups.SpecialPickup_BoostOverride", "TAGame.SpecialPickup_BoostOverride_TA")+  , ("Archetypes.SpecialPickups.SpecialPickup_CarSpring", "TAGame.SpecialPickup_BallCarSpring_TA")+  , ("Archetypes.SpecialPickups.SpecialPickup_GravityWell", "TAGame.SpecialPickup_BallGravity_TA")+  , ("Archetypes.SpecialPickups.SpecialPickup_StrongHit", "TAGame.SpecialPickup_HitForce_TA")+  , ("Archetypes.SpecialPickups.SpecialPickup_Swapper", "TAGame.SpecialPickup_Swapper_TA")+  , ("Archetypes.SpecialPickups.SpecialPickup_Tornado", "TAGame.SpecialPickup_Tornado_TA")+  , ("Archetypes.Teams.Team0", "TAGame.Team_Soccar_TA")+  , ("Archetypes.Teams.Team1", "TAGame.Team_Soccar_TA")+  , ("GameInfo_Basketball.GameInfo.GameInfo_Basketball:GameReplicationInfoArchetype", "TAGame.GRI_TA")+  , ("GameInfo_Items.GameInfo.GameInfo_Items:GameReplicationInfoArchetype", "TAGame.GRI_TA")+  , ("GameInfo_Season.GameInfo.GameInfo_Season:GameReplicationInfoArchetype", "TAGame.GRI_TA")+  , ("GameInfo_Soccar.GameInfo.GameInfo_Soccar:GameReplicationInfoArchetype", "TAGame.GRI_TA")+  , ("Gameinfo_Hockey.GameInfo.Gameinfo_Hockey:GameReplicationInfoArchetype", "TAGame.GRI_TA")+  , ("TAGame.Default__CameraSettingsActor_TA", "TAGame.CameraSettingsActor_TA")+  , ("TAGame.Default__PRI_TA", "TAGame.PRI_TA")+  , ("TheWorld:PersistentLevel.CrowdActor_TA", "TAGame.CrowdActor_TA")+  , ("TheWorld:PersistentLevel.CrowdManager_TA", "TAGame.CrowdManager_TA")+  , ("TheWorld:PersistentLevel.InMapScoreboard_TA", "TAGame.InMapScoreboard_TA")+  , ("TheWorld:PersistentLevel.VehiclePickup_Boost_TA", "TAGame.VehiclePickup_Boost_TA")+  ]++rawAttributeTypes :: [(String, AttributeType)]+rawAttributeTypes =+  [ ("Engine.Actor:DrawScale", FloatAttributeType)+  , ("Engine.Actor:Role", EnumAttributeType)+  , ("Engine.Actor:bBlockActors", BooleanAttributeType)+  , ("Engine.Actor:bCollideActors", BooleanAttributeType)+  , ("Engine.Actor:bHidden", BooleanAttributeType)+  , ("Engine.GameReplicationInfo:GameClass", FlaggedIntAttributeType)+  , ("Engine.GameReplicationInfo:ServerName", StringAttributeType)+  , ("Engine.GameReplicationInfo:bMatchIsOver", BooleanAttributeType)+  , ("Engine.Pawn:PlayerReplicationInfo", FlaggedIntAttributeType)+  , ("Engine.PlayerReplicationInfo:Ping", ByteAttributeType)+  , ("Engine.PlayerReplicationInfo:PlayerID", IntAttributeType)+  , ("Engine.PlayerReplicationInfo:PlayerName", StringAttributeType)+  , ("Engine.PlayerReplicationInfo:RemoteUserData", StringAttributeType)+  , ("Engine.PlayerReplicationInfo:Score", IntAttributeType)+  , ("Engine.PlayerReplicationInfo:Team", FlaggedIntAttributeType)+  , ("Engine.PlayerReplicationInfo:UniqueId", UniqueIdAttributeType)+  , ("Engine.PlayerReplicationInfo:bBot", BooleanAttributeType)+  , ("Engine.PlayerReplicationInfo:bIsSpectator", BooleanAttributeType)+  , ("Engine.PlayerReplicationInfo:bReadyToPlay", BooleanAttributeType)+  , ("Engine.PlayerReplicationInfo:bWaitingPlayer", BooleanAttributeType)+  , ("Engine.TeamInfo:Score", IntAttributeType)+  , ("ProjectX.GRI_X:GameServerID", QWordAttributeType)+  , ("ProjectX.GRI_X:ReplicatedGameMutatorIndex", IntAttributeType)+  , ("ProjectX.GRI_X:ReplicatedGamePlaylist", IntAttributeType)+  , ("ProjectX.GRI_X:Reservations", ReservationAttributeType)+  , ("ProjectX.GRI_X:bGameStarted", BooleanAttributeType)+  , ("TAGame.Ball_TA:GameEvent", FlaggedIntAttributeType)+  , ("TAGame.Ball_TA:HitTeamNum", ByteAttributeType)+  , ("TAGame.Ball_TA:ReplicatedAddedCarBounceScale", FloatAttributeType)+  , ("TAGame.Ball_TA:ReplicatedBallMaxLinearSpeedScale", FloatAttributeType)+  , ("TAGame.Ball_TA:ReplicatedBallScale", FloatAttributeType)+  , ("TAGame.Ball_TA:ReplicatedExplosionData", ExplosionAttributeType)+  , ("TAGame.Ball_TA:ReplicatedWorldBounceScale", FloatAttributeType)+  , ("TAGame.CameraSettingsActor_TA:CameraPitch", ByteAttributeType)+  , ("TAGame.CameraSettingsActor_TA:CameraYaw", ByteAttributeType)+  , ("TAGame.CameraSettingsActor_TA:PRI", FlaggedIntAttributeType)+  , ("TAGame.CameraSettingsActor_TA:ProfileSettings", CamSettingsAttributeType)+  , ("TAGame.CameraSettingsActor_TA:bUsingBehindView", BooleanAttributeType)+  , ("TAGame.CameraSettingsActor_TA:bUsingSecondaryCamera", BooleanAttributeType)+  , ("TAGame.CarComponent_Boost_TA:BoostModifier", FloatAttributeType)+  , ("TAGame.CarComponent_Boost_TA:RechargeDelay", FloatAttributeType)+  , ("TAGame.CarComponent_Boost_TA:RechargeRate", FloatAttributeType)+  , ("TAGame.CarComponent_Boost_TA:ReplicatedBoostAmount", ByteAttributeType)+  , ("TAGame.CarComponent_Boost_TA:UnlimitedBoostRefCount", IntAttributeType)+  , ("TAGame.CarComponent_Boost_TA:bNoBoost", BooleanAttributeType)+  , ("TAGame.CarComponent_Boost_TA:bUnlimitedBoost", BooleanAttributeType)+  , ("TAGame.CarComponent_Dodge_TA:DodgeTorque", LocationAttributeType)+  , ("TAGame.CarComponent_FlipCar_TA:FlipCarTime", FloatAttributeType)+  , ("TAGame.CarComponent_FlipCar_TA:bFlipRight", BooleanAttributeType)+  , ("TAGame.CarComponent_TA:ReplicatedActive", ByteAttributeType)+  , ("TAGame.CarComponent_TA:ReplicatedActivityTime", FloatAttributeType)+  , ("TAGame.CarComponent_TA:Vehicle", FlaggedIntAttributeType)+  , ("TAGame.Car_TA:AddedBallForceMultiplier", FloatAttributeType)+  , ("TAGame.Car_TA:AddedCarForceMultiplier", FloatAttributeType)+  , ("TAGame.Car_TA:AttachedPickup", FlaggedIntAttributeType)+  , ("TAGame.Car_TA:ClubColors", ClubColorsAttributeType)+  , ("TAGame.Car_TA:ReplicatedDemolish", DemolishAttributeType)+  , ("TAGame.Car_TA:TeamPaint", TeamPaintAttributeType)+  , ("TAGame.CrowdActor_TA:GameEvent", FlaggedIntAttributeType)+  , ("TAGame.CrowdActor_TA:ModifiedNoise", FloatAttributeType)+  , ("TAGame.CrowdActor_TA:ReplicatedCountDownNumber", IntAttributeType)+  , ("TAGame.CrowdActor_TA:ReplicatedOneShotSound", FlaggedIntAttributeType)+  , ("TAGame.CrowdActor_TA:ReplicatedRoundCountDownNumber", IntAttributeType)+  , ("TAGame.CrowdManager_TA:GameEvent", FlaggedIntAttributeType)+  , ("TAGame.CrowdManager_TA:ReplicatedGlobalOneShotSound", FlaggedIntAttributeType)+  , ("TAGame.GRI_TA:NewDedicatedServerIP", StringAttributeType)+  , ("TAGame.GameEvent_SoccarPrivate_TA:MatchSettings", PrivateMatchSettingsAttributeType)+  , ("TAGame.GameEvent_Soccar_TA:GameTime", IntAttributeType)+  , ("TAGame.GameEvent_Soccar_TA:ReplicatedMusicStinger", MusicStingerAttributeType)+  , ("TAGame.GameEvent_Soccar_TA:ReplicatedScoredOnTeam", ByteAttributeType)+  , ("TAGame.GameEvent_Soccar_TA:RoundNum", IntAttributeType)+  , ("TAGame.GameEvent_Soccar_TA:SecondsRemaining", IntAttributeType)+  , ("TAGame.GameEvent_Soccar_TA:SubRulesArchetype", FlaggedIntAttributeType)+  , ("TAGame.GameEvent_Soccar_TA:bBallHasBeenHit", BooleanAttributeType)+  , ("TAGame.GameEvent_Soccar_TA:bOverTime", BooleanAttributeType)+  , ("TAGame.GameEvent_TA:BotSkill", IntAttributeType)+  , ("TAGame.GameEvent_TA:GameMode", GameModeAttributeType)+  , ("TAGame.GameEvent_TA:MatchTypeClass", FlaggedIntAttributeType)+  , ("TAGame.GameEvent_TA:ReplicatedGameStateTimeRemaining", IntAttributeType)+  , ("TAGame.GameEvent_TA:ReplicatedStateIndex", ByteAttributeType)+  , ("TAGame.GameEvent_TA:ReplicatedStateName", IntAttributeType)+  , ("TAGame.GameEvent_TA:bCanVoteToForfeit", BooleanAttributeType)+  , ("TAGame.GameEvent_TA:bHasLeaveMatchPenalty", BooleanAttributeType)+  , ("TAGame.GameEvent_Team_TA:bForfeit", BooleanAttributeType)+  , ("TAGame.GameEvent_Team_TA:MaxTeamSize", IntAttributeType)+  , ("TAGame.PRI_TA:CameraPitch", ByteAttributeType)+  , ("TAGame.PRI_TA:CameraSettings", CamSettingsAttributeType)+  , ("TAGame.PRI_TA:CameraYaw", ByteAttributeType)+  , ("TAGame.PRI_TA:ClientLoadout", LoadoutAttributeType)+  , ("TAGame.PRI_TA:ClientLoadoutOnline", LoadoutOnlineAttributeType)+  , ("TAGame.PRI_TA:ClientLoadouts", LoadoutsAttributeType)+  , ("TAGame.PRI_TA:ClientLoadoutsOnline", LoadoutsOnlineAttributeType)+  , ("TAGame.PRI_TA:MatchAssists", IntAttributeType)+  , ("TAGame.PRI_TA:MatchGoals", IntAttributeType)+  , ("TAGame.PRI_TA:MatchSaves", IntAttributeType)+  , ("TAGame.PRI_TA:MatchScore", IntAttributeType)+  , ("TAGame.PRI_TA:MatchShots", IntAttributeType)+  , ("TAGame.PRI_TA:PartyLeader", PartyLeaderAttributeType)+  , ("TAGame.PRI_TA:PawnType", ByteAttributeType)+  , ("TAGame.PRI_TA:PersistentCamera", FlaggedIntAttributeType)+  , ("TAGame.PRI_TA:ReplicatedGameEvent", FlaggedIntAttributeType)+  , ("TAGame.PRI_TA:Title", IntAttributeType)+  , ("TAGame.PRI_TA:TotalXP", IntAttributeType)+  , ("TAGame.PRI_TA:bIsInSplitScreen", BooleanAttributeType)+  , ("TAGame.PRI_TA:bMatchMVP", BooleanAttributeType)+  , ("TAGame.PRI_TA:bOnlineLoadoutSet", BooleanAttributeType)+  , ("TAGame.PRI_TA:bOnlineLoadoutsSet", BooleanAttributeType)+  , ("TAGame.PRI_TA:bReady", BooleanAttributeType)+  , ("TAGame.PRI_TA:bUsingBehindView", BooleanAttributeType)+  , ("TAGame.PRI_TA:bUsingSecondaryCamera", BooleanAttributeType)+  , ("TAGame.RBActor_TA:ReplicatedRBState", RigidBodyStateAttributeType)+  , ("TAGame.RBActor_TA:WeldedInfo", WeldedInfoAttributeType)+  , ("TAGame.RBActor_TA:bFrozen", BooleanAttributeType)+  , ("TAGame.RBActor_TA:bIgnoreSyncing", BooleanAttributeType)+  , ("TAGame.RBActor_TA:bReplayActor", BooleanAttributeType)+  , ("TAGame.SpecialPickup_BallFreeze_TA:RepOrigSpeed", FloatAttributeType)+  , ("TAGame.SpecialPickup_BallVelcro_TA:AttachTime", FloatAttributeType)+  , ("TAGame.SpecialPickup_BallVelcro_TA:BreakTime", FloatAttributeType)+  , ("TAGame.SpecialPickup_BallVelcro_TA:bBroken", BooleanAttributeType)+  , ("TAGame.SpecialPickup_BallVelcro_TA:bHit", BooleanAttributeType)+  , ("TAGame.SpecialPickup_Targeted_TA:Targeted", FlaggedIntAttributeType)+  , ("TAGame.Team_Soccar_TA:GameScore", IntAttributeType)+  , ("TAGame.Team_TA:ClubColors", ClubColorsAttributeType)+  , ("TAGame.Team_TA:CustomTeamName", StringAttributeType)+  , ("TAGame.Team_TA:GameEvent", FlaggedIntAttributeType)+  , ("TAGame.Team_TA:LogoData", FlaggedIntAttributeType)+  , ("TAGame.VehiclePickup_TA:ReplicatedPickupData", PickupAttributeType)+  , ("TAGame.Vehicle_TA:ReplicatedSteer", ByteAttributeType)+  , ("TAGame.Vehicle_TA:ReplicatedThrottle", ByteAttributeType)+  , ("TAGame.Vehicle_TA:bDriving", BooleanAttributeType)+  , ("TAGame.Vehicle_TA:bReplicatedHandbrake", BooleanAttributeType)+  ]++rawCrc32Table :: Integral a => [a]+rawCrc32Table =+  [ 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9+  , 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005+  , 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61+  , 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd+  , 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9+  , 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75+  , 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011+  , 0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd+  , 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039+  , 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5+  , 0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81+  , 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d+  , 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49+  , 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95+  , 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1+  , 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d+  , 0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae+  , 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072+  , 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16+  , 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca+  , 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde+  , 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02+  , 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066+  , 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba+  , 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e+  , 0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692+  , 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6+  , 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a+  , 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e+  , 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2+  , 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686+  , 0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a+  , 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637+  , 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb+  , 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f+  , 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53+  , 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47+  , 0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b+  , 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff+  , 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623+  , 0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7+  , 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b+  , 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f+  , 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3+  , 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7+  , 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b+  , 0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f+  , 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3+  , 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640+  , 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c+  , 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8+  , 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24+  , 0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30+  , 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec+  , 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088+  , 0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654+  , 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0+  , 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c+  , 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18+  , 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4+  , 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0+  , 0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c+  , 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668+  , 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4+  ]
+ source/library/Rattletrap/Frame.hs view
@@ -0,0 +1,62 @@+module Rattletrap.Frame where++import Rattletrap.ActorMap+import Rattletrap.ClassAttributeMap+import Rattletrap.Primitive+import Rattletrap.Replication++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++data Frame = Frame+  { frameTime :: Float32+  -- ^ Time in seconds since the beginning of the match.+  , frameDelta :: Float32+  -- ^ Time in seconds since the last frame. Usually about 0.03 since there+  -- are 30 frames per second.+  , frameReplications :: [Replication]+  } deriving (Eq, Ord, Show)++getFrames+  :: (Int, Int)+  -> Int+  -> Word+  -> ClassAttributeMap+  -> ActorMap+  -> BinaryBit.BitGet ([Frame], ActorMap)+getFrames version numFrames maxChannels classAttributeMap actorMap =+  if numFrames <= 0+    then pure ([], actorMap)+    else do+      (frame, newActorMap) <-+        getFrame version maxChannels classAttributeMap actorMap+      (frames, newerActorMap) <-+        getFrames+          version+          (numFrames - 1)+          maxChannels+          classAttributeMap+          newActorMap+      pure (frame : frames, newerActorMap)++putFrames :: [Frame] -> BinaryBit.BitPut ()+putFrames = mapM_ putFrame++getFrame+  :: (Int, Int)+  -> Word+  -> ClassAttributeMap+  -> ActorMap+  -> BinaryBit.BitGet (Frame, ActorMap)+getFrame version maxChannels classAttributeMap actorMap = do+  time <- getFloat32Bits+  delta <- getFloat32Bits+  (replications, newActorMap) <-+    getReplications version maxChannels classAttributeMap actorMap+  pure (Frame time delta replications, newActorMap)++putFrame :: Frame -> BinaryBit.BitPut ()+putFrame frame = do+  putFloat32Bits (frameTime frame)+  putFloat32Bits (frameDelta frame)+  putReplications (frameReplications frame)
+ source/library/Rattletrap/Header.hs view
@@ -0,0 +1,100 @@+module Rattletrap.Header where++import Rattletrap.Primitive+import Rattletrap.Property+import Rattletrap.PropertyValue++import qualified Data.Binary as Binary+import qualified Data.Map as Map++-- | Contains high-level metadata about a 'Rattletrap.Replay.Replay'.+data Header = Header+  { headerEngineVersion :: Word32+  -- ^ The "major" version number.+  , headerLicenseeVersion :: Word32+  -- ^ The "minor" version number.+  , headerLabel :: Text+  -- ^ Always @TAGame.Replay_Soccar_TA@.+  , headerProperties :: Dictionary Property+  -- ^ These properties determine how a replay will look in the list of+  -- replays in-game. One element is required for the replay to show up:+  --+  -- - MapName: This is a 'Rattletrap.PropertyValue.NameProperty' with a+  --   case-insensitive map identifier, like @Stadium_P@.+  --+  -- There are many other properties that affect how the replay looks in the+  -- list of replays.+  --+  -- - Date: A 'Rattletrap.PropertyValue.StrProperty' with the format+  --   @YYYY-mm-dd:HH-MM@. Dates are not validated, but the month must be+  --   between 1 and 12 to show up. The hour is shown modulo 12 with AM or PM.+  -- - MatchType: A 'Rattletrap.PropertyValue.NameProperty'. If this is not+  --   one of the expected values, nothing will be shown next to the replay's+  --   map. The expected values are: @Online@, @Offline@, @Private@, and+  --   @Season@.+  -- - NumFrames: This 'Rattletrap.PropertyValue.IntProperty' is used to+  --   calculate the length of the match. There are 30 frames per second,+  --   a typical 5-minute match has about 9,000 frames.+  -- - PrimaryPlayerTeam: This is an 'Rattletrap.PropertyValue.IntProperty'.+  --   It is either 0 (blue) or 1 (orange). Any other value is ignored. If+  --   this would be 0, you don't have to set it at all.+  -- - ReplayName: An optional 'Rattletrap.PropertyValue.StrProperty' with a+  --   user-supplied name for the replay.+  -- - Team0Score: The blue team's score as an+  --   'Rattletrap.PropertyValue.IntProperty'. Can be omitted if the score is+  --   0.+  -- - Team1Score: The orange team's score as an+  --   'Rattletrap.PropertyValue.IntProperty'. Can also be omitted if the+  --   score is 0.+  -- - TeamSize: An 'Rattletrap.PropertyValue.IntProperty' with the number of+  --   players per team. This value is not validated, so you can put absurd+  --   values like 99. To get an "unfair" team size like 1v4, you must set the+  --   bUnfairBots 'Rattletrap.PropertyValue.BoolProperty' to @True@.+  } deriving (Eq, Ord, Show)++getHeader :: Binary.Get Header+getHeader = do+  engineVersion <- getWord32+  licenseeVersion <- getWord32+  label <- getText+  properties <- getDictionary getProperty+  pure (Header engineVersion licenseeVersion label properties)++putHeader :: Header -> Binary.Put+putHeader header = do+  putWord32 (headerEngineVersion header)+  putWord32 (headerLicenseeVersion header)+  putText (headerLabel header)+  putDictionary putProperty (headerProperties header)++getVersion :: Header -> (Int, Int)+getVersion header =+  let major = getMajorVersion header+      minor = getMinorVersion header+  in (major, minor)++getMajorVersion :: Header -> Int+getMajorVersion header =+  fromIntegral (word32Value (headerEngineVersion header))++getMinorVersion :: Header -> Int+getMinorVersion header =+  fromIntegral (word32Value (headerLicenseeVersion header))++getNumFrames :: Header -> Int+getNumFrames header =+  let key = textValue (stringToText "NumFrames")+      properties = dictionaryValue (headerProperties header)+  in case Map.lookup key properties of+       Just (Property _ _ (IntProperty numFrames)) ->+         fromIntegral (int32Value numFrames)+       _ -> 0++getMaxChannels :: Header -> Word+getMaxChannels header =+  let key = textValue (stringToText "MaxChannels")+      properties = dictionaryValue (headerProperties header)+  in case Map.lookup key properties of+       Just (Property _ _ (IntProperty numFrames)) ->+         fromIntegral (int32Value numFrames)+       _ -> 1023
+ source/library/Rattletrap/Initialization.hs view
@@ -0,0 +1,40 @@+module Rattletrap.Initialization where++import Rattletrap.Primitive++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++data Initialization = Initialization+  { initializationLocation :: Maybe Vector+  -- ^ Not every class has an initial location. See+  -- 'Rattletrap.Data.rawClassesWithLocation'.+  , initializationRotation :: Maybe Int8Vector+  -- ^ Only classes with location can have rotation, but not every one does.+  -- See 'Rattletrap.Data.rawClassesWithRotation'.+  } deriving (Eq, Ord, Show)++getInitialization :: Bool -> Bool -> BinaryBit.BitGet Initialization+getInitialization hasLocation hasRotation = do+  location <-+    if hasLocation+      then do+        location <- getVector+        pure (Just location)+      else pure Nothing+  rotation <-+    if hasRotation+      then do+        rotation <- getInt8Vector+        pure (Just rotation)+      else pure Nothing+  pure (Initialization location rotation)++putInitialization :: Initialization -> BinaryBit.BitPut ()+putInitialization initialization = do+  case initializationLocation initialization of+    Nothing -> pure ()+    Just location -> putVector location+  case initializationRotation initialization of+    Nothing -> pure ()+    Just rotation -> putInt8Vector rotation
+ source/library/Rattletrap/Json.hs view
@@ -0,0 +1,105 @@+{-# LANGUAGE TemplateHaskell #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}++module Rattletrap.Json where++import Rattletrap.Attribute+import Rattletrap.AttributeMapping+import Rattletrap.AttributeValue+import Rattletrap.Cache+import Rattletrap.ClassMapping+import Rattletrap.Content+import Rattletrap.Frame+import Rattletrap.Header+import Rattletrap.Initialization+import Rattletrap.KeyFrame+import Rattletrap.Mark+import Rattletrap.Message+import Rattletrap.Primitive+import Rattletrap.Property+import Rattletrap.PropertyValue+import Rattletrap.RemoteId+import Rattletrap.Replay+import Rattletrap.Replication+import Rattletrap.ReplicationValue++import qualified Control.Monad as Monad+import qualified Data.Aeson.Casing as Casing+import qualified Data.Aeson.TH as Aeson+import qualified Language.Haskell.TH as TH++$(let optionsFor name =+        (Casing.aesonDrop (length (TH.nameBase name)) Casing.snakeCase)+        { Aeson.constructorTagModifier = Casing.snakeCase+        , Aeson.omitNothingFields = True+        , Aeson.sumEncoding = Aeson.ObjectWithSingleField+        , Aeson.unwrapUnaryRecords = True+        }+      deriveJSON declarations name = do+        newDeclarations <- Aeson.deriveJSON (optionsFor name) name+        pure (newDeclarations ++ declarations)+      names =+        [ ''Attribute+        , ''AttributeMapping+        , ''AttributeValue+        , ''BooleanAttribute+        , ''ByteAttribute+        , ''Cache+        , ''CamSettingsAttribute+        , ''ClassMapping+        , ''ClubColorsAttribute+        , ''CompressedWord+        , ''CompressedWordVector+        , ''Content+        , ''DemolishAttribute+        , ''DestroyedReplication+        , ''Dictionary+        , ''EnumAttribute+        , ''ExplosionAttribute+        , ''FlaggedIntAttribute+        , ''Float32+        , ''FloatAttribute+        , ''Frame+        , ''GameModeAttribute+        , ''Header+        , ''Initialization+        , ''Int32+        , ''Int8+        , ''Int8Vector+        , ''IntAttribute+        , ''KeyFrame+        , ''List+        , ''LoadoutAttribute+        , ''LoadoutOnlineAttribute+        , ''LoadoutsAttribute+        , ''LoadoutsOnlineAttribute+        , ''LocationAttribute+        , ''Mark+        , ''Message+        , ''MusicStingerAttribute+        , ''PartyLeaderAttribute+        , ''PickupAttribute+        , ''PrivateMatchSettingsAttribute+        , ''Property+        , ''PropertyValue+        , ''QWordAttribute+        , ''RemoteId+        , ''Replay+        , ''Replication+        , ''ReplicationValue+        , ''ReservationAttribute+        , ''RigidBodyStateAttribute+        , ''Section+        , ''SpawnedReplication+        , ''StringAttribute+        , ''TeamPaintAttribute+        , ''Text+        , ''UniqueIdAttribute+        , ''UpdatedReplication+        , ''Vector+        , ''WeldedInfoAttribute+        , ''Word32+        , ''Word64+        , ''Word8+        ]+  in Monad.foldM deriveJSON [] names)
+ source/library/Rattletrap/KeyFrame.hs view
@@ -0,0 +1,27 @@+module Rattletrap.KeyFrame where++import Rattletrap.Primitive++import qualified Data.Binary as Binary++data KeyFrame = KeyFrame+  { keyFrameTime :: Float32+  -- ^ When this key frame occurs, in seconds.+  , keyFrameFrame :: Word32+  -- ^ The frame number of this key frame, starting from 0.+  , keyFramePosition :: Word32+  -- ^ The bit position of this key frame in the stream.+  } deriving (Eq, Ord, Show)++getKeyFrame :: Binary.Get KeyFrame+getKeyFrame = do+  time <- getFloat32+  frame <- getWord32+  position <- getWord32+  pure (KeyFrame time frame position)++putKeyFrame :: KeyFrame -> Binary.Put+putKeyFrame keyFrame = do+  putFloat32 (keyFrameTime keyFrame)+  putWord32 (keyFrameFrame keyFrame)+  putWord32 (keyFramePosition keyFrame)
+ source/library/Rattletrap/Main.hs view
@@ -0,0 +1,58 @@+module Rattletrap.Main where++import Rattletrap.Json ()+import Rattletrap.Replay+import Rattletrap.Version++import qualified Data.Aeson as Aeson+import qualified Data.Binary.Get as Binary+import qualified Data.Binary.Put as Binary+import qualified Data.ByteString.Lazy as ByteString+import qualified Data.Version as Version+import qualified System.Environment as Environment++-- | Gets command-line arguments and passes them to 'mainWithArgs'.+main :: IO ()+main = do+  args <- Environment.getArgs+  mainWithArgs args++-- | Runs the main command-line interface. Can be used in one of three modes:+--+-- 1. @mainWithArgs ["version"]@: Prints out the version number and exits.+--    Mostly useful for debugging.+-- 2. @mainWithArgs ["decode"]@: Parses a raw replay into JSON. By default+--    this reads from STDIN and writes to STDOUT. To read from a file, pass a+--    second argument like @mainWithArgs ["decode", "input.replay"]@. To write+--    to a file, pass a third argument like+--    @mainWithArgs ["decode", "input.replay", "output.json"]@.+-- 3. @mainWithArgs ["encode"]@: Generates a raw replay from JSON. The+--    handling of input and output is the same as decoding.+mainWithArgs :: [String] -> IO ()+mainWithArgs args =+  case args of+    ["version"] -> putStrLn (Version.showVersion version)+    "decode":files -> do+      (getInput, putOutput) <- getIO files+      input <- getInput+      let replay = Binary.runGet getReplay input+      let output = Aeson.encode replay+      putOutput output+    "encode":files -> do+      (getInput, putOutput) <- getIO files+      input <- getInput+      case Aeson.eitherDecode input of+        Left message -> fail ("could not parse JSON: " ++ message)+        Right replay -> do+          let output = Binary.runPut (putReplay replay)+          putOutput output+    _ -> fail ("unexpected arguments " ++ show args)++getIO :: [FilePath]+      -> IO (IO ByteString.ByteString, ByteString.ByteString -> IO ())+getIO files =+  case files of+    [] -> pure (ByteString.getContents, ByteString.putStr)+    [i] -> pure (ByteString.readFile i, ByteString.putStr)+    [i, o] -> pure (ByteString.readFile i, ByteString.writeFile o)+    _ -> fail ("unexpected arguments " ++ show files)
+ source/library/Rattletrap/Mark.hs view
@@ -0,0 +1,23 @@+module Rattletrap.Mark where++import Rattletrap.Primitive++import qualified Data.Binary as Binary++data Mark = Mark+  { markValue :: Text+  -- ^ Which type of mark this is, like @Team0Goal@.+  , markFrame :: Word32+  -- ^ Which frame this mark belongs to, starting from 0.+  } deriving (Eq, Ord, Show)++getMark :: Binary.Get Mark+getMark = do+  value <- getText+  frame <- getWord32+  pure (Mark value frame)++putMark :: Mark -> Binary.Put+putMark mark = do+  putText (markValue mark)+  putWord32 (markFrame mark)
+ source/library/Rattletrap/Message.hs view
@@ -0,0 +1,27 @@+module Rattletrap.Message where++import Rattletrap.Primitive++import qualified Data.Binary as Binary++data Message = Message+  { messageFrame :: Word32+  -- ^ Which frame this message belongs to, starting from 0.+  , messageName :: Text+  -- ^ The primary player's name.+  , messageValue :: Text+  -- ^ The content of the message.+  } deriving (Eq, Ord, Show)++getMessage :: Binary.Get Message+getMessage = do+  frame <- getWord32+  name <- getText+  value <- getText+  pure (Message frame name value)++putMessage :: Message -> Binary.Put+putMessage message = do+  putWord32 (messageFrame message)+  putText (messageName message)+  putText (messageValue message)
+ source/library/Rattletrap/Primitive.hs view
@@ -0,0 +1,31 @@+module Rattletrap.Primitive+  ( module Rattletrap.Primitive.CompressedWord+  , module Rattletrap.Primitive.CompressedWordVector+  , module Rattletrap.Primitive.Dictionary+  , module Rattletrap.Primitive.Float32+  , module Rattletrap.Primitive.Int8+  , module Rattletrap.Primitive.Int8Vector+  , module Rattletrap.Primitive.Int32+  , module Rattletrap.Primitive.List+  , module Rattletrap.Primitive.Section+  , module Rattletrap.Primitive.Text+  , module Rattletrap.Primitive.Vector+  , module Rattletrap.Primitive.Word8+  , module Rattletrap.Primitive.Word32+  , module Rattletrap.Primitive.Word64+  ) where++import Rattletrap.Primitive.CompressedWord+import Rattletrap.Primitive.CompressedWordVector+import Rattletrap.Primitive.Dictionary+import Rattletrap.Primitive.Float32+import Rattletrap.Primitive.Int32+import Rattletrap.Primitive.Int8+import Rattletrap.Primitive.Int8Vector+import Rattletrap.Primitive.List+import Rattletrap.Primitive.Section+import Rattletrap.Primitive.Text+import Rattletrap.Primitive.Vector+import Rattletrap.Primitive.Word32+import Rattletrap.Primitive.Word64+import Rattletrap.Primitive.Word8
+ source/library/Rattletrap/Primitive/CompressedWord.hs view
@@ -0,0 +1,59 @@+module Rattletrap.Primitive.CompressedWord where++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit+import qualified Data.Bits as Bits++data CompressedWord = CompressedWord+  { compressedWordLimit :: Word+  , compressedWordValue :: Word+  } deriving (Eq, Ord, Show)++getCompressedWord :: Word -> BinaryBit.BitGet CompressedWord+getCompressedWord limit = do+  value <- getCompressedWordStep limit (getMaxBits limit) 0 0+  pure (CompressedWord limit value)++putCompressedWord :: CompressedWord -> BinaryBit.BitPut ()+putCompressedWord compressedWord = do+  let limit = compressedWordLimit compressedWord+  let value = compressedWordValue compressedWord+  let maxBits = getMaxBits limit+  let go position soFar =+        if position < maxBits+          then do+            let x = Bits.shiftL 1 position+            if maxBits > 1 && position == maxBits - 1 && soFar + x > limit+              then pure ()+              else do+                let bit = Bits.testBit value position+                BinaryBit.putBool bit+                let delta =+                      if bit+                        then x+                        else 0+                go (position + 1) (soFar + delta)+          else pure ()+  go 0 0++getMaxBits+  :: (Integral a, Integral b)+  => a -> b+getMaxBits x = do+  let n = max 1 (ceiling (logBase (2 :: Double) (fromIntegral (max 1 x))))+  if x < 1024 && x == 2 ^ n+    then n + 1+    else n++getCompressedWordStep :: Word -> Word -> Word -> Word -> BinaryBit.BitGet Word+getCompressedWordStep limit maxBits position value = do+  let x = Bits.shiftL 1 (fromIntegral position)+  if position < maxBits && value + x <= limit+    then do+      bit <- BinaryBit.getBool+      let newValue =+            if bit+              then value + x+              else value+      getCompressedWordStep limit maxBits (position + 1) newValue+    else pure value
+ source/library/Rattletrap/Primitive/CompressedWordVector.hs view
@@ -0,0 +1,26 @@+module Rattletrap.Primitive.CompressedWordVector where++import Rattletrap.Primitive.CompressedWord++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++data CompressedWordVector = CompressedWordVector+  { compressedWordVectorX :: CompressedWord+  , compressedWordVectorY :: CompressedWord+  , compressedWordVectorZ :: CompressedWord+  } deriving (Eq, Ord, Show)++getCompressedWordVector :: BinaryBit.BitGet CompressedWordVector+getCompressedWordVector = do+  let limit = 65536+  x <- getCompressedWord limit+  y <- getCompressedWord limit+  z <- getCompressedWord limit+  pure (CompressedWordVector x y z)++putCompressedWordVector :: CompressedWordVector -> BinaryBit.BitPut ()+putCompressedWordVector compressedWordVector = do+  putCompressedWord (compressedWordVectorX compressedWordVector)+  putCompressedWord (compressedWordVectorY compressedWordVector)+  putCompressedWord (compressedWordVectorZ compressedWordVector)
+ source/library/Rattletrap/Primitive/Dictionary.hs view
@@ -0,0 +1,59 @@+module Rattletrap.Primitive.Dictionary where++import Rattletrap.Primitive.Text++import qualified Data.Binary as Binary+import qualified Data.Map as Map+import qualified Data.Text as Text++data Dictionary a = Dictionary+  { dictionaryKeys :: [Text]+  -- ^ Objects in JSON aren't ordered, so the order of the keys must be stored+  -- separately.+  , dictionaryLastKey :: Text+  -- ^ The last key is usually @None@ but sometimes contains extra null bytes.+  , dictionaryValue :: Map.Map Text.Text a+  -- ^ Be sure to update 'dictionaryKeys' if you add, change, or remove a key+  -- in this map.+  } deriving (Eq, Ord, Show)++getDictionary :: Binary.Get a -> Binary.Get (Dictionary a)+getDictionary getValue = do+  (elements, lastKey) <- getElements getValue+  let keys = map fst elements+  let value = Map.mapKeys textValue (Map.fromList elements)+  pure (Dictionary keys lastKey value)++getElements :: Binary.Get a -> Binary.Get ([(Text, a)], Text)+getElements getValue = do+  (key, maybeValue) <- getElement getValue+  case maybeValue of+    Nothing -> pure ([], key)+    Just value -> do+      let element = (key, value)+      (elements, lastKey) <- getElements getValue+      pure (element : elements, lastKey)++getElement :: Binary.Get a -> Binary.Get (Text, Maybe a)+getElement getValue = do+  key <- getText+  if isNoneKey key+    then pure (key, Nothing)+    else do+      value <- getValue+      pure (key, Just value)++isNoneKey :: Text -> Bool+isNoneKey text = filter (/= '\x00') (textToString text) == "None"++putDictionary :: (a -> Binary.Put) -> Dictionary a -> Binary.Put+putDictionary putValue dictionary = do+  let elements = dictionaryValue dictionary+  mapM_+    (\key -> do+       putText key+       case Map.lookup (textValue key) elements of+         Nothing -> fail ("could not find key " ++ textToString key)+         Just value -> putValue value)+    (dictionaryKeys dictionary)+  putText (dictionaryLastKey dictionary)
+ source/library/Rattletrap/Primitive/Float32.hs view
@@ -0,0 +1,33 @@+module Rattletrap.Primitive.Float32 where++import Rattletrap.Utility++import qualified Data.Binary as Binary+import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit+import qualified Data.Binary.Get as Binary+import qualified Data.Binary.IEEE754 as IEEE754+import qualified Data.Binary.Put as Binary+import qualified Data.ByteString.Lazy as ByteString++newtype Float32 = Float32+  { float32Value :: Float+  } deriving (Eq, Ord, Show)++getFloat32 :: Binary.Get Float32+getFloat32 = do+  float32 <- IEEE754.getFloat32le+  pure (Float32 float32)++putFloat32 :: Float32 -> Binary.Put+putFloat32 float32 = IEEE754.putFloat32le (float32Value float32)++getFloat32Bits :: BinaryBit.BitGet Float32+getFloat32Bits = do+  bytes <- BinaryBit.getLazyByteString 4+  pure (Binary.runGet getFloat32 (reverseBytes bytes))++putFloat32Bits :: Float32 -> BinaryBit.BitPut ()+putFloat32Bits float32 = do+  let bytes = Binary.runPut (putFloat32 float32)+  BinaryBit.putByteString (ByteString.toStrict (reverseBytes bytes))
+ source/library/Rattletrap/Primitive/Int32.hs view
@@ -0,0 +1,33 @@+module Rattletrap.Primitive.Int32 where++import Rattletrap.Utility++import qualified Data.Binary as Binary+import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit+import qualified Data.Binary.Get as Binary+import qualified Data.Binary.Put as Binary+import qualified Data.ByteString.Lazy as ByteString+import qualified Data.Int as Int++newtype Int32 = Int32+  { int32Value :: Int.Int32+  } deriving (Eq, Ord, Show)++getInt32 :: Binary.Get Int32+getInt32 = do+  int32 <- Binary.getInt32le+  pure (Int32 int32)++putInt32 :: Int32 -> Binary.Put+putInt32 int32 = Binary.putInt32le (int32Value int32)++getInt32Bits :: BinaryBit.BitGet Int32+getInt32Bits = do+  bytes <- BinaryBit.getLazyByteString 4+  pure (Binary.runGet getInt32 (reverseBytes bytes))++putInt32Bits :: Int32 -> BinaryBit.BitPut ()+putInt32Bits int32 = do+  let bytes = Binary.runPut (putInt32 int32)+  BinaryBit.putByteString (ByteString.toStrict (reverseBytes bytes))
+ source/library/Rattletrap/Primitive/Int8.hs view
@@ -0,0 +1,32 @@+module Rattletrap.Primitive.Int8 where++import Rattletrap.Utility++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit+import qualified Data.Binary.Get as Binary+import qualified Data.Binary.Put as Binary+import qualified Data.ByteString.Lazy as ByteString+import qualified Data.Int as Int++newtype Int8 = Int8+  { int8Value :: Int.Int8+  } deriving (Eq, Ord, Show)++getInt8 :: Binary.Get Int8+getInt8 = do+  int8 <- Binary.getInt8+  pure (Int8 int8)++putInt8 :: Int8 -> Binary.Put+putInt8 int8 = Binary.putInt8 (int8Value int8)++getInt8Bits :: BinaryBit.BitGet Int8+getInt8Bits = do+  bytes <- BinaryBit.getLazyByteString 1+  pure (Binary.runGet getInt8 (reverseBytes bytes))++putInt8Bits :: Int8 -> BinaryBit.BitPut ()+putInt8Bits int8 = do+  let bytes = Binary.runPut (putInt8 int8)+  BinaryBit.putByteString (ByteString.toStrict (reverseBytes bytes))
+ source/library/Rattletrap/Primitive/Int8Vector.hs view
@@ -0,0 +1,42 @@+module Rattletrap.Primitive.Int8Vector where++import Rattletrap.Primitive.Int8++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++data Int8Vector = Int8Vector+  { int8VectorX :: Maybe Int8+  , int8VectorY :: Maybe Int8+  , int8VectorZ :: Maybe Int8+  } deriving (Eq, Ord, Show)++getInt8Vector :: BinaryBit.BitGet Int8Vector+getInt8Vector = do+  x <- getInt8VectorField+  y <- getInt8VectorField+  z <- getInt8VectorField+  pure (Int8Vector x y z)++putInt8Vector :: Int8Vector -> BinaryBit.BitPut ()+putInt8Vector int8Vector = do+  putInt8VectorField (int8VectorX int8Vector)+  putInt8VectorField (int8VectorY int8Vector)+  putInt8VectorField (int8VectorZ int8Vector)++getInt8VectorField :: BinaryBit.BitGet (Maybe Int8)+getInt8VectorField = do+  hasField <- BinaryBit.getBool+  if hasField+    then do+      field <- getInt8Bits+      pure (Just field)+    else pure Nothing++putInt8VectorField :: Maybe Int8 -> BinaryBit.BitPut ()+putInt8VectorField maybeField =+  case maybeField of+    Nothing -> BinaryBit.putBool False+    Just field -> do+      BinaryBit.putBool True+      putInt8Bits field
+ source/library/Rattletrap/Primitive/List.hs view
@@ -0,0 +1,22 @@+module Rattletrap.Primitive.List where++import Rattletrap.Primitive.Word32++import qualified Control.Monad as Monad+import qualified Data.Binary as Binary++newtype List a = List+  { listValue :: [a]+  } deriving (Eq, Ord, Show)++getList :: Binary.Get a -> Binary.Get (List a)+getList getElement = do+  size <- getWord32+  elements <- Monad.replicateM (fromIntegral (word32Value size)) getElement+  pure (List elements)++putList :: (a -> Binary.Put) -> List a -> Binary.Put+putList putElement list = do+  let elements = listValue list+  putWord32 (Word32 (fromIntegral (length elements)))+  mapM_ putElement elements
+ source/library/Rattletrap/Primitive/Section.hs view
@@ -0,0 +1,55 @@+module Rattletrap.Primitive.Section where++import Rattletrap.Crc+import Rattletrap.Primitive.Word32++import qualified Control.Monad as Monad+import qualified Data.Binary as Binary+import qualified Data.Binary.Get as Binary+import qualified Data.Binary.Put as Binary+import qualified Data.ByteString.Lazy as ByteString++-- | A section is a large piece of a 'Rattletrap.Replay.Replay'. It has a+-- 32-bit size (in bytes), a 32-bit CRC (see "Rattletrap.Crc"), and then a+-- bunch of data (the body). This interface is provided so that you don't have+-- to think about the size and CRC.+newtype Section a = Section+  { sectionBody :: a+  -- ^ The actual content in the section.+  } deriving (Eq, Ord, Show)++-- | Given a way to get the 'sectionBody', gets a section. This will fail if+-- the section's CRC does not match.+--+-- @+-- let header = 'Data.Binary.Get.runGet' ('getSection' 'Rattletrap.Header.getHeader') bytes+-- @+getSection :: Binary.Get a -> Binary.Get (Section a)+getSection getBody = do+  size <- getWord32+  crc <- getWord32+  rawBody <- Binary.getLazyByteString (fromIntegral (word32Value size))+  let actualCrc = Word32 (getCrc32 rawBody)+  Monad.when (actualCrc /= crc) (fail (crcMessage actualCrc crc))+  let body = Binary.runGet getBody rawBody+  pure (Section body)++-- | Given a way to put the 'sectionBody', puts a section. This will also put+-- the size and CRC.+--+-- @+-- let bytes = 'Data.Binary.Put.runPut' ('putSection' 'Rattletrap.Content.putContent' content)+-- @+putSection :: (a -> Binary.Put) -> Section a -> Binary.Put+putSection putBody section = do+  let rawBody = Binary.runPut (putBody (sectionBody section))+  let size = ByteString.length rawBody+  let crc = getCrc32 rawBody+  putWord32 (Word32 (fromIntegral size))+  putWord32 (Word32 crc)+  Binary.putLazyByteString rawBody++crcMessage :: Word32 -> Word32 -> String+crcMessage actual expected =+  unwords+    ["actual CRC", show actual, "does not match expected CRC", show expected]
+ source/library/Rattletrap/Primitive/Text.hs view
@@ -0,0 +1,108 @@+module Rattletrap.Primitive.Text where++import Rattletrap.Primitive.Int32+import Rattletrap.Utility++import qualified Data.Binary as Binary+import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit+import qualified Data.Binary.Get as Binary+import qualified Data.Binary.Put as Binary+import qualified Data.ByteString.Lazy.Char8 as ByteString+import qualified Data.Char as Char+import qualified Data.Text as Text+import qualified Data.Text.Encoding as Encoding++newtype Text = Text+  { textValue :: Text.Text+  } deriving (Eq, Ord, Show)++getText :: Binary.Get Text+getText = do+  rawSize <- getInt32+  let decode = getTextDecoder rawSize+  let size = normalizeTextSize rawSize+  bytes <- Binary.getLazyByteString size+  let text = dropNull (decode bytes)+  pure (Text text)++putText :: Text -> Binary.Put+putText text = do+  let size = getTextSize text+  let encode = getTextEncoder size+  putInt32 size+  Binary.putLazyByteString (encode (addNull (textValue text)))++getTextBits :: BinaryBit.BitGet Text+getTextBits = do+  rawSize <- getInt32Bits+  let decode = getTextDecoder rawSize+  let size = normalizeTextSize rawSize+  bytes <- BinaryBit.getLazyByteString size+  let text = dropNull (decode (reverseBytes bytes))+  pure (Text text)++putTextBits :: Text -> BinaryBit.BitPut ()+putTextBits text = do+  let size = getTextSize text+  let encode = getTextEncoder size+  putInt32Bits size+  BinaryBit.putByteString+    (ByteString.toStrict (reverseBytes (encode (addNull (textValue text)))))++stringToText :: String -> Text+stringToText string = Text (Text.pack string)++textToString :: Text -> String+textToString text = Text.unpack (textValue text)++getTextSize :: Text -> Int32+getTextSize text =+  let value = textValue text+      scale =+        if Text.all Char.isLatin1 value+          then 1+          else -1+      rawSize =+        if Text.null value+          then 0+          else fromIntegral (Text.length value) + 1+      size =+        if value == Text.pack "\x00\x00\x00None"+          then 0x05000000+          else scale * rawSize+  in Int32 size++normalizeTextSize+  :: Integral a+  => Int32 -> a+normalizeTextSize size =+  case int32Value size of+    0x05000000 -> 8+    x ->+      if x < 0+        then (-2 * fromIntegral x)+        else fromIntegral x++getTextDecoder :: Int32 -> ByteString.ByteString -> Text.Text+getTextDecoder size bytes =+  let decode =+        if size < Int32 0+          then Encoding.decodeUtf16LE+          else Encoding.decodeLatin1+  in decode (ByteString.toStrict bytes)++getTextEncoder :: Int32 -> Text.Text -> ByteString.ByteString+getTextEncoder size text =+  if size < Int32 0+    then ByteString.fromStrict (Encoding.encodeUtf16LE text)+    else encodeLatin1 text++dropNull :: Text.Text -> Text.Text+dropNull text = Text.dropWhileEnd (== '\x00') text++addNull :: Text.Text -> Text.Text+addNull text =+  if Text.null text+    then text+    else Text.snoc text '\x00'
+ source/library/Rattletrap/Primitive/Vector.hs view
@@ -0,0 +1,40 @@+module Rattletrap.Primitive.Vector where++import Rattletrap.Primitive.CompressedWord++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++data Vector = Vector+  { vectorBias :: Word+  , vectorX :: Int+  , vectorY :: Int+  , vectorZ :: Int+  } deriving (Eq, Ord, Show)++getVector :: BinaryBit.BitGet Vector+getVector = do+  bitSize <- getCompressedWord 19+  let limit = 2 ^ (compressedWordValue bitSize + 2)+  dx <- getCompressedWord limit+  dy <- getCompressedWord limit+  dz <- getCompressedWord limit+  let fromCompressedWord x = fromIntegral (compressedWordValue x)+  let bias = 2 ^ (fromCompressedWord bitSize + 1 :: Word)+  let x = fromCompressedWord dx - fromIntegral bias+  let y = fromCompressedWord dy - fromIntegral bias+  let z = fromCompressedWord dz - fromIntegral bias+  pure (Vector bias x y z)++putVector :: Vector -> BinaryBit.BitPut ()+putVector vector = do+  let bitSize =+        round (logBase (2 :: Float) (fromIntegral (vectorBias vector))) - 1+  putCompressedWord (CompressedWord 19 bitSize)+  let dx = fromIntegral (vectorX vector + fromIntegral (vectorBias vector))+  let dy = fromIntegral (vectorY vector + fromIntegral (vectorBias vector))+  let dz = fromIntegral (vectorZ vector + fromIntegral (vectorBias vector))+  let limit = 2 ^ (bitSize + 2)+  putCompressedWord (CompressedWord limit dx)+  putCompressedWord (CompressedWord limit dy)+  putCompressedWord (CompressedWord limit dz)
+ source/library/Rattletrap/Primitive/Word32.hs view
@@ -0,0 +1,33 @@+module Rattletrap.Primitive.Word32 where++import Rattletrap.Utility++import qualified Data.Binary as Binary+import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit+import qualified Data.Binary.Get as Binary+import qualified Data.Binary.Put as Binary+import qualified Data.ByteString.Lazy as ByteString+import qualified Data.Word as Word++newtype Word32 = Word32+  { word32Value :: Word.Word32+  } deriving (Eq, Ord, Show)++getWord32 :: Binary.Get Word32+getWord32 = do+  word32 <- Binary.getWord32le+  pure (Word32 word32)++putWord32 :: Word32 -> Binary.Put+putWord32 word32 = Binary.putWord32le (word32Value word32)++getWord32Bits :: BinaryBit.BitGet Word32+getWord32Bits = do+  bytes <- BinaryBit.getLazyByteString 4+  pure (Binary.runGet getWord32 (reverseBytes bytes))++putWord32Bits :: Word32 -> BinaryBit.BitPut ()+putWord32Bits word32 = do+  let bytes = Binary.runPut (putWord32 word32)+  BinaryBit.putByteString (ByteString.toStrict (reverseBytes bytes))
+ source/library/Rattletrap/Primitive/Word64.hs view
@@ -0,0 +1,33 @@+module Rattletrap.Primitive.Word64 where++import Rattletrap.Utility++import qualified Data.Binary as Binary+import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit+import qualified Data.Binary.Get as Binary+import qualified Data.Binary.Put as Binary+import qualified Data.ByteString.Lazy as ByteString+import qualified Data.Word as Word++newtype Word64 = Word64+  { word64Value :: Word.Word64+  } deriving (Eq, Ord, Show)++getWord64 :: Binary.Get Word64+getWord64 = do+  word64 <- Binary.getWord64le+  pure (Word64 word64)++putWord64 :: Word64 -> Binary.Put+putWord64 word64 = Binary.putWord64le (word64Value word64)++getWord64Bits :: BinaryBit.BitGet Word64+getWord64Bits = do+  bytes <- BinaryBit.getLazyByteString 8+  pure (Binary.runGet getWord64 (reverseBytes bytes))++putWord64Bits :: Word64 -> BinaryBit.BitPut ()+putWord64Bits word64 = do+  let bytes = Binary.runPut (putWord64 word64)+  BinaryBit.putByteString (ByteString.toStrict (reverseBytes bytes))
+ source/library/Rattletrap/Primitive/Word8.hs view
@@ -0,0 +1,33 @@+module Rattletrap.Primitive.Word8 where++import Rattletrap.Utility++import qualified Data.Binary as Binary+import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit+import qualified Data.Binary.Get as Binary+import qualified Data.Binary.Put as Binary+import qualified Data.ByteString.Lazy as ByteString+import qualified Data.Word as Word++newtype Word8 = Word8+  { word8Value :: Word.Word8+  } deriving (Eq, Ord, Show)++getWord8 :: Binary.Get Word8+getWord8 = do+  word8 <- Binary.getWord8+  pure (Word8 word8)++putWord8 :: Word8 -> Binary.Put+putWord8 word8 = Binary.putWord8 (word8Value word8)++getWord8Bits :: BinaryBit.BitGet Word8+getWord8Bits = do+  bytes <- BinaryBit.getLazyByteString 1+  pure (Binary.runGet getWord8 (reverseBytes bytes))++putWord8Bits :: Word8 -> BinaryBit.BitPut ()+putWord8Bits word8 = do+  let bytes = Binary.runPut (putWord8 word8)+  BinaryBit.putByteString (ByteString.toStrict (reverseBytes bytes))
+ source/library/Rattletrap/Property.hs view
@@ -0,0 +1,26 @@+module Rattletrap.Property where++import Rattletrap.Primitive+import Rattletrap.PropertyValue++import qualified Data.Binary as Binary++data Property = Property+  { propertyKind :: Text+  , propertySize :: Word64+  -- ^ Not used.+  , propertyValue :: PropertyValue Property+  } deriving (Eq, Ord, Show)++getProperty :: Binary.Get Property+getProperty = do+  kind <- getText+  size <- getWord64+  value <- getPropertyValue getProperty kind+  pure (Property kind size value)++putProperty :: Property -> Binary.Put+putProperty property = do+  putText (propertyKind property)+  putWord64 (propertySize property)+  putPropertyValue putProperty (propertyValue property)
+ source/library/Rattletrap/PropertyValue.hs view
@@ -0,0 +1,72 @@+module Rattletrap.PropertyValue where++import Rattletrap.Primitive++import qualified Data.Binary as Binary++data PropertyValue a+  = ArrayProperty (List (Dictionary a))+  -- ^ Yes, a list of dictionaries. No, it doesn't make sense. These usually+  -- only have one element.+  | BoolProperty Word8+  | ByteProperty Text+                 (Maybe Text)+  -- ^ This is a strange name for essentially a key-value pair.+  | FloatProperty Float32+  | IntProperty Int32+  | NameProperty Text+  -- ^ It's unclear how exactly this is different than a 'StrProperty'.+  | QWordProperty Word64+  | StrProperty Text+  deriving (Eq, Ord, Show)++getPropertyValue :: Binary.Get a -> Text -> Binary.Get (PropertyValue a)+getPropertyValue getProperty kind =+  case textToString kind of+    "ArrayProperty" -> do+      list <- getList (getDictionary getProperty)+      pure (ArrayProperty list)+    "BoolProperty" -> do+      word8 <- getWord8+      pure (BoolProperty word8)+    "ByteProperty" -> do+      k <- getText+      v <-+        if textToString k == "OnlinePlatform_Steam"+          then pure Nothing+          else do+            v <- getText+            pure (Just v)+      pure (ByteProperty k v)+    "FloatProperty" -> do+      float32 <- getFloat32+      pure (FloatProperty float32)+    "IntProperty" -> do+      int32 <- getInt32+      pure (IntProperty int32)+    "NameProperty" -> do+      text <- getText+      pure (NameProperty text)+    "QWordProperty" -> do+      word64 <- getWord64+      pure (QWordProperty word64)+    "StrProperty" -> do+      text <- getText+      pure (StrProperty text)+    _ -> fail ("don't know how to read property value " ++ show kind)++putPropertyValue :: (a -> Binary.Put) -> PropertyValue a -> Binary.Put+putPropertyValue putProperty value =+  case value of+    ArrayProperty list -> putList (putDictionary putProperty) list+    BoolProperty word8 -> putWord8 word8+    ByteProperty k mv -> do+      putText k+      case mv of+        Nothing -> pure ()+        Just v -> putText v+    FloatProperty float32 -> putFloat32 float32+    IntProperty int32 -> putInt32 int32+    NameProperty text -> putText text+    QWordProperty word64 -> putWord64 word64+    StrProperty text -> putText text
+ source/library/Rattletrap/RemoteId.hs view
@@ -0,0 +1,55 @@+module Rattletrap.RemoteId where++import Rattletrap.Primitive+import Rattletrap.Utility++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit+import qualified Data.ByteString.Lazy as ByteString+import qualified Data.Text as Text+import qualified Data.Text.Encoding as Encoding+import qualified Data.Word as Word++data RemoteId+  = PlayStationId Text.Text+                  [Word.Word8]+  | SplitscreenId Word.Word32+  | SteamId Word64+  | XboxId Word64+  deriving (Eq, Ord, Show)++getRemoteId :: Word8 -> BinaryBit.BitGet RemoteId+getRemoteId systemId =+  case word8Value systemId of+    0 -> do+      word24 <- BinaryBit.getWord32be 24+      pure (SplitscreenId word24)+    1 -> do+      word64 <- getWord64Bits+      pure (SteamId word64)+    2 -> do+      rawName <- BinaryBit.getLazyByteString 16+      let name =+            Text.dropWhileEnd+              (== '\x00')+              (Encoding.decodeLatin1+                 (ByteString.toStrict (reverseBytes rawName)))+      bytes <- BinaryBit.getLazyByteString 16+      pure (PlayStationId name (ByteString.unpack bytes))+    4 -> do+      word64 <- getWord64Bits+      pure (XboxId word64)+    _ -> fail ("unknown system id " ++ show systemId)++putRemoteId :: RemoteId -> BinaryBit.BitPut ()+putRemoteId remoteId =+  case remoteId of+    PlayStationId name bytes -> do+      let rawName =+            ByteString.toStrict+              (reverseBytes (padBytes (16 :: Int) (encodeLatin1 name)))+      BinaryBit.putByteString rawName+      BinaryBit.putByteString (ByteString.toStrict (ByteString.pack bytes))+    SplitscreenId word24 -> BinaryBit.putWord32be 24 word24+    SteamId word64 -> putWord64Bits word64+    XboxId word64 -> putWord64Bits word64
+ source/library/Rattletrap/Replay.hs view
@@ -0,0 +1,39 @@+module Rattletrap.Replay where++import Rattletrap.Content+import Rattletrap.Header+import Rattletrap.Primitive.Section++import qualified Data.Binary as Binary++-- | A Rocket League replay.+data Replay = Replay+  { replayHeader :: Section Header+  -- ^ This has most of the high-level metadata.+  , replayContent :: Section Content+  -- ^ This has most of the low-level game data.+  } deriving (Eq, Ord, Show)++-- | Parses a raw replay. Use this with 'Data.Binary.Get.runGet'.+--+-- @+-- let replay = 'Data.Binary.Get.runGet' 'getReplay' bytes+-- @+getReplay :: Binary.Get Replay+getReplay = do+  header <- getSection getHeader+  let version = getVersion (sectionBody header)+  let numFrames = getNumFrames (sectionBody header)+  let maxChannels = getMaxChannels (sectionBody header)+  content <- getSection (getContent version numFrames maxChannels)+  pure (Replay header content)++-- | Generates a raw replay. Use this with 'Data.Binary.Put.runPut'.+--+-- @+-- let bytes = 'Data.Binary.Put.runPut' ('putReplay' replay)+-- @+putReplay :: Replay -> Binary.Put+putReplay replay = do+  putSection putHeader (replayHeader replay)+  putSection putContent (replayContent replay)
+ source/library/Rattletrap/Replication.hs view
@@ -0,0 +1,57 @@+module Rattletrap.Replication where++import Rattletrap.ActorMap+import Rattletrap.ClassAttributeMap+import Rattletrap.Primitive+import Rattletrap.ReplicationValue++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++data Replication = Replication+  { replicationActorId :: CompressedWord+  , replicationValue :: ReplicationValue+  } deriving (Eq, Ord, Show)++getReplications+  :: (Int, Int)+  -> Word+  -> ClassAttributeMap+  -> ActorMap+  -> BinaryBit.BitGet ([Replication], ActorMap)+getReplications version maxChannels classAttributeMap actorMap = do+  maybeReplication <-+    getReplication version maxChannels classAttributeMap actorMap+  case maybeReplication of+    Nothing -> pure ([], actorMap)+    Just (replication, newActorMap) -> do+      (replications, newerActorMap) <-+        getReplications version maxChannels classAttributeMap newActorMap+      pure (replication : replications, newerActorMap)++putReplications :: [Replication] -> BinaryBit.BitPut ()+putReplications replications = do+  mapM_ putReplication replications+  BinaryBit.putBool False++getReplication+  :: (Int, Int)+  -> Word+  -> ClassAttributeMap+  -> ActorMap+  -> BinaryBit.BitGet (Maybe (Replication, ActorMap))+getReplication version maxChannels classAttributeMap actorMap = do+  hasReplication <- BinaryBit.getBool+  if not hasReplication+    then pure Nothing+    else do+      actorId <- getCompressedWord maxChannels+      (value, newActorMap) <-+        getReplicationValue version classAttributeMap actorMap actorId+      pure (Just (Replication actorId value, newActorMap))++putReplication :: Replication -> BinaryBit.BitPut ()+putReplication replication = do+  BinaryBit.putBool True+  putCompressedWord (replicationActorId replication)+  putReplicationValue (replicationValue replication)
+ source/library/Rattletrap/Replication/Destroyed.hs view
@@ -0,0 +1,17 @@+module Rattletrap.Replication.Destroyed where++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++-- | Destroyed replications don't actually contain any extra information. All+-- you need to know is the actor's ID, which is given by the+-- 'Rattletrap.Replication.Replication'.+data DestroyedReplication = DestroyedReplication+  {+  } deriving (Eq, Ord, Show)++getDestroyedReplication :: BinaryBit.BitGet DestroyedReplication+getDestroyedReplication = pure DestroyedReplication++putDestroyedReplication :: DestroyedReplication -> BinaryBit.BitPut ()+putDestroyedReplication _ = pure ()
+ source/library/Rattletrap/Replication/Spawned.hs view
@@ -0,0 +1,95 @@+module Rattletrap.Replication.Spawned where++import Rattletrap.ActorMap+import Rattletrap.ClassAttributeMap+import Rattletrap.Initialization+import Rattletrap.Primitive++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++data SpawnedReplication = SpawnedReplication+  { spawnedReplicationFlag :: Bool+  -- ^ Unclear what this is.+  , spawnedReplicationNameIndex :: Maybe Word32+  , spawnedReplicationName :: Maybe Text+  -- ^ Read-only! Changing a replication's name requires editing the+  -- 'spawnedReplicationNameIndex' and maybe the class attribute map.+  , spawnedReplicationObjectId :: Word32+  , spawnedReplicationObjectName :: Text+  -- ^ Read-only! Changing a replication's object requires editing the class+  -- attribute map.+  , spawnedReplicationClassName :: Text+  -- ^ Read-only! Changing a replication's class requires editing the class+  -- attribute map.+  , spawnedReplicationInitialization :: Initialization+  } deriving (Eq, Ord, Show)++getSpawnedReplication+  :: (Int, Int)+  -> ClassAttributeMap+  -> ActorMap+  -> CompressedWord+  -> BinaryBit.BitGet (SpawnedReplication, ActorMap)+getSpawnedReplication version classAttributeMap actorMap actorId = do+  flag <- BinaryBit.getBool+  nameIndex <-+    if version < (868, 14)+      then pure Nothing+      else do+        nameIndex <- getWord32Bits+        pure (Just nameIndex)+  name <- lookupName classAttributeMap nameIndex+  objectId <- getWord32Bits+  let newActorMap = updateActorMap actorId objectId actorMap+  objectName <- lookupObjectName classAttributeMap objectId+  className <- lookupClassName objectName+  let hasLocation = classHasLocation className+  let hasRotation = classHasRotation className+  initialization <- getInitialization hasLocation hasRotation+  pure+    ( SpawnedReplication+        flag+        nameIndex+        name+        objectId+        objectName+        className+        initialization+    , newActorMap)++putSpawnedReplication :: SpawnedReplication -> BinaryBit.BitPut ()+putSpawnedReplication spawnedReplication = do+  BinaryBit.putBool (spawnedReplicationFlag spawnedReplication)+  case spawnedReplicationNameIndex spawnedReplication of+    Nothing -> pure ()+    Just nameIndex -> putWord32Bits nameIndex+  putWord32Bits (spawnedReplicationObjectId spawnedReplication)+  putInitialization (spawnedReplicationInitialization spawnedReplication)++lookupName+  :: Monad m+  => ClassAttributeMap -> Maybe Word32 -> m (Maybe Text)+lookupName classAttributeMap maybeNameIndex =+  case maybeNameIndex of+    Nothing -> pure Nothing+    Just nameIndex ->+      case getName (classAttributeMapNameMap classAttributeMap) nameIndex of+        Nothing -> fail ("could not get name for index " ++ show nameIndex)+        Just name -> pure (Just name)++lookupObjectName+  :: Monad m+  => ClassAttributeMap -> Word32 -> m Text+lookupObjectName classAttributeMap objectId =+  case getObjectName (classAttributeMapObjectMap classAttributeMap) objectId of+    Nothing -> fail ("could not get object name for id " ++ show objectId)+    Just objectName -> pure objectName++lookupClassName+  :: Monad m+  => Text -> m Text+lookupClassName objectName =+  case getClassName objectName of+    Nothing -> fail ("could not get class name for object " ++ show objectName)+    Just className -> pure className
+ source/library/Rattletrap/Replication/Updated.hs view
@@ -0,0 +1,27 @@+module Rattletrap.Replication.Updated where++import Rattletrap.ActorMap+import Rattletrap.Attribute+import Rattletrap.ClassAttributeMap+import Rattletrap.Primitive++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++newtype UpdatedReplication = UpdatedReplication+  { updatedReplicationAttributes :: [Attribute]+  } deriving (Eq, Ord, Show)++getUpdatedReplication+  :: (Int, Int)+  -> ClassAttributeMap+  -> ActorMap+  -> CompressedWord+  -> BinaryBit.BitGet UpdatedReplication+getUpdatedReplication version classAttributeMap actorMap actorId = do+  attributes <- getAttributes version classAttributeMap actorMap actorId+  pure (UpdatedReplication attributes)++putUpdatedReplication :: UpdatedReplication -> BinaryBit.BitPut ()+putUpdatedReplication updatedReplication =+  putAttributes (updatedReplicationAttributes updatedReplication)
+ source/library/Rattletrap/ReplicationValue.hs view
@@ -0,0 +1,63 @@+module Rattletrap.ReplicationValue+  ( module Rattletrap.ReplicationValue+  , module Rattletrap.Replication.Destroyed+  , module Rattletrap.Replication.Spawned+  , module Rattletrap.Replication.Updated+  ) where++import Rattletrap.ActorMap+import Rattletrap.ClassAttributeMap+import Rattletrap.Primitive+import Rattletrap.Replication.Destroyed+import Rattletrap.Replication.Spawned+import Rattletrap.Replication.Updated++import qualified Data.Binary.Bits.Get as BinaryBit+import qualified Data.Binary.Bits.Put as BinaryBit++data ReplicationValue+  = SpawnedReplicationValue SpawnedReplication+  -- ^ Creates a new actor.+  | UpdatedReplicationValue UpdatedReplication+  -- ^ Updates an existing actor.+  | DestroyedReplicationValue DestroyedReplication+  -- ^ Destroys an existing actor.+  deriving (Eq, Ord, Show)++getReplicationValue+  :: (Int, Int)+  -> ClassAttributeMap+  -> ActorMap+  -> CompressedWord+  -> BinaryBit.BitGet (ReplicationValue, ActorMap)+getReplicationValue version classAttributeMap actorMap actorId = do+  isOpen <- BinaryBit.getBool+  if isOpen+    then do+      isNew <- BinaryBit.getBool+      if isNew+        then do+          (x, newActorMap) <-+            getSpawnedReplication version classAttributeMap actorMap actorId+          pure (SpawnedReplicationValue x, newActorMap)+        else do+          x <- getUpdatedReplication version classAttributeMap actorMap actorId+          pure (UpdatedReplicationValue x, actorMap)+    else do+      x <- getDestroyedReplication+      pure (DestroyedReplicationValue x, actorMap)++putReplicationValue :: ReplicationValue -> BinaryBit.BitPut ()+putReplicationValue value =+  case value of+    SpawnedReplicationValue x -> do+      BinaryBit.putBool True+      BinaryBit.putBool True+      putSpawnedReplication x+    UpdatedReplicationValue x -> do+      BinaryBit.putBool True+      BinaryBit.putBool False+      putUpdatedReplication x+    DestroyedReplicationValue x -> do+      BinaryBit.putBool False+      putDestroyedReplication x
+ source/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.ByteString.Lazy.Char8 as ByteString8+import qualified Data.Text as Text+import qualified Data.Word as Word++encodeLatin1 :: Text.Text -> ByteString.ByteString+encodeLatin1 text = ByteString8.pack (Text.unpack text)++padBytes+  :: Integral a+  => a -> ByteString.ByteString -> ByteString.ByteString+padBytes size bytes =+  ByteString.concat+    [ bytes+    , ByteString.replicate (fromIntegral size - ByteString.length bytes) 0x00+    ]++reverseByte :: Word.Word8 -> Word.Word8+reverseByte byte =+  Bits.shiftR (byte Bits..&. Bits.bit 7) 7 ++  Bits.shiftR (byte Bits..&. Bits.bit 6) 5 ++  Bits.shiftR (byte Bits..&. Bits.bit 5) 3 ++  Bits.shiftR (byte Bits..&. Bits.bit 4) 1 ++  Bits.shiftL (byte Bits..&. Bits.bit 3) 1 ++  Bits.shiftL (byte Bits..&. Bits.bit 2) 3 ++  Bits.shiftL (byte Bits..&. Bits.bit 1) 5 ++  Bits.shiftL (byte Bits..&. Bits.bit 0) 7++reverseBytes :: ByteString.ByteString -> ByteString.ByteString+reverseBytes bytes = ByteString.map reverseByte bytes
+ source/library/Rattletrap/Version.hs view
@@ -0,0 +1,7 @@+module Rattletrap.Version where++import qualified Data.Version as Version+import qualified Paths_rattletrap as This++version :: Version.Version+version = This.version
+ source/tests/Main.hs view
@@ -0,0 +1,100 @@+import qualified Control.Monad as Monad+import qualified Data.ByteString.Lazy as ByteString+import qualified Rattletrap+import qualified System.FilePath as FilePath+import qualified System.IO.Temp as Temp+import qualified Test.Tasty as Tasty+import qualified Test.Tasty.Hspec as Hspec++main :: IO ()+main = do+  tests <- Hspec.testSpec "rattletrap" spec+  Tasty.defaultMain tests++spec :: Hspec.Spec+spec =+  Hspec.describe+    "Rattletrap"+    (mapM_ (\(uuid, description) -> itCanRoundTrip uuid description) replays)++itCanRoundTrip :: String -> String -> Hspec.Spec+itCanRoundTrip uuid description =+  Hspec.it (specName uuid description) (specBody uuid)++specName :: String -> String -> String+specName uuid description = unwords [take 4 uuid, description]++specBody :: String -> IO ()+specBody uuid = do+  let inputFile = pathToReplay uuid+  input <- ByteString.readFile inputFile+  Temp.withSystemTempDirectory+    "replay-"+    (\directory -> do+       let jsonFile = FilePath.combine directory "replay.json"+       Rattletrap.mainWithArgs ["decode", inputFile, jsonFile]+       let outputFile = FilePath.combine directory "output.replay"+       Rattletrap.mainWithArgs ["encode", jsonFile, outputFile]+       output <- ByteString.readFile outputFile+       Monad.unless+         (output == input)+         (Hspec.expectationFailure "output does not match input"))++pathToReplay :: String -> FilePath+pathToReplay uuid =+  FilePath.joinPath ["replays", FilePath.addExtension uuid ".replay"]++replays :: [(String, String)]+replays =+  [ ("00080014003600090000036E0F65CCEB", "a flip time")+  , ("07E925B1423653D44CB8B4B2524792C1", "a game mode before Neo Tokyo")+  , ("0AD21FEE4A512906BEB6C98136AAF49A", "some Latin-1 text")+  , ("1205D96C4D819800927791820096CD49", "rumble mode")+  , ("160CA83E41083BFD8E6315B4BFCA0561", "a dedicated server IP")+  , ("18D6738D415B70B5BE4C299588D3C141", "an online loadout attribute")+  , ("1A126AC24CAA0DB0E98835BD960B8AF8", "overtime")+  , ("1AE415514DFC65DCBF8B8391AD35488D", "a game time")+  , ("1BC2D01444ACE577D01E988EADD4DFD0", "no padding after the frames")+  , ("1D1DE97D4941C86E43FE0093563DB621", "a camera pitch")+  , ("1EF90FCC4F719F606A5327B3CDD782A4", "a private hoops match")+  , ("1F3798E540B0C37A946561ABBB3037F9", "splitscreen players")+  , ("211466D04B983F5A33CC2FA1D5928672", "a match save")+  , ("22BACD794ABE7B92E50E9CBDBD9C59CE", "a vote to forfeit")+  , ("27B6A7B64553F0F685874584F96BAB1B", "some UTF-16 text")+  , ("29F582C34A65EB34D358A784CBE3C189", "frames")+  , ("372DBFCA4BDB340E4357B6BD43032802", "a camera yaw attribute")+  , ("387F059C47C09E253C875CA990EFD9F2", "a frozen attribute")+  , ("3EA147DD485B8DD24810689A7A989E44", "a custom team name")+  , ("4126861E477F4A03DE2A4080374D7908", "a game mode after Neo Tokyo")+  , ("42F0D8DA4FC89AE7B80FCAB7F637A8EA", "reservations after Neo Tokyo")+  , ("504ED825482186E771FAA9B642CE5CE4", "some messages")+  , ("52AA67F94090C19D33C5009E54D31FE4", "a match-ending attribute")+  , ("540DA764423C8FB24EB9D486D982F16F", "a demolish attribute")+  , ("551CA4D44FF2B86015DE44A6B5790D4C", "private match settings")+  , ("6320E51C49066A7C210A2993C2201D5F", "a forfeit attribute")+  , ("6688EEE34BFEB3EC3A9E3283098CC712", "a malformed byte property")+  , ("6D1B06D844A5BB91B81FD4B5B28F08BA", "a flip right")+  , ("6F7CFCD24638F8A6567AB3A8B9958A90", "a map with numbers")+  , ("7109EB9846D303E54B7ACBA792036213", "a boost modifier")+  , ("7BF6073F4614CE0A438994B9A260DA6A", "an online loadouts attribute")+  , ("89CBA30E46FA5385BDD35DA4285D4D2E", "remote user data")+  , ("9704208245D7DD851F2FB2BC7DFD9AC3", "a batarang")+  , ("98E58A904D713F2DE202358E8573265D", "a player using behind view")+  , ("A128B3AB45D5A18E3EF9CF93C9576BCE", "a round count down")+  , ("A52F804845573D8DA65E97BF59026A43", "some more mutators")+  , ("A6711CE74272B2E663DCC9A200A218E3", "a waiting player")+  , ("A7F001A1417A19BFA8C90990D8F7C2FF", "a ready attribute")+  , ("B9F9B87D4A9D0A3D25D4EC91C0401DE2", "a party leader")+  , ("C14F7E0E4D9B5E6BE9AD5D8ED56B174C", "some mutators")+  , ("C8372B1345B1803DEF039F815DBD802D", "a spectator")+  , ("CC4CA70D4F7A67EBAD0ED9B9923106F7", "after Starbase ARC")+  , ("D428F81646A98C25902CE988AE5C14C8", "a private hockey match")+  , ("D7FB197A451D69075A0C99A2F49A4053", "an explosion attribute")+  , ("DCB3A6B94A9DBE46FDE5EAA9B012F6C8", "a pawn type attribute")+  , ("EAE311E84BA35B590A6FDBA6DD4F2FEB", "an actor/object ID collision")+  , ("EAE8DADA4BB2DC5422792C9B4A67392D", "custom team colors")+  , ("F299F176491554B11E34AB91CA76B2CE", "a location attribute")+  , ("F7B9E14545C7467B89A00895980FCD73", "a hockey game event")+  , ("F811C1D24888015E23B598AD8628C742", "no frames")+  , ("FDC79DA84DD463D4BCCE6B892829AC88", "an MVP")+  ]
− test/Test.hs
@@ -1,98 +0,0 @@-import qualified Control.Monad as Monad-import qualified Data.ByteString.Lazy as ByteString-import qualified Rattletrap-import qualified System.FilePath as FilePath-import qualified System.IO.Temp as Temp-import qualified Test.Tasty as Tasty-import qualified Test.Tasty.Hspec as Hspec--main :: IO ()-main = do-  tests <- Hspec.testSpec "rattletrap" spec-  Tasty.defaultMain tests--spec :: Hspec.Spec-spec =-  Hspec.describe-    "Rattletrap"-    (mapM_ (\(uuid, description) -> itCanRoundTrip uuid description) replays)--itCanRoundTrip :: String -> String -> Hspec.Spec-itCanRoundTrip uuid description =-  Hspec.it (specName uuid description) (specBody uuid)--specName :: String -> String -> String-specName uuid description = unwords [take 4 uuid, description]--specBody :: String -> IO ()-specBody uuid = do-  let inputFile = pathToReplay uuid-  input <- ByteString.readFile inputFile-  Temp.withSystemTempDirectory-    "replay-"-    (\directory -> do-       let jsonFile = FilePath.combine directory "replay.json"-       Rattletrap.mainWithArgs ["decode", inputFile, jsonFile]-       let outputFile = FilePath.combine directory "output.replay"-       Rattletrap.mainWithArgs ["encode", jsonFile, outputFile]-       output <- ByteString.readFile outputFile-       Monad.unless-         (output == input)-         (Hspec.expectationFailure "output does not match input"))--pathToReplay :: String -> FilePath-pathToReplay uuid =-  FilePath.joinPath ["test", "replays", FilePath.addExtension uuid ".replay"]--replays :: [(String, String)]-replays =-  [ ("00080014003600090000036E0F65CCEB", "a flip time")-  , ("07E925B1423653D44CB8B4B2524792C1", "a game mode before Neo Tokyo")-  , ("0AD21FEE4A512906BEB6C98136AAF49A", "some Latin-1 text")-  , ("1205D96C4D819800927791820096CD49", "rumble mode")-  , ("160CA83E41083BFD8E6315B4BFCA0561", "a dedicated server IP")-  , ("18D6738D415B70B5BE4C299588D3C141", "an online loadout attribute")-  , ("1A126AC24CAA0DB0E98835BD960B8AF8", "overtime")-  , ("1BC2D01444ACE577D01E988EADD4DFD0", "no padding after the frames")-  , ("1D1DE97D4941C86E43FE0093563DB621", "a camera pitch")-  , ("1EF90FCC4F719F606A5327B3CDD782A4", "a private hoops match")-  , ("1F3798E540B0C37A946561ABBB3037F9", "splitscreen players")-  , ("211466D04B983F5A33CC2FA1D5928672", "a match save")-  , ("22BACD794ABE7B92E50E9CBDBD9C59CE", "a vote to forfeit")-  , ("27B6A7B64553F0F685874584F96BAB1B", "some UTF-16 text")-  , ("29F582C34A65EB34D358A784CBE3C189", "frames")-  , ("372DBFCA4BDB340E4357B6BD43032802", "a camera yaw attribute")-  , ("387F059C47C09E253C875CA990EFD9F2", "a frozen attribute")-  , ("3EA147DD485B8DD24810689A7A989E44", "a custom team name")-  , ("4126861E477F4A03DE2A4080374D7908", "a game mode after Neo Tokyo")-  , ("42F0D8DA4FC89AE7B80FCAB7F637A8EA", "reservations after Neo Tokyo")-  , ("504ED825482186E771FAA9B642CE5CE4", "some messages")-  , ("52AA67F94090C19D33C5009E54D31FE4", "a match-ending attribute")-  , ("540DA764423C8FB24EB9D486D982F16F", "a demolish attribute")-  , ("551CA4D44FF2B86015DE44A6B5790D4C", "private match settings")-  , ("6688EEE34BFEB3EC3A9E3283098CC712", "a malformed byte property")-  , ("6D1B06D844A5BB91B81FD4B5B28F08BA", "a flip right")-  , ("6F7CFCD24638F8A6567AB3A8B9958A90", "a map with numbers")-  , ("7109EB9846D303E54B7ACBA792036213", "a boost modifier")-  , ("7BF6073F4614CE0A438994B9A260DA6A", "an online loadouts attribute")-  , ("89CBA30E46FA5385BDD35DA4285D4D2E", "remote user data")-  , ("9704208245D7DD851F2FB2BC7DFD9AC3", "a batarang")-  , ("98E58A904D713F2DE202358E8573265D", "a player using behind view")-  , ("A128B3AB45D5A18E3EF9CF93C9576BCE", "a round count down")-  , ("A52F804845573D8DA65E97BF59026A43", "some more mutators")-  , ("A6711CE74272B2E663DCC9A200A218E3", "a waiting player")-  , ("A7F001A1417A19BFA8C90990D8F7C2FF", "a ready attribute")-  , ("B82DDB624C393A4A425E68AB40DC2450", "a vote actor and trailing bits")-  , ("B9F9B87D4A9D0A3D25D4EC91C0401DE2", "a party leader")-  , ("C14F7E0E4D9B5E6BE9AD5D8ED56B174C", "some mutators")-  , ("C8372B1345B1803DEF039F815DBD802D", "a spectator")-  , ("D428F81646A98C25902CE988AE5C14C8", "a private hockey match")-  , ("D7FB197A451D69075A0C99A2F49A4053", "an explosion attribute")-  , ("DCB3A6B94A9DBE46FDE5EAA9B012F6C8", "a pawn type attribute")-  , ("EAE311E84BA35B590A6FDBA6DD4F2FEB", "an actor/object ID collision")-  , ("EAE8DADA4BB2DC5422792C9B4A67392D", "custom team colors")-  , ("F299F176491554B11E34AB91CA76B2CE", "a location attribute")-  , ("F7B9E14545C7467B89A00895980FCD73", "a hockey game event")-  , ("F811C1D24888015E23B598AD8628C742", "no frames")-  , ("FDC79DA84DD463D4BCCE6B892829AC88", "an MVP")-  ]
− test/replays/00080014003600090000036E0F65CCEB.replay

binary file changed (94347 → absent bytes)

− test/replays/07E925B1423653D44CB8B4B2524792C1.replay

binary file changed (30063 → absent bytes)

− test/replays/0AD21FEE4A512906BEB6C98136AAF49A.replay

binary file changed (27949 → absent bytes)

− test/replays/1205D96C4D819800927791820096CD49.replay

binary file changed (461470 → absent bytes)

− test/replays/160CA83E41083BFD8E6315B4BFCA0561.replay

binary file changed (565359 → absent bytes)

− test/replays/18D6738D415B70B5BE4C299588D3C141.replay

binary file changed (21996 → absent bytes)

− test/replays/1A126AC24CAA0DB0E98835BD960B8AF8.replay

binary file changed (98814 → absent bytes)

− test/replays/1BC2D01444ACE577D01E988EADD4DFD0.replay

binary file changed (22739 → absent bytes)

− test/replays/1D1DE97D4941C86E43FE0093563DB621.replay

binary file changed (59943 → absent bytes)

− test/replays/1EF90FCC4F719F606A5327B3CDD782A4.replay

binary file changed (737542 → absent bytes)

− test/replays/1F3798E540B0C37A946561ABBB3037F9.replay

binary file changed (27316 → absent bytes)

− test/replays/211466D04B983F5A33CC2FA1D5928672.replay

binary file changed (38258 → absent bytes)

− test/replays/22BACD794ABE7B92E50E9CBDBD9C59CE.replay

binary file changed (315922 → absent bytes)

− test/replays/27B6A7B64553F0F685874584F96BAB1B.replay

binary file changed (25332 → absent bytes)

− test/replays/29F582C34A65EB34D358A784CBE3C189.replay

binary file changed (21607 → absent bytes)

− test/replays/372DBFCA4BDB340E4357B6BD43032802.replay

binary file changed (28065 → absent bytes)

− test/replays/387F059C47C09E253C875CA990EFD9F2.replay

binary file changed (737279 → absent bytes)

− test/replays/3EA147DD485B8DD24810689A7A989E44.replay

binary file changed (596690 → absent bytes)

− test/replays/4126861E477F4A03DE2A4080374D7908.replay

binary file changed (26284 → absent bytes)

− test/replays/42F0D8DA4FC89AE7B80FCAB7F637A8EA.replay

binary file changed (116440 → absent bytes)

− test/replays/504ED825482186E771FAA9B642CE5CE4.replay

binary file changed (145094 → absent bytes)

− test/replays/52AA67F94090C19D33C5009E54D31FE4.replay

binary file changed (570657 → absent bytes)

− test/replays/540DA764423C8FB24EB9D486D982F16F.replay

binary file changed (34674 → absent bytes)

− test/replays/551CA4D44FF2B86015DE44A6B5790D4C.replay

binary file changed (754887 → absent bytes)

− test/replays/6688EEE34BFEB3EC3A9E3283098CC712.replay

binary file changed (1026 → absent bytes)

− test/replays/6D1B06D844A5BB91B81FD4B5B28F08BA.replay

binary file changed (91217 → absent bytes)

− test/replays/6F7CFCD24638F8A6567AB3A8B9958A90.replay

binary file changed (29715 → absent bytes)

− test/replays/7109EB9846D303E54B7ACBA792036213.replay

binary file changed (65002 → absent bytes)

− test/replays/7BF6073F4614CE0A438994B9A260DA6A.replay

binary file changed (22841 → absent bytes)

− test/replays/89CBA30E46FA5385BDD35DA4285D4D2E.replay

binary file changed (212429 → absent bytes)

− test/replays/9704208245D7DD851F2FB2BC7DFD9AC3.replay

binary file changed (36307 → absent bytes)

− test/replays/98E58A904D713F2DE202358E8573265D.replay

binary file changed (34047 → absent bytes)

− test/replays/A128B3AB45D5A18E3EF9CF93C9576BCE.replay

binary file changed (743537 → absent bytes)

− test/replays/A52F804845573D8DA65E97BF59026A43.replay

binary file changed (73064 → absent bytes)

− test/replays/A6711CE74272B2E663DCC9A200A218E3.replay

binary file changed (221897 → absent bytes)

− test/replays/A7F001A1417A19BFA8C90990D8F7C2FF.replay

binary file changed (396148 → absent bytes)

− test/replays/B82DDB624C393A4A425E68AB40DC2450.replay

binary file changed (1068865 → absent bytes)

− test/replays/B9F9B87D4A9D0A3D25D4EC91C0401DE2.replay

binary file changed (25888 → absent bytes)

− test/replays/C14F7E0E4D9B5E6BE9AD5D8ED56B174C.replay

binary file changed (25904 → absent bytes)

− test/replays/C8372B1345B1803DEF039F815DBD802D.replay

binary file changed (23625 → absent bytes)

− test/replays/D428F81646A98C25902CE988AE5C14C8.replay

binary file changed (476589 → absent bytes)

− test/replays/D7FB197A451D69075A0C99A2F49A4053.replay

binary file changed (29095 → absent bytes)

− test/replays/DCB3A6B94A9DBE46FDE5EAA9B012F6C8.replay

binary file changed (1055071 → absent bytes)

− test/replays/EAE311E84BA35B590A6FDBA6DD4F2FEB.replay

binary file changed (30151 → absent bytes)

− test/replays/EAE8DADA4BB2DC5422792C9B4A67392D.replay

binary file changed (25462 → absent bytes)

− test/replays/F299F176491554B11E34AB91CA76B2CE.replay

binary file changed (22732 → absent bytes)

− test/replays/F7B9E14545C7467B89A00895980FCD73.replay

binary file changed (25580 → absent bytes)

− test/replays/F811C1D24888015E23B598AD8628C742.replay

binary file changed (605 → absent bytes)

− test/replays/FDC79DA84DD463D4BCCE6B892829AC88.replay

binary file changed (783619 → absent bytes)