From ef5b18ab3b818a16a8297e4ecd4bf54bb7255e49 Mon Sep 17 00:00:00 2001 From: frosch Date: Sun, 3 Jul 2011 14:32:15 +0000 Subject: [PATCH] (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. --- src/command.cpp | 24 +++++++++++++++++++++++- src/command_type.h | 31 +++++++++++++++++++++++++++---- src/gui.h | 2 +- src/misc_gui.cpp | 34 ++++++++++++++++++++++------------ src/newgrf_industries.cpp | 20 ++++++++++---------- src/newgrf_industrytiles.cpp | 19 ++++++++++--------- 6 files changed, 93 insertions(+), 37 deletions(-) diff --git a/src/command.cpp b/src/command.cpp index 61e570da9b..b291ce7977 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -530,7 +530,7 @@ bool DoCommandP(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, CommandCallbac /* 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 @@ void CommandCost::AddCost(const CommandCost &ret) 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 _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); + } +} diff --git a/src/command_type.h b/src/command_type.h index 179f3be2b1..606bfc2870 100644 --- a/src/command_type.h +++ b/src/command_type.h @@ -25,30 +25,33 @@ class CommandCost { 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 @@ public: 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 diff --git a/src/gui.h b/src/gui.h index 68ae76d0ca..823423575f 100644 --- a/src/gui.h +++ b/src/gui.h @@ -63,7 +63,7 @@ enum WarningLevel { 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(); diff --git a/src/misc_gui.cpp b/src/misc_gui.cpp index bf49162789..d2bc2be0fb 100644 --- a/src/misc_gui.cpp +++ b/src/misc_gui.cpp @@ -566,6 +566,8 @@ 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. @@ -574,13 +576,17 @@ private: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: * @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 @@ void ShowErrorMessage(StringID summary_msg, StringID detailed_msg, WarningLevel 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); } /** diff --git a/src/newgrf_industries.cpp b/src/newgrf_industries.cpp index 5cf9eb6ae7..291d1a20f2 100644 --- a/src/newgrf_industries.cpp +++ b/src/newgrf_industries.cpp @@ -549,18 +549,18 @@ CommandCost CheckIfCallBackAllowsCreation(TileIndex tile, IndustryType type, uin 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; } /** diff --git a/src/newgrf_industrytiles.cpp b/src/newgrf_industrytiles.cpp index efaa5c3f67..522296530d 100644 --- a/src/newgrf_industrytiles.cpp +++ b/src/newgrf_industrytiles.cpp @@ -308,17 +308,18 @@ CommandCost PerformIndustryTileSlopeCheck(TileIndex ind_base_tile, TileIndex ind } 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. */