Add path browsing feature to make song file/directory selection easier

This commit is contained in:
kreatordxx 2010-08-27 14:09:19 +00:00
parent 0bc2257b06
commit 72c62df583
5 changed files with 325 additions and 9 deletions

View file

@ -1,5 +1,9 @@
D1X-Rebirth Changelog
20100827
--------
arch/include/jukebox.h, arch/sdl/jukebox.c, include/u_mem.h, main/menu.c: Add path browsing feature to make song file/directory selection easier
20100825
--------
main/titles.c: Made loading of Hires briefings a bit more simpler (more D2-ish) and not using a new briefing_screen structure; Also perform a check if requested image originates from descent.hog or a third-party mission and do not load a Hires version in the latter case

View file

@ -1,6 +1,8 @@
#ifndef __JUKEBOX_H__
#define __JUKEBOX_H__
extern char *jukebox_exts[];
void jukebox_unload();
void jukebox_load();
int jukebox_play();

View file

@ -39,11 +39,12 @@ void jukebox_unload()
JukeboxSongs = NULL;
}
char *jukebox_exts[] = { ".mp3", ".ogg", ".wav", ".aif", ".mid", NULL };
/* Loads music file names from a given directory */
void jukebox_load()
{
int count;
char *music_exts[] = { ".mp3", ".ogg", ".wav", ".aif", ".mid", NULL };
static char curpath[PATH_MAX+1];
if (memcmp(curpath,GameCfg.CMLevelMusicPath,PATH_MAX) || (GameCfg.MusicType != MUSIC_TYPE_CUSTOM))
@ -78,7 +79,7 @@ void jukebox_load()
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 = PHYSFSX_findabsoluteFiles("", GameCfg.CMLevelMusicPath, music_exts);
JukeboxSongs = PHYSFSX_findabsoluteFiles("", GameCfg.CMLevelMusicPath, jukebox_exts);
if (JukeboxSongs != NULL)
{

View file

@ -30,6 +30,8 @@ extern void * mem_realloc( void * buffer, unsigned int size, char * var, char *
extern void mem_free( void * buffer );
extern char * mem_strdup(char * str, char * var, char * file, int line );
#define MEM_K 1.5 // Dynamic array growth factor
/* DPH: Changed malloc, etc. to d_malloc. Overloading system calls is very evil and error prone */
#define d_malloc(size) mem_malloc((size),"Unknown", __FILE__,__LINE__, 0 )
#define d_calloc(n,size) mem_malloc((n*size),"Unknown", __FILE__,__LINE__, 1 )

View file

@ -56,6 +56,7 @@ COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED.
#include "state.h"
#include "mission.h"
#include "songs.h"
#include "jukebox.h" // for jukebox_exts
#include "config.h"
#include "gauges.h"
#include "hudmsg.h" //for HUD_max_num_disp
@ -1126,10 +1127,293 @@ void do_graphics_menu()
} while( i>-1 );
}
int opt_sm_digivol = -1, opt_sm_musicvol = -1, opt_sm_revstereo = -1, opt_sm_mtype0 = -1, opt_sm_mtype1 = -1, opt_sm_mtype2 = -1, opt_sm_mtype3 = -1, opt_sm_redbook_playorder = -1, opt_sm_mtype3_lmpath = -1, opt_sm_mtype3_lmplayorder1 = -1, opt_sm_mtype3_lmplayorder2 = -1, opt_sm_cm_mtype3_file1 = -1, opt_sm_cm_mtype3_file2 = -1, opt_sm_cm_mtype3_file3 = -1, opt_sm_cm_mtype3_file4 = -1, opt_sm_cm_mtype3_file5 = -1;
#define CUR_DIRLIST "DirectoryListDXX"
typedef struct browser
{
char *title; // The title - needed for making another listbox when changing directory
int (*when_selected)(void *userdata, const char *filename); // What to do when something chosen
void *userdata; // Whatever you want passed to when_selected
char **list; // All menu items
char *list_buf; // Buffer for menu item text: hopefully reduces memory fragmentation this way
char **ext_list; // List of file extensions we're looking for (if looking for a music file many types are possible)
int select_dir; // Allow selecting the current directory (e.g. for Jukebox level song directory)
int num_files; // Number of list items found (including parent directory and current directory if selectable)
int max_files; // How many entries we can have before having to grow the array
int max_buf; // How much text we can have before having to grow the buffer
char view_path[PATH_MAX]; // The absolute path we're currently looking at
int new_path; // Whether the view_path is a new searchpath, if so, remove it when finished
} browser;
// Figure out if we need to remove the view_path searchpath when finished
// We don't, and shouldn't, if it was already there
void searchpath_matches(browser *b, const char *str)
{
if (!strcmp(b->view_path, str))
b->new_path = 0;
}
void list_dir_el(browser *b, const char *origdir, const char *fname)
{
char *ext;
char **i;
ext = strrchr(fname, '.');
if (ext)
for (i = b->ext_list; *i != NULL && stricmp(ext, *i); i++) {} // see if the file is of a type we want
if ((!strcmp(PHYSFS_getRealDir(fname), b->view_path)) && (PHYSFS_isDirectory(fname) || (ext && *i))
#if defined(__MACH__) && defined(__APPLE__)
&& stricmp(fname, "Volumes") // this messes things up, use '..' instead
#endif
)
{
char *next_path = b->list[b->num_files - 1] + strlen(b->list[b->num_files - 1]) + 1;
// Need to grow an array?
if (b->num_files >= b->max_files)
{
char **new_list = d_realloc(b->list, b->max_files*sizeof(char *)*MEM_K);
if (new_list == NULL)
return;
b->max_files *= MEM_K;
b->list = new_list;
}
if (next_path + strlen(fname) + 1 - b->list_buf >= b->max_buf)
{
char *new_buf = d_realloc(b->list_buf, b->max_buf*sizeof(char)*MEM_K);
if (new_buf == NULL)
return;
b->max_buf *= MEM_K;
b->list_buf = new_buf;
}
strcpy(next_path, fname);
b->list[b->num_files++] = next_path;
}
}
int list_directory(browser *b)
{
char *temp_buf, **temp_list;
int i, j;
b->max_files = 1024;
MALLOC(b->list, char *, 1024);
if (b->list == NULL)
return 0;
b->max_buf = 1024; // bigger?
MALLOC(b->list_buf, char, 1024);
if (b->list_buf == NULL)
{
d_free(b->list);
return 0;
}
strcpy(b->list_buf, ".."); // go to parent directory
b->list[b->num_files++] = b->list_buf;
if (b->select_dir)
{
b->list[b->num_files] = b->list[b->num_files - 1] + strlen(b->list[b->num_files - 1]) + 1;
strcpy(b->list[b->num_files++], "<this directory>"); // choose the directory being viewed
}
PHYSFS_enumerateFilesCallback("", (PHYSFS_EnumFilesCallback) list_dir_el, b);
// Reduce memory fragmentation
temp_list = d_realloc(b->list, sizeof(char *)*b->num_files);
if (temp_list)
b->list = temp_list;
temp_buf = d_realloc(b->list_buf, b->list[b->num_files - 1] + strlen(b->list[b->num_files - 1]) + 1 - b->list_buf);
if (temp_buf)
{
for (i = 0; i < b->num_files; i++)
b->list[i] += (temp_buf - b->list_buf);
b->list_buf = temp_buf;
}
// Sort by name, except the .. string and 'this directory', if applicable
qsort(&b->list[1 + (b->select_dir ? 1 : 0)], b->num_files - 1 - (b->select_dir ? 1 : 0), sizeof(char *), (int (*)( const void *, const void * ))fname_sort_func);
// Remove duplicates
// Can't do this before reallocating, otherwise it makes a mess of things (the strings in the buffer aren't ordered)
for (i = 1 + (b->select_dir ? 1 : 0), j = 1 + (b->select_dir ? 1 : 0); i < b->num_files; i++)
#ifdef __LINUX__
if (strcmp(b->list[i - 1], b->list[i]))
#else
if (stricmp(b->list[i - 1], b->list[i]))
#endif
b->list[j++] = b->list[i];
b->num_files = j;
return 1;
}
int select_file_recursive(char *title, const char *orig_path, char **ext_list, int select_dir, int (*when_selected)(void *userdata, const char *filename), void *userdata);
int select_file_handler(listbox *menu, d_event *event, browser *b)
{
char newpath[PATH_MAX];
char **list = listbox_get_items(menu);
int citem = listbox_get_citem(menu);
const char *sep = PHYSFS_getDirSeparator();
switch (event->type)
{
case EVENT_NEWMENU_SELECTED:
strcpy(newpath, b->view_path);
if (citem == 0) // go to parent dir
{
char *p;
if ((p = strstr(&newpath[strlen(newpath) - strlen(sep)], sep)))
if (p != strstr(newpath, sep)) // if this isn't the only separator (i.e. it's not about to look at the root)
*p = 0;
p = newpath + strlen(newpath) - 1;
while ((p > newpath) && strncmp(p, sep, strlen(sep))) // make sure full separator string is matched (typically is)
p--;
if (p == strstr(newpath, sep)) // Look at root directory next, if not already
{
#if defined(__MACH__) && defined(__APPLE__)
if (!stricmp(p, "/Volumes"))
return 1;
#endif
if (p[strlen(sep)] != '\0')
p[strlen(sep)] = '\0';
else
{
#if defined(__MACH__) && defined(__APPLE__)
// For Mac OS X, list all active volumes if we leave the root
strcpy(newpath, "/Volumes");
#else
return 1;
#endif
}
}
else
*p = '\0';
}
else if (citem == 1 && b->select_dir)
return !(*b->when_selected)(b->userdata, "");
else
{
if (strncmp(&newpath[strlen(newpath) - strlen(sep)], sep, strlen(sep)))
{
strncat(newpath, sep, PATH_MAX - 1 - strlen(newpath));
newpath[PATH_MAX - 1] = '\0';
}
strncat(newpath, list[citem], PATH_MAX - 1 - strlen(newpath));
newpath[PATH_MAX - 1] = '\0';
}
if ((citem == 0) || PHYSFS_isDirectory(list[citem]))
{
// If it fails, stay in this one
return !select_file_recursive(b->title, newpath, b->ext_list, b->select_dir, b->when_selected, b->userdata);
}
return !(*b->when_selected)(b->userdata, list[citem]);
break;
case EVENT_WINDOW_CLOSE:
if (b->new_path)
PHYSFS_removeFromSearchPath(b->view_path);
if (list)
d_free(list);
if (b->list_buf)
d_free(b->list_buf);
d_free(b);
break;
default:
break;
}
return 0;
}
int select_file_recursive(char *title, const char *orig_path, char **ext_list, int select_dir, int (*when_selected)(void *userdata, const char *filename), void *userdata)
{
browser *b;
const char *sep = PHYSFS_getDirSeparator();
char *p;
MALLOC(b, browser, 1);
if (!b)
return 0;
b->title = title;
b->when_selected = when_selected;
b->userdata = userdata;
b->ext_list = ext_list;
b->select_dir = select_dir;
b->num_files = b->max_files = 0;
b->view_path[0] = '\0';
b->new_path = 1;
// Set the viewing directory to orig_path, or some parent of it
if (orig_path && *orig_path)
{
strncpy(b->view_path, orig_path, PATH_MAX - 1);
b->view_path[PATH_MAX - 1] = '\0';
p = b->view_path + strlen(b->view_path) - 1;
PHYSFS_getSearchPathCallback((PHYSFS_StringCallback) searchpath_matches, b);
while (!PHYSFS_addToSearchPath(b->view_path, 0))
{
while ((p > b->view_path) && strncmp(p, sep, strlen(sep)))
p--;
*p = '\0';
if (p == b->view_path)
break;
PHYSFS_getSearchPathCallback((PHYSFS_StringCallback) searchpath_matches, b);
}
}
// Set to user directory if we couldn't find a searchpath
if (!b->view_path[0])
{
strncpy(b->view_path, PHYSFS_getUserDir(), PATH_MAX - 1);
b->view_path[PATH_MAX - 1] = '\0';
PHYSFS_getSearchPathCallback((PHYSFS_StringCallback) searchpath_matches, b);
if (!PHYSFS_addToSearchPath(b->view_path, 0))
{
d_free(b);
return 0;
}
}
if (!list_directory(b))
{
d_free(b);
return 0;
}
return newmenu_listbox1(title, b->num_files, b->list, 1, 0, (int (*)(listbox *, d_event *, void *))select_file_handler, b) >= 0;
}
int opt_sm_digivol = -1, opt_sm_musicvol = -1, opt_sm_revstereo = -1, opt_sm_mtype0 = -1, opt_sm_mtype1 = -1, opt_sm_mtype2 = -1, opt_sm_mtype3 = -1, opt_sm_redbook_playorder = -1, opt_sm_mtype3_lmpath = -1, opt_sm_mtype3_lmplayorder1 = -1, opt_sm_mtype3_lmplayorder2 = -1, opt_sm_cm_mtype3_file1_b = -1, opt_sm_cm_mtype3_file1 = -1, opt_sm_cm_mtype3_file2_b = -1, opt_sm_cm_mtype3_file2 = -1, opt_sm_cm_mtype3_file3_b = -1, opt_sm_cm_mtype3_file3 = -1, opt_sm_cm_mtype3_file4_b = -1, opt_sm_cm_mtype3_file4 = -1, opt_sm_cm_mtype3_file5_b = -1, opt_sm_cm_mtype3_file5 = -1;
void set_extmusic_volume(int volume);
int get_absolute_path(char *full_path, const char *rel_path)
{
PHYSFSX_getRealPath(rel_path, full_path);
return 1;
}
#define SELECT_SONG(t, s) select_file_recursive(t, GameCfg.CMMiscMusic[s], jukebox_exts, 0, (int (*)(void *, const char *))get_absolute_path, GameCfg.CMMiscMusic[s])
int sound_menuset(newmenu *menu, d_event *event, void *userdata)
{
newmenu_item *items = newmenu_get_items(menu);
@ -1194,6 +1478,24 @@ int sound_menuset(newmenu *menu, d_event *event, void *userdata)
break;
case EVENT_NEWMENU_SELECTED:
if (citem == opt_sm_mtype3_lmpath)
{
char *ext_list[] = { NULL }; // only select a directory (for now)
select_file_recursive("Select directory to\n play level music from",
GameCfg.CMLevelMusicPath, ext_list, 1, // look in current music path for ext_list files and allow directory selection
(int (*)(void *, const char *))get_absolute_path, GameCfg.CMLevelMusicPath); // just copy the absolute path
}
else if (citem == opt_sm_cm_mtype3_file1_b)
SELECT_SONG("Select main menu music", SONG_TITLE);
else if (citem == opt_sm_cm_mtype3_file2_b)
SELECT_SONG("Select briefing music", SONG_BRIEFING);
else if (citem == opt_sm_cm_mtype3_file3_b)
SELECT_SONG("Select credits music", SONG_CREDITS);
else if (citem == opt_sm_cm_mtype3_file4_b)
SELECT_SONG("Select escape sequence music", SONG_ENDLEVEL);
else if (citem == opt_sm_cm_mtype3_file5_b)
SELECT_SONG("Select game ending music", SONG_ENDGAME);
rval = 1; // stay in menu
break;
@ -1288,7 +1590,7 @@ void do_sound_menu()
m[nitems].type = NM_TYPE_TEXT; m[nitems++].text = "jukebox options:";
opt_sm_mtype3_lmpath = nitems;
m[nitems].type = NM_TYPE_TEXT; m[nitems++].text = "path to music used for levels";
m[nitems].type = NM_TYPE_MENU; m[nitems++].text = "path to music used for levels (...)";
m[nitems].type = NM_TYPE_INPUT; m[nitems].text = GameCfg.CMLevelMusicPath; m[nitems++].text_len = NM_MAX_TEXT_LEN-1;
@ -1306,27 +1608,32 @@ void do_sound_menu()
m[nitems].type = NM_TYPE_TEXT; m[nitems++].text = "non-level music:";
m[nitems].type = NM_TYPE_TEXT; m[nitems++].text = "main menu";
opt_sm_cm_mtype3_file1_b = nitems;
m[nitems].type = NM_TYPE_MENU; m[nitems++].text = "main menu (browse...)";
opt_sm_cm_mtype3_file1 = nitems;
m[nitems].type = NM_TYPE_INPUT; m[nitems].text = GameCfg.CMMiscMusic[SONG_TITLE]; m[nitems++].text_len = NM_MAX_TEXT_LEN-1;
m[nitems].type = NM_TYPE_TEXT; m[nitems++].text = "briefing";
opt_sm_cm_mtype3_file2_b = nitems;
m[nitems].type = NM_TYPE_MENU; m[nitems++].text = "briefing (browse...)";
opt_sm_cm_mtype3_file2 = nitems;
m[nitems].type = NM_TYPE_INPUT; m[nitems].text = GameCfg.CMMiscMusic[SONG_BRIEFING]; m[nitems++].text_len = NM_MAX_TEXT_LEN-1;
m[nitems].type = NM_TYPE_TEXT; m[nitems++].text = "credits";
opt_sm_cm_mtype3_file3_b = nitems;
m[nitems].type = NM_TYPE_MENU; m[nitems++].text = "credits (browse...)";
opt_sm_cm_mtype3_file3 = nitems;
m[nitems].type = NM_TYPE_INPUT; m[nitems].text = GameCfg.CMMiscMusic[SONG_CREDITS]; m[nitems++].text_len = NM_MAX_TEXT_LEN-1;
m[nitems].type = NM_TYPE_TEXT; m[nitems++].text = "escape sequence";
opt_sm_cm_mtype3_file4_b = nitems;
m[nitems].type = NM_TYPE_MENU; m[nitems++].text = "escape sequence (browse...)";
opt_sm_cm_mtype3_file4 = nitems;
m[nitems].type = NM_TYPE_INPUT; m[nitems].text = GameCfg.CMMiscMusic[SONG_ENDLEVEL]; m[nitems++].text_len = NM_MAX_TEXT_LEN-1;
m[nitems].type = NM_TYPE_TEXT; m[nitems++].text = "game ending";
opt_sm_cm_mtype3_file5_b = nitems;
m[nitems].type = NM_TYPE_MENU; m[nitems++].text = "game ending (browse...)";
opt_sm_cm_mtype3_file5 = nitems;
m[nitems].type = NM_TYPE_INPUT; m[nitems].text = GameCfg.CMMiscMusic[SONG_ENDGAME]; m[nitems++].text_len = NM_MAX_TEXT_LEN-1;