module ID3.Parser.General where
import Text.ParserCombinators.Poly.State
import ID3.Parser.UnSync
import qualified Data.ByteString.Lazy as BS
import qualified Data.ByteString.Lazy.Char8 as C
import Data.Word (Word8)
import Bits
import Data.Accessor
import Data.Encoding
import Data.Encoding.ISO88591
import Data.Encoding.UTF8
import Data.Encoding.UTF16
import Data.ByteString.Lazy.UTF8 (toString)
import Codec.Binary.UTF8.String as Codec
---,--------------------------------
type Token = Word8
data St = State { tagPos :: Integer
, curSize :: Integer
, curEncoding :: Integer
}
instance Show St where
show st = show (tagPos st
,curSize st
,curEncoding st)
initState = State 0 10 0
type TagParser = Parser St Token
run :: TagParser a -> [Word8] -> (Either String a, [Token])
run p cont = (result, rest) where (result, _, rest) = runParser p initState cont
---,--------------------------------
posGet :: TagParser Integer
posGet = stGet >>= return . tagPos
posUpdate :: (Integer -> Integer) -> TagParser ()
posUpdate f = stUpdate ( \st -> st {tagPos = f (tagPos st)} )
posSet :: Integer -> TagParser ()
posSet p = posUpdate (\_ -> p)
posDec :: TagParser ()
posDec = posUpdate (\p -> p1)
posInc :: TagParser ()
posInc = posUpdate (\p -> p+1)
---,--------------------------------
sizeGet :: TagParser Integer
sizeGet = stGet >>= return . curSize
sizeUpdate :: (Integer -> Integer) -> TagParser ()
sizeUpdate f = stUpdate ( \st -> st {curSize = f (curSize st)} )
sizeSet :: Integer -> TagParser ()
sizeSet s = sizeUpdate (\_ -> s)
sizeDec :: TagParser ()
sizeDec = sizeUpdate (\x -> x1)
sizeInc :: TagParser ()
sizeInc = sizeUpdate (\x -> x+1)
---,--------------------------------
encGet :: TagParser Integer
encGet = stGet >>= return . curEncoding
encUpdate :: (Integer -> Integer) -> TagParser ()
encUpdate f = stUpdate ( \st -> st {curEncoding = f (curEncoding st)} )
encSet :: Integer -> TagParser ()
encSet e = encUpdate (\_ -> e)
encRead :: TagParser Integer
encRead = anyWord8 >>= encSet . toInteger >> encGet
---,--------------------------------
ifSize :: TagParser [a] -> TagParser [a]
ifSize p = do
s <- sizeGet
if s > 0
then p
else return []
withSize p = do
x <- p
sizeDec
posInc
return x
---,--------------------------------
many' :: TagParser a -> TagParser [a]
many' p = many1' p `onFail` return []
many1' :: TagParser a -> TagParser [a]
many1' p = ifSize $ do
x <- p
xs <- many' p
return (x:xs)
---,--------------------------------
manyTill' :: TagParser a -> TagParser z -> TagParser [a]
manyTill' p end = manyTill1' p end `onFail` return []
manyTill1' :: TagParser a -> TagParser z -> TagParser [a]
manyTill1' p end = ifSize $ (end >> return []) `onFail`
(ifSize $ do
x <- p
xs <- manyTill' p end
return (x:xs))
skipTill p end = end `onFail` do {p; skipTill p end}
---,--------------------------------
sepBy' :: TagParser a -> TagParser sep -> TagParser [a]
sepBy' p sep = sepBy1' p sep `onFail` return []
sepBy1' :: TagParser a -> TagParser sep -> TagParser [a]
sepBy1' p sep= ifSize $ do
x <- p
xs <- many' (sep >> p)
return (x:xs)
---,--------------------------------
count :: (Num n) => n -> TagParser a -> TagParser [a]
count 0 _ = return []
count n p = do
x <- p
xs <- count (n1) p
return (x:xs)
count' :: (Num n) => n -> TagParser a -> TagParser [a]
count' 0 _ = return []
count' n p = ifSize $ do
x <- p
xs <- count' (n1) p
return (x:xs)
---,--------------------------------
countSepBy' :: (Num n) => n -> TagParser a -> TagParser sep -> TagParser [a]
countSepBy' 0 _ _ = return []
countSepBy' n p sep = ifSize $ do
x <- p
xs <- count' (n1) (sep >> p)
return (x:xs)
---,--------------------------------
terminator :: TagParser [Token]
terminator = do
enc <- encGet
case enc of
0x01 -> word8s [00,00]
0x02 -> word8s [00,00]
_ -> word8s [00]
encPack :: Integer -> [Token] -> String
encPack 0x00 = toString . recode' ISO88591 UTF8 . BS.pack . filter (/=00)
encPack 0x01 = toString . recode' UTF16 UTF8 . BS.pack . filter (/=00)
encPack 0x02 = toString . recode' UTF16 UTF8 . BS.pack . filter (/=00)
encPack _ = Codec.decode . filter (/=00)
recode' from to = Data.Encoding.encodeLazyByteString to . Data.Encoding.decodeLazyByteString from
---,--------------------------------
parseValues :: (Num n) => n -> TagParser [[Token]]
parseValues n = countSepBy' n (many' nonNull) terminator
parseValue :: TagParser [Token]
parseValue = nonNull `manyTill'` terminator
nonNull = withSize $ satisfy (/=0x00) `adjustErr` (++"\nWTF: nonNull")
parseString :: TagParser String
parseString = do
e <- encGet
v <- parseValue
return $ encPack e v
parseNumber :: TagParser Integer
parseNumber = parseValue >>= return . sum . (zipWith (*) (iterate (*10) 1)) . reverse . map toInteger
parseLanguage :: TagParser String
parseLanguage = do
lang <- count' 3 anyWord8
return $ encPack 0x03 lang
formValues :: [String] -> TagParser [(String, String)]
formValues keys = do
enc <- encGet
vals <- parseValues (length keys)
return $ zip keys $ map (encPack enc) vals
---,--------------------------------
parsers :: [TagParser a] -> TagParser [a]
parsers [] = return []
parsers (p:ps) = do
x <- p
xs <- parsers ps
return (x:xs)
---,--------------------------------
word8 :: Token -> TagParser Token
word8 w = (withSize $ satisfy (==w)) `err` (" \nWTF: word8 "++(show w))
word8s :: [Token] -> TagParser [Token]
word8s ws = parsers $ map word8 ws
byteString :: BS.ByteString -> TagParser BS.ByteString
byteString bs = (word8s $ BS.unpack bs) >> return bs
string :: String -> TagParser BS.ByteString
string = byteString . C.pack
upper :: TagParser Token
upper = satisfy (\x -> (0x41<=x)&&(x<=0x5a)) `err` ("\nWTF: upper")
digit :: TagParser Token
digit = satisfy (\x -> (0x30<=x)&&(x<=0x39)) `err` ("\nWTF: digit")
---,--------------------------------
anyWord8 :: TagParser Token
anyWord8 = withSize next `err` "anyWord8"
err p s = do
pos <- posGet
p `adjustErr` (++"\n"++"at "++(show pos)++": "++s)
---,--------------------------------
type Size = Integer
parseSize_ :: Integer -> TagParser Size
parseSize_ n = do
s <- count n next
let size = unSynchronise s
posUpdate (+n)
sizeSet size
return size
parseFlags_ :: [Int] -> TagParser [Bool]
parseFlags_ nums = do
flag <- anyWord8
sizeInc
return $ map (\i -> testBit flag (i1)) nums