diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 4db3e0c05c1d6c3cb5981168dddd7e820cc112af..8dd1aeed8665a57fc3736911b92bb217bb62319b 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -85,8 +85,6 @@ SET(CPP_FILES
   input/xpipsaxhandler.cpp
   input/xtandemparamsaxhandler.cpp
   input/xtandemsaxhandler.cpp
-  output/masschroqml.cpp
-  output/proticdbml.cpp
   output/ods/comparbasesheet.cpp
   output/ods/comparspecificspectrasheet.cpp
   output/ods/comparspectrasheet.cpp
@@ -100,6 +98,9 @@ SET(CPP_FILES
   output/ods/samplesheet.cpp
   output/ods/simplesheet.cpp
   output/ods/spectrasheet.cpp
+  output/masschroqml.cpp
+  output/proticdbml.cpp
+  output/xpip.cpp
   utils/fastafilestore.cpp
   utils/identificationdatasourcestore.cpp
   utils/groupstore.cpp
diff --git a/src/core/identification_sources/identificationdatasource.cpp b/src/core/identification_sources/identificationdatasource.cpp
index c49f027c5d291828b0c40a2a35668d863836b693..ca74c0739bee724cf453c92354e21e73c093834c 100644
--- a/src/core/identification_sources/identificationdatasource.cpp
+++ b/src/core/identification_sources/identificationdatasource.cpp
@@ -37,6 +37,7 @@ IdentificationDataSource::IdentificationDataSource(const IdentificationDataSourc
 {
     _ms_run_sp = other._ms_run_sp;
     _resource_name = other._resource_name;
+    _xml_id = other._xml_id;
 }
 
 IdentificationDataSource::~IdentificationDataSource()
@@ -44,6 +45,12 @@ IdentificationDataSource::~IdentificationDataSource()
 
 }
 
+void IdentificationDataSource::setXmlId(const QString xmlid) {
+    _xml_id = xmlid;
+}
+const QString & IdentificationDataSource::getXmlId() const {
+    return _xml_id;
+}
 bool IdentificationDataSource::operator==(const IdentificationDataSource& other) const
 {
 
diff --git a/src/core/identification_sources/identificationdatasource.h b/src/core/identification_sources/identificationdatasource.h
index f99f8720b3007cc6f4894a7a0e911da4368751a7..7d23ac48213a1debe546e1840df116c31e611d62 100644
--- a/src/core/identification_sources/identificationdatasource.h
+++ b/src/core/identification_sources/identificationdatasource.h
@@ -43,6 +43,10 @@ public:
     IdentificationDataSource(const IdentificationDataSource& other);
     ~IdentificationDataSource();
     bool operator==(const IdentificationDataSource& other) const;
+    
+    
+    void setXmlId(const QString xmlid);
+    const QString & getXmlId() const;
 
     /** @brief URL or filename containing identification data
      * */
@@ -108,6 +112,7 @@ protected :
     IdentificationEngine _engine = IdentificationEngine::unknown;
 private :
     //static std::map<QString, pappso::MsRunIdSp> _map_msrunidsp;
+    QString _xml_id;
     QString _version;
     MsRunSp _ms_run_sp = nullptr;
     std::map<IdentificationEngineParam, QVariant> _params;
diff --git a/src/core/msrun.cpp b/src/core/msrun.cpp
index 390f27a0d3e8bda40eb76554e0830e1309020af2..206abdabf6e18ee3cce61ff77531135fda2ffac2 100644
--- a/src/core/msrun.cpp
+++ b/src/core/msrun.cpp
@@ -85,6 +85,11 @@ MzFormat MsRun::getMzFormat() const {
 void MsRun::setMsRunStatistics(MsRunStatistics param, const QVariant& value) {
     _param_stats.insert(std::pair<MsRunStatistics, QVariant>(param, value));
 }
+
+const std::map<MsRunStatistics, QVariant> & MsRun::getMsRunStatisticsMap() const {
+    return _param_stats;
+}
+
 const QVariant MsRun::getMsRunStatistics(MsRunStatistics param) const {
     try {
         return _param_stats.at(param);
diff --git a/src/core/msrun.h b/src/core/msrun.h
index ce038fc8de8be5466cf8851c16866513a5f6d7f8..fcba9f99856246140ca84b16eb2c583eb00d3a5c 100644
--- a/src/core/msrun.h
+++ b/src/core/msrun.h
@@ -67,6 +67,10 @@ public:
     /** \brief get MS run statistics
      */
     virtual const QVariant getMsRunStatistics(MsRunStatistics param) const;
+    
+    /** \brief get MS run statistics map
+     */
+    virtual const std::map<MsRunStatistics, QVariant> & getMsRunStatisticsMap() const;
 
 private :
     QString _xml_id;
diff --git a/src/gui/main.ui b/src/gui/main.ui
index 0fd652b6195de25760f97e3614efd725c8592ea3..b7bcf9354ab66da878df4a101f1c584b0f9c0366 100644
--- a/src/gui/main.ui
+++ b/src/gui/main.ui
@@ -74,6 +74,7 @@
     <addaction name="action_xtandem_run"/>
     <addaction name="actionLoad_results"/>
     <addaction name="actionLoad"/>
+    <addaction name="action_save_project"/>
     <addaction name="separator"/>
     <addaction name="menu_export_files"/>
     <addaction name="separator"/>
@@ -160,6 +161,11 @@
     <string>&amp;X!Tandem run</string>
    </property>
   </action>
+  <action name="action_save_project">
+   <property name="text">
+    <string>Save project</string>
+   </property>
+  </action>
  </widget>
  <resources>
   <include location="../xtpcpp.qrc"/>
@@ -405,6 +411,22 @@
     </hint>
    </hints>
   </connection>
+  <connection>
+   <sender>action_save_project</sender>
+   <signal>triggered()</signal>
+   <receiver>Main</receiver>
+   <slot>doActionSaveProject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>-1</x>
+     <y>-1</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>231</x>
+     <y>191</y>
+    </hint>
+   </hints>
+  </connection>
  </connections>
  <slots>
   <slot>selectXpipFile()</slot>
@@ -419,5 +441,6 @@
   <slot>doActionSpectralCountingMcq()</slot>
   <slot>doActionAbout()</slot>
   <slot>doActionTandemRun()</slot>
+  <slot>doActionSaveProject()</slot>
  </slots>
 </ui>
diff --git a/src/gui/mainwindow.cpp b/src/gui/mainwindow.cpp
index 73698b608134441cf04d6407433aacc28869493c..169b28a369912caa4851d2cdc934f5b0ba94ceb2 100644
--- a/src/gui/mainwindow.cpp
+++ b/src/gui/mainwindow.cpp
@@ -36,6 +36,7 @@
 #include "../utils/utils.h"
 #include "workerthread.h"
 #include "output/ods/comparspectrasheet.h"
+#include "output/xpip.h"
 
 class TsvNoSheetOutput: public TsvOutputStream {
 public :
@@ -71,6 +72,7 @@ MainWindow::MainWindow(QWidget *parent):
     ui->menu_export_files->setDisabled(true);
     ui->centralwidget->layout()->addWidget(_project_window);
     _project_window->hide();
+    ui->action_save_project->setDisabled(true);
     ui->default_display_widget->show();
     //_protein_list_window = new ProteinListWindow(this);
     //QDockWidget *dock = new QDockWidget(tr("Protein List"), this);
@@ -133,9 +135,9 @@ void MainWindow::doDisplayJobFinished(QString message) {
     qDebug() << "MainWindow::doDisplayJobFinished " <<  message;
     hideWaitingMessage();
     QMessageBox::information(this,
-                         tr("job finished"), message);
-    
-    
+                             tr("job finished"), message);
+
+
 }
 
 void MainWindow::doDisplayLoadingMessage(QString message) {
@@ -237,10 +239,11 @@ void MainWindow::doProjectReady(ProjectSp project_sp) {
 
     ui->menu_export_files->setDisabled(false);
     ui->menu_edit->setDisabled(false);
-    
-    
+
+
     _project_window->show();
     ui->default_display_widget->hide();
+    ui->action_save_project->setDisabled(false);
 
     qDebug() << "MainWindow::doProjectReady end";
 }
@@ -307,6 +310,36 @@ void MainWindow::selectXpipFile() {
 
 }
 
+
+void MainWindow::doActionSaveProject() {
+    try {
+        QMessageBox::warning(this,
+                             tr("Experimental feature"), "WARNING : project files export is not ready");
+        QSettings settings;
+        QString default_location = settings.value("path/xpipfile", "").toString();
+
+
+        QString filename = QFileDialog::getSaveFileName(this, tr("Save XPIP file"),
+                           QString("%1/untitled.xpip").arg(default_location),
+                           tr("XPIP (*.xpip)"));
+
+        if (filename.isEmpty()) {
+            return;
+        }
+
+        settings.setValue("path/xpipfile", QFileInfo(filename).absolutePath());
+
+        Xpip xpip_file(filename);
+        xpip_file.write(_project_sp);
+        xpip_file.close();
+
+    }
+    catch (pappso::PappsoException & error) {
+        viewError(tr("Error while writing XPIP file :\n%1").arg(error.qwhat()));
+    }
+
+}
+
 void MainWindow::doActionSpreadsheet() {
     qDebug() << "MainWindow::doActionSpreadsheet begin";
     try {
diff --git a/src/gui/mainwindow.h b/src/gui/mainwindow.h
index d0ab7b21ca5c6a7bc86824653d9bf76f44b4861e..402122239726e91ff8b37e8e947ea2ad249074c7 100644
--- a/src/gui/mainwindow.h
+++ b/src/gui/mainwindow.h
@@ -74,6 +74,7 @@ public slots:
     void doActionTandemRun();
     void doActionSpreadsheet();
     void doActionModifications();
+    void doActionSaveProject();
     void doDisplayLoadingMessage(QString message);
     void doDisplayLoadingMessagePercent(QString message, int value);
     void doDisplayJobFinished(QString message);
diff --git a/src/output/xpip.cpp b/src/output/xpip.cpp
index e9a836e2ec215676d8917fa098c4b65acc831f1c..662bf0c97bba24d997a39666f34856ab288378bf 100644
--- a/src/output/xpip.cpp
+++ b/src/output/xpip.cpp
@@ -28,6 +28,8 @@
 ******************************************************************************/
 
 #include "xpip.h"
+#include "../config.h"
+#include <QDateTime>
 #include <pappsomspp/pappsoexception.h>
 
 Xpip::Xpip(const QString & out_filename)
@@ -81,35 +83,36 @@ void Xpip::write(ProjectSp sp_project) {
     //xsi:schemaLocation="http://psidev.info/psi/pi/mzIdentML/1.1 http://www.psidev.info/files/mzIdentML1.1.0.xsd"
     _output_stream->writeAttribute("http://www.w3.org/2001/XMLSchema-instance","schemaLocation","http://pappso.inra.fr/xsd/xpip/4.0 http://pappso.inra.fr/xsd/xpip-4.0.xsd");
 
-    
-  //<information Data_Type="combi" match_number="6807"/>
-   // _output_stream->writeStartElement("information");
-  //  _output_stream->writeAttribute("Data_Type","combi");
-   // _output_stream->writeEndElement();
-    
-  //<filter_params pep_evalue="0.01" prot_evalue="-2.0" pep_number="2" filter_to_all="false" database_filter="/gorgone/pappso/jouy/raw/2013_Orbitrap/2013_07_04_Chanat/contaminants_standarts.fasta"/>
-    _output_stream->writeStartElement("filter_params");
-    _output_stream->writeAttribute("pep_evalue","combi");
-    _output_stream->writeAttribute("prot_evalue","combi");
-    _output_stream->writeAttribute("pep_number","combi");
-    _output_stream->writeAttribute("filter_to_all","combi");
-    _output_stream->writeEndElement();
-    
-  <identifications>
-    <identification>
-      <modifs_list_mass>
+
+    //<information Data_Type="combi" match_number="6807"/>
+    // _output_stream->writeStartElement("information");
+    //  _output_stream->writeAttribute("Data_Type","combi");
+    // _output_stream->writeEndElement();
+    writeDescription();
+
+    //<filter_params pep_evalue="0.01" prot_evalue="-2.0" pep_number="2" filter_to_all="false" database_filter="/gorgone/pappso/jouy/raw/2013_Orbitrap/2013_07_04_Chanat/contaminants_standarts.fasta"/>
+    //const AutomaticFilterParameters & getAutomaticFilterParameters() const;
+    writeFilterParameters(_sp_project.get()->getAutomaticFilterParameters());
+
+    writeMsrunList(_sp_project.get()->getMsRunStore());
+    writeIdentificationDataSourceList(_sp_project.get()->getIdentificationDataSourceStore());
+    /*
+        <identifications>
+        <identification>
+        <modifs_list_mass>
         <modifs_mass modvalue="-18.01056"/>
-        <modifs_mass modvalue="-17.02655"/>
-        <modifs_mass modvalue="15.99491"/>
-        <modifs_mass modvalue="42.01057"/>
-        <modifs_mass modvalue="42.01056"/>
-        <modifs_mass modvalue="57.02146"/>
-      </modifs_list_mass>
-      <samples>
-        <sample value="20120906_balliau_extract_1_A01_urnb-1"/>
-        <sample value="20120906_balliau_extract_1_A02_urzb-1"/>
-      </samples>
-    //<masschroq>
+                              <modifs_mass modvalue="-17.02655"/>
+                                      <modifs_mass modvalue="15.99491"/>
+                                              <modifs_mass modvalue="42.01057"/>
+                                                      <modifs_mass modvalue="42.01056"/>
+                                                              <modifs_mass modvalue="57.02146"/>
+                                                                      </modifs_list_mass>
+                                                                      <samples>
+                                                                      <sample value="20120906_balliau_extract_1_A01_urnb-1"/>
+                                                                              <sample value="20120906_balliau_extract_1_A02_urzb-1"/>
+                                                                                      </samples>
+                                                                                      //<masschroq>
+                                                                                      */
     // <rawdata><!-- time_values_dir="directory" to read retention time corrections-->
     _output_stream->writeStartElement("rawdata");
     _output_stream->writeComment("time_values_dir=\"directory\" to read retention time corrections");
@@ -135,16 +138,87 @@ void Xpip::write(ProjectSp sp_project) {
     _output_stream->writeEndElement();
 
 
-    writeGroups();
-    writeProteinList();
-    writePeptideList();
-    writeIsotopeLabelList();
-    writeAlignments();
-    writeQuantificationMethods();
-    _output_stream->writeStartElement("quantification");
-    writeQuantificationResults();
-    writeQuantificationTraces();
-    writeQuantify();
+    _output_stream->writeEndDocument();
+}
+void Xpip::writeDoubleAttribute(const QString & attribute, pappso::pappso_double value) {
+    _output_stream->writeAttribute(attribute,QString::number(value, 'f', 10));
+
+}
+void Xpip::writeBooleanAttribute(const QString & attribute, bool value) {
+    if (value) {
+        _output_stream->writeAttribute(attribute,"true");
+    }
+    else {
+        _output_stream->writeAttribute(attribute,"false");
+    }
+
+}
+void Xpip::writeFilterParameters(const AutomaticFilterParameters & filters) {
+
+    _output_stream->writeStartElement("filter_params");
+    writeDoubleAttribute("pep_evalue",filters.getFilterPeptideEvalue());
+    writeDoubleAttribute("prot_evalue",filters.getFilterProteinEvalue());
+    _output_stream->writeAttribute("pep_number",QString("%1").arg(filters.getFilterMinimumPeptidePerMatch()));
+    writeBooleanAttribute("cross_sample",filters.getFilterCrossSamplePeptideNumber());
+    _output_stream->writeEndElement();
+}
+
+void Xpip::writeDescription() {
+
+    _output_stream->writeStartElement("description");
+    _output_stream->writeAttribute("version",XTPCPP_VERSION);
+    writeBooleanAttribute("combine",_sp_project.get()->isCombineMode());
+    _output_stream->writeAttribute("date",QDateTime::currentDateTime().toString( Qt::ISODate));
+    _output_stream->writeEndElement();
+}
+
+
+void Xpip::writeIdentificationDataSourceList(const IdentificationDataSourceStore & ident_store) {
+
+    _output_stream->writeStartElement("identification_source_list");
+    for (const IdentificationDataSourceSp ident_source_sp : ident_store.getIdentificationDataSourceList()) {
+        _output_stream->writeStartElement("identification_source");
+        _output_stream->writeAttribute("id",ident_source_sp.get()->getXmlId());
+        _output_stream->writeAttribute("msrun_id",ident_source_sp.get()->getMsRunSp().get()->getXmlId());
+        //_output_stream->writeAttribute("format",QString("%1").arg(static_cast<std::int8_t>(ident_source_sp.get()->getIdentFormat())));
+        _output_stream->writeAttribute("path",ident_source_sp.get()->getResourceName());
+
+        _output_stream->writeAttribute("engine",QString("%1").arg(static_cast<std::int8_t>(ident_source_sp.get()->getIdentificationEngine())));
+        _output_stream->writeAttribute("version",ident_source_sp.get()->getIdentificationEngineVersion());
+
+        _output_stream->writeEndElement();
+    }
     _output_stream->writeEndElement();
+
 }
 
+void Xpip::writeMsrunList(const MsRunStore & msrun_store) {
+
+    _output_stream->writeStartElement("msrun_list");
+    for (const MsRunSp msrun_sp : msrun_store.getMsRunList()) {
+        _output_stream->writeStartElement("msrun");
+        _output_stream->writeAttribute("id",msrun_sp.get()->getXmlId());
+        _output_stream->writeAttribute("name",msrun_sp.get()->getSampleName());
+        _output_stream->writeAttribute("format",QString("%1").arg(static_cast<std::int8_t>(msrun_sp.get()->getMzFormat())));
+        QFileInfo mz_info(msrun_sp.get()->getFilename());
+        if (mz_info.exists()) {
+            _output_stream->writeAttribute("path",msrun_sp.get()->getFilename());
+        }
+        else {
+            _output_stream->writeAttribute("path",mz_info.completeBaseName());
+        }
+        const std::map<MsRunStatistics, QVariant> stats = msrun_sp.get()->getMsRunStatisticsMap();
+        if (stats.size() > 0) {
+            _output_stream->writeStartElement("stats");
+            for (const std::pair<MsRunStatistics, QVariant> stat_pair: stats) {
+                _output_stream->writeStartElement("stat");
+                _output_stream->writeAttribute("key",QString("%1").arg(static_cast<std::int8_t>(stat_pair.first)));
+                _output_stream->writeAttribute("value",stat_pair.second.toString());
+                _output_stream->writeEndElement();
+            }
+            _output_stream->writeEndElement();
+        }
+        _output_stream->writeEndElement();
+    }
+    _output_stream->writeEndElement();
+}
diff --git a/src/output/xpip.h b/src/output/xpip.h
index 2e2f190c55a9c5232a55f328295c6516ce8c0717..a6636dc7c3d47299792a0051106cc68397594df1 100644
--- a/src/output/xpip.h
+++ b/src/output/xpip.h
@@ -37,13 +37,23 @@
 
 class Xpip
 {
-        public:
+public:
     Xpip(const QString & out_filename);
     ~Xpip();
 
     void write(ProjectSp sp_project);
     void close();
-  
+
+private :
+    void writeFilterParameters(const AutomaticFilterParameters & filters);
+    void writeDescription();
+
+    void writeMsrunList(const MsRunStore & msrun_store);
+    void writeIdentificationDataSourceList(const IdentificationDataSourceStore & ident_store);
+    void writeDoubleAttribute(const QString & attribute, pappso::pappso_double value);
+    void writeBooleanAttribute(const QString & attribute, bool value);
+
+
 private :
     QFile * _output_file;
     QXmlStreamWriter * _output_stream;
diff --git a/src/utils/identificationdatasourcestore.cpp b/src/utils/identificationdatasourcestore.cpp
index c675520bd0bc6050d87d84d4dc9b89bc748ffce6..c01d92ac10a3b1c7911c005b083ac585bc21aed2 100644
--- a/src/utils/identificationdatasourcestore.cpp
+++ b/src/utils/identificationdatasourcestore.cpp
@@ -32,6 +32,7 @@
 #include "../core/identification_sources/identificationxtandemfile.h"
 #include "../core/identification_sources/identificationpwizfile.h"
 #include <pappsomspp/pappsoexception.h>
+#include <pappsomspp/utils.h>
 
 IdentificationDataSourceStore::IdentificationDataSourceStore()
 {
@@ -68,6 +69,7 @@ IdentificationDataSourceSp IdentificationDataSourceStore::getInstance(const QStr
         if (p_identfile == nullptr) {
             throw pappso::PappsoException(QObject::tr("Identification resource %1 not recognized (null pointer)").arg(location));
         }
+        p_identfile.get()->setXmlId(QString("ident%1").arg(pappso::Utils::getLexicalOrderedString(_map_identification_data_sources.size())));
         
         _map_identification_data_sources.insert(std::pair< QString, IdentificationDataSourceSp >(location, p_identfile));
         _map_identification_data_sources.insert(std::pair< QString, IdentificationDataSourceSp >(location_file.absoluteFilePath(), p_identfile));
diff --git a/src/utils/types.h b/src/utils/types.h
index 619d3b7cefc12ec649af280fb49d8453df73616e..68c5507b7217b4bc81cde01515ffbea26ea61903 100644
--- a/src/utils/types.h
+++ b/src/utils/types.h
@@ -43,11 +43,11 @@ enum class ExternalDatabase: std::int8_t {
 /** \def IdentificationEngine identification engine
  *
  */
-enum class IdentificationEngine {
-    unknown, ///< X!Tandem
-    XTandem, ///< X!Tandem
-    mascot, ///< Mascot
-    peptider ///< peptider
+enum class IdentificationEngine: std::int8_t {
+    unknown=0, ///< X!Tandem
+    XTandem=1, ///< X!Tandem
+    mascot=2, ///< Mascot
+    peptider=3 ///< peptider
 };
 
 /** \def PeptideMatchParam peptide match specific parameters
@@ -102,11 +102,11 @@ enum class GroupingType {
  *
  */
 
-enum class MzFormat {
-    unknown, ///< unknown format
-    mzML, ///< mzML
-    mzXML, ///< mzXML
-    MGF, ///< Mascot format
+enum class MzFormat: std::int8_t {
+    unknown=0, ///< unknown format
+    mzML=1, ///< mzML
+    mzXML=2, ///< mzXML
+    MGF=3, ///< Mascot format
 };
 
 /** \def ValidationState