decoder.c
author nathan
Sat, 24 Oct 2009 11:31:53 +0800
branchtrunk
changeset 32 cea1b4f741be
parent 29 640ce9201139
child 33 65ed49cbc08b
permissions -rw-r--r--
ignore errors while scaning recursive directories
     1 /*
     2  * MP3/MPlayer plugin to VDR (C++)
     3  *
     4  * (C) 2001-2009 Stefan Huelswitt <s.huelswitt@gmx.de>
     5  *
     6  * This code is free software; you can redistribute it and/or
     7  * modify it under the terms of the GNU General Public License
     8  * as published by the Free Software Foundation; either version 2
     9  * of the License, or (at your option) any later version.
    10  *
    11  * This code is distributed in the hope that it will be useful,
    12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    14  * GNU General Public License for more details.
    15  *
    16  * You should have received a copy of the GNU General Public License
    17  * along with this program; if not, write to the Free Software
    18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
    19  * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
    20  */
    21 
    22 #include <stdlib.h>
    23 #include <stdio.h>
    24 #include <errno.h>
    25 #include <sys/stat.h>
    26 #include <sys/vfs.h>
    27 
    28 #include <vdr/videodir.h>
    29 
    30 #include "common.h"
    31 #include "data-mp3.h"
    32 #include "data-src.h"
    33 #include "decoder.h"
    34 #include "decoder-core.h"
    35 #include "decoder-mp3.h"
    36 #include "decoder-mp3-stream.h"
    37 #include "decoder-snd.h"
    38 #include "decoder-ogg.h"
    39 
    40 #define CACHEFILENAME     "id3info.cache"
    41 #define CACHESAVETIMEOUT  120 // secs
    42 #define CACHEPURGETIMEOUT 120 // days
    43 
    44 extern cFileSources MP3Sources;
    45 
    46 cInfoCache InfoCache;
    47 char *cachedir=0;
    48 
    49 int MakeHashBuff(const char *buff, int len)
    50 {
    51   int h=len;
    52   while(len--) h=(h*13 + *buff++) & 0x7ff;
    53   return h;
    54 }
    55 
    56 // --- cStrConv ----------------------------------------------------------------
    57 
    58 class cStrConv : private cMutex {
    59 private:
    60   cCharSetConv toSys;
    61 public:
    62   cStrConv(void):toSys("UTF-8",cCharSetConv::SystemCharacterTable()) {}
    63   char *ToSys(char *from);
    64   };
    65 
    66 static cStrConv *strconv;
    67   
    68 char *cStrConv::ToSys(char *from)
    69 {
    70   if(from) {
    71     Lock();
    72     const char *r=toSys.Convert(from);
    73     Unlock();
    74     if(r!=from) {
    75       char *n=strdup(r);
    76       if(n) {
    77         free(from);
    78         return n;
    79         }
    80       }
    81     }
    82   return from;
    83 }
    84 
    85 // --- cSongInfo ---------------------------------------------------------------
    86 
    87 cSongInfo::cSongInfo(void)
    88 {
    89   Title=Artist=Album=0;
    90   Clear();
    91 }
    92 
    93 cSongInfo::~cSongInfo()
    94 {
    95   Clear();
    96 }
    97 
    98 void cSongInfo::Clear(void)
    99 {
   100   Frames=0; Total=-1; DecoderID=0;
   101   SampleFreq=Channels=Bitrate=MaxBitrate=ChMode=-1;
   102   free(Title); Title=0;
   103   free(Artist); Artist=0;
   104   free(Album); Album=0;
   105   Year=-1;
   106   Level=Peak=0.0;
   107   infoDone=false; utf8clean=true;
   108 }
   109 
   110 void cSongInfo::Set(cSongInfo *si, bool update)
   111 {
   112   if(!update || si->Utf8Clean()) {
   113     Clear();
   114     Title=si->Title ? strdup(si->Title):0;
   115     Artist=si->Artist ? strdup(si->Artist):0;
   116     Album=si->Album ? strdup(si->Album):0;
   117     utf8clean=si->utf8clean;
   118     }
   119   Frames=si->Frames;
   120   Total=si->Total;
   121   SampleFreq=si->SampleFreq;
   122   Channels=si->Channels;
   123   Bitrate=si->Bitrate;
   124   MaxBitrate=si->MaxBitrate;
   125   ChMode=si->ChMode;
   126   Year=si->Year;
   127   if(si->Level>0.0) { // preserve old level
   128     Level=si->Level;
   129     Peak=si->Peak;
   130     }
   131   DecoderID=si->DecoderID;
   132   InfoDone();
   133 }
   134 
   135 void cSongInfo::FakeTitle(const char *filename, const char *extention)
   136 {
   137   // if no title, try to build a reasonable from the filename
   138   if(!Title && filename)  {
   139     char *s=rindex(filename,'/');
   140     if(s && *s=='/') {
   141       s++;
   142       Title=strdup(s);
   143       strreplace(Title,'_',' ');
   144       if(extention) {                            // strip given extention
   145         int l=strlen(Title)-strlen(extention);
   146         if(l>0 && !strcasecmp(Title+l,extention)) Title[l]=0;
   147         }
   148       else {                                     // strip any extention
   149         s=rindex(Title,'.');
   150         if(s && *s=='.' && strlen(s)<=5) *s=0;
   151         }
   152       d(printf("mp3: faking title '%s' from filename '%s'\n",Title,filename))
   153       }
   154     }
   155 }
   156 
   157 void cSongInfo::ConvertToSys(void)
   158 {
   159   if(cCharSetConv::SystemCharacterTable()) {
   160     Title=strconv->ToSys(Title);
   161     Artist=strconv->ToSys(Artist);
   162     Album=strconv->ToSys(Album);
   163     utf8clean=false;
   164     }
   165 }
   166 
   167 // --- cFileInfo ---------------------------------------------------------------
   168 
   169 cFileInfo::cFileInfo(void)
   170 {
   171   Filename=FsID=0; Clear();
   172 }
   173 
   174 cFileInfo::cFileInfo(const char *Name)
   175 {
   176   Filename=FsID=0; Clear();
   177   Filename=strdup(Name);
   178 }
   179 
   180 cFileInfo::~cFileInfo()
   181 {
   182   Clear();
   183 }
   184 
   185 void cFileInfo::Clear(void)
   186 {
   187   free(Filename); Filename=0;
   188   free(FsID); FsID=0;
   189   Filesize=0; CTime=0; FsType=0; removable=-1;
   190   infoDone=false;
   191 }
   192 
   193 bool cFileInfo::Removable(void)
   194 {
   195   if(removable<0 && Filename) {
   196     cFileSource *src=MP3Sources.FindSource(Filename);
   197     if(src) removable=src->NeedsMount();
   198     else removable=1;
   199     }
   200   return (removable!=0);
   201 }
   202 
   203 void cFileInfo::Set(cFileInfo *fi)
   204 {
   205   Clear(); InfoDone();
   206   Filename=fi->Filename ? strdup(fi->Filename):0;
   207   FsID=fi->FsID ? strdup(fi->FsID):0;
   208   Filesize=fi->Filesize;
   209   CTime=fi->CTime;
   210 }
   211 
   212 
   213 bool cFileInfo::FileInfo(bool log)
   214 {
   215   if(Filename) {
   216     struct stat64 ds;
   217     if(!stat64(Filename,&ds)) {
   218       if(S_ISREG(ds.st_mode)) {
   219         free(FsID); FsID=0;
   220         FsType=0;
   221         struct statfs64 sfs;
   222         if(!statfs64(Filename,&sfs)) {
   223           if(Removable()) FsID=aprintf("%llx:%llx",sfs.f_blocks,sfs.f_files);
   224           FsType=sfs.f_type;
   225           }
   226         else if(errno!=ENOSYS && log) { esyslog("ERROR: can't statfs %s: %s",Filename,strerror(errno)); }
   227         Filesize=ds.st_size;
   228         CTime=ds.st_ctime;
   229 #ifdef CDFS_MAGIC
   230         if(FsType==CDFS_MAGIC) CTime=0; // CDFS returns mount time as ctime
   231 #endif
   232         InfoDone();
   233         return true;
   234         }
   235       else if(log) { esyslog("ERROR: %s is not a regular file",Filename); }
   236       }
   237     else if(log) { esyslog("ERROR: can't stat %s: %s",Filename,strerror(errno)); }
   238     }
   239   return false;
   240 }
   241 
   242 // --- cDecoders ---------------------------------------------------------------
   243 
   244 cDecoder *cDecoders::FindDecoder(cFileObj *Obj)
   245 {
   246   const char *full=Obj->FullPath();
   247   cFileInfo fi(full);
   248   cCacheData *dat;
   249   cDecoder *decoder=0;
   250   if(fi.FileInfo(false) && (dat=InfoCache.Search(&fi))) {
   251     if(dat->DecoderID) {
   252       //d(printf("mp3: found DecoderID '%s' for %s from cache\n",cDecoders::ID2Str(dat->DecoderID),Filename))
   253       switch(dat->DecoderID) {
   254         case DEC_MP3:  decoder=new cMP3Decoder(full); break;
   255         case DEC_MP3S: decoder=new cMP3StreamDecoder(full); break;
   256 #ifdef HAVE_SNDFILE
   257         case DEC_SND:  decoder=new cSndDecoder(full); break;
   258 #endif
   259 #ifdef HAVE_VORBISFILE
   260         case DEC_OGG:  decoder=new cOggDecoder(full); break;
   261 #endif
   262         default:       esyslog("ERROR: bad DecoderID '%s' from info cache: %s",cDecoders::ID2Str(dat->DecoderID),full); break;
   263         }
   264       }
   265     dat->Unlock();
   266     }
   267 
   268   if(!decoder || !decoder->Valid()) {
   269     // no decoder in cache or cached decoder doesn't matches.
   270     // try to detect a decoder
   271 
   272     delete decoder; decoder=0;
   273 #ifdef HAVE_SNDFILE
   274     if(!decoder) {
   275       decoder=new cSndDecoder(full);
   276       if(!decoder || !decoder->Valid()) { delete decoder; decoder=0; }
   277       }
   278 #endif
   279 #ifdef HAVE_VORBISFILE
   280     if(!decoder) {
   281       decoder=new cOggDecoder(full);
   282       if(!decoder || !decoder->Valid()) { delete decoder; decoder=0; }
   283       }
   284 #endif
   285     if(!decoder) {
   286       decoder=new cMP3StreamDecoder(full);
   287       if(!decoder || !decoder->Valid()) { delete decoder; decoder=0; }
   288       }
   289     if(!decoder) {
   290       decoder=new cMP3Decoder(full);
   291       if(!decoder || !decoder->Valid()) { delete decoder; decoder=0; }
   292       }
   293     if(!decoder) esyslog("ERROR: no decoder found for %s",Obj->Name());
   294     }
   295   return decoder;
   296 }
   297 
   298 const char *cDecoders::ID2Str(int id)
   299 {
   300   switch(id) {
   301     case DEC_MP3:  return DEC_MP3_STR;
   302     case DEC_MP3S: return DEC_MP3S_STR;
   303     case DEC_SND:  return DEC_SND_STR;
   304     case DEC_OGG:  return DEC_OGG_STR;
   305     }
   306   return 0;
   307 }
   308 
   309 int cDecoders::Str2ID(const char *str)
   310 {
   311   if     (!strcmp(str,DEC_MP3_STR )) return DEC_MP3;
   312   else if(!strcmp(str,DEC_MP3S_STR)) return DEC_MP3S;
   313   else if(!strcmp(str,DEC_SND_STR )) return DEC_SND;
   314   else if(!strcmp(str,DEC_OGG_STR )) return DEC_OGG;
   315   return 0;
   316 }
   317 
   318 // --- cDecoder ----------------------------------------------------------------
   319 
   320 cDecoder::cDecoder(const char *Filename)
   321 {
   322   filename=strdup(Filename);
   323   locked=0; urgentLock=playing=false;
   324 }
   325 
   326 cDecoder::~cDecoder()
   327 {
   328   free(filename);
   329 }
   330 
   331 void cDecoder::Lock(bool urgent)
   332 {
   333   locklock.Lock();
   334   if(urgent && locked) urgentLock=true; // signal other locks to release quickly
   335   locked++;
   336   locklock.Unlock(); // don't hold the "locklock" when locking "lock", may cause a deadlock
   337   lock.Lock();
   338   urgentLock=false;
   339 }
   340 
   341 void cDecoder::Unlock(void)
   342 {
   343   locklock.Lock();
   344   locked--;
   345   lock.Unlock();
   346   locklock.Unlock();
   347 }
   348 
   349 bool cDecoder::TryLock(void)
   350 {
   351   bool res=false;
   352   locklock.Lock();
   353   if(!locked && !playing) {
   354     Lock();
   355     res=true;
   356     }
   357   locklock.Unlock();
   358   return res;
   359 }
   360 
   361 // --- cCacheData -----------------------------------------------------
   362 
   363 cCacheData::cCacheData(void)
   364 {
   365   touch=0; version=0;
   366 }
   367 
   368 void cCacheData::Touch(void)
   369 {
   370   touch=time(0);
   371 }
   372 
   373 #define SECS_PER_DAY (24*60*60)
   374 
   375 bool cCacheData::Purge(void)
   376 {
   377   time_t now=time(0);
   378   //XXX does this realy made sense?
   379   //if(touch+CACHEPURGETIMEOUT*SECS_PER_DAY < now) {
   380   //  d(printf("cache: purged: timeout %s\n",Filename))
   381   //  return true;
   382   //  }
   383   if(touch+CACHEPURGETIMEOUT*SECS_PER_DAY/10 < now) {
   384     if(!Removable()) {                            // is this a permant source?
   385       struct stat64 ds;                           // does the file exists? if not, purge
   386       if(stat64(Filename,&ds) || !S_ISREG(ds.st_mode) || access(Filename,R_OK)) {
   387         d(printf("cache: purged: file not found %s\n",Filename))
   388         return true;
   389         }
   390       }
   391     }
   392   return false;
   393 }
   394 
   395 bool cCacheData::Check8bit(const char *str)
   396 {
   397   if(str) while(*str) if(*str++ & 0x80) return true;
   398   return false;
   399 }
   400 
   401 bool cCacheData::Upgrade(void)
   402 {
   403   if(version<8) {
   404     if(Check8bit(Title) || Check8bit(Artist) || Check8bit(Album))
   405       return false;              // Trash entries not 7bit clean
   406     }
   407   if(version<7) {
   408     if(DecoderID==DEC_SND || (Title && startswith(Title,"track-")))
   409       return false;              // Trash older SND entries (incomplete)
   410 
   411     if(Removable()) {
   412       if(!FsID) FsID=strdup("old"); // Dummy entry, will be replaced in InfoCache::Search()
   413       }
   414     else { free(FsID); FsID=0; }
   415     }
   416   if(version<4) {
   417     Touch();                     // Touch entry
   418     }
   419   if(version<3 && !Title) {
   420     FakeTitle(Filename,".mp3");  // Fake title
   421     }
   422   if(version<2 && Bitrate<=0) {
   423     return false;                // Trash entry without bitrate
   424     }
   425   return true;
   426 }
   427 
   428 void cCacheData::Create(cFileInfo *fi, cSongInfo *si, bool update)
   429 {
   430   cFileInfo::Set(fi);
   431   cSongInfo::Set(si,update);
   432   hash=MakeHash(Filename);
   433   Touch();
   434 }
   435 
   436 bool cCacheData::Save(FILE *f)
   437 {
   438   fprintf(f,"##BEGIN\n"
   439             "Filename=%s\n"
   440             "Filesize=%lld\n"
   441             "Timestamp=%ld\n"
   442             "Touch=%ld\n"
   443             "Version=%d\n"
   444             "Frames=%d\n"
   445             "Total=%d\n"
   446             "SampleFreq=%d\n"
   447             "Channels=%d\n"
   448             "Bitrate=%d\n"
   449             "MaxBitrate=%d\n"
   450             "ChMode=%d\n"
   451             "Year=%d\n"
   452             "Level=%.4f\n"
   453             "Peak=%.4f\n",
   454             Filename,Filesize,CTime,touch,CACHE_VERSION,Frames,Total,SampleFreq,Channels,Bitrate,MaxBitrate,ChMode,Year,Level,Peak);
   455   if(Title)     fprintf(f,"Title=%s\n"    ,Title);
   456   if(Artist)    fprintf(f,"Artist=%s\n"   ,Artist);
   457   if(Album)     fprintf(f,"Album=%s\n"    ,Album);
   458   if(DecoderID) fprintf(f,"DecoderID=%s\n",cDecoders::ID2Str(DecoderID));
   459   if(FsID)      fprintf(f,"FsID=%s\n"     ,FsID);
   460   fprintf(f,"##END\n");
   461   return ferror(f)==0;
   462 }
   463 
   464 bool cCacheData::Load(FILE *f)
   465 {
   466   static const char delimiters[] = { "=\n" };
   467   char buf[1024];
   468 
   469   cFileInfo::Clear();
   470   cSongInfo::Clear();
   471   while(fgets(buf,sizeof(buf),f)) {
   472     char *ptrptr;
   473     char *name =strtok_r(buf ,delimiters,&ptrptr);
   474     char *value=strtok_r(0,delimiters,&ptrptr);
   475     if(name) {
   476       if(!strcasecmp(name,"##END")) break;
   477       if(value) {
   478         if     (!strcasecmp(name,"Filename"))   Filename  =strdup(value);
   479         else if(!strcasecmp(name,"Filesize") ||
   480                 !strcasecmp(name,"Size"))       Filesize  =atoll(value);
   481         else if(!strcasecmp(name,"FsID"))       FsID      =strdup(value);
   482         else if(!strcasecmp(name,"Timestamp"))  CTime     =atol(value);
   483         else if(!strcasecmp(name,"Touch"))      touch     =atol(value);
   484         else if(!strcasecmp(name,"Version"))    version   =atoi(value);
   485         else if(!strcasecmp(name,"DecoderID"))  DecoderID =cDecoders::Str2ID(value);
   486         else if(!strcasecmp(name,"Frames"))     Frames    =atoi(value);
   487         else if(!strcasecmp(name,"Total"))      Total     =atoi(value);
   488         else if(!strcasecmp(name,"SampleFreq")) SampleFreq=atoi(value);
   489         else if(!strcasecmp(name,"Channels"))   Channels  =atoi(value);
   490         else if(!strcasecmp(name,"Bitrate"))    Bitrate   =atoi(value);
   491         else if(!strcasecmp(name,"MaxBitrate")) MaxBitrate=atoi(value);
   492         else if(!strcasecmp(name,"ChMode"))     ChMode    =atoi(value);
   493         else if(!strcasecmp(name,"Year"))       Year      =atoi(value);
   494         else if(!strcasecmp(name,"Title"))      Title     =strdup(value);
   495         else if(!strcasecmp(name,"Artist"))     Artist    =strdup(value);
   496         else if(!strcasecmp(name,"Album"))      Album     =strdup(value);
   497         else if(!strcasecmp(name,"Level"))      Level     =atof(value);
   498         else if(!strcasecmp(name,"Peak"))       Peak      =atof(value);
   499         else d(printf("cache: ignoring bad token '%s' from cache file\n",name))
   500         }
   501       }
   502     }
   503 
   504   if(ferror(f) || !Filename) return false;
   505   hash=MakeHash(Filename);
   506   return true;
   507 }
   508 
   509 // --- cInfoCache ----------------------------------------------------
   510 
   511 cInfoCache::cInfoCache(void)
   512 {
   513   lasttime=0; modified=false;
   514   lastpurge=time(0)-(50*60);
   515 }
   516 
   517 void cInfoCache::Shutdown(void)
   518 {
   519   Cancel(10);
   520   Save(true);
   521 }
   522 
   523 void cInfoCache::Cache(cSongInfo *info, cFileInfo *file)
   524 {
   525   lock.Lock();
   526   cCacheData *dat=Search(file);
   527   if(dat) {
   528     dat->Create(file,info,true);
   529     Modified();
   530     dat->Unlock();
   531     d(printf("cache: updating infos for %s\n",file->Filename))
   532     }
   533   else {
   534     dat=new cCacheData;
   535     dat->Create(file,info,false);
   536     AddEntry(dat);
   537     d(printf("cache: caching infos for %s\n",file->Filename))
   538     }
   539   lock.Unlock();
   540 }
   541 
   542 cCacheData *cInfoCache::Search(cFileInfo *file)
   543 {
   544   int hash=MakeHash(file->Filename);
   545   lock.Lock();
   546   cCacheData *dat=FirstEntry(hash);
   547   while(dat) {
   548     if(dat->hash==hash && !strcmp(dat->Filename,file->Filename) && dat->Filesize==file->Filesize) {
   549       dat->Lock();
   550       if(file->FsID && dat->FsID && !strcmp(dat->FsID,"old")) { // duplicate FsID for old entries
   551         dat->FsID=strdup(file->FsID);
   552         dat->Touch(); Modified();
   553         //d(printf("adding FsID for %s\n",dat->Filename))
   554         }
   555 
   556       if((!file->FsID && !dat->FsID) || (file->FsID && dat->FsID && !strcmp(dat->FsID,file->FsID))) {
   557         //d(printf("cache: found cache entry for %s\n",dat->Filename))
   558         dat->Touch(); Modified();
   559         if(dat->CTime!=file->CTime) {
   560           d(printf("cache: ctime differs, removing from cache: %s\n",dat->Filename))
   561           DelEntry(dat); dat=0;
   562           }
   563         break;
   564         }
   565       dat->Unlock();
   566       }
   567     dat=(cCacheData *)dat->Next();
   568     }
   569   lock.Unlock();
   570   return dat;
   571 }
   572 
   573 void cInfoCache::AddEntry(cCacheData *dat)
   574 {
   575   lists[dat->hash%CACHELINES].Add(dat);
   576   Modified();
   577 }
   578 
   579 void cInfoCache::DelEntry(cCacheData *dat)
   580 {
   581   dat->Lock();
   582   lists[dat->hash%CACHELINES].Del(dat);
   583   Modified();
   584 }
   585 
   586 cCacheData *cInfoCache::FirstEntry(int hash)
   587 {
   588   return lists[hash%CACHELINES].First();
   589 }
   590 
   591 bool cInfoCache::Purge(void)
   592 {
   593   time_t now=time(0);
   594   if(now-lastpurge>(60*60)) {
   595     isyslog("cleaning up id3 cache");
   596     Start();
   597     lastpurge=now;
   598     }
   599   return Active();
   600 }
   601 
   602 void cInfoCache::Action(void)
   603 {
   604   d(printf("cache: id3 cache purge thread started (pid=%d)\n",getpid()))
   605   if(nice(3)<0);
   606   lock.Lock();
   607   for(int i=0,n=0 ; i<CACHELINES && Running(); i++) {
   608     cCacheData *dat=FirstEntry(i);
   609     while(dat && Running()) {
   610       cCacheData *ndat=(cCacheData *)dat->Next();
   611       if(dat->Purge()) DelEntry(dat);
   612       dat=ndat;
   613 
   614       if(++n>30) {
   615         lastmod=false; n=0;
   616         lock.Unlock(); lock.Lock();    // give concurrent thread an access chance
   617         if(lastmod) dat=FirstEntry(i); // restart line, if cache changed meanwhile
   618         }
   619       }
   620     }
   621   lock.Unlock();
   622   d(printf("cache: id3 cache purge thread ended (pid=%d)\n",getpid()))
   623 }
   624 
   625 char *cInfoCache::CacheFile(void)
   626 {
   627   return AddPath(cachedir?cachedir:VideoDirectory,CACHEFILENAME);
   628 }
   629 
   630 void cInfoCache::Save(bool force)
   631 {
   632   if(modified && (force || (!Purge() && time(0)>lasttime))) {
   633     char *name=CacheFile();
   634     cSafeFile f(name);
   635     free(name);
   636     if(f.Open()) {
   637       lock.Lock();
   638       fprintf(f,"## This is a generated file. DO NOT EDIT!!\n"
   639                 "## This file will be OVERWRITTEN WITHOUT WARNING!!\n");
   640       for(int i=0 ; i<CACHELINES ; i++) {
   641         cCacheData *dat=FirstEntry(i);
   642         while(dat) {
   643           if(!dat->Save(f)) { i=CACHELINES+1; break; }
   644           dat=(cCacheData *)dat->Next();
   645           }
   646         }
   647       lock.Unlock();
   648       f.Close();
   649       modified=false; lasttime=time(0)+CACHESAVETIMEOUT;
   650       d(printf("cache: saved cache to file\n"))
   651       }
   652     }
   653 }
   654 
   655 void cInfoCache::Load(void)
   656 {
   657   if(!strconv) strconv=new cStrConv;
   658 
   659   char *name=CacheFile();
   660   if(access(name,F_OK)==0) {
   661     isyslog("loading id3 cache from %s",name);
   662     FILE *f=fopen(name,"r");
   663     if(f) {
   664       char buf[256];
   665       bool mod=false;
   666       lock.Lock();
   667       for(int i=0 ; i<CACHELINES ; i++) lists[i].Clear();
   668       while(fgets(buf,sizeof(buf),f)) {
   669         if(!strcasecmp(buf,"##BEGIN\n")) {
   670           cCacheData *dat=new cCacheData;
   671           if(dat->Load(f)) {
   672             if(dat->version!=CACHE_VERSION) {
   673               if(dat->Upgrade()) mod=true;
   674               else { delete dat; continue; }
   675               }
   676             AddEntry(dat);
   677             }
   678           else {
   679             delete dat;
   680             if(ferror(f)) {
   681               esyslog("ERROR: failed to load id3 cache");
   682               break;
   683               }
   684             }
   685           }
   686         }
   687       lock.Unlock();
   688       fclose(f);
   689       modified=false; if(mod) Modified();
   690       }
   691     else LOG_ERROR_STR(name);
   692     }
   693   free(name);
   694 }