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