packages feed

intricacy-0.8.0: BinaryInstances.hs

-- This file is part of Intricacy
-- Copyright (C) 2013 Martin Bays <mbays@sdf.org>
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of version 3 of the GNU General Public License as
-- published by the Free Software Foundation, or any later version.
--
-- You should have received a copy of the GNU General Public License
-- along with this program.  If not, see http://www.gnu.org/licenses/.

module BinaryInstances where
import           Control.Monad
import           Data.Binary
import           Data.Int       (Int8)
import qualified Data.Vector    as Vector

import           Frame
import           GameStateTypes
import           Hex
import           Physics

newtype SmallInt = SmallInt {fromSmallInt :: Int}
newtype SmallNat = SmallNat {fromSmallNat :: Int}
instance Binary SmallNat where
    put (SmallNat n) = if n < 255 then putWord8 (fromIntegral n) else putWord8 255 >> put n
    get = do
        n' <- get :: Get Word8
        if n' == 255 then SmallNat <$> get else return . SmallNat $ fromIntegral n'

instance Binary SmallInt where
    put (SmallInt n) = if abs n < 127 then put (fromIntegral n :: Int8) else put (127::Int8) >> put n
    get = do
        n' <- get :: Get Int8
        if n' == 127 then SmallInt <$> get else return . SmallInt $ fromIntegral n'

putPackedNat,putPackedInt :: Int -> Put
putPackedNat n = put $ SmallNat n
putPackedInt n = put $ SmallInt n
getPackedNat,getPackedInt :: Get Int
getPackedNat = fromSmallNat <$> get
getPackedInt = fromSmallInt <$> get

newtype ShortList a = ShortList {fromShortList :: [a]}
instance Binary a => Binary (ShortList a) where
    put (ShortList as) = putPackedNat (length as) >> mapM_ put as
    get = do
        n <- getPackedNat
        ShortList <$> getMany n

 -- | 'getMany n' get 'n' elements in order, without blowing the stack.
 -- [ copied from source of package 'binary' by Lennart Kolmodin ]
getMany :: Binary a => Int -> Get [a]
getMany = go []
 where
    go xs 0 = return $! reverse xs
    go xs i = do x <- get
                 -- we must seq x to avoid stack overflows due to laziness in
                 -- (>>=)
                 x `seq` go (x:xs) (i-1)
{-# INLINE getMany #-}

instance Binary HexVec where
    put (HexVec x y _) = putPackedInt x >> putPackedInt y
    get = do
        x <- getPackedInt
        y <- getPackedInt
        return $ tupxy2hv (x,y)
instance Binary g => Binary (PHS g) where
    put (PHS v) = put v
    get = PHS <$> get
instance Binary GameState where
    put (GameState pps conns) = put (ShortList $ Vector.toList pps) >> put (ShortList conns)
    get = liftM2 GameState (Vector.fromList . fromShortList <$> get) (fromShortList <$> get)
instance Binary PlacedPiece where
    put (PlacedPiece ppos p) = put ppos >> put p
    get = liftM2 PlacedPiece get get
instance Binary Piece where
    put (Block patt)         = put (0::Word8) >> put (ShortList patt)
    put (Pivot arms)         = put (1::Word8) >> put (ShortList arms)
    put (Hook arm stiffness) = put (2::Word8) >> put arm >> put stiffness
    put (Wrench mom)         = put (3::Word8) >> put mom
    put Ball                 = put (4::Word8)
    get = do
        tag <- get :: Get Word8
        case tag of
            0 -> Block . fromShortList <$> get
            1 -> Pivot . fromShortList <$> get
            2 -> liftM2 Hook get get
            3 -> Wrench <$> get
            4 -> return Ball
instance Binary Connection where
    put (Connection (ri,rp) (ei,ep) l) = putPackedInt ri >> put rp >> putPackedInt ei >> put ep >> put l
    get = do
        ri <- getPackedInt
        rp <- get
        ei <- getPackedInt
        ep <- get
        Connection (ri,rp) (ei,ep) <$> get
instance Binary Link where
    put (Free p)     = put (0::Word8) >> put p
    put (Spring d l) = put (1::Word8) >> put d >> putPackedInt l
    get = do
        tag <- get :: Get Word8
        case tag of
            0 -> Free <$> get
            1 -> liftM2 Spring get getPackedInt
instance Binary HookForce where
    put NullHF         = put (0::Word8)
    put (TorqueHF dir) = put (1::Word8) >> putPackedInt dir
    put (PushHF v)     = put (2::Word8) >> put v
    get = do
        tag <- get :: Get Word8
        case tag of
            0 -> return NullHF
            1 -> TorqueHF <$> getPackedInt
            2 -> PushHF <$> get
instance Binary Frame where
    put (BasicFrame s) = putPackedInt s
    get = BasicFrame <$> getPackedInt

instance Binary PlayerMove where
    put NullPM           = put (0::Word8)
    put (HookPush v)     = put (1::Word8) >> put v
    put (HookTorque dir) = put (2::Word8) >> putPackedInt dir
    put (WrenchPush v)   = put (3::Word8) >> put v
    get = do
        tag <- get :: Get Word8
        case tag of
            0 -> return NullPM
            1 -> HookPush <$> get
            2 -> HookTorque <$> getPackedInt
            3 -> WrenchPush <$> get