//**************************************************************************
//*                     This file is part of the                           *
//*                      Mpxplay - audio player.                           *
//*                  The source code of Mpxplay is                         *
//*        (C) copyright 1998-2011 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: in-file handling - main/common routines

//#define MPXPLAY_USE_DEBUGF 1
#define MPXPLAY_DEBUG_OUTPUT stdout
//#define MPXPLAY_USE_DEBUGMSG 1

#include "mpxplay.h"
#include "mpxinbuf.h"
#include "au_mixer\au_mixer.h"
#include "au_mixer\mix_func.h"
#include "decoders\decoders.h"
#include "deparser\tagging.h"
#include "diskdriv\diskdriv.h"
#include "newfunc\dll_load.h"
#include "display\display.h"

extern struct mpxpframe_s fr[3];
extern struct mainvars mvps;

//parsers
extern struct mpxplay_infile_func_s IN_AAC_funcs,IN_AC3_funcs,IN_APE_funcs;
extern struct mpxplay_infile_func_s IN_CDW_funcs,IN_FLAC_funcs;
extern struct mpxplay_infile_func_s IN_MP2_funcs,IN_MP3_funcs,IN_MPC_funcs;
extern struct mpxplay_infile_func_s IN_OGG_funcs,IN_TAK_funcs,IN_WAVPACK_funcs;

//containers
extern struct mpxplay_infile_func_s IN_ASF_funcs,IN_AVI_funcs,IN_MP4_funcs;
extern struct mpxplay_infile_func_s IN_WAV_funcs,IN_AIF_funcs,IN_FLV_funcs;
extern struct mpxplay_infile_func_s IN_MKV_funcs,IN_MPG_funcs,IN_TS_funcs;

static struct mpxplay_infile_func_s *all_infile_funcs[]={
#ifdef MPXPLAY_LINK_INFILE_WAV // first (it's possible with other filename extensions (ie:MP3))
 &IN_WAV_funcs,
 &IN_AIF_funcs,
#endif
#ifdef MPXPLAY_LINK_INFILE_ASF // the others are sorted by autodetection speed
 &IN_ASF_funcs,
#endif
#ifdef MPXPLAY_LINK_INFILE_AVI
 &IN_AVI_funcs,
#endif
#ifdef MPXPLAY_LINK_INFILE_MP4
 &IN_MP4_funcs,
#endif
#ifdef MPXPLAY_LINK_INFILE_OGG
 &IN_OGG_funcs,
#endif
#ifdef MPXPLAY_LINK_INFILE_FFMPG
 &IN_FLV_funcs,
 &IN_MKV_funcs,
#endif

#ifdef MPXPLAY_LINK_INFILE_APE
 &IN_APE_funcs,
#endif
#ifdef MPXPLAY_LINK_INFILE_CDW
 &IN_CDW_funcs,
#endif
#ifdef MPXPLAY_LINK_INFILE_FLAC
 &IN_FLAC_funcs,
#endif
#ifdef MPXPLAY_LINK_INFILE_MPC
 &IN_MPC_funcs,
#endif
#ifdef MPXPLAY_LINK_INFILE_TAK
 &IN_TAK_funcs,
#endif
#ifdef MPXPLAY_LINK_INFILE_WAVPACK
 &IN_WAVPACK_funcs,
#endif
#ifdef MPXPLAY_LINK_INFILE_FFMPG
 &IN_TS_funcs,
 &IN_MPG_funcs,
#endif
#ifdef MPXPLAY_LINK_INFILE_AC3
 &IN_AC3_funcs,
#endif
#ifdef MPXPLAY_LINK_INFILE_MPX
 &IN_MP3_funcs,
 &IN_MP2_funcs,
#endif
#ifdef MPXPLAY_LINK_INFILE_AAC // last (must be if autodetection is enabled, currently it isn't)
 &IN_AAC_funcs,
#endif
 NULL
};

#define LAST_INFILE_FUNCNUM (sizeof(all_infile_funcs)/sizeof(struct mpxplay_infile_func_s *)-2)

#ifdef MPXPLAY_LINK_DLLLOAD

static struct infiledlltype_s{
 unsigned int dlltype;
 unsigned int modulever;
}infiledlltypes[]=
{
 {MPXPLAY_DLLMODULETYPE_FILEIN_PARSER,   MPXPLAY_DLLMODULEVER_FILEIN_PARSER},
 {MPXPLAY_DLLMODULETYPE_FILEIN_CONTAINER,MPXPLAY_DLLMODULEVER_FILEIN_CONTAINER},
 {0,0}
};

#endif

static unsigned int infile_checkhead_by_ext(struct mpxpframe_s *frp,struct mpxplay_diskdrive_data_s *mdds,struct mpxplay_infile_func_s *infilefuncs,char *filename,char *extension,unsigned int *extfound,mpxp_uint32_t openmode);
static int infile_subdecode(struct mpxpframe_s *frp);

extern char *id3tagset[I3I_MAX+1];
extern unsigned int crossfadepart,displaymode,desktopmode,videocontrol;
extern unsigned int stream_select_audio,channelmode,loadid3tag;//,playcontrol;
extern mpxp_uint32_t mpxplay_programcontrol;

static struct mpxplay_infile_info_s        infile_infos[3];
static struct mpxplay_streampacket_info_s  audiopacket_infos[3];
static struct mpxplay_audio_decoder_info_s audiodecoder_infos[3];
#ifdef MPXPLAY_LINK_VIDEO
static struct mpxplay_streampacket_info_s  videopacket_infos[3];
static struct mpxplay_video_decoder_info_s videodecoder_infos[3];
#endif

//char mpxplay_tag_year[8];

int mpxplay_infile_decode(struct mpxplay_audioout_info_s *aui)
{
 long retcode_demuxer,retcode_decoder;
 unsigned int fileselect,retry_count_decoder;
 struct mpxpframe_s *frp0=aui->mvp->frp0,*frp;

 for(fileselect=0,frp=frp0;fileselect<=((crossfadepart==CROSS_FADE)? 1:0);fileselect++,frp++){
  struct mpxplay_audio_decoder_info_s *adi=frp->infile_infos->audio_decoder_infos;
#ifdef MPXPLAY_LINK_VIDEO
  struct mpxplay_video_decoder_info_s *vdi=frp->infile_infos->video_decoder_infos;

  if((displaymode&DISP_GRAPHICAL) && (vdi->flags&VDI_CNTRLBIT_DECODEVIDEO))
   funcbit_enable(vdi->flags,VDI_CNTRLBIT_SHOWVIDEO);
  else
   funcbit_disable(vdi->flags,VDI_CNTRLBIT_SHOWVIDEO);
#endif

  mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"infile_decode begin");

  if(frp->infile_infos->seektype){
   if(frp->infile_funcs && frp->infile_funcs->seek_postprocess)
    frp->infile_funcs->seek_postprocess(frp->filebuf_funcs,frp,frp->infile_infos,frp->infile_infos->seektype);
   mpxplay_decoders_clearbuf(frp->infile_infos,frp,frp->infile_infos->seektype);
   funcbit_smp_disable(frp->infile_infos->seektype,MPX_SEEKTYPES_CLEARBUF);
  }

  retcode_demuxer = MPXPLAY_ERROR_INFILE_OK;
  retry_count_decoder = 32; // if decoder doesn't give back correct retcode (if it always give back OK)
  do{
   if(frp->pcmdec_leftsamples && (frp->pcmdec_storedsamples >= frp->pcmdec_leftsamples)){
    unsigned long used_samples = AUMIXER_collect_pcmout_data(frp,(PCM_CV_TYPE_S *)(frp->pcmdec_buffer+(frp->pcmdec_storedsamples-frp->pcmdec_leftsamples)*adi->bytespersample),frp->pcmdec_leftsamples);
    frp->pcmdec_leftsamples -= used_samples;
    if(!used_samples || (frp->pcmout_storedsamples >= frp->pcmout_blocksize))
     break;
   }
   /*if(frp->pcmdec_leftsamples && (frp->pcmdec_storedsamples>=frp->pcmdec_leftsamples)){
    unsigned long used_samples=AUMIXER_collect_pcmout_data(frp,(PCM_CV_TYPE_S *)(frp->pcmdec_buffer+(frp->pcmdec_storedsamples-frp->pcmdec_leftsamples)*adi->bytespersample),frp->pcmdec_leftsamples);
    if(used_samples<frp->pcmdec_leftsamples){
     frp->pcmdec_leftsamples-=used_samples;
     break;
    }
   }*/
   adi->pcm_bufptr = frp->pcmdec_buffer;
   adi->pcm_samplenum = 0;
   mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"infile_decode demux n:%d",frp->frameNum);

   if(retcode_demuxer==MPXPLAY_ERROR_INFILE_OK)
    retcode_demuxer=infile_subdecode(frp);

   mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"infile_decode decode lb:%d wi:%4.4X",
     frp->infile_infos->audio_stream->bs_leftbytes,frp->infile_infos->audio_stream->wave_id);

   retcode_decoder=mpxplay_decoders_decode(frp->infile_infos);
   frp->pcmdec_storedsamples = frp->pcmdec_leftsamples = adi->pcm_samplenum;

   mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"decode ss:%4d lb:%d ub:%4d",frp->pcmdec_storedsamples,
     frp->infile_infos->audio_stream->bs_leftbytes,frp->infile_infos->audio_stream->bs_usedbytes);

   //if((adi->infobits&ADI_CNTRLBIT_BITSTREAMOUT) && adi->pcm_samplenum)
   // break;

  }while((--retry_count_decoder) && ((retcode_demuxer==MPXPLAY_ERROR_INFILE_OK) || (retcode_decoder==MPXPLAY_ERROR_INFILE_OK)));

  //if(frp->pcmdec_leftsamples || (frp->pcmout_storedsamples && (retcode_demuxer==MPXPLAY_ERROR_INFILE_EOF))){
  if(frp->pcmout_storedsamples){
   mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"get    ss:%4d bsc:%4d %8.8X ", frp->pcmout_storedsamples,frp->pcmout_blocksize,frp->pcmout_buffer);
   MIXER_conversion(&aui->mvp->mmi,aui,frp);
   frp->frameNum++;
   frp->framecounter++;
  }else if(fileselect && ((retcode_demuxer==MPXPLAY_ERROR_INFILE_EOF) || (retcode_demuxer==MPXPLAY_ERROR_INFILE_NODATA))){ // if crossfade
   mpxplay_decoders_reset(frp->infile_infos,frp);
   crossfade_part_step(aui->mvp);
  }else if((retcode_demuxer==MPXPLAY_ERROR_INFILE_EOF) || (retcode_demuxer==MPXPLAY_ERROR_INFILE_NODATA) || (retcode_demuxer==MPXPLAY_ERROR_INFILE_SYNC_IN) || (retcode_demuxer==MPXPLAY_ERROR_INFILE_RESYNC)){
   funcbit_disable(aui->card_controlbits,AUINFOS_CARDCNTRLBIT_DMADONTWAIT);
   return retcode_demuxer;
  }
 }

 MIXER_main(&aui->mvp->mmi,aui,frp0);

 AU_writedata(aui);

 return 0;
}

//--------------------------------------------------------------------------
// is extension supported?
unsigned int mpxplay_infile_check_extension(char *filename,struct mpxplay_diskdrive_data_s *mdds)
{
 unsigned int i,j;
 char *extension;

 extension=pds_strrchr(filename,'.');
 if(extension && extension[1]){
  extension++;
  for(i=0;i<=LAST_INFILE_FUNCNUM;i++){
   j=0;
   while(all_infile_funcs[i]->file_extensions[j]){
    if(pds_stricmp(all_infile_funcs[i]->file_extensions[j],extension)==0)
     return 1;
    j++;
   }
  }
#ifdef MPXPLAY_LINK_DLLLOAD // search file-extension in DLLs
  i=0;
  do{
   mpxplay_module_entry_s *dll_infile=NULL;
   do{
    dll_infile=newfunc_dllload_getmodule(infiledlltypes[i].dlltype,0,NULL,dll_infile);
    if(dll_infile){
     if(dll_infile->module_structure_version==infiledlltypes[i].modulever){ // !!!
      struct mpxplay_infile_func_s *inf=(struct mpxplay_infile_func_s *)dll_infile->module_callpoint;
      if(inf->file_extensions[0]){
       j=0;
       do{
        if(pds_stricmp(inf->file_extensions[j],extension)==0)
         return 1;
        j++;
       }while(inf->file_extensions[j]);
      }else{
       if(infile_checkhead_by_ext(&fr[3],mdds,inf,filename,extension,&j,MPXPLAY_INFILE_OPENMODE_INFO_HEAD))
        return 1;
      }
     }
    }
   }while(dll_infile);
   i++;
  }while(infiledlltypes[i].dlltype);
#endif
 }
 return 0;
}

unsigned int mpxplay_infile_get_samplenum_per_frame(unsigned int freq)
{
 struct mpxplay_audioout_info_s *aui=mvps.aui;
 unsigned int samplenum;

 if(freq>=32000 && freq<=48000)  // match with MPEG 1.0 and AC3 freqs
  samplenum=PCM_OUTSAMPLES;
 else if(freq>=8000 && freq<32000) // match with MPEG 2.x freqs
  samplenum=PCM_OUTSAMPLES/2;
 else
  samplenum=((PCM_OUTSAMPLES*freq)+22050)/44100;

 if(samplenum>PCM_MAX_SAMPLES)
  samplenum=PCM_MAX_SAMPLES;

 return samplenum;
}

/*unsigned int mpxplay_infile_get_samplenum_per_frame(unsigned int freq)
{
 struct mpxplay_audioout_info_s *aui=mvps.aui;
 unsigned int samplenum;
 if(freq>=32000 && freq<=48000)  // match with MPEG 1.0 and AC3 freqs (samplenum match at crossfade)
  samplenum=PCM_OUTSAMPLES;
 else
  if((freq>=16000 && freq<=24000) || (freq>=8000 && freq<=12000)) // match with MPEG 2.x freqs
   samplenum=PCM_OUTSAMPLES/2;
  else
   samplenum=((PCM_OUTSAMPLES*freq)+22050)/44100;

 if(samplenum>PCM_MAX_SAMPLES)
  samplenum=PCM_MAX_SAMPLES;

 return samplenum;
}*/

void miis_to_frp(struct mpxplay_infile_info_s *miis,struct mpxpframe_s *frp)
{
 struct mpxplay_audio_decoder_info_s *adi=miis->audio_decoder_infos;
 long samplenum_frame;

 funcbit_enable(frp->filetype,HFT_FILE_INT);
 frp->filesize=miis->filesize;

 // calculate samplenum
 if((adi->infobits&ADI_FLAG_BITSTREAMOUT) && adi->pcm_framelen)
  samplenum_frame=adi->pcm_framelen;
 else{
  samplenum_frame=mpxplay_infile_get_samplenum_per_frame(adi->freq);
  if(!adi->pcm_framelen) // ie: pcm decoder don't give back a specified length, we configure it
   adi->pcm_framelen=samplenum_frame;
 }

 // calculate allframes (if parser/demuxer didn't set)
 if(miis->allframes<=1){
  if(miis->timemsec) // containers (AVI,ASF,MP4,OGG,WAV)
   miis->allframes=(long)((float)miis->timemsec/1000.0*(float)adi->freq/(float)samplenum_frame);
  else
   if(adi->bitrate)  // pure bitstreams (AC3,DTS,MP3,etc.)
    miis->allframes=(long)((float)frp->filesize/(float)adi->bitrate*(float)adi->freq/(1000.0/8.0)/(float)samplenum_frame);
  if(miis->allframes<1)
   miis->allframes=1;
 }
 frp->allframes=miis->allframes;

 // calculate timesec (if parser/demuxer didn't set)
 if(!miis->timemsec){
  if(miis->allframes) // MP3-VBR,MPC (from header); AC3,DTS (calculated from bitrate)
   miis->timemsec=1000.0*(float)miis->allframes*(float)samplenum_frame/(float)adi->freq;
  else // ???
   if(adi->bitrate)
    miis->timemsec=(float)frp->filesize*8.0/(float)adi->bitrate;
 }

 if(adi->infobits&ADI_FLAG_FLOATOUT)
  adi->bytespersample=sizeof(PCM_CV_TYPE_F);
 else
  if(!adi->bytespersample)
   adi->bytespersample=(adi->bits+7) >> 3;
}

#ifdef MPXPLAY_LINK_VIDEO
void mpxplay_infile_video_config_open(struct mpxplay_videoout_info_s *voi,struct mpxplay_video_decoder_info_s *vdi)
{
 if(videocontrol&MPXPLAY_VIDEOCONTROL_DECODEVIDEO)
  funcbit_enable(vdi->flags,VDI_CNTRLBIT_DECODEVIDEO);
 vdi->picture=voi->screen_linear_ptr; // !!!
 vdi->screen_res_x=voi->screen_res_x;
 vdi->screen_res_y=voi->screen_res_y;
}
#endif

static unsigned long infile_get_header_autodetect(struct mpxpframe_s *frp,struct mpxplay_diskdrive_data_s *mdds,char *filename,int extfound,mpxp_uint32_t openmode)
{
 int i,retcode;

 for(i=0;i<=LAST_INFILE_FUNCNUM;i++){
  if((i!=extfound) && all_infile_funcs[i]->open){
   mpxplay_infile_close(frp);
   frp->mdds=mdds;
   frp->infile_funcs=all_infile_funcs[i];
   mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"chkaut: %4.4s ",frp->infile_funcs->file_extensions[0]);
   if(frp->infile_funcs->own_filehand_funcs)
    frp->filehand_funcs=frp->infile_funcs->own_filehand_funcs;
   retcode=frp->infile_funcs->open(frp->filebuf_funcs,frp,filename,frp->infile_infos,(openmode|MPXPLAY_INFILE_OPENMODE_AUTODETECT));
   switch(retcode){
    case MPXPLAY_ERROR_INFILE_FILEOPEN:goto err_out_autodetect;
    case MPXPLAY_ERROR_INFILE_OK:
     mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"infile: %4.4s ",frp->infile_funcs->file_extensions[0]);
     if(mpxplay_decoders_open(frp->infile_infos,frp)==MPXPLAY_ERROR_INFILE_OK){
      mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"decopen: %4.4s ",frp->infile_funcs->file_extensions[0]);
      miis_to_frp(frp->infile_infos,frp); // !!!
      return 1;
     }
   }
  }
 }
err_out_autodetect:
 mpxplay_infile_close(frp);
 return 0;
}

static unsigned int infile_checkhead_by_ext(struct mpxpframe_s *frp,struct mpxplay_diskdrive_data_s *mdds,struct mpxplay_infile_func_s *infilefuncs,char *filename,char *extension,unsigned int *extfound,mpxp_uint32_t openmode)
{
 unsigned int j=0;
 do{
  if(!infilefuncs->file_extensions[0] || pds_stricmp(infilefuncs->file_extensions[j],extension)==0){
   if(infilefuncs->file_extensions[j])
    *extfound=1;
   if(infilefuncs->open){
    mpxplay_infile_close(frp);
    frp->mdds=mdds;
    frp->infile_funcs=infilefuncs;
    if(frp->infile_funcs->own_filehand_funcs)
     frp->filehand_funcs=frp->infile_funcs->own_filehand_funcs;
    if(frp->infile_funcs->open(frp->filebuf_funcs,frp,filename,frp->infile_infos,openmode)==MPXPLAY_ERROR_INFILE_OK){
     mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"in oke: %4.4s ",frp->infile_funcs->file_extensions[j]);
     if(mpxplay_decoders_open(frp->infile_infos,frp)==MPXPLAY_ERROR_INFILE_OK){
      miis_to_frp(frp->infile_infos,frp); // !!!
      mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"decoder oke");
      return 1;
     }
    }
   }
   break;
  }
 }while(infilefuncs->file_extensions[++j]);
 mpxplay_infile_close(frp);
 mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"in fail: %4.4s extfound:%d",extension,*extfound);
 return 0;
}

unsigned long mpxplay_infile_get_header_by_ext(struct mpxpframe_s *frp,struct mpxplay_diskdrive_data_s *mdds,char *filename,mpxp_uint32_t openmode)
{
 unsigned int i, retry=2, extfound=0;
 int ext_infile_funcnum = -1;
 char *extension;

 mpxplay_infile_close(frp);
 frp->mdds = mdds;

 extension = pds_strrchr(filename,'.');
 if(extension)
  extension++;

 do{
  if(extension && extension[0]){

#ifdef MPXPLAY_LINK_DLLLOAD // !!! dll can override internal file parser
   // check DLLs (first)
   i=0;
   do{
    mpxplay_module_entry_s *dll_infile=NULL;
    do{
     dll_infile=newfunc_dllload_getmodule(infiledlltypes[i].dlltype,0,NULL,dll_infile);
     if(dll_infile){
      if(dll_infile->module_structure_version==infiledlltypes[i].modulever){ // !!!
       if(infile_checkhead_by_ext(frp,mdds,(struct mpxplay_infile_func_s *)dll_infile->module_callpoint,filename,extension,&extfound,openmode)){
        funcbit_enable(frp->filetype,HFT_FILE_DLL);
        return 1;
       }
       if(extfound)
        break;
      }
     }
    }while(dll_infile);
    i++;
   }while(infiledlltypes[i].dlltype);
   if(!extfound)
#endif
    //check builtin infiles (last)
    for(i=0;i<=LAST_INFILE_FUNCNUM;i++){
     if(infile_checkhead_by_ext(frp,mdds,all_infile_funcs[i],filename,extension,&extfound,openmode))
      return 1;
     if(extfound){
      ext_infile_funcnum=i;
      break;
     }
    }
  }

  if(!(--retry))
   break;

  if(!extfound){ // try to get file extension (content-type) from low level driver (ie:http)
   void *mdfd = mpxplay_diskdrive_file_open(mdds,filename,O_RDONLY|O_BINARY);
   if(mdfd){
    extension = NULL;
    mpxplay_diskdrive_file_config(mdfd,MPXPLAY_DISKFILE_CFGFUNCNUM_GET_CONTENTTYPEEXT,&extension,NULL);
    mpxplay_diskdrive_file_close(mdfd);
   }
  }

 }while(1);

 if((!extfound && !funcbit_test(desktopmode,DTM_EDIT_ALLFILES)) || (mpxplay_programcontrol&MPXPLAY_PROGRAMC_AUTODETECTALWAYS))  // unknown or missing file extension
  if(infile_get_header_autodetect(frp,mdds,filename,ext_infile_funcnum,openmode))
   return 1;

 return 0;
}

static void infile_assign_funcs(struct mpxpframe_s *frp)
{
 if(frp->infile_funcs){
  /*if(frp->filetype&HFT_FILE_DLL){
   mpxplay_module_entry_s *dll_infile=(mpxplay_module_entry_s *)frp->infile_funcs;
   if(newfunc_dllload_reloadmodule(dll_infile))
    frp->infile_funcs=(struct mpxplay_infile_func_s *)dll_infile->module_callpoint;
   else
    frp->infile_funcs=NULL;
  }
  if(frp->infile_funcs)*/
   if(frp->infile_funcs->own_filehand_funcs)
    frp->filehand_funcs=frp->infile_funcs->own_filehand_funcs;
 }
}

int mpxplay_infile_get_id3tag(struct mpxpframe_s *frp)
{
 struct mpxplay_filehand_buffered_func_s *fbfs=frp->filebuf_funcs;
 struct mpxplay_infile_info_s *miis=frp->infile_infos;
 int retcode=MPXPLAY_ERROR_OK;

 infile_assign_funcs(frp);
 if(frp->infile_funcs){
  miis->standard_id3tag_support=frp->infile_funcs->flags&MPXPLAY_TAGTYPE_FUNCMASK;
  if(!(frp->filetype&(HFT_DFT|HFT_STREAM)) && MPXPLAY_TAGTYPE_GET_SUPPORT(miis->standard_id3tag_support)){ // common/standard id3tag formats
   if(MPXPLAY_TAGTYPE_GET_SUPPORT(miis->standard_id3tag_support)&MPXPLAY_TAGTYPE_ID3V2){
    if(mpxplay_tagging_id3v2_check(fbfs,frp)){
     retcode=mpxplay_tagging_id3v2_get(fbfs,frp,miis);
     MPXPLAY_TAGTYPE_SET_FOUND(miis->standard_id3tag_support,MPXPLAY_TAGTYPE_ID3V2);
    }
   }
   if(MPXPLAY_TAGTYPE_GET_SUPPORT(miis->standard_id3tag_support)&MPXPLAY_TAGTYPE_ID3V1){
    if(mpxplay_tagging_id3v1_check(fbfs,frp)){
     retcode=mpxplay_tagging_id3v1_get(fbfs,frp,miis);
     MPXPLAY_TAGTYPE_SET_FOUND(miis->standard_id3tag_support,MPXPLAY_TAGTYPE_ID3V1);
    }
   }
   if((MPXPLAY_TAGTYPE_GET_SUPPORT(miis->standard_id3tag_support)&MPXPLAY_TAGTYPE_APETAG) && !(MPXPLAY_TAGTYPE_GET_FOUND(miis->standard_id3tag_support)&MPXPLAY_TAGTYPE_ID3V1)){
    if(mpxplay_tagging_apetag_check(fbfs,frp)){
     retcode=mpxplay_tagging_apetag_get(fbfs,frp,miis);
     MPXPLAY_TAGTYPE_SET_FOUND(miis->standard_id3tag_support,MPXPLAY_TAGTYPE_APETAG);
    }
   }
  }else{
   if(frp->infile_funcs->get_id3tag)
    frp->infile_funcs->get_id3tag(frp->filebuf_funcs,frp,frp->infile_infos);
  }
 }
 return retcode;
}

int mpxplay_infile_write_id3tag(struct mpxpframe_s *frp,char *filename,char **id3ip,unsigned long control)
{
 struct mpxplay_filehand_buffered_func_s *fbfs=frp->filebuf_funcs;
 struct mpxplay_infile_info_s *miis=frp->infile_infos;
 struct playlist_entry_info *pei=frp->pei;
 int error=MPXPLAY_ERROR_INFILE_CANTOPEN;
 unsigned int i,is_id3v1,is_id3v2,is_apetag;
 int id3v1_retcode,id3v2_retcode,apetag_retcode;

 if(frp->filetype&(HFT_DFT|HFT_STREAM))
  return MPXPLAY_ERROR_INFILE_WRITETAG_FILETYPE;

 infile_assign_funcs(frp);

 if(frp->infile_funcs){
  miis->standard_id3tag_support=frp->infile_funcs->flags&MPXPLAY_TAGTYPE_FUNCMASK;

  if(frp->infile_funcs->write_id3tag || MPXPLAY_TAGTYPE_GET_SUPPORT(miis->standard_id3tag_support)){

   pds_fileattrib_reset(filename,_A_RDONLY);

   for(i=0;i<=I3I_MAX;i++)
    if(id3tagset[i])
     playlist_editlist_add_id3_one(NULL,pei,i,id3tagset[i],-1);
     //id3ip[i]=id3tagset[i];

   //if(!id3ip[I3I_YEAR])
   // playlist_editlist_add_id3_one(NULL,pei,I3I_YEAR,mpxplay_tag_year,-1);
   // id3ip[I3I_YEAR]=&mpxplay_tag_year[0];

   if(MPXPLAY_TAGTYPE_GET_SUPPORT(miis->standard_id3tag_support)){ // common/standard id3tag write

    if(!fbfs->fopen(frp,filename,O_RDWR|O_BINARY,0))
     return error;

    is_id3v1=0;is_id3v2=0;is_apetag=0;id3v1_retcode=0;id3v2_retcode=0;apetag_retcode=0;

    if(MPXPLAY_TAGTYPE_GET_SUPPORT(miis->standard_id3tag_support)&MPXPLAY_TAGTYPE_ID3V2){
     if(mpxplay_tagging_id3v2_check(fbfs,frp)){
      id3v2_retcode=mpxplay_tagging_id3v2_put(fbfs,frp,miis,id3ip,control);
      if(id3v2_retcode==MPXPLAY_ERROR_INFILE_OK) // !!!
       is_id3v2=1;                               // hack to put id3v1 if v2 failed
     }
    }
    if(MPXPLAY_TAGTYPE_GET_SUPPORT(miis->standard_id3tag_support)&MPXPLAY_TAGTYPE_ID3V1){
     if(mpxplay_tagging_id3v1_check(fbfs,frp)){
      id3v1_retcode=mpxplay_tagging_id3v1_put(fbfs,frp,miis);
      is_id3v1=1;
     }
    }
    if(MPXPLAY_TAGTYPE_GET_SUPPORT(miis->standard_id3tag_support)&MPXPLAY_TAGTYPE_APETAG){
     if(mpxplay_tagging_apetag_check(fbfs,frp)){
      apetag_retcode=mpxplay_tagging_apetag_put(fbfs,frp,miis);
      is_apetag=1;
     }
    }
    if(!is_id3v1 && !is_id3v2 && !is_apetag){ // create new tag
     switch(MPXPLAY_TAGTYPE_GET_PRIMARY(miis->standard_id3tag_support)){
      case MPXPLAY_TAGTYPE_ID3V1 :id3v1_retcode=mpxplay_tagging_id3v1_put(fbfs,frp,miis);break;
      case MPXPLAY_TAGTYPE_ID3V2 :id3v2_retcode=mpxplay_tagging_id3v2_put(fbfs,frp,miis,id3ip,control);break;
      case MPXPLAY_TAGTYPE_APETAG:apetag_retcode=mpxplay_tagging_apetag_put(fbfs,frp,miis);break;
      default:error=MPXPLAY_ERROR_INFILE_WRITETAG_FILETYPE;
     }
    }

    error=min(id3v1_retcode,id3v2_retcode);
    error=min(error,apetag_retcode);

    fbfs->fclose(frp);
   }else{
    error=frp->infile_funcs->write_id3tag(fbfs,frp,frp->infile_infos,filename,control);
   }

  }else{
   error=MPXPLAY_ERROR_INFILE_WRITETAG_FILETYPE;
  }
 }

 return error;
}

//--------------------------------------------------------------------------
static void clear_infile_infos(struct mpxplay_infile_info_s *miis,struct mpxpframe_s *frp)
{
 if(miis){ // it can be NULL at 'soundcard init failed'
  miis->filesize=0;
  miis->timemsec=0;
  miis->allframes=1;
  miis->private_data=NULL;
  miis->longname=NULL;
  miis->seektype=0;
  miis->standard_id3tag_support=0;

  miis->audio_decoder_funcs=NULL;
  miis->video_decoder_funcs=NULL;

  mpxplay_decoders_clear(miis,frp);
 }
}

static void clear_frame(struct mpxpframe_s *frp)
{
 frp->filetype=0;
 frp->filesize=0;
 frp->filepos=0;
 frp->frameNum=0;
 frp->framecounter=0;
 frp->index_start=0;
 frp->index_end=0;
 frp->index_len=1;      //
 frp->allframes=1;      // division by zero bugfix
 frp->timesec=0;

 frp->buffertype=0;
 frp->prebuffergetp=0;
 frp->prebufferputp=0;
 frp->prebufferbytes_rewind=0;
 frp->prebufferbytes_forward=0;
 frp->prebuffer_seek_retry=PREBUFFER_SEEKRETRY_INVALID;

 frp->mdds=NULL;
 frp->filehand_funcs=NULL;
 frp->filehand_datas=NULL;

 frp->infile_funcs=NULL;

 frp->pcmdec_storedsamples=0;
 frp->pcmout_storedsamples=0;
}

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

int mpxplay_infile_open(struct mpxpframe_s *frp,char *filename,mpxp_uint32_t openmode)
{
 infile_assign_funcs(frp);

 if(frp->infile_funcs && frp->infile_funcs->open && filename && frp->filebuf_funcs && frp->infile_infos){
  if(frp->infile_funcs->open(frp->filebuf_funcs,frp,filename,frp->infile_infos,openmode)==MPXPLAY_ERROR_INFILE_OK){
   if(mpxplay_decoders_open(frp->infile_infos,frp)==MPXPLAY_ERROR_INFILE_OK){
    miis_to_frp(frp->infile_infos,frp); // !!!
    return 1;
   }
  }
  mpxplay_infile_close(frp);
 }
 return 0;
}

int mpxplay_infile_avfile_open(struct mpxpframe_s *frp,char *filename,mpxp_uint32_t openmode,unsigned long framenum)
{
 if(mpxplay_infile_open(frp,filename,openmode)){
  if(framenum){
   if(mpxplay_infile_fseek(frp,framenum)<0)
    mpxplay_infile_fseek(frp,0);
   else
    frp->infile_infos->seektype=MPX_SEEKTYPE_NORM;
  }
  AU_setrate(mvps.aui,frp->infile_infos->audio_decoder_infos);
  if((mpxplay_decoders_alloc(frp,1)==MPXPLAY_ERROR_INFILE_OK) && MIXER_configure(&mvps.mmi,mvps.aui,frp)){
   //MIXER_allfuncinit_reinit();
   return 1;
  }
 }
 return 0;
}

void mpxplay_infile_reset(struct mpxpframe_s *frp)
{
 funcbit_smp_value_put(frp->infile_infos->seektype, 0);
 funcbit_smp_value_put(frp->prebufferbytes_forward, 0);
 funcbit_smp_value_put(frp->prebufferbytes_rewind, 0);
}

void mpxplay_infile_suspend_close(struct mpxpframe_s *frp)
{
 mpxplay_timer_deletefunc(mpxplay_infile_close,frp);
 mpxplay_infile_reset(frp);
 if(frp->infile_funcs && frp->infile_funcs->close)
  frp->infile_funcs->close(frp->filebuf_funcs,frp,frp->infile_infos);
 mpxplay_decoders_close(frp->infile_infos,frp);
 clear_infile_infos(frp->infile_infos,frp);
}

void mpxplay_infile_close(struct mpxpframe_s *frp)
{
 mpxplay_infile_suspend_close(frp);
 clear_frame(frp);
}

static int infile_subdecode(struct mpxpframe_s *frp) // demux (AVI,ASF) or direct decoding (APE,WAV)
{
 int retcode=MPXPLAY_ERROR_INFILE_EOF;

 if(frp->infile_funcs && frp->infile_funcs->decode){
  if(frp->infile_infos->audio_stream->bs_leftbytes)
   retcode=MPXPLAY_ERROR_INFILE_OK;
  else
   retcode=frp->infile_funcs->decode(frp->filebuf_funcs,frp,frp->infile_infos);
 }

 return retcode;
}

long mpxplay_infile_fseek(struct mpxpframe_s *frp,long framenum_set)
{
 if(frp->infile_funcs && frp->infile_funcs->fseek){
  //if(!frp->frameNum || (framenum_set<frp->frameNum))
  if((framenum_set<=frp->frameNum) || (framenum_set==frp->index_start))
   funcbit_enable(frp->infile_infos->seektype,MPX_SEEKTYPE_BACKWARD);
  framenum_set=frp->infile_funcs->fseek(frp->filebuf_funcs,frp,frp->infile_infos,framenum_set);
 }
 funcbit_disable(frp->infile_infos->seektype,MPX_SEEKTYPES_FSEEK);

 //mpxplay_decoders_setpos(frp->infile_infos,framenum_set);

 return framenum_set;
}

//-------------------------------------------------------------------------
long mpxplay_infile_control_cb(void *cb_data,unsigned long funcnum,void *argp1,void *argp2)
{
 long retcode=MPXPLAY_ERROR_OK,i;
 char *p;
 struct mpxpframe_s *frp=(struct mpxpframe_s *)cb_data,*frp_new;
 unsigned int texttype,i3index,len;
 struct playlist_side_info *psi;
 struct playlist_entry_info *pei;
 char original_filename[MAX_PATHNAMELEN],tmpfile_filename[MAX_PATHNAMELEN];
 char cnvtmp[MPXPLAY_TEXTCONV_MAX_ID3LEN/2],sout[128];

 if(!frp)
  return MPXPLAY_ERROR_CFGFUNC_INVALIDDATA;
 psi=frp->psi;
 pei=frp->pei;

 if((funcnum&MPXPLAY_CFGFUNCNUM_INFILE_FILEOPEN_FUNCMASK)==MPXPLAY_CFGFUNCNUM_INFILE_FILE_OPEN){
  if(!argp1 || !argp2)
   return MPXPLAY_ERROR_CFGFUNC_ARGUMENTMISSING;
  frp_new=(struct mpxpframe_s *)calloc(1,sizeof(struct mpxpframe_s)); // !!!
  if(!frp_new)
   return MPXPLAY_ERROR_FILEHAND_MEMORY;
  frp_new->mdds=frp->mdds;
  frp_new->psi=frp->psi;
  if(!(funcnum&(O_WRONLY|O_RDWR|O_CREAT|O_APPEND|O_TRUNC)))
   mpxplay_mpxinbuf_alloc_ringbuffer(NULL,frp_new,PREBUFFERBLOCKS_SHORTRING);
  if(mpxplay_mpxinbuf_fopen(frp_new,(char *)argp2,(funcnum&(~MPXPLAY_CFGFUNCNUM_INFILE_FILEOPEN_FUNCMASK)),0)){
   *((void **)argp1)=(void *)frp_new; // !!!
   frp_new->pei=(struct playlist_entry_info *)calloc(1,sizeof(struct playlist_entry_info));
   if(frp_new->pei){
    frp_new->pei->filename=malloc(MAX_PATHNAMELEN);
    pds_strcpy(frp_new->pei->filename,(char *)argp2);
   }
   if(!(funcnum&(O_WRONLY|O_RDWR|O_CREAT|O_APPEND|O_TRUNC)))
    mpxplay_mpxinbuf_alloc_ringbuffer(NULL,frp_new,PREBUFFERBLOCKS_SHORTRING);
  }else{
   if(frp_new->prebufferbegin)
    free(frp_new->prebufferbegin);
   free(frp_new);
   retcode=MPXPLAY_ERROR_FILEHAND_CANTOPEN;
  }
 }else if((funcnum&MPXPLAY_CFGFUNCNUM_INFILE_ENTRY_TAG_FUNCMASK)==MPXPLAY_CFGFUNCNUM_INFILE_ENTRY_TAG_GET){
  if(!argp1 || !argp2)
   return MPXPLAY_ERROR_CFGFUNC_ARGUMENTMISSING;
  if(!pei || !psi)
   return MPXPLAY_ERROR_CFGFUNC_INVALIDDATA;
  texttype=(funcnum>>4)&0x0f;
  i3index=funcnum&0x0f;
  if(i3index>I3I_MAX)
   return MPXPLAY_ERROR_CFGFUNC_INVALIDDATA;
  if(!pei->id3info[i3index])
   return MPXPLAY_ERROR_OK;
  len=*((unsigned long *)argp2);
  if(len)
   p=*((char **)argp1);
  else{
   len=MAX_STRLEN;
   p=malloc(MAX_STRLEN+8);
   if(!p)
    return MPXPLAY_ERROR_INFILE_MEMORY;
  }
  i=mpxplay_playlist_textconv_by_texttypes(MPXPLAY_TEXTCONV_TYPES_PUT(MPXPLAY_TEXTCONV_TYPE_MPXPLAY,texttype),
    pei->id3info[i3index],-1,p,len);
  if(i<=0){
   if(!(*((unsigned long *)argp2)))
    free(p);
   p=NULL;
   i=0;
  }else{
   if(!(*((unsigned long *)argp2)) && (i<len)){
    char *np=malloc(i+2);
    if(np){
     pds_memcpy(np,p,i+2);
     free(p);
     p=np;
    }
   }
  }
  *((char **)argp1)=p;
  *((long *)argp2)=i;
 }else if(((funcnum&MPXPLAY_CFGFUNCNUM_INFILE_ENTRY_TAG_FUNCMASK)==MPXPLAY_CFGFUNCNUM_INFILE_ENTRY_TAG_PUT)
       || ((funcnum&MPXPLAY_CFGFUNCNUM_INFILE_ENTRY_TAG_FUNCMASK)==MPXPLAY_CFGFUNCNUM_INFILE_ENTRY_TAG_UPDATE)
 ){
  if(!argp1 || !argp2)
   return MPXPLAY_ERROR_CFGFUNC_ARGUMENTMISSING;
  if(!pei || !psi)
   return MPXPLAY_ERROR_CFGFUNC_INVALIDDATA;
  texttype=(funcnum>>4)&0x0f;
  i3index=funcnum&0x0f;
  if(i3index>I3I_MAX)
   return MPXPLAY_ERROR_CFGFUNC_INVALIDDATA;
  if(pei->id3info[i3index] && ((funcnum&MPXPLAY_CFGFUNCNUM_INFILE_ENTRY_TAG_FUNCMASK)==MPXPLAY_CFGFUNCNUM_INFILE_ENTRY_TAG_UPDATE))
   playlist_editlist_del_id3_one(psi,pei,i3index);
  if(!pei->id3info[i3index]){
   i=mpxplay_playlist_textconv_by_texttypes(MPXPLAY_TEXTCONV_TYPES_PUT(texttype,MPXPLAY_TEXTCONV_TYPE_MPXPLAY),
     (char*)argp1,*((long *)argp2),cnvtmp,sizeof(cnvtmp)-2);
   if(i>0){
    i=pds_str_clean(cnvtmp);
    if(i)
     playlist_editlist_add_id3_one(psi,pei,i3index,cnvtmp,i);
   }
  }
 }else{
  switch(funcnum){
   case MPXPLAY_CFGFUNCNUM_INFILE_ENTRY_TAGS_CLEAR:
    if(!pei || !psi)
     return MPXPLAY_ERROR_CFGFUNC_INVALIDDATA;
    playlist_editlist_del_id3_all(psi,pei);
    break;
   /*case MPXPLAY_CFGFUNCNUM_INFILE_ENTRY_TAGS_RELOAD:
    if(!pei || !psi)
     return MPXPLAY_ERROR_CFGFUNC_INVALIDDATA;
    playlist_chkentry_load_id3tag_from_file(psi,pei,frp,loadid3tag,0);
    //playlist_pei0_set(psi->mvp,pei,EDITLIST_MODE_ID3);
    break;*/
   case MPXPLAY_CFGFUNCNUM_INFILE_FILE_CLOSE:
    if(!argp1)
     return MPXPLAY_ERROR_CFGFUNC_ARGUMENTMISSING;
    frp=(struct mpxpframe_s *)*((void **)argp1);   // !!!
    if(frp){
     if(frp->filebuf_funcs)
      frp->filebuf_funcs->fclose(frp);
     if(frp->prebufferbegin)
      free(frp->prebufferbegin);
     if(frp->pei){
      if(frp->pei->filename)
       free(frp->pei->filename);
      free(frp->pei);
     }
     free(frp); // !!!
     *((void **)argp1)=NULL;
    }
    break;
   case MPXPLAY_CFGFUNCNUM_INFILE_FILE_SEEK:
    if(!argp1 || !argp2)
     return MPXPLAY_ERROR_CFGFUNC_ARGUMENTMISSING;
    if(!frp->filebuf_funcs)
     return MPXPLAY_ERROR_CFGFUNC_INVALIDDATA;
    return frp->filebuf_funcs->fseek(frp,*((mpxp_int64_t *)argp1),*((int *)argp2));
   case MPXPLAY_CFGFUNCNUM_INFILE_FILE_TELL:
    if(!argp1)
     return MPXPLAY_ERROR_CFGFUNC_ARGUMENTMISSING;
    if(!frp->filebuf_funcs)
     return MPXPLAY_ERROR_CFGFUNC_INVALIDDATA;
    *((mpxp_int64_t *)argp1)=frp->filebuf_funcs->ftell(frp);
    break;
   case MPXPLAY_CFGFUNCNUM_INFILE_FILE_LENGTH:
    if(!argp1)
     return MPXPLAY_ERROR_CFGFUNC_ARGUMENTMISSING;
    if(!frp->filebuf_funcs)
     return MPXPLAY_ERROR_CFGFUNC_INVALIDDATA;
    *((mpxp_int64_t *)argp1)=frp->filebuf_funcs->filelength(frp);
    break;
   case MPXPLAY_CFGFUNCNUM_INFILE_FILE_EOF:
    if(!frp->filebuf_funcs)
     return MPXPLAY_ERROR_CFGFUNC_INVALIDDATA;
    return frp->filebuf_funcs->eof(frp);
   case MPXPLAY_CFGFUNCNUM_INFILE_FILE_CHSIZE:
    if(!argp1)
     return MPXPLAY_ERROR_CFGFUNC_ARGUMENTMISSING;
    if(!frp->filebuf_funcs)
     return MPXPLAY_ERROR_CFGFUNC_INVALIDDATA;
    return frp->filebuf_funcs->chsize(frp,*((mpxp_int64_t *)argp1));
   case MPXPLAY_CFGFUNCNUM_INFILE_FILE_READ_BYTES:
    if(!argp1 || !argp2)
     return MPXPLAY_ERROR_CFGFUNC_ARGUMENTMISSING;
    if(!frp->filebuf_funcs)
     return MPXPLAY_ERROR_CFGFUNC_INVALIDDATA;
    return frp->filebuf_funcs->fread(frp,argp1,*(unsigned long *)argp2);
   case MPXPLAY_CFGFUNCNUM_INFILE_FILE_WRITE_BYTES:
    if(!argp1 || !argp2)
     return MPXPLAY_ERROR_CFGFUNC_ARGUMENTMISSING;
    if(!frp->filebuf_funcs)
     return MPXPLAY_ERROR_CFGFUNC_INVALIDDATA;
    return frp->filebuf_funcs->fwrite(frp,argp1,*(unsigned long *)argp2);
   case MPXPLAY_CFGFUNCNUM_INFILE_FILE_RENAME:
    if(!argp1 || !argp2)
     return MPXPLAY_ERROR_CFGFUNC_ARGUMENTMISSING;
    if(!frp->mdds)
     return MPXPLAY_ERROR_CFGFUNC_INVALIDDATA;
    retcode=mpxplay_diskdrive_rename(frp->mdds,(char *)argp1,(char *)argp2);
    break;
   case MPXPLAY_CFGFUNCNUM_INFILE_FILE_UNLINK:
    if(!argp1)
     return MPXPLAY_ERROR_CFGFUNC_ARGUMENTMISSING;
    if(!frp->mdds)
     return MPXPLAY_ERROR_CFGFUNC_INVALIDDATA;
    retcode=mpxplay_diskdrive_unlink(frp->mdds,(char *)argp1);
    break;
   case MPXPLAY_CFGFUNCNUM_INFILE_FILE_COPY:
   {
    char *copybuf;
    struct mpxpframe_s *frp_dest;
    struct mpxplay_filehand_buffered_func_s *fbfs;
    mpxp_int32_t last_percent;
    mpxp_filesize_t beginpos,copylen,filesize_src,copypos;
    if(!argp1 || !argp2)
     return MPXPLAY_ERROR_CFGFUNC_ARGUMENTMISSING;
    fbfs=frp->filebuf_funcs;
    if(!fbfs){
     retcode=MPXPLAY_ERROR_CFGFUNC_INVALIDDATA;
     break;
    }
    frp_dest=(struct mpxpframe_s *)argp1;
    beginpos=fbfs->ftell(frp);
    copylen=*((mpxp_int64_t *)argp2);
    filesize_src=fbfs->filelength(frp);
    if((copylen<0) || (copylen>(filesize_src-beginpos)))
     copylen=filesize_src-beginpos;
    if(!copylen){
     retcode=MPXPLAY_ERROR_INFILE_EOF;
     break;
    }
    copybuf=malloc(65536+32);
    if(!copybuf){
     retcode=MPXPLAY_ERROR_INFILE_MEMORY;
     break;
    }
    copypos=beginpos;
    last_percent=-1;
    if(frp->pei)
     display_message(1,0,pds_getfilename_from_fullname(frp->pei->filename));
    do{
     long blocksize,i,percent;
     percent=(long)(100.0*(float)copypos/(float)filesize_src);
     if(percent!=last_percent){
      sprintf(sout,"Duplicating file (%2d%%) ... (ESC to abort)",percent);
      display_message(0,0,sout);
      last_percent=percent;
      if(pds_look_extgetch()==KEY_ESC){
       pds_extgetch();
       retcode=MPXPLAY_ERROR_FILEHAND_USERABORT;
       break;
      }
     }
     if(copylen>65536)
      blocksize=65536;
     else
      blocksize=copylen;
     i=fbfs->fread(frp,copybuf,blocksize);
     //if(i!=blocksize){
     if((i<=0) || (i>blocksize)){
      if(i<0)
       retcode=i;
      else
       retcode=MPXPLAY_ERROR_INFILE_EOF;
      break;
     }
     blocksize=i;
     i=fbfs->fwrite(frp_dest,copybuf,blocksize);
     if(i!=blocksize){
      if(i<0)
       retcode=i;
      else
       retcode=MPXPLAY_ERROR_FILEHAND_CANTWRITE;
      break;
     }
     copylen-=blocksize;
     copypos+=blocksize;
    }while(copylen);
    clear_message();
    free(copybuf);
    break;
   }
   case MPXPLAY_CFGFUNCNUM_INFILE_FILE_TMPOPEN:
    if(!argp1)
     return MPXPLAY_ERROR_CFGFUNC_ARGUMENTMISSING;
    if(!frp->mdds || !frp->pei || !frp->pei->filename)
     return MPXPLAY_ERROR_CFGFUNC_INVALIDDATA;
    pds_getpath_from_fullname(tmpfile_filename,frp->pei->filename);
    i=pds_strlen(tmpfile_filename);
    if(tmpfile_filename[i-1]!=PDS_DIRECTORY_SEPARATOR_CHAR)
     i+=pds_strcpy(&tmpfile_filename[i],PDS_DIRECTORY_SEPARATOR_STR);
    pds_strcpy(&tmpfile_filename[i],"MPXPLAY.TMP");
    mpxplay_diskdrive_unlink(frp->mdds,tmpfile_filename);
    retcode=mpxplay_infile_control_cb(cb_data,MPXPLAY_CFGFUNCNUM_INFILE_FILE_OPEN|O_CREAT|O_TRUNC|O_RDWR|O_BINARY,argp1,tmpfile_filename);
    break;
   case MPXPLAY_CFGFUNCNUM_INFILE_FILE_TMPXCHCLOSE:
    {
    struct mpxpframe_s *frp0=NULL;
    //unsigned int playc_save=playcontrol;
    if(!argp1)
     return MPXPLAY_ERROR_CFGFUNC_ARGUMENTMISSING;
    if(!frp->mdds || !frp->pei || !frp->filebuf_funcs)
     return MPXPLAY_ERROR_CFGFUNC_INVALIDDATA;
    pds_strcpy(original_filename,frp->pei->filename);
    frp_new=(struct mpxpframe_s *)*((void **)argp1);
    if(!frp_new->pei)
     return MPXPLAY_ERROR_CFGFUNC_INVALIDDATA;
    pds_strcpy(tmpfile_filename,frp_new->pei->filename);
    mpxplay_infile_control_cb(frp,MPXPLAY_CFGFUNCNUM_INFILE_FILE_CLOSE,argp1,NULL);
    frp->filebuf_funcs->fclose(frp);
    if(mvps.frp0->filehand_datas && (pds_utf8_stricmp(mvps.pei0->filename,original_filename)==0)){
     frp0=mvps.frp0;
     if(frp0->infile_funcs && frp0->filebuf_funcs && frp0->infile_infos){
      AU_suspend_decoding(mvps.aui);
      //AU_stop(mvps.aui);
      //mpxplay_infile_suspend_close(frp0);
      frp0->infile_funcs->close(frp0->filebuf_funcs,frp0,frp0->infile_infos);
      mpxplay_infile_reset(frp0);
      frp0->filepos=0;
     }else
      frp0=NULL;
    }
    retcode=mpxplay_diskdrive_unlink(frp->mdds,original_filename);
    if(retcode==MPXPLAY_ERROR_OK)
     retcode=mpxplay_diskdrive_rename(frp->mdds,tmpfile_filename,original_filename);
    else
     mpxplay_diskdrive_unlink(frp->mdds,tmpfile_filename);
    if(frp0){
     /*if(mpxplay_infile_avfile_open(frp0,original_filename,MPXPLAY_INFILE_OPENMODE_PLAY,frp0->frameNum))
      if(playc_save&PLAYC_RUNNING)
       AU_prestart(mvps.aui);*/
     if(frp0->infile_funcs->open(frp0->filebuf_funcs,frp0,original_filename,frp0->infile_infos,MPXPLAY_INFILE_OPENMODE_PLAY)==MPXPLAY_ERROR_INFILE_OK){
      mpxplay_infile_fseek(frp0,frp0->frameNum);
      mpxplay_mpxinbuf_buffer_check(frp0);
      AU_resume_decoding(mvps.aui);
      mvps.adone=mvps.fdone=0;
     }
    }
    break;
    }
   case MPXPLAY_CFGFUNCNUM_INFILE_ENTRY_FINAME_GET:
    if(!argp1 || !argp2)
     return MPXPLAY_ERROR_CFGFUNC_ARGUMENTMISSING;
    if(!frp->pei)
     return MPXPLAY_ERROR_CFGFUNC_INVALIDDATA;
    i=*((unsigned long *)argp2);
    if(!i)
     return MPXPLAY_ERROR_CFGFUNC_INVALIDDATA;
    p=(char *)argp1;
    pds_strncpy(p,frp->pei->filename,i-1);
    p[i-1]=0;
    break;
   case MPXPLAY_CFGFUNCNUM_INFILE_ENTRY_ISSEEKABLE:
    if((frp->filetype&HFT_STREAM) || (frp->buffertype&PREBUFTYPE_NON_SEEKABLE))
     return 0;
    return 1;
   case MPXPLAY_CFGFUNCNUM_INFILE_ENTRY_ISSTREAM:
    if(frp->filetype&HFT_STREAM)
     return 1;
    return 0;
   default:return MPXPLAY_ERROR_CFGFUNC_UNSUPPFUNC;
  }
 }

 return retcode;
}

//-------------------------------------------------------------------------
static unsigned int infile_initialized;

void mpxplay_infile_init(struct mainvars *mvp)
{
 struct mpxpframe_s *frp=mvp->frp0;
 unsigned int i=0;

 do{
  struct mpxplay_infile_info_s *miis=&infile_infos[i];
  frp->infile_infos=miis;
  miis->control_cb=&mpxplay_infile_control_cb;
  miis->ccb_data=frp;
  miis->textconv_func=&mpxplay_playlist_textconv_by_texttypes;
  miis->audio_stream=&audiopacket_infos[i];
  miis->audio_stream->stream_select=stream_select_audio;
  miis->audio_decoder_infos=&audiodecoder_infos[i];
#ifdef MPXPLAY_LINK_VIDEO
  miis->video_stream=&videopacket_infos[i];
  miis->video_decoder_infos=&videodecoder_infos[i];
#endif
  mpxplay_decoders_alloc(frp,((i<2)? 1:0));
  clear_infile_infos(miis,frp);
  frp++;
  i++;
 }while(i<3);

 for(i=0;i<=LAST_INFILE_FUNCNUM;i++){
  if(all_infile_funcs[i]->preinit)
   all_infile_funcs[i]->preinit();
 }

 mpxplay_decoders_preinit();

 //i=pds_getdate();
 //sprintf(&mpxplay_tag_year[0],"%4d/%2.2d",i>>16,(i>>8)&0xff);
 infile_initialized=1;
}

void mpxplay_infile_deinit(void)
{
 unsigned int i;

 if(!infile_initialized)
  return;

 for(i=0;i<2;i++){
  mpxplay_infile_close(&fr[i]);
  mpxplay_decoders_free(fr[i].infile_infos,&fr[i]);
 }

 for(i=0;i<=LAST_INFILE_FUNCNUM;i++){
  if(all_infile_funcs[i]->deinit)
   all_infile_funcs[i]->deinit();
 }

 mpxplay_decoders_deinit();
}

// allocates a non-static frame (file/decoder) info structure (!!! clear the temporary (stack) frp field before calling)
struct mpxpframe_s *mpxplay_infile_frame_alloc(struct mpxpframe_s *frp)
{
 struct mpxplay_infile_info_s *miis;

 frp->infile_funcs = NULL;

 mpxplay_mpxinbuf_assign_funcs(frp);
 if(!mpxplay_mpxinbuf_alloc_ringbuffer(NULL,frp,PREBUFFERBLOCKS_SHORTRING))
  goto err_out_fa;

 if(!frp->infile_infos){
  miis=(struct mpxplay_infile_info_s *)calloc(1,sizeof(*frp->infile_infos));
  if(!miis)
   goto err_out_fa;
  frp->infile_infos=miis;
 }else
  miis=frp->infile_infos;

 miis->control_cb=&mpxplay_infile_control_cb;
 miis->ccb_data=frp;
 miis->textconv_func=&mpxplay_playlist_textconv_by_texttypes;
 if(!miis->audio_stream){
  miis->audio_stream=(struct mpxplay_streampacket_info_s *)calloc(1,sizeof(*miis->audio_stream));
  if(!miis->audio_stream)
   goto err_out_fa;
 }

 miis->audio_stream->stream_select=stream_select_audio;
 if(!miis->audio_decoder_infos){
  miis->audio_decoder_infos=(struct mpxplay_audio_decoder_info_s *)calloc(1,sizeof(*miis->audio_decoder_infos));
  if(!miis->audio_decoder_infos)
   goto err_out_fa;
 }

#ifdef MPXPLAY_LINK_VIDEO
 if(!miis->video_stream){
  miis->video_stream=(struct mpxplay_streampacket_info_s *)calloc(1,sizeof(*miis->video_stream));
  if(!miis->video_stream)
   goto err_out_fa;
 }
 if(!miis->video_decoder_infos){
  miis->video_decoder_infos=(struct mpxplay_video_decoder_info_s *)calloc(1,sizeof(*miis->video_decoder_infos));
  if(!miis->video_decoder_infos)
   goto err_out_fa;
 }
#endif
 mpxplay_decoders_alloc(frp,1);

 clear_infile_infos(miis,frp);

 return frp;

err_out_fa:
 mpxplay_infile_frame_free(frp);
 return NULL;
}

/*struct mpxpframe_s *mpxplay_infile_frame_alloc(struct mpxpframe_s *frp)
{
 struct mpxplay_infile_info_s *miis;

 pds_memset(frp,0,sizeof(struct mpxpframe_s));
 mpxplay_mpxinbuf_assign_funcs(frp);
 if(!mpxplay_mpxinbuf_alloc_ringbuffer(NULL,frp,PREBUFFERBLOCKS_SHORTRING))
  goto err_out_fa;
 miis=(struct mpxplay_infile_info_s *)calloc(1,sizeof(*frp->infile_infos));
 if(!miis)
  goto err_out_fa;
 frp->infile_infos=miis;
 miis->control_cb=&mpxplay_infile_control_cb;
 miis->ccb_data=frp;
 miis->textconv_func=&mpxplay_playlist_textconv_by_texttypes;
 miis->audio_stream=(struct mpxplay_streampacket_info_s *)calloc(1,sizeof(*miis->audio_stream));
 if(!miis->audio_stream)
  goto err_out_fa;
 miis->audio_stream->stream_select=stream_select_audio;
 miis->audio_decoder_infos=(struct mpxplay_audio_decoder_info_s *)calloc(1,sizeof(*miis->audio_decoder_infos));
 if(!miis->audio_decoder_infos)
  goto err_out_fa;
#ifdef MPXPLAY_LINK_VIDEO
 miis->video_stream=(struct mpxplay_streampacket_info_s *)calloc(1,sizeof(*miis->video_stream));
 if(!miis->video_stream)
  goto err_out_fa;
 miis->video_decoder_infos=(struct mpxplay_video_decoder_info_s *)calloc(1,sizeof(*miis->video_decoder_infos));
 if(!miis->video_decoder_infos)
  goto err_out_fa;
#endif
 mpxplay_decoders_alloc(frp,1);

 clear_infile_infos(miis,frp);

 return frp;

err_out_fa:
 mpxplay_infile_frame_free(frp);
 return NULL;
}*/

void mpxplay_infile_frame_free(struct mpxpframe_s *frp)
{
 struct mpxplay_infile_info_s *miis;
 if(frp){
  miis=frp->infile_infos;
  if(miis){
   mpxplay_infile_close(frp); // ???
   mpxplay_decoders_free(miis,frp);
   if(miis->audio_stream)
    free(miis->audio_stream);
   if(miis->audio_decoder_infos)
    free(miis->audio_decoder_infos);
#ifdef MPXPLAY_LINK_VIDEO
   if(miis->video_stream)
    free(miis->video_stream);
   if(miis->video_decoder_infos)
    free(miis->video_decoder_infos);
#endif
   free(miis);
   frp->infile_infos=NULL;
  }
  if(frp->prebufferbegin)
   free(frp->prebufferbegin);
  if(frp->pcmdec_buffer)
   free(frp->pcmdec_buffer);
  if(frp->pcmout_buffer)
   free(frp->pcmout_buffer);
  pds_memset(frp,0,sizeof(struct mpxpframe_s));
 }
}
