changeset 17838:8d2357ed5379 draft

(svn r22629) -Fix [FS#4599]: Remove all usages of the ErrorRefStack. It was continuously overwritten by e.g. industry prospection without closing the old error window; also StopTextRefStackUsage() was not called for errors returned by commands (which caused FS#4599). Now return in the CommandCost result whether the textref stack needs to be used, and store a copy of the stack values in the error window just like for the normal string parameters.
author frosch <frosch@openttd.org>
date Sun, 03 Jul 2011 14:32:15 +0000
parents b6608baed897
children 269813945876
files src/command.cpp src/command_type.h src/gui.h src/misc_gui.cpp src/newgrf_industries.cpp src/newgrf_industrytiles.cpp
diffstat 6 files changed, 93 insertions(+), 37 deletions(-) [+]
line wrap: on
line diff
--- a/src/command.cpp
+++ b/src/command.cpp
@@ -530,7 +530,7 @@
 		/* Only show the error when it's for us. */
 		StringID error_part1 = GB(cmd, 16, 16);
 		if (estimate_only || (IsLocalCompany() && error_part1 != 0 && my_cmd)) {
-			ShowErrorMessage(error_part1, res.GetErrorMessage(), WL_INFO, x, y);
+			ShowErrorMessage(error_part1, res.GetErrorMessage(), WL_INFO, x, y, res.GetTextRefStackSize(), res.GetTextRefStack());
 		}
 	} else if (estimate_only) {
 		ShowEstimatedCostOrIncome(res.GetCost(), x, y);
@@ -735,3 +735,25 @@
 		this->success = false;
 	}
 }
+
+/**
+ * Values to put on the #TextRefStack for the error message.
+ * There is only one static instance of the array, just like there is only one
+ * instance of normal DParams.
+ */
+uint32 CommandCost::textref_stack[16];
+
+/**
+ * Activate usage of the NewGRF #TextRefStack for the error message.
+ * @param number of entries to copy from the temporary NewGRF registers
+ */
+void CommandCost::UseTextRefStack(uint num_registers)
+{
+	extern TemporaryStorageArray<int32, 0x110> _temp_store;
+
+	assert(num_registers < lengthof(textref_stack));
+	this->textref_stack_size = num_registers;
+	for (uint i = 0; i < num_registers; i++) {
+		textref_stack[i] = _temp_store.GetValue(0x100 + i);
+	}
+}
--- a/src/command_type.h
+++ b/src/command_type.h
@@ -25,30 +25,33 @@
 	Money cost;       ///< The cost of this action
 	StringID message; ///< Warning message for when success is unset
 	bool success;     ///< Whether the comment went fine up to this moment
+	uint textref_stack_size;   ///< Number of uint32 values to put on the #TextRefStack for the error message.
+
+	static uint32 textref_stack[16];
 
 public:
 	/**
 	 * Creates a command cost return with no cost and no error
 	 */
-	CommandCost() : expense_type(INVALID_EXPENSES), cost(0), message(INVALID_STRING_ID), success(true) {}
+	CommandCost() : expense_type(INVALID_EXPENSES), cost(0), message(INVALID_STRING_ID), success(true), textref_stack_size(0) {}
 
 	/**
 	 * Creates a command return value the is failed with the given message
 	 */
-	explicit CommandCost(StringID msg) : expense_type(INVALID_EXPENSES), cost(0), message(msg), success(false) {}
+	explicit CommandCost(StringID msg) : expense_type(INVALID_EXPENSES), cost(0), message(msg), success(false), textref_stack_size(0) {}
 
 	/**
 	 * Creates a command cost with given expense type and start cost of 0
 	 * @param ex_t the expense type
 	 */
-	explicit CommandCost(ExpensesType ex_t) : expense_type(ex_t), cost(0), message(INVALID_STRING_ID), success(true) {}
+	explicit CommandCost(ExpensesType ex_t) : expense_type(ex_t), cost(0), message(INVALID_STRING_ID), success(true), textref_stack_size(0) {}
 
 	/**
 	 * Creates a command return value with the given start cost and expense type
 	 * @param ex_t the expense type
 	 * @param cst the initial cost of this command
 	 */
-	CommandCost(ExpensesType ex_t, const Money &cst) : expense_type(ex_t), cost(cst), message(INVALID_STRING_ID), success(true) {}
+	CommandCost(ExpensesType ex_t, const Money &cst) : expense_type(ex_t), cost(cst), message(INVALID_STRING_ID), success(true), textref_stack_size(0) {}
 
 
 	/**
@@ -100,6 +103,26 @@
 		this->message = message;
 	}
 
+	void UseTextRefStack(uint num_registers);
+
+	/**
+	 * Returns the number of uint32 values for the #TextRefStack of the error message.
+	 * @return number of uint32 values.
+	 */
+	uint GetTextRefStackSize() const
+	{
+		return this->textref_stack_size;
+	}
+
+	/**
+	 * Returns a pointer to the values for the #TextRefStack of the error message.
+	 * @return uint32 values for the #TextRefStack
+	 */
+	const uint32 *GetTextRefStack() const
+	{
+		return textref_stack;
+	}
+
 	/**
 	 * Returns the error message of a command
 	 * @return the error message, if succeeded #INVALID_STRING_ID
--- a/src/gui.h
+++ b/src/gui.h
@@ -63,7 +63,7 @@
 	WL_CRITICAL, ///< Critical errors, the MessageBox is shown in all cases
 };
 
-void ShowErrorMessage(StringID summary_msg, StringID detailed_msg, WarningLevel wl, int x = 0, int y = 0);
+void ShowErrorMessage(StringID summary_msg, StringID detailed_msg, WarningLevel wl, int x = 0, int y = 0, uint textref_stack_size = 0, const uint32 *textref_stack = NULL);
 
 void ShowExtraViewPortWindow(TileIndex tile = INVALID_TILE);
 void ShowExtraViewPortWindowForTileUnderCursor();
--- a/src/misc_gui.cpp
+++ b/src/misc_gui.cpp
@@ -566,6 +566,8 @@
 private:
 	uint duration;                  ///< Length of display of the message. 0 means forever,
 	uint64 decode_params[20];       ///< Parameters of the message strings.
+	uint textref_stack_size;        ///< Number of uint32 values to put on the #TextRefStack for the error message.
+	uint32 textref_stack[16];       ///< Values to put on the #TextRefStack for the error message.
 	StringID summary_msg;           ///< General error message showed in first line. Must be valid.
 	StringID detailed_msg;          ///< Detailed error message showed in second line. Can be #INVALID_STRING_ID.
 	uint height_summary;            ///< Height of the #summary_msg string in pixels in the #EMW_MESSAGE widget.
@@ -574,13 +576,17 @@
 	CompanyID face;                 ///< Company belonging to the face being shown. #INVALID_COMPANY if no face present.
 
 public:
-	ErrmsgWindow(Point pt, StringID summary_msg, StringID detailed_msg, bool no_timeout) : Window()
+	ErrmsgWindow(Point pt, StringID summary_msg, StringID detailed_msg, bool no_timeout, uint textref_stack_size, const uint32 *textref_stack) : Window()
 	{
 		this->position = pt;
 		this->duration = no_timeout ? 0 : _settings_client.gui.errmsg_duration;
 		CopyOutDParam(this->decode_params, 0, lengthof(this->decode_params));
 		this->summary_msg  = summary_msg;
 		this->detailed_msg = detailed_msg;
+		this->textref_stack_size = textref_stack_size;
+		if (textref_stack_size > 0) {
+			MemCpyT(this->textref_stack, textref_stack, textref_stack_size);
+		}
 
 		CompanyID company = (CompanyID)GetDParamX(this->decode_params, 2);
 		this->face = (this->detailed_msg == STR_ERROR_OWNED_BY && company < MAX_COMPANIES) ? company : INVALID_COMPANY;
@@ -596,17 +602,13 @@
 		if (widget != EMW_MESSAGE) return;
 
 		CopyInDParam(0, this->decode_params, lengthof(this->decode_params));
-		/* If the error message comes from a NewGRF, we must use the text ref. stack reserved for error messages.
-		 * If the message doesn't come from a NewGRF, it won't use the TTDP-style text ref. stack, so we won't hurt anything
-		 */
-		SwitchToErrorRefStack();
-		RewindTextRefStack();
+		if (this->textref_stack_size > 0) StartTextRefStackUsage(this->textref_stack_size, this->textref_stack);
 
 		int text_width = max(0, (int)size->width - WD_FRAMETEXT_LEFT - WD_FRAMETEXT_RIGHT);
 		this->height_summary  = GetStringHeight(this->summary_msg, text_width);
 		this->height_detailed = (this->detailed_msg == INVALID_STRING_ID) ? 0 : GetStringHeight(this->detailed_msg, text_width);
 
-		SwitchToNormalRefStack(); // Switch back to the normal text ref. stack for NewGRF texts.
+		if (this->textref_stack_size > 0) StopTextRefStackUsage();
 
 		uint panel_height = WD_FRAMERECT_TOP + this->height_summary + WD_FRAMERECT_BOTTOM;
 		if (this->detailed_msg != INVALID_STRING_ID) panel_height += this->height_detailed + WD_PAR_VSEP_WIDE;
@@ -672,8 +674,7 @@
 
 			case EMW_MESSAGE:
 				CopyInDParam(0, this->decode_params, lengthof(this->decode_params));
-				SwitchToErrorRefStack();
-				RewindTextRefStack();
+				if (this->textref_stack_size > 0) StartTextRefStackUsage(this->textref_stack_size, this->textref_stack);
 
 				if (this->detailed_msg == INVALID_STRING_ID) {
 					DrawStringMultiLine(r.left + WD_FRAMETEXT_LEFT, r.right - WD_FRAMETEXT_RIGHT, r.top + WD_FRAMERECT_TOP, r.bottom - WD_FRAMERECT_BOTTOM,
@@ -691,7 +692,7 @@
 					DrawStringMultiLine(r.left + WD_FRAMETEXT_LEFT, r.right - WD_FRAMETEXT_RIGHT, top, bottom, this->detailed_msg, TC_WHITE, SA_CENTER);
 				}
 
-				SwitchToNormalRefStack(); // Switch back to the normal text ref. stack for NewGRF texts.
+				if (this->textref_stack_size > 0) StopTextRefStackUsage();
 				break;
 
 			default:
@@ -734,19 +735,28 @@
  * @param wl           Message severity.
  * @param x            World X position (TileVirtX) of the error location. Set both x and y to 0 to just center the message when there is no related error tile.
  * @param y            World Y position (TileVirtY) of the error location. Set both x and y to 0 to just center the message when there is no related error tile.
+ * @param textref_stack_size Number of uint32 values to put on the #TextRefStack for the error message; 0 if the #TextRefStack shall not be used.
+ * @param textref_stack Values to put on the #TextRefStack.
  */
-void ShowErrorMessage(StringID summary_msg, StringID detailed_msg, WarningLevel wl, int x, int y)
+void ShowErrorMessage(StringID summary_msg, StringID detailed_msg, WarningLevel wl, int x, int y, uint textref_stack_size, const uint32 *textref_stack)
 {
+	assert(textref_stack_size == 0 || textref_stack != NULL);
 	if (summary_msg == STR_NULL) summary_msg = STR_EMPTY;
 
 	if (wl != WL_INFO) {
 		/* Print message to console */
 		char buf[DRAW_STRING_BUFFER];
+
+		if (textref_stack_size > 0) StartTextRefStackUsage(textref_stack_size, textref_stack);
+
 		char *b = GetString(buf, summary_msg, lastof(buf));
 		if (detailed_msg != INVALID_STRING_ID) {
 			b += seprintf(b, lastof(buf), " ");
 			GetString(b, detailed_msg, lastof(buf));
 		}
+
+		if (textref_stack_size > 0) StopTextRefStackUsage();
+
 		switch (wl) {
 			case WL_WARNING: IConsolePrint(CC_WARNING, buf); break;
 			default:         IConsoleError(buf); break;
@@ -760,7 +770,7 @@
 	DeleteWindowById(WC_ERRMSG, 0);
 
 	Point pt = {x, y};
-	new ErrmsgWindow(pt, summary_msg, detailed_msg, no_timeout);
+	new ErrmsgWindow(pt, summary_msg, detailed_msg, no_timeout, textref_stack_size, textref_stack);
 }
 
 /**
--- a/src/newgrf_industries.cpp
+++ b/src/newgrf_industries.cpp
@@ -549,18 +549,18 @@
 	uint16 result = group->GetCallbackResult();
 	if (result == 0x400 || result == CALLBACK_FAILED) return CommandCost();
 
-	/* Copy some parameters from the registers to the error message text ref. stack */
-	SwitchToErrorRefStack();
-	StartTextRefStackUsage(4);
-	SwitchToNormalRefStack();
-
+	CommandCost res;
 	switch (result) {
-		case 0x401: return_cmd_error(STR_ERROR_SITE_UNSUITABLE);
-		case 0x402: return_cmd_error(STR_ERROR_CAN_ONLY_BE_BUILT_IN_RAINFOREST);
-		case 0x403: return_cmd_error(STR_ERROR_CAN_ONLY_BE_BUILT_IN_DESERT);
-		default:    return_cmd_error(GetGRFStringID(indspec->grf_prop.grffile->grfid, 0xD000 + result));
+		case 0x401: res = CommandCost(STR_ERROR_SITE_UNSUITABLE);
+		case 0x402: res = CommandCost(STR_ERROR_CAN_ONLY_BE_BUILT_IN_RAINFOREST);
+		case 0x403: res = CommandCost(STR_ERROR_CAN_ONLY_BE_BUILT_IN_DESERT);
+		default:    res = CommandCost(GetGRFStringID(indspec->grf_prop.grffile->grfid, 0xD000 + result));
 	}
-	NOT_REACHED();
+
+	/* Copy some parameters from the registers to the error message text ref. stack */
+	res.UseTextRefStack(4);
+
+	return res;
 }
 
 /**
--- a/src/newgrf_industrytiles.cpp
+++ b/src/newgrf_industrytiles.cpp
@@ -308,17 +308,18 @@
 	}
 	if (callback_res == 0x400) return CommandCost();
 
-	/* Copy some parameters from the registers to the error message text ref. stack */
-	SwitchToErrorRefStack();
-	StartTextRefStackUsage(4);
-	SwitchToNormalRefStack();
-
+	CommandCost res;
 	switch (callback_res) {
-		case 0x401: return_cmd_error(STR_ERROR_SITE_UNSUITABLE);
-		case 0x402: return_cmd_error(STR_ERROR_CAN_ONLY_BE_BUILT_IN_RAINFOREST);
-		case 0x403: return_cmd_error(STR_ERROR_CAN_ONLY_BE_BUILT_IN_DESERT);
-		default:    return_cmd_error(GetGRFStringID(its->grf_prop.grffile->grfid, 0xD000 + callback_res));
+		case 0x401: res = CommandCost(STR_ERROR_SITE_UNSUITABLE);
+		case 0x402: res = CommandCost(STR_ERROR_CAN_ONLY_BE_BUILT_IN_RAINFOREST);
+		case 0x403: res = CommandCost(STR_ERROR_CAN_ONLY_BE_BUILT_IN_DESERT);
+		default:    res = CommandCost(GetGRFStringID(its->grf_prop.grffile->grfid, 0xD000 + callback_res));
 	}
+
+	/* Copy some parameters from the registers to the error message text ref. stack */
+	res.UseTextRefStack(4);
+
+	return res;
 }
 
 /* Simple wrapper for GetHouseCallback to keep the animation unified. */