AutoSaver.cpp 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. /*
  2. * AutoSaver.cpp
  3. * -------------
  4. * Purpose: Class for automatically saving open modules at a specified interval.
  5. * Notes : (currently none)
  6. * Authors: OpenMPT Devs
  7. * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
  8. */
  9. #include "stdafx.h"
  10. #include "Mptrack.h"
  11. #include "Mainfrm.h"
  12. #include "Moddoc.h"
  13. #include "AutoSaver.h"
  14. #include "FileDialog.h"
  15. #include "FolderScanner.h"
  16. #include "resource.h"
  17. #include "../soundlib/mod_specifications.h"
  18. #include <algorithm>
  19. OPENMPT_NAMESPACE_BEGIN
  20. CAutoSaver::CAutoSaver()
  21. : m_lastSave(timeGetTime())
  22. {
  23. }
  24. bool CAutoSaver::IsEnabled() const
  25. {
  26. return TrackerSettings::Instance().AutosaveEnabled;
  27. }
  28. bool CAutoSaver::GetUseOriginalPath() const
  29. {
  30. return TrackerSettings::Instance().AutosaveUseOriginalPath;
  31. }
  32. mpt::PathString CAutoSaver::GetPath() const
  33. {
  34. return TrackerSettings::Instance().AutosavePath.GetDefaultDir();
  35. }
  36. uint32 CAutoSaver::GetHistoryDepth() const
  37. {
  38. return TrackerSettings::Instance().AutosaveHistoryDepth;
  39. }
  40. uint32 CAutoSaver::GetSaveInterval() const
  41. {
  42. return TrackerSettings::Instance().AutosaveIntervalMinutes;
  43. }
  44. bool CAutoSaver::DoSave(DWORD curTime)
  45. {
  46. bool success = true;
  47. //If time to save and not already having save in progress.
  48. if (CheckTimer(curTime) && !m_saveInProgress)
  49. {
  50. m_saveInProgress = true;
  51. theApp.BeginWaitCursor(); //display hour glass
  52. for(auto &modDoc : theApp.GetOpenDocuments())
  53. {
  54. if(modDoc->ModifiedSinceLastAutosave())
  55. {
  56. if(SaveSingleFile(*modDoc))
  57. {
  58. CleanUpBackups(*modDoc);
  59. } else
  60. {
  61. TrackerSettings::Instance().AutosaveEnabled = false;
  62. Reporting::Warning("Warning: Auto Save failed and has been disabled. Please:\n- Review your Auto Save paths\n- Check available disk space and filesystem access rights");
  63. success = false;
  64. }
  65. }
  66. }
  67. m_lastSave = timeGetTime();
  68. theApp.EndWaitCursor(); // End display hour glass
  69. m_saveInProgress = false;
  70. }
  71. return success;
  72. }
  73. bool CAutoSaver::CheckTimer(DWORD curTime) const
  74. {
  75. return (curTime - m_lastSave) >= GetSaveIntervalMilliseconds();
  76. }
  77. mpt::PathString CAutoSaver::GetBasePath(const CModDoc &modDoc, bool createPath) const
  78. {
  79. mpt::PathString path;
  80. if(GetUseOriginalPath())
  81. {
  82. if(modDoc.m_bHasValidPath && !(path = modDoc.GetPathNameMpt()).empty())
  83. {
  84. // File has a user-chosen path - remove filename
  85. path = path.GetPath();
  86. } else
  87. {
  88. // if it doesn't, put it in settings dir
  89. path = theApp.GetConfigPath() + P_("Autosave\\");
  90. if(createPath && !CreateDirectory(path.AsNative().c_str(), nullptr) && GetLastError() == ERROR_PATH_NOT_FOUND)
  91. path = theApp.GetConfigPath();
  92. else if(!createPath && !path.IsDirectory())
  93. path = theApp.GetConfigPath();
  94. }
  95. } else
  96. {
  97. path = GetPath();
  98. }
  99. return path.EnsureTrailingSlash();
  100. }
  101. mpt::PathString CAutoSaver::GetBaseName(const CModDoc &modDoc) const
  102. {
  103. return mpt::PathString::FromCString(modDoc.GetTitle()).SanitizeComponent();
  104. }
  105. mpt::PathString CAutoSaver::BuildFileName(const CModDoc &modDoc) const
  106. {
  107. mpt::PathString name = GetBasePath(modDoc, true) + GetBaseName(modDoc);
  108. const CString timeStamp = CTime::GetCurrentTime().Format(_T(".AutoSave.%Y%m%d.%H%M%S."));
  109. name += mpt::PathString::FromCString(timeStamp); //append backtup tag + timestamp
  110. name += mpt::PathString::FromUTF8(modDoc.GetSoundFile().GetModSpecifications().fileExtension);
  111. return name;
  112. }
  113. bool CAutoSaver::SaveSingleFile(CModDoc &modDoc)
  114. {
  115. // We do not call CModDoc::DoSave as this populates the Recent Files
  116. // list with backups... hence we have duplicated code.. :(
  117. CSoundFile &sndFile = modDoc.GetSoundFile();
  118. mpt::PathString fileName = BuildFileName(modDoc);
  119. // We are actually not going to show the log for autosaved files.
  120. ScopedLogCapturer logcapturer(modDoc, _T(""), nullptr, false);
  121. bool success = false;
  122. mpt::ofstream f(fileName, std::ios::binary);
  123. if(f)
  124. {
  125. switch(modDoc.GetSoundFile().GetBestSaveFormat())
  126. {
  127. case MOD_TYPE_MOD: success = sndFile.SaveMod(f); break;
  128. case MOD_TYPE_S3M: success = sndFile.SaveS3M(f); break;
  129. case MOD_TYPE_XM: success = sndFile.SaveXM(f); break;
  130. case MOD_TYPE_IT: success = sndFile.SaveIT(f, fileName); break;
  131. case MOD_TYPE_MPT: success = sndFile.SaveIT(f, fileName); break;
  132. }
  133. }
  134. return success;
  135. }
  136. void CAutoSaver::CleanUpBackups(const CModDoc &modDoc) const
  137. {
  138. // Find all autosave files for this document, and delete the oldest ones if there are more than the user wants.
  139. std::vector<mpt::PathString> foundfiles;
  140. FolderScanner scanner(GetBasePath(modDoc, false), FolderScanner::kOnlyFiles, GetBaseName(modDoc) + P_(".AutoSave.*"));
  141. mpt::PathString fileName;
  142. while(scanner.Next(fileName))
  143. {
  144. foundfiles.push_back(std::move(fileName));
  145. }
  146. std::sort(foundfiles.begin(), foundfiles.end());
  147. size_t filesToDelete = std::max(static_cast<size_t>(GetHistoryDepth()), foundfiles.size()) - GetHistoryDepth();
  148. for(size_t i = 0; i < filesToDelete; i++)
  149. {
  150. DeleteFile(foundfiles[i].AsNative().c_str());
  151. }
  152. }
  153. OPENMPT_NAMESPACE_END