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
33#include "altium_parser_pcb.h"
36
37
45static const wxChar* traceAltiumImport = wxT( "KICAD_ALTIUM_IMPORT" );
46
47
48ALTIUM_LAYER altium_layer_from_name( const wxString& aName )
49{
50 static const std::unordered_map<std::string, ALTIUM_LAYER> hash_map = {
51 { "TOP", ALTIUM_LAYER::TOP_LAYER },
52 { "MID1", ALTIUM_LAYER::MID_LAYER_1 },
53 { "MID2", ALTIUM_LAYER::MID_LAYER_2 },
54 { "MID3", ALTIUM_LAYER::MID_LAYER_3 },
55 { "MID4", ALTIUM_LAYER::MID_LAYER_4 },
56 { "MID5", ALTIUM_LAYER::MID_LAYER_5 },
57 { "MID6", ALTIUM_LAYER::MID_LAYER_6 },
58 { "MID7", ALTIUM_LAYER::MID_LAYER_7 },
59 { "MID8", ALTIUM_LAYER::MID_LAYER_8 },
60 { "MID9", ALTIUM_LAYER::MID_LAYER_9 },
61 { "MID10", ALTIUM_LAYER::MID_LAYER_10 },
62 { "MID11", ALTIUM_LAYER::MID_LAYER_11 },
63 { "MID12", ALTIUM_LAYER::MID_LAYER_12 },
64 { "MID13", ALTIUM_LAYER::MID_LAYER_13 },
65 { "MID14", ALTIUM_LAYER::MID_LAYER_14 },
66 { "MID15", ALTIUM_LAYER::MID_LAYER_15 },
67 { "MID16", ALTIUM_LAYER::MID_LAYER_16 },
68 { "MID17", ALTIUM_LAYER::MID_LAYER_17 },
69 { "MID18", ALTIUM_LAYER::MID_LAYER_18 },
70 { "MID19", ALTIUM_LAYER::MID_LAYER_19 },
71 { "MID20", ALTIUM_LAYER::MID_LAYER_20 },
72 { "MID21", ALTIUM_LAYER::MID_LAYER_21 },
73 { "MID22", ALTIUM_LAYER::MID_LAYER_22 },
74 { "MID23", ALTIUM_LAYER::MID_LAYER_23 },
75 { "MID24", ALTIUM_LAYER::MID_LAYER_24 },
76 { "MID25", ALTIUM_LAYER::MID_LAYER_25 },
77 { "MID26", ALTIUM_LAYER::MID_LAYER_26 },
78 { "MID27", ALTIUM_LAYER::MID_LAYER_27 },
79 { "MID28", ALTIUM_LAYER::MID_LAYER_28 },
80 { "MID29", ALTIUM_LAYER::MID_LAYER_29 },
81 { "MID30", ALTIUM_LAYER::MID_LAYER_30 },
82 { "BOTTOM", ALTIUM_LAYER::BOTTOM_LAYER },
83
84 { "TOPOVERLAY", ALTIUM_LAYER::TOP_OVERLAY },
85 { "BOTTOMOVERLAY", ALTIUM_LAYER::BOTTOM_OVERLAY },
86 { "TOPPASTE", ALTIUM_LAYER::TOP_PASTE },
87 { "BOTTOMPASTE", ALTIUM_LAYER::BOTTOM_PASTE },
88 { "TOPSOLDER", ALTIUM_LAYER::TOP_SOLDER },
89 { "BOTTOMSOLDER", ALTIUM_LAYER::BOTTOM_SOLDER },
90
100 { "PLANE10", ALTIUM_LAYER::INTERNAL_PLANE_10 },
101 { "PLANE11", ALTIUM_LAYER::INTERNAL_PLANE_11 },
102 { "PLANE12", ALTIUM_LAYER::INTERNAL_PLANE_12 },
103 { "PLANE13", ALTIUM_LAYER::INTERNAL_PLANE_13 },
104 { "PLANE14", ALTIUM_LAYER::INTERNAL_PLANE_14 },
105 { "PLANE15", ALTIUM_LAYER::INTERNAL_PLANE_15 },
106 { "PLANE16", ALTIUM_LAYER::INTERNAL_PLANE_16 },
107
108 { "DRILLGUIDE", ALTIUM_LAYER::DRILL_GUIDE },
109 { "KEEPOUT", ALTIUM_LAYER::KEEP_OUT_LAYER },
110
111 { "MECHANICAL1", ALTIUM_LAYER::MECHANICAL_1 },
112 { "MECHANICAL2", ALTIUM_LAYER::MECHANICAL_2 },
113 { "MECHANICAL3", ALTIUM_LAYER::MECHANICAL_3 },
114 { "MECHANICAL4", ALTIUM_LAYER::MECHANICAL_4 },
115 { "MECHANICAL5", ALTIUM_LAYER::MECHANICAL_5 },
116 { "MECHANICAL6", ALTIUM_LAYER::MECHANICAL_6 },
117 { "MECHANICAL7", ALTIUM_LAYER::MECHANICAL_7 },
118 { "MECHANICAL8", ALTIUM_LAYER::MECHANICAL_8 },
119 { "MECHANICAL9", ALTIUM_LAYER::MECHANICAL_9 },
120 { "MECHANICAL10", ALTIUM_LAYER::MECHANICAL_10 },
121 { "MECHANICAL11", ALTIUM_LAYER::MECHANICAL_11 },
122 { "MECHANICAL12", ALTIUM_LAYER::MECHANICAL_12 },
123 { "MECHANICAL13", ALTIUM_LAYER::MECHANICAL_13 },
124 { "MECHANICAL14", ALTIUM_LAYER::MECHANICAL_14 },
125 { "MECHANICAL15", ALTIUM_LAYER::MECHANICAL_15 },
126 { "MECHANICAL16", ALTIUM_LAYER::MECHANICAL_16 },
127
128 { "DRILLDRAWING", ALTIUM_LAYER::DRILL_DRAWING },
129 { "MULTILAYER", ALTIUM_LAYER::MULTI_LAYER },
130
131 // FIXME: the following mapping is just a guess
132 { "CONNECTIONS", ALTIUM_LAYER::CONNECTIONS },
133 { "BACKGROUND", ALTIUM_LAYER::BACKGROUND },
134 { "DRCERRORMARKERS", ALTIUM_LAYER::DRC_ERROR_MARKERS },
135 { "SELECTIONS", ALTIUM_LAYER::SELECTIONS },
136 { "VISIBLEGRID1", ALTIUM_LAYER::VISIBLE_GRID_1 },
137 { "VISIBLEGRID2", ALTIUM_LAYER::VISIBLE_GRID_2 },
138 { "PADHOLES", ALTIUM_LAYER::PAD_HOLES },
139 { "VIAHOLES", ALTIUM_LAYER::VIA_HOLES },
140 };
141
142 auto it = hash_map.find( std::string( aName.c_str() ) );
143
144 if( it == hash_map.end() )
145 {
146 wxLogError( _( "Unknown mapping of the Altium layer '%s'." ), aName );
148 }
149 else
150 {
151 return it->second;
152 }
153}
154
155void altium_parse_polygons( std::map<wxString, wxString>& aProps,
156 std::vector<ALTIUM_VERTICE>& aVertices )
157{
158 for( size_t i = 0; i < std::numeric_limits<size_t>::max(); i++ )
159 {
160 const wxString si = std::to_string( i );
161
162 const wxString vxi = wxT( "VX" ) + si;
163 const wxString vyi = wxT( "VY" ) + si;
164
165 if( aProps.find( vxi ) == aProps.end() || aProps.find( vyi ) == aProps.end() )
166 break; // it doesn't seem like we know beforehand how many vertices are inside a polygon
167
168 const bool isRound = ALTIUM_PROPS_UTILS::ReadInt( aProps, wxT( "KIND" ) + si, 0 ) != 0;
169 const int32_t radius = ALTIUM_PROPS_UTILS::ReadKicadUnit( aProps, wxT( "R" ) + si, wxT( "0mil" ) );
170 const double sa = ALTIUM_PROPS_UTILS::ReadDouble( aProps, wxT( "SA" ) + si, 0. );
171 const double ea = ALTIUM_PROPS_UTILS::ReadDouble( aProps, wxT( "EA" ) + si, 0. );
172 const VECTOR2I vp = VECTOR2I( ALTIUM_PROPS_UTILS::ReadKicadUnit( aProps, vxi, wxT( "0mil" ) ),
173 -ALTIUM_PROPS_UTILS::ReadKicadUnit( aProps, vyi, wxT( "0mil" ) ) );
174 const VECTOR2I cp = VECTOR2I( ALTIUM_PROPS_UTILS::ReadKicadUnit( aProps, wxT( "CX" ) + si, wxT( "0mil" ) ),
175 -ALTIUM_PROPS_UTILS::ReadKicadUnit( aProps, wxT( "CY" ) + si, wxT( "0mil" ) ) );
176
177 aVertices.emplace_back( isRound, radius, sa, ea, vp, cp );
178 }
179}
180
181
182static ALTIUM_MODE ReadAltiumModeFromProperties( const std::map<wxString, wxString>& aProps,
183 wxString aKey )
184{
185 wxString mode = ALTIUM_PROPS_UTILS::ReadString( aProps, aKey, wxT( "" ) );
186
187 if( mode == wxT( "None" ) )
188 return ALTIUM_MODE::NONE;
189 else if( mode == wxT( "Rule" ) )
190 return ALTIUM_MODE::RULE;
191 else if( mode == wxT( "Manual" ) )
192 return ALTIUM_MODE::MANUAL;
193
194 wxLogError( _( "Unknown Mode string: '%s'." ), mode );
196}
197
198
199static ALTIUM_RECORD ReadAltiumRecordFromProperties( const std::map<wxString, wxString>& aProps,
200 wxString aKey )
201{
202 wxString record = ALTIUM_PROPS_UTILS::ReadString( aProps, aKey, wxT( "" ) );
203
204 if( record == wxT( "Arc" ) )
205 return ALTIUM_RECORD::ARC;
206 else if( record == wxT( "Pad" ) )
207 return ALTIUM_RECORD::PAD;
208 else if( record == wxT( "Via" ) )
209 return ALTIUM_RECORD::VIA;
210 else if( record == wxT( "Track" ) )
212 else if( record == wxT( "Text" ) )
213 return ALTIUM_RECORD::TEXT;
214 else if( record == wxT( "Fill" ) )
215 return ALTIUM_RECORD::FILL;
216 else if( record == wxT( "Region" ) ) // correct?
218 else if( record == wxT( "Model" ) )
220
221 wxLogError( _( "Unknown Record name string: '%s'." ), record );
223}
224
225
228 const std::map<wxString, wxString>& aProps, wxString aKey )
229{
230 wxString parsedType = ALTIUM_PROPS_UTILS::ReadString( aProps, aKey, wxT( "" ) );
231
232 if( parsedType == wxT( "Mask" ) )
234
235 wxLogError( _( "Unknown Extended Primitive Information type: '%s'." ), parsedType );
237}
238
239
248static void ExpectSubrecordLengthAtLeast( const std::string& aStreamType,
249 const std::string& aSubrecordName, size_t aExpectedLength,
250 size_t aActualLength )
251{
252 if( aActualLength < aExpectedLength )
253 {
254 THROW_IO_ERROR( wxString::Format( "%s stream %s has length %d, "
255 "which is unexpected (expected at least %d)",
256 aStreamType, aSubrecordName, aActualLength,
257 aExpectedLength ) );
258 }
259}
260
261
263{
264 const std::map<wxString, wxString> props = aReader.ReadProperties();
265
266 if( props.empty() )
267 THROW_IO_ERROR( wxT( "ExtendedPrimitiveInformation stream has no properties!" ) );
268
269 primitiveIndex = ALTIUM_PROPS_UTILS::ReadInt( props, wxT( "PRIMITIVEINDEX" ), -1 );
270 primitiveObjectId = ReadAltiumRecordFromProperties( props, wxT( "PRIMITIVEOBJECTID" ) );
272
273 pastemaskexpansionmode = ReadAltiumModeFromProperties( props, wxT( "PASTEMASKEXPANSIONMODE" ) );
275 props, wxT( "PASTEMASKEXPANSION_MANUAL" ), wxT( "0mil" ) );
277 ReadAltiumModeFromProperties( props, wxT( "SOLDERMASKEXPANSIONMODE" ) );
279 props, wxT( "SOLDERMASKEXPANSION_MANUAL" ), wxT( "0mil" ) );
280}
281
282
284{
285 std::map<wxString, wxString> props = aReader.ReadProperties();
286
287 if( props.empty() )
288 THROW_IO_ERROR( wxT( "Board6 stream has no properties!" ) );
289
290 sheetpos = VECTOR2I( ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "SHEETX" ), wxT( "0mil" ) ),
291 -ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "SHEETY" ), wxT( "0mil" ) ) );
292 sheetsize = wxSize( ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "SHEETWIDTH" ), wxT( "0mil" ) ),
293 ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "SHEETHEIGHT" ), wxT( "0mil" ) ) );
294
295 layercount = ALTIUM_PROPS_UTILS::ReadInt( props, wxT( "LAYERSETSCOUNT" ), 1 ) + 1;
296
297 for( size_t i = 1; i < std::numeric_limits<size_t>::max(); i++ )
298 {
299 const wxString layeri = wxT( "LAYER" ) + wxString( std::to_string( i ) );
300 const wxString layername = layeri + wxT( "NAME" );
301
302 auto layernameit = props.find( layername );
303
304 if( layernameit == props.end() )
305 break; // it doesn't seem like we know beforehand how many vertices are inside a polygon
306
308
309 l.name = ALTIUM_PROPS_UTILS::ReadString( props, layername, wxT( "" ) );
310 wxString originalName = l.name;
311 int ii = 2;
312
313 // Ensure that layer names are unique in KiCad
314 while( !layerNames.insert( l.name ).second )
315 l.name = wxString::Format( wxT( "%s %d" ), originalName, ii++ );
316
317 l.nextId = ALTIUM_PROPS_UTILS::ReadInt( props, layeri + wxT( "NEXT" ), 0 );
318 l.prevId = ALTIUM_PROPS_UTILS::ReadInt( props, layeri + wxT( "PREV" ), 0 );
319 l.copperthick = ALTIUM_PROPS_UTILS::ReadKicadUnit( props, layeri + wxT( "COPTHICK" ), wxT( "1.4mil" ) );
320
321 l.dielectricconst = ALTIUM_PROPS_UTILS::ReadDouble( props, layeri + wxT( "DIELCONST" ), 0. );
322 l.dielectricthick = ALTIUM_PROPS_UTILS::ReadKicadUnit( props, layeri + wxT( "DIELHEIGHT" ), wxT( "60mil" ) );
323 l.dielectricmaterial = ALTIUM_PROPS_UTILS::ReadString( props, layeri + wxT( "DIELMATERIAL" ), wxT( "FR-4" ) );
324
325 stackup.push_back( l );
326 }
327
329
330 if( aReader.HasParsingError() )
331 THROW_IO_ERROR( wxT( "Board6 stream was not parsed correctly!" ) );
332}
333
335{
336 std::map<wxString, wxString> properties = aReader.ReadProperties();
337
338 if( properties.empty() )
339 THROW_IO_ERROR( wxT( "Classes6 stream has no properties!" ) );
340
341 name = ALTIUM_PROPS_UTILS::ReadString( properties, wxT( "NAME" ), wxT( "" ) );
342 uniqueid = ALTIUM_PROPS_UTILS::ReadString( properties, wxT( "UNIQUEID" ), wxT( "" ) );
343 kind = static_cast<ALTIUM_CLASS_KIND>( ALTIUM_PROPS_UTILS::ReadInt( properties, wxT( "KIND" ), -1 ) );
344
345 for( size_t i = 0; i < std::numeric_limits<size_t>::max(); i++ )
346 {
347 auto mit = properties.find( wxT( "M" ) + wxString( std::to_string( i ) ) );
348
349 if( mit == properties.end() )
350 break; // it doesn't seem like we know beforehand how many components are in the netclass
351
352 names.push_back( mit->second );
353 }
354
355 if( aReader.HasParsingError() )
356 THROW_IO_ERROR( wxT( "Classes6 stream was not parsed correctly" ) );
357}
358
360{
361 std::map<wxString, wxString> props = aReader.ReadProperties();
362
363 if( props.empty() )
364 THROW_IO_ERROR( wxT( "Components6 stream has no props" ) );
365
366 layer = altium_layer_from_name( ALTIUM_PROPS_UTILS::ReadString( props, wxT( "LAYER" ), wxT( "" ) ) );
367 position = VECTOR2I( ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "X" ), wxT( "0mil" ) ),
368 -ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "Y" ), wxT( "0mil" ) ) );
369 rotation = ALTIUM_PROPS_UTILS::ReadDouble( props, wxT( "ROTATION" ), 0. );
370 locked = ALTIUM_PROPS_UTILS::ReadBool( props, wxT( "LOCKED" ), false );
371 nameon = ALTIUM_PROPS_UTILS::ReadBool( props, wxT( "NAMEON" ), true );
372 commenton = ALTIUM_PROPS_UTILS::ReadBool( props, wxT( "COMMENTON" ), false );
373 sourcedesignator = ALTIUM_PROPS_UTILS::ReadString( props, wxT( "SOURCEDESIGNATOR" ), wxT( "" ) );
374
375 sourceUniqueID = ALTIUM_PROPS_UTILS::ReadString( props, wxT( "SOURCEUNIQUEID" ), wxT( "" ) );
376
377 // Remove leading backslash from sourceUniqueID to match schematic component unique IDs
378 if( sourceUniqueID.starts_with( wxT( "\\" ) ) )
380
381 sourceHierachicalPath = ALTIUM_PROPS_UTILS::ReadString( props, wxT( "SOURCEHIERARCHICALPATH" ), wxT( "" ) );
383 ALTIUM_PROPS_UTILS::ReadUnicodeString( props, wxT( "SOURCEFOOTPRINTLIBRARY" ), wxT( "" ) );
384 pattern = ALTIUM_PROPS_UTILS::ReadUnicodeString( props, wxT( "PATTERN" ), wxT( "" ) );
385
386 sourcecomponentlibrary = ALTIUM_PROPS_UTILS::ReadString( props, wxT( "SOURCECOMPONENTLIBRARY" ), wxT( "" ) );
387 sourcelibreference = ALTIUM_PROPS_UTILS::ReadString( props, wxT( "SOURCELIBREFERENCE" ), wxT( "" ) );
388
390 ALTIUM_PROPS_UTILS::ReadInt( props, wxT( "NAMEAUTOPOSITION" ), 0 ) );
392 ALTIUM_PROPS_UTILS::ReadInt( props, wxT( "COMMENTAUTOPOSITION" ), 0 ) );
393
394 if( aReader.HasParsingError() )
395 THROW_IO_ERROR( wxT( "Components6 stream was not parsed correctly" ) );
396}
397
399{
400 aReader.Skip( 2 );
401
402 std::map<wxString, wxString> props = aReader.ReadProperties();
403
404 if( props.empty() )
405 THROW_IO_ERROR( wxT( "Dimensions6 stream has no props" ) );
406
407 layer = altium_layer_from_name( ALTIUM_PROPS_UTILS::ReadString( props, wxT( "LAYER" ), wxT( "" ) ) );
408 kind = static_cast<ALTIUM_DIMENSION_KIND>( ALTIUM_PROPS_UTILS::ReadInt( props, wxT( "DIMENSIONKIND" ), 0 ) );
409
410 textformat = ALTIUM_PROPS_UTILS::ReadString( props, wxT( "TEXTFORMAT" ), wxT( "" ) );
411 textprefix = ALTIUM_PROPS_UTILS::ReadString( props, wxT( "TEXTPREFIX" ), wxT( "" ) );
412 textsuffix = ALTIUM_PROPS_UTILS::ReadString( props, wxT( "TEXTSUFFIX" ), wxT( "" ) );
413
414 height = ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "HEIGHT" ), wxT( "0mil" ) );
415 angle = ALTIUM_PROPS_UTILS::ReadDouble( props, wxT( "ANGLE" ), 0. );
416
417 linewidth = ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "LINEWIDTH" ), wxT( "10mil" ) );
418 textheight = ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "TEXTHEIGHT" ), wxT( "10mil" ) );
419 textlinewidth = ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "TEXTLINEWIDTH" ), wxT( "6mil" ) );
420 textprecision = ALTIUM_PROPS_UTILS::ReadInt( props, wxT( "TEXTPRECISION" ), 2 );
421 textbold = ALTIUM_PROPS_UTILS::ReadBool( props, wxT( "TEXTLINEWIDTH" ), false );
422 textitalic = ALTIUM_PROPS_UTILS::ReadBool( props, wxT( "ITALIC" ), false );
423 textgap = ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "TEXTGAP" ), wxT( "10mil" ) );
424
425 arrowsize = ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "ARROWSIZE" ), wxT( "60mil" ) );
426
427 wxString text_position_raw = ALTIUM_PROPS_UTILS::ReadString( props, wxT( "TEXTPOSITION" ), wxT( "" ) );
428
429 xy1 = VECTOR2I( ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "X1" ), wxT( "0mil" ) ),
430 -ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "Y1" ), wxT( "0mil" ) ) );
431
432 int refcount = ALTIUM_PROPS_UTILS::ReadInt( props, wxT( "REFERENCES_COUNT" ), 0 );
433
434 for( int i = 0; i < refcount; i++ )
435 {
436 const std::string refi = "REFERENCE" + std::to_string( i ) + "POINT";
437 const wxString ref( refi );
438 referencePoint.emplace_back( ALTIUM_PROPS_UTILS::ReadKicadUnit( props, ref + wxT( "X" ), wxT( "0mil" ) ),
439 -ALTIUM_PROPS_UTILS::ReadKicadUnit( props, ref + wxT( "Y" ), wxT( "0mil" ) ) );
440 }
441
442 for( size_t i = 1; i < std::numeric_limits<size_t>::max(); i++ )
443 {
444 const std::string texti = "TEXT" + std::to_string( i );
445 const std::string textix = texti + "X";
446 const std::string textiy = texti + "Y";
447
448 if( props.find( textix ) == props.end() || props.find( textiy ) == props.end() )
449 break; // it doesn't seem like we know beforehand how many vertices are inside a polygon
450
451 textPoint.emplace_back( ALTIUM_PROPS_UTILS::ReadKicadUnit( props, textix, wxT( "0mil" ) ),
452 -ALTIUM_PROPS_UTILS::ReadKicadUnit( props, textiy, wxT( "0mil" ) ) );
453 }
454
455 wxString dimensionunit = ALTIUM_PROPS_UTILS::ReadString( props, wxT( "TEXTDIMENSIONUNIT" ), wxT( "Millimeters" ) );
456
457 if( dimensionunit == wxT( "Inches" ) ) textunit = ALTIUM_UNIT::INCH;
458 else if( dimensionunit == wxT( "Mils" ) ) textunit = ALTIUM_UNIT::MILS;
459 else if( dimensionunit == wxT( "Millimeters" ) ) textunit = ALTIUM_UNIT::MM;
460 else if( dimensionunit == wxT( "Centimeters" ) ) textunit = ALTIUM_UNIT::CM;
462
463 if( aReader.HasParsingError() )
464 THROW_IO_ERROR( wxT( "Dimensions6 stream was not parsed correctly" ) );
465}
466
468{
469 std::map<wxString, wxString> properties = aReader.ReadProperties();
470
471 if( properties.empty() )
472 THROW_IO_ERROR( wxT( "Model stream has no properties!" ) );
473
474 name = ALTIUM_PROPS_UTILS::ReadString( properties, wxT( "NAME" ), wxT( "" ) );
475 id = ALTIUM_PROPS_UTILS::ReadString( properties, wxT( "ID" ), wxT( "" ) );
476 isEmbedded = ALTIUM_PROPS_UTILS::ReadBool( properties, wxT( "EMBED" ), false );
477
478 rotation.x = ALTIUM_PROPS_UTILS::ReadDouble( properties, wxT( "ROTX" ), 0. );
479 rotation.y = ALTIUM_PROPS_UTILS::ReadDouble( properties, wxT( "ROTY" ), 0. );
480 rotation.z = ALTIUM_PROPS_UTILS::ReadDouble( properties, wxT( "ROTZ" ), 0. );
481
482 z_offset = ALTIUM_PROPS_UTILS::ReadDouble( properties, wxT( "DZ" ), 0. );
483 checksum = ALTIUM_PROPS_UTILS::ReadInt( properties, wxT( "CHECKSUM" ), 0 );
484
485 if( aReader.HasParsingError() )
486 THROW_IO_ERROR( wxT( "Model stream was not parsed correctly" ) );
487}
488
490{
491 std::map<wxString, wxString> properties = aReader.ReadProperties();
492
493 if( properties.empty() )
494 THROW_IO_ERROR( wxT( "Nets6 stream has no properties" ) );
495
496 name = ALTIUM_PROPS_UTILS::ReadString( properties, wxT( "NAME" ), wxT( "" ) );
497
498 if( aReader.HasParsingError() )
499 THROW_IO_ERROR( wxT( "Nets6 stream was not parsed correctly" ) );
500}
501
503{
504 std::map<wxString, wxString> properties = aReader.ReadProperties();
505
506 if( properties.empty() )
507 THROW_IO_ERROR( wxT( "Polygons6 stream has no properties" ) );
508
509 layer = altium_layer_from_name( ALTIUM_PROPS_UTILS::ReadString( properties, wxT( "LAYER" ), wxT( "" ) ) );
510 net = ALTIUM_PROPS_UTILS::ReadInt( properties, wxT( "NET" ), ALTIUM_NET_UNCONNECTED );
511 locked = ALTIUM_PROPS_UTILS::ReadBool( properties, wxT( "LOCKED" ), false );
512
513 // TODO: kind
514
515 gridsize = ALTIUM_PROPS_UTILS::ReadKicadUnit( properties, wxT( "GRIDSIZE" ), wxT( "0mil" ) );
516 trackwidth = ALTIUM_PROPS_UTILS::ReadKicadUnit( properties, wxT( "TRACKWIDTH" ), wxT( "0mil" ) );
517 minprimlength = ALTIUM_PROPS_UTILS::ReadKicadUnit( properties, wxT( "MINPRIMLENGTH" ), wxT( "0mil" ) );
518 useoctagons = ALTIUM_PROPS_UTILS::ReadBool( properties, wxT( "USEOCTAGONS" ), false );
519
520 pourindex = ALTIUM_PROPS_UTILS::ReadInt( properties, wxT( "POURINDEX" ), 0 );
521
522 wxString hatchstyleraw = ALTIUM_PROPS_UTILS::ReadString( properties, wxT( "HATCHSTYLE" ), wxT( "" ) );
523
524 if( hatchstyleraw == wxT( "Solid" ) ) hatchstyle = ALTIUM_POLYGON_HATCHSTYLE::SOLID;
525 else if( hatchstyleraw == wxT( "45Degree" ) ) hatchstyle = ALTIUM_POLYGON_HATCHSTYLE::DEGREE_45;
526 else if( hatchstyleraw == wxT( "90Degree" ) ) hatchstyle = ALTIUM_POLYGON_HATCHSTYLE::DEGREE_90;
527 else if( hatchstyleraw == wxT( "Horizontal" ) ) hatchstyle = ALTIUM_POLYGON_HATCHSTYLE::HORIZONTAL;
528 else if( hatchstyleraw == wxT( "Vertical" ) ) hatchstyle = ALTIUM_POLYGON_HATCHSTYLE::VERTICAL;
529 else if( hatchstyleraw == wxT( "None" ) ) hatchstyle = ALTIUM_POLYGON_HATCHSTYLE::NONE;
531
532 altium_parse_polygons( properties, vertices );
533
534 if( aReader.HasParsingError() )
535 THROW_IO_ERROR( wxT( "Polygons6 stream was not parsed correctly" ) );
536}
537
539{
540 aReader.Skip( 2 );
541
542 std::map<wxString, wxString> props = aReader.ReadProperties();
543
544 if( props.empty() )
545 THROW_IO_ERROR( wxT( "Rules6 stream has no props" ) );
546
547 name = ALTIUM_PROPS_UTILS::ReadString( props, wxT( "NAME" ), wxT( "" ) );
548 priority = ALTIUM_PROPS_UTILS::ReadInt( props, wxT( "PRIORITY" ), 1 );
549
550 scope1expr = ALTIUM_PROPS_UTILS::ReadString( props, wxT( "SCOPE1EXPRESSION" ), wxT( "" ) );
551 scope2expr = ALTIUM_PROPS_UTILS::ReadString( props, wxT( "SCOPE2EXPRESSION" ), wxT( "" ) );
552
553 wxString rulekind = ALTIUM_PROPS_UTILS::ReadString( props, wxT( "RULEKIND" ), wxT( "" ) );
554 if( rulekind == wxT( "Clearance" ) )
555 {
557 clearanceGap = ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "GAP" ), wxT( "10mil" ) );
558 }
559 else if( rulekind == wxT( "DiffPairsRouting" ) )
560 {
562 }
563 else if( rulekind == wxT( "Height" ) )
564 {
566 }
567 else if( rulekind == wxT( "HoleSize" ) )
568 {
570 minLimit = ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "MINLIMIT" ), wxT( "1mil" ) );
571 maxLimit = ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "MAXLIMIT" ), wxT( "150mil" ) );
572 }
573 else if( rulekind == wxT( "HoleToHoleClearance" ) )
574 {
576 clearanceGap = ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "GAP" ), wxT( "10mil" ) );
577 }
578 else if( rulekind == wxT( "RoutingVias" ) )
579 {
581 width = ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "WIDTH" ), wxT( "20mil" ) );
582 minWidth = ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "MINWIDTH" ), wxT( "20mil" ) );
583 maxWidth = ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "MAXWIDTH" ), wxT( "50mil" ) );
584 holeWidth = ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "HOLEWIDTH" ), wxT( "10mil" ) );
585 minHoleWidth = ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "MINHOLEWIDTH" ), wxT( "10mil" ) );
586 maxHoleWidth = ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "MAXHOLEWIDTH" ), wxT( "28mil" ) );
587 }
588 else if( rulekind == wxT( "Width" ) )
589 {
591 minLimit = ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "MINLIMIT" ), wxT( "6mil" ) );
592 maxLimit = ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "MAXLIMIT" ), wxT( "40mil" ) );
593 preferredWidth = ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "PREFERREDWIDTH" ), wxT( "6mil" ) );
594}
595 else if( rulekind == wxT( "PasteMaskExpansion" ) )
596 {
598 pastemaskExpansion = ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "EXPANSION" ), wxT( "0" ) );
599 }
600 else if( rulekind == wxT( "SolderMaskExpansion" ) )
601 {
603 soldermaskExpansion = ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "EXPANSION" ), wxT( "4mil" ) );
604 }
605 else if( rulekind == wxT( "PlaneClearance" ) )
606 {
608 planeclearanceClearance = ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "CLEARANCE" ), wxT( "10mil" ) );
609 }
610 else if( rulekind == wxT( "PolygonConnect" ) )
611 {
613 polygonconnectAirgapwidth = ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "AIRGAPWIDTH" ), wxT( "10mil" ) );
614 polygonconnectReliefconductorwidth = ALTIUM_PROPS_UTILS::ReadKicadUnit( props, wxT( "RELIEFCONDUCTORWIDTH" ), wxT( "10mil" ) );
615 polygonconnectReliefentries = ALTIUM_PROPS_UTILS::ReadInt( props, wxT( "RELIEFENTRIES" ), 4 );
616
617 wxString style = ALTIUM_PROPS_UTILS::ReadString( props, wxT( "CONNECTSTYLE" ), wxT( "" ) );
618
619 if( style == wxT( "Direct" ) ) polygonconnectStyle = ALTIUM_CONNECT_STYLE::DIRECT;
620 else if( style == wxT( "Relief" ) ) polygonconnectStyle = ALTIUM_CONNECT_STYLE::RELIEF;
621 else if( style == wxT( "NoConnect" ) ) polygonconnectStyle = ALTIUM_CONNECT_STYLE::NONE;
623 }
624 else
625 {
627 }
628
629 if( aReader.HasParsingError() )
630 THROW_IO_ERROR( wxT( "Rules6 stream was not parsed correctly" ) );
631}
632
634{
635 ALTIUM_RECORD recordtype = static_cast<ALTIUM_RECORD>( aReader.Read<uint8_t>() );
636 if( recordtype != ALTIUM_RECORD::ARC )
637 {
638 THROW_IO_ERROR( wxT( "Arcs6 stream has invalid recordtype" ) );
639 }
640
641 // Subrecord 1
643
644 layer = static_cast<ALTIUM_LAYER>( aReader.Read<uint8_t>() );
645
646 uint8_t flags1 = aReader.Read<uint8_t>();
647 is_locked = ( flags1 & 0x04 ) == 0;
648 is_polygonoutline = ( flags1 & 0x02 ) != 0;
649
650 uint8_t flags2 = aReader.Read<uint8_t>();
651 is_keepout = flags2 == 2;
652
653 net = aReader.Read<uint16_t>();
654 polygon = aReader.Read<uint16_t>();
655 component = aReader.Read<uint16_t>();
656 aReader.Skip( 4 );
657 center = aReader.ReadVector2IPos();
658 radius = aReader.ReadKicadUnit();
659 startangle = aReader.Read<double>();
660 endangle = aReader.Read<double>();
661 width = aReader.ReadKicadUnit();
662 subpolyindex = aReader.Read<uint16_t>();
663
664 if( aReader.GetRemainingSubrecordBytes() >= 10 )
665 {
666 aReader.Skip( 9 );
667 keepoutrestrictions = aReader.Read<uint8_t>();
668 }
669 else
670 {
671 keepoutrestrictions = is_keepout ? 0x1F : 0;
672 }
673
674 aReader.SkipSubrecord();
675
676 if( aReader.HasParsingError() )
677 {
678 THROW_IO_ERROR( wxT( "Arcs6 stream was not parsed correctly" ) );
679 }
680}
681
683{
684 ALTIUM_RECORD recordtype = static_cast<ALTIUM_RECORD>( aReader.Read<uint8_t>() );
685
686 if( recordtype != ALTIUM_RECORD::MODEL )
687 THROW_IO_ERROR( wxT( "ComponentsBodies6 stream has invalid recordtype" ) );
688
690
691 aReader.Skip( 7 );
692 component = aReader.Read<uint16_t>();
693 aReader.Skip( 9 );
694
695 std::map<wxString, wxString> properties = aReader.ReadProperties();
696
697 if( properties.empty() )
698 THROW_IO_ERROR( wxT( "ComponentsBodies6 stream has no properties" ) );
699
700 modelName = ALTIUM_PROPS_UTILS::ReadString( properties, wxT( "MODEL.NAME" ), wxT( "" ) );
701 modelId = ALTIUM_PROPS_UTILS::ReadString( properties, wxT( "MODELID" ), wxT( "" ) );
702 modelIsEmbedded = ALTIUM_PROPS_UTILS::ReadBool( properties, wxT( "MODEL.EMBED" ), false );
703
704 modelPosition.x = ALTIUM_PROPS_UTILS::ReadKicadUnit( properties, wxT( "MODEL.2D.X" ), wxT( "0mil" ) );
705 modelPosition.y = -ALTIUM_PROPS_UTILS::ReadKicadUnit( properties, wxT( "MODEL.2D.Y" ), wxT( "0mil" ) );
706 modelPosition.z = ALTIUM_PROPS_UTILS::ReadKicadUnit( properties, wxT( "MODEL.3D.DZ" ), wxT( "0mil" ) );
707
708 modelRotation.x = ALTIUM_PROPS_UTILS::ReadDouble( properties, wxT( "MODEL.3D.ROTX" ), 0. );
709 modelRotation.y = ALTIUM_PROPS_UTILS::ReadDouble( properties, wxT( "MODEL.3D.ROTY" ), 0. );
710 modelRotation.z = ALTIUM_PROPS_UTILS::ReadDouble( properties, wxT( "MODEL.3D.ROTZ" ), 0. );
711
712 rotation = ALTIUM_PROPS_UTILS::ReadDouble( properties, wxT( "MODEL.2D.ROTATION" ), 0. );
713
714 body_opacity_3d = ALTIUM_PROPS_UTILS::ReadDouble( properties, wxT( "BODYOPACITY3D" ), 1. );
715 body_projection = ALTIUM_PROPS_UTILS::ReadInt( properties, wxT( "BODYPROJECTION" ), 0 );
716
717 aReader.SkipSubrecord();
718
719 if( aReader.HasParsingError() )
720 THROW_IO_ERROR( wxT( "Components6 stream was not parsed correctly" ) );
721}
722
724{
725 ALTIUM_RECORD recordtype = static_cast<ALTIUM_RECORD>( aReader.Read<uint8_t>() );
726
727 if( recordtype != ALTIUM_RECORD::PAD )
728 THROW_IO_ERROR( wxT( "Pads6 stream has invalid recordtype" ) );
729
730 // Subrecord 1
731 size_t subrecord1 = aReader.ReadAndSetSubrecordLength();
732
733 if( subrecord1 == 0 )
734 THROW_IO_ERROR( wxT( "Pads6 stream has no subrecord1 data" ) );
735
736 name = aReader.ReadWxString();
737
738 if( aReader.GetRemainingSubrecordBytes() != 0 )
739 THROW_IO_ERROR( wxT( "Pads6 stream has invalid subrecord1 length" ) );
740
741 aReader.SkipSubrecord();
742
743 // Subrecord 2
745 aReader.SkipSubrecord();
746
747 // Subrecord 3
749 aReader.SkipSubrecord();
750
751 // Subrecord 4
753 aReader.SkipSubrecord();
754
755 // Subrecord 5
756 size_t subrecord5 = aReader.ReadAndSetSubrecordLength();
757
758 ExpectSubrecordLengthAtLeast( "Pads6", "subrecord5", 110, subrecord5 );
759
760 layer = static_cast<ALTIUM_LAYER>( aReader.Read<uint8_t>() );
763
764 uint8_t flags1 = aReader.Read<uint8_t>();
765 is_test_fab_top = ( flags1 & 0x80 ) != 0;
766 is_tent_bottom = ( flags1 & 0x40 ) != 0;
767 is_tent_top = ( flags1 & 0x20 ) != 0;
768 is_locked = ( flags1 & 0x04 ) == 0;
769
770 uint8_t flags2 = aReader.Read<uint8_t>();
771 is_test_fab_bottom = ( flags2 & 0x01 ) != 0;
772
773 net = aReader.Read<uint16_t>();
774 aReader.Skip( 2 );
775 component = aReader.Read<uint16_t>();
776 aReader.Skip( 4 ); // to 13
777
778 position = aReader.ReadVector2IPos();
779 topsize = aReader.ReadVector2ISize();
780 midsize = aReader.ReadVector2ISize();
781 botsize = aReader.ReadVector2ISize();
782 holesize = aReader.ReadKicadUnit(); // to 49
783
784 topshape = static_cast<ALTIUM_PAD_SHAPE>( aReader.Read<uint8_t>() );
785 midshape = static_cast<ALTIUM_PAD_SHAPE>( aReader.Read<uint8_t>() );
786 botshape = static_cast<ALTIUM_PAD_SHAPE>( aReader.Read<uint8_t>() );
787
788 direction = aReader.Read<double>();
789 plated = aReader.Read<uint8_t>() != 0;
790 aReader.Skip( 1 );
791 padmode = static_cast<ALTIUM_PAD_MODE>( aReader.Read<uint8_t>() );
792 aReader.Skip( 23 );
795 aReader.Skip( 7 );
796 pastemaskexpansionmode = static_cast<ALTIUM_MODE>( aReader.Read<uint8_t>() );
797 soldermaskexpansionmode = static_cast<ALTIUM_MODE>( aReader.Read<uint8_t>() );
798 aReader.Skip( 3 ); // to 106
799
802
803 if( subrecord5 == 110 )
804 {
805 // Don't know exactly what this is, but it's always been 0 in the files with 110-byte subrecord5.
806 // e.g. https://gitlab.com/kicad/code/kicad/-/issues/16514
807 const uint32_t unknown = aReader.ReadKicadUnit(); // to 110
808
809 if( unknown != 0 )
810 {
811 THROW_IO_ERROR( wxString::Format( "Pads6 stream subrecord5 + 106 has value %d, which is unexpected",
812 unknown ) );
813 }
814 holerotation = 0;
815 }
816 else
817 {
818 // More than 110, need at least 114
819 ExpectSubrecordLengthAtLeast( "Pads6", "subrecord5", 114, subrecord5 );
820 holerotation = aReader.Read<double>(); // to 114
821 }
822
823 if( subrecord5 >= 120 )
824 {
825 tolayer = static_cast<ALTIUM_LAYER>( aReader.Read<uint8_t>() );
826 aReader.Skip( 2 );
827 fromlayer = static_cast<ALTIUM_LAYER>( aReader.Read<uint8_t>() );
828 //aReader.skip( 2 );
829 }
830 else if( subrecord5 == 171 )
831 {
832 }
833
834 if( subrecord5 >= 202 )
835 {
836 aReader.Skip( 40 );
838 aReader.Skip( 32 );
839 pad_to_die_delay = KiROUND( aReader.Read<double>() * 1e18 );
840 }
841
842 aReader.SkipSubrecord();
843
844 // Subrecord 6
845 size_t subrecord6 = aReader.ReadAndSetSubrecordLength();
846 // Known lengths: 596, 628, 651
847 // 596 is the number of bytes read in this code-block
848 if( subrecord6 >= 596 )
849 {
850 sizeAndShape = std::make_unique<APAD6_SIZE_AND_SHAPE>();
851
852 for( wxSize& size : sizeAndShape->inner_size )
853 size.x = aReader.ReadKicadUnitX();
854
855 for( wxSize& size : sizeAndShape->inner_size )
856 size.y = aReader.ReadKicadUnitY();
857
858 for( ALTIUM_PAD_SHAPE& shape : sizeAndShape->inner_shape )
859 shape = static_cast<ALTIUM_PAD_SHAPE>( aReader.Read<uint8_t>() );
860
861 aReader.Skip( 1 );
862
863 sizeAndShape->holeshape = static_cast<ALTIUM_PAD_HOLE_SHAPE>( aReader.Read<uint8_t>() );
864 sizeAndShape->slotsize = aReader.ReadKicadUnit();
865 sizeAndShape->slotrotation = aReader.Read<double>();
866
867 for( VECTOR2I& pt : sizeAndShape->holeoffset )
868 pt.x = aReader.ReadKicadUnitX();
869
870 for( VECTOR2I& pt : sizeAndShape->holeoffset )
871 pt.y = aReader.ReadKicadUnitY();
872
873 aReader.Skip( 1 );
874
875 for( ALTIUM_PAD_SHAPE_ALT& shape : sizeAndShape->alt_shape )
876 shape = static_cast<ALTIUM_PAD_SHAPE_ALT>( aReader.Read<uint8_t>() );
877
878 for( uint8_t& radius : sizeAndShape->cornerradius )
879 radius = aReader.Read<uint8_t>();
880 }
881 else if( subrecord6 != 0 )
882 {
883 wxLogError( _( "Pads6 stream has unexpected length for subrecord 6: %d." ), subrecord6 );
884 }
885
886 aReader.SkipSubrecord();
887
888 if( aReader.HasParsingError() )
889 THROW_IO_ERROR( wxT( "Pads6 stream was not parsed correctly" ) );
890}
891
893{
894 ALTIUM_RECORD recordtype = static_cast<ALTIUM_RECORD>( aReader.Read<uint8_t>() );
895
896 if( recordtype != ALTIUM_RECORD::VIA )
897 THROW_IO_ERROR( wxT( "Vias6 stream has invalid recordtype" ) );
898
899 // Subrecord 1
900 size_t subrecord1 = aReader.ReadAndSetSubrecordLength();
901
902 aReader.Skip( 1 );
903 uint8_t flags1 = aReader.Read<uint8_t>();
904 is_test_fab_top = ( flags1 & 0x80 ) != 0;
905 is_tent_bottom = ( flags1 & 0x40 ) != 0;
906 is_tent_top = ( flags1 & 0x20 ) != 0;
907 is_locked = ( flags1 & 0x04 ) == 0;
908
909 uint8_t flags2 = aReader.Read<uint8_t>();
910 is_test_fab_bottom = ( flags2 & 0x01 ) != 0;
911
912 net = aReader.Read<uint16_t>();
913 aReader.Skip( 8 );
914 position = aReader.ReadVector2IPos();
915 diameter = aReader.ReadKicadUnit();
916 holesize = aReader.ReadKicadUnit();
917
918 layer_start = static_cast<ALTIUM_LAYER>( aReader.Read<uint8_t>() );
919 layer_end = static_cast<ALTIUM_LAYER>( aReader.Read<uint8_t>() );
920
921 if( subrecord1 <= 74 )
922 {
924 }
925 else
926 {
927 uint8_t temp_byte = aReader.Read<uint8_t>(); // Unknown.
928
930 thermal_relief_conductorcount = aReader.Read<uint8_t>();
931 aReader.Skip( 1 ); // Unknown.
932
934
935 aReader.ReadKicadUnit(); // Unknown. 20mil?
936 aReader.ReadKicadUnit(); // Unknown. 20mil?
937
938 aReader.Skip( 4 );
940 aReader.Skip( 8 );
941
942 temp_byte = aReader.Read<uint8_t>();
943 soldermask_expansion_manual = temp_byte & 0x02;
944
945 aReader.Skip( 7 );
946
947 viamode = static_cast<ALTIUM_PAD_MODE>( aReader.Read<uint8_t>() );
948
949 for( int ii = 0; ii < 32; ++ii )
950 {
951 diameter_by_layer[ii] = aReader.ReadKicadUnit();
952 }
953 }
954
955 if( subrecord1 >= 246 )
956 {
957 aReader.Skip( 38 );
958 soldermask_expansion_linked = aReader.Read<uint8_t>() & 0x01;
960 }
961
962 if( subrecord1 >= 307 )
963 {
964 aReader.Skip( 45 );
965
966 pos_tolerance = aReader.ReadKicadUnit();
967 neg_tolerance = aReader.ReadKicadUnit();
968 }
969
970 aReader.SkipSubrecord();
971
972 if( aReader.HasParsingError() )
973 THROW_IO_ERROR( wxT( "Vias6 stream was not parsed correctly" ) );
974}
975
977{
978 ALTIUM_RECORD recordtype = static_cast<ALTIUM_RECORD>( aReader.Read<uint8_t>() );
979
980 if( recordtype != ALTIUM_RECORD::TRACK )
981 THROW_IO_ERROR( wxT( "Tracks6 stream has invalid recordtype" ) );
982
983 // Subrecord 1
985
986 layer = static_cast<ALTIUM_LAYER>( aReader.Read<uint8_t>() );
987
988 uint8_t flags1 = aReader.Read<uint8_t>();
989 is_locked = ( flags1 & 0x04 ) == 0;
990 is_polygonoutline = ( flags1 & 0x02 ) != 0;
991
992 uint8_t flags2 = aReader.Read<uint8_t>();
993 is_keepout = flags2 == 2;
994
995 net = aReader.Read<uint16_t>();
996 polygon = aReader.Read<uint16_t>();
997 component = aReader.Read<uint16_t>();
998 aReader.Skip( 4 );
999 start = aReader.ReadVector2IPos();
1000 end = aReader.ReadVector2IPos();
1001 width = aReader.ReadKicadUnit();
1002 subpolyindex = aReader.Read<uint16_t>();
1003
1004 if( aReader.GetRemainingSubrecordBytes() >= 11 )
1005 {
1006 aReader.Skip( 10 );
1007 keepoutrestrictions = aReader.Read<uint8_t>();
1008 }
1009 else
1010 {
1011 keepoutrestrictions = is_keepout ? 0x1F : 0;
1012 }
1013
1014 aReader.SkipSubrecord();
1015
1016 if( aReader.HasParsingError() )
1017 THROW_IO_ERROR( wxT( "Tracks6 stream was not parsed correctly" ) );
1018}
1019
1020ATEXT6::ATEXT6( ALTIUM_BINARY_PARSER& aReader, std::map<uint32_t, wxString>& aStringTable )
1021{
1022 ALTIUM_RECORD recordtype = static_cast<ALTIUM_RECORD>( aReader.Read<uint8_t>() );
1023
1024 if( recordtype != ALTIUM_RECORD::TEXT )
1025 THROW_IO_ERROR( wxT( "Texts6 stream has invalid recordtype" ) );
1026
1027 // Subrecord 1 - Properties
1028 size_t subrecord1 = aReader.ReadAndSetSubrecordLength();
1029
1030 layer = static_cast<ALTIUM_LAYER>( aReader.Read<uint8_t>() );
1031 aReader.Skip( 6 );
1032 component = aReader.Read<uint16_t>();
1033 aReader.Skip( 4 );
1034 position = aReader.ReadVector2IPos();
1035 height = aReader.ReadKicadUnit();
1036 strokefonttype = static_cast<STROKE_FONT_TYPE>( aReader.Read<uint16_t>() );
1037 // TODO: The Serif font type doesn't match well with KiCad, we should replace it with a better match
1038
1039 rotation = aReader.Read<double>();
1040 isMirrored = aReader.Read<uint8_t>() != 0;
1041 strokewidth = aReader.ReadKicadUnit();
1042
1043 if( subrecord1 < 123 )
1044 {
1046 aReader.SkipSubrecord();
1047 return;
1048 }
1049
1050 isComment = aReader.Read<uint8_t>() != 0;
1051 isDesignator = aReader.Read<uint8_t>() != 0;
1052 aReader.Skip( 1 );
1053 fonttype = static_cast<ALTIUM_TEXT_TYPE>( aReader.Read<uint8_t>() );
1054 isBold = aReader.Read<uint8_t>() != 0;
1055 isItalic = aReader.Read<uint8_t>() != 0;
1056
1057 char fontData[64] = { 0 };
1058 aReader.ReadBytes( fontData, sizeof( fontData ) );
1059 fontname = wxString( fontData, wxMBConvUTF16LE(), sizeof( fontData ) ).BeforeFirst( '\0' );
1060
1061 char tmpbyte = aReader.Read<uint8_t>();
1062 isInverted = !!tmpbyte;
1063 margin_border_width = aReader.ReadKicadUnit(); // "Margin Border"
1064 widestring_index = aReader.Read<uint32_t>();
1065 aReader.Skip( 4 );
1066
1067 // An inverted rect in Altium is like a text box with the text inverted.
1068 isInvertedRect = aReader.Read<uint8_t>() != 0;
1069
1072 textbox_rect_justification = static_cast<ALTIUM_TEXT_POSITION>( aReader.Read<uint8_t>() );
1073 text_offset_width = aReader.ReadKicadUnit(); // "Text Offset"
1074
1075 int remaining = aReader.GetRemainingSubrecordBytes();
1076
1077 if( remaining >= 103 )
1078 {
1079 VECTOR2I unk_vec = aReader.ReadVector2ISize();
1080 wxLogTrace( traceAltiumImport, " Unk vec: %d, %d\n", unk_vec.x, unk_vec.y );
1081
1082 barcode_margin = aReader.ReadVector2ISize();
1083
1084 int32_t unk32 = aReader.ReadKicadUnit();
1085 wxLogTrace( traceAltiumImport, " Unk32: %d\n", unk32 );
1086
1087 barcode_type = static_cast<ALTIUM_BARCODE_TYPE>( aReader.Read<uint8_t>() );
1088 uint8_t unk8 = aReader.Read<uint8_t>();
1089 wxLogTrace( traceAltiumImport, " Unk8: %u\n", unk8 );
1090
1091 barcode_inverted = aReader.Read<uint8_t>() != 0;
1092 fonttype = static_cast<ALTIUM_TEXT_TYPE>( aReader.Read<uint8_t>() );
1093
1094 aReader.ReadBytes( fontData, sizeof( fontData ) );
1095 barcode_fontname = wxString( fontData, wxMBConvUTF16LE(), sizeof( fontData ) ).BeforeFirst( '\0' );
1096
1097 for( size_t ii = 0; ii < 5; ++ii )
1098 {
1099 uint8_t temp = aReader.Peek<uint8_t>();
1100 uint32_t temp32 = ii < 1 ? ALTIUM_PROPS_UTILS::ConvertToKicadUnit( aReader.Peek<uint32_t>() ) : 0;
1101 wxLogTrace( traceAltiumImport, "2ATEXT6 %zu:\t Byte:%u, Kicad:%u\n", ii, temp, temp32 );
1102 aReader.Skip( 1 );
1103 }
1104
1105 // "Frame" text type flag
1106 isFrame = aReader.Read<uint8_t>() != 0;
1107
1108 // Use "Offset" border value instead of "Margin"
1109 isOffsetBorder = aReader.Read<uint8_t>() != 0;
1110
1111 for( size_t ii = 0; ii < 8; ++ii )
1112 {
1113 uint8_t temp = aReader.Peek<uint8_t>();
1114 uint32_t temp32 = ii < 3 ? ALTIUM_PROPS_UTILS::ConvertToKicadUnit( aReader.Peek<uint32_t>() ) : 0;
1115 wxLogTrace( traceAltiumImport, "3ATEXT6 %zu:\t Byte:%u, Kicad:%u\n", ii, temp, temp32 );
1116 aReader.Skip( 1 );
1117 }
1118 }
1119 else
1120 {
1122 isOffsetBorder = false;
1123 }
1124
1125 if( remaining >= 115 )
1126 {
1127 // textbox_rect_justification will be wrong (5) when this flag is unset,
1128 // in that case, we should always use the left bottom justification.
1129 isJustificationValid = aReader.Read<uint8_t>() != 0;
1130 }
1131 else
1132 {
1133 isJustificationValid = false;
1134 }
1135
1136 aReader.SkipSubrecord();
1137
1138 // Subrecord 2 - Legacy 8bit string, max 255 chars, unknown codepage
1139 aReader.ReadAndSetSubrecordLength();
1140
1141 auto entry = aStringTable.find( widestring_index );
1142
1143 if( entry != aStringTable.end() )
1144 text = entry->second;
1145 else
1146 text = aReader.ReadWxString();
1147
1148 // Normalize Windows line endings
1149 text.Replace( wxT( "\r\n" ), wxT( "\n" ) );
1150
1151 aReader.SkipSubrecord();
1152
1153 // Altium only supports inverting truetype fonts
1155 {
1156 isInverted = false;
1157 isInvertedRect = false;
1158 }
1159
1160 if( aReader.HasParsingError() )
1161 THROW_IO_ERROR( wxT( "Texts6 stream was not parsed correctly" ) );
1162}
1163
1165{
1166 ALTIUM_RECORD recordtype = static_cast<ALTIUM_RECORD>( aReader.Read<uint8_t>() );
1167
1168 if( recordtype != ALTIUM_RECORD::FILL )
1169 THROW_IO_ERROR( wxT( "Fills6 stream has invalid recordtype" ) );
1170
1171 // Subrecord 1
1172 aReader.ReadAndSetSubrecordLength();
1173
1174 layer = static_cast<ALTIUM_LAYER>( aReader.Read<uint8_t>() );
1175
1176 uint8_t flags1 = aReader.Read<uint8_t>();
1177 is_locked = ( flags1 & 0x04 ) == 0;
1178
1179 uint8_t flags2 = aReader.Read<uint8_t>();
1180 is_keepout = flags2 == 2;
1181
1182 net = aReader.Read<uint16_t>();
1183 aReader.Skip( 2 );
1184 component = aReader.Read<uint16_t>();
1185 aReader.Skip( 4 );
1186 pos1 = aReader.ReadVector2IPos();
1187 pos2 = aReader.ReadVector2IPos();
1188 rotation = aReader.Read<double>();
1189
1190 if( aReader.GetRemainingSubrecordBytes() >= 10 )
1191 {
1192 aReader.Skip( 9 );
1193 keepoutrestrictions = aReader.Read<uint8_t>();
1194 }
1195 else
1196 {
1197 keepoutrestrictions = is_keepout ? 0x1F : 0;
1198 }
1199
1200 aReader.SkipSubrecord();
1201
1202 if( aReader.HasParsingError() )
1203 THROW_IO_ERROR( wxT( "Fills6 stream was not parsed correctly" ) );
1204}
1205
1206AREGION6::AREGION6( ALTIUM_BINARY_PARSER& aReader, bool aExtendedVertices )
1207{
1208 ALTIUM_RECORD recordtype = static_cast<ALTIUM_RECORD>( aReader.Read<uint8_t>() );
1209
1210 if( recordtype != ALTIUM_RECORD::REGION )
1211 THROW_IO_ERROR( wxT( "Regions6 stream has invalid recordtype" ) );
1212
1213 // Subrecord 1
1214 aReader.ReadAndSetSubrecordLength();
1215
1216 layer = static_cast<ALTIUM_LAYER>( aReader.Read<uint8_t>() );
1217
1218 uint8_t flags1 = aReader.Read<uint8_t>();
1219 is_locked = ( flags1 & 0x04 ) == 0;
1220 is_teardrop = ( flags1 & 0x10 ) != 0;
1221
1222 uint8_t flags2 = aReader.Read<uint8_t>();
1223 is_keepout = flags2 == 2;
1224
1225 net = aReader.Read<uint16_t>();
1226 polygon = aReader.Read<uint16_t>();
1227 component = aReader.Read<uint16_t>();
1228 aReader.Skip( 5 );
1229 holecount = aReader.Read<uint16_t>();
1230 aReader.Skip( 2 );
1231
1232 std::map<wxString, wxString> properties = aReader.ReadProperties();
1233
1234 if( properties.empty() )
1235 THROW_IO_ERROR( wxT( "Regions6 stream has empty properties" ) );
1236
1237 int pkind = ALTIUM_PROPS_UTILS::ReadInt( properties, wxT( "KIND" ), 0 );
1238 bool is_cutout = ALTIUM_PROPS_UTILS::ReadBool( properties, wxT( "ISBOARDCUTOUT" ), false );
1239
1240 is_shapebased = ALTIUM_PROPS_UTILS::ReadBool( properties, wxT( "ISSHAPEBASED" ), false );
1241 keepoutrestrictions = static_cast<uint8_t>(
1242 ALTIUM_PROPS_UTILS::ReadInt( properties, wxT( "KEEPOUTRESTRIC" ), 0x1F ) );
1243
1244 // TODO: this can differ from the other subpolyindex?!
1245 // Note: "the other subpolyindex" is "polygon"
1246 subpolyindex = static_cast<uint16_t>(
1247 ALTIUM_PROPS_UTILS::ReadInt( properties, "SUBPOLYINDEX", ALTIUM_POLYGON_NONE ) );
1248
1249 switch( pkind )
1250 {
1251 case 0:
1252 if( is_cutout )
1253 {
1255 }
1256 else
1257 {
1259 }
1260
1261 break;
1262
1263 case 1:
1265 break;
1266
1267 case 2:
1269 break;
1270
1271 case 3:
1272 kind = ALTIUM_REGION_KIND::UNKNOWN_3; // TODO: what kind is this?
1273 break;
1274
1275 case 4:
1277 break;
1278
1279 default:
1281 break;
1282 }
1283
1284 uint32_t num_outline_vertices = aReader.Read<uint32_t>();
1285
1286 if( aExtendedVertices )
1287 num_outline_vertices++; // Has a closing vertex
1288
1289 for( uint32_t i = 0; i < num_outline_vertices; i++ )
1290 {
1291 if( aExtendedVertices )
1292 {
1293 bool isRound = aReader.Read<uint8_t>() != 0;
1294 VECTOR2I position = aReader.ReadVector2IPos();
1295 VECTOR2I center = aReader.ReadVector2IPos();
1296 int32_t radius = aReader.ReadKicadUnit();
1297 double angle1 = aReader.Read<double>();
1298 double angle2 = aReader.Read<double>();
1299 outline.emplace_back( isRound, radius, angle1, angle2, position, center );
1300 }
1301 else
1302 {
1303 // For some regions the coordinates are stored as double and not as int32_t
1304 int32_t x = ALTIUM_PROPS_UTILS::ConvertToKicadUnit( aReader.Read<double>() );
1305 int32_t y = ALTIUM_PROPS_UTILS::ConvertToKicadUnit( -aReader.Read<double>() );
1306 outline.emplace_back( VECTOR2I( x, y ) );
1307 }
1308 }
1309
1310 holes.resize( holecount );
1311 for( uint16_t k = 0; k < holecount; k++ )
1312 {
1313 uint32_t num_hole_vertices = aReader.Read<uint32_t>();
1314 holes.at( k ).reserve( num_hole_vertices );
1315
1316 for( uint32_t i = 0; i < num_hole_vertices; i++ )
1317 {
1318 int32_t x = ALTIUM_PROPS_UTILS::ConvertToKicadUnit( aReader.Read<double>() );
1319 int32_t y = ALTIUM_PROPS_UTILS::ConvertToKicadUnit( -aReader.Read<double>() );
1320 holes.at( k ).emplace_back( VECTOR2I( x, y ) );
1321 }
1322 }
1323
1324 aReader.SkipSubrecord();
1325
1326 if( aReader.HasParsingError() )
1327 THROW_IO_ERROR( wxT( "Regions6 stream was not parsed correctly" ) );
1328}
void altium_parse_polygons(std::map< wxString, wxString > &aProps, std::vector< ALTIUM_VERTICE > &aVertices)
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)
static ALTIUM_MODE ReadAltiumModeFromProperties(const std::map< wxString, wxString > &aProps, wxString aKey)
static ALTIUM_RECORD ReadAltiumRecordFromProperties(const std::map< wxString, wxString > &aProps, wxString aKey)
ALTIUM_PAD_SHAPE_ALT
ALTIUM_TEXT_POSITION
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
bool is_polygonoutline
double endangle
uint16_t subpolyindex
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
std::vector< VECTOR2I > textPoint
ALTIUM_DIMENSION_KIND kind
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)
uint16_t component
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 tolayer
ALTIUM_PAD_SHAPE topshape
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
int32_t minprimlength
std::vector< ALTIUM_VERTICE > vertices
APOLYGON6(ALTIUM_BINARY_PARSER &aReader)
ALTIUM_POLYGON_HATCHSTYLE hatchstyle
ALTIUM_LAYER layer
uint8_t keepoutrestrictions
ALTIUM_LAYER layer
AREGION6(ALTIUM_BINARY_PARSER &aReader, bool aExtendedVertices)
uint16_t holecount
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
wxString scope2expr
int soldermaskExpansion
ARULE6(ALTIUM_BINARY_PARSER &aReader)
int32_t polygonconnectAirgapwidth
int polygonconnectReliefentries
uint32_t text_offset_width
VECTOR2I barcode_margin
uint32_t textbox_rect_height
uint16_t component
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_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
ATRACK6(ALTIUM_BINARY_PARSER &aReader)
uint32_t width
bool is_polygonoutline
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:695