2009-01-13 12:39:03 +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>
2009-01-13 12:39:03 +00:00
# include <string.h>
2010-09-02 00:07:37 +00:00
2009-01-13 12:39:03 +00:00
# include "physfsx.h"
# include "args.h"
# include "hudmsg.h"
2012-06-10 12:02:43 +00:00
# include "songs.h"
2009-01-13 12:39:03 +00:00
# include "jukebox.h"
2012-07-07 18:35:06 +00:00
# include "dxxerror.h"
2009-01-13 12:39:03 +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:35 +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 } ;
2009-01-13 12:39:03 +00:00
void jukebox_unload ( )
{
2010-09-26 13:15:20 +00:00
if ( JukeboxSongs . list_buf )
2010-10-10 10:48:24 +00:00
{
2010-09-26 13:15:20 +00:00
d_free ( JukeboxSongs . list_buf ) ;
2010-10-10 10:48:24 +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 ;
}
2013-11-26 22:19:52 +00:00
const file_extension_t jukebox_exts [ 7 ] = { SONG_EXT_HMP , SONG_EXT_MID , SONG_EXT_OGG , SONG_EXT_FLAC , SONG_EXT_MP3 , " " } ;
2009-01-13 12:39:03 +00:00
2013-10-27 22:00:14 +00:00
static int read_m3u ( void )
2010-09-26 13:15:20 +00:00
{
FILE * fp ;
int length ;
char * buf ;
2013-12-08 18:59:27 +00:00
char abspath [ PATH_MAX ] ;
2011-06-01 07:59:55 +00:00
if ( PHYSFSX_exists ( GameCfg . CMLevelMusicPath , 0 ) ) // it's a child of Sharepath, build full path
2011-04-18 12:32:36 +00:00
PHYSFSX_getRealPath ( GameCfg . CMLevelMusicPath , abspath ) ;
else
{
strncpy ( abspath , GameCfg . CMLevelMusicPath , PATH_MAX - 1 ) ;
abspath [ PATH_MAX - 1 ] = ' \0 ' ;
}
fp = fopen ( abspath , " rb " ) ;
2010-09-26 13:15:20 +00:00
if ( ! fp )
2011-04-18 12:32:36 +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:32:36 +00:00
return 0 ;
2010-09-26 13:15:20 +00:00
}
2009-01-13 12:39:03 +00:00
2010-09-26 13:15:20 +00:00
fseek ( fp , 0 , SEEK_SET ) ;
if ( ! fread ( JukeboxSongs . list_buf , length , 1 , fp ) )
2009-01-13 12:39:03 +00:00
{
2010-09-26 13:15:20 +00:00
d_free ( JukeboxSongs . list_buf ) ;
fclose ( fp ) ;
2011-04-18 12:32:36 +00:00
return 0 ;
2009-01-13 12:39:03 +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:32:36 +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:11:25 +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 )
{
2012-11-11 22:12:51 +00:00
char * * new_list = reinterpret_cast < char * * > ( d_realloc ( JukeboxSongs . list , JukeboxSongs . max_buf * sizeof ( char * ) * MEM_K ) ) ;
2010-09-26 13:15:20 +00:00
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:32:36 +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:35 +00:00
static int jukebox_init = 1 ;
2009-01-13 12:39:03 +00:00
2010-11-21 11:55:35 +00:00
// initialize JukeboxSongs structure once per runtime
if ( jukebox_init )
2009-01-13 12:39:03 +00:00
{
2010-11-21 11:55:35 +00:00
JukeboxSongs . list = NULL ;
JukeboxSongs . num_songs = JukeboxSongs . max_songs = JukeboxSongs . max_buf = 0 ;
jukebox_init = 0 ;
2009-01-13 12:39:03 +00:00
}
2010-11-21 11:55:35 +00:00
jukebox_unload ( ) ;
2009-01-13 12:39:03 +00:00
2011-04-18 12:32:36 +00:00
// Check if it's an M3U file
2012-05-18 23:36:43 +00:00
if ( ! d_stricmp ( & GameCfg . CMLevelMusicPath [ strlen ( GameCfg . CMLevelMusicPath ) - 4 ] , " .m3u " ) )
2011-04-18 12:32:36 +00:00
read_m3u ( ) ;
else // a directory
2010-06-14 08:13:16 +00:00
{
2011-04-18 12:32:36 +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:32:36 +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:32:36 +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:35 +00:00
// Read directory using PhysicsFS
2011-04-18 12:32:36 +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:35 +00:00
if ( ! JukeboxSongs . list )
2010-09-26 13:15:20 +00:00
{
2010-11-21 11:55:35 +00:00
if ( new_path )
PHYSFS_removeFromSearchPath ( GameCfg . CMLevelMusicPath ) ;
return ;
2009-01-13 12:39:03 +00:00
}
2010-11-21 11:55:35 +00:00
2011-01-20 11:17:30 +00:00
for ( i = 0 ; JukeboxSongs . list [ i ] ; i + + ) { }
2010-11-21 11:55:35 +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:35 +00:00
}
if ( JukeboxSongs . num_songs )
{
2013-12-07 00:47:27 +00:00
con_printf ( CON_DEBUG , " Jukebox: %d music file(s) found in %s " , JukeboxSongs . num_songs , GameCfg . CMLevelMusicPath ) ;
2010-11-21 11:55:35 +00:00
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
}
2009-01-13 12:39:03 +00:00
}
2010-11-21 11:55:35 +00:00
else
{
GameCfg . CMLevelMusicTrack [ 0 ] = - 1 ;
GameCfg . CMLevelMusicTrack [ 1 ] = - 1 ;
2013-12-07 00:47:27 +00:00
con_printf ( CON_DEBUG , " Jukebox music could not be found! " ) ;
2010-11-21 11:55:35 +00:00
}
2009-01-13 12:39:03 +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.
2013-10-27 22:00:14 +00:00
static void jukebox_hook_next ( )
2010-06-14 08:13:16 +00:00
{
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:30 +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 ( ) ;
}
2009-01-13 12:39:03 +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 ( )
{
2013-12-03 22:20:09 +00:00
const char * music_filename ;
2012-09-02 22:45:13 +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 ;
2009-01-13 12:39:03 +00:00
2013-12-03 22:12:42 +00:00
size_t size_music_filename = strlen ( music_filename ) ;
size_full_filename = strlen ( GameCfg . CMLevelMusicPath ) + size_music_filename + 1 ;
2013-12-08 18:22:17 +00:00
RAIIdmem < char > full_filename ;
2013-07-13 02:31:27 +00:00
CALLOC ( full_filename , char , size_full_filename ) ;
2013-12-03 22:12:42 +00:00
const char * LevelMusicPath ;
2012-05-18 23:36:43 +00:00
if ( ! d_stricmp ( & GameCfg . CMLevelMusicPath [ strlen ( GameCfg . CMLevelMusicPath ) - 4 ] , " .m3u " ) ) // if it's from an M3U playlist
2013-12-03 22:12:42 +00:00
LevelMusicPath = " " ;
2011-04-18 12:32:36 +00:00
else // if it's from a specified path
2013-12-03 22:12:42 +00:00
LevelMusicPath = GameCfg . CMLevelMusicPath ;
snprintf ( full_filename , size_full_filename , " %s%s " , LevelMusicPath , music_filename ) ;
2010-11-21 11:55:35 +00:00
2013-12-03 22:12:42 +00:00
int played = songs_play_file ( full_filename , ( ( GameCfg . CMLevelMusicPlayOrder = = MUSIC_CM_PLAYORDER_LEVEL ) ? 1 : 0 ) , ( ( GameCfg . CMLevelMusicPlayOrder = = MUSIC_CM_PLAYORDER_LEVEL ) ? NULL : jukebox_hook_next ) ) ;
2013-12-08 18:22:17 +00:00
full_filename = NULL ;
2013-12-03 22:12:42 +00:00
if ( ! played )
2010-11-21 11:55:35 +00:00
{
2010-04-05 11:54:23 +00:00
return 0 ; // whoops, got an error
2010-11-21 11:55:35 +00:00
}
2009-01-13 12:39:03 +00:00
// Formatting a pretty message
2013-12-03 22:20:09 +00:00
const char * prefix = " ... " ;
2013-12-03 22:12:42 +00:00
if ( size_music_filename > = MUSIC_HUDMSG_MAXLEN ) {
2013-12-03 22:20:09 +00:00
music_filename + = size_music_filename - MUSIC_HUDMSG_MAXLEN ;
2009-01-13 12:39:03 +00:00
} else {
2013-12-03 22:20:09 +00:00
prefix + = 3 ;
2009-01-13 12:39:03 +00:00
}
2013-12-03 22:20:09 +00:00
HUD_init_message ( HM_DEFAULT , " %s %s%s " , JUKEBOX_HUDMSG_PLAYING , prefix , music_filename ) ;
2009-03-03 12:55:27 +00:00
return 1 ;
2009-01-13 12:39:03 +00:00
}
char * jukebox_current ( ) {
2010-09-26 13:15:20 +00:00
return JukeboxSongs . list [ GameCfg . CMLevelMusicTrack [ 0 ] ] ;
2009-01-13 12:39:03 +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 ] ; }
2009-01-13 12:39:03 +00:00
void jukebox_list ( ) {
int i ;
2010-09-26 13:15:20 +00:00
if ( ! JukeboxSongs . list ) return ;
if ( ! ( * JukeboxSongs . list ) ) {
2013-12-07 00:47:27 +00:00
con_printf ( CON_DEBUG , " * No songs have been found " ) ;
2009-01-13 12:39:03 +00:00
}
else {
2010-06-14 08:13:16 +00:00
for ( i = 0 ; i < GameCfg . CMLevelMusicTrack [ 1 ] ; i + + )
2013-12-07 00:47:27 +00:00
con_printf ( CON_DEBUG , " * %s " , JukeboxSongs . list [ i ] ) ;
2009-01-13 12:39:03 +00:00
}
}