KiCad PCB EDA Suite
legacy_plugin.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2007-2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
5  * Copyright (C) 2019 Jean-Pierre Charras, jp.charras@wanadoo.fr
6  * Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, you may find one here:
20  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21  * or you may search the http://www.gnu.org website for the version 2 license,
22  * or you may write to the Free Software Foundation, Inc.,
23  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24  */
25 
26 /*
27  This implements loading and saving a BOARD, behind the PLUGIN interface.
28 
29  The definitions:
30 
31  *) a Board Internal Unit (BIU) is a unit of length that is used only internally
32  to PCBNEW, and is nanometers when this work is done, but deci-mils until done.
33 
34  The philosophies:
35 
36  *) BIUs should be typed as such to distinguish them from ints. This is mostly
37  for human readability, and having the type nearby in the source supports this readability.
38  *) Do not assume that BIUs will always be int, doing a sscanf() into a BIU
39  does not make sense in case the size of the BIU changes.
40  *) variables are put onto the stack in an automatic, even when it might look
41  more efficient to do otherwise. This is so we can seem them with a debugger.
42  *) Global variables should not be touched from within a PLUGIN, since it will eventually
43  be in a DLL/DSO. This includes window information too. The PLUGIN API knows
44  nothing of wxFrame or globals and all error reporting must be done by throwing
45  an exception.
46  *) No wxWindowing calls are made in here, since the UI resides higher up than in here,
47  and is going to process a bucket of detailed information thrown from down here
48  in the form of an exception if an error happens.
49  *) Much of what we do in this source file is for human readability, not performance.
50  Simply avoiding strtok() more often than the old code washes out performance losses.
51  Remember strncmp() will bail as soon as a mismatch happens, not going all the way
52  to end of string unless a full match.
53  *) angles are in the process of migrating to doubles, and 'int' if used, is
54  only shortterm, and along with this a change, and transition from from
55  "tenths of degrees" to simply "degrees" in the double (which has no problem
56  representing any portion of a degree).
57 */
58 
59 
60 #include <cerrno>
61 #include <cmath>
62 #include <cstdio>
63 #include <cstring>
64 #include <plugins/legacy/legacy_plugin.h> // implement this here
65 #include <wx/ffile.h>
66 #include <wx/string.h>
67 
68 #include <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( "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( "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( "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( "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( "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( "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.m_AuxOrigin = 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.m_GridOrigin = 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( "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 );
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( "S_POLGON point count mismatch." );
1640  }
1641 
1642  // e.g. "Dl 23 44\n"
1643 
1644  if( !TESTLINE( "Dl" ) )
1645  {
1646  THROW_IO_ERROR( "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( "%V", "${VALUE}" );
1714  m_field.Replace( "%R", "${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( "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  dseg->SetStart( wxPoint( start_x, start_y ) );
1851  dseg->SetEnd( wxPoint( end_x, end_y ) );
1852  }
1853  else if( TESTLINE( "De" ) )
1854  {
1855  BIU x = 0;
1856  BIU y;
1857 
1858  data = strtok_r( line + SZ( "De" ), delims, &saveptr );
1859 
1860  for( int i = 0; data; ++i, data = strtok_r( nullptr, delims, &saveptr ) )
1861  {
1862  switch( i )
1863  {
1864  case 0:
1865  LAYER_NUM layer;
1866  layer = layerParse( data );
1867 
1868  if( layer < FIRST_NON_COPPER_LAYER )
1869  layer = FIRST_NON_COPPER_LAYER;
1870 
1871  else if( layer > LAST_NON_COPPER_LAYER )
1872  layer = LAST_NON_COPPER_LAYER;
1873 
1874  dseg->SetLayer( leg_layer2new( m_cu_count, layer ) );
1875  break;
1876  case 1:
1877  ignore_unused( intParse( data ) );
1878  break;
1879  case 2:
1880  double angle;
1881  angle = degParse( data );
1882  dseg->SetArcAngleAndEnd( angle ); // m_Angle
1883  break;
1884  case 3:
1885  const_cast<KIID&>( dseg->m_Uuid ) = KIID( data );
1886  break;
1887  case 4:
1888  EDA_ITEM_FLAGS state;
1889  state = static_cast<EDA_ITEM_FLAGS>( hexParse( data ) );
1890  dseg->SetState( state, true );
1891  break;
1892 
1893  // Bezier Control Points
1894  case 5:
1895  x = biuParse( data );
1896  break;
1897  case 6:
1898  y = biuParse( data );
1899  dseg->SetBezierC1( wxPoint( x, y ));
1900  break;
1901 
1902  case 7:
1903  x = biuParse( data );
1904  break;
1905  case 8:
1906  y = biuParse( data );
1907  dseg->SetBezierC2( wxPoint( x, y ));
1908  break;
1909 
1910  default:
1911  break;
1912  }
1913  }
1914  }
1915  else if( TESTLINE( "$EndDRAWSEGMENT" ) )
1916  {
1917  m_board->Add( dseg.release(), ADD_MODE::APPEND );
1918  return; // preferred exit
1919  }
1920  }
1921 
1922  THROW_IO_ERROR( "Missing '$EndDRAWSEGMENT'" );
1923 }
1924 
1926 {
1927  /* a net description is something like
1928  * $EQUIPOT
1929  * Na 5 "/BIT1"
1930  * St ~
1931  * $EndEQUIPOT
1932  */
1933 
1934  char buf[1024];
1935 
1936  NETINFO_ITEM* net = nullptr;
1937  char* line;
1938  int netCode = 0;
1939 
1940  while( ( line = READLINE( m_reader ) ) != nullptr )
1941  {
1942  const char* data;
1943 
1944  if( TESTLINE( "Na" ) )
1945  {
1946  // e.g. "Na 58 "/cpu.sch/PAD7"\r\n"
1947 
1948  netCode = intParse( line + SZ( "Na" ), &data );
1949 
1950  ReadDelimitedText( buf, data, sizeof(buf) );
1951 
1952  if( net == nullptr )
1953  {
1955  netCode );
1956  }
1957  else
1958  {
1959  THROW_IO_ERROR( "Two net definitions in '$EQUIPOT' block" );
1960  }
1961  }
1962  else if( TESTLINE( "$EndEQUIPOT" ) )
1963  {
1964  // net 0 should be already in list, so store this net
1965  // if it is not the net 0, or if the net 0 does not exists.
1966  if( net && ( net->GetNetCode() > 0 || m_board->FindNet( 0 ) == nullptr ) )
1967  {
1968  m_board->Add( net );
1969 
1970  // Be sure we have room to store the net in m_netCodes
1971  if( (int)m_netCodes.size() <= netCode )
1972  m_netCodes.resize( netCode+1 );
1973 
1974  m_netCodes[netCode] = net->GetNetCode();
1975  net = nullptr;
1976  }
1977  else
1978  {
1979  delete net;
1980  net = nullptr; // Avoid double deletion.
1981  }
1982 
1983  return; // preferred exit
1984  }
1985  }
1986 
1987  // If we are here, there is an error.
1988  delete net;
1989  THROW_IO_ERROR( "Missing '$EndEQUIPOT'" );
1990 }
1991 
1992 
1994 {
1995  /* examples:
1996  For a single line text:
1997  ----------------------
1998  $TEXTPCB
1999  Te "Text example"
2000  Po 66750 53450 600 800 150 0
2001  De 24 1 0 Italic
2002  $EndTEXTPCB
2003 
2004  For a multi line text:
2005  ---------------------
2006  $TEXTPCB
2007  Te "Text example"
2008  Nl "Line 2"
2009  Po 66750 53450 600 800 150 0
2010  De 24 1 0 Italic
2011  $EndTEXTPCB
2012  Nl "line nn" is a line added to the current text
2013  */
2014 
2015  char text[1024];
2016 
2017  // maybe someday a constructor that takes all this data in one call?
2018  PCB_TEXT* pcbtxt = new PCB_TEXT( m_board );
2019  m_board->Add( pcbtxt, ADD_MODE::APPEND );
2020 
2021  char* line;
2022 
2023  while( ( line = READLINE( m_reader ) ) != nullptr )
2024  {
2025  const char* data;
2026 
2027  if( TESTLINE( "Te" ) ) // Text line (or first line for multi line texts)
2028  {
2029  ReadDelimitedText( text, line + SZ( "Te" ), sizeof(text) );
2031  }
2032  else if( TESTLINE( "nl" ) ) // next line of the current text
2033  {
2034  ReadDelimitedText( text, line + SZ( "nl" ), sizeof(text) );
2035  pcbtxt->SetText( pcbtxt->GetText() + wxChar( '\n' ) + FROM_UTF8( text ) );
2036  }
2037  else if( TESTLINE( "Po" ) )
2038  {
2039  wxSize size;
2040 
2041  BIU pos_x = biuParse( line + SZ( "Po" ), &data );
2042  BIU pos_y = biuParse( data, &data );
2043  size.x = biuParse( data, &data );
2044  size.y = biuParse( data, &data );
2045  BIU thickn = biuParse( data, &data );
2046  double angle = degParse( data );
2047 
2048  pcbtxt->SetTextSize( size );
2049  pcbtxt->SetTextThickness( thickn );
2050  pcbtxt->SetTextAngle( angle );
2051 
2052  pcbtxt->SetTextPos( wxPoint( pos_x, pos_y ) );
2053  }
2054  else if( TESTLINE( "De" ) )
2055  {
2056  // e.g. "De 21 1 68183921-93a5-49ac-91b0-49d05a0e1647 Normal C\r\n"
2057  LAYER_NUM layer_num = layerParse( line + SZ( "De" ), &data );
2058  int notMirrored = intParse( data, &data );
2059  char* uuid = strtok_r( (char*) data, delims, (char**) &data );
2060  char* style = strtok_r( nullptr, delims, (char**) &data );
2061  char* hJustify = strtok_r( nullptr, delims, (char**) &data );
2062  char* vJustify = strtok_r( nullptr, delims, (char**) &data );
2063 
2064  pcbtxt->SetMirrored( !notMirrored );
2065  const_cast<KIID&>( pcbtxt->m_Uuid ) = KIID( uuid );
2066  pcbtxt->SetItalic( !strcmp( style, "Italic" ) );
2067 
2068  if( hJustify )
2069  {
2070  pcbtxt->SetHorizJustify( horizJustify( hJustify ) );
2071  }
2072  else
2073  {
2074  // boom, somebody changed a constructor, I was relying on this:
2075  wxASSERT( pcbtxt->GetHorizJustify() == GR_TEXT_HJUSTIFY_CENTER );
2076  }
2077 
2078  if( vJustify )
2079  pcbtxt->SetVertJustify( vertJustify( vJustify ) );
2080 
2081  if( layer_num < FIRST_COPPER_LAYER )
2082  layer_num = FIRST_COPPER_LAYER;
2083  else if( layer_num > LAST_NON_COPPER_LAYER )
2084  layer_num = LAST_NON_COPPER_LAYER;
2085 
2086  if( layer_num >= FIRST_NON_COPPER_LAYER ||
2087  is_leg_copperlayer_valid( m_cu_count, layer_num ) )
2088  pcbtxt->SetLayer( leg_layer2new( m_cu_count, layer_num ) );
2089  else // not perfect, but putting this text on front layer is a workaround
2090  pcbtxt->SetLayer( F_Cu );
2091  }
2092  else if( TESTLINE( "$EndTEXTPCB" ) )
2093  {
2094  return; // preferred exit
2095  }
2096  }
2097 
2098  THROW_IO_ERROR( "Missing '$EndTEXTPCB'" );
2099 }
2100 
2101 
2102 void LEGACY_PLUGIN::loadTrackList( int aStructType )
2103 {
2104  char* line;
2105 
2106  while( ( line = READLINE( m_reader ) ) != nullptr )
2107  {
2108  checkpoint();
2109 
2110  // read two lines per loop iteration, each loop is one TRACK or VIA
2111  // example first line:
2112  // e.g. "Po 0 23994 28800 24400 28800 150 -1" for a track
2113  // e.g. "Po 3 21086 17586 21086 17586 180 -1" for a via (uses sames start and end)
2114  const char* data;
2115 
2116  if( line[0] == '$' ) // $EndTRACK
2117  return; // preferred exit
2118 
2119  assert( TESTLINE( "Po" ) );
2120 
2121  VIATYPE viatype = static_cast<VIATYPE>( intParse( line + SZ( "Po" ), &data ) );
2122  BIU start_x = biuParse( data, &data );
2123  BIU start_y = biuParse( data, &data );
2124  BIU end_x = biuParse( data, &data );
2125  BIU end_y = biuParse( data, &data );
2126  BIU width = biuParse( data, &data );
2127 
2128  // optional 7th drill parameter (must be optional in an old format?)
2129  data = strtok_r( (char*) data, delims, (char**) &data );
2130 
2131  BIU drill = data ? biuParse( data ) : -1; // SetDefault() if < 0
2132 
2133  // Read the 2nd line to determine the exact type, one of:
2134  // PCB_TRACE_T, PCB_VIA_T, or PCB_SEGZONE_T. The type field in 2nd line
2135  // differentiates between PCB_TRACE_T and PCB_VIA_T. With virtual
2136  // functions in use, it is critical to instantiate the PCB_VIA_T
2137  // exactly.
2138  READLINE( m_reader );
2139 
2140  line = m_reader->Line();
2141 
2142  // example second line:
2143  // "De 0 0 463 0 800000\r\n"
2144 
2145 #if 1
2146  assert( TESTLINE( "De" ) );
2147 #else
2148  if( !TESTLINE( "De" ) )
2149  {
2150  // mandatory 2nd line is missing
2151  THROW_IO_ERROR( "Missing 2nd line of a TRACK def" );
2152  }
2153 #endif
2154 
2155  int makeType;
2156 
2157  // parse the 2nd line to determine the type of object
2158  // e.g. "De 15 1 7 68183921-93a5-49ac-91b0-49d05a0e1647 0" for a via
2159  LAYER_NUM layer_num = layerParse( line + SZ( "De" ), &data );
2160  int type = intParse( data, &data );
2161  int net_code = intParse( data, &data );
2162  char* uuid = strtok_r( (char*) data, delims, (char**) &data );
2163  int flags_int = intParse( data, (const char**) &data );
2164 
2165  EDA_ITEM_FLAGS flags = static_cast<EDA_ITEM_FLAGS>( flags_int );
2166 
2167  if( aStructType == PCB_TRACE_T )
2168  {
2169  makeType = ( type == 1 ) ? PCB_VIA_T : PCB_TRACE_T;
2170  }
2171  else if (aStructType == NOT_USED )
2172  {
2173  continue;
2174  }
2175  else
2176  {
2177  wxFAIL_MSG( "Segment type unknown" );
2178  continue;
2179  }
2180 
2181  PCB_TRACK* newTrack;
2182 
2183  switch( makeType )
2184  {
2185  default:
2186  case PCB_TRACE_T: newTrack = new PCB_TRACK( m_board ); break;
2187  case PCB_VIA_T: newTrack = new PCB_VIA( m_board ); break;
2188  }
2189 
2190  const_cast<KIID&>( newTrack->m_Uuid ) = KIID( uuid );
2191  newTrack->SetPosition( wxPoint( start_x, start_y ) );
2192  newTrack->SetEnd( wxPoint( end_x, end_y ) );
2193 
2194  newTrack->SetWidth( width );
2195 
2196  if( makeType == PCB_VIA_T ) // Ensure layers are OK when possible:
2197  {
2198  PCB_VIA *via = static_cast<PCB_VIA*>( newTrack );
2199  via->SetViaType( viatype );
2200 
2201  if( drill < 0 )
2202  via->SetDrillDefault();
2203  else
2204  via->SetDrill( drill );
2205 
2206  if( via->GetViaType() == VIATYPE::THROUGH )
2207  {
2208  via->SetLayerPair( F_Cu, B_Cu );
2209  }
2210  else
2211  {
2212  PCB_LAYER_ID back = leg_layer2new( m_cu_count, (layer_num >> 4) & 0xf );
2213  PCB_LAYER_ID front = leg_layer2new( m_cu_count, layer_num & 0xf );
2214 
2215  if( is_leg_copperlayer_valid( m_cu_count, back ) &&
2217  {
2218  via->SetLayerPair( front, back );
2219  }
2220  else
2221  {
2222  delete via;
2223  newTrack = nullptr;
2224  }
2225  }
2226  }
2227  else
2228  {
2229  // A few legacy boards can have tracks on non existent layers, because
2230  // reducing the number of layers does not remove tracks on removed layers
2231  // If happens, skip them
2232  if( is_leg_copperlayer_valid( m_cu_count, layer_num ) )
2233  {
2234  newTrack->SetLayer( leg_layer2new( m_cu_count, layer_num ) );
2235  }
2236  else
2237  {
2238  delete newTrack;
2239  newTrack = nullptr;
2240  }
2241  }
2242 
2243  if( newTrack )
2244  {
2245  newTrack->SetNetCode( getNetCode( net_code ) );
2246  newTrack->SetState( flags, true );
2247 
2248  m_board->Add( newTrack );
2249  }
2250  }
2251 
2252  THROW_IO_ERROR( "Missing '$EndTRACK'" );
2253 }
2254 
2255 
2257 {
2258  char buf[1024];
2259  wxString netname;
2260  char* line;
2261 
2262  // create an empty NETCLASS without a name, but do not add it to the BOARD
2263  // yet since that would bypass duplicate netclass name checking within the BOARD.
2264  // store it temporarily in an unique_ptr until successfully inserted into the BOARD
2265  // just before returning.
2266  NETCLASSPTR nc = std::make_shared<NETCLASS>( wxEmptyString );
2267 
2268  while( ( line = READLINE( m_reader ) ) != nullptr )
2269  {
2270  if( TESTLINE( "AddNet" ) ) // most frequent type of line
2271  {
2272  // e.g. "AddNet "V3.3D"\n"
2273  ReadDelimitedText( buf, line + SZ( "AddNet" ), sizeof(buf) );
2274  netname = ConvertToNewOverbarNotation( FROM_UTF8( buf ) );
2275  nc->Add( netname );
2276  }
2277  else if( TESTLINE( "Clearance" ) )
2278  {
2279  BIU tmp = biuParse( line + SZ( "Clearance" ) );
2280  nc->SetClearance( tmp );
2281  }
2282  else if( TESTLINE( "TrackWidth" ) )
2283  {
2284  BIU tmp = biuParse( line + SZ( "TrackWidth" ) );
2285  nc->SetTrackWidth( tmp );
2286  }
2287  else if( TESTLINE( "ViaDia" ) )
2288  {
2289  BIU tmp = biuParse( line + SZ( "ViaDia" ) );
2290  nc->SetViaDiameter( tmp );
2291  }
2292  else if( TESTLINE( "ViaDrill" ) )
2293  {
2294  BIU tmp = biuParse( line + SZ( "ViaDrill" ) );
2295  nc->SetViaDrill( tmp );
2296  }
2297  else if( TESTLINE( "uViaDia" ) )
2298  {
2299  BIU tmp = biuParse( line + SZ( "uViaDia" ) );
2300  nc->SetuViaDiameter( tmp );
2301  }
2302  else if( TESTLINE( "uViaDrill" ) )
2303  {
2304  BIU tmp = biuParse( line + SZ( "uViaDrill" ) );
2305  nc->SetuViaDrill( tmp );
2306  }
2307  else if( TESTLINE( "Name" ) )
2308  {
2309  ReadDelimitedText( buf, line + SZ( "Name" ), sizeof(buf) );
2310  nc->SetName( FROM_UTF8( buf ) );
2311  }
2312  else if( TESTLINE( "Desc" ) )
2313  {
2314  ReadDelimitedText( buf, line + SZ( "Desc" ), sizeof(buf) );
2315  nc->SetDescription( FROM_UTF8( buf ) );
2316  }
2317  else if( TESTLINE( "$EndNCLASS" ) )
2318  {
2319  if( !m_board->GetDesignSettings().GetNetClasses().Add( nc ) )
2320  {
2321  // Must have been a name conflict, this is a bad board file.
2322  // User may have done a hand edit to the file.
2323 
2324  // unique_ptr will delete nc on this code path
2325 
2326  m_error.Printf( _( "Duplicate NETCLASS name '%s'." ), nc->GetName().GetData() );
2328  }
2329 
2330  return; // preferred exit
2331  }
2332  }
2333 
2334  THROW_IO_ERROR( "Missing '$EndNCLASS'" );
2335 }
2336 
2337 
2339 {
2340  std::unique_ptr<ZONE> zc = std::make_unique<ZONE>( m_board );
2341 
2343  bool endContour = false;
2344  int holeIndex = -1; // -1 is the main outline; holeIndex >= 0 = hole index
2345  char buf[1024];
2346  char* line;
2347 
2348  while( ( line = READLINE( m_reader ) ) != nullptr )
2349  {
2350  const char* data;
2351 
2352  if( TESTLINE( "ZCorner" ) ) // new corner of the zone outlines found
2353  {
2354  // e.g. "ZCorner 25650 49500 0"
2355  BIU x = biuParse( line + SZ( "ZCorner" ), &data );
2356  BIU y = biuParse( data, &data );
2357 
2358  if( endContour )
2359  {
2360  // the previous corner was the last corner of a contour.
2361  // so this corner is the first of a new hole
2362  endContour = false;
2363  zc->NewHole();
2364  holeIndex++;
2365  }
2366 
2367  zc->AppendCorner( wxPoint( x, y ), holeIndex );
2368 
2369  // Is this corner the end of current contour?
2370  // the next corner (if any) will be stored in a new contour (a hole)
2371  // intParse( data )returns 0 = usual corner, 1 = last corner of the current contour:
2372  endContour = intParse( data );
2373  }
2374  else if( TESTLINE( "ZInfo" ) ) // general info found
2375  {
2376  // e.g. 'ZInfo 68183921-93a5-49ac-91b0-49d05a0e1647 310 "COMMON"'
2377  char* uuid = strtok_r( (char*) line + SZ( "ZInfo" ), delims, (char**) &data );
2378  int netcode = intParse( data, &data );
2379 
2380  if( ReadDelimitedText( buf, data, sizeof(buf) ) > (int) sizeof(buf) )
2381  THROW_IO_ERROR( "ZInfo netname too long" );
2382 
2383  const_cast<KIID&>( zc->m_Uuid ) = KIID( uuid );
2384 
2385  // Init the net code only, not the netname, to be sure
2386  // the zone net name is the name read in file.
2387  // (When mismatch, the user will be prompted in DRC, to fix the actual name)
2388  zc->BOARD_CONNECTED_ITEM::SetNetCode( getNetCode( netcode ) );
2389  }
2390  else if( TESTLINE( "ZLayer" ) ) // layer found
2391  {
2392  LAYER_NUM layer_num = layerParse( line + SZ( "ZLayer" ) );
2393  zc->SetLayer( leg_layer2new( m_cu_count, layer_num ) );
2394  }
2395  else if( TESTLINE( "ZAux" ) ) // aux info found
2396  {
2397  // e.g. "ZAux 7 E"
2398  ignore_unused( intParse( line + SZ( "ZAux" ), &data ) );
2399  char* hopt = strtok_r( (char*) data, delims, (char**) &data );
2400 
2401  if( !hopt )
2402  {
2403  m_error.Printf( _( "Bad ZAux for CZONE_CONTAINER \"%s\"" ),
2404  zc->GetNetname().GetData() );
2406  }
2407 
2408  switch( *hopt ) // upper case required
2409  {
2410  case 'N': outline_hatch = ZONE_BORDER_DISPLAY_STYLE::NO_HATCH; break;
2411  case 'E': outline_hatch = ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE; break;
2412  case 'F': outline_hatch = ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_FULL; break;
2413  default:
2414  m_error.Printf( _( "Bad ZAux for CZONE_CONTAINER \"%s\"" ),
2415  zc->GetNetname().GetData() );
2417  }
2418 
2419  // Set hatch mode later, after reading corner outline data
2420  }
2421  else if( TESTLINE( "ZSmoothing" ) )
2422  {
2423  // e.g. "ZSmoothing 0 0"
2424  int smoothing = intParse( line + SZ( "ZSmoothing" ), &data );
2425  BIU cornerRadius = biuParse( data );
2426 
2427  if( smoothing >= ZONE_SETTINGS::SMOOTHING_LAST || smoothing < 0 )
2428  {
2429  m_error.Printf( _( "Bad ZSmoothing for CZONE_CONTAINER \"%s\"" ),
2430  zc->GetNetname().GetData() );
2432  }
2433 
2434  zc->SetCornerSmoothingType( smoothing );
2435  zc->SetCornerRadius( cornerRadius );
2436  }
2437  else if( TESTLINE( "ZKeepout" ) )
2438  {
2439  char* token;
2440  zc->SetIsRuleArea( true );
2441  zc->SetDoNotAllowPads( false ); // Not supported in legacy
2442  zc->SetDoNotAllowFootprints( false ); // Not supported in legacy
2443 
2444  // e.g. "ZKeepout tracks N vias N pads Y"
2445  token = strtok_r( line + SZ( "ZKeepout" ), delims, (char**) &data );
2446 
2447  while( token )
2448  {
2449  if( !strcmp( token, "tracks" ) )
2450  {
2451  token = strtok_r( nullptr, delims, (char**) &data );
2452  zc->SetDoNotAllowTracks( token && *token == 'N' );
2453  }
2454  else if( !strcmp( token, "vias" ) )
2455  {
2456  token = strtok_r( nullptr, delims, (char**) &data );
2457  zc->SetDoNotAllowVias( token && *token == 'N' );
2458  }
2459  else if( !strcmp( token, "copperpour" ) )
2460  {
2461  token = strtok_r( nullptr, delims, (char**) &data );
2462  zc->SetDoNotAllowCopperPour( token && *token == 'N' );
2463  }
2464 
2465  token = strtok_r( nullptr, delims, (char**) &data );
2466  }
2467  }
2468  else if( TESTLINE( "ZOptions" ) )
2469  {
2470  // e.g. "ZOptions 0 32 F 200 200"
2471  int fillmode = intParse( line + SZ( "ZOptions" ), &data );
2472  ignore_unused( intParse( data, &data ) );
2473  char fillstate = data[1]; // here e.g. " F"
2474  BIU thermalReliefGap = biuParse( data += 2 , &data ); // +=2 for " F"
2475  BIU thermalReliefCopperBridge = biuParse( data );
2476 
2477  if( fillmode)
2478  {
2479  // SEGMENT fill mode no longer supported. Make sure user is OK with converting
2480  // them.
2482  {
2483  KIDIALOG dlg( nullptr,
2484  _( "The legacy segment fill mode is no longer supported.\n"
2485  "Convert zones to polygon fills?"),
2486  _( "Legacy Zone Warning" ),
2487  wxYES_NO | wxICON_WARNING );
2488 
2489  dlg.DoNotShowCheckbox( __FILE__, __LINE__ );
2490 
2491  if( dlg.ShowModal() == wxID_NO )
2492  THROW_IO_ERROR( wxT( "CANCEL" ) );
2493 
2494  m_showLegacyZoneWarning = false;
2495  }
2496 
2497  // User OK'd; switch to polygon mode
2498  zc->SetFillMode( ZONE_FILL_MODE::POLYGONS );
2499  m_board->SetModified();
2500  }
2501  else
2502  {
2503  zc->SetFillMode( ZONE_FILL_MODE::POLYGONS );
2504  }
2505 
2506  zc->SetIsFilled( fillstate == 'S' );
2507  zc->SetThermalReliefGap( thermalReliefGap );
2508  zc->SetThermalReliefSpokeWidth( thermalReliefCopperBridge );
2509  }
2510  else if( TESTLINE( "ZClearance" ) ) // Clearance and pad options info found
2511  {
2512  // e.g. "ZClearance 40 I"
2513  BIU clearance = biuParse( line + SZ( "ZClearance" ), &data );
2514  char* padoption = strtok_r( (char*) data, delims, (char**) &data ); // data: " I"
2515 
2516  ZONE_CONNECTION popt;
2517  switch( *padoption )
2518  {
2519  case 'I': popt = ZONE_CONNECTION::FULL; break;
2520  case 'T': popt = ZONE_CONNECTION::THERMAL; break;
2521  case 'H': popt = ZONE_CONNECTION::THT_THERMAL; break;
2522  case 'X': popt = ZONE_CONNECTION::NONE; break;
2523  default:
2524  m_error.Printf( _( "Bad ZClearance padoption for CZONE_CONTAINER \"%s\"" ),
2525  zc->GetNetname().GetData() );
2527  }
2528 
2529  zc->SetLocalClearance( clearance );
2530  zc->SetPadConnection( popt );
2531  }
2532  else if( TESTLINE( "ZMinThickness" ) )
2533  {
2534  BIU thickness = biuParse( line + SZ( "ZMinThickness" ) );
2535  zc->SetMinThickness( thickness );
2536  }
2537  else if( TESTLINE( "ZPriority" ) )
2538  {
2539  int priority = intParse( line + SZ( "ZPriority" ) );
2540  zc->SetPriority( priority );
2541  }
2542  else if( TESTLINE( "$POLYSCORNERS" ) )
2543  {
2544  // Read the PolysList (polygons that are the solid areas in the filled zone)
2545  SHAPE_POLY_SET polysList;
2546 
2547  bool makeNewOutline = true;
2548 
2549  while( ( line = READLINE( m_reader ) ) != nullptr )
2550  {
2551  if( TESTLINE( "$endPOLYSCORNERS" ) )
2552  break;
2553 
2554  // e.g. "39610 43440 0 0"
2555  BIU x = biuParse( line, &data );
2556  BIU y = biuParse( data, &data );
2557 
2558  if( makeNewOutline )
2559  polysList.NewOutline();
2560 
2561  polysList.Append( x, y );
2562 
2563  // end_countour was a bool when file saved, so '0' or '1' here
2564  bool end_contour = intParse( data, &data );
2565  intParse( data ); // skip corner utility flag
2566 
2567  makeNewOutline = end_contour;
2568  }
2569 
2570  zc->SetFilledPolysList( zc->GetLayer(), polysList );
2571  }
2572  else if( TESTLINE( "$FILLSEGMENTS" ) )
2573  {
2574  while( ( line = READLINE( m_reader ) ) != nullptr )
2575  {
2576  if( TESTLINE( "$endFILLSEGMENTS" ) )
2577  break;
2578 
2579  // e.g. ""%d %d %d %d\n"
2580  BIU sx = biuParse( line, &data );
2581  BIU sy = biuParse( data, &data );
2582  BIU ex = biuParse( data, &data );
2583  BIU ey = biuParse( data );
2584 
2585  zc->FillSegments( zc->GetLayer() )
2586  .push_back( SEG( VECTOR2I( sx, sy ), VECTOR2I( ex, ey ) ) );
2587  }
2588  }
2589  else if( TESTLINE( "$endCZONE_OUTLINE" ) )
2590  {
2591  // Ensure keepout does not have a net
2592  // (which have no sense for a keepout zone)
2593  if( zc->GetIsRuleArea() )
2594  zc->SetNetCode( NETINFO_LIST::UNCONNECTED );
2595 
2596  // should always occur, but who knows, a zone without two corners
2597  // is no zone at all, it's a spot?
2598 
2599  if( zc->GetNumCorners() > 2 )
2600  {
2601  if( !zc->IsOnCopperLayer() )
2602  {
2603  zc->SetFillMode( ZONE_FILL_MODE::POLYGONS );
2604  zc->SetNetCode( NETINFO_LIST::UNCONNECTED );
2605  }
2606 
2607  // HatchBorder here, after outlines corners are read
2608  // Set hatch here, after outlines corners are read
2609  zc->SetBorderDisplayStyle( outline_hatch, ZONE::GetDefaultHatchPitch(), true );
2610 
2611  m_board->Add( zc.release() );
2612  }
2613 
2614  return; // preferred exit
2615  }
2616  }
2617 
2618  THROW_IO_ERROR( "Missing '$endCZONE_OUTLINE'" );
2619 }
2620 
2621 
2623 {
2624  std::unique_ptr<PCB_DIM_ALIGNED> dim = std::make_unique<PCB_DIM_ALIGNED>( m_board );
2625 
2626  char* line;
2627 
2628  while( ( line = READLINE( m_reader ) ) != nullptr )
2629  {
2630  const char* data;
2631 
2632  if( TESTLINE( "$endCOTATION" ) )
2633  {
2634  m_board->Add( dim.release(), ADD_MODE::APPEND );
2635  return; // preferred exit
2636  }
2637  else if( TESTLINE( "Va" ) )
2638  {
2639  BIU value = biuParse( line + SZ( "Va" ) );
2640 
2641  // unused; dimension value is calculated from coordinates
2642  ( void )value;
2643  }
2644  else if( TESTLINE( "Ge" ) )
2645  {
2646  // e.g. "Ge 1 21 68183921-93a5-49ac-91b0-49d05a0e1647\r\n"
2647  int shape = intParse( line + SZ( "De" ), (const char**) &data );
2648  LAYER_NUM layer_num = layerParse( data, &data );
2649  char* uuid = strtok_r( (char*) data, delims, (char**) &data );
2650 
2651  dim->SetLayer( leg_layer2new( m_cu_count, layer_num ) );
2652  const_cast<KIID&>( dim->m_Uuid ) = KIID( uuid );
2653 
2654  // not used
2655  ( void )shape;
2656  }
2657  else if( TESTLINE( "Te" ) )
2658  {
2659  char buf[2048];
2660 
2661  ReadDelimitedText( buf, line + SZ( "Te" ), sizeof(buf) );
2662  dim->SetText( FROM_UTF8( buf ) );
2663  }
2664  else if( TESTLINE( "Po" ) )
2665  {
2666  BIU pos_x = biuParse( line + SZ( "Po" ), &data );
2667  BIU pos_y = biuParse( data, &data );
2668  BIU width = biuParse( data, &data );
2669  BIU height = biuParse( data, &data );
2670  BIU thickn = biuParse( data, &data );
2671  double orient = degParse( data, &data );
2672  char* mirror = strtok_r( (char*) data, delims, (char**) &data );
2673 
2674  // This sets both DIMENSION's position and internal m_Text's.
2675  // @todo: But why do we even know about internal m_Text?
2676  dim->SetPosition( wxPoint( pos_x, pos_y ) );
2677  dim->SetTextSize( wxSize( width, height ) );
2678 
2679  dim->Text().SetMirrored( mirror && *mirror == '0' );
2680  dim->Text().SetTextThickness( thickn );
2681  dim->Text().SetTextAngle( orient );
2682  }
2683  else if( TESTLINE( "Sb" ) )
2684  {
2685  ignore_unused( biuParse( line + SZ( "Sb" ), &data ) );
2686  BIU crossBarOx = biuParse( data, &data );
2687  BIU crossBarOy = biuParse( data, &data );
2688  BIU crossBarFx = biuParse( data, &data );
2689  BIU crossBarFy = biuParse( data, &data );
2690  BIU width = biuParse( data );
2691 
2692  dim->SetLineThickness( width );
2693  dim->UpdateHeight( wxPoint( crossBarFx, crossBarFy ),
2694  wxPoint( crossBarOx, crossBarOy ) );
2695  }
2696  else if( TESTLINE( "Sd" ) )
2697  {
2698  ignore_unused( intParse( line + SZ( "Sd" ), &data ) );
2699  BIU featureLineDOx = biuParse( data, &data );
2700  BIU featureLineDOy = biuParse( data, &data );
2701 
2702  ignore_unused( biuParse( data, &data ) );
2703  ignore_unused( biuParse( data ) );
2704 
2705  dim->SetStart( wxPoint( featureLineDOx, featureLineDOy ) );
2706  }
2707  else if( TESTLINE( "Sg" ) )
2708  {
2709  ignore_unused( intParse( line + SZ( "Sg" ), &data ) );
2710  BIU featureLineGOx = biuParse( data, &data );
2711  BIU featureLineGOy = biuParse( data, &data );
2712 
2713  ignore_unused( biuParse( data, &data ) );
2714  ignore_unused( biuParse( data ) );
2715 
2716  dim->SetEnd( wxPoint( featureLineGOx, featureLineGOy ) );
2717  }
2718  else if( TESTLINE( "S1" ) ) // Arrow: no longer imported
2719  {
2720  ignore_unused( intParse( line + SZ( "S1" ), &data ) );
2721  biuParse( data, &data ); // skipping excessive data
2722  biuParse( data, &data ); // skipping excessive data
2723  biuParse( data, &data );
2724  biuParse( data );
2725  }
2726  else if( TESTLINE( "S2" ) ) // Arrow: no longer imported
2727  {
2728  ignore_unused( intParse( line + SZ( "S2" ), &data ) );
2729  biuParse( data, &data ); // skipping excessive data
2730  biuParse( data, &data ); // skipping excessive data
2731  biuParse( data, &data );
2732  biuParse( data, &data );
2733  }
2734  else if( TESTLINE( "S3" ) ) // Arrow: no longer imported
2735  {
2736  ignore_unused( intParse( line + SZ( "S3" ), &data ) );
2737  biuParse( data, &data ); // skipping excessive data
2738  biuParse( data, &data ); // skipping excessive data
2739  biuParse( data, &data );
2740  biuParse( data, &data );
2741  }
2742  else if( TESTLINE( "S4" ) ) // Arrow: no longer imported
2743  {
2744  ignore_unused( intParse( line + SZ( "S4" ), &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  }
2751 
2752  THROW_IO_ERROR( "Missing '$endCOTATION'" );
2753 }
2754 
2755 
2757 {
2758  char* line;
2759 
2760  while( ( line = READLINE( m_reader ) ) != nullptr )
2761  {
2762  const char* data;
2763 
2764  if( TESTLINE( "$EndPCB_TARGET" ) || TESTLINE( "$EndMIREPCB" ) )
2765  {
2766  return; // preferred exit
2767  }
2768  else if( TESTLINE( "Po" ) )
2769  {
2770  int shape = intParse( line + SZ( "Po" ), &data );
2771  LAYER_NUM layer_num = layerParse( data, &data );
2772  BIU pos_x = biuParse( data, &data );
2773  BIU pos_y = biuParse( data, &data );
2774  BIU size = biuParse( data, &data );
2775  BIU width = biuParse( data, &data );
2776  char* uuid = strtok_r( (char*) data, delims, (char**) &data );
2777 
2778  if( layer_num < FIRST_NON_COPPER_LAYER )
2779  layer_num = FIRST_NON_COPPER_LAYER;
2780  else if( layer_num > LAST_NON_COPPER_LAYER )
2781  layer_num = LAST_NON_COPPER_LAYER;
2782 
2783  PCB_TARGET* t = new PCB_TARGET( m_board, shape, leg_layer2new( m_cu_count, layer_num ),
2784  wxPoint( pos_x, pos_y ), size, width );
2785  m_board->Add( t, ADD_MODE::APPEND );
2786 
2787  const_cast<KIID&>( t->m_Uuid ) = KIID( uuid );
2788  }
2789  }
2790 
2791  THROW_IO_ERROR( "Missing '$EndDIMENSION'" );
2792 }
2793 
2794 
2795 BIU LEGACY_PLUGIN::biuParse( const char* aValue, const char** nptrptr )
2796 {
2797  char* nptr;
2798 
2799  errno = 0;
2800 
2801  double fval = strtod( aValue, &nptr );
2802 
2803  if( errno )
2804  {
2805  m_error.Printf( _( "Invalid floating point number in file: '%s'\nline: %d, offset: %d" ),
2806  m_reader->GetSource().GetData(),
2807  m_reader->LineNumber(),
2808  aValue - m_reader->Line() + 1 );
2809 
2811  }
2812 
2813  if( aValue == nptr )
2814  {
2815  m_error.Printf( _( "Missing floating point number in file: '%s'\nline: %d, offset: %d" ),
2816  m_reader->GetSource().GetData(),
2817  m_reader->LineNumber(),
2818  aValue - m_reader->Line() + 1 );
2819 
2821  }
2822 
2823  if( nptrptr )
2824  *nptrptr = nptr;
2825 
2826  fval *= diskToBiu;
2827 
2828  // fval is up into the whole number realm here, and should be bounded
2829  // within INT_MIN to INT_MAX since BIU's are nanometers.
2830  return KiROUND( fval );
2831 }
2832 
2833 
2834 double LEGACY_PLUGIN::degParse( const char* aValue, const char** nptrptr )
2835 {
2836  char* nptr;
2837 
2838  errno = 0;
2839 
2840  double fval = strtod( aValue, &nptr );
2841 
2842  if( errno )
2843  {
2844  m_error.Printf( _( "Invalid floating point number in file: '%s'\nline: %d, offset: %d" ),
2845  m_reader->GetSource().GetData(),
2846  m_reader->LineNumber(),
2847  aValue - m_reader->Line() + 1 );
2848 
2850  }
2851 
2852  if( aValue == nptr )
2853  {
2854  m_error.Printf( _( "Missing floating point number in file: '%s'\nline: %d, offset: %d" ),
2855  m_reader->GetSource().GetData(),
2856  m_reader->LineNumber(),
2857  aValue - m_reader->Line() + 1 );
2858 
2860  }
2861 
2862  if( nptrptr )
2863  *nptrptr = nptr;
2864 
2865  return fval;
2866 }
2867 
2868 
2869 void LEGACY_PLUGIN::init( const PROPERTIES* aProperties )
2870 {
2872  m_cu_count = 16;
2873  m_board = nullptr;
2874  m_showLegacyZoneWarning = true;
2875  m_props = aProperties;
2876 
2877  // conversion factor for saving RAM BIUs to KICAD legacy file format.
2878  biuToDisk = 1.0/IU_PER_MM; // BIUs are nanometers & file is mm
2879 
2880  // Conversion factor for loading KICAD legacy file format into BIUs in RAM
2881  // Start by assuming the *.brd file is in deci-mils.
2882  // If we see "Units mm" in the $GENERAL section, set diskToBiu to 1000000.0
2883  // then, during the file loading process, to start a conversion from
2884  // mm to nanometers. The deci-mil legacy files have no such "Units" marker
2885  // so we must assume the file is in deci-mils until told otherwise.
2886 
2887  diskToBiu = IU_PER_MILS / 10; // BIUs are nanometers
2888 }
2889 
2890 
2891 //-----<FOOTPRINT LIBRARY FUNCTIONS>--------------------------------------------
2892 
2893 /*
2894 
2895  The legacy file format is being obsoleted and this code will have a short
2896  lifetime, so it only needs to be good enough for a short duration of time.
2897  Caching all the MODULEs is a bit memory intensive, but it is a considerably
2898  faster way of fulfilling the API contract. Otherwise, without the cache, you
2899  would have to re-read the file when searching for any FOOTPRINT, and this would
2900  be very problematic filling a FOOTPRINT_LIST via this PLUGIN API. If memory
2901  becomes a concern, consider the cache lifetime policy, which determines the
2902  time that a LP_CACHE is in RAM. Note PLUGIN lifetime also plays a role in
2903  cache lifetime.
2904 
2905 */
2906 
2907 
2908 #include <boost/ptr_container/ptr_map.hpp>
2909 #include <wx/filename.h>
2910 
2911 typedef boost::ptr_map< std::string, FOOTPRINT > FOOTPRINT_MAP;
2912 
2913 
2918 struct LP_CACHE
2919 {
2920  LP_CACHE( LEGACY_PLUGIN* aOwner, const wxString& aLibraryPath );
2921 
2922  // Most all functions in this class throw IO_ERROR exceptions. There are no
2923  // error codes nor user interface calls from here, nor in any PLUGIN.
2924  // Catch these exceptions higher up please.
2925 
2926  void Load();
2927 
2928  void ReadAndVerifyHeader( LINE_READER* aReader );
2929 
2930  void SkipIndex( LINE_READER* aReader );
2931 
2932  void LoadModules( LINE_READER* aReader );
2933 
2934  bool IsModified();
2935  static long long GetTimestamp( const wxString& aLibPath );
2936 
2937  LEGACY_PLUGIN* m_owner; // my owner, I need its LEGACY_PLUGIN::loadFOOTPRINT()
2938  wxString m_lib_path;
2939  FOOTPRINT_MAP m_footprints; // map or tuple of footprint_name vs. FOOTPRINT*
2941 
2942  bool m_cache_dirty; // Stored separately because it's expensive to check
2943  // m_cache_timestamp against all the files.
2944  long long m_cache_timestamp; // A hash of the timestamps for all the footprint
2945  // files.
2946 };
2947 
2948 
2949 LP_CACHE::LP_CACHE( LEGACY_PLUGIN* aOwner, const wxString& aLibraryPath ) :
2950  m_owner( aOwner ),
2951  m_lib_path( aLibraryPath ),
2952  m_writable( true ),
2953  m_cache_dirty( true ),
2954  m_cache_timestamp( 0 )
2955 {
2956 }
2957 
2958 
2960 {
2962 
2963  return m_cache_dirty;
2964 }
2965 
2966 
2967 long long LP_CACHE::GetTimestamp( const wxString& aLibPath )
2968 {
2969  return wxFileName( aLibPath ).GetModificationTime().GetValue().GetValue();
2970 }
2971 
2972 
2974 {
2975  m_cache_dirty = false;
2976 
2977  FILE_LINE_READER reader( m_lib_path );
2978 
2979  ReadAndVerifyHeader( &reader );
2980  SkipIndex( &reader );
2981  LoadModules( &reader );
2982 
2983  // Remember the file modification time of library file when the
2984  // cache snapshot was made, so that in a networked environment we will
2985  // reload the cache as needed.
2987 }
2988 
2989 
2991 {
2992  char* line = aReader->ReadLine();
2993  char* data;
2994 
2995  if( !line )
2996  THROW_IO_ERROR( wxString::Format( _( "File '%s' is empty." ), m_lib_path ) );
2997 
2998  if( !TESTLINE( "PCBNEW-LibModule-V1" ) )
2999  THROW_IO_ERROR( wxString::Format( _( "File '%s' is not a legacy library." ), m_lib_path ) );
3000 
3001  while( ( line = aReader->ReadLine() ) != nullptr )
3002  {
3003  if( TESTLINE( "Units" ) )
3004  {
3005  const char* units = strtok_r( line + SZ( "Units" ), delims, &data );
3006 
3007  if( !strcmp( units, "mm" ) )
3009 
3010  }
3011  else if( TESTLINE( "$INDEX" ) )
3012  {
3013  return;
3014  }
3015  }
3016 }
3017 
3018 
3020 {
3021  // Some broken INDEX sections have more than one section, due to prior bugs.
3022  // So we must read the next line after $EndINDEX tag,
3023  // to see if this is not a new $INDEX tag.
3024  bool exit = false;
3025  char* line = aReader->Line();
3026 
3027  do
3028  {
3029  if( TESTLINE( "$INDEX" ) )
3030  {
3031  exit = false;
3032 
3033  while( ( line = aReader->ReadLine() ) != nullptr )
3034  {
3035  if( TESTLINE( "$EndINDEX" ) )
3036  {
3037  exit = true;
3038  break;
3039  }
3040  }
3041  }
3042  else if( exit )
3043  {
3044  break;
3045  }
3046  } while( ( line = aReader->ReadLine() ) != nullptr );
3047 }
3048 
3049 
3051 {
3052  m_owner->SetReader( aReader );
3053 
3054  char* line = aReader->Line();
3055 
3056  do
3057  {
3058  // test first for the $MODULE, even before reading because of INDEX bug.
3059  if( TESTLINE( "$MODULE" ) )
3060  {
3061  std::unique_ptr<FOOTPRINT> fp_ptr = std::make_unique<FOOTPRINT>( m_owner->m_board );
3062 
3063  std::string footprintName = StrPurge( line + SZ( "$MODULE" ) );
3064 
3065  // The footprint names in legacy libraries can contain the '/' and ':'
3066  // characters which will cause the LIB_ID parser to choke.
3067  ReplaceIllegalFileNameChars( &footprintName );
3068 
3069  // set the footprint name first thing, so exceptions can use name.
3070  fp_ptr->SetFPID( LIB_ID( wxEmptyString, footprintName ) );
3071 
3072  m_owner->loadFOOTPRINT( fp_ptr.get());
3073 
3074  FOOTPRINT* fp = fp_ptr.release(); // exceptions after this are not expected.
3075 
3076  // Not sure why this is asserting on debug builds. The debugger shows the
3077  // strings are the same. If it's not really needed maybe it can be removed.
3078 
3079  /*
3080 
3081  There was a bug in old legacy library management code
3082  (pre-LEGACY_PLUGIN) which was introducing duplicate footprint names
3083  in legacy libraries without notification. To best recover from such
3084  bad libraries, and use them to their fullest, there are a few
3085  strategies that could be used. (Note: footprints must have unique
3086  names to be accepted into this cache.) The strategy used here is to
3087  append a differentiating version counter to the end of the name as:
3088  _v2, _v3, etc.
3089 
3090  */
3091 
3092  FOOTPRINT_MAP::const_iterator it = m_footprints.find( footprintName );
3093 
3094  if( it == m_footprints.end() ) // footprintName is not present in cache yet.
3095  {
3096  if( !m_footprints.insert( footprintName, fp ).second )
3097  {
3098  wxFAIL_MSG( "error doing cache insert using guaranteed unique name" );
3099  }
3100  }
3101  else
3102  {
3103  // Bad library has a duplicate of this footprintName, generate a
3104  // unique footprint name and load it anyway.
3105  bool nameOK = false;
3106  int version = 2;
3107  char buf[48];
3108 
3109  while( !nameOK )
3110  {
3111  std::string newName = footprintName;
3112 
3113  newName += "_v";
3114  sprintf( buf, "%d", version++ );
3115  newName += buf;
3116 
3117  it = m_footprints.find( newName );
3118 
3119  if( it == m_footprints.end() )
3120  {
3121  nameOK = true;
3122 
3123  fp->SetFPID( LIB_ID( wxEmptyString, newName ) );
3124 
3125  if( !m_footprints.insert( newName, fp ).second )
3126  {
3127  wxFAIL_MSG( "error doing cache insert using guaranteed unique name" );
3128  }
3129  }
3130  }
3131  }
3132  }
3133 
3134  } while( ( line = aReader->ReadLine() ) != nullptr );
3135 }
3136 
3137 
3138 long long LEGACY_PLUGIN::GetLibraryTimestamp( const wxString& aLibraryPath ) const
3139 {
3140  return LP_CACHE::GetTimestamp( aLibraryPath );
3141 }
3142 
3143 
3144 void LEGACY_PLUGIN::cacheLib( const wxString& aLibraryPath )
3145 {
3146  if( !m_cache || m_cache->m_lib_path != aLibraryPath || m_cache->IsModified() )
3147  {
3148  // a spectacular episode in memory management:
3149  delete m_cache;
3150  m_cache = new LP_CACHE( this, aLibraryPath );
3151  m_cache->Load();
3152  }
3153 }
3154 
3155 
3156 void LEGACY_PLUGIN::FootprintEnumerate( wxArrayString& aFootprintNames, const wxString& aLibPath,
3157  bool aBestEfforts, const PROPERTIES* aProperties )
3158 {
3159  LOCALE_IO toggle; // toggles on, then off, the C locale.
3160  wxString errorMsg;
3161 
3162  init( aProperties );
3163 
3164  try
3165  {
3166  cacheLib( aLibPath );
3167  }
3168  catch( const IO_ERROR& ioe )
3169  {
3170  errorMsg = ioe.What();
3171  }
3172 
3173  // Some of the files may have been parsed correctly so we want to add the valid files to
3174  // the library.
3175 
3176  for( const auto& footprint : m_cache->m_footprints )
3177  aFootprintNames.Add( FROM_UTF8( footprint.first.c_str() ) );
3178 
3179  if( !errorMsg.IsEmpty() && !aBestEfforts )
3180  THROW_IO_ERROR( errorMsg );
3181 }
3182 
3183 
3184 FOOTPRINT* LEGACY_PLUGIN::FootprintLoad( const wxString& aLibraryPath,
3185  const wxString& aFootprintName, bool aKeepUUID,
3186  const PROPERTIES* aProperties )
3187 {
3188  LOCALE_IO toggle; // toggles on, then off, the C locale.
3189 
3190  init( aProperties );
3191 
3192  cacheLib( aLibraryPath );
3193 
3194  const FOOTPRINT_MAP& footprints = m_cache->m_footprints;
3195  FOOTPRINT_MAP::const_iterator it = footprints.find( TO_UTF8( aFootprintName ) );
3196 
3197  if( it == footprints.end() )
3198  {
3199  return nullptr;
3200  }
3201 
3202  // Return copy of already loaded FOOTPRINT
3203  FOOTPRINT* copy = (FOOTPRINT*) it->second->Duplicate();
3204  copy->SetParent( nullptr );
3205  return copy;
3206 }
3207 
3208 
3209 bool LEGACY_PLUGIN::FootprintLibDelete( const wxString& aLibraryPath,
3210  const PROPERTIES* aProperties )
3211 {
3212  wxFileName fn = aLibraryPath;
3213 
3214  if( !fn.FileExists() )
3215  return false;
3216 
3217  // Some of the more elaborate wxRemoveFile() crap puts up its own wxLog dialog
3218  // we don't want that. we want bare metal portability with no UI here.
3219  if( wxRemove( aLibraryPath ) )
3220  {
3221  THROW_IO_ERROR( wxString::Format( _( "Footprint library '%s' cannot be deleted." ),
3222  aLibraryPath.GetData() ) );
3223  }
3224 
3225  if( m_cache && m_cache->m_lib_path == aLibraryPath )
3226  {
3227  delete m_cache;
3228  m_cache = nullptr;
3229  }
3230 
3231  return true;
3232 }
3233 
3234 
3235 bool LEGACY_PLUGIN::IsFootprintLibWritable( const wxString& aLibraryPath )
3236 {
3237 #if 0 // no support for 32 Cu layers in legacy format
3238  return false;
3239 #else
3240  LOCALE_IO toggle;
3241 
3242  init( nullptr );
3243 
3244  cacheLib( aLibraryPath );
3245 
3246  return m_cache->m_writable;
3247 #endif
3248 }
3249 
3250 
3252  m_cu_count( 16 ), // for FootprintLoad()
3253  m_board( nullptr ),
3254  m_props( nullptr ),
3255  m_progressReporter( nullptr ),
3256  m_lastProgressLine( 0 ),
3257  m_lineCount( 0 ),
3258  m_reader( nullptr ),
3259  m_fp( nullptr ),
3260  m_cache( nullptr )
3261 {
3262  init( nullptr );
3263 }
3264 
3265 
3267 {
3268  delete m_cache;
3269 }
void SetMirrored(bool isMirrored)
Definition: eda_text.h:188
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:750
long long GetLibraryTimestamp(const wxString &aLibraryPath) const override
Generate a timestamp representing all the files in the library (including the library directory).
CITER next(CITER it)
Definition: ptree.cpp:126
NETINFO_ITEM * FindNet(int aNetcode) const
Search for a net with the given netcode.
Definition: board.cpp:1325
ZONE_CONNECTION
How pads are covered by copper in zone.
Definition: zones.h:41
void SetFileFormatVersionAtLoad(int aVersion)
Definition: board.h:288
void SetModified()
Definition: eda_item.cpp:65
static LAYER_NUM layerParse(const char *next, const char **out=nullptr)
Like intParse but returns a LAYER_NUM.
#define SILKSCREEN_N_BACK
An abstract class from which implementation specific LINE_READERs may be derived to read single lines...
Definition: richio.h:80
const UTF8 & GetLibItemName() const
Definition: lib_id.h:104
char * ReadLine() override
Read a line of text into the buffer and increments the line number counter.
Definition: richio.cpp:214
void DoNotShowCheckbox(wxString file, int line)
Checks the 'do not show again' setting for the dialog.
Definition: confirm.cpp:55
EDA_TEXT_HJUSTIFY_T
Definition: eda_text.h:61
void FootprintEnumerate(wxArrayString &aFootprintNames, const wxString &aLibraryPath, bool aBestEfforts, const PROPERTIES *aProperties=nullptr) override
Return a list of footprint names contained within the library at aLibraryPath.
char * Line() const
Return a pointer to the last line that was read in.
Definition: richio.h:117
unsigned LAYER_MSK
virtual void Report(const wxString &aMessage)=0
Display aMessage in the progress bar dialog.
wxPoint m_GridOrigin
origin for grid offsets
Container for project specific data.
Definition: project.h:62
std::list< FP_3DMODEL > & Models()
Definition: footprint.h:182
bool m_LegacyDesignSettingsLoaded
True if the legacy board design settings were loaded from a file.
Definition: board.h:268
static wxString FROM_UTF8(const char *cstring)
Convert a UTF8 encoded C string to a wxString for all wxWidgets build modes.
Definition: macros.h:110
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:226
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:161
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:204
void SetTitleBlock(const TITLE_BLOCK &aTitleBlock)
Definition: board.h:543
void SetEnd(const wxPoint &aEnd)
Definition: pcb_track.h:104
#define LAYER_N_BACK
static constexpr double IU_PER_MM
Mock up a conversion function.
void SetTextAngle(double aAngle) override
Definition: pcb_text.cpp:104
A progress reporter interface for use in multi-threaded environments.
void SetIsPlaced(bool isPlaced)
Definition: footprint.h:312
void SetTextAngle(double aAngle) override
Definition: fp_text.cpp:73
LEGACY_PLUGIN * m_owner
void SetPageSettings(const PAGE_INFO &aPageSettings)
Definition: board.h:536
the 3d code uses this value
Definition: typeinfo.h:79
void SetItalic(bool isItalic)
Definition: eda_text.h:179
Like smd, does not appear on the solder paste layer (default)
#define COMMENT_N
Smd pad, appears on the solder paste layer (default)
#define EDGE_N
#define ADHESIVE_N_FRONT
void SetTextPos(const wxPoint &aPoint)
Definition: eda_text.h:246
static const char delims[]
bool SetType(const wxString &aStandardPageDescriptionName, bool aIsPortrait=false)
Set the name of the page type and also the sizes and margins commonly associated with that type name.
Definition: page_info.cpp:119
double GetOrientation() const
Definition: footprint.h:190
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:224
std::uint32_t EDA_ITEM_FLAGS
static const wxChar Custom[]
"User" defined page type
Definition: page_info.h:77
#define READLINE(rdr)
void SetTextSize(const wxSize &aNewSize)
Definition: eda_text.h:237
static PCB_LAYER_ID leg_layer2new(int cu_count, LAYER_NUM aLayerNum)
bool SetLayerType(PCB_LAYER_ID aLayer, LAYER_T aLayerType)
Change the type of the layer given by aLayer.
Definition: board.cpp:410
void RotatePoint(int *pX, int *pY, double angle)
Definition: trigo.cpp:229
void SetThermalGap(int aGap)
Definition: footprint.h:232
void SetTrackWidth(int aWidth)
Definition: netclass.h:129
#define SOLDERMASK_N_FRONT
A logical library item identifier and consists of various portions much like a URI.
Definition: lib_id.h:51
wxString m_field
reused to stuff FOOTPRINT fields.
A name/value tuple with unique names and optional values.
Definition: properties.h:33
void SetType(TEXT_TYPE aType)
Definition: fp_text.h:140
void SetBoardThickness(int aThickness)
VECTOR2< int > VECTOR2I
Definition: vector2d.h:623
static int intParse(const char *next, const char **out=nullptr)
Parse an ASCII integer string with possible leading whitespace into an integer and updates the pointe...
Hold the information shown in the lower right corner of a plot, printout, or editing view.
Definition: title_block.h:40
PAD_DRILL_SHAPE_T
The set of pad drill shapes, used with PAD::{Set,Get}DrillShape()
Definition: pad_shapes.h:67
VECTOR3D m_Offset
3D model offset (mm)
Definition: footprint.h:95
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp: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:236
#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:229
void SetDescription(const wxString &aDoc)
Definition: footprint.h:198
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:185
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:142
static EDA_TEXT_HJUSTIFY_T horizJustify(const char *horizontal)
bool is_leg_copperlayer_valid(int aCu_Count, LAYER_NUM aLegacyLayerNum)
LSET is a set of PCB_LAYER_IDs.
Definition: layer_ids.h:504
pads are covered by copper
EDA_TEXT_HJUSTIFY_T GetHorizJustify() const
Definition: eda_text.h:198
virtual void SetText(const wxString &aText)
Definition: eda_text.cpp:114
void SetComment(int aIdx, const wxString &aComment)
Definition: title_block.h:101
virtual const wxString What() const
A composite of Problem() and Where()
Definition: exceptions.cpp:30
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:1092
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:1662
void SetVertJustify(EDA_TEXT_VJUSTIFY_T aType)
Definition: eda_text.h:202
A collection of nets and the parameters used to route or test these nets.
Definition: netclass.h:46
void LoadModules(LINE_READER *aReader)
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:93
void loadFP_SHAPE(FOOTPRINT *aFootprint)
void SetCompany(const wxString &aCompany)
Definition: title_block.h:91
void loadMODULE_TEXT(FP_TEXT *aText)
Helper for storing and iterating over GAL_LAYER_IDs.
Definition: layer_ids.h:279
const wxString & GetNetname() const
Definition: netinfo.h:119
void SetLocalClearance(int aClearance)
Definition: footprint.h:210
#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:194
#define ALL_CU_LAYERS
unsigned LEG_MASK
LEGACY_PLUGIN::BIU BIU
void SetFileName(const wxString &aFileName)
Definition: board.h:226
void SetPlotOptions(const PCB_PLOT_PARAMS &aOptions)
Definition: board.h:539
void SetuViaDiameter(int aSize)
Definition: netclass.h:141
double diskToBiu
convert from disk engineering units to BIUs with this scale factor
static LAYER_T ParseType(const char *aType)
Convert a string to a LAYER_T.
Definition: board.cpp: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:68
const KIID m_Uuid
Definition: eda_item.h:475
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
wxString m_Filename
The 3D shape filename in 3D library.
Definition: footprint.h:97
void loadZONE_CONTAINER()
Definition: seg.h:40
Use thermal relief for pads.
void SetFPID(const LIB_ID &aFPID)
Definition: footprint.h:195
bool Add(const NETCLASSPTR &aNetclass)
Add aNetclass and puts it into this NETCLASSES container.
Definition: netclass.cpp:90
void SetWidth(int aWidth)
Definition: pcb_track.h:101
#define LAYER_N_FRONT
static LSET leg_mask2new(int cu_count, unsigned aMask)
Handle the data for a net.
Definition: netinfo.h:64
int Parse(const UTF8 &aId, bool aFix=false)
Parse LIB_ID with the information from aId.
Definition: lib_id.cpp:49
VIATYPE
Definition: pcb_track.h:60
long long m_cache_timestamp
virtual char * ReadLine()=0
Read a line of text into the buffer and increments the line number counter.
VECTOR3D m_Rotation
3D model rotation (degrees)
Definition: footprint.h:94
#define FIRST_LAYER
void SetuViaDrill(int aSize)
Definition: netclass.h:145
static EDA_TEXT_VJUSTIFY_T vertJustify(const char *vertical)
int layerMaskCountSet(LEG_MASK aMask)
Count the number of set layers in the mask.
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:190
void SetHorizJustify(EDA_TEXT_HJUSTIFY_T aType)
Definition: eda_text.h:201
wxString m_lib_path
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:65
void SetLastEditTime(timestamp_t aTime)
Definition: footprint.h:341
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:201
NETCLASS * GetDefault() const
void SetLocked(bool isLocked) override
Set the #MODULE_is_LOCKED bit in the m_ModuleStatus.
Definition: footprint.h:303
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:136
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)
Pads are not covered.
wxPoint GetPosition() const override
Definition: footprint.h:186
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:295
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:159
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:1543
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:135
void ReadAndVerifyHeader(LINE_READER *aReader)
LAYER_T
The allowed types of layers, same as Specctra DSN spec.
Definition: board.h:68
int getNetCode(int aNetCode)
void loadPAD(FOOTPRINT *aFootprint)
void SetPortrait(bool aIsPortrait)
Rotate the paper page 90 degrees.
Definition: page_info.cpp:186
static const int UNCONNECTED
Constant that forces initialization of a netinfo item to the NETINFO_ITEM ORPHANED (typically -1) whe...
Definition: netinfo.h:365
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
Definition: ki_exception.h:75
#define THROW_IO_ERROR(msg)
Definition: ki_exception.h:38
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:133
#define TESTLINE(x)
C string compare test for a specific length of characters.
void loadTrackList(int aStructType)
Read a list of segments (Tracks and Vias, or Segzones)
void SetEnabledLayers(LSET aLayerMask)
A proxy function that calls the correspondent function in m_BoardSettings.
Definition: board.cpp:487
wxPoint m_AuxOrigin
origin for plot exports
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:207
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:221
int GetNetCode() const
Definition: netinfo.h:113
virtual void SetCurrentProgress(double aProgress)=0
Set the progress value to aProgress (0..1).