packages feed

hfd-0.0.2: src/Proto.hs

module Proto
(
setBreakpoint,
deleteBreakpoint,
-- deleteBreakpoints,
setDebuggerOption,
execFinish,
execContinue,
execStep,
execNext,
getFrame,
getField,
getProp,
nextMsg
)
where

import Data.Maybe (isJust)
import Data.List (find)
import Data.Word (Word32)
import Data.ByteString (hPut)
import Control.Monad (liftM)
import Control.Monad.Trans.Class (lift)
import Control.Monad.Trans.State (get)
import Control.Monad.IO.Class(liftIO, MonadIO)
import System.IO (hFlush)

import App (App, AppState(..), Breakpoint(..), setBreakpoints, newBreakId)
import OMsg (OMsg(..), binOMsg)
import IMsg (IMsg(..), AMF(..), nextIMessage)

-- | Set breakpoint
setBreakpoint :: MonadIO m
  => Int  -- ^ File id
  -> Int  -- ^ Line number
  -> App m ()
setBreakpoint fl ln = do
  bs <- lift . lift $ liftM asBreaks get
  let exists = isJust $ find (\(_, b) -> (bpFileId b == fl) && (bpLine b == ln)) bs
  if exists
    then liftIO $ putStrLn "Breakpoint exists. Multiple breakpoints at the same line are not supported."
    else do
      sendMsg (OMsgSetBreakpoint (fromIntegral fl) (fromIntegral ln))
      msg <- nextMsg
      case msg of
        IMsgBreakpoints bs' -> do
          let exists' = isJust $ find (== (fromIntegral fl, fromIntegral ln)) bs'
          if exists'
            then do
              newId <- newBreakId
              setBreakpoints $ (newId, Breakpoint fl ln) : bs
            else
              liftIO $ putStrLn "Can't resolve breakpoint (the line is not executable?)"
        _ -> liftIO $ putStrLn "doSetBreakpoint: Unexpected message from player"


-- | Delete breakpoint
deleteBreakpoint :: MonadIO m => Int -> Int -> App m ()
deleteBreakpoint fl ln =
  sendMsg (OMsgClearBreakpoint (fromIntegral fl) (fromIntegral ln))

-- | Delete all breakpoints
-- Hmm... Doesn't work... Use `deleteBreakpoint` for now
{-deleteBreakpoints :: MonadIO m => App m ()
deleteBreakpoints =
  sendMsg OMsgClearBreakpoints-}

-- | Set debuger option
setDebuggerOption :: MonadIO m
  => String  -- ^ Option name
  -> String  -- ^ Option value
  -> App m ()
setDebuggerOption op val = do
  sendMsg (OMsgSetDebuggerOptions op val)
  msg <- nextMsg
  case msg of
    IMsgDebuggerOption _ _ -> return ()
    _ -> liftIO $ putStrLn "doSetDebuggerOption: Unexpected message from player"


-- | Send @finish@ command to player
execFinish :: MonadIO m => App m ()
execFinish =  sendMsg OMsgFinish

-- | Send @continue@ command to player
execContinue :: MonadIO m => App m ()
execContinue =  sendMsg OMsgContinue

-- | Send @step@ command to player
execStep :: MonadIO m => App m ()
execStep =  sendMsg OMsgStep

-- | Send @next@ command to player
execNext :: MonadIO m => App m ()
execNext =  sendMsg OMsgNext

-- | Get function frame
getFrame :: MonadIO m => App m [AMF]
getFrame = do
  sendMsg (OMsgGetFunctionFrame 0)
  msg <- nextMsg
  case msg of
    IMsgFunctionFrame _ _ _ vs -> return vs
    _ -> liftIO (putStrLn "doPrint: Unexpected message from player") >>
         return []

-- | Get field value
getField :: MonadIO m
  => Word32  -- ^ Object address
  -> String  -- ^ Field name
  -> App m [AMF]
getField ptr name = do
  sendMsg (OMsgGetField ptr name)
  msg <- nextMsg
  case msg of
    IMsgGetField _ vs -> return vs
    _ -> liftIO (putStrLn "Unexpected message from player") >>
         return []

-- | Get property by calling getter
getProp :: MonadIO m => Word32 -> String -> App m (Maybe AMF)
getProp ptr name = do
  sendMsg (OMsgGetField ptr name)
  msg <- nextMsg
  case msg of
    IMsgGetField v _ -> return $ Just v
    _ -> liftIO (putStrLn "Unexpected message from player") >>
          return Nothing

-- | Take next message
-- This function is just a wrapper around nextIMessage,
-- the only difference is that it responses to `IMsgProcessTag` message
-- and prints traces
nextMsg :: MonadIO m => App m IMsg
nextMsg = do
  msg <- nextIMessage
  -- liftIO $ print msg
  case msg of
    IMsgCreateAnonymObject _ -> nextMsg
    IMsgSetLocalVars _ -> nextMsg
    IMsgDeleteField _ _ -> nextMsg
    IMsgSetField _ _ _ -> nextMsg
    IMsgSetField2 _ _ _ -> nextMsg
    IMsgProcessTag -> sendMsg OMsgProcessTag >> nextMsg
    IMsgTrace str  -> liftIO (putStrLn $ " [trace] " ++ str) >> nextMsg
    _              -> return msg

-- | Send message to player
sendMsg :: MonadIO m => OMsg -> App m ()
sendMsg msg = do
  -- liftIO $ print msg
  h <- lift . lift $ liftM asHandle get
  liftIO $ hPut h (binOMsg msg) >> hFlush h
  return ()