//**************************************************************************
//*                     This file is part of the                           *
//*                      Mpxplay - audio player.                           *
//*                  The source code of Mpxplay is                         *
//*        (C) copyright 1998-2006 by PDSoft (Attila Padar)                *
//*                    http://mpxplay.cjb.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:m3u,pls,fpl,mxu loading

#include <string.h>
#include "playlist.h"
#include "newfunc\newfunc.h"
#include "control\control.h"
#include "display\display.h"

typedef struct listhandler_s{
 char *extension;
 struct playlist_entry_info *(*loadlist)(struct playlist_entry_info *,struct playlist_side_info *,char *listname);
}listhandler_s;

static struct playlist_entry_info *open_load_m3u(struct playlist_entry_info *,struct playlist_side_info *,char *listname);
static struct playlist_entry_info *load_m3u(struct playlist_entry_info *,struct playlist_side_info *,FILE *listfile);
static struct playlist_entry_info *open_load_pls(struct playlist_entry_info *,struct playlist_side_info *,char *listname);
static struct playlist_entry_info *open_load_mxu(struct playlist_entry_info *,struct playlist_side_info *,char *listname);
static struct playlist_entry_info *open_load_fpl(struct playlist_entry_info *,struct playlist_side_info *,char *listname);
static struct playlist_entry_info *open_load_cue(struct playlist_entry_info *,struct playlist_side_info *,char *listname);

static struct listhandler_s listhandlers[]={
 {"m3u", &open_load_m3u },
 {"m3u8",&open_load_m3u },
 {"pls", &open_load_pls },
 {"mxu", &open_load_mxu },
 {"fpl", &open_load_fpl },
 {"cue", &open_load_cue },
 {NULL,NULL}
};

extern unsigned int loadid3tag,id3textconv,playlistsave;

static struct listhandler_s *select_listhandler_by_ext(char *filename)
{
 struct listhandler_s *lih;
 char *ext=pds_strrchr(filename,'.');
 if(ext){
  ext++;
  lih=&listhandlers[0];
  do{
   if(pds_stricmp(lih->extension,ext)==0)
    return lih;
   lih++;
  }while(lih->extension);
 }
 return NULL;
}

unsigned int playlist_loadlist_check_extension(char *filename)
{
 if(select_listhandler_by_ext(filename))
  return 1;
 return 0;
}

unsigned int playlist_loadlist_mainload(struct playlist_side_info *psi,char *listname,unsigned int loadtype)
{
 struct listhandler_s *listhand;
 unsigned int savedrive;
 int drive;
 struct playlist_entry_info *pei,*begine;
 char *path,*shortfname,strtmp[MAX_PATHNAMELEN],savedir[MAX_PATHNAMELEN];

 pei=begine=psi->lastentry+1;

 if(loadtype&PLL_STDIN){
  pei=load_m3u(pei,psi,stdin);
  psi->lastentry=pei-1;
 }else{

  pds_dir_save(&savedrive,savedir);

  if(listname[1]==':'){  // set drive
   drive=pds_getdrivenum_from_path(listname);
   if(drive>=0)
    pds_setdrive(drive);
   path=&listname[2];
  }else
   path=&listname[0];

  pds_strcpy(strtmp,path); // set subdir
  shortfname=pds_strrchr(strtmp,PDS_DIRECTORY_SEPARATOR_CHAR);
  if(shortfname>(&strtmp[0])){
   *shortfname++=0;
   if(pds_chdir(strtmp)!=0){   // (d:)\subdir\list.m3u
    pds_dir_restore(savedrive,savedir);
    goto plm_end;
   }
  }else{
   shortfname=&strtmp[0];
   if(shortfname[0]==PDS_DIRECTORY_SEPARATOR_CHAR){
    shortfname++;
    if(pds_chdir(PDS_DIRECTORY_SEPARATOR_STR)!=0){    // (d:)\list.m3u
     pds_dir_restore(savedrive,savedir);
     goto plm_end;
    }
   }
  }
  listhand=select_listhandler_by_ext(listname);
  if(listhand)
   pei=listhand->loadlist(pei,psi,shortfname);
  psi->lastentry=pei-1;
  pds_dir_restore(savedrive,savedir);
 }

plm_end:

 if(pei>psi->firstentry)
  playlist_enable_side(psi);
 else
  playlist_disable_side_full(psi);

 if(pei==begine)
  return 0;
 return 1;
}

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

static struct playlist_entry_info *open_load_m3u(struct playlist_entry_info *pei,struct playlist_side_info *psi,char *listname)
{
 FILE *listfile;

 if((listfile=pds_fopen(listname,"rt"))!=NULL){
  pei=load_m3u(pei,psi,listfile);
  fclose(listfile);
 }
 return pei;
}

static struct playlist_entry_info *load_m3u(struct playlist_entry_info *pei,struct playlist_side_info *psi,FILE *listfile)
{
 char *ps,*pa,*pt; // string pointer of timesec,artist,title
 int timesec;
 unsigned int len,firstline=1,this_is_m3u8=0,linecount=0;
 char strtmp[360];

 funcbit_disable(psi->editsidetype,PLT_EXTENDED);

 while(fgets(strtmp,350,listfile) && (pei<=psi->endentry) && (psi->filenameslastp<psi->filenamesendp)){
  if(strtmp[0]){
   if(firstline){
    if(strtmp[0]==0xef && strtmp[1]==0xbb && strtmp[2]==0xbf){
     pds_strcpy(strtmp,&strtmp[3]);
     this_is_m3u8=1;
    }
    firstline=0;
   }
   if(strtmp[0]!='#'){
    char *s;
    if(pds_look_extgetch()==KEY_ESC)
     break;
    s=pds_strchr(strtmp,'\n');
    if(s)
     s[0]=0;
    if(!s || (s>&strtmp[0])){
     pei->filename=psi->filenameslastp;
     pds_fullpath(pei->filename,strtmp);
     len=pds_strlen(pei->filename);
     if(this_is_m3u8 && (id3textconv&ID3TEXTCONV_UTF_AUTO))
      len=mpxplay_playlist_textconv_funcs.utf8_to_char(pei->filename,len);
     if(id3textconv&ID3TEXTCONV_FILENAME)
      len=mpxplay_playlist_textconv_do(pei->filename,len,~ID3TEXTCONV_CODEPAGE);
     psi->filenameslastp+=len+1;

     if(psi->editsidetype&PLT_EXTENDED){ // #EXTM3U
      if(!(pei->infobits&PEIF_ENABLED))   // entry has no #EXTINF
       funcbit_disable(psi->editsidetype,PLT_EXTENDED); // disable extfunc
     }
     pei++;
     sprintf(strtmp,"Loading list: %4d. (press ESC to stop)",++linecount);
     display_message(0,0,strtmp);
    }
   }else{ // winamp EXTINF check & load
    if(pds_strncmp(strtmp,"#EXTINF:",8)==0){
     ps=&strtmp[8]; // begin of timesec
     pa=pds_strchr(ps,',');
     if(pa){
      pa[0]=0;   // end of timesec
      timesec=pds_atol(ps);
      if(timesec>=0){
       pei->timemsec=timesec*1000;
       funcbit_enable(pei->infobits,PEIF_ENABLED);
      }

      if((loadid3tag&ID3LOADMODE_LIST) && (psi->id3infolastp<psi->id3infoendp)){
       pa++;      // begin of artist
       pt=pds_strchr(pa,'\n'); // cut '\n' from the end of string
       if(pt)
        pt[0]=0;
       if(!pt || (pt>pa)){
        unsigned int convdone=0;
        pt=pds_strstr(pa," - "); // search for the ' - ' separator (artist-title)
        if(pt){
         pt[0]=0;  // end of artist
         pt+=3;    // begin of title
        }
        len=pds_strcpy(psi->id3infolastp,pa);
        if(this_is_m3u8 && (id3textconv&ID3TEXTCONV_UTF_AUTO)){
         len=mpxplay_playlist_textconv_funcs.utf8_to_char(psi->id3infolastp,len);
         convdone=ID3TEXTCONV_UTF_ALL;
        }
        len=mpxplay_playlist_textconv_do(psi->id3infolastp,len,convdone);
        if(len){
         pei->id3info[I3I_ARTIST]=psi->id3infolastp;
         psi->id3infolastp+=len+1;
         funcbit_enable(pei->infobits,PEIF_ID3EXIST);
        }
        if(pt){
         len=pds_strcpy(psi->id3infolastp,pt);
         if(this_is_m3u8 && (id3textconv&ID3TEXTCONV_UTF_AUTO))
          len=mpxplay_playlist_textconv_funcs.utf8_to_char(psi->id3infolastp,len);
         len=mpxplay_playlist_textconv_do(psi->id3infolastp,len,convdone);
         if(len){
          pei->id3info[I3I_TITLE]=psi->id3infolastp;
          psi->id3infolastp+=len+1;
          funcbit_enable(pei->infobits,PEIF_ID3EXIST);
         }
        }
       }
      }
     }
    }else{
     if(pds_strncmp(strtmp,"#EXTM3U",7)==0)
      funcbit_enable(psi->editsidetype,PLT_EXTENDED);
    }
   }
  }
 }
 if(pds_look_extgetch()==KEY_ESC)
  pds_extgetch();
 clear_message();
 return pei;
}

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

static struct playlist_entry_info *open_load_pls(struct playlist_entry_info *pei,struct playlist_side_info *psi,char *listname)
{
 //struct playlist_entry_info *ipei;
 FILE *listfile;
 unsigned int len,first=1,linecount=0;
 char *datap,*sp,strtmp[360];

 if((listfile=pds_fopen(listname,"rt"))==NULL)
  return pei;

 funcbit_enable(psi->editsidetype,PLT_EXTENDED);

 pei--;

 while(fgets(strtmp,350,listfile) && (pei<=psi->endentry) && (psi->filenameslastp<psi->filenamesendp)){
  datap=pds_strchr(strtmp,'=');
  if(!datap)
   continue;
  *datap++=0;
  sp=pds_strchr(datap,'\n');
  if(sp){
   if(sp==datap)
    continue;
   *sp=0;
  }

  if(pds_strncmp(strtmp,"File",sizeof("File"))==0){
   if(pds_look_extgetch()==KEY_ESC)
    break;

   //ipei=pei+atol(&strtmp[sizeof("File")]);
   if(!first && (psi->editsidetype&PLT_EXTENDED)){
    if(!(pei->infobits&PEIF_ENABLED))
     funcbit_disable(psi->editsidetype,PLT_EXTENDED);
   }
   first=0;
   pei++;
   pei->filename=psi->filenameslastp;
   pds_fullpath(pei->filename,datap);
   len=pds_strlen(pei->filename);
   if(id3textconv&ID3TEXTCONV_FILENAME)
    len=mpxplay_playlist_textconv_do(pei->filename,len,~ID3TEXTCONV_CODEPAGE);
   psi->filenameslastp+=len+1;
   sprintf(strtmp,"Loading list: %4d. (press ESC to stop)",++linecount);
   display_message(0,0,strtmp);
   continue;
  }
  if(first)
   continue;
  if((loadid3tag&ID3LOADMODE_LIST) && (psi->id3infolastp<psi->id3infoendp)){
   if(pds_strncmp(strtmp,"Title",sizeof("Title"))==0){
    if(!pei->id3info[I3I_ARTIST] && !pei->id3info[I3I_TITLE]){
     char *pt=pds_strstr(datap," - "); // Winamp style
     if(!pt)
      pt=pds_strstr(datap," / ");      // Sonique style
     if(pt){
      pt[0]=0;  // end of artist
      pt+=3;    // begin of title
     }
     len=pds_strcpy(psi->id3infolastp,datap);
     len=mpxplay_playlist_textconv_do(psi->id3infolastp,len,0);
     if(len){
      pei->id3info[I3I_ARTIST]=psi->id3infolastp;
      psi->id3infolastp+=len+1;
      funcbit_enable(pei->infobits,PEIF_ID3EXIST);
     }
     if(pt){
      len=pds_strcpy(psi->id3infolastp,pt);
      len=mpxplay_playlist_textconv_do(psi->id3infolastp,len,0);
      if(len){
       pei->id3info[I3I_TITLE]=psi->id3infolastp;
       psi->id3infolastp+=len+1;
       funcbit_enable(pei->infobits,PEIF_ID3EXIST);
      }
     }
    }
    continue;
   }
  }
  if(pds_strncmp(strtmp,"Length",sizeof("Length"))==0){
   int timesec=pds_atol(datap);
   if(timesec>=0){
    pei->timemsec=timesec*1000;
    funcbit_enable(pei->infobits,PEIF_ENABLED);
   }
  }
 }
 pei++;
 if(pds_look_extgetch()==KEY_ESC)
  pds_extgetch();
 clear_message();
 fclose(listfile);
 return pei;
}

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

static struct playlist_entry_info *open_load_mxu(struct playlist_entry_info *pei,struct playlist_side_info *psi,char *listname)
{
 FILE *listfile;
 unsigned int len,timesec;
 char *lp,*listparts[MAX_ID3LISTPARTS];
 char strtmp[360],strtemp2[360];

 if((listfile=pds_fopen(listname,"rt"))==NULL)
  return pei;

 funcbit_enable(psi->editsidetype,PLT_EXTENDED);

 while(fgets(strtmp,350,listfile) && (pei<=psi->endentry) && (psi->filenameslastp<psi->filenamesendp)){
  if(strtmp[0]){
   if(strtmp[0]!='#'){
    pds_memset(&(listparts[0]),0,4*sizeof(char *));
    pds_strcpy(strtemp2,strtmp);
    listline_slice(&(listparts[0]),"°±²\n",strtemp2);
    //new mxu style (from v1.43)
    if(listparts[0] && listparts[1] && listparts[2] && listparts[3]){
     pei->filename=psi->filenameslastp;
     len=pds_strcpy(pei->filename,listparts[0]);
     if(id3textconv&ID3TEXTCONV_FILENAME)
      len=mpxplay_playlist_textconv_do(pei->filename,len,~ID3TEXTCONV_CODEPAGE);
     psi->filenameslastp+=len+1;

     if((loadid3tag&ID3LOADMODE_LIST) && (psi->id3infolastp<psi->id3infoendp)){
      if(listparts[1][0]){
       len=pds_strcpy(psi->id3infolastp,listparts[1]);
       len=mpxplay_playlist_textconv_do(psi->id3infolastp,len,0);
       if(len){
        pei->id3info[I3I_ARTIST]=psi->id3infolastp;
        psi->id3infolastp+=len+1;
        funcbit_enable(pei->infobits,PEIF_ID3EXIST);
       }
      }
      if(listparts[2][0]){
       len=pds_strcpy(psi->id3infolastp,listparts[2]);
       len=mpxplay_playlist_textconv_do(psi->id3infolastp,len,0);
       if(len){
        pei->id3info[I3I_TITLE]=psi->id3infolastp;
        psi->id3infolastp+=len+1;
        funcbit_enable(pei->infobits,PEIF_ID3EXIST);
       }
      }
     }
     timesec=pds_atol16(listparts[3]);
     if(timesec&MXUFLAG_ENABLED){
      pei->timemsec=(timesec&MXUFLAG_TIMEMASK)*1000;
      funcbit_enable(pei->infobits,PEIF_ENABLED);
     }else{
      pei->timemsec=0;
      playlist_loadlist_get_header_by_ext(pei,psi,pei->filename);
     }

    }else{
     // old mxu style (v1.42)
     pds_memset(&(listparts[0]),0,4*sizeof(char *));
     pds_strcpy(strtemp2,strtmp);
     listline_slice(&(listparts[0]),"°:±\n",strtemp2);

     if(listparts[0]){
      pei->filename=psi->filenameslastp;
      len=pds_strcpy(pei->filename,listparts[0]);
      if(id3textconv&ID3TEXTCONV_FILENAME)
       len=mpxplay_playlist_textconv_do(pei->filename,len,~ID3TEXTCONV_CODEPAGE);
      psi->filenameslastp+=len+1;
     }
     if((loadid3tag&ID3LOADMODE_LIST) && (psi->id3infolastp<psi->id3infoendp)){
      if(listparts[1]){
       lp=pds_strcutspc(listparts[1]);
       if(lp){
        len=pds_strcpy(psi->id3infolastp,lp);
        len=mpxplay_playlist_textconv_do(psi->id3infolastp,len,0);
        if(len){
      	 pei->id3info[I3I_ARTIST]=psi->id3infolastp;
	 psi->id3infolastp+=len+1;
         funcbit_enable(pei->infobits,PEIF_ID3EXIST);
        }
       }
      }
      if(listparts[2]){
       lp=pds_strcutspc(listparts[2]);
       if(lp){
        len=pds_strcpy(psi->id3infolastp,lp);
        len=mpxplay_playlist_textconv_do(psi->id3infolastp,len,0);
        if(len){
         pei->id3info[I3I_TITLE]=psi->id3infolastp;
	 psi->id3infolastp+=len+1;
         funcbit_enable(pei->infobits,PEIF_ID3EXIST);
        }
       }
      }
     }
     if(listparts[3]){
      funcbit_enable(pei->infobits,PEIF_ENABLED);
     }
    }
    pei++;
   }
  }
  if(pds_look_extgetch()==KEY_ESC)
   break;
 }
 if(pds_look_extgetch()==KEY_ESC)
  pds_extgetch();
 fclose(listfile);
 return pei;
}

//------------------------------------------------------------------------
// FPL loading

static unsigned long pds_read_long_le(FILE *listfile)
{
 unsigned long num;
 fread((char *)&num,4,1,listfile);
 return num;
}

typedef struct fpl_main_header_s{
 unsigned long a;
 unsigned long b;
 unsigned long c;
 unsigned long e;
 unsigned long f;
 unsigned long number_of_entries;
}fpl_main_header_s;

typedef struct fpl_entry_info_s{
 unsigned long filesize;
 unsigned long c;        // filesize_major ?
 unsigned long d;
 unsigned long e;
 unsigned long f;
 unsigned long g;
}fpl_entry_info_s;

#define FPL_TAG_TYPES 7

static char *fpltagtypes[FPL_TAG_TYPES]={"Title","Artist","Album","Date","Comment","Genre","Tracknumber"};
static unsigned int id3index[FPL_TAG_TYPES]={I3I_TITLE,I3I_ARTIST,I3I_ALBUM,I3I_YEAR,I3I_COMMENT,I3I_GENRE,I3I_TRACKNUM};

static struct playlist_entry_info *open_load_fpl(struct playlist_entry_info *pei,struct playlist_side_info *psi,char *listname)
{
 FILE *listfile;
 unsigned long e,t,i,g,filetype,tags_in_group,taglen,datalen,bitrate;
 struct fpl_main_header_s fpl_mainhead;
 struct fpl_entry_info_s fpl_entryinfo;
 char *s,tagname[360],tagdata[360];

 if((listfile=pds_fopen(listname,"rb"))==NULL)
  return pei;

 funcbit_enable(psi->editsidetype,PLT_EXTENDED);

 fread((char *)&fpl_mainhead,sizeof(struct fpl_main_header_s),1,listfile);

 for(e=0;(e<fpl_mainhead.number_of_entries) && (pei<=psi->endentry) && (psi->filenameslastp<psi->filenamesendp);e++){
  sprintf(tagdata,"Loading list %4d/%d (press ESC to stop)",e,fpl_mainhead.number_of_entries);
  display_message(0,0,tagdata);
  filetype=pds_read_long_le(listfile); // ???
  taglen  =pds_read_long_le(listfile);
  if(taglen>=360)
   break;
  fread(tagname,1,taglen,listfile);
  tagname[taglen]=0;
  pei->filename=psi->filenameslastp;
  s=&tagname[0];
  if(pds_strncmp(s,"file://",sizeof("file://")-1)==0){
   unsigned int len;
   s+=sizeof("file://")-1;
   pds_fullpath(pei->filename,s);
   len=pds_strlen(pei->filename);
   if(id3textconv&ID3TEXTCONV_FILENAME){
    unsigned int convdone=~ID3TEXTCONV_CODEPAGE;
    if(id3textconv&ID3TEXTCONV_UTF_AUTO){
     len=mpxplay_playlist_textconv_funcs.utf8_to_char(pei->filename,len); // ??? allways utf-8 ?
     convdone=ID3TEXTCONV_UTF_ALL;
    }
    len=mpxplay_playlist_textconv_do(pei->filename,len,convdone);
   }
   psi->filenameslastp+=len+1;
  }else{
   psi->filenameslastp+=pds_strcpy(pei->filename,s)+1;
  }
  pds_read_long_le(listfile); // unknown info (usually 0)
  if(filetype){
   fread((char *)&fpl_entryinfo,sizeof(fpl_entry_info_s),1,listfile);
   for(g=0;g<2;g++){ // ???
    tags_in_group=pds_read_long_le(listfile);
    for(t=0;t<tags_in_group;t++){
     taglen=pds_read_long_le(listfile);
     if(taglen>=360)
      break;
     fread(tagname,1,taglen,listfile);
     tagname[taglen]=0;
     datalen=pds_read_long_le(listfile);
     if(datalen>=360)
      break;
     fread(tagdata,1,datalen,listfile);
     tagdata[datalen]=0;
     if(pds_strcmp(tagname,"bitrate")==0){
      bitrate=pds_atol(tagdata);
      if(bitrate && fpl_entryinfo.filesize){
       pei->timemsec=(long)((float)fpl_entryinfo.filesize*8.0/(float)bitrate);
       funcbit_enable(pei->infobits,PEIF_ENABLED);
      }
     }else{
      if((loadid3tag&ID3LOADMODE_LIST) && (psi->id3infolastp<psi->id3infoendp)){
       for(i=0;i<FPL_TAG_TYPES;i++){
        if((pds_strcmp(tagname,fpltagtypes[i])==0) && !pei->id3info[id3index[i]]){
         unsigned int len=pds_strcpy(psi->id3infolastp,tagdata),convdone=0;
         if(id3textconv&ID3TEXTCONV_UTF_AUTO){
          len=mpxplay_playlist_textconv_funcs.utf8_to_char(psi->id3infolastp,len); // ??? UTF-8 allways?
          convdone=ID3TEXTCONV_UTF_ALL;
         }
         len=mpxplay_playlist_textconv_do(psi->id3infolastp,len,convdone);
         if(len){
          pei->id3info[id3index[i]]=psi->id3infolastp;
          psi->id3infolastp+=len+1;
          funcbit_enable(pei->infobits,PEIF_ID3EXIST);
         }
         break;
        }
       }
      }
     }
    }
   }
  }
  if(!(pei->infobits&PEIF_ENABLED))
   funcbit_disable(psi->editsidetype,PLT_EXTENDED);
  pei++;
  if(pds_look_extgetch()==KEY_ESC)
   break;
 }
 if(pds_look_extgetch()==KEY_ESC)
  pds_extgetch();
 clear_message();

 fclose(listfile);
 return pei;
}

//-------------------------------------------------------------------------
// CUE loading

typedef struct cue_handler_s{
 char *command;
 struct playlist_entry_info *(*handler)(struct playlist_entry_info *pei,struct playlist_side_info *psi,char *comm_arg);
}cue_handler_s;

static char *cue_cut_string(char *comm_arg) // cut " at begin and end
{
 char *s;

 if(*comm_arg=='\"'){
  comm_arg++;
  s=strchr(comm_arg,'\"');
  if(s)
   *s=0;
 }else{
  s=strrchr(comm_arg,'\"');
  if(s)
   *s=0;
  else{
   s=strchr(comm_arg,' ');
   if(s)
    *s=0;
  }
 }
 return comm_arg;
}

static unsigned int cue_filecount,cue_newfile,cue_trackcount,cue_indexcount;
static char cue_artist[MAX_ID3LEN/2],cue_title[MAX_ID3LEN/2];
static char cue_main_artist[MAX_ID3LEN/2],cue_main_title[MAX_ID3LEN/2];
static unsigned int cue_main_filenumpos_artist,cue_main_filenumpos_title;

static void cue_set_prev_id3info(struct playlist_entry_info *pei,struct playlist_side_info *psi,unsigned int newfile)
{
 struct playlist_entry_info *plx;
 unsigned int len;

 //if(cue_newfile || (newfile && (cue_indexcount<=2)))
 // return;
 if(pei<=psi->firstsong)
  return;
 plx=pei-1;

 if(!plx->id3info[I3I_ARTIST] && (cue_artist[0] || cue_main_artist[0] || cue_main_title[0]) && (psi->id3infolastp<psi->id3infoendp)){
  char *p;
  if(cue_artist[0])
   p=&cue_artist[0];
  else if(cue_main_title[0]) // !!! primary, because the main title usually an album name (?)
   p=&cue_main_title[0];
  else
   p=&cue_main_artist[0];
  len=pds_strcpy(psi->id3infolastp,p);
  if(len>0){
   plx->id3info[I3I_ARTIST]=psi->id3infolastp;
   psi->id3infolastp+=len+1;
   funcbit_enable(plx->infobits,PEIF_ID3EXIST);
  }
 }
 if(!plx->id3info[I3I_TITLE] && (psi->id3infolastp<psi->id3infoendp)){
  char *title;
  if(cue_title[0])
   title=&cue_title[0];
  else if(cue_main_title[0])
   title=&cue_main_title[0];
  else if(plx->id3info[I3I_ARTIST])
   title=&cue_title[0];
  else
   title=pds_getfilename_from_fullname(plx->filename);
  len=snprintf(psi->id3infolastp,MAX_ID3LEN/2,"%s%s(index %d.)",title,((title[0])? " ":""),cue_indexcount-1);
  if(len>0){
   plx->id3info[I3I_TITLE]=psi->id3infolastp;
   psi->id3infolastp+=len+1;
   funcbit_enable(plx->infobits,PEIF_ID3EXIST);
  }
 }
}

static struct playlist_entry_info *cue_commandhand_file(struct playlist_entry_info *pei,struct playlist_side_info *psi,char *comm_arg)
{
 unsigned int len;
 char strtmp[128];

 if(cue_filecount>cue_main_filenumpos_artist) // obsolete main artist
  cue_main_artist[0]=0;
 if(cue_filecount>cue_main_filenumpos_title)  // or title
  cue_main_title[0]=0;

 sprintf(strtmp,"Loading list: %4d. (press ESC to stop)",++cue_filecount);
 display_message(0,0,strtmp);

 comm_arg=cue_cut_string(comm_arg);

 pds_fullpath(psi->filenameslastp,comm_arg);
 len=pds_strlen(psi->filenameslastp);
 if(len){
  if(cue_newfile) // !!! (else pei was incremented in commandhand_index)
   pei++;
  //else
   cue_set_prev_id3info(pei,psi,1);
  if(pei>psi->endentry){
   cue_newfile=0;
   return pei;
  }
  pei->filename=psi->filenameslastp;
  if(id3textconv&ID3TEXTCONV_FILENAME)
   len=mpxplay_playlist_textconv_do(pei->filename,len,~ID3TEXTCONV_CODEPAGE);
  psi->filenameslastp+=len+1;
  cue_newfile=1;
  cue_trackcount=0;
  cue_indexcount=1;
 }

 cue_artist[0]=cue_title[0]=0;

 return pei;
}

static struct playlist_entry_info *cue_commandhand_track(struct playlist_entry_info *pei,struct playlist_side_info *psi,char *comm_arg)
{
 cue_trackcount++;
 return pei;
}

static struct playlist_entry_info *cue_commandhand_performer(struct playlist_entry_info *pei,struct playlist_side_info *psi,char *comm_arg)
{
 if((loadid3tag&ID3LOADMODE_LIST) && (psi->id3infolastp<psi->id3infoendp)){
  unsigned int len;
  char *destp;

  if(pei<=psi->lastentry){ // PERFORMER before FILE
   destp=&cue_main_artist[0];
   cue_main_filenumpos_artist=cue_filecount; // assigned to next file only
  //}else if(cue_newfile && !cue_trackcount){ // PERFORMER before TRACK
  // destp=&cue_main_artist[0];
  // cue_main_filenumpos_artist=cue_filecount-1; // assigned to curr file only ???
  }else{
   if(pei->id3info[I3I_ARTIST])
    return pei;
   destp=psi->id3infolastp;
  }

  comm_arg=cue_cut_string(comm_arg);

  len=pds_strcpy(destp,comm_arg);
  len=mpxplay_playlist_textconv_do(destp,len,0);
  if(len){
   if(pei>psi->lastentry){
    pei->id3info[I3I_ARTIST]=destp;
    psi->id3infolastp+=len+1;
    funcbit_enable(pei->infobits,PEIF_ID3EXIST);
    if(cue_newfile && !cue_trackcount)
     pds_strcpy(cue_artist,destp);
   }
  }
 }

 return pei;
}

static struct playlist_entry_info *cue_commandhand_title(struct playlist_entry_info *pei,struct playlist_side_info *psi,char *comm_arg)
{
 if((loadid3tag&ID3LOADMODE_LIST) && (psi->id3infolastp<psi->id3infoendp)){
  unsigned int len;
  char *destp;

  if(pei<=psi->lastentry){ // TITLE before FILE
   destp=&cue_main_title[0];
   cue_main_filenumpos_title=cue_filecount; // assigned to next file only
  //}else if(cue_newfile && !cue_trackcount){
  // destp=&cue_main_title[0];
  // cue_main_filenumpos_title=cue_filecount-1;
  }else{
   if(pei->id3info[I3I_TITLE])
    return pei;
   destp=psi->id3infolastp;
  }

  comm_arg=cue_cut_string(comm_arg);

  len=pds_strcpy(destp,comm_arg);
  len=mpxplay_playlist_textconv_do(destp,len,0);
  if(len){
   if(pei>psi->lastentry){
    pei->id3info[I3I_TITLE]=destp;
    psi->id3infolastp+=len+1;
    funcbit_enable(pei->infobits,PEIF_ID3EXIST);
    if(cue_newfile && !cue_trackcount)
     pds_strcpy(cue_title,destp);
   }
  }
 }

 return pei;
}

static struct playlist_entry_info *cue_commandhand_index(struct playlist_entry_info *pei,struct playlist_side_info *psi,char *comm_arg)
{
 long len;
 unsigned long hextime,pt;
 struct playlist_entry_info *plx;

 if(pei<=psi->lastentry) // first we need a filename (we haven't got it) ...
  return pei;

 if(cue_newfile)
  plx=NULL;
 else
  plx=pei-1;  // previous entry

 if((comm_arg[0]=='E') && (comm_arg[1]=='E')){ // magic index (EE) closes (sets the end of the) last index
  if(plx){
   comm_arg+=2;          // skip EE
   while(*comm_arg==' ') // skip spaces after EE
    comm_arg++;
   hextime=pds_strtime_to_hextime(comm_arg,1); // convert MM:SS:FF to a hex number (xxMMSSFF)
   plx->petime=((hextime>>16)&0x3fff)*60*1000+((hextime>>8)&0xff)*1000+(hextime&0xff)*100*1000/75; // convert hex number to millisecs
  }
 }else{
  while((*comm_arg!=' ') && (*comm_arg!=0)) // skip index number
   comm_arg++;
  pt=0;
  if(*comm_arg){
   while(*comm_arg==' ')                    // skip spaces after index number
    comm_arg++;
   hextime=pds_strtime_to_hextime(comm_arg,1); // convert MM:SS:FF to a hex number (xxMMSSFF)
   pt=((hextime>>16)&0x3fff)*60*1000+((hextime>>8)&0xff)*1000+(hextime&0xff)*10*100/75; // convert hex number to millisecs
  }

  pei->pstime=pt; // set the start of index
  funcbit_enable(pei->infobits,PEIF_INDEXED);

  if(plx){
   plx->petime=pt; // set the end of (previous) index

   // copy filename from previos track/index (if not exists in the current one)
   if(!pei->filename && plx->filename){
    if(psi->filenameslastp>=psi->filenamesendp)
     return pei;
    len=pds_strcpy(psi->filenameslastp,plx->filename);
    if(len<=0)
     return pei;
    pei->filename=psi->filenameslastp;
    psi->filenameslastp+=len+1;
   }
   // close id3info
   cue_set_prev_id3info(pei,psi,0);
  }

  pei++;
  cue_filecount++;
  cue_indexcount++;
  cue_newfile=0;
 }

 return pei;
}

static struct cue_handler_s cue_handlers[]=
{
 {"FILE",     &cue_commandhand_file},
 {"TRACK",    &cue_commandhand_track},
 {"PERFORMER",&cue_commandhand_performer},
 {"TITLE",    &cue_commandhand_title},
 {"INDEX",    &cue_commandhand_index},
 {NULL,NULL}
};

static struct playlist_entry_info *open_load_cue(struct playlist_entry_info *pei,struct playlist_side_info *psi,char *listname)
{
 FILE *listfile;
 char strtmp[360];

 if((listfile=pds_fopen(listname,"rt"))==NULL)
  return pei;

 cue_filecount=0;
 cue_newfile=1;
 cue_trackcount=0;
 cue_indexcount=1;
 cue_artist[0]=cue_title[0]=0;
 cue_main_artist[0]=cue_main_title[0]=0;

 pei--;

 while(fgets(strtmp,350,listfile) && (pei<=psi->endentry) && (psi->filenameslastp<psi->filenamesendp)){
  struct cue_handler_s *cuh=&cue_handlers[0];
  char *commp=&strtmp[0],*argp,*endp;

  while(*commp==' ') // skip spaces before command
   commp++;

  argp=commp;
  while((*argp!=' ') && (*argp!=0)) // skip command (search for the argument)
   argp++;

  if(*argp!=0){
   *argp=0;
   argp++;
   while(*argp==' ')  // skip spaces before argument
    argp++;
   endp=pds_strchr(argp,'\n');
   if(endp)
    *endp=0;
   do{
    if(pds_stricmp(commp,cuh->command)==0){
     pei=cuh->handler(pei,psi,argp);
     break;
    }
    cuh++;
   }while(cuh->command);
  }
  if(pds_look_extgetch()==KEY_ESC)
   break;
 }

 if(cue_newfile)
  pei++;
 else
  cue_set_prev_id3info(pei,psi,1);

 if(pds_look_extgetch()==KEY_ESC)
  pds_extgetch();
 clear_message();

 fclose(listfile);
 return pei;
}

//-------------------------------------------------------------------------
// assign playlist entrytype/id3-name
unsigned int playlist_loadlist_get_header_by_ext(struct playlist_entry_info *pei,struct playlist_side_info *psi,char *filename)
{
 if(!playlist_loadsub_check_extension(filename))
  return 0;

 if((psi->editsidetype&PLT_DIRECTORY) && !(psi->psio->editsidetype&PLT_DIRECTORY))
  pei->entrytype=DFT_PLAYLIST;
 else
  pei->entrytype=DFT_SUBLIST;
 if(!pei->id3info[I3I_ARTIST] && !pei->id3info[I3I_TITLE])
  switch(pei->entrytype){
   case DFT_PLAYLIST:pei->id3info[I3I_DFT_STORE]=DFTSTR_PLAYLIST;break;
   case DFT_SUBLIST :pei->id3info[I3I_DFT_STORE]=DFTSTR_SUBLIST;break;
  }
 return 1;
}

//-------------------------------------------------------------------------
void playlist_loadlist_load_autosaved_list(struct mainvars *mvp)
{
 if(playlistsave&PLST_AUTO){
  struct playlist_side_info *psi0=mvp->psi0;
  struct playlist_side_info *psi1=psi0+1;

  if(!(psi1->editsidetype&PLT_ENABLED)){
   char filename[MAX_PATHNAMELEN];
   pds_getpath_from_fullname(filename,freeopts[OPT_PROGNAME]);
   pds_strcat(filename,PDS_DIRECTORY_SEPARATOR_STR);
   pds_strcat(filename,playlist_savelist_get_savename(playlistsave));
   playlist_loadlist_mainload(psi1,filename,0);
  }
 }
}
