64#include <unordered_set>
77 0x3B, 0x9A, 0xCA, 0x00,
85 0x3B, 0x9A, 0xCA, 0x00,
139 0x00, 0x54, 0x00, 0x61, 0x00, 0x68,
140 0x00, 0x6F, 0x00, 0x6D, 0x00, 0x61,
169 0x3B, 0x9A, 0xCA, 0x00,
170 0x3B, 0x9A, 0xCA, 0x00,
202 return (
static_cast<uint32_t
>( r ) << 16 )
203 | (
static_cast<uint32_t
>( g ) << 8 )
204 |
static_cast<uint32_t
>( b );
213 return ( (
static_cast<int>( aData[aPos] ) << 16 )
214 | (
static_cast<int>( aData[aPos + 1] ) << 8 )
215 |
static_cast<int>( aData[aPos + 2] ) ) -
INT3_BIAS;
224 unsigned int raw = (
static_cast<unsigned int>( aData[aPos] ) << 24 )
225 | (
static_cast<unsigned int>( aData[aPos + 1] ) << 16 )
226 | (
static_cast<unsigned int>( aData[aPos + 2] ) << 8 )
227 |
static_cast<unsigned int>( aData[aPos + 3] );
228 return static_cast<int>( raw ) -
INT4_BIAS;
251 static const uint8_t ZERO_RUN[] = {
252 0x0F, 0x42, 0x40, 0x0F, 0x42, 0x40, 0x0F, 0x42, 0x40, 0x0F, 0x42, 0x40,
253 0x3B, 0x9A, 0xCA, 0x00, 0x3B, 0x9A, 0xCA, 0x00, 0x3B, 0x9A, 0xCA, 0x00, 0x3B, 0x9A, 0xCA, 0x00,
255 static constexpr size_t ZERO_RUN_LEN =
sizeof( ZERO_RUN );
256 static constexpr size_t MAX_LOOKBACK = 4096;
258 if( aBoundaryOffset < ZERO_RUN_LEN + 7 )
261 size_t scanStart = aBoundaryOffset > MAX_LOOKBACK ? aBoundaryOffset - MAX_LOOKBACK : 0;
265 for(
size_t p = aBoundaryOffset - ZERO_RUN_LEN; p + ZERO_RUN_LEN <= aDataSize; p-- )
267 if( std::memcmp( aData + p, ZERO_RUN, ZERO_RUN_LEN ) == 0 )
269 size_t idPos = p - 7;
273 if( idPos >= 6 &&
ReadInt3At( aData, idPos - 3 ) == -1 )
275 aQuarterTurns =
ReadInt3At( aData, idPos - 6 );
290 const char* value = std::getenv( aVarName );
292 return value && *value && std::strcmp( value,
"0" ) != 0;
301 const char* filterRaw = std::getenv(
"KICAD_DIPTRACE_DUMP_PAD_REFS" );
303 if( !filterRaw || !*filterRaw )
306 wxString
filter = wxString::FromUTF8( filterRaw ).Lower();
308 if(
filter == wxT(
"*" ) )
311 wxString haystack = wxT(
"," ) +
filter + wxT(
"," );
312 wxString needle = wxT(
"," ) + aRefdes.Lower() + wxT(
"," );
314 return haystack.Contains( needle );
323 const char* filterRaw = std::getenv(
"KICAD_DIPTRACE_DUMP_PAD_REFS" );
325 if( !filterRaw || !*filterRaw )
328 wxString
filter = wxString::FromUTF8( filterRaw ).Lower();
330 if(
filter == wxT(
"*" ) )
333 wxString haystack = wxT(
"," ) +
filter + wxT(
"," );
334 wxString needle = wxT(
"," ) + aRefdes.Lower() + wxT(
"," );
336 return haystack.Contains( needle );
345 const char* filterRaw = std::getenv(
"KICAD_DIPTRACE_DUMP_COMPONENT_REFS" );
347 if( !filterRaw || !*filterRaw )
350 wxString
filter = wxString::FromUTF8( filterRaw ).Lower();
352 if(
filter == wxT(
"*" ) )
355 wxString haystack = wxT(
"," ) +
filter + wxT(
"," );
356 wxString needle = wxT(
"," ) + aRefdes.Lower() + wxT(
"," );
358 return haystack.Contains( needle );
367 const char* filterRaw = std::getenv(
"KICAD_DIPTRACE_DUMP_FOOTPRINT_REFS" );
369 if( !filterRaw || !*filterRaw )
372 wxString
filter = wxString::FromUTF8( filterRaw ).Lower();
374 if(
filter == wxT(
"*" ) )
377 wxString haystack = wxT(
"," ) +
filter + wxT(
"," );
378 wxString needle = wxT(
"," ) + aRefdes.Lower() + wxT(
"," );
380 return haystack.Contains( needle );
396 int aFieldC,
int aFieldD,
int aFieldE,
int aFieldF,
397 uint8_t aSep1, uint8_t aSep2, uint8_t aSep3 )
403 wxT(
"DipTrace: component ref=%s value=%s pat=%s lib=%s flags=[%u,%u,%u,%u] "
404 "layer=%d pos=(%d,%d) rot=%d fieldA=%d fieldB=%d fieldC=%d fieldD=%d "
405 "fieldE=%d fieldF=%d sep=[%u,%u,%u] bbox=(%d,%d) "
407 "boundary=0x%06zX str=0x%06zX hdrEnd=0x%06zX regionEnd=0x%06zX" ),
409 static_cast<unsigned int>( aComp.
flags.size() > 0 ? aComp.
flags[0] : 0 ),
410 static_cast<unsigned int>( aComp.
flags.size() > 1 ? aComp.
flags[1] : 0 ),
411 static_cast<unsigned int>( aComp.
flags.size() > 2 ? aComp.
flags[2] : 0 ),
413 aComp.
positionY, aComp.
rotation, aFieldA, aFieldB, aFieldC, aFieldD, aFieldE, aFieldF,
414 static_cast<unsigned int>( aSep1 ),
static_cast<unsigned int>( aSep2 ),
423 uint32_t u =
static_cast<uint32_t
>( aData[aPos] )
424 | (
static_cast<uint32_t
>( aData[aPos + 1] ) << 8 )
425 | (
static_cast<uint32_t
>( aData[aPos + 2] ) << 16 )
426 | (
static_cast<uint32_t
>( aData[aPos + 3] ) << 24 );
427 return static_cast<int32_t
>( u );
433 uint32_t u =
static_cast<uint32_t
>( aData[aPos] )
434 | (
static_cast<uint32_t
>( aData[aPos + 1] ) << 8 )
435 | (
static_cast<uint32_t
>( aData[aPos + 2] ) << 16 )
436 | (
static_cast<uint32_t
>( aData[aPos + 3] ) << 24 );
438 std::memcpy( &out, &u,
sizeof( out ) );
443static wxString
BytesToHex(
const uint8_t* aData,
size_t aLen );
447 size_t aPosXPos,
size_t aPosYPos,
size_t aRotPos,
448 size_t aFieldCPos,
size_t aFieldDPos )
453 auto dumpOne = [&](
const wxString& aName,
size_t aPos )
459 wxT(
"DipTrace: component raw ref=%s field=%s off=0x%06zX bytes=[%s] "
460 "le_i32=%d le_f32=%s int4=%d" ),
464 dumpOne( wxT(
"posX" ), aPosXPos );
465 dumpOne( wxT(
"posY" ), aPosYPos );
466 dumpOne( wxT(
"rotation" ), aRotPos );
467 dumpOne( wxT(
"fieldC" ), aFieldCPos );
468 dumpOne( wxT(
"fieldD" ), aFieldDPos );
472static wxString
BytesToHex(
const uint8_t* aData,
size_t aLen )
475 out.reserve( aLen * 3 );
477 for(
size_t i = 0; i < aLen; i++ )
482 out += wxString::Format( wxT(
"%02X" ),
static_cast<unsigned int>( aData[i] ) );
491 static const int ANGLES[] = {
492 0, 15708, 31416, 47124, 62832, 9000000, 18000000, 27000000, 36000000
497 for(
int target : ANGLES )
499 if(
std::abs( absVal - target ) <= 8 )
517 bool fullScan =
EnvFlagEnabled(
"KICAD_DIPTRACE_DUMP_COMPONENT_SCAN_FULL" );
522 if( scanEnd <= scanStart + 4 )
526 wxT(
"DipTrace: comp-scan ref=%s pat=%s boundary=0x%06zX str=0x%06zX "
527 "headerEnd=0x%06zX regionEnd=0x%06zX full=%d scan=[0x%06zX..0x%06zX)" ),
531 for(
size_t off = scanStart; off + 3 <= scanEnd; off++ )
539 wxLogTrace(
traceDiptraceIo, wxT(
"DipTrace: comp-scan-hit-i3 ref=%s off=0x%06zX rel=%lld bytes=[%s] int3=%d" ),
540 aComp.
refdes, off,
static_cast<long long>( off - scanStart ), hex, i3 );
543 for(
size_t off = scanStart; off + 4 <= scanEnd; off++ )
553 wxT(
"DipTrace: comp-scan-hit ref=%s off=0x%06zX rel=%lld bytes=[%s] "
554 "int4=%d le_i32=%d" ),
555 aComp.
refdes, off,
static_cast<long long>( off - scanStart ), hex, i4, leI32 );
561 const uint8_t* aData,
size_t aPostDimPos,
size_t aPostDimSize )
566 int fieldA = ( aPostDimSize >= 3 ) ?
ReadInt3At( aData, aPostDimPos ) : 0;
567 int fieldC = ( aPostDimSize >= 7 ) ?
ReadInt3At( aData, aPostDimPos + 4 ) : 0;
568 int fieldE = ( aPostDimSize >= 11 ) ?
ReadInt3At( aData, aPostDimPos + 8 ) : 0;
569 int fieldG = ( aPostDimSize >= 15 ) ?
ReadInt3At( aData, aPostDimPos + 12 ) : 0;
570 int fieldH = ( aPostDimSize >= 18 ) ?
ReadInt3At( aData, aPostDimPos + 15 ) : 0;
571 int fieldI = ( aPostDimSize >= 21 ) ?
ReadInt3At( aData, aPostDimPos + 18 ) : 0;
572 int fieldJ = ( aPostDimSize >= 24 ) ?
ReadInt3At( aData, aPostDimPos + 21 ) : 0;
573 int fieldM = ( aPostDimSize >= 30 ) ?
ReadInt4At( aData, aPostDimPos + 26 ) : 0;
574 int fieldN = ( aPostDimSize >= 34 ) ?
ReadInt4At( aData, aPostDimPos + 30 ) : 0;
575 wxString hex =
BytesToHex( aData + aPostDimPos, aPostDimSize );
578 wxT(
"DipTrace: pad-post ref=%s pad=%s label=%s idx=%d net=%d xy=(%d,%d) wh=(%d,%d) "
579 "drill=(%d,%d) mount=%u orient=%u len=%lu "
580 "A=%d C=%d E=%d G=%d H=%d I=%d J=%d M=%d N=%d hex=[%s]" ),
583 static_cast<unsigned int>( aPad.
orientClass ),
static_cast<unsigned long>( aPostDimSize ), fieldA,
584 fieldC, fieldE, fieldG, fieldH, fieldI, fieldJ, fieldM, fieldN, hex );
589 size_t aGapStart,
size_t aGapEnd )
594 if( aGapEnd <= aGapStart )
596 wxLogTrace(
traceDiptraceIo, wxT(
"DipTrace: pad-gap ref=%s start=0x%06zX end=0x%06zX len=0" ), aComp.
refdes,
597 aGapStart, aGapEnd );
601 size_t gapLen = aGapEnd - aGapStart;
602 size_t sampleLen = std::min<size_t>( gapLen, 96 );
603 wxString hex =
BytesToHex( aData + aGapStart, sampleLen );
606 size_t int3Count = std::min<size_t>( 8, gapLen / 3 );
608 for(
size_t i = 0; i < int3Count; i++ )
611 int3Seq += wxT(
"," );
613 int3Seq += wxString::Format( wxT(
"%d" ),
618 size_t int4Count = std::min<size_t>( 8, gapLen / 4 );
620 for(
size_t i = 0; i < int4Count; i++ )
623 int4Seq += wxT(
"," );
625 int4Seq += wxString::Format( wxT(
"%d" ),
630 wxT(
"DipTrace: pad-gap ref=%s start=0x%06zX end=0x%06zX len=%lu "
631 "int3=[%s] int4=[%s] hex[%lu]=[%s]" ),
632 aComp.
refdes, aGapStart, aGapEnd,
static_cast<unsigned long>( gapLen ), int3Seq, int4Seq,
633 static_cast<unsigned long>( sampleLen ), hex );
638 size_t aTailStart,
int aVisibility,
639 uint8_t aSideFlag1, uint8_t aSideFlag2,
int aOrderIdx,
640 int aRefdesYOffset,
int aValueYOffset,
641 uint8_t aHasOffset, uint8_t aTailTerm )
649 wxT(
"DipTrace: component-tail ref=%s off=0x%06zX vis=%d side=[%u,%u] "
650 "order=%d yoff=[%d,%d] hasOffset=%u term=%u hex=[%s]" ),
651 aComp.
refdes, aTailStart, aVisibility,
static_cast<unsigned int>( aSideFlag1 ),
652 static_cast<unsigned int>( aSideFlag2 ), aOrderIdx, aRefdesYOffset, aValueYOffset,
653 static_cast<unsigned int>( aHasOffset ),
static_cast<unsigned int>( aTailTerm ), hex );
657static void DumpRulesetBlock(
int aRuleSetIndex,
const wxString& aRuleSetName,
int aBlockIndex,
658 const std::array<int, 26>& aValues )
665 for(
size_t i = 0; i < aValues.size(); i++ )
668 values += wxT(
"," );
670 values += wxString::Format( wxT(
"%d" ), aValues[i] );
673 wxLogTrace(
traceDiptraceIo, wxT(
"DipTrace: ruleset[%d] '%s' block[%d] values=[%s]" ), aRuleSetIndex, aRuleSetName,
674 aBlockIndex, values );
678static void DumpZoneHeader(
int aZoneIndex,
size_t aHeaderPos,
const uint8_t* aData,
int aFieldA,
679 int aFlags1,
int aFlags2,
int aFlags3,
int aMinWidth,
int aClearance,
680 int aMinimumArea,
int aSeparator,
int aLayer,
int aFieldB,
int aVtxCount,
681 const wxString& aNetName )
686 wxString headerHex =
BytesToHex( aData + aHeaderPos, 30 );
689 wxT(
"DipTrace: zone[%d] hdr=0x%06zX fieldA=%d flags=[%d,%d,%d] "
690 "lineWidth=%d clearance=%d minimumArea=%d sep=%d layer=%d net=%d('%s') vtx=%d "
692 aZoneIndex, aHeaderPos, aFieldA, aFlags1, aFlags2, aFlags3, aMinWidth, aClearance, aMinimumArea,
693 aSeparator, aLayer, aFieldB, aNetName, aVtxCount, headerHex );
697static void DumpZoneGap(
int aZoneIndex,
size_t aGapStart,
size_t aGapEnd,
const uint8_t* aData )
702 size_t gapLen = aGapEnd - aGapStart;
703 size_t sampleLen = std::min<size_t>( gapLen, 96 );
704 wxString hex =
BytesToHex( aData + aGapStart, sampleLen );
707 size_t int3Count = std::min<size_t>( 8, gapLen / 3 );
709 for(
size_t i = 0; i < int3Count; i++ )
712 int3Vals += wxT(
"," );
714 int3Vals += wxString::Format( wxT(
"%d" ),
719 size_t int4Count = std::min<size_t>( 6, gapLen / 4 );
721 for(
size_t i = 0; i < int4Count; i++ )
724 int4Vals += wxT(
"," );
726 int4Vals += wxString::Format( wxT(
"%d" ),
731 wxT(
"DipTrace: zone[%d] gap start=0x%06zX end=0x%06zX len=%lu int3=[%s] int4=[%s] hex[%lu]=[%s]" ),
732 aZoneIndex, aGapStart, aGapEnd,
static_cast<unsigned long>( gapLen ), int3Vals, int4Vals,
733 static_cast<unsigned long>( sampleLen ), hex );
737static void DumpZoneTail(
int aZoneIndex,
size_t aTailStart,
size_t aSearchEnd,
const uint8_t* aData )
742 size_t tailLen = std::min<size_t>( aSearchEnd - aTailStart, 256 );
743 size_t sampleLen = std::min<size_t>( tailLen, 96 );
744 wxString hex =
BytesToHex( aData + aTailStart, sampleLen );
747 size_t int3Count = std::min<size_t>( 8, tailLen / 3 );
749 for(
size_t i = 0; i < int3Count; i++ )
752 int3Vals += wxT(
"," );
754 int3Vals += wxString::Format( wxT(
"%d" ),
759 size_t int4Count = std::min<size_t>( 6, tailLen / 4 );
761 for(
size_t i = 0; i < int4Count; i++ )
764 int4Vals += wxT(
"," );
766 int4Vals += wxString::Format( wxT(
"%d" ),
771 wxT(
"DipTrace: zone[%d] tail start=0x%06zX end=0x%06zX len=%lu int3=[%s] int4=[%s] hex[%lu]=[%s]" ),
772 aZoneIndex, aTailStart, aTailStart + tailLen,
static_cast<unsigned long>( tailLen ), int3Vals, int4Vals,
773 static_cast<unsigned long>( sampleLen ), hex );
781static size_t StringFieldSize(
const uint8_t* aData,
size_t aDataSize,
size_t aPos,
int aVersion )
785 if( aPos + 3 > aDataSize )
790 if( byteCount < 0 || byteCount >
MAX_STRING_CHARS || aPos + 3 + byteCount > aDataSize )
793 return 3 +
static_cast<size_t>( byteCount );
797 if( aPos + 2 > aDataSize )
800 int charCount = (
static_cast<int>( aData[aPos] ) << 8 )
801 |
static_cast<int>( aData[aPos + 1] );
804 || aPos + 2 +
static_cast<size_t>( charCount ) * 2 > aDataSize )
809 return 2 +
static_cast<size_t>( charCount ) * 2;
819 size_t aPos,
int aVersion,
820 wxString& aOut,
size_t& aNewPos )
825 if( aPos + 3 > aDataSize )
828 const uint8_t* b = aData + aPos;
829 int byteCount =
static_cast<int>( (
static_cast<int>( b[0] ) << 16 )
830 | (
static_cast<int>( b[1] ) << 8 )
831 |
static_cast<int>( b[2] ) ) -
INT3_BIAS;
840 if( byteCount < 0 || byteCount > 500
841 || aPos + 3 +
static_cast<size_t>( byteCount ) > aDataSize )
846 for(
int i = 0; i < byteCount; i++ )
848 char c =
static_cast<char>( aData[aPos + 3 + i] );
850 if( !( ( c >= 0x20 && c < 0x7F ) || c ==
'\r' || c ==
'\n' || c ==
'\t' ) )
854 aOut = wxString::FromAscii(
reinterpret_cast<const char*
>( aData + aPos + 3 ),
856 aNewPos = aPos + 3 + byteCount;
862 if( aPos + 2 > aDataSize )
865 uint16_t cc = (
static_cast<uint16_t
>( aData[aPos] ) << 8 ) | aData[aPos + 1];
874 if( cc > 500 || aPos + 2 +
static_cast<size_t>( cc ) * 2 > aDataSize )
879 size_t base = aPos + 2;
881 for( uint16_t i = 0; i < cc; i++ )
883 uint16_t ch = (
static_cast<uint16_t
>( aData[base + i * 2] ) << 8 )
884 | aData[base + i * 2 + 1];
885 wxChar wch =
static_cast<wxChar
>( ch );
887 if( !wxIsprint( wch ) && wch !=
'\r' && wch !=
'\n' && wch !=
'\t' )
894 aNewPos = aPos + 2 +
static_cast<size_t>( cc ) * 2;
901 const uint8_t* aPattern,
size_t aPatternLen,
902 size_t aStart,
size_t aEnd )
904 std::vector<size_t> offsets;
906 if( aEnd == 0 || aEnd > aDataSize )
911 while( pos + aPatternLen <= aEnd && offsets.size() < 100000 )
913 const uint8_t* found = std::search( aData + pos, aData + aEnd,
914 aPattern, aPattern + aPatternLen );
916 if( found == aData + aEnd )
919 size_t idx =
static_cast<size_t>( found - aData );
920 offsets.push_back( idx );
971 oxMin = std::min( oxMin, v.x );
972 oxMax = std::max( oxMax, v.x );
973 oyMin = std::min( oyMin, v.y );
974 oyMax = std::max( oyMax, v.y );
978 wxT(
"DipTrace: board bbox=(%d,%d)-(%d,%d), outline verts=%zu bounds=(%d,%d)-(%d,%d)" ),
983 wxLogTrace(
traceDiptraceIo, wxT(
"DipTrace: board bbox=(%d,%d)-(%d,%d), no parsed outline vertices" ),
996 wxT(
"DipTrace: post-component sections parsed; inferring routing-ref pad nets" ) );
999 wxLogTrace(
traceDiptraceIo, wxT(
"DipTrace: applying board settings" ) );
1001 wxLogTrace(
traceDiptraceIo, wxT(
"DipTrace: creating board outline" ) );
1006 size_t footprintCompCount = 0;
1007 size_t standaloneViaCompCount = 0;
1011 if(
comp.isStandaloneVia )
1012 standaloneViaCompCount++;
1014 footprintCompCount++;
1018 wxT(
"DipTrace: creating %zu footprints (skipping %zu standalone-via components)" ),
1019 footprintCompCount, standaloneViaCompCount );
1023 if(
comp.isStandaloneVia )
1040 size_t compsWithPads = 0;
1041 size_t compsWithShapes = 0;
1045 if( !
comp.pads.empty() )
1048 if( !
comp.shapes.empty() )
1053 wxT(
"DipTrace v%d: %zu components (%zu with pads, %zu with shapes), "
1054 "%zu nets, %zu track chains, %zu zones" ),
1062 catch(
const std::exception& e )
1065 _(
"DipTrace parse error at offset 0x%06zX: %s" ),
1066 m_reader.GetOffset(), wxString::FromUTF8( e.what() ) ) );
1073 switch( aDipTraceLayer )
1081 case 8:
return F_Fab;
1082 case 9:
return B_Fab;
1086 if( aDipTraceLayer == 0 )
1089 if( aDipTraceLayer == 1 )
1092 if( aDipTraceLayer >= 14 && aDipTraceLayer <= 44 )
1094 int innerIdx = aDipTraceLayer - 14;
1096 if( innerIdx <= 30 )
1112 int ordinal = it->second;
1113 int copperCount = std::max( 2,
static_cast<int>(
m_layers.size() ) );
1118 if( ordinal >= copperCount - 1 )
1130 return static_cast<int>(
static_cast<int64_t
>( aDipTraceCoord ) * 100 / 3 );
1146 uint8_t magicLen =
m_reader.ReadByte();
1148 if( magicLen != 7 && magicLen != 11 )
1151 _(
"DipTrace: invalid magic length %u (expected 7 or 11)" ), magicLen ) );
1154 std::array<uint8_t, 11> magic = {};
1155 m_reader.ReadBytes( magic.data(), magicLen );
1157 if( std::memcmp( magic.data(),
"DTBOARD", 7 ) != 0 )
1159 THROW_IO_ERROR(
_(
"DipTrace: not a valid .dip board file (bad magic)" ) );
1167 std::string magicSuffix(
reinterpret_cast<const char*
>( magic.data() + 7 ),
1170 if( magicSuffix.size() != 4
1171 || std::isdigit(
static_cast<unsigned char>( magicSuffix[0] ) ) == 0
1172 || magicSuffix[1] !=
'.'
1173 || std::isdigit(
static_cast<unsigned char>( magicSuffix[2] ) ) == 0
1174 || std::isdigit(
static_cast<unsigned char>( magicSuffix[3] ) ) == 0 )
1176 THROW_IO_ERROR(
_(
"DipTrace: invalid legacy board version suffix" ) );
1179 int parsedMinor = ( magicSuffix[2] -
'0' ) * 10 + ( magicSuffix[3] -
'0' );
1222 int vertexCount =
m_reader.ReadInt3();
1226 for(
int i = 0; i < vertexCount; i++ )
1246 for(
int i = 0; i < 4; i++ )
1249 for(
int i = 0; i < 3; i++ )
1256 int layerCount =
m_reader.ReadInt3();
1261 for(
int i = 0; i < layerCount; i++ )
1278 int ordinal =
static_cast<int>(
m_layers.size() );
1288 for(
int i = 0; i < 6; i++ )
1301 uint8_t legacyPadding[12];
1302 m_reader.ReadBytes( legacyPadding,
sizeof( legacyPadding ) );
1321 const uint8_t* data =
m_reader.GetData();
1324 && data[
next + 1] <= 100 )
1331 m_reader.ReadBytes( padTail,
sizeof( padTail ) );
1334 int fieldCOrGroupCount =
m_reader.ReadInt3();
1342 int patternGroupCount =
m_reader.ReadInt3();
1351 if( aGroupCount < 0 || aGroupCount > 10000 )
1354 _(
"DipTrace: invalid pattern-name group count %d at offset 0x%06zX" ),
1355 aGroupCount,
m_reader.GetOffset() - 3 ) );
1358 for(
int i = 0; i < aGroupCount; i++ )
1362 int blockCount =
m_reader.ReadInt3();
1364 if( blockCount < 0 || blockCount > 10000 )
1367 _(
"DipTrace: invalid pattern-name block count %d" ), blockCount ) );
1370 for(
int j = 0; j < blockCount; j++ )
1381 if( aGroupCount < 0 || aGroupCount > 10000 )
1384 _(
"DipTrace: invalid pattern-style group count %d at offset 0x%06zX" ),
1385 aGroupCount,
m_reader.GetOffset() - 3 ) );
1390 for(
int i = 0; i < aGroupCount; i++ )
1395 m_reader.ReadBytes( color,
sizeof( color ) );
1401 int entryCount =
m_reader.ReadInt3();
1403 if( entryCount < 0 || entryCount > 10000 )
1406 _(
"DipTrace: invalid pattern-style entry count %d" ), entryCount ) );
1426 _(
"DipTrace: invalid implicit pattern-style entry count %d" ),
1436 bool dumpRuleSets =
EnvFlagEnabled(
"KICAD_DIPTRACE_DUMP_RULESETS" );
1448 if(
name.StartsWith( wxT(
"ViaStyle" ) ) )
1469 int ruleSetCount =
m_reader.ReadInt3();
1472 for(
int i = 0; i < ruleSetCount; i++ )
1474 wxString setName =
m_reader.ReadString();
1475 int setFieldA =
m_reader.ReadInt3();
1476 uint8_t flags[4] = { 0, 0, 0, 0 };
1478 for(
int f = 0; f < 4; f++ )
1481 int blockCount =
m_reader.ReadInt3();
1486 wxT(
"DipTrace: ruleset[%d] '%s' fieldA=%d flags=[%u,%u,%u,%u] blocks=%d" ), i, setName,
1487 setFieldA,
static_cast<unsigned int>( flags[0] ),
static_cast<unsigned int>( flags[1] ),
1488 static_cast<unsigned int>( flags[2] ),
static_cast<unsigned int>( flags[3] ), blockCount );
1491 for(
int b = 0; b < blockCount; b++ )
1493 std::array<int, 26> blockValues;
1495 for(
int v = 0; v < 25; v++ )
1496 blockValues[v] =
m_reader.ReadInt4();
1498 blockValues[25] = 0;
1505 int extraCount =
m_reader.ReadInt3();
1507 if( extraCount < 0 || extraCount > 10000 )
1510 _(
"DipTrace: invalid design-rule extra count %d" ), extraCount ) );
1513 for(
int e = 0; e < extraCount; e++ )
1517 m_reader.ReadBytes( rawPad,
sizeof( rawPad ) );
1521 if( i + 1 < ruleSetCount )
1529 static const uint8_t marker[] = { 0x4D, 0x7C, 0x6D, 0x00 };
1530 uint8_t
actual[
sizeof( marker )] = {};
1531 size_t markerOffset =
m_reader.GetOffset();
1535 if( std::memcmp(
actual, marker,
sizeof( marker ) ) != 0 )
1538 _(
"DipTrace: invalid ruleset transition marker at 0x%06zX" ), markerOffset ) );
1546 const uint8_t* data =
m_reader.GetData();
1549 && data[
next] == 0x01 && data[
next + 1] == 0x00 && data[
next + 2] == 0x14
1550 && data[
next + 3] == 0x89 && data[
next + 4] == 0x03 )
1553 m_reader.ReadBytes( suffix,
sizeof( suffix ) );
1563std::vector<std::pair<size_t, size_t>>
1566 const uint8_t* data =
m_reader.GetData();
1567 size_t fileSize =
m_reader.GetFileSize();
1569 static const int STRING_OFFSETS[] = { 14, 15, 16, 17, 18, 19, 20 };
1571 auto isBoundaryCoreAt = [&](
size_t aPos ) ->
bool
1578 auto stringOkAt = [&](
size_t aPos ) ->
bool
1588 auto cleanDeltaAt = [&](
size_t aBoundary ) ->
int
1590 for(
int d : STRING_OFFSETS )
1597 && s.length() >= 1 )
1611 int globalDelta = 14;
1615 if( !isBoundaryCoreAt( p ) )
1618 int d = cleanDeltaAt( p );
1628 std::vector<std::pair<size_t, size_t>> out;
1639 auto nextBoundaryAfter = [&](
size_t aFrom ) ->
size_t
1643 if( isBoundaryCoreAt( cand )
1644 && stringOkAt( cand +
static_cast<size_t>( globalDelta ) ) )
1657 out.emplace_back( b, b +
static_cast<size_t>( globalDelta ) );
1659 size_t next = nextBoundaryAfter( b );
1669 int declaredCount = ( parsedEnd + 3 <= fileSize ) ?
ReadInt3At( data, parsedEnd ) : -1;
1671 if( declaredCount > 0 && declaredCount <= 10000
1672 &&
static_cast<int>( out.size() ) != declaredCount )
1691 size_t projLib =
m_reader.FindString( wxT(
"Project Libraries" ), 0, 0 );
1704 struct ValidatedBoundary
1706 size_t boundaryOffset;
1710 std::vector<ValidatedBoundary> validated;
1711 bool fieldWalked =
false;
1717 std::vector<std::pair<size_t, size_t>> walk =
1735 std::set<size_t> stdSet( stdOffsets.begin(), stdOffsets.end() );
1736 std::vector<size_t> pureAlt;
1738 for(
size_t off : altOffsets )
1740 if( stdSet.find( off ) == stdSet.end() )
1741 pureAlt.push_back( off );
1745 std::vector<size_t> allBoundaries( stdOffsets );
1746 allBoundaries.insert( allBoundaries.end(), pureAlt.begin(), pureAlt.end() );
1747 std::sort( allBoundaries.begin(), allBoundaries.end() );
1750 allBoundaries.erase( std::unique( allBoundaries.begin(), allBoundaries.end() ),
1751 allBoundaries.end() );
1753 if( allBoundaries.empty() )
1759 static const int STRING_OFFSETS[] = { 14, 15, 16, 17, 18, 19, 20 };
1760 int stringDelta = 14;
1762 for(
size_t bOff : allBoundaries )
1764 if( parsedEnd > 50 && bOff < parsedEnd - 50 )
1769 for(
int delta : STRING_OFFSETS )
1771 size_t candidate = bOff +
delta;
1778 if( str.length() >= 1 )
1780 stringDelta =
delta;
1792 for(
size_t bOff : allBoundaries )
1794 if( parsedEnd > 50 && bOff < parsedEnd - 50 )
1797 size_t candidate = bOff + stringDelta;
1804 validated.push_back( { bOff, candidate } );
1811 if( validated.size() <= 1 && allBoundaries.size() > 1 )
1815 for(
size_t bOff : allBoundaries )
1817 if( parsedEnd > 50 && bOff < parsedEnd - 50 )
1822 for(
int delta : STRING_OFFSETS )
1824 size_t candidate = bOff +
delta;
1831 validated.push_back( { bOff, candidate } );
1837 if( !found && bOff + stringDelta + 3 <
m_reader.GetFileSize() )
1838 validated.push_back( { bOff, bOff + stringDelta } );
1844 if( !walk.empty() && walk.size() == validated.size() )
1848 for(
size_t i = 0; i < walk.size(); i++ )
1850 if( walk[i].first != validated[i].boundaryOffset )
1852 fieldWalked =
false;
1859 if( validated.empty() )
1861 wxLogWarning(
_(
"DipTrace: no validated component boundaries found" ) );
1866 int consecutiveFailures = 0;
1867 bool seenSuccess =
false;
1868 static constexpr int MAX_CONSECUTIVE_FAILURES = 3;
1869 static constexpr int MAX_COMPONENTS = 10000;
1871 for(
size_t vi = 0; vi < validated.size(); vi++ )
1873 if(
static_cast<int>(
m_components.size() ) >= MAX_COMPONENTS )
1876 const ValidatedBoundary& vb = validated[vi];
1877 m_reader.SetOffset( vb.stringStart );
1880 comp.boundaryOffset = vb.boundaryOffset;
1881 comp.stringStartOffset = vb.stringStart;
1890 size_t regionEnd = ( vi + 1 < validated.size() )
1891 ? validated[vi + 1].boundaryOffset
1893 comp.regionEndOffset = regionEnd;
1908 consecutiveFailures = 0;
1915 consecutiveFailures++;
1917 if( consecutiveFailures >= MAX_CONSECUTIVE_FAILURES )
1932 const uint8_t* data =
m_reader.GetData();
1933 size_t dataSize =
m_reader.GetFileSize();
1935 static constexpr double RAD_FIXED_TO_DEG = ( 180.0 /
M_PI ) / 1.0e4;
1936 static constexpr int MAX_ANGLE_FIXED = 200000;
1945 auto isThreeInt3 = [&](
size_t aPos ) ->
bool
1947 return aPos + 9 <= dataSize
1948 && data[aPos] == 0x0F && data[aPos + 1] == 0x42
1949 && data[aPos + 3] == 0x0F && data[aPos + 4] == 0x42
1950 && data[aPos + 6] == 0x0F && data[aPos + 7] == 0x42;
1953 std::vector<std::pair<size_t, double>> headers;
1955 for(
size_t off = 4; off + 32 <= dataSize; off++ )
1957 bool full = data[off] == 0x01 && data[off + 1] == 0x01 && isThreeInt3( off + 2 );
1958 bool compact = data[off] == 0x00 && data[off + 1] == 0x00 && isThreeInt3( off + 2 )
1959 && off + 22 <= dataSize
1960 && std::memcmp( data + off + 11,
"\0\0\0\0\0\0\0\0\0", 9 ) == 0
1961 && data[off + 20] == 0x0F && data[off + 21] == 0x42;
1963 if( !full && !compact )
1968 if( fixed >= -MAX_ANGLE_FIXED && fixed <= MAX_ANGLE_FIXED )
1969 headers.emplace_back( off,
static_cast<double>( fixed ) * RAD_FIXED_TO_DEG );
1972 if( headers.empty() )
1978 std::map<wxString, double> refdesAngle;
1980 for(
size_t off = 0; off + 4 <= dataSize; off++ )
1982 size_t len = (
static_cast<size_t>( data[off] ) << 8 ) | data[off + 1];
1984 if( len < 1 || len > 12 || off + 2 + 2 * len > dataSize )
1988 bool asciiUtf16 =
true;
1989 bool firstAlpha =
false;
1990 bool hasDigit =
false;
1992 for(
size_t c = 0; c < len; c++ )
1994 uint8_t hi = data[off + 2 + 2 * c];
1995 uint8_t lo = data[off + 3 + 2 * c];
1997 if( hi != 0 || lo < 0x20 || lo > 0x7E )
2004 firstAlpha = std::isalpha( lo );
2006 if( std::isdigit( lo ) )
2009 refdes +=
static_cast<char>( lo );
2012 if( !asciiUtf16 || !firstAlpha || !hasDigit )
2016 auto it = std::upper_bound( headers.begin(), headers.end(), off,
2017 [](
size_t aPos,
const std::pair<size_t, double>& aHdr )
2018 { return aPos < aHdr.first; } );
2020 if( it == headers.begin() )
2023 refdesAngle.emplace( wxString::FromUTF8( refdes ), ( it - 1 )->second );
2030 auto it = refdesAngle.find(
comp.refdes );
2032 if( it != refdesAngle.end() )
2034 comp.placementAngleDeg = it->second;
2035 comp.hasPlacementAngle =
true;
2040 wxLogTrace(
traceDiptraceIo, wxT(
"DipTrace: keyed %zu exact placement angles by refdes" ), applied );
2070 bool fatalHeaderError =
false;
2074 int quarterTurns = 0;
2092 aComp.
flags.resize( 4 );
2094 for(
int i = 0; i < 4; i++ )
2098 for(
int i = 0; i < 4; i++ )
2100 if( aComp.
flags[i] > 10 )
2107 fatalHeaderError =
true;
2109 _(
"DipTrace: invalid component flag byte %u at boundary 0x%06zX" ),
2110 static_cast<unsigned int>( aComp.
flags[i] ), aBoundaryOffset ) );
2120 size_t posXPos =
m_reader.GetOffset();
2122 size_t posYPos =
m_reader.GetOffset();
2124 size_t rotPos =
m_reader.GetOffset();
2126 size_t fieldCPos =
m_reader.GetOffset();
2130 uint8_t sep1 =
m_reader.ReadByte();
2135 size_t fieldDPos =
m_reader.GetOffset();
2139 uint8_t sep2 =
m_reader.ReadByte();
2140 uint8_t sep3 =
m_reader.ReadByte();
2142 if( sep2 > 1 || sep3 != 0 )
2175 fieldCPos, fieldDPos );
2179 if( fatalHeaderError )
2200 const uint8_t* data =
m_reader.GetData();
2201 size_t dataSize =
m_reader.GetFileSize();
2203 if( aRegionEnd > dataSize )
2204 aRegionEnd = dataSize;
2209 auto isPad1At = [&](
size_t aPos ) ->
bool
2211 if( aPos < aRegionStart || aPos + PAD_PRE_HEADER_SIZE + 4 > aRegionEnd )
2234 size_t labelPos = namePos + nameLen;
2240 size_t dimPos = labelPos + labelLen;
2248 return w > 0 && h > 0 && w <= 10000000 && h <= 10000000;
2251 size_t chainPos = 0;
2262 if( isPad1At( candidate ) )
2263 chainPos = candidate;
2270 static const uint8_t IDX1_PATTERN[] = { 0x0F, 0x42, 0x41 };
2272 std::vector<size_t> matches =
FindAllBoundaries( data, dataSize, IDX1_PATTERN, 3,
2273 aRegionStart, aRegionEnd );
2275 for(
size_t pos : matches )
2277 if( isPad1At( pos ) )
2290 wxLogTrace(
traceDiptraceIo, wxT(
"DipTrace: pad 1 not found in region 0x%06zX-0x%06zX for '%s'" ),
2298 for(
int padNum = 1; padNum < 500; padNum++ )
2304 int padNetIndex =
ReadInt3At( data, chainPos + 3 );
2306 int padY =
ReadInt4At( data, chainPos + 10 );
2309 if( padIndex != padNum )
2321 if( nameFieldLen == 0 )
2324 size_t labelPos = namePos + nameFieldLen;
2327 if( labelFieldLen == 0 )
2330 size_t dimPos = labelPos + labelFieldLen;
2338 int drillH =
ReadInt4At( data, dimPos + 12 );
2340 if( padW <= 0 || padH <= 0 || padW > 10000000 || padH > 10000000 )
2350 int padStyleC =
ReadInt3At( data, postDimPos + 4 );
2353 pad.index = padIndex;
2354 pad.netIndex = padNetIndex;
2359 pad.drillWidth = drillW;
2360 pad.drillHeight = drillH;
2361 pad.style = padStyleC;
2362 pad.mountType = data[postDimPos + 3];
2364 size_t afterName = 0;
2367 size_t afterLabel = 0;
2372 if(
pad.number.IsEmpty() && !
pad.label.IsEmpty() )
2376 if( padStyleC == 3 )
2378 int vertexCount =
ReadInt3At( data, postDimPos + 8 );
2380 if( vertexCount > 0 && vertexCount <= 200
2387 for(
int vi = 0; vi < vertexCount; vi++ )
2391 pad.polygonVertices.emplace_back( vx, vy );
2401 if( postDimPos + postDimSize > aRegionEnd )
2404 pad.orientClass = data[postDimPos + postDimSize - 1];
2409 chainPos = postDimPos + postDimSize;
2482 size_t aSearchEnd, std::vector<DT_MOUNT_HOLE>& aHoles )
2484 static constexpr int MAX_REASONABLE_DIM = 50000000;
2485 static constexpr int MAX_HOLE_COUNT = 64;
2493 int countField =
ReadInt3At( aData, aBlockStart );
2494 int holeCount = countField - 2;
2496 if( holeCount <= 0 || holeCount > MAX_HOLE_COUNT )
2499 uint8_t headerFlag = aData[aBlockStart + 3];
2501 if( headerFlag > 1 )
2504 for(
int i = 0; i < 4; i++ )
2506 if(
ReadInt4At( aData, aBlockStart + 4 +
static_cast<size_t>( i ) * 4 ) != 0 )
2512 size_t holeEnd = holeStart + holesBytes;
2513 size_t termPos = holeEnd;
2519 std::vector<DT_MOUNT_HOLE> parsedHoles;
2520 parsedHoles.reserve(
static_cast<size_t>( holeCount ) );
2522 for(
int hi = 0; hi < holeCount; hi++ )
2525 uint8_t holeFlagA = aData[hp];
2526 uint8_t holeFlagB = aData[hp + 1];
2528 if( holeFlagA != 0 || holeFlagB > 1 )
2536 if(
std::abs( x ) > MAX_REASONABLE_DIM ||
std::abs( y ) > MAX_REASONABLE_DIM
2537 || outer <= 0 || outer > MAX_REASONABLE_DIM
2538 || drill <= 0 || drill > MAX_REASONABLE_DIM
2549 parsedHoles.push_back( hole );
2552 if( aData[termPos] != 0 || aData[termPos + 1] != 0 )
2555 for(
int i = 0; i < 4; i++ )
2557 if(
ReadInt4At( aData, trailerPos +
static_cast<size_t>( i ) * 4 ) != 0 )
2561 aHoles = std::move( parsedHoles );
2569 wxUnusedVar( aRegionStart );
2570 aComp.
holes.clear();
2575 const uint8_t* data =
m_reader.GetData();
2576 size_t dataSize =
m_reader.GetFileSize();
2578 if( aRegionEnd > dataSize )
2579 aRegionEnd = dataSize;
2581 size_t searchEnd = aRegionEnd;
2586 std::vector<size_t> candidates;
2594 if( shapeCount > 0 && shapeCount <= 500 )
2597 candidates.push_back( shapeStart +
static_cast<size_t>( shapeCount ) * recSize );
2606 if( shapeCount >= 3 && shapeCount <= 200 )
2609 +
static_cast<size_t>( shapeCount - 1 )
2617 std::vector<size_t> uniqueCandidates;
2618 uniqueCandidates.reserve( candidates.size() );
2620 for(
size_t pos : candidates )
2622 if( std::find( uniqueCandidates.begin(), uniqueCandidates.end(), pos ) == uniqueCandidates.end() )
2623 uniqueCandidates.push_back( pos );
2626 size_t decodedAt = 0;
2628 for(
size_t pos : uniqueCandidates )
2637 if( decodedAt == 0 )
2643 wxT(
"DipTrace: mount-holes ref=%s count=%zu off=0x%06zX "
2644 "padEnd=0x%06zX gap=%zu" ),
2656 const uint8_t* data =
m_reader.GetData();
2657 size_t dataSize =
m_reader.GetFileSize();
2659 if( aRegionEnd > dataSize )
2660 aRegionEnd = dataSize;
2666 if( aComp.
shapes.empty() )
2675 wxLogTrace(
traceDiptraceIo, wxT(
"DipTrace: no pad region end for shape finding in '%s'" ),
2684 wxLogTrace(
traceDiptraceIo, wxT(
"DipTrace: shape region beyond component bounds for '%s'" ),
2694 if( shapeCount <= 0 || shapeCount > 500 )
2698 size_t shapeEnd = shapeStart +
static_cast<size_t>( shapeCount ) * recSize;
2700 if( shapeEnd > aRegionEnd )
2703 for(
int i = 0; i < shapeCount; i++ )
2705 size_t rp = shapeStart +
static_cast<size_t>( i ) * recSize;
2737 aComp.
shapes.push_back( shape );
2745 const uint8_t* data =
m_reader.GetData();
2746 size_t dataSize =
m_reader.GetFileSize();
2748 if( aRegionEnd > dataSize )
2749 aRegionEnd = dataSize;
2751 auto isTahomaAt = [&](
size_t aPos ) ->
bool
2757 auto u16beAt = [&](
size_t aPos ) ->
int
2759 return (
static_cast<int>( data[aPos] ) << 8 ) |
static_cast<int>( data[aPos + 1] );
2766 std::vector<size_t> fontBlocks;
2767 bool fieldWalked =
false;
2775 if( preLabel >= 0 && preLabel <= 256 )
2780 while( isTahomaAt( bs ) )
2784 bool slot0InBounds = ( body + 3 <= aRegionEnd );
2786 for(
int n = 0; n <= 3; n++ )
2788 if( body +
static_cast<size_t>( n ) * 8 + 3 > aRegionEnd )
2791 int st =
ReadInt3At( data, body +
static_cast<size_t>( n ) * 8 );
2793 if( st == 0 || st == 1 || st == 2 || st == 3 || st == 5 || st == 6 || st == 7
2809 fontBlocks.push_back( bs );
2821 if( lcPos + 2 > aRegionEnd )
2827 int lc = u16beAt( lcPos );
2829 if( lc < 0 || lc > 256 )
2837 fontBlocks.push_back( bs );
2840 + 2 *
static_cast<size_t>( lc );
2846 fieldWalked = walkOk && !fontBlocks.empty();
2855 aRegionStart, aRegionEnd );
2858 if( fontBlocks.empty() )
2861 size_t shapesBefore = aComp.
shapes.size();
2863 for(
size_t bi = 0; bi < fontBlocks.size(); bi++ )
2865 size_t blockStart = fontBlocks[bi];
2866 size_t nextBoundary = ( bi + 1 < fontBlocks.size() ) ? fontBlocks[bi + 1]
2871 if( headerEnd + 19 > nextBoundary )
2875 int lineWidth =
ReadInt4At( data, metaStart + 18 );
2880 int layerIdx = ( blockStart >= 5 ) ?
ReadInt3At( data, blockStart - 5 ) : 0;
2882 size_t bodyPos = headerEnd;
2897 if( x1 == 0 && y1 == 0 && x2 == 0 && y2 == 0 )
2900 int shapeType =
ReadInt3At( data, bodyPos + 16 );
2907 shape.
width = lineWidth;
2908 shape.
layer = layerIdx;
2912 if( shapeType == 0 )
2916 else if( shapeType == 1 || shapeType == 5 )
2920 else if( shapeType == 3 )
2928 if( bodyPos + 43 <= nextBoundary )
2947 aComp.
shapes.push_back( shape );
2950 if( !fieldWalked && aComp.
shapes.size() > shapesBefore )
2958 wxUnusedVar( aRegionStart );
2963 const uint8_t* data =
m_reader.GetData();
2964 size_t dataSize =
m_reader.GetFileSize();
2966 if( aRegionEnd > dataSize )
2967 aRegionEnd = dataSize;
2975 if( shapeCount < 3 || shapeCount > 200 )
2981 if( shapeEnd > aRegionEnd )
2984 std::vector<DT_FP_SHAPE> decoded;
2985 decoded.reserve(
static_cast<size_t>( shapeCount ) );
2987 auto inNormRange = [](
int aVal ) ->
bool
2993 for(
int i = 1; i + 1 < shapeCount; i++ )
3003 if( !inNormRange( x1 ) || !inNormRange( y1 ) || !inNormRange( x2 ) || !inNormRange( y2 ) )
3007 shape.
width = width;
3024 if( !inNormRange( x3 ) || !inNormRange( y3 ) )
3040 decoded.push_back( shape );
3043 if( !decoded.empty() )
3044 aComp.
shapes.insert( aComp.
shapes.end(), decoded.begin(), decoded.end() );
3057 const uint8_t* data =
m_reader.GetData();
3059 size_t savedOffset =
m_reader.GetOffset();
3060 bool parsed =
false;
3062 auto tryParseTailAt = [&](
size_t aTailStart ) ->
bool
3087 m_reader.SetOffset( aTailStart + 11 );
3092 if( check1 != 0 || check2 != 0 )
3095 uint8_t sideFlag1 =
m_reader.ReadByte();
3097 uint8_t sideFlag2 =
m_reader.ReadByte();
3098 int orderIdx =
m_reader.ReadInt3();
3100 int refdesYOffset =
m_reader.ReadInt4();
3101 int valueYOffset =
m_reader.ReadInt4();
3102 uint8_t hasOffset =
m_reader.ReadByte();
3103 uint8_t tailTerm =
m_reader.ReadByte();
3108 if( hasOffset > 1 || tailTerm != 0 )
3111 if(
std::abs( refdesYOffset ) > 50000000 ||
std::abs( valueYOffset ) > 50000000 )
3125 if( sideFlag1 == 1 && sideFlag2 == 1 )
3129 orderIdx, refdesYOffset, valueYOffset, hasOffset, tailTerm );
3139 parsed = tryParseTailAt( canonicalTailStart );
3143 size_t dumpLen = std::min<size_t>( 96, aRegionEnd );
3144 size_t dumpStart = aRegionEnd - dumpLen;
3145 wxString hex =
BytesToHex( data + dumpStart, dumpLen );
3148 wxT(
"DipTrace: component-tail-missing ref=%s regionEnd=0x%06zX "
3149 "tailHexStart=0x%06zX len=%lu hex=[%s]" ),
3150 aComp.
refdes, aRegionEnd, dumpStart,
static_cast<unsigned long>( dumpLen ), hex );
3163 size_t postComp =
m_reader.GetOffset();
3168 size_t projLibOffset =
m_reader.FindString( wxT(
"Project Libraries" ), 0, 0 );
3169 size_t gapEnd = ( projLibOffset !=
NOT_FOUND ) ? projLibOffset :
m_reader.GetFileSize();
3185 size_t pos = aSearchStart;
3186 auto textRecordsLookValid =
3187 [&](
size_t aRecordStart,
int aCount,
size_t aSectionEnd ) ->
bool
3189 size_t savedOffset =
m_reader.GetOffset();
3190 m_reader.SetOffset( aRecordStart );
3194 for(
int ti = 0; ti < aCount; ti++ )
3209 int lineWidth =
m_reader.ReadInt4();
3212 if( lineWidth < 0 || lineWidth > 10000000 || layer < -100 || layer > 100 )
3213 throw std::runtime_error(
"invalid text metrics" );
3231 if( ti < aCount - 1 )
3237 if(
m_reader.GetOffset() > aSectionEnd )
3238 throw std::runtime_error(
"text section overrun" );
3244 catch(
const std::exception& )
3251 while( pos + 20 < aSearchEnd )
3258 size_t countPos = idx + 9;
3260 if( countPos + 5 > aSearchEnd )
3263 const uint8_t* data =
m_reader.GetData();
3264 const uint8_t* b = data + countPos;
3265 int countVal =
static_cast<int>( (
static_cast<int>( b[0] ) << 16 )
3266 | (
static_cast<int>( b[1] ) << 8 )
3267 |
static_cast<int>( b[2] ) ) -
INT3_BIAS;
3269 if( countVal >= 1 && countVal <= 1000 )
3271 uint8_t flag1 = data[countPos + 3];
3272 uint8_t flag2 = data[countPos + 4];
3274 if( flag1 == 1 && flag2 == 0 )
3276 size_t recordStart = countPos + 5;
3278 if( textRecordsLookValid( recordStart, countVal, aSearchEnd ) )
3294 for(
int ti = 0; ti < aCount; ti++ )
3339 if( ti < aCount - 1 )
3350 _(
"DipTrace: text object [%d] parse error: %s" ),
3380 aSearchStart, aSearchEnd );
3382 static constexpr int MAX_NETS = 10000;
3383 static constexpr int MAX_REASONABLE_WIDTH = 5000000;
3385 size_t firstNetSentinel = 0;
3387 for(
size_t sentOff : sentinelOffsets )
3389 if(
static_cast<int>(
m_nets.size() ) >= MAX_NETS )
3395 if( pos + 3 + 3 + 4 + 4 + 2 >
m_reader.GetFileSize() )
3401 bool acceptedNetRecord =
false;
3405 int netIndex =
m_reader.ReadInt3();
3413 if( field0 < 0 || field0 > 10 || netIndex < 0 || netIndex >= MAX_NETS )
3416 if( width1 < 0 || width1 > MAX_REASONABLE_WIDTH
3417 || width2 < 0 || width2 > MAX_REASONABLE_WIDTH )
3422 bool expectedNetIndex = netIndex ==
static_cast<int>(
m_nets.size() );
3423 acceptedNetRecord = expectedNetIndex;
3428 if( expectedNetIndex )
3431 _(
"DipTrace import: invalid net name for net index %d at "
3432 "offset 0x%06zX." ),
3433 netIndex,
m_reader.GetOffset() ) );
3439 net.
index = netIndex;
3445 wxLogTrace(
traceDiptraceIo, wxT(
"DipTrace: net idx=%d name=%s width1=%d width2=%d" ), netIndex,
name,
3451 if( acceptedNetRecord )
3458 if( firstNetSentinel == 0 )
3459 firstNetSentinel = sentOff;
3463 m_nets.push_back( std::move( net ) );
3471 bool netSectionFieldAnchored =
false;
3473 if( firstNetSentinel >= 5 && !
m_nets.empty() )
3476 ==
static_cast<int>(
m_nets.size() );
3479 if( !
m_nets.empty() && !netSectionFieldAnchored )
3482 size_t totalNodes = 0;
3483 size_t viaStyleNodes = 0;
3484 size_t routeFlagNodes = 0;
3485 size_t viaStyleAndRouteFlagNodes = 0;
3486 size_t routeFlagOnlyNodes = 0;
3487 size_t routeMode0Nodes = 0;
3488 size_t routeMode1Nodes = 0;
3489 size_t routeMode3Nodes = 0;
3490 size_t routeModeOtherNodes = 0;
3507 if( hasStyle && hasFlag )
3508 viaStyleAndRouteFlagNodes++;
3510 routeFlagOnlyNodes++;
3527 routeModeOtherNodes++;
3534 wxT(
"DipTrace: parsed %zu net names, %zu track chains, %zu nodes "
3535 "(viaStyle=%zu, routeFlag=%zu, both=%zu, flagOnly=%zu, "
3536 "routeMode[0]=%zu, routeMode[1]=%zu, routeMode[3]=%zu, routeMode[other]=%zu)" ),
3538 viaStyleAndRouteFlagNodes, routeFlagOnlyNodes, routeMode0Nodes, routeMode1Nodes, routeMode3Nodes,
3539 routeModeOtherNodes );
3559 size_t startPos =
m_reader.GetOffset();
3562 static constexpr size_t MAX_NET_BODY = 65536;
3563 size_t scanEnd = std::min( startPos + MAX_NET_BODY,
m_reader.GetFileSize() );
3567 startPos + 20, scanEnd );
3569 if( nextSentinel != std::string::npos )
3570 scanEnd = nextSentinel;
3572 const uint8_t* data =
m_reader.GetData();
3573 size_t fileSize =
m_reader.GetFileSize();
3575 size_t chainScanStart = startPos;
3576 static constexpr int MAX_REASONABLE_VIA_DIM = 5000000;
3577 static constexpr int MAX_PADREFS_PER_COMPONENT = 12;
3579 int componentCount =
static_cast<int>(
m_components.size() );
3580 int maxReasonablePadRefs = std::max( 512, componentCount * MAX_PADREFS_PER_COMPONENT );
3584 if( startPos + 22 <= scanEnd )
3586 int viaOuterDefault =
ReadInt4At( data, startPos );
3587 int viaDrillDefault =
ReadInt4At( data, startPos + 4 );
3588 uint8_t marker = data[startPos + 8];
3589 bool zeroBlock = std::all_of( data + startPos + 9, data + startPos + 16,
3590 []( uint8_t b ) {
return b == 0; } );
3591 int separator =
ReadInt3At( data, startPos + 16 );
3592 int padRefCount =
ReadInt3At( data, startPos + 19 );
3594 if( marker <= 1 && zeroBlock && separator == 0 && padRefCount >= 0
3595 && padRefCount <= maxReasonablePadRefs )
3597 size_t refsStart = startPos + 22;
3598 size_t refsEnd = refsStart +
static_cast<size_t>( padRefCount ) * 6;
3600 if( refsEnd <= scanEnd )
3602 std::vector<DT_PAD_REF> parsedRefs;
3603 parsedRefs.reserve(
static_cast<size_t>( padRefCount ) );
3604 int rangeHitsBase0 = 0;
3605 int rangeHitsBase1 = 0;
3607 for(
int i = 0; i < padRefCount; i++ )
3609 size_t refPos = refsStart +
static_cast<size_t>( i ) * 6;
3611 int padIndex =
ReadInt3At( data, refPos + 3 );
3613 if( compIndex >= 0 && padIndex > 0 )
3615 parsedRefs.push_back( { compIndex, padIndex } );
3617 if( compIndex >= 0 && compIndex < componentCount )
3620 if( compIndex >= 1 && compIndex <= componentCount )
3625 int requiredRangeHits = std::min( 4,
static_cast<int>( parsedRefs.size() ) );
3626 bool plausibleRefs = parsedRefs.empty()
3627 || std::max( rangeHitsBase0, rangeHitsBase1 ) >= requiredRangeHits;
3631 if( viaOuterDefault > 0 && viaOuterDefault <= MAX_REASONABLE_VIA_DIM )
3634 if( viaDrillDefault > 0 && viaDrillDefault <= MAX_REASONABLE_VIA_DIM )
3637 aNet.
padRefs = std::move( parsedRefs );
3638 chainScanStart = refsEnd;
3645 size_t pos = chainScanStart;
3651 if( chainPos == std::string::npos )
3656 if( headerStart + 6 > fileSize )
3659 const uint8_t* h = data + headerStart;
3660 int chainIdx = ( (
static_cast<int>( h[0] ) << 16 )
3661 | (
static_cast<int>( h[1] ) << 8 )
3662 |
static_cast<int>( h[2] ) ) -
INT3_BIAS;
3663 int nodeCount = ( (
static_cast<int>( h[3] ) << 16 )
3664 | (
static_cast<int>( h[4] ) << 8 )
3665 |
static_cast<int>( h[5] ) ) -
INT3_BIAS;
3667 auto firstNodeLooksPlausible = [&]() ->
bool
3669 size_t firstNode = headerStart + 6;
3674 const uint8_t* n = data + firstNode;
3676 int x =
static_cast<int>(
3677 (
static_cast<unsigned int>( n[0] ) << 24 )
3678 | (
static_cast<unsigned int>( n[1] ) << 16 )
3679 | (
static_cast<unsigned int>( n[2] ) << 8 )
3680 |
static_cast<unsigned int>( n[3] ) ) -
INT4_BIAS;
3682 int y =
static_cast<int>(
3683 (
static_cast<unsigned int>( n[4] ) << 24 )
3684 | (
static_cast<unsigned int>( n[5] ) << 16 )
3685 | (
static_cast<unsigned int>( n[6] ) << 8 )
3686 |
static_cast<unsigned int>( n[7] ) ) -
INT4_BIAS;
3688 int layer = ( (
static_cast<int>( n[8] ) << 16 )
3689 | (
static_cast<int>( n[9] ) << 8 )
3690 |
static_cast<int>( n[10] ) ) -
INT3_BIAS;
3692 int width =
static_cast<int>(
3693 (
static_cast<unsigned int>( n[14] ) << 24 )
3694 | (
static_cast<unsigned int>( n[15] ) << 16 )
3695 | (
static_cast<unsigned int>( n[16] ) << 8 )
3696 |
static_cast<unsigned int>( n[17] ) ) -
INT4_BIAS;
3698 int viaStyleIdx = ( (
static_cast<int>( n[27] ) << 16 )
3699 | (
static_cast<int>( n[28] ) << 8 )
3700 |
static_cast<int>( n[29] ) ) -
INT3_BIAS;
3702 int routeMode = ( (
static_cast<int>( n[37] ) << 16 )
3703 | (
static_cast<int>( n[38] ) << 8 )
3704 |
static_cast<int>( n[39] ) ) -
INT3_BIAS;
3706 return x > -100000000 && x < 100000000
3707 && y > -100000000 && y < 100000000
3708 && layer >= 0 && layer <= 50
3709 && width > 0 && width <= 5000000
3710 && viaStyleIdx >= -1 && viaStyleIdx <= 10000
3711 && routeMode >= 0 && routeMode <= 10
3715 if( chainIdx < 0 || nodeCount < 1 || nodeCount > 10000 )
3717 if( chainIdx >= 0 && firstNodeLooksPlausible() )
3720 _(
"DipTrace import: invalid route-chain node count %d for net '%s' "
3721 "at offset 0x%06zX." ),
3722 nodeCount, aNet.
name, headerStart + 3 ) );
3729 size_t nodesStart = headerStart + 6;
3730 size_t nodesEnd = nodesStart +
static_cast<size_t>( nodeCount ) *
TRACK_NODE_SIZE;
3732 if( nodesEnd > scanEnd )
3734 if( firstNodeLooksPlausible() )
3737 _(
"DipTrace import: route-chain node count %d for net '%s' overruns "
3738 "record at offset 0x%06zX." ),
3739 nodeCount, aNet.
name, headerStart + 3 ) );
3748 chain.nodes.reserve( nodeCount );
3752 for(
int i = 0; i < nodeCount; i++ )
3754 const uint8_t* n = data + nodesStart +
static_cast<size_t>( i ) *
TRACK_NODE_SIZE;
3759 node.
x =
static_cast<int>(
3760 (
static_cast<unsigned int>( n[0] ) << 24 )
3761 | (
static_cast<unsigned int>( n[1] ) << 16 )
3762 | (
static_cast<unsigned int>( n[2] ) << 8 )
3763 |
static_cast<unsigned int>( n[3] ) ) -
INT4_BIAS;
3766 node.y =
static_cast<int>(
3767 (
static_cast<unsigned int>( n[4] ) << 24 )
3768 | (
static_cast<unsigned int>( n[5] ) << 16 )
3769 | (
static_cast<unsigned int>( n[6] ) << 8 )
3770 |
static_cast<unsigned int>( n[7] ) ) -
INT4_BIAS;
3773 node.layer = ( (
static_cast<int>( n[8] ) << 16 )
3774 | (
static_cast<int>( n[9] ) << 8 )
3775 |
static_cast<int>( n[10] ) ) -
INT3_BIAS;
3778 node.width =
static_cast<int>(
3779 (
static_cast<unsigned int>( n[14] ) << 24 )
3780 | (
static_cast<unsigned int>( n[15] ) << 16 )
3781 | (
static_cast<unsigned int>( n[16] ) << 8 )
3782 |
static_cast<unsigned int>( n[17] ) ) -
INT4_BIAS;
3785 node.viaStyleIdx = ( (
static_cast<int>( n[27] ) << 16 )
3786 | (
static_cast<int>( n[28] ) << 8 )
3787 |
static_cast<int>( n[29] ) ) -
INT3_BIAS;
3788 node.viaOuterDiam =
static_cast<int>(
3789 (
static_cast<unsigned int>( n[18] ) << 24 )
3790 | (
static_cast<unsigned int>( n[19] ) << 16 )
3791 | (
static_cast<unsigned int>( n[20] ) << 8 )
3792 |
static_cast<unsigned int>( n[21] ) ) -
INT4_BIAS;
3793 node.routeFlag = n[22];
3794 node.viaDrillDiam =
static_cast<int>(
3795 (
static_cast<unsigned int>( n[30] ) << 24 )
3796 | (
static_cast<unsigned int>( n[31] ) << 16 )
3797 | (
static_cast<unsigned int>( n[32] ) << 8 )
3798 |
static_cast<unsigned int>( n[33] ) ) -
INT4_BIAS;
3801 node.routeMode = ( (
static_cast<int>( n[37] ) << 16 )
3802 | (
static_cast<int>( n[38] ) << 8 )
3803 |
static_cast<int>( n[39] ) ) -
INT3_BIAS;
3807 node.hasVia = ( node.viaStyleIdx >= 0 );
3810 if( node.width <= 0 || node.width > 5000000 )
3816 if( node.layer < 0 || node.layer > 50 )
3822 chain.nodes.push_back( node );
3825 if( valid && !
chain.nodes.empty() )
3839 size_t totalRefs = 0;
3842 totalRefs += net.padRefs.size();
3844 if( totalRefs == 0 )
3847 size_t maxReasonableRefs = std::max<size_t>( 5000,
m_components.size() * 64 );
3849 if( totalRefs > maxReasonableRefs )
3851 wxLogTrace(
traceDiptraceIo, wxT(
"DipTrace: skipping routing-ref pad inference (%zu refs exceeds %zu cap)" ),
3852 totalRefs, maxReasonableRefs );
3856 std::vector<std::unordered_map<int, size_t>> padPosByIndex(
m_components.size() );
3860 const std::vector<DT_PAD>& pads =
m_components[c].pads;
3861 auto& padMap = padPosByIndex[c];
3863 padMap.reserve( pads.size() );
3865 for(
size_t p = 0; p < pads.size(); p++ )
3866 padMap.emplace( pads[p].index, p );
3877 auto scoreBase = [&](
int aBase ) -> SCORE
3890 if( compPos < 0 || compPos >=
static_cast<int>(
m_components.size() ) )
3893 const auto& padMap = padPosByIndex[compPos];
3894 auto it = padMap.find( ref.
padIndex );
3896 if( it == padMap.end() )
3902 if(
pad.netIndex < 0 )
3910 SCORE scoreBase0 = scoreBase( 0 );
3911 SCORE scoreBase1 = scoreBase( 1 );
3913 if( scoreBase0.hits == 0 && scoreBase1.hits == 0 )
3916 if( scoreBase0.hits == scoreBase1.hits && scoreBase0.fillable == scoreBase1.fillable )
3919 SCORE best = scoreBase0;
3921 if( scoreBase1.hits > scoreBase0.hits
3922 || ( scoreBase1.hits == scoreBase0.hits && scoreBase1.fillable > scoreBase0.fillable ) )
3939 if( compPos < 0 || compPos >=
static_cast<int>(
m_components.size() ) )
3942 auto& padMap = padPosByIndex[compPos];
3943 auto it = padMap.find( ref.
padIndex );
3945 if( it == padMap.end() )
3950 if(
pad.netIndex < 0 )
3952 pad.netIndex = net.index;
3955 else if(
pad.netIndex != net.index )
3962 if( assigned > 0 || conflicts > 0 )
3965 wxT(
"DipTrace: routing-ref pad net inference (base=%d): %d refs, %d hits, "
3966 "%d assigned, %d conflicts" ),
3967 best.base, best.refs, best.hits, assigned, conflicts );
3980 const uint8_t* data =
m_reader.GetData();
3981 size_t fileSize =
m_reader.GetFileSize();
3983 if( aSearchEnd > fileSize )
3984 aSearchEnd = fileSize;
3986 static constexpr int MAX_ZONES = 100;
3987 static constexpr int MAX_VERTICES = 50000;
3988 static constexpr int MAX_REASONABLE_DIM = 5000000;
4004 oxMin = std::min( oxMin, v.x );
4005 oxMax = std::max( oxMax, v.x );
4006 oyMin = std::min( oyMin, v.y );
4007 oyMax = std::max( oyMax, v.y );
4010 bboxXMin = oxMin - 5000000;
4011 bboxXMax = oxMax + 5000000;
4012 bboxYMin = oyMin - 5000000;
4013 bboxYMax = oyMax + 5000000;
4016 auto headerLooksPlausible = [&](
size_t aPos,
bool aCheckVertexSamples ) ->
bool
4018 if( aPos + 30 > aSearchEnd )
4022 int flags1 = data[aPos + 3];
4023 int flags3 = data[aPos + 5];
4026 int minimumArea =
ReadInt4At( data, aPos + 14 );
4027 int separator =
ReadInt3At( data, aPos + 18 );
4030 int vtxCount =
ReadInt3At( data, aPos + 27 );
4032 if( fieldA < 0 || fieldA > 10000 )
4035 if( fieldB < -1 || fieldB > 10000 )
4038 if( flags1 > 2 || flags3 > 2 )
4044 if( minWidth <= 0 || minWidth > MAX_REASONABLE_DIM )
4047 if( minimumArea < 0 || minimumArea > MAX_REASONABLE_DIM )
4053 if( layer < -10 || layer > 100 )
4056 if( vtxCount < 3 || vtxCount > MAX_VERTICES )
4059 size_t vtxStart = aPos + 30;
4060 size_t vtxEnd = vtxStart +
static_cast<size_t>( vtxCount ) * 8;
4062 if( vtxEnd > aSearchEnd )
4065 if( !aCheckVertexSamples )
4068 int sampleCount = std::min( 3, vtxCount );
4070 auto vertexInBounds = [&](
size_t aVertexPos ) ->
bool
4074 return x >= bboxXMin && x <= bboxXMax && y >= bboxYMin && y <= bboxYMax;
4077 for(
int i = 0; i < sampleCount; i++ )
4079 size_t vp = vtxStart +
static_cast<size_t>( i ) * 8;
4081 if( !vertexInBounds( vp ) )
4085 size_t lastVp = vtxStart +
static_cast<size_t>( vtxCount - 1 ) * 8;
4087 return vertexInBounds( lastVp );
4090 auto headerHasZoneSectionShape = [&](
size_t aPos ) ->
bool
4092 if( aPos + 30 > aSearchEnd )
4096 int flags1 = data[aPos + 3];
4097 int flags3 = data[aPos + 5];
4098 int separator =
ReadInt3At( data, aPos + 18 );
4101 int vtxCount =
ReadInt3At( data, aPos + 27 );
4103 return fieldA >= 0 && fieldA <= 10000
4104 && fieldB >= -1 && fieldB <= 10000
4105 && flags1 <= 2 && flags3 <= 2
4107 && layer >= -10 && layer <= 100
4108 && vtxCount >= 3 && vtxCount <= MAX_VERTICES
4109 && aPos + 30 +
static_cast<size_t>( vtxCount ) * 8 <= aSearchEnd;
4112 auto parseZoneTrailer = [&](
DT_ZONE& aZone,
size_t aSearchStartPos,
size_t aSearchEndPos,
4113 int aZoneIndex ) ->
void
4121 static constexpr size_t TRAILER_LEN = 28;
4122 static constexpr size_t STYLE_BLOCK_LEN = 14;
4123 static constexpr int CACHED_RECORD_LEN = 23;
4125 if( aSearchEndPos <= aSearchStartPos || aSearchEndPos - aSearchStartPos < TRAILER_LEN )
4128 size_t lastStart = aSearchEndPos - TRAILER_LEN;
4130 for(
size_t trailerPos = lastStart + 1; trailerPos-- > aSearchStartPos; )
4133 int zeroInt4 =
ReadInt4At( data, trailerPos + 3 );
4134 int boardClr =
ReadInt4At( data, trailerPos + 7 );
4135 uint8_t islandR = data[trailerPos + 11];
4136 uint8_t islandI = data[trailerPos + 12];
4137 uint8_t islandC = data[trailerPos + 13];
4138 int zoneId =
ReadInt3At( data, trailerPos + 14 );
4139 uint8_t viaDir = data[trailerPos + 17];
4140 uint8_t smdSep = data[trailerPos + 18];
4141 int smdSpokeMode =
ReadInt3At( data, trailerPos + 19 );
4142 int smdSpokeW =
ReadInt4At( data, trailerPos + 22 );
4143 uint8_t ratMode = data[trailerPos + 26];
4144 uint8_t doneFlag = data[trailerPos + 27];
4146 if( lead < 0 || lead > 100000 || zeroInt4 != -
INT4_BIAS )
4149 if( boardClr < 0 || boardClr > MAX_REASONABLE_DIM )
4152 if( islandR > 1 || islandI > 1 || islandC > 1 )
4155 if( zoneId < 0 || zoneId > 100000 )
4158 if( viaDir > 1 || smdSep > 1 )
4161 if( smdSpokeMode < 0 || smdSpokeMode > 4 )
4164 if( smdSpokeW <= 0 || smdSpokeW > MAX_REASONABLE_DIM )
4173 size_t payloadStart = aSearchStartPos;
4175 if( aSearchStartPos + STYLE_BLOCK_LEN <= aSearchEndPos )
4177 int styleLead =
ReadInt3At( data, aSearchStartPos );
4178 int styleSpokeMode =
ReadInt3At( data, aSearchStartPos + 3 );
4179 int styleLineSpacing =
ReadInt4At( data, aSearchStartPos + 6 );
4180 int styleSpokeWidth =
ReadInt4At( data, aSearchStartPos + 10 );
4183 && styleSpokeMode >= 0 && styleSpokeMode <= 4
4184 && styleLineSpacing > 0 && styleLineSpacing <= MAX_REASONABLE_DIM
4185 && styleSpokeWidth > 0 && styleSpokeWidth <= MAX_REASONABLE_DIM )
4187 payloadStart += STYLE_BLOCK_LEN;
4191 int cachedBytes = 0;
4192 int cachedRecords = 0;
4195 if( trailerPos > payloadStart )
4197 cachedBytes =
static_cast<int>( trailerPos - payloadStart );
4199 if( cachedBytes % CACHED_RECORD_LEN == 0 )
4201 cachedRecords = cachedBytes / CACHED_RECORD_LEN;
4204 for(
int recIdx = 0; recIdx < cachedRecords; recIdx++ )
4206 size_t recPos = payloadStart +
static_cast<size_t>( recIdx ) * CACHED_RECORD_LEN;
4208 if( recPos + CACHED_RECORD_LEN > trailerPos )
4243 wxT(
"DipTrace: zone[%d] trailer off=0x%06zX regionsCounted=%d "
4244 "cachedBytes=%d cachedRecords=%d boardClr=%d "
4245 "islands=[%u,%u,%u] id=%d viaDirect=%u smdSeparate=%u "
4246 "smdSpokeMode=%d smdSpokeWidth=%d ratMode=%u done=%u" ),
4247 aZoneIndex, trailerPos, lead, cachedBytes, cachedRecords, boardClr,
4248 static_cast<unsigned int>( islandR ),
static_cast<unsigned int>( islandI ),
4249 static_cast<unsigned int>( islandC ), zoneId,
static_cast<unsigned int>( viaDir ),
4250 static_cast<unsigned int>( smdSep ), smdSpokeMode, smdSpokeW,
4251 static_cast<unsigned int>( ratMode ),
static_cast<unsigned int>( doneFlag ) );
4255 int zoneXMin = aZone.
outline[0].first;
4256 int zoneXMax = aZone.
outline[0].first;
4257 int zoneYMin = aZone.
outline[0].second;
4258 int zoneYMax = aZone.
outline[0].second;
4260 for(
const auto& p : aZone.
outline )
4262 zoneXMin = std::min( zoneXMin, p.first );
4263 zoneXMax = std::max( zoneXMax, p.first );
4264 zoneYMin = std::min( zoneYMin, p.second );
4265 zoneYMax = std::max( zoneYMax, p.second );
4269 std::array<int, 6> minVals = {
4273 std::array<int, 6> maxVals = minVals;
4274 std::array<int, 6> inXHits = { 0, 0, 0, 0, 0, 0 };
4275 std::array<int, 6> inYHits = { 0, 0, 0, 0, 0, 0 };
4276 std::array<int, 6> nonNegHits = { 0, 0, 0, 0, 0, 0 };
4277 std::map<int, int> field0Hist;
4278 std::map<int, int> field5Hist;
4279 int f0EqRegions = 0;
4280 int xEqualCount = 0;
4281 int yEqualCount = 0;
4282 int bothEqualCount = 0;
4286 std::array<int, 6> vals = {
4291 field0Hist[rec.
field0]++;
4292 field5Hist[rec.
field5]++;
4309 for(
size_t fi = 0; fi < vals.size(); fi++ )
4311 minVals[fi] = std::min( minVals[fi], vals[fi] );
4312 maxVals[fi] = std::max( maxVals[fi], vals[fi] );
4314 if( vals[fi] >= zoneXMin && vals[fi] <= zoneXMax )
4317 if( vals[fi] >= zoneYMin && vals[fi] <= zoneYMax )
4326 wxT(
"DipTrace: zone[%d] cached-range "
4327 "f0=[%d,%d] f1=[%d,%d] f2=[%d,%d] f3=[%d,%d] f4=[%d,%d] f5=[%d,%d]" ),
4328 aZoneIndex, minVals[0], maxVals[0], minVals[1], maxVals[1], minVals[2], maxVals[2],
4329 minVals[3], maxVals[3], minVals[4], maxVals[4], minVals[5], maxVals[5] );
4332 wxT(
"DipTrace: zone[%d] cached-hits "
4333 "xHits=[%d,%d,%d,%d,%d,%d] yHits=[%d,%d,%d,%d,%d,%d] "
4334 "nonNeg=[%d,%d,%d,%d,%d,%d]" ),
4335 aZoneIndex, inXHits[0], inXHits[1], inXHits[2], inXHits[3], inXHits[4], inXHits[5],
4336 inYHits[0], inYHits[1], inYHits[2], inYHits[3], inYHits[4], inYHits[5], nonNegHits[0],
4337 nonNegHits[1], nonNegHits[2], nonNegHits[3], nonNegHits[4], nonNegHits[5] );
4339 wxLogTrace(
traceDiptraceIo, wxT(
"DipTrace: zone[%d] cached-zone-bbox=[%d,%d,%d,%d]" ), aZoneIndex,
4340 zoneXMin, zoneXMax, zoneYMin, zoneYMax );
4342 auto histToString = [](
const std::map<int, int>& aHist ) -> wxString
4347 for(
const auto&
kv : aHist )
4353 out += wxString::Format( wxT(
"%d:%d" ),
kv.first,
kv.second );
4360 wxT(
"DipTrace: zone[%d] cached-hist f0={%s} f5={%s} "
4361 "f0EqRegions=%d xEq=%d yEq=%d bothEq=%d" ),
4362 aZoneIndex, histToString( field0Hist ), histToString( field5Hist ), f0EqRegions,
4363 xEqualCount, yEqualCount, bothEqualCount );
4367 for(
size_t ri = 0; ri < sampleCount; ri++ )
4370 wxLogTrace(
traceDiptraceIo, wxT(
"DipTrace: zone[%d] cached-rec[%zu]={%d,%d,%d,%d,%d,%d}" ),
4382 bool zoneViaPreamble =
false;
4384 auto findZoneFontPreambleDataStart = [&](
size_t aStart ) ->
size_t
4386 static const wxString fontNames[] = {
4387 wxT(
"Arial" ), wxT(
"Tahoma" ), wxT(
"Times New Roman" ),
4388 wxT(
"Courier New" ), wxT(
"Verdana" ), wxT(
"Calibri" )
4393 for(
const wxString& fontName : fontNames )
4395 size_t fontPos =
m_reader.FindString( fontName, aStart, aSearchEnd );
4405 if( bc < 0 || bc > 500 )
4407 fontPos =
m_reader.FindString( fontName, fontPos + 3, aSearchEnd );
4411 strEnd = fontPos + 3 + bc;
4415 uint16_t cc = (
static_cast<uint16_t
>( data[fontPos] ) << 8 )
4416 | data[fontPos + 1];
4417 strEnd = fontPos + 2 +
static_cast<size_t>( cc ) * 2;
4420 if( strEnd + 16 > aSearchEnd )
4424 int bold = data[strEnd + 3];
4429 if( fontSize >= 5 && fontSize <= 30 && bold <= 1
4430 && fontH > 0 && fontH < 10000000
4431 && fontW > 0 && fontW < 10000000
4434 bestDataStart = std::min( bestDataStart, strEnd + 16 );
4438 fontPos =
m_reader.FindString( fontName, strEnd, aSearchEnd );
4442 return bestDataStart;
4445 size_t zoneDataStart = findZoneFontPreambleDataStart( aSearchStart );
4449 size_t preambleHeaderStart = zoneDataStart + 3;
4451 if( preambleHeaderStart + 30 <= aSearchEnd
4452 && headerLooksPlausible( preambleHeaderStart,
true ) )
4454 zoneHeaderStart = preambleHeaderStart;
4455 zoneViaPreamble =
true;
4459 if( preambleHeaderStart + 30 <= aSearchEnd
4460 && headerHasZoneSectionShape( preambleHeaderStart ) )
4463 _(
"DipTrace import: invalid copper-pour zone header after font preamble "
4464 "at offset 0x%06zX." ),
4465 preambleHeaderStart ) );
4468 zoneDataStart = findZoneFontPreambleDataStart( zoneDataStart + 1 );
4472 for(
size_t scanPos = aSearchStart; zoneHeaderStart ==
NOT_FOUND
4473 && scanPos + 30 <= aSearchEnd; scanPos++ )
4475 if( headerLooksPlausible( scanPos,
true ) )
4477 zoneHeaderStart = scanPos;
4478 wxLogTrace(
traceDiptraceIo, wxT(
"DipTrace: zone section found by structural scan at 0x%06zX" ),
4491 zoneDataStart = findZoneFontPreambleDataStart( aSearchStart );
4495 size_t fallbackStart = zoneDataStart + 3;
4497 if( fallbackStart + 30 <= aSearchEnd
4498 && headerLooksPlausible( fallbackStart,
true ) )
4500 zoneHeaderStart = fallbackStart;
4501 zoneViaPreamble =
true;
4505 size_t fallbackEnd = std::min( aSearchEnd, zoneDataStart + 256 );
4507 for(
size_t scanPos = zoneDataStart; scanPos + 30 <= fallbackEnd; scanPos++ )
4509 if( headerLooksPlausible( scanPos,
true ) )
4511 zoneHeaderStart = scanPos;
4519 wxLogTrace(
traceDiptraceIo, wxT(
"DipTrace: zone section found by font fallback at 0x%06zX" ),
4528 size_t pos = zoneHeaderStart;
4530 for(
int zi = 0; zi < MAX_ZONES && pos + 30 < aSearchEnd; zi++ )
4545 int flags1 = data[pos + 3];
4546 int flags2 = data[pos + 4];
4547 int flags3 = data[pos + 5];
4550 int minimumArea =
ReadInt4At( data, pos + 14 );
4551 int separator =
ReadInt3At( data, pos + 18 );
4557 if( fieldA < 0 || fieldA > 10000 )
4560 if( fieldB < -1 || fieldB > 10000 )
4563 if( flags1 > 2 || flags3 > 2 )
4569 if( minWidth <= 0 || minWidth > MAX_REASONABLE_DIM )
4572 if( minimumArea < 0 || minimumArea > MAX_REASONABLE_DIM )
4578 if( layer < -10 || layer > 100 )
4581 if( vtxCount < 3 || vtxCount > MAX_VERTICES )
4584 size_t vtxStart = pos + 30;
4585 size_t vtxEnd = vtxStart +
static_cast<size_t>( vtxCount ) * 8;
4587 if( vtxEnd > aSearchEnd )
4594 zone.
fillMode =
static_cast<uint8_t
>( flags1 );
4595 zone.
rawFlag2 =
static_cast<uint8_t
>( flags2 );
4601 zone.
outline.reserve( vtxCount );
4603 bool validOutline =
true;
4605 for(
int vi = 0; vi < vtxCount; vi++ )
4607 size_t vp = vtxStart +
static_cast<size_t>( vi ) * 8;
4611 if( x < bboxXMin || x > bboxXMax || y < bboxYMin || y > bboxYMax )
4613 validOutline =
false;
4617 zone.
outline.emplace_back( x, y );
4623 wxString zoneNetName;
4627 if( net.index == fieldB )
4629 zoneNetName = net.name;
4635 minimumArea, separator, layer, fieldB, vtxCount, zoneNetName );
4637 m_zones.push_back( std::move( zone ) );
4643 if( pos + 3 > aSearchEnd )
4648 if( fillSegCount < 0 || fillSegCount > 500000 )
4653 wxLogTrace(
traceDiptraceIo, wxT(
"DipTrace: zone[%d] fill-segments off=0x%06zX count=%d" ), zi, pos,
4657 pos += 3 +
static_cast<size_t>( fillSegCount ) * 19;
4660 if( pos + 3 > aSearchEnd )
4665 if( fillPolyCount < 0 || fillPolyCount > 50000 )
4670 wxLogTrace(
traceDiptraceIo, wxT(
"DipTrace: zone[%d] fill-polys off=0x%06zX count=%d" ), zi, pos,
4676 for(
int pi = 0; pi < fillPolyCount; pi++ )
4678 if( pos + 3 > aSearchEnd )
4686 if( pvc < 0 || pvc > MAX_VERTICES )
4692 pos += 3 +
static_cast<size_t>( pvc ) * 8 + 3;
4694 if( pos > aSearchEnd )
4706 if( pos + 14 <= aSearchEnd )
4710 int lineSpacing =
ReadInt4At( data, pos + 6 );
4711 int spokeWidth =
ReadInt4At( data, pos + 10 );
4714 && spokeMode >= 0 && spokeMode <= 4
4715 && lineSpacing > 0 && lineSpacing <= MAX_REASONABLE_DIM
4716 && spokeWidth > 0 && spokeWidth <= MAX_REASONABLE_DIM )
4725 wxT(
"DipTrace: zone[%d] style lead=%d spokeMode=%d lineSpacing=%d spokeWidth=%d" ), zi,
4726 styleLead, spokeMode, lineSpacing, spokeWidth );
4734 bool foundNext =
false;
4735 size_t scanStart = pos;
4737 for(
size_t testPos = pos; testPos + 30 <= aSearchEnd; testPos++ )
4739 if( !headerLooksPlausible( testPos,
false ) )
4742 if( !headerLooksPlausible( testPos,
true ) )
4750 if( foundNext && pos > scanStart )
4752 parseZoneTrailer( parsedZone, scanStart, pos, zi );
4758 parseZoneTrailer( parsedZone, scanStart, aSearchEnd, zi );
4772 if( !
m_zones.empty() && !zoneViaPreamble )
4786 int copperCount =
static_cast<int>(
m_layers.size() );
4788 if( copperCount < 2 )
4792 if( copperCount % 2 != 0 )
4795 m_board->SetCopperLayerCount( copperCount );
4814 enabledLayers.
set( kiLayer );
4816 if( !layer.name.empty() )
4817 m_board->SetLayerName( kiLayer, layer.name );
4821 m_board->SetEnabledLayers( enabledLayers );
4866 std::vector<VECTOR2I> pts;
4867 std::vector<uint8_t> arcs;
4874 arcs.push_back( v.arc );
4882 while( i < n && arcs[i] == 1 )
4888 size_t startIndex = i;
4890 size_t maxSteps = n * 4;
4900 std::vector<OUTLINE_PRIM> outlinePrims;
4901 outlinePrims.reserve( n );
4907 prim.start = aStart;
4909 outlinePrims.push_back( prim );
4916 prim.start = aStart;
4919 outlinePrims.push_back( prim );
4922 while( i < n && steps++ < maxSteps )
4924 size_t next = ( i + 1 ) % n;
4926 if( arcs[
next] == 1 )
4930 size_t afterArc = (
next + 1 ) % n;
4937 long long cross =
static_cast<long long>(
v1.x ) *
static_cast<long long>(
v2.y )
4938 -
static_cast<long long>(
v1.y ) *
static_cast<long long>(
v2.x );
4939 bool degenerateArc = ( start == mid ) || ( mid ==
end ) || ( start ==
end )
4940 || ( std::llabs( cross ) < 100 );
4948 addArc( start, mid,
end );
4963 if( i == startIndex )
4967 if( steps >= maxSteps )
4969 wxLogWarning(
_(
"DipTrace: outline traversal aborted after %zu steps (%zu vertices)" ),
4973 outlinePrims.clear();
4975 for(
size_t v = 0; v < n; v++ )
4979 for(
const OUTLINE_PRIM& prim : outlinePrims )
5013 for(
int i = 0; i < 4; i++ )
5019 seg->
SetEnd( corners[( i + 1 ) % 4] );
5037 if(
chain.netIndex < 0 )
5041 anchors.reserve( anchors.size() +
chain.nodes.size() );
5062 for(
size_t padIdx = 0; padIdx < aComp.
pads.size(); padIdx++ )
5068 pad->SetPosition( padLocal );
5083 std::vector<VECTOR2I> polyPts;
5091 else if( dtPad.
style == 2 )
5111 if( drillW > 0 && drillH <= 0 )
5114 if( drillH > 0 && drillW <= 0 )
5127 if( drillW > 0 && drillH > 0 )
5131 pad->SetDrillSize( drill );
5133 if( drillW == drillH )
5148 orientClass = aComp.
pads[padIdx - 1].orientClass;
5150 if( orientClass == 1 )
5164 PAD* holePad =
new PAD( footprint );
5187 bool hasAssemblyOutline =
false;
5193 hasAssemblyOutline =
true;
5212 std::swap( scaleX, scaleY );
5218 int minSX = INT_MAX, maxSX = INT_MIN;
5219 int minSY = INT_MAX, maxSY = INT_MIN;
5223 minSX = std::min( { minSX, s.
x1, s.
x2 } );
5224 maxSX = std::max( { maxSX, s.
x1, s.
x2 } );
5225 minSY = std::min( { minSY, s.
y1, s.
y2 } );
5226 maxSY = std::max( { maxSY, s.
y1, s.
y2 } );
5229 int shapeXRange = maxSX - minSX;
5230 int shapeYRange = maxSY - minSY;
5232 if( shapeXRange > 0 && shapeYRange > 0 )
5234 double physX =
static_cast<double>( shapeXRange ) *
std::abs( scaleX )
5236 double physY =
static_cast<double>( shapeYRange ) *
std::abs( scaleY )
5238 double aspect = physX / physY;
5240 if( aspect < 0.2 || aspect > 5.0 )
5241 std::swap( scaleX, scaleY );
5246 static constexpr int DEFAULT_LINE_WIDTH_DT = 3000;
5248 auto scaleShapeCoord = [&](
int aShapeVal,
int aBboxDim ) ->
int
5251 static_cast<int>(
static_cast<int64_t
>( aShapeVal ) * aBboxDim
5280 switch( dtShape.
layer )
5289 default: shapeLayer =
F_SilkS;
break;
5294 VECTOR2I p1( scaleShapeCoord( dtShape.
x1, scaleX ),
5295 scaleShapeCoord( dtShape.
y1, scaleY ) );
5296 VECTOR2I p2( scaleShapeCoord( dtShape.
x2, scaleX ),
5297 scaleShapeCoord( dtShape.
y2, scaleY ) );
5310 for(
int i = 0; i < 4; i++ )
5317 edge->
SetEnd( corners[( i + 1 ) % 4] );
5333 int radius = ( p2 - p1 ).EuclideanNorm() / 2;
5340 scaleShapeCoord( dtShape.
midY, scaleY ) );
5345 int64_t cross =
static_cast<int64_t
>( p2.
x - p1.
x ) * ( mid.
y - p1.
y )
5346 -
static_cast<int64_t
>( p2.
y - p1.
y ) * ( mid.
x - p1.
x );
5347 int64_t chordSq =
static_cast<int64_t
>( p2.
x - p1.
x ) * ( p2.
x - p1.
x )
5348 +
static_cast<int64_t
>( p2.
y - p1.
y ) * ( p2.
y - p1.
y );
5350 if( chordSq == 0 ||
std::abs( cross ) * 1000 < chordSq )
5388 double orientationDeg;
5396 if( orientationDeg < 0.0 )
5397 orientationDeg += 360.0;
5402 double nearest90 = std::round( orientationDeg / 90.0 ) * 90.0;
5404 if(
std::abs( orientationDeg - nearest90 ) < 0.02 )
5405 orientationDeg = std::fmod( nearest90, 360.0 );
5411 :
static_cast<int>( std::lround(
5413 orientationDeg = ( ( orientationQuarterTurns % 4 ) + 4 ) % 4 * 90.0;
5418 wxLogTrace(
traceDiptraceIo, wxT(
"DipTrace: fp-orient ref=%s pat=%s qturn=%d hasQ=%d exact=%d chosen=%.2f" ),
5424 if( aComp.
layer == 1 )
5451 if( aText.
text.empty() )
5461 text->SetLayer( textLayer );
5486 if( aDipTraceNetIndex < 0 )
5500 if( aDipTraceNetIndex < 0 )
5519 wxString netName = net.name;
5521 if( netName.empty() )
5522 netName = wxString::Format( wxS(
"DipTrace_Net_%d" ), net.index );
5532 if( net.index >= 0 )
5536 if( !inserted && it->second != netinfo )
5538 wxLogWarning(
_(
"DipTrace: net index %d maps to multiple net names (%s, %s)" ),
5539 net.index, it->second->GetNetname(), netinfo->
GetNetname() );
5544 if( !dtInserted && dtIt->second != &net )
5546 wxLogWarning(
_(
"DipTrace: duplicate net metadata for net index %d" ),
5558 int missingGeometry = 0;
5559 std::set<std::tuple<int, int, int>> createdKeys;
5561 auto hasBoardViaAt = [&](
const VECTOR2I& aPos,
int aNetCode ) ->
bool
5570 if(
via->GetPosition() == aPos &&
via->GetNetCode() == aNetCode )
5579 if( !
comp.isStandaloneVia )
5582 int viaX =
comp.positionX;
5583 int viaY =
comp.positionY;
5584 int viaOuter = std::max(
comp.bboxWidth,
comp.padWidthHint );
5585 int viaDrill = std::max(
comp.drillWidthHint,
comp.drillHeightHint );
5588 if( !
comp.pads.empty() )
5593 netIndex =
pad.netIndex;
5596 viaOuter =
pad.width;
5598 if(
pad.drillWidth > 0 )
5599 viaDrill =
pad.drillWidth;
5600 else if(
pad.drillHeight > 0 )
5601 viaDrill =
pad.drillHeight;
5604 if( viaOuter <= 0 || viaDrill <= 0 )
5613 std::tuple<int, int, int> key( viaPos.
x, viaPos.
y, netCode );
5615 if( createdKeys.find( key ) != createdKeys.end() || hasBoardViaAt( viaPos, netCode ) )
5622 via->SetPosition( viaPos );
5632 createdKeys.insert( key );
5636 if( created > 0 || duplicates > 0 || missingGeometry > 0 )
5639 wxT(
"DipTrace: created %d standalone vias from component records "
5640 "(duplicates=%d, missingGeometry=%d)" ),
5641 created, duplicates, missingGeometry );
5650 int missingChainNets = 0;
5651 int nonCopperTrackSkips = 0;
5652 int nonCopperViaSkips = 0;
5653 int inferredLayerChangeVias = 0;
5654 int explicitViaStyleVias = 0;
5655 int duplicateViaSkips = 0;
5656 int crossNetViaSkips = 0;
5664 bool operator==(
const POS_NET_KEY& aOther )
const
5666 return net == aOther.net && x == aOther.x && y == aOther.y;
5672 size_t operator()(
const POS_NET_KEY& aKey )
const
5674 size_t h1 =
static_cast<size_t>(
static_cast<uint32_t
>( aKey.net ) );
5675 size_t h2 =
static_cast<size_t>(
static_cast<uint32_t
>( aKey.x ) );
5676 size_t h3 =
static_cast<size_t>(
static_cast<uint32_t
>( aKey.y ) );
5677 return h1 ^ ( h2 * 0x9e3779b1U ) ^ ( h3 * 0x85ebca6bU );
5681 auto makeKey = [](
int aNet,
int aX,
int aY ) -> POS_NET_KEY
5695 bool operator==(
const POS_KEY& aOther )
const
5697 return x == aOther.x && y == aOther.y;
5703 size_t operator()(
const POS_KEY& aKey )
const
5705 size_t h1 =
static_cast<size_t>(
static_cast<uint32_t
>( aKey.x ) );
5706 size_t h2 =
static_cast<size_t>(
static_cast<uint32_t
>( aKey.y ) );
5707 return ( h1 * 0x9e3779b1U ) ^ ( h2 * 0x85ebca6bU );
5711 auto makePosKey = [](
int aX,
int aY ) -> POS_KEY
5719 auto resolveCopperLayer = [&](
int aDipTraceLayer ) ->
PCB_LAYER_ID
5724 layer =
MapLayer( aDipTraceLayer );
5732 std::unordered_map<POS_NET_KEY, std::set<int>, POS_NET_HASH> layersByNetPos;
5733 std::unordered_set<POS_NET_KEY, POS_NET_HASH> createdVias;
5734 std::unordered_map<POS_KEY, std::set<int>, POS_HASH> netsByPos;
5742 if(
chain.netIndex >= 0 )
5743 netsByPos[makePosKey( node.
x, node.
y )].insert(
chain.netIndex );
5747 std::shared_ptr<NETCLASS> defNetclass =
5748 m_board->GetDesignSettings().m_NetSettings->GetDefaultNetclass();
5755 if( !net &&
chain.netIndex >= 0 )
5759 for(
size_t i = 0; i + 1 <
chain.nodes.size(); i++ )
5774 nonCopperTrackSkips++;
5788 for(
size_t nodeIdx = 0; nodeIdx <
chain.nodes.size(); nodeIdx++ )
5792 auto posIt = layersByNetPos.find( key );
5793 bool explicitVia = node.
hasVia;
5799 auto netPosIt = netsByPos.find( makePosKey( node.
x, node.
y ) );
5801 if( netPosIt != netsByPos.end() && netPosIt->second.size() > 1 )
5807 if( createdVias.find( key ) != createdVias.end() )
5809 duplicateViaSkips++;
5813 if( explicitViaStyle )
5814 explicitViaStyleVias++;
5834 styleViaLayerA = l1;
5835 styleViaLayerB = l2;
5839 styleViaLayerA = l2;
5840 styleViaLayerB = l1;
5847 if( viaOuterDiam <= 0 )
5850 if( viaDrillDiam <= 0 )
5854 int viaWidthIU = ( viaOuterDiam > 0 ) ?
ToKiCadCoord( viaOuterDiam ) : 0;
5855 int viaDrillIU = ( viaDrillDiam > 0 ) ?
ToKiCadCoord( viaDrillDiam ) : 0;
5857 if( viaWidthIU <= 0 && defNetclass )
5858 viaWidthIU = defNetclass->GetViaDiameter();
5860 if( viaDrillIU <= 0 && defNetclass )
5861 viaDrillIU = defNetclass->GetViaDrill();
5863 if( viaWidthIU <= 0 )
5869 via->SetWidth( viaWidthIU );
5871 if( viaDrillIU > 0 )
5872 via->SetDrill( viaDrillIU );
5874 auto ordinalToLayer = [&](
size_t aOrdinal ) ->
PCB_LAYER_ID
5876 int copperCount =
m_board->GetCopperLayerCount();
5881 if( aOrdinal >=
static_cast<size_t>( copperCount - 1 ) )
5884 int innerIdx =
static_cast<int>( aOrdinal ) - 1;
5886 if( innerIdx < 0 || innerIdx > 29 )
5892 bool haveLayerRange =
false;
5896 auto accumulateDipLayer = [&](
int aDipLayer )
5905 if( !haveLayerRange )
5909 haveLayerRange =
true;
5913 minOrd = std::min( minOrd, ord );
5914 maxOrd = std::max( maxOrd, ord );
5918 accumulateDipLayer( node.
layer );
5921 accumulateDipLayer(
chain.nodes[nodeIdx - 1].layer );
5923 if( nodeIdx + 1 <
chain.nodes.size() )
5924 accumulateDipLayer(
chain.nodes[nodeIdx + 1].layer );
5926 if( posIt != layersByNetPos.end() )
5928 for(
int dipLayer : posIt->second )
5929 accumulateDipLayer( dipLayer );
5937 viaLayerA = styleViaLayerA;
5938 viaLayerB = styleViaLayerB;
5940 else if( haveLayerRange && minOrd < maxOrd )
5952 via->SetLayerPair( viaLayerA, viaLayerB );
5954 if( viaLayerA ==
F_Cu && viaLayerB ==
B_Cu )
5956 else if( viaLayerA ==
F_Cu || viaLayerB ==
B_Cu )
5965 createdVias.insert( key );
5971 wxT(
"DipTrace: created %d tracks and %d vias (%d chains with unresolved nets, "
5972 "%d non-copper tracks skipped, %d non-copper vias skipped, "
5973 "%d style vias, %d inferred layer-change vias, %d duplicate vias skipped, "
5974 "%d cross-net vias skipped)" ),
5975 trackCount, viaCount, missingChainNets, nonCopperTrackSkips, nonCopperViaSkips, explicitViaStyleVias,
5976 inferredLayerChangeVias, duplicateViaSkips, crossNetViaSkips );
5982 std::unordered_map<int, int> zoneSpokeModeByDipNet;
5983 std::unordered_set<int> zoneSpokeModeConflicts;
5987 if( dtZone.outline.size() < 3 )
5995 zoneLayer =
MapLayer( dtZone.layer );
6003 if( dtZone.clearance > 0 )
6006 if( dtZone.minWidth > 0 )
6013 if( dtZone.spokeWidth > 0 )
6016 if( dtZone.spokeMode == 0 )
6020 else if( dtZone.spokeMode > 0 && dtZone.smdSpokeMode == 0 )
6027 else if( dtZone.spokeMode > 0 )
6038 if( dtZone.islandInternal || dtZone.islandConnection )
6042 else if( dtZone.islandRegion )
6046 if( dtZone.minimumArea > 0 )
6048 long long minIslandLinearIU =
static_cast<long long>(
ToKiCadCoord( dtZone.minimumArea ) );
6063 for(
const auto& [x, y] : dtZone.outline )
6073 if( dtZone.netIndex >= 0 && dtZone.spokeMode >= 0 )
6075 auto [it, inserted] = zoneSpokeModeByDipNet.emplace( dtZone.netIndex, dtZone.spokeMode );
6077 if( !inserted && it->second != dtZone.spokeMode )
6078 zoneSpokeModeConflicts.insert( dtZone.netIndex );
6084 std::unordered_map<int, int> spokeModeByNetCode;
6086 for(
const auto& [dipNetIdx, spokeMode] : zoneSpokeModeByDipNet )
6088 if( zoneSpokeModeConflicts.count( dipNetIdx ) )
6096 spokeModeByNetCode[netIt->second->GetNetCode()] = spokeMode;
6099 int adjustedPadThermalAngles = 0;
6103 for(
PAD*
pad : fp->Pads() )
6108 auto modeIt = spokeModeByNetCode.find(
pad->GetNetCode() );
6110 if( modeIt == spokeModeByNetCode.end() )
6113 int spokeMode = modeIt->second;
6115 if( spokeMode == 1 || spokeMode == 4 )
6118 adjustedPadThermalAngles++;
6120 else if( spokeMode == 2 || spokeMode == 3 )
6123 adjustedPadThermalAngles++;
6128 if( adjustedPadThermalAngles > 0 )
6130 wxLogTrace(
traceDiptraceIo, wxT(
"DipTrace: applied thermal spoke angle overrides to %d pads" ),
6131 adjustedPadThermalAngles );
6158 planeOutline.
Append( x1, y1 );
6159 planeOutline.
Append( x2, y1 );
6160 planeOutline.
Append( x2, y2 );
6161 planeOutline.
Append( x1, y2 );
6169 if( layer.type != 1 || layer.planeNetIndex < 0 )
6182 wxT(
"DipTrace: plane layer %d references unresolved net index %d; skipping" ),
6183 layer.index, layer.planeNetIndex );
6206 wxT(
"DipTrace: synthesized plane zone on layer %d net '%s' (dt net %d)" ),
6207 layer.index, netinfo->
GetNetname(), layer.planeNetIndex );
6217 int edgeClearanceIU = 0;
6221 std::map<wxString, bool> viaDirectNets;
6225 if( zone.boardClearance > 0 )
6226 edgeClearanceIU = std::max( edgeClearanceIU,
ToKiCadCoord( zone.boardClearance ) );
6228 if( zone.viaDirect && zone.netIndex >= 0 )
6232 if( !net->GetNetname().IsEmpty() )
6233 viaDirectNets[net->GetNetname()] =
true;
6238 if( edgeClearanceIU <= 0 && viaDirectNets.empty() )
6239 return wxEmptyString;
6241 wxString rules = wxT(
"(version 1)\n" );
6243 if( edgeClearanceIU > 0 )
6247 rules += wxString::Format( wxT(
"\n(rule \"DipTrace zone board clearance\"\n" )
6248 wxT(
" (condition \"A.Type == 'Zone'\")\n" )
6249 wxT(
" (constraint edge_clearance (min %smm)))\n" ),
6253 for(
const auto& [netName, direct] : viaDirectNets )
6256 wxString escaped = netName;
6257 escaped.Replace( wxT(
"'" ), wxT(
"\\'" ) );
6259 rules += wxString::Format( wxT(
"\n(rule \"DipTrace via direct %s\"\n" )
6260 wxT(
" (condition \"A.Type == 'Via' && A.NetName == '%s'\")\n" )
6261 wxT(
" (constraint zone_connection solid))\n" ),
bool operator==(const wxAuiPaneInfo &aLhs, const wxAuiPaneInfo &aRhs)
constexpr EDA_IU_SCALE pcbIUScale
BASE_SET & set(size_t pos)
virtual void SetNet(NETINFO_ITEM *aNetInfo)
Set a NET_INFO object for the item.
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Container for design settings for a BOARD object.
std::shared_ptr< NET_SETTINGS > m_NetSettings
BOARD_STACKUP & GetStackupDescriptor()
Manage layers needed to make a physical board.
void RemoveAll()
Delete all items in list and clear the list.
void BuildDefaultStackupList(const BOARD_DESIGN_SETTINGS *aSettings, int aActiveCopperLayersCount=0)
Create a default stackup, according to the current BOARD_DESIGN_SETTINGS settings.
Information pertinent to a Pcbnew printed circuit board.
Low-level binary reader for DipTrace file formats.
void ReadColor(uint8_t &r, uint8_t &g, uint8_t &b)
Read a 3-byte RGB color value.
PCB_PARSER(const wxString &aFileName, BOARD *aBoard)
Construct a parser for the given file.
void ParseTextRecords(int aCount)
void CreateTracksAndVias()
std::vector< DT_TEXT_OBJECT > m_textObjects
static double ToKiCadAngleDeg(int aDipTraceAngle)
Convert a DipTrace angle value to degrees (tenths of degree).
std::unordered_map< int, NETINFO_ITEM * > m_kicadNetByDipTraceIndex
PCB_LAYER_ID MapCopperLayer(int aDipTraceLayer) const
std::vector< DT_VIA_STYLE > m_viaStyles
size_t m_componentUpperBound
static int ToKiCadCoord(int aDipTraceCoord)
Convert a DipTrace coordinate (DipTrace units) to KiCad internal units (nm).
static std::vector< size_t > FindAllBoundaries(const uint8_t *aData, size_t aDataSize, const uint8_t *aPattern, size_t aPatternLen, size_t aStart, size_t aEnd)
Find all occurrences of a byte pattern within data[start:end].
void ParsePatternStyleGroups(int aGroupCount)
std::vector< DT_COMPONENT > m_components
void ApplyBoardSettings()
void ParseComponentTail(DT_COMPONENT &aComp, size_t aRegionEnd)
Parse the 37-byte tail at the end of a component region to extract text positioning.
void CreateBoardOutline()
void CreateFootprint(const DT_COMPONENT &aComp)
void FindPadsInRegion(DT_COMPONENT &aComp, size_t aRegionStart, size_t aRegionEnd)
Search a component's data region for pad records using pad name anchors.
void CreateStandaloneVias()
std::vector< DT_DESIGN_RULE > m_designRules
std::vector< std::pair< size_t, size_t > > FieldWalkComponentBoundaries(size_t aUpperBound)
Deterministically walk the component boundaries from the design-rules end, anchoring on each componen...
bool m_hasLegacyMagicLayout
bool m_routingAnchorCacheBuilt
std::vector< DT_LAYER > m_layers
void ParsePatternNameGroups(int aGroupCount)
void ParseNetRouting(DT_NET &aNet)
void FindShapesInChainedBlocks(DT_COMPONENT &aComp, size_t aRegionStart, size_t aRegionEnd)
Parse shapes from chained fixed-size records used by some v46+ footprints.
int m_componentLocatorScans
static bool TryReadStringAt(const uint8_t *aData, size_t aDataSize, size_t aPos, int aVersion, wxString &aOut, size_t &aNewPos)
Try to read a string at a given raw data position.
std::vector< DT_NET > m_nets
std::unordered_map< int, std::vector< VECTOR2I > > m_routingAnchorsByNet
size_t m_postDesignRulesOffset
void ParsePostComponentSections()
void SkipInterRulesetTransition()
Read an inter-ruleset transition block.
NETINFO_ITEM * ResolveNetByIndex(int aDipTraceNetIndex) const
Resolve a DipTrace net index to the corresponding KiCad net object.
void ParseImplicitPatternStyleGroup()
std::vector< DT_VERTEX > m_outline
void CreatePlaneZones()
Synthesize board-outline-bounded plane fills for negative/solid-plane copper layers.
bool ParseSingleComponent(size_t aBoundaryOffset, size_t aUpperBound, DT_COMPONENT &aComp)
void FindMountHolesInRegion(DT_COMPONENT &aComp, size_t aRegionStart, size_t aRegionEnd)
Parse component-local mechanical holes (NPTH) from the post-pad region.
int m_mountHoleLocatorScans
size_t m_postLayersOffset
void CreateTextObject(const DT_TEXT_OBJECT &aText)
void ApplyPlacementAngles()
Refine component placement angles with the exact values from the placement section.
wxString GenerateDesignRules() const
Build a KiCad custom design-rule (.kicad_dru) document for the per-zone DipTrace properties that have...
static bool ClassifyStandaloneVia(const DT_COMPONENT &aComp)
Decide whether a parsed component is a standalone via rather than a placed footprint.
void ParseBoardProperties()
int m_sectionLocatorScans
std::unordered_map< int, const DT_NET * > m_dipTraceNetByIndex
const DT_NET * ResolveDipTraceNetByIndex(int aDipTraceNetIndex) const
void FindAndParseNets(size_t aSearchStart, size_t aSearchEnd)
void InferPadNetsFromRoutingRefs()
void FindAndParseTextObjects(size_t aSearchStart, size_t aSearchEnd)
void Parse()
Parse the file and populate the board. Throws IO_ERROR on failure.
void FindAndParseComponents()
std::unordered_map< int, int > m_copperLayerOrdinalById
PCB_LAYER_ID MapLayer(int aDipTraceLayer) const
Map a DipTrace layer index to a KiCad PCB_LAYER_ID.
void FindShapesInRegion(DT_COMPONENT &aComp, size_t aRegionStart, size_t aRegionEnd)
Parse footprint outline shapes from the region after pad data.
void FindAndParseZones(size_t aSearchStart, size_t aSearchEnd)
std::vector< DT_ZONE > m_zones
void FindShapesInFontBlocks(DT_COMPONENT &aComp, size_t aRegionStart, size_t aRegionEnd)
Parse shapes from per-layer font blocks (v46+ format).
std::vector< DT_TRACK_CHAIN > m_trackChains
void SetCenter(const VECTOR2I &aCenter)
virtual void SetFilled(bool aFlag)
void SetStart(const VECTOR2I &aStart)
void SetShape(SHAPE_T aShape)
void SetEnd(const VECTOR2I &aEnd)
void SetArcGeometry(const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd)
Set the three controlling points for an arc.
void SetWidth(int aWidth)
virtual void SetVisible(bool aVisible)
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
virtual const wxString What() const
A composite of Problem() and Where()
A logical library item identifier and consists of various portions much like a URI.
int SetLibItemName(const UTF8 &aLibItemName)
Override the library item name portion of the LIB_ID to aLibItemName.
LSET is a set of PCB_LAYER_IDs.
Handle the data for a net.
const wxString & GetNetname() const
std::shared_ptr< NETCLASS > GetDefaultNetclass()
Gets the default netclass for the project.
static constexpr PCB_LAYER_ID ALL_LAYERS
! Temporary layer identifier to identify code that is not padstack-aware
void SetAttribute(PAD_ATTRIB aAttribute)
static LSET PTHMask()
layer set for a through hole pad
void SetShape(PCB_LAYER_ID aLayer, PAD_SHAPE aShape)
Set the new shape of this pad.
static LSET UnplatedHoleMask()
layer set for a mechanical unplated through hole pad
void SetNumber(const wxString &aNumber)
Set the pad number (note that it can be alphanumeric, such as the array reference "AA12").
void SetDrillShape(PAD_DRILL_SHAPE aShape)
void SetPosition(const VECTOR2I &aPos) override
void SetDrillSize(const VECTOR2I &aSize)
void SetSize(PCB_LAYER_ID aLayer, const VECTOR2I &aSize)
static LSET SMDMask()
layer set for a SMD pad on Front layer
void SetLayerSet(const LSET &aLayers) override
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
void SetStroke(const STROKE_PARAMS &aStroke) override
virtual void SetPosition(const VECTOR2I &aPos) override
void SetEnd(const VECTOR2I &aEnd)
void SetStart(const VECTOR2I &aStart)
virtual void SetWidth(int aWidth)
int PointCount() const
Return the number of points (vertices) in this line chain.
Represent a set of closed polygons.
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Appends a vertex at the end of the given outline/hole (default: the last outline)
int NewOutline()
Creates a new empty polygon in the set and returns its index.
int OutlineCount() const
Return the number of outlines in the set.
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
Simple container to manage line stroke parameters.
Handle a list of polygons defining a copper zone.
void SetLocalClearance(std::optional< int > aClearance)
void AddPolygon(std::vector< VECTOR2I > &aPolygon)
Add a polygon to the zone outline.
void SetBorderDisplayStyle(ZONE_BORDER_DISPLAY_STYLE aBorderHatchStyle, int aBorderHatchPitch, bool aRebuilBorderdHatch)
Set all hatch parameters for the zone.
void SetMinThickness(int aMinThickness)
void SetThermalReliefSpokeWidth(int aThermalReliefSpokeWidth)
virtual void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
void SetNet(NETINFO_ITEM *aNetInfo) override
Override that drops aNetInfo when this zone is in copper-thieving fill mode.
void SetAssignedPriority(unsigned aPriority)
void SetPadConnection(ZONE_CONNECTION aPadConnection)
void SetZoneName(const wxString &aName)
void SetIslandRemovalMode(ISLAND_REMOVAL_MODE aRemove)
void SetMinIslandArea(long long int aArea)
static int GetDefaultHatchPitch()
static int ReadInt3At(const uint8_t *aData, size_t aPos)
Decode a 3-byte big-endian biased integer from raw data at a given offset.
static constexpr size_t FONT_PREAMBLE_FIXED_SIZE
static constexpr size_t PAD_PRE_HEADER_SIZE
Pad record layout constants.
static constexpr size_t TRACK_NODE_SIZE
Track node record size in bytes.
static constexpr int DT_FP_LAYER_TOP_MASK
static constexpr int DT_FP_LAYER_TOP_COURTYARD
static constexpr size_t FONT_PREAMBLE_LABEL_OFFSET
v46+ font-shape preamble (between the pad region end and the first font block).
static bool IsAngleLikeCode(int aValue)
static const uint8_t BOUNDARY_ALT[]
Alternate boundary pattern: int3(0) int3(0) int3(0) int4(0) – v41 nameplate files.
static void DumpComponentRawFields(const DT_COMPONENT &aComp, const uint8_t *aData, size_t aPosXPos, size_t aPosYPos, size_t aRotPos, size_t aFieldCPos, size_t aFieldDPos)
static void DumpZoneTail(int aZoneIndex, size_t aTailStart, size_t aSearchEnd, const uint8_t *aData)
static bool decodeMountHoleBlockAt(const uint8_t *aData, size_t aBlockStart, size_t aSearchEnd, std::vector< DT_MOUNT_HOLE > &aHoles)
static constexpr int FP_CHAIN_SHAPE_X1_OFFSET
static const uint8_t NET_SENTINEL[]
Net record sentinel: int3(0) int3(-1) int3(-1) This 9-byte pattern appears immediately before each ne...
static constexpr int FP_CHAIN_SHAPE_X3_OFFSET
static constexpr int FP_CHAIN_TYPE_LINE
static bool ShouldDumpComponentHeader(const wxString &aRefdes)
static void DumpPadPostBlock(const DT_COMPONENT &aComp, const DT_PAD &aPad, const uint8_t *aData, size_t aPostDimPos, size_t aPostDimSize)
static constexpr size_t NOT_FOUND
Sentinel for "not found" in pattern searches.
static constexpr int DT_FP_LAYER_TOP_PASTE
static constexpr int FP_CHAIN_SHAPE_Y2_OFFSET
static void DumpRulesetBlock(int aRuleSetIndex, const wxString &aRuleSetName, int aBlockIndex, const std::array< int, 26 > &aValues)
static bool ShouldDumpZones()
static constexpr int DT_FP_LAYER_TOP_KEEPOUT
static const uint8_t CHAIN_HEADER[]
Track chain header pattern: 3 zero bytes + int3(-1)
static const uint8_t BOARD_SETTINGS_FONT_MARKER[]
Board settings font marker: int3(4), int3(4), int3(0)
static constexpr int FP_CHAIN_SHAPE_Y1_OFFSET
static constexpr size_t COMPONENT_TAIL_SIZE
Size of the fixed-layout component tail found at the end of every component region.
static size_t StringFieldSize(const uint8_t *aData, size_t aDataSize, size_t aPos, int aVersion)
Compute the total byte length of a DipTrace string field at a given offset without allocating a wxStr...
static constexpr size_t PAD_POLYGON_VERTEX_SIZE
static constexpr int FP_CHAIN_SHAPE_DATA_OFFSET
static constexpr size_t PAD_POST_DIM_HEADER
static constexpr int FP_CHAIN_SHAPE_TYPE_OFFSET
static constexpr size_t TAHOMA_FONT_PATTERN_LEN
static constexpr int FP_SHAPE_RECORD_SIZE_V45
Record size for v45+ format.
static bool EnvFlagEnabled(const char *aVarName)
static void DumpComponentHeader(const DT_COMPONENT &aComp, int aFieldA, int aFieldB, int aFieldC, int aFieldD, int aFieldE, int aFieldF, uint8_t aSep1, uint8_t aSep2, uint8_t aSep3)
static const uint8_t COMPONENT_TAIL_PATTERN[]
The component tail starts with int3(0) + int4(0) + int4(0) = 11 constant bytes.
static bool ShouldDumpPadPostBlock(const wxString &aRefdes)
static constexpr int DT_FP_LAYER_TOP_OUTLINE
static constexpr size_t PAD_POST_DIM_FIXED_SIZE
static constexpr size_t COMPONENT_TAIL_PATTERN_LEN
static constexpr size_t NET_SENTINEL_LEN
static int ReadInt4At(const uint8_t *aData, size_t aPos)
Decode a 4-byte big-endian biased integer from raw data at a given offset.
static constexpr int FP_SHAPE_COUNT_OFFSET
Shape count (int3) is at this offset past the end of the pad region.
static constexpr size_t BOUNDARY_CORE_LEN
static constexpr size_t MOUNT_HOLE_TERM_SIZE
Hole block terminator bytes: 0x00 0x00.
static constexpr size_t CHAIN_HEADER_LEN
static const uint8_t BOUNDARY_STD[]
Component boundary pattern: int3(0) int3(-1) int3(-1) int4(0)
static constexpr size_t PAD_DIMENSIONS_SIZE
static constexpr size_t FONT_BLOCK_TRAILER_SIZE
static void DumpZoneGap(int aZoneIndex, size_t aGapStart, size_t aGapEnd, const uint8_t *aData)
static constexpr size_t PAD_HEADER_PREAMBLE_UTF16
Fixed byte preamble between the component header strings and the first pad record.
static void DumpComponentBinaryScan(const DT_COMPONENT &aComp, const uint8_t *aData, size_t aDataSize)
static constexpr int FP_CHAIN_SHAPE_COUNT_OFFSET
v46+ chained-shape block layout used by some footprints (e.g. TO-92 in PCB_6).
static bool ShouldDumpFootprintOrientation(const wxString &aRefdes)
static constexpr int FP_SHAPE_RECORD_SIZE_V37
Record size for v37 (legacy) format.
static bool ShouldDumpNets()
static constexpr int FP_CHAIN_SHAPE_Y3_OFFSET
static constexpr size_t PAD_HEADER_PREAMBLE_ASCII
static constexpr int FONT_BLOCK_SHAPE_VERSION
First version that stores per-component shape data in font (Tahoma) blocks rather than contiguous fix...
static constexpr size_t MOUNT_HOLE_HEADER_SIZE
Hole block header: int3(hole_count + 2) + byte(flag) + 4 * int4(0)
static constexpr int FP_SHAPE_DATA_OFFSET
Shape record data starts at this offset past the end of the pad region.
static int32_t ReadRawLE32(const uint8_t *aData, size_t aPos)
static constexpr int FP_SHAPE_NORM_RANGE
Normalized coordinate range used by DipTrace shape records.
static constexpr int FP_CHAIN_SHAPE_WIDTH_OFFSET
static constexpr size_t FONT_BLOCK_HEADER_SIZE
Size of the fixed metadata header following the font string in each font block.
static void DumpComponentTail(const DT_COMPONENT &aComp, const uint8_t *aData, size_t aTailStart, int aVisibility, uint8_t aSideFlag1, uint8_t aSideFlag2, int aOrderIdx, int aRefdesYOffset, int aValueYOffset, uint8_t aHasOffset, uint8_t aTailTerm)
static constexpr size_t MOUNT_HOLE_TRAILER_SIZE
Zero trailer following the hole block in observed v54 files.
static const uint8_t TAHOMA_FONT_PATTERN[]
UTF-16BE pattern for the string "Tahoma" as stored in v46+ component font blocks.
static constexpr int FP_CHAIN_TYPE_ARC
static void DumpZoneHeader(int aZoneIndex, size_t aHeaderPos, const uint8_t *aData, int aFieldA, int aFlags1, int aFlags2, int aFlags3, int aMinWidth, int aClearance, int aMinimumArea, int aSeparator, int aLayer, int aFieldB, int aVtxCount, const wxString &aNetName)
static uint32_t ReadColorPacked(BINARY_READER &aReader)
Read a 3-byte RGB color from the reader and return it packed as 0x00RRGGBB.
static void DumpPadGap(const DT_COMPONENT &aComp, const uint8_t *aData, size_t aGapStart, size_t aGapEnd)
static constexpr int PAD_MAX_NET_INDEX
Upper bound for a plausible pad net index, used as a chain-walk desync guard.
static constexpr size_t PAD_POST_DIM_TAIL
static constexpr int FP_CHAIN_SHAPE_RECORD_SIZE
static constexpr int DT_FP_LAYER_TOP_ASSY
static float ReadRawLEFloat32(const uint8_t *aData, size_t aPos)
static constexpr size_t MOUNT_HOLE_RECORD_SIZE
Per-hole record: byte + byte + int4(x) + int4(y) + int4(outer_diam) + int4(drill_diam)
static constexpr int FP_SHAPE_DEFAULT_WIDTH
Sentinel value for "use default line width" in shape width field.
static const uint8_t TEXT_SECTION_ZEROS[]
Text section zeros: 3x int3(0)
static wxString BytesToHex(const uint8_t *aData, size_t aLen)
static constexpr int FP_CHAIN_SHAPE_X2_OFFSET
static bool FindComponentRotation(const uint8_t *aData, size_t aDataSize, size_t aBoundaryOffset, int &aQuarterTurns)
Recover a placed component's rotation, expressed in 90-degree quarter turns.
static constexpr int DT_FP_LAYER_TOP_SILK
DipTrace footprint-graphic layer enum (int3 stored 5 bytes ahead of each shape's font block).
static constexpr int FP_CHAIN_MOUNT_HOLE_OFFSET
static constexpr size_t FONT_BLOCK_FIXED_SIZE
Fixed framing of each v46+ font-shape block, excluding its variable parts: Tahoma(14) + meta(25) + in...
static bool ShouldDumpPadGap(const wxString &aRefdes)
static constexpr int ZONE_FONT_PREAMBLE_TAIL
Zone section preamble constant: int4(-20000), the last field of the font block.
Parser for DipTrace binary .dip board files.
static constexpr EDA_ANGLE ANGLE_90
static constexpr EDA_ANGLE ANGLE_45
const wxChar *const traceDiptraceIo
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
size_t CopperLayerToOrdinal(PCB_LAYER_ID aLayer)
Converts KiCad copper layer enum to an ordinal between the front and back layers.
PCB_LAYER_ID
A quick note on layer IDs:
@ TOP_BOTTOM
Flip top to bottom (around the X axis)
constexpr int MAX_STRING_CHARS
Maximum sane string length (in characters) accepted by the reader.
constexpr int INT4_BIAS
Bias value added to stored 4-byte unsigned integers.
constexpr int LEGACY_STRING_VERSION
Format version at or below which strings use the legacy ASCII encoding (int3 byte-count + raw ASCII b...
@ DT_SHAPE_FILLOBROUND
Filled obround marker (e.g. diode cathode / pin-1 dot)
constexpr int INT3_BIAS
Bias value added to stored 3-byte unsigned integers.
constexpr double DIPTRACE_ANGLE_TO_DEG
DipTrace stores angles with 100 000 units per degree.
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
@ NPTH
like PAD_PTH, but not plated mechanical use only, no connection allowed
@ SMD
Smd pad, appears on the solder paste layer (default)
@ PTH
Plated through hole pad.
static wxString makeKey(const wxString &aFirst, const wxString &aSecond)
Assemble a two part key as a simple concatenation of aFirst and aSecond parts, using a separator.
static bool addSegment(VRML_LAYER &model, IDF_SEGMENT *seg, int icont, int iseg)
std::string FormatDouble2Str(double aValue)
Print a float number without using scientific notation and no trailing 0 This function is intended in...
int fieldD
Raw header int4; matches Pattern.Float3 in DipXML.
std::vector< DT_FP_SHAPE > shapes
Graphics in normalized coordinates.
int rotation
Raw header int4; matches Pattern.Float1 in DipXML (not placement angle)
int positionX
DipTrace units.
int padHeightHint
Raw bbox companion field (pad height in DipTrace units)
size_t regionEndOffset
Component region end offset (next boundary / upper bound)
bool valueVisible
(Currently same flag as refdesVisible)
bool isStandaloneVia
True for explicit standalone via components.
int fieldF
Raw header int3 field F (component kind discriminator)
int drillWidthHint
Raw bbox companion field (drill width in DipTrace units)
int bboxHeight
Footprint Y extent in DipTrace units (for shape scaling)
int placementQuarterTurns
Board-placement angle snapped to 90-degree turns (metadata Id-6 int3)
size_t boundaryOffset
Boundary marker offset for this component record.
std::vector< uint8_t > flags
int padWidthHint
Raw bbox companion field (pad width in DipTrace units)
size_t stringStartOffset
Parsed start offset of library-path string.
int refdesYOffset
Refdes text Y offset from component origin (DipTrace units)
double placementAngleDeg
Exact board-placement angle in degrees (placement section), when available.
std::vector< DT_MOUNT_HOLE > holes
int bboxWidth
Footprint X extent in DipTrace units (for shape scaling)
int layer
0 = top, 1 = bottom
std::vector< DT_PAD > pads
int positionY
DipTrace units.
int drillHeightHint
Raw bbox companion field (drill height in DipTrace units)
bool hasTailData
True if the 37-byte tail was successfully parsed.
bool hasPlacementQuarterTurns
size_t headerEndOffset
Byte offset after parsed component header strings.
int fieldC
Raw header int4; matches Pattern.Float2 in DipXML.
int valueYOffset
Value text Y offset from component origin (DipTrace units)
size_t padRegionEnd
Byte offset after last pad record (for shape finding)
bool refdesVisible
False when text visibility flag is -1.
int fieldA
Raw header int3 field A.
int midY
Arc midpoint Y in normalized units.
int x2
End X in normalized units.
int type
Shape type (DT_SHAPE_TYPE)
int y1
Start Y in normalized units.
int midX
Arc midpoint X in normalized units.
int x1
Start X in normalized units (range ±5000)
int width
Line width in normalized units (-10000 = default)
int layer
DipTrace layer index.
int y2
End Y in normalized units.
int type
Layer type from record field_a (0 = Signal, 1 = Plane)
int planeNetIndex
Plane net DipTrace index from record field_c (-1 = none/Signal)
int fieldD
Possibly default trace width.
int outerDiameter
Non-copper/clearance diameter in DipTrace units.
int drillDiameter
Drill diameter in DipTrace units.
int y
Y offset from component origin in DipTrace units.
int x
X offset from component origin in DipTrace units.
wxString name
Stored net name; may be empty in DipTrace files.
int defaultViaDrillDiam
Default via drill from net routing preamble.
int index
Sequential net index from the DipTrace file.
std::vector< DT_PAD_REF > padRefs
Optional net-to-pad links from routing metadata.
int traceWidth
Default trace width in DipTrace units; parsed but not yet used.
int defaultViaOuterDiam
Default via OD from net routing preamble.
int componentIndex
Component index referenced from net routing metadata.
int padIndex
Pad index (1-based within the component)
uint8_t orientClass
Pad orientation class from pad post-block tail byte.
int x
X offset from component origin in DipTrace units.
std::vector< std::pair< int, int > > polygonVertices
Custom polygon vertices relative to pad center (DipTrace units).
int netIndex
Net index from DipTrace file (-1 = unconnected)
int y
Y offset from component origin in DipTrace units.
uint8_t mountType
Explicit mount class from pad post-block (0=through, 1=SMD)
int style
Pad style (0=ellipse, 1=oval, 2=rectangle, 3=polygon)
int drillWidth
Drill width in DipTrace units (0 for SMD)
int height
Copper pad height in DipTrace units.
int drillHeight
Drill height in DipTrace units (0 for SMD)
wxString label
Functional label (e.g. "POS", "GND")
int index
Sequential pad index within the component (1-based)
int width
Copper pad width in DipTrace units.
wxString number
Pad number/name (e.g. "1", "2")
int lineWidth
DipTrace units.
int y
Y coordinate in DipTrace units.
int viaDrillDiam
Via drill diameter in DipTrace units.
int x
X coordinate in DipTrace units.
int viaStyleIdx
Index into ViaStyle table (-1 = none)
int routeMode
Raw routing-point mode int3 at payload +37 (observed: 0/1/3)
uint8_t routeFlag
Raw routing-point flag byte at payload +22 (semantics unresolved)
int viaOuterDiam
Via outer diameter in DipTrace units.
bool hasVia
True if a via exists at this node.
int layer
Copper layer index (0=top, 1=bottom, 14+=inner)
int width
Track width in DipTrace units.
uint8_t arc
0 = straight segment, 1 = arc segment
int x
X coordinate in DipTrace units.
int y
Y coordinate in DipTrace units.
int field2
Raw int4 payload field 2.
int field0
Raw int3 discriminator/index.
int field5
Raw int4 payload field 5.
int field1
Raw int4 payload field 1.
int field3
Raw int4 payload field 3.
int field4
Raw int4 payload field 4.
int spokeWidth
Thermal relief spoke width in DipTrace units.
uint8_t viaDirect
Raw ViaDirect flag from zone trailer.
uint8_t smdSeparate
Raw SMD_Separate flag from zone trailer.
int regionsCounted
Raw trailer int3, likely CopperPour Regions_Counted from Pcb.exe.
int smdSpokeWidth
Raw SMD_SpokeWidth from zone trailer.
uint8_t regionsDone
Raw RegionsDone flag from zone trailer.
int zoneId
Raw per-zone id from zone trailer (matches DipXML CopperPour@Id)
int minimumArea
Minimum island area scalar in DipTrace units (DipXML: MinimumArea)
int minWidth
Copper pour line width in DipTrace units (DipXML: LineWidth)
int layer
DipTrace layer (0=top, 1=bottom)
uint8_t connectionMode
Raw zone flag byte at header +5.
std::vector< std::pair< int, int > > outline
Outline vertices (x, y) in DipTrace units.
uint8_t rawFlag2
Raw zone flag byte at header +4 (semantics unknown)
int clearance
Zone clearance in DipTrace units.
uint8_t ratlineMode
Raw ratline mode (0=Automatically, 1=All Ratlines, 2=Do Not Hide)
int lineSpacing
Copper pour line spacing in DipTrace units (DipXML: LineSpacing)
int cachedFillRecordCount
Cached-fill record count when payload is 23-byte aligned.
std::vector< DT_ZONE_CACHED_FILL_RECORD > cachedFillRecords
Raw 23-byte cached fill records.
int cachedFillByteLen
Raw bytes between style block and trailer in inter-zone gap.
int smdSpokeMode
Raw SMD_Spoke enum from zone trailer.
int separator
Raw zone separator int3 at header +18.
uint8_t islandInternal
Raw IslandInternal flag from zone trailer.
int boardClearance
Raw board-clearance field from zone trailer.
int netIndex
Net index (-1 = unconnected)
uint8_t islandConnection
Raw IslandConnection flag from zone trailer.
uint8_t islandRegion
Raw IslandRegion flag from zone trailer.
int spokeMode
Raw spoke enum from post-fill style block (0=Direct, 3=4 spoke 45, 4=4 spoke)
uint8_t fillMode
Raw zone flag byte at header +3.
const SHAPE_LINE_CHAIN chain
wxString result
Test unit parsing edge cases and error handling.
wxLogTrace helper definitions.
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
VECTOR2< int32_t > VECTOR2I
@ THERMAL
Use thermal relief for pads.
@ THT_THERMAL
Thermal relief only for THT pads.
@ FULL
pads are covered by copper
#define ZONE_THICKNESS_MIN_VALUE_MM