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{
47}
48
49
50ORCAD_STREAM::ORCAD_STREAM( const std::vector<char>& aData ) :
51 ORCAD_STREAM( aData.data(), aData.size() )
52{
53}
54
55
56void ORCAD_STREAM::requireBytes( size_t aCount ) const
57{
58 if( m_offset > m_size || aCount > m_size - m_offset )
59 {
60 THROW_IO_ERROR( wxString::Format( wxS( "OrCAD stream: read of %zu bytes past end at 0x%zx "
61 "(stream size 0x%zx)" ),
62 aCount, m_offset, m_size ) );
63 }
64}
65
66
68{
69 requireBytes( 1 );
70 return m_data[m_offset++];
71}
72
73
75{
76 return static_cast<int8_t>( ReadU8() );
77}
78
79
81{
82 requireBytes( 2 );
83 uint16_t v = static_cast<uint16_t>( m_data[m_offset] )
84 | static_cast<uint16_t>( m_data[m_offset + 1] ) << 8;
85 m_offset += 2;
86 return v;
87}
88
89
91{
92 return static_cast<int16_t>( ReadU16() );
93}
94
95
97{
98 requireBytes( 4 );
99 uint32_t v = static_cast<uint32_t>( m_data[m_offset] )
100 | static_cast<uint32_t>( m_data[m_offset + 1] ) << 8
101 | static_cast<uint32_t>( m_data[m_offset + 2] ) << 16
102 | static_cast<uint32_t>( m_data[m_offset + 3] ) << 24;
103 m_offset += 4;
104 return v;
105}
106
107
109{
110 return static_cast<int32_t>( ReadU32() );
111}
112
113
115{
116 size_t lenPos = m_offset;
117 uint16_t len = ReadU16();
118
119 if( m_offset > m_size || static_cast<size_t>( len ) + 1 > m_size - m_offset )
120 {
121 THROW_IO_ERROR( wxString::Format( wxS( "OrCAD stream: string of length %u at 0x%zx "
122 "exceeds stream" ),
123 len, lenPos ) );
124 }
125
126 std::string s( reinterpret_cast<const char*>( m_data + m_offset ), len );
127 m_offset += len;
128
129 if( m_data[m_offset] != 0 )
130 {
131 THROW_IO_ERROR( wxString::Format( wxS( "OrCAD stream: string at 0x%zx missing NUL "
132 "terminator" ),
133 lenPos ) );
134 }
135
136 m_offset++;
137 return s;
138}
139
140
142{
143 if( m_offset >= m_size )
144 THROW_IO_ERROR( wxString::Format( wxS( "OrCAD stream: read past end at 0x%zx" ), m_offset ) );
145
146 const void* nul = memchr( m_data + m_offset, 0, m_size - m_offset );
147
148 if( !nul )
149 {
150 THROW_IO_ERROR( wxString::Format( wxS( "OrCAD stream: unterminated string at 0x%zx" ),
151 m_offset ) );
152 }
153
154 size_t end = static_cast<const uint8_t*>( nul ) - m_data;
155 std::string s( reinterpret_cast<const char*>( m_data + m_offset ), end - m_offset );
156 m_offset = end + 1;
157 return s;
158}
159
160
161std::vector<uint8_t> ORCAD_STREAM::ReadBytes( size_t aCount )
162{
163 requireBytes( aCount );
164 std::vector<uint8_t> v( m_data + m_offset, m_data + m_offset + aCount );
165 m_offset += aCount;
166 return v;
167}
168
169
170void ORCAD_STREAM::Skip( size_t aCount )
171{
172 if( m_offset > m_size || aCount > m_size - m_offset )
173 {
174 THROW_IO_ERROR( wxString::Format( wxS( "OrCAD stream: skip of %zu bytes past end at 0x%zx "
175 "(stream size 0x%zx)" ),
176 aCount, m_offset, m_size ) );
177 }
178
179 m_offset += aCount;
180}
181
182
183int ORCAD_STREAM::PeekU8( size_t aAhead ) const
184{
185 size_t pos = m_offset + aAhead;
186
187 if( pos >= m_size )
188 return -1;
189
190 return m_data[pos];
191}
192
193
194bool ORCAD_STREAM::PeekMatches( const uint8_t* aBytes, size_t aCount, size_t aAhead ) const
195{
196 size_t pos = m_offset + aAhead;
197
198 if( pos > m_size || aCount > m_size - pos )
199 return false;
200
201 return memcmp( m_data + pos, aBytes, aCount ) == 0;
202}
203
204
205bool ORCAD_STREAM::AtPreamble( size_t aAhead ) const
206{
207 return PeekMatches( PREAMBLE, 4, aAhead );
208}
209
210
211bool ORCAD_STREAM::HasPreambleAt( size_t aAbsoluteOffset ) const
212{
213 if( aAbsoluteOffset > m_size || 4 > m_size - aAbsoluteOffset )
214 return false;
215
216 return memcmp( m_data + aAbsoluteOffset, PREAMBLE, 4 ) == 0;
217}
218
219
220size_t ORCAD_STREAM::FindPreamble( size_t aFrom ) const
221{
222 if( m_size < 4 )
223 return npos;
224
225 for( size_t pos = aFrom; pos + 4 <= m_size; pos++ )
226 {
227 if( m_data[pos] == PREAMBLE[0] && memcmp( m_data + pos, PREAMBLE, 4 ) == 0 )
228 return pos;
229 }
230
231 return npos;
232}
233
234
235void ORCAD_STREAM::Expect( const uint8_t* aBytes, size_t aCount, const wxString& aWhat )
236{
237 size_t pos = m_offset;
238 std::vector<uint8_t> got = ReadBytes( aCount );
239
240 if( memcmp( got.data(), aBytes, aCount ) != 0 )
241 {
242 THROW_IO_ERROR( wxString::Format( wxS( "OrCAD stream: expected %s got %s at 0x%zx (%s)" ),
243 hexOf( aBytes, aCount ), hexOf( got.data(), aCount ),
244 pos, aWhat ) );
245 }
246}
247
248
249void ORCAD_STREAM::ExpectByte( uint8_t aValue, const wxString& aWhat )
250{
251 Expect( &aValue, 1, aWhat );
252}
253
254
255void ORCAD_STREAM::ExpectPreamble( const wxString& aWhat )
256{
257 Expect( PREAMBLE, 4, aWhat );
258}
259
260
262{
263 if( AtPreamble() )
264 {
265 Skip( 4 );
266 uint32_t trail = ReadU32();
267 Skip( trail );
268 }
269}
270
271
272wxString FromOrcadString( const std::string& aText )
273{
274 if( aText.empty() )
275 return wxEmptyString;
276
277 wxString converted( aText.c_str(), wxCSConv( wxFONTENCODING_CP1252 ) );
278
279 if( converted.IsEmpty() )
280 converted = wxString::From8BitData( aText.c_str(), aText.size() );
281
282 // OrCAD multi-line text uses CR LF; raw CR renders as replacement glyph in KiCad.
283 converted.Replace( wxS( "\r\n" ), wxS( "\n" ) );
284 converted.Replace( wxS( "\r" ), wxS( "\n" ) );
285
286 return converted;
287}
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
Peek one byte at cursor + aAhead without advancing.
size_t FindPreamble(size_t aFrom) const
Find the next preamble at or after the given absolute offset.
void SkipOptionalPreambleBlock()
If the preamble sits at the cursor, consume it plus its u32 trailLen and the trailLen trailing bytes;...
bool AtPreamble(size_t aAhead=0) const
True when the 4 preamble bytes FF E4 5C 39 sit at cursor + aAhead.
uint8_t ReadU8()
static constexpr size_t npos
Returned by FindPreamble() when no further preamble exists.
int8_t ReadI8()
bool HasPreambleAt(size_t aAbsoluteOffset) const
True when the preamble sits at the given absolute offset (false if out of range).
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.
static constexpr uint8_t PREAMBLE[4]
Magic preceding every framed structure body: FF E4 5C 39.
std::string ReadLzt()
Read a length-prefixed zero-terminated string: u16 length, length content bytes, then a mandatory 0x0...
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()
Read a bare zero-terminated string (no length prefix); consumes the NUL.
bool PeekMatches(const uint8_t *aBytes, size_t aCount, size_t aAhead=0) const
Compare aCount bytes at cursor + aAhead against aBytes without advancing.
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)
Consume aCount bytes and require them to equal aBytes; throws IO_ERROR naming aWhat and showing expec...
int16_t ReadI16()
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
wxString FromOrcadString(const std::string &aText)
Decode raw stream bytes (Windows-1252) into a wxString for UI/UX use.
static wxString hexOf(const uint8_t *aBytes, size_t aCount)
VECTOR2I end