aboutsummaryrefslogtreecommitdiff
path: root/Src/external_dependencies/openmpt-trunk/mptrack/AutoSaver.cpp
blob: c6eeac6d2710c7b06b1a1c37da32631e18c12fd4 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
/*
 * AutoSaver.cpp
 * -------------
 * Purpose: Class for automatically saving open modules at a specified interval.
 * Notes  : (currently none)
 * Authors: OpenMPT Devs
 * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
 */


#include "stdafx.h"
#include "Mptrack.h"
#include "Mainfrm.h"
#include "Moddoc.h"
#include "AutoSaver.h"
#include "FileDialog.h"
#include "FolderScanner.h"
#include "resource.h"
#include "../soundlib/mod_specifications.h"
#include <algorithm>


OPENMPT_NAMESPACE_BEGIN


CAutoSaver::CAutoSaver()
	: m_lastSave(timeGetTime())
{
}


bool CAutoSaver::IsEnabled() const
{
	return TrackerSettings::Instance().AutosaveEnabled;
}

bool CAutoSaver::GetUseOriginalPath() const
{
	return TrackerSettings::Instance().AutosaveUseOriginalPath;
}

mpt::PathString CAutoSaver::GetPath() const
{
	return TrackerSettings::Instance().AutosavePath.GetDefaultDir();
}

uint32 CAutoSaver::GetHistoryDepth() const
{
	return TrackerSettings::Instance().AutosaveHistoryDepth;
}

uint32 CAutoSaver::GetSaveInterval() const
{
	return TrackerSettings::Instance().AutosaveIntervalMinutes;
}


bool CAutoSaver::DoSave(DWORD curTime)
{
	bool success = true;

	//If time to save and not already having save in progress.
	if (CheckTimer(curTime) && !m_saveInProgress)
	{ 
		m_saveInProgress = true;

		theApp.BeginWaitCursor(); //display hour glass

		for(auto &modDoc : theApp.GetOpenDocuments())
		{
			if(modDoc->ModifiedSinceLastAutosave())
			{
				if(SaveSingleFile(*modDoc))
				{
					CleanUpBackups(*modDoc);
				} else
				{
					TrackerSettings::Instance().AutosaveEnabled = false;
					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");
					success = false;
				}
			}
		}

		m_lastSave = timeGetTime();
		theApp.EndWaitCursor();  // End display hour glass
		m_saveInProgress = false;
	}
	
	return success;
}


bool CAutoSaver::CheckTimer(DWORD curTime) const
{
	return (curTime - m_lastSave) >= GetSaveIntervalMilliseconds();
}


mpt::PathString CAutoSaver::GetBasePath(const CModDoc &modDoc, bool createPath) const
{
	mpt::PathString path;
	if(GetUseOriginalPath())
	{
		if(modDoc.m_bHasValidPath && !(path = modDoc.GetPathNameMpt()).empty())
		{
			// File has a user-chosen path - remove filename
			path = path.GetPath();
		} else
		{
			// if it doesn't, put it in settings dir
			path = theApp.GetConfigPath() + P_("Autosave\\");
			if(createPath && !CreateDirectory(path.AsNative().c_str(), nullptr) && GetLastError() == ERROR_PATH_NOT_FOUND)
				path = theApp.GetConfigPath();
			else if(!createPath && !path.IsDirectory())
				path = theApp.GetConfigPath();
		}
	} else
	{
		path = GetPath();
	}
	return path.EnsureTrailingSlash();
}


mpt::PathString CAutoSaver::GetBaseName(const CModDoc &modDoc) const
{
	return mpt::PathString::FromCString(modDoc.GetTitle()).SanitizeComponent();
}


mpt::PathString CAutoSaver::BuildFileName(const CModDoc &modDoc) const
{
	mpt::PathString name = GetBasePath(modDoc, true) + GetBaseName(modDoc);
	const CString timeStamp = CTime::GetCurrentTime().Format(_T(".AutoSave.%Y%m%d.%H%M%S."));
	name += mpt::PathString::FromCString(timeStamp);  //append backtup tag + timestamp
	name += mpt::PathString::FromUTF8(modDoc.GetSoundFile().GetModSpecifications().fileExtension);
	return name;
}


bool CAutoSaver::SaveSingleFile(CModDoc &modDoc)
{
	// We do not call CModDoc::DoSave as this populates the Recent Files
	// list with backups... hence we have duplicated code.. :(
	CSoundFile &sndFile = modDoc.GetSoundFile();
	
	mpt::PathString fileName = BuildFileName(modDoc);

	// We are actually not going to show the log for autosaved files.
	ScopedLogCapturer logcapturer(modDoc, _T(""), nullptr, false);

	bool success = false;
	mpt::ofstream f(fileName, std::ios::binary);
	if(f)
	{
		switch(modDoc.GetSoundFile().GetBestSaveFormat())
		{
		case MOD_TYPE_MOD: success = sndFile.SaveMod(f); break;
		case MOD_TYPE_S3M: success = sndFile.SaveS3M(f); break;
		case MOD_TYPE_XM:  success = sndFile.SaveXM(f); break;
		case MOD_TYPE_IT:  success = sndFile.SaveIT(f, fileName); break;
		case MOD_TYPE_MPT: success = sndFile.SaveIT(f, fileName); break;
		}
	}

	return success;
}


void CAutoSaver::CleanUpBackups(const CModDoc &modDoc) const
{
	// Find all autosave files for this document, and delete the oldest ones if there are more than the user wants.
	std::vector<mpt::PathString> foundfiles;
	FolderScanner scanner(GetBasePath(modDoc, false), FolderScanner::kOnlyFiles, GetBaseName(modDoc) + P_(".AutoSave.*"));
	mpt::PathString fileName;
	while(scanner.Next(fileName))
	{
		foundfiles.push_back(std::move(fileName));
	}
	std::sort(foundfiles.begin(), foundfiles.end());
	size_t filesToDelete = std::max(static_cast<size_t>(GetHistoryDepth()), foundfiles.size()) - GetHistoryDepth();
	for(size_t i = 0; i < filesToDelete; i++)
	{
		DeleteFile(foundfiles[i].AsNative().c_str());
	}
}


OPENMPT_NAMESPACE_END