KiCad PCB EDA Suite
Loading...
Searching...
No Matches
orcad_stream.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 <cstring>
27
28#include <ki_exception.h>
29#include <wx/strconv.h>
30
31
32static wxString hexOf( const uint8_t* aBytes, size_t aCount )
33{
34 wxString out;
35
36 for( size_t i = 0; i < aCount; i++ )
37 out += wxString::Format( wxS( "%02x" ), aBytes[i] );
38
39 return out;
40}
41
42
43ORCAD_STREAM::ORCAD_STREAM( const void* aData, size_t aLength ) :
44 m_data( static_cast<const uint8_t*>( aData ) ),
45 m_size( aLength ),
46 m_offset( 0 ),
47 m_nesting( 0 )
48{
49}
50
51
52ORCAD_STREAM::ORCAD_STREAM( const std::vector<char>& aData ) :
53 ORCAD_STREAM( aData.data(), aData.size() )
54{
55}
56
57
58ORCAD_STREAM::NEST_GUARD::NEST_GUARD( ORCAD_STREAM& aStream, const wxString& aWhat ) :
59 m_stream( aStream )
60{
61 // Throw before the count goes up; a failed constructor gets no destructor
62 if( m_stream.m_nesting >= MAX_NESTING )
63 {
64 THROW_IO_ERRORF( wxS( "OrCAD %s: nested deeper than %d levels" ), aWhat, MAX_NESTING );
65 }
66
67 m_stream.m_nesting++;
68}
69
70
75
76
78 m_stream( aStream ),
79 m_savedSize( aStream.m_size )
80{
81 if( aEnd > m_savedSize || aEnd < aStream.m_offset )
82 THROW_IO_ERRORF( wxS( "OrCAD stream: invalid record bound 0x%zx at 0x%zx" ), aEnd, aStream.m_offset );
83
84 aStream.m_size = aEnd;
85}
86
87
92
93
94void ORCAD_STREAM::requireBytes( size_t aCount ) const
95{
96 if( m_offset > m_size || aCount > m_size - m_offset )
97 {
98 THROW_IO_ERRORF( wxS( "OrCAD stream: read of %zu bytes past end at 0x%zx (stream size 0x%zx)" ),
99 aCount,
100 m_offset,
101 m_size );
102 }
103}
104
105
107{
108 requireBytes( 1 );
109 return m_data[m_offset++];
110}
111
112
114{
115 requireBytes( 2 );
116 uint16_t v = static_cast<uint16_t>( m_data[m_offset] )
117 | static_cast<uint16_t>( m_data[m_offset + 1] ) << 8;
118 m_offset += 2;
119 return v;
120}
121
122
124{
125 return static_cast<int16_t>( ReadU16() );
126}
127
128
130{
131 requireBytes( 4 );
132 uint32_t v = static_cast<uint32_t>( m_data[m_offset] )
133 | static_cast<uint32_t>( m_data[m_offset + 1] ) << 8
134 | static_cast<uint32_t>( m_data[m_offset + 2] ) << 16
135 | static_cast<uint32_t>( m_data[m_offset + 3] ) << 24;
136 m_offset += 4;
137 return v;
138}
139
140
142{
143 return static_cast<int32_t>( ReadU32() );
144}
145
146
148{
149 size_t lenPos = m_offset;
150 uint16_t len = ReadU16();
151
152 if( m_offset > m_size || static_cast<size_t>( len ) + 1 > m_size - m_offset )
153 THROW_IO_ERRORF( wxS( "OrCAD stream: string of length %u at 0x%zx exceeds stream" ), len, lenPos );
154
155 std::string s( reinterpret_cast<const char*>( m_data + m_offset ), len );
156 m_offset += len;
157
158 if( m_data[m_offset] != 0 )
159 THROW_IO_ERRORF( wxS( "OrCAD stream: string at 0x%zx missing NUL terminator" ), lenPos );
160
161 m_offset++;
162 return s;
163}
164
165
167{
168 if( m_offset >= m_size )
169 THROW_IO_ERRORF( wxS( "OrCAD stream: read past end at 0x%zx" ), m_offset );
170
171 const void* nul = memchr( m_data + m_offset, 0, m_size - m_offset );
172
173 if( !nul )
174 THROW_IO_ERRORF( wxS( "OrCAD stream: unterminated string at 0x%zx" ), m_offset );
175
176 size_t end = static_cast<const uint8_t*>( nul ) - m_data;
177 std::string s( reinterpret_cast<const char*>( m_data + m_offset ), end - m_offset );
178 m_offset = end + 1;
179 return s;
180}
181
182
183std::vector<uint8_t> ORCAD_STREAM::ReadBytes( size_t aCount )
184{
185 requireBytes( aCount );
186 std::vector<uint8_t> v( m_data + m_offset, m_data + m_offset + aCount );
187 m_offset += aCount;
188 return v;
189}
190
191
192void ORCAD_STREAM::Skip( size_t aCount )
193{
194 if( m_offset > m_size || aCount > m_size - m_offset )
195 {
196 THROW_IO_ERRORF( wxS( "OrCAD stream: skip of %zu bytes past end at 0x%zx (stream size 0x%zx)" ),
197 aCount,
198 m_offset,
199 m_size );
200 }
201
202 m_offset += aCount;
203}
204
205
206void ORCAD_STREAM::Seek( size_t aOffset )
207{
208 if( aOffset > m_size )
209 THROW_IO_ERRORF( wxS( "OrCAD stream: seek past end to 0x%zx (stream size 0x%zx)" ), aOffset, m_size );
210
211 m_offset = aOffset;
212}
213
214
215int ORCAD_STREAM::PeekU8( size_t aAhead ) const
216{
217 size_t pos = m_offset + aAhead;
218
219 if( pos >= m_size )
220 return -1;
221
222 return m_data[pos];
223}
224
225
226bool ORCAD_STREAM::PeekMatches( const uint8_t* aBytes, size_t aCount, size_t aAhead ) const
227{
228 size_t pos = m_offset + aAhead;
229
230 if( pos > m_size || aCount > m_size - pos )
231 return false;
232
233 return memcmp( m_data + pos, aBytes, aCount ) == 0;
234}
235
236
237bool ORCAD_STREAM::AtPreamble( size_t aAhead ) const
238{
239 return PeekMatches( PREAMBLE, 4, aAhead );
240}
241
242
243void ORCAD_STREAM::Expect( const uint8_t* aBytes, size_t aCount, const wxString& aWhat )
244{
245 size_t pos = m_offset;
246 std::vector<uint8_t> got = ReadBytes( aCount );
247
248 if( memcmp( got.data(), aBytes, aCount ) != 0 )
249 {
250 THROW_IO_ERRORF( wxS( "OrCAD stream: expected %s got %s at 0x%zx (%s)" ),
251 hexOf( aBytes, aCount ),
252 hexOf( got.data(), aCount ),
253 pos,
254 aWhat );
255 }
256}
257
258
259void ORCAD_STREAM::ExpectByte( uint8_t aValue, const wxString& aWhat )
260{
261 Expect( &aValue, 1, aWhat );
262}
263
264
265void ORCAD_STREAM::ExpectPreamble( const wxString& aWhat )
266{
267 Expect( PREAMBLE, 4, aWhat );
268}
269
270
272{
273 if( AtPreamble() )
274 {
275 Skip( 4 );
276 uint32_t trail = ReadU32();
277 Skip( trail );
278 }
279}
280
281
282wxString FromOrcadString( const std::string& aText )
283{
284 if( aText.empty() )
285 return wxEmptyString;
286
287 wxString converted( aText.c_str(), wxCSConv( wxFONTENCODING_CP1252 ) );
288
289 if( converted.IsEmpty() )
290 converted = wxString::From8BitData( aText.c_str(), aText.size() );
291
292 // OrCAD multi-line text uses CR LF; raw CR renders as replacement glyph in KiCad.
293 converted.Replace( wxS( "\r\n" ), wxS( "\n" ) );
294 converted.Replace( wxS( "\r" ), wxS( "\n" ) );
295
296 return converted;
297}
298
299
300static char orcadFold( char aChar )
301{
302 return aChar >= 'A' && aChar <= 'Z' ? static_cast<char>( aChar - 'A' + 'a' ) : aChar;
303}
304
305
306std::string OrcadLower( std::string_view aText )
307{
308 std::string out( aText );
309 std::transform( out.begin(), out.end(), out.begin(), orcadFold );
310 return out;
311}
312
313
314bool OrcadIEquals( std::string_view aLeft, std::string_view aRight )
315{
316 return aLeft.size() == aRight.size()
317 && std::equal( aLeft.begin(), aLeft.end(), aRight.begin(),
318 []( char a, char b )
319 {
320 return orcadFold( a ) == orcadFold( b );
321 } );
322}
LIMIT_GUARD(ORCAD_STREAM &aStream, size_t aEnd)
NEST_GUARD(ORCAD_STREAM &aStream, const wxString &aWhat)
ORCAD_STREAM & m_stream
static constexpr int MAX_NESTING
Nesting limit of the recursive readers; real files stay far below it.
void ExpectPreamble(const wxString &aWhat)
Consume the 4 preamble bytes; throws IO_ERROR naming aWhat when absent.
const uint8_t * m_data
int PeekU8(size_t aAhead=0) const
– non-throwing lookahead ---------------------------------------------------—
void SkipOptionalPreambleBlock()
Legacy primitives have no trailing preamble.
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) ----—
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.
static constexpr uint8_t PREAMBLE[4]
Magic preceding every framed structure body: FF E4 5C 39.
std::string ReadLzt()
– strings ----------------------------------------------------------------—
ORCAD_STREAM(const void *aData, size_t aLength)
void requireBytes(size_t aCount) const
Throw IO_ERROR unless aCount bytes remain at the cursor.
std::string ReadZt()
Consumes the NUL; throws IO_ERROR if no terminator remains.
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()
#define THROW_IO_ERRORF(msg,...)
static char orcadFold(char aChar)
static wxString hexOf(const uint8_t *aBytes, size_t aCount)
std::string OrcadLower(std::string_view aText)
Fold ASCII letters only.
wxString FromOrcadString(const std::string &aText)
Use an 8-bit fallback if Windows-1252 decoding fails.
bool OrcadIEquals(std::string_view aLeft, std::string_view aRight)
Case-insensitive comparison with the folding of OrcadLower.
VECTOR2I end