module Data.Embed.Header where
import Control.Monad
import qualified Data.ByteString as BS
import Data.ByteString.UTF8
import Data.Hashable
import qualified Data.IntMap.Strict as M
import Data.Serialize
import Data.Word
type FileMap = M.IntMap [(FilePath, (Word64, Word32))]
bundleMagicNumber :: Word64
bundleMagicNumber = 0x424e444c4c444e42
bundleCurrentVersion :: Word8
bundleCurrentVersion = 0
bundleMaxFileSize :: Integer
bundleMaxFileSize = fromIntegral (maxBound :: Word32)
bundleHeaderStaticSize :: Integer
bundleHeaderStaticSize = 25
data StaticHeader = StaticHeader {
hdrDataOffset :: !Word64,
hdrNumFiles :: !Word32,
hdrDynSize :: !Word32,
hdrVersion :: !Word8,
hdrMagicNumber :: !Word64
}
data BundleHeader = BundleHeader {
hdrFiles :: !FileMap,
hdrStatic :: !StaticHeader
}
mkStaticHdr :: Int -> Int -> Word64 -> StaticHeader
mkStaticHdr nfiles dynsize dataoff = StaticHeader {
hdrVersion = bundleCurrentVersion,
hdrMagicNumber = bundleMagicNumber,
hdrNumFiles = fromIntegral nfiles,
hdrDynSize = fromIntegral dynsize,
hdrDataOffset = dataoff
}
instance Serialize StaticHeader where
put hdr = do
putWord64le (hdrDataOffset hdr)
putWord32le (hdrNumFiles hdr)
putWord32le (hdrDynSize hdr)
putWord8 (hdrVersion hdr)
putWord64le (hdrMagicNumber hdr)
get =
StaticHeader <$> getWord64le
<*> getWord32le
<*> getWord32le
<*> getWord8
<*> getWord64le
getHdrFile :: Get (FilePath, (Word64, Word32))
getHdrFile = do
pathlen <- getWord32le
path <- getBytes (fromIntegral pathlen)
off <- getWord64le
sz <- getWord32le
return (toString path, (off, sz))
getHdrFiles :: Word32 -> Get FileMap
getHdrFiles = getFiles M.empty
where
getFiles !m 0 = pure m
getFiles !m n = do
f <- getHdrFile
getFiles (M.alter (ins f) (hash $ fst f) m) (n1)
ins f (Just fs) = Just (f:fs)
ins f _ = Just [f]
putHdrFiles :: FileMap -> BS.ByteString
putHdrFiles m = runPut $ forM_ (concat $ M.elems m) $ \(p, (off, sz)) -> do
let p' = fromString p
putWord32le (fromIntegral $ BS.length p')
putByteString p'
putWord64le off
putWord32le sz