KiCad PCB EDA Suite
Loading...
Searching...
No Matches
target3001_parser.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 The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20#include "target3001_parser.h"
21
22#include <algorithm>
23#include <cstring>
24#include <map>
25#include <set>
26
27#include <wx/ffile.h>
28#include <wx/intl.h>
29#include <wx/filename.h>
30#include <wx/strconv.h>
31
32#include <core/base64.h>
33#include <ki_exception.h>
34
35
36namespace TARGET3001
37{
38
39namespace
40{
41
42// Version thresholds at which the settings block gained fields
43constexpr int32_t V_SETTINGS_A = 0x232a;
44constexpr int32_t V_SETTINGS_B = 0x232b;
45constexpr int32_t V_TRACK_WIDTH = 0x232c;
46constexpr int32_t V_LOGBOOK = 0x232d;
47constexpr int32_t V_SETTINGS_C = 0x232e;
48constexpr int32_t V_SETTINGS_D = 0x232f;
49constexpr int32_t V_SETTINGS_E = 0x2330;
50constexpr int32_t V_COMPONENT_EXTRA = 0x2711;
51constexpr int32_t V_SETTINGS_F = 0x2712;
52constexpr int32_t V_SAVE_TIME = 0x271b;
53constexpr int32_t V_SETTINGS_G = 0x271c;
54constexpr int32_t V_SETTINGS_H = 0x271d;
55constexpr int32_t V_SETTINGS_I = 0x271e;
56constexpr int32_t V_SETTINGS_J = 0x271f;
57constexpr int32_t V_SETTINGS_K = 0x2720;
58constexpr int32_t V_BLOBS = 0x272e;
59constexpr int32_t V_SETTINGS_L = 0x272f;
60constexpr int32_t V_CHECKSUM = 0x2739;
61constexpr int32_t V_SETTINGS_M = 0x2743;
62
63constexpr int MAX_DEPTH = 32;
64
65// A component element storing this signal holds a pad number in its component field instead
66constexpr uint16_t LINKED_SIGNAL = 65503;
67
68
70void resolveLinkedSignals( COMPONENT& aComponent, uint16_t aOwner )
71{
72 std::map<uint16_t, uint16_t> padSignals;
73
74 for( const ELEMENT& e : aComponent.packageItems )
75 {
76 if( e.type == ELEMENT_TYPE::PAD && e.pad.number != 0 )
77 padSignals[e.pad.number] = e.base.signal;
78 }
79
80 auto resolve = [&]( ELEMENT& aElem )
81 {
82 if( aElem.base.signal != LINKED_SIGNAL )
83 return;
84
85 auto it = padSignals.find( aElem.base.component );
86 aElem.base.signal = ( it != padSignals.end() && it->second != LINKED_SIGNAL ) ? it->second : 0;
87 aElem.base.component = aOwner;
88 };
89
90 for( ELEMENT& e : aComponent.packageItems )
91 resolve( e );
92
93 for( ELEMENT& sym : aComponent.symbols )
94 {
95 if( sym.symbol )
96 {
97 for( ELEMENT& e : sym.symbol->children )
98 resolve( e );
99 }
100 }
101}
102
103const std::string PASSWORD_MARKER_A = "$%&\xa7";
104const std::string PASSWORD_MARKER_B = "$%&\xa7!";
105
106
107class READER
108{
109public:
110 explicit READER( const std::vector<uint8_t>& aData ) :
111 m_data( aData ),
112 m_pos( 0 )
113 {
114 }
115
116 size_t Pos() const { return m_pos; }
117 size_t Remaining() const { return m_data.size() - m_pos; }
118 bool AtEnd() const { return m_pos >= m_data.size(); }
119
120 void Require( size_t aBytes ) const
121 {
122 if( aBytes > Remaining() )
123 {
124 THROW_IO_ERROR( wxString::Format( _( "Unexpected end of TARGET 3001! file at offset %zu." ),
125 m_pos ) );
126 }
127 }
128
129 uint8_t U8()
130 {
131 Require( 1 );
132 return m_data[m_pos++];
133 }
134
135 uint16_t U16()
136 {
137 Require( 2 );
138 uint16_t v = m_data[m_pos] | ( m_data[m_pos + 1] << 8 );
139 m_pos += 2;
140 return v;
141 }
142
143 uint32_t U32()
144 {
145 Require( 4 );
146 uint32_t v = uint32_t( m_data[m_pos] ) | ( uint32_t( m_data[m_pos + 1] ) << 8 )
147 | ( uint32_t( m_data[m_pos + 2] ) << 16 ) | ( uint32_t( m_data[m_pos + 3] ) << 24 );
148 m_pos += 4;
149 return v;
150 }
151
152 int32_t S32() { return static_cast<int32_t>( U32() ); }
153
154 float F32()
155 {
156 uint32_t bits = U32();
157 float v;
158 std::memcpy( &v, &bits, sizeof( v ) );
159 return v;
160 }
161
162 void Skip( size_t aBytes )
163 {
164 Require( aBytes );
165 m_pos += aBytes;
166 }
167
168 std::vector<uint8_t> Bytes( size_t aBytes )
169 {
170 Require( aBytes );
171 std::vector<uint8_t> out( m_data.begin() + m_pos, m_data.begin() + m_pos + aBytes );
172 m_pos += aBytes;
173 return out;
174 }
175
177 size_t Count( size_t aMinItemSize )
178 {
179 int32_t n = S32();
180
181 if( n < 0 )
182 Fail( "negative count" );
183
184 if( aMinItemSize && size_t( n ) > Remaining() / aMinItemSize )
185 Fail( "count exceeds file size" );
186
187 return size_t( n );
188 }
189
190 std::string LStr()
191 {
192 size_t len = Count( 1 );
193 std::string s( reinterpret_cast<const char*>( m_data.data() + m_pos ), len );
194 m_pos += len;
195 return s;
196 }
197
198 [[noreturn]] void Fail( const char* aWhat ) const
199 {
200 THROW_IO_ERROR( wxString::Format( _( "Invalid TARGET 3001! file at offset %zu (%s)." ), m_pos,
201 aWhat ) );
202 }
203
204private:
205 const std::vector<uint8_t>& m_data;
206 size_t m_pos;
207};
208
209
210class PARSER
211{
212public:
213 explicit PARSER( const std::vector<uint8_t>& aData ) :
214 m_r( aData )
215 {
216 }
217
218 PROJECT Parse()
219 {
220 PROJECT p;
221
222 p.version = m_r.S32();
223 m_version = p.version;
224
225 parseHeader( p );
226
227 p.signals = parseTopList( nullptr );
228 p.components = parseTopList( &m_componentSlots );
229
230 // The two trees are known by their records' tags; a file may store them in the other order
231 if( holdsOnly( p.signals, ELEMENT_TYPE::COMPONENT ) && holdsOnly( p.components, ELEMENT_TYPE::SIGNAL ) )
232 std::swap( p.signals, p.components );
233
234 appendFlattened( p.schematicItems, 0 );
235 appendFlattened( p.boardItems, 0 );
236
237 parseSettings( p.settings );
238
239 if( !m_r.AtEnd() )
240 m_r.Fail( "trailing data" );
241
242 p.ApplyTextLanguage();
243
244 return p;
245 }
246
247private:
248 void parseHeader( PROJECT& aProject )
249 {
250 aProject.schematicTextFont = m_r.LStr();
251
252 if( aProject.schematicTextFont == PASSWORD_MARKER_A || aProject.schematicTextFont == PASSWORD_MARKER_B )
253 THROW_IO_ERROR( _( "Password-protected TARGET 3001! projects are not supported." ) );
254
255 aProject.boardTextFont = m_r.LStr();
256 aProject.signalNameFont = m_r.LStr();
257
258 m_r.Skip( 70 );
259
260 m_r.Skip( 94 );
261
262 std::vector<uint8_t> functions = m_r.Bytes( BOARD_LAYER_COUNT );
263 std::vector<uint8_t> associations = m_r.Bytes( BOARD_LAYER_COUNT );
264 std::copy( functions.begin(), functions.end(), aProject.layerFunctions.begin() );
265 std::copy( associations.begin(), associations.end(), aProject.layerAssociations.begin() );
266
267 m_r.Skip( 119 );
268 uint8_t prefixCount = m_r.U8();
269
270 for( int i = 0; i < prefixCount; i++ )
271 {
272 m_r.U16();
273 m_r.LStr();
274 }
275 }
276
283 static bool holdsOnly( const std::vector<ELEMENT>& aList, ELEMENT_TYPE aType )
284 {
285 bool any = false;
286
287 for( const ELEMENT& e : aList )
288 {
289 if( e.type == ELEMENT_TYPE::NONE )
290 continue;
291
292 if( e.type != aType )
293 return false;
294
295 any = true;
296 }
297
298 return any;
299 }
300
301 std::vector<ELEMENT> parseTopList( size_t* aSlots )
302 {
303 if( m_r.U8() != uint8_t( ELEMENT_TYPE::LIST ) )
304 m_r.Fail( "element list expected" );
305
306 size_t n = m_r.Count( 1 );
307 std::vector<ELEMENT> out;
308 out.reserve( n );
309
310 for( size_t i = 0; i < n; i++ )
311 {
312 size_t before = out.size();
313 appendFlattened( out, 1 );
314
315 if( out.size() == before )
316 out.emplace_back();
317 }
318
319 if( aSlots )
320 *aSlots = n;
321
322 return out;
323 }
324
325 size_t parseListBody( std::vector<ELEMENT>& aOut, int aDepth )
326 {
327 size_t n = m_r.Count( 1 );
328 aOut.reserve( aOut.size() + n );
329
330 for( size_t i = 0; i < n; i++ )
331 appendFlattened( aOut, aDepth + 1 );
332
333 return n;
334 }
335
337 void appendFlattened( std::vector<ELEMENT>& aOut, int aDepth )
338 {
339 if( aDepth > MAX_DEPTH )
340 m_r.Fail( "element nesting too deep" );
341
342 uint8_t tag = m_r.U8();
343
344 switch( tag )
345 {
346 case uint8_t( ELEMENT_TYPE::NONE ):
347 return;
348
349 case uint8_t( ELEMENT_TYPE::LIST ):
350 parseListBody( aOut, aDepth );
351 return;
352
353 case uint8_t( ELEMENT_TYPE::LAYER_FUNCTIONS ):
354 {
355 size_t n = m_r.Count( 4 );
356
357 for( size_t i = 0; i < n; i++ )
358 m_r.LStr();
359
360 return;
361 }
362
363 default:
364 break;
365 }
366
367 if( tag < uint8_t( ELEMENT_TYPE::LINE ) || tag > uint8_t( ELEMENT_TYPE::DRC_MARKER ) )
368 m_r.Fail( "unknown element class" );
369
370 ELEMENT& e = aOut.emplace_back();
371 e.type = ELEMENT_TYPE( tag );
372 parseBase( e.base );
373 parseBody( e, aDepth );
374 }
375
376 std::vector<ELEMENT> parseChild( int aDepth )
377 {
378 std::vector<ELEMENT> out;
379 appendFlattened( out, aDepth + 1 );
380 return out;
381 }
382
383 void parseBase( ELEMENT_BASE& aBase )
384 {
385 aBase.component = m_r.U16();
386 aBase.signal = m_r.U16();
387 aBase.x = m_r.S32();
388 aBase.y = m_r.S32();
389 aBase.flags = m_r.U8();
390 aBase.status = m_r.U8();
391 aBase.layer = m_r.U8();
392 aBase.classTag = m_r.U8();
393
394 if( ( aBase.flags & 0xfe ) == 2 )
395 aBase.extra = m_r.U32();
396
397 size_t n = m_r.Count( 4 );
398
399 if( n % 2 )
400 m_r.Fail( "odd property count" );
401
402 aBase.props.reserve( n / 2 );
403
404 for( size_t i = 0; i < n; i += 2 )
405 {
406 std::string key = m_r.LStr();
407 std::string value = m_r.LStr();
408 aBase.props.emplace_back( std::move( key ), std::move( value ) );
409 }
410 }
411
412 void parseBody( ELEMENT& aElem, int aDepth )
413 {
414 switch( aElem.type )
415 {
417 {
418 LINE& l = aElem.line;
419 l.x2 = m_r.S32();
420 l.y2 = m_r.S32();
421 l.width = m_r.S32();
422 l.clearance = m_r.S32();
423 l.dashStyle = m_r.U8();
424 l.roundEnds = m_r.U8();
425 break;
426 }
427
429 {
430 PIN& p = aElem.pin;
431 p.direction = m_r.F32();
432 p.length = m_r.S32();
433 p.width = m_r.S32();
434 p.electricalType = m_r.U8();
435 p.swapLevel = m_r.U16();
436 p.inverted = m_r.U8();
437 p.clock = m_r.U8();
438 p.showNumber = m_r.U8();
439 p.supplyPin = m_r.U8();
440 p.pinNumber = m_r.U16();
441 aElem.children = parseChild( aDepth );
442 break;
443 }
444
446 {
447 PAD& p = aElem.pad;
448 p.direction = m_r.F32();
449 p.shape = m_r.U8();
450 p.width = m_r.S32();
451 p.height = m_r.S32();
452 p.clearance = m_r.S32();
453 p.drill = m_r.S32();
454 p.reserved = m_r.S32();
455 p.number = m_r.U16();
456 break;
457 }
458
460 {
461 TRIANGLE& t = aElem.triangle;
462 t.x2 = m_r.S32();
463 t.y2 = m_r.S32();
464 t.x3 = m_r.S32();
465 t.y3 = m_r.S32();
466 break;
467 }
468
470 {
471 RECTANGLE& r = aElem.rectangle;
472 r.height = m_r.S32();
473 r.width = m_r.S32();
474 r.direction = m_r.F32();
475 break;
476 }
477
479 {
480 ARC& a = aElem.arc;
481 a.endX = m_r.S32();
482 a.endY = m_r.S32();
483 a.centerX = m_r.S32();
484 a.centerY = m_r.S32();
485 a.radius = m_r.S32();
486 a.width = m_r.S32();
487 a.clearance = m_r.S32();
488 a.startAngle = m_r.F32();
489 a.endAngle = m_r.F32();
490
491 if( !std::isfinite( a.startAngle ) || !std::isfinite( a.endAngle ) )
492 m_r.Fail( "arc angle is not a number" );
493
494 break;
495 }
496
498 aElem.disc.radius = m_r.S32();
499 break;
500
502 {
503 TEXT& t = aElem.text;
504 t.text = m_r.LStr();
505 t.height = m_r.S32();
506 t.width = m_r.S32();
507 t.direction = m_r.F32();
508 t.weight = m_r.U8();
509 t.noAutoUpright = m_r.U8();
510 t.mirrored = m_r.U8();
511 t.function = m_r.U8();
512 t.font = m_r.LStr();
513 break;
514 }
515
517 {
518 FIDUCIAL& f = aElem.fiducial;
519 f.kind = m_r.U8();
520 f.radius = m_r.S32();
521 f.width = m_r.S32();
522 f.direction = m_r.F32();
523 break;
524 }
525
527 {
528 auto c = std::make_shared<COMPONENT>();
529 c->name = m_r.LStr();
530 c->value = m_r.LStr();
531 c->prefix = m_r.LStr();
532 c->library = m_r.LStr();
533 c->package = m_r.LStr();
534 c->packageSource = m_r.LStr();
535 c->referenceNumber = m_r.U16();
536
537 if( c->referenceNumber >= 65000 && c->referenceNumber < 65535 )
538 c->referenceExtension = m_r.U16();
539
540 c->connectionCount = m_r.U16();
541 c->gateState = m_r.U8();
542 c->packageItems = parseChild( aDepth );
543 c->symbols = parseChild( aDepth );
544
545 for( uint8_t& b : c->setBitmap )
546 b = m_r.U8();
547
548 resolveLinkedSignals( *c, aElem.base.component );
549
550 aElem.component = std::move( c );
551 break;
552 }
553
555 {
556 auto s = std::make_shared<SYMBOL>();
557 s->name = m_r.LStr();
558 s->suffix = m_r.LStr();
559 s->gateOrdinal = m_r.U16();
560 s->insertRequired = m_r.U8();
561 s->swapLevel = m_r.U16();
562 s->children = parseChild( aDepth );
563 aElem.symbol = std::move( s );
564 break;
565 }
566
568 {
569 auto p = std::make_shared<PACKAGE>();
570 p->package = m_r.LStr();
571 p->library = m_r.LStr();
572 p->padCount = m_r.U16();
573 p->bottomSide = m_r.U8();
574 p->direction = m_r.F32();
575 aElem.package = std::move( p );
576 break;
577 }
578
580 {
581 auto s = std::make_shared<SIGNAL>();
582 s->name = m_r.LStr();
583 s->connections = parseChild( aDepth );
584 s->tracks = parseChild( aDepth );
585 s->minWidth = m_r.S32();
586 s->minSpacing = m_r.S32();
587 s->string3 = m_r.LStr();
588 s->string4 = m_r.LStr();
589 s->record = m_r.Bytes( 48 );
590 s->string5 = m_r.LStr();
591 aElem.signal = std::move( s );
592 break;
593 }
594
596 {
597 SPLINE& s = aElem.spline;
598 s.endX = m_r.S32();
599 s.endY = m_r.S32();
600 s.controlX = m_r.S32();
601 s.controlY = m_r.S32();
602 s.width = m_r.S32();
603 s.clearance = m_r.S32();
604
605 break;
606 }
607
609 aElem.marker.radius = m_r.S32();
610 aElem.marker.text = m_r.LStr();
611 break;
612
613 default:
614 m_r.Fail( "unknown element class" );
615 }
616 }
617
618 void skipStrings( size_t aCount )
619 {
620 for( size_t i = 0; i < aCount; i++ )
621 m_r.LStr();
622 }
623
624 void skipCountedStrings() { skipStrings( m_r.Count( 4 ) ); }
625
626 void parseSettings( PROJECT_SETTINGS& aSettings )
627 {
628 m_r.Skip( 16 + 16 + 52 );
629 m_r.Skip( 3 * 4 );
630 m_r.Skip( 40 + 40 );
631 m_r.Skip( 5 * 4 );
632 m_r.Skip( 29 + 29 );
633
634 size_t records = m_r.Count( 16 );
635 m_r.Skip( records * 16 );
636
637 skipCountedStrings(); // functions
638 m_r.Skip( 30 );
639 m_r.LStr();
640
641 uint16_t routeLayers = m_r.U16();
642 m_r.Skip( size_t( routeLayers ) * 4 );
643
644 size_t propertyStrings = m_r.Count( 4 );
645
646 for( size_t i = 0; i + 1 < propertyStrings; i += 2 )
647 {
648 std::string key = m_r.LStr();
649 aSettings.properties.emplace_back( std::move( key ), m_r.LStr() );
650 }
651
652 if( propertyStrings % 2 )
653 m_r.LStr();
654
655 size_t padstacks = m_r.Count( 4 + 5 + 500 );
656
657 for( size_t i = 0; i < padstacks; i++ )
658 {
659 PADSTACK_DEF& def = aSettings.padstacks.emplace_back();
660 def.name = m_r.LStr();
661 m_r.Skip( 5 ); // router cost and cached use count
662
663 for( bool& on : def.onLayer )
664 on = m_r.U8() != 0;
665
666 for( int32_t& grow : def.oversize )
667 grow = int32_t( m_r.U32() );
668 }
669
670 m_r.Skip( size_t( routeLayers ) * 5 );
671 m_r.Skip( padstacks * 8 );
672
673 m_r.U32();
674 skipCountedStrings();
675 m_r.Skip( 100 + 13 + 208 );
676 skipStrings( 16 );
677
678 m_r.U8();
679 m_r.LStr();
680 m_r.LStr();
681 m_r.Skip( 22 + 15 );
682 m_r.LStr();
683 aSettings.savedBy = m_r.LStr();
684
685 size_t records9 = m_r.Count( 4 + 9 );
686
687 for( size_t i = 0; i < records9; i++ )
688 {
689 m_r.LStr();
690 m_r.Skip( 9 );
691 }
692
693 m_r.U8();
694
695 aSettings.sheetNames.reserve( 100 );
696
697 for( int i = 0; i < 100; i++ )
698 aSettings.sheetNames.push_back( m_r.LStr() );
699
700 skipStrings( 32 );
701 m_r.U8();
702
703 if( m_version >= V_SETTINGS_A )
704 {
705 m_r.LStr();
706 m_r.LStr();
707 m_r.Skip( 49 );
708 }
709
710 if( m_version >= V_SETTINGS_B )
711 m_r.LStr();
712
713 if( m_version >= V_TRACK_WIDTH )
714 m_r.Skip( 8 );
715
716 if( m_version >= V_LOGBOOK )
717 skipStrings( 3 );
718
719 if( m_version >= V_SETTINGS_C )
720 m_r.LStr();
721
722 if( m_version >= V_SETTINGS_D )
723 m_r.U16();
724
725 if( m_version >= V_SETTINGS_E )
726 m_r.LStr();
727
728 // The reader only takes these tables when the stream has not ended
729 if( m_version >= V_SETTINGS_E && !m_r.AtEnd() )
730 {
731 for( int i = 0; i < 12; i++ )
732 {
733 m_r.LStr();
734 m_r.Skip( 10 );
735 }
736
737 for( int i = 0; i < 12; i++ )
738 {
739 m_r.LStr();
740 m_r.Skip( 18 );
741 }
742 }
743
744 if( m_version >= V_COMPONENT_EXTRA )
745 {
746 for( size_t i = 0; i < m_componentSlots; i++ )
747 {
748 if( m_r.U8() )
749 m_r.Skip( 28 );
750 }
751
752 skipStrings( 223 );
753 }
754
755 if( m_version >= V_SETTINGS_F )
756 skipStrings( 2 );
757
758 if( m_version >= V_SAVE_TIME )
759 m_r.Skip( 8 );
760
761 if( m_version >= V_SETTINGS_G )
762 m_r.Skip( 40 + 40 );
763
764 if( m_version >= V_SETTINGS_H )
765 m_r.Skip( 2 );
766
767 if( m_version >= V_SETTINGS_I )
768 {
769 uint8_t language = m_r.U8();
770
771 if( language <= uint8_t( TEXT_LANGUAGE::FRENCH ) )
772 aSettings.textLanguage = TEXT_LANGUAGE( language );
773 }
774
775 if( m_version >= V_SETTINGS_J )
776 m_r.Skip( 32 );
777
778 if( m_version >= V_SETTINGS_K )
779 m_r.Skip( 1 );
780
781 if( m_version >= V_BLOBS )
782 {
783 size_t blobs = m_r.Count( 8 );
784
785 for( size_t i = 0; i < blobs; i++ )
786 {
787 m_r.LStr();
788 m_r.Skip( m_r.Count( 1 ) );
789 }
790 }
791
792 if( m_version >= V_SETTINGS_L )
793 skipStrings( 12 );
794
795 if( m_version >= V_CHECKSUM )
796 m_r.Skip( 8 );
797
798 if( m_version >= V_SETTINGS_M )
799 m_r.Skip( 16 + 16 );
800 }
801
802 READER m_r;
803 int32_t m_version = 0;
804 size_t m_componentSlots = 0;
805};
806
807} // namespace
808
809
810const std::string* ELEMENT_BASE::FindProp( const std::string& aKey ) const
811{
812 for( const auto& [key, value] : props )
813 {
814 if( key == aKey )
815 return &value;
816 }
817
818 return nullptr;
819}
820
821
822std::vector<uint8_t> ELEMENT_BASE::Picture() const
823{
824 std::vector<uint8_t> data;
825 const std::string* encoded = FindProp( "JPEG_PICTURE" );
826
827 if( !encoded )
828 return data;
829
830 // Base64 with '_' standing in for '+'
831 std::vector<uint8_t> text( encoded->begin(), encoded->end() );
832 std::replace( text.begin(), text.end(), uint8_t( '_' ), uint8_t( '+' ) );
833 base64::decode( text, data );
834 return data;
835}
836
837
838bool ELEMENT_BASE::FlagProp( const std::string& aKey ) const
839{
840 const std::string* value = FindProp( aKey );
841
842 if( !value )
843 return false;
844
845 wxString word = ToWx( *value ).Trim().Trim( false ).Upper();
846
847 return word == wxS( "TRUE" ) || word == wxS( "YES" ) || word == wxS( "1" ) || word == wxS( "JA" )
848 || word == wxS( "OUI" ) || word == wxS( "OK" );
849}
850
851
852std::optional<std::pair<int32_t, int32_t>> ELEMENT_BASE::TextRightEnd() const
853{
854 const std::string* arxy = FindProp( "ARXY" );
855
856 if( !arxy )
857 return std::nullopt;
858
859 wxString value( *arxy );
860 long endX = 0;
861 long endY = 0;
862
863 if( !value.Contains( wxS( "|" ) ) || !value.BeforeFirst( '|' ).ToLong( &endX )
864 || !value.AfterFirst( '|' ).ToLong( &endY ) )
865 {
866 return std::nullopt;
867 }
868
869 return std::make_pair( int32_t( endX ), int32_t( endY ) );
870}
871
872
873const std::string* PROJECT::FindProperty( const std::string& aKey ) const
874{
875 for( const auto& [key, value] : settings.properties )
876 {
877 if( ToWx( key ).Trim().Trim( false ).IsSameAs( ToWx( aKey ), false ) )
878 return &value;
879 }
880
881 return nullptr;
882}
883
884
885std::optional<wxString> PROJECT::FunctionText( const TEXT& aText, bool aSchematic ) const
886{
887 // The change timestamp is the later of the file time and the previous save time in property 1218
888 auto changed = [&]()
889 {
890 wxDateTime when = fileModified;
891 double days = 0.0;
892
893 if( const std::string* saved = FindProperty( "1218" ) )
894 {
895 wxString value = ToWx( *saved );
896 value.Replace( wxS( "," ), wxS( "." ) );
897
898 // TDateTime counts days from 1899-12-30, which is Julian day 2415018.5
899 if( value.ToCDouble( &days ) && days > 0.0 )
900 {
901 wxDateTime previous( days + 2415018.5 );
902
903 if( !when.IsValid() || previous.IsLaterThan( when ) )
904 when = previous;
905 }
906 }
907
908 return when;
909 };
910
911 switch( TEXT_FUNCTION( aText.function ) )
912 {
914 {
915 wxDateTime when = changed();
916 return when.IsValid() ? when.Format( wxS( "%d.%m.%Y" ) ) : wxString();
917 }
918
920 return fileModified.IsValid() ? fileModified.Format( wxS( "%H:%M" ) ) : wxString();
921
922 // Output stamps stay live; KiCad formats them as ISO date and HH:MM:SS
923 case TEXT_FUNCTION::OUTPUT_DATE: return wxString( wxS( "${CURRENT_DATE}" ) );
924 case TEXT_FUNCTION::OUTPUT_TIME: return wxString( wxS( "${CURRENT_TIME_HH_MM_SS}" ) );
925
926 // Vector output always shows the unscaled drawing; printing formats it with two decimals
927 case TEXT_FUNCTION::SCALE: return wxString( aSchematic ? wxS( "100.00%" ) : wxS( "100%" ) );
928
929 case TEXT_FUNCTION::PROJECT_NAME: return wxFileName( fileName ).GetName();
931
933 return aSchematic ? std::optional<wxString>( wxS( "${#}" ) ) : std::nullopt;
934
936 return aSchematic ? std::optional<wxString>( wxS( "${SHEETNAME}" ) ) : std::nullopt;
937
938 default: return std::nullopt;
939 }
940}
941
942
943const ELEMENT* PROJECT::FindSignal( uint16_t aId ) const
944{
945 if( aId == 0 )
946 return nullptr;
947
948 for( const ELEMENT& e : signals )
949 {
950 if( e.type == ELEMENT_TYPE::SIGNAL && e.signal && e.base.signal == aId )
951 return &e;
952 }
953
954 return nullptr;
955}
956
957
958namespace
959{
960
961struct LANGUAGE_INFO
962{
964 const char* key;
965};
966
967
968const LANGUAGE_INFO LANGUAGES[] = { { TEXT_LANGUAGE::GERMAN, "$%&DEUTSCH" },
969 { TEXT_LANGUAGE::ENGLISH, "$%&ENGLISH" },
970 { TEXT_LANGUAGE::FRENCH, "$%&FRANCAIS" } };
971
972
973template <typename LIST, typename FN>
974void forEachText( LIST& aList, FN&& aFn )
975{
976 for( auto& e : aList )
977 {
978 if( e.type == ELEMENT_TYPE::TEXT )
979 aFn( e );
980
981 forEachText( e.children, aFn );
982
983 if( e.symbol )
984 forEachText( e.symbol->children, aFn );
985
986 if( e.component )
987 {
988 forEachText( e.component->packageItems, aFn );
989 forEachText( e.component->symbols, aFn );
990 }
991
992 if( e.signal )
993 {
994 forEachText( e.signal->connections, aFn );
995 forEachText( e.signal->tracks, aFn );
996 }
997 }
998}
999
1000
1001template <typename PROJ, typename FN>
1002void forEachProjectText( PROJ& aProject, FN&& aFn )
1003{
1004 forEachText( aProject.signals, aFn );
1005 forEachText( aProject.components, aFn );
1006 forEachText( aProject.schematicItems, aFn );
1007 forEachText( aProject.boardItems, aFn );
1008}
1009
1010
1011// Variants only ever replace literal text; other functions are computed from their owner
1012std::optional<std::string> variant( const ELEMENT& aText, const LANGUAGE_INFO& aLanguage )
1013{
1014 if( aText.text.function != uint8_t( TEXT_FUNCTION::LITERAL ) )
1015 return std::nullopt;
1016
1017 const std::string* value = aText.base.FindProp( aLanguage.key );
1018
1019 if( !value || value->empty() )
1020 return std::nullopt;
1021
1022 // Spaces are stored as 0x7f inside variants
1023 std::string text = *value;
1024 std::replace( text.begin(), text.end(), '\x7f', ' ' );
1025 return text;
1026}
1027
1028} // namespace
1029
1030
1032{
1033 bool automatic = false;
1034
1035 forEachProjectText( *this,
1036 [&]( const ELEMENT& aText )
1037 {
1038 wxString text = ToWx( aText.text.text ).Trim().Trim( false ).Upper();
1039 automatic |= text == wxS( "AUTOLANGUAGE=TRUE" );
1040 } );
1041
1042 // Without the switch the source program shows stored text whatever language the file names
1043 if( !automatic || !settings.textLanguage )
1044 return;
1045
1046 for( const LANGUAGE_INFO& lang : LANGUAGES )
1047 {
1048 if( lang.language != *settings.textLanguage )
1049 continue;
1050
1051 forEachProjectText( *this,
1052 [&]( ELEMENT& aText )
1053 {
1054 if( std::optional<std::string> text = variant( aText, lang ) )
1055 aText.text.text = *text;
1056 } );
1057 }
1058}
1059
1060
1061wxString PadNumber( const ELEMENT& aPad )
1062{
1063 if( const std::string* name = aPad.base.FindProp( "PADNAME" ) )
1064 return ToWx( *name );
1065
1066 if( aPad.pad.number )
1067 return wxString::Format( wxS( "%u" ), aPad.pad.number );
1068
1069 return wxEmptyString;
1070}
1071
1072
1073PROJECT ParseBuffer( const std::vector<uint8_t>& aData )
1074{
1075 PARSER parser( aData );
1076 return parser.Parse();
1077}
1078
1079
1080PROJECT ParseFile( const wxString& aFileName )
1081{
1082 wxFFile file( aFileName, wxS( "rb" ) );
1083
1084 if( !file.IsOpened() )
1085 THROW_IO_ERROR( wxString::Format( _( "Cannot open file '%s'." ), aFileName ) );
1086
1087 std::vector<uint8_t> data( file.Length() );
1088
1089 if( !data.empty() && file.Read( data.data(), data.size() ) != data.size() )
1090 THROW_IO_ERROR( wxString::Format( _( "Cannot read file '%s'." ), aFileName ) );
1091
1092 PROJECT project = ParseBuffer( data );
1093 wxFileName fn( aFileName );
1094
1095 project.fileName = fn.GetFullName();
1096 project.fileModified = fn.GetModificationTime();
1097 return project;
1098}
1099
1100
1101bool LooksLikeProject( const wxString& aFileName )
1102{
1103 wxFileName fn( aFileName );
1104
1105 if( fn.GetExt().CmpNoCase( wxS( "T3001" ) ) != 0 )
1106 return false;
1107
1108 wxFFile file( aFileName, wxS( "rb" ) );
1109
1110 if( !file.IsOpened() )
1111 return false;
1112
1113 uint8_t head[8];
1114
1115 if( file.Read( head, sizeof( head ) ) != sizeof( head ) )
1116 return false;
1117
1118 int32_t version = int32_t( head[0] | ( head[1] << 8 ) | ( head[2] << 16 ) | ( uint32_t( head[3] ) << 24 ) );
1119 int32_t fontLen = int32_t( head[4] | ( head[5] << 8 ) | ( head[6] << 16 ) | ( uint32_t( head[7] ) << 24 ) );
1120
1121 return version > 0 && version < 0x10000 && fontLen >= 0 && fontLen < 256;
1122}
1123
1124
1125wxString ToWx( const std::string& aRaw )
1126{
1127 static wxCSConv conv( wxFONTENCODING_CP1252 );
1128 return wxString( aRaw.data(), conv, aRaw.size() );
1129}
1130
1131} // namespace TARGET3001
const char * name
#define _(s)
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
PARSE_RESULT Parse(const std::string &aString, NOTATION aNotation=NOTATION::SI, SIM_VALUE::TYPE aValueType=SIM_VALUE::TYPE_FLOAT)
Reader for TARGET 3001!
wxString PadNumber(const ELEMENT &aPad)
Number of a pad element in KiCad, its PADNAME when present, else its stored number.
PROJECT ParseBuffer(const std::vector< uint8_t > &aData)
Parse a project held in memory.
PROJECT ParseFile(const wxString &aFileName)
Parse a complete .T3001 file.
bool LooksLikeProject(const wxString &aFileName)
Cheap check used by the plugin managers: the extension matches and the header is plausible.
wxString ToWx(const std::string &aRaw)
Convert a stored ANSI string to a wxString.
ELEMENT_TYPE
Element class tags as stored in the file.
constexpr int BOARD_LAYER_COUNT
Board layers 0..99 carry a per-project function; 100 and above have fixed meanings.
void decode(const std::vector< uint8_t > &aInput, std::vector< uint8_t > &aOutput)
Definition base64.cpp:113
double radius
double centerY
double centerX
double width
std::optional< uint32_t > extra
uint16_t component
ID of the owning component (a component stores its own), 0 for none.
uint16_t signal
ID of the owning signal (a signal stores its own), 0 for none.
std::vector< std::pair< std::string, std::string > > props
Free-form key/value properties in file order.
uint8_t classTag
repeats the element's class tag
FIDUCIAL fiducial
ELEMENT_BASE base
DRC_MARKER marker
ELEMENT_TYPE type
std::shared_ptr< COMPONENT > component
std::shared_ptr< SYMBOL > symbol
RECTANGLE rectangle
TRIANGLE triangle
std::shared_ptr< SIGNAL > signal
std::shared_ptr< PACKAGE > package
std::vector< ELEMENT > children
Children of a pin (its number text).
uint8_t kind
FIDUCIAL_KIND.
int32_t width
int32_t clearance
int32_t controlX
int32_t controlY
std::vector< uint8_t > Picture() const
Encoded image a rectangle shows stretched over its area instead of its fill, empty for none.
bool FlagProp(const std::string &aKey) const
True when property aKey holds one of the format's boolean true words.
uint16_t signal
ID of the owning signal (a signal stores its own), 0 for none.
std::vector< std::pair< std::string, std::string > > props
Free-form key/value properties in file order.
std::optional< std::pair< int32_t, int32_t > > TextRightEnd() const
Right end of right-aligned text from its ARXY property, in file coordinates.
const std::string * FindProp(const std::string &aKey) const
Return the value of the first property named aKey, if any.
One element of any class.
void ApplyTextLanguage()
Replace literal texts by their variant in the project's stored language when a text reads AUTOLANGUAG...
const std::string * FindProperty(const std::string &aKey) const
Value of project property aKey, compared case-insensitively.
wxString fileName
source file name without directory, empty for a buffer
const ELEMENT * FindSignal(uint16_t aId) const
Signal element whose ID is aId, or nullptr.
std::vector< ELEMENT > signals
PROJECT_SETTINGS settings
wxDateTime fileModified
source file modification time, invalid for a buffer
std::optional< wxString > FunctionText(const TEXT &aText, bool aSchematic) const
Text that a computed text function shows, where KiCad has an equivalent.
wxString text
double height
bool mirrored
double width
int direction
0..3, multiplied by 90 degrees counterclockwise