Browse Source
Use Record handling for parsing
Use Record handling for parsing
Now, we know how big a record is, and should be able to parse all boards without missing bytes?pull/16/head
committed by
Ian McInerney
32 changed files with 6088 additions and 26 deletions
-
14common/wildcards_and_files_ext.cpp
-
1cvpcb/CMakeLists.txt
-
18eeschema/sch_io_mgr.h
-
3include/wildcards_and_files_ext.h
-
2pcbnew/CMakeLists.txt
-
22pcbnew/altium2kicadpcb_plugin/CMakeLists.txt
-
101pcbnew/altium2kicadpcb_plugin/altium_circuit_maker_plugin.cpp
-
63pcbnew/altium2kicadpcb_plugin/altium_circuit_maker_plugin.h
-
101pcbnew/altium2kicadpcb_plugin/altium_circuit_studio_plugin.cpp
-
63pcbnew/altium2kicadpcb_plugin/altium_circuit_studio_plugin.h
-
101pcbnew/altium2kicadpcb_plugin/altium_designer_plugin.cpp
-
63pcbnew/altium2kicadpcb_plugin/altium_designer_plugin.h
-
234pcbnew/altium2kicadpcb_plugin/altium_parser.cpp
-
199pcbnew/altium2kicadpcb_plugin/altium_parser.h
-
658pcbnew/altium2kicadpcb_plugin/altium_parser.ksy
-
941pcbnew/altium2kicadpcb_plugin/altium_parser_pcb.cpp
-
616pcbnew/altium2kicadpcb_plugin/altium_parser_pcb.h
-
2011pcbnew/altium2kicadpcb_plugin/altium_pcb.cpp
-
187pcbnew/altium2kicadpcb_plugin/altium_pcb.h
-
1pcbnew/eagle_plugin.h
-
30pcbnew/files.cpp
-
19pcbnew/io_mgr.cpp
-
14pcbnew/io_mgr.h
-
3qa/gal/gal_pixel_alignment/CMakeLists.txt
-
1qa/pcbnew/CMakeLists.txt
-
1qa/pcbnew_tools/CMakeLists.txt
-
1thirdparty/CMakeLists.txt
-
3thirdparty/compoundfilereader/CMakeLists.txt
-
22thirdparty/compoundfilereader/LICENSE.MIT
-
4thirdparty/compoundfilereader/README.txt
-
480thirdparty/compoundfilereader/compoundfilereader.h
-
137thirdparty/compoundfilereader/utf.h
@ -0,0 +1,22 @@ |
|||
|
|||
# Sources for the pcbnew PLUGIN called ALTIUM_DESIGNER_PLUGIN, ALTIUM_CIRCUIT_STUDIO_PLUGIN and ALTIUM_CIRCUIT_MAKER_PLUGIN |
|||
|
|||
set( ALTIUM2PCBNEW_SRCS |
|||
altium_circuit_maker_plugin.cpp |
|||
altium_circuit_studio_plugin.cpp |
|||
altium_designer_plugin.cpp |
|||
altium_pcb.cpp |
|||
altium_parser.cpp |
|||
altium_parser_pcb.cpp |
|||
) |
|||
|
|||
add_library( altium2kicadpcb STATIC ${ALTIUM2PCBNEW_SRCS} ) |
|||
|
|||
add_dependencies( altium2kicadpcb compoundfilereader ) |
|||
|
|||
target_link_libraries( altium2kicadpcb pcbcommon ) |
|||
|
|||
target_include_directories( altium2kicadpcb PUBLIC |
|||
${CMAKE_CURRENT_SOURCE_DIR} |
|||
$<TARGET_PROPERTY:compoundfilereader,INTERFACE_INCLUDE_DIRECTORIES> |
|||
) |
@ -0,0 +1,101 @@ |
|||
/*
|
|||
* This program source code file is part of KiCad, a free EDA CAD application. |
|||
* |
|||
* Copyright (C) 2019 Thomas Pointhuber <thomas.pointhuber@gmx.at> |
|||
* |
|||
* 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 |
|||
*/ |
|||
|
|||
/**
|
|||
* @file altium_plugin.cpp |
|||
* @brief Pcbnew PLUGIN for Altium *.PcbDoc format. |
|||
*/ |
|||
|
|||
#include <iomanip>
|
|||
|
|||
#include <wx/string.h>
|
|||
|
|||
#include <altium_circuit_maker_plugin.h>
|
|||
#include <altium_pcb.h>
|
|||
|
|||
#include <class_board.h>
|
|||
|
|||
#include <compoundfilereader.h>
|
|||
#include <utf.h>
|
|||
|
|||
ALTIUM_CIRCUIT_MAKER_PLUGIN::ALTIUM_CIRCUIT_MAKER_PLUGIN() |
|||
{ |
|||
m_board = nullptr; |
|||
m_props = nullptr; |
|||
} |
|||
|
|||
|
|||
ALTIUM_CIRCUIT_MAKER_PLUGIN::~ALTIUM_CIRCUIT_MAKER_PLUGIN() |
|||
{ |
|||
} |
|||
|
|||
|
|||
const wxString ALTIUM_CIRCUIT_MAKER_PLUGIN::PluginName() const |
|||
{ |
|||
return wxT( "Altium Circuit Maker" ); |
|||
} |
|||
|
|||
|
|||
const wxString ALTIUM_CIRCUIT_MAKER_PLUGIN::GetFileExtension() const |
|||
{ |
|||
return wxT( "CMPcbDoc" ); |
|||
} |
|||
|
|||
|
|||
BOARD* ALTIUM_CIRCUIT_MAKER_PLUGIN::Load( |
|||
const wxString& aFileName, BOARD* aAppendToMe, const PROPERTIES* aProperties ) |
|||
{ |
|||
m_props = aProperties; |
|||
|
|||
m_board = aAppendToMe ? aAppendToMe : new BOARD(); |
|||
|
|||
// Give the filename to the board if it's new
|
|||
if( !aAppendToMe ) |
|||
m_board->SetFileName( aFileName ); |
|||
|
|||
// clang-format off
|
|||
const std::map<ALTIUM_PCB_DIR, std::string> mapping = { |
|||
{ ALTIUM_PCB_DIR::FILE_HEADER, "FileHeader" }, |
|||
{ ALTIUM_PCB_DIR::ARCS6, "1CEEB63FB33847F8AFC4485F64735E\\Data" }, |
|||
{ ALTIUM_PCB_DIR::BOARD6, "96B09F5C6CEE434FBCE0DEB3E88E70\\Data" }, |
|||
{ ALTIUM_PCB_DIR::BOARDREGIONS, "E3A544335C30403A991912052C936F\\Data" }, |
|||
{ ALTIUM_PCB_DIR::CLASSES6, "4F71DD45B09143988210841EA1C28D\\Data" }, |
|||
{ ALTIUM_PCB_DIR::COMPONENTS6, "F9D060ACC7DD4A85BC73CB785BAC81\\Data" }, |
|||
{ ALTIUM_PCB_DIR::DIMENSIONS6, "068B9422DBB241258BA2DE9A6BA1A6\\Data" }, |
|||
{ ALTIUM_PCB_DIR::FILLS6, "6FFE038462A940E9B422EFC8F5D85E\\Data" }, |
|||
{ ALTIUM_PCB_DIR::NETS6, "35D7CF51BB9B4875B3A138B32D80DC\\Data" }, |
|||
{ ALTIUM_PCB_DIR::PADS6, "4F501041A9BC4A06BDBDAB67D3820E\\Data" }, |
|||
{ ALTIUM_PCB_DIR::POLYGONS6, "A1931C8B0B084A61AA45146575FDD3\\Data" }, |
|||
{ ALTIUM_PCB_DIR::REGIONS6, "F513A5885418472886D3EF18A09E46\\Data" }, |
|||
{ ALTIUM_PCB_DIR::RULES6, "C27718A40C94421388FAE5BD7785D7\\Data" }, |
|||
{ ALTIUM_PCB_DIR::SHAPEBASEDREGIONS6,"BDAA2C70289849078C8EBEEC7F0848\\Data" }, |
|||
{ ALTIUM_PCB_DIR::TEXTS6, "A34BC67C2A5F408D8F377378C5C5E2\\Data" }, |
|||
{ ALTIUM_PCB_DIR::TRACKS6, "412A754DBB864645BF01CD6A80C358\\Data" }, |
|||
{ ALTIUM_PCB_DIR::VIAS6, "C87A685A0EFA4A90BEEFD666198B56\\Data" } |
|||
}; |
|||
// clang-format on
|
|||
|
|||
ParseAltiumPcb( m_board, aFileName, mapping ); |
|||
|
|||
return m_board; |
|||
} |
@ -0,0 +1,63 @@ |
|||
/* |
|||
* This program source code file is part of KiCad, a free EDA CAD application. |
|||
* |
|||
* Copyright (C) 2019 Thomas Pointhuber <thomas.pointhuber@gmx.at> |
|||
* |
|||
* 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 |
|||
*/ |
|||
|
|||
/** |
|||
* @file pcad_plugin.h |
|||
* @brief Pcbnew PLUGIN for Altium *.PcbDoc format. |
|||
*/ |
|||
|
|||
#ifndef ALTIUM_CIRCUIT_MAKER_PLUGIN_H_ |
|||
#define ALTIUM_CIRCUIT_MAKER_PLUGIN_H_ |
|||
|
|||
|
|||
#include <io_mgr.h> |
|||
|
|||
class ALTIUM_CIRCUIT_MAKER_PLUGIN : public PLUGIN |
|||
{ |
|||
public: |
|||
// -----<PUBLIC PLUGIN API>-------------------------------------------------- |
|||
|
|||
const wxString PluginName() const override; |
|||
|
|||
BOARD* Load( |
|||
const wxString& aFileName, BOARD* aAppendToMe, const PROPERTIES* aProperties ) override; |
|||
|
|||
const wxString GetFileExtension() const override; |
|||
|
|||
long long GetLibraryTimestamp( const wxString& aLibraryPath ) const override |
|||
{ |
|||
// TODO? |
|||
return 0; |
|||
} |
|||
|
|||
// -----</PUBLIC PLUGIN API>------------------------------------------------- |
|||
|
|||
ALTIUM_CIRCUIT_MAKER_PLUGIN(); |
|||
~ALTIUM_CIRCUIT_MAKER_PLUGIN(); |
|||
|
|||
private: |
|||
const PROPERTIES* m_props; |
|||
BOARD* m_board; |
|||
}; |
|||
|
|||
#endif // ALTIUM_CIRCUIT_MAKER_PLUGIN_H_ |
@ -0,0 +1,101 @@ |
|||
/*
|
|||
* This program source code file is part of KiCad, a free EDA CAD application. |
|||
* |
|||
* Copyright (C) 2019 Thomas Pointhuber <thomas.pointhuber@gmx.at> |
|||
* |
|||
* 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 |
|||
*/ |
|||
|
|||
/**
|
|||
* @file altium_plugin.cpp |
|||
* @brief Pcbnew PLUGIN for Altium *.PcbDoc format. |
|||
*/ |
|||
|
|||
#include <iomanip>
|
|||
|
|||
#include <wx/string.h>
|
|||
|
|||
#include <altium_circuit_studio_plugin.h>
|
|||
#include <altium_pcb.h>
|
|||
|
|||
#include <class_board.h>
|
|||
|
|||
#include <compoundfilereader.h>
|
|||
#include <utf.h>
|
|||
|
|||
ALTIUM_CIRCUIT_STUDIO_PLUGIN::ALTIUM_CIRCUIT_STUDIO_PLUGIN() |
|||
{ |
|||
m_board = nullptr; |
|||
m_props = nullptr; |
|||
} |
|||
|
|||
|
|||
ALTIUM_CIRCUIT_STUDIO_PLUGIN::~ALTIUM_CIRCUIT_STUDIO_PLUGIN() |
|||
{ |
|||
} |
|||
|
|||
|
|||
const wxString ALTIUM_CIRCUIT_STUDIO_PLUGIN::PluginName() const |
|||
{ |
|||
return wxT( "Altium Circuit Studio" ); |
|||
} |
|||
|
|||
|
|||
const wxString ALTIUM_CIRCUIT_STUDIO_PLUGIN::GetFileExtension() const |
|||
{ |
|||
return wxT( "CSPcbDoc" ); |
|||
} |
|||
|
|||
|
|||
BOARD* ALTIUM_CIRCUIT_STUDIO_PLUGIN::Load( |
|||
const wxString& aFileName, BOARD* aAppendToMe, const PROPERTIES* aProperties ) |
|||
{ |
|||
m_props = aProperties; |
|||
|
|||
m_board = aAppendToMe ? aAppendToMe : new BOARD(); |
|||
|
|||
// Give the filename to the board if it's new
|
|||
if( !aAppendToMe ) |
|||
m_board->SetFileName( aFileName ); |
|||
|
|||
// clang-format off
|
|||
const std::map<ALTIUM_PCB_DIR, std::string> mapping = { |
|||
{ ALTIUM_PCB_DIR::FILE_HEADER, "FileHeader" }, |
|||
{ ALTIUM_PCB_DIR::ARCS6, "00C595EB90524FFC8C3BD9670020A2\\Data" }, |
|||
{ ALTIUM_PCB_DIR::BOARD6, "88857D7F1DF64F7BBB61848C965636\\Data" }, |
|||
{ ALTIUM_PCB_DIR::BOARDREGIONS, "8957CF30F167408D9D263D23FE7C89\\Data" }, |
|||
{ ALTIUM_PCB_DIR::CLASSES6, "847EFBF87A5149B1AA326A52AD6357\\Data" }, |
|||
{ ALTIUM_PCB_DIR::COMPONENTS6, "465416896A15486999A39C643935D2\\Data" }, |
|||
{ ALTIUM_PCB_DIR::DIMENSIONS6, "16C81DBC13C447FF8B42A426677F3C\\Data" }, |
|||
{ ALTIUM_PCB_DIR::FILLS6, "4E83BDC3253747F08E9006D7F57020\\Data" }, |
|||
{ ALTIUM_PCB_DIR::NETS6, "D95A0DA2FE9047779A5194C127F30B\\Data" }, |
|||
{ ALTIUM_PCB_DIR::PADS6, "47D69BC5107A4B8DB8DAA23E39C238\\Data" }, |
|||
{ ALTIUM_PCB_DIR::POLYGONS6, "D7038392280E4E229B9D9B5426B295\\Data" }, |
|||
{ ALTIUM_PCB_DIR::REGIONS6, "FFDDC21382BB42FE8A7D0C328D272C\\Data" }, |
|||
{ ALTIUM_PCB_DIR::RULES6, "48B2FA96DB7546818752B34373D6C6\\Data" }, |
|||
{ ALTIUM_PCB_DIR::SHAPEBASEDREGIONS6, "D5F54B536E124FB89E2D51B1121508\\Data" }, |
|||
{ ALTIUM_PCB_DIR::TEXTS6, "349ABBB211DB4F5B8AE41B1B49555A\\Data" }, |
|||
{ ALTIUM_PCB_DIR::TRACKS6, "530C20C225354B858B2578CAB8C08D\\Data" }, |
|||
{ ALTIUM_PCB_DIR::VIAS6, "CA5F5989BCDB404DA70A9D1D3D5758\\Data" } |
|||
}; |
|||
// clang-format on
|
|||
|
|||
ParseAltiumPcb( m_board, aFileName, mapping ); |
|||
|
|||
return m_board; |
|||
} |
@ -0,0 +1,63 @@ |
|||
/* |
|||
* This program source code file is part of KiCad, a free EDA CAD application. |
|||
* |
|||
* Copyright (C) 2019 Thomas Pointhuber <thomas.pointhuber@gmx.at> |
|||
* |
|||
* 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 |
|||
*/ |
|||
|
|||
/** |
|||
* @file pcad_plugin.h |
|||
* @brief Pcbnew PLUGIN for Altium *.PcbDoc format. |
|||
*/ |
|||
|
|||
#ifndef ALTIUM_CIRCUIT_STUDIO_PLUGIN_H_ |
|||
#define ALTIUM_CIRCUIT_STUDIO_PLUGIN_H_ |
|||
|
|||
|
|||
#include <io_mgr.h> |
|||
|
|||
class ALTIUM_CIRCUIT_STUDIO_PLUGIN : public PLUGIN |
|||
{ |
|||
public: |
|||
// -----<PUBLIC PLUGIN API>-------------------------------------------------- |
|||
|
|||
const wxString PluginName() const override; |
|||
|
|||
BOARD* Load( |
|||
const wxString& aFileName, BOARD* aAppendToMe, const PROPERTIES* aProperties ) override; |
|||
|
|||
const wxString GetFileExtension() const override; |
|||
|
|||
long long GetLibraryTimestamp( const wxString& aLibraryPath ) const override |
|||
{ |
|||
// TODO? |
|||
return 0; |
|||
} |
|||
|
|||
// -----</PUBLIC PLUGIN API>------------------------------------------------- |
|||
|
|||
ALTIUM_CIRCUIT_STUDIO_PLUGIN(); |
|||
~ALTIUM_CIRCUIT_STUDIO_PLUGIN(); |
|||
|
|||
private: |
|||
const PROPERTIES* m_props; |
|||
BOARD* m_board; |
|||
}; |
|||
|
|||
#endif // ALTIUM_CIRCUIT_STUDIO_PLUGIN_H_ |
@ -0,0 +1,101 @@ |
|||
/*
|
|||
* This program source code file is part of KiCad, a free EDA CAD application. |
|||
* |
|||
* Copyright (C) 2019 Thomas Pointhuber <thomas.pointhuber@gmx.at> |
|||
* |
|||
* 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 |
|||
*/ |
|||
|
|||
/**
|
|||
* @file altium_plugin.cpp |
|||
* @brief Pcbnew PLUGIN for Altium *.PcbDoc format. |
|||
*/ |
|||
|
|||
#include <iomanip>
|
|||
|
|||
#include <wx/string.h>
|
|||
|
|||
#include <altium_designer_plugin.h>
|
|||
#include <altium_pcb.h>
|
|||
|
|||
#include <class_board.h>
|
|||
|
|||
#include <compoundfilereader.h>
|
|||
#include <utf.h>
|
|||
|
|||
ALTIUM_DESIGNER_PLUGIN::ALTIUM_DESIGNER_PLUGIN() |
|||
{ |
|||
m_board = nullptr; |
|||
m_props = nullptr; |
|||
} |
|||
|
|||
|
|||
ALTIUM_DESIGNER_PLUGIN::~ALTIUM_DESIGNER_PLUGIN() |
|||
{ |
|||
} |
|||
|
|||
|
|||
const wxString ALTIUM_DESIGNER_PLUGIN::PluginName() const |
|||
{ |
|||
return wxT( "Altium Designer" ); |
|||
} |
|||
|
|||
|
|||
const wxString ALTIUM_DESIGNER_PLUGIN::GetFileExtension() const |
|||
{ |
|||
return wxT( "PcbDoc" ); |
|||
} |
|||
|
|||
|
|||
BOARD* ALTIUM_DESIGNER_PLUGIN::Load( |
|||
const wxString& aFileName, BOARD* aAppendToMe, const PROPERTIES* aProperties ) |
|||
{ |
|||
m_props = aProperties; |
|||
|
|||
m_board = aAppendToMe ? aAppendToMe : new BOARD(); |
|||
|
|||
// Give the filename to the board if it's new
|
|||
if( !aAppendToMe ) |
|||
m_board->SetFileName( aFileName ); |
|||
|
|||
// clang-format off
|
|||
const std::map<ALTIUM_PCB_DIR, std::string> mapping = { |
|||
{ ALTIUM_PCB_DIR::FILE_HEADER, "FileHeader" }, |
|||
{ ALTIUM_PCB_DIR::ARCS6, "Arcs6\\Data" }, |
|||
{ ALTIUM_PCB_DIR::BOARD6, "Board6\\Data" }, |
|||
{ ALTIUM_PCB_DIR::BOARDREGIONS, "BoardRegions\\Data" }, |
|||
{ ALTIUM_PCB_DIR::CLASSES6, "Classes6\\Data" }, |
|||
{ ALTIUM_PCB_DIR::COMPONENTS6, "Components6\\Data" }, |
|||
{ ALTIUM_PCB_DIR::DIMENSIONS6, "Dimensions6\\Data" }, |
|||
{ ALTIUM_PCB_DIR::FILLS6, "Fills6\\Data" }, |
|||
{ ALTIUM_PCB_DIR::NETS6, "Nets6\\Data" }, |
|||
{ ALTIUM_PCB_DIR::PADS6, "Pads6\\Data" }, |
|||
{ ALTIUM_PCB_DIR::POLYGONS6, "Polygons6\\Data" }, |
|||
{ ALTIUM_PCB_DIR::REGIONS6, "Regions6\\Data" }, |
|||
{ ALTIUM_PCB_DIR::RULES6, "Rules6\\Data" }, |
|||
{ ALTIUM_PCB_DIR::SHAPEBASEDREGIONS6, "ShapeBasedRegions6\\Data" }, |
|||
{ ALTIUM_PCB_DIR::TEXTS6, "Texts6\\Data" }, |
|||
{ ALTIUM_PCB_DIR::TRACKS6, "Tracks6\\Data" }, |
|||
{ ALTIUM_PCB_DIR::VIAS6, "Vias6\\Data" } |
|||
}; |
|||
// clang-format on
|
|||
|
|||
ParseAltiumPcb( m_board, aFileName, mapping ); |
|||
|
|||
return m_board; |
|||
} |
@ -0,0 +1,63 @@ |
|||
/* |
|||
* This program source code file is part of KiCad, a free EDA CAD application. |
|||
* |
|||
* Copyright (C) 2019 Thomas Pointhuber <thomas.pointhuber@gmx.at> |
|||
* |
|||
* 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 |
|||
*/ |
|||
|
|||
/** |
|||
* @file pcad_plugin.h |
|||
* @brief Pcbnew PLUGIN for Altium *.PcbDoc format. |
|||
*/ |
|||
|
|||
#ifndef ALTIUM_DESIGNER_PLUGIN_H_ |
|||
#define ALTIUM_DESIGNER_PLUGIN_H_ |
|||
|
|||
|
|||
#include <io_mgr.h> |
|||
|
|||
class ALTIUM_DESIGNER_PLUGIN : public PLUGIN |
|||
{ |
|||
public: |
|||
// -----<PUBLIC PLUGIN API>-------------------------------------------------- |
|||
|
|||
const wxString PluginName() const override; |
|||
|
|||
BOARD* Load( |
|||
const wxString& aFileName, BOARD* aAppendToMe, const PROPERTIES* aProperties ) override; |
|||
|
|||
const wxString GetFileExtension() const override; |
|||
|
|||
long long GetLibraryTimestamp( const wxString& aLibraryPath ) const override |
|||
{ |
|||
// TODO? |
|||
return 0; |
|||
} |
|||
|
|||
// -----</PUBLIC PLUGIN API>------------------------------------------------- |
|||
|
|||
ALTIUM_DESIGNER_PLUGIN(); |
|||
~ALTIUM_DESIGNER_PLUGIN(); |
|||
|
|||
private: |
|||
const PROPERTIES* m_props; |
|||
BOARD* m_board; |
|||
}; |
|||
|
|||
#endif // ALTIUM_DESIGNER_PLUGIN_H_ |
@ -0,0 +1,234 @@ |
|||
/*
|
|||
* This program source code file is part of KiCad, a free EDA CAD application. |
|||
* |
|||
* Copyright (C) 2019-2020 Thomas Pointhuber <thomas.pointhuber@gmx.at> |
|||
* |
|||
* 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 "altium_parser.h"
|
|||
|
|||
#include <compoundfilereader.h>
|
|||
#include <ki_exception.h>
|
|||
#include <sstream>
|
|||
#include <utf.h>
|
|||
#include <wx/translation.h>
|
|||
#include <wx/wx.h>
|
|||
|
|||
|
|||
const CFB::COMPOUND_FILE_ENTRY* FindStream( |
|||
const CFB::CompoundFileReader& aReader, const char* aStreamName ) |
|||
{ |
|||
const CFB::COMPOUND_FILE_ENTRY* ret = nullptr; |
|||
aReader.EnumFiles( aReader.GetRootEntry(), -1, |
|||
[&]( const CFB::COMPOUND_FILE_ENTRY* aEntry, const CFB::utf16string& aU16dir, |
|||
int level ) -> void { |
|||
if( aReader.IsStream( aEntry ) ) |
|||
{ |
|||
std::string name = UTF16ToUTF8( aEntry->name ); |
|||
if( aU16dir.length() > 0 ) |
|||
{ |
|||
std::string dir = UTF16ToUTF8( aU16dir.c_str() ); |
|||
if( strncmp( aStreamName, dir.c_str(), dir.length() ) == 0 |
|||
&& aStreamName[dir.length()] == '\\' |
|||
&& strcmp( aStreamName + dir.length() + 1, name.c_str() ) == 0 ) |
|||
{ |
|||
ret = aEntry; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
if( strcmp( aStreamName, name.c_str() ) == 0 ) |
|||
{ |
|||
ret = aEntry; |
|||
} |
|||
} |
|||
} |
|||
} ); |
|||
return ret; |
|||
} |
|||
|
|||
|
|||
ALTIUM_PARSER::ALTIUM_PARSER( |
|||
const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry ) |
|||
{ |
|||
m_subrecord_end = nullptr; |
|||
if( aEntry->size > std::numeric_limits<size_t>::max() ) |
|||
{ |
|||
THROW_IO_ERROR( _( "stream too large" ) ); |
|||
} |
|||
|
|||
m_size = static_cast<size_t>( aEntry->size ); |
|||
m_error = false; |
|||
m_content.reset( new char[m_size] ); |
|||
m_pos = m_content.get(); |
|||
|
|||
// read file into buffer
|
|||
aReader.ReadFile( aEntry, 0, m_content.get(), m_size ); |
|||
} |
|||
|
|||
|
|||
std::map<wxString, wxString> ALTIUM_PARSER::ReadProperties() |
|||
{ |
|||
std::map<wxString, wxString> kv; |
|||
|
|||
uint32_t length = Read<uint32_t>(); |
|||
if( length > GetRemainingBytes() || m_pos[length - 1] != '\0' ) |
|||
{ |
|||
m_error = true; |
|||
return kv; |
|||
} |
|||
|
|||
//we use std::string because std::string can handle NULL-bytes
|
|||
//wxString would end the string at the first NULL-byte
|
|||
std::string str = std::string( m_pos, length - 1 ); |
|||
m_pos += length; |
|||
|
|||
std::size_t token_end = 0; |
|||
while( token_end < str.size() && token_end != std::string::npos ) |
|||
{ |
|||
std::size_t token_start = str.find( '|', token_end ); |
|||
std::size_t token_equal = str.find( '=', token_start ); |
|||
token_end = str.find( '|', token_equal ); |
|||
|
|||
std::string keyS = str.substr( token_start + 1, token_equal - token_start - 1 ); |
|||
std::string valueS = str.substr( token_equal + 1, token_end - token_equal - 1 ); |
|||
|
|||
//convert the strings to wxStrings, since we use them everywhere
|
|||
//value can have non-ASCII characters, so we convert them from LATIN1/ISO8859-1
|
|||
wxString key( keyS.c_str(), wxConvISO8859_1 ); |
|||
wxString value( valueS.c_str(), wxConvISO8859_1 ); |
|||
|
|||
kv.insert( { key, value } ); |
|||
} |
|||
|
|||
return kv; |
|||
} |
|||
|
|||
int ALTIUM_PARSER::PropertiesReadInt( |
|||
const std::map<wxString, wxString>& aProperties, const wxString& aKey, int aDefault ) |
|||
{ |
|||
try |
|||
{ |
|||
const wxString& value = aProperties.at( aKey ); |
|||
|
|||
return wxAtoi( value ); |
|||
} |
|||
catch( const std::out_of_range& oor ) |
|||
{ |
|||
return aDefault; |
|||
} |
|||
} |
|||
|
|||
double ALTIUM_PARSER::PropertiesReadDouble( |
|||
const std::map<wxString, wxString>& aProperties, const wxString& aKey, double aDefault ) |
|||
{ |
|||
try |
|||
{ |
|||
const wxString& value = aProperties.at( aKey ); |
|||
|
|||
// Locale independent str -> double conversation
|
|||
std::istringstream istr( (const char*) value.mb_str() ); |
|||
istr.imbue( std::locale( "C" ) ); |
|||
|
|||
double doubleValue; |
|||
istr >> doubleValue; |
|||
return doubleValue; |
|||
} |
|||
catch( const std::out_of_range& oor ) |
|||
{ |
|||
return aDefault; |
|||
} |
|||
} |
|||
|
|||
bool ALTIUM_PARSER::PropertiesReadBool( |
|||
const std::map<wxString, wxString>& aProperties, const wxString& aKey, bool aDefault ) |
|||
{ |
|||
try |
|||
{ |
|||
const wxString& value = aProperties.at( aKey ); |
|||
|
|||
return value == "TRUE"; |
|||
} |
|||
catch( const std::out_of_range& oor ) |
|||
{ |
|||
return aDefault; |
|||
} |
|||
} |
|||
|
|||
int32_t ALTIUM_PARSER::PropertiesReadKicadUnit( const std::map<wxString, wxString>& aProperties, |
|||
const wxString& aKey, const wxString& aDefault ) |
|||
{ |
|||
const wxString& value = PropertiesReadString( aProperties, aKey, aDefault ); |
|||
|
|||
size_t decimal_point = value.find( '.' ); |
|||
size_t value_end = value.find_first_not_of( "+-0123456789." ); |
|||
|
|||
wxString before_decimal_str = value.Left( decimal_point ); |
|||
int before_decimal = wxAtoi( before_decimal_str ); |
|||
int after_decimal = 0; |
|||
size_t after_decimal_digits = 0; |
|||
if( decimal_point != wxString::npos ) |
|||
{ |
|||
if( value_end != wxString::npos ) |
|||
{ |
|||
after_decimal_digits = value_end - ( decimal_point + 1 ); |
|||
} |
|||
else |
|||
{ |
|||
after_decimal_digits = value.size() - ( decimal_point + 1 ); // TODO: correct?
|
|||
} |
|||
wxString after_decimal_str = value.Mid( decimal_point + 1, after_decimal_digits ); |
|||
after_decimal = wxAtoi( after_decimal_str ); |
|||
} |
|||
|
|||
if( value.length() > 3 && value.compare( value.length() - 3, 3, "mil" ) == 0 ) |
|||
{ |
|||
// ensure after_decimal is formatted to base 1000
|
|||
int after_decimal_1000; |
|||
if( after_decimal_digits <= 4 ) |
|||
{ |
|||
after_decimal_1000 = |
|||
static_cast<int>( after_decimal * std::pow( 10, 4 - after_decimal_digits ) ); |
|||
} |
|||
else |
|||
{ |
|||
after_decimal_1000 = |
|||
static_cast<int>( after_decimal / std::pow( 10, after_decimal_digits - 4 ) ); |
|||
} |
|||
|
|||
int32_t mils = before_decimal * 10000 + after_decimal_1000; |
|||
return ConvertToKicadUnit( mils ); |
|||
} |
|||
|
|||
wxLogError( wxString::Format( _( "Unit '%s' does not end with mils" ) ), value ); |
|||
return 0; |
|||
} |
|||
|
|||
wxString ALTIUM_PARSER::PropertiesReadString( const std::map<wxString, wxString>& aProperties, |
|||
const wxString& aKey, const wxString& aDefault ) |
|||
{ |
|||
try |
|||
{ |
|||
return aProperties.at( aKey ); |
|||
} |
|||
catch( const std::out_of_range& oor ) |
|||
{ |
|||
return aDefault; |
|||
} |
|||
} |
@ -0,0 +1,199 @@ |
|||
/* |
|||
* This program source code file is part of KiCad, a free EDA CAD application. |
|||
* |
|||
* Copyright (C) 2019-2020 Thomas Pointhuber <thomas.pointhuber@gmx.at> |
|||
* |
|||
* 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 |
|||
*/ |
|||
|
|||
#ifndef ALTIUM_PARSER_H |
|||
#define ALTIUM_PARSER_H |
|||
|
|||
#include <map> |
|||
#include <memory> |
|||
|
|||
#include <math/util.h> |
|||
#include <wx/gdicmn.h> |
|||
|
|||
|
|||
namespace CFB |
|||
{ |
|||
class CompoundFileReader; |
|||
struct COMPOUND_FILE_ENTRY; |
|||
} // namespace CFB |
|||
|
|||
// Helper method to find file inside compound file |
|||
const CFB::COMPOUND_FILE_ENTRY* FindStream( |
|||
const CFB::CompoundFileReader& aReader, const char* aStreamName ); |
|||
|
|||
|
|||
class ALTIUM_PARSER |
|||
{ |
|||
public: |
|||
ALTIUM_PARSER( const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry ); |
|||
~ALTIUM_PARSER() = default; |
|||
|
|||
template <typename Type> |
|||
Type Read() |
|||
{ |
|||
if( GetRemainingBytes() >= sizeof( Type ) ) |
|||
{ |
|||
Type val = *(Type*) ( m_pos ); |
|||
m_pos += sizeof( Type ); |
|||
return val; |
|||
} |
|||
else |
|||
{ |
|||
m_error = true; |
|||
return 0; |
|||
} |
|||
} |
|||
|
|||
wxString ReadWxString() |
|||
{ |
|||
uint8_t len = Read<uint8_t>(); |
|||
if( GetRemainingBytes() >= len ) |
|||
{ |
|||
|
|||
//altium uses LATIN1/ISO 8859-1, convert it |
|||
wxString val = wxString( m_pos, wxConvISO8859_1, len ); |
|||
m_pos += len; |
|||
return val; |
|||
} |
|||
else |
|||
{ |
|||
m_error = true; |
|||
return wxString( "" ); |
|||
} |
|||
} |
|||
|
|||
int32_t ReadKicadUnit() |
|||
{ |
|||
return ConvertToKicadUnit( Read<int32_t>() ); |
|||
} |
|||
|
|||
int32_t ReadKicadUnitX() |
|||
{ |
|||
return ReadKicadUnit(); |
|||
} |
|||
|
|||
int32_t ReadKicadUnitY() |
|||
{ |
|||
return -ReadKicadUnit(); |
|||
} |
|||
|
|||
wxPoint ReadWxPoint() |
|||
{ |
|||
int32_t x = ReadKicadUnitX(); |
|||
int32_t y = ReadKicadUnitY(); |
|||
return { x, y }; |
|||
} |
|||
|
|||
wxSize ReadWxSize() |
|||
{ |
|||
int32_t x = ReadKicadUnit(); |
|||
int32_t y = ReadKicadUnit(); |
|||
return { x, y }; |
|||
} |
|||
|
|||
size_t ReadAndSetSubrecordLength() |
|||
{ |
|||
uint32_t length = Read<uint32_t>(); |
|||
m_subrecord_end = m_pos + length; |
|||
return length; |
|||
} |
|||
|
|||
std::map<wxString, wxString> ReadProperties(); |
|||
|
|||
static int32_t ConvertToKicadUnit( const int32_t aValue ) |
|||
{ |
|||
return ( ( (int64_t) aValue ) * 254L ) / 100; |
|||
} |
|||
|
|||
static int32_t ConvertToKicadUnit( const double aValue ) |
|||
{ |
|||
return KiROUND( aValue * 2.54L ); |
|||
} |
|||
|
|||
static int PropertiesReadInt( |
|||
const std::map<wxString, wxString>& aProperties, const wxString& aKey, int aDefault ); |
|||
|
|||
static double PropertiesReadDouble( const std::map<wxString, wxString>& aProperties, |
|||
const wxString& aKey, double aDefault ); |
|||
|
|||
static bool PropertiesReadBool( |
|||
const std::map<wxString, wxString>& aProperties, const wxString& aKey, bool aDefault ); |
|||
|
|||
static int32_t PropertiesReadKicadUnit( const std::map<wxString, wxString>& aProperties, |
|||
const wxString& aKey, const wxString& aDefault ); |
|||
|
|||
static wxString PropertiesReadString( const std::map<wxString, wxString>& aProperties, |
|||
const wxString& aKey, const wxString& aDefault ); |
|||
|
|||
void Skip( size_t aLength ) |
|||
{ |
|||
if( GetRemainingBytes() >= aLength ) |
|||
{ |
|||
m_pos += aLength; |
|||
} |
|||
else |
|||
{ |
|||
m_error = true; |
|||
} |
|||
} |
|||
|
|||
void SkipSubrecord() |
|||
{ |
|||
if( m_subrecord_end == nullptr || m_subrecord_end < m_pos ) |
|||
{ |
|||
m_error = true; |
|||
} |
|||
else |
|||
{ |
|||
m_pos = m_subrecord_end; |
|||
} |
|||
}; |
|||
|
|||
size_t GetRemainingBytes() const |
|||
{ |
|||
return m_pos == nullptr ? 0 : m_size - ( m_pos - m_content.get() ); |
|||
} |
|||
|
|||
size_t GetRemainingSubrecordBytes() const |
|||
{ |
|||
return m_pos == nullptr || m_subrecord_end == nullptr || m_subrecord_end <= m_pos ? |
|||
0 : |
|||
m_subrecord_end - m_pos; |
|||
}; |
|||
|
|||
bool HasParsingError() |
|||
{ |
|||
return m_error; |
|||
} |
|||
|
|||
private: |
|||
std::unique_ptr<char[]> m_content; |
|||
size_t m_size; |
|||
|
|||
char* m_pos; // current read pointer |
|||
char* m_subrecord_end; // pointer which points to next subrecord start |
|||
bool m_error; |
|||
}; |
|||
|
|||
|
|||
#endif //ALTIUM_PARSER_H |
@ -0,0 +1,658 @@ |
|||
# Can be viewed in: https://ide.kaitai.io/ |
|||
# |
|||
# This file is a formal specification of the binary format used in Altium. |
|||
# Files need to manually extracted using a program which can read the Microsoft Compound File Format. |
|||
# |
|||
# While I do not create a parser using this file, it is still very helpful to understand the binary |
|||
# format. |
|||
|
|||
meta: |
|||
id: altium_binary |
|||
endian: le |
|||
encoding: ISO8859-1 |
|||
|
|||
seq: |
|||
- id: record |
|||
type: record |
|||
repeat: eos |
|||
|
|||
# https://github.com/thesourcerer8/altium2kicad/blob/master/convertpcb.pl#L1291 |
|||
types: |
|||
record: |
|||
seq: |
|||
- id: recordtype |
|||
type: u1 |
|||
enum: record_id |
|||
- id: record |
|||
type: |
|||
switch-on: recordtype |
|||
cases: |
|||
record_id::arc6: arc |
|||
record_id::pad6: pad |
|||
record_id::via6: via |
|||
record_id::track6: track |
|||
record_id::text6: text |
|||
record_id::fill6: fill |
|||
record_id::region6: region |
|||
|
|||
arc: |
|||
seq: |
|||
- id: sub1_len |
|||
type: u4 |
|||
- id: data |
|||
type: arc_sub1 |
|||
size: sub1_len |
|||
|
|||
arc_sub1: |
|||
seq: |
|||
- id: layer |
|||
type: u1 |
|||
- #id: flags_u7 |
|||
type: b1 |
|||
- #id: flags_u6 |
|||
type: b1 |
|||
- #id: flags_u5 |
|||
type: b1 |
|||
- #id: flags_u4 |
|||
type: b1 |
|||
- #id: flags_u3 |
|||
type: b1 |
|||
- id: is_not_locked |
|||
type: b1 |
|||
- id: is_not_polygonoutline |
|||
type: b1 |
|||
- #id: flags_u0 |
|||
type: b1 |
|||
- id: is_keepout |
|||
type: u1 # KEEPOUT = 2 |
|||
- id: net |
|||
type: u2 |
|||
- id: subpolyindex |
|||
type: u2 |
|||
- id: component |
|||
type: u2 |
|||
- size: 4 |
|||
- id: center |
|||
type: xy |
|||
- id: radius |
|||
type: u4 |
|||
- id: start_angle |
|||
type: f8 |
|||
- id: end_angle |
|||
type: f8 |
|||
- id: width |
|||
type: u4 |
|||
|
|||
pad: |
|||
seq: |
|||
- id: sub1_len |
|||
type: u4 |
|||
- id: designator |
|||
type: pad_sub1 |
|||
size: sub1_len |
|||
- id: sub2_len |
|||
type: u4 |
|||
- size: sub2_len |
|||
- id: sub3_len |
|||
type: u4 |
|||
- size: sub3_len |
|||
- id: sub4_len |
|||
type: u4 |
|||
- size: sub4_len |
|||
- id: sub5_len |
|||
type: u4 |
|||
- id: size_and_shape |
|||
type: pad_sub5 |
|||
size: sub5_len |
|||
- id: sub6_len |
|||
type: u4 |
|||
- id: size_and_shape_by_layer |
|||
type: pad_sub6 |
|||
size: sub6_len |
|||
if: sub6_len > 0 |
|||
|
|||
pad_sub1: |
|||
seq: |
|||
- id: name_len # = len-1? |
|||
type: u1 |
|||
- id: name |
|||
type: str |
|||
size: name_len |
|||
|
|||
pad_sub5: |
|||
seq: |
|||
- id: layer # $pos+23 |
|||
type: u1 |
|||
enum: layer |
|||
- id: test_fab_top |
|||
type: b1 |
|||
- id: tent_bottom |
|||
type: b1 |
|||
- id: tent_top |
|||
type: b1 |
|||
- #id: flags_u4 |
|||
type: b1 |
|||
- #id: flags_u3 |
|||
type: b1 |
|||
- id: is_not_locked |
|||
type: b1 |
|||
- #id: flags_u1 |
|||
type: b1 |
|||
- #id: flags_u0 |
|||
type: b1 |
|||
- #id: flags2_u7 |
|||
type: b1 |
|||
- #id: flags2_u6 |
|||
type: b1 |
|||
- #id: flags2_u5 |
|||
type: b1 |
|||
- #id: flags2_u4 |
|||
type: b1 |
|||
- #id: flags2_u3 |
|||
type: b1 |
|||
- #id: flags2_u2 |
|||
type: b1 |
|||
- #id: flags2_u1 |
|||
type: b1 |
|||
- id: test_fab_bottom |
|||
type: b1 |
|||
#- id: u |
|||
# size: 1 |
|||
- id: net # $pos+26 |
|||
type: u2 |
|||
- size: 2 |
|||
- id: component # $pos+30 |
|||
type: u2 |
|||
- size: 4 |
|||
- id: position # $pos+36, $pos+40 |
|||
type: xy |
|||
- id: topsize # $pos+44, $pos+48 |
|||
type: xy |
|||
- id: midsize # $pos+52, $pos+56 |
|||
type: xy |
|||
- id: botsize # $pos+60, $pos+64 |
|||
type: xy |
|||
- id: holesize # $pos+68 |
|||
type: u4 |
|||
- id: topshape # $pos+72 |
|||
type: u1 |
|||
enum: pad_shape |
|||
- id: midshape # $pos+73 |
|||
type: u1 |
|||
enum: pad_shape |
|||
- id: botshape # $pos+74 |
|||
type: u1 |
|||
enum: pad_shape |
|||
- id: direction # $pos+75 |
|||
type: f8 |
|||
- id: plated # $pos+83 |
|||
type: u1 |
|||
enum: boolean |
|||
- size: 1 |
|||
- id: pad_mode # $pos+85 |
|||
type: u1 |
|||
enum: pad_mode |
|||
- size: 5 |
|||
- id: ccw # $pos+91 |
|||
type: u4 |
|||
- id: cen # $pos+95 |
|||
type: u1 |
|||
- size: 1 |
|||
- id: cag # $pos+97 |
|||
type: u4 |
|||
- id: cpr # $pos+101 |
|||
type: u4 |
|||
- id: cpc # $pos+105 |
|||
type: u4 |
|||
- id: pastemaskexpanionmanual |
|||
type: s4 |
|||
- id: soldermaskexpansionmanual # $pos+113 |
|||
type: s4 |
|||
- id: cpl # $pos+117 |
|||
type: u1 |
|||
- size: 6 |
|||
- id: pastemaskexpansionmode # $pos+124 |
|||
type: u1 |
|||
enum: pad_mode_rule |
|||
- id: soldermaskexpansionmode # $pos+125 |
|||
type: u1 |
|||
enum: pad_mode_rule |
|||
- size: 3 |
|||
- id: holerotation # $pos+129 |
|||
type: f8 |
|||
- size: 4 |
|||
- id: testpoint_assembly_top |
|||
type: u1 |
|||
enum: boolean |
|||
- id: testpoint_assembly_bottom |
|||
type: u1 |
|||
enum: boolean |
|||
|
|||
pad_sub6: |
|||
seq: |
|||
- id: x |
|||
type: s4 |
|||
repeat: expr |
|||
repeat-expr: 29 |
|||
- id: y |
|||
type: s4 |
|||
repeat: expr |
|||
repeat-expr: 29 |
|||
- id: shape |
|||
type: u1 |
|||
enum: pad_shape |
|||
repeat: expr |
|||
repeat-expr: 29 |
|||
- size: 1 |
|||
- id: hole_type |
|||
type: u1 |
|||
enum: pad_hole_type |
|||
- id: slot_length |
|||
type: s4 |
|||
- id: slot_rotation |
|||
type: f8 |
|||
- id: holeoffset_x |
|||
type: s4 |
|||
repeat: expr |
|||
repeat-expr: 32 |
|||
- id: holeoffset_y |
|||
type: s4 |
|||
repeat: expr |
|||
repeat-expr: 32 |
|||
- size: 1 |
|||
- id: shape_alt |
|||
type: u1 |
|||
enum: pad_shape_alt |
|||
repeat: expr |
|||
repeat-expr: 32 |
|||
- id: corner_radius |
|||
type: u1 |
|||
repeat: expr |
|||
repeat-expr: 32 |
|||
- size: 32 |
|||
|
|||
via: |
|||
seq: |
|||
- id: sub1_len |
|||
type: u4 |
|||
- id: data |
|||
type: via_sub1 |
|||
size: sub1_len |
|||
|
|||
via_sub1: |
|||
seq: |
|||
- size: 1 |
|||
- id: test_fab_top |
|||
type: b1 |
|||
- id: tent_bottom |
|||
type: b1 |
|||
- id: tent_top |
|||
type: b1 |
|||
- #id: flags_u4 |
|||
type: b1 |
|||
- #id: flags_u3 |
|||
type: b1 |
|||
- id: is_not_locked |
|||
type: b1 |
|||
- #id: flags_u1 |
|||
type: b1 |
|||
- #id: flags_u0 |
|||
type: b1 |
|||
- #id: flags2_u7 |
|||
type: b1 |
|||
- #id: flags2_u6 |
|||
type: b1 |
|||
- #id: flags2_u5 |
|||
type: b1 |
|||
- #id: flags2_u4 |
|||
type: b1 |
|||
- #id: flags2_u3 |
|||
type: b1 |
|||
- #id: flags2_u2 |
|||
type: b1 |
|||
- #id: flags2_u1 |
|||
type: b1 |
|||
- id: test_fab_bottom |
|||
type: b1 |
|||
- id: net |
|||
type: u2 |
|||
- size: 2 |
|||
- id: component |
|||
type: u2 |
|||
- size: 4 |
|||
- id: pos # 13 |
|||
type: xy |
|||
- id: diameter # 21 |
|||
type: s4 |
|||
- id: holesize # 29 |
|||
type: s4 |
|||
- id: start_layer |
|||
type: u1 |
|||
enum: layer |
|||
- id: end_layer |
|||
type: u1 |
|||
enum: layer |
|||
- size: 43 |
|||
- id: via_mode |
|||
type: u1 |
|||
enum: pad_mode |
|||
- id: diameter_alt |
|||
type: s4 |
|||
repeat: expr |
|||
repeat-expr: 32 |
|||
|
|||
track: |
|||
seq: |
|||
- id: sub1_len |
|||
type: u4 |
|||
- id: data |
|||
type: track_sub1 |
|||
size: sub1_len |
|||
|
|||
track_sub1: |
|||
seq: |
|||
- id: layer |
|||
type: u1 |
|||
enum: layer |
|||
- #id: flags_u7 |
|||
type: b1 |
|||
- #id: flags_u6 |
|||
type: b1 |
|||
- #id: flags_u5 |
|||
type: b1 |
|||
- #id: flags_u4 |
|||
type: b1 |
|||
- #id: flags_u3 |
|||
type: b1 |
|||
- id: is_not_locked |
|||
type: b1 |
|||
- id: is_not_polygonoutline |
|||
type: b1 |
|||
- #id: flags_u0 |
|||
type: b1 |
|||
- id: is_keepout |
|||
type: u1 # KEEPOUT = 2 |
|||
- id: net |
|||
type: u2 |
|||
- id: subpolyindex |
|||
type: u2 |
|||
- id: component |
|||
type: u2 |
|||
- size: 4 |
|||
- id: start # 13 |
|||
type: xy |
|||
- id: end # 21 |
|||
type: xy |
|||
- id: width # 29 |
|||
type: s4 |
|||
|
|||
text: |
|||
seq: |
|||
- id: sub1_len |
|||
type: u4 |
|||
- id: properties |
|||
type: text_sub1 |
|||
size: sub1_len |
|||
- id: sub2_len |
|||
type: u4 |
|||
- id: text |
|||
type: text_sub2 |
|||
size: sub2_len |
|||
|
|||
text_sub1: |
|||
seq: |
|||
- id: layer |
|||
type: u1 |
|||
- #id: flags_u7 |
|||
type: b1 |
|||
- #id: flags_u6 |
|||
type: b1 |
|||
- #id: flags_u5 |
|||
type: b1 |
|||
- #id: flags_u4 |
|||
type: b1 |
|||
- #id: flags_u3 |
|||
type: b1 |
|||
- id: is_not_locked |
|||
type: b1 |
|||
- #id: flags_u1 |
|||
type: b1 |
|||
- #id: flags_u0 |
|||
type: b1 |
|||
- size: 1 |
|||
- id: net |
|||
type: u2 |
|||
- size: 2 |
|||
- id: component |
|||
type: u2 |
|||
- size: 4 |
|||
- id: pos |
|||
type: xy |
|||
- id: height |
|||
type: u4 |
|||
- id: font_name_id |
|||
type: u1 |
|||
- size: 1 |
|||
- id: rotation |
|||
type: f8 |
|||
- id: mirrored |
|||
type: u1 |
|||
enum: boolean |
|||
- id: strokewidth |
|||
type: u4 |
|||
- id: is_comment |
|||
type: u1 |
|||
enum: boolean |
|||
- id: is_designator |
|||
type: u1 |
|||
enum: boolean |
|||
- size: 4 |
|||
- id: font_name |
|||
size: 64 |
|||
type: str # TODO: terminates with [0, 0] |
|||
encoding: UTF-16 |
|||
- size: 22 |
|||
- id: position |
|||
type: u1 |
|||
enum: text_position |
|||
- size: 27 |
|||
- id: truetype |
|||
type: u1 |
|||
enum: boolean |
|||
- id: barcode_name |
|||
size: 64 |
|||
type: str # TODO: terminates with [0, 0] |
|||
encoding: UTF-16 |
|||
|
|||
text_sub2: |
|||
seq: |
|||
- id: len |
|||
type: u1 |
|||
- id: name |
|||
type: str |
|||
size: len |
|||
|
|||
fill: |
|||
seq: |
|||
- id: sub1_len |
|||
type: u4 |
|||
- id: data |
|||
type: fill_sub1 |
|||
size: sub1_len |
|||
|
|||
fill_sub1: |
|||
seq: |
|||
- id: layer |
|||
type: u1 |
|||
- #id: flags_u7 |
|||
type: b1 |
|||
- #id: flags_u6 |
|||
type: b1 |
|||
- #id: flags_u5 |
|||
type: b1 |
|||
- #id: flags_u4 |
|||
type: b1 |
|||
- #id: flags_u3 |
|||
type: b1 |
|||
- id: is_not_locked |
|||
type: b1 |
|||
- #id: flags_u1 |
|||
type: b1 |
|||
- #id: flags_u0 |
|||
type: b1 |
|||
- id: is_keepout |
|||
type: u1 # KEEPOUT = 2 |
|||
- id: net |
|||
type: u2 |
|||
- size: 2 |
|||
- id: component |
|||
type: u2 |
|||
- size: 4 |
|||
- id: pos1 |
|||
type: xy |
|||
- id: pos2 |
|||
type: xy |
|||
- id: rotation |
|||
type: f8 |
|||
|
|||
region: |
|||
seq: |
|||
- id: sub1_len |
|||
type: u4 |
|||
- id: data |
|||
type: region_sub1 |
|||
size: sub1_len |
|||
|
|||
region_sub1: |
|||
seq: |
|||
- id: layer |
|||
type: u1 |
|||
- #id: flags_u7 |
|||
type: b1 |
|||
- #id: flags_u6 |
|||
type: b1 |
|||
- #id: flags_u5 |
|||
type: b1 |
|||
- #id: flags_u4 |
|||
type: b1 |
|||
- #id: flags_u3 |
|||
type: b1 |
|||
- id: is_not_locked |
|||
type: b1 |
|||
- #id: flags_u1 |
|||
type: b1 |
|||
- #id: flags_u0 |
|||
type: b1 |
|||
- id: is_keepout |
|||
type: u1 # KEEPOUT = 2 |
|||
- id: net |
|||
type: u2 |
|||
- size: 2 |
|||
- id: component |
|||
type: u2 |
|||
- size: 5 |
|||
- id: holecount # TODO: check |
|||
type: u2 |
|||
- size: 2 |
|||
- id: propterties_len |
|||
type: u4 |
|||
- id: properties |
|||
size: propterties_len |
|||
type: str |
|||
- id: vertices_num |
|||
type: u4 |
|||
- id: vertices # region1 type |
|||
repeat: expr |
|||
repeat-expr: vertices_num |
|||
type: xyf |
|||
#- id: vertices2 # region2 type |
|||
# repeat: expr |
|||
# repeat-expr: vertices_num+1 |
|||
# type: xyf2 |
|||
|
|||
xy: |
|||
seq: |
|||
- id: x |
|||
type: s4 |
|||
- id: y |
|||
type: s4 |
|||
|
|||
xyf: # no idea why two different formats? |
|||
seq: |
|||
- id: x |
|||
type: f8 |
|||
- id: y |
|||
type: f8 |
|||
|
|||
xyf2: # no idea why two different formats? |
|||
seq: |
|||
- id: is_round |
|||
type: u1 |
|||
enum: boolean |
|||
- id: position |
|||
type: xy |
|||
- id: center |
|||
type: xy |
|||
- id: radius |
|||
type: u4 |
|||
- id: angle1 |
|||
type: f8 |
|||
- id: angle2 |
|||
type: f8 |
|||
|
|||
enums: |
|||
record_id: |
|||
0x01: arc6 |
|||
0x02: pad6 |
|||
0x03: via6 |
|||
0x04: track6 |
|||
0x05: text6 |
|||
0x06: fill6 |
|||
0x0b: region6 |
|||
|
|||
boolean: |
|||
0: false |
|||
1: true |
|||
|
|||
pad_shape: |
|||
0: unknown |
|||
1: circle |
|||
2: rect |
|||
3: octagonal |
|||
|
|||
pad_shape_alt: |
|||
0: unknown |
|||
1: round |
|||
2: rect |
|||
3: octagonal |
|||
9: roundrectangle |
|||
|
|||
pad_hole_type: |
|||
0: normal |
|||
1: square |
|||
2: slot |
|||
|
|||
pad_mode: |
|||
0: simple |
|||
1: top_middle_bottom |
|||
2: full_stack |
|||
|
|||
pad_mode_rule: |
|||
0: unknown |
|||
1: rule |
|||
2: manual |
|||
|
|||
text_position: |
|||
1: left_top |
|||
2: left_center |
|||
3: left_bottom |
|||
4: center_top |
|||
5: center_center |
|||
6: center_bottom |
|||
7: right_top |
|||
8: right_center |
|||
9: right_bottom |
|||
|
|||
layer: |
|||
1: f_cu |
|||
32: b_cu |
@ -0,0 +1,941 @@ |
|||
/*
|
|||
* This program source code file is part of KiCad, a free EDA CAD application. |
|||
* |
|||
* Copyright (C) 2020 Thomas Pointhuber <thomas.pointhuber@gmx.at> |
|||
* |
|||
* 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 <map>
|
|||
#include <unordered_map>
|
|||
|
|||
#include <ki_exception.h>
|
|||
#include <math/util.h>
|
|||
#include <wx/translation.h>
|
|||
|
|||
#include "altium_parser.h"
|
|||
#include "altium_parser_pcb.h"
|
|||
|
|||
|
|||
ALTIUM_LAYER altium_layer_from_name( const wxString& aName ) |
|||
{ |
|||
static const std::unordered_map<std::string, ALTIUM_LAYER> hash_map = { |
|||
{ "TOP", ALTIUM_LAYER::TOP_LAYER }, |
|||
{ "MID1", ALTIUM_LAYER::MID_LAYER_1 }, |
|||
{ "MID2", ALTIUM_LAYER::MID_LAYER_2 }, |
|||
{ "MID3", ALTIUM_LAYER::MID_LAYER_3 }, |
|||
{ "MID4", ALTIUM_LAYER::MID_LAYER_4 }, |
|||
{ "MID5", ALTIUM_LAYER::MID_LAYER_5 }, |
|||
{ "MID6", ALTIUM_LAYER::MID_LAYER_6 }, |
|||
{ "MID7", ALTIUM_LAYER::MID_LAYER_7 }, |
|||
{ "MID8", ALTIUM_LAYER::MID_LAYER_8 }, |
|||
{ "MID9", ALTIUM_LAYER::MID_LAYER_9 }, |
|||
{ "MID10", ALTIUM_LAYER::MID_LAYER_10 }, |
|||
{ "MID11", ALTIUM_LAYER::MID_LAYER_11 }, |
|||
{ "MID12", ALTIUM_LAYER::MID_LAYER_12 }, |
|||
{ "MID13", ALTIUM_LAYER::MID_LAYER_13 }, |
|||
{ "MID14", ALTIUM_LAYER::MID_LAYER_14 }, |
|||
{ "MID15", ALTIUM_LAYER::MID_LAYER_15 }, |
|||
{ "MID16", ALTIUM_LAYER::MID_LAYER_16 }, |
|||
{ "MID17", ALTIUM_LAYER::MID_LAYER_17 }, |
|||
{ "MID18", ALTIUM_LAYER::MID_LAYER_18 }, |
|||
{ "MID19", ALTIUM_LAYER::MID_LAYER_19 }, |
|||
{ "MID20", ALTIUM_LAYER::MID_LAYER_20 }, |
|||
{ "MID21", ALTIUM_LAYER::MID_LAYER_21 }, |
|||
{ "MID22", ALTIUM_LAYER::MID_LAYER_22 }, |
|||
{ "MID23", ALTIUM_LAYER::MID_LAYER_23 }, |
|||
{ "MID24", ALTIUM_LAYER::MID_LAYER_24 }, |
|||
{ "MID25", ALTIUM_LAYER::MID_LAYER_25 }, |
|||
{ "MID26", ALTIUM_LAYER::MID_LAYER_26 }, |
|||
{ "MID27", ALTIUM_LAYER::MID_LAYER_27 }, |
|||
{ "MID28", ALTIUM_LAYER::MID_LAYER_28 }, |
|||
{ "MID29", ALTIUM_LAYER::MID_LAYER_29 }, |
|||
{ "MID30", ALTIUM_LAYER::MID_LAYER_30 }, |
|||
{ "BOTTOM", ALTIUM_LAYER::BOTTOM_LAYER }, |
|||
|
|||
{ "PLANE1", ALTIUM_LAYER::INTERNAL_PLANE_1 }, |
|||
{ "PLANE2", ALTIUM_LAYER::INTERNAL_PLANE_2 }, |
|||
{ "PLANE3", ALTIUM_LAYER::INTERNAL_PLANE_3 }, |
|||
{ "PLANE4", ALTIUM_LAYER::INTERNAL_PLANE_4 }, |
|||
{ "PLANE5", ALTIUM_LAYER::INTERNAL_PLANE_5 }, |
|||
{ "PLANE6", ALTIUM_LAYER::INTERNAL_PLANE_6 }, |
|||
{ "PLANE7", ALTIUM_LAYER::INTERNAL_PLANE_7 }, |
|||
{ "PLANE8", ALTIUM_LAYER::INTERNAL_PLANE_8 }, |
|||
{ "PLANE9", ALTIUM_LAYER::INTERNAL_PLANE_9 }, |
|||
{ "PLANE10", ALTIUM_LAYER::INTERNAL_PLANE_10 }, |
|||
{ "PLANE11", ALTIUM_LAYER::INTERNAL_PLANE_11 }, |
|||
{ "PLANE12", ALTIUM_LAYER::INTERNAL_PLANE_12 }, |
|||
{ "PLANE13", ALTIUM_LAYER::INTERNAL_PLANE_13 }, |
|||
{ "PLANE14", ALTIUM_LAYER::INTERNAL_PLANE_14 }, |
|||
{ "PLANE15", ALTIUM_LAYER::INTERNAL_PLANE_15 }, |
|||
{ "PLANE16", ALTIUM_LAYER::INTERNAL_PLANE_16 }, |
|||
|
|||
{ "MECHANICAL1", ALTIUM_LAYER::MECHANICAL_1 }, |
|||
{ "MECHANICAL2", ALTIUM_LAYER::MECHANICAL_2 }, |
|||
{ "MECHANICAL3", ALTIUM_LAYER::MECHANICAL_3 }, |
|||
{ "MECHANICAL4", ALTIUM_LAYER::MECHANICAL_4 }, |
|||
{ "MECHANICAL5", ALTIUM_LAYER::MECHANICAL_5 }, |
|||
{ "MECHANICAL6", ALTIUM_LAYER::MECHANICAL_6 }, |
|||
{ "MECHANICAL7", ALTIUM_LAYER::MECHANICAL_7 }, |
|||
{ "MECHANICAL8", ALTIUM_LAYER::MECHANICAL_8 }, |
|||
{ "MECHANICAL9", ALTIUM_LAYER::MECHANICAL_9 }, |
|||
{ "MECHANICAL10", ALTIUM_LAYER::MECHANICAL_10 }, |
|||
{ "MECHANICAL11", ALTIUM_LAYER::MECHANICAL_11 }, |
|||
{ "MECHANICAL12", ALTIUM_LAYER::MECHANICAL_12 }, |
|||
{ "MECHANICAL13", ALTIUM_LAYER::MECHANICAL_13 }, |
|||
{ "MECHANICAL14", ALTIUM_LAYER::MECHANICAL_14 }, |
|||
{ "MECHANICAL15", ALTIUM_LAYER::MECHANICAL_15 }, |
|||
{ "MECHANICAL16", ALTIUM_LAYER::MECHANICAL_16 }, |
|||
}; |
|||
|
|||
auto it = hash_map.find( std::string( aName.c_str() ) ); |
|||
if( it == hash_map.end() ) |
|||
{ |
|||
wxLogError( wxString::Format( |
|||
_( "Unknown mapping of the Altium layer '%s'. Please report as issue." ), aName ) ); |
|||
return ALTIUM_LAYER::UNKNOWN; |
|||
} |
|||
else |
|||
{ |
|||
return it->second; |
|||
} |
|||
} |
|||
|
|||
void altium_parse_polygons( |
|||
std::map<wxString, wxString>& aProperties, std::vector<ALTIUM_VERTICE>& aVertices ) |
|||
{ |
|||
for( size_t i = 0; i < std::numeric_limits<size_t>::max(); i++ ) |
|||
{ |
|||
const wxString si = std::to_string( i ); |
|||
|
|||
const wxString vxi = "VX" + si; |
|||
const wxString vyi = "VY" + si; |
|||
|
|||
if( aProperties.find( vxi ) == aProperties.end() |
|||
|| aProperties.find( vyi ) == aProperties.end() ) |
|||
{ |
|||
break; // it doesn't seem like we know beforehand how many vertices are inside a polygon
|
|||
} |
|||
|
|||
const bool isRound = ALTIUM_PARSER::PropertiesReadInt( aProperties, "KIND" + si, 0 ) != 0; |
|||
const int32_t radius = |
|||
ALTIUM_PARSER::PropertiesReadKicadUnit( aProperties, "R" + si, "0mil" ); |
|||
const double sa = ALTIUM_PARSER::PropertiesReadDouble( aProperties, "SA" + si, 0. ); |
|||
const double ea = ALTIUM_PARSER::PropertiesReadDouble( aProperties, "EA" + si, 0. ); |
|||
const wxPoint vp = |
|||
wxPoint( ALTIUM_PARSER::PropertiesReadKicadUnit( aProperties, vxi, "0mil" ), |
|||
-ALTIUM_PARSER::PropertiesReadKicadUnit( aProperties, vyi, "0mil" ) ); |
|||
const wxPoint cp = |
|||
wxPoint( ALTIUM_PARSER::PropertiesReadKicadUnit( aProperties, "CX" + si, "0mil" ), |
|||
-ALTIUM_PARSER::PropertiesReadKicadUnit( aProperties, "CY" + si, "0mil" ) ); |
|||
|
|||
aVertices.emplace_back( isRound, radius, sa, ea, vp, cp ); |
|||
} |
|||
} |
|||
|
|||
ABOARD6::ABOARD6( ALTIUM_PARSER& aReader ) |
|||
{ |
|||
std::map<wxString, wxString> properties = aReader.ReadProperties(); |
|||
if( properties.empty() ) |
|||
{ |
|||
THROW_IO_ERROR( _( "Board6 stream has no properties!" ) ); |
|||
} |
|||
|
|||
/*for (auto & property : properties) {
|
|||
std::cout << " * '" << property.first << "' = '" << property.second << "'" << std::endl; |
|||
}*/ |
|||
|
|||
sheetpos = wxPoint( ALTIUM_PARSER::PropertiesReadKicadUnit( properties, "SHEETX", "0mil" ), |
|||
-ALTIUM_PARSER::PropertiesReadKicadUnit( properties, "SHEETY", "0mil" ) ); |
|||
sheetsize = wxSize( ALTIUM_PARSER::PropertiesReadKicadUnit( properties, "SHEETWIDTH", "0mil" ), |
|||
ALTIUM_PARSER::PropertiesReadKicadUnit( properties, "SHEETHEIGHT", "0mil" ) ); |
|||
|
|||
layercount = ALTIUM_PARSER::PropertiesReadInt( properties, "LAYERSETSCOUNT", 1 ) + 1; |
|||
|
|||
for( size_t i = 1; i < std::numeric_limits<size_t>::max(); i++ ) |
|||
{ |
|||
const wxString layeri = "LAYER" + std::to_string( i ); |
|||
const wxString layername = layeri + "NAME"; |
|||
|
|||
auto layernameit = properties.find( layername ); |
|||
if( layernameit == properties.end() ) |
|||
{ |
|||
break; // it doesn't seem like we know beforehand how many vertices are inside a polygon
|
|||
} |
|||
|
|||
ABOARD6_LAYER_STACKUP curlayer; |
|||
|
|||
curlayer.name = ALTIUM_PARSER::PropertiesReadString( |
|||
properties, layername, "" ); // TODO: trim string
|
|||
curlayer.nextId = ALTIUM_PARSER::PropertiesReadInt( properties, layeri + "NEXT", 0 ); |
|||
curlayer.prevId = ALTIUM_PARSER::PropertiesReadInt( properties, layeri + "PREV", 0 ); |
|||
curlayer.copperthick = |
|||
ALTIUM_PARSER::PropertiesReadKicadUnit( properties, layeri + "COPTHICK", "1.4mil" ); |
|||
|
|||
curlayer.dielectricconst = |
|||
ALTIUM_PARSER::PropertiesReadDouble( properties, layeri + "DIELCONST", 0. ); |
|||
curlayer.dielectricthick = ALTIUM_PARSER::PropertiesReadKicadUnit( |
|||
properties, layeri + "DIELHEIGHT", "60mil" ); |
|||
curlayer.dielectricmaterial = |
|||
ALTIUM_PARSER::PropertiesReadString( properties, layeri + "DIELMATERIAL", "FR-4" ); |
|||
|
|||
stackup.push_back( curlayer ); |
|||
} |
|||
|
|||
altium_parse_polygons( properties, board_vertices ); |
|||
|
|||
if( aReader.HasParsingError() ) |
|||
{ |
|||
THROW_IO_ERROR( _( "Board6 stream was not parsed correctly!" ) ); |
|||
} |
|||
} |
|||
|
|||
ACLASS6::ACLASS6( ALTIUM_PARSER& aReader ) |
|||
{ |
|||
std::map<wxString, wxString> properties = aReader.ReadProperties(); |
|||
if( properties.empty() ) |
|||
{ |
|||
THROW_IO_ERROR( _( "Classes6 stream has no properties!" ) ); |
|||
} |
|||
|
|||
name = ALTIUM_PARSER::PropertiesReadString( properties, "NAME", "" ); |
|||
uniqueid = ALTIUM_PARSER::PropertiesReadString( properties, "UNIQUEID", "" ); |
|||
kind = static_cast<ALTIUM_CLASS_KIND>( |
|||
ALTIUM_PARSER::PropertiesReadInt( properties, "KIND", -1 ) ); |
|||
|
|||
for( size_t i = 0; i < std::numeric_limits<size_t>::max(); i++ ) |
|||
{ |
|||
auto mit = properties.find( "M" + std::to_string( i ) ); |
|||
if( mit == properties.end() ) |
|||
{ |
|||
break; // it doesn't seem like we know beforehand how many components are in the netclass
|
|||
} |
|||
names.push_back( mit->second ); |
|||
} |
|||
|
|||
if( aReader.HasParsingError() ) |
|||
{ |
|||
THROW_IO_ERROR( _( "Classes6 stream was not parsed correctly!" ) ); |
|||
} |
|||
} |
|||
|
|||
ACOMPONENT6::ACOMPONENT6( ALTIUM_PARSER& aReader ) |
|||
{ |
|||
std::map<wxString, wxString> properties = aReader.ReadProperties(); |
|||
if( properties.empty() ) |
|||
{ |
|||
THROW_IO_ERROR( _( "Components6 stream has no properties!" ) ); |
|||
} |
|||
|
|||
layer = altium_layer_from_name( |
|||
ALTIUM_PARSER::PropertiesReadString( properties, "LAYER", "" ) ); |
|||
position = wxPoint( ALTIUM_PARSER::PropertiesReadKicadUnit( properties, "X", "0mil" ), |
|||
-ALTIUM_PARSER::PropertiesReadKicadUnit( properties, "Y", "0mil" ) ); |
|||
rotation = ALTIUM_PARSER::PropertiesReadDouble( properties, "ROTATION", 0. ); |
|||
locked = ALTIUM_PARSER::PropertiesReadBool( properties, "LOCKED", false ); |
|||
nameon = ALTIUM_PARSER::PropertiesReadBool( properties, "NAMEON", true ); |
|||
commenton = ALTIUM_PARSER::PropertiesReadBool( properties, "COMMENTON", false ); |
|||
sourcedesignator = ALTIUM_PARSER::PropertiesReadString( properties, "SOURCEDESIGNATOR", "" ); |
|||
sourcefootprintlibrary = |
|||
ALTIUM_PARSER::PropertiesReadString( properties, "SOURCEFOOTPRINTLIBRARY", "" ); |
|||
sourcecomponentlibrary = |
|||
ALTIUM_PARSER::PropertiesReadString( properties, "SOURCECOMPONENTLIBRARY", "" ); |
|||
sourcelibreference = |
|||
ALTIUM_PARSER::PropertiesReadString( properties, "SOURCELIBREFERENCE", "" ); |
|||
|
|||
if( aReader.HasParsingError() ) |
|||
{ |
|||
THROW_IO_ERROR( _( "Components6 stream was not parsed correctly" ) ); |
|||
} |
|||
} |
|||
|
|||
ADIMENSION6::ADIMENSION6( ALTIUM_PARSER& aReader ) |
|||
{ |
|||
aReader.Skip( 2 ); |
|||
|
|||
std::map<wxString, wxString> properties = aReader.ReadProperties(); |
|||
if( properties.empty() ) |
|||
{ |
|||
THROW_IO_ERROR( _( "Dimensions6 stream has no properties" ) ); |
|||
} |
|||
|
|||
layer = altium_layer_from_name( |
|||
ALTIUM_PARSER::PropertiesReadString( properties, "LAYER", "" ) ); |
|||
kind = static_cast<ALTIUM_DIMENSION_KIND>( |
|||
ALTIUM_PARSER::PropertiesReadInt( properties, "DIMENSIONKIND", 0 ) ); |
|||
|
|||
textformat = ALTIUM_PARSER::PropertiesReadString( properties, "TEXTFORMAT", "" ); |
|||
|
|||
height = ALTIUM_PARSER::PropertiesReadKicadUnit( properties, "HEIGHT", "0mil" ); |
|||
angle = ALTIUM_PARSER::PropertiesReadDouble( properties, "ANGLE", 0. ); |
|||
|
|||
linewidth = ALTIUM_PARSER::PropertiesReadKicadUnit( properties, "LINEWIDTH", "10mil" ); |
|||
textheight = ALTIUM_PARSER::PropertiesReadKicadUnit( properties, "TEXTHEIGHT", "10mil" ); |
|||
textlinewidth = ALTIUM_PARSER::PropertiesReadKicadUnit( properties, "TEXTLINEWIDTH", "6mil" ); |
|||
textprecission = ALTIUM_PARSER::PropertiesReadInt( properties, "TEXTPRECISION", 2 ); |
|||
textbold = ALTIUM_PARSER::PropertiesReadBool( properties, "TEXTLINEWIDTH", false ); |
|||
textitalic = ALTIUM_PARSER::PropertiesReadBool( properties, "ITALIC", false ); |
|||
|
|||
arrowsize = ALTIUM_PARSER::PropertiesReadKicadUnit( properties, "ARROWSIZE", "60mil" ); |
|||
|
|||
xy1 = wxPoint( ALTIUM_PARSER::PropertiesReadKicadUnit( properties, "X1", "0mil" ), |
|||
-ALTIUM_PARSER::PropertiesReadKicadUnit( properties, "Y1", "0mil" ) ); |
|||
|
|||
int refcount = ALTIUM_PARSER::PropertiesReadInt( properties, "REFERENCES_COUNT", 0 ); |
|||
for( int i = 0; i < refcount; i++ ) |
|||
{ |
|||
const std::string refi = "REFERENCE" + std::to_string( i ); |
|||
referencePoint.emplace_back( |
|||
ALTIUM_PARSER::PropertiesReadKicadUnit( properties, refi + "POINTX", "0mil" ), |
|||
-ALTIUM_PARSER::PropertiesReadKicadUnit( properties, refi + "POINTY", "0mil" ) ); |
|||
} |
|||
|
|||
for( size_t i = 1; i < std::numeric_limits<size_t>::max(); i++ ) |
|||
{ |
|||
const std::string texti = "TEXT" + std::to_string( i ); |
|||
const std::string textix = texti + "X"; |
|||
const std::string textiy = texti + "Y"; |
|||
|
|||
if( properties.find( textix ) == properties.end() |
|||
|| properties.find( textiy ) == properties.end() ) |
|||
{ |
|||
break; // it doesn't seem like we know beforehand how many vertices are inside a polygon
|
|||
} |
|||
|
|||
textPoint.emplace_back( |
|||
ALTIUM_PARSER::PropertiesReadKicadUnit( properties, textix, "0mil" ), |
|||
-ALTIUM_PARSER::PropertiesReadKicadUnit( properties, textiy, "0mil" ) ); |
|||
} |
|||
|
|||
wxString dimensionunit = |
|||
ALTIUM_PARSER::PropertiesReadString( properties, "TEXTDIMENSIONUNIT", "Millimeters" ); |
|||
if( dimensionunit == "Inches" ) |
|||
{ |
|||
textunit = ALTIUM_UNIT::INCHES; |
|||
} |
|||
else if( dimensionunit == "Mils" ) |
|||
{ |
|||
textunit = ALTIUM_UNIT::MILS; |
|||
} |
|||
else if( dimensionunit == "Millimeters" ) |
|||
{ |
|||
textunit = ALTIUM_UNIT::MILLIMETERS; |
|||
} |
|||
else if( dimensionunit == "Centimeters" ) |
|||
{ |
|||
textunit = ALTIUM_UNIT::CENTIMETER; |
|||
} |
|||
else |
|||
{ |
|||
textunit = ALTIUM_UNIT::UNKNOWN; |
|||
} |
|||
|
|||
if( aReader.HasParsingError() ) |
|||
{ |
|||
THROW_IO_ERROR( _( "Dimensions6 stream was not parsed correctly" ) ); |
|||
} |
|||
} |
|||
|
|||
ANET6::ANET6( ALTIUM_PARSER& aReader ) |
|||
{ |
|||
std::map<wxString, wxString> properties = aReader.ReadProperties(); |
|||
if( properties.empty() ) |
|||
{ |
|||
THROW_IO_ERROR( _( "Nets6 stream has no properties" ) ); |
|||
} |
|||
|
|||
name = ALTIUM_PARSER::PropertiesReadString( properties, "NAME", "" ); |
|||
|
|||
if( aReader.HasParsingError() ) |
|||
{ |
|||
THROW_IO_ERROR( _( "Nets6 stream was not parsed correctly" ) ); |
|||
} |
|||
} |
|||
|
|||
APOLYGON6::APOLYGON6( ALTIUM_PARSER& aReader ) |
|||
{ |
|||
std::map<wxString, wxString> properties = aReader.ReadProperties(); |
|||
if( properties.empty() ) |
|||
{ |
|||
THROW_IO_ERROR( _( "Polygons6 stream has no properties" ) ); |
|||
} |
|||
|
|||
layer = altium_layer_from_name( |
|||
ALTIUM_PARSER::PropertiesReadString( properties, "LAYER", "" ) ); |
|||
net = ALTIUM_PARSER::PropertiesReadInt( properties, "NET", ALTIUM_NET_UNCONNECTED ); |
|||
locked = ALTIUM_PARSER::PropertiesReadBool( properties, "LOCKED", false ); |
|||
|
|||
// TODO: kind
|
|||
|
|||
gridsize = ALTIUM_PARSER::PropertiesReadKicadUnit( properties, "GRIDSIZE", "0mil" ); |
|||
trackwidth = ALTIUM_PARSER::PropertiesReadKicadUnit( properties, "TRACKWIDTH", "0mil" ); |
|||
minprimlength = ALTIUM_PARSER::PropertiesReadKicadUnit( properties, "MINPRIMLENGTH", "0mil" ); |
|||
useoctagons = ALTIUM_PARSER::PropertiesReadBool( properties, "USEOCTAGONS", false ); |
|||
|
|||
wxString hatchstyleraw = ALTIUM_PARSER::PropertiesReadString( properties, "HATCHSTYLE", "" ); |
|||
if( hatchstyleraw == "Solid" ) |
|||
{ |
|||
hatchstyle = ALTIUM_POLYGON_HATCHSTYLE::SOLID; |
|||
} |
|||
else if( hatchstyleraw == "45Degree" ) |
|||
{ |
|||
hatchstyle = ALTIUM_POLYGON_HATCHSTYLE::DEGREE_45; |
|||
} |
|||
else if( hatchstyleraw == "90Degree" ) |
|||
{ |
|||
hatchstyle = ALTIUM_POLYGON_HATCHSTYLE::DEGREE_90; |
|||
} |
|||
else if( hatchstyleraw == "Horizontal" ) |
|||
{ |
|||
hatchstyle = ALTIUM_POLYGON_HATCHSTYLE::HORIZONTAL; |
|||
} |
|||
else if( hatchstyleraw == "Vertical" ) |
|||
{ |
|||
hatchstyle = ALTIUM_POLYGON_HATCHSTYLE::VERTICAL; |
|||
} |
|||
else if( hatchstyleraw == "None" ) |
|||
{ |
|||
hatchstyle = ALTIUM_POLYGON_HATCHSTYLE::NONE; |
|||
} |
|||
else |
|||
{ |
|||
hatchstyle = ALTIUM_POLYGON_HATCHSTYLE::UNKNOWN; |
|||
} |
|||
|
|||
altium_parse_polygons( properties, vertices ); |
|||
|
|||
if( aReader.HasParsingError() ) |
|||
{ |
|||
THROW_IO_ERROR( _( "Polygons6 stream was not parsed correctly" ) ); |
|||
} |
|||
} |
|||
|
|||
ARULE6::ARULE6( ALTIUM_PARSER& aReader ) |
|||
{ |
|||
aReader.Skip( 2 ); |
|||
|
|||
std::map<wxString, wxString> properties = aReader.ReadProperties(); |
|||
if( properties.empty() ) |
|||
{ |
|||
THROW_IO_ERROR( _( "Rules6 stream has no properties" ) ); |
|||
} |
|||
|
|||
name = ALTIUM_PARSER::PropertiesReadString( properties, "NAME", "" ); |
|||
priority = ALTIUM_PARSER::PropertiesReadInt( properties, "PRIORITY", 1 ); |
|||
|
|||
scope1expr = ALTIUM_PARSER::PropertiesReadString( properties, "SCOPE1EXPRESSION", "" ); |
|||
scope2expr = ALTIUM_PARSER::PropertiesReadString( properties, "SCOPE2EXPRESSION", "" ); |
|||
|
|||
wxString rulekind = ALTIUM_PARSER::PropertiesReadString( properties, "RULEKIND", "" ); |
|||
if( rulekind == "Clearance" ) |
|||
{ |
|||
kind = ALTIUM_RULE_KIND::CLEARANCE; |
|||
clearanceGap = ALTIUM_PARSER::PropertiesReadKicadUnit( properties, "GAP", "10mil" ); |
|||
} |
|||
else if( rulekind == "DiffPairsRouting" ) |
|||
{ |
|||
kind = ALTIUM_RULE_KIND::DIFF_PAIR_ROUTINGS; |
|||
} |
|||
else if( rulekind == "Height" ) |
|||
{ |
|||
kind = ALTIUM_RULE_KIND::HEIGHT; |
|||
} |
|||
else if( rulekind == "HoleSize" ) |
|||
{ |
|||
kind = ALTIUM_RULE_KIND::HOLE_SIZE; |
|||
} |
|||
else if( rulekind == "HoleToHoleClearance" ) |
|||
{ |
|||
kind = ALTIUM_RULE_KIND::HOLE_TO_HOLE_CLEARANCE; |
|||
} |
|||
else if( rulekind == "Width" ) |
|||
{ |
|||
kind = ALTIUM_RULE_KIND::WIDTH; |
|||
} |
|||
else if( rulekind == "PasteMaskExpansion" ) |
|||
{ |
|||
kind = ALTIUM_RULE_KIND::PASTE_MASK_EXPANSION; |
|||
} |
|||
else if( rulekind == "PlaneClearance" ) |
|||
{ |
|||
kind = ALTIUM_RULE_KIND::PLANE_CLEARANCE; |
|||
planeclearanceClearance = |
|||
ALTIUM_PARSER::PropertiesReadKicadUnit( properties, "CLEARANCE", "10mil" ); |
|||
} |
|||
else if( rulekind == "PolygonConnect" ) |
|||
{ |
|||
kind = ALTIUM_RULE_KIND::POLYGON_CONNECT; |
|||
polygonconnectAirgapwidth = |
|||
ALTIUM_PARSER::PropertiesReadKicadUnit( properties, "AIRGAPWIDTH", "10mil" ); |
|||
polygonconnectReliefconductorwidth = ALTIUM_PARSER::PropertiesReadKicadUnit( |
|||
properties, "RELIEFCONDUCTORWIDTH", "10mil" ); |
|||
polygonconnectReliefentries = |
|||
ALTIUM_PARSER::PropertiesReadInt( properties, "RELIEFENTRIES", 4 ); |
|||
} |
|||
else |
|||
{ |
|||
kind = ALTIUM_RULE_KIND::UNKNOWN; |
|||
} |
|||
|
|||
if( aReader.HasParsingError() ) |
|||
{ |
|||
THROW_IO_ERROR( _( "Rules6 stream was not parsed correctly" ) ); |
|||
} |
|||
} |
|||
|
|||
AARC6::AARC6( ALTIUM_PARSER& aReader ) |
|||
{ |
|||
ALTIUM_RECORD recordtype = static_cast<ALTIUM_RECORD>( aReader.Read<uint8_t>() ); |
|||
if( recordtype != ALTIUM_RECORD::ARC ) |
|||
{ |
|||
THROW_IO_ERROR( _( "Arcs6 stream has invalid recordtype" ) ); |
|||
} |
|||
|
|||
// Subrecord 1
|
|||
aReader.ReadAndSetSubrecordLength(); |
|||
|
|||
layer = static_cast<ALTIUM_LAYER>( aReader.Read<uint8_t>() ); |
|||
|
|||
uint8_t flags1 = aReader.Read<uint8_t>(); |
|||
is_locked = ( flags1 & 0x04 ) == 0; |
|||
is_polygonoutline = ( flags1 & 0x02 ) != 0; |
|||
|
|||
uint8_t flags2 = aReader.Read<uint8_t>(); |
|||
is_keepout = flags2 == 2; |
|||
|
|||
net = aReader.Read<uint16_t>(); |
|||
subpolyindex = aReader.Read<uint16_t>(); |
|||
component = aReader.Read<uint16_t>(); |
|||
aReader.Skip( 4 ); |
|||
center = aReader.ReadWxPoint(); |
|||
radius = aReader.ReadKicadUnit(); |
|||
startangle = aReader.Read<double>(); |
|||
endangle = aReader.Read<double>(); |
|||
width = aReader.ReadKicadUnit(); |
|||
|
|||
aReader.SkipSubrecord(); |
|||
|
|||
if( aReader.HasParsingError() ) |
|||
{ |
|||
THROW_IO_ERROR( _( "Arcs6 stream was not parsed correctly" ) ); |
|||
} |
|||
} |
|||
|
|||
APAD6::APAD6( ALTIUM_PARSER& aReader ) |
|||
{ |
|||
ALTIUM_RECORD recordtype = static_cast<ALTIUM_RECORD>( aReader.Read<uint8_t>() ); |
|||
if( recordtype != ALTIUM_RECORD::PAD ) |
|||
{ |
|||
THROW_IO_ERROR( _( "Pads6 stream has invalid recordtype" ) ); |
|||
} |
|||
|
|||
// Subrecord 1
|
|||
size_t subrecord1 = aReader.ReadAndSetSubrecordLength(); |
|||
if( subrecord1 == 0 ) |
|||
{ |
|||
THROW_IO_ERROR( _( "Pads6 stream has no subrecord1 data" ) ); |
|||
} |
|||
name = aReader.ReadWxString(); |
|||
if( aReader.GetRemainingSubrecordBytes() != 0 ) |
|||
{ |
|||
THROW_IO_ERROR( _( "Pads6 stream has invalid subrecord1 length" ) ); |
|||
} |
|||
aReader.SkipSubrecord(); |
|||
|
|||
// Subrecord 2
|
|||
aReader.ReadAndSetSubrecordLength(); |
|||
aReader.SkipSubrecord(); |
|||
|
|||
// Subrecord 3
|
|||
aReader.ReadAndSetSubrecordLength(); |
|||
aReader.SkipSubrecord(); |
|||
|
|||
// Subrecord 4
|
|||
aReader.ReadAndSetSubrecordLength(); |
|||
aReader.SkipSubrecord(); |
|||
|
|||
// Subrecord 5
|
|||
size_t subrecord5 = aReader.ReadAndSetSubrecordLength(); |
|||
if( subrecord5 < 120 ) |
|||
{ |
|||
THROW_IO_ERROR( _( |
|||
"Pads6 stream subrecord has length < 120, which is unexpected" ) ); // TODO: exact minimum length we know?
|
|||
} |
|||
|
|||
layer = static_cast<ALTIUM_LAYER>( aReader.Read<uint8_t>() ); |
|||
|
|||
uint8_t flags1 = aReader.Read<uint8_t>(); |
|||
is_test_fab_top = ( flags1 & 0x80 ) != 0; |
|||
is_tent_bottom = ( flags1 & 0x40 ) != 0; |
|||
is_tent_top = ( flags1 & 0x20 ) != 0; |
|||
is_locked = ( flags1 & 0x04 ) == 0; |
|||
|
|||
uint8_t flags2 = aReader.Read<uint8_t>(); |
|||
is_test_fab_bottom = ( flags2 & 0x01 ) != 0; |
|||
|
|||
net = aReader.Read<uint16_t>(); |
|||
aReader.Skip( 2 ); |
|||
component = aReader.Read<uint16_t>(); |
|||
aReader.Skip( 4 ); |
|||
|
|||
position = aReader.ReadWxPoint(); |
|||
topsize = aReader.ReadWxSize(); |
|||
midsize = aReader.ReadWxSize(); |
|||
botsize = aReader.ReadWxSize(); |
|||
holesize = aReader.ReadKicadUnit(); |
|||
|
|||
topshape = static_cast<ALTIUM_PAD_SHAPE>( aReader.Read<uint8_t>() ); |
|||
midshape = static_cast<ALTIUM_PAD_SHAPE>( aReader.Read<uint8_t>() ); |
|||
botshape = static_cast<ALTIUM_PAD_SHAPE>( aReader.Read<uint8_t>() ); |
|||
|
|||
direction = aReader.Read<double>(); |
|||
plated = aReader.Read<uint8_t>() != 0; |
|||
aReader.Skip( 1 ); |
|||
padmode = static_cast<ALTIUM_PAD_MODE>( aReader.Read<uint8_t>() ); |
|||
aReader.Skip( 23 ); |
|||
pastemaskexpansionmanual = aReader.ReadKicadUnit(); |
|||
soldermaskexpansionmanual = aReader.ReadKicadUnit(); |
|||
aReader.Skip( 7 ); |
|||
pastemaskexpansionmode = static_cast<ALTIUM_PAD_RULE>( aReader.Read<uint8_t>() ); |
|||
soldermaskexpansionmode = static_cast<ALTIUM_PAD_RULE>( aReader.Read<uint8_t>() ); |
|||
aReader.Skip( 3 ); |
|||
holerotation = aReader.Read<double>(); |
|||
if( subrecord5 == 120 ) |
|||
{ |
|||
tolayer = static_cast<ALTIUM_LAYER>( aReader.Read<uint8_t>() ); |
|||
aReader.Skip( 2 ); |
|||
fromlayer = static_cast<ALTIUM_LAYER>( aReader.Read<uint8_t>() ); |
|||
//aReader.skip( 2 );
|
|||
} |
|||
else if( subrecord5 == 171 ) |
|||
{ |
|||
} |
|||
aReader.SkipSubrecord(); |
|||
|
|||
// Subrecord 6
|
|||
size_t subrecord6 = aReader.ReadAndSetSubrecordLength(); |
|||
if( subrecord6 == 651 |
|||
|| subrecord6 == 628 ) // TODO: better detection mechanism (Altium 14 = 628)
|
|||
{ // TODO: detect type from something else than the size?
|
|||
sizeAndShape = std::make_unique<APAD6_SIZE_AND_SHAPE>(); |
|||
|
|||
for( int i = 0; i < 29; i++ ) |
|||
{ |
|||
sizeAndShape->inner_size[i].x = aReader.ReadKicadUnitX(); |
|||
} |
|||
for( int i = 0; i < 29; i++ ) |
|||
{ |
|||
sizeAndShape->inner_size[i].y = aReader.ReadKicadUnitY(); |
|||
} |
|||
|
|||
for( int i = 0; i < 29; i++ ) |
|||
{ |
|||
sizeAndShape->inner_shape[i] = static_cast<ALTIUM_PAD_SHAPE>( aReader.Read<uint8_t>() ); |
|||
} |
|||
|
|||
aReader.Skip( 1 ); |
|||
|
|||
sizeAndShape->holeshape = static_cast<ALTIUM_PAD_HOLE_SHAPE>( aReader.Read<uint8_t>() ); |
|||
sizeAndShape->slotsize = aReader.ReadKicadUnit(); |
|||
sizeAndShape->slotrotation = aReader.Read<double>(); |
|||
|
|||
for( int i = 0; i < 32; i++ ) |
|||
{ |
|||
sizeAndShape->holeoffset[i].x = aReader.ReadKicadUnitX(); |
|||
} |
|||
for( int i = 0; i < 32; i++ ) |
|||
{ |
|||
sizeAndShape->holeoffset[i].y = aReader.ReadKicadUnitY(); |
|||
} |
|||
|
|||
aReader.Skip( 1 ); |
|||
|
|||
for( int i = 0; i < 32; i++ ) |
|||
{ |
|||
sizeAndShape->alt_shape[i] = |
|||
static_cast<ALTIUM_PAD_SHAPE_ALT>( aReader.Read<uint8_t>() ); |
|||
} |
|||
|
|||
for( int i = 0; i < 32; i++ ) |
|||
{ |
|||
sizeAndShape->cornerradius[i] = aReader.Read<uint8_t>(); |
|||
} |
|||
} |
|||
|
|||
aReader.SkipSubrecord(); |
|||
|
|||
if( aReader.HasParsingError() ) |
|||
{ |
|||
THROW_IO_ERROR( _( "Pads6 stream was not parsed correctly" ) ); |
|||
} |
|||
} |
|||
|
|||
AVIA6::AVIA6( ALTIUM_PARSER& aReader ) |
|||
{ |
|||
ALTIUM_RECORD recordtype = static_cast<ALTIUM_RECORD>( aReader.Read<uint8_t>() ); |
|||
if( recordtype != ALTIUM_RECORD::VIA ) |
|||
{ |
|||
THROW_IO_ERROR( _( "Vias6 stream has invalid recordtype" ) ); |
|||
} |
|||
|
|||
// Subrecord 1
|
|||
aReader.ReadAndSetSubrecordLength(); |
|||
|
|||
aReader.Skip( 1 ); |
|||
|
|||
uint8_t flags1 = aReader.Read<uint8_t>(); |
|||
is_test_fab_top = ( flags1 & 0x80 ) != 0; |
|||
is_tent_bottom = ( flags1 & 0x40 ) != 0; |
|||
is_tent_top = ( flags1 & 0x20 ) != 0; |
|||
is_locked = ( flags1 & 0x04 ) == 0; |
|||
|
|||
uint8_t flags2 = aReader.Read<uint8_t>(); |
|||
is_test_fab_bottom = ( flags2 & 0x01 ) != 0; |
|||
|
|||
net = aReader.Read<uint16_t>(); |
|||
aReader.Skip( 8 ); |
|||
position = aReader.ReadWxPoint(); |
|||
diameter = aReader.ReadKicadUnit(); |
|||
holesize = aReader.ReadKicadUnit(); |
|||
|
|||
layer_start = static_cast<ALTIUM_LAYER>( aReader.Read<uint8_t>() ); |
|||
layer_end = static_cast<ALTIUM_LAYER>( aReader.Read<uint8_t>() ); |
|||
aReader.Skip( 43 ); |
|||
viamode = static_cast<ALTIUM_PAD_MODE>( aReader.Read<uint8_t>() ); |
|||
|
|||
aReader.SkipSubrecord(); |
|||
|
|||
if( aReader.HasParsingError() ) |
|||
{ |
|||
THROW_IO_ERROR( _( "Vias6 stream was not parsed correctly" ) ); |
|||
} |
|||
} |
|||
|
|||
ATRACK6::ATRACK6( ALTIUM_PARSER& aReader ) |
|||
{ |
|||
ALTIUM_RECORD recordtype = static_cast<ALTIUM_RECORD>( aReader.Read<uint8_t>() ); |
|||
if( recordtype != ALTIUM_RECORD::TRACK ) |
|||
{ |
|||
THROW_IO_ERROR( _( "Tracks6 stream has invalid recordtype" ) ); |
|||
} |
|||
|
|||
// Subrecord 1
|
|||
aReader.ReadAndSetSubrecordLength(); |
|||
|
|||
layer = static_cast<ALTIUM_LAYER>( aReader.Read<uint8_t>() ); |
|||
|
|||
uint8_t flags1 = aReader.Read<uint8_t>(); |
|||
is_locked = ( flags1 & 0x04 ) == 0; |
|||
is_polygonoutline = ( flags1 & 0x02 ) != 0; |
|||
|
|||
uint8_t flags2 = aReader.Read<uint8_t>(); |
|||
is_keepout = flags2 == 2; |
|||
|
|||
net = aReader.Read<uint16_t>(); |
|||
subpolyindex = aReader.Read<uint16_t>(); |
|||
component = aReader.Read<uint16_t>(); |
|||
aReader.Skip( 4 ); |
|||
start = aReader.ReadWxPoint(); |
|||
end = aReader.ReadWxPoint(); |
|||
width = aReader.ReadKicadUnit(); |
|||
|
|||
aReader.SkipSubrecord(); |
|||
|
|||
if( aReader.HasParsingError() ) |
|||
{ |
|||
THROW_IO_ERROR( _( "Tracks6 stream was not parsed correctly" ) ); |
|||
} |
|||
} |
|||
|
|||
ATEXT6::ATEXT6( ALTIUM_PARSER& aReader ) |
|||
{ |
|||
ALTIUM_RECORD recordtype = static_cast<ALTIUM_RECORD>( aReader.Read<uint8_t>() ); |
|||
if( recordtype != ALTIUM_RECORD::TEXT ) |
|||
{ |
|||
THROW_IO_ERROR( _( "Texts6 stream has invalid recordtype" ) ); |
|||
} |
|||
|
|||
// Subrecord 1 - Properties
|
|||
size_t subrecord1 = aReader.ReadAndSetSubrecordLength(); |
|||
|
|||
layer = static_cast<ALTIUM_LAYER>( aReader.Read<uint8_t>() ); |
|||
aReader.Skip( 6 ); |
|||
component = aReader.Read<uint16_t>(); |
|||
aReader.Skip( 4 ); |
|||
position = aReader.ReadWxPoint(); |
|||
height = aReader.ReadKicadUnit(); |
|||
aReader.Skip( 2 ); |
|||
rotation = aReader.Read<double>(); |
|||
mirrored = aReader.Read<uint8_t>() != 0; |
|||
strokewidth = aReader.ReadKicadUnit(); |
|||
isComment = aReader.Read<uint8_t>() != 0; |
|||
isDesignator = aReader.Read<uint8_t>() != 0; |
|||
aReader.Skip( 90 ); |
|||
textposition = static_cast<ALTIUM_TEXT_POSITION>( aReader.Read<uint8_t>() ); |
|||
/**
|
|||
* In Altium 14 (subrecord1 == 230) only left bottom is valid? I think there is a bit missing. |
|||
* https://gitlab.com/kicad/code/kicad/merge_requests/60#note_274913397
|
|||
*/ |
|||
if( subrecord1 <= 230 ) |
|||
{ |
|||
textposition = ALTIUM_TEXT_POSITION::LEFT_BOTTOM; |
|||
} |
|||
aReader.Skip( 27 ); |
|||
isTruetype = aReader.Read<uint8_t>() != 0; |
|||
|
|||
aReader.SkipSubrecord(); |
|||
|
|||
// Subrecord 2 - String
|
|||
aReader.ReadAndSetSubrecordLength(); |
|||
|
|||
text = aReader.ReadWxString(); // TODO: what about strings with length > 255?
|
|||
|
|||
aReader.SkipSubrecord(); |
|||
|
|||
if( aReader.HasParsingError() ) |
|||
{ |
|||
THROW_IO_ERROR( _( "Texts6 stream was not parsed correctly" ) ); |
|||
} |
|||
} |
|||
|
|||
AFILL6::AFILL6( ALTIUM_PARSER& aReader ) |
|||
{ |
|||
ALTIUM_RECORD recordtype = static_cast<ALTIUM_RECORD>( aReader.Read<uint8_t>() ); |
|||
if( recordtype != ALTIUM_RECORD::FILL ) |
|||
{ |
|||
THROW_IO_ERROR( _( "Fills6 stream has invalid recordtype" ) ); |
|||
} |
|||
|
|||
// Subrecord 1
|
|||
aReader.ReadAndSetSubrecordLength(); |
|||
|
|||
layer = static_cast<ALTIUM_LAYER>( aReader.Read<uint8_t>() ); |
|||
|
|||
uint8_t flags1 = aReader.Read<uint8_t>(); |
|||
is_locked = ( flags1 & 0x04 ) == 0; |
|||
|
|||
uint8_t flags2 = aReader.Read<uint8_t>(); |
|||
is_keepout = flags2 == 2; |
|||
|
|||
net = aReader.Read<uint16_t>(); |
|||
aReader.Skip( 2 ); |
|||
component = aReader.Read<uint16_t>(); |
|||
aReader.Skip( 4 ); |
|||
pos1 = aReader.ReadWxPoint(); |
|||
pos2 = aReader.ReadWxPoint(); |
|||
rotation = aReader.Read<double>(); |
|||
|
|||
aReader.SkipSubrecord(); |
|||
|
|||
if( aReader.HasParsingError() ) |
|||
{ |
|||
THROW_IO_ERROR( _( "Fills6 stream was not parsed correctly" ) ); |
|||
} |
|||
} |
|||
|
|||
AREGION6::AREGION6( ALTIUM_PARSER& aReader, bool aExtendedVertices ) |
|||
{ |
|||
ALTIUM_RECORD recordtype = static_cast<ALTIUM_RECORD>( aReader.Read<uint8_t>() ); |
|||
if( recordtype != ALTIUM_RECORD::REGION ) |
|||
{ |
|||
THROW_IO_ERROR( _( "Regions6 stream has invalid recordtype" ) ); |
|||
} |
|||
|
|||
// Subrecord 1
|
|||
aReader.ReadAndSetSubrecordLength(); |
|||
|
|||
layer = static_cast<ALTIUM_LAYER>( aReader.Read<uint8_t>() ); |
|||
|
|||
uint8_t flags1 = aReader.Read<uint8_t>(); |
|||
is_locked = ( flags1 & 0x04 ) == 0; |
|||
|
|||
uint8_t flags2 = aReader.Read<uint8_t>(); |
|||
is_keepout = flags2 == 2; |
|||
|
|||
net = aReader.Read<uint16_t>(); |
|||
aReader.Skip( 2 ); |
|||
component = aReader.Read<uint16_t>(); |
|||
aReader.Skip( 9 ); |
|||
|
|||
std::map<wxString, wxString> properties = aReader.ReadProperties(); |
|||
if( properties.empty() ) |
|||
{ |
|||
THROW_IO_ERROR( _( "Regions6 stream has empty properties" ) ); |
|||
} |
|||
|
|||
int pkind = ALTIUM_PARSER::PropertiesReadInt( properties, "KIND", 0 ); |
|||
bool is_cutout = ALTIUM_PARSER::PropertiesReadBool( properties, "ISBOARDCUTOUT", false ); |
|||
|
|||
is_shapebased = ALTIUM_PARSER::PropertiesReadBool( properties, "ISSHAPEBASED", false ); |
|||
|
|||
subpolyindex = static_cast<uint16_t>( |
|||
ALTIUM_PARSER::PropertiesReadInt( properties, "SUBPOLYINDEX", ALTIUM_POLYGON_NONE ) ); |
|||
|
|||
switch( pkind ) |
|||
{ |
|||
case 0: |
|||
if( is_cutout ) |
|||
{ |
|||
kind = ALTIUM_REGION_KIND::BOARD_CUTOUT; |
|||
} |
|||
else |
|||
{ |
|||
kind = ALTIUM_REGION_KIND::COPPER; |
|||
} |
|||
break; |
|||
case 1: |
|||
kind = ALTIUM_REGION_KIND::POLYGON_CUTOUT; |
|||
break; |
|||
case 4: |
|||
kind = ALTIUM_REGION_KIND::CAVITY_DEFINITION; |
|||
break; |
|||
default: |
|||
kind = ALTIUM_REGION_KIND::UNKNOWN; |
|||
break; |
|||
} |
|||
|
|||
uint32_t num_vertices = aReader.Read<uint32_t>(); |
|||
|
|||
for( uint32_t i = 0; i < num_vertices; i++ ) |
|||
{ |
|||
if( aExtendedVertices ) |
|||
{ |
|||
bool isRound = aReader.Read<uint8_t>() != 0; |
|||
wxPoint position = aReader.ReadWxPoint(); |
|||
wxPoint center = aReader.ReadWxPoint(); |
|||
int32_t radius = aReader.ReadKicadUnit(); |
|||
double angle1 = aReader.Read<double>(); |
|||
double angle2 = aReader.Read<double>(); |
|||
vertices.emplace_back( isRound, radius, angle1, angle2, position, center ); |
|||
} |
|||
else |
|||
{ |
|||
// For some regions the coordinates are stored as double and not as int32_t
|
|||
int32_t x = ALTIUM_PARSER::ConvertToKicadUnit( aReader.Read<double>() ); |
|||
int32_t y = ALTIUM_PARSER::ConvertToKicadUnit( -aReader.Read<double>() ); |
|||
vertices.emplace_back( wxPoint( x, y ) ); |
|||
} |
|||
} |
|||
|
|||
aReader.SkipSubrecord(); |
|||
|
|||
if( aReader.HasParsingError() ) |
|||
{ |
|||
THROW_IO_ERROR( _( "Regions6 stream was not parsed correctly" ) ); |
|||
} |
|||
} |
@ -0,0 +1,616 @@ |
|||
/* |
|||
* This program source code file is part of KiCad, a free EDA CAD application. |
|||
* |
|||
* Copyright (C) 2020 Thomas Pointhuber <thomas.pointhuber@gmx.at> |
|||
* |
|||
* 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 |
|||
*/ |
|||
|
|||
#ifndef ALTIUM_PARSER_PCB_H |
|||
#define ALTIUM_PARSER_PCB_H |
|||
|
|||
#include <cstdint> |
|||
#include <cstring> |
|||
#include <memory> |
|||
#include <vector> |
|||
|
|||
#include <wx/gdicmn.h> |
|||
|
|||
// tthis constant specifies an unconnected net |
|||
const uint16_t ALTIUM_NET_UNCONNECTED = std::numeric_limits<uint16_t>::max(); |
|||
|
|||
// this constant specifies a item which is not inside an component |
|||
const uint16_t ALTIUM_COMPONENT_NONE = std::numeric_limits<uint16_t>::max(); |
|||
|
|||
// this constant specifies a item which does not define a polygon |
|||
const uint16_t ALTIUM_POLYGON_NONE = std::numeric_limits<uint16_t>::max(); |
|||
|
|||
|
|||
enum class ALTIUM_UNIT |
|||
{ |
|||
UNKNOWN = 0, |
|||
|
|||
INCHES = 1, |
|||
MILS = 2, |
|||
MILLIMETERS = 3, |
|||
CENTIMETER = 4 |
|||
}; |
|||
|
|||
enum class ALTIUM_CLASS_KIND |
|||
{ |
|||
UNKNOWN = -1, |
|||
|
|||
NET_CLASS = 0, |
|||
SOURCE_SCHEMATIC_CLASS = 1, |
|||
FROM_TO = 2, |
|||
PAD_CLASS = 3, |
|||
LAYER_CLASS = 4, |
|||
UNKNOWN_CLASS = 5, |
|||
DIFF_PAIR_CLASS = 6, |
|||
POLYGON_CLASS = 7 |
|||
}; |
|||
|
|||
enum class ALTIUM_DIMENSION_KIND |
|||
{ |
|||
UNKNOWN = 0, |
|||
|
|||
LINEAR = 1, |
|||
ANGULAR = 2, |
|||
RADIAL = 3, |
|||
LEADER = 4, |
|||
DATUM = 5, |
|||
BASELINE = 6, |
|||
CENTER = 7, |
|||
LINEAR_DIAMETER = 8, |
|||
RADIAL_DIAMETER = 9 |
|||
}; |
|||
|
|||
enum class ALTIUM_REGION_KIND |
|||
{ |
|||
UNKNOWN = -1, |
|||
|
|||
COPPER = 0, // KIND=0 |
|||
POLYGON_CUTOUT = 1, // KIND=1 |
|||
BOARD_CUTOUT = 2, // KIND=0 AND ISBOARDCUTOUT=TRUE |
|||
CAVITY_DEFINITION = 3, // KIND=4 |
|||
}; |
|||
|
|||
enum class ALTIUM_RULE_KIND |
|||
{ |
|||
UNKNOWN = 0, |
|||
|
|||
CLEARANCE = 1, |
|||
DIFF_PAIR_ROUTINGS = 2, |
|||
HEIGHT = 3, |
|||
HOLE_SIZE = 4, |
|||
HOLE_TO_HOLE_CLEARANCE = 5, |
|||
WIDTH = 6, |
|||
PASTE_MASK_EXPANSION = 7, |
|||
PLANE_CLEARANCE = 8, |
|||
POLYGON_CONNECT = 9, |
|||
}; |
|||
|
|||
enum class ALTIUM_RECORD |
|||
{ |
|||
ARC = 1, |
|||
PAD = 2, |
|||
VIA = 3, |
|||
TRACK = 4, |
|||
TEXT = 5, |
|||
FILL = 6, |
|||
REGION = 11, |
|||
MODEL = 12 |
|||
}; |
|||
|
|||
enum class ALTIUM_PAD_SHAPE |
|||
{ |
|||
UNKNOWN = 0, |
|||
CIRCLE = 1, |
|||
RECT = 2, |
|||
OCTAGONAL = 3 |
|||
}; |
|||
|
|||
enum class ALTIUM_PAD_SHAPE_ALT |
|||
{ |
|||
UNKNOWN = 0, |
|||
CIRCLE = 1, |
|||
RECT = 2, // TODO: valid? |
|||
OCTAGONAL = 3, // TODO: valid? |
|||
ROUNDRECT = 9 |
|||
}; |
|||
|
|||
enum class ALTIUM_PAD_HOLE_SHAPE |
|||
{ |
|||
UNKNOWN = -1, |
|||
ROUND = 0, |
|||
SQUARE = 1, |
|||
SLOT = 2 |
|||
}; |
|||
|
|||
enum class ALTIUM_PAD_MODE |
|||
{ |
|||
SIMPLE = 0, |
|||
TOP_MIDDLE_BOTTOM = 1, |
|||
FULL_STACK = 2 |
|||
}; |
|||
|
|||
enum class ALTIUM_PAD_RULE |
|||
{ |
|||
UNKNOWN = 0, |
|||
RULE = 1, |
|||
MANUAL = 2 |
|||
}; |
|||
|
|||
enum class ALTIUM_POLYGON_HATCHSTYLE |
|||
{ |
|||
UNKNOWN = 0, |
|||
|
|||
SOLID = 1, |
|||
DEGREE_45 = 2, |
|||
DEGREE_90 = 3, |
|||
HORIZONTAL = 4, |
|||
VERTICAL = 5, |
|||
NONE = 6 |
|||
}; |
|||
|
|||
enum class ALTIUM_TEXT_POSITION |
|||
{ |
|||
LEFT_TOP = 1, |
|||
LEFT_CENTER = 2, |
|||
LEFT_BOTTOM = 3, |
|||
CENTER_TOP = 4, |
|||
CENTER_CENTER = 5, |
|||
CENTER_BOTTOM = 6, |
|||
RIGHT_TOP = 7, |
|||
RIGHT_CENTER = 8, |
|||
RIGHT_BOTTOM = 9 |
|||
}; |
|||
|
|||
struct ALTIUM_VERTICE |
|||
{ |
|||
const bool isRound; |
|||
const int32_t radius; |
|||
const double startangle; |
|||
const double endangle; |
|||
const wxPoint position; |
|||
const wxPoint center; |
|||
|
|||
explicit ALTIUM_VERTICE( const wxPoint aPosition ) |
|||
: isRound( false ), |
|||
radius( 0 ), |
|||
startangle( 0. ), |
|||
endangle( 0. ), |
|||
position( aPosition ), |
|||
center( wxPoint( 0, 0 ) ) |
|||
{ |
|||
} |
|||
|
|||
explicit ALTIUM_VERTICE( bool aIsRound, int32_t aRadius, double aStartAngle, double aEndAngle, |
|||
const wxPoint aPosition, const wxPoint aCenter ) |
|||
: isRound( aIsRound ), |
|||
radius( aRadius ), |
|||
startangle( aStartAngle ), |
|||
endangle( aEndAngle ), |
|||
position( aPosition ), |
|||
center( aCenter ) |
|||
{ |
|||
} |
|||
}; |
|||
|
|||
enum class ALTIUM_LAYER |
|||
{ |
|||
UNKNOWN = 0, |
|||
|
|||
TOP_LAYER = 1, |
|||
MID_LAYER_1 = 2, |
|||
MID_LAYER_2 = 3, |
|||
MID_LAYER_3 = 4, |
|||
MID_LAYER_4 = 5, |
|||
MID_LAYER_5 = 6, |
|||
MID_LAYER_6 = 7, |
|||
MID_LAYER_7 = 8, |
|||
MID_LAYER_8 = 9, |
|||
MID_LAYER_9 = 10, |
|||
MID_LAYER_10 = 11, |
|||
MID_LAYER_11 = 12, |
|||
MID_LAYER_12 = 13, |
|||
MID_LAYER_13 = 14, |
|||
MID_LAYER_14 = 15, |
|||
MID_LAYER_15 = 16, |
|||
MID_LAYER_16 = 17, |
|||
MID_LAYER_17 = 18, |
|||
MID_LAYER_18 = 19, |
|||
MID_LAYER_19 = 20, |
|||
MID_LAYER_20 = 21, |
|||
MID_LAYER_21 = 22, |
|||
MID_LAYER_22 = 23, |
|||
MID_LAYER_23 = 24, |
|||
MID_LAYER_24 = 25, |
|||
MID_LAYER_25 = 26, |
|||
MID_LAYER_26 = 27, |
|||
MID_LAYER_27 = 28, |
|||
MID_LAYER_28 = 29, |
|||
MID_LAYER_29 = 30, |
|||
MID_LAYER_30 = 31, |
|||
BOTTOM_LAYER = 32, |
|||
|
|||
TOP_OVERLAY = 33, |
|||
BOTTOM_OVERLAY = 34, |
|||
TOP_PASTE = 35, |
|||
BOTTOM_PASTE = 36, |
|||
TOP_SOLDER = 37, |
|||
BOTTOM_SOLDER = 38, |
|||
|
|||
INTERNAL_PLANE_1 = 39, |
|||
INTERNAL_PLANE_2 = 40, |
|||
INTERNAL_PLANE_3 = 41, |
|||
INTERNAL_PLANE_4 = 42, |
|||
INTERNAL_PLANE_5 = 43, |
|||
INTERNAL_PLANE_6 = 44, |
|||
INTERNAL_PLANE_7 = 45, |
|||
INTERNAL_PLANE_8 = 46, |
|||
INTERNAL_PLANE_9 = 47, |
|||
INTERNAL_PLANE_10 = 48, |
|||
INTERNAL_PLANE_11 = 49, |
|||
INTERNAL_PLANE_12 = 50, |
|||
INTERNAL_PLANE_13 = 51, |
|||
INTERNAL_PLANE_14 = 52, |
|||
INTERNAL_PLANE_15 = 53, |
|||
INTERNAL_PLANE_16 = 54, |
|||
|
|||
DRILL_GUIDE = 55, |
|||
KEEP_OUT_LAYER = 56, |
|||
|
|||
MECHANICAL_1 = 57, |
|||
MECHANICAL_2 = 58, |
|||
MECHANICAL_3 = 59, |
|||
MECHANICAL_4 = 60, |
|||
MECHANICAL_5 = 61, |
|||
MECHANICAL_6 = 62, |
|||
MECHANICAL_7 = 63, |
|||
MECHANICAL_8 = 64, |
|||
MECHANICAL_9 = 65, |
|||
MECHANICAL_10 = 66, |
|||
MECHANICAL_11 = 67, |
|||
MECHANICAL_12 = 68, |
|||
MECHANICAL_13 = 69, |
|||
MECHANICAL_14 = 70, |
|||
MECHANICAL_15 = 71, |
|||
MECHANICAL_16 = 72, |
|||
|
|||
DRILL_DRAWING = 73, |
|||
MULTI_LAYER = 74, |
|||
CONNECTIONS = 75, |
|||
BACKGROUND = 76, |
|||
DRC_ERROR_MARKERS = 77, |
|||
SELECTIONS = 78, |
|||
VISIBLE_GRID_1 = 79, |
|||
VISIBLE_GRID_2 = 80, |
|||
PAD_HOLES = 81, |
|||
VIA_HOLES = 82, |
|||
}; |
|||
|
|||
class ALTIUM_PARSER; |
|||
|
|||
struct ABOARD6_LAYER_STACKUP |
|||
{ |
|||
wxString name; |
|||
|
|||
size_t nextId; |
|||
size_t prevId; |
|||
|
|||
int32_t copperthick; |
|||
|
|||
double dielectricconst; |
|||
int32_t dielectricthick; |
|||
wxString dielectricmaterial; |
|||
}; |
|||
|
|||
struct ABOARD6 |
|||
{ |
|||
wxPoint sheetpos; |
|||
wxSize sheetsize; |
|||
|
|||
int layercount; |
|||
std::vector<ABOARD6_LAYER_STACKUP> stackup; |
|||
|
|||
std::vector<ALTIUM_VERTICE> board_vertices; |
|||
|
|||
explicit ABOARD6( ALTIUM_PARSER& aReader ); |
|||
}; |
|||
|
|||
struct ACLASS6 |
|||
{ |
|||
wxString name; |
|||
wxString uniqueid; |
|||
|
|||
ALTIUM_CLASS_KIND kind; |
|||
|
|||
std::vector<wxString> names; |
|||
|
|||
explicit ACLASS6( ALTIUM_PARSER& aReader ); |
|||
}; |
|||
|
|||
struct ACOMPONENT6 |
|||
{ |
|||
ALTIUM_LAYER layer; |
|||
wxPoint position; |
|||
double rotation; |
|||
bool locked; |
|||
bool nameon; |
|||
bool commenton; |
|||
wxString sourcedesignator; |
|||
wxString sourcefootprintlibrary; |
|||
wxString sourcecomponentlibrary; |
|||
wxString sourcelibreference; |
|||
explicit ACOMPONENT6( ALTIUM_PARSER& aReader ); |
|||
}; |
|||
|
|||
struct ADIMENSION6 |
|||
{ |
|||
ALTIUM_LAYER layer; |
|||
ALTIUM_DIMENSION_KIND kind; |
|||
|
|||
wxString textformat; |
|||
|
|||
int32_t height; |
|||
double angle; |
|||
|
|||
uint32_t linewidth; |
|||
uint32_t textheight; |
|||
uint32_t textlinewidth; |
|||
int32_t textprecission; |
|||
bool textbold; |
|||
bool textitalic; |
|||
|
|||
int32_t arrowsize; |
|||
|
|||
ALTIUM_UNIT textunit; |
|||
|
|||
wxPoint xy1; |
|||
|
|||
std::vector<wxPoint> referencePoint; |
|||
std::vector<wxPoint> textPoint; |
|||
|
|||
explicit ADIMENSION6( ALTIUM_PARSER& aReader ); |
|||
}; |
|||
|
|||
struct ANET6 |
|||
{ |
|||
wxString name; |
|||
|
|||
explicit ANET6( ALTIUM_PARSER& aReader ); |
|||
}; |
|||
|
|||
struct APOLYGON6 |
|||
{ |
|||
ALTIUM_LAYER layer; |
|||
uint16_t net; |
|||
bool locked; |
|||
|
|||
ALTIUM_POLYGON_HATCHSTYLE hatchstyle; |
|||
|
|||
int32_t gridsize; |
|||
int32_t trackwidth; |
|||
int32_t minprimlength; |
|||
bool useoctagons; |
|||
|
|||
std::vector<ALTIUM_VERTICE> vertices; |
|||
|
|||
explicit APOLYGON6( ALTIUM_PARSER& aReader ); |
|||
}; |
|||
|
|||
|
|||
struct ARULE6 |
|||
{ |
|||
wxString name; |
|||
int priority; |
|||
|
|||
ALTIUM_RULE_KIND kind; |
|||
|
|||
wxString scope1expr; |
|||
wxString scope2expr; |
|||
|
|||
// ALTIUM_RULE_KIND::CLEARANCE |
|||
int clearanceGap; |
|||
|
|||
// ALTIUM_RULE_KIND::PLANE_CLEARANCE |
|||
int planeclearanceClearance; |
|||
|
|||
// ALTIUM_RULE_KIND::POLYGON_CONNECT |
|||
int32_t polygonconnectAirgapwidth; |
|||
int32_t polygonconnectReliefconductorwidth; |
|||
int polygonconnectReliefentries; |
|||
|
|||
// TODO: implement different types of rules we need to parse |
|||
|
|||
explicit ARULE6( ALTIUM_PARSER& aReader ); |
|||
}; |
|||
|
|||
struct AREGION6 |
|||
{ |
|||
bool is_locked; |
|||
bool is_keepout; |
|||
|
|||
bool is_shapebased; |
|||
|
|||
ALTIUM_LAYER layer; |
|||
uint16_t net; |
|||
uint16_t component; |
|||
uint16_t subpolyindex; |
|||
|
|||
ALTIUM_REGION_KIND kind; // I asume this means if normal or keepout? |
|||
|
|||
std::vector<ALTIUM_VERTICE> vertices; |
|||
|
|||
explicit AREGION6( ALTIUM_PARSER& aReader, bool aExtendedVertices ); |
|||
}; |
|||
|
|||
struct AARC6 |
|||
{ |
|||
bool is_locked; |
|||
bool is_keepout; |
|||
bool is_polygonoutline; |
|||
|
|||
ALTIUM_LAYER layer; |
|||
uint16_t net; |
|||
uint16_t component; |
|||
uint16_t subpolyindex; |
|||
|
|||
wxPoint center; |
|||
uint32_t radius; |
|||
double startangle; |
|||
double endangle; |
|||
uint32_t width; |
|||
|
|||
explicit AARC6( ALTIUM_PARSER& aReader ); |
|||
}; |
|||
|
|||
struct APAD6_SIZE_AND_SHAPE |
|||
{ |
|||
ALTIUM_PAD_HOLE_SHAPE holeshape; |
|||
uint32_t slotsize; |
|||
double slotrotation; |
|||
|
|||
wxSize inner_size[29]; |
|||
ALTIUM_PAD_SHAPE inner_shape[29]; |
|||
wxPoint holeoffset[32]; |
|||
ALTIUM_PAD_SHAPE_ALT alt_shape[32]; |
|||
uint8_t cornerradius[32]; |
|||
}; |
|||
|
|||
struct APAD6 |
|||
{ |
|||
bool is_locked; |
|||
bool is_tent_top; |
|||
bool is_tent_bottom; |
|||
bool is_test_fab_top; |
|||
bool is_test_fab_bottom; |
|||
|
|||
wxString name; |
|||
|
|||
ALTIUM_LAYER layer; |
|||
uint16_t net; |
|||
uint16_t component; |
|||
|
|||
wxPoint position; |
|||
wxSize topsize; |
|||
wxSize midsize; |
|||
wxSize botsize; |
|||
uint32_t holesize; |
|||
|
|||
ALTIUM_PAD_SHAPE topshape; |
|||
ALTIUM_PAD_SHAPE midshape; |
|||
ALTIUM_PAD_SHAPE botshape; |
|||
|
|||
ALTIUM_PAD_MODE padmode; |
|||
|
|||
double direction; |
|||
bool plated; |
|||
ALTIUM_PAD_RULE pastemaskexpansionmode; |
|||
int32_t pastemaskexpansionmanual; |
|||
ALTIUM_PAD_RULE soldermaskexpansionmode; |
|||
int32_t soldermaskexpansionmanual; |
|||
double holerotation; |
|||
|
|||
ALTIUM_LAYER tolayer; |
|||
ALTIUM_LAYER fromlayer; |
|||
|
|||
std::unique_ptr<APAD6_SIZE_AND_SHAPE> sizeAndShape; |
|||
|
|||
explicit APAD6( ALTIUM_PARSER& aReader ); |
|||
}; |
|||
|
|||
struct AVIA6 |
|||
{ |
|||
bool is_locked; |
|||
bool is_tent_top; |
|||
bool is_tent_bottom; |
|||
bool is_test_fab_top; |
|||
bool is_test_fab_bottom; |
|||
|
|||
uint16_t net; |
|||
|
|||
wxPoint position; |
|||
uint32_t diameter; |
|||
uint32_t holesize; |
|||
|
|||
ALTIUM_LAYER layer_start; |
|||
ALTIUM_LAYER layer_end; |
|||
ALTIUM_PAD_MODE viamode; |
|||
|
|||
explicit AVIA6( ALTIUM_PARSER& aReader ); |
|||
}; |
|||
|
|||
struct ATRACK6 |
|||
{ |
|||
bool is_locked; |
|||
bool is_keepout; |
|||
bool is_polygonoutline; |
|||
|
|||
ALTIUM_LAYER layer; |
|||
uint16_t net; |
|||
uint16_t component; |
|||
uint16_t subpolyindex; |
|||
|
|||
wxPoint start; |
|||
wxPoint end; |
|||
uint32_t width; |
|||
|
|||
explicit ATRACK6( ALTIUM_PARSER& aReader ); |
|||
}; |
|||
|
|||
struct ATEXT6 |
|||
{ |
|||
ALTIUM_LAYER layer; |
|||
uint16_t component; |
|||
|
|||
wxPoint position; |
|||
uint32_t height; |
|||
double rotation; |
|||
uint32_t strokewidth; |
|||
ALTIUM_TEXT_POSITION textposition; |
|||
bool mirrored; |
|||
|
|||
bool isComment; |
|||
bool isDesignator; |
|||
bool isTruetype; |
|||
|
|||
wxString text; |
|||
|
|||
explicit ATEXT6( ALTIUM_PARSER& aReader ); |
|||
}; |
|||
|
|||
struct AFILL6 |
|||
{ |
|||
bool is_locked; |
|||
bool is_keepout; |
|||
|
|||
ALTIUM_LAYER layer; |
|||
uint16_t component; |
|||
uint16_t net; |
|||
|
|||
wxPoint pos1; |
|||
wxPoint pos2; |
|||
double rotation; |
|||
|
|||
explicit AFILL6( ALTIUM_PARSER& aReader ); |
|||
}; |
|||
|
|||
|
|||
#endif //ALTIUM_PARSER_PCB_H |
2011
pcbnew/altium2kicadpcb_plugin/altium_pcb.cpp
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,187 @@ |
|||
/* |
|||
* This program source code file is part of KiCad, a free EDA CAD application. |
|||
* |
|||
* Copyright (C) 2019-2020 Thomas Pointhuber <thomas.pointhuber@gmx.at> |
|||
* |
|||
* 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 |
|||
*/ |
|||
|
|||
#ifndef ALTIUM_PCB_H |
|||
#define ALTIUM_PCB_H |
|||
|
|||
#include <functional> |
|||
#include <layers_id_colors_and_visibility.h> |
|||
#include <vector> |
|||
#include <zconf.h> |
|||
|
|||
#include <altium_parser_pcb.h> |
|||
|
|||
|
|||
enum class ALTIUM_PCB_DIR |
|||
{ |
|||
FILE_HEADER, |
|||
|
|||
ADVANCEDPLACEROPTIONS6, |
|||
ARCS6, |
|||
BOARD6, |
|||
BOARDREGIONS, |
|||
CLASSES6, |
|||
COMPONENTBODIES6, |
|||
COMPONENTS6, |
|||
CONNECTIONS6, |
|||
COORDINATES6, |
|||
DESIGNRULECHECKEROPTIONS6, |
|||
DIFFERENTIALPAIRS6, |
|||
DIMENSIONS6, |
|||
EMBEDDEDBOARDS6, |
|||
EMBEDDEDFONTS6, |
|||
EMBEDDEDS6, |
|||
EXTENDPRIMITIVEINFORMATION, |
|||
FILEVERSIONINFO, |
|||
FILLS6, |
|||
FROMTOS6, |
|||
MODELS, |
|||
MODELSNOEMBED, |
|||
NETS6, |
|||
PADS6, |
|||
PADVIALIBRARY, |
|||
PADVIALIBRARYCACHE, |
|||
PADVIALIBRARYLINKS, |
|||
PINSWAPOPTIONS6, |
|||
PINPAIRSSECTION, |
|||
POLYGONS6, |
|||
REGIONS6, |
|||
RULES6, |
|||
SHAPEBASEDCOMPONENTBODIES6, |
|||
SHAPEBASEDREGIONS6, |
|||
SIGNALCLASSES, |
|||
SMARTUNIONS, |
|||
TEXTS, |
|||
TEXTS6, |
|||
TEXTURES, |
|||
TRACKS6, |
|||
UNIONNAMES, |
|||
UNIQUEIDPRIMITIVEINFORMATION, |
|||
VIAS6, |
|||
WIDESTRINGS6 |
|||
}; |
|||
|
|||
|
|||
class BOARD; |
|||
class MODULE; |
|||
class ZONE_CONTAINER; |
|||
|
|||
|
|||
/** |
|||
* Helper method which opens a Altium Board File and parses it. |
|||
* |
|||
* @param aBoard board the pcb should be appended to |
|||
* @param aFileName file name of board file |
|||
* @param aFileMapping mapping how altium stream names are mapped |
|||
*/ |
|||
void ParseAltiumPcb( BOARD* aBoard, const wxString& aFileName, |
|||
const std::map<ALTIUM_PCB_DIR, std::string>& aFileMapping ); |
|||
|
|||
|
|||
namespace CFB |
|||
{ |
|||
class CompoundFileReader; |
|||
struct COMPOUND_FILE_ENTRY; |
|||
} // namespace CFB |
|||
|
|||
|
|||
// type declaration required for a helper method |
|||
class ALTIUM_PCB; |
|||
typedef std::function<void( const CFB::CompoundFileReader&, const CFB::COMPOUND_FILE_ENTRY* )> |
|||
PARSE_FUNCTION_POINTER_fp; |
|||
|
|||
|
|||
class ALTIUM_PCB |
|||
{ |
|||
public: |
|||
explicit ALTIUM_PCB( BOARD* aBoard ); |
|||
~ALTIUM_PCB(); |
|||
|
|||
void Parse( const CFB::CompoundFileReader& aReader, |
|||
const std::map<ALTIUM_PCB_DIR, std::string>& aFileMapping ); |
|||
|
|||
private: |
|||
PCB_LAYER_ID GetKicadLayer( ALTIUM_LAYER aAltiumLayer ) const; |
|||
int GetNetCode( uint16_t aId ) const; |
|||
const ARULE6* GetRule( ALTIUM_RULE_KIND aKind, const wxString& aName ) const; |
|||
const ARULE6* GetRuleDefault( ALTIUM_RULE_KIND aKind ) const; |
|||
|
|||
void ParseFileHeader( |
|||
const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry ); |
|||
|
|||
// Text Format |
|||
void ParseBoard6Data( |
|||
const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry ); |
|||
void ParseClasses6Data( |
|||
const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry ); |
|||
void ParseComponents6Data( |
|||
const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry ); |
|||
void ParseDimensions6Data( |
|||
const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry ); |
|||
void ParseNets6Data( |
|||
const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry ); |
|||
void ParsePolygons6Data( |
|||
const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry ); |
|||
void ParseRules6Data( |
|||
const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry ); |
|||
|
|||
// Binary Format |
|||
void ParseArcs6Data( |
|||
const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry ); |
|||
void ParsePads6Data( |
|||
const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry ); |
|||
void ParseVias6Data( |
|||
const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry ); |
|||
void ParseTracks6Data( |
|||
const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry ); |
|||
void ParseTexts6Data( |
|||
const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry ); |
|||
void ParseFills6Data( |
|||
const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry ); |
|||
void ParseBoardRegionsData( |
|||
const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry ); |
|||
void ParseShapeBasedRegions6Data( |
|||
const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry ); |
|||
void ParseRegions6Data( |
|||
const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry ); |
|||
|
|||
// Helper Functions |
|||
void HelperParseDimensions6Linear( const ADIMENSION6& aElem ); |
|||
void HelperParseDimensions6Leader( const ADIMENSION6& aElem ); |
|||
void HelperParseDimensions6Datum( const ADIMENSION6& aElem ); |
|||
void HelperParseDimensions6Center( const ADIMENSION6& aElem ); |
|||
|
|||
void HelperCreateBoardOutline( const std::vector<ALTIUM_VERTICE>& aVertices ); |
|||
|
|||
BOARD* m_board; |
|||
std::vector<MODULE*> m_components; |
|||
std::vector<ZONE_CONTAINER*> m_polygons; |
|||
size_t m_num_nets; |
|||
std::map<ALTIUM_LAYER, PCB_LAYER_ID> m_layermap; // used to correctly map copper layers |
|||
std::map<ALTIUM_RULE_KIND, std::vector<ARULE6>> m_rules; |
|||
|
|||
std::map<ALTIUM_LAYER, ZONE_CONTAINER*> m_outer_plane; |
|||
}; |
|||
|
|||
|
|||
#endif //ALTIUM_PCB_H |
@ -0,0 +1,3 @@ |
|||
add_library( compoundfilereader INTERFACE ) |
|||
|
|||
target_include_directories( compoundfilereader INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} ) |
@ -0,0 +1,22 @@ |
|||
The MIT License (MIT) |
|||
|
|||
Copyright (c) Microsoft Corporation |
|||
|
|||
Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
of this software and associated documentation files (the "Software"), to deal |
|||
in the Software without restriction, including without limitation the rights |
|||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|||
copies of the Software, and to permit persons to whom the Software is |
|||
furnished to do so, subject to the following conditions: |
|||
|
|||
The above copyright notice and this permission notice shall be included in all |
|||
copies or substantial portions of the Software. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
|||
SOFTWARE. |
|||
|
@ -0,0 +1,4 @@ |
|||
This directory contains the microsoft/compoundfilereader project from https://github.com/microsoft/compoundfilereader. |
|||
|
|||
They are licensed under MIT, with the license text in this directory. |
|||
|
@ -0,0 +1,480 @@ |
|||
/** |
|||
Microsoft Compound File (and Property Set) Reader |
|||
http://en.wikipedia.org/wiki/Compound_File_Binary_Format |
|||
|
|||
Format specification: |
|||
MS-CFB: https://msdn.microsoft.com/en-us/library/dd942138.aspx |
|||
MS-OLEPS: https://msdn.microsoft.com/en-us/library/dd942421.aspx |
|||
|
|||
Note: |
|||
1. For simplification, the code assumes that the target system is little-endian. |
|||
|
|||
2. The reader operates the passed buffer in-place. |
|||
You must keep the input buffer valid when you are using the reader. |
|||
|
|||
3. Single thread usage. |
|||
|
|||
Example 1: print all streams in a compound file |
|||
\code |
|||
CFB::CompoundFileReader reader(buffer, len); |
|||
reader.EnumFiles(reader.GetRootEntry(), -1, |
|||
[&](const CFB::COMPOUND_FILE_ENTRY* entry, const CFB::utf16string& dir, int level)->void |
|||
{ |
|||
bool isDirectory = !reader.IsStream(entry); |
|||
std::string name = UTF16ToUTF8(entry->name); |
|||
std::string indentstr(level * 4, ' '); |
|||
printf("%s%s%s%s\n", indentstr.c_str(), isDirectory ? "[" : "", name.c_str(), isDirectory ? "]" : ""); |
|||
}); |
|||
\endcode |
|||
*/ |
|||
|
|||
#include <algorithm> |
|||
#include <stdint.h> |
|||
#include <string.h> |
|||
#include <exception> |
|||
#include <stdexcept> |
|||
#include <functional> |
|||
|
|||
namespace CFB |
|||
{ |
|||
struct CFBException : public std::runtime_error |
|||
{ |
|||
CFBException(const char* desc) : std::runtime_error(desc) {} |
|||
}; |
|||
struct WrongFormat : public CFBException |
|||
{ |
|||
WrongFormat() : CFBException("Wrong file format") {} |
|||
}; |
|||
struct FileCorrupted : public CFBException |
|||
{ |
|||
FileCorrupted() : CFBException("File corrupted") {} |
|||
}; |
|||
|
|||
#pragma pack(push) |
|||
#pragma pack(1) |
|||
|
|||
struct COMPOUND_FILE_HDR |
|||
{ |
|||
unsigned char signature[8]; |
|||
unsigned char unused_clsid[16]; |
|||
uint16_t minorVersion; |
|||
uint16_t majorVersion; |
|||
uint16_t byteOrder; |
|||
uint16_t sectorShift; |
|||
uint16_t miniSectorShift; |
|||
unsigned char reserved[6]; |
|||
uint32_t numDirectorySector; |
|||
uint32_t numFATSector; |
|||
uint32_t firstDirectorySectorLocation; |
|||
uint32_t transactionSignatureNumber; |
|||
uint32_t miniStreamCutoffSize; |
|||
uint32_t firstMiniFATSectorLocation; |
|||
uint32_t numMiniFATSector; |
|||
uint32_t firstDIFATSectorLocation; |
|||
uint32_t numDIFATSector; |
|||
uint32_t headerDIFAT[109]; |
|||
}; |
|||
|
|||
struct COMPOUND_FILE_ENTRY |
|||
{ |
|||
uint16_t name[32]; |
|||
uint16_t nameLen; |
|||
uint8_t type; |
|||
uint8_t colorFlag; |
|||
uint32_t leftSiblingID; // Note that it's actually the left/right child in the RB-tree. |
|||
uint32_t rightSiblingID; // So entry.leftSibling.rightSibling does NOT go back to entry. |
|||
uint32_t childID; |
|||
unsigned char clsid[16]; |
|||
uint32_t stateBits; |
|||
uint64_t creationTime; |
|||
uint64_t modifiedTime; |
|||
uint32_t startSectorLocation; |
|||
uint64_t size; |
|||
}; |
|||
|
|||
struct PROPERTY_SET_STREAM_HDR |
|||
{ |
|||
unsigned char byteOrder[2]; |
|||
uint16_t version; |
|||
uint32_t systemIdentifier; |
|||
unsigned char clsid[16]; |
|||
uint32_t numPropertySets; |
|||
struct |
|||
{ |
|||
char fmtid[16]; |
|||
uint32_t offset; |
|||
} propertySetInfo[1]; |
|||
}; |
|||
|
|||
struct PROPERTY_SET_HDR |
|||
{ |
|||
uint32_t size; |
|||
uint32_t NumProperties; |
|||
struct |
|||
{ |
|||
uint32_t id; |
|||
uint32_t offset; |
|||
} propertyIdentifierAndOffset[1]; |
|||
}; |
|||
|
|||
#pragma pack(pop) |
|||
|
|||
const size_t MAXREGSECT = 0xFFFFFFFA; |
|||
|
|||
struct helper |
|||
{ |
|||
static uint32_t ParseUint32(const void* buffer) |
|||
{ |
|||
return *static_cast<const uint32_t*>(buffer); |
|||
} |
|||
}; |
|||
|
|||
typedef std::basic_string<uint16_t> utf16string; |
|||
typedef std::function<void(const COMPOUND_FILE_ENTRY*, const utf16string& dir, int level)> |
|||
EnumFilesCallback; |
|||
|
|||
class CompoundFileReader |
|||
{ |
|||
public: |
|||
CompoundFileReader(const void* buffer, size_t len) |
|||
: m_buffer(static_cast<const unsigned char*>(buffer)) |
|||
, m_bufferLen(len) |
|||
, m_hdr(static_cast<const COMPOUND_FILE_HDR*>(buffer)) |
|||
, m_sectorSize(512) |
|||
, m_minisectorSize(64) |
|||
, m_miniStreamStartSector(0) |
|||
{ |
|||
if (buffer == NULL || len == 0) throw std::invalid_argument(""); |
|||
|
|||
if (m_bufferLen < sizeof(*m_hdr) || |
|||
memcmp(m_hdr->signature, "\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1", 8) != 0) |
|||
{ |
|||
throw WrongFormat(); |
|||
} |
|||
|
|||
m_sectorSize = m_hdr->majorVersion == 3 ? 512 : 4096; |
|||
|
|||
// The file must contains at least 3 sectors |
|||
if (m_bufferLen < m_sectorSize * 3) throw FileCorrupted(); |
|||
|
|||
const COMPOUND_FILE_ENTRY* root = GetEntry(0); |
|||
if (root == NULL) throw FileCorrupted(); |
|||
|
|||
m_miniStreamStartSector = root->startSectorLocation; |
|||
} |
|||
|
|||
/// Get entry (directory or file) by its ID. |
|||
/// Pass "0" to get the root directory entry. -- This is the start point to navigate the compound file. |
|||
/// Use the returned object to access child entries. |
|||
const COMPOUND_FILE_ENTRY* GetEntry(size_t entryID) const |
|||
{ |
|||
if (entryID == 0xFFFFFFFF) |
|||
{ |
|||
return NULL; |
|||
} |
|||
|
|||
if (m_bufferLen / sizeof(COMPOUND_FILE_ENTRY) <= entryID) |
|||
{ |
|||
throw std::invalid_argument(""); |
|||
} |
|||
|
|||
size_t sector = 0; |
|||
size_t offset = 0; |
|||
LocateFinalSector(m_hdr->firstDirectorySectorLocation, entryID * sizeof(COMPOUND_FILE_ENTRY), §or, &offset); |
|||
return reinterpret_cast<const COMPOUND_FILE_ENTRY*>(SectorOffsetToAddress(sector, offset)); |
|||
} |
|||
|
|||
const COMPOUND_FILE_ENTRY* GetRootEntry() const |
|||
{ |
|||
return GetEntry(0); |
|||
} |
|||
|
|||
const COMPOUND_FILE_HDR* GetFileInfo() const |
|||
{ |
|||
return m_hdr; |
|||
} |
|||
|
|||
/// Get file(stream) data start with "offset". |
|||
/// The buffer must have enough space to store "len" bytes. Typically "len" is derived by the steam length. |
|||
void ReadFile(const COMPOUND_FILE_ENTRY* entry, size_t offset, char* buffer, size_t len) const |
|||
{ |
|||
if (entry->size < offset || entry->size - offset < len) throw std::invalid_argument(""); |
|||
|
|||
if (entry->size < m_hdr->miniStreamCutoffSize) |
|||
{ |
|||
ReadMiniStream(entry->startSectorLocation, offset, buffer, len); |
|||
} |
|||
else |
|||
{ |
|||
ReadStream(entry->startSectorLocation, offset, buffer, len); |
|||
} |
|||
} |
|||
|
|||
bool IsPropertyStream(const COMPOUND_FILE_ENTRY* entry) const |
|||
{ |
|||
// defined in [MS-OLEPS] 2.23 "Property Set Stream and Storage Names" |
|||
return entry->name[0] == 5; |
|||
} |
|||
|
|||
bool IsStream(const COMPOUND_FILE_ENTRY* entry) const |
|||
{ |
|||
return entry->type == 2; |
|||
} |
|||
|
|||
void EnumFiles(const COMPOUND_FILE_ENTRY* entry, int maxLevel, EnumFilesCallback callback) const |
|||
{ |
|||
utf16string dir; |
|||
EnumNodes(GetEntry(entry->childID), 0, maxLevel, dir, callback); |
|||
} |
|||
|
|||
private: |
|||
|
|||
// Enum entries with same level, including 'entry' itself |
|||
void EnumNodes(const COMPOUND_FILE_ENTRY* entry, int currentLevel, int maxLevel, |
|||
const utf16string& dir, EnumFilesCallback callback) const |
|||
{ |
|||
if (maxLevel > 0 && currentLevel >= maxLevel) |
|||
return; |
|||
if (entry == nullptr) |
|||
return; |
|||
|
|||
callback(entry, dir, currentLevel + 1); |
|||
|
|||
const COMPOUND_FILE_ENTRY* child = GetEntry(entry->childID); |
|||
if (child != nullptr) |
|||
{ |
|||
utf16string newDir = dir; |
|||
if (dir.length() != 0) |
|||
newDir.append(1, '\n'); |
|||
newDir.append(entry->name, entry->nameLen / 2); |
|||
EnumNodes(GetEntry(entry->childID), currentLevel + 1, maxLevel, newDir, callback); |
|||
} |
|||
|
|||
EnumNodes(GetEntry(entry->leftSiblingID), currentLevel, maxLevel, dir, callback); |
|||
EnumNodes(GetEntry(entry->rightSiblingID), currentLevel, maxLevel, dir, callback); |
|||
} |
|||
|
|||
void ReadStream(size_t sector, size_t offset, char* buffer, size_t len) const |
|||
{ |
|||
LocateFinalSector(sector, offset, §or, &offset); |
|||
|
|||
// copy as many as possible in each step |
|||
// copylen typically iterate as: m_sectorSize - offset --> m_sectorSize --> m_sectorSize --> ... --> remaining |
|||
while (len > 0) |
|||
{ |
|||
const unsigned char* src = SectorOffsetToAddress(sector, offset); |
|||
size_t copylen = std::min(len, m_sectorSize - offset); |
|||
if (m_buffer + m_bufferLen < src + copylen) throw FileCorrupted(); |
|||
|
|||
memcpy(buffer, src, copylen); |
|||
buffer += copylen; |
|||
len -= copylen; |
|||
sector = GetNextSector(sector); |
|||
offset = 0; |
|||
} |
|||
} |
|||
|
|||
// Same logic as "ReadStream" except that use MiniStream functions instead |
|||
void ReadMiniStream(size_t sector, size_t offset, char* buffer, size_t len) const |
|||
{ |
|||
LocateFinalMiniSector(sector, offset, §or, &offset); |
|||
|
|||
// copy as many as possible in each step |
|||
// copylen typically iterate as: m_sectorSize - offset --> m_sectorSize --> m_sectorSize --> ... --> remaining |
|||
while (len > 0) |
|||
{ |
|||
const unsigned char* src = MiniSectorOffsetToAddress(sector, offset); |
|||
size_t copylen = std::min(len, m_minisectorSize - offset); |
|||
if (m_buffer + m_bufferLen < src + copylen) throw FileCorrupted(); |
|||
|
|||
memcpy(buffer, src, copylen); |
|||
buffer += copylen; |
|||
len -= copylen; |
|||
sector = GetNextMiniSector(sector); |
|||
offset = 0; |
|||
} |
|||
} |
|||
|
|||
size_t GetNextSector(size_t sector) const |
|||
{ |
|||
// lookup FAT |
|||
size_t entriesPerSector = m_sectorSize / 4; |
|||
size_t fatSectorNumber = sector / entriesPerSector; |
|||
size_t fatSectorLocation = GetFATSectorLocation(fatSectorNumber); |
|||
return helper::ParseUint32(SectorOffsetToAddress(fatSectorLocation, sector % entriesPerSector * 4)); |
|||
} |
|||
|
|||
size_t GetNextMiniSector(size_t miniSector) const |
|||
{ |
|||
size_t sector, offset; |
|||
LocateFinalSector(m_hdr->firstMiniFATSectorLocation, miniSector * 4, §or, &offset); |
|||
return helper::ParseUint32(SectorOffsetToAddress(sector, offset)); |
|||
} |
|||
|
|||
// Get absolute address from sector and offset. |
|||
const unsigned char* SectorOffsetToAddress(size_t sector, size_t offset) const |
|||
{ |
|||
if (sector >= MAXREGSECT || |
|||
offset >= m_sectorSize || |
|||
m_bufferLen <= static_cast<uint64_t>(m_sectorSize) * sector + m_sectorSize + offset) |
|||
{ |
|||
throw FileCorrupted(); |
|||
} |
|||
|
|||
return m_buffer + m_sectorSize + m_sectorSize * sector + offset; |
|||
} |
|||
|
|||
const unsigned char* MiniSectorOffsetToAddress(size_t sector, size_t offset) const |
|||
{ |
|||
if (sector >= MAXREGSECT || |
|||
offset >= m_minisectorSize || |
|||
m_bufferLen <= static_cast<uint64_t>(m_minisectorSize) * sector + offset) |
|||
{ |
|||
throw FileCorrupted(); |
|||
} |
|||
|
|||
|
|||
LocateFinalSector(m_miniStreamStartSector, sector * m_minisectorSize + offset, §or, &offset); |
|||
return SectorOffsetToAddress(sector, offset); |
|||
} |
|||
|
|||
// Locate the final sector/offset when original offset expands multiple sectors |
|||
void LocateFinalSector(size_t sector, size_t offset, size_t* finalSector, size_t* finalOffset) const |
|||
{ |
|||
while (offset >= m_sectorSize) |
|||
{ |
|||
offset -= m_sectorSize; |
|||
sector = GetNextSector(sector); |
|||
} |
|||
*finalSector = sector; |
|||
*finalOffset = offset; |
|||
} |
|||
|
|||
void LocateFinalMiniSector(size_t sector, size_t offset, size_t* finalSector, size_t* finalOffset) const |
|||
{ |
|||
while (offset >= m_minisectorSize) |
|||
{ |
|||
offset -= m_minisectorSize; |
|||
sector = GetNextMiniSector(sector); |
|||
} |
|||
*finalSector = sector; |
|||
*finalOffset = offset; |
|||
} |
|||
|
|||
size_t GetFATSectorLocation(size_t fatSectorNumber) const |
|||
{ |
|||
if (fatSectorNumber < 109) |
|||
{ |
|||
return m_hdr->headerDIFAT[fatSectorNumber]; |
|||
} |
|||
else |
|||
{ |
|||
fatSectorNumber -= 109; |
|||
size_t entriesPerSector = m_sectorSize / 4 - 1; |
|||
size_t difatSectorLocation = m_hdr->firstDIFATSectorLocation; |
|||
while (fatSectorNumber >= entriesPerSector) |
|||
{ |
|||
fatSectorNumber -= entriesPerSector; |
|||
const unsigned char* addr = SectorOffsetToAddress(difatSectorLocation, m_sectorSize - 4); |
|||
difatSectorLocation = helper::ParseUint32(addr); |
|||
} |
|||
return helper::ParseUint32(SectorOffsetToAddress(difatSectorLocation, fatSectorNumber * 4)); |
|||
} |
|||
} |
|||
|
|||
private: |
|||
const unsigned char * m_buffer; |
|||
size_t m_bufferLen; |
|||
|
|||
const COMPOUND_FILE_HDR* m_hdr; |
|||
size_t m_sectorSize; |
|||
size_t m_minisectorSize; |
|||
size_t m_miniStreamStartSector; |
|||
}; |
|||
|
|||
class PropertySet |
|||
{ |
|||
public: |
|||
PropertySet(const void* buffer, size_t len, const char* fmtid) |
|||
: m_buffer(static_cast<const unsigned char*>(buffer)) |
|||
, m_bufferLen(len) |
|||
, m_hdr(reinterpret_cast<const PROPERTY_SET_HDR*>(buffer)) |
|||
, m_fmtid(fmtid) |
|||
{ |
|||
if (m_bufferLen < sizeof(*m_hdr) || |
|||
m_bufferLen < sizeof(*m_hdr) + (m_hdr->NumProperties - 1) * sizeof(m_hdr->propertyIdentifierAndOffset[0])) |
|||
{ |
|||
throw FileCorrupted(); |
|||
} |
|||
} |
|||
|
|||
/// return the string property in UTF-16 format |
|||
const uint16_t* GetStringProperty(uint32_t propertyID) |
|||
{ |
|||
for (uint32_t i = 0; i < m_hdr->NumProperties; i++) |
|||
{ |
|||
if (m_hdr->propertyIdentifierAndOffset[i].id == propertyID) |
|||
{ |
|||
uint32_t offset = m_hdr->propertyIdentifierAndOffset[i].offset; |
|||
if (m_bufferLen < offset + 8) throw FileCorrupted(); |
|||
uint32_t stringLengthInChar = helper::ParseUint32(m_buffer + offset + 4); |
|||
if (m_bufferLen < offset + 8 + stringLengthInChar*2) throw FileCorrupted(); |
|||
return reinterpret_cast<const uint16_t*>(m_buffer + offset + 8); |
|||
} |
|||
} |
|||
|
|||
return NULL; |
|||
} |
|||
|
|||
/// Note: Getting property of types other than "string" is not implemented yet. |
|||
/// However most other types are simpler than string so can be easily added. see [MS-OLEPS] |
|||
|
|||
const char* GetFmtID() |
|||
{ |
|||
return m_fmtid; |
|||
} |
|||
|
|||
private: |
|||
const unsigned char* m_buffer; |
|||
size_t m_bufferLen; |
|||
const PROPERTY_SET_HDR* m_hdr; |
|||
const char* m_fmtid; // 16 bytes |
|||
}; |
|||
|
|||
class PropertySetStream |
|||
{ |
|||
public: |
|||
PropertySetStream(const void* buffer, size_t len) |
|||
: m_buffer(static_cast<const unsigned char*>(buffer)) |
|||
, m_bufferLen(len) |
|||
, m_hdr(reinterpret_cast<const PROPERTY_SET_STREAM_HDR*>(buffer)) |
|||
{ |
|||
if (m_bufferLen < sizeof(*m_hdr) || |
|||
m_bufferLen < sizeof(*m_hdr) + (m_hdr->numPropertySets - 1) * sizeof(m_hdr->propertySetInfo[0])) |
|||
{ |
|||
throw FileCorrupted(); |
|||
} |
|||
} |
|||
|
|||
size_t GetPropertySetCount() |
|||
{ |
|||
return m_hdr->numPropertySets; |
|||
} |
|||
|
|||
PropertySet GetPropertySet(size_t index) |
|||
{ |
|||
if (index >= GetPropertySetCount()) throw FileCorrupted(); |
|||
uint32_t offset = m_hdr->propertySetInfo[index].offset; |
|||
if (m_bufferLen < offset + 4) throw FileCorrupted(); |
|||
uint32_t size = helper::ParseUint32(m_buffer + offset); |
|||
if (m_bufferLen < offset + size) throw FileCorrupted(); |
|||
return PropertySet(m_buffer + offset, size, m_hdr->propertySetInfo[index].fmtid); |
|||
} |
|||
|
|||
private: |
|||
const unsigned char * m_buffer; |
|||
size_t m_bufferLen; |
|||
const PROPERTY_SET_STREAM_HDR* m_hdr; |
|||
}; |
|||
|
|||
} |
@ -0,0 +1,137 @@ |
|||
#pragma once |
|||
|
|||
#include <stdint.h> |
|||
#include <string> |
|||
|
|||
template <typename T> |
|||
static bool GetNextCodePointFromUTF16z(const T* u16, size_t* pos, uint32_t* cp) |
|||
{ |
|||
*cp = static_cast<uint32_t>(u16[*pos]); |
|||
if (*cp == 0) |
|||
return false; |
|||
|
|||
(*pos)++; |
|||
if ((*cp & 0xFC00) == 0xD800) |
|||
{ |
|||
uint16_t cp2 = static_cast<uint16_t>(u16[*pos]); |
|||
if ((cp2 & 0xFC00) == 0xDC00) |
|||
{ |
|||
(*pos)++; |
|||
*cp = (*cp << 10) + cp2 - 0x35FDC00; |
|||
} |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
template <typename T> |
|||
static bool GetNextCodePointFromUTF16(const T* u16, size_t len, size_t* pos, uint32_t* cp) |
|||
{ |
|||
if (len == 0) |
|||
return GetNextCodePointFromUTF16z(u16, pos, cp); |
|||
|
|||
if (*pos >= len) |
|||
return false; |
|||
|
|||
*cp = static_cast<uint32_t>(u16[*pos]); |
|||
(*pos)++; |
|||
if ((*cp & 0xFC00) == 0xD800) |
|||
{ |
|||
if (*pos < len) |
|||
{ |
|||
uint16_t cp2 = static_cast<uint16_t>(u16[*pos]); |
|||
if ((cp2 & 0xFC00) == 0xDC00) |
|||
{ |
|||
(*pos)++; |
|||
*cp = (*cp << 10) + cp2 - 0x35FDC00; |
|||
} |
|||
} |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
static int CodePointToUTF8(uint32_t cp, uint32_t* c1, uint32_t* c2, uint32_t* c3, uint32_t* c4) |
|||
{ |
|||
if (cp < 0x80) |
|||
{ |
|||
*c1 = cp; |
|||
return 1; |
|||
} |
|||
else if (cp <= 0x7FF) |
|||
{ |
|||
*c1 = (cp >> 6) + 0xC0; |
|||
*c2 = (cp & 0x3F) + 0x80; |
|||
return 2; |
|||
} |
|||
else if (cp <= 0xFFFF) |
|||
{ |
|||
*c1 = (cp >> 12) + 0xE0; |
|||
*c2 = ((cp >> 6) & 0x3F) + 0x80; |
|||
*c3 = (cp & 0x3F) + 0x80; |
|||
return 3; |
|||
} |
|||
else if (cp <= 0x10FFFF) |
|||
{ |
|||
*c1 = (cp >> 18) + 0xF0; |
|||
*c2 = ((cp >> 12) & 0x3F) + 0x80; |
|||
*c3 = ((cp >> 6) & 0x3F) + 0x80; |
|||
*c4 = (cp & 0x3F) + 0x80; |
|||
return 4; |
|||
} |
|||
return 0; |
|||
} |
|||
|
|||
template <typename T> |
|||
std::string UTF16ToUTF8(const T* u16, size_t len = 0) |
|||
{ |
|||
std::string u8; |
|||
uint32_t cp; |
|||
size_t pos = 0; |
|||
while (GetNextCodePointFromUTF16(u16, len, &pos, &cp)) |
|||
{ |
|||
uint32_t c[4]; |
|||
int count = CodePointToUTF8(cp, c, c+1, c+2, c+3); |
|||
for (int i = 0; i < count; i++) |
|||
{ |
|||
u8 += static_cast<char>(c[i]); |
|||
} |
|||
} |
|||
return u8; |
|||
} |
|||
|
|||
template <typename T> |
|||
std::wstring UTF16ToWstring(const T* u16, size_t len = 0) |
|||
{ |
|||
std::wstring ret; |
|||
#ifdef _MSC_VER |
|||
while (*u16) ret += *u16++; |
|||
#else |
|||
uint32_t cp; |
|||
size_t pos = 0; |
|||
while (GetNextCodePointFromUTF16(u16, len, &pos, &cp)) |
|||
{ |
|||
ret += cp; |
|||
} |
|||
#endif |
|||
return ret; |
|||
} |
|||
|
|||
template <typename T> |
|||
std::string WstringToUTF8(const T* wstr) |
|||
{ |
|||
#ifdef _MSC_VER |
|||
return UTF16ToUTF8(wstr); |
|||
#else |
|||
std::string u8; |
|||
uint32_t cp; |
|||
while ((cp = *wstr++) != 0) |
|||
{ |
|||
uint32_t c[4]; |
|||
int count = CodePointToUTF8(cp, c, c+1, c+2, c+3); |
|||
for (int i = 0; i < count; i++) |
|||
{ |
|||
u8 += static_cast<char>(c[i]); |
|||
} |
|||
} |
|||
return u8; |
|||
#endif |
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue