diff --git a/Engine/AppInstance.h b/Engine/AppInstance.h index ab3561194c..383f635644 100644 --- a/Engine/AppInstance.h +++ b/Engine/AppInstance.h @@ -218,10 +218,18 @@ GCC_DIAG_SUGGEST_OVERRIDE_ON { } + virtual void loadProjectGui(bool /*isAutosave*/, boost::archive::binary_iarchive & /*archive*/) const + { + } + virtual void saveProjectGui(boost::archive::xml_oarchive & /*archive*/) { } + virtual void saveProjectGui(boost::archive::binary_oarchive & /*archive*/) + { + } + virtual void setupViewersForViews(const std::vector& /*viewNames*/) { } diff --git a/Engine/EngineFwd.h b/Engine/EngineFwd.h index 55f1bf6fbb..28d73dccf7 100644 --- a/Engine/EngineFwd.h +++ b/Engine/EngineFwd.h @@ -39,6 +39,8 @@ namespace boost { namespace archive { class xml_iarchive; class xml_oarchive; +class binary_iarchive; +class binary_oarchive; } namespace serialization { class access; diff --git a/Engine/Project.cpp b/Engine/Project.cpp index cc6c75cf9d..b852190d6c 100644 --- a/Engine/Project.cpp +++ b/Engine/Project.cpp @@ -287,11 +287,6 @@ Project::loadProjectInternal(const QString & path, } bool ret = false; - FStreamsSupport::ifstream ifile; - FStreamsSupport::open( &ifile, filePath.toStdString() ); - if (!ifile) { - throw std::runtime_error( tr("Failed to open %1").arg(filePath).toStdString() ); - } if ( (NATRON_VERSION_MAJOR == 1) && (NATRON_VERSION_MINOR == 0) && (NATRON_VERSION_REVISION == 0) ) { ///Try to determine if the project was made during Natron v1.0.0 - RC2 or RC3 to detect a bug we introduced at that time @@ -315,21 +310,28 @@ Project::loadProjectInternal(const QString & path, } LoadProjectSplashScreen_RAII __raii_splashscreen__(getApp(), name); + bool xml_loaded = false; try { - bool bgProject; - boost::archive::xml_iarchive iArchive(ifile); - { - FlagSetter __raii_loadingProjectInternal__(true, &_imp->isLoadingProjectInternal, &_imp->isLoadingProjectMutex); + // XML loading tests first, then binary loading + FStreamsSupport::ifstream ifile; + FStreamsSupport::open( &ifile, filePath.toStdString()); + if (ifile) { + bool bgProject; + boost::archive::xml_iarchive iArchive(ifile); + { + FlagSetter __raii_loadingProjectInternal__(true, &_imp->isLoadingProjectInternal, &_imp->isLoadingProjectMutex); + + iArchive >> boost::serialization::make_nvp("Background_project", bgProject); + ProjectSerialization projectSerializationObj( getApp() ); + iArchive >> boost::serialization::make_nvp("Project", projectSerializationObj); + ret = load(projectSerializationObj, name, path, mustSave); + } // __raii_loadingProjectInternal__ - iArchive >> boost::serialization::make_nvp("Background_project", bgProject); - ProjectSerialization projectSerializationObj( getApp() ); - iArchive >> boost::serialization::make_nvp("Project", projectSerializationObj); - ret = load(projectSerializationObj, name, path, mustSave); - } // __raii_loadingProjectInternal__ - - if (!bgProject) { - getApp()->loadProjectGui(isAutoSave, iArchive); + if (!bgProject) { + getApp()->loadProjectGui(isAutoSave, iArchive); + } + xml_loaded = true; } } catch (const std::exception &e) { const ProjectBeingLoadedInfo& pInfo = getApp()->getProjectBeingLoadedInfo(); @@ -339,7 +341,6 @@ Project::loadProjectInternal(const QString & path, QString message = tr("This project was saved with a more recent version (%1.%2.%3) of %4. Projects are not forward compatible and may only be opened in a version of %4 equal or more recent than the version that saved it.").arg(pInfo.vMajor).arg(pInfo.vMinor).arg(pInfo.vRev).arg(QString::fromUtf8(NATRON_APPLICATION_NAME)); throw std::runtime_error(message.toStdString()); } - throw std::runtime_error( tr("Unrecognized or damaged project file:").toStdString() + ' ' + e.what()); } catch (...) { const ProjectBeingLoadedInfo& pInfo = getApp()->getProjectBeingLoadedInfo(); if (pInfo.vMajor > NATRON_VERSION_MAJOR || @@ -348,7 +349,48 @@ Project::loadProjectInternal(const QString & path, QString message = tr("This project was saved with a more recent version (%1.%2.%3) of %4. Projects are not forward compatible and may only be opened in a version of %4 equal or more recent than the version that saved it.").arg(pInfo.vMajor).arg(pInfo.vMinor).arg(pInfo.vRev).arg(QString::fromUtf8(NATRON_APPLICATION_NAME)); throw std::runtime_error(message.toStdString()); } - throw std::runtime_error( tr("Unrecognized or damaged project file").toStdString() ); + } + + if (!xml_loaded){ + try { + FStreamsSupport::ifstream ifile; + FStreamsSupport::open( &ifile, filePath.toStdString(), std::ios::in | std::ios::binary ); + if (!ifile) { + throw std::runtime_error( tr("Failed to open %1").arg(filePath).toStdString() ); + } + bool bgProject; + boost::archive::binary_iarchive iArchive(ifile); + { + FlagSetter __raii_loadingProjectInternal__(true, &_imp->isLoadingProjectInternal, &_imp->isLoadingProjectMutex); + + iArchive >> boost::serialization::make_nvp("Background_project", bgProject); + ProjectSerialization projectSerializationObj( getApp() ); + iArchive >> boost::serialization::make_nvp("Project", projectSerializationObj); + ret = load(projectSerializationObj, name, path, mustSave); + } // __raii_loadingProjectInternal__ + + if (!bgProject) { + getApp()->loadProjectGui(isAutoSave, iArchive); + } + } catch (const std::exception &e) { + const ProjectBeingLoadedInfo& pInfo = getApp()->getProjectBeingLoadedInfo(); + if (pInfo.vMajor > NATRON_VERSION_MAJOR || + (pInfo.vMajor == NATRON_VERSION_MAJOR && pInfo.vMinor > NATRON_VERSION_MINOR) || + (pInfo.vMajor == NATRON_VERSION_MAJOR && pInfo.vMinor == NATRON_VERSION_MINOR && pInfo.vRev > NATRON_VERSION_REVISION)) { + QString message = tr("This project was saved with a more recent version (%1.%2.%3) of %4. Projects are not forward compatible and may only be opened in a version of %4 equal or more recent than the version that saved it.").arg(pInfo.vMajor).arg(pInfo.vMinor).arg(pInfo.vRev).arg(QString::fromUtf8(NATRON_APPLICATION_NAME)); + throw std::runtime_error(message.toStdString()); + } + throw std::runtime_error( tr("Unrecognized or damaged project file:").toStdString() + ' ' + e.what()); + } catch (...) { + const ProjectBeingLoadedInfo& pInfo = getApp()->getProjectBeingLoadedInfo(); + if (pInfo.vMajor > NATRON_VERSION_MAJOR || + (pInfo.vMajor == NATRON_VERSION_MAJOR && pInfo.vMinor > NATRON_VERSION_MINOR) || + (pInfo.vMajor == NATRON_VERSION_MAJOR && pInfo.vMinor == NATRON_VERSION_MINOR && pInfo.vRev > NATRON_VERSION_REVISION)) { + QString message = tr("This project was saved with a more recent version (%1.%2.%3) of %4. Projects are not forward compatible and may only be opened in a version of %4 equal or more recent than the version that saved it.").arg(pInfo.vMajor).arg(pInfo.vMinor).arg(pInfo.vRev).arg(QString::fromUtf8(NATRON_APPLICATION_NAME)); + throw std::runtime_error(message.toStdString()); + } + throw std::runtime_error( tr("Unrecognized or damaged project file").toStdString() ); + } } Format f; @@ -599,12 +641,6 @@ Project::saveProjectInternal(const QString & path, tmpFilename.append( QString::number( time.toMSecsSinceEpoch() ) ); { - FStreamsSupport::ofstream ofile; - FStreamsSupport::open( &ofile, tmpFilename.toStdString() ); - if (!ofile) { - throw std::runtime_error( tr("Failed to open file ").toStdString() + tmpFilename.toStdString() ); - } - ///Fix file paths before saving. QString oldProjectPath = QString::fromUtf8( _imp->getProjectPath().c_str() ); @@ -615,27 +651,62 @@ Project::saveProjectInternal(const QString & path, _imp->natronVersion->setValue( generateUserFriendlyNatronVersionName() ); } - try { - boost::archive::xml_oarchive oArchive(ofile); - bool bgProject = getApp()->isBackground(); - oArchive << boost::serialization::make_nvp("Background_project", bgProject); - ProjectSerialization projectSerializationObj( getApp() ); - save(&projectSerializationObj); - oArchive << boost::serialization::make_nvp("Project", projectSerializationObj); - if (!bgProject) { - AppInstancePtr app = getApp(); - if (app) { - app->saveProjectGui(oArchive); + if (!appPTR->getCurrentSettings()->saveAsBinary()){ + try { + FStreamsSupport::ofstream ofile; + FStreamsSupport::open( &ofile, tmpFilename.toStdString(), std::ios::out); + if (!ofile) { + throw std::runtime_error( tr("Failed to open file ").toStdString() + tmpFilename.toStdString() ); + } + boost::archive::xml_oarchive oArchive(ofile); + bool bgProject = getApp()->isBackground(); + oArchive << boost::serialization::make_nvp("Background_project", bgProject); + ProjectSerialization projectSerializationObj( getApp() ); + save(&projectSerializationObj); + oArchive << boost::serialization::make_nvp("Project", projectSerializationObj); + if (!bgProject) { + AppInstancePtr app = getApp(); + if (app) { + app->saveProjectGui(oArchive); + } + } + } catch (...) { + if (!autoSave && updateProjectProperties) { + ///Reset the old project path in case of failure. + _imp->autoSetProjectDirectory(oldProjectPath); } + throw; } - } catch (...) { - if (!autoSave && updateProjectProperties) { - ///Reset the old project path in case of failure. - _imp->autoSetProjectDirectory(oldProjectPath); + } + else + { + try { + FStreamsSupport::ofstream ofile; + FStreamsSupport::open( &ofile, tmpFilename.toStdString(), std::ios::out | std::ios::binary); + if (!ofile) { + throw std::runtime_error( tr("Failed to open file ").toStdString() + tmpFilename.toStdString() ); + } + boost::archive::binary_oarchive oArchive(ofile); + bool bgProject = getApp()->isBackground(); + oArchive << boost::serialization::make_nvp("Background_project", bgProject); + ProjectSerialization projectSerializationObj( getApp() ); + save(&projectSerializationObj); + oArchive << boost::serialization::make_nvp("Project", projectSerializationObj); + if (!bgProject) { + AppInstancePtr app = getApp(); + if (app) { + app->saveProjectGui(oArchive); + } + } + } catch (...) { + if (!autoSave && updateProjectProperties) { + ///Reset the old project path in case of failure. + _imp->autoSetProjectDirectory(oldProjectPath); + } + throw; } - throw; } - } // ofile + } if (!autoSave) { // rotate backups diff --git a/Engine/Settings.cpp b/Engine/Settings.cpp index 64b63b4c7b..31d297c202 100644 --- a/Engine/Settings.cpp +++ b/Engine/Settings.cpp @@ -207,6 +207,12 @@ Settings::initializeKnobsGeneral() "last saved), *.~2~ (third last saved).") ); _generalTab->addKnob(_saveVersions); + _saveAsBinary = AppManager::createKnob( this, tr("Save projects as binary") ); + _saveAsBinary->setName("saveAsBinary"); + _saveAsBinary->setHintToolTip( tr("When checked, %1 will save projects in a binary format which is faster to load and save. " + "When unchecked, projects will be saved in a human-readable text format. " + "Note that the text format is not guaranteed to be stable across versions of %1.").arg( QString::fromUtf8(NATRON_APPLICATION_NAME) ) ); + _hostName = AppManager::createKnob( this, tr("Appear to plug-ins as") ); _hostName->setName("pluginHostName"); _hostName->setHintToolTip( tr("%1 will appear with the name of the selected application to the OpenFX plug-ins. " @@ -1469,6 +1475,7 @@ Settings::setDefaultValues() _autoSaveUnSavedProjects->setDefaultValue(true); _autoSaveDelay->setDefaultValue(5, 0); _saveVersions->setDefaultValue(1); + _saveAsBinary->setDefaultValue(false); _hostName->setDefaultValue(0); _customHostName->setDefaultValue(NATRON_ORGANIZATION_DOMAIN_TOPLEVEL "." NATRON_ORGANIZATION_DOMAIN_SUB "." NATRON_APPLICATION_NAME); @@ -2912,6 +2919,12 @@ Settings::saveVersions() const return _saveVersions->getValue(); } +bool +Settings::saveAsBinary() const +{ + return _saveAsBinary->getValue(); +} + bool Settings::isSnapToNodeEnabled() const diff --git a/Engine/Settings.h b/Engine/Settings.h index 61fa7b582b..43b761d9dc 100644 --- a/Engine/Settings.h +++ b/Engine/Settings.h @@ -181,6 +181,8 @@ GCC_DIAG_SUGGEST_OVERRIDE_ON int saveVersions() const; + bool saveAsBinary() const; + bool isSnapToNodeEnabled() const; bool isCheckForUpdatesEnabled() const; @@ -434,6 +436,7 @@ GCC_DIAG_SUGGEST_OVERRIDE_ON KnobBoolPtr _autoSaveUnSavedProjects; KnobIntPtr _autoSaveDelay; KnobIntPtr _saveVersions; + KnobBoolPtr _saveAsBinary; KnobChoicePtr _hostName; KnobStringPtr _customHostName; diff --git a/Gui/Gui.h b/Gui/Gui.h index 8c0a6b52a9..310b559a6f 100644 --- a/Gui/Gui.h +++ b/Gui/Gui.h @@ -305,8 +305,10 @@ GCC_DIAG_SUGGEST_OVERRIDE_ON static QPixmap screenShot(QWidget* w); void loadProjectGui(bool isAutosave, boost::archive::xml_iarchive & obj) const; + void loadProjectGui(bool isAutosave, boost::archive::binary_iarchive & obj) const; void saveProjectGui(boost::archive::xml_oarchive & archive); + void saveProjectGui(boost::archive::binary_oarchive & archive); void setColorPickersColor(double r, double g, double b, double a); diff --git a/Gui/Gui20.cpp b/Gui/Gui20.cpp index 1ccbb77d17..e61470c6b7 100644 --- a/Gui/Gui20.cpp +++ b/Gui/Gui20.cpp @@ -1465,6 +1465,13 @@ Gui::loadProjectGui(bool isAutosave, boost::archive::xml_iarchive & obj) const _imp->_projectGui->load(isAutosave, obj); } +void +Gui::loadProjectGui(bool isAutosave, boost::archive::binary_iarchive & obj) const +{ + assert(_imp->_projectGui); + _imp->_projectGui->load(isAutosave, obj); +} + void Gui::saveProjectGui(boost::archive::xml_oarchive & archive) { @@ -1472,6 +1479,13 @@ Gui::saveProjectGui(boost::archive::xml_oarchive & archive) _imp->_projectGui->save(archive); } +void +Gui::saveProjectGui(boost::archive::binary_oarchive & archive) +{ + assert(_imp->_projectGui); + _imp->_projectGui->save(archive); +} + bool Gui::isAboutToClose() const { diff --git a/Gui/GuiAppInstance.cpp b/Gui/GuiAppInstance.cpp index a4eddc6798..ee378b30d4 100644 --- a/Gui/GuiAppInstance.cpp +++ b/Gui/GuiAppInstance.cpp @@ -788,6 +788,12 @@ GuiAppInstance::loadProjectGui(bool isAutosave, boost::archive::xml_iarchive & a _imp->_gui->loadProjectGui(isAutosave, archive); } +void +GuiAppInstance::loadProjectGui(bool isAutosave, boost::archive::binary_iarchive & archive) const +{ + _imp->_gui->loadProjectGui(isAutosave, archive); +} + void GuiAppInstance::saveProjectGui(boost::archive::xml_oarchive & archive) { @@ -796,6 +802,14 @@ GuiAppInstance::saveProjectGui(boost::archive::xml_oarchive & archive) } } +void +GuiAppInstance::saveProjectGui(boost::archive::binary_oarchive & archive) +{ + if (_imp->_gui) { + _imp->_gui->saveProjectGui(archive); + } +} + void GuiAppInstance::setupViewersForViews(const std::vector& viewNames) { diff --git a/Gui/GuiAppInstance.h b/Gui/GuiAppInstance.h index e542fb6659..d274e81928 100644 --- a/Gui/GuiAppInstance.h +++ b/Gui/GuiAppInstance.h @@ -132,7 +132,9 @@ GCC_DIAG_SUGGEST_OVERRIDE_ON Natron::StandardButtonEnum defaultButton, bool* stopAsking) OVERRIDE FINAL WARN_UNUSED_RETURN; virtual void loadProjectGui(bool isAutosave, boost::archive::xml_iarchive & archive) const OVERRIDE FINAL; + virtual void loadProjectGui(bool isAutosave, boost::archive::binary_iarchive & archive) const OVERRIDE FINAL; virtual void saveProjectGui(boost::archive::xml_oarchive & archive) OVERRIDE FINAL; + virtual void saveProjectGui(boost::archive::binary_oarchive & archive) OVERRIDE FINAL; virtual void notifyRenderStarted(const QString & sequenceName, int firstFrame, int lastFrame, int frameStep, bool canPause, diff --git a/Gui/ProjectGui.cpp b/Gui/ProjectGui.cpp index 0eddd2e6fd..b7e4bae636 100644 --- a/Gui/ProjectGui.cpp +++ b/Gui/ProjectGui.cpp @@ -261,10 +261,18 @@ AddFormatDialog::getFormat() const return Format(0, 0, w, h, name.toStdString(), pa); } +template +void +ProjectGui::save(boost::archive::xml_oarchive & archive) const; + +template +void +ProjectGui::save(boost::archive::binary_oarchive & archive) const; + // Version is handled in ProjectGuiSerialization -template<> +template void -ProjectGui::save(boost::archive::xml_oarchive & archive) const +ProjectGui::save(Archive & archive) const { ProjectGuiSerialization projectGuiSerializationObj; @@ -430,9 +438,15 @@ loadNodeGuiSerialization(Gui* gui, } } // loadNodeGuiSerialization -template<> +template void +ProjectGui::load(bool isAutosave, boost::archive::xml_iarchive & archive); + +template void +ProjectGui::load(bool isAutosave, boost::archive::binary_iarchive & archive); + +template void -ProjectGui::load(bool isAutosave, boost::archive::xml_iarchive & archive) +ProjectGui::load(bool isAutosave, Archive & archive) { ProjectGuiSerialization obj; diff --git a/Gui/ProjectGuiSerialization.h b/Gui/ProjectGuiSerialization.h index 5354c41ce5..ebe645455e 100644 --- a/Gui/ProjectGuiSerialization.h +++ b/Gui/ProjectGuiSerialization.h @@ -38,6 +38,8 @@ GCC_DIAG_OFF(unused-parameter) // /opt/local/include/boost/serialization/smart_cast.hpp:254:25: warning: unused parameter 'u' [-Wunused-parameter] #include #include +#include +#include #include #include // /usr/local/include/boost/serialization/shared_ptr.hpp:112:5: warning: unused typedef 'boost_static_assert_typedef_112' [-Wunused-local-typedef]