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 <cstring>
26
27#include <ki_exception.h>
28#include <wx/strconv.h>
29
30
31static wxString hexOf( const uint8_t* aBytes, size_t aCount )
32{
33 wxString out;
34
35 for( size_t i = 0; i < aCount; i++ )
36 out += wxString::Format( wxS( "%02x" ), aBytes[i] );
37
38 return out;
39}
40
41
42ORCAD_STREAM::ORCAD_STREAM( const void* aData, size_t aLength ) :
43 m_data( static_cast<const uint8_t*>( aData ) ),
44 m_size( aLength ),
45 m_offset( 0 ),
46 m_nesting( 0 )
47{
48}
49
50
51ORCAD_STREAM::ORCAD_STREAM( const std::vector<char>& aData ) :
52 ORCAD_STREAM( aData.data(), aData.size() )
53{
54}
55
56
57ORCAD_STREAM::NEST_GUARD::NEST_GUARD( ORCAD_STREAM& aStream, const wxString& aWhat ) :
58 m_stream( aStream )
59{
60 // Throw before the count goes up; a failed constructor gets no destructor
61 if( m_stream.m_nesting >= MAX_NESTING )
62 {
63 THROW_IO_ERRORF( wxS( "OrCAD %s: nested deeper than %d levels" ), aWhat, MAX_NESTING );
64 }
65
66 m_stream.m_nesting++;
67}
68
69
74
75
77 m_stream( aStream ),
78 m_savedSize( aStream.m_size )
79{
80 if( aEnd > m_savedSize || aEnd < aStream.m_offset )
81 THROW_IO_ERRORF( wxS( "OrCAD stream: invalid record bound 0x%zx at 0x%zx" ), aEnd, aStream.m_offset );
82
83 aStream.m_size = aEnd;
84}
85
86
91
92
93void ORCAD_STREAM::requireBytes( size_t aCount ) const
94{
95 if( m_offset > m_size || aCount > m_size - m_offset )
96 {
97 THROW_IO_ERRORF( wxS( "OrCAD stream: read of %zu bytes past end at 0x%zx (stream size 0x%zx)" ),
98 aCount,
100 m_size );
101 }
102}
103
104
106{
107 requireBytes( 1 );
108 return m_data[m_offset++];
109}
110
111
113{
114 return static_cast<int8_t>( ReadU8() );
115}
116
117
119{
120 requireBytes( 2 );
121 uint16_t v = static_cast<uint16_t>( m_data[m_offset] )
122 | static_cast<uint16_t>( m_data[m_offset + 1] ) << 8;
123 m_offset += 2;
124 return v;
125}
126
127
129{
130 return static_cast<int16_t>( ReadU16() );
131}
132
133
135{
136 requireBytes( 4 );
137 uint32_t v = static_cast<uint32_t>( m_data[m_offset] )
138 | static_cast<uint32_t>( m_data[m_offset + 1] ) << 8
139 | static_cast<uint32_t>( m_data[m_offset + 2] ) << 16
140 | static_cast<uint32_t>( m_data[m_offset + 3] ) << 24;
141 m_offset += 4;
142 return v;
143}
144
145
147{
148 return static_cast<int32_t>( ReadU32() );
149}
150
151
153{
154 size_t lenPos = m_offset;
155 uint16_t len = ReadU16();
156
157 if( m_offset > m_size || static_cast<size_t>( len ) + 1 > m_size - m_offset )
158 THROW_IO_ERRORF( wxS( "OrCAD stream: string of length %u at 0x%zx exceeds stream" ), len, lenPos );
159
160 std::string s( reinterpret_cast<const char*>( m_data + m_offset ), len );
161 m_offset += len;
162
163 if( m_data[m_offset] != 0 )
164 THROW_IO_ERRORF( wxS( "OrCAD stream: string at 0x%zx missing NUL terminator" ), lenPos );
165
166 m_offset++;
167 return s;
168}
169
170
172{
173 if( m_offset >= m_size )
174 THROW_IO_ERRORF( wxS( "OrCAD stream: read past end at 0x%zx" ), m_offset );
175
176 const void* nul = memchr( m_data + m_offset, 0, m_size - m_offset );
177
178 if( !nul )
179 THROW_IO_ERRORF( wxS( "OrCAD stream: unterminated string at 0x%zx" ), m_offset );
180
181 size_t end = static_cast<const uint8_t*>( nul ) - m_data;
182 std::string s( reinterpret_cast<const char*>( m_data + m_offset ), end - m_offset );
183 m_offset = end + 1;
184 return s;
185}
186
187
188std::vector<uint8_t> ORCAD_STREAM::ReadBytes( size_t aCount )
189{
190 requireBytes( aCount );
191 std::vector<uint8_t> v( m_data + m_offset, m_data + m_offset + aCount );
192 m_offset += aCount;
193 return v;
194}
195
196
197void ORCAD_STREAM::Skip( size_t aCount )
198{
199 if( m_offset > m_size || aCount > m_size - m_offset )
200 {
201 THROW_IO_ERRORF( wxS( "OrCAD stream: skip of %zu bytes past end at 0x%zx (stream size 0x%zx)" ),
202 aCount,
203 m_offset,
204 m_size );
205 }
206
207 m_offset += aCount;
208}
209
210
211void ORCAD_STREAM::Seek( size_t aOffset )
212{
213 if( aOffset > m_size )
214 THROW_IO_ERRORF( wxS( "OrCAD stream: seek past end to 0x%zx (stream size 0x%zx)" ), aOffset, m_size );
215
216 m_offset = aOffset;
217}
218
219
220int ORCAD_STREAM::PeekU8( size_t aAhead ) const
221{
222 size_t pos = m_offset + aAhead;
223
224 if( pos >= m_size )
225 return -1;
226
227 return m_data[pos];
228}
229
230
231bool ORCAD_STREAM::PeekMatches( const uint8_t* aBytes, size_t aCount, size_t aAhead ) const
232{
233 size_t pos = m_offset + aAhead;
234
235 if( pos > m_size || aCount > m_size - pos )
236 return false;
237
238 return memcmp( m_data + pos, aBytes, aCount ) == 0;
239}
240
241
242bool ORCAD_STREAM::AtPreamble( size_t aAhead ) const
243{
244 return PeekMatches( PREAMBLE, 4, aAhead );
245}
246
247
248void ORCAD_STREAM::Expect( const uint8_t* aBytes, size_t aCount, const wxString& aWhat )
249{
250 size_t pos = m_offset;
251 std::vector<uint8_t> got = ReadBytes( aCount );
252
253 if( memcmp( got.data(), aBytes, aCount ) != 0 )
254 {
255 THROW_IO_ERRORF( wxS( "OrCAD stream: expected %s got %s at 0x%zx (%s)" ),
256 hexOf( aBytes, aCount ),
257 hexOf( got.data(), aCount ),
258 pos,
259 aWhat );
260 }
261}
262
263
264void ORCAD_STREAM::ExpectByte( uint8_t aValue, const wxString& aWhat )
265{
266 Expect( &aValue, 1, aWhat );
267}
268
269
270void ORCAD_STREAM::ExpectPreamble( const wxString& aWhat )
271{
272 Expect( PREAMBLE, 4, aWhat );
273}
274
275
277{
278 if( AtPreamble() )
279 {
280 Skip( 4 );
281 uint32_t trail = ReadU32();
282 Skip( trail );
283 }
284}
285
286
287wxString FromOrcadString( const std::string& aText )
288{
289 if( aText.empty() )
290 return wxEmptyString;
291
292 wxString converted( aText.c_str(), wxCSConv( wxFONTENCODING_CP1252 ) );
293
294 if( converted.IsEmpty() )
295 converted = wxString::From8BitData( aText.c_str(), aText.size() );
296
297 // OrCAD multi-line text uses CR LF; raw CR renders as replacement glyph in KiCad.
298 converted.Replace( wxS( "\r\n" ), wxS( "\n" ) );
299 converted.Replace( wxS( "\r" ), wxS( "\n" ) );
300
301 return converted;
302}
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 wxString hexOf(const uint8_t *aBytes, size_t aCount)
wxString FromOrcadString(const std::string &aText)
Use an 8-bit fallback if Windows-1252 decoding fails.
VECTOR2I end