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 case 1:
1956 ignore_unused( intParse( data ) );
1957 break;
1958 case 2:
1959 {
1960 EDA_ANGLE angle = degParse( data );
1961
1962 if( dseg->GetShape() == SHAPE_T::ARC )
1963 dseg->SetArcAngleAndEnd( angle );
1964
1965 break;
1966 }
1967 case 3:
1968 const_cast<KIID&>( dseg->m_Uuid ) = KIID( data );
1969 break;
1970 case 4:
1971 {
1972 // Ignore state data
1973 hexParse( data );
1974 break;
1975 }
1976 // Bezier Control Points
1977 case 5:
1978 x = biuParse( data );
1979 break;
1980 case 6:
1981 y = biuParse( data );
1982 dseg->SetBezierC1( VECTOR2I( x, y ) );
1983 break;
1984 case 7:
1985 x = biuParse( data );
1986 break;
1987 case 8:
1988 y = biuParse( data );
1989 dseg->SetBezierC2( VECTOR2I( x, y ) );
1990 break;
1991
1992 default:
1993 break;
1994 }
1995 }
1996 }
1997 else if( TESTLINE( "$EndDRAWSEGMENT" ) )
1998 {
1999 m_board->Add( dseg.release(), ADD_MODE::APPEND );
2000 return; // preferred exit
2001 }
2002 }
2003
2004 THROW_IO_ERROR( wxT( "Missing '$EndDRAWSEGMENT'" ) );
2005}
2006
2008{
2009 /* a net description is something like
2010 * $EQUIPOT
2011 * Na 5 "/BIT1"
2012 * St ~
2013 * $EndEQUIPOT
2014 */
2015
2016 char buf[1024];
2017
2018 NETINFO_ITEM* net = nullptr;
2019 char* line;
2020 int netCode = 0;
2021
2022 while( ( line = READLINE( m_reader ) ) != nullptr )
2023 {
2024 const char* data;
2025
2026 if( TESTLINE( "Na" ) )
2027 {
2028 // e.g. "Na 58 "/cpu.sch/PAD7"\r\n"
2029
2030 netCode = intParse( line + SZ( "Na" ), &data );
2031
2032 ReadDelimitedText( buf, data, sizeof(buf) );
2033
2034 if( net == nullptr )
2035 {
2037 netCode );
2038 }
2039 else
2040 {
2041 THROW_IO_ERROR( wxT( "Two net definitions in '$EQUIPOT' block" ) );
2042 }
2043 }
2044 else if( TESTLINE( "$EndEQUIPOT" ) )
2045 {
2046 // net 0 should be already in list, so store this net
2047 // if it is not the net 0, or if the net 0 does not exists.
2048 if( net && ( net->GetNetCode() > 0 || m_board->FindNet( 0 ) == nullptr ) )
2049 {
2050 m_board->Add( net );
2051
2052 // Be sure we have room to store the net in m_netCodes
2053 if( (int)m_netCodes.size() <= netCode )
2054 m_netCodes.resize( netCode+1 );
2055
2056 m_netCodes[netCode] = net->GetNetCode();
2057 net = nullptr;
2058 }
2059 else
2060 {
2061 delete net;
2062 net = nullptr; // Avoid double deletion.
2063 }
2064
2065 return; // preferred exit
2066 }
2067 }
2068
2069 // If we are here, there is an error.
2070 delete net;
2071 THROW_IO_ERROR( wxT( "Missing '$EndEQUIPOT'" ) );
2072}
2073
2074
2076{
2077 /* examples:
2078 For a single line text:
2079 ----------------------
2080 $TEXTPCB
2081 Te "Text example"
2082 Po 66750 53450 600 800 150 0
2083 De 24 1 0 Italic
2084 $EndTEXTPCB
2085
2086 For a multi line text:
2087 ---------------------
2088 $TEXTPCB
2089 Te "Text example"
2090 Nl "Line 2"
2091 Po 66750 53450 600 800 150 0
2092 De 24 1 0 Italic
2093 $EndTEXTPCB
2094 Nl "line nn" is a line added to the current text
2095 */
2096
2097 char text[1024];
2098
2099 // maybe someday a constructor that takes all this data in one call?
2100 PCB_TEXT* pcbtxt = new PCB_TEXT( m_board );
2101 m_board->Add( pcbtxt, ADD_MODE::APPEND );
2102
2103 char* line;
2104
2105 while( ( line = READLINE( m_reader ) ) != nullptr )
2106 {
2107 const char* data;
2108
2109 if( TESTLINE( "Te" ) ) // Text line (or first line for multi line texts)
2110 {
2111 ReadDelimitedText( text, line + SZ( "Te" ), sizeof(text) );
2113 }
2114 else if( TESTLINE( "nl" ) ) // next line of the current text
2115 {
2116 ReadDelimitedText( text, line + SZ( "nl" ), sizeof(text) );
2117 pcbtxt->SetText( pcbtxt->GetText() + wxChar( '\n' ) + From_UTF8( text ) );
2118 }
2119 else if( TESTLINE( "Po" ) )
2120 {
2121 VECTOR2I size;
2122 BIU pos_x = biuParse( line + SZ( "Po" ), &data );
2123 BIU pos_y = biuParse( data, &data );
2124
2125 size.x = biuParse( data, &data );
2126 size.y = biuParse( data, &data );
2127
2128 BIU thickn = biuParse( data, &data );
2129 EDA_ANGLE angle = degParse( data );
2130
2131 pcbtxt->SetTextSize( size );
2132 pcbtxt->SetTextThickness( thickn );
2133 pcbtxt->SetTextAngle( angle );
2134
2135 pcbtxt->SetTextPos( VECTOR2I( pos_x, pos_y ) );
2136 }
2137 else if( TESTLINE( "De" ) )
2138 {
2139 // e.g. "De 21 1 68183921-93a5-49ac-91b0-49d05a0e1647 Normal C\r\n"
2140 int layer_num = intParse( line + SZ( "De" ), &data );
2141 int notMirrored = intParse( data, &data );
2142 char* uuid = strtok_r( (char*) data, delims, (char**) &data );
2143 char* style = strtok_r( nullptr, delims, (char**) &data );
2144 char* hJustify = strtok_r( nullptr, delims, (char**) &data );
2145 char* vJustify = strtok_r( nullptr, delims, (char**) &data );
2146
2147 pcbtxt->SetMirrored( !notMirrored );
2148 const_cast<KIID&>( pcbtxt->m_Uuid ) = KIID( uuid );
2149 pcbtxt->SetItalic( !strcmp( style, "Italic" ) );
2150
2151 if( hJustify )
2152 {
2153 pcbtxt->SetHorizJustify( horizJustify( hJustify ) );
2154 }
2155 else
2156 {
2157 // boom, somebody changed a constructor, I was relying on this:
2158 wxASSERT( pcbtxt->GetHorizJustify() == GR_TEXT_H_ALIGN_CENTER );
2159 }
2160
2161 if( vJustify )
2162 pcbtxt->SetVertJustify( vertJustify( vJustify ) );
2163
2164 if( layer_num < FIRST_COPPER_LAYER )
2165 layer_num = FIRST_COPPER_LAYER;
2166 else if( layer_num > LAST_NON_COPPER_LAYER )
2167 layer_num = LAST_NON_COPPER_LAYER;
2168
2169 if( layer_num >= FIRST_NON_COPPER_LAYER ||
2170 is_leg_copperlayer_valid( m_cu_count, layer_num ) )
2171 pcbtxt->SetLayer( leg_layer2new( m_cu_count, layer_num ) );
2172 else // not perfect, but putting this text on front layer is a workaround
2173 pcbtxt->SetLayer( F_Cu );
2174 }
2175 else if( TESTLINE( "$EndTEXTPCB" ) )
2176 {
2177 return; // preferred exit
2178 }
2179 }
2180
2181 THROW_IO_ERROR( wxT( "Missing '$EndTEXTPCB'" ) );
2182}
2183
2184
2186{
2187 char* line;
2188
2189 while( ( line = READLINE( m_reader ) ) != nullptr )
2190 {
2191 checkpoint();
2192
2193 // read two lines per loop iteration, each loop is one TRACK or VIA
2194 // example first line:
2195 // e.g. "Po 0 23994 28800 24400 28800 150 -1" for a track
2196 // e.g. "Po 3 21086 17586 21086 17586 180 -1" for a via (uses sames start and end)
2197 const char* data;
2198
2199 if( line[0] == '$' ) // $EndTRACK
2200 return; // preferred exit
2201
2202 assert( TESTLINE( "Po" ) );
2203
2204 VIATYPE viatype = static_cast<VIATYPE>( intParse( line + SZ( "Po" ), &data ) );
2205 BIU start_x = biuParse( data, &data );
2206 BIU start_y = biuParse( data, &data );
2207 BIU end_x = biuParse( data, &data );
2208 BIU end_y = biuParse( data, &data );
2209 BIU width = biuParse( data, &data );
2210
2211 // optional 7th drill parameter (must be optional in an old format?)
2212 data = strtok_r( (char*) data, delims, (char**) &data );
2213
2214 BIU drill = data ? biuParse( data ) : -1; // SetDefault() if < 0
2215
2216 // Read the 2nd line to determine the exact type, one of:
2217 // PCB_TRACE_T, PCB_VIA_T, or PCB_SEGZONE_T. The type field in 2nd line
2218 // differentiates between PCB_TRACE_T and PCB_VIA_T. With virtual
2219 // functions in use, it is critical to instantiate the PCB_VIA_T
2220 // exactly.
2221 READLINE( m_reader );
2222
2223 line = m_reader->Line();
2224
2225 // example second line:
2226 // "De 0 0 463 0 800000\r\n"
2227
2228#if 1
2229 assert( TESTLINE( "De" ) );
2230#else
2231 if( !TESTLINE( "De" ) )
2232 {
2233 // mandatory 2nd line is missing
2234 THROW_IO_ERROR( wxT( "Missing 2nd line of a TRACK def" ) );
2235 }
2236#endif
2237
2238 int makeType;
2239
2240 // parse the 2nd line to determine the type of object
2241 // e.g. "De 15 1 7 68183921-93a5-49ac-91b0-49d05a0e1647 0" for a via
2242 int layer_num = intParse( line + SZ( "De" ), &data );
2243 int type = intParse( data, &data );
2244 int net_code = intParse( data, &data );
2245 char* uuid = strtok_r( (char*) data, delims, (char**) &data );
2246
2247 // Discard flags data
2248 intParse( data, (const char**) &data );
2249
2250 if( aStructType == PCB_TRACE_T )
2251 {
2252 makeType = ( type == 1 ) ? PCB_VIA_T : PCB_TRACE_T;
2253 }
2254 else if (aStructType == NOT_USED )
2255 {
2256 continue;
2257 }
2258 else
2259 {
2260 wxFAIL_MSG( wxT( "Segment type unknown" ) );
2261 continue;
2262 }
2263
2264 PCB_TRACK* newTrack;
2265
2266 switch( makeType )
2267 {
2268 default:
2269 case PCB_TRACE_T: newTrack = new PCB_TRACK( m_board ); break;
2270 case PCB_VIA_T: newTrack = new PCB_VIA( m_board ); break;
2271 }
2272
2273 const_cast<KIID&>( newTrack->m_Uuid ) = KIID( uuid );
2274 newTrack->SetPosition( VECTOR2I( start_x, start_y ) );
2275 newTrack->SetEnd( VECTOR2I( end_x, end_y ) );
2276
2277 if( makeType == PCB_VIA_T ) // Ensure layers are OK when possible:
2278 {
2279 PCB_VIA *via = static_cast<PCB_VIA*>( newTrack );
2280 via->SetViaType( viatype );
2281 via->SetWidth( PADSTACK::ALL_LAYERS, width );
2282
2283 if( drill < 0 )
2284 via->SetDrillDefault();
2285 else
2286 via->SetDrill( drill );
2287
2288 if( via->GetViaType() == VIATYPE::THROUGH )
2289 {
2290 via->SetLayerPair( F_Cu, B_Cu );
2291 }
2292 else
2293 {
2294 PCB_LAYER_ID back = leg_layer2new( m_cu_count, (layer_num >> 4) & 0xf );
2295 PCB_LAYER_ID front = leg_layer2new( m_cu_count, layer_num & 0xf );
2296
2297 if( is_leg_copperlayer_valid( m_cu_count, back ) &&
2299 {
2300 via->SetLayerPair( front, back );
2301 }
2302 else
2303 {
2304 delete via;
2305 newTrack = nullptr;
2306 }
2307 }
2308 }
2309 else
2310 {
2311 newTrack->SetWidth( width );
2312
2313 // A few legacy boards can have tracks on non existent layers, because
2314 // reducing the number of layers does not remove tracks on removed layers
2315 // If happens, skip them
2316 if( is_leg_copperlayer_valid( m_cu_count, layer_num ) )
2317 {
2318 newTrack->SetLayer( leg_layer2new( m_cu_count, layer_num ) );
2319 }
2320 else
2321 {
2322 delete newTrack;
2323 newTrack = nullptr;
2324 }
2325 }
2326
2327 if( newTrack )
2328 {
2329 newTrack->SetNetCode( getNetCode( net_code ) );
2330
2331 m_board->Add( newTrack );
2332 }
2333 }
2334
2335 THROW_IO_ERROR( wxT( "Missing '$EndTRACK'" ) );
2336}
2337
2338
2340{
2341 char buf[1024];
2342 wxString netname;
2343 char* line;
2344
2345 // create an empty NETCLASS without a name, but do not add it to the BOARD
2346 // yet since that would bypass duplicate netclass name checking within the BOARD.
2347 // store it temporarily in an unique_ptr until successfully inserted into the BOARD
2348 // just before returning.
2349 std::shared_ptr<NETCLASS> nc = std::make_shared<NETCLASS>( wxEmptyString );
2350
2351 while( ( line = READLINE( m_reader ) ) != nullptr )
2352 {
2353 if( TESTLINE( "AddNet" ) ) // most frequent type of line
2354 {
2355 // e.g. "AddNet "V3.3D"\n"
2356 ReadDelimitedText( buf, line + SZ( "AddNet" ), sizeof(buf) );
2357 netname = ConvertToNewOverbarNotation( From_UTF8( buf ) );
2358
2359 m_board->GetDesignSettings().m_NetSettings->SetNetclassPatternAssignment(
2360 netname, nc->GetName() );
2361 }
2362 else if( TESTLINE( "Clearance" ) )
2363 {
2364 BIU tmp = biuParse( line + SZ( "Clearance" ) );
2365 nc->SetClearance( tmp );
2366 }
2367 else if( TESTLINE( "TrackWidth" ) )
2368 {
2369 BIU tmp = biuParse( line + SZ( "TrackWidth" ) );
2370 nc->SetTrackWidth( tmp );
2371 }
2372 else if( TESTLINE( "ViaDia" ) )
2373 {
2374 BIU tmp = biuParse( line + SZ( "ViaDia" ) );
2375 nc->SetViaDiameter( tmp );
2376 }
2377 else if( TESTLINE( "ViaDrill" ) )
2378 {
2379 BIU tmp = biuParse( line + SZ( "ViaDrill" ) );
2380 nc->SetViaDrill( tmp );
2381 }
2382 else if( TESTLINE( "uViaDia" ) )
2383 {
2384 BIU tmp = biuParse( line + SZ( "uViaDia" ) );
2385 nc->SetuViaDiameter( tmp );
2386 }
2387 else if( TESTLINE( "uViaDrill" ) )
2388 {
2389 BIU tmp = biuParse( line + SZ( "uViaDrill" ) );
2390 nc->SetuViaDrill( tmp );
2391 }
2392 else if( TESTLINE( "Name" ) )
2393 {
2394 ReadDelimitedText( buf, line + SZ( "Name" ), sizeof(buf) );
2395 nc->SetName( From_UTF8( buf ) );
2396 }
2397 else if( TESTLINE( "Desc" ) )
2398 {
2399 ReadDelimitedText( buf, line + SZ( "Desc" ), sizeof(buf) );
2400 nc->SetDescription( From_UTF8( buf ) );
2401 }
2402 else if( TESTLINE( "$EndNCLASS" ) )
2403 {
2404 if( m_board->GetDesignSettings().m_NetSettings->HasNetclass( nc->GetName() ) )
2405 {
2406 // Must have been a name conflict, this is a bad board file.
2407 // User may have done a hand edit to the file.
2408
2409 // unique_ptr will delete nc on this code path
2410
2411 m_error.Printf( _( "Duplicate NETCLASS name '%s'." ), nc->GetName() );
2413 }
2414 else
2415 {
2416 m_board->GetDesignSettings().m_NetSettings->SetNetclass( nc->GetName(), nc );
2417 }
2418
2419 return; // preferred exit
2420 }
2421 }
2422
2423 THROW_IO_ERROR( wxT( "Missing '$EndNCLASS'" ) );
2424}
2425
2426
2428{
2429 std::unique_ptr<ZONE> zc = std::make_unique<ZONE>( m_board );
2430
2432 bool endContour = false;
2433 int holeIndex = -1; // -1 is the main outline; holeIndex >= 0 = hole index
2434 char buf[1024];
2435 char* line;
2436
2437 while( ( line = READLINE( m_reader ) ) != nullptr )
2438 {
2439 const char* data;
2440
2441 if( TESTLINE( "ZCorner" ) ) // new corner of the zone outlines found
2442 {
2443 // e.g. "ZCorner 25650 49500 0"
2444 BIU x = biuParse( line + SZ( "ZCorner" ), &data );
2445 BIU y = biuParse( data, &data );
2446
2447 if( endContour )
2448 {
2449 // the previous corner was the last corner of a contour.
2450 // so this corner is the first of a new hole
2451 endContour = false;
2452 zc->NewHole();
2453 holeIndex++;
2454 }
2455
2456 zc->AppendCorner( VECTOR2I( x, y ), holeIndex );
2457
2458 // Is this corner the end of current contour?
2459 // the next corner (if any) will be stored in a new contour (a hole)
2460 // intParse( data )returns 0 = usual corner, 1 = last corner of the current contour:
2461 endContour = intParse( data );
2462 }
2463 else if( TESTLINE( "ZInfo" ) ) // general info found
2464 {
2465 // e.g. 'ZInfo 68183921-93a5-49ac-91b0-49d05a0e1647 310 "COMMON"'
2466 char* uuid = strtok_r( (char*) line + SZ( "ZInfo" ), delims, (char**) &data );
2467 int netcode = intParse( data, &data );
2468
2469 if( ReadDelimitedText( buf, data, sizeof(buf) ) > (int) sizeof(buf) )
2470 THROW_IO_ERROR( wxT( "ZInfo netname too long" ) );
2471
2472 const_cast<KIID&>( zc->m_Uuid ) = KIID( uuid );
2473
2474 // Init the net code only, not the netname, to be sure
2475 // the zone net name is the name read in file.
2476 // (When mismatch, the user will be prompted in DRC, to fix the actual name)
2477 zc->BOARD_CONNECTED_ITEM::SetNetCode( getNetCode( netcode ) );
2478 }
2479 else if( TESTLINE( "ZLayer" ) ) // layer found
2480 {
2481 int layer_num = intParse( line + SZ( "ZLayer" ) );
2482 zc->SetLayer( leg_layer2new( m_cu_count, layer_num ) );
2483 }
2484 else if( TESTLINE( "ZAux" ) ) // aux info found
2485 {
2486 // e.g. "ZAux 7 E"
2487 ignore_unused( intParse( line + SZ( "ZAux" ), &data ) );
2488 char* hopt = strtok_r( (char*) data, delims, (char**) &data );
2489
2490 if( !hopt )
2491 {
2492 m_error.Printf( _( "Bad ZAux for CZONE_CONTAINER '%s'" ),
2493 zc->GetNetname().GetData() );
2495 }
2496
2497 switch( *hopt ) // upper case required
2498 {
2499 case 'N': outline_hatch = ZONE_BORDER_DISPLAY_STYLE::NO_HATCH; break;
2500 case 'E': outline_hatch = ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE; break;
2501 case 'F': outline_hatch = ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_FULL; break;
2502 default:
2503 m_error.Printf( _( "Bad ZAux for CZONE_CONTAINER '%s'" ),
2504 zc->GetNetname().GetData() );
2506 }
2507
2508 // Set hatch mode later, after reading corner outline data
2509 }
2510 else if( TESTLINE( "ZSmoothing" ) )
2511 {
2512 // e.g. "ZSmoothing 0 0"
2513 int smoothing = intParse( line + SZ( "ZSmoothing" ), &data );
2514 BIU cornerRadius = biuParse( data );
2515
2516 if( smoothing >= ZONE_SETTINGS::SMOOTHING_LAST || smoothing < 0 )
2517 {
2518 m_error.Printf( _( "Bad ZSmoothing for CZONE_CONTAINER '%s'" ),
2519 zc->GetNetname().GetData() );
2521 }
2522
2523 zc->SetCornerSmoothingType( smoothing );
2524 zc->SetCornerRadius( cornerRadius );
2525 }
2526 else if( TESTLINE( "ZKeepout" ) )
2527 {
2528 char* token;
2529 zc->SetIsRuleArea( true );
2530 zc->SetDoNotAllowPads( false ); // Not supported in legacy
2531 zc->SetDoNotAllowFootprints( false ); // Not supported in legacy
2532
2533 // e.g. "ZKeepout tracks N vias N pads Y"
2534 token = strtok_r( line + SZ( "ZKeepout" ), delims, (char**) &data );
2535
2536 while( token )
2537 {
2538 if( !strcmp( token, "tracks" ) )
2539 {
2540 token = strtok_r( nullptr, delims, (char**) &data );
2541 zc->SetDoNotAllowTracks( token && *token == 'N' );
2542 }
2543 else if( !strcmp( token, "vias" ) )
2544 {
2545 token = strtok_r( nullptr, delims, (char**) &data );
2546 zc->SetDoNotAllowVias( token && *token == 'N' );
2547 }
2548 else if( !strcmp( token, "copperpour" ) )
2549 {
2550 token = strtok_r( nullptr, delims, (char**) &data );
2551 zc->SetDoNotAllowZoneFills( token && *token == 'N' );
2552 }
2553
2554 token = strtok_r( nullptr, delims, (char**) &data );
2555 }
2556 }
2557 else if( TESTLINE( "ZOptions" ) )
2558 {
2559 // e.g. "ZOptions 0 32 F 200 200"
2560 int fillmode = intParse( line + SZ( "ZOptions" ), &data );
2561 ignore_unused( intParse( data, &data ) );
2562 char fillstate = data[1]; // here e.g. " F"
2563 BIU thermalReliefGap = biuParse( data += 2 , &data ); // +=2 for " F"
2564 BIU thermalReliefCopperBridge = biuParse( data );
2565
2566 if( fillmode)
2567 {
2569 {
2570 wxLogWarning( _( "The legacy segment zone fill mode is no longer supported.\n"
2571 "Zone fills will be converted on a best-effort basis." ) );
2572
2574 }
2575 }
2576
2577 zc->SetFillMode( ZONE_FILL_MODE::POLYGONS );
2578 zc->SetIsFilled( fillstate == 'S' );
2579 zc->SetThermalReliefGap( thermalReliefGap );
2580 zc->SetThermalReliefSpokeWidth( thermalReliefCopperBridge );
2581 }
2582 else if( TESTLINE( "ZClearance" ) ) // Clearance and pad options info found
2583 {
2584 // e.g. "ZClearance 40 I"
2585 BIU clearance = biuParse( line + SZ( "ZClearance" ), &data );
2586 char* padoption = strtok_r( (char*) data, delims, (char**) &data ); // data: " I"
2587
2588 ZONE_CONNECTION popt;
2589 switch( *padoption )
2590 {
2591 case 'I': popt = ZONE_CONNECTION::FULL; break;
2592 case 'T': popt = ZONE_CONNECTION::THERMAL; break;
2593 case 'H': popt = ZONE_CONNECTION::THT_THERMAL; break;
2594 case 'X': popt = ZONE_CONNECTION::NONE; break;
2595 default:
2596 m_error.Printf( _( "Bad ZClearance padoption for CZONE_CONTAINER '%s'" ),
2597 zc->GetNetname().GetData() );
2599 }
2600
2601 zc->SetLocalClearance( clearance );
2602 zc->SetPadConnection( popt );
2603 }
2604 else if( TESTLINE( "ZMinThickness" ) )
2605 {
2606 BIU thickness = biuParse( line + SZ( "ZMinThickness" ) );
2607 zc->SetMinThickness( thickness );
2608 }
2609 else if( TESTLINE( "ZPriority" ) )
2610 {
2611 int priority = intParse( line + SZ( "ZPriority" ) );
2612 zc->SetAssignedPriority( priority );
2613 }
2614 else if( TESTLINE( "$POLYSCORNERS" ) )
2615 {
2616 // Read the PolysList (polygons that are the solid areas in the filled zone)
2617 SHAPE_POLY_SET polysList;
2618
2619 bool makeNewOutline = true;
2620
2621 while( ( line = READLINE( m_reader ) ) != nullptr )
2622 {
2623 if( TESTLINE( "$endPOLYSCORNERS" ) )
2624 break;
2625
2626 // e.g. "39610 43440 0 0"
2627 BIU x = biuParse( line, &data );
2628 BIU y = biuParse( data, &data );
2629
2630 if( makeNewOutline )
2631 polysList.NewOutline();
2632
2633 polysList.Append( x, y );
2634
2635 // end_countour was a bool when file saved, so '0' or '1' here
2636 bool end_contour = intParse( data, &data );
2637 intParse( data ); // skip corner utility flag
2638
2639 makeNewOutline = end_contour;
2640 }
2641
2642 zc->SetFilledPolysList( zc->GetFirstLayer(), polysList );
2643 }
2644 else if( TESTLINE( "$FILLSEGMENTS" ) )
2645 {
2646 while( ( line = READLINE( m_reader ) ) != nullptr )
2647 {
2648 if( TESTLINE( "$endFILLSEGMENTS" ) )
2649 break;
2650
2651 // e.g. ""%d %d %d %d\n"
2652 ignore_unused( biuParse( line, &data ) );
2653 ignore_unused( biuParse( data, &data ) );
2654 ignore_unused( biuParse( data, &data ) );
2655 ignore_unused( biuParse( data ) );
2656 }
2657 }
2658 else if( TESTLINE( "$endCZONE_OUTLINE" ) )
2659 {
2660 // Ensure keepout does not have a net
2661 // (which have no sense for a keepout zone)
2662 if( zc->GetIsRuleArea() )
2663 zc->SetNetCode( NETINFO_LIST::UNCONNECTED );
2664
2665 if( zc->GetMinThickness() > 0 )
2666 {
2667 // Inflate the fill polygon
2668 PCB_LAYER_ID layer = zc->GetFirstLayer();
2669 SHAPE_POLY_SET inflatedFill = SHAPE_POLY_SET( *zc->GetFilledPolysList( layer ) );
2670
2671 inflatedFill.InflateWithLinkedHoles( zc->GetMinThickness() / 2,
2673 ARC_HIGH_DEF / 2 );
2674
2675 zc->SetFilledPolysList( layer, inflatedFill );
2676 }
2677
2678 // should always occur, but who knows, a zone without two corners
2679 // is no zone at all, it's a spot?
2680
2681 if( zc->GetNumCorners() > 2 )
2682 {
2683 if( !zc->IsOnCopperLayer() )
2684 {
2685 zc->SetFillMode( ZONE_FILL_MODE::POLYGONS );
2686 zc->SetNetCode( NETINFO_LIST::UNCONNECTED );
2687 }
2688
2689 // HatchBorder here, after outlines corners are read
2690 // Set hatch here, after outlines corners are read
2691 zc->SetBorderDisplayStyle( outline_hatch, ZONE::GetDefaultHatchPitch(), true );
2692
2693 m_board->Add( zc.release() );
2694 }
2695
2696 return; // preferred exit
2697 }
2698 }
2699
2700 THROW_IO_ERROR( wxT( "Missing '$endCZONE_OUTLINE'" ) );
2701}
2702
2703
2705{
2706 std::unique_ptr<PCB_DIM_ALIGNED> dim = std::make_unique<PCB_DIM_ALIGNED>( m_board,
2708 VECTOR2I crossBarO;
2709 VECTOR2I crossBarF;
2710
2711 char* line;
2712
2713 while( ( line = READLINE( m_reader ) ) != nullptr )
2714 {
2715 const char* data;
2716
2717 if( TESTLINE( "$endCOTATION" ) )
2718 {
2719 dim->UpdateHeight( crossBarF, crossBarO );
2720
2721 m_board->Add( dim.release(), ADD_MODE::APPEND );
2722 return; // preferred exit
2723 }
2724 else if( TESTLINE( "Va" ) )
2725 {
2726 BIU value = biuParse( line + SZ( "Va" ) );
2727
2728 // unused; dimension value is calculated from coordinates
2729 ( void )value;
2730 }
2731 else if( TESTLINE( "Ge" ) )
2732 {
2733 // e.g. "Ge 1 21 68183921-93a5-49ac-91b0-49d05a0e1647\r\n"
2734 int shape = intParse( line + SZ( "De" ), (const char**) &data );
2735 int layer_num = intParse( data, &data );
2736 char* uuid = strtok_r( (char*) data, delims, (char**) &data );
2737
2738 dim->SetLayer( leg_layer2new( m_cu_count, layer_num ) );
2739 const_cast<KIID&>( dim->m_Uuid ) = KIID( uuid );
2740
2741 // not used
2742 ( void )shape;
2743 }
2744 else if( TESTLINE( "Te" ) )
2745 {
2746 char buf[2048];
2747
2748 ReadDelimitedText( buf, line + SZ( "Te" ), sizeof(buf) );
2749 dim->SetOverrideText( From_UTF8( buf ) );
2750 dim->SetOverrideTextEnabled( true );
2751 dim->SetUnitsFormat( DIM_UNITS_FORMAT::NO_SUFFIX );
2752 dim->SetAutoUnits();
2753 }
2754 else if( TESTLINE( "Po" ) )
2755 {
2756 BIU pos_x = biuParse( line + SZ( "Po" ), &data );
2757 BIU pos_y = biuParse( data, &data );
2758 BIU width = biuParse( data, &data );
2759 BIU height = biuParse( data, &data );
2760 BIU thickn = biuParse( data, &data );
2761 EDA_ANGLE orient = degParse( data, &data );
2762 char* mirror = strtok_r( (char*) data, delims, (char**) &data );
2763
2764 dim->SetTextPos( VECTOR2I( pos_x, pos_y ) );
2765 dim->SetTextSize( VECTOR2I( width, height ) );
2766 dim->SetMirrored( mirror && *mirror == '0' );
2767 dim->SetTextThickness( thickn );
2768 dim->SetTextAngle( orient );
2769 }
2770 else if( TESTLINE( "Sb" ) )
2771 {
2772 ignore_unused( biuParse( line + SZ( "Sb" ), &data ) );
2773 BIU crossBarOx = biuParse( data, &data );
2774 BIU crossBarOy = biuParse( data, &data );
2775 BIU crossBarFx = biuParse( data, &data );
2776 BIU crossBarFy = biuParse( data, &data );
2777 BIU width = biuParse( data );
2778
2779 dim->SetLineThickness( width );
2780 crossBarO = VECTOR2I( crossBarOx, crossBarOy );
2781 crossBarF = VECTOR2I( crossBarFx, crossBarFy );
2782 }
2783 else if( TESTLINE( "Sd" ) )
2784 {
2785 ignore_unused( intParse( line + SZ( "Sd" ), &data ) );
2786 BIU featureLineDOx = biuParse( data, &data );
2787 BIU featureLineDOy = biuParse( data, &data );
2788
2789 ignore_unused( biuParse( data, &data ) );
2790 ignore_unused( biuParse( data ) );
2791
2792 dim->SetStart( VECTOR2I( featureLineDOx, featureLineDOy ) );
2793 }
2794 else if( TESTLINE( "Sg" ) )
2795 {
2796 ignore_unused( intParse( line + SZ( "Sg" ), &data ) );
2797 BIU featureLineGOx = biuParse( data, &data );
2798 BIU featureLineGOy = biuParse( data, &data );
2799
2800 ignore_unused( biuParse( data, &data ) );
2801 ignore_unused( biuParse( data ) );
2802
2803 dim->SetEnd( VECTOR2I( featureLineGOx, featureLineGOy ) );
2804 }
2805 else if( TESTLINE( "S1" ) ) // Arrow: no longer imported
2806 {
2807 ignore_unused( intParse( line + SZ( "S1" ), &data ) );
2808 biuParse( data, &data ); // skipping excessive data
2809 biuParse( data, &data ); // skipping excessive data
2810 biuParse( data, &data );
2811 biuParse( data );
2812 }
2813 else if( TESTLINE( "S2" ) ) // Arrow: no longer imported
2814 {
2815 ignore_unused( intParse( line + SZ( "S2" ), &data ) );
2816 biuParse( data, &data ); // skipping excessive data
2817 biuParse( data, &data ); // skipping excessive data
2818 biuParse( data, &data );
2819 biuParse( data, &data );
2820 }
2821 else if( TESTLINE( "S3" ) ) // Arrow: no longer imported
2822 {
2823 ignore_unused( intParse( line + SZ( "S3" ), &data ) );
2824 biuParse( data, &data ); // skipping excessive data
2825 biuParse( data, &data ); // skipping excessive data
2826 biuParse( data, &data );
2827 biuParse( data, &data );
2828 }
2829 else if( TESTLINE( "S4" ) ) // Arrow: no longer imported
2830 {
2831 ignore_unused( intParse( line + SZ( "S4" ), &data ) );
2832 biuParse( data, &data ); // skipping excessive data
2833 biuParse( data, &data ); // skipping excessive data
2834 biuParse( data, &data );
2835 biuParse( data, &data );
2836 }
2837 }
2838
2839 THROW_IO_ERROR( wxT( "Missing '$endCOTATION'" ) );
2840}
2841
2842
2844{
2845 char* line;
2846
2847 while( ( line = READLINE( m_reader ) ) != nullptr )
2848 {
2849 const char* data;
2850
2851 if( TESTLINE( "$EndPCB_TARGET" ) || TESTLINE( "$EndMIREPCB" ) )
2852 {
2853 return; // preferred exit
2854 }
2855 else if( TESTLINE( "Po" ) )
2856 {
2857 int shape = intParse( line + SZ( "Po" ), &data );
2858 int layer_num = intParse( data, &data );
2859 BIU pos_x = biuParse( data, &data );
2860 BIU pos_y = biuParse( data, &data );
2861 BIU size = biuParse( data, &data );
2862 BIU width = biuParse( data, &data );
2863 char* uuid = strtok_r( (char*) data, delims, (char**) &data );
2864
2865 if( layer_num < FIRST_NON_COPPER_LAYER )
2866 layer_num = FIRST_NON_COPPER_LAYER;
2867 else if( layer_num > LAST_NON_COPPER_LAYER )
2868 layer_num = LAST_NON_COPPER_LAYER;
2869
2870 PCB_TARGET* t = new PCB_TARGET( m_board, shape, leg_layer2new( m_cu_count, layer_num ),
2871 VECTOR2I( pos_x, pos_y ), size, width );
2872 m_board->Add( t, ADD_MODE::APPEND );
2873
2874 const_cast<KIID&>( t->m_Uuid ) = KIID( uuid );
2875 }
2876 }
2877
2878 THROW_IO_ERROR( wxT( "Missing '$EndDIMENSION'" ) );
2879}
2880
2881
2882BIU PCB_IO_KICAD_LEGACY::biuParse( const char* aValue, const char** nptrptr )
2883{
2884 const char* end = aValue;
2885 double fval{};
2886 fast_float::from_chars_result result = fast_float::from_chars( aValue, aValue + strlen( aValue ), fval,
2887 fast_float::chars_format::skip_white_space );
2888 end = result.ptr;
2889
2890 if( result.ec != std::errc() )
2891 {
2892 m_error.Printf( _( "Invalid floating point number in file: '%s'\nline: %d, offset: %d" ),
2893 m_reader->GetSource().GetData(),
2894 (int) m_reader->LineNumber(),
2895 (int)( aValue - m_reader->Line() + 1 ) );
2896
2898 }
2899
2900 if( aValue == end )
2901 {
2902 m_error.Printf( _( "Missing floating point number in file: '%s'\nline: %d, offset: %d" ),
2903 m_reader->GetSource().GetData(),
2904 (int) m_reader->LineNumber(),
2905 (int)( aValue - m_reader->Line() + 1 ) );
2906
2908 }
2909
2910 if( nptrptr )
2911 *nptrptr = end;
2912
2913 fval *= diskToBiu;
2914
2915 // fval is up into the whole number realm here, and should be bounded
2916 // within INT_MIN to INT_MAX since BIU's are nanometers.
2917 return KiROUND( fval );
2918}
2919
2920
2921EDA_ANGLE PCB_IO_KICAD_LEGACY::degParse( const char* aValue, const char** nptrptr )
2922{
2923 const char* end = aValue;
2924 double fval{};
2925 fast_float::from_chars_result result = fast_float::from_chars( aValue, aValue + strlen( aValue ), fval,
2926 fast_float::chars_format::skip_white_space );
2927 end = result.ptr;
2928
2929 if( result.ec != std::errc() )
2930 {
2931 m_error.Printf( _( "Invalid floating point number in file: '%s'\nline: %d, offset: %d" ),
2932 m_reader->GetSource().GetData(),
2933 (int) m_reader->LineNumber(),
2934 (int)( aValue - m_reader->Line() + 1 ) );
2935
2937 }
2938
2939 if( aValue == end )
2940 {
2941 m_error.Printf( _( "Missing floating point number in file: '%s'\nline: %d, offset: %d" ),
2942 m_reader->GetSource().GetData(),
2943 (int) m_reader->LineNumber(),
2944 (int)( aValue - m_reader->Line() + 1 ) );
2945
2947 }
2948
2949 if( nptrptr )
2950 *nptrptr = end;
2951
2952 return EDA_ANGLE( fval, TENTHS_OF_A_DEGREE_T );
2953}
2954
2955
2956void PCB_IO_KICAD_LEGACY::init( const std::map<std::string, UTF8>* aProperties )
2957{
2959 m_cu_count = 16;
2960 m_board = nullptr;
2962 m_props = aProperties;
2963
2964 // conversion factor for saving RAM BIUs to KICAD legacy file format.
2965 biuToDisk = 1.0 / pcbIUScale.IU_PER_MM; // BIUs are nanometers & file is mm
2966
2967 // Conversion factor for loading KICAD legacy file format into BIUs in RAM
2968 // Start by assuming the *.brd file is in deci-mils.
2969 // If we see "Units mm" in the $GENERAL section, set diskToBiu to 1000000.0
2970 // then, during the file loading process, to start a conversion from
2971 // mm to nanometers. The deci-mil legacy files have no such "Units" marker
2972 // so we must assume the file is in deci-mils until told otherwise.
2973
2974 diskToBiu = pcbIUScale.IU_PER_MILS / 10; // BIUs are nanometers
2975}
2976
2977
2978//-----<FOOTPRINT LIBRARY FUNCTIONS>--------------------------------------------
2979
2980/*
2981
2982 The legacy file format is being obsoleted and this code will have a short
2983 lifetime, so it only needs to be good enough for a short duration of time.
2984 Caching all the MODULEs is a bit memory intensive, but it is a considerably
2985 faster way of fulfilling the API contract. Otherwise, without the cache, you
2986 would have to re-read the file when searching for any FOOTPRINT, and this would
2987 be very problematic filling a FOOTPRINT_LIST via this PLUGIN API. If memory
2988 becomes a concern, consider the cache lifetime policy, which determines the
2989 time that a LP_CACHE is in RAM. Note PLUGIN lifetime also plays a role in
2990 cache lifetime.
2991
2992*/
2993
2994
2995typedef boost::ptr_map< std::string, FOOTPRINT > FOOTPRINT_MAP;
2996
2997
3003{
3004 LP_CACHE( PCB_IO_KICAD_LEGACY* aOwner, const wxString& aLibraryPath );
3005
3006 // Most all functions in this class throw IO_ERROR exceptions. There are no
3007 // error codes nor user interface calls from here, nor in any PLUGIN.
3008 // Catch these exceptions higher up please.
3009
3010 void Load();
3011
3012 void ReadAndVerifyHeader( LINE_READER* aReader );
3013
3014 void SkipIndex( LINE_READER* aReader );
3015
3016 void LoadModules( LINE_READER* aReader );
3017
3018 bool IsModified();
3019 static long long GetTimestamp( const wxString& aLibPath );
3020
3021 PCB_IO_KICAD_LEGACY* m_owner; // my owner, I need its PCB_IO_KICAD_LEGACY::loadFOOTPRINT()
3022 wxString m_lib_path;
3023 FOOTPRINT_MAP m_footprints; // map or tuple of footprint_name vs. FOOTPRINT*
3025
3026 bool m_cache_dirty; // Stored separately because it's expensive to check
3027 // m_cache_timestamp against all the files.
3028 long long m_cache_timestamp; // A hash of the timestamps for all the footprint
3029 // files.
3030};
3031
3032
3033LP_CACHE::LP_CACHE( PCB_IO_KICAD_LEGACY* aOwner, const wxString& aLibraryPath ) :
3034 m_owner( aOwner ),
3035 m_lib_path( aLibraryPath ),
3036 m_writable( true ),
3037 m_cache_dirty( true ),
3039{
3040}
3041
3042
3049
3050
3051long long LP_CACHE::GetTimestamp( const wxString& aLibPath )
3052{
3053 wxFileName fn( aLibPath );
3054
3055 if( fn.IsFileReadable() && fn.GetModificationTime().IsValid() )
3056 return fn.GetModificationTime().GetValue().GetValue();
3057 else
3058 return 0;
3059}
3060
3061
3063{
3064 m_cache_dirty = false;
3065
3066 FILE_LINE_READER reader( m_lib_path );
3067
3068 ReadAndVerifyHeader( &reader );
3069 SkipIndex( &reader );
3070 LoadModules( &reader );
3071
3072 // Remember the file modification time of library file when the
3073 // cache snapshot was made, so that in a networked environment we will
3074 // reload the cache as needed.
3076}
3077
3078
3080{
3081 char* line = aReader->ReadLine();
3082 char* data;
3083
3084 if( !line )
3085 THROW_IO_ERROR( wxString::Format( _( "File '%s' is empty." ), m_lib_path ) );
3086
3087 if( !TESTLINE( "PCBNEW-LibModule-V1" ) )
3088 THROW_IO_ERROR( wxString::Format( _( "File '%s' is not a legacy library." ), m_lib_path ) );
3089
3090 while( ( line = aReader->ReadLine() ) != nullptr )
3091 {
3092 if( TESTLINE( "Units" ) )
3093 {
3094 const char* units = strtok_r( line + SZ( "Units" ), delims, &data );
3095
3096 if( !strcmp( units, "mm" ) )
3097 m_owner->diskToBiu = pcbIUScale.IU_PER_MM;
3098
3099 }
3100 else if( TESTLINE( "$INDEX" ) )
3101 {
3102 return;
3103 }
3104 }
3105}
3106
3107
3109{
3110 // Some broken INDEX sections have more than one section, due to prior bugs.
3111 // So we must read the next line after $EndINDEX tag,
3112 // to see if this is not a new $INDEX tag.
3113 bool exit = false;
3114 char* line = aReader->Line();
3115
3116 do
3117 {
3118 if( TESTLINE( "$INDEX" ) )
3119 {
3120 exit = false;
3121
3122 while( ( line = aReader->ReadLine() ) != nullptr )
3123 {
3124 if( TESTLINE( "$EndINDEX" ) )
3125 {
3126 exit = true;
3127 break;
3128 }
3129 }
3130 }
3131 else if( exit )
3132 {
3133 break;
3134 }
3135 } while( ( line = aReader->ReadLine() ) != nullptr );
3136}
3137
3138
3140{
3141 m_owner->SetReader( aReader );
3142
3143 char* line = aReader->Line();
3144
3145 do
3146 {
3147 // test first for the $MODULE, even before reading because of INDEX bug.
3148 if( TESTLINE( "$MODULE" ) )
3149 {
3150 std::unique_ptr<FOOTPRINT> fp_ptr = std::make_unique<FOOTPRINT>( m_owner->m_board );
3151
3152 std::string footprintName = StrPurge( line + SZ( "$MODULE" ) );
3153
3154 // The footprint names in legacy libraries can contain the '/' and ':'
3155 // characters which will cause the LIB_ID parser to choke.
3156 ReplaceIllegalFileNameChars( &footprintName );
3157
3158 // set the footprint name first thing, so exceptions can use name.
3159 fp_ptr->SetFPID( LIB_ID( wxEmptyString, footprintName ) );
3160
3161 m_owner->loadFOOTPRINT( fp_ptr.get());
3162
3163 FOOTPRINT* fp = fp_ptr.release(); // exceptions after this are not expected.
3164
3165 // Not sure why this is asserting on debug builds. The debugger shows the
3166 // strings are the same. If it's not really needed maybe it can be removed.
3167
3168 /*
3169
3170 There was a bug in old legacy library management code
3171 (pre-PCB_IO_KICAD_LEGACY) which was introducing duplicate footprint names
3172 in legacy libraries without notification. To best recover from such
3173 bad libraries, and use them to their fullest, there are a few
3174 strategies that could be used. (Note: footprints must have unique
3175 names to be accepted into this cache.) The strategy used here is to
3176 append a differentiating version counter to the end of the name as:
3177 _v2, _v3, etc.
3178
3179 */
3180
3181 FOOTPRINT_MAP::const_iterator it = m_footprints.find( footprintName );
3182
3183 if( it == m_footprints.end() ) // footprintName is not present in cache yet.
3184 {
3185 if( !m_footprints.insert( footprintName, fp ).second )
3186 {
3187 wxFAIL_MSG( wxT( "error doing cache insert using guaranteed unique name" ) );
3188 }
3189 }
3190 else
3191 {
3192 // Bad library has a duplicate of this footprintName, generate a
3193 // unique footprint name and load it anyway.
3194 bool nameOK = false;
3195 int version = 2;
3196 char buf[48];
3197
3198 while( !nameOK )
3199 {
3200 std::string newName = footprintName;
3201
3202 newName += "_v";
3203 snprintf( buf, sizeof(buf), "%d", version++ );
3204 newName += buf;
3205
3206 it = m_footprints.find( newName );
3207
3208 if( it == m_footprints.end() )
3209 {
3210 nameOK = true;
3211
3212 fp->SetFPID( LIB_ID( wxEmptyString, newName ) );
3213
3214 if( !m_footprints.insert( newName, fp ).second )
3215 {
3216 wxFAIL_MSG( wxT( "error doing cache insert using guaranteed unique "
3217 "name" ) );
3218 }
3219 }
3220 }
3221 }
3222 }
3223
3224 } while( ( line = aReader->ReadLine() ) != nullptr );
3225}
3226
3227
3228long long PCB_IO_KICAD_LEGACY::GetLibraryTimestamp( const wxString& aLibraryPath ) const
3229{
3230 return LP_CACHE::GetTimestamp( aLibraryPath );
3231}
3232
3233
3234void PCB_IO_KICAD_LEGACY::cacheLib( const wxString& aLibraryPath )
3235{
3236 if( !m_cache || m_cache->m_lib_path != aLibraryPath || m_cache->IsModified() )
3237 {
3238 // a spectacular episode in memory management:
3239 delete m_cache;
3240 m_cache = new LP_CACHE( this, aLibraryPath );
3241 m_cache->Load();
3242 }
3243}
3244
3245
3246void PCB_IO_KICAD_LEGACY::FootprintEnumerate( wxArrayString& aFootprintNames, const wxString& aLibPath,
3247 bool aBestEfforts, const std::map<std::string, UTF8>* aProperties )
3248{
3249 wxString errorMsg;
3250
3251 init( aProperties );
3252
3253 try
3254 {
3255 cacheLib( aLibPath );
3256 }
3257 catch( const IO_ERROR& ioe )
3258 {
3259 errorMsg = ioe.What();
3260 }
3261
3262 // Some of the files may have been parsed correctly so we want to add the valid files to
3263 // the library.
3264
3265 for( const auto& footprint : m_cache->m_footprints )
3266 aFootprintNames.Add( From_UTF8( footprint.first.c_str() ) );
3267
3268 if( !errorMsg.IsEmpty() && !aBestEfforts )
3269 THROW_IO_ERROR( errorMsg );
3270}
3271
3272
3273FOOTPRINT* PCB_IO_KICAD_LEGACY::FootprintLoad( const wxString& aLibraryPath,
3274 const wxString& aFootprintName, bool aKeepUUID,
3275 const std::map<std::string, UTF8>* aProperties )
3276{
3277 init( aProperties );
3278
3279 cacheLib( aLibraryPath );
3280
3281 const FOOTPRINT_MAP& footprints = m_cache->m_footprints;
3282 FOOTPRINT_MAP::const_iterator it = footprints.find( TO_UTF8( aFootprintName ) );
3283
3284 if( it == footprints.end() )
3285 return nullptr;
3286
3287 // Return copy of already loaded FOOTPRINT
3288 FOOTPRINT* copy = (FOOTPRINT*) it->second->Duplicate( IGNORE_PARENT_GROUP );
3289 copy->SetParent( nullptr );
3290 return copy;
3291}
3292
3293
3294bool PCB_IO_KICAD_LEGACY::DeleteLibrary( const wxString& aLibraryPath,
3295 const std::map<std::string, UTF8>* aProperties )
3296{
3297 wxFileName fn = aLibraryPath;
3298
3299 if( !fn.FileExists() )
3300 return false;
3301
3302 // Some of the more elaborate wxRemoveFile() crap puts up its own wxLog dialog
3303 // we don't want that. we want bare metal portability with no UI here.
3304 if( wxRemove( aLibraryPath ) )
3305 {
3306 THROW_IO_ERROR( wxString::Format( _( "Footprint library '%s' cannot be deleted." ),
3307 aLibraryPath.GetData() ) );
3308 }
3309
3310 if( m_cache && m_cache->m_lib_path == aLibraryPath )
3311 {
3312 delete m_cache;
3313 m_cache = nullptr;
3314 }
3315
3316 return true;
3317}
3318
3319
3320bool PCB_IO_KICAD_LEGACY::IsLibraryWritable( const wxString& aLibraryPath )
3321{
3322 init( nullptr );
3323
3324 cacheLib( aLibraryPath );
3325
3326 return m_cache->m_writable;
3327}
3328
3329
3331 m_cu_count( 16 ), // for FootprintLoad()
3332 m_progressReporter( nullptr ),
3333 m_lastProgressLine( 0 ),
3334 m_lineCount( 0 ),
3335 m_reader( nullptr ),
3336 m_fp( nullptr ),
3337 m_cache( nullptr )
3338{
3339 init( nullptr );
3340}
3341
3342
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:180
@ 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:280
void SetFPRelativePosition(const VECTOR2I &aPos)
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:317
const KIID m_Uuid
Definition eda_item.h:516
void SetTextSize(VECTOR2I aNewSize, bool aEnforceMinTextSize=true)
Definition eda_text.cpp:544
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:589
void SetMirrored(bool isMirrored)
Definition eda_text.cpp:404
void SetVertJustify(GR_TEXT_V_ALIGN_T aType)
Definition eda_text.cpp:428
GR_TEXT_H_ALIGN_T GetHorizJustify() const
Definition eda_text.h:200
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:397
void SetTextThickness(int aWidth)
The TextThickness is that set by the user.
Definition eda_text.cpp:295
virtual void SetText(const wxString &aText)
Definition eda_text.cpp:281
virtual void SetTextAngle(const EDA_ANGLE &aAngle)
Definition eda_text.cpp:310
void SetItalic(bool aItalic)
Set the text to be italic - this will also update the font if needed.
Definition eda_text.cpp:318
void SetHorizJustify(GR_TEXT_H_ALIGN_T aType)
Definition eda_text.cpp:420
A LINE_READER that reads from an open file.
Definition richio.h:185
void Rewind()
Rewind the file and resets the line number back to zero.
Definition richio.h:234
char * ReadLine() override
Read a line of text into the buffer and increments the line number counter.
Definition richio.cpp:249
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:402
GAL_SET & set()
Definition layer_ids.h:418
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:93
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:121
char * Line() const
Return a pointer to the last line that was read in.
Definition richio.h:129
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:365
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:145
void SetDrillSize(const VECTOR2I &aSize)
Definition pad.h:304
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:324
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:318
const std::map< std::string, UTF8 > * m_props
Properties passed via Save() or Load(), no ownership, may be NULL.
Definition pcb_io.h:327
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:148
void SetPosition(const VECTOR2I &aPos) override
Definition pcb_track.h:141
virtual void SetWidth(int aWidth)
Definition pcb_track.h:145
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:1281
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:216
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:81
@ NPTH
like PAD_PTH, but not plated mechanical use only, no connection allowed
Definition padstack.h:87
@ SMD
Smd pad, appears on the solder paste layer (default)
Definition padstack.h:83
@ PTH
Plated through hole pad.
Definition padstack.h:82
@ CONN
Like smd, does not appear on the solder paste layer (default) Note: also has a special attribute in G...
Definition padstack.h:84
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:66
@ THROUGH
Definition pcb_track.h:67
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:101
@ 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