rattletrap-10.0.0: src/lib/Rattletrap/Type/RemoteId.hs
module Rattletrap.Type.RemoteId 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.Type.U64 as U64
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
-- ^ Really only 24 bits.
| Steam U64.U64
| Switch U64.U64 U64.U64 U64.U64 U64.U64
| Xbox U64.U64
| Epic Str.Str
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"
]
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)]
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)]
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)
]
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
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
_ -> 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)