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