From 78e08fdd181fa69dfe27fb27f962032b0ef3f40b Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Thu, 25 Jan 2024 17:30:17 +0000 Subject: [PATCH] Script: Move old-style/lenient text param encode to separate function --- src/script/api/script_text.cpp | 202 ++++++++++++++++++--------------- src/script/api/script_text.hpp | 1 + 2 files changed, 109 insertions(+), 94 deletions(-) diff --git a/src/script/api/script_text.cpp b/src/script/api/script_text.cpp index 9dd4a508fd..b79d279caf 100644 --- a/src/script/api/script_text.cpp +++ b/src/script/api/script_text.cpp @@ -182,16 +182,8 @@ void ScriptText::_TextParamError(std::string msg) } } -void ScriptText::_GetEncodedText(std::back_insert_iterator &output, int ¶m_count, StringIDList &seen_ids) +void ScriptText::_GetEncodedTextParamsTraditional(std::back_insert_iterator &output, int ¶m_count, StringIDList &seen_ids, const std::string &name) { - const std::string &name = GetGameStringName(this->string); - - if (std::find(seen_ids.begin(), seen_ids.end(), this->string) != seen_ids.end()) throw Script_FatalError(fmt::format("{}: Circular reference detected", name)); - seen_ids.push_back(this->string); - - Utf8Encode(output, SCC_ENCODED); - fmt::format_to(output, "{:X}", this->string); - auto write_param_fallback = [&](int idx) { if (std::holds_alternative(this->param[idx])) { int count = 1; // 1 because the string id is included in consumed parameters @@ -210,9 +202,6 @@ void ScriptText::_GetEncodedText(std::back_insert_iterator &output, const StringParams ¶ms = GetGameStringParams(this->string); int cur_idx = 0; - int prev_string = -1; - int prev_idx = -1; - int prev_count = -1; for (const StringParam &cur_param : params) { if (cur_idx >= this->paramc) { @@ -220,107 +209,132 @@ void ScriptText::_GetEncodedText(std::back_insert_iterator &output, break; } - if (this->GetActiveInstance()->IsTextParamMismatchAllowed()) { - switch (cur_param.type) { - case StringParam::RAW_STRING: - if (!std::holds_alternative(this->param[cur_idx])) { - this->_TextParamError(fmt::format("{}: Parameter {} expects a raw string", name, cur_idx)); + switch (cur_param.type) { + case StringParam::RAW_STRING: + if (!std::holds_alternative(this->param[cur_idx])) { + this->_TextParamError(fmt::format("{}: Parameter {} expects a raw string", name, cur_idx)); + write_param_fallback(cur_idx++); + break; + } + fmt::format_to(output, ":\"{}\"", std::get(this->param[cur_idx++])); + param_count++; + break; + + case StringParam::STRING: { + if (!std::holds_alternative(this->param[cur_idx])) { + this->_TextParamError(fmt::format("{}: Parameter {} expects a substring", name, cur_idx)); + write_param_fallback(cur_idx++); + break; + } + int count = 1; // 1 because the string id is included in consumed parameters + fmt::format_to(output, ":"); + std::get(this->param[cur_idx++])->_GetEncodedText(output, count, seen_ids); + 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)); + } + param_count += count; + break; + } + + default: + if (cur_idx + cur_param.consumes > this->paramc) { + this->_TextParamError(fmt::format("{}: Not enough parameters", name)); + } + for (int i = 0; i < cur_param.consumes && cur_idx < this->paramc; i++) { + if (!std::holds_alternative(this->param[cur_idx])) { + this->_TextParamError(fmt::format("{}: Parameter {} expects an integer", name, cur_idx)); write_param_fallback(cur_idx++); - break; + continue; } - fmt::format_to(output, ":\"{}\"", std::get(this->param[cur_idx++])); + fmt::format_to(output, ":{:X}", std::get(this->param[cur_idx++])); param_count++; + } + break; + } + } + + for (int i = cur_idx; i < this->paramc; i++) { + write_param_fallback(i); + } +} + +void ScriptText::_GetEncodedText(std::back_insert_iterator &output, int ¶m_count, StringIDList &seen_ids) +{ + const std::string &name = GetGameStringName(this->string); + + if (std::find(seen_ids.begin(), seen_ids.end(), this->string) != seen_ids.end()) throw Script_FatalError(fmt::format("{}: Circular reference detected", name)); + seen_ids.push_back(this->string); + + Utf8Encode(output, SCC_ENCODED); + fmt::format_to(output, "{:X}", this->string); + + if (this->GetActiveInstance()->IsTextParamMismatchAllowed()) { + this->_GetEncodedTextParamsTraditional(output, param_count, seen_ids, name); + seen_ids.pop_back(); + return; + } + + const StringParams ¶ms = GetGameStringParams(this->string); + int cur_idx = 0; + int prev_string = -1; + int prev_idx = -1; + int prev_count = -1; + + for (const StringParam &cur_param : params) { + if (cur_idx >= this->paramc) throw Script_FatalError(fmt::format("{}: Not enough parameters", name)); + + if (prev_string != -1) { + /* The previous substring added more parameters than expected, means we will consume them but can't properly validate them. */ + for (int i = 0; i < cur_param.consumes; i++) { + if (prev_idx < prev_count) { + ScriptLog::Warning(fmt::format("{}: Parameter {} uses parameter {} from substring {} and cannot be validated", name, param_count + i, prev_idx++, prev_string)); + } else { + /* No more extra parameters, assume SQInteger are expected. */ + if (cur_idx >= this->paramc) throw Script_FatalError(fmt::format("{}: Not enough parameters", name)); + if (!std::holds_alternative(this->param[cur_idx])) throw Script_FatalError(fmt::format("{}: Parameter {} expects an integer", name, param_count + i)); + fmt::format_to(output, ":{:X}", std::get(this->param[cur_idx++])); + } + } + if (prev_idx == prev_count) { + /* Re-enable validation. */ + prev_string = -1; + } + } else { + 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, param_count)); + fmt::format_to(output, ":\"{}\"", std::get(this->param[cur_idx++])); break; case StringParam::STRING: { - if (!std::holds_alternative(this->param[cur_idx])) { - this->_TextParamError(fmt::format("{}: Parameter {} expects a substring", name, cur_idx)); - write_param_fallback(cur_idx++); - break; - } - int count = 1; // 1 because the string id is included in consumed parameters + if (!std::holds_alternative(this->param[cur_idx])) throw Script_FatalError(fmt::format("{}: Parameter {} expects a substring", name, param_count)); + int count = 0; fmt::format_to(output, ":"); std::get(this->param[cur_idx++])->_GetEncodedText(output, count, seen_ids); - 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)); + if (++count != cur_param.consumes) { + ScriptLog::Error(fmt::format("{}: Parameter {} substring consumes {}, but expected {} to be consumed", name, param_count, count - 1, cur_param.consumes - 1)); + /* Fill missing params if needed. */ + for (int i = count; i < cur_param.consumes; i++) fmt::format_to(output, ":0"); + /* Disable validation for the extra params if any. */ + if (count > cur_param.consumes) { + prev_string = param_count; + prev_idx = cur_param.consumes - 1; + prev_count = count - 1; + } } - param_count += count; break; } default: - if (cur_idx + cur_param.consumes > this->paramc) { - this->_TextParamError(fmt::format("{}: Not enough parameters", name)); - } - for (int i = 0; i < cur_param.consumes && cur_idx < this->paramc; i++) { - if (!std::holds_alternative(this->param[cur_idx])) { - this->_TextParamError(fmt::format("{}: Parameter {} expects an integer", name, cur_idx)); - write_param_fallback(cur_idx++); - continue; - } - fmt::format_to(output, ":{:X}", std::get(this->param[cur_idx++])); - param_count++; - } - break; - } - } else { - if (prev_string != -1) { - /* The previous substring added more parameters than expected, means we will consume them but can't properly validate them. */ - for (int i = 0; i < cur_param.consumes; i++) { - if (prev_idx < prev_count) { - ScriptLog::Warning(fmt::format("{}: Parameter {} uses parameter {} from substring {} and cannot be validated", name, param_count + i, prev_idx++, prev_string)); - } else { - /* No more extra parameters, assume SQInteger are expected. */ - if (cur_idx >= this->paramc) throw Script_FatalError(fmt::format("{}: Not enough parameters", name)); + if (cur_idx + cur_param.consumes > this->paramc) throw Script_FatalError(fmt::format("{}: Not enough parameters", name)); + 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, param_count + i)); fmt::format_to(output, ":{:X}", std::get(this->param[cur_idx++])); } - } - if (prev_idx == prev_count) { - /* Re-enable validation. */ - prev_string = -1; - } - } else { - 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, param_count)); - fmt::format_to(output, ":\"{}\"", std::get(this->param[cur_idx++])); - break; - - case StringParam::STRING: { - if (!std::holds_alternative(this->param[cur_idx])) throw Script_FatalError(fmt::format("{}: Parameter {} expects a substring", name, param_count)); - int count = 0; - fmt::format_to(output, ":"); - std::get(this->param[cur_idx++])->_GetEncodedText(output, count, seen_ids); - if (++count != cur_param.consumes) { - ScriptLog::Error(fmt::format("{}: Parameter {} substring consumes {}, but expected {} to be consumed", name, param_count, count - 1, cur_param.consumes - 1)); - /* Fill missing params if needed. */ - for (int i = count; i < cur_param.consumes; i++) fmt::format_to(output, ":0"); - /* Disable validation for the extra params if any. */ - if (count > cur_param.consumes) { - prev_string = param_count; - prev_idx = cur_param.consumes - 1; - prev_count = count - 1; - } - } - break; - } - - default: - if (cur_idx + cur_param.consumes > this->paramc) throw Script_FatalError(fmt::format("{}: Not enough parameters", name)); - 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, param_count + i)); - fmt::format_to(output, ":{:X}", std::get(this->param[cur_idx++])); - } - } } } - } - if (this->GetActiveInstance()->IsTextParamMismatchAllowed()) { - for (int i = cur_idx; i < this->paramc; i++) { - write_param_fallback(i); - } + param_count += cur_param.consumes; } seen_ids.pop_back(); diff --git a/src/script/api/script_text.hpp b/src/script/api/script_text.hpp index 8f91e29b39..1954ffe5fe 100644 --- a/src/script/api/script_text.hpp +++ b/src/script/api/script_text.hpp @@ -136,6 +136,7 @@ private: int paramc; void _TextParamError(std::string msg); + void _GetEncodedTextParamsTraditional(std::back_insert_iterator &output, int ¶m_count, StringIDList &seen_ids, const std::string &name); /** * Internal function for recursive calling this function over multiple