Use Record handling for parsing
Now, we know how big a record is, and should be able to parse all boards without missing bytes?
This commit is contained in:
parent
78dfbca9a3
commit
b707c84b62
|
@ -245,6 +245,20 @@ wxString PCadPcbFileWildcard()
|
||||||
return _( "P-Cad 200x ASCII PCB files" ) + AddFileExtListToFilter( { "pcb" } );
|
return _( "P-Cad 200x ASCII PCB files" ) + AddFileExtListToFilter( { "pcb" } );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wxString AltiumDesignerPcbFileWildcard()
|
||||||
|
{
|
||||||
|
return _( "Altium Designer PCB files" ) + AddFileExtListToFilter( { "PcbDoc" } );
|
||||||
|
}
|
||||||
|
|
||||||
|
wxString AltiumCircuitStudioPcbFileWildcard()
|
||||||
|
{
|
||||||
|
return _( "Altium Circuit Studio PCB files" ) + AddFileExtListToFilter( { "CSPcbDoc" } );
|
||||||
|
}
|
||||||
|
|
||||||
|
wxString AltiumCircuitMakerPcbFileWildcard()
|
||||||
|
{
|
||||||
|
return _( "Altium Circuit Maker PCB files" ) + AddFileExtListToFilter( { "CMPcbDoc" } );
|
||||||
|
}
|
||||||
|
|
||||||
wxString PcbFileWildcard()
|
wxString PcbFileWildcard()
|
||||||
{
|
{
|
||||||
|
|
|
@ -88,6 +88,7 @@ set_target_properties( cvpcb_kiface PROPERTIES
|
||||||
target_link_libraries( cvpcb_kiface
|
target_link_libraries( cvpcb_kiface
|
||||||
pcbcommon
|
pcbcommon
|
||||||
pcad2kicadpcb
|
pcad2kicadpcb
|
||||||
|
altium2kicadpcb
|
||||||
3d-viewer
|
3d-viewer
|
||||||
gal
|
gal
|
||||||
common
|
common
|
||||||
|
|
|
@ -188,6 +188,9 @@ extern wxString EaglePcbFileWildcard();
|
||||||
extern wxString EagleSchematicFileWildcard();
|
extern wxString EagleSchematicFileWildcard();
|
||||||
extern wxString EagleFilesWildcard();
|
extern wxString EagleFilesWildcard();
|
||||||
extern wxString PCadPcbFileWildcard();
|
extern wxString PCadPcbFileWildcard();
|
||||||
|
extern wxString AltiumDesignerPcbFileWildcard();
|
||||||
|
extern wxString AltiumCircuitStudioPcbFileWildcard();
|
||||||
|
extern wxString AltiumCircuitMakerPcbFileWildcard();
|
||||||
extern wxString PdfFileWildcard();
|
extern wxString PdfFileWildcard();
|
||||||
extern wxString PSFileWildcard();
|
extern wxString PSFileWildcard();
|
||||||
extern wxString MacrosFileWildcard();
|
extern wxString MacrosFileWildcard();
|
||||||
|
|
|
@ -569,6 +569,7 @@ endif()
|
||||||
|
|
||||||
|
|
||||||
add_subdirectory( pcad2kicadpcb_plugin )
|
add_subdirectory( pcad2kicadpcb_plugin )
|
||||||
|
add_subdirectory( altium2kicadpcb_plugin )
|
||||||
|
|
||||||
if( BUILD_GITHUB_PLUGIN )
|
if( BUILD_GITHUB_PLUGIN )
|
||||||
add_subdirectory( github )
|
add_subdirectory( github )
|
||||||
|
@ -666,6 +667,7 @@ set( PCBNEW_KIFACE_LIBRARIES
|
||||||
pcbcommon
|
pcbcommon
|
||||||
pnsrouter
|
pnsrouter
|
||||||
pcad2kicadpcb
|
pcad2kicadpcb
|
||||||
|
altium2kicadpcb
|
||||||
common
|
common
|
||||||
gal
|
gal
|
||||||
dxflib_qcad
|
dxflib_qcad
|
||||||
|
|
|
@ -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
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
|
@ -34,6 +34,7 @@
|
||||||
|
|
||||||
class D_PAD;
|
class D_PAD;
|
||||||
class TEXTE_MODULE;
|
class TEXTE_MODULE;
|
||||||
|
class ZONE_CONTAINER;
|
||||||
|
|
||||||
typedef std::map<wxString, MODULE*> MODULE_MAP;
|
typedef std::map<wxString, MODULE*> MODULE_MAP;
|
||||||
typedef std::vector<ZONE_CONTAINER*> ZONES;
|
typedef std::vector<ZONE_CONTAINER*> ZONES;
|
||||||
|
|
|
@ -76,6 +76,8 @@ bool AskLoadBoardFileName( wxWindow* aParent, int* aCtl, wxString* aFileName, bo
|
||||||
// load a BOARD. User may occasionally use the wrong plugin to load a
|
// load a BOARD. User may occasionally use the wrong plugin to load a
|
||||||
// *.brd file (since both legacy and eagle use *.brd extension),
|
// *.brd file (since both legacy and eagle use *.brd extension),
|
||||||
// but eventually *.kicad_pcb will be more common than legacy *.brd files.
|
// but eventually *.kicad_pcb will be more common than legacy *.brd files.
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
static const struct
|
static const struct
|
||||||
{
|
{
|
||||||
const wxString& filter;
|
const wxString& filter;
|
||||||
|
@ -84,9 +86,13 @@ bool AskLoadBoardFileName( wxWindow* aParent, int* aCtl, wxString* aFileName, bo
|
||||||
{
|
{
|
||||||
{ PcbFileWildcard(), IO_MGR::KICAD_SEXP }, // Current Kicad board files
|
{ PcbFileWildcard(), IO_MGR::KICAD_SEXP }, // Current Kicad board files
|
||||||
{ LegacyPcbFileWildcard(), IO_MGR::LEGACY }, // Old Kicad board files
|
{ LegacyPcbFileWildcard(), IO_MGR::LEGACY }, // Old Kicad board files
|
||||||
{ EaglePcbFileWildcard(), IO_MGR::EAGLE }, // Import board files
|
{ EaglePcbFileWildcard(), IO_MGR::EAGLE }, // Import Eagle board files
|
||||||
{ PCadPcbFileWildcard(), IO_MGR::PCAD }, // Import board files
|
{ PCadPcbFileWildcard(), IO_MGR::PCAD }, // Import PCAD board files
|
||||||
|
{ AltiumDesignerPcbFileWildcard(), IO_MGR::ALTIUM_DESIGNER }, // Import Altium Designer board files
|
||||||
|
{ AltiumCircuitStudioPcbFileWildcard(), IO_MGR::ALTIUM_CIRCUIT_STUDIO }, // Import Altium Circuit Studio board files
|
||||||
|
{ AltiumCircuitMakerPcbFileWildcard(), IO_MGR::ALTIUM_CIRCUIT_MAKER }, // Import Altium Circuit Maker board files
|
||||||
};
|
};
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
wxFileName fileName( *aFileName );
|
wxFileName fileName( *aFileName );
|
||||||
wxString fileFilters;
|
wxString fileFilters;
|
||||||
|
@ -357,7 +363,7 @@ IO_MGR::PCB_FILE_T plugin_type( const wxString& aFileName, int aCtl )
|
||||||
|
|
||||||
wxFileName fn = aFileName;
|
wxFileName fn = aFileName;
|
||||||
|
|
||||||
// Note: file extensions are expected to be in ower case.
|
// Note: file extensions are expected to be in lower case.
|
||||||
// This is not always true, especially when importing files, so the string
|
// This is not always true, especially when importing files, so the string
|
||||||
// comparisons are case insensitive to try to find the suitable plugin.
|
// comparisons are case insensitive to try to find the suitable plugin.
|
||||||
|
|
||||||
|
@ -370,6 +376,18 @@ IO_MGR::PCB_FILE_T plugin_type( const wxString& aFileName, int aCtl )
|
||||||
{
|
{
|
||||||
pluginType = IO_MGR::PCAD;
|
pluginType = IO_MGR::PCAD;
|
||||||
}
|
}
|
||||||
|
else if( fn.GetExt().CmpNoCase( IO_MGR::GetFileExtension( IO_MGR::ALTIUM_DESIGNER ) ) == 0 )
|
||||||
|
{
|
||||||
|
pluginType = IO_MGR::ALTIUM_DESIGNER;
|
||||||
|
}
|
||||||
|
else if( fn.GetExt().CmpNoCase( IO_MGR::GetFileExtension( IO_MGR::ALTIUM_CIRCUIT_STUDIO ) ) == 0 )
|
||||||
|
{
|
||||||
|
pluginType = IO_MGR::ALTIUM_CIRCUIT_STUDIO;
|
||||||
|
}
|
||||||
|
else if( fn.GetExt().CmpNoCase( IO_MGR::GetFileExtension( IO_MGR::ALTIUM_CIRCUIT_MAKER ) ) == 0 )
|
||||||
|
{
|
||||||
|
pluginType = IO_MGR::ALTIUM_CIRCUIT_MAKER;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
pluginType = IO_MGR::KICAD_SEXP;
|
pluginType = IO_MGR::KICAD_SEXP;
|
||||||
|
|
|
@ -25,13 +25,16 @@
|
||||||
#include <wx/filename.h>
|
#include <wx/filename.h>
|
||||||
#include <wx/uri.h>
|
#include <wx/uri.h>
|
||||||
|
|
||||||
#include <io_mgr.h>
|
#include <altium2kicadpcb_plugin/altium_circuit_maker_plugin.h>
|
||||||
#include <legacy_plugin.h>
|
#include <altium2kicadpcb_plugin/altium_circuit_studio_plugin.h>
|
||||||
#include <kicad_plugin.h>
|
#include <altium2kicadpcb_plugin/altium_designer_plugin.h>
|
||||||
#include <eagle_plugin.h>
|
|
||||||
#include <pcad2kicadpcb_plugin/pcad_plugin.h>
|
|
||||||
#include <gpcb_plugin.h>
|
|
||||||
#include <config.h>
|
#include <config.h>
|
||||||
|
#include <eagle_plugin.h>
|
||||||
|
#include <gpcb_plugin.h>
|
||||||
|
#include <io_mgr.h>
|
||||||
|
#include <kicad_plugin.h>
|
||||||
|
#include <legacy_plugin.h>
|
||||||
|
#include <pcad2kicadpcb_plugin/pcad_plugin.h>
|
||||||
|
|
||||||
#if defined(BUILD_GITHUB_PLUGIN)
|
#if defined(BUILD_GITHUB_PLUGIN)
|
||||||
#include <github/github_plugin.h>
|
#include <github/github_plugin.h>
|
||||||
|
@ -204,6 +207,14 @@ void IO_MGR::Save( PCB_FILE_T aFileType, const wxString& aFileName, BOARD* aBoar
|
||||||
static IO_MGR::REGISTER_PLUGIN registerEaglePlugin( IO_MGR::EAGLE, wxT("Eagle"), []() -> PLUGIN* { return new EAGLE_PLUGIN; } );
|
static IO_MGR::REGISTER_PLUGIN registerEaglePlugin( IO_MGR::EAGLE, wxT("Eagle"), []() -> PLUGIN* { return new EAGLE_PLUGIN; } );
|
||||||
static IO_MGR::REGISTER_PLUGIN registerKicadPlugin( IO_MGR::KICAD_SEXP, wxT("KiCad"), []() -> PLUGIN* { return new PCB_IO; } );
|
static IO_MGR::REGISTER_PLUGIN registerKicadPlugin( IO_MGR::KICAD_SEXP, wxT("KiCad"), []() -> PLUGIN* { return new PCB_IO; } );
|
||||||
static IO_MGR::REGISTER_PLUGIN registerPcadPlugin( IO_MGR::PCAD, wxT("P-Cad"), []() -> PLUGIN* { return new PCAD_PLUGIN; } );
|
static IO_MGR::REGISTER_PLUGIN registerPcadPlugin( IO_MGR::PCAD, wxT("P-Cad"), []() -> PLUGIN* { return new PCAD_PLUGIN; } );
|
||||||
|
static IO_MGR::REGISTER_PLUGIN registerAltiumDesignerPlugin( IO_MGR::ALTIUM_DESIGNER,
|
||||||
|
wxT( "Altium Designer" ), []() -> PLUGIN* { return new ALTIUM_DESIGNER_PLUGIN; } );
|
||||||
|
static IO_MGR::REGISTER_PLUGIN registerAltiumCircuitStudioPlugin( IO_MGR::ALTIUM_CIRCUIT_STUDIO,
|
||||||
|
wxT( "Altium Circuit Studio" ),
|
||||||
|
[]() -> PLUGIN* { return new ALTIUM_CIRCUIT_STUDIO_PLUGIN; } );
|
||||||
|
static IO_MGR::REGISTER_PLUGIN registerAltiumCircuitMakerPlugin( IO_MGR::ALTIUM_CIRCUIT_MAKER,
|
||||||
|
wxT( "Altium Circuit Maker" ),
|
||||||
|
[]() -> PLUGIN* { return new ALTIUM_CIRCUIT_MAKER_PLUGIN; } );
|
||||||
#ifdef BUILD_GITHUB_PLUGIN
|
#ifdef BUILD_GITHUB_PLUGIN
|
||||||
static IO_MGR::REGISTER_PLUGIN registerGithubPlugin( IO_MGR::GITHUB, wxT("Github"), []() -> PLUGIN* { return new GITHUB_PLUGIN; } );
|
static IO_MGR::REGISTER_PLUGIN registerGithubPlugin( IO_MGR::GITHUB, wxT("Github"), []() -> PLUGIN* { return new GITHUB_PLUGIN; } );
|
||||||
#endif /* BUILD_GITHUB_PLUGIN */
|
#endif /* BUILD_GITHUB_PLUGIN */
|
||||||
|
|
|
@ -57,6 +57,9 @@ public:
|
||||||
KICAD_SEXP, ///< S-expression Pcbnew file format.
|
KICAD_SEXP, ///< S-expression Pcbnew file format.
|
||||||
EAGLE,
|
EAGLE,
|
||||||
PCAD,
|
PCAD,
|
||||||
|
ALTIUM_DESIGNER,
|
||||||
|
ALTIUM_CIRCUIT_STUDIO,
|
||||||
|
ALTIUM_CIRCUIT_MAKER,
|
||||||
GEDA_PCB, ///< Geda PCB file formats.
|
GEDA_PCB, ///< Geda PCB file formats.
|
||||||
|
|
||||||
//N.B. This needs to be commented out to ensure compile-type errors
|
//N.B. This needs to be commented out to ensure compile-type errors
|
||||||
|
@ -65,7 +68,6 @@ public:
|
||||||
#endif
|
#endif
|
||||||
// add your type here.
|
// add your type here.
|
||||||
|
|
||||||
// ALTIUM,
|
|
||||||
// etc.
|
// etc.
|
||||||
|
|
||||||
FILE_TYPE_NONE
|
FILE_TYPE_NONE
|
||||||
|
|
|
@ -31,7 +31,7 @@ if( BUILD_GITHUB_PLUGIN )
|
||||||
set( GITHUB_PLUGIN_LIBRARIES github_plugin )
|
set( GITHUB_PLUGIN_LIBRARIES github_plugin )
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_dependencies( pnsrouter pcbcommon pcad2kicadpcb ${GITHUB_PLUGIN_LIBRARIES} )
|
add_dependencies( pnsrouter pcbcommon pcad2kicadpcb altium2kicadpcb ${GITHUB_PLUGIN_LIBRARIES} )
|
||||||
|
|
||||||
add_executable(test_gal_pixel_alignment WIN32
|
add_executable(test_gal_pixel_alignment WIN32
|
||||||
test_gal_pixel_alignment.cpp
|
test_gal_pixel_alignment.cpp
|
||||||
|
@ -72,6 +72,7 @@ target_link_libraries( test_gal_pixel_alignment
|
||||||
pnsrouter
|
pnsrouter
|
||||||
pcbcommon
|
pcbcommon
|
||||||
pcad2kicadpcb
|
pcad2kicadpcb
|
||||||
|
altium2kicadpcb
|
||||||
bitmaps
|
bitmaps
|
||||||
3d-viewer
|
3d-viewer
|
||||||
gal
|
gal
|
||||||
|
|
|
@ -67,6 +67,7 @@ target_link_libraries( qa_pcbnew
|
||||||
pcbcommon
|
pcbcommon
|
||||||
pnsrouter
|
pnsrouter
|
||||||
pcad2kicadpcb
|
pcad2kicadpcb
|
||||||
|
altium2kicadpcb
|
||||||
gal
|
gal
|
||||||
common
|
common
|
||||||
gal
|
gal
|
||||||
|
|
|
@ -54,6 +54,7 @@ target_link_libraries( qa_pcbnew_tools
|
||||||
pcbcommon
|
pcbcommon
|
||||||
pnsrouter
|
pnsrouter
|
||||||
pcad2kicadpcb
|
pcad2kicadpcb
|
||||||
|
altium2kicadpcb
|
||||||
gal
|
gal
|
||||||
dxflib_qcad
|
dxflib_qcad
|
||||||
tinyspline_lib
|
tinyspline_lib
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
add_subdirectory( clipper )
|
add_subdirectory( clipper )
|
||||||
|
add_subdirectory( compoundfilereader )
|
||||||
add_subdirectory( dxflib_qcad )
|
add_subdirectory( dxflib_qcad )
|
||||||
add_subdirectory( libcontext )
|
add_subdirectory( libcontext )
|
||||||
add_subdirectory( markdown2html )
|
add_subdirectory( markdown2html )
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
Loading…
Reference in New Issue