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