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