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 <cerrno>
61#include <cmath>
62#include <cstdio>
63#include <cstring>
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
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
494
496 {
497 m_lineCount = 0;
498
499 m_progressReporter->Report( wxString::Format( _( "Loading %s..." ), aFileName ) );
500
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 {
677 }
678 }
679 else if( TESTLINE( "LayerCount" ) )
680 {
681 int tmp = intParse( line + SZ( "LayerCount" ) );
682
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:
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
732
733 saw_LayerCount = true;
734 }
735 }
736 else if( TESTLINE( "BoardThickness" ) )
737 {
738 BIU thickn = biuParse( line + SZ( "BoardThickness" ) );
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( wname == PAGE_INFO::Custom )
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{
905 ZONE_SETTINGS zoneSettings = bds.GetDefaultZoneSettings();
906 std::shared_ptr<NETCLASS> defaultNetclass = bds.m_NetSettings->GetDefaultNetclass();
907 char* line;
908 char* saveptr;
909
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();
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
1098 bds.m_Pad_Master->SetSize( PADSTACK::ALL_LAYERS, VECTOR2I( x, y ) );
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
1465 PAD_DRILL_SHAPE drShape = PAD_DRILL_SHAPE::CIRCLE;
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 = []( const char* str, double& x, double& y, double& z ) -> bool
1817 {
1818 wxString wxStr( str );
1819 wxStr.Trim( false ).Trim( true );
1820
1821 wxStringTokenizer tokenizer( wxStr, wxT( " \t" ), wxTOKEN_STRTOK );
1822
1823 if( !tokenizer.HasMoreTokens() )
1824 return false;
1825
1826 wxString token1 = tokenizer.GetNextToken();
1827
1828 if( !token1.ToCDouble( &x ) || !tokenizer.HasMoreTokens() )
1829 return false;
1830
1831 wxString token2 = tokenizer.GetNextToken();
1832
1833 if( !token2.ToCDouble( &y ) || !tokenizer.HasMoreTokens() )
1834 return false;
1835
1836 wxString token3 = tokenizer.GetNextToken();
1837
1838 if( !token3.ToCDouble( &z ) )
1839 return false;
1840
1841 return true;
1842 };
1843
1844 char* line;
1845
1846 while( ( line = READLINE( m_reader ) ) != nullptr )
1847 {
1848 if( TESTLINE( "Na" ) ) // Shape File Name
1849 {
1850 char buf[512];
1851 ReadDelimitedText( buf, line + SZ( "Na" ), sizeof(buf) );
1852 t3D.m_Filename = buf;
1853 }
1854 else if( TESTLINE( "Sc" ) ) // Scale
1855 {
1856 if (!parseThreeDoubles(line + SZ("Sc"), t3D.m_Scale.x, t3D.m_Scale.y, t3D.m_Scale.z))
1857 {
1858 THROW_IO_ERROR( wxT( "Invalid scale values in 3D model" ) );
1859 }
1860 }
1861 else if( TESTLINE( "Of" ) ) // Offset
1862 {
1863 if (!parseThreeDoubles(line + SZ("Of"), t3D.m_Offset.x, t3D.m_Offset.y, t3D.m_Offset.z))
1864 {
1865 THROW_IO_ERROR( wxT( "Invalid offset values in 3D model" ) );
1866 }
1867 }
1868 else if( TESTLINE( "Ro" ) ) // Rotation
1869 {
1870 if (!parseThreeDoubles(line + SZ("Ro"), t3D.m_Rotation.x, t3D.m_Rotation.y, t3D.m_Rotation.z))
1871 {
1872 THROW_IO_ERROR( wxT( "Invalid rotation values in 3D model" ) );
1873 }
1874 }
1875 else if( TESTLINE( "$EndSHAPE3D" ) )
1876 {
1877 aFootprint->Models().push_back( t3D );
1878 return; // preferred exit
1879 }
1880 }
1881
1882 THROW_IO_ERROR( wxT( "Missing '$EndSHAPE3D'" ) );
1883}
1884
1885
1887{
1888 /* example:
1889 $DRAWSEGMENT
1890 Po 0 57500 -1000 57500 0 150
1891 De 24 0 900 0 0
1892 $EndDRAWSEGMENT
1893 */
1894
1895 std::unique_ptr<PCB_SHAPE> dseg = std::make_unique<PCB_SHAPE>( m_board );
1896
1897 char* line;
1898 char* saveptr;
1899
1900 while( ( line = READLINE( m_reader ) ) != nullptr )
1901 {
1902 const char* data;
1903
1904 if( TESTLINE( "Po" ) )
1905 {
1906 int shape = intParse( line + SZ( "Po" ), &data );
1907 BIU start_x = biuParse( data, &data );
1908 BIU start_y = biuParse( data, &data );
1909 BIU end_x = biuParse( data, &data );
1910 BIU end_y = biuParse( data, &data );
1911 BIU width = biuParse( data );
1912
1913 if( width < 0 )
1914 width = 0;
1915
1916 dseg->SetShape( static_cast<SHAPE_T>( shape ) );
1917 dseg->SetFilled( false );
1918 dseg->SetStroke( STROKE_PARAMS( width, LINE_STYLE::SOLID ) );
1919
1920 if( dseg->GetShape() == SHAPE_T::ARC )
1921 {
1922 dseg->SetCenter( VECTOR2I( start_x, start_y ) );
1923 dseg->SetStart( VECTOR2I( end_x, end_y ) );
1924 }
1925 else
1926 {
1927 dseg->SetStart( VECTOR2I( start_x, start_y ) );
1928 dseg->SetEnd( VECTOR2I( end_x, end_y ) );
1929 }
1930 }
1931 else if( TESTLINE( "De" ) )
1932 {
1933 BIU x = 0;
1934 BIU y;
1935
1936 data = strtok_r( line + SZ( "De" ), delims, &saveptr );
1937
1938 for( int i = 0; data; ++i, data = strtok_r( nullptr, delims, &saveptr ) )
1939 {
1940 switch( i )
1941 {
1942 case 0:
1943 int layer;
1944 layer = intParse( data );
1945
1946 if( layer < FIRST_NON_COPPER_LAYER )
1947 layer = FIRST_NON_COPPER_LAYER;
1948
1949 else if( layer > LAST_NON_COPPER_LAYER )
1950 layer = LAST_NON_COPPER_LAYER;
1951
1952 dseg->SetLayer( leg_layer2new( m_cu_count, layer ) );
1953 break;
1954 case 1:
1955 ignore_unused( intParse( data ) );
1956 break;
1957 case 2:
1958 {
1959 EDA_ANGLE angle = degParse( data );
1960
1961 if( dseg->GetShape() == SHAPE_T::ARC )
1962 dseg->SetArcAngleAndEnd( angle );
1963
1964 break;
1965 }
1966 case 3:
1967 const_cast<KIID&>( dseg->m_Uuid ) = KIID( data );
1968 break;
1969 case 4:
1970 {
1971 // Ignore state data
1972 hexParse( data );
1973 break;
1974 }
1975 // Bezier Control Points
1976 case 5:
1977 x = biuParse( data );
1978 break;
1979 case 6:
1980 y = biuParse( data );
1981 dseg->SetBezierC1( VECTOR2I( x, y ) );
1982 break;
1983 case 7:
1984 x = biuParse( data );
1985 break;
1986 case 8:
1987 y = biuParse( data );
1988 dseg->SetBezierC2( VECTOR2I( x, y ) );
1989 break;
1990
1991 default:
1992 break;
1993 }
1994 }
1995 }
1996 else if( TESTLINE( "$EndDRAWSEGMENT" ) )
1997 {
1998 m_board->Add( dseg.release(), ADD_MODE::APPEND );
1999 return; // preferred exit
2000 }
2001 }
2002
2003 THROW_IO_ERROR( wxT( "Missing '$EndDRAWSEGMENT'" ) );
2004}
2005
2007{
2008 /* a net description is something like
2009 * $EQUIPOT
2010 * Na 5 "/BIT1"
2011 * St ~
2012 * $EndEQUIPOT
2013 */
2014
2015 char buf[1024];
2016
2017 NETINFO_ITEM* net = nullptr;
2018 char* line;
2019 int netCode = 0;
2020
2021 while( ( line = READLINE( m_reader ) ) != nullptr )
2022 {
2023 const char* data;
2024
2025 if( TESTLINE( "Na" ) )
2026 {
2027 // e.g. "Na 58 "/cpu.sch/PAD7"\r\n"
2028
2029 netCode = intParse( line + SZ( "Na" ), &data );
2030
2031 ReadDelimitedText( buf, data, sizeof(buf) );
2032
2033 if( net == nullptr )
2034 {
2036 netCode );
2037 }
2038 else
2039 {
2040 THROW_IO_ERROR( wxT( "Two net definitions in '$EQUIPOT' block" ) );
2041 }
2042 }
2043 else if( TESTLINE( "$EndEQUIPOT" ) )
2044 {
2045 // net 0 should be already in list, so store this net
2046 // if it is not the net 0, or if the net 0 does not exists.
2047 if( net && ( net->GetNetCode() > 0 || m_board->FindNet( 0 ) == nullptr ) )
2048 {
2049 m_board->Add( net );
2050
2051 // Be sure we have room to store the net in m_netCodes
2052 if( (int)m_netCodes.size() <= netCode )
2053 m_netCodes.resize( netCode+1 );
2054
2055 m_netCodes[netCode] = net->GetNetCode();
2056 net = nullptr;
2057 }
2058 else
2059 {
2060 delete net;
2061 net = nullptr; // Avoid double deletion.
2062 }
2063
2064 return; // preferred exit
2065 }
2066 }
2067
2068 // If we are here, there is an error.
2069 delete net;
2070 THROW_IO_ERROR( wxT( "Missing '$EndEQUIPOT'" ) );
2071}
2072
2073
2075{
2076 /* examples:
2077 For a single line text:
2078 ----------------------
2079 $TEXTPCB
2080 Te "Text example"
2081 Po 66750 53450 600 800 150 0
2082 De 24 1 0 Italic
2083 $EndTEXTPCB
2084
2085 For a multi line text:
2086 ---------------------
2087 $TEXTPCB
2088 Te "Text example"
2089 Nl "Line 2"
2090 Po 66750 53450 600 800 150 0
2091 De 24 1 0 Italic
2092 $EndTEXTPCB
2093 Nl "line nn" is a line added to the current text
2094 */
2095
2096 char text[1024];
2097
2098 // maybe someday a constructor that takes all this data in one call?
2099 PCB_TEXT* pcbtxt = new PCB_TEXT( m_board );
2100 m_board->Add( pcbtxt, ADD_MODE::APPEND );
2101
2102 char* line;
2103
2104 while( ( line = READLINE( m_reader ) ) != nullptr )
2105 {
2106 const char* data;
2107
2108 if( TESTLINE( "Te" ) ) // Text line (or first line for multi line texts)
2109 {
2110 ReadDelimitedText( text, line + SZ( "Te" ), sizeof(text) );
2112 }
2113 else if( TESTLINE( "nl" ) ) // next line of the current text
2114 {
2115 ReadDelimitedText( text, line + SZ( "nl" ), sizeof(text) );
2116 pcbtxt->SetText( pcbtxt->GetText() + wxChar( '\n' ) + From_UTF8( text ) );
2117 }
2118 else if( TESTLINE( "Po" ) )
2119 {
2120 VECTOR2I size;
2121 BIU pos_x = biuParse( line + SZ( "Po" ), &data );
2122 BIU pos_y = biuParse( data, &data );
2123
2124 size.x = biuParse( data, &data );
2125 size.y = biuParse( data, &data );
2126
2127 BIU thickn = biuParse( data, &data );
2128 EDA_ANGLE angle = degParse( data );
2129
2130 pcbtxt->SetTextSize( size );
2131 pcbtxt->SetTextThickness( thickn );
2132 pcbtxt->SetTextAngle( angle );
2133
2134 pcbtxt->SetTextPos( VECTOR2I( pos_x, pos_y ) );
2135 }
2136 else if( TESTLINE( "De" ) )
2137 {
2138 // e.g. "De 21 1 68183921-93a5-49ac-91b0-49d05a0e1647 Normal C\r\n"
2139 int layer_num = intParse( line + SZ( "De" ), &data );
2140 int notMirrored = intParse( data, &data );
2141 char* uuid = strtok_r( (char*) data, delims, (char**) &data );
2142 char* style = strtok_r( nullptr, delims, (char**) &data );
2143 char* hJustify = strtok_r( nullptr, delims, (char**) &data );
2144 char* vJustify = strtok_r( nullptr, delims, (char**) &data );
2145
2146 pcbtxt->SetMirrored( !notMirrored );
2147 const_cast<KIID&>( pcbtxt->m_Uuid ) = KIID( uuid );
2148 pcbtxt->SetItalic( !strcmp( style, "Italic" ) );
2149
2150 if( hJustify )
2151 {
2152 pcbtxt->SetHorizJustify( horizJustify( hJustify ) );
2153 }
2154 else
2155 {
2156 // boom, somebody changed a constructor, I was relying on this:
2157 wxASSERT( pcbtxt->GetHorizJustify() == GR_TEXT_H_ALIGN_CENTER );
2158 }
2159
2160 if( vJustify )
2161 pcbtxt->SetVertJustify( vertJustify( vJustify ) );
2162
2163 if( layer_num < FIRST_COPPER_LAYER )
2164 layer_num = FIRST_COPPER_LAYER;
2165 else if( layer_num > LAST_NON_COPPER_LAYER )
2166 layer_num = LAST_NON_COPPER_LAYER;
2167
2168 if( layer_num >= FIRST_NON_COPPER_LAYER ||
2169 is_leg_copperlayer_valid( m_cu_count, layer_num ) )
2170 pcbtxt->SetLayer( leg_layer2new( m_cu_count, layer_num ) );
2171 else // not perfect, but putting this text on front layer is a workaround
2172 pcbtxt->SetLayer( F_Cu );
2173 }
2174 else if( TESTLINE( "$EndTEXTPCB" ) )
2175 {
2176 return; // preferred exit
2177 }
2178 }
2179
2180 THROW_IO_ERROR( wxT( "Missing '$EndTEXTPCB'" ) );
2181}
2182
2183
2185{
2186 char* line;
2187
2188 while( ( line = READLINE( m_reader ) ) != nullptr )
2189 {
2190 checkpoint();
2191
2192 // read two lines per loop iteration, each loop is one TRACK or VIA
2193 // example first line:
2194 // e.g. "Po 0 23994 28800 24400 28800 150 -1" for a track
2195 // e.g. "Po 3 21086 17586 21086 17586 180 -1" for a via (uses sames start and end)
2196 const char* data;
2197
2198 if( line[0] == '$' ) // $EndTRACK
2199 return; // preferred exit
2200
2201 assert( TESTLINE( "Po" ) );
2202
2203 VIATYPE viatype = static_cast<VIATYPE>( intParse( line + SZ( "Po" ), &data ) );
2204 BIU start_x = biuParse( data, &data );
2205 BIU start_y = biuParse( data, &data );
2206 BIU end_x = biuParse( data, &data );
2207 BIU end_y = biuParse( data, &data );
2208 BIU width = biuParse( data, &data );
2209
2210 // optional 7th drill parameter (must be optional in an old format?)
2211 data = strtok_r( (char*) data, delims, (char**) &data );
2212
2213 BIU drill = data ? biuParse( data ) : -1; // SetDefault() if < 0
2214
2215 // Read the 2nd line to determine the exact type, one of:
2216 // PCB_TRACE_T, PCB_VIA_T, or PCB_SEGZONE_T. The type field in 2nd line
2217 // differentiates between PCB_TRACE_T and PCB_VIA_T. With virtual
2218 // functions in use, it is critical to instantiate the PCB_VIA_T
2219 // exactly.
2220 READLINE( m_reader );
2221
2222 line = m_reader->Line();
2223
2224 // example second line:
2225 // "De 0 0 463 0 800000\r\n"
2226
2227#if 1
2228 assert( TESTLINE( "De" ) );
2229#else
2230 if( !TESTLINE( "De" ) )
2231 {
2232 // mandatory 2nd line is missing
2233 THROW_IO_ERROR( wxT( "Missing 2nd line of a TRACK def" ) );
2234 }
2235#endif
2236
2237 int makeType;
2238
2239 // parse the 2nd line to determine the type of object
2240 // e.g. "De 15 1 7 68183921-93a5-49ac-91b0-49d05a0e1647 0" for a via
2241 int layer_num = intParse( line + SZ( "De" ), &data );
2242 int type = intParse( data, &data );
2243 int net_code = intParse( data, &data );
2244 char* uuid = strtok_r( (char*) data, delims, (char**) &data );
2245
2246 // Discard flags data
2247 intParse( data, (const char**) &data );
2248
2249 if( aStructType == PCB_TRACE_T )
2250 {
2251 makeType = ( type == 1 ) ? PCB_VIA_T : PCB_TRACE_T;
2252 }
2253 else if (aStructType == NOT_USED )
2254 {
2255 continue;
2256 }
2257 else
2258 {
2259 wxFAIL_MSG( wxT( "Segment type unknown" ) );
2260 continue;
2261 }
2262
2263 PCB_TRACK* newTrack;
2264
2265 switch( makeType )
2266 {
2267 default:
2268 case PCB_TRACE_T: newTrack = new PCB_TRACK( m_board ); break;
2269 case PCB_VIA_T: newTrack = new PCB_VIA( m_board ); break;
2270 }
2271
2272 const_cast<KIID&>( newTrack->m_Uuid ) = KIID( uuid );
2273 newTrack->SetPosition( VECTOR2I( start_x, start_y ) );
2274 newTrack->SetEnd( VECTOR2I( end_x, end_y ) );
2275
2276 if( makeType == PCB_VIA_T ) // Ensure layers are OK when possible:
2277 {
2278 PCB_VIA *via = static_cast<PCB_VIA*>( newTrack );
2279 via->SetViaType( viatype );
2280 via->SetWidth( PADSTACK::ALL_LAYERS, width );
2281
2282 if( drill < 0 )
2283 via->SetDrillDefault();
2284 else
2285 via->SetDrill( drill );
2286
2287 if( via->GetViaType() == VIATYPE::THROUGH )
2288 {
2289 via->SetLayerPair( F_Cu, B_Cu );
2290 }
2291 else
2292 {
2293 PCB_LAYER_ID back = leg_layer2new( m_cu_count, (layer_num >> 4) & 0xf );
2294 PCB_LAYER_ID front = leg_layer2new( m_cu_count, layer_num & 0xf );
2295
2296 if( is_leg_copperlayer_valid( m_cu_count, back ) &&
2298 {
2299 via->SetLayerPair( front, back );
2300 }
2301 else
2302 {
2303 delete via;
2304 newTrack = nullptr;
2305 }
2306 }
2307 }
2308 else
2309 {
2310 newTrack->SetWidth( width );
2311
2312 // A few legacy boards can have tracks on non existent layers, because
2313 // reducing the number of layers does not remove tracks on removed layers
2314 // If happens, skip them
2315 if( is_leg_copperlayer_valid( m_cu_count, layer_num ) )
2316 {
2317 newTrack->SetLayer( leg_layer2new( m_cu_count, layer_num ) );
2318 }
2319 else
2320 {
2321 delete newTrack;
2322 newTrack = nullptr;
2323 }
2324 }
2325
2326 if( newTrack )
2327 {
2328 newTrack->SetNetCode( getNetCode( net_code ) );
2329
2330 m_board->Add( newTrack );
2331 }
2332 }
2333
2334 THROW_IO_ERROR( wxT( "Missing '$EndTRACK'" ) );
2335}
2336
2337
2339{
2340 char buf[1024];
2341 wxString netname;
2342 char* line;
2343
2344 // create an empty NETCLASS without a name, but do not add it to the BOARD
2345 // yet since that would bypass duplicate netclass name checking within the BOARD.
2346 // store it temporarily in an unique_ptr until successfully inserted into the BOARD
2347 // just before returning.
2348 std::shared_ptr<NETCLASS> nc = std::make_shared<NETCLASS>( wxEmptyString );
2349
2350 while( ( line = READLINE( m_reader ) ) != nullptr )
2351 {
2352 if( TESTLINE( "AddNet" ) ) // most frequent type of line
2353 {
2354 // e.g. "AddNet "V3.3D"\n"
2355 ReadDelimitedText( buf, line + SZ( "AddNet" ), sizeof(buf) );
2356 netname = ConvertToNewOverbarNotation( From_UTF8( buf ) );
2357
2358 m_board->GetDesignSettings().m_NetSettings->SetNetclassPatternAssignment(
2359 netname, nc->GetName() );
2360 }
2361 else if( TESTLINE( "Clearance" ) )
2362 {
2363 BIU tmp = biuParse( line + SZ( "Clearance" ) );
2364 nc->SetClearance( tmp );
2365 }
2366 else if( TESTLINE( "TrackWidth" ) )
2367 {
2368 BIU tmp = biuParse( line + SZ( "TrackWidth" ) );
2369 nc->SetTrackWidth( tmp );
2370 }
2371 else if( TESTLINE( "ViaDia" ) )
2372 {
2373 BIU tmp = biuParse( line + SZ( "ViaDia" ) );
2374 nc->SetViaDiameter( tmp );
2375 }
2376 else if( TESTLINE( "ViaDrill" ) )
2377 {
2378 BIU tmp = biuParse( line + SZ( "ViaDrill" ) );
2379 nc->SetViaDrill( tmp );
2380 }
2381 else if( TESTLINE( "uViaDia" ) )
2382 {
2383 BIU tmp = biuParse( line + SZ( "uViaDia" ) );
2384 nc->SetuViaDiameter( tmp );
2385 }
2386 else if( TESTLINE( "uViaDrill" ) )
2387 {
2388 BIU tmp = biuParse( line + SZ( "uViaDrill" ) );
2389 nc->SetuViaDrill( tmp );
2390 }
2391 else if( TESTLINE( "Name" ) )
2392 {
2393 ReadDelimitedText( buf, line + SZ( "Name" ), sizeof(buf) );
2394 nc->SetName( From_UTF8( buf ) );
2395 }
2396 else if( TESTLINE( "Desc" ) )
2397 {
2398 ReadDelimitedText( buf, line + SZ( "Desc" ), sizeof(buf) );
2399 nc->SetDescription( From_UTF8( buf ) );
2400 }
2401 else if( TESTLINE( "$EndNCLASS" ) )
2402 {
2403 if( m_board->GetDesignSettings().m_NetSettings->HasNetclass( nc->GetName() ) )
2404 {
2405 // Must have been a name conflict, this is a bad board file.
2406 // User may have done a hand edit to the file.
2407
2408 // unique_ptr will delete nc on this code path
2409
2410 m_error.Printf( _( "Duplicate NETCLASS name '%s'." ), nc->GetName() );
2412 }
2413 else
2414 {
2415 m_board->GetDesignSettings().m_NetSettings->SetNetclass( nc->GetName(), nc );
2416 }
2417
2418 return; // preferred exit
2419 }
2420 }
2421
2422 THROW_IO_ERROR( wxT( "Missing '$EndNCLASS'" ) );
2423}
2424
2425
2427{
2428 std::unique_ptr<ZONE> zc = std::make_unique<ZONE>( m_board );
2429
2430 ZONE_BORDER_DISPLAY_STYLE outline_hatch = ZONE_BORDER_DISPLAY_STYLE::NO_HATCH;
2431 bool endContour = false;
2432 int holeIndex = -1; // -1 is the main outline; holeIndex >= 0 = hole index
2433 char buf[1024];
2434 char* line;
2435
2436 while( ( line = READLINE( m_reader ) ) != nullptr )
2437 {
2438 const char* data;
2439
2440 if( TESTLINE( "ZCorner" ) ) // new corner of the zone outlines found
2441 {
2442 // e.g. "ZCorner 25650 49500 0"
2443 BIU x = biuParse( line + SZ( "ZCorner" ), &data );
2444 BIU y = biuParse( data, &data );
2445
2446 if( endContour )
2447 {
2448 // the previous corner was the last corner of a contour.
2449 // so this corner is the first of a new hole
2450 endContour = false;
2451 zc->NewHole();
2452 holeIndex++;
2453 }
2454
2455 zc->AppendCorner( VECTOR2I( x, y ), holeIndex );
2456
2457 // Is this corner the end of current contour?
2458 // the next corner (if any) will be stored in a new contour (a hole)
2459 // intParse( data )returns 0 = usual corner, 1 = last corner of the current contour:
2460 endContour = intParse( data );
2461 }
2462 else if( TESTLINE( "ZInfo" ) ) // general info found
2463 {
2464 // e.g. 'ZInfo 68183921-93a5-49ac-91b0-49d05a0e1647 310 "COMMON"'
2465 char* uuid = strtok_r( (char*) line + SZ( "ZInfo" ), delims, (char**) &data );
2466 int netcode = intParse( data, &data );
2467
2468 if( ReadDelimitedText( buf, data, sizeof(buf) ) > (int) sizeof(buf) )
2469 THROW_IO_ERROR( wxT( "ZInfo netname too long" ) );
2470
2471 const_cast<KIID&>( zc->m_Uuid ) = KIID( uuid );
2472
2473 // Init the net code only, not the netname, to be sure
2474 // the zone net name is the name read in file.
2475 // (When mismatch, the user will be prompted in DRC, to fix the actual name)
2476 zc->BOARD_CONNECTED_ITEM::SetNetCode( getNetCode( netcode ) );
2477 }
2478 else if( TESTLINE( "ZLayer" ) ) // layer found
2479 {
2480 int layer_num = intParse( line + SZ( "ZLayer" ) );
2481 zc->SetLayer( leg_layer2new( m_cu_count, layer_num ) );
2482 }
2483 else if( TESTLINE( "ZAux" ) ) // aux info found
2484 {
2485 // e.g. "ZAux 7 E"
2486 ignore_unused( intParse( line + SZ( "ZAux" ), &data ) );
2487 char* hopt = strtok_r( (char*) data, delims, (char**) &data );
2488
2489 if( !hopt )
2490 {
2491 m_error.Printf( _( "Bad ZAux for CZONE_CONTAINER '%s'" ),
2492 zc->GetNetname().GetData() );
2494 }
2495
2496 switch( *hopt ) // upper case required
2497 {
2498 case 'N': outline_hatch = ZONE_BORDER_DISPLAY_STYLE::NO_HATCH; break;
2499 case 'E': outline_hatch = ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE; break;
2500 case 'F': outline_hatch = ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_FULL; break;
2501 default:
2502 m_error.Printf( _( "Bad ZAux for CZONE_CONTAINER '%s'" ),
2503 zc->GetNetname().GetData() );
2505 }
2506
2507 // Set hatch mode later, after reading corner outline data
2508 }
2509 else if( TESTLINE( "ZSmoothing" ) )
2510 {
2511 // e.g. "ZSmoothing 0 0"
2512 int smoothing = intParse( line + SZ( "ZSmoothing" ), &data );
2513 BIU cornerRadius = biuParse( data );
2514
2515 if( smoothing >= ZONE_SETTINGS::SMOOTHING_LAST || smoothing < 0 )
2516 {
2517 m_error.Printf( _( "Bad ZSmoothing for CZONE_CONTAINER '%s'" ),
2518 zc->GetNetname().GetData() );
2520 }
2521
2522 zc->SetCornerSmoothingType( smoothing );
2523 zc->SetCornerRadius( cornerRadius );
2524 }
2525 else if( TESTLINE( "ZKeepout" ) )
2526 {
2527 char* token;
2528 zc->SetIsRuleArea( true );
2529 zc->SetDoNotAllowPads( false ); // Not supported in legacy
2530 zc->SetDoNotAllowFootprints( false ); // Not supported in legacy
2531
2532 // e.g. "ZKeepout tracks N vias N pads Y"
2533 token = strtok_r( line + SZ( "ZKeepout" ), delims, (char**) &data );
2534
2535 while( token )
2536 {
2537 if( !strcmp( token, "tracks" ) )
2538 {
2539 token = strtok_r( nullptr, delims, (char**) &data );
2540 zc->SetDoNotAllowTracks( token && *token == 'N' );
2541 }
2542 else if( !strcmp( token, "vias" ) )
2543 {
2544 token = strtok_r( nullptr, delims, (char**) &data );
2545 zc->SetDoNotAllowVias( token && *token == 'N' );
2546 }
2547 else if( !strcmp( token, "copperpour" ) )
2548 {
2549 token = strtok_r( nullptr, delims, (char**) &data );
2550 zc->SetDoNotAllowZoneFills( token && *token == 'N' );
2551 }
2552
2553 token = strtok_r( nullptr, delims, (char**) &data );
2554 }
2555 }
2556 else if( TESTLINE( "ZOptions" ) )
2557 {
2558 // e.g. "ZOptions 0 32 F 200 200"
2559 int fillmode = intParse( line + SZ( "ZOptions" ), &data );
2560 ignore_unused( intParse( data, &data ) );
2561 char fillstate = data[1]; // here e.g. " F"
2562 BIU thermalReliefGap = biuParse( data += 2 , &data ); // +=2 for " F"
2563 BIU thermalReliefCopperBridge = biuParse( data );
2564
2565 if( fillmode)
2566 {
2568 {
2569 wxLogWarning( _( "The legacy segment zone fill mode is no longer supported.\n"
2570 "Zone fills will be converted on a best-effort basis." ) );
2571
2573 }
2574 }
2575
2576 zc->SetFillMode( ZONE_FILL_MODE::POLYGONS );
2577 zc->SetIsFilled( fillstate == 'S' );
2578 zc->SetThermalReliefGap( thermalReliefGap );
2579 zc->SetThermalReliefSpokeWidth( thermalReliefCopperBridge );
2580 }
2581 else if( TESTLINE( "ZClearance" ) ) // Clearance and pad options info found
2582 {
2583 // e.g. "ZClearance 40 I"
2584 BIU clearance = biuParse( line + SZ( "ZClearance" ), &data );
2585 char* padoption = strtok_r( (char*) data, delims, (char**) &data ); // data: " I"
2586
2587 ZONE_CONNECTION popt;
2588 switch( *padoption )
2589 {
2590 case 'I': popt = ZONE_CONNECTION::FULL; break;
2591 case 'T': popt = ZONE_CONNECTION::THERMAL; break;
2592 case 'H': popt = ZONE_CONNECTION::THT_THERMAL; break;
2593 case 'X': popt = ZONE_CONNECTION::NONE; break;
2594 default:
2595 m_error.Printf( _( "Bad ZClearance padoption for CZONE_CONTAINER '%s'" ),
2596 zc->GetNetname().GetData() );
2598 }
2599
2600 zc->SetLocalClearance( clearance );
2601 zc->SetPadConnection( popt );
2602 }
2603 else if( TESTLINE( "ZMinThickness" ) )
2604 {
2605 BIU thickness = biuParse( line + SZ( "ZMinThickness" ) );
2606 zc->SetMinThickness( thickness );
2607 }
2608 else if( TESTLINE( "ZPriority" ) )
2609 {
2610 int priority = intParse( line + SZ( "ZPriority" ) );
2611 zc->SetAssignedPriority( priority );
2612 }
2613 else if( TESTLINE( "$POLYSCORNERS" ) )
2614 {
2615 // Read the PolysList (polygons that are the solid areas in the filled zone)
2616 SHAPE_POLY_SET polysList;
2617
2618 bool makeNewOutline = true;
2619
2620 while( ( line = READLINE( m_reader ) ) != nullptr )
2621 {
2622 if( TESTLINE( "$endPOLYSCORNERS" ) )
2623 break;
2624
2625 // e.g. "39610 43440 0 0"
2626 BIU x = biuParse( line, &data );
2627 BIU y = biuParse( data, &data );
2628
2629 if( makeNewOutline )
2630 polysList.NewOutline();
2631
2632 polysList.Append( x, y );
2633
2634 // end_countour was a bool when file saved, so '0' or '1' here
2635 bool end_contour = intParse( data, &data );
2636 intParse( data ); // skip corner utility flag
2637
2638 makeNewOutline = end_contour;
2639 }
2640
2641 zc->SetFilledPolysList( zc->GetFirstLayer(), polysList );
2642 }
2643 else if( TESTLINE( "$FILLSEGMENTS" ) )
2644 {
2645 while( ( line = READLINE( m_reader ) ) != nullptr )
2646 {
2647 if( TESTLINE( "$endFILLSEGMENTS" ) )
2648 break;
2649
2650 // e.g. ""%d %d %d %d\n"
2651 ignore_unused( biuParse( line, &data ) );
2652 ignore_unused( biuParse( data, &data ) );
2653 ignore_unused( biuParse( data, &data ) );
2654 ignore_unused( biuParse( data ) );
2655 }
2656 }
2657 else if( TESTLINE( "$endCZONE_OUTLINE" ) )
2658 {
2659 // Ensure keepout does not have a net
2660 // (which have no sense for a keepout zone)
2661 if( zc->GetIsRuleArea() )
2662 zc->SetNetCode( NETINFO_LIST::UNCONNECTED );
2663
2664 if( zc->GetMinThickness() > 0 )
2665 {
2666 // Inflate the fill polygon
2667 PCB_LAYER_ID layer = zc->GetFirstLayer();
2668 SHAPE_POLY_SET inflatedFill = SHAPE_POLY_SET( *zc->GetFilledPolysList( layer ) );
2669
2670 inflatedFill.InflateWithLinkedHoles( zc->GetMinThickness() / 2,
2671 CORNER_STRATEGY::ROUND_ALL_CORNERS,
2672 ARC_HIGH_DEF / 2 );
2673
2674 zc->SetFilledPolysList( layer, inflatedFill );
2675 }
2676
2677 // should always occur, but who knows, a zone without two corners
2678 // is no zone at all, it's a spot?
2679
2680 if( zc->GetNumCorners() > 2 )
2681 {
2682 if( !zc->IsOnCopperLayer() )
2683 {
2684 zc->SetFillMode( ZONE_FILL_MODE::POLYGONS );
2685 zc->SetNetCode( NETINFO_LIST::UNCONNECTED );
2686 }
2687
2688 // HatchBorder here, after outlines corners are read
2689 // Set hatch here, after outlines corners are read
2690 zc->SetBorderDisplayStyle( outline_hatch, ZONE::GetDefaultHatchPitch(), true );
2691
2692 m_board->Add( zc.release() );
2693 }
2694
2695 return; // preferred exit
2696 }
2697 }
2698
2699 THROW_IO_ERROR( wxT( "Missing '$endCZONE_OUTLINE'" ) );
2700}
2701
2702
2704{
2705 std::unique_ptr<PCB_DIM_ALIGNED> dim = std::make_unique<PCB_DIM_ALIGNED>( m_board,
2707 VECTOR2I crossBarO;
2708 VECTOR2I crossBarF;
2709
2710 char* line;
2711
2712 while( ( line = READLINE( m_reader ) ) != nullptr )
2713 {
2714 const char* data;
2715
2716 if( TESTLINE( "$endCOTATION" ) )
2717 {
2718 dim->UpdateHeight( crossBarF, crossBarO );
2719
2720 m_board->Add( dim.release(), ADD_MODE::APPEND );
2721 return; // preferred exit
2722 }
2723 else if( TESTLINE( "Va" ) )
2724 {
2725 BIU value = biuParse( line + SZ( "Va" ) );
2726
2727 // unused; dimension value is calculated from coordinates
2728 ( void )value;
2729 }
2730 else if( TESTLINE( "Ge" ) )
2731 {
2732 // e.g. "Ge 1 21 68183921-93a5-49ac-91b0-49d05a0e1647\r\n"
2733 int shape = intParse( line + SZ( "De" ), (const char**) &data );
2734 int layer_num = intParse( data, &data );
2735 char* uuid = strtok_r( (char*) data, delims, (char**) &data );
2736
2737 dim->SetLayer( leg_layer2new( m_cu_count, layer_num ) );
2738 const_cast<KIID&>( dim->m_Uuid ) = KIID( uuid );
2739
2740 // not used
2741 ( void )shape;
2742 }
2743 else if( TESTLINE( "Te" ) )
2744 {
2745 char buf[2048];
2746
2747 ReadDelimitedText( buf, line + SZ( "Te" ), sizeof(buf) );
2748 dim->SetOverrideText( From_UTF8( buf ) );
2749 dim->SetOverrideTextEnabled( true );
2750 dim->SetUnitsFormat( DIM_UNITS_FORMAT::NO_SUFFIX );
2751 dim->SetAutoUnits();
2752 }
2753 else if( TESTLINE( "Po" ) )
2754 {
2755 BIU pos_x = biuParse( line + SZ( "Po" ), &data );
2756 BIU pos_y = biuParse( data, &data );
2757 BIU width = biuParse( data, &data );
2758 BIU height = biuParse( data, &data );
2759 BIU thickn = biuParse( data, &data );
2760 EDA_ANGLE orient = degParse( data, &data );
2761 char* mirror = strtok_r( (char*) data, delims, (char**) &data );
2762
2763 dim->SetTextPos( VECTOR2I( pos_x, pos_y ) );
2764 dim->SetTextSize( VECTOR2I( width, height ) );
2765 dim->SetMirrored( mirror && *mirror == '0' );
2766 dim->SetTextThickness( thickn );
2767 dim->SetTextAngle( orient );
2768 }
2769 else if( TESTLINE( "Sb" ) )
2770 {
2771 ignore_unused( biuParse( line + SZ( "Sb" ), &data ) );
2772 BIU crossBarOx = biuParse( data, &data );
2773 BIU crossBarOy = biuParse( data, &data );
2774 BIU crossBarFx = biuParse( data, &data );
2775 BIU crossBarFy = biuParse( data, &data );
2776 BIU width = biuParse( data );
2777
2778 dim->SetLineThickness( width );
2779 crossBarO = VECTOR2I( crossBarOx, crossBarOy );
2780 crossBarF = VECTOR2I( crossBarFx, crossBarFy );
2781 }
2782 else if( TESTLINE( "Sd" ) )
2783 {
2784 ignore_unused( intParse( line + SZ( "Sd" ), &data ) );
2785 BIU featureLineDOx = biuParse( data, &data );
2786 BIU featureLineDOy = biuParse( data, &data );
2787
2788 ignore_unused( biuParse( data, &data ) );
2789 ignore_unused( biuParse( data ) );
2790
2791 dim->SetStart( VECTOR2I( featureLineDOx, featureLineDOy ) );
2792 }
2793 else if( TESTLINE( "Sg" ) )
2794 {
2795 ignore_unused( intParse( line + SZ( "Sg" ), &data ) );
2796 BIU featureLineGOx = biuParse( data, &data );
2797 BIU featureLineGOy = biuParse( data, &data );
2798
2799 ignore_unused( biuParse( data, &data ) );
2800 ignore_unused( biuParse( data ) );
2801
2802 dim->SetEnd( VECTOR2I( featureLineGOx, featureLineGOy ) );
2803 }
2804 else if( TESTLINE( "S1" ) ) // Arrow: no longer imported
2805 {
2806 ignore_unused( intParse( line + SZ( "S1" ), &data ) );
2807 biuParse( data, &data ); // skipping excessive data
2808 biuParse( data, &data ); // skipping excessive data
2809 biuParse( data, &data );
2810 biuParse( data );
2811 }
2812 else if( TESTLINE( "S2" ) ) // Arrow: no longer imported
2813 {
2814 ignore_unused( intParse( line + SZ( "S2" ), &data ) );
2815 biuParse( data, &data ); // skipping excessive data
2816 biuParse( data, &data ); // skipping excessive data
2817 biuParse( data, &data );
2818 biuParse( data, &data );
2819 }
2820 else if( TESTLINE( "S3" ) ) // Arrow: no longer imported
2821 {
2822 ignore_unused( intParse( line + SZ( "S3" ), &data ) );
2823 biuParse( data, &data ); // skipping excessive data
2824 biuParse( data, &data ); // skipping excessive data
2825 biuParse( data, &data );
2826 biuParse( data, &data );
2827 }
2828 else if( TESTLINE( "S4" ) ) // Arrow: no longer imported
2829 {
2830 ignore_unused( intParse( line + SZ( "S4" ), &data ) );
2831 biuParse( data, &data ); // skipping excessive data
2832 biuParse( data, &data ); // skipping excessive data
2833 biuParse( data, &data );
2834 biuParse( data, &data );
2835 }
2836 }
2837
2838 THROW_IO_ERROR( wxT( "Missing '$endCOTATION'" ) );
2839}
2840
2841
2843{
2844 char* line;
2845
2846 while( ( line = READLINE( m_reader ) ) != nullptr )
2847 {
2848 const char* data;
2849
2850 if( TESTLINE( "$EndPCB_TARGET" ) || TESTLINE( "$EndMIREPCB" ) )
2851 {
2852 return; // preferred exit
2853 }
2854 else if( TESTLINE( "Po" ) )
2855 {
2856 int shape = intParse( line + SZ( "Po" ), &data );
2857 int layer_num = intParse( data, &data );
2858 BIU pos_x = biuParse( data, &data );
2859 BIU pos_y = biuParse( data, &data );
2860 BIU size = biuParse( data, &data );
2861 BIU width = biuParse( data, &data );
2862 char* uuid = strtok_r( (char*) data, delims, (char**) &data );
2863
2864 if( layer_num < FIRST_NON_COPPER_LAYER )
2865 layer_num = FIRST_NON_COPPER_LAYER;
2866 else if( layer_num > LAST_NON_COPPER_LAYER )
2867 layer_num = LAST_NON_COPPER_LAYER;
2868
2869 PCB_TARGET* t = new PCB_TARGET( m_board, shape, leg_layer2new( m_cu_count, layer_num ),
2870 VECTOR2I( pos_x, pos_y ), size, width );
2871 m_board->Add( t, ADD_MODE::APPEND );
2872
2873 const_cast<KIID&>( t->m_Uuid ) = KIID( uuid );
2874 }
2875 }
2876
2877 THROW_IO_ERROR( wxT( "Missing '$EndDIMENSION'" ) );
2878}
2879
2880
2881BIU PCB_IO_KICAD_LEGACY::biuParse( const char* aValue, const char** nptrptr )
2882{
2883 char* nptr;
2884
2885 errno = 0;
2886
2887 double fval = strtod( aValue, &nptr );
2888
2889 if( errno )
2890 {
2891 m_error.Printf( _( "Invalid floating point number in file: '%s'\nline: %d, offset: %d" ),
2892 m_reader->GetSource().GetData(),
2893 (int) m_reader->LineNumber(),
2894 (int)( aValue - m_reader->Line() + 1 ) );
2895
2897 }
2898
2899 if( aValue == nptr )
2900 {
2901 m_error.Printf( _( "Missing floating point number in file: '%s'\nline: %d, offset: %d" ),
2902 m_reader->GetSource().GetData(),
2903 (int) m_reader->LineNumber(),
2904 (int)( aValue - m_reader->Line() + 1 ) );
2905
2907 }
2908
2909 if( nptrptr )
2910 *nptrptr = nptr;
2911
2912 fval *= diskToBiu;
2913
2914 // fval is up into the whole number realm here, and should be bounded
2915 // within INT_MIN to INT_MAX since BIU's are nanometers.
2916 return KiROUND( fval );
2917}
2918
2919
2920EDA_ANGLE PCB_IO_KICAD_LEGACY::degParse( const char* aValue, const char** nptrptr )
2921{
2922 char* nptr;
2923
2924 errno = 0;
2925
2926 double fval = strtod( aValue, &nptr );
2927
2928 if( errno )
2929 {
2930 m_error.Printf( _( "Invalid floating point number in file: '%s'\nline: %d, offset: %d" ),
2931 m_reader->GetSource().GetData(),
2932 (int) m_reader->LineNumber(),
2933 (int)( aValue - m_reader->Line() + 1 ) );
2934
2936 }
2937
2938 if( aValue == nptr )
2939 {
2940 m_error.Printf( _( "Missing floating point number in file: '%s'\nline: %d, offset: %d" ),
2941 m_reader->GetSource().GetData(),
2942 (int) m_reader->LineNumber(),
2943 (int)( aValue - m_reader->Line() + 1 ) );
2944
2946 }
2947
2948 if( nptrptr )
2949 *nptrptr = nptr;
2950
2951 return EDA_ANGLE( fval, TENTHS_OF_A_DEGREE_T );
2952}
2953
2954
2955void PCB_IO_KICAD_LEGACY::init( const std::map<std::string, UTF8>* aProperties )
2956{
2958 m_cu_count = 16;
2959 m_board = nullptr;
2961 m_props = aProperties;
2962
2963 // conversion factor for saving RAM BIUs to KICAD legacy file format.
2964 biuToDisk = 1.0 / pcbIUScale.IU_PER_MM; // BIUs are nanometers & file is mm
2965
2966 // Conversion factor for loading KICAD legacy file format into BIUs in RAM
2967 // Start by assuming the *.brd file is in deci-mils.
2968 // If we see "Units mm" in the $GENERAL section, set diskToBiu to 1000000.0
2969 // then, during the file loading process, to start a conversion from
2970 // mm to nanometers. The deci-mil legacy files have no such "Units" marker
2971 // so we must assume the file is in deci-mils until told otherwise.
2972
2973 diskToBiu = pcbIUScale.IU_PER_MILS / 10; // BIUs are nanometers
2974}
2975
2976
2977//-----<FOOTPRINT LIBRARY FUNCTIONS>--------------------------------------------
2978
2979/*
2980
2981 The legacy file format is being obsoleted and this code will have a short
2982 lifetime, so it only needs to be good enough for a short duration of time.
2983 Caching all the MODULEs is a bit memory intensive, but it is a considerably
2984 faster way of fulfilling the API contract. Otherwise, without the cache, you
2985 would have to re-read the file when searching for any FOOTPRINT, and this would
2986 be very problematic filling a FOOTPRINT_LIST via this PLUGIN API. If memory
2987 becomes a concern, consider the cache lifetime policy, which determines the
2988 time that a LP_CACHE is in RAM. Note PLUGIN lifetime also plays a role in
2989 cache lifetime.
2990
2991*/
2992
2993
2994typedef boost::ptr_map< std::string, FOOTPRINT > FOOTPRINT_MAP;
2995
2996
3002{
3003 LP_CACHE( PCB_IO_KICAD_LEGACY* aOwner, const wxString& aLibraryPath );
3004
3005 // Most all functions in this class throw IO_ERROR exceptions. There are no
3006 // error codes nor user interface calls from here, nor in any PLUGIN.
3007 // Catch these exceptions higher up please.
3008
3009 void Load();
3010
3011 void ReadAndVerifyHeader( LINE_READER* aReader );
3012
3013 void SkipIndex( LINE_READER* aReader );
3014
3015 void LoadModules( LINE_READER* aReader );
3016
3017 bool IsModified();
3018 static long long GetTimestamp( const wxString& aLibPath );
3019
3020 PCB_IO_KICAD_LEGACY* m_owner; // my owner, I need its PCB_IO_KICAD_LEGACY::loadFOOTPRINT()
3021 wxString m_lib_path;
3022 FOOTPRINT_MAP m_footprints; // map or tuple of footprint_name vs. FOOTPRINT*
3024
3025 bool m_cache_dirty; // Stored separately because it's expensive to check
3026 // m_cache_timestamp against all the files.
3027 long long m_cache_timestamp; // A hash of the timestamps for all the footprint
3028 // files.
3029};
3030
3031
3032LP_CACHE::LP_CACHE( PCB_IO_KICAD_LEGACY* aOwner, const wxString& aLibraryPath ) :
3033 m_owner( aOwner ),
3034 m_lib_path( aLibraryPath ),
3035 m_writable( true ),
3036 m_cache_dirty( true ),
3037 m_cache_timestamp( 0 )
3038{
3039}
3040
3041
3043{
3045
3046 return m_cache_dirty;
3047}
3048
3049
3050long long LP_CACHE::GetTimestamp( const wxString& aLibPath )
3051{
3052 wxFileName fn( aLibPath );
3053
3054 if( fn.IsFileReadable() && fn.GetModificationTime().IsValid() )
3055 return fn.GetModificationTime().GetValue().GetValue();
3056 else
3057 return 0;
3058}
3059
3060
3062{
3063 m_cache_dirty = false;
3064
3065 FILE_LINE_READER reader( m_lib_path );
3066
3067 ReadAndVerifyHeader( &reader );
3068 SkipIndex( &reader );
3069 LoadModules( &reader );
3070
3071 // Remember the file modification time of library file when the
3072 // cache snapshot was made, so that in a networked environment we will
3073 // reload the cache as needed.
3075}
3076
3077
3079{
3080 char* line = aReader->ReadLine();
3081 char* data;
3082
3083 if( !line )
3084 THROW_IO_ERROR( wxString::Format( _( "File '%s' is empty." ), m_lib_path ) );
3085
3086 if( !TESTLINE( "PCBNEW-LibModule-V1" ) )
3087 THROW_IO_ERROR( wxString::Format( _( "File '%s' is not a legacy library." ), m_lib_path ) );
3088
3089 while( ( line = aReader->ReadLine() ) != nullptr )
3090 {
3091 if( TESTLINE( "Units" ) )
3092 {
3093 const char* units = strtok_r( line + SZ( "Units" ), delims, &data );
3094
3095 if( !strcmp( units, "mm" ) )
3097
3098 }
3099 else if( TESTLINE( "$INDEX" ) )
3100 {
3101 return;
3102 }
3103 }
3104}
3105
3106
3108{
3109 // Some broken INDEX sections have more than one section, due to prior bugs.
3110 // So we must read the next line after $EndINDEX tag,
3111 // to see if this is not a new $INDEX tag.
3112 bool exit = false;
3113 char* line = aReader->Line();
3114
3115 do
3116 {
3117 if( TESTLINE( "$INDEX" ) )
3118 {
3119 exit = false;
3120
3121 while( ( line = aReader->ReadLine() ) != nullptr )
3122 {
3123 if( TESTLINE( "$EndINDEX" ) )
3124 {
3125 exit = true;
3126 break;
3127 }
3128 }
3129 }
3130 else if( exit )
3131 {
3132 break;
3133 }
3134 } while( ( line = aReader->ReadLine() ) != nullptr );
3135}
3136
3137
3139{
3140 m_owner->SetReader( aReader );
3141
3142 char* line = aReader->Line();
3143
3144 do
3145 {
3146 // test first for the $MODULE, even before reading because of INDEX bug.
3147 if( TESTLINE( "$MODULE" ) )
3148 {
3149 std::unique_ptr<FOOTPRINT> fp_ptr = std::make_unique<FOOTPRINT>( m_owner->m_board );
3150
3151 std::string footprintName = StrPurge( line + SZ( "$MODULE" ) );
3152
3153 // The footprint names in legacy libraries can contain the '/' and ':'
3154 // characters which will cause the LIB_ID parser to choke.
3155 ReplaceIllegalFileNameChars( &footprintName );
3156
3157 // set the footprint name first thing, so exceptions can use name.
3158 fp_ptr->SetFPID( LIB_ID( wxEmptyString, footprintName ) );
3159
3160 m_owner->loadFOOTPRINT( fp_ptr.get());
3161
3162 FOOTPRINT* fp = fp_ptr.release(); // exceptions after this are not expected.
3163
3164 // Not sure why this is asserting on debug builds. The debugger shows the
3165 // strings are the same. If it's not really needed maybe it can be removed.
3166
3167 /*
3168
3169 There was a bug in old legacy library management code
3170 (pre-PCB_IO_KICAD_LEGACY) which was introducing duplicate footprint names
3171 in legacy libraries without notification. To best recover from such
3172 bad libraries, and use them to their fullest, there are a few
3173 strategies that could be used. (Note: footprints must have unique
3174 names to be accepted into this cache.) The strategy used here is to
3175 append a differentiating version counter to the end of the name as:
3176 _v2, _v3, etc.
3177
3178 */
3179
3180 FOOTPRINT_MAP::const_iterator it = m_footprints.find( footprintName );
3181
3182 if( it == m_footprints.end() ) // footprintName is not present in cache yet.
3183 {
3184 if( !m_footprints.insert( footprintName, fp ).second )
3185 {
3186 wxFAIL_MSG( wxT( "error doing cache insert using guaranteed unique name" ) );
3187 }
3188 }
3189 else
3190 {
3191 // Bad library has a duplicate of this footprintName, generate a
3192 // unique footprint name and load it anyway.
3193 bool nameOK = false;
3194 int version = 2;
3195 char buf[48];
3196
3197 while( !nameOK )
3198 {
3199 std::string newName = footprintName;
3200
3201 newName += "_v";
3202 snprintf( buf, sizeof(buf), "%d", version++ );
3203 newName += buf;
3204
3205 it = m_footprints.find( newName );
3206
3207 if( it == m_footprints.end() )
3208 {
3209 nameOK = true;
3210
3211 fp->SetFPID( LIB_ID( wxEmptyString, newName ) );
3212
3213 if( !m_footprints.insert( newName, fp ).second )
3214 {
3215 wxFAIL_MSG( wxT( "error doing cache insert using guaranteed unique "
3216 "name" ) );
3217 }
3218 }
3219 }
3220 }
3221 }
3222
3223 } while( ( line = aReader->ReadLine() ) != nullptr );
3224}
3225
3226
3227long long PCB_IO_KICAD_LEGACY::GetLibraryTimestamp( const wxString& aLibraryPath ) const
3228{
3229 return LP_CACHE::GetTimestamp( aLibraryPath );
3230}
3231
3232
3233void PCB_IO_KICAD_LEGACY::cacheLib( const wxString& aLibraryPath )
3234{
3235 if( !m_cache || m_cache->m_lib_path != aLibraryPath || m_cache->IsModified() )
3236 {
3237 // a spectacular episode in memory management:
3238 delete m_cache;
3239 m_cache = new LP_CACHE( this, aLibraryPath );
3240 m_cache->Load();
3241 }
3242}
3243
3244
3245void PCB_IO_KICAD_LEGACY::FootprintEnumerate( wxArrayString& aFootprintNames, const wxString& aLibPath,
3246 bool aBestEfforts, const std::map<std::string, UTF8>* aProperties )
3247{
3248 wxString errorMsg;
3249
3250 init( aProperties );
3251
3252 try
3253 {
3254 cacheLib( aLibPath );
3255 }
3256 catch( const IO_ERROR& ioe )
3257 {
3258 errorMsg = ioe.What();
3259 }
3260
3261 // Some of the files may have been parsed correctly so we want to add the valid files to
3262 // the library.
3263
3264 for( const auto& footprint : m_cache->m_footprints )
3265 aFootprintNames.Add( From_UTF8( footprint.first.c_str() ) );
3266
3267 if( !errorMsg.IsEmpty() && !aBestEfforts )
3268 THROW_IO_ERROR( errorMsg );
3269}
3270
3271
3272FOOTPRINT* PCB_IO_KICAD_LEGACY::FootprintLoad( const wxString& aLibraryPath,
3273 const wxString& aFootprintName, bool aKeepUUID,
3274 const std::map<std::string, UTF8>* aProperties )
3275{
3276 init( aProperties );
3277
3278 cacheLib( aLibraryPath );
3279
3280 const FOOTPRINT_MAP& footprints = m_cache->m_footprints;
3281 FOOTPRINT_MAP::const_iterator it = footprints.find( TO_UTF8( aFootprintName ) );
3282
3283 if( it == footprints.end() )
3284 return nullptr;
3285
3286 // Return copy of already loaded FOOTPRINT
3287 FOOTPRINT* copy = (FOOTPRINT*) it->second->Duplicate( IGNORE_PARENT_GROUP );
3288 copy->SetParent( nullptr );
3289 return copy;
3290}
3291
3292
3293bool PCB_IO_KICAD_LEGACY::DeleteLibrary( const wxString& aLibraryPath,
3294 const std::map<std::string, UTF8>* aProperties )
3295{
3296 wxFileName fn = aLibraryPath;
3297
3298 if( !fn.FileExists() )
3299 return false;
3300
3301 // Some of the more elaborate wxRemoveFile() crap puts up its own wxLog dialog
3302 // we don't want that. we want bare metal portability with no UI here.
3303 if( wxRemove( aLibraryPath ) )
3304 {
3305 THROW_IO_ERROR( wxString::Format( _( "Footprint library '%s' cannot be deleted." ),
3306 aLibraryPath.GetData() ) );
3307 }
3308
3309 if( m_cache && m_cache->m_lib_path == aLibraryPath )
3310 {
3311 delete m_cache;
3312 m_cache = nullptr;
3313 }
3314
3315 return true;
3316}
3317
3318
3319bool PCB_IO_KICAD_LEGACY::IsLibraryWritable( const wxString& aLibraryPath )
3320{
3321 init( nullptr );
3322
3323 cacheLib( aLibraryPath );
3324
3325 return m_cache->m_writable;
3326}
3327
3328
3330 m_cu_count( 16 ), // for FootprintLoad()
3331 m_progressReporter( nullptr ),
3332 m_lastProgressLine( 0 ),
3333 m_lineCount( 0 ),
3334 m_reader( nullptr ),
3335 m_fp( nullptr ),
3336 m_cache( nullptr )
3337{
3338 init( nullptr );
3339}
3340
3341
3343{
3344 delete m_cache;
3345}
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]
void SetBoardThickness(int aThickness)
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)
Definition: board_item.cpp:360
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:317
void SetPlotOptions(const PCB_PLOT_PARAMS &aOptions)
Definition: board.h:746
bool m_LegacyDesignSettingsLoaded
True if the legacy board design settings were loaded from a file.
Definition: board.h:404
void SetVisibleLayers(const LSET &aLayerMask)
A proxy function that calls the correspondent function in m_BoardSettings changes the bit-mask of vis...
Definition: board.cpp:939
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition: board.cpp:1147
void SetFileName(const wxString &aFileName)
Definition: board.h:352
NETINFO_ITEM * FindNet(int aNetcode) const
Search for a net with the given netcode.
Definition: board.cpp:2099
bool SetLayerName(PCB_LAYER_ID aLayer, const wxString &aLayerName)
Changes the name of the layer given by aLayer.
Definition: board.cpp:698
void SetPageSettings(const PAGE_INFO &aPageSettings)
Definition: board.h:743
bool m_LegacyNetclassesLoaded
True if netclasses were loaded from the file.
Definition: board.h:408
void SetCopperLayerCount(int aCount)
Definition: board.cpp:865
bool SetLayerType(PCB_LAYER_ID aLayer, LAYER_T aLayerType)
Change the type of the layer given by aLayer.
Definition: board.cpp:749
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:1024
void SetEnabledLayers(const LSET &aLayerMask)
A proxy function that calls the correspondent function in m_BoardSettings.
Definition: board.cpp:927
void SetTitleBlock(const TITLE_BLOCK &aTitleBlock)
Definition: board.h:750
void SetVisibleElements(const GAL_SET &aMask)
A proxy function that calls the correspondent function in m_BoardSettings.
Definition: board.cpp:946
void SetFileFormatVersionAtLoad(int aVersion)
Definition: board.h:426
const KIID m_Uuid
Definition: eda_item.h:516
void SetTextSize(VECTOR2I aNewSize, bool aEnforceMinTextSize=true)
Definition: eda_text.cpp:533
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition: eda_text.h:97
void SetTextPos(const VECTOR2I &aPoint)
Definition: eda_text.cpp:578
void SetMirrored(bool isMirrored)
Definition: eda_text.cpp:393
void SetVertJustify(GR_TEXT_V_ALIGN_T aType)
Definition: eda_text.cpp:417
GR_TEXT_H_ALIGN_T GetHorizJustify() const
Definition: eda_text.h:197
virtual void SetVisible(bool aVisible)
Definition: eda_text.cpp:386
void SetTextThickness(int aWidth)
The TextThickness is that set by the user.
Definition: eda_text.cpp:284
virtual void SetText(const wxString &aText)
Definition: eda_text.cpp:270
virtual void SetTextAngle(const EDA_ANGLE &aAngle)
Definition: eda_text.cpp:299
void SetItalic(bool aItalic)
Set the text to be italic - this will also update the font if needed.
Definition: eda_text.cpp:307
void SetHorizJustify(GR_TEXT_H_ALIGN_T aType)
Definition: eda_text.cpp:409
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
Definition: footprint.cpp:2513
void SetFPID(const LIB_ID &aFPID)
Definition: footprint.h:252
void SetLocked(bool isLocked) override
Set the #MODULE_is_LOCKED bit in the m_ModuleStatus.
Definition: footprint.h:430
EDA_ANGLE GetOrientation() const
Definition: footprint.h:230
void Remove(BOARD_ITEM *aItem, REMOVE_MODE aMode=REMOVE_MODE::NORMAL) override
Removes an item from the container.
Definition: footprint.cpp:1149
void SetIsPlaced(bool isPlaced)
Definition: footprint.h:444
void SetOrientation(const EDA_ANGLE &aNewAngle)
Definition: footprint.cpp:2595
void SetLocalSolderPasteMarginRatio(std::optional< double > aRatio)
Definition: footprint.h:288
void SetPath(const KIID_PATH &aPath)
Definition: footprint.h:267
void SetKeywords(const wxString &aKeywords)
Definition: footprint.h:264
void SetAttributes(int aAttributes)
Definition: footprint.h:294
PCB_FIELD & Value()
read/write accessors:
Definition: footprint.h:663
void SetLocalZoneConnection(ZONE_CONNECTION aType)
Definition: footprint.h:290
const LIB_ID & GetFPID() const
Definition: footprint.h:251
PCB_FIELD & Reference()
Definition: footprint.h:664
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition: footprint.cpp:1080
std::vector< FP_3DMODEL > & Models()
Definition: footprint.h:223
void SetLibDescription(const wxString &aDesc)
Definition: footprint.h:261
void SetLocalSolderMaskMargin(std::optional< int > aMargin)
Definition: footprint.h:282
void SetLocalClearance(std::optional< int > aClearance)
Definition: footprint.h:279
void SetLocalSolderPasteMargin(std::optional< int > aMargin)
Definition: footprint.h:285
VECTOR2I GetPosition() const override
Definition: footprint.h:227
VECTOR3D m_Offset
3D model offset (mm)
Definition: footprint.h:104
VECTOR3D m_Rotation
3D model rotation (degrees)
Definition: footprint.h:103
VECTOR3D m_Scale
3D model scaling factor (dimensionless)
Definition: footprint.h:102
wxString m_Filename
The 3D shape filename in 3D library.
Definition: footprint.h:106
Helper for storing and iterating over GAL_LAYER_IDs.
Definition: layer_ids.h:393
GAL_SET & set()
Definition: layer_ids.h:409
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
Definition: ki_exception.h:77
virtual const wxString What() const
A composite of Problem() and Where()
Definition: exceptions.cpp:30
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
virtual unsigned LineNumber() const
Return the line number of the last line read from this LINE_READER.
Definition: richio.h:147
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:56
int GetNetCode() const
Definition: netinfo.h:108
static const int UNCONNECTED
Constant that holds the "unconnected net" number (typically 0) all items "connected" to this net are ...
Definition: netinfo.h:381
static constexpr PCB_LAYER_ID ALL_LAYERS
! Temporary layer identifier to identify code that is not padstack-aware
Definition: padstack.h:145
Describe the page size and margins of a paper page on which to eventually print or plot.
Definition: page_info.h:59
void SetPortrait(bool aIsPortrait)
Rotate the paper page 90 degrees.
Definition: page_info.cpp:189
static const wxChar Custom[]
"User" defined page type
Definition: page_info.h:82
void SetHeightMils(double aHeightInMils)
Definition: page_info.cpp:262
void SetWidthMils(double aWidthInMils)
Definition: page_info.cpp:248
bool SetType(const wxString &aStandardPageDescriptionName, bool aIsPortrait=false)
Set the name of the page type and also the sizes and margins commonly associated with that type name.
Definition: page_info.cpp:122
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,...
void SetReader(LINE_READER *aReader)
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)
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...
A base class that BOARD loading and saving plugins should derive from.
Definition: pcb_io.h:71
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
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
virtual bool KeepRefreshing(bool aWait=false)=0
Update the UI (if any).
virtual void Report(const wxString &aMessage)=0
Display aMessage in the progress bar dialog.
virtual void SetCurrentProgress(double aProgress)=0
Set the progress value to aProgress (0..1).
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.
Definition: stroke_params.h:94
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)
Definition: title_block.h:101
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
T y
Definition: vector3.h:64
T z
Definition: vector3.h:65
T x
Definition: vector3.h:63
Read lines of text from another LINE_READER but only returns non-comment lines and non-blank lines wi...
Definition: filter_reader.h:69
char * ReadLine() override
Read a line of text into the buffer and increments the line number counter.
ZONE_SETTINGS handles zones parameters.
Definition: zone_settings.h:88
static int GetDefaultHatchPitch()
Definition: zone.cpp:1281
This file is part of the common library.
static bool isSpace(char cc)
Test for whitespace.
Definition: dsnlexer.cpp:447
#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
@ FP_SMD
Definition: footprint.h:81
@ FP_EXCLUDE_FROM_POS_FILES
Definition: footprint.h:82
@ FP_EXCLUDE_FROM_BOM
Definition: footprint.h:83
@ FP_THROUGH_HOLE
Definition: footprint.h:80
void ignore_unused(const T &)
Definition: ignore.h:24
#define THROW_IO_ERROR(msg)
Definition: ki_exception.h:39
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
PAD_SHAPE
The set of pad shapes, used with PAD::{Set,Get}Shape()
Definition: padstack.h:52
std::map< wxString, FOOTPRINT * > FOOTPRINT_MAP
Definition: pcb_io_eagle.h:46
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
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.
Definition: string_utils.h:429
const double IU_PER_MM
Definition: base_units.h:76
const double IU_PER_MILS
Definition: base_units.h:77
static LAYER_T ParseType(const char *aType)
Convert a string to a LAYER_T.
Definition: board.cpp:778
The footprint portion of the PLUGIN API, and only for the PCB_IO_KICAD_LEGACY, so therefore is privat...
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)
int clearance
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.
Definition: zone_settings.h:59
ZONE_CONNECTION
How pads are covered by copper in zone.
Definition: zones.h:47