module Game.LambdaHack.Action.Save
( saveGameFile, restoreGame, rmBkpSaveDiary, saveGameBkp
) where
import System.Directory
import System.FilePath
import qualified Control.Exception as Ex hiding (handle)
import Control.Monad
import Control.Concurrent
import System.IO.Unsafe (unsafePerformIO)
import Data.Text (Text)
import qualified Data.Text as T
import Game.LambdaHack.Utils.File
import Game.LambdaHack.State
import Game.LambdaHack.Msg
import Game.LambdaHack.Config
saveDiary :: FilePath -> Diary -> IO ()
saveDiary configDiaryFile diary = encodeEOF configDiaryFile diary
saveLock :: MVar ()
saveLock = unsafePerformIO newEmptyMVar
saveGameFile :: ConfigUI -> State -> IO ()
saveGameFile ConfigUI{configSaveFile} state = do
putMVar saveLock ()
encodeEOF configSaveFile state
takeMVar saveLock
tryCreateDir :: FilePath -> IO ()
tryCreateDir dir =
Ex.catch
(createDirectory dir)
(\ e -> case e :: Ex.IOException of _ -> return ())
tryCopyDataFiles :: ConfigUI -> (FilePath -> IO FilePath) -> IO ()
tryCopyDataFiles ConfigUI{ configScoresFile
, configRulesCfgFile
, configUICfgFile } pathsDataFile = do
rulesFile <- pathsDataFile $ takeFileName configRulesCfgFile <.> ".default"
uiFile <- pathsDataFile $ takeFileName configUICfgFile <.> ".default"
scoresFile <- pathsDataFile $ takeFileName configScoresFile
let newRulesFile = configRulesCfgFile <.> ".ini"
newUIFile = configUICfgFile <.> ".ini"
newScoresFile = configScoresFile
Ex.catch
(copyFile rulesFile newRulesFile >>
copyFile uiFile newUIFile >>
copyFile scoresFile newScoresFile)
(\ e -> case e :: Ex.IOException of _ -> return ())
restoreGame :: ConfigUI -> (FilePath -> IO FilePath) -> Text
-> IO (Either (State, Diary, Msg) (Diary, Msg))
restoreGame config@ConfigUI{ configAppDataDir
, configDiaryFile
, configSaveFile
, configBkpFile } pathsDataFile title = do
ab <- doesDirectoryExist configAppDataDir
unless ab $ do
tryCreateDir configAppDataDir
tryCopyDataFiles config pathsDataFile
diary <-
do db <- doesFileExist configDiaryFile
if db
then strictDecodeEOF configDiaryFile
else defaultDiary
sb <- doesFileExist configSaveFile
bb <- doesFileExist configBkpFile
Ex.catch
(if sb
then do
renameFile configSaveFile configBkpFile
state <- strictDecodeEOF configBkpFile
let msg = "Welcome back to" <+> title <> "."
return $ Left (state, diary, msg)
else
if bb
then do
state <- strictDecodeEOF configBkpFile
let msg = "No savefile found. Restoring from a backup savefile."
return $ Left (state, diary, msg)
else return $ Right (diary, "Welcome to" <+> title <> "!"))
(\ e -> case e :: Ex.SomeException of
_ -> let msg = "Starting a new game, because restore failed."
<+> "The error message was:"
<+> (T.unwords . T.lines) (showT e)
in return $ Right (diary, msg))
saveGameBkp :: ConfigUI -> State -> Diary -> IO ()
saveGameBkp ConfigUI{ configDiaryFile
, configSaveFile
, configBkpFile } state diary = do
b <- tryPutMVar saveLock ()
when b $
void $ forkIO $ do
saveDiary configDiaryFile diary
encodeEOF configSaveFile state
renameFile configSaveFile configBkpFile
takeMVar saveLock
rmBkpSaveDiary :: ConfigUI -> Diary -> IO ()
rmBkpSaveDiary ConfigUI{ configDiaryFile
, configBkpFile } diary = do
putMVar saveLock ()
saveDiary configDiaryFile diary
bb <- doesFileExist configBkpFile
when bb $ removeFile configBkpFile
takeMVar saveLock