
 /**
 * @file item_io.cpp
 * @brief Routines for reading and saving of structures in ASCII file common to Pcbnew and CvPcb.
 *  This is migrationary and temporary while we move the IO_MGR.
 */

#include <fctsys.h>
#include <confirm.h>
#include <kicad_string.h>
#include <build_version.h>
#include <wxPcbStruct.h>
#include <richio.h>
#include <macros.h>
#include <pcbcommon.h>

#include <zones.h>

#ifdef CVPCB
#include <cvpcb.h>
#endif

#include <config.h>
#include <class_board.h>
#include <class_module.h>
#include <class_track.h>
#include <class_pcb_text.h>
#include <class_zone.h>
#include <class_dimension.h>
#include <class_drawsegment.h>
#include <class_mire.h>

#include <pcbnew.h>
#include <pcbnew_id.h>
#include <autorout.h>

#include <3d_struct.h>
#include <trigo.h>
#include <class_edge_mod.h>
#include <pcbnew.h>
#include <drawtxt.h>

#define MAX_WIDTH 10000  // Thickness (in 1 / 10000 ") of maximum reasonable features, text...


#if 1 || !defined(USE_NEW_PCBNEW_SAVE)

bool BOARD::Save( FILE* aFile ) const
{
    bool        rc = false;
    BOARD_ITEM* item;

    // save the nets
    for( unsigned ii = 0; ii < GetNetCount(); ii++ )
        if( !FindNet( ii )->Save( aFile ) )
            goto out;

    // Saved nets do not include netclass names, so save netclasses after nets.
    m_NetClasses.Save( aFile );

    // save the modules
    for( item = m_Modules; item; item = item->Next() )
        if( !item->Save( aFile ) )
            goto out;

    for( item = m_Drawings; item; item = item->Next() )
    {
        switch( item->Type() )
        {
        case PCB_TEXT_T:
        case PCB_LINE_T:
        case PCB_TARGET_T:
        case PCB_DIMENSION_T:
            if( !item->Save( aFile ) )
                goto out;

            break;

        default:

            // future: throw exception here
#if defined(DEBUG)
            printf( "BOARD::Save() ignoring m_Drawings type %d\n", item->Type() );
#endif
            break;
        }
    }

    // do not save MARKER_PCBs, they can be regenerated easily

    // save the tracks & vias
    fprintf( aFile, "$TRACK\n" );

    for( item = m_Track; item; item = item->Next() )
    {
        if( !item->Save( aFile ) )
            goto out;
    }

    fprintf( aFile, "$EndTRACK\n" );

    // save the zones
    fprintf( aFile, "$ZONE\n" );

    for( item = m_Zone; item; item = item->Next() )
    {
        if( !item->Save( aFile ) )
            goto out;
    }

    fprintf( aFile, "$EndZONE\n" );

    // save the zone edges
    for( unsigned ii = 0; ii < m_ZoneDescriptorList.size(); ii++ )
    {
        ZONE_CONTAINER* edge_zone = m_ZoneDescriptorList[ii];
        edge_zone->Save( aFile );
    }


    if( fprintf( aFile, "$EndBOARD\n" ) != sizeof("$EndBOARD\n") - 1 )
        goto out;

    rc = true;  // wrote all OK

out:
    return rc;
}


bool DRAWSEGMENT::Save( FILE* aFile ) const
{
    if( fprintf( aFile, "$DRAWSEGMENT\n" ) != sizeof("$DRAWSEGMENT\n") - 1 )
        return false;

    fprintf( aFile, "Po %d %d %d %d %d %d\n",
             m_Shape,
             m_Start.x, m_Start.y,
             m_End.x, m_End.y, m_Width );

    if( m_Type != S_CURVE )
    {
        fprintf( aFile, "De %d %d %g %lX %X\n",
                 m_Layer, m_Type, GetAngle(),
                 m_TimeStamp, GetStatus() );
    }
    else
    {
        fprintf( aFile, "De %d %d %g %lX %X %d %d %d %d\n",
                 m_Layer, m_Type, GetAngle(),
                 m_TimeStamp, GetStatus(),
                 m_BezierC1.x,m_BezierC1.y,
                 m_BezierC2.x,m_BezierC2.y);
    }

    if( fprintf( aFile, "$EndDRAWSEGMENT\n" ) != sizeof("$EndDRAWSEGMENT\n") - 1 )
        return false;

    return true;
}


/** Note: the old name of class NETINFO_ITEM was EQUIPOT
 * so in Save (and read) functions, for compatibility, we use EQUIPOT as
 * keyword
 */
bool NETINFO_ITEM::Save( FILE* aFile ) const
{
    bool success = false;

    fprintf( aFile, "$EQUIPOT\n" );
    fprintf( aFile, "Na %d %s\n", GetNet(), EscapedUTF8( m_Netname ).c_str() );
    fprintf( aFile, "St %s\n", "~" );

    if( fprintf( aFile, "$EndEQUIPOT\n" ) != sizeof("$EndEQUIPOT\n") - 1 )
        goto out;

    success = true;

out:
    return success;
}


bool PCB_TARGET::Save( FILE* aFile ) const
{
    bool rc = false;

    if( fprintf( aFile, "$PCB_TARGET\n" ) != sizeof("$PCB_TARGET\n")-1 )
        goto out;

    fprintf( aFile, "Po %X %d %d %d %d %d %8.8lX\n",
             m_Shape, m_Layer,
             m_Pos.x, m_Pos.y,
             m_Size, m_Width, m_TimeStamp );

    if( fprintf( aFile, "$EndPCB_TARGET\n" ) != sizeof("$EndPCB_TARGET\n")-1 )
        goto out;

    rc = true;

out:
    return rc;
}


bool ZONE_CONTAINER::Save( FILE* aFile ) const
{
    unsigned item_pos;
    int      ret;
    unsigned corners_count = m_Poly->corner.size();
    int      outline_hatch;

    fprintf( aFile, "$CZONE_OUTLINE\n" );

    // Save the outline main info
    ret = fprintf( aFile, "ZInfo %8.8lX %d %s\n",
                   m_TimeStamp, GetNet(),
                   EscapedUTF8( m_Netname ).c_str() );

    if( ret < 3 )
        return false;

    // Save the outline layer info
    ret = fprintf( aFile, "ZLayer %d\n", m_Layer );

    if( ret < 1 )
        return false;

    // Save the outline aux info
    switch( m_Poly->GetHatchStyle() )
    {
    default:
    case CPolyLine::NO_HATCH:
        outline_hatch = 'N';
        break;

    case CPolyLine::DIAGONAL_EDGE:
        outline_hatch = 'E';
        break;

    case CPolyLine::DIAGONAL_FULL:
        outline_hatch = 'F';
        break;
    }

    ret = fprintf( aFile, "ZAux %d %c\n", corners_count, outline_hatch );

    if( ret < 2 )
        return false;

    if( GetPriority() > 0 )
    {
        ret = fprintf( aFile, "ZPriority %d\n", GetPriority() );
        if( ret < 1 )
            return false;
    }

    // Save pad option and clearance
    int padConnection;
    switch( m_PadConnection )
    {
    default:
    case PAD_IN_ZONE:
        padConnection = 'I';
        break;

    case THERMAL_PAD:
        padConnection = 'T';
        break;

    case PAD_NOT_IN_ZONE:
        padConnection = 'X';
        break;
    }

    ret = fprintf( aFile, "ZClearance %d %c\n", m_ZoneClearance, padConnection );

    if( ret < 2 )
        return false;

    ret = fprintf( aFile, "ZMinThickness %d\n", m_ZoneMinThickness );

    if( ret < 1 )
        return false;

    ret = fprintf( aFile,
                   "ZOptions %d %d %c %d %d\n",
                   m_FillMode,
                   m_ArcToSegmentsCount,
                   m_IsFilled ? 'S' : 'F',
                   m_ThermalReliefGap,
                   m_ThermalReliefCopperBridge );

    if( ret < 3 )
        return false;

    ret = fprintf( aFile,
                   "ZSmoothing %d %d\n",
                   cornerSmoothingType, cornerRadius );

    if( ret < 2 )
        return false;

    // Save the corner list
    for( item_pos = 0; item_pos < corners_count; item_pos++ )
    {
        ret = fprintf( aFile, "ZCorner %d %d %d\n",
                       m_Poly->corner[item_pos].x, m_Poly->corner[item_pos].y,
                       m_Poly->corner[item_pos].end_contour );

        if( ret < 3 )
            return false;
    }

    // Save the PolysList
    if( m_FilledPolysList.size() )
    {
        fprintf( aFile, "$POLYSCORNERS\n" );

        for( unsigned ii = 0; ii < m_FilledPolysList.size(); ii++ )
        {
            const CPolyPt* corner = &m_FilledPolysList[ii];
            ret = fprintf( aFile,
                           "%d %d %d %d\n",
                           corner->x,
                           corner->y,
                           corner->end_contour,
                           corner->utility );

            if( ret < 4 )
                return false;
        }

        fprintf( aFile, "$endPOLYSCORNERS\n" );
    }

    // Save the filling segments list
    if( m_FillSegmList.size() )
    {
        fprintf( aFile, "$FILLSEGMENTS\n" );

        for( unsigned ii = 0; ii < m_FillSegmList.size(); ii++ )
        {
            ret = fprintf( aFile, "%d %d %d %d\n",
                           m_FillSegmList[ii].m_Start.x, m_FillSegmList[ii].m_Start.y,
                           m_FillSegmList[ii].m_End.x, m_FillSegmList[ii].m_End.y );

            if( ret < 4 )
                return false;
        }

        fprintf( aFile, "$endFILLSEGMENTS\n" );
    }

    fprintf( aFile, "$endCZONE_OUTLINE\n" );

    return true;
}


bool NETCLASSES::Save( FILE* aFile ) const
{
    bool result;

    // save the default first.
    result = m_Default.Save( aFile );

    if( result )
    {
        // the rest will be alphabetical in the *.brd file.
        for( const_iterator i = begin();  i!=end();  ++i )
        {
            NETCLASS*   netclass = i->second;

            result = netclass->Save( aFile );
            if( !result )
                break;
        }
    }

    return result;
}


bool NETCLASS::Save( FILE* aFile ) const
{
    bool result = true;

    fprintf( aFile, "$NCLASS\n" );
    fprintf( aFile, "Name %s\n",        EscapedUTF8( m_Name ).c_str() );
    fprintf( aFile, "Desc %s\n",        EscapedUTF8( GetDescription() ).c_str() );

    // Write parameters

    fprintf( aFile, "Clearance %d\n",       GetClearance() );
    fprintf( aFile, "TrackWidth %d\n",      GetTrackWidth() );

    fprintf( aFile, "ViaDia %d\n",          GetViaDiameter() );
    fprintf( aFile, "ViaDrill %d\n",        GetViaDrill() );

    fprintf( aFile, "uViaDia %d\n",         GetuViaDiameter() );
    fprintf( aFile, "uViaDrill %d\n",       GetuViaDrill() );

    // Write members:
    for( const_iterator i = begin();  i!=end();  ++i )
        fprintf( aFile, "AddNet %s\n", EscapedUTF8( *i ).c_str() );

    fprintf( aFile, "$EndNCLASS\n" );

    return result;
}


bool TEXTE_PCB::Save( FILE* aFile ) const
{
    if( m_Text.IsEmpty() )
        return true;

    if( fprintf( aFile, "$TEXTPCB\n" ) != sizeof("$TEXTPCB\n") - 1 )
        return false;

    const char* style = m_Italic ? "Italic" : "Normal";

    wxArrayString* list = wxStringSplit( m_Text, '\n' );

    for( unsigned ii = 0; ii < list->Count(); ii++ )
    {
        wxString txt  = list->Item( ii );

        if ( ii == 0 )
            fprintf( aFile, "Te %s\n", EscapedUTF8( txt ).c_str() );
        else
            fprintf( aFile, "nl %s\n", EscapedUTF8( txt ).c_str() );
    }

    delete list;

    fprintf( aFile, "Po %d %d %d %d %d %g\n",
             m_Pos.x, m_Pos.y, m_Size.x, m_Size.y, m_Thickness, GetOrientation() );

    char hJustify = 'L';
    switch( m_HJustify )
    {
    case GR_TEXT_HJUSTIFY_LEFT:
        hJustify = 'L';
        break;
    case GR_TEXT_HJUSTIFY_CENTER:
        hJustify = 'C';
        break;
    case GR_TEXT_HJUSTIFY_RIGHT:
        hJustify = 'R';
        break;
    default:
        hJustify = 'C';
        break;
    }

    fprintf( aFile, "De %d %d %lX %s %c\n", m_Layer,
             m_Mirror ? 0 : 1,
             m_TimeStamp, style, hJustify );

    if( fprintf( aFile, "$EndTEXTPCB\n" ) != sizeof("$EndTEXTPCB\n") - 1 )
        return false;

    return true;
}


/**
 * Function Save
 * writes the data structures for this object out to a FILE in "*.brd" format.
 * @param aFile The FILE to write to.
 * @return bool - true if success writing else false.
 */
bool TEXTE_MODULE::Save( FILE* aFile ) const
{
    MODULE* parent = (MODULE*) GetParent();
    int     orient = m_Orient;

    // Due to the Pcbnew history, m_Orient is saved in screen value
    // but it is handled as relative to its parent footprint
    if( parent )
        orient += parent->m_Orient;

    int ret = fprintf( aFile, "T%d %d %d %d %d %d %d %c %c %d %c %s\n",
                      m_Type,
                      m_Pos0.x, m_Pos0.y,
                      m_Size.y, m_Size.x,
                      orient,
                      m_Thickness,
                      m_Mirror ? 'M' : 'N', m_NoShow ? 'I' : 'V',
                      GetLayer(),
                      m_Italic ? 'I' : 'N',
                      EscapedUTF8( m_Text ).c_str()
                      );

    return ret > 20;
}


bool EDGE_MODULE::Save( FILE* aFile ) const
{
    int ret = -1;

    switch( m_Shape )
    {
    case S_SEGMENT:
        ret = fprintf( aFile, "DS %d %d %d %d %d %d\n",
                       m_Start0.x, m_Start0.y,
                       m_End0.x, m_End0.y,
                       m_Width, m_Layer );
        break;

    case S_CIRCLE:
        ret = fprintf( aFile, "DC %d %d %d %d %d %d\n",
                       m_Start0.x, m_Start0.y,
                       m_End0.x, m_End0.y,
                       m_Width, m_Layer );
        break;

    case S_ARC:
        ret = fprintf( aFile, "DA %d %d %d %d %g %d %d\n",
                       m_Start0.x, m_Start0.y,
                       m_End0.x, m_End0.y,
                       GetAngle(),
                       m_Width, m_Layer );
        break;

    case S_POLYGON:
        ret = fprintf( aFile, "DP %d %d %d %d %d %d %d\n",
                       m_Start0.x, m_Start0.y,
                       m_End0.x, m_End0.y,
                       (int) m_PolyPoints.size(),
                       m_Width, m_Layer );

        for( unsigned i = 0;  i<m_PolyPoints.size();  ++i )
            fprintf( aFile, "Dl %d %d\n", m_PolyPoints[i].x, m_PolyPoints[i].y );

        break;

    default:

        // future: throw an exception here
#if defined(DEBUG)
        printf( "EDGE_MODULE::Save(): unexpected m_Shape: %d\n", m_Shape );
#endif
        break;
    }

    return ret > 5;
}


bool TRACK::Save( FILE* aFile ) const
{
    int type = 0;

    if( Type() == PCB_VIA_T )
        type = 1;

    fprintf( aFile, "Po %d %d %d %d %d %d %d\n", m_Shape,
             m_Start.x, m_Start.y, m_End.x, m_End.y, m_Width, m_Drill );

    fprintf( aFile, "De %d %d %d %lX %X\n",
             m_Layer, type, GetNet(),
             m_TimeStamp, GetStatus() );

    return true;
}


bool DIMENSION::Save( FILE* aFile ) const
{
    bool rc = false;

    // note: COTATION was the previous name of DIMENSION
    // this old keyword is used here for compatibility
    const char keyWordLine[] = "$COTATION\n";
    const char keyWordLineEnd[] = "$endCOTATION\n";

    if( fputs( keyWordLine, aFile ) == EOF )
        goto out;

    fprintf( aFile, "Ge %d %d %lX\n", m_Shape, m_Layer, m_TimeStamp );

    fprintf( aFile, "Va %d\n", m_Value );

    if( !m_Text.GetText().IsEmpty() )
        fprintf( aFile, "Te %s\n", EscapedUTF8( m_Text.GetText() ).c_str() );
    else
        fprintf( aFile, "Te \"?\"\n" );

    fprintf( aFile, "Po %d %d %d %d %d %g %d\n",
             m_Text.m_Pos.x, m_Text.m_Pos.y,
             m_Text.m_Size.x, m_Text.m_Size.y,
             m_Text.GetThickness(), m_Text.GetOrientation(),
             m_Text.m_Mirror ? 0 : 1 );

    fprintf( aFile, "Sb %d %d %d %d %d %d\n", S_SEGMENT,
             m_crossBarOx, m_crossBarOy,
             m_crossBarFx, m_crossBarFy, m_Width );

    fprintf( aFile, "Sd %d %d %d %d %d %d\n", S_SEGMENT,
             m_featureLineDOx, m_featureLineDOy,
             m_featureLineDFx, m_featureLineDFy, m_Width );

    fprintf( aFile, "Sg %d %d %d %d %d %d\n", S_SEGMENT,
             m_featureLineGOx, m_featureLineGOy,
             m_featureLineGFx, m_featureLineGFy, m_Width );

    fprintf( aFile, "S1 %d %d %d %d %d %d\n", S_SEGMENT,
             m_arrowD1Ox, m_arrowD1Oy,
             m_arrowD1Fx, m_arrowD1Fy, m_Width );

    fprintf( aFile, "S2 %d %d %d %d %d %d\n", S_SEGMENT,
             m_arrowD2Ox, m_arrowD2Oy,
             m_arrowD2Fx, m_arrowD2Fy, m_Width );


    fprintf( aFile, "S3 %d %d %d %d %d %d\n", S_SEGMENT,
             m_arrowG1Ox, m_arrowG1Oy,
             m_arrowG1Fx, m_arrowG1Fy, m_Width );

    fprintf( aFile, "S4 %d %d %d %d %d %d\n", S_SEGMENT,
             m_arrowG2Ox, m_arrowG2Oy,
             m_arrowG2Fx, m_arrowG2Fy, m_Width );

    if( fputs( keyWordLineEnd, aFile ) == EOF )
        goto out;

    rc = true;

out:
    return rc;
}


bool D_PAD::Save( FILE* aFile ) const
{
    int         cshape;
    const char* texttype;

    // check the return values for first and last fprints() in this function
    if( fprintf( aFile, "$PAD\n" ) != sizeof("$PAD\n") - 1 )
        return false;

    switch( m_PadShape )
    {
    case PAD_CIRCLE:
        cshape = 'C'; break;

    case PAD_RECT:
        cshape = 'R'; break;

    case PAD_OVAL:
        cshape = 'O'; break;

    case PAD_TRAPEZOID:
        cshape = 'T'; break;

    default:
        cshape = 'C';
        DisplayError( NULL, _( "Unknown pad shape" ) );
        break;
    }

    fprintf( aFile, "Sh \"%.4s\" %c %d %d %d %d %g\n",
             m_Padname, cshape, m_Size.x, m_Size.y,
             m_DeltaSize.x, m_DeltaSize.y, m_Orient );

    fprintf( aFile, "Dr %d %d %d", m_Drill.x, m_Offset.x, m_Offset.y );

    if( m_DrillShape == PAD_OVAL )
    {
        fprintf( aFile, " %c %d %d", 'O', m_Drill.x, m_Drill.y );
    }

    fprintf( aFile, "\n" );

    switch( GetAttribute() )
    {
    case PAD_STANDARD:
        texttype = "STD"; break;

    case PAD_SMD:
        texttype = "SMD"; break;

    case PAD_CONN:
        texttype = "CONN"; break;

    case PAD_HOLE_NOT_PLATED:
        texttype = "HOLE"; break;

    default:
        texttype = "STD";
        DisplayError( NULL, wxT( "Invalid Pad attribute" ) );
        break;
    }

    fprintf( aFile, "At %s N %8.8X\n", texttype, m_layerMask );

    fprintf( aFile, "Ne %d %s\n", GetNet(), EscapedUTF8( m_Netname ).c_str() );

    fprintf( aFile, "Po %d %d\n", m_Pos0.x, m_Pos0.y );

    if( GetDieLength() != 0 )
        fprintf( aFile, "Le %d\n", GetDieLength() );

    if( GetLocalSolderMaskMargin() != 0 )
        fprintf( aFile, ".SolderMask %d\n", GetLocalSolderMaskMargin() );

    if( GetLocalSolderPasteMargin() != 0 )
        fprintf( aFile, ".SolderPaste %d\n", GetLocalSolderPasteMargin() );

    if( GetLocalSolderPasteMarginRatio() != 0 )
        fprintf( aFile, ".SolderPasteRatio %g\n", GetLocalSolderPasteMarginRatio() );

    if( GetLocalClearance() != 0 )
        fprintf( aFile, ".LocalClearance %d\n", GetLocalClearance() );

    if( m_ZoneConnection != UNDEFINED_CONNECTION )
        fprintf( aFile, ".ZoneConnection %d\n", m_ZoneConnection );

    if( m_ThermalWidth != 0 )
        fprintf( aFile, ".ThermalWidth %d\n", m_ThermalWidth );

    if( m_ThermalGap != 0 )
        fprintf( aFile, ".ThermalGap %d\n", m_ThermalGap );

    if( fprintf( aFile, "$EndPAD\n" ) != sizeof("$EndPAD\n") - 1 )
        return false;

    return true;
}


bool MODULE::Save( FILE* aFile ) const
{
    char        statusTxt[8];
    BOARD_ITEM* item;

    bool rc = false;

    fprintf( aFile, "$MODULE %s\n", TO_UTF8( m_LibRef ) );

    memset( statusTxt, 0, sizeof(statusTxt) );
    if( IsLocked() )
        statusTxt[0] = 'F';
    else
        statusTxt[0] = '~';

    if( m_ModuleStatus & MODULE_is_PLACED )
        statusTxt[1] = 'P';
    else
        statusTxt[1] = '~';

    fprintf( aFile, "Po %d %d %g %d %8.8lX %8.8lX %s\n",
             m_Pos.x, m_Pos.y,
             GetOrientation(), m_Layer, m_LastEdit_Time,
             m_TimeStamp, statusTxt );

    fprintf( aFile, "Li %s\n", TO_UTF8( m_LibRef ) );

    if( !m_Doc.IsEmpty() )
    {
        fprintf( aFile, "Cd %s\n", TO_UTF8( m_Doc ) );
    }

    if( !m_KeyWord.IsEmpty() )
    {
        fprintf( aFile, "Kw %s\n", TO_UTF8( m_KeyWord ) );
    }

    fprintf( aFile, "Sc %8.8lX\n", m_TimeStamp );
    fprintf( aFile, "AR %s\n", TO_UTF8( m_Path ) );
    fprintf( aFile, "Op %X %X 0\n", m_CntRot90, m_CntRot180 );

    if( GetLocalSolderMaskMargin() != 0 )
        fprintf( aFile, ".SolderMask %d\n", GetLocalSolderMaskMargin() );

    if( m_LocalSolderPasteMargin != 0 )
        fprintf( aFile, ".SolderPaste %d\n", GetLocalSolderPasteMargin() );

    if( GetLocalSolderPasteMarginRatio() != 0 )
        fprintf( aFile, ".SolderPasteRatio %g\n", GetLocalSolderPasteMarginRatio() );

    if( m_LocalClearance != 0 )
        fprintf( aFile, ".LocalClearance %d\n", GetLocalClearance() );

    if( m_ZoneConnection != UNDEFINED_CONNECTION )
        fprintf( aFile, ".ZoneConnection %d\n", m_ZoneConnection );

    if( m_ThermalWidth != 0 )
        fprintf( aFile, ".ThermalWidth %d\n", m_ThermalWidth );

    if( m_ThermalGap != 0 )
        fprintf( aFile, ".ThermalGap %d\n", m_ThermalGap );

    // attributes
    if( m_Attributs != MOD_DEFAULT )
    {
        fprintf( aFile, "At " );

        if( m_Attributs & MOD_CMS )
            fprintf( aFile, "SMD " );

        if( m_Attributs & MOD_VIRTUAL )
            fprintf( aFile, "VIRTUAL " );

        fprintf( aFile, "\n" );
    }

    // save reference
    if( !m_Reference->Save( aFile ) )
        goto out;

    // save value
    if( !m_Value->Save( aFile ) )
        goto out;

    // save drawing elements
    for( item = m_Drawings;  item;  item = item->Next() )
    {
        switch( item->Type() )
        {
        case PCB_MODULE_TEXT_T:
        case PCB_MODULE_EDGE_T:
            if( !item->Save( aFile ) )
                goto out;

            break;

        default:
#if defined(DEBUG)
            printf( "MODULE::Save() ignoring type %d\n", item->Type() );
#endif
            break;
        }
    }

    // save the pads
    for( item = m_Pads;  item;  item = item->Next() )
        if( !item->Save( aFile ) )
            goto out;

    Write_3D_Descr( aFile );

    fprintf( aFile, "$EndMODULE  %s\n", TO_UTF8( m_LibRef ) );

    rc = true;
out:
    return rc;
}

/* Save the description of 3D MODULE
 */
int MODULE::Write_3D_Descr( FILE* File ) const
{
    char buf[512];

    for( S3D_MASTER* t3D = m_3D_Drawings;  t3D;  t3D = t3D->Next() )
    {
        if( !t3D->m_Shape3DName.IsEmpty() )
        {
            fprintf( File, "$SHAPE3D\n" );

            fprintf( File, "Na %s\n", EscapedUTF8( t3D->m_Shape3DName ).c_str() );

            sprintf( buf, "Sc %lf %lf %lf\n",
                     t3D->m_MatScale.x,
                     t3D->m_MatScale.y,
                     t3D->m_MatScale.z );
            fprintf( File, "%s", to_point( buf ) );

            sprintf( buf, "Of %lf %lf %lf\n",
                     t3D->m_MatPosition.x,
                     t3D->m_MatPosition.y,
                     t3D->m_MatPosition.z );
            fprintf( File, "%s", to_point( buf ) );

            sprintf( buf, "Ro %lf %lf %lf\n",
                     t3D->m_MatRotation.x,
                     t3D->m_MatRotation.y,
                     t3D->m_MatRotation.z );
            fprintf( File, "%s", to_point( buf ) );

            fprintf( File, "$EndSHAPE3D\n" );
        }
    }

    return 0;
}

#endif  // USE_NEW_PCBNEW_SAVE


#if 1 || !defined(USE_NEW_PCBNEW_LOAD)

/* Read pad from file.
 * The 1st line of descr ($PAD) is assumed to be already read
 * Syntax:
 * $PAD
 * Sh "N1" C 550 550 0 0 1800
 * Dr 310 0 0
 * At STD N 00C0FFFF
 * Do 3 "netname"
 * Po 6000 -6000
 * $EndPAD
 */
int D_PAD::ReadDescr( LINE_READER* aReader )
{
    char*   Line;
    char    BufLine[1024], BufCar[256];
    char*   PtLine;
    int     nn, ll, dx, dy;

    while( aReader->ReadLine() )
    {
        Line = aReader->Line();

        if( Line[0] == '$' )
            return 0;

        PtLine = Line + 3;

        /* Decode the first code and read the corresponding data
         */
        switch( Line[0] )
        {
        case 'S': // = Sh
            // Read pad name
            nn = 0;

            while( (*PtLine != '"') && *PtLine )
                PtLine++;

            if( *PtLine )
                PtLine++;

            memset( m_Padname, 0, sizeof(m_Padname) );

            while( (*PtLine != '"') && *PtLine )
            {
                if( nn < (int) sizeof(m_Padname) )
                {
                    if( *PtLine > ' ' )
                    {
                        m_Padname[nn] = *PtLine; nn++;
                    }
                }
                PtLine++;
            }

            if( *PtLine == '"' )
                PtLine++;

            nn = sscanf( PtLine, " %s %d %d %d %d %lf",
                         BufCar, &m_Size.x, &m_Size.y,
                         &m_DeltaSize.x, &m_DeltaSize.y,
                         &m_Orient );

            ll = 0xFF & BufCar[0];

            // Read pad shape
            PAD_SHAPE_T shape;

            switch( ll )
            {
            default:
            case 'C':   shape = PAD_CIRCLE;     break;
            case 'R':   shape = PAD_RECT;       break;
            case 'O':   shape = PAD_OVAL;       break;
            case 'T':   shape = PAD_TRAPEZOID;  break;
            }

            SetShape( shape );  // sets m_boundingRadius = -1
            break;

        case 'D':
            BufCar[0] = 0;
            nn = sscanf( PtLine, "%d %d %d %s %d %d", &m_Drill.x,
                         &m_Offset.x, &m_Offset.y, BufCar, &dx, &dy );
            m_Drill.y    = m_Drill.x;
            m_DrillShape = PAD_CIRCLE;

            if( nn >= 6 )       // Drill shape = OVAL ?
            {
                if( BufCar[0] == 'O' )
                {
                    m_Drill.x = dx;
                    m_Drill.y = dy;

                    m_DrillShape = PAD_OVAL;
                }
            }

            break;

        case 'A':
            nn = sscanf( PtLine, "%s %s %X", BufLine, BufCar,
                         &m_layerMask );

            // BufCar is not used now update attributes
            SetAttribute( PAD_STANDARD );
            if( strncmp( BufLine, "SMD", 3 ) == 0 )
                SetAttribute( PAD_SMD );

            if( strncmp( BufLine, "CONN", 4 ) == 0 )
                SetAttribute( PAD_CONN );

            if( strncmp( BufLine, "HOLE", 4 ) == 0 )
                SetAttribute( PAD_HOLE_NOT_PLATED );

            break;

        case 'N':       // Read Netname
            int netcode;
            nn = sscanf( PtLine, "%d", &netcode );
            SetNet( netcode );

            // read Netname
            ReadDelimitedText( BufLine, PtLine, sizeof(BufLine) );
            SetNetname( FROM_UTF8( StrPurge( BufLine ) ) );
        break;

        case 'P':
            nn    = sscanf( PtLine, "%d %d", &m_Pos0.x, &m_Pos0.y );
            m_Pos = m_Pos0;
            break;

        case 'L':
            int lengthdie;
            nn    = sscanf( PtLine, "%d", &lengthdie );
            SetDieLength( lengthdie );
            break;

        case '.':    // Read specific data
            if( strnicmp( Line, ".SolderMask ", 12 ) == 0 )
                SetLocalSolderMaskMargin( atoi( Line + 12 ) );
            else if( strnicmp( Line, ".SolderPaste ", 13 )  == 0 )
                SetLocalSolderPasteMargin( atoi( Line + 13 ) );
            else if( strnicmp( Line, ".SolderPasteRatio ", 18 ) == 0 )
                SetLocalSolderPasteMarginRatio( atoi( Line + 18 ) );
            else if( strnicmp( Line, ".LocalClearance ", 16 ) == 0 )
                SetLocalClearance( atoi( Line + 16 ) );
            else if( strnicmp( Line, ".ZoneConnection ", 16 ) == 0 )
                m_ZoneConnection = (ZoneConnection)atoi( Line + 16 );
            else if( strnicmp( Line, ".ThermalWidth ", 14 ) == 0 )
                m_ThermalWidth = atoi( Line + 14 );
            else if( strnicmp( Line, ".ThermalGap ", 12 ) == 0 )
                m_ThermalGap = atoi( Line + 12 );
            break;

        default:
            DisplayError( NULL, wxT( "Err Pad: Id inconnu" ) );
            return 1;
        }
    }

    return 2;   // error : EOF
}


/* Read 3D module from file. (Ascii)
 * The 1st line of descr ($MODULE) is assumed to be already read
 * Returns 0 if OK
 */
int MODULE::Read_3D_Descr( LINE_READER* aReader )
{
    char*       Line = aReader->Line();
    char*       text = Line + 3;

    S3D_MASTER* t3D = m_3D_Drawings;

    if( !t3D->m_Shape3DName.IsEmpty() )
    {
        S3D_MASTER* n3D = new S3D_MASTER( this );

        m_3D_Drawings.PushBack( n3D );

        t3D = n3D;
    }

    while( aReader->ReadLine() )
    {
        Line = aReader->Line();

        switch( Line[0] )
        {
        case '$':
            if( Line[1] == 'E' )
                return 0;

            return 1;

        case 'N':       // Shape File Name
        {
            char buf[512];
            ReadDelimitedText( buf, text, 512 );
            t3D->m_Shape3DName = FROM_UTF8( buf );
            break;
        }

        case 'S':       // Scale
            sscanf( text, "%lf %lf %lf\n",
                    &t3D->m_MatScale.x,
                    &t3D->m_MatScale.y,
                    &t3D->m_MatScale.z );
            break;

        case 'O':       // Offset
            sscanf( text, "%lf %lf %lf\n",
                    &t3D->m_MatPosition.x,
                    &t3D->m_MatPosition.y,
                    &t3D->m_MatPosition.z );
            break;

        case 'R':       // Rotation
            sscanf( text, "%lf %lf %lf\n",
                    &t3D->m_MatRotation.x,
                    &t3D->m_MatRotation.y,
                    &t3D->m_MatRotation.z );
            break;

        default:
            break;
        }
    }

    return 1;
}


/* Read a MODULE description
 *  The first description line ($MODULE) is already read
 *  @return 0 if no error
 */
int MODULE::ReadDescr( LINE_READER* aReader )
{
    char* Line;
    char  BufLine[256], BufCar1[128], * PtLine;
    int   itmp1, itmp2;

    while( aReader->ReadLine() )
    {
        Line = aReader->Line();
        if( Line[0] == '$' )
        {
            if( Line[1] == 'E' )
                break;

            if( Line[1] == 'P' )
            {
                D_PAD* pad = new D_PAD( this );

                pad->ReadDescr( aReader );

                wxPoint padpos = pad->GetPosition();

                RotatePoint( &padpos, m_Orient );

                pad->SetPosition( padpos + m_Pos );

                m_Pads.PushBack( pad );
                continue;
            }

            if( Line[1] == 'S' )
                Read_3D_Descr( aReader );
        }

        if( strlen( Line ) < 4 )
            continue;

        PtLine = Line + 3;

        /* Decode the first code of the current line and read the
         * corresponding data
         */
        switch( Line[0] )
        {
        case 'P':
            double orientation;
            memset( BufCar1, 0, sizeof(BufCar1) );
            sscanf( PtLine, "%d %d %lf %d %lX %lX %s",
                    &m_Pos.x, &m_Pos.y,
                    &orientation, &m_Layer,
                    &m_LastEdit_Time, &m_TimeStamp, BufCar1 );

            SetOrientation( orientation );

            m_ModuleStatus = 0;

            if( BufCar1[0] == 'F' )
                SetLocked( true );

            if( BufCar1[1] == 'P' )
                m_ModuleStatus |= MODULE_is_PLACED;

            break;

        case 'L':       // Li = read the library name of the footprint
            *BufLine = 0;
            sscanf( PtLine, " %s", BufLine );
            m_LibRef = FROM_UTF8( BufLine );
            break;

        case 'S':
            sscanf( PtLine, " %lX", &m_TimeStamp );
            break;


        case 'O':       // (Op)tions for auto placement
            itmp1 = itmp2 = 0;
            sscanf( PtLine, " %X %X", &itmp1, &itmp2 );

            m_CntRot180 = itmp2 & 0x0F;

            if( m_CntRot180 > 10 )
                m_CntRot180 = 10;

            m_CntRot90 = itmp1 & 0x0F;

            if( m_CntRot90 > 10 )
                m_CntRot90 = 0;

            itmp1 = (itmp1 >> 4) & 0x0F;

            if( itmp1 > 10 )
                itmp1 = 0;

            m_CntRot90 |= itmp1 << 4;
            break;

        case 'A':
            if( Line[1] == 't' )
            {
                // At = (At)tributes of module
                if( strstr( PtLine, "SMD" ) )
                    m_Attributs |= MOD_CMS;

                if( strstr( PtLine, "VIRTUAL" ) )
                    m_Attributs |= MOD_VIRTUAL;
            }

            if( Line[1] == 'R' )
            {
                // alternate reference, e.g. /478C2408/478AD1B6
                sscanf( PtLine, " %s", BufLine );
                m_Path = FROM_UTF8( BufLine );
            }

            break;

        case 'T':    /* Read a footprint text description (ref, value, or
                      * drawing */
            TEXTE_MODULE * textm;
            sscanf( Line + 1, "%d", &itmp1 );

            if( itmp1 == TEXT_is_REFERENCE )
                textm = m_Reference;
            else if( itmp1 == TEXT_is_VALUE )
                textm = m_Value;
            else        // text is a drawing
            {
                textm = new TEXTE_MODULE( this );
                m_Drawings.PushBack( textm );
            }
            textm->ReadDescr( aReader );
            break;

        case 'D':    // read a drawing item
            EDGE_MODULE * edge;
            edge = new EDGE_MODULE( this );
            m_Drawings.PushBack( edge );
            edge->ReadDescr( aReader );
            edge->SetDrawCoord();
            break;

        case 'C':    // read documentation data
            m_Doc = FROM_UTF8( StrPurge( PtLine ) );
            break;

        case 'K':    // Read key words
            m_KeyWord = FROM_UTF8( StrPurge( PtLine ) );
            break;

        case '.':    // Read specific data
            if( strnicmp( Line, ".SolderMask ", 12 ) == 0 )
                SetLocalSolderMaskMargin( atoi( Line + 12 ) );
            else if( strnicmp( Line, ".SolderPaste ", 13 )  == 0 )
                SetLocalSolderPasteMargin( atoi( Line + 13 ) );
            else if( strnicmp( Line, ".SolderPasteRatio ", 18 ) == 0 )
                SetLocalSolderPasteMarginRatio( atof( Line + 18 ) );
            else if( strnicmp( Line, ".LocalClearance ", 16 ) == 0 )
                SetLocalClearance( atoi( Line + 16 ) );
            else if( strnicmp( Line, ".ZoneConnection ", 16 ) == 0 )
                m_ZoneConnection = (ZoneConnection)atoi( Line + 16 );
            else if( strnicmp( Line, ".ThermalWidth ", 14 ) == 0 )
                m_ThermalWidth = atoi( Line + 14 );
            else if( strnicmp( Line, ".ThermalGap ", 12 ) == 0 )
                m_ThermalGap = atoi( Line + 12 );
            break;

        default:
            break;
        }
    }

    // Recalculate the bounding box
    CalculateBoundingBox();
    return 0;
}


/* Read a description line like:
 *  DS 2600 0 2600 -600 120 21
 *  this description line is in Line
 *  EDGE_MODULE type can be:
 *  - Circle,
 *  - Segment (line)
 *  - Arc
 *  - Polygon
 *
 */
int EDGE_MODULE::ReadDescr( LINE_READER* aReader )
{
    int  ii;
    int  error = 0;
    char* Buf;
    char* Line;

    Line = aReader->Line();

    switch( Line[1] )
    {
    case 'S':
        m_Shape = S_SEGMENT;
        break;

    case 'C':
        m_Shape = S_CIRCLE;
        break;

    case 'A':
        m_Shape = S_ARC;
        break;

    case 'P':
        m_Shape = S_POLYGON;
        break;

    default:
        wxString msg;
        msg.Printf( wxT( "Unknown EDGE_MODULE type <%s>" ), Line );
        DisplayError( NULL, msg );
        error = 1;
        break;
    }

    switch( m_Shape )
    {
    case S_ARC:
        double angle;
        sscanf( Line + 3, "%d %d %d %d %lf %d %d",
                &m_Start0.x, &m_Start0.y,
                &m_End0.x, &m_End0.y,
                &angle, &m_Width, &m_Layer );

        NORMALIZE_ANGLE_360( angle );
        SetAngle( angle );
        break;

    case S_SEGMENT:
    case S_CIRCLE:
        sscanf( Line + 3, "%d %d %d %d %d %d",
                &m_Start0.x, &m_Start0.y,
                &m_End0.x, &m_End0.y,
                &m_Width, &m_Layer );
        break;

    case S_POLYGON:
        int pointCount;
        sscanf( Line + 3, "%d %d %d %d %d %d %d",
                &m_Start0.x, &m_Start0.y,
                &m_End0.x, &m_End0.y,
                &pointCount, &m_Width, &m_Layer );

        m_PolyPoints.clear();
        m_PolyPoints.reserve( pointCount );

        for( ii = 0;  ii<pointCount;  ii++ )
        {
            if( aReader->ReadLine() )
            {
                Buf = aReader->Line();

                if( strncmp( Buf, "Dl", 2 ) != 0 )
                {
                    error = 1;
                    break;
                }

                int x;
                int y;
                sscanf( Buf + 3, "%d %d\n", &x, &y );

                m_PolyPoints.push_back( wxPoint( x, y ) );
            }
            else
            {
                error = 1;
                break;
            }
        }

        break;

    default:
        sscanf( Line + 3, "%d %d %d %d %d %d",
                &m_Start0.x, &m_Start0.y,
                &m_End0.x, &m_End0.y,
                &m_Width, &m_Layer );
        break;
    }

    // Check for a reasonable width:
    if( m_Width <= 1 )
        m_Width = 1;

    if( m_Width > MAX_WIDTH )
        m_Width = MAX_WIDTH;

    // Check for a reasonable layer:
    // m_Layer must be >= FIRST_NON_COPPER_LAYER, but because microwave footprints
    // can use the copper layers m_Layer < FIRST_NON_COPPER_LAYER is allowed.
    // @todo: changes use of EDGE_MODULE these footprints and allows only
    // m_Layer >= FIRST_NON_COPPER_LAYER
    if( (m_Layer < 0) || (m_Layer > LAST_NON_COPPER_LAYER) )
        m_Layer = SILKSCREEN_N_FRONT;

    return error;
}


bool DIMENSION::ReadDimensionDescr( LINE_READER* aReader )
{
    char* Line;
    char  Text[2048];

    while( aReader->ReadLine() )
    {
        Line = aReader->Line();

        if( strnicmp( Line, "$EndDIMENSION", 4 ) == 0 )
            return true;

        if( Line[0] == 'V' )
        {
            sscanf( Line + 2, " %d", &m_Value );
            continue;
        }

        if( Line[0] == 'G' )
        {
            int layer;

            sscanf( Line + 2, " %d %d %lX", &m_Shape, &layer, &m_TimeStamp );

            if( layer < FIRST_NO_COPPER_LAYER )
                layer = FIRST_NO_COPPER_LAYER;

            if( layer > LAST_NO_COPPER_LAYER )
                layer = LAST_NO_COPPER_LAYER;

            SetLayer( layer );
            m_Text.SetLayer( layer );
            continue;
        }

        if( Line[0] == 'T' )
        {
            ReadDelimitedText( Text, Line + 2, sizeof(Text) );
            m_Text.m_Text = FROM_UTF8( Text );
            continue;
        }

        if( Line[0] == 'P' )
        {
            int normal_display = 1;
            int orientation;
            int thickness;
            sscanf( Line + 2, " %d %d %d %d %d %d %d",
                    &m_Text.m_Pos.x, &m_Text.m_Pos.y,
                    &m_Text.m_Size.x, &m_Text.m_Size.y,
                    &thickness, &orientation,
                    &normal_display );

            m_Text.m_Mirror = normal_display ? false : true;
            m_Pos = m_Text.m_Pos;
            m_Text.SetOrientation( orientation );
            m_Text.SetThickness( thickness );
            continue;
        }

        if( Line[0] == 'S' )
        {
            switch( Line[1] )
            {
                int Dummy;

            case 'b':
                sscanf( Line + 2, " %d %d %d %d %d %d",
                        &Dummy,
                        &m_crossBarOx, &m_crossBarOy,
                        &m_crossBarFx, &m_crossBarFy,
                        &m_Width );
                break;

            case 'd':
                sscanf( Line + 2, " %d %d %d %d %d %d",
                        &Dummy,
                        &m_featureLineDOx, &m_featureLineDOy,
                        &m_featureLineDFx, &m_featureLineDFy,
                        &Dummy );
                break;

            case 'g':
                sscanf( Line + 2, " %d %d %d %d %d %d",
                        &Dummy,
                        &m_featureLineGOx, &m_featureLineGOy,
                        &m_featureLineGFx, &m_featureLineGFy,
                        &Dummy );
                break;

            case '1':
                sscanf( Line + 2, " %d %d %d %d %d %d",
                        &Dummy,
                        &m_arrowD1Ox, &m_arrowD1Oy,
                        &m_arrowD1Fx, &m_arrowD1Fy,
                        &Dummy );
                break;

            case '2':
                sscanf( Line + 2, " %d %d %d %d %d %d",
                        &Dummy,
                        &m_arrowD2Ox, &m_arrowD2Oy,
                        &m_arrowD2Fx, &m_arrowD2Fy,
                        &Dummy );
                break;

            case '3':
                sscanf( Line + 2, " %d %d %d %d %d %d\n",
                        &Dummy,
                        &m_arrowG1Ox, &m_arrowG1Oy,
                        &m_arrowG1Fx, &m_arrowG1Fy,
                        &Dummy );
                break;

            case '4':
                sscanf( Line + 2, " %d %d %d %d %d %d",
                        &Dummy,
                        &m_arrowG2Ox, &m_arrowG2Oy,
                        &m_arrowG2Fx, &m_arrowG2Fy,
                        &Dummy );
                break;
            }

            continue;
        }
    }

    return false;
}


bool DRAWSEGMENT::ReadDrawSegmentDescr( LINE_READER* aReader )
{
    char* Line;

    while( aReader->ReadLine() )
    {
        Line = aReader->Line();

        if( strnicmp( Line, "$End", 4 ) == 0 )
            return true; // End of description

        if( Line[0] == 'P' )
        {
            sscanf( Line + 2, " %d %d %d %d %d %d",
                    &m_Shape, &m_Start.x, &m_Start.y,
                    &m_End.x, &m_End.y, &m_Width );

            if( m_Width < 0 )
                m_Width = 0;
        }

        if( Line[0] == 'D' )
        {
            int status;
            char* token = 0;

            token = strtok( Line," " );

            for( int i = 0; (token = strtok( NULL," " )) != NULL; i++ )
            {
                switch( i )
                {
                case 0:
                    sscanf( token,"%d",&m_Layer );
                    break;
                case 1:
                    sscanf( token,"%d",&m_Type );
                    break;
                case 2:
                    double angle;
                    sscanf( token, "%lf", &angle );
                    SetAngle( angle );
                    break;
                case 3:
                    sscanf( token,"%lX",&m_TimeStamp );
                    break;
                case 4:
                    sscanf( token,"%X",&status );
                    break;
                    // Bezier Control Points
                case 5:
                    sscanf( token,"%d",&m_BezierC1.x );
                    break;
                case 6:
                    sscanf( token,"%d",&m_BezierC1.y );
                    break;
                case 7:
                    sscanf( token,"%d",&m_BezierC2.x );
                    break;
                case 8:
                    sscanf( token,"%d",&m_BezierC2.y );
                    break;
                default:
                    break;
                }
            }

            if( m_Layer < FIRST_NO_COPPER_LAYER )
                m_Layer = FIRST_NO_COPPER_LAYER;

            if( m_Layer > LAST_NO_COPPER_LAYER )
                m_Layer = LAST_NO_COPPER_LAYER;

            SetState( status, ON );
        }
    }

    return false;
}


/* Read NETINFO_ITEM from file.
 * Returns 0 if OK
 * 1 if incomplete reading
 */
int NETINFO_ITEM::ReadDescr( LINE_READER* aReader )
{
    char* Line;
    char  Ltmp[1024];
    int   tmp;

    while( aReader->ReadLine() )
    {
        Line = aReader->Line();
        if( strnicmp( Line, "$End", 4 ) == 0 )
            return 0;

        if( strncmp( Line, "Na", 2 ) == 0 )
        {
            sscanf( Line + 2, " %d", &tmp );
            SetNet( tmp );

            ReadDelimitedText( Ltmp, Line + 2, sizeof(Ltmp) );
            m_Netname = FROM_UTF8( Ltmp );
            continue;
        }
    }

    return 1;
}


/* Read the description from the PCB file.
 */
bool PCB_TARGET::ReadMirePcbDescr( LINE_READER* aReader )
{
    char* Line;

    while( aReader->ReadLine() )
    {
        Line = aReader->Line();

        if( strnicmp( Line, "$End", 4 ) == 0 )
            return true;

        if( Line[0] == 'P' )
        {
            sscanf( Line + 2, " %X %d %d %d %d %d %lX",
                    &m_Shape, &m_Layer,
                    &m_Pos.x, &m_Pos.y,
                    &m_Size, &m_Width, &m_TimeStamp );

            if( m_Layer < FIRST_NO_COPPER_LAYER )
                m_Layer = FIRST_NO_COPPER_LAYER;

            if( m_Layer > LAST_NO_COPPER_LAYER )
                m_Layer = LAST_NO_COPPER_LAYER;
        }
    }

    return false;
}


int ZONE_CONTAINER::ReadDescr( LINE_READER* aReader )
{
    char* Line, * text;
    char  netname_buffer[1024];
    int   ret;
    int   outline_hatch = CPolyLine::NO_HATCH;
    bool  error = false, has_corner = false;

    netname_buffer[0] = 0;

    while( aReader->ReadLine() )
    {
        Line = aReader->Line();

        if( strnicmp( Line, "ZCorner", 7 ) == 0 ) // new corner found
        {
            int x;
            int y;
            int flag;

            text = Line + 7;
            ret  = sscanf( text, "%d %d %d", &x, &y, &flag );

            if( ret < 3 )
            {
                error = true;
            }
            else
            {
                if( !has_corner )
                    m_Poly->Start( m_Layer, x, y, outline_hatch );
                else
                    AppendCorner( wxPoint( x, y ) );

                has_corner = true;

                if( flag )
                    m_Poly->Close();
            }
        }
        else if( strnicmp( Line, "ZInfo", 5 ) == 0 )   // general info found
        {
            int ts;
            int netcode;

            text = Line + 5;
            ret  = sscanf( text, "%X %d %s", &ts, &netcode, netname_buffer );

            if( ret < 3 )
            {
                error = true;
            }
            else
            {
                SetTimeStamp( ts );
                SetNet( netcode );
                ReadDelimitedText( netname_buffer, netname_buffer, 1024 );
                m_Netname = FROM_UTF8( netname_buffer );
            }
        }
        else if( strnicmp( Line, "ZLayer", 6 ) == 0 )  // layer found
        {
            int x;

            text = Line + 6;
            ret  = sscanf( text, "%d", &x );

            if( ret < 1 )
                error = true;
            else
                m_Layer = x;
        }
        else if( strnicmp( Line, "ZAux", 4 ) == 0 )    // aux info found
        {
            int  x;
            char hopt[10];

            text = Line + 4;
            ret  = sscanf( text, "%d %c", &x, hopt );

            if( ret < 2 )
            {
                error = true;
            }
            else
            {
                switch( hopt[0] )
                {
                case 'n':
                case 'N':
                    outline_hatch = CPolyLine::NO_HATCH;
                    break;

                case 'e':
                case 'E':
                    outline_hatch = CPolyLine::DIAGONAL_EDGE;
                    break;

                case 'f':
                case 'F':
                    outline_hatch = CPolyLine::DIAGONAL_FULL;
                    break;
                }
            }
            // Set hatch mode later, after reading outlines corners data
        }

        else if( strnicmp( Line, "ZPriority", 9 ) == 0 )
        {
            int tmp = 0;
            text = Line + 9;
            ret  = sscanf( text, "%d", &tmp );
            if( ret < 1 )
                return false;
            SetPriority( tmp );
        }

        else if( strnicmp( Line, "ZSmoothing", 10 ) == 0 )
        {
            int tempSmoothingType;
            int tempCornerRadius;
            text = Line + 10;
            ret  = sscanf( text, "%d %d", &tempSmoothingType, &tempCornerRadius );

            if( ret < 2 )
                return false;

            if( tempSmoothingType >= ZONE_SETTINGS::SMOOTHING_LAST )
                return false;

            if( tempSmoothingType < 0 )
                return false;

            cornerSmoothingType = tempSmoothingType;
            SetCornerRadius( tempCornerRadius );
        }
        else if( strnicmp( Line, "ZOptions", 8 ) == 0 )    // Options info found
        {
            int  fillmode = 1;
            int  arcsegmentcount = ARC_APPROX_SEGMENTS_COUNT_LOW_DEF;
            char fillstate = 'F';
            text = Line + 8;
            ret  = sscanf( text, "%d %d %c %d %d", &fillmode, &arcsegmentcount, &fillstate,
                           &m_ThermalReliefGap, &m_ThermalReliefCopperBridge );

            if( ret < 1 )  // Must find 1 or more args.
                return false;
            else
                m_FillMode = fillmode ? 1 : 0;

            if( arcsegmentcount >= ARC_APPROX_SEGMENTS_COUNT_HIGHT_DEF )
                m_ArcToSegmentsCount = ARC_APPROX_SEGMENTS_COUNT_HIGHT_DEF;

            m_IsFilled = (fillstate == 'S') ? true : false;
        }
        else if( strnicmp( Line, "ZClearance", 10 ) == 0 ) // Clearance and pad options info found
        {
            int  clearance = 200;
            char padConnection;
            text = Line + 10;
            ret  = sscanf( text, "%d %1c", &clearance, &padConnection );

            if( ret < 2 )
            {
                error = true;
            }
            else
            {
                m_ZoneClearance = clearance;

                switch( padConnection )
                {
                case 'i':
                case 'I':
                    m_PadConnection = PAD_IN_ZONE;
                    break;

                case 't':
                case 'T':
                    m_PadConnection = THERMAL_PAD;
                    break;

                case 'x':
                case 'X':
                    m_PadConnection = PAD_NOT_IN_ZONE;
                    break;
                }
            }
        }
        else if( strnicmp( Line, "ZMinThickness", 13 ) == 0 )    // Min Thickness info found
        {
            int thickness;
            text = Line + 13;
            ret  = sscanf( text, "%d", &thickness );

            if( ret < 1 )
                error = true;
            else
                m_ZoneMinThickness = thickness;
        }
        else if( strnicmp( Line, "$POLYSCORNERS", 13 ) == 0  )  // Read the PolysList (polygons used for fill areas in the zone)
        {
            while( aReader->ReadLine() )
            {
                Line = aReader->Line();

                if( strnicmp( Line, "$endPOLYSCORNERS", 4 ) == 0  )
                    break;

                CPolyPt corner;
                int     end_contour, utility;
                utility = 0;
                ret     = sscanf( Line,
                                  "%d %d %d %d",
                                  &corner.x,
                                  &corner.y,
                                  &end_contour,
                                  &utility );
                if( ret < 4 )
                    return false;

                corner.end_contour = end_contour ? true : false;
                corner.utility     = utility;
                m_FilledPolysList.push_back( corner );
            }
        }
        else if( strnicmp( Line, "$FILLSEGMENTS", 13 ) == 0  )
        {
            SEGMENT segm;
            while( aReader->ReadLine() )
            {
                Line = aReader->Line();

                if( strnicmp( Line, "$endFILLSEGMENTS", 4 ) == 0  )
                    break;

                ret = sscanf( Line,
                              "%d %d %d %d",
                              &segm.m_Start.x,
                              &segm.m_Start.y,
                              &segm.m_End.x,
                              &segm.m_End.y );
                if( ret < 4 )
                    return false;

                m_FillSegmList.push_back( segm );
            }
        }
        else if( strnicmp( Line, "$end", 4 ) == 0 )    // end of description
        {
            break;
        }
    }

    if( !IsOnCopperLayer() )
    {
        m_FillMode = 0;
        SetNet( 0 );
    }

    // Set hatch here, when outlines corners are read
    m_Poly->SetHatch( outline_hatch, Mils2iu( m_Poly->GetDefaultHatchPitchMils() ) );

    return error ? 0 : 1;
}


bool NETCLASS::ReadDescr( LINE_READER* aReader )
{
    bool        result = false;
    char*       line;
    char        buf[1024];
    wxString    netname;

    while( aReader->ReadLine() )
    {
        line = aReader->Line();
        if( strnicmp( line, "AddNet", 6 ) == 0 )
        {
            ReadDelimitedText( buf, line + 6, sizeof(buf) );
            netname = FROM_UTF8( buf );
            Add( netname );
            continue;
        }

        if( strnicmp( line, "$endNCLASS", sizeof( "$endNCLASS" ) - 1 ) == 0 )
        {
            result = true;
            break;
        }

        if( strnicmp( line, "Clearance", 9 ) == 0 )
        {
            SetClearance( atoi( line + 9 ) );
            continue;
        }
        if( strnicmp( line, "TrackWidth", 10 ) == 0 )
        {
            SetTrackWidth( atoi( line + 10 ) );
            continue;
        }
        if( strnicmp( line, "ViaDia", 6 ) == 0 )
        {
            SetViaDiameter( atoi( line + 6 ) );
            continue;
        }
        if( strnicmp( line, "ViaDrill", 8 ) == 0 )
        {
            SetViaDrill( atoi( line + 8 ) );
            continue;
        }

        if( strnicmp( line, "uViaDia", 7 ) == 0 )
        {
            SetuViaDiameter( atoi( line + 7 ) );
            continue;
        }
        if( strnicmp( line, "uViaDrill", 9 ) == 0 )
        {
            SetuViaDrill( atoi( line + 9 ) );
            continue;
        }

        if( strnicmp( line, "Name", 4 ) == 0 )
        {
            ReadDelimitedText( buf, line + 4, sizeof(buf) );
            m_Name = FROM_UTF8( buf );
            continue;
        }
        if( strnicmp( line, "Desc", 4 ) == 0 )
        {
            ReadDelimitedText( buf, line + 4, sizeof(buf) );
            SetDescription( FROM_UTF8( buf ) );
            continue;
        }
    }

    return result;
}



/**
 * Function ReadTextePcbDescr
 * Read a text description from pcb file.
 *
 * For a single line text:
 *
 * $TEXTPCB
 * Te "Text example"
 * Po 66750 53450 600 800 150 0
 * From 24 1 0 Italic
 * $EndTEXTPCB
 *
 * For a multi line text
 *
 * $TEXTPCB
 * Te "Text example"
 * Nl "Line 2"
 * Po 66750 53450 600 800 150 0
 * From 24 1 0 Italic
 * $EndTEXTPCB
 * Nl "line nn" is a line added to the current text
 */
int TEXTE_PCB::ReadTextePcbDescr( LINE_READER* aReader )
{
    char* line;
    char  text[1024];
    char  style[256];

    while( aReader->ReadLine() )
    {
        line = aReader->Line();
        if( strnicmp( line, "$EndTEXTPCB", 11 ) == 0 )
            return 0;
        if( strncmp( line, "Te", 2 ) == 0 ) // Text line (first line for multi line texts
        {
            ReadDelimitedText( text, line + 2, sizeof(text) );
            m_Text = FROM_UTF8( text );
            continue;
        }
        if( strncmp( line, "nl", 2 ) == 0 ) // next line of the current text
        {
            ReadDelimitedText( text, line + 2, sizeof(text) );
            m_Text.Append( '\n' );
            m_Text += FROM_UTF8( text );
            continue;
        }
        if( strncmp( line, "Po", 2 ) == 0 )
        {
            double angle;
            sscanf( line + 2, " %d %d %d %d %d %lf",
                    &m_Pos.x, &m_Pos.y, &m_Size.x, &m_Size.y,
                    &m_Thickness, &angle );

            SetOrientation( angle );

            // Ensure the text has minimal size to see this text on screen:
            if( m_Size.x < 5 )
                m_Size.x = 5;
            if( m_Size.y < 5 )
                m_Size.y = 5;
            continue;
        }
        if( strncmp( line, "De", 2 ) == 0 )
        {
            style[0] = 0;
            int normal_display = 1;
            char hJustify = 'c';
            sscanf( line + 2, " %d %d %lX %s %c\n", &m_Layer, &normal_display,
                    &m_TimeStamp, style, &hJustify );

            m_Mirror = normal_display ? false : true;

            if( m_Layer < FIRST_COPPER_LAYER )
                m_Layer = FIRST_COPPER_LAYER;
            if( m_Layer > LAST_NO_COPPER_LAYER )
                m_Layer = LAST_NO_COPPER_LAYER;

            if( strnicmp( style, "Italic", 6 ) == 0 )
                m_Italic = 1;
            else
                m_Italic = 0;

            switch( hJustify )
            {
            case 'l':
            case 'L':
                m_HJustify = GR_TEXT_HJUSTIFY_LEFT;
                break;
            case 'c':
            case 'C':
                m_HJustify = GR_TEXT_HJUSTIFY_CENTER;
                break;
            case 'r':
            case 'R':
                m_HJustify = GR_TEXT_HJUSTIFY_RIGHT;
                break;
            default:
                m_HJustify = GR_TEXT_HJUSTIFY_CENTER;
                break;
            }
            continue;
        }
    }

     // Set a reasonable width:
    if( m_Thickness < 1 )
        m_Thickness = 1;
    m_Thickness = Clamp_Text_PenSize( m_Thickness, m_Size );

    return 1;
}

/**
 * Function ReadDescr
 * Read description from a given line in "*.brd" format.
 * @param aReader The line reader object which contains the first line of description.
 * @return int - > 0 if success reading else 0.
 */
int TEXTE_MODULE::ReadDescr( LINE_READER* aReader )
{
    int     success = true;
    int     type;
    char    BufCar1[128], BufCar2[128], BufCar3[128];
    char*   line = aReader->Line();
    double  angle;

    int     layer = SILKSCREEN_N_FRONT;

    BufCar1[0] = 0;
    BufCar2[0] = 0;
    BufCar3[0] = 0;

    if( sscanf( line + 1, "%d %d %d %d %d %lf %d %s %s %d %s",
                &type,
                &m_Pos0.x, &m_Pos0.y,
                &m_Size.y, &m_Size.x,
                &angle,    &m_Thickness,
                BufCar1, BufCar2, &layer, BufCar3 ) >= 10 )
    {
        success = true;

        SetOrientation( angle );
    }


    if( (type != TEXT_is_REFERENCE) && (type != TEXT_is_VALUE) )
        type = TEXT_is_DIVERS;

    m_Type = type;

    // Due to the Pcbnew history, .m_Orient is saved in screen value
    // but it is handled as relative to its parent footprint
    m_Orient -= ( (MODULE*) m_Parent )->m_Orient;

    if( BufCar1[0] == 'M' )
        m_Mirror = true;
    else
        m_Mirror = false;

    if( BufCar2[0]  == 'I' )
        m_NoShow = true;
    else
        m_NoShow = false;

    if( BufCar3[0]  == 'I' )
        m_Italic = true;
    else
        m_Italic = false;

    // Test for a reasonable layer:
    if( layer < 0 )
        layer = 0;
    if( layer > LAST_NO_COPPER_LAYER )
        layer = LAST_NO_COPPER_LAYER;
    if( layer == LAYER_N_BACK )
        layer = SILKSCREEN_N_BACK;
    else if( layer == LAYER_N_FRONT )
        layer = SILKSCREEN_N_FRONT;

    SetLayer( layer );

    // Calculate the actual position.
    SetDrawCoord();


    // Search and read the "text" string (a quoted text).
    ReadDelimitedText( &m_Text, line );

    // Test for a reasonable size:
    if( m_Size.x < TEXTS_MIN_SIZE )
        m_Size.x = TEXTS_MIN_SIZE;
    if( m_Size.y < TEXTS_MIN_SIZE )
        m_Size.y = TEXTS_MIN_SIZE;

    // Set a reasonable width:
    if( m_Thickness < 1 )
        m_Thickness = 1;
    m_Thickness = Clamp_Text_PenSize( m_Thickness, m_Size );

    return success;
}

#endif  // USE_NEW_PCBNEW_LOAD
