KiCad PCB EDA Suite
Loading...
Searching...
No Matches
orcad_cache.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * Based on the dsn2kicad reference implementation and on OrCAD file format
7 * documentation from the OpenOrCadParser project (MIT licensed).
8 *
9 * This program is free software: you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by the
11 * Free Software Foundation, either version 3 of the License, or (at your
12 * option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 */
22
24
25#include <algorithm>
26#include <array>
27#include <utility>
28
29#include <ki_exception.h>
30
31
32namespace
33{
34
35bool isPrimType( int aType )
36{
37 switch( aType )
38 {
39 case ORCAD_PRIM_RECT:
40 case ORCAD_PRIM_LINE:
41 case ORCAD_PRIM_ARC:
50 return true;
51 default:
52 return false;
53 }
54}
55
56
57bool isSymbolType( int aTypeId )
58{
59 switch( aTypeId )
60 {
69 return true;
70 default:
71 return false;
72 }
73}
74
75
76// Struct type bytes seen in cache streams; validates backtracked prefix-chain candidates.
77bool isKnownStructType( uint8_t aType )
78{
79 static const std::array<bool, 256> known = []()
80 {
81 std::array<bool, 256> table{};
82
83 for( int t : { 2, 4, 6, 9, 10, 11, 12, 13, 16, 20, 21, 23, 24, 26, 27, 29, 31, 32, 33,
84 34, 35, 37, 38, 39, 48, 49, 52, 53, 55, 56, 57, 58, 59, 60, 61, 62, 64,
85 65, 66, 67, 68, 69, 75, 76, 77, 78, 82, 88, 89, 91, 98, 103 } )
86 {
87 table[t] = true;
88 }
89
90 return table;
91 }();
92
93 return known[aType];
94}
95
96
97std::optional<ORCAD_PRIMITIVE> readPrimitiveBody( ORCAD_STREAM& aStream, int aType );
98
99
100// Nested prefix-framed vector graphic inside library parts.
101std::optional<ORCAD_PRIMITIVE> readSymbolVector( ORCAD_STREAM& aStream )
102{
103 // Type bytes (48,48) consumed; own prefix chain starts at second, so rewind one byte.
104 aStream.Seek( aStream.GetOffset() - 1 );
105
106 ORCAD_STRUCT_READER reader( aStream );
107 ORCAD_PREFIXES pfx = reader.ReadPrefixes();
108
111 group.x1 = aStream.ReadI16();
112 group.y1 = aStream.ReadI16();
113
114 uint16_t count = aStream.ReadU16();
115
116 for( uint16_t i = 0; i < count; i++ )
117 {
118 // Nested prims use 3-byte prefix: type, 0x00, type.
119 int t = aStream.ReadU8();
120 aStream.ExpectByte( 0x00, wxS( "symbol vector prim pad" ) );
121 int t2 = aStream.ReadU8();
122
123 if( t != t2 || !isPrimType( t ) )
124 THROW_IO_ERROR( wxS( "symbol vector prim prefix mismatch" ) );
125
126 std::optional<ORCAD_PRIMITIVE> child;
127
128 if( t == ORCAD_PRIM_SYMBOL_VECTOR )
129 {
130 aStream.Seek( aStream.GetOffset() - 1 );
131 child = readSymbolVector( aStream );
132 }
133 else
134 {
135 child = readPrimitiveBody( aStream, t );
136 }
137
138 if( child )
139 group.children.push_back( std::move( *child ) );
140 }
141
142 aStream.ReadLzt(); // vector name
143
144 if( pfx.end != 0 )
145 aStream.Seek( std::max( aStream.GetOffset(), pfx.end ) );
146
147 return group;
148}
149
150std::optional<ORCAD_PRIMITIVE> readPrimitiveBody( ORCAD_STREAM& aStream, int t1 )
151{
152 size_t start = aStream.GetOffset();
153 size_t size = aStream.ReadU32();
154
155 // CommentText uses exclusive byteLength in all eras.
156 if( t1 == ORCAD_PRIM_COMMENT_TEXT )
157 size += 8;
158
159 size_t end = start + size;
160
161 // Two byteLength conventions, sometimes mixed in one file: modern counts u32 size + 4-byte pad
162 // and is followed by a preamble; legacy excludes those 8 bytes. Detect per record via preamble magic.
163 if( t1 != ORCAD_PRIM_COMMENT_TEXT && !aStream.HasPreambleAt( end ) )
164 end += 8; // legacy exclusive-length record
165
166 static const uint8_t pad[4] = { 0x00, 0x00, 0x00, 0x00 };
167 aStream.Expect( pad, 4, wxS( "primitive pad" ) );
168
169 size_t clen = end - start;
170
171 std::optional<ORCAD_PRIMITIVE> prim;
172
173 if( t1 == ORCAD_PRIM_RECT || t1 == ORCAD_PRIM_ELLIPSE )
174 {
177 p.x1 = aStream.ReadI32();
178 p.y1 = aStream.ReadI32();
179 p.x2 = aStream.ReadI32();
180 p.y2 = aStream.ReadI32();
181
182 if( clen >= 32 )
183 {
184 p.lineStyle = aStream.ReadU32();
185 p.lineWidth = aStream.ReadU32();
186 }
187
188 if( clen >= 40 )
189 {
190 p.fillStyle = aStream.ReadU32();
191 p.hatchStyle = aStream.ReadU32();
192 }
193 prim = std::move( p );
194 }
195 else if( t1 == ORCAD_PRIM_LINE )
196 {
199 p.x1 = aStream.ReadI32();
200 p.y1 = aStream.ReadI32();
201 p.x2 = aStream.ReadI32();
202 p.y2 = aStream.ReadI32();
203
204 if( clen >= 32 )
205 {
206 p.lineStyle = aStream.ReadU32();
207 p.lineWidth = aStream.ReadU32();
208 }
209
210 prim = std::move( p );
211 }
212 else if( t1 == ORCAD_PRIM_ARC )
213 {
216 p.x1 = aStream.ReadI32();
217 p.y1 = aStream.ReadI32();
218 p.x2 = aStream.ReadI32();
219 p.y2 = aStream.ReadI32();
220
221 ORCAD_POINT arcStart;
222 arcStart.x = aStream.ReadI32();
223 arcStart.y = aStream.ReadI32();
224
225 ORCAD_POINT arcEnd;
226 arcEnd.x = aStream.ReadI32();
227 arcEnd.y = aStream.ReadI32();
228
229 p.start = arcStart;
230 p.end = arcEnd;
231
232 if( clen >= 48 )
233 {
234 p.lineStyle = aStream.ReadU32();
235 p.lineWidth = aStream.ReadU32();
236 }
237
238 prim = std::move( p );
239 }
240 else if( t1 == ORCAD_PRIM_POLYGON || t1 == ORCAD_PRIM_POLYLINE || t1 == ORCAD_PRIM_BEZIER )
241 {
242 // Point count found deterministically: must reconcile with stored byteLength under one convention.
243 size_t body = start + 8;
244 const uint8_t* data = aStream.Data();
245
246 std::vector<size_t> candidates;
247
248 if( t1 == ORCAD_PRIM_POLYGON )
249 candidates = { 16, 8, 0 };
250 else
251 candidates = { 8, 0 };
252
253 bool found = false;
254 size_t off = 0;
255
256 for( size_t candidate : candidates )
257 {
258 if( body + candidate + 2 > aStream.Size() )
259 continue;
260
261 size_t n = static_cast<size_t>( data[body + candidate] )
262 | static_cast<size_t>( data[body + candidate + 1] ) << 8;
263 size_t need = 8 + candidate + 2 + 4 * n;
264
265 if( size == need )
266 {
267 end = start + size;
268 off = candidate;
269 found = true;
270 break;
271 }
272
273 if( size == need - 8 )
274 {
275 end = start + size + 8;
276 off = candidate;
277 found = true;
278 break;
279 }
280 }
281
282 if( !found )
283 {
284 THROW_IO_ERROR( wxString::Format( wxS( "poly primitive at 0x%zx: no consistent "
285 "point count for size %zu" ),
286 start, size ) );
287 }
288
290
291 if( off >= 8 )
292 {
293 aStream.Seek( body );
294 p.lineStyle = aStream.ReadU32();
295 p.lineWidth = aStream.ReadU32();
296
297 if( t1 == ORCAD_PRIM_POLYGON && off >= 16 )
298 {
299 p.fillStyle = aStream.ReadU32();
300 p.hatchStyle = aStream.ReadU32();
301 }
302 }
303
304 aStream.Seek( body + off );
305
306 uint16_t pointCount = aStream.ReadU16();
307
308 if( t1 == ORCAD_PRIM_POLYGON )
310 else if( t1 == ORCAD_PRIM_POLYLINE )
312 else
314
315 for( uint16_t i = 0; i < pointCount; i++ )
316 {
317 ORCAD_POINT pt;
318 pt.y = aStream.ReadI16();
319 pt.x = aStream.ReadI16();
320 p.points.push_back( pt );
321 }
322
323 prim = std::move( p );
324 }
325 else if( t1 == ORCAD_PRIM_COMMENT_TEXT )
326 {
329 p.x1 = aStream.ReadI32();
330 p.y1 = aStream.ReadI32();
331 p.x2 = aStream.ReadI32();
332 p.y2 = aStream.ReadI32();
333 aStream.ReadI32(); // duplicate corner x
334 aStream.ReadI32(); // duplicate corner y
335 p.fontIdx = aStream.ReadU16();
336 aStream.Skip( 2 );
337 p.text = aStream.ReadLzt();
338 prim = std::move( p );
339 }
340 else if( t1 == ORCAD_PRIM_BITMAP )
341 {
344 p.x1 = aStream.ReadI32();
345 p.y1 = aStream.ReadI32();
346 p.x2 = aStream.ReadI32();
347 p.y2 = aStream.ReadI32();
348 aStream.Skip( 8 ); // x1, y1 duplicate corner
349 aStream.Skip( 8 ); // pixel width/height
350
351 uint32_t dataSize = aStream.ReadU32();
352
353 // Payload past record end = malformed bitmap; skip whole record.
354 if( aStream.GetOffset() + static_cast<size_t>( dataSize ) <= end )
355 {
356 p.data = aStream.ReadBytes( dataSize );
357 prim = std::move( p );
358 }
359 }
360 else if( t1 == ORCAD_PRIM_OLE_IMAGE )
361 {
364 p.x1 = aStream.ReadI32();
365 p.y1 = aStream.ReadI32();
366 p.x2 = aStream.ReadI32();
367 p.y2 = aStream.ReadI32();
368 aStream.Skip( 16 ); // crop/original-extent values
369
370 // OLE compound-document payload fills rest of record.
371 size_t from = std::min( aStream.GetOffset(), aStream.Size() );
372 size_t to = std::min( end, aStream.Size() );
373
374 if( to > from )
375 p.data.assign( aStream.Data() + from, aStream.Data() + to );
376
377 prim = std::move( p );
378 }
379
380 if( aStream.GetOffset() > end )
381 {
382 THROW_IO_ERROR( wxString::Format( wxS( "primitive type %d overran (0x%zx > 0x%zx)" ), t1,
383 aStream.GetOffset(), end ) );
384 }
385
386 aStream.Seek( end );
388 return prim;
389}
390
391} // namespace
392
393
394std::optional<ORCAD_PRIMITIVE> OrcadReadPrimitive( ORCAD_STREAM& aStream )
395{
396 int t1 = aStream.ReadU8();
397 int t2 = aStream.ReadU8();
398
399 if( t1 != t2 || !isPrimType( t1 ) )
400 {
402 wxString::Format( wxS( "bad primitive prefix %d/%d at 0x%zx" ), t1, t2, aStream.GetOffset() - 2 ) );
403 }
404
405 if( t1 == ORCAD_PRIM_SYMBOL_VECTOR )
406 return readSymbolVector( aStream );
407
408 return readPrimitiveBody( aStream, t1 );
409}
410
411
412std::optional<ORCAD_SYMBOL_PIN> OrcadReadSymbolPin( ORCAD_STRUCT_READER& aReader )
413{
414 ORCAD_STREAM& stream = aReader.Stream();
415
416 // Single 0x00 instead of prefix chain = skipped pin slot.
417 if( stream.PeekU8() == 0x00 )
418 {
419 stream.Skip( 1 );
420 return std::nullopt;
421 }
422
423 ORCAD_PREFIXES pfx = aReader.ReadPrefixes();
424
426 THROW_IO_ERROR( wxString::Format( wxS( "expected symbol pin, got type %d" ), pfx.typeId ) );
427
429 pin.name = stream.ReadLzt();
430 pin.startX = stream.ReadI32();
431 pin.startY = stream.ReadI32();
432 pin.hotptX = stream.ReadI32();
433 pin.hotptY = stream.ReadI32();
434 pin.shapeBits = stream.ReadU16();
435 stream.Skip( 2 ); // uninitialized junk
436
437 uint32_t portType = stream.ReadU32();
438 pin.portType = portType <= 7 ? static_cast<ORCAD_PORT_TYPE>( portType )
440
441 // Remaining body is junk/zeros; pin ends at outer stop.
442 if( pfx.end != 0 && pfx.end >= stream.GetOffset() )
443 stream.Seek( pfx.end );
444
445 return pin;
446}
447
448
450 bool aWithPins )
451{
452 ORCAD_STREAM& stream = aReader.Stream();
453
454 std::vector<size_t> stops = aPrefixes.stops;
455 std::sort( stops.begin(), stops.end() );
456
458 sym.typeId = aPrefixes.typeId;
459 sym.name = stream.ReadLzt();
460 sym.sourceLib = stream.ReadLzt();
461 sym.props = aReader.PropsDict( aPrefixes );
462
463 sym.color = static_cast<int>( stream.ReadU32() );
464
465 uint16_t primCount = stream.ReadU16();
466
467 for( uint16_t i = 0; i < primCount; i++ )
468 {
469 std::optional<ORCAD_PRIMITIVE> prim = OrcadReadPrimitive( stream );
470
471 if( prim )
472 sym.primitives.push_back( std::move( *prim ) );
473
474 // Occasional 8 zero bytes between prims; detected when next 2 bytes aren't a valid type pair.
475 if( i + 1 < primCount )
476 {
477 int b0 = stream.PeekU8( 0 );
478 int b1 = stream.PeekU8( 1 );
479
480 if( b0 >= 0 && b1 >= 0 && !( b0 == b1 && isPrimType( b0 ) ) )
481 stream.Skip( 8 );
482 }
483 }
484
485 // Bbox = last 8 bytes before next checkpoint (gap 8, or 16 w/ 8 legacy-trailer bytes), as 4x i16.
486 auto nextStopIt = std::upper_bound( stops.begin(), stops.end(), stream.GetOffset() );
487
488 if( nextStopIt != stops.end() )
489 {
490 size_t nextStop = *nextStopIt;
491 size_t gap = nextStop - stream.GetOffset();
492
493 if( gap >= 8 )
494 {
495 stream.Seek( nextStop - 8 );
496
497 int x1 = stream.ReadI16();
498 int y1 = stream.ReadI16();
499 int x2 = stream.ReadI16();
500 int y2 = stream.ReadI16();
501
502 if( x1 <= x2 && y1 <= y2 && x2 - x1 <= 4000 && y2 - y1 <= 4000 )
503 {
504 ORCAD_BBOX box;
505 box.x1 = x1;
506 box.y1 = y1;
507 box.x2 = x2;
508 box.y2 = y2;
509 sym.bbox = box;
510 }
511 }
512 else if( gap > 0 )
513 {
514 stream.Seek( nextStop );
515 }
516 }
517
518 if( aWithPins )
519 {
520 uint16_t pinCount = stream.ReadU16();
521
522 for( uint16_t i = 0; i < pinCount; i++ )
523 {
524 std::optional<ORCAD_SYMBOL_PIN> pin = OrcadReadSymbolPin( aReader );
525
526 if( pin )
527 {
528 pin->position = i;
529 sym.pins.push_back( std::move( *pin ) );
530 }
531 }
532
533 uint16_t propCount = stream.ReadU16();
534
535 for( uint16_t i = 0; i < propCount; i++ )
536 aReader.ReadStructure();
537
538 // LibraryPart GeneralProperties tail ends w/ u16 flags for pin number/name visibility; last before stop
539 if( sym.typeId == ORCAD_ST_LIBRARY_PART && aPrefixes.end >= 2
540 && aPrefixes.end - 2 >= stream.GetOffset() )
541 {
542 size_t save = stream.GetOffset();
543 stream.Seek( aPrefixes.end - 2 );
544 sym.generalFlags = stream.ReadU16();
545 stream.Seek( save );
546 }
547 }
548
549 if( aPrefixes.end != 0 && aPrefixes.end > stream.GetOffset() )
550 stream.Seek( aPrefixes.end );
551
552 return sym;
553}
554
555
557 const ORCAD_PREFIXES& aPrefixes )
558{
559 return OrcadReadSymbolDef( aReader, aPrefixes, false );
560}
561
562
564 const ORCAD_PREFIXES& aPrefixes )
565{
566 ORCAD_STREAM& stream = aReader.Stream();
567
568 stream.ReadU32(); // name string index (empty for blocks)
569 stream.ReadU32(); // source library string index
570 stream.ReadLzt(); // name ("")
571
572 uint32_t dbId = stream.ReadU32();
573
574 stream.ReadI16(); // anchor y
575 stream.ReadI16(); // anchor x
576 stream.ReadI16(); // bbox y2
577 stream.ReadI16(); // bbox x2
578
579 int x1 = stream.ReadI16();
580 int y1 = stream.ReadI16();
581
582 stream.Skip( 2 ); // color, orientation
583 stream.Skip( 2 ); // structId, unknown
584
585 std::vector<ORCAD_DISPLAY_PROP> displayProps = OrcadReadDisplayPropList( aReader );
586
587 // Inline LibraryPart carries block's pin interface.
588 uint8_t flag = stream.ReadU8();
589
591 {
592 THROW_IO_ERROR( wxString::Format( wxS( "drawn instance nested flag %d at 0x%zx" ),
593 static_cast<int>( flag ), stream.GetOffset() - 1 ) );
594 }
595
596 ORCAD_PREFIXES nestedPfx = aReader.ReadPrefixes();
597 ORCAD_SYMBOL_DEF nested = OrcadReadSymbolDef( aReader, nestedPfx, true );
598
599 ORCAD_BBOX bbox = nested.bbox.value_or( ORCAD_BBOX() );
600
602 block.dbId = dbId;
603 block.x1 = x1;
604 block.y1 = y1;
605 block.w = bbox.x2 - bbox.x1;
606 block.h = bbox.y2 - bbox.y1;
607 block.displayProps = std::move( displayProps );
608
609 // Block reference starts at second-to-last prefix checkpoint.
610 std::vector<size_t> stops = aPrefixes.stops;
611 std::sort( stops.begin(), stops.end() );
612
613 if( stops.size() >= 2 && stops[stops.size() - 2] >= stream.GetOffset() )
614 stream.Seek( stops[stops.size() - 2] );
615
616 block.reference = stream.ReadLzt();
617 stream.Skip( 14 );
618
619 // Framed T0x10 structs carry absolute pin page positions, in inline LibraryPart pin order.
620 uint16_t pinCount = stream.ReadU16();
621
622 std::vector<ORCAD_PIN_INST> pinInsts;
623
624 for( uint16_t i = 0; i < pinCount; i++ )
625 {
627
628 if( ORCAD_PIN_INST* pin = std::get_if<ORCAD_PIN_INST>( &result.record ) )
629 pinInsts.push_back( std::move( *pin ) );
630 }
631
632 for( size_t i = 0; i < pinInsts.size() && i < nested.pins.size(); i++ )
633 {
634 const ORCAD_SYMBOL_PIN& pin = nested.pins[i];
635
636 ORCAD_BLOCK_PIN blockPin;
637 blockPin.name = pin.name;
638 blockPin.portType = pin.portType;
639 blockPin.x = pinInsts[i].x;
640 blockPin.y = pinInsts[i].y;
641 block.pins.push_back( std::move( blockPin ) );
642 }
643
644 if( aPrefixes.end != 0 && aPrefixes.end > stream.GetOffset() )
645 stream.Seek( aPrefixes.end );
646
647 return block;
648}
649
650
652{
653 ORCAD_STREAM& stream = aReader.Stream();
654 ORCAD_PREFIXES pfx = aReader.ReadPrefixes();
655
656 if( pfx.typeId != ORCAD_ST_DEVICE )
657 THROW_IO_ERROR( wxString::Format( wxS( "expected Device, got type %d" ), pfx.typeId ) );
658
659 ORCAD_DEVICE device;
660 device.unitRef = stream.ReadLzt();
661 device.refDes = stream.ReadLzt();
662
663 uint16_t pinCount = stream.ReadU16();
664
665 for( uint16_t i = 0; i < pinCount; i++ )
666 {
667 // FF FF = empty pin slot (no number, ignored).
668 static const uint8_t emptyMarker[2] = { 0xFF, 0xFF };
669
670 if( stream.PeekMatches( emptyMarker, 2 ) )
671 {
672 stream.Skip( 2 );
673 device.pinNumbers.emplace_back();
674 device.pinIgnore.push_back( true );
675 continue;
676 }
677
678 device.pinNumbers.push_back( stream.ReadLzt() );
679
680 uint8_t config = stream.ReadU8();
681 device.pinIgnore.push_back( ( config & 0x80 ) != 0 );
682 }
683
684 if( pfx.end != 0 && pfx.end > stream.GetOffset() )
685 stream.Seek( pfx.end );
686
687 return device;
688}
689
690
692{
693 ORCAD_STREAM& stream = aReader.Stream();
694
695 ORCAD_PACKAGE pkg;
696 pkg.name = stream.ReadLzt();
697 pkg.sourceLib = stream.ReadLzt();
698 pkg.refDes = stream.ReadLzt();
699 stream.ReadLzt(); // unknown
700 pkg.pcbFootprint = stream.ReadLzt();
701
702 uint16_t deviceCount = stream.ReadU16();
703
704 for( uint16_t i = 0; i < deviceCount; i++ )
705 pkg.devices.push_back( OrcadReadDevice( aReader ) );
706
707 if( aPrefixes.end != 0 && aPrefixes.end > stream.GetOffset() )
708 stream.Seek( aPrefixes.end );
709
710 return pkg;
711}
712
713
714std::optional<size_t> OrcadFindStructureStart( const ORCAD_STREAM& aStream, size_t aPreamblePos )
715{
716 const uint8_t* data = aStream.Data();
717 size_t size = aStream.Size();
718
719 // Short prefix = (u8 type, i16 count, count*8 bytes) or (u8 type, i16 -1). Long prefixes 9 bytes each.
720 for( int propCount = 0; propCount < 40; propCount++ )
721 {
722 size_t shortLen = 3 + 8 * static_cast<size_t>( propCount );
723
724 if( aPreamblePos < shortLen )
725 break;
726
727 size_t shortPos = aPreamblePos - shortLen;
728 uint8_t type = data[shortPos];
729
730 if( !isKnownStructType( type ) )
731 continue;
732
733 int16_t count = static_cast<int16_t>( static_cast<uint16_t>( data[shortPos + 1] )
734 | static_cast<uint16_t>( data[shortPos + 2] ) << 8 );
735
736 if( count != propCount )
737 {
738 // Allow -1 marker with no pairs.
739 if( !( propCount == 0 && count == -1 ) )
740 continue;
741 }
742
743 // Count same-type long prefixes (u8 type, u32 len, u32 zero) backwards.
744 int longCount = 0;
745 size_t p = shortPos;
746
747 while( p >= 9 )
748 {
749 size_t q = p - 9;
750
751 if( data[q] == type && data[q + 5] == 0 && data[q + 6] == 0 && data[q + 7] == 0
752 && data[q + 8] == 0 )
753 {
754 longCount++;
755 p = q;
756 }
757 else
758 {
759 break;
760 }
761 }
762
763 if( longCount == 0 )
764 continue;
765
766 ORCAD_STREAM probe( data, size );
767 probe.Seek( p );
768
769 ORCAD_STRUCT_READER reader( probe );
770 ORCAD_PREFIXES pfx;
771
772 try
773 {
774 pfx = reader.TryReadPrefixes( longCount + 1 );
775 }
776 catch( const IO_ERROR& )
777 {
778 continue;
779 }
780
781 // Reject chains with extents past stream end.
782 bool valid = true;
783
784 for( size_t i = 0; i < pfx.bodyLens.size(); i++ )
785 {
786 size_t stop = p + 9 * i + 9 + pfx.bodyLens[i];
787
788 if( stop > size )
789 {
790 valid = false;
791 break;
792 }
793 }
794
795 if( !valid )
796 continue;
797
798 return p;
799 }
800
801 return std::nullopt;
802}
803
804
805void OrcadParseCache( const std::vector<char>& aData, const std::vector<std::string>& aStrings,
806 const ORCAD_WARN_FN& aWarn, std::map<std::string, ORCAD_SYMBOL_DEF>& aSymbols,
807 std::map<std::string, ORCAD_PACKAGE>& aPackages )
808{
809 ORCAD_STREAM stream( aData );
810 ORCAD_STRUCT_READER reader( stream, &aStrings, aWarn );
811
812 size_t pos = 0;
813
814 while( true )
815 {
816 size_t preamblePos = stream.FindPreamble( pos );
817
818 if( preamblePos == ORCAD_STREAM::npos )
819 break;
820
821 std::optional<size_t> start = OrcadFindStructureStart( stream, preamblePos );
822
823 if( !start )
824 {
825 pos = preamblePos + 4;
826 continue;
827 }
828
829 stream.Seek( *start );
830
831 ORCAD_PREFIXES pfx;
832
833 try
834 {
835 pfx = reader.ReadPrefixes();
836 }
837 catch( const IO_ERROR& )
838 {
839 pos = preamblePos + 4;
840 continue;
841 }
842
843 int typeId = pfx.typeId;
844 bool parsed = false;
845
846 try
847 {
848 if( isSymbolType( typeId ) )
849 {
850 ORCAD_SYMBOL_DEF sym = OrcadReadSymbolDef( reader, pfx, true );
851 parsed = true;
852
853 // Cache may hold stale versions of one name; first entry = default, later same-name = variants.
854 auto it = aSymbols.find( sym.name );
855
856 if( it == aSymbols.end() )
857 {
858 std::string key = sym.name;
859 aSymbols.emplace( std::move( key ), std::move( sym ) );
860 }
861 else
862 {
863 it->second.variants.push_back( std::move( sym ) );
864 }
865 }
866 else if( typeId == ORCAD_ST_PACKAGE )
867 {
868 ORCAD_PACKAGE pkg = OrcadReadPackage( reader, pfx );
869 parsed = true;
870
871 std::string key = pkg.name;
872 aPackages.insert_or_assign( std::move( key ), std::move( pkg ) );
873 }
874 }
875 catch( const IO_ERROR& e )
876 {
877 if( aWarn )
878 {
879 aWarn( wxString::Format( wxS( "cache struct type %d at 0x%zx: %s" ), typeId,
880 *start, e.Problem() ) );
881 }
882
883 if( pfx.end != 0 && pfx.end > preamblePos )
884 {
885 stream.Seek( pfx.end );
886 }
887 else
888 {
889 pos = preamblePos + 4;
890 continue;
891 }
892 }
893
894 if( parsed || ( pfx.end != 0 && pfx.end > preamblePos ) )
895 pos = std::max( stream.GetOffset(), preamblePos + 4 );
896 else
897 pos = preamblePos + 4;
898 }
899}
900
901
902void OrcadMergeCacheStreams( std::map<std::string, ORCAD_SYMBOL_DEF>& aSymbols,
903 std::map<std::string, ORCAD_PACKAGE>& aPackages,
904 std::map<std::string, ORCAD_SYMBOL_DEF>&& aExtraSymbols,
905 std::map<std::string, ORCAD_PACKAGE>&& aExtraPackages )
906{
907 for( auto& [name, sym] : aExtraSymbols )
908 {
909 auto it = aSymbols.find( name );
910
911 if( it == aSymbols.end() )
912 {
913 aSymbols.emplace( name, std::move( sym ) );
914 }
915 else
916 {
917 // Main cache def stays default; extra stream's default and variants appended in stream order.
918 std::vector<ORCAD_SYMBOL_DEF> extraVariants = std::move( sym.variants );
919 sym.variants.clear();
920
921 it->second.variants.push_back( std::move( sym ) );
922
923 for( ORCAD_SYMBOL_DEF& variant : extraVariants )
924 it->second.variants.push_back( std::move( variant ) );
925 }
926 }
927
928 for( auto& [name, pkg] : aExtraPackages )
929 aPackages.try_emplace( name, std::move( pkg ) );
930}
const char * name
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
virtual const wxString Problem() const
what was the problem?
Little-endian byte cursor over one OrCAD Capture DSN stream.
int PeekU8(size_t aAhead=0) const
Peek one byte at cursor + aAhead without advancing.
size_t FindPreamble(size_t aFrom) const
Find the next preamble at or after the given absolute offset.
void SkipOptionalPreambleBlock()
If the preamble sits at the cursor, consume it plus its u32 trailLen and the trailLen trailing bytes;...
uint8_t ReadU8()
static constexpr size_t npos
Returned by FindPreamble() when no further preamble exists.
size_t GetOffset() const
bool HasPreambleAt(size_t aAbsoluteOffset) const
True when the preamble sits at the given absolute offset (false if out of range).
uint16_t ReadU16()
std::vector< uint8_t > ReadBytes(size_t aCount)
Read exactly aCount bytes; throws IO_ERROR on overrun.
void Skip(size_t aCount)
Advance the cursor; throws IO_ERROR when aCount exceeds the remaining bytes.
std::string ReadLzt()
Read a length-prefixed zero-terminated string: u16 length, length content bytes, then a mandatory 0x0...
size_t Size() const
const uint8_t * Data() const
Raw buffer access for scan-and-backtrack parsers (cache/hierarchy walkers).
bool PeekMatches(const uint8_t *aBytes, size_t aCount, size_t aAhead=0) const
Compare aCount bytes at cursor + aAhead against aBytes without advancing.
void Seek(size_t aOffset)
Set the absolute cursor position.
int32_t ReadI32()
void ExpectByte(uint8_t aValue, const wxString &aWhat)
Consume one byte and require the given value.
uint32_t ReadU32()
void Expect(const uint8_t *aBytes, size_t aCount, const wxString &aWhat)
Consume aCount bytes and require them to equal aBytes; throws IO_ERROR naming aWhat and showing expec...
int16_t ReadI16()
Stateful reader shared by all structure parsers.
std::map< std::string, std::string > PropsDict(const ORCAD_PREFIXES &aPrefixes) const
Resolve the short-prefix (nameIdx, valueIdx) pairs; empty names are dropped.
ORCAD_STREAM & Stream()
ORCAD_READ_RESULT ReadStructure()
Read one structure of any type: prefixes, then the type-specific body via the reader dispatch below.
ORCAD_PREFIXES TryReadPrefixes(int aCount)
Attempt to read exactly aCount prefixes (aCount - 1 long + 1 short) at the current position and verif...
ORCAD_PREFIXES ReadPrefixes()
Discover the prefix count by trial from 10 down to 1 (longest chain first), then consume the prefixes...
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
void OrcadMergeCacheStreams(std::map< std::string, ORCAD_SYMBOL_DEF > &aSymbols, std::map< std::string, ORCAD_PACKAGE > &aPackages, std::map< std::string, ORCAD_SYMBOL_DEF > &&aExtraSymbols, std::map< std::string, ORCAD_PACKAGE > &&aExtraPackages)
Merge the results of a 'Packages/<name>' stream (locally modified parts) into the main cache maps: a ...
ORCAD_DEVICE OrcadReadDevice(ORCAD_STRUCT_READER &aReader)
Read one Device structure (reads its own prefixes; type must be 32).
std::optional< ORCAD_SYMBOL_PIN > OrcadReadSymbolPin(ORCAD_STRUCT_READER &aReader)
Read one symbol pin.
std::optional< size_t > OrcadFindStructureStart(const ORCAD_STREAM &aStream, size_t aPreamblePos)
Given the absolute position of a preamble magic, backtrack to find a valid prefix chain ending right ...
ORCAD_PACKAGE OrcadReadPackage(ORCAD_STRUCT_READER &aReader, const ORCAD_PREFIXES &aPrefixes)
Read a Package body (type 31); layout documented on ORCAD_PACKAGE.
ORCAD_DRAWN_INSTANCE OrcadReadDrawnInstance(ORCAD_STRUCT_READER &aReader, const ORCAD_PREFIXES &aPrefixes)
Structure type 12 (DrawnInstance): hierarchical block instance.
std::optional< ORCAD_PRIMITIVE > OrcadReadPrimitive(ORCAD_STREAM &aStream)
Read one graphic primitive including its doubled u8 type-pair prefix, at the current cursor.
void OrcadParseCache(const std::vector< char > &aData, const std::vector< std::string > &aStrings, const ORCAD_WARN_FN &aWarn, std::map< std::string, ORCAD_SYMBOL_DEF > &aSymbols, std::map< std::string, ORCAD_PACKAGE > &aPackages)
Scan a Cache-framed stream (the 'Cache' stream itself, or any 'Packages/<name>' stream — they share t...
ORCAD_SYMBOL_DEF OrcadReadSymbolDef(ORCAD_STRUCT_READER &aReader, const ORCAD_PREFIXES &aPrefixes, bool aWithPins)
Read a symbol definition body (LibraryPart / GlobalSymbol / PortSymbol / OffPageSymbol / TitleBlockSy...
ORCAD_SYMBOL_DEF OrcadReadSthInPages0(ORCAD_STRUCT_READER &aReader, const ORCAD_PREFIXES &aPrefixes)
Structure type 2 (SthInPages0): nested symbol body inside Graphic*Inst structures on pages; carries t...
Parsers for the DSN 'Cache' stream and the 'Packages/<name>' streams: symbol definitions with graphic...
std::function< void(const wxString &aMsg)> ORCAD_WARN_FN
Warning sink shared by all parser entry points (recoverable-issue channel).
@ ORCAD_PRIM_ARC
@ ORCAD_PRIM_BEZIER
@ ORCAD_PRIM_POLYLINE
@ ORCAD_PRIM_COMMENT_TEXT
@ ORCAD_PRIM_LINE
@ ORCAD_PRIM_ELLIPSE
@ ORCAD_PRIM_BITMAP
plain DIB (BITMAPINFOHEADER + pixels)
@ ORCAD_PRIM_OLE_IMAGE
OLE compound document embed.
@ ORCAD_PRIM_POLYGON
@ ORCAD_PRIM_RECT
@ ORCAD_PRIM_SYMBOL_VECTOR
nested prefix-framed vector graphic
@ ORCAD_ST_PORT_SYMBOL
@ ORCAD_ST_TITLEBLOCK_SYMBOL
@ ORCAD_ST_SYMBOL_PIN_SCALAR
@ ORCAD_ST_LIBRARY_PART
@ ORCAD_ST_DEVICE
@ ORCAD_ST_GLOBAL_SYMBOL
power symbol definition
@ ORCAD_ST_BOOKMARK_SYMBOL
@ ORCAD_ST_PACKAGE
@ ORCAD_ST_SYMBOL_PIN_BUS
@ ORCAD_ST_ERC_SYMBOL
@ ORCAD_ST_OFFPAGE_SYMBOL
@ ORCAD_ST_PIN_SHAPE_SYMBOL
std::vector< ORCAD_DISPLAY_PROP > OrcadReadDisplayPropList(ORCAD_STRUCT_READER &aReader)
Read a u16-counted list of framed DisplayProp structures via ReadStructure(), keeping only the succes...
Axis-aligned box in OrCAD DBU; corner order as stored (not normalized).
One interface pin of a hierarchical block, at its absolute page position.
ORCAD_PORT_TYPE portType
std::string name
One device of a package: the pin-number map of a unit (structure type 32).
std::vector< std::string > pinNumbers
std::string refDes
std::vector< bool > pinIgnore
std::string unitRef
Structure type 12: a hierarchical block instance placed on a page.
int x1
block rectangle top-left, page DBU
std::vector< ORCAD_BLOCK_PIN > pins
std::vector< ORCAD_DISPLAY_PROP > displayProps
A package: refdes prefix, footprint and per-unit devices (structure type 31).
std::string refDes
std::string sourceLib
std::vector< ORCAD_DEVICE > devices
std::string pcbFootprint
std::string name
T0x10: one pin of a placed instance, carrying the pin's absolute page position (the connection point ...
Integer point in OrCAD DBU.
The decoded prefix chain of one framed structure.
std::vector< size_t > stops
checkpoint offsets, one per long prefix: start + 9 * i + 9 + bodyLens[i]
int typeId
ORCAD_ST value (u8 in the stream)
std::vector< uint32_t > bodyLens
one per long prefix, outermost first
size_t end
offset right after the whole structure (from the outermost long prefix); 0 when unknown
One graphic primitive of a symbol body or nested page graphic.
int fillStyle
0 solid, 1 none, 2 hatch pattern
std::string text
kind == TEXT
std::vector< ORCAD_POINT > points
polygon/polyline/bezier vertices
ORCAD_PRIM_KIND kind
std::vector< uint8_t > data
kind == IMAGE: raw embedded payload
int lineStyle
0 solid, 1 dash, 2 dot, 3 dash-dot, 4 dash-dot-dot, 5 default
std::optional< ORCAD_POINT > start
arc start point
std::optional< ORCAD_POINT > end
arc end point
int lineWidth
Capture width enum: 0 thin, 1 medium, 2 wide, 3 default.
A symbol definition from the design Cache (LibraryPart / GlobalSymbol / PortSymbol / OffPageSymbol / ...
std::string name
cache name, e.g. "C.Normal"
std::vector< ORCAD_SYMBOL_PIN > pins
std::vector< ORCAD_PRIMITIVE > primitives
std::map< std::string, std::string > props
int typeId
ORCAD_ST value.
std::string sourceLib
std::optional< ORCAD_BBOX > bbox
symbol-space body box
int generalFlags
LibraryPart GeneralProperties flags (-1 = absent); bit0 = pin numbers visible, bit2 = pin names hidde...
One pin of a symbol definition (structure types 26/27).
KIBIS_PIN * pin
std::vector< std::vector< std::string > > table
VECTOR2I end
wxString result
Test unit parsing edge cases and error handling.