diff --git a/core/io/plist.cpp b/core/io/plist.cpp
index dd1e6dc439f6..0ef70b42133c 100644
--- a/core/io/plist.cpp
+++ b/core/io/plist.cpp
@@ -383,14 +383,16 @@ void PListNode::store_text(String &p_stream, uint8_t p_indent) const {
p_stream += String("\t").repeat(p_indent);
p_stream += "\n";
p_stream += String("\t").repeat(p_indent);
- p_stream += String(data_string.get_data()) + "\n";
+ // Data should be Base64 (i.e. ASCII only).
+ p_stream += String::ascii(data_string) + "\n";
p_stream += String("\t").repeat(p_indent);
p_stream += "\n";
} break;
case PList::PLNodeType::PL_NODE_TYPE_DATE: {
p_stream += String("\t").repeat(p_indent);
p_stream += "";
- p_stream += String(data_string.get_data());
+ // Data should be ISO 8601 (i.e. ASCII only).
+ p_stream += String::ascii(data_string);
p_stream += "\n";
} break;
case PList::PLNodeType::PL_NODE_TYPE_STRING: {
diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp
index be50b0b4e91d..24ed4bcdedb1 100644
--- a/core/string/ustring.cpp
+++ b/core/string/ustring.cpp
@@ -1954,6 +1954,34 @@ CharString String::ascii(bool p_allow_extended) const {
return cs;
}
+Error String::parse_ascii(const StrRange &p_range) {
+ if (p_range.len == 0) {
+ resize(0);
+ return OK;
+ }
+
+ resize(p_range.len + 1); // Include \0
+
+ const char *src = p_range.c_str;
+ const char *end = src + p_range.len;
+ char32_t *dst = ptrw();
+ bool decode_failed = false;
+
+ for (; src < end; ++src, ++dst) {
+ // If char is int8_t, a set sign bit will be reinterpreted as 256 - val implicitly.
+ const uint8_t chr = *src;
+ if (chr > 127) {
+ print_unicode_error(vformat("Invalid ASCII codepoint (%x)", (uint32_t)chr), true);
+ decode_failed = true;
+ *dst = _replacement_char;
+ } else {
+ *dst = chr;
+ }
+ }
+ *dst = _null;
+ return decode_failed ? ERR_INVALID_DATA : OK;
+}
+
String String::utf8(const char *p_utf8, int p_len) {
String ret;
ret.parse_utf8(p_utf8, p_len);
diff --git a/core/string/ustring.h b/core/string/ustring.h
index 7c405d740293..3bbe5bf6fade 100644
--- a/core/string/ustring.h
+++ b/core/string/ustring.h
@@ -519,6 +519,15 @@ class String {
char32_t unicode_at(int p_idx) const;
CharString ascii(bool p_allow_extended = false) const;
+ // Parse an ascii string.
+ // If any character is > 127, an error will be logged, and 0xfffd will be inserted.
+ Error parse_ascii(const StrRange &p_range);
+ static String ascii(const StrRange &p_range) {
+ String s;
+ s.parse_ascii(p_range);
+ return s;
+ }
+
CharString utf8() const;
Error parse_utf8(const char *p_utf8, int p_len = -1, bool p_skip_cr = false);
Error parse_utf8(const StrRange p_range, bool p_skip_cr = false) {