diff --git a/include/ConfigManager.h b/include/ConfigManager.h index de22d22af11..e5df02ff5e9 100644 --- a/include/ConfigManager.h +++ b/include/ConfigManager.h @@ -39,7 +39,6 @@ class LmmsCore; - const QString PROJECTS_PATH = "projects/"; const QString TEMPLATE_PATH = "templates/"; const QString PRESETS_PATH = "presets/"; @@ -55,6 +54,9 @@ const QString PORTABLE_MODE_FILE = "/portable_mode.txt"; class LMMS_EXPORT ConfigManager : public QObject { Q_OBJECT + + using UpgradeMethod = void(ConfigManager::*)(); + public: static inline ConfigManager * inst() { @@ -219,6 +221,10 @@ class LMMS_EXPORT ConfigManager : public QObject return m_version; } + // Used when the configversion attribute is not present in a configuration file. + // Returns the appropriate config file version based on the LMMS version. + unsigned int legacyConfigVersion(); + QString defaultVersion() const; @@ -270,6 +276,9 @@ class LMMS_EXPORT ConfigManager : public QObject void upgrade_1_1_91(); void upgrade(); + // List of all upgrade methods + static const std::vector UPGRADE_METHODS; + QString m_workingDir; QString m_dataDir; QString m_vstDir; @@ -286,6 +295,7 @@ class LMMS_EXPORT ConfigManager : public QObject QString m_backgroundPicFile; QString m_lmmsRcFile; QString m_version; + unsigned int m_configVersion; QStringList m_recentlyOpenedProjects; typedef QVector > stringPairVector; diff --git a/include/DataFile.h b/include/DataFile.h index 5890693da0a..38a1fa7631c 100644 --- a/include/DataFile.h +++ b/include/DataFile.h @@ -31,12 +31,16 @@ #include "lmms_export.h" #include "MemoryManager.h" +#include "ProjectVersion.h" class QTextStream; class LMMS_EXPORT DataFile : public QDomDocument { MM_OPERATORS + + using UpgradeMethod = void(DataFile::*)(); + public: enum Types { @@ -84,6 +88,8 @@ class LMMS_EXPORT DataFile : public QDomDocument return m_type; } + unsigned int legacyFileVersion(); + private: static Type type( const QString& typeName ); static QString typeName( Type type ); @@ -107,9 +113,13 @@ class LMMS_EXPORT DataFile : public QDomDocument void upgrade_1_1_0(); void upgrade_1_1_91(); void upgrade_1_2_0_rc3(); - void upgrade_1_2_0_rc2_42(); void upgrade_1_3_0(); + // List of all upgrade methods + static const std::vector UPGRADE_METHODS; + // List of ProjectVersions for the legacyFileVersion method + static const std::vector UPGRADE_VERSIONS; + void upgrade(); void loadData( const QByteArray & _data, const QString & _sourceFile ); @@ -125,14 +135,10 @@ class LMMS_EXPORT DataFile : public QDomDocument QDomElement m_content; QDomElement m_head; Type m_type; + unsigned int m_fileVersion; } ; -const int LDF_MAJOR_VERSION = 1; -const int LDF_MINOR_VERSION = 0; -const QString LDF_VERSION_STRING = QString::number( LDF_MAJOR_VERSION ) + "." + QString::number( LDF_MINOR_VERSION ); - - #endif diff --git a/src/core/ConfigManager.cpp b/src/core/ConfigManager.cpp index 53262dac7df..5336654aef0 100644 --- a/src/core/ConfigManager.cpp +++ b/src/core/ConfigManager.cpp @@ -38,6 +38,11 @@ #include "lmmsversion.h" +// Vector with all the upgrade methods +const std::vector ConfigManager::UPGRADE_METHODS = { + &ConfigManager::upgrade_1_1_90 , &ConfigManager::upgrade_1_1_91 +}; + static inline QString ensureTrailingSlash(const QString & s ) { if(! s.isEmpty() && !s.endsWith('/') && !s.endsWith('\\')) @@ -51,7 +56,9 @@ static inline QString ensureTrailingSlash(const QString & s ) ConfigManager * ConfigManager::s_instanceOfMe = NULL; -ConfigManager::ConfigManager() : m_version(defaultVersion()) +ConfigManager::ConfigManager() : + m_version(defaultVersion()), + m_configVersion( UPGRADE_METHODS.size() ) { if (QFileInfo::exists(qApp->applicationDirPath() + PORTABLE_MODE_FILE)) { @@ -114,7 +121,7 @@ void ConfigManager::upgrade_1_1_90() void ConfigManager::upgrade_1_1_91() -{ +{ // rename displaydbv to displaydbfs if (!value("app", "displaydbv").isNull()) { setValue("app", "displaydbfs", value("app", "displaydbv")); @@ -131,17 +138,15 @@ void ConfigManager::upgrade() return; } - ProjectVersion createdWith = m_version; - - if (createdWith.setCompareType(ProjectVersion::Build) < "1.1.90") - { - upgrade_1_1_90(); - } + // Runs all necessary upgrade methods + std::for_each( UPGRADE_METHODS.begin() + m_configVersion, UPGRADE_METHODS.end(), + [this](UpgradeMethod um) + { + (this->*um)(); + } + ); - if (createdWith.setCompareType(ProjectVersion::Build) < "1.1.91") - { - upgrade_1_1_91(); - } + ProjectVersion createdWith = m_version; // Don't use old themes as they break the UI (i.e. 0.4 != 1.0, etc) if (createdWith.setCompareType(ProjectVersion::Minor) != LMMS_VERSION) @@ -151,6 +156,7 @@ void ConfigManager::upgrade() // Bump the version, now that we are upgraded m_version = LMMS_VERSION; + m_configVersion = UPGRADE_METHODS.size(); } QString ConfigManager::defaultVersion() const @@ -400,11 +406,23 @@ void ConfigManager::loadConfigFile(const QString & configFile) QDomNode node = root.firstChild(); - // Cache the config version for upgrade() + // Cache LMMS version if (!root.attribute("version").isNull()) { m_version = root.attribute("version"); } + // Get the version of the configuration file (for upgrade purposes) + if( root.attribute("configversion").isNull() ) + { + m_configVersion = legacyConfigVersion(); // No configversion attribute found + } + else + { + bool success; + m_configVersion = root.attribute("configversion").toUInt(&success); + if( !success ) qWarning("Config Version conversion failure."); + } + // create the settings-map out of the DOM while(!node.isNull()) { @@ -565,6 +583,7 @@ void ConfigManager::saveConfigFile() QDomElement lmms_config = doc.createElement("lmms"); lmms_config.setAttribute("version", m_version); + lmms_config.setAttribute("configversion", m_configVersion); doc.appendChild(lmms_config); for(settingsMap::iterator it = m_settings.begin(); @@ -673,3 +692,25 @@ void ConfigManager::initDevelopmentWorkingDir() cmakeCache.close(); } } + +// If configversion is not present, we will convert the LMMS version to the appropriate +// configuration file version for backwards compatibility. +unsigned int ConfigManager::legacyConfigVersion() +{ + ProjectVersion createdWith = m_version; + + createdWith.setCompareType(ProjectVersion::Build); + + if( createdWith < "1.1.90" ) + { + return 0; + } + else if( createdWith < "1.1.91" ) + { + return 1; + } + else + { + return 2; + } +} diff --git a/src/core/DataFile.cpp b/src/core/DataFile.cpp index 44cb920d844..14ebfcb30d9 100644 --- a/src/core/DataFile.cpp +++ b/src/core/DataFile.cpp @@ -49,7 +49,28 @@ static void findIds(const QDomElement& elem, QList& idList); - +// Vector with all the upgrade methods +const std::vector DataFile::UPGRADE_METHODS = { + &DataFile::upgrade_0_2_1_20070501 , &DataFile::upgrade_0_2_1_20070508, + &DataFile::upgrade_0_3_0_rc2 , &DataFile::upgrade_0_3_0, + &DataFile::upgrade_0_4_0_20080104 , &DataFile::upgrade_0_4_0_20080118, + &DataFile::upgrade_0_4_0_20080129 , &DataFile::upgrade_0_4_0_20080409, + &DataFile::upgrade_0_4_0_20080607 , &DataFile::upgrade_0_4_0_20080622, + &DataFile::upgrade_0_4_0_beta1 , &DataFile::upgrade_0_4_0_rc2, + &DataFile::upgrade_1_0_99 , &DataFile::upgrade_1_1_0, + &DataFile::upgrade_1_1_91 , &DataFile::upgrade_1_2_0_rc3, + &DataFile::upgrade_1_3_0 +}; + +// Vector of all versions that have upgrade routines. +const std::vector DataFile::UPGRADE_VERSIONS = { + "0.2.1-20070501" , "0.2.1-20070508" , "0.3.0-rc2", + "0.3.0" , "0.4.0-20080104" , "0.4.0-20080118", + "0.4.0-20080129" , "0.4.0-20080409" , "0.4.0-20080607", + "0.4.0-20080622" , "0.4.0-beta1" , "0.4.0-rc2", + "1.0.99-0" , "1.1.0-0" , "1.1.91-0", + "1.2.0-rc3" , "1.3.0" +}; DataFile::typeDescStruct DataFile::s_types[DataFile::TypeCount] = @@ -71,11 +92,12 @@ DataFile::DataFile( Type type ) : QDomDocument( "lmms-project" ), m_content(), m_head(), - m_type( type ) + m_type( type ), + m_fileVersion( UPGRADE_METHODS.size() ) { appendChild( createProcessingInstruction("xml", "version=\"1.0\"")); QDomElement root = createElement( "lmms-project" ); - root.setAttribute( "version", LDF_VERSION_STRING ); + root.setAttribute( "version", m_fileVersion ); root.setAttribute( "type", typeName( type ) ); root.setAttribute( "creator", "LMMS" ); root.setAttribute( "creatorversion", LMMS_VERSION ); @@ -95,7 +117,8 @@ DataFile::DataFile( Type type ) : DataFile::DataFile( const QString & _fileName ) : QDomDocument(), m_content(), - m_head() + m_head(), + m_fileVersion( UPGRADE_METHODS.size() ) { QFile inFile( _fileName ); if( !inFile.open( QIODevice::ReadOnly ) ) @@ -873,31 +896,6 @@ void DataFile::upgrade_1_1_91() } -void DataFile::upgrade_1_2_0_rc3() -{ - // Upgrade from earlier bbtrack beat note behaviour of adding - // steps if a note is placed after the last step. - QDomNodeList bbtracks = elementsByTagName( "bbtrack" ); - for( int i = 0; !bbtracks.item( i ).isNull(); ++i ) - { - QDomNodeList patterns = bbtracks.item( i - ).toElement().elementsByTagName( - "pattern" ); - for( int j = 0; !patterns.item( j ).isNull(); ++j ) - { - int patternLength, steps; - QDomElement el = patterns.item( j ).toElement(); - if( el.attribute( "len" ) != "" ) - { - patternLength = el.attribute( "len" ).toInt(); - steps = patternLength / 12; - el.setAttribute( "steps", steps ); - } - } - } -} - - static void upgradeElement_1_2_0_rc2_42( QDomElement & el ) { if( el.hasAttribute( "syncmode" ) ) @@ -930,8 +928,30 @@ static void upgradeElement_1_2_0_rc2_42( QDomElement & el ) } -void DataFile::upgrade_1_2_0_rc2_42() +void DataFile::upgrade_1_2_0_rc3() { + // Upgrade from earlier bbtrack beat note behaviour of adding + // steps if a note is placed after the last step. + QDomNodeList bbtracks = elementsByTagName( "bbtrack" ); + for( int i = 0; !bbtracks.item( i ).isNull(); ++i ) + { + QDomNodeList patterns = bbtracks.item( i + ).toElement().elementsByTagName( + "pattern" ); + for( int j = 0; !patterns.item( j ).isNull(); ++j ) + { + int patternLength, steps; + QDomElement el = patterns.item( j ).toElement(); + if( el.attribute( "len" ) != "" ) + { + patternLength = el.attribute( "len" ).toInt(); + steps = patternLength / 12; + el.setAttribute( "steps", steps ); + } + } + } + + // DataFile::upgrade_1_2_0_rc2_42 QDomElement el = firstChildElement(); while ( !el.isNull() ) { @@ -1338,92 +1358,19 @@ void DataFile::upgrade_1_3_0() void DataFile::upgrade() { - ProjectVersion version = - documentElement().attribute( "creatorversion" ). - replace( "svn", "" ); - - if( version < "0.2.1-20070501" ) - { - upgrade_0_2_1_20070501(); - } - - if( version < "0.2.1-20070508" ) - { - upgrade_0_2_1_20070508(); - } - - if( version < "0.3.0-rc2" ) - { - upgrade_0_3_0_rc2(); - } - - if( version < "0.3.0" ) - { - upgrade_0_3_0(); - } - - if( version < "0.4.0-20080104" ) - { - upgrade_0_4_0_20080104(); - } - - if( version < "0.4.0-20080118" ) - { - upgrade_0_4_0_20080118(); - } - - if( version < "0.4.0-20080129" ) - { - upgrade_0_4_0_20080129(); - } - - if( version < "0.4.0-20080409" ) - { - upgrade_0_4_0_20080409(); - } - - if( version < "0.4.0-20080607" ) - { - upgrade_0_4_0_20080607(); - } - - if( version < "0.4.0-20080622" ) - { - upgrade_0_4_0_20080622(); - } + // Runs all necessary upgrade methods + std::for_each( UPGRADE_METHODS.begin() + m_fileVersion, UPGRADE_METHODS.end(), + [this](UpgradeMethod um) + { + (this->*um)(); + } + ); - if( version < "0.4.0-beta1" ) - { - upgrade_0_4_0_beta1(); - } - if( version < "0.4.0-rc2" ) - { - upgrade_0_4_0_rc2(); - } - if( version < "1.0.99-0" ) - { - upgrade_1_0_99(); - } - if( version < "1.1.0-0" ) - { - upgrade_1_1_0(); - } - if( version < "1.1.91-0" ) - { - upgrade_1_1_91(); - } - if( version < "1.2.0-rc3" ) - { - upgrade_1_2_0_rc3(); - upgrade_1_2_0_rc2_42(); - } - if( version < "1.3.0" ) - { - upgrade_1_3_0(); - } + // Bump the file version (which should be the size of the upgrade methods vector) + m_fileVersion = UPGRADE_METHODS.size(); // update document meta data - documentElement().setAttribute( "version", LDF_VERSION_STRING ); + documentElement().setAttribute( "version", m_fileVersion ); documentElement().setAttribute( "type", typeName( type() ) ); documentElement().setAttribute( "creator", "LMMS" ); documentElement().setAttribute( "creatorversion", LMMS_VERSION ); @@ -1483,6 +1430,20 @@ void DataFile::loadData( const QByteArray & _data, const QString & _sourceFile ) m_type = type( root.attribute( "type" ) ); m_head = root.elementsByTagName( "head" ).item( 0 ).toElement(); + if( root.hasAttribute( "version" ) ) + { + if( root.attribute( "version" ) == "1.0" ){ + // The file versioning is now a unsigned int, not maj.min, so we use + // legacyFileVersion() to retrieve the appropriate version + m_fileVersion = legacyFileVersion(); + } + else + { + bool success; + m_fileVersion = root.attribute( "version" ).toUInt( &success ); + if( !success ) qWarning("File Version conversion failure."); + } + } if( root.hasAttribute( "creatorversion" ) ) { @@ -1542,3 +1503,17 @@ void findIds(const QDomElement& elem, QList& idList) child = child.nextSiblingElement(); } } + +unsigned int DataFile::legacyFileVersion() +{ + // Version of LMMs that created this project + ProjectVersion creator = + documentElement().attribute( "creatorversion" ). + replace( "svn", "" ); + + // Get an iterator pointing at the first upgrade we need to run (or at the end if there is no such upgrade) + auto firstRequiredUpgrade = std::upper_bound( UPGRADE_VERSIONS.begin(), UPGRADE_VERSIONS.end(), creator ); + + // Convert the iterator to an index, which is our file version (starting at 0) + return std::distance( UPGRADE_VERSIONS.begin(), firstRequiredUpgrade ); +}