ips/src/IPS/Parse.hs

61 lines
1.5 KiB
Haskell
Raw Normal View History

2021-12-28 04:26:23 -05:00
module IPS.Parse (parser, parseFile) where
2021-02-24 10:27:30 -05:00
import IPS.Types
import Data.Attoparsec.ByteString (Parser)
import qualified Data.Attoparsec.ByteString as Parse
import Data.Bits
import qualified Data.ByteString as ByteString
2021-12-28 04:26:23 -05:00
import Data.Vector (Vector)
import qualified Data.Vector as Vector
2021-02-24 10:27:30 -05:00
import Control.Applicative
2021-12-28 04:26:23 -05:00
parseFile :: FilePath -> IO (Either String Patch)
parseFile f = Parse.parseOnly parser <$> ByteString.readFile f
2021-02-24 10:27:30 -05:00
parser :: Parser Patch
2021-12-28 04:26:23 -05:00
parser = Parse.string "PATCH" *> parseRest
parseRest :: Parser Patch
parseRest = make <$> go where
go = eof <|> chunk
eof = Rest 0 [] <$ Parse.string "EOF"
chunk = cons <$> parseChunk <*> go
data Rest = Rest {len :: {-# UNPACK #-} !Int, chunks :: [Chunk]}
cons :: Chunk -> Rest -> Rest
cons c (Rest {len, chunks}) = Rest {len = succ len, chunks = c : chunks}
make :: Rest -> Vector Chunk
make (Rest n cs) = Vector.fromListN n 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-12-28 04:26:23 -05:00
body <- parseBody size
2021-02-24 10:27:30 -05:00
pure $ Chunk {offset, body}
2021-12-28 04:26:23 -05:00
parseBody :: Word16 -> Parser ChunkBody
parseBody 0 = RLE <$> parseWord16 <*> Parse.anyWord8
parseBody n = Normal . makeBytes <$> Parse.take (fromIntegral n)
2021-02-24 10:27:30 -05:00
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)