packages feed

rattletrap 10.0.7 → 11.0.0

raw patch · 107 files changed

+1545/−872 lines, 107 filesdep −binarydep −caerbannogPVP ok

version bump matches the API change (PVP)

Dependencies removed: binary, caerbannog

API changes (from Hackage documentation)

- Rattletrap.BitGet: binaryGetToByteGet :: Get a -> ByteGet a
- Rattletrap.BitGet: word8 :: Int -> BitGet Word8
- Rattletrap.BitPut: fromBinaryBits :: BitPut () -> BitPut
- Rattletrap.BitPut: toBinaryBits :: BitPut -> BitPut ()
- Rattletrap.BitPut: word8 :: Int -> Word8 -> BitPut
- Rattletrap.Type.Attribute.CamSettings: hasTransitionSpeed :: Version -> Bool
- Rattletrap.Type.Attribute.GameMode: has8Bits :: Version -> Bool
- Rattletrap.Type.Attribute.GameMode: numBits_ :: Version -> Int
- Rattletrap.Type.Attribute.PartyLeader: [id] :: PartyLeader -> Maybe (RemoteId, U8)
- Rattletrap.Type.Attribute.ProductValue: hasNewColor :: Version -> Bool
- Rattletrap.Type.Attribute.Reservation: hasUnknown3 :: Version -> Bool
- Rattletrap.Type.Content: [keyFrames] :: ContentWith frames -> List KeyFrame
- Rattletrap.Type.Header: [engineVersion] :: Header -> U32
- Rattletrap.Type.Header: [licenseeVersion] :: Header -> U32
- Rattletrap.Type.Header: [patchVersion] :: Header -> Maybe U32
- Rattletrap.Type.KeyFrame: KeyFrame :: F32 -> U32 -> U32 -> KeyFrame
- Rattletrap.Type.KeyFrame: [frame] :: KeyFrame -> U32
- Rattletrap.Type.KeyFrame: [position] :: KeyFrame -> U32
- Rattletrap.Type.KeyFrame: [time] :: KeyFrame -> F32
- Rattletrap.Type.KeyFrame: byteGet :: ByteGet KeyFrame
- Rattletrap.Type.KeyFrame: bytePut :: KeyFrame -> BytePut
- Rattletrap.Type.KeyFrame: data KeyFrame
- Rattletrap.Type.KeyFrame: instance Data.Aeson.Types.FromJSON.FromJSON Rattletrap.Type.KeyFrame.KeyFrame
- Rattletrap.Type.KeyFrame: instance Data.Aeson.Types.ToJSON.ToJSON Rattletrap.Type.KeyFrame.KeyFrame
- Rattletrap.Type.KeyFrame: instance GHC.Classes.Eq Rattletrap.Type.KeyFrame.KeyFrame
- Rattletrap.Type.KeyFrame: instance GHC.Show.Show Rattletrap.Type.KeyFrame.KeyFrame
- Rattletrap.Type.KeyFrame: schema :: Schema
- Rattletrap.Type.RemoteId: decodePsBytes :: Version -> BitGet [Word8]
- Rattletrap.Type.RemoteId: decodePsName :: BitGet Text
- Rattletrap.Type.RemoteId: getWord256 :: BitGet (U64, U64, U64, U64)
- Rattletrap.Type.RemoteId: playStationIsU24 :: Version -> Bool
- Rattletrap.Type.RemoteId: psyNetIsU64 :: Version -> Bool
- Rattletrap.Type.RemoteId: putWord256 :: U64 -> U64 -> U64 -> U64 -> BitPut
- Rattletrap.Type.RemoteId: uncurry4 :: (a -> b -> c -> d -> e) -> (a, b, c, d) -> e
- Rattletrap.Type.Replay: getPatchVersion :: Header -> Int
- Rattletrap.Type.Replay: getVersion :: Header -> Version
- Rattletrap.Type.Rotation: isQuaternion :: Version -> Bool
- Rattletrap.Type.Vector: has21Bits :: Version -> Bool
- Rattletrap.Utility.Bytes: reverseBytes :: ByteString -> ByteString
+ Rattletrap.BitBuilder: BitBuilder :: Word8 -> Builder -> Int -> BitBuilder
+ Rattletrap.BitBuilder: [buffer] :: BitBuilder -> Word8
+ Rattletrap.BitBuilder: [builder] :: BitBuilder -> Builder
+ Rattletrap.BitBuilder: [offset] :: BitBuilder -> Int
+ Rattletrap.BitBuilder: data BitBuilder
+ Rattletrap.BitBuilder: empty :: BitBuilder
+ Rattletrap.BitBuilder: push :: Bool -> BitBuilder -> BitBuilder
+ Rattletrap.BitBuilder: toBuilder :: BitBuilder -> Builder
+ Rattletrap.BitPut: run :: BitPut -> BitBuilder -> BitBuilder
+ Rattletrap.BitString: BitString :: ByteString -> Int -> BitString
+ Rattletrap.BitString: [byteString] :: BitString -> ByteString
+ Rattletrap.BitString: [offset] :: BitString -> Int
+ Rattletrap.BitString: data BitString
+ Rattletrap.BitString: fromByteString :: ByteString -> BitString
+ Rattletrap.BitString: instance GHC.Classes.Eq Rattletrap.BitString.BitString
+ Rattletrap.BitString: instance GHC.Show.Show Rattletrap.BitString.BitString
+ Rattletrap.BitString: pop :: BitString -> Maybe (Bool, BitString)
+ Rattletrap.Console.Main: defaultMain :: Config -> IO ()
+ Rattletrap.Console.Main: encodeJson :: ToJSON a => Config -> a -> ByteString
+ Rattletrap.Console.Main: getConfig :: [String] -> IO Config
+ Rattletrap.Console.Main: getDecoder :: Config -> ByteString -> Either String Replay
+ Rattletrap.Console.Main: getEncoder :: Config -> Replay -> ByteString
+ Rattletrap.Console.Main: getInput :: Config -> IO ByteString
+ Rattletrap.Console.Main: helpMain :: String -> IO ()
+ Rattletrap.Console.Main: putOutput :: Config -> ByteString -> IO ()
+ Rattletrap.Console.Main: schema :: Value
+ Rattletrap.Console.Main: schemaMain :: Config -> IO ()
+ Rattletrap.Console.Main: versionMain :: IO ()
+ Rattletrap.Type.Attribute.PartyLeader: [localId] :: PartyLeader -> Maybe U8
+ Rattletrap.Type.Attribute.PartyLeader: [remoteId] :: PartyLeader -> Maybe RemoteId
+ Rattletrap.Type.ClassAttributeMap: bimap :: (Ord l, Ord r) => [(l, r)] -> Bimap l r
+ Rattletrap.Type.ClassAttributeMap: getClassId :: Map U32 Str -> Bimap U32 Str -> U32 -> Maybe U32
+ Rattletrap.Type.ClassAttributeMap: getParentClass :: Maybe Str -> U32 -> [(Maybe Str, U32, U32, U32)] -> Maybe U32
+ Rattletrap.Type.ClassAttributeMap: getParentClassById :: U32 -> [(Maybe Str, U32, U32, U32)] -> Maybe U32
+ Rattletrap.Type.ClassAttributeMap: getParentClassByName :: Str -> U32 -> [(Maybe Str, U32, U32, U32)] -> Maybe U32
+ Rattletrap.Type.ClassAttributeMap: getParentClasses :: Map U32 U32 -> U32 -> [U32]
+ Rattletrap.Type.ClassAttributeMap: lookupL :: Ord l => l -> Bimap l r -> Maybe r
+ Rattletrap.Type.ClassAttributeMap: lookupR :: Ord r => r -> Bimap l r -> Maybe l
+ Rattletrap.Type.ClassAttributeMap: makeAttributeMap :: List Cache -> Map U32 (Map U32 U32)
+ Rattletrap.Type.ClassAttributeMap: makeClassCache :: Bimap U32 Str -> List Cache -> [(Maybe Str, U32, U32, U32)]
+ Rattletrap.Type.ClassAttributeMap: makeClassMap :: List ClassMapping -> Bimap U32 Str
+ Rattletrap.Type.ClassAttributeMap: makeNameMap :: List Str -> IntMap Str
+ Rattletrap.Type.ClassAttributeMap: makeObjectClassMap :: Map U32 Str -> Bimap U32 Str -> Map U32 U32
+ Rattletrap.Type.ClassAttributeMap: makeObjectMap :: List Str -> Map U32 Str
+ Rattletrap.Type.ClassAttributeMap: makeParentMap :: [(Maybe Str, U32, U32, U32)] -> Map U32 [U32]
+ Rattletrap.Type.ClassAttributeMap: makeShallowParentMap :: [(Maybe Str, U32, U32, U32)] -> Map U32 U32
+ Rattletrap.Type.ClassAttributeMap: normalizeObjectName :: Str -> Str
+ Rattletrap.Type.ClassAttributeMap: type Bimap l r = (Map l r, Map r l)
+ Rattletrap.Type.Content: [keyframes] :: ContentWith frames -> List Keyframe
+ Rattletrap.Type.Header: [version] :: Header -> Version
+ Rattletrap.Type.Keyframe: Keyframe :: F32 -> U32 -> U32 -> Keyframe
+ Rattletrap.Type.Keyframe: [frame] :: Keyframe -> U32
+ Rattletrap.Type.Keyframe: [position] :: Keyframe -> U32
+ Rattletrap.Type.Keyframe: [time] :: Keyframe -> F32
+ Rattletrap.Type.Keyframe: byteGet :: ByteGet Keyframe
+ Rattletrap.Type.Keyframe: bytePut :: Keyframe -> BytePut
+ Rattletrap.Type.Keyframe: data Keyframe
+ Rattletrap.Type.Keyframe: instance Data.Aeson.Types.FromJSON.FromJSON Rattletrap.Type.Keyframe.Keyframe
+ Rattletrap.Type.Keyframe: instance Data.Aeson.Types.ToJSON.ToJSON Rattletrap.Type.Keyframe.Keyframe
+ Rattletrap.Type.Keyframe: instance GHC.Classes.Eq Rattletrap.Type.Keyframe.Keyframe
+ Rattletrap.Type.Keyframe: instance GHC.Show.Show Rattletrap.Type.Keyframe.Keyframe
+ Rattletrap.Type.Keyframe: schema :: Schema
+ Rattletrap.Type.Property.Array: Array :: List (Dictionary a) -> Array a
+ Rattletrap.Type.Property.Array: byteGet :: ByteGet a -> ByteGet (Array a)
+ Rattletrap.Type.Property.Array: bytePut :: (a -> BytePut) -> Array a -> BytePut
+ Rattletrap.Type.Property.Array: fromList :: List (Dictionary a) -> Array a
+ Rattletrap.Type.Property.Array: instance Data.Aeson.Types.FromJSON.FromJSON a => Data.Aeson.Types.FromJSON.FromJSON (Rattletrap.Type.Property.Array.Array a)
+ Rattletrap.Type.Property.Array: instance Data.Aeson.Types.ToJSON.ToJSON a => Data.Aeson.Types.ToJSON.ToJSON (Rattletrap.Type.Property.Array.Array a)
+ Rattletrap.Type.Property.Array: instance GHC.Classes.Eq a => GHC.Classes.Eq (Rattletrap.Type.Property.Array.Array a)
+ Rattletrap.Type.Property.Array: instance GHC.Show.Show a => GHC.Show.Show (Rattletrap.Type.Property.Array.Array a)
+ Rattletrap.Type.Property.Array: newtype Array a
+ Rattletrap.Type.Property.Array: schema :: Schema -> Schema
+ Rattletrap.Type.Property.Array: toList :: Array a -> List (Dictionary a)
+ Rattletrap.Type.Property.Bool: Bool :: U8 -> Bool
+ Rattletrap.Type.Property.Bool: byteGet :: ByteGet Bool
+ Rattletrap.Type.Property.Bool: bytePut :: Bool -> BytePut
+ Rattletrap.Type.Property.Bool: fromU8 :: U8 -> Bool
+ Rattletrap.Type.Property.Bool: instance Data.Aeson.Types.FromJSON.FromJSON Rattletrap.Type.Property.Bool.Bool
+ Rattletrap.Type.Property.Bool: instance Data.Aeson.Types.ToJSON.ToJSON Rattletrap.Type.Property.Bool.Bool
+ Rattletrap.Type.Property.Bool: instance GHC.Classes.Eq Rattletrap.Type.Property.Bool.Bool
+ Rattletrap.Type.Property.Bool: instance GHC.Show.Show Rattletrap.Type.Property.Bool.Bool
+ Rattletrap.Type.Property.Bool: newtype Bool
+ Rattletrap.Type.Property.Bool: schema :: Schema
+ Rattletrap.Type.Property.Bool: toU8 :: Bool -> U8
+ Rattletrap.Type.Property.Byte: Byte :: Str -> Maybe Str -> Byte
+ Rattletrap.Type.Property.Byte: [key] :: Byte -> Str
+ Rattletrap.Type.Property.Byte: [value] :: Byte -> Maybe Str
+ Rattletrap.Type.Property.Byte: byteGet :: ByteGet Byte
+ Rattletrap.Type.Property.Byte: bytePut :: Byte -> BytePut
+ Rattletrap.Type.Property.Byte: data Byte
+ Rattletrap.Type.Property.Byte: instance Data.Aeson.Types.FromJSON.FromJSON Rattletrap.Type.Property.Byte.Byte
+ Rattletrap.Type.Property.Byte: instance Data.Aeson.Types.ToJSON.ToJSON Rattletrap.Type.Property.Byte.Byte
+ Rattletrap.Type.Property.Byte: instance GHC.Classes.Eq Rattletrap.Type.Property.Byte.Byte
+ Rattletrap.Type.Property.Byte: instance GHC.Show.Show Rattletrap.Type.Property.Byte.Byte
+ Rattletrap.Type.Property.Byte: schema :: Schema
+ Rattletrap.Type.Property.Float: Float :: F32 -> Float
+ Rattletrap.Type.Property.Float: byteGet :: ByteGet Float
+ Rattletrap.Type.Property.Float: bytePut :: Float -> BytePut
+ Rattletrap.Type.Property.Float: fromF32 :: F32 -> Float
+ Rattletrap.Type.Property.Float: instance Data.Aeson.Types.FromJSON.FromJSON Rattletrap.Type.Property.Float.Float
+ Rattletrap.Type.Property.Float: instance Data.Aeson.Types.ToJSON.ToJSON Rattletrap.Type.Property.Float.Float
+ Rattletrap.Type.Property.Float: instance GHC.Classes.Eq Rattletrap.Type.Property.Float.Float
+ Rattletrap.Type.Property.Float: instance GHC.Show.Show Rattletrap.Type.Property.Float.Float
+ Rattletrap.Type.Property.Float: newtype Float
+ Rattletrap.Type.Property.Float: schema :: Schema
+ Rattletrap.Type.Property.Float: toF32 :: Float -> F32
+ Rattletrap.Type.Property.Int: Int :: I32 -> Int
+ Rattletrap.Type.Property.Int: byteGet :: ByteGet Int
+ Rattletrap.Type.Property.Int: bytePut :: Int -> BytePut
+ Rattletrap.Type.Property.Int: fromI32 :: I32 -> Int
+ Rattletrap.Type.Property.Int: instance Data.Aeson.Types.FromJSON.FromJSON Rattletrap.Type.Property.Int.Int
+ Rattletrap.Type.Property.Int: instance Data.Aeson.Types.ToJSON.ToJSON Rattletrap.Type.Property.Int.Int
+ Rattletrap.Type.Property.Int: instance GHC.Classes.Eq Rattletrap.Type.Property.Int.Int
+ Rattletrap.Type.Property.Int: instance GHC.Show.Show Rattletrap.Type.Property.Int.Int
+ Rattletrap.Type.Property.Int: newtype Int
+ Rattletrap.Type.Property.Int: schema :: Schema
+ Rattletrap.Type.Property.Int: toI32 :: Int -> I32
+ Rattletrap.Type.Property.Name: Name :: Str -> Name
+ Rattletrap.Type.Property.Name: byteGet :: ByteGet Name
+ Rattletrap.Type.Property.Name: bytePut :: Name -> BytePut
+ Rattletrap.Type.Property.Name: fromStr :: Str -> Name
+ Rattletrap.Type.Property.Name: instance Data.Aeson.Types.FromJSON.FromJSON Rattletrap.Type.Property.Name.Name
+ Rattletrap.Type.Property.Name: instance Data.Aeson.Types.ToJSON.ToJSON Rattletrap.Type.Property.Name.Name
+ Rattletrap.Type.Property.Name: instance GHC.Classes.Eq Rattletrap.Type.Property.Name.Name
+ Rattletrap.Type.Property.Name: instance GHC.Show.Show Rattletrap.Type.Property.Name.Name
+ Rattletrap.Type.Property.Name: newtype Name
+ Rattletrap.Type.Property.Name: schema :: Schema
+ Rattletrap.Type.Property.Name: toStr :: Name -> Str
+ Rattletrap.Type.Property.QWord: QWord :: U64 -> QWord
+ Rattletrap.Type.Property.QWord: byteGet :: ByteGet QWord
+ Rattletrap.Type.Property.QWord: bytePut :: QWord -> BytePut
+ Rattletrap.Type.Property.QWord: fromU64 :: U64 -> QWord
+ Rattletrap.Type.Property.QWord: instance Data.Aeson.Types.FromJSON.FromJSON Rattletrap.Type.Property.QWord.QWord
+ Rattletrap.Type.Property.QWord: instance Data.Aeson.Types.ToJSON.ToJSON Rattletrap.Type.Property.QWord.QWord
+ Rattletrap.Type.Property.QWord: instance GHC.Classes.Eq Rattletrap.Type.Property.QWord.QWord
+ Rattletrap.Type.Property.QWord: instance GHC.Show.Show Rattletrap.Type.Property.QWord.QWord
+ Rattletrap.Type.Property.QWord: newtype QWord
+ Rattletrap.Type.Property.QWord: schema :: Schema
+ Rattletrap.Type.Property.QWord: toU64 :: QWord -> U64
+ Rattletrap.Type.Property.Str: Str :: Str -> Str
+ Rattletrap.Type.Property.Str: byteGet :: ByteGet Str
+ Rattletrap.Type.Property.Str: bytePut :: Str -> BytePut
+ Rattletrap.Type.Property.Str: fromStr :: Str -> Str
+ Rattletrap.Type.Property.Str: instance Data.Aeson.Types.FromJSON.FromJSON Rattletrap.Type.Property.Str.Str
+ Rattletrap.Type.Property.Str: instance Data.Aeson.Types.ToJSON.ToJSON Rattletrap.Type.Property.Str.Str
+ Rattletrap.Type.Property.Str: instance GHC.Classes.Eq Rattletrap.Type.Property.Str.Str
+ Rattletrap.Type.Property.Str: instance GHC.Show.Show Rattletrap.Type.Property.Str.Str
+ Rattletrap.Type.Property.Str: newtype Str
+ Rattletrap.Type.Property.Str: schema :: Schema
+ Rattletrap.Type.Property.Str: toStr :: Str -> Str
+ Rattletrap.Type.RemoteId.Epic: Epic :: Str -> Epic
+ Rattletrap.Type.RemoteId.Epic: bitGet :: BitGet Epic
+ Rattletrap.Type.RemoteId.Epic: bitPut :: Epic -> BitPut
+ Rattletrap.Type.RemoteId.Epic: fromStr :: Str -> Epic
+ Rattletrap.Type.RemoteId.Epic: instance Data.Aeson.Types.FromJSON.FromJSON Rattletrap.Type.RemoteId.Epic.Epic
+ Rattletrap.Type.RemoteId.Epic: instance Data.Aeson.Types.ToJSON.ToJSON Rattletrap.Type.RemoteId.Epic.Epic
+ Rattletrap.Type.RemoteId.Epic: instance GHC.Classes.Eq Rattletrap.Type.RemoteId.Epic.Epic
+ Rattletrap.Type.RemoteId.Epic: instance GHC.Show.Show Rattletrap.Type.RemoteId.Epic.Epic
+ Rattletrap.Type.RemoteId.Epic: newtype Epic
+ Rattletrap.Type.RemoteId.Epic: schema :: Schema
+ Rattletrap.Type.RemoteId.Epic: toStr :: Epic -> Str
+ Rattletrap.Type.RemoteId.PlayStation: PlayStation :: Text -> [Word8] -> PlayStation
+ Rattletrap.Type.RemoteId.PlayStation: [code] :: PlayStation -> [Word8]
+ Rattletrap.Type.RemoteId.PlayStation: [name] :: PlayStation -> Text
+ Rattletrap.Type.RemoteId.PlayStation: bitGet :: Version -> BitGet PlayStation
+ Rattletrap.Type.RemoteId.PlayStation: bitPut :: PlayStation -> BitPut
+ Rattletrap.Type.RemoteId.PlayStation: data PlayStation
+ Rattletrap.Type.RemoteId.PlayStation: getCode :: BitGet Text
+ Rattletrap.Type.RemoteId.PlayStation: getName :: Version -> BitGet [Word8]
+ Rattletrap.Type.RemoteId.PlayStation: instance Data.Aeson.Types.FromJSON.FromJSON Rattletrap.Type.RemoteId.PlayStation.PlayStation
+ Rattletrap.Type.RemoteId.PlayStation: instance Data.Aeson.Types.ToJSON.ToJSON Rattletrap.Type.RemoteId.PlayStation.PlayStation
+ Rattletrap.Type.RemoteId.PlayStation: instance GHC.Classes.Eq Rattletrap.Type.RemoteId.PlayStation.PlayStation
+ Rattletrap.Type.RemoteId.PlayStation: instance GHC.Show.Show Rattletrap.Type.RemoteId.PlayStation.PlayStation
+ Rattletrap.Type.RemoteId.PlayStation: schema :: Schema
+ Rattletrap.Type.RemoteId.PsyNet: PsyNet :: Either U64 (U64, U64, U64, U64) -> PsyNet
+ Rattletrap.Type.RemoteId.PsyNet: bitGet :: Version -> BitGet PsyNet
+ Rattletrap.Type.RemoteId.PsyNet: bitPut :: PsyNet -> BitPut
+ Rattletrap.Type.RemoteId.PsyNet: fromEither :: Either U64 (U64, U64, U64, U64) -> PsyNet
+ Rattletrap.Type.RemoteId.PsyNet: instance Data.Aeson.Types.FromJSON.FromJSON Rattletrap.Type.RemoteId.PsyNet.PsyNet
+ Rattletrap.Type.RemoteId.PsyNet: instance Data.Aeson.Types.ToJSON.ToJSON Rattletrap.Type.RemoteId.PsyNet.PsyNet
+ Rattletrap.Type.RemoteId.PsyNet: instance GHC.Classes.Eq Rattletrap.Type.RemoteId.PsyNet.PsyNet
+ Rattletrap.Type.RemoteId.PsyNet: instance GHC.Show.Show Rattletrap.Type.RemoteId.PsyNet.PsyNet
+ Rattletrap.Type.RemoteId.PsyNet: newtype PsyNet
+ Rattletrap.Type.RemoteId.PsyNet: schema :: Schema
+ Rattletrap.Type.RemoteId.PsyNet: toEither :: PsyNet -> Either U64 (U64, U64, U64, U64)
+ Rattletrap.Type.RemoteId.Splitscreen: Splitscreen :: Word32 -> Splitscreen
+ Rattletrap.Type.RemoteId.Splitscreen: bitGet :: BitGet Splitscreen
+ Rattletrap.Type.RemoteId.Splitscreen: bitPut :: Splitscreen -> BitPut
+ Rattletrap.Type.RemoteId.Splitscreen: fromWord32 :: Word32 -> Splitscreen
+ Rattletrap.Type.RemoteId.Splitscreen: instance Data.Aeson.Types.FromJSON.FromJSON Rattletrap.Type.RemoteId.Splitscreen.Splitscreen
+ Rattletrap.Type.RemoteId.Splitscreen: instance Data.Aeson.Types.ToJSON.ToJSON Rattletrap.Type.RemoteId.Splitscreen.Splitscreen
+ Rattletrap.Type.RemoteId.Splitscreen: instance GHC.Classes.Eq Rattletrap.Type.RemoteId.Splitscreen.Splitscreen
+ Rattletrap.Type.RemoteId.Splitscreen: instance GHC.Show.Show Rattletrap.Type.RemoteId.Splitscreen.Splitscreen
+ Rattletrap.Type.RemoteId.Splitscreen: newtype Splitscreen
+ Rattletrap.Type.RemoteId.Splitscreen: schema :: Schema
+ Rattletrap.Type.RemoteId.Splitscreen: toWord32 :: Splitscreen -> Word32
+ Rattletrap.Type.RemoteId.Steam: Steam :: U64 -> Steam
+ Rattletrap.Type.RemoteId.Steam: bitGet :: BitGet Steam
+ Rattletrap.Type.RemoteId.Steam: bitPut :: Steam -> BitPut
+ Rattletrap.Type.RemoteId.Steam: fromU64 :: U64 -> Steam
+ Rattletrap.Type.RemoteId.Steam: instance Data.Aeson.Types.FromJSON.FromJSON Rattletrap.Type.RemoteId.Steam.Steam
+ Rattletrap.Type.RemoteId.Steam: instance Data.Aeson.Types.ToJSON.ToJSON Rattletrap.Type.RemoteId.Steam.Steam
+ Rattletrap.Type.RemoteId.Steam: instance GHC.Classes.Eq Rattletrap.Type.RemoteId.Steam.Steam
+ Rattletrap.Type.RemoteId.Steam: instance GHC.Show.Show Rattletrap.Type.RemoteId.Steam.Steam
+ Rattletrap.Type.RemoteId.Steam: newtype Steam
+ Rattletrap.Type.RemoteId.Steam: schema :: Schema
+ Rattletrap.Type.RemoteId.Steam: toU64 :: Steam -> U64
+ Rattletrap.Type.RemoteId.Switch: Switch :: U64 -> U64 -> U64 -> U64 -> Switch
+ Rattletrap.Type.RemoteId.Switch: [a] :: Switch -> U64
+ Rattletrap.Type.RemoteId.Switch: [b] :: Switch -> U64
+ Rattletrap.Type.RemoteId.Switch: [c] :: Switch -> U64
+ Rattletrap.Type.RemoteId.Switch: [d] :: Switch -> U64
+ Rattletrap.Type.RemoteId.Switch: bitGet :: BitGet Switch
+ Rattletrap.Type.RemoteId.Switch: bitPut :: Switch -> BitPut
+ Rattletrap.Type.RemoteId.Switch: data Switch
+ Rattletrap.Type.RemoteId.Switch: instance Data.Aeson.Types.FromJSON.FromJSON Rattletrap.Type.RemoteId.Switch.Switch
+ Rattletrap.Type.RemoteId.Switch: instance Data.Aeson.Types.ToJSON.ToJSON Rattletrap.Type.RemoteId.Switch.Switch
+ Rattletrap.Type.RemoteId.Switch: instance GHC.Classes.Eq Rattletrap.Type.RemoteId.Switch.Switch
+ Rattletrap.Type.RemoteId.Switch: instance GHC.Show.Show Rattletrap.Type.RemoteId.Switch.Switch
+ Rattletrap.Type.RemoteId.Switch: schema :: Schema
+ Rattletrap.Type.RemoteId.Xbox: Xbox :: U64 -> Xbox
+ Rattletrap.Type.RemoteId.Xbox: bitGet :: BitGet Xbox
+ Rattletrap.Type.RemoteId.Xbox: bitPut :: Xbox -> BitPut
+ Rattletrap.Type.RemoteId.Xbox: fromU64 :: U64 -> Xbox
+ Rattletrap.Type.RemoteId.Xbox: instance Data.Aeson.Types.FromJSON.FromJSON Rattletrap.Type.RemoteId.Xbox.Xbox
+ Rattletrap.Type.RemoteId.Xbox: instance Data.Aeson.Types.ToJSON.ToJSON Rattletrap.Type.RemoteId.Xbox.Xbox
+ Rattletrap.Type.RemoteId.Xbox: instance GHC.Classes.Eq Rattletrap.Type.RemoteId.Xbox.Xbox
+ Rattletrap.Type.RemoteId.Xbox: instance GHC.Show.Show Rattletrap.Type.RemoteId.Xbox.Xbox
+ Rattletrap.Type.RemoteId.Xbox: newtype Xbox
+ Rattletrap.Type.RemoteId.Xbox: schema :: Schema
+ Rattletrap.Type.RemoteId.Xbox: toU64 :: Xbox -> U64
+ Rattletrap.Type.Replay: getMatchType :: Header -> Maybe Str
+ Rattletrap.Type.Vector: getPart :: Word -> Word -> BitGet Int
+ Rattletrap.Type.Version: atLeast :: Int -> Int -> Int -> Version -> Bool
+ Rattletrap.Type.Version: byteGet :: ByteGet Version
+ Rattletrap.Type.Version: bytePut :: Version -> BytePut
+ Rattletrap.Utility.Crc: initial :: Word32
+ Rattletrap.Utility.Crc: table :: Array Word8 Word32
+ Rattletrap.Utility.Crc: unsafeWord32ToWord8 :: Word32 -> Word8
+ Rattletrap.Utility.Crc: update :: Word32 -> Word8 -> Word32
+ Rattletrap.Utility.Json: decode :: FromJSON a => ByteString -> Either String a
+ Rattletrap.Utility.Json: encode :: ToJSON a => a -> ByteString
+ Rattletrap.Utility.Json: encodePretty :: ToJSON a => a -> ByteString
- Rattletrap.BitGet: type BitGet = BitGet
+ Rattletrap.BitGet: type BitGet = Get BitString Identity
- Rattletrap.BitPut: BitPut :: BitPut () -> BitPut
+ Rattletrap.BitPut: BitPut :: (BitBuilder -> BitBuilder) -> BitPut
- Rattletrap.Type.Attribute.PartyLeader: PartyLeader :: U8 -> Maybe (RemoteId, U8) -> PartyLeader
+ Rattletrap.Type.Attribute.PartyLeader: PartyLeader :: U8 -> Maybe RemoteId -> Maybe U8 -> PartyLeader
- Rattletrap.Type.Content: Content :: List Str -> List KeyFrame -> U32 -> frames -> List Message -> List Mark -> List Str -> List Str -> List Str -> List ClassMapping -> List Cache -> [Word8] -> ContentWith frames
+ Rattletrap.Type.Content: Content :: List Str -> List Keyframe -> U32 -> frames -> List Message -> List Mark -> List Str -> List Str -> List Str -> List ClassMapping -> List Cache -> [Word8] -> ContentWith frames
- Rattletrap.Type.Content: byteGet :: Version -> Int -> Word -> ByteGet Content
+ Rattletrap.Type.Content: byteGet :: Maybe Str -> Version -> Int -> Word -> ByteGet Content
- Rattletrap.Type.Frame: bitGet :: Version -> Word -> ClassAttributeMap -> StateT (Map CompressedWord U32) BitGet Frame
+ Rattletrap.Type.Frame: bitGet :: Maybe Str -> Version -> Word -> ClassAttributeMap -> StateT (Map CompressedWord U32) BitGet Frame
- Rattletrap.Type.Frame: decodeFramesBits :: Version -> Int -> Word -> ClassAttributeMap -> StateT (Map CompressedWord U32) BitGet (List Frame)
+ Rattletrap.Type.Frame: decodeFramesBits :: Maybe Str -> Version -> Int -> Word -> ClassAttributeMap -> StateT (Map CompressedWord U32) BitGet (List Frame)
- Rattletrap.Type.Header: Header :: U32 -> U32 -> Maybe U32 -> Str -> Dictionary Property -> Header
+ Rattletrap.Type.Header: Header :: Version -> Str -> Dictionary Property -> Header
- Rattletrap.Type.PropertyValue: Array :: List (Dictionary a) -> PropertyValue a
+ Rattletrap.Type.PropertyValue: Array :: Array a -> PropertyValue a
- Rattletrap.Type.PropertyValue: Bool :: U8 -> PropertyValue a
+ Rattletrap.Type.PropertyValue: Bool :: Bool -> PropertyValue a
- Rattletrap.Type.PropertyValue: Byte :: Str -> Maybe Str -> PropertyValue a
+ Rattletrap.Type.PropertyValue: Byte :: Byte -> PropertyValue a
- Rattletrap.Type.PropertyValue: Float :: F32 -> PropertyValue a
+ Rattletrap.Type.PropertyValue: Float :: Float -> PropertyValue a
- Rattletrap.Type.PropertyValue: Int :: I32 -> PropertyValue a
+ Rattletrap.Type.PropertyValue: Int :: Int -> PropertyValue a
- Rattletrap.Type.PropertyValue: Name :: Str -> PropertyValue a
+ Rattletrap.Type.PropertyValue: Name :: Name -> PropertyValue a
- Rattletrap.Type.PropertyValue: QWord :: U64 -> PropertyValue a
+ Rattletrap.Type.PropertyValue: QWord :: QWord -> PropertyValue a
- Rattletrap.Type.RemoteId: Epic :: Str -> RemoteId
+ Rattletrap.Type.RemoteId: Epic :: Epic -> RemoteId
- Rattletrap.Type.RemoteId: PlayStation :: Text -> [Word8] -> RemoteId
+ Rattletrap.Type.RemoteId: PlayStation :: PlayStation -> RemoteId
- Rattletrap.Type.RemoteId: PsyNet :: Either U64 (U64, U64, U64, U64) -> RemoteId
+ Rattletrap.Type.RemoteId: PsyNet :: PsyNet -> RemoteId
- Rattletrap.Type.RemoteId: Splitscreen :: Word32 -> RemoteId
+ Rattletrap.Type.RemoteId: Splitscreen :: Splitscreen -> RemoteId
- Rattletrap.Type.RemoteId: Steam :: U64 -> RemoteId
+ Rattletrap.Type.RemoteId: Steam :: Steam -> RemoteId
- Rattletrap.Type.RemoteId: Switch :: U64 -> U64 -> U64 -> U64 -> RemoteId
+ Rattletrap.Type.RemoteId: Switch :: Switch -> RemoteId
- Rattletrap.Type.RemoteId: Xbox :: U64 -> RemoteId
+ Rattletrap.Type.RemoteId: Xbox :: Xbox -> RemoteId
- Rattletrap.Type.Replication: bitGet :: Version -> Word -> ClassAttributeMap -> StateT (Map CompressedWord U32) BitGet Replication
+ Rattletrap.Type.Replication: bitGet :: Maybe Str -> Version -> Word -> ClassAttributeMap -> StateT (Map CompressedWord U32) BitGet Replication
- Rattletrap.Type.Replication: decodeReplicationsBits :: Version -> Word -> ClassAttributeMap -> StateT (Map CompressedWord U32) BitGet (List Replication)
+ Rattletrap.Type.Replication: decodeReplicationsBits :: Maybe Str -> Version -> Word -> ClassAttributeMap -> StateT (Map CompressedWord U32) BitGet (List Replication)
- Rattletrap.Type.Replication.Spawned: bitGet :: Version -> ClassAttributeMap -> CompressedWord -> StateT (Map CompressedWord U32) BitGet Spawned
+ Rattletrap.Type.Replication.Spawned: bitGet :: Maybe Str -> Version -> ClassAttributeMap -> CompressedWord -> StateT (Map CompressedWord U32) BitGet Spawned
- Rattletrap.Type.Replication.Spawned: hasNameIndex :: Version -> Bool
+ Rattletrap.Type.Replication.Spawned: hasNameIndex :: Maybe Str -> Version -> Bool
- Rattletrap.Type.ReplicationValue: bitGet :: Version -> ClassAttributeMap -> CompressedWord -> StateT (Map CompressedWord U32) BitGet ReplicationValue
+ Rattletrap.Type.ReplicationValue: bitGet :: Maybe Str -> Version -> ClassAttributeMap -> CompressedWord -> StateT (Map CompressedWord U32) BitGet ReplicationValue
- Rattletrap.Type.Version: Version :: Int -> Int -> Int -> Version
+ Rattletrap.Type.Version: Version :: U32 -> U32 -> Maybe U32 -> Version
- Rattletrap.Type.Version: [major] :: Version -> Int
+ Rattletrap.Type.Version: [major] :: Version -> U32
- Rattletrap.Type.Version: [minor] :: Version -> Int
+ Rattletrap.Type.Version: [minor] :: Version -> U32
- Rattletrap.Type.Version: [patch] :: Version -> Int
+ Rattletrap.Type.Version: [patch] :: Version -> Maybe U32

Files

README.markdown view
@@ -2,6 +2,7 @@  [![Version badge][]][version] [![Build badge][]][build]+[![Docker badge][]][docker]  Rattletrap parses and generates [Rocket League][] replays. Parsing replays can be used to analyze data in order to collect high-level statistics like players@@ -23,7 +24,7 @@  Get Rattletrap by downloading [the latest release][] for your platform. -Rattletrap is also available as [a Docker image][].+Rattletrap is also available as [a Docker image][docker].  To build Rattletrap from source, install [Stack][]. Then run `stack --resolver lts-14.25 install rattletrap`.@@ -125,10 +126,12 @@ ```  [Rattletrap]: https://github.com/tfausak/rattletrap-[Version badge]: https://img.shields.io/hackage/v/rattletrap.svg?logo=haskell&label=version&color=brightgreen+[Version badge]: https://img.shields.io/hackage/v/rattletrap.svg?logo=haskell [version]: https://hackage.haskell.org/package/rattletrap [Build badge]: https://github.com/tfausak/rattletrap/workflows/ci/badge.svg [build]: https://github.com/tfausak/rattletrap/actions+[Docker badge]: https://img.shields.io/docker/v/taylorfausak/rattletrap?label=docker&logo=docker&logoColor=white+[docker]: https://hub.docker.com/r/taylorfausak/rattletrap [Rocket League]: https://www.rocketleague.com [1.92]: https://www.rocketleague.com/news/patch-notes-v1-92/ [Ball Chasing]: https://ballchasing.com@@ -136,5 +139,4 @@ [C# parser]: https://github.com/jjbott/RocketLeagueReplayParser [Rust parser]: https://github.com/nickbabcock/rrrocket [the latest release]: https://github.com/tfausak/rattletrap/releases/latest-[a Docker image]: https://hub.docker.com/r/taylorfausak/rattletrap [Stack]: https://docs.haskellstack.org/en/stable/README/
rattletrap.cabal view
@@ -1,7 +1,7 @@ cabal-version: 2.0  name: rattletrap-version: 10.0.7+version: 11.0.0 synopsis: Parse and generate Rocket League replays. description: Rattletrap parses and generates Rocket League replays. @@ -23,9 +23,7 @@     , aeson >= 1.5.5 && < 1.6     , aeson-pretty >= 0.8.8 && < 0.9     , array >= 0.5.4 && < 0.6-    , binary >= 0.8.8 && < 0.9     , bytestring >= 0.10.12 && < 0.11-    , caerbannog >= 0.6.0 && < 0.7     , containers >= 0.6.2 && < 0.7     , filepath >= 1.4.2 && < 1.5     , http-client >= 0.6.4 && < 0.8@@ -37,8 +35,10 @@   exposed-modules:     Paths_rattletrap     Rattletrap+    Rattletrap.BitBuilder     Rattletrap.BitGet     Rattletrap.BitPut+    Rattletrap.BitString     Rattletrap.ByteGet     Rattletrap.BytePut     Rattletrap.Console.Config@@ -107,14 +107,29 @@     Rattletrap.Type.I8     Rattletrap.Type.Initialization     Rattletrap.Type.Int8Vector-    Rattletrap.Type.KeyFrame+    Rattletrap.Type.Keyframe     Rattletrap.Type.List     Rattletrap.Type.Mark     Rattletrap.Type.Message     Rattletrap.Type.Property+    Rattletrap.Type.Property.Array+    Rattletrap.Type.Property.Bool+    Rattletrap.Type.Property.Byte+    Rattletrap.Type.Property.Float+    Rattletrap.Type.Property.Int+    Rattletrap.Type.Property.Name+    Rattletrap.Type.Property.QWord+    Rattletrap.Type.Property.Str     Rattletrap.Type.PropertyValue     Rattletrap.Type.Quaternion     Rattletrap.Type.RemoteId+    Rattletrap.Type.RemoteId.Epic+    Rattletrap.Type.RemoteId.PlayStation+    Rattletrap.Type.RemoteId.PsyNet+    Rattletrap.Type.RemoteId.Splitscreen+    Rattletrap.Type.RemoteId.Steam+    Rattletrap.Type.RemoteId.Switch+    Rattletrap.Type.RemoteId.Xbox     Rattletrap.Type.Replay     Rattletrap.Type.Replication     Rattletrap.Type.Replication.Destroyed
src/exe/Main.hs view
@@ -1,5 +1,4 @@-module Main-  ( main-  ) where+import qualified Rattletrap -import Rattletrap (main)+main :: IO ()+main = Rattletrap.main
+ src/lib/Rattletrap/BitBuilder.hs view
@@ -0,0 +1,33 @@+{- hlint ignore "Avoid restricted extensions" -}+{-# LANGUAGE BangPatterns #-}++module Rattletrap.BitBuilder where++import qualified Data.Bits as Bits+import qualified Data.ByteString.Builder as Builder+import qualified Data.Word as Word++data BitBuilder = BitBuilder+  { buffer :: Word.Word8+  , builder :: Builder.Builder+  , offset :: Int+  }++empty :: BitBuilder+empty = BitBuilder { buffer = 0x00, builder = mempty, offset = 0 }++push :: Bool -> BitBuilder -> BitBuilder+push b x =+  let !newBuffer = if b then Bits.setBit (buffer x) (offset x) else buffer x+  in+    if offset x == 7+      then BitBuilder+        { buffer = 0x00+        , builder = builder x <> Builder.word8 newBuffer+        , offset = 0+        }+      else x { buffer = newBuffer, offset = offset x + 1 }++toBuilder :: BitBuilder -> Builder.Builder+toBuilder x =+  if offset x == 0 then builder x else builder x <> Builder.word8 (buffer x)
src/lib/Rattletrap/BitGet.hs view
@@ -1,48 +1,45 @@ module Rattletrap.BitGet where  import qualified Control.Monad as Monad-import qualified Data.Binary.Bits.Get as BinaryBits-import qualified Data.Binary.Get as Binary import qualified Data.Bits as Bits import qualified Data.ByteString as ByteString-import qualified Data.ByteString.Lazy as LazyByteString-import qualified Data.Word as Word+import qualified Data.Functor.Identity as Identity+import qualified Rattletrap.BitString as BitString import qualified Rattletrap.ByteGet as ByteGet import qualified Rattletrap.Get as Get-import qualified Rattletrap.Utility.Bytes as Utility -type BitGet = BinaryBits.BitGet+type BitGet = Get.Get BitString.BitString Identity.Identity  toByteGet :: BitGet a -> ByteGet.ByteGet a-toByteGet = binaryGetToByteGet . BinaryBits.runBitGet--binaryGetToByteGet :: Binary.Get a -> ByteGet.ByteGet a-binaryGetToByteGet g = do+toByteGet g = do   s1 <- Get.get-  case Binary.runGetOrFail g $ LazyByteString.fromStrict s1 of-    Left (_, _, x) -> fail x-    Right (s2, _, x) -> do-      Get.put $ LazyByteString.toStrict s2+  case Identity.runIdentity . Get.run g $ BitString.fromByteString s1 of+    Left e -> fail e+    Right (s2, x) -> do+      Get.put $ BitString.byteString s2       pure x  fromByteGet :: ByteGet.ByteGet a -> Int -> BitGet a fromByteGet f n = do-  x <- BinaryBits.getByteString n-  either fail pure . ByteGet.run f $ Utility.reverseBytes x+  x <- byteString n+  either fail pure $ ByteGet.run f x  bits :: Bits.Bits a => Int -> BitGet a-bits n =-  foldr-      (\bit x -> let y = Bits.shiftL x 1 in if bit then Bits.setBit y 0 else y-      )-      Bits.zeroBits-    <$> Monad.replicateM n bool+bits n = do+  let+    f :: Bits.Bits a => Bool -> a -> a+    f bit x = let y = Bits.shiftL x 1 in if bit then Bits.setBit y 0 else y+  xs <- Monad.replicateM n bool+  pure $ foldr f Bits.zeroBits xs  bool :: BitGet Bool-bool = BinaryBits.getBool+bool = do+  s1 <- Get.get+  case BitString.pop s1 of+    Nothing -> fail "BitGet.bool"+    Just (x, s2) -> do+      Get.put s2+      pure x  byteString :: Int -> BitGet ByteString.ByteString-byteString = BinaryBits.getByteString--word8 :: Int -> BitGet Word.Word8-word8 = BinaryBits.getWord8+byteString n = fmap ByteString.pack . Monad.replicateM n $ bits 8
src/lib/Rattletrap/BitPut.hs view
@@ -1,41 +1,32 @@ module Rattletrap.BitPut where -import qualified Data.Binary.Bits.Put as BinaryBits-import qualified Data.Binary.Put as Binary import qualified Data.Bits as Bits import qualified Data.ByteString as ByteString-import qualified Data.Word as Word+import qualified Rattletrap.BitBuilder as BitBuilder import qualified Rattletrap.BytePut as BytePut-import qualified Rattletrap.Utility.Bytes as Utility -newtype BitPut = BitPut (BinaryBits.BitPut ())+newtype BitPut = BitPut (BitBuilder.BitBuilder -> BitBuilder.BitBuilder)  instance Semigroup BitPut where-  x <> y = fromBinaryBits $ toBinaryBits x >> toBinaryBits y+  f1 <> f2 = BitPut $ run f2 . run f1  instance Monoid BitPut where-  mempty = fromBinaryBits $ pure ()--fromBinaryBits :: BinaryBits.BitPut () -> BitPut-fromBinaryBits = BitPut+  mempty = BitPut id -toBinaryBits :: BitPut -> BinaryBits.BitPut ()-toBinaryBits (BitPut x) = x+run :: BitPut -> BitBuilder.BitBuilder -> BitBuilder.BitBuilder+run (BitPut f) = f  toBytePut :: BitPut -> BytePut.BytePut-toBytePut = Binary.execPut . BinaryBits.runBitPut . toBinaryBits+toBytePut b = BitBuilder.toBuilder $ run b BitBuilder.empty  fromBytePut :: BytePut.BytePut -> BitPut-fromBytePut = byteString . Utility.reverseBytes . BytePut.toByteString+fromBytePut = byteString . BytePut.toByteString  bits :: Bits.Bits a => Int -> a -> BitPut bits n x = foldMap (bool . Bits.testBit x) [0 .. n - 1]  bool :: Bool -> BitPut-bool = fromBinaryBits . BinaryBits.putBool+bool = BitPut . BitBuilder.push  byteString :: ByteString.ByteString -> BitPut-byteString = fromBinaryBits . BinaryBits.putByteString--word8 :: Int -> Word.Word8 -> BitPut-word8 n = fromBinaryBits . BinaryBits.putWord8 n+byteString = foldMap (bits 8) . ByteString.unpack
+ src/lib/Rattletrap/BitString.hs view
@@ -0,0 +1,23 @@+module Rattletrap.BitString where++import qualified Data.Bits as Bits+import qualified Data.ByteString as ByteString++data BitString = BitString+  { byteString :: ByteString.ByteString+  , offset :: Int+  }+  deriving (Eq, Show)++fromByteString :: ByteString.ByteString -> BitString+fromByteString byteString = BitString { byteString, offset = 0 }++pop :: BitString -> Maybe (Bool, BitString)+pop old = do+  (word, byteString) <- ByteString.uncons $ byteString old+  let+    bit = Bits.testBit word $ offset old+    new = if offset old == 7+      then fromByteString byteString+      else old { offset = offset old + 1 }+  pure (bit, new)
src/lib/Rattletrap/ByteGet.hs view
@@ -22,16 +22,16 @@   pure x  float :: ByteGet Float-float = Float.castWord32ToFloat <$> word32+float = fmap Float.castWord32ToFloat word32  int8 :: ByteGet Int.Int8-int8 = fromIntegral <$> word8+int8 = fmap fromIntegral word8  int32 :: ByteGet Int.Int32-int32 = fromIntegral <$> word32+int32 = fmap fromIntegral word32  int64 :: ByteGet Int.Int64-int64 = fromIntegral <$> word64+int64 = fmap fromIntegral word64  remaining :: ByteGet LazyByteString.ByteString remaining = do@@ -40,7 +40,7 @@   pure $ LazyByteString.fromStrict x  word8 :: ByteGet Word.Word8-word8 = ByteString.head <$> byteString 1+word8 = fmap ByteString.head $ byteString 1  word32 :: ByteGet Word.Word32 word32 = do
src/lib/Rattletrap/Console/Config.hs view
@@ -47,8 +47,8 @@ getMode :: Config -> Mode.Mode getMode config =   let-    i = FilePath.takeExtension <$> input config-    o = FilePath.takeExtension <$> output config+    i = fmap FilePath.takeExtension $ input config+    o = fmap FilePath.takeExtension $ output config   in case (i, o) of     (Just ".json", _) -> Mode.Encode     (Just ".replay", _) -> Mode.Decode
src/lib/Rattletrap/Console/Main.hs view
@@ -1,13 +1,10 @@-module Rattletrap.Console.Main-  ( main-  , rattletrap-  ) where+module Rattletrap.Console.Main where  import qualified Control.Monad as Monad-import qualified Data.Aeson as Aeson-import qualified Data.Aeson.Encode.Pretty as Aeson+import qualified Data.Bool as Bool import qualified Data.ByteString as ByteString import qualified Data.ByteString.Lazy as LazyByteString+import qualified Data.Text as Text import qualified Network.HTTP.Client as Client import qualified Network.HTTP.Client.TLS as Client import qualified Rattletrap.Console.Config as Config@@ -70,14 +67,19 @@ import qualified Rattletrap.Type.I8 as I8 import qualified Rattletrap.Type.Initialization as Initialization import qualified Rattletrap.Type.Int8Vector as Int8Vector-import qualified Rattletrap.Type.KeyFrame as KeyFrame+import qualified Rattletrap.Type.Keyframe as Keyframe import qualified Rattletrap.Type.List as List import qualified Rattletrap.Type.Mark as Mark import qualified Rattletrap.Type.Message as Message import qualified Rattletrap.Type.Property as Property+import qualified Rattletrap.Type.Property.Array as Property.Array+import qualified Rattletrap.Type.Property.Byte as Property.Byte import qualified Rattletrap.Type.PropertyValue as PropertyValue import qualified Rattletrap.Type.Quaternion as Quaternion import qualified Rattletrap.Type.RemoteId as RemoteId+import qualified Rattletrap.Type.RemoteId.PlayStation as RemoteId.PlayStation+import qualified Rattletrap.Type.RemoteId.PsyNet as RemoteId.PsyNet+import qualified Rattletrap.Type.RemoteId.Switch as RemoteId.Switch import qualified Rattletrap.Type.Replay as Replay import qualified Rattletrap.Type.Replication as Replication import qualified Rattletrap.Type.Replication.Destroyed as Replication.Destroyed@@ -127,18 +129,7 @@   putStrLn Version.string  schemaMain :: Config.Config -> IO ()-schemaMain config = do-  let-    json = Aeson.encodePretty'-      Aeson.defConfig-        { Aeson.confCompare = compare-        , Aeson.confIndent = Aeson.Tab-        , Aeson.confTrailingNewline = True-        }-      schema-  case Config.output config of-    Nothing -> LazyByteString.putStr json-    Just file -> LazyByteString.writeFile file json+schemaMain config = putOutput config $ encodeJson config schema  defaultMain :: Config.Config -> IO () defaultMain config = do@@ -148,16 +139,16 @@   let encode = getEncoder config   putOutput config (encode replay) -schema :: Aeson.Value+schema :: Json.Value schema =   let contentSchema = Content.schema $ List.schema Frame.schema   in-    Aeson.object-      [ Json.pair "$schema" "https://json-schema.org/draft-07/schema"+    Json.object+      [ Json.pair "$schema" "http://json-schema.org/draft-07/schema"       , Json.pair "$id" Replay.schemaUrl       , Json.pair "$ref" "#/definitions/replay"-      , Json.pair "definitions" . Aeson.object $ fmap-        (\s -> Schema.name s Aeson..= Schema.json s)+      , Json.pair "definitions" . Json.object $ fmap+        (\s -> Json.pair (Text.unpack $ Schema.name s) $ Schema.json s)         [ Attribute.schema         , Attribute.AppliedDamage.schema         , Attribute.Boolean.schema@@ -214,14 +205,19 @@         , I8.schema         , Initialization.schema         , Int8Vector.schema-        , KeyFrame.schema+        , Keyframe.schema         , List.schema Attribute.Product.schema         , Mark.schema         , Message.schema         , Property.schema+        , Property.Array.schema Property.schema+        , Property.Byte.schema         , PropertyValue.schema Property.schema         , Quaternion.schema         , RemoteId.schema+        , RemoteId.PlayStation.schema+        , RemoteId.PsyNet.schema+        , RemoteId.Switch.schema         , Replay.schema (Section.schema Header.schema)         . Section.schema         $ contentSchema@@ -255,8 +251,7 @@  getEncoder :: Config.Config -> Replay.Replay -> LazyByteString.ByteString getEncoder config = case Config.getMode config of-  Mode.Decode ->-    if Config.compact config then Aeson.encode else Rattletrap.encodeReplayJson+  Mode.Decode -> encodeJson config   Mode.Encode -> Rattletrap.encodeReplayFile $ Config.fast config  getInput :: Config.Config -> IO ByteString.ByteString@@ -272,6 +267,9 @@ putOutput :: Config.Config -> LazyByteString.ByteString -> IO () putOutput =   maybe LazyByteString.putStr LazyByteString.writeFile . Config.output++encodeJson :: Json.ToJSON a => Config.Config -> a -> LazyByteString.ByteString+encodeJson = Bool.bool Json.encodePretty Json.encode . Config.compact  getConfig :: [String] -> IO Config.Config getConfig arguments = do
src/lib/Rattletrap/Type/Attribute.hs view
@@ -57,13 +57,13 @@ bitGet version classes actors actor = do   attributes <- lookupAttributeMap classes actors actor   limit <- lookupAttributeIdLimit attributes actor-  attribute <- CompressedWord.bitGet limit-  name_ <- lookupAttributeName classes attributes attribute-  Attribute attribute name_-    <$> AttributeValue.bitGet-          version-          (ClassAttributeMap.objectMap classes)-          name_+  id <- CompressedWord.bitGet limit+  name <- lookupAttributeName classes attributes id+  value <- AttributeValue.bitGet+    version+    (ClassAttributeMap.objectMap classes)+    name+  pure Attribute { id, name, value }  lookupAttributeMap   :: ClassAttributeMap.ClassAttributeMap
src/lib/Rattletrap/Type/Attribute/AppliedDamage.hs view
@@ -49,9 +49,9 @@     <> I32.bitPut (unknown4 appliedDamageAttribute)  bitGet :: Version.Version -> BitGet.BitGet AppliedDamage-bitGet version =-  AppliedDamage-    <$> U8.bitGet-    <*> Vector.bitGet version-    <*> I32.bitGet-    <*> I32.bitGet+bitGet version = do+  unknown1 <- U8.bitGet+  location <- Vector.bitGet version+  unknown3 <- I32.bitGet+  unknown4 <- I32.bitGet+  pure AppliedDamage { unknown1, location, unknown3, unknown4 }
src/lib/Rattletrap/Type/Attribute/Boolean.hs view
@@ -22,4 +22,6 @@ bitPut booleanAttribute = BitPut.bool (value booleanAttribute)  bitGet :: BitGet.BitGet Boolean-bitGet = Boolean <$> BitGet.bool+bitGet = do+  value <- BitGet.bool+  pure Boolean { value }
src/lib/Rattletrap/Type/Attribute/Byte.hs view
@@ -23,4 +23,6 @@ bitPut byteAttribute = U8.bitPut (value byteAttribute)  bitGet :: BitGet.BitGet Byte-bitGet = Byte <$> U8.bitGet+bitGet = do+  value <- U8.bitGet+  pure Byte { value }
src/lib/Rattletrap/Type/Attribute/CamSettings.hs view
@@ -6,7 +6,7 @@ import qualified Rattletrap.Type.F32 as F32 import qualified Rattletrap.Type.Version as Version import qualified Rattletrap.Utility.Json as Json-import Rattletrap.Utility.Monad+import qualified Rattletrap.Utility.Monad as Monad  data CamSettings = CamSettings   { fov :: F32.F32@@ -73,16 +73,22 @@     <> foldMap F32.bitPut (transitionSpeed camSettingsAttribute)  bitGet :: Version.Version -> BitGet.BitGet CamSettings-bitGet version =-  CamSettings-    <$> F32.bitGet-    <*> F32.bitGet-    <*> F32.bitGet-    <*> F32.bitGet-    <*> F32.bitGet-    <*> F32.bitGet-    <*> whenMaybe (hasTransitionSpeed version) F32.bitGet--hasTransitionSpeed :: Version.Version -> Bool-hasTransitionSpeed v =-  Version.major v >= 868 && Version.minor v >= 20 && Version.patch v >= 0+bitGet version = do+  fov <- F32.bitGet+  height <- F32.bitGet+  angle <- F32.bitGet+  distance <- F32.bitGet+  stiffness <- F32.bitGet+  swivelSpeed <- F32.bitGet+  transitionSpeed <- Monad.whenMaybe+    (Version.atLeast 868 20 0 version)+    F32.bitGet+  pure CamSettings+    { fov+    , height+    , angle+    , distance+    , stiffness+    , swivelSpeed+    , transitionSpeed+    }
src/lib/Rattletrap/Type/Attribute/ClubColors.hs view
@@ -46,5 +46,9 @@     <> U8.bitPut (orangeColor clubColorsAttribute)  bitGet :: BitGet.BitGet ClubColors-bitGet =-  ClubColors <$> BitGet.bool <*> U8.bitGet <*> BitGet.bool <*> U8.bitGet+bitGet = do+  blueFlag <- BitGet.bool+  blueColor <- U8.bitGet+  orangeFlag <- BitGet.bool+  orangeColor <- U8.bitGet+  pure ClubColors { blueFlag, blueColor, orangeFlag, orangeColor }
src/lib/Rattletrap/Type/Attribute/CustomDemolish.hs view
@@ -44,5 +44,12 @@     <> Demolish.bitPut (demolish x)  bitGet :: Version.Version -> BitGet.BitGet CustomDemolish-bitGet version =-  CustomDemolish <$> BitGet.bool <*> I32.bitGet <*> Demolish.bitGet version+bitGet version = do+  flag <- BitGet.bool+  id <- I32.bitGet+  demolish <- Demolish.bitGet version+  pure CustomDemolish+    { flag+    , Rattletrap.Type.Attribute.CustomDemolish.id+    , demolish+    }
src/lib/Rattletrap/Type/Attribute/DamageState.hs view
@@ -66,11 +66,18 @@     <> BitPut.bool (unknown6 damageStateAttribute)  bitGet :: Version.Version -> BitGet.BitGet DamageState-bitGet version =-  DamageState-    <$> U8.bitGet-    <*> BitGet.bool-    <*> I32.bitGet-    <*> Vector.bitGet version-    <*> BitGet.bool-    <*> BitGet.bool+bitGet version = do+  unknown1 <- U8.bitGet+  unknown2 <- BitGet.bool+  unknown3 <- I32.bitGet+  unknown4 <- Vector.bitGet version+  unknown5 <- BitGet.bool+  unknown6 <- BitGet.bool+  pure DamageState+    { unknown1+    , unknown2+    , unknown3+    , unknown4+    , unknown5+    , unknown6+    }
src/lib/Rattletrap/Type/Attribute/Demolish.hs view
@@ -65,11 +65,18 @@     <> Vector.bitPut (victimVelocity demolishAttribute)  bitGet :: Version.Version -> BitGet.BitGet Demolish-bitGet version =-  Demolish-    <$> BitGet.bool-    <*> U32.bitGet-    <*> BitGet.bool-    <*> U32.bitGet-    <*> Vector.bitGet version-    <*> Vector.bitGet version+bitGet version = do+  attackerFlag <- BitGet.bool+  attackerActorId <- U32.bitGet+  victimFlag <- BitGet.bool+  victimActorId <- U32.bitGet+  attackerVelocity <- Vector.bitGet version+  victimVelocity <- Vector.bitGet version+  pure Demolish+    { attackerFlag+    , attackerActorId+    , victimFlag+    , victimActorId+    , attackerVelocity+    , victimVelocity+    }
src/lib/Rattletrap/Type/Attribute/Enum.hs view
@@ -24,4 +24,6 @@ bitPut enumAttribute = BitPut.bits 11 (value enumAttribute)  bitGet :: BitGet.BitGet Enum-bitGet = Enum <$> BitGet.bits 11+bitGet = do+  value <- BitGet.bits 11+  pure Enum { value }
src/lib/Rattletrap/Type/Attribute/Explosion.hs view
@@ -43,5 +43,8 @@     <> Vector.bitPut (location explosionAttribute)  bitGet :: Version.Version -> BitGet.BitGet Explosion-bitGet version =-  Explosion <$> BitGet.bool <*> I32.bitGet <*> Vector.bitGet version+bitGet version = do+  flag <- BitGet.bool+  actorId <- I32.bitGet+  location <- Vector.bitGet version+  pure Explosion { flag, actorId, location }
src/lib/Rattletrap/Type/Attribute/ExtendedExplosion.hs view
@@ -34,5 +34,7 @@ bitPut x = Explosion.bitPut (explosion x) <> FlaggedInt.bitPut (unknown x)  bitGet :: Version.Version -> BitGet.BitGet ExtendedExplosion-bitGet version =-  ExtendedExplosion <$> Explosion.bitGet version <*> FlaggedInt.bitGet+bitGet version = do+  explosion <- Explosion.bitGet version+  unknown <- FlaggedInt.bitGet+  pure ExtendedExplosion { explosion, unknown }
src/lib/Rattletrap/Type/Attribute/FlaggedByte.hs view
@@ -33,4 +33,7 @@   <> U8.bitPut (byte flaggedByteAttribute)  bitGet :: BitGet.BitGet FlaggedByte-bitGet = FlaggedByte <$> BitGet.bool <*> U8.bitGet+bitGet = do+  flag <- BitGet.bool+  byte <- U8.bitGet+  pure FlaggedByte { flag, byte }
src/lib/Rattletrap/Type/Attribute/FlaggedInt.hs view
@@ -32,4 +32,7 @@   <> I32.bitPut (int flaggedIntAttribute)  bitGet :: BitGet.BitGet FlaggedInt-bitGet = FlaggedInt <$> BitGet.bool <*> I32.bitGet+bitGet = do+  flag <- BitGet.bool+  int <- I32.bitGet+  pure FlaggedInt { flag, int }
src/lib/Rattletrap/Type/Attribute/Float.hs view
@@ -24,4 +24,6 @@ bitPut floatAttribute = F32.bitPut (value floatAttribute)  bitGet :: BitGet.BitGet Float-bitGet = Float <$> F32.bitGet+bitGet = do+  value <- F32.bitGet+  pure Float { value }
src/lib/Rattletrap/Type/Attribute/GameMode.hs view
@@ -35,15 +35,10 @@  bitPut :: GameMode -> BitPut.BitPut bitPut gameModeAttribute = do-  BitPut.word8 (numBits gameModeAttribute) (word gameModeAttribute)+  BitPut.bits (numBits gameModeAttribute) (word gameModeAttribute)  bitGet :: Version.Version -> BitGet.BitGet GameMode-bitGet version =-  GameMode (numBits_ version) <$> BitGet.word8 (numBits_ version)--numBits_ :: Version.Version -> Int-numBits_ version = if has8Bits version then 8 else 2--has8Bits :: Version.Version -> Bool-has8Bits v =-  Version.major v >= 868 && Version.minor v >= 12 && Version.patch v >= 0+bitGet version = do+  let numBits = if Version.atLeast 868 12 0 version then 8 else 2 :: Int+  word <- BitGet.bits numBits+  pure GameMode { numBits, word }
src/lib/Rattletrap/Type/Attribute/Int.hs view
@@ -24,4 +24,6 @@ bitPut intAttribute = I32.bitPut (value intAttribute)  bitGet :: BitGet.BitGet Int-bitGet = Int <$> I32.bitGet+bitGet = do+  value <- I32.bitGet+  pure Int { value }
src/lib/Rattletrap/Type/Attribute/Int64.hs view
@@ -23,4 +23,6 @@ putInt64Attribute int64Attribute = I64.bitPut (value int64Attribute)  bitGet :: BitGet.BitGet Int64-bitGet = Int64 <$> I64.bitGet+bitGet = do+  value <- I64.bitGet+  pure Int64 { value }
src/lib/Rattletrap/Type/Attribute/Loadout.hs view
@@ -6,7 +6,7 @@ import qualified Rattletrap.Type.U32 as U32 import qualified Rattletrap.Type.U8 as U8 import qualified Rattletrap.Utility.Json as Json-import Rattletrap.Utility.Monad+import qualified Rattletrap.Utility.Monad as Monad  data Loadout = Loadout   { version :: U8.U8@@ -133,21 +133,39 @@  bitGet :: BitGet.BitGet Loadout bitGet = do-  version_ <- U8.bitGet-  Loadout version_-    <$> U32.bitGet-    <*> U32.bitGet-    <*> U32.bitGet-    <*> U32.bitGet-    <*> U32.bitGet-    <*> U32.bitGet-    <*> U32.bitGet-    <*> whenMaybe (U8.toWord8 version_ >= 11) U32.bitGet-    <*> whenMaybe (U8.toWord8 version_ >= 16) U32.bitGet-    <*> whenMaybe (U8.toWord8 version_ >= 16) U32.bitGet-    <*> whenMaybe (U8.toWord8 version_ >= 16) U32.bitGet-    <*> whenMaybe (U8.toWord8 version_ >= 17) U32.bitGet-    <*> whenMaybe (U8.toWord8 version_ >= 19) U32.bitGet-    <*> whenMaybe (U8.toWord8 version_ >= 22) U32.bitGet-    <*> whenMaybe (U8.toWord8 version_ >= 22) U32.bitGet-    <*> whenMaybe (U8.toWord8 version_ >= 22) U32.bitGet+  version <- U8.bitGet+  body <- U32.bitGet+  decal <- U32.bitGet+  wheels <- U32.bitGet+  rocketTrail <- U32.bitGet+  antenna <- U32.bitGet+  topper <- U32.bitGet+  unknown1 <- U32.bitGet+  unknown2 <- Monad.whenMaybe (U8.toWord8 version >= 11) U32.bitGet+  engineAudio <- Monad.whenMaybe (U8.toWord8 version >= 16) U32.bitGet+  trail <- Monad.whenMaybe (U8.toWord8 version >= 16) U32.bitGet+  goalExplosion <- Monad.whenMaybe (U8.toWord8 version >= 16) U32.bitGet+  banner <- Monad.whenMaybe (U8.toWord8 version >= 17) U32.bitGet+  unknown3 <- Monad.whenMaybe (U8.toWord8 version >= 19) U32.bitGet+  unknown4 <- Monad.whenMaybe (U8.toWord8 version >= 22) U32.bitGet+  unknown5 <- Monad.whenMaybe (U8.toWord8 version >= 22) U32.bitGet+  unknown6 <- Monad.whenMaybe (U8.toWord8 version >= 22) U32.bitGet+  pure Loadout+    { version+    , body+    , decal+    , wheels+    , rocketTrail+    , antenna+    , topper+    , unknown1+    , unknown2+    , engineAudio+    , trail+    , goalExplosion+    , banner+    , unknown3+    , unknown4+    , unknown5+    , unknown6+    }
src/lib/Rattletrap/Type/Attribute/LoadoutOnline.hs view
@@ -41,6 +41,7 @@   :: Version.Version -> Map.Map U32.U32 Str.Str -> BitGet.BitGet LoadoutOnline bitGet version objectMap = do   size <- U8.bitGet-  LoadoutOnline <$> List.replicateM-    (fromIntegral (U8.toWord8 size))-    (Product.decodeProductAttributesBits version objectMap)+  value <-+    List.replicateM (fromIntegral $ U8.toWord8 size)+      $ Product.decodeProductAttributesBits version objectMap+  pure LoadoutOnline { value }
src/lib/Rattletrap/Type/Attribute/Loadouts.hs view
@@ -33,4 +33,7 @@   <> Loadout.bitPut (orange loadoutsAttribute)  bitGet :: BitGet.BitGet Loadouts-bitGet = Loadouts <$> Loadout.bitGet <*> Loadout.bitGet+bitGet = do+  blue <- Loadout.bitGet+  orange <- Loadout.bitGet+  pure Loadouts { blue, orange }
src/lib/Rattletrap/Type/Attribute/LoadoutsOnline.hs view
@@ -52,9 +52,9 @@  bitGet   :: Version.Version -> Map.Map U32.U32 Str.Str -> BitGet.BitGet LoadoutsOnline-bitGet version objectMap =-  LoadoutsOnline-    <$> LoadoutOnline.bitGet version objectMap-    <*> LoadoutOnline.bitGet version objectMap-    <*> BitGet.bool-    <*> BitGet.bool+bitGet version objectMap = do+  blue <- LoadoutOnline.bitGet version objectMap+  orange <- LoadoutOnline.bitGet version objectMap+  unknown1 <- BitGet.bool+  unknown2 <- BitGet.bool+  pure LoadoutsOnline { blue, orange, unknown1, unknown2 }
src/lib/Rattletrap/Type/Attribute/Location.hs view
@@ -24,4 +24,6 @@ bitPut locationAttribute = Vector.bitPut (value locationAttribute)  bitGet :: Version.Version -> BitGet.BitGet Location-bitGet version = Location <$> Vector.bitGet version+bitGet version = do+  value <- Vector.bitGet version+  pure Location { value }
src/lib/Rattletrap/Type/Attribute/MusicStinger.hs view
@@ -42,4 +42,8 @@     <> U8.bitPut (trigger musicStingerAttribute)  bitGet :: BitGet.BitGet MusicStinger-bitGet = MusicStinger <$> BitGet.bool <*> U32.bitGet <*> U8.bitGet+bitGet = do+  flag <- BitGet.bool+  cue <- U32.bitGet+  trigger <- U8.bitGet+  pure MusicStinger { flag, cue, trigger }
src/lib/Rattletrap/Type/Attribute/PartyLeader.hs view
@@ -1,6 +1,5 @@ module Rattletrap.Type.Attribute.PartyLeader where -import Prelude hiding (id) import qualified Rattletrap.BitGet as BitGet import qualified Rattletrap.BitPut as BitPut import qualified Rattletrap.Schema as Schema@@ -8,23 +7,31 @@ import qualified Rattletrap.Type.U8 as U8 import qualified Rattletrap.Type.Version as Version import qualified Rattletrap.Utility.Json as Json-import Rattletrap.Utility.Monad  data PartyLeader = PartyLeader   { systemId :: U8.U8-  , id :: Maybe (RemoteId.RemoteId, U8.U8)+  , remoteId :: Maybe RemoteId.RemoteId+  , localId :: Maybe U8.U8   }   deriving (Eq, Show)  instance Json.FromJSON PartyLeader where   parseJSON = Json.withObject "PartyLeader" $ \object -> do     systemId <- Json.required object "system_id"-    id <- Json.optional object "id"-    pure PartyLeader { systemId, id }+    maybeId <- Json.optional object "id"+    pure PartyLeader+      { systemId+      , remoteId = fmap fst maybeId+      , localId = fmap snd maybeId+      }  instance Json.ToJSON PartyLeader where-  toJSON x =-    Json.object [Json.pair "system_id" $ systemId x, Json.pair "id" $ id x]+  toJSON x = Json.object+    [ Json.pair "system_id" $ systemId x+    , Json.pair "id" $ case (remoteId x, localId x) of+      (Just r, Just l) -> Just (r, l)+      _ -> Nothing+    ]  schema :: Schema.Schema schema = Schema.named "attribute-party-leader" $ Schema.object@@ -38,13 +45,18 @@   ]  bitPut :: PartyLeader -> BitPut.BitPut-bitPut x = U8.bitPut (systemId x) <> foldMap-  (\(y, z) -> RemoteId.bitPut y <> U8.bitPut z)-  (Rattletrap.Type.Attribute.PartyLeader.id x)+bitPut x =+  U8.bitPut (systemId x) <> foldMap RemoteId.bitPut (remoteId x) <> foldMap+    U8.bitPut+    (localId x)  bitGet :: Version.Version -> BitGet.BitGet PartyLeader bitGet version = do-  systemId_ <- U8.bitGet-  PartyLeader systemId_ <$> whenMaybe-    (systemId_ /= U8.fromWord8 0)-    ((,) <$> RemoteId.bitGet version systemId_ <*> U8.bitGet)+  systemId <- U8.bitGet+  (remoteId, localId) <- if systemId == U8.fromWord8 0+    then pure (Nothing, Nothing)+    else do+      remoteId <- RemoteId.bitGet version systemId+      localId <- U8.bitGet+      pure (Just remoteId, Just localId)+  pure PartyLeader { systemId, remoteId, localId }
src/lib/Rattletrap/Type/Attribute/Pickup.hs view
@@ -5,7 +5,7 @@ import qualified Rattletrap.Schema as Schema import qualified Rattletrap.Type.U32 as U32 import qualified Rattletrap.Utility.Json as Json-import Rattletrap.Utility.Monad+import qualified Rattletrap.Utility.Monad as Monad  data Pickup = Pickup   { instigatorId :: Maybe U32.U32@@ -42,4 +42,6 @@ bitGet :: BitGet.BitGet Pickup bitGet = do   instigator <- BitGet.bool-  Pickup <$> whenMaybe instigator U32.bitGet <*> BitGet.bool+  instigatorId <- Monad.whenMaybe instigator U32.bitGet+  pickedUp <- BitGet.bool+  pure Pickup { instigatorId, pickedUp }
src/lib/Rattletrap/Type/Attribute/PickupNew.hs view
@@ -6,7 +6,7 @@ import qualified Rattletrap.Type.U32 as U32 import qualified Rattletrap.Type.U8 as U8 import qualified Rattletrap.Utility.Json as Json-import Rattletrap.Utility.Monad+import qualified Rattletrap.Utility.Monad as Monad  data PickupNew = PickupNew   { instigatorId :: Maybe U32.U32@@ -43,4 +43,6 @@ bitGet :: BitGet.BitGet PickupNew bitGet = do   instigator <- BitGet.bool-  PickupNew <$> whenMaybe instigator U32.bitGet <*> U8.bitGet+  instigatorId <- Monad.whenMaybe instigator U32.bitGet+  pickedUp <- U8.bitGet+  pure PickupNew { instigatorId, pickedUp }
src/lib/Rattletrap/Type/Attribute/PlayerHistoryKey.hs view
@@ -24,4 +24,6 @@ bitPut = BitPut.bits 14 . unknown  bitGet :: BitGet.BitGet PlayerHistoryKey-bitGet = PlayerHistoryKey <$> BitGet.bits 14+bitGet = do+  unknown <- BitGet.bits 14+  pure PlayerHistoryKey { unknown }
src/lib/Rattletrap/Type/Attribute/PrivateMatchSettings.hs view
@@ -64,11 +64,18 @@     <> BitPut.bool (flag privateMatchSettingsAttribute)  bitGet :: BitGet.BitGet PrivateMatchSettings-bitGet =-  PrivateMatchSettings-    <$> Str.bitGet-    <*> U32.bitGet-    <*> U32.bitGet-    <*> Str.bitGet-    <*> Str.bitGet-    <*> BitGet.bool+bitGet = do+  mutators <- Str.bitGet+  joinableBy <- U32.bitGet+  maxPlayers <- U32.bitGet+  gameName <- Str.bitGet+  password <- Str.bitGet+  flag <- BitGet.bool+  pure PrivateMatchSettings+    { mutators+    , joinableBy+    , maxPlayers+    , gameName+    , password+    , flag+    }
src/lib/Rattletrap/Type/Attribute/ProductValue.hs view
@@ -10,7 +10,7 @@ import qualified Rattletrap.Type.U32 as U32 import qualified Rattletrap.Type.Version as Version import qualified Rattletrap.Utility.Json as Json-import Rattletrap.Utility.Monad+import qualified Rattletrap.Utility.Monad as Monad  data ProductValue   = PaintedOld CompressedWord.CompressedWord@@ -25,14 +25,14 @@  instance Json.FromJSON ProductValue where   parseJSON = Json.withObject "ProductValue" $ \object -> Foldable.asum-    [ PaintedOld <$> Json.required object "painted_old"-    , PaintedNew <$> Json.required object "painted_new"-    , TeamEditionOld <$> Json.required object "team_edition_old"-    , TeamEditionNew <$> Json.required object "team_edition_new"-    , SpecialEdition <$> Json.required object "special_edition"-    , UserColorOld <$> Json.required object "user_color_old"-    , UserColorNew <$> Json.required object "user_color_new"-    , TitleId <$> Json.required object "title_id"+    [ fmap PaintedOld $ Json.required object "painted_old"+    , fmap PaintedNew $ Json.required object "painted_new"+    , fmap TeamEditionOld $ Json.required object "team_edition_old"+    , fmap TeamEditionNew $ Json.required object "team_edition_new"+    , fmap SpecialEdition $ Json.required object "special_edition"+    , fmap UserColorOld $ Json.required object "user_color_old"+    , fmap UserColorNew $ Json.required object "user_color_new"+    , fmap TitleId $ Json.required object "title_id"     ]  instance Json.ToJSON ProductValue where@@ -75,7 +75,7 @@ bitGet   :: Version.Version -> U32.U32 -> Maybe Str.Str -> BitGet.BitGet ProductValue bitGet version objectId maybeObjectName =-  case Str.toString <$> maybeObjectName of+  case fmap Str.toString maybeObjectName of     Just "TAGame.ProductAttribute_Painted_TA" -> decodePainted version     Just "TAGame.ProductAttribute_SpecialEdition_TA" -> decodeSpecialEdition     Just "TAGame.ProductAttribute_TeamEdition_TA" -> decodeTeamEdition version@@ -90,32 +90,27 @@     Nothing -> fail ("[RT06] missing object name for ID " <> show objectId)  decodeSpecialEdition :: BitGet.BitGet ProductValue-decodeSpecialEdition = SpecialEdition <$> BitGet.bits 31+decodeSpecialEdition = fmap SpecialEdition $ BitGet.bits 31  decodePainted :: Version.Version -> BitGet.BitGet ProductValue decodePainted version = if hasNewPainted version-  then PaintedNew <$> BitGet.bits 31-  else PaintedOld <$> CompressedWord.bitGet 13+  then fmap PaintedNew $ BitGet.bits 31+  else fmap PaintedOld $ CompressedWord.bitGet 13  decodeTeamEdition :: Version.Version -> BitGet.BitGet ProductValue decodeTeamEdition version = if hasNewPainted version-  then TeamEditionNew <$> BitGet.bits 31-  else TeamEditionOld <$> CompressedWord.bitGet 13+  then fmap TeamEditionNew $ BitGet.bits 31+  else fmap TeamEditionOld $ CompressedWord.bitGet 13  decodeColor :: Version.Version -> BitGet.BitGet ProductValue-decodeColor version = if hasNewColor version-  then UserColorNew <$> U32.bitGet+decodeColor version = if Version.atLeast 868 23 8 version+  then fmap UserColorNew U32.bitGet   else do     hasValue <- BitGet.bool-    UserColorOld <$> whenMaybe hasValue (BitGet.bits 31)+    fmap UserColorOld $ Monad.whenMaybe hasValue (BitGet.bits 31)  hasNewPainted :: Version.Version -> Bool-hasNewPainted v =-  Version.major v >= 868 && Version.minor v >= 18 && Version.patch v >= 0--hasNewColor :: Version.Version -> Bool-hasNewColor v =-  Version.major v >= 868 && Version.minor v >= 23 && Version.patch v >= 8+hasNewPainted = Version.atLeast 868 18 0  decodeTitle :: BitGet.BitGet ProductValue-decodeTitle = TitleId <$> Str.bitGet+decodeTitle = fmap TitleId Str.bitGet
src/lib/Rattletrap/Type/Attribute/QWord.hs view
@@ -23,4 +23,6 @@ bitPut qWordAttribute = U64.bitPut (value qWordAttribute)  bitGet :: BitGet.BitGet QWord-bitGet = QWord <$> U64.bitGet+bitGet = do+  value <- U64.bitGet+  pure QWord { value }
src/lib/Rattletrap/Type/Attribute/Reservation.hs view
@@ -10,7 +10,7 @@ import qualified Rattletrap.Type.U8 as U8 import qualified Rattletrap.Type.Version as Version import qualified Rattletrap.Utility.Json as Json-import Rattletrap.Utility.Monad+import qualified Rattletrap.Utility.Monad as Monad  data Reservation = Reservation   { number :: CompressedWord.CompressedWord@@ -59,18 +59,17 @@     <> foldMap Str.bitPut (name reservationAttribute)     <> BitPut.bool (unknown1 reservationAttribute)     <> BitPut.bool (unknown2 reservationAttribute)-    <> foldMap (BitPut.word8 6) (unknown3 reservationAttribute)+    <> foldMap (BitPut.bits 6) (unknown3 reservationAttribute)  bitGet :: Version.Version -> BitGet.BitGet Reservation bitGet version = do-  number_ <- CompressedWord.bitGet 7-  uniqueId_ <- UniqueId.bitGet version-  Reservation number_ uniqueId_-    <$> whenMaybe (UniqueId.systemId uniqueId_ /= U8.fromWord8 0) Str.bitGet-    <*> BitGet.bool-    <*> BitGet.bool-    <*> whenMaybe (hasUnknown3 version) (BitGet.word8 6)--hasUnknown3 :: Version.Version -> Bool-hasUnknown3 v =-  Version.major v >= 868 && Version.minor v >= 12 && Version.patch v >= 0+  number <- CompressedWord.bitGet 7+  uniqueId <- UniqueId.bitGet version+  name <- Monad.whenMaybe+    (UniqueId.systemId uniqueId /= U8.fromWord8 0)+    Str.bitGet+  unknown1 <- BitGet.bool+  unknown2 <- BitGet.bool+  unknown3 <- Monad.whenMaybe (Version.atLeast 868 12 0 version)+    $ BitGet.bits 6+  pure Reservation { number, uniqueId, name, unknown1, unknown2, unknown3 }
src/lib/Rattletrap/Type/Attribute/RigidBodyState.hs view
@@ -7,7 +7,7 @@ import qualified Rattletrap.Type.Vector as Vector import qualified Rattletrap.Type.Version as Version import qualified Rattletrap.Utility.Json as Json-import Rattletrap.Utility.Monad+import qualified Rattletrap.Utility.Monad as Monad  data RigidBodyState = RigidBodyState   { sleeping :: Bool@@ -65,9 +65,15 @@  bitGet :: Version.Version -> BitGet.BitGet RigidBodyState bitGet version = do-  sleeping_ <- BitGet.bool-  RigidBodyState sleeping_-    <$> Vector.bitGet version-    <*> Rotation.bitGet version-    <*> whenMaybe (not sleeping_) (Vector.bitGet version)-    <*> whenMaybe (not sleeping_) (Vector.bitGet version)+  sleeping <- BitGet.bool+  location <- Vector.bitGet version+  rotation <- Rotation.bitGet version+  linearVelocity <- Monad.whenMaybe (not sleeping) (Vector.bitGet version)+  angularVelocity <- Monad.whenMaybe (not sleeping) (Vector.bitGet version)+  pure RigidBodyState+    { sleeping+    , location+    , rotation+    , linearVelocity+    , angularVelocity+    }
src/lib/Rattletrap/Type/Attribute/StatEvent.hs view
@@ -33,4 +33,7 @@   <> I32.bitPut (objectId statEventAttribute)  bitGet :: BitGet.BitGet StatEvent-bitGet = StatEvent <$> BitGet.bool <*> I32.bitGet+bitGet = do+  unknown <- BitGet.bool+  objectId <- I32.bitGet+  pure StatEvent { unknown, objectId }
src/lib/Rattletrap/Type/Attribute/String.hs view
@@ -24,4 +24,6 @@ bitPut stringAttribute = Str.bitPut (value stringAttribute)  bitGet :: BitGet.BitGet String-bitGet = String <$> Str.bitGet+bitGet = do+  value <- Str.bitGet+  pure String { value }
src/lib/Rattletrap/Type/Attribute/TeamPaint.hs view
@@ -58,10 +58,16 @@     <> U32.bitPut (accentFinish teamPaintAttribute)  bitGet :: BitGet.BitGet TeamPaint-bitGet =-  TeamPaint-    <$> U8.bitGet-    <*> U8.bitGet-    <*> U8.bitGet-    <*> U32.bitGet-    <*> U32.bitGet+bitGet = do+  team <- U8.bitGet+  primaryColor <- U8.bitGet+  accentColor <- U8.bitGet+  primaryFinish <- U32.bitGet+  accentFinish <- U32.bitGet+  pure TeamPaint+    { team+    , primaryColor+    , accentColor+    , primaryFinish+    , accentFinish+    }
src/lib/Rattletrap/Type/Attribute/Title.hs view
@@ -75,13 +75,22 @@     <> BitPut.bool (unknown8 titleAttribute)  bitGet :: BitGet.BitGet Title-bitGet =-  Title-    <$> BitGet.bool-    <*> BitGet.bool-    <*> U32.bitGet-    <*> U32.bitGet-    <*> U32.bitGet-    <*> U32.bitGet-    <*> U32.bitGet-    <*> BitGet.bool+bitGet = do+  unknown1 <- BitGet.bool+  unknown2 <- BitGet.bool+  unknown3 <- U32.bitGet+  unknown4 <- U32.bitGet+  unknown5 <- U32.bitGet+  unknown6 <- U32.bitGet+  unknown7 <- U32.bitGet+  unknown8 <- BitGet.bool+  pure Title+    { unknown1+    , unknown2+    , unknown3+    , unknown4+    , unknown5+    , unknown6+    , unknown7+    , unknown8+    }
src/lib/Rattletrap/Type/Attribute/UniqueId.hs view
@@ -44,5 +44,7 @@  bitGet :: Version.Version -> BitGet.BitGet UniqueId bitGet version = do-  systemId_ <- U8.bitGet-  UniqueId systemId_ <$> RemoteId.bitGet version systemId_ <*> U8.bitGet+  systemId <- U8.bitGet+  remoteId <- RemoteId.bitGet version systemId+  localId <- U8.bitGet+  pure UniqueId { systemId, remoteId, localId }
src/lib/Rattletrap/Type/Attribute/WeldedInfo.hs view
@@ -55,10 +55,10 @@     <> Int8Vector.bitPut (rotation weldedInfoAttribute)  bitGet :: Version.Version -> BitGet.BitGet WeldedInfo-bitGet version =-  WeldedInfo-    <$> BitGet.bool-    <*> I32.bitGet-    <*> Vector.bitGet version-    <*> F32.bitGet-    <*> Int8Vector.bitGet+bitGet version = do+  active <- BitGet.bool+  actorId <- I32.bitGet+  offset <- Vector.bitGet version+  mass <- F32.bitGet+  rotation <- Int8Vector.bitGet+  pure WeldedInfo { active, actorId, offset, mass, rotation }
src/lib/Rattletrap/Type/AttributeMapping.hs view
@@ -32,4 +32,7 @@ bytePut x = U32.bytePut (objectId x) <> U32.bytePut (streamId x)  byteGet :: ByteGet.ByteGet AttributeMapping-byteGet = AttributeMapping <$> U32.byteGet <*> U32.byteGet+byteGet = do+  objectId <- U32.byteGet+  streamId <- U32.byteGet+  pure AttributeMapping { objectId, streamId }
src/lib/Rattletrap/Type/AttributeValue.hs view
@@ -92,43 +92,43 @@  instance Json.FromJSON AttributeValue where   parseJSON = Json.withObject "AttributeValue" $ \object -> Foldable.asum-    [ AppliedDamage <$> Json.required object "applied_damage"-    , Boolean <$> Json.required object "boolean"-    , Byte <$> Json.required object "byte"-    , CamSettings <$> Json.required object "cam_settings"-    , ClubColors <$> Json.required object "club_colors"-    , CustomDemolish <$> Json.required object "custom_demolish"-    , DamageState <$> Json.required object "damage_state"-    , Demolish <$> Json.required object "demolish"-    , Enum <$> Json.required object "enum"-    , Explosion <$> Json.required object "explosion"-    , ExtendedExplosion <$> Json.required object "extended_explosion"-    , FlaggedByte <$> Json.required object "flagged_byte"-    , FlaggedInt <$> Json.required object "flagged_int"-    , Float <$> Json.required object "float"-    , GameMode <$> Json.required object "game_mode"-    , Int <$> Json.required object "int"-    , Int64 <$> Json.required object "int64"-    , Loadout <$> Json.required object "loadout"-    , LoadoutOnline <$> Json.required object "loadout_online"-    , Loadouts <$> Json.required object "loadouts"-    , LoadoutsOnline <$> Json.required object "loadouts_online"-    , Location <$> Json.required object "location"-    , MusicStinger <$> Json.required object "music_stinger"-    , PartyLeader <$> Json.required object "party_leader"-    , Pickup <$> Json.required object "pickup"-    , PickupNew <$> Json.required object "pickup_new"-    , PlayerHistoryKey <$> Json.required object "player_history_key"-    , PrivateMatchSettings <$> Json.required object "private_match_settings"-    , QWord <$> Json.required object "q_word"-    , Reservation <$> Json.required object "reservation"-    , RigidBodyState <$> Json.required object "rigid_body_state"-    , StatEvent <$> Json.required object "stat_event"-    , String <$> Json.required object "string"-    , TeamPaint <$> Json.required object "team_paint"-    , Title <$> Json.required object "title"-    , UniqueId <$> Json.required object "unique_id"-    , WeldedInfo <$> Json.required object "welded_info"+    [ fmap AppliedDamage $ Json.required object "applied_damage"+    , fmap Boolean $ Json.required object "boolean"+    , fmap Byte $ Json.required object "byte"+    , fmap CamSettings $ Json.required object "cam_settings"+    , fmap ClubColors $ Json.required object "club_colors"+    , fmap CustomDemolish $ Json.required object "custom_demolish"+    , fmap DamageState $ Json.required object "damage_state"+    , fmap Demolish $ Json.required object "demolish"+    , fmap Enum $ Json.required object "enum"+    , fmap Explosion $ Json.required object "explosion"+    , fmap ExtendedExplosion $ Json.required object "extended_explosion"+    , fmap FlaggedByte $ Json.required object "flagged_byte"+    , fmap FlaggedInt $ Json.required object "flagged_int"+    , fmap Float $ Json.required object "float"+    , fmap GameMode $ Json.required object "game_mode"+    , fmap Int $ Json.required object "int"+    , fmap Int64 $ Json.required object "int64"+    , fmap Loadout $ Json.required object "loadout"+    , fmap LoadoutOnline $ Json.required object "loadout_online"+    , fmap Loadouts $ Json.required object "loadouts"+    , fmap LoadoutsOnline $ Json.required object "loadouts_online"+    , fmap Location $ Json.required object "location"+    , fmap MusicStinger $ Json.required object "music_stinger"+    , fmap PartyLeader $ Json.required object "party_leader"+    , fmap Pickup $ Json.required object "pickup"+    , fmap PickupNew $ Json.required object "pickup_new"+    , fmap PlayerHistoryKey $ Json.required object "player_history_key"+    , fmap PrivateMatchSettings $ Json.required object "private_match_settings"+    , fmap QWord $ Json.required object "q_word"+    , fmap Reservation $ Json.required object "reservation"+    , fmap RigidBodyState $ Json.required object "rigid_body_state"+    , fmap StatEvent $ Json.required object "stat_event"+    , fmap String $ Json.required object "string"+    , fmap TeamPaint $ Json.required object "team_paint"+    , fmap Title $ Json.required object "title"+    , fmap UniqueId $ Json.required object "unique_id"+    , fmap WeldedInfo $ Json.required object "welded_info"     ]  instance Json.ToJSON AttributeValue where@@ -266,47 +266,47 @@     (Map.lookup (Str.toText name) Data.attributeTypes)   case constructor of     AttributeType.AppliedDamage ->-      AppliedDamage <$> AppliedDamage.bitGet version-    AttributeType.Boolean -> Boolean <$> Boolean.bitGet-    AttributeType.Byte -> Byte <$> Byte.bitGet-    AttributeType.CamSettings -> CamSettings <$> CamSettings.bitGet version-    AttributeType.ClubColors -> ClubColors <$> ClubColors.bitGet+      fmap AppliedDamage $ AppliedDamage.bitGet version+    AttributeType.Boolean -> fmap Boolean Boolean.bitGet+    AttributeType.Byte -> fmap Byte Byte.bitGet+    AttributeType.CamSettings -> fmap CamSettings $ CamSettings.bitGet version+    AttributeType.ClubColors -> fmap ClubColors ClubColors.bitGet     AttributeType.CustomDemolish ->-      CustomDemolish <$> CustomDemolish.bitGet version-    AttributeType.DamageState -> DamageState <$> DamageState.bitGet version-    AttributeType.Demolish -> Demolish <$> Demolish.bitGet version-    AttributeType.Enum -> Enum <$> Enum.bitGet-    AttributeType.Explosion -> Explosion <$> Explosion.bitGet version+      fmap CustomDemolish $ CustomDemolish.bitGet version+    AttributeType.DamageState -> fmap DamageState $ DamageState.bitGet version+    AttributeType.Demolish -> fmap Demolish $ Demolish.bitGet version+    AttributeType.Enum -> fmap Enum Enum.bitGet+    AttributeType.Explosion -> fmap Explosion $ Explosion.bitGet version     AttributeType.ExtendedExplosion ->-      ExtendedExplosion <$> ExtendedExplosion.bitGet version-    AttributeType.FlaggedInt -> FlaggedInt <$> FlaggedInt.bitGet-    AttributeType.FlaggedByte -> FlaggedByte <$> FlaggedByte.bitGet-    AttributeType.Float -> Float <$> Float.bitGet-    AttributeType.GameMode -> GameMode <$> GameMode.bitGet version-    AttributeType.Int -> Int <$> Int.bitGet-    AttributeType.Int64 -> Int64 <$> Int64.bitGet-    AttributeType.Loadout -> Loadout <$> Loadout.bitGet+      fmap ExtendedExplosion $ ExtendedExplosion.bitGet version+    AttributeType.FlaggedInt -> fmap FlaggedInt FlaggedInt.bitGet+    AttributeType.FlaggedByte -> fmap FlaggedByte FlaggedByte.bitGet+    AttributeType.Float -> fmap Float Float.bitGet+    AttributeType.GameMode -> fmap GameMode $ GameMode.bitGet version+    AttributeType.Int -> fmap Int Int.bitGet+    AttributeType.Int64 -> fmap Int64 Int64.bitGet+    AttributeType.Loadout -> fmap Loadout Loadout.bitGet     AttributeType.LoadoutOnline ->-      LoadoutOnline <$> LoadoutOnline.bitGet version objectMap-    AttributeType.Loadouts -> Loadouts <$> Loadouts.bitGet+      fmap LoadoutOnline $ LoadoutOnline.bitGet version objectMap+    AttributeType.Loadouts -> fmap Loadouts Loadouts.bitGet     AttributeType.LoadoutsOnline ->-      LoadoutsOnline <$> LoadoutsOnline.bitGet version objectMap-    AttributeType.Location -> Location <$> Location.bitGet version-    AttributeType.MusicStinger -> MusicStinger <$> MusicStinger.bitGet-    AttributeType.PartyLeader -> PartyLeader <$> PartyLeader.bitGet version-    AttributeType.Pickup -> Pickup <$> Pickup.bitGet-    AttributeType.PickupNew -> PickupNew <$> PickupNew.bitGet+      fmap LoadoutsOnline $ LoadoutsOnline.bitGet version objectMap+    AttributeType.Location -> fmap Location $ Location.bitGet version+    AttributeType.MusicStinger -> fmap MusicStinger MusicStinger.bitGet+    AttributeType.PartyLeader -> fmap PartyLeader $ PartyLeader.bitGet version+    AttributeType.Pickup -> fmap Pickup Pickup.bitGet+    AttributeType.PickupNew -> fmap PickupNew PickupNew.bitGet     AttributeType.PlayerHistoryKey ->-      PlayerHistoryKey <$> PlayerHistoryKey.bitGet+      fmap PlayerHistoryKey PlayerHistoryKey.bitGet     AttributeType.PrivateMatchSettings ->-      PrivateMatchSettings <$> PrivateMatchSettings.bitGet-    AttributeType.QWord -> QWord <$> QWord.bitGet-    AttributeType.Reservation -> Reservation <$> Reservation.bitGet version+      fmap PrivateMatchSettings PrivateMatchSettings.bitGet+    AttributeType.QWord -> fmap QWord QWord.bitGet+    AttributeType.Reservation -> fmap Reservation $ Reservation.bitGet version     AttributeType.RigidBodyState ->-      RigidBodyState <$> RigidBodyState.bitGet version-    AttributeType.StatEvent -> StatEvent <$> StatEvent.bitGet-    AttributeType.String -> String <$> String.bitGet-    AttributeType.TeamPaint -> TeamPaint <$> TeamPaint.bitGet-    AttributeType.Title -> Title <$> Title.bitGet-    AttributeType.UniqueId -> UniqueId <$> UniqueId.bitGet version-    AttributeType.WeldedInfo -> WeldedInfo <$> WeldedInfo.bitGet version+      fmap RigidBodyState $ RigidBodyState.bitGet version+    AttributeType.StatEvent -> fmap StatEvent StatEvent.bitGet+    AttributeType.String -> fmap String String.bitGet+    AttributeType.TeamPaint -> fmap TeamPaint TeamPaint.bitGet+    AttributeType.Title -> fmap Title Title.bitGet+    AttributeType.UniqueId -> fmap UniqueId $ UniqueId.bitGet version+    AttributeType.WeldedInfo -> fmap WeldedInfo $ WeldedInfo.bitGet version
src/lib/Rattletrap/Type/Cache.hs view
@@ -51,9 +51,9 @@     <> List.bytePut AttributeMapping.bytePut (attributeMappings x)  byteGet :: ByteGet.ByteGet Cache-byteGet =-  Cache-    <$> U32.byteGet-    <*> U32.byteGet-    <*> U32.byteGet-    <*> List.byteGet AttributeMapping.byteGet+byteGet = do+  classId <- U32.byteGet+  parentCacheId <- U32.byteGet+  cacheId <- U32.byteGet+  attributeMappings <- List.byteGet AttributeMapping.byteGet+  pure Cache { classId, parentCacheId, cacheId, attributeMappings }
src/lib/Rattletrap/Type/ClassAttributeMap.hs view
@@ -1,15 +1,4 @@-module Rattletrap.Type.ClassAttributeMap-  ( ClassAttributeMap(..)-  , classHasLocation-  , classHasRotation-  , getAttributeIdLimit-  , getAttributeMap-  , getAttributeName-  , getClassName-  , getName-  , getObjectName-  , make-  ) where+module Rattletrap.Type.ClassAttributeMap where  import qualified Rattletrap.Data as Data import qualified Rattletrap.Type.AttributeMapping as AttributeMapping@@ -262,11 +251,9 @@ getObjectName objectMap_ objectId = Map.lookup objectId objectMap_  getClassName :: Str.Str -> Maybe Str.Str-getClassName rawObjectName =-  Str.fromText-    <$> Map.lookup-          (Str.toText $ normalizeObjectName rawObjectName)-          Data.objectClasses+getClassName rawObjectName = fmap Str.fromText $ Map.lookup+  (Str.toText $ normalizeObjectName rawObjectName)+  Data.objectClasses  normalizeObjectName :: Str.Str -> Str.Str normalizeObjectName objectName =
src/lib/Rattletrap/Type/ClassMapping.hs view
@@ -33,4 +33,7 @@ bytePut x = Str.bytePut (name x) <> U32.bytePut (streamId x)  byteGet :: ByteGet.ByteGet ClassMapping-byteGet = ClassMapping <$> Str.byteGet <*> U32.byteGet+byteGet = do+  name <- Str.byteGet+  streamId <- U32.byteGet+  pure ClassMapping { name, streamId }
src/lib/Rattletrap/Type/CompressedWord.hs view
@@ -66,7 +66,9 @@   in if x < 1024 && x == 2 ^ n then n + 1 else n  bitGet :: Word -> BitGet.BitGet CompressedWord-bitGet limit_ = CompressedWord limit_ <$> step limit_ (getMaxBits_ limit_) 0 0+bitGet limit = do+  value <- step limit (getMaxBits_ limit) 0 0+  pure CompressedWord { limit, value }  getMaxBits_ :: Word -> Word getMaxBits_ x = do
src/lib/Rattletrap/Type/CompressedWordVector.hs view
@@ -38,11 +38,11 @@     <> CompressedWord.bitPut (z compressedWordVector)  bitGet :: BitGet.BitGet CompressedWordVector-bitGet =-  CompressedWordVector-    <$> CompressedWord.bitGet limit-    <*> CompressedWord.bitGet limit-    <*> CompressedWord.bitGet limit+bitGet = do+  x <- CompressedWord.bitGet limit+  y <- CompressedWord.bitGet limit+  z <- CompressedWord.bitGet limit+  pure CompressedWordVector { x, y, z }  limit :: Word limit = 65536
src/lib/Rattletrap/Type/Content.hs view
@@ -9,7 +9,7 @@ import qualified Rattletrap.Type.ClassAttributeMap as ClassAttributeMap import qualified Rattletrap.Type.ClassMapping as ClassMapping import qualified Rattletrap.Type.Frame as Frame-import qualified Rattletrap.Type.KeyFrame as KeyFrame+import qualified Rattletrap.Type.Keyframe as Keyframe import qualified Rattletrap.Type.List as List import qualified Rattletrap.Type.Mark as Mark import qualified Rattletrap.Type.Message as Message@@ -17,12 +17,12 @@ import qualified Rattletrap.Type.U32 as U32 import qualified Rattletrap.Type.U8 as U8 import qualified Rattletrap.Type.Version as Version-import Rattletrap.Utility.Bytes+import qualified Rattletrap.Utility.Bytes as Bytes import qualified Rattletrap.Utility.Json as Json  import qualified Control.Monad.Trans.State as State-import qualified Data.ByteString as Bytes-import qualified Data.ByteString.Lazy as LazyBytes+import qualified Data.ByteString as ByteString+import qualified Data.ByteString.Lazy as LazyByteString import qualified Data.Word as Word  type Content = ContentWith (List.List Frame.Frame)@@ -31,7 +31,7 @@ data ContentWith frames = Content   { levels :: List.List Str.Str   -- ^ This typically only has one element, like @stadium_oob_audio_map@.-  , keyFrames :: List.List KeyFrame.KeyFrame+  , keyframes :: List.List Keyframe.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.@@ -66,7 +66,7 @@ instance Json.FromJSON frames => Json.FromJSON (ContentWith frames) where   parseJSON = Json.withObject "Content" $ \object -> do     levels <- Json.required object "levels"-    keyFrames <- Json.required object "key_frames"+    keyframes <- Json.required object "key_frames"     streamSize <- Json.required object "stream_size"     frames <- Json.required object "frames"     messages <- Json.required object "messages"@@ -79,7 +79,7 @@     unknown <- Json.required object "unknown"     pure Content       { levels-      , keyFrames+      , keyframes       , streamSize       , frames       , messages@@ -95,7 +95,7 @@ instance Json.ToJSON frames => Json.ToJSON (ContentWith frames) where   toJSON x = Json.object     [ Json.pair "levels" $ levels x-    , Json.pair "key_frames" $ keyFrames x+    , Json.pair "key_frames" $ keyframes x     , Json.pair "stream_size" $ streamSize x     , Json.pair "frames" $ frames x     , Json.pair "messages" $ messages x@@ -111,7 +111,7 @@ schema :: Schema.Schema -> Schema.Schema schema s = Schema.named "content" $ Schema.object   [ (Json.pair "levels" . Schema.json $ List.schema Str.schema, True)-  , (Json.pair "key_frames" . Schema.json $ List.schema KeyFrame.schema, True)+  , (Json.pair "key_frames" . Schema.json $ List.schema Keyframe.schema, True)   , (Json.pair "stream_size" $ Schema.ref U32.schema, True)   , (Json.pair "frames" $ Schema.json s, True)   , (Json.pair "messages" . Schema.json $ List.schema Message.schema, True)@@ -130,7 +130,7 @@ empty :: Content empty = Content   { levels = List.empty-  , keyFrames = List.empty+  , keyframes = List.empty   , streamSize = U32.fromWord32 0   , frames = List.empty   , messages = List.empty@@ -146,7 +146,7 @@ bytePut :: Content -> BytePut.BytePut bytePut x =   List.bytePut Str.bytePut (levels x)-    <> List.bytePut KeyFrame.bytePut (keyFrames x)+    <> List.bytePut Keyframe.bytePut (keyframes x)     <> putFrames x     <> List.bytePut Message.bytePut (messages x)     <> List.bytePut Mark.bytePut (marks x)@@ -173,14 +173,16 @@     -- Unforunately that isn't currently known. See this issue for details:     -- <https://github.com/tfausak/rattletrap/issues/171>.     expectedStreamSize = streamSize x-    actualStreamSize = U32.fromWord32 . fromIntegral $ Bytes.length stream+    actualStreamSize =+      U32.fromWord32 . fromIntegral $ ByteString.length stream     streamSize_ = U32.fromWord32       $ max (U32.toWord32 expectedStreamSize) (U32.toWord32 actualStreamSize)-  in U32.bytePut streamSize_ <> BytePut.byteString-    (reverseBytes (padBytes (U32.toWord32 streamSize_) stream))+  in U32.bytePut streamSize_+    <> BytePut.byteString (Bytes.padBytes (U32.toWord32 streamSize_) stream)  byteGet-  :: Version.Version+  :: Maybe Str.Str+  -> Version.Version   -- ^ Version numbers, usually from 'Rattletrap.Header.getVersion'.   -> Int   -- ^ The number of frames in the stream, usually from@@ -189,42 +191,43 @@   -- ^ The maximum number of channels in the stream, usually from   -- 'Rattletrap.Header.getMaxChannels'.   -> ByteGet.ByteGet Content-byteGet version numFrames maxChannels = do-  (levels_, keyFrames_, streamSize_) <--    (,,)-    <$> List.byteGet Str.byteGet-    <*> List.byteGet KeyFrame.byteGet-    <*> U32.byteGet-  (stream, messages_, marks_, packages_, objects_, names_, classMappings_, caches_) <--    (,,,,,,,)-    <$> ByteGet.byteString (fromIntegral (U32.toWord32 streamSize_))-    <*> List.byteGet Message.byteGet-    <*> List.byteGet Mark.byteGet-    <*> List.byteGet Str.byteGet-    <*> List.byteGet Str.byteGet-    <*> List.byteGet Str.byteGet-    <*> List.byteGet ClassMapping.byteGet-    <*> List.byteGet Cache.byteGet+byteGet matchType version numFrames maxChannels = do+  levels <- List.byteGet Str.byteGet+  keyframes <- List.byteGet Keyframe.byteGet+  streamSize <- U32.byteGet+  stream <- ByteGet.byteString . fromIntegral $ U32.toWord32 streamSize+  messages <- List.byteGet Message.byteGet+  marks <- List.byteGet Mark.byteGet+  packages <- List.byteGet Str.byteGet+  objects <- List.byteGet Str.byteGet+  names <- List.byteGet Str.byteGet+  classMappings <- List.byteGet ClassMapping.byteGet+  caches <- List.byteGet Cache.byteGet   let     classAttributeMap =-      ClassAttributeMap.make objects_ classMappings_ caches_ names_+      ClassAttributeMap.make objects classMappings caches names     bitGet = State.evalStateT-      (Frame.decodeFramesBits version numFrames maxChannels classAttributeMap)+      (Frame.decodeFramesBits+        matchType+        version+        numFrames+        maxChannels+        classAttributeMap+      )       mempty-  frames_ <--    either fail pure . ByteGet.run (BitGet.toByteGet bitGet) $ reverseBytes-      stream-  Content-      levels_-      keyFrames_-      streamSize_-      frames_-      messages_-      marks_-      packages_-      objects_-      names_-      classMappings_-      caches_-    . LazyBytes.unpack-    <$> ByteGet.remaining+  frames <- either fail pure $ ByteGet.run (BitGet.toByteGet bitGet) stream+  unknown <- fmap LazyByteString.unpack ByteGet.remaining+  pure Content+    { levels+    , keyframes+    , streamSize+    , frames+    , messages+    , marks+    , packages+    , objects+    , names+    , classMappings+    , caches+    , unknown+    }
src/lib/Rattletrap/Type/F32.hs view
@@ -33,7 +33,7 @@ bitPut = BitPut.fromBytePut . bytePut  byteGet :: ByteGet.ByteGet F32-byteGet = fromFloat <$> ByteGet.float+byteGet = fmap fromFloat ByteGet.float  bitGet :: BitGet.BitGet F32 bitGet = BitGet.fromByteGet byteGet 4
src/lib/Rattletrap/Type/Frame.hs view
@@ -8,6 +8,7 @@ import qualified Rattletrap.Type.F32 as F32 import qualified Rattletrap.Type.List as List import qualified Rattletrap.Type.Replication as Replication+import qualified Rattletrap.Type.Str as Str import qualified Rattletrap.Type.U32 as U32 import qualified Rattletrap.Type.Version as Version import qualified Rattletrap.Utility.Json as Json@@ -59,7 +60,8 @@     <> Replication.putReplications (replications frame)  decodeFramesBits-  :: Version.Version+  :: Maybe Str.Str+  -> Version.Version   -> Int   -> Word   -> ClassAttributeMap.ClassAttributeMap@@ -67,19 +69,24 @@        (Map.Map CompressedWord.CompressedWord U32.U32)        BitGet.BitGet        (List.List Frame)-decodeFramesBits version count limit classes =-  List.replicateM count $ bitGet version limit classes+decodeFramesBits matchType version count limit classes =+  List.replicateM count $ bitGet matchType version limit classes  bitGet-  :: Version.Version+  :: Maybe Str.Str+  -> Version.Version   -> Word   -> ClassAttributeMap.ClassAttributeMap   -> State.StateT        (Map.Map CompressedWord.CompressedWord U32.U32)        BitGet.BitGet        Frame-bitGet version limit classes =-  Frame-    <$> Trans.lift F32.bitGet-    <*> Trans.lift F32.bitGet-    <*> Replication.decodeReplicationsBits version limit classes+bitGet matchType version limit classes = do+  time <- Trans.lift F32.bitGet+  delta <- Trans.lift F32.bitGet+  replications <- Replication.decodeReplicationsBits+    matchType+    version+    limit+    classes+  pure Frame { time, delta, replications }
src/lib/Rattletrap/Type/Header.hs view
@@ -7,17 +7,12 @@ import qualified Rattletrap.Type.Property as Property import qualified Rattletrap.Type.Str as Str import qualified Rattletrap.Type.U32 as U32+import qualified Rattletrap.Type.Version as Version import qualified Rattletrap.Utility.Json as Json-import Rattletrap.Utility.Monad  -- | Contains high-level metadata about a 'Rattletrap.Replay.Replay'. data Header = Header-  { engineVersion :: U32.U32-  -- ^ The "major" ("engine") version number.-  , licenseeVersion :: U32.U32-  -- ^ The "minor" ("licensee") version number.-  , patchVersion :: Maybe U32.U32-  -- ^ The "patch" ("net") version number.+  { version :: Version.Version   , label :: Str.Str   -- ^ Always @TAGame.Replay_Soccar_TA@.   , properties :: Dictionary.Dictionary Property.Property@@ -60,24 +55,26 @@  instance Json.FromJSON Header where   parseJSON = Json.withObject "Header" $ \object -> do-    engineVersion <- Json.required object "engine_version"-    licenseeVersion <- Json.required object "licensee_version"-    patchVersion <- Json.optional object "patch_version"+    major <- Json.required object "engine_version"+    minor <- Json.required object "licensee_version"+    patch <- Json.optional object "patch_version"     label <- Json.required object "label"     properties <- Json.required object "properties"     pure Header-      { engineVersion-      , licenseeVersion-      , patchVersion+      { version = Version.Version+        { Version.major+        , Version.minor+        , Version.patch+        }       , label       , properties       }  instance Json.ToJSON Header where   toJSON x = Json.object-    [ Json.pair "engine_version" $ engineVersion x-    , Json.pair "licensee_version" $ licenseeVersion x-    , Json.pair "patch_version" $ patchVersion x+    [ Json.pair "engine_version" . Version.major $ version x+    , Json.pair "licensee_version" . Version.minor $ version x+    , Json.pair "patch_version" . Version.patch $ version x     , Json.pair "label" $ label x     , Json.pair "properties" $ properties x     ]@@ -95,18 +92,13 @@  bytePut :: Header -> BytePut.BytePut bytePut x =-  U32.bytePut (engineVersion x)-    <> U32.bytePut (licenseeVersion x)-    <> foldMap U32.bytePut (patchVersion x)-    <> Str.bytePut (label x)-    <> Dictionary.bytePut Property.bytePut (properties x)+  Version.bytePut (version x) <> Str.bytePut (label x) <> Dictionary.bytePut+    Property.bytePut+    (properties x)  byteGet :: ByteGet.ByteGet Header byteGet = do-  (major, minor) <- (,) <$> U32.byteGet <*> U32.byteGet-  Header major minor-    <$> whenMaybe-          (U32.toWord32 major >= 868 && U32.toWord32 minor >= 18)-          U32.byteGet-    <*> Str.byteGet-    <*> Dictionary.byteGet Property.byteGet+  version <- Version.byteGet+  label <- Str.byteGet+  properties <- Dictionary.byteGet Property.byteGet+  pure Header { version, label, properties }
src/lib/Rattletrap/Type/I32.hs view
@@ -38,7 +38,7 @@ bitPut = BitPut.fromBytePut . bytePut  byteGet :: ByteGet.ByteGet I32-byteGet = fromInt32 <$> ByteGet.int32+byteGet = fmap fromInt32 ByteGet.int32  bitGet :: BitGet.BitGet I32 bitGet = BitGet.fromByteGet byteGet 4
src/lib/Rattletrap/Type/I64.hs view
@@ -42,7 +42,7 @@ bitPut = BitPut.fromBytePut . bytePut  byteGet :: ByteGet.ByteGet I64-byteGet = fromInt64 <$> ByteGet.int64+byteGet = fmap fromInt64 ByteGet.int64  bitGet :: BitGet.BitGet I64 bitGet = BitGet.fromByteGet byteGet 8
src/lib/Rattletrap/Type/I8.hs view
@@ -38,7 +38,7 @@ bitPut = BitPut.fromBytePut . bytePut  byteGet :: ByteGet.ByteGet I8-byteGet = fromInt8 <$> ByteGet.int8+byteGet = fmap fromInt8 ByteGet.int8  bitGet :: BitGet.BitGet I8 bitGet = BitGet.fromByteGet byteGet 1
src/lib/Rattletrap/Type/Initialization.hs view
@@ -7,7 +7,7 @@ import qualified Rattletrap.Type.Vector as Vector import qualified Rattletrap.Type.Version as Version import qualified Rattletrap.Utility.Json as Json-import Rattletrap.Utility.Monad+import qualified Rattletrap.Utility.Monad as Monad  data Initialization = Initialization   { location :: Maybe Vector.Vector@@ -43,7 +43,7 @@     <> foldMap Int8Vector.bitPut (rotation initialization)  bitGet :: Version.Version -> Bool -> Bool -> BitGet.BitGet Initialization-bitGet version hasLocation hasRotation =-  Initialization-    <$> whenMaybe hasLocation (Vector.bitGet version)-    <*> whenMaybe hasRotation Int8Vector.bitGet+bitGet version hasLocation hasRotation = do+  location <- Monad.whenMaybe hasLocation (Vector.bitGet version)+  rotation <- Monad.whenMaybe hasRotation Int8Vector.bitGet+  pure Initialization { location, rotation }
src/lib/Rattletrap/Type/Int8Vector.hs view
@@ -5,7 +5,7 @@ import qualified Rattletrap.Schema as Schema import qualified Rattletrap.Type.I8 as I8 import qualified Rattletrap.Utility.Json as Json-import Rattletrap.Utility.Monad+import qualified Rattletrap.Utility.Monad as Monad  data Int8Vector = Int8Vector   { x :: Maybe I8.I8@@ -44,10 +44,13 @@   Just field -> BitPut.bool True <> I8.bitPut field  bitGet :: BitGet.BitGet Int8Vector-bitGet =-  Int8Vector <$> decodeFieldBits <*> decodeFieldBits <*> decodeFieldBits+bitGet = do+  x <- decodeFieldBits+  y <- decodeFieldBits+  z <- decodeFieldBits+  pure Int8Vector { x, y, z }  decodeFieldBits :: BitGet.BitGet (Maybe I8.I8) decodeFieldBits = do   hasField <- BitGet.bool-  whenMaybe hasField I8.bitGet+  Monad.whenMaybe hasField I8.bitGet
− src/lib/Rattletrap/Type/KeyFrame.hs
@@ -1,46 +0,0 @@-module Rattletrap.Type.KeyFrame where--import qualified Rattletrap.ByteGet as ByteGet-import qualified Rattletrap.BytePut as BytePut-import qualified Rattletrap.Schema as Schema-import qualified Rattletrap.Type.F32 as F32-import qualified Rattletrap.Type.U32 as U32-import qualified Rattletrap.Utility.Json as Json--data KeyFrame = KeyFrame-  { time :: F32.F32-  -- ^ When this key frame occurs, in seconds.-  , frame :: U32.U32-  -- ^ The frame number of this key frame, starting from 0.-  , position :: U32.U32-  -- ^ The bit position of this key frame in the stream.-  }-  deriving (Eq, Show)--instance Json.FromJSON KeyFrame where-  parseJSON = Json.withObject "KeyFrame" $ \object -> do-    time <- Json.required object "time"-    frame <- Json.required object "frame"-    position <- Json.required object "position"-    pure KeyFrame { time, frame, position }--instance Json.ToJSON KeyFrame where-  toJSON x = Json.object-    [ Json.pair "time" $ time x-    , Json.pair "frame" $ frame x-    , Json.pair "position" $ position x-    ]--schema :: Schema.Schema-schema = Schema.named "keyFrame" $ Schema.object-  [ (Json.pair "time" $ Schema.ref F32.schema, True)-  , (Json.pair "frame" $ Schema.ref U32.schema, True)-  , (Json.pair "position" $ Schema.ref U32.schema, True)-  ]--bytePut :: KeyFrame -> BytePut.BytePut-bytePut x =-  F32.bytePut (time x) <> U32.bytePut (frame x) <> U32.bytePut (position x)--byteGet :: ByteGet.ByteGet KeyFrame-byteGet = KeyFrame <$> F32.byteGet <*> U32.byteGet <*> U32.byteGet
+ src/lib/Rattletrap/Type/Keyframe.hs view
@@ -0,0 +1,50 @@+module Rattletrap.Type.Keyframe where++import qualified Rattletrap.ByteGet as ByteGet+import qualified Rattletrap.BytePut as BytePut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.F32 as F32+import qualified Rattletrap.Type.U32 as U32+import qualified Rattletrap.Utility.Json as Json++data Keyframe = Keyframe+  { time :: F32.F32+  -- ^ When this key frame occurs, in seconds.+  , frame :: U32.U32+  -- ^ The frame number of this key frame, starting from 0.+  , position :: U32.U32+  -- ^ The bit position of this key frame in the stream.+  }+  deriving (Eq, Show)++instance Json.FromJSON Keyframe where+  parseJSON = Json.withObject "Keyframe" $ \object -> do+    time <- Json.required object "time"+    frame <- Json.required object "frame"+    position <- Json.required object "position"+    pure Keyframe { time, frame, position }++instance Json.ToJSON Keyframe where+  toJSON x = Json.object+    [ Json.pair "time" $ time x+    , Json.pair "frame" $ frame x+    , Json.pair "position" $ position x+    ]++schema :: Schema.Schema+schema = Schema.named "keyframe" $ Schema.object+  [ (Json.pair "time" $ Schema.ref F32.schema, True)+  , (Json.pair "frame" $ Schema.ref U32.schema, True)+  , (Json.pair "position" $ Schema.ref U32.schema, True)+  ]++bytePut :: Keyframe -> BytePut.BytePut+bytePut x =+  F32.bytePut (time x) <> U32.bytePut (frame x) <> U32.bytePut (position x)++byteGet :: ByteGet.ByteGet Keyframe+byteGet = do+  time <- F32.byteGet+  frame <- U32.byteGet+  position <- U32.byteGet+  pure Keyframe { time, frame, position }
src/lib/Rattletrap/Type/Mark.hs view
@@ -35,4 +35,7 @@ bytePut x = Str.bytePut (value x) <> U32.bytePut (frame x)  byteGet :: ByteGet.ByteGet Mark-byteGet = Mark <$> Str.byteGet <*> U32.byteGet+byteGet = do+  value <- Str.byteGet+  frame <- U32.byteGet+  pure Mark { value, frame }
src/lib/Rattletrap/Type/Message.hs view
@@ -40,10 +40,11 @@  bytePut :: Message -> BytePut.BytePut bytePut x =-  do-      U32.bytePut (frame x)-    <> Str.bytePut (name x)-    <> Str.bytePut (value x)+  U32.bytePut (frame x) <> Str.bytePut (name x) <> Str.bytePut (value x)  byteGet :: ByteGet.ByteGet Message-byteGet = Message <$> U32.byteGet <*> Str.byteGet <*> Str.byteGet+byteGet = do+  frame <- U32.byteGet+  name <- Str.byteGet+  value <- Str.byteGet+  pure Message { frame, name, value }
src/lib/Rattletrap/Type/Property.hs view
@@ -45,5 +45,7 @@  byteGet :: ByteGet.ByteGet Property byteGet = do-  kind_ <- Str.byteGet-  Property kind_ <$> U64.byteGet <*> PropertyValue.byteGet byteGet kind_+  kind <- Str.byteGet+  size <- U64.byteGet+  value <- PropertyValue.byteGet byteGet kind+  pure Property { kind, size, value }
+ src/lib/Rattletrap/Type/Property/Array.hs view
@@ -0,0 +1,35 @@+module Rattletrap.Type.Property.Array where++import qualified Rattletrap.ByteGet as ByteGet+import qualified Rattletrap.BytePut as BytePut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.Dictionary as Dictionary+import qualified Rattletrap.Type.List as List+import qualified Rattletrap.Utility.Json as Json++newtype Array a+  = Array (List.List (Dictionary.Dictionary a))+  deriving (Eq, Show)++fromList :: List.List (Dictionary.Dictionary a) -> Array a+fromList = Array++toList :: Array a -> List.List (Dictionary.Dictionary a)+toList (Array x) = x++instance Json.FromJSON a => Json.FromJSON (Array a) where+  parseJSON = fmap fromList . Json.parseJSON++instance Json.ToJSON a => Json.ToJSON (Array a) where+  toJSON = Json.toJSON . toList++schema :: Schema.Schema -> Schema.Schema+schema s =+  Schema.named "property-array" . Schema.json . List.schema $ Dictionary.schema+    s++bytePut :: (a -> BytePut.BytePut) -> Array a -> BytePut.BytePut+bytePut f = List.bytePut (Dictionary.bytePut f) . toList++byteGet :: ByteGet.ByteGet a -> ByteGet.ByteGet (Array a)+byteGet f = fmap fromList $ List.byteGet (Dictionary.byteGet f)
+ src/lib/Rattletrap/Type/Property/Bool.hs view
@@ -0,0 +1,33 @@+module Rattletrap.Type.Property.Bool where++import Prelude hiding (Bool)+import qualified Rattletrap.ByteGet as ByteGet+import qualified Rattletrap.BytePut as BytePut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.U8 as U8+import qualified Rattletrap.Utility.Json as Json++newtype Bool+  = Bool U8.U8+  deriving (Eq, Show)++fromU8 :: U8.U8 -> Bool+fromU8 = Bool++toU8 :: Bool -> U8.U8+toU8 (Bool x) = x++instance Json.FromJSON Bool where+  parseJSON = fmap fromU8 . Json.parseJSON++instance Json.ToJSON Bool where+  toJSON = Json.toJSON . toU8++schema :: Schema.Schema+schema = U8.schema++bytePut :: Bool -> BytePut.BytePut+bytePut = U8.bytePut . toU8++byteGet :: ByteGet.ByteGet Bool+byteGet = fmap fromU8 U8.byteGet
+ src/lib/Rattletrap/Type/Property/Byte.hs view
@@ -0,0 +1,37 @@+module Rattletrap.Type.Property.Byte where++import qualified Rattletrap.ByteGet as ByteGet+import qualified Rattletrap.BytePut as BytePut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.Str as Str+import qualified Rattletrap.Utility.Json as Json+import qualified Rattletrap.Utility.Monad as Monad++data Byte = Byte+  { key :: Str.Str+  , value :: Maybe Str.Str+  }+  deriving (Eq, Show)++instance Json.FromJSON Byte where+  parseJSON json = do+    (key, value) <- Json.parseJSON json+    pure Byte { key, value }++instance Json.ToJSON Byte where+  toJSON byte = Json.toJSON (key byte, value byte)++schema :: Schema.Schema+schema = Schema.named "property-byte" $ Schema.tuple+  [Schema.ref Str.schema, Schema.json $ Schema.maybe Str.schema]++bytePut :: Byte -> BytePut.BytePut+bytePut byte = Str.bytePut (key byte) <> foldMap Str.bytePut (value byte)++byteGet :: ByteGet.ByteGet Byte+byteGet = do+  key <- Str.byteGet+  value <- Monad.whenMaybe+    (Str.toString key /= "OnlinePlatform_Steam")+    Str.byteGet+  pure Byte { key, value }
+ src/lib/Rattletrap/Type/Property/Float.hs view
@@ -0,0 +1,33 @@+module Rattletrap.Type.Property.Float where++import Prelude hiding (Float)+import qualified Rattletrap.ByteGet as ByteGet+import qualified Rattletrap.BytePut as BytePut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.F32 as F32+import qualified Rattletrap.Utility.Json as Json++newtype Float+  = Float F32.F32+  deriving (Eq, Show)++fromF32 :: F32.F32 -> Float+fromF32 = Float++toF32 :: Float -> F32.F32+toF32 (Float x) = x++instance Json.FromJSON Float where+  parseJSON = fmap fromF32 . Json.parseJSON++instance Json.ToJSON Float where+  toJSON = Json.toJSON . toF32++schema :: Schema.Schema+schema = F32.schema++bytePut :: Float -> BytePut.BytePut+bytePut = F32.bytePut . toF32++byteGet :: ByteGet.ByteGet Float+byteGet = fmap fromF32 F32.byteGet
+ src/lib/Rattletrap/Type/Property/Int.hs view
@@ -0,0 +1,33 @@+module Rattletrap.Type.Property.Int where++import Prelude hiding (Int)+import qualified Rattletrap.ByteGet as ByteGet+import qualified Rattletrap.BytePut as BytePut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.I32 as I32+import qualified Rattletrap.Utility.Json as Json++newtype Int+  = Int I32.I32+  deriving (Eq, Show)++fromI32 :: I32.I32 -> Int+fromI32 = Int++toI32 :: Int -> I32.I32+toI32 (Int x) = x++instance Json.FromJSON Int where+  parseJSON = fmap fromI32 . Json.parseJSON++instance Json.ToJSON Int where+  toJSON = Json.toJSON . toI32++schema :: Schema.Schema+schema = I32.schema++bytePut :: Int -> BytePut.BytePut+bytePut = I32.bytePut . toI32++byteGet :: ByteGet.ByteGet Int+byteGet = fmap fromI32 I32.byteGet
+ src/lib/Rattletrap/Type/Property/Name.hs view
@@ -0,0 +1,32 @@+module Rattletrap.Type.Property.Name where++import qualified Rattletrap.ByteGet as ByteGet+import qualified Rattletrap.BytePut as BytePut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.Str as Str+import qualified Rattletrap.Utility.Json as Json++newtype Name+  = Name Str.Str+  deriving (Eq, Show)++fromStr :: Str.Str -> Name+fromStr = Name++toStr :: Name -> Str.Str+toStr (Name x) = x++instance Json.FromJSON Name where+  parseJSON = fmap fromStr . Json.parseJSON++instance Json.ToJSON Name where+  toJSON = Json.toJSON . toStr++schema :: Schema.Schema+schema = Str.schema++bytePut :: Name -> BytePut.BytePut+bytePut = Str.bytePut . toStr++byteGet :: ByteGet.ByteGet Name+byteGet = fmap fromStr Str.byteGet
+ src/lib/Rattletrap/Type/Property/QWord.hs view
@@ -0,0 +1,32 @@+module Rattletrap.Type.Property.QWord where++import qualified Rattletrap.ByteGet as ByteGet+import qualified Rattletrap.BytePut as BytePut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.U64 as U64+import qualified Rattletrap.Utility.Json as Json++newtype QWord+  = QWord U64.U64+  deriving (Eq, Show)++fromU64 :: U64.U64 -> QWord+fromU64 = QWord++toU64 :: QWord -> U64.U64+toU64 (QWord x) = x++instance Json.FromJSON QWord where+  parseJSON = fmap fromU64 . Json.parseJSON++instance Json.ToJSON QWord where+  toJSON = Json.toJSON . toU64++schema :: Schema.Schema+schema = U64.schema++bytePut :: QWord -> BytePut.BytePut+bytePut = U64.bytePut . toU64++byteGet :: ByteGet.ByteGet QWord+byteGet = fmap fromU64 U64.byteGet
+ src/lib/Rattletrap/Type/Property/Str.hs view
@@ -0,0 +1,32 @@+module Rattletrap.Type.Property.Str where++import qualified Rattletrap.ByteGet as ByteGet+import qualified Rattletrap.BytePut as BytePut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.Str as Str+import qualified Rattletrap.Utility.Json as Json++newtype Str+  = Str Str.Str+  deriving (Eq, Show)++fromStr :: Str.Str -> Str+fromStr = Str++toStr :: Str -> Str.Str+toStr (Str x) = x++instance Json.FromJSON Str where+  parseJSON = fmap fromStr . Json.parseJSON++instance Json.ToJSON Str where+  toJSON = Json.toJSON . toStr++schema :: Schema.Schema+schema = Str.schema++bytePut :: Str -> BytePut.BytePut+bytePut = Str.bytePut . toStr++byteGet :: ByteGet.ByteGet Str+byteGet = fmap fromStr Str.byteGet
src/lib/Rattletrap/Type/PropertyValue.hs view
@@ -1,52 +1,52 @@ module Rattletrap.Type.PropertyValue where  import qualified Data.Foldable as Foldable-import qualified Data.Text as Text import qualified Rattletrap.ByteGet as ByteGet import qualified Rattletrap.BytePut as BytePut import qualified Rattletrap.Schema as Schema-import qualified Rattletrap.Type.Dictionary as Dictionary-import qualified Rattletrap.Type.F32 as F32-import qualified Rattletrap.Type.I32 as I32-import qualified Rattletrap.Type.List as List+import qualified Rattletrap.Type.Property.Array as Property.Array+import qualified Rattletrap.Type.Property.Bool as Property.Bool+import qualified Rattletrap.Type.Property.Byte as Property.Byte+import qualified Rattletrap.Type.Property.Float as Property.Float+import qualified Rattletrap.Type.Property.Int as Property.Int+import qualified Rattletrap.Type.Property.Name as Property.Name+import qualified Rattletrap.Type.Property.QWord as Property.QWord+import qualified Rattletrap.Type.Property.Str as Property.Str import qualified Rattletrap.Type.Str as Str-import qualified Rattletrap.Type.U64 as U64-import qualified Rattletrap.Type.U8 as U8 import qualified Rattletrap.Utility.Json as Json-import Rattletrap.Utility.Monad  data PropertyValue a-  = Array (List.List (Dictionary.Dictionary a))+  = Array (Property.Array.Array a)   -- ^ Yes, a list of dictionaries. No, it doesn't make sense. These usually   -- only have one element.-  | Bool U8.U8-  | Byte Str.Str (Maybe Str.Str)+  | Bool Property.Bool.Bool+  | Byte Property.Byte.Byte   -- ^ This is a strange name for essentially a key-value pair.-  | Float F32.F32-  | Int I32.I32-  | Name Str.Str+  | Float Property.Float.Float+  | Int Property.Int.Int+  | Name Property.Name.Name   -- ^ It's unclear how exactly this is different than a 'StrProperty'.-  | QWord U64.U64-  | Str Str.Str+  | QWord Property.QWord.QWord+  | Str Property.Str.Str   deriving (Eq, Show)  instance Json.FromJSON a => Json.FromJSON (PropertyValue a) where   parseJSON = Json.withObject "PropertyValue" $ \object -> Foldable.asum-    [ Array <$> Json.required object "array"-    , Bool <$> Json.required object "bool"-    , uncurry Byte <$> Json.required object "byte"-    , Float <$> Json.required object "float"-    , Int <$> Json.required object "int"-    , Name <$> Json.required object "name"-    , QWord <$> Json.required object "q_word"-    , Str <$> Json.required object "str"+    [ fmap Array $ Json.required object "array"+    , fmap Bool $ Json.required object "bool"+    , fmap Byte $ Json.required object "byte"+    , fmap Float $ Json.required object "float"+    , fmap Int $ Json.required object "int"+    , fmap Name $ Json.required object "name"+    , fmap QWord $ Json.required object "q_word"+    , fmap Str $ Json.required object "str"     ]  instance Json.ToJSON a => Json.ToJSON (PropertyValue a) where   toJSON x = case x of     Array y -> Json.object [Json.pair "array" y]     Bool y -> Json.object [Json.pair "bool" y]-    Byte y z -> Json.object [Json.pair "byte" (y, z)]+    Byte y -> Json.object [Json.pair "byte" y]     Float y -> Json.object [Json.pair "float" y]     Int y -> Json.object [Json.pair "int" y]     Name y -> Json.object [Json.pair "name" y]@@ -54,45 +54,37 @@     Str y -> Json.object [Json.pair "str" y]  schema :: Schema.Schema -> Schema.Schema-schema s =-  Schema.named ("property-value-" <> Text.unpack (Schema.name s))-    . Schema.oneOf-    $ fmap-        (\(k, v) -> Schema.object [(Json.pair k v, True)])-        [ ("array", Schema.json . List.schema $ Dictionary.schema s)-        , ("bool", Schema.ref U8.schema)-        , ( "byte"-          , Schema.tuple-            [Schema.ref Str.schema, Schema.json $ Schema.maybe Str.schema]-          )-        , ("float", Schema.ref F32.schema)-        , ("int", Schema.ref I32.schema)-        , ("name", Schema.ref Str.schema)-        , ("q_word", Schema.ref U64.schema)-        , ("str", Schema.ref Str.schema)-        ]+schema s = Schema.named "property-value" . Schema.oneOf $ fmap+  (\(k, v) -> Schema.object [(Json.pair k v, True)])+  [ ("array", Schema.ref $ Property.Array.schema s)+  , ("bool", Schema.ref Property.Bool.schema)+  , ("byte", Schema.ref Property.Byte.schema)+  , ("float", Schema.ref Property.Float.schema)+  , ("int", Schema.ref Property.Int.schema)+  , ("name", Schema.ref Property.Name.schema)+  , ("q_word", Schema.ref Property.QWord.schema)+  , ("str", Schema.ref Property.Str.schema)+  ]  bytePut :: (a -> BytePut.BytePut) -> PropertyValue a -> BytePut.BytePut bytePut putProperty value = case value of-  Array x -> List.bytePut (Dictionary.bytePut putProperty) x-  Bool x -> U8.bytePut x-  Byte k mv -> Str.bytePut k <> foldMap Str.bytePut mv-  Float x -> F32.bytePut x-  Int x -> I32.bytePut x-  Name x -> Str.bytePut x-  QWord x -> U64.bytePut x-  Str x -> Str.bytePut x+  Array x -> Property.Array.bytePut putProperty x+  Bool x -> Property.Bool.bytePut x+  Byte x -> Property.Byte.bytePut x+  Float x -> Property.Float.bytePut x+  Int x -> Property.Int.bytePut x+  Name x -> Property.Name.bytePut x+  QWord x -> Property.QWord.bytePut x+  Str x -> Property.Str.bytePut x  byteGet :: ByteGet.ByteGet a -> Str.Str -> ByteGet.ByteGet (PropertyValue a) byteGet getProperty kind = case Str.toString kind of-  "ArrayProperty" -> Array <$> List.byteGet (Dictionary.byteGet getProperty)-  "BoolProperty" -> Bool <$> U8.byteGet-  "ByteProperty" -> do-    k <- Str.byteGet-    Byte k <$> whenMaybe (Str.toString k /= "OnlinePlatform_Steam") Str.byteGet-  "FloatProperty" -> Float <$> F32.byteGet-  "IntProperty" -> Int <$> I32.byteGet-  "NameProperty" -> Name <$> Str.byteGet-  "QWordProperty" -> QWord <$> U64.byteGet-  "StrProperty" -> Str <$> Str.byteGet+  "ArrayProperty" -> fmap Array $ Property.Array.byteGet getProperty+  "BoolProperty" -> fmap Bool Property.Bool.byteGet+  "ByteProperty" -> fmap Byte Property.Byte.byteGet+  "FloatProperty" -> fmap Float Property.Float.byteGet+  "IntProperty" -> fmap Int Property.Int.byteGet+  "NameProperty" -> fmap Name Property.Name.byteGet+  "QWordProperty" -> fmap QWord Property.QWord.byteGet+  "StrProperty" -> fmap Str Property.Str.byteGet   _ -> fail ("[RT07] don't know how to read property value " <> show kind)
src/lib/Rattletrap/Type/Quaternion.hs view
@@ -144,8 +144,12 @@ putPart = CompressedWord.bitPut . compressPart  bitGet :: BitGet.BitGet Quaternion-bitGet =-  toQuaternion <$> decodeComponent <*> decodePart <*> decodePart <*> decodePart+bitGet = do+  component <- decodeComponent+  a <- decodePart+  b <- decodePart+  c <- decodePart+  pure $ toQuaternion component a b c  decodeComponent :: BitGet.BitGet Component decodeComponent = do@@ -158,4 +162,4 @@     y_ -> fail ("[RT08] invalid component: " <> show y_)  decodePart :: BitGet.BitGet Double-decodePart = decompressPart <$> CompressedWord.bitGet maxCompressedValue+decodePart = fmap decompressPart $ CompressedWord.bitGet maxCompressedValue
src/lib/Rattletrap/Type/RemoteId.hs view
@@ -3,133 +3,80 @@ import qualified Rattletrap.BitGet as BitGet import qualified Rattletrap.BitPut as BitPut import qualified Rattletrap.Schema as Schema-import qualified Rattletrap.Type.Str as Str-import qualified Rattletrap.Type.U64 as U64+import qualified Rattletrap.Type.RemoteId.Epic as Epic+import qualified Rattletrap.Type.RemoteId.PlayStation as PlayStation+import qualified Rattletrap.Type.RemoteId.PsyNet as PsyNet+import qualified Rattletrap.Type.RemoteId.Splitscreen as Splitscreen+import qualified Rattletrap.Type.RemoteId.Steam as Steam+import qualified Rattletrap.Type.RemoteId.Switch as Switch+import qualified Rattletrap.Type.RemoteId.Xbox as Xbox import qualified Rattletrap.Type.U8 as U8 import qualified Rattletrap.Type.Version as Version-import Rattletrap.Utility.Bytes import qualified Rattletrap.Utility.Json as Json -import qualified Data.ByteString as Bytes import qualified Data.Foldable as Foldable-import qualified Data.Text as Text-import qualified Data.Text.Encoding as Text-import qualified Data.Word as Word  data RemoteId-  = PlayStation Text.Text [Word.Word8]-  | PsyNet (Either U64.U64 (U64.U64, U64.U64, U64.U64, U64.U64))-  | Splitscreen Word.Word32+  = PlayStation PlayStation.PlayStation+  | PsyNet PsyNet.PsyNet+  | Splitscreen Splitscreen.Splitscreen   -- ^ Really only 24 bits.-  | Steam U64.U64-  | Switch U64.U64 U64.U64 U64.U64 U64.U64-  | Xbox U64.U64-  | Epic Str.Str+  | Steam Steam.Steam+  | Switch Switch.Switch+  | Xbox Xbox.Xbox+  | Epic Epic.Epic   deriving (Eq, Show)  instance Json.FromJSON RemoteId where   parseJSON = Json.withObject "RemoteId" $ \object -> Foldable.asum-    [ uncurry PlayStation <$> Json.required object "play_station"-    , PsyNet <$> Json.required object "psy_net"-    , Splitscreen <$> Json.required object "splitscreen"-    , Steam <$> Json.required object "steam"-    , uncurry4 Switch <$> Json.required object "switch"-    , Xbox <$> Json.required object "xbox"-    , Epic <$> Json.required object "epic"+    [ fmap PlayStation $ Json.required object "play_station"+    , fmap PsyNet $ Json.required object "psy_net"+    , fmap Splitscreen $ Json.required object "splitscreen"+    , fmap Steam $ Json.required object "steam"+    , fmap Switch $ Json.required object "switch"+    , fmap Xbox $ Json.required object "xbox"+    , fmap Epic $ Json.required object "epic"     ] -uncurry4 :: (a -> b -> c -> d -> e) -> (a, b, c, d) -> e-uncurry4 f (a, b, c, d) = f a b c d- instance Json.ToJSON RemoteId where   toJSON x = case x of-    PlayStation y z -> Json.object [Json.pair "play_station" (y, z)]+    PlayStation y -> Json.object [Json.pair "play_station" y]     PsyNet y -> Json.object [Json.pair "psy_net" y]     Splitscreen y -> Json.object [Json.pair "splitscreen" y]     Steam y -> Json.object [Json.pair "steam" y]-    Switch y z a b -> Json.object [Json.pair "switch" (y, z, a, b)]+    Switch y -> Json.object [Json.pair "switch" y]     Xbox y -> Json.object [Json.pair "xbox" y]     Epic y -> Json.object [Json.pair "epic" y]  schema :: Schema.Schema schema = Schema.named "remote-id" . Schema.oneOf $ fmap   (\(k, v) -> Schema.object [(Json.pair k v, True)])-  [ ( "play_station"-    , Schema.tuple-      [Schema.ref Schema.string, Schema.json $ Schema.array Schema.number]-    )-  , ( "psy_net"-    , Schema.oneOf-      [ Schema.object [(Json.pair "Left" $ Schema.ref U64.schema, True)]-      , Schema.object-        [ ( Json.pair "Right" . Schema.tuple . replicate 4 $ Schema.ref-            U64.schema-          , True-          )-        ]-      ]-    )-  , ("splitscreen", Schema.ref Schema.integer)-  , ("steam", Schema.ref U64.schema)-  , ("switch", Schema.tuple . replicate 4 $ Schema.ref U64.schema)-  , ("xbox", Schema.ref U64.schema)-  , ("epic", Schema.ref Str.schema)+  [ ("play_station", Schema.ref PlayStation.schema)+  , ("psy_net", Schema.ref PsyNet.schema)+  , ("splitscreen", Schema.ref Splitscreen.schema)+  , ("steam", Schema.ref Steam.schema)+  , ("switch", Schema.ref Switch.schema)+  , ("xbox", Schema.ref Xbox.schema)+  , ("epic", Schema.ref Epic.schema)   ]  bitPut :: RemoteId -> BitPut.BitPut bitPut remoteId = case remoteId of-  PlayStation name bytes ->-    let rawName = reverseBytes (padBytes (16 :: Int) (encodeLatin1 name))-    in BitPut.byteString rawName <> BitPut.byteString (Bytes.pack bytes)-  PsyNet e -> case e of-    Left l -> U64.bitPut l-    Right (a, b, c, d) -> putWord256 a b c d-  Splitscreen word24 -> BitPut.bits 24 word24-  Steam word64 -> U64.bitPut word64-  Switch a b c d -> putWord256 a b c d-  Xbox word64 -> U64.bitPut word64-  Epic str -> Str.bitPut str--putWord256 :: U64.U64 -> U64.U64 -> U64.U64 -> U64.U64 -> BitPut.BitPut-putWord256 a b c d =-  U64.bitPut a <> U64.bitPut b <> U64.bitPut c <> U64.bitPut d+  PlayStation x -> PlayStation.bitPut x+  PsyNet x -> PsyNet.bitPut x+  Splitscreen x -> Splitscreen.bitPut x+  Steam x -> Steam.bitPut x+  Switch x -> Switch.bitPut x+  Xbox x -> Xbox.bitPut x+  Epic x -> Epic.bitPut x  bitGet :: Version.Version -> U8.U8 -> BitGet.BitGet RemoteId bitGet version systemId = case U8.toWord8 systemId of-  0 -> Splitscreen <$> BitGet.bits 24-  1 -> Steam <$> U64.bitGet-  2 -> PlayStation <$> decodePsName <*> decodePsBytes version-  4 -> Xbox <$> U64.bitGet-  6 -> do-    (a, b, c, d) <- getWord256-    pure $ Switch a b c d-  7 -> if psyNetIsU64 version-    then PsyNet . Left <$> U64.bitGet-    else PsyNet . Right <$> getWord256-  11 -> Epic <$> Str.bitGet+  0 -> fmap Splitscreen Splitscreen.bitGet+  1 -> fmap Steam Steam.bitGet+  2 -> fmap PlayStation $ PlayStation.bitGet version+  4 -> fmap Xbox Xbox.bitGet+  6 -> fmap Switch Switch.bitGet+  7 -> fmap PsyNet $ PsyNet.bitGet version+  11 -> fmap Epic Epic.bitGet   _ -> fail ("[RT09] unknown system id " <> show systemId)--psyNetIsU64 :: Version.Version -> Bool-psyNetIsU64 v =-  Version.major v >= 868 && Version.minor v >= 24 && Version.patch v >= 10--decodePsName :: BitGet.BitGet Text.Text-decodePsName = fmap-  (Text.dropWhileEnd (== '\x00') . Text.decodeLatin1 . reverseBytes)-  (BitGet.byteString 16)--decodePsBytes :: Version.Version -> BitGet.BitGet [Word.Word8]-decodePsBytes version = Bytes.unpack-  <$> BitGet.byteString (if playStationIsU24 version then 24 else 16)--playStationIsU24 :: Version.Version -> Bool-playStationIsU24 v =-  Version.major v >= 868 && Version.minor v >= 20 && Version.patch v >= 1--getWord256 :: BitGet.BitGet (U64.U64, U64.U64, U64.U64, U64.U64)-getWord256 = do-  a <- U64.bitGet-  b <- U64.bitGet-  c <- U64.bitGet-  d <- U64.bitGet-  pure (a, b, c, d)
+ src/lib/Rattletrap/Type/RemoteId/Epic.hs view
@@ -0,0 +1,32 @@+module Rattletrap.Type.RemoteId.Epic where++import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.Str as Str+import qualified Rattletrap.Utility.Json as Json++newtype Epic+  = Epic Str.Str+  deriving (Eq, Show)++instance Json.FromJSON Epic where+  parseJSON = fmap fromStr . Json.parseJSON++instance Json.ToJSON Epic where+  toJSON = Json.toJSON . toStr++fromStr :: Str.Str -> Epic+fromStr = Epic++toStr :: Epic -> Str.Str+toStr (Epic x) = x++schema :: Schema.Schema+schema = Str.schema++bitPut :: Epic -> BitPut.BitPut+bitPut = Str.bitPut . toStr++bitGet :: BitGet.BitGet Epic+bitGet = fmap fromStr Str.bitGet
+ src/lib/Rattletrap/Type/RemoteId/PlayStation.hs view
@@ -0,0 +1,53 @@+module Rattletrap.Type.RemoteId.PlayStation where++import qualified Data.ByteString as ByteString+import qualified Data.Text as Text+import qualified Data.Text.Encoding as Text+import qualified Data.Word as Word+import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.Version as Version+import qualified Rattletrap.Utility.Bytes as Bytes+import qualified Rattletrap.Utility.Json as Json++data PlayStation = PlayStation+  { name :: Text.Text+  , code :: [Word.Word8]+  }+  deriving (Eq, Show)++instance Json.FromJSON PlayStation where+  parseJSON json = do+    (name, code) <- Json.parseJSON json+    pure PlayStation { name, code }++instance Json.ToJSON PlayStation where+  toJSON x = Json.toJSON (name x, code x)++schema :: Schema.Schema+schema = Schema.named "remote-id-play-station" $ Schema.tuple+  [Schema.ref Schema.string, Schema.json $ Schema.array Schema.number]++bitPut :: PlayStation -> BitPut.BitPut+bitPut x =+  let+    nameBytes = Bytes.padBytes (16 :: Int) . Bytes.encodeLatin1 $ name x+    codeBytes = ByteString.pack $ code x+  in BitPut.byteString nameBytes <> BitPut.byteString codeBytes++bitGet :: Version.Version -> BitGet.BitGet PlayStation+bitGet version = do+  name <- getCode+  code <- getName version+  pure PlayStation { name, code }++getCode :: BitGet.BitGet Text.Text+getCode = fmap (Text.dropWhileEnd (== '\x00') . Text.decodeLatin1)+  $ BitGet.byteString 16++getName :: Version.Version -> BitGet.BitGet [Word.Word8]+getName version =+  fmap ByteString.unpack+    . BitGet.byteString+    $ if Version.atLeast 868 20 1 version then 24 else 16
+ src/lib/Rattletrap/Type/RemoteId/PsyNet.hs view
@@ -0,0 +1,50 @@+module Rattletrap.Type.RemoteId.PsyNet where++import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.U64 as U64+import qualified Rattletrap.Type.Version as Version+import qualified Rattletrap.Utility.Json as Json++newtype PsyNet+  = PsyNet (Either U64.U64 (U64.U64, U64.U64, U64.U64, U64.U64))+  deriving (Eq, Show)++instance Json.FromJSON PsyNet where+  parseJSON = fmap fromEither . Json.parseJSON++instance Json.ToJSON PsyNet where+  toJSON = Json.toJSON . toEither++fromEither :: Either U64.U64 (U64.U64, U64.U64, U64.U64, U64.U64) -> PsyNet+fromEither = PsyNet++toEither :: PsyNet -> Either U64.U64 (U64.U64, U64.U64, U64.U64, U64.U64)+toEither (PsyNet x) = x++schema :: Schema.Schema+schema = Schema.named "remote-id-psy-net" $ Schema.oneOf+  [ Schema.object [(Json.pair "Left" $ Schema.ref U64.schema, True)]+  , Schema.object+    [ ( Json.pair "Right" . Schema.tuple . replicate 4 $ Schema.ref U64.schema+      , True+      )+    ]+  ]++bitPut :: PsyNet -> BitPut.BitPut+bitPut x = case toEither x of+  Left l -> U64.bitPut l+  Right (a, b, c, d) ->+    U64.bitPut a <> U64.bitPut b <> U64.bitPut c <> U64.bitPut d++bitGet :: Version.Version -> BitGet.BitGet PsyNet+bitGet version = fmap fromEither $ if Version.atLeast 868 24 10 version+  then fmap Left U64.bitGet+  else fmap Right $ do+    a <- U64.bitGet+    b <- U64.bitGet+    c <- U64.bitGet+    d <- U64.bitGet+    pure (a, b, c, d)
+ src/lib/Rattletrap/Type/RemoteId/Splitscreen.hs view
@@ -0,0 +1,32 @@+module Rattletrap.Type.RemoteId.Splitscreen where++import qualified Data.Word as Word+import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Utility.Json as Json++newtype Splitscreen+  = Splitscreen Word.Word32+  deriving (Eq, Show)++instance Json.FromJSON Splitscreen where+  parseJSON = fmap fromWord32 . Json.parseJSON++instance Json.ToJSON Splitscreen where+  toJSON = Json.toJSON . toWord32++fromWord32 :: Word.Word32 -> Splitscreen+fromWord32 = Splitscreen++toWord32 :: Splitscreen -> Word.Word32+toWord32 (Splitscreen x) = x++schema :: Schema.Schema+schema = Schema.integer++bitPut :: Splitscreen -> BitPut.BitPut+bitPut = BitPut.bits 24 . toWord32++bitGet :: BitGet.BitGet Splitscreen+bitGet = fmap fromWord32 $ BitGet.bits 24
+ src/lib/Rattletrap/Type/RemoteId/Steam.hs view
@@ -0,0 +1,32 @@+module Rattletrap.Type.RemoteId.Steam where++import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.U64 as U64+import qualified Rattletrap.Utility.Json as Json++newtype Steam+  = Steam U64.U64+  deriving (Eq, Show)++instance Json.FromJSON Steam where+  parseJSON = fmap fromU64 . Json.parseJSON++instance Json.ToJSON Steam where+  toJSON = Json.toJSON . toU64++fromU64 :: U64.U64 -> Steam+fromU64 = Steam++toU64 :: Steam -> U64.U64+toU64 (Steam x) = x++schema :: Schema.Schema+schema = U64.schema++bitPut :: Steam -> BitPut.BitPut+bitPut = U64.bitPut . toU64++bitGet :: BitGet.BitGet Steam+bitGet = fmap fromU64 U64.bitGet
+ src/lib/Rattletrap/Type/RemoteId/Switch.hs view
@@ -0,0 +1,40 @@+module Rattletrap.Type.RemoteId.Switch where++import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.U64 as U64+import qualified Rattletrap.Utility.Json as Json++data Switch = Switch+  { a :: U64.U64+  , b :: U64.U64+  , c :: U64.U64+  , d :: U64.U64+  }+  deriving (Eq, Show)++instance Json.FromJSON Switch where+  parseJSON json = do+    (a, b, c, d) <- Json.parseJSON json+    pure Switch { a, b, c, d }++instance Json.ToJSON Switch where+  toJSON x = Json.toJSON (a x, b x, c x, d x)++schema :: Schema.Schema+schema =+  Schema.named "remote-id-switch" . Schema.tuple . replicate 4 $ Schema.ref+    U64.schema++bitPut :: Switch -> BitPut.BitPut+bitPut x =+  U64.bitPut (a x) <> U64.bitPut (b x) <> U64.bitPut (c x) <> U64.bitPut (d x)++bitGet :: BitGet.BitGet Switch+bitGet = do+  a <- U64.bitGet+  b <- U64.bitGet+  c <- U64.bitGet+  d <- U64.bitGet+  pure Switch { a, b, c, d }
+ src/lib/Rattletrap/Type/RemoteId/Xbox.hs view
@@ -0,0 +1,32 @@+module Rattletrap.Type.RemoteId.Xbox where++import qualified Rattletrap.BitGet as BitGet+import qualified Rattletrap.BitPut as BitPut+import qualified Rattletrap.Schema as Schema+import qualified Rattletrap.Type.U64 as U64+import qualified Rattletrap.Utility.Json as Json++newtype Xbox+  = Xbox U64.U64+  deriving (Eq, Show)++instance Json.FromJSON Xbox where+  parseJSON = fmap fromU64 . Json.parseJSON++instance Json.ToJSON Xbox where+  toJSON = Json.toJSON . toU64++fromU64 :: U64.U64 -> Xbox+fromU64 = Xbox++toU64 :: Xbox -> U64.U64+toU64 (Xbox x) = x++schema :: Schema.Schema+schema = U64.schema++bitPut :: Xbox -> BitPut.BitPut+bitPut = U64.bitPut . toU64++bitGet :: BitGet.BitGet Xbox+bitGet = fmap fromU64 U64.bitGet
src/lib/Rattletrap/Type/Replay.hs view
@@ -8,11 +8,12 @@ import qualified Rattletrap.Type.Header as Header import qualified Rattletrap.Type.I32 as I32 import qualified Rattletrap.Type.Property as Property+import qualified Rattletrap.Type.Property.Int as Property.Int+import qualified Rattletrap.Type.Property.Name as Property.Name import qualified Rattletrap.Type.PropertyValue as PropertyValue import qualified Rattletrap.Type.Section as Section import qualified Rattletrap.Type.Str as Str import qualified Rattletrap.Type.U32 as U32-import qualified Rattletrap.Type.Version as Version import qualified Rattletrap.Utility.Json as Json import qualified Rattletrap.Version as Version @@ -76,30 +77,19 @@     }  getContent :: Header.Header -> ByteGet.ByteGet Content.Content-getContent h =-  Content.byteGet (getVersion h) (getNumFrames h) (getMaxChannels h)--getVersion :: Header.Header -> Version.Version-getVersion x = Version.Version-  { Version.major = fromIntegral . U32.toWord32 $ Header.engineVersion x-  , Version.minor = fromIntegral . U32.toWord32 $ Header.licenseeVersion x-  , Version.patch = getPatchVersion x-  }+getContent h = Content.byteGet+  (getMatchType h)+  (Header.version h)+  (getNumFrames h)+  (getMaxChannels h) -getPatchVersion :: Header.Header -> Int-getPatchVersion header_ = case Header.patchVersion header_ of-  Just version -> fromIntegral (U32.toWord32 version)-  Nothing ->-    case-        Dictionary.lookup-          (Str.fromString "MatchType")-          (Header.properties header_)-      of-      -- This is an ugly, ugly hack to handle replays from season 2 of RLCS.-      -- See `decodeSpawnedReplicationBits` and #85.-        Just Property.Property { Property.value = PropertyValue.Name str }-          | Str.toString str == "Lan" -> -1-        _ -> 0+getMatchType :: Header.Header -> Maybe Str.Str+getMatchType header = do+  Property.Property { Property.value } <-+    Dictionary.lookup (Str.fromString "MatchType") $ Header.properties header+  case value of+    PropertyValue.Name x -> Just $ Property.Name.toStr x+    _ -> Nothing  getNumFrames :: Header.Header -> Int getNumFrames header_ =@@ -109,7 +99,7 @@         (Header.properties header_)     of       Just (Property.Property _ _ (PropertyValue.Int numFrames)) ->-        fromIntegral (I32.toInt32 numFrames)+        fromIntegral (I32.toInt32 (Property.Int.toI32 numFrames))       _ -> 0  getMaxChannels :: Header.Header -> Word@@ -119,6 +109,6 @@         (Str.fromString "MaxChannels")         (Header.properties header_)     of-      Just (Property.Property _ _ (PropertyValue.Int numFrames)) ->-        fromIntegral (I32.toInt32 numFrames)+      Just (Property.Property _ _ (PropertyValue.Int maxChannels)) ->+        fromIntegral (I32.toInt32 (Property.Int.toI32 maxChannels))       _ -> 1023
src/lib/Rattletrap/Type/Replication.hs view
@@ -7,9 +7,11 @@ import qualified Rattletrap.Type.CompressedWord as CompressedWord import qualified Rattletrap.Type.List as List import qualified Rattletrap.Type.ReplicationValue as ReplicationValue+import qualified Rattletrap.Type.Str as Str import qualified Rattletrap.Type.U32 as U32 import qualified Rattletrap.Type.Version as Version import qualified Rattletrap.Utility.Json as Json+import qualified Rattletrap.Utility.Monad as Monad  import qualified Control.Monad.Trans.Class as Trans import qualified Control.Monad.Trans.State as State@@ -47,25 +49,28 @@   <> ReplicationValue.bitPut (value replication)  decodeReplicationsBits-  :: Version.Version+  :: Maybe Str.Str+  -> Version.Version   -> Word   -> ClassAttributeMap.ClassAttributeMap   -> State.StateT        (Map.Map CompressedWord.CompressedWord U32.U32)        BitGet.BitGet        (List.List Replication)-decodeReplicationsBits version limit classes = List.untilM $ do+decodeReplicationsBits matchType version limit classes = List.untilM $ do   p <- Trans.lift BitGet.bool-  if p then Just <$> bitGet version limit classes else pure Nothing+  Monad.whenMaybe p $ bitGet matchType version limit classes  bitGet-  :: Version.Version+  :: Maybe Str.Str+  -> Version.Version   -> Word   -> ClassAttributeMap.ClassAttributeMap   -> State.StateT        (Map.Map CompressedWord.CompressedWord U32.U32)        BitGet.BitGet        Replication-bitGet version limit classes = do+bitGet matchType version limit classes = do   actor <- Trans.lift (CompressedWord.bitGet limit)-  Replication actor <$> ReplicationValue.bitGet version classes actor+  fmap (Replication actor)+    $ ReplicationValue.bitGet matchType version classes actor
src/lib/Rattletrap/Type/Replication/Spawned.hs view
@@ -10,7 +10,7 @@ import qualified Rattletrap.Type.U32 as U32 import qualified Rattletrap.Type.Version as Version import qualified Rattletrap.Utility.Json as Json-import Rattletrap.Utility.Monad+import qualified Rattletrap.Utility.Monad as Monad  import qualified Control.Monad.Trans.Class as Trans import qualified Control.Monad.Trans.State as State@@ -83,16 +83,18 @@     <> Initialization.bitPut (initialization spawnedReplication)  bitGet-  :: Version.Version+  :: Maybe Str.Str+  -> Version.Version   -> ClassAttributeMap.ClassAttributeMap   -> CompressedWord.CompressedWord   -> State.StateT        (Map.Map CompressedWord.CompressedWord U32.U32)        BitGet.BitGet        Spawned-bitGet version classAttributeMap actorId = do+bitGet matchType version classAttributeMap actorId = do   flag_ <- Trans.lift BitGet.bool-  nameIndex_ <- whenMaybe (hasNameIndex version) (Trans.lift U32.bitGet)+  nameIndex_ <- Monad.whenMaybe (hasNameIndex matchType version)+    $ Trans.lift U32.bitGet   name_ <- either fail pure (lookupName classAttributeMap nameIndex_)   objectId_ <- Trans.lift U32.bitGet   State.modify (Map.insert actorId objectId_)@@ -116,9 +118,9 @@       initialization_     ) -hasNameIndex :: Version.Version -> Bool-hasNameIndex v =-  Version.major v >= 868 && Version.minor v >= 14 && Version.patch v >= 0+hasNameIndex :: Maybe Str.Str -> Version.Version -> Bool+hasNameIndex matchType version =+  Version.atLeast 868 14 0 version && matchType /= Just (Str.fromString "Lan")  lookupName   :: ClassAttributeMap.ClassAttributeMap
src/lib/Rattletrap/Type/Replication/Updated.hs view
@@ -10,6 +10,7 @@ import qualified Rattletrap.Type.U32 as U32 import qualified Rattletrap.Type.Version as Version import qualified Rattletrap.Utility.Json as Json+import qualified Rattletrap.Utility.Monad as Monad  import qualified Data.Map as Map @@ -42,6 +43,4 @@   -> BitGet.BitGet Updated bitGet version classes actors actor = fmap Updated . List.untilM $ do   p <- BitGet.bool-  if p-    then Just <$> Attribute.bitGet version classes actors actor-    else pure Nothing+  Monad.whenMaybe p $ Attribute.bitGet version classes actors actor
src/lib/Rattletrap/Type/ReplicationValue.hs view
@@ -8,6 +8,7 @@ import qualified Rattletrap.Type.Replication.Destroyed as Destroyed import qualified Rattletrap.Type.Replication.Spawned as Spawned import qualified Rattletrap.Type.Replication.Updated as Updated+import qualified Rattletrap.Type.Str as Str import qualified Rattletrap.Type.U32 as U32 import qualified Rattletrap.Type.Version as Version import qualified Rattletrap.Utility.Json as Json@@ -28,9 +29,9 @@  instance Json.FromJSON ReplicationValue where   parseJSON = Json.withObject "ReplicationValue" $ \object -> Foldable.asum-    [ Spawned <$> Json.required object "spawned"-    , Updated <$> Json.required object "updated"-    , Destroyed <$> Json.required object "destroyed"+    [ fmap Spawned $ Json.required object "spawned"+    , fmap Updated $ Json.required object "updated"+    , fmap Destroyed $ Json.required object "destroyed"     ]  instance Json.ToJSON ReplicationValue where@@ -54,21 +55,26 @@   Destroyed x -> BitPut.bool False <> Destroyed.bitPut x  bitGet-  :: Version.Version+  :: Maybe Str.Str+  -> Version.Version   -> ClassAttributeMap.ClassAttributeMap   -> CompressedWord.CompressedWord   -> State.StateT        (Map.Map CompressedWord.CompressedWord U32.U32)        BitGet.BitGet        ReplicationValue-bitGet version classAttributeMap actorId = do+bitGet matchType version classAttributeMap actorId = do   actorMap <- State.get   isOpen <- Trans.lift BitGet.bool   if isOpen     then do       isNew <- Trans.lift BitGet.bool       if isNew-        then Spawned <$> Spawned.bitGet version classAttributeMap actorId-        else Updated <$> Trans.lift-          (Updated.bitGet version classAttributeMap actorMap actorId)-    else Destroyed <$> Trans.lift Destroyed.bitGet+        then fmap Spawned+          $ Spawned.bitGet matchType version classAttributeMap actorId+        else fmap Updated . Trans.lift $ Updated.bitGet+          version+          classAttributeMap+          actorMap+          actorId+    else fmap Destroyed $ Trans.lift Destroyed.bitGet
src/lib/Rattletrap/Type/Rotation.hs view
@@ -16,8 +16,8 @@  instance Json.FromJSON Rotation where   parseJSON = Json.withObject "Rotation" $ \object -> Foldable.asum-    [ CompressedWordVector <$> Json.required object "compressed_word_vector"-    , Quaternion <$> Json.required object "quaternion"+    [ fmap CompressedWordVector $ Json.required object "compressed_word_vector"+    , fmap Quaternion $ Json.required object "quaternion"     ]  instance Json.ToJSON Rotation where@@ -39,10 +39,6 @@   Quaternion q -> Quaternion.bitPut q  bitGet :: Version.Version -> BitGet.BitGet Rotation-bitGet version = if isQuaternion version-  then Quaternion <$> Quaternion.bitGet-  else CompressedWordVector <$> CompressedWordVector.bitGet--isQuaternion :: Version.Version -> Bool-isQuaternion v =-  Version.major v >= 868 && Version.minor v >= 22 && Version.patch v >= 7+bitGet version = if Version.atLeast 868 22 7 version+  then fmap Quaternion Quaternion.bitGet+  else fmap CompressedWordVector CompressedWordVector.bitGet
src/lib/Rattletrap/Type/Section.hs view
@@ -8,7 +8,7 @@ import qualified Rattletrap.Utility.Json as Json  import qualified Control.Monad as Monad-import qualified Data.ByteString as Bytes+import qualified Data.ByteString as ByteString import qualified Data.Text as Text  -- | A section is a large piece of a 'Rattletrap.Replay.Replay'. It has a@@ -52,7 +52,7 @@   let bytes = BytePut.toByteString $ encode body_   in     Section-      { size = U32.fromWord32 . fromIntegral $ Bytes.length bytes+      { size = U32.fromWord32 . fromIntegral $ ByteString.length bytes       , crc = U32.fromWord32 $ Crc.compute bytes       , body = body_       }@@ -63,7 +63,7 @@ bytePut putBody section =   let     rawBody = BytePut.toByteString . putBody $ body section-    size_ = Bytes.length rawBody+    size_ = ByteString.length rawBody     crc_ = Crc.compute rawBody   in     U32.bytePut (U32.fromWord32 (fromIntegral size_))
src/lib/Rattletrap/Type/Str.hs view
@@ -6,16 +6,15 @@ import qualified Rattletrap.BytePut as BytePut import qualified Rattletrap.Schema as Schema import qualified Rattletrap.Type.I32 as I32-import Rattletrap.Utility.Bytes+import qualified Rattletrap.Utility.Bytes as Bytes import qualified Rattletrap.Utility.Json as Json -import qualified Data.ByteString as Bytes+import qualified Data.ByteString as ByteString import qualified Data.Char as Char import qualified Data.Int as Int import qualified Data.Text as Text import qualified Data.Text.Encoding as Text import qualified Data.Text.Encoding.Error as Text-import qualified Debug.Trace as Debug  newtype Str   = Str Text.Text@@ -65,9 +64,10 @@       else scale * rawSize :: Int.Int32   in I32.fromInt32 size -getTextEncoder :: I32.I32 -> Text.Text -> Bytes.ByteString-getTextEncoder size text =-  if I32.toInt32 size < 0 then Text.encodeUtf16LE text else encodeLatin1 text+getTextEncoder :: I32.I32 -> Text.Text -> ByteString.ByteString+getTextEncoder size text = if I32.toInt32 size < 0+  then Text.encodeUtf16LE text+  else Bytes.encodeLatin1 text  addNull :: Text.Text -> Text.Text addNull text = if Text.null text then text else Text.snoc text '\x00'@@ -82,20 +82,18 @@ bitGet = do   rawSize <- I32.bitGet   bytes <- BitGet.byteString (normalizeTextSize rawSize)-  pure (fromText (dropNull (getTextDecoder rawSize (reverseBytes bytes))))+  pure (fromText (dropNull (getTextDecoder rawSize bytes)))  normalizeTextSize :: Integral a => I32.I32 -> a normalizeTextSize size = case I32.toInt32 size of   0x05000000 -> 8   x -> if x < 0 then (-2 * fromIntegral x) else fromIntegral x -getTextDecoder :: I32.I32 -> Bytes.ByteString -> Text.Text+getTextDecoder :: I32.I32 -> ByteString.ByteString -> Text.Text getTextDecoder size bytes =   let     decode = if I32.toInt32 size < 0-      then Text.decodeUtf16LEWith $ \message input -> do-        Debug.traceM $ "WARNING: " <> show (Text.DecodeError message input)-        Text.lenientDecode message input+      then Text.decodeUtf16LEWith Text.lenientDecode       else Text.decodeLatin1   in decode bytes 
src/lib/Rattletrap/Type/U32.hs view
@@ -38,7 +38,7 @@ bitPut = BitPut.fromBytePut . bytePut  byteGet :: ByteGet.ByteGet U32-byteGet = fromWord32 <$> ByteGet.word32+byteGet = fmap fromWord32 ByteGet.word32  bitGet :: BitGet.BitGet U32 bitGet = BitGet.fromByteGet byteGet 4
src/lib/Rattletrap/Type/U64.hs view
@@ -42,7 +42,7 @@ bitPut = BitPut.fromBytePut . bytePut  byteGet :: ByteGet.ByteGet U64-byteGet = fromWord64 <$> ByteGet.word64+byteGet = fmap fromWord64 ByteGet.word64  bitGet :: BitGet.BitGet U64 bitGet = BitGet.fromByteGet byteGet 8
src/lib/Rattletrap/Type/U8.hs view
@@ -38,7 +38,7 @@ bitPut = BitPut.fromBytePut . bytePut  byteGet :: ByteGet.ByteGet U8-byteGet = fromWord8 <$> ByteGet.word8+byteGet = fmap fromWord8 ByteGet.word8  bitGet :: BitGet.BitGet U8 bitGet = BitGet.fromByteGet byteGet 1
src/lib/Rattletrap/Type/Vector.hs view
@@ -67,18 +67,18 @@  bitGet :: Version.Version -> BitGet.BitGet Vector bitGet version = do-  size_ <- CompressedWord.bitGet (if has21Bits version then 21 else 19)+  size <- CompressedWord.bitGet+    $ if Version.atLeast 868 22 7 version then 21 else 19   let-    limit = getLimit size_-    bias_ = getBias size_-  Vector size_ bias_-    <$> fmap (fromDelta bias_) (CompressedWord.bitGet limit)-    <*> fmap (fromDelta bias_) (CompressedWord.bitGet limit)-    <*> fmap (fromDelta bias_) (CompressedWord.bitGet limit)+    limit = getLimit size+    bias = getBias size+  x <- getPart limit bias+  y <- getPart limit bias+  z <- getPart limit bias+  pure Vector { size, bias, x, y, z } -has21Bits :: Version.Version -> Bool-has21Bits v =-  Version.major v >= 868 && Version.minor v >= 22 && Version.patch v >= 7+getPart :: Word -> Word -> BitGet.BitGet Int+getPart limit bias = fmap (fromDelta bias) (CompressedWord.bitGet limit)  getLimit :: CompressedWord.CompressedWord -> Word getLimit = (2 ^) . (+ 2) . CompressedWord.value
src/lib/Rattletrap/Type/Version.hs view
@@ -1,8 +1,33 @@ module Rattletrap.Type.Version where +import qualified Rattletrap.ByteGet as ByteGet+import qualified Rattletrap.BytePut as BytePut+import qualified Rattletrap.Type.U32 as U32+import qualified Rattletrap.Utility.Monad as Monad+ data Version = Version-  { major :: Int-  , minor :: Int-  , patch :: Int+  { major :: U32.U32+  , minor :: U32.U32+  , patch :: Maybe U32.U32   }   deriving (Eq, Show)++atLeast :: Int -> Int -> Int -> Version -> Bool+atLeast m n p v =+  (U32.toWord32 (major v) >= fromIntegral m)+    && (U32.toWord32 (minor v) >= fromIntegral n)+    && (maybe 0 U32.toWord32 (patch v) >= fromIntegral p)++bytePut :: Version -> BytePut.BytePut+bytePut x = U32.bytePut (major x) <> U32.bytePut (minor x) <> foldMap+  U32.bytePut+  (patch x)++byteGet :: ByteGet.ByteGet Version+byteGet = do+  major <- U32.byteGet+  minor <- U32.byteGet+  patch <- Monad.whenMaybe+    (U32.toWord32 major >= 868 && U32.toWord32 minor >= 18)+    U32.byteGet+  pure Version { major, minor, patch }
src/lib/Rattletrap/Utility/Bytes.hs view
@@ -1,32 +1,12 @@-module Rattletrap.Utility.Bytes-  ( encodeLatin1-  , padBytes-  , reverseBytes-  ) where+module Rattletrap.Utility.Bytes where -import qualified Data.Bits as Bits-import qualified Data.ByteString as Bytes-import qualified Data.ByteString.Char8 as Bytes8+import qualified Data.ByteString as ByteString+import qualified Data.ByteString.Char8 as Latin1 import qualified Data.Text as Text-import qualified Data.Word as Word -encodeLatin1 :: Text.Text -> Bytes.ByteString-encodeLatin1 text = Bytes8.pack (Text.unpack text)--padBytes :: Integral a => a -> Bytes.ByteString -> Bytes.ByteString-padBytes size bytes =-  bytes <> Bytes.replicate (fromIntegral size - Bytes.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+encodeLatin1 :: Text.Text -> ByteString.ByteString+encodeLatin1 text = Latin1.pack (Text.unpack text) -reverseBytes :: Bytes.ByteString -> Bytes.ByteString-reverseBytes = Bytes.map reverseByte+padBytes :: Integral a => a -> ByteString.ByteString -> ByteString.ByteString+padBytes size bytes = bytes+  <> ByteString.replicate (fromIntegral size - ByteString.length bytes) 0x00
src/lib/Rattletrap/Utility/Crc.hs view
@@ -1,10 +1,8 @@-module Rattletrap.Utility.Crc-  ( compute-  ) where+module Rattletrap.Utility.Crc where  import qualified Data.Array.Unboxed as Array import qualified Data.Bits as Bits-import qualified Data.ByteString as Bytes+import qualified Data.ByteString as ByteString import qualified Data.Word as Word  -- | Computes the CRC32 of some bytes. This is done to ensure that the bytes@@ -16,8 +14,8 @@ -- -- This CRC uses an initial value of @0xefcbf201@ and a polynomial of -- @0x04c11db7@.-compute :: Bytes.ByteString -> Word.Word32-compute = Bits.complement . Bytes.foldl' update initial+compute :: ByteString.ByteString -> Word.Word32+compute = Bits.complement . ByteString.foldl' update initial  update :: Word.Word32 -> Word.Word8 -> Word.Word32 update crc byte =
src/lib/Rattletrap/Utility/Helper.hs view
@@ -1,42 +1,32 @@ -- | This module provides helper functions for converting replays to and from -- both their binary format and JSON.-module Rattletrap.Utility.Helper-  ( decodeReplayFile-  , encodeReplayJson-  , decodeReplayJson-  , encodeReplayFile-  ) where+module Rattletrap.Utility.Helper where  import qualified Rattletrap.ByteGet as ByteGet import qualified Rattletrap.BytePut as BytePut import qualified Rattletrap.Type.Content as Content import qualified Rattletrap.Type.Replay as Replay import qualified Rattletrap.Type.Section as Section+import qualified Rattletrap.Utility.Json as Json -import qualified Data.Aeson as Aeson-import qualified Data.Aeson.Encode.Pretty as Aeson-import qualified Data.ByteString as Bytes-import qualified Data.ByteString.Lazy as LazyBytes+import qualified Data.ByteString as ByteString+import qualified Data.ByteString.Lazy as LazyByteString  -- | Parses a raw replay. decodeReplayFile-  :: Bool -> Bool -> Bytes.ByteString -> Either String Replay.Replay+  :: Bool -> Bool -> ByteString.ByteString -> Either String Replay.Replay decodeReplayFile fast = ByteGet.run . Replay.byteGet fast  -- | Encodes a replay as JSON.-encodeReplayJson :: Replay.Replay -> LazyBytes.ByteString-encodeReplayJson = Aeson.encodePretty' Aeson.defConfig-  { Aeson.confCompare = compare-  , Aeson.confIndent = Aeson.Tab-  , Aeson.confTrailingNewline = True-  }+encodeReplayJson :: Replay.Replay -> LazyByteString.ByteString+encodeReplayJson = Json.encodePretty  -- | Parses a JSON replay.-decodeReplayJson :: Bytes.ByteString -> Either String Replay.Replay-decodeReplayJson = Aeson.eitherDecodeStrict'+decodeReplayJson :: ByteString.ByteString -> Either String Replay.Replay+decodeReplayJson = Json.decode  -- | Encodes a raw replay.-encodeReplayFile :: Bool -> Replay.Replay -> LazyBytes.ByteString+encodeReplayFile :: Bool -> Replay.Replay -> LazyByteString.ByteString encodeReplayFile fast replay =   BytePut.toLazyByteString . Replay.bytePut $ if fast     then replay
src/lib/Rattletrap/Utility/Json.hs view
@@ -3,13 +3,17 @@   , Aeson.FromJSON(parseJSON)   , Aeson.ToJSON(toJSON)   , Aeson.Value+  , Aeson.encode   , Aeson.object   , Aeson.withObject   , Aeson.withText   ) where  import qualified Data.Aeson as Aeson+import qualified Data.Aeson.Encode.Pretty as Aeson import qualified Data.Aeson.Types as Aeson+import qualified Data.ByteString as ByteString+import qualified Data.ByteString.Lazy as LazyByteString import qualified Data.Text as Text  required@@ -25,3 +29,13 @@  pair :: (Aeson.ToJSON value, Aeson.KeyValue pair) => String -> value -> pair pair key value = Text.pack key Aeson..= value++decode :: Aeson.FromJSON a => ByteString.ByteString -> Either String a+decode = Aeson.eitherDecodeStrict'++encodePretty :: Aeson.ToJSON a => a -> LazyByteString.ByteString+encodePretty = Aeson.encodePretty' Aeson.defConfig+  { Aeson.confCompare = compare+  , Aeson.confIndent = Aeson.Tab+  , Aeson.confTrailingNewline = True+  }
src/lib/Rattletrap/Utility/Monad.hs view
@@ -1,4 +1,4 @@ module Rattletrap.Utility.Monad where  whenMaybe :: Applicative m => Bool -> m a -> m (Maybe a)-whenMaybe p f = if p then Just <$> f else pure Nothing+whenMaybe p f = if p then fmap Just f else pure Nothing
src/test/Main.hs view
@@ -1,13 +1,9 @@-module Main-  ( main-  ) where- import qualified Control.Monad as Monad-import qualified Data.ByteString as Bytes+import qualified Data.ByteString as ByteString import qualified GHC.Clock as Clock import qualified Rattletrap import qualified System.Exit as Exit-import qualified System.FilePath as Path+import qualified System.FilePath as FilePath import qualified Test.HUnit as Test import qualified Text.Printf as Printf @@ -18,7 +14,7 @@ runTests test = do   Rattletrap.rattletrap     ""-    ["--schema", "--output", Path.combine directory "schema.json"]+    ["--schema", "--output", FilePath.combine directory "schema.json"]   (result, elapsed) <- withElapsed $ Test.runTestTT test   Printf.printf "Total time: %.3f seconds\n" elapsed   Monad.when@@ -38,16 +34,15 @@ toAssertion :: String -> Test.Assertion toAssertion uuid = do   let-    inputFile = Path.combine "replays" $ uuid <> ".replay"-    jsonFile = Path.combine directory $ uuid <> ".json"-    outputFile = Path.combine directory $ uuid <> ".replay"-  input <- Bytes.readFile inputFile+    inputFile = FilePath.combine "replays" $ uuid <> ".replay"+    jsonFile = FilePath.combine directory $ uuid <> ".json"+    outputFile = FilePath.combine directory $ uuid <> ".replay"+  input <- ByteString.readFile inputFile   decode inputFile jsonFile   encode jsonFile outputFile-  output <- Bytes.readFile outputFile-  Monad.unless-    (output == input)-    (Test.assertFailure "output does not match input")+  output <- ByteString.readFile outputFile+  Monad.when (output /= input)+    $ Test.assertFailure "output does not match input"  decode :: FilePath -> FilePath -> IO () decode input output =