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