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 _(
"DipTrace parse error at offset 0x%06zX: %s" ),
1071 m_reader.GetOffset(), wxString::FromUTF8( e.what() ) ) );
1078 switch( aDipTraceLayer )
1086 case 8:
return F_Fab;
1087 case 9:
return B_Fab;
1091 if( aDipTraceLayer == 0 )
1094 if( aDipTraceLayer == 1 )
1097 if( aDipTraceLayer >= 14 && aDipTraceLayer <= 44 )
1099 int innerIdx = aDipTraceLayer - 14;
1101 if( innerIdx <= 30 )
1117 int ordinal = it->second;
1118 int copperCount = std::max( 2,
static_cast<int>(
m_layers.size() ) );
1123 if( ordinal >= copperCount - 1 )
1135 return static_cast<int>(
static_cast<int64_t
>( aDipTraceCoord ) * 100 / 3 );
1151 uint8_t magicLen =
m_reader.ReadByte();
1153 if( magicLen != 7 && magicLen != 11 )
1156 _(
"DipTrace: invalid magic length %u (expected 7 or 11)" ), magicLen ) );
1159 std::array<uint8_t, 11> magic = {};
1160 m_reader.ReadBytes( magic.data(), magicLen );
1162 if( std::memcmp( magic.data(),
"DTBOARD", 7 ) != 0 )
1164 THROW_IO_ERROR(
_(
"DipTrace: not a valid .dip board file (bad magic)" ) );
1172 std::string magicSuffix(
reinterpret_cast<const char*
>( magic.data() + 7 ),
1175 if( magicSuffix.size() != 4
1176 || std::isdigit(
static_cast<unsigned char>( magicSuffix[0] ) ) == 0
1177 || magicSuffix[1] !=
'.'
1178 || std::isdigit(
static_cast<unsigned char>( magicSuffix[2] ) ) == 0
1179 || std::isdigit(
static_cast<unsigned char>( magicSuffix[3] ) ) == 0 )
1181 THROW_IO_ERROR(
_(
"DipTrace: invalid legacy board version suffix" ) );
1184 int parsedMinor = ( magicSuffix[2] -
'0' ) * 10 + ( magicSuffix[3] -
'0' );
1219 int vertexCount =
m_reader.ReadInt3();
1223 if( vertexCount < 0 || vertexCount > 1000000 )
1224 THROW_IO_ERROR( wxString::Format(
_(
"DipTrace import: invalid outline vertex count %d." ),
1230 for(
int i = 0; i < vertexCount; i++ )
1250 for(
int i = 0; i < 4; i++ )
1253 for(
int i = 0; i < 3; i++ )
1260 int layerCount =
m_reader.ReadInt3();
1263 if( layerCount < 0 || layerCount > 100000 )
1264 THROW_IO_ERROR( wxString::Format(
_(
"DipTrace import: invalid layer count %d." ),
1271 for(
int i = 0; i < layerCount; i++ )
1288 int ordinal =
static_cast<int>(
m_layers.size() );
1297 for(
int i = 0; i < 6; i++ )
1310 uint8_t legacyPadding[12];
1311 m_reader.ReadBytes( legacyPadding,
sizeof( legacyPadding ) );
1330 const uint8_t* data =
m_reader.GetData();
1333 && data[
next + 1] <= 100 )
1340 m_reader.ReadBytes( padTail,
sizeof( padTail ) );
1343 int fieldCOrGroupCount =
m_reader.ReadInt3();
1345 if( fieldCOrGroupCount > 0 )
1351 int patternGroupCount =
m_reader.ReadInt3();
1360 if( aGroupCount < 0 || aGroupCount > 10000 )
1363 _(
"DipTrace: invalid pattern-name group count %d at offset 0x%06zX" ),
1364 aGroupCount,
m_reader.GetOffset() - 3 ) );
1367 for(
int i = 0; i < aGroupCount; i++ )
1371 int blockCount =
m_reader.ReadInt3();
1373 if( blockCount < 0 || blockCount > 10000 )
1376 _(
"DipTrace: invalid pattern-name block count %d" ), blockCount ) );
1379 for(
int j = 0; j < blockCount; j++ )
1390 if( aGroupCount < 0 || aGroupCount > 10000 )
1393 _(
"DipTrace: invalid pattern-style group count %d at offset 0x%06zX" ),
1394 aGroupCount,
m_reader.GetOffset() - 3 ) );
1399 for(
int i = 0; i < aGroupCount; i++ )
1404 m_reader.ReadBytes( color,
sizeof( color ) );
1410 int entryCount =
m_reader.ReadInt3();
1412 if( entryCount < 0 || entryCount > 10000 )
1415 _(
"DipTrace: invalid pattern-style entry count %d" ), entryCount ) );
1435 _(
"DipTrace: invalid implicit pattern-style entry count %d" ),
1445 bool dumpRuleSets =
EnvFlagEnabled(
"KICAD_DIPTRACE_DUMP_RULESETS" );
1457 if(
name.StartsWith( wxT(
"ViaStyle" ) ) )
1478 int ruleSetCount =
m_reader.ReadInt3();
1480 for(
int i = 0; i < ruleSetCount; i++ )
1482 wxString setName =
m_reader.ReadString();
1483 int setFieldA =
m_reader.ReadInt3();
1484 uint8_t flags[4] = { 0, 0, 0, 0 };
1486 for(
int f = 0; f < 4; f++ )
1489 int blockCount =
m_reader.ReadInt3();
1494 wxT(
"DipTrace: ruleset[%d] '%s' fieldA=%d flags=[%u,%u,%u,%u] blocks=%d" ), i, setName,
1495 setFieldA,
static_cast<unsigned int>( flags[0] ),
static_cast<unsigned int>( flags[1] ),
1496 static_cast<unsigned int>( flags[2] ),
static_cast<unsigned int>( flags[3] ), blockCount );
1499 for(
int b = 0; b < blockCount; b++ )
1501 std::array<int, 26> blockValues;
1503 for(
int v = 0; v < 25; v++ )
1504 blockValues[v] =
m_reader.ReadInt4();
1506 blockValues[25] = 0;
1513 int extraCount =
m_reader.ReadInt3();
1515 if( extraCount < 0 || extraCount > 10000 )
1518 _(
"DipTrace: invalid design-rule extra count %d" ), extraCount ) );
1521 for(
int e = 0; e < extraCount; e++ )
1525 m_reader.ReadBytes( rawPad,
sizeof( rawPad ) );
1529 if( i + 1 < ruleSetCount )
1537 static const uint8_t marker[] = { 0x4D, 0x7C, 0x6D, 0x00 };
1538 uint8_t
actual[
sizeof( marker )] = {};
1539 size_t markerOffset =
m_reader.GetOffset();
1543 if( std::memcmp(
actual, marker,
sizeof( marker ) ) != 0 )
1546 _(
"DipTrace: invalid ruleset transition marker at 0x%06zX" ), markerOffset ) );
1554 const uint8_t* data =
m_reader.GetData();
1557 && data[
next] == 0x01 && data[
next + 1] == 0x00 && data[
next + 2] == 0x14
1558 && data[
next + 3] == 0x89 && data[
next + 4] == 0x03 )
1561 m_reader.ReadBytes( suffix,
sizeof( suffix ) );
1571std::vector<std::pair<size_t, size_t>>
1574 const uint8_t* data =
m_reader.GetData();
1575 size_t fileSize =
m_reader.GetFileSize();
1577 static const int STRING_OFFSETS[] = { 14, 15, 16, 17, 18, 19, 20 };
1579 auto isBoundaryCoreAt = [&](
size_t aPos ) ->
bool
1586 auto stringOkAt = [&](
size_t aPos ) ->
bool
1596 auto cleanDeltaAt = [&](
size_t aBoundary ) ->
int
1598 for(
int d : STRING_OFFSETS )
1605 && s.length() >= 1 )
1619 int globalDelta = 14;
1623 if( !isBoundaryCoreAt( p ) )
1626 int d = cleanDeltaAt( p );
1636 std::vector<std::pair<size_t, size_t>> out;
1647 auto nextBoundaryAfter = [&](
size_t aFrom ) ->
size_t
1651 if( isBoundaryCoreAt( cand )
1652 && stringOkAt( cand +
static_cast<size_t>( globalDelta ) ) )
1665 out.emplace_back( b, b +
static_cast<size_t>( globalDelta ) );
1667 size_t next = nextBoundaryAfter( b );
1677 int declaredCount = ( parsedEnd + 3 <= fileSize ) ?
ReadInt3At( data, parsedEnd ) : -1;
1679 if( declaredCount > 0 && declaredCount <= 10000
1680 &&
static_cast<int>( out.size() ) != declaredCount )
1699 size_t projLib =
m_reader.FindString( wxT(
"Project Libraries" ), 0, 0 );
1712 struct ValidatedBoundary
1714 size_t boundaryOffset;
1718 std::vector<ValidatedBoundary> validated;
1719 bool fieldWalked =
false;
1725 std::vector<std::pair<size_t, size_t>> walk =
1741 std::set<size_t> stdSet( stdOffsets.begin(), stdOffsets.end() );
1742 std::vector<size_t> pureAlt;
1744 for(
size_t off : altOffsets )
1746 if( stdSet.find( off ) == stdSet.end() )
1747 pureAlt.push_back( off );
1750 std::vector<size_t> allBoundaries( stdOffsets );
1751 allBoundaries.insert( allBoundaries.end(), pureAlt.begin(), pureAlt.end() );
1752 std::sort( allBoundaries.begin(), allBoundaries.end() );
1754 allBoundaries.erase( std::unique( allBoundaries.begin(), allBoundaries.end() ),
1755 allBoundaries.end() );
1757 if( allBoundaries.empty() )
1762 static const int STRING_OFFSETS[] = { 14, 15, 16, 17, 18, 19, 20 };
1763 int stringDelta = 14;
1765 for(
size_t bOff : allBoundaries )
1767 if( parsedEnd > 50 && bOff < parsedEnd - 50 )
1772 for(
int delta : STRING_OFFSETS )
1774 size_t candidate = bOff +
delta;
1781 if( str.length() >= 1 )
1783 stringDelta =
delta;
1795 for(
size_t bOff : allBoundaries )
1797 if( parsedEnd > 50 && bOff < parsedEnd - 50 )
1800 size_t candidate = bOff + stringDelta;
1807 validated.push_back( { bOff, candidate } );
1814 if( validated.size() <= 1 && allBoundaries.size() > 1 )
1818 for(
size_t bOff : allBoundaries )
1820 if( parsedEnd > 50 && bOff < parsedEnd - 50 )
1825 for(
int delta : STRING_OFFSETS )
1827 size_t candidate = bOff +
delta;
1834 validated.push_back( { bOff, candidate } );
1840 if( !found && bOff + stringDelta + 3 <
m_reader.GetFileSize() )
1841 validated.push_back( { bOff, bOff + stringDelta } );
1847 if( !walk.empty() && walk.size() == validated.size() )
1851 for(
size_t i = 0; i < walk.size(); i++ )
1853 if( walk[i].first != validated[i].boundaryOffset )
1855 fieldWalked =
false;
1862 if( validated.empty() )
1864 wxLogWarning(
_(
"DipTrace: no validated component boundaries found" ) );
1869 int consecutiveFailures = 0;
1870 bool seenSuccess =
false;
1871 static constexpr int MAX_CONSECUTIVE_FAILURES = 3;
1872 static constexpr int MAX_COMPONENTS = 10000;
1874 for(
size_t vi = 0; vi < validated.size(); vi++ )
1876 if(
static_cast<int>(
m_components.size() ) >= MAX_COMPONENTS )
1879 const ValidatedBoundary& vb = validated[vi];
1880 m_reader.SetOffset( vb.stringStart );
1883 comp.boundaryOffset = vb.boundaryOffset;
1884 comp.stringStartOffset = vb.stringStart;
1893 size_t regionEnd = ( vi + 1 < validated.size() )
1894 ? validated[vi + 1].boundaryOffset
1896 comp.regionEndOffset = regionEnd;
1911 consecutiveFailures = 0;
1918 consecutiveFailures++;
1920 if( consecutiveFailures >= MAX_CONSECUTIVE_FAILURES )
1935 const uint8_t* data =
m_reader.GetData();
1936 size_t dataSize =
m_reader.GetFileSize();
1938 static constexpr double RAD_FIXED_TO_DEG = ( 180.0 /
M_PI ) / 1.0e4;
1939 static constexpr int MAX_ANGLE_FIXED = 200000;
1948 auto isThreeInt3 = [&](
size_t aPos ) ->
bool
1950 return aPos + 9 <= dataSize
1951 && data[aPos] == 0x0F && data[aPos + 1] == 0x42
1952 && data[aPos + 3] == 0x0F && data[aPos + 4] == 0x42
1953 && data[aPos + 6] == 0x0F && data[aPos + 7] == 0x42;
1956 std::vector<std::pair<size_t, double>> headers;
1958 for(
size_t off = 4; off + 32 <= dataSize; off++ )
1960 bool full = data[off] == 0x01 && data[off + 1] == 0x01 && isThreeInt3( off + 2 );
1961 bool compact = data[off] == 0x00 && data[off + 1] == 0x00 && isThreeInt3( off + 2 )
1962 && off + 22 <= dataSize
1963 && std::memcmp( data + off + 11,
"\0\0\0\0\0\0\0\0\0", 9 ) == 0
1964 && data[off + 20] == 0x0F && data[off + 21] == 0x42;
1966 if( !full && !compact )
1971 if( fixed >= -MAX_ANGLE_FIXED && fixed <= MAX_ANGLE_FIXED )
1972 headers.emplace_back( off,
static_cast<double>( fixed ) * RAD_FIXED_TO_DEG );
1975 if( headers.empty() )
1981 std::map<wxString, double> refdesAngle;
1983 for(
size_t off = 0; off + 4 <= dataSize; off++ )
1985 size_t len = (
static_cast<size_t>( data[off] ) << 8 ) | data[off + 1];
1987 if( len < 1 || len > 12 || off + 2 + 2 * len > dataSize )
1991 bool asciiUtf16 =
true;
1992 bool firstAlpha =
false;
1993 bool hasDigit =
false;
1995 for(
size_t c = 0; c < len; c++ )
1997 uint8_t hi = data[off + 2 + 2 * c];
1998 uint8_t lo = data[off + 3 + 2 * c];
2000 if( hi != 0 || lo < 0x20 || lo > 0x7E )
2007 firstAlpha = std::isalpha( lo );
2009 if( std::isdigit( lo ) )
2012 refdes +=
static_cast<char>( lo );
2015 if( !asciiUtf16 || !firstAlpha || !hasDigit )
2019 auto it = std::upper_bound( headers.begin(), headers.end(), off,
2020 [](
size_t aPos,
const std::pair<size_t, double>& aHdr )
2021 { return aPos < aHdr.first; } );
2023 if( it == headers.begin() )
2026 refdesAngle.emplace( wxString::FromUTF8( refdes ), ( it - 1 )->second );
2033 auto it = refdesAngle.find(
comp.refdes );
2035 if( it != refdesAngle.end() )
2037 comp.placementAngleDeg = it->second;
2038 comp.hasPlacementAngle =
true;
2043 wxLogTrace(
traceDiptraceIo, wxT(
"DipTrace: keyed %zu exact placement angles by refdes" ), applied );
2073 bool fatalHeaderError =
false;
2077 int quarterTurns = 0;
2095 aComp.
flags.resize( 4 );
2097 for(
int i = 0; i < 4; i++ )
2101 for(
int i = 0; i < 4; i++ )
2103 if( aComp.
flags[i] > 10 )
2110 fatalHeaderError =
true;
2112 _(
"DipTrace: invalid component flag byte %u at boundary 0x%06zX" ),
2113 static_cast<unsigned int>( aComp.
flags[i] ), aBoundaryOffset ) );
2123 size_t posXPos =
m_reader.GetOffset();
2125 size_t posYPos =
m_reader.GetOffset();
2127 size_t rotPos =
m_reader.GetOffset();
2129 size_t fieldCPos =
m_reader.GetOffset();
2133 uint8_t sep1 =
m_reader.ReadByte();
2138 size_t fieldDPos =
m_reader.GetOffset();
2142 uint8_t sep2 =
m_reader.ReadByte();
2143 uint8_t sep3 =
m_reader.ReadByte();
2145 if( sep2 > 1 || sep3 != 0 )
2178 fieldCPos, fieldDPos );
2182 if( fatalHeaderError )
2203 const uint8_t* data =
m_reader.GetData();
2204 size_t dataSize =
m_reader.GetFileSize();
2206 if( aRegionEnd > dataSize )
2207 aRegionEnd = dataSize;
2212 auto isPad1At = [&](
size_t aPos ) ->
bool
2214 if( aPos < aRegionStart || aPos + PAD_PRE_HEADER_SIZE + 4 > aRegionEnd )
2237 size_t labelPos = namePos + nameLen;
2243 size_t dimPos = labelPos + labelLen;
2251 return w > 0 && h > 0 && w <= 10000000 && h <= 10000000;
2254 size_t chainPos = 0;
2265 if( isPad1At( candidate ) )
2266 chainPos = candidate;
2273 static const uint8_t IDX1_PATTERN[] = { 0x0F, 0x42, 0x41 };
2275 std::vector<size_t> matches =
FindAllBoundaries( data, dataSize, IDX1_PATTERN, 3,
2276 aRegionStart, aRegionEnd );
2278 for(
size_t pos : matches )
2280 if( isPad1At( pos ) )
2293 wxLogTrace(
traceDiptraceIo, wxT(
"DipTrace: pad 1 not found in region 0x%06zX-0x%06zX for '%s'" ),
2301 for(
int padNum = 1; padNum < 500; padNum++ )
2307 int padNetIndex =
ReadInt3At( data, chainPos + 3 );
2309 int padY =
ReadInt4At( data, chainPos + 10 );
2311 if( padIndex != padNum )
2323 if( nameFieldLen == 0 )
2326 size_t labelPos = namePos + nameFieldLen;
2329 if( labelFieldLen == 0 )
2332 size_t dimPos = labelPos + labelFieldLen;
2340 int drillH =
ReadInt4At( data, dimPos + 12 );
2342 if( padW <= 0 || padH <= 0 || padW > 10000000 || padH > 10000000 )
2351 int padStyleC =
ReadInt3At( data, postDimPos + 4 );
2354 pad.index = padIndex;
2355 pad.netIndex = padNetIndex;
2360 pad.drillWidth = drillW;
2361 pad.drillHeight = drillH;
2362 pad.style = padStyleC;
2363 pad.mountType = data[postDimPos + 3];
2365 size_t afterName = 0;
2368 size_t afterLabel = 0;
2373 if(
pad.number.IsEmpty() && !
pad.label.IsEmpty() )
2377 if( padStyleC == 3 )
2379 int vertexCount =
ReadInt3At( data, postDimPos + 8 );
2381 if( vertexCount > 0 && vertexCount <= 200
2388 for(
int vi = 0; vi < vertexCount; vi++ )
2392 pad.polygonVertices.emplace_back( vx, vy );
2402 if( postDimPos + postDimSize > aRegionEnd )
2405 pad.orientClass = data[postDimPos + postDimSize - 1];
2410 chainPos = postDimPos + postDimSize;
2481 size_t aSearchEnd, std::vector<DT_MOUNT_HOLE>& aHoles )
2483 static constexpr int MAX_REASONABLE_DIM = 50000000;
2484 static constexpr int MAX_HOLE_COUNT = 64;
2492 int countField =
ReadInt3At( aData, aBlockStart );
2493 int holeCount = countField - 2;
2495 if( holeCount <= 0 || holeCount > MAX_HOLE_COUNT )
2498 uint8_t headerFlag = aData[aBlockStart + 3];
2500 if( headerFlag > 1 )
2503 for(
int i = 0; i < 4; i++ )
2505 if(
ReadInt4At( aData, aBlockStart + 4 +
static_cast<size_t>( i ) * 4 ) != 0 )
2511 size_t holeEnd = holeStart + holesBytes;
2512 size_t termPos = holeEnd;
2518 std::vector<DT_MOUNT_HOLE> parsedHoles;
2519 parsedHoles.reserve(
static_cast<size_t>( holeCount ) );
2521 for(
int hi = 0; hi < holeCount; hi++ )
2524 uint8_t holeFlagA = aData[hp];
2525 uint8_t holeFlagB = aData[hp + 1];
2527 if( holeFlagA != 0 || holeFlagB > 1 )
2535 if(
std::abs( x ) > MAX_REASONABLE_DIM ||
std::abs( y ) > MAX_REASONABLE_DIM
2536 || outer <= 0 || outer > MAX_REASONABLE_DIM
2537 || drill <= 0 || drill > MAX_REASONABLE_DIM
2548 parsedHoles.push_back( hole );
2551 if( aData[termPos] != 0 || aData[termPos + 1] != 0 )
2554 for(
int i = 0; i < 4; i++ )
2556 if(
ReadInt4At( aData, trailerPos +
static_cast<size_t>( i ) * 4 ) != 0 )
2560 aHoles = std::move( parsedHoles );
2568 wxUnusedVar( aRegionStart );
2569 aComp.
holes.clear();
2574 const uint8_t* data =
m_reader.GetData();
2575 size_t dataSize =
m_reader.GetFileSize();
2577 if( aRegionEnd > dataSize )
2578 aRegionEnd = dataSize;
2580 size_t searchEnd = aRegionEnd;
2585 std::vector<size_t> candidates;
2593 if( shapeCount > 0 && shapeCount <= 500 )
2596 candidates.push_back( shapeStart +
static_cast<size_t>( shapeCount ) * recSize );
2605 if( shapeCount >= 3 && shapeCount <= 200 )
2608 +
static_cast<size_t>( shapeCount - 1 )
2616 std::vector<size_t> uniqueCandidates;
2617 uniqueCandidates.reserve( candidates.size() );
2619 for(
size_t pos : candidates )
2621 if( std::find( uniqueCandidates.begin(), uniqueCandidates.end(), pos ) == uniqueCandidates.end() )
2622 uniqueCandidates.push_back( pos );
2625 size_t decodedAt = 0;
2627 for(
size_t pos : uniqueCandidates )
2636 if( decodedAt == 0 )
2642 wxT(
"DipTrace: mount-holes ref=%s count=%zu off=0x%06zX "
2643 "padEnd=0x%06zX gap=%zu" ),
2655 const uint8_t* data =
m_reader.GetData();
2656 size_t dataSize =
m_reader.GetFileSize();
2658 if( aRegionEnd > dataSize )
2659 aRegionEnd = dataSize;
2665 if( aComp.
shapes.empty() )
2673 wxLogTrace(
traceDiptraceIo, wxT(
"DipTrace: no pad region end for shape finding in '%s'" ),
2682 wxLogTrace(
traceDiptraceIo, wxT(
"DipTrace: shape region beyond component bounds for '%s'" ),
2692 if( shapeCount <= 0 || shapeCount > 500 )
2696 size_t shapeEnd = shapeStart +
static_cast<size_t>( shapeCount ) * recSize;
2698 if( shapeEnd > aRegionEnd )
2701 for(
int i = 0; i < shapeCount; i++ )
2703 size_t rp = shapeStart +
static_cast<size_t>( i ) * recSize;
2735 aComp.
shapes.push_back( shape );
2743 const uint8_t* data =
m_reader.GetData();
2744 size_t dataSize =
m_reader.GetFileSize();
2746 if( aRegionEnd > dataSize )
2747 aRegionEnd = dataSize;
2749 auto isTahomaAt = [&](
size_t aPos ) ->
bool
2755 auto u16beAt = [&](
size_t aPos ) ->
int
2757 return (
static_cast<int>( data[aPos] ) << 8 ) |
static_cast<int>( data[aPos + 1] );
2764 std::vector<size_t> fontBlocks;
2765 bool fieldWalked =
false;
2773 if( preLabel >= 0 && preLabel <= 256 )
2778 while( isTahomaAt( bs ) )
2782 bool slot0InBounds = ( body + 3 <= aRegionEnd );
2784 for(
int n = 0; n <= 3; n++ )
2786 if( body +
static_cast<size_t>( n ) * 8 + 3 > aRegionEnd )
2789 int st =
ReadInt3At( data, body +
static_cast<size_t>( n ) * 8 );
2791 if( st == 0 || st == 1 || st == 2 || st == 3 || st == 5 || st == 6 || st == 7
2807 fontBlocks.push_back( bs );
2819 if( lcPos + 2 > aRegionEnd )
2825 int lc = u16beAt( lcPos );
2827 if( lc < 0 || lc > 256 )
2835 fontBlocks.push_back( bs );
2838 + 2 *
static_cast<size_t>( lc );
2844 fieldWalked = walkOk && !fontBlocks.empty();
2853 aRegionStart, aRegionEnd );
2856 if( fontBlocks.empty() )
2859 size_t shapesBefore = aComp.
shapes.size();
2861 for(
size_t bi = 0; bi < fontBlocks.size(); bi++ )
2863 size_t blockStart = fontBlocks[bi];
2864 size_t nextBoundary = ( bi + 1 < fontBlocks.size() ) ? fontBlocks[bi + 1]
2869 if( headerEnd + 19 > nextBoundary )
2873 int lineWidth =
ReadInt4At( data, metaStart + 18 );
2878 int layerIdx = ( blockStart >= 5 ) ?
ReadInt3At( data, blockStart - 5 ) : 0;
2880 size_t bodyPos = headerEnd;
2895 if( x1 == 0 && y1 == 0 && x2 == 0 && y2 == 0 )
2898 int shapeType =
ReadInt3At( data, bodyPos + 16 );
2905 shape.
width = lineWidth;
2906 shape.
layer = layerIdx;
2910 if( shapeType == 0 )
2914 else if( shapeType == 1 || shapeType == 5 )
2918 else if( shapeType == 3 )
2926 if( bodyPos + 43 <= nextBoundary )
2945 aComp.
shapes.push_back( shape );
2948 if( !fieldWalked && aComp.
shapes.size() > shapesBefore )
2956 wxUnusedVar( aRegionStart );
2961 const uint8_t* data =
m_reader.GetData();
2962 size_t dataSize =
m_reader.GetFileSize();
2964 if( aRegionEnd > dataSize )
2965 aRegionEnd = dataSize;
2973 if( shapeCount < 3 || shapeCount > 200 )
2979 if( shapeEnd > aRegionEnd )
2982 std::vector<DT_FP_SHAPE> decoded;
2983 decoded.reserve(
static_cast<size_t>( shapeCount ) );
2985 auto inNormRange = [](
int aVal ) ->
bool
2991 for(
int i = 1; i + 1 < shapeCount; i++ )
3001 if( !inNormRange( x1 ) || !inNormRange( y1 ) || !inNormRange( x2 ) || !inNormRange( y2 ) )
3005 shape.
width = width;
3022 if( !inNormRange( x3 ) || !inNormRange( y3 ) )
3038 decoded.push_back( shape );
3041 if( !decoded.empty() )
3042 aComp.
shapes.insert( aComp.
shapes.end(), decoded.begin(), decoded.end() );
3055 const uint8_t* data =
m_reader.GetData();
3057 size_t savedOffset =
m_reader.GetOffset();
3058 bool parsed =
false;
3060 auto tryParseTailAt = [&](
size_t aTailStart ) ->
bool
3085 m_reader.SetOffset( aTailStart + 11 );
3090 if( check1 != 0 || check2 != 0 )
3093 uint8_t sideFlag1 =
m_reader.ReadByte();
3095 uint8_t sideFlag2 =
m_reader.ReadByte();
3096 int orderIdx =
m_reader.ReadInt3();
3098 int refdesYOffset =
m_reader.ReadInt4();
3099 int valueYOffset =
m_reader.ReadInt4();
3100 uint8_t hasOffset =
m_reader.ReadByte();
3101 uint8_t tailTerm =
m_reader.ReadByte();
3106 if( hasOffset > 1 || tailTerm != 0 )
3109 if(
std::abs( refdesYOffset ) > 50000000 ||
std::abs( valueYOffset ) > 50000000 )
3123 if( sideFlag1 == 1 && sideFlag2 == 1 )
3127 orderIdx, refdesYOffset, valueYOffset, hasOffset, tailTerm );
3137 parsed = tryParseTailAt( canonicalTailStart );
3141 size_t dumpLen = std::min<size_t>( 96, aRegionEnd );
3142 size_t dumpStart = aRegionEnd - dumpLen;
3143 wxString hex =
BytesToHex( data + dumpStart, dumpLen );
3146 wxT(
"DipTrace: component-tail-missing ref=%s regionEnd=0x%06zX "
3147 "tailHexStart=0x%06zX len=%lu hex=[%s]" ),
3148 aComp.
refdes, aRegionEnd, dumpStart,
static_cast<unsigned long>( dumpLen ), hex );
3161 size_t postComp =
m_reader.GetOffset();
3166 size_t projLibOffset =
m_reader.FindString( wxT(
"Project Libraries" ), 0, 0 );
3167 size_t gapEnd = ( projLibOffset !=
NOT_FOUND ) ? projLibOffset :
m_reader.GetFileSize();
3183 size_t pos = aSearchStart;
3184 auto textRecordsLookValid =
3185 [&](
size_t aRecordStart,
int aCount,
size_t aSectionEnd ) ->
bool
3187 size_t savedOffset =
m_reader.GetOffset();
3188 m_reader.SetOffset( aRecordStart );
3192 for(
int ti = 0; ti < aCount; ti++ )
3207 int lineWidth =
m_reader.ReadInt4();
3210 if( lineWidth < 0 || lineWidth > 10000000 || layer < -100 || layer > 100 )
3211 throw std::runtime_error(
"invalid text metrics" );
3229 if( ti < aCount - 1 )
3235 if(
m_reader.GetOffset() > aSectionEnd )
3236 throw std::runtime_error(
"text section overrun" );
3242 catch(
const std::exception& )
3249 while( pos + 20 < aSearchEnd )
3256 size_t countPos = idx + 9;
3258 if( countPos + 5 > aSearchEnd )
3261 const uint8_t* data =
m_reader.GetData();
3262 const uint8_t* b = data + countPos;
3263 int countVal =
static_cast<int>( (
static_cast<int>( b[0] ) << 16 )
3264 | (
static_cast<int>( b[1] ) << 8 )
3265 |
static_cast<int>( b[2] ) ) -
INT3_BIAS;
3267 if( countVal >= 1 && countVal <= 1000 )
3269 uint8_t flag1 = data[countPos + 3];
3270 uint8_t flag2 = data[countPos + 4];
3272 if( flag1 == 1 && flag2 == 0 )
3274 size_t recordStart = countPos + 5;
3276 if( textRecordsLookValid( recordStart, countVal, aSearchEnd ) )
3292 for(
int ti = 0; ti < aCount; ti++ )
3331 if( ti < aCount - 1 )
3342 _(
"DipTrace: text object [%d] parse error: %s" ),
3372 aSearchStart, aSearchEnd );
3374 static constexpr int MAX_NETS = 10000;
3375 static constexpr int MAX_REASONABLE_WIDTH = 5000000;
3377 size_t firstNetSentinel = 0;
3379 for(
size_t sentOff : sentinelOffsets )
3381 if(
static_cast<int>(
m_nets.size() ) >= MAX_NETS )
3387 if( pos + 3 + 3 + 4 + 4 + 2 >
m_reader.GetFileSize() )
3393 bool acceptedNetRecord =
false;
3397 int netIndex =
m_reader.ReadInt3();
3405 if( field0 < 0 || field0 > 10 || netIndex < 0 || netIndex >= MAX_NETS )
3408 if( width1 < 0 || width1 > MAX_REASONABLE_WIDTH
3409 || width2 < 0 || width2 > MAX_REASONABLE_WIDTH )
3414 bool expectedNetIndex = netIndex ==
static_cast<int>(
m_nets.size() );
3415 acceptedNetRecord = expectedNetIndex;
3420 if( expectedNetIndex )
3423 _(
"DipTrace import: invalid net name for net index %d at "
3424 "offset 0x%06zX." ),
3425 netIndex,
m_reader.GetOffset() ) );
3431 net.
index = netIndex;
3437 wxLogTrace(
traceDiptraceIo, wxT(
"DipTrace: net idx=%d name=%s width1=%d width2=%d" ), netIndex,
name,
3443 if( acceptedNetRecord )
3450 if( firstNetSentinel == 0 )
3451 firstNetSentinel = sentOff;
3455 m_nets.push_back( std::move( net ) );
3463 bool netSectionFieldAnchored =
false;
3465 if( firstNetSentinel >= 5 && !
m_nets.empty() )
3468 ==
static_cast<int>(
m_nets.size() );
3471 if( !
m_nets.empty() && !netSectionFieldAnchored )
3474 size_t totalNodes = 0;
3475 size_t viaStyleNodes = 0;
3476 size_t routeFlagNodes = 0;
3477 size_t viaStyleAndRouteFlagNodes = 0;
3478 size_t routeFlagOnlyNodes = 0;
3479 size_t routeMode0Nodes = 0;
3480 size_t routeMode1Nodes = 0;
3481 size_t routeMode3Nodes = 0;
3482 size_t routeModeOtherNodes = 0;
3499 if( hasStyle && hasFlag )
3500 viaStyleAndRouteFlagNodes++;
3502 routeFlagOnlyNodes++;
3519 routeModeOtherNodes++;
3526 wxT(
"DipTrace: parsed %zu net names, %zu track chains, %zu nodes "
3527 "(viaStyle=%zu, routeFlag=%zu, both=%zu, flagOnly=%zu, "
3528 "routeMode[0]=%zu, routeMode[1]=%zu, routeMode[3]=%zu, routeMode[other]=%zu)" ),
3530 viaStyleAndRouteFlagNodes, routeFlagOnlyNodes, routeMode0Nodes, routeMode1Nodes, routeMode3Nodes,
3531 routeModeOtherNodes );
3551 size_t startPos =
m_reader.GetOffset();
3553 static constexpr size_t MAX_NET_BODY = 65536;
3554 size_t scanEnd = std::min( startPos + MAX_NET_BODY,
m_reader.GetFileSize() );
3558 startPos + 20, scanEnd );
3560 if( nextSentinel != std::string::npos )
3561 scanEnd = nextSentinel;
3563 const uint8_t* data =
m_reader.GetData();
3564 size_t fileSize =
m_reader.GetFileSize();
3566 size_t chainScanStart = startPos;
3567 static constexpr int MAX_REASONABLE_VIA_DIM = 5000000;
3568 static constexpr int MAX_PADREFS_PER_COMPONENT = 12;
3570 int componentCount =
static_cast<int>(
m_components.size() );
3571 int maxReasonablePadRefs = std::max( 512, componentCount * MAX_PADREFS_PER_COMPONENT );
3575 if( startPos + 22 <= scanEnd )
3577 int viaOuterDefault =
ReadInt4At( data, startPos );
3578 int viaDrillDefault =
ReadInt4At( data, startPos + 4 );
3579 uint8_t marker = data[startPos + 8];
3580 bool zeroBlock = std::all_of( data + startPos + 9, data + startPos + 16,
3581 []( uint8_t b ) {
return b == 0; } );
3582 int separator =
ReadInt3At( data, startPos + 16 );
3583 int padRefCount =
ReadInt3At( data, startPos + 19 );
3585 if( marker <= 1 && zeroBlock && separator == 0 && padRefCount >= 0
3586 && padRefCount <= maxReasonablePadRefs )
3588 size_t refsStart = startPos + 22;
3589 size_t refsEnd = refsStart +
static_cast<size_t>( padRefCount ) * 6;
3591 if( refsEnd <= scanEnd )
3593 std::vector<DT_PAD_REF> parsedRefs;
3594 parsedRefs.reserve(
static_cast<size_t>( padRefCount ) );
3595 int rangeHitsBase0 = 0;
3596 int rangeHitsBase1 = 0;
3598 for(
int i = 0; i < padRefCount; i++ )
3600 size_t refPos = refsStart +
static_cast<size_t>( i ) * 6;
3602 int padIndex =
ReadInt3At( data, refPos + 3 );
3604 if( compIndex >= 0 && padIndex > 0 )
3606 parsedRefs.push_back( { compIndex, padIndex } );
3608 if( compIndex >= 0 && compIndex < componentCount )
3611 if( compIndex >= 1 && compIndex <= componentCount )
3616 int requiredRangeHits = std::min( 4,
static_cast<int>( parsedRefs.size() ) );
3617 bool plausibleRefs = parsedRefs.empty()
3618 || std::max( rangeHitsBase0, rangeHitsBase1 ) >= requiredRangeHits;
3622 if( viaOuterDefault > 0 && viaOuterDefault <= MAX_REASONABLE_VIA_DIM )
3625 if( viaDrillDefault > 0 && viaDrillDefault <= MAX_REASONABLE_VIA_DIM )
3628 aNet.
padRefs = std::move( parsedRefs );
3629 chainScanStart = refsEnd;
3635 size_t pos = chainScanStart;
3641 if( chainPos == std::string::npos )
3646 if( headerStart + 6 > fileSize )
3649 const uint8_t* h = data + headerStart;
3650 int chainIdx = ( (
static_cast<int>( h[0] ) << 16 )
3651 | (
static_cast<int>( h[1] ) << 8 )
3652 |
static_cast<int>( h[2] ) ) -
INT3_BIAS;
3653 int nodeCount = ( (
static_cast<int>( h[3] ) << 16 )
3654 | (
static_cast<int>( h[4] ) << 8 )
3655 |
static_cast<int>( h[5] ) ) -
INT3_BIAS;
3657 auto firstNodeLooksPlausible = [&]() ->
bool
3659 size_t firstNode = headerStart + 6;
3664 const uint8_t* n = data + firstNode;
3666 int x =
static_cast<int>(
3667 (
static_cast<unsigned int>( n[0] ) << 24 )
3668 | (
static_cast<unsigned int>( n[1] ) << 16 )
3669 | (
static_cast<unsigned int>( n[2] ) << 8 )
3670 |
static_cast<unsigned int>( n[3] ) ) -
INT4_BIAS;
3672 int y =
static_cast<int>(
3673 (
static_cast<unsigned int>( n[4] ) << 24 )
3674 | (
static_cast<unsigned int>( n[5] ) << 16 )
3675 | (
static_cast<unsigned int>( n[6] ) << 8 )
3676 |
static_cast<unsigned int>( n[7] ) ) -
INT4_BIAS;
3678 int layer = ( (
static_cast<int>( n[8] ) << 16 )
3679 | (
static_cast<int>( n[9] ) << 8 )
3680 |
static_cast<int>( n[10] ) ) -
INT3_BIAS;
3682 int width =
static_cast<int>(
3683 (
static_cast<unsigned int>( n[14] ) << 24 )
3684 | (
static_cast<unsigned int>( n[15] ) << 16 )
3685 | (
static_cast<unsigned int>( n[16] ) << 8 )
3686 |
static_cast<unsigned int>( n[17] ) ) -
INT4_BIAS;
3688 int viaStyleIdx = ( (
static_cast<int>( n[27] ) << 16 )
3689 | (
static_cast<int>( n[28] ) << 8 )
3690 |
static_cast<int>( n[29] ) ) -
INT3_BIAS;
3692 int routeMode = ( (
static_cast<int>( n[37] ) << 16 )
3693 | (
static_cast<int>( n[38] ) << 8 )
3694 |
static_cast<int>( n[39] ) ) -
INT3_BIAS;
3696 return x > -100000000 && x < 100000000
3697 && y > -100000000 && y < 100000000
3698 && layer >= 0 && layer <= 50
3699 && width > 0 && width <= 5000000
3700 && viaStyleIdx >= -1 && viaStyleIdx <= 10000
3701 && routeMode >= 0 && routeMode <= 10
3705 if( chainIdx < 0 || nodeCount < 1 || nodeCount > 10000 )
3707 if( chainIdx >= 0 && firstNodeLooksPlausible() )
3710 _(
"DipTrace import: invalid route-chain node count %d for net '%s' "
3711 "at offset 0x%06zX." ),
3712 nodeCount, aNet.
name, headerStart + 3 ) );
3719 size_t nodesStart = headerStart + 6;
3720 size_t nodesEnd = nodesStart +
static_cast<size_t>( nodeCount ) *
TRACK_NODE_SIZE;
3722 if( nodesEnd > scanEnd )
3724 if( firstNodeLooksPlausible() )
3727 _(
"DipTrace import: route-chain node count %d for net '%s' overruns "
3728 "record at offset 0x%06zX." ),
3729 nodeCount, aNet.
name, headerStart + 3 ) );
3738 chain.nodes.reserve( nodeCount );
3742 for(
int i = 0; i < nodeCount; i++ )
3744 const uint8_t* n = data + nodesStart +
static_cast<size_t>( i ) *
TRACK_NODE_SIZE;
3749 node.
x =
static_cast<int>(
3750 (
static_cast<unsigned int>( n[0] ) << 24 )
3751 | (
static_cast<unsigned int>( n[1] ) << 16 )
3752 | (
static_cast<unsigned int>( n[2] ) << 8 )
3753 |
static_cast<unsigned int>( n[3] ) ) -
INT4_BIAS;
3756 node.y =
static_cast<int>(
3757 (
static_cast<unsigned int>( n[4] ) << 24 )
3758 | (
static_cast<unsigned int>( n[5] ) << 16 )
3759 | (
static_cast<unsigned int>( n[6] ) << 8 )
3760 |
static_cast<unsigned int>( n[7] ) ) -
INT4_BIAS;
3763 node.layer = ( (
static_cast<int>( n[8] ) << 16 )
3764 | (
static_cast<int>( n[9] ) << 8 )
3765 |
static_cast<int>( n[10] ) ) -
INT3_BIAS;
3768 node.width =
static_cast<int>(
3769 (
static_cast<unsigned int>( n[14] ) << 24 )
3770 | (
static_cast<unsigned int>( n[15] ) << 16 )
3771 | (
static_cast<unsigned int>( n[16] ) << 8 )
3772 |
static_cast<unsigned int>( n[17] ) ) -
INT4_BIAS;
3775 node.viaStyleIdx = ( (
static_cast<int>( n[27] ) << 16 )
3776 | (
static_cast<int>( n[28] ) << 8 )
3777 |
static_cast<int>( n[29] ) ) -
INT3_BIAS;
3778 node.viaOuterDiam =
static_cast<int>(
3779 (
static_cast<unsigned int>( n[18] ) << 24 )
3780 | (
static_cast<unsigned int>( n[19] ) << 16 )
3781 | (
static_cast<unsigned int>( n[20] ) << 8 )
3782 |
static_cast<unsigned int>( n[21] ) ) -
INT4_BIAS;
3783 node.routeFlag = n[22];
3784 node.viaDrillDiam =
static_cast<int>(
3785 (
static_cast<unsigned int>( n[30] ) << 24 )
3786 | (
static_cast<unsigned int>( n[31] ) << 16 )
3787 | (
static_cast<unsigned int>( n[32] ) << 8 )
3788 |
static_cast<unsigned int>( n[33] ) ) -
INT4_BIAS;
3791 node.routeMode = ( (
static_cast<int>( n[37] ) << 16 )
3792 | (
static_cast<int>( n[38] ) << 8 )
3793 |
static_cast<int>( n[39] ) ) -
INT3_BIAS;
3797 node.hasVia = ( node.viaStyleIdx >= 0 );
3799 if( node.width <= 0 || node.width > 5000000 )
3805 if( node.layer < 0 || node.layer > 50 )
3811 chain.nodes.push_back( node );
3814 if( valid && !
chain.nodes.empty() )
3828 size_t totalRefs = 0;
3831 totalRefs += net.padRefs.size();
3833 if( totalRefs == 0 )
3836 size_t maxReasonableRefs = std::max<size_t>( 5000,
m_components.size() * 64 );
3838 if( totalRefs > maxReasonableRefs )
3840 wxLogTrace(
traceDiptraceIo, wxT(
"DipTrace: skipping routing-ref pad inference (%zu refs exceeds %zu cap)" ),
3841 totalRefs, maxReasonableRefs );
3845 std::vector<std::unordered_map<int, size_t>> padPosByIndex(
m_components.size() );
3849 const std::vector<DT_PAD>& pads =
m_components[c].pads;
3850 auto& padMap = padPosByIndex[c];
3852 padMap.reserve( pads.size() );
3854 for(
size_t p = 0; p < pads.size(); p++ )
3855 padMap.emplace( pads[p].index, p );
3866 auto scoreBase = [&](
int aBase ) -> SCORE
3879 if( compPos < 0 || compPos >=
static_cast<int>(
m_components.size() ) )
3882 const auto& padMap = padPosByIndex[compPos];
3883 auto it = padMap.find( ref.
padIndex );
3885 if( it == padMap.end() )
3891 if(
pad.netIndex < 0 )
3899 SCORE scoreBase0 = scoreBase( 0 );
3900 SCORE scoreBase1 = scoreBase( 1 );
3902 if( scoreBase0.hits == 0 && scoreBase1.hits == 0 )
3905 if( scoreBase0.hits == scoreBase1.hits && scoreBase0.fillable == scoreBase1.fillable )
3908 SCORE best = scoreBase0;
3910 if( scoreBase1.hits > scoreBase0.hits
3911 || ( scoreBase1.hits == scoreBase0.hits && scoreBase1.fillable > scoreBase0.fillable ) )
3928 if( compPos < 0 || compPos >=
static_cast<int>(
m_components.size() ) )
3931 auto& padMap = padPosByIndex[compPos];
3932 auto it = padMap.find( ref.
padIndex );
3934 if( it == padMap.end() )
3939 if(
pad.netIndex < 0 )
3941 pad.netIndex = net.index;
3944 else if(
pad.netIndex != net.index )
3951 if( assigned > 0 || conflicts > 0 )
3954 wxT(
"DipTrace: routing-ref pad net inference (base=%d): %d refs, %d hits, "
3955 "%d assigned, %d conflicts" ),
3956 best.base, best.refs, best.hits, assigned, conflicts );
3969 const uint8_t* data =
m_reader.GetData();
3970 size_t fileSize =
m_reader.GetFileSize();
3972 if( aSearchEnd > fileSize )
3973 aSearchEnd = fileSize;
3975 static constexpr int MAX_ZONES = 100;
3976 static constexpr int MAX_VERTICES = 50000;
3977 static constexpr int MAX_REASONABLE_DIM = 5000000;
3993 oxMin = std::min( oxMin, v.x );
3994 oxMax = std::max( oxMax, v.x );
3995 oyMin = std::min( oyMin, v.y );
3996 oyMax = std::max( oyMax, v.y );
3999 bboxXMin = oxMin - 5000000;
4000 bboxXMax = oxMax + 5000000;
4001 bboxYMin = oyMin - 5000000;
4002 bboxYMax = oyMax + 5000000;
4005 auto headerLooksPlausible = [&](
size_t aPos,
bool aCheckVertexSamples ) ->
bool
4007 if( aPos + 30 > aSearchEnd )
4011 int flags1 = data[aPos + 3];
4012 int flags3 = data[aPos + 5];
4015 int minimumArea =
ReadInt4At( data, aPos + 14 );
4016 int separator =
ReadInt3At( data, aPos + 18 );
4019 int vtxCount =
ReadInt3At( data, aPos + 27 );
4021 if( fieldA < 0 || fieldA > 10000 )
4024 if( fieldB < -1 || fieldB > 10000 )
4027 if( flags1 > 2 || flags3 > 2 )
4033 if( minWidth <= 0 || minWidth > MAX_REASONABLE_DIM )
4036 if( minimumArea < 0 || minimumArea > MAX_REASONABLE_DIM )
4042 if( layer < -10 || layer > 100 )
4045 if( vtxCount < 3 || vtxCount > MAX_VERTICES )
4048 size_t vtxStart = aPos + 30;
4049 size_t vtxEnd = vtxStart +
static_cast<size_t>( vtxCount ) * 8;
4051 if( vtxEnd > aSearchEnd )
4054 if( !aCheckVertexSamples )
4057 int sampleCount = std::min( 3, vtxCount );
4059 auto vertexInBounds = [&](
size_t aVertexPos ) ->
bool
4063 return x >= bboxXMin && x <= bboxXMax && y >= bboxYMin && y <= bboxYMax;
4066 for(
int i = 0; i < sampleCount; i++ )
4068 size_t vp = vtxStart +
static_cast<size_t>( i ) * 8;
4070 if( !vertexInBounds( vp ) )
4074 size_t lastVp = vtxStart +
static_cast<size_t>( vtxCount - 1 ) * 8;
4076 return vertexInBounds( lastVp );
4079 auto headerHasZoneSectionShape = [&](
size_t aPos ) ->
bool
4081 if( aPos + 30 > aSearchEnd )
4085 int flags1 = data[aPos + 3];
4086 int flags3 = data[aPos + 5];
4087 int separator =
ReadInt3At( data, aPos + 18 );
4090 int vtxCount =
ReadInt3At( data, aPos + 27 );
4092 return fieldA >= 0 && fieldA <= 10000
4093 && fieldB >= -1 && fieldB <= 10000
4094 && flags1 <= 2 && flags3 <= 2
4096 && layer >= -10 && layer <= 100
4097 && vtxCount >= 3 && vtxCount <= MAX_VERTICES
4098 && aPos + 30 +
static_cast<size_t>( vtxCount ) * 8 <= aSearchEnd;
4101 auto parseZoneTrailer = [&](
DT_ZONE& aZone,
size_t aSearchStartPos,
size_t aSearchEndPos,
4102 int aZoneIndex ) ->
void
4110 static constexpr size_t TRAILER_LEN = 28;
4111 static constexpr size_t STYLE_BLOCK_LEN = 14;
4112 static constexpr int CACHED_RECORD_LEN = 23;
4114 if( aSearchEndPos <= aSearchStartPos || aSearchEndPos - aSearchStartPos < TRAILER_LEN )
4117 size_t lastStart = aSearchEndPos - TRAILER_LEN;
4119 for(
size_t trailerPos = lastStart + 1; trailerPos-- > aSearchStartPos; )
4122 int zeroInt4 =
ReadInt4At( data, trailerPos + 3 );
4123 int boardClr =
ReadInt4At( data, trailerPos + 7 );
4124 uint8_t islandR = data[trailerPos + 11];
4125 uint8_t islandI = data[trailerPos + 12];
4126 uint8_t islandC = data[trailerPos + 13];
4127 int zoneId =
ReadInt3At( data, trailerPos + 14 );
4128 uint8_t viaDir = data[trailerPos + 17];
4129 uint8_t smdSep = data[trailerPos + 18];
4130 int smdSpokeMode =
ReadInt3At( data, trailerPos + 19 );
4131 int smdSpokeW =
ReadInt4At( data, trailerPos + 22 );
4132 uint8_t ratMode = data[trailerPos + 26];
4133 uint8_t doneFlag = data[trailerPos + 27];
4135 if( lead < 0 || lead > 100000 || zeroInt4 != -
INT4_BIAS )
4138 if( boardClr < 0 || boardClr > MAX_REASONABLE_DIM )
4141 if( islandR > 1 || islandI > 1 || islandC > 1 )
4144 if( zoneId < 0 || zoneId > 100000 )
4147 if( viaDir > 1 || smdSep > 1 )
4150 if( smdSpokeMode < 0 || smdSpokeMode > 4 )
4153 if( smdSpokeW <= 0 || smdSpokeW > MAX_REASONABLE_DIM )
4162 size_t payloadStart = aSearchStartPos;
4164 if( aSearchStartPos + STYLE_BLOCK_LEN <= aSearchEndPos )
4166 int styleLead =
ReadInt3At( data, aSearchStartPos );
4167 int styleSpokeMode =
ReadInt3At( data, aSearchStartPos + 3 );
4168 int styleLineSpacing =
ReadInt4At( data, aSearchStartPos + 6 );
4169 int styleSpokeWidth =
ReadInt4At( data, aSearchStartPos + 10 );
4172 && styleSpokeMode >= 0 && styleSpokeMode <= 4
4173 && styleLineSpacing > 0 && styleLineSpacing <= MAX_REASONABLE_DIM
4174 && styleSpokeWidth > 0 && styleSpokeWidth <= MAX_REASONABLE_DIM )
4176 payloadStart += STYLE_BLOCK_LEN;
4180 int cachedBytes = 0;
4181 int cachedRecords = 0;
4184 if( trailerPos > payloadStart )
4186 cachedBytes =
static_cast<int>( trailerPos - payloadStart );
4188 if( cachedBytes % CACHED_RECORD_LEN == 0 )
4190 cachedRecords = cachedBytes / CACHED_RECORD_LEN;
4193 for(
int recIdx = 0; recIdx < cachedRecords; recIdx++ )
4195 size_t recPos = payloadStart +
static_cast<size_t>( recIdx ) * CACHED_RECORD_LEN;
4197 if( recPos + CACHED_RECORD_LEN > trailerPos )
4232 wxT(
"DipTrace: zone[%d] trailer off=0x%06zX regionsCounted=%d "
4233 "cachedBytes=%d cachedRecords=%d boardClr=%d "
4234 "islands=[%u,%u,%u] id=%d viaDirect=%u smdSeparate=%u "
4235 "smdSpokeMode=%d smdSpokeWidth=%d ratMode=%u done=%u" ),
4236 aZoneIndex, trailerPos, lead, cachedBytes, cachedRecords, boardClr,
4237 static_cast<unsigned int>( islandR ),
static_cast<unsigned int>( islandI ),
4238 static_cast<unsigned int>( islandC ), zoneId,
static_cast<unsigned int>( viaDir ),
4239 static_cast<unsigned int>( smdSep ), smdSpokeMode, smdSpokeW,
4240 static_cast<unsigned int>( ratMode ),
static_cast<unsigned int>( doneFlag ) );
4244 int zoneXMin = aZone.
outline[0].first;
4245 int zoneXMax = aZone.
outline[0].first;
4246 int zoneYMin = aZone.
outline[0].second;
4247 int zoneYMax = aZone.
outline[0].second;
4249 for(
const auto& p : aZone.
outline )
4251 zoneXMin = std::min( zoneXMin, p.first );
4252 zoneXMax = std::max( zoneXMax, p.first );
4253 zoneYMin = std::min( zoneYMin, p.second );
4254 zoneYMax = std::max( zoneYMax, p.second );
4258 std::array<int, 6> minVals = {
4262 std::array<int, 6> maxVals = minVals;
4263 std::array<int, 6> inXHits = { 0, 0, 0, 0, 0, 0 };
4264 std::array<int, 6> inYHits = { 0, 0, 0, 0, 0, 0 };
4265 std::array<int, 6> nonNegHits = { 0, 0, 0, 0, 0, 0 };
4266 std::map<int, int> field0Hist;
4267 std::map<int, int> field5Hist;
4268 int f0EqRegions = 0;
4269 int xEqualCount = 0;
4270 int yEqualCount = 0;
4271 int bothEqualCount = 0;
4275 std::array<int, 6> vals = {
4280 field0Hist[rec.
field0]++;
4281 field5Hist[rec.
field5]++;
4298 for(
size_t fi = 0; fi < vals.size(); fi++ )
4300 minVals[fi] = std::min( minVals[fi], vals[fi] );
4301 maxVals[fi] = std::max( maxVals[fi], vals[fi] );
4303 if( vals[fi] >= zoneXMin && vals[fi] <= zoneXMax )
4306 if( vals[fi] >= zoneYMin && vals[fi] <= zoneYMax )
4315 wxT(
"DipTrace: zone[%d] cached-range "
4316 "f0=[%d,%d] f1=[%d,%d] f2=[%d,%d] f3=[%d,%d] f4=[%d,%d] f5=[%d,%d]" ),
4317 aZoneIndex, minVals[0], maxVals[0], minVals[1], maxVals[1], minVals[2], maxVals[2],
4318 minVals[3], maxVals[3], minVals[4], maxVals[4], minVals[5], maxVals[5] );
4321 wxT(
"DipTrace: zone[%d] cached-hits "
4322 "xHits=[%d,%d,%d,%d,%d,%d] yHits=[%d,%d,%d,%d,%d,%d] "
4323 "nonNeg=[%d,%d,%d,%d,%d,%d]" ),
4324 aZoneIndex, inXHits[0], inXHits[1], inXHits[2], inXHits[3], inXHits[4], inXHits[5],
4325 inYHits[0], inYHits[1], inYHits[2], inYHits[3], inYHits[4], inYHits[5], nonNegHits[0],
4326 nonNegHits[1], nonNegHits[2], nonNegHits[3], nonNegHits[4], nonNegHits[5] );
4328 wxLogTrace(
traceDiptraceIo, wxT(
"DipTrace: zone[%d] cached-zone-bbox=[%d,%d,%d,%d]" ), aZoneIndex,
4329 zoneXMin, zoneXMax, zoneYMin, zoneYMax );
4331 auto histToString = [](
const std::map<int, int>& aHist ) -> wxString
4336 for(
const auto&
kv : aHist )
4342 out += wxString::Format( wxT(
"%d:%d" ),
kv.first,
kv.second );
4349 wxT(
"DipTrace: zone[%d] cached-hist f0={%s} f5={%s} "
4350 "f0EqRegions=%d xEq=%d yEq=%d bothEq=%d" ),
4351 aZoneIndex, histToString( field0Hist ), histToString( field5Hist ), f0EqRegions,
4352 xEqualCount, yEqualCount, bothEqualCount );
4356 for(
size_t ri = 0; ri < sampleCount; ri++ )
4359 wxLogTrace(
traceDiptraceIo, wxT(
"DipTrace: zone[%d] cached-rec[%zu]={%d,%d,%d,%d,%d,%d}" ),
4371 bool zoneViaPreamble =
false;
4373 auto findZoneFontPreambleDataStart = [&](
size_t aStart ) ->
size_t
4375 static const wxString fontNames[] = {
4376 wxT(
"Arial" ), wxT(
"Tahoma" ), wxT(
"Times New Roman" ),
4377 wxT(
"Courier New" ), wxT(
"Verdana" ), wxT(
"Calibri" )
4382 for(
const wxString& fontName : fontNames )
4384 size_t fontPos =
m_reader.FindString( fontName, aStart, aSearchEnd );
4394 if( bc < 0 || bc > 500 )
4396 fontPos =
m_reader.FindString( fontName, fontPos + 3, aSearchEnd );
4400 strEnd = fontPos + 3 + bc;
4404 uint16_t cc = (
static_cast<uint16_t
>( data[fontPos] ) << 8 )
4405 | data[fontPos + 1];
4406 strEnd = fontPos + 2 +
static_cast<size_t>( cc ) * 2;
4409 if( strEnd + 16 > aSearchEnd )
4413 int bold = data[strEnd + 3];
4418 if( fontSize >= 5 && fontSize <= 30 && bold <= 1
4419 && fontH > 0 && fontH < 10000000
4420 && fontW > 0 && fontW < 10000000
4423 bestDataStart = std::min( bestDataStart, strEnd + 16 );
4427 fontPos =
m_reader.FindString( fontName, strEnd, aSearchEnd );
4431 return bestDataStart;
4434 size_t zoneDataStart = findZoneFontPreambleDataStart( aSearchStart );
4438 size_t preambleHeaderStart = zoneDataStart + 3;
4440 if( preambleHeaderStart + 30 <= aSearchEnd
4441 && headerLooksPlausible( preambleHeaderStart,
true ) )
4443 zoneHeaderStart = preambleHeaderStart;
4444 zoneViaPreamble =
true;
4448 if( preambleHeaderStart + 30 <= aSearchEnd
4449 && headerHasZoneSectionShape( preambleHeaderStart ) )
4452 _(
"DipTrace import: invalid copper-pour zone header after font preamble "
4453 "at offset 0x%06zX." ),
4454 preambleHeaderStart ) );
4457 zoneDataStart = findZoneFontPreambleDataStart( zoneDataStart + 1 );
4461 for(
size_t scanPos = aSearchStart; zoneHeaderStart ==
NOT_FOUND
4462 && scanPos + 30 <= aSearchEnd; scanPos++ )
4464 if( headerLooksPlausible( scanPos,
true ) )
4466 zoneHeaderStart = scanPos;
4467 wxLogTrace(
traceDiptraceIo, wxT(
"DipTrace: zone section found by structural scan at 0x%06zX" ),
4480 zoneDataStart = findZoneFontPreambleDataStart( aSearchStart );
4484 size_t fallbackStart = zoneDataStart + 3;
4486 if( fallbackStart + 30 <= aSearchEnd
4487 && headerLooksPlausible( fallbackStart,
true ) )
4489 zoneHeaderStart = fallbackStart;
4490 zoneViaPreamble =
true;
4494 size_t fallbackEnd = std::min( aSearchEnd, zoneDataStart + 256 );
4496 for(
size_t scanPos = zoneDataStart; scanPos + 30 <= fallbackEnd; scanPos++ )
4498 if( headerLooksPlausible( scanPos,
true ) )
4500 zoneHeaderStart = scanPos;
4508 wxLogTrace(
traceDiptraceIo, wxT(
"DipTrace: zone section found by font fallback at 0x%06zX" ),
4517 size_t pos = zoneHeaderStart;
4519 for(
int zi = 0; zi < MAX_ZONES && pos + 30 < aSearchEnd; zi++ )
4534 int flags1 = data[pos + 3];
4535 int flags2 = data[pos + 4];
4536 int flags3 = data[pos + 5];
4539 int minimumArea =
ReadInt4At( data, pos + 14 );
4540 int separator =
ReadInt3At( data, pos + 18 );
4545 if( fieldA < 0 || fieldA > 10000 )
4548 if( fieldB < -1 || fieldB > 10000 )
4551 if( flags1 > 2 || flags3 > 2 )
4557 if( minWidth <= 0 || minWidth > MAX_REASONABLE_DIM )
4560 if( minimumArea < 0 || minimumArea > MAX_REASONABLE_DIM )
4566 if( layer < -10 || layer > 100 )
4569 if( vtxCount < 3 || vtxCount > MAX_VERTICES )
4572 size_t vtxStart = pos + 30;
4573 size_t vtxEnd = vtxStart +
static_cast<size_t>( vtxCount ) * 8;
4575 if( vtxEnd > aSearchEnd )
4581 zone.
fillMode =
static_cast<uint8_t
>( flags1 );
4582 zone.
rawFlag2 =
static_cast<uint8_t
>( flags2 );
4588 zone.
outline.reserve( vtxCount );
4590 bool validOutline =
true;
4592 for(
int vi = 0; vi < vtxCount; vi++ )
4594 size_t vp = vtxStart +
static_cast<size_t>( vi ) * 8;
4598 if( x < bboxXMin || x > bboxXMax || y < bboxYMin || y > bboxYMax )
4600 validOutline =
false;
4604 zone.
outline.emplace_back( x, y );
4610 wxString zoneNetName;
4614 if( net.index == fieldB )
4616 zoneNetName = net.name;
4622 minimumArea, separator, layer, fieldB, vtxCount, zoneNetName );
4624 m_zones.push_back( std::move( zone ) );
4630 if( pos + 3 > aSearchEnd )
4635 if( fillSegCount < 0 || fillSegCount > 500000 )
4640 wxLogTrace(
traceDiptraceIo, wxT(
"DipTrace: zone[%d] fill-segments off=0x%06zX count=%d" ), zi, pos,
4644 pos += 3 +
static_cast<size_t>( fillSegCount ) * 19;
4647 if( pos + 3 > aSearchEnd )
4652 if( fillPolyCount < 0 || fillPolyCount > 50000 )
4657 wxLogTrace(
traceDiptraceIo, wxT(
"DipTrace: zone[%d] fill-polys off=0x%06zX count=%d" ), zi, pos,
4663 for(
int pi = 0; pi < fillPolyCount; pi++ )
4665 if( pos + 3 > aSearchEnd )
4673 if( pvc < 0 || pvc > MAX_VERTICES )
4679 pos += 3 +
static_cast<size_t>( pvc ) * 8 + 3;
4681 if( pos > aSearchEnd )
4693 if( pos + 14 <= aSearchEnd )
4697 int lineSpacing =
ReadInt4At( data, pos + 6 );
4698 int spokeWidth =
ReadInt4At( data, pos + 10 );
4701 && spokeMode >= 0 && spokeMode <= 4
4702 && lineSpacing > 0 && lineSpacing <= MAX_REASONABLE_DIM
4703 && spokeWidth > 0 && spokeWidth <= MAX_REASONABLE_DIM )
4712 wxT(
"DipTrace: zone[%d] style lead=%d spokeMode=%d lineSpacing=%d spokeWidth=%d" ), zi,
4713 styleLead, spokeMode, lineSpacing, spokeWidth );
4721 bool foundNext =
false;
4722 size_t scanStart = pos;
4724 for(
size_t testPos = pos; testPos + 30 <= aSearchEnd; testPos++ )
4726 if( !headerLooksPlausible( testPos,
false ) )
4729 if( !headerLooksPlausible( testPos,
true ) )
4737 if( foundNext && pos > scanStart )
4739 parseZoneTrailer( parsedZone, scanStart, pos, zi );
4745 parseZoneTrailer( parsedZone, scanStart, aSearchEnd, zi );
4759 if( !
m_zones.empty() && !zoneViaPreamble )
4773 int copperCount =
static_cast<int>(
m_layers.size() );
4775 if( copperCount < 2 )
4779 if( copperCount % 2 != 0 )
4782 m_board->SetCopperLayerCount( copperCount );
4799 enabledLayers.
set( kiLayer );
4801 if( !layer.name.empty() )
4802 m_board->SetLayerName( kiLayer, layer.name );
4806 m_board->SetEnabledLayers( enabledLayers );
4848 std::vector<VECTOR2I> pts;
4849 std::vector<uint8_t> arcs;
4856 arcs.push_back( v.arc );
4863 while( i < n && arcs[i] == 1 )
4869 size_t startIndex = i;
4871 size_t maxSteps = n * 4;
4881 std::vector<OUTLINE_PRIM> outlinePrims;
4882 outlinePrims.reserve( n );
4888 prim.start = aStart;
4890 outlinePrims.push_back( prim );
4897 prim.start = aStart;
4900 outlinePrims.push_back( prim );
4903 while( i < n && steps++ < maxSteps )
4905 size_t next = ( i + 1 ) % n;
4907 if( arcs[
next] == 1 )
4909 size_t afterArc = (
next + 1 ) % n;
4916 long long cross =
static_cast<long long>(
v1.x ) *
static_cast<long long>(
v2.y )
4917 -
static_cast<long long>(
v1.y ) *
static_cast<long long>(
v2.x );
4918 bool degenerateArc = ( start == mid ) || ( mid ==
end ) || ( start ==
end )
4919 || ( std::llabs( cross ) < 100 );
4927 addArc( start, mid,
end );
4940 if( i == startIndex )
4944 if( steps >= maxSteps )
4946 wxLogWarning(
_(
"DipTrace: outline traversal aborted after %zu steps (%zu vertices)" ),
4950 outlinePrims.clear();
4952 for(
size_t v = 0; v < n; v++ )
4956 for(
const OUTLINE_PRIM& prim : outlinePrims )
4990 for(
int i = 0; i < 4; i++ )
4996 seg->
SetEnd( corners[( i + 1 ) % 4] );
5014 if(
chain.netIndex < 0 )
5018 anchors.reserve( anchors.size() +
chain.nodes.size() );
5039 for(
size_t padIdx = 0; padIdx < aComp.
pads.size(); padIdx++ )
5045 pad->SetPosition( padLocal );
5060 std::vector<VECTOR2I> polyPts;
5068 else if( dtPad.
style == 2 )
5088 if( drillW > 0 && drillH <= 0 )
5091 if( drillH > 0 && drillW <= 0 )
5104 if( drillW > 0 && drillH > 0 )
5108 pad->SetDrillSize( drill );
5110 if( drillW == drillH )
5125 orientClass = aComp.
pads[padIdx - 1].orientClass;
5127 if( orientClass == 1 )
5141 PAD* holePad =
new PAD( footprint );
5164 bool hasAssemblyOutline =
false;
5170 hasAssemblyOutline =
true;
5189 std::swap( scaleX, scaleY );
5195 int minSX = INT_MAX, maxSX = INT_MIN;
5196 int minSY = INT_MAX, maxSY = INT_MIN;
5200 minSX = std::min( { minSX, s.
x1, s.
x2 } );
5201 maxSX = std::max( { maxSX, s.
x1, s.
x2 } );
5202 minSY = std::min( { minSY, s.
y1, s.
y2 } );
5203 maxSY = std::max( { maxSY, s.
y1, s.
y2 } );
5206 int shapeXRange = maxSX - minSX;
5207 int shapeYRange = maxSY - minSY;
5209 if( shapeXRange > 0 && shapeYRange > 0 )
5211 double physX =
static_cast<double>( shapeXRange ) *
std::abs( scaleX )
5213 double physY =
static_cast<double>( shapeYRange ) *
std::abs( scaleY )
5215 double aspect = physX / physY;
5217 if( aspect < 0.2 || aspect > 5.0 )
5218 std::swap( scaleX, scaleY );
5223 static constexpr int DEFAULT_LINE_WIDTH_DT = 3000;
5225 auto scaleShapeCoord = [&](
int aShapeVal,
int aBboxDim ) ->
int
5228 static_cast<int>(
static_cast<int64_t
>( aShapeVal ) * aBboxDim
5257 switch( dtShape.
layer )
5266 default: shapeLayer =
F_SilkS;
break;
5271 VECTOR2I p1( scaleShapeCoord( dtShape.
x1, scaleX ),
5272 scaleShapeCoord( dtShape.
y1, scaleY ) );
5273 VECTOR2I p2( scaleShapeCoord( dtShape.
x2, scaleX ),
5274 scaleShapeCoord( dtShape.
y2, scaleY ) );
5287 for(
int i = 0; i < 4; i++ )
5294 edge->
SetEnd( corners[( i + 1 ) % 4] );
5310 int radius = ( p2 - p1 ).EuclideanNorm() / 2;
5317 scaleShapeCoord( dtShape.
midY, scaleY ) );
5322 int64_t cross =
static_cast<int64_t
>( p2.
x - p1.
x ) * ( mid.
y - p1.
y )
5323 -
static_cast<int64_t
>( p2.
y - p1.
y ) * ( mid.
x - p1.
x );
5324 int64_t chordSq =
static_cast<int64_t
>( p2.
x - p1.
x ) * ( p2.
x - p1.
x )
5325 +
static_cast<int64_t
>( p2.
y - p1.
y ) * ( p2.
y - p1.
y );
5327 if( chordSq == 0 ||
std::abs( cross ) * 1000 < chordSq )
5365 double orientationDeg;
5373 if( orientationDeg < 0.0 )
5374 orientationDeg += 360.0;
5379 double nearest90 = std::round( orientationDeg / 90.0 ) * 90.0;
5381 if(
std::abs( orientationDeg - nearest90 ) < 0.02 )
5382 orientationDeg = std::fmod( nearest90, 360.0 );
5388 :
static_cast<int>( std::lround(
5390 orientationDeg = ( ( orientationQuarterTurns % 4 ) + 4 ) % 4 * 90.0;
5395 wxLogTrace(
traceDiptraceIo, wxT(
"DipTrace: fp-orient ref=%s pat=%s qturn=%d hasQ=%d exact=%d chosen=%.2f" ),
5401 if( aComp.
layer == 1 )
5428 if( aText.
text.empty() )
5438 text->SetLayer( textLayer );
5442 int cx =
ToKiCadCoord(
static_cast<int>( (
static_cast<int64_t
>( aText.
x1 ) + aText.
x2 ) / 2 ) );
5443 int cy =
ToKiCadCoord(
static_cast<int>( (
static_cast<int64_t
>( aText.
y1 ) + aText.
y2 ) / 2 ) );
5447 ToKiCadCoord(
static_cast<int>(
static_cast<int64_t
>( aText.
y2 ) - aText.
y1 ) ) );
5465 if( aDipTraceNetIndex < 0 )
5479 if( aDipTraceNetIndex < 0 )
5498 wxString netName = net.name;
5500 if( netName.empty() )
5501 netName = wxString::Format( wxS(
"DipTrace_Net_%d" ), net.index );
5511 if( net.index >= 0 )
5515 if( !inserted && it->second != netinfo )
5517 wxLogWarning(
_(
"DipTrace: net index %d maps to multiple net names (%s, %s)" ),
5518 net.index, it->second->GetNetname(), netinfo->
GetNetname() );
5523 if( !dtInserted && dtIt->second != &net )
5525 wxLogWarning(
_(
"DipTrace: duplicate net metadata for net index %d" ),
5537 int missingGeometry = 0;
5538 std::set<std::tuple<int, int, int>> createdKeys;
5540 auto hasBoardViaAt = [&](
const VECTOR2I& aPos,
int aNetCode ) ->
bool
5549 if(
via->GetPosition() == aPos &&
via->GetNetCode() == aNetCode )
5558 if( !
comp.isStandaloneVia )
5561 int viaX =
comp.positionX;
5562 int viaY =
comp.positionY;
5563 int viaOuter = std::max(
comp.bboxWidth,
comp.padWidthHint );
5564 int viaDrill = std::max(
comp.drillWidthHint,
comp.drillHeightHint );
5567 if( !
comp.pads.empty() )
5572 netIndex =
pad.netIndex;
5575 viaOuter =
pad.width;
5577 if(
pad.drillWidth > 0 )
5578 viaDrill =
pad.drillWidth;
5579 else if(
pad.drillHeight > 0 )
5580 viaDrill =
pad.drillHeight;
5583 if( viaOuter <= 0 || viaDrill <= 0 )
5592 std::tuple<int, int, int> key( viaPos.
x, viaPos.
y, netCode );
5594 if( createdKeys.find( key ) != createdKeys.end() || hasBoardViaAt( viaPos, netCode ) )
5601 via->SetPosition( viaPos );
5611 createdKeys.insert( key );
5615 if( created > 0 || duplicates > 0 || missingGeometry > 0 )
5618 wxT(
"DipTrace: created %d standalone vias from component records "
5619 "(duplicates=%d, missingGeometry=%d)" ),
5620 created, duplicates, missingGeometry );
5629 int missingChainNets = 0;
5630 int nonCopperTrackSkips = 0;
5631 int nonCopperViaSkips = 0;
5632 int inferredLayerChangeVias = 0;
5633 int explicitViaStyleVias = 0;
5634 int duplicateViaSkips = 0;
5635 int crossNetViaSkips = 0;
5643 bool operator==(
const POS_NET_KEY& aOther )
const
5645 return net == aOther.net && x == aOther.x && y == aOther.y;
5651 size_t operator()(
const POS_NET_KEY& aKey )
const
5653 size_t h1 =
static_cast<size_t>(
static_cast<uint32_t
>( aKey.net ) );
5654 size_t h2 =
static_cast<size_t>(
static_cast<uint32_t
>( aKey.x ) );
5655 size_t h3 =
static_cast<size_t>(
static_cast<uint32_t
>( aKey.y ) );
5656 return h1 ^ ( h2 * 0x9e3779b1U ) ^ ( h3 * 0x85ebca6bU );
5660 auto makeKey = [](
int aNet,
int aX,
int aY ) -> POS_NET_KEY
5674 bool operator==(
const POS_KEY& aOther )
const
5676 return x == aOther.x && y == aOther.y;
5682 size_t operator()(
const POS_KEY& aKey )
const
5684 size_t h1 =
static_cast<size_t>(
static_cast<uint32_t
>( aKey.x ) );
5685 size_t h2 =
static_cast<size_t>(
static_cast<uint32_t
>( aKey.y ) );
5686 return ( h1 * 0x9e3779b1U ) ^ ( h2 * 0x85ebca6bU );
5690 auto makePosKey = [](
int aX,
int aY ) -> POS_KEY
5698 auto resolveCopperLayer = [&](
int aDipTraceLayer ) ->
PCB_LAYER_ID
5703 layer =
MapLayer( aDipTraceLayer );
5711 std::unordered_map<POS_NET_KEY, std::set<int>, POS_NET_HASH> layersByNetPos;
5712 std::unordered_set<POS_NET_KEY, POS_NET_HASH> createdVias;
5713 std::unordered_map<POS_KEY, std::set<int>, POS_HASH> netsByPos;
5721 if(
chain.netIndex >= 0 )
5722 netsByPos[makePosKey( node.
x, node.
y )].insert(
chain.netIndex );
5726 std::shared_ptr<NETCLASS> defNetclass =
5727 m_board->GetDesignSettings().m_NetSettings->GetDefaultNetclass();
5734 if( !net &&
chain.netIndex >= 0 )
5737 for(
size_t i = 0; i + 1 <
chain.nodes.size(); i++ )
5752 nonCopperTrackSkips++;
5766 for(
size_t nodeIdx = 0; nodeIdx <
chain.nodes.size(); nodeIdx++ )
5770 auto posIt = layersByNetPos.find( key );
5771 bool explicitVia = node.
hasVia;
5777 auto netPosIt = netsByPos.find( makePosKey( node.
x, node.
y ) );
5779 if( netPosIt != netsByPos.end() && netPosIt->second.size() > 1 )
5785 if( createdVias.find( key ) != createdVias.end() )
5787 duplicateViaSkips++;
5791 if( explicitViaStyle )
5792 explicitViaStyleVias++;
5812 styleViaLayerA = l1;
5813 styleViaLayerB = l2;
5817 styleViaLayerA = l2;
5818 styleViaLayerB = l1;
5825 if( viaOuterDiam <= 0 )
5828 if( viaDrillDiam <= 0 )
5832 int viaWidthIU = ( viaOuterDiam > 0 ) ?
ToKiCadCoord( viaOuterDiam ) : 0;
5833 int viaDrillIU = ( viaDrillDiam > 0 ) ?
ToKiCadCoord( viaDrillDiam ) : 0;
5835 if( viaWidthIU <= 0 && defNetclass )
5836 viaWidthIU = defNetclass->GetViaDiameter();
5838 if( viaDrillIU <= 0 && defNetclass )
5839 viaDrillIU = defNetclass->GetViaDrill();
5841 if( viaWidthIU <= 0 )
5847 via->SetWidth( viaWidthIU );
5849 if( viaDrillIU > 0 )
5850 via->SetDrill( viaDrillIU );
5852 auto ordinalToLayer = [&](
size_t aOrdinal ) ->
PCB_LAYER_ID
5854 int copperCount =
m_board->GetCopperLayerCount();
5859 if( aOrdinal >=
static_cast<size_t>( copperCount - 1 ) )
5862 int innerIdx =
static_cast<int>( aOrdinal ) - 1;
5864 if( innerIdx < 0 || innerIdx > 29 )
5870 bool haveLayerRange =
false;
5874 auto accumulateDipLayer = [&](
int aDipLayer )
5883 if( !haveLayerRange )
5887 haveLayerRange =
true;
5891 minOrd = std::min( minOrd, ord );
5892 maxOrd = std::max( maxOrd, ord );
5896 accumulateDipLayer( node.
layer );
5899 accumulateDipLayer(
chain.nodes[nodeIdx - 1].layer );
5901 if( nodeIdx + 1 <
chain.nodes.size() )
5902 accumulateDipLayer(
chain.nodes[nodeIdx + 1].layer );
5904 if( posIt != layersByNetPos.end() )
5906 for(
int dipLayer : posIt->second )
5907 accumulateDipLayer( dipLayer );
5915 viaLayerA = styleViaLayerA;
5916 viaLayerB = styleViaLayerB;
5918 else if( haveLayerRange && minOrd < maxOrd )
5930 via->SetLayerPair( viaLayerA, viaLayerB );
5932 if( viaLayerA ==
F_Cu && viaLayerB ==
B_Cu )
5934 else if( viaLayerA ==
F_Cu || viaLayerB ==
B_Cu )
5943 createdVias.insert( key );
5949 wxT(
"DipTrace: created %d tracks and %d vias (%d chains with unresolved nets, "
5950 "%d non-copper tracks skipped, %d non-copper vias skipped, "
5951 "%d style vias, %d inferred layer-change vias, %d duplicate vias skipped, "
5952 "%d cross-net vias skipped)" ),
5953 trackCount, viaCount, missingChainNets, nonCopperTrackSkips, nonCopperViaSkips, explicitViaStyleVias,
5954 inferredLayerChangeVias, duplicateViaSkips, crossNetViaSkips );
5960 std::unordered_map<int, int> zoneSpokeModeByDipNet;
5961 std::unordered_set<int> zoneSpokeModeConflicts;
5965 if( dtZone.outline.size() < 3 )
5973 zoneLayer =
MapLayer( dtZone.layer );
5981 if( dtZone.clearance > 0 )
5984 if( dtZone.minWidth > 0 )
5991 if( dtZone.spokeWidth > 0 )
5994 if( dtZone.spokeMode == 0 )
5998 else if( dtZone.spokeMode > 0 && dtZone.smdSpokeMode == 0 )
6005 else if( dtZone.spokeMode > 0 )
6016 if( dtZone.islandInternal || dtZone.islandConnection )
6020 else if( dtZone.islandRegion )
6024 if( dtZone.minimumArea > 0 )
6026 long long minIslandLinearIU =
static_cast<long long>(
ToKiCadCoord( dtZone.minimumArea ) );
6041 for(
const auto& [x, y] : dtZone.outline )
6051 if( dtZone.netIndex >= 0 && dtZone.spokeMode >= 0 )
6053 auto [it, inserted] = zoneSpokeModeByDipNet.emplace( dtZone.netIndex, dtZone.spokeMode );
6055 if( !inserted && it->second != dtZone.spokeMode )
6056 zoneSpokeModeConflicts.insert( dtZone.netIndex );
6062 std::unordered_map<int, int> spokeModeByNetCode;
6064 for(
const auto& [dipNetIdx, spokeMode] : zoneSpokeModeByDipNet )
6066 if( zoneSpokeModeConflicts.count( dipNetIdx ) )
6074 spokeModeByNetCode[netIt->second->GetNetCode()] = spokeMode;
6077 int adjustedPadThermalAngles = 0;
6081 for(
PAD*
pad : fp->Pads() )
6086 auto modeIt = spokeModeByNetCode.find(
pad->GetNetCode() );
6088 if( modeIt == spokeModeByNetCode.end() )
6091 int spokeMode = modeIt->second;
6093 if( spokeMode == 1 || spokeMode == 4 )
6096 adjustedPadThermalAngles++;
6098 else if( spokeMode == 2 || spokeMode == 3 )
6101 adjustedPadThermalAngles++;
6106 if( adjustedPadThermalAngles > 0 )
6108 wxLogTrace(
traceDiptraceIo, wxT(
"DipTrace: applied thermal spoke angle overrides to %d pads" ),
6109 adjustedPadThermalAngles );
6136 planeOutline.
Append( x1, y1 );
6137 planeOutline.
Append( x2, y1 );
6138 planeOutline.
Append( x2, y2 );
6139 planeOutline.
Append( x1, y2 );
6147 if( layer.type != 1 || layer.planeNetIndex < 0 )
6160 wxT(
"DipTrace: plane layer %d references unresolved net index %d; skipping" ),
6161 layer.index, layer.planeNetIndex );
6184 wxT(
"DipTrace: synthesized plane zone on layer %d net '%s' (dt net %d)" ),
6185 layer.index, netinfo->
GetNetname(), layer.planeNetIndex );
6195 int edgeClearanceIU = 0;
6199 std::map<wxString, bool> viaDirectNets;
6203 if( zone.boardClearance > 0 )
6204 edgeClearanceIU = std::max( edgeClearanceIU,
ToKiCadCoord( zone.boardClearance ) );
6206 if( zone.viaDirect && zone.netIndex >= 0 )
6210 if( !net->GetNetname().IsEmpty() )
6211 viaDirectNets[net->GetNetname()] =
true;
6216 if( edgeClearanceIU <= 0 && viaDirectNets.empty() )
6217 return wxEmptyString;
6219 wxString rules = wxT(
"(version 1)\n" );
6221 if( edgeClearanceIU > 0 )
6225 rules += wxString::Format( wxT(
"\n(rule \"DipTrace zone board clearance\"\n" )
6226 wxT(
" (condition \"A.Type == 'Zone'\")\n" )
6227 wxT(
" (constraint edge_clearance (min %smm)))\n" ),
6231 for(
const auto& [netName, direct] : viaDirectNets )
6234 wxString escaped = netName;
6235 escaped.Replace( wxT(
"'" ), wxT(
"\\'" ) );
6237 rules += wxString::Format( wxT(
"\n(rule \"DipTrace via direct %s\"\n" )
6238 wxT(
" (condition \"A.Type == 'Via' && A.NetName == '%s'\")\n" )
6239 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
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