3636
3737#include " ui/QtGraphicsOperations.h"
3838
39+ #include < QDateTime>
40+ #include < QDir>
41+ #include < QFileInfo>
42+ #include < QStandardPaths>
43+
3944#include " object.h"
4045#include " management.h"
4146#include " util.h"
@@ -114,6 +119,13 @@ Editor::Editor() : currentObject{ -1 }, Shield_sys_teams(Iff_info.size(), Global
114119 // When a mission was loaded we need to notify everyone that the mission has changed
115120 connect (this , &Editor::missionLoaded, this , [this ](const std::string&) { missionChanged (); });
116121
122+ _autosaveDirectory = QStandardPaths::writableLocation (QStandardPaths::AppDataLocation) + " /autosave/" ;
123+ QDir ().mkpath (_autosaveDirectory);
124+
125+ _autosaveTimer = new QTimer (this );
126+ _autosaveTimer->setSingleShot (false );
127+ connect (_autosaveTimer, &QTimer::timeout, this , &Editor::performTimedAutosave);
128+
117129 fredApp->runAfterInit ([this ]() { initialSetup (); });
118130}
119131
@@ -134,119 +146,56 @@ void Editor::update() {
134146 }
135147}
136148
137- int Editor::autosave (const char * /* desc*/ ) {
138- if (autosaveDisabled || !_lastActiveViewport)
139- return 0 ;
140-
141- Fred_mission_save save;
142- save.set_always_save_display_names (_lastActiveViewport->Always_save_display_names );
143- save.set_view_pos (_lastActiveViewport->camera .view_pos );
144- save.set_view_orient (_lastActiveViewport->camera .view_orient );
145- save.set_fred_alt_names (Fred_alt_names);
146- save.set_fred_callsigns (Fred_callsigns);
147-
148- // autosave_mission_file() needs a mutable buffer because it reads but doesn't write through it
149- char backup_name_buf[] = MISSION_BACKUP_NAME ;
150- if (save.autosave_mission_file (backup_name_buf)) {
151- undoCount = undoAvailable = 0 ;
152- return -1 ;
153- }
154-
155- undoCount++;
156- checkUndo ();
157- return 0 ;
158- }
159-
160- int Editor::checkUndo () {
161- undoAvailable = 0 ;
162- if (undoCount == 0 )
163- return 0 ;
149+ void Editor::maybeUseAutosave (std::string& filepath)
150+ {
151+ const QString qpath = QString::fromStdString (filepath);
152+ const QString basename = QFileInfo (qpath).fileName ();
153+ const QString autosavePath = _autosaveDirectory + basename;
164154
165- // Undo is available when Backup.002 exists (Backup.001 is the current, .002 is what we load)
166- CFileLocation loc = cf_find_file_location (" Backup.002" , CF_TYPE_MISSIONS );
167- if (loc.found ) {
168- undoAvailable = 1 ;
169- return 1 ;
170- }
171- return 0 ;
172- }
155+ const QFileInfo autosaveInfo (autosavePath);
156+ if (!autosaveInfo.exists ())
157+ return ;
173158
174- bool Editor::autoload () {
175- if (!undoAvailable || !_lastActiveViewport )
176- return false ;
159+ const QFileInfo originalInfo (qpath);
160+ if (autosaveInfo. lastModified () <= originalInfo. lastModified () )
161+ return ;
177162
178- // Load the previous state from Backup.002
179- if (!loadMission (" Backup.002" , MPF_FAST_RELOAD ))
180- return false ;
163+ if (_lastActiveViewport == nullptr || _lastActiveViewport->dialogProvider == nullptr )
164+ return ;
181165
182- // Delete Backup.001 (the state we just replaced)
183- cf_delete (" Backup.001" , CF_TYPE_MISSIONS );
166+ const auto result = _lastActiveViewport->dialogProvider ->showButtonDialog (
167+ DialogType::Question,
168+ " Autosave Recovery" ,
169+ " An autosave file for this mission is newer than the original. Load the autosave instead?" ,
170+ { DialogButton::Yes, DialogButton::No });
184171
185- // Rotate backups back one slot: .003->.002, .004->.003, etc, .009->.008
186- char old_name[256 ], new_name[256 ];
187- for (int i = 1 ; i < MISSION_BACKUP_DEPTH ; i++) {
188- sprintf (old_name, " Backup.%.3d" , i + 1 );
189- sprintf (new_name, " Backup.%.3d" , i);
190- cf_rename (old_name, new_name, CF_TYPE_MISSIONS );
172+ if (result == DialogButton::Yes) {
173+ filepath = autosavePath.toStdString ();
191174 }
192-
193- if (undoCount > 0 )
194- undoCount--;
195- checkUndo ();
196- return true ;
197175}
198176
199- void Editor::maybeUseAutosave (std::string& filepath)
200- {
201- // first, just grab the info of this mission
202- if (!parse_main (filepath.c_str (), MPF_ONLY_MISSION_INFO ))
203- return ;
204- SCP_string created = The_mission.created ;
205- CFileLocation res = cf_find_file_location (filepath.c_str (), CF_TYPE_ANY );
206- time_t modified = res.m_time ;
207- if (!res.found )
208- {
209- UNREACHABLE (" Couldn't find path '%s' even though parse_main() succeeded!" , filepath.c_str ());
210- return ;
211- }
212-
213- // now check all the autosaves
214- SCP_string backup_name;
215- CFileLocation backup_res;
216- for (int i = 1 ; i <= MISSION_BACKUP_DEPTH ; ++i)
217- {
218- backup_name = MISSION_BACKUP_NAME ;
219- char extension[5 ];
220- sprintf (extension, " .%.3d" , i);
221- backup_name += extension;
222-
223- backup_res = cf_find_file_location (backup_name.c_str (), CF_TYPE_MISSIONS );
224- if (backup_res.found && parse_main (backup_res.full_name .c_str (), MPF_ONLY_MISSION_INFO ))
225- {
226- SCP_string this_created = The_mission.created ;
227- time_t this_modified = backup_res.m_time ;
228-
229- if (created == this_created && this_modified > modified)
230- break ;
231- }
177+ void Editor::startAutosaveTimer (int intervalSeconds) {
178+ _autosaveTimer->stop ();
179+ if (intervalSeconds > 0 )
180+ _autosaveTimer->start (intervalSeconds * 1000 );
181+ }
232182
233- backup_name.clear ();
234- }
183+ void Editor::stopAutosaveTimer () {
184+ _autosaveTimer->stop ();
185+ }
235186
236- // maybe load from the backup instead
237- if (!backup_name.empty ())
238- {
239- SCP_string prompt = " Autosaved file " ;
240- prompt += backup_name;
241- prompt += " has a file modification time more recent than the specified file. Do you want to load the autosave instead?" ;
187+ void Editor::setCurrentMissionPath (const QString& path) {
188+ _currentMissionPath = path;
189+ }
242190
243- auto z = _lastActiveViewport-> dialogProvider -> showButtonDialog (DialogType::Question,
244- " Recover from autosave " ,
245- prompt. c_str (),
246- { DialogButton::Yes, DialogButton::No }) ;
247- if (z == DialogButton::Yes)
248- filepath = backup_res. full_name ; // replace the specified file with the autosave file
191+ void Editor::performTimedAutosave () {
192+ QString savePath;
193+ if (_currentMissionPath. isEmpty ()) {
194+ savePath = _autosaveDirectory + " untitled_autosave.fs2 " ;
195+ } else {
196+ savePath = _autosaveDirectory + QFileInfo (_currentMissionPath). fileName ();
249197 }
198+ autosaveDue (savePath);
250199}
251200
252201bool Editor::loadMission (const std::string& mission_name, int flags) {
@@ -482,8 +431,7 @@ bool Editor::loadMission(const std::string& mission_name, int flags) {
482431 }
483432
484433 if (!(flags & MPF_FAST_RELOAD )) {
485- undoCount = undoAvailable = 0 ;
486- autosave (" nothing" );
434+ // TODO(Phase 3): _undoStack->clear()
487435 }
488436
489437 return true ;
@@ -850,8 +798,7 @@ void Editor::createNewMission() {
850798 clearMission ();
851799 create_player (&vmd_zero_vector, &vmd_identity_matrix);
852800 stars_post_level_init ();
853- undoCount = undoAvailable = 0 ;
854- autosave (" nothing" );
801+ // TODO(Phase 3): _undoStack->clear()
855802 missionLoaded (" " );
856803}
857804void Editor::hideMarkedObjects () {
0 commit comments