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