/* MikMod example player (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for complete list. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /*============================================================================== $Id: mlist.c,v 1.4 2003/09/25 16:42:19 raph Exp $ Functions to handle the playlist. ==============================================================================*/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef HAVE_UNISTD_H #include #endif #ifndef HAVE_FNMATCH_H #include "fnmatch.h" #else #include #endif #include #include #include #include #ifdef SRANDOM_IN_MATH_H #include #endif #include #include "player.h" #include "mutilities.h" static int mikmod_random(int limit) { #if defined(__OS2__)||defined(__EMX__) return (rand()*limit)/(RAND_MAX+1.0); #else return random()%limit; #endif } /* Mark all the modules in the playlist as not played */ static void PL_ClearPlayed(PLAYLIST *pl) { int i; for (i=0;inumused;i++) pl->entry[i].played=0; } BOOL PL_isPlaylistFilename(CHAR *filename) { char *cfg_name=NULL; if (!fnmatch("*.mpl",filename,0)) return 1; if ((cfg_name=PL_GetFilename())) { if (!filecmp(cfg_name,filename)) { free(cfg_name); return 1; } free(cfg_name); } return 0; } void PL_InitList(PLAYLIST* pl) { pl->numused=0; pl->current=-1; pl->entry=NULL; pl->add_pos=-1; #if defined(__OS2__)||defined(__EMX__) srand(time(NULL)); #else srandom(time(NULL)); #endif } /* Choose the first non-played module */ void PL_InitCurrent(PLAYLIST *pl) { pl->current=0; while ((pl->currentnumused)&&(pl->entry[pl->current].played)) pl->current++; if (pl->current>=pl->numused) { PL_ClearPlayed(pl); pl->current=0; } pl->current--; } void PL_ClearList(PLAYLIST* pl) { int i; for (i=0;inumused;i++) { if (pl->entry[i].file) free(pl->entry[i].file); if (pl->entry[i].archive) free(pl->entry[i].archive); } if (pl->entry) { free(pl->entry); pl->entry=NULL; } pl->current=-1; pl->numused=0; } PLAYENTRY *PL_GetCurrent(PLAYLIST *pl) { if ((pl->current<0)||(!pl->numused)) return NULL; return &pl->entry[pl->current]; } int PL_GetCurrentPos(PLAYLIST *pl) { if ((pl->current<0)||(!pl->numused)) return -1; return pl->current; } PLAYENTRY *PL_GetEntry(PLAYLIST *pl,int number) { if ((number<0)||(number>=pl->numused)) return NULL; return &pl->entry[number]; } int PL_GetLength(PLAYLIST *pl) { return pl->numused; } void PL_SetTimeCurrent(PLAYLIST *pl,long sngtime) { if (pl->current>=0 && pl->currentnumused) pl->entry[pl->current].time=sngtime>>10; } void PL_SetPlayedCurrent(PLAYLIST *pl) { if (pl->current>=0 && pl->currentnumused) pl->entry[pl->current].played=1; } BOOL PL_DelEntry(PLAYLIST *pl,int number) { int i; if (!pl->numused) return 0; if (pl->entry[number].file) free(pl->entry[number].file); if (pl->entry[number].archive) free(pl->entry[number].archive); pl->numused--; if (pl->current>=number) { pl->current--; if ((pl->numused)&&(pl->current==-1)) pl->current=0; } for (i=number;inumused;i++) pl->entry[i]=pl->entry[i+1]; pl->entry=realloc(pl->entry,pl->numused*sizeof(PLAYENTRY)); return 1; } BOOL PL_DelDouble(PLAYLIST *pl) { int i,j; if (!pl->numused) return 0; for (i=pl->numused-2;i>=0;i--) for (j=i+1;jnumused;j++) if (!filecmp(pl->entry[i].file,pl->entry[j].file)&& (!(pl->entry[i].archive||pl->entry[j].archive)|| (pl->entry[i].archive&&pl->entry[j].archive&& !filecmp(pl->entry[i].archive,pl->entry[j].archive)))) { /* keep the time and played information whenever possible */ if (!pl->entry[i].time) pl->entry[i].time=pl->entry[j].time; if (!pl->entry[i].played) pl->entry[i].played=pl->entry[j].played; PL_DelEntry(pl,j); } return 1; } /* Following PL_Add will insert at pos */ void PL_StartInsert(PLAYLIST *pl,int pos) { pl->add_pos=pos; } /* Following PL_Add will append at end of playlist */ void PL_StopInsert(PLAYLIST *pl) { pl->add_pos=-1; } static void PL_Insert(PLAYLIST *pl,int pos,CHAR *file,CHAR *arc,int time,BOOL played) { int i; pl->numused++; pl->entry=realloc(pl->entry,pl->numused*sizeof(PLAYENTRY)); for (i=pl->numused-1;i>pos;i--) pl->entry[i]=pl->entry[i-1]; if (pl->current>=pos) pl->current++; pl->entry[pos].file=malloc((strlen(file)+1)*sizeof(CHAR)); strcpy(pl->entry[pos].file,file); if (arc) { pl->entry[pos].archive=malloc((strlen(arc)+1)*sizeof(CHAR)); strcpy(pl->entry[pos].archive,arc); } else pl->entry[pos].archive=NULL; pl->entry[pos].time=time; pl->entry[pos].played=played; } /* pl->add_pos < 0 => Append entry at end of playlist pl->add_pos >= 0 => Insert entry at pl->add_pos and increment pl->add_pos */ void PL_Add (PLAYLIST *pl,CHAR *file,CHAR *arc,int time,BOOL played) { if (pl->add_pos>=0) { PL_Insert(pl,pl->add_pos,file,arc,time,played); pl->add_pos++; } else PL_Insert(pl,pl->numused,file,arc,time,played); } #define LINE_LEN (PATH_MAX*2+20) /* "file" "arc" time played */ /* Loads a playlist */ BOOL PL_Load(PLAYLIST *pl,CHAR *filename) { FILE *file; CHAR line[LINE_LEN]; CHAR *mod,*arc,*pos,*slash; int time,played; CHAR *ok=NULL; if (!(file=fopen(filename,"r"))) return 0; while ((ok=fgets(line,LINE_LEN,file))&&(strcasecmp(line,PL_IDENT))); if (!ok) { fclose(file); return 0; /* file is not a playlist */ } slash=strrchr(filename,PATH_SEP); while (fgets(line,LINE_LEN,file)) { mod=line+1; /* line == '"file" "arc" time played' */ pos=mod; while (*pos!='"') pos++; *pos='\0'; pos+=3; arc=pos; while (*pos!='"') pos++; *pos='\0'; if (arc==pos) arc=NULL; pos+=2; time=played=0; sscanf(pos,"%d %d",&time,&played); if ((!arc)&&(!time)&&(!played)) MA_FindFiles(pl,mod); else { /* we're loading a playlist, so it might be necessary to convert playlist paths to relative paths from cwd */ if (slash && path_relative(arc?arc:mod)) { CHAR *dummy; dummy=malloc(slash+1-filename+strlen(arc?arc:mod)+1); strncpy(dummy,filename,slash+1-filename); dummy[slash+1-filename]='\0'; strcat(dummy,arc?arc:mod); PL_Add(pl,arc?mod:dummy,arc?dummy:NULL,time,(BOOL)played); } else PL_Add(pl,mod,arc,time,(BOOL)played); } } fclose(file); return 1; } BOOL PL_Save(PLAYLIST *pl,CHAR *filename) { FILE *file; int i; PLAYENTRY *entry; if (!(file=fopen(filename,"w"))) return 0; if (fputs(PL_IDENT,file)!=EOF) { for (i=0;inumused;i++) { entry=&pl->entry[i]; if (entry->archive) fprintf(file,"\"%s\" \"%s\" %d %d\n", entry->file,entry->archive,entry->time, (int)entry->played); else fprintf(file,"\"%s\" \"\" %d %d\n", entry->file,entry->time,(int)entry->played); } fclose(file); return 1; } fclose(file); return 0; } char* PL_GetFilename(void) { #if defined(__OS2__)||defined(__EMX__) return get_cfg_name("mikmodpl.cfg"); #else return get_cfg_name(".mikmod_playlist"); #endif } BOOL PL_LoadDefault(PLAYLIST *pl) { char *name=PL_GetFilename(); BOOL ret=0; if (name) { ret=PL_Load(pl,name); free(name); } return ret; } BOOL PL_SaveDefault (PLAYLIST *pl) { char *name=PL_GetFilename(); BOOL ret=0; if (name) { ret=PL_Save(pl,name); free(name); } return ret; } /* check if selected file is a playlist and exchange it with the playlist */ static BOOL PL_CheckPlaylist(PLAYLIST *pl,BOOL *ok,int old_current,int cont,CHAR **retfile,CHAR **retarc,int arg) { /* check if selected file is a playlist */ if ((pl->entry[pl->current].file)&&(!pl->entry[pl->current].archive)) { pl->add_pos=pl->current+1; if (PL_Load(pl,pl->entry[pl->current].file)) { /* Yes -> del playlist-entry and get next entry in now modified list */ pl->add_pos=-1; PL_DelEntry(pl,pl->current); pl->current=old_current; switch (cont) { case PL_CONT_NEXT: *ok=PL_ContNext(pl,retfile,retarc,arg); return 1; case PL_CONT_PREV: *ok=PL_ContPrev(pl,retfile,retarc); return 1; case PL_CONT_POS: *ok=PL_ContPos(pl,retfile,retarc,arg); return 1; } } pl->add_pos=-1; } return 0; } /* get next module to play mode: PM_MODULE, PM_MULTI, PM_SHUFFLE or PM_RANDOM return: was there a module? */ BOOL PL_ContNext(PLAYLIST *pl,CHAR **retfile,CHAR **retarc,int mode) { int num,i,not_played,old_current=pl->current; BOOL ok= 1; if (!pl->numused) return 0; if (BTST(mode,PM_RANDOM)) { not_played=0; for (i=0;inumused;i++) if (!pl->entry[i].played) not_played++; if (!not_played) { PL_ClearPlayed (pl); not_played=pl->numused; if (BTST(mode,PM_SHUFFLE)) PL_Randomize(pl,0); if (!BTST(mode,PM_MULTI)) return 0; } num=mikmod_random(not_played)+1; while (num>0) { pl->current++; if (pl->current==pl->numused) pl->current=0; if (!pl->entry[pl->current].played) num--; } } else { pl->current++; if (pl->current==pl->numused) { if (config.exit_after) return 0; not_played=0; for (i=0;inumused;i++) if (!pl->entry[i].played) not_played++; if (!not_played) { PL_ClearPlayed(pl); if (BTST(mode,PM_SHUFFLE)) PL_Randomize(pl,0); } pl->current=0; if (!BTST(mode,PM_MULTI)) return 0; } } /* check if selected file is a playlist and load it */ if (PL_CheckPlaylist(pl,&ok,old_current,PL_CONT_NEXT,retfile,retarc,mode)) return ok; if (retfile) *retfile=pl->entry[pl->current].file; if (retarc) *retarc=pl->entry[pl->current].archive; return 1; } BOOL PL_ContPrev(PLAYLIST *pl,CHAR **retfile,CHAR **retarc) { int old_current=pl->current; BOOL ok=1; if (!pl->numused) return 0; pl->current--; if (pl->current<0) pl->current=pl->numused-1; /* check if selected file is a playlist and load it */ if (PL_CheckPlaylist(pl,&ok,old_current,PL_CONT_PREV,retfile,retarc,0)) return ok; if (retfile) *retfile=pl->entry[pl->current].file; if (retarc) *retarc=pl->entry[pl->current].archive; return 1; } BOOL PL_ContPos(PLAYLIST *pl,CHAR **retfile,CHAR **retarc,int number) { int old_current=pl->current; BOOL ok=1; if ((number<0)||(number>=pl->numused)) return 0; pl->current=number; /* check if selected file is a playlist and load it */ if (PL_CheckPlaylist(pl,&ok,old_current,PL_CONT_POS,retfile,retarc,number)) return ok; if (retfile) *retfile=pl->entry[pl->current].file; if (retarc) *retarc=pl->entry[pl->current].archive; return 1; } void PL_Sort(PLAYLIST *pl,int (*compar)(PLAYENTRY *small,PLAYENTRY *big)) { int i,j; BOOL end=0; PLAYENTRY tmp; for (i=0;inumused && !end;i++) { end=1; for (j=pl->numused-1;j>i;j--) if (compar(&pl->entry[j-1],&pl->entry[j])>0) { tmp=pl->entry[j]; pl->entry[j]=pl->entry[j-1]; pl->entry[j-1]=tmp; if (pl->current==j) pl->current=j-1; else if (pl->current==j-1) pl->current=j; end=0; } } } void PL_Randomize(PLAYLIST *pl,BOOL track) { if (pl->numused>1) { int i,target; for (i=0;inumused-1;i++) { target=mikmod_random(pl->numused-i)+i; if (target!=i) { PLAYENTRY temp; temp=pl->entry[i]; pl->entry[i]=pl->entry[target]; pl->entry[target]=temp; /* track selection if requested */ if (track) { if (pl->current==i) pl->current=target; else if (pl->current==target) pl->current=i; } } } if (!track) pl->current=-1; } } /* ex:set ts=4: */