diff --git a/bin/game/compat_14.nut b/bin/game/compat_14.nut index 00efae0b65..757e2496f4 100644 --- a/bin/game/compat_14.nut +++ b/bin/game/compat_14.nut @@ -9,6 +9,9 @@ GSBridge.GetBridgeID <- GSBridge.GetBridgeType; +/* Emulate old GSText parameter padding behaviour */ +GSText.SCRIPT_TEXT_MAX_PARAMETERS <- 20; + class GSCompat14 { function Text(text) { diff --git a/src/game/game_instance.cpp b/src/game/game_instance.cpp index 412e2b9204..1fc1df1fbd 100644 --- a/src/game/game_instance.cpp +++ b/src/game/game_instance.cpp @@ -50,9 +50,9 @@ void GameInstance::RegisterAPI() /* Register all classes */ SQGS_RegisterAll(*this->engine); - RegisterGameTranslation(*this->engine); - if (!this->LoadCompatibilityScripts(GAME_DIR, GameInfo::ApiVersions)) this->Died(); + + RegisterGameTranslation(*this->engine); } int GameInstance::GetSetting(const std::string &name) diff --git a/src/game/game_text.cpp b/src/game/game_text.cpp index 92482a706b..84bc577531 100644 --- a/src/game/game_text.cpp +++ b/src/game/game_text.cpp @@ -370,6 +370,8 @@ void RegisterGameTranslation(Squirrel &engine) sq_pop(vm, 2); + ScriptText::SetPadParameterCount(vm); + ReconsiderGameScriptLanguage(); } diff --git a/src/script/api/script_text.cpp b/src/script/api/script_text.cpp index 4e1c02ea3e..87468f4226 100644 --- a/src/script/api/script_text.cpp +++ b/src/script/api/script_text.cpp @@ -57,7 +57,7 @@ ScriptText::ScriptText(HSQUIRRELVM vm) SQInteger ScriptText::_SetParam(int parameter, HSQUIRRELVM vm) { - if (parameter >= SCRIPT_TEXT_MAX_PARAMETERS) return SQ_ERROR; + if (static_cast(parameter) >= std::size(this->param)) this->param.resize(parameter + 1); switch (sq_gettype(vm, -1)) { case OT_STRING: { @@ -99,10 +99,13 @@ SQInteger ScriptText::_SetParam(int parameter, HSQUIRRELVM vm) break; } + case OT_NULL: + this->param[parameter] = {}; + break; + default: return SQ_ERROR; } - if (this->paramc <= parameter) this->paramc = parameter + 1; return 0; } @@ -113,7 +116,6 @@ SQInteger ScriptText::SetParam(HSQUIRRELVM vm) SQInteger k; sq_getinteger(vm, 2, &k); - if (k > SCRIPT_TEXT_MAX_PARAMETERS) return SQ_ERROR; if (k < 1) return SQ_ERROR; k--; @@ -123,7 +125,7 @@ SQInteger ScriptText::SetParam(HSQUIRRELVM vm) SQInteger ScriptText::AddParam(HSQUIRRELVM vm) { SQInteger res; - res = this->_SetParam(this->paramc, vm); + res = this->_SetParam(static_cast(std::size(this->param)), vm); if (res != 0) return res; /* Push our own instance back on top of the stack */ @@ -140,7 +142,7 @@ SQInteger ScriptText::_set(HSQUIRRELVM vm) sq_getstring(vm, 2, view); std::string str = StrMakeValid(view); - if (!str.starts_with("param_") || str.size() > 8) return SQ_ERROR; + if (!str.starts_with("param_")) return SQ_ERROR; auto key = ParseInteger(str.substr(6)); if (!key.has_value()) return SQ_ERROR; @@ -153,13 +155,35 @@ SQInteger ScriptText::_set(HSQUIRRELVM vm) return SQ_ERROR; } - if (k > SCRIPT_TEXT_MAX_PARAMETERS) return SQ_ERROR; if (k < 1) return SQ_ERROR; k--; return this->_SetParam(k, vm); } +/** + * Set the number of padding parameters to use, for compatibility with old scripts. + * This is called during RegisterGameTranslation. + */ +void ScriptText::SetPadParameterCount(HSQUIRRELVM vm) +{ + ScriptText::pad_parameter_count = 0; + + SQInteger top = sq_gettop(vm); + sq_pushroottable(vm); + sq_pushstring(vm, "GSText", -1); + if (!SQ_FAILED(sq_get(vm, -2))) { + sq_pushstring(vm, "SCRIPT_TEXT_MAX_PARAMETERS", -1); + if (!SQ_FAILED(sq_get(vm, -2))) { + SQInteger value; + if (!SQ_FAILED(sq_getinteger(vm, -1, &value))) { + ScriptText::pad_parameter_count = value; + } + } + } + sq_pop(vm, top); +} + EncodedString ScriptText::GetEncodedText() { ScriptTextList seen_texts; @@ -169,7 +193,6 @@ EncodedString ScriptText::GetEncodedText() StringBuilder builder(result); this->_FillParamList(params, seen_texts); this->_GetEncodedText(builder, param_count, params, true); - if (param_count > SCRIPT_TEXT_MAX_PARAMETERS) throw Script_FatalError(fmt::format("{}: Too many parameters", GetGameStringName(this->string))); return ::EncodedString{std::move(result)}; } @@ -178,21 +201,21 @@ void ScriptText::_FillParamList(ParamList ¶ms, ScriptTextList &seen_texts) if (std::ranges::find(seen_texts, this) != seen_texts.end()) throw Script_FatalError(fmt::format("{}: Circular reference detected", GetGameStringName(this->string))); seen_texts.push_back(this); - for (int i = 0; i < this->paramc; i++) { - Param *p = &this->param[i]; - params.emplace_back(this->string, i, p); - if (!std::holds_alternative(*p)) continue; - std::get(*p)->_FillParamList(params, seen_texts); + for (int idx = 0; Param &p : this->param) { + params.emplace_back(this->string, idx, &p); + ++idx; + if (!std::holds_alternative(p)) continue; + std::get(p)->_FillParamList(params, seen_texts); } seen_texts.pop_back(); - /* Fill with dummy parameters to match FormatString() behaviour. */ - if (seen_texts.empty()) { - static Param dummy = 0; - int nb_extra = SCRIPT_TEXT_MAX_PARAMETERS - (int)params.size(); - for (int i = 0; i < nb_extra; i++) - params.emplace_back(StringIndexInTab(-1), i, &dummy); + /* Fill with dummy parameters to match old FormatString() compatibility behaviour. */ + if (seen_texts.empty() && ScriptText::pad_parameter_count > 0) { + static Param dummy = {}; + for (int idx = static_cast(std::size(this->param)); idx < ScriptText::pad_parameter_count; ++idx) { + params.emplace_back(StringIndexInTab(-1), idx, &dummy); + } } } @@ -204,6 +227,8 @@ void ScriptText::ParamCheck::Encode(StringBuilder &builder, std::string_view cmd struct visitor { StringBuilder &builder; + void operator()(const std::monostate &) { } + void operator()(std::string value) { this->builder.PutUtf8(SCC_ENCODED_STRING); diff --git a/src/script/api/script_text.hpp b/src/script/api/script_text.hpp index 50f4864358..62fd3ba587 100644 --- a/src/script/api/script_text.hpp +++ b/src/script/api/script_text.hpp @@ -75,8 +75,6 @@ private: */ class ScriptText : public Text { public: - static const int SCRIPT_TEXT_MAX_PARAMETERS = 20; ///< The maximum amount of parameters you can give to one object. - #ifndef DOXYGEN_API /** * The constructor wrapper from Squirrel. @@ -128,10 +126,15 @@ public: */ EncodedString GetEncodedText() override; + /** + * @api -all + */ + static void SetPadParameterCount(HSQUIRRELVM vm); + private: using ScriptTextRef = ScriptObjectRef; using ScriptTextList = std::vector; - using Param = std::variant; + using Param = std::variant; struct ParamCheck { StringIndexInTab owner; @@ -149,8 +152,9 @@ private: using ParamSpan = std::span; StringIndexInTab string; - std::array param = {}; - int paramc = 0; + std::vector param{}; + + static inline int pad_parameter_count = 0; ///< Pad parameters for relaxed string validation. /** * Internal function to recursively fill a list of parameters.