39#include <wx/translation.h>
41#include <compoundfilereader.h>
65 std::string
name = aName;
66 std::replace(
name.begin(),
name.end(),
'\x03',
':' );
75 const CFB::COMPOUND_FILE_ENTRY* aEntry )
80 uint64_t size = reader.GetStreamSize( aEntry );
82 if( size > reader.GetBufferLen() )
83 THROW_IO_ERROR( wxS(
"OrCAD stream size exceeds the compound file" ) );
85 std::vector<char> data(
static_cast<size_t>( size ) );
88 reader.ReadFile( aEntry, 0, data.data(), data.size() );
95std::vector<std::pair<std::string, const CFB::COMPOUND_FILE_ENTRY*>>
99 std::vector<std::pair<std::string, const CFB::COMPOUND_FILE_ENTRY*>> out;
103 reader.EnumFiles( aParent, 1,
104 [&](
const CFB::COMPOUND_FILE_ENTRY* aEntry,
const CFB::utf16string&,
int ) ->
int
106 if( reader.IsStream( aEntry ) == aStreams )
116std::string lowerCopy(
const std::string& aText )
118 std::string out = aText;
120 std::transform( out.begin(), out.end(), out.begin(),
121 [](
unsigned char c )
123 return static_cast<char>( std::tolower( c ) );
146 const CFB::COMPOUND_FILE_ENTRY* root = reader.GetRootEntry();
161 catch(
const CFB::CFBException& )
165 catch(
const std::exception& )
174 const std::map<std::string, UTF8>* aProperties )
176 wxASSERT( !aFileName.IsEmpty() && aSchematic );
182 wxCHECK_MSG( aSchematic->
IsValid(),
nullptr,
183 wxS(
"Can't append to a schematic with no root!" ) );
184 rootSheet = aAppendToMe;
224 const CFB::COMPOUND_FILE_ENTRY* root = reader.GetRootEntry();
227 const CFB::COMPOUND_FILE_ENTRY* libraryEntry =
232 THROW_IO_ERROR(
_(
"The file does not contain the 'Library' stream of an OrCAD "
233 "Capture design." ) );
245 warnFn(
_(
"This is a pre-2003 OrCAD design; symbol graphics are synthesized as "
246 "placeholders (the legacy symbol cache format is not decoded)." ) );
248 else if(
const CFB::COMPOUND_FILE_ENTRY* cacheEntry =
256 warnFn(
_(
"The design has no 'Cache' stream; placeholder symbols will be "
257 "synthesized for all parts." ) );
261 if(
const CFB::COMPOUND_FILE_ENTRY* packagesStorage =
264 for(
const auto& [streamName, entry] : enumChildren( cfbFile, packagesStorage,
true ) )
266 std::map<std::string, ORCAD_SYMBOL_DEF> extraSymbols;
267 std::map<std::string, ORCAD_PACKAGE> extraPackages;
272 warnFn, extraSymbols, extraPackages );
277 warnFn( wxString::Format(
_(
"Package stream '%s' could not be parsed: %s" ),
278 wxString::FromUTF8( streamName ), e.
What() ) );
283 std::move( extraSymbols ), std::move( extraPackages ) );
288 const CFB::COMPOUND_FILE_ENTRY* viewsStorage =
293 THROW_IO_ERROR(
_(
"The file does not contain a 'Views' storage; it is not a "
294 "supported OrCAD Capture design." ) );
297 std::vector<std::string> folders;
298 std::map<std::string, const CFB::COMPOUND_FILE_ENTRY*> folderEntries;
300 for(
const auto& [folderName, entry] : enumChildren( cfbFile, viewsStorage,
false ) )
302 if( folderEntries.emplace( folderName, entry ).second )
303 folders.push_back( folderName );
306 std::sort( folders.begin(), folders.end() );
308 if( folders.empty() )
313 std::string rootFolder;
316 if( !schematicName.empty() )
318 for(
const std::string& folder : folders )
320 if( lowerCopy( folder ) == schematicName )
328 if( rootFolder.empty() )
329 rootFolder = folders.front();
337 std::map<uint32_t, std::string> hierarchyLinks;
339 auto parseFolderPages = [&](
const std::string& aFolderName,
340 const CFB::COMPOUND_FILE_ENTRY* aFolderEntry,
341 std::vector<ORCAD_RAW_PAGE>& aOutPages )
343 const CFB::COMPOUND_FILE_ENTRY* pagesStorage =
346 std::vector<std::string> available;
347 std::map<std::string, const CFB::COMPOUND_FILE_ENTRY*> pageEntries;
351 for(
const auto& [pageName, entry] : enumChildren( cfbFile, pagesStorage,
true ) )
353 if( pageEntries.emplace( pageName, entry ).second )
354 available.push_back( pageName );
359 std::vector<std::string> ordered;
360 bool orderKnown =
false;
362 if(
const CFB::COMPOUND_FILE_ENTRY* orderEntry =
367 for(
const std::string& pageName :
370 if( pageEntries.count( pageName )
371 && std::find( ordered.begin(), ordered.end(), pageName )
374 ordered.push_back( pageName );
382 warnFn( wxString::Format(
383 _(
"The page display order for schematic folder '%s' could not be "
384 "read (%s); pages are imported in name order." ),
385 wxString::FromUTF8( aFolderName ), e.
What() ) );
392 for(
const std::string& pageName : available )
394 if( std::find( ordered.begin(), ordered.end(), pageName ) == ordered.end() )
395 ordered.push_back( pageName );
401 std::sort( ordered.begin(), ordered.end() );
404 for(
const std::string& pageName : ordered )
408 std::vector<char> pageData = readStream( cfbFile, pageEntries[pageName] );
417 warnFn( wxString::Format(
_(
"Page '%s' could not be parsed and was "
419 wxString::FromUTF8( pageName ), e.
What() ) );
424 parseFolderPages( rootFolder, folderEntries[rootFolder], design.
pages );
427 if(
const CFB::COMPOUND_FILE_ENTRY* hierarchyEntry =
428 cfbFile.
FindStream( folderEntries[rootFolder], {
"Hierarchy",
"Hierarchy" } ) )
430 std::vector<char> hierarchyData = readStream( cfbFile, hierarchyEntry );
441 for(
const auto& [dbId, childName] : hierarchyLinks )
457 collectLinks( block.
scope );
464 if( design.
pages.empty() )
465 THROW_IO_ERROR(
_(
"No schematic pages could be read from the design." ) );
469 std::map<std::string, std::string> folderByLowerName;
471 for(
const std::string& folder : folders )
472 folderByLowerName.emplace( lowerCopy( folder ), folder );
474 for(
const auto& [dbId, childName] : hierarchyLinks )
476 std::string key = lowerCopy( childName );
478 if( key == lowerCopy( rootFolder ) || design.
childFolderPages.count( key ) )
481 auto childIt = folderByLowerName.find( key );
483 if( childIt != folderByLowerName.end() )
484 parseFolderPages( childIt->second, folderEntries[childIt->second],
497 auto it = hierarchyLinks.find( block.
dbId );
499 if( it != hierarchyLinks.end() )
505 catch(
const CFB::CFBException& e )
512 converter.
Convert( rootSheet );
521const std::vector<std::unique_ptr<LIB_SYMBOL>>&
540 const CFB::COMPOUND_FILE_ENTRY* root = reader.GetRootEntry();
542 const CFB::COMPOUND_FILE_ENTRY* libraryEntry =
546 THROW_IO_ERROR(
_(
"The file is not an OrCAD Capture library (no 'Library' stream)." ) );
554 auto parseStream = [&](
const CFB::COMPOUND_FILE_ENTRY* aEntry,
bool aIsPackage )
556 std::map<std::string, ORCAD_SYMBOL_DEF> extraSymbols;
557 std::map<std::string, ORCAD_PACKAGE> extraPackages;
561 std::vector<char> data = readStream( cfbFile, aEntry );
563 if( isV2 && aIsPackage )
572 catch(
const std::exception& )
579 std::move( extraPackages ) );
585 if(
const CFB::COMPOUND_FILE_ENTRY* cacheEntry =
588 parseStream( cacheEntry,
false );
593 for(
const char* storageName : {
"Symbols",
"Packages" } )
595 bool isPackage = std::string( storageName ) ==
"Packages";
597 const CFB::COMPOUND_FILE_ENTRY* storage =
602 for(
const auto& [streamName, entry] : enumChildren( cfbFile, storage,
true ) )
605 if( !streamName.empty() && streamName.front() ==
'$' )
608 parseStream( entry, isPackage );
613 catch(
const CFB::CFBException& e )
622 catch(
const std::exception& e )
625 warnFn( wxString::Format(
_(
"The OrCAD library could not be fully parsed (%s); some "
626 "symbols may be missing." ),
627 wxString::FromUTF8( e.what() ) ) );
633 std::vector<std::unique_ptr<LIB_SYMBOL>> built;
636 built.emplace_back( symbol );
638 return m_libCache[aLibraryPath] = std::move( built );
643 const std::map<std::string, UTF8>* )
645 for(
const std::unique_ptr<LIB_SYMBOL>& symbol :
loadOlbSymbols( aLibraryPath ) )
646 aSymbolNameList.Add( symbol->GetName() );
651 const wxString& aLibraryPath,
652 const std::map<std::string, UTF8>* )
654 for(
const std::unique_ptr<LIB_SYMBOL>& symbol :
loadOlbSymbols( aLibraryPath ) )
655 aSymbolList.push_back( symbol.get() );
660 const std::map<std::string, UTF8>* )
662 for(
const std::unique_ptr<LIB_SYMBOL>& symbol :
loadOlbSymbols( aLibraryPath ) )
664 if( symbol->GetName() == aAliasName )
const CFB::CompoundFileReader & GetCompoundFileReader() const
const CFB::COMPOUND_FILE_ENTRY * FindStreamSingleLevel(const CFB::COMPOUND_FILE_ENTRY *aEntry, const std::string aName, const bool aIsStream) const
const CFB::COMPOUND_FILE_ENTRY * FindStream(const std::vector< std::string > &aStreamPath) const
REPORTER * m_reporter
Reporter to log errors/warnings to, may be nullptr.
PROGRESS_REPORTER * m_progressReporter
Progress reporter to track the progress of the operation, may be nullptr.
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()
Define a library symbol object.
Builds the KiCad schematic from a parsed OrCAD design.
SCH_SHEET * Convert(SCH_SHEET *aRootSheet)
Populate the schematic.
std::vector< LIB_SYMBOL * > BuildSymbolLibrary()
Build KiCad library symbols from the design cache and packages alone (no schematic pages),...
Holds all the data relating to one schematic.
int FixupJunctionsAfterImport()
Add junctions to this schematic where required.
bool IsValid() const
A simple test if the schematic is loaded, not a complete one.
void SetTopLevelSheets(const std::vector< SCH_SHEET * > &aSheets)
SCH_SHEET_PATH & CurrentSheet() const
const std::vector< std::unique_ptr< LIB_SYMBOL > > & loadOlbSymbols(const wxString &aLibraryPath)
Parse an .OLB and build its KiCad library symbols, cached by library path.
std::map< wxString, std::vector< std::unique_ptr< LIB_SYMBOL > > > m_libCache
bool CanReadSchematicFile(const wxString &aFileName) const override
The .dsn extension collides with SPECCTRA PCB session files (plain text), so beyond the extension gat...
SCH_SHEET * LoadSchematicFile(const wxString &aFileName, SCHEMATIC *aSchematic, SCH_SHEET *aAppendToMe=nullptr, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Orchestration: root sheet/screen boilerplate (honoring aAppendToMe), CFB open, Library parse + versio...
LIB_SYMBOL * LoadSymbol(const wxString &aLibraryPath, const wxString &aAliasName, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Load a LIB_SYMBOL object having aPartName from the aLibraryPath containing a library format that this...
void EnumerateSymbolLib(wxArrayString &aSymbolNameList, const wxString &aLibraryPath, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Enumerate the symbols of an OrCAD .OLB library (an OLE2/CFB compound document with the same Library/C...
virtual bool CanReadSchematicFile(const wxString &aFileName) const
Checks if this SCH_IO can read the specified schematic file.
const KIID & GetUuid() const
void SetFileName(const wxString &aFileName)
Set the file name for this screen to aFileName.
void UpdateAllScreenReferences() const
Update all the symbol references for this sheet path.
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
void SetFileName(const wxString &aFilename)
SCH_SCREEN * GetScreen() const
void SetScreen(SCH_SCREEN *aScreen)
Set the SCH_SCREEN associated with this sheet to aScreen.
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
const std::vector< uint8_t > COMPOUND_FILE_HEADER
bool fileHasBinaryHeader(const wxString &aFilePath, const std::vector< uint8_t > &aHeader, size_t aOffset)
Check if a file starts with a defined binary header.
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 OrcadParseCache(const std::vector< char > &aData, const std::vector< std::string > &aStrings, const ORCAD_WARN_FN &aWarn, std::map< std::string, ORCAD_SYMBOL_DEF > &aSymbols, std::map< std::string, ORCAD_PACKAGE > &aPackages)
Scan a Cache-framed stream (the 'Cache' stream itself, or any 'Packages/<name>' stream — they share t...
Parsers for the DSN 'Cache' stream and the 'Packages/<name>' streams: symbol definitions with graphic...
ORCAD_CONVERTER turns a parsed ORCAD_DESIGN into KiCad schematic objects.
ORCAD_LIBRARY_INFO OrcadParseLibrary(const std::vector< char > &aData)
Parse the whole 'Library' stream.
Parser for the DSN root 'Library' stream: format version, fonts, page settings and the global string ...
void OrcadParseOlbSymbolStreamV2(const std::vector< char > &aData, const std::vector< std::string > &aStrings, std::map< std::string, ORCAD_SYMBOL_DEF > &aSymbols)
Parse one v2.0 .OLB 'Symbols/<name>' stream (a single special symbol: power, port,...
std::map< uint32_t, std::string > OrcadReadHierarchyLinks(const std::vector< char > &aData, const std::vector< std::string > &aStrings, const ORCAD_WARN_FN &aWarn, std::map< uint32_t, std::string > *aOccurrenceRefs)
Parse a 'Views/<folder>/Hierarchy/Hierarchy' stream into block-instance links: block db id -> child f...
ORCAD_RAW_PAGE OrcadParsePageV2(const std::vector< char > &aData, const std::vector< std::string > &aStrings, const ORCAD_WARN_FN &)
Parse one v2.0 (pre-2003) 'Views/<folder>/Pages/<page>' stream.
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)
Parse one v2.0 .OLB 'Packages/<name>' stream (a part's inline symbol definitions plus its package/dev...
ORCAD_OCC_SCOPE OrcadReadOccurrenceTree(const std::vector< char > &aData, const ORCAD_WARN_FN &aWarn)
Parse the root folder 'Hierarchy/Hierarchy' stream into the full occurrence tree: per-scope part refe...
std::vector< std::string > OrcadParsePageOrder(const std::vector< char > &aData)
Parse the 'Views/<folder>/Schematic' stream and return the folder's page names in display order.
ORCAD_RAW_PAGE OrcadParsePage(const std::vector< char > &aData, const std::vector< std::string > &aStrings, const ORCAD_WARN_FN &aWarn)
Parse one 'Views/<folder>/Pages/<page>' stream into raw structure lists.
Parsers for the per-page streams 'Views/<folder>/Pages/<page>', the per-folder page-order stream 'Vie...
bool OrcadPageHasHierarchyBlocks(const ORCAD_RAW_PAGE &aPage)
True when the page contains hierarchical block instances (DrawnInstance, type 12).
Plain data records produced by the OrCAD Capture DSN stream parsers and consumed by ORCAD_CONVERTER.
std::function< void(const wxString &aMsg)> ORCAD_WARN_FN
Warning sink shared by all parser entry points (recoverable-issue channel).
std::string OrcadNormalizeCfbName(const std::string &aName)
Definition of the SCH_SHEET_PATH and SCH_SHEET_LIST classes for Eeschema.
Everything parsed from one .DSN, as handed to ORCAD_CONVERTER.
ORCAD_LIBRARY_INFO library
ORCAD_OCC_SCOPE occurrenceRoot
Occurrence tree from the root folder Hierarchy stream.
std::map< std::string, ORCAD_SYMBOL_DEF > symbols
cache, keyed by cache name
std::map< std::string, ORCAD_PACKAGE > packages
keyed by package name
std::string name
design (root schematic) name
std::map< std::string, std::vector< ORCAD_RAW_PAGE > > childFolderPages
Child schematic folder pages, keyed by lower-cased folder name; instantiated once per hierarchical bl...
std::vector< ORCAD_RAW_PAGE > pages
root schematic folder pages
Structure type 12: a hierarchical block instance placed on a page.
std::string childName
child folder, when embedded
std::vector< std::string > strings
global string table
std::string schematicName
root schematic folder name
A hierarchical block occurrence: one placement of a child schematic folder on a parent page.
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
One node of the design occurrence tree parsed from the root Hierarchy stream.
std::vector< ORCAD_OCC_BLOCK > blocks
hierarchical block occurrences
std::map< uint32_t, std::string > partRefs
type-13 dbId -> occurrence refdes
One parsed 'Views/<folder>/Pages/<page>' stream, raw structure lists in stream order.
std::vector< ORCAD_DRAWN_INSTANCE > blocks
hierarchical blocks (detection only)