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 <compoundfilereader.h>
#include <charconv>
#include <ki_exception.h>
#include <math/util.h>
#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
{
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<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
{
@ -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<wxString, wxString> 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<wxString, wxString> ALTIUM_BINARY_PARSER::ReadProperties(
}
return kv;
}
}

View File

@ -27,17 +27,17 @@
#include "altium_props_utils.h"
#include <charconv>
#include <map>
#include <memory>
#include <numeric>
#include <string>
#include <stdexcept>
#include <vector>
#include <wx/mstream.h>
#include <wx/zstream.h>
#include <math/vector2d.h>
#include <vector>
#include <string>
#include <stdexcept>
namespace CFB
{
@ -53,6 +53,16 @@ struct COMPOUND_FILE_ENTRY;
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
{
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*> GetLibSymbols( const CFB::COMPOUND_FILE_ENTRY* aStart ) const;
std::map<wxString, ALTIUM_SYMBOL_DATA> 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<int, std::string> ReadCompressedString()
std::pair<int, std::string*> 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;
}
};

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::vector<int> fontSizes;
struct SYMBOL_PIN_FRAC
{
int x_frac;
int y_frac;
int len_frac;
};
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 )
{
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;
int pin_index = 0;
if( reader.GetRemainingBytes() <= 0 )
{
@ -4304,10 +4343,9 @@ std::map<wxString,LIB_SYMBOL*> SCH_IO_ALTIUM::ParseLibFile( const ALTIUM_COMPOUN
symbols = ParseLibComponent( properties );
}
auto handleBinaryDataLambda =
[]( const std::string& binaryData ) -> std::map<wxString, wxString>
auto handleBinaryPinLambda =
[&]( const std::string& binaryData ) -> std::map<wxString, wxString>
{
std::map<wxString, wxString> result;
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_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<wxString,LIB_SYMBOL*> 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<std::string> partSeqSplit = split( partSeq, "|" );
if( partSeqSplit.size() == 3 )
@ -4352,7 +4397,7 @@ std::map<wxString,LIB_SYMBOL*> SCH_IO_ALTIUM::ParseLibFile( const ALTIUM_COMPOUN
while( reader.GetRemainingBytes() > 0 )
{
std::map<wxString, wxString> properties = reader.ReadProperties( handleBinaryDataLambda );
std::map<wxString, wxString> properties = reader.ReadProperties( handleBinaryPinLambda );
if( properties.empty() )
continue;
@ -4362,7 +4407,12 @@ std::map<wxString,LIB_SYMBOL*> 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;