KiCad PCB EDA Suite
class_library.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2004-2016 Jean-Pierre Charras, jp.charras at wanadoo.fr
5  * Copyright (C) 2008 Wayne Stambaugh <stambaughw@gmail.com>
6  * Copyright (C) 2004-2020 KiCad Developers, see AUTHORS.txt for contributors.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, you may find one here:
20  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21  * or you may search the http://www.gnu.org website for the version 2 license,
22  * or you may write to the Free Software Foundation, Inc.,
23  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24  */
25 
30 #include <algorithm>
31 #include <fctsys.h>
32 #include <kiface_i.h>
33 #include <gr_basic.h>
34 #include <macros.h>
35 #include <eda_base_frame.h>
36 #include <kicad_string.h>
37 #include <gestfich.h>
38 #include <eda_doc.h>
39 #include <richio.h>
40 #include <config_params.h>
42 #include <project_rescue.h>
43 #include <properties.h>
44 
45 #include <general.h>
46 #include <class_library.h>
47 #include <sch_legacy_plugin.h>
48 
49 #include <wx/progdlg.h>
50 #include <wx/tokenzr.h>
51 #include <wx/regex.h>
52 
53 #define DUPLICATE_NAME_MSG \
54  _( "Library \"%s\" has duplicate entry name \"%s\".\n" \
55  "This may cause some unexpected behavior when loading components into a schematic." )
56 
57 
58 PART_LIB::PART_LIB( int aType, const wxString& aFileName, SCH_IO_MGR::SCH_FILE_T aPluginType ) :
59  // start @ != 0 so each additional library added
60  // is immediately detectable, zero would not be.
61  m_mod_hash( PART_LIBS::s_modify_generation ),
62  m_pluginType( aPluginType )
63 {
64  type = aType;
65  isModified = false;
66  timeStamp = 0;
67  timeStamp = wxDateTime::Now();
68  versionMajor = 0; // Will be updated after reading the lib file
69  versionMinor = 0; // Will be updated after reading the lib file
70 
71  fileName = aFileName;
72 
73  if( !fileName.IsOk() )
74  fileName = "unnamed.lib";
75 
76  m_plugin.reset( SCH_IO_MGR::FindPlugin( m_pluginType ) );
77  m_properties = std::make_unique<PROPERTIES>();
78 }
79 
80 
82 {
83 }
84 
85 
86 void PART_LIB::Save( bool aSaveDocFile )
87 {
88  wxCHECK_RET( m_plugin != NULL, wxString::Format( "no plugin defined for library `%s`.",
89  fileName.GetFullPath() ) );
90 
91  PROPERTIES props;
92 
93  if( !aSaveDocFile )
95 
96  m_plugin->SaveLibrary( fileName.GetFullPath(), &props );
97  isModified = false;
98 }
99 
100 
101 void PART_LIB::Create( const wxString& aFileName )
102 {
103  wxString tmpFileName = fileName.GetFullPath();
104 
105  if( !aFileName.IsEmpty() )
106  tmpFileName = aFileName;
107 
108  m_plugin->CreateSymbolLib( tmpFileName, m_properties.get() );
109 }
110 
111 
112 void PART_LIB::SetPluginType( SCH_IO_MGR::SCH_FILE_T aPluginType )
113 {
114  if( m_pluginType != aPluginType )
115  {
116  m_pluginType = aPluginType;
117  m_plugin.reset( SCH_IO_MGR::FindPlugin( m_pluginType ) );
118  }
119 }
120 
121 
122 bool PART_LIB::IsCache() const
123 {
125 }
126 
127 
129 {
130  (*m_properties)[ SCH_LEGACY_PLUGIN::PropNoDocFile ] = "";
131 }
132 
133 
135 {
137 }
138 
139 
140 void PART_LIB::EnableBuffering( bool aEnable )
141 {
142  if( aEnable )
143  (*m_properties)[ SCH_LEGACY_PLUGIN::PropBuffering ] = "";
144  else
146 }
147 
148 
149 void PART_LIB::GetPartNames( wxArrayString& aNames ) const
150 {
151  m_plugin->EnumerateSymbolLib( aNames, fileName.GetFullPath(), m_properties.get() );
152 
153  aNames.Sort();
154 }
155 
156 
157 void PART_LIB::GetParts( std::vector<LIB_PART*>& aSymbols ) const
158 {
159  m_plugin->EnumerateSymbolLib( aSymbols, fileName.GetFullPath(), m_properties.get() );
160 
161  std::sort( aSymbols.begin(), aSymbols.end(),
162  [](LIB_PART *lhs, LIB_PART *rhs) -> bool
163  { return lhs->GetName() < rhs->GetName(); });
164 }
165 
166 
167 LIB_PART* PART_LIB::FindPart( const wxString& aName ) const
168 {
169  LIB_PART* symbol = m_plugin->LoadSymbol( fileName.GetFullPath(), aName, m_properties.get() );
170 
171  // Set the library to this even though technically the legacy cache plugin owns the
172  // symbols. This allows the symbol library table conversion tool to determine the
173  // correct library where the symbol was found.
174  if( symbol && !symbol->GetLib() )
175  symbol->SetLib( const_cast<PART_LIB*>( this ) );
176 
177  return symbol;
178 }
179 
180 
181 LIB_PART* PART_LIB::FindPart( const LIB_ID& aLibId ) const
182 {
183  return FindPart( aLibId.Format().wx_str() );
184 }
185 
186 
188 {
189  // add a clone, not the caller's copy, the plugin take ownership of the new symbol.
190  m_plugin->SaveSymbol( fileName.GetFullPath(), new LIB_PART( *aPart->SharedPtr().get(), this ),
191  m_properties.get() );
192 
193  // If we are not buffering, the library file is updated immediately when the plugin
194  // SaveSymbol() function is called.
195  if( IsBuffering() )
196  isModified = true;
197 
198  ++m_mod_hash;
199 }
200 
201 
203 {
204  wxCHECK_MSG( aEntry != NULL, NULL, "NULL pointer cannot be removed from library." );
205 
206  m_plugin->DeleteSymbol( fileName.GetFullPath(), aEntry->GetName(), m_properties.get() );
207 
208  // If we are not buffering, the library file is updated immediately when the plugin
209  // SaveSymbol() function is called.
210  if( IsBuffering() )
211  isModified = true;
212 
213  ++m_mod_hash;
214  return NULL;
215 }
216 
217 
219 {
220  wxASSERT( aOldPart != NULL );
221  wxASSERT( aNewPart != NULL );
222 
223  m_plugin->DeleteSymbol( fileName.GetFullPath(), aOldPart->GetName(), m_properties.get() );
224 
225  LIB_PART* my_part = new LIB_PART( *aNewPart, this );
226 
227  m_plugin->SaveSymbol( fileName.GetFullPath(), my_part, m_properties.get() );
228 
229  // If we are not buffering, the library file is updated immediately when the plugin
230  // SaveSymbol() function is called.
231  if( IsBuffering() )
232  isModified = true;
233 
234  ++m_mod_hash;
235  return my_part;
236 }
237 
238 
239 PART_LIB* PART_LIB::LoadLibrary( const wxString& aFileName )
240 {
241  std::unique_ptr<PART_LIB> lib( new PART_LIB( LIBRARY_TYPE_EESCHEMA, aFileName ) );
242 
243  std::vector<LIB_PART*> parts;
244  // This loads the library.
245  lib->GetParts( parts );
246 
247  // Now, set the LIB_PART m_library member but it will only be used
248  // when loading legacy libraries in the future. Once the symbols in the
249  // schematic have a full #LIB_ID, this will not get called.
250  for( size_t ii = 0; ii < parts.size(); ii++ )
251  {
252  LIB_PART* part = parts[ii];
253 
254  part->SetLib( lib.get() );
255  }
256 
257  PART_LIB* ret = lib.release();
258  return ret;
259 }
260 
261 
262 PART_LIB* PART_LIBS::AddLibrary( const wxString& aFileName )
263 {
264  PART_LIB* lib;
265 
266  wxFileName fn = aFileName;
267  // Don't reload the library if it is already loaded.
268  lib = FindLibrary( fn.GetName() );
269 
270  if( lib )
271  return lib;
272 
273  try
274  {
275  lib = PART_LIB::LoadLibrary( aFileName );
276  push_back( lib );
277 
278  return lib;
279  }
280  catch( ... )
281  {
282  return nullptr;
283  }
284 }
285 
286 
287 PART_LIB* PART_LIBS::AddLibrary( const wxString& aFileName, PART_LIBS::iterator& aIterator )
288 {
289  // Don't reload the library if it is already loaded.
290  wxFileName fn( aFileName );
291  PART_LIB* lib = FindLibrary( fn.GetName() );
292 
293  if( lib )
294  return lib;
295 
296  try
297  {
298  lib = PART_LIB::LoadLibrary( aFileName );
299 
300  if( aIterator >= begin() && aIterator < end() )
301  insert( aIterator, lib );
302  else
303  push_back( lib );
304 
305  return lib;
306  }
307  catch( ... )
308  {
309  return nullptr;
310  }
311 }
312 
313 
314 PART_LIB* PART_LIBS::FindLibrary( const wxString& aName )
315 {
316  for( PART_LIBS::iterator it = begin(); it!=end(); ++it )
317  {
318  if( it->GetName() == aName )
319  return &*it;
320  }
321 
322  return NULL;
323 }
324 
325 
327 {
328  for( PART_LIBS::iterator it = begin(); it!=end(); ++it )
329  {
330  if( it->IsCache() )
331  return &*it;
332  }
333 
334  return NULL;
335 }
336 
337 
338 PART_LIB* PART_LIBS::FindLibraryByFullFileName( const wxString& aFullFileName )
339 {
340  for( PART_LIBS::iterator it = begin(); it!=end(); ++it )
341  {
342  if( it->GetFullFileName() == aFullFileName )
343  return &*it;
344  }
345 
346  return NULL;
347 }
348 
349 
350 wxArrayString PART_LIBS::GetLibraryNames( bool aSorted )
351 {
352  wxArrayString cacheNames;
353  wxArrayString names;
354 
355  for( PART_LIB& lib : *this )
356  {
357  if( lib.IsCache() && aSorted )
358  cacheNames.Add( lib.GetName() );
359  else
360  names.Add( lib.GetName() );
361  }
362 
363  // Even sorted, the cache library is always at the end of the list.
364  if( aSorted )
365  names.Sort();
366 
367  for( unsigned int i = 0; i<cacheNames.Count(); i++ )
368  names.Add( cacheNames.Item( i ) );
369 
370  return names;
371 }
372 
373 
374 LIB_PART* PART_LIBS::FindLibPart( const LIB_ID& aLibId, const wxString& aLibraryName )
375 {
376  LIB_PART* part = NULL;
377 
378  for( PART_LIB& lib : *this )
379  {
380  if( !aLibraryName.IsEmpty() && lib.GetName() != aLibraryName )
381  continue;
382 
383  part = lib.FindPart( aLibId.GetLibItemName().wx_str() );
384 
385  if( part )
386  break;
387  }
388 
389  return part;
390 }
391 
392 
393 void PART_LIBS::FindLibraryNearEntries( std::vector<LIB_PART*>& aCandidates,
394  const wxString& aEntryName,
395  const wxString& aLibraryName )
396 {
397  for( PART_LIB& lib : *this )
398  {
399  if( !aLibraryName.IsEmpty() && lib.GetName() != aLibraryName )
400  continue;
401 
402  wxArrayString partNames;
403 
404  lib.GetPartNames( partNames );
405 
406  if( partNames.IsEmpty() )
407  continue;
408 
409  for( size_t i = 0; i < partNames.size(); i++ )
410  {
411  if( partNames[i].CmpNoCase( aEntryName ) == 0 )
412  aCandidates.push_back( lib.FindPart( partNames[i] ) );
413  }
414  }
415 }
416 
417 
418 int PART_LIBS::s_modify_generation = 1; // starts at 1 and goes up
419 
420 
422 {
423  int hash = 0;
424 
425  for( PART_LIBS::const_iterator it = begin(); it != end(); ++it )
426  {
427  hash += it->GetModHash();
428  }
429 
430  // Rebuilding the cache (m_cache) does not change the GetModHash() value,
431  // but changes PART_LIBS::s_modify_generation.
432  // Take this change in account:
434 
435  return hash;
436 }
437 
438 
439 void PART_LIBS::LibNamesAndPaths( PROJECT* aProject, bool doSave,
440  wxString* aPaths, wxArrayString* aNames )
441 {
442  wxString pro = aProject->GetProjectFullName();
443 
444  std::vector<PARAM_CFG*> ca;
445 
446  try
447  {
448  if( aPaths )
449  ca.push_back( new PARAM_CFG_FILENAME( "LibDir", aPaths ) );
450 
451  if( aNames )
452  ca.push_back( new PARAM_CFG_LIBNAME_LIST( wxT( "LibName" ), aNames, GROUP_SCH_LIBS ) );
453  }
454  catch( boost::bad_pointer& )
455  {
456  // Out of memory? Ship's going down anyway....
457  }
458 
459  if( doSave )
460  {
461  aProject->ConfigSave( Kiface().KifaceSearch(), GROUP_SCH, ca );
462 
463  /*
464  {
465  wxString msg = wxString::Format( _(
466  "Unable save project's \"%s\" file" ),
467  GetChars( pro )
468  );
469  THROW_IO_ERROR( msg );
470  }
471  */
472  }
473  else
474  {
475  if( !aProject->ConfigLoad( Kiface().KifaceSearch(), GROUP_SCH, ca ) )
476  {
477  wxString msg = wxString::Format( _(
478  "Unable to load project's \"%s\" file" ),
479  GetChars( pro )
480  );
481  THROW_IO_ERROR( msg );
482  }
483  }
484 }
485 
486 
487 const wxString PART_LIBS::CacheName( const wxString& aFullProjectFilename )
488 {
489  wxFileName name = aFullProjectFilename;
490 
491  name.SetName( name.GetName() + "-cache" );
493 
494  if( name.FileExists() )
495  return name.GetFullPath();
496 
497  return wxEmptyString;
498 }
499 
500 
501 void PART_LIBS::LoadAllLibraries( PROJECT* aProject, bool aShowProgress )
502 {
503  wxString filename;
504  wxString libs_not_found;
505  SEARCH_STACK* lib_search = aProject->SchSearchS();
506 
507 #if defined(DEBUG) && 0
508  lib_search->Show( __func__ );
509 #endif
510 
511  wxArrayString lib_names;
512 
513  LibNamesAndPaths( aProject, false, NULL, &lib_names );
514 
515  // Post symbol library table, this should be empty. Only the cache library should get loaded.
516  if( !lib_names.empty() )
517  {
518  wxProgressDialog lib_dialog( _( "Loading Symbol Libraries" ),
519  wxEmptyString,
520  lib_names.GetCount(),
521  NULL,
522  wxPD_APP_MODAL );
523 
524  if( aShowProgress )
525  {
526  lib_dialog.Show();
527  }
528 
529  wxString progress_message;
530 
531  for( unsigned i = 0; i < lib_names.GetCount(); ++i )
532  {
533  if( aShowProgress )
534  {
535  lib_dialog.Update( i, _( "Loading " + lib_names[i] ) );
536  }
537 
538  // lib_names[] does not store the file extension. Set it.
539  // Remember lib_names[i] can contain a '.' in name, so using a wxFileName
540  // before adding the extension can create incorrect full filename
541  wxString fullname = lib_names[i] + "." + LegacySymbolLibFileExtension;
542  // Now the full name is set, we can use a wxFileName.
543  wxFileName fn( fullname );
544 
545  // Skip if the file name is not valid..
546  if( !fn.IsOk() )
547  continue;
548 
549  if( !fn.FileExists() )
550  {
551  filename = lib_search->FindValidPath( fn.GetFullPath() );
552 
553  if( !filename )
554  {
555  libs_not_found += fn.GetFullPath();
556  libs_not_found += '\n';
557  continue;
558  }
559  }
560  else
561  { // ensure the lib filename has a absolute path.
562  // If the lib has no absolute path, and is found in the cwd by fn.FileExists(),
563  // make a full absolute path, to avoid issues with load library functions which
564  // expects an absolute path.
565  if( !fn.IsAbsolute() )
566  fn.MakeAbsolute();
567 
568  filename = fn.GetFullPath();
569  }
570 
571  try
572  {
573  AddLibrary( filename );
574  }
575  catch( const IO_ERROR& ioe )
576  {
577  wxString msg;
578  msg.Printf( _( "Symbol library \"%s\" failed to load. Error:\n %s" ),
579  GetChars( filename ), GetChars( ioe.What() ) );
580 
581  wxLogError( msg );
582  }
583  }
584  }
585 
586  // add the special cache library.
587  wxString cache_name = CacheName( aProject->GetProjectFullName() );
588  PART_LIB* cache_lib;
589 
590  if( !cache_name.IsEmpty() )
591  {
592  try
593  {
594  cache_lib = AddLibrary( cache_name );
595 
596  if( cache_lib )
597  cache_lib->SetCache();
598  }
599  catch( const IO_ERROR& ioe )
600  {
601  wxString msg = wxString::Format( _(
602  "Symbol library \"%s\" failed to load.\nError: %s" ),
603  GetChars( cache_name ),
604  GetChars( ioe.What() )
605  );
606 
607  THROW_IO_ERROR( msg );
608  }
609  }
610 
611  // Print the libraries not found
612  if( !libs_not_found.IsEmpty() )
613  {
614  // Use a different exception type so catch()er can route to proper use
615  // of the HTML_MESSAGE_BOX.
616  THROW_PARSE_ERROR( wxEmptyString, __func__, TO_UTF8( libs_not_found ), 0, 0 );
617  }
618 
619 #if defined(DEBUG) && 1
620  printf( "%s: lib_names:\n", __func__ );
621 
622  for( PART_LIBS::const_iterator it = begin(); it < end(); ++it )
623  printf( " %s\n", TO_UTF8( it->GetName() ) );
624 #endif
625 }
PART_LIB * GetLib()
const wxString GetName() const
Return the file name without path or extension.
int m_mod_hash
incremented each time library is changed.
void SetLib(PART_LIB *aLibrary)
wxString FindValidPath(const wxString &aFileName) const
Definition: search_stack.h:73
const UTF8 & GetLibItemName() const
Definition: lib_id.h:114
LIB_PART * ReplacePart(LIB_PART *aOldPart, LIB_PART *aNewPart)
Replace an existing part entry in the library.
std::unique_ptr< SCH_PLUGIN > m_plugin
static void LibNamesAndPaths(PROJECT *aProject, bool doSave, wxString *aPaths, wxArrayString *aNames=NULL)
Save or load the names of the currently configured part libraries (without paths).
PROJECT holds project specific data.
Definition: project.h:59
This file is part of the common library TODO brief description.
void LoadAllLibraries(PROJECT *aProject, bool aShowProgress=true)
Load all of the project's libraries into this container, which should be cleared before calling it.
wxString GetName() const override
static int s_modify_generation
helper for GetModifyHash()
void Save(bool aSaveDocFile=true)
bool isModified
Library modification status.
const std::string LegacySymbolLibFileExtension
This file is part of the common library.
PART_LIB * GetCacheLibrary()
wxArrayString GetLibraryNames(bool aSorted=true)
Return the list of part library file names without path and extension.
int GetModifyHash()
Return the modification hash for all libraries.
void FindLibraryNearEntries(std::vector< LIB_PART * > &aCandidates, const wxString &aEntryName, const wxString &aLibraryName=wxEmptyString)
Search all libraries in the list for a LIB_PART using a case insensitive comparison.
int type
Library type indicator.
LIB_PART * RemovePart(LIB_PART *aEntry)
Safely remove aEntry from the library and return the next entry.
SEARCH_STACK looks for files in a number of places.
Definition: search_stack.h:41
A logical library item identifier and consists of various portions much like a URI.
Definition: lib_id.h:51
PART_LIB * FindLibrary(const wxString &aName)
Find a part library by aName.
static const wxString CacheName(const wxString &aFullProjectFilename)
Return the name of the cache library after potentially fixing it from an older naming scheme.
PROPERTIES is a name/value tuple with unique names and optional values.
Definition: properties.h:34
void GetPartNames(wxArrayString &aNames) const
Load a string array with the names of all the entries in this library.
Configuration parameter - PARAM_CFG_FILENAME Class Same as PARAM_CFG_WXSTRING, but stores "\" as "/".
This file contains miscellaneous commonly used macros and functions.
VTBL_ENTRY bool ConfigLoad(const SEARCH_STACK &aSearchS, const wxString &aGroupName, const std::vector< PARAM_CFG * > &aParams, const wxString &aForeignConfigFileName=wxEmptyString)
Function ConfigLoad reads a subset of parameters from the "project" file.
Definition: project.cpp:407
bool IsBuffering() const
void EnableBuffering(bool aEnable=true)
#define NULL
virtual const wxString What() const
A composite of Problem() and Where()
Definition: exceptions.cpp:33
wxDateTime timeStamp
Library save time and date.
KIFACE_I & Kiface()
Global KIFACE_I "get" accessor.
PART_LIB * AddLibrary(const wxString &aFileName)
Allocate and adds a part library to the library list.
PART_SPTR SharedPtr()
static PART_LIB * LoadLibrary(const wxString &aFileName)
Allocate and load a symbol library file.
Define a library symbol object.
Definition of file extensions used in Kicad.
Base window classes and related definitions.
VTBL_ENTRY void ConfigSave(const SEARCH_STACK &aSList, const wxString &aGroupName, const std::vector< PARAM_CFG * > &aParams, const wxString &aFileName=wxEmptyString)
Function ConfigSave saves the current "project" parameters into the wxConfigBase* derivative.
Definition: project.cpp:362
#define GROUP_SCH
Definition: config_params.h:41
void SetCache()
std::unique_ptr< PROPERTIES > m_properties
Library properties.
#define GROUP_SCH_LIBS
(Now in fp lib tables)
Definition: config_params.h:47
LIB_PART * FindLibPart(const LIB_ID &aLibId, const wxString &aLibraryName=wxEmptyString)
Search all libraries in the list for a part.
#define THROW_IO_ERROR(msg)
static const char * PropNoDocFile
The property used internally by the plugin to disable writing the library documentation (....
VTBL_ENTRY const wxString GetProjectFullName() const
Function GetProjectFullName returns the full path and name of the project.
Definition: project.cpp:108
SCH_IO_MGR::SCH_FILE_T m_pluginType
#define THROW_PARSE_ERROR(aProblem, aSource, aInputLine, aLineNumber, aByteIndex)
LIB_PART * FindPart(const wxString &aName) const
Find LIB_PART by aName.
UTF8 Format() const
Definition: lib_id.cpp:237
YYCODETYPE lhs
wxFileName fileName
Library file name.
void GetParts(std::vector< LIB_PART * > &aPart) const
Load a vector with all the entries in this library.
A collection of PART_LIB objects.
void AddPart(LIB_PART *aPart)
Add aPart entry to library.
static const wxChar * GetChars(const wxString &s)
Function GetChars returns a wxChar* to the actual wxChar* data within a wxString, and is helpful for ...
Definition: macros.h:153
const char * name
Definition: DXF_plotter.cpp:60
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, CPTREE &aTree)
Function Format outputs a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:205
#define _(s)
Definition: 3d_actions.cpp:33
int versionMinor
Library minor version number.
wxString wx_str() const
Definition: utf8.cpp:51
#define TO_UTF8(wxstring)
static const char * PropBuffering
The property used internally by the plugin to enable cache buffering which prevents the library file ...
PART_LIB * FindLibraryByFullFileName(const wxString &aFullFileName)
void SetPluginType(SCH_IO_MGR::SCH_FILE_T aPluginType)
Definition for part library class.
PART_LIB(int aType, const wxString &aFileName, SCH_IO_MGR::SCH_FILE_T aPluginType=SCH_IO_MGR::SCH_LEGACY)
void Create(const wxString &aFileName=wxEmptyString)
Object used to load, save, search, and otherwise manipulate symbol library files.
Struct IO_ERROR is a class used to hold an error message and may be used when throwing exceptions con...
Definition: ki_exception.h:76
bool IsCache() const
int versionMajor
Library major version number.