diff options
Diffstat (limited to 'core/string')
-rw-r--r-- | core/string/locales.h | 2 | ||||
-rw-r--r-- | core/string/optimized_translation.cpp | 51 | ||||
-rw-r--r-- | core/string/optimized_translation.h | 1 | ||||
-rw-r--r-- | core/string/translation.cpp | 140 | ||||
-rw-r--r-- | core/string/translation.h | 2 | ||||
-rw-r--r-- | core/string/translation_po.cpp | 17 | ||||
-rw-r--r-- | core/string/translation_po.h | 1 | ||||
-rw-r--r-- | core/string/ustring.cpp | 165 | ||||
-rw-r--r-- | core/string/ustring.h | 11 |
9 files changed, 262 insertions, 128 deletions
diff --git a/core/string/locales.h b/core/string/locales.h index 32d6608ec2..0ccb17a436 100644 --- a/core/string/locales.h +++ b/core/string/locales.h @@ -1072,6 +1072,7 @@ static const char *script_list[][2] = { { "Jurchen", "Jurc" }, { "Kayah Li", "Kali" }, { "Katakana", "Kana" }, + { "Kawi", "Kawi" }, { "Kharoshthi", "Khar" }, { "Khmer", "Khmr" }, { "Khojki", "Khoj" }, @@ -1110,6 +1111,7 @@ static const char *script_list[][2] = { { "Meitei Mayek", "Mtei" }, { "Multani", "Mult" }, { "Myanmar (Burmese)", "Mymr" }, + { "Nag Mundari", "Nagm" }, { "Nandinagari", "Nand" }, { "Old North Arabian", "Narb" }, { "Nabataean", "Nbat" }, diff --git a/core/string/optimized_translation.cpp b/core/string/optimized_translation.cpp index 07302cc8c3..2fb3b54e27 100644 --- a/core/string/optimized_translation.cpp +++ b/core/string/optimized_translation.cpp @@ -179,14 +179,14 @@ void OptimizedTranslation::generate(const Ref<Translation> &p_from) { } bool OptimizedTranslation::_set(const StringName &p_name, const Variant &p_value) { - String name = p_name.operator String(); - if (name == "hash_table") { + String prop_name = p_name.operator String(); + if (prop_name == "hash_table") { hash_table = p_value; - } else if (name == "bucket_table") { + } else if (prop_name == "bucket_table") { bucket_table = p_value; - } else if (name == "strings") { + } else if (prop_name == "strings") { strings = p_value; - } else if (name == "load_from") { + } else if (prop_name == "load_from") { generate(p_value); } else { return false; @@ -196,12 +196,12 @@ bool OptimizedTranslation::_set(const StringName &p_name, const Variant &p_value } bool OptimizedTranslation::_get(const StringName &p_name, Variant &r_ret) const { - String name = p_name.operator String(); - if (name == "hash_table") { + String prop_name = p_name.operator String(); + if (prop_name == "hash_table") { r_ret = hash_table; - } else if (name == "bucket_table") { + } else if (prop_name == "bucket_table") { r_ret = bucket_table; - } else if (name == "strings") { + } else if (prop_name == "strings") { r_ret = strings; } else { return false; @@ -267,6 +267,39 @@ StringName OptimizedTranslation::get_message(const StringName &p_src_text, const } } +Vector<String> OptimizedTranslation::get_translated_message_list() const { + Vector<String> msgs; + + const int *htr = hash_table.ptr(); + const uint32_t *htptr = (const uint32_t *)&htr[0]; + const int *btr = bucket_table.ptr(); + const uint32_t *btptr = (const uint32_t *)&btr[0]; + const uint8_t *sr = strings.ptr(); + const char *sptr = (const char *)&sr[0]; + + for (int i = 0; i < hash_table.size(); i++) { + uint32_t p = htptr[i]; + if (p != 0xFFFFFFFF) { + const Bucket &bucket = *(const Bucket *)&btptr[p]; + for (int j = 0; j < bucket.size; j++) { + if (bucket.elem[j].comp_size == bucket.elem[j].uncomp_size) { + String rstr; + rstr.parse_utf8(&sptr[bucket.elem[j].str_offset], bucket.elem[j].uncomp_size); + msgs.push_back(rstr); + } else { + CharString uncomp; + uncomp.resize(bucket.elem[j].uncomp_size + 1); + smaz_decompress(&sptr[bucket.elem[j].str_offset], bucket.elem[j].comp_size, uncomp.ptrw(), bucket.elem[j].uncomp_size); + String rstr; + rstr.parse_utf8(uncomp.get_data()); + msgs.push_back(rstr); + } + } + } + } + return msgs; +} + StringName OptimizedTranslation::get_plural_message(const StringName &p_src_text, const StringName &p_plural_text, int p_n, const StringName &p_context) const { // The use of plurals translation is not yet supported in OptimizedTranslation. return get_message(p_src_text, p_context); diff --git a/core/string/optimized_translation.h b/core/string/optimized_translation.h index f3dbfe8f5c..1cd12782d0 100644 --- a/core/string/optimized_translation.h +++ b/core/string/optimized_translation.h @@ -81,6 +81,7 @@ protected: public: virtual StringName get_message(const StringName &p_src_text, const StringName &p_context = "") const override; //overridable for other implementations virtual StringName get_plural_message(const StringName &p_src_text, const StringName &p_plural_text, int p_n, const StringName &p_context = "") const override; + virtual Vector<String> get_translated_message_list() const override; void generate(const Ref<Translation> &p_from); OptimizedTranslation() {} diff --git a/core/string/translation.cpp b/core/string/translation.cpp index 4748f1a0cb..d1ac91957a 100644 --- a/core/string/translation.cpp +++ b/core/string/translation.cpp @@ -59,6 +59,18 @@ Vector<String> Translation::_get_message_list() const { return msgs; } +Vector<String> Translation::get_translated_message_list() const { + Vector<String> msgs; + msgs.resize(translation_map.size()); + int idx = 0; + for (const KeyValue<StringName, StringName> &E : translation_map) { + msgs.set(idx, E.value); + idx += 1; + } + + return msgs; +} + void Translation::_set_messages(const Dictionary &p_messages) { List<Variant> keys; p_messages.get_key_list(&keys); @@ -140,6 +152,7 @@ void Translation::_bind_methods() { ClassDB::bind_method(D_METHOD("get_plural_message", "src_message", "src_plural_message", "n", "context"), &Translation::get_plural_message, DEFVAL("")); ClassDB::bind_method(D_METHOD("erase_message", "src_message", "context"), &Translation::erase_message, DEFVAL("")); ClassDB::bind_method(D_METHOD("get_message_list"), &Translation::_get_message_list); + ClassDB::bind_method(D_METHOD("get_translated_message_list"), &Translation::get_translated_message_list); ClassDB::bind_method(D_METHOD("get_message_count"), &Translation::get_message_count); ClassDB::bind_method(D_METHOD("_set_messages", "messages"), &Translation::_set_messages); ClassDB::bind_method(D_METHOD("_get_messages"), &Translation::_get_messages); @@ -293,31 +306,35 @@ void TranslationServer::init_locale_info() { } String TranslationServer::standardize_locale(const String &p_locale) const { + return _standardize_locale(p_locale, false); +} + +String TranslationServer::_standardize_locale(const String &p_locale, bool p_add_defaults) const { // Replaces '-' with '_' for macOS style locales. String univ_locale = p_locale.replace("-", "_"); // Extract locale elements. - String lang, script, country, variant; + String lang_name, script_name, country_name, variant_name; Vector<String> locale_elements = univ_locale.get_slice("@", 0).split("_"); - lang = locale_elements[0]; + lang_name = locale_elements[0]; if (locale_elements.size() >= 2) { if (locale_elements[1].length() == 4 && is_ascii_upper_case(locale_elements[1][0]) && is_ascii_lower_case(locale_elements[1][1]) && is_ascii_lower_case(locale_elements[1][2]) && is_ascii_lower_case(locale_elements[1][3])) { - script = locale_elements[1]; + script_name = locale_elements[1]; } if (locale_elements[1].length() == 2 && is_ascii_upper_case(locale_elements[1][0]) && is_ascii_upper_case(locale_elements[1][1])) { - country = locale_elements[1]; + country_name = locale_elements[1]; } } if (locale_elements.size() >= 3) { if (locale_elements[2].length() == 2 && is_ascii_upper_case(locale_elements[2][0]) && is_ascii_upper_case(locale_elements[2][1])) { - country = locale_elements[2]; - } else if (variant_map.has(locale_elements[2].to_lower()) && variant_map[locale_elements[2].to_lower()] == lang) { - variant = locale_elements[2].to_lower(); + country_name = locale_elements[2]; + } else if (variant_map.has(locale_elements[2].to_lower()) && variant_map[locale_elements[2].to_lower()] == lang_name) { + variant_name = locale_elements[2].to_lower(); } } if (locale_elements.size() >= 4) { - if (variant_map.has(locale_elements[3].to_lower()) && variant_map[locale_elements[3].to_lower()] == lang) { - variant = locale_elements[3].to_lower(); + if (variant_map.has(locale_elements[3].to_lower()) && variant_map[locale_elements[3].to_lower()] == lang_name) { + variant_name = locale_elements[3].to_lower(); } } @@ -325,74 +342,76 @@ String TranslationServer::standardize_locale(const String &p_locale) const { Vector<String> script_extra = univ_locale.get_slice("@", 1).split(";"); for (int i = 0; i < script_extra.size(); i++) { if (script_extra[i].to_lower() == "cyrillic") { - script = "Cyrl"; + script_name = "Cyrl"; break; } else if (script_extra[i].to_lower() == "latin") { - script = "Latn"; + script_name = "Latn"; break; } else if (script_extra[i].to_lower() == "devanagari") { - script = "Deva"; + script_name = "Deva"; break; - } else if (variant_map.has(script_extra[i].to_lower()) && variant_map[script_extra[i].to_lower()] == lang) { - variant = script_extra[i].to_lower(); + } else if (variant_map.has(script_extra[i].to_lower()) && variant_map[script_extra[i].to_lower()] == lang_name) { + variant_name = script_extra[i].to_lower(); } } // Handles known non-ISO language names used e.g. on Windows. - if (locale_rename_map.has(lang)) { - lang = locale_rename_map[lang]; + if (locale_rename_map.has(lang_name)) { + lang_name = locale_rename_map[lang_name]; } // Handle country renames. - if (country_rename_map.has(country)) { - country = country_rename_map[country]; + if (country_rename_map.has(country_name)) { + country_name = country_rename_map[country_name]; } // Remove unsupported script codes. - if (!script_map.has(script)) { - script = ""; + if (!script_map.has(script_name)) { + script_name = ""; } // Add script code base on language and country codes for some ambiguous cases. - if (script.is_empty()) { - for (int i = 0; i < locale_script_info.size(); i++) { - const LocaleScriptInfo &info = locale_script_info[i]; - if (info.name == lang) { - if (country.is_empty() || info.supported_countries.has(country)) { - script = info.script; - break; + if (p_add_defaults) { + if (script_name.is_empty()) { + for (int i = 0; i < locale_script_info.size(); i++) { + const LocaleScriptInfo &info = locale_script_info[i]; + if (info.name == lang_name) { + if (country_name.is_empty() || info.supported_countries.has(country_name)) { + script_name = info.script; + break; + } } } } - } - if (!script.is_empty() && country.is_empty()) { - // Add conntry code based on script for some ambiguous cases. - for (int i = 0; i < locale_script_info.size(); i++) { - const LocaleScriptInfo &info = locale_script_info[i]; - if (info.name == lang && info.script == script) { - country = info.default_country; - break; + if (!script_name.is_empty() && country_name.is_empty()) { + // Add conntry code based on script for some ambiguous cases. + for (int i = 0; i < locale_script_info.size(); i++) { + const LocaleScriptInfo &info = locale_script_info[i]; + if (info.name == lang_name && info.script == script_name) { + country_name = info.default_country; + break; + } } } } // Combine results. - String locale = lang; - if (!script.is_empty()) { - locale = locale + "_" + script; + String out = lang_name; + if (!script_name.is_empty()) { + out = out + "_" + script_name; } - if (!country.is_empty()) { - locale = locale + "_" + country; + if (!country_name.is_empty()) { + out = out + "_" + country_name; } - if (!variant.is_empty()) { - locale = locale + "_" + variant; + if (!variant_name.is_empty()) { + out = out + "_" + variant_name; } - return locale; + return out; } int TranslationServer::compare_locales(const String &p_locale_a, const String &p_locale_b) const { - String locale_a = standardize_locale(p_locale_a); - String locale_b = standardize_locale(p_locale_b); + String locale_a = _standardize_locale(p_locale_a, true); + String locale_b = _standardize_locale(p_locale_b, true); if (locale_a == locale_b) { // Exact match. @@ -420,31 +439,29 @@ int TranslationServer::compare_locales(const String &p_locale_a, const String &p } String TranslationServer::get_locale_name(const String &p_locale) const { - String locale = standardize_locale(p_locale); - - String lang, script, country; - Vector<String> locale_elements = locale.split("_"); - lang = locale_elements[0]; + String lang_name, script_name, country_name; + Vector<String> locale_elements = standardize_locale(p_locale).split("_"); + lang_name = locale_elements[0]; if (locale_elements.size() >= 2) { if (locale_elements[1].length() == 4 && is_ascii_upper_case(locale_elements[1][0]) && is_ascii_lower_case(locale_elements[1][1]) && is_ascii_lower_case(locale_elements[1][2]) && is_ascii_lower_case(locale_elements[1][3])) { - script = locale_elements[1]; + script_name = locale_elements[1]; } if (locale_elements[1].length() == 2 && is_ascii_upper_case(locale_elements[1][0]) && is_ascii_upper_case(locale_elements[1][1])) { - country = locale_elements[1]; + country_name = locale_elements[1]; } } if (locale_elements.size() >= 3) { if (locale_elements[2].length() == 2 && is_ascii_upper_case(locale_elements[2][0]) && is_ascii_upper_case(locale_elements[2][1])) { - country = locale_elements[2]; + country_name = locale_elements[2]; } } - String name = language_map[lang]; - if (!script.is_empty()) { - name = name + " (" + script_map[script] + ")"; + String name = language_map[lang_name]; + if (!script_name.is_empty()) { + name = name + " (" + script_map[script_name] + ")"; } - if (!country.is_empty()) { - name = name + ", " + country_name_map[country]; + if (!country_name.is_empty()) { + name = name + ", " + country_name_map[country_name]; } return name; } @@ -630,12 +647,12 @@ TranslationServer *TranslationServer::singleton = nullptr; bool TranslationServer::_load_translations(const String &p_from) { if (ProjectSettings::get_singleton()->has_setting(p_from)) { - Vector<String> translations = ProjectSettings::get_singleton()->get(p_from); + const Vector<String> &translation_names = GLOBAL_GET(p_from); - int tcount = translations.size(); + int tcount = translation_names.size(); if (tcount) { - const String *r = translations.ptr(); + const String *r = translation_names.ptr(); for (int i = 0; i < tcount; i++) { Ref<Translation> tr = ResourceLoader::load(r[i]); @@ -964,7 +981,6 @@ void TranslationServer::_bind_methods() { } void TranslationServer::load_translations() { - String locale = get_locale(); _load_translations("internationalization/locale/translations"); //all _load_translations("internationalization/locale/translations_" + locale.substr(0, 2)); diff --git a/core/string/translation.h b/core/string/translation.h index 3f97a8d4fc..5e8344baac 100644 --- a/core/string/translation.h +++ b/core/string/translation.h @@ -64,6 +64,7 @@ public: virtual void erase_message(const StringName &p_src_text, const StringName &p_context = ""); virtual void get_message_list(List<StringName> *r_messages) const; virtual int get_message_count() const; + virtual Vector<String> get_translated_message_list() const; Translation() {} }; @@ -102,6 +103,7 @@ class TranslationServer : public Object { static TranslationServer *singleton; bool _load_translations(const String &p_from); + String _standardize_locale(const String &p_locale, bool p_add_defaults) const; StringName _get_message_from_translations(const StringName &p_message, const StringName &p_context, const String &p_locale, bool plural, const String &p_message_plural = "", int p_n = 0) const; diff --git a/core/string/translation_po.cpp b/core/string/translation_po.cpp index fa656b634d..724c1d42bc 100644 --- a/core/string/translation_po.cpp +++ b/core/string/translation_po.cpp @@ -103,6 +103,23 @@ void TranslationPO::_set_messages(const Dictionary &p_messages) { } } +Vector<String> TranslationPO::get_translated_message_list() const { + Vector<String> msgs; + for (const KeyValue<StringName, HashMap<StringName, Vector<StringName>>> &E : translation_map) { + if (E.key != StringName()) { + continue; + } + + for (const KeyValue<StringName, Vector<StringName>> &E2 : E.value) { + for (const StringName &E3 : E2.value) { + msgs.push_back(E3); + } + } + } + + return msgs; +} + Vector<String> TranslationPO::_get_message_list() const { // Return all keys in translation_map. diff --git a/core/string/translation_po.h b/core/string/translation_po.h index 7d63af2246..c50ea85744 100644 --- a/core/string/translation_po.h +++ b/core/string/translation_po.h @@ -70,6 +70,7 @@ protected: static void _bind_methods(); public: + Vector<String> get_translated_message_list() const override; void get_message_list(List<StringName> *r_messages) const override; int get_message_count() const override; void add_message(const StringName &p_src_text, const StringName &p_xlated_text, const StringName &p_context = "") override; diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp index d8b93998af..4e26b61334 100644 --- a/core/string/ustring.cpp +++ b/core/string/ustring.cpp @@ -161,6 +161,18 @@ bool CharString::operator<(const CharString &p_right) const { return is_str_less(get_data(), p_right.get_data()); } +bool CharString::operator==(const CharString &p_right) const { + if (length() == 0) { + // True if both have length 0, false if only p_right has a length + return p_right.length() == 0; + } else if (p_right.length() == 0) { + // False due to unequal length + return false; + } + + return strcmp(ptr(), p_right.ptr()) == 0; +} + CharString &CharString::operator+=(char p_char) { const int lhs_len = length(); resize(lhs_len + 2); @@ -1180,9 +1192,14 @@ Vector<String> String::split(const String &p_splitter, bool p_allow_empty, int p int len = length(); while (true) { - int end = find(p_splitter, from); - if (end < 0) { - end = len; + int end; + if (p_splitter.is_empty()) { + end = from + 1; + } else { + end = find(p_splitter, from); + if (end < 0) { + end = len; + } } if (p_allow_empty || (end > from)) { if (p_maxsplit <= 0) { @@ -1223,7 +1240,15 @@ Vector<String> String::rsplit(const String &p_splitter, bool p_allow_empty, int break; } - int left_edge = rfind(p_splitter, remaining_len - p_splitter.length()); + int left_edge; + if (p_splitter.is_empty()) { + left_edge = remaining_len - 1; + if (left_edge == 0) { + left_edge--; // Skip to the < 0 condition. + } + } else { + left_edge = rfind(p_splitter, remaining_len - p_splitter.length()); + } if (left_edge < 0) { // no more splitters, we're done @@ -1243,8 +1268,8 @@ Vector<String> String::rsplit(const String &p_splitter, bool p_allow_empty, int return ret; } -Vector<float> String::split_floats(const String &p_splitter, bool p_allow_empty) const { - Vector<float> ret; +Vector<double> String::split_floats(const String &p_splitter, bool p_allow_empty) const { + Vector<double> ret; int from = 0; int len = length(); @@ -1447,15 +1472,25 @@ String String::num(double p_num, int p_decimals) { fmt[5] = 'f'; fmt[6] = 0; } - char buf[256]; + // if we want to convert a double with as much decimal places as as + // DBL_MAX or DBL_MIN then we would theoretically need a buffer of at least + // DBL_MAX_10_EXP + 2 for DBL_MAX and DBL_MAX_10_EXP + 4 for DBL_MIN. + // BUT those values where still giving me exceptions, so I tested from + // DBL_MAX_10_EXP + 10 incrementing one by one and DBL_MAX_10_EXP + 17 (325) + // was the first buffer size not to throw an exception + char buf[325]; #if defined(__GNUC__) || defined(_MSC_VER) - snprintf(buf, 256, fmt, p_num); + // PLEASE NOTE that, albeit vcrt online reference states that snprintf + // should safely truncate the output to the given buffer size, we have + // found a case where this is not true, so we should create a buffer + // as big as needed + snprintf(buf, 325, fmt, p_num); #else sprintf(buf, fmt, p_num); #endif - buf[255] = 0; + buf[324] = 0; //destroy trailing zeroes { bool period = false; @@ -1567,10 +1602,11 @@ String String::num_real(double p_num, bool p_trailing) { #else int decimals = 6; #endif - // We want to align the digits to the above sane default, so we only - // need to subtract log10 for numbers with a positive power of ten. - if (p_num > 10) { - decimals -= (int)floor(log10(p_num)); + // We want to align the digits to the above sane default, so we only need + // to subtract log10 for numbers with a positive power of ten magnitude. + double abs_num = Math::abs(p_num); + if (abs_num > 10) { + decimals -= (int)floor(log10(abs_num)); } return num(p_num, decimals); } @@ -2624,10 +2660,11 @@ double String::to_float() const { uint32_t String::hash(const char *p_cstr) { uint32_t hashv = 5381; - uint32_t c; + uint32_t c = *p_cstr++; - while ((c = *p_cstr++)) { + while (c) { hashv = ((hashv << 5) + hashv) + c; /* hash * 33 + c */ + c = *p_cstr++; } return hashv; @@ -2653,10 +2690,11 @@ uint32_t String::hash(const wchar_t *p_cstr, int p_len) { uint32_t String::hash(const wchar_t *p_cstr) { uint32_t hashv = 5381; - uint32_t c; + uint32_t c = *p_cstr++; - while ((c = *p_cstr++)) { + while (c) { hashv = ((hashv << 5) + hashv) + c; /* hash * 33 + c */ + c = *p_cstr++; } return hashv; @@ -2673,10 +2711,11 @@ uint32_t String::hash(const char32_t *p_cstr, int p_len) { uint32_t String::hash(const char32_t *p_cstr) { uint32_t hashv = 5381; - uint32_t c; + uint32_t c = *p_cstr++; - while ((c = *p_cstr++)) { + while (c) { hashv = ((hashv << 5) + hashv) + c; /* hash * 33 + c */ + c = *p_cstr++; } return hashv; @@ -2687,10 +2726,11 @@ uint32_t String::hash() const { const char32_t *chr = get_data(); uint32_t hashv = 5381; - uint32_t c; + uint32_t c = *chr++; - while ((c = *chr++)) { + while (c) { hashv = ((hashv << 5) + hashv) + c; /* hash * 33 + c */ + c = *chr++; } return hashv; @@ -2701,10 +2741,11 @@ uint64_t String::hash64() const { const char32_t *chr = get_data(); uint64_t hashv = 5381; - uint64_t c; + uint64_t c = *chr++; - while ((c = *chr++)) { + while (c) { hashv = ((hashv << 5) + hashv) + c; /* hash * 33 + c */ + c = *chr++; } return hashv; @@ -2810,7 +2851,7 @@ String String::substr(int p_from, int p_chars) const { return String(*this); } - String s = String(); + String s; s.copy_from_unchecked(&get_data()[p_from], p_chars); return s; } @@ -3662,29 +3703,43 @@ bool String::is_network_share_path() const { String String::simplify_path() const { String s = *this; String drive; - if (s.begins_with("local://")) { - drive = "local://"; - s = s.substr(8, s.length()); - } else if (s.begins_with("res://")) { - drive = "res://"; - s = s.substr(6, s.length()); - } else if (s.begins_with("user://")) { - drive = "user://"; - s = s.substr(7, s.length()); - } else if (is_network_share_path()) { - drive = s.substr(0, 2); - s = s.substr(2, s.length() - 2); - } else if (s.begins_with("/") || s.begins_with("\\")) { - drive = s.substr(0, 1); - s = s.substr(1, s.length() - 1); - } else { - int p = s.find(":/"); - if (p == -1) { - p = s.find(":\\"); + + // Check if we have a special path (like res://) or a protocol identifier. + int p = s.find("://"); + bool found = false; + if (p > 0) { + bool only_chars = true; + for (int i = 0; i < p; i++) { + if (!is_ascii_alphanumeric_char(s[i])) { + only_chars = false; + break; + } + } + if (only_chars) { + found = true; + drive = s.substr(0, p + 3); + s = s.substr(p + 3); } - if (p != -1 && p < s.find("/")) { - drive = s.substr(0, p + 2); - s = s.substr(p + 2, s.length()); + } + if (!found) { + if (is_network_share_path()) { + // Network path, beginning with // or \\. + drive = s.substr(0, 2); + s = s.substr(2); + } else if (s.begins_with("/") || s.begins_with("\\")) { + // Absolute path. + drive = s.substr(0, 1); + s = s.substr(1); + } else { + // Windows-style drive path, like C:/ or C:\. + p = s.find(":/"); + if (p == -1) { + p = s.find(":\\"); + } + if (p != -1 && p < s.find("/")) { + drive = s.substr(0, p + 2); + s = s.substr(p + 2); + } } } @@ -3879,7 +3934,6 @@ String String::c_unescape() const { escaped = escaped.replace("\\v", "\v"); escaped = escaped.replace("\\'", "\'"); escaped = escaped.replace("\\\"", "\""); - escaped = escaped.replace("\\?", "\?"); escaped = escaped.replace("\\\\", "\\"); return escaped; @@ -3896,7 +3950,6 @@ String String::c_escape() const { escaped = escaped.replace("\t", "\\t"); escaped = escaped.replace("\v", "\\v"); escaped = escaped.replace("\'", "\\'"); - escaped = escaped.replace("\?", "\\?"); escaped = escaped.replace("\"", "\\\""); return escaped; @@ -4646,15 +4699,18 @@ String String::sprintf(const Array &values, bool *error) const { double value = values[value_index]; bool is_negative = (value < 0); String str = String::num(ABS(value), min_decimals); + const bool is_finite = Math::is_finite(value); // Pad decimals out. - str = str.pad_decimals(min_decimals); + if (is_finite) { + str = str.pad_decimals(min_decimals); + } int initial_len = str.length(); // Padding. Leave room for sign later if required. int pad_chars_count = (is_negative || show_sign) ? min_chars - 1 : min_chars; - String pad_char = pad_with_zeros ? String("0") : String(" "); + String pad_char = (pad_with_zeros && is_finite) ? String("0") : String(" "); // Never pad NaN or inf with zeros if (left_justified) { str = str.rpad(pad_chars_count, pad_char); } else { @@ -4704,14 +4760,19 @@ String String::sprintf(const Array &values, bool *error) const { String str = "("; for (int i = 0; i < count; i++) { double val = vec[i]; + String number_str = String::num(ABS(val), min_decimals); + const bool is_finite = Math::is_finite(val); + // Pad decimals out. - String number_str = String::num(ABS(val), min_decimals).pad_decimals(min_decimals); + if (is_finite) { + number_str = number_str.pad_decimals(min_decimals); + } int initial_len = number_str.length(); // Padding. Leave room for sign later if required. int pad_chars_count = val < 0 ? min_chars - 1 : min_chars; - String pad_char = pad_with_zeros ? String("0") : String(" "); + String pad_char = (pad_with_zeros && is_finite) ? String("0") : String(" "); // Never pad NaN or inf with zeros if (left_justified) { number_str = number_str.rpad(pad_chars_count, pad_char); } else { diff --git a/core/string/ustring.h b/core/string/ustring.h index b8ae3c2392..ed3848fb8a 100644 --- a/core/string/ustring.h +++ b/core/string/ustring.h @@ -28,11 +28,11 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -// Note: _GODOT suffix added to avoid conflict with ICU header with the same guard. - #ifndef USTRING_GODOT_H #define USTRING_GODOT_H +// Note: _GODOT suffix added to header guard to avoid conflict with ICU header. + #include "core/string/char_utils.h" #include "core/templates/cowdata.h" #include "core/templates/vector.h" @@ -156,6 +156,7 @@ public: void operator=(const char *p_cstr); bool operator<(const CharString &p_right) const; + bool operator==(const CharString &p_right) const; CharString &operator+=(char p_char); int length() const { return size() ? size() - 1 : 0; } const char *get_data() const; @@ -345,10 +346,10 @@ public: String get_slice(String p_splitter, int p_slice) const; String get_slicec(char32_t p_splitter, int p_slice) const; - Vector<String> split(const String &p_splitter, bool p_allow_empty = true, int p_maxsplit = 0) const; - Vector<String> rsplit(const String &p_splitter, bool p_allow_empty = true, int p_maxsplit = 0) const; + Vector<String> split(const String &p_splitter = "", bool p_allow_empty = true, int p_maxsplit = 0) const; + Vector<String> rsplit(const String &p_splitter = "", bool p_allow_empty = true, int p_maxsplit = 0) const; Vector<String> split_spaces() const; - Vector<float> split_floats(const String &p_splitter, bool p_allow_empty = true) const; + Vector<double> split_floats(const String &p_splitter, bool p_allow_empty = true) const; Vector<float> split_floats_mk(const Vector<String> &p_splitters, bool p_allow_empty = true) const; Vector<int> split_ints(const String &p_splitter, bool p_allow_empty = true) const; Vector<int> split_ints_mk(const Vector<String> &p_splitters, bool p_allow_empty = true) const; |