ADDED: Support Altium .IntLib (Integrated Library) in fp/sym library tables.

This commit is contained in:
Alex Shvartzkop 2023-11-17 07:20:15 +03:00
parent 4177b64c27
commit 76352234e5
6 changed files with 320 additions and 125 deletions

View File

@ -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<CFB::CompoundFileReader>( m_buffer.data(), m_buffer.size() );
}
catch( CFB::CFBException& exception )
{
THROW_IO_ERROR( exception.what() );
}
}
std::unique_ptr<ALTIUM_COMPOUND_FILE>
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<char*>( 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<ALTIUM_COMPOUND_FILE>( outStream->GetBufferStart(),
outStream->GetIntPosition() );
}
else if( buffer[0] == 0x00 )
{
return std::make_unique<ALTIUM_COMPOUND_FILE>(
reinterpret_cast<uint8_t*>( 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<wxString, wxString> 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<wxString, const CFB::COMPOUND_FILE_ENTRY*>
ALTIUM_COMPOUND_FILE::EnumDir( const std::wstring& aDir ) const
{
const CFB::COMPOUND_FILE_ENTRY* root = m_reader->GetRootEntry();
if( !root )
return {};
std::map<wxString, const CFB::COMPOUND_FILE_ENTRY*> 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<std::string>& aStreamPath ) const

View File

@ -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<ALTIUM_COMPOUND_FILE> DecodeIntLibStream( const CFB::COMPOUND_FILE_ENTRY& cfe );
std::map<wxString, wxString> ListLibFootprints();
std::tuple<wxString, const CFB::COMPOUND_FILE_ENTRY*> FindLibFootprintDirName( const wxString& aFpUnicodeName );
@ -78,6 +90,8 @@ public:
const std::string aName,
const bool aIsStream ) const;
std::map<wxString, const CFB::COMPOUND_FILE_ENTRY*> EnumDir( const std::wstring& aDir ) const;
std::map<wxString, const CFB::COMPOUND_FILE_ENTRY*> GetLibSymbols( const CFB::COMPOUND_FILE_ENTRY* aStart ) const;
private:

View File

@ -3752,20 +3752,27 @@ void SCH_ALTIUM_PLUGIN::ParseImplementation( const std::map<wxString, wxString>&
{
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() )
{
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 ));
symbol->SetFPFilters( fpFilters );
LIB_FIELD& footprintField = symbol->GetFootprintField();
footprintField.SetText( fpLibId.Format() );
}
@ -3793,16 +3800,13 @@ void SCH_ALTIUM_PLUGIN::ParseImplementation( const std::map<wxString, wxString>&
return;
}
LIB_ID fpLibId = AltiumToKiCadLibID( elem.libname, elem.name );
wxArrayString fpFilters;
fpFilters.Add( fpLibId.Format() );
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() );
}
}
@ -4028,15 +4032,41 @@ void SCH_ALTIUM_PLUGIN::ensureLoadedLibrary( const wxString& aLibraryPath,
return;
}
ALTIUM_COMPOUND_FILE altiumSchFile( aLibraryPath );
std::vector<std::unique_ptr<ALTIUM_COMPOUND_FILE>> compoundFiles;
wxFileName fileName( aLibraryPath );
m_libName = fileName.GetName();
try
{
std::map<wxString, LIB_SYMBOL*> ret = ParseLibFile( altiumSchFile );
m_libCache[aLibraryPath] = ret;
if( aLibraryPath.Lower().EndsWith( wxS( ".schlib" ) ) )
{
m_isIntLib = false;
compoundFiles.push_back( std::make_unique<ALTIUM_COMPOUND_FILE>( aLibraryPath ) );
}
else if( aLibraryPath.Lower().EndsWith( wxS( ".intlib" ) ) )
{
m_isIntLib = true;
std::unique_ptr<ALTIUM_COMPOUND_FILE> intCom =
std::make_unique<ALTIUM_COMPOUND_FILE>( aLibraryPath );
std::map<wxString, const CFB::COMPOUND_FILE_ENTRY*> schLibFiles =
intCom->EnumDir( L"SchLib" );
for( const auto& [schLibName, cfe] : schLibFiles )
compoundFiles.push_back( intCom->DecodeIntLibStream( *cfe ) );
}
std::map<wxString, LIB_SYMBOL*>& cacheMapRef = m_libCache[aLibraryPath];
for( auto& altiumSchFilePtr : compoundFiles )
{
std::map<wxString, LIB_SYMBOL*> 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;
}
}

View File

@ -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<STRING_UTF8_MAP> m_properties; // Library plugin properties.

View File

@ -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,24 +151,54 @@ long long ALTIUM_DESIGNER_PLUGIN::GetLibraryTimestamp( const wxString& aLibraryP
}
}
void ALTIUM_DESIGNER_PLUGIN::loadAltiumLibrary( const wxString& aLibraryPath )
{
try
{
auto it = m_fplibFiles.find( aLibraryPath );
if( it != m_fplibFiles.end() )
return; // Already loaded
if( aLibraryPath.Lower().EndsWith( wxS( ".pcblib" ) ) )
{
m_fplibFiles[aLibraryPath].emplace_back(
std::make_unique<ALTIUM_COMPOUND_FILE>( aLibraryPath ) );
}
else if( aLibraryPath.Lower().EndsWith( wxS( ".intlib" ) ) )
{
std::unique_ptr<ALTIUM_COMPOUND_FILE> intCom =
std::make_unique<ALTIUM_COMPOUND_FILE>( aLibraryPath );
std::map<wxString, const CFB::COMPOUND_FILE_ENTRY*> pcbLibFiles =
intCom->EnumDir( L"PCBLib" );
for( const auto& [pcbLibName, pcbCfe] : pcbLibFiles )
m_fplibFiles[aLibraryPath].push_back( intCom->DecodeIntLibStream( *pcbCfe ) );
}
}
catch( CFB::CFBException& exception )
{
THROW_IO_ERROR( exception.what() );
}
}
void ALTIUM_DESIGNER_PLUGIN::FootprintEnumerate( wxArrayString& aFootprintNames,
const wxString& aLibraryPath, bool aBestEfforts,
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<ALTIUM_COMPOUND_FILE>( aLibraryPath ) );
altiumLibFile = new_it.first->second.get();
}
else
{
altiumLibFile = it->second.get();
}
return; // No footprint libraries in file, ignore.
try
{
for( auto& altiumLibFile : it->second )
{
// Map code-page-dependent names to unicode names
std::map<wxString, wxString> patternMap = altiumLibFile->ListLibFootprints();
@ -177,8 +208,8 @@ void ALTIUM_DESIGNER_PLUGIN::FootprintEnumerate( wxArrayString& aFootprintNames
if( libraryData == nullptr )
{
THROW_IO_ERROR(
wxString::Format( _( "File not found: '%s'." ), FormatPath( streamName ) ) );
THROW_IO_ERROR( wxString::Format( _( "File not found: '%s'." ),
FormatPath( streamName ) ) );
}
ALTIUM_PARSER parser( *altiumLibFile, libraryData );
@ -202,7 +233,8 @@ void ALTIUM_DESIGNER_PLUGIN::FootprintEnumerate( wxArrayString& aFootprintNames
}
else
{
THROW_IO_ERROR( wxString::Format( "Component name not found: '%s'", fpPattern ) );
THROW_IO_ERROR(
wxString::Format( "Component name not found: '%s'", fpPattern ) );
}
parser.SkipSubrecord();
@ -216,8 +248,9 @@ void ALTIUM_DESIGNER_PLUGIN::FootprintEnumerate( wxArrayString& aFootprintNames
if( parser.GetRemainingBytes() != 0 )
{
THROW_IO_ERROR(
wxString::Format( "%s stream is not fully parsed", FormatPath( streamName ) ) );
THROW_IO_ERROR( wxString::Format( "%s stream is not fully parsed",
FormatPath( streamName ) ) );
}
}
}
catch( CFB::CFBException& exception )
@ -226,31 +259,39 @@ void ALTIUM_DESIGNER_PLUGIN::FootprintEnumerate( wxArrayString& aFootprintNames
}
}
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<ALTIUM_COMPOUND_FILE>( aLibraryPath ) );
altiumLibFile = new_it.first->second.get();
}
else
{
altiumLibFile = it->second.get();
}
THROW_IO_ERROR( _( "No footprints in library" ) );
try
{
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;
}

View File

@ -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<wxString, std::unique_ptr<ALTIUM_COMPOUND_FILE>> m_fplibFiles;
std::map<wxString, std::vector<std::unique_ptr<ALTIUM_COMPOUND_FILE>>> m_fplibFiles;
void loadAltiumLibrary( const wxString& aLibraryPath );
};
#endif // ALTIUM_DESIGNER_PLUGIN_H_