From e128896ba6b6769dafda9497b09dabf880fb46eb Mon Sep 17 00:00:00 2001 From: Alex Shvartzkop Date: Sat, 26 Aug 2023 22:28:53 +0300 Subject: [PATCH] Schematic plugins refactoring, fixes for PCB plugins. - Move PLUGIN_FILE_DESC to common. - SCH_PLUGIN: rename Load -> LoadSchematicFile, Save -> SaveSchematicFile. - Use PLUGIN_FILE_DESC and CanRead* in schematic plugins. - Return none/unknown types from Find/GuessPluginType functions. - Iterate over file types for file wildcards. - Clean-up header checking in IO plugins. - Use PCB plugin list in IO_MGR::GuessPluginTypeFromLibPath. --- common/CMakeLists.txt | 1 + common/plugin_file_desc.cpp | 32 ++++ common/wildcards_and_files_ext.cpp | 62 -------- eeschema/dialogs/dialog_sheet_properties.cpp | 2 +- eeschema/dialogs/panel_sym_lib_table.cpp | 76 +++++++-- eeschema/eeschema.cpp | 2 +- eeschema/eeschema_helpers.cpp | 2 +- eeschema/files-io.cpp | 73 +++++---- eeschema/sch_io_mgr.cpp | 139 ++++++----------- eeschema/sch_io_mgr.h | 86 +++++----- eeschema/sch_plugin.cpp | 147 ++++++++++++++++-- .../sch_plugins/altium/sch_altium_plugin.cpp | 25 +-- .../sch_plugins/altium/sch_altium_plugin.h | 20 ++- .../cadstar/cadstar_sch_archive_plugin.cpp | 28 +--- .../cadstar/cadstar_sch_archive_plugin.h | 21 ++- .../database/sch_database_plugin.cpp | 7 - .../database/sch_database_plugin.h | 15 +- .../sch_plugins/eagle/sch_eagle_plugin.cpp | 63 ++++---- eeschema/sch_plugins/eagle/sch_eagle_plugin.h | 25 ++- .../sch_plugins/kicad/sch_sexpr_plugin.cpp | 25 +-- eeschema/sch_plugins/kicad/sch_sexpr_plugin.h | 28 ++-- .../sch_plugins/legacy/sch_legacy_plugin.cpp | 31 ++-- .../sch_plugins/legacy/sch_legacy_plugin.h | 36 +++-- .../ltspice/ltspice_sch_plugin.cpp | 23 +-- .../sch_plugins/ltspice/ltspice_sch_plugin.h | 18 ++- eeschema/sheet.cpp | 9 +- eeschema/symbol_editor/symbol_editor.cpp | 3 + .../symbol_editor_import_export.cpp | 48 +++++- eeschema/symbol_lib_table.cpp | 4 +- eeschema/symbol_library_manager.cpp | 4 + include/plugin_file_desc.h | 59 +++++++ include/wildcards_and_files_ext.h | 13 -- pcbnew/files.cpp | 3 + pcbnew/footprint_libraries_utils.cpp | 4 + pcbnew/io_mgr.cpp | 56 ++----- pcbnew/io_mgr.h | 36 +---- pcbnew/load_select_footprint.cpp | 21 ++- pcbnew/plugin.cpp | 1 + pcbnew/plugins/eagle/eagle_plugin.cpp | 2 +- pcbnew/tools/pcb_control.cpp | 6 + qa/schematic_utils/eeschema_test_utils.cpp | 2 +- qa/schematic_utils/schematic_file_util.cpp | 2 +- qa/tests/eeschema/test_sch_sheet_list.cpp | 6 +- 43 files changed, 711 insertions(+), 555 deletions(-) create mode 100644 common/plugin_file_desc.cpp create mode 100644 include/plugin_file_desc.h diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index dab37c4be9..953fd637f5 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -382,6 +382,7 @@ set( COMMON_SRCS origin_transforms.cpp page_info.cpp paths.cpp + plugin_file_desc.cpp printout.cpp project.cpp ptree.cpp diff --git a/common/plugin_file_desc.cpp b/common/plugin_file_desc.cpp new file mode 100644 index 0000000000..de6c84e408 --- /dev/null +++ b/common/plugin_file_desc.cpp @@ -0,0 +1,32 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2023 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 + */ + +#include +#include +#include + + +wxString PLUGIN_FILE_DESC::FileFilter() const +{ + return wxGetTranslation( m_Description ) + AddFileExtListToFilter( m_FileExtensions ); +} \ No newline at end of file diff --git a/common/wildcards_and_files_ext.cpp b/common/wildcards_and_files_ext.cpp index 9b84c32484..1c56302b4f 100644 --- a/common/wildcards_and_files_ext.cpp +++ b/common/wildcards_and_files_ext.cpp @@ -130,7 +130,6 @@ const std::string KiCadSymbolLibFileExtension( "kicad_sym" ); const std::string SchematicSymbolFileExtension( "sym" ); const std::string LegacySymbolLibFileExtension( "lib" ); const std::string LegacySymbolDocumentFileExtension( "dcm" ); -const std::string LtspiceSymbolExtension( "asy" ); const std::string VrmlFileExtension( "wrl" ); @@ -138,12 +137,10 @@ const std::string ProjectFileExtension( "kicad_pro" ); const std::string LegacyProjectFileExtension( "pro" ); const std::string ProjectLocalSettingsFileExtension( "kicad_prl" ); const std::string LegacySchematicFileExtension( "sch" ); -const std::string EagleSchematicFileExtension( "sch" ); const std::string CadstarSchematicFileExtension( "csa" ); const std::string CadstarPartsLibraryFileExtension( "lib" ); const std::string KiCadSchematicFileExtension( "kicad_sch" ); const std::string SpiceFileExtension( "cir" ); -const std::string LtspiceSchematicExtension( "asc" ); const std::string CadstarNetlistFileExtension( "frp" ); const std::string OrCadPcb2NetlistFileExtension( "net" ); const std::string NetlistFileExtension( "net" ); @@ -214,12 +211,6 @@ wxString AllFilesWildcard() } -wxString SchematicSymbolFileWildcard() -{ - return _( "KiCad drawing symbol files" ) + AddFileExtListToFilter( { "sym" } ); -} - - wxString KiCadSymbolLibFileWildcard() { return _( "KiCad symbol library files" ) @@ -227,28 +218,6 @@ wxString KiCadSymbolLibFileWildcard() } -wxString LegacySymbolLibFileWildcard() -{ - return _( "KiCad legacy symbol library files" ) + AddFileExtListToFilter( { "lib" } ); -} - - -wxString DatabaseLibFileWildcard() -{ - return _( "KiCad database library files" ) - + AddFileExtListToFilter( { DatabaseLibraryFileExtension } ); -} - - -wxString AllSymbolLibFilesWildcard() -{ - return _( "All symbol library files" ) - + AddFileExtListToFilter( { KiCadSymbolLibFileExtension, - DatabaseLibraryFileExtension, - "lib" } ); -} - - wxString ProjectFileWildcard() { return _( "KiCad project files" ) + AddFileExtListToFilter( { ProjectFileExtension } ); @@ -290,43 +259,12 @@ wxString KiCadSchematicFileWildcard() } -wxString AltiumSchematicFileWildcard() -{ - return _( "Altium schematic files" ) + AddFileExtListToFilter( { "SchDoc" } ); -} - - -wxString CadstarPartsLibraryFileWildcard() -{ - return _( "CADSTAR Parts Library files" ) - + AddFileExtListToFilter( { CadstarPartsLibraryFileExtension } ); -} - - -wxString CadstarSchematicArchiveFileWildcard() -{ - return _( "CADSTAR Schematic Archive files" ) + AddFileExtListToFilter( { "csa" } ); -} - - wxString CadstarArchiveFilesWildcard() { return _( "CADSTAR Archive files" ) + AddFileExtListToFilter( { "csa", "cpa" } ); } -wxString EagleSchematicFileWildcard() -{ - return _( "Eagle XML schematic files" ) + AddFileExtListToFilter( { "sch" } ); -} - - -wxString LtspiceSchematicFileWildcard() -{ - return _( "LTspice schematic files" ) + AddFileExtListToFilter( { "asc" } ); -} - - wxString EagleFilesWildcard() { return _( "Eagle XML files" ) + AddFileExtListToFilter( { "sch", "brd" } ); diff --git a/eeschema/dialogs/dialog_sheet_properties.cpp b/eeschema/dialogs/dialog_sheet_properties.cpp index 417687e302..4b6cf65477 100644 --- a/eeschema/dialogs/dialog_sheet_properties.cpp +++ b/eeschema/dialogs/dialog_sheet_properties.cpp @@ -572,7 +572,7 @@ bool DIALOG_SHEET_PROPERTIES::onSheetFilenameChanged( const wxString& aNewFilena try { - pi->Save( newAbsoluteFilename, m_sheet, &schematic ); + pi->SaveSchematicFile( newAbsoluteFilename, m_sheet, &schematic ); } catch( const IO_ERROR& ioe ) { diff --git a/eeschema/dialogs/panel_sym_lib_table.cpp b/eeschema/dialogs/panel_sym_lib_table.cpp index 762e2c529f..032a4a33fc 100644 --- a/eeschema/dialogs/panel_sym_lib_table.cpp +++ b/eeschema/dialogs/panel_sym_lib_table.cpp @@ -116,6 +116,10 @@ public: if( aCol == COL_URI ) { SCH_IO_MGR::SCH_FILE_T pluginType = SCH_IO_MGR::GuessPluginTypeFromLibPath( aValue ); + + if( pluginType == SCH_IO_MGR::SCH_FILE_UNKNOWN ) + pluginType = SCH_IO_MGR::SCH_KICAD; + SetValue( aRow, COL_TYPE, SCH_IO_MGR::ShowType( pluginType ) ); } } @@ -270,11 +274,38 @@ PANEL_SYM_LIB_TABLE::PANEL_SYM_LIB_TABLE( DIALOG_EDIT_LIBRARY_TABLES* aParent, P attr = new wxGridCellAttr; - wxString wildcards = AllSymbolLibFilesWildcard() - + "|" + KiCadSymbolLibFileWildcard() - + "|" + LegacySymbolLibFileWildcard(); + wxString fileFiltersStr; + wxString allWildcardsStr; + + for( const SCH_IO_MGR::SCH_FILE_T& fileType : SCH_IO_MGR::SCH_FILE_T_vector ) + { + if( fileType == SCH_IO_MGR::SCH_KICAD || fileType == SCH_IO_MGR::SCH_LEGACY ) + continue; // this is "Import non-KiCad schematic" + + SCH_PLUGIN::SCH_PLUGIN_RELEASER pi( SCH_IO_MGR::FindPlugin( fileType ) ); + + if( !pi ) + continue; + + const PLUGIN_FILE_DESC& desc = pi->GetLibraryFileDesc(); + + if( desc.m_FileExtensions.empty() ) + continue; + + if( !fileFiltersStr.IsEmpty() ) + fileFiltersStr += wxChar( '|' ); + + fileFiltersStr += desc.FileFilter(); + + for( const std::string& ext : desc.m_FileExtensions ) + allWildcardsStr << wxT( "*." ) << formatWildcardExt( ext ) << wxT( ";" ); + } + + fileFiltersStr = _( "All supported formats" ) + wxT( "|" ) + allWildcardsStr + wxT( "|" ) + + fileFiltersStr; + attr->SetEditor( new GRID_CELL_PATH_EDITOR( m_parent, aGrid, - &cfg->m_lastSymbolLibDir, wildcards, + &cfg->m_lastSymbolLibDir, fileFiltersStr, true, m_project->GetProjectPath() ) ); aGrid->SetColAttr( COL_URI, attr ); @@ -533,11 +564,32 @@ void PANEL_SYM_LIB_TABLE::OnUpdateUI( wxUpdateUIEvent& event ) void PANEL_SYM_LIB_TABLE::browseLibrariesHandler( wxCommandEvent& event ) { - wxString wildcards = AllSymbolLibFilesWildcard() - + "|" + KiCadSymbolLibFileWildcard() - + "|" + LegacySymbolLibFileWildcard() - + "|" + DatabaseLibFileWildcard() - + "|" + CadstarPartsLibraryFileWildcard(); + wxString fileFiltersStr; + wxString allWildcardsStr; + + for( const SCH_IO_MGR::SCH_FILE_T& fileType : SCH_IO_MGR::SCH_FILE_T_vector ) + { + SCH_PLUGIN::SCH_PLUGIN_RELEASER pi( SCH_IO_MGR::FindPlugin( fileType ) ); + + if( !pi ) + continue; + + const PLUGIN_FILE_DESC& desc = pi->GetLibraryFileDesc(); + + if( desc.m_FileExtensions.empty() ) + continue; + + if( !fileFiltersStr.IsEmpty() ) + fileFiltersStr += wxChar( '|' ); + + fileFiltersStr += desc.FileFilter(); + + for( const std::string& ext : desc.m_FileExtensions ) + allWildcardsStr << wxT( "*." ) << formatWildcardExt( ext ) << wxT( ";" ); + } + + fileFiltersStr = _( "All supported formats" ) + wxT( "|" ) + allWildcardsStr + wxT( "|" ) + + fileFiltersStr; EESCHEMA_SETTINGS* cfg = Pgm().GetSettingsManager().GetAppSettings(); @@ -546,7 +598,7 @@ void PANEL_SYM_LIB_TABLE::browseLibrariesHandler( wxCommandEvent& event ) if( m_cur_grid == m_project_grid ) openDir = m_lastProjectLibDir; - wxFileDialog dlg( this, _( "Select Library" ), openDir, wxEmptyString, wildcards, + wxFileDialog dlg( this, _( "Select Library" ), openDir, wxEmptyString, fileFiltersStr, wxFD_OPEN | wxFD_FILE_MUST_EXIST | wxFD_MULTIPLE ); if( dlg.ShowModal() == wxID_CANCEL ) @@ -595,6 +647,10 @@ void PANEL_SYM_LIB_TABLE::browseLibrariesHandler( wxCommandEvent& event ) // attempt to auto-detect the plugin type SCH_IO_MGR::SCH_FILE_T pluginType = SCH_IO_MGR::GuessPluginTypeFromLibPath( filePath ); + + if( pluginType == SCH_IO_MGR::SCH_FILE_UNKNOWN ) + pluginType = SCH_IO_MGR::SCH_KICAD; + m_cur_grid->SetCellValue( last_row, COL_TYPE, SCH_IO_MGR::ShowType( pluginType ) ); // try to use path normalized to an environmental variable or project path diff --git a/eeschema/eeschema.cpp b/eeschema/eeschema.cpp index 26623db62e..a8843ed0b2 100644 --- a/eeschema/eeschema.cpp +++ b/eeschema/eeschema.cpp @@ -82,7 +82,7 @@ static std::unique_ptr readSchematicFromFile( const std::string& aFil manager.LoadProject( "" ); schematic->Reset(); schematic->SetProject( &manager.Prj() ); - schematic->SetRoot( pi->Load( aFilename, schematic.get() ) ); + schematic->SetRoot( pi->LoadSchematicFile( aFilename, schematic.get() ) ); schematic->CurrentSheet().push_back( &schematic->Root() ); SCH_SCREENS screens( schematic->Root() ); diff --git a/eeschema/eeschema_helpers.cpp b/eeschema/eeschema_helpers.cpp index 573dbf139a..482cce389c 100644 --- a/eeschema/eeschema_helpers.cpp +++ b/eeschema/eeschema_helpers.cpp @@ -128,7 +128,7 @@ SCHEMATIC* EESCHEMA_HELPERS::LoadSchematic( wxString& aFileName, SCH_IO_MGR::SCH try { - schematic->SetRoot( pi->Load( aFileName, schematic ) ); + schematic->SetRoot( pi->LoadSchematicFile( aFileName, schematic ) ); } catch( ... ) { diff --git a/eeschema/files-io.cpp b/eeschema/files-io.cpp index 93241ba8f9..2cd6f294e8 100644 --- a/eeschema/files-io.cpp +++ b/eeschema/files-io.cpp @@ -240,7 +240,7 @@ bool SCH_EDIT_FRAME::OpenProjectFiles( const std::vector& aFileSet, in { { wxBusyCursor busy; - Schematic().SetRoot( pi->Load( fullFileName, &Schematic() ) ); + Schematic().SetRoot( pi->LoadSchematicFile( fullFileName, &Schematic() ) ); } if( !pi->GetError().IsEmpty() ) @@ -620,39 +620,37 @@ void SCH_EDIT_FRAME::OnImportProject( wxCommandEvent& aEvent ) bool setProject = Prj().GetProjectFullName().IsEmpty() || Kiface().IsSingle(); wxString path = wxPathOnly( Prj().GetProjectFullName() ); - std::list> loaders; + wxString fileFiltersStr; + wxString allWildcardsStr; - // Import Altium schematic files. - loaders.emplace_back( AltiumSchematicFileWildcard(), SCH_IO_MGR::SCH_ALTIUM ); - - // Import CADSTAR Schematic Archive files. - loaders.emplace_back( CadstarSchematicArchiveFileWildcard(), SCH_IO_MGR::SCH_CADSTAR_ARCHIVE ); - - // Import Eagle schematic files. - loaders.emplace_back( EagleSchematicFileWildcard(), SCH_IO_MGR::SCH_EAGLE ); - - // Import LTspice schematic files. - loaders.emplace_back( LtspiceSchematicFileWildcard(), SCH_IO_MGR::SCH_LTSPICE ); - - wxString fileFilters; - wxString allWildcards; - - for( std::pair& loader : loaders ) + for( const SCH_IO_MGR::SCH_FILE_T& fileType : SCH_IO_MGR::SCH_FILE_T_vector ) { - if( !fileFilters.IsEmpty() ) - fileFilters += wxChar( '|' ); + if( fileType == SCH_IO_MGR::SCH_KICAD || fileType == SCH_IO_MGR::SCH_LEGACY ) + continue; // this is "Import non-KiCad schematic" - fileFilters += wxGetTranslation( loader.first ); + SCH_PLUGIN::SCH_PLUGIN_RELEASER pi( SCH_IO_MGR::FindPlugin( fileType ) ); - SCH_PLUGIN::SCH_PLUGIN_RELEASER plugin( SCH_IO_MGR::FindPlugin( loader.second ) ); - wxCHECK( plugin, /*void*/ ); - allWildcards += wxS( "*." ) + formatWildcardExt( plugin->GetFileExtension() ) + wxS( ";" ); + if( !pi ) + continue; + + const PLUGIN_FILE_DESC& desc = pi->GetSchematicFileDesc(); + + if( desc.m_FileExtensions.empty() ) + continue; + + if( !fileFiltersStr.IsEmpty() ) + fileFiltersStr += wxChar( '|' ); + + fileFiltersStr += desc.FileFilter(); + + for( const std::string& ext : desc.m_FileExtensions ) + allWildcardsStr << wxT( "*." ) << formatWildcardExt( ext ) << wxT( ";" ); } - fileFilters = _( "All supported formats" ) + wxS( "|" ) + allWildcards + wxS( "|" ) + - fileFilters; + fileFiltersStr = _( "All supported formats" ) + wxT( "|" ) + allWildcardsStr + wxT( "|" ) + + fileFiltersStr; - wxFileDialog dlg( this, _( "Import Schematic" ), path, wxEmptyString, fileFilters, + wxFileDialog dlg( this, _( "Import Schematic" ), path, wxEmptyString, fileFiltersStr, wxFD_OPEN | wxFD_FILE_MUST_EXIST ); // TODO if( dlg.ShowModal() == wxID_CANCEL ) @@ -683,18 +681,23 @@ void SCH_EDIT_FRAME::OnImportProject( wxCommandEvent& aEvent ) SCH_IO_MGR::SCH_FILE_T pluginType = SCH_IO_MGR::SCH_FILE_T::SCH_FILE_UNKNOWN; - for( std::pair& loader : loaders ) + for( const SCH_IO_MGR::SCH_FILE_T& fileType : SCH_IO_MGR::SCH_FILE_T_vector ) { - if( fn.GetExt().CmpNoCase( SCH_IO_MGR::GetFileExtension( loader.second ) ) == 0 ) + SCH_PLUGIN::SCH_PLUGIN_RELEASER pi( SCH_IO_MGR::FindPlugin( fileType ) ); + + if( !pi ) + continue; + + if( pi->CanReadSchematicFile( fn.GetFullPath() ) ) { - pluginType = loader.second; + pluginType = fileType; break; } } if( pluginType == SCH_IO_MGR::SCH_FILE_T::SCH_FILE_UNKNOWN ) { - wxLogError( _( "Unexpected file extension: '%s'." ), fn.GetExt() ); + wxLogError( _( "No loader can read the specified file: '%s'." ), fn.GetFullPath() ); return; } @@ -749,11 +752,15 @@ bool SCH_EDIT_FRAME::saveSchematicFile( SCH_SHEET* aSheet, const wxString& aSave SCH_IO_MGR::SCH_FILE_T pluginType = SCH_IO_MGR::GuessPluginTypeFromSchPath( schematicFileName.GetFullPath() ); + + if( pluginType == SCH_IO_MGR::SCH_FILE_UNKNOWN ) + pluginType = SCH_IO_MGR::SCH_KICAD; + SCH_PLUGIN::SCH_PLUGIN_RELEASER pi( SCH_IO_MGR::FindPlugin( pluginType ) ); try { - pi->Save( tempFile, aSheet, &Schematic() ); + pi->SaveSchematicFile( tempFile, aSheet, &Schematic() ); success = true; } catch( const IO_ERROR& ioe ) @@ -1255,7 +1262,7 @@ void SCH_EDIT_FRAME::importFile( const wxString& aFileName, int aFileType ) pi->SetReporter( errorReporter.m_Reporter ); pi->SetProgressReporter( &progressReporter ); - Schematic().SetRoot( pi->Load( aFileName, &Schematic() ) ); + Schematic().SetRoot( pi->LoadSchematicFile( aFileName, &Schematic() ) ); if( errorReporter.m_Reporter->HasMessage() ) { diff --git a/eeschema/sch_io_mgr.cpp b/eeschema/sch_io_mgr.cpp index 23fec78511..ac5d1a7220 100644 --- a/eeschema/sch_io_mgr.cpp +++ b/eeschema/sch_io_mgr.cpp @@ -32,9 +32,11 @@ #include #include #include -#include #include // for ExpandEnvVarSubstitutions +#include +#include + #define FMT_UNIMPLEMENTED _( "Plugin \"%s\" does not implement the \"%s\" function." ) #define FMT_NOTFOUND _( "Plugin type \"%s\" is not found." ) @@ -59,12 +61,12 @@ SCH_PLUGIN* SCH_IO_MGR::FindPlugin( SCH_FILE_T aFileType ) switch( aFileType ) { - case SCH_LEGACY: return new SCH_LEGACY_PLUGIN(); case SCH_KICAD: return new SCH_SEXPR_PLUGIN(); + case SCH_LEGACY: return new SCH_LEGACY_PLUGIN(); case SCH_ALTIUM: return new SCH_ALTIUM_PLUGIN(); case SCH_CADSTAR_ARCHIVE: return new CADSTAR_SCH_ARCHIVE_PLUGIN(); - case SCH_EAGLE: return new SCH_EAGLE_PLUGIN(); case SCH_DATABASE: return new SCH_DATABASE_PLUGIN(); + case SCH_EAGLE: return new SCH_EAGLE_PLUGIN(); case SCH_LTSPICE: return new SCH_LTSPICE_PLUGIN(); default: return nullptr; } @@ -89,12 +91,12 @@ const wxString SCH_IO_MGR::ShowType( SCH_FILE_T aType ) switch( aType ) { - case SCH_LEGACY: return wxString( wxT( "Legacy" ) ); case SCH_KICAD: return wxString( wxT( "KiCad" ) ); + case SCH_LEGACY: return wxString( wxT( "Legacy" ) ); case SCH_ALTIUM: return wxString( wxT( "Altium" ) ); case SCH_CADSTAR_ARCHIVE: return wxString( wxT( "CADSTAR Schematic Archive" ) ); - case SCH_EAGLE: return wxString( wxT( "EAGLE" ) ); case SCH_DATABASE: return wxString( wxT( "Database" ) ); + case SCH_EAGLE: return wxString( wxT( "EAGLE" ) ); case SCH_LTSPICE: return wxString( wxT( "LTspice" ) ); default: return wxString::Format( _( "Unknown SCH_FILE_T value: %d" ), aType ); @@ -108,114 +110,73 @@ SCH_IO_MGR::SCH_FILE_T SCH_IO_MGR::EnumFromStr( const wxString& aType ) // text spellings. If you change the spellings, you will obsolete // library tables, so don't do change, only additions are ok. - if( aType == wxT( "Legacy" ) ) - return SCH_LEGACY; - else if( aType == wxT( "KiCad" ) ) + if( aType == wxT( "KiCad" ) ) return SCH_KICAD; + else if( aType == wxT( "Legacy" ) ) + return SCH_LEGACY; else if( aType == wxT( "Altium" ) ) return SCH_ALTIUM; else if( aType == wxT( "CADSTAR Schematic Archive" ) ) return SCH_CADSTAR_ARCHIVE; - else if( aType == wxT( "EAGLE" ) ) - return SCH_EAGLE; else if( aType == wxT( "Database" ) ) return SCH_DATABASE; + else if( aType == wxT( "EAGLE" ) ) + return SCH_EAGLE; else if( aType == wxT( "LTspice" ) ) return SCH_LTSPICE; // wxASSERT( blow up here ) - return SCH_FILE_T( -1 ); + return SCH_FILE_UNKNOWN; } -const wxString SCH_IO_MGR::GetFileExtension( SCH_FILE_T aFileType ) +SCH_IO_MGR::SCH_FILE_T SCH_IO_MGR::GuessPluginTypeFromLibPath( const wxString& aLibPath, int aCtl ) { - wxString ext = wxEmptyString; - SCH_PLUGIN* plugin = FindPlugin( aFileType ); - - if( plugin != nullptr ) + for( const SCH_IO_MGR::SCH_FILE_T& fileType : SCH_IO_MGR::SCH_FILE_T_vector ) { - ext = plugin->GetFileExtension(); - ReleasePlugin( plugin ); + bool isKiCad = fileType == SCH_IO_MGR::SCH_KICAD || fileType == SCH_IO_MGR::SCH_LEGACY; + + if( ( aCtl & KICTL_KICAD_ONLY ) && !isKiCad ) + continue; + + if( ( aCtl & KICTL_NONKICAD_ONLY ) && isKiCad ) + continue; + + SCH_PLUGIN::SCH_PLUGIN_RELEASER pi( SCH_IO_MGR::FindPlugin( fileType ) ); + + if( !pi ) + continue; + + if( pi->CanReadLibrary( aLibPath ) ) + return fileType; } - return ext; + return SCH_IO_MGR::SCH_FILE_UNKNOWN; } -const wxString SCH_IO_MGR::GetLibraryFileExtension( SCH_FILE_T aFileType ) +SCH_IO_MGR::SCH_FILE_T SCH_IO_MGR::GuessPluginTypeFromSchPath( const wxString& aSchematicPath, + int aCtl ) { - wxString ext = wxEmptyString; - SCH_PLUGIN* plugin = FindPlugin( aFileType ); - - if( plugin != nullptr ) + for( const SCH_IO_MGR::SCH_FILE_T& fileType : SCH_IO_MGR::SCH_FILE_T_vector ) { - ext = plugin->GetLibraryFileExtension(); - ReleasePlugin( plugin ); + bool isKiCad = fileType == SCH_IO_MGR::SCH_KICAD || fileType == SCH_IO_MGR::SCH_LEGACY; + + if( ( aCtl & KICTL_KICAD_ONLY ) && !isKiCad ) + continue; + + if( ( aCtl & KICTL_NONKICAD_ONLY ) && isKiCad ) + continue; + + SCH_PLUGIN::SCH_PLUGIN_RELEASER pi( SCH_IO_MGR::FindPlugin( fileType ) ); + + if( !pi ) + continue; + + if( pi->CanReadSchematicFile( aSchematicPath ) ) + return fileType; } - return ext; -} - - -SCH_IO_MGR::SCH_FILE_T SCH_IO_MGR::GuessPluginTypeFromLibPath( const wxString& aLibPath ) -{ - SCH_FILE_T ret = SCH_KICAD; // default guess, unless detected otherwise. - wxFileName fn( aLibPath ); - wxString ext = fn.GetExt().Lower(); - - // .lib is shared between CADSTAR and Legacy KiCad file formats. Let's read the header - if( ext == LegacySymbolLibFileExtension ) - { - wxString fullName = ExpandEnvVarSubstitutions( aLibPath, nullptr ); - - // Of course the file should exist to be read. If not, use the SCH_LEGACY - // format: it is more usual than SCH_CADSTAR_ARCHIVE - if( !wxFileExists( fullName ) ) - return SCH_LEGACY; - - for( SCH_FILE_T pluginType : { SCH_LEGACY, SCH_CADSTAR_ARCHIVE } ) - { - SCH_PLUGIN::SCH_PLUGIN_RELEASER pi( SCH_IO_MGR::FindPlugin( pluginType ) ); - - if( pi ) - { - if( pi->CheckHeader( fullName ) ) - return pluginType; - } - } - - // If not found, use the SCH_LEGACY. - return SCH_LEGACY; - } - - for( SCH_IO_MGR::SCH_FILE_T piType : SCH_IO_MGR::SCH_FILE_T_vector ) - { - if( SCH_IO_MGR::GetLibraryFileExtension( piType ).Lower() == ext ) - { - ret = piType; - break; - } - } - - return ret; -} - - -SCH_IO_MGR::SCH_FILE_T SCH_IO_MGR::GuessPluginTypeFromSchPath( const wxString& aSchematicPath ) -{ - SCH_FILE_T ret = SCH_KICAD; // default guess, unless detected otherwise. - wxFileName fn( aSchematicPath ); - - if( fn.GetExt() == LegacySchematicFileExtension ) - { - ret = SCH_LEGACY; - } - else if( fn.GetExt() == KiCadSchematicFileExtension ) - { - ret = SCH_KICAD; - } - - return ret; + return SCH_IO_MGR::SCH_FILE_UNKNOWN; } diff --git a/eeschema/sch_io_mgr.h b/eeschema/sch_io_mgr.h index 2cda6d2ec9..790c7ef858 100644 --- a/eeschema/sch_io_mgr.h +++ b/eeschema/sch_io_mgr.h @@ -1,6 +1,3 @@ -#ifndef _SCH_IO_MGR_H_ -#define _SCH_IO_MGR_H_ - /* * This program source code file is part of KiCad, a free EDA CAD application. * @@ -23,11 +20,16 @@ * with this program. If not, see . */ +#ifndef _SCH_IO_MGR_H_ +#define _SCH_IO_MGR_H_ + #include #include #include #include #include +#include +#include class SCH_SHEET; @@ -56,8 +58,8 @@ public: // clang-format off DEFINE_ENUM_VECTOR( SCH_FILE_T, { - SCH_LEGACY, ///< Legacy Eeschema file formats prior to s-expression. SCH_KICAD, ///< The s-expression version of the schematic. + SCH_LEGACY, ///< Legacy Eeschema file formats prior to s-expression. SCH_ALTIUM, ///< Altium file format SCH_CADSTAR_ARCHIVE, ///< CADSTAR Schematic Archive SCH_DATABASE, ///< KiCad database library @@ -103,40 +105,22 @@ public: */ static SCH_FILE_T EnumFromStr( const wxString& aFileType ); - /** - * Return the schematic file extension for \a aFileType. - * - * @param aFileType is the #SCH_FILE_T type. - * - * @return the file extension for \a aFileType or an empty string if \a aFileType is invalid. - */ - static const wxString GetFileExtension( SCH_FILE_T aFileType ); - - /** - * Return the symbol library file extension (if any) for \a aFileType. - * - * @param aFileType is the #SCH_FILE_T type. - * - * @return the file extension for \a aFileType or an empty string if \a aFileType is invalid. - */ - static const wxString GetLibraryFileExtension( SCH_FILE_T aFileType ); - /** * Return a plugin type given a symbol library using the file extension of \a aLibPath. */ - static SCH_FILE_T GuessPluginTypeFromLibPath( const wxString& aLibPath ); + static SCH_FILE_T GuessPluginTypeFromLibPath( const wxString& aLibPath, int aCtl = 0 ); /** * Return a plugin type given a schematic using the file extension of \a aSchematicPath. */ - static SCH_FILE_T GuessPluginTypeFromSchPath( const wxString& aSchematicPath ); + static SCH_FILE_T GuessPluginTypeFromSchPath( const wxString& aSchematicPath, int aCtl = 0 ); }; /** * Base class that schematic file and library loading and saving plugins should derive from. - * Implementations can provide either Load() or Save() functions, or both. - * SCH_PLUGINs throw exceptions, so it is best that you wrap your calls to these + * Implementations can provide either LoadSchematicFile() or SaveSchematicFile() functions, + * or both. SCH_PLUGINs throw exceptions, so it is best that you wrap your calls to these * functions in a try catch block. Plugins throw exceptions because it is illegal * for them to have any user interface calls in them whatsoever, i.e. no windowing * or screen printing at all. @@ -144,9 +128,9 @@ public: *
  *   try
  *   {
- *        SCH_IO_MGR::Load(...);
+ *        SCH_IO_MGR::LoadSchematicFile(...);
  *   or
- *        SCH_IO_MGR::Save(...);
+ *        SCH_IO_MGR::SaveSchematicFile(...);
  *   }
  *   catch( const IO_ERROR& ioe )
  *   {
@@ -176,14 +160,26 @@ public:
     virtual void SetProgressReporter( PROGRESS_REPORTER* aReporter ) {}
 
     /**
-     * Return the file extension for the #SCH_PLUGIN.
+     * Returns schematic file description for the #SCH_PLUGIN.
      */
-    virtual const wxString GetFileExtension() const = 0;
+    virtual const PLUGIN_FILE_DESC GetSchematicFileDesc() const;
 
     /**
-     * Return the library file extension for the #SCH_PLUGIN object.
+     * Returns symbol library description for the #SCH_PLUGIN.
      */
-    virtual const wxString GetLibraryFileExtension() const = 0;
+    virtual const PLUGIN_FILE_DESC GetLibraryFileDesc() const;
+
+    /**
+     * Checks if this SCH_PLUGIN can read the specified schematic file.
+     * If not overriden, extension check is used.
+     */
+    virtual bool CanReadSchematicFile( const wxString& aFileName ) const;
+
+    /**
+     * Checks if this SCH_PLUGIN can read the specified symbol library file.
+     * If not overriden, extension check is used.
+     */
+    virtual bool CanReadLibrary( const wxString& aFileName ) const;
 
     /**
      * Return the modification hash from the library cache.
@@ -226,9 +222,9 @@ public:
      *                 wrong, using line number and character offsets of the input file if
      *                 possible.
      */
-    virtual SCH_SHEET* Load( const wxString& aFileName, SCHEMATIC* aSchematic,
-                             SCH_SHEET* aAppendToMe = nullptr,
-                             const STRING_UTF8_MAP* aProperties = nullptr );
+    virtual SCH_SHEET* LoadSchematicFile( const wxString& aFileName, SCHEMATIC* aSchematic,
+                                          SCH_SHEET*             aAppendToMe = nullptr,
+                                          const STRING_UTF8_MAP* aProperties = nullptr );
 
     /**
      * Write \a aSchematic to a storage file in a format that this #SCH_PLUGIN implementation
@@ -255,8 +251,9 @@ public:
      *
      * @throw IO_ERROR if there is a problem saving or exporting.
      */
-    virtual void Save( const wxString& aFileName, SCH_SHEET* aSheet, SCHEMATIC* aSchematic,
-                       const STRING_UTF8_MAP* aProperties = nullptr );
+    virtual void SaveSchematicFile( const wxString& aFileName, SCH_SHEET* aSheet,
+                                    SCHEMATIC*             aSchematic,
+                                    const STRING_UTF8_MAP* aProperties = nullptr );
 
     /**
      * Populate a list of #LIB_SYMBOL alias names contained within the library \a aLibraryPath.
@@ -493,14 +490,6 @@ public:
         return GetAvailableSymbolFields( aNames );
     }
 
-    /**
-     * Return true if the first line in @a aFileName begins with the expected header.
-     *
-     * @param aFileName is the name of the file to use as input
-     *
-     */
-    virtual bool CheckHeader( const wxString& aFileName );
-
     /**
      * Return an error string to the caller.
      *
@@ -579,6 +568,13 @@ public:
             return plugin;
         }
     };
+
+    protected:
+    static bool fileStartsWithPrefix( const wxString& aFilePath, const wxString& aPrefix,
+                                      bool aIgnoreWhitespace );
+
+    static bool fileStartsWithBinaryHeader( const wxString&             aFilePath,
+                                            const std::vector& aHeader );
 };
 
 #endif // _SCH_IO_MGR_H_
diff --git a/eeschema/sch_plugin.cpp b/eeschema/sch_plugin.cpp
index 0271da6048..6eb7fd8309 100644
--- a/eeschema/sch_plugin.cpp
+++ b/eeschema/sch_plugin.cpp
@@ -2,7 +2,7 @@
  * This program source code file is part of KiCad, a free EDA CAD application.
  *
  * Copyright (C) 2016 CERN
- * Copyright (C) 2016-2021 KiCad Developers, see AUTHORS.txt for contributors.
+ * Copyright (C) 2016-2023 KiCad Developers, see AUTHORS.txt for contributors.
  *
  * @author Wayne Stambaugh 
  *
@@ -21,9 +21,14 @@
  */
 
 #include 
+#include 
 
 #include 
 #include 
+#include 
+#include 
+#include 
+#include 
 
 #define FMT_UNIMPLEMENTED wxT( "Plugin \"%s\" does not implement the \"%s\" function." )
 #define NOT_IMPLEMENTED( aCaller )                                                   \
@@ -32,21 +37,100 @@
                                       wxString::FromUTF8( aCaller ).GetData() ) );
 
 
+const PLUGIN_FILE_DESC SCH_PLUGIN::GetSchematicFileDesc() const
+{
+    return PLUGIN_FILE_DESC( wxEmptyString, {} );
+}
+
+
+const PLUGIN_FILE_DESC SCH_PLUGIN::GetLibraryFileDesc() const
+{
+    return PLUGIN_FILE_DESC( wxEmptyString, {} );
+}
+
+
+bool SCH_PLUGIN::CanReadSchematicFile( const wxString& aFileName ) const
+{
+    const std::vector& exts = GetSchematicFileDesc().m_FileExtensions;
+
+    wxString fileExt = wxFileName( aFileName ).GetExt().MakeLower();
+
+    for( const std::string& ext : exts )
+    {
+        if( fileExt == wxString( ext ).Lower() )
+            return true;
+    }
+
+    return false;
+}
+
+
+bool SCH_PLUGIN::CanReadLibrary( const wxString& aFileName ) const
+{
+    const PLUGIN_FILE_DESC& desc = GetLibraryFileDesc();
+
+    if( desc.m_IsFile )
+    {
+        const std::vector& exts = desc.m_FileExtensions;
+
+        wxString fileExt = wxFileName( aFileName ).GetExt().MakeLower();
+
+        for( const std::string& ext : exts )
+        {
+            if( fileExt == wxString( ext ).Lower() )
+                return true;
+        }
+    }
+    else
+    {
+        wxDir dir( aFileName );
+
+        if( !dir.IsOpened() )
+            return false;
+
+        std::vector     exts = desc.m_ExtensionsInDir;
+        std::unordered_set lowerExts;
+
+        for( const std::string& ext : exts )
+            lowerExts.emplace( wxString( ext ).MakeLower() );
+
+        wxString filenameStr;
+
+        bool cont = dir.GetFirst( &filenameStr, wxEmptyString, wxDIR_FILES | wxDIR_HIDDEN );
+        while( cont )
+        {
+            wxString ext = wxS( "" );
+
+            int idx = filenameStr.Find( '.', true );
+            if( idx != -1 )
+                ext = filenameStr.Mid( idx + 1 ).MakeLower();
+
+            if( lowerExts.count( ext ) )
+                return true;
+
+            cont = dir.GetNext( &filenameStr );
+        }
+    }
+
+    return false;
+}
+
+
 void SCH_PLUGIN::SaveLibrary( const wxString& aFileName, const STRING_UTF8_MAP* aProperties )
 {
     NOT_IMPLEMENTED( __FUNCTION__ );
 }
 
 
-SCH_SHEET* SCH_PLUGIN::Load( const wxString& aFileName, SCHEMATIC* aSchematic,
-                             SCH_SHEET* aAppendToMe, const STRING_UTF8_MAP* aProperties )
+SCH_SHEET* SCH_PLUGIN::LoadSchematicFile( const wxString& aFileName, SCHEMATIC* aSchematic,
+                                      SCH_SHEET* aAppendToMe, const STRING_UTF8_MAP* aProperties )
 {
     NOT_IMPLEMENTED( __FUNCTION__ );
 }
 
 
-void SCH_PLUGIN::Save( const wxString& aFileName, SCH_SHEET* aSheet, SCHEMATIC* aSchematic,
-                       const STRING_UTF8_MAP* aProperties )
+void SCH_PLUGIN::SaveSchematicFile( const wxString& aFileName, SCH_SHEET* aSheet, SCHEMATIC* aSchematic,
+                            const STRING_UTF8_MAP* aProperties )
 {
     // not pure virtual so that plugins only have to implement subset of the SCH_PLUGIN interface.
     NOT_IMPLEMENTED( __FUNCTION__ );
@@ -128,15 +212,54 @@ void SCH_PLUGIN::SymbolLibOptions( STRING_UTF8_MAP* aListToAppendTo ) const
 }
 
 
-bool SCH_PLUGIN::CheckHeader( const wxString& aFileName )
-{
-    // not pure virtual so that plugins only have to implement subset of the SCH_PLUGIN interface.
-    NOT_IMPLEMENTED( __FUNCTION__ );
-}
-
-
 const wxString& SCH_PLUGIN::GetError() const
 {
     // not pure virtual so that plugins only have to implement subset of the SCH_PLUGIN interface.
     NOT_IMPLEMENTED( __FUNCTION__ );
 }
+
+
+bool SCH_PLUGIN::fileStartsWithPrefix( const wxString& aFilePath, const wxString& aPrefix,
+                                   bool aIgnoreWhitespace )
+{
+    wxFFileInputStream input( aFilePath );
+
+    if( input.IsOk() && !input.Eof() )
+    {
+        // Find first non-empty line
+        wxTextInputStream text( input );
+        wxString          line = text.ReadLine();
+
+        if( aIgnoreWhitespace )
+        {
+            while( line.IsEmpty() )
+                line = text.ReadLine().Trim( false /*trim from left*/ );
+        }
+
+        if( line.StartsWith( aPrefix ) )
+            return true;
+    }
+
+    return false;
+}
+
+
+bool SCH_PLUGIN::fileStartsWithBinaryHeader( const wxString&             aFilePath,
+                                         const std::vector& aHeader )
+{
+    wxFFileInputStream input( aFilePath );
+
+    if( input.IsOk() && !input.Eof() )
+    {
+        if( input.GetLength() < aHeader.size() )
+            return false;
+
+        std::vector parsedHeader( aHeader.size() );
+        if( !input.ReadAll( parsedHeader.data(), parsedHeader.size() ) )
+            return false;
+
+        return parsedHeader == aHeader;
+    }
+
+    return false;
+}
diff --git a/eeschema/sch_plugins/altium/sch_altium_plugin.cpp b/eeschema/sch_plugins/altium/sch_altium_plugin.cpp
index 14101472a0..ea3b6d7e7b 100644
--- a/eeschema/sch_plugins/altium/sch_altium_plugin.cpp
+++ b/eeschema/sch_plugins/altium/sch_altium_plugin.cpp
@@ -160,32 +160,12 @@ const wxString SCH_ALTIUM_PLUGIN::GetName() const
 }
 
 
-const wxString SCH_ALTIUM_PLUGIN::GetFileExtension() const
-{
-    return "SchDoc";
-}
-
-
-const wxString SCH_ALTIUM_PLUGIN::GetLibraryFileExtension() const
-{
-    return "SchLib";
-}
-
-
 int SCH_ALTIUM_PLUGIN::GetModifyHash() const
 {
     return 0;
 }
 
 
-bool SCH_ALTIUM_PLUGIN::CheckHeader( const wxString& aFileName )
-{
-    // TODO
-
-    return true;
-}
-
-
 wxString SCH_ALTIUM_PLUGIN::getLibName()
 {
     if( m_libName.IsEmpty() )
@@ -218,8 +198,9 @@ wxFileName SCH_ALTIUM_PLUGIN::getLibFileName()
 }
 
 
-SCH_SHEET* SCH_ALTIUM_PLUGIN::Load( const wxString& aFileName, SCHEMATIC* aSchematic,
-                                    SCH_SHEET* aAppendToMe, const STRING_UTF8_MAP* aProperties )
+SCH_SHEET* SCH_ALTIUM_PLUGIN::LoadSchematicFile( const wxString& aFileName, SCHEMATIC* aSchematic,
+                                                 SCH_SHEET*             aAppendToMe,
+                                                 const STRING_UTF8_MAP* aProperties )
 {
     wxCHECK( !aFileName.IsEmpty() && aSchematic, nullptr );
 
diff --git a/eeschema/sch_plugins/altium/sch_altium_plugin.h b/eeschema/sch_plugins/altium/sch_altium_plugin.h
index 376be3e0f9..a82fafafff 100644
--- a/eeschema/sch_plugins/altium/sch_altium_plugin.h
+++ b/eeschema/sch_plugins/altium/sch_altium_plugin.h
@@ -2,7 +2,7 @@
  * This program source code file is part of KiCad, a free EDA CAD application.
  *
  * Copyright (C) 2020 Thomas Pointhuber 
- * Copyright (C) 2021-2022 KiCad Developers, see AUTHORS.txt for contributors.
+ * Copyright (C) 2021-2023 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
@@ -55,17 +55,21 @@ public:
 
     void SetReporter( REPORTER* aReporter ) override { m_reporter = aReporter; }
 
-    const wxString GetFileExtension() const override;
+    const PLUGIN_FILE_DESC GetSchematicFileDesc() const override
+    {
+        return PLUGIN_FILE_DESC( _HKI( "Altium schematic files" ), { "SchDoc" } );
+    }
 
-    const wxString GetLibraryFileExtension() const override;
+    /*const PLUGIN_FILE_DESC GetLibraryFileDesc() const override
+    {
+        return PLUGIN_FILE_DESC( _HKI( "Altium schematic library files" ), { "SchLib" } );
+    }*/
 
     int GetModifyHash() const override;
 
-    SCH_SHEET* Load( const wxString& aFileName, SCHEMATIC* aSchematic,
-                     SCH_SHEET* aAppendToMe = nullptr,
-                     const STRING_UTF8_MAP* aProperties = nullptr ) override;
-
-    bool CheckHeader( const wxString& aFileName ) override;
+    SCH_SHEET* LoadSchematicFile( const wxString& aFileName, SCHEMATIC* aSchematic,
+                                  SCH_SHEET*             aAppendToMe = nullptr,
+                                  const STRING_UTF8_MAP* aProperties = nullptr ) override;
 
     // unimplemented functions. Will trigger a not_implemented IO error.
     //void SaveLibrary( const wxString& aFileName, const PROPERTIES* aProperties = NULL ) override;
diff --git a/eeschema/sch_plugins/cadstar/cadstar_sch_archive_plugin.cpp b/eeschema/sch_plugins/cadstar/cadstar_sch_archive_plugin.cpp
index 2f03564228..d0a1143afe 100644
--- a/eeschema/sch_plugins/cadstar/cadstar_sch_archive_plugin.cpp
+++ b/eeschema/sch_plugins/cadstar/cadstar_sch_archive_plugin.cpp
@@ -2,7 +2,7 @@
  * This program source code file is part of KiCad, a free EDA CAD application.
  *
  * Copyright (C) 2020 Roberto Fernandez Bautista 
- * Copyright (C) 2020-2021 KiCad Developers, see AUTHORS.txt for contributors.
+ * Copyright (C) 2020-2023 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
@@ -44,26 +44,16 @@ const wxString CADSTAR_SCH_ARCHIVE_PLUGIN::GetName() const
 }
 
 
-const wxString CADSTAR_SCH_ARCHIVE_PLUGIN::GetFileExtension() const
-{
-    return CadstarSchematicFileExtension;
-}
-
-
-const wxString CADSTAR_SCH_ARCHIVE_PLUGIN::GetLibraryFileExtension() const
-{
-    return CadstarPartsLibraryFileExtension;
-}
-
-
 int CADSTAR_SCH_ARCHIVE_PLUGIN::GetModifyHash() const
 {
     return 0;
 }
 
 
-SCH_SHEET* CADSTAR_SCH_ARCHIVE_PLUGIN::Load( const wxString& aFileName, SCHEMATIC* aSchematic,
-        SCH_SHEET* aAppendToMe, const STRING_UTF8_MAP* aProperties )
+SCH_SHEET* CADSTAR_SCH_ARCHIVE_PLUGIN::LoadSchematicFile( const wxString&        aFileName,
+                                                          SCHEMATIC*             aSchematic,
+                                                          SCH_SHEET*             aAppendToMe,
+                                                          const STRING_UTF8_MAP* aProperties )
 {
     wxCHECK( !aFileName.IsEmpty() && aSchematic, nullptr );
 
@@ -174,14 +164,6 @@ SCH_SHEET* CADSTAR_SCH_ARCHIVE_PLUGIN::Load( const wxString& aFileName, SCHEMATI
 }
 
 
-bool CADSTAR_SCH_ARCHIVE_PLUGIN::CheckHeader( const wxString& aFileName )
-{
-    // TODO: write a parser for the cpa header. For now assume it is valid
-    // and throw exceptions when parsing
-    return true;
-}
-
-
 void CADSTAR_SCH_ARCHIVE_PLUGIN::EnumerateSymbolLib( wxArrayString&         aSymbolNameList,
                                                      const wxString&        aLibraryPath,
                                                      const STRING_UTF8_MAP* aProperties )
diff --git a/eeschema/sch_plugins/cadstar/cadstar_sch_archive_plugin.h b/eeschema/sch_plugins/cadstar/cadstar_sch_archive_plugin.h
index cae6159645..b0fb2608ce 100644
--- a/eeschema/sch_plugins/cadstar/cadstar_sch_archive_plugin.h
+++ b/eeschema/sch_plugins/cadstar/cadstar_sch_archive_plugin.h
@@ -31,6 +31,7 @@
 #include 
 #include 
 #include 
+#include 
 
 
 class SCH_SHEET;
@@ -57,17 +58,23 @@ public:
         m_progressReporter = aReporter;
     }
 
-    const wxString GetFileExtension() const override;
+    const PLUGIN_FILE_DESC GetSchematicFileDesc() const override
+    {
+        return PLUGIN_FILE_DESC( _HKI( "CADSTAR Schematic Archive files" ),
+                                 { CadstarSchematicFileExtension } );
+    }
 
-    const wxString GetLibraryFileExtension() const override;
+    const PLUGIN_FILE_DESC GetLibraryFileDesc() const override
+    {
+        return PLUGIN_FILE_DESC( _HKI( "CADSTAR Parts Library files" ),
+                                 { CadstarPartsLibraryFileExtension } );
+    }
 
     int GetModifyHash() const override;
 
-    SCH_SHEET* Load( const wxString& aFileName, SCHEMATIC* aSchematic,
-                     SCH_SHEET* aAppendToMe = nullptr,
-                     const STRING_UTF8_MAP* aProperties = nullptr ) override;
-
-    bool CheckHeader( const wxString& aFileName ) override;
+    SCH_SHEET* LoadSchematicFile( const wxString& aFileName, SCHEMATIC* aSchematic,
+                                  SCH_SHEET*             aAppendToMe = nullptr,
+                                  const STRING_UTF8_MAP* aProperties = nullptr ) override;
 
     void EnumerateSymbolLib( wxArrayString&    aSymbolNameList,
                              const wxString&   aLibraryPath,
diff --git a/eeschema/sch_plugins/database/sch_database_plugin.cpp b/eeschema/sch_plugins/database/sch_database_plugin.cpp
index a3060b014c..1b9274cf44 100644
--- a/eeschema/sch_plugins/database/sch_database_plugin.cpp
+++ b/eeschema/sch_plugins/database/sch_database_plugin.cpp
@@ -207,13 +207,6 @@ void SCH_DATABASE_PLUGIN::GetDefaultSymbolFields( std::vector& aNames
 }
 
 
-bool SCH_DATABASE_PLUGIN::CheckHeader( const wxString& aFileName )
-{
-    // TODO: Implement this sometime; but CheckHeader isn't even called...
-    return true;
-}
-
-
 bool SCH_DATABASE_PLUGIN::TestConnection( wxString* aErrorMsg )
 {
     if( m_conn && m_conn->IsConnected() )
diff --git a/eeschema/sch_plugins/database/sch_database_plugin.h b/eeschema/sch_plugins/database/sch_database_plugin.h
index 7dff48cc48..3664dac18f 100644
--- a/eeschema/sch_plugins/database/sch_database_plugin.h
+++ b/eeschema/sch_plugins/database/sch_database_plugin.h
@@ -2,7 +2,7 @@
  * This program source code file is part of KiCad, a free EDA CAD application.
  *
  * Copyright (C) 2022 Jon Evans 
- * Copyright (C) 2022 KiCad Developers, see AUTHORS.txt for contributors.
+ * Copyright (C) 2022-2023 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
@@ -49,15 +49,10 @@ public:
         return wxT( "Database library" );
     }
 
-    const wxString GetLibraryFileExtension() const override
+    const PLUGIN_FILE_DESC GetLibraryFileDesc() const override
     {
-        return DatabaseLibraryFileExtension;
-    }
-
-    const wxString GetFileExtension() const override
-    {
-        wxFAIL_MSG( "Database libraries are not schematic files!  Fix call site." );
-        return DatabaseLibraryFileExtension;
+        return PLUGIN_FILE_DESC( _HKI( "KiCad database library files" ),
+                                 { DatabaseLibraryFileExtension } );
     }
 
     int GetModifyHash() const override { return 0; }
@@ -81,8 +76,6 @@ public:
 
     void GetDefaultSymbolFields( std::vector& aNames ) override;
 
-    bool CheckHeader( const wxString& aFileName ) override;
-
     // Database libraries can never be written using the symbol editing API
     bool IsSymbolLibWritable( const wxString& aLibraryPath ) override
     {
diff --git a/eeschema/sch_plugins/eagle/sch_eagle_plugin.cpp b/eeschema/sch_plugins/eagle/sch_eagle_plugin.cpp
index 546a4268e1..49aa3d0ffe 100644
--- a/eeschema/sch_plugins/eagle/sch_eagle_plugin.cpp
+++ b/eeschema/sch_plugins/eagle/sch_eagle_plugin.cpp
@@ -375,18 +375,6 @@ const wxString SCH_EAGLE_PLUGIN::GetName() const
 }
 
 
-const wxString SCH_EAGLE_PLUGIN::GetFileExtension() const
-{
-    return wxT( "sch" );
-}
-
-
-const wxString SCH_EAGLE_PLUGIN::GetLibraryFileExtension() const
-{
-    return wxT( "lbr" );
-}
-
-
 int SCH_EAGLE_PLUGIN::GetModifyHash() const
 {
     return 0;
@@ -413,8 +401,9 @@ void SCH_EAGLE_PLUGIN::checkpoint()
 }
 
 
-SCH_SHEET* SCH_EAGLE_PLUGIN::Load( const wxString& aFileName, SCHEMATIC* aSchematic,
-                                   SCH_SHEET* aAppendToMe, const STRING_UTF8_MAP* aProperties )
+SCH_SHEET* SCH_EAGLE_PLUGIN::LoadSchematicFile( const wxString& aFileName, SCHEMATIC* aSchematic,
+                                                SCH_SHEET*             aAppendToMe,
+                                                const STRING_UTF8_MAP* aProperties )
 {
     wxASSERT( !aFileName || aSchematic != nullptr );
     LOCALE_IO toggle; // toggles on, then off, the C locale.
@@ -2724,23 +2713,43 @@ void SCH_EAGLE_PLUGIN::adjustNetLabels()
 }
 
 
-bool SCH_EAGLE_PLUGIN::CheckHeader( const wxString& aFileName )
+bool SCH_EAGLE_PLUGIN::CanReadSchematicFile( const wxString& aFileName ) const
 {
-    // Open file and check first line
-    wxTextFile tempFile;
+    if( !SCH_PLUGIN::CanReadSchematicFile( aFileName ) )
+        return false;
 
-    tempFile.Open( aFileName );
-    wxString firstline;
+    return checkHeader( aFileName );
+}
 
-    // read the first line
-    firstline           = tempFile.GetFirstLine();
-    wxString secondline = tempFile.GetNextLine();
-    wxString thirdline  = tempFile.GetNextLine();
-    tempFile.Close();
 
-    return firstline.StartsWith( wxT( "
  * @author Maciej Suminski 
@@ -95,21 +95,30 @@ public:
         m_progressReporter = aReporter;
     }
 
-    const wxString GetFileExtension() const override;
+    const PLUGIN_FILE_DESC GetSchematicFileDesc() const override
+    {
+        return PLUGIN_FILE_DESC( _HKI( "Eagle XML schematic files" ), { "sch" } );
+    }
 
-    const wxString GetLibraryFileExtension() const override;
+    /*const PLUGIN_FILE_DESC GetLibraryFileDesc() const override
+    {
+        return PLUGIN_FILE_DESC( _HKI( "Eagle XML library files" ), { "lbr" } );
+    }*/
+
+    bool CanReadSchematicFile( const wxString& aFileName ) const override;
+    bool CanReadLibrary( const wxString& aFileName ) const override;
 
     int GetModifyHash() const override;
 
-    SCH_SHEET* Load( const wxString& aFileName, SCHEMATIC* aSchematic,
-                     SCH_SHEET* aAppendToMe = nullptr,
-                     const STRING_UTF8_MAP* aProperties = nullptr ) override;
-
-    bool CheckHeader( const wxString& aFileName ) override;
+    SCH_SHEET* LoadSchematicFile( const wxString& aFileName, SCHEMATIC* aSchematic,
+                                  SCH_SHEET*             aAppendToMe = nullptr,
+                                  const STRING_UTF8_MAP* aProperties = nullptr ) override;
 
 private:
     void checkpoint();
 
+    bool checkHeader( const wxString& aFileName ) const;
+
     void loadDrawing( wxXmlNode* aDrawingNode );
     void loadLayerDefs( wxXmlNode* aLayers );
     void loadSchematic( wxXmlNode* aSchematicNode );
diff --git a/eeschema/sch_plugins/kicad/sch_sexpr_plugin.cpp b/eeschema/sch_plugins/kicad/sch_sexpr_plugin.cpp
index adae93ba72..5c8cf83a52 100644
--- a/eeschema/sch_plugins/kicad/sch_sexpr_plugin.cpp
+++ b/eeschema/sch_plugins/kicad/sch_sexpr_plugin.cpp
@@ -97,8 +97,9 @@ void SCH_SEXPR_PLUGIN::init( SCHEMATIC* aSchematic, const STRING_UTF8_MAP* aProp
 }
 
 
-SCH_SHEET* SCH_SEXPR_PLUGIN::Load( const wxString& aFileName, SCHEMATIC* aSchematic,
-                                   SCH_SHEET* aAppendToMe, const STRING_UTF8_MAP* aProperties )
+SCH_SHEET* SCH_SEXPR_PLUGIN::LoadSchematicFile( const wxString& aFileName, SCHEMATIC* aSchematic,
+                                                SCH_SHEET*             aAppendToMe,
+                                                const STRING_UTF8_MAP* aProperties )
 {
     wxASSERT( !aFileName || aSchematic != nullptr );
 
@@ -328,8 +329,9 @@ void SCH_SEXPR_PLUGIN::LoadContent( LINE_READER& aReader, SCH_SHEET* aSheet, int
 }
 
 
-void SCH_SEXPR_PLUGIN::Save( const wxString& aFileName, SCH_SHEET* aSheet, SCHEMATIC* aSchematic,
-                             const STRING_UTF8_MAP* aProperties )
+void SCH_SEXPR_PLUGIN::SaveSchematicFile( const wxString& aFileName, SCH_SHEET* aSheet,
+                                          SCHEMATIC*             aSchematic,
+                                          const STRING_UTF8_MAP* aProperties )
 {
     wxCHECK_RET( aSheet != nullptr, "NULL SCH_SHEET object." );
     wxCHECK_RET( !aFileName.IsEmpty(), "No schematic file name defined." );
@@ -1613,21 +1615,6 @@ void SCH_SEXPR_PLUGIN::SaveLibrary( const wxString& aLibraryPath, const STRING_U
 }
 
 
-bool SCH_SEXPR_PLUGIN::CheckHeader( const wxString& aFileName )
-{
-    // Open file and check first line
-    wxTextFile tempFile;
-
-    tempFile.Open( aFileName );
-    wxString firstline;
-    // read the first line
-    firstline = tempFile.GetFirstLine();
-    tempFile.Close();
-
-    return firstline.StartsWith( wxS( "EESchema" ) );
-}
-
-
 bool SCH_SEXPR_PLUGIN::IsSymbolLibWritable( const wxString& aLibraryPath )
 {
     wxFileName fn( aLibraryPath );
diff --git a/eeschema/sch_plugins/kicad/sch_sexpr_plugin.h b/eeschema/sch_plugins/kicad/sch_sexpr_plugin.h
index b783c6a6a6..d068c53b99 100644
--- a/eeschema/sch_plugins/kicad/sch_sexpr_plugin.h
+++ b/eeschema/sch_plugins/kicad/sch_sexpr_plugin.h
@@ -1,6 +1,3 @@
-#ifndef _SCH_SEXPR_PLUGIN_H_
-#define _SCH_SEXPR_PLUGIN_H_
-
 /*
  * This program source code file is part of KiCad, a free EDA CAD application.
  *
@@ -23,11 +20,15 @@
  * with this program.  If not, see .
  */
 
+#ifndef _SCH_SEXPR_PLUGIN_H_
+#define _SCH_SEXPR_PLUGIN_H_
+
 #include 
 #include 
 #include 
 #include 
 #include 
+#include 
 
 
 class KIWAY;
@@ -71,14 +72,16 @@ public:
         return wxT( "Eeschema s-expression" );
     }
 
-    const wxString GetFileExtension() const override
+    const PLUGIN_FILE_DESC GetSchematicFileDesc() const override
     {
-        return wxT( "kicad_sch" );
+        return PLUGIN_FILE_DESC( _HKI( "KiCad s-expression schematic files" ),
+                                 { KiCadSchematicFileExtension } );
     }
 
-    const wxString GetLibraryFileExtension() const override
+    const PLUGIN_FILE_DESC GetLibraryFileDesc() const override
     {
-        return wxT( "kicad_sym" );
+        return PLUGIN_FILE_DESC( _HKI( "KiCad symbol library files" ),
+                                 { KiCadSymbolLibFileExtension } );
     }
 
     void SetProgressReporter( PROGRESS_REPORTER* aReporter ) override
@@ -95,15 +98,15 @@ public:
 
     int GetModifyHash() const override;
 
-    SCH_SHEET* Load( const wxString& aFileName, SCHEMATIC* aSchematic,
-                     SCH_SHEET* aAppendToMe = nullptr,
-                     const STRING_UTF8_MAP* aProperties = nullptr ) override;
+    SCH_SHEET* LoadSchematicFile( const wxString& aFileName, SCHEMATIC* aSchematic,
+                                  SCH_SHEET*             aAppendToMe = nullptr,
+                                  const STRING_UTF8_MAP* aProperties = nullptr ) override;
 
     void LoadContent( LINE_READER& aReader, SCH_SHEET* aSheet,
                       int aVersion = SEXPR_SCHEMATIC_FILE_VERSION );
 
-    void Save( const wxString& aFileName, SCH_SHEET* aSheet, SCHEMATIC* aSchematic,
-               const STRING_UTF8_MAP* aProperties = nullptr ) override;
+    void SaveSchematicFile( const wxString& aFileName, SCH_SHEET* aSheet, SCHEMATIC* aSchematic,
+                            const STRING_UTF8_MAP* aProperties = nullptr ) override;
 
     void Format( SCH_SHEET* aSheet );
 
@@ -129,7 +132,6 @@ public:
     void SaveLibrary( const wxString& aLibraryPath,
                       const STRING_UTF8_MAP* aProperties = nullptr ) override;
 
-    bool CheckHeader( const wxString& aFileName ) override;
     bool IsSymbolLibWritable( const wxString& aLibraryPath ) override;
 
     void GetAvailableSymbolFields( std::vector& aNames ) override;
diff --git a/eeschema/sch_plugins/legacy/sch_legacy_plugin.cpp b/eeschema/sch_plugins/legacy/sch_legacy_plugin.cpp
index f75e5b68b9..d0c2689d55 100644
--- a/eeschema/sch_plugins/legacy/sch_legacy_plugin.cpp
+++ b/eeschema/sch_plugins/legacy/sch_legacy_plugin.cpp
@@ -121,8 +121,9 @@ void SCH_LEGACY_PLUGIN::checkpoint()
 }
 
 
-SCH_SHEET* SCH_LEGACY_PLUGIN::Load( const wxString& aFileName, SCHEMATIC* aSchematic,
-                                    SCH_SHEET* aAppendToMe, const STRING_UTF8_MAP* aProperties )
+SCH_SHEET* SCH_LEGACY_PLUGIN::LoadSchematicFile( const wxString& aFileName, SCHEMATIC* aSchematic,
+                                                 SCH_SHEET*             aAppendToMe,
+                                                 const STRING_UTF8_MAP* aProperties )
 {
     wxASSERT( !aFileName || aSchematic != nullptr );
 
@@ -1450,8 +1451,9 @@ std::shared_ptr SCH_LEGACY_PLUGIN::loadBusAlias( LINE_READER& aReader
 }
 
 
-void SCH_LEGACY_PLUGIN::Save( const wxString& aFileName, SCH_SHEET* aSheet, SCHEMATIC* aSchematic,
-                              const STRING_UTF8_MAP* aProperties )
+void SCH_LEGACY_PLUGIN::SaveSchematicFile( const wxString& aFileName, SCH_SHEET* aSheet,
+                                           SCHEMATIC*             aSchematic,
+                                           const STRING_UTF8_MAP* aProperties )
 {
     wxCHECK_RET( aSheet != nullptr, "NULL SCH_SHEET object." );
     wxCHECK_RET( !aFileName.IsEmpty(), "No schematic file name defined." );
@@ -2231,18 +2233,21 @@ void SCH_LEGACY_PLUGIN::SaveLibrary( const wxString& aLibraryPath, const STRING_
 }
 
 
-bool SCH_LEGACY_PLUGIN::CheckHeader( const wxString& aFileName )
+bool SCH_LEGACY_PLUGIN::CanReadSchematicFile( const wxString& aFileName ) const
 {
-    // Open file and check first line
-    wxTextFile tempFile;
+    if( !SCH_PLUGIN::CanReadSchematicFile( aFileName ) )
+        return false;
 
-    tempFile.Open( aFileName );
-    wxString firstline;
-    // read the first line
-    firstline = tempFile.GetFirstLine();
-    tempFile.Close();
+    return fileStartsWithPrefix( aFileName, wxT( "EESchema" ), true );
+}
 
-    return firstline.StartsWith( wxS( "EESchema" ) );
+
+bool SCH_LEGACY_PLUGIN::CanReadLibrary( const wxString& aFileName ) const
+{
+    if( !SCH_PLUGIN::CanReadLibrary( aFileName ) )
+        return false;
+
+    return fileStartsWithPrefix( aFileName, wxT( "EESchema" ), true );
 }
 
 
diff --git a/eeschema/sch_plugins/legacy/sch_legacy_plugin.h b/eeschema/sch_plugins/legacy/sch_legacy_plugin.h
index 9d3aaa39cd..657f902a01 100644
--- a/eeschema/sch_plugins/legacy/sch_legacy_plugin.h
+++ b/eeschema/sch_plugins/legacy/sch_legacy_plugin.h
@@ -1,11 +1,8 @@
-#ifndef _SCH_LEGACY_PLUGIN_H_
-#define _SCH_LEGACY_PLUGIN_H_
-
 /*
  * This program source code file is part of KiCad, a free EDA CAD application.
  *
  * Copyright (C) 2016 CERN
- * Copyright (C) 2016-2022 KiCad Developers, see change_log.txt for contributors.
+ * Copyright (C) 2016-2023 KiCad Developers, see change_log.txt for contributors.
  *
  * @author Wayne Stambaugh 
  *
@@ -23,10 +20,14 @@
  * with this program.  If not, see .
  */
 
+#ifndef _SCH_LEGACY_PLUGIN_H_
+#define _SCH_LEGACY_PLUGIN_H_
+
 #include 
 #include 
 #include 
 #include         // for EESCHEMA_VERSION definition
+#include 
 
 
 class KIWAY;
@@ -72,16 +73,22 @@ public:
         return wxT( "Eeschema-Legacy" );
     }
 
-    const wxString GetFileExtension() const override
+    const PLUGIN_FILE_DESC GetSchematicFileDesc() const override
     {
-        return wxT( "sch" );
+        return PLUGIN_FILE_DESC( _HKI( "KiCad legacy schematic files" ),
+                                 { LegacySchematicFileExtension } );
     }
 
-    const wxString GetLibraryFileExtension() const override
+    const PLUGIN_FILE_DESC GetLibraryFileDesc() const override
     {
-        return wxT( "lib" );
+        return PLUGIN_FILE_DESC( _HKI( "KiCad legacy symbol library files" ),
+                                 { LegacySymbolLibFileExtension } );
     }
 
+    bool CanReadSchematicFile( const wxString& aFileName ) const override;
+
+    bool CanReadLibrary( const wxString& aFileName ) const override;
+
     void SetProgressReporter( PROGRESS_REPORTER* aReporter ) override
     {
         m_progressReporter = aReporter;
@@ -102,15 +109,15 @@ public:
 
     int GetModifyHash() const override;
 
-    SCH_SHEET* Load( const wxString& aFileName, SCHEMATIC* aSchematic,
-                     SCH_SHEET* aAppendToMe = nullptr,
-                     const STRING_UTF8_MAP* aProperties = nullptr ) override;
+    SCH_SHEET* LoadSchematicFile( const wxString& aFileName, SCHEMATIC* aSchematic,
+                                  SCH_SHEET*             aAppendToMe = nullptr,
+                                  const STRING_UTF8_MAP* aProperties = nullptr ) override;
 
     void LoadContent( LINE_READER& aReader, SCH_SCREEN* aScreen,
                       int version = EESCHEMA_VERSION );
 
-    void Save( const wxString& aFileName, SCH_SHEET* aScreen, SCHEMATIC* aSchematic,
-               const STRING_UTF8_MAP* aProperties = nullptr ) override;
+    void SaveSchematicFile( const wxString& aFileName, SCH_SHEET* aScreen, SCHEMATIC* aSchematic,
+                            const STRING_UTF8_MAP* aProperties = nullptr ) override;
 
     void Format( SCH_SHEET* aSheet );
 
@@ -134,8 +141,7 @@ public:
                           const STRING_UTF8_MAP* aProperties = nullptr ) override;
     void SaveLibrary( const wxString& aLibraryPath,
                       const STRING_UTF8_MAP* aProperties = nullptr ) override;
-
-    bool CheckHeader( const wxString& aFileName ) override;
+    
     bool IsSymbolLibWritable( const wxString& aLibraryPath ) override;
 
     const wxString& GetError() const override { return m_error; }
diff --git a/eeschema/sch_plugins/ltspice/ltspice_sch_plugin.cpp b/eeschema/sch_plugins/ltspice/ltspice_sch_plugin.cpp
index 9735220f36..c59d7be00b 100644
--- a/eeschema/sch_plugins/ltspice/ltspice_sch_plugin.cpp
+++ b/eeschema/sch_plugins/ltspice/ltspice_sch_plugin.cpp
@@ -38,26 +38,15 @@ const wxString SCH_LTSPICE_PLUGIN::GetName() const
 }
 
 
-const wxString SCH_LTSPICE_PLUGIN::GetFileExtension() const
-{
-    return wxT( "asc" );
-}
-
-
-const wxString SCH_LTSPICE_PLUGIN::GetLibraryFileExtension() const
-{
-    return wxT( "lib" );
-}
-
-
 int SCH_LTSPICE_PLUGIN::GetModifyHash() const
 {
     return 0;
 }
 
 
-SCH_SHEET* SCH_LTSPICE_PLUGIN::Load( const wxString& aFileName, SCHEMATIC* aSchematic,
-                                     SCH_SHEET* aAppendToMe, const STRING_UTF8_MAP* aProperties )
+SCH_SHEET* SCH_LTSPICE_PLUGIN::LoadSchematicFile( const wxString& aFileName, SCHEMATIC* aSchematic,
+                                                  SCH_SHEET*             aAppendToMe,
+                                                  const STRING_UTF8_MAP* aProperties )
 {
     wxASSERT( !aFileName || aSchematic );
 
@@ -125,9 +114,3 @@ SCH_SHEET* SCH_LTSPICE_PLUGIN::Load( const wxString& aFileName, SCHEMATIC* aSche
 
     return rootSheet;
 }
-
-
-bool SCH_LTSPICE_PLUGIN::CheckHeader( const wxString& aFileName )
-{
-    return true;
-}
diff --git a/eeschema/sch_plugins/ltspice/ltspice_sch_plugin.h b/eeschema/sch_plugins/ltspice/ltspice_sch_plugin.h
index daaac20f08..154b7ce759 100644
--- a/eeschema/sch_plugins/ltspice/ltspice_sch_plugin.h
+++ b/eeschema/sch_plugins/ltspice/ltspice_sch_plugin.h
@@ -53,17 +53,21 @@ public:
         m_progressReporter = aReporter;
     }
 
-    const wxString GetFileExtension() const override;
+    const PLUGIN_FILE_DESC GetSchematicFileDesc() const override
+    {
+        return PLUGIN_FILE_DESC( _HKI( "LTspice schematic files" ), { "asc" } );
+    }
 
-    const wxString GetLibraryFileExtension() const override;
+    /*const PLUGIN_FILE_DESC GetLibraryFileDesc() const override
+    {
+        return PLUGIN_FILE_DESC( _HKI( "LTspice library files" ), { "lib" } );
+    }*/
 
     int GetModifyHash() const override;
 
-    SCH_SHEET* Load( const wxString& aFileName, SCHEMATIC* aSchematic,
-                     SCH_SHEET* aAppendToMe = nullptr,
-                     const STRING_UTF8_MAP* aProperties = nullptr ) override;
-
-    bool CheckHeader( const wxString& aFileName ) override;
+    SCH_SHEET* LoadSchematicFile( const wxString& aFileName, SCHEMATIC* aSchematic,
+                                  SCH_SHEET*             aAppendToMe = nullptr,
+                                  const STRING_UTF8_MAP* aProperties = nullptr ) override;
 
 private:
     REPORTER*          m_reporter;          // current reporter for warnings/errors
diff --git a/eeschema/sheet.cpp b/eeschema/sheet.cpp
index be1a9ba448..6e31171f75 100644
--- a/eeschema/sheet.cpp
+++ b/eeschema/sheet.cpp
@@ -111,7 +111,12 @@ bool SCH_EDIT_FRAME::LoadSheetFromFile( SCH_SHEET* aSheet, SCH_SHEET_PATH* aHier
     wxString    msg;
     wxFileName  currentSheetFileName;
     bool        libTableChanged = false;
+
     SCH_IO_MGR::SCH_FILE_T schFileType = SCH_IO_MGR::GuessPluginTypeFromSchPath( aFileName );
+
+    if( schFileType == SCH_IO_MGR::SCH_FILE_UNKNOWN )
+        schFileType = SCH_IO_MGR::SCH_KICAD;
+
     SCH_PLUGIN::SCH_PLUGIN_RELEASER pi( SCH_IO_MGR::FindPlugin( schFileType ) );
     std::unique_ptr< SCH_SHEET> tmpSheet = std::make_unique( &Schematic() );
 
@@ -134,12 +139,12 @@ bool SCH_EDIT_FRAME::LoadSheetFromFile( SCH_SHEET* aSheet, SCH_SHEET_PATH* aHier
     {
         if( aSheet->GetScreen() != nullptr )
         {
-            tmpSheet.reset( pi->Load( fullFilename, &Schematic() ) );
+            tmpSheet.reset( pi->LoadSchematicFile( fullFilename, &Schematic() ) );
         }
         else
         {
             tmpSheet->SetFileName( fullFilename );
-            pi->Load( fullFilename, &Schematic(), tmpSheet.get() );
+            pi->LoadSchematicFile( fullFilename, &Schematic(), tmpSheet.get() );
         }
 
         if( !pi->GetError().IsEmpty() )
diff --git a/eeschema/symbol_editor/symbol_editor.cpp b/eeschema/symbol_editor/symbol_editor.cpp
index 8f85e8bcf2..e381691aaa 100644
--- a/eeschema/symbol_editor/symbol_editor.cpp
+++ b/eeschema/symbol_editor/symbol_editor.cpp
@@ -1071,6 +1071,9 @@ bool SYMBOL_EDIT_FRAME::saveLibrary( const wxString& aLibrary, bool aNewFile )
     {
         fn = prj.SchSymbolLibTable()->GetFullURI( aLibrary );
         fileType = SCH_IO_MGR::GuessPluginTypeFromLibPath( fn.GetFullPath() );
+
+        if( fileType == SCH_IO_MGR::SCH_FILE_UNKNOWN )
+            fileType = SCH_IO_MGR::SCH_KICAD;
     }
 
     // Verify the user has write privileges before attempting to save the library file.
diff --git a/eeschema/symbol_editor/symbol_editor_import_export.cpp b/eeschema/symbol_editor/symbol_editor_import_export.cpp
index a48239e887..64d08e7d52 100644
--- a/eeschema/symbol_editor/symbol_editor_import_export.cpp
+++ b/eeschema/symbol_editor/symbol_editor_import_export.cpp
@@ -48,12 +48,38 @@ void SYMBOL_EDIT_FRAME::ImportSymbol()
             return;
     }
 
-    wxString wildcards = AllSymbolLibFilesWildcard()
-                         + "|" + KiCadSymbolLibFileWildcard()
-                         + "|" + LegacySymbolLibFileWildcard();
+    wxString fileFiltersStr;
+    wxString allWildcardsStr;
 
-    wxFileDialog dlg( this, _( "Import Symbol" ), m_mruPath, wxEmptyString,
-                      wildcards, wxFD_OPEN | wxFD_FILE_MUST_EXIST );
+    for( const SCH_IO_MGR::SCH_FILE_T& fileType : SCH_IO_MGR::SCH_FILE_T_vector )
+    {
+        if( fileType == SCH_IO_MGR::SCH_KICAD || fileType == SCH_IO_MGR::SCH_LEGACY )
+            continue; // this is "Import non-KiCad schematic"
+
+        SCH_PLUGIN::SCH_PLUGIN_RELEASER pi( SCH_IO_MGR::FindPlugin( fileType ) );
+
+        if( !pi )
+            continue;
+
+        const PLUGIN_FILE_DESC& desc = pi->GetLibraryFileDesc();
+
+        if( desc.m_FileExtensions.empty() )
+            continue;
+
+        if( !fileFiltersStr.IsEmpty() )
+            fileFiltersStr += wxChar( '|' );
+
+        fileFiltersStr += desc.FileFilter();
+
+        for( const std::string& ext : desc.m_FileExtensions )
+            allWildcardsStr << wxT( "*." ) << formatWildcardExt( ext ) << wxT( ";" );
+    }
+
+    fileFiltersStr = _( "All supported formats" ) + wxT( "|" ) + allWildcardsStr + wxT( "|" )
+                     + fileFiltersStr;
+
+    wxFileDialog dlg( this, _( "Import Symbol" ), m_mruPath, wxEmptyString, fileFiltersStr,
+                      wxFD_OPEN | wxFD_FILE_MUST_EXIST );
 
     if( dlg.ShowModal() == wxID_CANCEL )
         return;
@@ -69,6 +95,14 @@ void SYMBOL_EDIT_FRAME::ImportSymbol()
 
     wxArrayString symbols;
     SCH_IO_MGR::SCH_FILE_T piType = SCH_IO_MGR::GuessPluginTypeFromLibPath( fn.GetFullPath() );
+
+    if( piType == SCH_IO_MGR::SCH_FILE_UNKNOWN )
+    {
+        msg.Printf( _( "Unable to find a reader for '%s'." ), fn.GetFullPath() );
+        DisplayError( this, msg );
+        return;
+    }
+
     SCH_PLUGIN::SCH_PLUGIN_RELEASER pi( SCH_IO_MGR::FindPlugin( piType ) );
 
     // TODO dialog to select the symbol to be imported if there is more than one
@@ -140,6 +174,10 @@ void SYMBOL_EDIT_FRAME::ExportSymbol()
 
     LIB_SYMBOL* old_symbol = nullptr;
     SCH_IO_MGR::SCH_FILE_T pluginType = SCH_IO_MGR::GuessPluginTypeFromLibPath( fn.GetFullPath() );
+
+    if( pluginType == SCH_IO_MGR::SCH_FILE_UNKNOWN )
+        pluginType = SCH_IO_MGR::SCH_KICAD;
+
     SCH_PLUGIN::SCH_PLUGIN_RELEASER pi( SCH_IO_MGR::FindPlugin( pluginType ) );
 
     if( fn.FileExists() )
diff --git a/eeschema/symbol_lib_table.cpp b/eeschema/symbol_lib_table.cpp
index 0367609fe0..1d8a120425 100644
--- a/eeschema/symbol_lib_table.cpp
+++ b/eeschema/symbol_lib_table.cpp
@@ -68,8 +68,8 @@ void SYMBOL_LIB_TABLE_ROW::SetType( const wxString& aType )
 {
     type = SCH_IO_MGR::EnumFromStr( aType );
 
-    if( SCH_IO_MGR::SCH_FILE_T( -1 ) == type )
-        type = SCH_IO_MGR::SCH_LEGACY;
+    if( type == SCH_IO_MGR::SCH_FILE_UNKNOWN )
+        type = SCH_IO_MGR::SCH_KICAD;
 
     plugin.release();
 }
diff --git a/eeschema/symbol_library_manager.cpp b/eeschema/symbol_library_manager.cpp
index b8f1c6b915..d75ef5d64d 100644
--- a/eeschema/symbol_library_manager.cpp
+++ b/eeschema/symbol_library_manager.cpp
@@ -736,6 +736,10 @@ bool SYMBOL_LIBRARY_MANAGER::addLibrary( const wxString& aFilePath, bool aCreate
     wxString relPath = NormalizePath( aFilePath, &Pgm().GetLocalEnvVariables(), &m_frame.Prj() );
 
     SCH_IO_MGR::SCH_FILE_T schFileType = SCH_IO_MGR::GuessPluginTypeFromLibPath( aFilePath );
+
+    if( schFileType == SCH_IO_MGR::SCH_FILE_UNKNOWN )
+        schFileType = SCH_IO_MGR::SCH_LEGACY;
+
     wxString typeName = SCH_IO_MGR::ShowType( schFileType );
     SYMBOL_LIB_TABLE_ROW* libRow = new SYMBOL_LIB_TABLE_ROW( libName, relPath, typeName );
     aTable->InsertRow( libRow );
diff --git a/include/plugin_file_desc.h b/include/plugin_file_desc.h
new file mode 100644
index 0000000000..9dbc78ef82
--- /dev/null
+++ b/include/plugin_file_desc.h
@@ -0,0 +1,59 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2023 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
+ */
+
+#ifndef PLUGIN_FILE_DESC_H_
+#define PLUGIN_FILE_DESC_H_
+
+#include 
+#include 
+#include 
+
+/**
+* Container that describes file type info
+*/
+struct PLUGIN_FILE_DESC
+{
+    wxString                 m_Description;    ///< Description shown in the file picker dialog
+    std::vector m_FileExtensions; ///< Filter used for file pickers if m_IsFile is true
+    std::vector m_ExtensionsInDir; ///< In case of folders: extensions of files inside
+    bool                     m_IsFile;          ///< Whether the library is a folder or a file
+
+    PLUGIN_FILE_DESC( const wxString& aDescription, const std::vector& aFileExtensions,
+                      const std::vector& aExtsInFolder = {}, bool aIsFile = true ) :
+            m_Description( aDescription ),
+            m_FileExtensions( aFileExtensions ), m_ExtensionsInDir( aExtsInFolder ),
+            m_IsFile( aIsFile )
+    {
+    }
+
+    PLUGIN_FILE_DESC() : PLUGIN_FILE_DESC( wxEmptyString, {} ) {}
+
+    /**
+     * @return translated description + wildcards string for file dialogs.
+     */
+    wxString FileFilter() const;
+
+    operator bool() const { return !m_Description.empty(); }
+};
+
+#endif // PLUGIN_FILE_DESC_H_
\ No newline at end of file
diff --git a/include/wildcards_and_files_ext.h b/include/wildcards_and_files_ext.h
index 6ba1df43f7..9a50d11f1d 100644
--- a/include/wildcards_and_files_ext.h
+++ b/include/wildcards_and_files_ext.h
@@ -122,7 +122,6 @@ extern const std::string ProjectFileExtension;
 extern const std::string LegacyProjectFileExtension;
 extern const std::string ProjectLocalSettingsFileExtension;
 extern const std::string LegacySchematicFileExtension;
-extern const std::string EagleSchematicFileExtension;
 extern const std::string CadstarSchematicFileExtension;
 extern const std::string CadstarPartsLibraryFileExtension;
 extern const std::string KiCadSchematicFileExtension;
@@ -160,9 +159,6 @@ extern const std::string FootprintPlaceFileExtension;
 extern const std::string KiCadFootprintFileExtension;
 extern const std::string KiCadFootprintLibPathExtension;
 extern const std::string AltiumFootprintLibPathExtension;
-extern const std::string CadstarFootprintLibPathExtension;
-extern const std::string LtspiceSchematicExtension;
-extern const std::string LtspiceSymbolExtension;
 extern const std::string GedaPcbFootprintLibFileExtension;
 extern const std::string EagleFootprintLibPathExtension;
 extern const std::string DrawingSheetFileExtension;
@@ -206,11 +202,7 @@ extern wxString AllFilesWildcard();
 
 extern wxString FootprintAssignmentFileWildcard();
 extern wxString DrawingSheetFileWildcard();
-extern wxString SchematicSymbolFileWildcard();
 extern wxString KiCadSymbolLibFileWildcard();
-extern wxString LegacySymbolLibFileWildcard();
-extern wxString DatabaseLibFileWildcard();
-extern wxString AllSymbolLibFilesWildcard();
 extern wxString ProjectFileWildcard();
 extern wxString LegacyProjectFileWildcard();
 extern wxString AllProjectFilesWildcard();
@@ -224,12 +216,7 @@ extern wxString AllegroNetlistFileWildcard();
 extern wxString HtmlFileWildcard();
 extern wxString CsvFileWildcard();
 extern wxString PcbFileWildcard();
-extern wxString AltiumSchematicFileWildcard();
-extern wxString CadstarPartsLibraryFileWildcard();
-extern wxString CadstarSchematicArchiveFileWildcard();
 extern wxString CadstarArchiveFilesWildcard();
-extern wxString EagleSchematicFileWildcard();
-extern wxString LtspiceSchematicFileWildcard();
 extern wxString EagleFilesWildcard();
 extern wxString PdfFileWildcard();
 extern wxString PSFileWildcard();
diff --git a/pcbnew/files.cpp b/pcbnew/files.cpp
index 0e6a7b7430..118085fdd2 100644
--- a/pcbnew/files.cpp
+++ b/pcbnew/files.cpp
@@ -550,6 +550,9 @@ bool PCB_EDIT_FRAME::OpenProjectFiles( const std::vector& aFileSet, in
 
     IO_MGR::PCB_FILE_T pluginType = IO_MGR::FindPluginTypeFromBoardPath( fullFileName, aCtl );
 
+    if( pluginType == IO_MGR::FILE_TYPE_NONE )
+        return false;
+
     bool converted =  pluginType != IO_MGR::LEGACY && pluginType != IO_MGR::KICAD_SEXP;
 
     // Loading a project should only be done under carefully considered circumstances.
diff --git a/pcbnew/footprint_libraries_utils.cpp b/pcbnew/footprint_libraries_utils.cpp
index 95482453b4..c37a9e6320 100644
--- a/pcbnew/footprint_libraries_utils.cpp
+++ b/pcbnew/footprint_libraries_utils.cpp
@@ -467,8 +467,12 @@ bool PCB_BASE_EDIT_FRAME::AddLibrary( const wxString& aFilename, FP_LIB_TABLE* a
 
     if( libName.IsEmpty() )
         return false;
+
     IO_MGR::PCB_FILE_T lib_type = IO_MGR::GuessPluginTypeFromLibPath( libPath );
 
+    if( lib_type == IO_MGR::FILE_TYPE_NONE )
+        lib_type = IO_MGR::LEGACY;
+
     wxString type = IO_MGR::ShowType( lib_type );
 
     // KiCad lib is our default guess.  So it might not have the .pretty extension
diff --git a/pcbnew/io_mgr.cpp b/pcbnew/io_mgr.cpp
index 389a8518ac..0c264ffb5a 100644
--- a/pcbnew/io_mgr.cpp
+++ b/pcbnew/io_mgr.cpp
@@ -58,12 +58,6 @@
 // plugins coexisting.
 
 
-wxString PLUGIN_FILE_DESC::FileFilter() const
-{
-    return wxGetTranslation( m_Description ) + AddFileExtListToFilter( m_FileExtensions );
-}
-
-
 PLUGIN* IO_MGR::PluginFind( PCB_FILE_T aFileType )
 {
     // This implementation is subject to change, any magic is allowed here.
@@ -141,45 +135,27 @@ IO_MGR::PCB_FILE_T IO_MGR::FindPluginTypeFromBoardPath( const wxString& aFileNam
 }
 
 
-IO_MGR::PCB_FILE_T IO_MGR::GuessPluginTypeFromLibPath( const wxString& aLibPath )
+IO_MGR::PCB_FILE_T IO_MGR::GuessPluginTypeFromLibPath( const wxString& aLibPath, int aCtl )
 {
-    PCB_FILE_T  ret = KICAD_SEXP;        // default guess, unless detected otherwise.
-    wxFileName  fn( aLibPath );
+    const auto& plugins = IO_MGR::PLUGIN_REGISTRY::Instance()->AllPlugins();
 
-    if( fn.GetExt() == LegacyFootprintLibPathExtension )
+    for( const auto& plugin : plugins )
     {
-        ret = LEGACY;
-    }
-    else if( fn.GetExt() == GedaPcbFootprintLibFileExtension )
-    {
-        ret = GEDA_PCB;
-    }
-    else if( fn.GetExt() == EagleFootprintLibPathExtension )
-    {
-        ret = EAGLE;
-    }
-    else if( fn.GetExt() == AltiumFootprintLibPathExtension )
-    {
-        ret = ALTIUM_DESIGNER;
-    }
-    else if( fn.GetExt() == CadstarFootprintLibPathExtension )
-    {
-        ret = CADSTAR_PCB_ARCHIVE;
+        bool isKiCad = plugin.m_type == IO_MGR::KICAD_SEXP || plugin.m_type == IO_MGR::LEGACY;
+
+        if( ( aCtl & KICTL_KICAD_ONLY ) && !isKiCad )
+            continue;
+
+        if( ( aCtl & KICTL_NONKICAD_ONLY ) && isKiCad )
+            continue;
+
+        PLUGIN::RELEASER pi( plugin.m_createFunc() );
+
+        if( pi->CanReadFootprintLib( aLibPath ) )
+            return plugin.m_type;
     }
 
-    // Test this one anyways, even though it's the default guess, to avoid
-    // the wxURI instantiation below.
-    // We default ret to KICAD above, because somebody might have
-    // mistakenly put a pretty library into a directory other than
-    // *.pretty/ with *.kicad_mod in there., and I don't want to return -1,
-    // since we only claimed to be guessing.
-    //
-    else if( fn.GetExt() == KiCadFootprintLibPathExtension )
-    {
-        ret = KICAD_SEXP;
-    }
-
-    return ret;
+    return IO_MGR::FILE_TYPE_NONE;
 }
 
 
diff --git a/pcbnew/io_mgr.h b/pcbnew/io_mgr.h
index 7b8817cacb..011bb6e7e2 100644
--- a/pcbnew/io_mgr.h
+++ b/pcbnew/io_mgr.h
@@ -1,6 +1,3 @@
-#ifndef IO_MGR_H_
-#define IO_MGR_H_
-
 /*
  * This program source code file is part of KICAD, a free EDA CAD application.
  *
@@ -25,11 +22,15 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
  */
 
+#ifndef IO_MGR_H_
+#define IO_MGR_H_
+
 #include 
 #include 
 #include 
 #include 
 #include 
+#include 
 
 class BOARD;
 class PLUGIN;
@@ -185,7 +186,7 @@ public:
     /**
      * Return a plugin type given a footprint library's libPath.
      */
-    static PCB_FILE_T GuessPluginTypeFromLibPath( const wxString& aLibPath );
+    static PCB_FILE_T GuessPluginTypeFromLibPath( const wxString& aLibPath, int aCtl = 0 );
 
     /**
      * Find the requested #PLUGIN and if found, calls the #PLUGIN::LoadBoard() function
@@ -235,33 +236,6 @@ public:
 };
 
 
-/**
-* Container that describes file type info
-*/
-struct PLUGIN_FILE_DESC
-{
-    wxString                 m_Description;    ///< Description shown in the file picker dialog
-    std::vector m_FileExtensions; ///< Filter used for file pickers if m_IsFile is true
-    std::vector m_ExtensionsInDir; ///< In case of folders: extensions of files inside
-    bool                     m_IsFile;          ///< Whether the library is a folder or a file
-
-    PLUGIN_FILE_DESC( const wxString& aDescription, const std::vector& aFileExtensions,
-                      const std::vector& aExtsInFolder = {}, bool aIsFile = true ) :
-            m_Description( aDescription ),
-            m_FileExtensions( aFileExtensions ), m_ExtensionsInDir( aExtsInFolder ),
-            m_IsFile( aIsFile )
-    {
-    }
-
-    /**
-     * @return translated description + wildcards string for file dialogs.
-     */
-    wxString FileFilter() const;
-
-    operator bool() const { return !m_Description.empty(); }
-};
-
-
 /**
  * A base class that #BOARD loading and saving plugins should derive from.
  *
diff --git a/pcbnew/load_select_footprint.cpp b/pcbnew/load_select_footprint.cpp
index d0dd918c19..05d4c02118 100644
--- a/pcbnew/load_select_footprint.cpp
+++ b/pcbnew/load_select_footprint.cpp
@@ -425,14 +425,31 @@ bool FOOTPRINT_EDIT_FRAME::SaveLibraryAs( const wxString& aLibraryPath )
     wxBusyCursor dummy;
     wxString msg;
 
-    IO_MGR::PCB_FILE_T  dstType = IO_MGR::GuessPluginTypeFromLibPath( dstLibPath );
-    IO_MGR::PCB_FILE_T  curType = IO_MGR::GuessPluginTypeFromLibPath( curLibPath );
+    IO_MGR::PCB_FILE_T dstType = IO_MGR::GuessPluginTypeFromLibPath( dstLibPath );
+    IO_MGR::PCB_FILE_T curType = IO_MGR::GuessPluginTypeFromLibPath( curLibPath );
+
+    if( dstType == IO_MGR::FILE_TYPE_NONE )
+        dstType = IO_MGR::KICAD_SEXP;
 
     try
     {
         PLUGIN::RELEASER cur( IO_MGR::PluginFind( curType ) );
         PLUGIN::RELEASER dst( IO_MGR::PluginFind( dstType ) );
 
+        if( !cur )
+        {
+            msg = wxString::Format( _( "Unable to find a reader for '%s." ), curLibPath );
+            DisplayError( this, msg );
+            return false;
+        }
+
+        if( !dst )
+        {
+            msg = wxString::Format( _( "Unable to find a writer for '%s." ), dstLibPath );
+            DisplayError( this, msg );
+            return false;
+        }
+
         wxArrayString footprints;
 
         cur->FootprintEnumerate( footprints, curLibPath, false );
diff --git a/pcbnew/plugin.cpp b/pcbnew/plugin.cpp
index ecabefd980..c5ca8941d5 100644
--- a/pcbnew/plugin.cpp
+++ b/pcbnew/plugin.cpp
@@ -318,6 +318,7 @@ bool PLUGIN::fileStartsWithPrefix( const wxString& aFilePath, const wxString& aP
     return false;
 }
 
+
 bool PLUGIN::fileStartsWithBinaryHeader( const wxString& aFilePath, const std::vector& aHeader )
 {
     wxFileInputStream input( aFilePath );
diff --git a/pcbnew/plugins/eagle/eagle_plugin.cpp b/pcbnew/plugins/eagle/eagle_plugin.cpp
index 53a08a4bfb..87c6f75939 100644
--- a/pcbnew/plugins/eagle/eagle_plugin.cpp
+++ b/pcbnew/plugins/eagle/eagle_plugin.cpp
@@ -282,7 +282,7 @@ bool EAGLE_PLUGIN::checkHeader(const wxString& aFileName) const
         if( input.Eof() )
             return false;
 
-        if( text.ReadLine().Lower().Contains( "eagle" ) )
+        if( text.ReadLine().Contains( wxS( "Load( fn.GetFullPath(), &m_schematic ) );
+    m_schematic.SetRoot( m_pi->LoadSchematicFile( fn.GetFullPath(), &m_schematic ) );
 
     BOOST_REQUIRE_EQUAL( m_pi->GetError().IsEmpty(), true );
 
diff --git a/qa/schematic_utils/schematic_file_util.cpp b/qa/schematic_utils/schematic_file_util.cpp
index 38c7830216..cd60c69ad1 100644
--- a/qa/schematic_utils/schematic_file_util.cpp
+++ b/qa/schematic_utils/schematic_file_util.cpp
@@ -41,7 +41,7 @@ namespace KI_TEST
 void DumpSchematicToFile( SCHEMATIC& aSchematic, SCH_SHEET& aSheet, const std::string& aFilename )
 {
     SCH_SEXPR_PLUGIN io;
-    io.Save( aFilename, &aSheet, &aSchematic );
+    io.SaveSchematicFile( aFilename, &aSheet, &aSchematic );
 }
 
 void LoadSheetSchematicContents( const std::string& fileName, SCH_SHEET* sheet )
diff --git a/qa/tests/eeschema/test_sch_sheet_list.cpp b/qa/tests/eeschema/test_sch_sheet_list.cpp
index 1270ef65c1..dbf7e52017 100644
--- a/qa/tests/eeschema/test_sch_sheet_list.cpp
+++ b/qa/tests/eeschema/test_sch_sheet_list.cpp
@@ -115,17 +115,17 @@ BOOST_AUTO_TEST_CASE( TestEditPageNumbersInSharedDesign )
         newPrjFn.SetExt( ProjectFileExtension );
         BOOST_CHECK( wxCopyFile( prjFn.GetFullPath(), newPrjFn.GetFullPath() ) );
 
-        m_pi->Save( rootFn.GetFullPath(), &m_schematic.Root(), &m_schematic );
+        m_pi->SaveSchematicFile( rootFn.GetFullPath(), &m_schematic.Root(), &m_schematic );
 
         wxFileName subSheetFn = rootFn;
         BOOST_CHECK( subSheetFn.AppendDir( "ampli_ht" ) );
         BOOST_CHECK( subSheetFn.Mkdir() );
 
         subSheetFn.SetName( "ampli_ht" );
-        m_pi->Save( subSheetFn.GetFullPath(), sheets.at( 1 ).Last(), &m_schematic );
+        m_pi->SaveSchematicFile( subSheetFn.GetFullPath(), sheets.at( 1 ).Last(), &m_schematic );
 
         subSheetFn.SetName( "filter" );
-        m_pi->Save( subSheetFn.GetFullPath(), sheets.at( 2 ).Last(), &m_schematic );
+        m_pi->SaveSchematicFile( subSheetFn.GetFullPath(), sheets.at( 2 ).Last(), &m_schematic );
 
         LoadSchematic( "complex_hierarchy_shared/temp/complex_hierarchy" );