KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pads_sdb.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 * 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 2
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/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 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#include "pads_sdb.h"
25
26#include <ki_exception.h>
27
28namespace PADS_IO
29{
30
31// The fixed GUID PADS writes into every binary footer; its presence is the
32// strongest single check that a file is a PADS binary database.
33static const char FOOTER_GUID[] = "{2FE18320-6448-11d1-A412-000000000000}";
34static constexpr size_t FOOTER_GUID_LEN = 38;
35
36
37bool PADS_SDB::HasMagic( const std::vector<uint8_t>& aBytes )
38{
39 return HasSdbMagic( aBytes, MAGIC1 );
40}
41
42
43bool PADS_SDB::IsSupportedVersion( uint16_t aVersion )
44{
45 switch( aVersion )
46 {
47 case 0x2017:
48 case 0x2019:
49 case 0x2021:
50 case 0x2022:
51 case 0x2024:
52 case 0x2025:
53 case 0x2026:
54 case 0x2027: return true;
55 default: return false;
56 }
57}
58
59
60void PADS_SDB::Load( std::vector<uint8_t> aBytes )
61{
62 m_data = std::move( aBytes );
63
69}
70
71
73{
74 return static_cast<int>( m_cursor.U32At( HEADER_SIZE + DIR_ENTRY_SIZE ) );
75}
76
77
79{
80 if( m_data.size() < static_cast<size_t>( HEADER_SIZE ) + FOOTER_SIZE )
81 THROW_IO_ERROR( "File too small for PADS binary format" );
82
83 if( !HasMagic( m_data ) )
84 THROW_IO_ERROR( "Invalid PADS binary magic bytes" );
85
86 m_version = m_cursor.U16At( 2 );
87
89 THROW_IO_ERROR( wxString::Format( "Unsupported PADS binary version 0x%04X", m_version ) );
90}
91
92
97
98
100{
101 int entryCount = directoryEntryCount();
102 size_t dirSize = static_cast<size_t>( entryCount ) * DIR_ENTRY_SIZE;
103
104 if( HEADER_SIZE + dirSize > m_data.size() )
105 THROW_IO_ERROR( "File too small for the PADS section directory" );
106
107 m_sections.clear();
108 m_sections.reserve( entryCount );
109
110 for( int i = 0; i < entryCount; ++i )
111 {
112 size_t entryOffset = HEADER_SIZE + static_cast<size_t>( i ) * DIR_ENTRY_SIZE;
113
114 SDB_SECTION section;
115 section.index = i;
116 section.count = m_cursor.U32At( entryOffset );
117 section.totalBytes = m_cursor.U32At( entryOffset + 4 );
118
119 if( section.count > 0 && section.totalBytes > 0 )
120 section.stride = section.totalBytes / section.count;
121
122 m_sections.push_back( section );
123 }
124}
125
126
128{
129 static constexpr int PAGE_DESCRIPTOR_BYTES = 12;
130 static constexpr int PAGE_TABLE_TAGS[] = { 65, 66, 45, 46, 47, 48, 41, 42, 74 };
131 static constexpr int PAGED_READ_ORDER[] = { 41, 42, 45, 46, 47, 48 };
132
133 auto recordStride = [this]( int aTag ) -> uint32_t
134 {
135 switch( aTag )
136 {
137 case 41: return m_version == 0x2017 ? 180 : 188;
138 case 42: return 80;
139 case 45: return m_version == 0x2017 ? 116 : 124;
140 case 46: return m_version <= 0x2019 ? 32 : 40;
141 case 47: return 24;
142 case 48:
143 if( m_version <= 0x2019 )
144 return 48;
145
146 return m_version <= 0x2022 ? 856 : 864;
147
148 case 65: return 28;
149 case 66: return m_version <= 0x2022 ? 28 : 280;
150 case 74: return 276;
151
152 default: return 0;
153 }
154 };
155
156 uint64_t cursor = 6 + static_cast<uint64_t>( m_sections.size() ) * DIR_ENTRY_SIZE;
157
158 for( int tag = 2; tag <= 25; ++tag )
159 {
160 SDB_SECTION& section = m_sections.at( tag );
161
162 uint32_t fixedStride = 0;
163
164 if( tag == 10 )
165 fixedStride = m_version <= 0x2022 ? 100 : 112;
166 else if( tag == 11 )
167 fixedStride = m_version <= 0x2024 ? 16 : 20;
168 else if( tag == 12 )
169 fixedStride = 12;
170 else if( tag == 24 )
171 fixedStride = 68;
172
173 if( fixedStride != 0 && static_cast<uint64_t>( section.count ) * fixedStride != section.totalBytes )
174 THROW_IO_ERROR( "Invalid PADS flat-controller extent" );
175
176 if( tag == 15 )
177 {
178 const uint32_t stride = m_version <= 0x2019 ? 20 : 36;
179
180 if( static_cast<uint64_t>( section.count ) * stride != section.totalBytes )
181 THROW_IO_ERROR( "Invalid PADS terminal-controller extent" );
182 }
183
184 section.physicalOffset = static_cast<uint32_t>( cursor );
185 section.physicalBytes = section.totalBytes;
186 section.physicalCount = section.count;
187 section.physicalLiveCount = section.count;
188 cursor += section.totalBytes;
189 }
190
191 for( int tag : { 26, 27 } )
192 {
193 SDB_SECTION& section = m_sections.at( tag );
194 section.physicalOffset = static_cast<uint32_t>( cursor );
195 section.physicalBytes = section.totalBytes;
196 section.physicalCount = section.count;
197 section.physicalLiveCount = section.count;
198 cursor += section.totalBytes;
199 }
200
201 SDB_SECTION& pageDirectory = m_sections.at( 26 );
202 uint64_t numPageDescriptors = 0;
203
204 for( int tag : PAGE_TABLE_TAGS )
205 {
206 if( tag < static_cast<int>( m_sections.size() ) )
207 numPageDescriptors += m_sections[tag].totalBytes;
208 }
209
210 if( pageDirectory.totalBytes != static_cast<uint64_t>( pageDirectory.count ) * PAGE_DESCRIPTOR_BYTES
211 || numPageDescriptors > pageDirectory.count )
212 {
213 THROW_IO_ERROR( "Invalid PADS page-controller directory" );
214 }
215
216 uint64_t descriptor =
217 pageDirectory.physicalOffset + ( pageDirectory.count - numPageDescriptors ) * PAGE_DESCRIPTOR_BYTES;
218
219 for( int tag : PAGE_TABLE_TAGS )
220 {
221 if( tag >= static_cast<int>( m_sections.size() ) )
222 continue;
223
224 SDB_SECTION& section = m_sections[tag];
225 uint64_t records = 0;
226
227 for( uint32_t page = 0; page < section.totalBytes; ++page )
228 {
229 if( descriptor + PAGE_DESCRIPTOR_BYTES > m_data.size() )
230 THROW_IO_ERROR( "Truncated PADS page-controller directory" );
231
232 records += m_cursor.U32At( descriptor + 8 );
233 descriptor += PAGE_DESCRIPTOR_BYTES;
234 }
235
236 if( records > UINT32_MAX )
237 THROW_IO_ERROR( "Invalid PADS page-controller record count" );
238
239 section.physicalCount = static_cast<uint32_t>( records );
240 section.physicalLiveCount = static_cast<uint32_t>( records );
241 }
242
243 SDB_SECTION& ordinals = m_sections.at( 29 );
244 ordinals.physicalOffset = static_cast<uint32_t>( cursor );
245 ordinals.physicalBytes = ordinals.totalBytes;
246 ordinals.physicalCount = ordinals.count;
247 ordinals.physicalLiveCount = ordinals.count;
248 cursor += ordinals.totalBytes;
249
250 for( int tag : PAGED_READ_ORDER )
251 {
252 SDB_SECTION& section = m_sections.at( tag );
253 uint64_t bytes = static_cast<uint64_t>( section.physicalCount ) * recordStride( tag );
254
255 if( cursor + bytes > m_data.size() || bytes > UINT32_MAX )
256 THROW_IO_ERROR( "Invalid PADS paged-controller extent" );
257
258 section.physicalOffset = static_cast<uint32_t>( cursor );
259 section.physicalBytes = static_cast<uint32_t>( bytes );
260 cursor += bytes;
261 }
262
263 SDB_SECTION& routeRules = m_sections.at( 46 );
264 uint32_t liveRouteRules = 0;
265 uint32_t routeRuleStride = recordStride( 46 );
266
267 for( uint32_t index = 0; index < routeRules.physicalCount; ++index )
268 {
269 uint64_t offset =
270 static_cast<uint64_t>( routeRules.physicalOffset ) + static_cast<uint64_t>( index ) * routeRuleStride;
271
272 if( m_cursor.U32At( offset ) < 0x80000000 && m_cursor.U32At( offset + 4 ) != 0 )
273 ++liveRouteRules;
274 }
275
276 routeRules.physicalLiveCount = liveRouteRules;
277
278 SDB_SECTION& relationships = m_sections.at( 49 );
279
280 if( cursor + relationships.totalBytes > m_data.size() - FOOTER_SIZE )
281 THROW_IO_ERROR( "Invalid PADS relationship-controller extent" );
282
283 relationships.physicalOffset = static_cast<uint32_t>( cursor );
284 relationships.physicalBytes = relationships.totalBytes;
285 relationships.physicalCount = relationships.count;
286 relationships.physicalLiveCount = relationships.count;
287 cursor += relationships.totalBytes;
288
289 uint64_t routeRuleStateBytes = static_cast<uint64_t>( liveRouteRules ) * 4;
290
291 if( cursor + routeRuleStateBytes > m_data.size() - FOOTER_SIZE )
292 THROW_IO_ERROR( "Invalid PADS route-rule state extent" );
293
294 cursor += routeRuleStateBytes;
295
296 for( int tag : { 51, 50, 52, 53, 54, 55, 56, 57 } )
297 {
298 SDB_SECTION& section = m_sections.at( tag );
299
300 if( cursor + section.totalBytes > m_data.size() - FOOTER_SIZE )
301 THROW_IO_ERROR( "Invalid PADS flat-controller extent" );
302
303 section.physicalOffset = static_cast<uint32_t>( cursor );
304 section.physicalBytes = section.totalBytes;
305 section.physicalCount = section.count;
306 section.physicalLiveCount = section.count;
307 cursor += section.totalBytes;
308 }
309
310 for( int tag = 58; tag <= 64; ++tag )
311 {
312 SDB_SECTION& section = m_sections.at( tag );
313
314 if( cursor + section.totalBytes > m_data.size() - FOOTER_SIZE )
315 THROW_IO_ERROR( "Invalid PADS flat-controller extent" );
316
317 section.physicalOffset = static_cast<uint32_t>( cursor );
318 section.physicalBytes = section.totalBytes;
319 section.physicalCount = section.count;
320 section.physicalLiveCount = section.count;
321 cursor += section.totalBytes;
322 }
323
324 for( int tag : { 65, 66 } )
325 {
326 SDB_SECTION& section = m_sections.at( tag );
327 uint64_t bytes = static_cast<uint64_t>( section.physicalCount ) * recordStride( tag );
328
329 if( cursor + bytes > m_data.size() - FOOTER_SIZE || bytes > UINT32_MAX )
330 THROW_IO_ERROR( "Invalid PADS paged-controller extent" );
331
332 section.physicalOffset = static_cast<uint32_t>( cursor );
333 section.physicalBytes = static_cast<uint32_t>( bytes );
334 section.physicalLiveCount = section.physicalCount;
335 cursor += bytes;
336 }
337
338 for( int tag : { 67, 68 } )
339 {
340 SDB_SECTION& section = m_sections.at( tag );
341
342 if( cursor + section.totalBytes > m_data.size() - FOOTER_SIZE )
343 THROW_IO_ERROR( "Invalid PADS flat-controller extent" );
344
345 section.physicalOffset = static_cast<uint32_t>( cursor );
346 section.physicalBytes = section.totalBytes;
347 section.physicalCount = section.count;
348 section.physicalLiveCount = section.count;
349 cursor += section.totalBytes;
350 }
351
352 SDB_SECTION& layers = m_sections.at( 69 );
353 const uint64_t layerBytes = 12 + static_cast<uint64_t>( layers.totalBytes );
354
355 if( cursor + layerBytes > m_data.size() - FOOTER_SIZE || layerBytes > UINT32_MAX )
356 THROW_IO_ERROR( "Invalid PADS layer-controller extent" );
357
358 layers.physicalOffset = static_cast<uint32_t>( cursor );
359 layers.physicalBytes = static_cast<uint32_t>( layerBytes );
360 layers.physicalCount = layers.count;
361 layers.physicalLiveCount = layers.count;
362 cursor += layerBytes;
363
364 SDB_SECTION& layerState = m_sections.at( 70 );
365
366 if( cursor + 4 > m_data.size() - FOOTER_SIZE )
367 THROW_IO_ERROR( "Invalid PADS layer-state extent" );
368
369 layerState.physicalOffset = static_cast<uint32_t>( cursor );
370 layerState.physicalBytes = 4;
371 layerState.physicalCount = layerState.count;
372 layerState.physicalLiveCount = layerState.count;
373 cursor += 4;
374
375 SDB_SECTION& displayPreferences = m_sections.at( 71 );
376
377 if( displayPreferences.totalBytes < 4 )
378 THROW_IO_ERROR( "Invalid PADS display-preferences extent" );
379
380 const uint64_t preferenceBytes = displayPreferences.totalBytes - 4;
381
382 if( cursor + preferenceBytes > m_data.size() - FOOTER_SIZE )
383 THROW_IO_ERROR( "Invalid PADS display-preferences extent" );
384
385 displayPreferences.physicalOffset = static_cast<uint32_t>( cursor );
386 displayPreferences.physicalBytes = static_cast<uint32_t>( preferenceBytes );
387 displayPreferences.physicalCount = displayPreferences.count;
388 displayPreferences.physicalLiveCount = displayPreferences.count;
389 cursor += preferenceBytes;
390
391 for( int tag = 72; tag <= 73 && tag < static_cast<int>( m_sections.size() ); ++tag )
392 {
393 SDB_SECTION& section = m_sections[tag];
394
395 if( cursor + section.totalBytes > m_data.size() - FOOTER_SIZE )
396 THROW_IO_ERROR( "Invalid PADS flat-controller extent" );
397
398 section.physicalOffset = static_cast<uint32_t>( cursor );
399 section.physicalBytes = section.totalBytes;
400 section.physicalCount = section.count;
401 section.physicalLiveCount = section.count;
402 cursor += section.totalBytes;
403 }
404
405 if( m_sections.size() > 74 )
406 {
407 SDB_SECTION& section = m_sections[74];
408 uint64_t bytes = static_cast<uint64_t>( section.physicalCount ) * recordStride( 74 );
409
410 if( cursor + bytes > m_data.size() - FOOTER_SIZE || bytes > UINT32_MAX )
411 THROW_IO_ERROR( "Invalid PADS paged-controller extent" );
412
413 section.physicalOffset = static_cast<uint32_t>( cursor );
414 section.physicalBytes = static_cast<uint32_t>( bytes );
415 section.physicalLiveCount = section.physicalCount;
416 cursor += bytes;
417 }
418
419 const uint32_t containerItemsOffset = m_cursor.U32At( m_data.size() - 4 );
420
421 if( containerItemsOffset > m_data.size() - FOOTER_SIZE || cursor > containerItemsOffset
422 || containerItemsOffset - cursor < 15 )
423 THROW_IO_ERROR( "Invalid PADS post-layer database extent" );
424
425 static constexpr char POWER_SYS_TITLE[] = "PowerSYS";
426
427 if( m_cursor.U8At( cursor + 4 ) != sizeof( POWER_SYS_TITLE ) - 1
428 || !std::equal( std::begin( POWER_SYS_TITLE ), std::end( POWER_SYS_TITLE ) - 1,
429 m_data.begin() + static_cast<std::ptrdiff_t>( cursor + 5 ) ) )
430 {
431 THROW_IO_ERROR( "Invalid PADS post-layer database header" );
432 }
433}
434
435
436const SDB_SECTION* PADS_SDB::Section( int aIndex ) const
437{
438 if( aIndex >= 0 && aIndex < static_cast<int>( m_sections.size() ) )
439 return &m_sections[aIndex];
440
441 return nullptr;
442}
443
444
446{
447 // Section 1 is the serialized *PCB* board-setup parameter block. Its rotated logical view
448 // starts before the physical section-2/3 stream, and the displacement is accounted for by
449 // two declared quantities, so the block is computed rather than searched for.
450 //
451 // The directory's own slot count is stored in entry 1 and is one more than the number of
452 // sections, which matters here because the version-implied count this reader still uses
453 // elsewhere is 16 bytes too large on v0x2022 and v0x2024. The rest of the displacement is
454 // section 2, a run of 48-byte view-state records that PADS writes ahead of the block; its
455 // length is simply that section's declared count.
456 //
457 // base = HEADER_SIZE + (entry[1].count - 1) * DIR_ENTRY_SIZE
458 // + entry[2].count * VIEW_STATE_RECORD_BYTES
459 //
460 // This resolves all 597 unique files in the four-root corpus. Every displacement the old
461 // field-range search absorbed is an exact multiple of 48 once the stored slot count is used.
462 const SDB_SECTION* directory = Section( 1 );
463 const SDB_SECTION* viewStates = Section( 2 );
464
465 if( !directory || !viewStates || directory->count == 0 )
466 THROW_IO_ERROR( "Invalid PADS board-setup controllers" );
467
468 const uint64_t base = static_cast<uint64_t>( HEADER_SIZE )
469 + static_cast<uint64_t>( directory->count - 1 ) * DIR_ENTRY_SIZE
470 + static_cast<uint64_t>( viewStates->count ) * VIEW_STATE_RECORD_BYTES;
471
472 if( base + 100 > m_data.size() )
473 THROW_IO_ERROR( "Invalid PADS board-setup extent" );
474
475 // These fields validate the block at its declared location; they never choose another
476 // location. They mirror the *PCB* section's ASCII-export order: SCALE, BACKUPTIME,
477 // REAL WIDTH, ALLSIGONOFF, REFNAMESIZE.
478 const float scale = m_cursor.F32At( base + 56 );
479
480 if( !( scale > 0.01f && scale < 1e6f ) )
481 THROW_IO_ERROR( "Invalid PADS board-setup scale" );
482
483 const int32_t backupTime = m_cursor.I32At( base + 76 );
484
485 if( backupTime < 0 || backupTime > 100000 )
486 THROW_IO_ERROR( "Invalid PADS board-setup backup interval" );
487
488 // REAL WIDTH is legitimately zero on boards that never set a default trace width, so the
489 // lower bound the old search used rejected 23 of the corpus outright. Every one of those
490 // rejects failed on this predicate alone and every one of them reads zero here.
491 const int32_t realWidth = m_cursor.I32At( base + 80 );
492
493 if( realWidth < 0 || realWidth >= 100000000 )
494 THROW_IO_ERROR( "Invalid PADS board-setup display width" );
495
496 const int32_t allSigOnOff = m_cursor.I32At( base + 84 );
497
498 if( allSigOnOff != 0 && allSigOnOff != 1 )
499 THROW_IO_ERROR( "Invalid PADS board-setup signal-display flag" );
500
501 const int32_t refHeight = m_cursor.I32At( base + 92 );
502 const int32_t refWidth = m_cursor.I32At( base + 96 );
503
504 if( !( refWidth > 100000 && refWidth <= refHeight && refHeight < 100000000 ) )
505 THROW_IO_ERROR( "Invalid PADS board-setup reference-text size" );
506
507 m_coords.m_originX = m_cursor.I32At( base + 60 );
508 m_coords.m_originY = m_cursor.I32At( base + 64 );
509 m_coords.m_found = true;
510 m_coords.m_headerBase = static_cast<uint32_t>( base );
511}
512
513} // namespace PADS_IO
int index
const SDB_SECTION * Section(int aIndex) const
Definition pads_sdb.cpp:436
static constexpr int HEADER_SIZE
Definition pads_sdb.h:152
void parseDirectory()
Definition pads_sdb.cpp:99
static constexpr int FOOTER_SIZE
Definition pads_sdb.h:153
static constexpr int VIEW_STATE_RECORD_BYTES
Stride of the section 2 view-state records that precede the board-setup block.
Definition pads_sdb.h:157
std::vector< uint8_t > m_data
Definition pads_sdb.h:159
uint16_t m_version
Definition pads_sdb.h:164
BINARY_CURSOR m_cursor
Definition pads_sdb.h:162
std::vector< SDB_SECTION > m_sections
Definition pads_sdb.h:165
SDB_COORDS m_coords
Definition pads_sdb.h:166
static bool HasMagic(const std::vector< uint8_t > &aBytes)
True if the first two bytes match the PADS binary magic.
Definition pads_sdb.cpp:37
static constexpr uint8_t MAGIC1
Definition pads_sdb.h:151
void parsePhysicalFraming()
Definition pads_sdb.cpp:127
static constexpr int DIR_ENTRY_SIZE
Definition pads_sdb.h:154
void Load(std::vector< uint8_t > aBytes)
Validate the header and footer, parse the section directory, and derive the coordinate origin.
Definition pads_sdb.cpp:60
static bool IsSupportedVersion(uint16_t aVersion)
Definition pads_sdb.cpp:43
int directoryEntryCount() const
Definition pads_sdb.cpp:72
void verifyFooter() const
Definition pads_sdb.cpp:93
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
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.
static constexpr size_t FOOTER_GUID_LEN
Definition pads_sdb.cpp:34
static const char FOOTER_GUID[]
Definition pads_sdb.cpp:33
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.
const int scale
The PADS PowerPCB .pcb file is a serialized snapshot of PADS' in-memory SDB (System DataBase) object ...
Definition pads_sdb.h:58
uint32_t physicalLiveCount
Definition pads_sdb.h:67
uint32_t physicalCount
Definition pads_sdb.h:66
uint32_t physicalOffset
Definition pads_sdb.h:64
uint32_t physicalBytes
Definition pads_sdb.h:65
uint32_t totalBytes
Definition pads_sdb.h:61