KiCad PCB EDA Suite
Loading...
Searching...
No Matches
altium_parser_pcb.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) 2020 Thomas Pointhuber <[email protected]>
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, you may find one here:
19 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20 * or you may search the http://www.gnu.org website for the version 2 license,
21 * or you may write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24
25#include <map>
26#include <unordered_map>
27
28#include <ki_exception.h>
29#include <math/util.h>
30
31#include <wx/log.h>
32#include <wx/translation.h>
33
34#include "altium_parser_pcb.h"
37
38
46static const wxChar* traceAltiumImport = wxT( "KICAD_ALTIUM_IMPORT" );
47
48
49/*
50 * Returns an Altium layer id from V6 and V7 file format data, compatible with the rest of the parser.
51 */
53{
55 return aV7Layer;
56
57 return aV6Layer;
58}
59
60
61bool altiumScopeExprMatchesPolygon( const wxString& aExpr )
62{
63 // INPOLY/ISPOLY are prefixes of INPOLYGON/ISPOLYGON, so two checks cover all four tokens.
64 wxString upper = aExpr.Upper();
65 return upper.Contains( wxT( "INPOLY" ) ) || upper.Contains( wxT( "ISPOLY" ) );
66}
67
68
69const ARULE6* selectAltiumPolygonRule( const std::vector<ARULE6>& aRulesByPriorityAsc )
70{
71 for( const ARULE6& rule : aRulesByPriorityAsc )
72 {
73 if( altiumScopeExprMatchesPolygon( rule.scope1expr )
74 || altiumScopeExprMatchesPolygon( rule.scope2expr ) )
75 {
76 return &rule;
77 }
78 }
79
80 return nullptr;
81}
82
83
84/*
85 * Returns V7 layer ids for Mechanical 17 and above. Otherwise, V6 layer ids.
86 */
87ALTIUM_LAYER altium_layer_from_name( const wxString& aName )
88{
89 if( aName.IsEmpty() )
91
92 static const std::unordered_map<std::string, ALTIUM_LAYER> hash_map = {
93 { "TOP", ALTIUM_LAYER::TOP_LAYER },
94 { "MID1", ALTIUM_LAYER::MID_LAYER_1 },
95 { "MID2", ALTIUM_LAYER::MID_LAYER_2 },
96 { "MID3", ALTIUM_LAYER::MID_LAYER_3 },
97 { "MID4", ALTIUM_LAYER::MID_LAYER_4 },
98 { "MID5", ALTIUM_LAYER::MID_LAYER_5 },
99 { "MID6", ALTIUM_LAYER::MID_LAYER_6 },
100 { "MID7", ALTIUM_LAYER::MID_LAYER_7 },
101 { "MID8", ALTIUM_LAYER::MID_LAYER_8 },
102 { "MID9", ALTIUM_LAYER::MID_LAYER_9 },
103 { "MID10", ALTIUM_LAYER::MID_LAYER_10 },
104 { "MID11", ALTIUM_LAYER::MID_LAYER_11 },
105 { "MID12", ALTIUM_LAYER::MID_LAYER_12 },
106 { "MID13", ALTIUM_LAYER::MID_LAYER_13 },
107 { "MID14", ALTIUM_LAYER::MID_LAYER_14 },
108 { "MID15", ALTIUM_LAYER::MID_LAYER_15 },
109 { "MID16", ALTIUM_LAYER::MID_LAYER_16 },
110 { "MID17", ALTIUM_LAYER::MID_LAYER_17 },
111 { "MID18", ALTIUM_LAYER::MID_LAYER_18 },
112 { "MID19", ALTIUM_LAYER::MID_LAYER_19 },
113 { "MID20", ALTIUM_LAYER::MID_LAYER_20 },
114 { "MID21", ALTIUM_LAYER::MID_LAYER_21 },
115 { "MID22", ALTIUM_LAYER::MID_LAYER_22 },
116 { "MID23", ALTIUM_LAYER::MID_LAYER_23 },
117 { "MID24", ALTIUM_LAYER::MID_LAYER_24 },
118 { "MID25", ALTIUM_LAYER::MID_LAYER_25 },
119 { "MID26", ALTIUM_LAYER::MID_LAYER_26 },
120 { "MID27", ALTIUM_LAYER::MID_LAYER_27 },
121 { "MID28", ALTIUM_LAYER::MID_LAYER_28 },
122 { "MID29", ALTIUM_LAYER::MID_LAYER_29 },
123 { "MID30", ALTIUM_LAYER::MID_LAYER_30 },
124 { "BOTTOM", ALTIUM_LAYER::BOTTOM_LAYER },
125
126 { "TOPOVERLAY", ALTIUM_LAYER::TOP_OVERLAY },
127 { "BOTTOMOVERLAY", ALTIUM_LAYER::BOTTOM_OVERLAY },
128 { "TOPPASTE", ALTIUM_LAYER::TOP_PASTE },
129 { "BOTTOMPASTE", ALTIUM_LAYER::BOTTOM_PASTE },
130 { "TOPSOLDER", ALTIUM_LAYER::TOP_SOLDER },
131 { "BOTTOMSOLDER", ALTIUM_LAYER::BOTTOM_SOLDER },
132
133 { "PLANE1", ALTIUM_LAYER::INTERNAL_PLANE_1 },
134 { "PLANE2", ALTIUM_LAYER::INTERNAL_PLANE_2 },
135 { "PLANE3", ALTIUM_LAYER::INTERNAL_PLANE_3 },
136 { "PLANE4", ALTIUM_LAYER::INTERNAL_PLANE_4 },
137 { "PLANE5", ALTIUM_LAYER::INTERNAL_PLANE_5 },
138 { "PLANE6", ALTIUM_LAYER::INTERNAL_PLANE_6 },
139 { "PLANE7", ALTIUM_LAYER::INTERNAL_PLANE_7 },
140 { "PLANE8", ALTIUM_LAYER::INTERNAL_PLANE_8 },
141 { "PLANE9", ALTIUM_LAYER::INTERNAL_PLANE_9 },
142 { "PLANE10", ALTIUM_LAYER::INTERNAL_PLANE_10 },
143 { "PLANE11", ALTIUM_LAYER::INTERNAL_PLANE_11 },
144 { "PLANE12", ALTIUM_LAYER::INTERNAL_PLANE_12 },
145 { "PLANE13", ALTIUM_LAYER::INTERNAL_PLANE_13 },
146 { "PLANE14", ALTIUM_LAYER::INTERNAL_PLANE_14 },
147 { "PLANE15", ALTIUM_LAYER::INTERNAL_PLANE_15 },
148 { "PLANE16", ALTIUM_LAYER::INTERNAL_PLANE_16 },
149
150 { "DRILLGUIDE", ALTIUM_LAYER::DRILL_GUIDE },
151 { "KEEPOUT", ALTIUM_LAYER::KEEP_OUT_LAYER },
152
153 { "MECHANICAL1", ALTIUM_LAYER::MECHANICAL_1 },
154 { "MECHANICAL2", ALTIUM_LAYER::MECHANICAL_2 },
155 { "MECHANICAL3", ALTIUM_LAYER::MECHANICAL_3 },
156 { "MECHANICAL4", ALTIUM_LAYER::MECHANICAL_4 },
157 { "MECHANICAL5", ALTIUM_LAYER::MECHANICAL_5 },
158 { "MECHANICAL6", ALTIUM_LAYER::MECHANICAL_6 },
159 { "MECHANICAL7", ALTIUM_LAYER::MECHANICAL_7 },
160 { "MECHANICAL8", ALTIUM_LAYER::MECHANICAL_8 },
161 { "MECHANICAL9", ALTIUM_LAYER::MECHANICAL_9 },
162 { "MECHANICAL10", ALTIUM_LAYER::MECHANICAL_10 },
163 { "MECHANICAL11", ALTIUM_LAYER::MECHANICAL_11 },
164 { "MECHANICAL12", ALTIUM_LAYER::MECHANICAL_12 },
165 { "MECHANICAL13", ALTIUM_LAYER::MECHANICAL_13 },
166 { "MECHANICAL14", ALTIUM_LAYER::MECHANICAL_14 },
167 { "MECHANICAL15", ALTIUM_LAYER::MECHANICAL_15 },
168 { "MECHANICAL16", ALTIUM_LAYER::MECHANICAL_16 },
169
170 { "DRILLDRAWING", ALTIUM_LAYER::DRILL_DRAWING },
171 { "MULTILAYER", ALTIUM_LAYER::MULTI_LAYER },
172
173 // FIXME: the following mapping is just a guess
174 { "CONNECTIONS", ALTIUM_LAYER::CONNECTIONS },
175 { "BACKGROUND", ALTIUM_LAYER::BACKGROUND },
176 { "DRCERRORMARKERS", ALTIUM_LAYER::DRC_ERROR_MARKERS },
177 { "SELECTIONS", ALTIUM_LAYER::SELECTIONS },
178 { "VISIBLEGRID1", ALTIUM_LAYER::VISIBLE_GRID_1 },
179 { "VISIBLEGRID2", ALTIUM_LAYER::VISIBLE_GRID_2 },
180 { "PADHOLES", ALTIUM_LAYER::PAD_HOLES },
181 { "VIAHOLES", ALTIUM_LAYER::VIA_HOLES },
182 };
183
184 auto it = hash_map.find( std::string( aName.c_str() ) );
185
186 if( it != hash_map.end() )
187 return it->second;
188
189 // Try V7 format mechanical layers
190 const wxString mechanicalStr( "MECHANICAL" );
191
192 if( aName.StartsWith( mechanicalStr ) )
193 {
194 unsigned long val = 0;
195
196 if( aName.Mid( mechanicalStr.length() ).ToULong( &val ) )
197 return static_cast<ALTIUM_LAYER>( static_cast<int>( ALTIUM_LAYER::V7_MECHANICAL_BASE ) + val );
198 }
199
200 wxLogError( _( "Unknown mapping of the Altium layer '%s'." ), aName );
202}
203
204
206{
207 static const std::unordered_map<std::string, ALTIUM_MECHKIND> hash_map = {
208 { "AssemblyTop", ALTIUM_MECHKIND::ASSEMBLY_TOP },
209 { "AssemblyBottom", ALTIUM_MECHKIND::ASSEMBLY_BOT },
210
211 { "AssemblyNotes", ALTIUM_MECHKIND::ASSEMBLY_NOTES },
212 { "Board", ALTIUM_MECHKIND::BOARD },
213
214 { "CoatingTop", ALTIUM_MECHKIND::COATING_TOP },
215 { "CoatingBottom", ALTIUM_MECHKIND::COATING_BOT },
216
217 { "ComponentCenterTop", ALTIUM_MECHKIND::COMPONENT_CENTER_TOP },
218 { "ComponentCenterBottom", ALTIUM_MECHKIND::COMPONENT_CENTER_BOT },
219
220 { "ComponentOutlineTop", ALTIUM_MECHKIND::COMPONENT_OUTLINE_TOP },
221 { "ComponentOutlineBottom", ALTIUM_MECHKIND::COMPONENT_OUTLINE_BOT },
222
223 { "CourtyardTop", ALTIUM_MECHKIND::COURTYARD_TOP },
224 { "CourtyardBottom", ALTIUM_MECHKIND::COURTYARD_BOT },
225
226 { "DesignatorTop", ALTIUM_MECHKIND::DESIGNATOR_TOP },
227 { "DesignatorBottom", ALTIUM_MECHKIND::DESIGNATOR_BOT },
228
229 { "Dimensions", ALTIUM_MECHKIND::DIMENSIONS },
230 { "DimensionsTop", ALTIUM_MECHKIND::DIMENSIONS_TOP },
231 { "DimensionsBottom", ALTIUM_MECHKIND::DIMENSIONS_BOT },
232
233 { "FabNotes", ALTIUM_MECHKIND::FAB_NOTES },
234
235 { "GluePointsTop", ALTIUM_MECHKIND::GLUE_POINTS_TOP },
236 { "GluePointsBottom", ALTIUM_MECHKIND::GLUE_POINTS_BOT },
237
238 { "GoldPlatingTop", ALTIUM_MECHKIND::GOLD_PLATING_TOP },
239 { "GoldPlatingBottom", ALTIUM_MECHKIND::GOLD_PLATING_BOT },
240
241 { "ValueTop", ALTIUM_MECHKIND::VALUE_TOP },
242 { "ValueBottom", ALTIUM_MECHKIND::VALUE_BOT },
243
244 { "VCut", ALTIUM_MECHKIND::V_CUT },
245
246 { "3DBodyTop", ALTIUM_MECHKIND::BODY_3D_TOP },
247 { "3DBodyBottom", ALTIUM_MECHKIND::BODY_3D_BOT },
248
249 { "RouteToolPath", ALTIUM_MECHKIND::ROUTE_TOOL_PATH },
250 { "Sheet", ALTIUM_MECHKIND::SHEET },
251 { "BoardShape", ALTIUM_MECHKIND::BOARD_SHAPE },
252 };
253
254 auto it = hash_map.find( std::string( aName.c_str() ) );
255
256 if( it != hash_map.end() )
257 {
258 return it->second;
259 }
260 else
261 {
262 wxLogError( _( "Unknown mapping of the Altium layer kind '%s'." ), aName );
264 }
265}
266
267
268void altium_parse_polygons( std::map<wxString, wxString>& aProps,
269 std::vector<ALTIUM_VERTICE>& aVertices )
270{
271 for( size_t i = 0; i < std::numeric_limits<size_t>::max(); i++ )
272 {
273 const wxString si = std::to_string( i );
274
275 const wxString vxi = wxT( "VX" ) + si;
276 const wxString vyi = wxT( "VY" ) + si;
277
278 if( aProps.find( vxi ) == aProps.end() || aProps.find( vyi ) == aProps.end() )
279 break; // it doesn't seem like we know beforehand how many vertices are inside a polygon
280
281 const bool isRound = ALTIUM_PROPS_UTILS::ReadInt( aProps, wxT( "KIND" ) + si, 0 ) != 0;
282 const int32_t radius = ALTIUM_PROPS_UTILS::ReadKicadUnit( aProps, wxT( "R" ) + si, wxT( "0mil" ) );
283 const double sa = ALTIUM_PROPS_UTILS::ReadDouble( aProps, wxT( "SA" ) + si, 0. );
284 const double ea = ALTIUM_PROPS_UTILS::ReadDouble( aProps, wxT( "EA" ) + si, 0. );
285 const VECTOR2I vp = VECTOR2I( ALTIUM_PROPS_UTILS::ReadKicadUnit( aProps, vxi, wxT( "0mil" ) ),
286 -ALTIUM_PROPS_UTILS::ReadKicadUnit( aProps, vyi, wxT( "0mil" ) ) );
287 const VECTOR2I cp = VECTOR2I( ALTIUM_PROPS_UTILS::ReadKicadUnit( aProps, wxT( "CX" ) + si, wxT( "0mil" ) ),
288 -ALTIUM_PROPS_UTILS::ReadKicadUnit( aProps, wxT( "CY" ) + si, wxT( "0mil" ) ) );
289
290 aVertices.emplace_back( isRound, radius, sa, ea, vp, cp );
291 }
292}
293
294
295static ALTIUM_MODE ReadAltiumModeFromProperties( const std::map<wxString, wxString>& aProps,
296 wxString aKey )
297{
298 wxString mode = ALTIUM_PROPS_UTILS::ReadString( aProps, aKey, wxT( "" ) );
299
300 if( mode == wxT( "None" ) )
301 return ALTIUM_MODE::NONE;
302 else if( mode == wxT( "Rule" ) )
303 return ALTIUM_MODE::RULE;
304 else if( mode == wxT( "Manual" ) )
305 return ALTIUM_MODE::MANUAL;
306
307 wxLogError( _( "Unknown Mode string: '%s'." ), mode );
309}
310
311
312static ALTIUM_RECORD ReadAltiumRecordFromProperties( const std::map<wxString, wxString>& aProps,
313 wxString aKey )
314{
315 wxString record = ALTIUM_PROPS_UTILS::ReadString( aProps, aKey, wxT( "" ) );
316
317 if( record == wxT( "Arc" ) )
318 return ALTIUM_RECORD::ARC;
319 else if( record == wxT( "Pad" ) )
320 return ALTIUM_RECORD::PAD;
321 else if( record == wxT( "Via" ) )
322 return ALTIUM_RECORD::VIA;
323 else if( record == wxT( "Track" ) )
325 else if( record == wxT( "Text" ) )
326 return ALTIUM_RECORD::TEXT;
327 else if( record == wxT( "Fill" ) )
328 return ALTIUM_RECORD::FILL;
329 else if( record == wxT( "Region" ) ) // correct?
331 else if( record == wxT( "Model" ) )
333
334 wxLogError( _( "Unknown Record name string: '%s'." ), record );
336}
337
338
341 const std::map<wxString, wxString>& aProps, wxString aKey )
342{
343 wxString parsedType = ALTIUM_PROPS_UTILS::ReadString( aProps, aKey, wxT( "" ) );
344
345 if( parsedType == wxT( "Mask" ) )
347
348 wxLogError( _( "Unknown Extended Primitive Information type: '%s'." ), parsedType );
350}
351
352
361static void ExpectSubrecordLengthAtLeast( const std::string& aStreamType,
362 const std::string& aSubrecordName, size_t aExpectedLength,
363 size_t aActualLength )
364{
365 if( aActualLength < aExpectedLength )
366 {
367 THROW_IO_ERROR( wxString::Format( "%s stream %s has length %d, "
368 "which is unexpected (expected at least %d)",
369 aStreamType, aSubrecordName, aActualLength,
370 aExpectedLength ) );
371 }
372}
373
374
376{
377 const std::map<wxString, wxString> props = aReader.ReadProperties();
378
379 if( props.empty() )
380 THROW_IO_ERROR( wxT( "ExtendedPrimitiveInformation stream has no properties!" ) );
381
382 primitiveIndex = ALTIUM_PROPS_UTILS::ReadInt( props, wxT( "PRIMITIVEINDEX" ), -1 );
383 primitiveObjectId = ReadAltiumRecordFromProperties( props, wxT( "PRIMITIVEOBJECTID" ) );
385
386 pastemaskexpansionmode = ReadAltiumModeFromProperties( props, wxT( "PASTEMASKEXPANSIONMODE" ) );
388 props, wxT( "PASTEMASKEXPANSION_MANUAL" ), wxT( "0mil" ) );
390 ReadAltiumModeFromProperties( props, wxT( "SOLDERMASKEXPANSIONMODE" ) );
392 props, wxT( "SOLDERMASKEXPANSION_MANUAL" ), wxT( "0mil" ) );
393}
394
395
396ABOARD6_LAYER_STACKUP::ABOARD6_LAYER_STACKUP( const std::map<wxString, wxString>& aProps, const wxString& aPrefix,
397 uint32_t aLayerIdFallback )
398{
399 // LAYERID is specific to V7 format
400 layerId = ALTIUM_PROPS_UTILS::ReadInt( aProps, aPrefix + wxT( "LAYERID" ), aLayerIdFallback );
401
402 name = ALTIUM_PROPS_UTILS::ReadString( aProps, aPrefix + wxT( "NAME" ), wxT( "" ) );
403 nextId = ALTIUM_PROPS_UTILS::ReadInt( aProps, aPrefix + wxT( "NEXT" ), 0 );
404 prevId = ALTIUM_PROPS_UTILS::ReadInt( aProps, aPrefix + wxT( "PREV" ), 0 );
405 copperthick = ALTIUM_PROPS_UTILS::ReadKicadUnit( aProps, aPrefix + wxT( "COPTHICK" ), wxT( "1.4mil" ) );
406
407 dielectricconst = ALTIUM_PROPS_UTILS::ReadDouble( aProps, aPrefix + wxT( "DIELCONST" ), 0. );
408 dielectricthick = ALTIUM_PROPS_UTILS::ReadKicadUnit( aProps, aPrefix + wxT( "DIELHEIGHT" ), wxT( "60mil" ) );
409 dielectricmaterial = ALTIUM_PROPS_UTILS::ReadString( aProps, aPrefix + wxT( "DIELMATERIAL" ), wxT( "FR-4" ) );
410
411 // TODO: In some component libraries MECHENABLED may be FALSE but the layers show up as used.
412 // (we should check if any objects exists on these layers?)
413 wxString mechEnabled = ALTIUM_PROPS_UTILS::ReadString( aProps, aPrefix + wxT( "MECHENABLED" ), wxT( "" ) );
414
415 mechenabled = !mechEnabled.Contains( wxS( "FALSE" ) );
416
417 if( mechenabled )
418 {
419 wxString mechKind = ALTIUM_PROPS_UTILS::ReadString( aProps, aPrefix + wxT( "MECHKIND" ), wxT( "" ) );
420
421 if( !mechKind.IsEmpty() )
423 }
424}
425
426
427static std::vector<ABOARD6_LAYER_STACKUP> ReadAltiumStackupFromProperties( const std::map<wxString, wxString>& aProps )
428{
429 std::vector<ABOARD6_LAYER_STACKUP> stackup;
430
431 for( size_t i = 1; i < std::numeric_limits<size_t>::max(); i++ )
432 {
433 const wxString layeri = wxString( wxT( "LAYER" ) ) << std::to_string( i );
434 const wxString layername = layeri + wxT( "NAME" );
435
436 auto layernameit = aProps.find( layername );
437
438 if( layernameit == aProps.end() )
439 break;
440
441 ABOARD6_LAYER_STACKUP l( aProps, layeri, i );
442 stackup.push_back( l );
443 }
444
445 // V7 format layers
446 for( size_t i = 0; i < std::numeric_limits<size_t>::max(); i++ )
447 {
448 const wxString layeri = wxString( wxT( "LAYERV7_" ) ) << std::to_string( i );
449 const wxString layername = layeri + wxT( "NAME" );
450
451 auto layernameit = aProps.find( layername );
452
453 if( layernameit == aProps.end() )
454 break;
455
456 ABOARD6_LAYER_STACKUP l( aProps, layeri, 0 );
457 stackup.push_back( l );
458 }
459
460 return stackup;
461}
462
463
465{
466 std::map<wxString, wxString> props = aReader.ReadProperties();
467
468 if( props.empty() )
469 THROW_IO_ERROR( wxT( "Library stream has no properties!" ) );
470
471 layercount = ALTIUM_PROPS_UTILS::ReadInt( props, wxT( "LAYERSETSCOUNT" ), 1 ) + 1;
472
474
476 {
477 wxString originalName = l.name;
478
479 // Ensure that layer names are unique in KiCad
480 for( int ii = 2; !layerNames.insert( l.name ).second; ii++ )
481 l.name = wxString::Format( wxT( "%s %d" ), originalName, ii );
482 }
483
484 if( aReader.HasParsingError() )
485 THROW_IO_ERROR( wxT( "Library stream was not parsed correctly!" ) );
486}
487
488
490{
491 std::map<wxString, wxString> props = aReader.ReadProperties();
492
493 if( props.empty() )
494 THROW_IO_ERROR( wxT( "Board6 stream has no properties!" ) );
495
496 sheetpos = VECTOR2I( ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "SHEETX" ), wxT( "0mil" ) ),
497 -ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "SHEETY" ), wxT( "0mil" ) ) );
498 sheetsize = wxSize( ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "SHEETWIDTH" ), wxT( "0mil" ) ),
499 ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "SHEETHEIGHT" ), wxT( "0mil" ) ) );
500
501 layercount = ALTIUM_PROPS_UTILS::ReadInt( props, wxT( "LAYERSETSCOUNT" ), 1 ) + 1;
502
504
506 {
507 wxString originalName = l.name;
508
509 // Ensure that layer names are unique in KiCad
510 for( int ii = 2; !layerNames.insert( l.name ).second; ii++ )
511 l.name = wxString::Format( wxT( "%s %d" ), originalName, ii );
512 }
513
515
516 if( aReader.HasParsingError() )
517 THROW_IO_ERROR( wxT( "Board6 stream was not parsed correctly!" ) );
518}
519
521{
522 std::map<wxString, wxString> properties = aReader.ReadProperties();
523
524 if( properties.empty() )
525 THROW_IO_ERROR( wxT( "Classes6 stream has no properties!" ) );
526
527 name = ALTIUM_PROPS_UTILS::ReadString( properties, wxT( "NAME" ), wxT( "" ) );
528 uniqueid = ALTIUM_PROPS_UTILS::ReadString( properties, wxT( "UNIQUEID" ), wxT( "" ) );
529 kind = static_cast<ALTIUM_CLASS_KIND>( ALTIUM_PROPS_UTILS::ReadInt( properties, wxT( "KIND" ), -1 ) );
530
531 for( size_t i = 0; i < std::numeric_limits<size_t>::max(); i++ )
532 {
533 auto mit = properties.find( wxT( "M" ) + wxString( std::to_string( i ) ) );
534
535 if( mit == properties.end() )
536 break; // it doesn't seem like we know beforehand how many components are in the netclass
537
538 names.push_back( mit->second );
539 }
540
541 if( aReader.HasParsingError() )
542 THROW_IO_ERROR( wxT( "Classes6 stream was not parsed correctly" ) );
543}
544
546{
547 std::map<wxString, wxString> props = aReader.ReadProperties();
548
549 if( props.empty() )
550 THROW_IO_ERROR( wxT( "Components6 stream has no props" ) );
551
552 layer = altium_layer_from_name( ALTIUM_PROPS_UTILS::ReadString( props, wxT( "LAYER" ), wxT( "" ) ) );
553 position = VECTOR2I( ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "X" ), wxT( "0mil" ) ),
554 -ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "Y" ), wxT( "0mil" ) ) );
555 rotation = ALTIUM_PROPS_UTILS::ReadDouble( props, wxT( "ROTATION" ), 0. );
556 locked = ALTIUM_PROPS_UTILS::ReadBool( props, wxT( "LOCKED" ), false );
557 nameon = ALTIUM_PROPS_UTILS::ReadBool( props, wxT( "NAMEON" ), true );
558 commenton = ALTIUM_PROPS_UTILS::ReadBool( props, wxT( "COMMENTON" ), false );
559 sourcedesignator = ALTIUM_PROPS_UTILS::ReadString( props, wxT( "SOURCEDESIGNATOR" ), wxT( "" ) );
560
561 sourceUniqueID = ALTIUM_PROPS_UTILS::ReadString( props, wxT( "SOURCEUNIQUEID" ), wxT( "" ) );
562
563 // Remove leading backslash from sourceUniqueID to match schematic component unique IDs
564 if( sourceUniqueID.starts_with( wxT( "\\" ) ) )
566
567 sourceHierachicalPath = ALTIUM_PROPS_UTILS::ReadString( props, wxT( "SOURCEHIERARCHICALPATH" ), wxT( "" ) );
569 ALTIUM_PROPS_UTILS::ReadUnicodeString( props, wxT( "SOURCEFOOTPRINTLIBRARY" ), wxT( "" ) );
570 pattern = ALTIUM_PROPS_UTILS::ReadUnicodeString( props, wxT( "PATTERN" ), wxT( "" ) );
571
572 sourcecomponentlibrary = ALTIUM_PROPS_UTILS::ReadString( props, wxT( "SOURCECOMPONENTLIBRARY" ), wxT( "" ) );
573 sourcelibreference = ALTIUM_PROPS_UTILS::ReadString( props, wxT( "SOURCELIBREFERENCE" ), wxT( "" ) );
574
576 ALTIUM_PROPS_UTILS::ReadInt( props, wxT( "NAMEAUTOPOSITION" ), 0 ) );
578 ALTIUM_PROPS_UTILS::ReadInt( props, wxT( "COMMENTAUTOPOSITION" ), 0 ) );
579
580 if( aReader.HasParsingError() )
581 THROW_IO_ERROR( wxT( "Components6 stream was not parsed correctly" ) );
582}
583
585{
586 aReader.Skip( 2 );
587
588 std::map<wxString, wxString> props = aReader.ReadProperties();
589
590 if( props.empty() )
591 THROW_IO_ERROR( wxT( "Dimensions6 stream has no props" ) );
592
593 layer_v6 = altium_layer_from_name( ALTIUM_PROPS_UTILS::ReadString( props, wxT( "LAYER" ), wxT( "" ) ) );
594 layer_v7 = altium_layer_from_name( ALTIUM_PROPS_UTILS::ReadString( props, wxT( "LAYER_V7" ), wxT( "" ) ) );
595 kind = static_cast<ALTIUM_DIMENSION_KIND>( ALTIUM_PROPS_UTILS::ReadInt( props, wxT( "DIMENSIONKIND" ), 0 ) );
596
597 textformat = ALTIUM_PROPS_UTILS::ReadString( props, wxT( "TEXTFORMAT" ), wxT( "" ) );
598 textprefix = ALTIUM_PROPS_UTILS::ReadString( props, wxT( "TEXTPREFIX" ), wxT( "" ) );
599 textsuffix = ALTIUM_PROPS_UTILS::ReadString( props, wxT( "TEXTSUFFIX" ), wxT( "" ) );
600
601 height = ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "HEIGHT" ), wxT( "0mil" ) );
602 angle = ALTIUM_PROPS_UTILS::ReadDouble( props, wxT( "ANGLE" ), 0. );
603
604 linewidth = ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "LINEWIDTH" ), wxT( "10mil" ) );
605 textheight = ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "TEXTHEIGHT" ), wxT( "10mil" ) );
606 textlinewidth = ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "TEXTLINEWIDTH" ), wxT( "6mil" ) );
607 textprecision = ALTIUM_PROPS_UTILS::ReadInt( props, wxT( "TEXTPRECISION" ), 2 );
608 textbold = ALTIUM_PROPS_UTILS::ReadBool( props, wxT( "TEXTLINEWIDTH" ), false );
609 textitalic = ALTIUM_PROPS_UTILS::ReadBool( props, wxT( "ITALIC" ), false );
610 textgap = ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "TEXTGAP" ), wxT( "10mil" ) );
611
612 arrowsize = ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "ARROWSIZE" ), wxT( "60mil" ) );
613
614 wxString text_position_raw = ALTIUM_PROPS_UTILS::ReadString( props, wxT( "TEXTPOSITION" ), wxT( "" ) );
615
616 xy1 = VECTOR2I( ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "X1" ), wxT( "0mil" ) ),
617 -ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "Y1" ), wxT( "0mil" ) ) );
618
619 int refcount = ALTIUM_PROPS_UTILS::ReadInt( props, wxT( "REFERENCES_COUNT" ), 0 );
620
621 for( int i = 0; i < refcount; i++ )
622 {
623 const std::string refi = "REFERENCE" + std::to_string( i ) + "POINT";
624 const wxString ref( refi );
625 referencePoint.emplace_back( ALTIUM_PROPS_UTILS::ReadKicadUnit( props, ref + wxT( "X" ), wxT( "0mil" ) ),
626 -ALTIUM_PROPS_UTILS::ReadKicadUnit( props, ref + wxT( "Y" ), wxT( "0mil" ) ) );
627 }
628
629 for( size_t i = 1; i < std::numeric_limits<size_t>::max(); i++ )
630 {
631 const std::string texti = "TEXT" + std::to_string( i );
632 const std::string textix = texti + "X";
633 const std::string textiy = texti + "Y";
634
635 if( props.find( textix ) == props.end() || props.find( textiy ) == props.end() )
636 break; // it doesn't seem like we know beforehand how many vertices are inside a polygon
637
638 textPoint.emplace_back( ALTIUM_PROPS_UTILS::ReadKicadUnit( props, textix, wxT( "0mil" ) ),
639 -ALTIUM_PROPS_UTILS::ReadKicadUnit( props, textiy, wxT( "0mil" ) ) );
640 }
641
642 wxString dimensionunit = ALTIUM_PROPS_UTILS::ReadString( props, wxT( "TEXTDIMENSIONUNIT" ), wxT( "Millimeters" ) );
643
644 if( dimensionunit == wxT( "Inches" ) ) textunit = ALTIUM_UNIT::INCH;
645 else if( dimensionunit == wxT( "Mils" ) ) textunit = ALTIUM_UNIT::MILS;
646 else if( dimensionunit == wxT( "Millimeters" ) ) textunit = ALTIUM_UNIT::MM;
647 else if( dimensionunit == wxT( "Centimeters" ) ) textunit = ALTIUM_UNIT::CM;
649
651
652 if( aReader.HasParsingError() )
653 THROW_IO_ERROR( wxT( "Dimensions6 stream was not parsed correctly" ) );
654}
655
657{
658 std::map<wxString, wxString> properties = aReader.ReadProperties();
659
660 if( properties.empty() )
661 THROW_IO_ERROR( wxT( "Model stream has no properties!" ) );
662
663 name = ALTIUM_PROPS_UTILS::ReadString( properties, wxT( "NAME" ), wxT( "" ) );
664 id = ALTIUM_PROPS_UTILS::ReadString( properties, wxT( "ID" ), wxT( "" ) );
665 isEmbedded = ALTIUM_PROPS_UTILS::ReadBool( properties, wxT( "EMBED" ), false );
666
667 rotation.x = ALTIUM_PROPS_UTILS::ReadDouble( properties, wxT( "ROTX" ), 0. );
668 rotation.y = ALTIUM_PROPS_UTILS::ReadDouble( properties, wxT( "ROTY" ), 0. );
669 rotation.z = ALTIUM_PROPS_UTILS::ReadDouble( properties, wxT( "ROTZ" ), 0. );
670
671 z_offset = ALTIUM_PROPS_UTILS::ReadDouble( properties, wxT( "DZ" ), 0. );
672 checksum = ALTIUM_PROPS_UTILS::ReadInt( properties, wxT( "CHECKSUM" ), 0 );
673
674 if( aReader.HasParsingError() )
675 THROW_IO_ERROR( wxT( "Model stream was not parsed correctly" ) );
676}
677
679{
680 std::map<wxString, wxString> properties = aReader.ReadProperties();
681
682 if( properties.empty() )
683 THROW_IO_ERROR( wxT( "Nets6 stream has no properties" ) );
684
685 name = ALTIUM_PROPS_UTILS::ReadString( properties, wxT( "NAME" ), wxT( "" ) );
686
687 if( aReader.HasParsingError() )
688 THROW_IO_ERROR( wxT( "Nets6 stream was not parsed correctly" ) );
689}
690
692{
693 std::map<wxString, wxString> properties = aReader.ReadProperties();
694
695 if( properties.empty() )
696 THROW_IO_ERROR( wxT( "Polygons6 stream has no properties" ) );
697
698 layer_v6 = altium_layer_from_name( ALTIUM_PROPS_UTILS::ReadString( properties, wxT( "LAYER" ), wxT( "" ) ) );
699 layer_v7 = altium_layer_from_name( ALTIUM_PROPS_UTILS::ReadString( properties, wxT( "LAYER_V7" ), wxT( "" ) ) );
700 net = ALTIUM_PROPS_UTILS::ReadInt( properties, wxT( "NET" ), ALTIUM_NET_UNCONNECTED );
701 locked = ALTIUM_PROPS_UTILS::ReadBool( properties, wxT( "LOCKED" ), false );
702
703 // TODO: kind
704
705 gridsize = ALTIUM_PROPS_UTILS::ReadKicadUnit( properties, wxT( "GRIDSIZE" ), wxT( "0mil" ) );
706 trackwidth = ALTIUM_PROPS_UTILS::ReadKicadUnit( properties, wxT( "TRACKWIDTH" ), wxT( "0mil" ) );
707 minprimlength = ALTIUM_PROPS_UTILS::ReadKicadUnit( properties, wxT( "MINPRIMLENGTH" ), wxT( "0mil" ) );
708 useoctagons = ALTIUM_PROPS_UTILS::ReadBool( properties, wxT( "USEOCTAGONS" ), false );
709
710 pourindex = ALTIUM_PROPS_UTILS::ReadInt( properties, wxT( "POURINDEX" ), 0 );
711
712 wxString hatchstyleraw = ALTIUM_PROPS_UTILS::ReadString( properties, wxT( "HATCHSTYLE" ), wxT( "" ) );
713
714 if( hatchstyleraw == wxT( "Solid" ) ) hatchstyle = ALTIUM_POLYGON_HATCHSTYLE::SOLID;
715 else if( hatchstyleraw == wxT( "45Degree" ) ) hatchstyle = ALTIUM_POLYGON_HATCHSTYLE::DEGREE_45;
716 else if( hatchstyleraw == wxT( "90Degree" ) ) hatchstyle = ALTIUM_POLYGON_HATCHSTYLE::DEGREE_90;
717 else if( hatchstyleraw == wxT( "Horizontal" ) ) hatchstyle = ALTIUM_POLYGON_HATCHSTYLE::HORIZONTAL;
718 else if( hatchstyleraw == wxT( "Vertical" ) ) hatchstyle = ALTIUM_POLYGON_HATCHSTYLE::VERTICAL;
719 else if( hatchstyleraw == wxT( "None" ) ) hatchstyle = ALTIUM_POLYGON_HATCHSTYLE::NONE;
721
722 altium_parse_polygons( properties, vertices );
723
725
726 if( aReader.HasParsingError() )
727 THROW_IO_ERROR( wxT( "Polygons6 stream was not parsed correctly" ) );
728}
729
731{
732 aReader.Skip( 2 );
733
734 std::map<wxString, wxString> props = aReader.ReadProperties();
735
736 if( props.empty() )
737 THROW_IO_ERROR( wxT( "Rules6 stream has no props" ) );
738
739 name = ALTIUM_PROPS_UTILS::ReadString( props, wxT( "NAME" ), wxT( "" ) );
740 priority = ALTIUM_PROPS_UTILS::ReadInt( props, wxT( "PRIORITY" ), 1 );
741
742 scope1expr = ALTIUM_PROPS_UTILS::ReadString( props, wxT( "SCOPE1EXPRESSION" ), wxT( "" ) );
743 scope2expr = ALTIUM_PROPS_UTILS::ReadString( props, wxT( "SCOPE2EXPRESSION" ), wxT( "" ) );
744
745 wxString rulekind = ALTIUM_PROPS_UTILS::ReadString( props, wxT( "RULEKIND" ), wxT( "" ) );
746 if( rulekind == wxT( "Clearance" ) )
747 {
749 clearanceGap = ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "GAP" ), wxT( "10mil" ) );
750 }
751 else if( rulekind == wxT( "DiffPairsRouting" ) )
752 {
754 }
755 else if( rulekind == wxT( "Height" ) )
756 {
758 }
759 else if( rulekind == wxT( "HoleSize" ) )
760 {
762 minLimit = ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "MINLIMIT" ), wxT( "1mil" ) );
763 maxLimit = ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "MAXLIMIT" ), wxT( "150mil" ) );
764 }
765 else if( rulekind == wxT( "HoleToHoleClearance" ) )
766 {
768 clearanceGap = ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "GAP" ), wxT( "10mil" ) );
769 }
770 else if( rulekind == wxT( "RoutingVias" ) )
771 {
773 width = ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "WIDTH" ), wxT( "20mil" ) );
774 minWidth = ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "MINWIDTH" ), wxT( "20mil" ) );
775 maxWidth = ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "MAXWIDTH" ), wxT( "50mil" ) );
776 holeWidth = ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "HOLEWIDTH" ), wxT( "10mil" ) );
777 minHoleWidth = ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "MINHOLEWIDTH" ), wxT( "10mil" ) );
778 maxHoleWidth = ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "MAXHOLEWIDTH" ), wxT( "28mil" ) );
779 }
780 else if( rulekind == wxT( "Width" ) )
781 {
783 minLimit = ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "MINLIMIT" ), wxT( "6mil" ) );
784 maxLimit = ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "MAXLIMIT" ), wxT( "40mil" ) );
785 preferredWidth = ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "PREFERREDWIDTH" ), wxT( "6mil" ) );
786}
787 else if( rulekind == wxT( "PasteMaskExpansion" ) )
788 {
790 pastemaskExpansion = ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "EXPANSION" ), wxT( "0" ) );
791 }
792 else if( rulekind == wxT( "SolderMaskExpansion" ) )
793 {
795 soldermaskExpansion = ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "EXPANSION" ), wxT( "4mil" ) );
796 }
797 else if( rulekind == wxT( "PlaneClearance" ) )
798 {
800 planeclearanceClearance = ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "CLEARANCE" ), wxT( "10mil" ) );
801 }
802 else if( rulekind == wxT( "PolygonConnect" ) )
803 {
805 polygonconnectAirgapwidth = ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "AIRGAPWIDTH" ), wxT( "10mil" ) );
806 polygonconnectReliefconductorwidth = ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "RELIEFCONDUCTORWIDTH" ), wxT( "10mil" ) );
807 polygonconnectReliefentries = ALTIUM_PROPS_UTILS::ReadInt( props, wxT( "RELIEFENTRIES" ), 4 );
808
809 wxString style = ALTIUM_PROPS_UTILS::ReadString( props, wxT( "CONNECTSTYLE" ), wxT( "" ) );
810
811 if( style == wxT( "Direct" ) ) polygonconnectStyle = ALTIUM_CONNECT_STYLE::DIRECT;
812 else if( style == wxT( "Relief" ) ) polygonconnectStyle = ALTIUM_CONNECT_STYLE::RELIEF;
813 else if( style == wxT( "NoConnect" ) ) polygonconnectStyle = ALTIUM_CONNECT_STYLE::NONE;
815 }
816 else
817 {
819 }
820
821 if( aReader.HasParsingError() )
822 THROW_IO_ERROR( wxT( "Rules6 stream was not parsed correctly" ) );
823}
824
826{
827 ALTIUM_RECORD recordtype = static_cast<ALTIUM_RECORD>( aReader.Read<uint8_t>() );
828 if( recordtype != ALTIUM_RECORD::ARC )
829 {
830 THROW_IO_ERROR( wxT( "Arcs6 stream has invalid recordtype" ) );
831 }
832
833 // Subrecord 1
835
836 layer_v6 = static_cast<ALTIUM_LAYER>( aReader.Read<uint8_t>() );
837
838 uint8_t flags1 = aReader.Read<uint8_t>();
839 is_locked = ( flags1 & 0x04 ) == 0;
840 is_polygonoutline = ( flags1 & 0x02 ) != 0;
841
842 uint8_t flags2 = aReader.Read<uint8_t>();
843 is_keepout = flags2 == 2;
844
845 net = aReader.Read<uint16_t>();
846 polygon = aReader.Read<uint16_t>();
847 component = aReader.Read<uint16_t>();
848 aReader.Skip( 4 );
849 center = aReader.ReadVector2IPos();
850 radius = aReader.ReadKicadUnit();
851 startangle = aReader.Read<double>();
852 endangle = aReader.Read<double>();
853 width = aReader.ReadKicadUnit();
854 subpolyindex = aReader.Read<uint16_t>();
855
856 int remaining = aReader.GetRemainingSubrecordBytes();
857
858 if( remaining >= 9 )
859 {
860 aReader.Skip( 5 );
861 layer_v7 = static_cast<ALTIUM_LAYER>( aReader.Read<uint32_t>() );
862 }
863
864 if( remaining >= 10 )
865 keepoutrestrictions = aReader.Read<uint8_t>();
866 else
867 keepoutrestrictions = is_keepout ? 0x1F : 0;
868
870
871 aReader.SkipSubrecord();
872
873 if( aReader.HasParsingError() )
874 {
875 THROW_IO_ERROR( wxT( "Arcs6 stream was not parsed correctly" ) );
876 }
877}
878
880{
881 ALTIUM_RECORD recordtype = static_cast<ALTIUM_RECORD>( aReader.Read<uint8_t>() );
882
883 if( recordtype != ALTIUM_RECORD::MODEL )
884 THROW_IO_ERROR( wxT( "ComponentsBodies6 stream has invalid recordtype" ) );
885
887
888 aReader.Skip( 7 );
889 component = aReader.Read<uint16_t>();
890 aReader.Skip( 9 );
891
892 std::map<wxString, wxString> properties = aReader.ReadProperties();
893
894 if( properties.empty() )
895 THROW_IO_ERROR( wxT( "ComponentsBodies6 stream has no properties" ) );
896
897 modelName = ALTIUM_PROPS_UTILS::ReadString( properties, wxT( "MODEL.NAME" ), wxT( "" ) );
898 modelId = ALTIUM_PROPS_UTILS::ReadString( properties, wxT( "MODELID" ), wxT( "" ) );
899 modelIsEmbedded = ALTIUM_PROPS_UTILS::ReadBool( properties, wxT( "MODEL.EMBED" ), false );
900
901 modelPosition.x = ALTIUM_PROPS_UTILS::ReadKicadUnit( properties, wxT( "MODEL.2D.X" ), wxT( "0mil" ) );
902 modelPosition.y = -ALTIUM_PROPS_UTILS::ReadKicadUnit( properties, wxT( "MODEL.2D.Y" ), wxT( "0mil" ) );
903 modelPosition.z = ALTIUM_PROPS_UTILS::ReadKicadUnit( properties, wxT( "MODEL.3D.DZ" ), wxT( "0mil" ) );
904
905 modelRotation.x = ALTIUM_PROPS_UTILS::ReadDouble( properties, wxT( "MODEL.3D.ROTX" ), 0. );
906 modelRotation.y = ALTIUM_PROPS_UTILS::ReadDouble( properties, wxT( "MODEL.3D.ROTY" ), 0. );
907 modelRotation.z = ALTIUM_PROPS_UTILS::ReadDouble( properties, wxT( "MODEL.3D.ROTZ" ), 0. );
908
909 rotation = ALTIUM_PROPS_UTILS::ReadDouble( properties, wxT( "MODEL.2D.ROTATION" ), 0. );
910
911 body_opacity_3d = ALTIUM_PROPS_UTILS::ReadDouble( properties, wxT( "BODYOPACITY3D" ), 1. );
912 body_projection = ALTIUM_PROPS_UTILS::ReadInt( properties, wxT( "BODYPROJECTION" ), 0 );
913
914 aReader.SkipSubrecord();
915
916 if( aReader.HasParsingError() )
917 THROW_IO_ERROR( wxT( "Components6 stream was not parsed correctly" ) );
918}
919
921{
922 ALTIUM_RECORD recordtype = static_cast<ALTIUM_RECORD>( aReader.Read<uint8_t>() );
923
924 if( recordtype != ALTIUM_RECORD::PAD )
925 THROW_IO_ERROR( wxT( "Pads6 stream has invalid recordtype" ) );
926
927 // Subrecord 1
928 size_t subrecord1 = aReader.ReadAndSetSubrecordLength();
929
930 if( subrecord1 == 0 )
931 THROW_IO_ERROR( wxT( "Pads6 stream has no subrecord1 data" ) );
932
933 name = aReader.ReadWxString();
934
935 if( aReader.GetRemainingSubrecordBytes() != 0 )
936 THROW_IO_ERROR( wxT( "Pads6 stream has invalid subrecord1 length" ) );
937
938 aReader.SkipSubrecord();
939
940 // Subrecord 2
942 aReader.SkipSubrecord();
943
944 // Subrecord 3
946 aReader.SkipSubrecord();
947
948 // Subrecord 4
950 aReader.SkipSubrecord();
951
952 // Subrecord 5
953 size_t subrecord5 = aReader.ReadAndSetSubrecordLength();
954
955 ExpectSubrecordLengthAtLeast( "Pads6", "subrecord5", 110, subrecord5 );
956
957 layer_v6 = static_cast<ALTIUM_LAYER>( aReader.Read<uint8_t>() );
960
961 uint8_t flags1 = aReader.Read<uint8_t>();
962 is_test_fab_top = ( flags1 & 0x80 ) != 0;
963 is_tent_bottom = ( flags1 & 0x40 ) != 0;
964 is_tent_top = ( flags1 & 0x20 ) != 0;
965 is_locked = ( flags1 & 0x04 ) == 0;
966
967 uint8_t flags2 = aReader.Read<uint8_t>();
968 is_test_fab_bottom = ( flags2 & 0x01 ) != 0;
969
970 net = aReader.Read<uint16_t>();
971 aReader.Skip( 2 );
972 component = aReader.Read<uint16_t>();
973 aReader.Skip( 4 ); // to 13
974
975 position = aReader.ReadVector2IPos();
976 topsize = aReader.ReadVector2ISize();
977 midsize = aReader.ReadVector2ISize();
978 botsize = aReader.ReadVector2ISize();
979 holesize = aReader.ReadKicadUnit(); // to 49
980
981 topshape = static_cast<ALTIUM_PAD_SHAPE>( aReader.Read<uint8_t>() );
982 midshape = static_cast<ALTIUM_PAD_SHAPE>( aReader.Read<uint8_t>() );
983 botshape = static_cast<ALTIUM_PAD_SHAPE>( aReader.Read<uint8_t>() );
984
985 direction = aReader.Read<double>();
986 plated = aReader.Read<uint8_t>() != 0;
987 aReader.Skip( 1 );
988 padmode = static_cast<ALTIUM_PAD_MODE>( aReader.Read<uint8_t>() );
989 aReader.Skip( 23 );
992 aReader.Skip( 7 );
993 pastemaskexpansionmode = static_cast<ALTIUM_MODE>( aReader.Read<uint8_t>() );
994 soldermaskexpansionmode = static_cast<ALTIUM_MODE>( aReader.Read<uint8_t>() );
995 aReader.Skip( 3 ); // to 106
996
999
1000 if( subrecord5 == 110 )
1001 {
1002 // Don't know exactly what this is, but it's always been 0 in the files with 110-byte subrecord5.
1003 // e.g. https://gitlab.com/kicad/code/kicad/-/issues/16514
1004 const uint32_t unknown = aReader.ReadKicadUnit(); // to 110
1005
1006 if( unknown != 0 )
1007 {
1008 THROW_IO_ERROR( wxString::Format( "Pads6 stream subrecord5 + 106 has value %d, which is unexpected",
1009 unknown ) );
1010 }
1011 holerotation = 0;
1012 }
1013 else
1014 {
1015 // More than 110, need at least 114
1016 ExpectSubrecordLengthAtLeast( "Pads6", "subrecord5", 114, subrecord5 );
1017 holerotation = aReader.Read<double>(); // to 114
1018 }
1019
1020 if( subrecord5 >= 120 )
1021 {
1022 tolayer = static_cast<ALTIUM_LAYER>( aReader.Read<uint8_t>() );
1023 aReader.Skip( 2 );
1024 fromlayer = static_cast<ALTIUM_LAYER>( aReader.Read<uint8_t>() );
1025 //aReader.skip( 2 );
1026 }
1027 else if( subrecord5 == 171 )
1028 {
1029 }
1030
1031 if( subrecord5 >= 202 )
1032 {
1033 aReader.Skip( 40 );
1034 pad_to_die_length = aReader.ReadKicadUnit();
1035 aReader.Skip( 32 );
1036 pad_to_die_delay = KiROUND( aReader.Read<double>() * 1e18 );
1037 }
1038
1039 aReader.SkipSubrecord();
1040
1041 // Subrecord 6
1042 size_t subrecord6 = aReader.ReadAndSetSubrecordLength();
1043 // Known lengths: 596, 628, 651
1044 // 596 is the number of bytes read in this code-block
1045 if( subrecord6 >= 596 )
1046 {
1047 sizeAndShape = std::make_unique<APAD6_SIZE_AND_SHAPE>();
1048
1049 for( wxSize& size : sizeAndShape->inner_size )
1050 size.x = aReader.ReadKicadUnitX();
1051
1052 for( wxSize& size : sizeAndShape->inner_size )
1053 size.y = aReader.ReadKicadUnitY();
1054
1055 for( ALTIUM_PAD_SHAPE& shape : sizeAndShape->inner_shape )
1056 shape = static_cast<ALTIUM_PAD_SHAPE>( aReader.Read<uint8_t>() );
1057
1058 aReader.Skip( 1 );
1059
1060 sizeAndShape->holeshape = static_cast<ALTIUM_PAD_HOLE_SHAPE>( aReader.Read<uint8_t>() );
1061 sizeAndShape->slotsize = aReader.ReadKicadUnit();
1062 sizeAndShape->slotrotation = aReader.Read<double>();
1063
1064 for( VECTOR2I& pt : sizeAndShape->holeoffset )
1065 pt.x = aReader.ReadKicadUnitX();
1066
1067 for( VECTOR2I& pt : sizeAndShape->holeoffset )
1068 pt.y = aReader.ReadKicadUnitY();
1069
1070 aReader.Skip( 1 );
1071
1072 for( ALTIUM_PAD_SHAPE_ALT& shape : sizeAndShape->alt_shape )
1073 shape = static_cast<ALTIUM_PAD_SHAPE_ALT>( aReader.Read<uint8_t>() );
1074
1075 for( uint8_t& radius : sizeAndShape->cornerradius )
1076 radius = aReader.Read<uint8_t>();
1077 }
1078 else if( subrecord6 != 0 )
1079 {
1080 wxLogError( _( "Pads6 stream has unexpected length for subrecord 6: %d." ), subrecord6 );
1081 }
1082
1084
1085 aReader.SkipSubrecord();
1086
1087 if( aReader.HasParsingError() )
1088 THROW_IO_ERROR( wxT( "Pads6 stream was not parsed correctly" ) );
1089}
1090
1092{
1093 ALTIUM_RECORD recordtype = static_cast<ALTIUM_RECORD>( aReader.Read<uint8_t>() );
1094
1095 if( recordtype != ALTIUM_RECORD::VIA )
1096 THROW_IO_ERROR( wxT( "Vias6 stream has invalid recordtype" ) );
1097
1098 // Subrecord 1
1099 size_t subrecord1 = aReader.ReadAndSetSubrecordLength();
1100
1101 aReader.Skip( 1 );
1102 uint8_t flags1 = aReader.Read<uint8_t>();
1103 is_test_fab_top = ( flags1 & 0x80 ) != 0;
1104 is_tent_bottom = ( flags1 & 0x40 ) != 0;
1105 is_tent_top = ( flags1 & 0x20 ) != 0;
1106 is_locked = ( flags1 & 0x04 ) == 0;
1107
1108 uint8_t flags2 = aReader.Read<uint8_t>();
1109 is_test_fab_bottom = ( flags2 & 0x01 ) != 0;
1110
1111 net = aReader.Read<uint16_t>();
1112 aReader.Skip( 8 );
1113 position = aReader.ReadVector2IPos();
1114 diameter = aReader.ReadKicadUnit();
1115 holesize = aReader.ReadKicadUnit();
1116
1117 layer_start = static_cast<ALTIUM_LAYER>( aReader.Read<uint8_t>() );
1118 layer_end = static_cast<ALTIUM_LAYER>( aReader.Read<uint8_t>() );
1119
1120 if( subrecord1 <= 74 )
1121 {
1123 }
1124 else
1125 {
1126 uint8_t temp_byte = aReader.Read<uint8_t>(); // Unknown.
1127
1129 thermal_relief_conductorcount = aReader.Read<uint8_t>();
1130 aReader.Skip( 1 ); // Unknown.
1131
1133
1134 aReader.ReadKicadUnit(); // Unknown. 20mil?
1135 aReader.ReadKicadUnit(); // Unknown. 20mil?
1136
1137 aReader.Skip( 4 );
1139 aReader.Skip( 8 );
1140
1141 temp_byte = aReader.Read<uint8_t>();
1142 soldermask_expansion_manual = temp_byte & 0x02;
1143
1144 aReader.Skip( 7 );
1145
1146 viamode = static_cast<ALTIUM_PAD_MODE>( aReader.Read<uint8_t>() );
1147
1148 for( int ii = 0; ii < 32; ++ii )
1149 {
1150 diameter_by_layer[ii] = aReader.ReadKicadUnit();
1151 }
1152 }
1153
1154 if( subrecord1 >= 246 )
1155 {
1156 aReader.Skip( 38 );
1157 soldermask_expansion_linked = aReader.Read<uint8_t>() & 0x01;
1159 }
1160
1161 if( subrecord1 >= 307 )
1162 {
1163 aReader.Skip( 45 );
1164
1165 pos_tolerance = aReader.ReadKicadUnit();
1166 neg_tolerance = aReader.ReadKicadUnit();
1167 }
1168
1169 aReader.SkipSubrecord();
1170
1171 if( aReader.HasParsingError() )
1172 THROW_IO_ERROR( wxT( "Vias6 stream was not parsed correctly" ) );
1173}
1174
1176{
1177 ALTIUM_RECORD recordtype = static_cast<ALTIUM_RECORD>( aReader.Read<uint8_t>() );
1178
1179 if( recordtype != ALTIUM_RECORD::TRACK )
1180 THROW_IO_ERROR( wxT( "Tracks6 stream has invalid recordtype" ) );
1181
1182 // Subrecord 1
1183 aReader.ReadAndSetSubrecordLength();
1184
1185 layer_v6 = static_cast<ALTIUM_LAYER>( aReader.Read<uint8_t>() );
1186
1187 uint8_t flags1 = aReader.Read<uint8_t>();
1188 is_locked = ( flags1 & 0x04 ) == 0;
1189 is_polygonoutline = ( flags1 & 0x02 ) != 0;
1190
1191 uint8_t flags2 = aReader.Read<uint8_t>();
1192 is_keepout = flags2 == 2;
1193
1194 net = aReader.Read<uint16_t>();
1195 polygon = aReader.Read<uint16_t>();
1196 component = aReader.Read<uint16_t>();
1197 aReader.Skip( 4 );
1198 start = aReader.ReadVector2IPos();
1199 end = aReader.ReadVector2IPos();
1200 width = aReader.ReadKicadUnit();
1201 subpolyindex = aReader.Read<uint16_t>();
1202 aReader.Skip( 1 );
1203
1204 int remaining = aReader.GetRemainingSubrecordBytes();
1205
1206 if( remaining >= 9 )
1207 {
1208 aReader.Skip( 5 );
1209 layer_v7 = static_cast<ALTIUM_LAYER>( aReader.Read<uint32_t>() );
1210 }
1211
1212 if( remaining >= 10 )
1213 keepoutrestrictions = aReader.Read<uint8_t>();
1214 else
1215 keepoutrestrictions = is_keepout ? 0x1F : 0;
1216
1218
1219 aReader.SkipSubrecord();
1220
1221 if( aReader.HasParsingError() )
1222 THROW_IO_ERROR( wxT( "Tracks6 stream was not parsed correctly" ) );
1223}
1224
1225ATEXT6::ATEXT6( ALTIUM_BINARY_PARSER& aReader, std::map<uint32_t, wxString>& aStringTable )
1226{
1227 ALTIUM_RECORD recordtype = static_cast<ALTIUM_RECORD>( aReader.Read<uint8_t>() );
1228
1229 if( recordtype != ALTIUM_RECORD::TEXT )
1230 THROW_IO_ERROR( wxT( "Texts6 stream has invalid recordtype" ) );
1231
1232 // Subrecord 1 - Properties
1233 size_t subrecord1 = aReader.ReadAndSetSubrecordLength();
1234
1235 layer_v6 = static_cast<ALTIUM_LAYER>( aReader.Read<uint8_t>() );
1236 aReader.Skip( 6 );
1237 component = aReader.Read<uint16_t>();
1238 aReader.Skip( 4 );
1239 position = aReader.ReadVector2IPos();
1240 height = aReader.ReadKicadUnit();
1241 strokefonttype = static_cast<STROKE_FONT_TYPE>( aReader.Read<uint16_t>() );
1242 // TODO: The Serif font type doesn't match well with KiCad, we should replace it with a better match
1243
1244 rotation = aReader.Read<double>();
1245 isMirrored = aReader.Read<uint8_t>() != 0;
1246 strokewidth = aReader.ReadKicadUnit();
1247
1248 if( subrecord1 < 123 )
1249 {
1251 aReader.SkipSubrecord();
1252 return;
1253 }
1254
1255 isComment = aReader.Read<uint8_t>() != 0;
1256 isDesignator = aReader.Read<uint8_t>() != 0;
1257 aReader.Skip( 1 );
1258 fonttype = static_cast<ALTIUM_TEXT_TYPE>( aReader.Read<uint8_t>() );
1259 isBold = aReader.Read<uint8_t>() != 0;
1260 isItalic = aReader.Read<uint8_t>() != 0;
1261
1262 char fontData[64] = { 0 };
1263 aReader.ReadBytes( fontData, sizeof( fontData ) );
1264 fontname = wxString( fontData, wxMBConvUTF16LE(), sizeof( fontData ) ).BeforeFirst( '\0' );
1265
1266 char tmpbyte = aReader.Read<uint8_t>();
1267 isInverted = !!tmpbyte;
1268 margin_border_width = aReader.ReadKicadUnit(); // "Margin Border"
1269 widestring_index = aReader.Read<uint32_t>();
1270 aReader.Skip( 4 );
1271
1272 // An inverted rect in Altium is like a text box with the text inverted.
1273 isInvertedRect = aReader.Read<uint8_t>() != 0;
1274
1277 textbox_rect_justification = static_cast<ALTIUM_TEXT_POSITION>( aReader.Read<uint8_t>() );
1278 text_offset_width = aReader.ReadKicadUnit(); // "Text Offset"
1279
1280 int remaining = aReader.GetRemainingSubrecordBytes();
1281
1282 if( remaining >= 93 )
1283 {
1284 VECTOR2I unk_vec = aReader.ReadVector2ISize();
1285 wxLogTrace( traceAltiumImport, " Unk vec: %d, %d\n", unk_vec.x, unk_vec.y );
1286
1287 barcode_margin = aReader.ReadVector2ISize();
1288
1289 int32_t unk32 = aReader.ReadKicadUnit();
1290 wxLogTrace( traceAltiumImport, " Unk32: %d\n", unk32 );
1291
1292 barcode_type = static_cast<ALTIUM_BARCODE_TYPE>( aReader.Read<uint8_t>() );
1293 uint8_t unk8 = aReader.Read<uint8_t>();
1294 wxLogTrace( traceAltiumImport, " Unk8: %u\n", unk8 );
1295
1296 barcode_inverted = aReader.Read<uint8_t>() != 0;
1297 fonttype = static_cast<ALTIUM_TEXT_TYPE>( aReader.Read<uint8_t>() );
1298
1299 aReader.ReadBytes( fontData, sizeof( fontData ) );
1300 barcode_fontname = wxString( fontData, wxMBConvUTF16LE(), sizeof( fontData ) ).BeforeFirst( '\0' );
1301
1302 aReader.Read<uint8_t>();
1303 wxLogTrace( traceAltiumImport, " Unk8_2: %u\n", unk8 );
1304
1305 layer_v7 = static_cast<ALTIUM_LAYER>( aReader.Read<uint32_t>() );
1306 }
1307
1308 if( remaining >= 103 )
1309 {
1310 // "Frame" text type flag
1311 isFrame = aReader.Read<uint8_t>() != 0;
1312
1313 // Use "Offset" border value instead of "Margin"
1314 isOffsetBorder = aReader.Read<uint8_t>() != 0;
1315
1316 for( size_t ii = 0; ii < 8; ++ii )
1317 {
1318 uint8_t temp = aReader.Peek<uint8_t>();
1319 uint32_t temp32 = ii < 3 ? ALTIUM_PROPS_UTILS::ConvertToKicadUnit( aReader.Peek<uint32_t>() ) : 0;
1320 wxLogTrace( traceAltiumImport, "3ATEXT6 %zu:\t Byte:%u, Kicad:%u\n", ii, temp, temp32 );
1321 aReader.Skip( 1 );
1322 }
1323 }
1324 else
1325 {
1327 isOffsetBorder = false;
1328 }
1329
1330 if( remaining >= 115 )
1331 {
1332 // textbox_rect_justification will be wrong (5) when this flag is unset,
1333 // in that case, we should always use the left bottom justification.
1334 isJustificationValid = aReader.Read<uint8_t>() != 0;
1335 }
1336 else
1337 {
1338 isJustificationValid = false;
1339 }
1340
1341 aReader.SkipSubrecord();
1342
1343 // Subrecord 2 - Legacy 8bit string, max 255 chars, unknown codepage
1344 aReader.ReadAndSetSubrecordLength();
1345
1346 auto entry = aStringTable.find( widestring_index );
1347
1348 if( entry != aStringTable.end() )
1349 text = entry->second;
1350 else
1351 text = aReader.ReadWxString();
1352
1353 // Normalize Windows line endings
1354 text.Replace( wxT( "\r\n" ), wxT( "\n" ) );
1355
1356 aReader.SkipSubrecord();
1357
1358 // Altium only supports inverting truetype fonts
1360 {
1361 isInverted = false;
1362 isInvertedRect = false;
1363 }
1364
1366
1367 if( aReader.HasParsingError() )
1368 THROW_IO_ERROR( wxT( "Texts6 stream was not parsed correctly" ) );
1369}
1370
1372{
1373 ALTIUM_RECORD recordtype = static_cast<ALTIUM_RECORD>( aReader.Read<uint8_t>() );
1374
1375 if( recordtype != ALTIUM_RECORD::FILL )
1376 THROW_IO_ERROR( wxT( "Fills6 stream has invalid recordtype" ) );
1377
1378 // Subrecord 1
1379 aReader.ReadAndSetSubrecordLength();
1380
1381 layer_v6 = static_cast<ALTIUM_LAYER>( aReader.Read<uint8_t>() );
1382
1383 uint8_t flags1 = aReader.Read<uint8_t>();
1384 is_locked = ( flags1 & 0x04 ) == 0;
1385
1386 uint8_t flags2 = aReader.Read<uint8_t>();
1387 is_keepout = flags2 == 2;
1388
1389 net = aReader.Read<uint16_t>();
1390 aReader.Skip( 2 );
1391 component = aReader.Read<uint16_t>();
1392 aReader.Skip( 4 );
1393 pos1 = aReader.ReadVector2IPos();
1394 pos2 = aReader.ReadVector2IPos();
1395 rotation = aReader.Read<double>();
1396
1397 int remaining = aReader.GetRemainingSubrecordBytes();
1398
1399 if( remaining >= 9 )
1400 {
1401 aReader.Skip( 5 );
1402 layer_v7 = static_cast<ALTIUM_LAYER>( aReader.Read<uint32_t>() );
1403 }
1404
1405 if( remaining >= 10 )
1406 keepoutrestrictions = aReader.Read<uint8_t>();
1407 else
1408 keepoutrestrictions = is_keepout ? 0x1F : 0;
1409
1411
1412 aReader.SkipSubrecord();
1413
1414 if( aReader.HasParsingError() )
1415 THROW_IO_ERROR( wxT( "Fills6 stream was not parsed correctly" ) );
1416}
1417
1418AREGION6::AREGION6( ALTIUM_BINARY_PARSER& aReader, bool aExtendedVertices )
1419{
1420 ALTIUM_RECORD recordtype = static_cast<ALTIUM_RECORD>( aReader.Read<uint8_t>() );
1421
1422 if( recordtype != ALTIUM_RECORD::REGION )
1423 THROW_IO_ERROR( wxT( "Regions6 stream has invalid recordtype" ) );
1424
1425 // Subrecord 1
1426 aReader.ReadAndSetSubrecordLength();
1427
1428 layer_v6 = static_cast<ALTIUM_LAYER>( aReader.Read<uint8_t>() );
1429
1430 uint8_t flags1 = aReader.Read<uint8_t>();
1431 is_locked = ( flags1 & 0x04 ) == 0;
1432 is_teardrop = ( flags1 & 0x10 ) != 0;
1433
1434 uint8_t flags2 = aReader.Read<uint8_t>();
1435 is_keepout = flags2 == 2;
1436
1437 net = aReader.Read<uint16_t>();
1438 polygon = aReader.Read<uint16_t>();
1439 component = aReader.Read<uint16_t>();
1440 aReader.Skip( 5 );
1441 holecount = aReader.Read<uint16_t>();
1442 aReader.Skip( 2 );
1443
1444 std::map<wxString, wxString> properties = aReader.ReadProperties();
1445
1446 if( properties.empty() )
1447 THROW_IO_ERROR( wxT( "Regions6 stream has empty properties" ) );
1448
1449 layer_v7 = altium_layer_from_name( ALTIUM_PROPS_UTILS::ReadString( properties, wxT( "V7_LAYER" ), "" ) );
1450
1451 int pkind = ALTIUM_PROPS_UTILS::ReadInt( properties, wxT( "KIND" ), 0 );
1452 bool is_cutout = ALTIUM_PROPS_UTILS::ReadBool( properties, wxT( "ISBOARDCUTOUT" ), false );
1453
1454 is_shapebased = ALTIUM_PROPS_UTILS::ReadBool( properties, wxT( "ISSHAPEBASED" ), false );
1455 keepoutrestrictions = static_cast<uint8_t>(
1456 ALTIUM_PROPS_UTILS::ReadInt( properties, wxT( "KEEPOUTRESTRIC" ), 0x1F ) );
1457
1458 // TODO: this can differ from the other subpolyindex?!
1459 // Note: "the other subpolyindex" is "polygon"
1460 subpolyindex = static_cast<uint16_t>(
1461 ALTIUM_PROPS_UTILS::ReadInt( properties, "SUBPOLYINDEX", ALTIUM_POLYGON_NONE ) );
1462
1463 switch( pkind )
1464 {
1465 case 0:
1466 if( is_cutout )
1467 {
1469 }
1470 else
1471 {
1473 }
1474
1475 break;
1476
1477 case 1:
1479 break;
1480
1481 case 2:
1483 break;
1484
1485 case 3:
1486 kind = ALTIUM_REGION_KIND::UNKNOWN_3; // TODO: what kind is this?
1487 break;
1488
1489 case 4:
1491 break;
1492
1493 default:
1495 break;
1496 }
1497
1498 uint32_t num_outline_vertices = aReader.Read<uint32_t>();
1499
1500 if( aExtendedVertices )
1501 num_outline_vertices++; // Has a closing vertex
1502
1503 for( uint32_t i = 0; i < num_outline_vertices; i++ )
1504 {
1505 if( aExtendedVertices )
1506 {
1507 bool isRound = aReader.Read<uint8_t>() != 0;
1508 VECTOR2I position = aReader.ReadVector2IPos();
1509 VECTOR2I center = aReader.ReadVector2IPos();
1510 int32_t radius = aReader.ReadKicadUnit();
1511 double angle1 = aReader.Read<double>();
1512 double angle2 = aReader.Read<double>();
1513 outline.emplace_back( isRound, radius, angle1, angle2, position, center );
1514 }
1515 else
1516 {
1517 // For some regions the coordinates are stored as double and not as int32_t
1518 int32_t x = ALTIUM_PROPS_UTILS::ConvertToKicadUnit( aReader.Read<double>() );
1519 int32_t y = ALTIUM_PROPS_UTILS::ConvertToKicadUnit( -aReader.Read<double>() );
1520 outline.emplace_back( VECTOR2I( x, y ) );
1521 }
1522 }
1523
1524 holes.resize( holecount );
1525 for( uint16_t k = 0; k < holecount; k++ )
1526 {
1527 uint32_t num_hole_vertices = aReader.Read<uint32_t>();
1528 holes.at( k ).reserve( num_hole_vertices );
1529
1530 for( uint32_t i = 0; i < num_hole_vertices; i++ )
1531 {
1532 int32_t x = ALTIUM_PROPS_UTILS::ConvertToKicadUnit( aReader.Read<double>() );
1533 int32_t y = ALTIUM_PROPS_UTILS::ConvertToKicadUnit( -aReader.Read<double>() );
1534 holes.at( k ).emplace_back( VECTOR2I( x, y ) );
1535 }
1536 }
1537
1539
1540 aReader.SkipSubrecord();
1541
1542 if( aReader.HasParsingError() )
1543 THROW_IO_ERROR( wxT( "Regions6 stream was not parsed correctly" ) );
1544}
bool altiumScopeExprMatchesPolygon(const wxString &aExpr)
Return true if an Altium rule scope expression targets polygon pour primitives (matches InPolygon,...
void altium_parse_polygons(std::map< wxString, wxString > &aProps, std::vector< ALTIUM_VERTICE > &aVertices)
ALTIUM_MECHKIND altium_mechkind_from_name(const wxString &aName)
static void ExpectSubrecordLengthAtLeast(const std::string &aStreamType, const std::string &aSubrecordName, size_t aExpectedLength, size_t aActualLength)
Throw an IO_ERROR if the actual length is less than the expected length.
ALTIUM_LAYER altium_layer_from_name(const wxString &aName)
static AEXTENDED_PRIMITIVE_INFORMATION_TYPE ReadAltiumExtendedPrimitiveInformationTypeFromProperties(const std::map< wxString, wxString > &aProps, wxString aKey)
ALTIUM_LAYER altium_versioned_layer(ALTIUM_LAYER aV6Layer, ALTIUM_LAYER aV7Layer)
const ARULE6 * selectAltiumPolygonRule(const std::vector< ARULE6 > &aRulesByPriorityAsc)
Select the highest Altium-priority rule whose scope references polygons.
static ALTIUM_MODE ReadAltiumModeFromProperties(const std::map< wxString, wxString > &aProps, wxString aKey)
static std::vector< ABOARD6_LAYER_STACKUP > ReadAltiumStackupFromProperties(const std::map< wxString, wxString > &aProps)
static ALTIUM_RECORD ReadAltiumRecordFromProperties(const std::map< wxString, wxString > &aProps, wxString aKey)
ALTIUM_PAD_SHAPE_ALT
ALTIUM_TEXT_POSITION
ALTIUM_MECHKIND
ALTIUM_PAD_MODE
ALTIUM_TEXT_TYPE
ALTIUM_PAD_SHAPE
ALTIUM_BARCODE_TYPE
ALTIUM_DIMENSION_KIND
ALTIUM_PAD_HOLE_SHAPE
const uint16_t ALTIUM_NET_UNCONNECTED
ALTIUM_CLASS_KIND
const uint16_t ALTIUM_POLYGON_NONE
AEXTENDED_PRIMITIVE_INFORMATION_TYPE
ALTIUM_RECORD
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:990
void Skip(size_t aLength)
size_t GetRemainingSubrecordBytes() const
std::map< wxString, wxString > ReadProperties(std::function< std::map< wxString, wxString >(const std::string &)> handleBinaryData=[](const std::string &) { return std::map< wxString, wxString >();})
int ReadBytes(char *aOut, size_t aSize)
static int ReadInt(const std::map< wxString, wxString > &aProps, const wxString &aKey, int aDefault)
static int32_t ReadKicadUnit(const std::map< wxString, wxString > &aProps, const wxString &aKey, const wxString &aDefault)
static bool ReadBool(const std::map< wxString, wxString > &aProps, const wxString &aKey, bool aDefault)
static wxString ReadUnicodeString(const std::map< wxString, wxString > &aProps, const wxString &aKey, const wxString &aDefault)
static wxString ReadString(const std::map< wxString, wxString > &aProps, const wxString &aKey, const wxString &aDefault)
static double ReadDouble(const std::map< wxString, wxString > &aProps, const wxString &aKey, double aDefault)
static int32_t ConvertToKicadUnit(const double aValue)
#define _(s)
static const wxChar * traceAltiumImport
Flag to enable Altium importer logging.
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
double startangle
uint16_t component
uint32_t width
ALTIUM_LAYER layer
AARC6(ALTIUM_BINARY_PARSER &aReader)
uint8_t keepoutrestrictions
uint16_t polygon
VECTOR2I center
uint32_t radius
uint16_t net
ALTIUM_LAYER layer_v6
ALTIUM_LAYER layer_v7
bool is_polygonoutline
double endangle
uint16_t subpolyindex
ABOARD6_LAYER_STACKUP(const std::map< wxString, wxString > &aProps, const wxString &aPrefix, uint32_t aLayerIdFallback)
VECTOR2I sheetpos
std::set< wxString > layerNames
ABOARD6(ALTIUM_BINARY_PARSER &aReader)
std::vector< ABOARD6_LAYER_STACKUP > stackup
std::vector< ALTIUM_VERTICE > board_vertices
wxString uniqueid
std::vector< wxString > names
ALTIUM_CLASS_KIND kind
wxString name
ACLASS6(ALTIUM_BINARY_PARSER &aReader)
wxString sourceHierachicalPath
ALTIUM_TEXT_POSITION commentautoposition
ALTIUM_TEXT_POSITION nameautoposition
ACOMPONENT6(ALTIUM_BINARY_PARSER &aReader)
wxString sourcefootprintlibrary
ALTIUM_LAYER layer
wxString sourcelibreference
wxString sourcedesignator
wxString sourceUniqueID
wxString sourcecomponentlibrary
ACOMPONENTBODY6(ALTIUM_BINARY_PARSER &aReader)
ALTIUM_UNIT textunit
uint32_t textlinewidth
ALTIUM_LAYER layer
ALTIUM_LAYER layer_v6
std::vector< VECTOR2I > textPoint
ALTIUM_DIMENSION_KIND kind
ALTIUM_LAYER layer_v7
ADIMENSION6(ALTIUM_BINARY_PARSER &aReader)
std::vector< VECTOR2I > referencePoint
AEXTENDED_PRIMITIVE_INFORMATION_TYPE type
AEXTENDED_PRIMITIVE_INFORMATION(ALTIUM_BINARY_PARSER &aReader)
VECTOR2I pos2
ALTIUM_LAYER layer
uint16_t net
VECTOR2I pos1
double rotation
uint8_t keepoutrestrictions
AFILL6(ALTIUM_BINARY_PARSER &aReader)
ALTIUM_LAYER layer_v6
uint16_t component
ALTIUM_LAYER layer_v7
std::set< wxString > layerNames
std::vector< ABOARD6_LAYER_STACKUP > stackup
ALIBRARY(ALTIUM_BINARY_PARSER &aReader)
double z_offset
int32_t checksum
wxString name
VECTOR3D rotation
AMODEL(ALTIUM_BINARY_PARSER &aReader)
ANET6(ALTIUM_BINARY_PARSER &aReader)
wxString name
double holerotation
int32_t soldermaskexpansionmanual
uint16_t net
ALTIUM_LAYER layer
APAD6(ALTIUM_BINARY_PARSER &aReader)
std::unique_ptr< APAD6_SIZE_AND_SHAPE > sizeAndShape
ALTIUM_LAYER layer_v7
ALTIUM_LAYER tolayer
ALTIUM_PAD_SHAPE topshape
ALTIUM_LAYER layer_v6
ALTIUM_PAD_MODE padmode
ALTIUM_MODE pastemaskexpansionmode
uint32_t holesize
double direction
ALTIUM_MODE soldermaskexpansionmode
wxString name
bool is_test_fab_bottom
int32_t pad_to_die_delay
bool is_tent_bottom
VECTOR2I botsize
ALTIUM_PAD_SHAPE botshape
uint16_t component
VECTOR2I midsize
ALTIUM_PAD_SHAPE midshape
int32_t pastemaskexpansionmanual
ALTIUM_LAYER fromlayer
VECTOR2I position
VECTOR2I topsize
bool is_test_fab_top
int32_t pad_to_die_length
ALTIUM_LAYER layer_v6
int32_t minprimlength
std::vector< ALTIUM_VERTICE > vertices
APOLYGON6(ALTIUM_BINARY_PARSER &aReader)
ALTIUM_LAYER layer_v7
ALTIUM_POLYGON_HATCHSTYLE hatchstyle
ALTIUM_LAYER layer
uint8_t keepoutrestrictions
ALTIUM_LAYER layer
AREGION6(ALTIUM_BINARY_PARSER &aReader, bool aExtendedVertices)
ALTIUM_LAYER layer_v7
uint16_t holecount
ALTIUM_LAYER layer_v6
std::vector< ALTIUM_VERTICE > outline
uint16_t subpolyindex
std::vector< std::vector< ALTIUM_VERTICE > > holes
uint16_t component
uint16_t polygon
ALTIUM_REGION_KIND kind
ALTIUM_RULE_KIND kind
ALTIUM_CONNECT_STYLE polygonconnectStyle
wxString scope1expr
wxString name
int planeclearanceClearance
int32_t polygonconnectReliefconductorwidth
int pastemaskExpansion
ARULE6()=default
wxString scope2expr
int soldermaskExpansion
int32_t polygonconnectAirgapwidth
int polygonconnectReliefentries
uint32_t text_offset_width
VECTOR2I barcode_margin
uint32_t textbox_rect_height
uint16_t component
ALTIUM_LAYER layer_v6
ALTIUM_TEXT_POSITION textbox_rect_justification
bool isInvertedRect
wxString text
uint32_t widestring_index
uint32_t textbox_rect_width
double rotation
uint32_t margin_border_width
uint32_t height
wxString fontname
bool isJustificationValid
ALTIUM_BARCODE_TYPE barcode_type
ALTIUM_LAYER layer
VECTOR2I position
ALTIUM_LAYER layer_v7
ALTIUM_TEXT_TYPE fonttype
STROKE_FONT_TYPE strokefonttype
wxString barcode_fontname
bool isOffsetBorder
ATEXT6(ALTIUM_BINARY_PARSER &aReader, std::map< uint32_t, wxString > &aStringTable)
bool barcode_inverted
uint32_t strokewidth
ALTIUM_LAYER layer_v6
ATRACK6(ALTIUM_BINARY_PARSER &aReader)
uint32_t width
bool is_polygonoutline
ALTIUM_LAYER layer_v7
uint16_t polygon
uint16_t subpolyindex
uint8_t keepoutrestrictions
VECTOR2I start
ALTIUM_LAYER layer
uint16_t component
bool soldermask_expansion_linked
uint32_t thermal_relief_conductorcount
uint32_t diameter
uint32_t neg_tolerance
uint16_t net
VECTOR2I position
int32_t thermal_relief_airgap
bool is_test_fab_top
int32_t soldermask_expansion_front
uint32_t pos_tolerance
bool is_tent_bottom
bool is_test_fab_bottom
bool soldermask_expansion_manual
ALTIUM_PAD_MODE viamode
AVIA6(ALTIUM_BINARY_PARSER &aReader)
uint32_t thermal_relief_conductorwidth
int32_t soldermask_expansion_back
uint32_t diameter_by_layer[32]
ALTIUM_LAYER layer_start
ALTIUM_LAYER layer_end
uint32_t holesize
VECTOR2I center
int radius
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:687