KiCad PCB EDA Suite
Loading...
Searching...
No Matches
eagle_bin_parser.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 * Binary Eagle parsing logic and the eagle_script[] format table ported from
5 * pcb-rnd src_plugins/io_eagle (eagle_bin.c) by Tibor 'Igor2' Palinkas and
6 * Erich S. Heinzle.
7 *
8 * COPYRIGHT (pcb-rnd, eagle_bin.c / eagle_bin.h)
9 *
10 * pcb-rnd, interactive printed circuit board design
11 * Copyright (C) 2017 Tibor 'Igor2' Palinkas
12 * Copyright (C) 2017 Erich S. Heinzle
13 *
14 * Copyright (C) 2026 KiCad Developers, see AUTHORS.txt for contributors.
15 *
16 * This program is free software; you can redistribute it and/or
17 * modify it under the terms of the GNU General Public License
18 * as published by the Free Software Foundation; either version 2
19 * of the License, or (at your option) any later version.
20 *
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, you may find one here:
28 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
29 * or you may search the http://www.gnu.org website for the version 2 license,
30 * or you may write to the Free Software Foundation, Inc.,
31 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
32 */
33
34#include "eagle_bin_parser.h"
35
36#include <cmath>
37#include <cstring>
38
39#include <wx/intl.h>
40#include <wx/stream.h>
41#include <wx/xml/xml.h>
42
43#include <ki_exception.h>
44#include <macros.h>
45#include <trace_helpers.h>
46#include <wx/log.h>
47
48// Section keyword ids; the high byte selects the record kind in the binary stream.
49enum EGKW
50{
69 EGKW_SECT_ARC = 0x2400,
74 EGKW_SECT_VIA = 0x2900,
75 EGKW_SECT_PAD = 0x2a00,
76 EGKW_SECT_SMD = 0x2b00,
77 EGKW_SECT_PIN = 0x2c00,
100
101 // Synthetic nodes created during post-processing.
104};
105
106namespace
107{
108enum ATTR_TYPE
109{
110 T_BMB, // bit-mask-bool: apply mask in len to byte at offs, result is a boolean
111 T_UBF, // unsigned bitfield, len is a BITFIELD() descriptor
112 T_INT, // signed little-endian integer
113 T_DBL, // 8-byte IEEE double
114 T_STR // fixed-length NUL-padded string
115};
116
117enum SS_TYPE
118{
119 SS_DIRECT, // number of direct children
120 SS_RECURSIVE, // number of all children, recursively
121 SS_RECURSIVE_MINUS_1 // same, but decrement the count first
122};
123
124// Describe a bitfield hosted in a field of the given width; first/last are
125// inclusive bit offsets counted from the LSB.
126constexpr uint32_t BITFIELD( uint32_t aWidth, uint32_t aFirst, uint32_t aLast )
127{
128 return ( aWidth << 16 ) | ( aFirst << 8 ) | aLast;
129}
130
131struct FMATCH
132{
133 int offs; // 0 terminates the list
134 unsigned len;
135 int val;
136};
137
138struct SUBSECT
139{
140 int offs; // 0 terminates the list
141 int len;
142 SS_TYPE ssType;
143 const char* treeName; // if set, wrap children in a synthetic subtree
144};
145
146struct ATTR
147{
148 const char* name; // nullptr terminates the list
149 ATTR_TYPE type;
150 int offs;
151 uint32_t len;
152};
153
154struct SCRIPT_ROW
155{
156 unsigned cmd, cmdMask; // matches when (block[0..1] & mask) == cmd
157 const char* name;
158 FMATCH fmatch[4];
159 SUBSECT subs[8];
160 ATTR attrs[32];
161};
162
163#define TERM_F \
164 { \
165 0, 0, 0 \
166 }
167#define TERM_S \
168 { \
169 0, 0, SS_DIRECT, nullptr \
170 }
171#define TERM_A \
172 { \
173 nullptr, T_INT, 0, 0 \
174 }
175
176// The format spec. Each row decodes one record kind; every offset is exact.
177const SCRIPT_ROW g_script[] = {
179 0xFF7F,
180 "drawing",
181 { TERM_F },
182 { { 4, 4, SS_RECURSIVE_MINUS_1, nullptr }, TERM_S },
183 { { "subsecs", T_INT, 2, 2 },
184 { "numsecs", T_INT, 4, 4 },
185 { "subsecsMSB", T_INT, 3, 1 },
186 { "subsecsLSB", T_INT, 2, 1 },
187 { "numsecsMSB2", T_INT, 7, 1 },
188 { "numsecsMSB1", T_INT, 6, 1 },
189 { "numsecsMSB0", T_INT, 5, 1 },
190 { "numsecsLSB", T_INT, 4, 1 },
191 { "v1", T_INT, 8, 1 },
192 { "v2", T_INT, 9, 1 },
193 TERM_A } },
194 { EGKW_SECT_UNKNOWN11, 0xFFFF, "unknown11", { TERM_F }, { TERM_S }, { TERM_A } },
196 0xFFFF,
197 "grid",
198 { TERM_F },
199 { TERM_S },
200 { { "display", T_BMB, 2, 0x01 },
201 { "visible", T_BMB, 2, 0x02 },
202 { "unit", T_UBF, 3, BITFIELD( 1, 0, 3 ) },
203 { "altunit", T_UBF, 3, BITFIELD( 1, 4, 7 ) },
204 { "multiple", T_INT, 4, 3 },
205 { "size", T_DBL, 8, 8 },
206 { "altsize", T_DBL, 16, 8 },
207 TERM_A } },
209 0xFF7F,
210 "layer",
211 { TERM_F },
212 { TERM_S },
213 { { "side", T_BMB, 2, 0x10 },
214 { "visible", T_UBF, 2, BITFIELD( 1, 2, 3 ) },
215 { "active", T_BMB, 2, 0x02 },
216 { "number", T_UBF, 3, BITFIELD( 1, 0, 7 ) },
217 { "other", T_INT, 4, 1 },
218 { "fill", T_UBF, 5, BITFIELD( 1, 0, 3 ) },
219 { "color", T_UBF, 6, BITFIELD( 1, 0, 5 ) },
220 { "name", T_STR, 15, 9 },
221 TERM_A } },
223 0xFFFF,
224 "schema",
225 { TERM_F },
226 { { 4, 4, SS_DIRECT, nullptr }, TERM_S },
227 { { "shtsubsecs", T_INT, 8, 4 }, { "atrsubsecs", T_INT, 12, 4 }, { "xref_format", T_STR, 19, 5 }, TERM_A } },
229 0xFF7F,
230 "library",
231 { TERM_F },
232 { { 4, 4, SS_RECURSIVE, nullptr }, { 8, 4, SS_RECURSIVE, nullptr }, { 12, 4, SS_RECURSIVE, nullptr }, TERM_S },
233 { { "devsubsecs", T_INT, 4, 4 },
234 { "symsubsecs", T_INT, 8, 4 },
235 { "pacsubsecs", T_INT, 12, 4 },
236 { "children", T_INT, 8, 4 },
237 { "name", T_STR, 16, 8 },
238 TERM_A } },
240 0xFF7F,
241 "devices",
242 { TERM_F },
243 { { 4, 4, SS_DIRECT, nullptr }, TERM_S },
244 { { "children", T_INT, 8, 4 }, { "library", T_STR, 16, 8 }, TERM_A } },
246 0xFF7F,
247 "symbols",
248 { TERM_F },
249 { { 4, 4, SS_RECURSIVE, nullptr }, TERM_S },
250 { { "children", T_INT, 8, 4 }, { "library", T_STR, 16, 8 }, TERM_A } },
252 0xFF5F,
253 "packages",
254 { TERM_F },
255 { { 4, 4, SS_RECURSIVE, nullptr }, TERM_S },
256 { { "subsects", T_INT, 4, 4 },
257 { "children", T_INT, 8, 2 },
258 { "desc", T_STR, 10, 6 },
259 { "library", T_STR, 16, 8 },
260 TERM_A } },
262 0xFFFF,
263 "schemasheet",
264 { TERM_F },
265 { { 2, 2, SS_DIRECT, nullptr }, TERM_S },
266 { { "minx", T_INT, 4, 2 },
267 { "miny", T_INT, 6, 2 },
268 { "maxx", T_INT, 8, 2 },
269 { "maxy", T_INT, 10, 2 },
270 { "partsubsecs", T_INT, 12, 4 },
271 { "bussubsecs", T_INT, 16, 4 },
272 { "netsubsecs", T_INT, 20, 4 },
273 TERM_A } },
275 0xFF37,
276 "board",
277 { TERM_F },
278 { { 12, 4, SS_RECURSIVE, "libraries" },
279 { 2, 2, SS_DIRECT, "plain" },
280 { 16, 4, SS_RECURSIVE, "elements" },
281 { 20, 4, SS_RECURSIVE, "signals" },
282 TERM_S },
283 { { "minx", T_INT, 4, 2 },
284 { "miny", T_INT, 6, 2 },
285 { "maxx", T_INT, 8, 2 },
286 { "maxy", T_INT, 10, 2 },
287 { "defsubsecs", T_INT, 12, 4 },
288 { "pacsubsecs", T_INT, 16, 4 },
289 { "netsubsecs", T_INT, 20, 4 },
290 TERM_A } },
292 0xFFB3,
293 "signal",
294 { TERM_F },
295 { { 2, 2, SS_DIRECT, nullptr }, TERM_S },
296 { { "minx", T_INT, 4, 2 },
297 { "miny", T_INT, 6, 2 },
298 { "maxx", T_INT, 8, 2 },
299 { "maxy", T_INT, 10, 2 },
300 { "airwires", T_BMB, 12, 0x02 },
301 { "netclass", T_UBF, 13, BITFIELD( 1, 0, 3 ) },
302 { "name", T_STR, 16, 8 },
303 TERM_A } },
305 0xFFFF,
306 "symbol",
307 { TERM_F },
308 { { 2, 2, SS_DIRECT, nullptr }, TERM_S },
309 { { "minx", T_INT, 4, 2 },
310 { "miny", T_INT, 6, 2 },
311 { "maxx", T_INT, 8, 2 },
312 { "maxy", T_INT, 10, 2 },
313 { "name", T_STR, 16, 8 },
314 TERM_A } },
316 0xFFDF,
317 "package",
318 { TERM_F },
319 { { 2, 2, SS_RECURSIVE, nullptr }, TERM_S },
320 { { "minx", T_INT, 4, 2 },
321 { "miny", T_INT, 6, 2 },
322 { "maxx", T_INT, 8, 2 },
323 { "maxy", T_INT, 10, 2 },
324 { "desc", T_STR, 13, 5 },
325 { "name", T_STR, 18, 6 },
326 TERM_A } },
328 0xFFFF,
329 "schemanet",
330 { TERM_F },
331 { { 2, 2, SS_DIRECT, nullptr }, TERM_S },
332 { { "minx", T_INT, 4, 2 },
333 { "miny", T_INT, 6, 2 },
334 { "maxx", T_INT, 8, 2 },
335 { "maxy", T_INT, 10, 2 },
336 { "netclass", T_UBF, 13, BITFIELD( 1, 0, 3 ) },
337 { "name", T_STR, 18, 6 },
338 TERM_A } },
340 0xFFFF,
341 "path",
342 { TERM_F },
343 { { 2, 2, SS_DIRECT, nullptr }, TERM_S },
344 { { "minx", T_INT, 4, 2 }, { "miny", T_INT, 6, 2 }, { "maxx", T_INT, 8, 2 }, { "maxy", T_INT, 10, 2 }, TERM_A } },
346 0xFFF7,
347 "polygon",
348 { TERM_F },
349 { { 2, 2, SS_DIRECT, nullptr }, TERM_S },
350 { { "minx", T_INT, 4, 2 },
351 { "miny", T_INT, 6, 2 },
352 { "maxx", T_INT, 8, 2 },
353 { "maxy", T_INT, 10, 2 },
354 { "width", T_INT, 12, 2 },
355 { "spacing", T_INT, 14, 2 },
356 { "isolate", T_INT, 16, 2 },
357 { "layer", T_UBF, 18, BITFIELD( 1, 0, 7 ) },
358 { "pour", T_BMB, 19, 0x01 },
359 { "rank", T_BMB, 19, BITFIELD( 1, 1, 3 ) },
360 { "thermals", T_BMB, 19, 0x80 },
361 { "orphans", T_BMB, 19, 0x40 },
362 TERM_A } },
364 0xFF43,
365 "wire",
366 { TERM_F },
367 { TERM_S },
368 { { "layer", T_UBF, 3, BITFIELD( 1, 0, 7 ) },
369 { "half_width", T_INT, 20, 2 },
370 { "stflags", T_BMB, 22, 0x33 },
371 { "clockwise", T_BMB, 22, 0x20 },
372 { "linetype", T_UBF, 23, BITFIELD( 1, 0, 7 ) },
373 { "linetype_0_x1", T_INT, 4, 4 },
374 { "linetype_0_y1", T_INT, 8, 4 },
375 { "linetype_0_x2", T_INT, 12, 4 },
376 { "linetype_0_y2", T_INT, 16, 4 },
377 { "arc_negflags", T_UBF, 19, BITFIELD( 1, 0, 4 ) },
378 { "arc_c1", T_INT, 7, 1 },
379 { "arc_c2", T_INT, 11, 1 },
380 { "arc_c3", T_INT, 15, 1 },
381 { "arc_x1", T_INT, 4, 3 },
382 { "arc_y1", T_INT, 8, 3 },
383 { "arc_x2", T_INT, 12, 3 },
384 { "arc_y2", T_INT, 16, 3 },
385 TERM_A } },
387 0xFFFF,
388 "arc",
389 { TERM_F },
390 { TERM_S },
391 { { "layer", T_UBF, 3, BITFIELD( 1, 0, 7 ) },
392 { "half_width", T_INT, 20, 2 },
393 { "clockwise", T_BMB, 22, 0x20 },
394 { "arctype", T_UBF, 23, BITFIELD( 1, 0, 7 ) },
395 { "arc_negflags", T_UBF, 19, BITFIELD( 1, 0, 7 ) },
396 { "arc_c1", T_INT, 7, 1 },
397 { "arc_c2", T_INT, 11, 1 },
398 { "arc_c3", T_INT, 15, 1 },
399 { "arc_x1", T_INT, 4, 3 },
400 { "arc_y1", T_INT, 8, 3 },
401 { "arc_x2", T_INT, 12, 3 },
402 { "arc_y2", T_INT, 16, 3 },
403 { "arctype_other_x1", T_INT, 4, 4 },
404 { "arctype_other_y1", T_INT, 8, 4 },
405 { "arctype_other_x2", T_INT, 12, 4 },
406 { "arctype_other_y2", T_INT, 16, 4 },
407 TERM_A } },
409 0xFF53,
410 "circle",
411 { TERM_F },
412 { TERM_S },
413 { { "layer", T_UBF, 3, BITFIELD( 1, 0, 7 ) },
414 { "x", T_INT, 4, 4 },
415 { "y", T_INT, 8, 4 },
416 { "radius", T_INT, 12, 4 },
417 { "half_width", T_INT, 20, 4 },
418 TERM_A } },
420 0xFF5F,
421 "rectangle",
422 { TERM_F },
423 { TERM_S },
424 { { "layer", T_UBF, 3, BITFIELD( 1, 0, 7 ) },
425 { "x1", T_INT, 4, 4 },
426 { "y1", T_INT, 8, 4 },
427 { "x2", T_INT, 12, 4 },
428 { "y2", T_INT, 16, 4 },
429 { "bin_rot", T_INT, 20, 2 },
430 TERM_A } },
432 0xFFFF,
433 "junction",
434 { TERM_F },
435 { TERM_S },
436 { { "layer", T_UBF, 3, BITFIELD( 1, 0, 7 ) },
437 { "x", T_INT, 4, 4 },
438 { "y", T_INT, 8, 4 },
439 { "width_2", T_INT, 12, 2 },
440 TERM_A } },
442 0xFF53,
443 "hole",
444 { TERM_F },
445 { TERM_S },
446 { { "x", T_INT, 4, 4 },
447 { "y", T_INT, 8, 4 },
448 { "half_diameter", T_UBF, 12, BITFIELD( 2, 0, 15 ) },
449 { "half_drill", T_UBF, 12, BITFIELD( 2, 0, 15 ) },
450 TERM_A } },
452 0xFF7F,
453 "via",
454 { TERM_F },
455 { TERM_S },
456 { { "shape", T_INT, 2, 1 },
457 { "x", T_INT, 4, 4 },
458 { "y", T_INT, 8, 4 },
459 { "half_drill", T_UBF, 12, BITFIELD( 2, 0, 15 ) },
460 { "half_diameter", T_UBF, 14, BITFIELD( 2, 0, 15 ) },
461 { "layers", T_UBF, 16, BITFIELD( 1, 0, 7 ) },
462 { "stop", T_BMB, 17, 0x01 },
463 TERM_A } },
465 0xFF5F,
466 "pad",
467 { TERM_F },
468 { TERM_S },
469 { { "shape", T_INT, 2, 1 },
470 { "x", T_INT, 4, 4 },
471 { "y", T_INT, 8, 4 },
472 { "half_drill", T_UBF, 12, BITFIELD( 2, 0, 15 ) },
473 { "half_diameter", T_UBF, 14, BITFIELD( 2, 0, 15 ) },
474 { "bin_rot", T_INT, 16, 2 },
475 { "stop", T_BMB, 18, 0x01 },
476 { "thermals", T_BMB, 18, 0x04 },
477 { "first", T_BMB, 18, 0x08 },
478 { "name", T_STR, 19, 5 },
479 TERM_A } },
481 0xFF7F,
482 "smd",
483 { TERM_F },
484 { TERM_S },
485 { { "roundness", T_INT, 2, 1 },
486 { "layer", T_UBF, 3, BITFIELD( 1, 0, 7 ) },
487 { "x", T_INT, 4, 4 },
488 { "y", T_INT, 8, 4 },
489 { "half_dx", T_UBF, 12, BITFIELD( 2, 0, 15 ) },
490 { "half_dy", T_UBF, 14, BITFIELD( 2, 0, 15 ) },
491 { "bin_rot", T_UBF, 16, BITFIELD( 2, 0, 11 ) },
492 { "stop", T_BMB, 18, 0x01 },
493 { "cream", T_BMB, 18, 0x02 },
494 { "thermals", T_BMB, 18, 0x04 },
495 { "first", T_BMB, 18, 0x08 },
496 { "name", T_STR, 19, 5 },
497 TERM_A } },
499 0xFF7F,
500 "pin",
501 { TERM_F },
502 { TERM_S },
503 { { "function", T_UBF, 2, BITFIELD( 1, 0, 1 ) },
504 { "visible", T_UBF, 2, BITFIELD( 1, 6, 7 ) },
505 { "x", T_INT, 4, 4 },
506 { "y", T_INT, 8, 4 },
507 { "direction", T_UBF, 12, BITFIELD( 1, 0, 3 ) },
508 { "length", T_UBF, 12, BITFIELD( 1, 4, 5 ) },
509 { "bin_rot", T_UBF, 12, BITFIELD( 1, 6, 7 ) },
510 { "swaplevel", T_INT, 13, 1 },
511 { "name", T_STR, 14, 10 },
512 TERM_A } },
514 0xFF7F,
515 "gate",
516 { TERM_F },
517 { TERM_S },
518 { { "x", T_INT, 4, 4 },
519 { "y", T_INT, 8, 4 },
520 { "addlevel", T_INT, 12, 1 },
521 { "swap", T_INT, 13, 1 },
522 { "symno", T_INT, 14, 2 },
523 { "name", T_STR, 16, 8 },
524 TERM_A } },
525 // Masking the element low byte with 0x53 leaks bit 6, so a real-world element
526 // with low byte 0x60 (spin plus another flag) fails to match. The whole low
527 // byte is flags here; rotation/mirror/spin are decoded from bytes 16-17, and
528 // 0x2e is a unique high byte, so match on the high byte alone.
530 0xFF00,
531 "element",
532 { TERM_F },
533 { { 2, 2, SS_DIRECT, nullptr }, TERM_S },
534 { { "x", T_INT, 4, 4 },
535 { "y", T_INT, 8, 4 },
536 { "library", T_INT, 12, 2 },
537 { "package", T_INT, 14, 2 },
538 { "bin_rot", T_UBF, 16, BITFIELD( 2, 0, 11 ) },
539 { "mirrored", T_BMB, 17, 0x10 },
540 { "spin", T_BMB, 17, 0x40 },
541 TERM_A } },
543 0xFF5F,
544 "element2",
545 { TERM_F },
546 { TERM_S },
547 { { "name", T_STR, 2, 8 }, { "value", T_STR, 10, 14 }, TERM_A } },
549 0xFFFF,
550 "instance",
551 { TERM_F },
552 { { 2, 2, SS_DIRECT, nullptr }, TERM_S },
553 { { "x", T_INT, 4, 4 },
554 { "y", T_INT, 8, 4 },
555 { "placed", T_INT, 12, 2 },
556 { "gateno", T_INT, 14, 2 },
557 { "bin_rot", T_UBF, 16, BITFIELD( 2, 10, 11 ) },
558 { "mirrored", T_UBF, 16, BITFIELD( 2, 12, 12 ) },
559 { "smashed", T_BMB, 18, 0x01 },
560 TERM_A } },
562 0xFF53,
563 "text",
564 { TERM_F },
565 { TERM_S },
566 { { "layer", T_UBF, 3, BITFIELD( 1, 0, 7 ) },
567 { "x", T_INT, 4, 4 },
568 { "y", T_INT, 8, 4 },
569 { "half_size", T_INT, 12, 2 },
570 { "ratio", T_UBF, 14, BITFIELD( 2, 2, 6 ) },
571 { "bin_rot", T_UBF, 16, BITFIELD( 2, 0, 11 ) },
572 { "mirrored", T_UBF, 16, BITFIELD( 2, 12, 12 ) },
573 { "spin", T_UBF, 16, BITFIELD( 2, 14, 14 ) },
574 { "textfield", T_STR, 18, 5 },
575 TERM_A } },
577 0xFFFF,
578 "netbuslabel",
579 { TERM_F },
580 { TERM_S },
581 { { "layer", T_UBF, 3, BITFIELD( 1, 0, 7 ) },
582 { "x", T_INT, 4, 4 },
583 { "y", T_INT, 8, 4 },
584 { "size", T_INT, 12, 2 },
585 { "ratio", T_UBF, 14, BITFIELD( 2, 2, 6 ) },
586 { "bin_rot", T_UBF, 16, BITFIELD( 2, 0, 11 ) },
587 { "mirrored", T_UBF, 16, BITFIELD( 2, 12, 12 ) },
588 { "spin", T_UBF, 16, BITFIELD( 2, 14, 14 ) },
589 { "textfield", T_STR, 18, 5 },
590 TERM_A } },
592 0xFF00,
593 "name",
594 { TERM_F },
595 { TERM_S },
596 { { "layer", T_UBF, 3, BITFIELD( 1, 0, 7 ) },
597 { "x", T_INT, 4, 4 },
598 { "y", T_INT, 8, 4 },
599 { "size", T_INT, 12, 2 },
600 { "ratio", T_UBF, 14, BITFIELD( 2, 2, 6 ) },
601 { "bin_rot", T_UBF, 16, BITFIELD( 2, 0, 11 ) },
602 { "mirrored", T_UBF, 16, BITFIELD( 2, 12, 12 ) },
603 { "spin", T_UBF, 16, BITFIELD( 2, 14, 14 ) },
604 { "textfield", T_STR, 18, 5 },
605 TERM_A } },
607 0xFF73,
608 "value",
609 { TERM_F },
610 { TERM_S },
611 { { "layer", T_UBF, 3, BITFIELD( 1, 0, 7 ) },
612 { "x", T_INT, 4, 4 },
613 { "y", T_INT, 8, 4 },
614 { "size", T_INT, 12, 2 },
615 { "ratio", T_UBF, 14, BITFIELD( 2, 2, 6 ) },
616 { "bin_rot", T_UBF, 16, BITFIELD( 2, 0, 11 ) },
617 { "mirrored", T_UBF, 16, BITFIELD( 2, 12, 12 ) },
618 { "spin", T_UBF, 16, BITFIELD( 2, 14, 14 ) },
619 { "textfield", T_STR, 18, 5 },
620 TERM_A } },
622 0xFF7F,
623 "packagevariant",
624 { TERM_F },
625 { { 2, 2, SS_DIRECT, nullptr }, TERM_S },
626 { { "package", T_INT, 4, 2 }, { "table", T_STR, 6, 13 }, { "name", T_STR, 19, 5 }, TERM_A } },
628 0xFF7F,
629 "device",
630 { TERM_F },
631 { { 2, 2, SS_RECURSIVE, "gates" }, { 4, 2, SS_RECURSIVE, "variants" }, TERM_S },
632 { { "gates", T_INT, 2, 2 },
633 { "variants", T_INT, 4, 2 },
634 { "prefix", T_STR, 8, 5 },
635 { "desc", T_STR, 13, 5 },
636 { "name", T_STR, 18, 5 },
637 TERM_A } },
639 0xFFFF,
640 "part",
641 { TERM_F },
642 { { 2, 2, SS_DIRECT, nullptr }, TERM_S },
643 { { "lib", T_INT, 4, 2 },
644 { "device", T_INT, 6, 2 },
645 { "variant", T_INT, 8, 1 },
646 { "technology", T_INT, 9, 2 },
647 { "name", T_STR, 11, 5 },
648 { "value", T_STR, 16, 8 },
649 TERM_A } },
650 { EGKW_SECT_SCHEMABUS, 0xFFFF, nullptr, { TERM_F }, { TERM_S }, { TERM_A } },
651 { EGKW_SECT_VARIANTCONNECTIONS, 0xFF7F, "variantconnections", { TERM_F }, { TERM_S }, { TERM_A } },
652 { EGKW_SECT_SCHEMACONNECTION, 0xFFFF, nullptr, { TERM_F }, { TERM_S }, { TERM_A } },
654 0xFF57,
655 "contactref",
656 { TERM_F },
657 { TERM_S },
658 { { "partnumber", T_INT, 4, 2 }, { "pin", T_INT, 6, 2 }, TERM_A } },
660 0xFFFF,
661 "smashedpart",
662 { TERM_F },
663 { TERM_S },
664 { { "layer", T_UBF, 3, BITFIELD( 1, 0, 7 ) },
665 { "x", T_INT, 4, 4 },
666 { "y", T_INT, 8, 4 },
667 { "size", T_INT, 12, 2 },
668 { "ratio", T_UBF, 14, BITFIELD( 2, 2, 6 ) },
669 { "bin_rot", T_UBF, 16, BITFIELD( 2, 0, 11 ) },
670 { "mirrored", T_UBF, 16, BITFIELD( 2, 12, 12 ) },
671 { "spin", T_UBF, 16, BITFIELD( 2, 14, 14 ) },
672 { "textfield", T_STR, 18, 5 },
673 TERM_A } },
675 0xFFFF,
676 "smashedgate",
677 { TERM_F },
678 { TERM_S },
679 { { "layer", T_UBF, 3, BITFIELD( 1, 0, 7 ) },
680 { "x", T_INT, 4, 4 },
681 { "y", T_INT, 8, 4 },
682 { "size", T_INT, 12, 2 },
683 { "ratio", T_UBF, 14, BITFIELD( 2, 2, 6 ) },
684 { "bin_rot", T_UBF, 16, BITFIELD( 2, 0, 11 ) },
685 { "mirrored", T_UBF, 16, BITFIELD( 2, 12, 12 ) },
686 { "spin", T_UBF, 16, BITFIELD( 2, 14, 14 ) },
687 { "textfield", T_STR, 18, 5 },
688 TERM_A } },
690 0xFF7F,
691 "attribute",
692 { TERM_F },
693 { TERM_S },
694 { { "layer", T_UBF, 3, BITFIELD( 1, 0, 7 ) },
695 { "x", T_INT, 4, 4 },
696 { "y", T_INT, 8, 4 },
697 { "size", T_INT, 12, 2 },
698 { "ratio", T_UBF, 14, BITFIELD( 2, 2, 6 ) },
699 { "bin_rot", T_UBF, 16, BITFIELD( 2, 0, 11 ) },
700 { "mirrored", T_UBF, 16, BITFIELD( 2, 12, 12 ) },
701 { "spin", T_UBF, 16, BITFIELD( 2, 14, 14 ) },
702 { "textfield", T_STR, 18, 5 },
703 TERM_A } },
705 0xFFFF,
706 "attribute-value",
707 { TERM_F },
708 { TERM_S },
709 { { "symbol", T_STR, 2, 5 }, { "attribute", T_STR, 7, 17 }, TERM_A } },
711 0xFFFF,
712 "frame",
713 { TERM_F },
714 { TERM_S },
715 { { "layer", T_UBF, 3, BITFIELD( 1, 0, 7 ) },
716 { "x1", T_INT, 4, 4 },
717 { "y1", T_INT, 8, 4 },
718 { "x2", T_INT, 12, 4 },
719 { "y2", T_INT, 16, 4 },
720 { "cols", T_INT, 20, 1 },
721 { "rows", T_INT, 21, 1 },
722 { "borders", T_INT, 22, 1 },
723 TERM_A } },
725 0xFFFF,
726 "smashedxref",
727 { TERM_F },
728 { TERM_S },
729 { { "layer", T_UBF, 3, BITFIELD( 1, 0, 7 ) },
730 { "x", T_INT, 4, 4 },
731 { "y", T_INT, 8, 4 },
732 { "size", T_INT, 12, 2 },
733 { "ratio", T_UBF, 14, BITFIELD( 2, 2, 6 ) },
734 { "bin_rot", T_UBF, 16, BITFIELD( 2, 0, 11 ) },
735 { "mirrored", T_UBF, 16, BITFIELD( 2, 12, 12 ) },
736 { "spin", T_UBF, 16, BITFIELD( 2, 14, 14 ) },
737 { "textfield", T_STR, 18, 5 },
738 TERM_A } },
739
740 // unknown leaves
741 { 0x5300, 0xFFFF, nullptr, { TERM_F }, { TERM_S }, { TERM_A } },
742 { 0x2d84, 0xFFFF, nullptr, { TERM_F }, { TERM_S }, { TERM_A } },
743 { 0, 0, nullptr, { TERM_F }, { TERM_S }, { TERM_A } } // end of table
744};
745} // namespace
746
747
749{
750 auto child = std::make_unique<EGB_NODE>();
751 child->id = aId;
752 child->name = aName;
753 child->parent = this;
754 children.push_back( std::move( child ) );
755
756 return children.back().get();
757}
758
759
760wxString EAGLE_BIN_PARSER::EGB_NODE::Prop( const wxString& aKey ) const
761{
762 auto it = props.find( aKey );
763 return it == props.end() ? wxString() : it->second;
764}
765
766
767long EAGLE_BIN_PARSER::EGB_NODE::PropLong( const wxString& aKey ) const
768{
769 long val = 0;
770 Prop( aKey ).ToLong( &val );
771
772 return val;
773}
774
775
776wxString EAGLE_BIN_PARSER::EGB_NODE::PropDoubled( const wxString& aKey ) const
777{
778 wxLongLong_t val = 0;
779 Prop( aKey ).ToLongLong( &val );
780
781 return wxString::Format( wxS( "%lld" ), val * 2 );
782}
783
784
786{
787 for( const auto& child : children )
788 {
789 if( child->id == aId )
790 return child.get();
791 }
792
793 return nullptr;
794}
795
796
798{
799 for( const auto& child : children )
800 {
801 if( child->name == aName )
802 return child.get();
803 }
804
805 return nullptr;
806}
807
808
811
812
813bool EAGLE_BIN_PARSER::IsBinaryEagle( wxInputStream& aStream )
814{
815 uint8_t buf[2] = { 0, 0 };
816
817 if( !aStream.IsOk() )
818 return false;
819
820 aStream.Read( buf, 2 );
821
822 if( aStream.LastRead() != 2 )
823 return false;
824
825 if( buf[0] == 0x10 && ( buf[1] == 0x00 || buf[1] == 0x80 ) )
826 return true;
827
828 return false;
829}
830
831
832void EAGLE_BIN_PARSER::requireBytes( size_t aOffs, size_t aLen ) const
833{
834 if( m_buf == nullptr || aOffs > m_buf->size() || aLen > m_buf->size() - aOffs )
835 THROW_IO_ERROR( _( "Short read in Eagle binary file (field out of bounds)." ) );
836}
837
838
839uint32_t EAGLE_BIN_PARSER::loadU32( size_t aOffs, unsigned aLen ) const
840{
841 requireBytes( aOffs, aLen );
842
843 uint32_t l = 0;
844
845 for( unsigned n = 0; n < aLen; n++ )
846 {
847 l <<= 8;
848 l |= ( *m_buf )[aOffs + aLen - n - 1];
849 }
850
851 return l;
852}
853
854
855int32_t EAGLE_BIN_PARSER::loadS32( size_t aOffs, unsigned aLen ) const
856{
857 requireBytes( aOffs, aLen );
858
859 uint32_t l = 0;
860
861 if( ( *m_buf )[aOffs + aLen - 1] & 0x80 )
862 l = 0xFFFFFFFF;
863
864 for( unsigned n = 0; n < aLen; n++ )
865 {
866 l <<= 8;
867 l |= ( *m_buf )[aOffs + aLen - n - 1];
868 }
869
870 return static_cast<int32_t>( l );
871}
872
873
874bool EAGLE_BIN_PARSER::loadBmb( size_t aOffs, uint32_t aMask ) const
875{
876 requireBytes( aOffs, 1 );
877
878 return ( ( *m_buf )[aOffs] & aMask ) != 0;
879}
880
881
882uint32_t EAGLE_BIN_PARSER::loadUbf( size_t aOffs, uint32_t aField ) const
883{
884 unsigned first = ( aField >> 8 ) & 0xff;
885 unsigned last = aField & 0xff;
886 uint32_t mask = ( 1u << ( last - first + 1 ) ) - 1;
887
888 // The high byte of the descriptor is the read length; keeping it inline ties
889 // the offset and field together rather than splitting them into locals.
890 uint32_t val = loadU32( aOffs, ( aField >> 16 ) & 0xff ) >> first;
891
892 return val & mask;
893}
894
895
896wxString EAGLE_BIN_PARSER::loadStr( size_t aOffs, unsigned aLen ) const
897{
898 requireBytes( aOffs, aLen );
899
900 const char* start = reinterpret_cast<const char*>( m_buf->data() + aOffs );
901
902 // The field is fixed length and NUL padded; stop at the first NUL but never
903 // run past the field.
904 size_t n = 0;
905
906 while( n < aLen && start[n] != '\0' )
907 n++;
908
909 return wxString::FromUTF8( start, n );
910}
911
912
913double EAGLE_BIN_PARSER::loadDouble( size_t aOffs ) const
914{
915 static_assert( sizeof( double ) == 8, "Eagle binary doubles are 8-byte IEEE-754" );
916
917 requireBytes( aOffs, sizeof( double ) );
918
919 // The file stores a little-endian IEEE-754 double. Assemble the bit pattern
920 // from individual bytes so decoding does not depend on host byte order, then
921 // reinterpret those bits as a double.
922 uint64_t bits = 0;
923
924 for( unsigned n = 0; n < sizeof( double ); n++ )
925 bits |= static_cast<uint64_t>( ( *m_buf )[aOffs + n] ) << ( 8 * n );
926
927 double d = 0.0;
928 memcpy( &d, &bits, sizeof( d ) );
929
930 return d;
931}
932
933
934int EAGLE_BIN_PARSER::readBlock( long& aNumBlocks, EGB_NODE* aParent )
935{
936 if( m_pos + 24 > m_buf->size() )
937 THROW_IO_ERROR( _( "Short read in Eagle binary file (truncated block)." ) );
938
939 size_t blockStart = m_pos;
940 m_pos += 24;
941
942 int processed = 1;
943
944 // The top-level drawing record carries the total block count.
945 if( aNumBlocks < 0 && ( *m_buf )[blockStart] == 0x10 )
946 aNumBlocks = loadS32( blockStart + 4, 4 );
947
948 const SCRIPT_ROW* sc = nullptr;
949
950 for( const SCRIPT_ROW* row = g_script; row->cmd != 0; row++ )
951 {
952 unsigned cmdh = ( row->cmd >> 8 ) & 0xFF;
953 unsigned cmdl = row->cmd & 0xFF;
954 unsigned mskh = ( row->cmdMask >> 8 ) & 0xFF;
955 unsigned mskl = row->cmdMask & 0xFF;
956
957 if( ( cmdh != ( ( *m_buf )[blockStart] & mskh ) ) || ( cmdl != ( ( *m_buf )[blockStart + 1] & mskl ) ) )
958 {
959 continue;
960 }
961
962 bool match = true;
963
964 for( const FMATCH* fm = row->fmatch; fm->offs != 0; fm++ )
965 {
966 if( loadS32( blockStart + fm->offs, fm->len ) != fm->val )
967 {
968 match = false;
969 break;
970 }
971 }
972
973 if( match )
974 {
975 sc = row;
976 break;
977 }
978 }
979
980 if( sc == nullptr )
981 {
982 THROW_IO_ERROR( wxString::Format( _( "Unknown Eagle binary block id 0x%02x%02x at offset %zu." ),
983 (unsigned) ( *m_buf )[blockStart], (unsigned) ( *m_buf )[blockStart + 1],
984 blockStart ) );
985 }
986
987 EGB_NODE* node =
988 aParent->AddChild( static_cast<int>( sc->cmd ),
989 sc->name ? wxString::FromUTF8( sc->name ) : wxString( wxS( "UNKNOWN" ) ) );
990
991 for( const ATTR* at = sc->attrs; at->name != nullptr; at++ )
992 {
993 wxString val;
994
995 switch( at->type )
996 {
997 // KiCad's Eagle XML reader parses boolean attributes as "yes"/"no", so
998 // emit T_BMB fields that way rather than "1"/"0".
999 case T_BMB: val = loadBmb( blockStart + at->offs, at->len ) ? wxS( "yes" ) : wxS( "no" ); break;
1000 case T_UBF: val = wxString::Format( wxS( "%u" ), loadUbf( blockStart + at->offs, at->len ) ); break;
1001 case T_INT: val = wxString::Format( wxS( "%d" ), loadS32( blockStart + at->offs, at->len ) ); break;
1002 case T_DBL: val = wxString::FromCDouble( loadDouble( blockStart + at->offs ) ); break;
1003 case T_STR: val = loadStr( blockStart + at->offs, at->len ); break;
1004 }
1005
1006 node->props[wxString::FromUTF8( at->name )] = val;
1007 }
1008
1009 aNumBlocks--;
1010
1011 for( const SUBSECT* ss = sc->subs; ss->offs != 0; ss++ )
1012 {
1013 uint32_t numch = loadU32( blockStart + ss->offs, ss->len );
1014 EGB_NODE* lpar = node;
1015
1016 if( ss->treeName != nullptr )
1017 lpar = node->AddChild( 0, wxString::FromUTF8( ss->treeName ) );
1018
1019 if( ss->ssType == SS_DIRECT )
1020 {
1021 for( uint32_t n = 0; n < numch && aNumBlocks > 0; n++ )
1022 {
1023 int res = readBlock( aNumBlocks, lpar );
1024 processed += res;
1025 }
1026 }
1027 else
1028 {
1029 if( ss->ssType == SS_RECURSIVE_MINUS_1 && numch > 0 )
1030 numch--;
1031
1032 long rem = numch;
1033
1034 for( uint32_t n = 0; n < numch && rem > 0; n++ )
1035 {
1036 int res = readBlock( rem, lpar );
1037 aNumBlocks -= res;
1038 processed += res;
1039 }
1040 }
1041 }
1042
1043 return processed;
1044}
1045
1046
1048{
1049 m_freeText.clear();
1050 m_freeTextCursor = 0;
1051
1052 if( m_pos + 8 > m_buf->size() )
1053 return false;
1054
1055 // The free-text section starts with the 0x1312 sentinel.
1056 if( ( *m_buf )[m_pos] != 0x13 || ( *m_buf )[m_pos + 1] != 0x12 )
1057 return false;
1058
1059 int textLen = loadS32( m_pos + 4, 2 );
1060 m_pos += 8;
1061
1062 if( textLen < 0 )
1063 return false;
1064
1065 // A trailing 4-byte checksum follows the text payload.
1066 size_t total = static_cast<size_t>( textLen ) + 4;
1067
1068 if( m_pos + total > m_buf->size() )
1069 return false;
1070
1071 // Split the blob into NUL-delimited strings; an empty string terminates.
1072 size_t end = m_pos + total;
1073 size_t cur = m_pos;
1074
1075 while( cur < end && ( *m_buf )[cur] != '\0' )
1076 {
1077 size_t s = cur;
1078
1079 while( cur < end && ( *m_buf )[cur] != '\0' )
1080 cur++;
1081
1082 m_freeText.push_back( wxString::FromUTF8( reinterpret_cast<const char*>( m_buf->data() + s ), cur - s ) );
1083 cur++; // skip the NUL
1084 }
1085
1086 m_pos = end;
1087 return true;
1088}
1089
1090
1092{
1093 if( m_freeTextCursor >= m_freeText.size() )
1094 {
1095 wxLogTrace( traceEagleIo, wxS( "Eagle bin: free-text reference out of strings" ) );
1096 m_invalidText = wxS( "<invalid>" );
1097 return m_invalidText;
1098 }
1099
1100 return m_freeText[m_freeTextCursor++];
1101}
1102
1103
1105{
1106 if( m_pos + 4 > m_buf->size() )
1107 return false;
1108
1109 // DRC start sentinel 0x10 0x04 0x00 0x20.
1110 if( !( ( *m_buf )[m_pos] == 0x10 && ( *m_buf )[m_pos + 1] == 0x04 && ( *m_buf )[m_pos + 2] == 0x00
1111 && ( *m_buf )[m_pos + 3] == 0x20 ) )
1112 {
1113 return false;
1114 }
1115
1116 m_pos += 4;
1117
1118 // Walk the variable-length preamble looking for the 0x12345678 end marker
1119 // that immediately follows a NUL byte.
1120 bool found = false;
1121
1122 while( !found )
1123 {
1124 if( m_pos + 1 > m_buf->size() )
1125 return false;
1126
1127 uint8_t c = ( *m_buf )[m_pos++];
1128
1129 if( c == '\0' )
1130 {
1131 if( m_pos + 4 > m_buf->size() )
1132 return false;
1133
1134 if( ( *m_buf )[m_pos] == 0x78 && ( *m_buf )[m_pos + 1] == 0x56 && ( *m_buf )[m_pos + 2] == 0x34
1135 && ( *m_buf )[m_pos + 3] == 0x12 )
1136 {
1137 found = true;
1138 }
1139
1140 m_pos += 4;
1141 }
1142 }
1143
1144 const size_t kDrcLen = 244;
1145
1146 if( m_pos + kDrcLen > m_buf->size() )
1147 return false;
1148
1149 size_t b = m_pos;
1150
1151 auto mil = [&]( size_t aOffs ) -> long
1152 {
1153 return static_cast<long>( loadS32( b + aOffs, 4 ) / 2.54 / 100 );
1154 };
1155
1156 aDrc.mdWireWire = mil( 0 );
1157 aDrc.msWidth = mil( 64 );
1158 aDrc.rvPadTop = loadDouble( b + 84 );
1159 aDrc.rvPadInner = loadDouble( b + 92 );
1160 aDrc.rvPadBottom = loadDouble( b + 100 );
1161
1162 m_pos += kDrcLen;
1163 return true;
1164}
1165
1166
1167void EAGLE_BIN_PARSER::fixLongText( EGB_NODE* aNode, const wxString& aField )
1168{
1169 auto it = aNode->props.find( aField );
1170
1171 if( it == aNode->props.end() || it->second.IsEmpty() )
1172 return;
1173
1174 // A leading 0x7F byte marks a deferred long-text reference into the notes.
1175 if( static_cast<unsigned char>( it->second[0] ) == 0x7F )
1176 it->second = nextLongText();
1177}
1178
1179
1180void EAGLE_BIN_PARSER::arcDecode( EGB_NODE* aElem, int aArcType, int aLineType )
1181{
1182 auto fixThreeByte = []( long num, bool neg ) -> long
1183 {
1184 if( num < 0 && neg )
1185 return num;
1186 else if( num > 0 && neg )
1187 return num - 0x800000;
1188 else if( num < 0 && !neg )
1189 return num + 0x800000;
1190
1191 return num;
1192 };
1193
1194 auto fixOneByte = []( long num ) -> long
1195 {
1196 return num < 0 ? num + 0x80 : num;
1197 };
1198
1199 auto setLong = [&]( const wxString& aKey, long aVal )
1200 {
1201 aElem->props[aKey] = wxString::Format( wxS( "%ld" ), aVal );
1202 };
1203
1204 // Linear interpolation of the unconstrained center coordinate. The 64-bit
1205 // product cannot overflow (Eagle stores 24-bit coordinates) while a plain
1206 // long would on 32-bit platforms, and integer division truncates toward zero.
1207 auto interpolate = []( int64_t aNumerator, int64_t aSpan, int64_t aDivisor, int64_t aOffset ) -> long
1208 {
1209 return static_cast<long>( aNumerator * aSpan / aDivisor + aOffset );
1210 };
1211
1212 if( aLineType == 129 || aArcType == 0 )
1213 {
1214 long arcFlags = aElem->PropLong( wxS( "arc_negflags" ) );
1215 long x1 = fixThreeByte( aElem->PropLong( wxS( "arc_x1" ) ), arcFlags & 0x02 );
1216 long y1 = fixThreeByte( aElem->PropLong( wxS( "arc_y1" ) ), arcFlags & 0x04 );
1217 long x2 = fixThreeByte( aElem->PropLong( wxS( "arc_x2" ) ), arcFlags & 0x08 );
1218 long y2 = fixThreeByte( aElem->PropLong( wxS( "arc_y2" ) ), arcFlags & 0x10 );
1219
1220 long c = fixOneByte( aElem->PropLong( wxS( "arc_c1" ) ) )
1221 + 256 * fixOneByte( aElem->PropLong( wxS( "arc_c2" ) ) )
1222 + 256L * 256 * fixOneByte( aElem->PropLong( wxS( "arc_c3" ) ) );
1223 c = fixThreeByte( c, arcFlags & 0x01 );
1224
1225 setLong( wxS( "x1" ), x1 );
1226 setLong( wxS( "y1" ), y1 );
1227 setLong( wxS( "x2" ), x2 );
1228 setLong( wxS( "y2" ), y2 );
1229
1230 long x3 = ( x1 + x2 ) / 2;
1231 long y3 = ( y1 + y2 ) / 2;
1232 long cx = 0, cy = 0;
1233
1234 if( x1 == x2 && y1 == y2 )
1235 {
1236 // Degenerate arc with coincident endpoints; both interpolation
1237 // branches would divide by zero, so collapse it to a point.
1238 cx = x1;
1239 cy = y1;
1240 }
1241 else if( std::abs( x2 - x1 ) < std::abs( y2 - y1 ) )
1242 {
1243 cx = c;
1244 cy = interpolate( x3 - cx, x2 - x1, y2 - y1, y3 );
1245 }
1246 else
1247 {
1248 cy = c;
1249 cx = interpolate( y3 - cy, y2 - y1, x2 - x1, x3 );
1250 }
1251
1252 long radius = static_cast<long>( std::hypot( cx - x2, cy - y2 ) );
1253 setLong( wxS( "radius" ), radius );
1254 setLong( wxS( "x" ), cx );
1255 setLong( wxS( "y" ), cy );
1256
1257 if( cx == x2 && cy == y1 && x2 < x1 && y2 > y1 )
1258 {
1259 aElem->props[wxS( "StartAngle" )] = wxS( "90" );
1260 aElem->props[wxS( "Delta" )] = wxS( "90" );
1261 }
1262 else if( cx == x1 && cy == y2 && x2 < x1 && y1 > y2 )
1263 {
1264 aElem->props[wxS( "StartAngle" )] = wxS( "0" );
1265 aElem->props[wxS( "Delta" )] = wxS( "90" );
1266 }
1267 else if( cx == x2 && cy == y1 && x2 > x1 && y1 > y2 )
1268 {
1269 aElem->props[wxS( "StartAngle" )] = wxS( "270" );
1270 aElem->props[wxS( "Delta" )] = wxS( "90" );
1271 }
1272 else if( cx == x1 && cy == y2 && x2 > x1 && y2 > y1 )
1273 {
1274 aElem->props[wxS( "StartAngle" )] = wxS( "180" );
1275 aElem->props[wxS( "Delta" )] = wxS( "90" );
1276 }
1277 else
1278 {
1279 double theta1 = 180.0 - 180.0 / M_PI * atan2( cy - y1, x1 - cx );
1280 double theta2 = 180.0 - 180.0 / M_PI * atan2( cy - y2, x2 - cx );
1281 double deltaTheta = theta2 - theta1;
1282
1283 while( theta1 > 360 )
1284 theta1 -= 360;
1285
1286 while( deltaTheta < -180 )
1287 deltaTheta += 360;
1288
1289 while( deltaTheta > 180 )
1290 deltaTheta -= 360;
1291
1292 setLong( wxS( "StartAngle" ), static_cast<long>( theta1 ) );
1293 setLong( wxS( "Delta" ), static_cast<long>( deltaTheta ) );
1294 }
1295 }
1296 else if( ( aLineType > 0 && aLineType < 129 ) || aArcType > 0 )
1297 {
1298 long x1 = 0, y1 = 0, x2 = 0, y2 = 0, cx = 0, cy = 0;
1299
1300 if( aElem->HasProp( wxS( "arctype_other_x1" ) ) )
1301 {
1302 x1 = aElem->PropLong( wxS( "arctype_other_x1" ) );
1303 y1 = aElem->PropLong( wxS( "arctype_other_y1" ) );
1304 x2 = aElem->PropLong( wxS( "arctype_other_x2" ) );
1305 y2 = aElem->PropLong( wxS( "arctype_other_y2" ) );
1306 }
1307 else
1308 {
1309 x1 = aElem->PropLong( wxS( "linetype_0_x1" ) );
1310 y1 = aElem->PropLong( wxS( "linetype_0_y1" ) );
1311 x2 = aElem->PropLong( wxS( "linetype_0_x2" ) );
1312 y2 = aElem->PropLong( wxS( "linetype_0_y2" ) );
1313 }
1314
1315 bool cxyOk = true;
1316 auto setAngles = [&]( const char* aStart, const char* aDelta )
1317 {
1318 aElem->props[wxS( "StartAngle" )] = wxString::FromUTF8( aStart );
1319 aElem->props[wxS( "Delta" )] = wxString::FromUTF8( aDelta );
1320 };
1321
1322 if( aLineType == 0x78 || aArcType == 0x01 )
1323 {
1324 cx = std::min( x1, x2 );
1325 cy = std::min( y1, y2 );
1326 setAngles( "180", "90" );
1327 }
1328 else if( aLineType == 0x79 || aArcType == 0x02 )
1329 {
1330 cx = std::max( x1, x2 );
1331 cy = std::min( y1, y2 );
1332 setAngles( "270", "90" );
1333 }
1334 else if( aLineType == 0x7a || aArcType == 0x03 )
1335 {
1336 cx = std::max( x1, x2 );
1337 cy = std::max( y1, y2 );
1338 setAngles( "0", "90" );
1339 }
1340 else if( aLineType == 0x7b || aArcType == 0x04 )
1341 {
1342 cx = std::min( x1, x2 );
1343 cy = std::max( y1, y2 );
1344 setAngles( "90", "90" );
1345 }
1346 else if( aLineType == 0x7c || aArcType == 0x05 )
1347 {
1348 cx = ( x1 + x2 ) / 2;
1349 cy = ( y1 + y2 ) / 2;
1350 setAngles( "90", "180" );
1351 }
1352 else if( aLineType == 0x7d || aArcType == 0x06 )
1353 {
1354 cx = ( x1 + x2 ) / 2;
1355 cy = ( y1 + y2 ) / 2;
1356 setAngles( "270", "180" );
1357 }
1358 else if( aLineType == 0x7e || aArcType == 0x07 )
1359 {
1360 cx = ( x1 + x2 ) / 2;
1361 cy = ( y1 + y2 ) / 2;
1362 setAngles( "180", "180" );
1363 }
1364 else if( aLineType == 0x7f || aArcType == 0x08 )
1365 {
1366 cx = ( x1 + x2 ) / 2;
1367 cy = ( y1 + y2 ) / 2;
1368 setAngles( "0", "180" );
1369 }
1370 else
1371 {
1372 cxyOk = false;
1373 }
1374
1375 if( !cxyOk )
1376 cx = cy = 0;
1377
1378 long radius = static_cast<long>( std::hypot( cx - x2, cy - y2 ) );
1379 setLong( wxS( "radius" ), radius );
1380 setLong( wxS( "x" ), cx );
1381 setLong( wxS( "y" ), cy );
1382 }
1383}
1384
1385
1387{
1388 if( aRoot->id == EGKW_SECT_LINE )
1389 {
1390 int lineType = aRoot->HasProp( wxS( "linetype" ) ) ? (int) aRoot->PropLong( wxS( "linetype" ) ) : -1;
1391
1392 if( lineType >= 0 )
1393 {
1394 // Straight and arc wires both keep their endpoints in the linetype_0
1395 // fields.
1396 aRoot->props[wxS( "x1" )] = aRoot->Prop( wxS( "linetype_0_x1" ) );
1397 aRoot->props[wxS( "y1" )] = aRoot->Prop( wxS( "linetype_0_y1" ) );
1398 aRoot->props[wxS( "x2" )] = aRoot->Prop( wxS( "linetype_0_x2" ) );
1399 aRoot->props[wxS( "y2" )] = aRoot->Prop( wxS( "linetype_0_y2" ) );
1400 aRoot->props[wxS( "width" )] = aRoot->PropDoubled( wxS( "half_width" ) );
1401 }
1402
1403 if( lineType > 0 )
1404 {
1405 // arcDecode adds the center, radius and swept angle. KiCad's wire
1406 // reader needs the endpoints plus a "curve" (the swept angle).
1407 arcDecode( aRoot, -1, lineType );
1408
1409 if( aRoot->HasProp( wxS( "Delta" ) ) )
1410 aRoot->props[wxS( "curve" )] = aRoot->Prop( wxS( "Delta" ) );
1411 }
1412 }
1413
1414 for( const auto& child : aRoot->children )
1415 postprocWires( child.get() );
1416}
1417
1418
1420{
1421 if( aRoot->id == EGKW_SECT_ARC )
1422 {
1423 int arcType = aRoot->HasProp( wxS( "arctype" ) ) ? (int) aRoot->PropLong( wxS( "arctype" ) ) : -1;
1424
1425 if( arcType == 0 )
1426 {
1427 aRoot->props[wxS( "x1" )] = aRoot->Prop( wxS( "arc_x1" ) );
1428 aRoot->props[wxS( "y1" )] = aRoot->Prop( wxS( "arc_y1" ) );
1429 aRoot->props[wxS( "x2" )] = aRoot->Prop( wxS( "arc_x2" ) );
1430 aRoot->props[wxS( "y2" )] = aRoot->Prop( wxS( "arc_y2" ) );
1431 }
1432 else if( arcType > 0 )
1433 {
1434 aRoot->props[wxS( "x1" )] = aRoot->Prop( wxS( "arctype_other_x1" ) );
1435 aRoot->props[wxS( "y1" )] = aRoot->Prop( wxS( "arctype_other_y1" ) );
1436 aRoot->props[wxS( "x2" )] = aRoot->Prop( wxS( "arctype_other_x2" ) );
1437 aRoot->props[wxS( "y2" )] = aRoot->Prop( wxS( "arctype_other_y2" ) );
1438 }
1439
1440 if( arcType >= 0 )
1441 aRoot->props[wxS( "width" )] = aRoot->PropDoubled( wxS( "half_width" ) );
1442
1443 arcDecode( aRoot, arcType, -1 );
1444
1445 if( aRoot->HasProp( wxS( "Delta" ) ) )
1446 aRoot->props[wxS( "curve" )] = aRoot->Prop( wxS( "Delta" ) );
1447 }
1448
1449 for( const auto& child : aRoot->children )
1450 postprocArcs( child.get() );
1451}
1452
1453
1455{
1456 if( aRoot->id == EGKW_SECT_VIA )
1457 {
1458 // KiCad requires an "extent" layer-range string. The binary layers byte
1459 // is not a 1:1 map and pre-v6 vias are through-hole (0xF0 sentinel), so
1460 // span the full copper stack. Blind/buried vias are out of scope.
1461 aRoot->props[wxS( "extent" )] = wxS( "1-16" );
1462 }
1463
1464 for( const auto& child : aRoot->children )
1465 postprocVias( child.get() );
1466}
1467
1468
1470{
1471 // Binary coordinates are decimicrons (0.1 um); KiCad's XML reader assumes
1472 // millimetres for unitless values. Rewrite every dimensional attribute as a
1473 // millimetre decimal so the reader scales it correctly. Counts, layer
1474 // numbers, ratios, angles and booleans are left untouched.
1475 static const wxString dimAttrs[] = { wxS( "x" ), wxS( "y" ), wxS( "x1" ), wxS( "y1" ),
1476 wxS( "x2" ), wxS( "y2" ), wxS( "x3" ), wxS( "y3" ),
1477 wxS( "width" ), wxS( "drill" ), wxS( "diameter" ), wxS( "radius" ),
1478 wxS( "size" ), wxS( "dx" ), wxS( "dy" ), wxS( "spacing" ),
1479 wxS( "isolate" ) };
1480
1481 for( const wxString& key : dimAttrs )
1482 {
1483 auto it = aRoot->props.find( key );
1484
1485 if( it == aRoot->props.end() )
1486 continue;
1487
1488 double du = 0;
1489
1490 if( it->second.ToCDouble( &du ) )
1491 it->second = wxString::FromCDouble( du * 0.0001, 4 );
1492 }
1493
1494 for( const auto& child : aRoot->children )
1495 postprocUnits( child.get() );
1496}
1497
1498
1500{
1501 if( aRoot->id == EGKW_SECT_CIRCLE && aRoot->HasProp( wxS( "half_width" ) ) )
1502 {
1503 aRoot->props[wxS( "width" )] = aRoot->PropDoubled( wxS( "half_width" ) );
1504 }
1505
1506 for( const auto& child : aRoot->children )
1507 postprocCircles( child.get() );
1508}
1509
1510
1512{
1513 if( aRoot->id == EGKW_SECT_SMD )
1514 {
1515 if( aRoot->HasProp( wxS( "half_dx" ) ) )
1516 {
1517 aRoot->props[wxS( "dx" )] = aRoot->PropDoubled( wxS( "half_dx" ) );
1518 }
1519
1520 if( aRoot->HasProp( wxS( "half_dy" ) ) )
1521 {
1522 aRoot->props[wxS( "dy" )] = aRoot->PropDoubled( wxS( "half_dy" ) );
1523 }
1524 }
1525
1526 for( const auto& child : aRoot->children )
1527 postprocSmd( child.get() );
1528}
1529
1530
1532{
1533 if( aRoot->id == EGKW_SECT_PAD || aRoot->id == EGKW_SECT_HOLE || aRoot->id == EGKW_SECT_VIA
1534 || aRoot->id == EGKW_SECT_TEXT )
1535 {
1536 if( aRoot->HasProp( wxS( "half_drill" ) ) )
1537 {
1538 aRoot->props[wxS( "drill" )] = aRoot->PropDoubled( wxS( "half_drill" ) );
1539 }
1540
1541 if( aRoot->HasProp( wxS( "half_diameter" ) ) )
1542 {
1543 aRoot->props[wxS( "diameter" )] = aRoot->PropDoubled( wxS( "half_diameter" ) );
1544 }
1545
1546 if( aRoot->HasProp( wxS( "half_size" ) ) )
1547 {
1548 aRoot->props[wxS( "size" )] = aRoot->PropDoubled( wxS( "half_size" ) );
1549 }
1550 }
1551
1552 for( const auto& child : aRoot->children )
1553 postprocDimensions( child.get() );
1554}
1555
1556
1558{
1559 switch( aId )
1560 {
1561 case EGKW_SECT_SMD:
1562 case EGKW_SECT_PIN:
1564 case EGKW_SECT_PAD:
1565 case EGKW_SECT_TEXT:
1573 case EGKW_SECT_INSTANCE:
1574 case EGKW_SECT_ELEMENT: return true;
1575 default: return false;
1576 }
1577}
1578
1579
1581{
1582 if( isRotatable( aRoot->id ) && aRoot->HasProp( wxS( "bin_rot" ) ) )
1583 {
1584 // mirrored/spin are read as T_BMB ("yes"/"no") or as a T_UBF integer
1585 // depending on the record, so treat anything other than the false tokens
1586 // as set.
1587 auto flagSet = [&]( const wxString& aKey )
1588 {
1589 if( !aRoot->HasProp( aKey ) )
1590 return false;
1591
1592 const wxString v = aRoot->Prop( aKey );
1593 return v != wxS( "no" ) && v != wxS( "0" );
1594 };
1595
1596 bool mirrored = flagSet( wxS( "mirrored" ) );
1597 bool spin = flagSet( wxS( "spin" ) );
1598
1599 long deg = aRoot->PropLong( wxS( "bin_rot" ) );
1600 wxString rot;
1601
1602 if( spin )
1603 rot << wxS( "S" );
1604
1605 if( mirrored )
1606 rot << wxS( "M" );
1607
1608 rot << wxS( "R" );
1609
1610 if( deg >= 1024 )
1611 rot << wxString::Format( wxS( "%ld" ), ( 360 * deg ) / 4096 ); // v4/v5
1612 else if( deg > 0 )
1613 rot << wxString::Format( wxS( "%ld" ), ( deg & 0x00f0 ) * 90 ); // v3
1614 else
1615 rot << wxS( "0" );
1616
1617 aRoot->props[wxS( "rot" )] = rot;
1618 }
1619
1620 for( const auto& child : aRoot->children )
1621 postprocRotation( child.get() );
1622}
1623
1624
1626{
1627 switch( aRoot->id )
1628 {
1629 case EGKW_SECT_TEXT:
1636 case EGKW_SECT_SMASHEDXREF: fixLongText( aRoot, wxS( "textfield" ) ); break;
1637
1638 case EGKW_SECT_LAYER:
1639 case EGKW_SECT_LIBRARY:
1640 case EGKW_SECT_SIGNAL:
1641 case EGKW_SECT_SYMBOL:
1643 case EGKW_SECT_PAD:
1644 case EGKW_SECT_SMD:
1645 case EGKW_SECT_PIN:
1646 case EGKW_SECT_GATE: fixLongText( aRoot, wxS( "name" ) ); break;
1647
1648 case EGKW_SECT_ELEMENT2:
1649 case EGKW_SECT_PART:
1650 fixLongText( aRoot, wxS( "name" ) );
1651 fixLongText( aRoot, wxS( "value" ) );
1652 break;
1653
1654 case EGKW_SECT_DEVICES:
1655 case EGKW_SECT_SYMBOLS: fixLongText( aRoot, wxS( "library" ) ); break;
1656
1658 fixLongText( aRoot, wxS( "table" ) );
1659 fixLongText( aRoot, wxS( "name" ) );
1660 break;
1661
1662 case EGKW_SECT_PACKAGE:
1663 fixLongText( aRoot, wxS( "desc" ) );
1664 fixLongText( aRoot, wxS( "name" ) );
1665 break;
1666
1667 case EGKW_SECT_PACKAGES:
1668 fixLongText( aRoot, wxS( "desc" ) );
1669 fixLongText( aRoot, wxS( "library" ) );
1670 break;
1671
1672 case EGKW_SECT_SCHEMA: fixLongText( aRoot, wxS( "xref_format" ) ); break;
1673
1675 fixLongText( aRoot, wxS( "symbol" ) );
1676 fixLongText( aRoot, wxS( "attribute" ) );
1677 break;
1678
1679 case EGKW_SECT_DEVICE:
1680 fixLongText( aRoot, wxS( "prefix" ) );
1681 fixLongText( aRoot, wxS( "desc" ) );
1682 fixLongText( aRoot, wxS( "name" ) );
1683 break;
1684
1685 default: break;
1686 }
1687
1688 for( const auto& child : aRoot->children )
1689 postprocFreeText( child.get() );
1690}
1691
1692
1694{
1695 // Move every drawing/layer under the synthetic drawing/layers node, keeping
1696 // order. Reparenting is by ownership transfer.
1697 std::vector<std::unique_ptr<EGB_NODE>> kept;
1698
1699 for( auto& child : aDrawing->children )
1700 {
1701 if( child->id == EGKW_SECT_LAYER )
1702 {
1703 // The binary stores "visible" as a 2-bit field, but KiCad's reader
1704 // parses it as a yes/no bool. Normalize to a truthy token.
1705 if( child->HasProp( wxS( "visible" ) ) )
1706 {
1707 child->props[wxS( "visible" )] = child->PropLong( wxS( "visible" ) ) != 0 ? wxS( "yes" ) : wxS( "no" );
1708 }
1709
1710 child->parent = aLayers;
1711 aLayers->children.push_back( std::move( child ) );
1712 }
1713 else
1714 {
1715 kept.push_back( std::move( child ) );
1716 }
1717 }
1718
1719 aDrawing->children = std::move( kept );
1720}
1721
1722
1723void EAGLE_BIN_PARSER::postprocDrc( EGB_NODE* aDrcNode, const DRC_CTX& aDrc )
1724{
1725 auto addParam = [&]( const wxString& aName, const wxString& aValue )
1726 {
1727 EGB_NODE* p = aDrcNode->AddChild( EGKW_SECT_DRC, wxS( "param" ) );
1728 p->props[wxS( "name" )] = aName;
1729 p->props[wxS( "value" )] = aValue;
1730 };
1731
1732 addParam( wxS( "mdWireWire" ), wxString::Format( wxS( "%ldmil" ), aDrc.mdWireWire ) );
1733 addParam( wxS( "msWidth" ), wxString::Format( wxS( "%ldmil" ), aDrc.msWidth ) );
1734 addParam( wxS( "rvPadTop" ), wxString::FromCDouble( aDrc.rvPadTop ) );
1735 addParam( wxS( "rvPadInner" ), wxString::FromCDouble( aDrc.rvPadInner ) );
1736 addParam( wxS( "rvPadBottom" ), wxString::FromCDouble( aDrc.rvPadBottom ) );
1737}
1738
1739
1741{
1742 // In a board, the libraries node holds packages nodes directly; the XML
1743 // schema expects each wrapped in a library node. Wrap every bare packages
1744 // child.
1745 if( aLibraries == nullptr )
1746 return;
1747
1748 if( aLibraries->FindChildById( EGKW_SECT_LIBRARY ) != nullptr )
1749 return; // already a proper library subtree
1750
1751 std::vector<std::unique_ptr<EGB_NODE>> wrapped;
1752
1753 for( auto& child : aLibraries->children )
1754 {
1755 if( child->id != EGKW_SECT_PACKAGES )
1756 continue;
1757
1758 auto lib = std::make_unique<EGB_NODE>();
1759 lib->id = EGKW_SECT_LIBRARY;
1760 lib->name = wxS( "library" );
1761 lib->parent = aLibraries;
1762 child->parent = lib.get();
1763 lib->children.push_back( std::move( child ) );
1764 wrapped.push_back( std::move( lib ) );
1765 }
1766
1767 if( !wrapped.empty() )
1768 aLibraries->children = std::move( wrapped );
1769}
1770
1771
1773{
1774 if( aElements == nullptr )
1775 return;
1776
1777 // Each element is followed (as a child) by an element2 record carrying its
1778 // name and value; merge those up onto the element.
1779 for( auto& el : aElements->children )
1780 {
1781 if( el->children.empty() || el->children.front()->id != EGKW_SECT_ELEMENT2 )
1782 continue;
1783
1784 for( auto& el2 : el->children )
1785 {
1786 if( el2->id != EGKW_SECT_ELEMENT2 )
1787 continue;
1788
1789 for( const auto& [key, value] : el2->props )
1790 {
1791 if( key == wxS( "name" ) )
1792 {
1793 if( value == wxS( "-" ) )
1794 el->props[wxS( "name" )] = wxS( "HYPHEN" );
1795 else
1796 el->props[wxS( "name" )] = value;
1797 }
1798 else if( key == wxS( "value" ) )
1799 {
1800 el->props[wxS( "value" )] = value;
1801 }
1802 }
1803 }
1804 }
1805}
1806
1807
1809{
1810 if( aLibraries == nullptr )
1811 return;
1812
1813 // The binary references libraries and packages by 1-based ordinal, but the
1814 // XML schema (and KiCad's reader) resolve them by name. Give every library
1815 // and package a unique non-empty name, then rewrite each element's numeric
1816 // library/package references to those names so footprint lookup succeeds.
1817
1818 for( size_t li = 0; li < aLibraries->children.size(); li++ )
1819 {
1820 EGB_NODE* lib = aLibraries->children[li].get();
1821
1822 if( lib->id != EGKW_SECT_LIBRARY )
1823 continue;
1824
1826
1827 // The library name is carried on the inner packages node; fall back to
1828 // the ordinal when it is blank.
1829 wxString libName = pkgs ? pkgs->Prop( wxS( "library" ) ) : wxString();
1830
1831 if( libName.IsEmpty() )
1832 libName = wxString::Format( wxS( "lib%zu" ), li + 1 );
1833
1834 lib->props[wxS( "name" )] = libName;
1835
1836 if( pkgs == nullptr )
1837 continue;
1838
1839 std::map<wxString, int> seen;
1840
1841 for( size_t pi = 0; pi < pkgs->children.size(); pi++ )
1842 {
1843 EGB_NODE* pkg = pkgs->children[pi].get();
1844 wxString name = pkg->Prop( wxS( "name" ) );
1845
1846 if( name.IsEmpty() )
1847 name = wxString::Format( wxS( "pkg%zu" ), pi + 1 );
1848
1849 // Disambiguate repeated names so the per-library package map stays
1850 // unique.
1851 if( int& count = seen[name]; count++ > 0 )
1852 name = wxString::Format( wxS( "%s_%d" ), name, count );
1853
1854 pkg->props[wxS( "name" )] = name;
1855 }
1856 }
1857
1858 if( aElements == nullptr )
1859 return;
1860
1861 auto nameByIdx = [&]( EGB_NODE* aParent, long aIdx ) -> wxString
1862 {
1863 if( aParent == nullptr || aIdx < 1 || aIdx > (long) aParent->children.size() )
1864 return wxString();
1865
1866 return aParent->children[aIdx - 1]->Prop( wxS( "name" ) );
1867 };
1868
1869 for( auto& el : aElements->children )
1870 {
1871 if( el->id != EGKW_SECT_ELEMENT )
1872 continue;
1873
1874 long libIdx = el->PropLong( wxS( "library" ) );
1875 EGB_NODE* lib = ( libIdx >= 1 && libIdx <= (long) aLibraries->children.size() )
1876 ? aLibraries->children[libIdx - 1].get()
1877 : nullptr;
1878
1879 if( lib == nullptr )
1880 continue;
1881
1882 el->props[wxS( "library" )] = lib->Prop( wxS( "name" ) );
1883
1885 wxString pkgName = nameByIdx( pkgs, el->PropLong( wxS( "package" ) ) );
1886
1887 if( !pkgName.IsEmpty() )
1888 el->props[wxS( "package" )] = pkgName;
1889 }
1890}
1891
1892
1894{
1895 if( aSignals == nullptr )
1896 return;
1897
1898 // Flatten any nested signal so every signal sits directly under signals.
1899 // Connectivity of nested nets is not preserved, matching Eagle's own
1900 // binary-to-XML conversion. Iterate by index because nested signals are
1901 // appended to the same vector and must themselves be flattened, which
1902 // handles three or more levels of nesting.
1903 for( size_t i = 0; i < aSignals->children.size(); i++ )
1904 {
1905 EGB_NODE* sig = aSignals->children[i].get();
1906
1907 if( sig->id != EGKW_SECT_SIGNAL )
1908 continue;
1909
1910 std::vector<std::unique_ptr<EGB_NODE>> kept;
1911 std::vector<std::unique_ptr<EGB_NODE>> promoted;
1912
1913 for( auto& inner : sig->children )
1914 {
1915 if( inner->id == EGKW_SECT_SIGNAL )
1916 {
1917 inner->parent = aSignals;
1918 promoted.push_back( std::move( inner ) );
1919 }
1920 else
1921 {
1922 kept.push_back( std::move( inner ) );
1923 }
1924 }
1925
1926 sig->children = std::move( kept );
1927
1928 // Append after rebuilding sig->children; this may reallocate, but sig
1929 // is only dereferenced above and i indexes the (stable) container.
1930 for( auto& p : promoted )
1931 aSignals->children.push_back( std::move( p ) );
1932 }
1933}
1934
1935
1936void EAGLE_BIN_PARSER::postprocContactRefs( EGB_NODE* aSignals, EGB_NODE* aElements, EGB_NODE* aLibraries )
1937{
1938 if( aSignals == nullptr || aElements == nullptr || aLibraries == nullptr )
1939 return;
1940
1941 auto elemByIdx = [&]( long aIdx ) -> EGB_NODE*
1942 {
1943 if( aIdx < 1 || aIdx > (long) aElements->children.size() )
1944 return nullptr;
1945
1946 return aElements->children[aIdx - 1].get();
1947 };
1948
1949 auto libByIdx = [&]( long aIdx ) -> EGB_NODE*
1950 {
1951 if( aIdx < 1 || aIdx > (long) aLibraries->children.size() )
1952 return nullptr;
1953
1954 return aLibraries->children[aIdx - 1].get();
1955 };
1956
1957 auto pkgByIdx = [&]( EGB_NODE* aLib, long aIdx ) -> EGB_NODE*
1958 {
1959 if( aLib == nullptr )
1960 return nullptr;
1961
1962 EGB_NODE* pkgs = aLib->FindChildById( EGKW_SECT_PACKAGES );
1963
1964 if( pkgs == nullptr || aIdx < 1 || aIdx > (long) pkgs->children.size() )
1965 return nullptr;
1966
1967 return pkgs->children[aIdx - 1].get();
1968 };
1969
1970 for( auto& sig : aSignals->children )
1971 {
1972 // Resolve every contactref, regardless of sibling order; wires or
1973 // polygons may precede the contactrefs within a signal.
1974 for( auto& cr : sig->children )
1975 {
1976 if( cr->id != EGKW_SECT_CONTACTREF )
1977 continue;
1978
1979 long partNum = cr->PropLong( wxS( "partnumber" ) );
1980 EGB_NODE* elem = elemByIdx( partNum );
1981
1982 if( elem == nullptr )
1983 continue;
1984
1985 cr->props[wxS( "element" )] = elem->Prop( wxS( "name" ) );
1986
1987 // Resolve the pad name by walking the package pads/pins/smd in order.
1988 EGB_NODE* lib = libByIdx( elem->PropLong( wxS( "library" ) ) );
1989 EGB_NODE* pkg = pkgByIdx( lib, elem->PropLong( wxS( "package" ) ) );
1990
1991 if( pkg == nullptr )
1992 {
1993 cr->props[wxS( "pad" )] = wxS( "PIN_NOT_FOUND" );
1994 continue;
1995 }
1996
1997 long pinNum = cr->PropLong( wxS( "pin" ) );
1998 EGB_NODE* found = nullptr;
1999
2000 for( const auto& child : pkg->children )
2001 {
2002 int kind = child->id & 0xFF00;
2003
2004 if( kind == EGKW_SECT_PAD || kind == EGKW_SECT_SMD || kind == EGKW_SECT_PIN )
2005 {
2006 if( --pinNum < 1 )
2007 {
2008 found = child.get();
2009 break;
2010 }
2011 }
2012 }
2013
2014 if( found == nullptr )
2015 cr->props[wxS( "pad" )] = wxS( "PIN_NOT_FOUND" );
2016 else if( found->HasProp( wxS( "name" ) ) )
2017 cr->props[wxS( "pad" )] = found->Prop( wxS( "name" ) );
2018 else
2019 cr->props[wxS( "pad" )] = cr->Prop( wxS( "pin" ) );
2020 }
2021 }
2022}
2023
2024
2026{
2027 EGB_NODE* drawing = aRoot->children.empty() ? nullptr : aRoot->children.front().get();
2028
2029 if( drawing == nullptr )
2030 THROW_IO_ERROR( _( "Eagle binary file has no drawing section." ) );
2031
2032 // KiCad's XML reader resolves the layer map from drawing/layers, so the
2033 // synthetic node must live under drawing, not the eagle root.
2034 EGB_NODE* layers = drawing->AddChild( EGKW_SECT_LAYERS, wxS( "layers" ) );
2035
2036 EGB_NODE* board = drawing->FindChildById( EGKW_SECT_BOARD );
2037 EGB_NODE* drcNode = nullptr;
2038 EGB_NODE* libraries = nullptr;
2039 EGB_NODE* signals = nullptr;
2040 EGB_NODE* elements = nullptr;
2041
2042 if( board != nullptr )
2043 {
2044 drcNode = board->AddChild( EGKW_SECT_DRC, wxS( "designrules" ) );
2045 libraries = board->FindChildByName( wxS( "libraries" ) );
2046
2047 if( libraries == nullptr )
2048 THROW_IO_ERROR( _( "Eagle binary layout is missing a board/libraries node." ) );
2049
2050 signals = board->FindChildByName( wxS( "signals" ) );
2051 elements = board->FindChildByName( wxS( "elements" ) );
2052 }
2053
2054 postprocLayers( drawing, layers );
2055
2056 if( drcNode != nullptr )
2057 postprocDrc( drcNode, aDrc );
2058
2059 postprocLibs( libraries );
2060 postprocElements( elements );
2061 postprocSignals( signals );
2062
2063 postprocWires( aRoot );
2064 postprocArcs( aRoot );
2065 postprocVias( aRoot );
2066 postprocCircles( aRoot );
2067 postprocSmd( aRoot );
2068 postprocDimensions( aRoot );
2069
2070 // Resolve long-text names before contactrefs copy element and pad names,
2071 // and before postprocNames disambiguates package names.
2072 postprocFreeText( aRoot );
2073
2074 // postprocContactRefs reads element library/package ordinals, so it must run
2075 // before postprocNames rewrites those ordinals into names.
2076 postprocContactRefs( signals, elements, libraries );
2077 postprocNames( libraries, elements );
2078
2079 postprocRotation( aRoot );
2080
2081 // Must run last so every dimensional attribute has its final value before
2082 // the decimicron-to-millimetre rewrite.
2083 postprocUnits( aRoot );
2084}
2085
2086
2087wxXmlNode* EAGLE_BIN_PARSER::toXml( const EGB_NODE* aNode ) const
2088{
2089 wxXmlNode* xml = new wxXmlNode( wxXML_ELEMENT_NODE, aNode->name );
2090
2091 for( const auto& [key, value] : aNode->props )
2092 xml->AddAttribute( key, value );
2093
2094 // wxXmlNode::AddChild appends to the end of the sibling chain, preserving
2095 // document order.
2096 for( const auto& child : aNode->children )
2097 xml->AddChild( toXml( child.get() ) );
2098
2099 return xml;
2100}
2101
2102
2103std::unique_ptr<wxXmlDocument> EAGLE_BIN_PARSER::Parse( const std::vector<uint8_t>& aBytes )
2104{
2105 m_buf = &aBytes;
2106 m_pos = 0;
2107
2108 if( aBytes.size() < 24 )
2109 THROW_IO_ERROR( _( "File is too small to be an Eagle binary board." ) );
2110
2111 m_root = std::make_unique<EGB_NODE>();
2112 m_root->id = 0;
2113 m_root->name = wxS( "eagle" );
2114
2115 long numBlocks = -1;
2116 readBlock( numBlocks, m_root.get() );
2117
2118 DRC_CTX drc;
2119
2120 // The trailing notes and DRC sections are present only in v4/v5; missing
2121 // sections are tolerated and fall back to defaults.
2122 readNotes();
2123 readDrc( drc );
2124
2125 postProcess( m_root.get(), drc );
2126
2127 auto doc = std::make_unique<wxXmlDocument>();
2128 doc->SetRoot( toXml( m_root.get() ) );
2129
2130 m_buf = nullptr;
2131 return doc;
2132}
const char * name
wxXmlNode * toXml(const EGB_NODE *aNode) const
void postprocSignals(EGB_NODE *aSignals)
std::unique_ptr< wxXmlDocument > Parse(const std::vector< uint8_t > &aBytes)
Parse a binary Eagle board into an XML DOM compatible with the XML walker.
void requireBytes(size_t aOffs, size_t aLen) const
uint32_t loadUbf(size_t aOffs, uint32_t aField) const
void postprocCircles(EGB_NODE *aRoot)
bool readDrc(DRC_CTX &aDrc)
void postprocFreeText(EGB_NODE *aRoot)
bool isRotatable(int aId) const
void postprocLibs(EGB_NODE *aLibraries)
const wxString & nextLongText()
uint32_t loadU32(size_t aOffs, unsigned aLen) const
size_t m_pos
current read cursor
void postprocUnits(EGB_NODE *aRoot)
void postprocArcs(EGB_NODE *aRoot)
wxString loadStr(size_t aOffs, unsigned aLen) const
void fixLongText(EGB_NODE *aNode, const wxString &aField)
void postProcess(EGB_NODE *aRoot, const DRC_CTX &aDrc)
int readBlock(long &aNumBlocks, EGB_NODE *aParent)
void postprocSmd(EGB_NODE *aRoot)
const std::vector< uint8_t > * m_buf
file contents, not owned
void postprocContactRefs(EGB_NODE *aSignals, EGB_NODE *aElements, EGB_NODE *aLibraries)
void postprocWires(EGB_NODE *aRoot)
std::vector< wxString > m_freeText
NUL-delimited notes strings.
void postprocLayers(EGB_NODE *aDrawing, EGB_NODE *aLayers)
bool loadBmb(size_t aOffs, uint32_t aMask) const
void postprocRotation(EGB_NODE *aRoot)
void arcDecode(EGB_NODE *aElem, int aArcType, int aLineType)
double loadDouble(size_t aOffs) const
static bool IsBinaryEagle(wxInputStream &aStream)
Probe the first two bytes for the binary magic.
void postprocDimensions(EGB_NODE *aRoot)
std::unique_ptr< EGB_NODE > m_root
wxString m_invalidText
returned when out of strings
void postprocDrc(EGB_NODE *aDrcNode, const DRC_CTX &aDrc)
void postprocVias(EGB_NODE *aRoot)
void postprocNames(EGB_NODE *aLibraries, EGB_NODE *aElements)
void postprocElements(EGB_NODE *aElements)
int32_t loadS32(size_t aOffs, unsigned aLen) const
#define TERM_A
#define TERM_S
#define TERM_F
@ EGKW_SECT_ARC
@ EGKW_SECT_FRAME
@ EGKW_SECT_DRC
@ EGKW_SECT_LAYER
@ EGKW_SECT_PACKAGES
@ EGKW_SECT_SMASHEDVALUE
@ EGKW_SECT_TEXT
@ EGKW_SECT_PAD
@ EGKW_SECT_GATE
@ EGKW_SECT_DEVICES
@ EGKW_SECT_SMASHEDXREF
@ EGKW_SECT_RECTANGLE
@ EGKW_SECT_SCHEMASHEET
@ EGKW_SECT_PATH
@ EGKW_SECT_CONTACTREF
@ EGKW_SECT_POLYGON
@ EGKW_SECT_PACKAGEVARIANT
@ EGKW_SECT_NETBUSLABEL
@ EGKW_SECT_SMASHEDGATE
@ EGKW_SECT_CIRCLE
@ EGKW_SECT_GRID
@ EGKW_SECT_START
@ EGKW_SECT_PACKAGE
@ EGKW_SECT_SCHEMACONNECTION
@ EGKW_SECT_DEVICE
@ EGKW_SECT_SCHEMANET
@ EGKW_SECT_HOLE
@ EGKW_SECT_BOARD
@ EGKW_SECT_SYMBOL
@ EGKW_SECT_ATTRIBUTE
@ EGKW_SECT_SMASHEDPART
@ EGKW_SECT_LINE
@ EGKW_SECT_PIN
@ EGKW_SECT_LAYERS
@ EGKW_SECT_VARIANTCONNECTIONS
@ EGKW_SECT_VIA
@ EGKW_SECT_SCHEMA
@ EGKW_SECT_ATTRIBUTEVALUE
@ EGKW_SECT_SMASHEDNAME
@ EGKW_SECT_UNKNOWN11
@ EGKW_SECT_SMD
@ EGKW_SECT_ELEMENT2
@ EGKW_SECT_SCHEMABUS
@ EGKW_SECT_PART
@ EGKW_SECT_SIGNAL
@ EGKW_SECT_ELEMENT
@ EGKW_SECT_JUNCTION
@ EGKW_SECT_INSTANCE
@ EGKW_SECT_LIBRARY
@ EGKW_SECT_SYMBOLS
@ EGKW_SECT_FREETEXT
#define _(s)
const wxChar *const traceEagleIo
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
This file contains miscellaneous commonly used macros and functions.
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:400
DRC values pulled from the trailing 244-byte block (or sane defaults).
Lightweight mutable tree node for the intermediate Eagle binary tree.
std::map< wxString, wxString > props
long PropLong(const wxString &aKey) const
std::vector< std::unique_ptr< EGB_NODE > > children
EGB_NODE * FindChildById(int aId) const
bool HasProp(const wxString &aKey) const
EGB_NODE * AddChild(int aId, const wxString &aName)
wxString PropDoubled(const wxString &aKey) const
Format a property doubled in 64-bit so the half-to-full widening cannot overflow a 32-bit long.
wxString Prop(const wxString &aKey) const
EGB_NODE * FindChildByName(const wxString &aName) const
VECTOR3I res
int radius
VECTOR2I end
#define M_PI
wxLogTrace helper definitions.