diff --git a/common/project/project_archiver.cpp b/common/project/project_archiver.cpp index 98d4306311..3821ccf656 100644 --- a/common/project/project_archiver.cpp +++ b/common/project/project_archiver.cpp @@ -33,14 +33,47 @@ #include #include +#include + #define ZipFileExtension wxT( "zip" ) - PROJECT_ARCHIVER::PROJECT_ARCHIVER() { } +bool PROJECT_ARCHIVER::AreZipArchivesIdentical( const wxString& aZipFileA, + const wxString& aZipFileB, REPORTER& aReporter ) +{ + wxFFileInputStream streamA( aZipFileA ); + wxFFileInputStream streamB( aZipFileB ); + + if( !streamA.IsOk() || !streamB.IsOk() ) + { + aReporter.Report( _( "Could not open archive file." ), RPT_SEVERITY_ERROR ); + return false; + } + + wxZipInputStream zipStreamA = wxZipInputStream( streamA ); + wxZipInputStream zipStreamB = wxZipInputStream( streamB ); + + std::set crcsA; + std::set crcsB; + + + for( wxZipEntry* entry = zipStreamA.GetNextEntry(); entry; entry = zipStreamA.GetNextEntry() ) + { + crcsA.insert( entry->GetCrc() ); + } + + for( wxZipEntry* entry = zipStreamB.GetNextEntry(); entry; entry = zipStreamB.GetNextEntry() ) + { + crcsB.insert( entry->GetCrc() ); + } + + return crcsA == crcsB; +} + // Unarchive Files code comes from wxWidgets sample/archive/archive.cpp bool PROJECT_ARCHIVER::Unarchive( const wxString& aSrcFile, const wxString& aDestDir, diff --git a/common/settings/settings_manager.cpp b/common/settings/settings_manager.cpp index 891c3b69ea..0c5d2907f9 100644 --- a/common/settings/settings_manager.cpp +++ b/common/settings/settings_manager.cpp @@ -1339,17 +1339,36 @@ bool SETTINGS_MANAGER::TriggerBackupIfNeeded( REPORTER& aReporter ) const return first.GetTicks() > second.GetTicks(); } ); - // Do we even need to back up? - if( !files.empty() ) + // Backup + bool backupSuccessful = BackupProject( aReporter ); + if( !backupSuccessful ) + return false; + + // Update the file list + files.clear(); + dir.Traverse( traverser, wxT( "*.zip" ) ); + + // Sort newest-first + std::sort( files.begin(), files.end(), + [&]( const wxString& aFirst, const wxString& aSecond ) -> bool + { + wxDateTime first = modTime( aFirst ); + wxDateTime second = modTime( aSecond ); + + return first.GetTicks() > second.GetTicks(); + } ); + + // Are there any changes since the last backup? + if( files.size() > 1 ) { - wxDateTime lastTime = modTime( files[0] ); + PROJECT_ARCHIVER archiver; + bool identicalToPrevious = + archiver.AreZipArchivesIdentical( files[0], files[1], aReporter ); - if( lastTime.IsValid() ) + if( identicalToPrevious ) { - wxTimeSpan delta = wxDateTime::Now() - modTime( files[0] ); - - if( delta.IsShorterThan( wxTimeSpan::Seconds( settings.min_interval ) ) ) - return true; + wxRemoveFile( files[0] ); + return true; } } @@ -1413,7 +1432,7 @@ bool SETTINGS_MANAGER::TriggerBackupIfNeeded( REPORTER& aReporter ) const wxRemoveFile( file ); } - return BackupProject( aReporter ); + return true; } diff --git a/include/project/project_archiver.h b/include/project/project_archiver.h index 1fccbb4478..3a4d09dced 100644 --- a/include/project/project_archiver.h +++ b/include/project/project_archiver.h @@ -36,6 +36,16 @@ public: ~PROJECT_ARCHIVER() = default; + /** + * Compares the crcs of all the files in zip archive to determine whether the archives are identical + * @param aZipFileA is the full path to the first zip + * @param aZipFileB is the full path to the second zip + * @param aReporter is used to report status + * @return true if the archives are identical + */ + bool AreZipArchivesIdentical( const wxString& aZipFileA, const wxString& aZipFileB, + REPORTER& aReporter ); + /** * Creates an archive of the project * @param aSrcFile is the full path to the project to be archived