29#include <wx/filename.h>
30#include <wx/strconv.h>
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;
63constexpr int MAX_DEPTH = 32;
66constexpr uint16_t LINKED_SIGNAL = 65503;
70void resolveLinkedSignals(
COMPONENT& aComponent, uint16_t aOwner )
72 std::map<uint16_t, uint16_t> padSignals;
74 for(
const ELEMENT& e : aComponent.packageItems )
80 auto resolve = [&](
ELEMENT& aElem )
82 if( aElem.base.signal != LINKED_SIGNAL )
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;
90 for(
ELEMENT& e : aComponent.packageItems )
93 for(
ELEMENT& sym : aComponent.symbols )
103const std::string PASSWORD_MARKER_A =
"$%&\xa7";
104const std::string PASSWORD_MARKER_B =
"$%&\xa7!";
110 explicit READER(
const std::vector<uint8_t>& aData ) :
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(); }
120 void Require(
size_t aBytes )
const
122 if( aBytes > Remaining() )
124 THROW_IO_ERROR( wxString::Format(
_(
"Unexpected end of TARGET 3001! file at offset %zu." ),
132 return m_data[m_pos++];
138 uint16_t v = m_data[m_pos] | ( m_data[m_pos + 1] << 8 );
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 );
152 int32_t S32() {
return static_cast<int32_t
>( U32() ); }
156 uint32_t bits = U32();
158 std::memcpy( &v, &bits,
sizeof( v ) );
162 void Skip(
size_t aBytes )
168 std::vector<uint8_t> Bytes(
size_t aBytes )
171 std::vector<uint8_t> out( m_data.begin() + m_pos, m_data.begin() + m_pos + aBytes );
177 size_t Count(
size_t aMinItemSize )
182 Fail(
"negative count" );
184 if( aMinItemSize &&
size_t( n ) > Remaining() / aMinItemSize )
185 Fail(
"count exceeds file size" );
192 size_t len = Count( 1 );
193 std::string s(
reinterpret_cast<const char*
>( m_data.data() + m_pos ), len );
198 [[noreturn]]
void Fail(
const char* aWhat )
const
200 THROW_IO_ERROR( wxString::Format(
_(
"Invalid TARGET 3001! file at offset %zu (%s)." ), m_pos,
205 const std::vector<uint8_t>& m_data;
213 explicit PARSER(
const std::vector<uint8_t>& aData ) :
222 p.version = m_r.S32();
223 m_version = p.version;
227 p.signals = parseTopList(
nullptr );
228 p.components = parseTopList( &m_componentSlots );
232 std::swap( p.signals, p.components );
234 appendFlattened( p.schematicItems, 0 );
235 appendFlattened( p.boardItems, 0 );
237 parseSettings( p.settings );
240 m_r.Fail(
"trailing data" );
242 p.ApplyTextLanguage();
248 void parseHeader(
PROJECT& aProject )
250 aProject.schematicTextFont = m_r.LStr();
252 if( aProject.schematicTextFont == PASSWORD_MARKER_A || aProject.schematicTextFont == PASSWORD_MARKER_B )
253 THROW_IO_ERROR(
_(
"Password-protected TARGET 3001! projects are not supported." ) );
255 aProject.boardTextFont = m_r.LStr();
256 aProject.signalNameFont = m_r.LStr();
264 std::copy( functions.begin(), functions.end(), aProject.layerFunctions.begin() );
265 std::copy( associations.begin(), associations.end(), aProject.layerAssociations.begin() );
268 uint8_t prefixCount = m_r.U8();
270 for(
int i = 0; i < prefixCount; i++ )
283 static bool holdsOnly(
const std::vector<ELEMENT>& aList,
ELEMENT_TYPE aType )
287 for(
const ELEMENT& e : aList )
292 if( e.
type != aType )
301 std::vector<ELEMENT> parseTopList(
size_t* aSlots )
304 m_r.Fail(
"element list expected" );
306 size_t n = m_r.Count( 1 );
307 std::vector<ELEMENT> out;
310 for(
size_t i = 0; i < n; i++ )
312 size_t before = out.size();
313 appendFlattened( out, 1 );
315 if( out.size() == before )
325 size_t parseListBody( std::vector<ELEMENT>& aOut,
int aDepth )
327 size_t n = m_r.Count( 1 );
328 aOut.reserve( aOut.size() + n );
330 for(
size_t i = 0; i < n; i++ )
331 appendFlattened( aOut, aDepth + 1 );
337 void appendFlattened( std::vector<ELEMENT>& aOut,
int aDepth )
339 if( aDepth > MAX_DEPTH )
340 m_r.Fail(
"element nesting too deep" );
342 uint8_t tag = m_r.U8();
350 parseListBody( aOut, aDepth );
355 size_t n = m_r.Count( 4 );
357 for(
size_t i = 0; i < n; i++ )
368 m_r.Fail(
"unknown element class" );
370 ELEMENT& e = aOut.emplace_back();
373 parseBody( e, aDepth );
376 std::vector<ELEMENT> parseChild(
int aDepth )
378 std::vector<ELEMENT> out;
379 appendFlattened( out, aDepth + 1 );
383 void parseBase( ELEMENT_BASE& aBase )
389 aBase.
flags = m_r.U8();
391 aBase.
layer = m_r.U8();
394 if( ( aBase.
flags & 0xfe ) == 2 )
395 aBase.
extra = m_r.U32();
397 size_t n = m_r.Count( 4 );
400 m_r.Fail(
"odd property count" );
402 aBase.
props.reserve( n / 2 );
404 for(
size_t i = 0; i < n; i += 2 )
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 ) );
412 void parseBody( ELEMENT& aElem,
int aDepth )
422 l.clearance = m_r.S32();
423 l.dashStyle = m_r.U8();
424 l.roundEnds = m_r.U8();
431 p.direction = m_r.F32();
432 p.length = m_r.S32();
434 p.electricalType = m_r.U8();
435 p.swapLevel = m_r.U16();
436 p.inverted = 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 );
448 p.direction = m_r.F32();
451 p.height = m_r.S32();
452 p.clearance = m_r.S32();
454 p.reserved = m_r.S32();
455 p.number = m_r.U16();
487 a.clearance = m_r.S32();
488 a.startAngle = m_r.F32();
489 a.endAngle = m_r.F32();
491 if( !std::isfinite( a.startAngle ) || !std::isfinite( a.endAngle ) )
492 m_r.Fail(
"arc angle is not a number" );
498 aElem.
disc.radius = m_r.S32();
509 t.noAutoUpright = m_r.U8();
511 t.function = m_r.U8();
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();
537 if( c->referenceNumber >= 65000 && c->referenceNumber < 65535 )
538 c->referenceExtension = m_r.U16();
540 c->connectionCount = m_r.U16();
541 c->gateState = m_r.U8();
542 c->packageItems = parseChild( aDepth );
543 c->symbols = parseChild( aDepth );
545 for( uint8_t& b : c->setBitmap )
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 );
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 );
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 );
609 aElem.
marker.radius = m_r.S32();
610 aElem.
marker.text = m_r.LStr();
614 m_r.Fail(
"unknown element class" );
618 void skipStrings(
size_t aCount )
620 for(
size_t i = 0; i < aCount; i++ )
624 void skipCountedStrings() { skipStrings( m_r.Count( 4 ) ); }
626 void parseSettings( PROJECT_SETTINGS& aSettings )
628 m_r.Skip( 16 + 16 + 52 );
634 size_t records = m_r.Count( 16 );
635 m_r.Skip( records * 16 );
637 skipCountedStrings();
641 uint16_t routeLayers = m_r.U16();
642 m_r.Skip(
size_t( routeLayers ) * 4 );
644 size_t propertyStrings = m_r.Count( 4 );
646 for(
size_t i = 0; i + 1 < propertyStrings; i += 2 )
648 std::string key = m_r.LStr();
649 aSettings.properties.emplace_back( std::move( key ), m_r.LStr() );
652 if( propertyStrings % 2 )
655 size_t padstacks = m_r.Count( 4 + 5 + 500 );
657 for(
size_t i = 0; i < padstacks; i++ )
659 PADSTACK_DEF& def = aSettings.padstacks.emplace_back();
660 def.name = m_r.LStr();
663 for(
bool& on : def.onLayer )
666 for( int32_t& grow : def.oversize )
667 grow = int32_t( m_r.U32() );
670 m_r.Skip(
size_t( routeLayers ) * 5 );
671 m_r.Skip( padstacks * 8 );
674 skipCountedStrings();
675 m_r.Skip( 100 + 13 + 208 );
683 aSettings.savedBy = m_r.LStr();
685 size_t records9 = m_r.Count( 4 + 9 );
687 for(
size_t i = 0; i < records9; i++ )
695 aSettings.sheetNames.reserve( 100 );
697 for(
int i = 0; i < 100; i++ )
698 aSettings.sheetNames.push_back( m_r.LStr() );
703 if( m_version >= V_SETTINGS_A )
710 if( m_version >= V_SETTINGS_B )
713 if( m_version >= V_TRACK_WIDTH )
716 if( m_version >= V_LOGBOOK )
719 if( m_version >= V_SETTINGS_C )
722 if( m_version >= V_SETTINGS_D )
725 if( m_version >= V_SETTINGS_E )
729 if( m_version >= V_SETTINGS_E && !m_r.AtEnd() )
731 for(
int i = 0; i < 12; i++ )
737 for(
int i = 0; i < 12; i++ )
744 if( m_version >= V_COMPONENT_EXTRA )
746 for(
size_t i = 0; i < m_componentSlots; i++ )
755 if( m_version >= V_SETTINGS_F )
758 if( m_version >= V_SAVE_TIME )
761 if( m_version >= V_SETTINGS_G )
764 if( m_version >= V_SETTINGS_H )
767 if( m_version >= V_SETTINGS_I )
775 if( m_version >= V_SETTINGS_J )
778 if( m_version >= V_SETTINGS_K )
781 if( m_version >= V_BLOBS )
783 size_t blobs = m_r.Count( 8 );
785 for(
size_t i = 0; i < blobs; i++ )
788 m_r.Skip( m_r.Count( 1 ) );
792 if( m_version >= V_SETTINGS_L )
795 if( m_version >= V_CHECKSUM )
798 if( m_version >= V_SETTINGS_M )
803 int32_t m_version = 0;
804 size_t m_componentSlots = 0;
812 for(
const auto& [key, value] :
props )
824 std::vector<uint8_t> data;
825 const std::string* encoded =
FindProp(
"JPEG_PICTURE" );
831 std::vector<uint8_t>
text( encoded->begin(), encoded->end() );
832 std::replace(
text.begin(),
text.end(), uint8_t(
'_' ), uint8_t(
'+' ) );
840 const std::string* value =
FindProp( aKey );
845 wxString word =
ToWx( *value ).Trim().Trim(
false ).Upper();
847 return word == wxS(
"TRUE" ) || word == wxS(
"YES" ) || word == wxS(
"1" ) || word == wxS(
"JA" )
848 || word == wxS(
"OUI" ) || word == wxS(
"OK" );
854 const std::string* arxy =
FindProp(
"ARXY" );
859 wxString value( *arxy );
863 if( !value.Contains( wxS(
"|" ) ) || !value.BeforeFirst(
'|' ).ToLong( &endX )
864 || !value.AfterFirst(
'|' ).ToLong( &endY ) )
869 return std::make_pair( int32_t( endX ), int32_t( endY ) );
875 for(
const auto& [key, value] :
settings.properties )
877 if(
ToWx( key ).Trim().Trim(
false ).IsSameAs(
ToWx( aKey ),
false ) )
895 wxString value =
ToWx( *saved );
896 value.Replace( wxS(
"," ), wxS(
"." ) );
899 if( value.ToCDouble( &days ) && days > 0.0 )
901 wxDateTime previous( days + 2415018.5 );
903 if( !when.IsValid() || previous.IsLaterThan( when ) )
915 wxDateTime when = changed();
916 return when.IsValid() ? when.Format( wxS(
"%d.%m.%Y" ) ) : wxString();
933 return aSchematic ? std::optional<wxString>( wxS(
"${#}" ) ) : std::nullopt;
936 return aSchematic ? std::optional<wxString>( wxS(
"${SHEETNAME}" ) ) : std::nullopt;
938 default:
return std::nullopt;
973template <
typename LIST,
typename FN>
974void forEachText(
LIST& aList, FN&& aFn )
976 for(
auto& e : aList )
984 forEachText( e.
symbol->children, aFn );
988 forEachText( e.
component->packageItems, aFn );
989 forEachText( e.
component->symbols, aFn );
994 forEachText( e.
signal->connections, aFn );
995 forEachText( e.
signal->tracks, aFn );
1001template <
typename PROJ,
typename FN>
1002void forEachProjectText( PROJ& aProject, FN&& aFn )
1004 forEachText( aProject.signals, aFn );
1005 forEachText( aProject.components, aFn );
1006 forEachText( aProject.schematicItems, aFn );
1007 forEachText( aProject.boardItems, aFn );
1012std::optional<std::string> variant(
const ELEMENT& aText,
const LANGUAGE_INFO& aLanguage )
1015 return std::nullopt;
1017 const std::string* value = aText.
base.
FindProp( aLanguage.key );
1019 if( !value || value->empty() )
1020 return std::nullopt;
1023 std::string
text = *value;
1024 std::replace(
text.begin(),
text.end(),
'\x7f',
' ' );
1033 bool automatic =
false;
1035 forEachProjectText( *
this,
1039 automatic |=
text == wxS(
"AUTOLANGUAGE=TRUE" );
1043 if( !automatic || !
settings.textLanguage )
1046 for(
const LANGUAGE_INFO& lang : LANGUAGES )
1048 if( lang.language != *
settings.textLanguage )
1051 forEachProjectText( *
this,
1054 if( std::optional<std::string>
text = variant( aText, lang ) )
1067 return wxString::Format( wxS(
"%u" ), aPad.
pad.
number );
1069 return wxEmptyString;
1075 PARSER parser( aData );
1076 return parser.Parse();
1082 wxFFile file( aFileName, wxS(
"rb" ) );
1084 if( !file.IsOpened() )
1085 THROW_IO_ERROR( wxString::Format(
_(
"Cannot open file '%s'." ), aFileName ) );
1087 std::vector<uint8_t> data( file.Length() );
1089 if( !data.empty() && file.Read( data.data(), data.size() ) != data.size() )
1090 THROW_IO_ERROR( wxString::Format(
_(
"Cannot read file '%s'." ), aFileName ) );
1093 wxFileName fn( aFileName );
1095 project.fileName = fn.GetFullName();
1096 project.fileModified = fn.GetModificationTime();
1103 wxFileName fn( aFileName );
1105 if( fn.GetExt().CmpNoCase( wxS(
"T3001" ) ) != 0 )
1108 wxFFile file( aFileName, wxS(
"rb" ) );
1110 if( !file.IsOpened() )
1115 if( file.Read( head,
sizeof( head ) ) !=
sizeof( head ) )
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 ) );
1121 return version > 0 && version < 0x10000 && fontLen >= 0 && fontLen < 256;
1125wxString
ToWx(
const std::string& aRaw )
1127 static wxCSConv conv( wxFONTENCODING_CP1252 );
1128 return wxString( aRaw.data(), conv, aRaw.size() );
#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)
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)
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
std::shared_ptr< COMPONENT > component
std::shared_ptr< SYMBOL > symbol
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.
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.
int direction
0..3, multiplied by 90 degrees counterclockwise