ADDED: Support Altium .IntLib (Integrated Library) in fp/sym library tables.
This commit is contained in:
parent
4177b64c27
commit
76352234e5
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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_
|
||||
|
|
Loading…
Reference in New Issue