module Data.Vhd.Serialize where import Control.Applicative import Control.Monad import Data.Bits import Data.Word import Data.Byteable import qualified Data.ByteString as B import Data.Serialize import qualified Data.Text as T import Data.Text.Encoding import Data.Vhd.Types import Data.Vhd.UniqueId import Data.Vhd.Time getCookie :: Get Cookie getCookie = cookie <$> getByteString 8 putCookie :: Cookie -> Put putCookie (Cookie c) = putByteString c getBlockSize :: Get BlockSize getBlockSize = BlockSize <$> getWord32be putBlockSize :: BlockSize -> Put putBlockSize (BlockSize sz) = putWord32be sz getCurrentSize = getWord64be putCurrentSize = putWord64be getDataOffset = getWord64be putDataOffset = putWord64be getMaxTableEntries = getWord32be putMaxTableEntries = putWord32be getOriginalSize = getWord64be putOriginalSize = putWord64be getTableOffset :: Get Word64 getTableOffset = getWord64be putTableOffset :: Word64 -> Put putTableOffset = putWord64be getParentTimeStamp :: Get VhdDiffTime getParentTimeStamp = getTimeStamp putParentTimeStamp :: VhdDiffTime -> Put putParentTimeStamp = putTimeStamp getTimeStamp :: Get VhdDiffTime getTimeStamp = VhdDiffTime <$> getWord32be putTimeStamp :: VhdDiffTime -> Put putTimeStamp (VhdDiffTime ts) = putWord32be ts getCreatorApplication :: Get CreatorApplication getCreatorApplication = creatorApplication <$> getByteString 4 putCreatorApplication :: CreatorApplication -> Put putCreatorApplication (CreatorApplication c) = putByteString c getCreatorHostOs :: Get CreatorHostOs getCreatorHostOs = convert <$> getWord32be where convert 0x4D616320 = CreatorHostOsMacintosh convert 0x5769326B = CreatorHostOsWindows convert _ = CreatorHostOsUnknown putCreatorHostOs :: CreatorHostOs -> Put putCreatorHostOs v = putWord32be $ case v of CreatorHostOsMacintosh -> 0x4D616320 CreatorHostOsWindows -> 0x5769326B CreatorHostOsUnknown -> 0 getDiskType :: Get DiskType getDiskType = convert <$> getWord32be where convert 2 = DiskTypeFixed convert 3 = DiskTypeDynamic convert 4 = DiskTypeDifferencing convert _ = error "invalid disk type" putDiskType :: DiskType -> Put putDiskType v = putWord32be $ case v of DiskTypeFixed -> 2 DiskTypeDynamic -> 3 DiskTypeDifferencing -> 4 getDiskGeometry :: Get DiskGeometry getDiskGeometry = DiskGeometry <$> getWord16be <*> getWord8 <*> getWord8 putDiskGeometry :: DiskGeometry -> Put putDiskGeometry (DiskGeometry c h s) = putWord16be c >> putWord8 h >> putWord8 s getIsTemporaryDisk :: Get Bool getIsTemporaryDisk = (\n -> n .&. 1 == 1) <$> getWord32be putIsTemporaryDisk :: Bool -> Put putIsTemporaryDisk i = putWord32be ((if i then 1 else 0) .|. 0x2) getIsSavedState :: Get Bool getIsSavedState = (== 1) <$> getWord8 putIsSavedState :: Bool -> Put putIsSavedState i = putWord8 (if i then 1 else 0) getUniqueId :: Get UniqueId getUniqueId = uniqueId <$> getByteString 16 putUniqueId :: UniqueId -> Put putUniqueId uid = putByteString $ toBytes uid getParentUniqueId :: Get UniqueId getParentUniqueId = getUniqueId putParentUniqueId :: UniqueId -> Put putParentUniqueId = putUniqueId getVersion :: Get Version getVersion = Version <$> getWord16be <*> getWord16be putVersion :: Version -> Put putVersion (Version major minor) = putWord16be major >> putWord16be minor getCreatorVersion :: Get Version getCreatorVersion = getVersion putCreatorVersion :: Version -> Put putCreatorVersion = putVersion getFormatVersion :: Get Version getFormatVersion = getVersion putFormatVersion :: Version -> Put putFormatVersion = putVersion getParentUnicodeName = parentUnicodeName . demarshall <$> getByteString 512 where demarshall = takeWhile ((/=) '\0') . T.unpack . decodeUtf16BE putParentUnicodeName (ParentUnicodeName c) | blen > 512 = error "parent unicode name length is greater than 512" | otherwise = putByteString b >> putByteString (B.replicate (512 - blen) 0) where b = encodeUtf16BE $ T.pack c blen = B.length b instance Serialize ParentLocatorEntry where get = ParentLocatorEntry <$> getWord32be <*> getWord32be <*> getWord32be <*> (getWord32be *> getWord64be) put ent = mapM_ putWord32be [locatorCode ent,locatorDataSpace ent,locatorDataLength ent,0] >> putWord64be (locatorDataOffset ent) instance Serialize ParentLocatorEntries where get = ParentLocatorEntries <$> replicateM 8 get put (ParentLocatorEntries es) = mapM_ put es