View Issue Details

IDProjectCategoryView StatusLast Update
0002688FSSCPmodular tablespublic2012-07-15 18:46
ReporterMjnMixael Assigned Tom_m  
PrioritynormalSeveritycrashReproducibilityalways
Status resolvedResolutionfixed 
Product Version3.6.13 
Summary0002688: Long credits causes stack overflow crash.
DescriptionIf the total length of credits.tbl + code credits + credits.tbms is too long, FSO will crash when you enter the credits viewer.
Steps To ReproducePlace the attached tbm into data/tables and enter the credits viewer.
Additional InformationFrom IRC discussion with The_E...

<@The_E> MjnMixael: Would you be so kind as to throw it on Mantis, for the attention of whoever coded the credits.tbl modularization?
<@MjnMixael> sure
<@The_E> The crash cause is a stack overflow due to excessive amounts of text
<@MjnMixael> it was m|m, btw
<@The_E> This can be fixed by increasing the default stack size, but that needs a project file update
<@MjnMixael> waaaaaaay out of my knowledge here, but is it possible to split up tbms into separate "stacks" and just display them in order of loading, or does it just not work like that?
<@The_E> ___It does not work that way _
<@The_E> Err
<@The_E> It does not work that way currently, but that would indeed be another possible solution
TagsNo tags attached.

Activities

m_m

2012-07-14 18:30

developer   ~0013859

I added a patch which implements the suggested behavior and it seems to work as expected and without any crashes.

MjnMixael

2012-07-14 19:08

manager   ~0013860

Last edited: 2012-07-14 19:37

The patch fixes the crash and displays all the credits properly.

However it also breaks the following table options, as in they parse but don't have any visible effect.

$Text Scroll Rate:
$Artworks Display Time:
$Artworks Fade Time:

m_m

2012-07-14 20:14

developer  

credits.patch (8,708 bytes)   
Index: code/menuui/credits.cpp
===================================================================
--- code/menuui/credits.cpp	(revision 9013)
+++ code/menuui/credits.cpp	(working copy)
@@ -210,7 +210,7 @@
 static float Credits_artwork_display_time	= 9.0f;
 static float Credits_artwork_fade_time		= 1.0f;
 
-static SCP_string Credit_text;
+static SCP_vector<SCP_string> Credit_text_parts;
 
 static bool Credits_parsed;
 
@@ -309,11 +309,11 @@
 		}
 	}
 
-	parse_optional_float("$Text scroll rate:", &Credits_scroll_rate, 15.0f, 0.0f);
+	parse_optional_float("$Text scroll rate:", &Credits_scroll_rate, 0.0f);
 
-	parse_optional_float("$Artworks display time:", &Credits_artwork_display_time, 9.0f, 0.0f);
+	parse_optional_float("$Artworks display time:", &Credits_artwork_display_time, 0.0f);
 
-	parse_optional_float("$Artworks fade time:", &Credits_artwork_fade_time, 1.0f, 0.0f);
+	parse_optional_float("$Artworks fade time:", &Credits_artwork_fade_time, 0.0f);
 
 	if (optional_string("$SCP Credits position:"))
 	{
@@ -331,6 +331,7 @@
 
 	ignore_white_space();
 	
+	SCP_string credits_text;
 	SCP_string line;
 
 	SCP_vector<int> charNum;
@@ -348,7 +349,7 @@
 		// Also don't append the default credits anymore when there was already a parsed table
 		if(first_run && !Credits_parsed && !line.compare(mod_check))
 		{
-			Credit_text.append(unmodified_credits);
+			credits_text.append(unmodified_credits);
 		}
 
 		first_run = false;
@@ -356,7 +357,7 @@
 		if (line.empty())
 		{
 			// If the line is empty then just append a newline, don't bother with splitting it first
-			Credit_text.append("\n");
+			credits_text.append("\n");
 		}
 		else
 		{
@@ -376,12 +377,14 @@
 			// Now add all splitted lines to the credit text and append a newline to the end
 			for (int i = 0; i < numLines; i++)
 			{
-				Credit_text.append(SCP_string(lines[i], charNum[i]));
-				Credit_text.append("\n");
+				credits_text.append(SCP_string(lines[i], charNum[i]));
+				credits_text.append("\n");
 			}
 		}
 	}
 
+	Credit_text_parts.push_back(credits_text);
+
 	Credits_parsed = true;
 }
 
@@ -402,7 +405,7 @@
 
 void credits_init()
 {
-	int i, w, h;
+	int i;
 	credits_screen_buttons *b;
 
 	int credits_spooled_music_index = event_music_get_spooled_music_index("Cinema");	
@@ -418,9 +421,7 @@
 
 	Credits_frametime = 0;
 	Credits_last_time = timer_get_milliseconds();
-
-	Credit_text = "";
-
+	
 	// this is moved up here so we can override it if desired
 	Credits_artwork_index = rand() % NUM_IMAGES;
 
@@ -429,17 +430,19 @@
 	credits_parse();
 
 	if (!Credits_parsed)
-		Credit_text.assign("No credits available.\n");
+	{
+		Credit_text_parts.push_back(SCP_string("No credits available.\n"));
+	}
 	else
 	{
 		switch (SCP_credits_position)
 		{
 			case START:
-				Credit_text.insert(0, fs2_open_credit_text);
+				Credit_text_parts.insert(Credit_text_parts.begin(), fs2_open_credit_text);
 				break;
 
 			case END:
-				Credit_text.append(fs2_open_credit_text);
+				Credit_text_parts.push_back(fs2_open_credit_text);
 				break;
 
 			default:
@@ -449,117 +452,128 @@
 	}
 
 	int ch;
-	for (SCP_string::iterator ii = Credit_text.begin(); ii != Credit_text.end(); ++ii)
+	for (SCP_vector<SCP_string>::iterator iter = Credit_text_parts.begin(); iter != Credit_text_parts.end(); ++iter)
 	{
-		ch = *ii;
-		switch (ch)
+		for (SCP_string::iterator ii = iter->begin(); ii != iter->end(); ++ii)
 		{
-			case -4:
-				ch = 129;
-				break;
+			ch = *ii;
+			switch (ch)
+			{
+				case -4:
+					ch = 129;
+					break;
 
-			case -28:
-				ch = 132;
-				break;
+				case -28:
+					ch = 132;
+					break;
 
-			case -10:
-				ch = 148;
-				break;
+				case -10:
+					ch = 148;
+					break;
 
-			case -23:
-				ch = 130;
-				break;
+				case -23:
+					ch = 130;
+					break;
 
-			case -30:
-				ch = 131;
-				break;
+				case -30:
+					ch = 131;
+					break;
 
-			case -25:
-				ch = 135;
-				break;
+				case -25:
+					ch = 135;
+					break;
 
-			case -21:
-				ch = 137;
-				break;
+				case -21:
+					ch = 137;
+					break;
 
-			case -24:
-				ch = 138;
-				break;
+				case -24:
+					ch = 138;
+					break;
 
-			case -17:
-				ch = 139;
-				break;
+				case -17:
+					ch = 139;
+					break;
 
-			case -18:
-				ch = 140;
-				break;
+				case -18:
+					ch = 140;
+					break;
 
-			case -60:
-				ch = 142;
-				break;
+				case -60:
+					ch = 142;
+					break;
 
-			case -55:
-				ch = 144;
-				break;
+				case -55:
+					ch = 144;
+					break;
 
-			case -12:
-				ch = 147;
-				break;
+				case -12:
+					ch = 147;
+					break;
 
-			case -14:
-				ch = 149;
-				break;
+				case -14:
+					ch = 149;
+					break;
 
-			case -5:
-				ch = 150;
-				break;
+				case -5:
+					ch = 150;
+					break;
 
-			case -7:
-				ch = 151;
-				break;
+				case -7:
+					ch = 151;
+					break;
 
-			case -42:
-				ch = 153;
-				break;
+				case -42:
+					ch = 153;
+					break;
 
-			case -36:
-				ch = 154;
-				break;
+				case -36:
+					ch = 154;
+					break;
 
-			case -31:
-				ch = 160;
-				break;
+				case -31:
+					ch = 160;
+					break;
 
-			case -19:
-				ch = 161;
-				break;
+				case -19:
+					ch = 161;
+					break;
 
-			case -13:
-				ch = 162;
-				break;
+				case -13:
+					ch = 162;
+					break;
 
-			case -6:
-				ch = 163;
-				break;
+				case -6:
+					ch = 163;
+					break;
 
-			case -32:
-				ch = 133;
-				break;
+				case -32:
+					ch = 133;
+					break;
 
-			case -22:
-				ch = 136;
-				break;
+				case -22:
+					ch = 136;
+					break;
 
-			case -20:
-				ch = 141;
-				break;
+				case -20:
+					ch = 141;
+					break;
+			}
+
+			*ii = (char) ch;
 		}
+	}
 
-		*ii = (char) ch;
+	int temp_h;
+	int h = 0;
+
+	for (SCP_vector<SCP_string>::iterator iter = Credit_text_parts.begin(); iter != Credit_text_parts.end(); ++iter)
+	{
+		gr_get_string_size(NULL, &temp_h, iter->c_str(), iter->length());
+
+		h = h + temp_h;
 	}
 
-	gr_get_string_size(&w, &h, Credit_text.c_str(), Credit_text.length());
-
 	Credit_start_pos = i2fl(Credits_text_coords[gr_screen.res][CREDITS_H_COORD]);
 	Credit_stop_pos = -i2fl(h);
 	Credit_position = Credit_start_pos;
@@ -611,7 +625,7 @@
 
 	credits_stop_music();
 
-	Credit_text = "";
+	Credit_text_parts.clear();
 
 	if (Background_bitmap){
 		bm_release(Background_bitmap);
@@ -738,16 +752,29 @@
 	gr_set_clip(Credits_text_coords[gr_screen.res][CREDITS_X_COORD], Credits_text_coords[gr_screen.res][CREDITS_Y_COORD], Credits_text_coords[gr_screen.res][CREDITS_W_COORD], Credits_text_coords[gr_screen.res][CREDITS_H_COORD]);
 	gr_set_font(FONT1);
 	gr_set_color_fast(&Color_normal);
-
-	int sy;
+	
+	int sy; // The current position of the first text part
 	if ( Credit_position > 0 ) {
 		sy = fl2i(Credit_position+0.5f);
 	} else {
 		sy = fl2i(Credit_position-0.5f);
 	}
 
-	gr_string(0x8000, sy, Credit_text.c_str());
+	for (SCP_vector<SCP_string>::iterator iter = Credit_text_parts.begin(); iter != Credit_text_parts.end(); ++iter)
+	{
+		int height;
 
+		gr_get_string_size(NULL, &height, iter->c_str(), iter->length());
+
+		// Check if the text part is actually visible
+		if (sy + height > 0)
+		{
+			gr_string(0x8000, sy, iter->c_str());
+		}
+
+		sy = sy + height;
+	}
+
 	int temp_time;
 	temp_time = timer_get_milliseconds();
 
Index: code/parse/parselo.cpp
===================================================================
--- code/parse/parselo.cpp	(revision 9013)
+++ code/parse/parselo.cpp	(working copy)
@@ -4055,7 +4055,7 @@
 	return num_files;
 }
 
-bool parse_optional_float(const char *tag, float *destination, float def, float min, float max)
+bool parse_optional_float(const char *tag, float *destination, float min, float max)
 {
 	Assert(tag != NULL);
 	Assert(destination != NULL);
@@ -4079,8 +4079,6 @@
 	}
 	else
 	{
-		*destination = def;
-
 		return false;
 	}
 }
Index: code/parse/parselo.h
===================================================================
--- code/parse/parselo.h	(revision 9013)
+++ code/parse/parselo.h	(working copy)
@@ -269,11 +269,10 @@
  *
  * @param tag The optional tag that should be checked
  * @param destination The destination float where the value should be stored
- * @param def The default value which should be used when the tag isn't present
  * @param min The minimum value which will be accepted. Optional parameter; defaults to @c FLT_MIN
  * @param max The maximum value which will be accepted. Optional parameter; defaults to @c FLT_MAX
  * @return
  */
-bool parse_optional_float(const char *tag, float *destination, float def, float min = FLT_MIN, float max = FLT_MAX);
+bool parse_optional_float(const char *tag, float *destination, float min = FLT_MIN, float max = FLT_MAX);
 
 #endif
credits.patch (8,708 bytes)   

m_m

2012-07-14 20:15

developer  

fsu_credits-crd.tbm (7,780 bytes)

m_m

2012-07-14 20:27

developer   ~0013861

I updated the patch with another bugfix where values set in a table got overwritten with the default values when a table didn't contain the options.

Also restored the sample table which I accidentally deleted...

MjnMixael

2012-07-15 06:05

manager   ~0013863

I confirm all issues are fixed with the latest patch.

Goober5000

2012-07-15 06:18

administrator   ~0013864

Why does the patch remove the ability to set a default value for the optional float?

m_m

2012-07-15 08:16

developer   ~0013865

Because there is not way to know whether the variable has been set before so the value which got set before gets overwritten every time the function doesn't find the optional tag.

Goober5000

2012-07-15 18:45

administrator   ~0013867

I see what you mean. It also looks like whoever wrote parse_optional_float wasn't familiar with the parsing functions in general. I've rewritten that section slightly to use the conventional methods. I've also added better minimum values, because we don't want the credits to freeze or crash with a div-0 exception due to entering a value of 0.

Goober5000

2012-07-15 18:46

administrator   ~0013868

Marking as fixed due to testing, code review, and commit.

Related Changesets

fs2open: trunk r9017

2012-07-15 14:48

Goober5000


Ported: N/A

Details Diff
this is a slightly modified version of m!m's patch for Mantis 0002688 Affected Issues
0002688
mod - /trunk/fs2_open/code/menuui/credits.cpp Diff File
mod - /trunk/fs2_open/code/parse/parselo.cpp Diff File
mod - /trunk/fs2_open/code/parse/parselo.h Diff File

Issue History

Date Modified Username Field Change
2012-07-14 17:06 MjnMixael New Issue
2012-07-14 17:06 MjnMixael File Added: fsu_credits-crd.tbm
2012-07-14 18:28 m_m Assigned To => m_m
2012-07-14 18:28 m_m Status new => assigned
2012-07-14 18:30 m_m Note Added: 0013859
2012-07-14 18:30 m_m Status assigned => code review
2012-07-14 18:30 m_m File Added: credits.patch
2012-07-14 19:08 MjnMixael Note Added: 0013860
2012-07-14 19:37 MjnMixael Note Edited: 0013860
2012-07-14 20:14 m_m File Deleted: fsu_credits-crd.tbm
2012-07-14 20:14 m_m File Deleted: credits.patch
2012-07-14 20:14 m_m File Added: credits.patch
2012-07-14 20:15 m_m File Added: fsu_credits-crd.tbm
2012-07-14 20:27 m_m Note Added: 0013861
2012-07-15 06:05 MjnMixael Note Added: 0013863
2012-07-15 06:18 Goober5000 Note Added: 0013864
2012-07-15 08:16 m_m Note Added: 0013865
2012-07-15 18:45 Goober5000 Note Added: 0013867
2012-07-15 18:45 Goober5000 Changeset attached => fs2open trunk r9017
2012-07-15 18:46 Goober5000 Note Added: 0013868
2012-07-15 18:46 Goober5000 Status code review => resolved
2012-07-15 18:46 Goober5000 Resolution open => fixed