Altium schematic parser: Add fraction pin placement

In the schematic libaries, pin positions that are off 10mil spacing need
fractional elements stored in a different location
This commit is contained in:
Seth Hillbrand 2024-06-10 15:01:30 -07:00
parent 35d60598d5
commit 439c25fca9
3 changed files with 118 additions and 43 deletions

View File

@ -26,6 +26,7 @@
#include "altium_parser_utils.h" #include "altium_parser_utils.h"
#include <compoundfilereader.h> #include <compoundfilereader.h>
#include <charconv>
#include <ki_exception.h> #include <ki_exception.h>
#include <math/util.h> #include <math/util.h>
#include <numeric> #include <numeric>
@ -204,7 +205,7 @@ ALTIUM_COMPOUND_FILE::FindStreamSingleLevel( const CFB::COMPOUND_FILE_ENTRY* aEn
} }
std::map<wxString, const CFB::COMPOUND_FILE_ENTRY*> std::map<wxString, ALTIUM_SYMBOL_DATA>
ALTIUM_COMPOUND_FILE::GetLibSymbols( const CFB::COMPOUND_FILE_ENTRY* aStart ) const ALTIUM_COMPOUND_FILE::GetLibSymbols( const CFB::COMPOUND_FILE_ENTRY* aStart ) const
{ {
const CFB::COMPOUND_FILE_ENTRY* root = aStart ? aStart : m_reader->GetRootEntry(); const CFB::COMPOUND_FILE_ENTRY* root = aStart ? aStart : m_reader->GetRootEntry();
@ -212,7 +213,7 @@ ALTIUM_COMPOUND_FILE::GetLibSymbols( const CFB::COMPOUND_FILE_ENTRY* aStart ) co
if( !root ) if( !root )
return {}; return {};
std::map<wxString, const CFB::COMPOUND_FILE_ENTRY*> folders; std::map<wxString, ALTIUM_SYMBOL_DATA> folders;
m_reader->EnumFiles( root, 1, [&]( const CFB::COMPOUND_FILE_ENTRY* tentry, const CFB::utf16string&, int ) -> int m_reader->EnumFiles( root, 1, [&]( const CFB::COMPOUND_FILE_ENTRY* tentry, const CFB::utf16string&, int ) -> int
{ {
@ -227,7 +228,16 @@ ALTIUM_COMPOUND_FILE::GetLibSymbols( const CFB::COMPOUND_FILE_ENTRY* aStart ) co
std::wstring fileName = UTF16ToWstring( entry->name, entry->nameLen ); std::wstring fileName = UTF16ToWstring( entry->name, entry->nameLen );
if( m_reader->IsStream( entry ) && fileName == L"Data" ) if( m_reader->IsStream( entry ) && fileName == L"Data" )
folders[dirName] = entry; folders[dirName].m_symbol = entry;
if( m_reader->IsStream( entry ) && fileName == L"PinFrac" )
folders[dirName].m_pinsFrac = entry;
if( m_reader->IsStream( entry ) && fileName == L"PinWideText" )
folders[dirName].m_pinsWideText = entry;
if( m_reader->IsStream( entry ) && fileName == L"PinTextData" )
folders[dirName].m_pinsTextData = entry;
return 0; return 0;
} ); } );
@ -420,7 +430,7 @@ std::map<wxString, wxString> ALTIUM_BINARY_PARSER::ReadProperties(
// Both the 'l' and the null-byte are missing, which looks like Altium swallowed two bytes. // Both the 'l' and the null-byte are missing, which looks like Altium swallowed two bytes.
bool hasNullByte = m_pos[length - 1] == '\0'; bool hasNullByte = m_pos[length - 1] == '\0';
if( !hasNullByte ) if( !hasNullByte && !isBinary )
{ {
wxLogError( _( "Missing null byte at end of property list. Imported data might be " wxLogError( _( "Missing null byte at end of property list. Imported data might be "
"malformed or missing." ) ); "malformed or missing." ) );
@ -500,5 +510,4 @@ std::map<wxString, wxString> ALTIUM_BINARY_PARSER::ReadProperties(
} }
return kv; return kv;
} }

View File

@ -27,17 +27,17 @@
#include "altium_props_utils.h" #include "altium_props_utils.h"
#include <charconv>
#include <map> #include <map>
#include <memory> #include <memory>
#include <numeric> #include <numeric>
#include <string>
#include <stdexcept>
#include <vector>
#include <wx/mstream.h> #include <wx/mstream.h>
#include <wx/zstream.h> #include <wx/zstream.h>
#include <math/vector2d.h> #include <math/vector2d.h>
#include <vector>
#include <string>
#include <stdexcept>
namespace CFB namespace CFB
{ {
@ -53,6 +53,16 @@ struct COMPOUND_FILE_ENTRY;
std::string FormatPath( const std::vector<std::string>& aVectorPath ); std::string FormatPath( const std::vector<std::string>& aVectorPath );
class ALTIUM_SYMBOL_DATA
{
public:
const CFB::COMPOUND_FILE_ENTRY* m_symbol;
const CFB::COMPOUND_FILE_ENTRY* m_pinsFrac;
const CFB::COMPOUND_FILE_ENTRY* m_pinsWideText;
const CFB::COMPOUND_FILE_ENTRY* m_pinsTextData;
const CFB::COMPOUND_FILE_ENTRY* m_pinsSymbolLineWidth;
};
class ALTIUM_COMPOUND_FILE class ALTIUM_COMPOUND_FILE
{ {
public: public:
@ -94,7 +104,7 @@ public:
std::map<wxString, const CFB::COMPOUND_FILE_ENTRY*> EnumDir( const std::wstring& aDir ) const; std::map<wxString, const CFB::COMPOUND_FILE_ENTRY*> EnumDir( const std::wstring& aDir ) const;
std::map<wxString, const CFB::COMPOUND_FILE_ENTRY*> GetLibSymbols( const CFB::COMPOUND_FILE_ENTRY* aStart ) const; std::map<wxString, ALTIUM_SYMBOL_DATA> GetLibSymbols( const CFB::COMPOUND_FILE_ENTRY* aStart ) const;
private: private:
@ -359,7 +369,7 @@ public:
return value; return value;
} }
std::string ReadPascalString() std::string ReadShortPascalString()
{ {
uint8_t length = ReadByte(); uint8_t length = ReadByte();
@ -371,6 +381,18 @@ public:
return pascalString; return pascalString;
} }
std::string ReadFullPascalString()
{
uint32_t length = ReadInt32();
if( m_position + length > m_data.size() )
throw std::out_of_range( "ALTIUM_BINARY_READER: out of range" );
std::string pascalString( &m_data[m_position], &m_data[m_position + length] );
m_position += length;
return pascalString;
}
private: private:
const std::string& m_data; const std::string& m_data;
size_t m_position; size_t m_position;
@ -382,31 +404,28 @@ public:
ALTIUM_COMPRESSED_READER( const std::string& aData ) : ALTIUM_BINARY_READER( aData ) ALTIUM_COMPRESSED_READER( const std::string& aData ) : ALTIUM_BINARY_READER( aData )
{} {}
std::pair<int, std::string> ReadCompressedString() std::pair<int, std::string*> ReadCompressedString()
{ {
std::string result; std::string* result;
int id = -1; int id = -1;
while( true ) uint8_t byte = ReadByte();
{ if( byte != 0xD0 )
uint8_t byte = ReadByte(); throw std::runtime_error( "ALTIUM_COMPRESSED_READER: invalid compressed string" );
if( byte != 0xD0 )
throw std::runtime_error( "ALTIUM_COMPRESSED_READER: invalid compressed string" );
std::string str = ReadPascalString(); std::string str = ReadShortPascalString();
std::from_chars( str.data(), str.data() + str.size(), id );
id = std::stoi( str ); std::string data = ReadFullPascalString();
result = decompressData( data );
std::string data = ReadPascalString();
result = decompressData( data );
}
return std::make_pair( id, result ); return std::make_pair( id, result );
} }
private: private:
std::string decompressData( std::string& aData ) std::string decompressedData;
std::string* decompressData( std::string& aData )
{ {
// Create a memory input stream with the buffer // Create a memory input stream with the buffer
wxMemoryInputStream memStream( (void*) aData.data(), aData.length() ); wxMemoryInputStream memStream( (void*) aData.data(), aData.length() );
@ -414,9 +433,6 @@ private:
// Create a zlib input stream with the memory input stream // Create a zlib input stream with the memory input stream
wxZlibInputStream zStream( memStream ); wxZlibInputStream zStream( memStream );
// Prepare a string to hold decompressed data
std::string decompressedData;
// Read decompressed data from the zlib input stream // Read decompressed data from the zlib input stream
while( !zStream.Eof() ) while( !zStream.Eof() )
{ {
@ -426,7 +442,7 @@ private:
decompressedData.append( buffer, bytesRead ); decompressedData.append( buffer, bytesRead );
} }
return decompressedData; return &decompressedData;
} }
}; };

View File

@ -4278,15 +4278,54 @@ std::map<wxString,LIB_SYMBOL*> SCH_IO_ALTIUM::ParseLibFile( const ALTIUM_COMPOUN
{ {
std::map<wxString,LIB_SYMBOL*> ret; std::map<wxString,LIB_SYMBOL*> ret;
std::vector<int> fontSizes; std::vector<int> fontSizes;
struct SYMBOL_PIN_FRAC
{
int x_frac;
int y_frac;
int len_frac;
};
ParseLibHeader( aAltiumLibFile, fontSizes ); ParseLibHeader( aAltiumLibFile, fontSizes );
std::map<wxString, const CFB::COMPOUND_FILE_ENTRY*> syms = aAltiumLibFile.GetLibSymbols( nullptr ); std::map<wxString, ALTIUM_SYMBOL_DATA> syms = aAltiumLibFile.GetLibSymbols( nullptr );
for( auto& [name, entry] : syms ) for( auto& [name, entry] : syms )
{ {
ALTIUM_BINARY_PARSER reader( aAltiumLibFile, entry );
std::map<int, SYMBOL_PIN_FRAC> pinFracs;
if( entry.m_pinsFrac )
{
auto parse_binary_pin_frac =
[&]( const std::string& binaryData ) -> std::map<wxString, wxString>
{
std::map<wxString, wxString> result;
ALTIUM_COMPRESSED_READER cmpreader( binaryData );
std::pair<int, std::string*> pinFracData = cmpreader.ReadCompressedString();
ALTIUM_BINARY_READER binreader( *pinFracData.second );
SYMBOL_PIN_FRAC pinFrac;
pinFrac.x_frac = binreader.ReadInt32();
pinFrac.y_frac = binreader.ReadInt32();
pinFrac.len_frac = binreader.ReadInt32();
pinFracs.insert( { pinFracData.first, pinFrac } );
return result;
};
ALTIUM_BINARY_PARSER reader( aAltiumLibFile, entry.m_pinsFrac );
while( reader.GetRemainingBytes() > 0 )
{
reader.ReadProperties( parse_binary_pin_frac );
}
}
ALTIUM_BINARY_PARSER reader( aAltiumLibFile, entry.m_symbol );
std::vector<LIB_SYMBOL*> symbols; std::vector<LIB_SYMBOL*> symbols;
int pin_index = 0;
if( reader.GetRemainingBytes() <= 0 ) if( reader.GetRemainingBytes() <= 0 )
{ {
@ -4304,10 +4343,9 @@ std::map<wxString,LIB_SYMBOL*> SCH_IO_ALTIUM::ParseLibFile( const ALTIUM_COMPOUN
symbols = ParseLibComponent( properties ); symbols = ParseLibComponent( properties );
} }
auto handleBinaryDataLambda = auto handleBinaryPinLambda =
[]( const std::string& binaryData ) -> std::map<wxString, wxString> [&]( const std::string& binaryData ) -> std::map<wxString, wxString>
{ {
std::map<wxString, wxString> result; std::map<wxString, wxString> result;
ALTIUM_BINARY_READER binreader( binaryData ); ALTIUM_BINARY_READER binreader( binaryData );
@ -4325,7 +4363,7 @@ std::map<wxString,LIB_SYMBOL*> SCH_IO_ALTIUM::ParseLibFile( const ALTIUM_COMPOUN
result["SYMBOL_OUTEREDGE"] = wxString::Format( "%d", binreader.ReadByte() ); result["SYMBOL_OUTEREDGE"] = wxString::Format( "%d", binreader.ReadByte() );
result["SYMBOL_INNER"] = wxString::Format( "%d", binreader.ReadByte() ); result["SYMBOL_INNER"] = wxString::Format( "%d", binreader.ReadByte() );
result["SYMBOL_OUTER"] = wxString::Format( "%d", binreader.ReadByte() ); result["SYMBOL_OUTER"] = wxString::Format( "%d", binreader.ReadByte() );
result["TEXT"] = binreader.ReadPascalString(); result["TEXT"] = binreader.ReadShortPascalString();
binreader.ReadByte(); // unknown binreader.ReadByte(); // unknown
result["ELECTRICAL"] = wxString::Format( "%d", binreader.ReadByte() ); result["ELECTRICAL"] = wxString::Format( "%d", binreader.ReadByte() );
result["PINCONGLOMERATE"] = wxString::Format( "%d", binreader.ReadByte() ); result["PINCONGLOMERATE"] = wxString::Format( "%d", binreader.ReadByte() );
@ -4333,12 +4371,19 @@ std::map<wxString,LIB_SYMBOL*> SCH_IO_ALTIUM::ParseLibFile( const ALTIUM_COMPOUN
result["LOCATION.X"] = wxString::Format( "%d", binreader.ReadInt16() ); result["LOCATION.X"] = wxString::Format( "%d", binreader.ReadInt16() );
result["LOCATION.Y"] = wxString::Format( "%d", binreader.ReadInt16() ); result["LOCATION.Y"] = wxString::Format( "%d", binreader.ReadInt16() );
result["COLOR"] = wxString::Format( "%d", binreader.ReadInt32() ); result["COLOR"] = wxString::Format( "%d", binreader.ReadInt32() );
result["NAME"] = binreader.ReadPascalString(); result["NAME"] = binreader.ReadShortPascalString();
result["DESIGNATOR"] = binreader.ReadPascalString(); result["DESIGNATOR"] = binreader.ReadShortPascalString();
result["SWAPIDGROUP"] = binreader.ReadPascalString(); result["SWAPIDGROUP"] = binreader.ReadShortPascalString();
std::string partSeq = binreader.ReadPascalString(); // This is 'part|&|seq' if( auto it = pinFracs.find( pin_index ); it != pinFracs.end() )
{
result["LOCATION.X_FRAC"] = wxString::Format( "%d", it->second.x_frac );
result["LOCATION.Y_FRAC"] = wxString::Format( "%d", it->second.y_frac );
result["PINLENGTH_FRAC"] = wxString::Format( "%d", it->second.len_frac );
}
std::string partSeq = binreader.ReadShortPascalString(); // This is 'part|&|seq'
std::vector<std::string> partSeqSplit = split( partSeq, "|" ); std::vector<std::string> partSeqSplit = split( partSeq, "|" );
if( partSeqSplit.size() == 3 ) if( partSeqSplit.size() == 3 )
@ -4352,7 +4397,7 @@ std::map<wxString,LIB_SYMBOL*> SCH_IO_ALTIUM::ParseLibFile( const ALTIUM_COMPOUN
while( reader.GetRemainingBytes() > 0 ) while( reader.GetRemainingBytes() > 0 )
{ {
std::map<wxString, wxString> properties = reader.ReadProperties( handleBinaryDataLambda ); std::map<wxString, wxString> properties = reader.ReadProperties( handleBinaryPinLambda );
if( properties.empty() ) if( properties.empty() )
continue; continue;
@ -4362,7 +4407,12 @@ std::map<wxString,LIB_SYMBOL*> SCH_IO_ALTIUM::ParseLibFile( const ALTIUM_COMPOUN
switch( record ) switch( record )
{ {
case ALTIUM_SCH_RECORD::PIN: ParsePin( properties, symbols ); break; case ALTIUM_SCH_RECORD::PIN:
{
ParsePin( properties, symbols );
pin_index++;
break;
}
case ALTIUM_SCH_RECORD::LABEL: ParseLabel( properties, symbols, fontSizes ); break; case ALTIUM_SCH_RECORD::LABEL: ParseLabel( properties, symbols, fontSizes ); break;