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 THROW_IO_ERRORF( wxS( "poly primitive at 0x%zx: no consistent point count for size %zu" ), start, size );
284
286
287 if( off >= 8 )
288 {
289 aStream.Seek( body );
290 p.lineStyle = aStream.ReadU32();
291 p.lineWidth = aStream.ReadU32();
292
293 if( t1 == ORCAD_PRIM_POLYGON && off >= 16 )
294 {
295 p.fillStyle = aStream.ReadU32();
296 p.hatchStyle = aStream.ReadU32();
297 }
298 }
299
300 aStream.Seek( body + off );
301
302 uint16_t pointCount = aStream.ReadU16();
303
304 if( t1 == ORCAD_PRIM_POLYGON )
306 else if( t1 == ORCAD_PRIM_POLYLINE )
308 else
310
311 for( uint16_t i = 0; i < pointCount; i++ )
312 {
313 ORCAD_POINT pt;
314 pt.y = aStream.ReadI16();
315 pt.x = aStream.ReadI16();
316 p.points.push_back( pt );
317 }
318
319 prim = std::move( p );
320 }
321 else if( t1 == ORCAD_PRIM_COMMENT_TEXT )
322 {
325 p.x1 = aStream.ReadI32();
326 p.y1 = aStream.ReadI32();
327 p.x2 = aStream.ReadI32();
328 p.y2 = aStream.ReadI32();
329 aStream.ReadI32(); // duplicate corner x
330 aStream.ReadI32(); // duplicate corner y
331 p.fontIdx = aStream.ReadU16();
332 aStream.Skip( 2 );
333 p.text = aStream.ReadLzt();
334 prim = std::move( p );
335 }
336 else if( t1 == ORCAD_PRIM_BITMAP )
337 {
340 p.x1 = aStream.ReadI32();
341 p.y1 = aStream.ReadI32();
342 p.x2 = aStream.ReadI32();
343 p.y2 = aStream.ReadI32();
344 aStream.Skip( 8 ); // x1, y1 duplicate corner
345 aStream.Skip( 8 ); // pixel width/height
346
347 uint32_t dataSize = aStream.ReadU32();
348
349 // Payload past record end = malformed bitmap; skip whole record.
350 if( aStream.GetOffset() + static_cast<size_t>( dataSize ) <= end )
351 {
352 p.data = aStream.ReadBytes( dataSize );
353 prim = std::move( p );
354 }
355 }
356 else if( t1 == ORCAD_PRIM_OLE_IMAGE )
357 {
360 p.x1 = aStream.ReadI32();
361 p.y1 = aStream.ReadI32();
362 p.x2 = aStream.ReadI32();
363 p.y2 = aStream.ReadI32();
364 aStream.Skip( 16 ); // crop/original-extent values
365
366 // OLE compound-document payload fills rest of record.
367 size_t from = std::min( aStream.GetOffset(), aStream.Size() );
368 size_t to = std::min( end, aStream.Size() );
369
370 if( to > from )
371 p.data.assign( aStream.Data() + from, aStream.Data() + to );
372
373 prim = std::move( p );
374 }
375
376 if( aStream.GetOffset() > end )
377 THROW_IO_ERRORF( wxS( "primitive type %d overran (0x%zx > 0x%zx)" ), t1, aStream.GetOffset(), end );
378
379 aStream.Seek( end );
381 return prim;
382}
383
384} // namespace
385
386
387std::optional<ORCAD_PRIMITIVE> OrcadReadPrimitive( ORCAD_STREAM& aStream )
388{
389 int t1 = aStream.ReadU8();
390 int t2 = aStream.ReadU8();
391
392 if( t1 != t2 || !isPrimType( t1 ) )
393 THROW_IO_ERRORF( wxS( "bad primitive prefix %d/%d at 0x%zx" ), t1, t2, aStream.GetOffset() - 2 );
394
395 if( t1 == ORCAD_PRIM_SYMBOL_VECTOR )
396 return readSymbolVector( aStream );
397
398 return readPrimitiveBody( aStream, t1 );
399}
400
401
402std::optional<ORCAD_SYMBOL_PIN> OrcadReadSymbolPin( ORCAD_STRUCT_READER& aReader )
403{
404 ORCAD_STREAM& stream = aReader.Stream();
405
406 // Single 0x00 instead of prefix chain = skipped pin slot.
407 if( stream.PeekU8() == 0x00 )
408 {
409 stream.Skip( 1 );
410 return std::nullopt;
411 }
412
413 ORCAD_PREFIXES pfx = aReader.ReadPrefixes();
414
416 THROW_IO_ERRORF( wxS( "expected symbol pin, got type %d" ), pfx.typeId );
417
419 pin.name = stream.ReadLzt();
420 pin.startX = stream.ReadI32();
421 pin.startY = stream.ReadI32();
422 pin.hotptX = stream.ReadI32();
423 pin.hotptY = stream.ReadI32();
424 pin.shapeBits = stream.ReadU16();
425 stream.Skip( 2 ); // uninitialized junk
426
427 uint32_t portType = stream.ReadU32();
428 pin.portType = portType <= 7 ? static_cast<ORCAD_PORT_TYPE>( portType )
430
431 // Remaining body is junk/zeros; pin ends at outer stop.
432 if( pfx.end != 0 && pfx.end >= stream.GetOffset() )
433 stream.Seek( pfx.end );
434
435 return pin;
436}
437
438
440 bool aWithPins )
441{
442 ORCAD_STREAM& stream = aReader.Stream();
443
444 std::vector<size_t> stops = aPrefixes.stops;
445 std::sort( stops.begin(), stops.end() );
446
448 sym.typeId = aPrefixes.typeId;
449 sym.name = stream.ReadLzt();
450 sym.sourceLib = stream.ReadLzt();
451 sym.props = aReader.PropsDict( aPrefixes );
452
453 sym.color = static_cast<int>( stream.ReadU32() );
454
455 uint16_t primCount = stream.ReadU16();
456
457 for( uint16_t i = 0; i < primCount; i++ )
458 {
459 std::optional<ORCAD_PRIMITIVE> prim = OrcadReadPrimitive( stream );
460
461 if( prim )
462 sym.primitives.push_back( std::move( *prim ) );
463
464 // Occasional 8 zero bytes between prims; detected when next 2 bytes aren't a valid type pair.
465 if( i + 1 < primCount )
466 {
467 int b0 = stream.PeekU8( 0 );
468 int b1 = stream.PeekU8( 1 );
469
470 if( b0 >= 0 && b1 >= 0 && !( b0 == b1 && isPrimType( b0 ) ) )
471 stream.Skip( 8 );
472 }
473 }
474
475 // Bbox = last 8 bytes before next checkpoint (gap 8, or 16 w/ 8 legacy-trailer bytes), as 4x i16.
476 auto nextStopIt = std::upper_bound( stops.begin(), stops.end(), stream.GetOffset() );
477
478 if( nextStopIt != stops.end() )
479 {
480 size_t nextStop = *nextStopIt;
481 size_t gap = nextStop - stream.GetOffset();
482
483 if( gap >= 8 )
484 {
485 stream.Seek( nextStop - 8 );
486
487 int x1 = stream.ReadI16();
488 int y1 = stream.ReadI16();
489 int x2 = stream.ReadI16();
490 int y2 = stream.ReadI16();
491
492 if( x1 <= x2 && y1 <= y2 && x2 - x1 <= 4000 && y2 - y1 <= 4000 )
493 {
494 ORCAD_BBOX box;
495 box.x1 = x1;
496 box.y1 = y1;
497 box.x2 = x2;
498 box.y2 = y2;
499 sym.bbox = box;
500 }
501 }
502 else if( gap > 0 )
503 {
504 stream.Seek( nextStop );
505 }
506 }
507
508 if( aWithPins )
509 {
510 uint16_t pinCount = stream.ReadU16();
511
512 for( uint16_t i = 0; i < pinCount; i++ )
513 {
514 std::optional<ORCAD_SYMBOL_PIN> pin = OrcadReadSymbolPin( aReader );
515
516 if( pin )
517 {
518 pin->position = i;
519 sym.pins.push_back( std::move( *pin ) );
520 }
521 }
522
523 uint16_t propCount = stream.ReadU16();
524
525 for( uint16_t i = 0; i < propCount; i++ )
526 aReader.ReadStructure();
527
528 // LibraryPart GeneralProperties tail ends w/ u16 flags for pin number/name visibility; last before stop
529 if( sym.typeId == ORCAD_ST_LIBRARY_PART && aPrefixes.end >= 2
530 && aPrefixes.end - 2 >= stream.GetOffset() )
531 {
532 size_t save = stream.GetOffset();
533 stream.Seek( aPrefixes.end - 2 );
534 sym.generalFlags = stream.ReadU16();
535 stream.Seek( save );
536 }
537 }
538
539 if( aPrefixes.end != 0 && aPrefixes.end > stream.GetOffset() )
540 stream.Seek( aPrefixes.end );
541
542 return sym;
543}
544
545
547 const ORCAD_PREFIXES& aPrefixes )
548{
549 return OrcadReadSymbolDef( aReader, aPrefixes, false );
550}
551
552
554 const ORCAD_PREFIXES& aPrefixes )
555{
556 ORCAD_STREAM& stream = aReader.Stream();
557
558 stream.ReadU32(); // name string index (empty for blocks)
559 stream.ReadU32(); // source library string index
560 stream.ReadLzt(); // name ("")
561
562 uint32_t dbId = stream.ReadU32();
563
564 stream.ReadI16(); // anchor y
565 stream.ReadI16(); // anchor x
566 stream.ReadI16(); // bbox y2
567 stream.ReadI16(); // bbox x2
568
569 int x1 = stream.ReadI16();
570 int y1 = stream.ReadI16();
571
572 stream.Skip( 2 ); // color, orientation
573 stream.Skip( 2 ); // structId, unknown
574
575 std::vector<ORCAD_DISPLAY_PROP> displayProps = OrcadReadDisplayPropList( aReader );
576
577 // Inline LibraryPart carries block's pin interface.
578 uint8_t flag = stream.ReadU8();
579
581 {
582 THROW_IO_ERRORF( wxS( "drawn instance nested flag %d at 0x%zx" ),
583 static_cast<int>( flag ), stream.GetOffset() - 1 );
584 }
585
586 ORCAD_PREFIXES nestedPfx = aReader.ReadPrefixes();
587 ORCAD_SYMBOL_DEF nested = OrcadReadSymbolDef( aReader, nestedPfx, true );
588
589 ORCAD_BBOX bbox = nested.bbox.value_or( ORCAD_BBOX() );
590
592 block.dbId = dbId;
593 block.x1 = x1;
594 block.y1 = y1;
595 block.w = bbox.x2 - bbox.x1;
596 block.h = bbox.y2 - bbox.y1;
597 block.displayProps = std::move( displayProps );
598
599 // Block reference starts at second-to-last prefix checkpoint.
600 std::vector<size_t> stops = aPrefixes.stops;
601 std::sort( stops.begin(), stops.end() );
602
603 if( stops.size() >= 2 && stops[stops.size() - 2] >= stream.GetOffset() )
604 stream.Seek( stops[stops.size() - 2] );
605
606 block.reference = stream.ReadLzt();
607 stream.Skip( 14 );
608
609 // Framed T0x10 structs carry absolute pin page positions, in inline LibraryPart pin order.
610 uint16_t pinCount = stream.ReadU16();
611
612 std::vector<ORCAD_PIN_INST> pinInsts;
613
614 for( uint16_t i = 0; i < pinCount; i++ )
615 {
617
618 if( ORCAD_PIN_INST* pin = std::get_if<ORCAD_PIN_INST>( &result.record ) )
619 pinInsts.push_back( std::move( *pin ) );
620 }
621
622 for( size_t i = 0; i < pinInsts.size() && i < nested.pins.size(); i++ )
623 {
624 const ORCAD_SYMBOL_PIN& pin = nested.pins[i];
625
626 ORCAD_BLOCK_PIN blockPin;
627 blockPin.name = pin.name;
628 blockPin.portType = pin.portType;
629 blockPin.x = pinInsts[i].x;
630 blockPin.y = pinInsts[i].y;
631 block.pins.push_back( std::move( blockPin ) );
632 }
633
634 if( aPrefixes.end != 0 && aPrefixes.end > stream.GetOffset() )
635 stream.Seek( aPrefixes.end );
636
637 return block;
638}
639
640
642{
643 ORCAD_STREAM& stream = aReader.Stream();
644 ORCAD_PREFIXES pfx = aReader.ReadPrefixes();
645
646 if( pfx.typeId != ORCAD_ST_DEVICE )
647 THROW_IO_ERRORF( wxS( "expected Device, got type %d" ), pfx.typeId );
648
649 ORCAD_DEVICE device;
650 device.unitRef = stream.ReadLzt();
651 device.refDes = stream.ReadLzt();
652
653 uint16_t pinCount = stream.ReadU16();
654
655 for( uint16_t i = 0; i < pinCount; i++ )
656 {
657 // FF FF = empty pin slot (no number, ignored).
658 static const uint8_t emptyMarker[2] = { 0xFF, 0xFF };
659
660 if( stream.PeekMatches( emptyMarker, 2 ) )
661 {
662 stream.Skip( 2 );
663 device.pinNumbers.emplace_back();
664 device.pinIgnore.push_back( true );
665 continue;
666 }
667
668 device.pinNumbers.push_back( stream.ReadLzt() );
669
670 uint8_t config = stream.ReadU8();
671 device.pinIgnore.push_back( ( config & 0x80 ) != 0 );
672 }
673
674 if( pfx.end != 0 && pfx.end > stream.GetOffset() )
675 stream.Seek( pfx.end );
676
677 return device;
678}
679
680
682{
683 ORCAD_STREAM& stream = aReader.Stream();
684
685 ORCAD_PACKAGE pkg;
686 pkg.name = stream.ReadLzt();
687 pkg.sourceLib = stream.ReadLzt();
688 pkg.refDes = stream.ReadLzt();
689 stream.ReadLzt(); // unknown
690 pkg.pcbFootprint = stream.ReadLzt();
691
692 uint16_t deviceCount = stream.ReadU16();
693
694 for( uint16_t i = 0; i < deviceCount; i++ )
695 pkg.devices.push_back( OrcadReadDevice( aReader ) );
696
697 pkg.props = aReader.PropsDict( aPrefixes );
698
699 if( aPrefixes.end != 0 && aPrefixes.end > stream.GetOffset() )
700 stream.Seek( aPrefixes.end );
701
702 return pkg;
703}
704
705
706std::optional<size_t> OrcadFindStructureStart( const ORCAD_STREAM& aStream, size_t aPreamblePos )
707{
708 const uint8_t* data = aStream.Data();
709 size_t size = aStream.Size();
710
711 // Short prefix = (u8 type, i16 count, count*8 bytes) or (u8 type, i16 -1). Long prefixes 9 bytes each.
712 for( int propCount = 0; propCount < 40; propCount++ )
713 {
714 size_t shortLen = 3 + 8 * static_cast<size_t>( propCount );
715
716 if( aPreamblePos < shortLen )
717 break;
718
719 size_t shortPos = aPreamblePos - shortLen;
720 uint8_t type = data[shortPos];
721
722 if( !isKnownStructType( type ) )
723 continue;
724
725 int16_t count = static_cast<int16_t>( static_cast<uint16_t>( data[shortPos + 1] )
726 | static_cast<uint16_t>( data[shortPos + 2] ) << 8 );
727
728 if( count != propCount )
729 {
730 // Allow -1 marker with no pairs.
731 if( !( propCount == 0 && count == -1 ) )
732 continue;
733 }
734
735 // Count same-type long prefixes (u8 type, u32 len, u32 zero) backwards.
736 int longCount = 0;
737 size_t p = shortPos;
738
739 while( p >= 9 )
740 {
741 size_t q = p - 9;
742
743 if( data[q] == type && data[q + 5] == 0 && data[q + 6] == 0 && data[q + 7] == 0
744 && data[q + 8] == 0 )
745 {
746 longCount++;
747 p = q;
748 }
749 else
750 {
751 break;
752 }
753 }
754
755 if( longCount == 0 )
756 continue;
757
758 ORCAD_STREAM probe( data, size );
759 probe.Seek( p );
760
761 ORCAD_STRUCT_READER reader( probe );
762 ORCAD_PREFIXES pfx;
763
764 try
765 {
766 pfx = reader.TryReadPrefixes( longCount + 1 );
767 }
768 catch( const IO_ERROR& )
769 {
770 continue;
771 }
772
773 // Reject chains with extents past stream end.
774 bool valid = true;
775
776 for( size_t i = 0; i < pfx.bodyLens.size(); i++ )
777 {
778 size_t stop = p + 9 * i + 9 + pfx.bodyLens[i];
779
780 if( stop > size )
781 {
782 valid = false;
783 break;
784 }
785 }
786
787 if( !valid )
788 continue;
789
790 return p;
791 }
792
793 return std::nullopt;
794}
795
796
797void OrcadParseCache( const std::vector<char>& aData, const std::vector<std::string>& aStrings,
798 const ORCAD_WARN_FN& aWarn, std::map<std::string, ORCAD_SYMBOL_DEF>& aSymbols,
799 std::map<std::string, ORCAD_PACKAGE>& aPackages )
800{
801 ORCAD_STREAM stream( aData );
802 ORCAD_STRUCT_READER reader( stream, &aStrings, aWarn );
803
804 size_t pos = 0;
805
806 while( true )
807 {
808 size_t preamblePos = stream.FindPreamble( pos );
809
810 if( preamblePos == ORCAD_STREAM::npos )
811 break;
812
813 std::optional<size_t> start = OrcadFindStructureStart( stream, preamblePos );
814
815 if( !start )
816 {
817 pos = preamblePos + 4;
818 continue;
819 }
820
821 stream.Seek( *start );
822
823 ORCAD_PREFIXES pfx;
824
825 try
826 {
827 pfx = reader.ReadPrefixes();
828 }
829 catch( const IO_ERROR& )
830 {
831 pos = preamblePos + 4;
832 continue;
833 }
834
835 int typeId = pfx.typeId;
836 bool parsed = false;
837
838 try
839 {
840 if( isSymbolType( typeId ) )
841 {
842 ORCAD_SYMBOL_DEF sym = OrcadReadSymbolDef( reader, pfx, true );
843 parsed = true;
844
845 // Cache may hold stale versions of one name; first entry = default, later same-name = variants.
846 auto it = aSymbols.find( sym.name );
847
848 if( it == aSymbols.end() )
849 {
850 std::string key = sym.name;
851 aSymbols.emplace( std::move( key ), std::move( sym ) );
852 }
853 else
854 {
855 it->second.variants.push_back( std::move( sym ) );
856 }
857 }
858 else if( typeId == ORCAD_ST_PACKAGE )
859 {
860 ORCAD_PACKAGE pkg = OrcadReadPackage( reader, pfx );
861 parsed = true;
862
863 std::string key = pkg.name;
864 aPackages.insert_or_assign( std::move( key ), std::move( pkg ) );
865 }
866 }
867 catch( const IO_ERROR& e )
868 {
869 if( aWarn )
870 {
871 aWarn( wxString::Format( wxS( "cache struct type %d at 0x%zx: %s" ), typeId,
872 *start, e.Problem() ) );
873 }
874
875 if( pfx.end != 0 && pfx.end > preamblePos )
876 {
877 stream.Seek( pfx.end );
878 }
879 else
880 {
881 pos = preamblePos + 4;
882 continue;
883 }
884 }
885
886 if( parsed || ( pfx.end != 0 && pfx.end > preamblePos ) )
887 pos = std::max( stream.GetOffset(), preamblePos + 4 );
888 else
889 pos = preamblePos + 4;
890 }
891}
892
893
894void OrcadMergeCacheStreams( std::map<std::string, ORCAD_SYMBOL_DEF>& aSymbols,
895 std::map<std::string, ORCAD_PACKAGE>& aPackages,
896 std::map<std::string, ORCAD_SYMBOL_DEF>&& aExtraSymbols,
897 std::map<std::string, ORCAD_PACKAGE>&& aExtraPackages )
898{
899 for( auto& [name, sym] : aExtraSymbols )
900 {
901 auto it = aSymbols.find( name );
902
903 if( it == aSymbols.end() )
904 {
905 aSymbols.emplace( name, std::move( sym ) );
906 }
907 else
908 {
909 // Main cache def stays default; extra stream's default and variants appended in stream order.
910 std::vector<ORCAD_SYMBOL_DEF> extraVariants = std::move( sym.variants );
911 sym.variants.clear();
912
913 it->second.variants.push_back( std::move( sym ) );
914
915 for( ORCAD_SYMBOL_DEF& variant : extraVariants )
916 it->second.variants.push_back( std::move( variant ) );
917 }
918 }
919
920 for( auto& [name, pkg] : aExtraPackages )
921 aPackages.try_emplace( name, std::move( pkg ) );
922}
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
#define THROW_IO_ERRORF(msg,...)
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::map< std::string, std::string > props
Part-level properties shared by every placement (Description, Tolerance, ...).
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 names visible, bit2 = pin numbers hidde...
One pin of a symbol definition (structure types 26/27).
KIBIS_PIN * pin
VECTOR2I end
wxString result
Test unit parsing edge cases and error handling.