KiCad PCB EDA Suite
Loading...
Searching...
No Matches
altium_binary_parser.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 (C) 2019-2020 Thomas Pointhuber <[email protected]>
5 * Copyright (C) 2020-2023 KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, you may find one here:
19 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20 * or you may search the http://www.gnu.org website for the version 2 license,
21 * or you may write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24
25#ifndef _ALTIUM_BINARY_PARSER_H
26#define _ALTIUM_BINARY_PARSER_H
27
28#include "altium_props_utils.h"
29
30#include <charconv>
31#include <map>
32#include <memory>
33#include <numeric>
34#include <string>
35#include <stdexcept>
36#include <vector>
37
38#include <wx/mstream.h>
39#include <wx/zstream.h>
40#include <math/vector2d.h>
41#include <math/vector3.h>
42
43namespace CFB
44{
45class CompoundFileReader;
46struct COMPOUND_FILE_ENTRY;
47} // namespace CFB
48
54std::string FormatPath( const std::vector<std::string>& aVectorPath );
55
56
58{
59public:
60 const CFB::COMPOUND_FILE_ENTRY* m_symbol;
61 const CFB::COMPOUND_FILE_ENTRY* m_pinsFrac;
62 const CFB::COMPOUND_FILE_ENTRY* m_pinsWideText;
63 const CFB::COMPOUND_FILE_ENTRY* m_pinsTextData;
64 const CFB::COMPOUND_FILE_ENTRY* m_pinsSymbolLineWidth;
65};
66
67
69{
71
72public:
78 ALTIUM_COMPOUND_FILE( const wxString& aFilePath );
79
87 ALTIUM_COMPOUND_FILE( const void* aBuffer, size_t aLen );
88
89 ALTIUM_COMPOUND_FILE( const ALTIUM_COMPOUND_FILE& temp_obj ) = delete;
92
93 const CFB::CompoundFileReader& GetCompoundFileReader() const { return *m_reader; }
94
95 std::unique_ptr<ALTIUM_COMPOUND_FILE> DecodeIntLibStream( const CFB::COMPOUND_FILE_ENTRY& cfe );
96
97 const CFB::COMPOUND_FILE_ENTRY* FindStream( const std::vector<std::string>& aStreamPath ) const;
98
99 const CFB::COMPOUND_FILE_ENTRY* FindStream( const CFB::COMPOUND_FILE_ENTRY* aStart, const std::vector<std::string>& aStreamPath ) const;
100
101 const CFB::COMPOUND_FILE_ENTRY* FindStreamSingleLevel( const CFB::COMPOUND_FILE_ENTRY* aEntry,
102 const std::string aName,
103 const bool aIsStream ) const;
104
105 std::map<wxString, const CFB::COMPOUND_FILE_ENTRY*> EnumDir( const std::wstring& aDir ) const;
106
107 std::map<wxString, ALTIUM_SYMBOL_DATA> GetLibSymbols( const CFB::COMPOUND_FILE_ENTRY* aStart ) const;
108
109private:
110
111 std::unique_ptr<CFB::CompoundFileReader> m_reader;
112 std::vector<char> m_buffer;
113};
114
115
117{
118public:
119 ALTIUM_BINARY_PARSER( const ALTIUM_COMPOUND_FILE& aFile, const CFB::COMPOUND_FILE_ENTRY* aEntry );
120 ALTIUM_BINARY_PARSER( std::unique_ptr<char[]>& aContent, size_t aSize );
122
123 template <typename Type>
124 Type Read()
125 {
126 const size_t remainingBytes = GetRemainingBytes();
127 if( remainingBytes >= sizeof( Type ) )
128 {
129 Type val = *(Type*) ( m_pos );
130 m_pos += sizeof( Type );
131 return val;
132 }
133 else
134 {
135 m_pos += remainingBytes; // Ensure remaining bytes are zero
136 m_error = true;
137 return 0;
138 }
139 }
140
141 template <typename Type>
142 Type Peek()
143 {
144 char* const oldPos = m_pos;
145 const bool oldError = m_error;
146 Type result = Read<Type>();
147 m_pos = oldPos;
148 m_error = oldError;
149 return result;
150 }
151
152 wxScopedCharBuffer ReadCharBuffer()
153 {
154 uint8_t len = Read<uint8_t>();
155
156 if( GetRemainingBytes() >= len )
157 {
158 char* buf = static_cast<char*>( malloc( len ) );
159 memcpy( buf, m_pos, len );
160 m_pos += len;
161
162 return wxScopedCharBuffer::CreateOwned( buf, len );
163 }
164 else
165 {
166 m_error = true;
167 return wxScopedCharBuffer();
168 }
169 }
170
171 wxString ReadWxString()
172 {
173 // TODO: Identify where the actual code page is stored. For now, this default code page
174 // has limited impact, because recent Altium files come with a UTF16 string table
175 return wxString( ReadCharBuffer(), wxConvISO8859_1 );
176 }
177
178 std::map<uint32_t, wxString> ReadWideStringTable()
179 {
180 std::map<uint32_t, wxString> table;
181 size_t remaining = GetRemainingBytes();
182
183 while( remaining >= 8 )
184 {
185 uint32_t index = Read<uint32_t>();
186 uint32_t length = Read<uint32_t>();
187 wxString str;
188 remaining -= 8;
189
190 if( length <= 2 )
191 length = 0; // for empty strings, not even the null bytes are present
192 else
193 {
194 if( length > remaining )
195 break;
196
197 str = wxString( m_pos, wxMBConvUTF16LE(), length - 2 );
198 }
199
200 table.emplace( index, str );
201 m_pos += length;
202 remaining -= length;
203 }
204
205 return table;
206 }
207
208 std::vector<char> ReadVector( size_t aSize )
209 {
210 if( aSize > GetRemainingBytes() )
211 {
212 m_error = true;
213 return {};
214 }
215 else
216 {
217 std::vector<char> data( m_pos, m_pos + aSize );
218 m_pos += aSize;
219 return data;
220 }
221 }
222
223 int ReadBytes( char* aOut, size_t aSize )
224 {
225 if( aSize > GetRemainingBytes() )
226 {
227 m_error = true;
228 return 0;
229 }
230 else
231 {
232 memcpy( aOut, m_pos, aSize );
233 m_pos += aSize;
234 return aSize;
235 }
236 }
237
239 {
240 return ALTIUM_PROPS_UTILS::ConvertToKicadUnit( Read<int32_t>() );
241 }
242
244 {
245 return ReadKicadUnit();
246 }
247
249 {
250 return -ReadKicadUnit();
251 }
252
254 {
255 int32_t x = ReadKicadUnitX();
256 int32_t y = ReadKicadUnitY();
257 return { x, y };
258 }
259
261 {
262 int32_t x = ReadKicadUnit();
263 int32_t y = ReadKicadUnit();
264 return { x, y };
265 }
266
268 {
269 uint32_t length = Read<uint32_t>();
270 m_subrecord_end = m_pos + length;
271 return length;
272 }
273
274 std::map<wxString, wxString> ReadProperties(
275 std::function<std::map<wxString, wxString>( const std::string& )> handleBinaryData =
276 []( const std::string& )
277 {
278 return std::map<wxString, wxString>();
279 } );
280
281 void Skip( size_t aLength )
282 {
283 if( GetRemainingBytes() >= aLength )
284 {
285 m_pos += aLength;
286 }
287 else
288 {
289 m_error = true;
290 }
291 }
292
294 {
295 if( m_subrecord_end == nullptr || m_subrecord_end < m_pos )
296 {
297 m_error = true;
298 }
299 else
300 {
302 }
303 };
304
305 size_t GetRemainingBytes() const
306 {
307 return m_pos == nullptr ? 0 : m_size - ( m_pos - m_content.get() );
308 }
309
311 {
312 return m_pos == nullptr || m_subrecord_end == nullptr || m_subrecord_end <= m_pos ?
313 0 :
315 };
316
318 {
319 return m_error;
320 }
321
322private:
323 std::unique_ptr<char[]> m_content;
324 size_t m_size;
325
326 char* m_pos; // current read pointer
327 char* m_subrecord_end; // pointer which points to next subrecord start
329};
330
331
333{
334public:
335 ALTIUM_BINARY_READER( const std::string& binaryData ) : m_data( binaryData ), m_position( 0 ) {}
336
337 int32_t ReadInt32()
338 {
339 if( m_position + sizeof( int32_t ) > m_data.size() )
340 throw std::out_of_range( "ALTIUM_BINARY_READER: out of range" );
341
342 int32_t value = *reinterpret_cast<const int32_t*>( &m_data[m_position] );
343 m_position += sizeof( int32_t );
344 return value;
345 }
346
347 int16_t ReadInt16()
348 {
349 if( m_position + sizeof( int16_t ) > m_data.size() )
350 throw std::out_of_range( "ALTIUM_BINARY_READER: out of range" );
351
352 int16_t value = *reinterpret_cast<const int16_t*>( &m_data[m_position] );
353 m_position += sizeof( int16_t );
354 return value;
355 }
356
357 uint8_t ReadByte()
358 {
359 if( m_position + sizeof( uint8_t ) > m_data.size() )
360 throw std::out_of_range( "ALTIUM_BINARY_READER: out of range" );
361
362 uint8_t value = *reinterpret_cast<const uint8_t*>( &m_data[m_position] );
363 m_position += sizeof( uint8_t );
364 return value;
365 }
366
368 {
369 uint8_t length = ReadByte();
370
371 if( m_position + length > m_data.size() )
372 throw std::out_of_range( "ALTIUM_BINARY_READER: out of range" );
373
374 std::string pascalString( &m_data[m_position], &m_data[m_position + length] );
375 m_position += length;
376 return pascalString;
377 }
378
380 {
381 uint32_t length = ReadInt32();
382
383 if( m_position + length > m_data.size() )
384 throw std::out_of_range( "ALTIUM_BINARY_READER: out of range" );
385
386 std::string pascalString( &m_data[m_position], &m_data[m_position + length] );
387 m_position += length;
388 return pascalString;
389 }
390
391private:
392 const std::string& m_data;
394};
395
397{
398public:
399 ALTIUM_COMPRESSED_READER( const std::string& aData ) : ALTIUM_BINARY_READER( aData )
400 {}
401
402 std::pair<int, std::string*> ReadCompressedString()
403 {
404 std::string* result;
405 int id = -1;
406
407 uint8_t byte = ReadByte();
408 if( byte != 0xD0 )
409 throw std::runtime_error( "ALTIUM_COMPRESSED_READER: invalid compressed string" );
410
411 std::string str = ReadShortPascalString();
412 std::from_chars( str.data(), str.data() + str.size(), id );
413
414 std::string data = ReadFullPascalString();
415 result = decompressData( data );
416
417 return std::make_pair( id, result );
418 }
419
420private:
421 std::string decompressedData;
422
423 std::string* decompressData( std::string& aData )
424 {
425 // Create a memory input stream with the buffer
426 wxMemoryInputStream memStream( (void*) aData.data(), aData.length() );
427
428 // Create a zlib input stream with the memory input stream
429 wxZlibInputStream zStream( memStream );
430
431 // Read decompressed data from the zlib input stream
432 while( !zStream.Eof() )
433 {
434 char buffer[1024];
435 zStream.Read( buffer, sizeof( buffer ) );
436 size_t bytesRead = zStream.LastRead();
437 decompressedData.append( buffer, bytesRead );
438 }
439
440 return &decompressedData;
441 }
442};
443
444#endif //_ALTIUM_BINARY_PARSER_H
std::string FormatPath(const std::vector< std::string > &aVectorPath)
Helper for debug logging (vector -> string)
size_t GetRemainingBytes() const
~ALTIUM_BINARY_PARSER()=default
void Skip(size_t aLength)
std::vector< char > ReadVector(size_t aSize)
std::unique_ptr< char[]> m_content
wxScopedCharBuffer ReadCharBuffer()
std::map< uint32_t, wxString > ReadWideStringTable()
size_t GetRemainingSubrecordBytes() const
std::map< wxString, wxString > ReadProperties(std::function< std::map< wxString, wxString >(const std::string &)> handleBinaryData=[](const std::string &) { return std::map< wxString, wxString >();})
int ReadBytes(char *aOut, size_t aSize)
std::string ReadShortPascalString()
std::string ReadFullPascalString()
const std::string & m_data
ALTIUM_BINARY_READER(const std::string &binaryData)
const CFB::CompoundFileReader & GetCompoundFileReader() const
std::unique_ptr< ALTIUM_COMPOUND_FILE > DecodeIntLibStream(const CFB::COMPOUND_FILE_ENTRY &cfe)
const CFB::COMPOUND_FILE_ENTRY * FindStreamSingleLevel(const CFB::COMPOUND_FILE_ENTRY *aEntry, const std::string aName, const bool aIsStream) const
~ALTIUM_COMPOUND_FILE()=default
std::map< wxString, const CFB::COMPOUND_FILE_ENTRY * > EnumDir(const std::wstring &aDir) const
ALTIUM_COMPOUND_FILE & operator=(const ALTIUM_COMPOUND_FILE &temp_obj)=delete
std::vector< char > m_buffer
std::unique_ptr< CFB::CompoundFileReader > m_reader
std::map< wxString, ALTIUM_SYMBOL_DATA > GetLibSymbols(const CFB::COMPOUND_FILE_ENTRY *aStart) const
const CFB::COMPOUND_FILE_ENTRY * FindStream(const std::vector< std::string > &aStreamPath) const
ALTIUM_COMPOUND_FILE(const ALTIUM_COMPOUND_FILE &temp_obj)=delete
std::string * decompressData(std::string &aData)
ALTIUM_COMPRESSED_READER(const std::string &aData)
std::pair< int, std::string * > ReadCompressedString()
static int32_t ConvertToKicadUnit(const double aValue)
const CFB::COMPOUND_FILE_ENTRY * m_pinsSymbolLineWidth
const CFB::COMPOUND_FILE_ENTRY * m_symbol
const CFB::COMPOUND_FILE_ENTRY * m_pinsFrac
const CFB::COMPOUND_FILE_ENTRY * m_pinsWideText
const CFB::COMPOUND_FILE_ENTRY * m_pinsTextData