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 biuParse( line + SZ( "PadSize" ), &data ); // x
1076 biuParse( data ); // y
1077 }
1078 else if( TESTLINE( "PadDrill" ) )
1079 {
1080 biuParse( line + SZ( "PadDrill" ) ); // drill size
1081 }
1082 else if( TESTLINE( "Pad2MaskClearance" ) )
1083 {
1084 BIU tmp = biuParse( line + SZ( "Pad2MaskClearance" ) );
1085 bds.m_SolderMaskExpansion = tmp;
1086 }
1087 else if( TESTLINE( "SolderMaskMinWidth" ) )
1088 {
1089 BIU tmp = biuParse( line + SZ( "SolderMaskMinWidth" ) );
1090 bds.m_SolderMaskMinWidth = tmp;
1091 }
1092 else if( TESTLINE( "Pad2PasteClearance" ) )
1093 {
1094 BIU tmp = biuParse( line + SZ( "Pad2PasteClearance" ) );
1095 bds.m_SolderPasteMargin = tmp;
1096 }
1097 else if( TESTLINE( "Pad2PasteClearanceRatio" ) )
1098 {
1099 double ratio = atof( line + SZ( "Pad2PasteClearanceRatio" ) );
1100 bds.m_SolderPasteMarginRatio = ratio;
1101 }
1102
1103 else if( TESTLINE( "GridOrigin" ) )
1104 {
1105 BIU x = biuParse( line + SZ( "GridOrigin" ), &data );
1106 BIU y = biuParse( data );
1107
1108 bds.SetGridOrigin( VECTOR2I( x, y ) );
1109 }
1110 else if( TESTLINE( "VisibleElements" ) )
1111 {
1112 // Keep all elements visible.
1113 // the old visibility control does not make sense in current Pcbnew version,
1114 // and this code does not work.
1115#if 0
1116 uint32_t visibleElements = hexParse( line + SZ( "VisibleElements" ) );
1117
1118 // Does not work: each old item should be tested one by one to set
1119 // visibility of new item list
1120 GAL_SET visibles;
1121
1122 for( size_t i = 0; i < visibles.size(); i++ )
1123 visibles.set( i, visibleElements & ( 1u << i ) );
1124
1125 m_board->SetVisibleElements( visibles );
1126#endif
1127 }
1128 else if( TESTLINE( "$EndSETUP" ) )
1129 {
1130 bds.SetDefaultZoneSettings( zoneSettings );
1131
1132 // Very old *.brd file does not have NETCLASSes
1133 // "TrackWidth", "ViaSize", "ViaDrill", "ViaMinSize", and "TrackClearence" were
1134 // defined in SETUP; these values are put into the default NETCLASS until later board
1135 // load code should override them. *.brd files which have been saved with knowledge
1136 // of NETCLASSes will override these defaults, very old boards (before 2009) will not
1137 // and use the setup values.
1138 // However these values should be the same as default NETCLASS.
1139
1140 return; // preferred exit
1141 }
1142 }
1143
1144 /*
1145 * Ensure tracks and vias sizes lists are ok:
1146 * Sort lists by by increasing value and remove duplicates
1147 * (the first value is not tested, because it is the netclass value)
1148 */
1149 sort( bds.m_ViasDimensionsList.begin() + 1, bds.m_ViasDimensionsList.end() );
1150 sort( bds.m_TrackWidthList.begin() + 1, bds.m_TrackWidthList.end() );
1151
1152 for( int ii = 1; ii < (int) bds.m_ViasDimensionsList.size() - 1; ii++ )
1153 {
1154 if( bds.m_ViasDimensionsList[ii] == bds.m_ViasDimensionsList[ii + 1] )
1155 {
1156 bds.m_ViasDimensionsList.erase( bds.m_ViasDimensionsList.begin() + ii );
1157 ii--;
1158 }
1159 }
1160
1161 for( int ii = 1; ii < (int) bds.m_TrackWidthList.size() - 1; ii++ )
1162 {
1163 if( bds.m_TrackWidthList[ii] == bds.m_TrackWidthList[ii + 1] )
1164 {
1165 bds.m_TrackWidthList.erase( bds.m_TrackWidthList.begin() + ii );
1166 ii--;
1167 }
1168 }
1169}
1170
1171
1173{
1174 char* line;
1175
1176 while( ( line = READLINE( m_reader ) ) != nullptr )
1177 {
1178 const char* data;
1179
1180 // most frequently encountered ones at the top
1181
1182 if( TESTSUBSTR( "D" ) && strchr( "SCAP", line[1] ) ) // read a drawing item, e.g. "DS"
1183 {
1184 loadFP_SHAPE( aFootprint );
1185 }
1186 else if( TESTLINE( "$PAD" ) )
1187 {
1188 loadPAD( aFootprint );
1189 }
1190 else if( TESTSUBSTR( "T" ) ) // Read a footprint text description (ref, value, or drawing)
1191 {
1192 // e.g. "T1 6940 -16220 350 300 900 60 M I 20 N "CFCARD"\r\n"
1193 int tnum = intParse( line + SZ( "T" ) );
1194
1195 PCB_TEXT* text = nullptr;
1196
1197 switch( tnum )
1198 {
1200 text = &aFootprint->Reference();
1201 break;
1202
1204 text = &aFootprint->Value();
1205 break;
1206
1207 // All other fields greater than 1.
1208 default:
1209 text = new PCB_TEXT( aFootprint );
1210 aFootprint->Add( text );
1211 }
1212
1214
1215 // Convert hidden footprint text (which is no longer supported) to a hidden field
1216 if( !text->IsVisible() && text->Type() == PCB_TEXT_T )
1217 {
1218 aFootprint->Remove( text );
1219 aFootprint->Add( new PCB_FIELD( *text, FIELD_T::USER ) );
1220 delete text;
1221 }
1222 }
1223 else if( TESTLINE( "Po" ) )
1224 {
1225 // e.g. "Po 19120 39260 900 0 4E823D06 68183921-93a5-49ac-91b0-49d05a0e1647 ~~\r\n"
1226 BIU pos_x = biuParse( line + SZ( "Po" ), &data );
1227 BIU pos_y = biuParse( data, &data );
1228 int orient = intParse( data, &data );
1229 int layer_num = intParse( data, &data );
1230 PCB_LAYER_ID layer_id = leg_layer2new( m_cu_count, layer_num );
1231
1232 [[maybe_unused]] uint32_t edittime = hexParse( data, &data );
1233
1234 char* uuid = strtok_r( (char*) data, delims, (char**) &data );
1235
1236 data = strtok_r( (char*) data+1, delims, (char**) &data );
1237
1238 // data is now a two character long string
1239 // Note: some old files do not have this field
1240 if( data && data[0] == 'F' )
1241 aFootprint->SetLocked( true );
1242
1243 if( data && data[1] == 'P' )
1244 aFootprint->SetIsPlaced( true );
1245
1246 aFootprint->SetPosition( VECTOR2I( pos_x, pos_y ) );
1247 aFootprint->SetLayer( layer_id );
1248 aFootprint->SetOrientation( EDA_ANGLE( orient, TENTHS_OF_A_DEGREE_T ) );
1249 aFootprint->SetUuidDirect( KIID( uuid ) );
1250 }
1251 else if( TESTLINE( "Sc" ) ) // timestamp
1252 {
1253 char* uuid = strtok_r( (char*) line + SZ( "Sc" ), delims, (char**) &data );
1254 aFootprint->SetUuidDirect( KIID( uuid ) );
1255 }
1256 else if( TESTLINE( "Op" ) ) // (Op)tions for auto placement (no longer supported)
1257 {
1258 hexParse( line + SZ( "Op" ), &data );
1259 hexParse( data );
1260 }
1261 else if( TESTLINE( "At" ) ) // (At)tributes of footprint
1262 {
1263 int attrs = 0;
1264
1265 data = line + SZ( "At" );
1266
1267 if( strstr( data, "SMD" ) )
1268 attrs |= FP_SMD;
1269 else if( strstr( data, "VIRTUAL" ) )
1271 else
1273
1274 aFootprint->SetAttributes( attrs );
1275 }
1276 else if( TESTLINE( "AR" ) ) // Alternate Reference
1277 {
1278 // e.g. "AR /68183921-93a5-49ac-e164-49d05a0e1647/93a549d0-49d0-e164-91b0-49d05a0e1647"
1279 data = strtok_r( line + SZ( "AR" ), delims, (char**) &data );
1280
1281 if( data )
1282 aFootprint->SetPath( KIID_PATH( From_UTF8( data ) ) );
1283 }
1284 else if( TESTLINE( "$SHAPE3D" ) )
1285 {
1286 load3D( aFootprint );
1287 }
1288 else if( TESTLINE( "Cd" ) )
1289 {
1290 // e.g. "Cd Double rangee de contacts 2 x 4 pins\r\n"
1291 aFootprint->SetLibDescription( From_UTF8( StrPurge( line + SZ( "Cd" ) ) ) );
1292 }
1293 else if( TESTLINE( "Kw" ) ) // Key words
1294 {
1295 aFootprint->SetKeywords( From_UTF8( StrPurge( line + SZ( "Kw" ) ) ) );
1296 }
1297 else if( TESTLINE( ".SolderPasteRatio" ) )
1298 {
1299 double tmp = atof( line + SZ( ".SolderPasteRatio" ) );
1300
1301 // Due to a bug in dialog editor in Footprint Editor, fixed in BZR version 3565
1302 // this parameter can be broken.
1303 // It should be >= -50% (no solder paste) and <= 0% (full area of the pad)
1304
1305 if( tmp < -0.50 )
1306 tmp = -0.50;
1307
1308 if( tmp > 0.0 )
1309 tmp = 0.0;
1310
1311 aFootprint->SetLocalSolderPasteMarginRatio( tmp );
1312 }
1313 else if( TESTLINE( ".SolderPaste" ) )
1314 {
1315 BIU tmp = biuParse( line + SZ( ".SolderPaste" ) );
1316 aFootprint->SetLocalSolderPasteMargin( tmp );
1317 }
1318 else if( TESTLINE( ".SolderMask" ) )
1319 {
1320 BIU tmp = biuParse( line + SZ( ".SolderMask" ) );
1321 aFootprint->SetLocalSolderMaskMargin( tmp );
1322 }
1323 else if( TESTLINE( ".LocalClearance" ) )
1324 {
1325 BIU tmp = biuParse( line + SZ( ".LocalClearance" ) );
1326 aFootprint->SetLocalClearance( tmp );
1327 }
1328 else if( TESTLINE( ".ZoneConnection" ) )
1329 {
1330 int tmp = intParse( line + SZ( ".ZoneConnection" ) );
1331 aFootprint->SetLocalZoneConnection((ZONE_CONNECTION) tmp );
1332 }
1333 else if( TESTLINE( ".ThermalWidth" ) )
1334 {
1335 BIU tmp = biuParse( line + SZ( ".ThermalWidth" ) );
1336 ignore_unused( tmp );
1337 }
1338 else if( TESTLINE( ".ThermalGap" ) )
1339 {
1340 BIU tmp = biuParse( line + SZ( ".ThermalGap" ) );
1341 ignore_unused( tmp );
1342 }
1343 else if( TESTLINE( "$EndMODULE" ) )
1344 {
1345 return; // preferred exit
1346 }
1347 }
1348
1349 THROW_IO_ERRORF( _( "Missing '$EndMODULE' for MODULE '%s'." ), aFootprint->GetFPID().GetLibItemName().wx_str() );
1350}
1351
1352
1354{
1355 std::unique_ptr<PAD> pad = std::make_unique<PAD>( aFootprint );
1356 char* line;
1357 char* saveptr;
1358
1359 while( ( line = READLINE( m_reader ) ) != nullptr )
1360 {
1361 const char* data;
1362
1363 if( TESTLINE( "Sh" ) ) // (Sh)ape and padname
1364 {
1365 // e.g. "Sh "A2" C 520 520 0 0 900"
1366 // or "Sh "1" R 157 1378 0 0 900"
1367
1368 // mypadnumber is LATIN1/CRYLIC for BOARD_FORMAT_VERSION 1, but for
1369 // BOARD_FORMAT_VERSION 2, it is UTF8 from disk.
1370 // Moving forward padnumbers will be in UTF8 on disk, as are all KiCad strings on disk.
1371 char mypadnumber[50];
1372
1373 data = line + SZ( "Sh" ) + 1; // +1 skips trailing whitespace
1374
1375 // +1 trailing whitespace.
1376 data = data + ReadDelimitedText( mypadnumber, data, sizeof( mypadnumber ) ) + 1;
1377
1378 while( isSpace( *data ) )
1379 ++data;
1380
1381 unsigned char padchar = (unsigned char) *data++;
1382 int padshape;
1383
1384 BIU size_x = biuParse( data, &data );
1385 BIU size_y = biuParse( data, &data );
1386 BIU delta_x = biuParse( data, &data );
1387 BIU delta_y = biuParse( data, &data );
1388 EDA_ANGLE orient = degParse( data );
1389
1390 switch( padchar )
1391 {
1392 case 'C': padshape = static_cast<int>( PAD_SHAPE::CIRCLE ); break;
1393 case 'R': padshape = static_cast<int>( PAD_SHAPE::RECTANGLE ); break;
1394 case 'O': padshape = static_cast<int>( PAD_SHAPE::OVAL ); break;
1395 case 'T': padshape = static_cast<int>( PAD_SHAPE::TRAPEZOID ); break;
1396 default:
1397 m_error.Printf( _( "Unknown padshape '%c=0x%02x' on line: %d of footprint: '%s'." ),
1398 padchar,
1399 padchar,
1400 (int) m_reader->LineNumber(),
1401 aFootprint->GetFPID().GetLibItemName().wx_str() );
1403 }
1404
1405 // go through a wxString to establish a universal character set properly
1406 wxString padNumber;
1407
1408 if( m_loading_format_version == 1 )
1409 {
1410 // add 8 bit bytes, file format 1 was KiCad font type byte,
1411 // simply promote those 8 bit bytes up into UNICODE. (subset of LATIN1)
1412 const unsigned char* cp = (unsigned char*) mypadnumber;
1413
1414 while( *cp )
1415 padNumber += *cp++; // unsigned, ls 8 bits only
1416 }
1417 else
1418 {
1419 // version 2, which is UTF8.
1420 padNumber = From_UTF8( mypadnumber );
1421 }
1422
1423 // chances are both were ASCII, but why take chances?
1424
1425 pad->SetNumber( padNumber );
1426 pad->SetPadstackMode( PADSTACK::MODE::NORMAL );
1427 pad->SetShape( PADSTACK::ALL_LAYERS, static_cast<PAD_SHAPE>( padshape ) );
1428 pad->SetSize( PADSTACK::ALL_LAYERS, VECTOR2I( size_x, size_y ) );
1429 pad->SetDelta( PADSTACK::ALL_LAYERS, VECTOR2I( delta_x, delta_y ) );
1430 pad->SetOrientation( orient );
1431 }
1432 else if( TESTLINE( "Dr" ) ) // (Dr)ill
1433 {
1434 // e.g. "Dr 350 0 0" or "Dr 0 0 0 O 0 0"
1435 BIU drill_x = biuParse( line + SZ( "Dr" ), &data );
1436 BIU drill_y = drill_x;
1437 BIU offs_x = biuParse( data, &data );
1438 BIU offs_y = biuParse( data, &data );
1439
1441
1442 data = strtok_r( (char*) data, delims, &saveptr );
1443
1444 if( data ) // optional shape
1445 {
1446 if( data[0] == 'O' )
1447 {
1448 drShape = PAD_DRILL_SHAPE::OBLONG;
1449
1450 data = strtok_r( nullptr, delims, &saveptr );
1451 drill_x = biuParse( data );
1452
1453 data = strtok_r( nullptr, delims, &saveptr );
1454 drill_y = biuParse( data );
1455 }
1456 }
1457
1458 pad->SetDrillShape( drShape );
1459 pad->SetPadstackMode( PADSTACK::MODE::NORMAL );
1460 pad->SetOffset( PADSTACK::ALL_LAYERS, VECTOR2I( offs_x, offs_y ) );
1461 pad->SetDrillSize( VECTOR2I( drill_x, drill_y ) );
1462 }
1463 else if( TESTLINE( "At" ) ) // (At)tribute
1464 {
1465 // e.g. "At SMD N 00888000"
1466 // sscanf( PtLine, "%s %s %X", BufLine, BufCar, &m_layerMask );
1467
1468 PAD_ATTRIB attribute;
1469
1470 data = strtok_r( line + SZ( "At" ), delims, &saveptr );
1471
1472 if( !strcmp( data, "SMD" ) )
1473 attribute = PAD_ATTRIB::SMD;
1474 else if( !strcmp( data, "CONN" ) )
1475 attribute = PAD_ATTRIB::CONN;
1476 else if( !strcmp( data, "HOLE" ) )
1477 attribute = PAD_ATTRIB::NPTH;
1478 else
1479 attribute = PAD_ATTRIB::PTH;
1480
1481 strtok_r( nullptr, delims, &saveptr ); // skip unused prm
1482 data = strtok_r( nullptr, delims, &saveptr );
1483
1484 LEG_MASK layer_mask = hexParse( data );
1485
1486 pad->SetLayerSet( leg_mask2new( m_cu_count, layer_mask ) );
1487 pad->SetAttribute( attribute );
1488 }
1489 else if( TESTLINE( "Ne" ) ) // (Ne)tname
1490 {
1491 // e.g. "Ne 461 "V5.0"
1492
1493 char buf[1024]; // can be fairly long
1494 int netcode = intParse( line + SZ( "Ne" ), &data );
1495
1496 // Store the new code mapping
1497 pad->SetNetCode( getNetCode( netcode ) );
1498
1499 // read Netname
1500 ReadDelimitedText( buf, data, sizeof(buf) );
1501
1502 if( m_board )
1503 {
1504 wxASSERT( m_board->FindNet( getNetCode( netcode ) )->GetNetname()
1506 }
1507 }
1508 else if( TESTLINE( "Po" ) ) // (Po)sition
1509 {
1510 // e.g. "Po 500 -500"
1511 VECTOR2I pos;
1512
1513 pos.x = biuParse( line + SZ( "Po" ), &data );
1514 pos.y = biuParse( data );
1515
1516 pad->SetFPRelativePosition( pos );
1517 }
1518 else if( TESTLINE( "Le" ) )
1519 {
1520 BIU tmp = biuParse( line + SZ( "Le" ) );
1521 pad->SetPadToDieLength( tmp );
1522 }
1523 else if( TESTLINE( ".SolderMask" ) )
1524 {
1525 BIU tmp = biuParse( line + SZ( ".SolderMask" ) );
1526 pad->SetLocalSolderMaskMargin( tmp );
1527 }
1528 else if( TESTLINE( ".SolderPasteRatio" ) )
1529 {
1530 double tmp = atof( line + SZ( ".SolderPasteRatio" ) );
1531 pad->SetLocalSolderPasteMarginRatio( tmp );
1532 }
1533 else if( TESTLINE( ".SolderPaste" ) )
1534 {
1535 BIU tmp = biuParse( line + SZ( ".SolderPaste" ) );
1536 pad->SetLocalSolderPasteMargin( tmp );
1537 }
1538 else if( TESTLINE( ".LocalClearance" ) )
1539 {
1540 BIU tmp = biuParse( line + SZ( ".LocalClearance" ) );
1541 pad->SetLocalClearance( tmp );
1542 }
1543 else if( TESTLINE( ".ZoneConnection" ) )
1544 {
1545 int tmp = intParse( line + SZ( ".ZoneConnection" ) );
1546 pad->SetLocalZoneConnection( (ZONE_CONNECTION) tmp );
1547 }
1548 else if( TESTLINE( ".ThermalWidth" ) )
1549 {
1550 BIU tmp = biuParse( line + SZ( ".ThermalWidth" ) );
1551 pad->SetLocalThermalSpokeWidthOverride( tmp );
1552 }
1553 else if( TESTLINE( ".ThermalGap" ) )
1554 {
1555 BIU tmp = biuParse( line + SZ( ".ThermalGap" ) );
1556 pad->SetLocalThermalGapOverride( tmp );
1557 }
1558 else if( TESTLINE( "$EndPAD" ) )
1559 {
1560 if( pad->GetSize( PADSTACK::ALL_LAYERS ).x > 0 && pad->GetSize( PADSTACK::ALL_LAYERS ).y > 0 )
1561 {
1562 aFootprint->Add( pad.release() );
1563 }
1564 else
1565 {
1566 Report( wxString::Format( _( "Invalid zero-sized pad ignored in\nfile: %s" ),
1567 m_reader->GetSource() ), RPT_SEVERITY_ERROR );
1568 }
1569
1570 return; // preferred exit
1571 }
1572 }
1573
1574 THROW_IO_ERROR( wxT( "Missing '$EndPAD'" ) );
1575}
1576
1577
1579{
1580 SHAPE_T shape;
1581 char* line = m_reader->Line(); // obtain current (old) line
1582
1583 switch( line[1] )
1584 {
1585 case 'S': shape = SHAPE_T::SEGMENT; break;
1586 case 'C': shape = SHAPE_T::CIRCLE; break;
1587 case 'A': shape = SHAPE_T::ARC; break;
1588 case 'P': shape = SHAPE_T::POLY; break;
1589 default:
1590 m_error.Printf( _( "Unknown PCB_SHAPE type:'%c=0x%02x' on line %d of footprint '%s'." ),
1591 (unsigned char) line[1],
1592 (unsigned char) line[1],
1593 (int) m_reader->LineNumber(),
1594 aFootprint->GetFPID().GetLibItemName().wx_str() );
1596 }
1597
1598 std::unique_ptr<PCB_SHAPE> dwg = std::make_unique<PCB_SHAPE>( aFootprint, shape ); // a drawing
1599
1600 const char* data;
1601
1602 // common to all cases, and we have to check their values uniformly at end
1603 BIU width = 1;
1604 int layer = FIRST_NON_COPPER_LAYER;
1605
1606 switch( shape )
1607 {
1608 case SHAPE_T::ARC:
1609 {
1610 BIU center0_x = biuParse( line + SZ( "DA" ), &data );
1611 BIU center0_y = biuParse( data, &data );
1612 BIU start0_x = biuParse( data, &data );
1613 BIU start0_y = biuParse( data, &data );
1614 EDA_ANGLE angle = degParse( data, &data );
1615
1616 width = biuParse( data, &data );
1617 layer = intParse( data );
1618
1619 dwg->SetCenter( VECTOR2I( center0_x, center0_y ) );
1620 dwg->SetStart( VECTOR2I( start0_x, start0_y ) );
1621 dwg->SetArcAngleAndEnd( angle, true );
1622 break;
1623 }
1624
1625 case SHAPE_T::SEGMENT:
1626 case SHAPE_T::CIRCLE:
1627 {
1628 // e.g. "DS -7874 -10630 7874 -10630 50 20\r\n"
1629 BIU start0_x = biuParse( line + SZ( "DS" ), &data );
1630 BIU start0_y = biuParse( data, &data );
1631 BIU end0_x = biuParse( data, &data );
1632 BIU end0_y = biuParse( data, &data );
1633
1634 width = biuParse( data, &data );
1635 layer = intParse( data );
1636
1637 dwg->SetStart( VECTOR2I( start0_x, start0_y ) );
1638 dwg->SetEnd( VECTOR2I( end0_x, end0_y ) );
1639 break;
1640 }
1641
1642 case SHAPE_T::POLY:
1643 {
1644 // e.g. "DP %d %d %d %d %d %d %d\n"
1645 BIU start0_x = biuParse( line + SZ( "DP" ), &data );
1646 BIU start0_y = biuParse( data, &data );
1647 BIU end0_x = biuParse( data, &data );
1648 BIU end0_y = biuParse( data, &data );
1649 int ptCount = intParse( data, &data );
1650
1651 width = biuParse( data, &data );
1652 layer = intParse( data );
1653
1654 dwg->SetStart( VECTOR2I( start0_x, start0_y ) );
1655 dwg->SetEnd( VECTOR2I( end0_x, end0_y ) );
1656
1657 std::vector<VECTOR2I> pts;
1658 pts.reserve( ptCount );
1659
1660 for( int ii = 0; ii < ptCount; ++ii )
1661 {
1662 if( ( line = READLINE( m_reader ) ) == nullptr )
1663 {
1664 THROW_IO_ERROR( wxT( "S_POLGON point count mismatch." ) );
1665 }
1666
1667 // e.g. "Dl 23 44\n"
1668
1669 if( !TESTLINE( "Dl" ) )
1670 {
1671 THROW_IO_ERROR( wxT( "Missing Dl point def" ) );
1672 }
1673
1674 BIU x = biuParse( line + SZ( "Dl" ), &data );
1675 BIU y = biuParse( data );
1676
1677 pts.emplace_back( x, y );
1678 }
1679
1680 dwg->SetPolyPoints( pts );
1681 break;
1682 }
1683
1684 default:
1685 // first switch code above prevents us from getting here.
1686 break;
1687 }
1688
1689 // Check for a reasonable layer:
1690 // layer must be >= FIRST_NON_COPPER_LAYER, but because microwave footprints can use the
1691 // copper layers, layer < FIRST_NON_COPPER_LAYER is allowed.
1692 if( layer < FIRST_LAYER || layer > LAST_NON_COPPER_LAYER )
1693 layer = SILKSCREEN_N_FRONT;
1694
1695 dwg->SetStroke( STROKE_PARAMS( width, LINE_STYLE::SOLID ) );
1696 dwg->SetLayer( leg_layer2new( m_cu_count, layer ) );
1697
1698 dwg->Rotate( { 0, 0 }, aFootprint->GetOrientation() );
1699 dwg->Move( aFootprint->GetPosition() );
1700 aFootprint->Add( dwg.release() );
1701}
1702
1703
1705{
1706 const char* data;
1707 const char* txt_end;
1708 const char* line = m_reader->Line(); // current (old) line
1709
1710 // e.g. "T1 6940 -16220 350 300 900 60 M I 20 N "CFCARD"\r\n"
1711 // or T1 0 500 600 400 900 80 M V 20 N"74LS245"
1712 // ouch, the last example has no space between N and "74LS245" !
1713 // that is an older version.
1714
1715 int type = intParse( line+1, &data );
1716 BIU pos0_x = biuParse( data, &data );
1717 BIU pos0_y = biuParse( data, &data );
1718 BIU size0_y = biuParse( data, &data );
1719 BIU size0_x = biuParse( data, &data );
1720 EDA_ANGLE orient = degParse( data, &data );
1721 BIU thickn = biuParse( data, &data );
1722
1723 // read the quoted text before the first call to strtok() which introduces
1724 // NULs into the string and chops it into multiple C strings, something
1725 // ReadDelimitedText() cannot traverse.
1726
1727 // convert the "quoted, escaped, UTF8, text" to a wxString, find it by skipping
1728 // as far forward as needed until the first double quote.
1729 txt_end = data + ReadDelimitedText( &m_field, data );
1730 m_field.Replace( wxT( "%V" ), wxT( "${VALUE}" ) );
1731 m_field.Replace( wxT( "%R" ), wxT( "${REFERENCE}" ) );
1733 aText->SetText( m_field );
1734
1735 // after switching to strtok, there's no easy coming back because of the
1736 // embedded nul(s?) placed to the right of the current field.
1737 // (that's the reason why strtok was deprecated...)
1738 char* mirror = strtok_r( (char*) data, delims, (char**) &data );
1739 char* hide = strtok_r( nullptr, delims, (char**) &data );
1740 char* tmp = strtok_r( nullptr, delims, (char**) &data );
1741
1742 int layer_num = tmp ? intParse( tmp ) : SILKSCREEN_N_FRONT;
1743
1744 char* italic = strtok_r( nullptr, delims, (char**) &data );
1745
1746 char* hjust = strtok_r( (char*) txt_end, delims, (char**) &data );
1747 char* vjust = strtok_r( nullptr, delims, (char**) &data );
1748
1751
1752 aText->SetFPRelativePosition( VECTOR2I( pos0_x, pos0_y ) );
1753 aText->SetTextSize( VECTOR2I( size0_x, size0_y ) );
1754
1755 aText->SetTextAngle( orient );
1756
1757 aText->SetTextThickness( thickn < 1 ? 0 : thickn );
1758
1759 aText->SetMirrored( mirror && *mirror == 'M' );
1760
1761 aText->SetVisible( !(hide && *hide == 'I') );
1762
1763 aText->SetItalic( italic && *italic == 'I' );
1764
1765 if( hjust )
1766 aText->SetHorizJustify( horizJustify( hjust ) );
1767
1768 if( vjust )
1769 aText->SetVertJustify( vertJustify( vjust ) );
1770
1771 // A protection against mal formed (or edited by hand) files:
1772 if( layer_num < FIRST_LAYER )
1773 layer_num = FIRST_LAYER;
1774 else if( layer_num > LAST_NON_COPPER_LAYER )
1775 layer_num = LAST_NON_COPPER_LAYER;
1776 else if( layer_num == LAYER_N_BACK )
1777 layer_num = SILKSCREEN_N_BACK;
1778 else if( layer_num == LAYER_N_FRONT )
1779 layer_num = SILKSCREEN_N_FRONT;
1780 else if( layer_num < LAYER_N_FRONT ) // this case is a internal layer
1781 layer_num = SILKSCREEN_N_FRONT;
1782
1783 aText->SetLayer( leg_layer2new( m_cu_count, layer_num ) );
1784}
1785
1786
1788{
1789 FP_3DMODEL t3D;
1790
1791 // Lambda to parse three space-separated doubles using wxString::ToCDouble with C locale
1792 auto parseThreeDoubles =
1793 []( const char* str, double& x, double& y, double& z ) -> bool
1794 {
1795 wxString wxStr( str );
1796 wxStr.Trim( true ).Trim( false );
1797
1798 wxStringTokenizer tokenizer( wxStr, " \t", wxTOKEN_STRTOK );
1799
1800 if( !tokenizer.HasMoreTokens() )
1801 return false;
1802
1803 wxString token1 = tokenizer.GetNextToken();
1804
1805 if( !token1.ToCDouble( &x ) || !tokenizer.HasMoreTokens() )
1806 return false;
1807
1808 wxString token2 = tokenizer.GetNextToken();
1809
1810 if( !token2.ToCDouble( &y ) || !tokenizer.HasMoreTokens() )
1811 return false;
1812
1813 wxString token3 = tokenizer.GetNextToken();
1814
1815 if( !token3.ToCDouble( &z ) )
1816 return false;
1817
1818 return true;
1819 };
1820
1821 char* line;
1822
1823 while( ( line = READLINE( m_reader ) ) != nullptr )
1824 {
1825 if( TESTLINE( "Na" ) ) // Shape File Name
1826 {
1827 char buf[512];
1828 ReadDelimitedText( buf, line + SZ( "Na" ), sizeof(buf) );
1829 t3D.m_Filename = buf;
1830 }
1831 else if( TESTLINE( "Sc" ) ) // Scale
1832 {
1833 if (!parseThreeDoubles(line + SZ("Sc"), t3D.m_Scale.x, t3D.m_Scale.y, t3D.m_Scale.z))
1834 {
1835 THROW_IO_ERROR( wxT( "Invalid scale values in 3D model" ) );
1836 }
1837 }
1838 else if( TESTLINE( "Of" ) ) // Offset
1839 {
1840 if (!parseThreeDoubles(line + SZ("Of"), t3D.m_Offset.x, t3D.m_Offset.y, t3D.m_Offset.z))
1841 {
1842 THROW_IO_ERROR( wxT( "Invalid offset values in 3D model" ) );
1843 }
1844 }
1845 else if( TESTLINE( "Ro" ) ) // Rotation
1846 {
1847 if (!parseThreeDoubles(line + SZ("Ro"), t3D.m_Rotation.x, t3D.m_Rotation.y, t3D.m_Rotation.z))
1848 {
1849 THROW_IO_ERROR( wxT( "Invalid rotation values in 3D model" ) );
1850 }
1851 }
1852 else if( TESTLINE( "$EndSHAPE3D" ) )
1853 {
1854 aFootprint->Models().push_back( t3D );
1855 return; // preferred exit
1856 }
1857 }
1858
1859 THROW_IO_ERROR( wxT( "Missing '$EndSHAPE3D'" ) );
1860}
1861
1862
1864{
1865 /* example:
1866 $DRAWSEGMENT
1867 Po 0 57500 -1000 57500 0 150
1868 De 24 0 900 0 0
1869 $EndDRAWSEGMENT
1870 */
1871
1872 std::unique_ptr<PCB_SHAPE> dseg = std::make_unique<PCB_SHAPE>( m_board );
1873
1874 char* line;
1875 char* saveptr;
1876
1877 while( ( line = READLINE( m_reader ) ) != nullptr )
1878 {
1879 const char* data;
1880
1881 if( TESTLINE( "Po" ) )
1882 {
1883 int shape = intParse( line + SZ( "Po" ), &data );
1884 BIU start_x = biuParse( data, &data );
1885 BIU start_y = biuParse( data, &data );
1886 BIU end_x = biuParse( data, &data );
1887 BIU end_y = biuParse( data, &data );
1888 BIU width = biuParse( data );
1889
1890 if( width < 0 )
1891 width = 0;
1892
1893 dseg->SetShape( static_cast<SHAPE_T>( shape ) );
1894 dseg->SetFilled( false );
1895 dseg->SetStroke( STROKE_PARAMS( width, LINE_STYLE::SOLID ) );
1896
1897 if( dseg->GetShape() == SHAPE_T::ARC )
1898 {
1899 dseg->SetCenter( VECTOR2I( start_x, start_y ) );
1900 dseg->SetStart( VECTOR2I( end_x, end_y ) );
1901 }
1902 else
1903 {
1904 dseg->SetStart( VECTOR2I( start_x, start_y ) );
1905 dseg->SetEnd( VECTOR2I( end_x, end_y ) );
1906 }
1907 }
1908 else if( TESTLINE( "De" ) )
1909 {
1910 BIU x = 0;
1911 BIU y;
1912
1913 data = strtok_r( line + SZ( "De" ), delims, &saveptr );
1914
1915 for( int i = 0; data; ++i, data = strtok_r( nullptr, delims, &saveptr ) )
1916 {
1917 switch( i )
1918 {
1919 case 0:
1920 int layer;
1921 layer = intParse( data );
1922
1923 if( layer < FIRST_NON_COPPER_LAYER )
1924 layer = FIRST_NON_COPPER_LAYER;
1925
1926 else if( layer > LAST_NON_COPPER_LAYER )
1927 layer = LAST_NON_COPPER_LAYER;
1928
1929 dseg->SetLayer( leg_layer2new( m_cu_count, layer ) );
1930 break;
1931
1932 case 1:
1933 ignore_unused( intParse( data ) );
1934 break;
1935
1936 case 2:
1937 {
1938 EDA_ANGLE angle = degParse( data );
1939
1940 if( dseg->GetShape() == SHAPE_T::ARC )
1941 dseg->SetArcAngleAndEnd( angle );
1942
1943 break;
1944 }
1945
1946 case 3:
1947 dseg->SetUuidDirect( KIID( data ) );
1948 break;
1949
1950 case 4:
1951 // Ignore state data
1952 hexParse( data );
1953 break;
1954
1955 // Bezier Control Points
1956 case 5:
1957 x = biuParse( data );
1958 break;
1959 case 6:
1960 y = biuParse( data );
1961 dseg->SetBezierC1( VECTOR2I( x, y ) );
1962 break;
1963 case 7:
1964 x = biuParse( data );
1965 break;
1966 case 8:
1967 y = biuParse( data );
1968 dseg->SetBezierC2( VECTOR2I( x, y ) );
1969 break;
1970
1971 default:
1972 break;
1973 }
1974 }
1975 }
1976 else if( TESTLINE( "$EndDRAWSEGMENT" ) )
1977 {
1978 m_board->Add( dseg.release(), ADD_MODE::APPEND );
1979 return; // preferred exit
1980 }
1981 }
1982
1983 THROW_IO_ERROR( wxT( "Missing '$EndDRAWSEGMENT'" ) );
1984}
1985
1987{
1988 /* a net description is something like
1989 * $EQUIPOT
1990 * Na 5 "/BIT1"
1991 * St ~
1992 * $EndEQUIPOT
1993 */
1994
1995 char buf[1024];
1996
1997 NETINFO_ITEM* net = nullptr;
1998 char* line;
1999 int netCode = 0;
2000
2001 while( ( line = READLINE( m_reader ) ) != nullptr )
2002 {
2003 const char* data;
2004
2005 if( TESTLINE( "Na" ) )
2006 {
2007 // e.g. "Na 58 "/cpu.sch/PAD7"\r\n"
2008
2009 netCode = intParse( line + SZ( "Na" ), &data );
2010
2011 ReadDelimitedText( buf, data, sizeof(buf) );
2012
2013 if( net == nullptr )
2014 {
2016 netCode );
2017 }
2018 else
2019 {
2020 THROW_IO_ERROR( wxT( "Two net definitions in '$EQUIPOT' block" ) );
2021 }
2022 }
2023 else if( TESTLINE( "$EndEQUIPOT" ) )
2024 {
2025 // net 0 should be already in list, so store this net
2026 // if it is not the net 0, or if the net 0 does not exists.
2027 if( net && ( net->GetNetCode() > 0 || m_board->FindNet( 0 ) == nullptr ) )
2028 {
2029 m_board->Add( net );
2030
2031 // Be sure we have room to store the net in m_netCodes
2032 if( (int)m_netCodes.size() <= netCode )
2033 m_netCodes.resize( netCode+1 );
2034
2035 m_netCodes[netCode] = net->GetNetCode();
2036 net = nullptr;
2037 }
2038 else
2039 {
2040 delete net;
2041 net = nullptr; // Avoid double deletion.
2042 }
2043
2044 return; // preferred exit
2045 }
2046 }
2047
2048 // If we are here, there is an error.
2049 delete net;
2050 THROW_IO_ERROR( wxT( "Missing '$EndEQUIPOT'" ) );
2051}
2052
2053
2055{
2056 /* examples:
2057 For a single line text:
2058 ----------------------
2059 $TEXTPCB
2060 Te "Text example"
2061 Po 66750 53450 600 800 150 0
2062 De 24 1 0 Italic
2063 $EndTEXTPCB
2064
2065 For a multi line text:
2066 ---------------------
2067 $TEXTPCB
2068 Te "Text example"
2069 Nl "Line 2"
2070 Po 66750 53450 600 800 150 0
2071 De 24 1 0 Italic
2072 $EndTEXTPCB
2073 Nl "line nn" is a line added to the current text
2074 */
2075
2076 char text[1024];
2077
2078 // maybe someday a constructor that takes all this data in one call?
2079 PCB_TEXT* pcbtxt = new PCB_TEXT( m_board );
2080 m_board->Add( pcbtxt, ADD_MODE::APPEND );
2081
2082 char* line;
2083
2084 while( ( line = READLINE( m_reader ) ) != nullptr )
2085 {
2086 const char* data;
2087
2088 if( TESTLINE( "Te" ) ) // Text line (or first line for multi line texts)
2089 {
2090 ReadDelimitedText( text, line + SZ( "Te" ), sizeof(text) );
2092 }
2093 else if( TESTLINE( "nl" ) ) // next line of the current text
2094 {
2095 ReadDelimitedText( text, line + SZ( "nl" ), sizeof(text) );
2096 pcbtxt->SetText( pcbtxt->GetText() + wxChar( '\n' ) + From_UTF8( text ) );
2097 }
2098 else if( TESTLINE( "Po" ) )
2099 {
2100 VECTOR2I size;
2101 BIU pos_x = biuParse( line + SZ( "Po" ), &data );
2102 BIU pos_y = biuParse( data, &data );
2103
2104 size.x = biuParse( data, &data );
2105 size.y = biuParse( data, &data );
2106
2107 BIU thickn = biuParse( data, &data );
2108 EDA_ANGLE angle = degParse( data );
2109
2110 pcbtxt->SetTextSize( size );
2111 pcbtxt->SetTextThickness( thickn );
2112 pcbtxt->SetTextAngle( angle );
2113
2114 pcbtxt->SetTextPos( VECTOR2I( pos_x, pos_y ) );
2115 }
2116 else if( TESTLINE( "De" ) )
2117 {
2118 // e.g. "De 21 1 68183921-93a5-49ac-91b0-49d05a0e1647 Normal C\r\n"
2119 int layer_num = intParse( line + SZ( "De" ), &data );
2120 int notMirrored = intParse( data, &data );
2121 char* uuid = strtok_r( (char*) data, delims, (char**) &data );
2122 char* style = strtok_r( nullptr, delims, (char**) &data );
2123 char* hJustify = strtok_r( nullptr, delims, (char**) &data );
2124 char* vJustify = strtok_r( nullptr, delims, (char**) &data );
2125
2126 pcbtxt->SetMirrored( !notMirrored );
2127 pcbtxt->SetUuidDirect( KIID( uuid ) );
2128 pcbtxt->SetItalic( !strcmp( style, "Italic" ) );
2129
2130 if( hJustify )
2131 {
2132 pcbtxt->SetHorizJustify( horizJustify( hJustify ) );
2133 }
2134 else
2135 {
2136 // boom, somebody changed a constructor, I was relying on this:
2137 wxASSERT( pcbtxt->GetHorizJustify() == GR_TEXT_H_ALIGN_CENTER );
2138 }
2139
2140 if( vJustify )
2141 pcbtxt->SetVertJustify( vertJustify( vJustify ) );
2142
2143 if( layer_num < FIRST_COPPER_LAYER )
2144 layer_num = FIRST_COPPER_LAYER;
2145 else if( layer_num > LAST_NON_COPPER_LAYER )
2146 layer_num = LAST_NON_COPPER_LAYER;
2147
2148 if( layer_num >= FIRST_NON_COPPER_LAYER ||
2149 is_leg_copperlayer_valid( m_cu_count, layer_num ) )
2150 pcbtxt->SetLayer( leg_layer2new( m_cu_count, layer_num ) );
2151 else // not perfect, but putting this text on front layer is a workaround
2152 pcbtxt->SetLayer( F_Cu );
2153 }
2154 else if( TESTLINE( "$EndTEXTPCB" ) )
2155 {
2156 return; // preferred exit
2157 }
2158 }
2159
2160 THROW_IO_ERROR( wxT( "Missing '$EndTEXTPCB'" ) );
2161}
2162
2163
2165{
2166 char* line;
2167
2168 while( ( line = READLINE( m_reader ) ) != nullptr )
2169 {
2170 checkpoint();
2171
2172 // read two lines per loop iteration, each loop is one TRACK or VIA
2173 // example first line:
2174 // e.g. "Po 0 23994 28800 24400 28800 150 -1" for a track
2175 // e.g. "Po 3 21086 17586 21086 17586 180 -1" for a via (uses sames start and end)
2176 const char* data;
2177
2178 if( line[0] == '$' ) // $EndTRACK
2179 return; // preferred exit
2180
2181 assert( TESTLINE( "Po" ) );
2182
2183 // legacy via type is 3 (through via) 2 (BLIND/BURIED) or 1 (MICROVIA)
2184 int legacy_viatype = intParse( line + SZ( "Po" ), &data );
2185
2186 BIU start_x = biuParse( data, &data );
2187 BIU start_y = biuParse( data, &data );
2188 BIU end_x = biuParse( data, &data );
2189 BIU end_y = biuParse( data, &data );
2190 BIU width = biuParse( data, &data );
2191
2192 // optional 7th drill parameter (must be optional in an old format?)
2193 data = strtok_r( (char*) data, delims, (char**) &data );
2194
2195 BIU drill = data ? biuParse( data ) : -1; // SetDefault() if < 0
2196
2197 // Read the 2nd line to determine the exact type, one of:
2198 // PCB_TRACE_T, PCB_VIA_T, or PCB_SEGZONE_T. The type field in 2nd line
2199 // differentiates between PCB_TRACE_T and PCB_VIA_T. With virtual
2200 // functions in use, it is critical to instantiate the PCB_VIA_T
2201 // exactly.
2202 READLINE( m_reader );
2203
2204 line = m_reader->Line();
2205
2206 // example second line:
2207 // "De 0 0 463 0 800000\r\n"
2208
2209#if 1
2210 assert( TESTLINE( "De" ) );
2211#else
2212 if( !TESTLINE( "De" ) )
2213 {
2214 // mandatory 2nd line is missing
2215 THROW_IO_ERROR( wxT( "Missing 2nd line of a TRACK def" ) );
2216 }
2217#endif
2218
2219 int makeType;
2220
2221 // parse the 2nd line to determine the type of object
2222 // e.g. "De 15 1 7 68183921-93a5-49ac-91b0-49d05a0e1647 0" for a via
2223 int layer_num = intParse( line + SZ( "De" ), &data );
2224 int type = intParse( data, &data );
2225 int net_code = intParse( data, &data );
2226 char* uuid = strtok_r( (char*) data, delims, (char**) &data );
2227
2228 // Discard flags data
2229 intParse( data, (const char**) &data );
2230
2231 if( aStructType == PCB_TRACE_T )
2232 {
2233 makeType = ( type == 1 ) ? PCB_VIA_T : PCB_TRACE_T;
2234 }
2235 else if (aStructType == NOT_USED )
2236 {
2237 continue;
2238 }
2239 else
2240 {
2241 wxFAIL_MSG( wxT( "Segment type unknown" ) );
2242 continue;
2243 }
2244
2245 PCB_TRACK* newTrack = nullptr;
2246 PCB_VIA* newVia = nullptr;
2247
2248 switch( makeType )
2249 {
2250 default:
2251 case PCB_TRACE_T: newTrack = new PCB_TRACK( m_board ); break;
2252 case PCB_VIA_T: newVia = new PCB_VIA( m_board ); break;
2253 }
2254
2255 if( makeType == PCB_VIA_T ) // Ensure layers are OK when possible:
2256 {
2257 VIATYPE viatype = VIATYPE::THROUGH;
2258
2259 if( legacy_viatype == 1 )
2260 viatype = VIATYPE::MICROVIA;
2261 else if( legacy_viatype == 2 )
2262 viatype = VIATYPE::BLIND;
2263
2264 newVia->SetViaType( viatype );
2266 newVia->SetWidth( PADSTACK::ALL_LAYERS, width );
2267
2268 newVia->SetUuidDirect( KIID( uuid ) );
2269 newVia->SetPosition( VECTOR2I( start_x, start_y ) );
2270 newVia->SetEnd( VECTOR2I( end_x, end_y ) );
2271
2272 if( drill < 0 )
2273 newVia->SetDrillDefault();
2274 else
2275 newVia->SetDrill( drill );
2276
2277 if( newVia->GetViaType() == VIATYPE::THROUGH )
2278 {
2279 newVia->SetLayerPair( F_Cu, B_Cu );
2280 }
2281 else
2282 {
2283 PCB_LAYER_ID back = leg_layer2new( m_cu_count, (layer_num >> 4) & 0xf );
2284 PCB_LAYER_ID front = leg_layer2new( m_cu_count, layer_num & 0xf );
2285
2286 if( is_leg_copperlayer_valid( m_cu_count, back ) &&
2288 {
2289 newVia->SetLayerPair( front, back );
2290 }
2291 else
2292 {
2293 delete newVia;
2294 newVia = nullptr;
2295 }
2296 }
2297 }
2298 else
2299 {
2300 newTrack->SetWidth( width );
2301
2302 newTrack->SetUuidDirect( KIID( uuid ) );
2303 newTrack->SetPosition( VECTOR2I( start_x, start_y ) );
2304 newTrack->SetEnd( VECTOR2I( end_x, end_y ) );
2305
2306 // A few legacy boards can have tracks on non existent layers, because
2307 // reducing the number of layers does not remove tracks on removed layers
2308 // If happens, skip them
2309 if( is_leg_copperlayer_valid( m_cu_count, layer_num ) )
2310 {
2311 newTrack->SetLayer( leg_layer2new( m_cu_count, layer_num ) );
2312 }
2313 else
2314 {
2315 delete newTrack;
2316 newTrack = nullptr;
2317 }
2318 }
2319
2320 if( newTrack )
2321 {
2322 newTrack->SetNetCode( getNetCode( net_code ) );
2323 m_board->Add( newTrack );
2324 }
2325
2326 if( newVia )
2327 {
2328 newVia->SetNetCode( getNetCode( net_code ) );
2329 m_board->Add( newVia );
2330 }
2331 }
2332
2333 THROW_IO_ERROR( wxT( "Missing '$EndTRACK'" ) );
2334}
2335
2336
2338{
2339 char buf[1024];
2340 wxString netname;
2341 char* line;
2342
2343 // create an empty NETCLASS without a name, but do not add it to the BOARD
2344 // yet since that would bypass duplicate netclass name checking within the BOARD.
2345 // store it temporarily in an unique_ptr until successfully inserted into the BOARD
2346 // just before returning.
2347 std::shared_ptr<NETCLASS> nc = std::make_shared<NETCLASS>( wxEmptyString );
2348
2349 while( ( line = READLINE( m_reader ) ) != nullptr )
2350 {
2351 if( TESTLINE( "AddNet" ) ) // most frequent type of line
2352 {
2353 // e.g. "AddNet "V3.3D"\n"
2354 ReadDelimitedText( buf, line + SZ( "AddNet" ), sizeof(buf) );
2355 netname = ConvertToNewOverbarNotation( From_UTF8( buf ) );
2356
2357 m_board->GetDesignSettings().m_NetSettings->SetNetclassPatternAssignment( netname, nc->GetName() );
2358 }
2359 else if( TESTLINE( "Clearance" ) )
2360 {
2361 BIU tmp = biuParse( line + SZ( "Clearance" ) );
2362 nc->SetClearance( tmp );
2363 }
2364 else if( TESTLINE( "TrackWidth" ) )
2365 {
2366 BIU tmp = biuParse( line + SZ( "TrackWidth" ) );
2367 nc->SetTrackWidth( tmp );
2368 }
2369 else if( TESTLINE( "ViaDia" ) )
2370 {
2371 BIU tmp = biuParse( line + SZ( "ViaDia" ) );
2372 nc->SetViaDiameter( tmp );
2373 }
2374 else if( TESTLINE( "ViaDrill" ) )
2375 {
2376 BIU tmp = biuParse( line + SZ( "ViaDrill" ) );
2377 nc->SetViaDrill( tmp );
2378 }
2379 else if( TESTLINE( "uViaDia" ) )
2380 {
2381 BIU tmp = biuParse( line + SZ( "uViaDia" ) );
2382 nc->SetuViaDiameter( tmp );
2383 }
2384 else if( TESTLINE( "uViaDrill" ) )
2385 {
2386 BIU tmp = biuParse( line + SZ( "uViaDrill" ) );
2387 nc->SetuViaDrill( tmp );
2388 }
2389 else if( TESTLINE( "Name" ) )
2390 {
2391 ReadDelimitedText( buf, line + SZ( "Name" ), sizeof(buf) );
2392 nc->SetName( From_UTF8( buf ) );
2393 }
2394 else if( TESTLINE( "Desc" ) )
2395 {
2396 ReadDelimitedText( buf, line + SZ( "Desc" ), sizeof(buf) );
2397 nc->SetDescription( From_UTF8( buf ) );
2398 }
2399 else if( TESTLINE( "$EndNCLASS" ) )
2400 {
2401 if( m_board->GetDesignSettings().m_NetSettings->HasNetclass( nc->GetName() ) )
2402 {
2403 // Must have been a name conflict, this is a bad board file.
2404 // User may have done a hand edit to the file.
2405
2406 // unique_ptr will delete nc on this code path
2407
2408 m_error.Printf( _( "Duplicate NETCLASS name '%s'." ), nc->GetName() );
2410 }
2411 else
2412 {
2413 m_board->GetDesignSettings().m_NetSettings->SetNetclass( nc->GetName(), nc );
2414 }
2415
2416 return; // preferred exit
2417 }
2418 }
2419
2420 THROW_IO_ERROR( wxT( "Missing '$EndNCLASS'" ) );
2421}
2422
2423
2425{
2426 std::unique_ptr<ZONE> zc = std::make_unique<ZONE>( m_board );
2427
2429 bool endContour = false;
2430 int holeIndex = -1; // -1 is the main outline; holeIndex >= 0 = hole index
2431 char buf[1024];
2432 char* line;
2433
2434 while( ( line = READLINE( m_reader ) ) != nullptr )
2435 {
2436 const char* data;
2437
2438 if( TESTLINE( "ZCorner" ) ) // new corner of the zone outlines found
2439 {
2440 // e.g. "ZCorner 25650 49500 0"
2441 BIU x = biuParse( line + SZ( "ZCorner" ), &data );
2442 BIU y = biuParse( data, &data );
2443
2444 if( endContour )
2445 {
2446 // the previous corner was the last corner of a contour.
2447 // so this corner is the first of a new hole
2448 endContour = false;
2449 zc->NewHole();
2450 holeIndex++;
2451 }
2452
2453 zc->AppendCorner( VECTOR2I( x, y ), holeIndex );
2454
2455 // Is this corner the end of current contour?
2456 // the next corner (if any) will be stored in a new contour (a hole)
2457 // intParse( data )returns 0 = usual corner, 1 = last corner of the current contour:
2458 endContour = intParse( data );
2459 }
2460 else if( TESTLINE( "ZInfo" ) ) // general info found
2461 {
2462 // e.g. 'ZInfo 68183921-93a5-49ac-91b0-49d05a0e1647 310 "COMMON"'
2463 char* uuid = strtok_r( (char*) line + SZ( "ZInfo" ), delims, (char**) &data );
2464 int netcode = intParse( data, &data );
2465
2466 if( ReadDelimitedText( buf, data, sizeof(buf) ) > (int) sizeof(buf) )
2467 THROW_IO_ERROR( wxT( "ZInfo netname too long" ) );
2468
2469 zc->SetUuidDirect( KIID( uuid ) );
2470
2471 // Init the net code only, not the netname, to be sure
2472 // the zone net name is the name read in file.
2473 // (When mismatch, the user will be prompted in DRC, to fix the actual name)
2474 zc->BOARD_CONNECTED_ITEM::SetNetCode( getNetCode( netcode ) );
2475 }
2476 else if( TESTLINE( "ZLayer" ) ) // layer found
2477 {
2478 int layer_num = intParse( line + SZ( "ZLayer" ) );
2479 zc->SetLayer( leg_layer2new( m_cu_count, layer_num ) );
2480 }
2481 else if( TESTLINE( "ZAux" ) ) // aux info found
2482 {
2483 // e.g. "ZAux 7 E"
2484 ignore_unused( intParse( line + SZ( "ZAux" ), &data ) );
2485 char* hopt = strtok_r( (char*) data, delims, (char**) &data );
2486
2487 if( !hopt )
2488 {
2489 m_error.Printf( _( "Bad ZAux for CZONE_CONTAINER '%s'" ),
2490 zc->GetNetname().GetData() );
2492 }
2493
2494 switch( *hopt ) // upper case required
2495 {
2496 case 'N': outline_hatch = ZONE_BORDER_DISPLAY_STYLE::NO_HATCH; break;
2497 case 'E': outline_hatch = ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE; break;
2498 case 'F': outline_hatch = ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_FULL; break;
2499 default:
2500 m_error.Printf( _( "Bad ZAux for CZONE_CONTAINER '%s'" ),
2501 zc->GetNetname().GetData() );
2503 }
2504
2505 // Set hatch mode later, after reading corner outline data
2506 }
2507 else if( TESTLINE( "ZSmoothing" ) )
2508 {
2509 // e.g. "ZSmoothing 0 0"
2510 int smoothingRaw = intParse( line + SZ( "ZSmoothing" ), &data );
2511 BIU cornerRadius = biuParse( data );
2512 std::optional<ZONE_SETTINGS::CORNER_SMOOTHING> smoothing =
2513 magic_enum::enum_cast<ZONE_SETTINGS::CORNER_SMOOTHING>( smoothingRaw );
2514
2515 if( !smoothing.has_value() )
2516 {
2517 m_error.Printf( _( "Bad ZSmoothing for CZONE_CONTAINER '%s'" ),
2518 zc->GetNetname().GetData() );
2520 }
2521
2522 zc->SetCornerSmoothingType( *smoothing );
2523 zc->SetCornerRadius( cornerRadius );
2524 }
2525 else if( TESTLINE( "ZKeepout" ) )
2526 {
2527 char* token;
2528 zc->SetIsRuleArea( true );
2529 zc->SetDoNotAllowPads( false ); // Not supported in legacy
2530 zc->SetDoNotAllowFootprints( false ); // Not supported in legacy
2531
2532 // e.g. "ZKeepout tracks N vias N pads Y"
2533 token = strtok_r( line + SZ( "ZKeepout" ), delims, (char**) &data );
2534
2535 while( token )
2536 {
2537 if( !strcmp( token, "tracks" ) )
2538 {
2539 token = strtok_r( nullptr, delims, (char**) &data );
2540 zc->SetDoNotAllowTracks( token && *token == 'N' );
2541 }
2542 else if( !strcmp( token, "vias" ) )
2543 {
2544 token = strtok_r( nullptr, delims, (char**) &data );
2545 zc->SetDoNotAllowVias( token && *token == 'N' );
2546 }
2547 else if( !strcmp( token, "copperpour" ) )
2548 {
2549 token = strtok_r( nullptr, delims, (char**) &data );
2550 zc->SetDoNotAllowZoneFills( token && *token == 'N' );
2551 }
2552
2553 token = strtok_r( nullptr, delims, (char**) &data );
2554 }
2555 }
2556 else if( TESTLINE( "ZOptions" ) )
2557 {
2558 // e.g. "ZOptions 0 32 F 200 200"
2559 int fillmode = intParse( line + SZ( "ZOptions" ), &data );
2560 ignore_unused( intParse( data, &data ) );
2561 char fillstate = data[1]; // here e.g. " F"
2562 BIU thermalReliefGap = biuParse( data += 2 , &data ); // +=2 for " F"
2563 BIU thermalReliefCopperBridge = biuParse( data );
2564
2565 if( fillmode)
2566 {
2568 {
2569 Report( _( "The legacy segment zone fill mode is no longer supported.\n"
2570 "Zone fills will be converted on a best-effort basis." ) , RPT_SEVERITY_WARNING );
2571
2573 }
2574 }
2575
2576 zc->SetFillMode( ZONE_FILL_MODE::POLYGONS );
2577 zc->SetIsFilled( fillstate == 'S' );
2578 zc->SetThermalReliefGap( thermalReliefGap );
2579 zc->SetThermalReliefSpokeWidth( thermalReliefCopperBridge );
2580 }
2581 else if( TESTLINE( "ZClearance" ) ) // Clearance and pad options info found
2582 {
2583 // e.g. "ZClearance 40 I"
2584 BIU clearance = biuParse( line + SZ( "ZClearance" ), &data );
2585 char* padoption = strtok_r( (char*) data, delims, (char**) &data ); // data: " I"
2586
2587 ZONE_CONNECTION popt;
2588 switch( *padoption )
2589 {
2590 case 'I': popt = ZONE_CONNECTION::FULL; break;
2591 case 'T': popt = ZONE_CONNECTION::THERMAL; break;
2592 case 'H': popt = ZONE_CONNECTION::THT_THERMAL; break;
2593 case 'X': popt = ZONE_CONNECTION::NONE; break;
2594 default:
2595 m_error.Printf( _( "Bad ZClearance padoption for CZONE_CONTAINER '%s'" ),
2596 zc->GetNetname().GetData() );
2598 }
2599
2600 zc->SetLocalClearance( clearance );
2601 zc->SetPadConnection( popt );
2602 }
2603 else if( TESTLINE( "ZMinThickness" ) )
2604 {
2605 BIU thickness = biuParse( line + SZ( "ZMinThickness" ) );
2606 zc->SetMinThickness( thickness );
2607 }
2608 else if( TESTLINE( "ZPriority" ) )
2609 {
2610 int priority = intParse( line + SZ( "ZPriority" ) );
2611 zc->SetAssignedPriority( priority );
2612 }
2613 else if( TESTLINE( "$POLYSCORNERS" ) )
2614 {
2615 // Read the PolysList (polygons that are the solid areas in the filled zone)
2616 SHAPE_POLY_SET polysList;
2617
2618 bool makeNewOutline = true;
2619
2620 while( ( line = READLINE( m_reader ) ) != nullptr )
2621 {
2622 if( TESTLINE( "$endPOLYSCORNERS" ) )
2623 break;
2624
2625 // e.g. "39610 43440 0 0"
2626 BIU x = biuParse( line, &data );
2627 BIU y = biuParse( data, &data );
2628
2629 if( makeNewOutline )
2630 polysList.NewOutline();
2631
2632 polysList.Append( x, y );
2633
2634 // end_countour was a bool when file saved, so '0' or '1' here
2635 bool end_contour = intParse( data, &data );
2636 intParse( data ); // skip corner utility flag
2637
2638 makeNewOutline = end_contour;
2639 }
2640
2641 zc->SetFilledPolysList( zc->GetFirstLayer(), polysList );
2642 }
2643 else if( TESTLINE( "$FILLSEGMENTS" ) )
2644 {
2645 while( ( line = READLINE( m_reader ) ) != nullptr )
2646 {
2647 if( TESTLINE( "$endFILLSEGMENTS" ) )
2648 break;
2649
2650 // e.g. ""%d %d %d %d\n"
2651 ignore_unused( biuParse( line, &data ) );
2652 ignore_unused( biuParse( data, &data ) );
2653 ignore_unused( biuParse( data, &data ) );
2654 ignore_unused( biuParse( data ) );
2655 }
2656 }
2657 else if( TESTLINE( "$endCZONE_OUTLINE" ) )
2658 {
2659 // Ensure keepout does not have a net
2660 // (which have no sense for a keepout zone)
2661 if( zc->GetIsRuleArea() )
2662 zc->SetNetCode( NETINFO_LIST::UNCONNECTED );
2663
2664 if( zc->GetMinThickness() > 0 )
2665 {
2666 // Inflate the fill polygon
2667 PCB_LAYER_ID layer = zc->GetFirstLayer();
2668 SHAPE_POLY_SET inflatedFill = SHAPE_POLY_SET( *zc->GetFilledPolysList( layer ) );
2669
2670 inflatedFill.InflateWithLinkedHoles( zc->GetMinThickness() / 2,
2672 ARC_HIGH_DEF / 2 );
2673
2674 zc->SetFilledPolysList( layer, inflatedFill );
2675 }
2676
2677 // should always occur, but who knows, a zone without two corners
2678 // is no zone at all, it's a spot?
2679
2680 if( zc->GetNumCorners() > 2 )
2681 {
2682 if( !zc->IsOnCopperLayer() )
2683 {
2684 zc->SetFillMode( ZONE_FILL_MODE::POLYGONS );
2685 zc->SetNetCode( NETINFO_LIST::UNCONNECTED );
2686 }
2687
2688 // HatchBorder here, after outlines corners are read
2689 // Set hatch here, after outlines corners are read
2690 zc->SetBorderDisplayStyle( outline_hatch, ZONE::GetDefaultHatchPitch(), true );
2691
2692 m_board->Add( zc.release() );
2693 }
2694
2695 return; // preferred exit
2696 }
2697 }
2698
2699 THROW_IO_ERROR( wxT( "Missing '$endCZONE_OUTLINE'" ) );
2700}
2701
2702
2704{
2705 std::unique_ptr<PCB_DIM_ALIGNED> dim = std::make_unique<PCB_DIM_ALIGNED>( m_board,
2707 VECTOR2I crossBarO;
2708 VECTOR2I crossBarF;
2709
2710 char* line;
2711
2712 while( ( line = READLINE( m_reader ) ) != nullptr )
2713 {
2714 const char* data;
2715
2716 if( TESTLINE( "$endCOTATION" ) )
2717 {
2718 dim->UpdateHeight( crossBarF, crossBarO );
2719
2720 m_board->Add( dim.release(), ADD_MODE::APPEND );
2721 return; // preferred exit
2722 }
2723 else if( TESTLINE( "Va" ) )
2724 {
2725 BIU value = biuParse( line + SZ( "Va" ) );
2726
2727 // unused; dimension value is calculated from coordinates
2728 ( void )value;
2729 }
2730 else if( TESTLINE( "Ge" ) )
2731 {
2732 // e.g. "Ge 1 21 68183921-93a5-49ac-91b0-49d05a0e1647\r\n"
2733 int shape = intParse( line + SZ( "De" ), (const char**) &data );
2734 int layer_num = intParse( data, &data );
2735 char* uuid = strtok_r( (char*) data, delims, (char**) &data );
2736
2737 dim->SetLayer( leg_layer2new( m_cu_count, layer_num ) );
2738 dim->SetUuidDirect( KIID( uuid ) );
2739
2740 // not used
2741 ( void )shape;
2742 }
2743 else if( TESTLINE( "Te" ) )
2744 {
2745 char buf[2048];
2746
2747 ReadDelimitedText( buf, line + SZ( "Te" ), sizeof(buf) );
2748 dim->SetOverrideText( From_UTF8( buf ) );
2749 dim->SetOverrideTextEnabled( true );
2750 dim->SetUnitsFormat( DIM_UNITS_FORMAT::NO_SUFFIX );
2751 dim->SetAutoUnits();
2752 }
2753 else if( TESTLINE( "Po" ) )
2754 {
2755 BIU pos_x = biuParse( line + SZ( "Po" ), &data );
2756 BIU pos_y = biuParse( data, &data );
2757 BIU width = biuParse( data, &data );
2758 BIU height = biuParse( data, &data );
2759 BIU thickn = biuParse( data, &data );
2760 EDA_ANGLE orient = degParse( data, &data );
2761 char* mirror = strtok_r( (char*) data, delims, (char**) &data );
2762
2763 dim->SetTextPos( VECTOR2I( pos_x, pos_y ) );
2764 dim->SetTextSize( VECTOR2I( width, height ) );
2765 dim->SetMirrored( mirror && *mirror == '0' );
2766 dim->SetTextThickness( thickn );
2767 dim->SetTextAngle( orient );
2768 }
2769 else if( TESTLINE( "Sb" ) )
2770 {
2771 ignore_unused( biuParse( line + SZ( "Sb" ), &data ) );
2772 BIU crossBarOx = biuParse( data, &data );
2773 BIU crossBarOy = biuParse( data, &data );
2774 BIU crossBarFx = biuParse( data, &data );
2775 BIU crossBarFy = biuParse( data, &data );
2776 BIU width = biuParse( data );
2777
2778 dim->SetLineThickness( width );
2779 crossBarO = VECTOR2I( crossBarOx, crossBarOy );
2780 crossBarF = VECTOR2I( crossBarFx, crossBarFy );
2781 }
2782 else if( TESTLINE( "Sd" ) )
2783 {
2784 ignore_unused( intParse( line + SZ( "Sd" ), &data ) );
2785 BIU featureLineDOx = biuParse( data, &data );
2786 BIU featureLineDOy = biuParse( data, &data );
2787
2788 ignore_unused( biuParse( data, &data ) );
2789 ignore_unused( biuParse( data ) );
2790
2791 dim->SetStart( VECTOR2I( featureLineDOx, featureLineDOy ) );
2792 }
2793 else if( TESTLINE( "Sg" ) )
2794 {
2795 ignore_unused( intParse( line + SZ( "Sg" ), &data ) );
2796 BIU featureLineGOx = biuParse( data, &data );
2797 BIU featureLineGOy = biuParse( data, &data );
2798
2799 ignore_unused( biuParse( data, &data ) );
2800 ignore_unused( biuParse( data ) );
2801
2802 dim->SetEnd( VECTOR2I( featureLineGOx, featureLineGOy ) );
2803 }
2804 else if( TESTLINE( "S1" ) ) // Arrow: no longer imported
2805 {
2806 ignore_unused( intParse( line + SZ( "S1" ), &data ) );
2807 biuParse( data, &data ); // skipping excessive data
2808 biuParse( data, &data ); // skipping excessive data
2809 biuParse( data, &data );
2810 biuParse( data );
2811 }
2812 else if( TESTLINE( "S2" ) ) // Arrow: no longer imported
2813 {
2814 ignore_unused( intParse( line + SZ( "S2" ), &data ) );
2815 biuParse( data, &data ); // skipping excessive data
2816 biuParse( data, &data ); // skipping excessive data
2817 biuParse( data, &data );
2818 biuParse( data, &data );
2819 }
2820 else if( TESTLINE( "S3" ) ) // Arrow: no longer imported
2821 {
2822 ignore_unused( intParse( line + SZ( "S3" ), &data ) );
2823 biuParse( data, &data ); // skipping excessive data
2824 biuParse( data, &data ); // skipping excessive data
2825 biuParse( data, &data );
2826 biuParse( data, &data );
2827 }
2828 else if( TESTLINE( "S4" ) ) // Arrow: no longer imported
2829 {
2830 ignore_unused( intParse( line + SZ( "S4" ), &data ) );
2831 biuParse( data, &data ); // skipping excessive data
2832 biuParse( data, &data ); // skipping excessive data
2833 biuParse( data, &data );
2834 biuParse( data, &data );
2835 }
2836 }
2837
2838 THROW_IO_ERROR( wxT( "Missing '$endCOTATION'" ) );
2839}
2840
2841
2843{
2844 char* line;
2845
2846 while( ( line = READLINE( m_reader ) ) != nullptr )
2847 {
2848 const char* data;
2849
2850 if( TESTLINE( "$EndPCB_TARGET" ) || TESTLINE( "$EndMIREPCB" ) )
2851 {
2852 return; // preferred exit
2853 }
2854 else if( TESTLINE( "Po" ) )
2855 {
2856 int shape = intParse( line + SZ( "Po" ), &data );
2857 int layer_num = intParse( data, &data );
2858 BIU pos_x = biuParse( data, &data );
2859 BIU pos_y = biuParse( data, &data );
2860 BIU size = biuParse( data, &data );
2861 BIU width = biuParse( data, &data );
2862 char* uuid = strtok_r( (char*) data, delims, (char**) &data );
2863
2864 if( layer_num < FIRST_NON_COPPER_LAYER )
2865 layer_num = FIRST_NON_COPPER_LAYER;
2866 else if( layer_num > LAST_NON_COPPER_LAYER )
2867 layer_num = LAST_NON_COPPER_LAYER;
2868
2869 PCB_TARGET* t = new PCB_TARGET( m_board, shape, leg_layer2new( m_cu_count, layer_num ),
2870 VECTOR2I( pos_x, pos_y ), size, width );
2871 m_board->Add( t, ADD_MODE::APPEND );
2872
2873 t->SetUuidDirect( KIID( uuid ) );
2874 }
2875 }
2876
2877 THROW_IO_ERROR( wxT( "Missing '$EndDIMENSION'" ) );
2878}
2879
2880
2881BIU PCB_IO_KICAD_LEGACY::biuParse( const char* aValue, const char** nptrptr )
2882{
2883 const char* end = aValue;
2884 double fval{};
2885 fast_float::from_chars_result result = fast_float::from_chars( aValue, aValue + strlen( aValue ), fval,
2886 fast_float::chars_format::skip_white_space );
2887 end = result.ptr;
2888
2889 if( result.ec != std::errc() )
2890 {
2891 m_error.Printf( _( "Invalid floating point number in file: '%s'\nline: %d, offset: %d" ),
2892 m_reader->GetSource().GetData(),
2893 (int) m_reader->LineNumber(),
2894 (int)( aValue - m_reader->Line() + 1 ) );
2895
2897 }
2898
2899 if( aValue == end )
2900 {
2901 m_error.Printf( _( "Missing floating point number in file: '%s'\nline: %d, offset: %d" ),
2902 m_reader->GetSource().GetData(),
2903 (int) m_reader->LineNumber(),
2904 (int)( aValue - m_reader->Line() + 1 ) );
2905
2907 }
2908
2909 if( nptrptr )
2910 *nptrptr = end;
2911
2912 fval *= diskToBiu;
2913
2914 // fval is up into the whole number realm here, and should be bounded
2915 // within INT_MIN to INT_MAX since BIU's are nanometers.
2916 return KiROUND( fval );
2917}
2918
2919
2920EDA_ANGLE PCB_IO_KICAD_LEGACY::degParse( const char* aValue, const char** nptrptr )
2921{
2922 const char* end = aValue;
2923 double fval{};
2924 fast_float::from_chars_result result = fast_float::from_chars( aValue, aValue + strlen( aValue ), fval,
2925 fast_float::chars_format::skip_white_space );
2926 end = result.ptr;
2927
2928 if( result.ec != std::errc() )
2929 {
2930 m_error.Printf( _( "Invalid floating point number in file: '%s'\nline: %d, offset: %d" ),
2931 m_reader->GetSource().GetData(),
2932 (int) m_reader->LineNumber(),
2933 (int)( aValue - m_reader->Line() + 1 ) );
2934
2936 }
2937
2938 if( aValue == end )
2939 {
2940 m_error.Printf( _( "Missing floating point number in file: '%s'\nline: %d, offset: %d" ),
2941 m_reader->GetSource().GetData(),
2942 (int) m_reader->LineNumber(),
2943 (int)( aValue - m_reader->Line() + 1 ) );
2944
2946 }
2947
2948 if( nptrptr )
2949 *nptrptr = end;
2950
2951 return EDA_ANGLE( fval, TENTHS_OF_A_DEGREE_T );
2952}
2953
2954
2955void PCB_IO_KICAD_LEGACY::init( const std::map<std::string, UTF8>* aProperties )
2956{
2958 m_cu_count = 16;
2959 m_board = nullptr;
2961 m_props = aProperties;
2962
2963 // conversion factor for saving RAM BIUs to KICAD legacy file format.
2964 biuToDisk = 1.0 / pcbIUScale.IU_PER_MM; // BIUs are nanometers & file is mm
2965
2966 // Conversion factor for loading KICAD legacy file format into BIUs in RAM
2967 // Start by assuming the *.brd file is in deci-mils.
2968 // If we see "Units mm" in the $GENERAL section, set diskToBiu to 1000000.0
2969 // then, during the file loading process, to start a conversion from
2970 // mm to nanometers. The deci-mil legacy files have no such "Units" marker
2971 // so we must assume the file is in deci-mils until told otherwise.
2972
2973 diskToBiu = pcbIUScale.IU_PER_MILS / 10; // BIUs are nanometers
2974}
2975
2976
2977//-----<FOOTPRINT LIBRARY FUNCTIONS>--------------------------------------------
2978
2979/*
2980
2981 The legacy file format is being obsoleted and this code will have a short
2982 lifetime, so it only needs to be good enough for a short duration of time.
2983 Caching all the MODULEs is a bit memory intensive, but it is a considerably
2984 faster way of fulfilling the API contract. Otherwise, without the cache, you
2985 would have to re-read the file when searching for any FOOTPRINT, and this would
2986 be very problematic filling a FOOTPRINT_LIST via this PLUGIN API. If memory
2987 becomes a concern, consider the cache lifetime policy, which determines the
2988 time that a LP_CACHE is in RAM. Note PLUGIN lifetime also plays a role in
2989 cache lifetime.
2990
2991*/
2992
2993
2994typedef boost::ptr_map< std::string, FOOTPRINT > FOOTPRINT_MAP;
2995
2996
3002{
3003 LP_CACHE( PCB_IO_KICAD_LEGACY* aOwner, const wxString& aLibraryPath );
3004
3005 // Most all functions in this class throw IO_ERROR exceptions. There are no
3006 // error codes nor user interface calls from here, nor in any PLUGIN.
3007 // Catch these exceptions higher up please.
3008
3009 void Load();
3010
3011 void ReadAndVerifyHeader( LINE_READER* aReader );
3012
3013 void SkipIndex( LINE_READER* aReader );
3014
3015 void LoadModules( LINE_READER* aReader );
3016
3017 bool IsModified();
3018 static long long GetTimestamp( const wxString& aLibPath );
3019
3020 PCB_IO_KICAD_LEGACY* m_owner; // my owner, I need its PCB_IO_KICAD_LEGACY::loadFOOTPRINT()
3021 wxString m_lib_path;
3022 FOOTPRINT_MAP m_footprints; // map or tuple of footprint_name vs. FOOTPRINT*
3024
3025 bool m_cache_dirty; // Stored separately because it's expensive to check
3026 // m_cache_timestamp against all the files.
3027 long long m_cache_timestamp; // A hash of the timestamps for all the footprint
3028 // files.
3029};
3030
3031
3032LP_CACHE::LP_CACHE( PCB_IO_KICAD_LEGACY* aOwner, const wxString& aLibraryPath ) :
3033 m_owner( aOwner ),
3034 m_lib_path( aLibraryPath ),
3035 m_writable( true ),
3036 m_cache_dirty( true ),
3038{
3039}
3040
3041
3048
3049
3050long long LP_CACHE::GetTimestamp( const wxString& aLibPath )
3051{
3052 wxFileName fn( aLibPath );
3053
3054 if( fn.IsFileReadable() && fn.GetModificationTime().IsValid() )
3055 return fn.GetModificationTime().GetValue().GetValue();
3056 else
3057 return 0;
3058}
3059
3060
3062{
3063 m_cache_dirty = false;
3064
3065 FILE_LINE_READER reader( m_lib_path );
3066
3067 ReadAndVerifyHeader( &reader );
3068 SkipIndex( &reader );
3069 LoadModules( &reader );
3070
3071 // Remember the file modification time of library file when the
3072 // cache snapshot was made, so that in a networked environment we will
3073 // reload the cache as needed.
3075}
3076
3077
3079{
3080 char* line = aReader->ReadLine();
3081 char* data;
3082
3083 if( !line )
3084 THROW_IO_ERRORF( _( "File '%s' is empty." ), m_lib_path );
3085
3086 if( !TESTLINE( "PCBNEW-LibModule-V1" ) )
3087 THROW_IO_ERRORF( _( "File '%s' is not a legacy library." ), m_lib_path );
3088
3089 while( ( line = aReader->ReadLine() ) != nullptr )
3090 {
3091 if( TESTLINE( "Units" ) )
3092 {
3093 const char* units = strtok_r( line + SZ( "Units" ), delims, &data );
3094
3095 if( !strcmp( units, "mm" ) )
3096 m_owner->diskToBiu = pcbIUScale.IU_PER_MM;
3097
3098 }
3099 else if( TESTLINE( "$INDEX" ) )
3100 {
3101 return;
3102 }
3103 }
3104}
3105
3106
3108{
3109 // Some broken INDEX sections have more than one section, due to prior bugs.
3110 // So we must read the next line after $EndINDEX tag,
3111 // to see if this is not a new $INDEX tag.
3112 bool exit = false;
3113 char* line = aReader->Line();
3114
3115 do
3116 {
3117 if( TESTLINE( "$INDEX" ) )
3118 {
3119 exit = false;
3120
3121 while( ( line = aReader->ReadLine() ) != nullptr )
3122 {
3123 if( TESTLINE( "$EndINDEX" ) )
3124 {
3125 exit = true;
3126 break;
3127 }
3128 }
3129 }
3130 else if( exit )
3131 {
3132 break;
3133 }
3134 } while( ( line = aReader->ReadLine() ) != nullptr );
3135}
3136
3137
3139{
3140 m_owner->SetReader( aReader );
3141
3142 char* line = aReader->Line();
3143
3144 do
3145 {
3146 // test first for the $MODULE, even before reading because of INDEX bug.
3147 if( TESTLINE( "$MODULE" ) )
3148 {
3149 std::unique_ptr<FOOTPRINT> fp_ptr = std::make_unique<FOOTPRINT>( m_owner->m_board );
3150
3151 std::string footprintName = StrPurge( line + SZ( "$MODULE" ) );
3152
3153 // The footprint names in legacy libraries can contain the '/' and ':'
3154 // characters which will cause the LIB_ID parser to choke.
3155 ReplaceIllegalFileNameChars( footprintName );
3156
3157 // set the footprint name first thing, so exceptions can use name.
3158 fp_ptr->SetFPID( LIB_ID( wxEmptyString, footprintName ) );
3159
3160 m_owner->loadFOOTPRINT( fp_ptr.get());
3161
3162 FOOTPRINT* fp = fp_ptr.release(); // exceptions after this are not expected.
3163
3164 // Not sure why this is asserting on debug builds. The debugger shows the
3165 // strings are the same. If it's not really needed maybe it can be removed.
3166
3167 /*
3168
3169 There was a bug in old legacy library management code
3170 (pre-PCB_IO_KICAD_LEGACY) which was introducing duplicate footprint names
3171 in legacy libraries without notification. To best recover from such
3172 bad libraries, and use them to their fullest, there are a few
3173 strategies that could be used. (Note: footprints must have unique
3174 names to be accepted into this cache.) The strategy used here is to
3175 append a differentiating version counter to the end of the name as:
3176 _v2, _v3, etc.
3177
3178 */
3179
3180 FOOTPRINT_MAP::const_iterator it = m_footprints.find( footprintName );
3181
3182 if( it == m_footprints.end() ) // footprintName is not present in cache yet.
3183 {
3184 if( !m_footprints.insert( footprintName, fp ).second )
3185 {
3186 wxFAIL_MSG( wxT( "error doing cache insert using guaranteed unique name" ) );
3187 }
3188 }
3189 else
3190 {
3191 // Bad library has a duplicate of this footprintName, generate a
3192 // unique footprint name and load it anyway.
3193 bool nameOK = false;
3194 int version = 2;
3195 char buf[48];
3196
3197 while( !nameOK )
3198 {
3199 std::string newName = footprintName;
3200
3201 newName += "_v";
3202 snprintf( buf, sizeof(buf), "%d", version++ );
3203 newName += buf;
3204
3205 it = m_footprints.find( newName );
3206
3207 if( it == m_footprints.end() )
3208 {
3209 nameOK = true;
3210
3211 fp->SetFPID( LIB_ID( wxEmptyString, newName ) );
3212
3213 if( !m_footprints.insert( newName, fp ).second )
3214 {
3215 wxFAIL_MSG( wxT( "error doing cache insert using guaranteed unique "
3216 "name" ) );
3217 }
3218 }
3219 }
3220 }
3221 }
3222
3223 } while( ( line = aReader->ReadLine() ) != nullptr );
3224}
3225
3226
3227long long PCB_IO_KICAD_LEGACY::GetLibraryTimestamp( const wxString& aLibraryPath ) const
3228{
3229 return LP_CACHE::GetTimestamp( aLibraryPath );
3230}
3231
3232
3233void PCB_IO_KICAD_LEGACY::cacheLib( const wxString& aLibraryPath )
3234{
3235 if( !m_cache || m_cache->m_lib_path != aLibraryPath || m_cache->IsModified() )
3236 {
3237 // a spectacular episode in memory management:
3238 delete m_cache;
3239 m_cache = new LP_CACHE( this, aLibraryPath );
3240 m_cache->Load();
3241 }
3242}
3243
3244
3245void PCB_IO_KICAD_LEGACY::FootprintEnumerate( wxArrayString& aFootprintNames, const wxString& aLibPath,
3246 bool aBestEfforts, const std::map<std::string, UTF8>* aProperties )
3247{
3248 wxString errorMsg;
3249
3250 init( aProperties );
3251
3252 try
3253 {
3254 cacheLib( aLibPath );
3255 }
3256 catch( const IO_ERROR& ioe )
3257 {
3258 errorMsg = ioe.What();
3259 }
3260
3261 // Some of the files may have been parsed correctly so we want to add the valid files to
3262 // the library.
3263
3264 for( const auto& footprint : m_cache->m_footprints )
3265 aFootprintNames.Add( From_UTF8( footprint.first.c_str() ) );
3266
3267 if( !errorMsg.IsEmpty() && !aBestEfforts )
3268 THROW_IO_ERROR( errorMsg );
3269}
3270
3271
3272std::unique_ptr<FOOTPRINT> PCB_IO_KICAD_LEGACY::FootprintLoad( const wxString& aLibraryPath,
3273 const wxString& aFootprintName, bool aKeepUUID,
3274 const std::map<std::string, UTF8>* aProperties )
3275{
3276 init( aProperties );
3277
3278 cacheLib( aLibraryPath );
3279
3280 const FOOTPRINT_MAP& footprints = m_cache->m_footprints;
3281 FOOTPRINT_MAP::const_iterator it = footprints.find( TO_UTF8( aFootprintName ) );
3282
3283 if( it == footprints.end() )
3284 return nullptr;
3285
3286 // Return copy of already loaded FOOTPRINT
3287 std::unique_ptr<FOOTPRINT> copy( static_cast<FOOTPRINT*>( it->second->Duplicate( IGNORE_PARENT_GROUP ) ) );
3288 copy->SetParent( nullptr );
3289 return copy;
3290}
3291
3292
3293bool PCB_IO_KICAD_LEGACY::DeleteLibrary( const wxString& aLibraryPath,
3294 const std::map<std::string, UTF8>* aProperties )
3295{
3296 wxFileName fn = aLibraryPath;
3297
3298 if( !fn.FileExists() )
3299 return false;
3300
3301 // Some of the more elaborate wxRemoveFile() crap puts up its own wxLog dialog
3302 // we don't want that. we want bare metal portability with no UI here.
3303 if( wxRemove( aLibraryPath ) )
3304 THROW_IO_ERRORF( _( "Footprint library '%s' cannot be deleted." ), aLibraryPath.GetData() );
3305
3306 if( m_cache && m_cache->m_lib_path == aLibraryPath )
3307 {
3308 delete m_cache;
3309 m_cache = nullptr;
3310 }
3311
3312 return true;
3313}
3314
3315
3316bool PCB_IO_KICAD_LEGACY::IsLibraryWritable( const wxString& aLibraryPath )
3317{
3318 init( nullptr );
3319
3320 cacheLib( aLibraryPath );
3321
3322 return m_cache->m_writable;
3323}
3324
3325
3327 m_cu_count( 16 ), // for FootprintLoad()
3328 m_progressReporter( nullptr ),
3329 m_lastProgressLine( 0 ),
3330 m_lineCount( 0 ),
3331 m_reader( nullptr ),
3332 m_fp( nullptr ),
3333 m_cache( nullptr )
3334{
3335 init( nullptr );
3336}
3337
3338
constexpr int ARC_HIGH_DEF
Definition base_units.h:144
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:128
LAYER_T
The allowed types of layers, same as Specctra DSN spec.
Definition board.h:243
@ LAYER_CLASS_OTHERS
@ LAYER_CLASS_SILK
@ LAYER_CLASS_COPPER
@ LAYER_CLASS_EDGES
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:982
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)
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.
void SetLocked(bool aLocked) override
Definition board_item.h:427
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition board_item.h:383
void SetFPRelativePosition(const VECTOR2I &aPos)
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:411
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:556
void SetMirrored(bool isMirrored)
Definition eda_text.cpp:354
void SetVertJustify(GR_TEXT_V_ALIGN_T aType)
Definition eda_text.cpp:378
GR_TEXT_H_ALIGN_T GetHorizJustify() const
Definition eda_text.h:239
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:347
virtual void SetText(const wxString &aText)
Definition eda_text.cpp:236
void SetItalic(bool aItalic)
Set the text to be italic - this will also update the font if needed.
Definition eda_text.cpp:290
void SetHorizJustify(GR_TEXT_H_ALIGN_T aType)
Definition eda_text.cpp:370
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:418
EDA_ANGLE GetOrientation() const
Definition footprint.h:382
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:657
void SetOrientation(const EDA_ANGLE &aNewAngle)
void SetLocalSolderPasteMarginRatio(std::optional< double > aRatio)
Definition footprint.h:488
void SetPath(const KIID_PATH &aPath)
Definition footprint.h:456
void SetKeywords(const wxString &aKeywords)
Definition footprint.h:453
void SetAttributes(int aAttributes)
Definition footprint.h:510
PCB_FIELD & Value()
read/write accessors:
Definition footprint.h:831
void SetLocalZoneConnection(ZONE_CONNECTION aType)
Definition footprint.h:490
const LIB_ID & GetFPID() const
Definition footprint.h:417
PCB_FIELD & Reference()
Definition footprint.h:832
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Adds an item to the container.
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
std::vector< FP_3DMODEL > & Models()
Definition footprint.h:375
void SetLibDescription(const wxString &aDesc)
Definition footprint.h:450
void SetLocalSolderMaskMargin(std::optional< int > aMargin)
Definition footprint.h:482
void SetLocalClearance(std::optional< int > aClearance)
Definition footprint.h:479
void SetLocalSolderPasteMargin(std::optional< int > aMargin)
Definition footprint.h:485
VECTOR2I GetPosition() const override
Definition footprint.h:379
VECTOR3D m_Offset
3D model offset (mm)
Definition footprint.h:134
VECTOR3D m_Rotation
3D model rotation (degrees)
Definition footprint.h:133
VECTOR3D m_Scale
3D model scaling factor (dimensionless)
Definition footprint.h:132
wxString m_Filename
The 3D shape filename in 3D library.
Definition footprint.h:136
Helper for storing and iterating over GAL_LAYER_IDs.
Definition layer_ids.h:431
GAL_SET & set()
Definition layer_ids.h:447
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:281
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
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:97
A PCB_IO 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
Check 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 with this scale factor
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
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:404
virtual bool CanReadFootprint(const wxString &aFileName) const
Check 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
Definition pcb_io.cpp:40
PCB_IO(const wxString &aName)
Definition pcb_io.h:383
const std::map< std::string, UTF8 > * m_props
Properties passed via Save() or Load(), no ownership, may be NULL.
Definition pcb_io.h:407
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:572
void SetEnd(const VECTOR2I &aEnd)
Definition pcb_track.h:94
void SetPosition(const VECTOR2I &aPos) override
Definition pcb_track.h:87
virtual void SetWidth(int aWidth)
Definition pcb_track.h:91
void SetDrillDefault()
Set the drill value for vias to the default value UNDEFINED_DRILL_DIAMETER.
Definition pcb_track.h:820
void SetDrill(int aDrill)
Definition pcb_track.h:801
void SetPadstackMode(PADSTACK::MODE aMode)
Definition pcb_track.h:495
void SetPosition(const VECTOR2I &aPoint) override
Definition pcb_track.h:604
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:423
VIATYPE GetViaType() const
Definition pcb_track.h:422
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.
Handle zones parameters.
static int GetDefaultHatchPitch()
Definition zone.cpp:1672
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:460
#define _(s)
@ TENTHS_OF_A_DEGREE_T
Definition eda_angle.h:29
#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:87
@ FP_EXCLUDE_FROM_POS_FILES
Definition footprint.h:88
@ FP_EXCLUDE_FROM_BOM
Definition footprint.h:89
@ FP_THROUGH_HOLE
Definition footprint.h:86
#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()
static wxString From_UTF8(const char *cstring)
Convert a UTF8 encoded C string to a wxString for all wxWidgets build modes.
Definition idf_helpers.h:44
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition idf_helpers.h:37
void ignore_unused(const T &)
Definition ignore.h:20
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:229
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.
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.
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:708
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