KiCad PCB EDA Suite
Loading...
Searching...
No Matches
altium_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_PARSER_H
26#define ALTIUM_PARSER_H
27
28#include <map>
29#include <memory>
30#include <numeric>
31
32#include <wx/mstream.h>
33#include <wx/zstream.h>
34#include <math/vector2d.h>
35#include <vector>
36
37#include <string>
38#include <stdexcept>
39
40namespace CFB
41{
42class CompoundFileReader;
43struct COMPOUND_FILE_ENTRY;
44} // namespace CFB
45
51std::string FormatPath( const std::vector<std::string>& aVectorPath );
52
53
55{
56public:
62 ALTIUM_COMPOUND_FILE( const wxString& aFilePath );
63
71 ALTIUM_COMPOUND_FILE( const void* aBuffer, size_t aLen );
72
73 ALTIUM_COMPOUND_FILE( const ALTIUM_COMPOUND_FILE& temp_obj ) = delete;
76
77 const CFB::CompoundFileReader& GetCompoundFileReader() const { return *m_reader; }
78
79 std::unique_ptr<ALTIUM_COMPOUND_FILE> DecodeIntLibStream( const CFB::COMPOUND_FILE_ENTRY& cfe );
80
81 std::map<wxString, wxString> ListLibFootprints();
82
83 std::tuple<wxString, const CFB::COMPOUND_FILE_ENTRY*> FindLibFootprintDirName( const wxString& aFpUnicodeName );
84
85 const CFB::COMPOUND_FILE_ENTRY* FindStream( const std::vector<std::string>& aStreamPath ) const;
86
87 const CFB::COMPOUND_FILE_ENTRY* FindStream( const CFB::COMPOUND_FILE_ENTRY* aStart, const std::vector<std::string>& aStreamPath ) const;
88
89 const CFB::COMPOUND_FILE_ENTRY* FindStreamSingleLevel( const CFB::COMPOUND_FILE_ENTRY* aEntry,
90 const std::string aName,
91 const bool aIsStream ) const;
92
93 std::map<wxString, const CFB::COMPOUND_FILE_ENTRY*> EnumDir( const std::wstring& aDir ) const;
94
95 std::map<wxString, const CFB::COMPOUND_FILE_ENTRY*> GetLibSymbols( const CFB::COMPOUND_FILE_ENTRY* aStart ) const;
96
97private:
98
100
101 std::unique_ptr<CFB::CompoundFileReader> m_reader;
102 std::vector<char> m_buffer;
103
104 std::map<wxString, const CFB::COMPOUND_FILE_ENTRY*> m_libFootprintNameCache;
105 std::map<wxString, wxString> m_libFootprintDirNameCache;
106};
107
108
110{
111public:
112 ALTIUM_PARSER( const ALTIUM_COMPOUND_FILE& aFile, const CFB::COMPOUND_FILE_ENTRY* aEntry );
113 ALTIUM_PARSER( std::unique_ptr<char[]>& aContent, size_t aSize );
114 ~ALTIUM_PARSER() = default;
115
116 template <typename Type>
117 Type Read()
118 {
119 const size_t remainingBytes = GetRemainingBytes();
120 if( remainingBytes >= sizeof( Type ) )
121 {
122 Type val = *(Type*) ( m_pos );
123 m_pos += sizeof( Type );
124 return val;
125 }
126 else
127 {
128 m_pos += remainingBytes; // Ensure remaining bytes are zero
129 m_error = true;
130 return 0;
131 }
132 }
133
134 template <typename Type>
135 Type Peek()
136 {
137 char* const oldPos = m_pos;
138 const bool oldError = m_error;
139 Type result = Read<Type>();
140 m_pos = oldPos;
141 m_error = oldError;
142 return result;
143 }
144
145 wxScopedCharBuffer ReadCharBuffer()
146 {
147 uint8_t len = Read<uint8_t>();
148 if( GetRemainingBytes() >= len )
149 {
150 char* buf = new char[len];
151 memcpy( buf, m_pos, len );
152 m_pos += len;
153
154 return wxScopedCharBuffer::CreateOwned( buf, len );
155 }
156 else
157 {
158 m_error = true;
159 return wxScopedCharBuffer();
160 }
161 }
162
163 wxString ReadWxString()
164 {
165 // TODO: Identify where the actual code page is stored. For now, this default code page
166 // has limited impact, because recent Altium files come with a UTF16 string table
167 return wxString( ReadCharBuffer(), wxConvISO8859_1 );
168 }
169
170 std::map<uint32_t, wxString> ReadWideStringTable()
171 {
172 std::map<uint32_t, wxString> table;
173 size_t remaining = GetRemainingBytes();
174
175 while( remaining >= 8 )
176 {
177 uint32_t index = Read<uint32_t>();
178 uint32_t length = Read<uint32_t>();
179 wxString str;
180 remaining -= 8;
181
182 if( length <= 2 )
183 length = 0; // for empty strings, not even the null bytes are present
184 else
185 {
186 if( length > remaining )
187 break;
188
189 str = wxString( m_pos, wxMBConvUTF16LE(), length - 2 );
190 }
191
192 table.emplace( index, str );
193 m_pos += length;
194 remaining -= length;
195 }
196
197 return table;
198 }
199
200 std::vector<char> ReadVector( size_t aSize )
201 {
202 if( aSize > GetRemainingBytes() )
203 {
204 m_error = true;
205 return {};
206 }
207 else
208 {
209 std::vector<char> data( m_pos, m_pos + aSize );
210 m_pos += aSize;
211 return data;
212 }
213 }
214
215 int ReadBytes( char* aOut, size_t aSize )
216 {
217 if( aSize > GetRemainingBytes() )
218 {
219 m_error = true;
220 return 0;
221 }
222 else
223 {
224 memcpy( aOut, m_pos, aSize );
225 m_pos += aSize;
226 return aSize;
227 }
228 }
229
231 {
232 return ConvertToKicadUnit( Read<int32_t>() );
233 }
234
236 {
237 return ReadKicadUnit();
238 }
239
241 {
242 return -ReadKicadUnit();
243 }
244
246 {
247 int32_t x = ReadKicadUnitX();
248 int32_t y = ReadKicadUnitY();
249 return { x, y };
250 }
251
253 {
254 int32_t x = ReadKicadUnit();
255 int32_t y = ReadKicadUnit();
256 return { x, y };
257 }
258
260 {
261 uint32_t length = Read<uint32_t>();
262 m_subrecord_end = m_pos + length;
263 return length;
264 }
265
266 std::map<wxString, wxString> ReadProperties(
267 std::function<std::map<wxString, wxString>( const std::string& )> handleBinaryData =
268 []( const std::string& )
269 {
270 return std::map<wxString, wxString>();
271 } );
272
273 static int32_t ConvertToKicadUnit( const double aValue );
274
275 static int ReadInt( const std::map<wxString, wxString>& aProps,
276 const wxString& aKey, int aDefault );
277
278 static double ReadDouble( const std::map<wxString, wxString>& aProps,
279 const wxString& aKey, double aDefault );
280
281 static bool ReadBool( const std::map<wxString, wxString>& aProps,
282 const wxString& aKey, bool aDefault );
283
284 static int32_t ReadKicadUnit( const std::map<wxString, wxString>& aProps,
285 const wxString& aKey, const wxString& aDefault );
286
287 static wxString ReadString( const std::map<wxString, wxString>& aProps,
288 const wxString& aKey, const wxString& aDefault );
289
290 static wxString ReadUnicodeString( const std::map<wxString, wxString>& aProps,
291 const wxString& aKey, const wxString& aDefault );
292
293 void Skip( size_t aLength )
294 {
295 if( GetRemainingBytes() >= aLength )
296 {
297 m_pos += aLength;
298 }
299 else
300 {
301 m_error = true;
302 }
303 }
304
306 {
307 if( m_subrecord_end == nullptr || m_subrecord_end < m_pos )
308 {
309 m_error = true;
310 }
311 else
312 {
314 }
315 };
316
317 size_t GetRemainingBytes() const
318 {
319 return m_pos == nullptr ? 0 : m_size - ( m_pos - m_content.get() );
320 }
321
323 {
324 return m_pos == nullptr || m_subrecord_end == nullptr || m_subrecord_end <= m_pos ?
325 0 :
327 };
328
330 {
331 return m_error;
332 }
333
334private:
335 std::unique_ptr<char[]> m_content;
336 size_t m_size;
337
338 char* m_pos; // current read pointer
339 char* m_subrecord_end; // pointer which points to next subrecord start
341};
342
343
345{
346public:
347 ALTIUM_BINARY_READER( const std::string& binaryData ) : m_data( binaryData ), m_position( 0 ) {}
348
349 int32_t ReadInt32()
350 {
351 if( m_position + sizeof( int32_t ) > m_data.size() )
352 throw std::out_of_range( "ALTIUM_BINARY_READER: out of range" );
353
354 int32_t value = *reinterpret_cast<const int32_t*>( &m_data[m_position] );
355 m_position += sizeof( int32_t );
356 return value;
357 }
358
359 int16_t ReadInt16()
360 {
361 if( m_position + sizeof( int16_t ) > m_data.size() )
362 throw std::out_of_range( "ALTIUM_BINARY_READER: out of range" );
363
364 int16_t value = *reinterpret_cast<const int16_t*>( &m_data[m_position] );
365 m_position += sizeof( int16_t );
366 return value;
367 }
368
369 uint8_t ReadByte()
370 {
371 if( m_position + sizeof( uint8_t ) > m_data.size() )
372 throw std::out_of_range( "ALTIUM_BINARY_READER: out of range" );
373
374 uint8_t value = *reinterpret_cast<const uint8_t*>( &m_data[m_position] );
375 m_position += sizeof( uint8_t );
376 return value;
377 }
378
379 std::string ReadPascalString()
380 {
381 uint8_t length = ReadByte();
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 while( true )
408 {
409 uint8_t byte = ReadByte();
410 if( byte != 0xD0 )
411 throw std::runtime_error( "ALTIUM_COMPRESSED_READER: invalid compressed string" );
412
413 std::string str = ReadPascalString();
414
415 id = std::stoi( str );
416
417 std::string data = ReadPascalString();
418
419 result = decompressData( data );
420 }
421
422 return std::make_pair( id, result );
423 }
424
425private:
426 std::string decompressData( std::string& aData )
427 {
428 // Create a memory input stream with the buffer
429 wxMemoryInputStream memStream( (void*) aData.data(), aData.length() );
430
431 // Create a zlib input stream with the memory input stream
432 wxZlibInputStream zStream( memStream );
433
434 // Prepare a string to hold decompressed data
435 std::string decompressedData;
436
437 // Read decompressed data from the zlib input stream
438 while( !zStream.Eof() )
439 {
440 char buffer[1024];
441 zStream.Read( buffer, sizeof( buffer ) );
442 size_t bytesRead = zStream.LastRead();
443 decompressedData.append( buffer, bytesRead );
444 }
445
446 return decompressedData;
447 }
448};
449
450#endif //ALTIUM_PARSER_H
std::string FormatPath(const std::vector< std::string > &aVectorPath)
Helper for debug logging (vector -> string)
const std::string & m_data
ALTIUM_BINARY_READER(const std::string &binaryData)
std::string ReadPascalString()
std::map< wxString, wxString > ListLibFootprints()
const CFB::CompoundFileReader & GetCompoundFileReader() const
Definition: altium_parser.h:77
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, const CFB::COMPOUND_FILE_ENTRY * > GetLibSymbols(const CFB::COMPOUND_FILE_ENTRY *aStart) const
std::map< wxString, const CFB::COMPOUND_FILE_ENTRY * > m_libFootprintNameCache
std::tuple< wxString, const CFB::COMPOUND_FILE_ENTRY * > FindLibFootprintDirName(const wxString &aFpUnicodeName)
const CFB::COMPOUND_FILE_ENTRY * FindStream(const std::vector< std::string > &aStreamPath) const
std::map< wxString, wxString > m_libFootprintDirNameCache
ALTIUM_COMPOUND_FILE(const ALTIUM_COMPOUND_FILE &temp_obj)=delete
std::pair< int, std::string > ReadCompressedString()
ALTIUM_COMPRESSED_READER(const std::string &aData)
std::string decompressData(std::string &aData)
wxScopedCharBuffer ReadCharBuffer()
wxString ReadWxString()
static int ReadInt(const std::map< wxString, wxString > &aProps, const wxString &aKey, int aDefault)
size_t GetRemainingBytes() const
int32_t ReadKicadUnitX()
bool HasParsingError()
size_t GetRemainingSubrecordBytes() const
static wxString ReadString(const std::map< wxString, wxString > &aProps, const wxString &aKey, const wxString &aDefault)
std::map< uint32_t, wxString > ReadWideStringTable()
std::map< wxString, wxString > ReadProperties(std::function< std::map< wxString, wxString >(const std::string &)> handleBinaryData=[](const std::string &) { return std::map< wxString, wxString >();})
std::unique_ptr< char[]> m_content
~ALTIUM_PARSER()=default
size_t ReadAndSetSubrecordLength()
int32_t ReadKicadUnitY()
std::vector< char > ReadVector(size_t aSize)
int ReadBytes(char *aOut, size_t aSize)
static double ReadDouble(const std::map< wxString, wxString > &aProps, const wxString &aKey, double aDefault)
VECTOR2I ReadVector2ISize()
static int32_t ConvertToKicadUnit(const double aValue)
int32_t ReadKicadUnit()
static bool ReadBool(const std::map< wxString, wxString > &aProps, const wxString &aKey, bool aDefault)
VECTOR2I ReadVector2IPos()
void Skip(size_t aLength)
char * m_subrecord_end
static wxString ReadUnicodeString(const std::map< wxString, wxString > &aProps, const wxString &aKey, const wxString &aDefault)
void SkipSubrecord()