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:
Thomas Pointhuber 2020-04-03 23:22:24 +00:00 committed by Ian McInerney
parent 78dfbca9a3
commit b707c84b62
32 changed files with 6089 additions and 27 deletions

View File

@ -245,6 +245,20 @@ wxString PCadPcbFileWildcard()
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()
{

View File

@ -88,6 +88,7 @@ set_target_properties( cvpcb_kiface PROPERTIES
target_link_libraries( cvpcb_kiface
pcbcommon
pcad2kicadpcb
altium2kicadpcb
3d-viewer
gal
common

View File

@ -51,15 +51,15 @@ public:
* has been a plugin written.
*/
DEFINE_ENUM_VECTOR( SCH_FILE_T,
{
SCH_LEGACY, ///< Legacy Eeschema file formats prior to s-expression.
SCH_KICAD, ///< The s-expression version of the schematic file formats.
SCH_EAGLE, ///< Autodesk Eagle file format
// Add your schematic type here.
{
SCH_LEGACY, ///< Legacy Eeschema file formats prior to s-expression.
SCH_KICAD, ///< The s-expression version of the schematic file formats.
SCH_EAGLE, ///< Autodesk Eagle file format
// Add your schematic type here.
// ALTIUM,
// etc.
} )
// ALTIUM,
// etc.
} )
/**
* Return a #SCH_PLUGIN which the caller can use to import, export, save, or load

View File

@ -188,6 +188,9 @@ extern wxString EaglePcbFileWildcard();
extern wxString EagleSchematicFileWildcard();
extern wxString EagleFilesWildcard();
extern wxString PCadPcbFileWildcard();
extern wxString AltiumDesignerPcbFileWildcard();
extern wxString AltiumCircuitStudioPcbFileWildcard();
extern wxString AltiumCircuitMakerPcbFileWildcard();
extern wxString PdfFileWildcard();
extern wxString PSFileWildcard();
extern wxString MacrosFileWildcard();

View File

@ -569,6 +569,7 @@ endif()
add_subdirectory( pcad2kicadpcb_plugin )
add_subdirectory( altium2kicadpcb_plugin )
if( BUILD_GITHUB_PLUGIN )
add_subdirectory( github )
@ -666,6 +667,7 @@ set( PCBNEW_KIFACE_LIBRARIES
pcbcommon
pnsrouter
pcad2kicadpcb
altium2kicadpcb
common
gal
dxflib_qcad

View File

@ -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>
)

View File

@ -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;
}

View File

@ -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_

View File

@ -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;
}

View File

@ -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_

View File

@ -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;
}

View File

@ -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_

View File

@ -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;
}
}

View File

@ -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

View File

@ -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

View File

@ -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" ) );
}
}

View File

@ -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

View File

@ -0,0 +1,187 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2019-2020 Thomas Pointhuber <thomas.pointhuber@gmx.at>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef ALTIUM_PCB_H
#define ALTIUM_PCB_H
#include <functional>
#include <layers_id_colors_and_visibility.h>
#include <vector>
#include <zconf.h>
#include <altium_parser_pcb.h>
enum class ALTIUM_PCB_DIR
{
FILE_HEADER,
ADVANCEDPLACEROPTIONS6,
ARCS6,
BOARD6,
BOARDREGIONS,
CLASSES6,
COMPONENTBODIES6,
COMPONENTS6,
CONNECTIONS6,
COORDINATES6,
DESIGNRULECHECKEROPTIONS6,
DIFFERENTIALPAIRS6,
DIMENSIONS6,
EMBEDDEDBOARDS6,
EMBEDDEDFONTS6,
EMBEDDEDS6,
EXTENDPRIMITIVEINFORMATION,
FILEVERSIONINFO,
FILLS6,
FROMTOS6,
MODELS,
MODELSNOEMBED,
NETS6,
PADS6,
PADVIALIBRARY,
PADVIALIBRARYCACHE,
PADVIALIBRARYLINKS,
PINSWAPOPTIONS6,
PINPAIRSSECTION,
POLYGONS6,
REGIONS6,
RULES6,
SHAPEBASEDCOMPONENTBODIES6,
SHAPEBASEDREGIONS6,
SIGNALCLASSES,
SMARTUNIONS,
TEXTS,
TEXTS6,
TEXTURES,
TRACKS6,
UNIONNAMES,
UNIQUEIDPRIMITIVEINFORMATION,
VIAS6,
WIDESTRINGS6
};
class BOARD;
class MODULE;
class ZONE_CONTAINER;
/**
* Helper method which opens a Altium Board File and parses it.
*
* @param aBoard board the pcb should be appended to
* @param aFileName file name of board file
* @param aFileMapping mapping how altium stream names are mapped
*/
void ParseAltiumPcb( BOARD* aBoard, const wxString& aFileName,
const std::map<ALTIUM_PCB_DIR, std::string>& aFileMapping );
namespace CFB
{
class CompoundFileReader;
struct COMPOUND_FILE_ENTRY;
} // namespace CFB
// type declaration required for a helper method
class ALTIUM_PCB;
typedef std::function<void( const CFB::CompoundFileReader&, const CFB::COMPOUND_FILE_ENTRY* )>
PARSE_FUNCTION_POINTER_fp;
class ALTIUM_PCB
{
public:
explicit ALTIUM_PCB( BOARD* aBoard );
~ALTIUM_PCB();
void Parse( const CFB::CompoundFileReader& aReader,
const std::map<ALTIUM_PCB_DIR, std::string>& aFileMapping );
private:
PCB_LAYER_ID GetKicadLayer( ALTIUM_LAYER aAltiumLayer ) const;
int GetNetCode( uint16_t aId ) const;
const ARULE6* GetRule( ALTIUM_RULE_KIND aKind, const wxString& aName ) const;
const ARULE6* GetRuleDefault( ALTIUM_RULE_KIND aKind ) const;
void ParseFileHeader(
const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry );
// Text Format
void ParseBoard6Data(
const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry );
void ParseClasses6Data(
const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry );
void ParseComponents6Data(
const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry );
void ParseDimensions6Data(
const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry );
void ParseNets6Data(
const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry );
void ParsePolygons6Data(
const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry );
void ParseRules6Data(
const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry );
// Binary Format
void ParseArcs6Data(
const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry );
void ParsePads6Data(
const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry );
void ParseVias6Data(
const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry );
void ParseTracks6Data(
const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry );
void ParseTexts6Data(
const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry );
void ParseFills6Data(
const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry );
void ParseBoardRegionsData(
const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry );
void ParseShapeBasedRegions6Data(
const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry );
void ParseRegions6Data(
const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry );
// Helper Functions
void HelperParseDimensions6Linear( const ADIMENSION6& aElem );
void HelperParseDimensions6Leader( const ADIMENSION6& aElem );
void HelperParseDimensions6Datum( const ADIMENSION6& aElem );
void HelperParseDimensions6Center( const ADIMENSION6& aElem );
void HelperCreateBoardOutline( const std::vector<ALTIUM_VERTICE>& aVertices );
BOARD* m_board;
std::vector<MODULE*> m_components;
std::vector<ZONE_CONTAINER*> m_polygons;
size_t m_num_nets;
std::map<ALTIUM_LAYER, PCB_LAYER_ID> m_layermap; // used to correctly map copper layers
std::map<ALTIUM_RULE_KIND, std::vector<ARULE6>> m_rules;
std::map<ALTIUM_LAYER, ZONE_CONTAINER*> m_outer_plane;
};
#endif //ALTIUM_PCB_H

View File

@ -34,6 +34,7 @@
class D_PAD;
class TEXTE_MODULE;
class ZONE_CONTAINER;
typedef std::map<wxString, MODULE*> MODULE_MAP;
typedef std::vector<ZONE_CONTAINER*> ZONES;

View File

@ -76,17 +76,23 @@ bool AskLoadBoardFileName( wxWindow* aParent, int* aCtl, wxString* aFileName, bo
// load a BOARD. User may occasionally use the wrong plugin to load a
// *.brd file (since both legacy and eagle use *.brd extension),
// but eventually *.kicad_pcb will be more common than legacy *.brd files.
// clang-format off
static const struct
{
const wxString& filter;
IO_MGR::PCB_FILE_T pluginType;
} loaders[] =
{
{ PcbFileWildcard(), IO_MGR::KICAD_SEXP }, // Current Kicad board files
{ LegacyPcbFileWildcard(), IO_MGR::LEGACY }, // Old Kicad board files
{ EaglePcbFileWildcard(), IO_MGR::EAGLE }, // Import board files
{ PCadPcbFileWildcard(), IO_MGR::PCAD }, // Import board files
{ PcbFileWildcard(), IO_MGR::KICAD_SEXP }, // Current Kicad board files
{ LegacyPcbFileWildcard(), IO_MGR::LEGACY }, // Old Kicad board files
{ EaglePcbFileWildcard(), IO_MGR::EAGLE }, // Import Eagle 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 );
wxString fileFilters;
@ -357,7 +363,7 @@ IO_MGR::PCB_FILE_T plugin_type( const wxString& aFileName, int aCtl )
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
// comparisons are case insensitive to try to find the suitable plugin.
@ -366,10 +372,22 @@ IO_MGR::PCB_FILE_T plugin_type( const wxString& aFileName, int aCtl )
// both legacy and eagle share a common file extension.
pluginType = ( aCtl & KICTL_EAGLE_BRD ) ? IO_MGR::EAGLE : IO_MGR::LEGACY;
}
else if( fn.GetExt().CmpNoCase( IO_MGR::GetFileExtension( IO_MGR::PCAD ) ) == 0 )
else if( fn.GetExt().CmpNoCase( IO_MGR::GetFileExtension( IO_MGR::PCAD ) ) == 0 )
{
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
{
pluginType = IO_MGR::KICAD_SEXP;

View File

@ -25,13 +25,16 @@
#include <wx/filename.h>
#include <wx/uri.h>
#include <io_mgr.h>
#include <legacy_plugin.h>
#include <kicad_plugin.h>
#include <eagle_plugin.h>
#include <pcad2kicadpcb_plugin/pcad_plugin.h>
#include <gpcb_plugin.h>
#include <altium2kicadpcb_plugin/altium_circuit_maker_plugin.h>
#include <altium2kicadpcb_plugin/altium_circuit_studio_plugin.h>
#include <altium2kicadpcb_plugin/altium_designer_plugin.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)
#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 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 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
static IO_MGR::REGISTER_PLUGIN registerGithubPlugin( IO_MGR::GITHUB, wxT("Github"), []() -> PLUGIN* { return new GITHUB_PLUGIN; } );
#endif /* BUILD_GITHUB_PLUGIN */

View File

@ -53,19 +53,21 @@ public:
*/
enum PCB_FILE_T
{
LEGACY, ///< Legacy Pcbnew file formats prior to s-expression.
KICAD_SEXP, ///< S-expression Pcbnew file format.
LEGACY, ///< Legacy Pcbnew file formats prior to s-expression.
KICAD_SEXP, ///< S-expression Pcbnew file format.
EAGLE,
PCAD,
GEDA_PCB, ///< Geda PCB file formats.
ALTIUM_DESIGNER,
ALTIUM_CIRCUIT_STUDIO,
ALTIUM_CIRCUIT_MAKER,
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
#if defined(BUILD_GITHUB_PLUGIN)
GITHUB, ///< Read only http://github.com repo holding pretty footprints
GITHUB, ///< Read only http://github.com repo holding pretty footprints
#endif
// add your type here.
// ALTIUM,
// etc.
FILE_TYPE_NONE

View File

@ -31,7 +31,7 @@ if( BUILD_GITHUB_PLUGIN )
set( GITHUB_PLUGIN_LIBRARIES github_plugin )
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
test_gal_pixel_alignment.cpp
@ -72,6 +72,7 @@ target_link_libraries( test_gal_pixel_alignment
pnsrouter
pcbcommon
pcad2kicadpcb
altium2kicadpcb
bitmaps
3d-viewer
gal

View File

@ -67,6 +67,7 @@ target_link_libraries( qa_pcbnew
pcbcommon
pnsrouter
pcad2kicadpcb
altium2kicadpcb
gal
common
gal

View File

@ -54,6 +54,7 @@ target_link_libraries( qa_pcbnew_tools
pcbcommon
pnsrouter
pcad2kicadpcb
altium2kicadpcb
gal
dxflib_qcad
tinyspline_lib

View File

@ -22,6 +22,7 @@
#
add_subdirectory( clipper )
add_subdirectory( compoundfilereader )
add_subdirectory( dxflib_qcad )
add_subdirectory( libcontext )
add_subdirectory( markdown2html )

View File

@ -0,0 +1,3 @@
add_library( compoundfilereader INTERFACE )
target_include_directories( compoundfilereader INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} )

View File

@ -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.

View File

@ -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.

View File

@ -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), &sector, &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, &sector, &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, &sector, &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, &sector, &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, &sector, &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;
};
}

137
thirdparty/compoundfilereader/utf.h vendored Normal file
View File

@ -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
}