60#include <unordered_set>
73 0x3B, 0x9A, 0xCA, 0x00,
81 0x3B, 0x9A, 0xCA, 0x00,
134 0x00, 0x54, 0x00, 0x61, 0x00, 0x68,
135 0x00, 0x6F, 0x00, 0x6D, 0x00, 0x61,
164 0x3B, 0x9A, 0xCA, 0x00,
165 0x3B, 0x9A, 0xCA, 0x00,
194 return (
static_cast<uint32_t
>( r ) << 16 )
195 | (
static_cast<uint32_t
>( g ) << 8 )
196 |
static_cast<uint32_t
>( b );
202 return ( (
static_cast<int>( aData[aPos] ) << 16 )
203 | (
static_cast<int>( aData[aPos + 1] ) << 8 )
204 |
static_cast<int>( aData[aPos + 2] ) ) -
INT3_BIAS;
210 unsigned int raw = (
static_cast<unsigned int>( aData[aPos] ) << 24 )
211 | (
static_cast<unsigned int>( aData[aPos + 1] ) << 16 )
212 | (
static_cast<unsigned int>( aData[aPos + 2] ) << 8 )
213 |
static_cast<unsigned int>( aData[aPos + 3] );
216 return static_cast<int>(
static_cast<int64_t
>( raw ) -
INT4_BIAS );
247 static constexpr size_t MAX_LOOKBACK = 4096;
248 static constexpr size_t MAX_LOOKAHEAD = 1024;
250 if( aBoundaryOffset < 6 )
253 size_t scanStart = aBoundaryOffset > MAX_LOOKBACK ? aBoundaryOffset - MAX_LOOKBACK : 6;
254 size_t scanEnd = std::min( aDataSize, aBoundaryOffset + MAX_LOOKAHEAD );
257 size_t bestDistance = MAX_LOOKBACK + MAX_LOOKAHEAD + 1;
262 for(
size_t idPos = scanStart; idPos + 7 <= scanEnd; idPos++ )
267 int quarterTurns =
ReadInt3At( aData, idPos - 6 );
270 uint8_t
flag = aData[idPos + 6];
272 if( quarterTurns < 0 || quarterTurns > 3 || kind < 0 || kind > 3 ||
flag > 1 )
275 if( componentId < 0 || componentId > 100000 )
278 size_t distance = ( idPos > aBoundaryOffset ) ? idPos - aBoundaryOffset
279 : aBoundaryOffset - idPos;
284 aQuarterTurns = quarterTurns;
295 const char* value = std::getenv( aVarName );
297 return value && *value && std::strcmp( value,
"0" ) != 0;
306 const char* filterRaw = std::getenv(
"KICAD_DIPTRACE_DUMP_PAD_REFS" );
308 if( !filterRaw || !*filterRaw )
311 wxString
filter = wxString::FromUTF8( filterRaw ).Lower();
313 if(
filter == wxT(
"*" ) )
316 wxString haystack = wxT(
"," ) +
filter + wxT(
"," );
317 wxString needle = wxT(
"," ) + aRefdes.Lower() + wxT(
"," );
319 return haystack.Contains( needle );
328 const char* filterRaw = std::getenv(
"KICAD_DIPTRACE_DUMP_PAD_REFS" );
330 if( !filterRaw || !*filterRaw )
333 wxString
filter = wxString::FromUTF8( filterRaw ).Lower();
335 if(
filter == wxT(
"*" ) )
338 wxString haystack = wxT(
"," ) +
filter + wxT(
"," );
339 wxString needle = wxT(
"," ) + aRefdes.Lower() + wxT(
"," );
341 return haystack.Contains( needle );
350 const char* filterRaw = std::getenv(
"KICAD_DIPTRACE_DUMP_COMPONENT_REFS" );
352 if( !filterRaw || !*filterRaw )
355 wxString
filter = wxString::FromUTF8( filterRaw ).Lower();
357 if(
filter == wxT(
"*" ) )
360 wxString haystack = wxT(
"," ) +
filter + wxT(
"," );
361 wxString needle = wxT(
"," ) + aRefdes.Lower() + wxT(
"," );
363 return haystack.Contains( needle );
372 const char* filterRaw = std::getenv(
"KICAD_DIPTRACE_DUMP_FOOTPRINT_REFS" );
374 if( !filterRaw || !*filterRaw )
377 wxString
filter = wxString::FromUTF8( filterRaw ).Lower();
379 if(
filter == wxT(
"*" ) )
382 wxString haystack = wxT(
"," ) +
filter + wxT(
"," );
383 wxString needle = wxT(
"," ) + aRefdes.Lower() + wxT(
"," );
385 return haystack.Contains( needle );
401 int aFieldC,
int aFieldD,
int aFieldE,
int aFieldF,
402 uint8_t aSep1, uint8_t aSep2, uint8_t aSep3 )
408 wxT(
"DipTrace: component ref=%s value=%s pat=%s lib=%s flags=[%u,%u,%u,%u] "
409 "layer=%d pos=(%d,%d) rot=%d fieldA=%d fieldB=%d fieldC=%d fieldD=%d "
410 "fieldE=%d fieldF=%d sep=[%u,%u,%u] bbox=(%d,%d) "
412 "boundary=0x%06zX str=0x%06zX hdrEnd=0x%06zX regionEnd=0x%06zX" ),
414 static_cast<unsigned int>( aComp.
flags.size() > 0 ? aComp.
flags[0] : 0 ),
415 static_cast<unsigned int>( aComp.
flags.size() > 1 ? aComp.
flags[1] : 0 ),
416 static_cast<unsigned int>( aComp.
flags.size() > 2 ? aComp.
flags[2] : 0 ),
418 aComp.
positionY, aComp.
rotation, aFieldA, aFieldB, aFieldC, aFieldD, aFieldE, aFieldF,
419 static_cast<unsigned int>( aSep1 ),
static_cast<unsigned int>( aSep2 ),
428 uint32_t u =
static_cast<uint32_t
>( aData[aPos] )
429 | (
static_cast<uint32_t
>( aData[aPos + 1] ) << 8 )
430 | (
static_cast<uint32_t
>( aData[aPos + 2] ) << 16 )
431 | (
static_cast<uint32_t
>( aData[aPos + 3] ) << 24 );
432 return static_cast<int32_t
>( u );
438 uint32_t u =
static_cast<uint32_t
>( aData[aPos] )
439 | (
static_cast<uint32_t
>( aData[aPos + 1] ) << 8 )
440 | (
static_cast<uint32_t
>( aData[aPos + 2] ) << 16 )
441 | (
static_cast<uint32_t
>( aData[aPos + 3] ) << 24 );
443 std::memcpy( &out, &u,
sizeof( out ) );
448static wxString
BytesToHex(
const uint8_t* aData,
size_t aLen );
452 size_t aPosXPos,
size_t aPosYPos,
size_t aRotPos,
453 size_t aFieldCPos,
size_t aFieldDPos )
458 auto dumpOne = [&](
const wxString& aName,
size_t aPos )
464 wxT(
"DipTrace: component raw ref=%s field=%s off=0x%06zX bytes=[%s] "
465 "le_i32=%d le_f32=%s int4=%d" ),
469 dumpOne( wxT(
"posX" ), aPosXPos );
470 dumpOne( wxT(
"posY" ), aPosYPos );
471 dumpOne( wxT(
"rotation" ), aRotPos );
472 dumpOne( wxT(
"fieldC" ), aFieldCPos );
473 dumpOne( wxT(
"fieldD" ), aFieldDPos );
477static wxString
BytesToHex(
const uint8_t* aData,
size_t aLen )
480 out.reserve( aLen * 3 );
482 for(
size_t i = 0; i < aLen; i++ )
487 out += wxString::Format( wxT(
"%02X" ),
static_cast<unsigned int>( aData[i] ) );
496 static const int ANGLES[] = {
497 0, 15708, 31416, 47124, 62832, 9000000, 18000000, 27000000, 36000000
502 for(
int target : ANGLES )
504 if(
std::abs( absVal - target ) <= 8 )
522 bool fullScan =
EnvFlagEnabled(
"KICAD_DIPTRACE_DUMP_COMPONENT_SCAN_FULL" );
527 if( scanEnd <= scanStart + 4 )
531 wxT(
"DipTrace: comp-scan ref=%s pat=%s boundary=0x%06zX str=0x%06zX "
532 "headerEnd=0x%06zX regionEnd=0x%06zX full=%d scan=[0x%06zX..0x%06zX)" ),
536 for(
size_t off = scanStart; off + 3 <= scanEnd; off++ )
544 wxLogTrace(
traceDiptraceIo, wxT(
"DipTrace: comp-scan-hit-i3 ref=%s off=0x%06zX rel=%lld bytes=[%s] int3=%d" ),
545 aComp.
refdes, off,
static_cast<long long>( off - scanStart ), hex, i3 );
548 for(
size_t off = scanStart; off + 4 <= scanEnd; off++ )
558 wxT(
"DipTrace: comp-scan-hit ref=%s off=0x%06zX rel=%lld bytes=[%s] "
559 "int4=%d le_i32=%d" ),
560 aComp.
refdes, off,
static_cast<long long>( off - scanStart ), hex, i4, leI32 );
566 const uint8_t* aData,
size_t aPostDimPos,
size_t aPostDimSize )
571 int fieldA = ( aPostDimSize >= 3 ) ?
ReadInt3At( aData, aPostDimPos ) : 0;
572 int fieldC = ( aPostDimSize >= 7 ) ?
ReadInt3At( aData, aPostDimPos + 4 ) : 0;
573 int fieldE = ( aPostDimSize >= 11 ) ?
ReadInt3At( aData, aPostDimPos + 8 ) : 0;
574 int fieldG = ( aPostDimSize >= 15 ) ?
ReadInt3At( aData, aPostDimPos + 12 ) : 0;
575 int fieldH = ( aPostDimSize >= 18 ) ?
ReadInt3At( aData, aPostDimPos + 15 ) : 0;
576 int fieldI = ( aPostDimSize >= 21 ) ?
ReadInt3At( aData, aPostDimPos + 18 ) : 0;
577 int fieldJ = ( aPostDimSize >= 24 ) ?
ReadInt3At( aData, aPostDimPos + 21 ) : 0;
578 int fieldM = ( aPostDimSize >= 30 ) ?
ReadInt4At( aData, aPostDimPos + 26 ) : 0;
579 int fieldN = ( aPostDimSize >= 34 ) ?
ReadInt4At( aData, aPostDimPos + 30 ) : 0;
580 wxString hex =
BytesToHex( aData + aPostDimPos, aPostDimSize );
583 wxT(
"DipTrace: pad-post ref=%s pad=%s label=%s idx=%d net=%d xy=(%d,%d) wh=(%d,%d) "
584 "drill=(%d,%d) mount=%u orient=%u len=%lu "
585 "A=%d C=%d E=%d G=%d H=%d I=%d J=%d M=%d N=%d hex=[%s]" ),
588 static_cast<unsigned int>( aPad.
orientClass ),
static_cast<unsigned long>( aPostDimSize ), fieldA,
589 fieldC, fieldE, fieldG, fieldH, fieldI, fieldJ, fieldM, fieldN, hex );
594 size_t aGapStart,
size_t aGapEnd )
599 if( aGapEnd <= aGapStart )
601 wxLogTrace(
traceDiptraceIo, wxT(
"DipTrace: pad-gap ref=%s start=0x%06zX end=0x%06zX len=0" ), aComp.
refdes,
602 aGapStart, aGapEnd );
606 size_t gapLen = aGapEnd - aGapStart;
607 size_t sampleLen = std::min<size_t>( gapLen, 96 );
608 wxString hex =
BytesToHex( aData + aGapStart, sampleLen );
611 size_t int3Count = std::min<size_t>( 8, gapLen / 3 );
613 for(
size_t i = 0; i < int3Count; i++ )
616 int3Seq += wxT(
"," );
618 int3Seq += wxString::Format( wxT(
"%d" ),
623 size_t int4Count = std::min<size_t>( 8, gapLen / 4 );
625 for(
size_t i = 0; i < int4Count; i++ )
628 int4Seq += wxT(
"," );
630 int4Seq += wxString::Format( wxT(
"%d" ),
635 wxT(
"DipTrace: pad-gap ref=%s start=0x%06zX end=0x%06zX len=%lu "
636 "int3=[%s] int4=[%s] hex[%lu]=[%s]" ),
637 aComp.
refdes, aGapStart, aGapEnd,
static_cast<unsigned long>( gapLen ), int3Seq, int4Seq,
638 static_cast<unsigned long>( sampleLen ), hex );
643 size_t aTailStart,
int aVisibility,
644 uint8_t aSideFlag1, uint8_t aSideFlag2,
int aOrderIdx,
645 int aRefdesYOffset,
int aValueYOffset,
646 uint8_t aHasOffset, uint8_t aTailTerm )
654 wxT(
"DipTrace: component-tail ref=%s off=0x%06zX vis=%d side=[%u,%u] "
655 "order=%d yoff=[%d,%d] hasOffset=%u term=%u hex=[%s]" ),
656 aComp.
refdes, aTailStart, aVisibility,
static_cast<unsigned int>( aSideFlag1 ),
657 static_cast<unsigned int>( aSideFlag2 ), aOrderIdx, aRefdesYOffset, aValueYOffset,
658 static_cast<unsigned int>( aHasOffset ),
static_cast<unsigned int>( aTailTerm ), hex );
662static void DumpRulesetBlock(
int aRuleSetIndex,
const wxString& aRuleSetName,
int aBlockIndex,
663 const std::array<int, 26>& aValues )
670 for(
size_t i = 0; i < aValues.size(); i++ )
673 values += wxT(
"," );
675 values += wxString::Format( wxT(
"%d" ), aValues[i] );
678 wxLogTrace(
traceDiptraceIo, wxT(
"DipTrace: ruleset[%d] '%s' block[%d] values=[%s]" ), aRuleSetIndex, aRuleSetName,
679 aBlockIndex, values );
683static void DumpZoneHeader(
int aZoneIndex,
size_t aHeaderPos,
const uint8_t* aData,
int aFieldA,
684 int aFlags1,
int aFlags2,
int aFlags3,
int aMinWidth,
int aClearance,
685 int aMinimumArea,
int aSeparator,
int aLayer,
int aFieldB,
int aVtxCount,
686 const wxString& aNetName )
691 wxString headerHex =
BytesToHex( aData + aHeaderPos, 30 );
694 wxT(
"DipTrace: zone[%d] hdr=0x%06zX fieldA=%d flags=[%d,%d,%d] "
695 "lineWidth=%d clearance=%d minimumArea=%d sep=%d layer=%d net=%d('%s') vtx=%d "
697 aZoneIndex, aHeaderPos, aFieldA, aFlags1, aFlags2, aFlags3, aMinWidth, aClearance, aMinimumArea,
698 aSeparator, aLayer, aFieldB, aNetName, aVtxCount, headerHex );
702static void DumpZoneGap(
int aZoneIndex,
size_t aGapStart,
size_t aGapEnd,
const uint8_t* aData )
707 size_t gapLen = aGapEnd - aGapStart;
708 size_t sampleLen = std::min<size_t>( gapLen, 96 );
709 wxString hex =
BytesToHex( aData + aGapStart, sampleLen );
712 size_t int3Count = std::min<size_t>( 8, gapLen / 3 );
714 for(
size_t i = 0; i < int3Count; i++ )
717 int3Vals += wxT(
"," );
719 int3Vals += wxString::Format( wxT(
"%d" ),
724 size_t int4Count = std::min<size_t>( 6, gapLen / 4 );
726 for(
size_t i = 0; i < int4Count; i++ )
729 int4Vals += wxT(
"," );
731 int4Vals += wxString::Format( wxT(
"%d" ),
736 wxT(
"DipTrace: zone[%d] gap start=0x%06zX end=0x%06zX len=%lu int3=[%s] int4=[%s] hex[%lu]=[%s]" ),
737 aZoneIndex, aGapStart, aGapEnd,
static_cast<unsigned long>( gapLen ), int3Vals, int4Vals,
738 static_cast<unsigned long>( sampleLen ), hex );
742static void DumpZoneTail(
int aZoneIndex,
size_t aTailStart,
size_t aSearchEnd,
const uint8_t* aData )
747 size_t tailLen = std::min<size_t>( aSearchEnd - aTailStart, 256 );
748 size_t sampleLen = std::min<size_t>( tailLen, 96 );
749 wxString hex =
BytesToHex( aData + aTailStart, sampleLen );
752 size_t int3Count = std::min<size_t>( 8, tailLen / 3 );
754 for(
size_t i = 0; i < int3Count; i++ )
757 int3Vals += wxT(
"," );
759 int3Vals += wxString::Format( wxT(
"%d" ),
764 size_t int4Count = std::min<size_t>( 6, tailLen / 4 );
766 for(
size_t i = 0; i < int4Count; i++ )
769 int4Vals += wxT(
"," );
771 int4Vals += wxString::Format( wxT(
"%d" ),
776 wxT(
"DipTrace: zone[%d] tail start=0x%06zX end=0x%06zX len=%lu int3=[%s] int4=[%s] hex[%lu]=[%s]" ),
777 aZoneIndex, aTailStart, aTailStart + tailLen,
static_cast<unsigned long>( tailLen ), int3Vals, int4Vals,
778 static_cast<unsigned long>( sampleLen ), hex );
786static size_t StringFieldSize(
const uint8_t* aData,
size_t aDataSize,
size_t aPos,
int aVersion )
790 if( aPos + 3 > aDataSize )
795 if( byteCount < 0 || byteCount >
MAX_STRING_CHARS || aPos + 3 + byteCount > aDataSize )
798 return 3 +
static_cast<size_t>( byteCount );
802 if( aPos + 2 > aDataSize )
805 int charCount = (
static_cast<int>( aData[aPos] ) << 8 )
806 |
static_cast<int>( aData[aPos + 1] );
809 || aPos + 2 +
static_cast<size_t>( charCount ) * 2 > aDataSize )
814 return 2 +
static_cast<size_t>( charCount ) * 2;
824 size_t aPos,
int aVersion,
825 wxString& aOut,
size_t& aNewPos )
830 if( aPos + 3 > aDataSize )
833 const uint8_t* b = aData + aPos;
834 int byteCount =
static_cast<int>( (
static_cast<int>( b[0] ) << 16 )
835 | (
static_cast<int>( b[1] ) << 8 )
836 |
static_cast<int>( b[2] ) ) -
INT3_BIAS;
845 if( byteCount < 0 || byteCount > 500
846 || aPos + 3 +
static_cast<size_t>( byteCount ) > aDataSize )
851 for(
int i = 0; i < byteCount; i++ )
853 char c =
static_cast<char>( aData[aPos + 3 + i] );
855 if( !( ( c >= 0x20 && c < 0x7F ) || c ==
'\r' || c ==
'\n' || c ==
'\t' ) )
859 aOut = wxString::FromAscii(
reinterpret_cast<const char*
>( aData + aPos + 3 ),
861 aNewPos = aPos + 3 + byteCount;
867 if( aPos + 2 > aDataSize )
870 uint16_t cc = (
static_cast<uint16_t
>( aData[aPos] ) << 8 ) | aData[aPos + 1];
879 if( cc > 500 || aPos + 2 +
static_cast<size_t>( cc ) * 2 > aDataSize )
884 size_t base = aPos + 2;
886 for( uint16_t i = 0; i < cc; i++ )
888 uint16_t ch = (
static_cast<uint16_t
>( aData[base + i * 2] ) << 8 )
889 | aData[base + i * 2 + 1];
890 wxChar wch =
static_cast<wxChar
>( ch );
892 if( !wxIsprint( wch ) && wch !=
'\r' && wch !=
'\n' && wch !=
'\t' )
899 aNewPos = aPos + 2 +
static_cast<size_t>( cc ) * 2;
906 const uint8_t* aPattern,
size_t aPatternLen,
907 size_t aStart,
size_t aEnd )
909 std::vector<size_t> offsets;
911 if( aEnd == 0 || aEnd > aDataSize )
916 while( pos + aPatternLen <= aEnd && offsets.size() < 100000 )
918 const uint8_t* found = std::search( aData + pos, aData + aEnd,
919 aPattern, aPattern + aPatternLen );
921 if( found == aData + aEnd )
924 size_t idx =
static_cast<size_t>( found - aData );
925 offsets.push_back( idx );
976 oxMin = std::min( oxMin, v.x );
977 oxMax = std::max( oxMax, v.x );
978 oyMin = std::min( oyMin, v.y );
979 oyMax = std::max( oyMax, v.y );
983 wxT(
"DipTrace: board bbox=(%d,%d)-(%d,%d), outline verts=%zu bounds=(%d,%d)-(%d,%d)" ),
988 wxLogTrace(
traceDiptraceIo, wxT(
"DipTrace: board bbox=(%d,%d)-(%d,%d), no parsed outline vertices" ),
1001 wxT(
"DipTrace: post-component sections parsed; inferring routing-ref pad nets" ) );
1004 wxLogTrace(
traceDiptraceIo, wxT(
"DipTrace: applying board settings" ) );
1006 wxLogTrace(
traceDiptraceIo, wxT(
"DipTrace: creating board outline" ) );
1011 size_t footprintCompCount = 0;
1012 size_t standaloneViaCompCount = 0;
1016 if(
comp.isStandaloneVia )
1017 standaloneViaCompCount++;
1019 footprintCompCount++;
1023 wxT(
"DipTrace: creating %zu footprints (skipping %zu standalone-via components)" ),
1024 footprintCompCount, standaloneViaCompCount );
1028 if(
comp.isStandaloneVia )
1045 size_t compsWithPads = 0;
1046 size_t compsWithShapes = 0;
1050 if( !
comp.pads.empty() )
1053 if( !
comp.shapes.empty() )
1058 wxT(
"DipTrace v%d: %zu components (%zu with pads, %zu with shapes), "
1059 "%zu nets, %zu track chains, %zu zones" ),
1067 catch(
const std::exception& e )
1070 m_reader.GetOffset(), wxString::FromUTF8( e.what() ) );
1077 switch( aDipTraceLayer )
1085 case 8:
return F_Fab;
1086 case 9:
return B_Fab;
1090 if( aDipTraceLayer == 0 )
1093 if( aDipTraceLayer == 1 )
1096 if( aDipTraceLayer >= 14 && aDipTraceLayer <= 44 )
1098 int innerIdx = aDipTraceLayer - 14;
1100 if( innerIdx <= 30 )
1116 int ordinal = it->second;
1117 int copperCount = std::max( 2,
static_cast<int>(
m_layers.size() ) );
1122 if( ordinal >= copperCount - 1 )
1134 return static_cast<int>(
static_cast<int64_t
>( aDipTraceCoord ) * 100 / 3 );
1150 uint8_t magicLen =
m_reader.ReadByte();
1152 if( magicLen != 7 && magicLen != 11 )
1153 THROW_IO_ERRORF(
_(
"DipTrace: invalid magic length %u (expected 7 or 11)" ), magicLen );
1155 std::array<uint8_t, 11> magic = {};
1156 m_reader.ReadBytes( magic.data(), magicLen );
1158 if( std::memcmp( magic.data(),
"DTBOARD", 7 ) != 0 )
1159 THROW_IO_ERROR(
_(
"DipTrace: not a valid .dip board file (bad magic)" ) );
1166 std::string magicSuffix(
reinterpret_cast<const char*
>( magic.data() + 7 ),
1169 if( magicSuffix.size() != 4
1170 || std::isdigit(
static_cast<unsigned char>( magicSuffix[0] ) ) == 0
1171 || magicSuffix[1] !=
'.'
1172 || std::isdigit(
static_cast<unsigned char>( magicSuffix[2] ) ) == 0
1173 || std::isdigit(
static_cast<unsigned char>( magicSuffix[3] ) ) == 0 )
1175 THROW_IO_ERROR(
_(
"DipTrace: invalid legacy board version suffix" ) );
1178 int parsedMinor = ( magicSuffix[2] -
'0' ) * 10 + ( magicSuffix[3] -
'0' );
1213 int vertexCount =
m_reader.ReadInt3();
1217 if( vertexCount < 0 || vertexCount > 1000000 )
1218 THROW_IO_ERRORF(
_(
"DipTrace import: invalid outline vertex count %d." ), vertexCount );
1223 for(
int i = 0; i < vertexCount; i++ )
1243 for(
int i = 0; i < 4; i++ )
1246 for(
int i = 0; i < 3; i++ )
1253 int layerCount =
m_reader.ReadInt3();
1256 if( layerCount < 0 || layerCount > 100000 )
1257 THROW_IO_ERRORF(
_(
"DipTrace import: invalid layer count %d." ), layerCount );
1263 for(
int i = 0; i < layerCount; i++ )
1280 int ordinal =
static_cast<int>(
m_layers.size() );
1289 for(
int i = 0; i < 6; i++ )
1302 uint8_t legacyPadding[12];
1303 m_reader.ReadBytes( legacyPadding,
sizeof( legacyPadding ) );
1322 const uint8_t* data =
m_reader.GetData();
1325 && data[
next + 1] <= 100 )
1332 m_reader.ReadBytes( padTail,
sizeof( padTail ) );
1335 int fieldCOrGroupCount =
m_reader.ReadInt3();
1337 if( fieldCOrGroupCount > 0 )
1343 int patternGroupCount =
m_reader.ReadInt3();
1352 if( aGroupCount < 0 || aGroupCount > 10000 )
1354 THROW_IO_ERRORF(
_(
"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 )
1365 THROW_IO_ERRORF(
_(
"DipTrace: invalid pattern-name block count %d" ), blockCount );
1367 for(
int j = 0; j < blockCount; j++ )
1378 if( aGroupCount < 0 || aGroupCount > 10000 )
1380 THROW_IO_ERRORF(
_(
"DipTrace: invalid pattern-style group count %d at offset 0x%06zX" ),
1381 aGroupCount,
m_reader.GetOffset() - 3 );
1386 for(
int i = 0; i < aGroupCount; i++ )
1391 m_reader.ReadBytes( color,
sizeof( color ) );
1397 int entryCount =
m_reader.ReadInt3();
1399 if( entryCount < 0 || entryCount > 10000 )
1400 THROW_IO_ERRORF(
_(
"DipTrace: invalid pattern-style entry count %d" ), entryCount );
1425 bool dumpRuleSets =
EnvFlagEnabled(
"KICAD_DIPTRACE_DUMP_RULESETS" );
1437 if(
name.StartsWith( wxT(
"ViaStyle" ) ) )
1458 int ruleSetCount =
m_reader.ReadInt3();
1460 for(
int i = 0; i < ruleSetCount; i++ )
1462 wxString setName =
m_reader.ReadString();
1463 int setFieldA =
m_reader.ReadInt3();
1464 uint8_t flags[4] = { 0, 0, 0, 0 };
1466 for(
int f = 0; f < 4; f++ )
1469 int blockCount =
m_reader.ReadInt3();
1474 wxT(
"DipTrace: ruleset[%d] '%s' fieldA=%d flags=[%u,%u,%u,%u] blocks=%d" ), i, setName,
1475 setFieldA,
static_cast<unsigned int>( flags[0] ),
static_cast<unsigned int>( flags[1] ),
1476 static_cast<unsigned int>( flags[2] ),
static_cast<unsigned int>( flags[3] ), blockCount );
1479 for(
int b = 0; b < blockCount; b++ )
1481 std::array<int, 26> blockValues;
1483 for(
int v = 0; v < 25; v++ )
1484 blockValues[v] =
m_reader.ReadInt4();
1486 blockValues[25] = 0;
1493 int extraCount =
m_reader.ReadInt3();
1495 if( extraCount < 0 || extraCount > 10000 )
1496 THROW_IO_ERRORF(
_(
"DipTrace: invalid design-rule extra count %d" ), extraCount );
1498 for(
int e = 0; e < extraCount; e++ )
1502 m_reader.ReadBytes( rawPad,
sizeof( rawPad ) );
1506 if( i + 1 < ruleSetCount )
1514 static const uint8_t marker[] = { 0x4D, 0x7C, 0x6D, 0x00 };
1515 uint8_t
actual[
sizeof( marker )] = {};
1516 size_t markerOffset =
m_reader.GetOffset();
1520 if( std::memcmp(
actual, marker,
sizeof( marker ) ) != 0 )
1521 THROW_IO_ERRORF(
_(
"DipTrace: invalid ruleset transition marker at 0x%06zX" ), markerOffset );
1528 const uint8_t* data =
m_reader.GetData();
1531 && data[
next] == 0x01 && data[
next + 1] == 0x00 && data[
next + 2] == 0x14
1532 && data[
next + 3] == 0x89 && data[
next + 4] == 0x03 )
1535 m_reader.ReadBytes( suffix,
sizeof( suffix ) );
1545std::vector<std::pair<size_t, size_t>>
1548 const uint8_t* data =
m_reader.GetData();
1549 size_t fileSize =
m_reader.GetFileSize();
1551 static const int STRING_OFFSETS[] = { 14, 15, 16, 17, 18, 19, 20 };
1553 auto isBoundaryCoreAt = [&](
size_t aPos ) ->
bool
1560 auto stringOkAt = [&](
size_t aPos ) ->
bool
1570 auto cleanDeltaAt = [&](
size_t aBoundary ) ->
int
1572 for(
int d : STRING_OFFSETS )
1579 && s.length() >= 1 )
1593 int globalDelta = 14;
1597 if( !isBoundaryCoreAt( p ) )
1600 int d = cleanDeltaAt( p );
1610 std::vector<std::pair<size_t, size_t>> out;
1621 auto nextBoundaryAfter = [&](
size_t aFrom ) ->
size_t
1625 if( isBoundaryCoreAt( cand )
1626 && stringOkAt( cand +
static_cast<size_t>( globalDelta ) ) )
1639 out.emplace_back( b, b +
static_cast<size_t>( globalDelta ) );
1641 size_t next = nextBoundaryAfter( b );
1651 int declaredCount = ( parsedEnd + 3 <= fileSize ) ?
ReadInt3At( data, parsedEnd ) : -1;
1653 if( declaredCount > 0 && declaredCount <= 10000
1654 &&
static_cast<int>( out.size() ) != declaredCount )
1673 size_t projLib =
m_reader.FindString( wxT(
"Project Libraries" ), 0, 0 );
1686 struct ValidatedBoundary
1688 size_t boundaryOffset;
1692 std::vector<ValidatedBoundary> validated;
1693 bool fieldWalked =
false;
1699 std::vector<std::pair<size_t, size_t>> walk =
1715 std::set<size_t> stdSet( stdOffsets.begin(), stdOffsets.end() );
1716 std::vector<size_t> pureAlt;
1718 for(
size_t off : altOffsets )
1720 if( stdSet.find( off ) == stdSet.end() )
1721 pureAlt.push_back( off );
1724 std::vector<size_t> allBoundaries( stdOffsets );
1725 allBoundaries.insert( allBoundaries.end(), pureAlt.begin(), pureAlt.end() );
1726 std::sort( allBoundaries.begin(), allBoundaries.end() );
1728 allBoundaries.erase( std::unique( allBoundaries.begin(), allBoundaries.end() ),
1729 allBoundaries.end() );
1731 if( allBoundaries.empty() )
1736 static const int STRING_OFFSETS[] = { 14, 15, 16, 17, 18, 19, 20 };
1737 int stringDelta = 14;
1739 for(
size_t bOff : allBoundaries )
1741 if( parsedEnd > 50 && bOff < parsedEnd - 50 )
1746 for(
int delta : STRING_OFFSETS )
1748 size_t candidate = bOff +
delta;
1755 if( str.length() >= 1 )
1757 stringDelta =
delta;
1769 for(
size_t bOff : allBoundaries )
1771 if( parsedEnd > 50 && bOff < parsedEnd - 50 )
1774 size_t candidate = bOff + stringDelta;
1781 validated.push_back( { bOff, candidate } );
1788 if( validated.size() <= 1 && allBoundaries.size() > 1 )
1792 for(
size_t bOff : allBoundaries )
1794 if( parsedEnd > 50 && bOff < parsedEnd - 50 )
1799 for(
int delta : STRING_OFFSETS )
1801 size_t candidate = bOff +
delta;
1808 validated.push_back( { bOff, candidate } );
1814 if( !found && bOff + stringDelta + 3 <
m_reader.GetFileSize() )
1815 validated.push_back( { bOff, bOff + stringDelta } );
1821 if( !walk.empty() && walk.size() == validated.size() )
1825 for(
size_t i = 0; i < walk.size(); i++ )
1827 if( walk[i].first != validated[i].boundaryOffset )
1829 fieldWalked =
false;
1836 if( validated.empty() )
1838 wxLogTrace(
traceDiptraceIo, wxT(
"DipTrace: no validated component boundaries found" ) );
1843 int consecutiveFailures = 0;
1844 bool seenSuccess =
false;
1845 static constexpr int MAX_CONSECUTIVE_FAILURES = 3;
1846 static constexpr int MAX_COMPONENTS = 10000;
1848 for(
size_t vi = 0; vi < validated.size(); vi++ )
1850 if(
static_cast<int>(
m_components.size() ) >= MAX_COMPONENTS )
1853 const ValidatedBoundary& vb = validated[vi];
1854 m_reader.SetOffset( vb.stringStart );
1857 comp.boundaryOffset = vb.boundaryOffset;
1858 comp.stringStartOffset = vb.stringStart;
1867 size_t regionEnd = ( vi + 1 < validated.size() )
1868 ? validated[vi + 1].boundaryOffset
1870 comp.regionEndOffset = regionEnd;
1885 consecutiveFailures = 0;
1892 consecutiveFailures++;
1894 if( consecutiveFailures >= MAX_CONSECUTIVE_FAILURES )
1909 const uint8_t* data =
m_reader.GetData();
1910 size_t dataSize =
m_reader.GetFileSize();
1912 static constexpr double RAD_FIXED_TO_DEG = ( 180.0 /
M_PI ) / 1.0e4;
1913 static constexpr int MAX_ANGLE_FIXED = 200000;
1922 auto isThreeInt3 = [&](
size_t aPos ) ->
bool
1924 return aPos + 9 <= dataSize
1925 && data[aPos] == 0x0F && data[aPos + 1] == 0x42
1926 && data[aPos + 3] == 0x0F && data[aPos + 4] == 0x42
1927 && data[aPos + 6] == 0x0F && data[aPos + 7] == 0x42;
1930 std::vector<std::pair<size_t, double>> headers;
1932 for(
size_t off = 4; off + 32 <= dataSize; off++ )
1934 bool full = data[off] == 0x01 && data[off + 1] == 0x01 && isThreeInt3( off + 2 );
1935 bool compact = data[off] == 0x00 && data[off + 1] == 0x00 && isThreeInt3( off + 2 )
1936 && off + 22 <= dataSize
1937 && std::memcmp( data + off + 11,
"\0\0\0\0\0\0\0\0\0", 9 ) == 0
1938 && data[off + 20] == 0x0F && data[off + 21] == 0x42;
1940 if( !full && !compact )
1945 if( fixed >= -MAX_ANGLE_FIXED && fixed <= MAX_ANGLE_FIXED )
1946 headers.emplace_back( off,
static_cast<double>( fixed ) * RAD_FIXED_TO_DEG );
1949 if( headers.empty() )
1955 std::map<wxString, double> refdesAngle;
1957 for(
size_t off = 0; off + 4 <= dataSize; off++ )
1959 size_t len = (
static_cast<size_t>( data[off] ) << 8 ) | data[off + 1];
1961 if( len < 1 || len > 12 || off + 2 + 2 * len > dataSize )
1965 bool asciiUtf16 =
true;
1966 bool firstAlpha =
false;
1967 bool hasDigit =
false;
1969 for(
size_t c = 0; c < len; c++ )
1971 uint8_t hi = data[off + 2 + 2 * c];
1972 uint8_t lo = data[off + 3 + 2 * c];
1974 if( hi != 0 || lo < 0x20 || lo > 0x7E )
1981 firstAlpha = std::isalpha( lo );
1983 if( std::isdigit( lo ) )
1986 refdes +=
static_cast<char>( lo );
1989 if( !asciiUtf16 || !firstAlpha || !hasDigit )
1993 auto it = std::upper_bound( headers.begin(), headers.end(), off,
1994 [](
size_t aPos,
const std::pair<size_t, double>& aHdr )
1995 { return aPos < aHdr.first; } );
1997 if( it == headers.begin() )
2000 refdesAngle.emplace( wxString::FromUTF8( refdes ), ( it - 1 )->second );
2007 auto it = refdesAngle.find(
comp.refdes );
2009 if( it != refdesAngle.end() )
2011 comp.placementAngleDeg = it->second;
2012 comp.hasPlacementAngle =
true;
2017 wxLogTrace(
traceDiptraceIo, wxT(
"DipTrace: keyed %zu exact placement angles by refdes" ), applied );
2047 bool fatalHeaderError =
false;
2051 int quarterTurns = 0;
2069 aComp.
flags.resize( 4 );
2071 for(
int i = 0; i < 4; i++ )
2075 for(
int i = 0; i < 4; i++ )
2077 if( aComp.
flags[i] > 10 )
2084 fatalHeaderError =
true;
2085 THROW_IO_ERRORF(
_(
"DipTrace: invalid component flag byte %u at boundary 0x%06zX" ),
2086 static_cast<unsigned int>( aComp.
flags[i] ), aBoundaryOffset );
2096 size_t posXPos =
m_reader.GetOffset();
2098 size_t posYPos =
m_reader.GetOffset();
2100 size_t rotPos =
m_reader.GetOffset();
2102 size_t fieldCPos =
m_reader.GetOffset();
2106 uint8_t sep1 =
m_reader.ReadByte();
2111 size_t fieldDPos =
m_reader.GetOffset();
2115 uint8_t sep2 =
m_reader.ReadByte();
2116 uint8_t sep3 =
m_reader.ReadByte();
2118 if( sep2 > 1 || sep3 != 0 )
2151 fieldCPos, fieldDPos );
2155 if( fatalHeaderError )
2176 const uint8_t* data =
m_reader.GetData();
2177 size_t dataSize =
m_reader.GetFileSize();
2179 if( aRegionEnd > dataSize )
2180 aRegionEnd = dataSize;
2185 auto isPad1At = [&](
size_t aPos ) ->
bool
2187 if( aPos < aRegionStart || aPos + PAD_PRE_HEADER_SIZE + 4 > aRegionEnd )
2210 size_t labelPos = namePos + nameLen;
2216 size_t dimPos = labelPos + labelLen;
2224 return w > 0 && h > 0 && w <= 10000000 && h <= 10000000;
2227 size_t chainPos = 0;
2238 if( isPad1At( candidate ) )
2239 chainPos = candidate;
2246 static const uint8_t IDX1_PATTERN[] = { 0x0F, 0x42, 0x41 };
2248 std::vector<size_t> matches =
FindAllBoundaries( data, dataSize, IDX1_PATTERN, 3,
2249 aRegionStart, aRegionEnd );
2251 for(
size_t pos : matches )
2253 if( isPad1At( pos ) )
2266 wxLogTrace(
traceDiptraceIo, wxT(
"DipTrace: pad 1 not found in region 0x%06zX-0x%06zX for '%s'" ),
2274 for(
int padNum = 1; padNum < 500; padNum++ )
2280 int padNetIndex =
ReadInt3At( data, chainPos + 3 );
2282 int padY =
ReadInt4At( data, chainPos + 10 );
2284 if( padIndex != padNum )
2296 if( nameFieldLen == 0 )
2299 size_t labelPos = namePos + nameFieldLen;
2302 if( labelFieldLen == 0 )
2305 size_t dimPos = labelPos + labelFieldLen;
2313 int drillH =
ReadInt4At( data, dimPos + 12 );
2315 if( padW <= 0 || padH <= 0 || padW > 10000000 || padH > 10000000 )
2324 int padStyleC =
ReadInt3At( data, postDimPos + 4 );
2327 pad.index = padIndex;
2328 pad.netIndex = padNetIndex;
2333 pad.drillWidth = drillW;
2334 pad.drillHeight = drillH;
2335 pad.style = padStyleC;
2336 pad.mountType = data[postDimPos + 3];
2338 size_t afterName = 0;
2341 size_t afterLabel = 0;
2346 if(
pad.number.IsEmpty() && !
pad.label.IsEmpty() )
2350 if( padStyleC == 3 )
2352 int vertexCount =
ReadInt3At( data, postDimPos + 8 );
2354 if( vertexCount > 0 && vertexCount <= 200
2361 for(
int vi = 0; vi < vertexCount; vi++ )
2365 pad.polygonVertices.emplace_back( vx, vy );
2375 if( postDimPos + postDimSize > aRegionEnd )
2378 pad.orientClass = data[postDimPos + postDimSize - 1];
2383 chainPos = postDimPos + postDimSize;
2454 size_t aSearchEnd, std::vector<DT_MOUNT_HOLE>& aHoles )
2456 static constexpr int MAX_REASONABLE_DIM = 50000000;
2457 static constexpr int MAX_HOLE_COUNT = 64;
2465 int countField =
ReadInt3At( aData, aBlockStart );
2466 int holeCount = countField - 2;
2468 if( holeCount <= 0 || holeCount > MAX_HOLE_COUNT )
2471 uint8_t headerFlag = aData[aBlockStart + 3];
2473 if( headerFlag > 1 )
2476 for(
int i = 0; i < 4; i++ )
2478 if(
ReadInt4At( aData, aBlockStart + 4 +
static_cast<size_t>( i ) * 4 ) != 0 )
2484 size_t holeEnd = holeStart + holesBytes;
2485 size_t termPos = holeEnd;
2491 std::vector<DT_MOUNT_HOLE> parsedHoles;
2492 parsedHoles.reserve(
static_cast<size_t>( holeCount ) );
2494 for(
int hi = 0; hi < holeCount; hi++ )
2497 uint8_t holeFlagA = aData[hp];
2498 uint8_t holeFlagB = aData[hp + 1];
2500 if( holeFlagA != 0 || holeFlagB > 1 )
2508 if(
std::abs( x ) > MAX_REASONABLE_DIM ||
std::abs( y ) > MAX_REASONABLE_DIM
2509 || outer <= 0 || outer > MAX_REASONABLE_DIM
2510 || drill <= 0 || drill > MAX_REASONABLE_DIM
2521 parsedHoles.push_back( hole );
2524 if( aData[termPos] != 0 || aData[termPos + 1] != 0 )
2527 for(
int i = 0; i < 4; i++ )
2529 if(
ReadInt4At( aData, trailerPos +
static_cast<size_t>( i ) * 4 ) != 0 )
2533 aHoles = std::move( parsedHoles );
2541 wxUnusedVar( aRegionStart );
2542 aComp.
holes.clear();
2547 const uint8_t* data =
m_reader.GetData();
2548 size_t dataSize =
m_reader.GetFileSize();
2550 if( aRegionEnd > dataSize )
2551 aRegionEnd = dataSize;
2553 size_t searchEnd = aRegionEnd;
2558 std::vector<size_t> candidates;
2566 if( shapeCount > 0 && shapeCount <= 500 )
2569 candidates.push_back( shapeStart +
static_cast<size_t>( shapeCount ) * recSize );
2578 if( shapeCount >= 3 && shapeCount <= 200 )
2581 +
static_cast<size_t>( shapeCount - 1 )
2589 std::vector<size_t> uniqueCandidates;
2590 uniqueCandidates.reserve( candidates.size() );
2592 for(
size_t pos : candidates )
2594 if( std::find( uniqueCandidates.begin(), uniqueCandidates.end(), pos ) == uniqueCandidates.end() )
2595 uniqueCandidates.push_back( pos );
2598 size_t decodedAt = 0;
2600 for(
size_t pos : uniqueCandidates )
2609 if( decodedAt == 0 )
2615 wxT(
"DipTrace: mount-holes ref=%s count=%zu off=0x%06zX "
2616 "padEnd=0x%06zX gap=%zu" ),
2628 const uint8_t* data =
m_reader.GetData();
2629 size_t dataSize =
m_reader.GetFileSize();
2631 if( aRegionEnd > dataSize )
2632 aRegionEnd = dataSize;
2638 if( aComp.
shapes.empty() )
2646 wxLogTrace(
traceDiptraceIo, wxT(
"DipTrace: no pad region end for shape finding in '%s'" ),
2655 wxLogTrace(
traceDiptraceIo, wxT(
"DipTrace: shape region beyond component bounds for '%s'" ),
2665 if( shapeCount <= 0 || shapeCount > 500 )
2669 size_t shapeEnd = shapeStart +
static_cast<size_t>( shapeCount ) * recSize;
2671 if( shapeEnd > aRegionEnd )
2674 for(
int i = 0; i < shapeCount; i++ )
2676 size_t rp = shapeStart +
static_cast<size_t>( i ) * recSize;
2708 aComp.
shapes.push_back( shape );
2716 const uint8_t* data =
m_reader.GetData();
2717 size_t dataSize =
m_reader.GetFileSize();
2719 if( aRegionEnd > dataSize )
2720 aRegionEnd = dataSize;
2722 auto isTahomaAt = [&](
size_t aPos ) ->
bool
2728 auto u16beAt = [&](
size_t aPos ) ->
int
2730 return (
static_cast<int>( data[aPos] ) << 8 ) |
static_cast<int>( data[aPos + 1] );
2737 std::vector<size_t> fontBlocks;
2738 bool fieldWalked =
false;
2746 if( preLabel >= 0 && preLabel <= 256 )
2751 while( isTahomaAt( bs ) )
2755 bool slot0InBounds = ( body + 3 <= aRegionEnd );
2757 for(
int n = 0; n <= 3; n++ )
2759 if( body +
static_cast<size_t>( n ) * 8 + 3 > aRegionEnd )
2762 int st =
ReadInt3At( data, body +
static_cast<size_t>( n ) * 8 );
2764 if( st == 0 || st == 1 || st == 2 || st == 3 || st == 5 || st == 6 || st == 7
2780 fontBlocks.push_back( bs );
2792 if( lcPos + 2 > aRegionEnd )
2798 int lc = u16beAt( lcPos );
2800 if( lc < 0 || lc > 256 )
2808 fontBlocks.push_back( bs );
2811 + 2 *
static_cast<size_t>( lc );
2817 fieldWalked = walkOk && !fontBlocks.empty();
2826 aRegionStart, aRegionEnd );
2829 if( fontBlocks.empty() )
2832 size_t shapesBefore = aComp.
shapes.size();
2834 for(
size_t bi = 0; bi < fontBlocks.size(); bi++ )
2836 size_t blockStart = fontBlocks[bi];
2837 size_t nextBoundary = ( bi + 1 < fontBlocks.size() ) ? fontBlocks[bi + 1]
2842 if( headerEnd + 19 > nextBoundary )
2846 int lineWidth =
ReadInt4At( data, metaStart + 18 );
2851 int layerIdx = ( blockStart >= 5 ) ?
ReadInt3At( data, blockStart - 5 ) : 0;
2853 size_t bodyPos = headerEnd;
2868 if( x1 == 0 && y1 == 0 && x2 == 0 && y2 == 0 )
2871 int shapeType =
ReadInt3At( data, bodyPos + 16 );
2878 shape.
width = lineWidth;
2879 shape.
layer = layerIdx;
2883 if( shapeType == 0 )
2887 else if( shapeType == 1 || shapeType == 5 )
2891 else if( shapeType == 3 )
2899 if( bodyPos + 43 <= nextBoundary )
2918 aComp.
shapes.push_back( shape );
2921 if( !fieldWalked && aComp.
shapes.size() > shapesBefore )
2929 wxUnusedVar( aRegionStart );
2934 const uint8_t* data =
m_reader.GetData();
2935 size_t dataSize =
m_reader.GetFileSize();
2937 if( aRegionEnd > dataSize )
2938 aRegionEnd = dataSize;
2946 if( shapeCount < 3 || shapeCount > 200 )
2952 if( shapeEnd > aRegionEnd )
2955 std::vector<DT_FP_SHAPE> decoded;
2956 decoded.reserve(
static_cast<size_t>( shapeCount ) );
2958 auto inNormRange = [](
int aVal ) ->
bool
2964 for(
int i = 1; i + 1 < shapeCount; i++ )
2974 if( !inNormRange( x1 ) || !inNormRange( y1 ) || !inNormRange( x2 ) || !inNormRange( y2 ) )
2978 shape.
width = width;
2995 if( !inNormRange( x3 ) || !inNormRange( y3 ) )
3011 decoded.push_back( shape );
3014 if( !decoded.empty() )
3015 aComp.
shapes.insert( aComp.
shapes.end(), decoded.begin(), decoded.end() );
3028 const uint8_t* data =
m_reader.GetData();
3030 size_t savedOffset =
m_reader.GetOffset();
3031 bool parsed =
false;
3033 auto tryParseTailAt = [&](
size_t aTailStart ) ->
bool
3058 m_reader.SetOffset( aTailStart + 11 );
3063 if( check1 != 0 || check2 != 0 )
3066 uint8_t sideFlag1 =
m_reader.ReadByte();
3068 uint8_t sideFlag2 =
m_reader.ReadByte();
3069 int orderIdx =
m_reader.ReadInt3();
3071 int refdesYOffset =
m_reader.ReadInt4();
3072 int valueYOffset =
m_reader.ReadInt4();
3073 uint8_t hasOffset =
m_reader.ReadByte();
3074 uint8_t tailTerm =
m_reader.ReadByte();
3079 if( hasOffset > 1 || tailTerm != 0 )
3082 if(
std::abs( refdesYOffset ) > 50000000 ||
std::abs( valueYOffset ) > 50000000 )
3096 if( sideFlag1 == 1 && sideFlag2 == 1 )
3100 orderIdx, refdesYOffset, valueYOffset, hasOffset, tailTerm );
3110 parsed = tryParseTailAt( canonicalTailStart );
3114 size_t dumpLen = std::min<size_t>( 96, aRegionEnd );
3115 size_t dumpStart = aRegionEnd - dumpLen;
3116 wxString hex =
BytesToHex( data + dumpStart, dumpLen );
3119 wxT(
"DipTrace: component-tail-missing ref=%s regionEnd=0x%06zX "
3120 "tailHexStart=0x%06zX len=%lu hex=[%s]" ),
3121 aComp.
refdes, aRegionEnd, dumpStart,
static_cast<unsigned long>( dumpLen ), hex );
3134 size_t postComp =
m_reader.GetOffset();
3139 size_t projLibOffset =
m_reader.FindString( wxT(
"Project Libraries" ), 0, 0 );
3140 size_t gapEnd = ( projLibOffset !=
NOT_FOUND ) ? projLibOffset :
m_reader.GetFileSize();
3156 size_t pos = aSearchStart;
3157 auto textRecordsLookValid =
3158 [&](
size_t aRecordStart,
int aCount,
size_t aSectionEnd ) ->
bool
3160 size_t savedOffset =
m_reader.GetOffset();
3161 m_reader.SetOffset( aRecordStart );
3165 for(
int ti = 0; ti < aCount; ti++ )
3180 int lineWidth =
m_reader.ReadInt4();
3183 if( lineWidth < 0 || lineWidth > 10000000 || layer < -100 || layer > 100 )
3184 throw std::runtime_error(
"invalid text metrics" );
3202 if( ti < aCount - 1 )
3208 if(
m_reader.GetOffset() > aSectionEnd )
3209 throw std::runtime_error(
"text section overrun" );
3215 catch(
const std::exception& )
3222 while( pos + 20 < aSearchEnd )
3229 size_t countPos = idx + 9;
3231 if( countPos + 5 > aSearchEnd )
3234 const uint8_t* data =
m_reader.GetData();
3235 const uint8_t* b = data + countPos;
3236 int countVal =
static_cast<int>( (
static_cast<int>( b[0] ) << 16 )
3237 | (
static_cast<int>( b[1] ) << 8 )
3238 |
static_cast<int>( b[2] ) ) -
INT3_BIAS;
3240 if( countVal >= 1 && countVal <= 1000 )
3242 uint8_t flag1 = data[countPos + 3];
3243 uint8_t flag2 = data[countPos + 4];
3245 if( flag1 == 1 && flag2 == 0 )
3247 size_t recordStart = countPos + 5;
3249 if( textRecordsLookValid( recordStart, countVal, aSearchEnd ) )
3265 for(
int ti = 0; ti < aCount; ti++ )
3304 if( ti < aCount - 1 )
3343 aSearchStart, aSearchEnd );
3345 static constexpr int MAX_NETS = 10000;
3346 static constexpr int MAX_REASONABLE_WIDTH = 5000000;
3348 size_t firstNetSentinel = 0;
3350 for(
size_t sentOff : sentinelOffsets )
3352 if(
static_cast<int>(
m_nets.size() ) >= MAX_NETS )
3358 if( pos + 3 + 3 + 4 + 4 + 2 >
m_reader.GetFileSize() )
3364 bool acceptedNetRecord =
false;
3368 int netIndex =
m_reader.ReadInt3();
3376 if( field0 < 0 || field0 > 10 || netIndex < 0 || netIndex >= MAX_NETS )
3379 if( width1 < 0 || width1 > MAX_REASONABLE_WIDTH
3380 || width2 < 0 || width2 > MAX_REASONABLE_WIDTH )
3385 bool expectedNetIndex = netIndex ==
static_cast<int>(
m_nets.size() );
3386 acceptedNetRecord = expectedNetIndex;
3391 if( expectedNetIndex )
3393 THROW_IO_ERRORF(
_(
"DipTrace import: invalid net name for net index %d at offset 0x%06zX." ),
3400 net.
index = netIndex;
3406 wxLogTrace(
traceDiptraceIo, wxT(
"DipTrace: net idx=%d name=%s width1=%d width2=%d" ), netIndex,
name,
3412 if( acceptedNetRecord )
3419 if( firstNetSentinel == 0 )
3420 firstNetSentinel = sentOff;
3424 m_nets.push_back( std::move( net ) );
3432 bool netSectionFieldAnchored =
false;
3434 if( firstNetSentinel >= 5 && !
m_nets.empty() )
3437 ==
static_cast<int>(
m_nets.size() );
3440 if( !
m_nets.empty() && !netSectionFieldAnchored )
3443 size_t totalNodes = 0;
3444 size_t viaStyleNodes = 0;
3445 size_t routeFlagNodes = 0;
3446 size_t viaStyleAndRouteFlagNodes = 0;
3447 size_t routeFlagOnlyNodes = 0;
3448 size_t routeMode0Nodes = 0;
3449 size_t routeMode1Nodes = 0;
3450 size_t routeMode3Nodes = 0;
3451 size_t routeModeOtherNodes = 0;
3468 if( hasStyle && hasFlag )
3469 viaStyleAndRouteFlagNodes++;
3471 routeFlagOnlyNodes++;
3488 routeModeOtherNodes++;
3495 wxT(
"DipTrace: parsed %zu net names, %zu track chains, %zu nodes "
3496 "(viaStyle=%zu, routeFlag=%zu, both=%zu, flagOnly=%zu, "
3497 "routeMode[0]=%zu, routeMode[1]=%zu, routeMode[3]=%zu, routeMode[other]=%zu)" ),
3499 viaStyleAndRouteFlagNodes, routeFlagOnlyNodes, routeMode0Nodes, routeMode1Nodes, routeMode3Nodes,
3500 routeModeOtherNodes );
3520 size_t startPos =
m_reader.GetOffset();
3522 static constexpr size_t MAX_NET_BODY = 65536;
3523 size_t scanEnd = std::min( startPos + MAX_NET_BODY,
m_reader.GetFileSize() );
3527 startPos + 20, scanEnd );
3529 if( nextSentinel != std::string::npos )
3530 scanEnd = nextSentinel;
3532 const uint8_t* data =
m_reader.GetData();
3533 size_t fileSize =
m_reader.GetFileSize();
3535 size_t chainScanStart = startPos;
3536 static constexpr int MAX_REASONABLE_VIA_DIM = 5000000;
3537 static constexpr int MAX_PADREFS_PER_COMPONENT = 12;
3539 int componentCount =
static_cast<int>(
m_components.size() );
3540 int maxReasonablePadRefs = std::max( 512, componentCount * MAX_PADREFS_PER_COMPONENT );
3544 if( startPos + 22 <= scanEnd )
3546 int viaOuterDefault =
ReadInt4At( data, startPos );
3547 int viaDrillDefault =
ReadInt4At( data, startPos + 4 );
3548 uint8_t marker = data[startPos + 8];
3549 bool zeroBlock = std::all_of( data + startPos + 9, data + startPos + 16,
3550 []( uint8_t b ) {
return b == 0; } );
3551 int separator =
ReadInt3At( data, startPos + 16 );
3552 int padRefCount =
ReadInt3At( data, startPos + 19 );
3554 if( marker <= 1 && zeroBlock && separator == 0 && padRefCount >= 0
3555 && padRefCount <= maxReasonablePadRefs )
3557 size_t refsStart = startPos + 22;
3558 size_t refsEnd = refsStart +
static_cast<size_t>( padRefCount ) * 6;
3560 if( refsEnd <= scanEnd )
3562 std::vector<DT_PAD_REF> parsedRefs;
3563 parsedRefs.reserve(
static_cast<size_t>( padRefCount ) );
3564 int rangeHitsBase0 = 0;
3565 int rangeHitsBase1 = 0;
3567 for(
int i = 0; i < padRefCount; i++ )
3569 size_t refPos = refsStart +
static_cast<size_t>( i ) * 6;
3571 int padIndex =
ReadInt3At( data, refPos + 3 );
3573 if( compIndex >= 0 && padIndex > 0 )
3575 parsedRefs.push_back( { compIndex, padIndex } );
3577 if( compIndex >= 0 && compIndex < componentCount )
3580 if( compIndex >= 1 && compIndex <= componentCount )
3585 int requiredRangeHits = std::min( 4,
static_cast<int>( parsedRefs.size() ) );
3586 bool plausibleRefs = parsedRefs.empty()
3587 || std::max( rangeHitsBase0, rangeHitsBase1 ) >= requiredRangeHits;
3591 if( viaOuterDefault > 0 && viaOuterDefault <= MAX_REASONABLE_VIA_DIM )
3594 if( viaDrillDefault > 0 && viaDrillDefault <= MAX_REASONABLE_VIA_DIM )
3597 aNet.
padRefs = std::move( parsedRefs );
3598 chainScanStart = refsEnd;
3604 size_t pos = chainScanStart;
3610 if( chainPos == std::string::npos )
3615 if( headerStart + 6 > fileSize )
3618 const uint8_t* h = data + headerStart;
3619 int chainIdx = ( (
static_cast<int>( h[0] ) << 16 )
3620 | (
static_cast<int>( h[1] ) << 8 )
3621 |
static_cast<int>( h[2] ) ) -
INT3_BIAS;
3622 int nodeCount = ( (
static_cast<int>( h[3] ) << 16 )
3623 | (
static_cast<int>( h[4] ) << 8 )
3624 |
static_cast<int>( h[5] ) ) -
INT3_BIAS;
3626 auto firstNodeLooksPlausible = [&]() ->
bool
3628 size_t firstNode = headerStart + 6;
3633 const uint8_t* n = data + firstNode;
3635 int x =
static_cast<int>(
3636 (
static_cast<unsigned int>( n[0] ) << 24 )
3637 | (
static_cast<unsigned int>( n[1] ) << 16 )
3638 | (
static_cast<unsigned int>( n[2] ) << 8 )
3639 |
static_cast<unsigned int>( n[3] ) ) -
INT4_BIAS;
3641 int y =
static_cast<int>(
3642 (
static_cast<unsigned int>( n[4] ) << 24 )
3643 | (
static_cast<unsigned int>( n[5] ) << 16 )
3644 | (
static_cast<unsigned int>( n[6] ) << 8 )
3645 |
static_cast<unsigned int>( n[7] ) ) -
INT4_BIAS;
3647 int layer = ( (
static_cast<int>( n[8] ) << 16 )
3648 | (
static_cast<int>( n[9] ) << 8 )
3649 |
static_cast<int>( n[10] ) ) -
INT3_BIAS;
3651 int width =
static_cast<int>(
3652 (
static_cast<unsigned int>( n[14] ) << 24 )
3653 | (
static_cast<unsigned int>( n[15] ) << 16 )
3654 | (
static_cast<unsigned int>( n[16] ) << 8 )
3655 |
static_cast<unsigned int>( n[17] ) ) -
INT4_BIAS;
3657 int viaStyleIdx = ( (
static_cast<int>( n[27] ) << 16 )
3658 | (
static_cast<int>( n[28] ) << 8 )
3659 |
static_cast<int>( n[29] ) ) -
INT3_BIAS;
3661 int routeMode = ( (
static_cast<int>( n[37] ) << 16 )
3662 | (
static_cast<int>( n[38] ) << 8 )
3663 |
static_cast<int>( n[39] ) ) -
INT3_BIAS;
3665 return x > -100000000 && x < 100000000
3666 && y > -100000000 && y < 100000000
3667 && layer >= 0 && layer <= 50
3668 && width > 0 && width <= 5000000
3669 && viaStyleIdx >= -1 && viaStyleIdx <= 10000
3670 && routeMode >= 0 && routeMode <= 10
3674 if( chainIdx < 0 || nodeCount < 1 || nodeCount > 10000 )
3676 if( chainIdx >= 0 && firstNodeLooksPlausible() )
3678 THROW_IO_ERRORF(
_(
"DipTrace import: invalid route-chain node count %d for net '%s' at offset "
3689 size_t nodesStart = headerStart + 6;
3690 size_t nodesEnd = nodesStart +
static_cast<size_t>( nodeCount ) *
TRACK_NODE_SIZE;
3692 if( nodesEnd > scanEnd )
3694 if( firstNodeLooksPlausible() )
3696 THROW_IO_ERRORF(
_(
"DipTrace import: route-chain node count %d for net '%s' overruns record at "
3697 "offset 0x%06zX." ),
3709 chain.nodes.reserve( nodeCount );
3713 for(
int i = 0; i < nodeCount; i++ )
3715 const uint8_t* n = data + nodesStart +
static_cast<size_t>( i ) *
TRACK_NODE_SIZE;
3720 node.
x =
static_cast<int>(
3721 (
static_cast<unsigned int>( n[0] ) << 24 )
3722 | (
static_cast<unsigned int>( n[1] ) << 16 )
3723 | (
static_cast<unsigned int>( n[2] ) << 8 )
3724 |
static_cast<unsigned int>( n[3] ) ) -
INT4_BIAS;
3727 node.y =
static_cast<int>(
3728 (
static_cast<unsigned int>( n[4] ) << 24 )
3729 | (
static_cast<unsigned int>( n[5] ) << 16 )
3730 | (
static_cast<unsigned int>( n[6] ) << 8 )
3731 |
static_cast<unsigned int>( n[7] ) ) -
INT4_BIAS;
3734 node.layer = ( (
static_cast<int>( n[8] ) << 16 )
3735 | (
static_cast<int>( n[9] ) << 8 )
3736 |
static_cast<int>( n[10] ) ) -
INT3_BIAS;
3739 node.width =
static_cast<int>(
3740 (
static_cast<unsigned int>( n[14] ) << 24 )
3741 | (
static_cast<unsigned int>( n[15] ) << 16 )
3742 | (
static_cast<unsigned int>( n[16] ) << 8 )
3743 |
static_cast<unsigned int>( n[17] ) ) -
INT4_BIAS;
3746 node.viaStyleIdx = ( (
static_cast<int>( n[27] ) << 16 )
3747 | (
static_cast<int>( n[28] ) << 8 )
3748 |
static_cast<int>( n[29] ) ) -
INT3_BIAS;
3749 node.viaOuterDiam =
static_cast<int>(
3750 (
static_cast<unsigned int>( n[18] ) << 24 )
3751 | (
static_cast<unsigned int>( n[19] ) << 16 )
3752 | (
static_cast<unsigned int>( n[20] ) << 8 )
3753 |
static_cast<unsigned int>( n[21] ) ) -
INT4_BIAS;
3754 node.routeFlag = n[22];
3755 node.viaDrillDiam =
static_cast<int>(
3756 (
static_cast<unsigned int>( n[30] ) << 24 )
3757 | (
static_cast<unsigned int>( n[31] ) << 16 )
3758 | (
static_cast<unsigned int>( n[32] ) << 8 )
3759 |
static_cast<unsigned int>( n[33] ) ) -
INT4_BIAS;
3762 node.routeMode = ( (
static_cast<int>( n[37] ) << 16 )
3763 | (
static_cast<int>( n[38] ) << 8 )
3764 |
static_cast<int>( n[39] ) ) -
INT3_BIAS;
3768 node.hasVia = ( node.viaStyleIdx >= 0 );
3770 if( node.width <= 0 || node.width > 5000000 )
3776 if( node.layer < 0 || node.layer > 50 )
3782 chain.nodes.push_back( node );
3785 if( valid && !
chain.nodes.empty() )
3799 size_t totalRefs = 0;
3802 totalRefs += net.padRefs.size();
3804 if( totalRefs == 0 )
3807 size_t maxReasonableRefs = std::max<size_t>( 5000,
m_components.size() * 64 );
3809 if( totalRefs > maxReasonableRefs )
3811 wxLogTrace(
traceDiptraceIo, wxT(
"DipTrace: skipping routing-ref pad inference (%zu refs exceeds %zu cap)" ),
3812 totalRefs, maxReasonableRefs );
3816 std::vector<std::unordered_map<int, size_t>> padPosByIndex(
m_components.size() );
3820 const std::vector<DT_PAD>& pads =
m_components[c].pads;
3821 auto& padMap = padPosByIndex[c];
3823 padMap.reserve( pads.size() );
3825 for(
size_t p = 0; p < pads.size(); p++ )
3826 padMap.emplace( pads[p].index, p );
3837 auto scoreBase = [&](
int aBase ) -> SCORE
3850 if( compPos < 0 || compPos >=
static_cast<int>(
m_components.size() ) )
3853 const auto& padMap = padPosByIndex[compPos];
3854 auto it = padMap.find( ref.
padIndex );
3856 if( it == padMap.end() )
3862 if(
pad.netIndex < 0 )
3870 SCORE scoreBase0 = scoreBase( 0 );
3871 SCORE scoreBase1 = scoreBase( 1 );
3873 if( scoreBase0.hits == 0 && scoreBase1.hits == 0 )
3876 if( scoreBase0.hits == scoreBase1.hits && scoreBase0.fillable == scoreBase1.fillable )
3879 SCORE best = scoreBase0;
3881 if( scoreBase1.hits > scoreBase0.hits
3882 || ( scoreBase1.hits == scoreBase0.hits && scoreBase1.fillable > scoreBase0.fillable ) )
3899 if( compPos < 0 || compPos >=
static_cast<int>(
m_components.size() ) )
3902 auto& padMap = padPosByIndex[compPos];
3903 auto it = padMap.find( ref.
padIndex );
3905 if( it == padMap.end() )
3910 if(
pad.netIndex < 0 )
3912 pad.netIndex = net.index;
3915 else if(
pad.netIndex != net.index )
3922 if( assigned > 0 || conflicts > 0 )
3925 wxT(
"DipTrace: routing-ref pad net inference (base=%d): %d refs, %d hits, "
3926 "%d assigned, %d conflicts" ),
3927 best.base, best.refs, best.hits, assigned, conflicts );
3940 const uint8_t* data =
m_reader.GetData();
3941 size_t fileSize =
m_reader.GetFileSize();
3943 if( aSearchEnd > fileSize )
3944 aSearchEnd = fileSize;
3946 static constexpr int MAX_ZONES = 100;
3947 static constexpr int MAX_VERTICES = 50000;
3948 static constexpr int MAX_REASONABLE_DIM = 5000000;
3964 oxMin = std::min( oxMin, v.x );
3965 oxMax = std::max( oxMax, v.x );
3966 oyMin = std::min( oyMin, v.y );
3967 oyMax = std::max( oyMax, v.y );
3970 bboxXMin = oxMin - 5000000;
3971 bboxXMax = oxMax + 5000000;
3972 bboxYMin = oyMin - 5000000;
3973 bboxYMax = oyMax + 5000000;
3976 auto headerLooksPlausible = [&](
size_t aPos,
bool aCheckVertexSamples ) ->
bool
3978 if( aPos + 30 > aSearchEnd )
3982 int flags1 = data[aPos + 3];
3983 int flags3 = data[aPos + 5];
3986 int minimumArea =
ReadInt4At( data, aPos + 14 );
3987 int separator =
ReadInt3At( data, aPos + 18 );
3990 int vtxCount =
ReadInt3At( data, aPos + 27 );
3992 if( fieldA < 0 || fieldA > 10000 )
3995 if( fieldB < -1 || fieldB > 10000 )
3998 if( flags1 > 2 || flags3 > 2 )
4004 if( minWidth <= 0 || minWidth > MAX_REASONABLE_DIM )
4007 if( minimumArea < 0 || minimumArea > MAX_REASONABLE_DIM )
4013 if( layer < -10 || layer > 100 )
4016 if( vtxCount < 3 || vtxCount > MAX_VERTICES )
4019 size_t vtxStart = aPos + 30;
4020 size_t vtxEnd = vtxStart +
static_cast<size_t>( vtxCount ) * 8;
4022 if( vtxEnd > aSearchEnd )
4025 if( !aCheckVertexSamples )
4028 int sampleCount = std::min( 3, vtxCount );
4030 auto vertexInBounds = [&](
size_t aVertexPos ) ->
bool
4034 return x >= bboxXMin && x <= bboxXMax && y >= bboxYMin && y <= bboxYMax;
4037 for(
int i = 0; i < sampleCount; i++ )
4039 size_t vp = vtxStart +
static_cast<size_t>( i ) * 8;
4041 if( !vertexInBounds( vp ) )
4045 size_t lastVp = vtxStart +
static_cast<size_t>( vtxCount - 1 ) * 8;
4047 return vertexInBounds( lastVp );
4050 auto headerHasZoneSectionShape = [&](
size_t aPos ) ->
bool
4052 if( aPos + 30 > aSearchEnd )
4056 int flags1 = data[aPos + 3];
4057 int flags3 = data[aPos + 5];
4058 int separator =
ReadInt3At( data, aPos + 18 );
4061 int vtxCount =
ReadInt3At( data, aPos + 27 );
4063 return fieldA >= 0 && fieldA <= 10000
4064 && fieldB >= -1 && fieldB <= 10000
4065 && flags1 <= 2 && flags3 <= 2
4067 && layer >= -10 && layer <= 100
4068 && vtxCount >= 3 && vtxCount <= MAX_VERTICES
4069 && aPos + 30 +
static_cast<size_t>( vtxCount ) * 8 <= aSearchEnd;
4072 auto parseZoneTrailer = [&](
DT_ZONE& aZone,
size_t aSearchStartPos,
size_t aSearchEndPos,
4073 int aZoneIndex ) ->
void
4081 static constexpr size_t TRAILER_LEN = 28;
4082 static constexpr size_t STYLE_BLOCK_LEN = 14;
4083 static constexpr int CACHED_RECORD_LEN = 23;
4085 if( aSearchEndPos <= aSearchStartPos || aSearchEndPos - aSearchStartPos < TRAILER_LEN )
4088 size_t lastStart = aSearchEndPos - TRAILER_LEN;
4090 for(
size_t trailerPos = lastStart + 1; trailerPos-- > aSearchStartPos; )
4093 int zeroInt4 =
ReadInt4At( data, trailerPos + 3 );
4094 int boardClr =
ReadInt4At( data, trailerPos + 7 );
4095 uint8_t islandR = data[trailerPos + 11];
4096 uint8_t islandI = data[trailerPos + 12];
4097 uint8_t islandC = data[trailerPos + 13];
4098 int zoneId =
ReadInt3At( data, trailerPos + 14 );
4099 uint8_t viaDir = data[trailerPos + 17];
4100 uint8_t smdSep = data[trailerPos + 18];
4101 int smdSpokeMode =
ReadInt3At( data, trailerPos + 19 );
4102 int smdSpokeW =
ReadInt4At( data, trailerPos + 22 );
4103 uint8_t ratMode = data[trailerPos + 26];
4104 uint8_t doneFlag = data[trailerPos + 27];
4106 if( lead < 0 || lead > 100000 || zeroInt4 != -
INT4_BIAS )
4109 if( boardClr < 0 || boardClr > MAX_REASONABLE_DIM )
4112 if( islandR > 1 || islandI > 1 || islandC > 1 )
4115 if( zoneId < 0 || zoneId > 100000 )
4118 if( viaDir > 1 || smdSep > 1 )
4121 if( smdSpokeMode < 0 || smdSpokeMode > 4 )
4124 if( smdSpokeW <= 0 || smdSpokeW > MAX_REASONABLE_DIM )
4133 size_t payloadStart = aSearchStartPos;
4135 if( aSearchStartPos + STYLE_BLOCK_LEN <= aSearchEndPos )
4137 int styleLead =
ReadInt3At( data, aSearchStartPos );
4138 int styleSpokeMode =
ReadInt3At( data, aSearchStartPos + 3 );
4139 int styleLineSpacing =
ReadInt4At( data, aSearchStartPos + 6 );
4140 int styleSpokeWidth =
ReadInt4At( data, aSearchStartPos + 10 );
4143 && styleSpokeMode >= 0 && styleSpokeMode <= 4
4144 && styleLineSpacing > 0 && styleLineSpacing <= MAX_REASONABLE_DIM
4145 && styleSpokeWidth > 0 && styleSpokeWidth <= MAX_REASONABLE_DIM )
4147 payloadStart += STYLE_BLOCK_LEN;
4151 int cachedBytes = 0;
4152 int cachedRecords = 0;
4155 if( trailerPos > payloadStart )
4157 cachedBytes =
static_cast<int>( trailerPos - payloadStart );
4159 if( cachedBytes % CACHED_RECORD_LEN == 0 )
4161 cachedRecords = cachedBytes / CACHED_RECORD_LEN;
4164 for(
int recIdx = 0; recIdx < cachedRecords; recIdx++ )
4166 size_t recPos = payloadStart +
static_cast<size_t>( recIdx ) * CACHED_RECORD_LEN;
4168 if( recPos + CACHED_RECORD_LEN > trailerPos )
4203 wxT(
"DipTrace: zone[%d] trailer off=0x%06zX regionsCounted=%d "
4204 "cachedBytes=%d cachedRecords=%d boardClr=%d "
4205 "islands=[%u,%u,%u] id=%d viaDirect=%u smdSeparate=%u "
4206 "smdSpokeMode=%d smdSpokeWidth=%d ratMode=%u done=%u" ),
4207 aZoneIndex, trailerPos, lead, cachedBytes, cachedRecords, boardClr,
4208 static_cast<unsigned int>( islandR ),
static_cast<unsigned int>( islandI ),
4209 static_cast<unsigned int>( islandC ), zoneId,
static_cast<unsigned int>( viaDir ),
4210 static_cast<unsigned int>( smdSep ), smdSpokeMode, smdSpokeW,
4211 static_cast<unsigned int>( ratMode ),
static_cast<unsigned int>( doneFlag ) );
4215 int zoneXMin = aZone.
outline[0].first;
4216 int zoneXMax = aZone.
outline[0].first;
4217 int zoneYMin = aZone.
outline[0].second;
4218 int zoneYMax = aZone.
outline[0].second;
4220 for(
const auto& p : aZone.
outline )
4222 zoneXMin = std::min( zoneXMin, p.first );
4223 zoneXMax = std::max( zoneXMax, p.first );
4224 zoneYMin = std::min( zoneYMin, p.second );
4225 zoneYMax = std::max( zoneYMax, p.second );
4229 std::array<int, 6> minVals = {
4233 std::array<int, 6> maxVals = minVals;
4234 std::array<int, 6> inXHits = { 0, 0, 0, 0, 0, 0 };
4235 std::array<int, 6> inYHits = { 0, 0, 0, 0, 0, 0 };
4236 std::array<int, 6> nonNegHits = { 0, 0, 0, 0, 0, 0 };
4237 std::map<int, int> field0Hist;
4238 std::map<int, int> field5Hist;
4239 int f0EqRegions = 0;
4240 int xEqualCount = 0;
4241 int yEqualCount = 0;
4242 int bothEqualCount = 0;
4246 std::array<int, 6> vals = {
4251 field0Hist[rec.
field0]++;
4252 field5Hist[rec.
field5]++;
4269 for(
size_t fi = 0; fi < vals.size(); fi++ )
4271 minVals[fi] = std::min( minVals[fi], vals[fi] );
4272 maxVals[fi] = std::max( maxVals[fi], vals[fi] );
4274 if( vals[fi] >= zoneXMin && vals[fi] <= zoneXMax )
4277 if( vals[fi] >= zoneYMin && vals[fi] <= zoneYMax )
4286 wxT(
"DipTrace: zone[%d] cached-range "
4287 "f0=[%d,%d] f1=[%d,%d] f2=[%d,%d] f3=[%d,%d] f4=[%d,%d] f5=[%d,%d]" ),
4288 aZoneIndex, minVals[0], maxVals[0], minVals[1], maxVals[1], minVals[2], maxVals[2],
4289 minVals[3], maxVals[3], minVals[4], maxVals[4], minVals[5], maxVals[5] );
4292 wxT(
"DipTrace: zone[%d] cached-hits "
4293 "xHits=[%d,%d,%d,%d,%d,%d] yHits=[%d,%d,%d,%d,%d,%d] "
4294 "nonNeg=[%d,%d,%d,%d,%d,%d]" ),
4295 aZoneIndex, inXHits[0], inXHits[1], inXHits[2], inXHits[3], inXHits[4], inXHits[5],
4296 inYHits[0], inYHits[1], inYHits[2], inYHits[3], inYHits[4], inYHits[5], nonNegHits[0],
4297 nonNegHits[1], nonNegHits[2], nonNegHits[3], nonNegHits[4], nonNegHits[5] );
4299 wxLogTrace(
traceDiptraceIo, wxT(
"DipTrace: zone[%d] cached-zone-bbox=[%d,%d,%d,%d]" ), aZoneIndex,
4300 zoneXMin, zoneXMax, zoneYMin, zoneYMax );
4302 auto histToString = [](
const std::map<int, int>& aHist ) -> wxString
4307 for(
const auto&
kv : aHist )
4313 out += wxString::Format( wxT(
"%d:%d" ),
kv.first,
kv.second );
4320 wxT(
"DipTrace: zone[%d] cached-hist f0={%s} f5={%s} "
4321 "f0EqRegions=%d xEq=%d yEq=%d bothEq=%d" ),
4322 aZoneIndex, histToString( field0Hist ), histToString( field5Hist ), f0EqRegions,
4323 xEqualCount, yEqualCount, bothEqualCount );
4327 for(
size_t ri = 0; ri < sampleCount; ri++ )
4330 wxLogTrace(
traceDiptraceIo, wxT(
"DipTrace: zone[%d] cached-rec[%zu]={%d,%d,%d,%d,%d,%d}" ),
4342 bool zoneViaPreamble =
false;
4344 auto findZoneFontPreambleDataStart = [&](
size_t aStart ) ->
size_t
4346 static const wxString fontNames[] = {
4347 wxT(
"Arial" ), wxT(
"Tahoma" ), wxT(
"Times New Roman" ),
4348 wxT(
"Courier New" ), wxT(
"Verdana" ), wxT(
"Calibri" )
4353 for(
const wxString& fontName : fontNames )
4355 size_t fontPos =
m_reader.FindString( fontName, aStart, aSearchEnd );
4365 if( bc < 0 || bc > 500 )
4367 fontPos =
m_reader.FindString( fontName, fontPos + 3, aSearchEnd );
4371 strEnd = fontPos + 3 + bc;
4375 uint16_t cc = (
static_cast<uint16_t
>( data[fontPos] ) << 8 )
4376 | data[fontPos + 1];
4377 strEnd = fontPos + 2 +
static_cast<size_t>( cc ) * 2;
4380 if( strEnd + 16 > aSearchEnd )
4384 int bold = data[strEnd + 3];
4389 if( fontSize >= 5 && fontSize <= 30 && bold <= 1
4390 && fontH > 0 && fontH < 10000000
4391 && fontW > 0 && fontW < 10000000
4394 bestDataStart = std::min( bestDataStart, strEnd + 16 );
4398 fontPos =
m_reader.FindString( fontName, strEnd, aSearchEnd );
4402 return bestDataStart;
4405 size_t zoneDataStart = findZoneFontPreambleDataStart( aSearchStart );
4409 size_t preambleHeaderStart = zoneDataStart + 3;
4411 if( preambleHeaderStart + 30 <= aSearchEnd
4412 && headerLooksPlausible( preambleHeaderStart,
true ) )
4414 zoneHeaderStart = preambleHeaderStart;
4415 zoneViaPreamble =
true;
4419 if( preambleHeaderStart + 30 <= aSearchEnd
4420 && headerHasZoneSectionShape( preambleHeaderStart ) )
4422 THROW_IO_ERRORF(
_(
"DipTrace import: invalid copper-pour zone header after font preamble "
4423 "at offset 0x%06zX." ), preambleHeaderStart );
4426 zoneDataStart = findZoneFontPreambleDataStart( zoneDataStart + 1 );
4430 for(
size_t scanPos = aSearchStart; zoneHeaderStart ==
NOT_FOUND
4431 && scanPos + 30 <= aSearchEnd; scanPos++ )
4433 if( headerLooksPlausible( scanPos,
true ) )
4435 zoneHeaderStart = scanPos;
4436 wxLogTrace(
traceDiptraceIo, wxT(
"DipTrace: zone section found by structural scan at 0x%06zX" ),
4449 zoneDataStart = findZoneFontPreambleDataStart( aSearchStart );
4453 size_t fallbackStart = zoneDataStart + 3;
4455 if( fallbackStart + 30 <= aSearchEnd
4456 && headerLooksPlausible( fallbackStart,
true ) )
4458 zoneHeaderStart = fallbackStart;
4459 zoneViaPreamble =
true;
4463 size_t fallbackEnd = std::min( aSearchEnd, zoneDataStart + 256 );
4465 for(
size_t scanPos = zoneDataStart; scanPos + 30 <= fallbackEnd; scanPos++ )
4467 if( headerLooksPlausible( scanPos,
true ) )
4469 zoneHeaderStart = scanPos;
4477 wxLogTrace(
traceDiptraceIo, wxT(
"DipTrace: zone section found by font fallback at 0x%06zX" ),
4486 size_t pos = zoneHeaderStart;
4488 for(
int zi = 0; zi < MAX_ZONES && pos + 30 < aSearchEnd; zi++ )
4503 int flags1 = data[pos + 3];
4504 int flags2 = data[pos + 4];
4505 int flags3 = data[pos + 5];
4508 int minimumArea =
ReadInt4At( data, pos + 14 );
4509 int separator =
ReadInt3At( data, pos + 18 );
4514 if( fieldA < 0 || fieldA > 10000 )
4517 if( fieldB < -1 || fieldB > 10000 )
4520 if( flags1 > 2 || flags3 > 2 )
4526 if( minWidth <= 0 || minWidth > MAX_REASONABLE_DIM )
4529 if( minimumArea < 0 || minimumArea > MAX_REASONABLE_DIM )
4535 if( layer < -10 || layer > 100 )
4538 if( vtxCount < 3 || vtxCount > MAX_VERTICES )
4541 size_t vtxStart = pos + 30;
4542 size_t vtxEnd = vtxStart +
static_cast<size_t>( vtxCount ) * 8;
4544 if( vtxEnd > aSearchEnd )
4550 zone.
fillMode =
static_cast<uint8_t
>( flags1 );
4551 zone.
rawFlag2 =
static_cast<uint8_t
>( flags2 );
4557 zone.
outline.reserve( vtxCount );
4559 bool validOutline =
true;
4561 for(
int vi = 0; vi < vtxCount; vi++ )
4563 size_t vp = vtxStart +
static_cast<size_t>( vi ) * 8;
4567 if( x < bboxXMin || x > bboxXMax || y < bboxYMin || y > bboxYMax )
4569 validOutline =
false;
4573 zone.
outline.emplace_back( x, y );
4579 wxString zoneNetName;
4583 if( net.index == fieldB )
4585 zoneNetName = net.name;
4591 minimumArea, separator, layer, fieldB, vtxCount, zoneNetName );
4593 m_zones.push_back( std::move( zone ) );
4599 if( pos + 3 > aSearchEnd )
4604 if( fillSegCount < 0 || fillSegCount > 500000 )
4609 wxLogTrace(
traceDiptraceIo, wxT(
"DipTrace: zone[%d] fill-segments off=0x%06zX count=%d" ), zi, pos,
4613 pos += 3 +
static_cast<size_t>( fillSegCount ) * 19;
4616 if( pos + 3 > aSearchEnd )
4621 if( fillPolyCount < 0 || fillPolyCount > 50000 )
4626 wxLogTrace(
traceDiptraceIo, wxT(
"DipTrace: zone[%d] fill-polys off=0x%06zX count=%d" ), zi, pos,
4632 for(
int pi = 0; pi < fillPolyCount; pi++ )
4634 if( pos + 3 > aSearchEnd )
4642 if( pvc < 0 || pvc > MAX_VERTICES )
4648 pos += 3 +
static_cast<size_t>( pvc ) * 8 + 3;
4650 if( pos > aSearchEnd )
4662 if( pos + 14 <= aSearchEnd )
4666 int lineSpacing =
ReadInt4At( data, pos + 6 );
4667 int spokeWidth =
ReadInt4At( data, pos + 10 );
4670 && spokeMode >= 0 && spokeMode <= 4
4671 && lineSpacing > 0 && lineSpacing <= MAX_REASONABLE_DIM
4672 && spokeWidth > 0 && spokeWidth <= MAX_REASONABLE_DIM )
4681 wxT(
"DipTrace: zone[%d] style lead=%d spokeMode=%d lineSpacing=%d spokeWidth=%d" ), zi,
4682 styleLead, spokeMode, lineSpacing, spokeWidth );
4690 bool foundNext =
false;
4691 size_t scanStart = pos;
4693 for(
size_t testPos = pos; testPos + 30 <= aSearchEnd; testPos++ )
4695 if( !headerLooksPlausible( testPos,
false ) )
4698 if( !headerLooksPlausible( testPos,
true ) )
4706 if( foundNext && pos > scanStart )
4708 parseZoneTrailer( parsedZone, scanStart, pos, zi );
4714 parseZoneTrailer( parsedZone, scanStart, aSearchEnd, zi );
4728 if( !
m_zones.empty() && !zoneViaPreamble )
4742 int copperCount =
static_cast<int>(
m_layers.size() );
4744 if( copperCount < 2 )
4748 if( copperCount % 2 != 0 )
4751 m_board->SetCopperLayerCount( copperCount );
4768 enabledLayers.
set( kiLayer );
4770 if( !layer.name.empty() )
4771 m_board->SetLayerName( kiLayer, layer.name );
4775 m_board->SetEnabledLayers( enabledLayers );
4817 std::vector<VECTOR2I> pts;
4818 std::vector<uint8_t> arcs;
4825 arcs.push_back( v.arc );
4832 while( i < n && arcs[i] == 1 )
4838 size_t startIndex = i;
4840 size_t maxSteps = n * 4;
4850 std::vector<OUTLINE_PRIM> outlinePrims;
4851 outlinePrims.reserve( n );
4857 prim.start = aStart;
4859 outlinePrims.push_back( prim );
4866 prim.start = aStart;
4869 outlinePrims.push_back( prim );
4872 while( i < n && steps++ < maxSteps )
4874 size_t next = ( i + 1 ) % n;
4876 if( arcs[
next] == 1 )
4878 size_t afterArc = (
next + 1 ) % n;
4885 long long cross =
static_cast<long long>(
v1.x ) *
static_cast<long long>(
v2.y )
4886 -
static_cast<long long>(
v1.y ) *
static_cast<long long>(
v2.x );
4887 bool degenerateArc = ( start == mid ) || ( mid ==
end ) || ( start ==
end )
4888 || ( std::llabs( cross ) < 100 );
4896 addArc( start, mid,
end );
4909 if( i == startIndex )
4913 if( steps >= maxSteps )
4915 wxLogTrace(
traceDiptraceIo, wxT(
"DipTrace: outline traversal aborted after %zu steps (%zu vertices)" ),
4919 outlinePrims.clear();
4921 for(
size_t v = 0; v < n; v++ )
4925 for(
const OUTLINE_PRIM& prim : outlinePrims )
4959 for(
int i = 0; i < 4; i++ )
4965 seg->
SetEnd( corners[( i + 1 ) % 4] );
4983 if(
chain.netIndex < 0 )
4987 anchors.reserve( anchors.size() +
chain.nodes.size() );
5008 for(
size_t padIdx = 0; padIdx < aComp.
pads.size(); padIdx++ )
5014 pad->SetPosition( padLocal );
5029 std::vector<VECTOR2I> polyPts;
5037 else if( dtPad.
style == 2 )
5057 if( drillW > 0 && drillH <= 0 )
5060 if( drillH > 0 && drillW <= 0 )
5073 if( drillW > 0 && drillH > 0 )
5077 pad->SetDrillSize( drill );
5079 if( drillW == drillH )
5094 orientClass = aComp.
pads[padIdx - 1].orientClass;
5096 if( orientClass == 1 )
5110 PAD* holePad =
new PAD( footprint );
5133 bool hasAssemblyOutline =
false;
5139 hasAssemblyOutline =
true;
5158 std::swap( scaleX, scaleY );
5164 int minSX = INT_MAX, maxSX = INT_MIN;
5165 int minSY = INT_MAX, maxSY = INT_MIN;
5169 minSX = std::min( { minSX, s.
x1, s.
x2 } );
5170 maxSX = std::max( { maxSX, s.
x1, s.
x2 } );
5171 minSY = std::min( { minSY, s.
y1, s.
y2 } );
5172 maxSY = std::max( { maxSY, s.
y1, s.
y2 } );
5175 int shapeXRange = maxSX - minSX;
5176 int shapeYRange = maxSY - minSY;
5178 if( shapeXRange > 0 && shapeYRange > 0 )
5180 double physX =
static_cast<double>( shapeXRange ) *
std::abs( scaleX )
5182 double physY =
static_cast<double>( shapeYRange ) *
std::abs( scaleY )
5184 double aspect = physX / physY;
5186 if( aspect < 0.2 || aspect > 5.0 )
5187 std::swap( scaleX, scaleY );
5192 static constexpr int DEFAULT_LINE_WIDTH_DT = 3000;
5194 auto scaleShapeCoord = [&](
int aShapeVal,
int aBboxDim ) ->
int
5197 static_cast<int>(
static_cast<int64_t
>( aShapeVal ) * aBboxDim
5226 switch( dtShape.
layer )
5235 default: shapeLayer =
F_SilkS;
break;
5240 VECTOR2I p1( scaleShapeCoord( dtShape.
x1, scaleX ),
5241 scaleShapeCoord( dtShape.
y1, scaleY ) );
5242 VECTOR2I p2( scaleShapeCoord( dtShape.
x2, scaleX ),
5243 scaleShapeCoord( dtShape.
y2, scaleY ) );
5256 for(
int i = 0; i < 4; i++ )
5263 edge->
SetEnd( corners[( i + 1 ) % 4] );
5279 int radius = ( p2 - p1 ).EuclideanNorm() / 2;
5286 scaleShapeCoord( dtShape.
midY, scaleY ) );
5291 int64_t cross =
static_cast<int64_t
>( p2.
x - p1.
x ) * ( mid.
y - p1.
y )
5292 -
static_cast<int64_t
>( p2.
y - p1.
y ) * ( mid.
x - p1.
x );
5293 int64_t chordSq =
static_cast<int64_t
>( p2.
x - p1.
x ) * ( p2.
x - p1.
x )
5294 +
static_cast<int64_t
>( p2.
y - p1.
y ) * ( p2.
y - p1.
y );
5296 if( chordSq == 0 ||
std::abs( cross ) * 1000 < chordSq )
5334 double orientationDeg;
5342 if( orientationDeg < 0.0 )
5343 orientationDeg += 360.0;
5348 double nearest90 = std::round( orientationDeg / 90.0 ) * 90.0;
5350 if(
std::abs( orientationDeg - nearest90 ) < 0.02 )
5351 orientationDeg = std::fmod( nearest90, 360.0 );
5357 :
static_cast<int>( std::lround(
5359 orientationDeg = ( ( orientationQuarterTurns % 4 ) + 4 ) % 4 * 90.0;
5364 wxLogTrace(
traceDiptraceIo, wxT(
"DipTrace: fp-orient ref=%s pat=%s qturn=%d hasQ=%d exact=%d chosen=%.2f" ),
5370 if( aComp.
layer == 1 )
5397 if( aText.
text.empty() )
5407 text->SetLayer( textLayer );
5411 int cx =
ToKiCadCoord(
static_cast<int>( (
static_cast<int64_t
>( aText.
x1 ) + aText.
x2 ) / 2 ) );
5412 int cy =
ToKiCadCoord(
static_cast<int>( (
static_cast<int64_t
>( aText.
y1 ) + aText.
y2 ) / 2 ) );
5416 ToKiCadCoord(
static_cast<int>(
static_cast<int64_t
>( aText.
y2 ) - aText.
y1 ) ) );
5434 if( aDipTraceNetIndex < 0 )
5448 if( aDipTraceNetIndex < 0 )
5467 wxString netName = net.name;
5469 if( netName.empty() )
5470 netName = wxString::Format( wxS(
"DipTrace_Net_%d" ), net.index );
5480 if( net.index >= 0 )
5484 if( !inserted && it->second != netinfo )
5486 wxLogTrace(
traceDiptraceIo, wxT(
"DipTrace: net index %d maps to multiple net names (%s, %s)" ),
5487 net.index, it->second->GetNetname(), netinfo->
GetNetname() );
5492 if( !dtInserted && dtIt->second != &net )
5494 wxLogTrace(
traceDiptraceIo, wxT(
"DipTrace: duplicate net metadata for net index %d" ),
5506 int missingGeometry = 0;
5507 std::set<std::tuple<int, int, int>> createdKeys;
5509 auto hasBoardViaAt = [&](
const VECTOR2I& aPos,
int aNetCode ) ->
bool
5518 if(
via->GetPosition() == aPos &&
via->GetNetCode() == aNetCode )
5527 if( !
comp.isStandaloneVia )
5530 int viaX =
comp.positionX;
5531 int viaY =
comp.positionY;
5532 int viaOuter = std::max(
comp.bboxWidth,
comp.padWidthHint );
5533 int viaDrill = std::max(
comp.drillWidthHint,
comp.drillHeightHint );
5536 if( !
comp.pads.empty() )
5541 netIndex =
pad.netIndex;
5544 viaOuter =
pad.width;
5546 if(
pad.drillWidth > 0 )
5547 viaDrill =
pad.drillWidth;
5548 else if(
pad.drillHeight > 0 )
5549 viaDrill =
pad.drillHeight;
5552 if( viaOuter <= 0 || viaDrill <= 0 )
5561 std::tuple<int, int, int> key( viaPos.
x, viaPos.
y, netCode );
5563 if( createdKeys.find( key ) != createdKeys.end() || hasBoardViaAt( viaPos, netCode ) )
5570 via->SetPosition( viaPos );
5580 createdKeys.insert( key );
5584 if( created > 0 || duplicates > 0 || missingGeometry > 0 )
5587 wxT(
"DipTrace: created %d standalone vias from component records "
5588 "(duplicates=%d, missingGeometry=%d)" ),
5589 created, duplicates, missingGeometry );
5598 int missingChainNets = 0;
5599 int nonCopperTrackSkips = 0;
5600 int nonCopperViaSkips = 0;
5601 int inferredLayerChangeVias = 0;
5602 int explicitViaStyleVias = 0;
5603 int duplicateViaSkips = 0;
5604 int crossNetViaSkips = 0;
5612 bool operator==(
const POS_NET_KEY& aOther )
const
5614 return net == aOther.net && x == aOther.x && y == aOther.y;
5620 size_t operator()(
const POS_NET_KEY& aKey )
const
5622 size_t h1 =
static_cast<size_t>(
static_cast<uint32_t
>( aKey.net ) );
5623 size_t h2 =
static_cast<size_t>(
static_cast<uint32_t
>( aKey.x ) );
5624 size_t h3 =
static_cast<size_t>(
static_cast<uint32_t
>( aKey.y ) );
5625 return h1 ^ ( h2 * 0x9e3779b1U ) ^ ( h3 * 0x85ebca6bU );
5629 auto makeKey = [](
int aNet,
int aX,
int aY ) -> POS_NET_KEY
5643 bool operator==(
const POS_KEY& aOther )
const
5645 return x == aOther.x && y == aOther.y;
5651 size_t operator()(
const POS_KEY& aKey )
const
5653 size_t h1 =
static_cast<size_t>(
static_cast<uint32_t
>( aKey.x ) );
5654 size_t h2 =
static_cast<size_t>(
static_cast<uint32_t
>( aKey.y ) );
5655 return ( h1 * 0x9e3779b1U ) ^ ( h2 * 0x85ebca6bU );
5659 auto makePosKey = [](
int aX,
int aY ) -> POS_KEY
5667 auto resolveCopperLayer = [&](
int aDipTraceLayer ) ->
PCB_LAYER_ID
5672 layer =
MapLayer( aDipTraceLayer );
5680 std::unordered_map<POS_NET_KEY, std::set<int>, POS_NET_HASH> layersByNetPos;
5681 std::unordered_set<POS_NET_KEY, POS_NET_HASH> createdVias;
5682 std::unordered_map<POS_KEY, std::set<int>, POS_HASH> netsByPos;
5690 if(
chain.netIndex >= 0 )
5691 netsByPos[makePosKey( node.
x, node.
y )].insert(
chain.netIndex );
5695 std::shared_ptr<NETCLASS> defNetclass =
5696 m_board->GetDesignSettings().m_NetSettings->GetDefaultNetclass();
5703 if( !net &&
chain.netIndex >= 0 )
5706 for(
size_t i = 0; i + 1 <
chain.nodes.size(); i++ )
5721 nonCopperTrackSkips++;
5735 for(
size_t nodeIdx = 0; nodeIdx <
chain.nodes.size(); nodeIdx++ )
5739 auto posIt = layersByNetPos.find( key );
5740 bool explicitVia = node.
hasVia;
5746 auto netPosIt = netsByPos.find( makePosKey( node.
x, node.
y ) );
5748 if( netPosIt != netsByPos.end() && netPosIt->second.size() > 1 )
5754 if( createdVias.find( key ) != createdVias.end() )
5756 duplicateViaSkips++;
5760 if( explicitViaStyle )
5761 explicitViaStyleVias++;
5781 styleViaLayerA = l1;
5782 styleViaLayerB = l2;
5786 styleViaLayerA = l2;
5787 styleViaLayerB = l1;
5794 if( viaOuterDiam <= 0 )
5797 if( viaDrillDiam <= 0 )
5801 int viaWidthIU = ( viaOuterDiam > 0 ) ?
ToKiCadCoord( viaOuterDiam ) : 0;
5802 int viaDrillIU = ( viaDrillDiam > 0 ) ?
ToKiCadCoord( viaDrillDiam ) : 0;
5804 if( viaWidthIU <= 0 && defNetclass )
5805 viaWidthIU = defNetclass->GetViaDiameter();
5807 if( viaDrillIU <= 0 && defNetclass )
5808 viaDrillIU = defNetclass->GetViaDrill();
5810 if( viaWidthIU <= 0 )
5816 via->SetWidth( viaWidthIU );
5818 if( viaDrillIU > 0 )
5819 via->SetDrill( viaDrillIU );
5821 auto ordinalToLayer = [&](
size_t aOrdinal ) ->
PCB_LAYER_ID
5823 int copperCount =
m_board->GetCopperLayerCount();
5828 if( aOrdinal >=
static_cast<size_t>( copperCount - 1 ) )
5831 int innerIdx =
static_cast<int>( aOrdinal ) - 1;
5833 if( innerIdx < 0 || innerIdx > 29 )
5839 bool haveLayerRange =
false;
5843 auto accumulateDipLayer = [&](
int aDipLayer )
5852 if( !haveLayerRange )
5856 haveLayerRange =
true;
5860 minOrd = std::min( minOrd, ord );
5861 maxOrd = std::max( maxOrd, ord );
5865 accumulateDipLayer( node.
layer );
5868 accumulateDipLayer(
chain.nodes[nodeIdx - 1].layer );
5870 if( nodeIdx + 1 <
chain.nodes.size() )
5871 accumulateDipLayer(
chain.nodes[nodeIdx + 1].layer );
5873 if( posIt != layersByNetPos.end() )
5875 for(
int dipLayer : posIt->second )
5876 accumulateDipLayer( dipLayer );
5884 viaLayerA = styleViaLayerA;
5885 viaLayerB = styleViaLayerB;
5887 else if( haveLayerRange && minOrd < maxOrd )
5899 via->SetLayerPair( viaLayerA, viaLayerB );
5901 if( viaLayerA ==
F_Cu && viaLayerB ==
B_Cu )
5903 else if( viaLayerA ==
F_Cu || viaLayerB ==
B_Cu )
5912 createdVias.insert( key );
5918 wxT(
"DipTrace: created %d tracks and %d vias (%d chains with unresolved nets, "
5919 "%d non-copper tracks skipped, %d non-copper vias skipped, "
5920 "%d style vias, %d inferred layer-change vias, %d duplicate vias skipped, "
5921 "%d cross-net vias skipped)" ),
5922 trackCount, viaCount, missingChainNets, nonCopperTrackSkips, nonCopperViaSkips, explicitViaStyleVias,
5923 inferredLayerChangeVias, duplicateViaSkips, crossNetViaSkips );
5929 std::unordered_map<int, int> zoneSpokeModeByDipNet;
5930 std::unordered_set<int> zoneSpokeModeConflicts;
5934 if( dtZone.outline.size() < 3 )
5942 zoneLayer =
MapLayer( dtZone.layer );
5950 if( dtZone.clearance > 0 )
5953 if( dtZone.minWidth > 0 )
5960 if( dtZone.spokeWidth > 0 )
5963 if( dtZone.spokeMode == 0 )
5967 else if( dtZone.spokeMode > 0 && dtZone.smdSpokeMode == 0 )
5974 else if( dtZone.spokeMode > 0 )
5985 if( dtZone.islandInternal || dtZone.islandConnection )
5989 else if( dtZone.islandRegion )
5993 if( dtZone.minimumArea > 0 )
5995 long long minIslandLinearIU =
static_cast<long long>(
ToKiCadCoord( dtZone.minimumArea ) );
6010 for(
const auto& [x, y] : dtZone.outline )
6020 if( dtZone.netIndex >= 0 && dtZone.spokeMode >= 0 )
6022 auto [it, inserted] = zoneSpokeModeByDipNet.emplace( dtZone.netIndex, dtZone.spokeMode );
6024 if( !inserted && it->second != dtZone.spokeMode )
6025 zoneSpokeModeConflicts.insert( dtZone.netIndex );
6031 std::unordered_map<int, int> spokeModeByNetCode;
6033 for(
const auto& [dipNetIdx, spokeMode] : zoneSpokeModeByDipNet )
6035 if( zoneSpokeModeConflicts.count( dipNetIdx ) )
6043 spokeModeByNetCode[netIt->second->GetNetCode()] = spokeMode;
6046 int adjustedPadThermalAngles = 0;
6050 for(
PAD*
pad : fp->Pads() )
6055 auto modeIt = spokeModeByNetCode.find(
pad->GetNetCode() );
6057 if( modeIt == spokeModeByNetCode.end() )
6060 int spokeMode = modeIt->second;
6062 if( spokeMode == 1 || spokeMode == 4 )
6065 adjustedPadThermalAngles++;
6067 else if( spokeMode == 2 || spokeMode == 3 )
6070 adjustedPadThermalAngles++;
6075 if( adjustedPadThermalAngles > 0 )
6077 wxLogTrace(
traceDiptraceIo, wxT(
"DipTrace: applied thermal spoke angle overrides to %d pads" ),
6078 adjustedPadThermalAngles );
6105 planeOutline.
Append( x1, y1 );
6106 planeOutline.
Append( x2, y1 );
6107 planeOutline.
Append( x2, y2 );
6108 planeOutline.
Append( x1, y2 );
6116 if( layer.type != 1 || layer.planeNetIndex < 0 )
6129 wxT(
"DipTrace: plane layer %d references unresolved net index %d; skipping" ),
6130 layer.index, layer.planeNetIndex );
6153 wxT(
"DipTrace: synthesized plane zone on layer %d net '%s' (dt net %d)" ),
6154 layer.index, netinfo->
GetNetname(), layer.planeNetIndex );
6164 int edgeClearanceIU = 0;
6168 std::map<wxString, bool> viaDirectNets;
6172 if( zone.boardClearance > 0 )
6173 edgeClearanceIU = std::max( edgeClearanceIU,
ToKiCadCoord( zone.boardClearance ) );
6175 if( zone.viaDirect && zone.netIndex >= 0 )
6179 if( !net->GetNetname().IsEmpty() )
6180 viaDirectNets[net->GetNetname()] =
true;
6185 if( edgeClearanceIU <= 0 && viaDirectNets.empty() )
6186 return wxEmptyString;
6188 wxString rules = wxT(
"(version 1)\n" );
6190 if( edgeClearanceIU > 0 )
6194 rules += wxString::Format( wxT(
"\n(rule \"DipTrace zone board clearance\"\n" )
6195 wxT(
" (condition \"A.Type == 'Zone'\")\n" )
6196 wxT(
" (constraint edge_clearance (min %smm)))\n" ),
6200 for(
const auto& [netName, direct] : viaDirectNets )
6203 wxString escaped = netName;
6204 escaped.Replace( wxT(
"'" ), wxT(
"\\'" ) );
6206 rules += wxString::Format( wxT(
"\n(rule \"DipTrace via direct %s\"\n" )
6207 wxT(
" (condition \"A.Type == 'Via' && A.NetName == '%s'\")\n" )
6208 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)
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() const
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 SetWidth(int aWidth) override
void SetShape(SHAPE_T aShape) override
void SetEnd(const VECTOR2I &aEnd) override
void SetArcGeometry(const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd)
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
void SetStart(const VECTOR2I &aStart) override
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)
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
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
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)
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
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)
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 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 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
#define THROW_IO_ERRORF(msg,...)
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 float distance(const SFVEC2UI &a, const SFVEC2UI &b)
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