packages feed

octane-0.4.17: library/Octane/Parser.hs

module Octane.Parser where

import qualified Control.Newtype as Newtype
import qualified Data.Binary.Bits.Get as Bits
import qualified Data.Binary.Get as Binary
import qualified Data.Bits as Bits
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as BSL
import Data.Function ((&))
import qualified Octane.Type as Type

parseFrames :: Type.Replay -> [Type.Frame]
parseFrames replay =
    Binary.runGet
        (replay & extractContext & getFrames & Bits.runBitGet)
        (replay & Type.replayStream & Newtype.unpack & BSL.fromStrict)

-- TODO: This will need at least the actors and cache items.
data Context = Context
    {
    }

extractContext :: Type.Replay -> Context
extractContext _replay =
    Context
    {
    }

getFrames :: Context -> Bits.BitGet [Type.Frame]
getFrames context = do
    maybeFrame <- getFrame context
    case maybeFrame of
        Nothing -> return []
        Just frame -> do
            frames <- getFrames context
            return (frame : frames)

getFrame :: Context -> Bits.BitGet (Maybe Type.Frame)
getFrame context = do
    -- TODO: Convert time bytes into a float.
    time <- Bits.getByteString 32
    -- TODO: Convert delta bytes into a float.
    delta <- Bits.getByteString 32
    if BS.all (== 0) time && BS.all (== 0) delta
        then return Nothing
        else do
            replications <- getReplications context
            let frame =
                    Type.Frame
                    { Type.frameTime = time
                    , Type.frameDelta = delta
                    , Type.frameReplications = replications
                    }
            return (Just frame)

getReplications :: Context -> Bits.BitGet [Type.Replication]
getReplications context = do
    (context',maybeReplication) <- getReplication context
    case maybeReplication of
        Nothing -> return []
        Just replication -> do
            replications <- getReplications context'
            return (replication : replications)

getReplication :: Context -> Bits.BitGet (Context, Maybe Type.Replication)
getReplication context = do
    hasReplication <- Bits.getBool
    if not hasReplication
        then return (context, Nothing)
        else do
            -- TODO: Convert actor ID into an integer.
            actorId <- Bits.getByteString (bitSize maxChannels)
            isOpen <- Bits.getBool
            if isOpen
                then do
                    isNew <- Bits.getBool
                    if isNew
                        then do
                            _unknownFlag <- Bits.getBool
                            _objectId <- getInt 32
                            -- TODO: Parse new actor.
                            return
                                ( context
                                , Just
                                      (Type.Replication
                                       { Type.replicationActorId = actorId
                                       , Type.replicationIsOpen = isOpen
                                       , Type.replicationIsNew = Just isNew
                                       }))
                        else do
                            let maybeClassId = getClassId context actorId
                            case maybeClassId of
                                Nothing -> fail "TODO: Could not get class ID."
                                Just _classId ->
                                    -- TODO: Parse existing actor.
                                    return
                                        ( context
                                        , Just
                                              (Type.Replication
                                               { Type.replicationActorId = actorId
                                               , Type.replicationIsOpen = isOpen
                                               , Type.replicationIsNew = Just isNew
                                               }))
                else return
                         ( context
                         , Just
                               (Type.Replication
                                { Type.replicationActorId = actorId
                                , Type.replicationIsOpen = isOpen
                                , Type.replicationIsNew = Nothing
                                }))

maxChannels
    :: (Integral a)
    => a
maxChannels = 1024

bitSize
    :: (Integral a)
    => a -> a
bitSize x = x & fromIntegral & logBase (2 :: Double) & ceiling

-- Reads an integer bitwise. The bits of the integer are backwards, so the
-- least significant bit is first. The argument is the maximum value this
-- integer can have. Bits will be read until the next bit would be greater than
-- the maximum value, or the number of bits necessary to reach the maximum
-- value has been reached, whichever comes first.
--
-- For example, if the maximum value is 4 and "11" has been read already,
-- nothing more will be read because another "1" would put the value over the
-- maximum.
getInt
    :: Int -> Bits.BitGet Int
getInt maxValue = do
    let maxBits = bitSize maxValue
        go i value = do
            let x = Bits.shiftL 1 i
            if i < maxBits && value + x <= maxValue
                then do
                    bit <- Bits.getBool
                    let newValue =
                            if bit
                                then value + x
                                else value
                    go (i + 1) newValue
                else return value
    go 0 0

-- TODO: Actually implement this.
getClassId :: Context -> BS.ByteString -> Maybe ()
getClassId _context _actorId = Nothing