[Haskell-cafe] IFF reader and writer

Chris Kuklewicz haskell at list.mightyreason.com
Fri Dec 1 16:14:35 EST 2006


This parser was quick to write and works on AIFF files.  It does not do much
validation, and bits from 2 to 4 GB in length will cause errors.

module LoadIFF(IFF(..),parseIFF,IDType,FormType,ContentsType) where

import Data.List(unfoldr,span)
import Data.Bits((.|.),shiftL)
import Data.Word(Word32)
import Data.ByteString(ByteString)
import qualified Data.ByteString as B(take,drop,splitAt,length,unpack)

type IDType = String
type FormType = String
type ContentsType = String

data IFF = IFF_Form {len :: Word32
                    ,typeID :: FormType
                    ,parts :: [IFF]
                    }
         | IFF_List {len :: Word32
                    ,typeID :: ContentsType
                    ,props :: [IFF]
                    ,parts :: [IFF]
                    }
         | IFF_Cat  {len :: Word32
                    ,typeID :: ContentsType
                    ,parts :: [IFF]
                    }
         | IFF_Prop {len :: Word32
                    ,typeID :: FormType
                    ,parts :: [IFF]
                    }
         | IFF_Chunk {len :: Word32
                     ,typeID :: IDType
                     ,rawContent :: ByteString
                     }

instance Show IFF where
  show IFF_Form { typeID = name, len = size, parts = p } =
    "IFF_Form {typeID="++show name++",size="++show size++",parts="++show p++"}"
  show IFF_List { typeID = name, len = size, props = ps, parts = p } =
    "IFF_List {typeID="++show name++",size="++show size++",props"++show
ps++",parts="++show p++"}"
  show IFF_Cat { typeID = name, len = size, parts = p } =
    "IFF_Cat {typeID="++show name++",size="++show size++",parts="++show p++"}"
  show IFF_Prop { typeID = name, len = size, parts = p } =
    "IFF_Prop {typeID="++show name++",size="++show size++",parts="++show p++"}"
  show IFF_Chunk { typeID = name, len = size } = "IFF_Chunk {typeID="++show
name++",size="++show size++"}"

b2s = map (toEnum . fromEnum) . B.unpack

isProp IFF_Prop {} = True
isProp _ = False

parseIFF :: ByteString -> Maybe (IFF,ByteString)
parseIFF b | B.length b <=8 = Nothing
           | otherwise =
  let (bID,b') = B.splitAt 4 b
      (bLEN,b'') = B.splitAt 4 b'
      (bTypeID,content) = B.splitAt 4 b''
      [x1,x2,x3,x4] = map fromIntegral (B.unpack bLEN)
      iLEN = (shiftL x1 24) .|. (shiftL x2 16) .|. (shiftL x3 8) .|. x4
      toNext = (if odd iLEN then succ else id) (fromIntegral iLEN)
      rest = B.drop toNext b''
  in if iLEN > fromIntegral (B.length b'')
       then Nothing
       else let iff = case b2s bID of
                        "FORM" -> IFF_Form {len = iLEN
                                           ,typeID = b2s bTypeID
                                           ,parts = unfoldr parseIFF content}
                        "LIST" -> let (ps,cs) = span isProp (unfoldr parseIFF
content)
                                  in IFF_List {len = iLEN
                                              ,typeID = b2s bTypeID
                                              ,props = ps
                                              ,parts = cs}
                        "CAT " -> IFF_Cat {len = iLEN
                                          ,typeID = b2s bTypeID
                                          ,parts = unfoldr parseIFF content}
                        "Prop" -> IFF_Prop {len = iLEN
                                           ,typeID = b2s bTypeID
                                           ,parts = unfoldr parseIFF content}
                        chunkID -> IFF_Chunk {len = iLEN
                                             ,typeID = chunkID
                                             ,rawContent = content}
            in Just (iff,rest)


More information about the Haskell-Cafe mailing list