diff --git a/src/newgrf.cpp b/src/newgrf.cpp index 26a6a66fea..15a06a53f4 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -148,9 +148,12 @@ GRFError *DisableGrf(StringID message, GRFConfig *config) if (message == STR_NULL) return nullptr; - config->error = {STR_NEWGRF_ERROR_MSG_FATAL, message}; - if (config == _cur_gps.grfconfig) config->error->param_value[0] = _cur_gps.nfo_line; - return &config->error.value(); + auto it = std::ranges::find(config->errors, _cur_gps.nfo_line, &GRFError::nfo_line); + if (it == std::end(config->errors)) { + it = config->errors.emplace(it, STR_NEWGRF_ERROR_MSG_FATAL, _cur_gps.nfo_line, message); + } + if (config == _cur_gps.grfconfig) it->param_value[0] = _cur_gps.nfo_line; + return &*it; } /** @@ -392,7 +395,7 @@ static void ResetNewGRF() static void ResetNewGRFErrors() { for (const auto &c : _grfconfig) { - c->error.reset(); + c->errors.clear(); } } @@ -1841,7 +1844,7 @@ void LoadNewGRF(SpriteID load_index, uint num_baseset) if (num_non_static == NETWORK_MAX_GRF_COUNT) { Debug(grf, 0, "'{}' is not loaded as the maximum number of non-static GRFs has been reached", c->filename); c->status = GCS_DISABLED; - c->error = {STR_NEWGRF_ERROR_MSG_FATAL, STR_NEWGRF_ERROR_TOO_MANY_NEWGRFS_LOADED}; + c->errors.emplace_back(STR_NEWGRF_ERROR_MSG_FATAL, 0, STR_NEWGRF_ERROR_TOO_MANY_NEWGRFS_LOADED); continue; } num_non_static++; diff --git a/src/newgrf/newgrf_actb.cpp b/src/newgrf/newgrf_actb.cpp index 53aed45267..11a3f7e322 100644 --- a/src/newgrf/newgrf_actb.cpp +++ b/src/newgrf/newgrf_actb.cpp @@ -73,9 +73,6 @@ static void GRFLoadError(ByteReader &buf) /* This is a fatal error, so make sure the GRF is deactivated and no * more of it gets loaded. */ DisableGrf(); - - /* Make sure we show fatal errors, instead of silly infos from before */ - _cur_gps.grfconfig->error.reset(); } if (message_id >= lengthof(msgstr) && message_id != 0xFF) { @@ -88,39 +85,41 @@ static void GRFLoadError(ByteReader &buf) return; } - /* For now we can only show one message per newgrf file. */ - if (_cur_gps.grfconfig->error.has_value()) return; + /* An error may be emitted multiple times in different loading stages. Re-use if so. */ + auto it = std::ranges::find(_cur_gps.grfconfig->errors, _cur_gps.nfo_line, &GRFError::nfo_line); + if (it == std::end(_cur_gps.grfconfig->errors)) { + it = _cur_gps.grfconfig->errors.emplace(it, sevstr[severity], _cur_gps.nfo_line); + } - _cur_gps.grfconfig->error = {sevstr[severity]}; - GRFError *error = &_cur_gps.grfconfig->error.value(); + GRFError &error = *it; if (message_id == 0xFF) { /* This is a custom error message. */ if (buf.HasData()) { std::string_view message = buf.ReadString(); - error->custom_message = TranslateTTDPatchCodes(_cur_gps.grffile->grfid, lang, true, message, SCC_RAW_STRING_POINTER); + error.custom_message = TranslateTTDPatchCodes(_cur_gps.grffile->grfid, lang, true, message, SCC_RAW_STRING_POINTER); } else { GrfMsg(7, "GRFLoadError: No custom message supplied."); - error->custom_message.clear(); + error.custom_message.clear(); } } else { - error->message = msgstr[message_id]; + error.message = msgstr[message_id]; } if (buf.HasData()) { std::string_view data = buf.ReadString(); - error->data = TranslateTTDPatchCodes(_cur_gps.grffile->grfid, lang, true, data); + error.data = TranslateTTDPatchCodes(_cur_gps.grffile->grfid, lang, true, data); } else { GrfMsg(7, "GRFLoadError: No message data supplied."); - error->data.clear(); + error.data.clear(); } /* Only two parameter numbers can be used in the string. */ - for (uint i = 0; i < error->param_value.size() && buf.HasData(); i++) { + for (uint i = 0; i < error.param_value.size() && buf.HasData(); i++) { uint param_number = buf.ReadByte(); - error->param_value[i] = _cur_gps.grffile->GetParam(param_number); + error.param_value[i] = _cur_gps.grffile->GetParam(param_number); } } diff --git a/src/newgrf_config.cpp b/src/newgrf_config.cpp index eb4862c7a0..422fd57f4c 100644 --- a/src/newgrf_config.cpp +++ b/src/newgrf_config.cpp @@ -40,7 +40,7 @@ GRFConfig::GRFConfig(const GRFConfig &config) : name(config.name), info(config.info), url(config.url), - error(config.error), + errors(config.errors), version(config.version), min_loadable_version(config.min_loadable_version), flags(config.flags), @@ -156,15 +156,6 @@ GRFConfigList _grfconfig_newgame; GRFConfigList _grfconfig_static; uint _missing_extra_graphics = 0; -/** - * Construct a new GRFError. - * @param severity The severity of this error. - * @param message The actual error-string. - */ -GRFError::GRFError(StringID severity, StringID message) : message(message), severity(severity) -{ -} - /** * Get the value of the given user-changeable parameter. * @param info The grf parameter info to get the value for. @@ -472,7 +463,7 @@ compatible_grf: c->ident.md5sum = f->ident.md5sum; c->name = f->name; c->info = f->name; - c->error.reset(); + c->errors.clear(); c->version = f->version; c->min_loadable_version = f->min_loadable_version; c->num_valid_params = f->num_valid_params; diff --git a/src/newgrf_config.h b/src/newgrf_config.h index 337058f6b6..8b03088f96 100644 --- a/src/newgrf_config.h +++ b/src/newgrf_config.h @@ -107,12 +107,14 @@ struct GRFIdentifier { /** Information about why GRF had problems during initialisation */ struct GRFError { - GRFError(StringID severity, StringID message = {}); + GRFError(StringID severity, uint32_t nfo_line, StringID message = {}) + : message(message), severity(severity), nfo_line(nfo_line) {} std::string custom_message{}; ///< Custom message (if present) std::string data{}; ///< Additional data for message and custom_message StringID message{}; ///< Default message StringID severity{}; ///< Info / Warning / Error / Fatal + uint32_t nfo_line; ///< Line within NewGRF of error. std::array param_value{}; ///< Values of GRF parameters to show for message and custom_message }; @@ -169,7 +171,7 @@ struct GRFConfig { GRFTextWrapper name{}; ///< NOSAVE: GRF name (Action 0x08) GRFTextWrapper info{}; ///< NOSAVE: GRF info (author, copyright, ...) (Action 0x08) GRFTextWrapper url{}; ///< NOSAVE: URL belonging to this GRF. - std::optional error = std::nullopt; ///< NOSAVE: Error/Warning during GRF loading (Action 0x0B) + std::vector errors; ///< NOSAVE: Error/Warning during GRF loading (Action 0x0B) uint32_t version = 0; ///< NOSAVE: Version a NewGRF can set so only the newest NewGRF is shown uint32_t min_loadable_version = 0; ///< NOSAVE: Minimum compatible version a NewGRF can define diff --git a/src/newgrf_gui.cpp b/src/newgrf_gui.cpp index 28a8cf52e6..99b6152608 100644 --- a/src/newgrf_gui.cpp +++ b/src/newgrf_gui.cpp @@ -51,17 +51,20 @@ void ShowNewGRFError() for (const auto &c : _grfconfig) { /* Only show Fatal and Error level messages */ - if (!c->error.has_value() || (c->error->severity != STR_NEWGRF_ERROR_MSG_FATAL && c->error->severity != STR_NEWGRF_ERROR_MSG_ERROR)) continue; + if (c->errors.empty()) continue; + + const GRFError &error = c->errors.back(); + if (error.severity != STR_NEWGRF_ERROR_MSG_FATAL && error.severity != STR_NEWGRF_ERROR_MSG_ERROR) continue; std::vector params; params.emplace_back(c->GetName()); - params.emplace_back(c->error->message != STR_NULL ? c->error->message : STR_JUST_RAW_STRING); - params.emplace_back(c->error->custom_message); + params.emplace_back(error.message != STR_NULL ? error.message : STR_JUST_RAW_STRING); + params.emplace_back(error.custom_message); params.emplace_back(c->filename); - params.emplace_back(c->error->data); - for (const uint32_t &value : c->error->param_value) params.emplace_back(value); + params.emplace_back(error.data); + for (const uint32_t &value : error.param_value) params.emplace_back(value); - if (c->error->severity == STR_NEWGRF_ERROR_MSG_FATAL) { + if (error.severity == STR_NEWGRF_ERROR_MSG_FATAL) { ShowErrorMessage(GetEncodedStringWithArgs(STR_NEWGRF_ERROR_FATAL_POPUP, params), {}, WL_CRITICAL); } else { ShowErrorMessage(GetEncodedStringWithArgs(STR_NEWGRF_ERROR_POPUP, params), {}, WL_ERROR); @@ -81,15 +84,15 @@ static StringID GetGRFPaletteString(uint8_t palette) static void ShowNewGRFInfo(const GRFConfig &c, const Rect &r, bool show_params) { Rect tr = r.Shrink(WidgetDimensions::scaled.frametext); - if (c.error.has_value()) { - std::arrayparam_value)>> params{}; + for (const GRFError &error : c.errors) { + std::array> params{}; auto it = params.begin(); - *it++ = c.error->custom_message; // is skipped by built-in messages + *it++ = error.custom_message; // is skipped by built-in messages *it++ = c.filename; - *it++ = c.error->data; - for (const uint32_t &value : c.error->param_value) *it++ = value; + *it++ = error.data; + for (const uint32_t &value : error.param_value) *it++ = value; - tr.top = DrawStringMultiLine(tr, GetString(c.error->severity, GetStringWithArgs(c.error->message != STR_NULL ? c.error->message : STR_JUST_RAW_STRING, {params.begin(), it}))); + tr.top = DrawStringMultiLine(tr, GetString(error.severity, GetStringWithArgs(error.message != STR_NULL ? error.message : STR_JUST_RAW_STRING, {params.begin(), it}))); } /* Draw filename or not if it is not known (GRF sent over internet) */ @@ -868,8 +871,8 @@ struct NewGRFWindow : public Window, NewGRFScanCallback { } } DrawSprite(SPR_SQUARE, pal, square_left, tr.top + square_offset_y); - if (c->error.has_value()) DrawSprite(SPR_WARNING_SIGN, 0, warning_left, tr.top + warning_offset_y); - uint txtoffset = !c->error.has_value() ? 0 : warning.width; + if (!c->errors.empty()) DrawSprite(SPR_WARNING_SIGN, 0, warning_left, tr.top + warning_offset_y); + uint txtoffset = c->errors.empty() ? 0 : warning.width; DrawString(text_left + (rtl ? 0 : txtoffset), text_right - (rtl ? txtoffset : 0), tr.top + offset_y, std::move(text), h ? TC_WHITE : TC_ORANGE); tr.top += step_height; }