From 43a786899469992318040f6ba1eeca44973620fc Mon Sep 17 00:00:00 2001 From: Ian McInerney <ian.s.mcinerney@ieee.org> Date: Sat, 4 Jan 2025 21:38:37 +0000 Subject: [PATCH] Fix reading old plot layer settings into new layer IDs Fixes https://gitlab.com/kicad/code/kicad/-/issues/19475 --- common/lset.cpp | 120 ---------- include/base_set.h | 137 ++++++++++++ include/lset.h | 20 -- .../kicad_sexpr/pcb_io_kicad_sexpr_parser.cpp | 2 +- pcbnew/pcb_plot_params.cpp | 207 +++++++++++++++++- pcbnew/pcb_plot_params_parser.h | 4 +- 6 files changed, 340 insertions(+), 150 deletions(-) diff --git a/common/lset.cpp b/common/lset.cpp index 9a0289e320..0f01bd39f2 100644 --- a/common/lset.cpp +++ b/common/lset.cpp @@ -297,126 +297,6 @@ LSEQ LSET::TechAndUserUIOrder() const } -std::string LSET::FmtBin() const -{ - std::string ret; - - int bit_count = size(); - - for( int bit=0; bit<bit_count; ++bit ) - { - if( bit ) - { - if( !( bit % 8 ) ) - ret += '|'; - else if( !( bit % 4 ) ) - ret += '_'; - } - - ret += (*this)[bit] ? '1' : '0'; - } - - // reverse of string - return std::string( ret.rbegin(), ret.rend() ); -} - - -std::string LSET::FmtHex() const -{ - std::string ret; - - static const char hex[] = "0123456789abcdef"; - - size_t nibble_count = ( size() + 3 ) / 4; - - for( size_t nibble = 0; nibble < nibble_count; ++nibble ) - { - unsigned int ndx = 0; - - // test 4 consecutive bits and set ndx to 0-15 - for( size_t nibble_bit = 0; nibble_bit < 4; ++nibble_bit ) - { - size_t nibble_pos = nibble_bit + ( nibble * 4 ); - // make sure it's not extra bits that don't exist in the bitset but need to in the - // hex format - if( nibble_pos >= size() ) - break; - - if( ( *this )[nibble_pos] ) - ndx |= ( 1 << nibble_bit ); - } - - if( nibble && !( nibble % 8 ) ) - ret += '_'; - - assert( ndx < arrayDim( hex ) ); - - ret += hex[ndx]; - } - - // reverse of string - return std::string( ret.rbegin(), ret.rend() ); -} - - -int LSET::ParseHex( const std::string& str ) -{ - return ParseHex( str.c_str(), str.length() ); -} - - -int LSET::ParseHex( const char* aStart, int aCount ) -{ - LSET tmp; - - const char* rstart = aStart + aCount - 1; - const char* rend = aStart - 1; - - const int bitcount = size(); - - int nibble_ndx = 0; - - while( rstart > rend ) - { - int cc = *rstart--; - - if( cc == '_' ) - continue; - - int nibble; - - if( cc >= '0' && cc <= '9' ) - nibble = cc - '0'; - else if( cc >= 'a' && cc <= 'f' ) - nibble = cc - 'a' + 10; - else if( cc >= 'A' && cc <= 'F' ) - nibble = cc - 'A' + 10; - else - break; - - int bit = nibble_ndx * 4; - - for( int ndx=0; bit<bitcount && ndx<4; ++bit, ++ndx ) - if( nibble & (1<<ndx) ) - tmp.set( bit ); - - if( bit >= bitcount ) - break; - - ++nibble_ndx; - } - - int byte_count = aStart + aCount - 1 - rstart; - - assert( byte_count >= 0 ); - - if( byte_count > 0 ) - *this = tmp; - - return byte_count; -} - - LSEQ LSET::Seq( const LSEQ& aSequence ) const { LSEQ ret; diff --git a/include/base_set.h b/include/base_set.h index 20db3c8c6a..6c4c819eec 100644 --- a/include/base_set.h +++ b/include/base_set.h @@ -26,6 +26,7 @@ #include <stdexcept> #include <dynamic_bitset.h> +#include <core/arraydim.h> #include <core/kicad_algo.h> #include <kicommon.h> @@ -214,6 +215,142 @@ public: return alg::lexicographical_compare_three_way( begin(), end(), other.begin(), other.end() ) < 0; } + /** + * Return a binary string showing contents of this set. + */ + std::string FmtBin() const + { + std::string ret; + + int bit_count = size(); + + for( int bit=0; bit<bit_count; ++bit ) + { + if( bit ) + { + if( !( bit % 8 ) ) + ret += '|'; + else if( !( bit % 4 ) ) + ret += '_'; + } + + ret += (*this)[bit] ? '1' : '0'; + } + + // reverse of string + return std::string( ret.rbegin(), ret.rend() ); + } + + /** + * Return a hex string showing contents of this set. + */ + std::string FmtHex() const + { + std::string ret; + + static const char hex[] = "0123456789abcdef"; + + size_t nibble_count = ( size() + 3 ) / 4; + + for( size_t nibble = 0; nibble < nibble_count; ++nibble ) + { + unsigned int ndx = 0; + + // test 4 consecutive bits and set ndx to 0-15 + for( size_t nibble_bit = 0; nibble_bit < 4; ++nibble_bit ) + { + size_t nibble_pos = nibble_bit + ( nibble * 4 ); + // make sure it's not extra bits that don't exist in the bitset but need to in the + // hex format + if( nibble_pos >= size() ) + break; + + if( ( *this )[nibble_pos] ) + ndx |= ( 1 << nibble_bit ); + } + + if( nibble && !( nibble % 8 ) ) + ret += '_'; + + assert( ndx < arrayDim( hex ) ); + + ret += hex[ndx]; + } + + // reverse of string + return std::string( ret.rbegin(), ret.rend() ); + } + + /** + * Convert the output of FmtHex() and replaces this set's values + * with those given in the input string. Parsing stops at the first + * non hex ASCII byte, except that marker bytes output from FmtHex are + * not terminators. + * @return int - number of bytes consumed + */ + int ParseHex( const std::string& str ) + { + return ParseHex( str.c_str(), str.length() ); + } + + /** + * Convert the output of FmtHex() and replaces this set's values + * with those given in the input string. Parsing stops at the first + * non hex ASCII byte, except that marker bytes output from FmtHex are + * not terminators. + * @return int - number of bytes consumed + */ + int ParseHex( const char* aStart, int aCount ) + { + BASE_SET tmp(size()); + + const char* rstart = aStart + aCount - 1; + const char* rend = aStart - 1; + + const int bitcount = size(); + + int nibble_ndx = 0; + + while( rstart > rend ) + { + int cc = *rstart--; + + if( cc == '_' ) + continue; + + int nibble; + + if( cc >= '0' && cc <= '9' ) + nibble = cc - '0'; + else if( cc >= 'a' && cc <= 'f' ) + nibble = cc - 'a' + 10; + else if( cc >= 'A' && cc <= 'F' ) + nibble = cc - 'A' + 10; + else + break; + + int bit = nibble_ndx * 4; + + for( int ndx=0; bit<bitcount && ndx<4; ++bit, ++ndx ) + if( nibble & (1<<ndx) ) + tmp.set( bit ); + + if( bit >= bitcount ) + break; + + ++nibble_ndx; + } + + int byte_count = aStart + aCount - 1 - rstart; + + assert( byte_count >= 0 ); + + if( byte_count > 0 ) + *this = tmp; + + return byte_count; + } + // Custom iterator to iterate over set bits class KICOMMON_API set_bits_iterator { diff --git a/include/lset.h b/include/lset.h index 2dc1c2b6c7..c915d41113 100644 --- a/include/lset.h +++ b/include/lset.h @@ -249,26 +249,6 @@ public: } } - /** - * Return a hex string showing contents of this LSEQ. - */ - std::string FmtHex() const; - - /** - * Convert the output of FmtHex() and replaces this set's values - * with those given in the input string. Parsing stops at the first - * non hex ASCII byte, except that marker bytes output from FmtHex are - * not terminators. - * @return int - number of bytes consumed - */ - int ParseHex( const char* aStart, int aCount ); - int ParseHex( const std::string& str ); - - /** - * Return a binary string showing contents of this LSEQ. - */ - std::string FmtBin() const; - /** * Find the first set PCB_LAYER_ID. Returns UNDEFINED_LAYER if more * than one is set or UNSELECTED_LAYER if none is set. diff --git a/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr_parser.cpp b/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr_parser.cpp index c6a5529a12..f9385e1901 100644 --- a/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr_parser.cpp +++ b/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr_parser.cpp @@ -2482,7 +2482,7 @@ void PCB_IO_KICAD_SEXPR_PARSER::parseSetup() case T_pcbplotparams: { PCB_PLOT_PARAMS plotParams; - PCB_PLOT_PARAMS_PARSER parser( reader ); + PCB_PLOT_PARAMS_PARSER parser( reader, m_requiredVersion ); // parser must share the same current line as our current PCB parser // synchronize it. parser.SyncLineReaderWith( *this ); diff --git a/pcbnew/pcb_plot_params.cpp b/pcbnew/pcb_plot_params.cpp index 9a67406675..20cc6698ab 100644 --- a/pcbnew/pcb_plot_params.cpp +++ b/pcbnew/pcb_plot_params.cpp @@ -404,18 +404,183 @@ bool PCB_PLOT_PARAMS::SetHPGLPenSpeed( int aValue ) } -PCB_PLOT_PARAMS_PARSER::PCB_PLOT_PARAMS_PARSER( LINE_READER* aReader ) : - PCB_PLOT_PARAMS_LEXER( aReader ) +PCB_PLOT_PARAMS_PARSER::PCB_PLOT_PARAMS_PARSER( LINE_READER* aReader, int aBoardFileVersion ) : + PCB_PLOT_PARAMS_LEXER( aReader ), + m_boardFileVersion( aBoardFileVersion ) { } PCB_PLOT_PARAMS_PARSER::PCB_PLOT_PARAMS_PARSER( char* aLine, const wxString& aSource ) : - PCB_PLOT_PARAMS_LEXER( aLine, aSource ) + PCB_PLOT_PARAMS_LEXER( aLine, aSource ), + m_boardFileVersion( 0 ) { } +/** + * These are the layer IDs from before 5e0abadb23425765e164f49ee2f893e94ddb97fc, + * and are needed for mapping old PCB files to the new layer numbering. + */ +enum LEGACY_PCB_LAYER_ID: int +{ + LEGACY_UNDEFINED_LAYER = -1, + LEGACY_UNSELECTED_LAYER = -2, + + LEGACY_F_Cu = 0, + LEGACY_In1_Cu, + LEGACY_In2_Cu, + LEGACY_In3_Cu, + LEGACY_In4_Cu, + LEGACY_In5_Cu, + LEGACY_In6_Cu, + LEGACY_In7_Cu, + LEGACY_In8_Cu, + LEGACY_In9_Cu, + LEGACY_In10_Cu, + LEGACY_In11_Cu, + LEGACY_In12_Cu, + LEGACY_In13_Cu, + LEGACY_In14_Cu, + LEGACY_In15_Cu, + LEGACY_In16_Cu, + LEGACY_In17_Cu, + LEGACY_In18_Cu, + LEGACY_In19_Cu, + LEGACY_In20_Cu, + LEGACY_In21_Cu, + LEGACY_In22_Cu, + LEGACY_In23_Cu, + LEGACY_In24_Cu, + LEGACY_In25_Cu, + LEGACY_In26_Cu, + LEGACY_In27_Cu, + LEGACY_In28_Cu, + LEGACY_In29_Cu, + LEGACY_In30_Cu, + LEGACY_B_Cu, // 31 + + LEGACY_B_Adhes, + LEGACY_F_Adhes, + + LEGACY_B_Paste, + LEGACY_F_Paste, + + LEGACY_B_SilkS, + LEGACY_F_SilkS, + + LEGACY_B_Mask, + LEGACY_F_Mask, // 39 + + LEGACY_Dwgs_User, + LEGACY_Cmts_User, + LEGACY_Eco1_User, + LEGACY_Eco2_User, + LEGACY_Edge_Cuts, + LEGACY_Margin, // 45 + + LEGACY_B_CrtYd, + LEGACY_F_CrtYd, + + LEGACY_B_Fab, + LEGACY_F_Fab, // 49 + + // User definable layers. + LEGACY_User_1, + LEGACY_User_2, + LEGACY_User_3, + LEGACY_User_4, + LEGACY_User_5, + LEGACY_User_6, + LEGACY_User_7, + LEGACY_User_8, + LEGACY_User_9, + + LEGACY_Rescue, // 59 + + // Four reserved layers (60 - 63) for future expansion within the 64 bit integer limit. + + LEGACY_PCB_LAYER_ID_COUNT +}; + +/* + * Mapping to translate a legacy layer ID into the new PCB layer IDs. + */ +static const std::map<LEGACY_PCB_LAYER_ID, PCB_LAYER_ID> s_legacyLayerIdMap{ + {LEGACY_F_Cu, F_Cu}, + {LEGACY_B_Cu, B_Cu}, + {LEGACY_In1_Cu, In1_Cu}, + {LEGACY_In2_Cu, In2_Cu}, + {LEGACY_In3_Cu, In3_Cu}, + {LEGACY_In4_Cu, In4_Cu}, + {LEGACY_In5_Cu, In5_Cu}, + {LEGACY_In6_Cu, In6_Cu}, + {LEGACY_In7_Cu, In7_Cu}, + {LEGACY_In8_Cu, In8_Cu}, + {LEGACY_In9_Cu, In9_Cu}, + {LEGACY_In10_Cu, In10_Cu}, + {LEGACY_In11_Cu, In11_Cu}, + {LEGACY_In12_Cu, In12_Cu}, + {LEGACY_In13_Cu, In13_Cu}, + {LEGACY_In14_Cu, In14_Cu}, + {LEGACY_In15_Cu, In15_Cu}, + {LEGACY_In16_Cu, In16_Cu}, + {LEGACY_In17_Cu, In17_Cu}, + {LEGACY_In18_Cu, In18_Cu}, + {LEGACY_In19_Cu, In19_Cu}, + {LEGACY_In20_Cu, In20_Cu}, + {LEGACY_In21_Cu, In21_Cu}, + {LEGACY_In22_Cu, In22_Cu}, + {LEGACY_In23_Cu, In23_Cu}, + {LEGACY_In24_Cu, In24_Cu}, + {LEGACY_In25_Cu, In25_Cu}, + {LEGACY_In26_Cu, In26_Cu}, + {LEGACY_In27_Cu, In27_Cu}, + {LEGACY_In28_Cu, In28_Cu}, + {LEGACY_In29_Cu, In29_Cu}, + {LEGACY_In30_Cu, In30_Cu}, + {LEGACY_F_Mask, F_Mask}, + {LEGACY_B_Mask, B_Mask}, + {LEGACY_F_SilkS, F_SilkS}, + {LEGACY_B_SilkS, B_SilkS}, + {LEGACY_F_Adhes, F_Adhes}, + {LEGACY_B_Adhes, B_Adhes}, + {LEGACY_F_Paste, F_Paste}, + {LEGACY_B_Paste, B_Paste}, + {LEGACY_Dwgs_User, Dwgs_User}, + {LEGACY_Cmts_User, Cmts_User}, + {LEGACY_Eco1_User, Eco1_User}, + {LEGACY_Eco2_User, Eco2_User}, + {LEGACY_Edge_Cuts, Edge_Cuts}, + {LEGACY_Margin, Margin}, + {LEGACY_B_CrtYd, B_CrtYd}, + {LEGACY_F_CrtYd, F_CrtYd}, + {LEGACY_B_Fab, B_Fab}, + {LEGACY_F_Fab, F_Fab}, + {LEGACY_User_1, User_1}, + {LEGACY_User_2, User_2}, + {LEGACY_User_3, User_3}, + {LEGACY_User_4, User_4}, + {LEGACY_User_5, User_5}, + {LEGACY_User_6, User_6}, + {LEGACY_User_7, User_7}, + {LEGACY_User_8, User_8}, + {LEGACY_User_9, User_9}, + {LEGACY_Rescue, Rescue}, +}; + + +LSET remapLegacyLayerLSET( const BASE_SET& aLegacyLSET ) +{ + LSET newLayers; + + for( const auto& [legacyLayer, newLayer] : s_legacyLayerIdMap ) + newLayers[newLayer] = aLegacyLSET[legacyLayer]; + + return newLayers; +} + + void PCB_PLOT_PARAMS_PARSER::Parse( PCB_PLOT_PARAMS* aPcbPlotParams ) { T token; @@ -451,8 +616,21 @@ void PCB_PLOT_PARAMS_PARSER::Parse( PCB_PLOT_PARAMS* aPcbPlotParams ) } else if( cur.find_first_of( "0x" ) == 0 ) // pretty ver. 4. { - // skip the leading 2 0x bytes. - aPcbPlotParams->m_layerSelection.ParseHex( cur.c_str() + 2, cur.size() - 2 ); + // The layers were renumbered in 5e0abadb23425765e164f49ee2f893e94ddb97fc, but there wasn't + // a board file version change with it, so this value is the one immediately after that happened. + if( m_boardFileVersion < 20240819 ) + { + BASE_SET legacyLSET( LEGACY_PCB_LAYER_ID_COUNT ); + + // skip the leading 2 0x bytes. + legacyLSET.ParseHex( cur.c_str() + 2, cur.size() - 2 ); + aPcbPlotParams->SetLayerSelection( remapLegacyLayerLSET( legacyLSET ) ); + } + else + { + // skip the leading 2 0x bytes. + aPcbPlotParams->m_layerSelection.ParseHex( cur.c_str() + 2, cur.size() - 2 ); + } } else { @@ -470,9 +648,22 @@ void PCB_PLOT_PARAMS_PARSER::Parse( PCB_PLOT_PARAMS* aPcbPlotParams ) if( cur.find_first_of( "0x" ) == 0 ) { - // skip the leading 2 0x bytes. - aPcbPlotParams->m_plotOnAllLayersSelection.ParseHex( cur.c_str() + 2, - cur.size() - 2 ); + // The layers were renumbered in 5e0abadb23425765e164f49ee2f893e94ddb97fc, but there wasn't + // a board file version change with it, so this value is the one immediately after that happened. + if( m_boardFileVersion < 20240819 ) + { + BASE_SET legacyLSET( LEGACY_PCB_LAYER_ID_COUNT ); + + // skip the leading 2 0x bytes. + legacyLSET.ParseHex( cur.c_str() + 2, cur.size() - 2 ); + aPcbPlotParams->SetPlotOnAllLayersSelection( remapLegacyLayerLSET( legacyLSET ) ); + } + else + { + // skip the leading 2 0x bytes. + aPcbPlotParams->m_plotOnAllLayersSelection.ParseHex( cur.c_str() + 2, + cur.size() - 2 ); + } } else { diff --git a/pcbnew/pcb_plot_params_parser.h b/pcbnew/pcb_plot_params_parser.h index 303e5ac0d4..bde48f8317 100644 --- a/pcbnew/pcb_plot_params_parser.h +++ b/pcbnew/pcb_plot_params_parser.h @@ -37,7 +37,7 @@ class LINE_READER; class PCB_PLOT_PARAMS_PARSER : public PCB_PLOT_PARAMS_LEXER { public: - PCB_PLOT_PARAMS_PARSER( LINE_READER* aReader ); + PCB_PLOT_PARAMS_PARSER( LINE_READER* aReader, int aBoardFileVersion ); PCB_PLOT_PARAMS_PARSER( char* aLine, const wxString& aSource ); LINE_READER* GetReader() { return reader; }; @@ -69,6 +69,8 @@ private: * Search for the RIGHT parenthesis which closes the current description. */ void skipCurrent(); + + int m_boardFileVersion; }; #endif // PCB_PLOT_PARAMS_PARSER_H_