KiCad PCB EDA Suite
pcb_parser.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 CERN
5  * Copyright (C) 2012-2021 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, you may find one here:
19  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20  * or you may search the http://www.gnu.org website for the version 2 license,
21  * or you may write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23  */
24 
30 #include <cerrno>
31 #include <confirm.h>
32 #include <macros.h>
33 #include <title_block.h>
34 #include <trigo.h>
35 
36 #include <board.h>
37 #include <board_design_settings.h>
38 #include <pcb_dimension.h>
39 #include <pcb_shape.h>
40 #include <fp_shape.h>
41 #include <pcb_group.h>
42 #include <pcb_target.h>
43 #include <footprint.h>
45 
46 #include <netclass.h>
47 #include <pad.h>
48 #include <pcb_track.h>
49 #include <zone.h>
51 #include <pcb_plot_params_parser.h>
52 #include <pcb_plot_params.h>
53 #include <locale_io.h>
54 #include <zones.h>
56 #include <convert_basic_shapes_to_polygon.h> // for RECT_CHAMFER_POSITIONS definition
57 #include <math/util.h> // KiROUND, Clamp
58 #include <kicad_string.h>
59 #include <wx/log.h>
61 
62 using namespace PCB_KEYS_T;
63 
64 
66 {
67  m_showLegacyZoneWarning = true;
68  m_tooRecent = false;
69  m_requiredVersion = 0;
70  m_layerIndices.clear();
71  m_layerMasks.clear();
72  m_resetKIIDMap.clear();
73 
74  // Add untranslated default (i.e. English) layernames.
75  // Some may be overridden later if parsing a board rather than a footprint.
76  // The English name will survive if parsing only a footprint.
77  for( LAYER_NUM layer = 0; layer < PCB_LAYER_ID_COUNT; ++layer )
78  {
79  std::string untranslated = TO_UTF8( wxString( LSET::Name( PCB_LAYER_ID( layer ) ) ) );
80 
81  m_layerIndices[ untranslated ] = PCB_LAYER_ID( layer );
82  m_layerMasks[ untranslated ] = LSET( PCB_LAYER_ID( layer ) );
83  }
84 
85  m_layerMasks[ "*.Cu" ] = LSET::AllCuMask();
86  m_layerMasks[ "*In.Cu" ] = LSET::InternalCuMask();
87  m_layerMasks[ "F&B.Cu" ] = LSET( 2, F_Cu, B_Cu );
88  m_layerMasks[ "*.Adhes" ] = LSET( 2, B_Adhes, F_Adhes );
89  m_layerMasks[ "*.Paste" ] = LSET( 2, B_Paste, F_Paste );
90  m_layerMasks[ "*.Mask" ] = LSET( 2, B_Mask, F_Mask );
91  m_layerMasks[ "*.SilkS" ] = LSET( 2, B_SilkS, F_SilkS );
92  m_layerMasks[ "*.Fab" ] = LSET( 2, B_Fab, F_Fab );
93  m_layerMasks[ "*.CrtYd" ] = LSET( 2, B_CrtYd, F_CrtYd );
94 
95  // This is for the first pretty & *.kicad_pcb formats, which had
96  // Inner1_Cu - Inner14_Cu with the numbering sequence
97  // reversed from the subsequent format's In1_Cu - In30_Cu numbering scheme.
98  // The newer format brought in an additional 16 Cu layers and flipped the cu stack but
99  // kept the gap between one of the outside layers and the last cu internal.
100 
101  for( int i=1; i<=14; ++i )
102  {
103  std::string key = StrPrintf( "Inner%d.Cu", i );
104 
105  m_layerMasks[ key ] = LSET( PCB_LAYER_ID( In15_Cu - i ) );
106  }
107 }
108 
109 
111 {
112  const unsigned PROGRESS_DELTA = 250;
113 
114  if( m_progressReporter )
115  {
116  unsigned curLine = m_lineReader->LineNumber();
117 
118  if( curLine > m_lastProgressLine + PROGRESS_DELTA )
119  {
120  m_progressReporter->SetCurrentProgress( ( (double) curLine )
121  / std::max( 1U, m_lineCount ) );
122 
123  if( !m_progressReporter->KeepRefreshing() )
124  THROW_IO_ERROR( ( "Open cancelled by user." ) );
125 
126  m_lastProgressLine = curLine;
127  }
128  }
129 }
130 
131 
133 {
134  int curr_level = 0;
135  T token;
136 
137  while( ( token = NextTok() ) != T_EOF )
138  {
139  if( token == T_LEFT )
140  curr_level--;
141 
142  if( token == T_RIGHT )
143  {
144  curr_level++;
145 
146  if( curr_level > 0 )
147  return;
148  }
149  }
150 }
151 
152 
153 void PCB_PARSER::pushValueIntoMap( int aIndex, int aValue )
154 {
155  // Add aValue in netcode mapping (m_netCodes) at index aNetCode
156  // ensure there is room in m_netCodes for that, and add room if needed.
157 
158  if( (int)m_netCodes.size() <= aIndex )
159  m_netCodes.resize( static_cast<std::size_t>( aIndex ) + 1 );
160 
161  m_netCodes[aIndex] = aValue;
162 }
163 
164 
166 {
167  char* tmp;
168 
169  errno = 0;
170 
171  double fval = strtod( CurText(), &tmp );
172 
173  if( errno )
174  {
175  wxString error;
176  error.Printf( _( "Invalid floating point number in\nfile: '%s'\nline: %d\noffset: %d" ),
177  CurSource(), CurLineNumber(), CurOffset() );
178 
179  THROW_IO_ERROR( error );
180  }
181 
182  if( CurText() == tmp )
183  {
184  wxString error;
185  error.Printf( _( "Missing floating point number in\nfile: '%s'\nline: %d\noffset: %d" ),
186  CurSource(), CurLineNumber(), CurOffset() );
187 
188  THROW_IO_ERROR( error );
189  }
190 
191  return fval;
192 }
193 
194 
196 {
197  // There should be no major rounding issues here, since the values in
198  // the file are in mm and get converted to nano-meters.
199  // See test program tools/test-nm-biu-to-ascii-mm-round-tripping.cpp
200  // to confirm or experiment. Use a similar strategy in both places, here
201  // and in the test program. Make that program with:
202  // $ make test-nm-biu-to-ascii-mm-round-tripping
203  auto retval = parseDouble() * IU_PER_MM;
204 
205  // N.B. we currently represent board units as integers. Any values that are
206  // larger or smaller than those board units represent undefined behavior for
207  // the system. We limit values to the largest that is visible on the screen
208  // This is the diagonal distance of the full screen ~1.5m
209  double int_limit = std::numeric_limits<int>::max() * 0.7071; // 0.7071 = roughly 1/sqrt(2)
210  return KiROUND( Clamp<double>( -int_limit, retval, int_limit ) );
211 }
212 
213 
214 int PCB_PARSER::parseBoardUnits( const char* aExpected )
215 {
216  auto retval = parseDouble( aExpected ) * IU_PER_MM;
217 
218  // N.B. we currently represent board units as integers. Any values that are
219  // larger or smaller than those board units represent undefined behavior for
220  // the system. We limit values to the largest that is visible on the screen
221  double int_limit = std::numeric_limits<int>::max() * 0.7071;
222 
223  // Use here #KiROUND, not EKIROUND (see comments about them) when having a function as
224  // argument, because it will be called twice with #KIROUND.
225  return KiROUND( Clamp<double>( -int_limit, retval, int_limit ) );
226 }
227 
228 
230 {
231  T token = NextTok();
232 
233  if( token == T_yes )
234  return true;
235  else if( token == T_no )
236  return false;
237  else
238  Expecting( "yes or no" );
239 
240  return false;
241 }
242 
243 
245 {
246  int year, month, day;
247 
248  year = m_requiredVersion / 10000;
249  month = ( m_requiredVersion / 100 ) - ( year * 100 );
250  day = m_requiredVersion - ( year * 10000 ) - ( month * 100 );
251 
252  // wx throws an assertion, not a catchable exception, when the date is invalid.
253  // User input shouldn't give wx asserts, so check manually and throw a proper
254  // error instead
255  if( day <= 0 || month <= 0 || month > 12 ||
256  day > wxDateTime::GetNumberOfDays( (wxDateTime::Month)( month - 1 ), year ) )
257  {
258  wxString err;
259  err.Printf( _( "Cannot interpret date code %d" ), m_requiredVersion );
260  THROW_PARSE_ERROR( err, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
261  }
262 
263  wxDateTime date( day, (wxDateTime::Month)( month - 1 ), year, 0, 0, 0, 0 );
264  return date.FormatDate();
265 }
266 
267 
269 {
270  if( CurTok() != T_LEFT )
271  NeedLEFT();
272 
273  wxPoint pt;
274  T token = NextTok();
275 
276  if( token != T_xy )
277  Expecting( T_xy );
278 
279  pt.x = parseBoardUnits( "X coordinate" );
280  pt.y = parseBoardUnits( "Y coordinate" );
281 
282  NeedRIGHT();
283 
284  return pt;
285 }
286 
287 
289 {
290  if( CurTok() != T_LEFT )
291  NeedLEFT();
292 
293  T token = NextTok();
294 
295  switch( token )
296  {
297  case T_xy:
298  {
299  int x = parseBoardUnits( "X coordinate" );
300  int y = parseBoardUnits( "Y coordinate" );
301 
302  NeedRIGHT();
303 
304  aPoly.Append( x, y );
305  break;
306  }
307  case T_arc:
308  {
309  bool has_start = false;
310  bool has_mid = false;
311  bool has_end = false;
312 
313  VECTOR2I arc_start, arc_mid, arc_end;
314 
315  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
316  {
317  if( token != T_LEFT )
318  Expecting( T_LEFT );
319 
320  token = NextTok();
321 
322  switch( token )
323  {
324  case T_start:
325  arc_start.x = parseBoardUnits( "start x" );
326  arc_start.y = parseBoardUnits( "start y" );
327  has_start = true;
328  break;
329 
330  case T_mid:
331  arc_mid.x = parseBoardUnits( "mid x" );
332  arc_mid.y = parseBoardUnits( "mid y" );
333  has_mid = true;
334  break;
335 
336  case T_end:
337  arc_end.x = parseBoardUnits( "end x" );
338  arc_end.y = parseBoardUnits( "end y" );
339  has_end = true;
340  break;
341 
342  default:
343  Expecting( "start, mid or end" );
344  }
345 
346  NeedRIGHT();
347  }
348 
349  if( !has_start )
350  Expecting( "start" );
351 
352  if( !has_mid )
353  Expecting( "mid" );
354 
355  if( !has_end )
356  Expecting( "end" );
357 
358  SHAPE_ARC arc( arc_start, arc_mid, arc_end, 0 );
359 
360  aPoly.Append( arc );
361 
362  if( token != T_RIGHT )
363  Expecting( T_RIGHT );
364 
365  break;
366  }
367  default:
368  Expecting( "xy or arc" );
369  }
370 }
371 
372 
373 void PCB_PARSER::parseXY( int* aX, int* aY )
374 {
375  wxPoint pt = parseXY();
376 
377  if( aX )
378  *aX = pt.x;
379 
380  if( aY )
381  *aY = pt.y;
382 }
383 
384 
385 std::pair<wxString, wxString> PCB_PARSER::parseProperty()
386 {
387  wxString pName;
388  wxString pValue;
389 
390  NeedSYMBOL();
391  pName = FromUTF8();
392  NeedSYMBOL();
393  pValue = FromUTF8();
394  NeedRIGHT();
395 
396  return { pName, pValue };
397 }
398 
399 
401 {
402  wxCHECK_RET( CurTok() == T_effects,
403  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as EDA_TEXT." ) );
404 
405  // In version 20210606 the notation for overbars was changed from `~...~` to `~{...}`.
406  // We need to convert the old syntax to the new one.
407  if( m_requiredVersion < 20210606 )
408  aText->SetText( ConvertToNewOverbarNotation( aText->GetText() ) );
409 
410  T token;
411 
412  // Prior to v5.0 text size was omitted from file format if equal to 60mils
413  // Now, it is always explicitly written to file
414  bool foundTextSize = false;
415 
416  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
417  {
418  if( token == T_LEFT )
419  token = NextTok();
420 
421  switch( token )
422  {
423  case T_font:
424  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
425  {
426  if( token == T_LEFT )
427  continue;
428 
429  switch( token )
430  {
431  case T_size:
432  {
433  wxSize sz;
434  sz.SetHeight( parseBoardUnits( "text height" ) );
435  sz.SetWidth( parseBoardUnits( "text width" ) );
436  aText->SetTextSize( sz );
437  NeedRIGHT();
438 
439  foundTextSize = true;
440  break;
441  }
442 
443  case T_thickness:
444  aText->SetTextThickness( parseBoardUnits( "text thickness" ) );
445  NeedRIGHT();
446  break;
447 
448  case T_bold:
449  aText->SetBold( true );
450  break;
451 
452  case T_italic:
453  aText->SetItalic( true );
454  break;
455 
456  default:
457  Expecting( "size, bold, or italic" );
458  }
459  }
460  break;
461 
462  case T_justify:
463  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
464  {
465  if( token == T_LEFT )
466  continue;
467 
468  switch( token )
469  {
470  case T_left:
472  break;
473 
474  case T_right:
476  break;
477 
478  case T_top:
480  break;
481 
482  case T_bottom:
484  break;
485 
486  case T_mirror:
487  aText->SetMirrored( true );
488  break;
489 
490  default:
491  Expecting( "left, right, top, bottom, or mirror" );
492  }
493 
494  }
495 
496  break;
497 
498  case T_hide:
499  aText->SetVisible( false );
500  break;
501 
502  default:
503  Expecting( "font, justify, or hide" );
504  }
505  }
506 
507  // Text size was not specified in file, force legacy default units
508  // 60mils is 1.524mm
509  if( !foundTextSize )
510  {
511  const double defaultTextSize = 1.524 * IU_PER_MM;
512 
513  aText->SetTextSize( wxSize( defaultTextSize, defaultTextSize ) );
514  }
515 }
516 
517 
519 {
520  wxCHECK_MSG( CurTok() == T_model, nullptr,
521  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as FP_3DMODEL." ) );
522 
523  T token;
524 
525  FP_3DMODEL* n3D = new FP_3DMODEL;
526  NeedSYMBOLorNUMBER();
527  n3D->m_Filename = FromUTF8();
528 
529  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
530  {
531  if( token == T_LEFT )
532  token = NextTok();
533 
534  switch( token )
535  {
536  case T_at:
537  NeedLEFT();
538  token = NextTok();
539 
540  if( token != T_xyz )
541  Expecting( T_xyz );
542 
543  /* Note:
544  * Prior to KiCad v5, model offset was designated by "at",
545  * and the units were in inches.
546  * Now we use mm, but support reading of legacy files
547  */
548 
549  n3D->m_Offset.x = parseDouble( "x value" ) * 25.4f;
550  n3D->m_Offset.y = parseDouble( "y value" ) * 25.4f;
551  n3D->m_Offset.z = parseDouble( "z value" ) * 25.4f;
552 
553  NeedRIGHT(); // xyz
554  NeedRIGHT(); // at
555  break;
556 
557  case T_hide:
558  n3D->m_Show = false;
559  break;
560 
561  case T_opacity:
562  n3D->m_Opacity = parseDouble( "opacity value" );
563  NeedRIGHT();
564  break;
565 
566  case T_offset:
567  NeedLEFT();
568  token = NextTok();
569 
570  if( token != T_xyz )
571  Expecting( T_xyz );
572 
573  /*
574  * 3D model offset is in mm
575  */
576  n3D->m_Offset.x = parseDouble( "x value" );
577  n3D->m_Offset.y = parseDouble( "y value" );
578  n3D->m_Offset.z = parseDouble( "z value" );
579 
580  NeedRIGHT(); // xyz
581  NeedRIGHT(); // offset
582  break;
583 
584  case T_scale:
585  NeedLEFT();
586  token = NextTok();
587 
588  if( token != T_xyz )
589  Expecting( T_xyz );
590 
591  n3D->m_Scale.x = parseDouble( "x value" );
592  n3D->m_Scale.y = parseDouble( "y value" );
593  n3D->m_Scale.z = parseDouble( "z value" );
594 
595  NeedRIGHT(); // xyz
596  NeedRIGHT(); // scale
597  break;
598 
599  case T_rotate:
600  NeedLEFT();
601  token = NextTok();
602 
603  if( token != T_xyz )
604  Expecting( T_xyz );
605 
606  n3D->m_Rotation.x = parseDouble( "x value" );
607  n3D->m_Rotation.y = parseDouble( "y value" );
608  n3D->m_Rotation.z = parseDouble( "z value" );
609 
610  NeedRIGHT(); // xyz
611  NeedRIGHT(); // rotate
612  break;
613 
614  default:
615  Expecting( "at, hide, opacity, offset, scale, or rotate" );
616  }
617 
618  }
619 
620  return n3D;
621 }
622 
623 
625 {
626  T token;
627  BOARD_ITEM* item;
628  LOCALE_IO toggle;
629 
630  m_groupInfos.clear();
631 
632  // FOOTPRINTS can be prefixed with an initial block of single line comments and these are
633  // kept for Format() so they round trip in s-expression form. BOARDs might eventually do
634  // the same, but currently do not.
635  std::unique_ptr<wxArrayString> initial_comments( ReadCommentLines() );
636 
637  token = CurTok();
638 
639  if( token != T_LEFT )
640  Expecting( T_LEFT );
641 
642  switch( NextTok() )
643  {
644  case T_kicad_pcb:
645  if( m_board == nullptr )
646  m_board = new BOARD();
647 
648  item = (BOARD_ITEM*) parseBOARD();
649  break;
650 
651  case T_module: // legacy token
652  case T_footprint:
653  item = (BOARD_ITEM*) parseFOOTPRINT( initial_comments.release() );
654 
655  // Locking a footprint has no meaning outside of a board.
656  item->SetLocked( false );
657  break;
658 
659  default:
660  wxString err;
661  err.Printf( _( "Unknown token '%s'" ), FromUTF8() );
662  THROW_PARSE_ERROR( err, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
663  }
664 
665  resolveGroups( item );
666 
667  return item;
668 }
669 
670 
672 {
673  try
674  {
675  return parseBOARD_unchecked();
676  }
677  catch( const PARSE_ERROR& parse_error )
678  {
679  if( m_tooRecent )
680  throw FUTURE_FORMAT_ERROR( parse_error, GetRequiredVersion() );
681  else
682  throw;
683  }
684 }
685 
686 
688 {
689  T token;
690  std::map<wxString, wxString> properties;
691 
692  parseHeader();
693 
694  std::vector<BOARD_ITEM*> bulkAddedItems;
695  BOARD_ITEM* item = nullptr;
696 
697  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
698  {
699  checkpoint();
700 
701  if( token != T_LEFT )
702  Expecting( T_LEFT );
703 
704  token = NextTok();
705 
706  if( token == T_page && m_requiredVersion <= 20200119 )
707  token = T_paper;
708 
709  switch( token )
710  {
711  case T_host: // legacy token
712  NeedSYMBOL();
713  m_board->SetGenerator( FromUTF8() );
714 
715  // Older formats included build data
716  if( m_requiredVersion < BOARD_FILE_HOST_VERSION )
717  NeedSYMBOL();
718 
719  NeedRIGHT();
720  break;
721 
722  case T_generator:
723  NeedSYMBOL();
724  m_board->SetGenerator( FromUTF8() );
725  NeedRIGHT();
726  break;
727 
728  case T_general:
729  parseGeneralSection();
730  break;
731 
732  case T_paper:
733  parsePAGE_INFO();
734  break;
735 
736  case T_title_block:
737  parseTITLE_BLOCK();
738  break;
739 
740  case T_layers:
741  parseLayers();
742  break;
743 
744  case T_setup:
745  parseSetup();
746  break;
747 
748  case T_property:
749  properties.insert( parseProperty() );
750  break;
751 
752  case T_net:
753  parseNETINFO_ITEM();
754  break;
755 
756  case T_net_class:
757  parseNETCLASS();
758  m_board->m_LegacyNetclassesLoaded = true;
759  break;
760 
761  case T_gr_arc:
762  case T_gr_curve:
763  case T_gr_line:
764  case T_gr_poly:
765  case T_gr_circle:
766  case T_gr_rect:
767  item = parsePCB_SHAPE();
768  m_board->Add( item, ADD_MODE::BULK_APPEND );
769  bulkAddedItems.push_back( item );
770  break;
771 
772  case T_gr_text:
773  item = parsePCB_TEXT();
774  m_board->Add( item, ADD_MODE::BULK_APPEND );
775  bulkAddedItems.push_back( item );
776  break;
777 
778  case T_dimension:
779  item = parseDIMENSION();
780  m_board->Add( item, ADD_MODE::BULK_APPEND );
781  bulkAddedItems.push_back( item );
782  break;
783 
784  case T_module: // legacy token
785  case T_footprint:
786  item = parseFOOTPRINT();
787  m_board->Add( item, ADD_MODE::BULK_APPEND );
788  bulkAddedItems.push_back( item );
789  break;
790 
791  case T_segment:
792  item = parsePCB_TRACK();
793  m_board->Add( item, ADD_MODE::BULK_APPEND );
794  bulkAddedItems.push_back( item );
795  break;
796 
797  case T_arc:
798  item = parseARC();
799  m_board->Add( item, ADD_MODE::BULK_APPEND );
800  bulkAddedItems.push_back( item );
801  break;
802 
803  case T_group:
804  parseGROUP( m_board );
805  break;
806 
807  case T_via:
808  item = parsePCB_VIA();
809  m_board->Add( item, ADD_MODE::BULK_APPEND );
810  bulkAddedItems.push_back( item );
811  break;
812 
813  case T_zone:
814  item = parseZONE( m_board );
815  m_board->Add( item, ADD_MODE::BULK_APPEND );
816  bulkAddedItems.push_back( item );
817  break;
818 
819  case T_target:
820  item = parsePCB_TARGET();
821  m_board->Add( item, ADD_MODE::BULK_APPEND );
822  bulkAddedItems.push_back( item );
823  break;
824 
825  default:
826  wxString err;
827  err.Printf( _( "Unknown token '%s'" ), FromUTF8() );
828  THROW_PARSE_ERROR( err, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
829  }
830  }
831 
832  if( bulkAddedItems.size() > 0 )
833  m_board->FinalizeBulkAdd( bulkAddedItems );
834 
835  m_board->SetProperties( properties );
836 
837  if( m_undefinedLayers.size() > 0 )
838  {
839  bool deleteItems;
840  std::vector<BOARD_ITEM*> deleteList;
841  wxString msg = wxString::Format( _( "Items found on undefined layers. Do you wish to\n"
842  "rescue them to the User.Comments layer?" ) );
843  wxString details = wxString::Format( _( "Undefined layers:" ) );
844 
845  for( const wxString& undefinedLayer : m_undefinedLayers )
846  details += wxT( "\n " ) + undefinedLayer;
847 
848  wxRichMessageDialog dlg( nullptr, msg, _( "Warning" ),
849  wxYES_NO | wxCANCEL | wxCENTRE | wxICON_WARNING | wxSTAY_ON_TOP );
850  dlg.ShowDetailedText( details );
851  dlg.SetYesNoCancelLabels( _( "Rescue" ), _( "Delete" ), _( "Cancel" ) );
852 
853  switch( dlg.ShowModal() )
854  {
855  case wxID_YES: deleteItems = false; break;
856  case wxID_NO: deleteItems = true; break;
857  case wxID_CANCEL:
858  default: THROW_IO_ERROR( wxT( "CANCEL" ) );
859  }
860 
861  auto visitItem = [&]( BOARD_ITEM* curr_item )
862  {
863  if( curr_item->GetLayer() == Rescue )
864  {
865  if( deleteItems )
866  deleteList.push_back( curr_item );
867  else
868  curr_item->SetLayer( Cmts_User );
869  }
870  };
871 
872  for( PCB_TRACK* track : m_board->Tracks() )
873  {
874  if( track->Type() == PCB_VIA_T )
875  {
876  PCB_VIA* via = static_cast<PCB_VIA*>( track );
877  PCB_LAYER_ID top_layer, bottom_layer;
878 
879  if( via->GetViaType() == VIATYPE::THROUGH )
880  continue;
881 
882  via->LayerPair( &top_layer, &bottom_layer );
883 
884  if( top_layer == Rescue || bottom_layer == Rescue )
885  {
886  if( deleteItems )
887  deleteList.push_back( via );
888  else
889  {
890  if( top_layer == Rescue )
891  top_layer = F_Cu;
892 
893  if( bottom_layer == Rescue )
894  bottom_layer = B_Cu;
895 
896  via->SetLayerPair( top_layer, bottom_layer );
897  }
898  }
899  }
900  else
901  {
902  visitItem( track );
903  }
904  }
905 
906  for( BOARD_ITEM* zone : m_board->Zones() )
907  visitItem( zone );
908 
909  for( BOARD_ITEM* drawing : m_board->Drawings() )
910  visitItem( drawing );
911 
912  for( FOOTPRINT* fp : m_board->Footprints() )
913  {
914  for( BOARD_ITEM* drawing : fp->GraphicalItems() )
915  visitItem( drawing );
916 
917  for( BOARD_ITEM* zone : fp->Zones() )
918  visitItem( zone );
919  }
920 
921  for( BOARD_ITEM* curr_item : deleteList )
922  m_board->Delete( curr_item );
923 
924  m_undefinedLayers.clear();
925  }
926 
927  return m_board;
928 }
929 
930 
932 {
933  auto getItem = [&]( const KIID& aId )
934  {
935  BOARD_ITEM* aItem = nullptr;
936 
937  if( dynamic_cast<BOARD*>( aParent ) )
938  {
939  aItem = static_cast<BOARD*>( aParent )->GetItem( aId );
940  }
941  else if( aParent->Type() == PCB_FOOTPRINT_T )
942  {
943  static_cast<FOOTPRINT*>( aParent )->RunOnChildren(
944  [&]( BOARD_ITEM* child )
945  {
946  if( child->m_Uuid == aId )
947  aItem = child;
948  } );
949  }
950 
951  return aItem;
952  };
953 
954  // Now that we've parsed the other Uuids in the file we can resolve the uuids referred
955  // to in the group declarations we saw.
956  //
957  // First add all group objects so subsequent GetItem() calls for nested groups work.
958 
959  for( size_t idx = 0; idx < m_groupInfos.size(); idx++ )
960  {
961  GROUP_INFO& aGrp = m_groupInfos[idx];
962  PCB_GROUP* group = new PCB_GROUP( aGrp.parent );
963 
964  group->SetName( aGrp.name );
965  const_cast<KIID&>( group->m_Uuid ) = aGrp.uuid;
966 
967  if( aGrp.locked )
968  group->SetLocked( true );
969 
970  if( aGrp.parent->Type() == PCB_FOOTPRINT_T )
971  static_cast<FOOTPRINT*>( aGrp.parent )->Add( group );
972  else
973  static_cast<BOARD*>( aGrp.parent )->Add( group );
974  }
975 
976  wxString error;
977 
978  for( size_t idx = 0; idx < m_groupInfos.size(); idx++ )
979  {
980  GROUP_INFO& aGrp = m_groupInfos[idx];
981  BOARD_ITEM* bItem = getItem( aGrp.uuid );
982 
983  if( bItem == nullptr || bItem->Type() != PCB_GROUP_T )
984  continue;
985 
986  PCB_GROUP* group = static_cast<PCB_GROUP*>( bItem );
987 
988  for( const KIID& aUuid : aGrp.memberUuids )
989  {
990  BOARD_ITEM* item;
991 
992  if( m_resetKIIDs )
993  item = getItem( m_resetKIIDMap[ aUuid.AsString() ] );
994  else
995  item = getItem( aUuid );
996 
997  if( item && item->Type() != NOT_USED )
998  {
999  switch( item->Type() )
1000  {
1001  // We used to allow fp items in non-footprint groups. It was a mistake.
1002  case PCB_FP_TEXT_T:
1003  case PCB_FP_SHAPE_T:
1004  case PCB_FP_ZONE_T:
1005  if( item->GetParent() == group->GetParent() )
1006  group->AddItem( item );
1007 
1008  break;
1009 
1010  // This is the deleted item singleton, which means we didn't find the uuid.
1011  case NOT_USED:
1012  break;
1013 
1014  default:
1015  group->AddItem( item );
1016  }
1017  }
1018  }
1019  }
1020 
1021  // Don't allow group cycles
1022  if( m_board )
1023  m_board->GroupsSanityCheck( true );
1024 }
1025 
1026 
1028 {
1029  wxCHECK_RET( CurTok() == T_kicad_pcb,
1030  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a header." ) );
1031 
1032  NeedLEFT();
1033 
1034  T tok = NextTok();
1035 
1036  if( tok == T_version )
1037  {
1038  m_requiredVersion = parseInt( FromUTF8().mb_str( wxConvUTF8 ) );
1039  m_tooRecent = ( m_requiredVersion > SEXPR_BOARD_FILE_VERSION );
1040  NeedRIGHT();
1041  }
1042  else
1043  {
1044  m_requiredVersion = 20201115; // Last version before we started writing version #s
1045  // in footprint files as well as board files.
1046  m_tooRecent = ( m_requiredVersion > SEXPR_BOARD_FILE_VERSION );
1047  }
1048 
1049  m_board->SetFileFormatVersionAtLoad( m_requiredVersion );
1050 }
1051 
1052 
1054 {
1055  wxCHECK_RET( CurTok() == T_general,
1056  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) +
1057  wxT( " as a general section." ) );
1058 
1059  T token;
1060 
1061  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1062  {
1063  if( token != T_LEFT )
1064  Expecting( T_LEFT );
1065 
1066  token = NextTok();
1067 
1068  switch( token )
1069  {
1070  case T_thickness:
1071  m_board->GetDesignSettings().SetBoardThickness( parseBoardUnits( T_thickness ) );
1072  NeedRIGHT();
1073  break;
1074 
1075  default: // Skip everything but the board thickness.
1076  while( ( token = NextTok() ) != T_RIGHT )
1077  {
1078  if( !IsSymbol( token ) && token != T_NUMBER )
1079  Expecting( "symbol or number" );
1080  }
1081  }
1082  }
1083 }
1084 
1085 
1087 {
1088  wxCHECK_RET( ( CurTok() == T_page && m_requiredVersion <= 20200119 ) || CurTok() == T_paper,
1089  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a PAGE_INFO." ) );
1090 
1091  T token;
1092  PAGE_INFO pageInfo;
1093 
1094  NeedSYMBOL();
1095 
1096  wxString pageType = FromUTF8();
1097 
1098  if( !pageInfo.SetType( pageType ) )
1099  {
1100  wxString err;
1101  err.Printf( _( "Page type '%s' is not valid." ), FromUTF8() );
1102  THROW_PARSE_ERROR( err, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
1103  }
1104 
1105  if( pageType == PAGE_INFO::Custom )
1106  {
1107  double width = parseDouble( "width" ); // width in mm
1108 
1109  // Perform some controls to avoid crashes if the size is edited by hands
1110  if( width < 100.0 )
1111  width = 100.0;
1112  else if( width > 1200.0 )
1113  width = 1200.0;
1114 
1115  double height = parseDouble( "height" ); // height in mm
1116 
1117  if( height < 100.0 )
1118  height = 100.0;
1119  else if( height > 1200.0 )
1120  height = 1200.0;
1121 
1122  pageInfo.SetWidthMils( Mm2mils( width ) );
1123  pageInfo.SetHeightMils( Mm2mils( height ) );
1124  }
1125 
1126  token = NextTok();
1127 
1128  if( token == T_portrait )
1129  {
1130  pageInfo.SetPortrait( true );
1131  NeedRIGHT();
1132  }
1133  else if( token != T_RIGHT )
1134  {
1135  Expecting( "portrait|)" );
1136  }
1137 
1138  m_board->SetPageSettings( pageInfo );
1139 }
1140 
1141 
1143 {
1144  wxCHECK_RET( CurTok() == T_title_block,
1145  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) +
1146  wxT( " as TITLE_BLOCK." ) );
1147 
1148  T token;
1149  TITLE_BLOCK titleBlock;
1150 
1151  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1152  {
1153  if( token != T_LEFT )
1154  Expecting( T_LEFT );
1155 
1156  token = NextTok();
1157 
1158  switch( token )
1159  {
1160  case T_title:
1161  NextTok();
1162  titleBlock.SetTitle( FromUTF8() );
1163  break;
1164 
1165  case T_date:
1166  NextTok();
1167  titleBlock.SetDate( FromUTF8() );
1168  break;
1169 
1170  case T_rev:
1171  NextTok();
1172  titleBlock.SetRevision( FromUTF8() );
1173  break;
1174 
1175  case T_company:
1176  NextTok();
1177  titleBlock.SetCompany( FromUTF8() );
1178  break;
1179 
1180  case T_comment:
1181  {
1182  int commentNumber = parseInt( "comment" );
1183 
1184  switch( commentNumber )
1185  {
1186  case 1:
1187  NextTok();
1188  titleBlock.SetComment( 0, FromUTF8() );
1189  break;
1190 
1191  case 2:
1192  NextTok();
1193  titleBlock.SetComment( 1, FromUTF8() );
1194  break;
1195 
1196  case 3:
1197  NextTok();
1198  titleBlock.SetComment( 2, FromUTF8() );
1199  break;
1200 
1201  case 4:
1202  NextTok();
1203  titleBlock.SetComment( 3, FromUTF8() );
1204  break;
1205 
1206  case 5:
1207  NextTok();
1208  titleBlock.SetComment( 4, FromUTF8() );
1209  break;
1210 
1211  case 6:
1212  NextTok();
1213  titleBlock.SetComment( 5, FromUTF8() );
1214  break;
1215 
1216  case 7:
1217  NextTok();
1218  titleBlock.SetComment( 6, FromUTF8() );
1219  break;
1220 
1221  case 8:
1222  NextTok();
1223  titleBlock.SetComment( 7, FromUTF8() );
1224  break;
1225 
1226  case 9:
1227  NextTok();
1228  titleBlock.SetComment( 8, FromUTF8() );
1229  break;
1230 
1231  default:
1232  wxString err;
1233  err.Printf( wxT( "%d is not a valid title block comment number" ), commentNumber );
1234  THROW_PARSE_ERROR( err, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
1235  }
1236 
1237  break;
1238  }
1239 
1240  default:
1241  Expecting( "title, date, rev, company, or comment" );
1242  }
1243 
1244  NeedRIGHT();
1245  }
1246 
1247  m_board->SetTitleBlock( titleBlock );
1248 }
1249 
1250 
1252 {
1253  T token;
1254 
1255  std::string name;
1256  std::string userName;
1257  std::string type;
1258  bool isVisible = true;
1259 
1260  aLayer->clear();
1261 
1262  if( CurTok() != T_LEFT )
1263  Expecting( T_LEFT );
1264 
1265  // this layer_num is not used, we DO depend on LAYER_T however.
1266  LAYER_NUM layer_num = parseInt( "layer index" );
1267 
1268  NeedSYMBOLorNUMBER();
1269  name = CurText();
1270 
1271  NeedSYMBOL();
1272  type = CurText();
1273 
1274  token = NextTok();
1275 
1276  // @todo Figure out why we are looking for a hide token in the layer definition.
1277  if( token == T_hide )
1278  {
1279  isVisible = false;
1280  NeedRIGHT();
1281  }
1282  else if( token == T_STRING )
1283  {
1284  userName = CurText();
1285  NeedRIGHT();
1286  }
1287  else if( token != T_RIGHT )
1288  {
1289  Expecting( "hide, user defined name, or )" );
1290  }
1291 
1292  aLayer->m_name = FROM_UTF8( name.c_str() );
1293  aLayer->m_type = LAYER::ParseType( type.c_str() );
1294  aLayer->m_number = layer_num;
1295  aLayer->m_visible = isVisible;
1296 
1297  if( !userName.empty() )
1298  aLayer->m_userName = FROM_UTF8( userName.c_str() );
1299 
1300  // The canonical name will get reset back to the default for copper layer on the next
1301  // save. The user defined name is now a separate optional layer token from the canonical
1302  // name.
1303  if( aLayer->m_name != LSET::Name( static_cast<PCB_LAYER_ID>( aLayer->m_number ) ) )
1304  aLayer->m_userName = aLayer->m_name;
1305 }
1306 
1307 
1309 {
1310  T token;
1311  wxString name;
1312  int dielectric_idx = 1; // the index of dielectric layers
1313  BOARD_STACKUP& stackup = m_board->GetDesignSettings().GetStackupDescriptor();
1314 
1315  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1316  {
1317  if( CurTok() != T_LEFT )
1318  Expecting( T_LEFT );
1319 
1320  token = NextTok();
1321 
1322  if( token != T_layer )
1323  {
1324  switch( token )
1325  {
1326  case T_copper_finish:
1327  NeedSYMBOL();
1328  stackup.m_FinishType = FromUTF8();
1329  NeedRIGHT();
1330  break;
1331 
1332  case T_edge_plating:
1333  token = NextTok();
1334  stackup.m_EdgePlating = token == T_yes;
1335  NeedRIGHT();
1336  break;
1337 
1338  case T_dielectric_constraints:
1339  token = NextTok();
1340  stackup.m_HasDielectricConstrains = token == T_yes;
1341  NeedRIGHT();
1342  break;
1343 
1344  case T_edge_connector:
1345  token = NextTok();
1347 
1348  if( token == T_yes )
1350  else if( token == T_bevelled )
1352 
1353  NeedRIGHT();
1354  break;
1355 
1356  case T_castellated_pads:
1357  token = NextTok();
1358  stackup.m_CastellatedPads = token == T_yes;
1359  NeedRIGHT();
1360  break;
1361 
1362  default:
1363  // Currently, skip this item if not defined, because the stackup def
1364  // is a moving target
1365  //Expecting( "copper_finish, edge_plating, dielectric_constrains, edge_connector, castellated_pads" );
1366  skipCurrent();
1367  break;
1368  }
1369 
1370  continue;
1371  }
1372 
1373  NeedSYMBOL();
1374  name = FromUTF8();
1375 
1376  // init the layer id. For dielectric, layer id = UNDEFINED_LAYER
1377  PCB_LAYER_ID layerId = m_board->GetLayerID( name );
1378 
1379  // Init the type
1381 
1382  if( layerId == F_SilkS || layerId == B_SilkS )
1383  type = BS_ITEM_TYPE_SILKSCREEN;
1384  else if( layerId == F_Mask || layerId == B_Mask )
1385  type = BS_ITEM_TYPE_SOLDERMASK;
1386  else if( layerId == F_Paste || layerId == B_Paste )
1387  type = BS_ITEM_TYPE_SOLDERPASTE;
1388  else if( layerId == UNDEFINED_LAYER )
1389  type = BS_ITEM_TYPE_DIELECTRIC;
1390  else if( layerId >= F_Cu && layerId <= B_Cu )
1391  type = BS_ITEM_TYPE_COPPER;
1392 
1393  BOARD_STACKUP_ITEM* item = nullptr;
1394 
1395  if( type != BS_ITEM_TYPE_UNDEFINED )
1396  {
1397  item = new BOARD_STACKUP_ITEM( type );
1398  item->SetBrdLayerId( layerId );
1399 
1400  if( type == BS_ITEM_TYPE_DIELECTRIC )
1401  item->SetDielectricLayerId( dielectric_idx++ );
1402 
1403  stackup.Add( item );
1404  }
1405  else
1406  {
1407  Expecting( "layer_name" );
1408  }
1409 
1410  bool has_next_sublayer = true;
1411  int sublayer_idx = 0; // the index of dielectric sub layers
1412  // sublayer 0 is always existing (main sublayer)
1413 
1414  while( has_next_sublayer )
1415  {
1416  has_next_sublayer = false;
1417 
1418  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1419  {
1420  if( token == T_addsublayer )
1421  {
1422  has_next_sublayer = true;
1423  break;
1424  }
1425 
1426  if( token == T_LEFT )
1427  {
1428  token = NextTok();
1429 
1430  switch( token )
1431  {
1432  case T_type:
1433  NeedSYMBOL();
1434  item->SetTypeName( FromUTF8() );
1435  NeedRIGHT();
1436  break;
1437 
1438  case T_thickness:
1439  item->SetThickness( parseBoardUnits( T_thickness ), sublayer_idx );
1440  token = NextTok();
1441 
1442  if( token == T_LEFT )
1443  break;
1444 
1445  if( token == T_locked )
1446  {
1447  // Dielectric thickness can be locked (for impedance controlled layers)
1448  if( type == BS_ITEM_TYPE_DIELECTRIC )
1449  item->SetThicknessLocked( true, sublayer_idx );
1450 
1451  NeedRIGHT();
1452  }
1453 
1454  break;
1455 
1456  case T_material:
1457  NeedSYMBOL();
1458  item->SetMaterial( FromUTF8(), sublayer_idx );
1459  NeedRIGHT();
1460  break;
1461 
1462  case T_epsilon_r:
1463  NextTok();
1464  item->SetEpsilonR( parseDouble(), sublayer_idx );
1465  NeedRIGHT();
1466  break;
1467 
1468  case T_loss_tangent:
1469  NextTok();
1470  item->SetLossTangent( parseDouble(), sublayer_idx );
1471  NeedRIGHT();
1472  break;
1473 
1474  case T_color:
1475  NeedSYMBOL();
1476  item->SetColor( FromUTF8() );
1477  NeedRIGHT();
1478  break;
1479 
1480  default:
1481  // Currently, skip this item if not defined, because the stackup def
1482  // is a moving target
1483  //Expecting( "type, thickness, material, epsilon_r, loss_tangent, color" );
1484  skipCurrent();
1485  }
1486  }
1487  }
1488 
1489  if( has_next_sublayer ) // Prepare reading the next sublayer description
1490  {
1491  sublayer_idx++;
1492  item->AddDielectricPrms( sublayer_idx );
1493  }
1494  }
1495  }
1496 
1497  if( token != T_RIGHT )
1498  {
1499  Expecting( ")" );
1500  }
1501 
1502  // Success:
1503  m_board->GetDesignSettings().m_HasStackup = true;
1504 }
1505 
1506 
1507 void PCB_PARSER::createOldLayerMapping( std::unordered_map< std::string, std::string >& aMap )
1508 {
1509  // N.B. This mapping only includes Italian, Polish and French as they were the only languages
1510  // that mapped the layer names as of cc2022b1ac739aa673d2a0b7a2047638aa7a47b3 (kicad-i18n)
1511  // when the bug was fixed in KiCad source.
1512 
1513  // Italian
1514  aMap["Adesivo.Retro"] = "B.Adhes";
1515  aMap["Adesivo.Fronte"] = "F.Adhes";
1516  aMap["Pasta.Retro"] = "B.Paste";
1517  aMap["Pasta.Fronte"] = "F.Paste";
1518  aMap["Serigrafia.Retro"] = "B.SilkS";
1519  aMap["Serigrafia.Fronte"] = "F.SilkS";
1520  aMap["Maschera.Retro"] = "B.Mask";
1521  aMap["Maschera.Fronte"] = "F.Mask";
1522  aMap["Grafica"] = "Dwgs.User";
1523  aMap["Commenti"] = "Cmts.User";
1524  aMap["Eco1"] = "Eco1.User";
1525  aMap["Eco2"] = "Eco2.User";
1526  aMap["Contorno.scheda"] = "Edge.Cuts";
1527 
1528  // Polish
1529  aMap["Kleju_Dolna"] = "B.Adhes";
1530  aMap["Kleju_Gorna"] = "F.Adhes";
1531  aMap["Pasty_Dolna"] = "B.Paste";
1532  aMap["Pasty_Gorna"] = "F.Paste";
1533  aMap["Opisowa_Dolna"] = "B.SilkS";
1534  aMap["Opisowa_Gorna"] = "F.SilkS";
1535  aMap["Maski_Dolna"] = "B.Mask";
1536  aMap["Maski_Gorna"] = "F.Mask";
1537  aMap["Rysunkowa"] = "Dwgs.User";
1538  aMap["Komentarzy"] = "Cmts.User";
1539  aMap["ECO1"] = "Eco1.User";
1540  aMap["ECO2"] = "Eco2.User";
1541  aMap["Krawedziowa"] = "Edge.Cuts";
1542 
1543  // French
1544  aMap["Dessous.Adhes"] = "B.Adhes";
1545  aMap["Dessus.Adhes"] = "F.Adhes";
1546  aMap["Dessous.Pate"] = "B.Paste";
1547  aMap["Dessus.Pate"] = "F.Paste";
1548  aMap["Dessous.SilkS"] = "B.SilkS";
1549  aMap["Dessus.SilkS"] = "F.SilkS";
1550  aMap["Dessous.Masque"] = "B.Mask";
1551  aMap["Dessus.Masque"] = "F.Mask";
1552  aMap["Dessin.User"] = "Dwgs.User";
1553  aMap["Contours.Ci"] = "Edge.Cuts";
1554 }
1555 
1556 
1558 {
1559  wxCHECK_RET( CurTok() == T_layers,
1560  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as layers." ) );
1561 
1562  T token;
1563  LSET visibleLayers;
1564  LSET enabledLayers;
1565  int copperLayerCount = 0;
1566  LAYER layer;
1567  bool anyHidden = false;
1568 
1569  std::unordered_map< std::string, std::string > v3_layer_names;
1570  std::vector<LAYER> cu;
1571 
1572  createOldLayerMapping( v3_layer_names );
1573 
1574  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1575  {
1576  parseLayer( &layer );
1577 
1578  if( layer.m_type == LT_UNDEFINED ) // it's a non-copper layer
1579  break;
1580 
1581  cu.push_back( layer ); // it's copper
1582  }
1583 
1584  // All Cu layers are parsed, but not the non-cu layers here.
1585 
1586  // The original *.kicad_pcb file format and the inverted
1587  // Cu stack format both have all the Cu layers first, so use this
1588  // trick to handle either. The layer number in the (layers ..)
1589  // s-expression element are ignored.
1590  if( cu.size() )
1591  {
1592  // Rework the layer numbers, which changed when the Cu stack
1593  // was flipped. So we instead use position in the list.
1594  cu[cu.size()-1].m_number = B_Cu;
1595 
1596  for( unsigned i=0; i < cu.size()-1; ++i )
1597  {
1598  cu[i].m_number = i;
1599  }
1600 
1601  for( std::vector<LAYER>::const_iterator it = cu.begin(); it<cu.end(); ++it )
1602  {
1603  enabledLayers.set( it->m_number );
1604 
1605  if( it->m_visible )
1606  visibleLayers.set( it->m_number );
1607  else
1608  anyHidden = true;
1609 
1610  m_board->SetLayerDescr( PCB_LAYER_ID( it->m_number ), *it );
1611 
1612  UTF8 name = it->m_name;
1613 
1614  m_layerIndices[ name ] = PCB_LAYER_ID( it->m_number );
1615  m_layerMasks[ name ] = LSET( PCB_LAYER_ID( it->m_number ) );
1616  }
1617 
1618  copperLayerCount = cu.size();
1619  }
1620 
1621  // process non-copper layers
1622  while( token != T_RIGHT )
1623  {
1624  LAYER_ID_MAP::const_iterator it = m_layerIndices.find( UTF8( layer.m_name ) );
1625 
1626  if( it == m_layerIndices.end() )
1627  {
1628  auto new_layer_it = v3_layer_names.find( layer.m_name.ToStdString() );
1629 
1630  if( new_layer_it != v3_layer_names.end() )
1631  it = m_layerIndices.find( new_layer_it->second );
1632 
1633  if( it == m_layerIndices.end() )
1634  {
1635  wxString error;
1636  error.Printf( _( "Layer '%s' in file '%s' at line %d is not in fixed layer hash." ),
1637  layer.m_name,
1638  CurSource(),
1639  CurLineNumber(),
1640  CurOffset() );
1641 
1642  THROW_IO_ERROR( error );
1643  }
1644 
1645  // If we are here, then we have found a translated layer name. Put it in the maps
1646  // so that items on this layer get the appropriate layer ID number.
1647  m_layerIndices[ UTF8( layer.m_name ) ] = it->second;
1648  m_layerMasks[ UTF8( layer.m_name ) ] = it->second;
1649  layer.m_name = it->first;
1650  }
1651 
1652  layer.m_number = it->second;
1653  enabledLayers.set( layer.m_number );
1654 
1655  if( layer.m_visible )
1656  visibleLayers.set( layer.m_number );
1657  else
1658  anyHidden = true;
1659 
1660  m_board->SetLayerDescr( it->second, layer );
1661 
1662  token = NextTok();
1663 
1664  if( token != T_LEFT )
1665  break;
1666 
1667  parseLayer( &layer );
1668  }
1669 
1670  // We need at least 2 copper layers and there must be an even number of them.
1671  if( copperLayerCount < 2 || (copperLayerCount % 2) != 0 )
1672  {
1673  wxString err = wxString::Format( _( "%d is not a valid layer count" ), copperLayerCount );
1674 
1675  THROW_PARSE_ERROR( err, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
1676  }
1677 
1678  m_board->SetCopperLayerCount( copperLayerCount );
1679  m_board->SetEnabledLayers( enabledLayers );
1680 
1681  // Only set this if any layers were explicitly marked as hidden. Otherwise, we want to leave
1682  // this alone; default visibility will show everything
1683  if( anyHidden )
1684  m_board->m_LegacyVisibleLayers = visibleLayers;
1685 }
1686 
1687 
1688 template<class T, class M>
1689 T PCB_PARSER::lookUpLayer( const M& aMap )
1690 {
1691  // avoid constructing another std::string, use lexer's directly
1692  typename M::const_iterator it = aMap.find( curText );
1693 
1694  if( it == aMap.end() )
1695  {
1696  m_undefinedLayers.insert( curText );
1697  return Rescue;
1698  }
1699 
1700  // Some files may have saved items to the Rescue Layer due to an issue in v5
1701  if( it->second == Rescue )
1702  m_undefinedLayers.insert( curText );
1703 
1704  return it->second;
1705 }
1706 
1707 
1709 {
1710  wxCHECK_MSG( CurTok() == T_layer, UNDEFINED_LAYER,
1711  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as layer." ) );
1712 
1713  NextTok();
1714 
1715  PCB_LAYER_ID layerIndex = lookUpLayer<PCB_LAYER_ID>( m_layerIndices );
1716 
1717  // Handle closing ) in object parser.
1718 
1719  return layerIndex;
1720 }
1721 
1722 
1724 {
1725  wxCHECK_MSG( CurTok() == T_layers, LSET(),
1726  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) +
1727  wxT( " as item layer mask." ) );
1728 
1729  LSET layerMask;
1730 
1731  for( T token = NextTok(); token != T_RIGHT; token = NextTok() )
1732  {
1733  LSET mask = lookUpLayer<LSET>( m_layerMasks );
1734  layerMask |= mask;
1735  }
1736 
1737  return layerMask;
1738 }
1739 
1740 
1742 {
1743  wxCHECK_RET( CurTok() == T_setup,
1744  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as setup." ) );
1745 
1746  T token;
1747  NETCLASS* defaultNetClass = m_board->GetDesignSettings().GetDefault();
1748  BOARD_DESIGN_SETTINGS& designSettings = m_board->GetDesignSettings();
1749  ZONE_SETTINGS& zoneSettings = designSettings.GetDefaultZoneSettings();
1750 
1751  // Missing soldermask min width value means that the user has set the value to 0 and
1752  // not the default value (0.25mm)
1753  designSettings.m_SolderMaskMinWidth = 0;
1754 
1755  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
1756  {
1757  if( token != T_LEFT )
1758  Expecting( T_LEFT );
1759 
1760  token = NextTok();
1761 
1762  switch( token )
1763  {
1764  case T_stackup:
1765  parseBoardStackup();
1766  break;
1767 
1768  case T_last_trace_width: // not used now
1769  /* lastTraceWidth =*/ parseBoardUnits( T_last_trace_width );
1770  NeedRIGHT();
1771  break;
1772 
1773  case T_user_trace_width:
1774  {
1775  // Make room for the netclass value
1776  if( designSettings.m_TrackWidthList.empty() )
1777  designSettings.m_TrackWidthList.emplace_back( 0 );
1778 
1779  designSettings.m_TrackWidthList.push_back( parseBoardUnits( T_user_trace_width ) );
1780  m_board->m_LegacyDesignSettingsLoaded = true;
1781  NeedRIGHT();
1782  break;
1783  }
1784 
1785  case T_trace_clearance:
1786  defaultNetClass->SetClearance( parseBoardUnits( T_trace_clearance ) );
1787  m_board->m_LegacyDesignSettingsLoaded = true;
1788  NeedRIGHT();
1789  break;
1790 
1791  case T_zone_clearance:
1792  zoneSettings.m_ZoneClearance = parseBoardUnits( T_zone_clearance );
1793  m_board->m_LegacyDesignSettingsLoaded = true;
1794  NeedRIGHT();
1795  break;
1796 
1797  case T_zone_45_only:
1798  zoneSettings.m_Zone_45_Only = parseBool();
1799  m_board->m_LegacyDesignSettingsLoaded = true;
1800  NeedRIGHT();
1801  break;
1802 
1803  case T_clearance_min:
1804  designSettings.m_MinClearance = parseBoardUnits( T_clearance_min );
1805  m_board->m_LegacyDesignSettingsLoaded = true;
1806  NeedRIGHT();
1807  break;
1808 
1809  case T_trace_min:
1810  designSettings.m_TrackMinWidth = parseBoardUnits( T_trace_min );
1811  m_board->m_LegacyDesignSettingsLoaded = true;
1812  NeedRIGHT();
1813  break;
1814 
1815  case T_via_size:
1816  defaultNetClass->SetViaDiameter( parseBoardUnits( T_via_size ) );
1817  m_board->m_LegacyDesignSettingsLoaded = true;
1818  NeedRIGHT();
1819  break;
1820 
1821  case T_via_drill:
1822  defaultNetClass->SetViaDrill( parseBoardUnits( T_via_drill ) );
1823  m_board->m_LegacyDesignSettingsLoaded = true;
1824  NeedRIGHT();
1825  break;
1826 
1827  case T_via_min_annulus:
1828  designSettings.m_ViasMinAnnularWidth = parseBoardUnits( T_via_min_annulus );
1829  m_board->m_LegacyDesignSettingsLoaded = true;
1830  NeedRIGHT();
1831  break;
1832 
1833  case T_via_min_size:
1834  designSettings.m_ViasMinSize = parseBoardUnits( T_via_min_size );
1835  m_board->m_LegacyDesignSettingsLoaded = true;
1836  NeedRIGHT();
1837  break;
1838 
1839  case T_through_hole_min:
1840  designSettings.m_MinThroughDrill = parseBoardUnits( T_through_hole_min );
1841  m_board->m_LegacyDesignSettingsLoaded = true;
1842  NeedRIGHT();
1843  break;
1844 
1845  // Legacy token for T_through_hole_min
1846  case T_via_min_drill:
1847  designSettings.m_MinThroughDrill = parseBoardUnits( T_via_min_drill );
1848  m_board->m_LegacyDesignSettingsLoaded = true;
1849  NeedRIGHT();
1850  break;
1851 
1852  case T_hole_to_hole_min:
1853  designSettings.m_HoleToHoleMin = parseBoardUnits( T_hole_to_hole_min );
1854  m_board->m_LegacyDesignSettingsLoaded = true;
1855  NeedRIGHT();
1856  break;
1857 
1858  case T_user_via:
1859  {
1860  int viaSize = parseBoardUnits( "user via size" );
1861  int viaDrill = parseBoardUnits( "user via drill" );
1862 
1863  // Make room for the netclass value
1864  if( designSettings.m_ViasDimensionsList.empty() )
1865  designSettings.m_ViasDimensionsList.emplace_back( VIA_DIMENSION( 0, 0 ) );
1866 
1867  designSettings.m_ViasDimensionsList.emplace_back( VIA_DIMENSION( viaSize, viaDrill ) );
1868  m_board->m_LegacyDesignSettingsLoaded = true;
1869  NeedRIGHT();
1870  break;
1871  }
1872 
1873  case T_uvia_size:
1874  defaultNetClass->SetuViaDiameter( parseBoardUnits( T_uvia_size ) );
1875  m_board->m_LegacyDesignSettingsLoaded = true;
1876  NeedRIGHT();
1877  break;
1878 
1879  case T_uvia_drill:
1880  defaultNetClass->SetuViaDrill( parseBoardUnits( T_uvia_drill ) );
1881  m_board->m_LegacyDesignSettingsLoaded = true;
1882  NeedRIGHT();
1883  break;
1884 
1885  case T_uvias_allowed:
1886  designSettings.m_MicroViasAllowed = parseBool();
1887  m_board->m_LegacyDesignSettingsLoaded = true;
1888  NeedRIGHT();
1889  break;
1890 
1891  case T_blind_buried_vias_allowed:
1892  designSettings.m_BlindBuriedViaAllowed = parseBool();
1893  m_board->m_LegacyDesignSettingsLoaded = true;
1894  NeedRIGHT();
1895  break;
1896 
1897  case T_uvia_min_size:
1898  designSettings.m_MicroViasMinSize = parseBoardUnits( T_uvia_min_size );
1899  m_board->m_LegacyDesignSettingsLoaded = true;
1900  NeedRIGHT();
1901  break;
1902 
1903  case T_uvia_min_drill:
1904  designSettings.m_MicroViasMinDrill = parseBoardUnits( T_uvia_min_drill );
1905  m_board->m_LegacyDesignSettingsLoaded = true;
1906  NeedRIGHT();
1907  break;
1908 
1909  case T_user_diff_pair:
1910  {
1911  int width = parseBoardUnits( "user diff-pair width" );
1912  int gap = parseBoardUnits( "user diff-pair gap" );
1913  int viaGap = parseBoardUnits( "user diff-pair via gap" );
1914  designSettings.m_DiffPairDimensionsList.emplace_back(
1915  DIFF_PAIR_DIMENSION( width, gap, viaGap ) );
1916  m_board->m_LegacyDesignSettingsLoaded = true;
1917  NeedRIGHT();
1918  }
1919 
1920  break;
1921 
1922  case T_segment_width: // note: legacy (pre-6.0) token
1923  designSettings.m_LineThickness[ LAYER_CLASS_COPPER ] =
1924  parseBoardUnits( T_segment_width );
1925  m_board->m_LegacyDesignSettingsLoaded = true;
1926  NeedRIGHT();
1927  break;
1928 
1929  case T_edge_width: // note: legacy (pre-6.0) token
1930  designSettings.m_LineThickness[ LAYER_CLASS_EDGES ] = parseBoardUnits( T_edge_width );
1931  m_board->m_LegacyDesignSettingsLoaded = true;
1932  NeedRIGHT();
1933  break;
1934 
1935  case T_mod_edge_width: // note: legacy (pre-6.0) token
1936  designSettings.m_LineThickness[ LAYER_CLASS_SILK ] =
1937  parseBoardUnits( T_mod_edge_width );
1938  m_board->m_LegacyDesignSettingsLoaded = true;
1939  NeedRIGHT();
1940  break;
1941 
1942  case T_pcb_text_width: // note: legacy (pre-6.0) token
1943  designSettings.m_TextThickness[ LAYER_CLASS_COPPER ] =
1944  parseBoardUnits( T_pcb_text_width );
1945  m_board->m_LegacyDesignSettingsLoaded = true;
1946  NeedRIGHT();
1947  break;
1948 
1949  case T_mod_text_width: // note: legacy (pre-6.0) token
1950  designSettings.m_TextThickness[ LAYER_CLASS_SILK ] =
1951  parseBoardUnits( T_mod_text_width );
1952  m_board->m_LegacyDesignSettingsLoaded = true;
1953  NeedRIGHT();
1954  break;
1955 
1956  case T_pcb_text_size: // note: legacy (pre-6.0) token
1957  designSettings.m_TextSize[ LAYER_CLASS_COPPER ].x = parseBoardUnits( "pcb text width" );
1958  designSettings.m_TextSize[ LAYER_CLASS_COPPER ].y =
1959  parseBoardUnits( "pcb text height" );
1960  m_board->m_LegacyDesignSettingsLoaded = true;
1961  NeedRIGHT();
1962  break;
1963 
1964  case T_mod_text_size: // note: legacy (pre-6.0) token
1965  designSettings.m_TextSize[ LAYER_CLASS_SILK ].x =
1966  parseBoardUnits( "footprint text width" );
1967  designSettings.m_TextSize[ LAYER_CLASS_SILK ].y =
1968  parseBoardUnits( "footprint text height" );
1969  m_board->m_LegacyDesignSettingsLoaded = true;
1970  NeedRIGHT();
1971  break;
1972 
1973  case T_defaults:
1974  parseDefaults( designSettings );
1975  m_board->m_LegacyDesignSettingsLoaded = true;
1976  break;
1977 
1978  case T_pad_size:
1979  {
1980  wxSize sz;
1981  sz.SetWidth( parseBoardUnits( "master pad width" ) );
1982  sz.SetHeight( parseBoardUnits( "master pad height" ) );
1983  designSettings.m_Pad_Master->SetSize( sz );
1984  m_board->m_LegacyDesignSettingsLoaded = true;
1985  NeedRIGHT();
1986  break;
1987  }
1988 
1989  case T_pad_drill:
1990  {
1991  int drillSize = parseBoardUnits( T_pad_drill );
1992  designSettings.m_Pad_Master->SetDrillSize( wxSize( drillSize, drillSize ) );
1993  m_board->m_LegacyDesignSettingsLoaded = true;
1994  NeedRIGHT();
1995  break;
1996  }
1997 
1998  case T_pad_to_mask_clearance:
1999  designSettings.m_SolderMaskMargin = parseBoardUnits( T_pad_to_mask_clearance );
2000  NeedRIGHT();
2001  break;
2002 
2003  case T_solder_mask_min_width:
2004  designSettings.m_SolderMaskMinWidth = parseBoardUnits( T_solder_mask_min_width );
2005  NeedRIGHT();
2006  break;
2007 
2008  case T_pad_to_paste_clearance:
2009  designSettings.m_SolderPasteMargin = parseBoardUnits( T_pad_to_paste_clearance );
2010  NeedRIGHT();
2011  break;
2012 
2013  case T_pad_to_paste_clearance_ratio:
2014  designSettings.m_SolderPasteMarginRatio = parseDouble( T_pad_to_paste_clearance_ratio );
2015  NeedRIGHT();
2016  break;
2017 
2018  case T_aux_axis_origin:
2019  {
2020  int x = parseBoardUnits( "auxiliary origin X" );
2021  int y = parseBoardUnits( "auxiliary origin Y" );
2022  designSettings.m_AuxOrigin = wxPoint( x, y );
2023 
2024  // Aux origin still stored in board for the moment
2025  //m_board->m_LegacyDesignSettingsLoaded = true;
2026  NeedRIGHT();
2027  break;
2028  }
2029 
2030  case T_grid_origin:
2031  {
2032  int x = parseBoardUnits( "grid origin X" );
2033  int y = parseBoardUnits( "grid origin Y" );
2034  designSettings.m_GridOrigin = wxPoint( x, y );
2035  // Grid origin still stored in board for the moment
2036  //m_board->m_LegacyDesignSettingsLoaded = true;
2037  NeedRIGHT();
2038  break;
2039  }
2040 
2041  // Stored in board prior to 6.0
2042  case T_visible_elements:
2043  {
2044  // Make sure to start with DefaultVisible so all new layers are set
2045  m_board->m_LegacyVisibleItems = GAL_SET::DefaultVisible();
2046 
2047  int visible = parseHex() | MIN_VISIBILITY_MASK;
2048 
2049  for( size_t i = 0; i < sizeof( int ) * CHAR_BIT; i++ )
2050  m_board->m_LegacyVisibleItems.set( i, visible & ( 1u << i ) );
2051 
2052  NeedRIGHT();
2053  break;
2054  }
2055 
2056  case T_max_error:
2057  designSettings.m_MaxError = parseBoardUnits( T_max_error );
2058  m_board->m_LegacyDesignSettingsLoaded = true;
2059  NeedRIGHT();
2060  break;
2061 
2062  case T_filled_areas_thickness: // Note: legacy (early 5.99) token
2063  designSettings.m_ZoneFillVersion = parseBool() ? 5 : 6;
2064  m_board->m_LegacyDesignSettingsLoaded = true;
2065  NeedRIGHT();
2066  break;
2067 
2068  case T_pcbplotparams:
2069  {
2070  PCB_PLOT_PARAMS plotParams;
2071  PCB_PLOT_PARAMS_PARSER parser( reader );
2072  // parser must share the same current line as our current PCB parser
2073  // synchronize it.
2074  parser.SyncLineReaderWith( *this );
2075 
2076  plotParams.Parse( &parser );
2077  SyncLineReaderWith( parser );
2078 
2079  m_board->SetPlotOptions( plotParams );
2080  break;
2081  }
2082 
2083  default:
2084  Unexpected( CurText() );
2085  }
2086  }
2087 }
2088 
2089 
2091 {
2092  T token;
2093 
2094  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2095  {
2096  if( token != T_LEFT )
2097  Expecting( T_LEFT );
2098 
2099  token = NextTok();
2100 
2101  switch( token )
2102  {
2103  case T_edge_clearance:
2104  designSettings.m_CopperEdgeClearance = parseBoardUnits( T_edge_clearance );
2105  m_board->m_LegacyCopperEdgeClearanceLoaded = true;
2106  NeedRIGHT();
2107  break;
2108 
2109  case T_copper_line_width:
2110  designSettings.m_LineThickness[ LAYER_CLASS_COPPER ] = parseBoardUnits( token );
2111  NeedRIGHT();
2112  break;
2113 
2114  case T_copper_text_dims:
2115  parseDefaultTextDims( designSettings, LAYER_CLASS_COPPER );
2116  break;
2117 
2118  case T_courtyard_line_width:
2119  designSettings.m_LineThickness[ LAYER_CLASS_COURTYARD ] = parseBoardUnits( token );
2120  NeedRIGHT();
2121  break;
2122 
2123  case T_edge_cuts_line_width:
2124  designSettings.m_LineThickness[ LAYER_CLASS_EDGES ] = parseBoardUnits( token );
2125  NeedRIGHT();
2126  break;
2127 
2128  case T_silk_line_width:
2129  designSettings.m_LineThickness[ LAYER_CLASS_SILK ] = parseBoardUnits( token );
2130  NeedRIGHT();
2131  break;
2132 
2133  case T_silk_text_dims:
2134  parseDefaultTextDims( designSettings, LAYER_CLASS_SILK );
2135  break;
2136 
2137  case T_fab_layers_line_width:
2138  designSettings.m_LineThickness[ LAYER_CLASS_FAB ] = parseBoardUnits( token );
2139  NeedRIGHT();
2140  break;
2141 
2142  case T_fab_layers_text_dims:
2143  parseDefaultTextDims( designSettings, LAYER_CLASS_FAB );
2144  break;
2145 
2146  case T_other_layers_line_width:
2147  designSettings.m_LineThickness[ LAYER_CLASS_OTHERS ] = parseBoardUnits( token );
2148  NeedRIGHT();
2149  break;
2150 
2151  case T_other_layers_text_dims:
2152  parseDefaultTextDims( designSettings, LAYER_CLASS_OTHERS );
2153  break;
2154 
2155  case T_dimension_units:
2156  designSettings.m_DimensionUnitsMode =
2157  static_cast<DIM_UNITS_MODE>( parseInt( "dimension units" ) );
2158  NeedRIGHT();
2159  break;
2160 
2161  case T_dimension_precision:
2162  designSettings.m_DimensionPrecision = parseInt( "dimension precision" );
2163  NeedRIGHT();
2164  break;
2165 
2166  default:
2167  Unexpected( CurText() );
2168  }
2169  }
2170 }
2171 
2172 
2174 {
2175  T token;
2176 
2177  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2178  {
2179  if( token == T_LEFT )
2180  token = NextTok();
2181 
2182  switch( token )
2183  {
2184  case T_size:
2185  aSettings.m_TextSize[ aLayer ].x = parseBoardUnits( "default text size X" );
2186  aSettings.m_TextSize[ aLayer ].y = parseBoardUnits( "default text size Y" );
2187  NeedRIGHT();
2188  break;
2189 
2190  case T_thickness:
2191  aSettings.m_TextThickness[ aLayer ] = parseBoardUnits( "default text width" );
2192  NeedRIGHT();
2193  break;
2194 
2195  case T_italic:
2196  aSettings.m_TextItalic[ aLayer ] = true;
2197  break;
2198 
2199  case T_keep_upright:
2200  aSettings.m_TextUpright[ aLayer ] = true;
2201  break;
2202 
2203  default:
2204  Expecting( "size, thickness, italic or keep_upright" );
2205  }
2206  }
2207 }
2208 
2209 
2211 {
2212  wxCHECK_RET( CurTok() == T_net,
2213  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as net." ) );
2214 
2215  int netCode = parseInt( "net number" );
2216 
2217  NeedSYMBOLorNUMBER();
2218  wxString name = FromUTF8();
2219 
2220  // Convert overbar syntax from `~...~` to `~{...}`. These were left out of the first merge
2221  // so the version is a bit later.
2222  if( m_requiredVersion < 20210606 )
2224 
2225  NeedRIGHT();
2226 
2227  // net 0 should be already in list, so store this net
2228  // if it is not the net 0, or if the net 0 does not exists.
2229  // (TODO: a better test.)
2230  if( netCode > NETINFO_LIST::UNCONNECTED || !m_board->FindNet( NETINFO_LIST::UNCONNECTED ) )
2231  {
2232  NETINFO_ITEM* net = new NETINFO_ITEM( m_board, name, netCode );
2233  m_board->Add( net );
2234 
2235  // Store the new code mapping
2236  pushValueIntoMap( netCode, net->GetNetCode() );
2237  }
2238 }
2239 
2240 
2242 {
2243  wxCHECK_RET( CurTok() == T_net_class,
2244  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as net class." ) );
2245 
2246  T token;
2247 
2248  NETCLASSPTR nc = std::make_shared<NETCLASS>( wxEmptyString );
2249 
2250  // Read netclass name (can be a name or just a number like track width)
2251  NeedSYMBOLorNUMBER();
2252  nc->SetName( FromUTF8() );
2253  NeedSYMBOL();
2254  nc->SetDescription( FromUTF8() );
2255 
2256  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2257  {
2258  if( token != T_LEFT )
2259  Expecting( T_LEFT );
2260 
2261  token = NextTok();
2262 
2263  switch( token )
2264  {
2265  case T_clearance:
2266  nc->SetClearance( parseBoardUnits( T_clearance ) );
2267  break;
2268 
2269  case T_trace_width:
2270  nc->SetTrackWidth( parseBoardUnits( T_trace_width ) );
2271  break;
2272 
2273  case T_via_dia:
2274  nc->SetViaDiameter( parseBoardUnits( T_via_dia ) );
2275  break;
2276 
2277  case T_via_drill:
2278  nc->SetViaDrill( parseBoardUnits( T_via_drill ) );
2279  break;
2280 
2281  case T_uvia_dia:
2282  nc->SetuViaDiameter( parseBoardUnits( T_uvia_dia ) );
2283  break;
2284 
2285  case T_uvia_drill:
2286  nc->SetuViaDrill( parseBoardUnits( T_uvia_drill ) );
2287  break;
2288 
2289  case T_diff_pair_width:
2290  nc->SetDiffPairWidth( parseBoardUnits( T_diff_pair_width ) );
2291  break;
2292 
2293  case T_diff_pair_gap:
2294  nc->SetDiffPairGap( parseBoardUnits( T_diff_pair_gap ) );
2295  break;
2296 
2297  case T_add_net:
2298  NeedSYMBOLorNUMBER();
2299 
2300  // Convert overbar syntax from `~...~` to `~{...}`. These were left out of the
2301  // first merge so the version is a bit later.
2302  if( m_requiredVersion < 20210606 )
2303  nc->Add( ConvertToNewOverbarNotation( FromUTF8() ) );
2304  else
2305  nc->Add( FromUTF8() );
2306 
2307  break;
2308 
2309  default:
2310  Expecting( "clearance, trace_width, via_dia, via_drill, uvia_dia, uvia_drill, "
2311  "diff_pair_width, diff_pair_gap or add_net" );
2312  }
2313 
2314  NeedRIGHT();
2315  }
2316 
2317  if( !m_board->GetDesignSettings().GetNetClasses().Add( nc ) )
2318  {
2319  // Must have been a name conflict, this is a bad board file.
2320  // User may have done a hand edit to the file.
2321 
2322  // unique_ptr will delete nc on this code path
2323 
2324  wxString error;
2325  error.Printf( _( "Duplicate NETCLASS name '%s' in file '%s' at line %d, offset %d." ),
2326  nc->GetName().GetData(), CurSource().GetData(), CurLineNumber(),
2327  CurOffset() );
2328  THROW_IO_ERROR( error );
2329  }
2330 }
2331 
2332 
2334 {
2335  wxCHECK_MSG( CurTok() == T_gr_arc || CurTok() == T_gr_circle || CurTok() == T_gr_curve ||
2336  CurTok() == T_gr_rect || CurTok() == T_gr_line || CurTok() == T_gr_poly, nullptr,
2337  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as PCB_SHAPE." ) );
2338 
2339  T token;
2340  wxPoint pt;
2341  std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( nullptr );
2342 
2343  switch( CurTok() )
2344  {
2345  case T_gr_arc:
2346  shape->SetShape( SHAPE_T::ARC );
2347  token = NextTok();
2348 
2349  if( token == T_locked )
2350  {
2351  shape->SetLocked( true );
2352  token = NextTok();
2353  }
2354 
2355  if( token != T_LEFT )
2356  Expecting( T_LEFT );
2357 
2358  token = NextTok();
2359 
2360  // the start keyword actually gives the arc center
2361  // Allows also T_center for future change
2362  if( token != T_start && token != T_center )
2363  Expecting( T_start );
2364 
2365  pt.x = parseBoardUnits( "X coordinate" );
2366  pt.y = parseBoardUnits( "Y coordinate" );
2367  shape->SetCenter( pt );
2368  NeedRIGHT();
2369  NeedLEFT();
2370  token = NextTok();
2371 
2372  if( token != T_end ) // the end keyword actually gives the starting point of the arc
2373  Expecting( T_end );
2374 
2375  pt.x = parseBoardUnits( "X coordinate" );
2376  pt.y = parseBoardUnits( "Y coordinate" );
2377  shape->SetArcStart( pt );
2378  NeedRIGHT();
2379  break;
2380 
2381  case T_gr_circle:
2382  shape->SetShape( SHAPE_T::CIRCLE );
2383  token = NextTok();
2384 
2385  if( token == T_locked )
2386  {
2387  shape->SetLocked( true );
2388  token = NextTok();
2389  }
2390 
2391  if( token != T_LEFT )
2392  Expecting( T_LEFT );
2393 
2394  token = NextTok();
2395 
2396  if( token != T_center )
2397  Expecting( T_center );
2398 
2399  pt.x = parseBoardUnits( "X coordinate" );
2400  pt.y = parseBoardUnits( "Y coordinate" );
2401  shape->SetCenter( pt );
2402  NeedRIGHT();
2403  NeedLEFT();
2404 
2405  token = NextTok();
2406 
2407  if( token != T_end )
2408  Expecting( T_end );
2409 
2410  pt.x = parseBoardUnits( "X coordinate" );
2411  pt.y = parseBoardUnits( "Y coordinate" );
2412  shape->SetEnd( pt );
2413  NeedRIGHT();
2414  break;
2415 
2416  case T_gr_curve:
2417  shape->SetShape( SHAPE_T::BEZIER );
2418  token = NextTok();
2419 
2420  if( token == T_locked )
2421  {
2422  shape->SetLocked( true );
2423  token = NextTok();
2424  }
2425 
2426  if( token != T_LEFT )
2427  Expecting( T_LEFT );
2428 
2429  token = NextTok();
2430 
2431  if( token != T_pts )
2432  Expecting( T_pts );
2433 
2434  shape->SetStart( parseXY() );
2435  shape->SetBezierC1( parseXY());
2436  shape->SetBezierC2( parseXY());
2437  shape->SetEnd( parseXY() );
2438  NeedRIGHT();
2439  break;
2440 
2441  case T_gr_rect:
2442  shape->SetShape( SHAPE_T::RECT );
2443  token = NextTok();
2444 
2445  if( token == T_locked )
2446  {
2447  shape->SetLocked( true );
2448  token = NextTok();
2449  }
2450 
2451  if( token != T_LEFT )
2452  Expecting( T_LEFT );
2453 
2454  token = NextTok();
2455 
2456  if( token != T_start )
2457  Expecting( T_start );
2458 
2459  pt.x = parseBoardUnits( "X coordinate" );
2460  pt.y = parseBoardUnits( "Y coordinate" );
2461  shape->SetStart( pt );
2462  NeedRIGHT();
2463  NeedLEFT();
2464  token = NextTok();
2465 
2466  if( token != T_end )
2467  Expecting( T_end );
2468 
2469  pt.x = parseBoardUnits( "X coordinate" );
2470  pt.y = parseBoardUnits( "Y coordinate" );
2471  shape->SetEnd( pt );
2472  NeedRIGHT();
2473  break;
2474 
2475  case T_gr_line:
2476  // Default PCB_SHAPE type is S_SEGMENT.
2477  token = NextTok();
2478 
2479  if( token == T_locked )
2480  {
2481  shape->SetLocked( true );
2482  token = NextTok();
2483  }
2484 
2485  if( token != T_LEFT )
2486  Expecting( T_LEFT );
2487 
2488  token = NextTok();
2489 
2490  if( token != T_start )
2491  Expecting( T_start );
2492 
2493  pt.x = parseBoardUnits( "X coordinate" );
2494  pt.y = parseBoardUnits( "Y coordinate" );
2495  shape->SetStart( pt );
2496  NeedRIGHT();
2497  NeedLEFT();
2498  token = NextTok();
2499 
2500  if( token != T_end )
2501  Expecting( T_end );
2502 
2503  pt.x = parseBoardUnits( "X coordinate" );
2504  pt.y = parseBoardUnits( "Y coordinate" );
2505  shape->SetEnd( pt );
2506  NeedRIGHT();
2507  break;
2508 
2509  case T_gr_poly:
2510  {
2511  shape->SetShape( SHAPE_T::POLY );
2512  shape->SetWidth( 0 ); // this is the default value. will be (perhaps) modified later
2513  shape->SetPolyPoints( {} );
2514 
2515  SHAPE_LINE_CHAIN& outline = shape->GetPolyShape().Outline( 0 );
2516 
2517  token = NextTok();
2518 
2519  if( token == T_locked )
2520  {
2521  shape->SetLocked( true );
2522  token = NextTok();
2523  }
2524 
2525  if( token != T_LEFT )
2526  Expecting( T_LEFT );
2527 
2528  token = NextTok();
2529 
2530  if( token != T_pts )
2531  Expecting( T_pts );
2532 
2533  while( (token = NextTok() ) != T_RIGHT )
2534  {
2535  parseOutlinePoints( outline );
2536  }
2537 
2538  break;
2539  }
2540 
2541  default:
2542  Expecting( "gr_arc, gr_circle, gr_curve, gr_line, gr_poly, or gp_rect" );
2543  }
2544 
2545  bool foundFill = false;
2546 
2547  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2548  {
2549  if( token != T_LEFT )
2550  Expecting( T_LEFT );
2551 
2552  token = NextTok();
2553 
2554  switch( token )
2555  {
2556  case T_angle:
2557  shape->SetAngle( parseDouble( "segment angle" ) * 10.0 );
2558  NeedRIGHT();
2559  break;
2560 
2561  case T_layer:
2562  shape->SetLayer( parseBoardItemLayer() );
2563  NeedRIGHT();
2564  break;
2565 
2566  case T_width:
2567  shape->SetWidth( parseBoardUnits( T_width ) );
2568  NeedRIGHT();
2569  break;
2570 
2571  case T_tstamp:
2572  NextTok();
2573  const_cast<KIID&>( shape->m_Uuid ) = CurStrToKIID();
2574  NeedRIGHT();
2575  break;
2576 
2577  case T_fill:
2578  foundFill = true;
2579 
2580  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2581  {
2582  if( token == T_LEFT )
2583  token = NextTok();
2584 
2585  switch( token )
2586  {
2587  // T_yes was used to indicate filling when first introduced,
2588  // so treat it like a solid fill since that was the only fill available
2589  case T_yes:
2590  case T_solid:
2591  shape->SetFilled( true );
2592  break;
2593 
2594  case T_none:
2595  shape->SetFilled( false );
2596  break;
2597 
2598  default:
2599  Expecting( "yes, none, solid" );
2600  }
2601  }
2602 
2603  break;
2604 
2605  // We continue to parse the status field but it is no longer written
2606  case T_status:
2607  shape->SetStatus( static_cast<EDA_ITEM_FLAGS>( parseHex() ) );
2608  NeedRIGHT();
2609  break;
2610 
2611  // Continue to process "(locked)" format which was output during 5.99 development
2612  case T_locked:
2613  shape->SetLocked( true );
2614  NeedRIGHT();
2615  break;
2616 
2617  default:
2618  Expecting( "layer, width, fill, tstamp, locked or status" );
2619  }
2620  }
2621 
2622  if( !foundFill )
2623  {
2624  // Legacy versions didn't have a filled flag but allowed some shapes to indicate they
2625  // should be filled by specifying a 0 stroke-width.
2626  if( shape->GetWidth() == 0
2627  && ( shape->GetShape() == SHAPE_T::RECT || shape->GetShape() == SHAPE_T::CIRCLE ) )
2628  {
2629  shape->SetFilled( true );
2630  }
2631  else if( shape->GetShape() == SHAPE_T::POLY && shape->GetLayer() != Edge_Cuts )
2632  {
2633  // Polygons on non-Edge_Cuts layers were always filled.
2634  shape->SetFilled( true );
2635  }
2636  }
2637 
2638  // Only filled shapes may have a zero line-width. This is not permitted in KiCad but some
2639  // external tools can generate invalid files.
2640  if( shape->GetWidth() <= 0 && !shape->IsFilled() )
2641  {
2642  shape->SetWidth( Millimeter2iu( DEFAULT_LINE_WIDTH ) );
2643  }
2644 
2645  return shape.release();
2646 }
2647 
2648 
2650 {
2651  wxCHECK_MSG( CurTok() == T_gr_text, nullptr,
2652  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as PCB_TEXT." ) );
2653 
2654  T token;
2655 
2656  std::unique_ptr<PCB_TEXT> text = std::make_unique<PCB_TEXT>( m_board );
2657  NeedSYMBOLorNUMBER();
2658 
2659  text->SetText( FromUTF8() );
2660  NeedLEFT();
2661  token = NextTok();
2662 
2663  if( token != T_at )
2664  Expecting( T_at );
2665 
2666  wxPoint pt;
2667 
2668  pt.x = parseBoardUnits( "X coordinate" );
2669  pt.y = parseBoardUnits( "Y coordinate" );
2670  text->SetTextPos( pt );
2671 
2672  // If there is no orientation defined, then it is the default value of 0 degrees.
2673  token = NextTok();
2674 
2675  if( token == T_NUMBER )
2676  {
2677  text->SetTextAngle( parseDouble() * 10.0 );
2678  NeedRIGHT();
2679  }
2680  else if( token != T_RIGHT )
2681  {
2682  Unexpected( CurText() );
2683  }
2684 
2685  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2686  {
2687  if( token != T_LEFT )
2688  Expecting( T_LEFT );
2689 
2690  token = NextTok();
2691 
2692  switch( token )
2693  {
2694  case T_layer:
2695  text->SetLayer( parseBoardItemLayer() );
2696  NeedRIGHT();
2697  break;
2698 
2699  case T_tstamp:
2700  NextTok();
2701  const_cast<KIID&>( text->m_Uuid ) = CurStrToKIID();
2702  NeedRIGHT();
2703  break;
2704 
2705  case T_effects:
2706  parseEDA_TEXT( (EDA_TEXT*) text.get() );
2707  break;
2708 
2709  default:
2710  Expecting( "layer, tstamp or effects" );
2711  }
2712  }
2713 
2714  return text.release();
2715 }
2716 
2717 
2719 {
2720  wxCHECK_MSG( CurTok() == T_dimension, nullptr,
2721  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as DIMENSION." ) );
2722 
2723  T token;
2724  bool locked = false;
2725  std::unique_ptr<PCB_DIMENSION_BASE> dimension;
2726 
2727  token = NextTok();
2728 
2729  if( token == T_locked )
2730  {
2731  locked = true;
2732  token = NextTok();
2733  }
2734 
2735  // skip value that used to be saved
2736  if( token != T_LEFT )
2737  NeedLEFT();
2738 
2739  token = NextTok();
2740 
2741  bool isLegacyDimension = false;
2742 
2743  // Old format
2744  if( token == T_width )
2745  {
2746  isLegacyDimension = true;
2747  dimension = std::make_unique<PCB_DIM_ALIGNED>( nullptr );
2748  dimension->SetLineThickness( parseBoardUnits( "dimension width value" ) );
2749  NeedRIGHT();
2750  }
2751  else
2752  {
2753  if( token != T_type )
2754  Expecting( T_type );
2755 
2756  switch( NextTok() )
2757  {
2758  case T_aligned:
2759  dimension = std::make_unique<PCB_DIM_ALIGNED>( nullptr );
2760  break;
2761 
2762  case T_orthogonal:
2763  dimension = std::make_unique<PCB_DIM_ORTHOGONAL>( nullptr );
2764  break;
2765 
2766  case T_leader:
2767  dimension = std::make_unique<PCB_DIM_LEADER>( nullptr );
2768  break;
2769 
2770  case T_center:
2771  dimension = std::make_unique<PCB_DIM_CENTER>( nullptr );
2772  break;
2773 
2774  default:
2775  wxFAIL_MSG( wxT( "Cannot parse unknown dimension type %s" ) +
2776  GetTokenString( CurTok() ) );
2777  }
2778 
2779  NeedRIGHT();
2780  }
2781 
2782  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2783  {
2784  if( token != T_LEFT )
2785  Expecting( T_LEFT );
2786 
2787  token = NextTok();
2788 
2789  switch( token )
2790  {
2791  case T_layer:
2792  dimension->SetLayer( parseBoardItemLayer() );
2793  NeedRIGHT();
2794  break;
2795 
2796  case T_tstamp:
2797  NextTok();
2798  const_cast<KIID&>( dimension->m_Uuid ) = CurStrToKIID();
2799  NeedRIGHT();
2800  break;
2801 
2802  case T_gr_text:
2803  {
2804  PCB_TEXT* text = parsePCB_TEXT();
2805  dimension->Text() = *text;
2806 
2807  // The text is part of the dimension and shares its uuid
2808  const_cast<KIID&>( dimension->Text().m_Uuid ) = dimension->m_Uuid;
2809 
2810  // Fetch other dimension properties out of the text item
2811  dimension->Text().SetTextPos( text->GetTextPos() );
2812 
2813  if( isLegacyDimension )
2814  {
2815  EDA_UNITS units = EDA_UNITS::INCHES;
2816  FetchUnitsFromString( text->GetText(), units );
2817  dimension->SetUnits( units );
2818  }
2819 
2820  delete text;
2821  break;
2822  }
2823 
2824  // New format: feature points
2825  case T_pts:
2826  {
2827  wxPoint point;
2828 
2829  parseXY( &point.x, &point.y );
2830  dimension->SetStart( point );
2831  parseXY( &point.x, &point.y );
2832  dimension->SetEnd( point );
2833 
2834  NeedRIGHT();
2835  break;
2836  }
2837 
2838  case T_height:
2839  {
2840  wxCHECK_MSG( dimension->Type() == PCB_DIM_ALIGNED_T ||
2841  dimension->Type() == PCB_DIM_ORTHOGONAL_T, nullptr,
2842  wxT( "Invalid height token" ) );
2843  PCB_DIM_ALIGNED* aligned = static_cast<PCB_DIM_ALIGNED*>( dimension.get() );
2844  aligned->SetHeight( parseBoardUnits( "dimension height value" ) );
2845  NeedRIGHT();
2846  break;
2847  }
2848 
2849  case T_orientation:
2850  {
2851  wxCHECK_MSG( dimension->Type() == PCB_DIM_ORTHOGONAL_T, nullptr,
2852  wxT( "Invalid orientation token" ) );
2853  PCB_DIM_ORTHOGONAL* ortho = static_cast<PCB_DIM_ORTHOGONAL*>( dimension.get() );
2854 
2855  int orientation = parseInt( "orthogonal dimension orientation" );
2856  orientation = std::max( 0, std::min( 1, orientation ) );
2857  ortho->SetOrientation( static_cast<PCB_DIM_ORTHOGONAL::DIR>( orientation ) );
2858  NeedRIGHT();
2859  break;
2860  }
2861 
2862  case T_format:
2863  {
2864  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2865  {
2866  switch( token )
2867  {
2868  case T_LEFT:
2869  continue;
2870 
2871  case T_prefix:
2872  NeedSYMBOLorNUMBER();
2873  dimension->SetPrefix( FromUTF8() );
2874  NeedRIGHT();
2875  break;
2876 
2877  case T_suffix:
2878  NeedSYMBOLorNUMBER();
2879  dimension->SetSuffix( FromUTF8() );
2880  NeedRIGHT();
2881  break;
2882 
2883  case T_units:
2884  {
2885  int mode = parseInt( "dimension units mode" );
2886  mode = std::max( 0, std::min( 4, mode ) );
2887  dimension->SetUnitsMode( static_cast<DIM_UNITS_MODE>( mode ) );
2888  NeedRIGHT();
2889  break;
2890  }
2891 
2892  case T_units_format:
2893  {
2894  int format = parseInt( "dimension units format" );
2895  format = std::max( 0, std::min( 3, format ) );
2896  dimension->SetUnitsFormat( static_cast<DIM_UNITS_FORMAT>( format ) );
2897  NeedRIGHT();
2898  break;
2899  }
2900 
2901  case T_precision:
2902  dimension->SetPrecision( parseInt( "dimension precision" ) );
2903  NeedRIGHT();
2904  break;
2905 
2906  case T_override_value:
2907  NeedSYMBOLorNUMBER();
2908  dimension->SetOverrideTextEnabled( true );
2909  dimension->SetOverrideText( FromUTF8() );
2910  NeedRIGHT();
2911  break;
2912 
2913  case T_suppress_zeroes:
2914  dimension->SetSuppressZeroes( true );
2915  break;
2916 
2917  default:
2918  Expecting( "prefix, suffix, units, units_format, precision, override_value, "
2919  "suppress_zeroes" );
2920  }
2921  }
2922  break;
2923  }
2924 
2925  case T_style:
2926  {
2927  // new format: default to keep text aligned off unless token is present
2928  dimension->SetKeepTextAligned( false );
2929 
2930  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
2931  {
2932  switch( token )
2933  {
2934  case T_LEFT:
2935  continue;
2936 
2937  case T_thickness:
2938  dimension->SetLineThickness( parseBoardUnits( "extension line thickness" ) );
2939  NeedRIGHT();
2940  break;
2941 
2942  case T_arrow_length:
2943  dimension->SetArrowLength( parseBoardUnits( "arrow length" ) );
2944  NeedRIGHT();
2945  break;
2946 
2947  case T_text_position_mode:
2948  {
2949  int mode = parseInt( "dimension text position mode" );
2950  mode = std::max( 0, std::min( 3, mode ) );
2951  dimension->SetTextPositionMode( static_cast<DIM_TEXT_POSITION>( mode ) );
2952  NeedRIGHT();
2953  break;
2954  }
2955 
2956  case T_extension_height:
2957  {
2958  PCB_DIM_ALIGNED* aligned = dynamic_cast<PCB_DIM_ALIGNED*>( dimension.get() );
2959  wxCHECK_MSG( aligned, nullptr, wxT( "Invalid extension_height token" ) );
2960  aligned->SetExtensionHeight( parseBoardUnits( "extension height" ) );
2961  NeedRIGHT();
2962  break;
2963  }
2964 
2965  case T_extension_offset:
2966  dimension->SetExtensionOffset( parseBoardUnits( "extension offset" ) );
2967  NeedRIGHT();
2968  break;
2969 
2970  case T_keep_text_aligned:
2971  dimension->SetKeepTextAligned( true );
2972  break;
2973 
2974  case T_text_frame:
2975  {
2976  wxCHECK_MSG( dimension->Type() == PCB_DIM_LEADER_T, nullptr,
2977  wxT( "Invalid text_frame token" ) );
2978  PCB_DIM_LEADER* leader = static_cast<PCB_DIM_LEADER*>( dimension.get() );
2979 
2980  int textFrame = parseInt( "dimension text frame mode" );
2981  textFrame = std::max( 0, std::min( 3, textFrame ) );
2982  leader->SetTextFrame( static_cast<DIM_TEXT_FRAME>( textFrame ) );
2983  NeedRIGHT();
2984  break;
2985  }
2986 
2987  default:
2988  Expecting( "thickness, arrow_length, text_position_mode, extension_height, "
2989  "extension_offset" );
2990  }
2991  }
2992 
2993  break;
2994  }
2995 
2996  // Old format: feature1 stores a feature line. We only care about the origin.
2997  case T_feature1:
2998  {
2999  NeedLEFT();
3000  token = NextTok();
3001 
3002  if( token != T_pts )
3003  Expecting( T_pts );
3004 
3005  wxPoint point;
3006 
3007  parseXY( &point.x, &point.y );
3008  dimension->SetStart( point );
3009 
3010  parseXY( nullptr, nullptr ); // Ignore second point
3011  NeedRIGHT();
3012  NeedRIGHT();
3013  break;
3014  }
3015 
3016  // Old format: feature2 stores a feature line. We only care about the end point.
3017  case T_feature2:
3018  {
3019  NeedLEFT();
3020  token = NextTok();
3021 
3022  if( token != T_pts )
3023  Expecting( T_pts );
3024 
3025  wxPoint point;
3026 
3027  parseXY( &point.x, &point.y );
3028  dimension->SetEnd( point );
3029 
3030  parseXY( nullptr, nullptr ); // Ignore second point
3031 
3032  NeedRIGHT();
3033  NeedRIGHT();
3034  break;
3035  }
3036 
3037  case T_crossbar:
3038  {
3039  NeedLEFT();
3040  token = NextTok();
3041 
3042  if( token == T_pts )
3043  {
3044  // If we have a crossbar, we know we're an old aligned dimension
3045  PCB_DIM_ALIGNED* aligned = static_cast<PCB_DIM_ALIGNED*>( dimension.get() );
3046 
3047  // Old style: calculate height from crossbar
3048  wxPoint point1, point2;
3049  parseXY( &point1.x, &point1.y );
3050  parseXY( &point2.x, &point2.y );
3051  aligned->UpdateHeight( point2, point1 ); // Yes, backwards intentionally
3052  NeedRIGHT();
3053  }
3054 
3055  NeedRIGHT();
3056  break;
3057  }
3058 
3059  // Arrow: no longer saved; no-op
3060  case T_arrow1a:
3061  NeedLEFT();
3062  token = NextTok();
3063 
3064  if( token != T_pts )
3065  Expecting( T_pts );
3066 
3067  parseXY( nullptr, nullptr );
3068  parseXY( nullptr, nullptr );
3069  NeedRIGHT();
3070  NeedRIGHT();
3071  break;
3072 
3073  // Arrow: no longer saved; no-op
3074  case T_arrow1b:
3075  NeedLEFT();
3076  token = NextTok();
3077 
3078  if( token != T_pts )
3079  Expecting( T_pts );
3080 
3081  parseXY( nullptr, nullptr );
3082  parseXY( nullptr, nullptr );
3083  NeedRIGHT();
3084  NeedRIGHT();
3085  break;
3086 
3087  // Arrow: no longer saved; no-op
3088  case T_arrow2a:
3089  NeedLEFT();
3090  token = NextTok();
3091 
3092  if( token != T_pts )
3093  Expecting( T_pts );
3094 
3095  parseXY( nullptr, nullptr );
3096  parseXY( nullptr, nullptr );
3097  NeedRIGHT();
3098  NeedRIGHT();
3099  break;
3100 
3101  // Arrow: no longer saved; no-op
3102  case T_arrow2b:
3103  NeedLEFT();
3104  token = NextTok();
3105 
3106  if( token != T_pts )
3107  Expecting( T_pts );
3108 
3109  parseXY( nullptr, nullptr );
3110  parseXY( nullptr, nullptr );
3111  NeedRIGHT();
3112  NeedRIGHT();
3113  break;
3114 
3115  default:
3116  Expecting( "layer, tstamp, gr_text, feature1, feature2, crossbar, arrow1a, "
3117  "arrow1b, arrow2a, or arrow2b" );
3118  }
3119  }
3120 
3121  if( locked )
3122  dimension->SetLocked( true );
3123 
3124  dimension->Update();
3125 
3126  return dimension.release();
3127 }
3128 
3129 
3130 FOOTPRINT* PCB_PARSER::parseFOOTPRINT( wxArrayString* aInitialComments )
3131 {
3132  try
3133  {
3134  return parseFOOTPRINT_unchecked( aInitialComments );
3135  }
3136  catch( const PARSE_ERROR& parse_error )
3137  {
3138  if( m_tooRecent )
3139  throw FUTURE_FORMAT_ERROR( parse_error, GetRequiredVersion() );
3140  else
3141  throw;
3142  }
3143 }
3144 
3145 
3146 FOOTPRINT* PCB_PARSER::parseFOOTPRINT_unchecked( wxArrayString* aInitialComments )
3147 {
3148  wxCHECK_MSG( CurTok() == T_module || CurTok() == T_footprint, nullptr,
3149  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as FOOTPRINT." ) );
3150 
3151  wxString name;
3152  wxPoint pt;
3153  T token;
3154  LIB_ID fpid;
3155  int attributes = 0;
3156 
3157  std::unique_ptr<FOOTPRINT> footprint = std::make_unique<FOOTPRINT>( m_board );
3158 
3159  std::map<wxString, wxString> properties;
3160 
3161  footprint->SetInitialComments( aInitialComments );
3162 
3163  token = NextTok();
3164 
3165  if( !IsSymbol( token ) && token != T_NUMBER )
3166  Expecting( "symbol|number" );
3167 
3168  name = FromUTF8();
3169 
3170  if( !name.IsEmpty() && fpid.Parse( name, true ) >= 0 )
3171  {
3172  wxString error;
3173  error.Printf( _( "Invalid footprint ID in\nfile: '%s'\nline: %d\noffset: %d." ),
3174  CurSource(), CurLineNumber(), CurOffset() );
3175  THROW_IO_ERROR( error );
3176  }
3177 
3178  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3179  {
3180  if( token == T_LEFT )
3181  token = NextTok();
3182 
3183  switch( token )
3184  {
3185  case T_version:
3186  {
3187  // Theoretically a footprint nested in a PCB could declare its own version, though
3188  // as of writing this comment we don't do that. Just in case, take the greater
3189  // version.
3190  int this_version = parseInt( FromUTF8().mb_str( wxConvUTF8 ) );
3191  NeedRIGHT();
3192  m_requiredVersion = std::max( m_requiredVersion, this_version );
3193  m_tooRecent = ( m_requiredVersion > SEXPR_BOARD_FILE_VERSION );
3194  break;
3195  }
3196 
3197  case T_generator:
3198  // We currently ignore the generator when parsing. It is included in the file for manual
3199  // indication of where the footprint came from.
3200  NeedSYMBOL();
3201  NeedRIGHT();
3202  break;
3203 
3204  case T_locked:
3205  footprint->SetLocked( true );
3206  break;
3207 
3208  case T_placed:
3209  footprint->SetIsPlaced( true );
3210  break;
3211 
3212  case T_layer:
3213  {
3214  // Footprints can be only on the front side or the back side.
3215  // but because we can find some stupid layer in file, ensure a
3216  // acceptable layer is set for the footprint
3217  PCB_LAYER_ID layer = parseBoardItemLayer();
3218  footprint->SetLayer( layer == B_Cu ? B_Cu : F_Cu );
3219  NeedRIGHT();
3220  break;
3221  }
3222 
3223  case T_tedit:
3224  footprint->SetLastEditTime( parseHex() );
3225  NeedRIGHT();
3226  break;
3227 
3228  case T_tstamp:
3229  NextTok();
3230  const_cast<KIID&>( footprint->m_Uuid ) = CurStrToKIID();
3231  NeedRIGHT();
3232  break;
3233 
3234  case T_at:
3235  pt.x = parseBoardUnits( "X coordinate" );
3236  pt.y = parseBoardUnits( "Y coordinate" );
3237  footprint->SetPosition( pt );
3238  token = NextTok();
3239 
3240  if( token == T_NUMBER )
3241  {
3242  footprint->SetOrientation( parseDouble() * 10.0 );
3243  NeedRIGHT();
3244  }
3245  else if( token != T_RIGHT )
3246  {
3247  Expecting( T_RIGHT );
3248  }
3249 
3250  break;
3251 
3252  case T_descr:
3253  NeedSYMBOLorNUMBER(); // some symbols can be 0508, so a number is also a symbol here
3254  footprint->SetDescription( FromUTF8() );
3255  NeedRIGHT();
3256  break;
3257 
3258  case T_tags:
3259  NeedSYMBOLorNUMBER(); // some symbols can be 0508, so a number is also a symbol here
3260  footprint->SetKeywords( FromUTF8() );
3261  NeedRIGHT();
3262  break;
3263 
3264  case T_property:
3265  properties.insert( parseProperty() );
3266  break;
3267 
3268  case T_path:
3269  NeedSYMBOLorNUMBER(); // Paths can be numerical so a number is also a symbol here
3270  footprint->SetPath( KIID_PATH( FromUTF8() ) );
3271  NeedRIGHT();
3272  break;
3273 
3274  case T_autoplace_cost90:
3275  footprint->SetPlacementCost90( parseInt( "auto place cost at 90 degrees" ) );
3276  NeedRIGHT();
3277  break;
3278 
3279  case T_autoplace_cost180:
3280  footprint->SetPlacementCost180( parseInt( "auto place cost at 180 degrees" ) );
3281  NeedRIGHT();
3282  break;
3283 
3284  case T_solder_mask_margin:
3285  footprint->SetLocalSolderMaskMargin( parseBoardUnits( "local solder mask margin "
3286  "value" ) );
3287  NeedRIGHT();
3288  break;
3289 
3290  case T_solder_paste_margin:
3291  footprint->SetLocalSolderPasteMargin(
3292  parseBoardUnits( "local solder paste margin value" ) );
3293  NeedRIGHT();
3294  break;
3295 
3296  case T_solder_paste_ratio:
3297  footprint->SetLocalSolderPasteMarginRatio(
3298  parseDouble( "local solder paste margin ratio value" ) );
3299  NeedRIGHT();
3300  break;
3301 
3302  case T_clearance:
3303  footprint->SetLocalClearance( parseBoardUnits( "local clearance value" ) );
3304  NeedRIGHT();
3305  break;
3306 
3307  case T_zone_connect:
3308  footprint->SetZoneConnection((ZONE_CONNECTION) parseInt( "zone connection value" ) );
3309  NeedRIGHT();
3310  break;
3311 
3312  case T_thermal_width:
3313  footprint->SetThermalWidth( parseBoardUnits( "thermal width value" ) );
3314  NeedRIGHT();
3315  break;
3316 
3317  case T_thermal_gap:
3318  footprint->SetThermalGap( parseBoardUnits( "thermal gap value" ) );
3319  NeedRIGHT();
3320  break;
3321 
3322  case T_attr:
3323  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3324  {
3325  switch( token )
3326  {
3327  case T_virtual: // legacy token prior to version 20200826
3329  break;
3330 
3331  case T_through_hole:
3332  attributes |= FP_THROUGH_HOLE;
3333  break;
3334 
3335  case T_smd:
3336  attributes |= FP_SMD;
3337  break;
3338 
3339  case T_board_only:
3340  attributes |= FP_BOARD_ONLY;
3341  break;
3342 
3343  case T_exclude_from_pos_files:
3344  attributes |= FP_EXCLUDE_FROM_POS_FILES;
3345  break;
3346 
3347  case T_exclude_from_bom:
3348  attributes |= FP_EXCLUDE_FROM_BOM;
3349  break;
3350 
3351  default:
3352  Expecting( "through_hole, smd, virtual, board_only, exclude_from_pos_files "
3353  "or exclude_from_bom" );
3354  }
3355  }
3356 
3357  break;
3358 
3359  case T_fp_text:
3360  {
3361  FP_TEXT* text = parseFP_TEXT();
3362  text->SetParent( footprint.get() );
3363  double orientation = text->GetTextAngle();
3364  orientation -= footprint->GetOrientation();
3365  text->SetTextAngle( orientation );
3366  text->SetDrawCoord();
3367 
3368  switch( text->GetType() )
3369  {
3371  footprint->Reference() = *text;
3372  const_cast<KIID&>( footprint->Reference().m_Uuid ) = text->m_Uuid;
3373  delete text;
3374  break;
3375 
3377  footprint->Value() = *text;
3378  const_cast<KIID&>( footprint->Value().m_Uuid ) = text->m_Uuid;
3379  delete text;
3380  break;
3381 
3382  default:
3383  footprint->Add( text, ADD_MODE::APPEND );
3384  }
3385 
3386  break;
3387  }
3388 
3389  case T_fp_arc:
3390  {
3391  FP_SHAPE* shape = parseFP_SHAPE();
3392 
3393  // Drop 0 and NaN angles as these can corrupt/crash the schematic
3394  if( std::isnormal( shape->GetAngle() ) )
3395  {
3396  shape->SetParent( footprint.get() );
3397  shape->SetDrawCoord();
3398  footprint->Add( shape, ADD_MODE::APPEND );
3399  }
3400  else
3401  {
3402  delete shape;
3403  }
3404 
3405  break;
3406  }
3407 
3408  case T_fp_circle:
3409  case T_fp_curve:
3410  case T_fp_rect:
3411  case T_fp_line:
3412  case T_fp_poly:
3413  {
3414  FP_SHAPE* shape = parseFP_SHAPE();
3415  shape->SetParent( footprint.get() );
3416  shape->SetDrawCoord();
3417  footprint->Add( shape, ADD_MODE::APPEND );
3418  break;
3419  }
3420 
3421  case T_pad:
3422  {
3423  PAD* pad = parsePAD( footprint.get() );
3424  pt = pad->GetPos0();
3425 
3426  RotatePoint( &pt, footprint->GetOrientation() );
3427  pad->SetPosition( pt + footprint->GetPosition() );
3428  footprint->Add( pad, ADD_MODE::APPEND );
3429  break;
3430  }
3431 
3432  case T_model:
3433  {
3434  FP_3DMODEL* model = parse3DModel();
3435  footprint->Add3DModel( model );
3436  delete model;
3437  break;
3438  }
3439 
3440  case T_zone:
3441  {
3442  ZONE* zone = parseZONE( footprint.get() );
3443  footprint->Add( zone, ADD_MODE::APPEND );
3444  break;
3445  }
3446 
3447  case T_group:
3448  parseGROUP( footprint.get() );
3449  break;
3450 
3451  default:
3452  Expecting( "locked, placed, tedit, tstamp, at, descr, tags, path, "
3453  "autoplace_cost90, autoplace_cost180, solder_mask_margin, "
3454  "solder_paste_margin, solder_paste_ratio, clearance, "
3455  "zone_connect, thermal_width, thermal_gap, attr, fp_text, "
3456  "fp_arc, fp_circle, fp_curve, fp_line, fp_poly, fp_rect, pad, "
3457  "zone, group, generator, version or model" );
3458  }
3459  }
3460 
3461  // In legacy files the lack of attributes indicated a through-hole component which was by
3462  // default excluded from pos files. However there was a hack to look for SMD pads and
3463  // consider those "mislabeled through-hole components" and therefore include them in place
3464  // files. We probably don't want to get into that game so we'll just include them by
3465  // default and let the user change it if required.
3466  if( m_requiredVersion < 20200826 && attributes == 0 )
3467  attributes |= FP_THROUGH_HOLE;
3468 
3469  // Legacy files controlled pad locking at the footprint level.
3470  if( m_requiredVersion < 20210108 )
3471  {
3472  for( PAD* pad : footprint->Pads() )
3473  pad->SetLocked( footprint->IsLocked() || footprint->LegacyPadsLocked() );
3474  }
3475 
3476  footprint->SetAttributes( attributes );
3477 
3478  footprint->SetFPID( fpid );
3479  footprint->SetProperties( properties );
3480 
3481  return footprint.release();
3482 }
3483 
3484 
3486 {
3487  wxCHECK_MSG( CurTok() == T_fp_text, nullptr,
3488  wxString::Format( wxT( "Cannot parse %s as FP_TEXT at line %d, offset %d." ),
3489  GetTokenString( CurTok() ), CurLineNumber(), CurOffset() ) );
3490 
3491  T token = NextTok();
3492 
3493  std::unique_ptr<FP_TEXT> text = std::make_unique<FP_TEXT>( nullptr );
3494 
3495  switch( token )
3496  {
3497  case T_reference:
3498  text->SetType( FP_TEXT::TEXT_is_REFERENCE );
3499  break;
3500 
3501  case T_value:
3502  text->SetType( FP_TEXT::TEXT_is_VALUE );
3503  break;
3504 
3505  case T_user:
3506  break; // Default type is user text.
3507 
3508  default:
3510  wxString::Format( _( "Cannot handle footprint text type %s" ), FromUTF8() ) );
3511  }
3512 
3513  NeedSYMBOLorNUMBER();
3514 
3515  wxString value = FromUTF8();
3516  value.Replace( "%V", "${VALUE}" );
3517  value.Replace( "%R", "${REFERENCE}" );
3518  text->SetText( value );
3519  NeedLEFT();
3520  token = NextTok();
3521 
3522  if( token != T_at )
3523  Expecting( T_at );
3524 
3525  wxPoint pt;
3526 
3527  pt.x = parseBoardUnits( "X coordinate" );
3528  pt.y = parseBoardUnits( "Y coordinate" );
3529  text->SetPos0( pt );
3530 
3531  NextTok();
3532 
3533  if( CurTok() == T_NUMBER )
3534  {
3535  text->SetTextAngle( parseDouble() * 10.0 );
3536  NextTok();
3537  }
3538 
3539  if( CurTok() == T_unlocked )
3540  {
3541  text->SetKeepUpright( false );
3542  NextTok();
3543  }
3544 
3545  if( CurTok() != T_RIGHT )
3546  {
3547  Unexpected( CurText() );
3548  }
3549 
3550  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3551  {
3552  if( token == T_LEFT )
3553  token = NextTok();
3554 
3555  switch( token )
3556  {
3557  case T_layer:
3558  text->SetLayer( parseBoardItemLayer() );
3559  NeedRIGHT();
3560  break;
3561 
3562  case T_hide:
3563  text->SetVisible( false );
3564  break;
3565 
3566  case T_effects:
3567  parseEDA_TEXT( (EDA_TEXT*) text.get() );
3568  break;
3569 
3570  case T_tstamp:
3571  NextTok();
3572  const_cast<KIID&>( text->m_Uuid ) = CurStrToKIID();
3573  NeedRIGHT();
3574  break;
3575 
3576  default:
3577  Expecting( "layer, hide, effects or tstamp" );
3578  }
3579  }
3580 
3581  return text.release();
3582 }
3583 
3584 
3586 {
3587  wxCHECK_MSG( CurTok() == T_fp_arc || CurTok() == T_fp_circle || CurTok() == T_fp_curve ||
3588  CurTok() == T_fp_rect || CurTok() == T_fp_line || CurTok() == T_fp_poly, nullptr,
3589  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as FP_SHAPE." ) );
3590 
3591  wxPoint pt;
3592  T token;
3593 
3594  std::unique_ptr<FP_SHAPE> shape = std::make_unique<FP_SHAPE>( nullptr );
3595 
3596  switch( CurTok() )
3597  {
3598  case T_fp_arc:
3599  shape->SetShape( SHAPE_T::ARC );
3600  token = NextTok();
3601 
3602  if( token == T_locked )
3603  {
3604  shape->SetLocked( true );
3605  token = NextTok();
3606  }
3607 
3608  if( token != T_LEFT )
3609  Expecting( T_LEFT );
3610 
3611  token = NextTok();
3612 
3613  // the start keyword actually gives the arc center
3614  // Allows also T_center for future change
3615  if( token != T_start && token != T_center )
3616  Expecting( T_start );
3617 
3618  pt.x = parseBoardUnits( "X coordinate" );
3619  pt.y = parseBoardUnits( "Y coordinate" );
3620  shape->SetStart0( pt );
3621  NeedRIGHT();
3622  NeedLEFT();
3623  token = NextTok();
3624 
3625  if( token != T_end ) // end keyword actually gives the starting point of the arc
3626  Expecting( T_end );
3627 
3628  pt.x = parseBoardUnits( "X coordinate" );
3629  pt.y = parseBoardUnits( "Y coordinate" );
3630  shape->SetEnd0( pt );
3631  NeedRIGHT();
3632  NeedLEFT();
3633  token = NextTok();
3634 
3635  if( token != T_angle )
3636  Expecting( T_angle );
3637 
3638  // Setting angle will set m_thirdPoint0, so must be done after setting
3639  // m_start0 and m_end0
3640  shape->SetAngle( parseDouble( "segment angle" ) * 10.0 );
3641  NeedRIGHT();
3642  break;
3643 
3644  case T_fp_circle:
3645  shape->SetShape( SHAPE_T::CIRCLE );
3646  token = NextTok();
3647 
3648  if( token == T_locked )
3649  {
3650  shape->SetLocked( true );
3651  token = NextTok();
3652  }
3653 
3654  if( token != T_LEFT )
3655  Expecting( T_LEFT );
3656 
3657  token = NextTok();
3658 
3659  if( token != T_center )
3660  Expecting( T_center );
3661 
3662  pt.x = parseBoardUnits( "X coordinate" );
3663  pt.y = parseBoardUnits( "Y coordinate" );
3664  shape->SetStart0( pt );
3665  NeedRIGHT();
3666  NeedLEFT();
3667  token = NextTok();
3668 
3669  if( token != T_end )
3670  Expecting( T_end );
3671 
3672  pt.x = parseBoardUnits( "X coordinate" );
3673  pt.y = parseBoardUnits( "Y coordinate" );
3674  shape->SetEnd0( pt );
3675  NeedRIGHT();
3676  break;
3677 
3678  case T_fp_curve:
3679  shape->SetShape( SHAPE_T::BEZIER );
3680  token = NextTok();
3681 
3682  if( token == T_locked )
3683  {
3684  shape->SetLocked( true );
3685  token = NextTok();
3686  }
3687 
3688  if( token != T_LEFT )
3689  Expecting( T_LEFT );
3690 
3691  token = NextTok();
3692 
3693  if( token != T_pts )
3694  Expecting( T_pts );
3695 
3696  shape->SetStart0( parseXY() );
3697  shape->SetBezierC1_0( parseXY() );
3698  shape->SetBezierC2_0( parseXY() );
3699  shape->SetEnd0( parseXY() );
3700  NeedRIGHT();
3701  break;
3702 
3703  case T_fp_rect:
3704  shape->SetShape( SHAPE_T::RECT );
3705  token = NextTok();
3706 
3707  if( token == T_locked )
3708  {
3709  shape->SetLocked( true );
3710  token = NextTok();
3711  }
3712 
3713  if( token != T_LEFT )
3714  Expecting( T_LEFT );
3715 
3716  token = NextTok();
3717 
3718  if( token != T_start )
3719  Expecting( T_start );
3720 
3721  pt.x = parseBoardUnits( "X coordinate" );
3722  pt.y = parseBoardUnits( "Y coordinate" );
3723  shape->SetStart0( pt );
3724 
3725  NeedRIGHT();
3726  NeedLEFT();
3727  token = NextTok();
3728 
3729  if( token != T_end )
3730  Expecting( T_end );
3731 
3732  pt.x = parseBoardUnits( "X coordinate" );
3733  pt.y = parseBoardUnits( "Y coordinate" );
3734  shape->SetEnd0( pt );
3735  NeedRIGHT();
3736  break;
3737 
3738  case T_fp_line:
3739  // Default PCB_SHAPE type is S_SEGMENT.
3740  token = NextTok();
3741 
3742  if( token == T_locked )
3743  {
3744  shape->SetLocked( true );
3745  token = NextTok();
3746  }
3747 
3748  if( token != T_LEFT )
3749  Expecting( T_LEFT );
3750 
3751  token = NextTok();
3752 
3753  if( token != T_start )
3754  Expecting( T_start );
3755 
3756  pt.x = parseBoardUnits( "X coordinate" );
3757  pt.y = parseBoardUnits( "Y coordinate" );
3758  shape->SetStart0( pt );
3759 
3760  NeedRIGHT();
3761  NeedLEFT();
3762  token = NextTok();
3763 
3764  if( token != T_end )
3765  Expecting( T_end );
3766 
3767  pt.x = parseBoardUnits( "X coordinate" );
3768  pt.y = parseBoardUnits( "Y coordinate" );
3769  shape->SetEnd0( pt );
3770  NeedRIGHT();
3771  break;
3772 
3773  case T_fp_poly:
3774  {
3775  shape->SetShape( SHAPE_T::POLY );
3776  shape->SetPolyPoints( {} );
3777  SHAPE_LINE_CHAIN& outline = shape->GetPolyShape().Outline( 0 );
3778 
3779  token = NextTok();
3780 
3781  if( token == T_locked )
3782  {
3783  shape->SetLocked( true );
3784  token = NextTok();
3785  }
3786 
3787  if( token != T_LEFT )
3788  Expecting( T_LEFT );
3789 
3790  token = NextTok();
3791 
3792  if( token != T_pts )
3793  Expecting( T_pts );
3794 
3795  while( (token = NextTok() ) != T_RIGHT )
3796  parseOutlinePoints( outline );
3797 
3798  break;
3799  }
3800 
3801  default:
3802  Expecting( "fp_arc, fp_circle, fp_curve, fp_line, fp_poly, or fp_rect" );
3803  }
3804 
3805  bool foundFill = false;
3806 
3807  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3808  {
3809  if( token != T_LEFT )
3810  Expecting( T_LEFT );
3811 
3812  token = NextTok();
3813 
3814  switch( token )
3815  {
3816  case T_layer:
3817  shape->SetLayer( parseBoardItemLayer() );
3818  NeedRIGHT();
3819  break;
3820 
3821  case T_width:
3822  shape->SetWidth( parseBoardUnits( T_width ) );
3823  NeedRIGHT();
3824  break;
3825 
3826  case T_tstamp:
3827  NextTok();
3828  const_cast<KIID&>( shape->m_Uuid ) = CurStrToKIID();
3829  NeedRIGHT();
3830  break;
3831 
3832  case T_fill:
3833  foundFill = true;
3834 
3835  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3836  {
3837  if( token == T_LEFT )
3838  token = NextTok();
3839 
3840  switch( token )
3841  {
3842  // T_yes was used to indicate filling when first introduced,
3843  // so treat it like a solid fill since that was the only fill available
3844  case T_yes:
3845  case T_solid:
3846  shape->SetFilled( true );
3847  break;
3848 
3849  case T_none:
3850  shape->SetFilled( false );
3851  break;
3852 
3853  default:
3854  Expecting( "yes, none, solid" );
3855  }
3856  }
3857 
3858  break;
3859 
3860  // We continue to parse the status field but it is no longer written
3861  case T_status:
3862  shape->SetStatus( static_cast<EDA_ITEM_FLAGS>( parseHex() ) );
3863  NeedRIGHT();
3864  break;
3865 
3866  // Continue to process "(locked)" format which was output during 5.99 development
3867  case T_locked:
3868  shape->SetLocked( true );
3869  NeedRIGHT();
3870  break;
3871 
3872  default:
3873  Expecting( "layer, width, fill, tstamp, locked, or status" );
3874  }
3875  }
3876 
3877  if( !foundFill )
3878  {
3879  // Legacy versions didn't have a filled flag but allowed some shapes to indicate they
3880  // should be filled by specifying a 0 stroke-width.
3881  if( shape->GetWidth() == 0
3882  && ( shape->GetShape() == SHAPE_T::RECT || shape->GetShape() == SHAPE_T::CIRCLE ) )
3883  {
3884  shape->SetFilled( true );
3885  }
3886  // Polygons on non-Edge_Cuts layers were always filled
3887  else if( shape->GetShape() == SHAPE_T::POLY && shape->GetLayer() != Edge_Cuts )
3888  {
3889  shape->SetFilled( true );
3890  }
3891  }
3892 
3893  // Only filled shapes may have a zero line-width. This is not permitted in KiCad but some
3894  // external tools can generate invalid files.
3895  if( shape->GetWidth() <= 0 && !shape->IsFilled() )
3896  {
3897  shape->SetWidth( Millimeter2iu( DEFAULT_LINE_WIDTH ) );
3898  }
3899 
3900  return shape.release();
3901 }
3902 
3903 
3905 {
3906  wxCHECK_MSG( CurTok() == T_pad, nullptr,
3907  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as PAD." ) );
3908 
3909  wxSize sz;
3910  wxPoint pt;
3911 
3912  std::unique_ptr<PAD> pad = std::make_unique<PAD>( aParent );
3913 
3914  // File only contains a token if KeepTopBottom is true
3915  pad->SetKeepTopBottom( false );
3916 
3917  NeedSYMBOLorNUMBER();
3918  pad->SetName( FromUTF8() );
3919 
3920  T token = NextTok();
3921 
3922  switch( token )
3923  {
3924  case T_thru_hole:
3925  pad->SetAttribute( PAD_ATTRIB::PTH );
3926  break;
3927 
3928  case T_smd:
3929  pad->SetAttribute( PAD_ATTRIB::SMD );
3930 
3931  // Default PAD object is thru hole with drill.
3932  // SMD pads have no hole
3933  pad->SetDrillSize( wxSize( 0, 0 ) );
3934  break;
3935 
3936  case T_connect:
3937  pad->SetAttribute( PAD_ATTRIB::CONN );
3938 
3939  // Default PAD object is thru hole with drill.
3940  // CONN pads have no hole
3941  pad->SetDrillSize( wxSize( 0, 0 ) );
3942  break;
3943 
3944  case T_np_thru_hole:
3945  pad->SetAttribute( PAD_ATTRIB::NPTH );
3946  break;
3947 
3948  default:
3949  Expecting( "thru_hole, smd, connect, or np_thru_hole" );
3950  }
3951 
3952  token = NextTok();
3953 
3954  switch( token )
3955  {
3956  case T_circle:
3957  pad->SetShape( PAD_SHAPE::CIRCLE );
3958  break;
3959 
3960  case T_rect:
3961  pad->SetShape( PAD_SHAPE::RECT );
3962  break;
3963 
3964  case T_oval:
3965  pad->SetShape( PAD_SHAPE::OVAL );
3966  break;
3967 
3968  case T_trapezoid:
3969  pad->SetShape( PAD_SHAPE::TRAPEZOID );
3970  break;
3971 
3972  case T_roundrect:
3973  // Note: the shape can be PAD_SHAPE::ROUNDRECT or PAD_SHAPE::CHAMFERED_RECT
3974  // (if chamfer parameters are found later in pad descr.)
3975  pad->SetShape( PAD_SHAPE::ROUNDRECT );
3976  break;
3977 
3978  case T_custom:
3979  pad->SetShape( PAD_SHAPE::CUSTOM );
3980  break;
3981 
3982  default:
3983  Expecting( "circle, rectangle, roundrect, oval, trapezoid or custom" );
3984  }
3985 
3986  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
3987  {
3988  if( token == T_locked )
3989  {
3990  pad->SetLocked( true );
3991  token = NextTok();
3992  }
3993 
3994  if( token != T_LEFT )
3995  Expecting( T_LEFT );
3996 
3997  token = NextTok();
3998 
3999  switch( token )
4000  {
4001  case T_size:
4002  sz.SetWidth( parseBoardUnits( "width value" ) );
4003  sz.SetHeight( parseBoardUnits( "height value" ) );
4004  pad->SetSize( sz );
4005  NeedRIGHT();
4006  break;
4007 
4008  case T_at:
4009  pt.x = parseBoardUnits( "X coordinate" );
4010  pt.y = parseBoardUnits( "Y coordinate" );
4011  pad->SetPos0( pt );
4012  token = NextTok();
4013 
4014  if( token == T_NUMBER )
4015  {
4016  pad->SetOrientation( parseDouble() * 10.0 );
4017  NeedRIGHT();
4018  }
4019  else if( token != T_RIGHT )
4020  {
4021  Expecting( ") or angle value" );
4022  }
4023 
4024  break;
4025 
4026  case T_rect_delta:
4027  {
4028  wxSize delta;
4029  delta.SetWidth( parseBoardUnits( "rectangle delta width" ) );
4030  delta.SetHeight( parseBoardUnits( "rectangle delta height" ) );
4031  pad->SetDelta( delta );
4032  NeedRIGHT();
4033  break;
4034  }
4035 
4036  case T_drill:
4037  {
4038  bool haveWidth = false;
4039  wxSize drillSize = pad->GetDrillSize();
4040 
4041  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4042  {
4043  if( token == T_LEFT )
4044  token = NextTok();
4045 
4046  switch( token )
4047  {
4048  case T_oval: pad->SetDrillShape( PAD_DRILL_SHAPE_OBLONG ); break;
4049 
4050  case T_NUMBER:
4051  {
4052  if( !haveWidth )
4053  {
4054  drillSize.SetWidth( parseBoardUnits() );
4055 
4056  // If height is not defined the width and height are the same.
4057  drillSize.SetHeight( drillSize.GetWidth() );
4058  haveWidth = true;
4059  }
4060  else
4061  {
4062  drillSize.SetHeight( parseBoardUnits() );
4063  }
4064  }
4065 
4066  break;
4067 
4068  case T_offset:
4069  pt.x = parseBoardUnits( "drill offset x" );
4070  pt.y = parseBoardUnits( "drill offset y" );
4071  pad->SetOffset( pt );
4072  NeedRIGHT();
4073  break;
4074 
4075  default:
4076  Expecting( "oval, size, or offset" );
4077  }
4078  }
4079 
4080  // This fixes a bug caused by setting the default PAD drill size to a value other
4081  // than 0 used to fix a bunch of debug assertions even though it is defined as a
4082  // through hole pad. Wouldn't a though hole pad with no drill be a surface mount
4083  // pad (or a conn pad which is a smd pad with no solder paste)?
4084  if( ( pad->GetAttribute() != PAD_ATTRIB::SMD )
4085  && ( pad->GetAttribute() != PAD_ATTRIB::CONN ) )
4086  pad->SetDrillSize( drillSize );
4087  else
4088  pad->SetDrillSize( wxSize( 0, 0 ) );
4089 
4090  break;
4091  }
4092 
4093  case T_layers:
4094  {
4095  LSET layerMask = parseBoardItemLayersAsMask();
4096  pad->SetLayerSet( layerMask );
4097  break;
4098  }
4099 
4100  case T_net:
4101  if( ! pad->SetNetCode( getNetCode( parseInt( "net number" ) ), /* aNoAssert */ true ) )
4102  {
4103  wxLogError( _( "Invalid net ID in\nfile: %s\nline: %d offset: %d" ), CurSource(),
4104  CurLineNumber(), CurOffset() );
4105  }
4106 
4107  NeedSYMBOLorNUMBER();
4108 
4109  // Test validity of the netname in file for netcodes expected having a net name
4110  if( m_board && pad->GetNetCode() > 0 )
4111  {
4112  wxString netName( FromUTF8() );
4113 
4114  // Convert overbar syntax from `~...~` to `~{...}`. These were left out of the
4115  // first merge so the version is a bit later.
4116  if( m_requiredVersion < 20210606 )
4117  netName = ConvertToNewOverbarNotation( netName );
4118 
4119  if( netName != m_board->FindNet( pad->GetNetCode() )->GetNetname() )
4120  {
4121  pad->SetNetCode( NETINFO_LIST::ORPHANED, /* aNoAssert */ true );
4122  wxLogError( _( "Net name doesn't match ID in\nfile: %s\nline: %d offset: %d" ),
4123  CurSource(), CurLineNumber(), CurOffset() );
4124  }
4125  }
4126 
4127  NeedRIGHT();
4128  break;
4129 
4130  case T_pinfunction:
4131  NeedSYMBOLorNUMBER();
4132  pad->SetPinFunction( FromUTF8() );
4133  NeedRIGHT();
4134  break;
4135 
4136  case T_pintype:
4137  NeedSYMBOLorNUMBER();
4138  pad->SetPinType( FromUTF8() );
4139  NeedRIGHT();
4140  break;
4141 
4142  case T_die_length:
4143  pad->SetPadToDieLength( parseBoardUnits( T_die_length ) );
4144  NeedRIGHT();
4145  break;
4146 
4147  case T_solder_mask_margin:
4148  pad->SetLocalSolderMaskMargin( parseBoardUnits( T_solder_mask_margin ) );
4149  NeedRIGHT();
4150  break;
4151 
4152  case T_solder_paste_margin:
4153  pad->SetLocalSolderPasteMargin( parseBoardUnits( T_solder_paste_margin ) );
4154  NeedRIGHT();
4155  break;
4156 
4157  case T_solder_paste_margin_ratio:
4158  pad->SetLocalSolderPasteMarginRatio(
4159  parseDouble( "pad local solder paste margin ratio value" ) );
4160  NeedRIGHT();
4161  break;
4162 
4163  case T_clearance:
4164  pad->SetLocalClearance( parseBoardUnits( "local clearance value" ) );
4165  NeedRIGHT();
4166  break;
4167 
4168  case T_zone_connect:
4169  pad->SetZoneConnection( (ZONE_CONNECTION) parseInt( "zone connection value" ) );
4170  NeedRIGHT();
4171  break;
4172 
4173  case T_thermal_width:
4174  pad->SetThermalSpokeWidth( parseBoardUnits( T_thermal_width ) );
4175  NeedRIGHT();
4176  break;
4177 
4178  case T_thermal_gap:
4179  pad->SetThermalGap( parseBoardUnits( T_thermal_gap ) );
4180  NeedRIGHT();
4181  break;
4182 
4183  case T_roundrect_rratio:
4184  pad->SetRoundRectRadiusRatio( parseDouble( "roundrect radius ratio" ) );
4185  NeedRIGHT();
4186  break;
4187 
4188  case T_chamfer_ratio:
4189  pad->SetChamferRectRatio( parseDouble( "chamfer ratio" ) );
4190 
4191  if( pad->GetChamferRectRatio() > 0 )
4192  pad->SetShape( PAD_SHAPE::CHAMFERED_RECT );
4193 
4194  NeedRIGHT();
4195  break;
4196 
4197  case T_chamfer:
4198  {
4199  int chamfers = 0;
4200  bool end_list = false;
4201 
4202  while( !end_list )
4203  {
4204  token = NextTok();
4205 
4206  switch( token )
4207  {
4208  case T_top_left:
4209  chamfers |= RECT_CHAMFER_TOP_LEFT;
4210  break;
4211 
4212  case T_top_right:
4213  chamfers |= RECT_CHAMFER_TOP_RIGHT;
4214  break;
4215 
4216  case T_bottom_left:
4217  chamfers |= RECT_CHAMFER_BOTTOM_LEFT;
4218  break;
4219 
4220  case T_bottom_right:
4221  chamfers |= RECT_CHAMFER_BOTTOM_RIGHT;
4222  break;
4223 
4224  case T_RIGHT:
4225  pad->SetChamferPositions( chamfers );
4226  end_list = true;
4227  break;
4228 
4229  default:
4230  Expecting( "chamfer_top_left chamfer_top_right chamfer_bottom_left or "
4231  "chamfer_bottom_right" );
4232  }
4233  }
4234 
4235  if( pad->GetChamferPositions() != RECT_NO_CHAMFER )
4236  pad->SetShape( PAD_SHAPE::CHAMFERED_RECT );
4237 
4238  break;
4239  }
4240 
4241  case T_property:
4242  {
4243  while( token != T_RIGHT )
4244  {
4245  token = NextTok();
4246 
4247  switch( token )
4248  {
4249  case T_pad_prop_bga:
4250  pad->SetProperty( PAD_PROP::BGA );
4251  break;
4252 
4253  case T_pad_prop_fiducial_glob:
4254  pad->SetProperty( PAD_PROP::FIDUCIAL_GLBL );
4255  break;
4256 
4257  case T_pad_prop_fiducial_loc:
4258  pad->SetProperty( PAD_PROP::FIDUCIAL_LOCAL );
4259  break;
4260 
4261  case T_pad_prop_testpoint:
4262  pad->SetProperty( PAD_PROP::TESTPOINT );
4263  break;
4264 
4265  case T_pad_prop_castellated:
4266  pad->SetProperty( PAD_PROP::CASTELLATED );
4267  break;
4268 
4269  case T_pad_prop_heatsink:
4270  pad->SetProperty( PAD_PROP::HEATSINK );
4271  break;
4272 
4273  case T_none:
4274  pad->SetProperty( PAD_PROP::NONE );
4275  break;
4276 
4277  case T_RIGHT:
4278  break;
4279 
4280  default:
4281 #if 0 // Currently: skip unknown property
4282  Expecting( "pad_prop_bga pad_prop_fiducial_glob pad_prop_fiducial_loc"
4283  " pad_prop_heatsink or pad_prop_castellated" );
4284 #endif
4285  break;
4286  }
4287  }
4288 
4289  break;
4290  }
4291 
4292  case T_options:
4293  parsePAD_option( pad.get() );
4294  break;
4295 
4296  case T_primitives:
4297  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4298  {
4299  if( token == T_LEFT )
4300  token = NextTok();
4301 
4302  // Currently, I am using parsePCB_SHAPE() to read basic shapes parameters,
4303  // because they are the same as a PCB_SHAPE.
4304  // However it could be better to write a specific parser, to avoid possible issues
4305  // if the PCB_SHAPE parser is modified.
4306  PCB_SHAPE* dummyShape = nullptr;
4307 
4308  switch( token )
4309  {
4310  case T_gr_arc:
4311  dummyShape = parsePCB_SHAPE();
4312  pad->AddPrimitiveArc( dummyShape->GetCenter(), dummyShape->GetArcStart(),
4313  dummyShape->GetAngle(), dummyShape->GetWidth() );
4314  break;
4315 
4316  case T_gr_line:
4317  dummyShape = parsePCB_SHAPE();
4318  pad->AddPrimitiveSegment( dummyShape->GetStart(), dummyShape->GetEnd(),
4319  dummyShape->GetWidth() );
4320  break;
4321 
4322  case T_gr_circle:
4323  dummyShape = parsePCB_SHAPE();
4324  pad->AddPrimitiveCircle( dummyShape->GetCenter(), dummyShape->GetRadius(),
4325  dummyShape->GetWidth(), dummyShape->IsFilled() );
4326  break;
4327 
4328  case T_gr_rect:
4329  dummyShape = parsePCB_SHAPE();
4330  pad->AddPrimitiveRect( dummyShape->GetStart(), dummyShape->GetEnd(),
4331  dummyShape->GetWidth(), dummyShape->IsFilled() );
4332  break;
4333 
4334 
4335  case T_gr_poly:
4336  dummyShape = parsePCB_SHAPE();
4337  pad->AddPrimitivePoly( dummyShape->BuildPolyPointsList(), dummyShape->GetWidth(),
4338  dummyShape->IsFilled() );
4339  break;
4340 
4341  case T_gr_curve:
4342  dummyShape = parsePCB_SHAPE();
4343  pad->AddPrimitiveCurve( dummyShape->GetStart(), dummyShape->GetEnd(),
4344  dummyShape->GetBezierC1(),
4345  dummyShape->GetBezierC2(), dummyShape->GetWidth() );
4346  break;
4347 
4348  default:
4349  Expecting( "gr_line, gr_arc, gr_circle, gr_curve, gr_rect or gr_poly" );
4350  break;
4351  }
4352 
4353  delete dummyShape;
4354  }
4355 
4356  break;
4357 
4358  case T_remove_unused_layers:
4359  pad->SetRemoveUnconnected( true );
4360  NeedRIGHT();
4361  break;
4362 
4363  case T_keep_end_layers:
4364  pad->SetKeepTopBottom( true );
4365  NeedRIGHT();
4366  break;
4367 
4368  // Continue to process "(locked)" format which was output during 5.99 development
4369  case T_locked:
4370  pad->SetLocked( true );
4371  NeedRIGHT();
4372  break;
4373 
4374  case T_tstamp:
4375  NextTok();
4376  const_cast<KIID&>( pad->m_Uuid ) = CurStrToKIID();
4377  NeedRIGHT();
4378  break;
4379 
4380  default:
4381  Expecting( "at, locked, drill, layers, net, die_length, roundrect_rratio, "
4382  "solder_mask_margin, solder_paste_margin, solder_paste_margin_ratio, "
4383  "clearance, tstamp, primitives, remove_unused_layers, keep_end_layers, "
4384  "pinfunction, pintype, zone_connect, thermal_width, or thermal_gap" );
4385  }
4386  }
4387 
4388  return pad.release();
4389 }
4390 
4391 
4393 {
4394  // Parse only the (option ...) inside a pad description
4395  for( T token = NextTok(); token != T_RIGHT; token = NextTok() )
4396  {
4397  if( token != T_LEFT )
4398  Expecting( T_LEFT );
4399 
4400  token = NextTok();
4401 
4402  switch( token )
4403  {
4404  case T_anchor:
4405  token = NextTok();
4406  // Custom shaped pads have a "anchor pad", which is the reference
4407  // for connection calculations.
4408  // Because this is an anchor, only the 2 very basic shapes are managed:
4409  // circle and rect. The default is circle
4410  switch( token )
4411  {
4412  case T_circle: // default
4413  break;
4414 
4415  case T_rect:
4417  break;
4418 
4419  default:
4420  // Currently, because pad options is a moving target
4421  // just skip unknown keywords
4422  break;
4423  }
4424  NeedRIGHT();
4425  break;
4426 
4427  case T_clearance:
4428  token = NextTok();
4429  // Custom shaped pads have a clearance area that is the pad shape
4430  // (like usual pads) or the convex hull of the pad shape.
4431  switch( token )
4432  {
4433  case T_outline:
4435  break;
4436 
4437  case T_convexhull:
4439  break;
4440 
4441  default:
4442  // Currently, because pad options is a moving target
4443  // just skip unknown keywords
4444  break;
4445  }
4446 
4447  NeedRIGHT();
4448  break;
4449 
4450  default:
4451  // Currently, because pad options is a moving target
4452  // just skip unknown keywords
4453  while( (token = NextTok() ) != T_RIGHT )
4454  {}
4455 
4456  break;
4457  }
4458  }
4459 
4460  return true;
4461 }
4462 
4463 
4465 {
4466  wxCHECK_RET( CurTok() == T_group,
4467  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as PCB_GROUP." ) );
4468 
4469  wxPoint pt;
4470  T token;
4471 
4472  m_groupInfos.push_back( GROUP_INFO() );
4473  GROUP_INFO& groupInfo = m_groupInfos.back();
4474  groupInfo.parent = aParent;
4475 
4476  while( ( token = NextTok() ) != T_LEFT )
4477  {
4478  if( token == T_STRING )
4479  groupInfo.name = FromUTF8();
4480  else if( token == T_locked )
4481  groupInfo.locked = true;
4482  else
4483  Expecting( "group name or locked" );
4484  }
4485 
4486  token = NextTok();
4487 
4488  if( token != T_id )
4489  Expecting( T_id );
4490 
4491  NextTok();
4492  groupInfo.uuid = CurStrToKIID();
4493  NeedRIGHT();
4494 
4495  NeedLEFT();
4496  token = NextTok();
4497 
4498  if( token != T_members )
4499  Expecting( T_members );
4500 
4501  while( ( token = NextTok() ) != T_RIGHT )
4502  {
4503  // This token is the Uuid of the item in the group.
4504  // Since groups are serialized at the end of the file/footprint, the Uuid should already
4505  // have been seen and exist in the board.
4506  KIID uuid( CurStr() );
4507  groupInfo.memberUuids.push_back( uuid );
4508  }
4509 
4510  NeedRIGHT();
4511 }
4512 
4513 
4515 {
4516  wxCHECK_MSG( CurTok() == T_arc, nullptr,
4517  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as ARC." ) );
4518 
4519  wxPoint pt;
4520  T token;
4521 
4522  std::unique_ptr<PCB_ARC> arc = std::make_unique<PCB_ARC>( m_board );
4523 
4524  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4525  {
4526  if( token == T_locked )
4527  {
4528  arc->SetLocked( true );
4529  token = NextTok();
4530  }
4531 
4532  if( token != T_LEFT )
4533  Expecting( T_LEFT );
4534 
4535  token = NextTok();
4536 
4537  switch( token )
4538  {
4539  case T_start:
4540  pt.x = parseBoardUnits( "start x" );
4541  pt.y = parseBoardUnits( "start y" );
4542  arc->SetStart( pt );
4543  break;
4544 
4545  case T_mid:
4546  pt.x = parseBoardUnits( "mid x" );
4547  pt.y = parseBoardUnits( "mid y" );
4548  arc->SetMid( pt );
4549  break;
4550 
4551  case T_end:
4552  pt.x = parseBoardUnits( "end x" );
4553  pt.y = parseBoardUnits( "end y" );
4554  arc->SetEnd( pt );
4555  break;
4556 
4557  case T_width:
4558  arc->SetWidth( parseBoardUnits( "width" ) );
4559  break;
4560 
4561  case T_layer:
4562  arc->SetLayer( parseBoardItemLayer() );
4563  break;
4564 
4565  case T_net:
4566  if( !arc->SetNetCode( getNetCode( parseInt( "net number" ) ), /* aNoAssert */ true ) )
4568  _( "Invalid net ID in\nfile: '%s'\nline: %d\noffset: %d." ), CurSource(),
4569  CurLineNumber(), CurOffset() ) );
4570  break;
4571 
4572  case T_tstamp:
4573  NextTok();
4574  const_cast<KIID&>( arc->m_Uuid ) = CurStrToKIID();
4575  break;
4576 
4577  // We continue to parse the status field but it is no longer written
4578  case T_status:
4579  arc->SetStatus( static_cast<EDA_ITEM_FLAGS>( parseHex() ) );
4580  break;
4581 
4582  // Continue to process "(locked)" format which was output during 5.99 development
4583  case T_locked:
4584  arc->SetLocked( true );
4585  break;
4586 
4587  default:
4588  Expecting( "start, mid, end, width, layer, net, tstamp, or status" );
4589  }
4590 
4591  NeedRIGHT();
4592  }
4593 
4594  return arc.release();
4595 }
4596 
4597 
4599 {
4600  wxCHECK_MSG( CurTok() == T_segment, nullptr,
4601  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as PCB_TRACK." ) );
4602 
4603  wxPoint pt;
4604  T token;
4605 
4606  std::unique_ptr<PCB_TRACK> track = std::make_unique<PCB_TRACK>( m_board );
4607 
4608  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4609  {
4610  if( token == T_locked )
4611  {
4612  track->SetLocked( true );
4613  token = NextTok();
4614  }
4615 
4616  if( token != T_LEFT )
4617  Expecting( T_LEFT );
4618 
4619  token = NextTok();
4620 
4621  switch( token )
4622  {
4623  case T_start:
4624  pt.x = parseBoardUnits( "start x" );
4625  pt.y = parseBoardUnits( "start y" );
4626  track->SetStart( pt );
4627  break;
4628 
4629  case T_end:
4630  pt.x = parseBoardUnits( "end x" );
4631  pt.y = parseBoardUnits( "end y" );
4632  track->SetEnd( pt );
4633  break;
4634 
4635  case T_width:
4636  track->SetWidth( parseBoardUnits( "width" ) );
4637  break;
4638 
4639  case T_layer:
4640  track->SetLayer( parseBoardItemLayer() );
4641  break;
4642 
4643  case T_net:
4644  if( !track->SetNetCode( getNetCode( parseInt( "net number" ) ), /* aNoAssert */ true ) )
4646  _( "Invalid net ID in\nfile: '%s'\nline: %d\noffset: %d." ), CurSource(),
4647  CurLineNumber(), CurOffset() ) );
4648  break;
4649 
4650  case T_tstamp:
4651  NextTok();
4652  const_cast<KIID&>( track->m_Uuid ) = CurStrToKIID();
4653  break;
4654 
4655  // We continue to parse the status field but it is no longer written
4656  case T_status:
4657  track->SetStatus( static_cast<EDA_ITEM_FLAGS>( parseHex() ) );
4658  break;
4659 
4660  // Continue to process "(locked)" format which was output during 5.99 development
4661  case T_locked:
4662  track->SetLocked( true );
4663  break;
4664 
4665  default:
4666  Expecting( "start, end, width, layer, net, tstamp, or locked" );
4667  }
4668 
4669  NeedRIGHT();
4670  }
4671 
4672  return track.release();
4673 }
4674 
4675 
4677 {
4678  wxCHECK_MSG( CurTok() == T_via, nullptr,
4679  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as PCB_VIA." ) );
4680 
4681  wxPoint pt;
4682  T token;
4683 
4684  std::unique_ptr<PCB_VIA> via = std::make_unique<PCB_VIA>( m_board );
4685 
4686  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4687  {
4688  if( token == T_locked )
4689  {
4690  via->SetLocked( true );
4691  token = NextTok();
4692  }
4693 
4694  if( token == T_LEFT )
4695  token = NextTok();
4696 
4697  switch( token )
4698  {
4699  case T_blind:
4700  via->SetViaType( VIATYPE::BLIND_BURIED );
4701  break;
4702 
4703  case T_micro:
4704  via->SetViaType( VIATYPE::MICROVIA );
4705  break;
4706 
4707  case T_at:
4708  pt.x = parseBoardUnits( "start x" );
4709  pt.y = parseBoardUnits( "start y" );
4710  via->SetStart( pt );
4711  via->SetEnd( pt );
4712  NeedRIGHT();
4713  break;
4714 
4715  case T_size:
4716  via->SetWidth( parseBoardUnits( "via width" ) );
4717  NeedRIGHT();
4718  break;
4719 
4720  case T_drill:
4721  via->SetDrill( parseBoardUnits( "drill diameter" ) );
4722  NeedRIGHT();
4723  break;
4724 
4725  case T_layers:
4726  {
4727  PCB_LAYER_ID layer1, layer2;
4728  NextTok();
4729  layer1 = lookUpLayer<PCB_LAYER_ID>( m_layerIndices );
4730  NextTok();
4731  layer2 = lookUpLayer<PCB_LAYER_ID>( m_layerIndices );
4732  via->SetLayerPair( layer1, layer2 );
4733  NeedRIGHT();
4734  break;
4735  }
4736 
4737  case T_net:
4738  if( !via->SetNetCode( getNetCode( parseInt( "net number" ) ), /* aNoAssert */ true ) )
4739  {
4740  THROW_IO_ERROR( wxString::Format( _( "Invalid net ID in\n"
4741  "file: '%s'\n"
4742  "line: %d\n"
4743  "offset: %d" ),
4744  CurSource(),
4745  CurLineNumber(),
4746  CurOffset() ) );
4747  }
4748 
4749  NeedRIGHT();
4750  break;
4751 
4752  case T_remove_unused_layers:
4753  via->SetRemoveUnconnected( true );
4754  NeedRIGHT();
4755  break;
4756 
4757  case T_keep_end_layers:
4758  via->SetKeepTopBottom( true );
4759  NeedRIGHT();
4760  break;
4761 
4762  case T_tstamp:
4763  NextTok();
4764  const_cast<KIID&>( via->m_Uuid ) = CurStrToKIID();
4765  NeedRIGHT();
4766  break;
4767 
4768  // We continue to parse the status field but it is no longer written
4769  case T_status:
4770  via->SetStatus( static_cast<EDA_ITEM_FLAGS>( parseHex() ) );
4771  NeedRIGHT();
4772  break;
4773 
4774  // Continue to process "(locked)" format which was output during 5.99 development
4775  case T_locked:
4776  via->SetLocked( true );
4777  NeedRIGHT();
4778  break;
4779 
4780  case T_free:
4781  via->SetIsFree();
4782  NeedRIGHT();
4783  break;
4784 
4785  default:
4786  Expecting( "blind, micro, at, size, drill, layers, net, free, tstamp, or status" );
4787  }
4788  }
4789 
4790  return via.release();
4791 }
4792 
4793 
4795 {
4796  wxCHECK_MSG( CurTok() == T_zone, nullptr,
4797  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) +
4798  wxT( " as ZONE." ) );
4799 
4801 
4802  int hatchPitch = ZONE::GetDefaultHatchPitch();
4803  wxPoint pt;
4804  T token;
4805  int tmp;
4806  wxString netnameFromfile; // the zone net name find in file
4807 
4808  // bigger scope since each filled_polygon is concatenated in here
4809  std::map<PCB_LAYER_ID, SHAPE_POLY_SET> pts;
4810  bool inFootprint = false;
4811  PCB_LAYER_ID filledLayer;
4812  bool addedFilledPolygons = false;
4813 
4814  if( dynamic_cast<FOOTPRINT*>( aParent ) ) // The zone belongs a footprint
4815  inFootprint = true;
4816 
4817  std::unique_ptr<ZONE> zone;
4818 
4819  if( inFootprint )
4820  zone = std::make_unique<FP_ZONE>( aParent );
4821  else
4822  zone = std::make_unique<ZONE>( aParent );
4823 
4824  zone->SetPriority( 0 );
4825 
4826  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4827  {
4828  if( token == T_locked )
4829  {
4830  zone->SetLocked( true );
4831  token = NextTok();
4832  }
4833 
4834  if( token == T_LEFT )
4835  token = NextTok();
4836 
4837  switch( token )
4838  {
4839  case T_net:
4840  // Init the net code only, not the netname, to be sure
4841  // the zone net name is the name read in file.
4842  // (When mismatch, the user will be prompted in DRC, to fix the actual name)
4843  tmp = getNetCode( parseInt( "net number" ) );
4844 
4845  if( tmp < 0 )
4846  tmp = 0;
4847 
4848  if( !zone->SetNetCode( tmp, /* aNoAssert */ true ) )
4850  _( "Invalid net ID in\n file: '%s;\nline: %d\noffset: %d." ),
4851  CurSource(),
4852  CurLineNumber(),
4853  CurOffset() ) );
4854 
4855  NeedRIGHT();
4856  break;
4857 
4858  case T_net_name:
4859  NeedSYMBOLorNUMBER();
4860  netnameFromfile = FromUTF8();
4861  NeedRIGHT();
4862  break;
4863 
4864  case T_layer: // keyword for zones that are on only one layer
4865  zone->SetLayer( parseBoardItemLayer() );
4866  NeedRIGHT();
4867  break;
4868 
4869  case T_layers: // keyword for zones that can live on a set of layers
4870  zone->SetLayerSet( parseBoardItemLayersAsMask() );
4871  break;
4872 
4873  case T_tstamp:
4874  NextTok();
4875  const_cast<KIID&>( zone->m_Uuid ) = CurStrToKIID();
4876  NeedRIGHT();
4877  break;
4878 
4879  case T_hatch:
4880  token = NextTok();
4881 
4882  if( token != T_none && token != T_edge && token != T_full )
4883  Expecting( "none, edge, or full" );
4884 
4885  switch( token )
4886  {
4887  default:
4888  case T_none: hatchStyle = ZONE_BORDER_DISPLAY_STYLE::NO_HATCH; break;
4889  case T_edge: hatchStyle = ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE; break;
4890  case T_full: hatchStyle = ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_FULL; break;
4891  }
4892 
4893  hatchPitch = parseBoardUnits( "hatch pitch" );
4894  NeedRIGHT();
4895  break;
4896 
4897  case T_priority:
4898  zone->SetPriority( parseInt( "zone priority" ) );
4899  NeedRIGHT();
4900  break;
4901 
4902  case T_connect_pads:
4903  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4904  {
4905  if( token == T_LEFT )
4906  token = NextTok();
4907 
4908  switch( token )
4909  {
4910  case T_yes:
4911  zone->SetPadConnection( ZONE_CONNECTION::FULL );
4912  break;
4913 
4914  case T_no:
4915  zone->SetPadConnection( ZONE_CONNECTION::NONE );
4916  break;
4917 
4918  case T_thru_hole_only:
4919  zone->SetPadConnection( ZONE_CONNECTION::THT_THERMAL );
4920  break;
4921 
4922  case T_clearance:
4923  zone->SetLocalClearance( parseBoardUnits( "zone clearance" ) );
4924  NeedRIGHT();
4925  break;
4926 
4927  default:
4928  Expecting( "yes, no, or clearance" );
4929  }
4930  }
4931 
4932  break;
4933 
4934  case T_min_thickness:
4935  zone->SetMinThickness( parseBoardUnits( T_min_thickness ) );
4936  NeedRIGHT();
4937  break;
4938 
4939  case T_filled_areas_thickness:
4940  zone->SetFillVersion( parseBool() ? 5 : 6 );
4941  NeedRIGHT();
4942  break;
4943 
4944  case T_fill:
4945  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
4946  {
4947  if( token == T_LEFT )
4948  token = NextTok();
4949 
4950  switch( token )
4951  {
4952  case T_yes:
4953  zone->SetIsFilled( true );
4954  break;
4955 
4956  case T_mode:
4957  token = NextTok();
4958 
4959  if( token != T_segment && token != T_hatch && token != T_polygon )
4960  Expecting( "segment, hatch or polygon" );
4961 
4962  if( token == T_segment ) // deprecated
4963  {
4964  // SEGMENT fill mode no longer supported. Make sure user is OK with
4965  // converting them.
4966  if( m_showLegacyZoneWarning )
4967  {
4968  KIDIALOG dlg( nullptr,
4969  _( "The legacy segment fill mode is no longer supported."
4970  "\nConvert zones to polygon fills?"),
4971  _( "Legacy Zone Warning" ),
4972  wxYES_NO | wxICON_WARNING );
4973 
4974  dlg.DoNotShowCheckbox( __FILE__, __LINE__ );
4975 
4976  if( dlg.ShowModal() == wxID_NO )
4977  THROW_IO_ERROR( wxT( "CANCEL" ) );
4978 
4979  m_showLegacyZoneWarning = false;
4980  }
4981 
4982  zone->SetFillMode( ZONE_FILL_MODE::POLYGONS );
4983  m_board->SetModified();
4984  }
4985  else if( token == T_hatch )
4986  {
4987  zone->SetFillMode( ZONE_FILL_MODE::HATCH_PATTERN );
4988  }
4989  else
4990  {
4991  zone->SetFillMode( ZONE_FILL_MODE::POLYGONS );
4992  }
4993 
4994  NeedRIGHT();
4995  break;
4996 
4997  case T_hatch_thickness:
4998  zone->SetHatchThickness( parseBoardUnits( T_hatch_thickness ) );
4999  NeedRIGHT();
5000  break;
5001 
5002  case T_hatch_gap:
5003  zone->SetHatchGap( parseBoardUnits( T_hatch_gap ) );
5004  NeedRIGHT();
5005  break;
5006 
5007  case T_hatch_orientation:
5008  zone->SetHatchOrientation( parseDouble( T_hatch_orientation ) );
5009  NeedRIGHT();
5010  break;
5011 
5012  case T_hatch_smoothing_level:
5013  zone->SetHatchSmoothingLevel( parseDouble( T_hatch_smoothing_level ) );
5014  NeedRIGHT();
5015  break;
5016 
5017  case T_hatch_smoothing_value:
5018  zone->SetHatchSmoothingValue( parseDouble( T_hatch_smoothing_value ) );
5019  NeedRIGHT();
5020  break;
5021 
5022  case T_hatch_border_algorithm:
5023  token = NextTok();
5024 
5025  if( token != T_hatch_thickness && token != T_min_thickness )
5026  Expecting( "hatch_thickness or min_thickness" );
5027 
5028  zone->SetHatchBorderAlgorithm( token == T_hatch_thickness ? 1 : 0 );
5029  NeedRIGHT();
5030  break;
5031 
5032  case T_hatch_min_hole_area:
5033  zone->SetHatchHoleMinArea( parseDouble( T_hatch_min_hole_area ) );
5034  NeedRIGHT();
5035  break;
5036 
5037  case T_arc_segments:
5038  static_cast<void>( parseInt( "arc segment count" ) );
5039  NeedRIGHT();
5040  break;
5041 
5042  case T_thermal_gap:
5043  zone->SetThermalReliefGap( parseBoardUnits( T_thermal_gap ) );
5044  NeedRIGHT();
5045  break;
5046 
5047  case T_thermal_bridge_width:
5048  zone->SetThermalReliefSpokeWidth( parseBoardUnits( T_thermal_bridge_width ) );
5049  NeedRIGHT();
5050  break;
5051 
5052  case T_smoothing:
5053  switch( NextTok() )
5054  {
5055  case T_none:
5056  zone->SetCornerSmoothingType( ZONE_SETTINGS::SMOOTHING_NONE );
5057  break;
5058 
5059  case T_chamfer:
5060  if( !zone->GetIsRuleArea() ) // smoothing has meaning only for filled zones
5061  zone->SetCornerSmoothingType( ZONE_SETTINGS::SMOOTHING_CHAMFER );
5062 
5063  break;
5064 
5065  case T_fillet:
5066  if( !zone->GetIsRuleArea() ) // smoothing has meaning only for filled zones
5067  zone->SetCornerSmoothingType( ZONE_SETTINGS::SMOOTHING_FILLET );
5068 
5069  break;
5070 
5071  default:
5072  Expecting( "none, chamfer, or fillet" );
5073  }
5074 
5075  NeedRIGHT();
5076  break;
5077 
5078  case T_radius:
5079  tmp = parseBoardUnits( "corner radius" );
5080  if( !zone->GetIsRuleArea() ) // smoothing has meaning only for filled zones
5081  zone->SetCornerRadius( tmp );
5082  NeedRIGHT();
5083  break;
5084 
5085  case T_island_removal_mode:
5086  tmp = parseInt( "island_removal_mode" );
5087 
5088  if( tmp >= 0 && tmp <= 2 )
5089  zone->SetIslandRemovalMode( static_cast<ISLAND_REMOVAL_MODE>( tmp ) );
5090 
5091  NeedRIGHT();
5092  break;
5093 
5094  case T_island_area_min:
5095  {
5096  int area = parseBoardUnits( T_island_area_min );
5097  zone->SetMinIslandArea( area * IU_PER_MM );
5098  NeedRIGHT();
5099  break;
5100  }
5101 
5102  default:
5103  Expecting( "mode, arc_segments, thermal_gap, thermal_bridge_width, "
5104  "hatch_thickness, hatch_gap, hatch_orientation, "
5105  "hatch_smoothing_level, hatch_smoothing_value, "
5106  "hatch_border_algorithm, hatch_min_hole_area, smoothing, radius, "
5107  "island_removal_mode, or island_area_min" );
5108  }
5109  }
5110 
5111  break;
5112 
5113  case T_keepout:
5114  // "keepout" now means rule area, but the file token stays the same
5115  zone->SetIsRuleArea( true );
5116 
5117  // Initialize these two because their tokens won't appear in older files:
5118  zone->SetDoNotAllowPads( false );
5119  zone->SetDoNotAllowFootprints( false );
5120 
5121  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5122  {
5123  if( token == T_LEFT )
5124  token = NextTok();
5125 
5126  switch( token )
5127  {
5128  case T_tracks:
5129  token = NextTok();
5130 
5131  if( token != T_allowed && token != T_not_allowed )
5132  Expecting( "allowed or not_allowed" );
5133  zone->SetDoNotAllowTracks( token == T_not_allowed );
5134  break;
5135 
5136  case T_vias:
5137  token = NextTok();
5138 
5139  if( token != T_allowed && token != T_not_allowed )
5140  Expecting( "allowed or not_allowed" );
5141  zone->SetDoNotAllowVias( token == T_not_allowed );
5142  break;
5143 
5144  case T_copperpour:
5145  token = NextTok();
5146 
5147  if( token != T_allowed && token != T_not_allowed )
5148  Expecting( "allowed or not_allowed" );
5149  zone->SetDoNotAllowCopperPour( token == T_not_allowed );
5150  break;
5151 
5152  case T_pads:
5153  token = NextTok();
5154 
5155  if( token != T_allowed && token != T_not_allowed )
5156  Expecting( "allowed or not_allowed" );
5157  zone->SetDoNotAllowPads( token == T_not_allowed );
5158  break;
5159 
5160  case T_footprints:
5161  token = NextTok();
5162 
5163  if( token != T_allowed && token != T_not_allowed )
5164  Expecting( "allowed or not_allowed" );
5165  zone->SetDoNotAllowFootprints( token == T_not_allowed );
5166  break;
5167 
5168  default:
5169  Expecting( "tracks, vias or copperpour" );
5170  }
5171 
5172  NeedRIGHT();
5173  }
5174 
5175  break;
5176 
5177  case T_polygon:
5178  {
5179  SHAPE_LINE_CHAIN outline;
5180 
5181  NeedLEFT();
5182  token = NextTok();
5183 
5184  if( token != T_pts )
5185  Expecting( T_pts );
5186 
5187  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5188  parseOutlinePoints( outline );
5189 
5190  NeedRIGHT();
5191 
5192  outline.SetClosed( true );
5193 
5194  // Remark: The first polygon is the main outline.
5195  // Others are holes inside the main outline.
5196  zone->AddPolygon( outline );
5197  break;
5198  }
5199 
5200  case T_filled_polygon:
5201  {
5202  // "(filled_polygon (pts"
5203  NeedLEFT();
5204  token = NextTok();
5205 
5206  if( token == T_layer )
5207  {
5208  filledLayer = parseBoardItemLayer();
5209  NeedRIGHT();
5210  token = NextTok();
5211 
5212  if( token != T_LEFT )
5213  Expecting( T_LEFT );
5214 
5215  token = NextTok();
5216  }
5217  else
5218  {
5219  filledLayer = zone->GetLayer();
5220  }
5221 
5222  bool island = false;
5223 
5224  if( token == T_island )
5225  {
5226  island = true;
5227  NeedRIGHT();
5228  NeedLEFT();
5229  token = NextTok();
5230  }
5231 
5232  if( token != T_pts )
5233  Expecting( T_pts );
5234 
5235  if( !pts.count( filledLayer ) )
5236  pts[filledLayer] = SHAPE_POLY_SET();
5237 
5238  SHAPE_POLY_SET& poly = pts.at( filledLayer );
5239 
5240  int idx = poly.NewOutline();
5241  SHAPE_LINE_CHAIN& chain = poly.Outline( idx );
5242 
5243  if( island )
5244  zone->SetIsIsland( filledLayer, idx );
5245 
5246  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5247  parseOutlinePoints( chain );
5248 
5249  NeedRIGHT();
5250 
5251  addedFilledPolygons |= !poly.IsEmpty();
5252  }
5253 
5254  break;
5255 
5256  case T_fill_segments:
5257  {
5258  ZONE_SEGMENT_FILL segs;
5259 
5260  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5261  {
5262  if( token != T_LEFT )
5263  Expecting( T_LEFT );
5264 
5265  token = NextTok();
5266 
5267  if( token == T_layer )
5268  {
5269  filledLayer = parseBoardItemLayer();
5270  NeedRIGHT();
5271  token = NextTok();
5272 
5273  if( token != T_LEFT )
5274  Expecting( T_LEFT );
5275 
5276  token = NextTok();
5277  }
5278  else
5279  {
5280  filledLayer = zone->GetLayer();
5281  }
5282 
5283  if( token != T_pts )
5284  Expecting( T_pts );
5285 
5286  SEG segment( parseXY(), parseXY() );
5287  NeedRIGHT();
5288  segs.push_back( segment );
5289  }
5290 
5291  zone->SetFillSegments( filledLayer, segs );
5292  break;
5293  }
5294 
5295  case T_name:
5296  NextTok();
5297  zone->SetZoneName( FromUTF8() );
5298  NeedRIGHT();
5299  break;
5300 
5301  default:
5302  Expecting( "net, layer/layers, tstamp, hatch, priority, connect_pads, min_thickness, "
5303  "fill, polygon, filled_polygon, fill_segments, or name" );
5304  }
5305  }
5306 
5307  if( zone->GetNumCorners() > 2 )
5308  {
5309  if( !zone->IsOnCopperLayer() )
5310  {
5311  //zone->SetFillMode( ZONE_FILL_MODE::POLYGONS );
5312  zone->SetNetCode( NETINFO_LIST::UNCONNECTED );
5313  }
5314 
5315  // Set hatch here, after outlines corners are read
5316  zone->SetBorderDisplayStyle( hatchStyle, hatchPitch, true );
5317  }
5318 
5319  if( addedFilledPolygons )
5320  {
5321  for( auto& pair : pts )
5322  zone->SetFilledPolysList( pair.first, pair.second );
5323 
5324  zone->CalculateFilledArea();
5325  }
5326 
5327  // Ensure keepout and non copper zones do not have a net
5328  // (which have no sense for these zones)
5329  // the netcode 0 is used for these zones
5330  bool zone_has_net = zone->IsOnCopperLayer() && !zone->GetIsRuleArea();
5331 
5332  if( !zone_has_net )
5333  zone->SetNetCode( NETINFO_LIST::UNCONNECTED );
5334 
5335  // Ensure the zone net name is valid, and matches the net code, for copper zones
5336  if( zone_has_net && ( zone->GetNet()->GetNetname() != netnameFromfile ) )
5337  {
5338  // Can happens which old boards, with nonexistent nets ...
5339  // or after being edited by hand
5340  // We try to fix the mismatch.
5341  NETINFO_ITEM* net = m_board->FindNet( netnameFromfile );
5342 
5343  if( net ) // An existing net has the same net name. use it for the zone
5344  {
5345  zone->SetNetCode( net->GetNetCode() );
5346  }
5347  else // Not existing net: add a new net to keep trace of the zone netname
5348  {
5349  int newnetcode = m_board->GetNetCount();
5350  net = new NETINFO_ITEM( m_board, netnameFromfile, newnetcode );
5351  m_board->Add( net );
5352 
5353  // Store the new code mapping
5354  pushValueIntoMap( newnetcode, net->GetNetCode() );
5355 
5356  // and update the zone netcode
5357  zone->SetNetCode( net->GetNetCode() );
5358  }
5359  }
5360 
5361  // Clear flags used in zone edition:
5362  zone->SetNeedRefill( false );
5363 
5364  return zone.release();
5365 }
5366 
5367 
5369 {
5370  wxCHECK_MSG( CurTok() == T_target, nullptr,
5371  wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as PCB_TARGET." ) );
5372 
5373  wxPoint pt;
5374  T token;
5375 
5376  std::unique_ptr<PCB_TARGET> target = std::make_unique<PCB_TARGET>( nullptr );
5377 
5378  for( token = NextTok(); token != T_RIGHT; token = NextTok() )
5379  {
5380  if( token == T_LEFT )
5381  token = NextTok();
5382 
5383  switch( token )
5384  {
5385  case T_x:
5386  target->SetShape( 1 );
5387  break;
5388 
5389  case T_plus:
5390  target->SetShape( 0 );
5391  break;
5392 
5393  case T_at:
5394  pt.x = parseBoardUnits( "target x position" );
5395  pt.y = parseBoardUnits( "target y position" );
5396  target->SetPosition( pt );
5397  NeedRIGHT();
5398  break;
5399 
5400  case T_size:
5401  target->SetSize( parseBoardUnits( "target size" ) );
5402  NeedRIGHT();
5403  break;
5404 
5405  case T_width:
5406  target->SetWidth( parseBoardUnits( "target thickness" ) );
5407  NeedRIGHT();
5408  break;
5409 
5410  case T_layer:
5411  target->SetLayer( parseBoardItemLayer() );
5412  NeedRIGHT();
5413  break;
5414 
5415  case T_tstamp:
5416  NextTok();
5417  const_cast<KIID&>( target->m_Uuid ) = CurStrToKIID();
5418  NeedRIGHT();
5419  break;
5420 
5421  default:
5422  Expecting( "x, plus, at, size, width, layer or tstamp" );
5423  }
5424  }
5425 
5426  return target.release();
5427 }
5428 
5429 
5431 {
5432  KIID aId;
5433 
5434  if( m_resetKIIDs )
5435  {
5436  aId = KIID();
5437  m_resetKIIDMap.insert( std::make_pair( CurStr(), aId ) );
5438  }
5439  else
5440  {
5441  aId = KIID( CurStr() );
5442  }
5443 
5444  return aId;
5445 }
void SetMirrored(bool isMirrored)
Definition: eda_text.h:188
void parseHeader()
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:750
An 8 bit string that is assuredly encoded in UTF8, and supplies special conversion support to and fro...
Definition: utf8.h:70
PCB_DIMENSION_BASE * parseDIMENSION()
ZONE_CONNECTION
How pads are covered by copper in zone.
Definition: zones.h:41
Container to handle a stock of specific vias each with unique diameter and drill sizes in the BOARD c...
int m_ZoneFillVersion
Option to select different fill algorithms.
Arcs (with rounded ends)
bool IsFilled() const
Definition: pcb_shape.h:75
void SetBrdLayerId(PCB_LAYER_ID aBrdLayerId)
Bezier Curve.
void SetTypeName(const wxString &aName)
void DoNotShowCheckbox(wxString file, int line)
Checks the 'do not show again' setting for the dialog.
Definition: confirm.cpp:55
KIID CurStrToKIID()
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
Definition: typeinfo.h:100
class PCB_DIM_LEADER, a leader dimension (graphic item)
Definition: typeinfo.h:101
LSET parseBoardItemLayersAsMask()
Parse the layers definition of a BOARD_ITEM object.
BOARD_STACKUP_ITEM_TYPE
Definition: board_stackup.h:40
class FP_TEXT, text in a footprint
Definition: typeinfo.h:92
wxPoint m_GridOrigin
origin for grid offsets
static wxString FROM_UTF8(const char *cstring)
Convert a UTF8 encoded C string to a wxString for all wxWidgets build modes.
Definition: macros.h:110
void SetHeight(int aHeight)
Set the distance from the feature points to the crossbar line.
polygon (not yet used for tracks, but could be in microwave apps)
wxString m_name
The canonical name of the layer.
Definition: board.h:108
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition: locale_io.h:40
Helper class to create more flexible dialogs, including 'do not show again' checkbox handling.
Definition: confirm.h:45
const wxPoint & GetEnd() const
Return the ending point of the graphic.
Definition: pcb_shape.h:134
Manage layers needed to make a physical board.
This file is part of the common library.
PCB_PLOT_PARAMS_PARSER is the parser class for PCB_PLOT_PARAMS.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:80
std::vector< int > m_TrackWidthList
class PCB_GROUP, a set of BOARD_ITEMs
Definition: typeinfo.h:108
void SetRevision(const wxString &aRevision)
Definition: title_block.h:81
wxString m_FinishType
The name of external copper finish.
static constexpr double IU_PER_MM
Mock up a conversion function.
A set of BOARD_ITEMs (i.e., without duplicates).
Definition: pcb_group.h:50
std::vector< DIFF_PAIR_DIMENSION > m_DiffPairDimensionsList
bool m_EdgePlating
True if the edge board is plated.
the 3d code uses this value
Definition: typeinfo.h:79
int GetRadius() const
Return the radius of this item.
Definition: pcb_shape.cpp:487
LAYER_T m_type
The type of the layer.
Definition: board.h:110
void SetItalic(bool isItalic)
Definition: eda_text.h:179
std::vector< KIID > memberUuids
Definition: pcb_parser.h:386
bool IsEmpty() const
int GetWidth() const
Definition: pcb_shape.h:97
Like smd, does not appear on the solder paste layer (default)
BS_EDGE_CONNECTOR_CONSTRAINTS m_EdgeConnectorConstraints
If the board has edge connector cards, some constrains can be specified in job file: BS_EDGE_CONNECTO...
void parseBoardStackup()
Smd pad, appears on the solder paste layer (default)
const wxPoint & GetBezierC2() const
Definition: pcb_shape.h:116
wxPoint parseXY()
Parse a coordinate pair (xy X Y) in board units (mm).
Definition: pcb_parser.cpp:268
void parseNETINFO_ITEM()
bool SetType(const wxString &aStandardPageDescriptionName, bool aIsPortrait=false)
Set the name of the page type and also the sizes and margins commonly associated with that type name.
Definition: page_info.cpp:119
void createOldLayerMapping(std::unordered_map< std::string, std::string > &aMap)
Create a mapping from the (short-lived) bug where layer names were translated.
bool m_CastellatedPads
True if castellated pads exist.
int m_DimensionPrecision
Number of digits after the decimal.
#define DEFAULT_LINE_WIDTH
static uint32_t parseHex(LINE_READER &aReader, const char *aLine, const char **aOutput=nullptr)
Parse an ASCII hex integer string with possible leading whitespace into a long integer and updates th...
FOOTPRINT * parseFOOTPRINT(wxArrayString *aInitialComments=nullptr)
BOARD * parseBOARD()
Definition: pcb_parser.cpp:671
void SetDate(const wxString &aDate)
Set the date field, and defaults to the current time and date.
Definition: title_block.h:71
wxString AsString() const
Definition: kiid.cpp:218
class FP_SHAPE, a footprint edge
Definition: typeinfo.h:93
void parseEDA_TEXT(EDA_TEXT *aText)
Parse the common settings for any object derived from EDA_TEXT.
Definition: pcb_parser.cpp:400
Abstract dimension API.
Definition: pcb_dimension.h:95
static const wxChar Custom[]
"User" defined page type
Definition: page_info.h:77
void checkpoint()
Definition: pcb_parser.cpp:110
void SetTextSize(const wxSize &aNewSize)
Definition: eda_text.h:237
virtual void SetLocked(bool aLocked)
Modify the 'lock' status for of the item.
Definition: board_item.h:252
void parseTITLE_BLOCK()
PCB_SHAPE * parsePCB_SHAPE()
static double parseDouble(LINE_READER &aReader, const char *aLine, const char **aOutput=nullptr)
Parses an ASCII point string with possible leading whitespace into a double precision floating point ...
void RotatePoint(int *pX, int *pY, double angle)
Definition: trigo.cpp:228
A logical library item identifier and consists of various portions much like a URI.
Definition: lib_id.h:51
a test point pad
Hold the information shown in the lower right corner of a plot, printout, or editing view.
Definition: title_block.h:40
VECTOR3D m_Offset
3D model offset (mm)
Definition: footprint.h:95
PCB_ARC * parseARC()
An orthogonal dimension is like an aligned dimension, but the extension lines are locked to the X or ...
Container to handle a stock of specific differential pairs each with unique track width,...
std::unique_ptr< PAD > m_Pad_Master
void SetViaDrill(int aSize)
Definition: netclass.h:137
virtual void SetParent(EDA_ITEM *aParent)
Definition: eda_item.h:116
int parseBoardUnits()
Definition: pcb_parser.cpp:195
void parseLayers()
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
DIM_UNITS_MODE m_DimensionUnitsMode
Plated through hole pad.
This file contains miscellaneous commonly used macros and functions.
wxPoint GetArcStart() const
Definition: pcb_shape.h:156
int Mm2mils(double x)
Convert mm to mils.
Definition: base_units.cpp:56
For better understanding of the points that make a dimension:
FP_SHAPE * parseFP_SHAPE()
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition: eda_text.h:119
a pad used as heat sink, usually in SMD footprints
void parsePAGE_INFO()
int StrPrintf(std::string *result, const char *format,...)
This is like sprintf() but the output is appended to a std::string instead of to a character array.
Definition: richio.cpp:78
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition: macros.h:96
wxSize m_TextSize[LAYER_CLASS_COUNT]
bool parseBool()
Definition: pcb_parser.cpp:229
virtual void SetVisible(bool aVisible)
Definition: eda_text.h:185
void skipCurrent()
Skip the current token level, i.e search for the RIGHT parenthesis which closes the current descripti...
Definition: pcb_parser.cpp:132
bool m_HasDielectricConstrains
True if some layers have impedance controlled tracks or have specific constrains for micro-wave appli...
Definition: kiid.h:44
const std::vector< wxPoint > BuildPolyPointsList() const
Build and return the list of corners in a std::vector<wxPoint>
Definition: pcb_shape.cpp:1277
bool parsePAD_option(PAD *aPad)
void SetClosed(bool aClosed)
Mark the line chain as closed (i.e.
bool m_Show
Include model in rendering.
Definition: footprint.h:98
like PAD_PTH, but not plated
PCB_LAYER_ID
A quick note on layer IDs:
wxString GetRequiredVersion()
Return a string representing the version of KiCad required to open this file.
Definition: pcb_parser.cpp:244
int m_TextThickness[LAYER_CLASS_COUNT]
LSET is a set of PCB_LAYER_IDs.
#define THROW_PARSE_ERROR(aProblem, aSource, aInputLine, aLineNumber, aByteIndex)
Definition: ki_exception.h:164
pads are covered by copper
PCB_VIA * parsePCB_VIA()
void FetchUnitsFromString(const wxString &aTextValue, EDA_UNITS &aUnits)
Function FetchUnitsFromString writes any unit info found in the string to aUnits.
Definition: base_units.cpp:387
virtual void SetText(const wxString &aText)
Definition: eda_text.cpp:114
void SetComment(int aIdx, const wxString &aComment)
Definition: title_block.h:101
std::pair< wxString, wxString > parseProperty()
Definition: pcb_parser.cpp:385
PCB_TRACK * parsePCB_TRACK()
static int GetDefaultHatchPitch()
Definition: zone.cpp:1088
static const wxChar * Name(PCB_LAYER_ID aLayerId)
Return the fixed name association with aLayerId.
Definition: lset.cpp:82
#define MIN_VISIBILITY_MASK
void SetClearance(int aClearance)
Definition: netclass.h:125
Represent a set of closed polygons.
SHAPE_LINE_CHAIN & Outline(int aIndex)
Describe the page size and margins of a paper page on which to eventually print or plot.
Definition: page_info.h:53
void SetDielectricLayerId(int aLayerId)
PCB_TEXT * parsePCB_TEXT()
void SetVertJustify(EDA_TEXT_VJUSTIFY_T aType)
Definition: eda_text.h:202
A collection of nets and the parameters used to route or test these nets.
Definition: netclass.h:46
void SetNetCode(int aNetCode)
Definition: netinfo.h:114
wxPoint GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition: pcb_shape.cpp:385
const wxPoint & GetStart() const
Return the starting point of the graphic.
Definition: pcb_shape.h:124
VECTOR3D m_Scale
3D model scaling factor (dimensionless)
Definition: footprint.h:93
Smd pad, used in BGA footprints.
void SetExtensionHeight(int aHeight)
bool m_TextItalic[LAYER_CLASS_COUNT]
PCB_TARGET * parsePCB_TARGET()
PCB_LAYER_ID parseBoardItemLayer()
Parse the layer definition of a BOARD_ITEM object.
void SetCompany(const wxString &aCompany)
Definition: title_block.h:91
static LSET InternalCuMask()
Return a complete set of internal copper layers which is all Cu layers except F_Cu and B_Cu.
Definition: lset.cpp:710
bool m_visible
Definition: board.h:111
const wxPoint & GetBezierC1() const
Definition: pcb_shape.h:113
static GAL_SET DefaultVisible()
Definition: lset.cpp:933
a fiducial (usually a smd) local to the parent footprint
#define _(s)
FP_TEXT * parseFP_TEXT()
double m_Opacity
Definition: footprint.h:96
static int parseInt(LINE_READER &aReader, const char *aLine, const char **aOutput=nullptr)
Parse an ASCII integer string with possible leading whitespace into an integer and updates the pointe...
bool m_BlindBuriedViaAllowed
true to allow blind/buried vias
void SetMaterial(const wxString &aName, int aDielectricSubLayer=0)
Handle a list of polygons defining a copper zone.
Definition: zone.h:57
wxString m_userName
The user defined name of the layer.
Definition: board.h:109
void SetuViaDiameter(int aSize)
Definition: netclass.h:141
#define BOARD_FILE_HOST_VERSION
Earlier files than this include the host tag.
FOOTPRINT * parseFOOTPRINT_unchecked(wxArrayString *aInitialComments=nullptr)
void parseOutlinePoints(SHAPE_LINE_CHAIN &aPoly)
Parses possible outline points and stores them into aPoly.
Definition: pcb_parser.cpp:288
static LAYER_T ParseType(const char *aType)
Convert a string to a LAYER_T.
Definition: board.cpp:438
void SetCustomShapeInZoneOpt(CUST_PAD_SHAPE_IN_ZONE aOption)
Set the option for the custom pad shape to use as clearance area in copper zones.
Definition: pad.h:194
BOARD_ITEM * Parse()
Definition: pcb_parser.cpp:624
void SetDrawCoord()
Set draw coordinates (absolute values ) from relative coordinates.
Definition: fp_shape.cpp:82
void SetTitle(const wxString &aTitle)
Definition: title_block.h:58
Parameters and options when plotting/printing a board.
void parseNETCLASS()
int NewOutline()
Creates a new hole in a given outline.
Manage one layer needed to make a physical board.
Definition: board_stackup.h:89
int m_LineThickness[LAYER_CLASS_COUNT]
PAD * parsePAD(FOOTPRINT *aParent=nullptr)
void SetHeightMils(int aHeightInMils)
Definition: page_info.cpp:257
void SetEpsilonR(double aEpsilon, int aDielectricSubLayer=0)
void AddDielectricPrms(int aDielectricPrmsIdx)
Add (insert) a DIELECTRIC_PRMS item to m_DielectricPrmsList all values are set to default.
Thermal relief only for THT pads.
void parseDefaults(BOARD_DESIGN_SETTINGS &aSettings)
void SetThickness(int aThickness, int aDielectricSubLayer=0)
static const int ORPHANED
NETINFO_ITEM meaning that there was no net assigned for an item, as there was no board storing net li...
Definition: netinfo.h:369
int LAYER_NUM
This can be replaced with int and removed.
class FOOTPRINT, a footprint
Definition: typeinfo.h:88
ZONE_SETTINGS handles zones parameters.
Definition: zone_settings.h:67
const KIID m_Uuid
Definition: eda_item.h:475
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
wxString m_Filename
The 3D shape filename in 3D library.
Definition: footprint.h:97
Definition: seg.h:40
void parseGROUP(BOARD_ITEM *aParent)
EDA_UNITS
Definition: eda_units.h:38
BOARD * parseBOARD_unchecked()
Definition: pcb_parser.cpp:687
void UpdateHeight(const wxPoint &aCrossbarStart, const wxPoint &aCrossbarEnd)
Update the stored height basing on points coordinates.
void clear()
Definition: board.h:88
void parseLayer(LAYER *aLayer)
void SetThicknessLocked(bool aLocked, int aDielectricSubLayer=0)
Handle the data for a net.
Definition: netinfo.h:64
int Parse(const UTF8 &aId, bool aFix=false)
Parse LIB_ID with the information from aId.
Definition: lib_id.cpp:49
A filename or source description, a problem input line, a line number, a byte offset,...
Definition: ki_exception.h:118
void parseDefaultTextDims(BOARD_DESIGN_SETTINGS &aSettings, int aLayer)
VECTOR3D m_Rotation
3D model rotation (degrees)
Definition: footprint.h:94
void SetuViaDrill(int aSize)
Definition: netclass.h:145
std::vector< SEG > ZONE_SEGMENT_FILL
Definition: zone.h:44
a fiducial (usually a smd) for the full board
const char * name
Definition: DXF_plotter.cpp:59
no special fabrication property
Container to hold information pertinent to a layer of a BOARD.
Definition: board.h:81
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:190
#define SEXPR_BOARD_FILE_VERSION
Current s-expression file format version. 2 was the last legacy format version.
void SetHorizJustify(EDA_TEXT_HJUSTIFY_T aType)
Definition: eda_text.h:201
Represent a polyline (an zero-thickness chain of connected line segments).
void parseGeneralSection()
void init()
Clear and re-establish m_layerMap with the default layer names.
Definition: pcb_parser.cpp:65
void SetWidthMils(int aWidthInMils)
Definition: page_info.cpp:243
class ZONE, managed by a footprint
Definition: typeinfo.h:94
double GetAngle() const
Definition: pcb_shape.h:107
constexpr ret_type KiROUND(fp_type v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:73
void pushValueIntoMap(int aIndex, int aValue)
Add aValue value in netcode mapping (m_netCodes) at aIndex.
Definition: pcb_parser.cpp:153
Pads are not covered.
std::vector< VIA_DIMENSION > m_ViasDimensionsList
double parseDouble()
Parse the current token as an ASCII numeric string with possible leading whitespace into a double pre...
Definition: pcb_parser.cpp:165
int ShowModal() override
Definition: confirm.cpp:99
void SetViaDiameter(int aDia)
Definition: netclass.h:133
ZONE_BORDER_DISPLAY_STYLE
Zone border styles.
Definition: zone_settings.h:46
A leader is a dimension-like object pointing to a specific point.
a pad with a castellated through hole
class PCB_DIM_ORTHOGONAL, a linear dimension constrained to x/y
Definition: typeinfo.h:103
class PCB_VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:96
void SetTextThickness(int aWidth)
The TextThickness is that set by the user.
Definition: eda_text.h:159
wxString ConvertToNewOverbarNotation(const wxString &aOldStr)
Convert the old ~...~ overbar notation to the new ~{...} one.
Definition: string.cpp:44
Pcbnew s-expression file format parser definition.
ZONE_SETTINGS & GetDefaultZoneSettings()
Variant of PARSE_ERROR indicating that a syntax or related error was likely caused by a file generate...
Definition: ki_exception.h:174
bool m_MicroViasAllowed
true to allow micro vias
void SetColor(const wxString &aColorName)
Abstract interface for BOARD_ITEMs capable of storing other items inside.
int m_number
The layer ID.
Definition: board.h:112
segment with non rounded ends
void Add(BOARD_STACKUP_ITEM *aItem)
Add a new item in stackup layer.
Definition: pad.h:57
void Parse(PCB_PLOT_PARAMS_PARSER *aParser)
BOARD_ITEM * parent
Definition: pcb_parser.h:382
static constexpr int Millimeter2iu(double mm)
void resolveGroups(BOARD_ITEM *aParent)
Called after parsing a footprint definition or board to build the group membership lists.
Definition: pcb_parser.cpp:931
void SetBold(bool aBold)
Definition: eda_text.h:182
void SetPortrait(bool aIsPortrait)
Rotate the paper page 90 degrees.
Definition: page_info.cpp:186
static const int UNCONNECTED
Constant that forces initialization of a netinfo item to the NETINFO_ITEM ORPHANED (typically -1) whe...
Definition: netinfo.h:365
#define THROW_IO_ERROR(msg)
Definition: ki_exception.h:38
void SetLossTangent(double aTg, int aDielectricSubLayer=0)
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition: eda_text.h:133
wxPoint m_AuxOrigin
origin for plot exports
FP_3DMODEL * parse3DModel()
Definition: pcb_parser.cpp:518
bool m_TextUpright[LAYER_CLASS_COUNT]
void parseSetup()
T lookUpLayer(const M &aMap)
Parse the current token for the layer definition of a BOARD_ITEM object.
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:113
void SetAnchorPadShape(PAD_SHAPE aShape)
Set the shape of the anchor pad for custom shaped pads.
Definition: pad.h:205
Container for design settings for a BOARD object.
int GetNetCode() const
Definition: netinfo.h:113
ZONE * parseZONE(BOARD_ITEM_CONTAINER *aParent)