diff --git a/common/plugins/altium/altium_parser.cpp b/common/plugins/altium/altium_parser.cpp index 35b54dc3ad..54363e1098 100644 --- a/common/plugins/altium/altium_parser.cpp +++ b/common/plugins/altium/altium_parser.cpp @@ -89,6 +89,65 @@ ALTIUM_COMPOUND_FILE::ALTIUM_COMPOUND_FILE( const wxString& aFilePath ) } +ALTIUM_COMPOUND_FILE::ALTIUM_COMPOUND_FILE( const void* aBuffer, size_t aLen ) +{ + m_buffer.resize( aLen ); + memcpy( m_buffer.data(), aBuffer, aLen ); + + try + { + m_reader = std::make_unique( m_buffer.data(), m_buffer.size() ); + } + catch( CFB::CFBException& exception ) + { + THROW_IO_ERROR( exception.what() ); + } +} + + +std::unique_ptr +ALTIUM_COMPOUND_FILE::DecodeIntLibStream( const CFB::COMPOUND_FILE_ENTRY& cfe ) +{ + wxCHECK( cfe.size >= 1, nullptr ); + + size_t streamSize = cfe.size; + wxMemoryBuffer buffer( streamSize ); + buffer.SetDataLen( streamSize ); + + // read file into buffer + GetCompoundFileReader().ReadFile( &cfe, 0, reinterpret_cast( buffer.GetData() ), + streamSize ); + + // 0x02: compressed stream, 0x00: uncompressed + if( buffer[0] == 0x02 ) + { + wxMemoryInputStream memoryInputStream( buffer.GetData(), streamSize ); + memoryInputStream.SeekI( 1, wxFromStart ); + + wxZlibInputStream zlibInputStream( memoryInputStream ); + wxMemoryOutputStream decodedPcbLibStream; + decodedPcbLibStream << zlibInputStream; + + wxStreamBuffer* outStream = decodedPcbLibStream.GetOutputStreamBuffer(); + + return std::make_unique( outStream->GetBufferStart(), + outStream->GetIntPosition() ); + } + else if( buffer[0] == 0x00 ) + { + return std::make_unique( + reinterpret_cast( buffer.GetData() ) + 1, streamSize - 1 ); + } + else + { + wxFAIL_MSG( wxString::Format( "Altium IntLib unknown header: %02x %02x %02x %02x %02x", + buffer[0], buffer[1], buffer[2], buffer[3], buffer[4] ) ); + } + + return nullptr; +} + + std::map ALTIUM_COMPOUND_FILE::ListLibFootprints() { if( m_libFootprintDirNameCache.empty() ) @@ -180,6 +239,51 @@ ALTIUM_COMPOUND_FILE::GetLibSymbols( const CFB::COMPOUND_FILE_ENTRY* aStart ) co } +std::map +ALTIUM_COMPOUND_FILE::EnumDir( const std::wstring& aDir ) const +{ + const CFB::COMPOUND_FILE_ENTRY* root = m_reader->GetRootEntry(); + + if( !root ) + return {}; + + std::map files; + + m_reader->EnumFiles( + root, 1, + [&]( const CFB::COMPOUND_FILE_ENTRY* tentry, const CFB::utf16string& dir, + int level ) -> int + { + if( m_reader->IsStream( tentry ) ) + return 0; + + std::wstring dirName = UTF16ToWstring( tentry->name, tentry->nameLen ); + + if( dirName != aDir ) + return 0; + + m_reader->EnumFiles( + tentry, 1, + [&]( const CFB::COMPOUND_FILE_ENTRY* entry, const CFB::utf16string&, + int ) -> int + { + if( m_reader->IsStream( entry ) ) + { + std::wstring fileName = + UTF16ToWstring( entry->name, entry->nameLen ); + + files[fileName] = entry; + } + + return 0; + } ); + return 0; + } ); + + return files; +} + + const CFB::COMPOUND_FILE_ENTRY* ALTIUM_COMPOUND_FILE::FindStream( const CFB::COMPOUND_FILE_ENTRY* aStart, const std::vector& aStreamPath ) const diff --git a/common/plugins/altium/altium_parser.h b/common/plugins/altium/altium_parser.h index f647b45869..1d2ab599e9 100644 --- a/common/plugins/altium/altium_parser.h +++ b/common/plugins/altium/altium_parser.h @@ -60,12 +60,24 @@ public: * @param aFilePath path to file to open */ ALTIUM_COMPOUND_FILE( const wxString& aFilePath ); + + /** + * Load a CFB file from memory. Constructor might throw an IO_ERROR. + * Data is copied. + * + * @param aBuffer data buffer + * @param aLen data length + */ + ALTIUM_COMPOUND_FILE( const void* aBuffer, size_t aLen ); + ALTIUM_COMPOUND_FILE( const ALTIUM_COMPOUND_FILE& temp_obj ) = delete; ALTIUM_COMPOUND_FILE& operator=( const ALTIUM_COMPOUND_FILE& temp_obj ) = delete; ~ALTIUM_COMPOUND_FILE() = default; const CFB::CompoundFileReader& GetCompoundFileReader() const { return *m_reader; } + std::unique_ptr DecodeIntLibStream( const CFB::COMPOUND_FILE_ENTRY& cfe ); + std::map ListLibFootprints(); std::tuple FindLibFootprintDirName( const wxString& aFpUnicodeName ); @@ -78,6 +90,8 @@ public: const std::string aName, const bool aIsStream ) const; + std::map EnumDir( const std::wstring& aDir ) const; + std::map GetLibSymbols( const CFB::COMPOUND_FILE_ENTRY* aStart ) const; private: diff --git a/eeschema/sch_plugins/altium/sch_altium_plugin.cpp b/eeschema/sch_plugins/altium/sch_altium_plugin.cpp index 16be0e852b..b240042c04 100644 --- a/eeschema/sch_plugins/altium/sch_altium_plugin.cpp +++ b/eeschema/sch_plugins/altium/sch_altium_plugin.cpp @@ -3748,61 +3748,65 @@ void SCH_ALTIUM_PLUGIN::ParseImplementationList( int aIndex, void SCH_ALTIUM_PLUGIN::ParseImplementation( const std::map& aProperties, - std::vector& aSymbol ) + std::vector& aSymbol ) { ASCH_IMPLEMENTATION elem( aProperties ); - // Only get footprint, currently assigned only - if( ( elem.type == "PCBLIB" ) && ( elem.isCurrent ) ) + if( elem.type != wxS( "PCBLIB" ) ) + return; + + // For schematic files, we need to check if the model is current. + if( aSymbol.size() == 0 && !elem.isCurrent ) + return; + + // For IntLibs we want to use the same lib name for footprints + wxString libName = m_isIntLib ? m_libName : elem.libname; + + wxArrayString fpFilters; + fpFilters.Add( wxString::Format( wxS( "*%s*" ), elem.name ) ); + + // Parse the footprint fields for the library symbol + if( !aSymbol.empty() ) { - // Parse the footprint fields for the library symbol - if( !aSymbol.empty() ) + for( LIB_SYMBOL* symbol : aSymbol ) { - for( LIB_SYMBOL* symbol : aSymbol ) - { - LIB_ID fpLibId = AltiumToKiCadLibID( elem.libname, elem.name ); + LIB_ID fpLibId = AltiumToKiCadLibID( libName, elem.name ); - wxString txt; - txt.Printf( "*%s*", elem.name ); - - symbol->SetFPFilters( wxArrayString( 1, &txt )); - LIB_FIELD& footprintField = symbol->GetFootprintField(); - footprintField.SetText( fpLibId.Format() ); - } - - return; + symbol->SetFPFilters( fpFilters ); + LIB_FIELD& footprintField = symbol->GetFootprintField(); + footprintField.SetText( fpLibId.Format() ); } - const auto& implementationOwnerIt = m_altiumImplementationList.find( elem.ownerindex ); - - if( implementationOwnerIt == m_altiumImplementationList.end() ) - { - m_reporter->Report( wxString::Format( wxT( "Implementation's owner (%d) not found." ), - elem.ownerindex ), - RPT_SEVERITY_DEBUG ); - return; - } - - const auto& libSymbolIt = m_libSymbols.find( implementationOwnerIt->second ); - - if( libSymbolIt == m_libSymbols.end() ) - { - m_reporter->Report( wxString::Format( wxT( "Footprint's owner (%d) not found." ), - implementationOwnerIt->second ), - RPT_SEVERITY_DEBUG ); - return; - } - - LIB_ID fpLibId = AltiumToKiCadLibID( elem.libname, elem.name ); - wxArrayString fpFilters; - fpFilters.Add( fpLibId.Format() ); - - libSymbolIt->second->SetFPFilters( fpFilters ); // TODO: not ideal as we overwrite it - - SCH_SYMBOL* symbol = m_symbols.at( libSymbolIt->first ); - - symbol->SetFootprintFieldText( fpLibId.Format() ); + return; } + + const auto& implementationOwnerIt = m_altiumImplementationList.find( elem.ownerindex ); + + if( implementationOwnerIt == m_altiumImplementationList.end() ) + { + m_reporter->Report( wxString::Format( wxT( "Implementation's owner (%d) not found." ), + elem.ownerindex ), + RPT_SEVERITY_DEBUG ); + return; + } + + const auto& libSymbolIt = m_libSymbols.find( implementationOwnerIt->second ); + + if( libSymbolIt == m_libSymbols.end() ) + { + m_reporter->Report( wxString::Format( wxT( "Footprint's owner (%d) not found." ), + implementationOwnerIt->second ), + RPT_SEVERITY_DEBUG ); + return; + } + + LIB_ID fpLibId = AltiumToKiCadLibID( libName, elem.name ); + + libSymbolIt->second->SetFPFilters( fpFilters ); // TODO: not ideal as we overwrite it + + SCH_SYMBOL* symbol = m_symbols.at( libSymbolIt->first ); + + symbol->SetFootprintFieldText( fpLibId.Format() ); } @@ -4017,7 +4021,7 @@ long long SCH_ALTIUM_PLUGIN::getLibraryTimestamp( const wxString& aLibraryPath ) } -void SCH_ALTIUM_PLUGIN::ensureLoadedLibrary( const wxString& aLibraryPath, +void SCH_ALTIUM_PLUGIN::ensureLoadedLibrary( const wxString& aLibraryPath, const STRING_UTF8_MAP* aProperties ) { if( m_libCache.count( aLibraryPath ) ) @@ -4028,15 +4032,41 @@ void SCH_ALTIUM_PLUGIN::ensureLoadedLibrary( const wxString& aLibraryPath, return; } - ALTIUM_COMPOUND_FILE altiumSchFile( aLibraryPath ); + std::vector> compoundFiles; wxFileName fileName( aLibraryPath ); m_libName = fileName.GetName(); try { - std::map ret = ParseLibFile( altiumSchFile ); - m_libCache[aLibraryPath] = ret; + if( aLibraryPath.Lower().EndsWith( wxS( ".schlib" ) ) ) + { + m_isIntLib = false; + + compoundFiles.push_back( std::make_unique( aLibraryPath ) ); + } + else if( aLibraryPath.Lower().EndsWith( wxS( ".intlib" ) ) ) + { + m_isIntLib = true; + + std::unique_ptr intCom = + std::make_unique( aLibraryPath ); + + std::map schLibFiles = + intCom->EnumDir( L"SchLib" ); + + for( const auto& [schLibName, cfe] : schLibFiles ) + compoundFiles.push_back( intCom->DecodeIntLibStream( *cfe ) ); + } + + std::map& cacheMapRef = m_libCache[aLibraryPath]; + + for( auto& altiumSchFilePtr : compoundFiles ) + { + std::map parsed = ParseLibFile( *altiumSchFilePtr ); + cacheMapRef.insert( parsed.begin(), parsed.end() ); + } + m_timestamps[aLibraryPath] = getLibraryTimestamp( aLibraryPath ); } catch( const CFB::CFBException& exception ) @@ -4045,7 +4075,8 @@ void SCH_ALTIUM_PLUGIN::ensureLoadedLibrary( const wxString& aLibraryPath, } catch( const std::exception& exc ) { - wxLogDebug( wxT( "Unhandled exception in Altium schematic parsers: %s." ), exc.what() ); + wxFAIL_MSG( wxString::Format( wxT( "Unhandled exception in Altium schematic parsers: %s." ), + exc.what() ) ); throw; } } diff --git a/eeschema/sch_plugins/altium/sch_altium_plugin.h b/eeschema/sch_plugins/altium/sch_altium_plugin.h index 586e19bbdc..8538681959 100644 --- a/eeschema/sch_plugins/altium/sch_altium_plugin.h +++ b/eeschema/sch_plugins/altium/sch_altium_plugin.h @@ -66,7 +66,8 @@ public: const PLUGIN_FILE_DESC GetLibraryFileDesc() const override { - return PLUGIN_FILE_DESC( _HKI( "Altium schematic library files" ), { "SchLib" } ); + return PLUGIN_FILE_DESC( _HKI( "Altium Schematic Library or Integrated Library" ), + { "SchLib", "IntLib" } ); } bool CanReadSchematicFile( const wxString& aFileName ) const override; @@ -189,6 +190,7 @@ private: SCH_SHEET_PATH m_sheetPath; SCHEMATIC* m_schematic; // Passed to Load(), the schematic object being loaded wxString m_libName; // Library name to save symbols + bool m_isIntLib; // Flag to indicate Integrated Library SCH_PLUGIN::SCH_PLUGIN_RELEASER m_pi; // Plugin to create KiCad symbol library. std::unique_ptr m_properties; // Library plugin properties. diff --git a/pcbnew/plugins/altium/altium_designer_plugin.cpp b/pcbnew/plugins/altium/altium_designer_plugin.cpp index b492b33238..64c54ca8af 100644 --- a/pcbnew/plugins/altium/altium_designer_plugin.cpp +++ b/pcbnew/plugins/altium/altium_designer_plugin.cpp @@ -130,6 +130,7 @@ BOARD* ALTIUM_DESIGNER_PLUGIN::LoadBoard( const wxString& aFileName, BOARD* aApp return m_board; } + long long ALTIUM_DESIGNER_PLUGIN::GetLibraryTimestamp( const wxString& aLibraryPath ) const { // File hasn't been loaded yet. @@ -150,74 +151,31 @@ long long ALTIUM_DESIGNER_PLUGIN::GetLibraryTimestamp( const wxString& aLibraryP } } -void ALTIUM_DESIGNER_PLUGIN::FootprintEnumerate( wxArrayString& aFootprintNames, - const wxString& aLibraryPath, bool aBestEfforts, - const STRING_UTF8_MAP* aProperties ) + +void ALTIUM_DESIGNER_PLUGIN::loadAltiumLibrary( const wxString& aLibraryPath ) { - ALTIUM_COMPOUND_FILE* altiumLibFile = nullptr; - auto it = m_fplibFiles.find( aLibraryPath ); - - if( it == m_fplibFiles.end() ) - { - auto new_it = m_fplibFiles.emplace( aLibraryPath, std::make_unique( aLibraryPath ) ); - altiumLibFile = new_it.first->second.get(); - } - else - { - altiumLibFile = it->second.get(); - } - try { - // Map code-page-dependent names to unicode names - std::map patternMap = altiumLibFile->ListLibFootprints(); + auto it = m_fplibFiles.find( aLibraryPath ); - const std::vector streamName = { "Library", "Data" }; - const CFB::COMPOUND_FILE_ENTRY* libraryData = altiumLibFile->FindStream( streamName ); + if( it != m_fplibFiles.end() ) + return; // Already loaded - if( libraryData == nullptr ) + if( aLibraryPath.Lower().EndsWith( wxS( ".pcblib" ) ) ) { - THROW_IO_ERROR( - wxString::Format( _( "File not found: '%s'." ), FormatPath( streamName ) ) ); + m_fplibFiles[aLibraryPath].emplace_back( + std::make_unique( aLibraryPath ) ); } - - ALTIUM_PARSER parser( *altiumLibFile, libraryData ); - - std::map properties = parser.ReadProperties(); - - uint32_t numberOfFootprints = parser.Read(); - aFootprintNames.Alloc( numberOfFootprints ); - - for( size_t i = 0; i < numberOfFootprints; i++ ) + else if( aLibraryPath.Lower().EndsWith( wxS( ".intlib" ) ) ) { - parser.ReadAndSetSubrecordLength(); + std::unique_ptr intCom = + std::make_unique( aLibraryPath ); - wxScopedCharBuffer charBuffer = parser.ReadCharBuffer(); - wxString fpPattern( charBuffer, wxConvISO8859_1 ); + std::map pcbLibFiles = + intCom->EnumDir( L"PCBLib" ); - auto it = patternMap.find( fpPattern ); - if( it != patternMap.end() ) - { - aFootprintNames.Add( it->second ); // Proper unicode name - } - else - { - THROW_IO_ERROR( wxString::Format( "Component name not found: '%s'", fpPattern ) ); - } - - parser.SkipSubrecord(); - } - - if( parser.HasParsingError() ) - { - THROW_IO_ERROR( wxString::Format( "%s stream was not parsed correctly", - FormatPath( streamName ) ) ); - } - - if( parser.GetRemainingBytes() != 0 ) - { - THROW_IO_ERROR( - wxString::Format( "%s stream is not fully parsed", FormatPath( streamName ) ) ); + for( const auto& [pcbLibName, pcbCfe] : pcbLibFiles ) + m_fplibFiles[aLibraryPath].push_back( intCom->DecodeIntLibStream( *pcbCfe ) ); } } catch( CFB::CFBException& exception ) @@ -226,31 +184,114 @@ void ALTIUM_DESIGNER_PLUGIN::FootprintEnumerate( wxArrayString& aFootprintNames } } + +void ALTIUM_DESIGNER_PLUGIN::FootprintEnumerate( wxArrayString& aFootprintNames, + const wxString& aLibraryPath, bool aBestEfforts, + const STRING_UTF8_MAP* aProperties ) +{ + loadAltiumLibrary( aLibraryPath ); + + auto it = m_fplibFiles.find( aLibraryPath ); + + if( it == m_fplibFiles.end() ) + return; // No footprint libraries in file, ignore. + + try + { + for( auto& altiumLibFile : it->second ) + { + // Map code-page-dependent names to unicode names + std::map patternMap = altiumLibFile->ListLibFootprints(); + + const std::vector streamName = { "Library", "Data" }; + const CFB::COMPOUND_FILE_ENTRY* libraryData = altiumLibFile->FindStream( streamName ); + + if( libraryData == nullptr ) + { + THROW_IO_ERROR( wxString::Format( _( "File not found: '%s'." ), + FormatPath( streamName ) ) ); + } + + ALTIUM_PARSER parser( *altiumLibFile, libraryData ); + + std::map properties = parser.ReadProperties(); + + uint32_t numberOfFootprints = parser.Read(); + aFootprintNames.Alloc( numberOfFootprints ); + + for( size_t i = 0; i < numberOfFootprints; i++ ) + { + parser.ReadAndSetSubrecordLength(); + + wxScopedCharBuffer charBuffer = parser.ReadCharBuffer(); + wxString fpPattern( charBuffer, wxConvISO8859_1 ); + + auto it = patternMap.find( fpPattern ); + if( it != patternMap.end() ) + { + aFootprintNames.Add( it->second ); // Proper unicode name + } + else + { + THROW_IO_ERROR( + wxString::Format( "Component name not found: '%s'", fpPattern ) ); + } + + parser.SkipSubrecord(); + } + + if( parser.HasParsingError() ) + { + THROW_IO_ERROR( wxString::Format( "%s stream was not parsed correctly", + FormatPath( streamName ) ) ); + } + + if( parser.GetRemainingBytes() != 0 ) + { + THROW_IO_ERROR( wxString::Format( "%s stream is not fully parsed", + FormatPath( streamName ) ) ); + } + } + } + catch( CFB::CFBException& exception ) + { + THROW_IO_ERROR( exception.what() ); + } +} + + FOOTPRINT* ALTIUM_DESIGNER_PLUGIN::FootprintLoad( const wxString& aLibraryPath, const wxString& aFootprintName, bool aKeepUUID, const STRING_UTF8_MAP* aProperties ) { - ALTIUM_COMPOUND_FILE* altiumLibFile = nullptr; + loadAltiumLibrary( aLibraryPath ); + auto it = m_fplibFiles.find( aLibraryPath ); if( it == m_fplibFiles.end() ) - { - auto new_it = m_fplibFiles.emplace( aLibraryPath, std::make_unique( aLibraryPath ) ); - altiumLibFile = new_it.first->second.get(); - } - else - { - altiumLibFile = it->second.get(); - } + THROW_IO_ERROR( _( "No footprints in library" ) ); try { - // Parse File - ALTIUM_PCB pcb( m_board, nullptr ); - return pcb.ParseFootprint( *altiumLibFile, aFootprintName ); + for( auto& altiumLibFile : it->second ) + { + auto [dirName, fpCfe] = altiumLibFile->FindLibFootprintDirName( aFootprintName ); + + if( dirName.IsEmpty() ) + continue; + + // Parse File + ALTIUM_PCB pcb( m_board, nullptr ); + return pcb.ParseFootprint( *altiumLibFile, aFootprintName ); + } } catch( CFB::CFBException& exception ) { THROW_IO_ERROR( exception.what() ); } -} + + THROW_IO_ERROR( + wxString::Format( _( "Footprint directory not found: '%s'." ), aFootprintName ) ); + + return nullptr; +} \ No newline at end of file diff --git a/pcbnew/plugins/altium/altium_designer_plugin.h b/pcbnew/plugins/altium/altium_designer_plugin.h index f07109ddee..b673b1f654 100644 --- a/pcbnew/plugins/altium/altium_designer_plugin.h +++ b/pcbnew/plugins/altium/altium_designer_plugin.h @@ -46,7 +46,8 @@ public: PLUGIN_FILE_DESC GetFootprintLibDesc() const override { - return PLUGIN_FILE_DESC( _HKI( "Altium PCB footprint library files" ), { "PcbLib" } ); + return PLUGIN_FILE_DESC( _HKI( "Altium PCB Library or Integrated Library" ), + { "PcbLib", "IntLib" } ); } PLUGIN_FILE_DESC GetFootprintFileDesc() const override { return GetFootprintLibDesc(); } @@ -82,7 +83,9 @@ private: const STRING_UTF8_MAP* m_props; BOARD* m_board; - std::map> m_fplibFiles; + std::map>> m_fplibFiles; + + void loadAltiumLibrary( const wxString& aLibraryPath ); }; #endif // ALTIUM_DESIGNER_PLUGIN_H_