KiCad PCB EDA Suite
legacy_plugin.cpp
Go to the documentation of this file.
1 
2 /*
3  * This program source code file is part of KiCad, a free EDA CAD application.
4  *
5  * Copyright (C) 2007-2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
6  * Copyright (C) 2019 Jean-Pierre Charras, jp.charras@wanadoo.fr
7  * Copyright (C) 1992-2020 KiCad Developers, see AUTHORS.txt for contributors.
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation; either version 2
12  * of the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, you may find one here:
21  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
22  * or you may search the http://www.gnu.org website for the version 2 license,
23  * or you may write to the Free Software Foundation, Inc.,
24  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
25  */
26 
27 /*
28  This implements loading and saving a BOARD, behind the PLUGIN interface.
29 
30  The definitions:
31 
32  *) a Board Internal Unit (BIU) is a unit of length that is used only internally
33  to PCBNEW, and is nanometers when this work is done, but deci-mils until done.
34 
35  The philosophies:
36 
37  *) BIUs should be typed as such to distinguish them from ints. This is mostly
38  for human readability, and having the type nearby in the source supports this readability.
39  *) Do not assume that BIUs will always be int, doing a sscanf() into a BIU
40  does not make sense in case the size of the BIU changes.
41  *) variables are put onto the stack in an automatic, even when it might look
42  more efficient to do otherwise. This is so we can seem them with a debugger.
43  *) Global variables should not be touched from within a PLUGIN, since it will eventually
44  be in a DLL/DSO. This includes window information too. The PLUGIN API knows
45  nothing of wxFrame or globals and all error reporting must be done by throwing
46  an exception.
47  *) No wxWindowing calls are made in here, since the UI resides higher up than in here,
48  and is going to process a bucket of detailed information thrown from down here
49  in the form of an exception if an error happens.
50  *) Much of what we do in this source file is for human readability, not performance.
51  Simply avoiding strtok() more often than the old code washes out performance losses.
52  Remember strncmp() will bail as soon as a mismatch happens, not going all the way
53  to end of string unless a full match.
54  *) angles are in the process of migrating to doubles, and 'int' if used, is
55  only shortterm, and along with this a change, and transition from from
56  "tenths of degrees" to simply "degrees" in the double (which has no problem
57  representing any portion of a degree).
58 */
59 
60 
61 #include <cerrno>
62 #include <cmath>
63 #include <cstdio>
64 #include <cstring>
65 #include <plugins/legacy/legacy_plugin.h> // implement this here
66 #include <wx/ffile.h>
67 #include <wx/string.h>
68 
69 #include <kicad_string.h>
70 #include <locale_io.h>
71 #include <macros.h>
72 #include <properties.h>
73 #include <zones.h>
74 
75 #include <board.h>
76 #include <footprint.h>
77 #include <track.h>
78 #include <pcb_text.h>
79 #include <zone.h>
80 #include <dimension.h>
81 #include <pcb_shape.h>
82 #include <pcb_target.h>
83 #include <fp_shape.h>
84 #include <pcb_plot_params.h>
85 #include <pcb_plot_params_parser.h>
86 #include <convert_to_biu.h>
87 #include <trigo.h>
88 #include <confirm.h>
89 #include <math/util.h> // for KiROUND
90 
92 
93 
94 #define VERSION_ERROR_FORMAT _( "File \"%s\" is format version: %d.\nI only support format version <= %d.\nPlease upgrade Pcbnew to load this file." )
95 
96 
97 typedef unsigned LEG_MASK;
98 
99 #define FIRST_LAYER 0
100 #define FIRST_COPPER_LAYER 0
101 #define LAYER_N_BACK 0
102 #define LAYER_N_2 1
103 #define LAYER_N_3 2
104 #define LAYER_N_4 3
105 #define LAYER_N_5 4
106 #define LAYER_N_6 5
107 #define LAYER_N_7 6
108 #define LAYER_N_8 7
109 #define LAYER_N_9 8
110 #define LAYER_N_10 9
111 #define LAYER_N_11 10
112 #define LAYER_N_12 11
113 #define LAYER_N_13 12
114 #define LAYER_N_14 13
115 #define LAYER_N_15 14
116 #define LAYER_N_FRONT 15
117 #define LAST_COPPER_LAYER LAYER_N_FRONT
118 
119 #define FIRST_NON_COPPER_LAYER 16
120 #define ADHESIVE_N_BACK 16
121 #define ADHESIVE_N_FRONT 17
122 #define SOLDERPASTE_N_BACK 18
123 #define SOLDERPASTE_N_FRONT 19
124 #define SILKSCREEN_N_BACK 20
125 #define SILKSCREEN_N_FRONT 21
126 #define SOLDERMASK_N_BACK 22
127 #define SOLDERMASK_N_FRONT 23
128 #define DRAW_N 24
129 #define COMMENT_N 25
130 #define ECO1_N 26
131 #define ECO2_N 27
132 #define EDGE_N 28
133 #define LAST_NON_COPPER_LAYER 28
134 
135 // Masks to identify a layer by a bit map
136 typedef unsigned LAYER_MSK;
137 #define LAYER_BACK (1 << LAYER_N_BACK)
138 #define LAYER_2 (1 << LAYER_N_2)
139 #define LAYER_3 (1 << LAYER_N_3)
140 #define LAYER_4 (1 << LAYER_N_4)
141 #define LAYER_5 (1 << LAYER_N_5)
142 #define LAYER_6 (1 << LAYER_N_6)
143 #define LAYER_7 (1 << LAYER_N_7)
144 #define LAYER_8 (1 << LAYER_N_8)
145 #define LAYER_9 (1 << LAYER_N_9)
146 #define LAYER_10 (1 << LAYER_N_10)
147 #define LAYER_11 (1 << LAYER_N_11)
148 #define LAYER_12 (1 << LAYER_N_12)
149 #define LAYER_13 (1 << LAYER_N_13)
150 #define LAYER_14 (1 << LAYER_N_14)
151 #define LAYER_15 (1 << LAYER_N_15)
152 #define LAYER_FRONT (1 << LAYER_N_FRONT)
153 #define ADHESIVE_LAYER_BACK (1 << ADHESIVE_N_BACK)
154 #define ADHESIVE_LAYER_FRONT (1 << ADHESIVE_N_FRONT)
155 #define SOLDERPASTE_LAYER_BACK (1 << SOLDERPASTE_N_BACK)
156 #define SOLDERPASTE_LAYER_FRONT (1 << SOLDERPASTE_N_FRONT)
157 #define SILKSCREEN_LAYER_BACK (1 << SILKSCREEN_N_BACK)
158 #define SILKSCREEN_LAYER_FRONT (1 << SILKSCREEN_N_FRONT)
159 #define SOLDERMASK_LAYER_BACK (1 << SOLDERMASK_N_BACK)
160 #define SOLDERMASK_LAYER_FRONT (1 << SOLDERMASK_N_FRONT)
161 #define DRAW_LAYER (1 << DRAW_N)
162 #define COMMENT_LAYER (1 << COMMENT_N)
163 #define ECO1_LAYER (1 << ECO1_N)
164 #define ECO2_LAYER (1 << ECO2_N)
165 #define EDGE_LAYER (1 << EDGE_N)
166 
167 // Helpful global layer masks:
168 // ALL_AUX_LAYERS layers are technical layers, ALL_NO_CU_LAYERS has user
169 // and edge layers too!
170 #define ALL_NO_CU_LAYERS 0x1FFF0000
171 #define ALL_CU_LAYERS 0x0000FFFF
172 #define FRONT_TECH_LAYERS (SILKSCREEN_LAYER_FRONT | SOLDERMASK_LAYER_FRONT \
173  | ADHESIVE_LAYER_FRONT | SOLDERPASTE_LAYER_FRONT)
174 #define BACK_TECH_LAYERS (SILKSCREEN_LAYER_BACK | SOLDERMASK_LAYER_BACK \
175  | ADHESIVE_LAYER_BACK | SOLDERPASTE_LAYER_BACK)
176 #define ALL_TECH_LAYERS (FRONT_TECH_LAYERS | BACK_TECH_LAYERS)
177 #define BACK_LAYERS (LAYER_BACK | BACK_TECH_LAYERS)
178 #define FRONT_LAYERS (LAYER_FRONT | FRONT_TECH_LAYERS)
179 
180 #define ALL_USER_LAYERS (DRAW_LAYER | COMMENT_LAYER | ECO1_LAYER | ECO2_LAYER )
181 
182 #define NO_LAYERS 0x00000000
183 
184 
185 // Old internal units definition (UI = decimil)
186 #define PCB_LEGACY_INTERNAL_UNIT 10000
187 
189 #define SZ( x ) (sizeof(x)-1)
190 
191 
192 static const char delims[] = " \t\r\n";
193 
194 
195 static bool inline isSpace( int c ) { return strchr( delims, c ) != 0; }
196 
197 #define MASK(x) (1<<(x))
198 
199 //-----<BOARD Load Functions>---------------------------------------------------
200 
202 #define TESTLINE( x ) ( !strncasecmp( line, x, SZ( x ) ) && isSpace( line[SZ( x )] ) )
203 
205 #define TESTSUBSTR( x ) ( !strncasecmp( line, x, SZ( x ) ) )
206 
207 
208 #if 1
209 #define READLINE( rdr ) rdr->ReadLine()
210 
211 #else
212 static inline char* ReadLine( LINE_READER* rdr, const char* caller )
216 {
217  char* ret = rdr->ReadLine();
218 
219  const char* line = rdr->Line();
220 
221 #if 0 // trap
222  if( !strcmp( "loadSETUP", caller ) && !strcmp( "$EndSETUP\n", line ) )
223  {
224  int breakhere = 1;
225  }
226 #endif
227 
228  return ret;
229 }
230 #define READLINE( rdr ) ReadLine( rdr, __FUNCTION__ )
231 #endif
232 
233 
234 static EDA_TEXT_HJUSTIFY_T horizJustify( const char* horizontal )
235 {
236  if( !strcmp( "L", horizontal ) )
237  return GR_TEXT_HJUSTIFY_LEFT;
238  if( !strcmp( "R", horizontal ) )
239  return GR_TEXT_HJUSTIFY_RIGHT;
241 }
242 
243 static EDA_TEXT_VJUSTIFY_T vertJustify( const char* vertical )
244 {
245  if( !strcmp( "T", vertical ) )
246  return GR_TEXT_VJUSTIFY_TOP;
247  if( !strcmp( "B", vertical ) )
250 }
251 
252 
254 inline int layerMaskCountSet( LEG_MASK aMask )
255 {
256  int count = 0;
257 
258  for( int i = 0; aMask; ++i, aMask >>= 1 )
259  {
260  if( aMask & 1 )
261  ++count;
262  }
263 
264  return count;
265 }
266 
267 
268 // return true if aLegacyLayerNum is a valid copper layer legacy id, therefore
269 // top, bottom or inner activated layer
270 inline bool is_leg_copperlayer_valid( int aCu_Count, LAYER_NUM aLegacyLayerNum )
271 {
272  return ( aLegacyLayerNum == LAYER_N_FRONT ) || ( aLegacyLayerNum < aCu_Count );
273 }
274 
275 
277 {
278  int newid;
279  unsigned old = aLayerNum;
280 
281  // this is a speed critical function, be careful.
282 
283  if( unsigned( old ) <= unsigned( LAYER_N_FRONT ) )
284  {
285  // In .brd files, the layers are numbered from back to front
286  // (the opposite of the .kicad_pcb files)
287  if( old == LAYER_N_FRONT )
288  newid = F_Cu;
289  else if( old == LAYER_N_BACK )
290  newid = B_Cu;
291  else
292  {
293  newid = cu_count - 1 - old;
294  wxASSERT( newid >= 0 );
295  // This is of course incorrect, but at least it avoid crashing pcbnew:
296  if( newid < 0 )
297  newid = 0;
298  }
299  }
300  else
301  {
302  switch( old )
303  {
304  case ADHESIVE_N_BACK: newid = B_Adhes; break;
305  case ADHESIVE_N_FRONT: newid = F_Adhes; break;
306  case SOLDERPASTE_N_BACK: newid = B_Paste; break;
307  case SOLDERPASTE_N_FRONT: newid = F_Paste; break;
308  case SILKSCREEN_N_BACK: newid = B_SilkS; break;
309  case SILKSCREEN_N_FRONT: newid = F_SilkS; break;
310  case SOLDERMASK_N_BACK: newid = B_Mask; break;
311  case SOLDERMASK_N_FRONT: newid = F_Mask; break;
312  case DRAW_N: newid = Dwgs_User; break;
313  case COMMENT_N: newid = Cmts_User; break;
314  case ECO1_N: newid = Eco1_User; break;
315  case ECO2_N: newid = Eco2_User; break;
316  case EDGE_N: newid = Edge_Cuts; break;
317  default:
318 // wxASSERT( 0 );
319  // Remap all illegal non copper layers to comment layer
320  newid = Cmts_User;
321  }
322  }
323 
324  return PCB_LAYER_ID( newid );
325 }
326 
327 
328 LSET LEGACY_PLUGIN::leg_mask2new( int cu_count, unsigned aMask )
329 {
330  LSET ret;
331 
332  if( ( aMask & ALL_CU_LAYERS ) == ALL_CU_LAYERS )
333  {
334  ret = LSET::AllCuMask();
335 
336  aMask &= ~ALL_CU_LAYERS;
337  }
338 
339  for( int i=0; aMask; ++i, aMask >>= 1 )
340  {
341  if( aMask & 1 )
342  ret.set( leg_layer2new( cu_count, i ) );
343  }
344 
345  return ret;
346 }
347 
348 
356 static inline int intParse( const char* next, const char** out = NULL )
357 {
358  // please just compile this and be quiet, hide casting ugliness:
359  return (int) strtol( next, (char**) out, 10 );
360 }
361 
366 static inline LAYER_NUM layerParse( const char* next, const char** out = NULL )
367 {
368  return intParse( next, out );
369 }
370 
378 static inline long hexParse( const char* next, const char** out = NULL )
379 {
380  // please just compile this and be quiet, hide casting ugliness:
381  return strtol( next, (char**) out, 16 );
382 }
383 
384 
385 BOARD* LEGACY_PLUGIN::Load( const wxString& aFileName, BOARD* aAppendToMe,
386  const PROPERTIES* aProperties )
387 {
388  LOCALE_IO toggle; // toggles on, then off, the C locale.
389 
390  init( aProperties );
391 
392  m_board = aAppendToMe ? aAppendToMe : new BOARD();
393 
394  // Give the filename to the board if it's new
395  if( !aAppendToMe )
396  m_board->SetFileName( aFileName );
397 
398  // delete on exception, iff I own m_board, according to aAppendToMe
399  std::unique_ptr<BOARD> deleter( aAppendToMe ? NULL : m_board );
400 
401  FILE_LINE_READER reader( aFileName );
402 
403  m_reader = &reader; // member function accessibility
404 
405  checkVersion();
406 
407  loadAllSections( bool( aAppendToMe ) );
408 
409  deleter.release();
410  return m_board;
411 }
412 
413 
414 void LEGACY_PLUGIN::loadAllSections( bool doAppend )
415 {
416  // $GENERAL section is first
417 
418  // $SHEETDESCR section is next
419 
420  // $SETUP section is next
421 
422  // Then follows $EQUIPOT and all the rest
423  char* line;
424 
425  while( ( line = READLINE( m_reader ) ) != NULL )
426  {
427  // put the more frequent ones at the top, but realize TRACKs are loaded as a group
428 
429  if( TESTLINE( "$MODULE" ) )
430  {
431  std::unique_ptr<FOOTPRINT> footprint = std::make_unique<FOOTPRINT>( m_board );
432 
433  LIB_ID fpid;
434  std::string fpName = StrPurge( line + SZ( "$MODULE" ) );
435 
436  // The footprint names in legacy libraries can contain the '/' and ':'
437  // characters which will cause the FPID parser to choke.
438  ReplaceIllegalFileNameChars( &fpName );
439 
440  if( !fpName.empty() )
441  fpid.Parse( fpName, LIB_ID::ID_PCB, true );
442 
443  footprint->SetFPID( fpid );
444 
445  loadFOOTPRINT( footprint.get());
446  m_board->Add( footprint.release(), ADD_MODE::APPEND );
447  }
448 
449  else if( TESTLINE( "$DRAWSEGMENT" ) )
450  {
451  loadPCB_LINE();
452  }
453 
454  else if( TESTLINE( "$EQUIPOT" ) )
455  {
457  }
458 
459  else if( TESTLINE( "$TEXTPCB" ) )
460  {
461  loadPCB_TEXT();
462  }
463 
464  else if( TESTLINE( "$TRACK" ) )
465  {
467  }
468 
469  else if( TESTLINE( "$NCLASS" ) )
470  {
471  loadNETCLASS();
472  }
473 
474  else if( TESTLINE( "$CZONE_OUTLINE" ) )
475  {
477  }
478 
479  else if( TESTLINE( "$COTATION" ) )
480  {
481  loadDIMENSION();
482  }
483 
484  else if( TESTLINE( "$PCB_TARGET" ) || TESTLINE( "$MIREPCB" ) )
485  {
486  loadPCB_TARGET();
487  }
488 
489  else if( TESTLINE( "$ZONE" ) )
490  {
491  // No longer supported; discard segment fills
493  }
494 
495  else if( TESTLINE( "$GENERAL" ) )
496  {
497  loadGENERAL();
498  }
499 
500  else if( TESTLINE( "$SHEETDESCR" ) )
501  {
502  loadSHEET();
503  }
504 
505  else if( TESTLINE( "$SETUP" ) )
506  {
507  if( !doAppend )
508  {
509  loadSETUP();
510  }
511  else
512  {
513  while( ( line = READLINE( m_reader ) ) != NULL )
514  {
515  // gobble until $EndSetup
516  if( TESTLINE( "$EndSETUP" ) )
517  break;
518  }
519  }
520  }
521 
522  else if( TESTLINE( "$EndBOARD" ) )
523  return; // preferred exit
524  }
525 
526  THROW_IO_ERROR( "Missing '$EndBOARD'" );
527 }
528 
529 
531 {
532  // Read first line and TEST if it is a PCB file format header like this:
533  // "PCBNEW-BOARD Version 1 ...."
534 
535  m_reader->ReadLine();
536 
537  char* line = m_reader->Line();
538 
539  if( !TESTLINE( "PCBNEW-BOARD" ) )
540  {
541  THROW_IO_ERROR( "Unknown file type" );
542  }
543 
544  int ver = 1; // if sccanf fails
545  sscanf( line, "PCBNEW-BOARD Version %d", &ver );
546 
547 #if !defined(DEBUG)
548  if( ver > LEGACY_BOARD_FILE_VERSION )
549  {
550  // "File \"%s\" is format version: %d.\nI only support format version <= %d.\nPlease upgrade Pcbnew to load this file."
552  m_reader->GetSource().GetData(), ver, LEGACY_BOARD_FILE_VERSION );
554  }
555 #endif
556 
559 }
560 
561 
563 {
564  char* line;
565  char* saveptr;
566  bool saw_LayerCount = false;
567 
568  while( ( line = READLINE( m_reader ) ) != NULL )
569  {
570  const char* data;
571 
572  if( TESTLINE( "Units" ) )
573  {
574  // what are the engineering units of the lengths in the BOARD?
575  data = strtok_r( line + SZ("Units"), delims, &saveptr );
576 
577  if( !strcmp( data, "mm" ) )
578  {
580  }
581  }
582 
583  else if( TESTLINE( "LayerCount" ) )
584  {
585  int tmp = intParse( line + SZ( "LayerCount" ) );
586 
588 
589  // This has to be set early so that leg_layer2new() works OK, and
590  // that means before parsing "EnabledLayers" and "VisibleLayers".
591  m_cu_count = tmp;
592 
593  saw_LayerCount = true;
594  }
595 
596  else if( TESTLINE( "EnabledLayers" ) )
597  {
598  if( !saw_LayerCount )
599  THROW_IO_ERROR( "Missing '$GENERAL's LayerCount" );
600 
601  LEG_MASK enabledLayers = hexParse( line + SZ( "EnabledLayers" ) );
602  LSET new_mask = leg_mask2new( m_cu_count, enabledLayers );
603 
604  m_board->SetEnabledLayers( new_mask );
605 
606  // layer visibility equals layer usage, unless overridden later via "VisibleLayers"
607  // Must call SetEnabledLayers() before calling SetVisibleLayers().
608  m_board->SetVisibleLayers( new_mask );
609 
610  // Ensure copper layers count is not modified:
612  }
613 
614  else if( TESTLINE( "VisibleLayers" ) )
615  {
616  // Keep all enabled layers visible.
617  // the old visibility control does not make sense in current Pcbnew version
618  // However, this code works.
619  #if 0
620  if( !saw_LayerCount )
621  THROW_IO_ERROR( "Missing '$GENERAL's LayerCount" );
622 
623  LEG_MASK visibleLayers = hexParse( line + SZ( "VisibleLayers" ) );
624 
625  LSET new_mask = leg_mask2new( m_cu_count, visibleLayers );
626 
627  m_board->SetVisibleLayers( new_mask );
628  #endif
629  }
630 
631  else if( TESTLINE( "Ly" ) ) // Old format for Layer count
632  {
633  if( !saw_LayerCount )
634  {
635  LEG_MASK layer_mask = hexParse( line + SZ( "Ly" ) );
636 
637  m_cu_count = layerMaskCountSet( layer_mask & ALL_CU_LAYERS );
639 
640  saw_LayerCount = true;
641  }
642  }
643 
644  else if( TESTLINE( "BoardThickness" ) )
645  {
646  BIU thickn = biuParse( line + SZ( "BoardThickness" ) );
648  }
649 
650  else if( TESTLINE( "NoConn" ) )
651  {
652  // ignore
653  intParse( line + SZ( "NoConn" ) );
654  }
655 
656  else if( TESTLINE( "Di" ) )
657  {
658  biuParse( line + SZ( "Di" ), &data );
659  biuParse( data, &data );
660  biuParse( data, &data );
661  biuParse( data );
662  }
663 
664  else if( TESTLINE( "Nnets" ) )
665  {
666  m_netCodes.resize( intParse( line + SZ( "Nnets" ) ) );
667  }
668 
669  else if( TESTLINE( "Nn" ) ) // id "Nnets" for old .brd files
670  {
671  m_netCodes.resize( intParse( line + SZ( "Nn" ) ) );
672  }
673 
674  else if( TESTLINE( "$EndGENERAL" ) )
675  return; // preferred exit
676  }
677 
678  THROW_IO_ERROR( "Missing '$EndGENERAL'" );
679 }
680 
681 
683 {
684  char buf[260];
685  TITLE_BLOCK tb;
686  char* line;
687  char* data;
688 
689  while( ( line = READLINE( m_reader ) ) != NULL )
690  {
691  if( TESTLINE( "Sheet" ) )
692  {
693  // e.g. "Sheet A3 16535 11700"
694  // width and height are in 1/1000th of an inch, always
695  PAGE_INFO page;
696  char* sname = strtok_r( line + SZ( "Sheet" ), delims, &data );
697 
698  if( sname )
699  {
700  wxString wname = FROM_UTF8( sname );
701 
702  if( !page.SetType( wname ) )
703  {
704  m_error.Printf( _( "Unknown sheet type \"%s\" on line:%d" ),
705  wname.GetData(),
706  m_reader->LineNumber() );
708  }
709 
710  char* width = strtok_r( NULL, delims, &data );
711  char* height = strtok_r( NULL, delims, &data );
712  char* orient = strtok_r( NULL, delims, &data );
713 
714  // only parse the width and height if page size is custom ("User")
715  if( wname == PAGE_INFO::Custom )
716  {
717  if( width && height )
718  {
719  // legacy disk file describes paper in mils
720  // (1/1000th of an inch)
721  int w = intParse( width );
722  int h = intParse( height );
723 
724  page.SetWidthMils( w );
725  page.SetHeightMils( h );
726  }
727  }
728 
729  if( orient && !strcmp( orient, "portrait" ) )
730  {
731  page.SetPortrait( true );
732  }
733 
734  m_board->SetPageSettings( page );
735  }
736  }
737 
738  else if( TESTLINE( "Title" ) )
739  {
740  ReadDelimitedText( buf, line, sizeof(buf) );
741  tb.SetTitle( FROM_UTF8( buf ) );
742  }
743 
744  else if( TESTLINE( "Date" ) )
745  {
746  ReadDelimitedText( buf, line, sizeof(buf) );
747  tb.SetDate( FROM_UTF8( buf ) );
748  }
749 
750  else if( TESTLINE( "Rev" ) )
751  {
752  ReadDelimitedText( buf, line, sizeof(buf) );
753  tb.SetRevision( FROM_UTF8( buf ) );
754  }
755 
756  else if( TESTLINE( "Comp" ) )
757  {
758  ReadDelimitedText( buf, line, sizeof(buf) );
759  tb.SetCompany( FROM_UTF8( buf ) );
760  }
761 
762  else if( TESTLINE( "Comment1" ) )
763  {
764  ReadDelimitedText( buf, line, sizeof(buf) );
765  tb.SetComment( 0, FROM_UTF8( buf ) );
766  }
767 
768  else if( TESTLINE( "Comment2" ) )
769  {
770  ReadDelimitedText( buf, line, sizeof(buf) );
771  tb.SetComment( 1, FROM_UTF8( buf ) );
772  }
773 
774  else if( TESTLINE( "Comment3" ) )
775  {
776  ReadDelimitedText( buf, line, sizeof(buf) );
777  tb.SetComment( 2, FROM_UTF8( buf ) );
778  }
779 
780  else if( TESTLINE( "Comment4" ) )
781  {
782  ReadDelimitedText( buf, line, sizeof(buf) );
783  tb.SetComment( 3, FROM_UTF8( buf ) );
784  }
785 
786  else if( TESTLINE( "Comment5" ) )
787  {
788  ReadDelimitedText( buf, line, sizeof(buf) );
789  tb.SetComment( 4, FROM_UTF8( buf ) );
790  }
791 
792  else if( TESTLINE( "Comment6" ) )
793  {
794  ReadDelimitedText( buf, line, sizeof(buf) );
795  tb.SetComment( 5, FROM_UTF8( buf ) );
796  }
797 
798  else if( TESTLINE( "Comment7" ) )
799  {
800  ReadDelimitedText( buf, line, sizeof(buf) );
801  tb.SetComment( 6, FROM_UTF8( buf ) );
802  }
803 
804  else if( TESTLINE( "Comment8" ) )
805  {
806  ReadDelimitedText( buf, line, sizeof(buf) );
807  tb.SetComment( 7, FROM_UTF8( buf ) );
808  }
809 
810  else if( TESTLINE( "Comment9" ) )
811  {
812  ReadDelimitedText( buf, line, sizeof(buf) );
813  tb.SetComment( 8, FROM_UTF8( buf ) );
814  }
815 
816  else if( TESTLINE( "$EndSHEETDESCR" ) )
817  {
818  m_board->SetTitleBlock( tb );
819  return; // preferred exit
820  }
821  }
822 
823  THROW_IO_ERROR( "Missing '$EndSHEETDESCR'" );
824 }
825 
826 
828 {
831  NETCLASS* netclass_default = bds.GetDefault();
832  char* line;
833  char* saveptr;
834 
836 
837  while( ( line = READLINE( m_reader ) ) != NULL )
838  {
839  const char* data;
840 
841  if( TESTLINE( "PcbPlotParams" ) )
842  {
843  PCB_PLOT_PARAMS plot_opts;
844 
845  PCB_PLOT_PARAMS_PARSER parser( line + SZ( "PcbPlotParams" ), m_reader->GetSource() );
846 
847  plot_opts.Parse( &parser );
848 
849  m_board->SetPlotOptions( plot_opts );
850  }
851 
852  else if( TESTLINE( "AuxiliaryAxisOrg" ) )
853  {
854  BIU gx = biuParse( line + SZ( "AuxiliaryAxisOrg" ), &data );
855  BIU gy = biuParse( data );
856 
857  // m_board->SetAuxOrigin( wxPoint( gx, gy ) ); gets overwritten by SetDesignSettings() below
858  bds.m_AuxOrigin = wxPoint( gx, gy );
859  }
860 
861  /* Done from $General above's "LayerCount"
862  else if( TESTLINE( "Layers" ) )
863  {
864  int tmp = intParse( line + SZ( "Layers" ) );
865  m_board->SetCopperLayerCount( tmp );
866 
867  m_cu_count = tmp;
868  }
869  */
870 
871  else if( TESTSUBSTR( "Layer[" ) )
872  {
873  // eg: "Layer[n] <a_Layer_name_with_no_spaces> <LAYER_T>"
874 
875  LAYER_NUM layer_num = layerParse( line + SZ( "Layer[" ), &data );
876  PCB_LAYER_ID layer_id = leg_layer2new( m_cu_count, layer_num );
877 
878  /*
879  switch( layer_num )
880  {
881  case LAYER_N_BACK:
882  layer_id = B_Cu;
883  break;
884 
885  case LAYER_N_FRONT:
886  layer_id = F_Cu;
887  break;
888 
889  default:
890  layer_id = PCB_LAYER_ID( layer_num );
891  }
892  */
893 
894  data = strtok_r( (char*) data+1, delims, &saveptr ); // +1 for ']'
895 
896  if( data )
897  {
898  wxString layerName = FROM_UTF8( data );
899  m_board->SetLayerName( layer_id, layerName );
900 
901  data = strtok_r( NULL, delims, &saveptr );
902 
903  if( data ) // optional in old board files
904  {
905  LAYER_T type = LAYER::ParseType( data );
906  m_board->SetLayerType( layer_id, type );
907  }
908  }
909  }
910 
911  else if( TESTLINE( "TrackWidth" ) )
912  {
913  BIU tmp = biuParse( line + SZ( "TrackWidth" ) );
914  netclass_default->SetTrackWidth( tmp );
915  }
916 
917  else if( TESTLINE( "TrackWidthList" ) )
918  {
919  BIU tmp = biuParse( line + SZ( "TrackWidthList" ) );
920  bds.m_TrackWidthList.push_back( tmp );
921  }
922 
923  else if( TESTLINE( "TrackClearence" ) )
924  {
925  BIU tmp = biuParse( line + SZ( "TrackClearence" ) );
926  netclass_default->SetClearance( tmp );
927  }
928 
929  else if( TESTLINE( "TrackMinWidth" ) )
930  {
931  BIU tmp = biuParse( line + SZ( "TrackMinWidth" ) );
932  bds.m_TrackMinWidth = tmp;
933  }
934 
935  else if( TESTLINE( "ZoneClearence" ) )
936  {
937  BIU tmp = biuParse( line + SZ( "ZoneClearence" ) );
938  zs.m_ZoneClearance = tmp;
939  }
940 
941  else if( TESTLINE( "Zone_45_Only" ) )
942  {
943  bool tmp = (bool) intParse( line + SZ( "Zone_45_Only" ) );
944  zs.m_Zone_45_Only = tmp;
945  }
946 
947  else if( TESTLINE( "DrawSegmWidth" ) )
948  {
949  BIU tmp = biuParse( line + SZ( "DrawSegmWidth" ) );
950  bds.m_LineThickness[ LAYER_CLASS_COPPER ] = tmp;
951  }
952 
953  else if( TESTLINE( "EdgeSegmWidth" ) )
954  {
955  BIU tmp = biuParse( line + SZ( "EdgeSegmWidth" ) );
956  bds.m_LineThickness[ LAYER_CLASS_EDGES ] = tmp;
957  }
958 
959  else if( TESTLINE( "ViaMinSize" ) )
960  {
961  BIU tmp = biuParse( line + SZ( "ViaMinSize" ) );
962  bds.m_ViasMinSize = tmp;
963  }
964 
965  else if( TESTLINE( "MicroViaMinSize" ) )
966  {
967  BIU tmp = biuParse( line + SZ( "MicroViaMinSize" ) );
968  bds.m_MicroViasMinSize = tmp;
969  }
970 
971  else if( TESTLINE( "ViaSizeList" ) )
972  {
973  // e.g. "ViaSizeList DIAMETER [DRILL]"
974 
975  BIU drill = 0;
976  BIU diameter = biuParse( line + SZ( "ViaSizeList" ), &data );
977 
978  data = strtok_r( (char*) data, delims, (char**) &data );
979  if( data ) // DRILL may not be present ?
980  drill = biuParse( data );
981 
982  bds.m_ViasDimensionsList.emplace_back( diameter,
983  drill );
984  }
985 
986  else if( TESTLINE( "ViaSize" ) )
987  {
988  BIU tmp = biuParse( line + SZ( "ViaSize" ) );
989  netclass_default->SetViaDiameter( tmp );
990  }
991 
992  else if( TESTLINE( "ViaDrill" ) )
993  {
994  BIU tmp = biuParse( line + SZ( "ViaDrill" ) );
995  netclass_default->SetViaDrill( tmp );
996  }
997 
998  else if( TESTLINE( "ViaMinDrill" ) )
999  {
1000  BIU tmp = biuParse( line + SZ( "ViaMinDrill" ) );
1001  bds.m_MinThroughDrill = tmp;
1002  }
1003 
1004  else if( TESTLINE( "MicroViaSize" ) )
1005  {
1006  BIU tmp = biuParse( line + SZ( "MicroViaSize" ) );
1007  netclass_default->SetuViaDiameter( tmp );
1008  }
1009 
1010  else if( TESTLINE( "MicroViaDrill" ) )
1011  {
1012  BIU tmp = biuParse( line + SZ( "MicroViaDrill" ) );
1013  netclass_default->SetuViaDrill( tmp );
1014  }
1015 
1016  else if( TESTLINE( "MicroViaMinDrill" ) )
1017  {
1018  BIU tmp = biuParse( line + SZ( "MicroViaMinDrill" ) );
1019  bds.m_MicroViasMinDrill = tmp;
1020  }
1021 
1022  else if( TESTLINE( "MicroViasAllowed" ) )
1023  {
1024  int tmp = intParse( line + SZ( "MicroViasAllowed" ) );
1025  bds.m_MicroViasAllowed = tmp;
1026  }
1027 
1028  else if( TESTLINE( "TextPcbWidth" ) )
1029  {
1030  BIU tmp = biuParse( line + SZ( "TextPcbWidth" ) );
1031  bds.m_TextThickness[ LAYER_CLASS_COPPER ] = tmp;
1032  }
1033 
1034  else if( TESTLINE( "TextPcbSize" ) )
1035  {
1036  BIU x = biuParse( line + SZ( "TextPcbSize" ), &data );
1037  BIU y = biuParse( data );
1038 
1039  bds.m_TextSize[ LAYER_CLASS_COPPER ] = wxSize( x, y );
1040  }
1041 
1042  else if( TESTLINE( "EdgeModWidth" ) )
1043  {
1044  BIU tmp = biuParse( line + SZ( "EdgeModWidth" ) );
1045  bds.m_LineThickness[ LAYER_CLASS_SILK ] = tmp;
1046  bds.m_LineThickness[ LAYER_CLASS_OTHERS ] = tmp;
1047  }
1048 
1049  else if( TESTLINE( "TextModWidth" ) )
1050  {
1051  BIU tmp = biuParse( line + SZ( "TextModWidth" ) );
1052  bds.m_TextThickness[ LAYER_CLASS_SILK ] = tmp;
1053  bds.m_TextThickness[ LAYER_CLASS_OTHERS ] = tmp;
1054  }
1055 
1056  else if( TESTLINE( "TextModSize" ) )
1057  {
1058  BIU x = biuParse( line + SZ( "TextModSize" ), &data );
1059  BIU y = biuParse( data );
1060 
1061  bds.m_TextSize[ LAYER_CLASS_SILK ] = wxSize( x, y );
1062  bds.m_TextSize[ LAYER_CLASS_OTHERS ] = wxSize( x, y );
1063  }
1064 
1065  else if( TESTLINE( "PadSize" ) )
1066  {
1067  BIU x = biuParse( line + SZ( "PadSize" ), &data );
1068  BIU y = biuParse( data );
1069 
1070  bds.m_Pad_Master.SetSize( wxSize( x, y ) );
1071  }
1072 
1073  else if( TESTLINE( "PadDrill" ) )
1074  {
1075  BIU tmp = biuParse( line + SZ( "PadDrill" ) );
1076  bds.m_Pad_Master.SetDrillSize( wxSize( tmp, tmp ) );
1077  }
1078 
1079  else if( TESTLINE( "Pad2MaskClearance" ) )
1080  {
1081  BIU tmp = biuParse( line + SZ( "Pad2MaskClearance" ) );
1082  bds.m_SolderMaskMargin = tmp;
1083  }
1084 
1085  else if( TESTLINE( "SolderMaskMinWidth" ) )
1086  {
1087  BIU tmp = biuParse( line + SZ( "SolderMaskMinWidth" ) );
1088  bds.m_SolderMaskMinWidth = tmp;
1089  }
1090 
1091  else if( TESTLINE( "Pad2PasteClearance" ) )
1092  {
1093  BIU tmp = biuParse( line + SZ( "Pad2PasteClearance" ) );
1094  bds.m_SolderPasteMargin = tmp;
1095  }
1096 
1097  else if( TESTLINE( "Pad2PasteClearanceRatio" ) )
1098  {
1099  double ratio = atof( line + SZ( "Pad2PasteClearanceRatio" ) );
1100  bds.m_SolderPasteMarginRatio = ratio;
1101  }
1102 
1103  else if( TESTLINE( "GridOrigin" ) )
1104  {
1105  BIU x = biuParse( line + SZ( "GridOrigin" ), &data );
1106  BIU y = biuParse( data );
1107 
1108  // m_board->SetGridOrigin( wxPoint( x, y ) ); gets overwritten by SetDesignSettings() below
1109  bds.m_GridOrigin = wxPoint( x, y );
1110  }
1111 
1112  else if( TESTLINE( "VisibleElements" ) )
1113  {
1114  // Keep all elements visible.
1115  // the old visibility control does not make sense in current Pcbnew version,
1116  // and this code does not work.
1117  #if 0
1118  int visibleElements = hexParse( line + SZ( "VisibleElements" ) );
1119 
1120  // Does not work: each old item should be tested one by one to set
1121  // visibility of new item list
1122  GAL_SET visibles;
1123  for( size_t i = 0; i < visibles.size(); i++ )
1124  visibles.set( i, visibleElements & ( 1u << i ) );
1125 
1126  m_board->SetVisibleElements( visibles );
1127  #endif
1128  }
1129 
1130  else if( TESTLINE( "$EndSETUP" ) )
1131  {
1132  m_board->SetZoneSettings( zs );
1133 
1134  // Very old *.brd file does not have NETCLASSes
1135  // "TrackWidth", "ViaSize", "ViaDrill", "ViaMinSize", and "TrackClearence" were
1136  // defined in SETUP; these values are put into the default NETCLASS until later board
1137  // load code should override them. *.brd files which have been saved with knowledge
1138  // of NETCLASSes will override these defaults, very old boards (before 2009) will not
1139  // and use the setup values.
1140  // However these values should be the same as default NETCLASS.
1141 
1142  return; // preferred exit
1143  }
1144  }
1145 
1146  /*
1147  * Ensure tracks and vias sizes lists are ok:
1148  * Sort lists by by increasing value and remove duplicates
1149  * (the first value is not tested, because it is the netclass value)
1150  */
1151  BOARD_DESIGN_SETTINGS& designSettings = m_board->GetDesignSettings();
1152  sort( designSettings.m_ViasDimensionsList.begin() + 1, designSettings.m_ViasDimensionsList.end() );
1153  sort( designSettings.m_TrackWidthList.begin() + 1, designSettings.m_TrackWidthList.end() );
1154 
1155  for( unsigned ii = 1; ii < designSettings.m_ViasDimensionsList.size() - 1; ii++ )
1156  {
1157  if( designSettings.m_ViasDimensionsList[ii] == designSettings.m_ViasDimensionsList[ii + 1] )
1158  {
1159  designSettings.m_ViasDimensionsList.erase( designSettings.m_ViasDimensionsList.begin() + ii );
1160  ii--;
1161  }
1162  }
1163 
1164  for( unsigned ii = 1; ii < designSettings.m_TrackWidthList.size() - 1; ii++ )
1165  {
1166  if( designSettings.m_TrackWidthList[ii] == designSettings.m_TrackWidthList[ii + 1] )
1167  {
1168  designSettings.m_TrackWidthList.erase( designSettings.m_TrackWidthList.begin() + ii );
1169  ii--;
1170  }
1171  }
1172 }
1173 
1174 
1176 {
1177  char* line;
1178 
1179  while( ( line = READLINE( m_reader ) ) != NULL )
1180  {
1181  const char* data;
1182 
1183  // most frequently encountered ones at the top
1184 
1185  if( TESTSUBSTR( "D" ) && strchr( "SCAP", line[1] ) ) // read a drawing item, e.g. "DS"
1186  {
1187  loadFP_SHAPE( aFootprint );
1188  }
1189 
1190  else if( TESTLINE( "$PAD" ) )
1191  {
1192  loadPAD( aFootprint );
1193  }
1194 
1195  // Read a footprint text description (ref, value, or drawing)
1196  else if( TESTSUBSTR( "T" ) )
1197  {
1198  // e.g. "T1 6940 -16220 350 300 900 60 M I 20 N "CFCARD"\r\n"
1199  int tnum = intParse( line + SZ( "T" ) );
1200 
1201  FP_TEXT* text = nullptr;
1202 
1203  switch( tnum )
1204  {
1206  text = &aFootprint->Reference();
1207  break;
1208 
1210  text = &aFootprint->Value();
1211  break;
1212 
1213  // All other fields greater than 1.
1214  default:
1215  text = new FP_TEXT( aFootprint );
1216  aFootprint->Add( text );
1217  }
1218 
1219  loadMODULE_TEXT( text );
1220  }
1221 
1222  else if( TESTLINE( "Po" ) )
1223  {
1224  // e.g. "Po 19120 39260 900 0 4E823D06 68183921-93a5-49ac-91b0-49d05a0e1647 ~~\r\n"
1225  BIU pos_x = biuParse( line + SZ( "Po" ), &data );
1226  BIU pos_y = biuParse( data, &data );
1227  int orient = intParse( data, &data );
1228  LAYER_NUM layer_num = layerParse( data, &data );
1229  PCB_LAYER_ID layer_id = leg_layer2new( m_cu_count, layer_num );
1230  long edittime = hexParse( data, &data );
1231  char* uuid = strtok_r( (char*) data, delims, (char**) &data );
1232 
1233  data = strtok_r( (char*) data+1, delims, (char**) &data );
1234 
1235  // data is now a two character long string
1236  // Note: some old files do not have this field
1237  if( data && data[0] == 'F' )
1238  aFootprint->SetLocked( true );
1239 
1240  if( data && data[1] == 'P' )
1241  aFootprint->SetIsPlaced( true );
1242 
1243  aFootprint->SetPosition( wxPoint( pos_x, pos_y ) );
1244  aFootprint->SetLayer( layer_id );
1245  aFootprint->SetOrientation( orient );
1246  const_cast<KIID&>( aFootprint->m_Uuid ) = KIID( uuid );
1247  aFootprint->SetLastEditTime( edittime );
1248  }
1249 
1250  /* footprint name set earlier, immediately after FOOTPRINT construction
1251  else if( TESTLINE( "Li" ) ) // Library name of footprint
1252  {
1253  // There can be whitespace in the footprint name on some old libraries.
1254  // Grab everything after "Li" up to end of line:
1255  //aFootprint->SetFPID( FROM_UTF8( StrPurge( line + SZ( "Li" ) ) ) );
1256  }
1257  */
1258 
1259  else if( TESTLINE( "Sc" ) ) // timestamp
1260  {
1261  char* uuid = strtok_r( (char*) line + SZ( "Sc" ), delims, (char**) &data );
1262  const_cast<KIID&>( aFootprint->m_Uuid ) = KIID( uuid );
1263  }
1264 
1265  else if( TESTLINE( "Op" ) ) // (Op)tions for auto placement
1266  {
1267  int itmp1 = hexParse( line + SZ( "Op" ), &data );
1268  int itmp2 = hexParse( data );
1269 
1270  int cntRot180 = itmp2 & 0x0F;
1271  if( cntRot180 > 10 )
1272  cntRot180 = 10;
1273 
1274  aFootprint->SetPlacementCost180( cntRot180 );
1275 
1276  int cntRot90 = itmp1 & 0x0F;
1277  if( cntRot90 > 10 )
1278  cntRot90 = 0;
1279 
1280  itmp1 = (itmp1 >> 4) & 0x0F;
1281  if( itmp1 > 10 )
1282  itmp1 = 0;
1283 
1284  aFootprint->SetPlacementCost90((itmp1 << 4) | cntRot90 );
1285  }
1286 
1287  else if( TESTLINE( "At" ) ) // (At)tributes of footprint
1288  {
1289  int attrs = 0;
1290 
1291  data = line + SZ( "At" );
1292 
1293  if( strstr( data, "SMD" ) )
1294  attrs |= FP_SMD;
1295  else if( strstr( data, "VIRTUAL" ) )
1297  else
1299 
1300  aFootprint->SetAttributes( attrs );
1301  }
1302 
1303  else if( TESTLINE( "AR" ) ) // Alternate Reference
1304  {
1305  // e.g. "AR /68183921-93a5-49ac-e164-49d05a0e1647/93a549d0-49d0-e164-91b0-49d05a0e1647"
1306  data = strtok_r( line + SZ( "AR" ), delims, (char**) &data );
1307 
1308  if( data )
1309  aFootprint->SetPath( KIID_PATH( FROM_UTF8( data ) ) );
1310  }
1311 
1312  else if( TESTLINE( "$SHAPE3D" ) )
1313  {
1314  load3D( aFootprint );
1315  }
1316 
1317  else if( TESTLINE( "Cd" ) )
1318  {
1319  // e.g. "Cd Double rangee de contacts 2 x 4 pins\r\n"
1320  aFootprint->SetDescription( FROM_UTF8( StrPurge( line + SZ( "Cd" ) ) ) );
1321  }
1322 
1323  else if( TESTLINE( "Kw" ) ) // Key words
1324  {
1325  aFootprint->SetKeywords( FROM_UTF8( StrPurge( line + SZ( "Kw" ) ) ) );
1326  }
1327 
1328  else if( TESTLINE( ".SolderPasteRatio" ) )
1329  {
1330  double tmp = atof( line + SZ( ".SolderPasteRatio" ) );
1331  // Due to a bug in dialog editor in Footprint Editor, fixed in BZR version 3565
1332  // this parameter can be broken.
1333  // It should be >= -50% (no solder paste) and <= 0% (full area of the pad)
1334 
1335  if( tmp < -0.50 )
1336  tmp = -0.50;
1337  if( tmp > 0.0 )
1338  tmp = 0.0;
1339  aFootprint->SetLocalSolderPasteMarginRatio( tmp );
1340  }
1341 
1342  else if( TESTLINE( ".SolderPaste" ) )
1343  {
1344  BIU tmp = biuParse( line + SZ( ".SolderPaste" ) );
1345  aFootprint->SetLocalSolderPasteMargin( tmp );
1346  }
1347 
1348  else if( TESTLINE( ".SolderMask" ) )
1349  {
1350  BIU tmp = biuParse( line + SZ( ".SolderMask" ) );
1351  aFootprint->SetLocalSolderMaskMargin( tmp );
1352  }
1353 
1354  else if( TESTLINE( ".LocalClearance" ) )
1355  {
1356  BIU tmp = biuParse( line + SZ( ".LocalClearance" ) );
1357  aFootprint->SetLocalClearance( tmp );
1358  }
1359 
1360  else if( TESTLINE( ".ZoneConnection" ) )
1361  {
1362  int tmp = intParse( line + SZ( ".ZoneConnection" ) );
1363  aFootprint->SetZoneConnection((ZONE_CONNECTION) tmp );
1364  }
1365 
1366  else if( TESTLINE( ".ThermalWidth" ) )
1367  {
1368  BIU tmp = biuParse( line + SZ( ".ThermalWidth" ) );
1369  aFootprint->SetThermalWidth( tmp );
1370  }
1371 
1372  else if( TESTLINE( ".ThermalGap" ) )
1373  {
1374  BIU tmp = biuParse( line + SZ( ".ThermalGap" ) );
1375  aFootprint->SetThermalGap( tmp );
1376  }
1377 
1378  else if( TESTLINE( "$EndMODULE" ) )
1379  {
1380  aFootprint->CalculateBoundingBox();
1381 
1382  return; // preferred exit
1383  }
1384  }
1385 
1386  wxString msg = wxString::Format(
1387  _( "Missing '$EndMODULE' for MODULE \"%s\"" ), aFootprint->GetFPID().GetLibItemName().wx_str() );
1388 
1389  THROW_IO_ERROR( msg );
1390 }
1391 
1392 
1394 {
1395  std::unique_ptr<PAD> pad = std::make_unique<PAD>( aFootprint );
1396  char* line;
1397  char* saveptr;
1398 
1399  while( ( line = READLINE( m_reader ) ) != NULL )
1400  {
1401  const char* data;
1402 
1403  if( TESTLINE( "Sh" ) ) // (Sh)ape and padname
1404  {
1405  // e.g. "Sh "A2" C 520 520 0 0 900"
1406  // or "Sh "1" R 157 1378 0 0 900"
1407 
1408  // mypadname is LATIN1/CRYLIC for BOARD_FORMAT_VERSION 1,
1409  // but for BOARD_FORMAT_VERSION 2, it is UTF8 from disk.
1410  // So we have to go through two code paths. Moving forward
1411  // padnames will be in UTF8 on disk, as are all KiCad strings on disk.
1412  char mypadname[50];
1413 
1414  data = line + SZ( "Sh" ) + 1; // +1 skips trailing whitespace
1415 
1416  data = data + ReadDelimitedText( mypadname, data, sizeof(mypadname) ) + 1; // +1 trailing whitespace
1417 
1418  while( isSpace( *data ) )
1419  ++data;
1420 
1421  unsigned char padchar = (unsigned char) *data++;
1422  int padshape;
1423 
1424  BIU size_x = biuParse( data, &data );
1425  BIU size_y = biuParse( data, &data );
1426  BIU delta_x = biuParse( data, &data );
1427  BIU delta_y = biuParse( data, &data );
1428  double orient = degParse( data );
1429 
1430  switch( padchar )
1431  {
1432  case 'C': padshape = PAD_SHAPE_CIRCLE; break;
1433  case 'R': padshape = PAD_SHAPE_RECT; break;
1434  case 'O': padshape = PAD_SHAPE_OVAL; break;
1435  case 'T': padshape = PAD_SHAPE_TRAPEZOID; break;
1436  default:
1437  m_error.Printf( _( "Unknown padshape '%c=0x%02x' on line: %d of footprint: \"%s\"" ),
1438  padchar,
1439  padchar,
1440  m_reader->LineNumber(),
1441  aFootprint->GetFPID().GetLibItemName().wx_str()
1442  );
1444  }
1445 
1446  // go through a wxString to establish a universal character set properly
1447  wxString padname;
1448 
1449  if( m_loading_format_version == 1 )
1450  {
1451  // add 8 bit bytes, file format 1 was KiCad font type byte,
1452  // simply promote those 8 bit bytes up into UNICODE. (subset of LATIN1)
1453  const unsigned char* cp = (unsigned char*) mypadname;
1454  while( *cp )
1455  {
1456  padname += *cp++; // unsigned, ls 8 bits only
1457  }
1458  }
1459  else
1460  {
1461  // version 2, which is UTF8.
1462  padname = FROM_UTF8( mypadname );
1463  }
1464  // chances are both were ASCII, but why take chances?
1465 
1466  pad->SetName( padname );
1467  pad->SetShape( PAD_SHAPE_T( padshape ) );
1468  pad->SetSize( wxSize( size_x, size_y ) );
1469  pad->SetDelta( wxSize( delta_x, delta_y ) );
1470  pad->SetOrientation( orient );
1471  }
1472 
1473  else if( TESTLINE( "Dr" ) ) // (Dr)ill
1474  {
1475  // e.g. "Dr 350 0 0" or "Dr 0 0 0 O 0 0"
1476  BIU drill_x = biuParse( line + SZ( "Dr" ), &data );
1477  BIU drill_y = drill_x;
1478  BIU offs_x = biuParse( data, &data );
1479  BIU offs_y = biuParse( data, &data );
1480 
1482 
1483  data = strtok_r( (char*) data, delims, &saveptr );
1484  if( data ) // optional shape
1485  {
1486  if( data[0] == 'O' )
1487  {
1488  drShape = PAD_DRILL_SHAPE_OBLONG;
1489 
1490  data = strtok_r( NULL, delims, &saveptr );
1491  drill_x = biuParse( data );
1492 
1493  data = strtok_r( NULL, delims, &saveptr );
1494  drill_y = biuParse( data );
1495  }
1496  }
1497 
1498  pad->SetDrillShape( drShape );
1499  pad->SetOffset( wxPoint( offs_x, offs_y ) );
1500  pad->SetDrillSize( wxSize( drill_x, drill_y ) );
1501  }
1502 
1503  else if( TESTLINE( "At" ) ) // (At)tribute
1504  {
1505  // e.g. "At SMD N 00888000"
1506  // sscanf( PtLine, "%s %s %X", BufLine, BufCar, &m_layerMask );
1507 
1508  PAD_ATTR_T attribute;
1509 
1510  data = strtok_r( line + SZ( "At" ), delims, &saveptr );
1511 
1512  if( !strcmp( data, "SMD" ) )
1513  attribute = PAD_ATTRIB_SMD;
1514  else if( !strcmp( data, "CONN" ) )
1515  attribute = PAD_ATTRIB_CONN;
1516  else if( !strcmp( data, "HOLE" ) )
1517  attribute = PAD_ATTRIB_NPTH;
1518  else
1519  attribute = PAD_ATTRIB_PTH;
1520 
1521  strtok_r( NULL, delims, &saveptr ); // skip unused prm
1522  data = strtok_r( NULL, delims, &saveptr );
1523 
1524  LEG_MASK layer_mask = hexParse( data );
1525 
1526  pad->SetLayerSet( leg_mask2new( m_cu_count, layer_mask ) );
1527  pad->SetAttribute( attribute );
1528  }
1529 
1530  else if( TESTLINE( "Ne" ) ) // (Ne)tname
1531  {
1532  // e.g. "Ne 461 "V5.0"
1533 
1534  char buf[1024]; // can be fairly long
1535  int netcode = intParse( line + SZ( "Ne" ), &data );
1536 
1537  // Store the new code mapping
1538  pad->SetNetCode( getNetCode( netcode ) );
1539 
1540  // read Netname
1541  ReadDelimitedText( buf, data, sizeof(buf) );
1542 
1543  if( m_board )
1544  wxASSERT( m_board->FindNet( getNetCode( netcode ) )->GetNetname()
1545  == FROM_UTF8( StrPurge( buf ) ) );
1546  }
1547 
1548  else if( TESTLINE( "Po" ) ) // (Po)sition
1549  {
1550  // e.g. "Po 500 -500"
1551  wxPoint pos;
1552 
1553  pos.x = biuParse( line + SZ( "Po" ), &data );
1554  pos.y = biuParse( data );
1555 
1556  pad->SetPos0( pos );
1557  // pad->SetPosition( pos ); set at function return
1558  }
1559 
1560  else if( TESTLINE( "Le" ) )
1561  {
1562  BIU tmp = biuParse( line + SZ( "Le" ) );
1563  pad->SetPadToDieLength( tmp );
1564  }
1565 
1566  else if( TESTLINE( ".SolderMask" ) )
1567  {
1568  BIU tmp = biuParse( line + SZ( ".SolderMask" ) );
1569  pad->SetLocalSolderMaskMargin( tmp );
1570  }
1571 
1572  else if( TESTLINE( ".SolderPasteRatio" ) )
1573  {
1574  double tmp = atof( line + SZ( ".SolderPasteRatio" ) );
1575  pad->SetLocalSolderPasteMarginRatio( tmp );
1576  }
1577 
1578  else if( TESTLINE( ".SolderPaste" ) )
1579  {
1580  BIU tmp = biuParse( line + SZ( ".SolderPaste" ) );
1581  pad->SetLocalSolderPasteMargin( tmp );
1582  }
1583 
1584  else if( TESTLINE( ".LocalClearance" ) )
1585  {
1586  BIU tmp = biuParse( line + SZ( ".LocalClearance" ) );
1587  pad->SetLocalClearance( tmp );
1588  }
1589 
1590  else if( TESTLINE( ".ZoneConnection" ) )
1591  {
1592  int tmp = intParse( line + SZ( ".ZoneConnection" ) );
1593  pad->SetZoneConnection( (ZONE_CONNECTION) tmp );
1594  }
1595 
1596  else if( TESTLINE( ".ThermalWidth" ) )
1597  {
1598  BIU tmp = biuParse( line + SZ( ".ThermalWidth" ) );
1599  pad->SetThermalSpokeWidth( tmp );
1600  }
1601 
1602  else if( TESTLINE( ".ThermalGap" ) )
1603  {
1604  BIU tmp = biuParse( line + SZ( ".ThermalGap" ) );
1605  pad->SetThermalGap( tmp );
1606  }
1607 
1608  else if( TESTLINE( "$EndPAD" ) )
1609  {
1610  // pad's "Position" is not relative to the footprint's, whereas Pos0 is relative
1611  // to the footprint's but is the unrotated coordinate.
1612 
1613  wxPoint padpos = pad->GetPos0();
1614 
1615  RotatePoint( &padpos, aFootprint->GetOrientation() );
1616 
1617  pad->SetPosition( padpos + aFootprint->GetPosition() );
1618 
1619  aFootprint->Add( pad.release() );
1620  return; // preferred exit
1621  }
1622  }
1623 
1624  THROW_IO_ERROR( "Missing '$EndPAD'" );
1625 }
1626 
1627 
1629 {
1630  PCB_SHAPE_TYPE_T shape;
1631  char* line = m_reader->Line(); // obtain current (old) line
1632 
1633  switch( line[1] )
1634  {
1635  case 'S': shape = S_SEGMENT; break;
1636  case 'C': shape = S_CIRCLE; break;
1637  case 'A': shape = S_ARC; break;
1638  case 'P': shape = S_POLYGON; break;
1639  default:
1640  m_error.Printf( _( "Unknown FP_SHAPE type:'%c=0x%02x' on line:%d of footprint:\"%s\"" ),
1641  (unsigned char) line[1],
1642  (unsigned char) line[1],
1643  m_reader->LineNumber(),
1644  aFootprint->GetFPID().GetLibItemName().wx_str()
1645  );
1647  }
1648 
1649  std::unique_ptr<FP_SHAPE> dwg = std::make_unique<FP_SHAPE>( aFootprint, shape ); // a drawing
1650 
1651  const char* data;
1652 
1653  // common to all cases, and we have to check their values uniformly at end
1654  BIU width = 1;
1656 
1657  switch( shape )
1658  {
1659  case S_ARC:
1660  {
1661  BIU start0_x = biuParse( line + SZ( "DA" ), &data );
1662  BIU start0_y = biuParse( data, &data );
1663  BIU end0_x = biuParse( data, &data );
1664  BIU end0_y = biuParse( data, &data );
1665  double angle = degParse( data, &data );
1666 
1667  width = biuParse( data, &data );
1668  layer = layerParse( data );
1669 
1670  dwg->m_Start0 = wxPoint( start0_x, start0_y );
1671  dwg->m_End0 = wxPoint( end0_x, end0_y );
1672 
1673  // Setting angle will set m_ThirdPoint0, so must be done after setting
1674  // m_Start0 and m_End0
1675  dwg->SetAngle( angle );
1676  }
1677  break;
1678 
1679  case S_SEGMENT:
1680  case S_CIRCLE:
1681  {
1682  // e.g. "DS -7874 -10630 7874 -10630 50 20\r\n"
1683  BIU start0_x = biuParse( line + SZ( "DS" ), &data );
1684  BIU start0_y = biuParse( data, &data );
1685  BIU end0_x = biuParse( data, &data );
1686  BIU end0_y = biuParse( data, &data );
1687 
1688  width = biuParse( data, &data );
1689  layer = layerParse( data );
1690 
1691  dwg->m_Start0 = wxPoint( start0_x, start0_y );
1692  dwg->m_End0 = wxPoint( end0_x, end0_y );
1693  }
1694  break;
1695 
1696  case S_POLYGON:
1697  {
1698  // e.g. "DP %d %d %d %d %d %d %d\n"
1699  BIU start0_x = biuParse( line + SZ( "DP" ), &data );
1700  BIU start0_y = biuParse( data, &data );
1701  BIU end0_x = biuParse( data, &data );
1702  BIU end0_y = biuParse( data, &data );
1703  int ptCount = intParse( data, &data );
1704 
1705  width = biuParse( data, &data );
1706  layer = layerParse( data );
1707 
1708  dwg->m_Start0 = wxPoint( start0_x, start0_y );
1709  dwg->m_End0 = wxPoint( end0_x, end0_y );
1710 
1711  std::vector<wxPoint> pts;
1712  pts.reserve( ptCount );
1713 
1714  for( int ii = 0; ii<ptCount; ++ii )
1715  {
1716  if( ( line = READLINE( m_reader ) ) == NULL )
1717  {
1718  THROW_IO_ERROR( "S_POLGON point count mismatch." );
1719  }
1720 
1721  // e.g. "Dl 23 44\n"
1722 
1723  if( !TESTLINE( "Dl" ) )
1724  {
1725  THROW_IO_ERROR( "Missing Dl point def" );
1726  }
1727 
1728  BIU x = biuParse( line + SZ( "Dl" ), &data );
1729  BIU y = biuParse( data );
1730 
1731  pts.emplace_back( x, y );
1732  }
1733 
1734  dwg->SetPolyPoints( pts );
1735  }
1736  break;
1737 
1738  default:
1739  // first switch code above prevents us from getting here.
1740  break;
1741  }
1742 
1743  // Check for a reasonable layer:
1744  // layer must be >= FIRST_NON_COPPER_LAYER, but because microwave footprints can use the
1745  // copper layers, layer < FIRST_NON_COPPER_LAYER is allowed.
1746  if( layer < FIRST_LAYER || layer > LAST_NON_COPPER_LAYER )
1747  layer = SILKSCREEN_N_FRONT;
1748 
1749  dwg->SetWidth( width );
1750  dwg->SetLayer( leg_layer2new( m_cu_count, layer ) );
1751 
1752  FP_SHAPE* fpShape = dwg.release();
1753 
1754  aFootprint->Add( fpShape );
1755 
1756  // this had been done at the FOOTPRINT level before, presumably because the FP_SHAPE needs
1757  // to be already added to a footprint before this function will work.
1758  fpShape->SetDrawCoord();
1759 }
1760 
1761 
1763 {
1764  const char* data;
1765  const char* txt_end;
1766  const char* line = m_reader->Line(); // current (old) line
1767 
1768  // sscanf( line + 1, "%d %d %d %d %d %d %d %s %s %d %s",
1769  // &type, &m_Pos0.x, &m_Pos0.y, &m_Size.y, &m_Size.x,
1770  // &m_Orient, &m_Thickness, BufCar1, BufCar2, &layer, BufCar3 ) >= 10 )
1771 
1772  // e.g. "T1 6940 -16220 350 300 900 60 M I 20 N "CFCARD"\r\n"
1773  // or T1 0 500 600 400 900 80 M V 20 N"74LS245"
1774  // ouch, the last example has no space between N and "74LS245" !
1775  // that is an older version.
1776 
1777  int type = intParse( line+1, &data );
1778  BIU pos0_x = biuParse( data, &data );
1779  BIU pos0_y = biuParse( data, &data );
1780  BIU size0_y = biuParse( data, &data );
1781  BIU size0_x = biuParse( data, &data );
1782  double orient = degParse( data, &data );
1783  BIU thickn = biuParse( data, &data );
1784 
1785  // read the quoted text before the first call to strtok() which introduces
1786  // NULs into the string and chops it into mutliple C strings, something
1787  // ReadDelimitedText() cannot traverse.
1788 
1789  // convert the "quoted, escaped, UTF8, text" to a wxString, find it by skipping
1790  // as far forward as needed until the first double quote.
1791  txt_end = data + ReadDelimitedText( &m_field, data );
1792  m_field.Replace( "%V", "${VALUE}" );
1793  m_field.Replace( "%R", "${REFERENCE}" );
1794  aText->SetText( m_field );
1795 
1796  // after switching to strtok, there's no easy coming back because of the
1797  // embedded nul(s?) placed to the right of the current field.
1798  // (that's the reason why strtok was deprecated...)
1799  char* mirror = strtok_r( (char*) data, delims, (char**) &data );
1800  char* hide = strtok_r( NULL, delims, (char**) &data );
1801  char* tmp = strtok_r( NULL, delims, (char**) &data );
1802 
1803  LAYER_NUM layer_num = tmp ? layerParse( tmp ) : SILKSCREEN_N_FRONT;
1804 
1805  char* italic = strtok_r( NULL, delims, (char**) &data );
1806 
1807  char* hjust = strtok_r( (char*) txt_end, delims, (char**) &data );
1808  char* vjust = strtok_r( NULL, delims, (char**) &data );
1809 
1810  if( type != FP_TEXT::TEXT_is_REFERENCE
1811  && type != FP_TEXT::TEXT_is_VALUE )
1812  type = FP_TEXT::TEXT_is_DIVERS;
1813 
1814  aText->SetType( static_cast<FP_TEXT::TEXT_TYPE>( type ) );
1815 
1816  aText->SetPos0( wxPoint( pos0_x, pos0_y ) );
1817  aText->SetTextSize( wxSize( size0_x, size0_y ) );
1818 
1819  orient -= ( static_cast<FOOTPRINT*>( aText->GetParent() ) )->GetOrientation();
1820 
1821  aText->SetTextAngle( orient );
1822 
1823  aText->SetTextThickness( thickn < 1 ? 0 : thickn );
1824 
1825  aText->SetMirrored( mirror && *mirror == 'M' );
1826 
1827  aText->SetVisible( !(hide && *hide == 'I') );
1828 
1829  aText->SetItalic( italic && *italic == 'I' );
1830 
1831  if( hjust )
1832  aText->SetHorizJustify( horizJustify( hjust ) );
1833 
1834  if( vjust )
1835  aText->SetVertJustify( vertJustify( vjust ) );
1836 
1837  // A protection against mal formed (or edited by hand) files:
1838  if( layer_num < FIRST_LAYER )
1839  layer_num = FIRST_LAYER;
1840  else if( layer_num > LAST_NON_COPPER_LAYER )
1841  layer_num = LAST_NON_COPPER_LAYER;
1842  else if( layer_num == LAYER_N_BACK )
1843  layer_num = SILKSCREEN_N_BACK;
1844  else if( layer_num == LAYER_N_FRONT )
1845  layer_num = SILKSCREEN_N_FRONT;
1846  else if( layer_num < LAYER_N_FRONT ) // this case is a internal layer
1847  layer_num = SILKSCREEN_N_FRONT;
1848 
1849  aText->SetLayer( leg_layer2new( m_cu_count, layer_num ) );
1850 
1851  // Calculate the actual position.
1852  aText->SetDrawCoord();
1853 }
1854 
1855 
1857 {
1858  FP_3DMODEL t3D;
1859 
1860  char* line;
1861  while( ( line = READLINE( m_reader ) ) != NULL )
1862  {
1863  if( TESTLINE( "Na" ) ) // Shape File Name
1864  {
1865  char buf[512];
1866  ReadDelimitedText( buf, line + SZ( "Na" ), sizeof(buf) );
1867  t3D.m_Filename = buf;
1868  }
1869 
1870  else if( TESTLINE( "Sc" ) ) // Scale
1871  {
1872  sscanf( line + SZ( "Sc" ), "%lf %lf %lf\n",
1873  &t3D.m_Scale.x,
1874  &t3D.m_Scale.y,
1875  &t3D.m_Scale.z );
1876  }
1877 
1878  else if( TESTLINE( "Of" ) ) // Offset
1879  {
1880  sscanf( line + SZ( "Of" ), "%lf %lf %lf\n",
1881  &t3D.m_Offset.x,
1882  &t3D.m_Offset.y,
1883  &t3D.m_Offset.z );
1884  }
1885 
1886  else if( TESTLINE( "Ro" ) ) // Rotation
1887  {
1888  sscanf( line + SZ( "Ro" ), "%lf %lf %lf\n",
1889  &t3D.m_Rotation.x,
1890  &t3D.m_Rotation.y,
1891  &t3D.m_Rotation.z );
1892  }
1893 
1894  else if( TESTLINE( "$EndSHAPE3D" ) )
1895  {
1896  aFootprint->Models().push_back( t3D );
1897  return; // preferred exit
1898  }
1899  }
1900 
1901  THROW_IO_ERROR( "Missing '$EndSHAPE3D'" );
1902 }
1903 
1904 
1906 {
1907  /* example:
1908  $DRAWSEGMENT
1909  Po 0 57500 -1000 57500 0 150
1910  De 24 0 900 0 0
1911  $EndDRAWSEGMENT
1912  */
1913 
1914  std::unique_ptr<PCB_SHAPE> dseg = std::make_unique<PCB_SHAPE>( m_board );
1915 
1916  char* line;
1917  char* saveptr;
1918 
1919  while( ( line = READLINE( m_reader ) ) != NULL )
1920  {
1921  const char* data;
1922 
1923  if( TESTLINE( "Po" ) )
1924  {
1925  int shape = intParse( line + SZ( "Po" ), &data );
1926  BIU start_x = biuParse( data, &data );
1927  BIU start_y = biuParse( data, &data );
1928  BIU end_x = biuParse( data, &data );
1929  BIU end_y = biuParse( data, &data );
1930  BIU width = biuParse( data );
1931 
1932  if( width < 0 )
1933  width = 0;
1934 
1935  dseg->SetShape( PCB_SHAPE_TYPE_T( shape ) );
1936  dseg->SetFilled( false );
1937  dseg->SetWidth( width );
1938  dseg->SetStart( wxPoint( start_x, start_y ) );
1939  dseg->SetEnd( wxPoint( end_x, end_y ) );
1940  }
1941 
1942  else if( TESTLINE( "De" ) )
1943  {
1944  BIU x = 0;
1945  BIU y;
1946 
1947  data = strtok_r( line + SZ( "De" ), delims, &saveptr );
1948  for( int i = 0; data; ++i, data = strtok_r( NULL, delims, &saveptr ) )
1949  {
1950  switch( i )
1951  {
1952  case 0:
1953  LAYER_NUM layer;
1954  layer = layerParse( data );
1955 
1956  if( layer < FIRST_NON_COPPER_LAYER )
1957  layer = FIRST_NON_COPPER_LAYER;
1958 
1959  else if( layer > LAST_NON_COPPER_LAYER )
1960  layer = LAST_NON_COPPER_LAYER;
1961 
1962  dseg->SetLayer( leg_layer2new( m_cu_count, layer ) );
1963  break;
1964  case 1:
1965  int mtype;
1966  mtype = intParse( data );
1967  break;
1968  case 2:
1969  double angle;
1970  angle = degParse( data );
1971  dseg->SetAngle( angle ); // m_Angle
1972  break;
1973  case 3:
1974  const_cast<KIID&>( dseg->m_Uuid ) = KIID( data );
1975  break;
1976  case 4:
1977  STATUS_FLAGS state;
1978  state = static_cast<STATUS_FLAGS>( hexParse( data ) );
1979  dseg->SetState( state, true );
1980  break;
1981 
1982  // Bezier Control Points
1983  case 5:
1984  x = biuParse( data );
1985  break;
1986  case 6:
1987  y = biuParse( data );
1988  dseg->SetBezControl1( wxPoint( x, y ) );
1989  break;
1990 
1991  case 7:
1992  x = biuParse( data );
1993  break;
1994  case 8:
1995  y = biuParse( data );
1996  dseg->SetBezControl2( wxPoint( x, y ) );
1997  break;
1998 
1999  default:
2000  break;
2001  }
2002  }
2003  }
2004 
2005  else if( TESTLINE( "$EndDRAWSEGMENT" ) )
2006  {
2007  m_board->Add( dseg.release(), ADD_MODE::APPEND );
2008  return; // preferred exit
2009  }
2010  }
2011 
2012  THROW_IO_ERROR( "Missing '$EndDRAWSEGMENT'" );
2013 }
2014 
2016 {
2017  /* a net description is something like
2018  * $EQUIPOT
2019  * Na 5 "/BIT1"
2020  * St ~
2021  * $EndEQUIPOT
2022  */
2023 
2024  char buf[1024];
2025 
2026  NETINFO_ITEM* net = NULL;
2027  char* line;
2028  int netCode = 0;
2029 
2030  while( ( line = READLINE( m_reader ) ) != NULL )
2031  {
2032  const char* data;
2033 
2034  if( TESTLINE( "Na" ) )
2035  {
2036  // e.g. "Na 58 "/cpu.sch/PAD7"\r\n"
2037 
2038  netCode = intParse( line + SZ( "Na" ), &data );
2039 
2040  ReadDelimitedText( buf, data, sizeof(buf) );
2041 
2042  if( net == NULL )
2043  net = new NETINFO_ITEM( m_board, FROM_UTF8( buf ), netCode );
2044  else
2045  {
2046  THROW_IO_ERROR( "Two net definitions in '$EQUIPOT' block" );
2047  }
2048  }
2049 
2050  else if( TESTLINE( "$EndEQUIPOT" ) )
2051  {
2052  // net 0 should be already in list, so store this net
2053  // if it is not the net 0, or if the net 0 does not exists.
2054  if( net && ( net->GetNet() > 0 || m_board->FindNet( 0 ) == NULL ) )
2055  {
2056  m_board->Add( net );
2057 
2058  // Be sure we have room to store the net in m_netCodes
2059  if( (int)m_netCodes.size() <= netCode )
2060  m_netCodes.resize( netCode+1 );
2061 
2062  m_netCodes[netCode] = net->GetNet();
2063  net = NULL;
2064  }
2065  else
2066  {
2067  delete net;
2068  net = NULL; // Avoid double deletion.
2069  }
2070 
2071  return; // preferred exit
2072  }
2073  }
2074 
2075  // If we are here, there is an error.
2076  delete net;
2077  THROW_IO_ERROR( "Missing '$EndEQUIPOT'" );
2078 }
2079 
2080 
2082 {
2083  /* examples:
2084  For a single line text:
2085  ----------------------
2086  $TEXTPCB
2087  Te "Text example"
2088  Po 66750 53450 600 800 150 0
2089  De 24 1 0 Italic
2090  $EndTEXTPCB
2091 
2092  For a multi line text:
2093  ---------------------
2094  $TEXTPCB
2095  Te "Text example"
2096  Nl "Line 2"
2097  Po 66750 53450 600 800 150 0
2098  De 24 1 0 Italic
2099  $EndTEXTPCB
2100  Nl "line nn" is a line added to the current text
2101  */
2102 
2103  char text[1024];
2104 
2105  // maybe someday a constructor that takes all this data in one call?
2106  PCB_TEXT* pcbtxt = new PCB_TEXT( m_board );
2107  m_board->Add( pcbtxt, ADD_MODE::APPEND );
2108 
2109  char* line;
2110 
2111  while( ( line = READLINE( m_reader ) ) != NULL )
2112  {
2113  const char* data;
2114 
2115  if( TESTLINE( "Te" ) ) // Text line (or first line for multi line texts)
2116  {
2117  ReadDelimitedText( text, line + SZ( "Te" ), sizeof(text) );
2118  pcbtxt->SetText( FROM_UTF8( text ) );
2119  }
2120 
2121  else if( TESTLINE( "nl" ) ) // next line of the current text
2122  {
2123  ReadDelimitedText( text, line + SZ( "nl" ), sizeof(text) );
2124  pcbtxt->SetText( pcbtxt->GetText() + wxChar( '\n' ) + FROM_UTF8( text ) );
2125  }
2126 
2127  else if( TESTLINE( "Po" ) )
2128  {
2129  wxSize size;
2130 
2131  BIU pos_x = biuParse( line + SZ( "Po" ), &data );
2132  BIU pos_y = biuParse( data, &data );
2133  size.x = biuParse( data, &data );
2134  size.y = biuParse( data, &data );
2135  BIU thickn = biuParse( data, &data );
2136  double angle = degParse( data );
2137 
2138  pcbtxt->SetTextSize( size );
2139  pcbtxt->SetTextThickness( thickn );
2140  pcbtxt->SetTextAngle( angle );
2141 
2142  pcbtxt->SetTextPos( wxPoint( pos_x, pos_y ) );
2143  }
2144 
2145  else if( TESTLINE( "De" ) )
2146  {
2147  // e.g. "De 21 1 68183921-93a5-49ac-91b0-49d05a0e1647 Normal C\r\n"
2148  LAYER_NUM layer_num = layerParse( line + SZ( "De" ), &data );
2149  int notMirrored = intParse( data, &data );
2150  char* uuid = strtok_r( (char*) data, delims, (char**) &data );
2151  char* style = strtok_r( NULL, delims, (char**) &data );
2152  char* hJustify = strtok_r( NULL, delims, (char**) &data );
2153  char* vJustify = strtok_r( NULL, delims, (char**) &data );
2154 
2155  pcbtxt->SetMirrored( !notMirrored );
2156  const_cast<KIID&>( pcbtxt->m_Uuid ) = KIID( uuid );
2157  pcbtxt->SetItalic( !strcmp( style, "Italic" ) );
2158 
2159  if( hJustify )
2160  pcbtxt->SetHorizJustify( horizJustify( hJustify ) );
2161  else
2162  {
2163  // boom, somebody changed a constructor, I was relying on this:
2164  wxASSERT( pcbtxt->GetHorizJustify() == GR_TEXT_HJUSTIFY_CENTER );
2165  }
2166 
2167  if( vJustify )
2168  pcbtxt->SetVertJustify( vertJustify( vJustify ) );
2169 
2170  if( layer_num < FIRST_COPPER_LAYER )
2171  layer_num = FIRST_COPPER_LAYER;
2172  else if( layer_num > LAST_NON_COPPER_LAYER )
2173  layer_num = LAST_NON_COPPER_LAYER;
2174 
2175  if( layer_num >= FIRST_NON_COPPER_LAYER ||
2176  is_leg_copperlayer_valid( m_cu_count, layer_num ) )
2177  pcbtxt->SetLayer( leg_layer2new( m_cu_count, layer_num ) );
2178  else // not perfect, but putting this text on front layer is a workaround
2179  pcbtxt->SetLayer( F_Cu );
2180  }
2181 
2182  else if( TESTLINE( "$EndTEXTPCB" ) )
2183  {
2184  return; // preferred exit
2185  }
2186  }
2187 
2188  THROW_IO_ERROR( "Missing '$EndTEXTPCB'" );
2189 }
2190 
2191 
2192 void LEGACY_PLUGIN::loadTrackList( int aStructType )
2193 {
2194  char* line;
2195 
2196  while( ( line = READLINE( m_reader ) ) != NULL )
2197  {
2198  // read two lines per loop iteration, each loop is one TRACK or VIA
2199  // example first line:
2200  // e.g. "Po 0 23994 28800 24400 28800 150 -1" for a track
2201  // e.g. "Po 3 21086 17586 21086 17586 180 -1" for a via (uses sames start and end)
2202  const char* data;
2203 
2204  if( line[0] == '$' ) // $EndTRACK
2205  return; // preferred exit
2206 
2207  assert( TESTLINE( "Po" ) );
2208 
2209  VIATYPE viatype = static_cast<VIATYPE>( intParse( line + SZ( "Po" ), &data ) );
2210  BIU start_x = biuParse( data, &data );
2211  BIU start_y = biuParse( data, &data );
2212  BIU end_x = biuParse( data, &data );
2213  BIU end_y = biuParse( data, &data );
2214  BIU width = biuParse( data, &data );
2215 
2216  // optional 7th drill parameter (must be optional in an old format?)
2217  data = strtok_r( (char*) data, delims, (char**) &data );
2218 
2219  BIU drill = data ? biuParse( data ) : -1; // SetDefault() if < 0
2220 
2221  // Read the 2nd line to determine the exact type, one of:
2222  // PCB_TRACE_T, PCB_VIA_T, or PCB_SEGZONE_T. The type field in 2nd line
2223  // differentiates between PCB_TRACE_T and PCB_VIA_T. With virtual
2224  // functions in use, it is critical to instantiate the PCB_VIA_T
2225  // exactly.
2226  READLINE( m_reader );
2227 
2228  line = m_reader->Line();
2229 
2230  // example second line:
2231  // "De 0 0 463 0 800000\r\n"
2232 
2233 #if 1
2234  assert( TESTLINE( "De" ) );
2235 #else
2236  if( !TESTLINE( "De" ) )
2237  {
2238  // mandatory 2nd line is missing
2239  THROW_IO_ERROR( "Missing 2nd line of a TRACK def" );
2240  }
2241 #endif
2242 
2243  int makeType;
2244 
2245  // parse the 2nd line to determine the type of object
2246  // e.g. "De 15 1 7 68183921-93a5-49ac-91b0-49d05a0e1647 0" for a via
2247  LAYER_NUM layer_num = layerParse( line + SZ( "De" ), &data );
2248  int type = intParse( data, &data );
2249  int net_code = intParse( data, &data );
2250  char* uuid = strtok_r( (char*) data, delims, (char**) &data );
2251  int flags_int = intParse( data, (const char**) &data );
2252 
2253  STATUS_FLAGS flags = static_cast<STATUS_FLAGS>( flags_int );
2254 
2255  if( aStructType == PCB_TRACE_T )
2256  {
2257  makeType = ( type == 1 ) ? PCB_VIA_T : PCB_TRACE_T;
2258  }
2259  else if (aStructType == NOT_USED )
2260  {
2261  continue;
2262  }
2263  else
2264  {
2265  wxFAIL_MSG( "Segment type unknown" );
2266  continue;
2267  }
2268 
2269  TRACK* newTrack;
2270 
2271  switch( makeType )
2272  {
2273  default:
2274  case PCB_TRACE_T: newTrack = new TRACK( m_board ); break;
2275  case PCB_VIA_T: newTrack = new VIA( m_board ); break;
2276  }
2277 
2278  const_cast<KIID&>( newTrack->m_Uuid ) = KIID( uuid );
2279  newTrack->SetPosition( wxPoint( start_x, start_y ) );
2280  newTrack->SetEnd( wxPoint( end_x, end_y ) );
2281 
2282  newTrack->SetWidth( width );
2283 
2284  if( makeType == PCB_VIA_T ) // Ensure layers are OK when possible:
2285  {
2286  VIA *via = static_cast<VIA*>( newTrack );
2287  via->SetViaType( viatype );
2288 
2289  if( drill < 0 )
2290  via->SetDrillDefault();
2291  else
2292  via->SetDrill( drill );
2293 
2294  if( via->GetViaType() == VIATYPE::THROUGH )
2295  via->SetLayerPair( F_Cu, B_Cu );
2296  else
2297  {
2298  PCB_LAYER_ID back = leg_layer2new( m_cu_count, (layer_num >> 4) & 0xf );
2299  PCB_LAYER_ID front = leg_layer2new( m_cu_count, layer_num & 0xf );
2300 
2301  if( is_leg_copperlayer_valid( m_cu_count, back ) &&
2303  via->SetLayerPair( front, back );
2304  else
2305  {
2306  delete via;
2307  newTrack = NULL;
2308  }
2309  }
2310  }
2311  else
2312  {
2313  // A few legacy boards can have tracks on non existent layers, because
2314  // reducing the number of layers does not remove tracks on removed layers
2315  // If happens, skip them
2316  if( is_leg_copperlayer_valid( m_cu_count, layer_num ) )
2317  newTrack->SetLayer( leg_layer2new( m_cu_count, layer_num ) );
2318  else
2319  {
2320  delete newTrack;
2321  newTrack = NULL;
2322  }
2323  }
2324 
2325  if( newTrack )
2326  {
2327  newTrack->SetNetCode( getNetCode( net_code ) );
2328  newTrack->SetState( flags, true );
2329 
2330  m_board->Add( newTrack );
2331  }
2332  }
2333 
2334  THROW_IO_ERROR( "Missing '$EndTRACK'" );
2335 }
2336 
2337 
2339 {
2340  char buf[1024];
2341  wxString netname;
2342  char* line;
2343 
2344  // create an empty NETCLASS without a name, but do not add it to the BOARD
2345  // yet since that would bypass duplicate netclass name checking within the BOARD.
2346  // store it temporarily in an unique_ptr until successfully inserted into the BOARD
2347  // just before returning.
2348  NETCLASSPTR nc = std::make_shared<NETCLASS>( wxEmptyString );
2349 
2350  while( ( line = READLINE( m_reader ) ) != NULL )
2351  {
2352  if( TESTLINE( "AddNet" ) ) // most frequent type of line
2353  {
2354  // e.g. "AddNet "V3.3D"\n"
2355  ReadDelimitedText( buf, line + SZ( "AddNet" ), sizeof(buf) );
2356  netname = FROM_UTF8( buf );
2357  nc->Add( netname );
2358  }
2359 
2360  else if( TESTLINE( "Clearance" ) )
2361  {
2362  BIU tmp = biuParse( line + SZ( "Clearance" ) );
2363  nc->SetClearance( tmp );
2364  }
2365 
2366  else if( TESTLINE( "TrackWidth" ) )
2367  {
2368  BIU tmp = biuParse( line + SZ( "TrackWidth" ) );
2369  nc->SetTrackWidth( tmp );
2370  }
2371 
2372  else if( TESTLINE( "ViaDia" ) )
2373  {
2374  BIU tmp = biuParse( line + SZ( "ViaDia" ) );
2375  nc->SetViaDiameter( tmp );
2376  }
2377 
2378  else if( TESTLINE( "ViaDrill" ) )
2379  {
2380  BIU tmp = biuParse( line + SZ( "ViaDrill" ) );
2381  nc->SetViaDrill( tmp );
2382  }
2383 
2384  else if( TESTLINE( "uViaDia" ) )
2385  {
2386  BIU tmp = biuParse( line + SZ( "uViaDia" ) );
2387  nc->SetuViaDiameter( tmp );
2388  }
2389 
2390  else if( TESTLINE( "uViaDrill" ) )
2391  {
2392  BIU tmp = biuParse( line + SZ( "uViaDrill" ) );
2393  nc->SetuViaDrill( tmp );
2394  }
2395 
2396  else if( TESTLINE( "Name" ) )
2397  {
2398  ReadDelimitedText( buf, line + SZ( "Name" ), sizeof(buf) );
2399  nc->SetName( FROM_UTF8( buf ) );
2400  }
2401 
2402  else if( TESTLINE( "Desc" ) )
2403  {
2404  ReadDelimitedText( buf, line + SZ( "Desc" ), sizeof(buf) );
2405  nc->SetDescription( FROM_UTF8( buf ) );
2406  }
2407 
2408  else if( TESTLINE( "$EndNCLASS" ) )
2409  {
2410  if( !m_board->GetDesignSettings().GetNetClasses().Add( nc ) )
2411  {
2412  // Must have been a name conflict, this is a bad board file.
2413  // User may have done a hand edit to the file.
2414 
2415  // unique_ptr will delete nc on this code path
2416 
2417  m_error.Printf( _( "duplicate NETCLASS name \"%s\"" ), nc->GetName().GetData() );
2419  }
2420 
2421  return; // preferred exit
2422  }
2423  }
2424 
2425  THROW_IO_ERROR( "Missing '$EndNCLASS'" );
2426 }
2427 
2428 
2430 {
2431  std::unique_ptr<ZONE> zc = std::make_unique<ZONE>( m_board );
2432 
2434  bool endContour = false;
2435  int holeIndex = -1; // -1 is the main outline; holeIndex >= 0 = hole index
2436  char buf[1024];
2437  char* line;
2438 
2439  while( ( line = READLINE( m_reader ) ) != NULL )
2440  {
2441  const char* data;
2442 
2443  if( TESTLINE( "ZCorner" ) ) // new corner of the zone outlines found
2444  {
2445  // e.g. "ZCorner 25650 49500 0"
2446  BIU x = biuParse( line + SZ( "ZCorner" ), &data );
2447  BIU y = biuParse( data, &data );
2448 
2449  if( endContour )
2450  {
2451  // the previous corner was the last corner of a contour.
2452  // so this corner is the first of a new hole
2453  endContour = false;
2454  zc->NewHole();
2455  holeIndex++;
2456  }
2457 
2458  zc->AppendCorner( wxPoint( x, y ), holeIndex );
2459 
2460  // Is this corner the end of current contour?
2461  // the next corner (if any) will be stored in a new contour (a hole)
2462  // intParse( data )returns 0 = usual corner, 1 = last corner of the current contour:
2463  endContour = intParse( data );
2464  }
2465 
2466  else if( TESTLINE( "ZInfo" ) ) // general info found
2467  {
2468  // e.g. 'ZInfo 68183921-93a5-49ac-91b0-49d05a0e1647 310 "COMMON"'
2469  char* uuid = strtok_r( (char*) line + SZ( "ZInfo" ), delims, (char**) &data );
2470  int netcode = intParse( data, &data );
2471 
2472  if( ReadDelimitedText( buf, data, sizeof(buf) ) > (int) sizeof(buf) )
2473  THROW_IO_ERROR( "ZInfo netname too long" );
2474 
2475  const_cast<KIID&>( zc->m_Uuid ) = KIID( uuid );
2476  // Init the net code only, not the netname, to be sure
2477  // the zone net name is the name read in file.
2478  // (When mismatch, the user will be prompted in DRC, to fix the actual name)
2479  zc->BOARD_CONNECTED_ITEM::SetNetCode( getNetCode( netcode ) );
2480  }
2481 
2482  else if( TESTLINE( "ZLayer" ) ) // layer found
2483  {
2484  LAYER_NUM layer_num = layerParse( line + SZ( "ZLayer" ) );
2485  zc->SetLayer( leg_layer2new( m_cu_count, layer_num ) );
2486  }
2487 
2488  else if( TESTLINE( "ZAux" ) ) // aux info found
2489  {
2490  // e.g. "ZAux 7 E"
2491  int ignore = intParse( line + SZ( "ZAux" ), &data );
2492  char* hopt = strtok_r( (char*) data, delims, (char**) &data );
2493 
2494  if( !hopt )
2495  {
2496  m_error.Printf( _( "Bad ZAux for CZONE_CONTAINER \"%s\"" ),
2497  zc->GetNetname().GetData() );
2499  }
2500 
2501  switch( *hopt ) // upper case required
2502  {
2503  case 'N': outline_hatch = ZONE_BORDER_DISPLAY_STYLE::NO_HATCH; break;
2504  case 'E': outline_hatch = ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE; break;
2505  case 'F': outline_hatch = ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_FULL; break;
2506  default:
2507  m_error.Printf( _( "Bad ZAux for CZONE_CONTAINER \"%s\"" ),
2508  zc->GetNetname().GetData() );
2510  }
2511 
2512  (void) ignore;
2513 
2514  // Set hatch mode later, after reading corner outline data
2515  }
2516 
2517  else if( TESTLINE( "ZSmoothing" ) )
2518  {
2519  // e.g. "ZSmoothing 0 0"
2520  int smoothing = intParse( line + SZ( "ZSmoothing" ), &data );
2521  BIU cornerRadius = biuParse( data );
2522 
2523  if( smoothing >= ZONE_SETTINGS::SMOOTHING_LAST || smoothing < 0 )
2524  {
2525  m_error.Printf( _( "Bad ZSmoothing for CZONE_CONTAINER \"%s\"" ),
2526  zc->GetNetname().GetData() );
2528  }
2529 
2530  zc->SetCornerSmoothingType( smoothing );
2531  zc->SetCornerRadius( cornerRadius );
2532  }
2533 
2534  else if( TESTLINE( "ZKeepout" ) )
2535  {
2536  char* token;
2537  zc->SetIsRuleArea( true );
2538  zc->SetDoNotAllowPads( false ); // Not supported in legacy
2539  zc->SetDoNotAllowFootprints( false ); // Not supported in legacy
2540 
2541  // e.g. "ZKeepout tracks N vias N pads Y"
2542  token = strtok_r( line + SZ( "ZKeepout" ), delims, (char**) &data );
2543 
2544  while( token )
2545  {
2546  if( !strcmp( token, "tracks" ) )
2547  {
2548  token = strtok_r( NULL, delims, (char**) &data );
2549  zc->SetDoNotAllowTracks( token && *token == 'N' );
2550  }
2551  else if( !strcmp( token, "vias" ) )
2552  {
2553  token = strtok_r( NULL, delims, (char**) &data );
2554  zc->SetDoNotAllowVias( token && *token == 'N' );
2555  }
2556  else if( !strcmp( token, "copperpour" ) )
2557  {
2558  token = strtok_r( NULL, delims, (char**) &data );
2559  zc->SetDoNotAllowCopperPour( token && *token == 'N' );
2560  }
2561 
2562  token = strtok_r( NULL, delims, (char**) &data );
2563  }
2564  }
2565 
2566  else if( TESTLINE( "ZOptions" ) )
2567  {
2568  // e.g. "ZOptions 0 32 F 200 200"
2569  int fillmode = intParse( line + SZ( "ZOptions" ), &data );
2570  static_cast<void>( intParse( data, &data ) ); // Ignored
2571  char fillstate = data[1]; // here e.g. " F"
2572  BIU thermalReliefGap = biuParse( data += 2 , &data ); // +=2 for " F"
2573  BIU thermalReliefCopperBridge = biuParse( data );
2574 
2575  if( fillmode)
2576  {
2577  // SEGMENT fill mode no longer supported. Make sure user is OK with converting them.
2579  {
2580  KIDIALOG dlg( nullptr,
2581  _( "The legacy segment fill mode is no longer supported.\n"
2582  "Convert zones to polygon fills?"),
2583  _( "Legacy Zone Warning" ),
2584  wxYES_NO | wxICON_WARNING );
2585 
2586  dlg.DoNotShowCheckbox( __FILE__, __LINE__ );
2587 
2588  if( dlg.ShowModal() == wxID_NO )
2589  THROW_IO_ERROR( wxT( "CANCEL" ) );
2590 
2591  m_showLegacyZoneWarning = false;
2592  }
2593 
2594  // User OK'd; switch to polygon mode
2595  zc->SetFillMode( ZONE_FILL_MODE::POLYGONS );
2596  m_board->SetModified();
2597  }
2598  else
2599  zc->SetFillMode( ZONE_FILL_MODE::POLYGONS );
2600 
2601  zc->SetIsFilled( fillstate == 'S' );
2602  zc->SetThermalReliefGap( thermalReliefGap );
2603  zc->SetThermalReliefSpokeWidth( thermalReliefCopperBridge );
2604  }
2605 
2606  else if( TESTLINE( "ZClearance" ) ) // Clearance and pad options info found
2607  {
2608  // e.g. "ZClearance 40 I"
2609  BIU clearance = biuParse( line + SZ( "ZClearance" ), &data );
2610  char* padoption = strtok_r( (char*) data, delims, (char**) &data ); // data: " I"
2611 
2612  ZONE_CONNECTION popt;
2613  switch( *padoption )
2614  {
2615  case 'I': popt = ZONE_CONNECTION::FULL; break;
2616  case 'T': popt = ZONE_CONNECTION::THERMAL; break;
2617  case 'H': popt = ZONE_CONNECTION::THT_THERMAL; break;
2618  case 'X': popt = ZONE_CONNECTION::NONE; break;
2619  default:
2620  m_error.Printf( _( "Bad ZClearance padoption for CZONE_CONTAINER \"%s\"" ),
2621  zc->GetNetname().GetData() );
2623  }
2624 
2625  zc->SetLocalClearance( clearance );
2626  zc->SetPadConnection( popt );
2627  }
2628 
2629  else if( TESTLINE( "ZMinThickness" ) )
2630  {
2631  BIU thickness = biuParse( line + SZ( "ZMinThickness" ) );
2632  zc->SetMinThickness( thickness );
2633  }
2634 
2635  else if( TESTLINE( "ZPriority" ) )
2636  {
2637  int priority = intParse( line + SZ( "ZPriority" ) );
2638  zc->SetPriority( priority );
2639  }
2640 
2641  else if( TESTLINE( "$POLYSCORNERS" ) )
2642  {
2643  // Read the PolysList (polygons that are the solid areas in the filled zone)
2644  SHAPE_POLY_SET polysList;
2645 
2646  bool makeNewOutline = true;
2647 
2648  while( ( line = READLINE( m_reader ) ) != NULL )
2649  {
2650  if( TESTLINE( "$endPOLYSCORNERS" ) )
2651  break;
2652 
2653  // e.g. "39610 43440 0 0"
2654  BIU x = biuParse( line, &data );
2655  BIU y = biuParse( data, &data );
2656 
2657  if( makeNewOutline )
2658  polysList.NewOutline();
2659 
2660  polysList.Append( x, y );
2661 
2662  bool end_contour = intParse( data, &data ); // end_countour was a bool when file saved, so '0' or '1' here
2663  intParse( data ); // skip corner utility flag
2664 
2665  makeNewOutline = end_contour;
2666  }
2667 
2668  zc->SetFilledPolysList( zc->GetLayer(), polysList );
2669  }
2670 
2671  else if( TESTLINE( "$FILLSEGMENTS" ) )
2672  {
2673  while( ( line = READLINE( m_reader ) ) != NULL )
2674  {
2675  if( TESTLINE( "$endFILLSEGMENTS" ) )
2676  break;
2677 
2678  // e.g. ""%d %d %d %d\n"
2679  BIU sx = biuParse( line, &data );
2680  BIU sy = biuParse( data, &data );
2681  BIU ex = biuParse( data, &data );
2682  BIU ey = biuParse( data );
2683 
2684  zc->FillSegments( zc->GetLayer() )
2685  .push_back( SEG( VECTOR2I( sx, sy ), VECTOR2I( ex, ey ) ) );
2686  }
2687  }
2688 
2689  else if( TESTLINE( "$endCZONE_OUTLINE" ) )
2690  {
2691  // Ensure keepout does not have a net
2692  // (which have no sense for a keepout zone)
2693  if( zc->GetIsRuleArea() )
2694  zc->SetNetCode( NETINFO_LIST::UNCONNECTED );
2695 
2696  // should always occur, but who knows, a zone without two corners
2697  // is no zone at all, it's a spot?
2698 
2699  if( zc->GetNumCorners() > 2 )
2700  {
2701  if( !zc->IsOnCopperLayer() )
2702  {
2703  zc->SetFillMode( ZONE_FILL_MODE::POLYGONS );
2704  zc->SetNetCode( NETINFO_LIST::UNCONNECTED );
2705  }
2706 
2707  // HatchBorder here, after outlines corners are read
2708  // Set hatch here, after outlines corners are read
2709  zc->SetBorderDisplayStyle( outline_hatch, ZONE::GetDefaultHatchPitch(), true );
2710 
2711  m_board->Add( zc.release() );
2712  }
2713 
2714  return; // preferred exit
2715  }
2716  }
2717 
2718  THROW_IO_ERROR( "Missing '$endCZONE_OUTLINE'" );
2719 }
2720 
2721 
2723 {
2724  std::unique_ptr<ALIGNED_DIMENSION> dim = std::make_unique<ALIGNED_DIMENSION>( m_board );
2725 
2726  char* line;
2727 
2728  while( ( line = READLINE( m_reader ) ) != NULL )
2729  {
2730  const char* data;
2731 
2732  if( TESTLINE( "$endCOTATION" ) )
2733  {
2734  m_board->Add( dim.release(), ADD_MODE::APPEND );
2735  return; // preferred exit
2736  }
2737 
2738  else if( TESTLINE( "Va" ) )
2739  {
2740  BIU value = biuParse( line + SZ( "Va" ) );
2741  // unused; dimension value is calculated from coordinates
2742  ( void )value;
2743  }
2744 
2745  else if( TESTLINE( "Ge" ) )
2746  {
2747  // e.g. "Ge 1 21 68183921-93a5-49ac-91b0-49d05a0e1647\r\n"
2748  int shape = intParse( line + SZ( "De" ), (const char**) &data );
2749  LAYER_NUM layer_num = layerParse( data, &data );
2750  char* uuid = strtok_r( (char*) data, delims, (char**) &data );
2751 
2752  dim->SetLayer( leg_layer2new( m_cu_count, layer_num ) );
2753  const_cast<KIID&>( dim->m_Uuid ) = KIID( uuid );
2754  // not used
2755  ( void )shape;
2756  }
2757 
2758  else if( TESTLINE( "Te" ) )
2759  {
2760  char buf[2048];
2761 
2762  ReadDelimitedText( buf, line + SZ( "Te" ), sizeof(buf) );
2763  dim->SetText( FROM_UTF8( buf ) );
2764  }
2765 
2766  else if( TESTLINE( "Po" ) )
2767  {
2768  BIU pos_x = biuParse( line + SZ( "Po" ), &data );
2769  BIU pos_y = biuParse( data, &data );
2770  BIU width = biuParse( data, &data );
2771  BIU height = biuParse( data, &data );
2772  BIU thickn = biuParse( data, &data );
2773  double orient = degParse( data, &data );
2774  char* mirror = strtok_r( (char*) data, delims, (char**) &data );
2775 
2776  // This sets both DIMENSION's position and internal m_Text's.
2777  // @todo: But why do we even know about internal m_Text?
2778  dim->SetPosition( wxPoint( pos_x, pos_y ) );
2779  dim->SetTextSize( wxSize( width, height ) );
2780 
2781  dim->Text().SetMirrored( mirror && *mirror == '0' );
2782  dim->Text().SetTextThickness( thickn );
2783  dim->Text().SetTextAngle( orient );
2784  }
2785 
2786  else if( TESTLINE( "Sb" ) )
2787  {
2788  int ignore = biuParse( line + SZ( "Sb" ), &data );
2789  BIU crossBarOx = biuParse( data, &data );
2790  BIU crossBarOy = biuParse( data, &data );
2791  BIU crossBarFx = biuParse( data, &data );
2792  BIU crossBarFy = biuParse( data, &data );
2793  BIU width = biuParse( data );
2794 
2795  dim->SetLineThickness( width );
2796  dim->UpdateHeight( wxPoint( crossBarFx, crossBarFy ),
2797  wxPoint( crossBarOx, crossBarOy ) );
2798  (void) ignore;
2799  }
2800 
2801  else if( TESTLINE( "Sd" ) )
2802  {
2803  int ignore = intParse( line + SZ( "Sd" ), &data );
2804  BIU featureLineDOx = biuParse( data, &data );
2805  BIU featureLineDOy = biuParse( data, &data );
2806  BIU featureLineDFx = biuParse( data, &data );
2807  BIU featureLineDFy = biuParse( data );
2808 
2809  dim->SetStart( wxPoint( featureLineDOx, featureLineDOy ) );
2810  ( void )ignore;
2811  ( void )featureLineDFx;
2812  ( void )featureLineDFy;
2813  }
2814 
2815  else if( TESTLINE( "Sg" ) )
2816  {
2817  int ignore = intParse( line + SZ( "Sg" ), &data );
2818  BIU featureLineGOx = biuParse( data, &data );
2819  BIU featureLineGOy = biuParse( data, &data );
2820  BIU featureLineGFx = biuParse( data, &data );
2821  BIU featureLineGFy = biuParse( data );
2822 
2823  (void) ignore;
2824  dim->SetEnd( wxPoint( featureLineGOx, featureLineGOy ) );
2825  ( void )ignore;
2826  ( void )featureLineGFx;
2827  ( void )featureLineGFy;
2828  }
2829 
2830  // Arrow: no longer imported
2831  else if( TESTLINE( "S1" ) )
2832  {
2833  int ignore = intParse( line + SZ( "S1" ), &data );
2834  biuParse( data, &data ); // skipping excessive data
2835  biuParse( data, &data ); // skipping excessive data
2836  biuParse( data, &data );
2837  biuParse( data );
2838 
2839  (void) ignore;
2840  }
2841 
2842  // Arrow: no longer imported
2843  else if( TESTLINE( "S2" ) )
2844  {
2845  int ignore = intParse( line + SZ( "S2" ), &data );
2846  biuParse( data, &data ); // skipping excessive data
2847  biuParse( data, &data ); // skipping excessive data
2848  biuParse( data, &data );
2849  biuParse( data, &data );
2850 
2851  (void) ignore;
2852  }
2853 
2854  // Arrow: no longer imported
2855  else if( TESTLINE( "S3" ) )
2856  {
2857  int ignore = intParse( line + SZ( "S3" ), &data );
2858  biuParse( data, &data ); // skipping excessive data
2859  biuParse( data, &data ); // skipping excessive data
2860  biuParse( data, &data );
2861  biuParse( data, &data );
2862 
2863  (void) ignore;
2864  }
2865 
2866  // Arrow: no longer imported
2867  else if( TESTLINE( "S4" ) )
2868  {
2869  int ignore = intParse( line + SZ( "S4" ), &data );
2870  biuParse( data, &data ); // skipping excessive data
2871  biuParse( data, &data ); // skipping excessive data
2872  biuParse( data, &data );
2873  biuParse( data, &data );
2874 
2875  (void) ignore;
2876  }
2877  }
2878 
2879  THROW_IO_ERROR( "Missing '$endCOTATION'" );
2880 }
2881 
2882 
2884 {
2885  char* line;
2886 
2887  while( ( line = READLINE( m_reader ) ) != NULL )
2888  {
2889  const char* data;
2890 
2891  if( TESTLINE( "$EndPCB_TARGET" ) || TESTLINE( "$EndMIREPCB" ) )
2892  {
2893  return; // preferred exit
2894  }
2895 
2896  else if( TESTLINE( "Po" ) )
2897  {
2898  int shape = intParse( line + SZ( "Po" ), &data );
2899  LAYER_NUM layer_num = layerParse( data, &data );
2900  BIU pos_x = biuParse( data, &data );
2901  BIU pos_y = biuParse( data, &data );
2902  BIU size = biuParse( data, &data );
2903  BIU width = biuParse( data, &data );
2904  char* uuid = strtok_r( (char*) data, delims, (char**) &data );
2905 
2906  if( layer_num < FIRST_NON_COPPER_LAYER )
2907  layer_num = FIRST_NON_COPPER_LAYER;
2908 
2909  else if( layer_num > LAST_NON_COPPER_LAYER )
2910  layer_num = LAST_NON_COPPER_LAYER;
2911 
2912  PCB_TARGET* t = new PCB_TARGET( m_board, shape, leg_layer2new( m_cu_count, layer_num ),
2913  wxPoint( pos_x, pos_y ), size, width );
2914  m_board->Add( t, ADD_MODE::APPEND );
2915 
2916  const_cast<KIID&>( t->m_Uuid ) = KIID( uuid );
2917  }
2918  }
2919 
2920  THROW_IO_ERROR( "Missing '$EndDIMENSION'" );
2921 }
2922 
2923 
2924 BIU LEGACY_PLUGIN::biuParse( const char* aValue, const char** nptrptr )
2925 {
2926  char* nptr;
2927 
2928  errno = 0;
2929 
2930  double fval = strtod( aValue, &nptr );
2931 
2932  if( errno )
2933  {
2934  m_error.Printf( _( "invalid float number in file: \"%s\"\nline: %d, offset: %d" ),
2935  m_reader->GetSource().GetData(),
2936  m_reader->LineNumber(), aValue - m_reader->Line() + 1 );
2937 
2939  }
2940 
2941  if( aValue == nptr )
2942  {
2943  m_error.Printf( _( "missing float number in file: \"%s\"\nline: %d, offset: %d" ),
2944  m_reader->GetSource().GetData(),
2945  m_reader->LineNumber(), aValue - m_reader->Line() + 1 );
2946 
2948  }
2949 
2950  if( nptrptr )
2951  *nptrptr = nptr;
2952 
2953  fval *= diskToBiu;
2954 
2955  // fval is up into the whole number realm here, and should be bounded
2956  // within INT_MIN to INT_MAX since BIU's are nanometers.
2957  return KiROUND( fval );
2958 }
2959 
2960 
2961 double LEGACY_PLUGIN::degParse( const char* aValue, const char** nptrptr )
2962 {
2963  char* nptr;
2964 
2965  errno = 0;
2966 
2967  double fval = strtod( aValue, &nptr );
2968 
2969  if( errno )
2970  {
2971  m_error.Printf( _( "invalid float number in file: \"%s\"\nline: %d, offset: %d" ),
2972  m_reader->GetSource().GetData(), m_reader->LineNumber(), aValue - m_reader->Line() + 1 );
2973 
2975  }
2976 
2977  if( aValue == nptr )
2978  {
2979  m_error.Printf( _( "missing float number in file: \"%s\"\nline: %d, offset: %d" ),
2980  m_reader->GetSource().GetData(), m_reader->LineNumber(), aValue - m_reader->Line() + 1 );
2981 
2983  }
2984 
2985  if( nptrptr )
2986  *nptrptr = nptr;
2987 
2988  return fval;
2989 }
2990 
2991 
2992 void LEGACY_PLUGIN::init( const PROPERTIES* aProperties )
2993 {
2995  m_cu_count = 16;
2996  m_board = NULL;
2997  m_showLegacyZoneWarning = true;
2998  m_props = aProperties;
2999 
3000  // conversion factor for saving RAM BIUs to KICAD legacy file format.
3001  biuToDisk = 1.0/IU_PER_MM; // BIUs are nanometers & file is mm
3002 
3003  // Conversion factor for loading KICAD legacy file format into BIUs in RAM
3004  // Start by assuming the *.brd file is in deci-mils.
3005  // If we see "Units mm" in the $GENERAL section, set diskToBiu to 1000000.0
3006  // then, during the file loading process, to start a conversion from
3007  // mm to nanometers. The deci-mil legacy files have no such "Units" marker
3008  // so we must assume the file is in deci-mils until told otherwise.
3009 
3010  diskToBiu = IU_PER_MILS / 10; // BIUs are nanometers
3011 }
3012 
3013 
3014 void LEGACY_PLUGIN::SaveFP3DModels( const FOOTPRINT* aFootprint ) const
3015 {
3016  auto sM = aFootprint->Models().begin();
3017  auto eM = aFootprint->Models().end();
3018 
3019  while( sM != eM )
3020  {
3021  if( sM->m_Filename.empty() )
3022  {
3023  ++sM;
3024  continue;
3025  }
3026 
3027  fprintf( m_fp, "$SHAPE3D\n" );
3028 
3029  fprintf( m_fp, "Na %s\n", EscapedUTF8( sM->m_Filename ).c_str() );
3030 
3031  fprintf(m_fp,
3032 #if defined(DEBUG)
3033  // use old formats for testing, just to verify compatibility
3034  // using "diff", then switch to more concise form for release builds.
3035  "Sc %lf %lf %lf\n",
3036 #else
3037  "Sc %.10g %.10g %.10g\n",
3038 #endif
3039  sM->m_Scale.x,
3040  sM->m_Scale.y,
3041  sM->m_Scale.z );
3042 
3043  fprintf(m_fp,
3044 #if defined(DEBUG)
3045  "Of %lf %lf %lf\n",
3046 #else
3047  "Of %.10g %.10g %.10g\n",
3048 #endif
3049  sM->m_Offset.x,
3050  sM->m_Offset.y,
3051  sM->m_Offset.z );
3052 
3053  fprintf(m_fp,
3054 #if defined(DEBUG)
3055  "Ro %lf %lf %lf\n",
3056 #else
3057  "Ro %.10g %.10g %.10g\n",
3058 #endif
3059  sM->m_Rotation.x,
3060  sM->m_Rotation.y,
3061  sM->m_Rotation.z );
3062 
3063  fprintf( m_fp, "$EndSHAPE3D\n" );
3064 
3065  ++sM;
3066  }
3067 }
3068 
3069 
3070 //-----<FOOTPRINT LIBRARY FUNCTIONS>--------------------------------------------
3071 
3072 /*
3073 
3074  The legacy file format is being obsoleted and this code will have a short
3075  lifetime, so it only needs to be good enough for a short duration of time.
3076  Caching all the MODULEs is a bit memory intensive, but it is a considerably
3077  faster way of fulfilling the API contract. Otherwise, without the cache, you
3078  would have to re-read the file when searching for any FOOTPRINT, and this would
3079  be very problematic filling a FOOTPRINT_LIST via this PLUGIN API. If memory
3080  becomes a concern, consider the cache lifetime policy, which determines the
3081  time that a LP_CACHE is in RAM. Note PLUGIN lifetime also plays a role in
3082  cache lifetime.
3083 
3084 */
3085 
3086 
3087 #include <boost/ptr_container/ptr_map.hpp>
3088 #include <wx/filename.h>
3089 
3090 typedef boost::ptr_map< std::string, FOOTPRINT > FOOTPRINT_MAP;
3091 
3092 
3099 struct LP_CACHE
3100 {
3101  LEGACY_PLUGIN* m_owner; // my owner, I need its LEGACY_PLUGIN::loadFOOTPRINT()
3102  wxString m_lib_path;
3103  FOOTPRINT_MAP m_footprints; // map or tuple of footprint_name vs. FOOTPRINT*
3105 
3106  bool m_cache_dirty; // Stored separately because it's expensive to check
3107  // m_cache_timestamp against all the files.
3108  long long m_cache_timestamp; // A hash of the timestamps for all the footprint
3109  // files.
3110 
3111  LP_CACHE( LEGACY_PLUGIN* aOwner, const wxString& aLibraryPath );
3112 
3113  // Most all functions in this class throw IO_ERROR exceptions. There are no
3114  // error codes nor user interface calls from here, nor in any PLUGIN.
3115  // Catch these exceptions higher up please.
3116 
3117  void Load();
3118 
3119  void ReadAndVerifyHeader( LINE_READER* aReader );
3120 
3121  void SkipIndex( LINE_READER* aReader );
3122 
3123  void LoadModules( LINE_READER* aReader );
3124 
3125  bool IsModified();
3126  static long long GetTimestamp( const wxString& aLibPath );
3127 };
3128 
3129 
3130 LP_CACHE::LP_CACHE( LEGACY_PLUGIN* aOwner, const wxString& aLibraryPath ) :
3131  m_owner( aOwner ),
3132  m_lib_path( aLibraryPath ),
3133  m_writable( true ),
3134  m_cache_dirty( true ),
3135  m_cache_timestamp( 0 )
3136 {
3137 }
3138 
3139 
3141 {
3143 
3144  return m_cache_dirty;
3145 }
3146 
3147 
3148 long long LP_CACHE::GetTimestamp( const wxString& aLibPath )
3149 {
3150  return wxFileName( aLibPath ).GetModificationTime().GetValue().GetValue();
3151 }
3152 
3153 
3155 {
3156  m_cache_dirty = false;
3157 
3158  FILE_LINE_READER reader( m_lib_path );
3159 
3160  ReadAndVerifyHeader( &reader );
3161  SkipIndex( &reader );
3162  LoadModules( &reader );
3163 
3164  // Remember the file modification time of library file when the
3165  // cache snapshot was made, so that in a networked environment we will
3166  // reload the cache as needed.
3168 }
3169 
3170 
3172 {
3173  char* line = aReader->ReadLine();
3174  char* data;
3175 
3176  if( !line )
3177  THROW_IO_ERROR( wxString::Format( _( "File '%s' is empty." ), m_lib_path ) );
3178 
3179  if( !TESTLINE( "PCBNEW-LibModule-V1" ) )
3180  THROW_IO_ERROR( wxString::Format( _( "File '%s' is not a legacy library." ), m_lib_path ) );
3181 
3182  while( ( line = aReader->ReadLine() ) != NULL )
3183  {
3184  if( TESTLINE( "Units" ) )
3185  {
3186  const char* units = strtok_r( line + SZ( "Units" ), delims, &data );
3187 
3188  if( !strcmp( units, "mm" ) )
3190 
3191  }
3192  else if( TESTLINE( "$INDEX" ) )
3193  return;
3194  }
3195 }
3196 
3197 
3199 {
3200  // Some broken INDEX sections have more than one section, due to prior bugs.
3201  // So we must read the next line after $EndINDEX tag,
3202  // to see if this is not a new $INDEX tag.
3203  bool exit = false;
3204  char* line = aReader->Line();
3205 
3206  do
3207  {
3208  if( TESTLINE( "$INDEX" ) )
3209  {
3210  exit = false;
3211 
3212  while( ( line = aReader->ReadLine() ) != NULL )
3213  {
3214  if( TESTLINE( "$EndINDEX" ) )
3215  {
3216  exit = true;
3217  break;
3218  }
3219  }
3220  }
3221  else if( exit )
3222  break;
3223  } while( ( line = aReader->ReadLine() ) != NULL );
3224 }
3225 
3226 
3228 {
3229  m_owner->SetReader( aReader );
3230 
3231  char* line = aReader->Line();
3232 
3233  do
3234  {
3235  // test first for the $MODULE, even before reading because of INDEX bug.
3236  if( TESTLINE( "$MODULE" ) )
3237  {
3238  std::unique_ptr<FOOTPRINT> fp_ptr = std::make_unique<FOOTPRINT>( m_owner->m_board );
3239 
3240  std::string footprintName = StrPurge( line + SZ( "$MODULE" ) );
3241 
3242  // The footprint names in legacy libraries can contain the '/' and ':'
3243  // characters which will cause the LIB_ID parser to choke.
3244  ReplaceIllegalFileNameChars( &footprintName );
3245 
3246  // set the footprint name first thing, so exceptions can use name.
3247  fp_ptr->SetFPID( LIB_ID( wxEmptyString, footprintName ) );
3248 
3249  m_owner->loadFOOTPRINT( fp_ptr.get());
3250 
3251  FOOTPRINT* fp = fp_ptr.release(); // exceptions after this are not expected.
3252 
3253  // Not sure why this is asserting on debug builds. The debugger shows the
3254  // strings are the same. If it's not really needed maybe it can be removed.
3255 // wxASSERT( footprintName == m->GetFPID().GetLibItemName() );
3256 
3257  /*
3258 
3259  There was a bug in old legacy library management code
3260  (pre-LEGACY_PLUGIN) which was introducing duplicate footprint names
3261  in legacy libraries without notification. To best recover from such
3262  bad libraries, and use them to their fullest, there are a few
3263  strategies that could be used. (Note: footprints must have unique
3264  names to be accepted into this cache.) The strategy used here is to
3265  append a differentiating version counter to the end of the name as:
3266  _v2, _v3, etc.
3267 
3268  */
3269 
3270  FOOTPRINT_MAP::const_iterator it = m_footprints.find( footprintName );
3271 
3272  if( it == m_footprints.end() ) // footprintName is not present in cache yet.
3273  {
3274  if( !m_footprints.insert( footprintName, fp ).second )
3275  {
3276  wxFAIL_MSG( "error doing cache insert using guaranteed unique name" );
3277  }
3278  }
3279 
3280  // Bad library has a duplicate of this footprintName, generate a
3281  // unique footprint name and load it anyway.
3282  else
3283  {
3284  bool nameOK = false;
3285  int version = 2;
3286  char buf[48];
3287 
3288  while( !nameOK )
3289  {
3290  std::string newName = footprintName;
3291 
3292  newName += "_v";
3293  sprintf( buf, "%d", version++ );
3294  newName += buf;
3295 
3296  it = m_footprints.find( newName );
3297 
3298  if( it == m_footprints.end() )
3299  {
3300  nameOK = true;
3301 
3302  fp->SetFPID( LIB_ID( wxEmptyString, newName ) );
3303 
3304  if( !m_footprints.insert( newName, fp ).second )
3305  {
3306  wxFAIL_MSG( "error doing cache insert using guaranteed unique name" );
3307  }
3308  }
3309  }
3310  }
3311  }
3312 
3313  } while( ( line = aReader->ReadLine() ) != NULL );
3314 }
3315 
3316 
3317 long long LEGACY_PLUGIN::GetLibraryTimestamp( const wxString& aLibraryPath ) const
3318 {
3319  return LP_CACHE::GetTimestamp( aLibraryPath );
3320 }
3321 
3322 
3323 void LEGACY_PLUGIN::cacheLib( const wxString& aLibraryPath )
3324 {
3325  if( !m_cache || m_cache->m_lib_path != aLibraryPath || m_cache->IsModified() )
3326  {
3327  // a spectacular episode in memory management:
3328  delete m_cache;
3329  m_cache = new LP_CACHE( this, aLibraryPath );
3330  m_cache->Load();
3331  }
3332 }
3333 
3334 
3335 void LEGACY_PLUGIN::FootprintEnumerate( wxArrayString& aFootprintNames, const wxString& aLibPath,
3336  bool aBestEfforts, const PROPERTIES* aProperties )
3337 {
3338  LOCALE_IO toggle; // toggles on, then off, the C locale.
3339  wxString errorMsg;
3340 
3341  init( aProperties );
3342 
3343  try
3344  {
3345  cacheLib( aLibPath );
3346  }
3347  catch( const IO_ERROR& ioe )
3348  {
3349  errorMsg = ioe.What();
3350  }
3351 
3352  // Some of the files may have been parsed correctly so we want to add the valid files to
3353  // the library.
3354 
3355  for( const auto& footprint : m_cache->m_footprints )
3356  aFootprintNames.Add( FROM_UTF8( footprint.first.c_str() ) );
3357 
3358  if( !errorMsg.IsEmpty() && !aBestEfforts )
3359  THROW_IO_ERROR( errorMsg );
3360 }
3361 
3362 
3363 FOOTPRINT* LEGACY_PLUGIN::FootprintLoad( const wxString& aLibraryPath,
3364  const wxString& aFootprintName,
3365  const PROPERTIES* aProperties )
3366 {
3367  LOCALE_IO toggle; // toggles on, then off, the C locale.
3368 
3369  init( aProperties );
3370 
3371  cacheLib( aLibraryPath );
3372 
3373  const FOOTPRINT_MAP& footprints = m_cache->m_footprints;
3374  FOOTPRINT_MAP::const_iterator it = footprints.find( TO_UTF8( aFootprintName ) );
3375 
3376  if( it == footprints.end() )
3377  {
3378  /*
3379  THROW_IO_ERROR( wxString::Format( _( "No \"%s\" footprint in library \"%s\"" ),
3380  aFootprintName.GetData(), aLibraryPath.GetData() ) );
3381  */
3382 
3383  return NULL;
3384  }
3385 
3386  // Return copy of already loaded FOOTPRINT
3387  return (FOOTPRINT*) it->second->Duplicate();
3388 }
3389 
3390 
3391 bool LEGACY_PLUGIN::FootprintLibDelete( const wxString& aLibraryPath, const PROPERTIES* aProperties )
3392 {
3393  wxFileName fn = aLibraryPath;
3394 
3395  if( !fn.FileExists() )
3396  return false;
3397 
3398  // Some of the more elaborate wxRemoveFile() crap puts up its own wxLog dialog
3399  // we don't want that. we want bare metal portability with no UI here.
3400  if( wxRemove( aLibraryPath ) )
3401  {
3403  _( "library \"%s\" cannot be deleted" ),
3404  aLibraryPath.GetData() ) );
3405  }
3406 
3407  if( m_cache && m_cache->m_lib_path == aLibraryPath )
3408  {
3409  delete m_cache;
3410  m_cache = 0;
3411  }
3412 
3413  return true;
3414 }
3415 
3416 
3417 bool LEGACY_PLUGIN::IsFootprintLibWritable( const wxString& aLibraryPath )
3418 {
3419 #if 0 // no support for 32 Cu layers in legacy format
3420  return false;
3421 #else
3422  LOCALE_IO toggle;
3423 
3424  init( NULL );
3425 
3426  cacheLib( aLibraryPath );
3427 
3428  return m_cache->m_writable;
3429 #endif
3430 }
3431 
3432 
3434  m_cu_count( 16 ), // for FootprintLoad()
3435  m_board( nullptr ),
3436  m_props( nullptr ),
3437  m_reader( nullptr ),
3438  m_fp( nullptr ),
3439  m_cache( nullptr ),
3440  m_mapping( new NETINFO_MAPPING() )
3441 {
3442  init( NULL );
3443 }
3444 
3445 
3447 {
3448  delete m_cache;
3449  delete m_mapping;
3450 }
void SetMirrored(bool isMirrored)
Definition: eda_text.h:194
const ZONE_SETTINGS & GetZoneSettings() const override
Fetch the zone settings for this container.
Definition: board.h:564
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:750
long long GetLibraryTimestamp(const wxString &aLibraryPath) const override
Generate a timestamp representing all the files in the library (including the library directory).
CITER next(CITER it)
Definition: ptree.cpp:126
NETINFO_ITEM * FindNet(int aNetcode) const
Search for a net with the given netcode.
Definition: board.cpp:1234
ZONE_CONNECTION
How pads are covered by copper in zone.
Definition: zones.h:41
void SetFileFormatVersionAtLoad(int aVersion)
Definition: board.h:336
void SetModified()
Definition: eda_item.cpp:79
Definition: track.h:354
#define SILKSCREEN_N_BACK
LINE_READER is an abstract class from which implementation specific LINE_READERs may be derived to re...
Definition: richio.h:82
const UTF8 & GetLibItemName() const
Definition: lib_id.h:114
void DoNotShowCheckbox(wxString file, int line)
Shows the 'do not show again' checkbox
Definition: confirm.cpp:54
EDA_TEXT_HJUSTIFY_T
Definition: eda_text.h:61
char * Line() const
Function Line returns a pointer to the last line that was read in.
Definition: richio.h:140
unsigned LAYER_MSK
wxPoint m_GridOrigin
origin for grid offsets
std::list< FP_3DMODEL > & Models()
Definition: footprint.h:196
bool m_LegacyDesignSettingsLoaded
True if the legacy board design settings were loaded from a file.
Definition: board.h:316
static wxString FROM_UTF8(const char *cstring)
function FROM_UTF8 converts a UTF8 encoded C string to a wxString for all wxWidgets build modes.
Definition: macros.h:109
void cacheLib(const wxString &aLibraryPath)
we only cache one footprint library for now, this determines which one.
char * StrPurge(char *text)
Remove leading and training spaces, tabs and end of line chars in text.
Definition: string.cpp:365
virtual const wxString & GetSource() const
Function GetSource returns the name of the source of the lines in an abstract sense.
Definition: richio.h:131
void SetZoneConnection(ZONE_CONNECTION aType)
Definition: footprint.h:240
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition: locale_io.h:40
PAD_ATTR_T
Enum PAD_ATTR_T is the set of pad shapes, used with PAD::{Set,Get}Attribute() The double name is for ...
Definition: pad_shapes.h:78
Helper class to create more flexible dialogs, including 'do not show again' checkbox handling.
Definition: confirm.h:44
LP_CACHE(LEGACY_PLUGIN *aOwner, const wxString &aLibraryPath)
virtual void SetLayer(PCB_LAYER_ID aLayer)
Function SetLayer sets the layer this item is on.
Definition: board_item.h:206
This file is part of the common library.
PCB_PLOT_PARAMS_PARSER is the parser class for PCB_PLOT_PARAMS.
bool m_showLegacyZoneWarning
void SetEnd(const wxPoint &aEnd)
Definition: track.h:112
std::vector< int > m_TrackWidthList
static int intParse(const char *next, const char **out=NULL)
Function intParse parses an ASCII integer string with possible leading whitespace into an integer and...
void SetRevision(const wxString &aRevision)
Definition: title_block.h:84
void SetPath(const KIID_PATH &aPath)
Definition: footprint.h:218
void SetTitleBlock(const TITLE_BLOCK &aTitleBlock)
Definition: board.h:581
#define LAYER_N_BACK
static constexpr double IU_PER_MM
Mock up a conversion function.
void SetTextAngle(double aAngle) override
Definition: pcb_text.cpp:101
void SetIsPlaced(bool isPlaced)
Definition: footprint.h:310
void SetTextAngle(double aAngle) override
Definition: fp_text.cpp:69
LEGACY_PLUGIN * m_owner
void SetPageSettings(const PAGE_INFO &aPageSettings)
Definition: board.h:575
the 3d code uses this value
Definition: typeinfo.h:80
void SetItalic(bool isItalic)
Definition: eda_text.h:185
#define COMMENT_N
Smd pad, appears on the solder paste layer (default)
Definition: pad_shapes.h:81
#define EDGE_N
#define ADHESIVE_N_FRONT
void SetTextPos(const wxPoint &aPoint)
Definition: eda_text.h:253
static const char delims[]
polygon (not yet used for tracks, but could be in microwave apps)
Definition: board_item.h:56
void SetVisible(bool aVisible)
Definition: eda_text.h:191
bool SetType(const wxString &aStandardPageDescriptionName, bool aIsPortrait=false)
Function SetType sets the name of the page type and also the sizes and margins commonly associated wi...
Definition: page_info.cpp:119
double GetOrientation() const
Definition: footprint.h:204
void SetCopperLayerCount(int aCount)
Definition: board.cpp:431
static LAYER_NUM layerParse(const char *next, const char **out=NULL)
Function layerParse Like intParse but returns a LAYER_NUM.
friend struct LP_CACHE
Definition: legacy_plugin.h:63
bool SetNetCode(int aNetCode, bool aNoAssert)
Sets net using a net code.
void SetVisibleElements(const GAL_SET &aMask)
A proxy function that calls the correspondent function in m_BoardSettings changes the bit-mask of vis...
Definition: board.cpp:470
void SetDrawCoord()
Set absolute coordinates.
Definition: fp_text.cpp:198
void SkipIndex(LINE_READER *aReader)
BOARD * Load(const wxString &aFileName, BOARD *aAppendToMe, const PROPERTIES *aProperties=NULL) override
Function Load loads information from some input file format that this PLUGIN implementation knows abo...
usual segment : line with rounded ends
Definition: board_item.h:52
bool FootprintLibDelete(const wxString &aLibraryPath, const PROPERTIES *aProperties=NULL) override
Function FootprintLibDelete deletes an existing footprint library and returns true,...
bool ReplaceIllegalFileNameChars(std::string *aName, int aReplaceChar)
Checks aName for illegal file name characters.
Definition: string.cpp:735
void SetDate(const wxString &aDate)
Function SetDate sets the date field, and defaults to the current time and date.
Definition: title_block.h:74
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.h:559
bool IsModified()
void SetLocalSolderPasteMarginRatio(double aRatio)
Definition: footprint.h:238
Arcs (with rounded ends)
Definition: board_item.h:54
static const wxChar Custom[]
"User" defined page type
Definition: page_info.h:79
#define READLINE(rdr)
void SetTextSize(const wxSize &aNewSize)
Definition: eda_text.h:244
void SetSize(const wxSize &aSize)
Definition: pad.h:225
static PCB_LAYER_ID leg_layer2new(int cu_count, LAYER_NUM aLayerNum)
bool SetLayerType(PCB_LAYER_ID aLayer, LAYER_T aLayerType)
Change the type of the layer given by aLayer.
Definition: board.cpp:380
void RotatePoint(int *pX, int *pY, double angle)
Definition: trigo.cpp:208
void SetThermalGap(int aGap)
Definition: footprint.h:246
like PAD_PTH, but not plated mechanical use only, no connection allowed
Definition: pad_shapes.h:85
void SetTrackWidth(int aWidth)
Definition: netclass.h:164
#define SOLDERMASK_N_FRONT
A logical library item identifier and consists of various portions much like a URI.
Definition: lib_id.h:51
wxString m_field
reused to stuff FOOTPRINT fields.
PROPERTIES is a name/value tuple with unique names and optional values.
Definition: properties.h:34
void SetType(TEXT_TYPE aType)
Definition: fp_text.h:138
void SetBoardThickness(int aThickness)
VECTOR2< int > VECTOR2I
Definition: vector2d.h:594
TITLE_BLOCK holds the information shown in the lower right corner of a plot, printout,...
Definition: title_block.h:40
PAD_DRILL_SHAPE_T
Enum PAD_DRILL_SHAPE_T is the set of pad drill shapes, used with PAD::{Set,Get}DrillShape()
Definition: pad_shapes.h:66
VECTOR3D m_Offset
3D model offset (mm)
Definition: footprint.h:96
void FootprintEnumerate(wxArrayString &aFootprintNames, const wxString &aLibraryPath, bool aBestEfforts, const PROPERTIES *aProperties=NULL) override
Return a list of footprint names contained within the library at aLibraryPath.
int m_loading_format_version
which BOARD_FORMAT_VERSION am I Load()ing?
#define ECO2_N
void SetViaDrill(int aSize)
Definition: netclass.h:172
PAD_SHAPE_T
Enum PAD_SHAPE_T is the set of pad shapes, used with PAD::{Set,Get}Shape()
Definition: pad_shapes.h:33
void SetAttributes(int aAttributes)
Definition: footprint.h:250
#define SOLDERMASK_N_BACK
void SetReader(LINE_READER *aReader)
class TRACK, a track segment (segment on a copper layer)
Definition: typeinfo.h:96
#define ADHESIVE_N_BACK
void SetPlacementCost180(int aCost)
Definition: footprint.h:553
FILE * m_fp
no ownership here.
This file contains miscellaneous commonly used macros and functions.
void SetWidth(int aWidth)
Definition: track.h:109
FP_TEXT & Value()
read/write accessors:
Definition: footprint.h:473
bool m_cache_dirty
void SetThermalWidth(int aWidth)
Definition: footprint.h:243
void SetDescription(const wxString &aDoc)
Definition: footprint.h:212
double biuToDisk
convert from BIUs to disk engineering units with this scale factor
LP_CACHE assists only for the footprint portion of the PLUGIN API, and only for the LEGACY_PLUGIN,...
std::string EscapedUTF8(wxString aString)
Function EscapedUTF8 returns an 8 bit UTF8 string given aString in unicode form.
Definition: string.cpp:304
#define SILKSCREEN_N_FRONT
FP_TEXT & Reference()
Definition: footprint.h:474
#define TO_UTF8(wxstring)
Macro TO_UTF8 converts a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition: macros.h:95
wxSize m_TextSize[LAYER_CLASS_COUNT]
BIU biuParse(const char *aValue, const char **nptrptr=NULL)
Function biuParse parses an ASCII decimal floating point value and scales it into a BIU according to ...
std::vector< int > m_netCodes
net codes mapping for boards being loaded
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT) override
Adds an item to the container.
Definition: board.cpp:553
Definition: kiid.h:44
PCB_LAYER_ID
A quick note on layer IDs:
LINE_READER * m_reader
no ownership here.
FILE_LINE_READER is a LINE_READER that reads from an open file.
Definition: richio.h:181
PCB_SHAPE_TYPE_T
Enum PCB_SHAPE_TYPE_T is the set of shapes for PCB graphics and tracks and footprint graphics in the ...
Definition: board_item.h:50
int m_TextThickness[LAYER_CLASS_COUNT]
static EDA_TEXT_HJUSTIFY_T horizJustify(const char *horizontal)
bool is_leg_copperlayer_valid(int aCu_Count, LAYER_NUM aLegacyLayerNum)
LSET is a set of PCB_LAYER_IDs.
pads are covered by copper
EDA_TEXT_HJUSTIFY_T GetHorizJustify() const
Definition: eda_text.h:205
virtual void SetText(const wxString &aText)
Definition: eda_text.cpp:121
void SetComment(int aIdx, const wxString &aComment)
Definition: title_block.h:104
#define NULL
virtual const wxString What() const
A composite of Problem() and Where()
Definition: exceptions.cpp:29
boost::ptr_map< std::string, FOOTPRINT > FOOTPRINT_MAP
virtual unsigned LineNumber() const
Function Line Number returns the line number of the last line read from this LINE_READER.
Definition: richio.h:160
static int GetDefaultHatchPitch()
Function GetDefaultHatchPitchMils.
Definition: zone.cpp:1091
void init(const PROPERTIES *aProperties)
initialize PLUGIN like a constructor would, and futz with fresh BOARD if needed.
void SetClearance(int aClearance)
Definition: netclass.h:160
SHAPE_POLY_SET.
bool IsFootprintLibWritable(const wxString &aLibraryPath) override
Function IsFootprintLibWritable returns true iff the library at aLibraryPath is writable.
void SetVisibleLayers(LSET aLayerMask)
A proxy function that calls the correspondent function in m_BoardSettings changes the bit-mask of vis...
Definition: board.cpp:463
LEGACY_PLUGIN is a PLUGIN derivation which could possibly be put into a DLL/DSO.
Definition: legacy_plugin.h:61
PAGE_INFO describes the page size and margins of a paper page on which to eventually print or plot.
Definition: page_info.h:54
void SetOrientation(double aNewAngle)
Definition: footprint.cpp:1423
void SetVertJustify(EDA_TEXT_VJUSTIFY_T aType)
Definition: eda_text.h:209
void SetDrillSize(const wxSize &aSize)
Definition: pad.h:235
NETCLASS handles a collection of nets and the parameters used to route or test these nets.
Definition: netclass.h:49
void LoadModules(LINE_READER *aReader)
bool SetLayerName(PCB_LAYER_ID aLayer, const wxString &aLayerName)
Changes the name of the layer given by aLayer.
Definition: board.cpp:348
VECTOR3D m_Scale
3D model scaling factor (dimensionless)
Definition: footprint.h:94
void loadFP_SHAPE(FOOTPRINT *aFootprint)
void SetCompany(const wxString &aCompany)
Definition: title_block.h:94
void loadMODULE_TEXT(FP_TEXT *aText)
Helper for storing and iterating over GAL_LAYER_IDs.
unsigned STATUS_FLAGS
Definition: eda_item.h:142
const wxString & GetNetname() const
Function GetNetname.
Definition: netinfo.h:231
void SetLocalClearance(int aClearance)
Definition: footprint.h:224
#define ECO1_N
NETCLASSES & GetNetClasses() const
void SetLayerPair(PCB_LAYER_ID aTopLayer, PCB_LAYER_ID aBottomLayer)
Function SetLayerPair For a via m_layer contains the top layer, the other layer is in m_bottomLayer.
Definition: track.cpp:401
const LIB_ID & GetFPID() const
Definition: footprint.h:208
#define ALL_CU_LAYERS
unsigned LEG_MASK
LEGACY_PLUGIN::BIU BIU
void SetFileName(const wxString &aFileName)
Definition: board.h:276
void SetPlotOptions(const PCB_PLOT_PARAMS &aOptions)
Definition: board.h:578
void SetuViaDiameter(int aSize)
Definition: netclass.h:176
double diskToBiu
convert from disk engineering units to BIUs with this scale factor
static LAYER_T ParseType(const char *aType)
Convert a string to a LAYER_T.
Definition: board.cpp:410
void SetDrawCoord()
Set draw coordinates (absolute values ) from relative coordinates.
Definition: fp_shape.cpp:81
void SetTitle(const wxString &aTitle)
Definition: title_block.h:60
PCB_PLOT_PARAMS handles plot parameters and options when plotting/printing a board.
Like smd, does not appear on the solder paste layer (default) note also has a special attribute in Ge...
Definition: pad_shapes.h:82
int NewOutline()
Creates a new empty polygon in the set and returns its index
BOARD * m_board
which BOARD, no ownership here
int m_LineThickness[LAYER_CLASS_COUNT]
#define TESTSUBSTR(x)
C sub-string compare test for a specific length of characters.
void SetHeightMils(int aHeightInMils)
Definition: page_info.cpp:257
void SetPos0(const wxPoint &aPos)
Definition: fp_text.h:165
void SetDrillDefault()
Function SetDrillDefault sets the drill value for vias to the default value UNDEFINED_DRILL_DIAMETER.
Definition: track.h:506
void loadAllSections(bool doAppend)
Thermal relief only for THT pads.
int LAYER_NUM
This can be replaced with int and removed.
void loadFOOTPRINT(FOOTPRINT *aFootprint)
static bool isSpace(int c)
ZONE_SETTINGS handles zones parameters.
Definition: zone_settings.h:67
EDA_TEXT_VJUSTIFY_T
Definition: eda_text.h:68
const KIID m_Uuid
Definition: eda_item.h:151
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Function Format outputs 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:98
void loadZONE_CONTAINER()
Definition: seg.h:39
double degParse(const char *aValue, const char **nptrptr=NULL)
Function degParse parses an ASCII decimal floating point value which is certainly an angle.
Use thermal relief for pads.
void SetFPID(const LIB_ID &aFPID)
Definition: footprint.h:209
bool Add(const NETCLASSPTR &aNetclass)
Function Add takes aNetclass and puts it into this NETCLASSES container.
Definition: netclass.cpp:90
#define LAYER_N_FRONT
static LSET leg_mask2new(int cu_count, unsigned aMask)
NETINFO_ITEM handles the data for a net.
Definition: netinfo.h:65
long long m_cache_timestamp
virtual char * ReadLine()=0
Function ReadLine reads a line of text into the buffer and increments the line number counter.
VECTOR3D m_Rotation
3D model rotation (degrees)
Definition: footprint.h:95
#define FIRST_LAYER
void SetuViaDrill(int aSize)
Definition: netclass.h:180
void SetState(int type, int state)
Definition: eda_item.h:209
void SaveFP3DModels(const FOOTPRINT *aFootprint) const
#define VERSION_ERROR_FORMAT
static EDA_TEXT_VJUSTIFY_T vertJustify(const char *vertical)
int GetNet() const
Function GetNet.
Definition: netinfo.h:223
int layerMaskCountSet(LEG_MASK aMask)
Count the number of set layers in the mask.
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:186
void SetHorizJustify(EDA_TEXT_HJUSTIFY_T aType)
Definition: eda_text.h:208
#define _(s)
Definition: 3d_actions.cpp:33
wxString m_lib_path
void SetLastEditTime(timestamp_t aTime)
Definition: footprint.h:337
wxString m_error
for throwing exceptions
void SetPosition(const wxPoint &aPos) override
Definition: track.h:105
std::map< wxString, FOOTPRINT * > FOOTPRINT_MAP
Definition: eagle_parser.h:51
#define LAST_NON_COPPER_LAYER
static DIRECTION_45::AngleType angle(const VECTOR2I &a, const VECTOR2I &b)
static long hexParse(const char *next, const char **out=NULL)
Function hexParse parses an ASCII hex integer string with possible leading whitespace into a long int...
#define SOLDERPASTE_N_FRONT
#define DRAW_N
void SetWidthMils(int aWidthInMils)
Definition: page_info.cpp:243
FOOTPRINT_MAP m_footprints
wxString wx_str() const
Definition: utf8.cpp:51
void SetKeywords(const wxString &aKeywords)
Definition: footprint.h:215
Plated through hole pad.
Definition: pad_shapes.h:80
char * strtok_r(char *str, const char *delim, char **nextp)
void SetDrill(int aDrill)
Function SetDrill sets the drill value for vias.
Definition: track.h:485
NETCLASS * GetDefault() const
Function GetDefault.
void SetLocked(bool isLocked) override
Function SetLocked sets the MODULE_is_LOCKED bit in the m_ModuleStatus.
Definition: footprint.h:301
static long long GetTimestamp(const wxString &aLibPath)
VIATYPE GetViaType() const
Definition: track.h:384
void SetZoneSettings(const ZONE_SETTINGS &aSettings) override
Set the zone settings for this container.
Definition: board.h:569
#define IU_PER_MILS
Definition: plotter.cpp:137
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:68
void load3D(FOOTPRINT *aFootprint)
Pads are not covered.
wxPoint GetPosition() const override
Definition: footprint.h:200
std::vector< VIA_DIMENSION > m_ViasDimensionsList
#define FIRST_COPPER_LAYER
int ShowModal() override
Definition: confirm.cpp:96
ring
Definition: board_item.h:55
void SetViaDiameter(int aDia)
Definition: netclass.h:168
ZONE_BORDER_DISPLAY_STYLE
Zone border styles.
Definition: zone_settings.h:46
class VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:97
void SetTextThickness(int aWidth)
The TextThickness is that set by the user.
Definition: eda_text.h:164
FOOTPRINT * FootprintLoad(const wxString &aLibraryPath, const wxString &aFootprintName, const PROPERTIES *aProperties=NULL) override
Function FootprintLoad loads a footprint having aFootprintName from the aLibraryPath containing a lib...
bool m_MicroViasAllowed
true to allow micro vias
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT) override
Definition: footprint.cpp:442
int ReadDelimitedText(wxString *aDest, const char *aSource)
Copy bytes from aSource delimited string segment to aDest wxString.
Definition: string.cpp:210
VIATYPE
Definition: track.h:68
void SetPosition(const wxPoint &aPos) override
Definition: footprint.cpp:1323
int Parse(const UTF8 &aId, LIB_ID_TYPE aType, bool aFix=false)
Parse LIB_ID with the information from aId.
Definition: lib_id.cpp:122
void Parse(PCB_PLOT_PARAMS_PARSER *aParser)
#define SZ(x)
Get the length of a string constant, at compile time.
BOARD_ITEM_CONTAINER * GetParent() const
Definition: board_item.h:179
void ReadAndVerifyHeader(LINE_READER *aReader)
LAYER_T
The allowed types of layers, same as Specctra DSN spec.
Definition: board.h:62
void SetViaType(VIATYPE aViaType)
Definition: track.h:385
int getNetCode(int aNetCode)
Converts net code using the mapping table if available, otherwise returns unchanged net code
void loadPAD(FOOTPRINT *aFootprint)
void SetPortrait(bool aIsPortrait)
Function SetPortrait will rotate the paper page 90 degrees.
Definition: page_info.cpp:186
static const int UNCONNECTED
Constant that holds the "unconnected net" number (typically 0) all items "connected" to this net are ...
Definition: netinfo.h:478
Struct IO_ERROR is a class used to hold an error message and may be used when throwing exceptions con...
Definition: ki_exception.h:76
#define THROW_IO_ERROR(msg)
Definition: ki_exception.h:38
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition: eda_text.h:133
#define TESTLINE(x)
C string compare test for a specific length of characters.
void loadTrackList(int aStructType)
Function loadTrackList reads a list of segments (Tracks and Vias, or Segzones)
void SetEnabledLayers(LSET aLayerMask)
A proxy function that calls the correspondent function in m_BoardSettings Changes the bit-mask of ena...
Definition: board.cpp:457
wxPoint m_AuxOrigin
origin for plot exports
NETINFO_MAPPING * m_mapping
mapping for net codes, so only not empty nets are stored with consecutive integers as net codes
Definition: track.h:83
LP_CACHE * m_cache
void SetPlacementCost90(int aCost)
Definition: footprint.h:556
#define SOLDERPASTE_N_BACK
#define FIRST_NON_COPPER_LAYER
void SetLocalSolderMaskMargin(int aMargin)
Definition: footprint.h:221
void CalculateBoundingBox()
Function CalculateBoundingBox calculates the bounding box in board coordinates.
Definition: footprint.cpp:565
const PROPERTIES * m_props
passed via Save() or Load(), no ownership, may be NULL.
BOARD_DESIGN_SETTINGS contains design settings for a BOARD object.
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Appends a vertex at the end of the given outline/hole (default: the last outline)
void SetLocalSolderPasteMargin(int aMargin)
Definition: footprint.h:235