2008-05-24 08:59:35 +00:00
/*
* DXX Rebirth " jukebox " code
* MD 2211 < md2211 @ users . sourceforge . net > , 2007
*/
# include <stdlib.h>
2010-09-26 13:15:20 +00:00
# include <stdio.h>
2008-05-24 08:59:35 +00:00
# include <string.h>
2010-09-02 00:07:37 +00:00
2008-05-24 08:59:35 +00:00
# include "physfsx.h"
# include "args.h"
# include "dl_list.h"
# include "hudmsg.h"
2012-06-10 12:02:40 +00:00
# include "songs.h"
2008-05-24 08:59:35 +00:00
# include "jukebox.h"
2012-07-07 18:35:06 +00:00
# include "dxxerror.h"
2008-05-24 08:59:35 +00:00
# include "console.h"
# include "config.h"
# define MUSIC_HUDMSG_MAXLEN 40
# define JUKEBOX_HUDMSG_PLAYING "Now playing:"
# define JUKEBOX_HUDMSG_STOPPED "Jukebox stopped"
2010-09-26 13:15:20 +00:00
typedef struct jukebox_songs
{
char * * list ; // the actual list
2010-11-21 11:55:08 +00:00
char * list_buf ; // buffer containing song file path text
2010-09-26 13:15:20 +00:00
int num_songs ; // number of jukebox songs
int max_songs ; // maximum number of pointers that 'list' can hold, i.e. size of list / size of one pointer
int max_buf ; // size of list_buf
} jukebox_songs ;
static jukebox_songs JukeboxSongs = { NULL , NULL , 0 , 0 , 0 } ;
2008-05-24 08:59:35 +00:00
char hud_msg_buf [ MUSIC_HUDMSG_MAXLEN + 4 ] ;
void jukebox_unload ( )
{
2010-09-26 13:15:20 +00:00
if ( JukeboxSongs . list_buf )
2010-10-10 10:42:53 +00:00
{
2010-09-26 13:15:20 +00:00
d_free ( JukeboxSongs . list_buf ) ;
2010-10-10 10:42:53 +00:00
if ( JukeboxSongs . list )
d_free ( JukeboxSongs . list ) ;
}
else if ( JukeboxSongs . list )
{
PHYSFS_freeList ( JukeboxSongs . list ) ;
JukeboxSongs . list = NULL ;
}
2010-09-26 13:15:20 +00:00
JukeboxSongs . num_songs = JukeboxSongs . max_songs = JukeboxSongs . max_buf = 0 ;
}
2012-07-22 23:17:52 +00:00
const char * const jukebox_exts [ ] = { SONG_EXT_HMP , SONG_EXT_MID , SONG_EXT_OGG , SONG_EXT_FLAC , SONG_EXT_MP3 , NULL } ;
2009-01-13 12:39:03 +00:00
2011-04-18 12:30:46 +00:00
int read_m3u ( void )
2010-09-26 13:15:20 +00:00
{
FILE * fp ;
int length ;
char * buf ;
2011-04-18 12:30:46 +00:00
char * abspath ;
2010-09-26 13:15:20 +00:00
2011-04-18 12:30:46 +00:00
MALLOC ( abspath , char , PATH_MAX ) ;
if ( ! abspath )
return 0 ;
2011-06-01 07:59:51 +00:00
if ( PHYSFSX_exists ( GameCfg . CMLevelMusicPath , 0 ) ) // it's a child of Sharepath, build full path
2011-04-18 12:30:46 +00:00
PHYSFSX_getRealPath ( GameCfg . CMLevelMusicPath , abspath ) ;
else
{
strncpy ( abspath , GameCfg . CMLevelMusicPath , PATH_MAX - 1 ) ;
abspath [ PATH_MAX - 1 ] = ' \0 ' ;
}
fp = fopen ( abspath , " rb " ) ;
d_free ( abspath ) ;
2010-09-26 13:15:20 +00:00
if ( ! fp )
2011-04-18 12:30:46 +00:00
return 0 ;
2010-09-26 13:15:20 +00:00
fseek ( fp , - 1 , SEEK_END ) ;
length = ftell ( fp ) + 1 ;
MALLOC ( JukeboxSongs . list_buf , char , length + 1 ) ;
if ( ! JukeboxSongs . list_buf )
{
fclose ( fp ) ;
2011-04-18 12:30:46 +00:00
return 0 ;
2010-09-26 13:15:20 +00:00
}
2008-05-24 08:59:35 +00:00
2010-09-26 13:15:20 +00:00
fseek ( fp , 0 , SEEK_SET ) ;
if ( ! fread ( JukeboxSongs . list_buf , length , 1 , fp ) )
2008-05-24 08:59:35 +00:00
{
2010-09-26 13:15:20 +00:00
d_free ( JukeboxSongs . list_buf ) ;
fclose ( fp ) ;
2011-04-18 12:30:46 +00:00
return 0 ;
2008-05-24 08:59:35 +00:00
}
2010-09-26 13:15:20 +00:00
fclose ( fp ) ; // Finished with it
// The growing string list is allocated last, hopefully reducing memory fragmentation when it grows
MALLOC ( JukeboxSongs . list , char * , 1024 ) ;
if ( ! JukeboxSongs . list )
{
d_free ( JukeboxSongs . list_buf ) ;
2011-04-18 12:30:46 +00:00
return 0 ;
2010-09-26 13:15:20 +00:00
}
JukeboxSongs . max_songs = 1024 ;
2010-08-27 14:09:19 +00:00
2010-09-26 13:15:20 +00:00
JukeboxSongs . list_buf [ length ] = ' \0 ' ; // make sure the last string is terminated
JukeboxSongs . max_buf = length + 1 ;
buf = JukeboxSongs . list_buf ;
2010-11-13 05:10:57 +00:00
while ( buf < JukeboxSongs . list_buf + length - 1 )
2010-09-26 13:15:20 +00:00
{
while ( * buf = = 0 | | * buf = = 10 | | * buf = = 13 ) // find new line - support DOS, Unix and Mac line endings
buf + + ;
if ( * buf ! = ' # ' ) // ignore comments / extra info
{
if ( JukeboxSongs . num_songs > = JukeboxSongs . max_songs )
{
char * * new_list = d_realloc ( JukeboxSongs . list , JukeboxSongs . max_buf * sizeof ( char * ) * MEM_K ) ;
if ( new_list = = NULL )
break ;
JukeboxSongs . max_buf * = MEM_K ;
JukeboxSongs . list = new_list ;
}
JukeboxSongs . list [ JukeboxSongs . num_songs + + ] = buf ;
}
while ( * buf ! = 0 & & * buf ! = 10 & & * buf ! = 13 ) // find end of line
buf + + ;
* buf = 0 ;
}
2011-04-18 12:30:46 +00:00
return 1 ;
2010-09-26 13:15:20 +00:00
}
/* Loads music file names from a given directory or M3U playlist */
2010-06-14 15:32:55 +00:00
void jukebox_load ( )
{
2010-11-21 11:55:08 +00:00
static int jukebox_init = 1 ;
2008-05-24 08:59:35 +00:00
2010-11-21 11:55:08 +00:00
// initialize JukeboxSongs structure once per runtime
if ( jukebox_init )
2008-05-24 08:59:35 +00:00
{
2010-11-21 11:55:08 +00:00
JukeboxSongs . list = NULL ;
JukeboxSongs . num_songs = JukeboxSongs . max_songs = JukeboxSongs . max_buf = 0 ;
jukebox_init = 0 ;
2008-05-24 08:59:35 +00:00
}
2010-11-21 11:55:08 +00:00
jukebox_unload ( ) ;
2008-05-24 08:59:35 +00:00
2011-04-18 12:30:46 +00:00
// Check if it's an M3U file
2012-05-18 23:36:41 +00:00
if ( ! d_stricmp ( & GameCfg . CMLevelMusicPath [ strlen ( GameCfg . CMLevelMusicPath ) - 4 ] , " .m3u " ) )
2011-04-18 12:30:46 +00:00
read_m3u ( ) ;
else // a directory
2010-06-14 08:13:16 +00:00
{
2011-04-18 12:30:46 +00:00
int new_path = 0 ;
char * p ;
const char * sep = PHYSFS_getDirSeparator ( ) ;
int i ;
2010-06-14 08:13:16 +00:00
2011-04-18 12:30:46 +00:00
// stick a separator on the end if necessary.
if ( strlen ( GameCfg . CMLevelMusicPath ) > = strlen ( sep ) )
2010-06-14 10:33:36 +00:00
{
2011-04-18 12:30:46 +00:00
p = GameCfg . CMLevelMusicPath + strlen ( GameCfg . CMLevelMusicPath ) - strlen ( sep ) ;
if ( strcmp ( p , sep ) )
strncat ( GameCfg . CMLevelMusicPath , sep , PATH_MAX - 1 - strlen ( GameCfg . CMLevelMusicPath ) ) ;
2010-09-26 13:15:20 +00:00
}
2010-11-21 11:55:08 +00:00
// Read directory using PhysicsFS
2011-04-18 12:30:46 +00:00
if ( PHYSFS_isDirectory ( GameCfg . CMLevelMusicPath ) ) // find files in relative directory
JukeboxSongs . list = PHYSFSX_findFiles ( GameCfg . CMLevelMusicPath , jukebox_exts ) ;
else
{
new_path = PHYSFSX_isNewPath ( GameCfg . CMLevelMusicPath ) ;
PHYSFS_addToSearchPath ( GameCfg . CMLevelMusicPath , 0 ) ;
// as mountpoints are no option (yet), make sure only files originating from GameCfg.CMLevelMusicPath are aded to the list.
JukeboxSongs . list = PHYSFSX_findabsoluteFiles ( " " , GameCfg . CMLevelMusicPath , jukebox_exts ) ;
}
2010-11-21 11:55:08 +00:00
if ( ! JukeboxSongs . list )
2010-09-26 13:15:20 +00:00
{
2010-11-21 11:55:08 +00:00
if ( new_path )
PHYSFS_removeFromSearchPath ( GameCfg . CMLevelMusicPath ) ;
return ;
2008-05-24 08:59:35 +00:00
}
2010-11-21 11:55:08 +00:00
2011-01-20 11:17:24 +00:00
for ( i = 0 ; JukeboxSongs . list [ i ] ; i + + ) { }
2010-11-21 11:55:08 +00:00
JukeboxSongs . num_songs = i ;
if ( new_path )
2010-09-26 13:15:20 +00:00
PHYSFS_removeFromSearchPath ( GameCfg . CMLevelMusicPath ) ;
2010-11-21 11:55:08 +00:00
}
if ( JukeboxSongs . num_songs )
{
con_printf ( CON_DEBUG , " Jukebox: %d music file(s) found in %s \n " , JukeboxSongs . num_songs , GameCfg . CMLevelMusicPath ) ;
if ( GameCfg . CMLevelMusicTrack [ 1 ] ! = JukeboxSongs . num_songs )
{
GameCfg . CMLevelMusicTrack [ 1 ] = JukeboxSongs . num_songs ;
GameCfg . CMLevelMusicTrack [ 0 ] = 0 ; // number of songs changed so start from beginning.
2010-09-26 13:15:20 +00:00
}
2008-05-24 08:59:35 +00:00
}
2010-11-21 11:55:08 +00:00
else
{
GameCfg . CMLevelMusicTrack [ 0 ] = - 1 ;
GameCfg . CMLevelMusicTrack [ 1 ] = - 1 ;
con_printf ( CON_DEBUG , " Jukebox music could not be found! \n " ) ;
}
2008-05-24 08:59:35 +00:00
}
2010-06-14 08:13:16 +00:00
// To proceed tru our playlist. Usually used for continous play, but can loop as well.
void jukebox_hook_next ( )
{
2010-09-26 13:15:20 +00:00
if ( ! JukeboxSongs . list | | GameCfg . CMLevelMusicTrack [ 0 ] = = - 1 ) return ;
2009-03-03 12:55:27 +00:00
2011-01-20 11:17:24 +00:00
if ( GameCfg . CMLevelMusicPlayOrder = = MUSIC_CM_PLAYORDER_RAND )
GameCfg . CMLevelMusicTrack [ 0 ] = d_rand ( ) % GameCfg . CMLevelMusicTrack [ 1 ] ; // simply a random selection - no check if this song has already been played. But that's how I roll!
else
GameCfg . CMLevelMusicTrack [ 0 ] + + ;
2010-06-14 08:13:16 +00:00
if ( GameCfg . CMLevelMusicTrack [ 0 ] + 1 > GameCfg . CMLevelMusicTrack [ 1 ] )
GameCfg . CMLevelMusicTrack [ 0 ] = 0 ;
jukebox_play ( ) ;
}
2008-05-24 08:59:35 +00:00
2010-06-14 08:13:16 +00:00
// Play tracks from Jukebox directory. Play track specified in GameCfg.CMLevelMusicTrack[0] and loop depending on GameCfg.CMLevelMusicPlayOrder
int jukebox_play ( )
{
2010-11-21 11:55:08 +00:00
char * music_filename , * full_filename ;
2012-09-02 22:43:52 +00:00
unsigned long size_full_filename = 0 ;
2009-03-03 12:55:27 +00:00
2010-09-26 13:15:20 +00:00
if ( ! JukeboxSongs . list )
2010-06-14 08:13:16 +00:00
return 0 ;
2009-03-03 12:55:27 +00:00
2010-06-14 08:13:16 +00:00
if ( GameCfg . CMLevelMusicTrack [ 0 ] < 0 | | GameCfg . CMLevelMusicTrack [ 0 ] + 1 > GameCfg . CMLevelMusicTrack [ 1 ] )
return 0 ;
2009-01-13 12:39:03 +00:00
2010-09-26 13:15:20 +00:00
music_filename = JukeboxSongs . list [ GameCfg . CMLevelMusicTrack [ 0 ] ] ;
2010-02-27 13:05:34 +00:00
if ( ! music_filename )
return 0 ;
2008-05-24 08:59:35 +00:00
2012-09-02 22:43:52 +00:00
size_full_filename = strlen ( GameCfg . CMLevelMusicPath ) + strlen ( music_filename ) + 1 ;
MALLOC ( full_filename , char , size_full_filename ) ;
memset ( full_filename , ' \0 ' , size_full_filename ) ;
2012-05-18 23:36:41 +00:00
if ( ! d_stricmp ( & GameCfg . CMLevelMusicPath [ strlen ( GameCfg . CMLevelMusicPath ) - 4 ] , " .m3u " ) ) // if it's from an M3U playlist
2011-04-18 12:30:46 +00:00
strcpy ( full_filename , music_filename ) ;
else // if it's from a specified path
2012-09-02 22:43:52 +00:00
snprintf ( full_filename , size_full_filename , " %s%s " , GameCfg . CMLevelMusicPath , music_filename ) ;
2010-11-21 11:55:08 +00:00
2012-06-10 12:02:40 +00:00
if ( ! songs_play_file ( full_filename , ( ( GameCfg . CMLevelMusicPlayOrder = = MUSIC_CM_PLAYORDER_LEVEL ) ? 1 : 0 ) , ( ( GameCfg . CMLevelMusicPlayOrder = = MUSIC_CM_PLAYORDER_LEVEL ) ? NULL : jukebox_hook_next ) ) )
2010-11-21 11:55:08 +00:00
{
d_free ( full_filename ) ;
2010-04-05 11:54:23 +00:00
return 0 ; // whoops, got an error
2010-11-21 11:55:08 +00:00
}
2008-05-24 08:59:35 +00:00
// Formatting a pretty message
if ( strlen ( music_filename ) > = MUSIC_HUDMSG_MAXLEN ) {
2010-09-26 13:15:20 +00:00
strcpy ( hud_msg_buf , " ... " ) ;
strncat ( hud_msg_buf , & music_filename [ strlen ( music_filename ) - MUSIC_HUDMSG_MAXLEN ] , MUSIC_HUDMSG_MAXLEN ) ;
2008-05-24 08:59:35 +00:00
hud_msg_buf [ MUSIC_HUDMSG_MAXLEN + 3 ] = ' \0 ' ;
} else {
strcpy ( hud_msg_buf , music_filename ) ;
}
2010-07-13 06:35:25 +00:00
HUD_init_message ( HM_DEFAULT , " %s %s " , JUKEBOX_HUDMSG_PLAYING , hud_msg_buf ) ;
2009-03-03 12:55:27 +00:00
2010-11-21 11:55:08 +00:00
d_free ( full_filename ) ;
2009-03-03 12:55:27 +00:00
return 1 ;
2008-05-24 08:59:35 +00:00
}
char * jukebox_current ( ) {
2010-09-26 13:15:20 +00:00
return JukeboxSongs . list [ GameCfg . CMLevelMusicTrack [ 0 ] ] ;
2008-05-24 08:59:35 +00:00
}
2010-09-26 13:15:20 +00:00
int jukebox_is_loaded ( ) { return ( JukeboxSongs . list ! = NULL ) ; }
2010-06-14 08:13:16 +00:00
int jukebox_is_playing ( ) { return GameCfg . CMLevelMusicTrack [ 0 ] + 1 ; }
int jukebox_numtracks ( ) { return GameCfg . CMLevelMusicTrack [ 1 ] ; }
2008-05-24 08:59:35 +00:00
void jukebox_list ( ) {
2009-01-13 12:39:03 +00:00
int i ;
2010-09-26 13:15:20 +00:00
if ( ! JukeboxSongs . list ) return ;
if ( ! ( * JukeboxSongs . list ) ) {
2008-05-24 08:59:35 +00:00
con_printf ( CON_DEBUG , " * No songs have been found \n " ) ;
}
else {
2010-06-14 08:13:16 +00:00
for ( i = 0 ; i < GameCfg . CMLevelMusicTrack [ 1 ] ; i + + )
2010-09-26 13:15:20 +00:00
con_printf ( CON_DEBUG , " * %s \n " , JukeboxSongs . list [ i ] ) ;
2008-05-24 08:59:35 +00:00
}
}