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