KiCad PCB EDA Suite
Loading...
Searching...
No Matches
altium_project_variants.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
21
22#include <boost/uuid/name_generator_sha1.hpp>
23#include <boost/uuid/string_generator.hpp>
24#include <boost/uuid/uuid_io.hpp>
25
26#include <wx/fileconf.h>
27#include <wx/log.h>
28
29
30KIID AltiumUniqueIdToKiid( const wxString& aUniqueId )
31{
32 // Fixed namespace so a given id always maps to the same KIID. Must never change or
33 // previously stamped footprint paths would stop matching.
34 static const boost::uuids::uuid s_namespace =
35 boost::uuids::string_generator()( "6f9619ff-8b86-d011-b42d-00cf4fc964ff" );
36
37 boost::uuids::name_generator_sha1 generator( s_namespace );
38 boost::uuids::uuid uuid = generator( aUniqueId.utf8_string() );
39
40 return KIID( wxString::FromUTF8( boost::uuids::to_string( uuid ) ) );
41}
42
43
44static ALTIUM_VARIANT_ENTRY ParseVariationString( const wxString& aValue )
45{
47
48 for( const wxString& token : wxSplit( aValue, '|' ) )
49 {
50 int eqPos = token.Find( '=' );
51
52 if( eqPos == wxNOT_FOUND )
53 continue;
54
55 wxString key = token.Left( eqPos ).Trim().Trim( false );
56 wxString val = token.Mid( eqPos + 1 ).Trim().Trim( false );
57
58 if( key.CmpNoCase( wxS( "Designator" ) ) == 0 )
59 {
60 entry.designator = val;
61 }
62 else if( key.CmpNoCase( wxS( "UniqueId" ) ) == 0 )
63 {
64 // The target is a backslash-delimited path; only the final segment is the
65 // component's own unique id, which is what symbol and footprint records store.
66 int sep = val.Find( '\\', true );
67
68 if( sep != wxNOT_FOUND )
69 entry.uniqueId = val.Mid( sep + 1 );
70 else
71 entry.uniqueId = val;
72 }
73 else if( key.CmpNoCase( wxS( "Kind" ) ) == 0 )
74 {
75 long k = 0;
76 val.ToLong( &k );
77 entry.kind = static_cast<int>( k );
78 }
79 else if( key.CmpNoCase( wxS( "AlternatePart" ) ) == 0 )
80 {
81 if( val.empty() )
82 continue;
83
84 // AlternatePart contains sub-fields in the same pipe-delimited format, but since
85 // it appears at the end of the line, the remaining tokens are its sub-fields.
86 // We've already split on '|', so we just keep accumulating from here.
87 // However, wxSplit already split everything. The AlternatePart sub-fields are the
88 // remaining tokens after this one. We handle this below after the loop.
89 }
90 }
91
92 // Parse AlternatePart sub-fields. In the raw line, AlternatePart= is followed by
93 // pipe-separated key=value pairs that are part of the alternate part definition.
94 // Since we already split on '|', find the AlternatePart token and treat everything
95 // after it as sub-fields.
96 bool inAlternatePart = false;
97
98 for( const wxString& token : wxSplit( aValue, '|' ) )
99 {
100 int eqPos = token.Find( '=' );
101
102 if( eqPos == wxNOT_FOUND )
103 continue;
104
105 wxString key = token.Left( eqPos ).Trim().Trim( false );
106 wxString val = token.Mid( eqPos + 1 ).Trim().Trim( false );
107
108 if( key.CmpNoCase( wxS( "AlternatePart" ) ) == 0 )
109 {
110 inAlternatePart = true;
111
112 // The value after AlternatePart= might itself be the first sub-field value
113 // In practice it's typically empty for Kind=1, or a lib reference for Kind=0
114 if( !val.empty() )
115 entry.alternateFields[wxS( "LibReference" )] = val;
116
117 continue;
118 }
119
120 if( inAlternatePart && !val.empty() )
121 entry.alternateFields[key] = val;
122 }
123
124 return entry;
125}
126
127
128std::map<wxString, wxString> ParseAltiumProjectParameters( const wxString& aPrjPcbPath )
129{
130 std::map<wxString, wxString> parameters;
131
132 wxFileConfig config( wxEmptyString, wxEmptyString, wxEmptyString, aPrjPcbPath,
133 wxCONFIG_USE_NO_ESCAPE_CHARACTERS );
134
135 wxString groupname;
136 long groupid;
137
138 for( bool more = config.GetFirstGroup( groupname, groupid ); more;
139 more = config.GetNextGroup( groupname, groupid ) )
140 {
141 if( !groupname.StartsWith( wxS( "Parameter" ) ) )
142 continue;
143
144 // Only numbered [ParameterN] sections hold the project parameters; reject look-alikes
145 // such as [ParameterEngine] by requiring a trailing integer.
146 wxString numStr = groupname.Mid( 9 );
147 long num;
148
149 if( !numStr.ToLong( &num ) )
150 continue;
151
152 wxString name = config.Read( groupname + wxS( "/Name" ), wxEmptyString );
153
154 if( name.empty() )
155 continue;
156
157 wxString value = config.Read( groupname + wxS( "/Value" ), wxEmptyString );
158
159 // KiCad variable references are matched case-insensitively by resolving to upper case,
160 // which is what AltiumPcbSpecialStringsToKiCadStrings emits, so key on the upper-cased
161 // name to keep ${PCB_REVISION} and friends resolvable.
162 name.UpperCase();
163
164 parameters[name] = value;
165 }
166
167 return parameters;
168}
169
170
171std::vector<ALTIUM_PROJECT_VARIANT> ParseAltiumProjectVariants( const wxString& aPrjPcbPath )
172{
173 std::vector<ALTIUM_PROJECT_VARIANT> variants;
174
175 wxFileConfig config( wxEmptyString, wxEmptyString, wxEmptyString, aPrjPcbPath,
176 wxCONFIG_USE_NO_ESCAPE_CHARACTERS );
177
178 wxString groupname;
179 long groupid;
180
181 for( bool more = config.GetFirstGroup( groupname, groupid ); more;
182 more = config.GetNextGroup( groupname, groupid ) )
183 {
184 if( !groupname.StartsWith( wxS( "ProjectVariant" ) ) )
185 continue;
186
187 wxString numStr = groupname.Mid( 14 );
188 long num;
189
190 if( !numStr.ToLong( &num ) )
191 continue;
192
194
195 pv.description = config.Read( groupname + wxS( "/Description" ), wxEmptyString );
196 pv.name = pv.description;
197
198 if( pv.name.empty() )
199 pv.name = groupname;
200
201 long variationCount = 0;
202 config.Read( groupname + wxS( "/VariationCount" ), &variationCount, 0 );
203
204 for( long vi = 1; vi <= variationCount; ++vi )
205 {
206 wxString key = wxString::Format( wxS( "%s/Variation%ld" ), groupname, vi );
207 wxString value = config.Read( key, wxEmptyString );
208
209 if( value.empty() )
210 continue;
211
213
214 if( !entry.designator.empty() )
215 pv.variations.push_back( std::move( entry ) );
216 }
217
218 if( !pv.variations.empty() )
219 variants.push_back( std::move( pv ) );
220 }
221
222 return variants;
223}
const char * name
std::vector< ALTIUM_PROJECT_VARIANT > ParseAltiumProjectVariants(const wxString &aPrjPcbPath)
Parse all [ProjectVariantN] sections from an Altium .PrjPcb project file.
std::map< wxString, wxString > ParseAltiumProjectParameters(const wxString &aPrjPcbPath)
Parse all [ParameterN] sections from an Altium .PrjPcb project file.
KIID AltiumUniqueIdToKiid(const wxString &aUniqueId)
Derive a stable KIID from an Altium component unique id.
static ALTIUM_VARIANT_ENTRY ParseVariationString(const wxString &aValue)
Definition kiid.h:44
A project-level assembly variant parsed from an Altium .PrjPcb file.
std::vector< ALTIUM_VARIANT_ENTRY > variations
A single component variation within an Altium project variant.
int kind
wxString uniqueId
wxString designator
std::map< wxString, wxString > alternateFields