From 439c25fca959fb3c6f66f53cf012a227fd5ab5d2 Mon Sep 17 00:00:00 2001 From: Seth Hillbrand Date: Mon, 10 Jun 2024 15:01:30 -0700 Subject: [PATCH] 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 --- common/io/altium/altium_binary_parser.cpp | 21 +++++-- common/io/altium/altium_binary_parser.h | 66 ++++++++++++-------- eeschema/sch_io/altium/sch_io_altium.cpp | 74 +++++++++++++++++++---- 3 files changed, 118 insertions(+), 43 deletions(-) diff --git a/common/io/altium/altium_binary_parser.cpp b/common/io/altium/altium_binary_parser.cpp index d36a82ca39..9654c6d1e1 100644 --- a/common/io/altium/altium_binary_parser.cpp +++ b/common/io/altium/altium_binary_parser.cpp @@ -26,6 +26,7 @@ #include "altium_parser_utils.h" #include +#include #include #include #include @@ -204,7 +205,7 @@ ALTIUM_COMPOUND_FILE::FindStreamSingleLevel( const CFB::COMPOUND_FILE_ENTRY* aEn } -std::map +std::map ALTIUM_COMPOUND_FILE::GetLibSymbols( const CFB::COMPOUND_FILE_ENTRY* aStart ) const { 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 ) return {}; - std::map folders; + std::map folders; 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 ); 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; } ); @@ -420,7 +430,7 @@ std::map ALTIUM_BINARY_PARSER::ReadProperties( // Both the 'l' and the null-byte are missing, which looks like Altium swallowed two bytes. 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 " "malformed or missing." ) ); @@ -500,5 +510,4 @@ std::map ALTIUM_BINARY_PARSER::ReadProperties( } return kv; -} - +} \ No newline at end of file diff --git a/common/io/altium/altium_binary_parser.h b/common/io/altium/altium_binary_parser.h index 1ca7ba49df..6e2796daaf 100644 --- a/common/io/altium/altium_binary_parser.h +++ b/common/io/altium/altium_binary_parser.h @@ -27,17 +27,17 @@ #include "altium_props_utils.h" +#include #include #include #include +#include +#include +#include #include #include #include -#include - -#include -#include namespace CFB { @@ -53,6 +53,16 @@ struct COMPOUND_FILE_ENTRY; std::string FormatPath( const std::vector& 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 { public: @@ -94,7 +104,7 @@ public: std::map EnumDir( const std::wstring& aDir ) const; - std::map GetLibSymbols( const CFB::COMPOUND_FILE_ENTRY* aStart ) const; + std::map GetLibSymbols( const CFB::COMPOUND_FILE_ENTRY* aStart ) const; private: @@ -359,7 +369,7 @@ public: return value; } - std::string ReadPascalString() + std::string ReadShortPascalString() { uint8_t length = ReadByte(); @@ -371,6 +381,18 @@ public: 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: const std::string& m_data; size_t m_position; @@ -382,31 +404,28 @@ public: ALTIUM_COMPRESSED_READER( const std::string& aData ) : ALTIUM_BINARY_READER( aData ) {} - std::pair ReadCompressedString() + std::pair ReadCompressedString() { - std::string result; + std::string* result; int id = -1; - while( true ) - { - uint8_t byte = ReadByte(); - if( byte != 0xD0 ) - throw std::runtime_error( "ALTIUM_COMPRESSED_READER: invalid compressed string" ); + uint8_t byte = ReadByte(); + 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 = ReadPascalString(); - - result = decompressData( data ); - } + std::string data = ReadFullPascalString(); + result = decompressData( data ); return std::make_pair( id, result ); } private: - std::string decompressData( std::string& aData ) + std::string decompressedData; + + std::string* decompressData( std::string& aData ) { // Create a memory input stream with the buffer wxMemoryInputStream memStream( (void*) aData.data(), aData.length() ); @@ -414,9 +433,6 @@ private: // Create a zlib input stream with the memory input stream wxZlibInputStream zStream( memStream ); - // Prepare a string to hold decompressed data - std::string decompressedData; - // Read decompressed data from the zlib input stream while( !zStream.Eof() ) { @@ -426,7 +442,7 @@ private: decompressedData.append( buffer, bytesRead ); } - return decompressedData; + return &decompressedData; } }; diff --git a/eeschema/sch_io/altium/sch_io_altium.cpp b/eeschema/sch_io/altium/sch_io_altium.cpp index 26e540ce0c..5a0fec0b4c 100644 --- a/eeschema/sch_io/altium/sch_io_altium.cpp +++ b/eeschema/sch_io/altium/sch_io_altium.cpp @@ -4278,15 +4278,54 @@ std::map SCH_IO_ALTIUM::ParseLibFile( const ALTIUM_COMPOUN { std::map ret; std::vector fontSizes; + struct SYMBOL_PIN_FRAC + { + int x_frac; + int y_frac; + int len_frac; + }; ParseLibHeader( aAltiumLibFile, fontSizes ); - std::map syms = aAltiumLibFile.GetLibSymbols( nullptr ); + std::map syms = aAltiumLibFile.GetLibSymbols( nullptr ); for( auto& [name, entry] : syms ) { - ALTIUM_BINARY_PARSER reader( aAltiumLibFile, entry ); + + std::map pinFracs; + + if( entry.m_pinsFrac ) + { + auto parse_binary_pin_frac = + [&]( const std::string& binaryData ) -> std::map + { + std::map result; + ALTIUM_COMPRESSED_READER cmpreader( binaryData ); + + std::pair 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 symbols; + int pin_index = 0; if( reader.GetRemainingBytes() <= 0 ) { @@ -4304,10 +4343,9 @@ std::map SCH_IO_ALTIUM::ParseLibFile( const ALTIUM_COMPOUN symbols = ParseLibComponent( properties ); } - auto handleBinaryDataLambda = - []( const std::string& binaryData ) -> std::map + auto handleBinaryPinLambda = + [&]( const std::string& binaryData ) -> std::map { - std::map result; ALTIUM_BINARY_READER binreader( binaryData ); @@ -4325,7 +4363,7 @@ std::map SCH_IO_ALTIUM::ParseLibFile( const ALTIUM_COMPOUN result["SYMBOL_OUTEREDGE"] = wxString::Format( "%d", binreader.ReadByte() ); result["SYMBOL_INNER"] = wxString::Format( "%d", binreader.ReadByte() ); result["SYMBOL_OUTER"] = wxString::Format( "%d", binreader.ReadByte() ); - result["TEXT"] = binreader.ReadPascalString(); + result["TEXT"] = binreader.ReadShortPascalString(); binreader.ReadByte(); // unknown result["ELECTRICAL"] = wxString::Format( "%d", binreader.ReadByte() ); result["PINCONGLOMERATE"] = wxString::Format( "%d", binreader.ReadByte() ); @@ -4333,12 +4371,19 @@ std::map SCH_IO_ALTIUM::ParseLibFile( const ALTIUM_COMPOUN result["LOCATION.X"] = wxString::Format( "%d", binreader.ReadInt16() ); result["LOCATION.Y"] = wxString::Format( "%d", binreader.ReadInt16() ); result["COLOR"] = wxString::Format( "%d", binreader.ReadInt32() ); - result["NAME"] = binreader.ReadPascalString(); - result["DESIGNATOR"] = binreader.ReadPascalString(); - result["SWAPIDGROUP"] = binreader.ReadPascalString(); + result["NAME"] = binreader.ReadShortPascalString(); + result["DESIGNATOR"] = binreader.ReadShortPascalString(); + 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 partSeqSplit = split( partSeq, "|" ); if( partSeqSplit.size() == 3 ) @@ -4352,7 +4397,7 @@ std::map SCH_IO_ALTIUM::ParseLibFile( const ALTIUM_COMPOUN while( reader.GetRemainingBytes() > 0 ) { - std::map properties = reader.ReadProperties( handleBinaryDataLambda ); + std::map properties = reader.ReadProperties( handleBinaryPinLambda ); if( properties.empty() ) continue; @@ -4362,7 +4407,12 @@ std::map SCH_IO_ALTIUM::ParseLibFile( const ALTIUM_COMPOUN 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;