diff --git a/src/build_vehicle_gui.cpp b/src/build_vehicle_gui.cpp index aefd82c5bb..d6713700cb 100644 --- a/src/build_vehicle_gui.cpp +++ b/src/build_vehicle_gui.cpp @@ -804,10 +804,7 @@ static std::optional GetNewGRFAdditionalText(EngineID engine) return std::nullopt; } - StartTextRefStackUsage(grffile, 6); - std::string result = GetString(GetGRFStringID(grffile->grfid, GRFSTR_MISC_GRF_TEXT + callback)); - StopTextRefStackUsage(); - return result; + return GetGRFStringWithTextStack(grffile, GRFSTR_MISC_GRF_TEXT + callback, 6); } /** diff --git a/src/command.cpp b/src/command.cpp index 34c917e91f..1609fca847 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -407,32 +407,8 @@ void CommandCost::AddCost(const CommandCost &ret) } } -/** - * 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. - */ -/* static */ uint32_t CommandCost::textref_stack[16]; - /* static */ EncodedString CommandCost::encoded_message; -/** - * Activate usage of the NewGRF #TextRefStack for the error message. - * @param grffile NewGRF that provides the #TextRefStack - * @param num_registers number of entries to copy from the temporary NewGRF registers - */ -void CommandCost::UseTextRefStack(const GRFFile *grffile, uint num_registers) -{ - extern TemporaryStorageArray _temp_store; - - assert(num_registers < lengthof(textref_stack)); - this->textref_stack_grffile = grffile; - this->textref_stack_size = num_registers; - for (uint i = 0; i < num_registers; i++) { - textref_stack[i] = _temp_store.GetValue(0x100 + i); - } -} - /** * Return an error status, with string and parameter. * @param str StringID of error. diff --git a/src/command_type.h b/src/command_type.h index 6f1a457113..011babfadf 100644 --- a/src/command_type.h +++ b/src/command_type.h @@ -27,12 +27,8 @@ class CommandCost { ExpensesType expense_type; ///< the type of expence as shown on the finances view bool success; ///< Whether the command went fine up to this moment Owner owner = CompanyID::Invalid(); ///< Originator owner of error. - const GRFFile *textref_stack_grffile = nullptr; ///< NewGRF providing the #TextRefStack content. - uint textref_stack_size = 0; ///< Number of uint32_t values to put on the #TextRefStack for the error message. StringID extra_message = INVALID_STRING_ID; ///< Additional warning message for when success is unset - static uint32_t textref_stack[16]; - static EncodedString encoded_message; ///< Encoded error message, used if the error message includes parameters. public: @@ -146,35 +142,6 @@ public: this->extra_message = INVALID_STRING_ID; } - void UseTextRefStack(const GRFFile *grffile, uint num_registers); - - /** - * Returns the NewGRF providing the #TextRefStack of the error message. - * @return the NewGRF. - */ - const GRFFile *GetTextRefStackGRF() const - { - return this->textref_stack_grffile; - } - - /** - * Returns the number of uint32_t values for the #TextRefStack of the error message. - * @return number of uint32_t values. - */ - uint GetTextRefStackSize() const - { - return this->textref_stack_size; - } - - /** - * Returns a pointer to the values for the #TextRefStack of the error message. - * @return uint32_t values for the #TextRefStack - */ - const uint32_t *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/error.h b/src/error.h index d1746134e4..fe3857da31 100644 --- a/src/error.h +++ b/src/error.h @@ -31,9 +31,6 @@ enum WarningLevel : uint8_t { class ErrorMessageData { protected: bool is_critical; ///< Whether the error message is critical. - const GRFFile *textref_stack_grffile; ///< NewGRF that filled the #TextRefStack for the error message. - uint textref_stack_size; ///< Number of uint32_t values to put on the #TextRefStack for the error message. - uint32_t textref_stack[16]; ///< Values to put on the #TextRefStack for the error message. EncodedString summary_msg; ///< General error message showed in first line. Must be valid. EncodedString detailed_msg; ///< Detailed error message showed in second line. Can be #INVALID_STRING_ID. EncodedString extra_msg; ///< Extra error message shown in third line. Can be #INVALID_STRING_ID. @@ -41,11 +38,7 @@ protected: CompanyID company; ///< Company belonging to the face being shown. #CompanyID::Invalid() if no face present. public: - ErrorMessageData(const ErrorMessageData &data); - ErrorMessageData(EncodedString &&summary_msg, EncodedString &&detailed_msg, bool is_critical = false, int x = 0, int y = 0, const GRFFile *textref_stack_grffile = nullptr, uint textref_stack_size = 0, const uint32_t *textref_stack = nullptr, EncodedString &&extra_msg = {}, CompanyID company = CompanyID::Invalid()); - - /* Remove the copy assignment, as the default implementation will not do the right thing. */ - ErrorMessageData &operator=(ErrorMessageData &rhs) = delete; + ErrorMessageData(EncodedString &&summary_msg, EncodedString &&detailed_msg, bool is_critical = false, int x = 0, int y = 0, EncodedString &&extra_msg = {}, CompanyID company = CompanyID::Invalid()); /** Check whether error window shall display a company manager face */ bool HasFace() const { return company != CompanyID::Invalid(); } @@ -58,7 +51,7 @@ void ScheduleErrorMessage(ErrorList &datas); void ScheduleErrorMessage(const ErrorMessageData &data); void ShowErrorMessage(EncodedString &&summary_msg, int x, int y, const CommandCost &cc); -void ShowErrorMessage(EncodedString &&summary_msg, EncodedString &&detailed_msg, WarningLevel wl, int x = 0, int y = 0, const GRFFile *textref_stack_grffile = nullptr, uint textref_stack_size = 0, const uint32_t *textref_stack = nullptr, EncodedString &&extra_msg = {}, CompanyID company = CompanyID::Invalid()); +void ShowErrorMessage(EncodedString &&summary_msg, EncodedString &&detailed_msg, WarningLevel wl, int x = 0, int y = 0, EncodedString &&extra_msg = {}, CompanyID company = CompanyID::Invalid()); bool HideActiveErrorMessage(); void ClearErrorMessages(); diff --git a/src/error_gui.cpp b/src/error_gui.cpp index 081a9f825d..af57bf18bf 100644 --- a/src/error_gui.cpp +++ b/src/error_gui.cpp @@ -70,17 +70,6 @@ static WindowDesc _errmsg_face_desc( _nested_errmsg_face_widgets ); -/** - * Copy the given data into our instance. - * @param data The data to copy. - */ -ErrorMessageData::ErrorMessageData(const ErrorMessageData &data) : - is_critical(data.is_critical), textref_stack_grffile(data.textref_stack_grffile), textref_stack_size(data.textref_stack_size), - summary_msg(data.summary_msg), detailed_msg(data.detailed_msg), extra_msg(data.extra_msg), position(data.position), company(data.company) -{ - memcpy(this->textref_stack, data.textref_stack, sizeof(this->textref_stack)); -} - /** * Display an error message in a window. * @param summary_msg General error message showed in first line. Must be valid. @@ -88,25 +77,16 @@ ErrorMessageData::ErrorMessageData(const ErrorMessageData &data) : * @param is_critical Whether the error is critical. Critical messages never go away on their own. * @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_grffile NewGRF that provides the #TextRefStack for the error message. - * @param textref_stack_size Number of uint32_t 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. * @param extra_msg Extra error message showed in third line. Can be empty. */ -ErrorMessageData::ErrorMessageData(EncodedString &&summary_msg, EncodedString &&detailed_msg, bool is_critical, int x, int y, const GRFFile *textref_stack_grffile, uint textref_stack_size, const uint32_t *textref_stack, EncodedString &&extra_msg, CompanyID company) : +ErrorMessageData::ErrorMessageData(EncodedString &&summary_msg, EncodedString &&detailed_msg, bool is_critical, int x, int y, EncodedString &&extra_msg, CompanyID company) : is_critical(is_critical), - textref_stack_grffile(textref_stack_grffile), - textref_stack_size(textref_stack_size), summary_msg(std::move(summary_msg)), detailed_msg(std::move(detailed_msg)), extra_msg(std::move(extra_msg)), + position(x, y), company(company) { - this->position.x = x; - this->position.y = y; - - if (textref_stack_size > 0) MemCpyT(this->textref_stack, textref_stack, textref_stack_size); - assert(!this->summary_msg.empty()); } @@ -143,14 +123,10 @@ public: { switch (widget) { case WID_EM_MESSAGE: { - if (this->textref_stack_size > 0) StartTextRefStackUsage(this->textref_stack_grffile, this->textref_stack_size, this->textref_stack); - this->height_summary = GetStringHeight(this->summary_msg.GetDecodedString(), size.width); this->height_detailed = (this->detailed_msg.empty()) ? 0 : GetStringHeight(this->detailed_msg.GetDecodedString(), size.width); this->height_extra = (this->extra_msg.empty()) ? 0 : GetStringHeight(this->extra_msg.GetDecodedString(), size.width); - if (this->textref_stack_size > 0) StopTextRefStackUsage(); - uint panel_height = this->height_summary; if (!this->detailed_msg.empty()) panel_height += this->height_detailed + WidgetDimensions::scaled.vsep_wide; if (!this->extra_msg.empty()) panel_height += this->height_extra + WidgetDimensions::scaled.vsep_wide; @@ -215,8 +191,6 @@ public: } case WID_EM_MESSAGE: - if (this->textref_stack_size > 0) StartTextRefStackUsage(this->textref_stack_grffile, this->textref_stack_size, this->textref_stack); - if (this->detailed_msg.empty()) { DrawStringMultiLine(r, this->summary_msg.GetDecodedString(), TC_FROMSTRING, SA_CENTER); } else if (this->extra_msg.empty()) { @@ -239,7 +213,6 @@ public: DrawStringMultiLine(bottom_section, this->extra_msg.GetDecodedString(), TC_WHITE, SA_CENTER); } - if (this->textref_stack_size > 0) StopTextRefStackUsage(); break; default: @@ -310,7 +283,7 @@ void UnshowCriticalError() * @param summary_msg General error message showed in first line. Must be valid. * @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 cc CommandCost containing the optional detailed and extra error messages shown in the second and third lines (can be empty) and TextRefStack info. + * @param cc CommandCost containing the optional detailed and extra error messages shown in the second and third lines (can be empty). */ void ShowErrorMessage(EncodedString &&summary_msg, int x, int y, const CommandCost &cc) { @@ -318,7 +291,6 @@ void ShowErrorMessage(EncodedString &&summary_msg, int x, int y, const CommandCo if (error.empty()) error = GetEncodedStringIfValid(cc.GetErrorMessage()); ShowErrorMessage(std::move(summary_msg), std::move(error), WL_INFO, x, y, - cc.GetTextRefStackGRF(), cc.GetTextRefStackSize(), cc.GetTextRefStack(), GetEncodedStringIfValid(cc.GetExtraErrorMessage()), cc.GetErrorOwner()); } @@ -329,20 +301,13 @@ void ShowErrorMessage(EncodedString &&summary_msg, int x, int y, const CommandCo * @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_grffile NewGRF providing the #TextRefStack for the error message. - * @param textref_stack_size Number of uint32_t 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. * @param extra_msg Extra error message shown in third line. Can be empty. */ -void ShowErrorMessage(EncodedString &&summary_msg, EncodedString &&detailed_msg, WarningLevel wl, int x, int y, const GRFFile *textref_stack_grffile, uint textref_stack_size, const uint32_t *textref_stack, EncodedString &&extra_msg, CompanyID company) +void ShowErrorMessage(EncodedString &&summary_msg, EncodedString &&detailed_msg, WarningLevel wl, int x, int y, EncodedString &&extra_msg, CompanyID company) { - assert(textref_stack_size == 0 || (textref_stack_grffile != nullptr && textref_stack != nullptr)); - if (wl != WL_INFO) { /* Print message to console */ - if (textref_stack_size > 0) StartTextRefStackUsage(textref_stack_grffile, textref_stack_size, textref_stack); - std::string message = summary_msg.GetDecodedString(); if (!detailed_msg.empty()) { message += " "; @@ -353,8 +318,6 @@ void ShowErrorMessage(EncodedString &&summary_msg, EncodedString &&detailed_msg, message += extra_msg.GetDecodedString(); } - if (textref_stack_size > 0) StopTextRefStackUsage(); - IConsolePrint(wl == WL_WARNING ? CC_WARNING : CC_ERROR, message); } @@ -363,7 +326,7 @@ void ShowErrorMessage(EncodedString &&summary_msg, EncodedString &&detailed_msg, if (_game_mode == GM_BOOTSTRAP) return; if (_settings_client.gui.errmsg_duration == 0 && !is_critical) return; - ErrorMessageData data(std::move(summary_msg), std::move(detailed_msg), is_critical, x, y, textref_stack_grffile, textref_stack_size, textref_stack, std::move(extra_msg), company); + ErrorMessageData data(std::move(summary_msg), std::move(detailed_msg), is_critical, x, y, std::move(extra_msg), company); ErrmsgWindow *w = dynamic_cast(FindWindowById(WC_ERRMSG, 0)); if (w != nullptr) { diff --git a/src/industry_gui.cpp b/src/industry_gui.cpp index 09265bbd5d..007d5f7c22 100644 --- a/src/industry_gui.cpp +++ b/src/industry_gui.cpp @@ -104,9 +104,7 @@ static void GetCargoSuffix(uint cargo, CargoSuffixType cst, const Industry *ind, if (indspec->grf_prop.grffile->grf_version < 8) { if (GB(callback, 0, 8) == 0xFF) return; if (callback < 0x400) { - StartTextRefStackUsage(indspec->grf_prop.grffile, 6); - suffix.text = GetString(GetGRFStringID(indspec->grf_prop.grfid, GRFSTR_MISC_GRF_TEXT + callback)); - StopTextRefStackUsage(); + suffix.text = GetGRFStringWithTextStack(indspec->grf_prop.grffile, GRFSTR_MISC_GRF_TEXT + callback, 6); suffix.display = CSD_CARGO_AMOUNT_TEXT; return; } @@ -120,16 +118,12 @@ static void GetCargoSuffix(uint cargo, CargoSuffixType cst, const Industry *ind, return; } if (callback < 0x400) { - StartTextRefStackUsage(indspec->grf_prop.grffile, 6); - suffix.text = GetString(GetGRFStringID(indspec->grf_prop.grfid, GRFSTR_MISC_GRF_TEXT + callback)); - StopTextRefStackUsage(); + suffix.text = GetGRFStringWithTextStack(indspec->grf_prop.grffile, GRFSTR_MISC_GRF_TEXT + callback, 6); suffix.display = CSD_CARGO_AMOUNT_TEXT; return; } if (callback >= 0x800 && callback < 0xC00) { - StartTextRefStackUsage(indspec->grf_prop.grffile, 6); - suffix.text = GetString(GetGRFStringID(indspec->grf_prop.grfid, GRFSTR_MISC_GRF_TEXT + callback - 0x800)); - StopTextRefStackUsage(); + suffix.text = GetGRFStringWithTextStack(indspec->grf_prop.grffile, GRFSTR_MISC_GRF_TEXT + callback - 0x800, 6); suffix.display = CSD_CARGO_TEXT; return; } @@ -604,11 +598,9 @@ public: if (callback_res > 0x400) { ErrorUnknownCallbackResult(indsp->grf_prop.grfid, CBID_INDUSTRY_FUND_MORE_TEXT, callback_res); } else { - StringID str = GetGRFStringID(indsp->grf_prop.grfid, GRFSTR_MISC_GRF_TEXT + callback_res); // No. here's the new string - if (str != STR_UNDEFINED) { - StartTextRefStackUsage(indsp->grf_prop.grffile, 6); + std::string str = GetGRFStringWithTextStack(indsp->grf_prop.grffile, GRFSTR_MISC_GRF_TEXT + callback_res, 6); + if (!str.empty()) { DrawStringMultiLine(ir, str, TC_YELLOW); - StopTextRefStackUsage(); } } } @@ -1006,13 +998,10 @@ public: if (callback_res > 0x400) { ErrorUnknownCallbackResult(ind->grf_prop.grfid, CBID_INDUSTRY_WINDOW_MORE_TEXT, callback_res); } else { - StringID message = GetGRFStringID(ind->grf_prop.grfid, GRFSTR_MISC_GRF_TEXT + callback_res); - if (message != STR_NULL && message != STR_UNDEFINED) { + std::string str = GetGRFStringWithTextStack(ind->grf_prop.grffile, GRFSTR_MISC_GRF_TEXT + callback_res, 6); + if (!str.empty()) { ir.top += WidgetDimensions::scaled.vsep_wide; - - StartTextRefStackUsage(ind->grf_prop.grffile, 6); - ir.top = DrawStringMultiLine(ir, message, TC_BLACK); - StopTextRefStackUsage(); + ir.top = DrawStringMultiLine(ir, str, TC_YELLOW); } } } diff --git a/src/newgrf_commons.cpp b/src/newgrf_commons.cpp index 74cda21591..a72d5a9ccd 100644 --- a/src/newgrf_commons.cpp +++ b/src/newgrf_commons.cpp @@ -473,6 +473,13 @@ CommandCost GetErrorMessageFromLocationCallbackResult(uint16_t cb_res, const GRF if (cb_res < 0x400) { res = CommandCost(GetGRFStringID(grffile->grfid, GRFSTR_MISC_GRF_TEXT + cb_res)); + + /* If this error isn't for the local player then it won't be seen, so don't bother encoding anything. */ + if (!IsLocalCompany()) return res; + + StringID stringid = GetGRFStringID(grffile->grfid, GRFSTR_MISC_GRF_TEXT + cb_res); + auto params = GetGRFSringTextStackParameters(grffile, stringid, 4); + res.SetEncodedMessage(GetEncodedStringWithArgs(stringid, params)); } else { switch (cb_res) { case 0x400: return res; // No error. @@ -490,14 +497,6 @@ CommandCost GetErrorMessageFromLocationCallbackResult(uint16_t cb_res, const GRF } } - /* If this error isn't for the local player then it won't be seen, so don't bother encoding anything. */ - if (!IsLocalCompany()) return res; - - /* Copy some parameters from the registers to the error message text ref. stack */ - std::array params{}; - res.UseTextRefStack(grffile, 4); - res.SetEncodedMessage(GetEncodedStringWithArgs(res.GetErrorMessage(), params)); - return res; } diff --git a/src/newgrf_text.cpp b/src/newgrf_text.cpp index 1d09cdea21..851d7f43d0 100644 --- a/src/newgrf_text.cpp +++ b/src/newgrf_text.cpp @@ -17,6 +17,7 @@ #include "stdafx.h" +#include "debug.h" #include "newgrf.h" #include "strings_internal.h" #include "newgrf_storage.h" @@ -685,12 +686,24 @@ void CleanUpStrings() } struct TextRefStack { - std::array stack; - uint8_t position; - const GRFFile *grffile; - bool used; + std::array stack{}; + uint8_t position = 0; + const GRFFile *grffile = nullptr; - TextRefStack() : position(0), grffile(nullptr), used(false) {} + TextRefStack(const GRFFile *grffile, uint8_t num_entries) : grffile(grffile) + { + extern TemporaryStorageArray _temp_store; + + assert(num_entries < sizeof(uint32_t) * std::size(stack)); + + auto stack_it = this->stack.begin(); + for (uint i = 0; i < num_entries; i++) { + uint32_t value = _temp_store.GetValue(0x100 + i); + for (uint j = 0; j < 32; j += 8) { + *stack_it++ = GB(value, j, 8); + } + } + } uint8_t PopUnsignedByte() { assert(this->position < this->stack.size()); return this->stack[this->position++]; } int8_t PopSignedByte() { return (int8_t)this->PopUnsignedByte(); } @@ -736,213 +749,95 @@ struct TextRefStack { this->stack[this->position] = GB(word, 0, 8); this->stack[this->position + 1] = GB(word, 8, 8); } - - void ResetStack(const GRFFile *grffile) - { - assert(grffile != nullptr); - this->position = 0; - this->grffile = grffile; - this->used = true; - } }; -/** The stack that is used for TTDP compatible string code parsing */ -static TextRefStack _newgrf_textrefstack; +static void HandleNewGRFStringControlCodes(const char *str, TextRefStack &stack, std::vector ¶ms); /** - * Check whether the NewGRF text stack is in use. - * @return True iff the NewGRF text stack is used. + * Process NewGRF string control code instructions. + * @param scc The string control code that has been read. + * @param str The string that we are reading from. + * @param stack The TextRefStack. + * @param[out] params Output parameters */ -bool UsingNewGRFTextStack() +static void RemapNewGRFStringControlCode(char32_t scc, const char **str, TextRefStack &stack, std::vector ¶ms) { - return _newgrf_textrefstack.used; -} + auto it = std::back_inserter(params); -/** - * Create a backup of the current NewGRF text stack. - * @return A copy of the current text stack. - */ -struct TextRefStack *CreateTextRefStackBackup() -{ - return new TextRefStack(_newgrf_textrefstack); -} - -/** - * Restore a copy of the text stack to the used stack. - * @param backup The copy to restore. - */ -void RestoreTextRefStackBackup(struct TextRefStack *backup) -{ - _newgrf_textrefstack = *backup; - delete backup; -} - -/** - * Start using the TTDP compatible string code parsing. - * - * On start a number of values is copied on the #TextRefStack. - * You can then use #GetString() and the normal string drawing functions, - * and they will use the #TextRefStack for NewGRF string codes. - * - * However, when you want to draw a string multiple times using the same stack, - * you have to call #RewindTextRefStack() between draws. - * - * After you are done with drawing, you must disable usage of the #TextRefStack - * by calling #StopTextRefStackUsage(), so NewGRF string codes operate on the - * normal string parameters again. - * - * @param grffile the NewGRF providing the stack data - * @param numEntries number of entries to copy from the registers - * @param values values to copy onto the stack; if nullptr the temporary NewGRF registers will be used instead - */ -void StartTextRefStackUsage(const GRFFile *grffile, uint8_t numEntries, const uint32_t *values) -{ - extern TemporaryStorageArray _temp_store; - - _newgrf_textrefstack.ResetStack(grffile); - - auto stack_it = _newgrf_textrefstack.stack.begin(); - for (uint i = 0; i < numEntries; i++) { - uint32_t value = values != nullptr ? values[i] : _temp_store.GetValue(0x100 + i); - for (uint j = 0; j < 32; j += 8) { - *stack_it = GB(value, j, 8); - stack_it++; - } - } -} - -/** Stop using the TTDP compatible string code parsing */ -void StopTextRefStackUsage() -{ - _newgrf_textrefstack.used = false; -} - -/** - * FormatString for NewGRF specific "magic" string control codes - * @param scc the string control code that has been read - * @param str the string that we need to write - * @param parameters the OpenTTD string formatting parameters - * @param modify_parameters When true, modify the OpenTTD string formatting parameters. - * @return the string control code to "execute" now - */ -char32_t RemapNewGRFStringControlCode(char32_t scc, const char **str, StringParameters ¶meters, bool modify_parameters) -{ + /* There is data on the NewGRF text stack, and we want to move them to OpenTTD's string stack. + * After this call, a new call is made with `modify_parameters` set to false when the string is finally formatted. */ switch (scc) { - default: break; + default: return; + case SCC_NEWGRF_PRINT_BYTE_SIGNED: *it = stack.PopSignedByte(); break; + case SCC_NEWGRF_PRINT_QWORD_CURRENCY: *it = stack.PopSignedQWord(); break; - /* These control codes take one string parameter, check there are at least that many available. */ - case SCC_NEWGRF_PRINT_DWORD_SIGNED: - case SCC_NEWGRF_PRINT_WORD_SIGNED: - case SCC_NEWGRF_PRINT_BYTE_SIGNED: - case SCC_NEWGRF_PRINT_WORD_UNSIGNED: - case SCC_NEWGRF_PRINT_BYTE_HEX: - case SCC_NEWGRF_PRINT_WORD_HEX: - case SCC_NEWGRF_PRINT_DWORD_HEX: - case SCC_NEWGRF_PRINT_QWORD_HEX: case SCC_NEWGRF_PRINT_DWORD_CURRENCY: - case SCC_NEWGRF_PRINT_QWORD_CURRENCY: - case SCC_NEWGRF_PRINT_WORD_STRING_ID: - case SCC_NEWGRF_PRINT_WORD_DATE_LONG: - case SCC_NEWGRF_PRINT_DWORD_DATE_LONG: - case SCC_NEWGRF_PRINT_WORD_DATE_SHORT: - case SCC_NEWGRF_PRINT_DWORD_DATE_SHORT: + case SCC_NEWGRF_PRINT_DWORD_SIGNED: *it = stack.PopSignedDWord(); break; + + case SCC_NEWGRF_PRINT_BYTE_HEX: *it = stack.PopUnsignedByte(); break; + case SCC_NEWGRF_PRINT_QWORD_HEX: *it = stack.PopUnsignedQWord(); break; + case SCC_NEWGRF_PRINT_WORD_SPEED: case SCC_NEWGRF_PRINT_WORD_VOLUME_LONG: case SCC_NEWGRF_PRINT_WORD_VOLUME_SHORT: + case SCC_NEWGRF_PRINT_WORD_SIGNED: *it = stack.PopSignedWord(); break; + + case SCC_NEWGRF_PRINT_WORD_HEX: case SCC_NEWGRF_PRINT_WORD_WEIGHT_LONG: case SCC_NEWGRF_PRINT_WORD_WEIGHT_SHORT: case SCC_NEWGRF_PRINT_WORD_POWER: - case SCC_NEWGRF_PRINT_DWORD_FORCE: case SCC_NEWGRF_PRINT_WORD_STATION_NAME: - case SCC_NEWGRF_PRINT_WORD_CARGO_NAME: - if (parameters.GetDataLeft() < 1) { - Debug(misc, 0, "Too many NewGRF string parameters."); - return 0; - } - break; + case SCC_NEWGRF_PRINT_WORD_UNSIGNED: *it = stack.PopUnsignedWord(); break; + + case SCC_NEWGRF_PRINT_DWORD_FORCE: + case SCC_NEWGRF_PRINT_DWORD_DATE_LONG: + case SCC_NEWGRF_PRINT_DWORD_DATE_SHORT: + case SCC_NEWGRF_PRINT_DWORD_HEX: *it = stack.PopUnsignedDWord(); break; + + /* Dates from NewGRFs have 1920-01-01 as their zero point, convert it to OpenTTD's epoch. */ + case SCC_NEWGRF_PRINT_WORD_DATE_LONG: + case SCC_NEWGRF_PRINT_WORD_DATE_SHORT: *it = CalendarTime::DAYS_TILL_ORIGINAL_BASE_YEAR + stack.PopUnsignedWord(); break; + + case SCC_NEWGRF_DISCARD_WORD: stack.PopUnsignedWord(); break; + + case SCC_NEWGRF_ROTATE_TOP_4_WORDS: stack.RotateTop4Words(); break; + case SCC_NEWGRF_PUSH_WORD: stack.PushWord(Utf8Consume(str)); break; - /* These string code take two string parameters, check there are at least that many available. */ case SCC_NEWGRF_PRINT_WORD_CARGO_LONG: case SCC_NEWGRF_PRINT_WORD_CARGO_SHORT: case SCC_NEWGRF_PRINT_WORD_CARGO_TINY: - if (parameters.GetDataLeft() < 2) { - Debug(misc, 0, "Too many NewGRF string parameters."); - return 0; - } + *it = GetCargoTranslation(stack.PopUnsignedWord(), stack.grffile); + *it = stack.PopUnsignedWord(); break; - } - if (_newgrf_textrefstack.used && modify_parameters) { - /* There is data on the NewGRF text stack, and we want to move them to OpenTTD's string stack. - * After this call, a new call is made with `modify_parameters` set to false when the string is finally formatted. */ - switch (scc) { - default: NOT_REACHED(); - case SCC_NEWGRF_PRINT_BYTE_SIGNED: parameters.SetParam(0, _newgrf_textrefstack.PopSignedByte()); break; - case SCC_NEWGRF_PRINT_QWORD_CURRENCY: parameters.SetParam(0, _newgrf_textrefstack.PopSignedQWord()); break; - - case SCC_NEWGRF_PRINT_DWORD_CURRENCY: - case SCC_NEWGRF_PRINT_DWORD_SIGNED: parameters.SetParam(0, _newgrf_textrefstack.PopSignedDWord()); break; - - case SCC_NEWGRF_PRINT_BYTE_HEX: parameters.SetParam(0, _newgrf_textrefstack.PopUnsignedByte()); break; - case SCC_NEWGRF_PRINT_QWORD_HEX: parameters.SetParam(0, _newgrf_textrefstack.PopUnsignedQWord()); break; - - case SCC_NEWGRF_PRINT_WORD_SPEED: - case SCC_NEWGRF_PRINT_WORD_VOLUME_LONG: - case SCC_NEWGRF_PRINT_WORD_VOLUME_SHORT: - case SCC_NEWGRF_PRINT_WORD_SIGNED: parameters.SetParam(0, _newgrf_textrefstack.PopSignedWord()); break; - - case SCC_NEWGRF_PRINT_WORD_HEX: - case SCC_NEWGRF_PRINT_WORD_WEIGHT_LONG: - case SCC_NEWGRF_PRINT_WORD_WEIGHT_SHORT: - case SCC_NEWGRF_PRINT_WORD_POWER: - case SCC_NEWGRF_PRINT_WORD_STATION_NAME: - case SCC_NEWGRF_PRINT_WORD_UNSIGNED: parameters.SetParam(0, _newgrf_textrefstack.PopUnsignedWord()); break; - - case SCC_NEWGRF_PRINT_DWORD_FORCE: - case SCC_NEWGRF_PRINT_DWORD_DATE_LONG: - case SCC_NEWGRF_PRINT_DWORD_DATE_SHORT: - case SCC_NEWGRF_PRINT_DWORD_HEX: parameters.SetParam(0, _newgrf_textrefstack.PopUnsignedDWord()); break; - - /* Dates from NewGRFs have 1920-01-01 as their zero point, convert it to OpenTTD's epoch. */ - case SCC_NEWGRF_PRINT_WORD_DATE_LONG: - case SCC_NEWGRF_PRINT_WORD_DATE_SHORT: parameters.SetParam(0, CalendarTime::DAYS_TILL_ORIGINAL_BASE_YEAR + _newgrf_textrefstack.PopUnsignedWord()); break; - - case SCC_NEWGRF_DISCARD_WORD: _newgrf_textrefstack.PopUnsignedWord(); break; - - case SCC_NEWGRF_ROTATE_TOP_4_WORDS: _newgrf_textrefstack.RotateTop4Words(); break; - case SCC_NEWGRF_PUSH_WORD: _newgrf_textrefstack.PushWord(Utf8Consume(str)); break; - - case SCC_NEWGRF_PRINT_WORD_CARGO_LONG: - case SCC_NEWGRF_PRINT_WORD_CARGO_SHORT: - case SCC_NEWGRF_PRINT_WORD_CARGO_TINY: - parameters.SetParam(0, GetCargoTranslation(_newgrf_textrefstack.PopUnsignedWord(), _newgrf_textrefstack.grffile)); - parameters.SetParam(1, _newgrf_textrefstack.PopUnsignedWord()); - break; - - case SCC_NEWGRF_PRINT_WORD_STRING_ID: - parameters.SetParam(0, MapGRFStringID(_newgrf_textrefstack.grffile->grfid, GRFStringID{_newgrf_textrefstack.PopUnsignedWord()})); - break; - - case SCC_NEWGRF_PRINT_WORD_CARGO_NAME: { - CargoType cargo = GetCargoTranslation(_newgrf_textrefstack.PopUnsignedWord(), _newgrf_textrefstack.grffile); - parameters.SetParam(0, cargo < NUM_CARGO ? 1ULL << cargo : 0); - break; - } + case SCC_NEWGRF_PRINT_WORD_STRING_ID: { + StringID stringid = MapGRFStringID(stack.grffile->grfid, GRFStringID{stack.PopUnsignedWord()}); + *it = stringid; + /* We also need to handle the substring's stack usage. */ + HandleNewGRFStringControlCodes(GetStringPtr(stringid), stack, params); + break; } - } else { - /* Consume additional parameter characters that follow the NewGRF string code. */ - switch (scc) { - default: break; - case SCC_NEWGRF_PUSH_WORD: - Utf8Consume(str); - break; + case SCC_NEWGRF_PRINT_WORD_CARGO_NAME: { + CargoType cargo = GetCargoTranslation(stack.PopUnsignedWord(), stack.grffile); + *it = cargo < NUM_CARGO ? 1ULL << cargo : 0; + break; } } +} - /* Emit OpenTTD's internal string code for the different NewGRF variants. */ +/** + * Emit OpenTTD's internal string code for the different NewGRF string codes. + * @param scc NewGRF string code. + * @param[in,out] str String iterator, moved forward if SCC_NEWGRF_PUSH_WORD is found. + * @returns String code to use. + */ +char32_t RemapNewGRFStringControlCode(char32_t scc, const char **str) +{ switch (scc) { - default: NOT_REACHED(); + default: + return scc; + case SCC_NEWGRF_PRINT_DWORD_SIGNED: case SCC_NEWGRF_PRINT_WORD_SIGNED: case SCC_NEWGRF_PRINT_BYTE_SIGNED: @@ -1007,9 +902,66 @@ char32_t RemapNewGRFStringControlCode(char32_t scc, const char **str, StringPara return SCC_STATION_NAME; /* These NewGRF string codes modify the NewGRF stack or otherwise do not map to OpenTTD string codes. */ + case SCC_NEWGRF_PUSH_WORD: + Utf8Consume(str); + return 0; + case SCC_NEWGRF_DISCARD_WORD: case SCC_NEWGRF_ROTATE_TOP_4_WORDS: - case SCC_NEWGRF_PUSH_WORD: return 0; } } + +/** + * Handle control codes in a NewGRF string, processing the stack and filling parameters. + * @param str String to process. + * @param[in,out] stack Stack to use. + * @param[out] params Parameters to fill. + */ +static void HandleNewGRFStringControlCodes(const char *str, TextRefStack &stack, std::vector ¶ms) +{ + if (str == nullptr) return; + + for (const char *p = str; *p != '\0'; /* nothing */) { + char32_t scc; + p += Utf8Decode(&scc, p); + RemapNewGRFStringControlCode(scc, &p, stack, params); + } +} + +/** + * Process the text ref stack for a GRF String and return its parameters. + * @param grffile GRFFile of string. + * @param stringid StringID of string. + * @param num_entries Number of temporary storage registers to import. + * @returns Parameters for GRF string. + */ +std::vector GetGRFSringTextStackParameters(const GRFFile *grffile, StringID stringid, uint8_t num_entries) +{ + if (stringid == INVALID_STRING_ID) return {}; + + const char *str = GetStringPtr(stringid); + if (str == nullptr) return {}; + + std::vector params; + params.reserve(20); + + TextRefStack stack{grffile, num_entries}; + HandleNewGRFStringControlCodes(str, stack, params); + + return params; +} + +/** + * Format a GRF string using the text ref stack for parameters. + * @param grffile GRFFile of string. + * @param grfstringid GRFStringID of string. + * @param num_entries Number of temporary storage registers to import. + * @returns Formatted string. + */ +std::string GetGRFStringWithTextStack(const struct GRFFile *grffile, GRFStringID grfstringid, uint8_t num_entries) +{ + StringID stringid = GetGRFStringID(grffile->grfid, grfstringid); + auto params = GetGRFSringTextStackParameters(grffile, stringid, num_entries); + return GetStringWithArgs(stringid, params); +} diff --git a/src/newgrf_text.h b/src/newgrf_text.h index 2d1ea70886..afbc01cfe9 100644 --- a/src/newgrf_text.h +++ b/src/newgrf_text.h @@ -28,10 +28,7 @@ void AddGRFTextToList(GRFTextWrapper &list, std::string_view text_to_add); bool CheckGrfLangID(uint8_t lang_id, uint8_t grf_version); -void StartTextRefStackUsage(const struct GRFFile *grffile, uint8_t numEntries, const uint32_t *values = nullptr); -void StopTextRefStackUsage(); -bool UsingNewGRFTextStack(); -struct TextRefStack *CreateTextRefStackBackup(); -void RestoreTextRefStackBackup(struct TextRefStack *backup); +std::vector GetGRFSringTextStackParameters(const struct GRFFile *grffile, StringID stringid, uint8_t num_entries); +std::string GetGRFStringWithTextStack(const struct GRFFile *grffile, GRFStringID grfstringid, uint8_t num_entries); #endif /* NEWGRF_TEXT_H */ diff --git a/src/object_gui.cpp b/src/object_gui.cpp index cc3150ad08..33c9a47d49 100644 --- a/src/object_gui.cpp +++ b/src/object_gui.cpp @@ -245,11 +245,9 @@ public: if (callback_res > 0x400) { ErrorUnknownCallbackResult(spec->grf_prop.grfid, CBID_OBJECT_FUND_MORE_TEXT, callback_res); } else { - StringID message = GetGRFStringID(spec->grf_prop.grfid, GRFSTR_MISC_GRF_TEXT + callback_res); - if (message != STR_NULL && message != STR_UNDEFINED) { - StartTextRefStackUsage(spec->grf_prop.grffile, 6); - tr.top = DrawStringMultiLine(tr, message, TC_ORANGE); - StopTextRefStackUsage(); + std::string str = GetGRFStringWithTextStack(spec->grf_prop.grffile, GRFSTR_MISC_GRF_TEXT + callback_res, 6); + if (!str.empty()) { + tr.top = DrawStringMultiLine(tr, str, TC_ORANGE); } } } diff --git a/src/strings.cpp b/src/strings.cpp index c28df39e8e..c6e8270979 100644 --- a/src/strings.cpp +++ b/src/strings.cpp @@ -1143,19 +1143,7 @@ static void FormatString(StringBuilder &builder, const char *str_arg, StringPara */ std::string buffer; StringBuilder dry_run_builder(buffer); - if (UsingNewGRFTextStack()) { - /* Values from the NewGRF text stack are only copied to the normal - * argv array at the time they are encountered. That means that if - * another string command references a value later in the string it - * would fail. We solve that by running FormatString twice. The first - * pass makes sure the argv array is correctly filled and the second - * pass can reference later values without problems. */ - struct TextRefStack *backup = CreateTextRefStackBackup(); - FormatString(dry_run_builder, str_arg, args, case_index, game_script, true); - RestoreTextRefStackBackup(backup); - } else { - FormatString(dry_run_builder, str_arg, args, case_index, game_script, true); - } + FormatString(dry_run_builder, str_arg, args, case_index, game_script, true); /* We have to restore the original offset here to to read the correct values. */ args.SetOffset(orig_offset); } @@ -1174,8 +1162,7 @@ static void FormatString(StringBuilder &builder, const char *str_arg, StringPara if (SCC_NEWGRF_FIRST <= b && b <= SCC_NEWGRF_LAST) { /* We need to pass some stuff as it might be modified. */ - StringParameters remaining = args.GetRemainingParameters(); - b = RemapNewGRFStringControlCode(b, &str, remaining, dry_run); + b = RemapNewGRFStringControlCode(b, &str); if (b == 0) continue; } @@ -1677,11 +1664,7 @@ static void FormatString(StringBuilder &builder, const char *str_arg, StringPara const GRFFile *grffile = e->GetGRF(); assert(grffile != nullptr); - StartTextRefStackUsage(grffile, 6); - ArrayStringParameters<6> tmp_params; - GetStringWithArgs(builder, GetGRFStringID(grffile->grfid, GRFSTR_MISC_GRF_TEXT + callback), tmp_params); - StopTextRefStackUsage(); - + builder += GetGRFStringWithTextStack(grffile, GRFSTR_MISC_GRF_TEXT + callback, 6); break; } } diff --git a/src/strings_internal.h b/src/strings_internal.h index 976dd7524b..a8869afe71 100644 --- a/src/strings_internal.h +++ b/src/strings_internal.h @@ -338,6 +338,6 @@ void GenerateTownNameString(StringBuilder &builder, size_t lang, uint32_t seed); void GetTownName(StringBuilder &builder, const struct Town *t); void GRFTownNameGenerate(StringBuilder &builder, uint32_t grfid, uint16_t gen, uint32_t seed); -char32_t RemapNewGRFStringControlCode(char32_t scc, const char **str, StringParameters ¶meters, bool modify_parameters); +char32_t RemapNewGRFStringControlCode(char32_t scc, const char **str); #endif /* STRINGS_INTERNAL_H */