[Scummvm-git-logs] scummvm master -> 0d4fc51b6453eab82db1dfa96aee444643f4e8a8

sev- noreply at scummvm.org
Thu Jun 19 22:07:29 UTC 2025


This automated email contains information about 7 new commits which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .

Summary:
966eda9b80 COMMON: Extend interface for getting string characters at indexes
b5a019c4e5 COMMON: Make String::assign public and extend it
b78d519ed3 COMMON: Make String::assignAppend public and extend it
c6ae30e7ea COMMON: Support creating strings from a repeated single character
e0cd20dd55 COMMON: Add more member types to String and Array
d0713ce659 ULTIMA: Reduce differences with common String and Array classes
0d4fc51b64 TEST: Add tests for new assign() and append() methods


Commit: 966eda9b8081507e65bb90676fc83ce2a8782abf
    https://github.com/scummvm/scummvm/commit/966eda9b8081507e65bb90676fc83ce2a8782abf
Author: Cameron Cawley (ccawley2011 at gmail.com)
Date: 2025-06-20T00:07:23+02:00

Commit Message:
COMMON: Extend interface for getting string characters at indexes

Changed paths:
    common/str-base.h
    engines/ultima/shared/std/string.h


diff --git a/common/str-base.h b/common/str-base.h
index 986b6fb404e..10ee9dab007 100644
--- a/common/str-base.h
+++ b/common/str-base.h
@@ -180,13 +180,31 @@ public:
 	value_type firstChar() const    { return (_size > 0) ? _str[0] : 0; }
 	value_type lastChar() const     { return (_size > 0) ? _str[_size - 1] : 0; }
 
-	value_type operator[](int idx) const {
+	/**
+	 * String square brackets allows modifying characters
+	 */
+	value_type &operator[](size_t idx) {
+		assert(_str);
+		assert(idx >= 0);
+		assert(idx < _size);
+		return _str[idx];
+	}
+
+	/**
+	 * Square brackets for const strings simply returns characters
+	 */
+	value_type operator[](size_t idx) const {
 		assert(_str);
 		assert(idx >= 0);
-		assert(idx < (int)_size);
+		assert(idx < _size);
 		return _str[idx];
 	}
 
+	/**
+	 * Get a character at an index
+	 */
+	value_type at(size_t idx) const { return operator[](idx); }
+
 	/**
 	 * Checks if a given string is present in the internal string or not.
 	 */
diff --git a/engines/ultima/shared/std/string.h b/engines/ultima/shared/std/string.h
index 6b1c80d26a1..89834cfc20e 100644
--- a/engines/ultima/shared/std/string.h
+++ b/engines/ultima/shared/std/string.h
@@ -86,26 +86,6 @@ public:
 			*this = string(s.c_str(), count);
 	}
 
-	/**
-	 * String square brackets allows modifying characters
-	 */
-	char &operator[](size_t idx) {
-		assert(idx < _size);
-		return _str[idx];
-	}
-
-	/**
-	 * Square brackets for const strings simply returns characters
-	 */
-	char operator[](size_t idx) const {
-		return Common::String::operator[](idx);
-	}
-
-	/**
-	 * Get a character at an index
-	 */
-	char at(size_t idx) { return operator[](idx); }
-
 	virtual int Compare(const string &s) const {
 		return compareTo(s);
 	}


Commit: b5a019c4e544db79b20e07959fadbee0af24db64
    https://github.com/scummvm/scummvm/commit/b5a019c4e544db79b20e07959fadbee0af24db64
Author: Cameron Cawley (ccawley2011 at gmail.com)
Date: 2025-06-20T00:07:23+02:00

Commit Message:
COMMON: Make String::assign public and extend it

Changed paths:
    common/str-base.cpp
    common/str-base.h
    engines/ultima/shared/std/string.h


diff --git a/common/str-base.cpp b/common/str-base.cpp
index 4268cf3819f..b4d626f38b2 100644
--- a/common/str-base.cpp
+++ b/common/str-base.cpp
@@ -1077,13 +1077,20 @@ TEMPLATE void BASESTRING::assign(const value_type *str) {
 	memmove(_str, str, (len + 1) * sizeof(value_type));
 }
 
+TEMPLATE void BASESTRING::assign(const value_type *str, size_type count) {
+	uint32 len = MIN(count, cStrLen(str));
+	ensureCapacity(len, false);
+	_size = len;
+	memmove(_str, str, (len + 1) * sizeof(value_type));
+}
+
 TEMPLATE uint BASESTRING::getUnsignedValue(uint pos) const {
 	const int shift = (sizeof(uint) - sizeof(value_type)) * 8;
 	return ((uint)_str[pos]) << shift >> shift;
 }
 
-TEMPLATE uint32 BASESTRING::cStrLen(const value_type *str) {
-	uint32 len = 0;
+TEMPLATE size_t BASESTRING::cStrLen(const value_type *str) {
+	size_t len = 0;
 	while (str[len])
 		len++;
 
@@ -1133,8 +1140,8 @@ TEMPLATE uint BASESTRING::hash() const {
 }
 
 template<>
-uint32 BaseString<char>::cStrLen(const value_type *str) {
-	return static_cast<uint32>(strlen(str));
+size_t BaseString<char>::cStrLen(const value_type *str) {
+	return strlen(str);
 }
 
 template<>
diff --git a/common/str-base.h b/common/str-base.h
index 10ee9dab007..f4796dd29b3 100644
--- a/common/str-base.h
+++ b/common/str-base.h
@@ -306,6 +306,15 @@ public:
 	 */
 	void replace(value_type from, value_type to);
 
+	/**
+	 * Assign a new string
+	 */
+	void assign(const BaseString &str);
+	void assign(BaseString &&str);
+	void assign(value_type c);
+	void assign(const value_type *str);
+	void assign(const value_type *str, size_t count);
+
 	/** Appends a string containing the characters between beginP (including) and endP (excluding). */
 	void append(const value_type *begin, const value_type *end);
 
@@ -368,10 +377,6 @@ protected:
 	void assignAppend(const value_type *str);
 	void assignAppend(value_type c);
 	void assignAppend(const BaseString &str);
-	void assign(const BaseString &str);
-	void assign(BaseString &&str);
-	void assign(value_type c);
-	void assign(const value_type *str);
 
 	bool pointerInOwnBuffer(const value_type *str) const;
 
@@ -379,7 +384,7 @@ protected:
 
 	void toCase(int (*caseChangeFunc)(int));
 
-	static uint32 cStrLen(const value_type *str);
+	static size_t cStrLen(const value_type *str);
 	static const value_type *cMemChr(const value_type *ptr, value_type c, size_t count);
 	static       value_type *cMemChr(value_type *ptr,       value_type c, size_t count);
 	static int cMemCmp(const value_type* ptr1, const value_type* ptr2, size_t count);
diff --git a/engines/ultima/shared/std/string.h b/engines/ultima/shared/std/string.h
index 89834cfc20e..c5786d63d03 100644
--- a/engines/ultima/shared/std/string.h
+++ b/engines/ultima/shared/std/string.h
@@ -70,22 +70,6 @@ public:
 
 	size_t length() const { return size(); }
 
-	/**
-	 * Assign a new string
-	 */
-	void assign(const char *s, size_t count = npos) {
-		if (count == npos)
-			*this = s;
-		else
-			*this = string(s, count);
-	}
-	void assign(const string &s, size_t count = npos) {
-		if (count == npos)
-			*this = s;
-		else
-			*this = string(s.c_str(), count);
-	}
-
 	virtual int Compare(const string &s) const {
 		return compareTo(s);
 	}


Commit: b78d519ed38bccec0bb98a4de2a205adb6bbcde2
    https://github.com/scummvm/scummvm/commit/b78d519ed38bccec0bb98a4de2a205adb6bbcde2
Author: Cameron Cawley (ccawley2011 at gmail.com)
Date: 2025-06-20T00:07:23+02:00

Commit Message:
COMMON: Make String::assignAppend public and extend it

Changed paths:
    common/dbcs-str.cpp
    common/str-base.cpp
    common/str-base.h
    common/str.cpp
    common/ustr.cpp
    engines/ultima/nuvie/core/converse_interpret.cpp
    engines/ultima/shared/std/string.h


diff --git a/common/dbcs-str.cpp b/common/dbcs-str.cpp
index 321e411f037..79b737aa3f1 100644
--- a/common/dbcs-str.cpp
+++ b/common/dbcs-str.cpp
@@ -96,12 +96,12 @@ DBCSString &DBCSString::operator=(value_type c) {
 }
 
 DBCSString &DBCSString::operator+=(const DBCSString &str) {
-	assignAppend(str);
+	append(str);
 	return *this;
 }
 
 DBCSString &DBCSString::operator+=(value_type c) {
-	assignAppend(c);
+	push_back(c);
 	return *this;
 }
 
diff --git a/common/str-base.cpp b/common/str-base.cpp
index b4d626f38b2..495ec9a12b7 100644
--- a/common/str-base.cpp
+++ b/common/str-base.cpp
@@ -962,7 +962,7 @@ TEMPLATE void BASESTRING::append(const value_type *beginP, const value_type *end
 
 	// Don't test endP as it must be in the same buffer
 	if (pointerInOwnBuffer(beginP)) {
-		assignAppend(BaseString(beginP, endP));
+		append(BaseString(beginP, endP));
 		return;
 	}
 
@@ -986,7 +986,7 @@ TEMPLATE void BASESTRING::assignInsert(value_type c, uint32 p) {
 	_size++;
 }
 
-TEMPLATE void BASESTRING::assignAppend(value_type c) {
+TEMPLATE void BASESTRING::append(value_type c) {
 	if (c == 0) {
 #ifndef SCUMMVM_UTIL
 		warning("Adding \\0 to String. This is permitted, but can have unwanted consequences. (This warning will be removed later.)");
@@ -1014,9 +1014,9 @@ TEMPLATE void BASESTRING::assignInsert(const BaseString &str, uint32 p) {
 	}
 }
 
-TEMPLATE void BASESTRING::assignAppend(const BaseString &str) {
+TEMPLATE void BASESTRING::append(const BaseString &str) {
 	if (&str == this) {
-		assignAppend(BaseString(str));
+		append(BaseString(str));
 		return;
 	}
 
@@ -1055,9 +1055,9 @@ TEMPLATE void BASESTRING::assignInsert(const value_type *str, uint32 p) {
 	}
 }
 
-TEMPLATE void BASESTRING::assignAppend(const value_type *str) {
+TEMPLATE void BASESTRING::append(const value_type *str) {
 	if (pointerInOwnBuffer(str)) {
-		assignAppend(BaseString(str));
+		append(BaseString(str));
 		return;
 	}
 
diff --git a/common/str-base.h b/common/str-base.h
index f4796dd29b3..33c8d183b05 100644
--- a/common/str-base.h
+++ b/common/str-base.h
@@ -315,9 +315,23 @@ public:
 	void assign(const value_type *str);
 	void assign(const value_type *str, size_t count);
 
+	/**
+	 * Append another string to this one
+	 */
+	void append(const value_type *str);
+	void append(value_type c);
+	void append(const BaseString &str);
+
 	/** Appends a string containing the characters between beginP (including) and endP (excluding). */
 	void append(const value_type *begin, const value_type *end);
 
+	/**
+	 * Append a character to the string
+	 */
+	inline void push_back(value_type c) {
+		append(c);
+	}
+
 	/**
 	 * Wraps the text in the string to the given line maximum. Lines will be
 	 * broken at any whitespace character. New lines are assumed to be
@@ -374,9 +388,6 @@ protected:
 	void assignInsert(const value_type *str, uint32 p);
 	void assignInsert(value_type c, uint32 p);
 	void assignInsert(const BaseString &str, uint32 p);
-	void assignAppend(const value_type *str);
-	void assignAppend(value_type c);
-	void assignAppend(const BaseString &str);
 
 	bool pointerInOwnBuffer(const value_type *str) const;
 
diff --git a/common/str.cpp b/common/str.cpp
index 5f2533af4c1..9104cfe9d57 100644
--- a/common/str.cpp
+++ b/common/str.cpp
@@ -57,17 +57,17 @@ String &String::operator=(char c) {
 }
 
 String &String::operator+=(const char *str) {
-	assignAppend(str);
+	append(str);
 	return *this;
 }
 
 String &String::operator+=(const String &str) {
-	assignAppend(str);
+	append(str);
 	return *this;
 }
 
 String &String::operator+=(char c) {
-	assignAppend(c);
+	push_back(c);
 	return *this;
 }
 
diff --git a/common/ustr.cpp b/common/ustr.cpp
index 779cebfc0ae..77c2e1d715c 100644
--- a/common/ustr.cpp
+++ b/common/ustr.cpp
@@ -81,17 +81,17 @@ U32String &U32String::operator=(value_type c) {
 }
 
 U32String &U32String::operator+=(const value_type *str) {
-	assignAppend(str);
+	append(str);
 	return *this;
 }
 
 U32String &U32String::operator+=(const U32String &str) {
-	assignAppend(str);
+	append(str);
 	return *this;
 }
 
 U32String &U32String::operator+=(value_type c) {
-	assignAppend(c);
+	push_back(c);
 	return *this;
 }
 
diff --git a/engines/ultima/nuvie/core/converse_interpret.cpp b/engines/ultima/nuvie/core/converse_interpret.cpp
index a93e98f1470..3b14f7e0436 100644
--- a/engines/ultima/nuvie/core/converse_interpret.cpp
+++ b/engines/ultima/nuvie/core/converse_interpret.cpp
@@ -151,7 +151,7 @@ void ConverseInterpret::step() {
 void ConverseInterpret::add_text(unsigned char c) {
 	ConvScript *cs = converse->script;
 	do {
-		text.append(1, (unsigned char)cs->read());
+		text.push_back((unsigned char)cs->read());
 	} while (!cs->overflow() && is_print(cs->peek()));
 }
 
@@ -323,7 +323,7 @@ string ConverseInterpret::get_formatted_text(const char *c_str) {
 			        c_str[i + count] >= 'a' && c_str[i + count] <= 'z')
 				// NOTE: the above check might not work for non-english
 				count++;
-			if (show) output.append(count, c_str[i]);
+			if (show) output.append(Std::string(count, c_str[i]));
 			i += count;
 			break;
 		}
@@ -341,7 +341,7 @@ string ConverseInterpret::get_formatted_text(const char *c_str) {
 					i++;
 				}
 			} else {
-				output.append(1, c_str[i]);
+				output.push_back(c_str[i]);
 				i += 1;
 			}
 			break;
diff --git a/engines/ultima/shared/std/string.h b/engines/ultima/shared/std/string.h
index c5786d63d03..cc725d1a481 100644
--- a/engines/ultima/shared/std/string.h
+++ b/engines/ultima/shared/std/string.h
@@ -84,31 +84,6 @@ public:
 			insertChar(c, pos);
 	}
 
-	/**
-	 * Append a given character a number of times to the string
-	 */
-	void append(size_t n, char c) {
-		string str(n, c);
-		*this += str;
-	}
-
-	/**
-	 * Append another string to this one
-	 */
-	void append(const string &str, size_t theSize = npos) {
-		if (theSize == npos)
-			*this += str;
-		else
-			*this += Common::String(str.c_str(), theSize);
-	}
-
-	/**
-	 * Append a character to the string
-	 */
-	void push_back(char c) {
-		*this += c;
-	}
-
 	reverse_iterator rbegin() {
 		return reverse_iterator(this, (int)size() - 1);
 	}


Commit: c6ae30e7ea8c817b647c4ac880e6d70ab2623a76
    https://github.com/scummvm/scummvm/commit/c6ae30e7ea8c817b647c4ac880e6d70ab2623a76
Author: Cameron Cawley (ccawley2011 at gmail.com)
Date: 2025-06-20T00:07:23+02:00

Commit Message:
COMMON: Support creating strings from a repeated single character

Changed paths:
    common/dbcs-str.cpp
    common/dbcs-str.h
    common/str-base.cpp
    common/str-base.h
    common/str.cpp
    common/str.h
    common/ustr.cpp
    common/ustr.h
    engines/ultima/nuvie/core/converse_interpret.cpp
    engines/ultima/shared/std/string.cpp
    engines/ultima/shared/std/string.h


diff --git a/common/dbcs-str.cpp b/common/dbcs-str.cpp
index 79b737aa3f1..06367e3f6a2 100644
--- a/common/dbcs-str.cpp
+++ b/common/dbcs-str.cpp
@@ -91,7 +91,7 @@ DBCSString &DBCSString::operator=(const char *str) {
 }
 
 DBCSString &DBCSString::operator=(value_type c) {
-	assign(c);
+	assign(1, c);
 	return *this;
 }
 
diff --git a/common/dbcs-str.h b/common/dbcs-str.h
index ede861bf194..d43a60346f1 100644
--- a/common/dbcs-str.h
+++ b/common/dbcs-str.h
@@ -75,6 +75,9 @@ public:
 	/** Construct a string consisting of the given character. */
 	explicit constexpr DBCSString(value_type c) : BaseString<uint16>(c) {}
 
+	/** Construct a string consisting of n copies of the given character. */
+	DBCSString(size_t n, value_type c) : BaseString<uint16>(n, c) {}
+
 	/** Assign a given string to this string. */
 	DBCSString &operator=(const DBCSString &str);
 
diff --git a/common/str-base.cpp b/common/str-base.cpp
index 495ec9a12b7..a41377a0c0f 100644
--- a/common/str-base.cpp
+++ b/common/str-base.cpp
@@ -102,6 +102,14 @@ BASESTRING::BaseString(BASESTRING &&str) : _size(str._size) {
 	str._size = 0;
 }
 
+TEMPLATE BASESTRING::BaseString(size_t n, value_type c) : _size(0), _str(_storage) {
+	if (n == 0) {
+		_storage[0] = 0;
+		_size = 0;
+	} else
+		initWithValueTypeChar(n, c);
+}
+
 TEMPLATE BASESTRING::BaseString(const value_type *str) : _size(0), _str(_storage) {
 	if (str == nullptr) {
 		_storage[0] = 0;
@@ -241,6 +249,27 @@ void BASESTRING::decRefCount(int *oldRefCount) {
 	}
 }
 
+TEMPLATE void BASESTRING::initWithValueTypeChar(size_t count, value_type c) {
+	assert(count);
+	assert(c);
+
+	_storage[0] = 0;
+
+	_size = count;
+
+	if (count >= _builtinCapacity) {
+		// Not enough internal storage, so allocate more
+		_extern._capacity = computeCapacity(count + 1);
+		_extern._refCount = nullptr;
+		_str = new value_type[_extern._capacity];
+		assert(_str != nullptr);
+	}
+
+	// Copy the string into the storage area
+	cMemSet(_str, c, count);
+	_str[count] = 0;
+}
+
 TEMPLATE void BASESTRING::initWithValueTypeStr(const value_type *str, uint32 len) {
 	assert(str);
 
@@ -543,14 +572,17 @@ TEMPLATE void BASESTRING::assign(BaseString &&str) {
 	str._str = str._storage;
 }
 
-TEMPLATE void BASESTRING::assign(value_type c) {
-	decRefCount(_extern._refCount);
-	_str = _storage;
-
-	_str[0] = c;
-	_str[1] = 0;
+TEMPLATE void BASESTRING::assign(size_t count, value_type c) {
+	if (c == 0) {
+#ifndef SCUMMVM_UTIL
+		warning("Adding \\0 to String. This is permitted, but can have unwanted consequences. (This warning will be removed later.)");
+#endif
+	}
 
-	_size = (c == 0) ? 0 : 1;
+	ensureCapacity(count, false);
+	cMemSet(_str, c, count);
+	_size = count;
+	_str[_size] = 0;
 }
 
 TEMPLATE void BASESTRING::insertString(const value_type *s, uint32 p) {
@@ -986,15 +1018,16 @@ TEMPLATE void BASESTRING::assignInsert(value_type c, uint32 p) {
 	_size++;
 }
 
-TEMPLATE void BASESTRING::append(value_type c) {
+TEMPLATE void BASESTRING::append(size_t count, value_type c) {
 	if (c == 0) {
 #ifndef SCUMMVM_UTIL
 		warning("Adding \\0 to String. This is permitted, but can have unwanted consequences. (This warning will be removed later.)");
 #endif
 	}
-	ensureCapacity(_size + 1, true);
+	ensureCapacity(_size + count, true);
 
-	_str[_size++] = c;
+	cMemSet(_str + _size, c, count);
+	_size += count;
 	_str[_size] = 0;
 }
 
@@ -1130,6 +1163,12 @@ TEMPLATE int BASESTRING::cMemCmp(const value_type* ptr1, const value_type* ptr2,
 	return 0;
 }
 
+TEMPLATE void BASESTRING::cMemSet(value_type *str, value_type c, size_t count) {
+	for (size_t i = 0; i < count; ++i) {
+		str[i] = c;
+	}
+}
+
 // Hash function for strings, taken from CPython.
 TEMPLATE uint BASESTRING::hash() const {
 	uint hashResult = getUnsignedValue(0) << 7;
@@ -1159,6 +1198,11 @@ int BaseString<char>::cMemCmp(const value_type* ptr1, const value_type* ptr2, si
 	return memcmp(ptr1, ptr2, count);
 }
 
+template<>
+void BaseString<char>::cMemSet(value_type *str, value_type c, size_t count) {
+	memset(str, c, count);
+}
+
 template class BaseString<char>;
 template class BaseString<uint16>;
 template class BaseString<u32char_type_t>;
diff --git a/common/str-base.h b/common/str-base.h
index 33c8d183b05..d1d1a5bf430 100644
--- a/common/str-base.h
+++ b/common/str-base.h
@@ -86,6 +86,9 @@ public:
 	/** Construct a string consisting of the given character. */
 	explicit constexpr BaseString(value_type c) : _size((c == 0) ? 0 : 1), _str(_storage), _storage{c, 0} {}
 
+	/** Construct a string consisting of n copies of the given character. */
+	BaseString(size_t n, value_type c);
+
 	/** Construct a copy of the given string. */
 	BaseString(const BaseString &str);
 
@@ -311,7 +314,7 @@ public:
 	 */
 	void assign(const BaseString &str);
 	void assign(BaseString &&str);
-	void assign(value_type c);
+	void assign(size_t count, value_type c);
 	void assign(const value_type *str);
 	void assign(const value_type *str, size_t count);
 
@@ -319,7 +322,7 @@ public:
 	 * Append another string to this one
 	 */
 	void append(const value_type *str);
-	void append(value_type c);
+	void append(size_t count, value_type c);
 	void append(const BaseString &str);
 
 	/** Appends a string containing the characters between beginP (including) and endP (excluding). */
@@ -329,7 +332,7 @@ public:
 	 * Append a character to the string
 	 */
 	inline void push_back(value_type c) {
-		append(c);
+		append(1, c);
 	}
 
 	/**
@@ -383,6 +386,7 @@ protected:
 	void ensureCapacity(uint32 new_size, bool keep_old);
 	void incRefCount() const;
 	void decRefCount(int *oldRefCount);
+	void initWithValueTypeChar(size_t count, value_type c);
 	void initWithValueTypeStr(const value_type *str, uint32 len);
 
 	void assignInsert(const value_type *str, uint32 p);
@@ -399,6 +403,7 @@ protected:
 	static const value_type *cMemChr(const value_type *ptr, value_type c, size_t count);
 	static       value_type *cMemChr(value_type *ptr,       value_type c, size_t count);
 	static int cMemCmp(const value_type* ptr1, const value_type* ptr2, size_t count);
+	static void cMemSet(value_type *ptr, value_type c, size_t count);
 };
 }
 #endif
diff --git a/common/str.cpp b/common/str.cpp
index 9104cfe9d57..efccba51634 100644
--- a/common/str.cpp
+++ b/common/str.cpp
@@ -52,7 +52,7 @@ String &String::operator=(String &&str) {
 }
 
 String &String::operator=(char c) {
-	assign(c);
+	assign(1, c);
 	return *this;
 }
 
diff --git a/common/str.h b/common/str.h
index ac1f4607c9c..6f435e71d89 100644
--- a/common/str.h
+++ b/common/str.h
@@ -86,6 +86,9 @@ public:
 	/** Construct a string consisting of the given character. */
 	explicit constexpr String(value_type c) : BaseString<char>(c) {}
 
+	/** Construct a string consisting of n copies of the given character. */
+	String(size_t n, value_type c) : BaseString<char>(n, c) {}
+
 	/** Construct a new string from the given u32 string. */
 	String(const U32String &str, CodePage page = kUtf8);
 
diff --git a/common/ustr.cpp b/common/ustr.cpp
index 77c2e1d715c..4c6c0614e22 100644
--- a/common/ustr.cpp
+++ b/common/ustr.cpp
@@ -76,7 +76,7 @@ U32String &U32String::operator=(const char *str) {
 }
 
 U32String &U32String::operator=(value_type c) {
-	assign(c);
+	assign(1, c);
 	return *this;
 }
 
diff --git a/common/ustr.h b/common/ustr.h
index a8ae9e7bc10..a6a3942c6b5 100644
--- a/common/ustr.h
+++ b/common/ustr.h
@@ -98,6 +98,9 @@ public:
 	/** Construct a string consisting of the given character. */
 	explicit constexpr U32String(value_type c) : BaseString<u32char_type_t>(c) {}
 
+	/** Construct a string consisting of n copies of the given character. */
+	U32String(size_t n, value_type c) : BaseString<u32char_type_t>(n, c) {}
+
 	/** Assign a given string to this string. */
 	U32String &operator=(const U32String &str);
 
diff --git a/engines/ultima/nuvie/core/converse_interpret.cpp b/engines/ultima/nuvie/core/converse_interpret.cpp
index 3b14f7e0436..04887ae9f8e 100644
--- a/engines/ultima/nuvie/core/converse_interpret.cpp
+++ b/engines/ultima/nuvie/core/converse_interpret.cpp
@@ -323,7 +323,7 @@ string ConverseInterpret::get_formatted_text(const char *c_str) {
 			        c_str[i + count] >= 'a' && c_str[i + count] <= 'z')
 				// NOTE: the above check might not work for non-english
 				count++;
-			if (show) output.append(Std::string(count, c_str[i]));
+			if (show) output.append(count, c_str[i]);
 			i += count;
 			break;
 		}
diff --git a/engines/ultima/shared/std/string.cpp b/engines/ultima/shared/std/string.cpp
index 661d8e3ccc8..2bf1c66a7b9 100644
--- a/engines/ultima/shared/std/string.cpp
+++ b/engines/ultima/shared/std/string.cpp
@@ -36,12 +36,6 @@ Common::String to_uppercase(const Common::String &s) {
 	return str;
 }
 
-string::string(size_t n, char c) : Common::String() {
-	ensureCapacity(n, false);
-	for (size_t idx = 0; idx < n; ++idx)
-		(*this) += c;
-}
-
 void string::resize(size_t count) {
 	if (count == 0)
 		clear();
diff --git a/engines/ultima/shared/std/string.h b/engines/ultima/shared/std/string.h
index cc725d1a481..7afbf02b168 100644
--- a/engines/ultima/shared/std/string.h
+++ b/engines/ultima/shared/std/string.h
@@ -65,7 +65,7 @@ public:
 	string(const char *beginP, const char *endP) : Common::String(beginP, endP) {}
 	string(const String &str) : Common::String(str) {}
 	string(char c) : Common::String(c) {}
-	string(size_t n, char c);
+	string(size_t n, char c) : Common::String(n, c) {}
 	virtual ~string() {}
 
 	size_t length() const { return size(); }


Commit: e0cd20dd555a657b1630048745e80e2be6288806
    https://github.com/scummvm/scummvm/commit/e0cd20dd555a657b1630048745e80e2be6288806
Author: Cameron Cawley (ccawley2011 at gmail.com)
Date: 2025-06-20T00:07:23+02:00

Commit Message:
COMMON: Add more member types to String and Array

Changed paths:
    common/array.h
    common/str-base.h
    engines/ultima/shared/std/containers.h
    engines/ultima/shared/std/string.h


diff --git a/common/array.h b/common/array.h
index c1458983f24..1a8ef62df31 100644
--- a/common/array.h
+++ b/common/array.h
@@ -56,6 +56,9 @@ public:
 
 	typedef T value_type; /*!< Value type of the array. */
 
+	typedef value_type &reference;
+	typedef const value_type &const_reference;
+
 	typedef uint size_type; /*!< Size type of the array. */
 
 protected:
diff --git a/common/str-base.h b/common/str-base.h
index d1d1a5bf430..35efa52c8e9 100644
--- a/common/str-base.h
+++ b/common/str-base.h
@@ -37,6 +37,7 @@ public:
 	typedef T          value_type;
 	typedef T *        iterator;
 	typedef const T *  const_iterator;
+	typedef size_t     size_type;
 
 protected:
 	/**
diff --git a/engines/ultima/shared/std/containers.h b/engines/ultima/shared/std/containers.h
index 07a7073d7a4..6d15eea923f 100644
--- a/engines/ultima/shared/std/containers.h
+++ b/engines/ultima/shared/std/containers.h
@@ -87,9 +87,6 @@ public:
 		}
 	};
 public:
-	typedef T reference;
-	typedef const T const_reference;
-
 	vector() : Common::Array<T>() {}
 	vector(size_t newSize) : Common::Array<T>(newSize) {}
 	vector(size_t newSize, const T elem) : Common::Array<T>(newSize, elem) {}
diff --git a/engines/ultima/shared/std/string.h b/engines/ultima/shared/std/string.h
index 7afbf02b168..9575ad7e5e5 100644
--- a/engines/ultima/shared/std/string.h
+++ b/engines/ultima/shared/std/string.h
@@ -29,8 +29,6 @@ namespace Std {
 
 class string : public Common::String {
 public:
-	typedef size_t size_type;
-
 	struct reverse_iterator {
 	private:
 		string *_owner;


Commit: d0713ce6594636397537f1e33ace21da88dd7f76
    https://github.com/scummvm/scummvm/commit/d0713ce6594636397537f1e33ace21da88dd7f76
Author: Cameron Cawley (ccawley2011 at gmail.com)
Date: 2025-06-20T00:07:23+02:00

Commit Message:
ULTIMA: Reduce differences with common String and Array classes

Changed paths:
  R engines/ultima/shared/std/string.cpp
    engines/ultima/module.mk
    engines/ultima/nuvie/actors/actor_manager.cpp
    engines/ultima/nuvie/core/converse_interpret.cpp
    engines/ultima/nuvie/core/converse_interpret.h
    engines/ultima/nuvie/core/events.cpp
    engines/ultima/nuvie/files/tmx_map.cpp
    engines/ultima/nuvie/gui/gui_console.cpp
    engines/ultima/nuvie/gui/widgets/converse_gump.cpp
    engines/ultima/nuvie/gui/widgets/msg_scroll.cpp
    engines/ultima/nuvie/gui/widgets/msg_scroll_new_ui.cpp
    engines/ultima/nuvie/keybinding/keys.cpp
    engines/ultima/nuvie/save/save_game.cpp
    engines/ultima/nuvie/script/script_cutscene.cpp
    engines/ultima/nuvie/usecode/u6_usecode.cpp
    engines/ultima/shared/std/containers.h
    engines/ultima/shared/std/string.h
    engines/ultima/ultima8/games/u8_game.cpp
    engines/ultima/ultima8/gumps/bark_gump.cpp
    engines/ultima/ultima8/gumps/cru_inventory_gump.cpp
    engines/ultima/ultima8/gumps/widgets/edit_widget.cpp
    engines/ultima/ultima8/world/actors/main_actor.cpp


diff --git a/engines/ultima/module.mk b/engines/ultima/module.mk
index 10b90868531..7badeb2e647 100644
--- a/engines/ultima/module.mk
+++ b/engines/ultima/module.mk
@@ -5,8 +5,7 @@ MODULE_OBJS := \
 	shared/conf/xml_node.o \
 	shared/conf/xml_tree.o \
 	shared/engine/data_archive.o \
-	shared/engine/events.o \
-	shared/std/string.o
+	shared/engine/events.o
 
 ifdef ENABLE_ULTIMA1
 MODULE_OBJS += \
diff --git a/engines/ultima/nuvie/actors/actor_manager.cpp b/engines/ultima/nuvie/actors/actor_manager.cpp
index 44333b43fc4..b2e8a95a97b 100644
--- a/engines/ultima/nuvie/actors/actor_manager.cpp
+++ b/engines/ultima/nuvie/actors/actor_manager.cpp
@@ -1109,7 +1109,7 @@ void ActorManager::loadAvatarTiles(const Common::Path &datadir) {
 	Std::set<Std::string> files = getCustomTileFilenames(datadir, "avatar_###_####.bmp");
 
 	for (const Std::string &filename : files) {
-		if (filename.length() != 19) { // avatar_nnn_nnnn.bmp
+		if (filename.size() != 19) { // avatar_nnn_nnnn.bmp
 			continue;
 		}
 		Std::string num_str = filename.substr(7, 3);
@@ -1137,7 +1137,7 @@ void ActorManager::loadNPCTiles(const Common::Path &datadir) {
 	Std::set<Std::string> files = getCustomTileFilenames(datadir, "actor_###_####.bmp");
 
 	for (const Std::string &filename : files) {
-		if (filename.length() != 18) { // actor_nnn_nnnn.bmp
+		if (filename.size() != 18) { // actor_nnn_nnnn.bmp
 			continue;
 		}
 		Std::string num_str = filename.substr(6, 3);
diff --git a/engines/ultima/nuvie/core/converse_interpret.cpp b/engines/ultima/nuvie/core/converse_interpret.cpp
index 04887ae9f8e..ee89594cb4d 100644
--- a/engines/ultima/nuvie/core/converse_interpret.cpp
+++ b/engines/ultima/nuvie/core/converse_interpret.cpp
@@ -1373,7 +1373,7 @@ converse_value ConverseInterpret::find_db_string(uint32 loc, const char *dstring
 				free(item);
 				// match keywords format: clamp item to 4 characters
 				if (item_str.size() > 4)
-					item_str.resize(4);
+					item_str.erase(4);
 				if (check_keywords(item_str, find_str))
 					return i;
 			}
diff --git a/engines/ultima/nuvie/core/converse_interpret.h b/engines/ultima/nuvie/core/converse_interpret.h
index 1c113910ce5..3c753af34e0 100644
--- a/engines/ultima/nuvie/core/converse_interpret.h
+++ b/engines/ultima/nuvie/core/converse_interpret.h
@@ -248,7 +248,7 @@ protected:
 	void flush() {
 		in.resize(0);
 		in_start = 0;
-		text.resize(0);
+		text.clear();
 	}
 
 	/* operating on input */
diff --git a/engines/ultima/nuvie/core/events.cpp b/engines/ultima/nuvie/core/events.cpp
index 8cf2b7d58be..944fb98e26b 100644
--- a/engines/ultima/nuvie/core/events.cpp
+++ b/engines/ultima/nuvie/core/events.cpp
@@ -3143,7 +3143,7 @@ void Events::doAction() {
 			drop_select(input.obj);
 		} else if (!drop_qty) {
 			assert(input.str);
-			if (strncmp(input.str->c_str(), "", input.str->length()) == 0) {
+			if (input.str->empty()) {
 				char buf[6];
 				snprintf(buf, sizeof(buf), "%u", drop_obj->qty);
 				scroll->display_string(buf);
@@ -3163,7 +3163,7 @@ void Events::doAction() {
 			return;
 		}
 		assert(input.str);
-		if (strncmp(input.str->c_str(), "", input.str->length()) == 0) {
+		if (input.str->empty()) {
 			if (rest_time == 0)
 				scroll->display_string("0");
 			rest_input(0);
diff --git a/engines/ultima/nuvie/files/tmx_map.cpp b/engines/ultima/nuvie/files/tmx_map.cpp
index 2519ff3b1c5..eb35019fd14 100644
--- a/engines/ultima/nuvie/files/tmx_map.cpp
+++ b/engines/ultima/nuvie/files/tmx_map.cpp
@@ -80,7 +80,7 @@ void TMXMap::writeLayer(NuvieIOFileWrite *tmx, uint16 sideLength, Std::string la
 	                     + slen + "\">\n";
 	header += "  <data encoding=\"csv\">\n";
 
-	tmx->writeBuf((const unsigned char *)header.c_str(), header.length());
+	tmx->writeBuf((const unsigned char *)header.c_str(), header.size());
 
 	uint16 mx, my;
 	for (my = 0; my < sideLength; my++) {
@@ -104,19 +104,19 @@ void TMXMap::writeLayer(NuvieIOFileWrite *tmx, uint16 sideLength, Std::string la
 	Std::string footer = "  </data>\n";
 	footer += " </layer>\n";
 
-	tmx->writeBuf((const unsigned char *)footer.c_str(), footer.length());
+	tmx->writeBuf((const unsigned char *)footer.c_str(), footer.size());
 }
 
 void TMXMap::writeObjectLayer(NuvieIOFileWrite *tmx, uint8 level) {
 	Std::string xml = "<objectgroup name=\"Object Layer\">\n";
-	tmx->writeBuf((const unsigned char *)xml.c_str(), xml.length());
+	tmx->writeBuf((const unsigned char *)xml.c_str(), xml.size());
 
 	writeObjects(tmx, level, true, false);
 	writeObjects(tmx, level, false, false);
 	writeObjects(tmx, level, false, true);
 
 	xml = "</objectgroup>\n";
-	tmx->writeBuf((const unsigned char *)xml.c_str(), xml.length());
+	tmx->writeBuf((const unsigned char *)xml.c_str(), xml.size());
 }
 
 bool TMXMap::canDrawTile(Tile *t, bool forceLower, bool toptile) {
@@ -178,7 +178,7 @@ void TMXMap::writeObjects(NuvieIOFileWrite *tmx, uint8 level, bool forceLower, b
 					if (t->dbl_width && t->dbl_height) {
 						s += writeObjectTile(obj, " -x,-y", t->tile_num - 3, x - 1, y - 1, forceLower, toptiles);
 					}
-					tmx->writeBuf((const unsigned char *)s.c_str(), s.length());
+					tmx->writeBuf((const unsigned char *)s.c_str(), s.size());
 				}
 			}
 		}
@@ -213,7 +213,7 @@ bool TMXMap::exportMapLevel(uint8 level) {
 		header += " </tileset>\n";
 	}
 
-	tmx.writeBuf((const unsigned char *)header.c_str(), header.length());
+	tmx.writeBuf((const unsigned char *)header.c_str(), header.size());
 
 	writeLayer(&tmx, width, "BaseLayer", 0, 8, mapdata);
 
@@ -226,7 +226,7 @@ bool TMXMap::exportMapLevel(uint8 level) {
 	Std::string footer = "</map>\n";
 
 
-	tmx.writeBuf((const unsigned char *)footer.c_str(), footer.length());
+	tmx.writeBuf((const unsigned char *)footer.c_str(), footer.size());
 
 
 
diff --git a/engines/ultima/nuvie/gui/gui_console.cpp b/engines/ultima/nuvie/gui/gui_console.cpp
index e685f782d91..c59af9ee8c1 100644
--- a/engines/ultima/nuvie/gui/gui_console.cpp
+++ b/engines/ultima/nuvie/gui/gui_console.cpp
@@ -64,7 +64,7 @@ void GUI_Console:: Display(bool full_redraw) {
 }
 
 void GUI_Console::AddLine(const Std::string &line) {
-	uint16 len = line.length();
+	uint16 len = line.size();
 	uint16 i;
 
 	if (len > num_cols) {
diff --git a/engines/ultima/nuvie/gui/widgets/converse_gump.cpp b/engines/ultima/nuvie/gui/widgets/converse_gump.cpp
index 1466f50e9d1..a8a239757ab 100644
--- a/engines/ultima/nuvie/gui/widgets/converse_gump.cpp
+++ b/engines/ultima/nuvie/gui/widgets/converse_gump.cpp
@@ -361,7 +361,7 @@ Std::string ConverseGump::strip_whitespace_after_break(Std::string s) {
 bool ConverseGump::parse_token(MsgText *token) {
 	int at_idx = token->s.findFirstOf('@', 0);
 	int i = 0;
-	int len = (int)token->s.length();
+	int len = (int)token->s.size();
 	while (at_idx != -1 && i < len) {
 		Std::string keyword = "";
 		for (i = at_idx + 1; i < len; i++) {
@@ -390,7 +390,7 @@ bool ConverseGump::parse_token(MsgText *token) {
 void ConverseGump::parse_fm_towns_token(MsgText *token) {
 	int at_idx = token->s.findFirstOf('+', 0);
 	int i = 0;
-	int len = (int)token->s.length();
+	int len = (int)token->s.size();
 	bool has_met = false;
 	while (at_idx != -1 && i < len) {
 		i = at_idx + 1;
@@ -483,7 +483,7 @@ Std::string ConverseGump::get_token_at_cursor() {
 			if (!is_permanent_keyword(keyword)) {
 				keyword_list->erase(iter);
 				if (permit_input)
-					keyword = keyword.at(2); // only return first char after " *"
+					keyword = Std::string(keyword.at(2)); // only return first char after " *"
 			}
 			return keyword;
 		}
@@ -501,8 +501,8 @@ bool ConverseGump::input_buf_add_char(char c) {
 }
 
 bool ConverseGump::input_buf_remove_char() {
-	if (input_buf.length()) {
-		input_buf.erase(input_buf.length() - 1, 1);
+	if (!input_buf.empty()) {
+		input_buf.deleteLastChar();
 		return true;
 	}
 
@@ -541,7 +541,7 @@ void ConverseGump::Display(bool full_redraw) {
 				screen->fill(CURSOR_COLOR, area.left + portrait_width / 2 + portrait_width + 16 + total_length, y + 4 + 8, token_len - 8, 1);
 			}
 			total_length += token_len;
-			//total_length += t.s.length();
+			//total_length += t.s.size();
 		}
 		y += 16;
 		font->drawString(screen, " *", area.left + portrait_width / 2 + portrait_width + 8, y, 0, 0);
@@ -558,7 +558,7 @@ void ConverseGump::Display(bool full_redraw) {
 		for (const MsgText *token : line->text) {
 			total_length += token->font->drawString(screen, token->s.c_str(), area.left + 4 + frame_w + 4 + total_length, y + 4, 0, 0); //FIX for hardcoded font height
 
-			//token->s.length();
+			//token->s.size();
 			//token->font->drawChar(screen, ' ', area.left + portrait_width + 8 + total_length * 8, y, 0);
 			//total_length += 1;
 
@@ -711,7 +711,7 @@ GUI_status ConverseGump::MouseUp(int x, int y, Shared::MouseButton button) {
 	} else if (button == 1) { // left click == select word
 		if (input_mode) {
 			token_str = get_token_string_at_pos(x, y);
-			if (token_str.length() > 0) {
+			if (!token_str.empty()) {
 				input_add_string(token_str);
 				set_input_mode(false);
 				clear_scroll();
@@ -732,7 +732,7 @@ GUI_status ConverseGump::MouseUp(int x, int y, Shared::MouseButton button) {
 
 void ConverseGump::input_add_string(Std::string token_str) {
 	input_buf.clear();
-	for (uint16 i = 0; i < token_str.length(); i++) {
+	for (uint16 i = 0; i < token_str.size(); i++) {
 		if (Common::isAlnum(token_str[i]) && (!permit_input || strchr(permit_input, token_str[i])
 		                              || strchr(permit_input, tolower(token_str[i]))))
 			input_buf_add_char(token_str[i]);
diff --git a/engines/ultima/nuvie/gui/widgets/msg_scroll.cpp b/engines/ultima/nuvie/gui/widgets/msg_scroll.cpp
index 172e843be05..7c214a28689 100644
--- a/engines/ultima/nuvie/gui/widgets/msg_scroll.cpp
+++ b/engines/ultima/nuvie/gui/widgets/msg_scroll.cpp
@@ -61,7 +61,7 @@ void MsgText::copy(MsgText *msg_text) {
 }
 
 uint32 MsgText::length() {
-	return (uint32)s.length();
+	return (uint32)s.size();
 }
 
 uint16 MsgText::getDisplayWidth() {
@@ -80,7 +80,7 @@ void MsgLine::append(MsgText *new_text) {
 	if (text.size() > 0)
 		msg_text = text.back();
 
-	if (msg_text && msg_text->font == new_text->font && msg_text->color == new_text->color && new_text->s.length() == 1 && new_text->s[0] != ' ')
+	if (msg_text && msg_text->font == new_text->font && msg_text->color == new_text->color && new_text->s.size() == 1 && new_text->s[0] != ' ')
 		msg_text->s.append(new_text->s);
 	else {
 		msg_text = new MsgText();
@@ -88,7 +88,7 @@ void MsgLine::append(MsgText *new_text) {
 		text.push_back(msg_text);
 	}
 
-	total_length += new_text->s.length();
+	total_length += new_text->s.size();
 
 	return;
 }
@@ -101,9 +101,9 @@ void MsgLine::remove_char() {
 		return;
 
 	msg_text = text.back();
-	msg_text->s.erase(msg_text->s.length() - 1, 1);
+	msg_text->s.deleteLastChar();
 
-	if (msg_text->s.length() == 0) {
+	if (msg_text->s.empty()) {
 		text.pop_back();
 		delete msg_text;
 	}
@@ -125,10 +125,10 @@ MsgText *MsgLine::get_text_at_pos(uint16 pos) {
 		return nullptr;
 
 	for (auto *t : text) {
-		if (i + t->s.length() >= pos)
+		if (i + t->s.size() >= pos)
 			return t;
 
-		i += t->s.length();
+		i += t->s.size();
 	}
 
 	return nullptr;
@@ -411,13 +411,13 @@ MsgText *MsgScroll::holding_buffer_get_token() {
 	if (i == 0) i++;
 
 	if (i == -1)
-		i = input->s.length();
+		i = input->s.size();
 
 	if (i > 0) {
 		token = new MsgText(input->s.substr(0, i), font); // FIX maybe a format flag. // input->font);
 		token->color = input->color;
 		input->s.erase(0, i);
-		if (input->s.length() == 0) {
+		if (input->s.empty()) {
 			holding_buffer.pop_front();
 			delete input;
 		}
@@ -438,7 +438,7 @@ bool MsgScroll::can_fit_token_on_msgline(MsgLine *msg_line, MsgText *token) {
 bool MsgScroll::parse_token(MsgText *token) {
 	MsgLine *msg_line = nullptr;
 
-	if (!(token && token->s.length()))
+	if (!(token && token->s.size()))
 		return true;
 
 	if (!msg_buf.empty())
@@ -475,7 +475,7 @@ bool MsgScroll::parse_token(MsgText *token) {
 				msg_line = add_new_line();
 			}
 			// This adds extra newlines. (SB-X)
-			//                 if(msg_line->total_length + token->length() == scroll_width) //we add a new line but write to the old line.
+			//                 if(msg_line->total_length + token->size() == scroll_width) //we add a new line but write to the old line.
 			//                    add_new_line();
 
 			if (msg_line->total_length == 0 && token->s[0] == ' ' && discard_whitespace) // discard whitespace at the start of a line.
@@ -634,7 +634,7 @@ void MsgScroll::set_input_mode(bool state, const char *allowed, bool can_escape,
 		if (allowed && strlen(allowed))
 			set_permitted_input(allowed);
 		//FIXME SDL2 SDL_EnableUNICODE(1); // allow character translation
-		input_buf.erase(0, input_buf.length());
+		input_buf.erase(0, input_buf.size());
 	} else {
 		//FIXME SDL2 SDL_EnableUNICODE(0); // reduce translation overhead when not needed
 		if (callback_target)
@@ -869,7 +869,7 @@ GUI_status MsgScroll::MouseUp(int x, int y, Shared::MouseButton button) {
 	if (button == 1) { // left click == select word
 		if (input_mode) {
 			token_str = get_token_string_at_pos(x, y);
-			if (permit_input != nullptr && token_str.length()) {
+			if (permit_input != nullptr && !token_str.empty()) {
 				if (strchr(permit_input, token_str[0])
 				        || strchr(permit_input, tolower(token_str[0]))) {
 					input_buf_add_char(token_str[0]);
@@ -878,7 +878,7 @@ GUI_status MsgScroll::MouseUp(int x, int y, Shared::MouseButton button) {
 				return GUI_YUM;
 			}
 
-			for (i = 0; i < token_str.length(); i++) {
+			for (i = 0; i < token_str.size(); i++) {
 				if (Common::isAlnum(token_str[i]))
 					input_buf_add_char(token_str[i]);
 			}
@@ -980,7 +980,7 @@ inline void MsgScroll::drawLine(Screen *theScreen, MsgLine *msg_line, uint16 lin
 
 	for (MsgText *token : msg_line->text) {
 		token->font->drawString(theScreen, token->s.c_str(), area.left + left_margin + total_length * 8, area.top + line_y * 8, token->color, font_highlight_color); //FIX for hardcoded font height
-		total_length += token->s.length();
+		total_length += token->s.size();
 	}
 }
 
@@ -1042,8 +1042,8 @@ bool MsgScroll::input_buf_add_char(char c) {
 }
 
 bool MsgScroll::input_buf_remove_char() {
-	if (input_buf.length()) {
-		input_buf.erase(input_buf.length() - 1, 1);
+	if (!input_buf.empty()) {
+		input_buf.deleteLastChar();
 		scroll_updated = true;
 		remove_char();
 
diff --git a/engines/ultima/nuvie/gui/widgets/msg_scroll_new_ui.cpp b/engines/ultima/nuvie/gui/widgets/msg_scroll_new_ui.cpp
index d0a1391fb51..14807199267 100644
--- a/engines/ultima/nuvie/gui/widgets/msg_scroll_new_ui.cpp
+++ b/engines/ultima/nuvie/gui/widgets/msg_scroll_new_ui.cpp
@@ -115,8 +115,8 @@ void MsgScrollNewUI::display_string(const Std::string &str, Font *f, bool includ
 	}
 
 	if (i > 0) {
-		trailing_whitespace = s.substr(s.length() - i, i);
-		s = s.substr(0, s.length() - i);
+		trailing_whitespace = s.substr(s.size() - i, i);
+		s = s.substr(0, s.size() - i);
 	}
 
 	if (!s.empty()) {
diff --git a/engines/ultima/nuvie/keybinding/keys.cpp b/engines/ultima/nuvie/keybinding/keys.cpp
index 18503a680ec..97c2b2ceda9 100644
--- a/engines/ultima/nuvie/keybinding/keys.cpp
+++ b/engines/ultima/nuvie/keybinding/keys.cpp
@@ -576,11 +576,12 @@ void KeyBinder::ParseLine(const char *line) {
 			i = s.findFirstOf(whitespace);
 			keycode = s.substr(0, i);
 			s.erase(0, i);
-			string t = Std::to_uppercase(keycode);
+			string t = keycode;
+			t.toUppercase();
 
 			if (t.empty()) {
 				::error("Keybinder: parse error in line: %s", s.c_str());
-			} else if (t.length() == 1) {
+			} else if (t.size() == 1) {
 				// translate 1-letter keys straight to Common::KeyCode
 				char c = t[0];
 				if (c >= 33 && c <= 122 && c != 37) {
@@ -613,7 +614,7 @@ void KeyBinder::ParseLine(const char *line) {
 	i = s.findFirstOf(whitespace);
 	string t = s.substr(0, i);
 	s.erase(0, i);
-	t = Std::to_uppercase(t);
+	t.toUppercase();
 
 	ParseActionMap::const_iterator action_index = _actions.find(t);
 	ActionType a;
diff --git a/engines/ultima/nuvie/save/save_game.cpp b/engines/ultima/nuvie/save/save_game.cpp
index a4fc57647a7..c463f6a1304 100644
--- a/engines/ultima/nuvie/save/save_game.cpp
+++ b/engines/ultima/nuvie/save/save_game.cpp
@@ -147,7 +147,7 @@ bool SaveGame::load_original() {
 	init(obj_manager);
 
 	objblk_filename = OBJBLK_FILENAME;
-	len = objblk_filename.length();
+	len = objblk_filename.size();
 
 	uint8 i = 0;
 
@@ -440,7 +440,7 @@ void SaveGame::update_objlist_for_new_game_u6() {
 
 	config->value("config/newgamedata/name", name, "Avatar");
 	objlist.seek(0xf00);
-	int len = name.length();
+	int len = name.size();
 	if (len > 13)
 		len = 13;
 
@@ -490,7 +490,7 @@ void SaveGame::update_objlist_for_new_game_se() {
 
 	config->value("config/newgamedata/name", name, "Avatar");
 	objlist.seek(0xf00);
-	int len = name.length();
+	int len = name.size();
 	if (len > 13)
 		len = 13;
 
@@ -524,7 +524,7 @@ void SaveGame::update_objlist_for_new_game_md() {
 
 	config->value("config/newgamedata/name", name, "Avatar");
 	objlist.seek(0xf00);
-	int len = name.length();
+	int len = name.size();
 	if (len > 13)
 		len = 13;
 
diff --git a/engines/ultima/nuvie/script/script_cutscene.cpp b/engines/ultima/nuvie/script/script_cutscene.cpp
index 79cf237c875..1f4962833da 100644
--- a/engines/ultima/nuvie/script/script_cutscene.cpp
+++ b/engines/ultima/nuvie/script/script_cutscene.cpp
@@ -1525,8 +1525,8 @@ void ScriptCutscene::print_text(CSImage *image, const char *s, uint16 *x, uint16
 		*x += space_width;
 	}
 
-	if (start < str.length()) {
-		Std::string token = str.substr(start, str.length() - start);
+	if (start < str.size()) {
+		Std::string token = str.substr(start, str.size() - start);
 		if (len + font->getStringWidth(token.c_str()) > width) {
 			*y += 8;
 			*x = startx;
@@ -1675,7 +1675,7 @@ void ScriptCutscene::Display(bool full_redraw) {
 					screen->blit(x_off + s->x - x, y_off + s->y - y, s->image->shp->get_data(), 8, w, h, w, true, s->clip_rect.width() != 0 ? &s->clip_rect : &clip_rect, s->opacity);
 				}
 
-				if (s->text.length() > 0) {
+				if (!s->text.empty()) {
 					if (s->text_align != 0) {
 						display_wrapped_text(s);
 					} else {
diff --git a/engines/ultima/nuvie/usecode/u6_usecode.cpp b/engines/ultima/nuvie/usecode/u6_usecode.cpp
index 6e87b701fd9..d0c6f231624 100644
--- a/engines/ultima/nuvie/usecode/u6_usecode.cpp
+++ b/engines/ultima/nuvie/usecode/u6_usecode.cpp
@@ -1428,7 +1428,7 @@ bool U6UseCode::use_rubber_ducky(Obj *obj, UseCodeEvent ev) {
 }
 
 sint16 U6UseCode::parseLatLongString(U6UseCodeLatLonEnum mode, Std::string *input) {
-	uint16 len = input->length();
+	uint16 len = input->size();
 	sint16 val = 0;
 	for (uint16 i = 0; i < len; i++) {
 		char c = (*input)[i];
diff --git a/engines/ultima/shared/std/containers.h b/engines/ultima/shared/std/containers.h
index 6d15eea923f..b21fb2b5095 100644
--- a/engines/ultima/shared/std/containers.h
+++ b/engines/ultima/shared/std/containers.h
@@ -87,7 +87,7 @@ public:
 		}
 	};
 public:
-	vector() : Common::Array<T>() {}
+	constexpr vector() : Common::Array<T>() {}
 	vector(size_t newSize) : Common::Array<T>(newSize) {}
 	vector(size_t newSize, const T elem) : Common::Array<T>(newSize, elem) {}
 
@@ -103,14 +103,6 @@ public:
 	const_reverse_iterator rend() const {
 		return const_reverse_iterator(this, -1);
 	}
-
-	void pop_front() {
-		Common::Array<T>::remove_at(0);
-	}
-
-	T at(size_t index) const {
-		return (*this)[index];
-	}
 };
 
 template<class T>
diff --git a/engines/ultima/shared/std/string.cpp b/engines/ultima/shared/std/string.cpp
deleted file mode 100644
index 2bf1c66a7b9..00000000000
--- a/engines/ultima/shared/std/string.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include "ultima/shared/std/string.h"
-#include "common/algorithm.h"
-
-namespace Ultima {
-namespace Std {
-
-const char *const endl = "\n";
-
-Common::String to_uppercase(const Common::String &s) {
-	Common::String str = s;
-	Common::String::iterator X;
-	for (X = str.begin(); X != str.end(); ++X)
-		*X = toupper(*X);
-
-	return str;
-}
-
-void string::resize(size_t count) {
-	if (count == 0)
-		clear();
-	else if (count < size())
-		*this = string(_str, _str + count);
-	else {
-		while (size() < count)
-			*this += ' ';
-	}
-}
-
-} // End of namespace Std
-} // End of namespace Ultima
diff --git a/engines/ultima/shared/std/string.h b/engines/ultima/shared/std/string.h
index 9575ad7e5e5..fc16c89b534 100644
--- a/engines/ultima/shared/std/string.h
+++ b/engines/ultima/shared/std/string.h
@@ -27,7 +27,7 @@
 namespace Ultima {
 namespace Std {
 
-class string : public Common::String {
+class string final : public Common::String {
 public:
 	struct reverse_iterator {
 	private:
@@ -57,30 +57,13 @@ public:
 		}
 	};
 public:
-	string() : Common::String() {}
+	constexpr string() : Common::String() {}
 	string(const char *str) : Common::String(str) {}
 	string(const char *str, uint32 len) : Common::String(str, len) {}
 	string(const char *beginP, const char *endP) : Common::String(beginP, endP) {}
 	string(const String &str) : Common::String(str) {}
-	string(char c) : Common::String(c) {}
+	explicit constexpr string(char c) : Common::String(c) {}
 	string(size_t n, char c) : Common::String(n, c) {}
-	virtual ~string() {}
-
-	size_t length() const { return size(); }
-
-	virtual int Compare(const string &s) const {
-		return compareTo(s);
-	}
-
-	/**
-	 * Resizes a string
-	 */
-	void resize(size_t count);
-
-	void insert(size_t pos, size_t n, char c) {
-		for (uint idx = 0; idx < n; ++idx)
-			insertChar(c, pos);
-	}
 
 	reverse_iterator rbegin() {
 		return reverse_iterator(this, (int)size() - 1);
@@ -88,28 +71,8 @@ public:
 	reverse_iterator rend() {
 		return reverse_iterator(this, -1);
 	}
-
-	bool operator==(const string &x) const {
-		return !Compare(x);
-	}
-
-	bool operator==(const char *x) const {
-		return !Compare(x);
-	}
-
-	bool operator!=(const string &x) const {
-		return Compare(x) != 0;
-	}
-
-	bool operator !=(const char *x) const {
-		return Compare(x) != 0;
-	}
 };
 
-extern const char *const endl;
-
-extern Common::String to_uppercase(const Common::String &s);
-
 } // End of namespace Std
 } // End of namespace Ultima
 
diff --git a/engines/ultima/ultima8/games/u8_game.cpp b/engines/ultima/ultima8/games/u8_game.cpp
index f8bc32848f4..fcf43b88a29 100644
--- a/engines/ultima/ultima8/games/u8_game.cpp
+++ b/engines/ultima/ultima8/games/u8_game.cpp
@@ -286,9 +286,8 @@ void U8Game::writeSaveInfo(Common::WriteStream *ws) {
 }
 
 Std::string U8Game::getCreditText(Common::SeekableReadStream *rs) {
-	Std::string text;
 	unsigned int size = rs->size();
-	text.resize(size);
+	Std::string text(size, ' ');
 	for (unsigned int i = 0; i < size; ++i) {
 		uint8 c = rs->readByte();
 		int x;
diff --git a/engines/ultima/ultima8/gumps/bark_gump.cpp b/engines/ultima/ultima8/gumps/bark_gump.cpp
index 90eba7e84bc..7288c4c0fe4 100644
--- a/engines/ultima/ultima8/gumps/bark_gump.cpp
+++ b/engines/ultima/ultima8/gumps/bark_gump.cpp
@@ -148,8 +148,8 @@ int BarkGump::calculateTicks() {
 	uint length = end - start;
 	int ticks = INT_MAX_VALUE;
 	if (length > 0) {
-		if (_speechLength && _barked.length()) {
-			ticks = (length * _speechLength) / (_barked.length() * MILLIS_PER_TICK);
+		if (_speechLength && !_barked.empty()) {
+			ticks = (length * _speechLength) / (_barked.size() * MILLIS_PER_TICK);
 		} else if (_talkSpeed) {
 			ticks = (length * NO_SPEECH_LENGTH) / _talkSpeed;
 		}
diff --git a/engines/ultima/ultima8/gumps/cru_inventory_gump.cpp b/engines/ultima/ultima8/gumps/cru_inventory_gump.cpp
index bb43269604c..01f532e9c4c 100644
--- a/engines/ultima/ultima8/gumps/cru_inventory_gump.cpp
+++ b/engines/ultima/ultima8/gumps/cru_inventory_gump.cpp
@@ -117,7 +117,7 @@ void CruInventoryGump::PaintThis(RenderSurface *surf, int32 lerp_factor, bool sc
 					_inventoryText->InitGump(this, false);
 				}
 			} else {
-				if (_inventoryText->getText().length() > 0) {
+				if (!_inventoryText->getText().empty()) {
 					resetText();
 				}
 			}
diff --git a/engines/ultima/ultima8/gumps/widgets/edit_widget.cpp b/engines/ultima/ultima8/gumps/widgets/edit_widget.cpp
index b802e30dfd7..ee9e78e6f5b 100644
--- a/engines/ultima/ultima8/gumps/widgets/edit_widget.cpp
+++ b/engines/ultima/ultima8/gumps/widgets/edit_widget.cpp
@@ -151,8 +151,8 @@ void EditWidget::renderText() {
 		                               cv ? _cursor : Std::string::npos);
 
 		// Trim text to fit
-		if (remaining < _text.length()) {
-			_text.resize(remaining);
+		if (remaining < _text.size()) {
+			_text.erase(remaining);
 			_cursor = _text.size();
 		}
 	}
@@ -253,7 +253,7 @@ bool EditWidget::OnTextInput(int unicode) {
 	if (!c) return true;
 
 	Std::string newtext = _text;
-	newtext.insert(_cursor, 1, c);
+	newtext.insertChar(c, _cursor);
 
 	if (textFits(newtext)) {
 		_text = newtext;
diff --git a/engines/ultima/ultima8/world/actors/main_actor.cpp b/engines/ultima/ultima8/world/actors/main_actor.cpp
index d4d6cc876c3..5e6720fae48 100644
--- a/engines/ultima/ultima8/world/actors/main_actor.cpp
+++ b/engines/ultima/ultima8/world/actors/main_actor.cpp
@@ -798,7 +798,7 @@ bool MainActor::loadData(Common::ReadStream *rs, uint32 version) {
 	}
 
 	uint8 namelength = rs->readByte();
-	_name.resize(namelength);
+	_name.assign(namelength, ' ');
 	for (unsigned int i = 0; i < namelength; ++i)
 		_name[i] = rs->readByte();
 


Commit: 0d4fc51b6453eab82db1dfa96aee444643f4e8a8
    https://github.com/scummvm/scummvm/commit/0d4fc51b6453eab82db1dfa96aee444643f4e8a8
Author: Cameron Cawley (ccawley2011 at gmail.com)
Date: 2025-06-20T00:07:23+02:00

Commit Message:
TEST: Add tests for new assign() and append() methods

Changed paths:
    test/common/str.h


diff --git a/test/common/str.h b/test/common/str.h
index 3ba30de8032..c36adc3cf01 100644
--- a/test/common/str.h
+++ b/test/common/str.h
@@ -17,6 +17,8 @@ class StringTestSuite : public CxxTest::TestSuite
 		TS_ASSERT_EQUALS(str, "test-string");
 		str = Common::String(str.c_str()+5, str.c_str()+8);
 		TS_ASSERT_EQUALS(str, "str");
+		str = Common::String(5, 'a');
+		TS_ASSERT_EQUALS(str, "aaaaa");
 	}
 
 	void test_trim() {
@@ -76,6 +78,24 @@ class StringTestSuite : public CxxTest::TestSuite
 		TS_ASSERT_EQUALS(str2.firstChar(), 'b');
 	}
 
+	void test_assign1() {
+		Common::String str("foobar");
+		str.assign("bar");
+		TS_ASSERT_EQUALS(str, "bar");
+	}
+
+	void test_assign2() {
+		Common::String str("foobar");
+		str.assign("abcdef", 3);
+		TS_ASSERT_EQUALS(str, "abc");
+	}
+
+	void test_assign3() {
+		Common::String str("foobar");
+		str.assign(3, '0');
+		TS_ASSERT_EQUALS(str, "000");
+	}
+
 	void test_concat1() {
 		Common::String str("foo");
 		Common::String str2("bar");
@@ -96,6 +116,12 @@ class StringTestSuite : public CxxTest::TestSuite
 		TS_ASSERT_EQUALS(str, "fooX");
 	}
 
+	void test_concat4() {
+		Common::String str("foo");
+		str.append(2, '+');
+		TS_ASSERT_EQUALS(str, "foo++");
+	}
+
 	void test_refCount() {
 		// using internal storage
 		Common::String foo1("foo");




More information about the Scummvm-git-logs mailing list