diff --git a/common/lib_tree_model_adapter.cpp b/common/lib_tree_model_adapter.cpp
index 4453d4b15a..ac031f33cd 100644
--- a/common/lib_tree_model_adapter.cpp
+++ b/common/lib_tree_model_adapter.cpp
@@ -29,7 +29,6 @@
 #include <wx/wupdlock.h>
 
 
-#define LIST_COLUMN_WIDTH_KEY wxT( "SelectorColumnWidth" )
 #define PINNED_ITEMS_KEY      wxT( "PinnedItems" )
 
 static const int kDataViewIndent = 20;
@@ -91,9 +90,12 @@ LIB_TREE_MODEL_ADAPTER::LIB_TREE_MODEL_ADAPTER( EDA_BASE_FRAME* aParent ) :
     auto cfg = Kiface().KifaceSettings();
     m_colWidths[PART_COL] = cfg->m_LibTree.column_width;
 
+    // TODO(JE) PROJECT
+#if 0
     // Read the pinned entries from the project config
     m_parent->Kiway().Prj().ConfigLoad( Kiface().KifaceSearch(), m_parent->GetName(),
                                         GetProjectFileParameters() );
+#endif
 }
 
 
@@ -136,8 +138,11 @@ void LIB_TREE_MODEL_ADAPTER::SavePinnedItems()
             m_pinnedLibs.push_back( child->m_LibId.GetLibNickname() );
     }
 
+    // TODO(JE) PROJECT
+#if 0
     m_parent->Kiway().Prj().ConfigSave( Kiface().KifaceSearch(), m_parent->GetName(),
                                         GetProjectFileParameters() );
+#endif
 }
 
 
diff --git a/common/project.cpp b/common/project.cpp
index 2f3677fcb0..ab0f6e50d4 100644
--- a/common/project.cpp
+++ b/common/project.cpp
@@ -186,20 +186,8 @@ const wxString PROJECT::GetSheetName( const KIID& aSheetID )
 {
     if( m_sheetNames.empty() )
     {
-        std::unique_ptr<wxConfigBase> config( configCreate( SEARCH_STACK(), GROUP_SHEET_NAMES ) );
-
-        config->SetPath( GROUP_SHEET_NAMES );
-
-        int index = 1;
-        wxString entry;
-
-        while( config->Read( wxString::Format( "%d", index++ ), &entry ) )
-        {
-            wxArrayString tokens = wxSplit( entry, ':' );
-
-            if( tokens.size() == 2 )
-                m_sheetNames[ KIID( tokens[0] ) ] = tokens[1];
-        }
+        for( auto pair : GetProjectFile().GetSheets() )
+            m_sheetNames[pair.first] = pair.second;
     }
 
     if( m_sheetNames.count( aSheetID ) )
diff --git a/common/settings/json_settings.cpp b/common/settings/json_settings.cpp
index 5688fe49c3..5b088e6c9a 100644
--- a/common/settings/json_settings.cpp
+++ b/common/settings/json_settings.cpp
@@ -98,6 +98,18 @@ bool JSON_SETTINGS::LoadFromFile( const std::string& aDirectory )
     LOCALE_IO locale;
 
     auto migrateFromLegacy = [&] ( wxFileName& aPath ) {
+        // Backup and restore during migration so that the original can be mutated if convenient
+        wxFileName temp;
+        temp.AssignTempFileName( aPath.GetFullPath() );
+
+        bool backed_up = true;
+
+        if( !wxCopyFile( aPath.GetFullPath(), temp.GetFullPath() ) )
+        {
+            wxLogTrace( traceSettings, "%s: could not create temp file for migration", m_filename );
+            backed_up = false;
+        }
+
         wxConfigBase::DontCreateOnDemand();
         auto cfg = std::make_unique<wxFileConfig>( wxT( "" ), wxT( "" ), aPath.GetFullPath() );
 
@@ -113,6 +125,13 @@ bool JSON_SETTINGS::LoadFromFile( const std::string& aDirectory )
             wxLogTrace( traceSettings, "%s: migrated from legacy format", m_filename );
         }
 
+        if( backed_up )
+        {
+            cfg.reset();
+            wxCopyFile( temp.GetFullPath(), aPath.GetFullPath() );
+            wxRemoveFile( temp.GetFullPath() );
+        }
+
         // Either way, we want to clean up the old file afterwards
         legacy_migrated = true;
     };
diff --git a/common/settings/project_file.cpp b/common/settings/project_file.cpp
index 75c8bde1a4..d30b9161b1 100644
--- a/common/settings/project_file.cpp
+++ b/common/settings/project_file.cpp
@@ -42,6 +42,15 @@ PROJECT_FILE::PROJECT_FILE( const std::string& aFullPath ) :
     m_params.emplace_back( new PARAM_LIST<FILE_INFO_PAIR>( "sheets", &m_sheets, {} ) );
 
     m_params.emplace_back( new PARAM_LIST<FILE_INFO_PAIR>( "boards", &m_boards, {} ) );
+
+    m_params.emplace_back(
+            new PARAM_LIST<wxString>( "libraries.pinned_symbol_libs", &m_PinnedSymbolLibs, {} ) );
+
+    m_params.emplace_back( new PARAM_LIST<wxString>(
+            "libraries.pinned_footprint_libs", &m_PinnedFootprintLibs, {} ) );
+
+    m_params.emplace_back(
+            new PARAM_PATH_LIST( "cvpcb.equivalence_files", &m_EquivalenceFiles, {} ) );
 }
 
 
@@ -51,9 +60,67 @@ bool PROJECT_FILE::MigrateFromLegacy( wxConfigBase* aLegacyFile )
     wxString str;
     long     index = 0;
 
+    std::set<wxString> group_blacklist;
+
     // Legacy files don't store board info; they assume board matches project name
     // We will leave m_boards empty here so it can be populated with other code
 
+    // First handle migration of data that will be stored locally in this object
+
+    auto loadPinnedLibs =
+            [&]( const std::string& aDest )
+            {
+                int      libIndex = 1;
+                wxString libKey   = wxT( "PinnedItems" );
+                libKey << libIndex;
+
+                nlohmann::json libs = nlohmann::json::array();
+
+                while( aLegacyFile->Read( libKey, &str ) )
+                {
+                    libs.push_back( str );
+
+                    aLegacyFile->DeleteEntry( libKey, true );
+
+                    libKey = wxT( "PinnedItems" );
+                    libKey << ++libIndex;
+                }
+
+                ( *this )[PointerFromString( aDest )] = libs;
+            };
+
+    aLegacyFile->SetPath( wxT( "/LibeditFrame" ) );
+    loadPinnedLibs( "libraries.pinned_symbol_libs" );
+
+    aLegacyFile->SetPath( wxT( "/ModEditFrame" ) );
+    loadPinnedLibs( "libraries.pinned_footprint_libs" );
+
+    aLegacyFile->SetPath( wxT( "/cvpcb/equfiles" ) );
+
+    {
+        int      eqIdx = 1;
+        wxString eqKey = wxT( "EquName" );
+        eqKey << eqIdx;
+
+        nlohmann::json eqs = nlohmann::json::array();
+
+        while( aLegacyFile->Read( eqKey, &str ) )
+        {
+            eqs.push_back( str );
+
+            eqKey = wxT( "EquName" );
+            eqKey << ++eqIdx;
+        }
+
+        ( *this )[PointerFromString( "cvpcb.equivalence_files" )] = eqs;
+    }
+
+    // No other cvpcb params are currently used
+    group_blacklist.insert( "/cvpcb" );
+
+    // Next load sheet names and put all other legacy data in the legacy dict
+    aLegacyFile->SetPath( "/" );
+
     auto loadSheetNames =
             [&]() -> bool
             {
@@ -63,7 +130,7 @@ bool PROJECT_FILE::MigrateFromLegacy( wxConfigBase* aLegacyFile )
 
                 wxLogTrace( traceSettings, "Migrating sheet names" );
 
-                aLegacyFile->SetPath( GROUP_SHEET_NAMES );
+                aLegacyFile->SetPath( wxT( "/sheetnames" ) );
 
                 while( aLegacyFile->Read( wxString::Format( "%d", sheet++ ), &entry ) )
                 {
@@ -76,7 +143,7 @@ bool PROJECT_FILE::MigrateFromLegacy( wxConfigBase* aLegacyFile )
                     }
                 }
 
-              ( *this )[PointerFromString( "sheets" )] = arr;
+                ( *this )[PointerFromString( "sheets" )] = arr;
 
                 aLegacyFile->SetPath( "/" );
 
@@ -86,13 +153,7 @@ bool PROJECT_FILE::MigrateFromLegacy( wxConfigBase* aLegacyFile )
 
     std::vector<wxString> groups;
 
-    while( aLegacyFile->GetNextGroup( str, index ) )
-    {
-        if( str == wxString( GROUP_SHEET_NAMES ).Mid( 1 ) )
-            ret |= loadSheetNames();
-        else
-            groups.emplace_back( "/" + str );
-    }
+    groups.emplace_back( "" );
 
     auto loadLegacyPairs =
             [&]( const std::string& aGroup ) -> bool
@@ -127,18 +188,31 @@ bool PROJECT_FILE::MigrateFromLegacy( wxConfigBase* aLegacyFile )
                 return success;
             };
 
-    ret &= loadLegacyPairs( "" );
-
     for( size_t i = 0; i < groups.size(); i++ )
     {
         aLegacyFile->SetPath( groups[i] );
 
+        if( groups[i] == wxT( "/sheetnames" ) )
+        {
+            ret |= loadSheetNames();
+            continue;
+        }
+
+        aLegacyFile->DeleteEntry( wxT( "last_client" ), true );
+        aLegacyFile->DeleteEntry( wxT( "update" ), true );
+        aLegacyFile->DeleteEntry( wxT( "version" ), true );
+
         ret &= loadLegacyPairs( groups[i].ToStdString() );
 
         index = 0;
 
         while( aLegacyFile->GetNextGroup( str, index ) )
-            groups.emplace_back( groups[i] + "/" + str );
+        {
+            wxString group = groups[i] + "/" + str;
+
+            if( !group_blacklist.count( group ) )
+                groups.emplace_back( group );
+        }
 
         aLegacyFile->SetPath( "/" );
     }
diff --git a/cvpcb/CMakeLists.txt b/cvpcb/CMakeLists.txt
index 902cd04748..920891ebfa 100644
--- a/cvpcb/CMakeLists.txt
+++ b/cvpcb/CMakeLists.txt
@@ -28,7 +28,6 @@ set( CVPCB_SRCS
     ${CMAKE_SOURCE_DIR}/common/base_units.cpp
     ${CMAKE_SOURCE_DIR}/pcbnew/board_stackup_manager/stackup_predefined_prms.cpp
     auto_associate.cpp
-    cfg.cpp
     components_listbox.cpp
     display_footprints_frame.cpp
     footprints_listbox.cpp
diff --git a/cvpcb/auto_associate.cpp b/cvpcb/auto_associate.cpp
index f06790ccb7..7f6bf9fba4 100644
--- a/cvpcb/auto_associate.cpp
+++ b/cvpcb/auto_associate.cpp
@@ -39,6 +39,7 @@
 #include <cvpcb_association.h>
 #include <cvpcb_mainframe.h>
 #include <listboxes.h>
+#include <settings/project_file.h>
 
 #define QUOTE   '\''
 
@@ -83,13 +84,14 @@ int CVPCB_MAINFRAME::buildEquivalenceList( FOOTPRINT_EQUIVALENCE_LIST& aList, wx
     wxFileName  fn;
     wxString    tmp, error_msg;
 
-    SEARCH_STACK& search = Kiface().KifaceSearch();
+    SEARCH_STACK& search  = Kiface().KifaceSearch();
+    PROJECT_FILE& project = Prj().GetProjectFile();
 
     // Find equivalences in all available files, and populates the
     // equiv_List with all equivalences found in .equ files
-    for( unsigned ii = 0; ii < m_EquFilesNames.GetCount(); ii++ )
+    for( const auto& equfile : project.m_EquivalenceFiles )
     {
-        fn =  wxExpandEnvVars( m_EquFilesNames[ii] );
+        fn =  wxExpandEnvVars( equfile );
 
         tmp = search.FindValidPath( fn.GetFullPath() );
 
diff --git a/cvpcb/cfg.cpp b/cvpcb/cfg.cpp
deleted file mode 100644
index 9c9fbe11a4..0000000000
--- a/cvpcb/cfg.cpp
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * This program source code file is part of KiCad, a free EDA CAD application.
- *
- * Copyright (C) 2015 Jean-Pierre Charras, jp.charras at wanadoo.fr
- * Copyright (C) 1992-2011 KiCad Developers, see AUTHORS.txt for contributors.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, you may find one here:
- * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
- * or you may search the http://www.gnu.org website for the version 2 license,
- * or you may write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
- */
-
-/**
- * @file cfg.cpp
- */
-
-#include <config_params.h>
-#include <kiface_i.h>
-#include <project.h>
-
-#include <cvpcb_mainframe.h>
-
-
-std::vector<PARAM_CFG*>& CVPCB_MAINFRAME::GetProjectFileParameters()
-{
-    if( !m_projectFileParams.empty() )
-        return m_projectFileParams;
-
-    m_projectFileParams.push_back( new PARAM_CFG( GROUP_PCB_LIBS, PARAM_COMMAND_ERASE ) );
-
-    m_projectFileParams.push_back( new PARAM_CFG_LIBNAME_LIST(
-        wxT( "EquName" ), &m_EquFilesNames, GROUP_CVP_EQU ) );
-
-    return m_projectFileParams;
-}
-
-
-void CVPCB_MAINFRAME::LoadProjectFile()
-{
-    PROJECT&    prj = Prj();
-
-    m_ModuleLibNames.Clear();
-    m_EquFilesNames.Clear();
-
-    prj.ConfigLoad( Kiface().KifaceSearch(), GROUP_CVP, GetProjectFileParameters() );
-}
-
-
-void CVPCB_MAINFRAME::SaveProjectFile()
-{
-    PROJECT&    prj = Prj();
-    wxFileName  fn = prj.GetProjectFullName();
-
-    if( !IsWritable( fn ) )
-    {
-        wxMessageBox( _( "Project file \"%s\" is not writable" ), fn.GetFullPath() );
-        return;
-    }
-
-    wxString pro_name = fn.GetFullPath();
-
-    prj.ConfigSave( Kiface().KifaceSearch(), GROUP_CVP, GetProjectFileParameters(), pro_name );
-}
-
diff --git a/cvpcb/cvpcb_mainframe.h b/cvpcb/cvpcb_mainframe.h
index 03132ee13f..a4cf0b3d26 100644
--- a/cvpcb/cvpcb_mainframe.h
+++ b/cvpcb/cvpcb_mainframe.h
@@ -75,9 +75,6 @@ class CVPCB_MAINFRAME : public KIWAY_PLAYER
     wxButton*                 m_saveAndContinue;
 
 public:
-    wxArrayString             m_ModuleLibNames;
-    wxArrayString             m_EquFilesNames;
-
     FOOTPRINT_LIST*           m_FootprintsList;
 
 protected:
@@ -85,7 +82,6 @@ protected:
     bool                      m_skipComponentSelect;   // skip component selection event during
                                                        // automatic selection/deletion of
                                                        // associations
-    std::vector<PARAM_CFG*>   m_projectFileParams;
 
     bool                      m_initialized;
 
@@ -283,18 +279,6 @@ public:
      */
     int ReadSchematicNetlist( const std::string& aNetlist );
 
-    /**
-     * Function LoadProjectFile
-     * reads the CvPcb configuration parameter from the project (.pro) file \a aFileName
-     */
-    void LoadProjectFile();
-
-    /**
-     * Function SaveProjectFile
-     * Saves the CvPcb configuration parameter from the project (.pro) file \a aFileName
-     */
-    void SaveProjectFile();
-
     void LoadSettings( APP_SETTINGS_BASE* aCfg ) override;
 
     void SaveSettings( APP_SETTINGS_BASE* aCfg ) override;
@@ -326,21 +310,6 @@ public:
      */
     bool LoadFootprintFiles();
 
-    /**
-     * Function GetProjectFileParameters
-     * return project file parameter list for CvPcb.
-     * <p>
-     * Populate the project file parameter array specific to CvPcb if it hasn't
-     * already been populated and return a reference to the array to the caller.
-     * Creating the parameter list at run time has the advantage of being able
-     * to define local variables.  The old method of statically building the array
-     * at compile time requiring global variable definitions.
-     * </p>
-     *
-     * @return reference to a std::vector<PARAM_CFG*> contain the project settings for CvPcb.
-     */
-    std::vector<PARAM_CFG*>& GetProjectFileParameters( void );
-
     /**
      * Function SendMessageToEESCHEMA
      * Send a remote command to Eeschema via a socket,
diff --git a/cvpcb/dialogs/dialog_config_equfiles.cpp b/cvpcb/dialogs/dialog_config_equfiles.cpp
index 273a6034ac..a8d42a0733 100644
--- a/cvpcb/dialogs/dialog_config_equfiles.cpp
+++ b/cvpcb/dialogs/dialog_config_equfiles.cpp
@@ -35,6 +35,8 @@
 
 #include <cvpcb_mainframe.h>
 #include <dialog_config_equfiles.h>
+#include <settings/project_file.h>
+#include <settings/settings_manager.h>
 #include <wildcards_and_files_ext.h>
 
 
@@ -59,8 +61,17 @@ void DIALOG_CONFIG_EQUFILES::Init()
     m_sdbSizerOK->SetDefault();
     m_ListChanged = false;
 
-    if( !m_Parent->m_EquFilesNames.IsEmpty() )
-        m_ListEquiv->InsertItems( m_Parent->m_EquFilesNames, 0 );
+    PROJECT_FILE& project = Prj().GetProjectFile();
+
+    if( !project.m_EquivalenceFiles.empty() )
+    {
+        wxArrayString arr;
+
+        for( const auto& entry : project.m_EquivalenceFiles )
+            arr.Add( entry );
+
+        m_ListEquiv->InsertItems( arr, 0 );
+    }
 
     if( getEnvVarCount() < 2 )
         m_gridEnvVars->AppendRows(2 - getEnvVarCount() );
@@ -113,14 +124,16 @@ void DIALOG_CONFIG_EQUFILES::OnOkClick( wxCommandEvent& event )
     // Save new equ file list if the files list was modified
     if( m_ListChanged  )
     {
+        PROJECT_FILE& project = Prj().GetProjectFile();
+
         // Recreate equ list
-        m_Parent->m_EquFilesNames.Clear();
+        project.m_EquivalenceFiles.clear();
 
         for( unsigned ii = 0; ii < m_ListEquiv->GetCount(); ii++ )
-            m_Parent->m_EquFilesNames.Add( m_ListEquiv->GetString( ii ) );
+            project.m_EquivalenceFiles.emplace_back( m_ListEquiv->GetString( ii ) );
 
         wxCommandEvent evt( ID_SAVE_PROJECT );
-        m_Parent->SaveProjectFile();
+        Pgm().GetSettingsManager().SaveProject();
     }
 
     EndModal( wxID_OK );
diff --git a/cvpcb/readwrite_dlgs.cpp b/cvpcb/readwrite_dlgs.cpp
index f6e3f3e2d6..fc7dc229e1 100644
--- a/cvpcb/readwrite_dlgs.cpp
+++ b/cvpcb/readwrite_dlgs.cpp
@@ -88,8 +88,6 @@ bool CVPCB_MAINFRAME::ReadNetListAndFpFiles( const std::string& aNetlist )
     if( m_compListBox == NULL )
         return false;
 
-    LoadProjectFile();
-
     wxSafeYield();
 
     LoadFootprintFiles();
diff --git a/eeschema/files-io.cpp b/eeschema/files-io.cpp
index d7b1602ce1..0ce6ecea22 100644
--- a/eeschema/files-io.cpp
+++ b/eeschema/files-io.cpp
@@ -54,6 +54,7 @@
 #include <connection_graph.h>
 #include <tool/actions.h>
 #include <tools/sch_editor_control.h>
+#include <settings/project_file.h>
 #include <settings/settings_manager.h>
 #include <netlist.h>
 #include <widgets/infobar.h>
@@ -716,22 +717,16 @@ bool SCH_EDIT_FRAME::SaveProject()
         UpdateFileHistory( Schematic().RootScreen()->GetFileName() );
 
     // Save the sheet name map to the project file
-    wxString      configFile = Prj().GetProjectFullName();
-    wxConfigBase* config = new wxFileConfig( wxEmptyString, wxEmptyString, configFile );
-    int           index = 1;
-
-    config->DeleteGroup( GROUP_SHEET_NAMES );
-    config->SetPath( GROUP_SHEET_NAMES );
+    std::vector<FILE_INFO_PAIR>& sheets = Prj().GetProjectFile().GetSheets();
+    sheets.clear();
 
     for( SCH_SHEET_PATH& sheetPath : Schematic().GetSheets() )
     {
         SCH_SHEET* sheet = sheetPath.Last();
-        config->Write( wxString::Format( "%d", index++ ),
-                       wxString::Format( "%s:%s", sheet->m_Uuid.AsString(), sheet->GetName() ) );
+        sheets.emplace_back( std::make_pair( sheet->m_Uuid, sheet->GetName() ) );
     }
 
-    config->Flush();
-    delete config;
+    Pgm().GetSettingsManager().SaveProject();
 
     UpdateTitle();
 
diff --git a/include/config_params.h b/include/config_params.h
index ebc5a92b26..57a5cc95fe 100644
--- a/include/config_params.h
+++ b/include/config_params.h
@@ -46,10 +46,6 @@ using KIGFX::COLOR4D;
                                                         /// (Now in fp lib tables)
 #define GROUP_SCH_LIBS      wxT( "/eeschema/libraries" )    /// library list section
 
-#define GROUP_CVP           wxT( "/cvpcb" )
-#define GROUP_CVP_EQU       wxT( "/cvpcb/equfiles" )
-
-#define GROUP_SHEET_NAMES   wxT( "/sheetnames" )
 #define GROUP_TEXT_VARS     wxT( "/text_variables" )
 
 #define CONFIG_VERSION      1
diff --git a/include/project.h b/include/project.h
index 21b9b5e5fb..96750d3171 100644
--- a/include/project.h
+++ b/include/project.h
@@ -126,6 +126,12 @@ public:
      */
     VTBL_ENTRY const wxString SymbolLibTableName() const;
 
+    VTBL_ENTRY PROJECT_FILE& GetProjectFile() const
+    {
+        wxASSERT( m_projectFile );
+        return *m_projectFile;
+    }
+
     /**
      * Function ConfigSave
      * saves the current "project" parameters into the wxConfigBase* derivative.
diff --git a/include/settings/parameters.h b/include/settings/parameters.h
index 5bd4d92aae..91090ca07a 100644
--- a/include/settings/parameters.h
+++ b/include/settings/parameters.h
@@ -422,12 +422,85 @@ public:
         return false;
     }
 
-private:
+protected:
     std::vector<Type>* m_ptr;
 
     std::vector<Type> m_default;
 };
 
+/**
+ * Represents a list of strings holding directory paths.
+ * Normalizes paths to unix directory separator style in the file.
+ */
+class PARAM_PATH_LIST : public PARAM_LIST<wxString>
+{
+public:
+    PARAM_PATH_LIST( const std::string& aJsonPath, std::vector<wxString>* aPtr,
+                     std::initializer_list<wxString> aDefault, bool aReadOnly = false ) :
+            PARAM_LIST( aJsonPath, aPtr, aDefault, aReadOnly )
+    { }
+
+    PARAM_PATH_LIST( const std::string& aJsonPath, std::vector<wxString>* aPtr,
+                     std::vector<wxString> aDefault, bool aReadOnly = false ) :
+            PARAM_LIST( aJsonPath, aPtr, aDefault, aReadOnly )
+    { }
+
+    void Load( JSON_SETTINGS* aSettings ) const override
+    {
+        if( m_readOnly )
+            return;
+
+        PARAM_LIST::Load( aSettings );
+
+        for( size_t i = 0; i < m_ptr->size(); i++ )
+            ( *m_ptr )[i] = fromFileFormat( ( *m_ptr )[i] );
+    }
+
+    void Store( JSON_SETTINGS* aSettings) const override
+    {
+        nlohmann::json js = nlohmann::json::array();
+
+        for( const auto& el : *m_ptr )
+            js.push_back( toFileFormat( el ) );
+
+        aSettings->Set<nlohmann::json>( m_path, js );
+    }
+
+    bool MatchesFile( JSON_SETTINGS* aSettings ) const override
+    {
+        if( OPT<nlohmann::json> js = aSettings->GetJson( m_path ) )
+        {
+            if( js->is_array() )
+            {
+                std::vector<wxString> val;
+
+                for( const auto& el : js->items() )
+                    val.emplace_back( fromFileFormat( el.value().get<wxString>() ) );
+
+                return val == *m_ptr;
+            }
+        }
+
+        return false;
+    }
+
+private:
+    wxString toFileFormat( const wxString& aString ) const
+    {
+        wxString ret = aString;
+        ret.Replace( wxT( "\\" ), wxT( "/" ) );
+        return ret;
+    }
+
+    wxString fromFileFormat( const wxString& aString ) const
+    {
+        wxString ret = aString;
+#ifdef __WINDOWS__
+        ret.Replace( wxT( "/" ), wxT( "\\" ) );
+#endif
+        return ret;
+    }
+};
 
 /**
  * Represents a map of <std::string, Value>.  The key parameter has to be a string in JSON.
diff --git a/include/settings/project_file.h b/include/settings/project_file.h
index 70dbc1ffd0..7587b18fa3 100644
--- a/include/settings/project_file.h
+++ b/include/settings/project_file.h
@@ -51,6 +51,16 @@ public:
 
     virtual bool MigrateFromLegacy( wxConfigBase* aLegacyFile ) override;
 
+    std::vector<FILE_INFO_PAIR>& GetSheets()
+    {
+        return m_sheets;
+    }
+
+    std::vector<FILE_INFO_PAIR>& GetBoards()
+    {
+        return m_boards;
+    }
+
 protected:
     wxString getFileExt() const override;
 
@@ -63,6 +73,28 @@ private:
 
     /// A list of board files in this project
     std::vector<FILE_INFO_PAIR> m_boards;
+
+    /**
+     * Below are project-level settings that have not been moved to a dedicated file
+     */
+public:
+
+    /**
+     * Shared params, used by more than one application
+     */
+
+    /// The list of pinned symbol libraries
+    std::vector<wxString> m_PinnedSymbolLibs;
+
+    /// The list of pinned footprint libraries
+    std::vector<wxString> m_PinnedFootprintLibs;
+
+    /**
+     * CvPcb params
+     */
+
+    /// List of equivalence (equ) files used in the project
+    std::vector<wxString> m_EquivalenceFiles;
 };
 
 // Specializations to allow directly reading/writing FILE_INFO_PAIRs from JSON
diff --git a/kicad/tools/kicad_manager_control.cpp b/kicad/tools/kicad_manager_control.cpp
index 3426a0e4d3..f87a123113 100644
--- a/kicad/tools/kicad_manager_control.cpp
+++ b/kicad/tools/kicad_manager_control.cpp
@@ -292,8 +292,8 @@ int KICAD_MANAGER_CONTROL::NewFromTemplate( const TOOL_EVENT& aEvent )
 
 int KICAD_MANAGER_CONTROL::OpenProject( const TOOL_EVENT& aEvent )
 {
-    wxString wildcard = ProjectFileWildcard() + "|" + LegacyProjectFileWildcard() + "|"
-                        + AllProjectFilesWildcard();
+    wxString wildcard = AllProjectFilesWildcard() + "|" + ProjectFileWildcard() + "|"
+                        + LegacyProjectFileWildcard();
 
     wxString     default_dir = m_frame->GetMruPath();
     wxFileDialog dlg( m_frame, _( "Open Existing Project" ), default_dir, wxEmptyString,