diff --git a/src/script/api/script_text.cpp b/src/script/api/script_text.cpp index f53a47bdbe..429d17e7ea 100644 --- a/src/script/api/script_text.cpp +++ b/src/script/api/script_text.cpp @@ -13,6 +13,8 @@ #include "../../game/game_text.hpp" #include "script_text.hpp" #include "../script_fatalerror.hpp" +#include "../script_instance.hpp" +#include "script_log.hpp" #include "../../table/control_codes.h" #include "../../3rdparty/fmt/format.h" @@ -168,6 +170,15 @@ const std::string ScriptText::GetEncodedText() return buf; } +void ScriptText::_TextParamError(const std::string &msg) +{ + if (this->GetActiveInstance()->IsTextParamMismatchAllowed()) { + ScriptLog::Error(msg.c_str()); + } else { + throw Script_FatalError(msg); + } +} + char *ScriptText::_GetEncodedText(char *p, char *lastofp, int ¶m_count, StringIDList &seen_ids) { const std::string &name = GetGameStringName(this->string); @@ -182,27 +193,45 @@ char *ScriptText::_GetEncodedText(char *p, char *lastofp, int ¶m_count, Stri int cur_idx = 0; for (const StringParam &cur_param : params) { - if (cur_idx >= this->paramc) throw Script_FatalError(fmt::format("{}: Not enough parameters", name)); + if (cur_idx >= this->paramc) { + this->_TextParamError(fmt::format("{}: Not enough parameters", name)); + break; + } switch (cur_param.type) { case StringParam::RAW_STRING: - if (!std::holds_alternative(this->param[cur_idx])) throw Script_FatalError(fmt::format("{}: Parameter {} expects a raw string", name, cur_idx)); + if (!std::holds_alternative(this->param[cur_idx])) { + this->_TextParamError(fmt::format("{}: Parameter {} expects a raw string", name, cur_idx)); + p += seprintf(p, lastofp, ":\"\""); + break; + } p += seprintf(p, lastofp, ":\"%s\"", std::get(this->param[cur_idx++]).c_str()); break; case StringParam::STRING: { - if (!std::holds_alternative(this->param[cur_idx])) throw Script_FatalError(fmt::format("{}: Parameter {} expects a substring", name, cur_idx)); + if (!std::holds_alternative(this->param[cur_idx])) { + this->_TextParamError(fmt::format("{}: Parameter {} expects a substring", name, cur_idx)); + p += seprintf(p, lastofp, ":\"\""); + break; + } int count = 1; // 1 because the string id is included in consumed parameters p += seprintf(p, lastofp, ":"); p = std::get(this->param[cur_idx++])->_GetEncodedText(p, lastofp, count, seen_ids); - if (count != cur_param.consumes) throw Script_FatalError(fmt::format("{}: Parameter {} substring consumes {}, but expected {} to be consumed", name, cur_idx, count - 1, cur_param.consumes - 1)); + if (count != cur_param.consumes) this->_TextParamError(fmt::format("{}: Parameter {} substring consumes {}, but expected {} to be consumed", name, cur_idx, count - 1, cur_param.consumes - 1)); break; } default: - if (cur_idx + cur_param.consumes > this->paramc) throw Script_FatalError(fmt::format("{}: Not enough parameters", name)); + if (cur_idx + cur_param.consumes > this->paramc) { + this->_TextParamError(fmt::format("{}: Not enough parameters", name)); + break; + } for (int i = 0; i < cur_param.consumes; i++) { - if (!std::holds_alternative(this->param[cur_idx])) throw Script_FatalError(fmt::format("{}: Parameter {} expects an integer", name, cur_idx)); + if (!std::holds_alternative(this->param[cur_idx])) { + this->_TextParamError(fmt::format("{}: Parameter {} expects an integer", name, cur_idx)); + p += seprintf(p, lastofp, ":0"); + break; + } p += seprintf(p, lastofp,":" OTTD_PRINTFHEX64, std::get(this->param[cur_idx++])); } } diff --git a/src/script/api/script_text.hpp b/src/script/api/script_text.hpp index 32dbc1ab10..0d86576138 100644 --- a/src/script/api/script_text.hpp +++ b/src/script/api/script_text.hpp @@ -135,6 +135,8 @@ private: std::variant param[SCRIPT_TEXT_MAX_PARAMETERS]; int paramc; + void _TextParamError(const std::string &msg); + /** * Internal function for recursive calling this function over multiple * instances, while writing in the same buffer. diff --git a/src/script/script_instance.cpp b/src/script/script_instance.cpp index fd0b21862b..69aeeeea79 100644 --- a/src/script/script_instance.cpp +++ b/src/script/script_instance.cpp @@ -62,7 +62,8 @@ ScriptInstance::ScriptInstance(const char *APIName) : is_paused(false), in_shutdown(false), callback(nullptr), - APIName(APIName) + APIName(APIName), + allow_text_param_mismatch(false) { this->storage = new ScriptStorage(); this->engine = new Squirrel(APIName); @@ -122,6 +123,16 @@ void ScriptInstance::RegisterAPI() bool ScriptInstance::LoadCompatibilityScripts(const char *api_version, Subdirectory dir) { + const char *api_vers[] = { "1.2", "1.3", "1.4", "1.5", "1.6", "1.7", "1.8", "1.9", "1.10", "1.11", "12", "13", "14" }; + uint api_idx = 0; + for (; api_idx < lengthof(api_vers) ; api_idx++) { + if (strcmp(api_version, api_vers[api_idx]) == 0) break; + } + if (api_idx < 12) { + /* 13 and below */ + this->allow_text_param_mismatch = true; + } + char script_name[32]; seprintf(script_name, lastof(script_name), "compat_%s.nut", api_version); for (Searchpath sp : _valid_searchpaths) { diff --git a/src/script/script_instance.hpp b/src/script/script_instance.hpp index c29b8d5606..da4cbe8f52 100644 --- a/src/script/script_instance.hpp +++ b/src/script/script_instance.hpp @@ -296,6 +296,7 @@ private: Script_SuspendCallbackProc *callback; ///< Callback that should be called in the next tick the script runs. size_t last_allocated_memory; ///< Last known allocated memory value (for display for crashed scripts) const char *APIName; ///< Name of the API used for this squirrel. + bool allow_text_param_mismatch; ///< Whether ScriptText parameter mismatches are allowed /** * Call the script Load function if it exists and data was loaded @@ -322,6 +323,9 @@ private: static bool LoadObjects(ScriptData *data); static bool LoadObjects(HSQUIRRELVM vm, ScriptData *data); + +public: + inline bool IsTextParamMismatchAllowed() const { return this->allow_text_param_mismatch; } }; #endif /* SCRIPT_INSTANCE_HPP */