KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pcb_io_geda.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2012 Wayne Stambaugh <[email protected]>
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 *
20 * This file contains file format knowledge derived from the gEDA/pcb project:
21 *
22 * gEDA/gaf - Copyright (C) 1998-2010 Ales Hvezda
23 * Copyright (C) 1998-2016 gEDA Contributors
24 * Lepton EDA - Copyright (C) 2017-2024 Lepton EDA Contributors
25 *
26 * Both projects are licensed under the GNU General Public License v2 or later.
27 * See https://github.com/lepton-eda/lepton-eda and
28 * https://github.com/rlutz/geda-gaf
29 */
30
36
37#include <kiplatform/io.h>
39#include <string_utils.h>
40#include <trace_helpers.h>
41#include <math/util.h> // for KiROUND
42
43#include <board.h>
45#include <font/fontconfig.h>
46#include <footprint.h>
47#include <gestfich.h>
48#include <netinfo.h>
49#include <pad.h>
50#include <macros.h>
51#include <pcb_text.h>
52#include <pcb_track.h>
53#include <pcb_shape.h>
54#include <reporter.h>
55#include <zone.h>
56#include <wx_filename.h>
57
58#include <wx/dir.h>
59#include <wx/log.h>
60#include <wx/filename.h>
61#include <wx/txtstrm.h>
62#include <wx/wfstream.h>
63#include <boost/ptr_container/ptr_map.hpp>
64#include <filter_reader.h>
65
66
67static inline long parseInt( const wxString& aValue, double aScalar )
68{
69 double value = std::numeric_limits<double>::max();
70
71 /*
72 * In 2011 gEDA/pcb introduced values with units, like "10mm" or "200mil".
73 * Unit-less values are still centimils (100000 units per inch), like with
74 * the previous format.
75 *
76 * Distinction between the even older format (mils, 1000 units per inch)
77 * and the pre-2011 format is done in ::parseFOOTPRINT already; the
78 * distinction is by whether an object definition opens with '(' or '['.
79 * All values with explicit unit open with a '[' so there's no need to
80 * consider this distinction when parsing them.
81 *
82 * The solution here is to watch for a unit and, if present, convert the
83 * value to centimils. All unit-less values are read unaltered. This way
84 * the code below can continue to consider all read values to be in mils or
85 * centimils. It also matches the strategy gEDA/pcb uses for backwards
86 * compatibility with its own layouts.
87 *
88 * Fortunately gEDA/pcb allows only units 'mil' and 'mm' in files, see
89 * definition of ALLOW_READABLE in gEDA/pcb's pcb_printf.h. So we don't
90 * have to test for all 11 units gEDA/pcb allows in user dialogs.
91 */
92 if( aValue.EndsWith( wxT( "mm" ) ) )
93 {
94 aScalar *= 100000.0 / 25.4;
95 }
96 else if( aValue.EndsWith( wxT( "mil" ) ) )
97 {
98 aScalar *= 100.;
99 }
100
101 // This conversion reports failure on strings as simple as "1000", still
102 // it returns the right result in &value. Thus, ignore the return value.
103 aValue.ToCDouble(&value);
104
105 if( value == std::numeric_limits<double>::max() ) // conversion really failed
106 THROW_IO_ERRORF( _( "Cannot convert '%s' to an integer." ), aValue.GetData() );
107
108 return KiROUND( value * aScalar );
109}
110
111
112#define TEXT_DEFAULT_SIZE ( 40 * pcbIUScale.IU_PER_MILS )
113#define OLD_GPCB_UNIT_CONV pcbIUScale.IU_PER_MILS
114#define NEW_GPCB_UNIT_CONV ( 0.01 * pcbIUScale.IU_PER_MILS )
115
116
126{
127public:
128 GPCB_FPL_CACHE_ENTRY( FOOTPRINT* aFootprint, const WX_FILENAME& aFileName ) :
129 m_filename( aFileName ),
130 m_footprint( aFootprint )
131 { }
132
134 std::unique_ptr<FOOTPRINT>& GetFootprint() { return m_footprint; }
135
136private:
138 std::unique_ptr<FOOTPRINT> m_footprint;
139};
140
141
143{
144public:
145 GPCB_FPL_CACHE( PCB_IO_GEDA* aOwner, const wxString& aLibraryPath );
146
147 wxString GetPath() const { return m_lib_path.GetPath(); }
148 bool IsWritable() const { return m_lib_path.IsOk() && m_lib_path.IsDirWritable(); }
149 boost::ptr_map<std::string, GPCB_FPL_CACHE_ENTRY>& GetFootprints() { return m_footprints; }
150
151 // Most all functions in this class throw IO_ERROR exceptions. There are no
152 // error codes nor user interface calls from here, nor in any PLUGIN.
153 // Catch these exceptions higher up please.
154
156
157 void Load();
158
159 void Remove( const wxString& aFootprintName );
160
167 static long long GetTimestamp( const wxString& aLibPath );
168
172 bool IsModified();
173
174private:
175 FOOTPRINT* parseFOOTPRINT( LINE_READER* aLineReader );
176
187 bool testFlags( const wxString& aFlag, long aMask, const wxChar* aName );
188
204 void parseParameters( wxArrayString& aParameterList, LINE_READER* aLineReader );
205
207 wxFileName m_lib_path;
208
209 boost::ptr_map<std::string, GPCB_FPL_CACHE_ENTRY> m_footprints;
211
216};
217
218
219GPCB_FPL_CACHE::GPCB_FPL_CACHE( PCB_IO_GEDA* aOwner, const wxString& aLibraryPath )
220{
221 m_owner = aOwner;
222 m_lib_path.SetPath( aLibraryPath );
224 m_cache_dirty = true;
225}
226
227
229{
230 m_cache_dirty = false;
232
233 // Note: like our .pretty footprint libraries, the gpcb footprint libraries are folders,
234 // and the footprints are the .fp files inside this folder.
235
236 wxDir dir( m_lib_path.GetPath() );
237
238 if( !dir.IsOpened() )
239 THROW_IO_ERRORF( _( "Footprint library '%s' not found." ), m_lib_path.GetPath().GetData() );
240
241 wxString fullName;
242 wxString fileSpec = wxT( "*." ) + wxString( FILEEXT::GedaPcbFootprintLibFileExtension );
243
244 // wxFileName construction is egregiously slow. Construct it once and just swap out
245 // the filename thereafter.
246 WX_FILENAME fn( m_lib_path.GetPath(), wxT( "dummyName" ) );
247
248 if( !dir.GetFirst( &fullName, fileSpec ) )
249 return;
250
251 wxString cacheErrorMsg;
252
253 do
254 {
255 fn.SetFullName( fullName );
256
257 // Queue I/O errors so only files that fail to parse don't get loaded.
258 try
259 {
260 // reader now owns fp, will close on exception or return
261 FILE_LINE_READER reader( fn.GetFullPath() );
262 std::string name = TO_UTF8( fn.GetName() );
263 FOOTPRINT* footprint = parseFOOTPRINT( &reader );
264
265 // The footprint name is the file name without the extension.
266 footprint->SetFPID( LIB_ID( wxEmptyString, fn.GetName() ) );
267 m_footprints.insert( name, new GPCB_FPL_CACHE_ENTRY( footprint, fn ) );
268 }
269 catch( const IO_ERROR& ioe )
270 {
271 if( !cacheErrorMsg.IsEmpty() )
272 cacheErrorMsg += wxT( "\n\n" );
273
274 cacheErrorMsg += ioe.What();
275 }
276 } while( dir.GetNext( &fullName ) );
277
278 if( !cacheErrorMsg.IsEmpty() )
279 THROW_IO_ERROR( cacheErrorMsg );
280}
281
282
283void GPCB_FPL_CACHE::Remove( const wxString& aFootprintName )
284{
285 std::string footprintName = TO_UTF8( aFootprintName );
286
287 auto it = m_footprints.find( footprintName );
288
289 if( it == m_footprints.end() )
290 {
291 THROW_IO_ERRORF( _( "Library '%s' has no footprint '%s'." ),
292 m_lib_path.GetPath().GetData(),
293 aFootprintName.GetData() );
294 }
295
296 // Remove the footprint from the cache and delete the footprint file from the library.
297 wxString fullPath = it->second->GetFileName().GetFullPath();
298 m_footprints.erase( footprintName );
299 wxRemoveFile( fullPath );
300}
301
302
309
310
311long long GPCB_FPL_CACHE::GetTimestamp( const wxString& aLibPath )
312{
313 wxString fileSpec = wxT( "*." ) + wxString( FILEEXT::GedaPcbFootprintLibFileExtension );
314
315 return KIPLATFORM::IO::TimestampDir( aLibPath, fileSpec );
316}
317
318
320{
321 int paramCnt;
322
323 // GPCB unit = 0.01 mils and Pcbnew 0.1.
324 double conv_unit = NEW_GPCB_UNIT_CONV;
325 wxString msg;
326 wxArrayString parameters;
327 std::unique_ptr<FOOTPRINT> footprint = std::make_unique<FOOTPRINT>( nullptr );
328
329 if( aLineReader->ReadLine() == nullptr )
330 THROW_IO_ERRORF( wxT( "%s: empty file" ), aLineReader->GetSource() );
331
332 parameters.Clear();
333 parseParameters( parameters, aLineReader );
334 paramCnt = parameters.GetCount();
335
336 /* From the Geda PCB documentation, valid Element definitions:
337 * Element [SFlags "Desc" "Name" "Value" MX MY TX TY TDir TScale TSFlags]
338 * Element (NFlags "Desc" "Name" "Value" MX MY TX TY TDir TScale TNFlags)
339 * Element (NFlags "Desc" "Name" "Value" TX TY TDir TScale TNFlags)
340 * Element (NFlags "Desc" "Name" TX TY TDir TScale TNFlags)
341 * Element ("Desc" "Name" TX TY TDir TScale TNFlags)
342 */
343
344 if( parameters[0].CmpNoCase( wxT( "Element" ) ) != 0 )
345 {
346 msg.Printf( _( "Unknown token '%s'" ), parameters[0] );
347 THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
348 aLineReader->LineNumber(), 0 );
349 }
350
351 if( paramCnt < 10 || paramCnt > 14 )
352 {
353 msg.Printf( _( "Element token contains %d parameters." ), paramCnt );
354 THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
355 aLineReader->LineNumber(), 0 );
356 }
357
358 // Test symbol after "Element": if [ units = 0.01 mils, and if ( units = 1 mil
359 if( parameters[1] == wxT( "(" ) )
360 conv_unit = OLD_GPCB_UNIT_CONV;
361
362 if( paramCnt > 10 )
363 {
364 footprint->SetLibDescription( parameters[3] );
365 footprint->SetReference( parameters[4] );
366 }
367 else
368 {
369 footprint->SetLibDescription( parameters[2] );
370 footprint->SetReference( parameters[3] );
371 }
372
373 // Read value
374 if( paramCnt > 10 )
375 footprint->SetValue( parameters[5] );
376
377 // With gEDA/pcb, value is meaningful after instantiation, only, so it's
378 // often empty in bare footprints.
379 if( footprint->Value().GetText().IsEmpty() )
380 footprint->Value().SetText( wxT( "VAL**" ) );
381
382 if( footprint->Reference().GetText().IsEmpty() )
383 footprint->Reference().SetText( wxT( "REF**" ) );
384
385 while( aLineReader->ReadLine() )
386 {
387 parameters.Clear();
388 parseParameters( parameters, aLineReader );
389
390 if( parameters.IsEmpty() || parameters[0] == wxT( "(" ) )
391 continue;
392
393 if( parameters[0] == wxT( ")" ) )
394 break;
395
396 paramCnt = parameters.GetCount();
397
398 // Test units value for a string line param (more than 3 parameters : ident [ xx ] )
399 if( paramCnt > 3 )
400 {
401 if( parameters[1] == wxT( "(" ) )
402 conv_unit = OLD_GPCB_UNIT_CONV;
403 else
404 conv_unit = NEW_GPCB_UNIT_CONV;
405 }
406
407 wxLogTrace( traceGedaPcbPlugin, wxT( "%s parameter count = %d." ),
408 parameters[0], paramCnt );
409
410 // Parse a line with format: ElementLine [X1 Y1 X2 Y2 Thickness]
411 if( parameters[0].CmpNoCase( wxT( "ElementLine" ) ) == 0 )
412 {
413 if( paramCnt != 8 )
414 {
415 msg.Printf( wxT( "ElementLine token contains %d parameters." ), paramCnt );
416 THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
417 aLineReader->LineNumber(), 0 );
418 }
419
420 PCB_SHAPE* shape = new PCB_SHAPE( footprint.get(), SHAPE_T::SEGMENT );
421 shape->SetLayer( F_SilkS );
422 shape->SetStart( VECTOR2I( static_cast<int>( parseInt( parameters[2], conv_unit ) ),
423 static_cast<int>( parseInt( parameters[3], conv_unit ) ) ) );
424 shape->SetEnd( VECTOR2I( static_cast<int>( parseInt( parameters[4], conv_unit ) ),
425 static_cast<int>( parseInt( parameters[5], conv_unit ) ) ) );
426 shape->SetStroke( STROKE_PARAMS( static_cast<int>( parseInt( parameters[6], conv_unit ) ),
428
429 shape->Rotate( { 0, 0 }, footprint->GetOrientation() );
430 shape->Move( footprint->GetPosition() );
431
432 footprint->Add( shape );
433 continue;
434 }
435
436 // Parse an arc with format: ElementArc [X Y Width Height StartAngle DeltaAngle Thickness]
437 if( parameters[0].CmpNoCase( wxT( "ElementArc" ) ) == 0 )
438 {
439 if( paramCnt != 10 )
440 {
441 msg.Printf( wxT( "ElementArc token contains %d parameters." ), paramCnt );
442 THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
443 aLineReader->LineNumber(), 0 );
444 }
445
446 // Pcbnew does know ellipse so we must have Width = Height
447 PCB_SHAPE* shape = new PCB_SHAPE( footprint.get(), SHAPE_T::ARC );
448 shape->SetLayer( F_SilkS );
449 footprint->Add( shape );
450
451 // for and arc: ibuf[3] = ibuf[4]. Pcbnew does not know ellipses
452 int radius = static_cast<int>( ( parseInt( parameters[4], conv_unit ) +
453 parseInt( parameters[5], conv_unit ) ) / 2 );
454
455 VECTOR2I centre( static_cast<int>( parseInt( parameters[2], conv_unit ) ),
456 static_cast<int>( parseInt( parameters[3], conv_unit ) ) );
457
458 // Pcbnew start angles are inverted and 180 degrees from Geda PCB angles.
459 EDA_ANGLE start_angle( static_cast<int>( parseInt( parameters[6], -10.0 ) ),
461 start_angle += ANGLE_180;
462
463 // Pcbnew delta angle direction is the opposite of Geda PCB delta angles.
464 EDA_ANGLE sweep_angle( static_cast<int>( parseInt( parameters[7], -10.0 ) ),
466
467 // Geda PCB does not support circles.
468 if( sweep_angle == -ANGLE_360 )
469 {
470 shape->SetShape( SHAPE_T::CIRCLE );
471 shape->SetCenter( centre );
472 shape->SetEnd( centre + VECTOR2I( radius, 0 ) );
473 }
474 else
475 {
476 // Calculate start point coordinate of arc
477 VECTOR2I arcStart( radius, 0 );
478 RotatePoint( arcStart, -start_angle );
479 shape->SetCenter( centre );
480 shape->SetStart( arcStart + centre );
481
482 // Angle value is clockwise in gpcb and Pcbnew.
483 shape->SetArcAngleAndEnd( sweep_angle, true );
484 }
485
486 shape->SetStroke( STROKE_PARAMS( static_cast<int>( parseInt( parameters[8], conv_unit ) ),
488
489 shape->Rotate( { 0, 0 }, footprint->GetOrientation() );
490 shape->Move( footprint->GetPosition() );
491 continue;
492 }
493
494 // Parse a Pad with no hole with format:
495 // Pad [rX1 rY1 rX2 rY2 Thickness Clearance Mask "Name" "Number" SFlags]
496 // Pad (rX1 rY1 rX2 rY2 Thickness Clearance Mask "Name" "Number" NFlags)
497 // Pad (aX1 aY1 aX2 aY2 Thickness "Name" "Number" NFlags)
498 // Pad (aX1 aY1 aX2 aY2 Thickness "Name" NFlags)
499 if( parameters[0].CmpNoCase( wxT( "Pad" ) ) == 0 )
500 {
501 if( paramCnt < 10 || paramCnt > 13 )
502 {
503 msg.Printf( wxT( "Pad token contains %d parameters." ), paramCnt );
504 THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
505 aLineReader->LineNumber(), 0 );
506 }
507
508 std::unique_ptr<PAD> pad = std::make_unique<PAD>( footprint.get() );
509
510 static const LSET pad_front( { F_Cu, F_Mask, F_Paste } );
511 static const LSET pad_back( { B_Cu, B_Mask, B_Paste } );
512
513 pad->SetPadstackMode( PADSTACK::MODE::NORMAL ); // gEDA doesn't have complex padstacks
515 pad->SetAttribute( PAD_ATTRIB::SMD );
516 pad->SetLayerSet( pad_front );
517
518 if( testFlags( parameters[paramCnt-2], 0x0080, wxT( "onsolder" ) ) )
519 pad->SetLayerSet( pad_back );
520
521 // Set the pad name:
522 // Pcbnew pad name is used for electrical connection calculations.
523 // Accordingly it should be mapped to gEDA's pin/pad number,
524 // which is used for the same purpose.
525 // gEDA also features a pin/pad "name", which is an arbitrary string
526 // and set to the pin name of the netlist on instantiation. Many gEDA
527 // bare footprints use identical strings for name and number, so this
528 // can be a bit confusing.
529 pad->SetNumber( parameters[paramCnt-3] );
530
531 int x1 = static_cast<int>( parseInt( parameters[2], conv_unit ) );
532 int x2 = static_cast<int>( parseInt( parameters[4], conv_unit ) );
533 int y1 = static_cast<int>( parseInt( parameters[3], conv_unit ) );
534 int y2 = static_cast<int>( parseInt( parameters[5], conv_unit ) );
535 int width = static_cast<int>( parseInt( parameters[6], conv_unit ) );
536 VECTOR2I delta( x2 - x1, y2 - y1 );
537 double angle = atan2( (double)delta.y, (double)delta.x );
538
539 // Get the pad clearance and the solder mask clearance.
540 if( paramCnt == 13 )
541 {
542 int clearance = static_cast<int>( parseInt( parameters[7], conv_unit ) );
543 // One of gEDA's oddities is that clearance between pad and polygon
544 // is given as the gap on both sides of the pad together, so for
545 // KiCad it has to halfed.
546 pad->SetLocalClearance( clearance / 2 );
547
548 // In GEDA, the mask value is the size of the hole in this
549 // solder mask. In Pcbnew, it is a margin, therefore the distance
550 // between the copper and the mask
551 int maskMargin = static_cast<int>( parseInt( parameters[8], conv_unit ) );
552 maskMargin = ( maskMargin - width ) / 2;
553 pad->SetLocalSolderMaskMargin( maskMargin );
554 }
555
556 // Negate angle (due to Y reversed axis)
557 EDA_ANGLE orient( -angle, RADIANS_T );
558 pad->SetOrientation( orient );
559
560 VECTOR2I padPos( ( x1 + x2 ) / 2, ( y1 + y2 ) / 2 );
561
562 pad->SetSize( PADSTACK::ALL_LAYERS, VECTOR2I( delta.EuclideanNorm() + width, width ) );
563
564 padPos += footprint->GetPosition();
565 pad->SetPosition( padPos );
566
567 if( !testFlags( parameters[paramCnt-2], 0x0100, wxT( "square" ) ) )
568 {
569 if( pad->GetSize( PADSTACK::ALL_LAYERS ).x == pad->GetSize( PADSTACK::ALL_LAYERS ).y )
571 else
573 }
574
575 bool badPad = false;
576
577 pad->Padstack().ForEachUniqueLayer(
578 [&]( PCB_LAYER_ID layer )
579 {
580 VECTOR2I padSize = pad->GetSize( layer );
581
582 if( padSize.x <= 0 || padSize.y <= 0 )
583 badPad = true;
584 } );
585
586 if( badPad )
587 {
588 m_owner->Report( wxString::Format( _( "Invalid zero-sized pad ignored in\nfile: %s" ),
589 aLineReader->GetSource() ),
591 }
592 else
593 {
594 footprint->Add( pad.release() );
595 }
596
597 continue;
598 }
599
600 // Parse a Pin with through hole with format:
601 // Pin [rX rY Thickness Clearance Mask Drill "Name" "Number" SFlags]
602 // Pin (rX rY Thickness Clearance Mask Drill "Name" "Number" NFlags)
603 // Pin (aX aY Thickness Drill "Name" "Number" NFlags)
604 // Pin (aX aY Thickness Drill "Name" NFlags)
605 // Pin (aX aY Thickness "Name" NFlags)
606 if( parameters[0].CmpNoCase( wxT( "Pin" ) ) == 0 )
607 {
608 if( paramCnt < 8 || paramCnt > 12 )
609 {
610 msg.Printf( wxT( "Pin token contains %d parameters." ), paramCnt );
611 THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
612 aLineReader->LineNumber(), 0 );
613 }
614
615 PAD* pad = new PAD( footprint.get() );
616
617 pad->SetPadstackMode( PADSTACK::MODE::NORMAL ); // gEDA doesn't have complex padstacks
619
620 static const LSET pad_set = LSET::AllCuMask() | LSET( { F_SilkS, F_Mask, B_Mask } );
621
622 pad->SetLayerSet( pad_set );
623
624 if( testFlags( parameters[paramCnt-2], 0x0100, wxT( "square" ) ) )
626
627 // Set the pad name:
628 // Pcbnew pad name is used for electrical connection calculations.
629 // Accordingly it should be mapped to gEDA's pin/pad number,
630 // which is used for the same purpose.
631 pad->SetNumber( parameters[paramCnt-3] );
632
633 VECTOR2I padPos( static_cast<int>( parseInt( parameters[2], conv_unit ) ),
634 static_cast<int>( parseInt( parameters[3], conv_unit ) ) );
635
636 int padSize = static_cast<int>( parseInt( parameters[4], conv_unit ) );
637
638 pad->SetSize( PADSTACK::ALL_LAYERS, VECTOR2I( padSize, padSize ) );
639
640 int drillSize = 0;
641
642 // Get the pad clearance, solder mask clearance, and drill size.
643 if( paramCnt == 12 )
644 {
645 int clearance = static_cast<int>( parseInt( parameters[5], conv_unit ) );
646 // One of gEDA's oddities is that clearance between pad and polygon
647 // is given as the gap on both sides of the pad together, so for
648 // KiCad it has to halfed.
649 pad->SetLocalClearance( clearance / 2 );
650
651 // In GEDA, the mask value is the size of the hole in this
652 // solder mask. In Pcbnew, it is a margin, therefore the distance
653 // between the copper and the mask
654 int maskMargin = static_cast<int>( parseInt( parameters[6], conv_unit ) );
655 maskMargin = ( maskMargin - padSize ) / 2;
656 pad->SetLocalSolderMaskMargin( maskMargin );
657
658 drillSize = static_cast<int>( parseInt( parameters[7], conv_unit ) );
659 }
660 else
661 {
662 drillSize = static_cast<int>( parseInt( parameters[5], conv_unit ) );
663 }
664
665 pad->SetDrillSize( VECTOR2I( drillSize, drillSize ) );
666
667 padPos += footprint->GetPosition();
668 pad->SetPosition( padPos );
669
670 if( pad->GetShape( PADSTACK::ALL_LAYERS ) == PAD_SHAPE::CIRCLE
671 && pad->GetSize( PADSTACK::ALL_LAYERS ).x != pad->GetSize( PADSTACK::ALL_LAYERS ).y )
672 {
674 }
675
676 footprint->Add( pad );
677 continue;
678 }
679 }
680
681 footprint->AutoPositionFields();
682
683 return footprint.release();
684}
685
686
687void GPCB_FPL_CACHE::parseParameters( wxArrayString& aParameterList, LINE_READER* aLineReader )
688{
689 char key;
690 wxString tmp;
691 char* line = aLineReader->Line();
692
693 // Last line already ready in main parser loop.
694 while( *line != 0 )
695 {
696 key = *line;
697 line++;
698
699 switch( key )
700 {
701 case '[':
702 case '(':
703 if( !tmp.IsEmpty() )
704 {
705 aParameterList.Add( tmp );
706 tmp.Clear();
707 }
708
709 tmp.Append( key );
710 aParameterList.Add( tmp );
711 tmp.Clear();
712
713 // Opening delimiter "(" after Element statement. Any other occurrence is part
714 // of a keyword definition.
715 if( aParameterList.GetCount() == 1 )
716 {
717 wxLogTrace( traceGedaPcbPlugin, dump( aParameterList ) );
718 return;
719 }
720
721 break;
722
723 case ']':
724 case ')':
725 if( !tmp.IsEmpty() )
726 {
727 aParameterList.Add( tmp );
728 tmp.Clear();
729 }
730
731 tmp.Append( key );
732 aParameterList.Add( tmp );
733 wxLogTrace( traceGedaPcbPlugin, dump( aParameterList ) );
734 return;
735
736 case '\n':
737 case '\r':
738 // Element descriptions can span multiple lines.
739 line = aLineReader->ReadLine();
741
742 case '\t':
743 case ' ':
744 if( !tmp.IsEmpty() )
745 {
746 aParameterList.Add( tmp );
747 tmp.Clear();
748 }
749
750 break;
751
752 case '"':
753 // Handle empty quotes.
754 if( *line == '"' )
755 {
756 line++;
757 tmp.Clear();
758 aParameterList.Add( wxEmptyString );
759 break;
760 }
761
762 while( *line != 0 )
763 {
764 key = *line;
765 line++;
766
767 if( key == '"' )
768 {
769 aParameterList.Add( tmp );
770 tmp.Clear();
771 break;
772 }
773 else
774 {
775 tmp.Append( key );
776 }
777 }
778
779 break;
780
781 case '#':
782 line = aLineReader->ReadLine();
783
784 if( !line )
785 return;
786
787 break;
788
789 default:
790 tmp.Append( key );
791 break;
792 }
793 }
794}
795
796
797bool GPCB_FPL_CACHE::testFlags( const wxString& aFlag, long aMask, const wxChar* aName )
798{
799 wxString number;
800
801 if( aFlag.StartsWith( wxT( "0x" ), &number ) || aFlag.StartsWith( wxT( "0X" ), &number ) )
802 {
803 long lflags;
804
805 if( number.ToLong( &lflags, 16 ) && ( lflags & aMask ) )
806 return true;
807 }
808 else if( aFlag.Contains( aName ) )
809 {
810 return true;
811 }
812
813 return false;
814}
815
816
817PCB_IO_GEDA::PCB_IO_GEDA() : PCB_IO( wxS( "gEDA PCB" ) ),
818 m_cache( nullptr ),
819 m_ctl( 0 ),
821{
822 m_reader = nullptr;
823 init( nullptr );
824}
825
826
827PCB_IO_GEDA::PCB_IO_GEDA( int aControlFlags ) : PCB_IO( wxS( "gEDA PCB" ) ),
828 m_cache( nullptr ),
829 m_ctl( aControlFlags ),
831{
832 m_reader = nullptr;
833 init( nullptr );
834}
835
836
838{
839 for( FOOTPRINT* fp : m_cachedFootprints )
840 delete fp;
841
842 delete m_cache;
843}
844
845
846void PCB_IO_GEDA::init( const std::map<std::string, UTF8>* aProperties )
847{
848 m_props = aProperties;
849}
850
851
852void PCB_IO_GEDA::validateCache( const wxString& aLibraryPath, bool checkModified )
853{
854 if( !m_cache || ( checkModified && m_cache->IsModified() ) )
855 {
856 // a spectacular episode in memory management:
857 delete m_cache;
858 m_cache = new GPCB_FPL_CACHE( this, aLibraryPath );
859 m_cache->Load();
860 }
861}
862
863
864std::unique_ptr<FOOTPRINT> PCB_IO_GEDA::ImportFootprint( const wxString& aFootprintPath, wxString& aFootprintNameOut,
865 const std::map<std::string, UTF8>* aProperties )
866{
867 wxFileName fn( aFootprintPath );
868
869 FILE_LINE_READER freader( aFootprintPath );
870 WHITESPACE_FILTER_READER reader( freader );
871
872 reader.ReadLine();
873 char* line = reader.Line();
874
875 if( !line )
876 return nullptr;
877
878 if( strncasecmp( line, "Element", strlen( "Element" ) ) != 0 )
879 return nullptr;
880
881 aFootprintNameOut = fn.GetName();
882
883 return FootprintLoad( fn.GetPath(), aFootprintNameOut );
884}
885
886
887void PCB_IO_GEDA::FootprintEnumerate( wxArrayString& aFootprintNames, const wxString& aLibraryPath,
888 bool aBestEfforts, const std::map<std::string, UTF8>* aProperties )
889{
890 wxDir dir( aLibraryPath );
891 wxString errorMsg;
892
893 if( !dir.IsOpened() )
894 {
895 if( aBestEfforts )
896 return;
897 else
898 THROW_IO_ERRORF( _( "Footprint library '%s' not found." ), aLibraryPath );
899 }
900
901 init( aProperties );
902
903 try
904 {
905 validateCache( aLibraryPath );
906 }
907 catch( const IO_ERROR& ioe )
908 {
909 errorMsg = ioe.What();
910 }
911
912 // Some of the files may have been parsed correctly so we want to add the valid files to
913 // the library.
914
915 for( const auto& footprint : m_cache->GetFootprints() )
916 aFootprintNames.Add( From_UTF8( footprint.first.c_str() ) );
917
918 if( !errorMsg.IsEmpty() && !aBestEfforts )
919 THROW_IO_ERROR( errorMsg );
920}
921
922
923const FOOTPRINT* PCB_IO_GEDA::getFootprint( const wxString& aLibraryPath,
924 const wxString& aFootprintName,
925 const std::map<std::string, UTF8>* aProperties,
926 bool checkModified )
927{
928 init( aProperties );
929
930 validateCache( aLibraryPath, checkModified );
931
932 auto it = m_cache->GetFootprints().find( TO_UTF8( aFootprintName ) );
933
934 if( it == m_cache->GetFootprints().end() )
935 return nullptr;
936
937 return it->second->GetFootprint().get();
938}
939
940
941std::unique_ptr<FOOTPRINT> PCB_IO_GEDA::FootprintLoad( const wxString& aLibraryPath, const wxString& aFootprintName,
942 bool aKeepUUID, const std::map<std::string, UTF8>* aProperties )
943{
944 // Suppress font substitution warnings (RAII - automatically restored on scope exit)
945 FONTCONFIG_REPORTER_SCOPE fontconfigScope( nullptr );
946
947 const FOOTPRINT* footprint = getFootprint( aLibraryPath, aFootprintName, aProperties, true );
948
949 if( footprint )
950 {
951 std::unique_ptr<FOOTPRINT> copy( static_cast<FOOTPRINT*>( footprint->Duplicate( IGNORE_PARENT_GROUP ) ) );
952 copy->SetParent( nullptr );
953 return copy;
954 }
955
956 return nullptr;
957}
958
959
960void PCB_IO_GEDA::FootprintDelete( const wxString& aLibraryPath, const wxString& aFootprintName,
961 const std::map<std::string, UTF8>* aProperties )
962{
963 init( aProperties );
964
965 validateCache( aLibraryPath );
966
967 if( !m_cache->IsWritable() )
968 THROW_IO_ERRORF( _( "Library '%s' is read only." ), aLibraryPath.GetData() );
969
970 m_cache->Remove( aFootprintName );
971}
972
973
974bool PCB_IO_GEDA::DeleteLibrary( const wxString& aLibraryPath, const std::map<std::string, UTF8>* aProperties )
975{
976 wxFileName fn;
977 fn.SetPath( aLibraryPath );
978
979 // Return if there is no library path to delete.
980 if( !fn.DirExists() )
981 return false;
982
983 if( !fn.IsDirWritable() )
984 THROW_IO_ERRORF( _( "Insufficient permissions to delete folder '%s'." ), aLibraryPath.GetData() );
985
986 wxDir dir( aLibraryPath );
987
988 if( dir.HasSubDirs() )
989 THROW_IO_ERRORF( _( "Library folder '%s' has unexpected sub-folders." ), aLibraryPath.GetData() );
990
991 // All the footprint files must be deleted before the directory can be deleted.
992 if( dir.HasFiles() )
993 {
994 wxFileName tmp;
995 wxArrayString files;
996
997 CollectFilesLoopSafe( aLibraryPath, files );
998
999 for( unsigned i = 0; i < files.GetCount(); i++ )
1000 {
1001 tmp = files[i];
1002
1003 if( tmp.GetExt() != FILEEXT::KiCadFootprintFileExtension )
1004 {
1005 THROW_IO_ERRORF( _( "Unexpected file '%s' found in library '%s'." ),
1006 files[i].GetData(),
1007 aLibraryPath.GetData() );
1008 }
1009 }
1010
1011 for( unsigned i = 0; i < files.GetCount(); i++ )
1012 wxRemoveFile( files[i] );
1013 }
1014
1015 wxLogTrace( traceGedaPcbPlugin, wxT( "Removing footprint library '%s'" ), aLibraryPath.GetData() );
1016
1017 // Some of the more elaborate wxRemoveFile() crap puts up its own wxLog dialog
1018 // we don't want that. we want bare metal portability with no UI here.
1019 if( !wxRmdir( aLibraryPath ) )
1020 THROW_IO_ERRORF( _( "Footprint library '%s' cannot be deleted." ), aLibraryPath.GetData() );
1021
1022 // For some reason removing a directory in Windows is not immediately updated. This delay
1023 // prevents an error when attempting to immediately recreate the same directory when over
1024 // writing an existing library.
1025#ifdef __WINDOWS__
1026 wxMilliSleep( 250L );
1027#endif
1028
1029 if( m_cache && m_cache->GetPath() == aLibraryPath )
1030 {
1031 delete m_cache;
1032 m_cache = nullptr;
1033 }
1034
1035 return true;
1036}
1037
1038
1039long long PCB_IO_GEDA::GetLibraryTimestamp( const wxString& aLibraryPath ) const
1040{
1041 return GPCB_FPL_CACHE::GetTimestamp( aLibraryPath );
1042}
1043
1044
1045bool PCB_IO_GEDA::IsLibraryWritable( const wxString& aLibraryPath )
1046{
1047 init( nullptr );
1048
1049 validateCache( aLibraryPath );
1050
1051 return m_cache->IsWritable();
1052}
1053
1054
1055// =====================================================================
1056// Board-level import
1057// =====================================================================
1058
1059
1060void PCB_IO_GEDA::parseParameters( wxArrayString& aParameterList, LINE_READER* aLineReader )
1061{
1062 char key;
1063 wxString tmp;
1064 char* line = aLineReader->Line();
1065
1066 while( *line != 0 )
1067 {
1068 key = *line;
1069 line++;
1070
1071 switch( key )
1072 {
1073 case '[':
1074 case '(':
1075 if( !tmp.IsEmpty() )
1076 {
1077 aParameterList.Add( tmp );
1078 tmp.Clear();
1079 }
1080
1081 tmp.Append( key );
1082 aParameterList.Add( tmp );
1083 tmp.Clear();
1084
1085 if( aParameterList.GetCount() == 1 )
1086 {
1087 wxLogTrace( traceGedaPcbPlugin, dump( aParameterList ) );
1088 return;
1089 }
1090
1091 break;
1092
1093 case ']':
1094 case ')':
1095 if( !tmp.IsEmpty() )
1096 {
1097 aParameterList.Add( tmp );
1098 tmp.Clear();
1099 }
1100
1101 tmp.Append( key );
1102 aParameterList.Add( tmp );
1103 wxLogTrace( traceGedaPcbPlugin, dump( aParameterList ) );
1104 return;
1105
1106 case '\n':
1107 case '\r':
1108 line = aLineReader->ReadLine();
1109
1110 if( !line )
1111 return;
1112
1114
1115 case '\t':
1116 case ' ':
1117 if( !tmp.IsEmpty() )
1118 {
1119 aParameterList.Add( tmp );
1120 tmp.Clear();
1121 }
1122
1123 break;
1124
1125 case '"':
1126 if( *line == '"' )
1127 {
1128 line++;
1129 tmp.Clear();
1130 aParameterList.Add( wxEmptyString );
1131 break;
1132 }
1133
1134 while( *line != 0 )
1135 {
1136 key = *line;
1137 line++;
1138
1139 if( key == '"' )
1140 {
1141 aParameterList.Add( tmp );
1142 tmp.Clear();
1143 break;
1144 }
1145 else
1146 {
1147 tmp.Append( key );
1148 }
1149 }
1150
1151 break;
1152
1153 case '#':
1154 line = aLineReader->ReadLine();
1155
1156 if( !line )
1157 return;
1158
1159 break;
1160
1161 default:
1162 tmp.Append( key );
1163 break;
1164 }
1165 }
1166}
1167
1168
1169bool PCB_IO_GEDA::testFlags( const wxString& aFlag, long aMask, const wxChar* aName )
1170{
1171 wxString number;
1172
1173 if( aFlag.StartsWith( wxT( "0x" ), &number ) || aFlag.StartsWith( wxT( "0X" ), &number ) )
1174 {
1175 long lflags;
1176
1177 if( number.ToLong( &lflags, 16 ) && ( lflags & aMask ) )
1178 return true;
1179 }
1180 else if( aFlag.Contains( aName ) )
1181 {
1182 return true;
1183 }
1184
1185 return false;
1186}
1187
1188
1189bool PCB_IO_GEDA::CanReadBoard( const wxString& aFileName ) const
1190{
1191 if( !PCB_IO::CanReadBoard( aFileName ) )
1192 return false;
1193
1194 wxFileInputStream input( aFileName );
1195
1196 if( !input.IsOk() )
1197 return false;
1198
1199 wxTextInputStream text( input );
1200
1201 for( int i = 0; i < 20; i++ )
1202 {
1203 if( input.Eof() )
1204 return false;
1205
1206 wxString line = text.ReadLine();
1207
1208 if( line.Contains( wxS( "PCB[" ) ) || line.Contains( wxS( "PCB(" ) ) )
1209 return true;
1210 }
1211
1212 return false;
1213}
1214
1215
1216PCB_LAYER_ID PCB_IO_GEDA::mapLayer( int aGedaLayer, const wxString& aLayerName ) const
1217{
1218 wxString name = aLayerName.Lower();
1219
1220 if( name.Contains( wxT( "outline" ) ) || name.Contains( wxT( "route" ) ) )
1221 return Edge_Cuts;
1222
1223 if( name.Contains( wxT( "silk" ) ) )
1224 {
1225 if( name.Contains( wxT( "solder" ) ) || name.Contains( wxT( "bottom" ) ) )
1226 return B_SilkS;
1227
1228 return F_SilkS;
1229 }
1230
1231 if( name.Contains( wxT( "mask" ) ) )
1232 {
1233 if( name.Contains( wxT( "solder" ) ) || name.Contains( wxT( "bottom" ) ) )
1234 return B_Mask;
1235
1236 return F_Mask;
1237 }
1238
1239 if( name.Contains( wxT( "paste" ) ) )
1240 {
1241 if( name.Contains( wxT( "solder" ) ) || name.Contains( wxT( "bottom" ) ) )
1242 return B_Paste;
1243
1244 return F_Paste;
1245 }
1246
1247 if( name.Contains( wxT( "fab" ) ) )
1248 return F_Fab;
1249
1250 // Copper layers: gEDA uses 1-based numbering. 1 = component/top, 2 = solder/bottom.
1251 if( name.Contains( wxT( "component" ) ) || name.Contains( wxT( "top" ) )
1252 || ( aGedaLayer == 1 && !name.Contains( wxT( "solder" ) ) ) )
1253 {
1254 return F_Cu;
1255 }
1256
1257 if( name.Contains( wxT( "solder" ) ) || name.Contains( wxT( "bottom" ) )
1258 || aGedaLayer == 2 )
1259 {
1260 return B_Cu;
1261 }
1262
1263 // Inner copper layers (gEDA layer numbers 3+)
1264 if( aGedaLayer >= 3 && aGedaLayer <= 16 )
1265 {
1266 int innerIdx = aGedaLayer - 3;
1267 PCB_LAYER_ID innerLayers[] = { In1_Cu, In2_Cu, In3_Cu, In4_Cu, In5_Cu, In6_Cu,
1269 In13_Cu, In14_Cu };
1270
1271 if( innerIdx < 14 )
1272 return innerLayers[innerIdx];
1273 }
1274
1275 return F_Cu;
1276}
1277
1278
1279void PCB_IO_GEDA::parseVia( wxArrayString& aParameters, double aConvUnit )
1280{
1281 // Via[X Y Thickness Clearance Mask Drill "Name" SFlags]
1282 int paramCnt = aParameters.GetCount();
1283
1284 if( paramCnt < 10 )
1285 THROW_IO_ERRORF( _( "Via token contains %d parameters, expected at least 10." ), paramCnt );
1286
1287 PCB_VIA* via = new PCB_VIA( m_board );
1288
1289 int x = static_cast<int>( parseInt( aParameters[2], aConvUnit ) );
1290 int y = static_cast<int>( parseInt( aParameters[3], aConvUnit ) );
1291 int thickness = static_cast<int>( parseInt( aParameters[4], aConvUnit ) );
1292 int drill = static_cast<int>( parseInt( aParameters[7], aConvUnit ) );
1293
1294 via->SetPosition( VECTOR2I( x, y ) );
1295 via->SetPadstackMode( PADSTACK::MODE::NORMAL ); // gEDA doesn't have complex padstacks
1296 via->SetWidth( PADSTACK::ALL_LAYERS, thickness );
1297 via->SetDrill( drill );
1298 via->SetViaType( VIATYPE::THROUGH );
1299 via->SetLayerPair( F_Cu, B_Cu );
1300 via->SetNet( NETINFO_LIST::OrphanedItem() );
1301
1302 m_board->Add( via, ADD_MODE::APPEND );
1303}
1304
1305
1306FOOTPRINT* PCB_IO_GEDA::parseElement( wxArrayString& aParameters, LINE_READER* aLineReader, double aConvUnit )
1307{
1308 int paramCnt = aParameters.GetCount();
1309 double conv_unit = aConvUnit;
1310
1311 std::unique_ptr<FOOTPRINT> footprint = std::make_unique<FOOTPRINT>( m_board );
1312
1313 if( paramCnt < 10 || paramCnt > 14 )
1314 THROW_IO_ERRORF( _( "Element token contains %d parameters." ), paramCnt );
1315
1316 // The long form has SFlags, Desc, Name, Value, MX, MY, TX, TY, TDir, TScale, TSFlags
1317 // paramCnt == 14: Element [ SFlags "Desc" "Name" "Value" MX MY TX TY TDir TScale TSFlags ]
1318 // paramCnt == 12: Element ( NFlags "Desc" "Name" "Value" TX TY TDir TScale TNFlags )
1319 int descIdx, nameIdx, valueIdx, mxIdx;
1320
1321 if( paramCnt > 10 )
1322 {
1323 descIdx = 3;
1324 nameIdx = 4;
1325 valueIdx = 5;
1326 mxIdx = 6;
1327 }
1328 else
1329 {
1330 descIdx = 2;
1331 nameIdx = 3;
1332 valueIdx = -1;
1333 mxIdx = -1;
1334 }
1335
1336 footprint->SetLibDescription( aParameters[descIdx] );
1337 footprint->SetReference( aParameters[nameIdx] );
1338
1339 if( valueIdx > 0 )
1340 footprint->SetValue( aParameters[valueIdx] );
1341
1342 if( footprint->Value().GetText().IsEmpty() )
1343 footprint->Value().SetText( wxT( "VAL**" ) );
1344
1345 if( footprint->Reference().GetText().IsEmpty() )
1346 footprint->Reference().SetText( wxT( "REF**" ) );
1347
1348 // Set footprint position from MX, MY (absolute board coordinates)
1349 if( mxIdx > 0 && paramCnt > 12 )
1350 {
1351 int mx = static_cast<int>( parseInt( aParameters[mxIdx], conv_unit ) );
1352 int my = static_cast<int>( parseInt( aParameters[mxIdx + 1], conv_unit ) );
1353 footprint->SetPosition( VECTOR2I( mx, my ) );
1354 }
1355
1356 wxArrayString parameters;
1357
1358 while( aLineReader->ReadLine() )
1359 {
1360 parameters.Clear();
1361 parseParameters( parameters, aLineReader );
1362
1363 if( parameters.IsEmpty() || parameters[0] == wxT( "(" ) )
1364 continue;
1365
1366 if( parameters[0] == wxT( ")" ) )
1367 break;
1368
1369 paramCnt = parameters.GetCount();
1370
1371 if( paramCnt > 3 )
1372 {
1373 if( parameters[1] == wxT( "(" ) )
1374 conv_unit = OLD_GPCB_UNIT_CONV;
1375 else
1376 conv_unit = NEW_GPCB_UNIT_CONV;
1377 }
1378
1379 // ElementLine [X1 Y1 X2 Y2 Thickness]
1380 if( parameters[0].CmpNoCase( wxT( "ElementLine" ) ) == 0 )
1381 {
1382 if( paramCnt != 8 )
1383 continue;
1384
1385 PCB_SHAPE* shape = new PCB_SHAPE( footprint.get(), SHAPE_T::SEGMENT );
1386 shape->SetLayer( F_SilkS );
1387 shape->SetStart( VECTOR2I( static_cast<int>( parseInt( parameters[2], conv_unit ) ),
1388 static_cast<int>( parseInt( parameters[3], conv_unit ) ) ) );
1389 shape->SetEnd( VECTOR2I( static_cast<int>( parseInt( parameters[4], conv_unit ) ),
1390 static_cast<int>( parseInt( parameters[5], conv_unit ) ) ) );
1391 shape->SetStroke( STROKE_PARAMS( static_cast<int>( parseInt( parameters[6], conv_unit ) ),
1393
1394 shape->Rotate( { 0, 0 }, footprint->GetOrientation() );
1395 shape->Move( footprint->GetPosition() );
1396
1397 footprint->Add( shape );
1398 continue;
1399 }
1400
1401 // ElementArc [X Y Width Height StartAngle DeltaAngle Thickness]
1402 if( parameters[0].CmpNoCase( wxT( "ElementArc" ) ) == 0 )
1403 {
1404 if( paramCnt != 10 )
1405 continue;
1406
1407 PCB_SHAPE* shape = new PCB_SHAPE( footprint.get(), SHAPE_T::ARC );
1408 shape->SetLayer( F_SilkS );
1409 footprint->Add( shape );
1410
1411 int radius = static_cast<int>( ( parseInt( parameters[4], conv_unit )
1412 + parseInt( parameters[5], conv_unit ) ) / 2 );
1413
1414 VECTOR2I centre( static_cast<int>( parseInt( parameters[2], conv_unit ) ),
1415 static_cast<int>( parseInt( parameters[3], conv_unit ) ) );
1416
1417 EDA_ANGLE start_angle( static_cast<int>( parseInt( parameters[6], -10.0 ) ), TENTHS_OF_A_DEGREE_T );
1418 start_angle += ANGLE_180;
1419
1420 EDA_ANGLE sweep_angle( static_cast<int>( parseInt( parameters[7], -10.0 ) ), TENTHS_OF_A_DEGREE_T );
1421
1422 if( sweep_angle == -ANGLE_360 )
1423 {
1424 shape->SetShape( SHAPE_T::CIRCLE );
1425 shape->SetCenter( centre );
1426 shape->SetEnd( centre + VECTOR2I( radius, 0 ) );
1427 }
1428 else
1429 {
1430 VECTOR2I arcStart( radius, 0 );
1431 RotatePoint( arcStart, -start_angle );
1432 shape->SetCenter( centre );
1433 shape->SetStart( arcStart + centre );
1434 shape->SetArcAngleAndEnd( sweep_angle, true );
1435 }
1436
1437 shape->SetStroke( STROKE_PARAMS( static_cast<int>( parseInt( parameters[8], conv_unit ) ),
1439
1440 shape->Rotate( { 0, 0 }, footprint->GetOrientation() );
1441 shape->Move( footprint->GetPosition() );
1442 continue;
1443 }
1444
1445 // Pad [rX1 rY1 rX2 rY2 Thickness Clearance Mask "Name" "Number" SFlags]
1446 if( parameters[0].CmpNoCase( wxT( "Pad" ) ) == 0 )
1447 {
1448 if( paramCnt < 10 || paramCnt > 13 )
1449 continue;
1450
1451 std::unique_ptr<PAD> pad = std::make_unique<PAD>( footprint.get() );
1452
1453 static const LSET pad_front( { F_Cu, F_Mask, F_Paste } );
1454 static const LSET pad_back( { B_Cu, B_Mask, B_Paste } );
1455
1456 pad->SetPadstackMode( PADSTACK::MODE::NORMAL ); // gEDA doesn't have complex padstacks
1458 pad->SetAttribute( PAD_ATTRIB::SMD );
1459 pad->SetLayerSet( pad_front );
1460
1461 if( testFlags( parameters[paramCnt - 2], 0x0080, wxT( "onsolder" ) ) )
1462 pad->SetLayerSet( pad_back );
1463
1464 pad->SetNumber( parameters[paramCnt - 3] );
1465
1466 int x1 = static_cast<int>( parseInt( parameters[2], conv_unit ) );
1467 int x2 = static_cast<int>( parseInt( parameters[4], conv_unit ) );
1468 int y1 = static_cast<int>( parseInt( parameters[3], conv_unit ) );
1469 int y2 = static_cast<int>( parseInt( parameters[5], conv_unit ) );
1470 int width = static_cast<int>( parseInt( parameters[6], conv_unit ) );
1471 VECTOR2I delta( x2 - x1, y2 - y1 );
1472 double angle = atan2( (double) delta.y, (double) delta.x );
1473
1474 if( paramCnt == 13 )
1475 {
1476 int clearance = static_cast<int>( parseInt( parameters[7], conv_unit ) );
1477 pad->SetLocalClearance( clearance / 2 );
1478
1479 int maskMargin = static_cast<int>( parseInt( parameters[8], conv_unit ) );
1480 maskMargin = ( maskMargin - width ) / 2;
1481 pad->SetLocalSolderMaskMargin( maskMargin );
1482 }
1483
1484 EDA_ANGLE orient( -angle, RADIANS_T );
1485 pad->SetOrientation( orient );
1486
1487 VECTOR2I padPos( ( x1 + x2 ) / 2, ( y1 + y2 ) / 2 );
1488
1489 pad->SetSize( PADSTACK::ALL_LAYERS, VECTOR2I( delta.EuclideanNorm() + width, width ) );
1490
1491 padPos += footprint->GetPosition();
1492 pad->SetPosition( padPos );
1493
1494 if( !testFlags( parameters[paramCnt - 2], 0x0100, wxT( "square" ) ) )
1495 {
1496 if( pad->GetSize( PADSTACK::ALL_LAYERS ).x == pad->GetSize( PADSTACK::ALL_LAYERS ).y )
1498 else
1500 }
1501
1502 bool badPad = false;
1503
1504 pad->Padstack().ForEachUniqueLayer(
1505 [&]( PCB_LAYER_ID layer )
1506 {
1507 VECTOR2I padSize = pad->GetSize( layer );
1508
1509 if( padSize.x <= 0 || padSize.y <= 0 )
1510 badPad = true;
1511 } );
1512
1513 if( badPad )
1514 {
1515 Report( wxString::Format( _( "Invalid zero-sized pad ignored in\nfile: %s" ),
1516 m_board->GetFileName() ),
1518 }
1519 else
1520 {
1521 footprint->Add( pad.release() );
1522 }
1523
1524 continue;
1525 }
1526
1527 // Pin [rX rY Thickness Clearance Mask Drill "Name" "Number" SFlags]
1528 if( parameters[0].CmpNoCase( wxT( "Pin" ) ) == 0 )
1529 {
1530 if( paramCnt < 8 || paramCnt > 12 )
1531 continue;
1532
1533 PAD* pad = new PAD( footprint.get() );
1534
1535 pad->SetPadstackMode( PADSTACK::MODE::NORMAL ); // gEDA doesn't have complex padstacks
1537
1538 static const LSET pad_set = LSET::AllCuMask() | LSET( { F_SilkS, F_Mask, B_Mask } );
1539
1540 pad->SetLayerSet( pad_set );
1541
1542 if( testFlags( parameters[paramCnt - 2], 0x0100, wxT( "square" ) ) )
1544
1545 pad->SetNumber( parameters[paramCnt - 3] );
1546
1547 VECTOR2I padPos( static_cast<int>( parseInt( parameters[2], conv_unit ) ),
1548 static_cast<int>( parseInt( parameters[3], conv_unit ) ) );
1549
1550 int padSize = static_cast<int>( parseInt( parameters[4], conv_unit ) );
1551 pad->SetSize( PADSTACK::ALL_LAYERS, VECTOR2I( padSize, padSize ) );
1552
1553 int drillSize = 0;
1554
1555 if( paramCnt == 12 )
1556 {
1557 int clearance = static_cast<int>( parseInt( parameters[5], conv_unit ) );
1558 pad->SetLocalClearance( clearance / 2 );
1559
1560 int maskMargin = static_cast<int>( parseInt( parameters[6], conv_unit ) );
1561 maskMargin = ( maskMargin - padSize ) / 2;
1562 pad->SetLocalSolderMaskMargin( maskMargin );
1563
1564 drillSize = static_cast<int>( parseInt( parameters[7], conv_unit ) );
1565 }
1566 else
1567 {
1568 drillSize = static_cast<int>( parseInt( parameters[5], conv_unit ) );
1569 }
1570
1571 pad->SetDrillSize( VECTOR2I( drillSize, drillSize ) );
1572
1573 padPos += footprint->GetPosition();
1574 pad->SetPosition( padPos );
1575
1576 if( pad->GetShape( PADSTACK::ALL_LAYERS ) == PAD_SHAPE::CIRCLE
1577 && pad->GetSize( PADSTACK::ALL_LAYERS ).x != pad->GetSize( PADSTACK::ALL_LAYERS ).y )
1578 {
1580 }
1581
1582 footprint->Add( pad );
1583 continue;
1584 }
1585 }
1586
1587 // Handle the onsolder element flag to flip bottom-side components.
1588 // In the long form, SFlags is at index 2; in the short form, NFlags is at index 2.
1589 wxString elementFlags = aParameters[2];
1590
1591 if( elementFlags.Contains( wxT( "onsolder" ) ) )
1592 footprint->Flip( footprint->GetPosition(), FLIP_DIRECTION::TOP_BOTTOM );
1593
1594 footprint->AutoPositionFields();
1595
1596 return footprint.release();
1597}
1598
1599
1600void PCB_IO_GEDA::parseLayer( wxArrayString& aParameters, LINE_READER* aLineReader, double aConvUnit )
1601{
1602 // Layer(N "name") ( ... objects ... )
1603 // In new format: Layer[N "name"]
1604 int paramCnt = aParameters.GetCount();
1605
1606 if( paramCnt < 4 )
1607 return;
1608
1609 long layerNum = 0;
1610 aParameters[2].ToLong( &layerNum );
1611
1612 wxString layerName;
1613
1614 if( paramCnt > 4 )
1615 layerName = aParameters[3];
1616
1617 PCB_LAYER_ID kicadLayer = mapLayer( (int) layerNum, layerName );
1618
1619 bool isCopperLayer = IsCopperLayer( kicadLayer );
1620
1621 if( isCopperLayer )
1622 {
1623 // gEDA layer numbers are 1-based (1=component, 2=solder, 3+=inner)
1624 int layerCount = static_cast<int>( layerNum );
1625
1626 if( layerCount > m_numCopperLayers )
1627 m_numCopperLayers = layerCount;
1628 }
1629
1630 wxArrayString parameters;
1631 double conv_unit = aConvUnit;
1632
1633 while( aLineReader->ReadLine() )
1634 {
1635 parameters.Clear();
1636 parseParameters( parameters, aLineReader );
1637
1638 if( parameters.IsEmpty() || parameters[0] == wxT( "(" ) )
1639 continue;
1640
1641 if( parameters[0] == wxT( ")" ) )
1642 break;
1643
1644 paramCnt = parameters.GetCount();
1645
1646 if( paramCnt > 3 )
1647 {
1648 if( parameters[1] == wxT( "(" ) )
1649 conv_unit = OLD_GPCB_UNIT_CONV;
1650 else
1651 conv_unit = NEW_GPCB_UNIT_CONV;
1652 }
1653
1654 // Line[X1 Y1 X2 Y2 Thickness Clearance SFlags]
1655 if( parameters[0].CmpNoCase( wxT( "Line" ) ) == 0 )
1656 {
1657 if( paramCnt < 9 )
1658 continue;
1659
1660 int x1 = static_cast<int>( parseInt( parameters[2], conv_unit ) );
1661 int y1 = static_cast<int>( parseInt( parameters[3], conv_unit ) );
1662 int x2 = static_cast<int>( parseInt( parameters[4], conv_unit ) );
1663 int y2 = static_cast<int>( parseInt( parameters[5], conv_unit ) );
1664 int thickness = static_cast<int>( parseInt( parameters[6], conv_unit ) );
1665
1666 if( isCopperLayer )
1667 {
1668 PCB_TRACK* track = new PCB_TRACK( m_board );
1669 track->SetStart( VECTOR2I( x1, y1 ) );
1670 track->SetEnd( VECTOR2I( x2, y2 ) );
1671 track->SetWidth( thickness );
1672 track->SetLayer( kicadLayer );
1674 m_board->Add( track, ADD_MODE::APPEND );
1675 }
1676 else
1677 {
1679 shape->SetStart( VECTOR2I( x1, y1 ) );
1680 shape->SetEnd( VECTOR2I( x2, y2 ) );
1681 shape->SetStroke( STROKE_PARAMS( thickness, LINE_STYLE::SOLID ) );
1682 shape->SetLayer( kicadLayer );
1683 m_board->Add( shape, ADD_MODE::APPEND );
1684 }
1685
1686 continue;
1687 }
1688
1689 // Arc[X Y Width Height Thickness Clearance StartAngle DeltaAngle SFlags]
1690 if( parameters[0].CmpNoCase( wxT( "Arc" ) ) == 0 )
1691 {
1692 if( paramCnt < 11 )
1693 continue;
1694
1695 int cx = static_cast<int>( parseInt( parameters[2], conv_unit ) );
1696 int cy = static_cast<int>( parseInt( parameters[3], conv_unit ) );
1697 int arcWidth = static_cast<int>( parseInt( parameters[4], conv_unit ) );
1698 int arcHeight = static_cast<int>( parseInt( parameters[5], conv_unit ) );
1699 int thickness = static_cast<int>( parseInt( parameters[6], conv_unit ) );
1700 int radius = ( arcWidth + arcHeight ) / 2;
1701
1702 VECTOR2I centre( cx, cy );
1703
1704 EDA_ANGLE start_angle( static_cast<int>( parseInt( parameters[8], -10.0 ) ), TENTHS_OF_A_DEGREE_T );
1705 start_angle += ANGLE_180;
1706
1707 EDA_ANGLE sweep_angle( static_cast<int>( parseInt( parameters[9], -10.0 ) ), TENTHS_OF_A_DEGREE_T );
1708
1709 if( isCopperLayer )
1710 {
1711 PCB_ARC* arc = new PCB_ARC( m_board );
1712 arc->SetLayer( kicadLayer );
1713 arc->SetWidth( thickness );
1715
1716 VECTOR2I arcStart( radius, 0 );
1717 RotatePoint( arcStart, -start_angle );
1718 arc->SetStart( arcStart + centre );
1719
1720 VECTOR2I arcMid( radius, 0 );
1721 RotatePoint( arcMid, -start_angle - sweep_angle / 2 );
1722 arc->SetMid( arcMid + centre );
1723
1724 VECTOR2I arcEnd( radius, 0 );
1725 RotatePoint( arcEnd, -start_angle - sweep_angle );
1726 arc->SetEnd( arcEnd + centre );
1727
1728 m_board->Add( arc, ADD_MODE::APPEND );
1729 }
1730 else
1731 {
1732 PCB_SHAPE* shape = new PCB_SHAPE( m_board, SHAPE_T::ARC );
1733 shape->SetLayer( kicadLayer );
1734
1735 if( sweep_angle == -ANGLE_360 )
1736 {
1737 shape->SetShape( SHAPE_T::CIRCLE );
1738 shape->SetCenter( centre );
1739 shape->SetEnd( centre + VECTOR2I( radius, 0 ) );
1740 }
1741 else
1742 {
1743 VECTOR2I arcStart( radius, 0 );
1744 RotatePoint( arcStart, -start_angle );
1745 shape->SetCenter( centre );
1746 shape->SetStart( arcStart + centre );
1747 shape->SetArcAngleAndEnd( sweep_angle, true );
1748 }
1749
1750 shape->SetStroke( STROKE_PARAMS( thickness, LINE_STYLE::SOLID ) );
1751 m_board->Add( shape, ADD_MODE::APPEND );
1752 }
1753
1754 continue;
1755 }
1756
1757 // Polygon(SFlags) ( [X Y] [X Y] ... )
1758 if( parameters[0].CmpNoCase( wxT( "Polygon" ) ) == 0 )
1759 {
1760 ZONE* zone = new ZONE( m_board );
1761 zone->SetLayer( kicadLayer );
1763 zone->SetLocalClearance( 0 );
1764 zone->SetAssignedPriority( 0 );
1765
1766 const int outlineIdx = -1;
1767 bool parsingPoints = false;
1768
1769 while( aLineReader->ReadLine() )
1770 {
1771 wxArrayString polyParams;
1772 parseParameters( polyParams, aLineReader );
1773
1774 if( polyParams.IsEmpty() )
1775 continue;
1776
1777 if( polyParams[0] == wxT( ")" ) )
1778 break;
1779
1780 if( polyParams[0] == wxT( "(" ) )
1781 {
1782 parsingPoints = true;
1783 continue;
1784 }
1785
1786 if( !parsingPoints )
1787 continue;
1788
1789 // Parse coordinate pairs [X Y]
1790 for( size_t i = 0; i < polyParams.GetCount(); i++ )
1791 {
1792 if( polyParams[i] == wxT( "[" ) && i + 2 < polyParams.GetCount() )
1793 {
1794 int px = static_cast<int>( parseInt( polyParams[i + 1], conv_unit ) );
1795 int py = static_cast<int>( parseInt( polyParams[i + 2], conv_unit ) );
1796 zone->AppendCorner( VECTOR2I( px, py ), outlineIdx );
1797 i += 3; // skip past X, Y, ]
1798 }
1799 }
1800 }
1801
1802 if( zone->GetNumCorners() >= 3 )
1803 {
1804 zone->SetIsFilled( false );
1805 m_board->Add( zone, ADD_MODE::APPEND );
1806 }
1807 else
1808 {
1809 delete zone;
1810 }
1811
1812 continue;
1813 }
1814
1815 // Text[X Y Direction Scale "String" SFlags]
1816 if( parameters[0].CmpNoCase( wxT( "Text" ) ) == 0 )
1817 {
1818 if( paramCnt < 8 )
1819 continue;
1820
1821 PCB_TEXT* text = new PCB_TEXT( m_board );
1822 text->SetLayer( kicadLayer );
1823
1824 int tx = static_cast<int>( parseInt( parameters[2], conv_unit ) );
1825 int ty = static_cast<int>( parseInt( parameters[3], conv_unit ) );
1826 text->SetPosition( VECTOR2I( tx, ty ) );
1827
1828 long direction = 0;
1829 parameters[4].ToLong( &direction );
1830
1831 EDA_ANGLE textAngle( static_cast<double>( direction ) * 90.0, DEGREES_T );
1832 text->SetTextAngle( textAngle );
1833
1834 long scale = 100;
1835 parameters[5].ToLong( &scale );
1836
1837 int textSize = KiROUND( TEXT_DEFAULT_SIZE * static_cast<double>( scale ) / 100.0 );
1838 text->SetTextSize( VECTOR2I( textSize, textSize ) );
1839
1840 text->SetText( parameters[6] );
1841 m_board->Add( text, ADD_MODE::APPEND );
1842 continue;
1843 }
1844 }
1845}
1846
1847
1849{
1850 // NetList() (
1851 // Net("netname" "style") (
1852 // Connect("refdes-pinnumber")
1853 // )
1854 // )
1855
1856 // Build a lookup map for fast refdes -> footprint resolution
1857 std::map<wxString, FOOTPRINT*> fpByRef;
1858
1859 for( FOOTPRINT* fp : m_board->Footprints() )
1860 fpByRef[fp->GetReference()] = fp;
1861
1862 wxArrayString parameters;
1863
1864 while( aLineReader->ReadLine() )
1865 {
1866 parameters.Clear();
1867 parseParameters( parameters, aLineReader );
1868
1869 if( parameters.IsEmpty() )
1870 continue;
1871
1872 if( parameters[0] == wxT( ")" ) )
1873 break;
1874
1875 if( parameters[0] == wxT( "(" ) )
1876 continue;
1877
1878 // Net("netname" "style") (
1879 if( parameters[0].CmpNoCase( wxT( "Net" ) ) == 0 )
1880 {
1881 wxString netName;
1882
1883 if( parameters.GetCount() > 3 )
1884 netName = parameters[2];
1885
1886 // Create or find the net
1887 NETINFO_ITEM* netInfo = nullptr;
1888 auto it = m_netMap.find( netName );
1889
1890 if( it != m_netMap.end() )
1891 {
1892 netInfo = it->second;
1893 }
1894 else
1895 {
1896 netInfo = new NETINFO_ITEM( m_board, netName );
1897 m_board->Add( netInfo );
1898 m_netMap[netName] = netInfo;
1899 }
1900
1901 // Parse Connect entries within this Net
1902 while( aLineReader->ReadLine() )
1903 {
1904 wxArrayString netParams;
1905 parseParameters( netParams, aLineReader );
1906
1907 if( netParams.IsEmpty() )
1908 continue;
1909
1910 if( netParams[0] == wxT( ")" ) )
1911 break;
1912
1913 if( netParams[0] == wxT( "(" ) )
1914 continue;
1915
1916 // Connect("refdes-pinnumber")
1917 if( netParams[0].CmpNoCase( wxT( "Connect" ) ) == 0 && netParams.GetCount() > 3 )
1918 {
1919 wxString connectStr = netParams[2];
1920
1921 // Find the last hyphen to split refdes from pinnumber
1922 int lastDash = connectStr.Find( '-', true );
1923
1924 if( lastDash == wxNOT_FOUND )
1925 continue;
1926
1927 wxString refdes = connectStr.Left( lastDash );
1928 wxString pinNumber = connectStr.Mid( lastDash + 1 );
1929
1930 auto fpIt = fpByRef.find( refdes );
1931
1932 if( fpIt == fpByRef.end() )
1933 continue;
1934
1935 for( PAD* pad : fpIt->second->Pads() )
1936 {
1937 if( pad->GetNumber() == pinNumber )
1938 {
1939 pad->SetNet( netInfo );
1940 break;
1941 }
1942 }
1943 }
1944 }
1945 }
1946 }
1947}
1948
1949
1950void PCB_IO_GEDA::loadBoard( const wxString& aFileName, BOARD& aBoard, bool aIsNewLoad,
1951 const std::map<std::string, UTF8>* aProperties, PROJECT* aProject )
1952{
1954
1955 init( aProperties );
1956
1957 m_board = &aBoard;
1958
1959 for( FOOTPRINT* fp : m_cachedFootprints )
1960 delete fp;
1961
1962 m_cachedFootprints.clear();
1963 m_netMap.clear();
1965
1966 FILE_LINE_READER reader( aFileName );
1967
1968 double conv_unit = NEW_GPCB_UNIT_CONV;
1969
1970 while( reader.ReadLine() )
1971 {
1972 wxArrayString parameters;
1973 parseParameters( parameters, &reader );
1974
1975 if( parameters.IsEmpty() )
1976 continue;
1977
1978 int paramCnt = parameters.GetCount();
1979
1980 if( paramCnt > 3 )
1981 {
1982 if( parameters[1] == wxT( "(" ) )
1983 conv_unit = OLD_GPCB_UNIT_CONV;
1984 else
1985 conv_unit = NEW_GPCB_UNIT_CONV;
1986 }
1987
1988 // PCB["name" width height]
1989 if( parameters[0].CmpNoCase( wxT( "PCB" ) ) == 0 )
1990 {
1991 if( paramCnt > 4 )
1992 {
1993 int boardWidth = static_cast<int>( parseInt( parameters[3], conv_unit ) );
1994 int boardHeight = static_cast<int>( parseInt( parameters[4], conv_unit ) );
1995
1996 // Set page size from board dimensions
1997 VECTOR2I pageSize( boardWidth, boardHeight );
1998 PAGE_INFO page;
1999 page.SetWidthMils( boardWidth / pcbIUScale.IU_PER_MILS );
2000 page.SetHeightMils( boardHeight / pcbIUScale.IU_PER_MILS );
2001 m_board->SetPageSettings( page );
2002 }
2003
2004 continue;
2005 }
2006
2007 // FileVersion[YYYYMMDD]
2008 if( parameters[0].CmpNoCase( wxT( "FileVersion" ) ) == 0 )
2009 continue;
2010
2011 // Grid, Cursor, Thermal, DRC, Flags, Groups, Styles -- skip
2012 if( parameters[0].CmpNoCase( wxT( "Grid" ) ) == 0
2013 || parameters[0].CmpNoCase( wxT( "Cursor" ) ) == 0
2014 || parameters[0].CmpNoCase( wxT( "Thermal" ) ) == 0
2015 || parameters[0].CmpNoCase( wxT( "DRC" ) ) == 0
2016 || parameters[0].CmpNoCase( wxT( "Flags" ) ) == 0
2017 || parameters[0].CmpNoCase( wxT( "Groups" ) ) == 0
2018 || parameters[0].CmpNoCase( wxT( "Styles" ) ) == 0
2019 || parameters[0].CmpNoCase( wxT( "Attribute" ) ) == 0 )
2020 {
2021 continue;
2022 }
2023
2024 // Via[X Y Thickness Clearance Mask Drill "Name" SFlags]
2025 if( parameters[0].CmpNoCase( wxT( "Via" ) ) == 0 )
2026 {
2027 parseVia( parameters, conv_unit );
2028 continue;
2029 }
2030
2031 // Element[SFlags "Desc" "Name" "Value" MX MY TX TY TDir TScale TSFlags] (...)
2032 if( parameters[0].CmpNoCase( wxT( "Element" ) ) == 0 )
2033 {
2034 FOOTPRINT* fp = parseElement( parameters, &reader, conv_unit );
2035
2036 if( fp )
2037 {
2038 m_board->Add( fp, ADD_MODE::APPEND );
2039
2040 // Cache a copy for GetImportedCachedLibraryFootprints
2041 FOOTPRINT* fpCopy = static_cast<FOOTPRINT*>( fp->Clone() );
2042 fpCopy->SetParent( nullptr );
2043 m_cachedFootprints.push_back( fpCopy );
2044 }
2045
2046 continue;
2047 }
2048
2049 // Layer(N "name") ( ... )
2050 if( parameters[0].CmpNoCase( wxT( "Layer" ) ) == 0 )
2051 {
2052 parseLayer( parameters, &reader, conv_unit );
2053 continue;
2054 }
2055
2056 // Rat[X1 Y1 Group1 X2 Y2 Group2 SFlags] -- skip rats nest
2057 if( parameters[0].CmpNoCase( wxT( "Rat" ) ) == 0 )
2058 continue;
2059
2060 // NetList() ( ... )
2061 if( parameters[0].CmpNoCase( wxT( "NetList" ) ) == 0 )
2062 {
2063 parseNetList( &reader );
2064 continue;
2065 }
2066 }
2067
2068 // Set copper layer count
2069 BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
2070 m_board->SetCopperLayerCount( std::max( 2, m_numCopperLayers ) );
2071
2072 LSET enabledLayers = bds.GetEnabledLayers();
2073 enabledLayers.set( F_Cu );
2074 enabledLayers.set( B_Cu );
2075 enabledLayers.set( F_SilkS );
2076 enabledLayers.set( B_SilkS );
2077 enabledLayers.set( F_Mask );
2078 enabledLayers.set( B_Mask );
2079 enabledLayers.set( Edge_Cuts );
2080 bds.SetEnabledLayers( enabledLayers );
2081
2082 m_board->m_LegacyDesignSettingsLoaded = true;
2083 m_board->m_LegacyNetclassesLoaded = true;
2084}
2085
2086
2088{
2089 std::vector<FOOTPRINT*> retval;
2090
2091 for( FOOTPRINT* fp : m_cachedFootprints )
2092 retval.push_back( static_cast<FOOTPRINT*>( fp->Clone() ) );
2093
2094 return retval;
2095}
const char * name
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:128
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:982
BASE_SET & set(size_t pos)
Definition base_set.h:126
virtual void SetNet(NETINFO_ITEM *aNetInfo)
Set a NET_INFO object for the item.
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Container for design settings for a BOARD object.
void SetEnabledLayers(const LSET &aMask)
Change the bit-mask of enabled layers to aMask.
const LSET & GetEnabledLayers() const
Return a bit-mask of all the layers that are enabled.
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:411
virtual void SetParent(EDA_ITEM *aParent)
Definition eda_item.cpp:153
A LINE_READER that reads from an open file.
Definition richio.h:157
char * ReadLine() override
Read a line of text into the buffer and increments the line number counter.
Definition richio.cpp:202
RAII class to set and restore the fontconfig reporter.
Definition reporter.h:385
void SetFPID(const LIB_ID &aFPID)
Definition footprint.h:418
EDA_ITEM * Clone() const override
Create a duplicate of this item with linked list members set to NULL.
BOARD_ITEM * Duplicate(bool addToParentGroup, BOARD_COMMIT *aCommit=nullptr) const override
Create a copy of this BOARD_ITEM.
helper class for creating a footprint library cache.
std::unique_ptr< FOOTPRINT > m_footprint
WX_FILENAME GetFileName() const
std::unique_ptr< FOOTPRINT > & GetFootprint()
WX_FILENAME m_filename
The full file name and path of the footprint to cache.
GPCB_FPL_CACHE_ENTRY(FOOTPRINT *aFootprint, const WX_FILENAME &aFileName)
FOOTPRINT * parseFOOTPRINT(LINE_READER *aLineReader)
void Remove(const wxString &aFootprintName)
boost::ptr_map< std::string, GPCB_FPL_CACHE_ENTRY > & GetFootprints()
GPCB_FPL_CACHE(PCB_IO_GEDA *aOwner, const wxString &aLibraryPath)
bool IsModified()
Return true if the cache is not up-to-date.
static long long GetTimestamp(const wxString &aLibPath)
Generate a timestamp representing all source files in the cache (including the parent directory).
PCB_IO_GEDA * m_owner
Plugin object that owns the cache.
boost::ptr_map< std::string, GPCB_FPL_CACHE_ENTRY > m_footprints
Map of footprint filename to cache entries.
void parseParameters(wxArrayString &aParameterList, LINE_READER *aLineReader)
Extract parameters and tokens from aLineReader and adds them to aParameterList.
long long m_cache_timestamp
A hash of the timestamps for all the footprint files.
bool IsWritable() const
wxFileName m_lib_path
The path of the library.
bool m_cache_dirty
Stored separately because it's expensive to check m_cache_timestamp against all the files.
void Load()
Save not implemented for the Geda PCB footprint library format.
bool testFlags(const wxString &aFlag, long aMask, const wxChar *aName)
Test aFlag for aMask or aName.
wxString GetPath() const
virtual void Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED) const
Definition io_base.cpp:124
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
virtual const wxString What() const
A composite of Problem() and Where()
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:45
An abstract class from which implementation specific LINE_READERs may be derived to read single lines...
Definition richio.h:65
virtual char * ReadLine()=0
Read a line of text into the buffer and increments the line number counter.
virtual const wxString & GetSource() const
Returns the name of the source of the lines in an abstract sense.
Definition richio.h:93
virtual unsigned LineNumber() const
Return the line number of the last line read from this LINE_READER.
Definition richio.h:119
char * Line() const
Return a pointer to the last line that was read in.
Definition richio.h:101
static LOAD_INFO_REPORTER & GetInstance()
Definition reporter.cpp:351
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
static const LSET & AllCuMask()
return AllCuMask( MAX_CU_LAYERS );
Definition lset.cpp:604
Handle the data for a net.
Definition netinfo.h:50
static const int UNCONNECTED
Constant that holds the "unconnected net" number (typically 0) all items "connected" to this net are ...
Definition netinfo.h:281
static NETINFO_ITEM * OrphanedItem()
NETINFO_ITEM meaning that there was no net assigned for an item, as there was no board storing net li...
Definition netinfo.h:289
@ NORMAL
Shape is the same on all layers.
Definition padstack.h:170
static constexpr PCB_LAYER_ID ALL_LAYERS
! The layer identifier to use for the single defintion on normal padstacks
Definition padstack.h:179
Definition pad.h:61
Describe the page size and margins of a paper page on which to eventually print or plot.
Definition page_info.h:75
void SetHeightMils(double aHeightInMils)
void SetWidthMils(double aWidthInMils)
void SetMid(const VECTOR2I &aMid)
Definition pcb_track.h:299
A PCB_IO derivation for saving and loading Geda PCB files.
Definition pcb_io_geda.h:59
void FootprintDelete(const wxString &aLibraryPath, const wxString &aFootprintName, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Delete aFootprintName from the library at aLibraryPath.
void parseVia(wxArrayString &aParameters, double aConvUnit)
void loadBoard(const wxString &aFileName, BOARD &aBoard, bool aIsNewLoad, const std::map< std::string, UTF8 > *aProperties=nullptr, PROJECT *aProject=nullptr) override
Parse aFileName into aBoard.
void parseNetList(LINE_READER *aLineReader)
std::unique_ptr< FOOTPRINT > FootprintLoad(const wxString &aLibraryPath, const wxString &aFootprintName, bool aKeepUUID=false, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Load a footprint having aFootprintName from the aLibraryPath containing a library format that this PC...
bool IsLibraryWritable(const wxString &aLibraryPath) override
Return true if the library at aLibraryPath is writable.
void FootprintEnumerate(wxArrayString &aFootprintNames, const wxString &aLibraryPath, bool aBestEfforts, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Return a list of footprint names contained within the library at aLibraryPath.
long long GetLibraryTimestamp(const wxString &aLibraryPath) const override
Generate a timestamp representing all the files in the library (including the library directory).
std::map< wxString, NETINFO_ITEM * > m_netMap
bool DeleteLibrary(const wxString &aLibraryPath, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Delete an existing library and returns true, or if library does not exist returns false,...
bool CanReadBoard(const wxString &aFileName) const override
~PCB_IO_GEDA() override
int m_numCopperLayers
std::vector< FOOTPRINT * > GetImportedCachedLibraryFootprints() override
Return a container with the cached library footprints generated in the last call to Load.
void init(const std::map< std::string, UTF8 > *aProperties)
std::unique_ptr< FOOTPRINT > ImportFootprint(const wxString &aFootprintPath, wxString &aFootprintNameOut, const std::map< std::string, UTF8 > *aProperties) override
Load a single footprint from aFootprintPath and put its name in aFootprintNameOut.
GPCB_FPL_CACHE * m_cache
Footprint library cache.
bool testFlags(const wxString &aFlag, long aMask, const wxChar *aName)
FOOTPRINT * parseElement(wxArrayString &aParameters, LINE_READER *aLineReader, double aConvUnit)
void validateCache(const wxString &aLibraryPath, bool checkModified=true)
void parseParameters(wxArrayString &aParameterList, LINE_READER *aLineReader)
void parseLayer(wxArrayString &aParameters, LINE_READER *aLineReader, double aConvUnit)
std::vector< FOOTPRINT * > m_cachedFootprints
LINE_READER * m_reader
no ownership here.
friend class GPCB_FPL_CACHE
PCB_LAYER_ID mapLayer(int aGedaLayer, const wxString &aLayerName) const
const FOOTPRINT * getFootprint(const wxString &aLibraryPath, const wxString &aFootprintName, const std::map< std::string, UTF8 > *aProperties, bool checkModified)
BOARD * m_board
The board BOARD being worked on, no ownership here.
Definition pcb_io.h:404
virtual bool CanReadBoard(const wxString &aFileName) const
Definition pcb_io.cpp:40
PCB_IO(const wxString &aName)
Definition pcb_io.h:383
const std::map< std::string, UTF8 > * m_props
Properties passed via Save() or Load(), no ownership, may be NULL.
Definition pcb_io.h:407
void SetCenter(const VECTOR2I &aCenter) override
void Rotate(const VECTOR2I &aRotCentre, const EDA_ANGLE &aAngle) override
Rotate this object.
void SetArcAngleAndEnd(const EDA_ANGLE &aAngle, bool aCheckNegativeAngle=false)
Definition pcb_shape.h:109
void SetShape(SHAPE_T aShape) override
Definition pcb_shape.h:212
void SetEnd(const VECTOR2I &aEnd) override
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
void Move(const VECTOR2I &aMoveVector) override
Move this object.
void SetStart(const VECTOR2I &aStart) override
void SetStroke(const STROKE_PARAMS &aStroke) override
void SetEnd(const VECTOR2I &aEnd)
Definition pcb_track.h:94
void SetStart(const VECTOR2I &aStart)
Definition pcb_track.h:97
virtual void SetWidth(int aWidth)
Definition pcb_track.h:91
Container for project specific data.
Definition project.h:63
Simple container to manage line stroke parameters.
Read lines of text from another LINE_READER but only returns non-comment lines and non-blank lines wi...
char * ReadLine() override
Read a line of text into the buffer and increments the line number counter.
A wrapper around a wxFileName which is much more performant with a subset of the API.
Definition wx_filename.h:46
void SetFullName(const wxString &aFileNameAndExtension)
wxString GetName() const
wxString GetFullPath() const
Handle a list of polygons defining a copper zone.
Definition zone.h:70
void SetLocalClearance(std::optional< int > aClearance)
Definition zone.h:183
bool AppendCorner(const VECTOR2I &aPosition, int aHoleIdx, bool aAllowDuplication=false)
Add a new corner to the zone outline (to the main outline or a hole)
Definition zone.cpp:1496
virtual void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Definition zone.cpp:675
bool SetNetCode(int aNetCode, bool aNoAssert) override
Override that clamps the netcode to 0 when this zone is in copper-thieving fill mode.
Definition zone.cpp:657
void SetIsFilled(bool isFilled)
Definition zone.h:307
void SetAssignedPriority(unsigned aPriority)
Definition zone.h:117
int GetNumCorners(void) const
Access to m_Poly parameters.
Definition zone.h:617
#define _(s)
@ TENTHS_OF_A_DEGREE_T
Definition eda_angle.h:29
@ RADIANS_T
Definition eda_angle.h:31
@ DEGREES_T
Definition eda_angle.h:30
static constexpr EDA_ANGLE ANGLE_360
Definition eda_angle.h:454
static constexpr EDA_ANGLE ANGLE_180
Definition eda_angle.h:452
#define IGNORE_PARENT_GROUP
Definition eda_item.h:55
@ SEGMENT
Definition eda_shape.h:56
void CollectFilesLoopSafe(const wxString &aRoot, wxArrayString &aFiles, const wxString &aFileSpec, int aFlags)
Recursively collect every file under aRoot, deduplicating subdirectories by their resolved path.
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
#define THROW_IO_ERRORF(msg,...)
#define THROW_PARSE_ERROR(aProblem, aSource, aInputLine, aLineNumber, aByteIndex)
static const std::string GedaPcbFootprintLibFileExtension
static const std::string KiCadFootprintFileExtension
const wxChar *const traceGedaPcbPlugin
Flag to enable GEDA PCB plugin debug output.
static wxString From_UTF8(const char *cstring)
Convert a UTF8 encoded C string to a wxString for all wxWidgets build modes.
Definition idf_helpers.h:44
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition idf_helpers.h:37
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition layer_ids.h:741
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ In11_Cu
Definition layer_ids.h:72
@ Edge_Cuts
Definition layer_ids.h:108
@ F_Paste
Definition layer_ids.h:100
@ In9_Cu
Definition layer_ids.h:70
@ In7_Cu
Definition layer_ids.h:68
@ B_Mask
Definition layer_ids.h:94
@ B_Cu
Definition layer_ids.h:61
@ F_Mask
Definition layer_ids.h:93
@ B_Paste
Definition layer_ids.h:101
@ In2_Cu
Definition layer_ids.h:63
@ F_Fab
Definition layer_ids.h:115
@ In10_Cu
Definition layer_ids.h:71
@ F_SilkS
Definition layer_ids.h:96
@ In4_Cu
Definition layer_ids.h:65
@ In1_Cu
Definition layer_ids.h:62
@ B_SilkS
Definition layer_ids.h:97
@ In13_Cu
Definition layer_ids.h:74
@ In8_Cu
Definition layer_ids.h:69
@ In14_Cu
Definition layer_ids.h:75
@ In12_Cu
Definition layer_ids.h:73
@ In6_Cu
Definition layer_ids.h:67
@ In5_Cu
Definition layer_ids.h:66
@ In3_Cu
Definition layer_ids.h:64
@ F_Cu
Definition layer_ids.h:60
This file contains miscellaneous commonly used macros and functions.
#define KI_FALLTHROUGH
The KI_FALLTHROUGH macro is to be used when switch statement cases should purposely fallthrough from ...
Definition macros.h:79
@ TOP_BOTTOM
Flip top to bottom (around the X axis)
Definition mirror.h:25
long long TimestampDir(const wxString &aDirPath, const wxString &aFilespec)
Computes a hash of modification times and sizes for files matching a pattern.
Definition unix/io.cpp:123
@ SMD
Smd pad, appears on the solder paste layer (default)
Definition padstack.h:98
@ RECTANGLE
Definition padstack.h:53
static long parseInt(const wxString &aValue, double aScalar)
#define NEW_GPCB_UNIT_CONV
#define OLD_GPCB_UNIT_CONV
#define TEXT_DEFAULT_SIZE
Geda PCB file plugin definition file.
@ RPT_SEVERITY_ERROR
int parseInt(LINE_READER &aReader, const char *aLine, const char **aOutput)
Parse an ASCII integer string with possible leading whitespace into an integer and updates the pointe...
const int scale
int radius
int clearance
int delta
wxString dump(const wxArrayString &aArray)
Debug helper for printing wxArrayString contents.
wxLogTrace helper definitions.
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Calculate the new point of coord coord pX, pY, for a rotation center 0, 0.
Definition trigo.cpp:227
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708
Definition of file extensions used in Kicad.