KiCad PCB EDA Suite
Loading...
Searching...
No Matches
orcad_page.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 <utility>
27#include <set>
28
29#include <ki_exception.h>
30#include <wx/translation.h>
31
36
37
38namespace
39{
40
41template <typename T>
42bool takeRecord( ORCAD_READ_RESULT& aResult, std::vector<T>& aTarget )
43{
44 if( T* record = std::get_if<T>( &aResult.record ) )
45 {
46 aTarget.push_back( std::move( *record ) );
47 return true;
48 }
49
50 return false;
51}
52
53
54template <typename T>
55void readStructureList( ORCAD_STRUCT_READER& aReader, std::vector<T>& aTarget )
56{
57 uint16_t count = aReader.Stream().ReadU16();
58
59 for( uint16_t i = 0; i < count; i++ )
60 {
62 takeRecord( result, aTarget );
63 }
64}
65
66} // namespace
67
68
69ORCAD_RAW_PAGE OrcadParsePage( const std::vector<char>& aData, const std::vector<std::string>& aStrings,
70 const ORCAD_WARN_FN& aWarn )
71{
72 ORCAD_STREAM stream( aData );
73 ORCAD_STRUCT_READER reader( stream, &aStrings, aWarn );
74 ORCAD_RAW_PAGE page;
75
76 ORCAD_PREFIXES prefixes = reader.ReadPrefixes( ORCAD_ST_PAGE );
77
78 page.props = reader.PropsDict( prefixes );
79 page.name = stream.ReadLzt();
80 page.pageSize = stream.ReadLzt();
81
83 page.width = settings.width;
84 page.height = settings.height;
85 page.isMetric = settings.isMetric;
86 page.horizontalCount = settings.horizontalCount;
87 page.verticalCount = settings.verticalCount;
88 page.horizontalWidth = settings.horizontalWidth;
89 page.verticalWidth = settings.verticalWidth;
90 page.horizontalChar = settings.horizontalChar;
92 page.verticalChar = settings.verticalChar;
94 page.borderPrinted = settings.borderPrinted;
95 page.gridRefPrinted = settings.gridRefPrinted;
96 page.createTimestamp = settings.createTimestamp;
97 page.modifyTimestamp = settings.modifyTimestamp;
98
99 readStructureList( reader, page.titleBlocks );
100
101 // T0x34/T0x35 raw (not prefix-framed); read only to stay aligned.
102 uint16_t count = stream.ReadU16();
103
104 for( uint16_t i = 0; i < count; i++ )
105 page.netGroups.push_back( OrcadReadT0x34Raw( stream ) );
106
107 count = stream.ReadU16();
108
109 for( uint16_t i = 0; i < count; i++ )
110 page.netGroups.push_back( OrcadReadT0x35Raw( stream ) );
111
112 // Net table = lzt net name + u32 net db id; authoritative for names/junctions, wires key it.
113 count = stream.ReadU16();
114
115 for( uint16_t i = 0; i < count; i++ )
116 {
117 std::string netName = stream.ReadLzt();
118 uint32_t netId = stream.ReadU32();
119 page.netAliases[netId].push_back( netName );
120 page.netmap[netId] = std::move( netName );
121 }
122
123 readStructureList( reader, page.wires );
124
125 // Placed parts; type 13 = part instance, type 12 = hier block instance.
126 count = stream.ReadU16();
127
128 for( uint16_t i = 0; i < count; i++ )
129 {
131
132 if( !takeRecord( result, page.instances ) )
133 takeRecord( result, page.blocks );
134 }
135
136 readStructureList( reader, page.ports );
137
138 // Each Global and OffPageConnector list entry followed by 5 bytes.
139 count = stream.ReadU16();
140
141 for( uint16_t i = 0; i < count; i++ )
142 {
144 takeRecord( result, page.globals );
145 stream.Skip( 5 );
146 }
147
148 count = stream.ReadU16();
149
150 for( uint16_t i = 0; i < count; i++ )
151 {
153 takeRecord( result, page.offpage );
154 stream.Skip( 5 );
155 }
156
157 readStructureList( reader, page.ercObjects );
158 readStructureList( reader, page.busEntries );
159 readStructureList( reader, page.graphics );
160
161 if( stream.GetOffset() > prefixes.end )
162 THROW_IO_ERROR( wxS( "OrCAD page stream: page body exceeds its outer stop" ) );
163
164 return page;
165}
166
167
168static std::vector<std::string> pageOrderBody( ORCAD_STREAM& aStream )
169{
170 aStream.ReadLzt(); // schematic folder name
171 aStream.Skip( 4 );
172
173 uint16_t count = aStream.ReadU16();
174
175 std::vector<std::string> names;
176 names.reserve( count );
177
178 for( uint16_t i = 0; i < count; i++ )
179 names.push_back( aStream.ReadLzt() );
180
181 std::reverse( names.begin(), names.end() );
182
183 return names;
184}
185
186
187std::vector<std::string> OrcadParsePageOrder( const std::vector<char>& aData )
188{
189 ORCAD_STREAM stream( aData );
190 ORCAD_STRUCT_READER reader( stream );
191
193
194 return pageOrderBody( stream );
195}
196
197
198std::vector<std::string> OrcadParseSchematicFolderOrder( const std::vector<char>& aData )
199{
200 ORCAD_STREAM stream( aData );
201 stream.Skip( 4 );
202
203 uint16_t count = stream.ReadU16();
204 std::vector<std::string> names;
205 names.reserve( count );
206
207 for( uint16_t i = 0; i < count; ++i )
208 {
209 names.push_back( stream.ReadLzt() );
210
211 if( stream.ReadU16() != 9 )
212 THROW_IO_ERROR( wxS( "OrCAD Views Directory: invalid folder record" ) );
213
214 stream.Skip( 20 );
215 }
216
217 if( !stream.AtEnd() )
218 THROW_IO_ERROR( wxS( "OrCAD Views Directory: trailing data" ) );
219
220 return names;
221}
222
223
224// Occurrence headers always use one long prefix, regardless of the normal type depth.
225static uint8_t readOccHeader( ORCAD_STREAM& aStream, const std::vector<std::string>& aStrings, int aExpectType,
226 std::map<std::string, std::string>* aProps = nullptr )
227{
228 ORCAD_STRUCT_READER reader( aStream, &aStrings );
229 ORCAD_PREFIXES prefixes = reader.ReadPrefixes( aExpectType, ORCAD_STREAM::npos, 1 );
230
231 if( aProps )
232 *aProps = reader.PropsDict( prefixes );
233
234 return static_cast<uint8_t>( prefixes.typeId );
235}
236
237
238static ORCAD_OCC_SCOPE readOccScope( ORCAD_STREAM& aStream, const std::vector<std::string>& aStrings );
239
240
241static void readOccurrence( ORCAD_STREAM& aStream, const std::vector<std::string>& aStrings, ORCAD_OCC_SCOPE& aScope )
242{
243 std::map<std::string, std::string> props;
244 readOccHeader( aStream, aStrings, 0x42, &props );
245
246 aStream.ReadU32();
247 uint32_t blockObjectId = aStream.ReadU32();
248
249 aStream.ExpectByte( 0x42, wxS( "occurrence inner marker" ) );
250
251 aStream.ReadU32(); // C (symbol-complexity correlated)
252 aStream.ReadU32(); // D (zero observed)
253
254 std::string child = aStream.ReadLzt(); // child folder name; empty for parts
255 std::string ref = aStream.ReadLzt(); // occurrence refdes; empty when none
256
257 uint32_t unitRefIdx = aStream.ReadU32();
258
259 uint16_t pinCount = aStream.ReadU16();
260
261 for( uint16_t i = 0; i < pinCount; i++ )
262 {
263 readOccHeader( aStream, aStrings, -1 ); // 0x44 scalar / 0x45 bus pin occurrence
264 aStream.ReadU32(); // pin occurrence db id
265 aStream.ReadU16(); // pin index
266 }
267
268 ORCAD_OCC_SCOPE nested = readOccScope( aStream, aStrings );
269
270 if( !child.empty() )
271 {
272 ORCAD_OCC_BLOCK block;
273 block.targetDbId = blockObjectId;
274 block.childFolder = child;
275 block.scope = std::move( nested );
276 aScope.blocks.push_back( std::move( block ) );
277 }
278 else
279 {
280 if( !ref.empty() )
281 aScope.partRefs[blockObjectId] = ref;
282
283 if( !props.empty() )
284 aScope.partProps[blockObjectId] = std::move( props );
285
286 if( unitRefIdx < aStrings.size() && !aStrings[unitRefIdx].empty() )
287 {
288 aScope.partUnitRefs[blockObjectId] = aStrings[unitRefIdx];
289 }
290 }
291}
292
293
294// Scope = net occ (0x43), title-block occ (0x52), global/off-page occ (0x5b, u32-counted),
295// optional separator preamble, then part/block occ; only part/block kept, rest read to stay aligned.
296static ORCAD_OCC_SCOPE readOccScope( ORCAD_STREAM& aStream, const std::vector<std::string>& aStrings )
297{
298 ORCAD_STREAM::NEST_GUARD guard( aStream, wxS( "occurrence scope" ) );
299
300 ORCAD_OCC_SCOPE scope;
301
302 uint16_t netCount = aStream.ReadU16();
303
304 for( uint16_t i = 0; i < netCount; i++ )
305 {
306 readOccHeader( aStream, aStrings, 0x43 );
307 uint32_t occurrenceId = aStream.ReadU32();
308 std::string name = aStream.ReadLzt();
309 scope.netNames[occurrenceId] = std::move( name );
310 }
311
312 uint16_t titleBlockCount = aStream.ReadU16();
313
314 for( uint16_t i = 0; i < titleBlockCount; i++ )
315 {
316 readOccHeader( aStream, aStrings, 0x52 );
317 aStream.ReadU32();
318 aStream.ReadU32();
319 }
320
321 uint32_t globalCount = aStream.ReadU32();
322
323 for( uint32_t i = 0; i < globalCount; i++ )
324 {
325 readOccHeader( aStream, aStrings, 0x5b );
326 aStream.ReadU32();
327 aStream.ReadU32();
328 }
329
330 if( aStream.AtPreamble() )
331 {
332 aStream.Skip( 4 );
333 aStream.Skip( aStream.ReadU32() );
334 }
335
336 uint16_t occCount = aStream.ReadU16();
337
338 for( uint16_t i = 0; i < occCount; i++ )
339 readOccurrence( aStream, aStrings, scope );
340
341 return scope;
342}
343
344
345// Legacy structures use short prefixes and u16 string indices. See orcad_dsn.ksy for layouts.
346
347static std::string v2Resolve( const std::vector<std::string>& aStrings, uint16_t aIdx )
348{
349 return ( aIdx != 0xFFFF && aIdx < aStrings.size() ) ? aStrings[aIdx] : std::string();
350}
351
352
353// Version 1 display properties use a 10-byte body. Keep the mode local to each loading thread.
354static thread_local bool g_v2ShortDisplayProp = false;
355
356
358{
359public:
360 explicit V2_DISPLAY_PROP_SCOPE( bool aShort ) :
362 {
363 g_v2ShortDisplayProp = aShort;
364 }
365
367
368private:
370};
371
372
373// Reject repeat count that cannot fit remaining bytes or exceeds ceiling, so mis-parsed count
374// throws (stream skipped) not runaway allocation.
375static void v2CheckCount( ORCAD_STREAM& aStream, uint32_t aCount )
376{
377 if( aCount > 30000 || aCount > aStream.Remaining() )
378 {
379 THROW_IO_ERRORF( wxS( "v2 repeat count %u exceeds the sane bound (%zu bytes remain)" ), aCount,
380 aStream.Remaining() );
381 }
382}
383
384
385// v2 short prefix; fills aProps w/ resolved name/value pairs (empty value when index 0xFFFF).
386static uint8_t v2Prefix( ORCAD_STREAM& aStream, const std::vector<std::string>& aStrings,
387 std::map<std::string, std::string>* aProps = nullptr )
388{
389 uint8_t type = aStream.ReadU8();
390 int16_t count = aStream.ReadI16();
391
392 if( count > 1000 )
393 THROW_IO_ERRORF( wxS( "v2 prefix: absurd property count %d" ), count );
394
395 for( int i = 0; i < count; i++ )
396 {
397 uint16_t nameIdx = aStream.ReadU16();
398 uint16_t valueIdx = aStream.ReadU16();
399
400 if( aProps )
401 ( *aProps )[v2Resolve( aStrings, nameIdx )] = v2Resolve( aStrings, valueIdx );
402 }
403
404 return type;
405}
406
407
408static ORCAD_DISPLAY_PROP v2DisplayProp( ORCAD_STREAM& aStream, const std::vector<std::string>& aStrings )
409{
410 v2Prefix( aStream, aStrings );
411
413 prop.nameIdx = aStream.ReadU16();
414 prop.name = v2Resolve( aStrings, static_cast<uint16_t>( prop.nameIdx ) );
415 prop.x = aStream.ReadI16();
416 prop.y = aStream.ReadI16();
417
418 uint16_t rotFont = aStream.ReadU16();
419 prop.fontIdx = rotFont & 0x3FFF;
420 prop.rotation = ( rotFont >> 14 ) & 0x3;
421
423 {
424 aStream.ReadU8();
425 prop.color = aStream.ReadU8();
426 prop.dispMode = 0x100;
427 }
428 else
429 {
430 prop.color = aStream.ReadU8();
431 prop.dispMode = aStream.ReadU16();
432 aStream.ExpectByte( 0x00, wxS( "v2 display prop terminator" ) );
433 }
434
435 return prop;
436}
437
438
439static std::vector<ORCAD_DISPLAY_PROP> v2DisplayPropList( ORCAD_STREAM& aStream,
440 const std::vector<std::string>& aStrings )
441{
442 std::vector<ORCAD_DISPLAY_PROP> props;
443 uint16_t count = aStream.ReadU16();
444
445 for( uint16_t i = 0; i < count; i++ )
446 props.push_back( v2DisplayProp( aStream, aStrings ) );
447
448 return props;
449}
450
451
452static ORCAD_ALIAS v2Alias( ORCAD_STREAM& aStream, const std::vector<std::string>& aStrings )
453{
454 v2Prefix( aStream, aStrings );
455
456 ORCAD_ALIAS alias;
457 alias.x = aStream.ReadI32();
458 alias.y = aStream.ReadI32();
459 alias.color = aStream.ReadU32();
460 alias.rotation = aStream.ReadU32() & 0x3;
461 alias.fontIdx = aStream.ReadU32();
462 alias.name = aStream.ReadLzt();
463
464 return alias;
465}
466
467
468static void v2Structure( ORCAD_STREAM& aStream, const std::vector<std::string>& aStrings );
469
470
471static ORCAD_WIRE v2Wire( ORCAD_STREAM& aStream, const std::vector<std::string>& aStrings )
472{
473 ORCAD_STREAM::NEST_GUARD guard( aStream, wxS( "v2 wire" ) );
474
475 uint8_t type = v2Prefix( aStream, aStrings );
476
477 ORCAD_WIRE wire;
478 wire.dbId = aStream.ReadU32();
479 wire.id = aStream.ReadU32(); // net db id, keys page net table
480 wire.color = static_cast<int>( aStream.ReadU32() );
481 wire.x1 = aStream.ReadI32();
482 wire.y1 = aStream.ReadI32();
483 wire.x2 = aStream.ReadI32();
484 wire.y2 = aStream.ReadI32();
485 wire.isBus = type == ORCAD_ST_WIRE_BUS;
486 aStream.Skip( 1 );
487
488 uint16_t aliasCount = aStream.ReadU16();
489
490 for( uint16_t i = 0; i < aliasCount; i++ )
491 wire.aliases.push_back( v2Alias( aStream, aStrings ) );
492
493 uint16_t propCount = aStream.ReadU16();
494
495 for( uint16_t i = 0; i < propCount; i++ )
496 v2Structure( aStream, aStrings ); // framed wire properties, consumed in full
497
498 return wire;
499}
500
501
502static ORCAD_PIN_INST v2PinInst( ORCAD_STREAM& aStream, const std::vector<std::string>& aStrings )
503{
504 v2Prefix( aStream, aStrings );
505
507 pin.pinIndex = aStream.ReadI16();
508 pin.x = aStream.ReadI16();
509 pin.y = aStream.ReadI16();
510 pin.wordA = aStream.ReadU32();
511 pin.wordB = aStream.ReadU32();
512 pin.displayProps = v2DisplayPropList( aStream, aStrings );
513
514 return pin;
515}
516
517
518static ORCAD_PLACED_INSTANCE v2PlacedInstance( ORCAD_STREAM& aStream, const std::vector<std::string>& aStrings )
519{
521 v2Prefix( aStream, aStrings, &inst.props );
522
523 aStream.ReadU16();
524 inst.sourceLibrary = v2Resolve( aStrings, aStream.ReadU16() );
525 aStream.Skip( 4 );
526 inst.pkgName = aStream.ReadLzt();
527 inst.dbId = aStream.ReadU32();
528
529 int by1 = aStream.ReadI16();
530 int bx1 = aStream.ReadI16();
531 int by2 = aStream.ReadI16();
532 int bx2 = aStream.ReadI16();
533 inst.bbox.x1 = bx1;
534 inst.bbox.y1 = by1;
535 inst.bbox.x2 = bx2;
536 inst.bbox.y2 = by2;
537
538 inst.x = aStream.ReadI16();
539 inst.y = aStream.ReadI16();
540 inst.color = aStream.ReadU8();
541
542 uint8_t orientation = aStream.ReadU8();
543 inst.rotation = orientation & 0x3;
544 inst.mirror = ( orientation & 0x4 ) != 0;
545 inst.partIndex = aStream.ReadU8();
546 inst.partByte = aStream.ReadU8();
547
548 inst.displayProps = v2DisplayPropList( aStream, aStrings );
549 aStream.Skip( 1 );
550 inst.reference = aStream.ReadLzt();
551 inst.value = v2Resolve( aStrings, aStream.ReadU16() ); // v2 u16 value index
552 aStream.Skip( 6 ); // v2 6 bytes (modern 10)
553
554 uint16_t pinCount = aStream.ReadU16();
555
556 for( uint16_t i = 0; i < pinCount; i++ )
557 inst.pins.push_back( v2PinInst( aStream, aStrings ) );
558
559 inst.sourcePackage = aStream.ReadLzt();
560 inst.unitIndex = aStream.ReadU16();
561
562 return inst;
563}
564
565
566static ORCAD_PRIMITIVE v2PrimBody( ORCAD_STREAM& aStream, uint8_t aType );
567
568
570{
571 return v2PrimBody( aStream, aStream.ReadU8() );
572}
573
574
575static ORCAD_SYMBOL_DEF v2SymbolDef( ORCAD_STREAM& aStream, const std::vector<std::string>& aStrings )
576{
577 v2Prefix( aStream, aStrings );
578
581 def.name = aStream.ReadLzt();
582 def.sourceLib = aStream.ReadLzt();
583 def.color = static_cast<int>( aStream.ReadU32() );
584
585 uint16_t primCount = aStream.ReadU16();
586
587 for( uint16_t i = 0; i < primCount; i++ )
588 def.primitives.push_back( v2Primitive( aStream ) );
589
591 {
592 ORCAD_BBOX bbox;
593 bbox.x1 = aStream.ReadI16();
594 bbox.y1 = aStream.ReadI16();
595 bbox.x2 = aStream.ReadI16();
596 bbox.y2 = aStream.ReadI16();
597 def.bbox = bbox;
598 }
599
600 return def;
601}
602
603
604static ORCAD_PRIMITIVE v2PrimBody( ORCAD_STREAM& aStream, uint8_t aType )
605{
606 ORCAD_STREAM::NEST_GUARD guard( aStream, wxS( "v2 primitive" ) );
607
608 uint8_t type = aType;
609 ORCAD_PRIMITIVE prim;
610
611 switch( type )
612 {
613 case ORCAD_PRIM_RECT:
616 prim.x1 = aStream.ReadI32();
617 prim.y1 = aStream.ReadI32();
618 prim.x2 = aStream.ReadI32();
619 prim.y2 = aStream.ReadI32();
620 prim.lineStyle = aStream.ReadU32();
621 prim.lineWidth = aStream.ReadU32();
622 prim.fillStyle = aStream.ReadU32();
623 prim.hatchStyle = aStream.ReadU32();
624 break;
625
626 case ORCAD_PRIM_LINE:
628 prim.x1 = aStream.ReadI32();
629 prim.y1 = aStream.ReadI32();
630 prim.x2 = aStream.ReadI32();
631 prim.y2 = aStream.ReadI32();
632 prim.lineStyle = aStream.ReadU32();
633 prim.lineWidth = aStream.ReadU32();
634 break;
635
636 case ORCAD_PRIM_ARC:
638 prim.x1 = aStream.ReadI32();
639 prim.y1 = aStream.ReadI32();
640 prim.x2 = aStream.ReadI32();
641 prim.y2 = aStream.ReadI32();
642 prim.start = ORCAD_POINT{ aStream.ReadI32(), aStream.ReadI32() };
643 prim.end = ORCAD_POINT{ aStream.ReadI32(), aStream.ReadI32() };
644 prim.lineStyle = aStream.ReadU32();
645 prim.lineWidth = aStream.ReadU32();
646 break;
647
651 {
655 prim.lineStyle = aStream.ReadU32();
656 prim.lineWidth = aStream.ReadU32();
657
658 if( type == ORCAD_PRIM_POLYGON )
659 {
660 prim.fillStyle = aStream.ReadU32();
661 prim.hatchStyle = aStream.ReadU32();
662 }
663
664 uint16_t count = aStream.ReadU16();
665 v2CheckCount( aStream, count );
666
667 for( uint16_t i = 0; i < count; i++ )
668 {
669 int y = aStream.ReadI16(); // points are stored y-first
670 int x = aStream.ReadI16();
671 prim.points.push_back( ORCAD_POINT{ x, y } );
672 }
673
674 break;
675 }
676
678 {
679 // Nested graphic doubled type byte, prefix, i16 location, nested prims each padded (u8
680 // type, u8 0x00); flatten not meaningful, so read to stay aligned and drop.
681 aStream.ExpectByte( ORCAD_PRIM_SYMBOL_VECTOR, wxS( "v2 symbol vector pair" ) );
682
683 int16_t nProp = aStream.ReadI16();
684
685 for( int i = 0; i < std::max<int>( nProp, 0 ); i++ )
686 {
687 aStream.ReadU16();
688 aStream.ReadU16();
689 }
690
692 prim.x1 = aStream.ReadI16();
693 prim.y1 = aStream.ReadI16();
694
695 uint16_t nested = aStream.ReadU16();
696 v2CheckCount( aStream, nested );
697
698 for( uint16_t i = 0; i < nested; i++ )
699 {
700 uint8_t nestedType = aStream.ReadU8();
701 aStream.ExpectByte( 0x00, wxS( "v2 vector prim pad" ) );
702 prim.children.push_back( v2PrimBody( aStream, nestedType ) );
703 }
704
705 aStream.ReadLzt(); // vector name
706 break;
707 }
708
711 prim.x1 = aStream.ReadI32();
712 prim.y1 = aStream.ReadI32();
713 prim.x2 = aStream.ReadI32();
714 prim.y2 = aStream.ReadI32();
715 prim.textBoundsStart = ORCAD_POINT{ aStream.ReadI32(), aStream.ReadI32() };
716 prim.fontIdx = aStream.ReadU16();
717 aStream.Skip( 2 );
718 prim.text = aStream.ReadLzt();
719 break;
720
722 {
724 prim.x1 = aStream.ReadI32();
725 prim.y1 = aStream.ReadI32();
726 prim.x2 = aStream.ReadI32();
727 prim.y2 = aStream.ReadI32();
728 aStream.Skip( 8 ); // duplicate corner
729 aStream.Skip( 8 ); // pixel width/height
730
731 uint32_t dataSize = aStream.ReadU32();
732
733 if( dataSize > aStream.Remaining() )
734 {
735 THROW_IO_ERRORF( wxS( "v2 bitmap: payload %u exceeds the %zu bytes left" ), dataSize, aStream.Remaining() );
736 }
737
738 prim.data = aStream.ReadBytes( dataSize );
739 break;
740 }
741
742 default: THROW_IO_ERRORF( wxS( "v2 primitive: unhandled type %d at 0x%zx" ), (int) type, aStream.GetOffset() - 1 );
743 }
744
745 return prim;
746}
747
748
749static ORCAD_GRAPHIC_INST v2GraphicInst( ORCAD_STREAM& aStream, const std::vector<std::string>& aStrings )
750{
752 inst.typeId = v2Prefix( aStream, aStrings, &inst.props );
753
754 uint16_t nameIdx = aStream.ReadU16();
755 uint16_t sourceLibraryIdx = aStream.ReadU16();
756 aStream.Skip( 4 ); // uninitialized junk
757 inst.logicalName = v2Resolve( aStrings, nameIdx );
758 inst.name = aStream.ReadLzt();
759
760 if( std::string sourceLibrary = v2Resolve( aStrings, sourceLibraryIdx ); !sourceLibrary.empty() )
761 inst.props["Source Library"] = std::move( sourceLibrary );
762
763 inst.dbId = aStream.ReadU32();
764
765 inst.y = aStream.ReadI16();
766 inst.x = aStream.ReadI16();
767 int y2 = aStream.ReadI16();
768 int x2 = aStream.ReadI16();
769 int x1 = aStream.ReadI16();
770 int y1 = aStream.ReadI16();
771 inst.bbox.x1 = x1;
772 inst.bbox.y1 = y1;
773 inst.bbox.x2 = x2;
774 inst.bbox.y2 = y2;
775
776 inst.color = aStream.ReadU8();
777 uint8_t orientation = aStream.ReadU8();
778 inst.rotation = orientation & 0x3;
779 inst.mirror = ( orientation & 0x4 ) != 0;
780 aStream.Skip( 2 );
781
782 inst.displayProps = v2DisplayPropList( aStream, aStrings );
783
784 uint8_t flag = aStream.ReadU8();
785
786 if( flag == 0x02 )
787 inst.nested = std::make_unique<ORCAD_SYMBOL_DEF>( v2SymbolDef( aStream, aStrings ) );
788
789 return inst;
790}
791
792
793static void v2Structure( ORCAD_STREAM& aStream, const std::vector<std::string>& aStrings )
794{
795 switch( aStream.PeekU8() )
796 {
797 case ORCAD_ST_STH_IN_PAGES0: v2SymbolDef( aStream, aStrings ); break;
798 case ORCAD_ST_SYMBOL_DISPLAY_PROP: v2DisplayProp( aStream, aStrings ); break;
799 case ORCAD_ST_ALIAS: v2Alias( aStream, aStrings ); break;
800 case ORCAD_ST_T0X10:
801 case ORCAD_ST_T0X11: v2PinInst( aStream, aStrings ); break;
802 case ORCAD_ST_PLACED_INSTANCE: v2PlacedInstance( aStream, aStrings ); break;
804 case ORCAD_ST_WIRE_BUS: v2Wire( aStream, aStrings ); break;
805 default: THROW_IO_ERRORF( wxS( "v2 nested structure: unhandled type %d" ), aStream.PeekU8() );
806 }
807}
808
809
810// -- v2.0 .OLB symbol-library streams -----------------------------------------------
811
812static ORCAD_PORT_TYPE v2PortType( uint32_t aRaw )
813{
814 return aRaw <= 7 ? static_cast<ORCAD_PORT_TYPE>( aRaw ) : ORCAD_PORT_TYPE::PASSIVE;
815}
816
817
818// One symbol pin (type 26 scalar / 27 bus); lone 0x00 = skipped slot
819static bool v2SymbolPin( ORCAD_STREAM& aStream, const std::vector<std::string>& aStrings, ORCAD_SYMBOL_PIN& aOut )
820{
821 if( aStream.PeekU8() == 0x00 )
822 {
823 aStream.Skip( 1 );
824 return false;
825 }
826
827 uint8_t type = v2Prefix( aStream, aStrings );
828
829 aOut.name = aStream.ReadLzt();
830 aOut.startX = aStream.ReadI32();
831 aOut.startY = aStream.ReadI32();
832 aOut.hotptX = aStream.ReadI32();
833 aOut.hotptY = aStream.ReadI32();
834 aOut.shapeBits = aStream.ReadU16();
835 aStream.Skip( 2 );
836 aOut.portType = v2PortType( aStream.ReadU32() );
837 aStream.ExpectByte( type, wxS( "v2 pin type echo" ) );
838 aStream.Skip( 3 );
839
840 uint16_t dispCount = aStream.ReadU16();
841 v2CheckCount( aStream, dispCount );
842
843 for( uint16_t i = 0; i < dispCount; i++ )
844 aOut.displayProps.push_back( v2DisplayProp( aStream, aStrings ) );
845
846 return true;
847}
848
849
850// Symbol-def body shared by Symbols streams and inline LibraryParts (read after prefix).
851static ORCAD_SYMBOL_DEF v2LibSymbolDef( ORCAD_STREAM& aStream, const std::vector<std::string>& aStrings, int aTypeId )
852{
854 def.typeId = aTypeId;
855 def.name = aStream.ReadLzt();
856 def.sourceLib = aStream.ReadLzt();
857 def.color = static_cast<int>( aStream.ReadU32() );
858
859 uint16_t primCount = aStream.ReadU16();
860 v2CheckCount( aStream, primCount );
861
862 for( uint16_t i = 0; i < primCount; i++ )
863 def.primitives.push_back( v2Primitive( aStream ) );
864
865 ORCAD_BBOX bbox;
866 bbox.x1 = aStream.ReadI16();
867 bbox.y1 = aStream.ReadI16();
868 bbox.x2 = aStream.ReadI16();
869 bbox.y2 = aStream.ReadI16();
870 def.bbox = bbox;
871
872 uint16_t pinCount = aStream.ReadU16();
873 v2CheckCount( aStream, pinCount );
874
875 for( uint16_t i = 0; i < pinCount; i++ )
876 {
878
879 if( v2SymbolPin( aStream, aStrings, pin ) )
880 def.pins.push_back( std::move( pin ) );
881 }
882
883 uint16_t dispCount = aStream.ReadU16();
884 v2CheckCount( aStream, dispCount );
885
886 for( uint16_t i = 0; i < dispCount; i++ )
887 v2DisplayProp( aStream, aStrings );
888
889 return def;
890}
891
892
893static void v2LibraryPartTail( ORCAD_STREAM& aStream, ORCAD_SYMBOL_DEF& aDef )
894{
895 std::string implementationPath = aStream.ReadLzt();
896
897 if( !implementationPath.empty() )
898 aDef.props["Implementation Path"] = std::move( implementationPath );
899
900 aStream.ReadLzt(); // implementation (PSpice model)
901 aStream.ReadLzt(); // reference prefix
902 aStream.ReadLzt(); // part value
903 aDef.generalFlags = aStream.ReadU16();
904}
905
906
907// One part cell (type 6); views + one inline LibraryPart per view w/ GeneralProperties tail.
909{
910 std::string name;
911 std::vector<ORCAD_SYMBOL_DEF> symbols;
912 std::vector<std::string> viewNames;
913 std::string refDesPrefix;
914 std::map<std::string, std::string> props;
915};
916
917
918static V2_PART_CELL v2PartCell( ORCAD_STREAM& aStream, const std::vector<std::string>& aStrings )
919{
920 std::map<std::string, std::string> cellProps;
921 v2Prefix( aStream, aStrings, &cellProps ); // type 6
922
923 V2_PART_CELL cell;
924 cell.props = cellProps;
925 cell.name = aStream.ReadLzt();
926 aStream.ReadLzt(); // source library
927
928 uint16_t viewCount = aStream.ReadU16();
929 v2CheckCount( aStream, viewCount );
930
931 for( uint16_t i = 0; i < viewCount; i++ )
932 cell.viewNames.push_back( aStream.ReadLzt() );
933
934 uint16_t symbolCount = aStream.ReadU16();
935 v2CheckCount( aStream, symbolCount );
936
937 for( uint16_t i = 0; i < symbolCount; i++ )
938 {
939 std::map<std::string, std::string> symbolProps;
940 v2Prefix( aStream, aStrings, &symbolProps ); // type 24 LibraryPart
941 ORCAD_SYMBOL_DEF def = v2LibSymbolDef( aStream, aStrings, ORCAD_ST_LIBRARY_PART );
942 def.props = cell.props;
943 for( const auto& [name, value] : symbolProps )
944 def.props[name] = value;
945
946 std::string implementationPath = aStream.ReadLzt();
947
948 if( !implementationPath.empty() )
949 def.props["Implementation Path"] = std::move( implementationPath );
950
951 aStream.ReadLzt(); // implementation (PSpice model)
952 cell.refDesPrefix = aStream.ReadLzt();
953 aStream.ReadLzt(); // part value
954 def.generalFlags = aStream.ReadU16(); // pin number/name visibility bits
955
956 cell.symbols.push_back( std::move( def ) );
957 }
958
959 return cell;
960}
961
962
963static ORCAD_DEVICE v2Device( ORCAD_STREAM& aStream, const std::vector<std::string>& aStrings )
964{
965 v2Prefix( aStream, aStrings ); // type 32
966
967 ORCAD_DEVICE dev;
968 dev.unitRef = aStream.ReadLzt();
969 dev.refDes = aStream.ReadLzt(); // part name selecting part cell
970
971 uint16_t pinCount = aStream.ReadU16();
972 v2CheckCount( aStream, pinCount );
973
974 static const uint8_t emptySlot[2] = { 0xFF, 0xFF };
975
976 for( uint16_t i = 0; i < pinCount; i++ )
977 {
978 if( aStream.PeekMatches( emptySlot, 2 ) )
979 {
980 aStream.Skip( 2 );
981 dev.pinNumbers.push_back( std::string() );
982 dev.pinIgnore.push_back( true );
983 continue;
984 }
985
986 dev.pinNumbers.push_back( aStream.ReadLzt() );
987 dev.pinIgnore.push_back( ( aStream.ReadU8() & 0x80 ) != 0 );
988 }
989
990 return dev;
991}
992
993
994static ORCAD_PACKAGE v2Package( ORCAD_STREAM& aStream, const std::vector<std::string>& aStrings )
995{
996 ORCAD_PACKAGE pkg;
997 v2Prefix( aStream, aStrings, &pkg.props ); // type 31
998 pkg.name = aStream.ReadLzt();
999 pkg.sourceLib = aStream.ReadLzt();
1000 pkg.refDes = aStream.ReadLzt();
1001 aStream.ReadLzt(); // unknown
1002 pkg.pcbFootprint = aStream.ReadLzt();
1003
1004 uint16_t deviceCount = aStream.ReadU16();
1005 v2CheckCount( aStream, deviceCount );
1006
1007 for( uint16_t i = 0; i < deviceCount; i++ )
1008 pkg.devices.push_back( v2Device( aStream, aStrings ) );
1009
1010 return pkg;
1011}
1012
1013
1014static ORCAD_DRAWN_INSTANCE v2DrawnInstance( ORCAD_STREAM& aStream, const std::vector<std::string>& aStrings )
1015{
1017 v2Prefix( aStream, aStrings, &block.props );
1018
1019 uint16_t nameIdx = aStream.ReadU16();
1020 aStream.ReadU16(); // source library string index
1021 aStream.Skip( 4 ); // uninitialized header bytes
1022 aStream.ReadLzt(); // name
1023
1024 block.name = v2Resolve( aStrings, nameIdx );
1025 block.dbId = aStream.ReadU32();
1026
1027 aStream.ReadI16(); // anchor y
1028 aStream.ReadI16(); // anchor x
1029 aStream.ReadI16(); // bbox y2
1030 aStream.ReadI16(); // bbox x2
1031 block.x1 = aStream.ReadI16();
1032 block.y1 = aStream.ReadI16();
1033 aStream.ReadU8(); // color
1034 uint8_t orientation = aStream.ReadU8();
1035 aStream.Skip( 2 ); // structure id
1036
1037 block.displayProps = v2DisplayPropList( aStream, aStrings );
1038 aStream.ExpectByte( ORCAD_ST_LIBRARY_PART, wxS( "v2 drawn instance nested flag" ) );
1039
1040 std::map<std::string, std::string> nestedProps;
1041 uint8_t nestedType = v2Prefix( aStream, aStrings, &nestedProps );
1042
1043 if( nestedType != ORCAD_ST_LIBRARY_PART )
1044 {
1045 THROW_IO_ERRORF( wxS( "v2 drawn instance: unexpected nested type %d" ), static_cast<int>( nestedType ) );
1046 }
1047
1048 ORCAD_SYMBOL_DEF nested = v2LibSymbolDef( aStream, aStrings, nestedType );
1049 block.props.insert( nestedProps.begin(), nestedProps.end() );
1050 ORCAD_BBOX bbox = nested.bbox.value_or( ORCAD_BBOX() );
1051 bool quarterTurn = ( orientation & 1 ) != 0;
1052 block.w = quarterTurn ? bbox.y2 - bbox.y1 : bbox.x2 - bbox.x1;
1053 block.h = quarterTurn ? bbox.x2 - bbox.x1 : bbox.y2 - bbox.y1;
1054
1055 aStream.Skip( 14 );
1056 block.reference = aStream.ReadLzt();
1057 uint16_t valueIdx = aStream.ReadU16();
1058 aStream.ReadU16();
1059 uint16_t implementationIdx = aStream.ReadU16();
1060 aStream.ReadU16();
1061 block.props["Value"] = v2Resolve( aStrings, valueIdx );
1062 block.props["Implementation"] = v2Resolve( aStrings, implementationIdx );
1063
1064 uint16_t pinCount = aStream.ReadU16();
1065 v2CheckCount( aStream, pinCount );
1066
1067 std::vector<ORCAD_PIN_INST> pinInsts;
1068
1069 for( uint16_t i = 0; i < pinCount; i++ )
1070 pinInsts.push_back( v2PinInst( aStream, aStrings ) );
1071
1072 std::set<std::pair<int, int>> placedPoints;
1073 std::set<std::pair<int, int>> definitionPoints;
1074
1075 for( const ORCAD_PIN_INST& pin : pinInsts )
1076 placedPoints.emplace( pin.x, pin.y );
1077
1078 for( const ORCAD_SYMBOL_PIN& pin : nested.pins )
1079 definitionPoints.emplace( pin.hotptX, pin.hotptY );
1080
1081 bool useDefinitionGeometry = placedPoints.size() <= 1 && definitionPoints.size() > 1;
1082
1083 for( size_t i = 0; i < pinInsts.size() && i < nested.pins.size(); i++ )
1084 {
1086 pin.name = nested.pins[i].name;
1087 pin.portType = nested.pins[i].portType;
1088 pin.x = useDefinitionGeometry ? block.x1 + nested.pins[i].hotptX - bbox.x1 : pinInsts[i].x;
1089 pin.y = useDefinitionGeometry ? block.y1 + nested.pins[i].hotptY - bbox.y1 : pinInsts[i].y;
1090 pin.noConnect = pinInsts[i].IsNoConnect();
1091 block.pins.push_back( std::move( pin ) );
1092 }
1093
1094 return block;
1095}
1096
1097
1098ORCAD_RAW_PAGE OrcadParsePageV2( const std::vector<char>& aData, const std::vector<std::string>& aStrings,
1099 const ORCAD_WARN_FN& /* aWarn */, bool aShortDisplayProp )
1100{
1101 V2_DISPLAY_PROP_SCOPE displayPropScope( aShortDisplayProp );
1102 ORCAD_STREAM stream( aData );
1103 ORCAD_RAW_PAGE page;
1104
1105 uint8_t type = v2Prefix( stream, aStrings, &page.props );
1106
1107 if( type != ORCAD_ST_PAGE )
1108 THROW_IO_ERRORF( wxS( "v2 page: unexpected root type %d" ), (int) type );
1109
1110 page.name = stream.ReadLzt();
1111 page.pageSize = stream.ReadLzt();
1112
1113 ORCAD_PAGE_SETTINGS settings = OrcadParsePageSettings( stream );
1114 page.width = settings.width;
1115 page.height = settings.height;
1116 page.isMetric = settings.isMetric;
1117 page.horizontalCount = settings.horizontalCount;
1118 page.verticalCount = settings.verticalCount;
1119 page.horizontalWidth = settings.horizontalWidth;
1120 page.verticalWidth = settings.verticalWidth;
1121 page.horizontalChar = settings.horizontalChar;
1123 page.verticalChar = settings.verticalChar;
1124 page.verticalAscending = settings.verticalAscending;
1125 page.borderPrinted = settings.borderPrinted;
1126 page.gridRefPrinted = settings.gridRefPrinted;
1127 page.createTimestamp = settings.createTimestamp;
1128 page.modifyTimestamp = settings.modifyTimestamp;
1129
1130 uint16_t titleBlockCount = stream.ReadU16();
1131
1132 for( uint16_t i = 0; i < titleBlockCount; i++ )
1133 {
1134 page.titleBlocks.push_back( v2GraphicInst( stream, aStrings ) );
1135 stream.Skip( 12 ); // title-block trailer
1136 }
1137
1138 uint16_t allNetCount = stream.ReadU16(); // every net on page (name empty)
1139
1140 for( uint16_t i = 0; i < allNetCount; i++ )
1141 {
1142 stream.ReadU32();
1143 stream.ReadLzt();
1144 }
1145
1146 uint16_t netGroupCount = stream.ReadU16();
1147
1148 for( uint16_t i = 0; i < netGroupCount; i++ )
1149 {
1151 group.id = stream.ReadU32();
1152 group.name = stream.ReadLzt();
1153
1154 uint16_t memberCount = stream.ReadU16();
1155 v2CheckCount( stream, memberCount );
1156
1157 for( uint16_t j = 0; j < memberCount; j++ )
1158 group.members.push_back( stream.ReadU32() );
1159
1160 page.netGroups.push_back( std::move( group ) );
1161 }
1162
1163 uint16_t netmapCount = stream.ReadU16();
1164
1165 for( uint16_t i = 0; i < netmapCount; i++ )
1166 {
1167 std::string netName = stream.ReadLzt();
1168 uint32_t netId = stream.ReadU32();
1169 page.netAliases[netId].push_back( netName );
1170 page.netmap[netId] = std::move( netName );
1171 }
1172
1173 uint16_t wireCount = stream.ReadU16();
1174
1175 for( uint16_t i = 0; i < wireCount; i++ )
1176 page.wires.push_back( v2Wire( stream, aStrings ) );
1177
1178 uint16_t instanceCount = stream.ReadU16();
1179
1180 for( uint16_t i = 0; i < instanceCount; i++ )
1181 {
1182 if( stream.PeekU8() == ORCAD_ST_DRAWN_INSTANCE )
1183 page.blocks.push_back( v2DrawnInstance( stream, aStrings ) );
1184 else
1185 page.instances.push_back( v2PlacedInstance( stream, aStrings ) );
1186 }
1187
1188 uint16_t portCount = stream.ReadU16();
1189
1190 for( uint16_t i = 0; i < portCount; i++ )
1191 {
1192 page.ports.push_back( v2GraphicInst( stream, aStrings ) );
1193 stream.Skip( 9 );
1194 }
1195
1196 uint16_t globalCount = stream.ReadU16();
1197
1198 for( uint16_t i = 0; i < globalCount; i++ )
1199 {
1200 page.globals.push_back( v2GraphicInst( stream, aStrings ) );
1201 stream.Skip( 5 );
1202 }
1203
1204 uint16_t offPageCount = stream.ReadU16();
1205
1206 for( uint16_t i = 0; i < offPageCount; i++ )
1207 {
1208 page.offpage.push_back( v2GraphicInst( stream, aStrings ) );
1209 stream.Skip( 5 );
1210 }
1211
1212 uint16_t ercCount = stream.ReadU16();
1213
1214 for( uint16_t i = 0; i < ercCount; i++ )
1215 {
1216 page.ercObjects.push_back( v2GraphicInst( stream, aStrings ) );
1217 stream.ReadLzt();
1218 stream.ReadLzt();
1219 stream.ReadLzt();
1220 }
1221
1222 uint16_t busEntryCount = stream.ReadU16();
1223
1224 for( uint16_t i = 0; i < busEntryCount; i++ )
1225 {
1226 v2Prefix( stream, aStrings );
1227 page.busEntries.push_back( OrcadReadBusEntryBody( stream ) );
1228 }
1229
1230 uint16_t graphicCount = stream.ReadU16();
1231
1232 for( uint16_t i = 0; i < graphicCount; i++ )
1233 page.graphics.push_back( v2GraphicInst( stream, aStrings ) );
1234
1235 return page;
1236}
1237
1238
1239void OrcadParseOlbSymbolStreamV2( const std::vector<char>& aData, const std::vector<std::string>& aStrings,
1240 std::map<std::string, ORCAD_SYMBOL_DEF>& aSymbols, bool aShortDisplayProp )
1241{
1242 V2_DISPLAY_PROP_SCOPE displayPropScope( aShortDisplayProp );
1243 ORCAD_STREAM stream( aData );
1244
1245 std::map<std::string, std::string> props;
1246 uint8_t type = v2Prefix( stream, aStrings, &props );
1247 ORCAD_SYMBOL_DEF def = v2LibSymbolDef( stream, aStrings, type );
1248 def.props = std::move( props );
1249
1250 if( type == ORCAD_ST_LIBRARY_PART && stream.Remaining() != 0 )
1251 v2LibraryPartTail( stream, def );
1252
1253 if( stream.Remaining() != 0 )
1254 THROW_IO_ERROR( wxS( "v2 symbol stream: trailing bytes" ) );
1255
1256 if( def.name.empty() )
1257 return;
1258
1259 auto existing = aSymbols.find( def.name );
1260
1261 if( existing == aSymbols.end() )
1262 aSymbols.emplace( def.name, std::move( def ) );
1263 else
1264 existing->second.variants.push_back( std::move( def ) );
1265}
1266
1267
1268std::vector<std::string> OrcadParsePageOrderV2( const std::vector<char>& aData,
1269 const std::vector<std::string>& aStrings )
1270{
1271 ORCAD_STREAM stream( aData );
1272
1273 v2Prefix( stream, aStrings );
1274
1275 return pageOrderBody( stream );
1276}
1277
1278
1279// Cache PartCells end after the view list. Package PartCells also contain inline symbols.
1280static void v2CachePartCell( ORCAD_STREAM& aStream, const std::vector<std::string>& aStrings )
1281{
1282 v2Prefix( aStream, aStrings );
1283 aStream.ReadLzt(); // part cell name
1284 aStream.ReadLzt(); // source library
1285
1286 uint16_t viewCount = aStream.ReadU16();
1287
1288 v2CheckCount( aStream, viewCount );
1289
1290 for( uint16_t i = 0; i < viewCount; ++i )
1291 aStream.ReadLzt();
1292}
1293
1294
1295void OrcadParseCacheV2( const std::vector<char>& aData, const std::vector<std::string>& aStrings,
1296 const ORCAD_WARN_FN& aWarn, std::map<std::string, ORCAD_SYMBOL_DEF>& aSymbols,
1297 std::map<std::string, ORCAD_PACKAGE>& aPackages )
1298{
1299 // The group count includes all variants. Each of the four sections uses legacy framing.
1300 ORCAD_STREAM stream( aData );
1301
1302 try
1303 {
1304 if( stream.ReadU16() != 0 )
1305 THROW_IO_ERROR( wxS( "OrCAD legacy Cache: invalid marker" ) );
1306
1307 for( int section = 0; section < 4; ++section )
1308 {
1309 uint16_t groupCount = stream.ReadU16();
1310
1311 v2CheckCount( stream, groupCount );
1312
1313 for( uint16_t group = 0; group < groupCount; ++group )
1314 {
1315 stream.ReadLzt(); // group name
1316
1317 uint16_t variantCount = stream.ReadU16();
1318
1319 v2CheckCount( stream, variantCount );
1320
1321 for( uint16_t variant = 0; variant < variantCount; ++variant )
1322 {
1323 stream.ReadLzt(); // source library
1324 stream.ReadU32(); // created
1325 stream.ReadU32(); // modified
1326
1327 int typeId = stream.ReadU8();
1328
1329 stream.ExpectByte( 0, wxS( "legacy Cache entry pad" ) );
1330
1331 if( typeId == ORCAD_ST_PART_CELL )
1332 {
1333 v2CachePartCell( stream, aStrings );
1334 }
1335 else if( typeId == ORCAD_ST_PACKAGE )
1336 {
1337 ORCAD_PACKAGE pkg = v2Package( stream, aStrings );
1338 auto existing = aPackages.find( pkg.name );
1339
1340 if( existing == aPackages.end() )
1341 aPackages.emplace( pkg.name, std::move( pkg ) );
1342 else
1343 existing->second.variants.push_back( std::move( pkg ) );
1344 }
1345 else
1346 {
1347 std::map<std::string, std::string> props;
1348 uint8_t bodyType = v2Prefix( stream, aStrings, &props );
1349 ORCAD_SYMBOL_DEF def = v2LibSymbolDef( stream, aStrings, bodyType );
1350 def.props = std::move( props );
1351
1352 // Only a LibraryPart carries the implementation and reference-prefix
1353 // tail; the other symbol types end at their definition.
1354 if( typeId == ORCAD_ST_LIBRARY_PART )
1355 v2LibraryPartTail( stream, def );
1356
1357 if( !def.name.empty() )
1358 {
1359 auto existing = aSymbols.find( def.name );
1360
1361 if( existing == aSymbols.end() )
1362 {
1363 aSymbols.emplace( def.name, std::move( def ) );
1364 }
1365 else
1366 {
1367 if( existing->second.generalFlags < 0 && def.generalFlags >= 0 )
1368 existing->second.generalFlags = def.generalFlags;
1369
1370 existing->second.variants.push_back( std::move( def ) );
1371 }
1372 }
1373 }
1374 }
1375 }
1376 }
1377
1378 if( !stream.AtEnd() )
1379 THROW_IO_ERRORF( wxS( "OrCAD legacy Cache: %zu trailing bytes" ), stream.Remaining() );
1380 }
1381 catch( const IO_ERROR& e )
1382 {
1383 // Nothing above LoadSchematicFile catches IO_ERROR, so a cache we cannot frame keeps
1384 // whatever was read rather than failing the whole import.
1385 if( aWarn )
1386 {
1387 aWarn( wxString::Format( _( "The legacy design cache could not be read past 0x%zx (%s); the "
1388 "remaining symbols fall back to placeholders." ),
1389 stream.GetOffset(), e.What() ) );
1390 }
1391 }
1392}
1393
1394
1395void OrcadParseOlbPackageStreamV2( const std::vector<char>& aData, const std::vector<std::string>& aStrings,
1396 std::map<std::string, ORCAD_SYMBOL_DEF>& aSymbols,
1397 std::map<std::string, ORCAD_PACKAGE>& aPackages, bool aShortDisplayProp )
1398{
1399 V2_DISPLAY_PROP_SCOPE displayPropScope( aShortDisplayProp );
1400 ORCAD_STREAM stream( aData );
1401
1402 uint16_t partCount = stream.ReadU16();
1403 v2CheckCount( stream, partCount );
1404 std::vector<V2_PART_CELL> cells;
1405
1406 for( uint16_t i = 0; i < partCount; i++ )
1407 cells.push_back( v2PartCell( stream, aStrings ) );
1408
1409 ORCAD_PACKAGE pkg = v2Package( stream, aStrings );
1410
1411 if( stream.Remaining() != 0 )
1412 THROW_IO_ERROR( wxS( "v2 package stream: trailing bytes" ) );
1413
1414 // Inline symbol keyed by view name ("7400.Normal"); view suffix later stripped to part base.
1415 for( const V2_PART_CELL& cell : cells )
1416 {
1417 for( const ORCAD_SYMBOL_DEF& def : cell.symbols )
1418 {
1419 if( def.name.empty() )
1420 continue;
1421
1422 auto existing = aSymbols.find( def.name );
1423
1424 if( existing == aSymbols.end() )
1425 aSymbols.emplace( def.name, def );
1426 else
1427 existing->second.variants.push_back( def );
1428 }
1429
1430 if( pkg.refDes.empty() && !cell.refDesPrefix.empty() )
1431 pkg.refDes = cell.refDesPrefix;
1432 }
1433
1434 if( pkg.name.empty() )
1435 return;
1436
1437 auto it = aPackages.find( pkg.name );
1438
1439 if( it == aPackages.end() )
1440 aPackages.emplace( pkg.name, std::move( pkg ) );
1441 else
1442 it->second.variants.push_back( std::move( pkg ) );
1443}
1444
1445
1446static void v2ReadOccurrence( ORCAD_STREAM& aStream, const std::vector<std::string>& aStrings,
1447 ORCAD_OCC_SCOPE& aScope );
1448
1449
1450static ORCAD_OCC_SCOPE v2ReadOccScope( ORCAD_STREAM& aStream, const std::vector<std::string>& aStrings )
1451{
1452 ORCAD_STREAM::NEST_GUARD guard( aStream, wxS( "v2 occurrence scope" ) );
1453 ORCAD_OCC_SCOPE scope;
1454
1455 uint16_t netCount = aStream.ReadU16();
1456 v2CheckCount( aStream, netCount );
1457
1458 for( uint16_t i = 0; i < netCount; i++ )
1459 {
1460 if( v2Prefix( aStream, aStrings ) != 0x43 )
1461 THROW_IO_ERROR( wxS( "v2 occurrence scope: unexpected net record" ) );
1462
1463 uint32_t occurrenceId = aStream.ReadU32();
1464 std::string name = aStream.ReadLzt();
1465
1466 scope.netNames[occurrenceId] = std::move( name );
1467 }
1468
1469 uint16_t titleBlockCount = aStream.ReadU16();
1470 v2CheckCount( aStream, titleBlockCount );
1471
1472 for( uint16_t i = 0; i < titleBlockCount; i++ )
1473 {
1474 if( v2Prefix( aStream, aStrings ) != 0x52 )
1475 THROW_IO_ERROR( wxS( "v2 occurrence scope: unexpected title-block record" ) );
1476
1477 aStream.ReadU32();
1478 aStream.ReadU32();
1479 }
1480
1481 uint16_t occurrenceCount = aStream.ReadU16();
1482 v2CheckCount( aStream, occurrenceCount );
1483
1484 for( uint16_t i = 0; i < occurrenceCount; i++ )
1485 v2ReadOccurrence( aStream, aStrings, scope );
1486
1487 return scope;
1488}
1489
1490
1491static void v2ReadOccurrence( ORCAD_STREAM& aStream, const std::vector<std::string>& aStrings,
1492 ORCAD_OCC_SCOPE& aScope )
1493{
1494 std::map<std::string, std::string> props;
1495
1496 if( v2Prefix( aStream, aStrings, &props ) != 0x42 )
1497 THROW_IO_ERROR( wxS( "v2 occurrence: unexpected record" ) );
1498
1499 aStream.ReadU32();
1500 uint32_t targetDbId = aStream.ReadU32();
1501 std::string child = aStream.ReadLzt();
1502 std::string ref = aStream.ReadLzt();
1503 uint16_t unitRefIdx = aStream.ReadU16();
1504
1505 uint16_t pinCount = aStream.ReadU16();
1506 v2CheckCount( aStream, pinCount );
1507 for( uint16_t i = 0; i < pinCount; i++ )
1508 {
1509 uint8_t type = v2Prefix( aStream, aStrings );
1510
1511 if( type != 0x44 && type != 0x45 )
1512 THROW_IO_ERROR( wxS( "v2 occurrence: unexpected pin record" ) );
1513
1514 aStream.ReadU32();
1515 aStream.ReadU16();
1516 }
1517
1518 ORCAD_OCC_SCOPE nested = v2ReadOccScope( aStream, aStrings );
1519
1520 if( !child.empty() )
1521 {
1522 ORCAD_OCC_BLOCK block;
1523 block.targetDbId = targetDbId;
1524 block.childFolder = std::move( child );
1525 block.scope = std::move( nested );
1526 aScope.blocks.push_back( std::move( block ) );
1527 }
1528 else
1529 {
1530 if( !ref.empty() )
1531 aScope.partRefs[targetDbId] = ref;
1532
1533 if( !props.empty() )
1534 aScope.partProps[targetDbId] = std::move( props );
1535
1536 if( unitRefIdx < aStrings.size() && !aStrings[unitRefIdx].empty() )
1537 {
1538 aScope.partUnitRefs[targetDbId] = aStrings[unitRefIdx];
1539 }
1540 }
1541}
1542
1543
1544ORCAD_OCC_SCOPE OrcadReadOccurrenceTreeV2( const std::vector<char>& aData, const std::vector<std::string>& aStrings )
1545{
1546 ORCAD_STREAM stream( aData );
1547 stream.ReadLzt();
1548 stream.Skip( 5 );
1549
1550 uint16_t powerCount = stream.ReadU16();
1551 v2CheckCount( stream, powerCount );
1552
1553 for( uint16_t i = 0; i < powerCount; ++i )
1554 {
1555 if( v2Prefix( stream, aStrings ) != 0x44 )
1556 THROW_IO_ERROR( wxS( "v2 occurrence tree: unexpected power record" ) );
1557
1558 stream.ReadU32();
1559 stream.ReadLzt();
1560 }
1561
1562 ORCAD_OCC_SCOPE root = v2ReadOccScope( stream, aStrings );
1563
1564 if( stream.Remaining() != 0 )
1565 THROW_IO_ERRORF( wxS( "v2 occurrence tree: %zu trailing bytes" ), stream.Remaining() );
1566
1567 return root;
1568}
1569
1570
1571ORCAD_OCC_SCOPE OrcadReadOccurrenceTree( const std::vector<char>& aData, const std::vector<std::string>& aStrings,
1572 const ORCAD_WARN_FN& aWarn )
1573{
1574 // Modern streams open w/ type-0x42 long prefix (u8 0x42, u32 bodyLen, u32 0); legacy pre-preamble
1575 // streams start at view-name lzt with only instance-annotated refs, so empty tree is correct.
1576 if( aData.size() < 9 || (uint8_t) aData[0] != 0x42 || aData[5] || aData[6] || aData[7] || aData[8] )
1577 {
1578 return {};
1579 }
1580
1581 ORCAD_STREAM stream( aData );
1582
1583 try
1584 {
1585 stream.ReadU8(); // 0x42
1586 stream.ReadU32(); // bodyLen
1587 stream.ReadU32(); // 0
1588 stream.ReadLzt(); // view name
1589 stream.Skip( 7 ); // zeros
1590
1591 uint16_t powerCount = stream.ReadU16();
1592
1593 for( uint16_t i = 0; i < powerCount; ++i )
1594 {
1595 readOccHeader( stream, aStrings, 0x44 );
1596 stream.ReadU32();
1597 stream.ReadLzt();
1598 }
1599
1600 return readOccScope( stream, aStrings );
1601 }
1602 catch( const IO_ERROR& e )
1603 {
1604 aWarn( wxString::Format( wxS( "The design occurrence tree could not be fully read (%s); "
1605 "reference designators fall back to the placed instances." ),
1606 e.What() ) );
1607 return {};
1608 }
1609}
const char * name
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()
Bound shared parser recursion; entering a level above MAX_NESTING throws IO_ERROR.
The caller owns the buffer.
size_t Remaining() const
Bytes left; 0 when the cursor is at or past the end.
int PeekU8(size_t aAhead=0) const
– non-throwing lookahead ---------------------------------------------------—
bool AtPreamble(size_t aAhead=0) const
True when the 4 preamble bytes FF E4 5C 39 sit at cursor + aAhead.
uint8_t ReadU8()
– scalars (little-endian, bounds-checked, throw IO_ERROR on overrun) ----—
static constexpr size_t npos
Generic invalid offset sentinel.
size_t GetOffset() const
uint16_t ReadU16()
std::vector< uint8_t > ReadBytes(size_t aCount)
– buffers / cursor ---------------------------------------------------------—
void Skip(size_t aCount)
Advance the cursor; throws IO_ERROR when aCount exceeds the remaining bytes.
bool AtEnd() const
std::string ReadLzt()
– strings ----------------------------------------------------------------—
bool PeekMatches(const uint8_t *aBytes, size_t aCount, size_t aAhead=0) const
Returns false if fewer than aCount bytes remain; does not advance the cursor.
int32_t ReadI32()
void ExpectByte(uint8_t aValue, const wxString &aWhat)
Consume one byte and require the given value.
uint32_t ReadU32()
int16_t ReadI16()
ReadStructure can recover from a body error when the prefix supplies a valid end offset.
std::map< std::string, std::string > PropsDict(const ORCAD_PREFIXES &aPrefixes) const
Resolve the short-prefix (nameIdx, valueIdx) pairs; empty names are dropped.
ORCAD_PREFIXES ReadPrefixes(int aExpectedType=-1, size_t aEnclosingEnd=ORCAD_STREAM::npos, size_t aLongPrefixCount=ORCAD_STREAM::npos)
aLongPrefixCount overrides the type depth; aEnclosingEnd bounds all stops.
ORCAD_STREAM & Stream()
ORCAD_READ_RESULT ReadStructure()
Skip unknown bodies.
V2_DISPLAY_PROP_SCOPE(bool aShort)
#define _(s)
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
#define THROW_IO_ERRORF(msg,...)
ORCAD_PAGE_SETTINGS OrcadParsePageSettings(ORCAD_STREAM &aStream)
Consumes 156 bytes; throws IO_ERROR on overrun.
static ORCAD_PRIMITIVE v2PrimBody(ORCAD_STREAM &aStream, uint8_t aType)
static ORCAD_PIN_INST v2PinInst(ORCAD_STREAM &aStream, const std::vector< std::string > &aStrings)
static ORCAD_OCC_SCOPE v2ReadOccScope(ORCAD_STREAM &aStream, const std::vector< std::string > &aStrings)
static std::string v2Resolve(const std::vector< std::string > &aStrings, uint16_t aIdx)
static ORCAD_OCC_SCOPE readOccScope(ORCAD_STREAM &aStream, const std::vector< std::string > &aStrings)
static void v2Structure(ORCAD_STREAM &aStream, const std::vector< std::string > &aStrings)
static void readOccurrence(ORCAD_STREAM &aStream, const std::vector< std::string > &aStrings, ORCAD_OCC_SCOPE &aScope)
std::vector< std::string > OrcadParsePageOrderV2(const std::vector< char > &aData, const std::vector< std::string > &aStrings)
Returns display order; throws IO_ERROR for invalid legacy framing.
static ORCAD_PORT_TYPE v2PortType(uint32_t aRaw)
void OrcadParseCacheV2(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)
Keep decoded entries if a framing error ends the legacy cache.
void OrcadParseOlbSymbolStreamV2(const std::vector< char > &aData, const std::vector< std::string > &aStrings, std::map< std::string, ORCAD_SYMBOL_DEF > &aSymbols, bool aShortDisplayProp)
aShortDisplayProp selects the version 1 display-property layout.
ORCAD_RAW_PAGE OrcadParsePageV2(const std::vector< char > &aData, const std::vector< std::string > &aStrings, const ORCAD_WARN_FN &, bool aShortDisplayProp)
Legacy records have no stop offsets.
static ORCAD_DISPLAY_PROP v2DisplayProp(ORCAD_STREAM &aStream, const std::vector< std::string > &aStrings)
static ORCAD_PACKAGE v2Package(ORCAD_STREAM &aStream, const std::vector< std::string > &aStrings)
static ORCAD_SYMBOL_DEF v2LibSymbolDef(ORCAD_STREAM &aStream, const std::vector< std::string > &aStrings, int aTypeId)
static ORCAD_DRAWN_INSTANCE v2DrawnInstance(ORCAD_STREAM &aStream, const std::vector< std::string > &aStrings)
static void v2CheckCount(ORCAD_STREAM &aStream, uint32_t aCount)
static ORCAD_SYMBOL_DEF v2SymbolDef(ORCAD_STREAM &aStream, const std::vector< std::string > &aStrings)
static ORCAD_WIRE v2Wire(ORCAD_STREAM &aStream, const std::vector< std::string > &aStrings)
static ORCAD_GRAPHIC_INST v2GraphicInst(ORCAD_STREAM &aStream, const std::vector< std::string > &aStrings)
static ORCAD_PRIMITIVE v2Primitive(ORCAD_STREAM &aStream)
static void v2ReadOccurrence(ORCAD_STREAM &aStream, const std::vector< std::string > &aStrings, ORCAD_OCC_SCOPE &aScope)
static V2_PART_CELL v2PartCell(ORCAD_STREAM &aStream, const std::vector< std::string > &aStrings)
void OrcadParseOlbPackageStreamV2(const std::vector< char > &aData, const std::vector< std::string > &aStrings, std::map< std::string, ORCAD_SYMBOL_DEF > &aSymbols, std::map< std::string, ORCAD_PACKAGE > &aPackages, bool aShortDisplayProp)
aShortDisplayProp selects the version 1 display-property layout.
static ORCAD_ALIAS v2Alias(ORCAD_STREAM &aStream, const std::vector< std::string > &aStrings)
static uint8_t v2Prefix(ORCAD_STREAM &aStream, const std::vector< std::string > &aStrings, std::map< std::string, std::string > *aProps=nullptr)
std::vector< std::string > OrcadParsePageOrder(const std::vector< char > &aData)
Returns display order.
ORCAD_OCC_SCOPE OrcadReadOccurrenceTree(const std::vector< char > &aData, const std::vector< std::string > &aStrings, const ORCAD_WARN_FN &aWarn)
Returns occurrence references and child scopes.
static std::vector< std::string > pageOrderBody(ORCAD_STREAM &aStream)
static ORCAD_PLACED_INSTANCE v2PlacedInstance(ORCAD_STREAM &aStream, const std::vector< std::string > &aStrings)
static uint8_t readOccHeader(ORCAD_STREAM &aStream, const std::vector< std::string > &aStrings, int aExpectType, std::map< std::string, std::string > *aProps=nullptr)
static bool v2SymbolPin(ORCAD_STREAM &aStream, const std::vector< std::string > &aStrings, ORCAD_SYMBOL_PIN &aOut)
ORCAD_OCC_SCOPE OrcadReadOccurrenceTreeV2(const std::vector< char > &aData, const std::vector< std::string > &aStrings)
Parse a short-prefix-only v2.0 Hierarchy stream without scan recovery.
static thread_local bool g_v2ShortDisplayProp
static std::vector< ORCAD_DISPLAY_PROP > v2DisplayPropList(ORCAD_STREAM &aStream, const std::vector< std::string > &aStrings)
static ORCAD_DEVICE v2Device(ORCAD_STREAM &aStream, const std::vector< std::string > &aStrings)
std::vector< std::string > OrcadParseSchematicFolderOrder(const std::vector< char > &aData)
Unlisted Views storages can be stale.
ORCAD_RAW_PAGE OrcadParsePage(const std::vector< char > &aData, const std::vector< std::string > &aStrings, const ORCAD_WARN_FN &aWarn)
A body failure skips that structure.
static void v2CachePartCell(ORCAD_STREAM &aStream, const std::vector< std::string > &aStrings)
static void v2LibraryPartTail(ORCAD_STREAM &aStream, ORCAD_SYMBOL_DEF &aDef)
std::function< void(const wxString &aMsg)> ORCAD_WARN_FN
Coordinates use DBU with Y down.
@ 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_POLYGON
@ ORCAD_PRIM_RECT
@ ORCAD_PRIM_SYMBOL_VECTOR
nested prefix-framed vector graphic
ORCAD_PORT_TYPE
Map unknown electrical type codes to PASSIVE.
@ ORCAD_ST_STH_IN_PAGES0
nested symbol body inside Graphic*Inst
@ ORCAD_ST_T0X10
scalar pin instance (absolute pos)
@ ORCAD_ST_WIRE_SCALAR
@ ORCAD_ST_PLACED_INSTANCE
@ ORCAD_ST_PART_CELL
@ ORCAD_ST_DRAWN_INSTANCE
hierarchical block instance
@ ORCAD_ST_PAGE
@ ORCAD_ST_LIBRARY_PART
@ ORCAD_ST_PACKAGE
@ ORCAD_ST_ALIAS
net alias attached to a wire
@ ORCAD_ST_WIRE_BUS
@ ORCAD_ST_SCH_LIB
@ ORCAD_ST_SYMBOL_DISPLAY_PROP
@ ORCAD_ST_T0X11
bus pin instance (absolute pos)
ORCAD_NET_GROUP OrcadReadT0x34Raw(ORCAD_STREAM &aStream)
T0x34 records have no prefixes; consume their fixed layout to preserve alignment.
ORCAD_BUS_ENTRY OrcadReadBusEntryBody(ORCAD_STREAM &aStream)
ORCAD_NET_GROUP OrcadReadT0x35Raw(ORCAD_STREAM &aStream)
T0x35 = the T0x34 raw layout followed by u16 n and 4 * n bytes.
The owning wire defines the connection.
std::string name
int rotation
0..3 quarter turns
Axis-aligned box in OrCAD DBU; corner order as stored (not normalized).
One interface pin of a hierarchical block, at its absolute page position.
Device unit names omit the view suffix.
std::vector< std::string > pinNumbers
std::string refDes
std::vector< bool > pinIgnore
std::string unitRef
Display positions use symbol coordinates; rotFont combines the font index and quarter turns.
int fontIdx
1-based into ORCAD_LIBRARY_INFO::fonts; 0 = default
int rotation
0..3 quarter turns
std::string name
resolved property name (empty when index invalid)
The inline LibraryPart defines the block interface; placed pin records supply absolute positions.
std::string name
intrinsic Name property used for flat-net scoping
int x1
block rectangle top-left, page DBU
std::vector< ORCAD_BLOCK_PIN > pins
std::vector< ORCAD_DISPLAY_PROP > displayProps
std::map< std::string, std::string > props
Free graphics use nested primitive coordinates.
std::unique_ptr< ORCAD_SYMBOL_DEF > nested
SthInPages0 body, else nullptr.
std::map< std::string, std::string > props
int rotation
0..3 quarter turns
std::string name
cache symbol name
std::vector< ORCAD_DISPLAY_PROP > displayProps
std::string logicalName
ports: resolved net/port name
int typeId
ORCAD_ST value.
Repeated child folders have separate scopes and reference designators.
uint32_t targetDbId
type-12 drawn-instance dbId on the parent page
ORCAD_OCC_SCOPE scope
the child's occurrences under this path
std::string childFolder
child schematic folder name
Each scope holds the references and child blocks for one instantiation path.
std::vector< ORCAD_OCC_BLOCK > blocks
hierarchical block occurrences
std::map< uint32_t, std::map< std::string, std::string > > partProps
dbId -> occurrence properties
std::map< uint32_t, std::string > netNames
occurrence net id -> effective net name
std::map< uint32_t, std::string > partRefs
type-13 dbId -> occurrence refdes
std::map< uint32_t, std::string > partUnitRefs
dbId -> package unit reference
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
Dimensions use mils when isMetric is zero, otherwise micrometres.
Pin positions are absolute page connection points.
The placed box includes displayed text.
std::string sourcePackage
package base name
std::vector< ORCAD_DISPLAY_PROP > displayProps
ORCAD_BBOX bbox
placed box, page DBU
int rotation
0..3 quarter turns
uint16_t unitIndex
zero-based package device index
std::map< std::string, std::string > props
short-prefix property pairs
bool mirror
orientation bit 2
std::string sourceLibrary
source library path from the placed-instance header
std::string value
resolved Part Value
std::vector< ORCAD_PIN_INST > pins
successfully parsed T0x10 records
Integer point in OrCAD DBU.
Prefix lengths supply structure bounds.
int typeId
ORCAD_ST value (u8 in the stream)
size_t end
offset right after the whole structure
Primitive byte lengths can include or exclude the eight-byte size envelope.
int fillStyle
0 solid, 1 none, 2 hatch pattern
std::string text
kind == TEXT
std::vector< ORCAD_PRIMITIVE > children
kind == GROUP, translated by (x1, y1)
std::vector< ORCAD_POINT > points
polygon/polyline/bezier vertices
std::optional< ORCAD_POINT > textBoundsStart
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.
Wire IDs refer to netmap, which supplies the source net names.
uint32_t verticalWidth
uint32_t horizontalWidth
std::vector< ORCAD_GRAPHIC_INST > ports
std::map< uint32_t, std::vector< std::string > > netAliases
every name recorded for a net db id
std::string name
std::vector< ORCAD_GRAPHIC_INST > globals
placed power symbols
std::map< uint32_t, std::string > netmap
net db id -> net name
std::map< std::string, std::string > props
std::vector< ORCAD_WIRE > wires
uint32_t width
mils, or um when isMetric
std::vector< ORCAD_NET_GROUP > netGroups
bus net id -> member net ids
std::vector< ORCAD_DRAWN_INSTANCE > blocks
hierarchical blocks (detection only)
std::vector< ORCAD_PLACED_INSTANCE > instances
uint16_t horizontalCount
std::vector< ORCAD_GRAPHIC_INST > graphics
free comment text/shapes/images
std::vector< ORCAD_GRAPHIC_INST > offpage
off-page connectors
std::vector< ORCAD_GRAPHIC_INST > titleBlocks
std::vector< ORCAD_BUS_ENTRY > busEntries
uint16_t verticalCount
std::string pageSize
page-size name string, e.g. "B"
std::vector< ORCAD_GRAPHIC_INST > ercObjects
saved design-rule-check markers
uint32_t createTimestamp
uint32_t modifyTimestamp
ORCAD_RECORD_VARIANT record
The bounding box occupies the final eight bytes before the next prefix stop.
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, bit1 = pin text rotates ...
Pin coordinates use symbol space with Y down.
std::vector< ORCAD_DISPLAY_PROP > displayProps
ORCAD_PORT_TYPE portType
The wire ID refers to the page net table.
uint32_t id
uint32_t dbId
bool isBus
true for structure type 21
std::vector< ORCAD_ALIAS > aliases
std::map< std::string, std::string > props
std::vector< std::string > viewNames
std::string refDesPrefix
std::string name
std::vector< ORCAD_SYMBOL_DEF > symbols
one per view, keyed positionally
KIBIS_PIN * pin
wxString result
Test unit parsing edge cases and error handling.