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