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