KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pads_binary_utils.h
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 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 3
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, you may find one here:
18 * http://www.gnu.org/licenses/gpl-3.0.html
19 * or you may search the http://www.gnu.org website for the version 3 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
24#ifndef PADS_BINARY_UTILS_H_
25#define PADS_BINARY_UTILS_H_
26
27#include <algorithm>
28#include <cstdint>
29#include <cstring>
30#include <optional>
31#include <fstream>
32#include <string>
33#include <vector>
34
35#include <wx/string.h>
36
37#include <ki_exception.h>
38#include <string_utils.h>
39
40namespace PADS_IO
41{
42
43// A live SDB record header carries the FE FF (little-endian 0xFFFE) sentinel.
44constexpr uint16_t SDB_RECORD_SENTINEL = 0xFFFE;
45
46// PADS BASIC coordinate units per mil (one BASIC unit is 1/38100 mil).
47constexpr double SDB_BASIC_PER_MIL = 38100.0;
48
53inline uint8_t getU8( const std::vector<uint8_t>& aData, size_t aOffset )
54{
55 return aData[aOffset];
56}
57
58
59inline uint16_t getU16LE( const std::vector<uint8_t>& aData, size_t aOffset )
60{
61 return static_cast<uint16_t>( aData[aOffset] )
62 | ( static_cast<uint16_t>( aData[aOffset + 1] ) << 8 );
63}
64
65
66inline uint32_t getU32LE( const std::vector<uint8_t>& aData, size_t aOffset )
67{
68 return static_cast<uint32_t>( aData[aOffset] )
69 | ( static_cast<uint32_t>( aData[aOffset + 1] ) << 8 )
70 | ( static_cast<uint32_t>( aData[aOffset + 2] ) << 16 )
71 | ( static_cast<uint32_t>( aData[aOffset + 3] ) << 24 );
72}
73
74
75inline double getF64LE( const std::vector<uint8_t>& aData, size_t aOffset )
76{
77 uint64_t bits = 0;
78
79 for( int i = 0; i < 8; ++i )
80 bits |= static_cast<uint64_t>( aData[aOffset + i] ) << ( 8 * i );
81
82 double value = 0;
83 std::memcpy( &value, &bits, sizeof( value ) );
84
85 return value;
86}
87
88
89inline float getF32LE( const std::vector<uint8_t>& aData, size_t aOffset )
90{
91 uint32_t bits = getU32LE( aData, aOffset );
92 float value = 0;
93 std::memcpy( &value, &bits, sizeof( value ) );
94
95 return value;
96}
97
98
104inline bool ReadFileHeader( const wxString& aFileName, std::vector<uint8_t>& aOut, size_t aCount )
105{
106 std::ifstream file( aFileName.fn_str(), std::ios::binary );
107
108 if( !file.is_open() )
109 return false;
110
111 aOut.resize( aCount );
112 file.read( reinterpret_cast<char*>( aOut.data() ), static_cast<std::streamsize>( aCount ) );
113 aOut.resize( static_cast<size_t>( file.gcount() ) );
114
115 return true;
116}
117
118
122inline bool ReadFileToBuffer( const wxString& aFileName, std::vector<uint8_t>& aOut )
123{
124 std::ifstream file( aFileName.fn_str(), std::ios::binary | std::ios::ate );
125
126 if( !file.is_open() )
127 return false;
128
129 std::streamoff len = file.tellg();
130 file.seekg( 0, std::ios::beg );
131
132 if( len <= 0 )
133 return false;
134
135 aOut.resize( static_cast<size_t>( len ) );
136 file.read( reinterpret_cast<char*>( aOut.data() ), len );
137
138 return file.gcount() == len;
139}
140
141
149inline std::string readFixedStringRaw( const std::vector<uint8_t>& aData, size_t aOffset,
150 size_t aMaxLen )
151{
152 if( aOffset >= aData.size() )
153 return {};
154
155 size_t available = std::min( aMaxLen, aData.size() - aOffset );
156 const uint8_t* start = &aData[aOffset];
157
158 const uint8_t* null_pos = std::find( start, start + available, 0 );
159 size_t len = static_cast<size_t>( null_pos - start );
160
161 for( size_t i = 0; i < len; ++i )
162 {
163 if( start[i] < 0x20 || start[i] == 0x7F )
164 return {};
165 }
166
167 return std::string( reinterpret_cast<const char*>( start ), len );
168}
169
170
174inline std::string readFixedString( const std::vector<uint8_t>& aData, size_t aOffset, size_t aMaxLen )
175{
176 std::string raw = readFixedStringRaw( aData, aOffset, aMaxLen );
177
178 // Trimmed here rather than through StrPurge, which also strips leading whitespace and walks
179 // one before the start on an empty string
180 while( !raw.empty() && raw.back() == ' ' )
181 raw.pop_back();
182
183 return raw;
184}
185
186
192inline bool HasSdbMagic( const std::vector<uint8_t>& aData, uint8_t aMagic1 )
193{
194 return aData.size() >= 2 && aData[0] == 0x00 && aData[1] == aMagic1;
195}
196
197
200{
201 size_t offset;
202 const char* detail;
203};
204
205
215std::optional<SDB_FOOTER_ERROR> CheckSdbFooter( const std::vector<uint8_t>& aData, size_t aFooterStart,
216 const char* aGuid, size_t aGuidLen );
217
218
223void ValidateSdbFooter( const std::vector<uint8_t>& aData, size_t aFooterStart, const char* aGuid,
224 size_t aGuidLen );
225
226
237{
238public:
239 explicit BINARY_CURSOR( const std::vector<uint8_t>& aData ) : m_data( aData ) {}
240
241 size_t Size() const { return m_data.size(); }
242
243 // Overflow-safe: a near-SIZE_MAX offset must not wrap aOffset + aCount into range.
244 bool InBounds( size_t aOffset, size_t aCount ) const
245 {
246 return aOffset <= m_data.size() && aCount <= m_data.size() - aOffset;
247 }
248
249 uint8_t U8At( size_t aOffset ) const
250 {
251 check( aOffset, 1 );
252 return getU8( m_data, aOffset );
253 }
254
255 uint16_t U16At( size_t aOffset ) const
256 {
257 check( aOffset, 2 );
258 return getU16LE( m_data, aOffset );
259 }
260
261 uint32_t U32At( size_t aOffset ) const
262 {
263 check( aOffset, 4 );
264 return getU32LE( m_data, aOffset );
265 }
266
267 int32_t I32At( size_t aOffset ) const { return static_cast<int32_t>( U32At( aOffset ) ); }
268
269 double F64At( size_t aOffset ) const
270 {
271 check( aOffset, 8 );
272 return getF64LE( m_data, aOffset );
273 }
274
275 float F32At( size_t aOffset ) const
276 {
277 check( aOffset, 4 );
278 return getF32LE( m_data, aOffset );
279 }
280
281 // Unlike the numeric accessors this does not throw out of range; aMaxLen is
282 // clamped to the available bytes and the read returns empty past EOF, so a
283 // short field abutting a boundary still reads cleanly.
284 std::string StringAt( size_t aOffset, size_t aMaxLen ) const
285 {
286 return readFixedString( m_data, aOffset, aMaxLen );
287 }
288
289private:
290 void check( size_t aOffset, size_t aCount ) const
291 {
292 if( !InBounds( aOffset, aCount ) )
293 {
294 THROW_IO_ERROR( wxString::Format( "PADS binary read out of bounds at offset %zu (file size %zu)",
295 aOffset, m_data.size() ) );
296 }
297 }
298
299 const std::vector<uint8_t>& m_data;
300};
301
302
313{
314public:
315 SDB_RECORD( const BINARY_CURSOR& aCursor, size_t aBase ) : m_cursor( aCursor ), m_base( aBase )
316 {
317 }
318
319 uint8_t U8( size_t aOffset ) const { return m_cursor.U8At( m_base + aOffset ); }
320 uint16_t U16( size_t aOffset ) const { return m_cursor.U16At( m_base + aOffset ); }
321 uint32_t U32( size_t aOffset ) const { return m_cursor.U32At( m_base + aOffset ); }
322 int32_t I32( size_t aOffset ) const { return m_cursor.I32At( m_base + aOffset ); }
323 double F64( size_t aOffset ) const { return m_cursor.F64At( m_base + aOffset ); }
324 std::string Str( size_t aOffset, size_t aMaxLen ) const
325 {
326 return m_cursor.StringAt( m_base + aOffset, aMaxLen );
327 }
328
329 size_t Base() const { return m_base; }
330
331private:
333 size_t m_base;
334};
335
336} // namespace PADS_IO
337
338#endif // PADS_BINARY_UTILS_H_
Bounds-checked little-endian read cursor over a PADS binary buffer.
std::string StringAt(size_t aOffset, size_t aMaxLen) const
int32_t I32At(size_t aOffset) const
float F32At(size_t aOffset) const
BINARY_CURSOR(const std::vector< uint8_t > &aData)
uint8_t U8At(size_t aOffset) const
const std::vector< uint8_t > & m_data
double F64At(size_t aOffset) const
uint16_t U16At(size_t aOffset) const
bool InBounds(size_t aOffset, size_t aCount) const
void check(size_t aOffset, size_t aCount) const
uint32_t U32At(size_t aOffset) const
uint32_t U32(size_t aOffset) const
uint8_t U8(size_t aOffset) const
uint16_t U16(size_t aOffset) const
const BINARY_CURSOR & m_cursor
int32_t I32(size_t aOffset) const
double F64(size_t aOffset) const
SDB_RECORD(const BINARY_CURSOR &aCursor, size_t aBase)
std::string Str(size_t aOffset, size_t aMaxLen) const
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
bool ReadFileHeader(const wxString &aFileName, std::vector< uint8_t > &aOut, size_t aCount)
Read at most aCount leading bytes of aFileName into aOut, which is sized to what was actually read.
bool HasSdbMagic(const std::vector< uint8_t > &aData, uint8_t aMagic1)
Check the PADS SDB container magic, a leading 0x00 followed by a format-specific second byte.
constexpr uint16_t SDB_RECORD_SENTINEL
constexpr double SDB_BASIC_PER_MIL
uint8_t getU8(const std::vector< uint8_t > &aData, size_t aOffset)
Little-endian byte assembly with no bounds checking.
std::optional< SDB_FOOTER_ERROR > CheckSdbFooter(const std::vector< uint8_t > &aData, size_t aFooterStart, const char *aGuid, size_t aGuidLen)
Check a PADS SDB footer at aFooterStart: the ASCII GUID followed by an absolute back-pointer to the s...
float getF32LE(const std::vector< uint8_t > &aData, size_t aOffset)
uint32_t getU32LE(const std::vector< uint8_t > &aData, size_t aOffset)
bool ReadFileToBuffer(const wxString &aFileName, std::vector< uint8_t > &aOut)
Read an entire file into aOut.
void ValidateSdbFooter(const std::vector< uint8_t > &aData, size_t aFooterStart, const char *aGuid, size_t aGuidLen)
As CheckSdbFooter, throwing a bare IO_ERROR on failure.
uint16_t getU16LE(const std::vector< uint8_t > &aData, size_t aOffset)
std::string readFixedStringRaw(const std::vector< uint8_t > &aData, size_t aOffset, size_t aMaxLen)
Read a NUL-terminated fixed-width field starting at aOffset, scanning at most aMaxLen bytes,...
std::string readFixedString(const std::vector< uint8_t > &aData, size_t aOffset, size_t aMaxLen)
As readFixedStringRaw, then trim trailing spaces.
double getF64LE(const std::vector< uint8_t > &aData, size_t aOffset)