/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2020 Thomas Pointhuber * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, you may find one here: * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * or you may search the http://www.gnu.org website for the version 2 license, * or you may write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include #include #include #include #include "altium_parser.h" #include "altium_parser_pcb.h" ALTIUM_LAYER altium_layer_from_name( const wxString& aName ) { static const std::unordered_map hash_map = { { "TOP", ALTIUM_LAYER::TOP_LAYER }, { "MID1", ALTIUM_LAYER::MID_LAYER_1 }, { "MID2", ALTIUM_LAYER::MID_LAYER_2 }, { "MID3", ALTIUM_LAYER::MID_LAYER_3 }, { "MID4", ALTIUM_LAYER::MID_LAYER_4 }, { "MID5", ALTIUM_LAYER::MID_LAYER_5 }, { "MID6", ALTIUM_LAYER::MID_LAYER_6 }, { "MID7", ALTIUM_LAYER::MID_LAYER_7 }, { "MID8", ALTIUM_LAYER::MID_LAYER_8 }, { "MID9", ALTIUM_LAYER::MID_LAYER_9 }, { "MID10", ALTIUM_LAYER::MID_LAYER_10 }, { "MID11", ALTIUM_LAYER::MID_LAYER_11 }, { "MID12", ALTIUM_LAYER::MID_LAYER_12 }, { "MID13", ALTIUM_LAYER::MID_LAYER_13 }, { "MID14", ALTIUM_LAYER::MID_LAYER_14 }, { "MID15", ALTIUM_LAYER::MID_LAYER_15 }, { "MID16", ALTIUM_LAYER::MID_LAYER_16 }, { "MID17", ALTIUM_LAYER::MID_LAYER_17 }, { "MID18", ALTIUM_LAYER::MID_LAYER_18 }, { "MID19", ALTIUM_LAYER::MID_LAYER_19 }, { "MID20", ALTIUM_LAYER::MID_LAYER_20 }, { "MID21", ALTIUM_LAYER::MID_LAYER_21 }, { "MID22", ALTIUM_LAYER::MID_LAYER_22 }, { "MID23", ALTIUM_LAYER::MID_LAYER_23 }, { "MID24", ALTIUM_LAYER::MID_LAYER_24 }, { "MID25", ALTIUM_LAYER::MID_LAYER_25 }, { "MID26", ALTIUM_LAYER::MID_LAYER_26 }, { "MID27", ALTIUM_LAYER::MID_LAYER_27 }, { "MID28", ALTIUM_LAYER::MID_LAYER_28 }, { "MID29", ALTIUM_LAYER::MID_LAYER_29 }, { "MID30", ALTIUM_LAYER::MID_LAYER_30 }, { "BOTTOM", ALTIUM_LAYER::BOTTOM_LAYER }, { "TOPOVERLAY", ALTIUM_LAYER::TOP_OVERLAY }, { "BOTTOMOVERLAY", ALTIUM_LAYER::BOTTOM_OVERLAY }, { "TOPPASTE", ALTIUM_LAYER::TOP_PASTE }, { "BOTTOMPASTE", ALTIUM_LAYER::BOTTOM_PASTE }, { "TOPSOLDER", ALTIUM_LAYER::TOP_SOLDER }, { "BOTTOMSOLDER", ALTIUM_LAYER::BOTTOM_SOLDER }, { "PLANE1", ALTIUM_LAYER::INTERNAL_PLANE_1 }, { "PLANE2", ALTIUM_LAYER::INTERNAL_PLANE_2 }, { "PLANE3", ALTIUM_LAYER::INTERNAL_PLANE_3 }, { "PLANE4", ALTIUM_LAYER::INTERNAL_PLANE_4 }, { "PLANE5", ALTIUM_LAYER::INTERNAL_PLANE_5 }, { "PLANE6", ALTIUM_LAYER::INTERNAL_PLANE_6 }, { "PLANE7", ALTIUM_LAYER::INTERNAL_PLANE_7 }, { "PLANE8", ALTIUM_LAYER::INTERNAL_PLANE_8 }, { "PLANE9", ALTIUM_LAYER::INTERNAL_PLANE_9 }, { "PLANE10", ALTIUM_LAYER::INTERNAL_PLANE_10 }, { "PLANE11", ALTIUM_LAYER::INTERNAL_PLANE_11 }, { "PLANE12", ALTIUM_LAYER::INTERNAL_PLANE_12 }, { "PLANE13", ALTIUM_LAYER::INTERNAL_PLANE_13 }, { "PLANE14", ALTIUM_LAYER::INTERNAL_PLANE_14 }, { "PLANE15", ALTIUM_LAYER::INTERNAL_PLANE_15 }, { "PLANE16", ALTIUM_LAYER::INTERNAL_PLANE_16 }, { "DRILLGUIDE", ALTIUM_LAYER::DRILL_GUIDE }, { "KEEPOUT", ALTIUM_LAYER::KEEP_OUT_LAYER }, { "MECHANICAL1", ALTIUM_LAYER::MECHANICAL_1 }, { "MECHANICAL2", ALTIUM_LAYER::MECHANICAL_2 }, { "MECHANICAL3", ALTIUM_LAYER::MECHANICAL_3 }, { "MECHANICAL4", ALTIUM_LAYER::MECHANICAL_4 }, { "MECHANICAL5", ALTIUM_LAYER::MECHANICAL_5 }, { "MECHANICAL6", ALTIUM_LAYER::MECHANICAL_6 }, { "MECHANICAL7", ALTIUM_LAYER::MECHANICAL_7 }, { "MECHANICAL8", ALTIUM_LAYER::MECHANICAL_8 }, { "MECHANICAL9", ALTIUM_LAYER::MECHANICAL_9 }, { "MECHANICAL10", ALTIUM_LAYER::MECHANICAL_10 }, { "MECHANICAL11", ALTIUM_LAYER::MECHANICAL_11 }, { "MECHANICAL12", ALTIUM_LAYER::MECHANICAL_12 }, { "MECHANICAL13", ALTIUM_LAYER::MECHANICAL_13 }, { "MECHANICAL14", ALTIUM_LAYER::MECHANICAL_14 }, { "MECHANICAL15", ALTIUM_LAYER::MECHANICAL_15 }, { "MECHANICAL16", ALTIUM_LAYER::MECHANICAL_16 }, { "DRILLDRAWING", ALTIUM_LAYER::DRILL_DRAWING }, { "MULTILAYER", ALTIUM_LAYER::MULTI_LAYER }, // FIXME: the following mapping is just a guess { "CONNECTIONS", ALTIUM_LAYER::CONNECTIONS }, { "BACKGROUND", ALTIUM_LAYER::BACKGROUND }, { "DRCERRORMARKERS", ALTIUM_LAYER::DRC_ERROR_MARKERS }, { "SELECTIONS", ALTIUM_LAYER::SELECTIONS }, { "VISIBLEGRID1", ALTIUM_LAYER::VISIBLE_GRID_1 }, { "VISIBLEGRID2", ALTIUM_LAYER::VISIBLE_GRID_2 }, { "PADHOLES", ALTIUM_LAYER::PAD_HOLES }, { "VIAHOLES", ALTIUM_LAYER::VIA_HOLES }, }; auto it = hash_map.find( std::string( aName.c_str() ) ); if( it == hash_map.end() ) { wxLogError( wxString::Format( "Unknown mapping of the Altium layer '%s'. Please report as issue.", aName ) ); return ALTIUM_LAYER::UNKNOWN; } else { return it->second; } } void altium_parse_polygons( std::map& aProperties, std::vector& aVertices ) { for( size_t i = 0; i < std::numeric_limits::max(); i++ ) { const wxString si = std::to_string( i ); const wxString vxi = "VX" + si; const wxString vyi = "VY" + si; if( aProperties.find( vxi ) == aProperties.end() || aProperties.find( vyi ) == aProperties.end() ) { break; // it doesn't seem like we know beforehand how many vertices are inside a polygon } const bool isRound = ALTIUM_PARSER::PropertiesReadInt( aProperties, "KIND" + si, 0 ) != 0; const int32_t radius = ALTIUM_PARSER::PropertiesReadKicadUnit( aProperties, "R" + si, "0mil" ); const double sa = ALTIUM_PARSER::PropertiesReadDouble( aProperties, "SA" + si, 0. ); const double ea = ALTIUM_PARSER::PropertiesReadDouble( aProperties, "EA" + si, 0. ); const wxPoint vp = wxPoint( ALTIUM_PARSER::PropertiesReadKicadUnit( aProperties, vxi, "0mil" ), -ALTIUM_PARSER::PropertiesReadKicadUnit( aProperties, vyi, "0mil" ) ); const wxPoint cp = wxPoint( ALTIUM_PARSER::PropertiesReadKicadUnit( aProperties, "CX" + si, "0mil" ), -ALTIUM_PARSER::PropertiesReadKicadUnit( aProperties, "CY" + si, "0mil" ) ); aVertices.emplace_back( isRound, radius, sa, ea, vp, cp ); } } ABOARD6::ABOARD6( ALTIUM_PARSER& aReader ) { std::map properties = aReader.ReadProperties(); if( properties.empty() ) { THROW_IO_ERROR( "Board6 stream has no properties!" ); } /*for (auto & property : properties) { std::cout << " * '" << property.first << "' = '" << property.second << "'" << std::endl; }*/ sheetpos = wxPoint( ALTIUM_PARSER::PropertiesReadKicadUnit( properties, "SHEETX", "0mil" ), -ALTIUM_PARSER::PropertiesReadKicadUnit( properties, "SHEETY", "0mil" ) ); sheetsize = wxSize( ALTIUM_PARSER::PropertiesReadKicadUnit( properties, "SHEETWIDTH", "0mil" ), ALTIUM_PARSER::PropertiesReadKicadUnit( properties, "SHEETHEIGHT", "0mil" ) ); layercount = ALTIUM_PARSER::PropertiesReadInt( properties, "LAYERSETSCOUNT", 1 ) + 1; for( size_t i = 1; i < std::numeric_limits::max(); i++ ) { const wxString layeri = "LAYER" + std::to_string( i ); const wxString layername = layeri + "NAME"; auto layernameit = properties.find( layername ); if( layernameit == properties.end() ) { break; // it doesn't seem like we know beforehand how many vertices are inside a polygon } ABOARD6_LAYER_STACKUP curlayer; curlayer.name = ALTIUM_PARSER::PropertiesReadString( properties, layername, "" ); curlayer.nextId = ALTIUM_PARSER::PropertiesReadInt( properties, layeri + "NEXT", 0 ); curlayer.prevId = ALTIUM_PARSER::PropertiesReadInt( properties, layeri + "PREV", 0 ); curlayer.copperthick = ALTIUM_PARSER::PropertiesReadKicadUnit( properties, layeri + "COPTHICK", "1.4mil" ); curlayer.dielectricconst = ALTIUM_PARSER::PropertiesReadDouble( properties, layeri + "DIELCONST", 0. ); curlayer.dielectricthick = ALTIUM_PARSER::PropertiesReadKicadUnit( properties, layeri + "DIELHEIGHT", "60mil" ); curlayer.dielectricmaterial = ALTIUM_PARSER::PropertiesReadString( properties, layeri + "DIELMATERIAL", "FR-4" ); stackup.push_back( curlayer ); } altium_parse_polygons( properties, board_vertices ); if( aReader.HasParsingError() ) { THROW_IO_ERROR( "Board6 stream was not parsed correctly!" ); } } ACLASS6::ACLASS6( ALTIUM_PARSER& aReader ) { std::map properties = aReader.ReadProperties(); if( properties.empty() ) { THROW_IO_ERROR( "Classes6 stream has no properties!" ); } name = ALTIUM_PARSER::PropertiesReadString( properties, "NAME", "" ); uniqueid = ALTIUM_PARSER::PropertiesReadString( properties, "UNIQUEID", "" ); kind = static_cast( ALTIUM_PARSER::PropertiesReadInt( properties, "KIND", -1 ) ); for( size_t i = 0; i < std::numeric_limits::max(); i++ ) { auto mit = properties.find( "M" + std::to_string( i ) ); if( mit == properties.end() ) { break; // it doesn't seem like we know beforehand how many components are in the netclass } names.push_back( mit->second ); } if( aReader.HasParsingError() ) { THROW_IO_ERROR( "Classes6 stream was not parsed correctly" ); } } ACOMPONENT6::ACOMPONENT6( ALTIUM_PARSER& aReader ) { std::map properties = aReader.ReadProperties(); if( properties.empty() ) { THROW_IO_ERROR( "Components6 stream has no properties" ); } layer = altium_layer_from_name( ALTIUM_PARSER::PropertiesReadString( properties, "LAYER", "" ) ); position = wxPoint( ALTIUM_PARSER::PropertiesReadKicadUnit( properties, "X", "0mil" ), -ALTIUM_PARSER::PropertiesReadKicadUnit( properties, "Y", "0mil" ) ); rotation = ALTIUM_PARSER::PropertiesReadDouble( properties, "ROTATION", 0. ); locked = ALTIUM_PARSER::PropertiesReadBool( properties, "LOCKED", false ); nameon = ALTIUM_PARSER::PropertiesReadBool( properties, "NAMEON", true ); commenton = ALTIUM_PARSER::PropertiesReadBool( properties, "COMMENTON", false ); sourcedesignator = ALTIUM_PARSER::PropertiesReadString( properties, "SOURCEDESIGNATOR", "" ); sourcefootprintlibrary = ALTIUM_PARSER::PropertiesReadString( properties, "SOURCEFOOTPRINTLIBRARY", "" ); sourcecomponentlibrary = ALTIUM_PARSER::PropertiesReadString( properties, "SOURCECOMPONENTLIBRARY", "" ); sourcelibreference = ALTIUM_PARSER::PropertiesReadString( properties, "SOURCELIBREFERENCE", "" ); if( aReader.HasParsingError() ) { THROW_IO_ERROR( "Components6 stream was not parsed correctly" ); } } ADIMENSION6::ADIMENSION6( ALTIUM_PARSER& aReader ) { aReader.Skip( 2 ); std::map properties = aReader.ReadProperties(); if( properties.empty() ) { THROW_IO_ERROR( "Dimensions6 stream has no properties" ); } layer = altium_layer_from_name( ALTIUM_PARSER::PropertiesReadString( properties, "LAYER", "" ) ); kind = static_cast( ALTIUM_PARSER::PropertiesReadInt( properties, "DIMENSIONKIND", 0 ) ); textformat = ALTIUM_PARSER::PropertiesReadString( properties, "TEXTFORMAT", "" ); height = ALTIUM_PARSER::PropertiesReadKicadUnit( properties, "HEIGHT", "0mil" ); angle = ALTIUM_PARSER::PropertiesReadDouble( properties, "ANGLE", 0. ); linewidth = ALTIUM_PARSER::PropertiesReadKicadUnit( properties, "LINEWIDTH", "10mil" ); textheight = ALTIUM_PARSER::PropertiesReadKicadUnit( properties, "TEXTHEIGHT", "10mil" ); textlinewidth = ALTIUM_PARSER::PropertiesReadKicadUnit( properties, "TEXTLINEWIDTH", "6mil" ); textprecission = ALTIUM_PARSER::PropertiesReadInt( properties, "TEXTPRECISION", 2 ); textbold = ALTIUM_PARSER::PropertiesReadBool( properties, "TEXTLINEWIDTH", false ); textitalic = ALTIUM_PARSER::PropertiesReadBool( properties, "ITALIC", false ); arrowsize = ALTIUM_PARSER::PropertiesReadKicadUnit( properties, "ARROWSIZE", "60mil" ); xy1 = wxPoint( ALTIUM_PARSER::PropertiesReadKicadUnit( properties, "X1", "0mil" ), -ALTIUM_PARSER::PropertiesReadKicadUnit( properties, "Y1", "0mil" ) ); int refcount = ALTIUM_PARSER::PropertiesReadInt( properties, "REFERENCES_COUNT", 0 ); for( int i = 0; i < refcount; i++ ) { const std::string refi = "REFERENCE" + std::to_string( i ); referencePoint.emplace_back( ALTIUM_PARSER::PropertiesReadKicadUnit( properties, refi + "POINTX", "0mil" ), -ALTIUM_PARSER::PropertiesReadKicadUnit( properties, refi + "POINTY", "0mil" ) ); } for( size_t i = 1; i < std::numeric_limits::max(); i++ ) { const std::string texti = "TEXT" + std::to_string( i ); const std::string textix = texti + "X"; const std::string textiy = texti + "Y"; if( properties.find( textix ) == properties.end() || properties.find( textiy ) == properties.end() ) { break; // it doesn't seem like we know beforehand how many vertices are inside a polygon } textPoint.emplace_back( ALTIUM_PARSER::PropertiesReadKicadUnit( properties, textix, "0mil" ), -ALTIUM_PARSER::PropertiesReadKicadUnit( properties, textiy, "0mil" ) ); } wxString dimensionunit = ALTIUM_PARSER::PropertiesReadString( properties, "TEXTDIMENSIONUNIT", "Millimeters" ); if( dimensionunit == "Inches" ) { textunit = ALTIUM_UNIT::INCHES; } else if( dimensionunit == "Mils" ) { textunit = ALTIUM_UNIT::MILS; } else if( dimensionunit == "Millimeters" ) { textunit = ALTIUM_UNIT::MILLIMETERS; } else if( dimensionunit == "Centimeters" ) { textunit = ALTIUM_UNIT::CENTIMETER; } else { textunit = ALTIUM_UNIT::UNKNOWN; } if( aReader.HasParsingError() ) { THROW_IO_ERROR( "Dimensions6 stream was not parsed correctly" ); } } ANET6::ANET6( ALTIUM_PARSER& aReader ) { std::map properties = aReader.ReadProperties(); if( properties.empty() ) { THROW_IO_ERROR( "Nets6 stream has no properties" ); } name = ALTIUM_PARSER::PropertiesReadString( properties, "NAME", "" ); if( aReader.HasParsingError() ) { THROW_IO_ERROR( "Nets6 stream was not parsed correctly" ); } } APOLYGON6::APOLYGON6( ALTIUM_PARSER& aReader ) { std::map properties = aReader.ReadProperties(); if( properties.empty() ) { THROW_IO_ERROR( "Polygons6 stream has no properties" ); } layer = altium_layer_from_name( ALTIUM_PARSER::PropertiesReadString( properties, "LAYER", "" ) ); net = ALTIUM_PARSER::PropertiesReadInt( properties, "NET", ALTIUM_NET_UNCONNECTED ); locked = ALTIUM_PARSER::PropertiesReadBool( properties, "LOCKED", false ); // TODO: kind gridsize = ALTIUM_PARSER::PropertiesReadKicadUnit( properties, "GRIDSIZE", "0mil" ); trackwidth = ALTIUM_PARSER::PropertiesReadKicadUnit( properties, "TRACKWIDTH", "0mil" ); minprimlength = ALTIUM_PARSER::PropertiesReadKicadUnit( properties, "MINPRIMLENGTH", "0mil" ); useoctagons = ALTIUM_PARSER::PropertiesReadBool( properties, "USEOCTAGONS", false ); wxString hatchstyleraw = ALTIUM_PARSER::PropertiesReadString( properties, "HATCHSTYLE", "" ); if( hatchstyleraw == "Solid" ) { hatchstyle = ALTIUM_POLYGON_HATCHSTYLE::SOLID; } else if( hatchstyleraw == "45Degree" ) { hatchstyle = ALTIUM_POLYGON_HATCHSTYLE::DEGREE_45; } else if( hatchstyleraw == "90Degree" ) { hatchstyle = ALTIUM_POLYGON_HATCHSTYLE::DEGREE_90; } else if( hatchstyleraw == "Horizontal" ) { hatchstyle = ALTIUM_POLYGON_HATCHSTYLE::HORIZONTAL; } else if( hatchstyleraw == "Vertical" ) { hatchstyle = ALTIUM_POLYGON_HATCHSTYLE::VERTICAL; } else if( hatchstyleraw == "None" ) { hatchstyle = ALTIUM_POLYGON_HATCHSTYLE::NONE; } else { hatchstyle = ALTIUM_POLYGON_HATCHSTYLE::UNKNOWN; } altium_parse_polygons( properties, vertices ); if( aReader.HasParsingError() ) { THROW_IO_ERROR( "Polygons6 stream was not parsed correctly" ); } } ARULE6::ARULE6( ALTIUM_PARSER& aReader ) { // Initalize all variables and make Coverity happy planeclearanceClearance = 0; polygonconnectAirgapwidth = 0; polygonconnectReliefconductorwidth = 0; polygonconnectReliefentries = 0; aReader.Skip( 2 ); std::map properties = aReader.ReadProperties(); if( properties.empty() ) { THROW_IO_ERROR( "Rules6 stream has no properties" ); } name = ALTIUM_PARSER::PropertiesReadString( properties, "NAME", "" ); priority = ALTIUM_PARSER::PropertiesReadInt( properties, "PRIORITY", 1 ); scope1expr = ALTIUM_PARSER::PropertiesReadString( properties, "SCOPE1EXPRESSION", "" ); scope2expr = ALTIUM_PARSER::PropertiesReadString( properties, "SCOPE2EXPRESSION", "" ); wxString rulekind = ALTIUM_PARSER::PropertiesReadString( properties, "RULEKIND", "" ); if( rulekind == "Clearance" ) { kind = ALTIUM_RULE_KIND::CLEARANCE; clearanceGap = ALTIUM_PARSER::PropertiesReadKicadUnit( properties, "GAP", "10mil" ); } else if( rulekind == "DiffPairsRouting" ) { kind = ALTIUM_RULE_KIND::DIFF_PAIR_ROUTINGS; } else if( rulekind == "Height" ) { kind = ALTIUM_RULE_KIND::HEIGHT; } else if( rulekind == "HoleSize" ) { kind = ALTIUM_RULE_KIND::HOLE_SIZE; } else if( rulekind == "HoleToHoleClearance" ) { kind = ALTIUM_RULE_KIND::HOLE_TO_HOLE_CLEARANCE; } else if( rulekind == "Width" ) { kind = ALTIUM_RULE_KIND::WIDTH; } else if( rulekind == "PasteMaskExpansion" ) { kind = ALTIUM_RULE_KIND::PASTE_MASK_EXPANSION; } else if( rulekind == "PlaneClearance" ) { kind = ALTIUM_RULE_KIND::PLANE_CLEARANCE; planeclearanceClearance = ALTIUM_PARSER::PropertiesReadKicadUnit( properties, "CLEARANCE", "10mil" ); } else if( rulekind == "PolygonConnect" ) { kind = ALTIUM_RULE_KIND::POLYGON_CONNECT; polygonconnectAirgapwidth = ALTIUM_PARSER::PropertiesReadKicadUnit( properties, "AIRGAPWIDTH", "10mil" ); polygonconnectReliefconductorwidth = ALTIUM_PARSER::PropertiesReadKicadUnit( properties, "RELIEFCONDUCTORWIDTH", "10mil" ); polygonconnectReliefentries = ALTIUM_PARSER::PropertiesReadInt( properties, "RELIEFENTRIES", 4 ); } else { kind = ALTIUM_RULE_KIND::UNKNOWN; } if( aReader.HasParsingError() ) { THROW_IO_ERROR( "Rules6 stream was not parsed correctly" ); } } AARC6::AARC6( ALTIUM_PARSER& aReader ) { ALTIUM_RECORD recordtype = static_cast( aReader.Read() ); if( recordtype != ALTIUM_RECORD::ARC ) { THROW_IO_ERROR( "Arcs6 stream has invalid recordtype" ); } // Subrecord 1 aReader.ReadAndSetSubrecordLength(); layer = static_cast( aReader.Read() ); uint8_t flags1 = aReader.Read(); is_locked = ( flags1 & 0x04 ) == 0; is_polygonoutline = ( flags1 & 0x02 ) != 0; uint8_t flags2 = aReader.Read(); is_keepout = flags2 == 2; net = aReader.Read(); subpolyindex = aReader.Read(); component = aReader.Read(); aReader.Skip( 4 ); center = aReader.ReadWxPoint(); radius = aReader.ReadKicadUnit(); startangle = aReader.Read(); endangle = aReader.Read(); width = aReader.ReadKicadUnit(); aReader.SkipSubrecord(); if( aReader.HasParsingError() ) { THROW_IO_ERROR( "Arcs6 stream was not parsed correctly" ); } } APAD6::APAD6( ALTIUM_PARSER& aReader ) { ALTIUM_RECORD recordtype = static_cast( aReader.Read() ); if( recordtype != ALTIUM_RECORD::PAD ) THROW_IO_ERROR( "Pads6 stream has invalid recordtype" ); // Subrecord 1 size_t subrecord1 = aReader.ReadAndSetSubrecordLength(); if( subrecord1 == 0 ) THROW_IO_ERROR( "Pads6 stream has no subrecord1 data" ); name = aReader.ReadWxString(); if( aReader.GetRemainingSubrecordBytes() != 0 ) THROW_IO_ERROR( "Pads6 stream has invalid subrecord1 length" ); aReader.SkipSubrecord(); // Subrecord 2 aReader.ReadAndSetSubrecordLength(); aReader.SkipSubrecord(); // Subrecord 3 aReader.ReadAndSetSubrecordLength(); aReader.SkipSubrecord(); // Subrecord 4 aReader.ReadAndSetSubrecordLength(); aReader.SkipSubrecord(); // Subrecord 5 size_t subrecord5 = aReader.ReadAndSetSubrecordLength(); if( subrecord5 < 114 ) THROW_IO_ERROR( "Pads6 stream subrecord has length < 114, which is unexpected" ); layer = static_cast( aReader.Read() ); tolayer = ALTIUM_LAYER::UNKNOWN; fromlayer = ALTIUM_LAYER::UNKNOWN; uint8_t flags1 = aReader.Read(); is_test_fab_top = ( flags1 & 0x80 ) != 0; is_tent_bottom = ( flags1 & 0x40 ) != 0; is_tent_top = ( flags1 & 0x20 ) != 0; is_locked = ( flags1 & 0x04 ) == 0; uint8_t flags2 = aReader.Read(); is_test_fab_bottom = ( flags2 & 0x01 ) != 0; net = aReader.Read(); aReader.Skip( 2 ); component = aReader.Read(); aReader.Skip( 4 ); position = aReader.ReadWxPoint(); topsize = aReader.ReadWxSize(); midsize = aReader.ReadWxSize(); botsize = aReader.ReadWxSize(); holesize = aReader.ReadKicadUnit(); topshape = static_cast( aReader.Read() ); midshape = static_cast( aReader.Read() ); botshape = static_cast( aReader.Read() ); direction = aReader.Read(); plated = aReader.Read() != 0; aReader.Skip( 1 ); padmode = static_cast( aReader.Read() ); aReader.Skip( 23 ); pastemaskexpansionmanual = aReader.ReadKicadUnit(); soldermaskexpansionmanual = aReader.ReadKicadUnit(); aReader.Skip( 7 ); pastemaskexpansionmode = static_cast( aReader.Read() ); soldermaskexpansionmode = static_cast( aReader.Read() ); aReader.Skip( 3 ); holerotation = aReader.Read(); if( subrecord5 >= 120 ) { tolayer = static_cast( aReader.Read() ); aReader.Skip( 2 ); fromlayer = static_cast( aReader.Read() ); //aReader.skip( 2 ); } else if( subrecord5 == 171 ) { } aReader.SkipSubrecord(); // Subrecord 6 size_t subrecord6 = aReader.ReadAndSetSubrecordLength(); if( subrecord6 == 651 || subrecord6 == 628 ) // TODO: better detection mechanism (Altium 14 = 628) { // TODO: detect type from something else than the size? sizeAndShape = std::make_unique(); for( wxSize& size : sizeAndShape->inner_size ) size.x = aReader.ReadKicadUnitX(); for( wxSize& size : sizeAndShape->inner_size ) size.y = aReader.ReadKicadUnitY(); for( ALTIUM_PAD_SHAPE& shape : sizeAndShape->inner_shape ) shape = static_cast( aReader.Read() ); aReader.Skip( 1 ); sizeAndShape->holeshape = static_cast( aReader.Read() ); sizeAndShape->slotsize = aReader.ReadKicadUnit(); sizeAndShape->slotrotation = aReader.Read(); for( wxPoint& pt : sizeAndShape->holeoffset ) pt.x = aReader.ReadKicadUnitX(); for( wxPoint& pt : sizeAndShape->holeoffset ) pt.y = aReader.ReadKicadUnitY(); aReader.Skip( 1 ); for( ALTIUM_PAD_SHAPE_ALT& shape : sizeAndShape->alt_shape ) shape = static_cast( aReader.Read() ); for( uint8_t& radius : sizeAndShape->cornerradius ) radius = aReader.Read(); } aReader.SkipSubrecord(); if( aReader.HasParsingError() ) THROW_IO_ERROR( "Pads6 stream was not parsed correctly" ); } AVIA6::AVIA6( ALTIUM_PARSER& aReader ) { ALTIUM_RECORD recordtype = static_cast( aReader.Read() ); if( recordtype != ALTIUM_RECORD::VIA ) { THROW_IO_ERROR( "Vias6 stream has invalid recordtype" ); } // Subrecord 1 aReader.ReadAndSetSubrecordLength(); aReader.Skip( 1 ); uint8_t flags1 = aReader.Read(); is_test_fab_top = ( flags1 & 0x80 ) != 0; is_tent_bottom = ( flags1 & 0x40 ) != 0; is_tent_top = ( flags1 & 0x20 ) != 0; is_locked = ( flags1 & 0x04 ) == 0; uint8_t flags2 = aReader.Read(); is_test_fab_bottom = ( flags2 & 0x01 ) != 0; net = aReader.Read(); aReader.Skip( 8 ); position = aReader.ReadWxPoint(); diameter = aReader.ReadKicadUnit(); holesize = aReader.ReadKicadUnit(); layer_start = static_cast( aReader.Read() ); layer_end = static_cast( aReader.Read() ); aReader.Skip( 43 ); viamode = static_cast( aReader.Read() ); aReader.SkipSubrecord(); if( aReader.HasParsingError() ) { THROW_IO_ERROR( "Vias6 stream was not parsed correctly" ); } } ATRACK6::ATRACK6( ALTIUM_PARSER& aReader ) { ALTIUM_RECORD recordtype = static_cast( aReader.Read() ); if( recordtype != ALTIUM_RECORD::TRACK ) { THROW_IO_ERROR( "Tracks6 stream has invalid recordtype" ); } // Subrecord 1 aReader.ReadAndSetSubrecordLength(); layer = static_cast( aReader.Read() ); uint8_t flags1 = aReader.Read(); is_locked = ( flags1 & 0x04 ) == 0; is_polygonoutline = ( flags1 & 0x02 ) != 0; uint8_t flags2 = aReader.Read(); is_keepout = flags2 == 2; net = aReader.Read(); subpolyindex = aReader.Read(); component = aReader.Read(); aReader.Skip( 4 ); start = aReader.ReadWxPoint(); end = aReader.ReadWxPoint(); width = aReader.ReadKicadUnit(); aReader.SkipSubrecord(); if( aReader.HasParsingError() ) { THROW_IO_ERROR( "Tracks6 stream was not parsed correctly" ); } } ATEXT6::ATEXT6( ALTIUM_PARSER& aReader ) { ALTIUM_RECORD recordtype = static_cast( aReader.Read() ); if( recordtype != ALTIUM_RECORD::TEXT ) { THROW_IO_ERROR( "Texts6 stream has invalid recordtype" ); } // Subrecord 1 - Properties size_t subrecord1 = aReader.ReadAndSetSubrecordLength(); layer = static_cast( aReader.Read() ); aReader.Skip( 6 ); component = aReader.Read(); aReader.Skip( 4 ); position = aReader.ReadWxPoint(); height = aReader.ReadKicadUnit(); aReader.Skip( 2 ); rotation = aReader.Read(); mirrored = aReader.Read() != 0; strokewidth = aReader.ReadKicadUnit(); isComment = aReader.Read() != 0; isDesignator = aReader.Read() != 0; aReader.Skip( 90 ); textposition = static_cast( aReader.Read() ); /** * In Altium 14 (subrecord1 == 230) only left bottom is valid? I think there is a bit missing. * https://gitlab.com/kicad/code/kicad/merge_requests/60#note_274913397 */ if( subrecord1 <= 230 ) { textposition = ALTIUM_TEXT_POSITION::LEFT_BOTTOM; } aReader.Skip( 27 ); isTruetype = aReader.Read() != 0; aReader.SkipSubrecord(); // Subrecord 2 - String aReader.ReadAndSetSubrecordLength(); text = aReader.ReadWxString(); // TODO: what about strings with length > 255? aReader.SkipSubrecord(); if( aReader.HasParsingError() ) { THROW_IO_ERROR( "Texts6 stream was not parsed correctly" ); } } AFILL6::AFILL6( ALTIUM_PARSER& aReader ) { ALTIUM_RECORD recordtype = static_cast( aReader.Read() ); if( recordtype != ALTIUM_RECORD::FILL ) { THROW_IO_ERROR( "Fills6 stream has invalid recordtype" ); } // Subrecord 1 aReader.ReadAndSetSubrecordLength(); layer = static_cast( aReader.Read() ); uint8_t flags1 = aReader.Read(); is_locked = ( flags1 & 0x04 ) == 0; uint8_t flags2 = aReader.Read(); is_keepout = flags2 == 2; net = aReader.Read(); aReader.Skip( 2 ); component = aReader.Read(); aReader.Skip( 4 ); pos1 = aReader.ReadWxPoint(); pos2 = aReader.ReadWxPoint(); rotation = aReader.Read(); aReader.SkipSubrecord(); if( aReader.HasParsingError() ) { THROW_IO_ERROR( "Fills6 stream was not parsed correctly" ); } } AREGION6::AREGION6( ALTIUM_PARSER& aReader, bool aExtendedVertices ) { ALTIUM_RECORD recordtype = static_cast( aReader.Read() ); if( recordtype != ALTIUM_RECORD::REGION ) { THROW_IO_ERROR( "Regions6 stream has invalid recordtype" ); } // Subrecord 1 aReader.ReadAndSetSubrecordLength(); layer = static_cast( aReader.Read() ); uint8_t flags1 = aReader.Read(); is_locked = ( flags1 & 0x04 ) == 0; uint8_t flags2 = aReader.Read(); is_keepout = flags2 == 2; net = aReader.Read(); aReader.Skip( 2 ); component = aReader.Read(); aReader.Skip( 9 ); std::map properties = aReader.ReadProperties(); if( properties.empty() ) { THROW_IO_ERROR( "Regions6 stream has empty properties" ); } int pkind = ALTIUM_PARSER::PropertiesReadInt( properties, "KIND", 0 ); bool is_cutout = ALTIUM_PARSER::PropertiesReadBool( properties, "ISBOARDCUTOUT", false ); is_shapebased = ALTIUM_PARSER::PropertiesReadBool( properties, "ISSHAPEBASED", false ); subpolyindex = static_cast( ALTIUM_PARSER::PropertiesReadInt( properties, "SUBPOLYINDEX", ALTIUM_POLYGON_NONE ) ); switch( pkind ) { case 0: if( is_cutout ) { kind = ALTIUM_REGION_KIND::BOARD_CUTOUT; } else { kind = ALTIUM_REGION_KIND::COPPER; } break; case 1: kind = ALTIUM_REGION_KIND::POLYGON_CUTOUT; break; case 4: kind = ALTIUM_REGION_KIND::CAVITY_DEFINITION; break; default: kind = ALTIUM_REGION_KIND::UNKNOWN; break; } uint32_t num_vertices = aReader.Read(); for( uint32_t i = 0; i < num_vertices; i++ ) { if( aExtendedVertices ) { bool isRound = aReader.Read() != 0; wxPoint position = aReader.ReadWxPoint(); wxPoint center = aReader.ReadWxPoint(); int32_t radius = aReader.ReadKicadUnit(); double angle1 = aReader.Read(); double angle2 = aReader.Read(); vertices.emplace_back( isRound, radius, angle1, angle2, position, center ); } else { // For some regions the coordinates are stored as double and not as int32_t int32_t x = ALTIUM_PARSER::ConvertToKicadUnit( aReader.Read() ); int32_t y = ALTIUM_PARSER::ConvertToKicadUnit( -aReader.Read() ); vertices.emplace_back( wxPoint( x, y ) ); } } aReader.SkipSubrecord(); if( aReader.HasParsingError() ) { THROW_IO_ERROR( "Regions6 stream was not parsed correctly" ); } }