You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4398 lines
140 KiB

8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2016 CERN
  5. * Copyright (C) 2016-2019 KiCad Developers, see AUTHORS.txt for contributors.
  6. *
  7. * @author Wayne Stambaugh <stambaughw@gmail.com>
  8. *
  9. * This program is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU General Public License
  11. * as published by the Free Software Foundation; either version 2
  12. * of the License, or (at your option) any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License along
  20. * with this program. If not, see <http://www.gnu.org/licenses/>.
  21. */
  22. #include <ctype.h>
  23. #include <algorithm>
  24. #include <wx/mstream.h>
  25. #include <wx/filename.h>
  26. #include <wx/tokenzr.h>
  27. #include <pgm_base.h>
  28. #include <draw_graphic_text.h>
  29. #include <kiway.h>
  30. #include <kicad_string.h>
  31. #include <richio.h>
  32. #include <core/typeinfo.h>
  33. #include <properties.h>
  34. #include <trace_helpers.h>
  35. #include <general.h>
  36. #include <sch_bitmap.h>
  37. #include <sch_bus_entry.h>
  38. #include <sch_component.h>
  39. #include <sch_junction.h>
  40. #include <sch_line.h>
  41. #include <sch_marker.h>
  42. #include <sch_no_connect.h>
  43. #include <sch_text.h>
  44. #include <sch_sheet.h>
  45. #include <sch_legacy_plugin.h>
  46. #include <template_fieldnames.h>
  47. #include <sch_screen.h>
  48. #include <class_libentry.h>
  49. #include <class_library.h>
  50. #include <lib_arc.h>
  51. #include <lib_bezier.h>
  52. #include <lib_circle.h>
  53. #include <lib_field.h>
  54. #include <lib_pin.h>
  55. #include <lib_polyline.h>
  56. #include <lib_rectangle.h>
  57. #include <lib_text.h>
  58. #include <eeschema_id.h> // for MAX_UNIT_COUNT_PER_PACKAGE definition
  59. #include <symbol_lib_table.h> // for PropPowerSymsOnly definintion.
  60. #include <confirm.h>
  61. // Must be the first line of part library document (.dcm) files.
  62. #define DOCFILE_IDENT "EESchema-DOCLIB Version 2.0"
  63. #define SCH_PARSE_ERROR( text, reader, pos ) \
  64. THROW_PARSE_ERROR( text, reader.GetSource(), reader.Line(), \
  65. reader.LineNumber(), pos - reader.Line() )
  66. // Token delimiters.
  67. const char* delims = " \t\r\n";
  68. // Tokens to read/save graphic lines style
  69. #define T_STYLE "style"
  70. #define T_COLOR "rgb" // cannot be modifed (used by wxWidgets)
  71. #define T_COLORA "rgba" // cannot be modifed (used by wxWidgets)
  72. #define T_WIDTH "width"
  73. static bool is_eol( char c )
  74. {
  75. // The default file eol character used internally by KiCad.
  76. // |
  77. // | Possible eol if someone edited the file by hand on certain platforms.
  78. // | |
  79. // | | May have gone past eol with strtok().
  80. // | | |
  81. if( c == '\n' || c == '\r' || c == 0 )
  82. return true;
  83. return false;
  84. }
  85. /**
  86. * Compare \a aString to the string starting at \a aLine and advances the character point to
  87. * the end of \a String and returns the new pointer position in \a aOutput if it is not NULL.
  88. *
  89. * @param aString - A pointer to the string to compare.
  90. * @param aLine - A pointer to string to begin the comparison.
  91. * @param aOutput - A pointer to a string pointer to the end of the comparison if not NULL.
  92. * @return true if \a aString was found starting at \a aLine. Otherwise false.
  93. */
  94. static bool strCompare( const char* aString, const char* aLine, const char** aOutput = NULL )
  95. {
  96. size_t len = strlen( aString );
  97. bool retv = ( strncasecmp( aLine, aString, len ) == 0 ) &&
  98. ( isspace( aLine[ len ] ) || aLine[ len ] == 0 );
  99. if( retv && aOutput )
  100. {
  101. const char* tmp = aLine;
  102. // Move past the end of the token.
  103. tmp += len;
  104. // Move to the beginning of the next token.
  105. while( *tmp && isspace( *tmp ) )
  106. tmp++;
  107. *aOutput = tmp;
  108. }
  109. return retv;
  110. }
  111. /**
  112. * Parse an ASCII integer string with possible leading whitespace into
  113. * an integer and updates the pointer at \a aOutput if it is not NULL, just
  114. * like "man strtol()".
  115. *
  116. * @param aReader - The line reader used to generate exception throw information.
  117. * @param aLine - A pointer the current position in a string.
  118. * @param aOutput - The pointer to a string pointer to copy the string pointer position when
  119. * the parsing is complete.
  120. * @return A valid integer value.
  121. * @throw An #IO_ERROR on an unexpected end of line.
  122. * @throw A #PARSE_ERROR if the parsed token is not a valid integer.
  123. */
  124. static int parseInt( LINE_READER& aReader, const char* aLine, const char** aOutput = NULL )
  125. {
  126. if( !*aLine )
  127. SCH_PARSE_ERROR( _( "unexpected end of line" ), aReader, aLine );
  128. // Clear errno before calling strtol() in case some other crt call set it.
  129. errno = 0;
  130. long retv = strtol( aLine, (char**) aOutput, 10 );
  131. // Make sure no error occurred when calling strtol().
  132. if( errno == ERANGE )
  133. SCH_PARSE_ERROR( "invalid integer value", aReader, aLine );
  134. // strtol does not strip off whitespace before the next token.
  135. if( aOutput )
  136. {
  137. const char* next = *aOutput;
  138. while( *next && isspace( *next ) )
  139. next++;
  140. *aOutput = next;
  141. }
  142. return (int) retv;
  143. }
  144. /**
  145. * Parse an ASCII hex integer string with possible leading whitespace into
  146. * a long integer and updates the pointer at \a aOutput if it is not NULL, just
  147. * like "man strtoll".
  148. *
  149. * @param aReader - The line reader used to generate exception throw information.
  150. * @param aLine - A pointer the current position in a string.
  151. * @param aOutput - The pointer to a string pointer to copy the string pointer position when
  152. * the parsing is complete.
  153. * @return A valid uint32_t value.
  154. * @throw IO_ERROR on an unexpected end of line.
  155. * @throw PARSE_ERROR if the parsed token is not a valid integer.
  156. */
  157. static uint32_t parseHex( LINE_READER& aReader, const char* aLine,
  158. const char** aOutput = NULL )
  159. {
  160. if( !*aLine )
  161. SCH_PARSE_ERROR( _( "unexpected end of line" ), aReader, aLine );
  162. // Due to some issues between some files created by a 64 bits version and those
  163. // created by a 32 bits version, we use here a temporary at least 64 bits storage:
  164. unsigned long long retv;
  165. // Clear errno before calling strtoull() in case some other crt call set it.
  166. errno = 0;
  167. retv = strtoull( aLine, (char**) aOutput, 16 );
  168. // Make sure no error occurred when calling strtoull().
  169. if( errno == ERANGE )
  170. SCH_PARSE_ERROR( "invalid hexadecimal number", aReader, aLine );
  171. // Strip off whitespace before the next token.
  172. if( aOutput )
  173. {
  174. const char* next = *aOutput;
  175. while( *next && isspace( *next ) )
  176. next++;
  177. *aOutput = next;
  178. }
  179. return (uint32_t)retv;
  180. }
  181. /**
  182. * Parses an ASCII point string with possible leading whitespace into a double precision
  183. * floating point number and updates the pointer at \a aOutput if it is not NULL, just
  184. * like "man strtod".
  185. *
  186. * @param aReader - The line reader used to generate exception throw information.
  187. * @param aLine - A pointer the current position in a string.
  188. * @param aOutput - The pointer to a string pointer to copy the string pointer position when
  189. * the parsing is complete.
  190. * @return A valid double value.
  191. * @throw IO_ERROR on an unexpected end of line.
  192. * @throw PARSE_ERROR if the parsed token is not a valid integer.
  193. */
  194. static double parseDouble( LINE_READER& aReader, const char* aLine,
  195. const char** aOutput = NULL )
  196. {
  197. if( !*aLine )
  198. SCH_PARSE_ERROR( _( "unexpected end of line" ), aReader, aLine );
  199. // Clear errno before calling strtod() in case some other crt call set it.
  200. errno = 0;
  201. double retv = strtod( aLine, (char**) aOutput );
  202. // Make sure no error occurred when calling strtod().
  203. if( errno == ERANGE )
  204. SCH_PARSE_ERROR( "invalid floating point number", aReader, aLine );
  205. // strtod does not strip off whitespace before the next token.
  206. if( aOutput )
  207. {
  208. const char* next = *aOutput;
  209. while( *next && isspace( *next ) )
  210. next++;
  211. *aOutput = next;
  212. }
  213. return retv;
  214. }
  215. /**
  216. * Parse a single ASCII character and updates the pointer at \a aOutput if it is not NULL.
  217. *
  218. * @param aReader - The line reader used to generate exception throw information.
  219. * @param aCurrentToken - A pointer the current position in a string.
  220. * @param aNextToken - The pointer to a string pointer to copy the string pointer position when
  221. * the parsing is complete.
  222. * @return A valid ASCII character.
  223. * @throw IO_ERROR on an unexpected end of line.
  224. * @throw PARSE_ERROR if the parsed token is not a a single character token.
  225. */
  226. static char parseChar( LINE_READER& aReader, const char* aCurrentToken,
  227. const char** aNextToken = NULL )
  228. {
  229. while( *aCurrentToken && isspace( *aCurrentToken ) )
  230. aCurrentToken++;
  231. if( !*aCurrentToken )
  232. SCH_PARSE_ERROR( _( "unexpected end of line" ), aReader, aCurrentToken );
  233. if( !isspace( *( aCurrentToken + 1 ) ) )
  234. SCH_PARSE_ERROR( "expected single character token", aReader, aCurrentToken );
  235. if( aNextToken )
  236. {
  237. const char* next = aCurrentToken + 2;
  238. while( *next && isspace( *next ) )
  239. next++;
  240. *aNextToken = next;
  241. }
  242. return *aCurrentToken;
  243. }
  244. /**
  245. * Parse an unquoted utf8 string and updates the pointer at \a aOutput if it is not NULL.
  246. *
  247. * The parsed string must be a continuous string with no white space.
  248. *
  249. * @param aString - A reference to the parsed string.
  250. * @param aReader - The line reader used to generate exception throw information.
  251. * @param aCurrentToken - A pointer the current position in a string.
  252. * @param aNextToken - The pointer to a string pointer to copy the string pointer position when
  253. * the parsing is complete.
  254. * @param aCanBeEmpty - True if the parsed string is optional. False if it is mandatory.
  255. * @throw IO_ERROR on an unexpected end of line.
  256. * @throw PARSE_ERROR if the \a aCanBeEmpty is false and no string was parsed.
  257. */
  258. static void parseUnquotedString( wxString& aString, LINE_READER& aReader,
  259. const char* aCurrentToken, const char** aNextToken = NULL,
  260. bool aCanBeEmpty = false )
  261. {
  262. if( !*aCurrentToken )
  263. {
  264. if( aCanBeEmpty )
  265. return;
  266. else
  267. SCH_PARSE_ERROR( _( "unexpected end of line" ), aReader, aCurrentToken );
  268. }
  269. const char* tmp = aCurrentToken;
  270. while( *tmp && isspace( *tmp ) )
  271. tmp++;
  272. if( !*tmp )
  273. {
  274. if( aCanBeEmpty )
  275. return;
  276. else
  277. SCH_PARSE_ERROR( _( "unexpected end of line" ), aReader, aCurrentToken );
  278. }
  279. std::string utf8;
  280. while( *tmp && !isspace( *tmp ) )
  281. utf8 += *tmp++;
  282. aString = FROM_UTF8( utf8.c_str() );
  283. if( aString.IsEmpty() && !aCanBeEmpty )
  284. SCH_PARSE_ERROR( _( "expected unquoted string" ), aReader, aCurrentToken );
  285. if( aNextToken )
  286. {
  287. const char* next = tmp;
  288. while( *next && isspace( *next ) )
  289. next++;
  290. *aNextToken = next;
  291. }
  292. }
  293. /**
  294. * Parse an quoted ASCII utf8 and updates the pointer at \a aOutput if it is not NULL.
  295. *
  296. * The parsed string must be contained within a single line. There are no multi-line
  297. * quoted strings in the legacy schematic file format.
  298. *
  299. * @param aString - A reference to the parsed string.
  300. * @param aReader - The line reader used to generate exception throw information.
  301. * @param aCurrentToken - A pointer the current position in a string.
  302. * @param aNextToken - The pointer to a string pointer to copy the string pointer position when
  303. * the parsing is complete.
  304. * @param aCanBeEmpty - True if the parsed string is optional. False if it is mandatory.
  305. * @throw IO_ERROR on an unexpected end of line.
  306. * @throw PARSE_ERROR if the \a aCanBeEmpty is false and no string was parsed.
  307. */
  308. static void parseQuotedString( wxString& aString, LINE_READER& aReader,
  309. const char* aCurrentToken, const char** aNextToken = NULL,
  310. bool aCanBeEmpty = false )
  311. {
  312. if( !*aCurrentToken )
  313. {
  314. if( aCanBeEmpty )
  315. return;
  316. else
  317. SCH_PARSE_ERROR( _( "unexpected end of line" ), aReader, aCurrentToken );
  318. }
  319. const char* tmp = aCurrentToken;
  320. while( *tmp && isspace( *tmp ) )
  321. tmp++;
  322. if( !*tmp )
  323. {
  324. if( aCanBeEmpty )
  325. return;
  326. else
  327. SCH_PARSE_ERROR( _( "unexpected end of line" ), aReader, aCurrentToken );
  328. }
  329. // Verify opening quote.
  330. if( *tmp != '"' )
  331. SCH_PARSE_ERROR( "expecting opening quote", aReader, aCurrentToken );
  332. tmp++;
  333. std::string utf8; // utf8 without escapes and quotes.
  334. // Fetch everything up to closing quote.
  335. while( *tmp )
  336. {
  337. if( *tmp == '\\' )
  338. {
  339. tmp++;
  340. if( !*tmp )
  341. SCH_PARSE_ERROR( _( "unexpected end of line" ), aReader, aCurrentToken );
  342. // Do not copy the escape byte if it is followed by \ or "
  343. if( *tmp != '"' && *tmp != '\\' )
  344. utf8 += '\\';
  345. utf8 += *tmp;
  346. }
  347. else if( *tmp == '"' ) // Closing double quote.
  348. {
  349. break;
  350. }
  351. else
  352. {
  353. utf8 += *tmp;
  354. }
  355. tmp++;
  356. }
  357. aString = FROM_UTF8( utf8.c_str() );
  358. if( aString.IsEmpty() && !aCanBeEmpty )
  359. SCH_PARSE_ERROR( "expected quoted string", aReader, aCurrentToken );
  360. if( *tmp && *tmp != '"' )
  361. SCH_PARSE_ERROR( "no closing quote for string found", aReader, tmp );
  362. // Move past the closing quote.
  363. tmp++;
  364. if( aNextToken )
  365. {
  366. const char* next = tmp;
  367. while( *next && *next == ' ' )
  368. next++;
  369. *aNextToken = next;
  370. }
  371. }
  372. /**
  373. * A cache assistant for the part library portion of the #SCH_PLUGIN API, and only for the
  374. * #SCH_LEGACY_PLUGIN, so therefore is private to this implementation file, i.e. not placed
  375. * into a header.
  376. */
  377. class SCH_LEGACY_PLUGIN_CACHE
  378. {
  379. static int m_modHash; // Keep track of the modification status of the library.
  380. wxString m_fileName; // Absolute path and file name.
  381. wxFileName m_libFileName; // Absolute path and file name is required here.
  382. wxDateTime m_fileModTime;
  383. LIB_ALIAS_MAP m_aliases; // Map of names of LIB_ALIAS pointers.
  384. bool m_isWritable;
  385. bool m_isModified;
  386. int m_versionMajor;
  387. int m_versionMinor;
  388. int m_libType; // Is this cache a component or symbol library.
  389. void loadHeader( FILE_LINE_READER& aReader );
  390. static void loadAliases( std::unique_ptr< LIB_PART >& aPart, LINE_READER& aReader );
  391. static void loadField( std::unique_ptr< LIB_PART >& aPart, LINE_READER& aReader );
  392. static void loadDrawEntries( std::unique_ptr< LIB_PART >& aPart, LINE_READER& aReader,
  393. int aMajorVersion, int aMinorVersion );
  394. static void loadFootprintFilters( std::unique_ptr< LIB_PART >& aPart,
  395. LINE_READER& aReader );
  396. void loadDocs();
  397. static LIB_ARC* loadArc( std::unique_ptr< LIB_PART >& aPart, LINE_READER& aReader );
  398. static LIB_CIRCLE* loadCircle( std::unique_ptr< LIB_PART >& aPart, LINE_READER& aReader );
  399. static LIB_TEXT* loadText( std::unique_ptr< LIB_PART >& aPart, LINE_READER& aReader,
  400. int aMajorVersion, int aMinorVersion );
  401. static LIB_RECTANGLE* loadRectangle( std::unique_ptr< LIB_PART >& aPart,
  402. LINE_READER& aReader );
  403. static LIB_PIN* loadPin( std::unique_ptr< LIB_PART >& aPart, LINE_READER& aReader );
  404. static LIB_POLYLINE* loadPolyLine( std::unique_ptr< LIB_PART >& aPart, LINE_READER& aReader );
  405. static LIB_BEZIER* loadBezier( std::unique_ptr< LIB_PART >& aPart, LINE_READER& aReader );
  406. static FILL_T parseFillMode( LINE_READER& aReader, const char* aLine,
  407. const char** aOutput );
  408. // bool checkForDuplicates( wxString& aAliasName );
  409. LIB_ALIAS* removeAlias( LIB_ALIAS* aAlias );
  410. void saveDocFile();
  411. static void saveArc( LIB_ARC* aArc, OUTPUTFORMATTER& aFormatter );
  412. static void saveBezier( LIB_BEZIER* aBezier, OUTPUTFORMATTER& aFormatter );
  413. static void saveCircle( LIB_CIRCLE* aCircle, OUTPUTFORMATTER& aFormatter );
  414. static void saveField( LIB_FIELD* aField, OUTPUTFORMATTER& aFormatter );
  415. static void savePin( LIB_PIN* aPin, OUTPUTFORMATTER& aFormatter );
  416. static void savePolyLine( LIB_POLYLINE* aPolyLine, OUTPUTFORMATTER& aFormatter );
  417. static void saveRectangle( LIB_RECTANGLE* aRectangle, OUTPUTFORMATTER& aFormatter );
  418. static void saveText( LIB_TEXT* aText, OUTPUTFORMATTER& aFormatter );
  419. friend SCH_LEGACY_PLUGIN;
  420. public:
  421. SCH_LEGACY_PLUGIN_CACHE( const wxString& aLibraryPath );
  422. ~SCH_LEGACY_PLUGIN_CACHE();
  423. int GetModifyHash() const { return m_modHash; }
  424. // Most all functions in this class throw IO_ERROR exceptions. There are no
  425. // error codes nor user interface calls from here, nor in any SCH_PLUGIN objects.
  426. // Catch these exceptions higher up please.
  427. /// Save the entire library to file m_libFileName;
  428. void Save( bool aSaveDocFile = true );
  429. void Load();
  430. void AddSymbol( const LIB_PART* aPart );
  431. void DeleteAlias( const wxString& aAliasName );
  432. void DeleteSymbol( const wxString& aAliasName );
  433. // If m_libFileName is a symlink follow it to the real source file
  434. wxFileName GetRealFile() const;
  435. wxDateTime GetLibModificationTime();
  436. bool IsFile( const wxString& aFullPathAndFileName ) const;
  437. bool IsFileChanged() const;
  438. void SetModified( bool aModified = true ) { m_isModified = aModified; }
  439. wxString GetLogicalName() const { return m_libFileName.GetName(); }
  440. void SetFileName( const wxString& aFileName ) { m_libFileName = aFileName; }
  441. wxString GetFileName() const { return m_libFileName.GetFullPath(); }
  442. static LIB_PART* LoadPart( LINE_READER& aReader, int aMajorVersion, int aMinorVersion );
  443. static void SaveSymbol( LIB_PART* aSymbol, OUTPUTFORMATTER& aFormatter );
  444. };
  445. SCH_LEGACY_PLUGIN::SCH_LEGACY_PLUGIN()
  446. {
  447. init( NULL );
  448. }
  449. SCH_LEGACY_PLUGIN::~SCH_LEGACY_PLUGIN()
  450. {
  451. delete m_cache;
  452. }
  453. void SCH_LEGACY_PLUGIN::init( KIWAY* aKiway, const PROPERTIES* aProperties )
  454. {
  455. m_version = 0;
  456. m_rootSheet = NULL;
  457. m_props = aProperties;
  458. m_kiway = aKiway;
  459. m_cache = NULL;
  460. m_out = NULL;
  461. }
  462. SCH_SHEET* SCH_LEGACY_PLUGIN::Load( const wxString& aFileName, KIWAY* aKiway,
  463. SCH_SHEET* aAppendToMe, const PROPERTIES* aProperties )
  464. {
  465. wxASSERT( !aFileName || aKiway != NULL );
  466. LOCALE_IO toggle; // toggles on, then off, the C locale.
  467. SCH_SHEET* sheet;
  468. wxFileName fn = aFileName;
  469. // Unfortunately child sheet file names the legacy schematic file format are not fully
  470. // qualified and are always appended to the project path. The aFileName attribute must
  471. // always be an absolute path so the project path can be used for load child sheet files.
  472. wxASSERT( fn.IsAbsolute() );
  473. if( aAppendToMe )
  474. {
  475. wxLogTrace( traceSchLegacyPlugin, "Append \"%s\" to sheet \"%s\".",
  476. aFileName, aAppendToMe->GetFileName() );
  477. wxFileName normedFn = aAppendToMe->GetFileName();
  478. if( !normedFn.IsAbsolute() )
  479. {
  480. if( aFileName.Right( normedFn.GetFullPath().Length() ) == normedFn.GetFullPath() )
  481. m_path = aFileName.Left( aFileName.Length() - normedFn.GetFullPath().Length() );
  482. }
  483. if( m_path.IsEmpty() )
  484. m_path = aKiway->Prj().GetProjectPath();
  485. wxLogTrace( traceSchLegacyPlugin, "Normalized append path \"%s\".", m_path );
  486. }
  487. else
  488. {
  489. m_path = aKiway->Prj().GetProjectPath();
  490. }
  491. m_currentPath.push( m_path );
  492. init( aKiway, aProperties );
  493. if( aAppendToMe == NULL )
  494. {
  495. // Clean up any allocated memory if an exception occurs loading the schematic.
  496. std::unique_ptr< SCH_SHEET > newSheet( new SCH_SHEET );
  497. newSheet->SetFileName( aFileName );
  498. m_rootSheet = newSheet.get();
  499. loadHierarchy( newSheet.get() );
  500. // If we got here, the schematic loaded successfully.
  501. sheet = newSheet.release();
  502. }
  503. else
  504. {
  505. m_rootSheet = aAppendToMe->GetRootSheet();
  506. wxASSERT( m_rootSheet != NULL );
  507. sheet = aAppendToMe;
  508. loadHierarchy( sheet );
  509. }
  510. wxASSERT( m_currentPath.size() == 1 ); // only the project path should remain
  511. return sheet;
  512. }
  513. // Everything below this comment is recursive. Modify with care.
  514. void SCH_LEGACY_PLUGIN::loadHierarchy( SCH_SHEET* aSheet )
  515. {
  516. SCH_SCREEN* screen = NULL;
  517. if( !aSheet->GetScreen() )
  518. {
  519. // SCH_SCREEN objects store the full path and file name where the SCH_SHEET object only
  520. // stores the file name and extension. Add the project path to the file name and
  521. // extension to compare when calling SCH_SHEET::SearchHierarchy().
  522. wxFileName fileName = aSheet->GetFileName();
  523. if( !fileName.IsAbsolute() )
  524. fileName.MakeAbsolute( m_currentPath.top() );
  525. // Save the current path so that it gets restored when decending and ascending the
  526. // sheet hierarchy which allows for sheet schematic files to be nested in folders
  527. // relative to the last path a schematic was loaded from.
  528. wxLogTrace( traceSchLegacyPlugin, "Saving path \"%s\"", m_currentPath.top() );
  529. m_currentPath.push( fileName.GetPath() );
  530. wxLogTrace( traceSchLegacyPlugin, "Current path \"%s\"", m_currentPath.top() );
  531. wxLogTrace( traceSchLegacyPlugin, "Loading \"%s\"", fileName.GetFullPath() );
  532. m_rootSheet->SearchHierarchy( fileName.GetFullPath(), &screen );
  533. if( screen )
  534. {
  535. aSheet->SetScreen( screen );
  536. // Do not need to load the sub-sheets - this has already been done.
  537. }
  538. else
  539. {
  540. aSheet->SetScreen( new SCH_SCREEN( m_kiway ) );
  541. aSheet->GetScreen()->SetFileName( fileName.GetFullPath() );
  542. try
  543. {
  544. loadFile( fileName.GetFullPath(), aSheet->GetScreen() );
  545. EDA_ITEM* item = aSheet->GetScreen()->GetDrawItems();
  546. while( item )
  547. {
  548. if( item->Type() == SCH_SHEET_T )
  549. {
  550. SCH_SHEET* sheet = (SCH_SHEET*) item;
  551. // Set the parent to aSheet. This effectively creates a method to find
  552. // the root sheet from any sheet so a pointer to the root sheet does not
  553. // need to be stored globally. Note: this is not the same as a hierarchy.
  554. // Complex hierarchies can have multiple copies of a sheet. This only
  555. // provides a simple tree to find the root sheet.
  556. sheet->SetParent( aSheet );
  557. // Recursion starts here.
  558. loadHierarchy( sheet );
  559. }
  560. item = item->Next();
  561. }
  562. }
  563. catch( const IO_ERROR& ioe )
  564. {
  565. // If there is a problem loading the root sheet, there is no recovery.
  566. if( aSheet == m_rootSheet )
  567. throw( ioe );
  568. // For all subsheets, queue up the error message for the caller.
  569. if( !m_error.IsEmpty() )
  570. m_error += "\n";
  571. m_error += ioe.What();
  572. }
  573. }
  574. m_currentPath.pop();
  575. wxLogTrace( traceSchLegacyPlugin, "Restoring path \"%s\"", m_currentPath.top() );
  576. }
  577. }
  578. void SCH_LEGACY_PLUGIN::loadFile( const wxString& aFileName, SCH_SCREEN* aScreen )
  579. {
  580. FILE_LINE_READER reader( aFileName );
  581. loadHeader( reader, aScreen );
  582. while( reader.ReadLine() )
  583. {
  584. char* line = reader.Line();
  585. while( *line && *line == ' ' )
  586. line++;
  587. // Either an object will be loaded properly or the file load will fail and raise
  588. // an exception.
  589. if( strCompare( "$Descr", line ) )
  590. loadPageSettings( reader, aScreen );
  591. else if( strCompare( "$Comp", line ) )
  592. aScreen->Append( loadComponent( reader ) );
  593. else if( strCompare( "$Sheet", line ) )
  594. aScreen->Append( loadSheet( reader ) );
  595. else if( strCompare( "$Bitmap", line ) )
  596. aScreen->Append( loadBitmap( reader ) );
  597. else if( strCompare( "Connection", line ) )
  598. aScreen->Append( loadJunction( reader ) );
  599. else if( strCompare( "NoConn", line ) )
  600. aScreen->Append( loadNoConnect( reader ) );
  601. else if( strCompare( "Wire", line ) )
  602. aScreen->Append( loadWire( reader ) );
  603. else if( strCompare( "Entry", line ) )
  604. aScreen->Append( loadBusEntry( reader ) );
  605. else if( strCompare( "Text", line ) )
  606. aScreen->Append( loadText( reader ) );
  607. else if( strCompare( "$EndSCHEMATC", line ) )
  608. return;
  609. }
  610. // Unfortunately schematic files prior to version 2 are not terminated with $EndSCHEMATC
  611. // so checking for it's existance will fail so just exit here and take our chances. :(
  612. if( m_version > 1 )
  613. THROW_IO_ERROR( "'$EndSCHEMATC' not found" );
  614. }
  615. void SCH_LEGACY_PLUGIN::loadHeader( FILE_LINE_READER& aReader, SCH_SCREEN* aScreen )
  616. {
  617. const char* line = aReader.ReadLine();
  618. if( !line || !strCompare( "Eeschema Schematic File Version", line, &line ) )
  619. {
  620. m_error.Printf( _( "\"%s\" does not appear to be an Eeschema file" ),
  621. GetChars( aScreen->GetFileName() ) );
  622. THROW_IO_ERROR( m_error );
  623. }
  624. // get the file version here.
  625. m_version = parseInt( aReader, line, &line );
  626. // The next lines are the lib list section, and are mainly comments, like:
  627. // LIBS:power
  628. // the lib list is not used, but is in schematic file just in case.
  629. // It is usually not empty, but we accept empty list.
  630. // If empty, there is a legacy section, not used
  631. // EELAYER i j
  632. // and the last line is
  633. // EELAYER END
  634. // Skip all lines until the end of header "EELAYER END" is found
  635. while( aReader.ReadLine() )
  636. {
  637. line = aReader.Line();
  638. while( *line == ' ' )
  639. line++;
  640. if( strCompare( "EELAYER END", line ) )
  641. return;
  642. }
  643. THROW_IO_ERROR( _( "Missing 'EELAYER END'" ) );
  644. }
  645. void SCH_LEGACY_PLUGIN::loadPageSettings( FILE_LINE_READER& aReader, SCH_SCREEN* aScreen )
  646. {
  647. wxASSERT( aScreen != NULL );
  648. wxString buf;
  649. const char* line = aReader.Line();
  650. PAGE_INFO pageInfo;
  651. TITLE_BLOCK tb;
  652. wxCHECK_RET( strCompare( "$Descr", line, &line ), "Invalid sheet description" );
  653. parseUnquotedString( buf, aReader, line, &line );
  654. if( !pageInfo.SetType( buf ) )
  655. SCH_PARSE_ERROR( "invalid page size", aReader, line );
  656. int pagew = parseInt( aReader, line, &line );
  657. int pageh = parseInt( aReader, line, &line );
  658. if( buf == PAGE_INFO::Custom )
  659. {
  660. pageInfo.SetWidthMils( pagew );
  661. pageInfo.SetHeightMils( pageh );
  662. }
  663. else
  664. {
  665. wxString orientation;
  666. // Non custom size, set portrait if its present. Can be empty string which defaults
  667. // to landscape.
  668. parseUnquotedString( orientation, aReader, line, &line, true );
  669. if( orientation == "portrait" )
  670. pageInfo.SetPortrait( true );
  671. }
  672. aScreen->SetPageSettings( pageInfo );
  673. while( line != NULL )
  674. {
  675. buf.clear();
  676. if( !aReader.ReadLine() )
  677. SCH_PARSE_ERROR( _( "unexpected end of file" ), aReader, line );
  678. line = aReader.Line();
  679. if( strCompare( "Sheet", line, &line ) )
  680. {
  681. aScreen->m_ScreenNumber = parseInt( aReader, line, &line );
  682. aScreen->m_NumberOfScreens = parseInt( aReader, line, &line );
  683. }
  684. else if( strCompare( "Title", line, &line ) )
  685. {
  686. parseQuotedString( buf, aReader, line, &line, true );
  687. tb.SetTitle( buf );
  688. }
  689. else if( strCompare( "Date", line, &line ) )
  690. {
  691. parseQuotedString( buf, aReader, line, &line, true );
  692. tb.SetDate( buf );
  693. }
  694. else if( strCompare( "Rev", line, &line ) )
  695. {
  696. parseQuotedString( buf, aReader, line, &line, true );
  697. tb.SetRevision( buf );
  698. }
  699. else if( strCompare( "Comp", line, &line ) )
  700. {
  701. parseQuotedString( buf, aReader, line, &line, true );
  702. tb.SetCompany( buf );
  703. }
  704. else if( strCompare( "Comment1", line, &line ) )
  705. {
  706. parseQuotedString( buf, aReader, line, &line, true );
  707. tb.SetComment1( buf );
  708. }
  709. else if( strCompare( "Comment2", line, &line ) )
  710. {
  711. parseQuotedString( buf, aReader, line, &line, true );
  712. tb.SetComment2( buf );
  713. }
  714. else if( strCompare( "Comment3", line, &line ) )
  715. {
  716. parseQuotedString( buf, aReader, line, &line, true );
  717. tb.SetComment3( buf );
  718. }
  719. else if( strCompare( "Comment4", line, &line ) )
  720. {
  721. parseQuotedString( buf, aReader, line, &line, true );
  722. tb.SetComment4( buf );
  723. }
  724. else if( strCompare( "$EndDescr", line ) )
  725. {
  726. aScreen->SetTitleBlock( tb );
  727. return;
  728. }
  729. }
  730. SCH_PARSE_ERROR( "missing 'EndDescr'", aReader, line );
  731. }
  732. SCH_SHEET* SCH_LEGACY_PLUGIN::loadSheet( FILE_LINE_READER& aReader )
  733. {
  734. std::unique_ptr< SCH_SHEET > sheet( new SCH_SHEET() );
  735. sheet->SetTimeStamp( GetNewTimeStamp() );
  736. const char* line = aReader.ReadLine();
  737. while( line != NULL )
  738. {
  739. if( strCompare( "S", line, &line ) ) // Sheet dimensions.
  740. {
  741. wxPoint position;
  742. position.x = parseInt( aReader, line, &line );
  743. position.y = parseInt( aReader, line, &line );
  744. sheet->SetPosition( position );
  745. wxSize size;
  746. size.SetWidth( parseInt( aReader, line, &line ) );
  747. size.SetHeight( parseInt( aReader, line, &line ) );
  748. sheet->SetSize( size );
  749. }
  750. else if( strCompare( "U", line, &line ) ) // Sheet time stamp.
  751. {
  752. sheet->SetTimeStamp( parseHex( aReader, line ) );
  753. }
  754. else if( *line == 'F' ) // Sheet field.
  755. {
  756. line++;
  757. wxString text;
  758. int size;
  759. int fieldId = parseInt( aReader, line, &line );
  760. if( fieldId == 0 || fieldId == 1 ) // Sheet name and file name.
  761. {
  762. parseQuotedString( text, aReader, line, &line );
  763. size = parseInt( aReader, line, &line );
  764. if( fieldId == 0 )
  765. {
  766. sheet->SetName( text );
  767. sheet->SetSheetNameSize( size );
  768. }
  769. else
  770. {
  771. sheet->SetFileName( text );
  772. sheet->SetFileNameSize( size );
  773. }
  774. }
  775. else // Sheet pin.
  776. {
  777. std::unique_ptr< SCH_SHEET_PIN > sheetPin( new SCH_SHEET_PIN( sheet.get() ) );
  778. sheetPin->SetNumber( fieldId );
  779. // Can be empty fields.
  780. parseQuotedString( text, aReader, line, &line, true );
  781. sheetPin->SetText( text );
  782. if( line == NULL )
  783. THROW_IO_ERROR( _( "unexpected end of line" ) );
  784. switch( parseChar( aReader, line, &line ) )
  785. {
  786. case 'I':
  787. sheetPin->SetShape( NET_INPUT );
  788. break;
  789. case 'O':
  790. sheetPin->SetShape( NET_OUTPUT );
  791. break;
  792. case 'B':
  793. sheetPin->SetShape( NET_BIDI );
  794. break;
  795. case 'T':
  796. sheetPin->SetShape( NET_TRISTATE );
  797. break;
  798. case 'U':
  799. sheetPin->SetShape( NET_UNSPECIFIED );
  800. break;
  801. default:
  802. SCH_PARSE_ERROR( "invalid sheet pin type", aReader, line );
  803. }
  804. switch( parseChar( aReader, line, &line ) )
  805. {
  806. case 'R': /* pin on right side */
  807. sheetPin->SetEdge( SCH_SHEET_PIN::SHEET_RIGHT_SIDE );
  808. break;
  809. case 'T': /* pin on top side */
  810. sheetPin->SetEdge( SCH_SHEET_PIN::SHEET_TOP_SIDE );
  811. break;
  812. case 'B': /* pin on bottom side */
  813. sheetPin->SetEdge( SCH_SHEET_PIN::SHEET_BOTTOM_SIDE );
  814. break;
  815. case 'L': /* pin on left side */
  816. sheetPin->SetEdge( SCH_SHEET_PIN::SHEET_LEFT_SIDE );
  817. break;
  818. default:
  819. SCH_PARSE_ERROR( "invalid sheet pin side", aReader, line );
  820. }
  821. wxPoint position;
  822. position.x = parseInt( aReader, line, &line );
  823. position.y = parseInt( aReader, line, &line );
  824. sheetPin->SetPosition( position );
  825. size = parseInt( aReader, line, &line );
  826. sheetPin->SetTextSize( wxSize( size, size ) );
  827. sheet->AddPin( sheetPin.release() );
  828. }
  829. }
  830. else if( strCompare( "$EndSheet", line ) )
  831. return sheet.release();
  832. line = aReader.ReadLine();
  833. }
  834. SCH_PARSE_ERROR( "missing '$EndSheet`", aReader, line );
  835. return NULL; // Prevents compiler warning. Should never get here.
  836. }
  837. SCH_BITMAP* SCH_LEGACY_PLUGIN::loadBitmap( FILE_LINE_READER& aReader )
  838. {
  839. std::unique_ptr< SCH_BITMAP > bitmap( new SCH_BITMAP );
  840. const char* line = aReader.Line();
  841. wxCHECK( strCompare( "$Bitmap", line, &line ), NULL );
  842. line = aReader.ReadLine();
  843. while( line != NULL )
  844. {
  845. if( strCompare( "Pos", line, &line ) )
  846. {
  847. wxPoint position;
  848. position.x = parseInt( aReader, line, &line );
  849. position.y = parseInt( aReader, line, &line );
  850. bitmap->SetPosition( position );
  851. }
  852. else if( strCompare( "Scale", line, &line ) )
  853. {
  854. auto scalefactor = parseDouble( aReader, line, &line );
  855. // Prevent scalefactor values that cannot be displayed.
  856. // In the case of a bad value, we accept that the image might be mis-scaled
  857. // rather than removing the full image. Users can then edit the scale factor in
  858. // Eeschema to the appropriate value
  859. if( !std::isnormal( scalefactor ) )
  860. scalefactor = 1.0;
  861. bitmap->GetImage()->SetScale( scalefactor );
  862. }
  863. else if( strCompare( "Data", line, &line ) )
  864. {
  865. wxMemoryOutputStream stream;
  866. while( line )
  867. {
  868. if( !aReader.ReadLine() )
  869. SCH_PARSE_ERROR( _( "Unexpected end of file" ), aReader, line );
  870. line = aReader.Line();
  871. if( strCompare( "EndData", line ) )
  872. {
  873. // all the PNG date is read.
  874. // We expect here m_image and m_bitmap are void
  875. wxImage* image = new wxImage();
  876. wxMemoryInputStream istream( stream );
  877. image->LoadFile( istream, wxBITMAP_TYPE_PNG );
  878. bitmap->GetImage()->SetImage( image );
  879. bitmap->GetImage()->SetBitmap( new wxBitmap( *image ) );
  880. break;
  881. }
  882. // Read PNG data, stored in hexadecimal,
  883. // each byte = 2 hexadecimal digits and a space between 2 bytes
  884. // and put it in memory stream buffer
  885. int len = strlen( line );
  886. for( ; len > 0 && !isspace( *line ); len -= 3, line += 3 )
  887. {
  888. int value = 0;
  889. if( sscanf( line, "%X", &value ) == 1 )
  890. stream.PutC( (char) value );
  891. else
  892. THROW_IO_ERROR( "invalid PNG data" );
  893. }
  894. }
  895. if( line == NULL )
  896. THROW_IO_ERROR( _( "unexpected end of file" ) );
  897. }
  898. else if( strCompare( "$EndBitmap", line ) )
  899. return bitmap.release();
  900. line = aReader.ReadLine();
  901. }
  902. THROW_IO_ERROR( _( "unexpected end of file" ) );
  903. }
  904. SCH_JUNCTION* SCH_LEGACY_PLUGIN::loadJunction( FILE_LINE_READER& aReader )
  905. {
  906. std::unique_ptr< SCH_JUNCTION > junction( new SCH_JUNCTION );
  907. const char* line = aReader.Line();
  908. wxCHECK( strCompare( "Connection", line, &line ), NULL );
  909. wxString name;
  910. parseUnquotedString( name, aReader, line, &line );
  911. wxPoint position;
  912. position.x = parseInt( aReader, line, &line );
  913. position.y = parseInt( aReader, line, &line );
  914. junction->SetPosition( position );
  915. return junction.release();
  916. }
  917. SCH_NO_CONNECT* SCH_LEGACY_PLUGIN::loadNoConnect( FILE_LINE_READER& aReader )
  918. {
  919. std::unique_ptr< SCH_NO_CONNECT > no_connect( new SCH_NO_CONNECT );
  920. const char* line = aReader.Line();
  921. wxCHECK( strCompare( "NoConn", line, &line ), NULL );
  922. wxString name;
  923. parseUnquotedString( name, aReader, line, &line );
  924. wxPoint position;
  925. position.x = parseInt( aReader, line, &line );
  926. position.y = parseInt( aReader, line, &line );
  927. no_connect->SetPosition( position );
  928. return no_connect.release();
  929. }
  930. SCH_LINE* SCH_LEGACY_PLUGIN::loadWire( FILE_LINE_READER& aReader )
  931. {
  932. std::unique_ptr< SCH_LINE > wire( new SCH_LINE );
  933. const char* line = aReader.Line();
  934. wxCHECK( strCompare( "Wire", line, &line ), NULL );
  935. if( strCompare( "Wire", line, &line ) )
  936. wire->SetLayer( LAYER_WIRE );
  937. else if( strCompare( "Bus", line, &line ) )
  938. wire->SetLayer( LAYER_BUS );
  939. else if( strCompare( "Notes", line, &line ) )
  940. wire->SetLayer( LAYER_NOTES );
  941. else
  942. SCH_PARSE_ERROR( "invalid line type", aReader, line );
  943. if( !strCompare( "Line", line, &line ) )
  944. SCH_PARSE_ERROR( "invalid wire definition", aReader, line );
  945. // Since Sept 15, 2017, a line style is alloved (width, style, color)
  946. // Only non default values are stored
  947. while( !is_eol( *line ) )
  948. {
  949. wxString buf;
  950. parseUnquotedString( buf, aReader, line, &line );
  951. if( buf == ")" )
  952. continue;
  953. else if( buf == T_WIDTH )
  954. {
  955. int size = parseInt( aReader, line, &line );
  956. wire->SetLineWidth( size );
  957. }
  958. else if( buf == T_STYLE )
  959. {
  960. parseUnquotedString( buf, aReader, line, &line );
  961. int style = SCH_LINE::GetLineStyleInternalId( buf );
  962. wire->SetLineStyle( style );
  963. }
  964. else // should be the color parameter.
  965. {
  966. // The color param is something like rgb(150, 40, 191)
  967. // and because there is no space between ( and 150
  968. // the first param is inside buf.
  969. // So break keyword and the first param into 2 separate strings.
  970. wxString prm, keyword;
  971. keyword = buf.BeforeLast( '(', &prm );
  972. if( ( keyword == T_COLOR ) || ( keyword == T_COLORA ) )
  973. {
  974. long color[4] = { 0 };
  975. int ii = 0;
  976. if( !prm.IsEmpty() )
  977. {
  978. prm.ToLong( &color[ii] );
  979. ii++;
  980. }
  981. int prm_count = ( keyword == T_COLORA ) ? 4 : 3;
  982. // fix opacity to 1.0 or 255, when not exists in file
  983. color[3] = 255;
  984. for(; ii < prm_count && !is_eol( *line ); ii++ )
  985. {
  986. color[ii] = parseInt( aReader, line, &line );
  987. // Skip the separator between values
  988. if( *line == ',' || *line == ' ')
  989. line++;
  990. }
  991. wire->SetLineColor( color[0]/255.0, color[1]/255.0, color[2]/255.0,color[3]/255.0 );
  992. }
  993. }
  994. }
  995. // Read the segment en points coordinates:
  996. line = aReader.ReadLine();
  997. wxPoint begin, end;
  998. begin.x = parseInt( aReader, line, &line );
  999. begin.y = parseInt( aReader, line, &line );
  1000. end.x = parseInt( aReader, line, &line );
  1001. end.y = parseInt( aReader, line, &line );
  1002. wire->SetStartPoint( begin );
  1003. wire->SetEndPoint( end );
  1004. return wire.release();
  1005. }
  1006. SCH_BUS_ENTRY_BASE* SCH_LEGACY_PLUGIN::loadBusEntry( FILE_LINE_READER& aReader )
  1007. {
  1008. const char* line = aReader.Line();
  1009. wxCHECK( strCompare( "Entry", line, &line ), NULL );
  1010. std::unique_ptr< SCH_BUS_ENTRY_BASE > busEntry;
  1011. if( strCompare( "Wire", line, &line ) )
  1012. {
  1013. busEntry.reset( new SCH_BUS_WIRE_ENTRY );
  1014. if( !strCompare( "Line", line, &line ) )
  1015. SCH_PARSE_ERROR( "invalid bus entry definition expected 'Line'", aReader, line );
  1016. }
  1017. else if( strCompare( "Bus", line, &line ) )
  1018. {
  1019. busEntry.reset( new SCH_BUS_BUS_ENTRY );
  1020. if( !strCompare( "Bus", line, &line ) )
  1021. SCH_PARSE_ERROR( "invalid bus entry definition expected 'Bus'", aReader, line );
  1022. }
  1023. else
  1024. SCH_PARSE_ERROR( "invalid bus entry type", aReader, line );
  1025. line = aReader.ReadLine();
  1026. wxPoint pos;
  1027. wxSize size;
  1028. pos.x = parseInt( aReader, line, &line );
  1029. pos.y = parseInt( aReader, line, &line );
  1030. size.x = parseInt( aReader, line, &line );
  1031. size.y = parseInt( aReader, line, &line );
  1032. size.x -= pos.x;
  1033. size.y -= pos.y;
  1034. busEntry->SetPosition( pos );
  1035. busEntry->SetSize( size );
  1036. return busEntry.release();
  1037. }
  1038. SCH_TEXT* SCH_LEGACY_PLUGIN::loadText( FILE_LINE_READER& aReader )
  1039. {
  1040. const char* line = aReader.Line();
  1041. wxCHECK( strCompare( "Text", line, &line ), NULL );
  1042. std::unique_ptr< SCH_TEXT> text;
  1043. if( strCompare( "Notes", line, &line ) )
  1044. text.reset( new SCH_TEXT );
  1045. else if( strCompare( "Label", line, &line ) )
  1046. text.reset( new SCH_LABEL );
  1047. else if( strCompare( "HLabel", line, &line ) )
  1048. text.reset( new SCH_HIERLABEL );
  1049. else if( strCompare( "GLabel", line, &line ) )
  1050. {
  1051. // Prior to version 2, the SCH_GLOBALLABEL object did not exist.
  1052. if( m_version == 1 )
  1053. text.reset( new SCH_HIERLABEL );
  1054. else
  1055. text.reset( new SCH_GLOBALLABEL );
  1056. }
  1057. else
  1058. SCH_PARSE_ERROR( "unknown Text type", aReader, line );
  1059. // Parse the parameters common to all text objects.
  1060. wxPoint position;
  1061. position.x = parseInt( aReader, line, &line );
  1062. position.y = parseInt( aReader, line, &line );
  1063. text->SetPosition( position );
  1064. text->SetLabelSpinStyle( parseInt( aReader, line, &line ) );
  1065. int size = parseInt( aReader, line, &line );
  1066. text->SetTextSize( wxSize( size, size ) );
  1067. // Parse the global and hierarchical label type.
  1068. if( text->Type() == SCH_HIERARCHICAL_LABEL_T || text->Type() == SCH_GLOBAL_LABEL_T )
  1069. {
  1070. if( strCompare( SheetLabelType[NET_INPUT], line, &line ) )
  1071. text->SetShape( NET_INPUT );
  1072. else if( strCompare( SheetLabelType[NET_OUTPUT], line, &line ) )
  1073. text->SetShape( NET_OUTPUT );
  1074. else if( strCompare( SheetLabelType[NET_BIDI], line, &line ) )
  1075. text->SetShape( NET_BIDI );
  1076. else if( strCompare( SheetLabelType[NET_TRISTATE], line, &line ) )
  1077. text->SetShape( NET_TRISTATE );
  1078. else if( strCompare( SheetLabelType[NET_UNSPECIFIED], line, &line ) )
  1079. text->SetShape( NET_UNSPECIFIED );
  1080. else
  1081. SCH_PARSE_ERROR( "invalid label type", aReader, line );
  1082. }
  1083. int thickness = 0;
  1084. // The following tokens do not exist in version 1 schematic files,
  1085. // and not always in version 2 for HLabels and GLabels
  1086. if( m_version > 1 )
  1087. {
  1088. if( m_version > 2 || *line >= ' ' )
  1089. {
  1090. if( strCompare( "Italic", line, &line ) )
  1091. text->SetItalic( true );
  1092. else if( !strCompare( "~", line, &line ) )
  1093. SCH_PARSE_ERROR( _( "expected 'Italics' or '~'" ), aReader, line );
  1094. }
  1095. // The thickness token does not exist in older versions of the schematic file format
  1096. // so calling parseInt will be made only if the EOL is not reached.
  1097. if( *line >= ' ' )
  1098. thickness = parseInt( aReader, line, &line );
  1099. }
  1100. text->SetBold( thickness != 0 );
  1101. text->SetThickness( thickness != 0 ? GetPenSizeForBold( size ) : 0 );
  1102. // Read the text string for the text.
  1103. char* tmp = aReader.ReadLine();
  1104. tmp = strtok( tmp, "\r\n" );
  1105. wxString val = FROM_UTF8( tmp );
  1106. for( ; ; )
  1107. {
  1108. int i = val.find( wxT( "\\n" ) );
  1109. if( i == wxNOT_FOUND )
  1110. break;
  1111. val.erase( i, 2 );
  1112. val.insert( i, wxT( "\n" ) );
  1113. }
  1114. text->SetText( val );
  1115. return text.release();
  1116. }
  1117. SCH_COMPONENT* SCH_LEGACY_PLUGIN::loadComponent( FILE_LINE_READER& aReader )
  1118. {
  1119. const char* line = aReader.Line();
  1120. wxCHECK( strCompare( "$Comp", line, &line ), NULL );
  1121. std::unique_ptr< SCH_COMPONENT > component( new SCH_COMPONENT() );
  1122. line = aReader.ReadLine();
  1123. while( line != NULL )
  1124. {
  1125. if( strCompare( "L", line, &line ) )
  1126. {
  1127. wxString libName;
  1128. size_t pos = 2; // "X" plus ' ' space character.
  1129. wxString utf8Line = wxString::FromUTF8( line );
  1130. wxStringTokenizer tokens( utf8Line, " \r\n\t" );
  1131. if( tokens.CountTokens() < 2 )
  1132. THROW_PARSE_ERROR( "invalid symbol library definition", aReader.GetSource(),
  1133. aReader.Line(), aReader.LineNumber(), pos );
  1134. libName = tokens.GetNextToken();
  1135. libName.Replace( "~", " " );
  1136. LIB_ID libId;
  1137. // Prior to schematic version 4, library IDs did not have a library nickname so
  1138. // parsing the symbol name with LIB_ID::Parse() would break symbol library links
  1139. // that contained '/' and ':' characters.
  1140. if( m_version > 3 )
  1141. libId.Parse( libName, LIB_ID::ID_SCH, true );
  1142. else
  1143. libId.SetLibItemName( libName, false );
  1144. component->SetLibId( libId );
  1145. wxString refDesignator = tokens.GetNextToken();
  1146. refDesignator.Replace( "~", " " );
  1147. wxString prefix = refDesignator;
  1148. while( prefix.Length() )
  1149. {
  1150. if( ( prefix.Last() < '0' || prefix.Last() > '9') && prefix.Last() != '?' )
  1151. break;
  1152. prefix.RemoveLast();
  1153. }
  1154. // Avoid a prefix containing trailing/leading spaces
  1155. prefix.Trim( true );
  1156. prefix.Trim( false );
  1157. if( prefix.IsEmpty() )
  1158. component->SetPrefix( wxString( "U" ) );
  1159. else
  1160. component->SetPrefix( prefix );
  1161. }
  1162. else if( strCompare( "U", line, &line ) )
  1163. {
  1164. // This fixes a potentially buggy files caused by unit being set to zero which
  1165. // causes netlist issues. See https://bugs.launchpad.net/kicad/+bug/1677282.
  1166. int unit = parseInt( aReader, line, &line );
  1167. if( unit == 0 )
  1168. {
  1169. unit = 1;
  1170. // Set the file as modified so the user can be warned.
  1171. if( m_rootSheet && m_rootSheet->GetScreen() )
  1172. m_rootSheet->GetScreen()->SetModify();
  1173. }
  1174. component->SetUnit( unit );
  1175. // Same can also happen with the convert parameter
  1176. int convert = parseInt( aReader, line, &line );
  1177. if( convert == 0 )
  1178. {
  1179. convert = 1;
  1180. // Set the file as modified so the user can be warned.
  1181. if( m_rootSheet && m_rootSheet->GetScreen() )
  1182. m_rootSheet->GetScreen()->SetModify();
  1183. }
  1184. component->SetConvert( convert );
  1185. component->SetTimeStamp( parseHex( aReader, line, &line ) );
  1186. }
  1187. else if( strCompare( "P", line, &line ) )
  1188. {
  1189. wxPoint pos;
  1190. pos.x = parseInt( aReader, line, &line );
  1191. pos.y = parseInt( aReader, line, &line );
  1192. component->SetPosition( pos );
  1193. }
  1194. else if( strCompare( "AR", line, &line ) )
  1195. {
  1196. const char* strCompare = "Path=";
  1197. int len = strlen( strCompare );
  1198. if( strncasecmp( strCompare, line, len ) != 0 )
  1199. SCH_PARSE_ERROR( "missing 'Path=' token", aReader, line );
  1200. line += len;
  1201. wxString path, reference, unit;
  1202. parseQuotedString( path, aReader, line, &line );
  1203. strCompare = "Ref=";
  1204. len = strlen( strCompare );
  1205. if( strncasecmp( strCompare, line, len ) != 0 )
  1206. SCH_PARSE_ERROR( "missing 'Ref=' token", aReader, line );
  1207. line+= len;
  1208. parseQuotedString( reference, aReader, line, &line );
  1209. strCompare = "Part=";
  1210. len = strlen( strCompare );
  1211. if( strncasecmp( strCompare, line, len ) != 0 )
  1212. SCH_PARSE_ERROR( "missing 'Part=' token", aReader, line );
  1213. line+= len;
  1214. parseQuotedString( unit, aReader, line, &line );
  1215. long tmp;
  1216. if( !unit.ToLong( &tmp, 10 ) )
  1217. SCH_PARSE_ERROR( "expected integer value", aReader, line );
  1218. if( tmp < 0 || tmp > MAX_UNIT_COUNT_PER_PACKAGE )
  1219. SCH_PARSE_ERROR( "unit value out of range", aReader, line );
  1220. component->AddHierarchicalReference( path, reference, (int)tmp );
  1221. component->GetField( REFERENCE )->SetText( reference );
  1222. }
  1223. else if( strCompare( "F", line, &line ) )
  1224. {
  1225. int index = parseInt( aReader, line, &line );
  1226. wxString text, name;
  1227. parseQuotedString( text, aReader, line, &line, true );
  1228. char orientation = parseChar( aReader, line, &line );
  1229. wxPoint pos;
  1230. pos.x = parseInt( aReader, line, &line );
  1231. pos.y = parseInt( aReader, line, &line );
  1232. int size = parseInt( aReader, line, &line );
  1233. int attributes = parseHex( aReader, line, &line );
  1234. if( index >= component->GetFieldCount() )
  1235. {
  1236. // The first MANDATOR_FIELDS _must_ be constructed within
  1237. // the SCH_COMPONENT constructor. This assert is simply here
  1238. // to guard against a change in that constructor.
  1239. wxASSERT( component->GetFieldCount() >= MANDATORY_FIELDS );
  1240. // Ignore the _supplied_ fieldNdx. It is not important anymore
  1241. // if within the user defined fields region (i.e. >= MANDATORY_FIELDS).
  1242. // We freely renumber the index to fit the next available field slot.
  1243. index = component->GetFieldCount(); // new has this index after insertion
  1244. SCH_FIELD field( wxPoint( 0, 0 ), -1, component.get(), name );
  1245. component->AddField( field );
  1246. }
  1247. // Prior to version 2 of the schematic file format, none of the following existed.
  1248. if( m_version > 1 )
  1249. {
  1250. wxString textAttrs;
  1251. char hjustify = parseChar( aReader, line, &line );
  1252. parseUnquotedString( textAttrs, aReader, line, &line );
  1253. // The name of the field is optional.
  1254. parseQuotedString( name, aReader, line, &line, true );
  1255. if( hjustify == 'L' )
  1256. component->GetField( index )->SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT );
  1257. else if( hjustify == 'R' )
  1258. component->GetField( index )->SetHorizJustify( GR_TEXT_HJUSTIFY_RIGHT );
  1259. else if( hjustify != 'C' )
  1260. SCH_PARSE_ERROR( "component field text horizontal justification must be "
  1261. "L, R, or C", aReader, line );
  1262. // We are guaranteed to have a least one character here for older file formats
  1263. // otherwise an exception would have been raised..
  1264. if( textAttrs[0] == 'T' )
  1265. component->GetField( index )->SetVertJustify( GR_TEXT_VJUSTIFY_TOP );
  1266. else if( textAttrs[0] == 'B' )
  1267. component->GetField( index )->SetVertJustify( GR_TEXT_VJUSTIFY_BOTTOM );
  1268. else if( textAttrs[0] != 'C' )
  1269. SCH_PARSE_ERROR( "component field text vertical justification must be "
  1270. "B, T, or C", aReader, line );
  1271. // Newer file formats include the bold and italics text attribute.
  1272. if( textAttrs.Length() > 1 )
  1273. {
  1274. if( textAttrs.Length() != 3 )
  1275. SCH_PARSE_ERROR( _( "component field text attributes must be 3 characters wide" ),
  1276. aReader, line );
  1277. if( textAttrs[1] == 'I' )
  1278. component->GetField( index )->SetItalic( true );
  1279. else if( textAttrs[1] != 'N' )
  1280. SCH_PARSE_ERROR( "component field text italics indicator must be I or N",
  1281. aReader, line );
  1282. if( textAttrs[2] == 'B' )
  1283. component->GetField( index )->SetBold( true );
  1284. else if( textAttrs[2] != 'N' )
  1285. SCH_PARSE_ERROR( "component field text bold indicator must be B or N",
  1286. aReader, line );
  1287. }
  1288. }
  1289. component->GetField( index )->SetText( text );
  1290. component->GetField( index )->SetTextPos( pos );
  1291. component->GetField( index )->SetVisible( !attributes );
  1292. component->GetField( index )->SetTextSize( wxSize( size, size ) );
  1293. if( orientation == 'H' )
  1294. component->GetField( index )->SetTextAngle( TEXT_ANGLE_HORIZ );
  1295. else if( orientation == 'V' )
  1296. component->GetField( index )->SetTextAngle( TEXT_ANGLE_VERT );
  1297. else
  1298. SCH_PARSE_ERROR( "component field orientation must be H or V",
  1299. aReader, line );
  1300. if( name.IsEmpty() )
  1301. name = TEMPLATE_FIELDNAME::GetDefaultFieldName( index );
  1302. component->GetField( index )->SetName( name );
  1303. }
  1304. else if( strCompare( "$EndComp", line ) )
  1305. {
  1306. // Ensure all flags (some are set by previous initializations) are reset:
  1307. component->ClearFlags();
  1308. return component.release();
  1309. }
  1310. else
  1311. {
  1312. // There are two lines that begin with a tab or spaces that includes a line with the
  1313. // redundant position information and the transform matrix settings.
  1314. // Parse the redundant position information just the same to check for formatting
  1315. // errors.
  1316. parseInt( aReader, line, &line ); // Always 1.
  1317. parseInt( aReader, line, &line ); // The X coordinate.
  1318. parseInt( aReader, line, &line ); // The Y coordinate.
  1319. line = aReader.ReadLine();
  1320. TRANSFORM transform;
  1321. transform.x1 = parseInt( aReader, line, &line );
  1322. if( transform.x1 < -1 || transform.x1 > 1 )
  1323. SCH_PARSE_ERROR( "invalid component X1 transform value", aReader, line );
  1324. transform.y1 = parseInt( aReader, line, &line );
  1325. if( transform.y1 < -1 || transform.y1 > 1 )
  1326. SCH_PARSE_ERROR( "invalid component Y1 transform value", aReader, line );
  1327. transform.x2 = parseInt( aReader, line, &line );
  1328. if( transform.x2 < -1 || transform.x2 > 1 )
  1329. SCH_PARSE_ERROR( "invalid component X2 transform value", aReader, line );
  1330. transform.y2 = parseInt( aReader, line, &line );
  1331. if( transform.y2 < -1 || transform.y2 > 1 )
  1332. SCH_PARSE_ERROR( "invalid component Y2 transform value", aReader, line );
  1333. component->SetTransform( transform );
  1334. }
  1335. line = aReader.ReadLine();
  1336. }
  1337. SCH_PARSE_ERROR( "invalid component line", aReader, line );
  1338. return NULL; // Prevents compiler warning. Should never get here.
  1339. }
  1340. void SCH_LEGACY_PLUGIN::Save( const wxString& aFileName, SCH_SCREEN* aScreen, KIWAY* aKiway,
  1341. const PROPERTIES* aProperties )
  1342. {
  1343. wxCHECK_RET( aScreen != NULL, "NULL SCH_SCREEN object." );
  1344. wxCHECK_RET( !aFileName.IsEmpty(), "No schematic file name defined." );
  1345. LOCALE_IO toggle; // toggles on, then off, the C locale, to write floating point values.
  1346. init( aKiway, aProperties );
  1347. wxFileName fn = aFileName;
  1348. // File names should be absolute. Don't assume everything relative to the project path
  1349. // works properly.
  1350. wxASSERT( fn.IsAbsolute() );
  1351. FILE_OUTPUTFORMATTER formatter( fn.GetFullPath() );
  1352. m_out = &formatter; // no ownership
  1353. Format( aScreen );
  1354. }
  1355. void SCH_LEGACY_PLUGIN::Format( SCH_SCREEN* aScreen )
  1356. {
  1357. wxCHECK_RET( aScreen != NULL, "NULL SCH_SCREEN* object." );
  1358. wxCHECK_RET( m_kiway != NULL, "NULL KIWAY* object." );
  1359. // Write the header
  1360. m_out->Print( 0, "%s %s %d\n", "EESchema", SCHEMATIC_HEAD_STRING, EESCHEMA_VERSION );
  1361. // Write the project libraries.
  1362. for( const PART_LIB& lib : *m_kiway->Prj().SchLibs() )
  1363. m_out->Print( 0, "LIBS:%s\n", TO_UTF8( lib.GetName() ) );
  1364. // This section is not used, but written for file compatibility
  1365. m_out->Print( 0, "EELAYER %d %d\n", SCH_LAYER_ID_COUNT, 0 );
  1366. m_out->Print( 0, "EELAYER END\n" );
  1367. /* Write page info, ScreenNumber and NumberOfScreen; not very meaningful for
  1368. * SheetNumber and Sheet Count in a complex hierarchy, but useful in
  1369. * simple hierarchy and flat hierarchy. Used also to search the root
  1370. * sheet ( ScreenNumber = 1 ) within the files
  1371. */
  1372. const TITLE_BLOCK& tb = aScreen->GetTitleBlock();
  1373. const PAGE_INFO& page = aScreen->GetPageSettings();
  1374. m_out->Print( 0, "$Descr %s %d %d%s\n", TO_UTF8( page.GetType() ),
  1375. page.GetWidthMils(),
  1376. page.GetHeightMils(),
  1377. !page.IsCustom() && page.IsPortrait() ? " portrait" : "" );
  1378. m_out->Print( 0, "encoding utf-8\n" );
  1379. m_out->Print( 0, "Sheet %d %d\n", aScreen->m_ScreenNumber, aScreen->m_NumberOfScreens );
  1380. m_out->Print( 0, "Title %s\n", EscapedUTF8( tb.GetTitle() ).c_str() );
  1381. m_out->Print( 0, "Date %s\n", EscapedUTF8( tb.GetDate() ).c_str() );
  1382. m_out->Print( 0, "Rev %s\n", EscapedUTF8( tb.GetRevision() ).c_str() );
  1383. m_out->Print( 0, "Comp %s\n", EscapedUTF8( tb.GetCompany() ).c_str() );
  1384. m_out->Print( 0, "Comment1 %s\n", EscapedUTF8( tb.GetComment1() ).c_str() );
  1385. m_out->Print( 0, "Comment2 %s\n", EscapedUTF8( tb.GetComment2() ).c_str() );
  1386. m_out->Print( 0, "Comment3 %s\n", EscapedUTF8( tb.GetComment3() ).c_str() );
  1387. m_out->Print( 0, "Comment4 %s\n", EscapedUTF8( tb.GetComment4() ).c_str() );
  1388. m_out->Print( 0, "$EndDescr\n" );
  1389. for( SCH_ITEM* item = aScreen->GetDrawItems(); item; item = item->Next() )
  1390. {
  1391. switch( item->Type() )
  1392. {
  1393. case SCH_COMPONENT_T:
  1394. saveComponent( static_cast< SCH_COMPONENT* >( item ) );
  1395. break;
  1396. case SCH_BITMAP_T:
  1397. saveBitmap( static_cast< SCH_BITMAP* >( item ) );
  1398. break;
  1399. case SCH_SHEET_T:
  1400. saveSheet( static_cast< SCH_SHEET* >( item ) );
  1401. break;
  1402. case SCH_JUNCTION_T:
  1403. saveJunction( static_cast< SCH_JUNCTION* >( item ) );
  1404. break;
  1405. case SCH_NO_CONNECT_T:
  1406. saveNoConnect( static_cast< SCH_NO_CONNECT* >( item ) );
  1407. break;
  1408. case SCH_BUS_WIRE_ENTRY_T:
  1409. case SCH_BUS_BUS_ENTRY_T:
  1410. saveBusEntry( static_cast< SCH_BUS_ENTRY_BASE* >( item ) );
  1411. break;
  1412. case SCH_LINE_T:
  1413. saveLine( static_cast< SCH_LINE* >( item ) );
  1414. break;
  1415. case SCH_TEXT_T:
  1416. case SCH_LABEL_T:
  1417. case SCH_GLOBAL_LABEL_T:
  1418. case SCH_HIERARCHICAL_LABEL_T:
  1419. saveText( static_cast< SCH_TEXT* >( item ) );
  1420. break;
  1421. default:
  1422. wxASSERT( "Unexpected schematic object type in SCH_LEGACY_PLUGIN::Format()" );
  1423. }
  1424. }
  1425. m_out->Print( 0, "$EndSCHEMATC\n" );
  1426. }
  1427. void SCH_LEGACY_PLUGIN::saveComponent( SCH_COMPONENT* aComponent )
  1428. {
  1429. std::string name1;
  1430. std::string name2;
  1431. wxArrayString reference_fields;
  1432. static wxString delimiters( wxT( " " ) );
  1433. // This is redundant with the AR entries below, but it makes the files backwards-compatible.
  1434. if( aComponent->GetPathsAndReferences().GetCount() > 0 )
  1435. {
  1436. reference_fields = wxStringTokenize( aComponent->GetPathsAndReferences()[0], delimiters );
  1437. name1 = toUTFTildaText( reference_fields[1] );
  1438. }
  1439. else
  1440. {
  1441. if( aComponent->GetField( REFERENCE )->GetText().IsEmpty() )
  1442. name1 = toUTFTildaText( aComponent->GetPrefix() );
  1443. else
  1444. name1 = toUTFTildaText( aComponent->GetField( REFERENCE )->GetText() );
  1445. }
  1446. wxString part_name = aComponent->GetLibId().Format();
  1447. if( part_name.size() )
  1448. {
  1449. name2 = toUTFTildaText( part_name );
  1450. }
  1451. else
  1452. {
  1453. name2 = "_NONAME_";
  1454. }
  1455. m_out->Print( 0, "$Comp\n" );
  1456. m_out->Print( 0, "L %s %s\n", name2.c_str(), name1.c_str() );
  1457. // Generate unit number, convert and time stamp
  1458. m_out->Print( 0, "U %d %d %8.8X\n", aComponent->GetUnit(), aComponent->GetConvert(),
  1459. aComponent->GetTimeStamp() );
  1460. // Save the position
  1461. m_out->Print( 0, "P %d %d\n", aComponent->GetPosition().x, aComponent->GetPosition().y );
  1462. /* If this is a complex hierarchy; save hierarchical references.
  1463. * but for simple hierarchies it is not necessary.
  1464. * the reference inf is already saved
  1465. * this is useful for old Eeschema version compatibility
  1466. */
  1467. if( aComponent->GetPathsAndReferences().GetCount() > 1 )
  1468. {
  1469. for( unsigned int ii = 0; ii < aComponent->GetPathsAndReferences().GetCount(); ii++ )
  1470. {
  1471. /*format:
  1472. * AR Path="/140/2" Ref="C99" Part="1"
  1473. * where 140 is the uid of the containing sheet
  1474. * and 2 is the timestamp of this component.
  1475. * (timestamps are actually 8 hex chars)
  1476. * Ref is the conventional component reference for this 'path'
  1477. * Part is the conventional component part selection for this 'path'
  1478. */
  1479. reference_fields = wxStringTokenize( aComponent->GetPathsAndReferences()[ii],
  1480. delimiters );
  1481. m_out->Print( 0, "AR Path=\"%s\" Ref=\"%s\" Part=\"%s\" \n",
  1482. TO_UTF8( reference_fields[0] ),
  1483. TO_UTF8( reference_fields[1] ),
  1484. TO_UTF8( reference_fields[2] ) );
  1485. }
  1486. }
  1487. // update the ugly field index, which I would like to see go away someday soon.
  1488. for( int i = 0; i < aComponent->GetFieldCount(); ++i )
  1489. aComponent->GetField( i )->SetId( i );
  1490. // Fixed fields:
  1491. // Save mandatory fields even if they are blank,
  1492. // because the visibility, size and orientation are set from libary editor.
  1493. for( unsigned i = 0; i < MANDATORY_FIELDS; ++i )
  1494. saveField( aComponent->GetField( i ) );
  1495. // User defined fields:
  1496. // The *policy* about which user defined fields are part of a symbol is now
  1497. // only in the dialog editors. No policy should be enforced here, simply
  1498. // save all the user defined fields, they are present because a dialog editor
  1499. // thought they should be. If you disagree, go fix the dialog editors.
  1500. for( int i = MANDATORY_FIELDS; i < aComponent->GetFieldCount(); ++i )
  1501. saveField( aComponent->GetField( i ) );
  1502. // Unit number, position, box ( old standard )
  1503. m_out->Print( 0, "\t%-4d %-4d %-4d\n", aComponent->GetUnit(), aComponent->GetPosition().x,
  1504. aComponent->GetPosition().y );
  1505. TRANSFORM transform = aComponent->GetTransform();
  1506. m_out->Print( 0, "\t%-4d %-4d %-4d %-4d\n",
  1507. transform.x1, transform.y1, transform.x2, transform.y2 );
  1508. m_out->Print( 0, "$EndComp\n" );
  1509. }
  1510. void SCH_LEGACY_PLUGIN::saveField( SCH_FIELD* aField )
  1511. {
  1512. char hjustify = 'C';
  1513. if( aField->GetHorizJustify() == GR_TEXT_HJUSTIFY_LEFT )
  1514. hjustify = 'L';
  1515. else if( aField->GetHorizJustify() == GR_TEXT_HJUSTIFY_RIGHT )
  1516. hjustify = 'R';
  1517. char vjustify = 'C';
  1518. if( aField->GetVertJustify() == GR_TEXT_VJUSTIFY_BOTTOM )
  1519. vjustify = 'B';
  1520. else if( aField->GetVertJustify() == GR_TEXT_VJUSTIFY_TOP )
  1521. vjustify = 'T';
  1522. m_out->Print( 0, "F %d %s %c %-3d %-3d %-3d %4.4X %c %c%c%c",
  1523. aField->GetId(),
  1524. EscapedUTF8( aField->GetText() ).c_str(), // wraps in quotes too
  1525. aField->GetTextAngle() == TEXT_ANGLE_HORIZ ? 'H' : 'V',
  1526. aField->GetLibPosition().x, aField->GetLibPosition().y,
  1527. aField->GetTextWidth(),
  1528. !aField->IsVisible(),
  1529. hjustify, vjustify,
  1530. aField->IsItalic() ? 'I' : 'N',
  1531. aField->IsBold() ? 'B' : 'N' );
  1532. // Save field name, if the name is user definable
  1533. if( aField->GetId() >= FIELD1 )
  1534. {
  1535. m_out->Print( 0, " %s", EscapedUTF8( aField->GetName() ).c_str() );
  1536. }
  1537. m_out->Print( 0, "\n" );
  1538. }
  1539. void SCH_LEGACY_PLUGIN::saveBitmap( SCH_BITMAP* aBitmap )
  1540. {
  1541. wxCHECK_RET( aBitmap != NULL, "SCH_BITMAP* is NULL" );
  1542. wxImage* image = aBitmap->GetImage()->GetImageData();
  1543. wxCHECK_RET( image != NULL, "wxImage* is NULL" );
  1544. m_out->Print( 0, "$Bitmap\n" );
  1545. m_out->Print( 0, "Pos %-4d %-4d\n", aBitmap->GetPosition().x, aBitmap->GetPosition().y );
  1546. m_out->Print( 0, "Scale %f\n", aBitmap->GetImage()->GetScale() );
  1547. m_out->Print( 0, "Data\n" );
  1548. wxMemoryOutputStream stream;
  1549. image->SaveFile( stream, wxBITMAP_TYPE_PNG );
  1550. // Write binary data in hexadecimal form (ASCII)
  1551. wxStreamBuffer* buffer = stream.GetOutputStreamBuffer();
  1552. char* begin = (char*) buffer->GetBufferStart();
  1553. for( int ii = 0; begin < buffer->GetBufferEnd(); begin++, ii++ )
  1554. {
  1555. if( ii >= 32 )
  1556. {
  1557. ii = 0;
  1558. m_out->Print( 0, "\n" );
  1559. }
  1560. m_out->Print( 0, "%2.2X ", *begin & 0xFF );
  1561. }
  1562. m_out->Print( 0, "\nEndData\n" );
  1563. m_out->Print( 0, "$EndBitmap\n" );
  1564. }
  1565. void SCH_LEGACY_PLUGIN::saveSheet( SCH_SHEET* aSheet )
  1566. {
  1567. wxCHECK_RET( aSheet != NULL, "SCH_SHEET* is NULL" );
  1568. m_out->Print( 0, "$Sheet\n" );
  1569. m_out->Print( 0, "S %-4d %-4d %-4d %-4d\n",
  1570. aSheet->GetPosition().x, aSheet->GetPosition().y,
  1571. aSheet->GetSize().x, aSheet->GetSize().y );
  1572. m_out->Print( 0, "U %8.8X\n", aSheet->GetTimeStamp() );
  1573. if( !aSheet->GetName().IsEmpty() )
  1574. m_out->Print( 0, "F0 %s %d\n", EscapedUTF8( aSheet->GetName() ).c_str(),
  1575. aSheet->GetSheetNameSize() );
  1576. if( !aSheet->GetFileName().IsEmpty() )
  1577. m_out->Print( 0, "F1 %s %d\n", EscapedUTF8( aSheet->GetFileName() ).c_str(),
  1578. aSheet->GetFileNameSize() );
  1579. for( const SCH_SHEET_PIN& pin : aSheet->GetPins() )
  1580. {
  1581. int type, side;
  1582. if( pin.GetText().IsEmpty() )
  1583. break;
  1584. switch( pin.GetEdge() )
  1585. {
  1586. default:
  1587. case SCH_SHEET_PIN::SHEET_LEFT_SIDE:
  1588. side = 'L';
  1589. break;
  1590. case SCH_SHEET_PIN::SHEET_RIGHT_SIDE:
  1591. side = 'R';
  1592. break;
  1593. case SCH_SHEET_PIN::SHEET_TOP_SIDE:
  1594. side = 'T';
  1595. break;
  1596. case SCH_SHEET_PIN::SHEET_BOTTOM_SIDE:
  1597. side = 'B';
  1598. break;
  1599. }
  1600. switch( pin.GetShape() )
  1601. {
  1602. case NET_INPUT:
  1603. type = 'I'; break;
  1604. case NET_OUTPUT:
  1605. type = 'O'; break;
  1606. case NET_BIDI:
  1607. type = 'B'; break;
  1608. case NET_TRISTATE:
  1609. type = 'T'; break;
  1610. default:
  1611. case NET_UNSPECIFIED:
  1612. type = 'U'; break;
  1613. }
  1614. m_out->Print( 0, "F%d %s %c %c %-3d %-3d %-3d\n", pin.GetNumber(),
  1615. EscapedUTF8( pin.GetText() ).c_str(), // supplies wrapping quotes
  1616. type, side, pin.GetPosition().x, pin.GetPosition().y,
  1617. pin.GetTextWidth() );
  1618. }
  1619. m_out->Print( 0, "$EndSheet\n" );
  1620. }
  1621. void SCH_LEGACY_PLUGIN::saveJunction( SCH_JUNCTION* aJunction )
  1622. {
  1623. wxCHECK_RET( aJunction != NULL, "SCH_JUNCTION* is NULL" );
  1624. m_out->Print( 0, "Connection ~ %-4d %-4d\n",
  1625. aJunction->GetPosition().x, aJunction->GetPosition().y );
  1626. }
  1627. void SCH_LEGACY_PLUGIN::saveNoConnect( SCH_NO_CONNECT* aNoConnect )
  1628. {
  1629. wxCHECK_RET( aNoConnect != NULL, "SCH_NOCONNECT* is NULL" );
  1630. m_out->Print( 0, "NoConn ~ %-4d %-4d\n", aNoConnect->GetPosition().x,
  1631. aNoConnect->GetPosition().y );
  1632. }
  1633. void SCH_LEGACY_PLUGIN::saveBusEntry( SCH_BUS_ENTRY_BASE* aBusEntry )
  1634. {
  1635. wxCHECK_RET( aBusEntry != NULL, "SCH_BUS_ENTRY_BASE* is NULL" );
  1636. if( aBusEntry->GetLayer() == LAYER_WIRE )
  1637. m_out->Print( 0, "Entry Wire Line\n\t%-4d %-4d %-4d %-4d\n",
  1638. aBusEntry->GetPosition().x, aBusEntry->GetPosition().y,
  1639. aBusEntry->m_End().x, aBusEntry->m_End().y );
  1640. else
  1641. m_out->Print( 0, "Entry Bus Bus\n\t%-4d %-4d %-4d %-4d\n",
  1642. aBusEntry->GetPosition().x, aBusEntry->GetPosition().y,
  1643. aBusEntry->m_End().x, aBusEntry->m_End().y );
  1644. }
  1645. void SCH_LEGACY_PLUGIN::saveLine( SCH_LINE* aLine )
  1646. {
  1647. wxCHECK_RET( aLine != NULL, "SCH_LINE* is NULL" );
  1648. const char* layer = "Notes";
  1649. const char* width = "Line";
  1650. if( aLine->GetLayer() == LAYER_WIRE )
  1651. layer = "Wire";
  1652. else if( aLine->GetLayer() == LAYER_BUS )
  1653. layer = "Bus";
  1654. m_out->Print( 0, "Wire %s %s", layer, width );
  1655. // Write line style (width, type, color) only for non default values
  1656. if( aLine->GetLayer() == LAYER_NOTES )
  1657. {
  1658. if( aLine->GetPenSize() != aLine->GetDefaultWidth() )
  1659. m_out->Print( 0, " %s %d", T_WIDTH, aLine->GetLineSize() );
  1660. if( aLine->GetLineStyle() != aLine->GetDefaultStyle() )
  1661. m_out->Print( 0, " %s %s", T_STYLE, SCH_LINE::GetLineStyleName( aLine->GetLineStyle() ) );
  1662. if( aLine->GetLineColor() != aLine->GetDefaultColor() )
  1663. m_out->Print( 0, " %s",
  1664. TO_UTF8( aLine->GetLineColor().ToColour().GetAsString( wxC2S_CSS_SYNTAX ) ) );
  1665. }
  1666. m_out->Print( 0, "\n" );
  1667. m_out->Print( 0, "\t%-4d %-4d %-4d %-4d",
  1668. aLine->GetStartPoint().x, aLine->GetStartPoint().y,
  1669. aLine->GetEndPoint().x, aLine->GetEndPoint().y );
  1670. m_out->Print( 0, "\n");
  1671. }
  1672. void SCH_LEGACY_PLUGIN::saveText( SCH_TEXT* aText )
  1673. {
  1674. wxCHECK_RET( aText != NULL, "SCH_TEXT* is NULL" );
  1675. const char* italics = "~";
  1676. const char* textType = "Notes";
  1677. if( aText->IsItalic() )
  1678. italics = "Italic";
  1679. wxString text = aText->GetText();
  1680. SCH_LAYER_ID layer = aText->GetLayer();
  1681. if( layer == LAYER_NOTES || layer == LAYER_LOCLABEL )
  1682. {
  1683. if( layer == LAYER_NOTES )
  1684. {
  1685. // For compatibility reasons, the text must be saved in only one text line
  1686. // so replace all EOLs with \\n
  1687. text.Replace( wxT( "\n" ), wxT( "\\n" ) );
  1688. // Here we should have no CR or LF character in line
  1689. // This is not always the case if a multiline text was copied (using a copy/paste
  1690. // function) from a text that uses E.O.L characters that differs from the current
  1691. // EOL format. This is mainly the case under Linux using LF symbol when copying
  1692. // a text from Windows (using CRLF symbol) so we must just remove the extra CR left
  1693. // (or LF left under MacOSX)
  1694. for( unsigned ii = 0; ii < text.Len(); )
  1695. {
  1696. if( text[ii] == 0x0A || text[ii] == 0x0D )
  1697. text.erase( ii, 1 );
  1698. else
  1699. ii++;
  1700. }
  1701. }
  1702. else
  1703. {
  1704. textType = "Label";
  1705. }
  1706. m_out->Print( 0, "Text %s %-4d %-4d %-4d %-4d %s %d\n%s\n", textType,
  1707. aText->GetPosition().x, aText->GetPosition().y,
  1708. aText->GetLabelSpinStyle(),
  1709. aText->GetTextWidth(),
  1710. italics, aText->GetThickness(), TO_UTF8( text ) );
  1711. }
  1712. else if( layer == LAYER_GLOBLABEL || layer == LAYER_HIERLABEL )
  1713. {
  1714. textType = ( layer == LAYER_GLOBLABEL ) ? "GLabel" : "HLabel";
  1715. m_out->Print( 0, "Text %s %-4d %-4d %-4d %-4d %s %s %d\n%s\n", textType,
  1716. aText->GetPosition().x, aText->GetPosition().y,
  1717. aText->GetLabelSpinStyle(),
  1718. aText->GetTextWidth(),
  1719. SheetLabelType[aText->GetShape()],
  1720. italics,
  1721. aText->GetThickness(), TO_UTF8( text ) );
  1722. }
  1723. }
  1724. int SCH_LEGACY_PLUGIN_CACHE::m_modHash = 1; // starts at 1 and goes up
  1725. SCH_LEGACY_PLUGIN_CACHE::SCH_LEGACY_PLUGIN_CACHE( const wxString& aFullPathAndFileName ) :
  1726. m_fileName( aFullPathAndFileName ),
  1727. m_libFileName( aFullPathAndFileName ),
  1728. m_isWritable( true ),
  1729. m_isModified( false )
  1730. {
  1731. m_versionMajor = -1;
  1732. m_versionMinor = -1;
  1733. m_libType = LIBRARY_TYPE_EESCHEMA;
  1734. }
  1735. SCH_LEGACY_PLUGIN_CACHE::~SCH_LEGACY_PLUGIN_CACHE()
  1736. {
  1737. // When the cache is destroyed, all of the alias objects on the heap should be deleted.
  1738. for( LIB_ALIAS_MAP::iterator it = m_aliases.begin(); it != m_aliases.end(); ++it )
  1739. {
  1740. wxLogTrace( traceSchLegacyPlugin, wxT( "Removing alias %s from library %s." ),
  1741. GetChars( it->second->GetName() ), GetChars( GetLogicalName() ) );
  1742. LIB_PART* part = it->second->GetPart();
  1743. LIB_ALIAS* alias = it->second;
  1744. delete alias;
  1745. // When the last alias of a part is destroyed, the part is no longer required and it
  1746. // too is destroyed.
  1747. if( part && part->GetAliasCount() == 0 )
  1748. delete part;
  1749. }
  1750. m_aliases.clear();
  1751. }
  1752. // If m_libFileName is a symlink follow it to the real source file
  1753. wxFileName SCH_LEGACY_PLUGIN_CACHE::GetRealFile() const
  1754. {
  1755. wxFileName fn( m_libFileName );
  1756. #ifndef __WINDOWS__
  1757. if( fn.Exists( wxFILE_EXISTS_SYMLINK ) )
  1758. {
  1759. char buffer[ PATH_MAX + 1 ];
  1760. ssize_t pathLen = readlink( TO_UTF8( fn.GetFullPath() ), buffer, PATH_MAX );
  1761. if( pathLen > 0 )
  1762. {
  1763. buffer[ pathLen ] = '\0';
  1764. fn.Assign( fn.GetPath() + wxT( "/" ) + wxString::FromUTF8( buffer ) );
  1765. fn.Normalize();
  1766. }
  1767. }
  1768. #endif
  1769. return fn;
  1770. }
  1771. wxDateTime SCH_LEGACY_PLUGIN_CACHE::GetLibModificationTime()
  1772. {
  1773. wxFileName fn = GetRealFile();
  1774. // update the writable flag while we have a wxFileName, in a network this
  1775. // is possibly quite dynamic anyway.
  1776. m_isWritable = fn.IsFileWritable();
  1777. return fn.GetModificationTime();
  1778. }
  1779. bool SCH_LEGACY_PLUGIN_CACHE::IsFile( const wxString& aFullPathAndFileName ) const
  1780. {
  1781. return m_fileName == aFullPathAndFileName;
  1782. }
  1783. bool SCH_LEGACY_PLUGIN_CACHE::IsFileChanged() const
  1784. {
  1785. wxFileName fn = GetRealFile();
  1786. if( m_fileModTime.IsValid() && fn.IsOk() && fn.FileExists() )
  1787. return fn.GetModificationTime() != m_fileModTime;
  1788. return false;
  1789. }
  1790. LIB_ALIAS* SCH_LEGACY_PLUGIN_CACHE::removeAlias( LIB_ALIAS* aAlias )
  1791. {
  1792. wxCHECK_MSG( aAlias != NULL, NULL, "NULL pointer cannot be removed from library." );
  1793. LIB_ALIAS_MAP::iterator it = m_aliases.find( aAlias->GetName() );
  1794. if( it == m_aliases.end() )
  1795. return NULL;
  1796. // If the entry pointer doesn't match the name it is mapped to in the library, we
  1797. // have done something terribly wrong.
  1798. wxCHECK_MSG( *it->second == aAlias, NULL,
  1799. "Pointer mismatch while attempting to remove alias entry <" + aAlias->GetName() +
  1800. "> from library cache <" + m_libFileName.GetName() + ">." );
  1801. LIB_ALIAS* alias = aAlias;
  1802. LIB_PART* part = alias->GetPart();
  1803. alias = part->RemoveAlias( alias );
  1804. if( !alias )
  1805. {
  1806. delete part;
  1807. if( m_aliases.size() > 1 )
  1808. {
  1809. LIB_ALIAS_MAP::iterator next = it;
  1810. next++;
  1811. if( next == m_aliases.end() )
  1812. next = m_aliases.begin();
  1813. alias = next->second;
  1814. }
  1815. }
  1816. m_aliases.erase( it );
  1817. m_isModified = true;
  1818. ++m_modHash;
  1819. return alias;
  1820. }
  1821. void SCH_LEGACY_PLUGIN_CACHE::AddSymbol( const LIB_PART* aPart )
  1822. {
  1823. // aPart is cloned in PART_LIB::AddPart(). The cache takes ownership of aPart.
  1824. wxArrayString aliasNames = aPart->GetAliasNames();
  1825. for( size_t i = 0; i < aliasNames.size(); i++ )
  1826. {
  1827. LIB_ALIAS_MAP::iterator it = m_aliases.find( aliasNames[i] );
  1828. if( it != m_aliases.end() )
  1829. removeAlias( it->second );
  1830. LIB_ALIAS* alias = const_cast< LIB_PART* >( aPart )->GetAlias( aliasNames[i] );
  1831. wxASSERT_MSG( alias != NULL, "No alias <" + aliasNames[i] + "> found in symbol <" +
  1832. aPart->GetName() +">." );
  1833. m_aliases[ aliasNames[i] ] = alias;
  1834. }
  1835. m_isModified = true;
  1836. ++m_modHash;
  1837. }
  1838. void SCH_LEGACY_PLUGIN_CACHE::Load()
  1839. {
  1840. if( !m_libFileName.FileExists() )
  1841. {
  1842. wxString msg = wxString::Format( _( "Library file \"%s\" not found.\n\n"
  1843. "Use the Manage Symbol Libraries dialog to fix the "
  1844. "path (or remove the library)." ),
  1845. m_libFileName.GetFullPath() );
  1846. KIDIALOG dlg( Pgm().App().GetTopWindow(), msg, KIDIALOG::KD_ERROR );
  1847. dlg.DoNotShowCheckbox( __FILE__, __LINE__ );
  1848. dlg.ShowModal();
  1849. return;
  1850. }
  1851. wxCHECK_RET( m_libFileName.IsAbsolute(),
  1852. wxString::Format( "Cannot use relative file paths in legacy plugin to "
  1853. "open library \"%s\".", m_libFileName.GetFullPath() ) );
  1854. wxLogTrace( traceSchLegacyPlugin, "Loading legacy symbol file \"%s\"",
  1855. m_libFileName.GetFullPath() );
  1856. FILE_LINE_READER reader( m_libFileName.GetFullPath() );
  1857. if( !reader.ReadLine() )
  1858. THROW_IO_ERROR( _( "unexpected end of file" ) );
  1859. const char* line = reader.Line();
  1860. if( !strCompare( "EESchema-LIBRARY Version", line, &line ) )
  1861. {
  1862. // Old .sym files (which are libraries with only one symbol, used to store and reuse shapes)
  1863. // EESchema-LIB Version x.x SYMBOL. They are valid files.
  1864. if( !strCompare( "EESchema-LIB Version", line, &line ) )
  1865. SCH_PARSE_ERROR( "file is not a valid component or symbol library file", reader, line );
  1866. }
  1867. m_versionMajor = parseInt( reader, line, &line );
  1868. if( *line != '.' )
  1869. SCH_PARSE_ERROR( "invalid file version formatting in header", reader, line );
  1870. line++;
  1871. m_versionMinor = parseInt( reader, line, &line );
  1872. if( m_versionMajor < 1 || m_versionMinor < 0 || m_versionMinor > 99 )
  1873. SCH_PARSE_ERROR( "invalid file version in header", reader, line );
  1874. // Check if this is a symbol library which is the same as a component library but without
  1875. // any alias, documentation, footprint filters, etc.
  1876. if( strCompare( "SYMBOL", line, &line ) )
  1877. {
  1878. // Symbol files add date and time stamp info to the header.
  1879. m_libType = LIBRARY_TYPE_SYMBOL;
  1880. /// @todo Probably should check for a valid date and time stamp even though it's not used.
  1881. }
  1882. else
  1883. {
  1884. m_libType = LIBRARY_TYPE_EESCHEMA;
  1885. }
  1886. while( reader.ReadLine() )
  1887. {
  1888. line = reader.Line();
  1889. if( *line == '#' || isspace( *line ) ) // Skip comments and blank lines.
  1890. continue;
  1891. // Headers where only supported in older library file formats.
  1892. if( m_libType == LIBRARY_TYPE_EESCHEMA && strCompare( "$HEADER", line ) )
  1893. loadHeader( reader );
  1894. if( strCompare( "DEF", line ) )
  1895. {
  1896. // Read one DEF/ENDDEF part entry from library:
  1897. LIB_PART * part = LoadPart( reader, m_versionMajor, m_versionMinor );
  1898. // Add aliases to cache
  1899. for( size_t ii = 0; ii < part->GetAliasCount(); ++ii )
  1900. {
  1901. LIB_ALIAS* alias = part->GetAlias( ii );
  1902. const wxString& aliasName = alias->GetName();
  1903. // This section seems to do a similar job as checkForDuplicates, so
  1904. // I'm not sure checkForDuplicates needs to be preserved.
  1905. auto it = m_aliases.find( aliasName );
  1906. if( it != m_aliases.end() )
  1907. {
  1908. // Find a new name for the alias
  1909. wxString newName;
  1910. int idx = 0;
  1911. LIB_ALIAS_MAP::const_iterator jt;
  1912. do
  1913. {
  1914. newName = wxString::Format( "%s_%d", aliasName, idx );
  1915. jt = m_aliases.find( newName );
  1916. ++idx;
  1917. }
  1918. while( jt != m_aliases.end() );
  1919. wxLogWarning( "Symbol name conflict in library:\n%s\n"
  1920. "'%s' has been renamed to '%s'",
  1921. m_fileName, aliasName, newName );
  1922. if( alias->IsRoot() )
  1923. part->SetName( newName );
  1924. else
  1925. alias->SetName( newName );
  1926. m_aliases[newName] = alias;
  1927. }
  1928. else
  1929. {
  1930. m_aliases[aliasName] = alias;
  1931. }
  1932. }
  1933. }
  1934. }
  1935. ++m_modHash;
  1936. // Remember the file modification time of library file when the
  1937. // cache snapshot was made, so that in a networked environment we will
  1938. // reload the cache as needed.
  1939. m_fileModTime = GetLibModificationTime();
  1940. if( USE_OLD_DOC_FILE_FORMAT( m_versionMajor, m_versionMinor ) )
  1941. loadDocs();
  1942. }
  1943. void SCH_LEGACY_PLUGIN_CACHE::loadDocs()
  1944. {
  1945. const char* line;
  1946. wxString text;
  1947. wxString aliasName;
  1948. wxFileName fn = m_libFileName;
  1949. LIB_ALIAS* alias = NULL;;
  1950. fn.SetExt( DOC_EXT );
  1951. // Not all libraries will have a document file.
  1952. if( !fn.FileExists() )
  1953. return;
  1954. if( !fn.IsFileReadable() )
  1955. THROW_IO_ERROR( wxString::Format( _( "user does not have permission to read library "
  1956. "document file \"%s\"" ), fn.GetFullPath() ) );
  1957. FILE_LINE_READER reader( fn.GetFullPath() );
  1958. line = reader.ReadLine();
  1959. if( !line )
  1960. THROW_IO_ERROR( _( "symbol document library file is empty" ) );
  1961. if( !strCompare( DOCFILE_IDENT, line, &line ) )
  1962. SCH_PARSE_ERROR( "invalid document library file version formatting in header",
  1963. reader, line );
  1964. while( reader.ReadLine() )
  1965. {
  1966. line = reader.Line();
  1967. if( *line == '#' ) // Comment line.
  1968. continue;
  1969. if( !strCompare( "$CMP", line, &line ) != 0 )
  1970. SCH_PARSE_ERROR( "$CMP command expected", reader, line );
  1971. aliasName = wxString::FromUTF8( line );
  1972. aliasName.Trim();
  1973. aliasName = LIB_ID::FixIllegalChars( aliasName, LIB_ID::ID_SCH );
  1974. LIB_ALIAS_MAP::iterator it = m_aliases.find( aliasName );
  1975. if( it == m_aliases.end() )
  1976. wxLogWarning( "Alias '%s' not found in library:\n\n"
  1977. "'%s'\n\nat line %d offset %d", aliasName, fn.GetFullPath(),
  1978. reader.LineNumber(), (int) (line - reader.Line() ) );
  1979. else
  1980. alias = it->second;
  1981. // Read the curent alias associated doc.
  1982. // if the alias does not exist, just skip the description
  1983. // (Can happen if a .dcm is not synchronized with the corresponding .lib file)
  1984. while( reader.ReadLine() )
  1985. {
  1986. line = reader.Line();
  1987. if( !line )
  1988. SCH_PARSE_ERROR( "unexpected end of file", reader, line );
  1989. if( strCompare( "$ENDCMP", line, &line ) )
  1990. break;
  1991. text = FROM_UTF8( line + 2 );
  1992. // Remove spaces at eol, and eol chars:
  1993. text = text.Trim();
  1994. switch( line[0] )
  1995. {
  1996. case 'D':
  1997. if( alias )
  1998. alias->SetDescription( text );
  1999. break;
  2000. case 'K':
  2001. if( alias )
  2002. alias->SetKeyWords( text );
  2003. break;
  2004. case 'F':
  2005. if( alias )
  2006. alias->SetDocFileName( text );
  2007. break;
  2008. case 0:
  2009. case '\n':
  2010. case '\r':
  2011. case '#':
  2012. // Empty line or commment
  2013. break;
  2014. default:
  2015. SCH_PARSE_ERROR( "expected token in symbol definition", reader, line );
  2016. }
  2017. }
  2018. }
  2019. }
  2020. void SCH_LEGACY_PLUGIN_CACHE::loadHeader( FILE_LINE_READER& aReader )
  2021. {
  2022. const char* line = aReader.Line();
  2023. wxASSERT( strCompare( "$HEADER", line, &line ) );
  2024. while( aReader.ReadLine() )
  2025. {
  2026. line = (char*) aReader;
  2027. // The time stamp saved in old library files is not used or saved in the latest
  2028. // library file version.
  2029. if( strCompare( "TimeStamp", line, &line ) )
  2030. continue;
  2031. else if( strCompare( "$ENDHEADER", line, &line ) )
  2032. return;
  2033. }
  2034. SCH_PARSE_ERROR( "$ENDHEADER not found", aReader, line );
  2035. }
  2036. LIB_PART* SCH_LEGACY_PLUGIN_CACHE::LoadPart( LINE_READER& aReader, int aMajorVersion,
  2037. int aMinorVersion )
  2038. {
  2039. const char* line = aReader.Line();
  2040. wxCHECK( strCompare( "DEF", line, &line ), NULL );
  2041. long num;
  2042. size_t pos = 4; // "DEF" plus the first space.
  2043. wxString utf8Line = wxString::FromUTF8( line );
  2044. wxStringTokenizer tokens( utf8Line, " \r\n\t" );
  2045. if( tokens.CountTokens() < 8 )
  2046. SCH_PARSE_ERROR( "invalid symbol definition", aReader, line );
  2047. // Read DEF line:
  2048. std::unique_ptr< LIB_PART > part( new LIB_PART( wxEmptyString ) );
  2049. wxString name, prefix, tmp;
  2050. name = tokens.GetNextToken();
  2051. pos += name.size() + 1;
  2052. prefix = tokens.GetNextToken();
  2053. pos += prefix.size() + 1;
  2054. tmp = tokens.GetNextToken();
  2055. pos += tmp.size() + 1; // NumOfPins, unused.
  2056. tmp = tokens.GetNextToken(); // Pin name offset.
  2057. if( !tmp.ToLong( &num ) )
  2058. THROW_PARSE_ERROR( "invalid pin offset", aReader.GetSource(), aReader.Line(),
  2059. aReader.LineNumber(), pos );
  2060. pos += tmp.size() + 1;
  2061. part->SetPinNameOffset( (int)num );
  2062. tmp = tokens.GetNextToken(); // Show pin numbers.
  2063. if( !( tmp == "Y" || tmp == "N") )
  2064. THROW_PARSE_ERROR( "expected Y or N", aReader.GetSource(), aReader.Line(),
  2065. aReader.LineNumber(), pos );
  2066. pos += tmp.size() + 1;
  2067. part->SetShowPinNumbers( ( tmp == "N" ) ? false : true );
  2068. tmp = tokens.GetNextToken(); // Show pin names.
  2069. if( !( tmp == "Y" || tmp == "N") )
  2070. THROW_PARSE_ERROR( "expected Y or N", aReader.GetSource(), aReader.Line(),
  2071. aReader.LineNumber(), pos );
  2072. pos += tmp.size() + 1;
  2073. part->SetShowPinNames( ( tmp == "N" ) ? false : true );
  2074. tmp = tokens.GetNextToken(); // Number of units.
  2075. if( !tmp.ToLong( &num ) )
  2076. THROW_PARSE_ERROR( "invalid unit count", aReader.GetSource(), aReader.Line(),
  2077. aReader.LineNumber(), pos );
  2078. pos += tmp.size() + 1;
  2079. part->SetUnitCount( (int)num );
  2080. // Ensure m_unitCount is >= 1. Could be read as 0 in old libraries.
  2081. if( part->GetUnitCount() < 1 )
  2082. part->SetUnitCount( 1 );
  2083. // Copy part name and prefix.
  2084. // The root alias is added to the alias list by SetName() which is called by SetText().
  2085. if( name.IsEmpty() )
  2086. {
  2087. part->SetName( "~" );
  2088. }
  2089. else if( name[0] != '~' )
  2090. {
  2091. part->SetName( name );
  2092. }
  2093. else
  2094. {
  2095. part->SetName( name.Right( name.Length() - 1 ) );
  2096. part->GetValueField().SetVisible( false );
  2097. }
  2098. // Don't set the library alias, this is determined by the symbol library table.
  2099. part->SetLibId( LIB_ID( wxEmptyString, part->GetName() ) );
  2100. // There are some code paths in SetText() that do not set the root alias to the
  2101. // alias list so add it here if it didn't get added by SetText().
  2102. if( !part->HasAlias( part->GetName() ) )
  2103. part->AddAlias( part->GetName() );
  2104. LIB_FIELD& reference = part->GetReferenceField();
  2105. if( prefix == "~" )
  2106. {
  2107. reference.Empty();
  2108. reference.SetVisible( false );
  2109. }
  2110. else
  2111. {
  2112. reference.SetText( prefix );
  2113. }
  2114. // In version 2.2 and earlier, this parameter was a '0' which was just a place holder.
  2115. // The was no concept of interchangeable multiple unit symbols.
  2116. if( LIB_VERSION( aMajorVersion, aMinorVersion ) > 0
  2117. && LIB_VERSION( aMajorVersion, aMinorVersion ) <= LIB_VERSION( 2, 2 ) )
  2118. {
  2119. // Nothing needs to be set since the default setting for symbols with multiple
  2120. // units were never interchangeable. Just parse the 0 an move on.
  2121. tmp = tokens.GetNextToken();
  2122. pos += tmp.size() + 1;
  2123. }
  2124. else
  2125. {
  2126. tmp = tokens.GetNextToken();
  2127. if( tmp == "L" )
  2128. part->LockUnits( true );
  2129. else if( tmp == "F" || tmp == "0" )
  2130. part->LockUnits( false );
  2131. else
  2132. THROW_PARSE_ERROR( "expected L, F, or 0", aReader.GetSource(), aReader.Line(),
  2133. aReader.LineNumber(), pos );
  2134. pos += tmp.size() + 1;
  2135. }
  2136. // There is the optional power component flag.
  2137. if( tokens.HasMoreTokens() )
  2138. {
  2139. tmp = tokens.GetNextToken();
  2140. if( tmp == "P" )
  2141. part->SetPower();
  2142. else if( tmp == "N" )
  2143. part->SetNormal();
  2144. else
  2145. THROW_PARSE_ERROR( "expected P or N", aReader.GetSource(), aReader.Line(),
  2146. aReader.LineNumber(), pos );
  2147. }
  2148. line = aReader.ReadLine();
  2149. // Read lines until "ENDDEF" is found.
  2150. while( line )
  2151. {
  2152. if( *line == '#' ) // Comment
  2153. ;
  2154. else if( strCompare( "Ti", line, &line ) ) // Modification date is ignored.
  2155. continue;
  2156. else if( strCompare( "ALIAS", line, &line ) ) // Aliases
  2157. loadAliases( part, aReader );
  2158. else if( *line == 'F' ) // Fields
  2159. loadField( part, aReader );
  2160. else if( strCompare( "DRAW", line, &line ) ) // Drawing objects.
  2161. loadDrawEntries( part, aReader, aMajorVersion, aMinorVersion );
  2162. else if( strCompare( "$FPLIST", line, &line ) ) // Footprint filter list
  2163. loadFootprintFilters( part, aReader );
  2164. else if( strCompare( "ENDDEF", line, &line ) ) // End of part description
  2165. {
  2166. return part.release();
  2167. }
  2168. line = aReader.ReadLine();
  2169. }
  2170. SCH_PARSE_ERROR( "missing ENDDEF", aReader, line );
  2171. }
  2172. #if 0
  2173. bool SCH_LEGACY_PLUGIN_CACHE::checkForDuplicates( wxString& aAliasName )
  2174. {
  2175. wxCHECK_MSG( !aAliasName.IsEmpty(), false, "alias name cannot be empty" );
  2176. // The alias name is not a duplicate so don't change it.
  2177. if( m_aliases.find( aAliasName ) == m_aliases.end() )
  2178. return false;
  2179. int dupCounter = 1;
  2180. wxString newAlias = aAliasName;
  2181. // If the alias is already loaded, the library is broken. It may have been possible in
  2182. // the past that this could happen so we assign a new alias name to prevent any conflicts
  2183. // rather than throw an exception.
  2184. while( m_aliases.find( newAlias ) != m_aliases.end() )
  2185. {
  2186. newAlias = aAliasName << dupCounter;
  2187. dupCounter++;
  2188. }
  2189. aAliasName = newAlias;
  2190. return true;
  2191. }
  2192. #endif
  2193. void SCH_LEGACY_PLUGIN_CACHE::loadAliases( std::unique_ptr< LIB_PART >& aPart,
  2194. LINE_READER& aReader )
  2195. {
  2196. wxString newAlias;
  2197. const char* line = aReader.Line();
  2198. wxCHECK_RET( strCompare( "ALIAS", line, &line ), "Invalid ALIAS section" );
  2199. wxString utf8Line = wxString::FromUTF8( line );
  2200. wxStringTokenizer tokens( utf8Line, " \r\n\t" );
  2201. // Parse the ALIAS list.
  2202. while( tokens.HasMoreTokens() )
  2203. {
  2204. newAlias = tokens.GetNextToken();
  2205. aPart->AddAlias( newAlias );
  2206. }
  2207. }
  2208. void SCH_LEGACY_PLUGIN_CACHE::loadField( std::unique_ptr< LIB_PART >& aPart,
  2209. LINE_READER& aReader )
  2210. {
  2211. const char* line = aReader.Line();
  2212. wxCHECK_RET( *line == 'F', "Invalid field line" );
  2213. int id;
  2214. if( sscanf( line + 1, "%d", &id ) != 1 || id < 0 )
  2215. SCH_PARSE_ERROR( "invalid field ID", aReader, line + 1 );
  2216. LIB_FIELD* field;
  2217. if( (unsigned) id < MANDATORY_FIELDS )
  2218. {
  2219. field = aPart->GetField( id );
  2220. // this will fire only if somebody broke a constructor or editor.
  2221. // MANDATORY_FIELDS are always present in ram resident components, no
  2222. // exceptions, and they always have their names set, even fixed fields.
  2223. wxASSERT( field );
  2224. }
  2225. else
  2226. {
  2227. field = new LIB_FIELD( aPart.get(), id );
  2228. aPart->AddDrawItem( field );
  2229. }
  2230. // Skip to the first double quote.
  2231. while( *line != '"' && *line != 0 )
  2232. line++;
  2233. if( *line == 0 )
  2234. SCH_PARSE_ERROR( _( "unexpected end of line" ), aReader, line );
  2235. parseQuotedString( field->m_Text, aReader, line, &line, true );
  2236. // Doctor the *.lib file field which has a "~" in blank fields. New saves will
  2237. // not save like this.
  2238. if( field->m_Text.size() == 1 && field->m_Text[0] == '~' )
  2239. field->m_Text.clear();
  2240. wxPoint pos;
  2241. pos.x = parseInt( aReader, line, &line );
  2242. pos.y = parseInt( aReader, line, &line );
  2243. field->SetPosition( pos );
  2244. wxSize textSize;
  2245. textSize.x = textSize.y = parseInt( aReader, line, &line );
  2246. field->SetTextSize( textSize );
  2247. char textOrient = parseChar( aReader, line, &line );
  2248. if( textOrient == 'H' )
  2249. field->SetTextAngle( TEXT_ANGLE_HORIZ );
  2250. else if( textOrient == 'V' )
  2251. field->SetTextAngle( TEXT_ANGLE_VERT );
  2252. else
  2253. SCH_PARSE_ERROR( "invalid field text orientation parameter", aReader, line );
  2254. char textVisible = parseChar( aReader, line, &line );
  2255. if( textVisible == 'V' )
  2256. field->SetVisible( true );
  2257. else if ( textVisible == 'I' )
  2258. field->SetVisible( false );
  2259. else
  2260. SCH_PARSE_ERROR( "invalid field text visibility parameter", aReader, line );
  2261. // It may be technically correct to use the library version to determine if the field text
  2262. // attributes are present. If anyone knows if that is valid and what version that would be,
  2263. // please change this to test the library version rather than an EOL or the quoted string
  2264. // of the field name.
  2265. if( *line != 0 && *line != '"' )
  2266. {
  2267. char textHJustify = parseChar( aReader, line, &line );
  2268. if( textHJustify == 'C' )
  2269. field->SetHorizJustify( GR_TEXT_HJUSTIFY_CENTER );
  2270. else if( textHJustify == 'L' )
  2271. field->SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT );
  2272. else if( textHJustify == 'R' )
  2273. field->SetHorizJustify( GR_TEXT_HJUSTIFY_RIGHT );
  2274. else
  2275. SCH_PARSE_ERROR( "invalid field text horizontal justification", aReader, line );
  2276. wxString attributes;
  2277. parseUnquotedString( attributes, aReader, line, &line );
  2278. size_t attrSize = attributes.size();
  2279. if( !(attrSize == 3 || attrSize == 1 ) )
  2280. SCH_PARSE_ERROR( "invalid field text attributes size", aReader, line );
  2281. switch( (wxChar) attributes[0] )
  2282. {
  2283. case 'C': field->SetVertJustify( GR_TEXT_VJUSTIFY_CENTER ); break;
  2284. case 'B': field->SetVertJustify( GR_TEXT_VJUSTIFY_BOTTOM ); break;
  2285. case 'T': field->SetVertJustify( GR_TEXT_VJUSTIFY_TOP ); break;
  2286. default: SCH_PARSE_ERROR( "invalid field text vertical justification", aReader, line );
  2287. }
  2288. if( attrSize == 3 )
  2289. {
  2290. wxChar attr_1 = attributes[1];
  2291. wxChar attr_2 = attributes[2];
  2292. if( attr_1 == 'I' ) // Italic
  2293. field->SetItalic( true );
  2294. else if( attr_1 != 'N' ) // No italics is default, check for error.
  2295. SCH_PARSE_ERROR( "invalid field text italic parameter", aReader, line );
  2296. if ( attr_2 == 'B' ) // Bold
  2297. field->SetBold( true );
  2298. else if( attr_2 != 'N' ) // No bold is default, check for error.
  2299. SCH_PARSE_ERROR( "invalid field text bold parameter", aReader, line );
  2300. }
  2301. }
  2302. // Fields in RAM must always have names.
  2303. if( (unsigned) id < MANDATORY_FIELDS )
  2304. {
  2305. // Fields in RAM must always have names, because we are trying to get
  2306. // less dependent on field ids and more dependent on names.
  2307. // Plus assumptions are made in the field editors.
  2308. field->m_name = TEMPLATE_FIELDNAME::GetDefaultFieldName( id );
  2309. // Ensure the VALUE field = the part name (can be not the case
  2310. // with malformed libraries: edited by hand, or converted from other tools)
  2311. if( id == VALUE )
  2312. field->m_Text = aPart->GetName();
  2313. }
  2314. else
  2315. {
  2316. parseQuotedString( field->m_name, aReader, line, &line, true ); // Optional.
  2317. }
  2318. }
  2319. void SCH_LEGACY_PLUGIN_CACHE::loadDrawEntries( std::unique_ptr< LIB_PART >& aPart,
  2320. LINE_READER& aReader,
  2321. int aMajorVersion,
  2322. int aMinorVersion )
  2323. {
  2324. const char* line = aReader.Line();
  2325. wxCHECK_RET( strCompare( "DRAW", line, &line ), "Invalid DRAW section" );
  2326. line = aReader.ReadLine();
  2327. while( line )
  2328. {
  2329. if( strCompare( "ENDDRAW", line, &line ) )
  2330. return;
  2331. switch( line[0] )
  2332. {
  2333. case 'A': // Arc
  2334. aPart->AddDrawItem( loadArc( aPart, aReader ) );
  2335. break;
  2336. case 'C': // Circle
  2337. aPart->AddDrawItem( loadCircle( aPart, aReader ) );
  2338. break;
  2339. case 'T': // Text
  2340. aPart->AddDrawItem( loadText( aPart, aReader, aMajorVersion, aMinorVersion ) );
  2341. break;
  2342. case 'S': // Square
  2343. aPart->AddDrawItem( loadRectangle( aPart, aReader ) );
  2344. break;
  2345. case 'X': // Pin Description
  2346. aPart->AddDrawItem( loadPin( aPart, aReader ) );
  2347. break;
  2348. case 'P': // Polyline
  2349. aPart->AddDrawItem( loadPolyLine( aPart, aReader ) );
  2350. break;
  2351. case 'B': // Bezier Curves
  2352. aPart->AddDrawItem( loadBezier( aPart, aReader ) );
  2353. break;
  2354. case '#': // Comment
  2355. case '\n': // Empty line
  2356. case '\r':
  2357. case 0:
  2358. break;
  2359. default:
  2360. SCH_PARSE_ERROR( "undefined DRAW entry", aReader, line );
  2361. }
  2362. line = aReader.ReadLine();
  2363. }
  2364. SCH_PARSE_ERROR( "file ended prematurely loading component draw element", aReader, line );
  2365. }
  2366. FILL_T SCH_LEGACY_PLUGIN_CACHE::parseFillMode( LINE_READER& aReader, const char* aLine,
  2367. const char** aOutput )
  2368. {
  2369. switch( parseChar( aReader, aLine, aOutput ) )
  2370. {
  2371. case 'F': return FILLED_SHAPE;
  2372. case 'f': return FILLED_WITH_BG_BODYCOLOR;
  2373. case 'N': return NO_FILL;
  2374. default: SCH_PARSE_ERROR( "invalid fill type, expected f, F, or N", aReader, aLine );
  2375. }
  2376. }
  2377. LIB_ARC* SCH_LEGACY_PLUGIN_CACHE::loadArc( std::unique_ptr< LIB_PART >& aPart,
  2378. LINE_READER& aReader )
  2379. {
  2380. const char* line = aReader.Line();
  2381. wxCHECK_MSG( strCompare( "A", line, &line ), NULL, "Invalid LIB_ARC definition" );
  2382. LIB_ARC* arc = new LIB_ARC( aPart.get() );
  2383. wxPoint center;
  2384. center.x = parseInt( aReader, line, &line );
  2385. center.y = parseInt( aReader, line, &line );
  2386. arc->SetPosition( center );
  2387. arc->SetRadius( parseInt( aReader, line, &line ) );
  2388. int angle1 = parseInt( aReader, line, &line );
  2389. int angle2 = parseInt( aReader, line, &line );
  2390. NORMALIZE_ANGLE_POS( angle1 );
  2391. NORMALIZE_ANGLE_POS( angle2 );
  2392. arc->SetFirstRadiusAngle( angle1 );
  2393. arc->SetSecondRadiusAngle( angle2 );
  2394. arc->SetUnit( parseInt( aReader, line, &line ) );
  2395. arc->SetConvert( parseInt( aReader, line, &line ) );
  2396. arc->SetWidth( parseInt( aReader, line, &line ) );
  2397. // Old libraries (version <= 2.2) do not have always this FILL MODE param
  2398. // when fill mode is no fill (default mode).
  2399. if( *line != 0 )
  2400. arc->SetFillMode( parseFillMode( aReader, line, &line ) );
  2401. // Actual Coordinates of arc ends are read from file
  2402. if( *line != 0 )
  2403. {
  2404. wxPoint arcStart, arcEnd;
  2405. arcStart.x = parseInt( aReader, line, &line );
  2406. arcStart.y = parseInt( aReader, line, &line );
  2407. arcEnd.x = parseInt( aReader, line, &line );
  2408. arcEnd.y = parseInt( aReader, line, &line );
  2409. arc->SetStart( arcStart );
  2410. arc->SetEnd( arcEnd );
  2411. }
  2412. else
  2413. {
  2414. // Actual Coordinates of arc ends are not read from file
  2415. // (old library), calculate them
  2416. wxPoint arcStart( arc->GetRadius(), 0 );
  2417. wxPoint arcEnd( arc->GetRadius(), 0 );
  2418. RotatePoint( &arcStart.x, &arcStart.y, -angle1 );
  2419. arcStart += arc->GetPosition();
  2420. arc->SetStart( arcStart );
  2421. RotatePoint( &arcEnd.x, &arcEnd.y, -angle2 );
  2422. arcEnd += arc->GetPosition();
  2423. arc->SetEnd( arcEnd );
  2424. }
  2425. return arc;
  2426. }
  2427. LIB_CIRCLE* SCH_LEGACY_PLUGIN_CACHE::loadCircle( std::unique_ptr< LIB_PART >& aPart,
  2428. LINE_READER& aReader )
  2429. {
  2430. const char* line = aReader.Line();
  2431. wxCHECK_MSG( strCompare( "C", line, &line ), NULL, "Invalid LIB_CIRCLE definition" );
  2432. LIB_CIRCLE* circle = new LIB_CIRCLE( aPart.get() );
  2433. wxPoint center;
  2434. center.x = parseInt( aReader, line, &line );
  2435. center.y = parseInt( aReader, line, &line );
  2436. circle->SetPosition( center );
  2437. circle->SetRadius( parseInt( aReader, line, &line ) );
  2438. circle->SetUnit( parseInt( aReader, line, &line ) );
  2439. circle->SetConvert( parseInt( aReader, line, &line ) );
  2440. circle->SetWidth( parseInt( aReader, line, &line ) );
  2441. if( *line != 0 )
  2442. circle->SetFillMode( parseFillMode( aReader, line, &line ) );
  2443. return circle;
  2444. }
  2445. LIB_TEXT* SCH_LEGACY_PLUGIN_CACHE::loadText( std::unique_ptr< LIB_PART >& aPart,
  2446. LINE_READER& aReader,
  2447. int aMajorVersion,
  2448. int aMinorVersion )
  2449. {
  2450. const char* line = aReader.Line();
  2451. wxCHECK_MSG( strCompare( "T", line, &line ), NULL, "Invalid LIB_TEXT definition" );
  2452. LIB_TEXT* text = new LIB_TEXT( aPart.get() );
  2453. text->SetTextAngle( (double) parseInt( aReader, line, &line ) );
  2454. wxPoint center;
  2455. center.x = parseInt( aReader, line, &line );
  2456. center.y = parseInt( aReader, line, &line );
  2457. text->SetPosition( center );
  2458. wxSize size;
  2459. size.x = size.y = parseInt( aReader, line, &line );
  2460. text->SetTextSize( size );
  2461. text->SetVisible( !parseInt( aReader, line, &line ) );
  2462. text->SetUnit( parseInt( aReader, line, &line ) );
  2463. text->SetConvert( parseInt( aReader, line, &line ) );
  2464. wxString str;
  2465. // If quoted string loading fails, load as not quoted string.
  2466. if( *line == '"' )
  2467. parseQuotedString( str, aReader, line, &line );
  2468. else
  2469. {
  2470. parseUnquotedString( str, aReader, line, &line );
  2471. // In old libs, "spaces" are replaced by '~' in unquoted strings:
  2472. str.Replace( "~", " " );
  2473. }
  2474. if( !str.IsEmpty() )
  2475. {
  2476. // convert two apostrophes back to double quote
  2477. str.Replace( "''", "\"" );
  2478. }
  2479. text->SetText( str );
  2480. // Here things are murky and not well defined. At some point it appears the format
  2481. // was changed to add text properties. However rather than add the token to the end of
  2482. // the text definition, it was added after the string and no mention if the file
  2483. // verion was bumped or not so this code make break on very old component libraries.
  2484. //
  2485. // Update: apparently even in the latest version this can be different so added a test
  2486. // for end of line before checking for the text properties.
  2487. if( LIB_VERSION( aMajorVersion, aMinorVersion ) > 0
  2488. && LIB_VERSION( aMajorVersion, aMinorVersion ) > LIB_VERSION( 2, 0 ) && !is_eol( *line ) )
  2489. {
  2490. if( strCompare( "Italic", line, &line ) )
  2491. text->SetItalic( true );
  2492. else if( !strCompare( "Normal", line, &line ) )
  2493. SCH_PARSE_ERROR( "invalid text stype, expected 'Normal' or 'Italic'", aReader, line );
  2494. if( parseInt( aReader, line, &line ) > 0 )
  2495. text->SetBold( true );
  2496. // Some old libaries version > 2.0 do not have these options for text justification:
  2497. if( !is_eol( *line ) )
  2498. {
  2499. switch( parseChar( aReader, line, &line ) )
  2500. {
  2501. case 'L': text->SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT ); break;
  2502. case 'C': text->SetHorizJustify( GR_TEXT_HJUSTIFY_CENTER ); break;
  2503. case 'R': text->SetHorizJustify( GR_TEXT_HJUSTIFY_RIGHT ); break;
  2504. default: SCH_PARSE_ERROR( "invalid horizontal text justication; expected L, C, or R",
  2505. aReader, line );
  2506. }
  2507. switch( parseChar( aReader, line, &line ) )
  2508. {
  2509. case 'T': text->SetVertJustify( GR_TEXT_VJUSTIFY_TOP ); break;
  2510. case 'C': text->SetVertJustify( GR_TEXT_VJUSTIFY_CENTER ); break;
  2511. case 'B': text->SetVertJustify( GR_TEXT_VJUSTIFY_BOTTOM ); break;
  2512. default: SCH_PARSE_ERROR( "invalid vertical text justication; expected T, C, or B",
  2513. aReader, line );
  2514. }
  2515. }
  2516. }
  2517. return text;
  2518. }
  2519. LIB_RECTANGLE* SCH_LEGACY_PLUGIN_CACHE::loadRectangle( std::unique_ptr< LIB_PART >& aPart,
  2520. LINE_READER& aReader )
  2521. {
  2522. const char* line = aReader.Line();
  2523. wxCHECK_MSG( strCompare( "S", line, &line ), NULL, "Invalid LIB_RECTANGLE definition" );
  2524. LIB_RECTANGLE* rectangle = new LIB_RECTANGLE( aPart.get() );
  2525. wxPoint pos;
  2526. pos.x = parseInt( aReader, line, &line );
  2527. pos.y = parseInt( aReader, line, &line );
  2528. rectangle->SetPosition( pos );
  2529. wxPoint end;
  2530. end.x = parseInt( aReader, line, &line );
  2531. end.y = parseInt( aReader, line, &line );
  2532. rectangle->SetEnd( end );
  2533. rectangle->SetUnit( parseInt( aReader, line, &line ) );
  2534. rectangle->SetConvert( parseInt( aReader, line, &line ) );
  2535. rectangle->SetWidth( parseInt( aReader, line, &line ) );
  2536. if( *line != 0 )
  2537. rectangle->SetFillMode( parseFillMode( aReader, line, &line ) );
  2538. return rectangle;
  2539. }
  2540. LIB_PIN* SCH_LEGACY_PLUGIN_CACHE::loadPin( std::unique_ptr< LIB_PART >& aPart,
  2541. LINE_READER& aReader )
  2542. {
  2543. const char* line = aReader.Line();
  2544. wxCHECK_MSG( strCompare( "X", line, &line ), NULL, "Invalid LIB_PIN definition" );
  2545. LIB_PIN* pin = new LIB_PIN( aPart.get() );
  2546. size_t pos = 2; // "X" plus ' ' space character.
  2547. wxString tmp;
  2548. wxString utf8Line = wxString::FromUTF8( line );
  2549. wxStringTokenizer tokens( utf8Line, " \r\n\t" );
  2550. if( tokens.CountTokens() < 11 )
  2551. SCH_PARSE_ERROR( "invalid pin definition", aReader, line );
  2552. pin->m_name = tokens.GetNextToken();
  2553. pos += pin->m_name.size() + 1;
  2554. pin->m_number = tokens.GetNextToken();
  2555. pos += pin->m_number.size() + 1;
  2556. long num;
  2557. wxPoint position;
  2558. tmp = tokens.GetNextToken();
  2559. if( !tmp.ToLong( &num ) )
  2560. THROW_PARSE_ERROR( "invalid pin X coordinate", aReader.GetSource(), aReader.Line(),
  2561. aReader.LineNumber(), pos );
  2562. pos += tmp.size() + 1;
  2563. position.x = (int) num;
  2564. tmp = tokens.GetNextToken();
  2565. if( !tmp.ToLong( &num ) )
  2566. THROW_PARSE_ERROR( "invalid pin Y coordinate", aReader.GetSource(), aReader.Line(),
  2567. aReader.LineNumber(), pos );
  2568. pos += tmp.size() + 1;
  2569. position.y = (int) num;
  2570. pin->m_position = position;
  2571. tmp = tokens.GetNextToken();
  2572. if( !tmp.ToLong( &num ) )
  2573. THROW_PARSE_ERROR( "invalid pin length", aReader.GetSource(), aReader.Line(),
  2574. aReader.LineNumber(), pos );
  2575. pos += tmp.size() + 1;
  2576. pin->m_length = (int) num;
  2577. tmp = tokens.GetNextToken();
  2578. if( tmp.size() > 1 )
  2579. THROW_PARSE_ERROR( "invalid pin orientation", aReader.GetSource(), aReader.Line(),
  2580. aReader.LineNumber(), pos );
  2581. pos += tmp.size() + 1;
  2582. pin->m_orientation = tmp[0];
  2583. tmp = tokens.GetNextToken();
  2584. if( !tmp.ToLong( &num ) )
  2585. THROW_PARSE_ERROR( "invalid pin number text size", aReader.GetSource(), aReader.Line(),
  2586. aReader.LineNumber(), pos );
  2587. pos += tmp.size() + 1;
  2588. pin->m_numTextSize = (int) num;
  2589. tmp = tokens.GetNextToken();
  2590. if( !tmp.ToLong( &num ) )
  2591. THROW_PARSE_ERROR( "invalid pin name text size", aReader.GetSource(), aReader.Line(),
  2592. aReader.LineNumber(), pos );
  2593. pos += tmp.size() + 1;
  2594. pin->m_nameTextSize = (int) num;
  2595. tmp = tokens.GetNextToken();
  2596. if( !tmp.ToLong( &num ) )
  2597. THROW_PARSE_ERROR( "invalid pin unit", aReader.GetSource(), aReader.Line(),
  2598. aReader.LineNumber(), pos );
  2599. pos += tmp.size() + 1;
  2600. pin->m_Unit = (int) num;
  2601. tmp = tokens.GetNextToken();
  2602. if( !tmp.ToLong( &num ) )
  2603. THROW_PARSE_ERROR( "invalid pin alternate body type", aReader.GetSource(), aReader.Line(),
  2604. aReader.LineNumber(), pos );
  2605. pos += tmp.size() + 1;
  2606. pin->m_Convert = (int) num;
  2607. tmp = tokens.GetNextToken();
  2608. if( tmp.size() != 1 )
  2609. THROW_PARSE_ERROR( "invalid pin type", aReader.GetSource(), aReader.Line(),
  2610. aReader.LineNumber(), pos );
  2611. pos += tmp.size() + 1;
  2612. char type = tmp[0];
  2613. wxString attributes;
  2614. switch( type )
  2615. {
  2616. case 'I': pin->m_type = PIN_INPUT; break;
  2617. case 'O': pin->m_type = PIN_OUTPUT; break;
  2618. case 'B': pin->m_type = PIN_BIDI; break;
  2619. case 'T': pin->m_type = PIN_TRISTATE; break;
  2620. case 'P': pin->m_type = PIN_PASSIVE; break;
  2621. case 'U': pin->m_type = PIN_UNSPECIFIED; break;
  2622. case 'W': pin->m_type = PIN_POWER_IN; break;
  2623. case 'w': pin->m_type = PIN_POWER_OUT; break;
  2624. case 'C': pin->m_type = PIN_OPENCOLLECTOR; break;
  2625. case 'E': pin->m_type = PIN_OPENEMITTER; break;
  2626. case 'N': pin->m_type = PIN_NC; break;
  2627. default: THROW_PARSE_ERROR( "unknown pin type", aReader.GetSource(),
  2628. aReader.Line(), aReader.LineNumber(), pos );
  2629. }
  2630. // Optional
  2631. if( tokens.HasMoreTokens() ) /* Special Symbol defined */
  2632. {
  2633. tmp = tokens.GetNextToken();
  2634. enum
  2635. {
  2636. INVERTED = 1 << 0,
  2637. CLOCK = 1 << 1,
  2638. LOWLEVEL_IN = 1 << 2,
  2639. LOWLEVEL_OUT = 1 << 3,
  2640. FALLING_EDGE = 1 << 4,
  2641. NONLOGIC = 1 << 5
  2642. };
  2643. int flags = 0;
  2644. for( int j = tmp.size(); j > 0; )
  2645. {
  2646. switch( tmp[--j].GetValue() )
  2647. {
  2648. case '~': break;
  2649. case 'N': pin->m_attributes |= PIN_INVISIBLE; break;
  2650. case 'I': flags |= INVERTED; break;
  2651. case 'C': flags |= CLOCK; break;
  2652. case 'L': flags |= LOWLEVEL_IN; break;
  2653. case 'V': flags |= LOWLEVEL_OUT; break;
  2654. case 'F': flags |= FALLING_EDGE; break;
  2655. case 'X': flags |= NONLOGIC; break;
  2656. default: THROW_PARSE_ERROR( "invalid pin attribut", aReader.GetSource(),
  2657. aReader.Line(), aReader.LineNumber(), pos );
  2658. }
  2659. pos += 1;
  2660. }
  2661. switch( flags )
  2662. {
  2663. case 0: pin->m_shape = PINSHAPE_LINE; break;
  2664. case INVERTED: pin->m_shape = PINSHAPE_INVERTED; break;
  2665. case CLOCK: pin->m_shape = PINSHAPE_CLOCK; break;
  2666. case INVERTED | CLOCK: pin->m_shape = PINSHAPE_INVERTED_CLOCK; break;
  2667. case LOWLEVEL_IN: pin->m_shape = PINSHAPE_INPUT_LOW; break;
  2668. case LOWLEVEL_IN | CLOCK: pin->m_shape = PINSHAPE_CLOCK_LOW; break;
  2669. case LOWLEVEL_OUT: pin->m_shape = PINSHAPE_OUTPUT_LOW; break;
  2670. case FALLING_EDGE: pin->m_shape = PINSHAPE_FALLING_EDGE_CLOCK; break;
  2671. case NONLOGIC: pin->m_shape = PINSHAPE_NONLOGIC; break;
  2672. default: SCH_PARSE_ERROR( "pin attributes do not define a valid pin shape", aReader, line );
  2673. }
  2674. }
  2675. return pin;
  2676. }
  2677. LIB_POLYLINE* SCH_LEGACY_PLUGIN_CACHE::loadPolyLine( std::unique_ptr< LIB_PART >& aPart,
  2678. LINE_READER& aReader )
  2679. {
  2680. const char* line = aReader.Line();
  2681. wxCHECK_MSG( strCompare( "P", line, &line ), NULL, "Invalid LIB_POLYLINE definition" );
  2682. LIB_POLYLINE* polyLine = new LIB_POLYLINE( aPart.get() );
  2683. int points = parseInt( aReader, line, &line );
  2684. polyLine->SetUnit( parseInt( aReader, line, &line ) );
  2685. polyLine->SetConvert( parseInt( aReader, line, &line ) );
  2686. polyLine->SetWidth( parseInt( aReader, line, &line ) );
  2687. polyLine->Reserve( points );
  2688. wxPoint pt;
  2689. for( int i = 0; i < points; i++ )
  2690. {
  2691. pt.x = parseInt( aReader, line, &line );
  2692. pt.y = parseInt( aReader, line, &line );
  2693. polyLine->AddPoint( pt );
  2694. }
  2695. if( *line != 0 )
  2696. polyLine->SetFillMode( parseFillMode( aReader, line, &line ) );
  2697. return polyLine;
  2698. }
  2699. LIB_BEZIER* SCH_LEGACY_PLUGIN_CACHE::loadBezier( std::unique_ptr< LIB_PART >& aPart,
  2700. LINE_READER& aReader )
  2701. {
  2702. const char* line = aReader.Line();
  2703. wxCHECK_MSG( strCompare( "B", line, &line ), NULL, "Invalid LIB_BEZIER definition" );
  2704. LIB_BEZIER* bezier = new LIB_BEZIER( aPart.get() );
  2705. int points = parseInt( aReader, line, &line );
  2706. bezier->SetUnit( parseInt( aReader, line, &line ) );
  2707. bezier->SetConvert( parseInt( aReader, line, &line ) );
  2708. bezier->SetWidth( parseInt( aReader, line, &line ) );
  2709. wxPoint pt;
  2710. bezier->Reserve( points );
  2711. for( int i = 0; i < points; i++ )
  2712. {
  2713. pt.x = parseInt( aReader, line, &line );
  2714. pt.y = parseInt( aReader, line, &line );
  2715. bezier->AddPoint( pt );
  2716. }
  2717. if( *line != 0 )
  2718. bezier->SetFillMode( parseFillMode( aReader, line, &line ) );
  2719. return bezier;
  2720. }
  2721. void SCH_LEGACY_PLUGIN_CACHE::loadFootprintFilters( std::unique_ptr< LIB_PART >& aPart,
  2722. LINE_READER& aReader )
  2723. {
  2724. const char* line = aReader.Line();
  2725. wxCHECK_RET( strCompare( "$FPLIST", line, &line ), "Invalid footprint filter list" );
  2726. line = aReader.ReadLine();
  2727. while( line )
  2728. {
  2729. if( strCompare( "$ENDFPLIST", line, &line ) )
  2730. return;
  2731. wxString footprint;
  2732. parseUnquotedString( footprint, aReader, line, &line );
  2733. aPart->GetFootprints().Add( footprint );
  2734. line = aReader.ReadLine();
  2735. }
  2736. SCH_PARSE_ERROR( "file ended prematurely while loading footprint filters", aReader, line );
  2737. }
  2738. void SCH_LEGACY_PLUGIN_CACHE::Save( bool aSaveDocFile )
  2739. {
  2740. if( !m_isModified )
  2741. return;
  2742. // Write through symlinks, don't replace them
  2743. wxFileName fn = GetRealFile();
  2744. std::unique_ptr< FILE_OUTPUTFORMATTER > formatter( new FILE_OUTPUTFORMATTER( fn.GetFullPath() ) );
  2745. formatter->Print( 0, "%s %d.%d\n", LIBFILE_IDENT, LIB_VERSION_MAJOR, LIB_VERSION_MINOR );
  2746. formatter->Print( 0, "#encoding utf-8\n");
  2747. for( LIB_ALIAS_MAP::iterator it = m_aliases.begin(); it != m_aliases.end(); it++ )
  2748. {
  2749. if( !it->second->IsRoot() )
  2750. continue;
  2751. SaveSymbol( it->second->GetPart(), *formatter.get() );
  2752. }
  2753. formatter->Print( 0, "#\n#End Library\n" );
  2754. formatter.reset();
  2755. m_fileModTime = fn.GetModificationTime();
  2756. m_isModified = false;
  2757. if( aSaveDocFile )
  2758. saveDocFile();
  2759. }
  2760. void SCH_LEGACY_PLUGIN_CACHE::SaveSymbol( LIB_PART* aSymbol,
  2761. OUTPUTFORMATTER& aFormatter )
  2762. {
  2763. wxCHECK_RET( aSymbol, "Invalid LIB_PART pointer." );
  2764. LIB_FIELD& value = aSymbol->GetValueField();
  2765. // First line: it s a comment (component name for readers)
  2766. aFormatter.Print( 0, "#\n# %s\n#\n", TO_UTF8( value.GetText() ) );
  2767. // Save data
  2768. aFormatter.Print( 0, "DEF" );
  2769. if( value.IsVisible() )
  2770. {
  2771. aFormatter.Print( 0, " %s", TO_UTF8( value.GetText() ) );
  2772. }
  2773. else
  2774. {
  2775. aFormatter.Print( 0, " ~%s", TO_UTF8( value.GetText() ) );
  2776. }
  2777. LIB_FIELD& reference = aSymbol->GetReferenceField();
  2778. if( !reference.GetText().IsEmpty() )
  2779. {
  2780. aFormatter.Print( 0, " %s", TO_UTF8( reference.GetText() ) );
  2781. }
  2782. else
  2783. {
  2784. aFormatter.Print( 0, " ~" );
  2785. }
  2786. aFormatter.Print( 0, " %d %d %c %c %d %c %c\n",
  2787. 0, aSymbol->GetPinNameOffset(),
  2788. aSymbol->ShowPinNumbers() ? 'Y' : 'N',
  2789. aSymbol->ShowPinNames() ? 'Y' : 'N',
  2790. aSymbol->GetUnitCount(), aSymbol->UnitsLocked() ? 'L' : 'F',
  2791. aSymbol->IsPower() ? 'P' : 'N' );
  2792. timestamp_t dateModified = aSymbol->GetDateLastEdition();
  2793. if( dateModified != 0 )
  2794. {
  2795. int sec = dateModified & 63;
  2796. int min = ( dateModified >> 6 ) & 63;
  2797. int hour = ( dateModified >> 12 ) & 31;
  2798. int day = ( dateModified >> 17 ) & 31;
  2799. int mon = ( dateModified >> 22 ) & 15;
  2800. int year = ( dateModified >> 26 ) + 1990;
  2801. aFormatter.Print( 0, "Ti %d/%d/%d %d:%d:%d\n", year, mon, day, hour, min, sec );
  2802. }
  2803. LIB_FIELDS fields;
  2804. aSymbol->GetFields( fields );
  2805. // Mandatory fields:
  2806. // may have their own save policy so there is a separate loop for them.
  2807. // Empty fields are saved, because the user may have set visibility,
  2808. // size and orientation
  2809. for( int i = 0; i < MANDATORY_FIELDS; ++i )
  2810. {
  2811. saveField( &fields[i], aFormatter );
  2812. }
  2813. // User defined fields:
  2814. // may have their own save policy so there is a separate loop for them.
  2815. int fieldId = MANDATORY_FIELDS; // really wish this would go away.
  2816. for( unsigned i = MANDATORY_FIELDS; i < fields.size(); ++i )
  2817. {
  2818. // There is no need to save empty fields, i.e. no reason to preserve field
  2819. // names now that fields names come in dynamically through the template
  2820. // fieldnames.
  2821. if( !fields[i].GetText().IsEmpty() )
  2822. {
  2823. fields[i].SetId( fieldId++ );
  2824. saveField( &fields[i], aFormatter );
  2825. }
  2826. }
  2827. // Save the alias list: a line starting by "ALIAS". The first alias is the root
  2828. // and has the same name as the component. In the old library file format this
  2829. // alias does not get added to the alias list.
  2830. if( aSymbol->GetAliasCount() > 1 )
  2831. {
  2832. wxArrayString aliases = aSymbol->GetAliasNames();
  2833. aFormatter.Print( 0, "ALIAS" );
  2834. for( unsigned i = 1; i < aliases.size(); i++ )
  2835. {
  2836. aFormatter.Print( 0, " %s", TO_UTF8( aliases[i] ) );
  2837. }
  2838. aFormatter.Print( 0, "\n" );
  2839. }
  2840. wxArrayString footprints = aSymbol->GetFootprints();
  2841. // Write the footprint filter list
  2842. if( footprints.GetCount() != 0 )
  2843. {
  2844. aFormatter.Print( 0, "$FPLIST\n" );
  2845. for( unsigned i = 0; i < footprints.GetCount(); i++ )
  2846. {
  2847. aFormatter.Print( 0, " %s\n", TO_UTF8( footprints[i] ) );
  2848. }
  2849. aFormatter.Print( 0, "$ENDFPLIST\n" );
  2850. }
  2851. // Save graphics items (including pins)
  2852. if( !aSymbol->GetDrawItems().empty() )
  2853. {
  2854. // Sort the draw items in order to editing a file editing by hand.
  2855. aSymbol->GetDrawItems().sort();
  2856. aFormatter.Print( 0, "DRAW\n" );
  2857. for( LIB_ITEM& item : aSymbol->GetDrawItems() )
  2858. {
  2859. switch( item.Type() )
  2860. {
  2861. case LIB_FIELD_T: // Fields have already been saved above.
  2862. continue;
  2863. case LIB_ARC_T:
  2864. saveArc( (LIB_ARC*) &item, aFormatter );
  2865. break;
  2866. case LIB_BEZIER_T:
  2867. saveBezier( (LIB_BEZIER*) &item, aFormatter );
  2868. break;
  2869. case LIB_CIRCLE_T:
  2870. saveCircle( ( LIB_CIRCLE* ) &item, aFormatter );
  2871. break;
  2872. case LIB_PIN_T:
  2873. savePin( (LIB_PIN* ) &item, aFormatter );
  2874. break;
  2875. case LIB_POLYLINE_T:
  2876. savePolyLine( ( LIB_POLYLINE* ) &item, aFormatter );
  2877. break;
  2878. case LIB_RECTANGLE_T:
  2879. saveRectangle( ( LIB_RECTANGLE* ) &item, aFormatter );
  2880. break;
  2881. case LIB_TEXT_T:
  2882. saveText( ( LIB_TEXT* ) &item, aFormatter );
  2883. break;
  2884. default:
  2885. ;
  2886. }
  2887. }
  2888. aFormatter.Print( 0, "ENDDRAW\n" );
  2889. }
  2890. aFormatter.Print( 0, "ENDDEF\n" );
  2891. }
  2892. void SCH_LEGACY_PLUGIN_CACHE::saveArc( LIB_ARC* aArc,
  2893. OUTPUTFORMATTER& aFormatter )
  2894. {
  2895. wxCHECK_RET( aArc && aArc->Type() == LIB_ARC_T, "Invalid LIB_ARC object." );
  2896. int x1 = aArc->GetFirstRadiusAngle();
  2897. if( x1 > 1800 )
  2898. x1 -= 3600;
  2899. int x2 = aArc->GetSecondRadiusAngle();
  2900. if( x2 > 1800 )
  2901. x2 -= 3600;
  2902. aFormatter.Print( 0, "A %d %d %d %d %d %d %d %d %c %d %d %d %d\n",
  2903. aArc->GetPosition().x, aArc->GetPosition().y,
  2904. aArc->GetRadius(), x1, x2, aArc->GetUnit(), aArc->GetConvert(),
  2905. aArc->GetWidth(), fill_tab[aArc->GetFillMode()],
  2906. aArc->GetStart().x, aArc->GetStart().y,
  2907. aArc->GetEnd().x, aArc->GetEnd().y );
  2908. }
  2909. void SCH_LEGACY_PLUGIN_CACHE::saveBezier( LIB_BEZIER* aBezier,
  2910. OUTPUTFORMATTER& aFormatter )
  2911. {
  2912. wxCHECK_RET( aBezier && aBezier->Type() == LIB_BEZIER_T, "Invalid LIB_BEZIER object." );
  2913. aFormatter.Print( 0, "B %u %d %d %d", (unsigned)aBezier->GetPoints().size(),
  2914. aBezier->GetUnit(), aBezier->GetConvert(), aBezier->GetWidth() );
  2915. for( const auto& pt : aBezier->GetPoints() )
  2916. aFormatter.Print( 0, " %d %d", pt.x, pt.y );
  2917. aFormatter.Print( 0, " %c\n", fill_tab[aBezier->GetFillMode()] );
  2918. }
  2919. void SCH_LEGACY_PLUGIN_CACHE::saveCircle( LIB_CIRCLE* aCircle,
  2920. OUTPUTFORMATTER& aFormatter )
  2921. {
  2922. wxCHECK_RET( aCircle && aCircle->Type() == LIB_CIRCLE_T, "Invalid LIB_CIRCLE object." );
  2923. aFormatter.Print( 0, "C %d %d %d %d %d %d %c\n",
  2924. aCircle->GetPosition().x, aCircle->GetPosition().y,
  2925. aCircle->GetRadius(), aCircle->GetUnit(), aCircle->GetConvert(),
  2926. aCircle->GetWidth(), fill_tab[aCircle->GetFillMode()] );
  2927. }
  2928. void SCH_LEGACY_PLUGIN_CACHE::saveField( LIB_FIELD* aField,
  2929. OUTPUTFORMATTER& aFormatter )
  2930. {
  2931. wxCHECK_RET( aField && aField->Type() == LIB_FIELD_T, "Invalid LIB_FIELD object." );
  2932. int hjustify, vjustify;
  2933. int id = aField->GetId();
  2934. wxString text = aField->m_Text;
  2935. hjustify = 'C';
  2936. if( aField->GetHorizJustify() == GR_TEXT_HJUSTIFY_LEFT )
  2937. hjustify = 'L';
  2938. else if( aField->GetHorizJustify() == GR_TEXT_HJUSTIFY_RIGHT )
  2939. hjustify = 'R';
  2940. vjustify = 'C';
  2941. if( aField->GetVertJustify() == GR_TEXT_VJUSTIFY_BOTTOM )
  2942. vjustify = 'B';
  2943. else if( aField->GetVertJustify() == GR_TEXT_VJUSTIFY_TOP )
  2944. vjustify = 'T';
  2945. aFormatter.Print( 0, "F%d %s %d %d %d %c %c %c %c%c%c",
  2946. id,
  2947. EscapedUTF8( text ).c_str(), // wraps in quotes
  2948. aField->GetTextPos().x, aField->GetTextPos().y, aField->GetTextWidth(),
  2949. aField->GetTextAngle() == 0 ? 'H' : 'V',
  2950. aField->IsVisible() ? 'V' : 'I',
  2951. hjustify, vjustify,
  2952. aField->IsItalic() ? 'I' : 'N',
  2953. aField->IsBold() ? 'B' : 'N' );
  2954. /* Save field name, if necessary
  2955. * Field name is saved only if it is not the default name.
  2956. * Just because default name depends on the language and can change from
  2957. * a country to another
  2958. */
  2959. wxString defName = TEMPLATE_FIELDNAME::GetDefaultFieldName( id );
  2960. if( id >= FIELD1 && !aField->m_name.IsEmpty() && aField->m_name != defName )
  2961. aFormatter.Print( 0, " %s", EscapedUTF8( aField->m_name ).c_str() );
  2962. aFormatter.Print( 0, "\n" );
  2963. }
  2964. void SCH_LEGACY_PLUGIN_CACHE::savePin( LIB_PIN* aPin,
  2965. OUTPUTFORMATTER& aFormatter )
  2966. {
  2967. wxCHECK_RET( aPin && aPin->Type() == LIB_PIN_T, "Invalid LIB_PIN object." );
  2968. int Etype;
  2969. switch( aPin->GetType() )
  2970. {
  2971. default:
  2972. case PIN_INPUT:
  2973. Etype = 'I';
  2974. break;
  2975. case PIN_OUTPUT:
  2976. Etype = 'O';
  2977. break;
  2978. case PIN_BIDI:
  2979. Etype = 'B';
  2980. break;
  2981. case PIN_TRISTATE:
  2982. Etype = 'T';
  2983. break;
  2984. case PIN_PASSIVE:
  2985. Etype = 'P';
  2986. break;
  2987. case PIN_UNSPECIFIED:
  2988. Etype = 'U';
  2989. break;
  2990. case PIN_POWER_IN:
  2991. Etype = 'W';
  2992. break;
  2993. case PIN_POWER_OUT:
  2994. Etype = 'w';
  2995. break;
  2996. case PIN_OPENCOLLECTOR:
  2997. Etype = 'C';
  2998. break;
  2999. case PIN_OPENEMITTER:
  3000. Etype = 'E';
  3001. break;
  3002. case PIN_NC:
  3003. Etype = 'N';
  3004. break;
  3005. }
  3006. if( !aPin->GetName().IsEmpty() )
  3007. aFormatter.Print( 0, "X %s", TO_UTF8( aPin->GetName() ) );
  3008. else
  3009. aFormatter.Print( 0, "X ~" );
  3010. aFormatter.Print( 0, " %s %d %d %d %c %d %d %d %d %c",
  3011. aPin->GetNumber().IsEmpty() ? "~" : TO_UTF8( aPin->GetNumber() ),
  3012. aPin->GetPosition().x, aPin->GetPosition().y,
  3013. (int) aPin->GetLength(), (int) aPin->GetOrientation(),
  3014. aPin->GetNumberTextSize(), aPin->GetNameTextSize(),
  3015. aPin->GetUnit(), aPin->GetConvert(), Etype );
  3016. if( aPin->GetShape() || !aPin->IsVisible() )
  3017. aFormatter.Print( 0, " " );
  3018. if( !aPin->IsVisible() )
  3019. aFormatter.Print( 0, "N" );
  3020. switch( aPin->GetShape() )
  3021. {
  3022. case PINSHAPE_LINE:
  3023. break;
  3024. case PINSHAPE_INVERTED:
  3025. aFormatter.Print( 0, "I" );
  3026. break;
  3027. case PINSHAPE_CLOCK:
  3028. aFormatter.Print( 0, "C" );
  3029. break;
  3030. case PINSHAPE_INVERTED_CLOCK:
  3031. aFormatter.Print( 0, "IC" );
  3032. break;
  3033. case PINSHAPE_INPUT_LOW:
  3034. aFormatter.Print( 0, "L" );
  3035. break;
  3036. case PINSHAPE_CLOCK_LOW:
  3037. aFormatter.Print( 0, "CL" );
  3038. break;
  3039. case PINSHAPE_OUTPUT_LOW:
  3040. aFormatter.Print( 0, "V" );
  3041. break;
  3042. case PINSHAPE_FALLING_EDGE_CLOCK:
  3043. aFormatter.Print( 0, "F" );
  3044. break;
  3045. case PINSHAPE_NONLOGIC:
  3046. aFormatter.Print( 0, "X" );
  3047. break;
  3048. default:
  3049. assert( !"Invalid pin shape" );
  3050. }
  3051. aFormatter.Print( 0, "\n" );
  3052. aPin->ClearFlags( IS_CHANGED );
  3053. }
  3054. void SCH_LEGACY_PLUGIN_CACHE::savePolyLine( LIB_POLYLINE* aPolyLine,
  3055. OUTPUTFORMATTER& aFormatter )
  3056. {
  3057. wxCHECK_RET( aPolyLine && aPolyLine->Type() == LIB_POLYLINE_T, "Invalid LIB_POLYLINE object." );
  3058. int ccount = aPolyLine->GetCornerCount();
  3059. aFormatter.Print( 0, "P %d %d %d %d", ccount, aPolyLine->GetUnit(), aPolyLine->GetConvert(),
  3060. aPolyLine->GetWidth() );
  3061. for( const auto& pt : aPolyLine->GetPolyPoints() )
  3062. {
  3063. aFormatter.Print( 0, " %d %d", pt.x, pt.y );
  3064. }
  3065. aFormatter.Print( 0, " %c\n", fill_tab[aPolyLine->GetFillMode()] );
  3066. }
  3067. void SCH_LEGACY_PLUGIN_CACHE::saveRectangle( LIB_RECTANGLE* aRectangle,
  3068. OUTPUTFORMATTER& aFormatter )
  3069. {
  3070. wxCHECK_RET( aRectangle && aRectangle->Type() == LIB_RECTANGLE_T,
  3071. "Invalid LIB_RECTANGLE object." );
  3072. aFormatter.Print( 0, "S %d %d %d %d %d %d %d %c\n",
  3073. aRectangle->GetPosition().x, aRectangle->GetPosition().y,
  3074. aRectangle->GetEnd().x, aRectangle->GetEnd().y,
  3075. aRectangle->GetUnit(), aRectangle->GetConvert(),
  3076. aRectangle->GetWidth(), fill_tab[aRectangle->GetFillMode()] );
  3077. }
  3078. void SCH_LEGACY_PLUGIN_CACHE::saveText( LIB_TEXT* aText,
  3079. OUTPUTFORMATTER& aFormatter )
  3080. {
  3081. wxCHECK_RET( aText && aText->Type() == LIB_TEXT_T, "Invalid LIB_TEXT object." );
  3082. wxString text = aText->GetText();
  3083. if( text.Contains( wxT( " " ) ) || text.Contains( wxT( "~" ) ) || text.Contains( wxT( "\"" ) ) )
  3084. {
  3085. // convert double quote to similar-looking two apostrophes
  3086. text.Replace( wxT( "\"" ), wxT( "''" ) );
  3087. text = wxT( "\"" ) + text + wxT( "\"" );
  3088. }
  3089. aFormatter.Print( 0, "T %g %d %d %d %d %d %d %s", aText->GetTextAngle(),
  3090. aText->GetTextPos().x, aText->GetTextPos().y,
  3091. aText->GetTextWidth(), !aText->IsVisible(),
  3092. aText->GetUnit(), aText->GetConvert(), TO_UTF8( text ) );
  3093. aFormatter.Print( 0, " %s %d", aText->IsItalic() ? "Italic" : "Normal", aText->IsBold() );
  3094. char hjustify = 'C';
  3095. if( aText->GetHorizJustify() == GR_TEXT_HJUSTIFY_LEFT )
  3096. hjustify = 'L';
  3097. else if( aText->GetHorizJustify() == GR_TEXT_HJUSTIFY_RIGHT )
  3098. hjustify = 'R';
  3099. char vjustify = 'C';
  3100. if( aText->GetVertJustify() == GR_TEXT_VJUSTIFY_BOTTOM )
  3101. vjustify = 'B';
  3102. else if( aText->GetVertJustify() == GR_TEXT_VJUSTIFY_TOP )
  3103. vjustify = 'T';
  3104. aFormatter.Print( 0, " %c %c\n", hjustify, vjustify );
  3105. }
  3106. void SCH_LEGACY_PLUGIN_CACHE::saveDocFile()
  3107. {
  3108. wxFileName fileName = m_libFileName;
  3109. fileName.SetExt( DOC_EXT );
  3110. FILE_OUTPUTFORMATTER formatter( fileName.GetFullPath() );
  3111. formatter.Print( 0, "%s\n", DOCFILE_IDENT );
  3112. for( LIB_ALIAS_MAP::iterator it = m_aliases.begin(); it != m_aliases.end(); it++ )
  3113. {
  3114. wxString description = it->second->GetDescription();
  3115. wxString keyWords = it->second->GetKeyWords();
  3116. wxString docFileName = it->second->GetDocFileName();
  3117. if( description.IsEmpty() && keyWords.IsEmpty() && docFileName.IsEmpty() )
  3118. continue;
  3119. formatter.Print( 0, "#\n$CMP %s\n", TO_UTF8( it->second->GetName() ) );
  3120. if( !description.IsEmpty() )
  3121. formatter.Print( 0, "D %s\n", TO_UTF8( description ) );
  3122. if( !keyWords.IsEmpty() )
  3123. formatter.Print( 0, "K %s\n", TO_UTF8( keyWords ) );
  3124. if( !docFileName.IsEmpty() )
  3125. formatter.Print( 0, "F %s\n", TO_UTF8( docFileName ) );
  3126. formatter.Print( 0, "$ENDCMP\n" );
  3127. }
  3128. formatter.Print( 0, "#\n#End Doc Library\n" );
  3129. }
  3130. void SCH_LEGACY_PLUGIN_CACHE::DeleteAlias( const wxString& aAliasName )
  3131. {
  3132. LIB_ALIAS_MAP::iterator it = m_aliases.find( aAliasName );
  3133. if( it == m_aliases.end() )
  3134. THROW_IO_ERROR( wxString::Format( _( "library %s does not contain an alias %s" ),
  3135. m_libFileName.GetFullName(), aAliasName ) );
  3136. LIB_ALIAS* alias = it->second;
  3137. LIB_PART* part = alias->GetPart();
  3138. alias = part->RemoveAlias( alias );
  3139. if( !alias )
  3140. {
  3141. delete part;
  3142. if( m_aliases.size() > 1 )
  3143. {
  3144. LIB_ALIAS_MAP::iterator next = it;
  3145. next++;
  3146. if( next == m_aliases.end() )
  3147. next = m_aliases.begin();
  3148. alias = next->second;
  3149. }
  3150. }
  3151. m_aliases.erase( it );
  3152. ++m_modHash;
  3153. m_isModified = true;
  3154. }
  3155. void SCH_LEGACY_PLUGIN_CACHE::DeleteSymbol( const wxString& aAliasName )
  3156. {
  3157. LIB_ALIAS_MAP::iterator it = m_aliases.find( aAliasName );
  3158. if( it == m_aliases.end() )
  3159. THROW_IO_ERROR( wxString::Format( _( "library %s does not contain an alias %s" ),
  3160. m_libFileName.GetFullName(), aAliasName ) );
  3161. LIB_ALIAS* alias = it->second;
  3162. LIB_PART* part = alias->GetPart();
  3163. wxArrayString aliasNames = part->GetAliasNames();
  3164. // Deleting all of the aliases deletes the symbol from the library.
  3165. for( size_t i = 0; i < aliasNames.Count(); i++ )
  3166. DeleteAlias( aliasNames[i] );
  3167. }
  3168. void SCH_LEGACY_PLUGIN::cacheLib( const wxString& aLibraryFileName )
  3169. {
  3170. if( !m_cache || !m_cache->IsFile( aLibraryFileName ) || m_cache->IsFileChanged() )
  3171. {
  3172. // a spectacular episode in memory management:
  3173. delete m_cache;
  3174. m_cache = new SCH_LEGACY_PLUGIN_CACHE( aLibraryFileName );
  3175. // Because m_cache is rebuilt, increment PART_LIBS::s_modify_generation
  3176. // to modify the hash value that indicate component to symbol links
  3177. // must be updated.
  3178. PART_LIBS::s_modify_generation++;
  3179. if( !isBuffering( m_props ) )
  3180. m_cache->Load();
  3181. }
  3182. }
  3183. bool SCH_LEGACY_PLUGIN::writeDocFile( const PROPERTIES* aProperties )
  3184. {
  3185. std::string propName( SCH_LEGACY_PLUGIN::PropNoDocFile );
  3186. if( aProperties && aProperties->find( propName ) != aProperties->end() )
  3187. return false;
  3188. return true;
  3189. }
  3190. bool SCH_LEGACY_PLUGIN::isBuffering( const PROPERTIES* aProperties )
  3191. {
  3192. return ( aProperties && aProperties->Exists( SCH_LEGACY_PLUGIN::PropBuffering ) );
  3193. }
  3194. int SCH_LEGACY_PLUGIN::GetModifyHash() const
  3195. {
  3196. if( m_cache )
  3197. return m_cache->GetModifyHash();
  3198. // If the cache hasn't been loaded, it hasn't been modified.
  3199. return 0;
  3200. }
  3201. size_t SCH_LEGACY_PLUGIN::GetSymbolLibCount( const wxString& aLibraryPath,
  3202. const PROPERTIES* aProperties )
  3203. {
  3204. LOCALE_IO toggle;
  3205. m_props = aProperties;
  3206. cacheLib( aLibraryPath );
  3207. return m_cache->m_aliases.size();
  3208. }
  3209. void SCH_LEGACY_PLUGIN::EnumerateSymbolLib( wxArrayString& aAliasNameList,
  3210. const wxString& aLibraryPath,
  3211. const PROPERTIES* aProperties )
  3212. {
  3213. LOCALE_IO toggle; // toggles on, then off, the C locale.
  3214. m_props = aProperties;
  3215. bool powerSymbolsOnly = ( aProperties &&
  3216. aProperties->find( SYMBOL_LIB_TABLE::PropPowerSymsOnly ) != aProperties->end() );
  3217. cacheLib( aLibraryPath );
  3218. const LIB_ALIAS_MAP& aliases = m_cache->m_aliases;
  3219. for( LIB_ALIAS_MAP::const_iterator it = aliases.begin(); it != aliases.end(); ++it )
  3220. {
  3221. if( !powerSymbolsOnly || it->second->GetPart()->IsPower() )
  3222. aAliasNameList.Add( it->first );
  3223. }
  3224. }
  3225. void SCH_LEGACY_PLUGIN::EnumerateSymbolLib( std::vector<LIB_ALIAS*>& aAliasList,
  3226. const wxString& aLibraryPath,
  3227. const PROPERTIES* aProperties )
  3228. {
  3229. LOCALE_IO toggle; // toggles on, then off, the C locale.
  3230. m_props = aProperties;
  3231. bool powerSymbolsOnly = ( aProperties &&
  3232. aProperties->find( SYMBOL_LIB_TABLE::PropPowerSymsOnly ) != aProperties->end() );
  3233. cacheLib( aLibraryPath );
  3234. const LIB_ALIAS_MAP& aliases = m_cache->m_aliases;
  3235. for( LIB_ALIAS_MAP::const_iterator it = aliases.begin(); it != aliases.end(); ++it )
  3236. {
  3237. if( !powerSymbolsOnly || it->second->GetPart()->IsPower() )
  3238. aAliasList.push_back( it->second );
  3239. }
  3240. }
  3241. LIB_ALIAS* SCH_LEGACY_PLUGIN::LoadSymbol( const wxString& aLibraryPath, const wxString& aAliasName,
  3242. const PROPERTIES* aProperties )
  3243. {
  3244. LOCALE_IO toggle; // toggles on, then off, the C locale.
  3245. m_props = aProperties;
  3246. cacheLib( aLibraryPath );
  3247. LIB_ALIAS_MAP::const_iterator it = m_cache->m_aliases.find( aAliasName );
  3248. if( it == m_cache->m_aliases.end() )
  3249. return NULL;
  3250. return it->second;
  3251. }
  3252. void SCH_LEGACY_PLUGIN::SaveSymbol( const wxString& aLibraryPath, const LIB_PART* aSymbol,
  3253. const PROPERTIES* aProperties )
  3254. {
  3255. m_props = aProperties;
  3256. cacheLib( aLibraryPath );
  3257. m_cache->AddSymbol( aSymbol );
  3258. if( !isBuffering( aProperties ) )
  3259. m_cache->Save( writeDocFile( aProperties ) );
  3260. }
  3261. void SCH_LEGACY_PLUGIN::DeleteAlias( const wxString& aLibraryPath, const wxString& aAliasName,
  3262. const PROPERTIES* aProperties )
  3263. {
  3264. m_props = aProperties;
  3265. cacheLib( aLibraryPath );
  3266. m_cache->DeleteAlias( aAliasName );
  3267. if( !isBuffering( aProperties ) )
  3268. m_cache->Save( writeDocFile( aProperties ) );
  3269. }
  3270. void SCH_LEGACY_PLUGIN::DeleteSymbol( const wxString& aLibraryPath, const wxString& aAliasName,
  3271. const PROPERTIES* aProperties )
  3272. {
  3273. m_props = aProperties;
  3274. cacheLib( aLibraryPath );
  3275. m_cache->DeleteSymbol( aAliasName );
  3276. if( !isBuffering( aProperties ) )
  3277. m_cache->Save( writeDocFile( aProperties ) );
  3278. }
  3279. void SCH_LEGACY_PLUGIN::CreateSymbolLib( const wxString& aLibraryPath,
  3280. const PROPERTIES* aProperties )
  3281. {
  3282. if( wxFileExists( aLibraryPath ) )
  3283. {
  3284. THROW_IO_ERROR( wxString::Format(
  3285. _( "symbol library \"%s\" already exists, cannot create a new library" ),
  3286. aLibraryPath.GetData() ) );
  3287. }
  3288. LOCALE_IO toggle;
  3289. m_props = aProperties;
  3290. delete m_cache;
  3291. m_cache = new SCH_LEGACY_PLUGIN_CACHE( aLibraryPath );
  3292. m_cache->SetModified();
  3293. m_cache->Save( writeDocFile( aProperties ) );
  3294. m_cache->Load(); // update m_writable and m_mod_time
  3295. }
  3296. bool SCH_LEGACY_PLUGIN::DeleteSymbolLib( const wxString& aLibraryPath,
  3297. const PROPERTIES* aProperties )
  3298. {
  3299. wxFileName fn = aLibraryPath;
  3300. if( !fn.FileExists() )
  3301. return false;
  3302. // Some of the more elaborate wxRemoveFile() crap puts up its own wxLog dialog
  3303. // we don't want that. we want bare metal portability with no UI here.
  3304. if( wxRemove( aLibraryPath ) )
  3305. {
  3306. THROW_IO_ERROR( wxString::Format( _( "library \"%s\" cannot be deleted" ),
  3307. aLibraryPath.GetData() ) );
  3308. }
  3309. if( m_cache && m_cache->IsFile( aLibraryPath ) )
  3310. {
  3311. delete m_cache;
  3312. m_cache = 0;
  3313. }
  3314. return true;
  3315. }
  3316. void SCH_LEGACY_PLUGIN::SaveLibrary( const wxString& aLibraryPath, const PROPERTIES* aProperties )
  3317. {
  3318. if( !m_cache )
  3319. m_cache = new SCH_LEGACY_PLUGIN_CACHE( aLibraryPath );
  3320. wxString oldFileName = m_cache->GetFileName();
  3321. if( !m_cache->IsFile( aLibraryPath ) )
  3322. {
  3323. m_cache->SetFileName( aLibraryPath );
  3324. }
  3325. // This is a forced save.
  3326. m_cache->SetModified();
  3327. m_cache->Save( writeDocFile( aProperties ) );
  3328. m_cache->SetFileName( oldFileName );
  3329. }
  3330. bool SCH_LEGACY_PLUGIN::CheckHeader( const wxString& aFileName )
  3331. {
  3332. // Open file and check first line
  3333. wxTextFile tempFile;
  3334. tempFile.Open( aFileName );
  3335. wxString firstline;
  3336. // read the first line
  3337. firstline = tempFile.GetFirstLine();
  3338. tempFile.Close();
  3339. return firstline.StartsWith( "EESchema" );
  3340. }
  3341. bool SCH_LEGACY_PLUGIN::IsSymbolLibWritable( const wxString& aLibraryPath )
  3342. {
  3343. return wxFileName::IsFileWritable( aLibraryPath );
  3344. }
  3345. LIB_PART* SCH_LEGACY_PLUGIN::ParsePart( LINE_READER& reader, int aMajorVersion,
  3346. int aMinorVersion )
  3347. {
  3348. return SCH_LEGACY_PLUGIN_CACHE::LoadPart( reader, aMajorVersion, aMinorVersion );
  3349. }
  3350. void SCH_LEGACY_PLUGIN::FormatPart( LIB_PART* part, OUTPUTFORMATTER & formatter )
  3351. {
  3352. SCH_LEGACY_PLUGIN_CACHE::SaveSymbol( part, formatter );
  3353. }
  3354. const char* SCH_LEGACY_PLUGIN::PropBuffering = "buffering";
  3355. const char* SCH_LEGACY_PLUGIN::PropNoDocFile = "no_doc_file";