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 <cstdlib>
27#include <utility>
28#include <set>
29
30#include <ki_exception.h>
31#include <wx/translation.h>
32
34
35
36namespace
37{
38
39bool isPrimType( int aType )
40{
41 switch( aType )
42 {
43 case ORCAD_PRIM_RECT:
44 case ORCAD_PRIM_LINE:
45 case ORCAD_PRIM_ARC:
53 case ORCAD_PRIM_OLE_IMAGE: return true;
54 default: return false;
55 }
56}
57
58
59bool isSymbolType( int aTypeId )
60{
61 switch( aTypeId )
62 {
69 case ORCAD_ST_BOOKMARK_SYMBOL: return true;
70
71 // ORCAD_ST_PIN_SHAPE_SYMBOL is absent on purpose. It has no registered prefix depth, so
72 // claiming it here would promise a decode we cannot frame. No corpus file contains one.
73 default: return false;
74 }
75}
76
77
78std::optional<ORCAD_PRIMITIVE> readPrimitiveBody( ORCAD_STREAM& aStream, int aType );
79
80
81// Nested prefix-framed vector graphic inside library parts. Entered on the second of the two
82// type bytes, which is where the vector's own one-long-prefix chain begins.
83std::optional<ORCAD_PRIMITIVE> readSymbolVector( ORCAD_STREAM& aStream )
84{
85 ORCAD_STREAM::NEST_GUARD guard( aStream, wxS( "symbol vector" ) );
86
87 ORCAD_STRUCT_READER reader( aStream );
88 ORCAD_PREFIXES pfx = reader.ReadPrefixes( ORCAD_ST_SYMBOL_VECTOR, ORCAD_STREAM::npos, 1 );
89 ORCAD_STREAM::LIMIT_GUARD limit( aStream, pfx.end );
90
93 group.x1 = aStream.ReadI16();
94 group.y1 = aStream.ReadI16();
95
96 uint16_t count = aStream.ReadU16();
97
98 for( uint16_t i = 0; i < count; i++ )
99 {
100 // Nested prims use 3-byte prefix: type, 0x00, type.
101 int t = aStream.ReadU8();
102 aStream.ExpectByte( 0x00, wxS( "symbol vector prim pad" ) );
103
104 if( !isPrimType( t ) )
105 THROW_IO_ERROR( wxS( "symbol vector prim prefix mismatch" ) );
106
107 std::optional<ORCAD_PRIMITIVE> child;
108
109 if( t == ORCAD_PRIM_SYMBOL_VECTOR )
110 {
111 child = readSymbolVector( aStream );
112 }
113 else
114 {
115 int t2 = aStream.ReadU8();
116
117 if( t != t2 )
118 THROW_IO_ERROR( wxS( "symbol vector prim prefix mismatch" ) );
119
120 child = readPrimitiveBody( aStream, t );
121 }
122
123 if( child )
124 group.children.push_back( std::move( *child ) );
125 }
126
127 aStream.ReadLzt(); // vector name
128
129 if( pfx.end != 0 )
130 aStream.Seek( std::max( aStream.GetOffset(), pfx.end ) );
131
132 return group;
133}
134
135// Field layout shared by modern and legacy primitives; false for types with their own framing
136bool readPrimitiveFields( ORCAD_STREAM& aStream, int aType, ORCAD_PRIMITIVE& aPrim )
137{
138 switch( aType )
139 {
140 case ORCAD_PRIM_RECT:
143 aPrim.x1 = aStream.ReadI32();
144 aPrim.y1 = aStream.ReadI32();
145 aPrim.x2 = aStream.ReadI32();
146 aPrim.y2 = aStream.ReadI32();
147 aPrim.lineStyle = aStream.ReadU32();
148 aPrim.lineWidth = aStream.ReadU32();
149 aPrim.fillStyle = aStream.ReadU32();
150 aPrim.hatchStyle = aStream.ReadU32();
151 return true;
152
153 case ORCAD_PRIM_LINE:
155 aPrim.x1 = aStream.ReadI32();
156 aPrim.y1 = aStream.ReadI32();
157 aPrim.x2 = aStream.ReadI32();
158 aPrim.y2 = aStream.ReadI32();
159 aPrim.lineStyle = aStream.ReadU32();
160 aPrim.lineWidth = aStream.ReadU32();
161 return true;
162
163 case ORCAD_PRIM_ARC:
165 aPrim.x1 = aStream.ReadI32();
166 aPrim.y1 = aStream.ReadI32();
167 aPrim.x2 = aStream.ReadI32();
168 aPrim.y2 = aStream.ReadI32();
169 aPrim.start = ORCAD_POINT{ aStream.ReadI32(), aStream.ReadI32() };
170 aPrim.end = ORCAD_POINT{ aStream.ReadI32(), aStream.ReadI32() };
171 aPrim.lineStyle = aStream.ReadU32();
172 aPrim.lineWidth = aStream.ReadU32();
173 return true;
174
178 {
182 aPrim.lineStyle = aStream.ReadU32();
183 aPrim.lineWidth = aStream.ReadU32();
184
185 if( aType == ORCAD_PRIM_POLYGON )
186 {
187 aPrim.fillStyle = aStream.ReadU32();
188 aPrim.hatchStyle = aStream.ReadU32();
189 }
190
191 uint16_t pointCount = aStream.ReadU16();
192
193 for( uint16_t i = 0; i < pointCount; i++ )
194 {
195 ORCAD_POINT pt;
196 pt.y = aStream.ReadI16();
197 pt.x = aStream.ReadI16();
198 aPrim.points.push_back( pt );
199 }
200
201 return true;
202 }
203
206 aPrim.x1 = aStream.ReadI32();
207 aPrim.y1 = aStream.ReadI32();
208 aPrim.x2 = aStream.ReadI32();
209 aPrim.y2 = aStream.ReadI32();
210 aPrim.textBoundsStart = ORCAD_POINT{ aStream.ReadI32(), aStream.ReadI32() };
211 aPrim.fontIdx = aStream.ReadU16();
212 aStream.Skip( 2 );
213 aPrim.text = aStream.ReadLzt();
214 return true;
215
218 aPrim.x1 = aStream.ReadI32();
219 aPrim.y1 = aStream.ReadI32();
220 aPrim.x2 = aStream.ReadI32();
221 aPrim.y2 = aStream.ReadI32();
222 aStream.Skip( 8 ); // x1, y1 duplicate corner
223 aStream.Skip( 8 ); // pixel width/height
224 aPrim.data = aStream.ReadBytes( aStream.ReadU32() );
225 return true;
226
227 default: return false;
228 }
229}
230
231
232std::optional<ORCAD_PRIMITIVE> readPrimitiveBody( ORCAD_STREAM& aStream, int t1 )
233{
234 size_t start = aStream.GetOffset();
235 size_t size = aStream.ReadU32();
236 size_t available = aStream.Size() - start;
237
238 if( size > available )
239 THROW_IO_ERRORF( wxS( "primitive type %d at 0x%zx exceeds its enclosing structure" ), t1, start );
240
241 // Two byteLength conventions are mixed even within one file: the modern one counts the u32
242 // size and its 4-byte pad, the legacy one excludes them. The bound below admits both.
243 size_t recordSize = size;
244
245 if( t1 != ORCAD_PRIM_OLE_IMAGE && available - size >= 8 )
246 recordSize += 8;
247
248 std::optional<ORCAD_PRIMITIVE> prim;
249
250 {
251 ORCAD_STREAM::LIMIT_GUARD limit( aStream, start + recordSize );
252
253 static const uint8_t pad[4] = { 0x00, 0x00, 0x00, 0x00 };
254 aStream.Expect( pad, 4, wxS( "primitive pad" ) );
255
257
258 if( t1 == ORCAD_PRIM_OLE_IMAGE )
259 {
261 p.x1 = aStream.ReadI32();
262 p.y1 = aStream.ReadI32();
263 p.x2 = aStream.ReadI32();
264 p.y2 = aStream.ReadI32();
265 aStream.Skip( 16 ); // crop/original-extent values
266
267 // OLE compound-document payload fills the rest of the record.
268 size_t from = aStream.GetOffset();
269 size_t to = start + size;
270
271 if( to < from )
272 THROW_IO_ERRORF( wxS( "OLE primitive at 0x%zx is shorter than its header" ), start );
273
274 if( to > from )
275 p.data.assign( aStream.Data() + from, aStream.Data() + to );
276
277 aStream.Seek( to );
278 prim = std::move( p );
279 }
280 else if( readPrimitiveFields( aStream, t1, p ) )
281 {
282 prim = std::move( p );
283 }
284
285 // CommentText keeps undecoded padding after the string, so its length owns the extent.
286 // Every other type is decoded in full, so its length must agree with one of the conventions.
287 if( t1 == ORCAD_PRIM_COMMENT_TEXT )
288 {
289 aStream.Seek( start + recordSize );
290 }
291 else
292 {
293 size_t physicalSize = aStream.GetOffset() - start;
294 bool validSize = t1 == ORCAD_PRIM_OLE_IMAGE ? size == physicalSize
295 : size == physicalSize || size + 8 == physicalSize;
296
297 if( !validSize )
298 {
299 THROW_IO_ERRORF( wxS( "primitive type %d stores %zu bytes but consumes %zu" ), t1, size, physicalSize );
300 }
301 }
302 }
303
305 return prim;
306}
307
308
309ORCAD_PRIMITIVE readLegacyPrimitive( ORCAD_STRUCT_READER& aReader, int aType );
310
311
312// Legacy vectors frame their children in place: a short prefix, the origin, then padded primitives
313ORCAD_PRIMITIVE readLegacySymbolVector( ORCAD_STRUCT_READER& aReader )
314{
315 ORCAD_STREAM& stream = aReader.Stream();
316 ORCAD_STREAM::NEST_GUARD guard( stream, wxS( "legacy symbol vector" ) );
317
318 stream.ExpectByte( ORCAD_PRIM_SYMBOL_VECTOR, wxS( "legacy symbol vector pair" ) );
319
320 int16_t propertyCount = stream.ReadI16();
321 stream.Skip( 4 * std::max<int>( propertyCount, 0 ) );
322
325 group.x1 = stream.ReadI16();
326 group.y1 = stream.ReadI16();
327
328 uint16_t count = aReader.ReadCount();
329
330 for( uint16_t i = 0; i < count; i++ )
331 {
332 int type = stream.ReadU8();
333 stream.ExpectByte( 0x00, wxS( "legacy vector prim pad" ) );
334 group.children.push_back( readLegacyPrimitive( aReader, type ) );
335 }
336
337 stream.ReadLzt(); // vector name
338
339 return group;
340}
341
342
343// Legacy primitives carry one type byte and no size envelope
344ORCAD_PRIMITIVE readLegacyPrimitive( ORCAD_STRUCT_READER& aReader, int aType )
345{
346 if( aType == ORCAD_PRIM_SYMBOL_VECTOR )
347 return readLegacySymbolVector( aReader );
348
349 ORCAD_PRIMITIVE prim;
350
351 if( !readPrimitiveFields( aReader.Stream(), aType, prim ) )
352 {
353 THROW_IO_ERRORF( wxS( "OrCAD legacy primitive: unhandled type %d at 0x%zx" ), aType,
354 aReader.Stream().GetOffset() );
355 }
356
357 return prim;
358}
359
360} // namespace
361
362
363std::optional<ORCAD_PRIMITIVE> OrcadReadPrimitive( ORCAD_STRUCT_READER& aReader )
364{
365 ORCAD_STREAM& stream = aReader.Stream();
366 int t1 = stream.ReadU8();
367
368 if( aReader.Dialect().legacy )
369 return readLegacyPrimitive( aReader, t1 );
370
371 if( !isPrimType( t1 ) )
372 THROW_IO_ERRORF( wxS( "bad primitive type %d at 0x%zx" ), t1, stream.GetOffset() - 1 );
373
374 if( t1 == ORCAD_PRIM_SYMBOL_VECTOR )
375 return readSymbolVector( stream );
376
377 int t2 = stream.ReadU8();
378
379 if( t1 != t2 )
380 THROW_IO_ERRORF( wxS( "bad primitive prefix %d/%d at 0x%zx" ), t1, t2, stream.GetOffset() - 2 );
381
382 return readPrimitiveBody( stream, t1 );
383}
384
385
386std::optional<ORCAD_SYMBOL_PIN> OrcadReadSymbolPin( ORCAD_STRUCT_READER& aReader )
387{
388 ORCAD_STREAM& stream = aReader.Stream();
389
390 // Single 0x00 instead of prefix chain = skipped pin slot.
391 if( stream.PeekU8() == 0x00 )
392 {
393 stream.Skip( 1 );
394 return std::nullopt;
395 }
396
397 ORCAD_PREFIXES pfx = aReader.ReadPrefixes();
398
400 THROW_IO_ERRORF( wxS( "expected symbol pin, got type %d" ), pfx.typeId );
401
403 pin.name = stream.ReadLzt();
404 pin.startX = stream.ReadI32();
405 pin.startY = stream.ReadI32();
406 pin.hotptX = stream.ReadI32();
407 pin.hotptY = stream.ReadI32();
408 pin.shapeBits = stream.ReadU16();
409 stream.Skip( 2 ); // uninitialized junk
410
411 uint32_t portType = stream.ReadU32();
412 pin.portType = portType <= 7 ? static_cast<ORCAD_PORT_TYPE>( portType ) : ORCAD_PORT_TYPE::PASSIVE;
413
414 // Legacy pins always carry the type echo and display properties
415 if( aReader.Dialect().legacy
416 || ( pfx.end != 0 && pfx.end >= stream.GetOffset() + 6 && stream.PeekU8() == pfx.typeId ) )
417 {
418 stream.ExpectByte( static_cast<uint8_t>( pfx.typeId ), wxS( "symbol pin type echo" ) );
419 stream.Skip( 3 );
420 pin.displayProps = OrcadReadDisplayPropList( aReader );
421 }
422
423 if( pfx.end != 0 && pfx.end >= stream.GetOffset() )
424 stream.Seek( pfx.end );
425
426 return pin;
427}
428
429
430ORCAD_SYMBOL_DEF OrcadReadSymbolDef( ORCAD_STRUCT_READER& aReader, const ORCAD_PREFIXES& aPrefixes, bool aWithPins )
431{
432 ORCAD_STREAM& stream = aReader.Stream();
433
434 std::vector<size_t> stops = aPrefixes.stops;
435 std::sort( stops.begin(), stops.end() );
436
438 sym.typeId = aPrefixes.typeId;
439 sym.name = stream.ReadLzt();
440 sym.sourceLib = stream.ReadLzt();
441 sym.props = aReader.PropsDict( aPrefixes );
442
443 sym.color = static_cast<int>( stream.ReadU32() );
444
445 uint16_t primCount = aReader.ReadCount();
446
447 for( uint16_t i = 0; i < primCount; i++ )
448 {
449 std::optional<ORCAD_PRIMITIVE> prim = OrcadReadPrimitive( aReader );
450
451 if( prim )
452 sym.primitives.push_back( std::move( *prim ) );
453
454 // Occasional 8 zero bytes between prims.
455 static const uint8_t zeroTrailer[8] = {};
456
457 if( i + 1 < primCount && stream.PeekMatches( zeroTrailer, sizeof( zeroTrailer ) ) )
458 stream.Skip( 8 );
459 }
460
461 // Bbox = last 8 bytes before next checkpoint (gap 8, or 16 w/ 8 legacy-trailer bytes), as 4x i16.
462 auto nextStopIt = std::upper_bound( stops.begin(), stops.end(), stream.GetOffset() );
463
464 if( aReader.Dialect().legacy )
465 {
466 // Legacy boxes follow the primitives directly; version 1 page symbols have none
467 if( aWithPins || !aReader.Dialect().shortDisplayProp )
468 {
469 ORCAD_BBOX box;
470 box.x1 = stream.ReadI16();
471 box.y1 = stream.ReadI16();
472 box.x2 = stream.ReadI16();
473 box.y2 = stream.ReadI16();
474 sym.bbox = box;
475 }
476 }
477 else if( nextStopIt != stops.end() )
478 {
479 size_t nextStop = *nextStopIt;
480 size_t gap = nextStop - stream.GetOffset();
481
482 if( gap >= 8 )
483 {
484 stream.Seek( nextStop - 8 );
485
486 int x1 = stream.ReadI16();
487 int y1 = stream.ReadI16();
488 int x2 = stream.ReadI16();
489 int y2 = stream.ReadI16();
490
491 if( x1 <= x2 && y1 <= y2 && x2 - x1 <= 4000 && y2 - y1 <= 4000 )
492 {
493 ORCAD_BBOX box;
494 box.x1 = x1;
495 box.y1 = y1;
496 box.x2 = x2;
497 box.y2 = y2;
498 sym.bbox = box;
499 }
500 }
501 else if( gap > 0 )
502 {
503 stream.Seek( nextStop );
504 }
505 }
506
507 if( aWithPins )
508 {
509 uint16_t pinCount = aReader.ReadCount();
510
511 for( uint16_t i = 0; i < pinCount; i++ )
512 {
513 std::optional<ORCAD_SYMBOL_PIN> pin = OrcadReadSymbolPin( aReader );
514
515 if( pin )
516 {
517 pin->position = i;
518 sym.pins.push_back( std::move( *pin ) );
519 }
520 }
521
522 uint16_t propCount = aReader.ReadCount();
523
524 for( uint16_t i = 0; i < propCount; i++ )
525 aReader.ReadStructure();
526
527 // The fifth prefix bounds LibraryPart metadata. Reject data outside those stops.
528 if( sym.typeId == ORCAD_ST_LIBRARY_PART && aPrefixes.stops.size() >= 2 )
529 {
530 size_t save = stream.GetOffset();
531 size_t tailStart = aPrefixes.stops[1];
532
533 if( stream.GetOffset() == tailStart && tailStart < aPrefixes.end )
534 {
535 std::string implementationPath = stream.ReadLzt();
536 std::string implementation = stream.ReadLzt();
537 stream.ReadLzt(); // reference prefix
538 stream.ReadLzt(); // part value
539
540 int flags = stream.ReadU16();
541
542 // Landing anywhere but the outer stop means the strings were not the tail.
543 if( stream.GetOffset() == aPrefixes.end )
544 {
545 if( !implementationPath.empty() )
546 sym.props["Implementation Path"] = std::move( implementationPath );
547
548 if( !implementation.empty() )
549 sym.props["Implementation"] = std::move( implementation );
550
551 sym.generalFlags = flags;
552 }
553 }
554
555 stream.Seek( save );
556 }
557 }
558
559 if( aPrefixes.end != 0 && aPrefixes.end > stream.GetOffset() )
560 stream.Seek( aPrefixes.end );
561
562 return sym;
563}
564
565
567{
568 ORCAD_STREAM& stream = aReader.Stream();
569 bool legacy = aReader.Dialect().legacy;
570
571 uint32_t nameIdx = aReader.ReadStrIdx();
572 aReader.ReadStrIdx(); // source library string index
573
574 if( legacy )
575 stream.Skip( 4 ); // uninitialized
576
577 stream.ReadLzt(); // name
578
579 uint32_t dbId = stream.ReadU32();
580
581 stream.ReadI16(); // anchor y
582 stream.ReadI16(); // anchor x
583 stream.ReadI16(); // bbox y2
584 stream.ReadI16(); // bbox x2
585
586 int x1 = stream.ReadI16();
587 int y1 = stream.ReadI16();
588
589 stream.ReadU8(); // color
590 uint8_t orientation = stream.ReadU8();
591 stream.Skip( 2 ); // structId, unknown
592
593 std::vector<ORCAD_DISPLAY_PROP> displayProps = OrcadReadDisplayPropList( aReader );
594
595 // Inline LibraryPart carries block's pin interface.
596 uint8_t flag = stream.ReadU8();
597
599 {
600 THROW_IO_ERRORF( wxS( "drawn instance nested flag %d at 0x%zx" ), static_cast<int>( flag ),
601 stream.GetOffset() - 1 );
602 }
603
604 ORCAD_PREFIXES nestedPfx = aReader.ReadPrefixes( ORCAD_ST_LIBRARY_PART, aPrefixes.end );
605 ORCAD_SYMBOL_DEF nested = OrcadReadSymbolDef( aReader, nestedPfx, true );
606
607 ORCAD_BBOX bbox = nested.bbox.value_or( ORCAD_BBOX() );
608
610 block.dbId = dbId;
611 block.name = aReader.Resolve( nameIdx );
612 block.props = aReader.PropsDict( aPrefixes );
613 block.x1 = x1;
614 block.y1 = y1;
615 bool quarterTurn = ( orientation & 1 ) != 0;
616 block.w = quarterTurn ? bbox.y2 - bbox.y1 : bbox.x2 - bbox.x1;
617 block.h = quarterTurn ? bbox.x2 - bbox.x1 : bbox.y2 - bbox.y1;
618 block.displayProps = std::move( displayProps );
619
620 if( legacy )
621 {
622 block.props.insert( nested.props.begin(), nested.props.end() );
623 stream.Skip( 14 );
624 block.reference = stream.ReadLzt();
625
626 uint32_t valueIdx = aReader.ReadStrIdx();
627 aReader.ReadStrIdx();
628 uint32_t implementationIdx = aReader.ReadStrIdx();
629 aReader.ReadStrIdx();
630 block.props["Value"] = aReader.Resolve( valueIdx );
631 block.props["Implementation"] = aReader.Resolve( implementationIdx );
632 }
633 else
634 {
635 // Block reference starts at second-to-last prefix checkpoint.
636 std::vector<size_t> stops = aPrefixes.stops;
637 std::sort( stops.begin(), stops.end() );
638
639 if( stops.size() >= 2 && stops[stops.size() - 2] >= stream.GetOffset() )
640 stream.Seek( stops[stops.size() - 2] );
641
642 block.reference = stream.ReadLzt();
643 stream.Skip( 14 );
644 }
645
646 // Framed T0x10 structs carry absolute pin page positions, in inline LibraryPart pin order.
647 uint16_t pinCount = aReader.ReadCount();
648
649 std::vector<ORCAD_PIN_INST> pinInsts;
650
651 for( uint16_t i = 0; i < pinCount; i++ )
652 {
654
655 if( ORCAD_PIN_INST* pin = std::get_if<ORCAD_PIN_INST>( &result.record ) )
656 pinInsts.push_back( std::move( *pin ) );
657 }
658
659 block.pins = OrcadResolveBlockPins( orientation, x1, y1, nested, pinInsts );
660
661 return block;
662}
663
664
665std::vector<ORCAD_BLOCK_PIN> OrcadResolveBlockPins( int aOrient, int aX1, int aY1, const ORCAD_SYMBOL_DEF& aDefinition,
666 const std::vector<ORCAD_PIN_INST>& aPlaced )
667{
668 ORCAD_BBOX box = aDefinition.bbox.value_or( ORCAD_BBOX() );
669 std::set<std::pair<int, int>> placedPoints;
670 std::set<std::pair<int, int>> definitionPoints;
671
672 for( const ORCAD_PIN_INST& pin : aPlaced )
673 placedPoints.emplace( pin.x, pin.y );
674
675 for( const ORCAD_SYMBOL_PIN& pin : aDefinition.pins )
676 definitionPoints.emplace( pin.hotptX, pin.hotptY );
677
678 bool useDefinitionGeometry = placedPoints.size() <= 1 && definitionPoints.size() > 1;
679
680 std::vector<ORCAD_BLOCK_PIN> pins;
681
682 for( size_t i = 0; i < aPlaced.size(); i++ )
683 {
684 const ORCAD_PIN_INST& placed = aPlaced[i];
685 int slot = placed.pinIndex ? std::abs( placed.pinIndex ) - 1 : static_cast<int>( i );
686
687 // Definition pins are stored in ascending slot order
688 auto definition = std::lower_bound( aDefinition.pins.begin(), aDefinition.pins.end(), slot,
689 []( const ORCAD_SYMBOL_PIN& aPin, int aSlot )
690 {
691 return aPin.position < aSlot;
692 } );
693
694 if( definition == aDefinition.pins.end() || definition->position != slot )
695 continue;
696
698 pin.name = definition->name;
699 pin.portType = definition->portType;
700 pin.x = placed.x;
701 pin.y = placed.y;
702 pin.noConnect = placed.IsNoConnect();
703
704 if( useDefinitionGeometry )
705 {
706 VECTOR2I pos = OrcadTransformPoint( aOrient, box.x2 - box.x1, box.y2 - box.y1, aX1, aY1,
707 definition->hotptX - box.x1, definition->hotptY - box.y1 );
708 pin.x = pos.x;
709 pin.y = pos.y;
710 }
711
712 pins.push_back( std::move( pin ) );
713 }
714
715 return pins;
716}
717
718
720{
721 ORCAD_STREAM& stream = aReader.Stream();
723
724 ORCAD_DEVICE device;
725 device.unitRef = stream.ReadLzt();
726 device.refDes = stream.ReadLzt();
727
728 uint16_t pinCount = aReader.ReadCount();
729
730 for( uint16_t i = 0; i < pinCount; i++ )
731 {
732 // FF FF = empty pin slot (no number, ignored).
733 static const uint8_t emptyMarker[2] = { 0xFF, 0xFF };
734
735 if( stream.PeekMatches( emptyMarker, 2 ) )
736 {
737 stream.Skip( 2 );
738 device.pinNumbers.emplace_back();
739 device.pinIgnore.push_back( true );
740 continue;
741 }
742
743 device.pinNumbers.push_back( stream.ReadLzt() );
744
745 uint8_t config = stream.ReadU8();
746 device.pinIgnore.push_back( ( config & 0x80 ) != 0 );
747 }
748
749 if( pfx.end != 0 && pfx.end > stream.GetOffset() )
750 stream.Seek( pfx.end );
751
752 return device;
753}
754
755
757{
758 ORCAD_STREAM& stream = aReader.Stream();
759
760 ORCAD_PACKAGE pkg;
761 pkg.name = stream.ReadLzt();
762 pkg.sourceLib = stream.ReadLzt();
763 pkg.refDes = stream.ReadLzt();
764 stream.ReadLzt(); // unknown
765 pkg.pcbFootprint = stream.ReadLzt();
766
767 uint16_t deviceCount = aReader.ReadCount();
768
769 for( uint16_t i = 0; i < deviceCount; i++ )
770 pkg.devices.push_back( OrcadReadDevice( aReader ) );
771
772 pkg.props = aReader.PropsDict( aPrefixes );
773
774 if( aPrefixes.end != 0 && aPrefixes.end > stream.GetOffset() )
775 stream.Seek( aPrefixes.end );
776
777 return pkg;
778}
779
780
781// Store one framed Cache/Packages record. The Cache may hold several stale library versions of
782// one name; the first entry wins as the default and later ones become variants.
783static void storeFramedRecord( ORCAD_STRUCT_READER& aReader, const ORCAD_PREFIXES& aPrefixes,
784 std::map<std::string, ORCAD_SYMBOL_DEF>& aSymbols,
785 std::map<std::string, ORCAD_PACKAGE>& aPackages )
786{
787 ORCAD_STREAM::LIMIT_GUARD limit( aReader.Stream(), aPrefixes.end );
788
789 if( isSymbolType( aPrefixes.typeId ) )
790 {
791 OrcadAddOrVariant( aSymbols, OrcadReadSymbolDef( aReader, aPrefixes, true ) );
792 }
793 else if( aPrefixes.typeId == ORCAD_ST_PACKAGE )
794 {
795 OrcadAddOrVariant( aPackages, OrcadReadPackage( aReader, aPrefixes ) );
796 }
797 else
798 {
799 aReader.SkipStructure( aPrefixes, wxString::Format( wxS( "type %d" ), aPrefixes.typeId ) );
800 }
801}
802
803
804// The type each Cache section is allowed to hold. Section membership is what proves the walk
805// is still aligned, so a section carrying the wrong type is a framing error, not a bad record.
806static bool cacheSectionAcceptsType( int aSection, int aTypeId )
807{
808 switch( aSection )
809 {
810 // Type 24 lives in section 1 and carries a deeper prefix chain. Accepting it here would let
811 // a walk that has lost alignment land on a LibraryPart and be waved through.
812 case 0: return aTypeId != ORCAD_ST_LIBRARY_PART && isSymbolType( aTypeId );
813 case 1: return aTypeId == ORCAD_ST_LIBRARY_PART;
814 case 2: return aTypeId == ORCAD_ST_PART_CELL;
815 default: return aTypeId == ORCAD_ST_PACKAGE;
816 }
817}
818
819
820// Legacy LibraryParts end with this tail; framed ones carry it inside their last prefix stop
821static std::string readLegacyLibraryPartTail( ORCAD_STREAM& aStream, ORCAD_SYMBOL_DEF& aDef )
822{
823 std::string implementationPath = aStream.ReadLzt();
824
825 if( !implementationPath.empty() )
826 aDef.props["Implementation Path"] = std::move( implementationPath );
827
828 aStream.ReadLzt(); // implementation (PSpice model)
829 std::string refDesPrefix = aStream.ReadLzt();
830 aStream.ReadLzt(); // part value
831 aDef.generalFlags = aStream.ReadU16();
832
833 return refDesPrefix;
834}
835
836
837// Nothing frames a legacy Cache entry, so each body must be read in full to find the next
838static void storeLegacyCacheRecord( ORCAD_STRUCT_READER& aReader, int aTypeId,
839 std::map<std::string, ORCAD_SYMBOL_DEF>& aSymbols,
840 std::map<std::string, ORCAD_PACKAGE>& aPackages )
841{
842 ORCAD_STREAM& stream = aReader.Stream();
843 ORCAD_PREFIXES prefixes = aReader.ReadPrefixes();
844
845 if( aTypeId == ORCAD_ST_PART_CELL )
846 {
847 stream.ReadLzt(); // part cell name
848 stream.ReadLzt(); // source library
849
850 uint16_t viewCount = aReader.ReadCount();
851
852 for( uint16_t i = 0; i < viewCount; ++i )
853 stream.ReadLzt();
854 }
855 else if( aTypeId == ORCAD_ST_PACKAGE )
856 {
857 OrcadAddOrVariant( aPackages, OrcadReadPackage( aReader, prefixes ) );
858 }
859 else
860 {
861 ORCAD_SYMBOL_DEF def = OrcadReadSymbolDef( aReader, prefixes, true );
862
863 if( aTypeId == ORCAD_ST_LIBRARY_PART )
864 readLegacyLibraryPartTail( stream, def );
865
866 if( !def.name.empty() )
867 {
868 int flags = def.generalFlags;
869 ORCAD_SYMBOL_DEF& stored = OrcadAddOrVariant( aSymbols, std::move( def ) );
870
871 if( stored.generalFlags < 0 && flags >= 0 )
872 stored.generalFlags = flags;
873 }
874 }
875}
876
877
878void OrcadParseCache( const std::vector<char>& aData, const std::vector<std::string>& aStrings,
879 const ORCAD_WARN_FN& aWarn, std::map<std::string, ORCAD_SYMBOL_DEF>& aSymbols,
880 std::map<std::string, ORCAD_PACKAGE>& aPackages, ORCAD_DIALECT aDialect )
881{
882 ORCAD_STREAM stream( aData );
883 ORCAD_STRUCT_READER reader( stream, &aStrings, aWarn, aDialect );
884
885 // Keep decoded symbols if the cache fails. The remaining parts use placeholders.
886 try
887 {
888 if( stream.ReadU16() != 0 )
889 THROW_IO_ERROR( wxS( "OrCAD Cache: invalid marker" ) );
890
891 // Four counted sections: loose symbols, LibraryParts, PartCells, Packages. An empty
892 // cache is the marker plus four zero counts, which is the ten-byte stream in the wild.
893 for( int section = 0; section < 4; ++section )
894 {
895 uint16_t groupCount = reader.ReadCount();
896
897 for( uint16_t group = 0; group < groupCount; ++group )
898 {
899 stream.ReadLzt(); // group name
900 uint16_t variantCount = reader.ReadCount();
901
902 for( uint16_t variant = 0; variant < variantCount; ++variant )
903 {
904 stream.ReadLzt(); // source library
905 stream.ReadU32(); // created
906 stream.ReadU32(); // modified
907
908 int typeId = stream.ReadU8();
909
910 stream.ExpectByte( 0, wxS( "Cache entry pad" ) );
911
912 if( aDialect.legacy )
913 {
914 storeLegacyCacheRecord( reader, typeId, aSymbols, aPackages );
915 continue;
916 }
917
918 if( !cacheSectionAcceptsType( section, typeId ) )
919 {
920 THROW_IO_ERRORF( wxS( "OrCAD Cache: section %d cannot hold structure type %d" ), section,
921 typeId );
922 }
923
924 ORCAD_PREFIXES prefixes = reader.ReadPrefixes( typeId );
925
926 if( prefixes.end == 0 || prefixes.end > stream.Size() )
927 THROW_IO_ERROR( wxS( "OrCAD Cache: entry frame runs past the stream" ) );
928
929 try
930 {
931 storeFramedRecord( reader, prefixes, aSymbols, aPackages );
932 }
933 catch( const IO_ERROR& e )
934 {
935 // A body we cannot decode is recoverable: the entry's own frame says
936 // where the next one starts.
937 if( aWarn )
938 {
939 aWarn( wxString::Format( wxS( "cache struct type %d at 0x%zx: %s" ), typeId,
940 prefixes.start, e.Problem() ) );
941 }
942 }
943
944 stream.Seek( prefixes.end );
945 }
946 }
947 }
948
949 if( !stream.AtEnd() )
950 THROW_IO_ERRORF( wxS( "OrCAD Cache: %zu trailing bytes" ), stream.Remaining() );
951 }
952 catch( const IO_ERROR& e )
953 {
954 if( aWarn && aDialect.legacy )
955 {
956 aWarn( wxString::Format( _( "The legacy design cache could not be read past 0x%zx (%s); the "
957 "remaining symbols fall back to placeholders." ),
958 stream.GetOffset(), e.What() ) );
959 }
960 else if( aWarn )
961 {
962 aWarn( wxString::Format( wxS( "OrCAD Cache: stopped at 0x%zx (%s); symbols after this point fall "
963 "back to synthesized placeholders" ),
964 stream.GetOffset(), e.Problem() ) );
965 }
966 }
967}
968
969
970void OrcadParseSymbolStream( const std::vector<char>& aData, const std::vector<std::string>& aStrings,
971 std::map<std::string, ORCAD_SYMBOL_DEF>& aSymbols, ORCAD_DIALECT aDialect )
972{
973 ORCAD_STREAM stream( aData );
974 ORCAD_STRUCT_READER reader( stream, &aStrings, nullptr, aDialect );
975 ORCAD_PREFIXES prefixes = reader.ReadPrefixes();
976
977 if( aDialect.legacy )
978 {
979 ORCAD_SYMBOL_DEF def = OrcadReadSymbolDef( reader, prefixes, true );
980
981 if( def.typeId == ORCAD_ST_LIBRARY_PART && stream.Remaining() != 0 )
982 readLegacyLibraryPartTail( stream, def );
983
984 if( stream.Remaining() != 0 )
985 THROW_IO_ERROR( wxS( "OrCAD symbol stream: trailing bytes" ) );
986
987 if( !def.name.empty() )
988 OrcadAddOrVariant( aSymbols, std::move( def ) );
989
990 return;
991 }
992
993 if( !isSymbolType( prefixes.typeId ) )
994 THROW_IO_ERRORF( wxS( "OrCAD symbol stream: expected a symbol structure, got type %d" ), prefixes.typeId );
995
996 std::map<std::string, ORCAD_PACKAGE> unusedPackages;
997 storeFramedRecord( reader, prefixes, aSymbols, unusedPackages );
998
999 if( !stream.AtEnd() )
1000 THROW_IO_ERROR( wxS( "OrCAD symbol stream: trailing bytes" ) );
1001}
1002
1003
1004void OrcadParsePackageStream( const std::vector<char>& aData, const std::vector<std::string>& aStrings,
1005 std::map<std::string, ORCAD_SYMBOL_DEF>& aSymbols,
1006 std::map<std::string, ORCAD_PACKAGE>& aPackages, ORCAD_DIALECT aDialect )
1007{
1008 ORCAD_STREAM stream( aData );
1009 ORCAD_STRUCT_READER reader( stream, &aStrings, nullptr, aDialect );
1010 std::string refDesPrefix;
1011
1012 uint16_t partCellCount = reader.ReadCount();
1013
1014 if( partCellCount > 1000 )
1015 THROW_IO_ERROR( wxS( "OrCAD package stream: implausible PartCell count" ) );
1016
1017 for( uint16_t i = 0; i < partCellCount; ++i )
1018 {
1020
1021 std::map<std::string, std::string> cellProps = reader.PropsDict( cellPfx );
1022 stream.ReadLzt(); // PartCell name
1023 stream.ReadLzt(); // source library
1024
1025 uint16_t viewCount = reader.ReadCount();
1026
1027 if( viewCount > 1000 )
1028 THROW_IO_ERROR( wxS( "OrCAD package stream: implausible view count" ) );
1029
1030 for( uint16_t view = 0; view < viewCount; ++view )
1031 stream.ReadLzt();
1032
1033 if( !aDialect.legacy )
1034 {
1035 if( cellPfx.end == 0 || stream.GetOffset() > cellPfx.end )
1036 THROW_IO_ERROR( wxS( "OrCAD package stream: PartCell exceeds its frame" ) );
1037
1038 stream.Seek( cellPfx.end );
1039 }
1040
1041 uint16_t symbolCount = reader.ReadCount();
1042
1043 if( symbolCount > 1000 )
1044 THROW_IO_ERROR( wxS( "OrCAD package stream: implausible LibraryPart count" ) );
1045
1046 std::string cellRefDesPrefix;
1047
1048 for( uint16_t symbolIndex = 0; symbolIndex < symbolCount; ++symbolIndex )
1049 {
1051 ORCAD_SYMBOL_DEF symbol = OrcadReadSymbolDef( reader, symbolPfx, true );
1052
1053 if( aDialect.legacy )
1054 cellRefDesPrefix = readLegacyLibraryPartTail( stream, symbol );
1055
1056 for( const auto& [name, value] : cellProps )
1057 symbol.props.try_emplace( name, value );
1058
1059 if( !aDialect.legacy || !symbol.name.empty() )
1060 OrcadAddOrVariant( aSymbols, std::move( symbol ) );
1061 }
1062
1063 if( refDesPrefix.empty() )
1064 refDesPrefix = cellRefDesPrefix;
1065 }
1066
1067 ORCAD_PREFIXES packagePfx = reader.ReadPrefixes( ORCAD_ST_PACKAGE );
1068 ORCAD_PACKAGE package = OrcadReadPackage( reader, packagePfx );
1069
1070 if( stream.Remaining() != 0 )
1071 THROW_IO_ERROR( wxS( "OrCAD package stream: trailing bytes" ) );
1072
1073 if( package.refDes.empty() )
1074 package.refDes = refDesPrefix;
1075
1076 if( !aDialect.legacy || !package.name.empty() )
1077 OrcadAddOrVariant( aPackages, std::move( package ) );
1078}
1079
1080
1081void OrcadMergeCacheStreams( std::map<std::string, ORCAD_SYMBOL_DEF>& aSymbols,
1082 std::map<std::string, ORCAD_PACKAGE>& aPackages,
1083 std::map<std::string, ORCAD_SYMBOL_DEF>&& aExtraSymbols,
1084 std::map<std::string, ORCAD_PACKAGE>&& aExtraPackages )
1085{
1086 for( auto& [name, sym] : aExtraSymbols )
1087 {
1088 auto it = aSymbols.find( name );
1089
1090 if( it == aSymbols.end() )
1091 {
1092 aSymbols.emplace( name, std::move( sym ) );
1093 }
1094 else
1095 {
1096 // Main cache def stays default; extra stream's default and variants appended in stream order.
1097 std::vector<ORCAD_SYMBOL_DEF> extraVariants = std::move( sym.variants );
1098 sym.variants.clear();
1099
1100 if( it->second.generalFlags < 0 && sym.generalFlags >= 0 )
1101 it->second.generalFlags = sym.generalFlags;
1102
1103 it->second.variants.push_back( std::move( sym ) );
1104
1105 for( ORCAD_SYMBOL_DEF& variant : extraVariants )
1106 it->second.variants.push_back( std::move( variant ) );
1107 }
1108 }
1109
1110 for( auto& [name, pkg] : aExtraPackages )
1111 {
1112 auto it = aPackages.find( name );
1113
1114 if( it == aPackages.end() )
1115 {
1116 aPackages.emplace( name, std::move( pkg ) );
1117 }
1118 else
1119 {
1120 std::vector<ORCAD_PACKAGE> variants = std::move( pkg.variants );
1121 pkg.variants.clear();
1122 it->second.variants.push_back( std::move( pkg ) );
1123
1124 for( ORCAD_PACKAGE& variant : variants )
1125 it->second.variants.push_back( std::move( variant ) );
1126 }
1127 }
1128}
1129
1130
1131void OrcadMergeSymbolGeneralProperties( std::map<std::string, ORCAD_SYMBOL_DEF>& aSymbols,
1132 const std::map<std::string, ORCAD_SYMBOL_DEF>& aMetadataSymbols )
1133{
1134 for( const auto& [name, metadata] : aMetadataSymbols )
1135 {
1136 auto symbol = aSymbols.find( name );
1137
1138 if( symbol != aSymbols.end() && symbol->second.generalFlags < 0 && metadata.generalFlags >= 0 )
1139 symbol->second.generalFlags = metadata.generalFlags;
1140 }
1141}
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()
virtual const wxString Problem() const
what was the problem?
Limit reads to one record so a malformed body cannot consume the next record.
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 ---------------------------------------------------—
void SkipOptionalPreambleBlock()
Legacy primitives have no trailing preamble.
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 ----------------------------------------------------------------—
size_t Size() const
const uint8_t * Data() const
Raw buffer access for bounded lexical lookahead and payload extraction.
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.
void Seek(size_t aOffset)
Set the absolute cursor position; throws IO_ERROR when aOffset exceeds Size().
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)
– validated reads ------------------------------------------------------------—
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.
void SkipStructure(const ORCAD_PREFIXES &aPrefixes, const wxString &aWhat)
Throws IO_ERROR if the end is unknown or behind the cursor.
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.
const ORCAD_DIALECT & Dialect() const
ORCAD_STREAM & Stream()
uint32_t ReadStrIdx()
A string-table index in the dialect's width.
std::string Resolve(uint32_t aIndex) const
String-table lookup; returns "" for out-of-range indices and the legacy sentinel.
uint16_t ReadCount()
A u16 list count.
ORCAD_READ_RESULT ReadStructure()
Always resumes at the prefix end, skipping unknown bodies and undecoded trailers.
#define _(s)
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
#define THROW_IO_ERRORF(msg,...)
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:437
void OrcadMergeSymbolGeneralProperties(std::map< std::string, ORCAD_SYMBOL_DEF > &aSymbols, const std::map< std::string, ORCAD_SYMBOL_DEF > &aMetadataSymbols)
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 ...
void OrcadParseSymbolStream(const std::vector< char > &aData, const std::vector< std::string > &aStrings, std::map< std::string, ORCAD_SYMBOL_DEF > &aSymbols, ORCAD_DIALECT aDialect)
Throws IO_ERROR for invalid framing or trailing bytes.
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, ORCAD_DIALECT aDialect)
The first entry is the default; later entries become variants.
ORCAD_DEVICE OrcadReadDevice(ORCAD_STRUCT_READER &aReader)
std::optional< ORCAD_SYMBOL_PIN > OrcadReadSymbolPin(ORCAD_STRUCT_READER &aReader)
A zero byte marks an empty pin slot and returns nullopt.
static void storeLegacyCacheRecord(ORCAD_STRUCT_READER &aReader, int aTypeId, std::map< std::string, ORCAD_SYMBOL_DEF > &aSymbols, std::map< std::string, ORCAD_PACKAGE > &aPackages)
static bool cacheSectionAcceptsType(int aSection, int aTypeId)
std::optional< ORCAD_PRIMITIVE > OrcadReadPrimitive(ORCAD_STRUCT_READER &aReader)
Consumes one primitive and its optional preamble.
std::vector< ORCAD_BLOCK_PIN > OrcadResolveBlockPins(int aOrient, int aX1, int aY1, const ORCAD_SYMBOL_DEF &aDefinition, const std::vector< ORCAD_PIN_INST > &aPlaced)
Pair placed pin records with the inline definition by pin index.
ORCAD_PACKAGE OrcadReadPackage(ORCAD_STRUCT_READER &aReader, const ORCAD_PREFIXES &aPrefixes)
ORCAD_DRAWN_INSTANCE OrcadReadDrawnInstance(ORCAD_STRUCT_READER &aReader, const ORCAD_PREFIXES &aPrefixes)
static void storeFramedRecord(ORCAD_STRUCT_READER &aReader, const ORCAD_PREFIXES &aPrefixes, std::map< std::string, ORCAD_SYMBOL_DEF > &aSymbols, std::map< std::string, ORCAD_PACKAGE > &aPackages)
static std::string readLegacyLibraryPartTail(ORCAD_STREAM &aStream, ORCAD_SYMBOL_DEF &aDef)
void OrcadParsePackageStream(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, ORCAD_DIALECT aDialect)
Package streams contain counted PartCells and LibraryParts, followed by one Package.
ORCAD_SYMBOL_DEF OrcadReadSymbolDef(ORCAD_STRUCT_READER &aReader, const ORCAD_PREFIXES &aPrefixes, bool aWithPins)
Read a symbol definition body (LibraryPart / GlobalSymbol / PortSymbol / OffPageSymbol / TitleBlockSy...
Parsers for the DSN 'Cache' stream and the 'Packages/<name>' streams: symbol definitions with graphic...
VECTOR2I OrcadTransformPoint(int aOrient, int aWidth, int aHeight, int aBaseX, int aBaseY, int aPx, int aPy)
Parts use the instance anchor as the base.
std::function< void(const wxString &aMsg)> ORCAD_WARN_FN
Coordinates use DBU with Y down.
T & OrcadAddOrVariant(std::map< std::string, T > &aMap, T &&aRecord)
Store aRecord under its name, or append it as a variant of the entry already there.
@ 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_PART_CELL
@ ORCAD_ST_SYMBOL_VECTOR
@ 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
std::vector< ORCAD_DISPLAY_PROP > OrcadReadDisplayPropList(ORCAD_STRUCT_READER &aReader)
Keep only display properties that ReadStructure decodes.
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
Legacy (pre-v3) files have short prefixes only, u16 string indices with a 0xFFFF sentinel and nested ...
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
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
Pin positions are absolute page connection points.
bool IsNoConnect() const
Integer point in OrCAD DBU.
Prefix lengths supply structure bounds.
std::vector< size_t > stops
< (from the outermost long prefix); < 0 when unknown
int typeId
ORCAD_ST value (u8 in the stream)
size_t end
offset right after the whole structure
size_t start
stream offset where the chain began
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_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.
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.
KIBIS_PIN * pin
wxString result
Test unit parsing edge cases and error handling.
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708