Changeset - r17828:bcc2d03aefbe
[Not reviewed]
master
0 6 0
frosch - 13 years ago 2011-07-03 14:32:15
frosch@openttd.org
(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.
6 files changed with 93 insertions and 37 deletions:
0 comments (0 inline, 0 general)
src/command.cpp
Show inline comments
 
@@ -527,13 +527,13 @@ bool DoCommandP(TileIndex tile, uint32 p
 

	
 
	CommandCost res = DoCommandPInternal(tile, p1, p2, cmd, callback, text, my_cmd, estimate_only);
 
	if (res.Failed()) {
 
		/* 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);
 
	} else if (!only_sending && res.GetCost() != 0 && tile != 0 && IsLocalCompany() && _game_mode != GM_EDITOR) {
 
		/* Only show the cost animation when we did actually
 
		 * execute the command, i.e. we're not sending it to
 
@@ -732,6 +732,28 @@ void CommandCost::AddCost(const CommandC
 
	this->AddCost(ret.cost);
 
	if (this->success && !ret.success) {
 
		this->message = ret.message;
 
		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);
 
	}
 
}
src/command_type.h
Show inline comments
 
@@ -22,36 +22,39 @@
 
 */
 
class CommandCost {
 
	ExpensesType expense_type; ///< the type of expence as shown on the finances view
 
	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) {}
 

	
 

	
 
	/**
 
	 * Adds the given cost to the cost of the command.
 
	 * @param cost the cost to add
 
	 */
 
@@ -97,12 +100,32 @@ public:
 
	{
 
		assert(message != INVALID_STRING_ID);
 
		this->success = false;
 
		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
 
	 */
 
	StringID GetErrorMessage() const
 
	{
src/gui.h
Show inline comments
 
@@ -60,13 +60,13 @@ enum WarningLevel {
 
	WL_INFO,     ///< Used for DoCommand-like (and some nonfatal AI GUI) errors/information
 
	WL_WARNING,  ///< Other information
 
	WL_ERROR,    ///< Errors (eg. saving/loading failed)
 
	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();
 

	
 
/* bridge_gui.cpp */
 
void ShowBuildBridgeWindow(TileIndex start, TileIndex end, TransportType transport_type, byte bridge_type);
src/misc_gui.cpp
Show inline comments
 
@@ -563,27 +563,33 @@ static const WindowDesc _errmsg_face_des
 

	
 
/** Window class for displaying an error message window. */
 
struct ErrmsgWindow : public Window {
 
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.
 
	uint height_detailed;           ///< Height of the #detailed_msg string in pixels in the #EMW_MESSAGE widget.
 
	Point position;                 ///< Position of the error message window.
 
	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;
 
		const WindowDesc *desc = (face == INVALID_COMPANY) ? &_errmsg_desc : &_errmsg_face_desc;
 

	
 
		assert(summary_msg != INVALID_STRING_ID);
 
@@ -593,23 +599,19 @@ public:
 

	
 
	virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
 
	{
 
		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;
 

	
 
		size->height = max(size->height, panel_height);
 
	}
 
@@ -669,14 +671,13 @@ public:
 
				DrawCompanyManagerFace(c->face, c->colour, r.left, r.top);
 
				break;
 
			}
 

	
 
			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,
 
							this->summary_msg, TC_FROMSTRING, SA_CENTER);
 
				} else {
 
					int extra = (r.bottom - r.top + 1 - this->height_summary - this->height_detailed - WD_PAR_VSEP_WIDE) / 2;
 
@@ -688,13 +689,13 @@ public:
 

	
 
					bottom = r.bottom - WD_FRAMERECT_BOTTOM;
 
					top = bottom - this->height_detailed - extra;
 
					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:
 
				break;
 
		}
 
	}
 
@@ -731,25 +732,34 @@ public:
 
 * Display an error message in a window.
 
 * @param summary_msg  General error message showed in first line. Must be valid.
 
 * @param detailed_msg Detailed error message showed in second line. Can be INVALID_STRING_ID.
 
 * @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;
 
		}
 
	}
 

	
 
@@ -757,13 +767,13 @@ void ShowErrorMessage(StringID summary_m
 

	
 
	if (_settings_client.gui.errmsg_duration == 0 && !no_timeout) return;
 

	
 
	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);
 
}
 

	
 
/**
 
 * Display estimated costs.
 
 * @param cost Estimated cost (or income if negative).
 
 * @param x    X position of the notification window.
src/newgrf_industries.cpp
Show inline comments
 
@@ -546,24 +546,24 @@ CommandCost CheckIfCallBackAllowsCreatio
 
	/* Unlike the "normal" cases, not having a valid result means we allow
 
	 * the building of the industry, as that's how it's done in TTDP. */
 
	if (group == NULL) return CommandCost();
 
	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;
 
}
 

	
 
/**
 
 * Check with callback #CBM_IND_AVAILABLE whether the industry can be built.
 
 * @param type Industry type to check.
 
 * @param creation_type Reason to construct a new industry.
src/newgrf_industrytiles.cpp
Show inline comments
 
@@ -305,23 +305,24 @@ CommandCost PerformIndustryTileSlopeChec
 
	if (its->grf_prop.grffile->grf_version < 7) {
 
		if (callback_res != 0) return CommandCost();
 
		return_cmd_error(STR_ERROR_SITE_UNSUITABLE);
 
	}
 
	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. */
 
uint16 GetSimpleIndustryCallback(CallbackID callback, uint32 param1, uint32 param2, const IndustryTileSpec *spec, Industry *ind, TileIndex tile)
 
{
 
	return GetIndustryTileCallback(callback, param1, param2, spec - GetIndustryTileSpec(0), ind, tile);
0 comments (0 inline, 0 general)