summaryrefslogtreecommitdiff
path: root/utils/idftools/idf_parser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'utils/idftools/idf_parser.cpp')
-rw-r--r--utils/idftools/idf_parser.cpp4288
1 files changed, 4288 insertions, 0 deletions
diff --git a/utils/idftools/idf_parser.cpp b/utils/idftools/idf_parser.cpp
new file mode 100644
index 0000000..c4d3e4c
--- /dev/null
+++ b/utils/idftools/idf_parser.cpp
@@ -0,0 +1,4288 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2014 Cirilo Bernardo
+ *
+ * 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 <iostream>
+#include <iomanip>
+#include <fstream>
+#include <sstream>
+#include <cmath>
+#include <cerrno>
+#include <algorithm>
+
+#include <idf_parser.h>
+#include <idf_helpers.h>
+
+using namespace std;
+using namespace IDF3;
+
+
+static bool MatchCompOutline( IDF3_COMP_OUTLINE* aOutlineA, IDF3_COMP_OUTLINE* aOutlineB )
+{
+ if( aOutlineA->GetComponentClass() != aOutlineB->GetComponentClass() )
+ return false;
+
+ if( aOutlineA->OutlinesSize() != aOutlineB->OutlinesSize() )
+ return false;
+
+ // are both outlines empty?
+ if( aOutlineA->OutlinesSize() == 0 )
+ return true;
+
+ IDF_OUTLINE* opA = aOutlineA->GetOutline( 0 );
+ IDF_OUTLINE* opB = aOutlineB->GetOutline( 0 );
+
+ if( opA->size() != opB->size() )
+ return false;
+
+ if( opA->size() == 0 )
+ return true;
+
+ std::list<IDF_SEGMENT*>::iterator olAs = opA->begin();
+ std::list<IDF_SEGMENT*>::iterator olAe = opA->end();
+ std::list<IDF_SEGMENT*>::iterator olBs = opB->begin();
+
+ while( olAs != olAe )
+ {
+ if( !(*olAs)->MatchesStart( (*olBs)->startPoint ) )
+ return false;
+
+ if( !(*olAs)->MatchesEnd( (*olBs)->endPoint ) )
+ return false;
+
+ ++olAs;
+ ++olBs;
+ }
+
+ return true;
+}
+
+
+/*
+ * CLASS: IDF3_COMP_OUTLINE_DATA
+ * This represents the outline placement
+ * information and other data specific to
+ * each component instance.
+ */
+IDF3_COMP_OUTLINE_DATA::IDF3_COMP_OUTLINE_DATA()
+{
+ parent = NULL;
+ outline = NULL;
+ xoff = 0.0;
+ yoff = 0.0;
+ zoff = 0.0;
+ aoff = 0.0;
+
+ return;
+}
+
+IDF3_COMP_OUTLINE_DATA::IDF3_COMP_OUTLINE_DATA( IDF3_COMPONENT* aParent,
+ IDF3_COMP_OUTLINE* aOutline )
+{
+ parent = aParent;
+ outline = aOutline;
+ xoff = 0.0;
+ yoff = 0.0;
+ zoff = 0.0;
+ aoff = 0.0;
+
+ if( aOutline )
+ aOutline->incrementRef();
+
+ return;
+}
+
+IDF3_COMP_OUTLINE_DATA::IDF3_COMP_OUTLINE_DATA( IDF3_COMPONENT* aParent,
+ IDF3_COMP_OUTLINE* aOutline,
+ double aXoff, double aYoff,
+ double aZoff, double aAngleOff )
+{
+ parent = aParent;
+ outline = aOutline;
+ xoff = aXoff;
+ yoff = aYoff;
+ zoff = aZoff;
+ aoff = aAngleOff;
+ return;
+}
+
+IDF3_COMP_OUTLINE_DATA::~IDF3_COMP_OUTLINE_DATA()
+{
+ if( outline )
+ outline->decrementRef();
+
+ return;
+}
+
+#ifndef DISABLE_IDF_OWNERSHIP
+bool IDF3_COMP_OUTLINE_DATA::checkOwnership( int aSourceLine, const char* aSourceFunc )
+{
+ if( !parent )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << aSourceLine << ":" << aSourceFunc << "():\n";
+ ostr << "* BUG: IDF3_COMP_OUTLINE_DATA::parent not set; cannot enforce ownership rules\n";
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ IDF3::IDF_PLACEMENT placement = parent->GetPlacement();
+ IDF3::CAD_TYPE parentCAD = parent->GetCadType();
+
+ if( placement == PS_PLACED || placement == PS_UNPLACED )
+ return true;
+
+ if( placement == PS_MCAD && parentCAD == CAD_MECH )
+ return true;
+
+ if( placement == PS_ECAD && parentCAD == CAD_ELEC )
+ return true;
+
+ do
+ {
+ ostringstream ostr;
+ ostr << "* " << __FILE__ << ":" << aSourceLine << ":" << aSourceFunc << "():\n";
+ ostr << "* ownership violation; CAD type is ";
+
+ if( parentCAD == CAD_MECH )
+ ostr << "MCAD ";
+ else
+ ostr << "ECAD ";
+
+ ostr << "while outline owner is " << GetPlacementString( placement ) << "\n";
+ errormsg = ostr.str();
+
+ } while( 0 );
+
+ return false;
+}
+#endif
+
+bool IDF3_COMP_OUTLINE_DATA::SetOffsets( double aXoff, double aYoff,
+ double aZoff, double aAngleOff )
+{
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !checkOwnership( __LINE__, __FUNCTION__ ) )
+ return false;
+#endif
+
+ xoff = aXoff;
+ yoff = aYoff;
+ zoff = aZoff;
+ aoff = aAngleOff;
+ return true;
+}
+
+void IDF3_COMP_OUTLINE_DATA::GetOffsets( double& aXoff, double& aYoff,
+ double& aZoff, double& aAngleOff )
+{
+ aXoff = xoff;
+ aYoff = yoff;
+ aZoff = zoff;
+ aAngleOff = aoff;
+ return;
+}
+
+
+void IDF3_COMP_OUTLINE_DATA::SetParent( IDF3_COMPONENT* aParent )
+{
+ parent = aParent;
+}
+
+bool IDF3_COMP_OUTLINE_DATA::SetOutline( IDF3_COMP_OUTLINE* aOutline )
+{
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !checkOwnership( __LINE__, __FUNCTION__ ) )
+ return false;
+#endif
+
+ if( outline )
+ outline->decrementRef();
+
+ outline = aOutline;
+
+ if( outline )
+ outline->incrementRef();
+
+ return true;
+}
+
+
+bool IDF3_COMP_OUTLINE_DATA::readPlaceData( std::ifstream &aBoardFile,
+ IDF3::FILE_STATE& aBoardState,
+ IDF3_BOARD *aBoard,
+ IDF3::IDF_VERSION aIdfVersion,
+ bool aNoSubstituteOutlines )
+{
+ if( !aBoard )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "\n* BUG: invoked with no reference to the parent IDF_BOARD" ) );
+
+ // clear out data possibly left over from previous use of the object
+ outline = NULL;
+ parent = NULL;
+
+ std::string iline; // the input line
+ bool isComment; // true if a line just read in is a comment line
+ std::streampos pos;
+ int idx = 0;
+ bool quoted = false;
+ std::string token;
+ std::string uid;
+ std::string refdes;
+ IDF3::IDF_PLACEMENT placement = IDF3::PS_UNPLACED;
+ IDF3::IDF_LAYER side = IDF3::LYR_TOP;
+
+ // RECORD 2: 'package name', 'part number', 'Refdes' (any, NOREFDES, BOARD)
+ while( !FetchIDFLine( aBoardFile, iline, isComment, pos ) && aBoardFile.good() );
+
+ if( ( !aBoardFile.good() && !aBoardFile.eof() ) || iline.empty() )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* violation: could not read PLACEMENT section\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( isComment )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* violation: comment within PLACEMENT section\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ idx = 0;
+ GetIDFString( iline, token, quoted, idx );
+
+ if( !quoted && CompareToken( ".END_PLACEMENT", token ) )
+ {
+ aBoardState = IDF3::FILE_PLACEMENT;
+ return false;
+ }
+
+ std::string ngeom = token;
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* violation: no PART NAME in PLACEMENT RECORD2\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ std::string npart = token;
+ uid = ngeom + "_" + npart;
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* violation: no REFDES in PLACEMENT RECORD2\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( CompareToken( "NOREFDES", token ) )
+ {
+ // according to the IDF3.0 specification, this is a
+ // mechanical component. The specification is defective
+ // since it is impossible to associate mechanical
+ // components with their holes unless the mechanical
+ // component is given a unique RefDes. This class of defect
+ // is one reason IDF does not work well in faithfully
+ // conveying information between ECAD and MCAD.
+ refdes = aBoard->GetNewRefDes();
+ }
+ else if( CompareToken( "BOARD", token ) )
+ {
+ ostringstream ostr;
+
+ ostr << "UNSUPPORTED FEATURE\n";
+ ostr << "* RefDes is 'BOARD', indicating this is a PANEL FILE (not supported)\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+ else if( CompareToken( "PANEL", token ) )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* violation: RefDes in PLACEMENT RECORD2 is 'PANEL'\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+ else if( token.empty() )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* violation: empty RefDes string in PLACEMENT RECORD2\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+ else
+ {
+ // note: perversely, spaces can be a valid RefDes
+ refdes = token;
+ }
+
+ // V2: RECORD 3: X, Y, ROT, SIDE (top/bot), PLACEMENT (fixed, placed, unplaced)
+ // V3: RECORD 3: X, Y, Z, ROT, SIDE (top/bot), PLACEMENT (placed, unplaced, mcad, ecad)
+ while( !FetchIDFLine( aBoardFile, iline, isComment, pos ) && aBoardFile.good() );
+
+ if( !aBoardFile.good() )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* problems reading PLACEMENT SECTION, RECORD 3\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( isComment )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* violation: comment within PLACEMENT section\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ idx = 0;
+ GetIDFString( iline, token, quoted, idx );
+
+ if( quoted )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* violation: X value must not be in quotes (PLACEMENT RECORD 3)\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ istringstream istr;
+ istr.str( token );
+
+ istr >> xoff;
+ if( istr.fail() )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* violation: X value is not numeric (PLACEMENT RECORD 3)\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* violation: no Y value (PLACEMENT RECORD 3)\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ istr.clear();
+ istr.str( token );
+
+ istr >> yoff;
+ if( istr.fail() )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* violation: Y value is not numeric (PLACEMENT RECORD 3)\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( aIdfVersion > IDF_V2 )
+ {
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDFv3 file\n";
+ ostr << "* violation: no Z value (PLACEMENT RECORD 3)\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ istr.clear();
+ istr.str( token );
+
+ istr >> zoff;
+ if( istr.fail() )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDFv3 file\n";
+ ostr << "* violation: Z value is not numeric (PLACEMENT RECORD 3)\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* violation: no rotation value (PLACEMENT RECORD 3)\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ istr.clear();
+ istr.str( token );
+
+ istr >> aoff;
+ if( istr.fail() )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* violation: rotation value is not numeric (PLACEMENT RECORD 3)\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* violation: no SIDE value (PLACEMENT RECORD 3)\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( CompareToken( "TOP", token ) )
+ {
+ side = IDF3::LYR_TOP;
+ }
+ else if( CompareToken( "BOTTOM", token ) )
+ {
+ side = IDF3::LYR_BOTTOM;
+ }
+ else
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* violation: invalid SIDE value in PLACEMENT RECORD 3 ('";
+ ostr << token << "'); must be one of TOP/BOTTOM\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* violation: no PLACEMENT value in PLACEMENT RECORD 3\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( CompareToken( "PLACED", token ) )
+ {
+ placement = IDF3::PS_PLACED;
+ }
+ else if( CompareToken( "UNPLACED", token ) )
+ {
+ placement = IDF3::PS_UNPLACED;
+ }
+ else if( aIdfVersion > IDF_V2 && CompareToken( "MCAD", token ) )
+ {
+ placement = IDF3::PS_MCAD;
+ }
+ else if( aIdfVersion > IDF_V2 && CompareToken( "ECAD", token ) )
+ {
+ placement = IDF3::PS_ECAD;
+ }
+ else if( aIdfVersion < IDF_V3 && CompareToken( "FIXED", token ) )
+ {
+ if( aBoard->GetCadType() == CAD_ELEC )
+ placement = IDF3::PS_MCAD;
+ else
+ placement = IDF3::PS_ECAD;
+ }
+ else
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* violation: invalid PLACEMENT value ('";
+ ostr << token << "') in PLACEMENT RECORD 3\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ outline = aBoard->GetComponentOutline( uid );
+
+ if( outline == NULL && !aNoSubstituteOutlines )
+ {
+ ERROR_IDF << "MISSING OUTLINE\n";
+ cerr << "* GeomName( " << ngeom << " ), PartName( " << npart << " )\n";
+ cerr << "* Substituting default outline.\n";
+ outline = aBoard->GetInvalidOutline( ngeom, npart );
+
+ if( outline == NULL )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "\n* missing outline: cannot create default" ) );
+ }
+
+ if( aBoard->GetUnit() == IDF3::UNIT_THOU )
+ {
+ xoff *= IDF_THOU_TO_MM;
+ yoff *= IDF_THOU_TO_MM;
+ zoff *= IDF_THOU_TO_MM;
+ }
+
+ parent = aBoard->FindComponent( refdes );
+
+ if( parent == NULL )
+ {
+ IDF3_COMPONENT* cp = new IDF3_COMPONENT( aBoard );
+
+ if( cp == NULL )
+ {
+ outline = NULL;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "cannot create component object" ) );
+ }
+
+ cp->SetRefDes( refdes );
+ cp->SetPosition( xoff, yoff, aoff, side );
+ cp->SetPlacement( placement );
+
+ xoff = 0;
+ yoff = 0;
+ aoff = 0;
+
+ aBoard->AddComponent( cp );
+
+ parent = cp;
+ }
+ else
+ {
+ double tX, tY, tA;
+ IDF3::IDF_LAYER tL;
+
+ if( parent->GetPosition( tX, tY, tA, tL ) )
+ {
+ if( side != tL )
+ {
+ outline = NULL;
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* violation: inconsistent PLACEMENT data; ";
+ ostr << "* SIDE value has changed from " << GetLayerString( tL );
+ ostr << " to " << GetLayerString( side ) << "\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ xoff -= tX;
+ yoff -= tY;
+ aoff -= tA;
+ }
+ else
+ {
+ parent->SetPosition( xoff, yoff, aoff, side );
+ parent->SetPlacement( placement );
+
+ xoff = 0;
+ yoff = 0;
+ aoff = 0;
+ }
+
+ if( placement != parent->GetPlacement() )
+ {
+ outline = NULL;
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* violation: inconsistent PLACEMENT data; ";
+ ostr << "* PLACEMENT value has changed from ";
+ ostr << GetPlacementString( parent->GetPlacement() );
+ ostr << " to " << GetPlacementString( placement ) << "\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ }
+
+ // copy internal data to a new object and push it into the component's outline list
+ IDF3_COMP_OUTLINE_DATA* cdp = new IDF3_COMP_OUTLINE_DATA;
+ *cdp = *this;
+ if( outline ) outline->incrementRef();
+ outline = NULL;
+
+ if( !parent->AddOutlineData( cdp ) )
+ {
+ delete cdp;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "could not add outline data object" ) );
+ }
+
+ return true;
+} // IDF3_COMP_OUTLINE_DATA::readPlaceData
+
+
+void IDF3_COMP_OUTLINE_DATA::writePlaceData( std::ofstream& aBoardFile,
+ double aXpos, double aYpos, double aAngle,
+ const std::string aRefDes,
+ IDF3::IDF_PLACEMENT aPlacement,
+ IDF3::IDF_LAYER aSide )
+{
+ if( outline == NULL )
+ return;
+
+ if( outline->GetUID().empty() )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "empty GEOM and PART names" ) );
+
+ if( aPlacement == PS_INVALID )
+ {
+ ERROR_IDF << "placement invalid (" << aRefDes << ":";
+ std::cerr << aPlacement << "); defaulting to PLACED\n";
+ aPlacement = PS_PLACED;
+ }
+
+ if( aSide != LYR_TOP && aSide != LYR_BOTTOM )
+ {
+ ostringstream ostr;
+ ostr << "\n* invalid side (" << GetLayerString( aSide ) << "); ";
+ ostr << "must be TOP or BOTTOM\n";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ // calculate the final position based on layer
+ double xpos, ypos, ang;
+
+ switch( aSide )
+ {
+ case LYR_TOP:
+ xpos = aXpos + xoff;
+ ypos = aYpos + yoff;
+ ang = aAngle + aoff;
+ break;
+
+ default:
+ xpos = aXpos - xoff;
+ ypos = aYpos + yoff;
+ ang = aAngle - aoff;
+ break;
+ }
+
+ std::string arefdes = aRefDes;
+
+ if( arefdes.empty() || !arefdes.compare( "~" )
+ || ( arefdes.size() >= 8 && CompareToken( "NOREFDES", arefdes.substr(0, 8) ) ) )
+ arefdes = "NOREFDES";
+
+ aBoardFile << "\"" << outline->GetGeomName() << "\" \"" << outline->GetPartName() << "\" "
+ << arefdes << "\n";
+
+ IDF3::IDF_UNIT unit = UNIT_MM;
+
+ if( parent )
+ unit = parent->GetUnit();
+
+ if( unit == UNIT_MM )
+ {
+ aBoardFile << setiosflags(ios::fixed) << setprecision(5) << xpos << " "
+ << ypos << " " << setprecision(3) << zoff << " "
+ << ang << " ";
+ }
+ else
+ {
+ aBoardFile << setiosflags(ios::fixed) << setprecision(1) << (xpos / IDF_THOU_TO_MM) << " "
+ << (ypos / IDF_THOU_TO_MM) << " " << (zoff / IDF_THOU_TO_MM) << " "
+ << setprecision(3) << ang << " ";
+ }
+
+ WriteLayersText( aBoardFile, aSide );
+
+ switch( aPlacement )
+ {
+ case PS_PLACED:
+ aBoardFile << " PLACED\n";
+ break;
+
+ case PS_UNPLACED:
+ aBoardFile << " UNPLACED\n";
+ break;
+
+ case PS_MCAD:
+ aBoardFile << " MCAD\n";
+ break;
+
+ default:
+ aBoardFile << " ECAD\n";
+ break;
+ }
+
+ return;
+}
+
+
+/*
+ * CLASS: IDF3_COMPONENT
+ *
+ * This represents a component and its associated
+ * IDF outlines and ancillary data (position, etc)
+ */
+IDF3_COMPONENT::IDF3_COMPONENT( IDF3_BOARD* aParent )
+{
+ xpos = 0.0;
+ ypos = 0.0;
+ angle = 0.0;
+
+ hasPosition = false;
+ placement = PS_INVALID;
+ layer = LYR_INVALID;
+
+ parent = aParent;
+ return;
+}
+
+IDF3_COMPONENT::~IDF3_COMPONENT()
+{
+ std::list< IDF3_COMP_OUTLINE_DATA* >::iterator itcS = components.begin();
+ std::list< IDF3_COMP_OUTLINE_DATA* >::iterator itcE = components.end();
+
+ while( itcS != itcE )
+ {
+ delete *itcS;
+ ++itcS;
+ }
+
+ components.clear();
+
+ std::list< IDF_DRILL_DATA* >::iterator itdS = drills.begin();
+ std::list< IDF_DRILL_DATA* >::iterator itdE = drills.end();
+
+ while( itdS != itdE )
+ {
+ delete *itdS;
+ ++itdS;
+ }
+
+ drills.clear();
+
+ return;
+}
+
+#ifndef DISABLE_IDF_OWNERSHIP
+bool IDF3_COMPONENT::checkOwnership( int aSourceLine, const char* aSourceFunc )
+{
+ if( !parent )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << aSourceLine << ":" << aSourceFunc << "():\n";
+ ostr << "\n* BUG: parent not set";
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ IDF3::CAD_TYPE pcad = parent->GetCadType();
+
+ switch( placement )
+ {
+ case PS_UNPLACED:
+ case PS_PLACED:
+ case PS_INVALID:
+ break;
+
+ case PS_MCAD:
+
+ if( pcad != CAD_MECH )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "\n* ownership violation; internal CAD type (MCAD) conflicts with PLACEMENT (";
+ ostr << GetPlacementString( placement ) << ")";
+ errormsg = ostr.str();
+
+ return false;
+ }
+ break;
+
+ case PS_ECAD:
+
+ if( pcad != CAD_ELEC )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "\n* ownership violation; internal CAD type (MCAD) conflicts with PLACEMENT (";
+ ostr << GetPlacementString( placement ) << ")";
+ errormsg = ostr.str();
+
+ return false;
+ }
+ break;
+
+ default:
+ do{
+ ostringstream ostr;
+ ostr << "\n* BUG: unhandled internal placement value (" << placement << ")";
+ errormsg = ostr.str();
+
+ return false;
+ } while( 0 );
+
+ break;
+ }
+
+ return true;
+}
+#endif
+
+
+void IDF3_COMPONENT::SetParent( IDF3_BOARD* aParent )
+{
+ parent = aParent;
+ return;
+}
+
+IDF3::CAD_TYPE IDF3_COMPONENT::GetCadType( void )
+{
+ if( parent )
+ return parent->GetCadType();
+
+ return CAD_INVALID;
+}
+
+IDF3::IDF_UNIT IDF3_COMPONENT::GetUnit( void )
+{
+ if( parent )
+ return parent->GetUnit();
+
+ return UNIT_INVALID;
+}
+
+bool IDF3_COMPONENT::SetRefDes( const std::string& aRefDes )
+{
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !checkOwnership( __LINE__, __FUNCTION__ ) )
+ return false;
+#endif
+
+ if( aRefDes.empty() )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): invalid RefDes (empty)";
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ if( CompareToken( "PANEL", aRefDes ) )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* BUG: PANEL is a reserved designator and may not be used by components";
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ refdes = aRefDes;
+ return true;
+}
+
+
+const std::string& IDF3_COMPONENT::GetRefDes( void )
+{
+ return refdes;
+}
+
+IDF_DRILL_DATA* IDF3_COMPONENT::AddDrill( double aDia, double aXpos, double aYpos,
+ IDF3::KEY_PLATING aPlating,
+ const std::string aHoleType,
+ IDF3::KEY_OWNER aOwner )
+{
+ IDF_DRILL_DATA* dp = new IDF_DRILL_DATA( aDia, aXpos, aYpos, aPlating,
+ refdes, aHoleType, aOwner );
+
+ if( dp == NULL )
+ return NULL;
+
+ drills.push_back( dp );
+
+ return dp;
+}
+
+
+IDF_DRILL_DATA* IDF3_COMPONENT::AddDrill( IDF_DRILL_DATA* aDrilledHole )
+{
+ if( !aDrilledHole )
+ return NULL;
+
+ if( CompareToken( "PANEL", refdes ) )
+ {
+ ERROR_IDF;
+ cerr << "\n* BUG: PANEL drills not supported at component level\n";
+ return NULL;
+ }
+
+ if( refdes.compare( aDrilledHole->GetDrillRefDes() ) )
+ {
+ ERROR_IDF;
+ cerr << "\n* BUG: pushing an incorrect REFDES ('" << aDrilledHole->GetDrillRefDes();
+ cerr << "') to component ('" << refdes << "')\n";
+ return NULL;
+ }
+
+ drills.push_back( aDrilledHole );
+
+ return aDrilledHole;
+}
+
+
+bool IDF3_COMPONENT::DelDrill( double aDia, double aXpos, double aYpos )
+{
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !checkOwnership( __LINE__, __FUNCTION__ ) )
+ return false;
+#endif
+
+ errormsg.clear();
+
+ if( drills.empty() )
+ return false;
+
+ bool val = false;
+
+ list< IDF_DRILL_DATA* >::iterator itS = drills.begin();
+ list< IDF_DRILL_DATA* >::iterator itE = drills.end();
+
+ while( !drills.empty() && itS != itE )
+ {
+ if( (*itS)->Matches( aDia, aXpos, aYpos ) )
+ {
+ val = true;
+ delete *itS;
+ itS = drills.erase( itS );
+ continue;
+ }
+ ++itS;
+ }
+
+ return val;
+}
+
+
+bool IDF3_COMPONENT::DelDrill( IDF_DRILL_DATA* aDrill )
+{
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !checkOwnership( __LINE__, __FUNCTION__ ) )
+ return false;
+#endif
+
+ errormsg.clear();
+
+ if( drills.empty() )
+ return false;
+
+ list< IDF_DRILL_DATA* >::iterator itS = drills.begin();
+ list< IDF_DRILL_DATA* >::iterator itE = drills.end();
+
+ while( !drills.empty() && itS != itE )
+ {
+ if( *itS == aDrill )
+ {
+ delete *itS;
+ drills.erase( itS );
+ return true;
+ }
+ ++itS;
+ }
+
+ return false;
+}
+
+const std::list< IDF_DRILL_DATA* >*const IDF3_COMPONENT::GetDrills( void )
+{
+ return &drills;
+}
+
+bool IDF3_COMPONENT::AddOutlineData( IDF3_COMP_OUTLINE_DATA* aComponentOutline )
+{
+ if( aComponentOutline == NULL )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): invalid aComponentOutline (NULL)";
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+
+ components.push_back( aComponentOutline );
+
+ return true;
+}
+
+bool IDF3_COMPONENT::DeleteOutlineData( IDF3_COMP_OUTLINE_DATA* aComponentOutline )
+{
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !checkOwnership( __LINE__, __FUNCTION__ ) )
+ return false;
+#endif
+
+ if( components.empty() )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): component list is empty";
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ if( aComponentOutline == NULL )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): invalid aComponentOutline (NULL)";
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ errormsg.clear();
+
+ std::list< IDF3_COMP_OUTLINE_DATA* >::iterator itS = components.begin();
+ std::list< IDF3_COMP_OUTLINE_DATA* >::iterator itE = components.end();
+
+ while( itS != itE )
+ {
+ if( *itS == aComponentOutline )
+ {
+ delete *itS;
+ components.erase( itS );
+ return true;
+ }
+
+ ++itS;
+ }
+
+ return false;
+}
+
+bool IDF3_COMPONENT::DeleteOutlineData( size_t aIndex )
+{
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !checkOwnership( __LINE__, __FUNCTION__ ) )
+ return false;
+#endif
+
+ if( aIndex >= components.size() )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* aIndex (" << aIndex << ") out of range; list size is " << components.size();
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ std::list< IDF3_COMP_OUTLINE_DATA* >::iterator itS = components.begin();
+ std::list< IDF3_COMP_OUTLINE_DATA* >::iterator itE = components.end();
+ size_t idx = 0;
+
+ while( itS != itE )
+ {
+ if( idx == aIndex )
+ {
+ delete *itS;
+ components.erase( itS );
+ return true;
+ }
+
+ ++idx;
+ ++itS;
+ }
+
+ return false;
+}
+
+size_t IDF3_COMPONENT::GetOutlinesSize( void )
+{
+ return components.size();
+}
+
+const std::list< IDF3_COMP_OUTLINE_DATA* >*const IDF3_COMPONENT::GetOutlinesData( void )
+{
+ return &components;
+}
+
+bool IDF3_COMPONENT::GetPosition( double& aXpos, double& aYpos, double& aAngle,
+ IDF3::IDF_LAYER& aLayer )
+{
+ errormsg.clear();
+
+ if( !hasPosition )
+ {
+ aXpos = 0.0;
+ aYpos = 0.0;
+ aAngle = 0.0;
+ aLayer = IDF3::LYR_INVALID;
+ return false;
+ }
+
+ aXpos = xpos;
+ aYpos = ypos;
+ aAngle = angle;
+ aLayer = layer;
+ return true;
+}
+
+bool IDF3_COMPONENT::SetPosition( double aXpos, double aYpos, double aAngle, IDF3::IDF_LAYER aLayer )
+{
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !checkOwnership( __LINE__, __FUNCTION__ ) )
+ return false;
+#endif
+
+ errormsg.clear();
+
+ switch( aLayer )
+ {
+ case LYR_TOP:
+ case LYR_BOTTOM:
+ break;
+
+ default:
+ do{
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "\n* invalid side (must be TOP or BOTTOM only): " << GetLayerString( aLayer );
+ errormsg = ostr.str();
+
+ return false;
+ } while( 0 );
+ break;
+ }
+
+ if( hasPosition )
+ return false;
+
+ hasPosition = true;
+ xpos = aXpos;
+ ypos = aYpos;
+ angle = aAngle;
+ layer = aLayer;
+ return true;
+}
+
+
+IDF3::IDF_PLACEMENT IDF3_COMPONENT::GetPlacement( void )
+{
+ return placement;
+}
+
+
+bool IDF3_COMPONENT::SetPlacement( IDF3::IDF_PLACEMENT aPlacementValue )
+{
+ if( aPlacementValue < PS_UNPLACED || aPlacementValue >= PS_INVALID )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "\n* invalid PLACEMENT value (" << aPlacementValue << ")";
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !checkOwnership( __LINE__, __FUNCTION__ ) )
+ return false;
+#endif
+
+ placement = aPlacementValue;
+
+ return true;
+}
+
+bool IDF3_COMPONENT::writeDrillData( std::ofstream& aBoardFile )
+{
+ if( drills.empty() )
+ return true;
+
+ std::list< IDF_DRILL_DATA* >::iterator itS = drills.begin();
+ std::list< IDF_DRILL_DATA* >::iterator itE = drills.end();
+
+ while( itS != itE )
+ {
+ (*itS)->write( aBoardFile, GetUnit() );
+ ++itS;
+ }
+
+ return true;
+}
+
+
+bool IDF3_COMPONENT::writePlaceData( std::ofstream& aBoardFile )
+{
+ if( components.empty() )
+ return true;
+
+ std::list< IDF3_COMP_OUTLINE_DATA* >::iterator itS = components.begin();
+ std::list< IDF3_COMP_OUTLINE_DATA* >::iterator itE = components.end();
+
+ while( itS != itE )
+ {
+ (*itS)->writePlaceData( aBoardFile, xpos, ypos, angle, refdes, placement, layer );
+ ++itS;
+ }
+
+ return true;
+}
+
+
+IDF3_BOARD::IDF3_BOARD( IDF3::CAD_TYPE aCadType )
+{
+ idfVer = IDF_V3;
+ state = FILE_START;
+ cadType = aCadType;
+ userPrec = 5;
+ userScale = 1.0;
+ userXoff = 0.0;
+ userYoff = 0.0;
+ brdFileVersion = 0;
+ libFileVersion = 0;
+ iRefDes = 0;
+ unit = UNIT_MM;
+
+ // unlike other outlines which are created as necessary,
+ // the board outline always exists and its parent must
+ // be set here
+ olnBoard.setParent( this );
+ olnBoard.setThickness( 1.6 );
+
+ return;
+}
+
+IDF3_BOARD::~IDF3_BOARD()
+{
+ Clear();
+
+ return;
+}
+
+
+const std::string& IDF3_BOARD::GetNewRefDes( void )
+{
+ ostringstream ostr;
+ ostr << "NOREFDESn" << iRefDes++;
+
+ sRefDes = ostr.str();
+
+ return sRefDes;
+}
+
+
+#ifndef DISABLE_IDF_OWNERSHIP
+bool IDF3_BOARD::checkComponentOwnership( int aSourceLine, const char* aSourceFunc,
+ IDF3_COMPONENT* aComponent )
+{
+ if( !aComponent )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << aSourceLine << ":" << aSourceFunc;
+ ostr << "(): Invalid component pointer (NULL)";
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ IDF3::IDF_PLACEMENT place = aComponent->GetPlacement();
+
+ if( place == PS_PLACED || place == PS_UNPLACED )
+ return true;
+
+ if( place == PS_MCAD && cadType == CAD_MECH )
+ return true;
+
+ if( place == PS_ECAD && cadType == CAD_ELEC )
+ return true;
+
+ do
+ {
+ ostringstream ostr;
+ ostr << "* " << __FILE__ << ":" << aSourceLine << ":" << aSourceFunc << "():\n";
+ ostr << "* ownership violation; CAD type is ";
+
+ if( cadType == CAD_MECH )
+ ostr << "MCAD ";
+ else
+ ostr << "ECAD ";
+
+ ostr << "while outline owner is " << GetPlacementString( place ) << "\n";
+ errormsg = ostr.str();
+
+ } while( 0 );
+
+ return false;
+}
+#endif
+
+IDF3::CAD_TYPE IDF3_BOARD::GetCadType( void )
+{
+ return cadType;
+}
+
+void IDF3_BOARD::SetBoardName( std::string aBoardName )
+{
+ boardName = aBoardName;
+ return;
+}
+
+const std::string& IDF3_BOARD::GetBoardName( void )
+{
+ return boardName;
+}
+
+bool IDF3_BOARD::setUnit( IDF3::IDF_UNIT aUnit, bool convert )
+{
+ switch( aUnit )
+ {
+ case UNIT_MM:
+ case UNIT_THOU:
+ unit = aUnit;
+ break;
+
+ case UNIT_TNM:
+ ERROR_IDF << "\n* TNM unit is not supported; defaulting to mm\n";
+ unit = UNIT_MM;
+ break;
+
+ default:
+ do
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* invalid board unit (" << aUnit << ")";
+ errormsg = ostr.str();
+
+ return false;
+ } while( 0 );
+
+ break;
+ }
+
+ // iterate through all owned OUTLINE objects (except IDF3_COMP_OUTLINE)
+ // and set to the same unit
+
+ olnBoard.SetUnit( aUnit );
+
+ do
+ {
+ std::map< std::string, OTHER_OUTLINE*>::iterator its = olnOther.begin();
+ std::map< std::string, OTHER_OUTLINE*>::iterator ite = olnOther.end();
+
+ while( its != ite )
+ {
+ its->second->SetUnit( aUnit );
+ ++its;
+ }
+
+ } while( 0 );
+
+ do
+ {
+ std::list<ROUTE_OUTLINE*>::iterator its = olnRoute.begin();
+ std::list<ROUTE_OUTLINE*>::iterator ite = olnRoute.end();
+
+ while( its != ite )
+ {
+ (*its)->SetUnit( aUnit );
+ ++its;
+ }
+
+ } while( 0 );
+
+ do
+ {
+ std::list<PLACE_OUTLINE*>::iterator its = olnPlace.begin();
+ std::list<PLACE_OUTLINE*>::iterator ite = olnPlace.end();
+
+ while( its != ite )
+ {
+ (*its)->SetUnit( aUnit );
+ ++its;
+ }
+
+ } while( 0 );
+
+ do
+ {
+ std::list<ROUTE_KO_OUTLINE*>::iterator its = olnRouteKeepout.begin();
+ std::list<ROUTE_KO_OUTLINE*>::iterator ite = olnRouteKeepout.end();
+
+ while( its != ite )
+ {
+ (*its)->SetUnit( aUnit );
+ ++its;
+ }
+
+ } while( 0 );
+
+ do
+ {
+ std::list<VIA_KO_OUTLINE*>::iterator its = olnViaKeepout.begin();
+ std::list<VIA_KO_OUTLINE*>::iterator ite = olnViaKeepout.end();
+
+ while( its != ite )
+ {
+ (*its)->SetUnit( aUnit );
+ ++its;
+ }
+
+ } while( 0 );
+
+ do
+ {
+ std::list<PLACE_KO_OUTLINE*>::iterator its = olnPlaceKeepout.begin();
+ std::list<PLACE_KO_OUTLINE*>::iterator ite = olnPlaceKeepout.end();
+
+ while( its != ite )
+ {
+ (*its)->SetUnit( aUnit );
+ ++its;
+ }
+
+ } while( 0 );
+
+ do
+ {
+ std::multimap<std::string, GROUP_OUTLINE*>::iterator its = olnGroup.begin();
+ std::multimap<std::string, GROUP_OUTLINE*>::iterator ite = olnGroup.end();
+
+ while( its != ite )
+ {
+ its->second->SetUnit( aUnit );
+ ++its;
+ }
+
+ } while( 0 );
+
+ //iterate through all owned IDF3_COMP_OUTLINE objects and
+ // set to the same unit IF convert = true
+ if( convert )
+ {
+ std::map<std::string, IDF3_COMP_OUTLINE*>::iterator its = compOutlines.begin();
+ std::map<std::string, IDF3_COMP_OUTLINE*>::iterator ite = compOutlines.end();
+
+ while( its != ite )
+ {
+ its->second->SetUnit( aUnit );
+ ++its;
+ }
+
+ }
+
+ return true;
+}
+
+
+IDF3::IDF_UNIT IDF3_BOARD::GetUnit( void )
+{
+ return unit;
+}
+
+
+bool IDF3_BOARD::SetBoardThickness( double aBoardThickness )
+{
+ if( aBoardThickness <= 0.0 )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): ";
+ ostr << "board thickness (" << aBoardThickness << ") must be > 0";
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ if(! olnBoard.SetThickness( aBoardThickness ) )
+ {
+ errormsg = olnBoard.GetError();
+ return false;
+ }
+
+ return true;
+}
+
+
+double IDF3_BOARD::GetBoardThickness( void )
+{
+ return olnBoard.GetThickness();
+}
+
+
+// read the DRILLED HOLES section
+void IDF3_BOARD::readBrdDrills( std::ifstream& aBoardFile, IDF3::FILE_STATE& aBoardState )
+{
+ IDF_DRILL_DATA drill;
+
+ while( drill.read( aBoardFile, unit, aBoardState, idfVer ) )
+ {
+ IDF_DRILL_DATA *dp = new IDF_DRILL_DATA;
+ *dp = drill;
+
+ if( AddDrill( dp ) == NULL )
+ {
+ delete dp;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "\n* BUG: could not add drill data; cannot continue reading the file" ) );
+ }
+ }
+
+ return;
+}
+
+
+// read the NOTES section
+void IDF3_BOARD::readBrdNotes( std::ifstream& aBoardFile, IDF3::FILE_STATE& aBoardState )
+{
+ IDF_NOTE note;
+
+ while( note.readNote( aBoardFile, aBoardState, unit ) )
+ {
+ IDF_NOTE *np = new IDF_NOTE;
+ *np = note;
+ notes.push_back( np );
+ }
+
+ return;
+}
+
+
+// read the component placement section
+void IDF3_BOARD::readBrdPlacement( std::ifstream& aBoardFile, IDF3::FILE_STATE& aBoardState, bool aNoSubstituteOutlines )
+{
+ IDF3_COMP_OUTLINE_DATA oldata;
+
+ while( oldata.readPlaceData( aBoardFile, aBoardState, this, idfVer, aNoSubstituteOutlines ) );
+
+ return;
+}
+
+
+// read the board HEADER
+void IDF3_BOARD::readBrdHeader( std::ifstream& aBoardFile, IDF3::FILE_STATE& aBoardState )
+{
+ std::string iline; // the input line
+ bool isComment; // true if a line just read in is a comment line
+ std::streampos pos;
+ int idx = 0;
+ bool quoted = false;
+ std::string token;
+
+ // RECORD 1: ".HEADER" must be the very first line
+ while( !FetchIDFLine( aBoardFile, iline, isComment, pos ) && aBoardFile.good() );
+
+ if( !aBoardFile.good() )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "problems reading board header" ) );
+
+ if( isComment )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: first line must be .HEADER\n" ) );
+
+ if( !CompareToken( ".HEADER", iline ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification:\n"
+ "* first line must be .HEADER and have no quotes or trailing text" ) );
+
+ // RECORD 2:
+ // File Type [str]: BOARD_FILE (PANEL_FILE not supported)
+ // IDF Version Number [float]: must be 3.0
+ // Source System [str]: ignored
+ // Date [str]: ignored
+ // Board File Version [int]: ignored
+ while( !FetchIDFLine( aBoardFile, iline, isComment, pos ) && aBoardFile.good() );
+
+ if( !aBoardFile.good() )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "problems reading board header, RECORD 2" ) );
+
+ if( isComment )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: comment within .HEADER section" ) );
+
+ idx = 0;
+ GetIDFString( iline, token, quoted, idx );
+
+ if( quoted )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification:\n"
+ "* File Type in HEADER section must not be in quotes" ) );
+
+ if( !CompareToken( "BOARD_FILE", token ) )
+ {
+ ERROR_IDF;
+
+ if( CompareToken( "PANEL_FILE", token ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "not a board file\n"
+ "* PANEL_FILE is not supported (expecting BOARD_FILE)" ) );
+ else
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Expecting string: BOARD_FILE" ) );
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: HEADER section, RECORD 2: no FIELD 2" ) );
+
+ if( quoted )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: IDF Version must not be in quotes" ) );
+
+ if( !token.compare( "3.0" ) || !token.compare( "3." ) || !token.compare( "3" ) )
+ idfVer = IDF_V3;
+ else if( !token.compare( "2.0" ) || !token.compare( "2." ) || !token.compare( "2" ) )
+ idfVer = IDF_V2;
+ else
+ {
+ ostringstream ostr;
+
+ ostr << "unsupported IDF version\n";
+ ostr << "* Expecting version to be a variant of '3.0', '2.0' (value: '" << token << "')\n";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification:\n"
+ "* HEADER section, RECORD 2, FIELD 3: no Source System string" ) );
+
+ brdSource = token;
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification:\n"
+ "* HEADER section, RECORD 2, FIELD 4: no Date string" ) );
+
+ brdDate = token;
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification:\n"
+ "* HEADER section, RECORD 2, FIELD 5: no Board File Version number" ) );
+
+ std::istringstream istr;
+ istr.str( token );
+
+ istr >> brdFileVersion;
+
+ if( istr.fail() )
+ {
+ ERROR_IDF << "invalid Board File Version in header\n";
+ cerr << "* Setting default version of 1\n";
+ brdFileVersion = 1;
+ }
+
+ if( quoted )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification:\n"
+ "* HEADER section, RECORD 2, FIELD 5: Board File Version must not be in quotes" ) );
+
+ // RECORD 3:
+ // Board Name [str]: stored
+ // Units [str]: MM or THOU
+ while( !FetchIDFLine( aBoardFile, iline, isComment, pos ) && aBoardFile.good() );
+
+ if( !aBoardFile.good() )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "\n* problems reading board header, RECORD 2" ) );
+
+ if( isComment )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: comment within .HEADER section" ) );
+
+ idx = 0;
+ GetIDFString( iline, token, quoted, idx );
+
+ boardName = token;
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification:\n"
+ "* HEADER section, RECORD 3, FIELD 1: no Board Name" ) );
+
+ if( quoted )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification:\n"
+ "* HEADER section, RECORD 3, FIELD 2: UNIT may not be in quotes" ) );
+
+ if( CompareToken( "MM", token ) )
+ {
+ unit = IDF3::UNIT_MM;
+ }
+ else if( CompareToken( "THOU", token ) )
+ {
+ unit = IDF3::UNIT_THOU;
+ }
+ else if( ( idfVer == IDF_V2 ) && CompareToken( "TNM", token ) )
+ {
+ unit = IDF3::UNIT_TNM;
+ }
+ else
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* HEADER section, RECORD 3, FIELD 2: expecting MM or THOU (got '" << token << "')\n";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ olnBoard.SetUnit( unit );
+
+ // RECORD 4:
+ // .END_HEADER
+ while( !FetchIDFLine( aBoardFile, iline, isComment, pos ) && aBoardFile.good() );
+
+ if( ( !aBoardFile.good() && !aBoardFile.eof() ) || iline.empty() )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "problems reading board header, RECORD 4" ) );
+
+ if( isComment )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: comment within .HEADER section\n" ) );
+
+ if( !CompareToken( ".END_HEADER", iline ) )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* Violation of specification: expected .END_HEADER\n";
+ ostr << "* line: '" << iline << "'";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ aBoardState = IDF3::FILE_HEADER;
+ return;
+}
+
+
+// read individual board sections; pay attention to IDFv3 section specifications
+void IDF3_BOARD::readBrdSection( std::ifstream& aBoardFile, IDF3::FILE_STATE& aBoardState,
+ bool aNoSubstituteOutlines )
+{
+ std::list< std::string > comments; // comments associated with a section
+
+ // Reads in .SECTION_ID or #COMMENTS
+ // Expected SECTION IDs:
+ // .BOARD_OUTLINE
+ // .PANEL_OUTLINE (NOT SUPPORTED)
+ // .OTHER_OUTLINE
+ // .ROUTE_OUTLINE
+ // .PLACE_OUTLINE
+ // .ROUTE_KEEPOUT
+ // .VIA_KEEPOUT
+ // .PLACE_KEEPOUT
+ // .PLACE_REGION
+ // .DRILLED_HOLES
+ // .NOTES
+ // .PLACEMENT
+ std::string iline; // the input line
+ bool isComment; // true if a line just read in is a comment line
+ std::streampos pos;
+ int idx = 0;
+ bool quoted = false;
+ std::string token;
+
+ while( aBoardFile.good() )
+ {
+ while( !FetchIDFLine( aBoardFile, iline, isComment, pos ) && aBoardFile.good() );
+
+ if( !aBoardFile.good() )
+ {
+ if( aBoardFile.eof() && aBoardState >= IDF3::FILE_HEADER && aBoardState < IDF3::FILE_INVALID )
+ {
+ if( !comments.empty() )
+ ERROR_IDF << "[warning]: trailing comments in IDF file (comments will be lost)\n";
+
+ return;
+ }
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "problems reading board section" ) );
+ }
+
+ if( isComment )
+ {
+ comments.push_back( iline );
+ continue;
+ }
+
+ // This must be a header
+ GetIDFString( iline, token, quoted, idx );
+
+ if( quoted )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* Violation of specification: quoted string where SECTION HEADER expected\n";
+ ostr << "* line: '" << iline << "'";
+ ostr << "* position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( CompareToken( ".BOARD_OUTLINE", token ) )
+ {
+ if( aBoardState != IDF3::FILE_HEADER )
+ {
+ aBoardState = IDF3::FILE_INVALID;
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: no HEADER section" ) );
+ }
+
+ olnBoard.readData( aBoardFile, iline, idfVer );
+
+ if( !comments.empty() )
+ {
+ std::list<std::string>::iterator its = comments.begin();
+ std::list<std::string>::iterator ite = comments.end();
+
+ while( its != ite )
+ {
+ olnBoard.AddComment( *its );
+ ++its;
+ }
+ }
+
+ aBoardState = IDF3::FILE_OUTLINE;
+ return;
+ }
+
+ if( CompareToken( ".PANEL_OUTLINE", token ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "PANEL_OUTLINE not supported" ) );
+
+ if( CompareToken( ".OTHER_OUTLINE", token ) )
+ {
+ if( aBoardState != IDF3::FILE_OUTLINE )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: expecting .BOARD_OUTLINE, have .OTHER_OUTLINE" ) );
+
+ OTHER_OUTLINE* op = new OTHER_OUTLINE( this );
+
+ if( op == NULL )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "could not create OTHER_OUTLINE object" ) );
+
+ op->SetUnit( unit );
+ op->readData( aBoardFile, iline, idfVer );
+
+ if( !comments.empty() )
+ {
+ std::list<std::string>::iterator its = comments.begin();
+ std::list<std::string>::iterator ite = comments.end();
+
+ while( its != ite )
+ {
+ op->AddComment( *its );
+ ++its;
+ }
+ }
+
+ if( olnOther.insert( pair<string, OTHER_OUTLINE*>(op->GetOutlineIdentifier(), op) ).second == false )
+ {
+ ostringstream ostr;
+ ostr << "invalid IDF file\n";
+ ostr << "* Violation of specification. Non-unique ID in OTHER_OUTLINE '";
+ ostr << op->GetOutlineIdentifier() << "'\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* pos: " << pos;
+ delete op;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ return;
+ }
+
+ if( CompareToken( ".ROUTE_OUTLINE", token ) )
+ {
+ if( aBoardState != IDF3::FILE_OUTLINE )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: expecting .BOARD_OUTLINE, have .ROUTE_OUTLINE" ) );
+
+ ROUTE_OUTLINE* op = new ROUTE_OUTLINE( this );
+
+ if( op == NULL )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "could not create ROUTE_OUTLINE object" ) );
+
+ op->SetUnit( unit );
+ op->readData( aBoardFile, iline, idfVer );
+
+ if( !comments.empty() )
+ {
+ std::list<std::string>::iterator its = comments.begin();
+ std::list<std::string>::iterator ite = comments.end();
+
+ while( its != ite )
+ {
+ op->AddComment( *its );
+ ++its;
+ }
+ }
+
+ olnRoute.push_back( op );
+
+ return;
+ }
+
+ if( CompareToken( ".PLACE_OUTLINE", token ) )
+ {
+ if( aBoardState != IDF3::FILE_OUTLINE )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: expecting .BOARD_OUTLINE, have .PLACE_OUTLINE" ) );
+
+ PLACE_OUTLINE* op = new PLACE_OUTLINE( this );
+
+ if( op == NULL )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "could not create PLACE_OUTLINE object" ) );
+
+ op->SetUnit( unit );
+ op->readData( aBoardFile, iline, idfVer );
+
+ if( !comments.empty() )
+ {
+ std::list<std::string>::iterator its = comments.begin();
+ std::list<std::string>::iterator ite = comments.end();
+
+ while( its != ite )
+ {
+ op->AddComment( *its );
+ ++its;
+ }
+ }
+
+ olnPlace.push_back( op );
+
+ return;
+ }
+
+ if( CompareToken( ".ROUTE_KEEPOUT", token ) )
+ {
+ if( aBoardState != IDF3::FILE_OUTLINE )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: expecting .BOARD_OUTLINE, have .ROUTE_KEEPOUT" ) );
+
+ ROUTE_KO_OUTLINE* op = new ROUTE_KO_OUTLINE( this );
+
+ if( op == NULL )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "could not create ROUTE_KEEPOUT object" ) );
+
+ op->SetUnit( unit );
+ op->readData( aBoardFile, iline, idfVer );
+
+ if( !comments.empty() )
+ {
+ std::list<std::string>::iterator its = comments.begin();
+ std::list<std::string>::iterator ite = comments.end();
+
+ while( its != ite )
+ {
+ op->AddComment( *its );
+ ++its;
+ }
+ }
+
+ olnRouteKeepout.push_back( op );
+
+ return;
+ }
+
+ if( CompareToken( ".VIA_KEEPOUT", token ) )
+ {
+ if( aBoardState != IDF3::FILE_OUTLINE )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: expecting .BOARD_OUTLINE, have .VIA_KEEPOUT" ) );
+
+ VIA_KO_OUTLINE* op = new VIA_KO_OUTLINE( this );
+
+ if( op == NULL )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "could not create VIA_KEEPOUT object" ) );
+
+ op->SetUnit( unit );
+ op->readData( aBoardFile, iline, idfVer );
+
+ if( !comments.empty() )
+ {
+ std::list<std::string>::iterator its = comments.begin();
+ std::list<std::string>::iterator ite = comments.end();
+
+ while( its != ite )
+ {
+ op->AddComment( *its );
+ ++its;
+ }
+ }
+
+ olnViaKeepout.push_back( op );
+
+ return;
+ }
+
+ if( CompareToken( ".PLACE_KEEPOUT", token ) )
+ {
+ if( aBoardState != IDF3::FILE_OUTLINE )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: expecting .BOARD_OUTLINE, have .PLACE_KEEPOUT" ) );
+
+ PLACE_KO_OUTLINE* op = new PLACE_KO_OUTLINE( this );
+
+ if( op == NULL )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "could not create PLACE_KEEPOUT object" ) );
+
+ op->SetUnit( unit );
+ op->readData( aBoardFile, iline, idfVer );
+
+ if( !comments.empty() )
+ {
+ std::list<std::string>::iterator its = comments.begin();
+ std::list<std::string>::iterator ite = comments.end();
+
+ while( its != ite )
+ {
+ op->AddComment( *its );
+ ++its;
+ }
+ }
+
+ olnPlaceKeepout.push_back( op );
+
+ return;
+ }
+
+ if( CompareToken( ".PLACE_REGION", token ) )
+ {
+ if( aBoardState != IDF3::FILE_OUTLINE )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: expecting .BOARD_OUTLINE, have .PLACE_REGION" ) );
+
+ GROUP_OUTLINE* op = new GROUP_OUTLINE( this );
+
+ if( op == NULL )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "could not create PLACE_REGION object" ) );
+
+ op->SetUnit( unit );
+ op->readData( aBoardFile, iline, idfVer );
+
+ if( !comments.empty() )
+ {
+ std::list<std::string>::iterator its = comments.begin();
+ std::list<std::string>::iterator ite = comments.end();
+
+ while( its != ite )
+ {
+ op->AddComment( *its );
+ ++its;
+ }
+ }
+
+ olnGroup.insert( pair<string, GROUP_OUTLINE*>(op->GetGroupName(), op) );
+
+ return;
+ }
+
+ if( CompareToken( ".DRILLED_HOLES", token ) )
+ {
+ if( aBoardState != IDF3::FILE_OUTLINE )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: expecting .BOARD_OUTLINE, have .DRILLED_HOLES" ) );
+
+ readBrdDrills( aBoardFile, aBoardState );
+
+ if( !comments.empty() )
+ {
+ std::list<std::string>::iterator its = comments.begin();
+ std::list<std::string>::iterator ite = comments.end();
+
+ while( its != ite )
+ {
+ drillComments.push_back( *its );
+ ++its;
+ }
+ }
+
+ return;
+ }
+
+ if( CompareToken( ".NOTES", token ) )
+ {
+ if( aBoardState != IDF3::FILE_OUTLINE )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: expecting .BOARD_OUTLINE, have .NOTES" ) );
+
+ if( idfVer < IDF_V3 )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDFv2 file\n"
+ "* Violation of specification: NOTES section not in specification" ) );
+
+ readBrdNotes( aBoardFile, aBoardState );
+
+ if( !comments.empty() )
+ {
+ std::list<std::string>::iterator its = comments.begin();
+ std::list<std::string>::iterator ite = comments.end();
+
+ while( its != ite )
+ {
+ noteComments.push_back( *its );
+ ++its;
+ }
+ }
+
+ return;
+ }
+
+ if( CompareToken( ".PLACEMENT", token ) )
+ {
+ if( aBoardState != IDF3::FILE_OUTLINE )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: expecting .BOARD_OUTLINE, have .PLACEMENT" ) );
+
+ readBrdPlacement( aBoardFile, aBoardState, aNoSubstituteOutlines );
+
+ if( !comments.empty() )
+ {
+ std::list<std::string>::iterator its = comments.begin();
+ std::list<std::string>::iterator ite = comments.end();
+
+ while( its != ite )
+ {
+ placeComments.push_back( *its );
+ ++its;
+ }
+ }
+
+ return;
+ }
+ } // while( aBoardFile.good()
+
+ return;
+} // readBrdSection()
+
+
+// read the board file data
+void IDF3_BOARD::readBoardFile( const std::string& aFileName, bool aNoSubstituteOutlines )
+{
+ std::ifstream brd;
+
+ brd.exceptions ( std::ifstream::badbit );
+
+ try
+ {
+ brd.open( aFileName.c_str(), std::ios_base::in | std::ios_base::binary );
+
+ if( !brd.is_open() )
+ {
+ ostringstream ostr;
+ ostr << "\n* could not open file: '" << aFileName << "'";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ std::string iline; // the input line
+ bool isComment; // true if a line just read in is a comment line
+ std::streampos pos;
+ IDF3::FILE_STATE state = IDF3::FILE_START;
+
+ // note: as per IDFv3 specification:
+ // "The Header section must be the first section in the file, the second
+ // section must be the Outline section, and the last section must be the
+ // Placement section. All other sections may be in any order."
+
+ // further notes: Except for the HEADER section, sections may be preceeded by
+ // comment lines which will be copied back out on write(). No comments may
+ // be associated with the board file itself since the only logical location
+ // for unambiguous association is at the end of the file, which is inconvenient
+ // for large files.
+
+ readBrdHeader( brd, state );
+
+ // read the various sections
+ while( state != IDF3::FILE_PLACEMENT && brd.good() )
+ readBrdSection( brd, state, aNoSubstituteOutlines );
+
+ if( !brd.good() )
+ {
+ // check if we have valid data
+ if( brd.eof() && state >= IDF3::FILE_OUTLINE && state < IDF3::FILE_INVALID )
+ {
+ brd.close();
+ return;
+ }
+
+ brd.close();
+
+ ostringstream ostr;
+ ostr << "\n* empty IDF file: '" << aFileName << "'";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( brd.good() && state == IDF3::FILE_PLACEMENT )
+ {
+ // read in any trailing lines and report on ignored comments (minor fault)
+ // and any non-comment item (non-compliance with IDFv3)
+ while( brd.good() )
+ {
+ while( !FetchIDFLine( brd, iline, isComment, pos ) && brd.good() );
+
+ // normally this is a fault but we have all the data in accordance with specs
+ if( ( !brd.good() && !brd.eof() ) || iline.empty() )
+ break;
+
+ if( isComment )
+ {
+ ERROR_IDF << "[warning]: trailing comments after PLACEMENT\n";
+ }
+ else
+ {
+ ostringstream ostr;
+ ostr << "\n* problems reading file: '" << aFileName << "'";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: non-comment lines after PLACEMENT section" ) );
+ }
+ }
+ }
+ }
+ catch( const std::exception& e )
+ {
+ brd.exceptions ( std::ios_base::goodbit );
+
+ if( brd.is_open() )
+ brd.close();
+
+ throw;
+ }
+
+ brd.close();
+ return;
+} // readBoardFile()
+
+
+// read the library sections (outlines)
+void IDF3_BOARD::readLibSection( std::ifstream& aLibFile, IDF3::FILE_STATE& aLibState, IDF3_BOARD* aBoard )
+{
+ if( aBoard == NULL )
+ {
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "\n* BUG: invoked with NULL reference aBoard" ) );
+ }
+
+ std::list< std::string > comments; // comments associated with a section
+
+ // Reads in .ELECTRICAL, .MECHANICAL or #COMMENTS
+ std::string iline; // the input line
+ bool isComment; // true if a line just read in is a comment line
+ std::streampos pos;
+ int idx = 0;
+ bool quoted = false;
+ std::string token;
+ IDF3_COMP_OUTLINE *pout = new IDF3_COMP_OUTLINE( this );
+
+ if( !pout )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "\n* memory allocation failure" ) );
+
+ while( aLibFile.good() )
+ {
+ while( !FetchIDFLine( aLibFile, iline, isComment, pos ) && aLibFile.good() );
+
+ if( !aLibFile.good() && !aLibFile.eof() )
+ {
+ delete pout;
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "problems reading library section" ) );
+ }
+
+ // no data was read; this only happens at eof()
+ if( iline.empty() )
+ {
+ delete pout;
+ return;
+ }
+
+ if( isComment )
+ {
+ comments.push_back( iline );
+ continue;
+ }
+
+ // This must be a header
+ GetIDFString( iline, token, quoted, idx );
+
+ if( quoted )
+ {
+ ostringstream ostr;
+ ostr << "invalid IDF library\n";
+ ostr << "* Violation of specification: quoted string where .ELECTRICAL or .MECHANICAL expected\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* pos: " << pos;
+ delete pout;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( CompareToken( ".ELECTRICAL", token ) || CompareToken( ".MECHANICAL", token ) )
+ {
+ pout->readData( aLibFile, token, idfVer );
+
+ if( !comments.empty() )
+ {
+ std::list<std::string>::iterator its = comments.begin();
+ std::list<std::string>::iterator ite = comments.end();
+
+ while( its != ite )
+ {
+ pout->AddComment( *its );
+ ++its;
+ }
+ }
+
+ IDF3_COMP_OUTLINE* cop = aBoard->GetComponentOutline( pout->GetUID() );
+
+ if( cop == NULL )
+ {
+ compOutlines.insert( pair<const std::string, IDF3_COMP_OUTLINE*>( pout->GetUID(), pout ) );
+ }
+ else
+ {
+ if( MatchCompOutline( pout, cop ) )
+ {
+ delete pout;
+ // everything is fine; the outlines are genuine duplicates
+ return;
+ }
+
+ ostringstream ostr;
+ ostr << "invalid IDF library\n";
+ ostr << "duplicate Component Outline: '" << pout->GetUID() << "'\n";
+ ostr << "* Violation of specification: multiple outlines have the same GEOM and PART name\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* pos: " << pos;
+ delete pout;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ return;
+ }
+ else
+ {
+ ostringstream ostr;
+ ostr << "invalid IDF library\n";
+ ostr << "* Expecting .ELECTRICAL or .MECHANICAL, got '" << token << "'\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* pos: " << pos;
+ delete pout;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+ }
+
+ delete pout;
+
+ if( !aLibFile.eof() )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "problems reading IDF library file" ) );
+
+ return;
+}
+
+
+// read the library HEADER
+void IDF3_BOARD::readLibHeader( std::ifstream& aLibFile, IDF3::FILE_STATE& aLibState )
+{
+ std::string iline; // the input line
+ bool isComment; // true if a line just read in is a comment line
+ std::streampos pos;
+ int idx = 0;
+ bool quoted = false;
+ std::string token;
+
+ // RECORD 1: ".HEADER" must be the very first line
+ while( !FetchIDFLine( aLibFile, iline, isComment, pos ) && aLibFile.good() );
+
+ if( !aLibFile.good() )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF library file\n"
+ "* premature end of file (no HEADER)" ) );
+
+ if( isComment )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF library file\n"
+ "* Violation of specification: first line must be .HEADER" ) );
+
+ if( !CompareToken( ".HEADER", iline ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF library file\n"
+ "* Violation of specification:\n"
+ "* first line must be .HEADER and have no quotes or trailing text" ) );
+
+ // RECORD 2:
+ // File Type [str]: LIBRARY_FILE
+ // IDF Version Number [float]: must be 3.0
+ // Source System [str]: ignored
+ // Date [str]: ignored
+ // Library File Version [int]: ignored
+ while( !FetchIDFLine( aLibFile, iline, isComment, pos ) && aLibFile.good() );
+
+ if( !aLibFile.good() )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF library file\n"
+ "* premature end of HEADER" ) );
+
+ if( isComment )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF library file\n"
+ "* Violation of specification: comment within .HEADER section" ) );
+
+ idx = 0;
+ GetIDFString( iline, token, quoted, idx );
+
+ if( quoted )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF library file\n"
+ "* Violation of specification:\n"
+ "* file Type in HEADER section must not be in quotes" ) );
+
+ if( !CompareToken( "LIBRARY_FILE", token ) )
+ {
+ ostringstream ostr;
+ ostr << "invalid IDF library\n";
+ ostr << "* Expecting string: LIBRARY_FILE (got '" << token << "')\n";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF library file\n"
+ "* Violation of specification: HEADER section, RECORD 2: no FIELD 2" ) );
+
+ if( quoted )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF library file\n"
+ "* Violation of specification: IDF Version must not be in quotes" ) );
+
+ if( !token.compare( "3.0" ) || !token.compare( "3." ) || !token.compare( "3" ) )
+ idfVer = IDF_V3;
+ else if( !token.compare( "2.0" ) || !token.compare( "2." ) || !token.compare( "2" ) )
+ idfVer = IDF_V2;
+ else
+ {
+ ostringstream ostr;
+
+ ostr << "unsupported IDF version\n";
+ ostr << "* Expecting version to be a variant of '3.0', '2.0' (value: '" << token << "')\n";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF library file\n"
+ "* Violation of specification:\n"
+ "* HEADER section, RECORD 2, FIELD 3: no Source System string" ) );
+
+ libSource = token;
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF library file\n"
+ "* Violation of specification:\n"
+ "* HEADER section, RECORD 2, FIELD 4: no Date string" ) );
+
+ libDate = token;
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF library file\n"
+ "* Violation of specification:\n"
+ "* HEADER section, RECORD 2, FIELD 5: no Board File Version number" ) );
+
+ std::istringstream istr;
+ istr.str( token );
+
+ istr >> libFileVersion;
+
+ if( istr.fail() )
+ {
+ ERROR_IDF << "invalid Library File Version in header\n";
+ cerr << "* Setting default version of 1\n";
+ libFileVersion = 1;
+ }
+
+ if( quoted )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF library file\n"
+ "* Violation of specification:\n"
+ "* HEADER section, RECORD 2, FIELD 5: Library File Version must not be in quotes" ) );
+
+ // RECORD 3:
+ // .END_HEADER
+ while( !FetchIDFLine( aLibFile, iline, isComment, pos ) && aLibFile.good() );
+
+ if( ( !aLibFile.good() && !aLibFile.eof() ) || iline.empty() )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "problems reading library header, RECORD 3" ) );
+
+ if( isComment )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF library file\n"
+ "* Violation of specification: comment within .HEADER section" ) );
+
+ if( !CompareToken( ".END_HEADER", iline ) )
+ {
+ ostringstream ostr;
+ ostr << "invalid IDF header\n";
+ ostr << "* Violation of specification: expected .END_HEADER (got '" << iline << "')\n";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ aLibState = IDF3::FILE_HEADER;
+ return;
+}
+
+
+// read the library file data
+void IDF3_BOARD::readLibFile( const std::string& aFileName )
+{
+ std::ifstream lib;
+
+ lib.exceptions ( std::ifstream::badbit );
+
+ try
+ {
+ lib.open( aFileName.c_str(), std::ios_base::in | std::ios_base::binary );
+
+ IDF3::FILE_STATE state = IDF3::FILE_START;
+
+ readLibHeader( lib, state );
+
+ while( lib.good() ) readLibSection( lib, state, this );
+ }
+ catch( const std::exception& e )
+ {
+ lib.exceptions ( std::ios_base::goodbit );
+
+ if( lib.is_open() )
+ lib.close();
+
+ throw;
+ }
+
+ lib.close();
+ return;
+}
+
+
+bool IDF3_BOARD::ReadFile( const wxString& aFullFileName, bool aNoSubstituteOutlines )
+{
+ // 1. Check that the file extension is 'emn'
+ // 2. Check if a file with extension 'emp' exists and read it
+ // 3. Open the specified filename and read it
+
+ std::string fname = TO_UTF8( aFullFileName );
+
+ wxFileName brdname( aFullFileName );
+ wxFileName libname( aFullFileName );
+
+ brdname.SetExt( wxT( "emn" ) );
+ libname.SetExt( wxT( "emp" ) );
+
+ std::string bfname = TO_UTF8( aFullFileName );
+
+ try
+ {
+ if( !brdname.IsOk() )
+ {
+ ostringstream ostr;
+ ostr << "\n* invalid file name: '" << bfname << "'";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !brdname.FileExists() )
+ {
+ ostringstream ostr;
+ ostr << "\n* no such file: '" << bfname << "'";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !brdname.IsFileReadable() )
+ {
+ ostringstream ostr;
+ ostr << "\n* cannot read file: '" << bfname << "'";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ bfname = TO_UTF8( brdname.GetFullPath() );
+ std::string lfname = TO_UTF8( libname.GetFullPath() );
+
+ if( !libname.FileExists() )
+ {
+ // NOTE: Since this is a common case we simply proceed
+ // with the assumption that there is no library file;
+ // however we print a message to inform the user.
+ ERROR_IDF;
+ cerr << "no associated library file (*.emp)\n";
+ }
+ else if( !libname.IsFileReadable() )
+ {
+ ostringstream ostr;
+ ostr << "\n* cannot read library file: '" << lfname << "'";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+ else
+ {
+ // read the library file before proceeding
+ readLibFile( lfname );
+ }
+
+ // read the board file
+ readBoardFile( bfname, aNoSubstituteOutlines );
+ }
+ catch( const std::exception& e )
+ {
+ Clear();
+ errormsg = e.what();
+
+ return false;
+ }
+
+ return true;
+}
+
+
+// write the library file data
+bool IDF3_BOARD::writeLibFile( const std::string& aFileName )
+{
+ std::ofstream lib;
+ lib.exceptions( std::ofstream::failbit );
+
+ try
+ {
+ lib.open( aFileName.c_str(), std::ios_base::out );
+
+ wxDateTime tdate( time( NULL ) );
+
+ if( idfSource.empty() )
+ idfSource = "KiCad-IDF Framework";
+
+ ostringstream fileDate;
+ fileDate << setfill( '0' ) << setw(4) << tdate.GetYear();
+ fileDate << "/" << setw(2) << tdate.GetMonth() << "/" << tdate.GetDay();
+ fileDate << "." << tdate.GetHour() << ":" << tdate.GetMinute() << ":" << tdate.GetSecond();
+ libDate = fileDate.str();
+
+ lib << ".HEADER\n";
+ lib << "LIBRARY_FILE 3.0 \"Created by " << idfSource;
+ lib << "\" " << libDate << " " << (++libFileVersion) << "\n";
+ lib << ".END_HEADER\n\n";
+
+ std::map< std::string, IDF3_COMP_OUTLINE*>::iterator its = compOutlines.begin();
+ std::map< std::string, IDF3_COMP_OUTLINE*>::iterator ite = compOutlines.end();
+
+ while( its != ite )
+ {
+ its->second->writeData( lib );
+ ++its;
+ }
+
+ }
+ catch( const std::exception& e )
+ {
+ lib.exceptions( std::ios_base::goodbit );
+
+ if( lib.is_open() )
+ lib.close();
+
+ throw;
+ }
+
+ lib.close();
+
+ return true;
+}
+
+// write the board file data
+void IDF3_BOARD::writeBoardFile( const std::string& aFileName )
+{
+ std::ofstream brd;
+ brd.exceptions( std::ofstream::failbit );
+
+ try
+ {
+ brd.open( aFileName.c_str(), std::ios_base::out );
+
+ wxDateTime tdate( time( NULL ) );
+
+ if( idfSource.empty() )
+ idfSource = "KiCad-IDF Framework";
+
+ ostringstream fileDate;
+ fileDate << setfill( '0' ) << setw(4) << tdate.GetYear();
+ fileDate << "/" << setw(2) << tdate.GetMonth() << "/" << tdate.GetDay();
+ fileDate << "." << tdate.GetHour() << ":" << tdate.GetMinute() << ":" << tdate.GetSecond();
+ brdDate = fileDate.str();
+
+ brd << ".HEADER\n";
+ brd << "BOARD_FILE 3.0 \"Created by " << idfSource;
+ brd << "\" " << brdDate << " " << (++brdFileVersion) << "\n";
+
+ if( boardName.empty() )
+ brd << "\"BOARD WITH NO NAME\" ";
+ else
+ brd << "\"" << boardName << "\" ";
+
+ brd << setw(1) << setfill( ' ' );
+
+ if( unit == IDF3::UNIT_MM )
+ brd << "MM\n";
+ else
+ brd << "THOU\n";
+
+ brd << ".END_HEADER\n\n";
+
+ // write the BOARD_OUTLINE
+ olnBoard.writeData( brd );
+
+ // OTHER outlines
+ do
+ {
+ std::map<std::string, OTHER_OUTLINE*>::iterator its = olnOther.begin();
+ std::map<std::string, OTHER_OUTLINE*>::iterator ite = olnOther.end();
+
+ while(its != ite )
+ {
+ its->second->writeData( brd );
+ ++its;
+ }
+
+ } while( 0 );
+
+ // ROUTE outlines
+ do
+ {
+ std::list<ROUTE_OUTLINE*>::iterator its = olnRoute.begin();
+ std::list<ROUTE_OUTLINE*>::iterator ite = olnRoute.end();
+
+ while( its != ite )
+ {
+ (*its)->writeData( brd );
+ ++its;
+ }
+
+ } while( 0 );
+
+ // PLACEMENT outlines
+ do
+ {
+ std::list<PLACE_OUTLINE*>::iterator its = olnPlace.begin();
+ std::list<PLACE_OUTLINE*>::iterator ite = olnPlace.end();
+
+ while( its != ite )
+ {
+ (*its)->writeData( brd );
+ ++its;
+ }
+
+ } while( 0 );
+
+ // ROUTE KEEPOUT outlines
+ do
+ {
+ std::list<ROUTE_KO_OUTLINE*>::iterator its = olnRouteKeepout.begin();
+ std::list<ROUTE_KO_OUTLINE*>::iterator ite = olnRouteKeepout.end();
+
+ while( its != ite )
+ {
+ (*its)->writeData( brd );
+ ++its;
+ }
+
+ } while( 0 );
+
+ // VIA KEEPOUT outlines
+ do
+ {
+ std::list<VIA_KO_OUTLINE*>::iterator its = olnViaKeepout.begin();
+ std::list<VIA_KO_OUTLINE*>::iterator ite = olnViaKeepout.end();
+
+ while( its != ite )
+ {
+ (*its)->writeData( brd );
+ ++its;
+ }
+
+ } while( 0 );
+
+ // PLACE KEEPOUT outlines
+ do
+ {
+ std::list<PLACE_KO_OUTLINE*>::iterator its = olnPlaceKeepout.begin();
+ std::list<PLACE_KO_OUTLINE*>::iterator ite = olnPlaceKeepout.end();
+
+ while( its != ite )
+ {
+ (*its)->writeData( brd );
+ ++its;
+ }
+
+ } while( 0 );
+
+ // PLACEMENT GROUP outlines
+ do
+ {
+ std::multimap<std::string, GROUP_OUTLINE*>::iterator its = olnGroup.begin();
+ std::multimap<std::string, GROUP_OUTLINE*>::iterator ite = olnGroup.end();
+
+ while( its != ite )
+ {
+ its->second->writeData( brd );
+ ++its;
+ }
+
+ } while( 0 );
+
+ // Drilled holes
+ do
+ {
+ std::list<std::string>::iterator itds = drillComments.begin();
+ std::list<std::string>::iterator itde = drillComments.end();
+
+ while( itds != itde )
+ {
+ brd << "# " << *itds << "\n";
+ ++itds;
+ }
+
+ brd << ".DRILLED_HOLES\n";
+
+ std::list<IDF_DRILL_DATA*>::iterator itbs = board_drills.begin();
+ std::list<IDF_DRILL_DATA*>::iterator itbe = board_drills.end();
+
+ while( itbs != itbe )
+ {
+ (*itbs)->write( brd, unit );
+ ++itbs;
+ }
+
+ std::map< std::string, IDF3_COMPONENT*>::iterator itcs = components.begin();
+ std::map< std::string, IDF3_COMPONENT*>::iterator itce = components.end();
+
+ while( itcs != itce )
+ {
+ itcs->second->writeDrillData( brd );
+ ++itcs;
+ }
+
+ brd << ".END_DRILLED_HOLES\n\n";
+ } while( 0 );
+
+ // Notes
+ if( !notes.empty() )
+ {
+ std::list<std::string>::iterator itncs = noteComments.begin();
+ std::list<std::string>::iterator itnce = noteComments.end();
+
+ while( itncs != itnce )
+ {
+ brd << "# " << *itncs << "\n";
+ ++itncs;
+ }
+
+ brd << ".NOTES\n";
+
+ std::list<IDF_NOTE*>::iterator itns = notes.begin();
+ std::list<IDF_NOTE*>::iterator itne = notes.end();
+
+ while( itns != itne )
+ {
+ (*itns)->writeNote( brd, unit );
+ ++itns;
+ }
+
+ brd << ".END_NOTES\n\n";
+
+ }
+
+ // Placement
+ if( !components.empty() )
+ {
+ std::list<std::string>::iterator itpcs = placeComments.begin();
+ std::list<std::string>::iterator itpce = placeComments.end();
+
+ while( itpcs != itpce )
+ {
+ brd << "# " << *itpcs << "\n";
+ ++itpcs;
+ }
+
+ std::map< std::string, IDF3_COMPONENT*>::iterator itcs = components.begin();
+ std::map< std::string, IDF3_COMPONENT*>::iterator itce = components.end();
+
+ // determine if there are any component outlines at all and avoid
+ // writing an empty PLACEMENT section if there are no outlines.
+ // this will cost a little time but prevents software such as
+ // CircuitWorks from segfaulting on an empty section.
+
+ bool hasOutlines = false;
+
+ while( itcs != itce )
+ {
+ if( itcs->second->GetOutlinesSize() > 0 )
+ {
+ itcs = components.begin();
+ hasOutlines = true;
+ break;
+ }
+
+ ++itcs;
+ }
+
+ if( hasOutlines )
+ {
+ brd << ".PLACEMENT\n";
+
+ while( itcs != itce )
+ {
+ itcs->second->writePlaceData( brd );
+ ++itcs;
+ }
+
+ brd << ".END_PLACEMENT\n";
+ }
+
+ }
+
+ }
+ catch( const std::exception& e )
+ {
+ brd.exceptions( std::ios_base::goodbit );
+
+ if( brd.is_open() )
+ brd.close();
+
+ throw;
+ }
+
+ brd.close();
+
+ return;
+}
+
+
+bool IDF3_BOARD::WriteFile( const wxString& aFullFileName, bool aUnitMM, bool aForceUnitFlag )
+{
+ if( aUnitMM != IDF3::UNIT_THOU )
+ setUnit( IDF3::UNIT_MM, aForceUnitFlag );
+ else
+ setUnit( IDF3::UNIT_THOU, aForceUnitFlag );
+
+ // 1. Check that the file extension is 'emn'
+ // 2. Write the *.emn file according to the IDFv3 spec
+ // 3. Write the *.emp file according to the IDFv3 spec
+
+ std::string fname = TO_UTF8( aFullFileName );
+
+ wxFileName brdname( aFullFileName );
+ wxFileName libname( aFullFileName );
+
+ brdname.SetExt( wxT( "emn" ) );
+ libname.SetExt( wxT( "emp" ) );
+
+ std::string bfname = TO_UTF8( aFullFileName );
+
+ try
+ {
+ if( !brdname.IsOk() )
+ {
+ ostringstream ostr;
+ ostr << "\n* invalid file name: '" << bfname << "'";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( brdname.FileExists() && !brdname.IsFileWritable() )
+ {
+ ostringstream ostr;
+ ostr << "cannot overwrite existing board file\n";
+ ostr << "* filename: '" << bfname << "'";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ bfname = TO_UTF8( brdname.GetFullPath() );
+ std::string lfname = TO_UTF8( libname.GetFullPath() );
+
+ if( libname.FileExists() && !libname.IsFileWritable() )
+ {
+ ostringstream ostr;
+ ostr << "cannot overwrite existing library file\n";
+ ostr << "* filename: '" << lfname << "'";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ writeLibFile( lfname );
+ writeBoardFile( bfname );
+
+ }
+ catch( const std::exception& e )
+ {
+ errormsg = e.what();
+
+ return false;
+ }
+
+ return true;
+}
+
+
+const std::string& IDF3_BOARD::GetIDFSource( void )
+{
+ return idfSource;
+}
+
+
+void IDF3_BOARD::SetIDFSource( const std::string& aIDFSource )
+{
+ idfSource = aIDFSource;
+ return;
+}
+
+const std::string& IDF3_BOARD::GetBoardSource( void )
+{
+ return brdSource;
+}
+
+const std::string& IDF3_BOARD::GetLibrarySource( void )
+{
+ return libSource;
+}
+
+const std::string& IDF3_BOARD::GetBoardDate( void )
+{
+ return brdDate;
+}
+
+const std::string& IDF3_BOARD::GetLibraryDate( void )
+{
+ return libDate;
+}
+
+int IDF3_BOARD::GetBoardVersion( void )
+{
+ return brdFileVersion;
+}
+
+bool IDF3_BOARD::SetBoardVersion( int aVersion )
+{
+ if( aVersion < 0 )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* board version (" << aVersion << ") must be >= 0";
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ brdFileVersion = aVersion;
+
+ return true;
+}
+
+
+int IDF3_BOARD::GetLibraryVersion( void )
+{
+ return libFileVersion;
+}
+
+
+bool IDF3_BOARD::SetLibraryVersion( int aVersion )
+{
+ if( aVersion < 0 )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* library version (" << aVersion << ") must be >= 0";
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ libFileVersion = aVersion;
+
+ return true;
+}
+
+
+double IDF3_BOARD::GetUserScale( void )
+{
+ return userScale;
+}
+
+
+bool IDF3_BOARD::SetUserScale( double aScaleFactor )
+{
+ if( aScaleFactor == 0.0 )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* BUG: user scale factor must not be 0";
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ userScale = aScaleFactor;
+ return true;
+}
+
+int IDF3_BOARD::GetUserPrecision( void )
+{
+ return userPrec;
+}
+
+bool IDF3_BOARD::SetUserPrecision( int aPrecision )
+{
+ if( aPrecision < 1 || aPrecision > 8 )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* precision value (" << aPrecision << ") must be 1..8";
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ userPrec = aPrecision;
+ return true;
+}
+
+
+void IDF3_BOARD::GetUserOffset( double& aXoff, double& aYoff )
+{
+ aXoff = userXoff;
+ aYoff = userYoff;
+ return;
+}
+
+
+void IDF3_BOARD::SetUserOffset( double aXoff, double aYoff )
+{
+ userXoff = aXoff;
+ userYoff = aYoff;
+ return;
+}
+
+
+bool IDF3_BOARD::AddBoardOutline( IDF_OUTLINE* aOutline )
+{
+ if( !olnBoard.AddOutline( aOutline ) )
+ {
+ errormsg = olnBoard.GetError();
+
+ return false;
+ }
+
+ return true;
+}
+
+
+bool IDF3_BOARD::DelBoardOutline( IDF_OUTLINE* aOutline )
+{
+ if( !olnBoard.DelOutline( aOutline ) )
+ {
+ errormsg = olnBoard.GetError();
+ return false;
+ }
+
+ return true;
+}
+
+
+bool IDF3_BOARD::DelBoardOutline( size_t aIndex )
+{
+ if( !olnBoard.DelOutline( aIndex ) )
+ {
+ errormsg = olnBoard.GetError();
+ return false;
+ }
+
+ return true;
+}
+
+
+size_t IDF3_BOARD::GetBoardOutlinesSize( void )
+{
+ return olnBoard.OutlinesSize();
+}
+
+
+BOARD_OUTLINE* IDF3_BOARD::GetBoardOutline( void )
+{
+ return &olnBoard;
+}
+
+
+const std::list< IDF_OUTLINE* >*const IDF3_BOARD::GetBoardOutlines( void )
+{
+ return olnBoard.GetOutlines();
+}
+
+
+IDF_DRILL_DATA* IDF3_BOARD::AddBoardDrill( double aDia, double aXpos, double aYpos,
+ IDF3::KEY_PLATING aPlating,
+ const std::string aHoleType,
+ IDF3::KEY_OWNER aOwner )
+{
+ IDF_DRILL_DATA* drill = new IDF_DRILL_DATA( aDia, aXpos, aYpos, aPlating,
+ "BOARD", aHoleType, aOwner );
+
+ if( drill != NULL )
+ board_drills.push_back( drill );
+
+ return drill;
+}
+
+IDF_DRILL_DATA* IDF3_BOARD::AddDrill( IDF_DRILL_DATA* aDrilledHole )
+{
+ if( !aDrilledHole )
+ return NULL;
+
+ // note: PANEL drills are essentially BOARD drills which
+ // the panel requires to be present
+ if( CompareToken( "BOARD", aDrilledHole->GetDrillRefDes() )
+ || CompareToken( "PANEL", aDrilledHole->GetDrillRefDes() ) )
+ {
+ board_drills.push_back( aDrilledHole );
+ return aDrilledHole;
+ }
+
+ return addCompDrill( aDrilledHole );
+}
+
+
+bool IDF3_BOARD::DelBoardDrill( double aDia, double aXpos, double aYpos )
+{
+ errormsg.clear();
+
+ std::list<IDF_DRILL_DATA*>::iterator sp = board_drills.begin();
+ std::list<IDF_DRILL_DATA*>::iterator ep = board_drills.end();
+ bool rval = false;
+
+ while( sp != ep )
+ {
+ if( (*sp)->Matches( aDia, aXpos, aYpos ) )
+ {
+#ifndef DISABLE_IDF_OWNERSHIP
+ IDF3::KEY_OWNER keyo = (*sp)->GetDrillOwner();
+
+ if( keyo == UNOWNED || ( keyo == MCAD && cadType == CAD_MECH )
+ || ( keyo == ECAD && cadType == CAD_ELEC ) )
+ {
+ rval = true;
+ delete *sp;
+ sp = board_drills.erase( sp );
+ continue;
+ }
+ else
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* ownership violation; drill owner (";
+
+ switch( keyo )
+ {
+ case ECAD:
+ ostr << "ECAD";
+ break;
+
+ case MCAD:
+ ostr << "MCAD";
+ break;
+
+ default:
+ ostr << "invalid: " << keyo;
+ break;
+ }
+
+ ostr << ") may not be modified by ";
+
+ if( cadType == CAD_MECH )
+ ostr << "MCAD";
+ else
+ ostr << "ECAD";
+
+ errormsg = ostr.str();
+
+ ++sp;
+ continue;
+ }
+#else
+ rval = true;
+ delete *sp;
+ sp = board_drills.erase( sp );
+ continue;
+#endif
+ }
+
+ ++sp;
+ }
+
+ return rval;
+}
+
+
+// a slot is a deficient representation of a kicad slotted hole;
+// it is usually associated with a component but IDFv3 does not
+// provide for such an association. Note: this mechanism must bypass
+// the BOARD_OUTLINE ownership rules
+bool IDF3_BOARD::AddSlot( double aWidth, double aLength, double aOrientation, double aX, double aY )
+{
+ if( aWidth < IDF_MIN_DIA_MM )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* slot width (" << aWidth << ") must be >= " << IDF_MIN_DIA_MM;
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ if( aLength < IDF_MIN_DIA_MM )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* slot length (" << aLength << ") must be >= " << IDF_MIN_DIA_MM;
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ IDF_POINT c[2]; // centers
+ IDF_POINT pt[4];
+
+ double a1 = aOrientation / 180.0 * M_PI;
+ double a2 = a1 + M_PI_2;
+ double d1 = aLength / 2.0;
+ double d2 = aWidth / 2.0;
+ double sa1 = sin( a1 );
+ double ca1 = cos( a1 );
+ double dsa2 = d2 * sin( a2 );
+ double dca2 = d2 * cos( a2 );
+
+ c[0].x = aX + d1 * ca1;
+ c[0].y = aY + d1 * sa1;
+
+ c[1].x = aX - d1 * ca1;
+ c[1].y = aY - d1 * sa1;
+
+ pt[0].x = c[0].x - dca2;
+ pt[0].y = c[0].y - dsa2;
+
+ pt[1].x = c[1].x - dca2;
+ pt[1].y = c[1].y - dsa2;
+
+ pt[2].x = c[1].x + dca2;
+ pt[2].y = c[1].y + dsa2;
+
+ pt[3].x = c[0].x + dca2;
+ pt[3].y = c[0].y + dsa2;
+
+ IDF_OUTLINE* outline = new IDF_OUTLINE;
+
+ if( outline == NULL )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* could not create an outline object";
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ // first straight run
+ IDF_SEGMENT* seg = new IDF_SEGMENT( pt[0], pt[1] );
+ outline->push( seg );
+ // first 180 degree cap
+ seg = new IDF_SEGMENT( c[1], pt[1], -180.0, true );
+ outline->push( seg );
+ // final straight run
+ seg = new IDF_SEGMENT( pt[2], pt[3] );
+ outline->push( seg );
+ // final 180 degree cap
+ seg = new IDF_SEGMENT( c[0], pt[3], -180.0, true );
+ outline->push( seg );
+
+ if( !olnBoard.addOutline( outline ) )
+ {
+ errormsg = olnBoard.GetError();
+ return false;
+ }
+
+ return true;
+}
+
+
+IDF_DRILL_DATA* IDF3_BOARD::addCompDrill( double aDia, double aXpos, double aYpos,
+ IDF3::KEY_PLATING aPlating,
+ const std::string aHoleType,
+ IDF3::KEY_OWNER aOwner,
+ const std::string& aRefDes )
+{
+ // first find the matching component; if it doesn't exist we must create it somehow -
+ // question is, do we need a component outline at this stage or can those be added later?
+ //
+ // Presumably we can create a component with no outline and add the outlines later.
+ // If a component is created and an outline specified but the outline is not loaded,
+ // we're screwed if (a) we have already read the library file (*.emp) or (b) we don't
+ // know the filename
+
+ std::string refdes = aRefDes;
+
+ // note: for components 'NOREFDES' would be assigned a Unique ID, but for holes
+ // there is no way of associating the hole with the correct entity (if any)
+ // so a hole added with "NOREFDES" goes to a generic component "NOREFDES"
+ if( refdes.empty() )
+ refdes = "NOREFDES";
+
+ // check if the target is BOARD or PANEL
+ if( CompareToken( "BOARD", refdes ) )
+ return AddBoardDrill( aDia, aXpos, aYpos, aPlating, aHoleType, aOwner );
+
+ if( CompareToken( "PANEL", refdes ) )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* PANEL data not supported";
+ errormsg = ostr.str();
+
+ return NULL;
+ }
+
+ std::map<std::string, IDF3_COMPONENT*>::iterator ref = components.find( refdes );
+
+ if( ref == components.end() )
+ {
+ // create the item
+ IDF3_COMPONENT* comp = new IDF3_COMPONENT( this );
+
+ if( comp == NULL )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* could not create new component object";
+ errormsg = ostr.str();
+
+ return NULL;
+ }
+
+ comp->SetParent( this );
+ comp->SetRefDes( refdes );
+ ref = components.insert( std::pair< std::string, IDF3_COMPONENT*> ( comp->GetRefDes(), comp ) ).first;
+ }
+
+ // add the drill
+ IDF_DRILL_DATA* dp = ref->second->AddDrill( aDia, aXpos, aYpos, aPlating, aHoleType, aOwner );
+
+ if( !dp )
+ {
+ errormsg = ref->second->GetError();
+ return NULL;
+ }
+
+ return dp;
+}
+
+
+IDF_DRILL_DATA* IDF3_BOARD::addCompDrill( IDF_DRILL_DATA* aDrilledHole )
+{
+ if( !aDrilledHole )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): NULL pointer";
+ errormsg = ostr.str();
+
+ return NULL;
+ }
+
+ if( CompareToken( "PANEL", aDrilledHole->GetDrillRefDes() ) )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* PANEL data not supported";
+ errormsg = ostr.str();
+
+ return NULL;
+ }
+
+ std::map<std::string, IDF3_COMPONENT*>::iterator ref = components.find( aDrilledHole->GetDrillRefDes() );
+
+ if( ref == components.end() )
+ {
+ // create the item
+ IDF3_COMPONENT* comp = new IDF3_COMPONENT( this );
+
+ if( comp == NULL )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* could not create new component object";
+ errormsg = ostr.str();
+
+ return NULL;
+ }
+
+ comp->SetParent( this );
+ comp->SetRefDes( aDrilledHole->GetDrillRefDes() );
+ ref = components.insert( std::pair< std::string, IDF3_COMPONENT*> ( comp->GetRefDes(), comp ) ).first;
+ }
+
+ IDF_DRILL_DATA* dp = ref->second->AddDrill( aDrilledHole );
+
+ if( !dp )
+ {
+ errormsg = ref->second->GetError();
+ return NULL;
+ }
+
+ return dp;
+}
+
+
+bool IDF3_BOARD::delCompDrill( double aDia, double aXpos, double aYpos, std::string aRefDes )
+{
+ errormsg.clear();
+
+ std::map<std::string, IDF3_COMPONENT*>::iterator ref = components.find( aRefDes );
+
+ if( ref == components.end() )
+ return false;
+
+ if( !ref->second->DelDrill( aDia, aXpos, aYpos ) )
+ {
+ errormsg = ref->second->GetError();
+ return false;
+ }
+
+ return true;
+}
+
+
+bool IDF3_BOARD::AddComponent( IDF3_COMPONENT* aComponent )
+{
+ if( !aComponent )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__;
+ ostr << "(): Invalid component pointer (NULL)";
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ if( components.insert( std::pair<std::string, IDF3_COMPONENT*>
+ ( aComponent->GetRefDes(), aComponent ) ).second == false )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): \n";
+ ostr << "* duplicate RefDes ('" << aComponent->GetRefDes() << "')";
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ return true;
+}
+
+
+bool IDF3_BOARD::DelComponent( IDF3_COMPONENT* aComponent )
+{
+ errormsg.clear();
+
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !checkComponentOwnership( __LINE__, __FUNCTION__, aComponent ) )
+ return false;
+#endif
+
+ std::map<std::string, IDF3_COMPONENT*>::iterator it =
+ components.find( aComponent->GetRefDes() );
+
+ if( it == components.end() )
+ return false;
+
+ delete it->second;
+ components.erase( it );
+
+ return true;
+}
+
+
+bool IDF3_BOARD::DelComponent( size_t aIndex )
+{
+ if( aIndex >= components.size() )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): \n";
+ ostr << "* aIndex (" << aIndex << ") out of range (" << components.size() << ")";
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ std::map<std::string, IDF3_COMPONENT*>::iterator it = components.begin();
+
+ while( aIndex-- > 0 ) ++it;
+
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !checkComponentOwnership( __LINE__, __FUNCTION__, it->second ) )
+ return false;
+#endif
+
+ delete it->second;
+ components.erase( it );
+
+ return true;
+}
+
+
+size_t IDF3_BOARD::GetComponentsSize( void )
+{
+ return components.size();
+}
+
+
+std::map< std::string, IDF3_COMPONENT* >*const IDF3_BOARD::GetComponents( void )
+{
+ return &components;
+}
+
+
+IDF3_COMPONENT* IDF3_BOARD::FindComponent( std::string aRefDes )
+{
+ std::map<std::string, IDF3_COMPONENT*>::iterator it = components.find( aRefDes );
+
+ if( it == components.end() )
+ return NULL;
+
+ return it->second;
+}
+
+
+// returns a pointer to a component outline object or NULL
+// if the object doesn't exist
+IDF3_COMP_OUTLINE* IDF3_BOARD::GetComponentOutline( wxString aFullFileName )
+{
+ std::string fname = TO_UTF8( aFullFileName );
+ wxFileName idflib( aFullFileName );
+
+ if( !idflib.IsOk() )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): \n";
+ cerr << "* invalid file name: '" << fname << "'";
+ errormsg = ostr.str();
+
+ return NULL;
+ }
+
+ if( !idflib.FileExists() )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): \n";
+ cerr << "* no such file: '" << fname << "'";
+ errormsg = ostr.str();
+
+ return NULL;
+ }
+
+ if( !idflib.IsFileReadable() )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): \n";
+ cerr << "* cannot read file: '" << fname << "'";
+ errormsg = ostr.str();
+
+ return NULL;
+ }
+
+ std::map< std::string, std::string >::iterator itm = uidFileList.find( fname );
+
+ if( itm != uidFileList.end() )
+ return GetComponentOutline( itm->second );
+
+ IDF3_COMP_OUTLINE* cp = new IDF3_COMP_OUTLINE( this );
+
+ if( cp == NULL )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): \n";
+ cerr << "* failed to create outline\n";
+ cerr << "* filename: '" << fname << "'";
+ errormsg = ostr.str();
+
+ return NULL;
+ }
+
+ std::ifstream model;
+ model.exceptions ( std::ifstream::badbit );
+
+ try
+ {
+ model.open( fname.c_str(), std::ios_base::in | std::ios_base::binary );
+
+
+ std::string iline; // the input line
+ bool isComment; // true if a line just read in is a comment line
+ std::streampos pos;
+
+
+ while( true )
+ {
+ while( !FetchIDFLine( model, iline, isComment, pos ) && model.good() );
+
+ if( !model.good() )
+ {
+ ostringstream ostr;
+ ostr << "\n* problems reading file: '" << fname << "'";
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ // accept comment lines, .ELECTRICAL, or .MECHANICAL only
+ if( isComment )
+ {
+ cp->AddComment( iline );
+ continue;
+ }
+
+ if( CompareToken( ".ELECTRICAL", iline ) || CompareToken( ".MECHANICAL", iline ) )
+ {
+ cp->readData( model, iline, idfVer );
+ break;
+ }
+ else
+ {
+ ostringstream ostr;
+ ostr << "faulty IDF component definition\n";
+ ostr << "* Expecting .ELECTRICAL or .MECHANICAL, got '" << iline << "'\n";
+ cerr << "* File: '" << fname << "'\n";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+ } // while( true )
+ }
+ catch( const std::exception& e )
+ {
+ delete cp;
+
+ model.exceptions ( std::ios_base::goodbit );
+
+ if( model.is_open() )
+ model.close();
+
+ errormsg = e.what();
+
+ return NULL;
+ }
+
+ model.close();
+
+ // check the unique ID against the list from library components
+ std::list< std::string >::iterator lsts = uidLibList.begin();
+ std::list< std::string >::iterator lste = uidLibList.end();
+ std::string uid = cp->GetUID();
+ IDF3_COMP_OUTLINE* oldp = NULL;
+
+ while( lsts != lste )
+ {
+ if( ! lsts->compare( uid ) )
+ {
+ oldp = GetComponentOutline( uid );
+
+ if( MatchCompOutline( cp, oldp ) )
+ {
+ // everything is fine; the outlines are genuine duplicates; delete the copy
+ delete cp;
+ // make sure we can find the item via its filename
+ uidFileList.insert( std::pair< std::string, std::string>( fname, uid ) );
+ // return the pointer to the original
+ return oldp;
+ }
+ else
+ {
+ delete cp;
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* duplicate UID for different Component Outlines: '" << uid << "'\n";
+ ostr << "* original loaded from library, duplicate in current file\n";
+ ostr << "* file: '" << fname << "'";
+
+ errormsg = ostr.str();
+ return NULL;
+ }
+ }
+
+ ++lsts;
+ }
+
+ // if we got this far then any duplicates are from files previously read
+ oldp = GetComponentOutline( uid );
+
+ if( oldp == NULL )
+ {
+ // everything is fine, there are no existing entries
+ uidFileList.insert( std::pair< std::string, std::string>( fname, uid ) );
+ compOutlines.insert( pair<const std::string, IDF3_COMP_OUTLINE*>( uid, cp ) );
+
+ return cp;
+ }
+
+ if( MatchCompOutline( cp, oldp ) )
+ {
+ // everything is fine; the outlines are genuine duplicates; delete the copy
+ delete cp;
+ // make sure we can find the item via its other filename
+ uidFileList.insert( std::pair< std::string, std::string>( fname, uid ) );
+ // return the pointer to the original
+ return oldp;
+ }
+
+ delete cp;
+
+ // determine the file name of the first instance
+ std::map< std::string, std::string >::iterator ufls = uidFileList.begin();
+ std::map< std::string, std::string >::iterator ufle = uidFileList.end();
+ std::string oldfname;
+
+ while( ufls != ufle )
+ {
+ if( ! ufls->second.compare( uid ) )
+ {
+ oldfname = ufls->first;
+ break;
+ }
+
+ ++ufls;
+ }
+
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* duplicate UID for different Component Outlines: '" << uid << "'\n";
+ ostr << "* original file: '" << oldfname << "'\n";
+ ostr << "* this file: '" << fname << "'";
+
+ errormsg = ostr.str();
+ return NULL;
+}
+
+
+// returns a pointer to the component outline object with the
+// unique ID aComponentID
+IDF3_COMP_OUTLINE* IDF3_BOARD::GetComponentOutline( std::string aComponentID )
+{
+ std::map< std::string, IDF3_COMP_OUTLINE*>::iterator its = compOutlines.find( aComponentID );
+
+ if( its != compOutlines.end() )
+ return its->second;
+
+ return NULL;
+}
+
+
+// returns a pointer to the outline which is substituted
+// whenever a true outline cannot be found or is defective
+IDF3_COMP_OUTLINE* IDF3_BOARD::GetInvalidOutline( const std::string& aGeomName, const std::string& aPartName )
+{
+ std::string uid;
+ bool empty = false;
+
+ if( aGeomName.empty() && aPartName.empty() )
+ {
+ uid = "NOGEOM_NOPART";
+ empty = true;
+ }
+ else
+ {
+ uid = aGeomName + "_" + aPartName;
+ }
+
+ IDF3_COMP_OUTLINE* cp = GetComponentOutline( uid );
+
+ if( cp != NULL )
+ return cp;
+
+ cp = new IDF3_COMP_OUTLINE( this );
+
+ if( cp == NULL )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): ";
+ cerr << "could not create new outline";
+ errormsg = ostr.str();
+
+ return NULL;
+ }
+
+ if( empty )
+ cp->CreateDefaultOutline( "", "" );
+ else
+ cp->CreateDefaultOutline( aGeomName, aPartName );
+
+ compOutlines.insert( pair<const std::string, IDF3_COMP_OUTLINE*>(cp->GetUID(), cp) );
+
+ return cp;
+}
+
+
+// clears all data
+void IDF3_BOARD::Clear( void )
+{
+ // preserve the board thickness
+ double thickness = olnBoard.GetThickness();
+
+ idfSource.clear();
+ brdSource.clear();
+ libSource.clear();
+ brdDate.clear();
+ libDate.clear();
+ uidFileList.clear();
+ uidLibList.clear();
+ brdFileVersion = 0;
+ libFileVersion = 0;
+ iRefDes = 0;
+ sRefDes.clear();
+
+ // delete comment lists
+ noteComments.clear();
+ drillComments.clear();
+ placeComments.clear();
+
+ // delete notes
+ while( !notes.empty() )
+ {
+ delete notes.front();
+ notes.pop_front();
+ }
+
+ // delete drill list
+ do
+ {
+ std::list<IDF_DRILL_DATA*>::iterator ds = board_drills.begin();
+ std::list<IDF_DRILL_DATA*>::iterator de = board_drills.end();
+
+ while( ds != de )
+ {
+ delete *ds;
+ ++ds;
+ }
+
+ board_drills.clear();
+ } while(0);
+
+
+ // delete components
+ do
+ {
+ std::map<std::string, IDF3_COMPONENT*>::iterator cs = components.begin();
+ std::map<std::string, IDF3_COMPONENT*>::iterator ce = components.end();
+
+ while( cs != ce )
+ {
+ delete cs->second;
+ ++cs;
+ }
+
+ components.clear();
+ } while(0);
+
+
+ // delete component outlines
+ do
+ {
+ std::map<std::string, IDF3_COMP_OUTLINE*>::iterator cs = compOutlines.begin();
+ std::map<std::string, IDF3_COMP_OUTLINE*>::iterator ce = compOutlines.end();
+
+ while( cs != ce )
+ {
+ delete cs->second;
+ ++cs;
+ }
+
+ compOutlines.clear();
+ } while(0);
+
+
+ // delete OTHER outlines
+ do
+ {
+ std::map<std::string, OTHER_OUTLINE*>::iterator os = olnOther.begin();
+ std::map<std::string, OTHER_OUTLINE*>::iterator oe = olnOther.end();
+
+ while( os != oe )
+ {
+ delete os->second;
+ ++os;
+ }
+
+ olnOther.clear();
+ } while(0);
+
+
+ // delete ROUTE outlines
+ do
+ {
+ std::list<ROUTE_OUTLINE*>::iterator os = olnRoute.begin();
+ std::list<ROUTE_OUTLINE*>::iterator oe = olnRoute.end();
+
+ while( os != oe )
+ {
+ delete *os;
+ ++os;
+ }
+
+ olnRoute.clear();
+ } while(0);
+
+
+ // delete PLACE outlines
+ do
+ {
+ std::list<PLACE_OUTLINE*>::iterator os = olnPlace.begin();
+ std::list<PLACE_OUTLINE*>::iterator oe = olnPlace.end();
+
+ while( os != oe )
+ {
+ delete *os;
+ ++os;
+ }
+
+ olnPlace.clear();
+ } while(0);
+
+
+ // delete ROUTE KEEPOUT outlines
+ do
+ {
+ std::list<ROUTE_KO_OUTLINE*>::iterator os = olnRouteKeepout.begin();
+ std::list<ROUTE_KO_OUTLINE*>::iterator oe = olnRouteKeepout.end();
+
+ while( os != oe )
+ {
+ delete *os;
+ ++os;
+ }
+
+ olnRouteKeepout.clear();
+ } while(0);
+
+
+ // delete VIA KEEPOUT outlines
+ do
+ {
+ std::list<VIA_KO_OUTLINE*>::iterator os = olnViaKeepout.begin();
+ std::list<VIA_KO_OUTLINE*>::iterator oe = olnViaKeepout.end();
+
+ while( os != oe )
+ {
+ delete *os;
+ ++os;
+ }
+
+ olnViaKeepout.clear();
+ } while(0);
+
+
+ // delete PLACEMENT KEEPOUT outlines
+ do
+ {
+ std::list<PLACE_KO_OUTLINE*>::iterator os = olnPlaceKeepout.begin();
+ std::list<PLACE_KO_OUTLINE*>::iterator oe = olnPlaceKeepout.end();
+
+ while( os != oe )
+ {
+ delete *os;
+ ++os;
+ }
+
+ olnPlaceKeepout.clear();
+ } while(0);
+
+
+ // delete PLACEMENT GROUP outlines
+ do
+ {
+ std::multimap<std::string, GROUP_OUTLINE*>::iterator os = olnGroup.begin();
+ std::multimap<std::string, GROUP_OUTLINE*>::iterator oe = olnGroup.end();
+
+ while( os != oe )
+ {
+ delete os->second;
+ ++os;
+ }
+
+ olnGroup.clear();
+ } while(0);
+
+ boardName.clear();
+ olnBoard.setThickness( thickness );
+
+ state = FILE_START;
+ unit = UNIT_MM;
+ userScale = 1.0;
+ userXoff = 0.0;
+ userYoff = 0.0;
+
+ return;
+}
+
+
+const std::map<std::string, OTHER_OUTLINE*>*const
+IDF3_BOARD::GetOtherOutlines( void )
+{
+ return &olnOther;
+}