//**************************************************************************
//*                     This file is part of the                           *
//*                      Mpxplay - audio player.                           *
//*                  The source code of Mpxplay is                         *
//*        (C) copyright 1998-2013 by PDSoft (Attila Padar)                *
//*                http://mpxplay.sourceforge.net                          *
//*                  email: mpxplay@freemail.hu                            *
//**************************************************************************
//*  This program is distributed in the hope that it will be useful,       *
//*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *
//*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                  *
//*  Please contact with the author (with me) if you want to use           *
//*  or modify this source.                                                *
//**************************************************************************
//function:check playlist entries

//#define MPXPLAY_USE_DEBUGF 1
#define MPXPLAY_DEBUG_OUTPUT stdout

#include "mpxplay.h"
#include "control\control.h"
#include "display\display.h"
#include "deparser\tagging.h"
#include "diskdriv\diskdriv.h"
#include "mpxinbuf.h"
#include <ctype.h>
#include <io.h>

static void get_fileinfos_under_play(struct mainvars *mvp);
static unsigned int playlist_chkentry_get_onefileinfos_check(struct playlist_side_info *psi,struct playlist_entry_info *pei);
static unsigned int chkentry_get_onefileinfos_common(struct playlist_side_info *psi,struct playlist_entry_info *pei,struct mpxpframe_s *frp,unsigned int id3loadflags,unsigned int openmode,int *err_code);
static unsigned int get_onefileinfos_from_tagset(struct playlist_entry_info *pei_dest);
static unsigned int get_onefileinfos_from_previndex(struct playlist_side_info *psi,struct playlist_entry_info *pei,unsigned int id3loadflags);
//static unsigned int get_onefileinfos_from_anytab(struct playlist_side_info *psi_dest,struct playlist_entry_info *pei_dest,unsigned int id3loadflags);
static unsigned int get_onefileinfos_from_anyside(struct playlist_side_info *psi_dest,struct playlist_entry_info *pei_dest,struct playlist_side_info *psi_src,unsigned int id3loadflags);

extern char *id3tagset[I3I_MAX+1];
extern mpxp_uint32_t mpxplay_playlistcontrol;
extern unsigned int playcontrol,playrand,preloadinfo,loadid3tag;
extern unsigned int refdisp,fullelapstime,timemode,desktopmode;
#ifdef MPXPLAY_GUI_CONSOLE
extern unsigned int is_lfn_support,uselfn,id3textconv;
#endif

void playlist_chkentry_get_allfileinfos(struct mainvars *mvp)
{
 struct playlist_side_info *psi=mvp->psi0;
 struct playlist_entry_info *pei,*dispend;
 int tabnum,allsongs;
 char sout[64];

 for(tabnum=0;tabnum<mvp->editorside_all_tabs;tabnum++,psi++){
  if(psi->editsidetype&PLT_ENABLED){
   if(playlist_sortlist_is_preordered_type(psi))
    playlist_order_side(psi);
   if((psi->editsidetype&PLT_EXTENDED) && !(psi->editsidetype&PLT_EXTENDINC))
    playlist_fulltime_getside(psi);
   else{
    switch(preloadinfo){
     case PLI_PRELOAD:  // -irl
      pei=psi->firstsong;
#ifdef MPXPLAY_GUI_CONSOLE
      dispend=pei+mpxplay_display_editor_getlinenums(psi);
#endif
      allsongs=psi->lastentry-pei+1;
      display_clear_timed_message();
      while(pei<=psi->lastentry){
       if(pds_kbhit())
        if(pds_extgetch()==KEY_ESC)
         break;
       sprintf(sout,"Preloading file informations (%2d/%d) ",pei-psi->firstsong+1,allsongs);
       display_message(0,0,sout);
       display_message(1,0,"(press ESC to terminate it)");
       playlist_chkentry_get_onefileinfos_check(psi,pei);
       playlist_order_entry_block(psi,pei,psi->firstentry,pei);
       if(psi==mvp->psip)
        draw_browserbox(mvp,pei);
#ifdef MPXPLAY_GUI_CONSOLE
      if(pei<dispend)
       draweditor(mvp);
#else
       display_draw_editor_oneline(psi, pei, -1);
#endif
       pei++;
      }
	  clear_message();
     if(pei<=psi->lastentry)
      playlist_chkfile_start_norm(psi,pei);
      break;
     case PLI_PLAYLOAD: // -ipl
      playlist_chkfile_start_norm(psi,NULL);
      break;
    }
   }
   funcbit_disable(psi->editsidetype,(PLT_EXTENDED|PLT_EXTENDINC));
  }
 }
 if(preloadinfo==PLI_PRELOAD) // !!!
  preloadinfo=PLI_DISPLOAD;   //
}

static void get_fileinfos_under_play(struct mainvars *mvp)
{
 unsigned int fastfound,fastcount,tabnum;
 struct playlist_side_info *psi;
 struct playlist_entry_info *pei,*spei;
 static unsigned int disp_counter;
 char sout[64];

 psi=mvp->psie;
 if(!psi->chkfilenum_curr)
  psi=mvp->psie->psio;
 if(!psi->chkfilenum_curr){
  psi=mvp->psi0;
  tabnum=0;
  do{
   if(psi->chkfilenum_curr)
    break;
   if(++tabnum>=mvp->editorside_all_tabs){
    mpxplay_timer_deletefunc(&get_fileinfos_under_play,NULL);
    return;
   }
   psi++;
  }while(1);
 }

 pei=psi->chkfilenum_curr;
 if(pei<=psi->chkfilenum_end){
  fastcount = 15;
  do{
   if((psi->editloadtype&PLL_FILTERED) && (pei->infobits&PEIF_FILTEROUT)){
    funcbit_enable(psi->editloadtype, PLL_FILTCHKSKP);
    pei++;
    continue;
   }
   fastfound=playlist_chkentry_get_onefileinfos_check(psi,pei);
   switch(preloadinfo){
    case PLI_DISPLOAD:
     display_draw_editor_oneline(psi,pei,-1);
     pei++;
     break;
    default:
     spei=playlist_order_entry_block(psi,pei,psi->firstentry,pei);
     if(!spei || spei==pei)
      display_draw_editor_oneline(psi,pei,-1);
     else
      refdisp|=RDT_EDITOR; // ??? better solution?
     pei++;
   }
   if(!fastfound)
    break;
  }while((--fastcount) && (pei<=psi->chkfilenum_end));
  psi->chkfilenum_curr=pei;
  if(!(playcontrol&PLAYC_RUNNING)){
   if((fastcount || !disp_counter) && (pei<=psi->chkfilenum_end)){
    sprintf(sout,"Loading file informations (%2d/%d)",(int)(pei-psi->firstsong+1),(int)(psi->lastentry-psi->firstsong+1));
    display_message(0,0,sout);
#ifdef MPXPLAY_GUI_CONSOLE
    sout[0]=0;
    display_message(1,0,sout);
#endif
    disp_counter = 39;
   }
   if(disp_counter)
    disp_counter--;
  }
 }else{
  playlist_chkfile_stop(psi);
  if(psi==mvp->psip)
   refdisp|=RDT_BROWSER;
  refdisp|=RDT_EDITOR;
 }
}

//--------------------------------------------------------------------------

int playlist_chkentry_get_onefileinfos_play(struct playlist_side_info *psi,struct playlist_entry_info *pei,struct mpxpframe_s *frp)
{
 unsigned int openmode;
 int retcode = MPXPLAY_ERROR_INFILE_CANTOPEN;
 if(pei->entrytype==DFT_UNKNOWN)
  return retcode;
 openmode=MPXPLAY_INFILE_OPENMODE_PLAY;
 if(pei->entrytype==DFT_NOTCHECKED)
  funcbit_enable(openmode,MPXPLAY_INFILE_OPENMODE_CHECK);
 mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT, "playlist_chkentry_get_onefileinfos_play chkentry_get_onefileinfos_common BEGIN");
 chkentry_get_onefileinfos_common(psi,pei,frp,loadid3tag,openmode,&retcode);
 mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT, "playlist_chkentry_get_onefileinfos_play chkentry_get_onefileinfos_common END ret:%d", retcode);
 if(retcode != MPXPLAY_ERROR_OK) // FIXME: something is wrong in sub-routines
  mpxplay_infile_close(frp);
 return retcode;
} // file is open

int playlist_chkentry_get_onefileinfos_entry(struct playlist_side_info *psi,struct playlist_entry_info *pei)
{
 int retcode = MPXPLAY_ERROR_OK;
 if((pei<psi->firstentry) || (pei>psi->lastentry))
  return retcode;
 if(pei->entrytype==DFT_NOTCHECKED){
  pei->entrytype=DFT_UNKNOWN;
  chkentry_get_onefileinfos_common(psi, pei, psi->mvp->fr_check, loadid3tag, MPXPLAY_INFILE_OPENMODE_CHECK, &retcode);
  mpxplay_infile_close(psi->mvp->fr_check);
  if((pei->entrytype == DFT_UNKNOWN) && (retcode == MPXPLAY_ERROR_DISKDRIV_OPENMODE)) // FIXME: hack for DTV + crossfade
   pei->entrytype = DFT_NOTCHECKED;
 }
 return retcode;
} // file is closed

static unsigned int playlist_chkentry_get_onefileinfos_check(struct playlist_side_info *psi,struct playlist_entry_info *pei)
{
 unsigned int fastfound=0;
 struct pds_find_t ffblk;

 if((pei<psi->firstentry) || (pei>psi->lastentry))
  return 0;
 if((!pei->filesize || !pei->filedate.month) && (((desktopmode&DTM_MASK_COMMANDER)==DTM_MASK_COMMANDER) || (psi->id3ordernum_primary==ID3ORDER_FILESIZE) ||  (psi->id3ordernum_primary==ID3ORDER_FILEDATE))){
  struct mpxplay_diskdrive_data_s *mdds=playlist_loaddir_path_to_drivehandler(psi, pei->filename);
  if(!mdds)
   mdds=pei->mdds;
  fastfound=mpxplay_diskdrive_findfirst(mdds,pei->filename,_A_NORMAL,&ffblk);
  if(fastfound==0){
   pei->filesize=ffblk.size;
   *((mpxp_uint32_t *)&pei->filedate)=*((mpxp_uint32_t *)&ffblk.fdate);
   mpxplay_diskdrive_findclose(mdds,&ffblk);
   funcbit_enable(psi->editloadtype,PLL_CHG_LEN);
  }
  fastfound=EDITLIST_MODE_HEAD;
 }
 if((desktopmode&DTM_MASK_COMMANDER)!=DTM_MASK_COMMANDER){
  fastfound=chkentry_get_onefileinfos_common(psi,pei,psi->mvp->fr_check,(loadid3tag|ID3LOADMODE_OTHERENTRY|ID3LOADMODE_FASTSEARCH),MPXPLAY_INFILE_OPENMODE_CHECK,NULL);
  mpxplay_infile_close(psi->mvp->fr_check);
 }
 return fastfound;
} // file is closed

void playlist_chkentry_get_onefileinfos_is(struct playlist_side_info *psi,struct playlist_entry_info *pei)
{
 if((pei<psi->firstentry) || (pei>psi->lastentry))
  return;
 if(pei->entrytype==DFT_NOTCHECKED)
  pei->entrytype=DFT_UNKNOWN;
 chkentry_get_onefileinfos_common(psi,pei,psi->mvp->fr_check,loadid3tag,MPXPLAY_INFILE_OPENMODE_CHECK,NULL);
} // file is open

void playlist_chkentry_get_onefileinfos_allagain(struct playlist_side_info *psi,struct playlist_entry_info *pei,struct mpxpframe_s *frp,unsigned int id3loadflags,unsigned int openmode)
{
 pei->entrytype=DFT_UNKNOWN;
 funcbit_disable(pei->infobits,(PEIF_ID3EXIST|PEIF_ID3LOADED));
 chkentry_get_onefileinfos_common(psi,pei,frp,id3loadflags,openmode,NULL);
 if(pei==psi->mvp->aktfilenum)
  playlist_pei0_set(psi->mvp,pei,0);
} // file is open

static unsigned int chkentry_get_onefileinfos_common(struct playlist_side_info *psi,struct playlist_entry_info *pei,struct mpxpframe_s *frp,unsigned int id3loadflags,unsigned int openmode, int *err_code)
{
 unsigned int found=0,exist=0;
 int retcode=MPXPLAY_ERROR_OK;

 if((pei<psi->firstentry) || (pei>psi->lastentry))
  return found;
 if(pei->entrytype&(DFTM_DIR|DFTM_DRIVE))
  return (EDITLIST_MODE_HEAD|EDITLIST_MODE_ID3);
 if(pei->infobits&PEIF_ENABLED)
  exist|=EDITLIST_MODE_HEAD;
 if(pei->infobits&PEIF_ID3EXIST)
  exist|=EDITLIST_MODE_ID3;
 playlist_fulltime_del(psi,pei);
 if((pei->entrytype!=DFT_UNKNOWN) && (pei->infobits&PEIF_ENABLED))// && (!(pei->infobits&PEIF_INDEXED) || (pei->entrytype>=DFT_AUDIOFILE)))
  found|=EDITLIST_MODE_HEAD;
 if((pei->infobits&PEIF_ID3EXIST) || !funcbit_test(id3loadflags,ID3LOADMODE_ALL))
  found|=EDITLIST_MODE_ID3;
 if(found!=(EDITLIST_MODE_HEAD|EDITLIST_MODE_ID3)){
  found|=get_onefileinfos_from_tagset(pei);
  found|=get_onefileinfos_from_id3list(psi,pei,id3loadflags);
  if(found!=(EDITLIST_MODE_HEAD|EDITLIST_MODE_ID3))
   found|=get_onefileinfos_from_previndex(psi,pei,id3loadflags);
  if(found!=(EDITLIST_MODE_HEAD|EDITLIST_MODE_ID3))
   //found|=get_onefileinfos_from_anytab(psi,pei,id3loadflags);
   found|=get_onefileinfos_from_anyside(psi,pei,psi->psio,id3loadflags);
  if(!funcbit_test(id3loadflags,ID3LOADMODE_FILE) || funcbit_test(id3loadflags,ID3LOADMODE_PREFER_LIST))
   playlist_chkentry_create_id3infos_from_filename(psi,pei,id3loadflags);
  if(!(found&EDITLIST_MODE_HEAD) || (openmode&(MPXPLAY_INFILE_OPENMODE_INFO_DECODER|MPXPLAY_INFILE_OPENMODE_LOAD_SEEKTAB)))
   retcode = playlist_chkentry_get_onefileinfos_from_file(psi,pei,frp,id3loadflags,found,openmode);
  if(funcbit_test(id3loadflags,ID3LOADMODE_FILE) && !funcbit_test(id3loadflags,ID3LOADMODE_PREFER_LIST))
   playlist_chkentry_create_id3infos_from_filename(psi,pei,id3loadflags);
  if((pei->infobits&PEIF_ENABLED) && !(exist&EDITLIST_MODE_HEAD)) // ??? we should check deeper the length and id3 changes
   funcbit_enable(psi->editloadtype,PLL_CHG_LEN); // file infos have changed
  if((pei->infobits&PEIF_ID3EXIST) && !(exist&EDITLIST_MODE_ID3))
   funcbit_enable(psi->editloadtype,PLL_CHG_ID3); // id3 infos have changed
 }else if(openmode&(MPXPLAY_INFILE_OPENMODE_INFO_DECODER|MPXPLAY_INFILE_OPENMODE_LOAD_SEEKTAB))
  retcode = playlist_chkentry_get_onefileinfos_from_file(psi,pei,frp,id3loadflags,found,openmode);
 playlist_fulltime_add(psi,pei);
 if(err_code)
  *err_code = retcode;

 return (found&EDITLIST_MODE_HEAD);
}

static unsigned int get_onefileinfos_from_tagset(struct playlist_entry_info *pei_dest)
{
 unsigned int i;

 for(i=0;i<=I3I_MAX;i++)
  if(id3tagset[i])
   if(pei_dest->infobits&PEIF_ALLOCATED)
    playlist_editlist_add_id3_one(NULL,pei_dest,i,id3tagset[i],-1);
   else
    pei_dest->id3info[i]=id3tagset[i];

 if(pei_dest->id3info[I3I_ARTIST] || pei_dest->id3info[I3I_TITLE])
  return EDITLIST_MODE_ID3;

 return 0;
}

static unsigned int get_onefileinfos_from_previndex(struct playlist_side_info *psi,struct playlist_entry_info *pei,unsigned int id3loadflags)
{
 struct playlist_entry_info *pen=NULL;
 unsigned int found=0;
 if(!funcbit_test(id3loadflags,ID3LOADMODE_LIST) || !funcbit_test(id3loadflags,ID3LOADMODE_OTHERENTRY) || !(pei->infobits&PEIF_INDEXED))
  return found;
 while((pen=playlist_search_filename(psi,pei->filename,-1,pen,0))!=NULL){
  if(pen->entrytype>=DFT_AUDIOFILE){
   found|=playlist_editlist_addfile_one(psi,psi,pen,pei,(EDITLIST_MODE_HEAD|EDITLIST_MODE_ID3));
   break;
  }
  pen++;
 }
 return found;
}

/*static unsigned int get_onefileinfos_from_anytab(struct playlist_side_info *psi_dest,struct playlist_entry_info *pei_dest,unsigned int id3loadflags)
{
 struct playlist_side_info *psi_src;
 struct playlist_entry_info *pei_src;
 struct mainvars *mvp;
 unsigned int modify,tab;
 static playlist_side_info *psi_prev_src=NULL; // !!!

 if(!funcbit_test(id3loadflags,ID3LOADMODE_LIST) || !funcbit_test(id3loadflags,ID3LOADMODE_OTHERENTRY))
  return 0;
 modify=0;
 mvp=psi_dest->mvp;
 if(psi_prev_src)
  psi_src=psi_prev_src; // start the search with the last tab
 else
  psi_src=mvp->psi0;
 tab=mvp->editorside_all_tabs;
 do{
  if((psi_src==psi_prev_src) && (tab<mvp->editorside_all_tabs))
   goto skip_tab;
  if( (psi_src==psi_dest) || !(psi_src->editsidetype&PEIF_ENABLED)
   || (psi_src->lastentry<psi_src->firstsong)
   || ((psi_src->lastentry-psi_src->firstsong)>20000) // !!! ??? we don't search on too large tabs
   || (psi_src->chkfilenum_curr && !(psi_src->editsidetype&PLT_EXTENDED)) )
   goto skip_tab;
  pei_src=psi_src->firstsong;
  do{
   if(pds_strcmp(pei_src->filename,pei_dest->filename)==0){
    if((pei_dest->entrytype==DFT_NOTCHECKED || pei_dest->entrytype==DFT_UNKNOWN) && (pei_src->entrytype!=DFT_UNKNOWN) && (pei_src->entrytype!=DFT_NOTCHECKED || (id3loadflags&ID3LOADMODE_FASTSEARCH)) && (pei_src->infobits&PEIF_ENABLED) && !(pei_src->infobits&PEIF_INDEXED))
     modify|=EDITLIST_MODE_HEAD;
    if(pei_src->infobits&PEIF_ID3EXIST)
     if( (!(pei_dest->infobits&PEIF_ID3EXIST) || ((pei_src->infobits&PEIF_ID3MASK) > (pei_dest->infobits&PEIF_ID3MASK)) ) && !(pei_src->infobits&PEIF_INDEXED))
      modify|=EDITLIST_MODE_ID3;
    if(modify){
     modify=playlist_editlist_addfile_one(psi_src,NULL,pei_src,pei_dest,modify);
     psi_prev_src=psi_src;
     return modify;
    }
    break;
   }
  }while(++pei_src<=psi_src->lastentry);
skip_tab:
  if((psi_src==psi_prev_src) && (tab==mvp->editorside_all_tabs))
   psi_src=mvp->psi0; // rewind (start again the search from the 0. tab)
  else
   psi_src++;
 }while(--tab);
 return modify;
}*/

static unsigned int get_onefileinfos_from_anyside(struct playlist_side_info *psi_dest,struct playlist_entry_info *pei_dest,struct playlist_side_info *psi_src,unsigned int id3loadflags)
{
 struct playlist_entry_info *pei_src;
 unsigned int modify;

 if( !funcbit_test(id3loadflags,ID3LOADMODE_LIST) || !funcbit_test(id3loadflags,ID3LOADMODE_OTHERENTRY) || !funcbit_test(psi_src->editsidetype,PLT_ENABLED)
  || (psi_src->lastentry<psi_src->firstsong)
  || ((psi_src->lastentry-psi_src->firstsong)>20000) // !!! ??? we don't search on too large tabs
  || (psi_src->chkfilenum_curr && !(psi_src->editsidetype&PLT_EXTENDED)) )
  return 0;
 modify=0;
 for(pei_src=psi_src->firstsong;pei_src<=psi_src->lastentry;pei_src++){
  if((pei_src!=pei_dest) && (pds_strcmp(pei_src->filename,pei_dest->filename)==0)){
   if((pei_dest->entrytype==DFT_NOTCHECKED || pei_dest->entrytype==DFT_UNKNOWN) && (pei_src->entrytype!=DFT_UNKNOWN) && (pei_src->entrytype!=DFT_NOTCHECKED || (id3loadflags&ID3LOADMODE_FASTSEARCH)) && (pei_src->infobits&PEIF_ENABLED) && !(pei_src->infobits&PEIF_INDEXED))
    modify|=EDITLIST_MODE_HEAD;
   if(pei_src->infobits&PEIF_ID3EXIST)
    if( (!(pei_dest->infobits&PEIF_ID3EXIST) || ((pei_src->infobits&PEIF_ID3MASK) > (pei_dest->infobits&PEIF_ID3MASK)) ) && !(pei_src->infobits&PEIF_INDEXED))
     modify|=EDITLIST_MODE_ID3;
   if(modify)
    modify=playlist_editlist_addfile_one(psi_src,NULL,pei_src,pei_dest,modify);
   break;
  }
 }
 return modify;
}

int playlist_chkentry_get_onefileinfos_from_file(struct playlist_side_info *psi,struct playlist_entry_info *pei,struct mpxpframe_s *frp,unsigned int id3loadflags,unsigned int found,unsigned int openmode)
{
 unsigned int i;
 struct mpxplay_infile_info_s *miis = frp->infile_infos;
 struct mpxplay_audio_decoder_info_s *adi;
 int retcode;
 char strtmp[256];

 if(!pei->mdds)
  pei->mdds=playlist_loaddir_filename_to_drivehandler(pei->filename);

 // is file playlist?
 if(playlist_loadlist_get_header_by_ext(psi,pei,pei->filename))
  return MPXPLAY_ERROR_INFILE_CANTOPEN;

 // is file ftp or http? (slow device)
 if(!(id3loadflags & ID3LOADMODE_SLOWACCESS) && (id3loadflags & ID3LOADMODE_FASTSEARCH)){
  struct mpxplay_drivehand_func_s *mdfs = (pei->mdds && pei->mdds->mdfs)?  pei->mdds->mdfs : mpxplay_diskdrive_search_driver(pei->filename);
  if(mdfs && (mdfs->infobits & MPXPLAY_DRIVEHANDFUNC_INFOBIT_SLOWACCESS))
   return MPXPLAY_ERROR_INFILE_CANTOPEN;
 }

 // is file audio?
 if((id3loadflags&ID3LOADMODE_FILE) && (!(found&EDITLIST_MODE_ID3) || !(pei->infobits&PEIF_ID3LOADED)))
  funcbit_enable(openmode,MPXPLAY_INFILE_OPENMODE_INFO_ID3);

 if(GET_HFT(pei->entrytype)&HFT_STREAM){
  funcbit_disable(openmode,MPXPLAY_INFILE_OPENMODE_MASK_NONSTREAM);
  miis->timemsec = PEI_TIMEMSEC_STREAM;
 }

 pei->entrytype=DFT_UNKNOWN;
 funcbit_disable(pei->infobits,PEIF_ENABLED);
 frp->mdds=pei->mdds;
 frp->psi=psi;
 frp->pei=pei;

 retcode = mpxplay_infile_open(frp,pei->filename,openmode);
 mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"mpxplay_infile_open om:%8.8X ret:%d fn:%s", openmode, retcode, pei->filename);
 if(retcode != MPXPLAY_ERROR_OK){
  if(MPXPLAY_ERROR_IS_DISKDRIV(retcode)){
   mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"playlist_chkentry_get_onefileinfos_from_file 1. ret:%d err:%d", retcode, frp->last_errorcode);
   return retcode;
  }
  if(!frp->infile_funcs){
   mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"mpxplay_infile_get_header_by_ext");
   retcode = mpxplay_infile_get_header_by_ext(frp,pei->mdds, pei->filename, openmode);
   if(retcode != MPXPLAY_ERROR_OK){
    mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"playlist_chkentry_get_onefileinfos_from_file 2. ret:%d err:%d", retcode, frp->last_errorcode);
    return retcode;
   }
  }
 }

 miis=frp->infile_infos;
 adi=miis->audio_decoder_infos;

 if(frp->filetype&HFT_STREAM){
  funcbit_disable(id3loadflags,ID3LOADMODE_FILE);
  if(adi->bitrate)
   miis->timemsec = 0;
  else
   miis->timemsec = PEI_TIMEMSEC_STREAM;
  miis->allframes = 0;
  miis_to_frp(miis,frp);
 }

 if(frp->filetype && frp->infile_funcs && miis->timemsec){
  if((pei->filesize!=frp->filesize) || (pei->timemsec!=miis->timemsec))
   funcbit_enable(psi->editloadtype,PLL_CHG_LEN);
  pei->filesize=frp->filesize;
  pei->timemsec=miis->timemsec;
  pei->mdds=frp->mdds;
  pei->infile_funcs=frp->infile_funcs;
  funcbit_enable(pei->infobits,PEIF_ENABLED);
  pei->entrytype=MPXPLAY_HFT_PUT(frp->filetype);
 }else{
  mpxplay_infile_close(frp);
  return MPXPLAY_ERROR_INFILE_FILETYPE;
 }

 if(frp->filetype&HFT_STREAM){
  if(!(id3loadflags & ID3LOADMODE_LOWLEVEL))
   return MPXPLAY_ERROR_OK;
  if(!pei->id3info[I3I_ARTIST] || !(id3loadflags & ID3LOADMODE_PREFER_LIST)){
   i = sizeof(strtmp);
   strtmp[0] = 0;
   mpxplay_diskdrive_file_config(frp->filehand_datas,MPXPLAY_DISKFILE_CFGFUNCNUM_GET_CONTENT_ARTIST,&strtmp[0],&i);
   if(strtmp[0] && (pds_strcmp(pei->id3info[I3I_ARTIST], strtmp) != 0)){
    int len = pds_str_limitc(strtmp, strtmp, MAX_ID3_DISKFILE_ARTIST_LEN, ' '); // !!!
    if(playlist_editlist_add_id3_one(psi, pei, I3I_ARTIST, strtmp, len) > 0){
     funcbit_enable(pei->infobits,(PEIF_ID3EXIST|PEIF_ID3LOADED));
     funcbit_enable(psi->editloadtype, PLL_CHG_ID3);
    }
   }
  }
  if(!pei->id3info[I3I_GENRE] || !(id3loadflags & ID3LOADMODE_PREFER_LIST)){
   i = sizeof(strtmp);
   strtmp[0] = 0;
   mpxplay_diskdrive_file_config(frp->filehand_datas,MPXPLAY_DISKFILE_CFGFUNCNUM_GET_CONTENT_GENRE,&strtmp[0],&i);
   if(strtmp[0] && (pds_strcmp(pei->id3info[I3I_GENRE], strtmp) != 0))
    if(playlist_editlist_add_id3_one(psi, pei, I3I_GENRE, strtmp, -1) > 0){
     //funcbit_enable(psi->editloadtype, PLL_CHG_ID3); // not really needed yet
    }
  }
 }else
  playlist_chkentry_load_id3tag_from_file(psi,pei,frp,id3loadflags,found);

 return MPXPLAY_ERROR_OK;
}

//we have to run get_headerinfo (to open infile) before get_id3tag (it's done above)
void playlist_chkentry_load_id3tag_from_file(struct playlist_side_info *psi,struct playlist_entry_info *pei,struct mpxpframe_s *frp,unsigned int id3loadflags,unsigned int found)
{
 unsigned int i;
 struct playlist_entry_info pei_tmp;

 if((id3loadflags&ID3LOADMODE_FILE) && (!(found&EDITLIST_MODE_ID3) || !(pei->infobits&PEIF_ID3LOADED))){
  pds_memcpy(&pei_tmp,pei,sizeof(struct playlist_entry_info));
  pei_tmp.filename=NULL;
  pds_memset(&pei_tmp.id3info[0],0,sizeof(pei_tmp.id3info));
  funcbit_enable(pei_tmp.infobits,PEIF_ALLOCATED);
  playlist_editlist_add_filename(NULL,&pei_tmp,pei->filename);
  frp->pei=&pei_tmp;

  mpxplay_infile_get_id3tag(frp); // load tag infos from audio file

  for(i=0;i<=I3I_MAX;i++)
   if(pei_tmp.id3info[i]){
    if(!pei->id3info[i] || (!(id3loadflags&ID3LOADMODE_PREFER_LIST) && !(pei->infobits&PEIF_INDEXED))){
     if(pds_strcmp(pei->id3info[i],pei_tmp.id3info[i]) != 0){
      if(playlist_editlist_add_id3_one(psi,pei,i,pei_tmp.id3info[i],-1)>0){
       funcbit_enable(pei->infobits,(PEIF_ID3EXIST|PEIF_ID3LOADED));
       funcbit_enable(psi->editloadtype, PLL_CHG_ID3);
      }
     }
    }
   }

  playlist_editlist_del_filename(NULL,&pei_tmp);
  playlist_editlist_del_id3_all(NULL,&pei_tmp);

  frp->pei=pei;
 }
}

//--------------------------------------------------------------------------
static void ciff_add_id3info(struct playlist_side_info *psi,struct playlist_entry_info *pei,unsigned int i3i,char *str)
{
 if(!pei->id3info[i3i] && str && str[0]){
  char *sp=str;
#ifndef MPXPLAY_UTF8
  char strtmp[256];
#endif
  do{
   if(sp[0]=='_')  // convert all underlines to spaces
    sp[0]=' ';
   sp++;
  }while(sp[0]);

  pds_strcutspc(str);

  if(!str[0])
   return;
#ifndef MPXPLAY_UTF8
  if(!(id3textconv&ID3TEXTCONV_FILENAME)){
   strtmp[0]=0;
   mpxplay_playlist_textconv_by_texttypes(
       MPXPLAY_TEXTCONV_TYPES_PUT(MPXPLAY_TEXTCONV_TYPE_MPXNATIVE,MPXPLAY_TEXTCONV_TYPE_MPXPLAY),
       str,-1,strtmp,sizeof(strtmp)-1);
   str=&strtmp[0];
  }
#endif
  playlist_editlist_add_id3_one(psi,pei,i3i,str,-1);
  funcbit_enable(pei->infobits,PEIF_ID3EXIST);
 }
}

static long ciff_add_tracknum(struct playlist_side_info *psi,struct playlist_entry_info *pei,char *str)
{
 long tracknum;
 char strtmp[64];
 while(str[0]==' ') // skip spaces
  str++;
 if(!str[0])
  return 0;
 tracknum=pds_atol(str);
 if(tracknum<=0 || (tracknum<10 && str[0]!='0')) // 01-09,10-NNN
  return 0;
 if(!pei->id3info[I3I_TRACKNUM]){
  int len=snprintf(strtmp,sizeof(strtmp),"%d",tracknum);
  if(len>0)
   playlist_editlist_add_id3_one(psi,pei,I3I_TRACKNUM,strtmp,len);
 }
 return tracknum;
}

// recommended/handled formats are:
//  Artist - Album (year) \ NN. Title.mp3
//  Album (year) \ NN. Artist - Title.mp3
//  Album (year) \ Artist - NN - Title.mp3
void playlist_chkentry_create_id3infos_from_filename(struct playlist_side_info *psi,struct playlist_entry_info *pei,unsigned int id3loadflags)
{
 char *fn,*a,*t,*album,*t2,*e;
 long tracknum=0,endtrnum=0,len;
 unsigned int original_title=0,original_artist=0,long_tr_selector=0,long_at_selector=0;
 char filename[MAX_PATHNAMELEN],updirname[MAX_PATHNAMELEN];

 if(!(id3loadflags&ID3LOADMODE_CLFN))
  return;
#ifndef MPXPLAY_WIN32
 if(!is_lfn_support || !(uselfn&USELFN_ENABLED))
  return;
#endif
 if((pei->entrytype>=DFTM_DFT) && (pei->entrytype<DFT_AUDIOFILE))
  return;
 if((pei->id3info[I3I_ARTIST] && pei->id3info[I3I_TITLE]) && pei->id3info[I3I_ALBUM] && pei->id3info[I3I_TRACKNUM] && (pds_atol(pei->id3info[I3I_TRACKNUM])>0))
  return;

 pds_getfilename_noext_from_fullname(filename, pei->filename);
 if(!filename[0])
  return;
 fn=&filename[0];

 // get tracknum from the end or the begin of filename
 if(pds_strlicmp(fn,"track")==0){  // trackNN
  tracknum=ciff_add_tracknum(psi,pei,&fn[sizeof("track")-1]);
  if(tracknum>0)
   endtrnum=1;
 }
 if(tracknum<=0)                  // NN artist-title
  tracknum=ciff_add_tracknum(psi,pei,fn);
 if(tracknum<=0){                 // ??? search for "anythingNN" format
  len=pds_strlen(fn);
  if((len>=3) && isdigit(fn[len-1]) && isdigit(fn[len-2]) && !isdigit(fn[len-3]) && (fn[len-3]!='\'')){
   tracknum=ciff_add_tracknum(psi,pei,&fn[len-2]);
   if(tracknum>0)
    endtrnum=1;
  }
 }

 if(pei->id3info[I3I_ARTIST] && pei->id3info[I3I_TITLE] && pei->id3info[I3I_ALBUM])
  return;
 if(pei->id3info[I3I_ARTIST])
  original_artist=1;
 if(pei->id3info[I3I_TITLE])
  original_title=1;

 a=fn;   // skip tracknum (at begin of filename)
 t=fn;
 do{
  if(t[0]=='_')  // convert all underlines to spaces
   t[0]=' ';
  t++;
 }while(t[0]);

 if((tracknum>0) && !endtrnum){
  while((a[0]>='0' && a[0]<='9') || a[0]==' ') // skip numbers (tracknum) and spaces
   a++;
  if(pds_strncmp((a-1)," - ",3)==0) // sign " - " selector
   long_tr_selector=1;
  while(a[0]=='.'){ // skip dots
   long_tr_selector=1; // hack
   a++;
  }
 }
 while(a[0]==' ' || a[0]=='-' || a[0]=='_')  // skip spaces,'-','_'
  a++;
 if(!a[0])       // no more data (end of filename)
  return;

 fn=a;           // possible artist begins here

 if(pds_strlicmp(a,"track")==0){ // possibly no real artist-title
  t=fn;
  goto skip_artist_title;
 }

 t=pds_strstr(fn," - ");      // search begin of title
 if(t){
  e=t;  // end of artist
  t+=3; // begin of title
  long_at_selector=1;
 }else{
  t=pds_strchr(fn,'-');
  if(t){
   e=t;
   t++;
  }
 }

 if(t){
  a=fn; // possible artist
  if(tracknum<=0){
   t2=pds_strchr(t,'-');         // check "artist - NN - title" format
   if(t2){
    tracknum=ciff_add_tracknum(psi,pei,t);
    if(tracknum>0)
     t=t2+1;      // title
   }
  }else
   t2=NULL;

  while(t[0] && t[0]==' ') // title: cut spaces from begin
   t++;

  if(t[0] || (t2 && tracknum))
   if(
    ((long_at_selector || (!long_tr_selector && !long_at_selector))  // FIXME: parenthesises
    &&
    (
     ((a[0]>='A') && (a[0]<='Z') && (t[0]>='A') && (t[0]<='Z')) // case of begin chars are the same
     || ((a[0]>='a') && (a[0]<='z') && (t[0]>='a') && (t[0]<='z')) //
     || (!((a[0]>='A') && (a[0]<='Z')) && !((a[0]>='a') && (a[0]<='z'))) // non US chars in artist or title
     || (!((t[0]>='A') && (t[0]<='Z')) && !((t[0]>='a') && (t[0]<='z')))
    ))
    || (t2 && tracknum)  // artist-NN-title
    //|| !t2 ){            // only one " - "
   ){
    *e=0;
    ciff_add_id3info(psi,pei,I3I_ARTIST,a);
   }else
    t=a;
 }else
  t=fn;             // title only

skip_artist_title:

 if(((tracknum>0) && !endtrnum) || (pei->id3info[I3I_ARTIST] && !original_artist)) // "NN.title.MP3" or "artist - title.MP3"
  ciff_add_id3info(psi,pei,I3I_TITLE,t);

 if(!pei->id3info[I3I_ARTIST] || !pei->id3info[I3I_ALBUM]){   // get artist - album from updir name
  pds_strcpy(updirname,pei->filename);
  e=pds_strrchr(updirname,PDS_DIRECTORY_SEPARATOR_CHAR);
  if(e){
   *e=0;
   a=pds_strrchr(updirname,PDS_DIRECTORY_SEPARATOR_CHAR); // artist
   if(a){
    a++;
    album=pds_strstr(a," - ");     // album
    if(album){
     *album=0;
     album+=3;
    }else{
     album=pds_strchr(a,'-');      //
     if(album)
      *album++=0;
    }
    if(album && album[0]){ // artist-album\NN-title.mp3
     ciff_add_id3info(psi,pei,I3I_ARTIST,a);
     ciff_add_id3info(psi,pei,I3I_TITLE,t);
    }else if(!pei->id3info[I3I_ARTIST] && (tracknum>0) && !endtrnum){ // artist\NN.title.mp3
     if(pei->id3info[I3I_TITLE] && !original_title && ((e-a)>=2))
      ciff_add_id3info(psi,pei,I3I_ARTIST,a);
    }
    if(album && album[0]){
     char *year,*yb;
     yb=year=pds_strchr(album,'(');
     if(year){
      int val;
      year++;
      if(year[0]=='\''){ // ie: '99
       year++;
       val=pds_atol(year);
       if(val<30)        // !!! will not good after 2029
        val+=2000;
       else
        val+=1900;
      }else
       val=pds_atol(year);
      e=pds_strchr(year,')');
      if(val>=1200 && val<=9999 && e){
       char ytmp[8];
       *yb=0;*e=0;
       snprintf(ytmp,sizeof(ytmp),"%4d",val);
       ciff_add_id3info(psi,pei,I3I_YEAR,ytmp);
      }
     }
     ciff_add_id3info(psi,pei,I3I_ALBUM,album);
    }
   }
  }
 }
 if((pei->id3info[I3I_ARTIST] && pei->id3info[I3I_TITLE]) && !original_artist && !original_title)
  funcbit_enable(pei->infobits, (PEIF_ID3EXIST|PEIF_ID3FILENAME));
}

//-------------------------------------------------------------------------
//static unsigned long starttime;

void playlist_chkfile_start_norm(struct playlist_side_info *psi,struct playlist_entry_info *startsong)
{
 //starttime=pds_gettimeh();
 if(psi->editsidetype&PLT_ENABLED){
  struct mainvars *mvp=psi->mvp;
  if(psi==mvp->psip){
   mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"Chkfile t:%d \"%s\" a:%d", psi->tabnum, mvp->pei0->filename, ((mvp->aktfilenum)? (mvp->aktfilenum - psi->firstentry) : -1));
  }
  if((psi==mvp->psip) && !(psi->editloadtype&PLL_JUKEBOX) && ((mvp->aktfilenum<psi->firstsong) || (mvp->aktfilenum>psi->lastentry))){
   struct playlist_entry_info *pei;
   struct mpxpframe_s *frp=mvp->fr_primary;
   long timempos=(frp->allframes)? ((long)((float)frp->infile_infos->timemsec*(float)frp->frameNum/(float)frp->allframes)):-1;

   pei=playlist_search_filename(psi,mvp->pei0->filename,timempos,NULL,0);
   mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"Search t:%d e:%d \"%s\"", psi->tabnum, (pei? (pei-psi->firstentry+1) : -1), mvp->pei0->filename);
   if(pei && (psi==mvp->psip) && ((mvp->aktfilenum < psi->firstentry) || (mvp->aktfilenum > psi->lastentry)) && (pds_stricmp(mvp->pei0->filename,pei->filename)==0)){
    mvp->aktfilenum=pei;
    playlist_editlist_addfile_one(NULL,psi,mvp->pei0,pei,(EDITLIST_MODE_HEAD|EDITLIST_MODE_ID3));
/*   if(pei){
    mvp->aktfilenum=pei;
    if(pei->infobits&PEIF_INDEXED)
     playlist_chkentry_get_onefileinfos_allagain(psi,pei,mvp->fr_check,loadid3tag,MPXPLAY_INFILE_OPENMODE_CHECK); // to get all id3 infos (not only artist/title/album from cue)
    else
     playlist_chkentry_get_onefileinfos_check(psi,pei);
    playlist_pei0_set(mvp,pei,(EDITLIST_MODE_ID3|EDITLIST_MODE_INDEX));*/
    playlist_randlist_pushq(psi,pei);
    mpxplay_calculate_index_start_end(frp,mvp,pei); // switch between cue and other playlist
    if((psi==mvp->psie) && (psi->editorhighline>=psi->firstentry) && (psi->editorhighline<=psi->lastentry) && !psi->editorhighline->entrytype)
     playlist_loadlist_get_header_by_ext(psi,psi->editorhighline,psi->editorhighline->filename);
    if((psi!=mvp->psie) || (psi->editorhighline<psi->firstentry) || (GET_HFT(psi->editorhighline->entrytype)!=HFT_DFT))
     playlist_editorhighline_set(psi,pei);
   }
  }
  if(((desktopmode&DTM_MASK_COMMANDER)==DTM_MASK_COMMANDER) || (psi->id3ordernum_primary==ID3ORDER_FILESIZE) || (psi->id3ordernum_primary==ID3ORDER_FILEDATE)) // !!!
   funcbit_enable(psi->editsidetype,PLT_EXTENDINC);        // better solution?
  if(playlist_sortlist_is_preordered_type(psi) && !psi->chkfilenum_curr){
   playlist_order_side(psi);
   playlist_fulltime_getside(psi);
  }
  if(!(psi->editsidetype&PLT_EXTENDED) || (psi->editsidetype&PLT_EXTENDINC) || psi->chkfilenum_curr){
   switch(preloadinfo){
    case PLI_PLAYLOAD:
      if(startsong<psi->firstsong)
       startsong=psi->firstsong;
      if(!psi->chkfilenum_curr || startsong<psi->chkfilenum_curr)
       psi->chkfilenum_curr=startsong;
      if(!psi->chkfilenum_begin || (psi->chkfilenum_begin>psi->chkfilenum_curr))
       psi->chkfilenum_begin=psi->chkfilenum_curr;
      psi->chkfilenum_end=psi->lastentry;
      mpxplay_timer_addfunc(&get_fileinfos_under_play,mvp,MPXPLAY_TIMERTYPE_REPEAT|MPXPLAY_TIMERFLAG_INDOS|MPXPLAY_TIMERFLAG_LOWPRIOR,0);
      break;
    case PLI_PRELOAD :
    case PLI_DISPLOAD:
      display_editorside_reset(psi);
      break;
   }
  }
  funcbit_disable(psi->editsidetype,(PLT_EXTENDED|PLT_EXTENDINC));
  if(psi==mvp->psip)
   refdisp|=RDT_EDITOR|RDT_BROWSER;
  else
   refdisp|=RDT_EDITOR;
 }
}

void playlist_chkfile_start_disp(struct playlist_side_info *psi,struct playlist_entry_info *startsong,struct playlist_entry_info *endsong)
{
 if((preloadinfo==PLI_DISPLOAD) && (psi->editsidetype&PLT_ENABLED)){
  playlist_chkfile_stop(psi);
  if(startsong<psi->firstsong)
   startsong=psi->firstsong;
  psi->chkfilenum_begin=psi->chkfilenum_curr=startsong;
  if(!endsong || (endsong>psi->lastentry))
   endsong=psi->lastentry;
  psi->chkfilenum_end=endsong;
  mpxplay_timer_addfunc(&get_fileinfos_under_play,psi->mvp,MPXPLAY_TIMERTYPE_REPEAT|MPXPLAY_TIMERFLAG_INDOS|MPXPLAY_TIMERFLAG_LOWPRIOR,0);
 }
}

void playlist_chkfile_start_ehline(struct playlist_side_info *psi,struct playlist_entry_info *pei)
{
 if((preloadinfo==PLI_EHLINELOAD) && (psi->editsidetype&PLT_ENABLED)){
  if(pei>=psi->firstsong && pei<=psi->lastentry){
   struct mainvars *mvp=psi->mvp;
   if(((mvp->aui->card_infobits&AUINFOS_CARDINFOBIT_DMAFULL) && (mvp->fr_primary->buffertype&PREBUFTYPE_FILLED)) || !(playcontrol&PLAYC_RUNNING)){
    playlist_chkentry_get_onefileinfos_check(psi,pei);
    playlist_order_entry(psi,pei);
    if(pei->entrytype==DFT_NOTCHECKED)
     playlist_chkentry_get_onefileinfos_check(psi,pei);
   }else
    refdisp|=RDT_EDITOR;
  }
 }
}

void playlist_chkfile_stop(struct playlist_side_info *psi)
{
 //char sout[100];
 if((preloadinfo==PLI_DISPLOAD) && psi->chkfilenum_begin)
  playlist_order_block(psi,psi->chkfilenum_begin,(min(psi->chkfilenum_curr,psi->chkfilenum_end)));
 psi->chkfilenum_begin=NULL;
 psi->chkfilenum_curr=NULL;
 psi->chkfilenum_end=NULL;
 if(mpxplay_playlistcontrol&MPXPLAY_PLAYLISTC_ID3LIST_LOCAL){ // ??? other cases?
  struct mainvars *mvp=psi->mvp;
  struct playlist_entry_info *pei=mvp->aktfilenum;
  if((psi==mvp->psip) && (pei>=psi->firstsong) && (pei<=psi->lastentry))
   playlist_pei0_set(mvp,pei,EDITLIST_MODE_ID3);
 }
 if(!(playcontrol&PLAYC_RUNNING))
  clear_message();
 //sprintf(sout,"chktime:%4d",pds_gettimeh()-starttime);
 //display_timed_message(sout);
}

//------------------------------------------------------------------------
unsigned long playlist_entry_get_timemsec(struct playlist_entry_info *pei)
{
 unsigned long timemsec;

 if(pei->infobits&PEIF_INDEXED){
  timemsec=(pei->petime)? pei->petime:pei->timemsec;
  if(pei->pstime<=timemsec)
   timemsec-=pei->pstime;
 }else
  timemsec=pei->timemsec;

 return timemsec;
}

void playlist_fulltime_add(struct playlist_side_info *psi,struct playlist_entry_info *pei)
{
 if(pei->infobits&PEIF_ENABLED){
  if(!(pei->infobits&PEIF_FULLTIMEADDED)){
   struct mainvars *mvp;
   unsigned long timesec=(playlist_entry_get_timemsec(pei)+500)/1000;

   psi->fulltimesec+=timesec;
   funcbit_enable(pei->infobits,PEIF_FULLTIMEADDED);

   mvp=psi->mvp;
   if(!playrand && (psi==mvp->psip) && ((pei<mvp->aktfilenum) || (mvp->aktfilenum<psi->firstsong) || (mvp->aktfilenum > psi->lastentry)))
    fullelapstime+=timesec;
  }
 }
}

void playlist_fulltime_del(struct playlist_side_info *psi,struct playlist_entry_info *pei)
{
 unsigned long timesec=(playlist_entry_get_timemsec(pei)+500)/1000;

 if(pei->infobits&PEIF_FULLTIMEADDED){
  psi->fulltimesec-=timesec;
  funcbit_disable(pei->infobits,PEIF_FULLTIMEADDED);

  if(playrand){
   if(pei->infobits&PEIF_RNDPLAYED)
    fullelapstime-=timesec;
  }else{
   struct mainvars *mvp=psi->mvp;
   if((psi==mvp->psip) && ((pei<mvp->aktfilenum) || (mvp->aktfilenum<psi->firstsong) || (mvp->aktfilenum > psi->lastentry)))
    fullelapstime-=timesec;
  }
 }
}

void playlist_fulltime_clearside(struct playlist_side_info *psi)
{
 psi->fulltimesec=0;
 if(psi==psi->mvp->psip)
  fullelapstime=0;
}

void playlist_fulltime_getside(struct playlist_side_info *psi)
{
 struct playlist_entry_info *pei;
 mpxp_int64_t loc_fulltime=0;

 for(pei=psi->firstsong;pei<=psi->lastentry;pei++){
  if(pei->infobits&PEIF_ENABLED){
   loc_fulltime+=playlist_entry_get_timemsec(pei);
   funcbit_enable(pei->infobits,PEIF_FULLTIMEADDED);
  }
 }
 psi->fulltimesec=(loc_fulltime+500)/1000;
}

unsigned int playlist_fulltime_getelapsed(struct mainvars *mvp,unsigned int cleartime)
{
 struct playlist_side_info *psip;
 struct playlist_entry_info *pei;
 struct playlist_entry_info *end;

 if(cleartime)
  fullelapstime=0;

 if(timemode>=2 && !fullelapstime){
  psip=mvp->psip;
  if(psip->chkfilenum_curr)
   end=psip->chkfilenum_curr;
  else
   end=psip->lastentry;
  if(end>=psip->firstsong){
   mpxp_int64_t loc_fullelapstime=0;
   if(playrand){
    for(pei=psip->firstsong;pei<=end;pei++)
     if((pei->infobits&PEIF_RNDPLAYED) && (pei->infobits&PEIF_ENABLED))
      loc_fullelapstime+=playlist_entry_get_timemsec(pei);
    if((mvp->aktfilenum>=psip->firstsong) && (mvp->aktfilenum<=psip->lastentry))
     loc_fullelapstime-=playlist_entry_get_timemsec(mvp->aktfilenum);
   }else{
    if(end>=mvp->aktfilenum)
     end=mvp->aktfilenum-1;
    for(pei=psip->firstsong;pei<=end;pei++)
     if(pei->infobits&PEIF_ENABLED)
      loc_fullelapstime+=playlist_entry_get_timemsec(pei);
   }
   fullelapstime=(loc_fullelapstime+500)/1000;
  }else{
   fullelapstime=0;
  }
 }
 return fullelapstime;
}

void playlist_chkentry_enable_entries(struct playlist_side_info *psi)
{
 struct playlist_entry_info *pei;
 for(pei=psi->firstsong;pei<=psi->lastentry;pei++)
  if(pei->entrytype==DFT_UNKNOWN)
   pei->entrytype=DFT_NOTCHECKED;
}
