module Amiga where import qualified System.IO as IO import qualified Foreign.Marshal.Alloc as Alloc import Foreign.Storable (Storable, peekByteOff) import Foreign.Ptr (Ptr) import Control.Monad (when) import Control.Applicative ((<$>)) import qualified Data.List.Reverse.StrictSpine as ListRev import Data.List.HT (sliceVertical) import Data.Storable.Endian (HasBigEndian, getBigEndian) import Data.Word (Word8, Word16, Word32) import Data.Int (Int8, Int16, Int32) type MEM = Ptr () type PTR = Word32 type LONG = Int32 type ULONG = Word32 type WORD = Int16 type UWORD = Word16 type BYTE = Int8 type UBYTE = Word8 type Peek a = MEM -> PTR -> IO a peekOffset :: (Storable a) => Peek a peekOffset mem ptr = peekByteOff mem (fromIntegral ptr) peekBig :: (Storable a, HasBigEndian a) => Peek a peekBig mem ptr = getBigEndian <$> peekOffset mem ptr peekPTR :: Peek PTR ; peekPTR = peekBig peekLONG :: Peek LONG ; peekLONG = peekBig peekULONG :: Peek ULONG ; peekULONG = peekBig peekWORD :: Peek WORD ; peekWORD = peekBig peekUWORD :: Peek UWORD ; peekUWORD = peekBig peekBYTE :: Peek BYTE ; peekBYTE = peekOffset peekUBYTE :: Peek UBYTE ; peekUBYTE = peekOffset infixr 0 $? ($?) :: (PTR -> IO a) -> PTR -> IO (Maybe a) f $? ptr = skipIf (ptr == 0) (f ptr) skipIf :: Bool -> IO a -> IO (Maybe a) skipIf cond act = if cond then return Nothing else Just <$> act loadMEM :: String -> IO MEM loadMEM s = IO.withBinaryFile s IO.ReadMode $ \h -> do size <- fromInteger <$> IO.hFileSize h ptr <- Alloc.mallocBytes size readSize <- IO.hGetBuf h ptr size when (readSize IO () freeMEM = Alloc.free pointerRange :: PTR -> ULONG -> Int -> [PTR] pointerRange start step len = take len $ iterate (fromIntegral step +) start pointerRangeGen :: (Integral i) => PTR -> ULONG -> i -> [PTR] pointerRangeGen start step len = pointerRange start step (fromIntegral len) {- | Return empty list if start pointer is zero. -} pointerRangeGenCheck :: (Integral i) => PTR -> ULONG -> i -> [PTR] pointerRangeGenCheck start step len = if start == 0 then [] else pointerRangeGen start step len pointerRangeGen2 :: (Integral i, Integral j) => PTR -> ULONG -> i -> j -> [PTR] pointerRangeGen2 start step len0 len1 = pointerRange start step (fromIntegral len0 * fromIntegral len1) chunk :: (Integral i) => i -> [a] -> [[a]] chunk k = sliceVertical (fromIntegral k) -- | Strings tend to be fixed width fields with trailing zeros. stringFromBytes :: [UBYTE] -> String stringFromBytes = map (toEnum . fromEnum) . ListRev.dropWhile (==0)