additional music checks

This adds several fixes to make the event and menu music more robust:
1. The game will now run with zero soundtracks.  All accesses into a potentially empty vector should now have checks.
2. Soundtracks and menu music are now checked for validity before being played (not just when importing a FSM mission).
3. The parsing validity checks and the in-game event music now check for skippable patterns using the same function.
4. Zero measures and zero samples-per-measure now cause the game to skip a pattern.  Negative values now trigger a warning.
5. Omitting some or all of the `$Name:` lines for a soundtrack will not cause an assert.

Fixes #5576.  Follow-up to #5572.
This commit is contained in:
Goober5000 committed 2023-08-25 19:22:03 -04:00
1 parent 9d28ba568c
commit 6a550a6701
6 files changed
+75 -50

No files matched your search

+47 -24
View File
@@ -257,6 +257,19 @@ SCP_vector<menu_music> Spooled_music;
int Mission_music[NUM_SCORES];
bool event_music_pattern_is_skippable(const SOUNDTRACK_PATTERN_INFO &pattern)
{
// check for blank or "none"
if (pattern.fname[0] == '\0' || !strnicmp(pattern.fname, NOX("none.wav"), 4))
return true;
// no content means this pattern isn't intended to play
if (pattern.num_measures == 0 || pattern.samples_per_measure == 0)
return true;
return false;
}
// -------------------------------------------------------------------------------------------------
// event_music_init()
//
@@ -294,7 +307,7 @@ void event_music_init()
// now that we've parsed everything, check the validity
for (auto &soundtrack : Soundtracks)
{
bool all_patterns_found = true;
bool all_patterns_valid = true;
// Goober5000 - set the valid flag according to whether we can load all our patterns
// (since someone may be running an enhanced music.tbl without warble_fs1 installed)
@@ -302,10 +315,18 @@ void event_music_init()
{
auto filename = soundtrack.patterns[i].fname;
// check for "none"
if (!strlen(filename) || !strnicmp(filename, "none", 4))
// some patterns aren't played for various legitimate reasons
if (event_music_pattern_is_skippable(soundtrack.patterns[i]))
continue;
// check for negative numbers (zero is allowed)
if (soundtrack.patterns[i].num_measures < 0 || soundtrack.patterns[i].samples_per_measure < 0)
{
Warning(LOCATION, "Soundtrack file %s has a negative 'num_measures' or 'samples_per_measure'; this is not allowed", soundtrack.patterns[i].fname);
all_patterns_valid = false;
break;
}
// check for file with exact extension
// (since event music specifies samples and measures, the audio format is important; so we don't want to assume any format will work)
if (!cf_exists_full(filename, CF_TYPE_MUSIC))
@@ -318,12 +339,12 @@ void event_music_init()
#endif
Warning(LOCATION, "One or more files in '%s' could not be loaded. The soundtrack will not be used.", soundtrack.name);
all_patterns_found = false;
all_patterns_valid = false;
break;
}
}
if (all_patterns_found)
if (all_patterns_valid)
soundtrack.flags |= EMF_VALID;
}
@@ -581,14 +602,11 @@ void event_music_level_start(int force_soundtrack)
if (force_soundtrack != -1)
Current_soundtrack_num = force_soundtrack;
// Check that soundtrack index is in range, except that < 0 means no soundtrack
if (Current_soundtrack_num < 0)
{
return;
}
Assert(Current_soundtrack_num >= 0 && Current_soundtrack_num < (int)Soundtracks.size());
if (Current_soundtrack_num < 0 || Current_soundtrack_num > (int)Soundtracks.size())
Assert(Current_soundtrack_num < (int)Soundtracks.size());
if (Current_soundtrack_num >= (int)Soundtracks.size())
return;
strack = &Soundtracks[Current_soundtrack_num];
@@ -601,7 +619,7 @@ void event_music_level_start(int force_soundtrack)
// tracks because their patterns weren't -1
for (i = 0; i < MAX_PATTERNS; i++)
{
if (!strnicmp(strack->patterns[i].fname, NOX("none.wav"), 4))
if (event_music_pattern_is_skippable(strack->patterns[i]))
{
Patterns[i].handle = -1;
continue;
@@ -1203,9 +1221,6 @@ void parse_soundtrack()
int i, strack_idx = -1;
bool nocreate = false;
//Start parsing soundtrack
//required_string("#Soundtrack Start");
//Get the name, and do we have this track already?
required_string("$SoundTrack Name:");
stuff_string(namebuf, F_NAME, NAME_LENGTH);
@@ -1599,7 +1614,7 @@ int hostile_ships_present()
// NOTE: callers to this function are advised to allocate a 256 byte buffer
void event_music_get_info(char *outbuf)
{
if ( Event_music_enabled == FALSE || Event_music_level_inited == FALSE || Current_pattern == -1 ) {
if ( Event_music_enabled == FALSE || Event_music_level_inited == FALSE || Current_soundtrack_num < 0 || Current_pattern < 0 ) {
strcpy(outbuf,XSTR( "Event music is not playing", 213));
}
else {
@@ -1618,7 +1633,7 @@ int event_music_next_soundtrack(int delta)
{
int new_soundtrack;
if ( Event_music_enabled == FALSE || Event_music_level_inited == FALSE ) {
if ( Event_music_enabled == FALSE || Event_music_level_inited == FALSE || Soundtracks.empty() ) {
return -1;
}
@@ -1662,7 +1677,7 @@ void event_sexp_change_soundtrack(const char *name)
// NOTE: callers to this function are advised to allocate a NAME_LENGTH buffer
void event_music_get_soundtrack_name(char *outbuf)
{
if ( Event_music_enabled == FALSE || Event_music_level_inited == FALSE ) {
if ( Event_music_enabled == FALSE || Event_music_level_inited == FALSE || Current_soundtrack_num < 0 ) {
strcpy(outbuf, XSTR( "Event music is not playing", 213));
}
else {
@@ -1673,11 +1688,15 @@ void event_music_get_soundtrack_name(char *outbuf)
// set the current soundtrack based on name
void event_music_set_soundtrack(const char *name)
{
Current_soundtrack_num = event_music_get_soundtrack_index(name);
int index = event_music_get_soundtrack_index(name);
if ( Current_soundtrack_num == -1 ) {
if (index >= 0 && Soundtracks[index].flags & EMF_VALID)
Current_soundtrack_num = index;
else
Current_soundtrack_num = -1;
if (Current_soundtrack_num == -1)
mprintf(("Current soundtrack set to -1 in event_music_set_soundtrack\n"));
}
}
int event_music_get_soundtrack_index(const char *name)
@@ -1694,7 +1713,7 @@ int event_music_get_soundtrack_index(const char *name)
int event_music_get_spooled_music_index(const char *name)
{
// find the correct index for the event music
// find the correct index for the spooled music
for ( int i = 0; i < (int)Spooled_music.size(); i++ ) {
if ( !stricmp(name, Spooled_music[i].name) ) {
return i;
@@ -1714,8 +1733,12 @@ void event_music_set_score(int score_index, const char *name)
{
Assert(score_index < NUM_SCORES);
// find the correct index for the event music
Mission_music[score_index] = event_music_get_spooled_music_index(name);
int index = event_music_get_spooled_music_index(name);
if (index >= 0 && Spooled_music[index].flags & SMF_VALID)
Mission_music[score_index] = index;
else
Mission_music[score_index] = -1;
}
// reset what sort of music is to be used for this mission
+6 -5
View File
@@ -74,15 +74,16 @@ extern SCP_vector<menu_music> Spooled_music;
// event music soundtrack storage
typedef struct tagSOUNDTRACK_PATTERN_INFO {
char fname[MAX_FILENAME_LEN];
float num_measures;
int samples_per_measure;
} SOUNDTRACK_PATTERN_INFO;
typedef struct tagSOUNDTRACK_INFO {
int flags;
int num_patterns;
char name[NAME_LENGTH];
struct {
char fname[MAX_FILENAME_LEN];
float num_measures;
int samples_per_measure;
} patterns[MAX_PATTERNS];
SOUNDTRACK_PATTERN_INFO patterns[MAX_PATTERNS];
} SOUNDTRACK_INFO;
// Goober5000 - event music flags
+13 -7
View File
@@ -1126,7 +1126,7 @@ void parse_briefing_info(mission * /*pm*/)
*/
void parse_music(mission *pm, int flags)
{
int index, num;
int index;
char *ch;
char temp[NAME_LENGTH];
@@ -1233,9 +1233,12 @@ void parse_music(mission *pm, int flags)
}
}
// last resort: pick a random track out of the 7 FS2 soundtracks
num = std::max((int)Soundtracks.size(), 7);
strcpy_s(pm->event_music_name, Soundtracks[Random::next(num)].name);
if (!Soundtracks.empty())
{
// last resort: pick a random track out of the 7 FS2 soundtracks
int num = std::max((int)Soundtracks.size(), 7);
strcpy_s(pm->event_music_name, Soundtracks[Random::next(num)].name);
}
done_event_music:
@@ -1260,9 +1263,12 @@ done_event_music:
if (event_music_get_spooled_music_index(pm->briefing_music_name) >= 0)
goto done_briefing_music;
// last resort: pick a random track out of the first 7 FS2 briefings (the regular ones)...
num = std::max((int)Spooled_music.size(), 7);
strcpy_s(pm->briefing_music_name, Spooled_music[Random::next(num)].name);
if (!Spooled_music.empty())
{
// last resort: pick a random track out of the first 7 FS2 briefings (the regular ones)...
int num = std::max((int)Spooled_music.size(), 7);
strcpy_s(pm->briefing_music_name, Spooled_music[Random::next(num)].name);
}
done_briefing_music:
+5 -5
View File
@@ -315,10 +315,10 @@ void set_active_ui(UI_WINDOW *ui_window)
Active_ui_window = ui_window;
}
SCP_string common_music_get_filename(int score_index)
const char *common_music_get_filename(int score_index)
{
if (Cmdline_freespace_no_music) {
return SCP_string();
return "";
}
Assertion(score_index >= 0 && score_index < NUM_SCORES, "Invalid score index %d.", score_index);
@@ -330,7 +330,7 @@ SCP_string common_music_get_filename(int score_index)
"No briefing music is selected, so play first briefing track: %s\n",
Spooled_music[Mission_music[score_index]].name));
} else {
return SCP_string();
return "";
}
}
@@ -341,11 +341,11 @@ void common_music_init(int score_index)
{
const auto file_name = common_music_get_filename(score_index);
if (file_name.empty()) {
if (file_name[0] == '\0') {
return;
}
briefing_load_music(file_name.c_str());
briefing_load_music(file_name);
// Use this id to trigger the start of music playing on the briefing screen
Briefing_music_begin_timestamp = ui_timestamp(BRIEFING_MUSIC_DELAY);
}
+1 -6
View File
@@ -106,15 +106,10 @@ void unload_wing_icons();
void common_flash_button_init();
int common_flash_bright();
// functions for the multiplayer chat window
void common_render_chat_window();
void multi_chat_scroll_up();
void multi_chat_scroll_down();
void set_active_ui(UI_WINDOW *ui_window);
// music functions exported for multiplayer team selection screen to start briefing music
SCP_string common_music_get_filename(int score_index);
const char *common_music_get_filename(int score_index);
void common_music_init( int score_index );
void common_music_do();
void common_music_close();
+3 -3
View File
@@ -570,7 +570,7 @@ ADE_FUNC(getBriefingMusicName,
"string",
"The file name or empty if no music")
{
return ade_set_args(L, "s", common_music_get_filename(SCORE_BRIEFING).c_str());
return ade_set_args(L, "s", common_music_get_filename(SCORE_BRIEFING));
}
ADE_FUNC(runBriefingStageHook,
@@ -956,7 +956,7 @@ ADE_FUNC(getDebriefingMusicName,
"string",
"The file name or empty if no music")
{
return ade_set_args(L, "s", common_music_get_filename(debrief_select_music()).c_str());
return ade_set_args(L, "s", common_music_get_filename(debrief_select_music()));
}
ADE_FUNC(getDebriefing, l_UserInterface_Debrief, nullptr, "Get the debriefing", "debriefing", "The debriefing data")
@@ -1228,7 +1228,7 @@ ADE_FUNC(getFictionMusicName, l_UserInterface_FictionViewer, nullptr,
"string",
"The file name or empty if no music")
{
return ade_set_args(L, "s", common_music_get_filename(SCORE_FICTION_VIEWER).c_str());
return ade_set_args(L, "s", common_music_get_filename(SCORE_FICTION_VIEWER));
}
//**********SUBLIBRARY: UserInterface/ShipWepSelect