{-# LANGUAGE CPP #-}
{-# LANGUAGE DeriveDataTypeable #-}
module Data.DTA.Base
( DTA(..), Tree(..), Chunk(..)
, renumberFrom
) where
#if __GLASGOW_HASKELL__ < 710
import Control.Applicative ((<$>))
#endif
import Control.Applicative (liftA2)
import Control.Monad (replicateM)
import qualified Data.ByteString as B
import Data.Data (Data)
import Data.Int (Int32)
import Data.Typeable (Typeable)
import Data.Word (Word32, Word8)
import qualified Control.Monad.Trans.State as S
import Data.Binary (Binary (..), Get, Put)
import Data.Binary.Get (getByteString, getWord16le,
getWord32le, skip)
import Data.Binary.IEEE754 (getFloat32le, putFloat32le)
import Data.Binary.Put (putByteString, putWord16le,
putWord32le)
data DTA = DTA { byteZero :: Word8, topTree :: Tree }
deriving (Eq, Ord, Show, Read, Typeable, Data)
data Tree = Tree { nodeID :: Word32, treeChunks :: [Chunk] }
deriving (Eq, Ord, Show, Read, Typeable, Data)
data Chunk
= Int Int32
| Float Float
| Var B.ByteString
| Key B.ByteString
| Unhandled
| IfDef B.ByteString
| Else
| EndIf
| Parens Tree
| Braces Tree
| String B.ByteString
| Brackets Tree
| Define B.ByteString
| Include B.ByteString
| Merge B.ByteString
| IfNDef B.ByteString
deriving (Eq, Ord, Show, Read, Typeable, Data)
instance Binary DTA where
put (DTA b t) = put b >> put t
get = liftA2 DTA get get
instance Binary Tree where
put (Tree nid chks) = do
putWord16le $ fromIntegral $ length chks
putWord32le nid
mapM_ put chks
get = do
len <- getWord16le
liftA2 Tree getWord32le $ replicateM (fromIntegral len) get
instance Binary Chunk where
put c = case c of
Int i -> putWord32le 0x0 >> putWord32le (fromIntegral i)
Float f -> putWord32le 0x1 >> putFloat32le f
Var b -> putWord32le 0x2 >> putLenStr b
Key b -> putWord32le 0x5 >> putLenStr b
Unhandled -> putWord32le 0x6 >> putWord32le 0
IfDef b -> putWord32le 0x7 >> putLenStr b
Else -> putWord32le 0x8 >> putWord32le 0
EndIf -> putWord32le 0x9 >> putWord32le 0
Parens tr -> putWord32le 0x10 >> put tr
Braces tr -> putWord32le 0x11 >> put tr
String b -> putWord32le 0x12 >> putLenStr b
Brackets tr -> putWord32le 0x13 >> put tr
Define b -> putWord32le 0x20 >> putLenStr b
Include b -> putWord32le 0x21 >> putLenStr b
Merge b -> putWord32le 0x22 >> putLenStr b
IfNDef b -> putWord32le 0x23 >> putLenStr b
get = getWord32le >>= \cid -> case cid of
0x0 -> Int . fromIntegral <$> getWord32le
0x1 -> Float <$> getFloat32le
0x2 -> Var <$> getLenStr
0x5 -> Key <$> getLenStr
0x6 -> skip 4 >> return Unhandled
0x7 -> IfDef <$> getLenStr
0x8 -> skip 4 >> return Else
0x9 -> skip 4 >> return EndIf
0x10 -> Parens <$> get
0x11 -> Braces <$> get
0x12 -> String <$> getLenStr
0x13 -> Brackets <$> get
0x20 -> Define <$> getLenStr
0x21 -> Include <$> getLenStr
0x22 -> Merge <$> getLenStr
0x23 -> IfNDef <$> getLenStr
_ -> fail $ "Unidentified DTB chunk with ID " ++ show cid
putLenStr :: B.ByteString -> Put
putLenStr b = putWord32le (fromIntegral $ B.length b) >> putByteString b
getLenStr :: Get B.ByteString
getLenStr = getWord32le >>= getByteString . fromIntegral
renumberFrom :: Word32 -> DTA -> DTA
renumberFrom w (DTA b t) = DTA b $ S.evalState (renumberTree t) w where
renumberTree :: Tree -> S.State Word32 Tree
renumberTree (Tree _ sub) = liftA2 Tree S.get $
S.modify (+ 1) >> mapM renumberChunk sub
renumberChunk :: Chunk -> S.State Word32 Chunk
renumberChunk c = case c of
Parens tr -> Parens <$> renumberTree tr
Braces tr -> Braces <$> renumberTree tr
Brackets tr -> Brackets <$> renumberTree tr
_ -> return c