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 +5/−3
- rattletrap.cabal +19/−4
- src/exe/Main.hs +3/−4
- src/lib/Rattletrap/BitBuilder.hs +33/−0
- src/lib/Rattletrap/BitGet.hs +24/−27
- src/lib/Rattletrap/BitPut.hs +10/−19
- src/lib/Rattletrap/BitString.hs +23/−0
- src/lib/Rattletrap/ByteGet.hs +5/−5
- src/lib/Rattletrap/Console/Config.hs +2/−2
- src/lib/Rattletrap/Console/Main.hs +25/−27
- src/lib/Rattletrap/Type/Attribute.hs +7/−7
- src/lib/Rattletrap/Type/Attribute/AppliedDamage.hs +6/−6
- src/lib/Rattletrap/Type/Attribute/Boolean.hs +3/−1
- src/lib/Rattletrap/Type/Attribute/Byte.hs +3/−1
- src/lib/Rattletrap/Type/Attribute/CamSettings.hs +20/−14
- src/lib/Rattletrap/Type/Attribute/ClubColors.hs +6/−2
- src/lib/Rattletrap/Type/Attribute/CustomDemolish.hs +9/−2
- src/lib/Rattletrap/Type/Attribute/DamageState.hs +15/−8
- src/lib/Rattletrap/Type/Attribute/Demolish.hs +15/−8
- src/lib/Rattletrap/Type/Attribute/Enum.hs +3/−1
- src/lib/Rattletrap/Type/Attribute/Explosion.hs +5/−2
- src/lib/Rattletrap/Type/Attribute/ExtendedExplosion.hs +4/−2
- src/lib/Rattletrap/Type/Attribute/FlaggedByte.hs +4/−1
- src/lib/Rattletrap/Type/Attribute/FlaggedInt.hs +4/−1
- src/lib/Rattletrap/Type/Attribute/Float.hs +3/−1
- src/lib/Rattletrap/Type/Attribute/GameMode.hs +5/−10
- src/lib/Rattletrap/Type/Attribute/Int.hs +3/−1
- src/lib/Rattletrap/Type/Attribute/Int64.hs +3/−1
- src/lib/Rattletrap/Type/Attribute/Loadout.hs +37/−19
- src/lib/Rattletrap/Type/Attribute/LoadoutOnline.hs +4/−3
- src/lib/Rattletrap/Type/Attribute/Loadouts.hs +4/−1
- src/lib/Rattletrap/Type/Attribute/LoadoutsOnline.hs +6/−6
- src/lib/Rattletrap/Type/Attribute/Location.hs +3/−1
- src/lib/Rattletrap/Type/Attribute/MusicStinger.hs +5/−1
- src/lib/Rattletrap/Type/Attribute/PartyLeader.hs +26/−14
- src/lib/Rattletrap/Type/Attribute/Pickup.hs +4/−2
- src/lib/Rattletrap/Type/Attribute/PickupNew.hs +4/−2
- src/lib/Rattletrap/Type/Attribute/PlayerHistoryKey.hs +3/−1
- src/lib/Rattletrap/Type/Attribute/PrivateMatchSettings.hs +15/−8
- src/lib/Rattletrap/Type/Attribute/ProductValue.hs +20/−25
- src/lib/Rattletrap/Type/Attribute/QWord.hs +3/−1
- src/lib/Rattletrap/Type/Attribute/Reservation.hs +12/−13
- src/lib/Rattletrap/Type/Attribute/RigidBodyState.hs +13/−7
- src/lib/Rattletrap/Type/Attribute/StatEvent.hs +4/−1
- src/lib/Rattletrap/Type/Attribute/String.hs +3/−1
- src/lib/Rattletrap/Type/Attribute/TeamPaint.hs +13/−7
- src/lib/Rattletrap/Type/Attribute/Title.hs +19/−10
- src/lib/Rattletrap/Type/Attribute/UniqueId.hs +4/−2
- src/lib/Rattletrap/Type/Attribute/WeldedInfo.hs +7/−7
- src/lib/Rattletrap/Type/AttributeMapping.hs +4/−1
- src/lib/Rattletrap/Type/AttributeValue.hs +74/−74
- src/lib/Rattletrap/Type/Cache.hs +6/−6
- src/lib/Rattletrap/Type/ClassAttributeMap.hs +4/−17
- src/lib/Rattletrap/Type/ClassMapping.hs +4/−1
- src/lib/Rattletrap/Type/CompressedWord.hs +3/−1
- src/lib/Rattletrap/Type/CompressedWordVector.hs +5/−5
- src/lib/Rattletrap/Type/Content.hs +53/−50
- src/lib/Rattletrap/Type/F32.hs +1/−1
- src/lib/Rattletrap/Type/Frame.hs +16/−9
- src/lib/Rattletrap/Type/Header.hs +20/−28
- src/lib/Rattletrap/Type/I32.hs +1/−1
- src/lib/Rattletrap/Type/I64.hs +1/−1
- src/lib/Rattletrap/Type/I8.hs +1/−1
- src/lib/Rattletrap/Type/Initialization.hs +5/−5
- src/lib/Rattletrap/Type/Int8Vector.hs +7/−4
- src/lib/Rattletrap/Type/KeyFrame.hs +0/−46
- src/lib/Rattletrap/Type/Keyframe.hs +50/−0
- src/lib/Rattletrap/Type/Mark.hs +4/−1
- src/lib/Rattletrap/Type/Message.hs +6/−5
- src/lib/Rattletrap/Type/Property.hs +4/−2
- src/lib/Rattletrap/Type/Property/Array.hs +35/−0
- src/lib/Rattletrap/Type/Property/Bool.hs +33/−0
- src/lib/Rattletrap/Type/Property/Byte.hs +37/−0
- src/lib/Rattletrap/Type/Property/Float.hs +33/−0
- src/lib/Rattletrap/Type/Property/Int.hs +33/−0
- src/lib/Rattletrap/Type/Property/Name.hs +32/−0
- src/lib/Rattletrap/Type/Property/QWord.hs +32/−0
- src/lib/Rattletrap/Type/Property/Str.hs +32/−0
- src/lib/Rattletrap/Type/PropertyValue.hs +52/−60
- src/lib/Rattletrap/Type/Quaternion.hs +7/−3
- src/lib/Rattletrap/Type/RemoteId.hs +44/−97
- src/lib/Rattletrap/Type/RemoteId/Epic.hs +32/−0
- src/lib/Rattletrap/Type/RemoteId/PlayStation.hs +53/−0
- src/lib/Rattletrap/Type/RemoteId/PsyNet.hs +50/−0
- src/lib/Rattletrap/Type/RemoteId/Splitscreen.hs +32/−0
- src/lib/Rattletrap/Type/RemoteId/Steam.hs +32/−0
- src/lib/Rattletrap/Type/RemoteId/Switch.hs +40/−0
- src/lib/Rattletrap/Type/RemoteId/Xbox.hs +32/−0
- src/lib/Rattletrap/Type/Replay.hs +17/−27
- src/lib/Rattletrap/Type/Replication.hs +11/−6
- src/lib/Rattletrap/Type/Replication/Spawned.hs +9/−7
- src/lib/Rattletrap/Type/Replication/Updated.hs +2/−3
- src/lib/Rattletrap/Type/ReplicationValue.hs +15/−9
- src/lib/Rattletrap/Type/Rotation.hs +5/−9
- src/lib/Rattletrap/Type/Section.hs +3/−3
- src/lib/Rattletrap/Type/Str.hs +9/−11
- src/lib/Rattletrap/Type/U32.hs +1/−1
- src/lib/Rattletrap/Type/U64.hs +1/−1
- src/lib/Rattletrap/Type/U8.hs +1/−1
- src/lib/Rattletrap/Type/Vector.hs +10/−10
- src/lib/Rattletrap/Type/Version.hs +28/−3
- src/lib/Rattletrap/Utility/Bytes.hs +8/−28
- src/lib/Rattletrap/Utility/Crc.hs +4/−6
- src/lib/Rattletrap/Utility/Helper.hs +10/−20
- src/lib/Rattletrap/Utility/Json.hs +14/−0
- src/lib/Rattletrap/Utility/Monad.hs +1/−1
- src/test/Main.hs +10/−15
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 =