ips/src/IPS/Parse.hs

51 lines
1.3 KiB
Haskell
Raw Normal View History

2021-02-24 10:27:30 -05:00
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
2021-05-07 08:13:54 -04:00
where cons c (!n, cs) = (n + 1, c : cs)
2021-02-24 10:27:30 -05:00
parseChunk :: Parser Chunk
parseChunk = do
offset <- parseWord24
2021-05-07 08:14:01 -04:00
size <- parseWord16
2021-02-24 10:27:30 -05:00
body <-
2021-05-07 08:14:01 -04:00
if size == 0 then
2021-02-24 10:27:30 -05:00
liftA2 RLE parseWord16 Parse.anyWord8
else
2021-05-07 08:14:01 -04:00
Normal . makeBytes <$> Parse.take (fromIntegral size)
2021-02-24 10:27:30 -05:00
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
2021-05-07 08:13:54 -04:00
go !acc 0 = pure acc
go !acc i = do
2021-02-24 10:27:30 -05:00
b <- fromIntegral <$> Parse.anyWord8
go ((acc `shiftL` 8) .|. b) (i - 1)