35bool isPrimType(
int aType )
50 default:
return false;
55bool isSymbolType(
int aTypeId )
69 default:
return false;
74std::optional<ORCAD_PRIMITIVE> readPrimitiveBody(
ORCAD_STREAM& aStream,
int aType );
79std::optional<ORCAD_PRIMITIVE> readSymbolVector(
ORCAD_STREAM& aStream )
92 uint16_t count = aStream.
ReadU16();
94 for( uint16_t i = 0; i < count; i++ )
98 aStream.
ExpectByte( 0x00, wxS(
"symbol vector prim pad" ) );
100 if( !isPrimType( t ) )
103 std::optional<ORCAD_PRIMITIVE> child;
107 child = readSymbolVector( aStream );
111 int t2 = aStream.
ReadU8();
116 child = readPrimitiveBody( aStream, t );
120 group.children.push_back( std::move( *child ) );
131std::optional<ORCAD_PRIMITIVE> readPrimitiveBody(
ORCAD_STREAM& aStream,
int t1 )
134 size_t size = aStream.
ReadU32();
135 size_t available = aStream.
Size() - start;
137 if( size > available )
138 THROW_IO_ERRORF( wxS(
"primitive type %d at 0x%zx exceeds its enclosing structure" ), t1, start );
142 size_t recordSize = size;
147 std::optional<ORCAD_PRIMITIVE> prim;
152 static const uint8_t
pad[4] = { 0x00, 0x00, 0x00, 0x00 };
153 aStream.
Expect(
pad, 4, wxS(
"primitive pad" ) );
167 prim = std::move( p );
179 prim = std::move( p );
202 prim = std::move( p );
225 uint16_t pointCount = aStream.
ReadU16();
227 for( uint16_t i = 0; i < pointCount; i++ )
235 prim = std::move( p );
249 prim = std::move( p );
262 uint32_t dataSize = aStream.
ReadU32();
264 prim = std::move( p );
278 size_t to = start + size;
281 THROW_IO_ERRORF( wxS(
"OLE primitive at 0x%zx is shorter than its header" ), start );
284 p.
data.assign( aStream.
Data() + from, aStream.
Data() + to );
287 prim = std::move( p );
294 aStream.
Seek( start + recordSize );
298 size_t physicalSize = aStream.
GetOffset() - start;
300 : size == physicalSize || size + 8 == physicalSize;
304 THROW_IO_ERRORF( wxS(
"primitive type %d stores %zu bytes but consumes %zu" ), t1, size, physicalSize );
318 int t1 = aStream.
ReadU8();
320 if( !isPrimType( t1 ) )
324 return readSymbolVector( aStream );
326 int t2 = aStream.
ReadU8();
331 return readPrimitiveBody( aStream, t1 );
340 if( stream.
PeekU8() == 0x00 )
360 uint32_t portType = stream.
ReadU32();
365 stream.
ExpectByte(
static_cast<uint8_t
>( pfx.
typeId ), wxS(
"symbol pin type echo" ) );
381 std::vector<size_t> stops = aPrefixes.
stops;
382 std::sort( stops.begin(), stops.end() );
392 uint16_t primCount = stream.
ReadU16();
394 for( uint16_t i = 0; i < primCount; i++ )
399 sym.
primitives.push_back( std::move( *prim ) );
402 static const uint8_t zeroTrailer[8] = {};
404 if( i + 1 < primCount && stream.
PeekMatches( zeroTrailer,
sizeof( zeroTrailer ) ) )
409 auto nextStopIt = std::upper_bound( stops.begin(), stops.end(), stream.
GetOffset() );
411 if( nextStopIt != stops.end() )
413 size_t nextStop = *nextStopIt;
414 size_t gap = nextStop - stream.
GetOffset();
418 stream.
Seek( nextStop - 8 );
425 if( x1 <= x2 && y1 <= y2 && x2 - x1 <= 4000 && y2 - y1 <= 4000 )
437 stream.
Seek( nextStop );
443 uint16_t pinCount = stream.
ReadU16();
445 for( uint16_t i = 0; i < pinCount; i++ )
452 sym.
pins.push_back( std::move( *
pin ) );
456 uint16_t propCount = stream.
ReadU16();
458 for( uint16_t i = 0; i < propCount; i++ )
465 size_t tailStart = aPrefixes.
stops[1];
467 if( stream.
GetOffset() == tailStart && tailStart < aPrefixes.
end )
469 std::string implementationPath = stream.
ReadLzt();
470 std::string implementation = stream.
ReadLzt();
479 if( !implementationPath.empty() )
480 sym.
props[
"Implementation Path"] = std::move( implementationPath );
482 if( !implementation.empty() )
483 sym.
props[
"Implementation"] = std::move( implementation );
510 uint32_t nameIdx = stream.
ReadU32();
514 uint32_t dbId = stream.
ReadU32();
525 uint8_t orientation = stream.
ReadU8();
535 THROW_IO_ERRORF( wxS(
"drawn instance nested flag %d at 0x%zx" ),
static_cast<int>(
flag ),
550 bool quarterTurn = ( orientation & 1 ) != 0;
551 block.
w = quarterTurn ? bbox.
y2 - bbox.
y1 : bbox.
x2 - bbox.
x1;
552 block.
h = quarterTurn ? bbox.
x2 - bbox.
x1 : bbox.
y2 - bbox.
y1;
556 std::vector<size_t> stops = aPrefixes.
stops;
557 std::sort( stops.begin(), stops.end() );
559 if( stops.size() >= 2 && stops[stops.size() - 2] >= stream.
GetOffset() )
560 stream.
Seek( stops[stops.size() - 2] );
566 uint16_t pinCount = stream.
ReadU16();
568 std::vector<ORCAD_PIN_INST> pinInsts;
570 for( uint16_t i = 0; i < pinCount; i++ )
575 pinInsts.push_back( std::move( *
pin ) );
578 std::set<std::pair<int, int>> placedPoints;
579 std::set<std::pair<int, int>> definitionPoints;
582 placedPoints.emplace(
pin.x,
pin.y );
585 definitionPoints.emplace(
pin.hotptX,
pin.hotptY );
587 bool useDefinitionGeometry = placedPoints.size() <= 1 && definitionPoints.size() > 1;
589 for(
size_t i = 0; i < pinInsts.size() && i < nested.
pins.size(); i++ )
596 blockPin.
x = useDefinitionGeometry ? x1 +
pin.hotptX - bbox.
x1 : pinInsts[i].x;
597 blockPin.
y = useDefinitionGeometry ? y1 +
pin.hotptY - bbox.
y1 : pinInsts[i].y;
598 blockPin.
noConnect = pinInsts[i].IsNoConnect();
599 block.
pins.push_back( std::move( blockPin ) );
618 uint16_t pinCount = stream.
ReadU16();
620 for( uint16_t i = 0; i < pinCount; i++ )
623 static const uint8_t emptyMarker[2] = { 0xFF, 0xFF };
657 uint16_t deviceCount = stream.
ReadU16();
659 for( uint16_t i = 0; i < deviceCount; i++ )
674 std::map<std::string, ORCAD_SYMBOL_DEF>& aSymbols,
675 std::map<std::string, ORCAD_PACKAGE>& aPackages )
679 if( isSymbolType( aPrefixes.
typeId ) )
682 auto existing = aSymbols.find( symbol.
name );
684 if( existing == aSymbols.end() )
686 std::string key = symbol.
name;
687 aSymbols.emplace( std::move( key ), std::move( symbol ) );
691 existing->second.variants.push_back( std::move( symbol ) );
696 ORCAD_PACKAGE package = OrcadReadPackage( aReader, aPrefixes );
697 auto existing = aPackages.find( package.name );
699 if( existing == aPackages.end() )
701 std::string key = package.name;
702 aPackages.emplace( std::move( key ), std::move( package ) );
706 existing->second.variants.push_back( std::move( package ) );
711 aReader.
SkipStructure( aPrefixes, wxString::Format( wxS(
"type %d" ), aPrefixes.
typeId ) );
732void OrcadParseCache(
const std::vector<char>& aData,
const std::vector<std::string>& aStrings,
733 const ORCAD_WARN_FN& aWarn, std::map<std::string, ORCAD_SYMBOL_DEF>& aSymbols,
734 std::map<std::string, ORCAD_PACKAGE>& aPackages )
747 for(
int section = 0; section < 4; ++section )
749 uint16_t groupCount = stream.
ReadU16();
754 uint16_t variantCount = stream.
ReadU16();
756 for( uint16_t variant = 0; variant < variantCount; ++variant )
762 int typeId = stream.
ReadU8();
764 stream.
ExpectByte( 0, wxS(
"Cache entry pad" ) );
768 THROW_IO_ERRORF( wxS(
"OrCAD Cache: section %d cannot hold structure type %d" ), section,
774 if( prefixes.
end == 0 || prefixes.
end > stream.
Size() )
775 THROW_IO_ERROR( wxS(
"OrCAD Cache: entry frame runs past the stream" ) );
787 aWarn( wxString::Format( wxS(
"cache struct type %d at 0x%zx: %s" ), typeId,
797 if( !stream.
AtEnd() )
804 aWarn( wxString::Format( wxS(
"OrCAD Cache: stopped at 0x%zx (%s); symbols after this point fall "
805 "back to synthesized placeholders" ),
813 std::map<std::string, ORCAD_SYMBOL_DEF>& aSymbols )
819 if( !isSymbolType( prefixes.
typeId ) )
820 THROW_IO_ERRORF( wxS(
"OrCAD symbol stream: expected a symbol structure, got type %d" ), prefixes.
typeId );
822 std::map<std::string, ORCAD_PACKAGE> unusedPackages;
825 if( !stream.
AtEnd() )
831 std::map<std::string, ORCAD_SYMBOL_DEF>& aSymbols,
832 std::map<std::string, ORCAD_PACKAGE>& aPackages )
837 uint16_t partCellCount = stream.
ReadU16();
839 if( partCellCount > 1000 )
840 THROW_IO_ERROR( wxS(
"OrCAD package stream: implausible PartCell count" ) );
842 for( uint16_t i = 0; i < partCellCount; ++i )
846 std::map<std::string, std::string> cellProps = reader.
PropsDict( cellPfx );
850 uint16_t viewCount = stream.
ReadU16();
852 if( viewCount > 1000 )
853 THROW_IO_ERROR( wxS(
"OrCAD package stream: implausible view count" ) );
855 for( uint16_t view = 0; view < viewCount; ++view )
859 THROW_IO_ERROR( wxS(
"OrCAD package stream: PartCell exceeds its frame" ) );
863 uint16_t symbolCount = stream.
ReadU16();
865 if( symbolCount > 1000 )
866 THROW_IO_ERROR( wxS(
"OrCAD package stream: implausible LibraryPart count" ) );
868 for( uint16_t symbolIndex = 0; symbolIndex < symbolCount; ++symbolIndex )
873 for(
const auto& [
name, value] : cellProps )
876 auto existing = aSymbols.find( symbol.
name );
878 if( existing == aSymbols.end() )
880 std::string key = symbol.
name;
881 aSymbols.emplace( std::move( key ), std::move( symbol ) );
885 existing->second.variants.push_back( std::move( symbol ) );
891 ORCAD_PACKAGE package = OrcadReadPackage( reader, packagePfx );
896 auto existing = aPackages.find( package.name );
898 if( existing == aPackages.end() )
900 std::string key = package.name;
901 aPackages.emplace( std::move( key ), std::move( package ) );
905 existing->second.variants.push_back( std::move( package ) );
911 std::map<std::string, ORCAD_PACKAGE>& aPackages,
912 std::map<std::string, ORCAD_SYMBOL_DEF>&& aExtraSymbols,
913 std::map<std::string, ORCAD_PACKAGE>&& aExtraPackages )
915 for(
auto& [
name, sym] : aExtraSymbols )
917 auto it = aSymbols.find(
name );
919 if( it == aSymbols.end() )
921 aSymbols.emplace(
name, std::move( sym ) );
926 std::vector<ORCAD_SYMBOL_DEF> extraVariants = std::move( sym.variants );
927 sym.variants.clear();
929 if( it->second.generalFlags < 0 && sym.generalFlags >= 0 )
930 it->second.generalFlags = sym.generalFlags;
932 it->second.variants.push_back( std::move( sym ) );
935 it->second.variants.push_back( std::move( variant ) );
939 for(
auto& [
name, pkg] : aExtraPackages )
941 auto it = aPackages.find(
name );
943 if( it == aPackages.end() )
945 aPackages.emplace(
name, std::move( pkg ) );
949 std::vector<ORCAD_PACKAGE> variants = std::move( pkg.variants );
950 pkg.variants.clear();
951 it->second.variants.push_back( std::move( pkg ) );
954 it->second.variants.push_back( std::move( variant ) );
961 const std::map<std::string, ORCAD_SYMBOL_DEF>& aMetadataSymbols )
963 for(
const auto& [
name, metadata] : aMetadataSymbols )
965 auto symbol = aSymbols.find(
name );
967 if( symbol != aSymbols.end() && symbol->second.generalFlags < 0 && metadata.generalFlags >= 0 )
968 symbol->second.generalFlags = metadata.generalFlags;
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
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.
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.
std::string ReadLzt()
– strings ----------------------------------------------------------------—
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().
void ExpectByte(uint8_t aValue, const wxString &aWhat)
Consume one byte and require the given value.
void Expect(const uint8_t *aBytes, size_t aCount, const wxString &aWhat)
– validated reads ------------------------------------------------------------—
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.
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.
std::string Resolve(uint32_t aIndex) const
String-table lookup; returns "" for out-of-range indices.
ORCAD_READ_RESULT ReadStructure()
Skip unknown bodies.
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
#define THROW_IO_ERRORF(msg,...)
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)
Existing symbols gain 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 bool cacheSectionAcceptsType(int aSection, int aTypeId)
void OrcadParseSymbolStream(const std::vector< char > &aData, const std::vector< std::string > &aStrings, std::map< std::string, ORCAD_SYMBOL_DEF > &aSymbols)
Throws IO_ERROR for invalid framing or trailing bytes.
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)
std::optional< ORCAD_PRIMITIVE > OrcadReadPrimitive(ORCAD_STREAM &aStream)
Consumes one primitive and its optional preamble.
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)
The first entry is the default; later entries become variants.
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)
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)
Set aWithPins to read the trailing pin and property lists.
ORCAD_SYMBOL_DEF OrcadReadSthInPages0(ORCAD_STRUCT_READER &aReader, const ORCAD_PREFIXES &aPrefixes)
std::function< void(const wxString &aMsg)> ORCAD_WARN_FN
Coordinates use DBU with Y down.
@ ORCAD_PRIM_COMMENT_TEXT
@ ORCAD_PRIM_BITMAP
plain DIB (BITMAPINFOHEADER + pixels)
@ ORCAD_PRIM_OLE_IMAGE
OLE compound document embed.
@ ORCAD_PRIM_SYMBOL_VECTOR
nested prefix-framed vector graphic
@ ORCAD_ST_TITLEBLOCK_SYMBOL
@ ORCAD_ST_SYMBOL_PIN_SCALAR
@ ORCAD_ST_GLOBAL_SYMBOL
power symbol definition
@ ORCAD_ST_BOOKMARK_SYMBOL
@ ORCAD_ST_SYMBOL_PIN_BUS
@ 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::vector< bool > pinIgnore
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::map< std::string, std::string > props
Part-level properties shared by every placement (Description, Tolerance, ...).
std::vector< ORCAD_DEVICE > devices
Pin positions are absolute page connection points.
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
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::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.
wxString result
Test unit parsing edge cases and error handling.