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