51 lines
1.3 KiB
Haskell
51 lines
1.3 KiB
Haskell
module IPS.Parse (parser, read) where
|
|
|
|
import Prelude hiding (read)
|
|
import IPS.Types
|
|
import Data.Attoparsec.ByteString (Parser)
|
|
import qualified Data.Attoparsec.ByteString as Parse
|
|
import Data.Bits
|
|
import qualified Data.ByteString as ByteString
|
|
import Data.Functor
|
|
import qualified Data.Vector.Generic as Vector
|
|
import Control.Applicative
|
|
|
|
|
|
read :: FilePath -> IO (Either String Patch)
|
|
read f = Parse.parseOnly parser <$> ByteString.readFile f
|
|
|
|
|
|
parser :: Parser Patch
|
|
parser = Parse.string "PATCH" *> parseBody
|
|
|
|
parseBody :: Parser Patch
|
|
parseBody = uncurry Vector.fromListN <$> go where
|
|
go = eof <|> chunk
|
|
eof = Parse.string "EOF" $> (0, [])
|
|
chunk = liftA2 cons parseChunk go
|
|
where cons c (!n, cs) = (n + 1, c : cs)
|
|
|
|
parseChunk :: Parser Chunk
|
|
parseChunk = do
|
|
offset <- parseWord24
|
|
size <- parseWord16
|
|
body <-
|
|
if size == 0 then
|
|
liftA2 RLE parseWord16 Parse.anyWord8
|
|
else
|
|
Normal . makeBytes <$> Parse.take (fromIntegral size)
|
|
pure $ Chunk {offset, body}
|
|
|
|
parseWord16 :: Parser Word16
|
|
parseWord16 = parseWordBE 2
|
|
|
|
parseWord24 :: Parser Word24
|
|
parseWord24 = parseWordBE 3
|
|
|
|
parseWordBE :: (Integral a, Bits a) => Word -> Parser a
|
|
parseWordBE = go 0 where
|
|
go !acc 0 = pure acc
|
|
go !acc i = do
|
|
b <- fromIntegral <$> Parse.anyWord8
|
|
go ((acc `shiftL` 8) .|. b) (i - 1)
|