altium: Allow extraction of names of footprints stored in *.PcbLib file

See: https://gitlab.com/kicad/code/kicad/-/issues/10274
This commit is contained in:
Thomas Pointhuber 2022-01-06 15:01:05 +01:00
parent c6504628f0
commit 2cc9517cfd
9 changed files with 137 additions and 13 deletions

View File

@ -54,7 +54,8 @@ public:
template <typename Type> template <typename Type>
Type Read() Type Read()
{ {
if( GetRemainingBytes() >= sizeof( Type ) ) const size_t remainingBytes = GetRemainingBytes();
if( remainingBytes >= sizeof( Type ) )
{ {
Type val = *(Type*) ( m_pos ); Type val = *(Type*) ( m_pos );
m_pos += sizeof( Type ); m_pos += sizeof( Type );
@ -62,6 +63,7 @@ public:
} }
else else
{ {
m_pos += remainingBytes; // Ensure remaining bytes are zero
m_error = true; m_error = true;
return 0; return 0;
} }

View File

@ -156,6 +156,7 @@ const std::string FootprintPlaceFileExtension( "pos" );
const std::string KiCadFootprintLibPathExtension( "pretty" ); // this is a directory const std::string KiCadFootprintLibPathExtension( "pretty" ); // this is a directory
const std::string LegacyFootprintLibPathExtension( "mod" ); // this is a file const std::string LegacyFootprintLibPathExtension( "mod" ); // this is a file
const std::string AltiumFootprintLibPathExtension( "PcbLib" ); // this is a file
const std::string EagleFootprintLibPathExtension( "lbr" ); // this is a file const std::string EagleFootprintLibPathExtension( "lbr" ); // this is a file
const std::string GedaPcbFootprintLibFileExtension( "fp" ); // this is a file const std::string GedaPcbFootprintLibFileExtension( "fp" ); // this is a file
@ -361,6 +362,12 @@ wxString LegacyFootprintLibPathWildcard()
} }
wxString AltiumFootprintLibPathWildcard()
{
return _( "Altium PCB footprint library files" ) + AddFileExtListToFilter( { "PcbLib" } );
}
wxString EagleFootprintLibPathWildcard() wxString EagleFootprintLibPathWildcard()
{ {
return _( "Eagle ver. 6.x XML library files" ) + AddFileExtListToFilter( { "lbr" } ); return _( "Eagle ver. 6.x XML library files" ) + AddFileExtListToFilter( { "lbr" } );

View File

@ -147,6 +147,7 @@ extern const std::string ReportFileExtension;
extern const std::string FootprintPlaceFileExtension; extern const std::string FootprintPlaceFileExtension;
extern const std::string KiCadFootprintFileExtension; extern const std::string KiCadFootprintFileExtension;
extern const std::string KiCadFootprintLibPathExtension; extern const std::string KiCadFootprintLibPathExtension;
extern const std::string AltiumFootprintLibPathExtension;
extern const std::string GedaPcbFootprintLibFileExtension; extern const std::string GedaPcbFootprintLibFileExtension;
extern const std::string EagleFootprintLibPathExtension; extern const std::string EagleFootprintLibPathExtension;
extern const std::string DrawingSheetFileExtension; extern const std::string DrawingSheetFileExtension;
@ -223,6 +224,7 @@ extern wxString DocModulesFileName();
extern wxString LegacyFootprintLibPathWildcard(); extern wxString LegacyFootprintLibPathWildcard();
extern wxString KiCadFootprintLibFileWildcard(); extern wxString KiCadFootprintLibFileWildcard();
extern wxString KiCadFootprintLibPathWildcard(); extern wxString KiCadFootprintLibPathWildcard();
extern wxString AltiumFootprintLibPathWildcard();
extern wxString GedaPcbFootprintLibFileWildcard(); extern wxString GedaPcbFootprintLibFileWildcard();
extern wxString EagleFootprintLibPathWildcard(); extern wxString EagleFootprintLibPathWildcard();
extern wxString TextFileWildcard(); extern wxString TextFileWildcard();

View File

@ -84,6 +84,7 @@ struct SUPPORTED_FILE_TYPE
*/ */
enum { enum {
ID_PANEL_FPLIB_ADD_KICADMOD = ID_PCBNEW_END_LIST, ID_PANEL_FPLIB_ADD_KICADMOD = ID_PCBNEW_END_LIST,
ID_PANEL_FPLIB_ADD_ALTIUM,
ID_PANEL_FPLIB_ADD_EAGLE6, ID_PANEL_FPLIB_ADD_EAGLE6,
ID_PANEL_FPLIB_ADD_KICADLEGACY, ID_PANEL_FPLIB_ADD_KICADLEGACY,
ID_PANEL_FPLIB_ADD_GEDA, ID_PANEL_FPLIB_ADD_GEDA,
@ -111,6 +112,11 @@ static const std::map<int, SUPPORTED_FILE_TYPE>& fileTypes()
false, IO_MGR::KICAD_SEXP false, IO_MGR::KICAD_SEXP
} }
}, },
{ ID_PANEL_FPLIB_ADD_ALTIUM,
{
"Altium (*.PcbLib)", AltiumFootprintLibPathWildcard(), "", true, IO_MGR::ALTIUM_DESIGNER
}
},
{ ID_PANEL_FPLIB_ADD_EAGLE6, { ID_PANEL_FPLIB_ADD_EAGLE6,
{ {
"Eagle 6.x (*.lbr)", EagleFootprintLibPathWildcard(), "", true, IO_MGR::EAGLE "Eagle 6.x (*.lbr)", EagleFootprintLibPathWildcard(), "", true, IO_MGR::EAGLE
@ -369,6 +375,7 @@ PANEL_FP_LIB_TABLE::PANEL_FP_LIB_TABLE( DIALOG_EDIT_LIBRARY_TABLES* aParent,
choices.Add( IO_MGR::ShowType( IO_MGR::KICAD_SEXP ) ); choices.Add( IO_MGR::ShowType( IO_MGR::KICAD_SEXP ) );
choices.Add( IO_MGR::ShowType( IO_MGR::LEGACY ) ); choices.Add( IO_MGR::ShowType( IO_MGR::LEGACY ) );
choices.Add( IO_MGR::ShowType( IO_MGR::ALTIUM_DESIGNER ) );
choices.Add( IO_MGR::ShowType( IO_MGR::EAGLE ) ); choices.Add( IO_MGR::ShowType( IO_MGR::EAGLE ) );
choices.Add( IO_MGR::ShowType( IO_MGR::GEDA_PCB ) ); choices.Add( IO_MGR::ShowType( IO_MGR::GEDA_PCB ) );

View File

@ -138,6 +138,10 @@ IO_MGR::PCB_FILE_T IO_MGR::GuessPluginTypeFromLibPath( const wxString& aLibPath
{ {
ret = EAGLE; ret = EAGLE;
} }
else if( fn.GetExt() == AltiumFootprintLibPathExtension )
{
ret = ALTIUM_DESIGNER;
}
// Test this one anyways, even though it's the default guess, to avoid // Test this one anyways, even though it's the default guess, to avoid
// the wxURI instantiation below. // the wxURI instantiation below.

View File

@ -104,3 +104,37 @@ BOARD* ALTIUM_DESIGNER_PLUGIN::Load( const wxString& aFileName, BOARD* aAppendTo
return m_board; return m_board;
} }
long long ALTIUM_DESIGNER_PLUGIN::GetLibraryTimestamp( const wxString& aLibraryPath ) const
{
// File hasn't been loaded yet.
if( aLibraryPath.IsEmpty() )
{
return 0;
}
wxFileName fn( aLibraryPath );
if( fn.IsFileReadable() )
{
return fn.GetModificationTime().GetValue().GetValue();
}
else
{
return 0;
}
}
void ALTIUM_DESIGNER_PLUGIN::FootprintEnumerate( wxArrayString& aFootprintNames,
const wxString& aLibraryPath, bool aBestEfforts,
const PROPERTIES* aProperties )
{
ParseAltiumPcbLibFootprintNames( aFootprintNames, aLibraryPath );
}
FOOTPRINT* ALTIUM_DESIGNER_PLUGIN::FootprintLoad( const wxString& aLibraryPath,
const wxString& aFootprintName, bool aKeepUUID,
const PROPERTIES* aProperties )
{
return nullptr; // TODO: implement
}

View File

@ -46,11 +46,18 @@ public:
const wxString GetFileExtension() const override; const wxString GetFileExtension() const override;
long long GetLibraryTimestamp( const wxString& aLibraryPath ) const override long long GetLibraryTimestamp( const wxString& aLibraryPath ) const override;
{
// TODO? void FootprintEnumerate( wxArrayString& aFootprintNames, const wxString& aLibraryPath,
return 0; bool aBestEfforts, const PROPERTIES* aProperties = nullptr ) override;
}
FOOTPRINT* FootprintLoad( const wxString& aLibraryPath, const wxString& aFootprintName,
bool aKeepUUID = false,
const PROPERTIES* aProperties = nullptr ) override;
//bool FootprintExists( const wxString& aLibraryPath, const wxString& aFootprintName, const PROPERTIES* aProperties = nullptr );
bool IsFootprintLibWritable( const wxString& aLibraryPath ) override { return false; }
// -----</PUBLIC PLUGIN API>------------------------------------------------- // -----</PUBLIC PLUGIN API>-------------------------------------------------

View File

@ -58,16 +58,14 @@
constexpr double BOLD_FACTOR = 1.75; // CSS font-weight-normal is 400; bold is 700 constexpr double BOLD_FACTOR = 1.75; // CSS font-weight-normal is 400; bold is 700
void ParseAltiumPcb( BOARD* aBoard, const wxString& aFileName, PROGRESS_REPORTER* aProgressReporter, static std::vector<char> ReadFileIntoVector( const wxString& aFileName )
const std::map<ALTIUM_PCB_DIR, std::string>& aFileMapping )
{ {
// Open file // Open file
FILE* fp = wxFopen( aFileName, "rb" ); FILE* fp = wxFopen( aFileName, "rb" );
if( fp == nullptr ) if( fp == nullptr )
{ {
wxLogError( _( "Cannot open file '%s'." ), aFileName ); THROW_IO_ERROR( wxString::Format( _( "Cannot open file '%s'." ), aFileName ) );
return;
} }
fseek( fp, 0, SEEK_END ); fseek( fp, 0, SEEK_END );
@ -79,10 +77,11 @@ void ParseAltiumPcb( BOARD* aBoard, const wxString& aFileName, PROGRESS_REPORTER
THROW_IO_ERROR( _( "Error reading file: cannot determine length." ) ); THROW_IO_ERROR( _( "Error reading file: cannot determine length." ) );
} }
std::unique_ptr<unsigned char[]> buffer( new unsigned char[len] ); std::vector<char> buffer( len );
fseek( fp, 0, SEEK_SET ); fseek( fp, 0, SEEK_SET );
size_t bytesRead = fread( buffer.get(), sizeof( unsigned char ), len, fp ); size_t bytesRead = fread( buffer.data(), sizeof( unsigned char ), len, fp );
fclose( fp ); fclose( fp );
if( static_cast<size_t>( len ) != bytesRead ) if( static_cast<size_t>( len ) != bytesRead )
@ -90,9 +89,18 @@ void ParseAltiumPcb( BOARD* aBoard, const wxString& aFileName, PROGRESS_REPORTER
THROW_IO_ERROR( _( "Error reading file." ) ); THROW_IO_ERROR( _( "Error reading file." ) );
} }
return buffer;
}
void ParseAltiumPcb( BOARD* aBoard, const wxString& aFileName, PROGRESS_REPORTER* aProgressReporter,
const std::map<ALTIUM_PCB_DIR, std::string>& aFileMapping )
{
std::vector<char> buffer = ReadFileIntoVector( aFileName );
try try
{ {
CFB::CompoundFileReader reader( buffer.get(), bytesRead ); CFB::CompoundFileReader reader( buffer.data(), buffer.size() );
// Parse File // Parse File
ALTIUM_PCB pcb( aBoard, aProgressReporter ); ALTIUM_PCB pcb( aBoard, aProgressReporter );
@ -105,6 +113,51 @@ void ParseAltiumPcb( BOARD* aBoard, const wxString& aFileName, PROGRESS_REPORTER
} }
void ParseAltiumPcbLibFootprintNames( wxArrayString& aFootprintNames, const wxString& aLibraryPath )
{
std::vector<char> buffer = ReadFileIntoVector( aLibraryPath );
try
{
CFB::CompoundFileReader reader( buffer.data(), buffer.size() );
std::string streamName = "Library\\Data";
const CFB::COMPOUND_FILE_ENTRY* libraryData = FindStream( reader, streamName );
if( libraryData == nullptr )
{
THROW_IO_ERROR( wxString::Format( _( "File not found: '%s'." ), streamName ) );
}
ALTIUM_PARSER parser( reader, libraryData );
std::map<wxString, wxString> properties = parser.ReadProperties();
uint32_t numberOfFootprints = parser.Read<uint32_t>();
aFootprintNames.Alloc( numberOfFootprints );
for( size_t i = 0; i < numberOfFootprints; i++ )
{
parser.ReadAndSetSubrecordLength();
wxString footprintName = parser.ReadWxString();
aFootprintNames.Add( footprintName );
}
if( parser.HasParsingError() )
{
THROW_IO_ERROR( wxString::Format( "%s stream was not parsed correctly", streamName ) );
}
if( parser.GetRemainingBytes() != 0 )
{
THROW_IO_ERROR( wxString::Format( "%s stream is not fully parsed", streamName ) );
}
}
catch( CFB::CFBException& exception )
{
THROW_IO_ERROR( exception.what() );
}
}
bool IsAltiumLayerCopper( ALTIUM_LAYER aLayer ) bool IsAltiumLayerCopper( ALTIUM_LAYER aLayer )
{ {
return aLayer >= ALTIUM_LAYER::TOP_LAYER && aLayer <= ALTIUM_LAYER::BOTTOM_LAYER; return aLayer >= ALTIUM_LAYER::TOP_LAYER && aLayer <= ALTIUM_LAYER::BOTTOM_LAYER;

View File

@ -100,6 +100,14 @@ class PROGRESS_REPORTER;
void ParseAltiumPcb( BOARD* aBoard, const wxString& aFileName, PROGRESS_REPORTER* aProgressReporter, void ParseAltiumPcb( BOARD* aBoard, const wxString& aFileName, PROGRESS_REPORTER* aProgressReporter,
const std::map<ALTIUM_PCB_DIR, std::string>& aFileMapping ); const std::map<ALTIUM_PCB_DIR, std::string>& aFileMapping );
/**
* Helper method to get all footprint names in a given library
*
* @param aFootprintNames footprint names to populate
* @param aLibraryPath path to PcbLib
*/
void ParseAltiumPcbLibFootprintNames( wxArrayString& aFootprintNames,
const wxString& aLibraryPath );
namespace CFB namespace CFB
{ {