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