diff --git a/common/plugins/altium/altium_parser.h b/common/plugins/altium/altium_parser.h index 652ad268eb..c0ba718cc6 100644 --- a/common/plugins/altium/altium_parser.h +++ b/common/plugins/altium/altium_parser.h @@ -88,6 +88,17 @@ public: } } + template + Type Peek() + { + char* const oldPos = m_pos; + const bool oldError = m_error; + Type result = Read(); + m_pos = oldPos; + m_error = oldError; + return result; + } + wxString ReadWxString() { uint8_t len = Read(); diff --git a/pcbnew/plugins/altium/altium_designer_plugin.cpp b/pcbnew/plugins/altium/altium_designer_plugin.cpp index a9c329bf3c..95a6fc6237 100644 --- a/pcbnew/plugins/altium/altium_designer_plugin.cpp +++ b/pcbnew/plugins/altium/altium_designer_plugin.cpp @@ -139,12 +139,61 @@ void ALTIUM_DESIGNER_PLUGIN::FootprintEnumerate( wxArrayString& aFootprintNames const wxString& aLibraryPath, bool aBestEfforts, const PROPERTIES* aProperties ) { - ParseAltiumPcbLibFootprintNames( aFootprintNames, aLibraryPath ); + ALTIUM_COMPOUND_FILE altiumLibFile( aLibraryPath ); + + try + { + std::string streamName = "Library\\Data"; + const CFB::COMPOUND_FILE_ENTRY* libraryData = altiumLibFile.FindStream( streamName ); + if( libraryData == nullptr ) + { + THROW_IO_ERROR( wxString::Format( _( "File not found: '%s'." ), 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(); + wxString footprintName = parser.ReadWxString(); + aFootprintNames.Add( footprintName ); + parser.SkipSubrecord(); + } + + 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() ); + } } FOOTPRINT* ALTIUM_DESIGNER_PLUGIN::FootprintLoad( const wxString& aLibraryPath, const wxString& aFootprintName, bool aKeepUUID, const PROPERTIES* aProperties ) { - return nullptr; // TODO: implement + ALTIUM_COMPOUND_FILE altiumLibFile( aLibraryPath ); + + try + { + // Parse File + ALTIUM_PCB pcb( m_board, nullptr ); + return pcb.ParseFootprint( altiumLibFile, aFootprintName ); + } + catch( CFB::CFBException& exception ) + { + THROW_IO_ERROR( exception.what() ); + } } diff --git a/pcbnew/plugins/altium/altium_pcb.cpp b/pcbnew/plugins/altium/altium_pcb.cpp index fe7734a5d6..2befd51034 100644 --- a/pcbnew/plugins/altium/altium_pcb.cpp +++ b/pcbnew/plugins/altium/altium_pcb.cpp @@ -58,49 +58,6 @@ constexpr double BOLD_FACTOR = 1.75; // CSS font-weight-normal is 400; bold is 700 -void ParseAltiumPcbLibFootprintNames( wxArrayString& aFootprintNames, const wxString& aLibraryPath ) -{ - ALTIUM_COMPOUND_FILE altiumLibFile( aLibraryPath ); - - try - { - std::string streamName = "Library\\Data"; - const CFB::COMPOUND_FILE_ENTRY* libraryData = altiumLibFile.FindStream( streamName ); - if( libraryData == nullptr ) - { - THROW_IO_ERROR( wxString::Format( _( "File not found: '%s'." ), 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(); - 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 ) { return aLayer >= ALTIUM_LAYER::TOP_LAYER && aLayer <= ALTIUM_LAYER::BOTTOM_LAYER; @@ -630,6 +587,94 @@ void ALTIUM_PCB::Parse( const ALTIUM_COMPOUND_FILE& altiumPcbFi m_board->SetModified(); } +FOOTPRINT* ALTIUM_PCB::ParseFootprint( const ALTIUM_COMPOUND_FILE& altiumLibFile, + const wxString& aFootprintName ) +{ + std::unique_ptr footprint = std::make_unique( m_board ); + std::map stringTable; // TODO + + std::string streamName = aFootprintName.ToStdString() + "\\Data"; + const CFB::COMPOUND_FILE_ENTRY* footprintData = altiumLibFile.FindStream( streamName ); + if( footprintData == nullptr ) + { + THROW_IO_ERROR( wxString::Format( _( "File not found: '%s'." ), streamName ) ); + } + + ALTIUM_PARSER parser( altiumLibFile, footprintData ); + + parser.ReadAndSetSubrecordLength(); + wxString footprintName = parser.ReadWxString(); + parser.SkipSubrecord(); + + LIB_ID fpID = AltiumToKiCadLibID( "", footprintName ); // TODO: library name + footprint->SetFPID( fpID ); + + footprint->SetDescription( "Test Description for " + aFootprintName + " - " + footprintName ); + + while( parser.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ ) + { + ALTIUM_RECORD recordtype = static_cast( parser.Peek() ); + switch( recordtype ) + { + case ALTIUM_RECORD::ARC: + { + AARC6 arc( parser ); + break; + } + case ALTIUM_RECORD::PAD: + { + APAD6 pad( parser ); + break; + } + case ALTIUM_RECORD::VIA: + { + AVIA6 via( parser ); + break; + } + case ALTIUM_RECORD::TRACK: + { + ATRACK6 track( parser ); + ParseTracks6OfFootprint( footprint.get(), track ); + break; + } + case ALTIUM_RECORD::TEXT: + { + ATEXT6 text( parser, stringTable ); + break; + } + case ALTIUM_RECORD::FILL: + { + AFILL6 fill( parser ); + break; + } + case ALTIUM_RECORD::REGION: + { + AREGION6 region( parser, false /* TODO */ ); + break; + } + case ALTIUM_RECORD::MODEL: + { + ACOMPONENTBODY6 componentBody( parser ); + break; + } + default: + THROW_IO_ERROR( wxString::Format( _( "Record of unknown type: '%d'." ), recordtype ) ); + } + } + + 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 ) ); + } + + return footprint.release(); +} + int ALTIUM_PCB::GetNetCode( uint16_t aId ) const { if( aId == ALTIUM_NET_UNCONNECTED ) @@ -2525,6 +2570,32 @@ void ALTIUM_PCB::ParseVias6Data( const ALTIUM_COMPOUND_FILE& aAltiumPcbFile, } } +void ALTIUM_PCB::ParseTracks6OfFootprint( FOOTPRINT* footprint, const ATRACK6& elem ) +{ + PCB_LAYER_ID klayer = GetKicadLayer( elem.layer ); + + if( klayer == UNDEFINED_LAYER ) + { + /*wxLogWarning( _( "Track found on an Altium layer (%d) with no KiCad " + "equivalent. It has been moved to KiCad layer Eco1_User." ), + elem.layer );*/ + klayer = Eco1_User; + } + + if( elem.is_polygonoutline || elem.subpolyindex != ALTIUM_POLYGON_NONE ) + return; + + FP_SHAPE* shape = new FP_SHAPE( footprint, SHAPE_T::SEGMENT ); + + shape->SetStart0( elem.start ); + shape->SetEnd0( elem.end ); + shape->SetStroke( STROKE_PARAMS( elem.width, PLOT_DASH_TYPE::SOLID ) ); + shape->SetLayer( klayer ); + + shape->SetDrawCoord(); // TODO: use HelperShapeSetLocalCoord + footprint->Add( shape, ADD_MODE::APPEND ); +} + void ALTIUM_PCB::ParseTracks6Data( const ALTIUM_COMPOUND_FILE& aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY* aEntry ) { diff --git a/pcbnew/plugins/altium/altium_pcb.h b/pcbnew/plugins/altium/altium_pcb.h index eda806519d..5b916a15a2 100644 --- a/pcbnew/plugins/altium/altium_pcb.h +++ b/pcbnew/plugins/altium/altium_pcb.h @@ -88,14 +88,6 @@ class ZONE; class PCB_DIM_RADIAL; class PROGRESS_REPORTER; -/** - * 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 { @@ -118,6 +110,9 @@ public: void Parse( const ALTIUM_COMPOUND_FILE& aAltiumPcbFile, const std::map& aFileMapping ); + FOOTPRINT* ParseFootprint( const ALTIUM_COMPOUND_FILE& altiumLibFile, + const wxString& aFootprintName ); + private: void checkpoint(); @@ -156,6 +151,7 @@ private: const CFB::COMPOUND_FILE_ENTRY* aEntry ); void ParseVias6Data( const ALTIUM_COMPOUND_FILE& aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY* aEntry ); + void ParseTracks6OfFootprint( FOOTPRINT* footprint, const ATRACK6& elem ); void ParseTracks6Data( const ALTIUM_COMPOUND_FILE& aAltiumPcbFile, const CFB::COMPOUND_FILE_ENTRY* aEntry ); void ParseTexts6Data( const ALTIUM_COMPOUND_FILE& aAltiumPcbFile,