KiCad PCB EDA Suite
Loading...
Searching...
No Matches
orcad_library.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 <cstdint>
26#include <utility>
27
28#include <ki_exception.h>
29
30
31namespace
32{
33
34int32_t i32At( const std::vector<uint8_t>& aBytes, size_t aOffset )
35{
36 return static_cast<int32_t>( static_cast<uint32_t>( aBytes[aOffset] )
37 | static_cast<uint32_t>( aBytes[aOffset + 1] ) << 8
38 | static_cast<uint32_t>( aBytes[aOffset + 2] ) << 16
39 | static_cast<uint32_t>( aBytes[aOffset + 3] ) << 24 );
40}
41
42} // namespace
43
44
46{
47 ORCAD_PAGE_SETTINGS settings;
48
49 aStream.Skip( 8 ); // create/modify dates
50 aStream.Skip( 16 ); // unknown
51
52 settings.width = aStream.ReadU32();
53 settings.height = aStream.ReadU32();
54 settings.pinToPin = aStream.ReadU32();
55
56 aStream.Skip( 2 );
57 aStream.Skip( 4 ); // horizontal/vertical count
58 aStream.Skip( 2 );
59 aStream.Skip( 8 ); // horizontal/vertical width
60 aStream.Skip( 48 ); // unknown
61 aStream.Skip( 24 ); // grid-reference character settings
62
63 // 8 x u32 flags; flags[0] set = metric units (width/height in um, not mils)
64 uint32_t flags[8];
65
66 for( uint32_t& flag : flags )
67 flag = aStream.ReadU32();
68
69 settings.isMetric = flags[0] != 0;
70
71 return settings;
72}
73
74
75ORCAD_LIBRARY_INFO OrcadParseLibrary( const std::vector<char>& aData )
76{
77 ORCAD_STREAM stream( aData );
79
80 // Introduction NUL-terminated in fixed 32-byte buffer; version words at offset 32
81 lib.introduction = stream.ReadZt();
82 stream.Seek( 32 );
83
84 lib.versionMajor = stream.ReadU16();
85 lib.versionMinor = stream.ReadU16();
86 stream.Skip( 8 ); // create/modify dates
87 stream.Skip( 4 ); // zeros
88
89 // u16 font count stores count+1; count-1 LOGFONTA records follow
90 int fontCount = stream.ReadU16();
91
92 for( int i = 0; i < fontCount - 1; i++ )
93 {
94 // 60-byte LOGFONTA; lfHeight i32 +0, lfWeight i32 +16, lfItalic u8 +20, lfFaceName char[32] +28
95 std::vector<uint8_t> rec = stream.ReadBytes( 60 );
96
97 ORCAD_FONT font;
98 font.height = i32At( rec, 0 );
99 font.italic = rec[20] != 0;
100 font.bold = i32At( rec, 16 ) >= 600;
101
102 size_t faceLen = 0;
103
104 while( faceLen < 32 && rec[28 + faceLen] != 0 )
105 faceLen++;
106
107 font.face.assign( reinterpret_cast<const char*>( rec.data() + 28 ), faceLen );
108
109 lib.fonts.push_back( std::move( font ) );
110 }
111
112 if( lib.versionMajor >= 2 )
113 {
114 // u16 length (always 24), 2 * length bytes, 8 bytes
115 uint16_t someLen = stream.ReadU16();
116 stream.Skip( 2 * static_cast<size_t>( someLen ) );
117 stream.Skip( 8 );
118 }
119 else
120 {
121 // v1.x fixed 42 bytes (17 x u16 + 2 x u32, no count field)
122 stream.Skip( 42 );
123 }
124
125 // 8 named part fields (Part Reference, Value, ...)
126 for( int i = 0; i < 8; i++ )
127 lib.partFields.push_back( stream.ReadLzt() );
128
129 ORCAD_PAGE_SETTINGS settings = OrcadParsePageSettings( stream );
130 lib.pinToPin = settings.pinToPin;
131
132 size_t tablePos = stream.GetOffset();
133
134 // Count = u32 (modern) or u16 (legacy). Wrong width yields implausible count, so
135 // sanity-check each vs remaining bytes; failed read keeps parsed strings, never aborts file
136 auto readStringTable = [&]( bool aU16Count ) -> bool
137 {
138 stream.Seek( tablePos );
139 lib.strings.clear();
140
141 uint32_t count = aU16Count ? stream.ReadU16() : stream.ReadU32();
142
143 if( count > 2000000
144 || static_cast<uint64_t>( count ) * 3
145 > static_cast<uint64_t>( stream.Remaining() ) + 16 )
146 {
147 return false;
148 }
149
150 for( uint32_t i = 0; i < count; i++ )
151 lib.strings.push_back( stream.ReadLzt() );
152
153 return true;
154 };
155
156 // Legacy v2.x always u16; u32-first can pass sanity w/ bogus large count -> huge alloc
157 bool u16First = lib.versionMajor < 3;
158
159 try
160 {
161 if( !readStringTable( u16First ) && !readStringTable( !u16First ) )
162 {
163 stream.Seek( tablePos );
164 lib.strings.clear();
165 }
166 }
167 catch( const IO_ERROR& )
168 {
169 try
170 {
171 if( !readStringTable( !u16First ) )
172 {
173 stream.Seek( tablePos );
174 lib.strings.clear();
175 }
176 }
177 catch( const IO_ERROR& )
178 {
179 stream.Seek( tablePos );
180 lib.strings.clear();
181 }
182 }
183
184 // Alias pairs and root schematic folder follow; both optional, so read failure
185 // must not sink whole library
186 try
187 {
188 uint16_t aliasCount = stream.ReadU16();
189
190 if( static_cast<uint64_t>( aliasCount ) * 2 <= stream.Remaining() + 4 )
191 {
192 for( uint16_t i = 0; i < aliasCount; i++ )
193 {
194 std::string alias = stream.ReadLzt();
195 std::string part = stream.ReadLzt();
196
197 lib.partAliases.emplace_back( std::move( alias ), std::move( part ) );
198 }
199 }
200
201 // Design files (not standalone libs) carry root schematic folder
202 if( lib.introduction.rfind( "OrCAD Windows Design", 0 ) == 0 )
203 {
204 stream.Skip( 8 );
205 lib.schematicName = stream.ReadLzt();
206 }
207 }
208 catch( const IO_ERROR& )
209 {
210 }
211
212 return lib;
213}
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
Little-endian byte cursor over one OrCAD Capture DSN stream.
size_t Remaining() const
Bytes left; 0 when the cursor is at or past the end.
size_t GetOffset() const
uint16_t ReadU16()
std::vector< uint8_t > ReadBytes(size_t aCount)
Read exactly aCount bytes; throws IO_ERROR on overrun.
void Skip(size_t aCount)
Advance the cursor; throws IO_ERROR when aCount exceeds the remaining bytes.
std::string ReadLzt()
Read a length-prefixed zero-terminated string: u16 length, length content bytes, then a mandatory 0x0...
std::string ReadZt()
Read a bare zero-terminated string (no length prefix); consumes the NUL.
void Seek(size_t aOffset)
Set the absolute cursor position.
uint32_t ReadU32()
ORCAD_PAGE_SETTINGS OrcadParsePageSettings(ORCAD_STREAM &aStream)
Read one 156-byte PageSettings block at the current cursor (embedded both in the Library stream and i...
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 ...
One text font from the Library stream (a Win32 LOGFONTA record, 60 bytes: lfHeight i32 at +0,...
bool bold
lfWeight >= 600
std::string face
int height
raw lfHeight (typically negative)
Decoded Library stream: format version, fonts, and the string table that all property name/value indi...
std::string introduction
fixed 32-byte NUL-terminated buffer
std::vector< std::string > strings
global string table
std::vector< ORCAD_FONT > fonts
std::vector< std::pair< std::string, std::string > > partAliases
std::string schematicName
root schematic folder name
std::vector< std::string > partFields
the 8 named part fields
The 156-byte PageSettings block embedded in the Library stream and in every Page stream: 8 bytes crea...