module Main where
import Options.Applicative
import DarkPlaces.DemoMetadata
import DarkPlaces.Text
import qualified Data.ByteString.Lazy as BL
import qualified Data.ByteString.Lazy.Char8 as BLC
import System.Directory (doesFileExist)
import System.Exit
import Control.Monad.Error
import Text.Printf
import Data.Either
import Data.Fixed (mod')
data CommandArgs = CommandArgs {
onlyMapname :: Bool,
filename :: String
} deriving(Show, Eq)
data ResourceDownload = ResourceDownload {
as :: String,
for :: String,
url :: String
} deriving (Show, Eq)
data FileMetadata = FileMetadata {
mapName :: String,
demoTime :: Float,
downloads :: [ResourceDownload]
} deriving (Show, Eq)
type MaybeError = ErrorT String IO
argsParser :: Parser CommandArgs
argsParser = CommandArgs
<$> switch
( long "map"
<> short 'm'
<> help "Print only map name")
<*> argument str (
metavar "FILE"
<> help "Input demo file")
printMessages :: MetadataList -> IO ()
printMessages metadata = putStrLn "Messages:" >> (mapM_ print $ filter pred metadata)
where
pred (DemoMessage _ _) = True
pred _ = False
print (DemoMessage _ m) = putStrUtf $ parseDPText m
print _ = return ()
openFile :: CommandArgs -> MaybeError BL.ByteString
openFile args = do
let file = filename args
exist <- liftIO $ doesFileExist file
if exist
then liftIO $ BL.readFile file
else throwError $ printf "File \"%s\" does not exists\n" file
formatTime :: Float -> String
formatTime d
| d <= 60 = s_repr
| d <= (60 * 60) = printf "%s %s" m_repr s_repr
| otherwise = printf "%s %s %s" h_repr m_repr s_repr
where
s = d `mod'` 60 :: Float
m = (truncate $ d / 60) `rem` 60 :: Int
h = (truncate $ d / (60 * 60)) `rem` 24 :: Int
s_repr = choose "second" "seconds" s
m_repr = choose "minute" "minutes" m
h_repr = choose "hour" "hours" h
choose s1 s2 v
| v == 1 = show v ++ " " ++ s1
| otherwise = show v ++ " " ++ s2
formatMetadata :: MetadataList -> MaybeError ()
formatMetadata metadata = do
let meta = foldMetadata metadata initState
liftIO $ putStrLn $ printf "Map: %s" $ mapName meta
liftIO $ putStrLn $ printf "Time: %s" $ formatTime $ demoTime meta
liftIO $ printDownloads meta
liftIO $ printMessages metadata
return ()
where
initState = FileMetadata "" 0 []
foldMetadata ((MapName m):xs) ms = foldMetadata xs $ ms {mapName=m}
foldMetadata ((DemoTime t):xs) ms = foldMetadata xs $ ms {demoTime=t}
foldMetadata ((CurlDownload as for url):xs) ms = foldMetadata xs ms'
where
ms' = ms {downloads=(ResourceDownload (BLC.unpack as) (BLC.unpack for) (BLC.unpack url)): downloads ms}
foldMetadata (_:xs) ms = foldMetadata xs ms
foldMetadata [] ms = ms {downloads= reverse $ downloads ms}
printDownloads meta = mapM_ printDownload $ downloads meta
where
printDownload d = putStrLn $ printf "Download: %s -- as %s" (url d) (as d)
processDemo :: CommandArgs -> MaybeError ()
processDemo args = do
file_data <- openFile args
if onlyMapname $ args
then do
let maybe_map = getMapname file_data
case maybe_map of
(Right (Just m)) -> liftIO $ putStrLn $ printf "Map: %s" m
(Right Nothing) -> liftIO $ putStrLn "No map information in demo file"
(Left _) -> throwError "Error during parsing file"
else do
let (errors, metadata) = partitionEithers $ getMetadata file_data
if null errors
then formatMetadata metadata
else throwError "Error during parsing file"
demoInfo :: CommandArgs -> IO ()
demoInfo args = do
r <- runErrorT $ processDemo args
case r of
Right _ -> exitSuccess
Left e -> putStrLn e >> exitFailure
main :: IO ()
main = demoInfo =<< execParser opts
where
opts = info (helper <*> argsParser)
(fullDesc
<> progDesc "Get demo file metada")