diff --git a/TheForceEngine/TFE_FileSystem/filestream.cpp b/TheForceEngine/TFE_FileSystem/filestream.cpp index 3d4464197..2fbc871ff 100644 --- a/TheForceEngine/TFE_FileSystem/filestream.cpp +++ b/TheForceEngine/TFE_FileSystem/filestream.cpp @@ -30,6 +30,14 @@ bool FileStream::exists(const char* filename) return res; } +bool FileStream::renameFile(const char* oldName, const char* newName) +{ + remove(newName); + int status = rename(oldName, newName); + if (status < 0) { return false; } + return true; +} + bool FileStream::open(const char* filename, AccessMode mode) { const char* modeStrings[] = { "rb", "wb", "rb+" }; diff --git a/TheForceEngine/TFE_FileSystem/filestream.h b/TheForceEngine/TFE_FileSystem/filestream.h index dfc988987..b937d8cfe 100644 --- a/TheForceEngine/TFE_FileSystem/filestream.h +++ b/TheForceEngine/TFE_FileSystem/filestream.h @@ -16,6 +16,7 @@ class FileStream : public Stream ~FileStream(); bool exists(const char* filename); + bool renameFile(const char* oldName, const char* newName); bool open(const char* filename, AccessMode mode); bool open(const FilePath* filePath, AccessMode mode); void close(); diff --git a/TheForceEngine/TFE_Game/saveSystem.cpp b/TheForceEngine/TFE_Game/saveSystem.cpp index 2f508b627..8aa460ad9 100644 --- a/TheForceEngine/TFE_Game/saveSystem.cpp +++ b/TheForceEngine/TFE_Game/saveSystem.cpp @@ -187,17 +187,27 @@ namespace TFE_SaveSystem bool saveGame(const char* filename, const char* saveName) { + // We first write the new save to a temporary file, then rename the temporary file. + // This way, if we are saving into an existing slot and something goes wrong during + // the save process, we won't overwrite a valid save with a corrupted save. + + char tempFilePath[TFE_MAX_PATH]; char filePath[TFE_MAX_PATH]; + string tempFilename = string(filename) + ".tmp"; + sprintf(tempFilePath, "%s%s", s_gameSavePath, tempFilename.c_str()); sprintf(filePath, "%s%s", s_gameSavePath, filename); bool ret = false; FileStream stream; - if (stream.open(filePath, Stream::MODE_WRITE)) + if (stream.open(tempFilePath, Stream::MODE_WRITE)) { saveHeader(&stream, saveName); ret = s_game->serializeGameState(&stream, filename, true); stream.close(); } + + stream.renameFile(tempFilePath, filePath); + return ret; }