[Scummvm-git-logs] scummvm master -> 29cfa7bb0f2c1c76a9ef285b8e5799a7ec91020b

sev- sev at scummvm.org
Sat Oct 31 13:05:34 UTC 2020


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

Summary:
29cfa7bb0f COMMON: Merge code for str.cpp and ustr.cpp


Commit: 29cfa7bb0f2c1c76a9ef285b8e5799a7ec91020b
    https://github.com/scummvm/scummvm/commit/29cfa7bb0f2c1c76a9ef285b8e5799a7ec91020b
Author: Vladimir Serbinenko (phcoder at google.com)
Date: 2020-10-31T14:05:30+01:00

Commit Message:
COMMON: Merge code for str.cpp and ustr.cpp

Most of the code in str.cpp and ustr.cpp is actually the same. Do some
template magic to merge them.

Changed paths:
  A common/base-str.cpp
  A common/base-str.h
  A devtools/create_titanic/base-str.cpp
  A devtools/create_xeen/base-str.cpp
  R devtools/create_titanic/str.h
    audio/musicplugin.cpp
    common/hash-str.h
    common/module.mk
    common/serializer.h
    common/str.cpp
    common/str.h
    common/ustr.cpp
    common/ustr.h
    common/winexe.h
    devtools/create_titanic/hash-str.h
    devtools/create_titanic/module.mk
    devtools/create_titanic/str.cpp
    devtools/create_titanic/winexe.cpp
    devtools/create_titanic/winexe.h
    devtools/create_titanic/winexe_pe.cpp
    devtools/create_titanic/winexe_pe.h
    devtools/create_xeen/module.mk
    devtools/create_xeen/str.cpp
    engines/glk/adrift/os_glk.cpp
    engines/glk/advsys/glk_interface.cpp
    engines/glk/comprehend/comprehend.cpp
    engines/glk/glk_api.cpp
    engines/glk/scott/scott.cpp
    engines/glk/window_text_buffer.cpp
    engines/glk/zcode/zcode.cpp
    gui/widgets/popup.cpp


diff --git a/audio/musicplugin.cpp b/audio/musicplugin.cpp
index 56f73fd57e..50d6d1af1c 100644
--- a/audio/musicplugin.cpp
+++ b/audio/musicplugin.cpp
@@ -57,5 +57,5 @@ Common::String MusicDevice::getCompleteId() {
 }
 
 MidiDriver::DeviceHandle MusicDevice::getHandle() {
-	return (MidiDriver::DeviceHandle)Common::hashit(getCompleteId());
+	return (MidiDriver::DeviceHandle)getCompleteId().hash();
 }
diff --git a/common/base-str.cpp b/common/base-str.cpp
new file mode 100644
index 0000000000..9a2fb7ffd2
--- /dev/null
+++ b/common/base-str.cpp
@@ -0,0 +1,803 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/base-str.h"
+#include "common/hash-str.h"
+#include "common/list.h"
+#include "common/memorypool.h"
+#include "common/util.h"
+#include "common/mutex.h"
+
+namespace Common {
+
+#define TEMPLATE template<class T>
+#define BASESTRING BaseString<T>
+
+MemoryPool *g_refCountPool = nullptr; // FIXME: This is never freed right now
+#ifndef SCUMMVM_UTIL
+Mutex *g_refCountPoolMutex = nullptr;
+
+void lockMemoryPoolMutex() {
+	// The Mutex class can only be used once g_system is set and initialized,
+	// but we may use the String class earlier than that (it is for example
+	// used in the OSystem_POSIX constructor). However in those early stages
+	// we can hope we don't have multiple threads either.
+	if (!g_system || !g_system->backendInitialized())
+		return;
+	if (!g_refCountPoolMutex)
+		g_refCountPoolMutex = new Mutex();
+	g_refCountPoolMutex->lock();
+}
+
+void unlockMemoryPoolMutex() {
+	if (g_refCountPoolMutex)
+		g_refCountPoolMutex->unlock();
+}
+
+TEMPLATE void BASESTRING::releaseMemoryPoolMutex() {
+	if (g_refCountPoolMutex){
+		delete g_refCountPoolMutex;
+		g_refCountPoolMutex = nullptr;
+	}
+}
+
+#endif
+
+static uint32 computeCapacity(uint32 len) {
+	// By default, for the capacity we use the next multiple of 32
+	return ((len + 32 - 1) & ~0x1F);
+}
+
+TEMPLATE
+BASESTRING::BaseString(const BASESTRING &str)
+    : _size(str._size) {
+	if (str.isStorageIntern()) {
+		// String in internal storage: just copy it
+		memcpy(_storage, str._storage, _builtinCapacity * sizeof(value_type));
+		_str = _storage;
+	} else {
+		// String in external storage: use refcount mechanism
+		str.incRefCount();
+		_extern._refCount = str._extern._refCount;
+		_extern._capacity = str._extern._capacity;
+		_str = str._str;
+	}
+	assert(_str != nullptr);
+}
+
+TEMPLATE BASESTRING::BaseString(const value_type *str) : _size(0), _str(_storage) {
+	if (str == nullptr) {
+		_storage[0] = 0;
+		_size = 0;
+	} else {
+		uint32 len = 0;
+		const value_type *s = str;
+		while (*s++) {
+			++len;
+		}
+		initWithValueTypeStr(str, len);
+	}
+}
+
+TEMPLATE BASESTRING::BaseString(const value_type *str, uint32 len) : _size(0), _str(_storage) {
+	initWithValueTypeStr(str, len);
+}
+
+TEMPLATE BASESTRING::BaseString(const value_type *beginP, const value_type *endP) : _size(0), _str(_storage) {
+	assert(endP >= beginP);
+	initWithValueTypeStr(beginP, endP - beginP);
+}
+
+TEMPLATE void BASESTRING::makeUnique() {
+	ensureCapacity(_size, true);
+}
+
+TEMPLATE void BASESTRING::ensureCapacity(uint32 new_size, bool keep_old) {
+	bool isShared;
+	uint32 curCapacity, newCapacity;
+	value_type *newStorage;
+	int *oldRefCount = _extern._refCount;
+
+	if (isStorageIntern()) {
+		isShared = false;
+		curCapacity = _builtinCapacity;
+	} else {
+		isShared = (oldRefCount && *oldRefCount > 1);
+		curCapacity = _extern._capacity;
+	}
+
+	// Special case: If there is enough space, and we do not share
+	// the storage, then there is nothing to do.
+	if (!isShared && new_size < curCapacity)
+		return;
+
+	if (isShared && new_size < _builtinCapacity) {
+		// We share the storage, but there is enough internal storage: Use that.
+		newStorage = _storage;
+		newCapacity = _builtinCapacity;
+	} else {
+		// We need to allocate storage on the heap!
+
+		// Compute a suitable new capacity limit
+		// If the current capacity is sufficient we use the same capacity
+		if (new_size < curCapacity)
+			newCapacity = curCapacity;
+		else
+			newCapacity = MAX(curCapacity * 2, computeCapacity(new_size + 1));
+
+		// Allocate new storage
+		newStorage = new value_type[newCapacity];
+		assert(newStorage);
+	}
+
+	// Copy old data if needed, elsewise reset the new storage.
+	if (keep_old) {
+		assert(_size < newCapacity);
+		memcpy(newStorage, _str, (_size + 1) * sizeof(value_type));
+	} else {
+		_size = 0;
+		newStorage[0] = 0;
+	}
+
+	// Release hold on the old storage ...
+	decRefCount(oldRefCount);
+
+	// ... in favor of the new storage
+	_str = newStorage;
+
+	if (!isStorageIntern()) {
+		// Set the ref count & capacity if we use an external storage.
+		// It is important to do this *after* copying any old content,
+		// else we would override data that has not yet been copied!
+		_extern._refCount = nullptr;
+		_extern._capacity = newCapacity;
+	}
+}
+
+TEMPLATE
+void BASESTRING::incRefCount() const {
+	assert(!isStorageIntern());
+	if (_extern._refCount == nullptr) {
+		if (g_refCountPool == nullptr) {
+			g_refCountPool = new MemoryPool(sizeof(int));
+			assert(g_refCountPool);
+		}
+
+		_extern._refCount = (int *)g_refCountPool->allocChunk();
+		*_extern._refCount = 2;
+	} else {
+		++(*_extern._refCount);
+	}
+}
+
+TEMPLATE
+void BASESTRING::decRefCount(int *oldRefCount) {
+	if (isStorageIntern())
+		return;
+
+	if (oldRefCount) {
+		--(*oldRefCount);
+	}
+	if (!oldRefCount || *oldRefCount <= 0) {
+		// The ref count reached zero, so we free the string storage
+		// and the ref count storage.
+		if (oldRefCount) {
+			assert(g_refCountPool);
+			g_refCountPool->freeChunk(oldRefCount);
+		}
+		// Coverity thinks that we always free memory, as it assumes
+		// (correctly) that there are cases when oldRefCount == 0
+		// Thus, DO NOT COMPILE, trick it and shut tons of false positives
+#ifndef __COVERITY__
+		delete[] _str;
+#endif
+
+		// Even though _str points to a freed memory block now,
+		// we do not change its value, because any code that calls
+		// decRefCount will have to do this afterwards anyway.
+	}
+}
+
+TEMPLATE void BASESTRING::initWithValueTypeStr(const value_type *str, uint32 len) {
+	assert(str);
+
+	_storage[0] = 0;
+
+	_size = len;
+
+	if (len >= _builtinCapacity) {
+		// Not enough internal storage, so allocate more
+		_extern._capacity = computeCapacity(len + 1);
+		_extern._refCount = nullptr;
+		_str = new value_type[_extern._capacity];
+		assert(_str != nullptr);
+	}
+
+	// Copy the string into the storage area
+	memmove(_str, str, len * sizeof(value_type));
+	_str[len] = 0;
+}
+
+TEMPLATE void BASESTRING::initWithCStr(const char *str, uint32 len) {
+	assert(str);
+
+	// Init _storage member explicitly (ie. without calling its constructor)
+	// for GCC 2.95.x compatibility (see also tracker item #1602879).
+	_storage[0] = 0;
+
+	_size = len;
+
+	if (len >= _builtinCapacity) {
+		// Not enough internal storage, so allocate more
+		_extern._capacity = computeCapacity(len + 1);
+		_extern._refCount = nullptr;
+		_str = new value_type[_extern._capacity];
+		assert(_str != nullptr);
+	}
+
+	// Copy the string into the storage area
+	for (size_t idx = 0; idx < len; ++idx, ++str)
+		_str[idx] = (byte)(*str);
+
+	_str[len] = 0;
+}
+
+TEMPLATE bool BASESTRING::equals(const BaseString &x) const {
+	if (this == &x || _str == x._str) {
+		return true;
+	}
+
+	if (x.size() != _size) {
+		return false;
+	}
+
+	return !memcmp(_str, x._str, _size * sizeof(value_type));
+}
+
+TEMPLATE bool BASESTRING::equals(const value_type *ptr) const {
+	if (_str == ptr) {
+		return true;
+	}
+
+	uint i = 0;
+
+	for (; i < _size && *ptr; i++, ptr++) {
+		if (_str[i] != *ptr)
+			return false;
+	}
+
+	if (i == _size && *ptr == 0) {
+		return true;
+	}
+
+	return false;
+}
+
+TEMPLATE bool BASESTRING::equalsC(const char *ptr) const {
+	uint i = 0;
+
+	for (; i < _size && *ptr; i++, ptr++) {
+		if (_str[i] != *ptr)
+			return false;
+	}
+
+	if (i == _size && *ptr == 0) {
+		return true;
+	}
+
+	return false;
+}
+
+TEMPLATE bool BASESTRING::operator==(const BaseString &x) const {
+	return equals(x);
+}
+
+TEMPLATE bool BASESTRING::operator==(const value_type *x) const {
+	return equals(BaseString(x));
+}
+
+TEMPLATE bool BASESTRING::operator!=(const BaseString &x) const {
+	return !equals(x);
+}
+
+TEMPLATE bool BASESTRING::operator!=(const value_type *x) const {
+	return !equals(BaseString(x));
+}
+
+TEMPLATE int BASESTRING::compareTo(const BaseString &x) const {
+	for (uint32 i = 0, n = x.size(); i < _size && i < n; ++i) {
+		uint32 sc = _str[i];
+		uint32 xc = x[i];
+		if (sc < xc)
+			return -1;
+		else if (sc > xc)
+			return +1;
+	}
+	if (_size < x.size())
+		return -1;
+	if (_size == x.size())
+		return 0;
+	return +1;
+}
+
+TEMPLATE int BASESTRING::compareTo(const value_type *ptr) const {
+	uint32 i = 0;
+	for (; i < _size && *ptr; ++i, ptr++) {
+		uint32 sc = _str[i];
+		uint32 xc = *ptr;
+		if (sc < xc)
+			return -1;
+		else if (sc > xc)
+			return +1;
+	}
+	if (i == _size && *ptr == 0)
+		return 0;
+	if (*ptr == 0)
+		return +1;
+	return -1;
+}
+
+TEMPLATE int BASESTRING::compareToC(const char *ptr) const {
+	uint32 i = 0;
+	for (; i < _size && *ptr; ++i, ptr++) {
+		uint32 sc = _str[i];
+		uint32 xc = *ptr;
+		if (sc < xc)
+			return -1;
+		else if (sc > xc)
+			return +1;
+	}
+	if (i == _size && *ptr == 0)
+		return 0;
+	if (*ptr == 0)
+		return +1;
+	return -1;
+}
+
+TEMPLATE bool BASESTRING::operator<(const BaseString &x) const {
+	return compareTo(x) < 0;
+}
+
+TEMPLATE bool BASESTRING::operator<=(const BaseString &x) const {
+	return compareTo(x) <= 0;
+}
+
+TEMPLATE bool BASESTRING::operator>(const BaseString &x) const {
+	return compareTo(x) > 0;
+}
+
+TEMPLATE bool BASESTRING::operator>=(const BaseString &x) const {
+	return compareTo(x) >= 0;
+}
+
+TEMPLATE bool BASESTRING::operator<(const value_type *x) const {
+	return compareTo(x) < 0;
+}
+
+TEMPLATE bool BASESTRING::operator<=(const value_type *x) const {
+	return compareTo(x) <= 0;
+}
+
+TEMPLATE bool BASESTRING::operator>(const value_type *x) const {
+	return compareTo(x) > 0;
+}
+
+TEMPLATE bool BASESTRING::operator>=(const value_type *x) const {
+	return compareTo(x) >= 0;
+}
+
+TEMPLATE bool BASESTRING::contains(value_type x) const {
+	for (uint32 i = 0; i < _size; ++i) {
+		if (_str[i] == x) {
+			return true;
+		}
+	}
+
+	return false;
+}
+
+TEMPLATE bool BASESTRING::contains(const BaseString &otherString) const {
+	if (empty() || otherString.empty() || _size < otherString.size()) {
+		return false;
+	}
+
+	uint32 size = 0;
+	BaseString::const_iterator itr = otherString.begin();
+
+	for (BaseString::const_iterator itr2 = begin(); itr != otherString.end() && itr2 != end(); itr2++) {
+		if (*itr == *itr2) {
+			itr++;
+			size++;
+			if (size == otherString.size())
+				return true;
+		} else {
+			size = 0;
+			itr = otherString.begin();
+		}
+	}
+
+	return false;
+}
+
+TEMPLATE void BASESTRING::insertChar(value_type c, uint32 p) {
+	assert(p <= _size);
+
+	ensureCapacity(_size + 1, true);
+	_size++;
+	for (uint32 i = _size; i > p; --i)
+		_str[i] = _str[i - 1];
+	_str[p] = c;
+}
+
+TEMPLATE void BASESTRING::deleteChar(uint32 p) {
+	assert(p < _size);
+
+	makeUnique();
+	while (p++ < _size)
+		_str[p - 1] = _str[p];
+	_size--;
+}
+
+TEMPLATE void BASESTRING::deleteLastChar() {
+	if (_size > 0)
+		deleteChar(_size - 1);
+}
+
+TEMPLATE void BASESTRING::erase(uint32 p, uint32 len) {
+	assert(p < _size);
+
+	makeUnique();
+	// If len == npos or p + len is over the end, remove all the way to the end
+	if (len == npos || p + len >= _size) {
+		// Delete char at p as well. So _size = (p - 1) + 1
+		_size = p;
+		// Null terminate
+		_str[_size] = 0;
+		return;
+	}
+
+	for ( ; p + len <= _size; p++) {
+		_str[p] = _str[p + len];
+	}
+	_size -= len;
+}
+
+TEMPLATE typename BASESTRING::iterator BASESTRING::erase(iterator it) {
+	this->deleteChar(it - _str);
+	return it;
+}
+
+TEMPLATE void BASESTRING::clear() {
+	decRefCount(_extern._refCount);
+
+	_size = 0;
+	_str = _storage;
+	_storage[0] = 0;
+}
+
+	
+TEMPLATE void BASESTRING::setChar(value_type c, uint32 p) {
+	assert(p < _size);
+
+	makeUnique();
+	_str[p] = c;
+}
+
+TEMPLATE void BASESTRING::assign(const BaseString &str) {
+	if (&str == this)
+		return;
+
+	if (str.isStorageIntern()) {
+		decRefCount(_extern._refCount);
+		_size = str._size;
+		_str = _storage;
+		memcpy(_str, str._str, (_size + 1) * sizeof(value_type));
+	} else {
+		str.incRefCount();
+		decRefCount(_extern._refCount);
+
+		_extern._refCount = str._extern._refCount;
+		_extern._capacity = str._extern._capacity;
+		_size = str._size;
+		_str = str._str;
+	}
+}
+
+TEMPLATE void BASESTRING::assign(value_type c) {
+	decRefCount(_extern._refCount);
+	_str = _storage;
+
+	_str[0] = c;
+	_str[1] = 0;
+
+	_size = (c == 0) ? 0 : 1;
+}
+
+TEMPLATE void BASESTRING::insertString(const value_type *s, uint32 p) {
+	while (*s != '\0') {
+		BaseString::insertChar(*s++, p++);
+	}
+}
+
+TEMPLATE void BASESTRING::insertString(const BaseString &s, uint32 p) {
+    for (uint i = 0; i < s._size; i++) {
+		BaseString::insertChar(s[i], p+i);
+	}
+}
+
+TEMPLATE uint32 BASESTRING::find(value_type x, uint32 pos) const {
+	for (uint32 i = pos; i < _size; ++i) {
+		if (_str[i] == x) {
+			return i;
+		}
+	}
+
+	return npos;
+}
+
+TEMPLATE uint32 BASESTRING::find(const BaseString &str, uint32 pos) const {
+	if (pos >= _size) {
+		return npos;
+	}
+
+	const value_type *strP = str.c_str();
+
+	for (const_iterator cur = begin() + pos; cur != end(); ++cur) {
+		uint i = 0;
+		while (true) {
+			if (!strP[i]) {
+				return cur - begin();
+			}
+
+			if (cur[i] != strP[i]) {
+				break;
+			}
+
+			++i;
+		}
+	}
+
+	return npos;
+}
+
+TEMPLATE size_t BASESTRING::find(const value_type *strP, uint32 pos) const {
+    	if (pos >= _size) {
+		return npos;
+	}
+
+	for (const_iterator cur = begin() + pos; cur != end(); ++cur) {
+		uint i = 0;
+		while (true) {
+			if (!strP[i]) {
+				return cur - begin();
+			}
+
+			if (cur[i] != strP[i]) {
+				break;
+			}
+
+			++i;
+		}
+	}
+
+	return npos;
+}
+
+TEMPLATE uint64 BASESTRING::asUint64() const {
+	uint64 result = 0;
+	for (uint32 i = 0; i < _size; ++i) {
+		if (_str[i] < '0' || _str[i] > '9') break;
+		result = result * 10L + (_str[i] - '0');
+	}
+	return result;
+}
+
+TEMPLATE uint64 BASESTRING::asUint64Ext() const {
+	uint64 result = 0;
+	uint64 base = 10;
+	uint32 skip = 0;
+	
+	if (_size >= 3 && _str[0] == '0' && _str[1] == 'x') {
+		base = 16;
+		skip = 2;
+	} else if (_size >= 2 && _str[0] == '0') {
+		base = 8;
+		skip = 1;
+	} else {
+		base = 10;
+		skip = 0;
+	}
+	for (uint32 i = skip; i < _size; ++i) {
+		char digit = _str[i];
+		uint64 digitval = 17; // sentinel
+		if (digit >= '0' && digit <= '9')
+			digitval = digit - '0';
+		else if (digit >= 'a' && digit <= 'f')
+			digitval = digit - 'a' + 10;
+		else if (digit >= 'A' && digit <= 'F')
+			digitval = digit - 'A' + 10;
+		if (digitval > base)
+			break;
+		result = result * base + digitval;
+	}
+	return result;
+}
+
+#ifndef SCUMMVM_UTIL
+
+TEMPLATE void BASESTRING::wordWrap(const uint32 maxLength) {
+	if (_size < maxLength) {
+		return;
+	}
+
+	makeUnique();
+
+	const uint32 kNoSpace = 0xFFFFFFFF;
+
+	uint32 i = 0;
+	while (i < _size) {
+		uint32 lastSpace = kNoSpace;
+		uint32 x = 0;
+		while (i < _size && x <= maxLength) {
+			const char c = _str[i];
+			if (c == '\n') {
+				lastSpace = kNoSpace;
+				x = 0;
+			} else {
+				if (Common::isSpace(c)) {
+					lastSpace = i;
+				}
+				++x;
+			}
+			++i;
+		}
+
+		if (x > maxLength) {
+			if (lastSpace == kNoSpace) {
+				insertChar('\n', i - 1);
+			} else {
+				setChar('\n', lastSpace);
+				i = lastSpace + 1;
+			}
+		}
+	}
+}
+
+#endif
+
+TEMPLATE void BASESTRING::toLowercase() {
+	makeUnique();
+	for (uint32 i = 0; i < _size; ++i) {
+		if (_str[i] > 0 && _str[i] < 128) {
+			_str[i] = tolower(_str[i]);
+		}
+	}
+}
+
+TEMPLATE void BASESTRING::toUppercase() {
+	makeUnique();
+	for (uint32 i = 0; i < _size; ++i) {
+	    if (_str[i] > 0 && _str[i] < 128) {
+			_str[i] = toupper(_str[i]);
+		}
+	}
+}
+
+#ifndef SCUMMVM_UTIL
+
+TEMPLATE void BASESTRING::trim() {
+	if (_size == 0)
+		return;
+
+	makeUnique();
+
+	// Trim trailing whitespace
+	while (_size >= 1 && isSpace(_str[_size - 1]))
+		--_size;
+	_str[_size] = 0;
+
+	// Trim leading whitespace
+	value_type *t = _str;
+	while (isSpace(*t))
+		t++;
+
+	if (t != _str) {
+		_size -= t - _str;
+		memmove(_str, t, (_size + 1) * sizeof(_str[0]));
+	}
+}
+#endif
+
+TEMPLATE void BASESTRING::assignAppend(value_type c) {
+	ensureCapacity(_size + 1, true);
+
+	_str[_size++] = c;
+	_str[_size] = 0;
+}
+
+TEMPLATE void BASESTRING::assignAppend(const BaseString &str) {
+    	if (&str == this) {
+		assignAppend(BaseString(str));
+		return;
+	}
+
+	int len = str._size;
+	if (len > 0) {
+		ensureCapacity(_size + len, true);
+
+		memcpy(_str + _size, str._str, (len + 1) * sizeof(value_type));
+		_size += len;
+	}
+}
+
+TEMPLATE bool BASESTRING::pointerInOwnBuffer(const value_type *str) const {
+	//compared pointers must be in the same array or UB
+	//cast to intptr however is IB
+	//which includes comparision of the values
+	uintptr ownBuffStart = (uintptr)_str;
+	uintptr ownBuffEnd = (uintptr)(_str + _size);
+	uintptr candidateAddr = (uintptr)str;
+	return ownBuffStart <= candidateAddr && candidateAddr <= ownBuffEnd;
+}
+
+TEMPLATE void BASESTRING::assignAppend(const value_type *str) {
+	if (pointerInOwnBuffer(str)) {
+		assignAppend(BaseString(str));
+		return;
+	}
+
+	uint32 len;
+	for (len = 0; str[len]; len++);
+	if (len > 0) {
+		ensureCapacity(_size + len, true);
+
+		memcpy(_str + _size, str, (len + 1) * sizeof(value_type));
+		_size += len;
+	}
+}
+
+TEMPLATE void BASESTRING::assign(const value_type *str) {
+	uint32 len;
+	for (len = 0; str[len]; len++);
+	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;
+}
+
+// Hash function for strings, taken from CPython.
+TEMPLATE uint BASESTRING::hash() const {
+	uint hash = getUnsignedValue(0) << 7;
+	for (uint i = 0; i < _size; i++) {
+		hash = (1000003 * hash) ^ getUnsignedValue(i);
+	}
+	return hash ^ _size;
+}
+
+template class BaseString<char>;
+template class BaseString<u32char_type_t>;
+
+}
diff --git a/common/base-str.h b/common/base-str.h
new file mode 100644
index 0000000000..b3119322e2
--- /dev/null
+++ b/common/base-str.h
@@ -0,0 +1,258 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef COMMON_BASE_STRING_H
+#define COMMON_BASE_STRING_H
+
+#include "common/scummsys.h"
+#include "common/str-enc.h"
+
+#include <stdarg.h>
+
+namespace Common {
+template<class T>
+class BaseString {
+public:
+    	static void releaseMemoryPoolMutex();
+
+    	static const uint32 npos = 0xFFFFFFFF;
+	typedef T          value_type;
+	typedef T *        iterator;
+	typedef const T *  const_iterator;
+
+protected:
+	/**
+	 * The size of the internal storage. Increasing this means less heap
+	 * allocations are needed, at the cost of more stack memory usage,
+	 * and of course lots of wasted memory.
+	 */
+	static const uint32 _builtinCapacity = 32 - (sizeof(uint32) + sizeof(char *)) / sizeof(value_type);
+
+	/**
+	 * Length of the string. Stored to avoid having to call strlen
+	 * a lot. Yes, we limit ourselves to strings shorter than 4GB --
+	 * on purpose :-).
+	 */
+	uint32 _size;
+
+	/**
+	 * Pointer to the actual string storage. Either points to _storage,
+	 * or to a block allocated on the heap via malloc.
+	 */
+	value_type  *_str;
+
+
+	union {
+		/**
+		 * Internal string storage.
+		 */
+		value_type _storage[_builtinCapacity];
+		/**
+		 * External string storage data -- the refcounter, and the
+		 * capacity of the string _str points to.
+		 */
+		struct {
+			mutable int *_refCount;
+			uint32       _capacity;
+		} _extern;
+	};
+
+	inline bool isStorageIntern() const {
+		return _str == _storage;
+	}
+
+public:
+	/** Construct a new empty string. */
+	BaseString() : _size(0), _str(_storage) { _storage[0] = 0; }
+
+	/** Construct a copy of the given string. */
+	BaseString(const BaseString &str);
+
+	/** Construct a new string from the given NULL-terminated C string. */
+	explicit BaseString(const value_type *str);
+
+	/** Construct a new string containing exactly len characters read from address str. */
+	BaseString(const value_type *str, uint32 len);
+
+	/** Construct a new string containing the characters between beginP (including) and endP (excluding). */
+	BaseString(const value_type *beginP, const value_type *endP);
+
+	bool operator==(const BaseString &x) const;
+	bool operator==(const value_type *x) const;
+	bool operator!=(const BaseString &x) const;
+	bool operator!=(const value_type *x) const;
+
+	bool operator<(const BaseString &x) const;
+	bool operator<(const value_type *x) const;
+	bool operator<=(const BaseString &x) const;
+	bool operator<=(const value_type *x) const;
+	bool operator>(const BaseString &x) const;
+	bool operator>(const value_type *x) const;
+	bool operator>=(const BaseString &x) const;
+	bool operator>=(const value_type *x) const;
+
+	/**
+	 * Compares whether two BaseString are the same based on memory comparison.
+	 * This does *not* do comparison based on canonical equivalence.
+	 */
+	bool equals(const BaseString &x) const;
+	bool equals(const value_type *x) const;
+	bool equalsC(const char *x) const;
+	int compareTo(const BaseString &x) const;           // strcmp clone
+	int compareTo(const value_type *x) const;             // strcmp clone
+    	int compareToC(const char *x) const;             // strcmp clone
+
+	/** Set character c at position p, replacing the previous character there. */
+	void setChar(value_type c, uint32 p);
+
+	/**
+	 * Removes the value at position p from the string.
+	 * Using this on decomposed characters will not remove the whole
+	 * character!
+	 */
+	void deleteChar(uint32 p);
+
+	/** Remove the last character from the string. */
+	void deleteLastChar();
+
+	/** Remove all characters from position p to the p + len. If len = String::npos, removes all characters to the end */
+	void erase(uint32 p, uint32 len = npos);
+
+	/** Erases the character at the given iterator location */
+	iterator erase(iterator it);
+
+	/** Clears the string, making it empty. */
+	void clear();
+
+	iterator begin() {
+		// Since the user could potentially
+		// change the string via the returned
+		// iterator we have to assure we are
+		// pointing to a unique storage.
+		makeUnique();
+
+		return _str;
+	}
+
+	iterator end() {
+		return begin() + size();
+	}
+
+	const_iterator begin() const {
+		return _str;
+	}
+
+	const_iterator end() const {
+		return begin() + size();
+	}
+
+    	inline const value_type *c_str() const { return _str; }
+	inline uint size() const         { return _size; }
+
+	inline bool empty() const { return (_size == 0); }
+	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 {
+		assert(_str && idx >= 0 && idx < (int)_size);
+		return _str[idx];
+	}
+
+	/**
+	 * Checks if a given string is present in the internal string or not.
+	 */
+	bool contains(const BaseString &otherString) const;
+	bool contains(value_type x) const;
+
+	/** Insert character c before position p. */
+	void insertChar(value_type c, uint32 p);
+    	void insertString(const value_type *s, uint32 p);
+	void insertString(const BaseString &s, uint32 p);
+
+    	/** Finds the index of a character in the string */
+    	uint32 find(value_type x, uint32 pos = 0) const;
+    	/** Does a find for the passed string */
+	size_t find(const value_type *s, uint32 pos = 0) const;
+	uint32 find(const BaseString &str, uint32 pos = 0) const;
+
+    	/**
+	 * 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
+	 * represented using '\n'.
+	 *
+	 * This is a very basic line wrap which does not perform tab stop
+	 * calculation, consecutive whitespace collapsing, auto-hyphenation, or line
+	 * balancing.
+	 */
+	void wordWrap(const uint32 maxLength);
+
+    	/** Return uint64 corrensponding to String's contents. */
+	uint64 asUint64() const;
+
+      	/** Return uint64 corrensponding to String's contents. This variant recognizes 0 (oct) and 0x (hex) prefixes. */
+	uint64 asUint64Ext() const;
+    
+	/**
+	 * Convert all characters in the string to lowercase.
+	 *
+	 * Be aware that this only affects the case of ASCII characters. All
+	 * other characters will not be touched at all.
+	 */
+	void toLowercase();
+
+	/**
+	 * Convert all characters in the string to uppercase.
+	 *
+	 * Be aware that this only affects the case of ASCII characters. All
+	 * other characters will not be touched at all.
+	 */
+	void toUppercase();
+
+	/**
+	 * Removes trailing and leading whitespaces. Uses isspace() to decide
+	 * what is whitespace and what not.
+	 */
+	void trim();
+
+	uint hash() const;
+
+protected:
+	void makeUnique();
+	void ensureCapacity(uint32 new_size, bool keep_old);
+	void incRefCount() const;
+	void decRefCount(int *oldRefCount);
+	void initWithValueTypeStr(const value_type *str, uint32 len);
+	void initWithCStr(const char *str, uint32 len);
+
+	void assignAppend(const value_type *str);
+	void assignAppend(value_type c);
+	void assignAppend(const BaseString &str);
+	void assign(const BaseString &str);
+	void assign(value_type c);
+	void assign(const value_type *str);
+
+	bool pointerInOwnBuffer(const value_type *str) const;
+
+	uint getUnsignedValue(uint pos) const;
+};
+}
+#endif
diff --git a/common/hash-str.h b/common/hash-str.h
index fcd41ab6a7..f8a75f632c 100644
--- a/common/hash-str.h
+++ b/common/hash-str.h
@@ -30,7 +30,6 @@ namespace Common {
 
 uint hashit(const char *str);
 uint hashit_lower(const char *str); // Generate a hash based on the lowercase version of the string
-inline uint hashit(const String &str) { return hashit(str.c_str()); }
 inline uint hashit_lower(const String &str) { return hashit_lower(str.c_str()); }
 
 // FIXME: The following functors obviously are not consistently named
@@ -40,7 +39,7 @@ struct CaseSensitiveString_EqualTo {
 };
 
 struct CaseSensitiveString_Hash {
-	uint operator()(const String& x) const { return hashit(x.c_str()); }
+	uint operator()(const String& x) const { return x.hash(); }
 };
 
 
@@ -61,7 +60,14 @@ struct IgnoreCase_Hash {
 template<>
 struct Hash<String> {
 	uint operator()(const String& s) const {
-		return hashit(s.c_str());
+		return s.hash();
+	}
+};
+
+template<>
+struct Hash<U32String> {
+	uint operator()(const U32String& s) const {
+		return s.hash();
 	}
 };
 
diff --git a/common/module.mk b/common/module.mk
index a258dd480a..a897e85a83 100644
--- a/common/module.mk
+++ b/common/module.mk
@@ -3,6 +3,7 @@ MODULE := common
 MODULE_OBJS := \
 	achievements.o \
 	archive.o \
+	base-str.o \
 	config-manager.o \
 	coroutines.o \
 	dcl.o \
diff --git a/common/serializer.h b/common/serializer.h
index 8478030759..ea2cda91d0 100644
--- a/common/serializer.h
+++ b/common/serializer.h
@@ -264,6 +264,29 @@ public:
 		}
 	}
 
+	/**
+	 * Sync a U32-string
+	 */
+	void syncString32(U32String &str, Version minVersion = 0, Version maxVersion = kLastVersion) {
+		if (_version < minVersion || _version > maxVersion)
+			return; // Ignore anything which is not supposed to be present in this save game version
+
+		uint32 len = str.size();
+
+		syncAsUint32LE(len);
+
+		if (isLoading()) {
+			U32String::value_type *sl = new U32String::value_type[len];
+			for (uint i = 0; i < len; i++)
+				syncAsUint32LE(sl[i]);
+			str = U32String(sl, len);
+		} else {
+			for (uint i = 0; i < len; i++)
+				_saveStream->writeUint32LE(str[i]);
+			_bytesSynced += 4 * len;
+		}
+	}
+
 	template <typename T>
 	void syncArray(T *arr, size_t entries, void (*serializer)(Serializer &, T &), Version minVersion = 0, Version maxVersion = kLastVersion) {
 		if (_version < minVersion || _version > maxVersion)
diff --git a/common/str.cpp b/common/str.cpp
index 4a15ccd957..fb1dba44e6 100644
--- a/common/str.cpp
+++ b/common/str.cpp
@@ -29,95 +29,8 @@
 
 namespace Common {
 
-MemoryPool *g_refCountPool = nullptr; // FIXME: This is never freed right now
-Mutex *g_refCountPoolMutex = nullptr;
-
-void lockMemoryPoolMutex() {
-	// The Mutex class can only be used once g_system is set and initialized,
-	// but we may use the String class earlier than that (it is for example
-	// used in the OSystem_POSIX constructor). However in those early stages
-	// we can hope we don't have multiple threads either.
-	if (!g_system || !g_system->backendInitialized())
-		return;
-	if (!g_refCountPoolMutex)
-		g_refCountPoolMutex = new Mutex();
-	g_refCountPoolMutex->lock();
-}
-
-void unlockMemoryPoolMutex() {
-	if (g_refCountPoolMutex)
-		g_refCountPoolMutex->unlock();
-}
-
-void String::releaseMemoryPoolMutex() {
-	if (g_refCountPoolMutex){
-		delete g_refCountPoolMutex;
-		g_refCountPoolMutex = nullptr;
-	}
-}
-
-static uint32 computeCapacity(uint32 len) {
-	// By default, for the capacity we use the next multiple of 32
-	return ((len + 32 - 1) & ~0x1F);
-}
-
-String::String(const char *str) : _size(0), _str(_storage) {
-	if (str == nullptr) {
-		_storage[0] = 0;
-		_size = 0;
-	} else
-		initWithCStr(str, strlen(str));
-}
-
-String::String(const char *str, uint32 len) : _size(0), _str(_storage) {
-	initWithCStr(str, len);
-}
-
-String::String(const char *beginP, const char *endP) : _size(0), _str(_storage) {
-	assert(endP >= beginP);
-	initWithCStr(beginP, endP - beginP);
-}
-
-void String::initWithCStr(const char *str, uint32 len) {
-	assert(str);
-
-	// Init _storage member explicitly (ie. without calling its constructor)
-	// for GCC 2.95.x compatibility (see also tracker item #1602879).
-	_storage[0] = 0;
-
-	_size = len;
-
-	if (len >= _builtinCapacity) {
-		// Not enough internal storage, so allocate more
-		_extern._capacity = computeCapacity(len + 1);
-		_extern._refCount = nullptr;
-		_str = new char[_extern._capacity];
-		assert(_str != nullptr);
-	}
-
-	// Copy the string into the storage area
-	memmove(_str, str, len);
-	_str[len] = 0;
-}
-
-String::String(const String &str)
-	: _size(str._size) {
-	if (str.isStorageIntern()) {
-		// String in internal storage: just copy it
-		memcpy(_storage, str._storage, _builtinCapacity);
-		_str = _storage;
-	} else {
-		// String in external storage: use refcount mechanism
-		str.incRefCount();
-		_extern._refCount = str._extern._refCount;
-		_extern._capacity = str._extern._capacity;
-		_str = str._str;
-	}
-	assert(_str != nullptr);
-}
-
 String::String(char c)
-	: _size(0), _str(_storage) {
+	: BaseString() {
 
 	_storage[0] = c;
 	_storage[1] = 0;
@@ -125,214 +38,45 @@ String::String(char c)
 	_size = (c == 0) ? 0 : 1;
 }
 
+#ifndef SCUMMVM_UTIL
 String::String(const U32String &str)
-	: _size(0), _str(_storage) {
+	: BaseString() {
 	_storage[0] = 0;
 	*this = String(str.encode());
 }
+#endif
 
 String::~String() {
 	decRefCount(_extern._refCount);
 }
 
-void String::makeUnique() {
-	ensureCapacity(_size, true);
-}
-
-/**
- * Ensure that enough storage is available to store at least new_size
- * characters plus a null byte. In addition, if we currently share
- * the storage with another string, unshare it, so that we can safely
- * write to the storage.
- */
-void String::ensureCapacity(uint32 new_size, bool keep_old) {
-	bool isShared;
-	uint32 curCapacity, newCapacity;
-	char *newStorage;
-	int *oldRefCount = _extern._refCount;
-
-	if (isStorageIntern()) {
-		isShared = false;
-		curCapacity = _builtinCapacity;
-	} else {
-		isShared = (oldRefCount && *oldRefCount > 1);
-		curCapacity = _extern._capacity;
-	}
-
-	// Special case: If there is enough space, and we do not share
-	// the storage, then there is nothing to do.
-	if (!isShared && new_size < curCapacity)
-		return;
-
-	// We need to allocate storage on the heap!
-
-	// Compute a suitable new capacity limit
-	// If the current capacity is sufficient we use the same capacity
-	if (new_size < curCapacity)
-		newCapacity = curCapacity;
-	else
-		newCapacity = MAX(curCapacity * 2, computeCapacity(new_size+1));
-
-	// Allocate new storage
-	newStorage = new char[newCapacity];
-	assert(newStorage);
-
-
-	// Copy old data if needed, elsewise reset the new storage.
-	if (keep_old) {
-		assert(_size < newCapacity);
-		memcpy(newStorage, _str, _size + 1);
-	} else {
-		_size = 0;
-		newStorage[0] = 0;
-	}
-
-	// Release hold on the old storage ...
-	decRefCount(oldRefCount);
-
-	// ... in favor of the new storage
-	_str = newStorage;
-
-	if (!isStorageIntern()) {
-		// Set the ref count & capacity if we use an external storage.
-		// It is important to do this *after* copying any old content,
-		// else we would override data that has not yet been copied!
-		_extern._refCount = nullptr;
-		_extern._capacity = newCapacity;
-	}
-}
-
-void String::incRefCount() const {
-	assert(!isStorageIntern());
-	if (_extern._refCount == nullptr) {
-		lockMemoryPoolMutex();
-		if (g_refCountPool == nullptr) {
-			g_refCountPool = new MemoryPool(sizeof(int));
-			assert(g_refCountPool);
-		}
-
-		_extern._refCount = (int *)g_refCountPool->allocChunk();
-		unlockMemoryPoolMutex();
-		*_extern._refCount = 2;
-	} else {
-		++(*_extern._refCount);
-	}
-}
-
-void String::decRefCount(int *oldRefCount) {
-	if (isStorageIntern())
-		return;
-
-	if (oldRefCount) {
-		--(*oldRefCount);
-	}
-	if (!oldRefCount || *oldRefCount <= 0) {
-		// The ref count reached zero, so we free the string storage
-		// and the ref count storage.
-		if (oldRefCount) {
-			lockMemoryPoolMutex();
-			assert(g_refCountPool);
-			g_refCountPool->freeChunk(oldRefCount);
-			unlockMemoryPoolMutex();
-		}
-		// Coverity thinks that we always free memory, as it assumes
-		// (correctly) that there are cases when oldRefCount == 0
-		// Thus, DO NOT COMPILE, trick it and shut tons of false positives
-#ifndef __COVERITY__
-		delete[] _str;
-#endif
-
-		// Even though _str points to a freed memory block now,
-		// we do not change its value, because any code that calls
-		// decRefCount will have to do this afterwards anyway.
-	}
-}
-
 String &String::operator=(const char *str) {
-	uint32 len = strlen(str);
-	ensureCapacity(len, false);
-	_size = len;
-	memmove(_str, str, len + 1);
+	assign(str);
 	return *this;
 }
 
 String &String::operator=(const String &str) {
-	if (&str == this)
-		return *this;
-
-	if (str.isStorageIntern()) {
-		decRefCount(_extern._refCount);
-		_size = str._size;
-		_str = _storage;
-		memcpy(_str, str._str, _size + 1);
-	} else {
-		str.incRefCount();
-		decRefCount(_extern._refCount);
-
-		_extern._refCount = str._extern._refCount;
-		_extern._capacity = str._extern._capacity;
-		_size = str._size;
-		_str = str._str;
-	}
-
+	assign(str);
 	return *this;
 }
 
 String &String::operator=(char c) {
-	decRefCount(_extern._refCount);
-	_str = _storage;
-
-	_str[0] = c;
-	_str[1] = 0;
-
-	_size = (c == 0) ? 0 : 1;
+	assign(c);
 	return *this;
 }
 
 String &String::operator+=(const char *str) {
-	if (pointerInOwnBuffer(str))
-		return operator+=(String(str));
-
-	int len = strlen(str);
-	if (len > 0) {
-		ensureCapacity(_size + len, true);
-
-		memcpy(_str + _size, str, len + 1);
-		_size += len;
-	}
+	assignAppend(str);
 	return *this;
 }
 
-bool String::pointerInOwnBuffer(const char *str) const {
-	//compared pointers must be in the same array or UB
-	//cast to intptr however is IB
-	//which includes comparision of the values
-	uintptr ownBuffStart = (uintptr)_str;
-	uintptr ownBuffEnd = (uintptr)(_str + _size);
-	uintptr candidateAddr = (uintptr)str;
-	return ownBuffStart <= candidateAddr && candidateAddr <= ownBuffEnd;
-}
-
 String &String::operator+=(const String &str) {
-	if (&str == this)
-		return operator+=(String(str));
-
-	int len = str._size;
-	if (len > 0) {
-		ensureCapacity(_size + len, true);
-
-		memcpy(_str + _size, str._str, len + 1);
-		_size += len;
-	}
+	assignAppend(str);
 	return *this;
 }
 
 String &String::operator+=(char c) {
-	ensureCapacity(_size + 1, true);
-
-	_str[_size++] = c;
-	_str[_size] = 0;
-
+	assignAppend(c);
 	return *this;
 }
 
@@ -432,45 +176,7 @@ bool String::contains(uint32 x) const {
 	return false;
 }
 
-uint64 String::asUint64() const {
-	uint64 result = 0;
-	for (uint32 i = 0; i < _size; ++i) {
-		if (_str[i] < '0' || _str[i] > '9') break;
-		result = result * 10L + (_str[i] - '0');
-	}
-	return result;
-}
-
-uint64 String::asUint64Ext() const {
-	uint64 result = 0;
-	uint64 base = 10;
-	uint32 skip = 0;
-	
-	if (_size >= 3 && _str[0] == '0' && _str[1] == 'x') {
-		base = 16;
-		skip = 2;
-	} else if (_size >= 2 && _str[0] == '0') {
-		base = 8;
-		skip = 1;
-	} else {
-		base = 10;
-		skip = 0;
-	}
-	for (uint32 i = skip; i < _size; ++i) {
-		char digit = _str[i];
-		uint64 digitval = 17; // sentinel
-		if (digit >= '0' && digit <= '9')
-			digitval = digit - '0';
-		else if (digit >= 'a' && digit <= 'f')
-			digitval = digit - 'a' + 10;
-		else if (digit >= 'A' && digit <= 'F')
-			digitval = digit - 'A' + 10;
-		if (digitval > base)
-			break;
-		result = result * base + digitval;
-	}
-	return result;
-}
+#ifndef SCUMMVM_UTIL
 
 bool String::matchString(const char *pat, bool ignoreCase, bool pathMode) const {
 	return Common::matchString(c_str(), pat, ignoreCase, pathMode);
@@ -480,146 +186,7 @@ bool String::matchString(const String &pat, bool ignoreCase, bool pathMode) cons
 	return Common::matchString(c_str(), pat.c_str(), ignoreCase, pathMode);
 }
 
-void String::deleteLastChar() {
-	if (_size > 0)
-		deleteChar(_size - 1);
-}
-
-void String::deleteChar(uint32 p) {
-	assert(p < _size);
-
-	makeUnique();
-	while (p++ < _size)
-		_str[p - 1] = _str[p];
-	_size--;
-}
-
-void String::erase(uint32 p, uint32 len) {
-	if (p == npos || len == 0)
-		return;
-	assert(p < _size);
-
-	makeUnique();
-	// If len == npos or p + len is over the end, remove all the way to the end
-	if (len == npos || p + len >= _size) {
-		// Delete char at p as well. So _size = (p - 1) + 1
-		_size = p;
-		// Null terminate
-		_str[_size] = 0;
-		return;
-	}
-
-	for ( ; p + len <= _size; p++) {
-		_str[p] = _str[p + len];
-	}
-	_size -= len;
-}
-
-String::iterator String::erase(iterator it) {
-	this->deleteChar(it - _str);
-	return it;
-}
-
-void String::clear() {
-	decRefCount(_extern._refCount);
-
-	_size = 0;
-	_str = _storage;
-	_storage[0] = 0;
-}
-
-void String::setChar(char c, uint32 p) {
-	assert(p < _size);
-
-	makeUnique();
-	_str[p] = c;
-}
-
-void String::insertChar(char c, uint32 p) {
-	assert(p <= _size);
-
-	ensureCapacity(_size + 1, true);
-	_size++;
-	for (uint32 i = _size; i > p; --i)
-		_str[i] = _str[i - 1];
-	_str[p] = c;
-}
-
-void String::toLowercase() {
-	makeUnique();
-	for (uint32 i = 0; i < _size; ++i)
-		_str[i] = tolower(_str[i]);
-}
-
-void String::toUppercase() {
-	makeUnique();
-	for (uint32 i = 0; i < _size; ++i)
-		_str[i] = toupper(_str[i]);
-}
-
-void String::trim() {
-	if (_size == 0)
-		return;
-
-	makeUnique();
-
-	// Trim trailing whitespace
-	while (_size >= 1 && isSpace(_str[_size - 1]))
-		--_size;
-	_str[_size] = 0;
-
-	// Trim leading whitespace
-	char *t = _str;
-	while (isSpace(*t))
-		t++;
-
-	if (t != _str) {
-		_size -= t - _str;
-		memmove(_str, t, _size + 1);
-	}
-}
-
-void String::wordWrap(const uint32 maxLength) {
-	if (_size < maxLength) {
-		return;
-	}
-
-	makeUnique();
-
-	const uint32 kNoSpace = 0xFFFFFFFF;
-
-	uint32 i = 0;
-	while (i < _size) {
-		uint32 lastSpace = kNoSpace;
-		uint32 x = 0;
-		while (i < _size && x <= maxLength) {
-			const char c = _str[i];
-			if (c == '\n') {
-				lastSpace = kNoSpace;
-				x = 0;
-			} else {
-				if (Common::isSpace(c)) {
-					lastSpace = i;
-				}
-				++x;
-			}
-			++i;
-		}
-
-		if (x > maxLength) {
-			if (lastSpace == kNoSpace) {
-				insertChar('\n', i - 1);
-			} else {
-				setChar('\n', lastSpace);
-				i = lastSpace + 1;
-			}
-		}
-	}
-}
-
-uint String::hash() const {
-	return hashit(c_str());
-}
+#endif
 
 void String::replace(uint32 pos, uint32 count, const String &str) {
 	replace(pos, count, str, 0, str._size);
@@ -645,12 +212,13 @@ void String::replace(uint32 posOri, uint32 countOri, const String &str,
 void String::replace(uint32 posOri, uint32 countOri, const char *str,
 					 uint32 posDest, uint32 countDest) {
 
-	ensureCapacity(_size + countDest - countOri, true);
-
 	// Prepare string for the replaced text.
 	if (countOri < countDest) {
 		uint32 offset = countDest - countOri; ///< Offset to copy the characters
 		uint32 newSize = _size + offset;
+
+		ensureCapacity(newSize, true);
+
 		_size = newSize;
 
 		// Push the old characters to the end of the string
@@ -660,11 +228,15 @@ void String::replace(uint32 posOri, uint32 countOri, const char *str,
 	} else if (countOri > countDest){
 		uint32 offset = countOri - countDest; ///< Number of positions that we have to pull back
 
+		makeUnique();
+
 		// Pull the remainder string back
-		for (uint32 i = posOri + countDest; i < _size; i++)
+		for (uint32 i = posOri + countDest; i + offset <= _size; i++)
 			_str[i] = _str[i + offset];
 
 		_size -= offset;
+	} else {
+		makeUnique();
 	}
 
 	// Copy the replaced part of the string
@@ -673,31 +245,6 @@ void String::replace(uint32 posOri, uint32 countOri, const char *str,
 
 }
 
-uint32 String::find(const String &str, uint32 pos) const {
-	if (pos >= _size) {
-		return npos;
-	}
-
-	const char *strP = str.c_str();
-
-	for (const_iterator cur = begin() + pos; *cur; ++cur) {
-		uint i = 0;
-		while (true) {
-			if (!strP[i]) {
-				return cur - begin();
-			}
-
-			if (cur[i] != strP[i]) {
-				break;
-			}
-
-			++i;
-		}
-	}
-
-	return npos;
-}
-
 // static
 String String::format(const char *fmt, ...) {
 	String output;
@@ -762,17 +309,6 @@ String String::vformat(const char *fmt, va_list args) {
 	return output;
 }
 
-
-size_t String::find(char c, size_t pos) const {
-	const char *p = strchr(_str + pos, c);
-	return p ? p - _str : npos;
-}
-
-size_t String::find(const char *s) const {
-	const char *str = strstr(_str, s);
-	return str ? str - _str : npos;
-}
-
 size_t String::rfind(const char *s) const {
 	int sLen = strlen(s);
 
@@ -874,42 +410,6 @@ String String::substr(size_t pos, size_t len) const {
 
 #pragma mark -
 
-bool String::operator==(const String &x) const {
-	return equals(x);
-}
-
-bool String::operator==(const char *x) const {
-	assert(x != nullptr);
-	return equals(x);
-}
-
-bool String::operator!=(const String &x) const {
-	return !equals(x);
-}
-
-bool String::operator !=(const char *x) const {
-	assert(x != nullptr);
-	return !equals(x);
-}
-
-bool String::operator<(const String &x) const {
-	return compareTo(x) < 0;
-}
-
-bool String::operator<=(const String &x) const {
-	return compareTo(x) <= 0;
-}
-
-bool String::operator>(const String &x) const {
-	return compareTo(x) > 0;
-}
-
-bool String::operator>=(const String &x) const {
-	return compareTo(x) >= 0;
-}
-
-#pragma mark -
-
 bool operator==(const char* y, const String &x) {
 	return (x == y);
 }
@@ -920,15 +420,6 @@ bool operator!=(const char* y, const String &x) {
 
 #pragma mark -
 
-bool String::equals(const String &x) const {
-	return (0 == compareTo(x));
-}
-
-bool String::equals(const char *x) const {
-	assert(x != nullptr);
-	return (0 == compareTo(x));
-}
-
 bool String::equalsIgnoreCase(const String &x) const {
 	return (0 == compareToIgnoreCase(x));
 }
@@ -938,15 +429,6 @@ bool String::equalsIgnoreCase(const char *x) const {
 	return (0 == compareToIgnoreCase(x));
 }
 
-int String::compareTo(const String &x) const {
-	return compareTo(x.c_str());
-}
-
-int String::compareTo(const char *x) const {
-	assert(x != nullptr);
-	return strcmp(c_str(), x);
-}
-
 int String::compareToIgnoreCase(const String &x) const {
 	return compareToIgnoreCase(x.c_str());
 }
@@ -997,6 +479,8 @@ String operator+(const String &x, char y) {
 	return temp;
 }
 
+#ifndef SCUMMVM_UTIL
+
 char *ltrim(char *t) {
 	while (isSpace(*t))
 		t++;
@@ -1014,6 +498,8 @@ char *trim(char *t) {
 	return rtrim(ltrim(t));
 }
 
+#endif
+
 String lastPathComponent(const String &path, const char sep) {
 	const char *str = path.c_str();
 	const char *last = str + path.size();
@@ -1089,6 +575,8 @@ String normalizePath(const String &path, const char sep) {
 	return result;
 }
 
+#ifndef SCUMMVM_UTIL
+
 bool matchString(const char *str, const char *pat, bool ignoreCase, bool pathMode) {
 	assert(str);
 	assert(pat);
@@ -1193,6 +681,8 @@ String tag2string(uint32 tag) {
 	return String(str);
 }
 
+#endif
+
 size_t strlcpy(char *dst, const char *src, size_t size) {
 	// Our backup of the source's start, we need this
 	// to calculate the source's length.
diff --git a/common/str.h b/common/str.h
index 5371bcffda..6b1113aa64 100644
--- a/common/str.h
+++ b/common/str.h
@@ -26,6 +26,7 @@
 #include "common/scummsys.h"
 #include "common/str-enc.h"
 #include "common/ustr.h"
+#include "common/base-str.h"
 
 #include <stdarg.h>
 
@@ -56,84 +57,29 @@ class U32String;
  * The presence of \0 characters in the string will cause undefined
  * behavior in some operations.
  */
-class String {
+class String : public BaseString<char> {
 public:
-	static const uint32 npos = 0xFFFFFFFF;
-
-	static void releaseMemoryPoolMutex();
-
-	typedef char          value_type;
 	/**
 	 * Unsigned version of the underlying type. This can be used to cast
 	 * individual string characters to bigger integer types without sign
 	 * extension happening.
 	 */
 	typedef unsigned char unsigned_type;
-	typedef char *        iterator;
-	typedef const char *  const_iterator;
-
-protected:
-	/**
-	 * The size of the internal storage. Increasing this means less heap
-	 * allocations are needed, at the cost of more stack memory usage,
-	 * and of course lots of wasted memory. Empirically, 90% or more of
-	 * all String instances are less than 32 chars long. If a platform
-	 * is very short on stack space, it would be possible to lower this.
-	 * A value of 24 still seems acceptable, though considerably worse,
-	 * while 16 seems to be the lowest you want to go... Anything lower
-	 * than 8 makes no sense, since that's the size of member _extern
-	 * (on 32 bit machines; 12 bytes on systems with 64bit pointers).
-	 */
-	static const uint32 _builtinCapacity = 32 - sizeof(uint32) - sizeof(char *);
-
-	/**
-	 * Length of the string. Stored to avoid having to call strlen
-	 * a lot. Yes, we limit ourselves to strings shorter than 4GB --
-	 * on purpose :-).
-	 */
-	uint32 _size;
 
-	/**
-	 * Pointer to the actual string storage. Either points to _storage,
-	 * or to a block allocated on the heap via malloc.
-	 */
-	char  *_str;
-
-
-	union {
-		/**
-		 * Internal string storage.
-		 */
-		char _storage[_builtinCapacity];
-		/**
-		 * External string storage data -- the refcounter, and the
-		 * capacity of the string _str points to.
-		 */
-		struct {
-			mutable int *_refCount;
-			uint32       _capacity;
-		} _extern;
-	};
-
-	inline bool isStorageIntern() const {
-		return _str == _storage;
-	}
-
-public:
 	/** Construct a new empty string. */
-	String() : _size(0), _str(_storage) { _storage[0] = 0; }
+	String() : BaseString() {}
 
 	/** Construct a new string from the given NULL-terminated C string. */
-	String(const char *str);
+	String(const char *str) : BaseString(str) {}
 
 	/** Construct a new string containing exactly len characters read from address str. */
-	String(const char *str, uint32 len);
+	String(const char *str, uint32 len) : BaseString(str, len) {}
 
 	/** Construct a new string containing the characters between beginP (including) and endP (excluding). */
-	String(const char *beginP, const char *endP);
+	String(const char *beginP, const char *endP) : BaseString(beginP, endP) {}
 
 	/** Construct a copy of the given string. */
-	String(const String &str);
+	String(const String &str) : BaseString(str) {};
 
 	/** Construct a string consisting of the given character. */
 	explicit String(char c);
@@ -150,24 +96,10 @@ public:
 	String &operator+=(const String &str);
 	String &operator+=(char c);
 
-	bool operator==(const String &x) const;
-	bool operator==(const char *x) const;
-	bool operator!=(const String &x) const;
-	bool operator!=(const char *x) const;
-
-	bool operator<(const String &x) const;
-	bool operator<=(const String &x) const;
-	bool operator>(const String &x) const;
-	bool operator>=(const String &x) const;
-
-	bool equals(const String &x) const;
 	bool equalsIgnoreCase(const String &x) const;
-	int compareTo(const String &x) const;           // strcmp clone
 	int compareToIgnoreCase(const String &x) const; // stricmp clone
 
-	bool equals(const char *x) const;
 	bool equalsIgnoreCase(const char *x) const;
-	int compareTo(const char *x) const;             // strcmp clone
 	int compareToIgnoreCase(const char *x) const;   // stricmp clone
 	int compareDictionary(const String &x) const;
 	int compareDictionary(const char *x) const;
@@ -187,12 +119,6 @@ public:
 	bool contains(char x) const;
 	bool contains(uint32 x) const;
 
-	/** Return uint64 corrensponding to String's contents. */
-	uint64 asUint64() const;
-
-  	/** Return uint64 corrensponding to String's contents. This variant recognizes 0 (oct) and 0x (hex) prefixes. */
-	uint64 asUint64Ext() const;
-
 	/**
 	 * Simple DOS-style pattern matching function (understands * and ? like used in DOS).
 	 * Taken from exult/files/listfiles.cc
@@ -221,65 +147,6 @@ public:
 	bool matchString(const char *pat, bool ignoreCase = false, bool pathMode = false) const;
 	bool matchString(const String &pat, bool ignoreCase = false, bool pathMode = false) const;
 
-
-	inline const char *c_str() const { return _str; }
-	inline uint size() const         { return _size; }
-
-	inline bool empty() const { return (_size == 0); }
-	char firstChar() const    { return (_size > 0) ? _str[0] : 0; }
-	char lastChar() const     { return (_size > 0) ? _str[_size - 1] : 0; }
-
-	char operator[](int idx) const {
-		assert(_str && idx >= 0 && idx < (int)_size);
-		return _str[idx];
-	}
-
-	/** Remove the last character from the string. */
-	void deleteLastChar();
-
-	/** Remove the character at position p from the string. */
-	void deleteChar(uint32 p);
-
-	/** Remove all characters from position p to the p + len. If len = String::npos, removes all characters to the end */
-	void erase(uint32 p, uint32 len = npos);
-
-	/** Erases the character at the given iterator location */
-	iterator erase(iterator it);
-
-	/** Set character c at position p, replacing the previous character there. */
-	void setChar(char c, uint32 p);
-
-	/** Insert character c before position p. */
-	void insertChar(char c, uint32 p);
-
-	/** Clears the string, making it empty. */
-	void clear();
-
-	/** Convert all characters in the string to lowercase. */
-	void toLowercase();
-
-	/** Convert all characters in the string to uppercase. */
-	void toUppercase();
-
-	/**
-	 * Removes trailing and leading whitespaces. Uses isspace() to decide
-	 * what is whitespace and what not.
-	 */
-	void trim();
-
-	/**
-	 * 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
-	 * represented using '\n'.
-	 *
-	 * This is a very basic line wrap which does not perform tab stop
-	 * calculation, consecutive whitespace collapsing, auto-hyphenation, or line
-	 * balancing.
-	 */
-	void wordWrap(const uint32 maxLength);
-
-	uint hash() const;
-
 	/**@{
 	 * Functions to replace some amount of chars with chars from some other string.
 	 *
@@ -326,13 +193,6 @@ public:
 	 */
 	static String vformat(const char *fmt, va_list args);
 
-	/** Finds the index of a character in the string */
-	size_t find(char c, size_t pos = 0) const;
-
-	/** Does a find for the passed string */
-	size_t find(const char *s) const;
-	uint32 find(const String &str, uint32 pos = 0) const;
-
 	/** Does a reverse find for the passed string */
 	size_t rfind(const char *s) const;
 	size_t rfind(const String &s) const {
@@ -381,42 +241,10 @@ public:
 	/** Return a substring of this string */
 	String substr(size_t pos = 0, size_t len = npos) const;
 
-public:
-
-	iterator begin() {
-		// Since the user could potentially
-		// change the string via the returned
-		// iterator we have to assure we are
-		// pointing to a unique storage.
-		makeUnique();
-
-		return _str;
-	}
-
-	iterator end() {
-		return begin() + size();
-	}
-
-	const_iterator begin() const {
-		return _str;
-	}
-
-	const_iterator end() const {
-		return begin() + size();
-	}
-
 	/** Python-like method **/
 	U32String decode(CodePage page = kUtf8) const;
 
 protected:
-	void makeUnique();
-	void ensureCapacity(uint32 new_size, bool keep_old);
-	void incRefCount() const;
-	void decRefCount(int *oldRefCount);
-	void initWithCStr(const char *str, uint32 len);
-
-	bool pointerInOwnBuffer(const char *str) const;
-
 	void decodeUTF8(U32String &dst) const;
 	void decodeOneByte(U32String &dst, CodePage page) const;
 };
diff --git a/common/ustr.cpp b/common/ustr.cpp
index 00584dd629..a4bad790a6 100644
--- a/common/ustr.cpp
+++ b/common/ustr.cpp
@@ -28,53 +28,7 @@
 
 namespace Common {
 
-extern MemoryPool *g_refCountPool;
-
-static uint32 computeCapacity(uint32 len) {
-	// By default, for the capacity we use the next multiple of 32
-	return ((len + 32 - 1) & ~0x1F);
-}
-
-U32String::U32String(const value_type *str) : _size(0), _str(_storage) {
-	if (str == nullptr) {
-		_storage[0] = 0;
-		_size = 0;
-	} else {
-		uint32 len = 0;
-		const value_type *s = str;
-		while (*s++) {
-			++len;
-		}
-		initWithCStr(str, len);
-	}
-}
-
-U32String::U32String(const value_type *str, uint32 len) : _size(0), _str(_storage) {
-	initWithCStr(str, len);
-}
-
-U32String::U32String(const value_type *beginP, const value_type *endP) : _size(0), _str(_storage) {
-	assert(endP >= beginP);
-	initWithCStr(beginP, endP - beginP);
-}
-
-U32String::U32String(const U32String &str)
-    : _size(str._size) {
-	if (str.isStorageIntern()) {
-		// String in internal storage: just copy it
-		memcpy(_storage, str._storage, _builtinCapacity * sizeof(value_type));
-		_str = _storage;
-	} else {
-		// String in external storage: use refcount mechanism
-		str.incRefCount();
-		_extern._refCount = str._extern._refCount;
-		_extern._capacity = str._extern._capacity;
-		_str = str._str;
-	}
-	assert(_str != nullptr);
-}
-
-U32String::U32String(const char *str) : _size(0), _str(_storage) {
+U32String::U32String(const char *str) : BaseString() {
 	if (str == nullptr) {
 		_storage[0] = 0;
 		_size = 0;
@@ -83,21 +37,21 @@ U32String::U32String(const char *str) : _size(0), _str(_storage) {
 	}
 }
 
-U32String::U32String(const char *str, uint32 len) : _size(0), _str(_storage) {
+U32String::U32String(const char *str, uint32 len) : BaseString() {
 	initWithCStr(str, len);
 }
 
-U32String::U32String(const char *beginP, const char *endP) : _size(0), _str(_storage) {
+U32String::U32String(const char *beginP, const char *endP) : BaseString() {
 	assert(endP >= beginP);
 	initWithCStr(beginP, endP - beginP);
 }
 
-U32String::U32String(const String &str) : _size(0), _str(_storage) {
+U32String::U32String(const String &str) : BaseString() {
 	initWithCStr(str.c_str(), str.size());
 }
 
-U32String::U32String(const UnicodeBiDiText &txt) : _size(0), _str(_storage) {
-	initWithCStr(txt.visual.c_str(), txt.visual.size());
+U32String::U32String(const UnicodeBiDiText &txt) : BaseString() {
+	initWithValueTypeStr(txt.visual.c_str(), txt.visual.size());
 }
 
 U32String::~U32String() {
@@ -105,24 +59,7 @@ U32String::~U32String() {
 }
 
 U32String &U32String::operator=(const U32String &str) {
-	if (&str == this)
-		return *this;
-
-	if (str.isStorageIntern()) {
-		decRefCount(_extern._refCount);
-		_size = str._size;
-		_str = _storage;
-		memcpy(_str, str._str, (_size + 1) * sizeof(value_type));
-	} else {
-		str.incRefCount();
-		decRefCount(_extern._refCount);
-
-		_extern._refCount = str._extern._refCount;
-		_extern._capacity = str._extern._capacity;
-		_size = str._size;
-		_str = str._str;
-	}
-
+	assign(str);
 	return *this;
 }
 
@@ -166,465 +103,43 @@ U32String &U32String::operator+=(value_type c) {
 	return *this;
 }
 
-bool U32String::operator==(const U32String &x) const {
-	return equals(x);
-}
-
 bool U32String::operator==(const String &x) const {
-	return equals(x);
-}
-
-bool U32String::operator==(const value_type *x) const {
-	return equals(U32String(x));
+	return equalsC(x.c_str());
 }
 
 bool U32String::operator==(const char *x) const {
-	return equals(x);
-}
-
-bool U32String::operator!=(const U32String &x) const {
-	return !equals(x);
+	return equalsC(x);
 }
 
 bool U32String::operator!=(const String &x) const {
-	return !equals(x);
-}
-
-bool U32String::operator!=(const value_type *x) const {
-	return !equals(U32String(x));
+	return !equalsC(x.c_str());
 }
 
 bool U32String::operator!=(const char *x) const {
-	return !equals(x);
-}
-
-bool U32String::operator<(const U32String &x) const {
-	for (uint32 i = 0, n = x.size(); i < _size && i < n; ++i) {
-		uint32 sc = _str[i];
-		uint32 xc = x[i];
-		if (sc < xc)
-			return true;
-		else if (sc > xc)
-			return false;
-	}
-	return (_size < x.size());
-}
-
-bool U32String::operator<=(const U32String &x) const {
-	return !operator>(x);
-}
-
-bool U32String::operator>(const U32String &x) const {
-	for (uint i = 0, n = x.size(); i < _size && i < n; ++i) {
-		uint32 sc = _str[i];
-		uint32 xc = x[i];
-		if (sc > xc)
-			return true;
-		else if (sc < xc)
-			return false;
-	}
-	return (_size > x.size());
-}
-
-bool U32String::operator>=(const U32String &x) const {
-	return !operator<(x);
-}
-
-bool U32String::equals(const U32String &x) const {
-	if (this == &x || _str == x._str) {
-		return true;
-	}
-
-	if (x.size() != _size) {
-		return false;
-	}
-
-	return !memcmp(_str, x._str, _size * sizeof(value_type));
-}
-
-bool U32String::equals(const String &x) const {
-	if (x.size() != _size)
-		return false;
-
-	for (uint32 idx = 0; idx < _size; ++idx)
-		if (_str[idx] != static_cast<value_type>(x[idx]))
-			return false;
-
-	return true;
-}
-
-bool U32String::contains(value_type x) const {
-	for (uint32 i = 0; i < _size; ++i) {
-		if (_str[i] == x) {
-			return true;
-		}
-	}
-
-	return false;
-}
-
-bool U32String::contains(const U32String &otherString) const {
-	if (empty() || otherString.empty() || _size < otherString.size()) {
-		return false;
-	}
-
-	uint32 size = 0;
-	U32String::const_iterator itr = otherString.begin();
-
-	for (U32String::const_iterator itr2 = begin(); itr != otherString.end() && itr2 != end(); itr2++) {
-		if (*itr == *itr2) {
-			itr++;
-			size++;
-			if (size == otherString.size())
-				return true;
-		} else {
-			size = 0;
-			itr = otherString.begin();
-		}
-	}
-
-	return false;
-}
-
-void U32String::insertChar(value_type c, uint32 p) {
-	assert(p <= _size);
-
-	ensureCapacity(_size + 1, true);
-	_size++;
-	for (uint32 i = _size; i > p; --i)
-		_str[i] = _str[i - 1];
-	_str[p] = c;
-}
-
-void U32String::insertString(String s, uint32 p) {
-	for (String::iterator i = s.begin(); i != s.end(); i++) {
-		U32String::insertChar(*i, p++);
-	}
-}
-
-void U32String::insertString(value_type *s, uint32 p) {
-	while (*s != '\0') {
-		U32String::insertChar(*s++, p++);
-	}
-}
-
-void U32String::deleteChar(uint32 p) {
-	assert(p < _size);
-
-	makeUnique();
-	while (p++ < _size)
-		_str[p - 1] = _str[p];
-	_size--;
-}
-
-void U32String::deleteLastChar() {
-	if (_size > 0)
-		deleteChar(_size - 1);
-}
-
-void U32String::erase(uint32 p, uint32 len) {
-	assert(p < _size);
-
-	makeUnique();
-	// If len == npos or p + len is over the end, remove all the way to the end
-	if (len == npos || p + len >= _size) {
-		// Delete char at p as well. So _size = (p - 1) + 1
-		_size = p;
-		// Null terminate
-		_str[_size] = 0;
-		return;
-	}
-
-	for ( ; p + len <= _size; p++) {
-		_str[p] = _str[p + len];
-	}
-	_size -= len;
-}
-
-void U32String::clear() {
-	decRefCount(_extern._refCount);
-
-	_size = 0;
-	_str = _storage;
-	_storage[0] = 0;
-}
-
-void U32String::toLowercase() {
-	makeUnique();
-	for (uint32 i = 0; i < _size; ++i) {
-		if (_str[i] < 128) {
-			_str[i] = tolower(_str[i]);
-		}
-	}
-}
-
-void U32String::toUppercase() {
-	makeUnique();
-	for (uint32 i = 0; i < _size; ++i) {
-		if (_str[i] < 128) {
-			_str[i] = toupper(_str[i]);
-		}
-	}
-}
-
-uint32 U32String::find(value_type x, uint32 pos) const {
-	for (uint32 i = pos; i < _size; ++i) {
-		if (_str[i] == x) {
-			return i;
-		}
-	}
-
-	return npos;
-}
-
-uint32 U32String::find(const U32String &str, uint32 pos) const {
-	if (pos >= _size) {
-		return npos;
-	}
-
-	const value_type *strP = str.c_str();
-
-	for (const_iterator cur = begin() + pos; *cur; ++cur) {
-		uint i = 0;
-		while (true) {
-			if (!strP[i]) {
-				return cur - begin();
-			}
-
-			if (cur[i] != strP[i]) {
-				break;
-			}
-
-			++i;
-		}
-	}
-
-	return npos;
-}
-
-void U32String::makeUnique() {
-	ensureCapacity(_size, true);
-}
-
-void U32String::ensureCapacity(uint32 new_size, bool keep_old) {
-	bool isShared;
-	uint32 curCapacity, newCapacity;
-	value_type *newStorage;
-	int *oldRefCount = _extern._refCount;
-
-	if (isStorageIntern()) {
-		isShared = false;
-		curCapacity = _builtinCapacity;
-	} else {
-		isShared = (oldRefCount && *oldRefCount > 1);
-		curCapacity = _extern._capacity;
-	}
-
-	// Special case: If there is enough space, and we do not share
-	// the storage, then there is nothing to do.
-	if (!isShared && new_size < curCapacity)
-		return;
-
-	if (isShared && new_size < _builtinCapacity) {
-		// We share the storage, but there is enough internal storage: Use that.
-		newStorage = _storage;
-		newCapacity = _builtinCapacity;
-	} else {
-		// We need to allocate storage on the heap!
-
-		// Compute a suitable new capacity limit
-		// If the current capacity is sufficient we use the same capacity
-		if (new_size < curCapacity)
-			newCapacity = curCapacity;
-		else
-			newCapacity = MAX(curCapacity * 2, computeCapacity(new_size + 1));
-
-		// Allocate new storage
-		newStorage = new value_type[newCapacity];
-		assert(newStorage);
-	}
-
-	// Copy old data if needed, elsewise reset the new storage.
-	if (keep_old) {
-		assert(_size < newCapacity);
-		memcpy(newStorage, _str, (_size + 1) * sizeof(value_type));
-	} else {
-		_size = 0;
-		newStorage[0] = 0;
-	}
-
-	// Release hold on the old storage ...
-	decRefCount(oldRefCount);
-
-	// ... in favor of the new storage
-	_str = newStorage;
-
-	if (!isStorageIntern()) {
-		// Set the ref count & capacity if we use an external storage.
-		// It is important to do this *after* copying any old content,
-		// else we would override data that has not yet been copied!
-		_extern._refCount = nullptr;
-		_extern._capacity = newCapacity;
-	}
-}
-
-void U32String::incRefCount() const {
-	assert(!isStorageIntern());
-	if (_extern._refCount == nullptr) {
-		if (g_refCountPool == nullptr) {
-			g_refCountPool = new MemoryPool(sizeof(int));
-			assert(g_refCountPool);
-		}
-
-		_extern._refCount = (int *)g_refCountPool->allocChunk();
-		*_extern._refCount = 2;
-	} else {
-		++(*_extern._refCount);
-	}
-}
-
-void U32String::decRefCount(int *oldRefCount) {
-	if (isStorageIntern())
-		return;
-
-	if (oldRefCount) {
-		--(*oldRefCount);
-	}
-	if (!oldRefCount || *oldRefCount <= 0) {
-		// The ref count reached zero, so we free the string storage
-		// and the ref count storage.
-		if (oldRefCount) {
-			assert(g_refCountPool);
-			g_refCountPool->freeChunk(oldRefCount);
-		}
-		// Coverity thinks that we always free memory, as it assumes
-		// (correctly) that there are cases when oldRefCount == 0
-		// Thus, DO NOT COMPILE, trick it and shut tons of false positives
-#ifndef __COVERITY__
-		delete[] _str;
-#endif
-
-		// Even though _str points to a freed memory block now,
-		// we do not change its value, because any code that calls
-		// decRefCount will have to do this afterwards anyway.
-	}
-}
-
-void U32String::initWithCStr(const value_type *str, uint32 len) {
-	assert(str);
-
-	_storage[0] = 0;
-
-	_size = len;
-
-	if (len >= _builtinCapacity) {
-		// Not enough internal storage, so allocate more
-		_extern._capacity = computeCapacity(len + 1);
-		_extern._refCount = nullptr;
-		_str = new value_type[_extern._capacity];
-		assert(_str != nullptr);
-	}
-
-	// Copy the string into the storage area
-	memmove(_str, str, len * sizeof(value_type));
-	_str[len] = 0;
-}
-
-void U32String::initWithCStr(const char *str, uint32 len) {
-	assert(str);
-
-	_storage[0] = 0;
-
-	_size = len;
-
-	if (len >= _builtinCapacity) {
-		// Not enough internal storage, so allocate more
-		_extern._capacity = computeCapacity(len + 1);
-		_extern._refCount = nullptr;
-		_str = new value_type[_extern._capacity];
-		assert(_str != nullptr);
-	}
-
-	// Copy the string into the storage area
-	for (size_t idx = 0; idx < len; ++idx, ++str)
-		_str[idx] = (byte)(*str);
-
-	_str[len] = 0;
+	return !equalsC(x);
 }
 
-
 U32String operator+(const U32String &x, const U32String &y) {
 	U32String temp(x);
 	temp += y;
 	return temp;
 }
 
-void U32String::wordWrap(const uint32 maxLength) {
-	if (_size < maxLength) {
-		return;
-	}
-
-	makeUnique();
-
-	const uint32 kNoSpace = 0xFFFFFFFF;
-
-	uint32 i = 0;
-	while (i < _size) {
-		uint32 lastSpace = kNoSpace;
-		uint32 x = 0;
-		while (i < _size && x <= maxLength) {
-			const char c = _str[i];
-			if (c == '\n') {
-				lastSpace = kNoSpace;
-				x = 0;
-			} else {
-				if (Common::isSpace(c)) {
-					lastSpace = i;
-				}
-				++x;
-			}
-			++i;
-		}
-
-		if (x > maxLength) {
-			if (lastSpace == kNoSpace) {
-				insertChar('\n', i - 1);
-			} else {
-				setChar('\n', lastSpace);
-				i = lastSpace + 1;
-			}
-		}
-	}
+U32String operator+(const U32String &x, const uint32 y) {
+	U32String temp(x);
+	temp += y;
+	return temp;
 }
 
-uint64 U32String::asUint64() const {
-	uint64 result = 0;
-	for (uint32 i = 0; i < _size; ++i) {
-		if (_str[i] < '0' || _str[i] > '9') break;
-		result = result * 10L + (_str[i] - '0');
+void U32String::insertString(const char *s, uint32 p) {
+	while (*s != '\0') {
+		BaseString::insertChar(*s++, p++);
 	}
-	return result;
 }
 
-void U32String::trim() {
-	if (_size == 0)
-		return;
-
-	makeUnique();
-
-	// Trim trailing whitespace
-	while (_size >= 1 && isSpace(_str[_size - 1]))
-		--_size;
-	_str[_size] = 0;
-
-	// Trim leading whitespace
-	value_type *t = _str;
-	while (isSpace(*t))
-		t++;
-
-	if (t != _str) {
-		_size -= t - _str;
-		memmove(_str, t, _size + 1);
+void U32String::insertString(const String &s, uint32 p) {
+	for (uint32 i = 0; i < s.size(); ++i) {
+		BaseString::insertChar(s[i], p++);
 	}
 }
 
@@ -633,7 +148,7 @@ U32String U32String::format(U32String fmt, ...) {
 
 	va_list va;
 	va_start(va, fmt);
-	U32String::vformat(fmt.begin(), fmt.end(), output, va);
+	U32String::vformat(fmt.c_str(), fmt.c_str() + fmt.size(), output, va);
 	va_end(va);
 
 	return output;
@@ -645,13 +160,14 @@ U32String U32String::format(const char *fmt, ...) {
 	Common::U32String fmtU32(fmt);
 	va_list va;
 	va_start(va, fmt);
-	U32String::vformat(fmtU32.begin(), fmtU32.end(), output, va);
+	U32String::vformat(fmtU32.c_str(), fmtU32.c_str() + fmtU32.size(),
+			   output, va);
 	va_end(va);
 
 	return output;
 }
 
-int U32String::vformat(U32String::const_iterator fmt, const U32String::const_iterator inputItrEnd, U32String &output, va_list args) {
+int U32String::vformat(const value_type *fmt, const value_type *fmtEnd, U32String &output, va_list args) {
 	int int_temp;
 	char *string_temp;
 
@@ -664,7 +180,7 @@ int U32String::vformat(U32String::const_iterator fmt, const U32String::const_ite
 
 	char buffer[512];
 
-	while (fmt != inputItrEnd) {
+	while (fmt != fmtEnd) {
 		ch = *fmt++;
 		if (ch == '%') {
 			switch (ch = *fmt++) {
@@ -680,10 +196,10 @@ int U32String::vformat(U32String::const_iterator fmt, const U32String::const_ite
 				break;
 			case 's':
 				string_temp = va_arg(args, char *);
-				len = strlen(string_temp);
-				length += len;
-
+				tempPos = output.size();
 				output.insertString(string_temp, pos);
+				len = output.size() - tempPos;
+				length += len;
 				pos += len - 1;
 				break;
 			case 'i':
diff --git a/common/ustr.h b/common/ustr.h
index db4d078717..c602b1d1eb 100644
--- a/common/ustr.h
+++ b/common/ustr.h
@@ -25,6 +25,7 @@
 
 #include "common/scummsys.h"
 #include "common/str-enc.h"
+#include "common/base-str.h"
 
 namespace Common {
 
@@ -52,66 +53,36 @@ class UnicodeBiDiText;
  * The presence of \0 characters in the string will cause undefined
  * behavior in some operations.
  */
-class U32String {
-public:
-	static const uint32 npos = 0xFFFFFFFF;
+#ifdef USE_CXX11
+typedef char32_t u32char_type_t;
+#else
+typedef uint32 u32char_type_t;
+#endif
 
-	typedef uint32 value_type;
+class U32String : public BaseString<u32char_type_t> {
+public:
 	typedef uint32 unsigned_type;
-private:
-	/**
-	 * The size of the internal storage. Increasing this means less heap
-	 * allocations are needed, at the cost of more stack memory usage,
-	 * and of course lots of wasted memory.
-	 */
-	static const uint32 _builtinCapacity = 32;
-
-	/**
-	 * Length of the string.
-	 */
-	uint32 _size;
-
-	/**
-	 * Pointer to the actual string storage. Either points to _storage,
-	 * or to a block allocated on the heap via malloc.
-	 */
-	value_type  *_str;
-
-
-	union {
-		/**
-		 * Internal string storage.
-		 */
-		value_type _storage[_builtinCapacity];
-		/**
-		 * External string storage data -- the refcounter, and the
-		 * capacity of the string _str points to.
-		 */
-		struct {
-			mutable int *_refCount;
-			uint32       _capacity;
-		} _extern;
-	};
-
-	inline bool isStorageIntern() const {
-		return _str == _storage;
-	}
-
 public:
 	/** Construct a new empty string. */
-	U32String() : _size(0), _str(_storage) { _storage[0] = 0; }
+	U32String() : BaseString() {}
 
 	/** Construct a new string from the given NULL-terminated C string. */
-	explicit U32String(const value_type *str);
+	explicit U32String(const value_type *str) : BaseString(str) {}
 
 	/** Construct a new string containing exactly len characters read from address str. */
-	U32String(const value_type *str, uint32 len);
+	U32String(const value_type *str, uint32 len) : BaseString(str, len) {}
+
+#ifdef USE_CXX11
+	explicit U32String(const uint32 *str) : BaseString((const value_type *) str) {}
+	U32String(const uint32 *str, uint32 len) : BaseString((const value_type *) str, len) {}
+    	U32String(const uint32 *beginP, const uint32 *endP) : BaseString((const value_type *) beginP, (const value_type *) endP) {}
+#endif
 
 	/** Construct a new string containing the characters between beginP (including) and endP (excluding). */
-	U32String(const value_type *beginP, const value_type *endP);
+	U32String(const value_type *beginP, const value_type *endP) : BaseString(beginP, endP) {}
 
 	/** Construct a copy of the given string. */
-	U32String(const U32String &str);
+	U32String(const U32String &str) : BaseString(str) {}
 
 	/** Construct a copy of the given unicode BiDi converted string. */
 	U32String(const UnicodeBiDiText &txt);
@@ -136,128 +107,16 @@ public:
 	U32String &operator=(const char *str);
 	U32String &operator+=(const U32String &str);
 	U32String &operator+=(value_type c);
-	bool operator==(const U32String &x) const;
+	using BaseString<value_type>::operator==;
+	using BaseString<value_type>::operator!=;
 	bool operator==(const String &x) const;
-	bool operator==(const value_type *x) const;
 	bool operator==(const char *x) const;
-	bool operator!=(const U32String &x) const;
 	bool operator!=(const String &x) const;
-	bool operator!=(const value_type *x) const;
 	bool operator!=(const char *x) const;
 
-	bool operator<(const U32String &x) const;
-	bool operator<=(const U32String &x) const;
-	bool operator>(const U32String &x) const;
-	bool operator>=(const U32String &x) const;
-
-	/**
-	 * Compares whether two U32String are the same based on memory comparison.
-	 * This does *not* do comparison based on canonical equivalence.
-	 */
-	bool equals(const U32String &x) const;
-
-	/**
-	 * Compares whether two U32String are the same based on memory comparison.
-	 * This does *not* do comparison based on canonical equivalence.
-	 */
-	bool equals(const String &x) const;
-
-	bool contains(value_type x) const;
-
-	/**
-	 * Checks if a given string is present in the internal string or not.
-	 */
-	bool contains(const U32String &otherString) const;
-
-	inline const value_type *c_str() const { return _str; }
-	inline uint32 size() const             { return _size; }
-
-	inline bool empty() const { return (_size == 0); }
-
-	value_type operator[](int idx) const {
-		assert(_str && idx >= 0 && idx < (int)_size);
-		return _str[idx];
-	}
-
-	/** Set character c at position p, replacing the previous character there. */
-	void setChar(value_type c, uint32 p) {
-		_str[p] = c;
-	}
-
-	/** Insert character c before position p. */
-	void insertChar(value_type c, uint32 p);
-	void insertString(String s, uint32 p);
-	void insertString(value_type *s, uint32 p);
-
-	/**
-	 * Removes the value at position p from the string.
-	 * Using this on decomposed characters will not remove the whole
-	 * character!
-	 */
-	void deleteChar(uint32 p);
-
-	/** Remove the last character from the string. */
-	void deleteLastChar();
-
-	/** Remove all characters from position p to the p + len. If len = String::npos, removes all characters to the end */
-	void erase(uint32 p, uint32 len = npos);
-
-	/** Clears the string, making it empty. */
-	void clear();
-
-	/**
-	 * Convert all characters in the string to lowercase.
-	 *
-	 * Be aware that this only affects the case of ASCII characters. All
-	 * other characters will not be touched at all.
-	 */
-	void toLowercase();
-
-	/**
-	 * Convert all characters in the string to uppercase.
-	 *
-	 * Be aware that this only affects the case of ASCII characters. All
-	 * other characters will not be touched at all.
-	 */
-	void toUppercase();
-
-	uint32 find(value_type x, uint32 pos = 0) const;
-	uint32 find(const U32String &str, uint32 pos = 0) const;
-
-	typedef value_type *        iterator;
-	typedef const value_type *  const_iterator;
-
-	iterator begin() {
-		// Since the user could potentially
-		// change the string via the returned
-		// iterator we have to assure we are
-		// pointing to a unique storage.
-		makeUnique();
-
-		return _str;
-	}
-
-	iterator end() {
-		return begin() + size();
-	}
-
-	const_iterator begin() const {
-		return _str;
-	}
-
-	const_iterator end() const {
-		return begin() + size();
-	}
-
     /** Python-like method **/
     String encode(CodePage page = kUtf8) const;
 
-	void wordWrap(const uint32 maxLength);
-
-	uint64 asUint64() const;
-
-	void trim();
-
 	/**
 	 * Print formatted data into a U32String object.
 	 */
@@ -268,7 +127,7 @@ public:
 	 * Print formatted data into a U32String object. It takes in the
 	 * output by reference and works with iterators.
 	 */
-	static int vformat(U32String::const_iterator fmt, const U32String::const_iterator inputItrEnd, U32String &output, va_list args);
+	static int vformat(const value_type *fmt, const value_type *fmtEnd, U32String &output, va_list args);
 
 	/**
 	 * Helper function for vformat, convert an int to string
@@ -276,19 +135,21 @@ public:
 	*/
 	static char* itoa(int num, char* str, int base);
 
-private:
-	void makeUnique();
-	void ensureCapacity(uint32 new_size, bool keep_old);
-	void incRefCount() const;
-	void decRefCount(int *oldRefCount);
-	void initWithCStr(const value_type *str, uint32 len);
-	void initWithCStr(const char *str, uint32 len);
+	using BaseString<value_type>::insertString;
+	void insertString(const char *s, uint32 p);
+	void insertString(const String &s, uint32 p);
+
+	const uint32 *u32_str() const {
+		return (const uint32 *) _str;
+	}
 
+private:
 	void encodeUTF8(String &dst) const;
 	void encodeOneByte(String &dst, CodePage page) const;
 };
 
 U32String operator+(const U32String &x, const U32String &y);
+U32String operator+(const U32String &x, U32String::value_type y);
 
 /** @} */
 
diff --git a/common/winexe.h b/common/winexe.h
index 1bf203338a..b6c21cc543 100644
--- a/common/winexe.h
+++ b/common/winexe.h
@@ -94,7 +94,7 @@ private:
 };
 
 struct WinResourceID_Hash {
-	uint operator()(const WinResourceID &id) const { return hashit(id.toString()); }
+	uint operator()(const WinResourceID &id) const { return id.toString().hash(); }
 };
 
 struct WinResourceID_EqualTo {
diff --git a/devtools/create_titanic/base-str.cpp b/devtools/create_titanic/base-str.cpp
new file mode 100644
index 0000000000..f5ebe1ad3b
--- /dev/null
+++ b/devtools/create_titanic/base-str.cpp
@@ -0,0 +1,2 @@
+#define SCUMMVM_UTIL 1
+#include "../common/base-str.cpp"
diff --git a/devtools/create_titanic/hash-str.h b/devtools/create_titanic/hash-str.h
index b9f6d503f8..2e0bdafd1a 100644
--- a/devtools/create_titanic/hash-str.h
+++ b/devtools/create_titanic/hash-str.h
@@ -24,7 +24,7 @@
 #define COMMON_HASH_STR_H
 
 #include "hashmap.h"
-#include "str.h"
+#include "common/str.h"
 
 namespace Common {
 
diff --git a/devtools/create_titanic/module.mk b/devtools/create_titanic/module.mk
index b86fe18a2e..1a9e7bbb14 100644
--- a/devtools/create_titanic/module.mk
+++ b/devtools/create_titanic/module.mk
@@ -12,6 +12,7 @@ MODULE_OBJS := \
 	script_ranges.o \
 	script_responses.o \
 	script_states.o \
+	base-str.o \
 	str.o \
 	tag_maps.o \
 	winexe.o \
diff --git a/devtools/create_titanic/str.cpp b/devtools/create_titanic/str.cpp
index 6aa66d0d20..5623f82718 100644
--- a/devtools/create_titanic/str.cpp
+++ b/devtools/create_titanic/str.cpp
@@ -1,786 +1,2 @@
-/* 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 2
- * 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, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "common/hash-str.h"
-#include "common/list.h"
-#include "memorypool.h"
-#include "common/str.h"
-#include "common/util.h"
-
-namespace Common {
-
-MemoryPool *g_refCountPool = 0; // FIXME: This is never freed right now
-
-static uint32 computeCapacity(uint32 len) {
-	// By default, for the capacity we use the next multiple of 32
-	return ((len + 32 - 1) & ~0x1F);
-}
-
-String::String(const char *str) : _size(0), _str(_storage) {
-	if (str == 0) {
-		_storage[0] = 0;
-		_size = 0;
-	} else
-		initWithCStr(str, strlen(str));
-}
-
-String::String(const char *str, uint32 len) : _size(0), _str(_storage) {
-	initWithCStr(str, len);
-}
-
-String::String(const char *beginP, const char *endP) : _size(0), _str(_storage) {
-	assert(endP >= beginP);
-	initWithCStr(beginP, endP - beginP);
-}
-
-void String::initWithCStr(const char *str, uint32 len) {
-	assert(str);
-
-	// Init _storage member explicitly (ie. without calling its constructor)
-	// for GCC 2.95.x compatibility (see also tracker item #1602879).
-	_storage[0] = 0;
-
-	_size = len;
-
-	if (len >= _builtinCapacity) {
-		// Not enough internal storage, so allocate more
-		_extern._capacity = computeCapacity(len+1);
-		_extern._refCount = 0;
-		_str = new char[_extern._capacity];
-		assert(_str != 0);
-	}
-
-	// Copy the string into the storage area
-	memmove(_str, str, len);
-	_str[len] = 0;
-}
-
-String::String(const String &str)
-    : _size(str._size) {
-	if (str.isStorageIntern()) {
-		// String in internal storage: just copy it
-		memcpy(_storage, str._storage, _builtinCapacity);
-		_str = _storage;
-	} else {
-		// String in external storage: use refcount mechanism
-		str.incRefCount();
-		_extern._refCount = str._extern._refCount;
-		_extern._capacity = str._extern._capacity;
-		_str = str._str;
-	}
-	assert(_str != 0);
-}
-
-String::String(char c)
-    : _size(0), _str(_storage) {
-
-	_storage[0] = c;
-	_storage[1] = 0;
-
-	_size = (c == 0) ? 0 : 1;
-}
-
-String::~String() {
-	decRefCount(_extern._refCount);
-}
-
-void String::makeUnique() {
-	ensureCapacity(_size, true);
-}
-
-/**
- * Ensure that enough storage is available to store at least new_size
- * characters plus a null byte. In addition, if we currently share
- * the storage with another string, unshare it, so that we can safely
- * write to the storage.
- */
-void String::ensureCapacity(uint32 new_size, bool keep_old) {
-	bool isShared;
-	uint32 curCapacity, newCapacity;
-	char *newStorage;
-	int *oldRefCount = _extern._refCount;
-
-	if (isStorageIntern()) {
-		isShared = false;
-		curCapacity = _builtinCapacity;
-	} else {
-		isShared = (oldRefCount && *oldRefCount > 1);
-		curCapacity = _extern._capacity;
-	}
-
-	// Special case: If there is enough space, and we do not share
-	// the storage, then there is nothing to do.
-	if (!isShared && new_size < curCapacity)
-		return;
-
-	if (isShared && new_size < _builtinCapacity) {
-		// We share the storage, but there is enough internal storage: Use that.
-		newStorage = _storage;
-		newCapacity = _builtinCapacity;
-	} else {
-		// We need to allocate storage on the heap!
-
-		// Compute a suitable new capacity limit
-		// If the current capacity is sufficient we use the same capacity
-		if (new_size < curCapacity)
-			newCapacity = curCapacity;
-		else
-			newCapacity = MAX(curCapacity * 2, computeCapacity(new_size+1));
-
-		// Allocate new storage
-		newStorage = new char[newCapacity];
-		assert(newStorage);
-	}
-
-	// Copy old data if needed, elsewise reset the new storage.
-	if (keep_old) {
-		assert(_size < newCapacity);
-		memcpy(newStorage, _str, _size + 1);
-	} else {
-		_size = 0;
-		newStorage[0] = 0;
-	}
-
-	// Release hold on the old storage ...
-	decRefCount(oldRefCount);
-
-	// ... in favor of the new storage
-	_str = newStorage;
-
-	if (!isStorageIntern()) {
-		// Set the ref count & capacity if we use an external storage.
-		// It is important to do this *after* copying any old content,
-		// else we would override data that has not yet been copied!
-		_extern._refCount = 0;
-		_extern._capacity = newCapacity;
-	}
-}
-
-void String::incRefCount() const {
-	assert(!isStorageIntern());
-	if (_extern._refCount == 0) {
-		if (g_refCountPool == 0) {
-			g_refCountPool = new MemoryPool(sizeof(int));
-			assert(g_refCountPool);
-		}
-
-		_extern._refCount = (int *)g_refCountPool->allocChunk();
-		*_extern._refCount = 2;
-	} else {
-		++(*_extern._refCount);
-	}
-}
-
-void String::decRefCount(int *oldRefCount) {
-	if (isStorageIntern())
-		return;
-
-	if (oldRefCount) {
-		--(*oldRefCount);
-	}
-	if (!oldRefCount || *oldRefCount <= 0) {
-		// The ref count reached zero, so we free the string storage
-		// and the ref count storage.
-		if (oldRefCount) {
-			assert(g_refCountPool);
-			g_refCountPool->freeChunk(oldRefCount);
-		}
-		delete[] _str;
-
-		// Even though _str points to a freed memory block now,
-		// we do not change its value, because any code that calls
-		// decRefCount will have to do this afterwards anyway.
-	}
-}
-
-String &String::operator=(const char *str) {
-	uint32 len = strlen(str);
-	ensureCapacity(len, false);
-	_size = len;
-	memmove(_str, str, len + 1);
-	return *this;
-}
-
-String &String::operator=(const String &str) {
-	if (&str == this)
-		return *this;
-
-	if (str.isStorageIntern()) {
-		decRefCount(_extern._refCount);
-		_size = str._size;
-		_str = _storage;
-		memcpy(_str, str._str, _size + 1);
-	} else {
-		str.incRefCount();
-		decRefCount(_extern._refCount);
-
-		_extern._refCount = str._extern._refCount;
-		_extern._capacity = str._extern._capacity;
-		_size = str._size;
-		_str = str._str;
-	}
-
-	return *this;
-}
-
-String &String::operator=(char c) {
-	decRefCount(_extern._refCount);
-	_str = _storage;
-
-	_str[0] = c;
-	_str[1] = 0;
-
-	_size = (c == 0) ? 0 : 1;
-	return *this;
-}
-
-String &String::operator+=(const char *str) {
-	if (_str <= str && str <= _str + _size)
-		return operator+=(String(str));
-
-	int len = strlen(str);
-	if (len > 0) {
-		ensureCapacity(_size + len, true);
-
-		memcpy(_str + _size, str, len + 1);
-		_size += len;
-	}
-	return *this;
-}
-
-String &String::operator+=(const String &str) {
-	if (&str == this)
-		return operator+=(String(str));
-
-	int len = str._size;
-	if (len > 0) {
-		ensureCapacity(_size + len, true);
-
-		memcpy(_str + _size, str._str, len + 1);
-		_size += len;
-	}
-	return *this;
-}
-
-String &String::operator+=(char c) {
-	ensureCapacity(_size + 1, true);
-
-	_str[_size++] = c;
-	_str[_size] = 0;
-
-	return *this;
-}
-
-bool String::hasPrefix(const String &x) const {
-	return hasPrefix(x.c_str());
-}
-
-bool String::hasPrefix(const char *x) const {
-	assert(x != 0);
-	// Compare x with the start of _str.
-	const char *y = c_str();
-	while (*x && *x == *y) {
-		++x;
-		++y;
-	}
-	// It's a prefix, if and only if all letters in x are 'used up' before
-	// _str ends.
-	return *x == 0;
-}
-
-bool String::hasSuffix(const String &x) const {
-	return hasSuffix(x.c_str());
-}
-
-bool String::hasSuffix(const char *x) const {
-	assert(x != 0);
-	// Compare x with the end of _str.
-	const uint32 x_size = strlen(x);
-	if (x_size > _size)
-		return false;
-	const char *y = c_str() + _size - x_size;
-	while (*x && *x == *y) {
-		++x;
-		++y;
-	}
-	// It's a suffix, if and only if all letters in x are 'used up' before
-	// _str ends.
-	return *x == 0;
-}
-
-bool String::contains(const String &x) const {
-	return strstr(c_str(), x.c_str()) != NULL;
-}
-
-bool String::contains(const char *x) const {
-	assert(x != 0);
-	return strstr(c_str(), x) != NULL;
-}
-
-bool String::contains(char x) const {
-	return strchr(c_str(), x) != NULL;
-}
-
-void String::deleteLastChar() {
-	if (_size > 0)
-		deleteChar(_size - 1);
-}
-
-void String::deleteChar(uint32 p) {
-	assert(p < _size);
-
-	makeUnique();
-	while (p++ < _size)
-		_str[p - 1] = _str[p];
-	_size--;
-}
-
-void String::erase(uint32 p, uint32 len) {
-	assert(p < _size);
-
-	makeUnique();
-	// If len == npos or p + len is over the end, remove all the way to the end
-	if (len == npos || p + len >= _size) {
-		// Delete char at p as well. So _size = (p - 1) + 1
-		_size = p;
-		// Null terminate
-		_str[_size] = 0;
-		return;
-	}
-
-	for ( ; p + len <= _size; p++) {
-		_str[p] = _str[p + len];
-	}
-	_size -= len;
-}
-
-void String::clear() {
-	decRefCount(_extern._refCount);
-
-	_size = 0;
-	_str = _storage;
-	_storage[0] = 0;
-}
-
-void String::setChar(char c, uint32 p) {
-	assert(p < _size);
-
-	makeUnique();
-	_str[p] = c;
-}
-
-void String::insertChar(char c, uint32 p) {
-	assert(p <= _size);
-
-	ensureCapacity(_size + 1, true);
-	_size++;
-	for (uint32 i = _size; i > p; --i)
-		_str[i] = _str[i - 1];
-	_str[p] = c;
-}
-
-void String::toLowercase() {
-	makeUnique();
-	for (uint32 i = 0; i < _size; ++i)
-		_str[i] = tolower(_str[i]);
-}
-
-void String::toUppercase() {
-	makeUnique();
-	for (uint32 i = 0; i < _size; ++i)
-		_str[i] = toupper(_str[i]);
-}
-
-uint String::hash() const {
-	return hashit(c_str());
-}
-
-// static
-String String::format(const char *fmt, ...) {
-	String output;
-
-	va_list va;
-	va_start(va, fmt);
-	output = String::vformat(fmt, va);
-	va_end(va);
-
-	return output;
-}
-
-// static
-String String::vformat(const char *fmt, va_list args) {
-	String output;
-	assert(output.isStorageIntern());
-
-	va_list va;
-	scumm_va_copy(va, args);
-	int len = vsnprintf(output._str, _builtinCapacity, fmt, va);
-	va_end(va);
-
-	if (len == -1 || len == _builtinCapacity - 1) {
-		// MSVC and IRIX don't return the size the full string would take up.
-		// MSVC returns -1, IRIX returns the number of characters actually written,
-		// which is at the most the size of the buffer minus one, as the string is
-		// truncated to fit.
-
-		// We assume MSVC failed to output the correct, null-terminated string
-		// if the return value is either -1 or size.
-		// For IRIX, because we lack a better mechanism, we assume failure
-		// if the return value equals size - 1.
-		// The downside to this is that whenever we try to format a string where the
-		// size is 1 below the built-in capacity, the size is needlessly increased.
-
-		// Try increasing the size of the string until it fits.
-		int size = _builtinCapacity;
-		do {
-			size *= 2;
-			output.ensureCapacity(size - 1, false);
-			assert(!output.isStorageIntern());
-			size = output._extern._capacity;
-
-			scumm_va_copy(va, args);
-			len = vsnprintf(output._str, size, fmt, va);
-			va_end(va);
-		} while (len == -1 || len >= size - 1);
-		output._size = len;
-	} else if (len < (int)_builtinCapacity) {
-		// vsnprintf succeeded
-		output._size = len;
-	} else {
-		// vsnprintf didn't have enough space, so grow buffer
-		output.ensureCapacity(len, false);
-		scumm_va_copy(va, args);
-		int len2 = vsnprintf(output._str, len+1, fmt, va);
-		va_end(va);
-		assert(len == len2);
-		output._size = len2;
-	}
-
-	return output;
-}
-
-
-#pragma mark -
-
-bool String::operator==(const String &x) const {
-	return equals(x);
-}
-
-bool String::operator==(const char *x) const {
-	assert(x != 0);
-	return equals(x);
-}
-
-bool String::operator!=(const String &x) const {
-	return !equals(x);
-}
-
-bool String::operator !=(const char *x) const {
-	assert(x != 0);
-	return !equals(x);
-}
-
-bool String::operator<(const String &x) const {
-	return compareTo(x) < 0;
-}
-
-bool String::operator<=(const String &x) const {
-	return compareTo(x) <= 0;
-}
-
-bool String::operator>(const String &x) const {
-	return compareTo(x) > 0;
-}
-
-bool String::operator>=(const String &x) const {
-	return compareTo(x) >= 0;
-}
-
-#pragma mark -
-
-bool operator==(const char* y, const String &x) {
-	return (x == y);
-}
-
-bool operator!=(const char* y, const String &x) {
-	return x != y;
-}
-
-#pragma mark -
-
-bool String::equals(const String &x) const {
-	return (0 == compareTo(x));
-}
-
-bool String::equals(const char *x) const {
-	assert(x != 0);
-	return (0 == compareTo(x));
-}
-
-bool String::equalsIgnoreCase(const String &x) const {
-	return (0 == compareToIgnoreCase(x));
-}
-
-bool String::equalsIgnoreCase(const char *x) const {
-	assert(x != 0);
-	return (0 == compareToIgnoreCase(x));
-}
-
-int String::compareTo(const String &x) const {
-	return compareTo(x.c_str());
-}
-
-int String::compareTo(const char *x) const {
-	assert(x != 0);
-	return strcmp(c_str(), x);
-}
-
-int String::compareToIgnoreCase(const String &x) const {
-	return compareToIgnoreCase(x.c_str());
-}
-
-int String::compareToIgnoreCase(const char *x) const {
-	assert(x != 0);
-	return scumm_stricmp(c_str(), x);
-}
-
-#pragma mark -
-
-String operator+(const String &x, const String &y) {
-	String temp(x);
-	temp += y;
-	return temp;
-}
-
-String operator+(const char *x, const String &y) {
-	String temp(x);
-	temp += y;
-	return temp;
-}
-
-String operator+(const String &x, const char *y) {
-	String temp(x);
-	temp += y;
-	return temp;
-}
-
-String operator+(char x, const String &y) {
-	String temp(x);
-	temp += y;
-	return temp;
-}
-
-String operator+(const String &x, char y) {
-	String temp(x);
-	temp += y;
-	return temp;
-}
-
-String lastPathComponent(const String &path, const char sep) {
-	const char *str = path.c_str();
-	const char *last = str + path.size();
-
-	// Skip over trailing slashes
-	while (last > str && *(last-1) == sep)
-		--last;
-
-	// Path consisted of only slashes -> return empty string
-	if (last == str)
-		return String();
-
-	// Now scan the whole component
-	const char *first = last - 1;
-	while (first > str && *first != sep)
-		--first;
-
-	if (*first == sep)
-		first++;
-
-	return String(first, last);
-}
-
-String normalizePath(const String &path, const char sep) {
-	if (path.empty())
-		return path;
-
-	const char *cur = path.c_str();
-	String result;
-
-	// If there is a leading slash, preserve that:
-	if (*cur == sep) {
-		result += sep;
-		// Skip over multiple leading slashes, so "//" equals "/"
-		while (*cur == sep)
-			++cur;
-	}
-
-	// Scan for path components till the end of the String
-	List<String> comps;
-	while (*cur != 0) {
-		const char *start = cur;
-
-		// Scan till the next path separator resp. the end of the string
-		while (*cur != sep && *cur != 0)
-			cur++;
-
-		const String component(start, cur);
-
-		if (component.empty() || component == ".") {
-			// Skip empty components and dot components
-		} else if (!comps.empty() && component == ".." && comps.back() != "..") {
-			// If stack is non-empty and top is not "..", remove top
-			comps.pop_back();
-		} else {
-			// Add the component to the stack
-			comps.push_back(component);
-		}
-
-		// Skip over separator chars
-		while (*cur == sep)
-			cur++;
-	}
-
-	// Finally, assemble all components back into a path
-	while (!comps.empty()) {
-		result += comps.front();
-		comps.pop_front();
-		if (!comps.empty())
-			result += sep;
-	}
-
-	return result;
-}
-
-size_t strlcpy(char *dst, const char *src, size_t size) {
-	// Our backup of the source's start, we need this
-	// to calculate the source's length.
-	const char * const srcStart = src;
-
-	// In case a non-empty size was specified we
-	// copy over (size - 1) bytes at max.
-	if (size != 0) {
-		// Copy over (size - 1) bytes at max.
-		while (--size != 0) {
-			if ((*dst++ = *src) == 0)
-				break;
-			++src;
-		}
-
-		// In case the source string was longer than the
-		// destination, we need to add a terminating
-		// zero.
-		if (size == 0)
-			*dst = 0;
-	}
-
-	// Move to the terminating zero of the source
-	// string, we need this to determine the length
-	// of the source string.
-	while (*src)
-		++src;
-
-	// Return the source string's length.
-	return src - srcStart;
-}
-
-size_t strlcat(char *dst, const char *src, size_t size) {
-	// In case the destination buffer does not contain
-	// space for at least 1 character, we will just
-	// return the source string's length.
-	if (size == 0)
-		return strlen(src);
-
-	// Our backup of the source's start, we need this
-	// to calculate the source's length.
-	const char * const srcStart = src;
-
-	// Our backup of the destination's start, we need
-	// this to calculate the destination's length.
-	const char * const dstStart = dst;
-
-	// Search the end of the destination, but do not
-	// move past the terminating zero.
-	while (size-- != 0 && *dst != 0)
-		++dst;
-
-	// Calculate the destination's length;
-	const size_t dstLength = dst - dstStart;
-
-	// In case we reached the end of the destination
-	// buffer before we had a chance to append any
-	// characters we will just return the destination
-	// length plus the source string's length.
-	if (size == 0)
-		return dstLength + strlen(srcStart);
-
-	// Copy over all of the source that fits
-	// the destination buffer. We also need
-	// to take the terminating zero we will
-	// add into consideration.
-	while (size-- != 0 && *src != 0)
-		*dst++ = *src++;
-	*dst = 0;
-
-	// Move to the terminating zero of the source
-	// string, we need this to determine the length
-	// of the source string.
-	while (*src)
-		++src;
-
-	// Return the total length of the result string
-	return dstLength + (src - srcStart);
-}
-
-} // End of namespace Common
-
-// Portable implementation of stricmp / strcasecmp / strcmpi.
-// TODO: Rename this to Common::strcasecmp
-int scumm_stricmp(const char *s1, const char *s2) {
-	byte l1, l2;
-	do {
-		// Don't use ++ inside tolower, in case the macro uses its
-		// arguments more than once.
-		l1 = (byte)*s1++;
-		l1 = tolower(l1);
-		l2 = (byte)*s2++;
-		l2 = tolower(l2);
-	} while (l1 == l2 && l1 != 0);
-	return l1 - l2;
-}
-
-// Portable implementation of strnicmp / strncasecmp / strncmpi.
-// TODO: Rename this to Common::strncasecmp
-int scumm_strnicmp(const char *s1, const char *s2, uint n) {
-	byte l1, l2;
-	do {
-		if (n-- == 0)
-			return 0;	// no difference found so far -> signal equality
-
-		// Don't use ++ inside tolower, in case the macro uses its
-		// arguments more than once.
-		l1 = (byte)*s1++;
-		l1 = tolower(l1);
-		l2 = (byte)*s2++;
-		l2 = tolower(l2);
-	} while (l1 == l2 && l1 != 0);
-	return l1 - l2;
-}
+#define SCUMMVM_UTIL
+#include "../common/str.cpp"
diff --git a/devtools/create_titanic/str.h b/devtools/create_titanic/str.h
deleted file mode 100644
index 2f954dcfca..0000000000
--- a/devtools/create_titanic/str.h
+++ /dev/null
@@ -1,386 +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 2
- * 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, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef COMMON_STRING_H
-#define COMMON_STRING_H
-
-#include "common/scummsys.h"
-
-#include <stdarg.h>
-
-namespace Common {
-
-/**
- * Simple string class for ScummVM. Provides automatic storage managment,
- * and overloads several operators in a 'natural' fashion, mimicking
- * the std::string class. Even provides simple iterators.
- *
- * This class tries to avoid allocating lots of small blocks on the heap,
- * since that is inefficient on several platforms supported by ScummVM.
- * Instead, small strings are stored 'inside' the string object (i.e. on
- * the stack, for stack allocated objects), and only for strings exceeding
- * a certain length do we allocate a buffer on the heap.
- *
- * The presence of \0 characters in the string will cause undefined
- * behavior in some operations.
- */
-class String {
-public:
-	static const uint32 npos = 0xFFFFFFFF;
-protected:
-	/**
-	 * The size of the internal storage. Increasing this means less heap
-	 * allocations are needed, at the cost of more stack memory usage,
-	 * and of course lots of wasted memory. Empirically, 90% or more of
-	 * all String instances are less than 32 chars long. If a platform
-	 * is very short on stack space, it would be possible to lower this.
-	 * A value of 24 still seems acceptable, though considerably worse,
-	 * while 16 seems to be the lowest you want to go... Anything lower
-	 * than 8 makes no sense, since that's the size of member _extern
-	 * (on 32 bit machines; 12 bytes on systems with 64bit pointers).
-	 */
-	static const uint32 _builtinCapacity = 32 - sizeof(uint32) - sizeof(char *);
-
-	/**
-	 * Length of the string. Stored to avoid having to call strlen
-	 * a lot. Yes, we limit ourselves to strings shorter than 4GB --
-	 * on purpose :-).
-	 */
-	uint32 _size;
-
-	/**
-	 * Pointer to the actual string storage. Either points to _storage,
-	 * or to a block allocated on the heap via malloc.
-	 */
-	char  *_str;
-
-
-	union {
-		/**
-		 * Internal string storage.
-		 */
-		char _storage[_builtinCapacity];
-		/**
-		 * External string storage data -- the refcounter, and the
-		 * capacity of the string _str points to.
-		 */
-		struct {
-			mutable int *_refCount;
-			uint32       _capacity;
-		} _extern;
-	};
-
-	inline bool isStorageIntern() const {
-		return _str == _storage;
-	}
-
-public:
-	/** Construct a new empty string. */
-	String() : _size(0), _str(_storage) { _storage[0] = 0; }
-
-	/** Construct a new string from the given NULL-terminated C string. */
-	String(const char *str);
-
-	/** Construct a new string containing exactly len characters read from address str. */
-	String(const char *str, uint32 len);
-
-	/** Construct a new string containing the characters between beginP (including) and endP (excluding). */
-	String(const char *beginP, const char *endP);
-
-	/** Construct a copy of the given string. */
-	String(const String &str);
-
-	/** Construct a string consisting of the given character. */
-	explicit String(char c);
-
-	~String();
-
-	String &operator=(const char *str);
-	String &operator=(const String &str);
-	String &operator=(char c);
-	String &operator+=(const char *str);
-	String &operator+=(const String &str);
-	String &operator+=(char c);
-
-	bool operator==(const String &x) const;
-	bool operator==(const char *x) const;
-	bool operator!=(const String &x) const;
-	bool operator!=(const char *x) const;
-
-	bool operator<(const String &x) const;
-	bool operator<=(const String &x) const;
-	bool operator>(const String &x) const;
-	bool operator>=(const String &x) const;
-
-	bool equals(const String &x) const;
-	bool equalsIgnoreCase(const String &x) const;
-	int compareTo(const String &x) const;           // strcmp clone
-	int compareToIgnoreCase(const String &x) const; // stricmp clone
-
-	bool equals(const char *x) const;
-	bool equalsIgnoreCase(const char *x) const;
-	int compareTo(const char *x) const;             // strcmp clone
-	int compareToIgnoreCase(const char *x) const;   // stricmp clone
-
-	bool hasSuffix(const String &x) const;
-	bool hasSuffix(const char *x) const;
-
-	bool hasPrefix(const String &x) const;
-	bool hasPrefix(const char *x) const;
-
-	bool contains(const String &x) const;
-	bool contains(const char *x) const;
-	bool contains(char x) const;
-
-	inline const char *c_str() const { return _str; }
-	inline uint size() const         { return _size; }
-
-	inline bool empty() const { return (_size == 0); }
-	char firstChar() const    { return (_size > 0) ? _str[0] : 0; }
-	char lastChar() const     { return (_size > 0) ? _str[_size - 1] : 0; }
-
-	char operator[](int idx) const {
-		assert(_str && idx >= 0 && idx < (int)_size);
-		return _str[idx];
-	}
-
-	/** Remove the last character from the string. */
-	void deleteLastChar();
-
-	/** Remove the character at position p from the string. */
-	void deleteChar(uint32 p);
-
-	/** Remove all characters from position p to the p + len. If len = String::npos, removes all characters to the end */
-	void erase(uint32 p, uint32 len = npos);
-
-	/** Set character c at position p, replacing the previous character there. */
-	void setChar(char c, uint32 p);
-
-	/** Insert character c before position p. */
-	void insertChar(char c, uint32 p);
-
-	/** Clears the string, making it empty. */
-	void clear();
-
-	/** Convert all characters in the string to lowercase. */
-	void toLowercase();
-
-	/** Convert all characters in the string to uppercase. */
-	void toUppercase();
-
-	/**
-	 * Removes trailing and leading whitespaces. Uses isspace() to decide
-	 * what is whitespace and what not.
-	 */
-	void trim();
-
-	uint hash() const;
-
-	/**
-	 * Print formatted data into a String object. Similar to sprintf,
-	 * except that it stores the result in (variably sized) String
-	 * instead of a fixed size buffer.
-	 */
-	static String format(const char *fmt, ...) GCC_PRINTF(1,2);
-
-	/**
-	 * Print formatted data into a String object. Similar to vsprintf,
-	 * except that it stores the result in (variably sized) String
-	 * instead of a fixed size buffer.
-	 */
-	static String vformat(const char *fmt, va_list args);
-
-public:
-	typedef char          value_type;
-	/**
-	 * Unsigned version of the underlying type. This can be used to cast
-	 * individual string characters to bigger integer types without sign
-	 * extension happening.
-	 */
-	typedef unsigned char unsigned_type;
-	typedef char *        iterator;
-	typedef const char *  const_iterator;
-
-	iterator begin() {
-		// Since the user could potentially
-		// change the string via the returned
-		// iterator we have to assure we are
-		// pointing to a unique storage.
-		makeUnique();
-
-		return _str;
-	}
-
-	iterator end() {
-		return begin() + size();
-	}
-
-	const_iterator begin() const {
-		return _str;
-	}
-
-	const_iterator end() const {
-		return begin() + size();
-	}
-
-protected:
-	void makeUnique();
-	void ensureCapacity(uint32 new_size, bool keep_old);
-	void incRefCount() const;
-	void decRefCount(int *oldRefCount);
-	void initWithCStr(const char *str, uint32 len);
-};
-
-// Append two strings to form a new (temp) string
-String operator+(const String &x, const String &y);
-
-String operator+(const char *x, const String &y);
-String operator+(const String &x, const char *y);
-
-String operator+(const String &x, char y);
-String operator+(char x, const String &y);
-
-// Some useful additional comparison operators for Strings
-bool operator==(const char *x, const String &y);
-bool operator!=(const char *x, const String &y);
-
-// Utility functions to remove leading and trailing whitespaces
-extern char *ltrim(char *t);
-extern char *rtrim(char *t);
-extern char *trim(char *t);
-
-
-/**
- * Returns the last component of a given path.
- *
- * Examples:
- *          /foo/bar.txt    would return 'bar.txt'
- *          /foo/bar/       would return 'bar'
- *          /foo/./bar//    would return 'bar'
- *
- * @param path the path of which we want to know the last component
- * @param sep character used to separate path components
- * @return The last component of the path.
- */
-String lastPathComponent(const String &path, const char sep);
-
-/**
- * Normalize a given path to a canonical form. In particular:
- * - trailing separators are removed:  /foo/bar/ -> /foo/bar
- * - double separators (= empty components) are removed:   /foo//bar -> /foo/bar
- * - dot components are removed:  /foo/./bar -> /foo/bar
- *
- * @todo remove double dot components:  /foo/baz/../bar -> /foo/bar
- *
- * @param path  the path to normalize
- * @param sep   the separator token (usually '/' on Unix-style systems, or '\\' on Windows based stuff)
- * @return      the normalized path
- */
-String normalizePath(const String &path, const char sep);
-
-
-/**
- * Simple DOS-style pattern matching function (understands * and ? like used in DOS).
- * Taken from exult/files/listfiles.cc
- *
- * Token meaning:
- *      "*": any character, any amount of times.
- *      "?": any character, only once.
- *      "#": any decimal digit, only once.
- *
- * Example strings/patterns:
- *      String: monkey.s01   Pattern: monkey.s??    => true
- *      String: monkey.s101  Pattern: monkey.s??    => false
- *      String: monkey.s99   Pattern: monkey.s?1    => false
- *      String: monkey.s101  Pattern: monkey.s*     => true
- *      String: monkey.s99   Pattern: monkey.s*1    => false
- *      String: monkey.s01   Pattern: monkey.s##    => true
- *      String: monkey.s01   Pattern: monkey.###    => false
- *
- * @param str Text to be matched against the given pattern.
- * @param pat Glob pattern.
- * @param ignoreCase Whether to ignore the case when doing pattern match
- * @param pathMode Whether to use path mode, i.e., whether slashes must be matched explicitly.
- *
- * @return true if str matches the pattern, false otherwise.
- */
-bool matchString(const char *str, const char *pat, bool ignoreCase = false, bool pathMode = false);
-
-
-/**
- * Take a 32 bit value and turn it into a four character string, where each of
- * the four bytes is turned into one character. Most significant byte is printed
- * first.
- */
-String tag2string(uint32 tag);
-
-/**
- * Copy up to size - 1 characters from src to dst and also zero terminate the
- * result. Note that src must be a zero terminated string.
- *
- * In case size is zero this function just returns the length of the source
- * string.
- *
- * @note This is modeled after OpenBSD's strlcpy. See the manpage here:
- *       http://www.openbsd.org/cgi-bin/man.cgi?query=strlcpy
- *
- * @param dst The destination buffer.
- * @param src The source string.
- * @param size The size of the destination buffer.
- * @return The length of the (non-truncated) result, i.e. strlen(src).
- */
-size_t strlcpy(char *dst, const char *src, size_t size);
-
-/**
- * Append the string src to the string dst. Note that both src and dst must be
- * zero terminated. The result will be zero terminated. At most
- * "size - strlen(dst) - 1" bytes will be appended.
- *
- * In case the dst string does not contain a zero within the first "size" bytes
- * the dst string will not be changed and size + strlen(src) is returned.
- *
- * @note This is modeled after OpenBSD's strlcat. See the manpage here:
- *       http://www.openbsd.org/cgi-bin/man.cgi?query=strlcat
- *
- * @param dst The string the source string should be appended to.
- * @param src The source string.
- * @param size The (total) size of the destination buffer.
- * @return The length of the (non-truncated) result. That is
- *         strlen(dst) + strlen(src). In case strlen(dst) > size
- *         size + strlen(src) is returned.
- */
-size_t strlcat(char *dst, const char *src, size_t size);
-
-/**
- * Convenience wrapper for tag2string which "returns" a C string.
- * Note: It is *NOT* safe to do anything with the return value other than directly
- * copying or printing it.
- */
-#define tag2str(x)	Common::tag2string(x).c_str()
-
-
-} // End of namespace Common
-
-extern int scumm_stricmp(const char *s1, const char *s2);
-extern int scumm_strnicmp(const char *s1, const char *s2, uint n);
-
-#endif
diff --git a/devtools/create_titanic/winexe.cpp b/devtools/create_titanic/winexe.cpp
index 49be23dbd4..6371598d57 100644
--- a/devtools/create_titanic/winexe.cpp
+++ b/devtools/create_titanic/winexe.cpp
@@ -22,7 +22,7 @@
 
 #define FORBIDDEN_SYMBOL_ALLOW_ALL
 
-#include "str.h"
+#include "common/str.h"
 #include "winexe.h"
 
 namespace Common {
diff --git a/devtools/create_titanic/winexe.h b/devtools/create_titanic/winexe.h
index 6bfe2a25a0..e5d7fb84cc 100644
--- a/devtools/create_titanic/winexe.h
+++ b/devtools/create_titanic/winexe.h
@@ -25,7 +25,7 @@
 
 #include "file.h"
 #include "hash-str.h"
-#include "str.h"
+#include "common/str.h"
 
 namespace Common {
 
diff --git a/devtools/create_titanic/winexe_pe.cpp b/devtools/create_titanic/winexe_pe.cpp
index f55740f692..6d38261787 100644
--- a/devtools/create_titanic/winexe_pe.cpp
+++ b/devtools/create_titanic/winexe_pe.cpp
@@ -23,7 +23,7 @@
 #define FORBIDDEN_SYMBOL_ALLOW_ALL
 
 #include "file.h"
-#include "str.h"
+#include "common/str.h"
 #include "winexe_pe.h"
 #include "common/array.h"
 #include "common/endian.h"
diff --git a/devtools/create_titanic/winexe_pe.h b/devtools/create_titanic/winexe_pe.h
index 6ab7ae847a..dd2603750e 100644
--- a/devtools/create_titanic/winexe_pe.h
+++ b/devtools/create_titanic/winexe_pe.h
@@ -26,7 +26,7 @@
 #include "file.h"
 #include "hash-str.h"
 #include "hashmap.h"
-#include "str.h"
+#include "common/str.h"
 #include "winexe.h"
 
 namespace Common {
diff --git a/devtools/create_xeen/base-str.cpp b/devtools/create_xeen/base-str.cpp
new file mode 100644
index 0000000000..f5ebe1ad3b
--- /dev/null
+++ b/devtools/create_xeen/base-str.cpp
@@ -0,0 +1,2 @@
+#define SCUMMVM_UTIL 1
+#include "../common/base-str.cpp"
diff --git a/devtools/create_xeen/module.mk b/devtools/create_xeen/module.mk
index eabd739b4f..0714fbceb6 100644
--- a/devtools/create_xeen/module.mk
+++ b/devtools/create_xeen/module.mk
@@ -9,6 +9,7 @@ MODULE_OBJS := \
 	map.o \
 	memorypool.o \
 	str.o \
+	base-str.o \
 	swords.o
 
 # Set the name of the executable
diff --git a/devtools/create_xeen/str.cpp b/devtools/create_xeen/str.cpp
index 6aa66d0d20..5623f82718 100644
--- a/devtools/create_xeen/str.cpp
+++ b/devtools/create_xeen/str.cpp
@@ -1,786 +1,2 @@
-/* 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 2
- * 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, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "common/hash-str.h"
-#include "common/list.h"
-#include "memorypool.h"
-#include "common/str.h"
-#include "common/util.h"
-
-namespace Common {
-
-MemoryPool *g_refCountPool = 0; // FIXME: This is never freed right now
-
-static uint32 computeCapacity(uint32 len) {
-	// By default, for the capacity we use the next multiple of 32
-	return ((len + 32 - 1) & ~0x1F);
-}
-
-String::String(const char *str) : _size(0), _str(_storage) {
-	if (str == 0) {
-		_storage[0] = 0;
-		_size = 0;
-	} else
-		initWithCStr(str, strlen(str));
-}
-
-String::String(const char *str, uint32 len) : _size(0), _str(_storage) {
-	initWithCStr(str, len);
-}
-
-String::String(const char *beginP, const char *endP) : _size(0), _str(_storage) {
-	assert(endP >= beginP);
-	initWithCStr(beginP, endP - beginP);
-}
-
-void String::initWithCStr(const char *str, uint32 len) {
-	assert(str);
-
-	// Init _storage member explicitly (ie. without calling its constructor)
-	// for GCC 2.95.x compatibility (see also tracker item #1602879).
-	_storage[0] = 0;
-
-	_size = len;
-
-	if (len >= _builtinCapacity) {
-		// Not enough internal storage, so allocate more
-		_extern._capacity = computeCapacity(len+1);
-		_extern._refCount = 0;
-		_str = new char[_extern._capacity];
-		assert(_str != 0);
-	}
-
-	// Copy the string into the storage area
-	memmove(_str, str, len);
-	_str[len] = 0;
-}
-
-String::String(const String &str)
-    : _size(str._size) {
-	if (str.isStorageIntern()) {
-		// String in internal storage: just copy it
-		memcpy(_storage, str._storage, _builtinCapacity);
-		_str = _storage;
-	} else {
-		// String in external storage: use refcount mechanism
-		str.incRefCount();
-		_extern._refCount = str._extern._refCount;
-		_extern._capacity = str._extern._capacity;
-		_str = str._str;
-	}
-	assert(_str != 0);
-}
-
-String::String(char c)
-    : _size(0), _str(_storage) {
-
-	_storage[0] = c;
-	_storage[1] = 0;
-
-	_size = (c == 0) ? 0 : 1;
-}
-
-String::~String() {
-	decRefCount(_extern._refCount);
-}
-
-void String::makeUnique() {
-	ensureCapacity(_size, true);
-}
-
-/**
- * Ensure that enough storage is available to store at least new_size
- * characters plus a null byte. In addition, if we currently share
- * the storage with another string, unshare it, so that we can safely
- * write to the storage.
- */
-void String::ensureCapacity(uint32 new_size, bool keep_old) {
-	bool isShared;
-	uint32 curCapacity, newCapacity;
-	char *newStorage;
-	int *oldRefCount = _extern._refCount;
-
-	if (isStorageIntern()) {
-		isShared = false;
-		curCapacity = _builtinCapacity;
-	} else {
-		isShared = (oldRefCount && *oldRefCount > 1);
-		curCapacity = _extern._capacity;
-	}
-
-	// Special case: If there is enough space, and we do not share
-	// the storage, then there is nothing to do.
-	if (!isShared && new_size < curCapacity)
-		return;
-
-	if (isShared && new_size < _builtinCapacity) {
-		// We share the storage, but there is enough internal storage: Use that.
-		newStorage = _storage;
-		newCapacity = _builtinCapacity;
-	} else {
-		// We need to allocate storage on the heap!
-
-		// Compute a suitable new capacity limit
-		// If the current capacity is sufficient we use the same capacity
-		if (new_size < curCapacity)
-			newCapacity = curCapacity;
-		else
-			newCapacity = MAX(curCapacity * 2, computeCapacity(new_size+1));
-
-		// Allocate new storage
-		newStorage = new char[newCapacity];
-		assert(newStorage);
-	}
-
-	// Copy old data if needed, elsewise reset the new storage.
-	if (keep_old) {
-		assert(_size < newCapacity);
-		memcpy(newStorage, _str, _size + 1);
-	} else {
-		_size = 0;
-		newStorage[0] = 0;
-	}
-
-	// Release hold on the old storage ...
-	decRefCount(oldRefCount);
-
-	// ... in favor of the new storage
-	_str = newStorage;
-
-	if (!isStorageIntern()) {
-		// Set the ref count & capacity if we use an external storage.
-		// It is important to do this *after* copying any old content,
-		// else we would override data that has not yet been copied!
-		_extern._refCount = 0;
-		_extern._capacity = newCapacity;
-	}
-}
-
-void String::incRefCount() const {
-	assert(!isStorageIntern());
-	if (_extern._refCount == 0) {
-		if (g_refCountPool == 0) {
-			g_refCountPool = new MemoryPool(sizeof(int));
-			assert(g_refCountPool);
-		}
-
-		_extern._refCount = (int *)g_refCountPool->allocChunk();
-		*_extern._refCount = 2;
-	} else {
-		++(*_extern._refCount);
-	}
-}
-
-void String::decRefCount(int *oldRefCount) {
-	if (isStorageIntern())
-		return;
-
-	if (oldRefCount) {
-		--(*oldRefCount);
-	}
-	if (!oldRefCount || *oldRefCount <= 0) {
-		// The ref count reached zero, so we free the string storage
-		// and the ref count storage.
-		if (oldRefCount) {
-			assert(g_refCountPool);
-			g_refCountPool->freeChunk(oldRefCount);
-		}
-		delete[] _str;
-
-		// Even though _str points to a freed memory block now,
-		// we do not change its value, because any code that calls
-		// decRefCount will have to do this afterwards anyway.
-	}
-}
-
-String &String::operator=(const char *str) {
-	uint32 len = strlen(str);
-	ensureCapacity(len, false);
-	_size = len;
-	memmove(_str, str, len + 1);
-	return *this;
-}
-
-String &String::operator=(const String &str) {
-	if (&str == this)
-		return *this;
-
-	if (str.isStorageIntern()) {
-		decRefCount(_extern._refCount);
-		_size = str._size;
-		_str = _storage;
-		memcpy(_str, str._str, _size + 1);
-	} else {
-		str.incRefCount();
-		decRefCount(_extern._refCount);
-
-		_extern._refCount = str._extern._refCount;
-		_extern._capacity = str._extern._capacity;
-		_size = str._size;
-		_str = str._str;
-	}
-
-	return *this;
-}
-
-String &String::operator=(char c) {
-	decRefCount(_extern._refCount);
-	_str = _storage;
-
-	_str[0] = c;
-	_str[1] = 0;
-
-	_size = (c == 0) ? 0 : 1;
-	return *this;
-}
-
-String &String::operator+=(const char *str) {
-	if (_str <= str && str <= _str + _size)
-		return operator+=(String(str));
-
-	int len = strlen(str);
-	if (len > 0) {
-		ensureCapacity(_size + len, true);
-
-		memcpy(_str + _size, str, len + 1);
-		_size += len;
-	}
-	return *this;
-}
-
-String &String::operator+=(const String &str) {
-	if (&str == this)
-		return operator+=(String(str));
-
-	int len = str._size;
-	if (len > 0) {
-		ensureCapacity(_size + len, true);
-
-		memcpy(_str + _size, str._str, len + 1);
-		_size += len;
-	}
-	return *this;
-}
-
-String &String::operator+=(char c) {
-	ensureCapacity(_size + 1, true);
-
-	_str[_size++] = c;
-	_str[_size] = 0;
-
-	return *this;
-}
-
-bool String::hasPrefix(const String &x) const {
-	return hasPrefix(x.c_str());
-}
-
-bool String::hasPrefix(const char *x) const {
-	assert(x != 0);
-	// Compare x with the start of _str.
-	const char *y = c_str();
-	while (*x && *x == *y) {
-		++x;
-		++y;
-	}
-	// It's a prefix, if and only if all letters in x are 'used up' before
-	// _str ends.
-	return *x == 0;
-}
-
-bool String::hasSuffix(const String &x) const {
-	return hasSuffix(x.c_str());
-}
-
-bool String::hasSuffix(const char *x) const {
-	assert(x != 0);
-	// Compare x with the end of _str.
-	const uint32 x_size = strlen(x);
-	if (x_size > _size)
-		return false;
-	const char *y = c_str() + _size - x_size;
-	while (*x && *x == *y) {
-		++x;
-		++y;
-	}
-	// It's a suffix, if and only if all letters in x are 'used up' before
-	// _str ends.
-	return *x == 0;
-}
-
-bool String::contains(const String &x) const {
-	return strstr(c_str(), x.c_str()) != NULL;
-}
-
-bool String::contains(const char *x) const {
-	assert(x != 0);
-	return strstr(c_str(), x) != NULL;
-}
-
-bool String::contains(char x) const {
-	return strchr(c_str(), x) != NULL;
-}
-
-void String::deleteLastChar() {
-	if (_size > 0)
-		deleteChar(_size - 1);
-}
-
-void String::deleteChar(uint32 p) {
-	assert(p < _size);
-
-	makeUnique();
-	while (p++ < _size)
-		_str[p - 1] = _str[p];
-	_size--;
-}
-
-void String::erase(uint32 p, uint32 len) {
-	assert(p < _size);
-
-	makeUnique();
-	// If len == npos or p + len is over the end, remove all the way to the end
-	if (len == npos || p + len >= _size) {
-		// Delete char at p as well. So _size = (p - 1) + 1
-		_size = p;
-		// Null terminate
-		_str[_size] = 0;
-		return;
-	}
-
-	for ( ; p + len <= _size; p++) {
-		_str[p] = _str[p + len];
-	}
-	_size -= len;
-}
-
-void String::clear() {
-	decRefCount(_extern._refCount);
-
-	_size = 0;
-	_str = _storage;
-	_storage[0] = 0;
-}
-
-void String::setChar(char c, uint32 p) {
-	assert(p < _size);
-
-	makeUnique();
-	_str[p] = c;
-}
-
-void String::insertChar(char c, uint32 p) {
-	assert(p <= _size);
-
-	ensureCapacity(_size + 1, true);
-	_size++;
-	for (uint32 i = _size; i > p; --i)
-		_str[i] = _str[i - 1];
-	_str[p] = c;
-}
-
-void String::toLowercase() {
-	makeUnique();
-	for (uint32 i = 0; i < _size; ++i)
-		_str[i] = tolower(_str[i]);
-}
-
-void String::toUppercase() {
-	makeUnique();
-	for (uint32 i = 0; i < _size; ++i)
-		_str[i] = toupper(_str[i]);
-}
-
-uint String::hash() const {
-	return hashit(c_str());
-}
-
-// static
-String String::format(const char *fmt, ...) {
-	String output;
-
-	va_list va;
-	va_start(va, fmt);
-	output = String::vformat(fmt, va);
-	va_end(va);
-
-	return output;
-}
-
-// static
-String String::vformat(const char *fmt, va_list args) {
-	String output;
-	assert(output.isStorageIntern());
-
-	va_list va;
-	scumm_va_copy(va, args);
-	int len = vsnprintf(output._str, _builtinCapacity, fmt, va);
-	va_end(va);
-
-	if (len == -1 || len == _builtinCapacity - 1) {
-		// MSVC and IRIX don't return the size the full string would take up.
-		// MSVC returns -1, IRIX returns the number of characters actually written,
-		// which is at the most the size of the buffer minus one, as the string is
-		// truncated to fit.
-
-		// We assume MSVC failed to output the correct, null-terminated string
-		// if the return value is either -1 or size.
-		// For IRIX, because we lack a better mechanism, we assume failure
-		// if the return value equals size - 1.
-		// The downside to this is that whenever we try to format a string where the
-		// size is 1 below the built-in capacity, the size is needlessly increased.
-
-		// Try increasing the size of the string until it fits.
-		int size = _builtinCapacity;
-		do {
-			size *= 2;
-			output.ensureCapacity(size - 1, false);
-			assert(!output.isStorageIntern());
-			size = output._extern._capacity;
-
-			scumm_va_copy(va, args);
-			len = vsnprintf(output._str, size, fmt, va);
-			va_end(va);
-		} while (len == -1 || len >= size - 1);
-		output._size = len;
-	} else if (len < (int)_builtinCapacity) {
-		// vsnprintf succeeded
-		output._size = len;
-	} else {
-		// vsnprintf didn't have enough space, so grow buffer
-		output.ensureCapacity(len, false);
-		scumm_va_copy(va, args);
-		int len2 = vsnprintf(output._str, len+1, fmt, va);
-		va_end(va);
-		assert(len == len2);
-		output._size = len2;
-	}
-
-	return output;
-}
-
-
-#pragma mark -
-
-bool String::operator==(const String &x) const {
-	return equals(x);
-}
-
-bool String::operator==(const char *x) const {
-	assert(x != 0);
-	return equals(x);
-}
-
-bool String::operator!=(const String &x) const {
-	return !equals(x);
-}
-
-bool String::operator !=(const char *x) const {
-	assert(x != 0);
-	return !equals(x);
-}
-
-bool String::operator<(const String &x) const {
-	return compareTo(x) < 0;
-}
-
-bool String::operator<=(const String &x) const {
-	return compareTo(x) <= 0;
-}
-
-bool String::operator>(const String &x) const {
-	return compareTo(x) > 0;
-}
-
-bool String::operator>=(const String &x) const {
-	return compareTo(x) >= 0;
-}
-
-#pragma mark -
-
-bool operator==(const char* y, const String &x) {
-	return (x == y);
-}
-
-bool operator!=(const char* y, const String &x) {
-	return x != y;
-}
-
-#pragma mark -
-
-bool String::equals(const String &x) const {
-	return (0 == compareTo(x));
-}
-
-bool String::equals(const char *x) const {
-	assert(x != 0);
-	return (0 == compareTo(x));
-}
-
-bool String::equalsIgnoreCase(const String &x) const {
-	return (0 == compareToIgnoreCase(x));
-}
-
-bool String::equalsIgnoreCase(const char *x) const {
-	assert(x != 0);
-	return (0 == compareToIgnoreCase(x));
-}
-
-int String::compareTo(const String &x) const {
-	return compareTo(x.c_str());
-}
-
-int String::compareTo(const char *x) const {
-	assert(x != 0);
-	return strcmp(c_str(), x);
-}
-
-int String::compareToIgnoreCase(const String &x) const {
-	return compareToIgnoreCase(x.c_str());
-}
-
-int String::compareToIgnoreCase(const char *x) const {
-	assert(x != 0);
-	return scumm_stricmp(c_str(), x);
-}
-
-#pragma mark -
-
-String operator+(const String &x, const String &y) {
-	String temp(x);
-	temp += y;
-	return temp;
-}
-
-String operator+(const char *x, const String &y) {
-	String temp(x);
-	temp += y;
-	return temp;
-}
-
-String operator+(const String &x, const char *y) {
-	String temp(x);
-	temp += y;
-	return temp;
-}
-
-String operator+(char x, const String &y) {
-	String temp(x);
-	temp += y;
-	return temp;
-}
-
-String operator+(const String &x, char y) {
-	String temp(x);
-	temp += y;
-	return temp;
-}
-
-String lastPathComponent(const String &path, const char sep) {
-	const char *str = path.c_str();
-	const char *last = str + path.size();
-
-	// Skip over trailing slashes
-	while (last > str && *(last-1) == sep)
-		--last;
-
-	// Path consisted of only slashes -> return empty string
-	if (last == str)
-		return String();
-
-	// Now scan the whole component
-	const char *first = last - 1;
-	while (first > str && *first != sep)
-		--first;
-
-	if (*first == sep)
-		first++;
-
-	return String(first, last);
-}
-
-String normalizePath(const String &path, const char sep) {
-	if (path.empty())
-		return path;
-
-	const char *cur = path.c_str();
-	String result;
-
-	// If there is a leading slash, preserve that:
-	if (*cur == sep) {
-		result += sep;
-		// Skip over multiple leading slashes, so "//" equals "/"
-		while (*cur == sep)
-			++cur;
-	}
-
-	// Scan for path components till the end of the String
-	List<String> comps;
-	while (*cur != 0) {
-		const char *start = cur;
-
-		// Scan till the next path separator resp. the end of the string
-		while (*cur != sep && *cur != 0)
-			cur++;
-
-		const String component(start, cur);
-
-		if (component.empty() || component == ".") {
-			// Skip empty components and dot components
-		} else if (!comps.empty() && component == ".." && comps.back() != "..") {
-			// If stack is non-empty and top is not "..", remove top
-			comps.pop_back();
-		} else {
-			// Add the component to the stack
-			comps.push_back(component);
-		}
-
-		// Skip over separator chars
-		while (*cur == sep)
-			cur++;
-	}
-
-	// Finally, assemble all components back into a path
-	while (!comps.empty()) {
-		result += comps.front();
-		comps.pop_front();
-		if (!comps.empty())
-			result += sep;
-	}
-
-	return result;
-}
-
-size_t strlcpy(char *dst, const char *src, size_t size) {
-	// Our backup of the source's start, we need this
-	// to calculate the source's length.
-	const char * const srcStart = src;
-
-	// In case a non-empty size was specified we
-	// copy over (size - 1) bytes at max.
-	if (size != 0) {
-		// Copy over (size - 1) bytes at max.
-		while (--size != 0) {
-			if ((*dst++ = *src) == 0)
-				break;
-			++src;
-		}
-
-		// In case the source string was longer than the
-		// destination, we need to add a terminating
-		// zero.
-		if (size == 0)
-			*dst = 0;
-	}
-
-	// Move to the terminating zero of the source
-	// string, we need this to determine the length
-	// of the source string.
-	while (*src)
-		++src;
-
-	// Return the source string's length.
-	return src - srcStart;
-}
-
-size_t strlcat(char *dst, const char *src, size_t size) {
-	// In case the destination buffer does not contain
-	// space for at least 1 character, we will just
-	// return the source string's length.
-	if (size == 0)
-		return strlen(src);
-
-	// Our backup of the source's start, we need this
-	// to calculate the source's length.
-	const char * const srcStart = src;
-
-	// Our backup of the destination's start, we need
-	// this to calculate the destination's length.
-	const char * const dstStart = dst;
-
-	// Search the end of the destination, but do not
-	// move past the terminating zero.
-	while (size-- != 0 && *dst != 0)
-		++dst;
-
-	// Calculate the destination's length;
-	const size_t dstLength = dst - dstStart;
-
-	// In case we reached the end of the destination
-	// buffer before we had a chance to append any
-	// characters we will just return the destination
-	// length plus the source string's length.
-	if (size == 0)
-		return dstLength + strlen(srcStart);
-
-	// Copy over all of the source that fits
-	// the destination buffer. We also need
-	// to take the terminating zero we will
-	// add into consideration.
-	while (size-- != 0 && *src != 0)
-		*dst++ = *src++;
-	*dst = 0;
-
-	// Move to the terminating zero of the source
-	// string, we need this to determine the length
-	// of the source string.
-	while (*src)
-		++src;
-
-	// Return the total length of the result string
-	return dstLength + (src - srcStart);
-}
-
-} // End of namespace Common
-
-// Portable implementation of stricmp / strcasecmp / strcmpi.
-// TODO: Rename this to Common::strcasecmp
-int scumm_stricmp(const char *s1, const char *s2) {
-	byte l1, l2;
-	do {
-		// Don't use ++ inside tolower, in case the macro uses its
-		// arguments more than once.
-		l1 = (byte)*s1++;
-		l1 = tolower(l1);
-		l2 = (byte)*s2++;
-		l2 = tolower(l2);
-	} while (l1 == l2 && l1 != 0);
-	return l1 - l2;
-}
-
-// Portable implementation of strnicmp / strncasecmp / strncmpi.
-// TODO: Rename this to Common::strncasecmp
-int scumm_strnicmp(const char *s1, const char *s2, uint n) {
-	byte l1, l2;
-	do {
-		if (n-- == 0)
-			return 0;	// no difference found so far -> signal equality
-
-		// Don't use ++ inside tolower, in case the macro uses its
-		// arguments more than once.
-		l1 = (byte)*s1++;
-		l1 = tolower(l1);
-		l2 = (byte)*s2++;
-		l2 = tolower(l2);
-	} while (l1 == l2 && l1 != 0);
-	return l1 - l2;
-}
+#define SCUMMVM_UTIL
+#include "../common/str.cpp"
diff --git a/engines/glk/adrift/os_glk.cpp b/engines/glk/adrift/os_glk.cpp
index 372fcd1f3f..b4b9e4c5e2 100644
--- a/engines/glk/adrift/os_glk.cpp
+++ b/engines/glk/adrift/os_glk.cpp
@@ -2811,7 +2811,7 @@ static int gsc_startup_code(Common::SeekableReadStream *game_stream, int restore
 		 * Display a brief loading game message; here we have to use a timeout
 		 * to ensure that the text is flushed to Glk.
 		 */
-		g_vm->glk_put_string_uni(_("Loading game...\n").c_str());
+		g_vm->glk_put_string_uni(_("Loading game...\n").u32_str());
 		if (g_vm->glk_gestalt(gestalt_Timer, 0)) {
 			event_t event;
 
diff --git a/engines/glk/advsys/glk_interface.cpp b/engines/glk/advsys/glk_interface.cpp
index e1f08d79cc..badfbefdb3 100644
--- a/engines/glk/advsys/glk_interface.cpp
+++ b/engines/glk/advsys/glk_interface.cpp
@@ -41,7 +41,7 @@ void GlkInterface::print(const Common::U32String &msg) {
 	// Don't print out text if loading a savegame directly from the launcher, since we don't
 	// want any of the intro text displayed by the startup code to show
 	if (_saveSlot == -1)
-		glk_put_string_stream_uni(glk_window_get_stream(_window), msg.c_str());
+		glk_put_string_stream_uni(glk_window_get_stream(_window), msg.u32_str());
 }
 
 void GlkInterface::print(int number) {
diff --git a/engines/glk/comprehend/comprehend.cpp b/engines/glk/comprehend/comprehend.cpp
index 0416908de5..7900202a76 100644
--- a/engines/glk/comprehend/comprehend.cpp
+++ b/engines/glk/comprehend/comprehend.cpp
@@ -139,7 +139,7 @@ void Comprehend::print(const Common::U32String fmt, ...) {
 	va_end(argp);
 
 	glk_put_string_stream_uni(glk_window_get_stream(_bottomWindow),
-	                          outputMsg.c_str());
+	                          outputMsg.u32_str());
 }
 
 void Comprehend::readLine(char *buffer, size_t maxLen) {
diff --git a/engines/glk/glk_api.cpp b/engines/glk/glk_api.cpp
index d9ea372d00..5398d1c8ef 100644
--- a/engines/glk/glk_api.cpp
+++ b/engines/glk/glk_api.cpp
@@ -60,7 +60,7 @@ GlkAPI::GlkAPI(OSystem *syst, const GlkGameDescription &gameDesc) :
 }
 
 void GlkAPI::glk_exit(void) {
-	glk_put_string_uni(_("[ press any key to exit ]").c_str());
+	glk_put_string_uni(_("[ press any key to exit ]").u32_str());
 	_events->waitForPress();
 
 	// Trigger a ScumMVM shutdown of game
diff --git a/engines/glk/scott/scott.cpp b/engines/glk/scott/scott.cpp
index e9c9fa58dc..5fdf2261a1 100644
--- a/engines/glk/scott/scott.cpp
+++ b/engines/glk/scott/scott.cpp
@@ -176,7 +176,7 @@ void Scott::display(winid_t w, const Common::U32String fmt, ...) {
 	Common::U32String::vformat(fmt.begin(), fmt.end(), msg, ap);
 	va_end(ap);
 
-	glk_put_string_stream_uni(glk_window_get_stream(w), msg.c_str());
+	glk_put_string_stream_uni(glk_window_get_stream(w), msg.u32_str());
 }
 
 void Scott::delay(int seconds) {
diff --git a/engines/glk/window_text_buffer.cpp b/engines/glk/window_text_buffer.cpp
index 19cfbb5040..870ea999b1 100644
--- a/engines/glk/window_text_buffer.cpp
+++ b/engines/glk/window_text_buffer.cpp
@@ -1304,7 +1304,7 @@ void TextBufferWindow::acceptReadLine(uint32 arg) {
 		if (_historyPos < 0)
 			_historyPos += HISTORYLEN;
 		s = _history[_historyPos];
-		putTextUni(s.c_str(), s.size(), _inFence, _numChars - _inFence);
+		putTextUni(s.u32_str(), s.size(), _inFence, _numChars - _inFence);
 		break;
 
 	case keycode_Down:
@@ -1314,7 +1314,7 @@ void TextBufferWindow::acceptReadLine(uint32 arg) {
 		if (_historyPos >= HISTORYLEN)
 			_historyPos -= HISTORYLEN;
 		s = _history[_historyPos];
-		putTextUni(s.c_str(), s.size(), _inFence, _numChars - _inFence);
+		putTextUni(s.u32_str(), s.size(), _inFence, _numChars - _inFence);
 		break;
 
 	// Cursor movement keys, during line input.
diff --git a/engines/glk/zcode/zcode.cpp b/engines/glk/zcode/zcode.cpp
index 179a4b99f0..d5a5520605 100644
--- a/engines/glk/zcode/zcode.cpp
+++ b/engines/glk/zcode/zcode.cpp
@@ -142,7 +142,7 @@ Common::Error ZCode::saveGameState(int slot, const Common::String &desc, bool is
 	file->close();
 
 	if (!success)
-		print_string_uni(_("Error writing save file\n").c_str());
+		print_string_uni(_("Error writing save file\n").u32_str());
 
 	return Common::kNoError;
 
diff --git a/gui/widgets/popup.cpp b/gui/widgets/popup.cpp
index 6bc8f05cc9..148abd7be0 100644
--- a/gui/widgets/popup.cpp
+++ b/gui/widgets/popup.cpp
@@ -475,7 +475,7 @@ void PopUpWidget::handleMouseWheel(int x, int y, int direction) {
 
 		// Skip separator entries
 		while ((newSelection >= 0) && (newSelection < (int)_entries.size()) &&
-			_entries[newSelection].name.equals("")) {
+		       _entries[newSelection].name.empty()) {
 			newSelection += direction;
 		}
 




More information about the Scummvm-git-logs mailing list