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