{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE StrictData #-} module Codec.GLB where import Prelude hiding (length) import Data.Binary (Binary(..), decodeFileOrFail) import Data.Binary.Get (ByteOffset, getWord32le, getByteString, isEmpty) import Data.Binary.Put (putByteString, putWord32le) import Data.ByteString (ByteString) import Data.Vector (Vector, unfoldrM) import Data.Word (Word32) import GHC.Generics (Generic) fromFile :: FilePath -> IO (Either (ByteOffset, String) GLB) fromFile = decodeFileOrFail data GLB = GLB { header :: Header , chunks :: Vector Chunk } deriving (Eq, Show, Generic) instance Binary GLB where get = GLB <$> get <*> unfoldrM getChunks () where getChunks () = do done <- isEmpty if done then pure Nothing else do chunk <- get pure $ Just (chunk, ()) put GLB{..} = do put header mapM_ put chunks data Header = Header { magic :: Word32 , version :: Word32 , length :: Word32 } deriving (Eq, Show, Generic) instance Binary Header where get = Header <$> getWord32le <*> getWord32le <*> getWord32le put Header{..} = do putWord32le magic putWord32le version putWord32le length data Chunk = Chunk { chunkLength :: Word32 , chunkType :: Word32 , chunkData :: ByteString } deriving (Eq, Show, Generic) instance Binary Chunk where get = do chunkLength <- getWord32le chunkType <- getWord32le chunkData <- getByteString (fromIntegral chunkLength) pure Chunk{..} put Chunk{..} = do putWord32le chunkLength putWord32le chunkType putByteString chunkData