//**************************************************************************
//*                     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: input file buffering

#include "newfunc\newfunc.h"
#include "mpxinbuf.h"
#include "au_cards\au_cards.h"
#include "display\display.h"

//#define MPXINBUF_FOR_ALL_FILECHECK 1

static unsigned int mpxinbuf_alloc_fullbuffer(struct mainvars *mvp,struct frame *frp);
static unsigned int mpxinbuf_alloc_ringbuffer(struct mainvars *mvp,struct frame *frp,unsigned long blocks);
static unsigned int mpxinbuf_ringbuffer_fill(struct frame *);

static unsigned int mpxinbuf_lowlevel_file_chsize(struct frame *frp,long offset);

static unsigned int  mpxplay_mpxinbuf_fopen_read(void *fbds,char *filename,unsigned long pb_blocksize);
static unsigned int  mpxplay_mpxinbuf_fopen_write(void *fbds,char *filename);
static void          mpxplay_mpxinbuf_fclose(void *fbds);
static unsigned long mpxplay_mpxinbuf_fread(void *fbds,void *ptr,unsigned long num);
static long          mpxplay_mpxinbuf_fwrite(void *fbds,void *ptr,unsigned long num);
static long          mpxplay_mpxinbuf_seek_unbuffered(struct frame *frp,long newpos_absolute);
static long          mpxplay_mpxinbuf_fseek(void *fbds,long offset,int whence);
static long          mpxplay_mpxinbuf_ftell(void *fbds);
static long          mpxplay_mpxinbuf_filelength(void *fbds);
static int           mpxplay_mpxinbuf_feof(void *fbds);
static int           mpxplay_mpxinbuf_chsize(void *fbds,long offset);
static mpxp_uint32_t mpxplay_mpxinbuf_get_byte(void *fbds);
static mpxp_uint32_t mpxplay_mpxinbuf_get_le16(void *fbds);
static mpxp_uint32_t mpxplay_mpxinbuf_get_le32(void *fbds);
static mpxp_uint64_t mpxplay_mpxinbuf_get_le64(void *fbds);
static mpxp_uint32_t mpxplay_mpxinbuf_get_be16(void *fbds);
static mpxp_uint32_t mpxplay_mpxinbuf_get_be32(void *fbds);
static mpxp_uint64_t mpxplay_mpxinbuf_get_be64(void *fbds);

static struct mpxplay_filehand_buffered_func_s mpxinbuf_functions={
 &mpxplay_mpxinbuf_fopen_read,
 &mpxplay_mpxinbuf_fopen_write,
 &mpxplay_mpxinbuf_fclose,
 &mpxplay_mpxinbuf_fread,
 &mpxplay_mpxinbuf_fwrite,
 &mpxplay_mpxinbuf_fseek,
 &mpxplay_mpxinbuf_ftell,
 &mpxplay_mpxinbuf_filelength,
 &mpxplay_mpxinbuf_feof,
 &mpxplay_mpxinbuf_chsize,
 &mpxplay_mpxinbuf_get_byte,
 &mpxplay_mpxinbuf_get_le16,
 &mpxplay_mpxinbuf_get_le32,
 &mpxplay_mpxinbuf_get_le64,
 &mpxplay_mpxinbuf_get_be16,
 &mpxplay_mpxinbuf_get_be32,
 &mpxplay_mpxinbuf_get_be64,
};

extern unsigned int prebuffertype,prebufferblocks;
extern unsigned int intsoundconfig,intsoundcontrol;
extern volatile unsigned long mpxplay_signal_events;
extern volatile unsigned int playcontrol;

void mpxplay_mpxinbuf_init(struct mainvars *mvp)
{
 struct frame *frp=mvp->frp0;

 frp->filebuf_funcs=&mpxinbuf_functions;
 frp++;
 frp->filebuf_funcs=&mpxinbuf_functions;
 frp++;
 frp->filebuf_funcs=&mpxinbuf_functions;

 if(prebuffertype&PREBUFTYPE_MASK)
  mpxinbuf_alloc_ringbuffer(mvp,frp,PREBUFFERBLOCKS_SHORTRING);

 if(prebufferblocks>(0x7fffffff/PREBUFFERBLOCKSIZE_DECODE)) // int32 overflow in allocation
  prebufferblocks=0x7fffffff/PREBUFFERBLOCKSIZE_DECODE;
}

void mpxplay_mpxinbuf_prealloc(struct mainvars *mvp)
{
 if(prebuffertype&PREBUFTYPE_RING){
  unsigned int i=2;
  struct frame *frp=mvp->frp0;
  display_message(0,0,"Prebuffer memory allocation (a few seconds)");
  do{
   if(!mpxinbuf_alloc_ringbuffer(mvp,frp,prebufferblocks))
    mpxplay_close_program(MPXERROR_XMS_MEM);
   frp++;
  }while(--i);
  clear_message();
 }
}

void mpxplay_mpxinbuf_close(struct mainvars *mvp)
{
 struct frame *frp=mvp->frp0;
 unsigned int i=3;

 do{
  if(frp->prebufferbegin){
   free(frp->prebufferbegin);
   frp->prebufferbegin=NULL;
  }
  frp++;
 }while(--i);
}

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

unsigned int mpxplay_mpxinbuf_alloc(struct mainvars *mvp,struct frame *frp)
{
 unsigned int ok=0;

 if(prebuffertype&PREBUFTYPE_FULL)
  ok=mpxinbuf_alloc_fullbuffer(mvp,frp);
 if((prebuffertype&PREBUFTYPE_RING) || ((prebuffertype&PREBUFTYPE_FULL) && !ok))
  ok=mpxinbuf_alloc_ringbuffer(mvp,frp,prebufferblocks);
 if(!(prebuffertype&PREBUFTYPE_MASK)){
  funcbit_enable(frp->buffertype,PREBUFTYPE_FILLED);
  ok=1;
 }
 frp->prebuffer_seek_retry=PREBUFFER_SEEKRETRY_INVALID;
 return ok;
}

#define FULLBUFFER_READ_BLOCKS 20

static unsigned int mpxinbuf_alloc_fullbuffer(struct mainvars *mvp,struct frame *frp)
{
 struct frame *frop;
 int blocksize,percent;
 char *pointer,sout[40];

 frp->buffertype=PREBUFTYPE_NONE;
 if(playcontrol&PLAYC_RUNNING){
  AU_stop(mvp->aui);
  funcbit_enable(playcontrol,PLAYC_STARTNEXT);
 }
 frop=frp->fro;
 mpxplay_infile_close(frop);
 if(frop->prebufferbegin!=NULL){
  pds_free(frop->prebufferbegin);
  frop->prebufferbegin=NULL;
  frop->buffertype=0;
 }
 if(frp->prebufferbegin!=NULL)
  pds_free(frp->prebufferbegin);
 frp->prebufferbegin=pds_malloc(frp->filesize);
 if(frp->prebufferbegin==NULL)
  return 0;
 mpxinbuf_lowlevel_file_seek(frp,0);
 blocksize=frp->filesize/FULLBUFFER_READ_BLOCKS;
 if(blocksize){
  pointer=frp->prebufferbegin;
  for(percent=0;percent<100;percent+=(100/FULLBUFFER_READ_BLOCKS)){
   if(pds_look_extgetch()==KEY_ESC)
    break;
   sprintf(sout,"Loading song : %d%%",percent);
   display_message(0,0,sout);
   mpxinbuf_lowlevel_file_read(frp,pointer,blocksize);
   pointer+=blocksize;
  }
  if(pds_look_extgetch()==KEY_ESC){
   pds_extgetch();
   pds_free(frp->prebufferbegin);
   frp->prebufferbegin=NULL;
   return 0;
  }
 }
 mpxinbuf_lowlevel_file_read(frp,pointer,frp->filesize-(FULLBUFFER_READ_BLOCKS*blocksize));
 frp->prebufferbytes_forward=frp->prebufferputp=frp->prebuffersize=frp->filesize;
 frp->buffertype=PREBUFTYPE_FULL|PREBUFTYPE_FILLED;
 return 1;
}

static unsigned int mpxinbuf_alloc_ringbuffer(struct mainvars *mvp,struct frame *frp,unsigned long blocks)
{
 frp->prebufferblocksize=PREBUFFERBLOCKSIZE_DECODE;
 frp->prebuffersize=blocks*frp->prebufferblocksize;

 if(frp->prebufferbegin==NULL){
  frp->prebufferbegin=pds_malloc(frp->prebuffersize+32);
  if(frp->prebufferbegin==NULL){
   struct frame *frop;
   if(playcontrol&PLAYC_RUNNING){
    AU_stop(mvp->aui);
    funcbit_enable(playcontrol,PLAYC_STARTNEXT);
   }
   frop=frp->fro;
   if(!frop || !frop->prebufferbegin)
    return 0;
   frp->prebufferbegin=frop->prebufferbegin;
   frp->prebuffersize =frop->prebuffersize;  // ??? have to be the same
   frop->prebufferbegin=NULL;
   frop->buffertype=0;
  }
 }
 frp->buffertype=PREBUFTYPE_RING;
 return 1;
}

void mpxplay_mpxinbuf_set_intsound(struct frame *frp,unsigned int intcfg)
{
 if(intcfg&INTSOUND_DECODER)
  funcbit_enable(frp->buffertype,PREBUFTYPE_INT);
}

unsigned int mpxplay_mpxinbuf_buffer_check(struct frame *frp) // returns 1 at eof/read-error
{
 funcbit_enable(frp->buffertype,PREBUFTYPE_FILLED);
 if(frp->prebuffer_seek_retry>=0){
  if(mpxplay_mpxinbuf_seek_unbuffered(frp,frp->prebuffer_seek_retry)<0)
   return 1;
  frp->prebuffer_seek_retry=PREBUFFER_SEEKRETRY_INVALID;
 }
 if((frp->buffertype&PREBUFTYPE_RING) && !(frp->buffertype&PREBUFTYPE_WRITEPROTECT)){
  if((frp->prebuffersize-frp->prebufferbytes_forward)>frp->prebufferblocksize){
   if(!mpxinbuf_ringbuffer_fill(frp))
    return 1;
   funcbit_disable(frp->buffertype,PREBUFTYPE_FILLED);
  }
  return 0;
 }
 return 1;
}

//------------------------------------------------------------------------
//fill prebuffer

static void check_buffer_overflow(struct frame *frp)
{
 if(frp->prebufferputp<0)
  frp->prebufferputp+=frp->prebuffersize;
 if(frp->prebufferputp<0)
  frp->prebufferputp=0;
 if(frp->prebufferputp>=frp->prebuffersize)
  frp->prebufferputp-=frp->prebuffersize;
 if(frp->prebufferputp>=frp->prebuffersize)
  frp->prebufferputp=0;
}

static unsigned int mpxinbuf_ringbuffer_fill(struct frame *frp)
{
 if(!mpxinbuf_lowlevel_file_eof(frp)){
  long i,j,outbytes;

  check_buffer_overflow(frp);

  i=frp->prebuffersize-frp->prebufferputp;
  if(i<frp->prebufferblocksize){
   j=mpxinbuf_lowlevel_file_read(frp,&frp->prebufferbegin[frp->prebufferputp],i);
   if(j<i){
    frp->prebufferputp+=j;
    i=0;
   }else{
    frp->prebufferputp=0;
    i=frp->prebufferblocksize-i;
   }
   outbytes=j;
  }else{
   i=frp->prebufferblocksize;
   outbytes=0;
  }
  if(i){
   j=mpxinbuf_lowlevel_file_read(frp,&frp->prebufferbegin[frp->prebufferputp],i);
   frp->prebufferputp+=j;
   outbytes+=j;
  }
  frp->prebufferbytes_forward+=outbytes;

  if(frp->prebufferbytes_rewind>(frp->prebuffersize-frp->prebufferbytes_forward))
   frp->prebufferbytes_rewind=frp->prebuffersize-frp->prebufferbytes_forward;

  return outbytes;
 }

 return 0;
}

//-------------------------------------------------------------------------
// low level
void mpxinbuf_lowlevel_file_parse_lowfuncs(struct frame *frp,char *filename)
{
 frp->filehand_funcs=NULL;
}

unsigned int mpxinbuf_lowlevel_file_isopen(struct frame *frp)
{
 if(frp->filehand_funcs){
  if(frp->filehand_datas)
   return 1;
 }else{
  if(frp->mpxfilept)
   return 1;
 }
 return 0;
}

unsigned int mpxinbuf_lowlevel_file_open_read(struct frame *frp,char *filename)
{
 struct mpxplay_filehand_low_func_s *lowfuncs=frp->filehand_funcs;

 if(lowfuncs){
  if(lowfuncs->open_read){
   frp->filehand_datas=lowfuncs->open_read(filename);
   if(frp->filehand_datas)
    return 1;
  }
  return 0;
 }
 frp->mpxfilept=pds_open_read(filename,O_RDONLY|O_BINARY);
 return frp->mpxfilept;
}

unsigned int mpxinbuf_lowlevel_file_open_write(struct frame *frp,char *filename)
{
 struct mpxplay_filehand_low_func_s *lowfuncs=frp->filehand_funcs;

 if(lowfuncs){
  if(lowfuncs->open_write){
   frp->filehand_datas=lowfuncs->open_write(filename);
   if(frp->filehand_datas)
    return 1;
  }
  return 0;
 }
 frp->mpxfilept=pds_open_write(filename,O_RDWR|O_BINARY);
 return frp->mpxfilept;
}

unsigned int mpxinbuf_lowlevel_file_open_create(struct frame *frp,char *filename)
{
 struct mpxplay_filehand_low_func_s *lowfuncs=frp->filehand_funcs;

 if(lowfuncs){
  if(lowfuncs->open_create){
   frp->filehand_datas=lowfuncs->open_create(filename);
   if(frp->filehand_datas)
    return 1;
  }
  return 0;
 }
 frp->mpxfilept=pds_open_create(filename,O_RDWR|O_BINARY);
 return frp->mpxfilept;
}

void mpxinbuf_lowlevel_file_close(struct frame *frp)
{
 struct mpxplay_filehand_low_func_s *lowfuncs=frp->filehand_funcs;

 if(lowfuncs){
  if(lowfuncs->close && frp->filehand_datas)
   lowfuncs->close(frp->filehand_datas);
  frp->filehand_datas=NULL;
 }else{
  pds_close(frp->mpxfilept);
  frp->mpxfilept=0;
 }
}

long mpxinbuf_lowlevel_file_read(struct frame *frp,char *ptr,unsigned int num)
{
 struct mpxplay_filehand_low_func_s *lowfuncs=frp->filehand_funcs;
 long bytes=0;

 if(lowfuncs){
  if(lowfuncs->read && frp->filehand_datas)
   bytes=lowfuncs->read(frp->filehand_datas,ptr,num);
 }else{
  bytes=pds_dos_read(frp->mpxfilept,ptr,num);
 }
 if(bytes<0)
  bytes=0;
 else
  funcbit_enable(mpxplay_signal_events,MPXPLAY_SIGNALTYPE_DISKACCESS);
 frp->filepos+=bytes;
 return bytes;
}

long mpxinbuf_lowlevel_file_write(struct frame *frp,char *ptr,unsigned int num)
{
 struct mpxplay_filehand_low_func_s *lowfuncs=frp->filehand_funcs;
 long bytes;

 if(lowfuncs && lowfuncs->write && frp->filehand_datas)
  bytes=lowfuncs->write(frp->filehand_datas,ptr,num);
 else
  bytes=pds_dos_write(frp->mpxfilept,ptr,num);
 if(bytes<0)
  bytes=0;
 else
  funcbit_enable(mpxplay_signal_events,MPXPLAY_SIGNALTYPE_DISKACCESS);
 frp->filepos+=bytes;
 if(frp->filepos>=frp->filesize)
  frp->filesize=frp->filepos+1;
 return bytes;
}

long mpxinbuf_lowlevel_file_length(struct frame *frp)
{
 struct mpxplay_filehand_low_func_s *lowfuncs=frp->filehand_funcs;
 long filelen=0;

 if(lowfuncs){
  if(lowfuncs->filelength && frp->filehand_datas)
   filelen=lowfuncs->filelength(frp->filehand_datas);
 }else
  filelen=pds_filelength(frp->mpxfilept);
 return filelen;
}

long mpxinbuf_lowlevel_file_seek(struct frame *frp,long newpos_absolute)
{
 struct mpxplay_filehand_low_func_s *lowfuncs=frp->filehand_funcs;
 long filepos=0;

 if(lowfuncs){
  if(lowfuncs->seek && frp->filehand_datas)
   filepos=lowfuncs->seek(frp->filehand_datas,newpos_absolute,SEEK_SET);
 }else{
  filepos=pds_lseek(frp->mpxfilept,newpos_absolute,SEEK_SET);
 }
 if(filepos>=0){
  frp->filepos=filepos;
  funcbit_enable(mpxplay_signal_events,MPXPLAY_SIGNALTYPE_DISKACCESS);
 }

 if(filepos<0)
  return MPXPLAY_ERROR_MPXINBUF_SEEK_LOW;

 return filepos;
}

unsigned int mpxinbuf_lowlevel_file_eof(struct frame *frp)
{
 struct mpxplay_filehand_low_func_s *lowfuncs=frp->filehand_funcs;
 int eof_flag=-1;

 if(lowfuncs){
  if(lowfuncs->eof){
   if(frp->filehand_datas)
    eof_flag=lowfuncs->eof(frp->filehand_datas);
   else
    eof_flag=1;
  }
 }else{
  eof_flag=pds_eof(frp->mpxfilept);
 }
 if(eof_flag<0)
  eof_flag=(frp->filepos>=frp->filesize)? 1:0;

 return eof_flag;
}

static unsigned int mpxinbuf_lowlevel_file_chsize(struct frame *frp,long offset)
{
 struct mpxplay_filehand_low_func_s *lowfuncs=frp->filehand_funcs;
 int success;

 if(lowfuncs && lowfuncs->chsize && frp->filehand_datas)
  success=lowfuncs->chsize(frp->filehand_datas,offset);
 else
  success=pds_chsize(frp->mpxfilept,offset);

 return success;
}

//-------------------------------------------------------------------------
// buffered open & close
static void mpxinbuf_reset(struct frame *frp)
{
 frp->filepos=0;
 frp->buffertype=0;
 frp->prebuffergetp=frp->prebufferputp=0;
 frp->prebufferbytes_forward=frp->prebufferbytes_rewind=0;
}

static unsigned int mpxplay_mpxinbuf_fopen_read(void *fbds,char *filename,unsigned long pb_blocksize)
{
 struct frame *frp=fbds;
 if(mpxinbuf_lowlevel_file_open_read(frp,filename)){
  mpxinbuf_reset(frp);

  frp->filesize=mpxinbuf_lowlevel_file_length(frp);
  if(frp->filesize>=8){ // !!! (less is not a real audio file)

#ifdef MPXINBUF_FOR_ALL_FILECHECK
   if(!pb_blocksize)
    pb_blocksize=PREBUFFERBLOCKSIZE_CHECK;
#endif

   // configure prebuffer for file checking
   if(frp->prebufferbegin && pb_blocksize){
    if(pb_blocksize>(frp->prebuffersize/2))
     pb_blocksize=frp->prebuffersize/2;
    frp->buffertype=PREBUFTYPE_RING;
    frp->prebufferblocksize=pb_blocksize;
   }
   return 1;
  }

  mpxplay_mpxinbuf_fclose(frp);
 }
 return 0;
}

static unsigned int mpxplay_mpxinbuf_fopen_write(void *fbds,char *filename)
{
 struct frame *frp=fbds;
 if(mpxinbuf_lowlevel_file_open_write(frp,filename)){
  mpxinbuf_reset(frp);
  frp->filesize=mpxinbuf_lowlevel_file_length(frp);
  return 1;
 }
 return 0;
}

static void mpxplay_mpxinbuf_fclose(void *fbds)
{
 struct frame *frp=fbds;
 mpxinbuf_lowlevel_file_close(frp);
 mpxinbuf_reset(frp);
}

//-----------------------------------------------------------------------
//read & write

static long check_buffer_underrun(struct frame *frp,long num)
{
 if(frp->prebuffergetp<0)
  frp->prebuffergetp+=frp->prebuffersize;
 if(frp->prebuffergetp<0)
  frp->prebuffergetp=0;
 if(frp->prebuffergetp>=frp->prebuffersize)
  frp->prebuffergetp-=frp->prebuffersize;
 if(frp->prebuffergetp>=frp->prebuffersize)
  frp->prebuffergetp=0;

 if(frp->prebufferbytes_forward<0)
  frp->prebufferbytes_forward=0;

 if(frp->prebufferbytes_rewind>(frp->prebuffersize-frp->prebufferbytes_forward)) // ???
  frp->prebufferbytes_rewind=frp->prebuffersize-frp->prebufferbytes_forward;

 if(num>frp->prebufferbytes_forward) // required!
  num=frp->prebufferbytes_forward;

 return num;
}

static unsigned long mpxinbuf_buffer_read(struct frame *frp,char *ptr,unsigned int num)
{
 long i=check_buffer_underrun(frp,num);
 if(i){
  if((frp->prebuffergetp+i)>=frp->prebuffersize){
   long j=frp->prebuffersize-frp->prebuffergetp;
   pds_memcpy(ptr,&frp->prebufferbegin[frp->prebuffergetp],j);
   pds_memcpy(ptr+j,&frp->prebufferbegin[0],i-j);
   frp->prebuffergetp=i-j;
  }else{
   pds_memcpy(ptr,&frp->prebufferbegin[frp->prebuffergetp],i);
   frp->prebuffergetp+=i;
  }
  frp->prebufferbytes_forward-=i;
  frp->prebufferbytes_rewind+=i;
 }
 if(i<num)
  pds_memset(ptr+i,0,num-i);
 return i;
}

static unsigned long mpxplay_mpxinbuf_read_unbuffered(struct frame *frp,char *ptr,unsigned int num)
{
 unsigned long bytes;
 frp->prebuffergetp=frp->prebufferputp=0;
 frp->prebufferbytes_rewind=frp->prebufferbytes_forward=0;
 bytes=mpxinbuf_lowlevel_file_read(frp,ptr,num);
 return bytes;
}

static unsigned long mpxplay_mpxinbuf_read_buffered(struct frame *frp,char *ptr,unsigned int num)
{
 unsigned long bytes=0;
 while(num){
  unsigned int curr;
  if((num>frp->prebufferbytes_forward) && (num<frp->prebufferblocksize)) // !!!
   mpxplay_mpxinbuf_buffer_check(frp);
  curr=mpxinbuf_buffer_read(frp,ptr+bytes,num);
  if(!curr){
   // !!! assumes that the file pointer is at the end_of_buffer+1 (the end of buffer is at the file pointer)
   bytes+=mpxplay_mpxinbuf_read_unbuffered(frp,ptr+bytes,num);
   break;
  }
  num-=curr;
  bytes+=curr;
 }
 return bytes;
}

static unsigned long mpxplay_mpxinbuf_fread(void *fbds,void *ptr,unsigned long num)
{
 struct frame *frp=fbds;
 unsigned long bytes=0;
 switch(frp->buffertype&PREBUFTYPE_MASK){
  case PREBUFTYPE_NONE:bytes=mpxplay_mpxinbuf_read_unbuffered(frp,ptr,num);break;
  case PREBUFTYPE_RING:if(frp->buffertype&PREBUFTYPE_INT)
                        bytes=mpxinbuf_buffer_read(frp,ptr,num);
                       else
                        bytes=mpxplay_mpxinbuf_read_buffered(frp,ptr,num);
                       break;
  case PREBUFTYPE_FULL:bytes=mpxinbuf_buffer_read(frp,ptr,num);break;
 }
 return bytes;
}

static long mpxplay_mpxinbuf_fwrite(void *fbds,void *ptr,unsigned long num)
{
 struct frame *frp=fbds;
 return mpxinbuf_lowlevel_file_write(frp,ptr,num);
}

//-------------------------------------------------------------------------
// seek
static long mpxplay_mpxinbuf_seek_unbuffered(struct frame *frp,long newpos_absolute)
{
 frp->prebuffergetp=frp->prebufferputp=0;
 frp->prebufferbytes_forward=frp->prebufferbytes_rewind=0;
 return mpxinbuf_lowlevel_file_seek(frp,newpos_absolute);
}

static long mpxplay_mpxinbuf_ringbuffer_seek(struct frame *frp,long newpos_absolute,long newpos_relative)
{
 if( ((newpos_relative>0) && (newpos_relative<frp->prebufferbytes_forward))
  || ((newpos_relative<0) && ((-newpos_relative)<=frp->prebufferbytes_rewind))){
  frp->prebufferbytes_forward-=newpos_relative;
  frp->prebufferbytes_rewind +=newpos_relative;
  frp->prebuffergetp         +=newpos_relative;
  if(frp->prebuffergetp>=frp->prebuffersize) // at forward
   frp->prebuffergetp-=frp->prebuffersize;
  if(frp->prebuffergetp<0)                   // at rewind
   frp->prebuffergetp+=frp->prebuffersize;
  return newpos_absolute;
 }
 frp->prebufferbytes_forward=frp->prebufferbytes_rewind=0;
 return MPXPLAY_ERROR_MPXINBUF_SEEK_BUF;
}

static long mpxplay_mpxinbuf_fullbuffer_seek(struct frame *frp,long newpos_absolute)
{
 if(newpos_absolute<frp->prebuffersize){
  frp->prebuffergetp         = newpos_absolute;
  frp->prebufferbytes_rewind = newpos_absolute;
  frp->prebufferbytes_forward= frp->prebuffersize-newpos_absolute;
  return newpos_absolute;
 }
 return MPXPLAY_ERROR_MPXINBUF_SEEK_EOF;
}

static long mpxplay_mpxinbuf_ringbuffer_advance(struct frame *frp,long newpos_absolute)
{
 if((newpos_absolute>frp->filepos) && (newpos_absolute<(frp->filepos+frp->prebufferblocksize))){
  long advance=newpos_absolute-frp->filepos;
  long newgp=frp->prebufferputp;

  frp->prebufferbytes_rewind+=frp->prebufferbytes_forward;
  if(frp->prebufferbytes_rewind>(frp->prebuffersize-frp->prebufferblocksize))
   frp->prebufferbytes_rewind=frp->prebuffersize-frp->prebufferblocksize;
  frp->prebufferbytes_forward=0;
  mpxinbuf_ringbuffer_fill(frp);
  if(frp->prebufferbytes_forward<=advance)
   return MPXPLAY_ERROR_MPXINBUF_SEEK_LOW;
  frp->prebuffergetp=newgp+advance;
  frp->prebufferbytes_forward-=advance;
  frp->prebufferbytes_rewind+=advance;

 }else{
  if(mpxplay_mpxinbuf_seek_unbuffered(frp,newpos_absolute)<0)
   return MPXPLAY_ERROR_MPXINBUF_SEEK_LOW;
  mpxinbuf_ringbuffer_fill(frp);
  if(!frp->prebufferbytes_forward)
   return MPXPLAY_ERROR_MPXINBUF_SEEK_LOW;
 }
 return newpos_absolute;
}

static long mpxplay_mpxinbuf_fseek(void *fbds,long offset,int whence)
{
 struct frame *frp=fbds;
 long newpos_absolute,newpos_relative,np;

 switch(whence){
  case SEEK_SET:newpos_absolute=offset;newpos_relative=offset-mpxplay_mpxinbuf_ftell(frp);break;
  case SEEK_CUR:newpos_absolute=mpxplay_mpxinbuf_ftell(frp)+offset;newpos_relative=offset;break;
  case SEEK_END:newpos_absolute=mpxplay_mpxinbuf_filelength(frp)+offset;newpos_relative=newpos_absolute-mpxplay_mpxinbuf_ftell(frp);break;
  default:return MPXPLAY_ERROR_MPXINBUF_SEEK_EOF; // program should never reach this point
 }

 if(!newpos_relative && !(frp->buffertype&PREBUFTYPE_FULL))
  return newpos_absolute;

 if((newpos_absolute<0) || (newpos_absolute>mpxplay_mpxinbuf_filelength(frp)))
  return MPXPLAY_ERROR_MPXINBUF_SEEK_EOF;

 switch(frp->buffertype&PREBUFTYPE_MASK){
  case PREBUFTYPE_NONE:
     return mpxplay_mpxinbuf_seek_unbuffered(frp,newpos_absolute);
  case PREBUFTYPE_FULL:
     return mpxplay_mpxinbuf_fullbuffer_seek(frp,newpos_absolute);
  case PREBUFTYPE_RING:
     np=mpxplay_mpxinbuf_ringbuffer_seek(frp,newpos_absolute,newpos_relative);
     if(np>=0)
      return np;
     if(frp->buffertype&PREBUFTYPE_INT){
      frp->prebuffer_seek_retry=newpos_absolute;
      return np;
     }
     if(frp->buffertype&PREBUFTYPE_WRITEPROTECT)
      return mpxplay_mpxinbuf_seek_unbuffered(frp,newpos_absolute);
     return mpxplay_mpxinbuf_ringbuffer_advance(frp,newpos_absolute);
 }

 return MPXPLAY_ERROR_MPXINBUF_SEEK_BUF; // program should never reach this point
}

long mpxplay_mpxinbuf_ftell(void *fbds)
{
 struct frame *frp=fbds;
 return (frp->filepos-frp->prebufferbytes_forward);
}

static long mpxplay_mpxinbuf_filelength(void *fbds)
{
 struct frame *frp=fbds;
 return (frp->filesize);
}

static int mpxplay_mpxinbuf_feof(void *fbds)
{
 struct frame *frp=fbds;
 return ((frp->filepos>=frp->filesize && !frp->prebufferbytes_forward)? 1:0);
}

static int mpxplay_mpxinbuf_chsize(void *fbds,long offset)
{
 struct frame *frp=fbds;
 return mpxinbuf_lowlevel_file_chsize(frp,offset);
}

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

static mpxp_uint32_t mpxplay_mpxinbuf_get_byte(void *fbds)
{
 mpxp_uint8_t tmp;
 if(mpxplay_mpxinbuf_fread(fbds,&tmp,1)!=1)
  return 0;
 return ((mpxp_uint32_t)tmp);
}

static mpxp_uint32_t mpxplay_mpxinbuf_get_le16(void *fbds)
{
 mpxp_uint16_t tmp;
 if(mpxplay_mpxinbuf_fread(fbds,&tmp,2)!=2)
  return 0;
 return ((mpxp_uint32_t)tmp);
}

static mpxp_uint32_t mpxplay_mpxinbuf_get_le32(void *fbds)
{
 mpxp_uint32_t tmp;
 if(mpxplay_mpxinbuf_fread(fbds,&tmp,4)!=4)
  return 0;
 return tmp;
}

static mpxp_uint64_t mpxplay_mpxinbuf_get_le64(void *fbds)
{
 mpxp_uint64_t tmp;
 if(mpxplay_mpxinbuf_fread(fbds,&tmp,8)!=8)
  return 0;
 return tmp;
}

static mpxp_uint32_t mpxplay_mpxinbuf_get_be16(void *fbds)
{
 mpxp_uint16_t tmp;
 if(mpxplay_mpxinbuf_fread(fbds,&tmp,2)!=2)
  return 0;
 return (pds_bswap16(tmp));
}

static mpxp_uint32_t mpxplay_mpxinbuf_get_be32(void *fbds)
{
 mpxp_uint32_t tmp;
 if(mpxplay_mpxinbuf_fread(fbds,&tmp,4)!=4)
  return 0;
 return (pds_bswap32(tmp));
}

static mpxp_uint64_t mpxplay_mpxinbuf_get_be64(void *fbds)
{
 mpxp_uint64_t tmp;
 tmp =((mpxp_uint64_t)mpxplay_mpxinbuf_get_be32(fbds))<<32;
 tmp|=(mpxp_uint64_t)mpxplay_mpxinbuf_get_be32(fbds);
 return tmp;
}

//------------------------------------------------------------------------
static struct frame fr_seek_helper;

struct frame *mpxplay_mpxinbuf_seekhelper_init(struct frame *frp)
{
 unsigned int intsoundcntrl_save;
 MPXPLAY_INTSOUNDDECODER_DISALLOW;
 pds_memcpy((void *)&fr_seek_helper,(void *)frp,sizeof(struct frame));
 MPXPLAY_INTSOUNDDECODER_ALLOW;
 funcbit_disable(fr_seek_helper.buffertype,PREBUFTYPE_INT);
 funcbit_enable(fr_seek_helper.buffertype,PREBUFTYPE_WRITEPROTECT);
 return (&fr_seek_helper);
}

void mpxplay_mpxinbuf_seekhelper_close(struct frame *frp)
{
 unsigned int intsoundcntrl_save;
 MPXPLAY_INTSOUNDDECODER_DISALLOW;
 frp->prebuffergetp         =fr_seek_helper.prebuffergetp;
 frp->prebufferputp         =fr_seek_helper.prebufferputp;
 frp->prebufferbytes_rewind =fr_seek_helper.prebufferbytes_rewind;
 frp->prebufferbytes_forward=fr_seek_helper.prebufferbytes_forward;
 frp->filepos               =fr_seek_helper.filepos;
 MPXPLAY_INTSOUNDDECODER_ALLOW;
 //mpxplay_mpxinbuf_buffer_check(frp);
}
