(svn r7182) -Feature: Merge utf8 branch. This brings us support for Unicode/UTF-8 and the option for fonts rendered by FreeType. Language changes to come.
authorpeter1138
Thu, 16 Nov 2006 22:05:33 +0000
changeset 5108 dc67d70b5a45
parent 5107 8791beb0ae51
child 5109 b2689c3a2956
(svn r7182) -Feature: Merge utf8 branch. This brings us support for Unicode/UTF-8 and the option for fonts rendered by FreeType. Language changes to come.
Makefile
configure
console.c
currency.c
debug.c
debug.h
fontcache.c
fontcache.h
functions.h
gfx.c
gfx.h
gui.h
lang/english.txt
main_gui.c
makefiledir/Makefile.config_writer
makefiledir/Makefile.libdetection
misc.c
misc_gui.c
namegen.c
network.c
newgrf.c
newgrf_text.c
news_gui.c
openttd.c
openttd.h
openttd.vcproj
openttd_vs80.vcproj
saveload.c
settings.c
strgen/strgen.c
string.c
string.h
strings.c
strings.h
table/control_codes.h
table/namegen.h
table/unicode.h
unix.c
variables.h
video/win32_v.c
win32.c
window.c
window.h
--- a/Makefile	Thu Nov 16 20:57:23 2006 +0000
+++ b/Makefile	Thu Nov 16 22:05:33 2006 +0000
@@ -235,6 +235,12 @@
 	endif
 endif
 
+ifdef WITH_FREETYPE
+	ifndef FREETYPE_CONFIG
+$(error WITH_FREETYPE can't be used when FREETYPE_CONFIG is not set. Edit Makefile.config to correct this)
+	endif
+endif
+
 ##############################################################################
 #
 # Compiler configuration
@@ -493,6 +499,15 @@
 	LIBS += -lc
 endif
 
+# freetype config
+ifdef WITH_FREETYPE
+CDEFS += -DWITH_FREETYPE
+CCFLAGS_FREETYPE := $(shell $(FREETYPE_CONFIG) --cflags)
+LDFLAGS_FREETYPE := $(shell $(FREETYPE_CONFIG) --libs)
+CFLAGS += $(CCFLAGS_FREETYPE)
+LIBS += $(LDFLAGS_FREETYPE)
+endif
+
 # iconv is enabled defaultly on OSX >= 10.3
 ifdef OSX
 	ifndef JAGUAR
@@ -670,6 +685,7 @@
 SRCS += engine_gui.c
 SRCS += fileio.c
 SRCS += fios.c
+SRCS += fontcache.c
 SRCS += genworld.c
 SRCS += genworld_gui.c
 SRCS += gfx.c
@@ -884,7 +900,7 @@
 	$(Q)$(CXX_TARGET) $(LDFLAGS) $(TTDLDFLAGS) $(OBJS) $(LIBS) -o $@
 endif
 
-$(STRGEN): strgen/strgen.c string.c endian_host.h
+$(STRGEN): strgen/strgen.c string.c endian_host.h table/control_codes.h
 	@echo '===> Compiling and Linking $@'
 	$(Q)$(CC_HOST) $(CFLAGS_HOST) -DSTRGEN strgen/strgen.c string.c -o $@
 
--- a/configure	Thu Nov 16 20:57:23 2006 +0000
+++ b/configure	Thu Nov 16 22:05:33 2006 +0000
@@ -35,11 +35,13 @@
 	echo " iconv              Do you want iconv-support?          [no]"
 	echo " network            Do you want network-support?        [yes]"
 	echo " cocoa              Do you want cocoa-support? (MacOSX) [no]"
+	echo " freetype           Do you want freetype-support?       [yes]"
 	echo ""
 	echo "Params used to configure external libs:"
 	echo " --static-zlib-path Set the path to your static zlib    []"
 	echo " --sdl-config       Where is your sdl-config            [sdl-config]"
 	echo " --libpng-config    Where is your libpng-config         [libpng-config]"
+	echo " --freetype-config  Where is your freetype-config       [freetype-config]"
 	echo " --with-iconv       Set the path to your iconv headers  []"
 	echo " "
 }
@@ -181,6 +183,12 @@
 		--without-cocoa)
 			PARAM="$PARAM WITH_COCOA="
 			;;
+		--with-freetype)
+			PARAM="$PARAM WITH_FREETYPE=1"
+			;;
+		--without-freetype)
+			PARAM="$PARAM WITH_FREETYPE="
+			;;
 		--static-zlib-path=*)
 			handle STATIC_ZLIB_PATH "$n"
 			;;
@@ -199,6 +207,12 @@
 		--libpng-config)
 			ITEM="LIBPNG_CONFIG"
 			;;
+		--freetype-config=*)
+			handle FREETYPE_CONFIG "$n"
+			;;
+		--freetype-config)
+			ITEM="FREETYPE_CONFIG"
+			;;
 
 		--*=*)
 			echo -n "Unknown switch "
--- a/console.c	Thu Nov 16 20:57:23 2006 +0000
+++ b/console.c	Thu Nov 16 22:05:33 2006 +0000
@@ -181,9 +181,9 @@
 					}
 					break;
 				default:
-					if (IsValidAsciiChar(e->we.keypress.ascii, CS_ALPHANUMERAL)) {
+					if (IsValidChar(e->we.keypress.key, CS_ALPHANUMERAL)) {
 						_iconsole_scroll = ICON_BUFFER;
-						InsertTextBufferChar(&_iconsole_cmdline, e->we.keypress.ascii);
+						InsertTextBufferChar(&_iconsole_cmdline, e->we.keypress.key);
 						IConsoleResetHistoryPos();
 						SetWindowDirty(w);
 					} else {
@@ -1057,7 +1057,7 @@
 	if (cmdstr[0] == '#') return; // comments
 
 	for (cmdptr = cmdstr; *cmdptr != '\0'; cmdptr++) {
-		if (!IsValidAsciiChar(*cmdptr, CS_ALPHANUMERAL)) {
+		if (!IsValidChar(*cmdptr, CS_ALPHANUMERAL)) {
 			IConsoleError("command contains malformed characters, aborting");
 			IConsolePrintF(_icolour_err, "ERROR: command was: '%s'", cmdstr);
 			return;
--- a/currency.c	Thu Nov 16 20:57:23 2006 +0000
+++ b/currency.c	Thu Nov 16 22:05:33 2006 +0000
@@ -13,14 +13,14 @@
 	//   |   |    Euro year  |       |       |    name
 	//   |   |    |          |       |       |    |
 static const CurrencySpec origin_currency_specs[NUM_CURRENCY] = {
-	{    1, ',', CF_NOEURO, "\xA3", "",      0,  STR_CURR_GBP    }, // british pounds
+	{    1, ',', CF_NOEURO, "£",    "",      0,  STR_CURR_GBP    }, // british pounds
 	{    2, ',', CF_NOEURO, "$",    "",      0,  STR_CURR_USD    }, // us dollars
-	{    2, ',', CF_ISEURO, "",    "",      0,  STR_CURR_EUR    }, // Euro
-	{  220, ',', CF_NOEURO, "\xA5", "",      0,  STR_CURR_YEN    }, // yen
+	{    2, ',', CF_ISEURO, "€",    "",      0,  STR_CURR_EUR    }, // Euro
+	{  220, ',', CF_NOEURO, "¥",    "",      0,  STR_CURR_YEN    }, // yen
 	{   20, ',', 2002,      "",     " S.",   1,  STR_CURR_ATS    }, // austrian schilling
 	{   59, ',', 2002,      "BEF ", "",      0,  STR_CURR_BEF    }, // belgian franc
 	{    2, ',', CF_NOEURO, "CHF ", "",      0,  STR_CURR_CHF    }, // swiss franc
-	{   41, ',', CF_NOEURO, "",     " Kc",   1,  STR_CURR_CZK    }, // czech koruna // TODO: Should use the "c" with an upside down "^"
+	{   41, ',', CF_NOEURO, "",     " Kč",   1,  STR_CURR_CZK    }, // czech koruna
 	{    3, '.', 2002,      "DM ",  "",      0,  STR_CURR_DEM    }, // deutsche mark
 	{   11, '.', CF_NOEURO, "",     " kr",   1,  STR_CURR_DKK    }, // danish krone
 	{  245, '.', 2002,      "Pts ", "",      0,  STR_CURR_ESP    }, // spanish pesetas
--- a/debug.c	Thu Nov 16 20:57:23 2006 +0000
+++ b/debug.c	Thu Nov 16 22:05:33 2006 +0000
@@ -21,6 +21,7 @@
 int _debug_ntp_level;
 int _debug_npf_level;
 int _debug_yapf_level;
+int _debug_freetype_level;
 
 
 void CDECL debug(const char *s, ...)
@@ -53,7 +54,8 @@
 	DEBUG_LEVEL(oldloader),
 	DEBUG_LEVEL(ntp),
 	DEBUG_LEVEL(npf),
-	DEBUG_LEVEL(yapf)
+	DEBUG_LEVEL(yapf),
+	DEBUG_LEVEL(freetype)
 	};
 #undef DEBUG_LEVEL
 
--- a/debug.h	Thu Nov 16 20:57:23 2006 +0000
+++ b/debug.h	Thu Nov 16 22:05:33 2006 +0000
@@ -20,6 +20,7 @@
 	extern int _debug_ntp_level;
 	extern int _debug_npf_level;
 	extern int _debug_yapf_level;
+	extern int _debug_freetype_level;
 #endif
 
 void CDECL debug(const char *s, ...);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fontcache.c	Thu Nov 16 22:05:33 2006 +0000
@@ -0,0 +1,310 @@
+/* $Id$ */
+
+#include "stdafx.h"
+#include "openttd.h"
+#include "functions.h"
+#include "macros.h"
+#include "debug.h"
+#include "table/sprites.h"
+#include "table/control_codes.h"
+#include "spritecache.h"
+#include "gfx.h"
+#include "string.h"
+#include "fontcache.h"
+
+#ifdef WITH_FREETYPE
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_GLYPH_H
+
+static FT_Library _library = NULL;
+static FT_Face _face_small = NULL;
+static FT_Face _face_medium = NULL;
+static FT_Face _face_large = NULL;
+
+FreeTypeSettings _freetype;
+
+enum {
+	FACE_COLOUR = 1,
+	SHADOW_COLOUR = 2,
+};
+
+
+static void LoadFreeTypeFont(const char *font_name, FT_Face *face, const char *type)
+{
+	FT_Error error;
+
+	if (strlen(font_name) == 0) return;
+
+	error = FT_New_Face(_library, font_name, 0, face);
+	if (error == FT_Err_Ok) {
+		/* Attempt to select the unicode character map */
+		error = FT_Select_Charmap(*face, ft_encoding_unicode);
+		if (error == FT_Err_Ok) {
+			/* Success */
+			return;
+		} else if (error == FT_Err_Invalid_CharMap_Handle) {
+			/* Try to pick a different character map instead. We default to
+			 * the first map, but platform_id 0 encoding_id 0 should also
+			 * be unicode (strange system...) */
+			FT_CharMap found = (*face)->charmaps[0];
+			int i;
+
+			for (i = 0; i < (*face)->num_charmaps; i++) {
+				FT_CharMap charmap = (*face)->charmaps[i];
+				if (charmap->platform_id == 0 && charmap->encoding_id == 0) {
+					found = charmap;
+				}
+			}
+
+			if (found != NULL) {
+				error = FT_Set_Charmap(*face, found);
+				if (error == FT_Err_Ok) return;
+			}
+		}
+
+		FT_Done_Face(*face);
+		*face = NULL;
+	}
+
+	ShowInfoF("Unable to use '%s' for %s font, FreeType reported error 0x%X, using sprite font instead", font_name, type, error);
+}
+
+
+void InitFreeType(void)
+{
+	if (strlen(_freetype.small_font) == 0 && strlen(_freetype.medium_font) == 0 && strlen(_freetype.large_font) == 0) {
+		DEBUG(freetype, 1) ("[FreeType] No font faces specified, using sprite fonts instead");
+		return;
+	}
+
+	if (FT_Init_FreeType(&_library) != FT_Err_Ok) {
+		ShowInfoF("Unable to initialize FreeType, using sprite fonts instead");
+		return;
+	}
+
+	DEBUG(freetype, 2) ("[FreeType] Initialized");
+
+	/* Load each font */
+	LoadFreeTypeFont(_freetype.small_font,  &_face_small,  "small");
+	LoadFreeTypeFont(_freetype.medium_font, &_face_medium, "medium");
+	LoadFreeTypeFont(_freetype.large_font,  &_face_large,  "large");
+
+	/* Set each font size */
+	if (_face_small  != NULL) FT_Set_Pixel_Sizes(_face_small,  0, _freetype.small_size);
+	if (_face_medium != NULL) FT_Set_Pixel_Sizes(_face_medium, 0, _freetype.medium_size);
+	if (_face_large  != NULL) FT_Set_Pixel_Sizes(_face_large,  0, _freetype.large_size);
+}
+
+
+static FT_Face GetFontFace(FontSize size)
+{
+	switch (size) {
+		default: NOT_REACHED();
+		case FS_NORMAL: return _face_medium;
+		case FS_SMALL:  return _face_small;
+		case FS_LARGE:  return _face_large;
+	}
+}
+
+
+typedef struct GlyphEntry {
+	Sprite *sprite;
+	byte width;
+} GlyphEntry;
+
+
+/* The glyph cache. This is structured to reduce memory consumption.
+ * 1) There is a 'segment' table for each font size.
+ * 2) Each segment table is a discrete block of characters.
+ * 3) Each block contains 256 (aligned) characters sequential characters.
+ *
+ * The cache is accessed in the following way:
+ * For character 0x0041  ('A'): _glyph_ptr[FS_NORMAL][0x00][0x41]
+ * For character 0x20AC (Euro): _glyph_ptr[FS_NORMAL][0x20][0xAC]
+ *
+ * Currently only 256 segments are allocated, "limiting" us to 65536 characters.
+ * This can be simply changed in the two functions Get & SetGlyphPtr.
+ */
+static GlyphEntry **_glyph_ptr[FS_END];
+
+
+static GlyphEntry *GetGlyphPtr(FontSize size, WChar key)
+{
+	if (_glyph_ptr[size] == NULL) return NULL;
+	if (_glyph_ptr[size][GB(key, 8, 8)] == NULL) return NULL;
+	return &_glyph_ptr[size][GB(key, 8, 8)][GB(key, 0, 8)];
+}
+
+
+static void SetGlyphPtr(FontSize size, WChar key, const GlyphEntry *glyph)
+{
+	if (_glyph_ptr[size] == NULL) {
+		DEBUG(freetype, 3) ("[FreeType] Allocating root glyph cache for size %u", size);
+		_glyph_ptr[size] = calloc(256, sizeof(**_glyph_ptr));
+	}
+
+	if (_glyph_ptr[size][GB(key, 8, 8)] == NULL) {
+		DEBUG(freetype, 3) ("[FreeType] Allocating glyph cache for range 0x%02X00, size %u", GB(key, 8, 8), size);
+		_glyph_ptr[size][GB(key, 8, 8)] = calloc(256, sizeof(***_glyph_ptr));
+	}
+
+	DEBUG(freetype, 4) ("[FreeType] Set glyph for unicode character 0x%04X, size %u", key, size);
+	_glyph_ptr[size][GB(key, 8, 8)][GB(key, 0, 8)].sprite = glyph->sprite;
+	_glyph_ptr[size][GB(key, 8, 8)][GB(key, 0, 8)].width  = glyph->width;
+}
+
+
+const Sprite *GetGlyph(FontSize size, WChar key)
+{
+	FT_Face face = GetFontFace(size);
+	FT_GlyphSlot slot;
+	GlyphEntry new_glyph;
+	GlyphEntry *glyph;
+	Sprite *sprite;
+	int width;
+	int height;
+	int x;
+	int y;
+	int y_adj;
+
+	assert(IsPrintable(key));
+
+	/* Bail out if no face loaded, or for our special characters */
+	if (face == NULL || (key >= SCC_SPRITE_START && key <= SCC_SPRITE_END)) {
+		SpriteID sprite = GetUnicodeGlyph(size, key);
+		if (sprite == 0) sprite = GetUnicodeGlyph(size, '?');
+		return GetSprite(sprite);
+	}
+
+	/* Check for the glyph in our cache */
+	glyph = GetGlyphPtr(size, key);
+	if (glyph != NULL && glyph->sprite != NULL) return glyph->sprite;
+
+	slot = face->glyph;
+
+	FT_Load_Char(face, key, FT_LOAD_DEFAULT);
+	FT_Render_Glyph(face->glyph, FT_RENDER_MODE_MONO);
+
+	/* Add 1 pixel for the shadow on the medium font. Our sprite must be at least 1x1 pixel */
+	width  = max(1, slot->bitmap.width + (size == FS_NORMAL));
+	height = max(1, slot->bitmap.rows  + (size == FS_NORMAL));
+
+	/* FreeType has rendered the glyph, now we allocate a sprite and copy the image into it */
+	sprite = calloc(width * height + 8, 1);
+	sprite->info   = 1;
+	sprite->width  = width;
+	sprite->height = height;
+	sprite->x_offs = slot->bitmap_left;
+	// XXX 2 should be determined somehow... it's right for the normal face
+	y_adj = (size == FS_NORMAL) ? 2 : 0;
+	sprite->y_offs = GetCharacterHeight(size) - slot->bitmap_top - y_adj;
+
+	/* Draw shadow for medium size */
+	if (size == FS_NORMAL) {
+		for (y = 0; y < slot->bitmap.rows; y++) {
+			for (x = 0; x < slot->bitmap.width; x++) {
+				if (HASBIT(slot->bitmap.buffer[(x / 8) + y * slot->bitmap.pitch], 7 - (x % 8))) {
+					sprite->data[1 + x + (1 + y) * sprite->width] = SHADOW_COLOUR;
+				}
+			}
+		}
+	}
+
+	for (y = 0; y < slot->bitmap.rows; y++) {
+		for (x = 0; x < slot->bitmap.width; x++) {
+			if (HASBIT(slot->bitmap.buffer[(x / 8) + y * slot->bitmap.pitch], 7 - (x % 8))) {
+				sprite->data[x + y * sprite->width] = FACE_COLOUR;
+			}
+		}
+	}
+
+	new_glyph.sprite = sprite;
+	new_glyph.width  = (slot->advance.x >> 6) + (size != FS_NORMAL);
+
+	SetGlyphPtr(size, key, &new_glyph);
+
+	return sprite;
+}
+
+
+uint GetGlyphWidth(FontSize size, WChar key)
+{
+	FT_Face face = GetFontFace(size);
+	GlyphEntry *glyph;
+
+	if (face == NULL || (key >= SCC_SPRITE_START && key <= SCC_SPRITE_END)) {
+		SpriteID sprite = GetUnicodeGlyph(size, key);
+		if (sprite == 0) sprite = GetUnicodeGlyph(size, '?');
+		return SpriteExists(sprite) ? GetSprite(sprite)->width + (size != FS_NORMAL) : 0;
+	}
+
+	glyph = GetGlyphPtr(size, key);
+	if (glyph == NULL || glyph->sprite == NULL) {
+		GetGlyph(size, key);
+		glyph = GetGlyphPtr(size, key);
+	}
+
+	return glyph->width;
+}
+
+
+#endif /* WITH_FREETYPE */
+
+/* Sprite based glyph mapping */
+
+#include "table/unicode.h"
+
+static SpriteID **_unicode_glyph_map[FS_END];
+
+
+/** Get the SpriteID of the first glyph for the given font size */
+static SpriteID GetFontBase(FontSize size)
+{
+	switch (size) {
+		default: NOT_REACHED();
+		case FS_NORMAL: return SPR_ASCII_SPACE;
+		case FS_SMALL:  return SPR_ASCII_SPACE_SMALL;
+		case FS_LARGE:  return SPR_ASCII_SPACE_BIG;
+	}
+}
+
+
+SpriteID GetUnicodeGlyph(FontSize size, uint32 key)
+{
+	if (_unicode_glyph_map[size][GB(key, 8, 8)] == NULL) return 0;
+	return _unicode_glyph_map[size][GB(key, 8, 8)][GB(key, 0, 8)];
+}
+
+
+void SetUnicodeGlyph(FontSize size, uint32 key, SpriteID sprite)
+{
+	if (_unicode_glyph_map[size] == NULL) _unicode_glyph_map[size] = calloc(256, sizeof(*_unicode_glyph_map[size]));
+	if (_unicode_glyph_map[size][GB(key, 8, 8)] == NULL) _unicode_glyph_map[size][GB(key, 8, 8)] = calloc(256, sizeof(**_unicode_glyph_map[size]));
+	_unicode_glyph_map[size][GB(key, 8, 8)][GB(key, 0, 8)] = sprite;
+}
+
+
+void InitializeUnicodeGlyphMap(void)
+{
+	FontSize size;
+	SpriteID base;
+	SpriteID sprite;
+	uint i;
+
+	for (size = FS_NORMAL; size != FS_END; size++) {
+		base = GetFontBase(size);
+		for (i = ASCII_LETTERSTART; i < 256; i++) {
+			sprite = base + i - ASCII_LETTERSTART;
+			if (!SpriteExists(sprite)) continue;
+			SetUnicodeGlyph(size, i, sprite);
+			SetUnicodeGlyph(size, i + SCC_SPRITE_START, sprite);
+		}
+		for (i = 0; i < lengthof(_default_unicode_map); i++) {
+			sprite = base + _default_unicode_map[i].key - ASCII_LETTERSTART;
+			SetUnicodeGlyph(size, _default_unicode_map[i].code, sprite);
+		}
+	}
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fontcache.h	Thu Nov 16 22:05:33 2006 +0000
@@ -0,0 +1,56 @@
+/* $Id$ */
+
+#ifndef FONTCACHE_H
+#define FONTCACHE_H
+
+/** Get the SpriteID mapped to the given font size and key */
+SpriteID GetUnicodeGlyph(FontSize size, uint32 key);
+
+/** Map a SpriteID to the font size and key */
+void SetUnicodeGlyph(FontSize size, uint32 key, SpriteID sprite);
+
+/** Initialize the glyph map */
+void InitializeUnicodeGlyphMap(void);
+
+#ifdef WITH_FREETYPE
+
+typedef struct FreeTypeSettings {
+	char small_font[260];
+	char medium_font[260];
+	char large_font[260];
+	uint small_size;
+	uint medium_size;
+	uint large_size;
+} FreeTypeSettings;
+
+extern FreeTypeSettings _freetype;
+
+void InitFreeType(void);
+const struct Sprite *GetGlyph(FontSize size, uint32 key);
+uint GetGlyphWidth(FontSize size, uint32 key);
+
+#else
+
+/* Stub for initializiation */
+static inline void InitFreeType(void) {}
+
+/** Get the Sprite for a glyph */
+static inline const Sprite *GetGlyph(FontSize size, uint32 key)
+{
+	SpriteID sprite = GetUnicodeGlyph(size, key);
+	if (sprite == 0) sprite = GetUnicodeGlyph(size, '?');
+	return GetSprite(sprite);
+}
+
+
+/** Get the width of a glyph */
+static inline uint GetGlyphWidth(FontSize size, uint32 key)
+{
+	SpriteID sprite = GetUnicodeGlyph(size, key);
+	if (sprite == 0) sprite = GetUnicodeGlyph(size, '?');
+	return SpriteExists(sprite) ? GetSprite(sprite)->width + (size != FS_NORMAL) : 0;
+}
+
+#endif /* WITH_FREETYPE */
+
+#endif /* FONTCACHE_H */
--- a/functions.h	Thu Nov 16 20:57:23 2006 +0000
+++ b/functions.h	Thu Nov 16 22:05:33 2006 +0000
@@ -147,6 +147,7 @@
 #define AllocateNameUnique(name, skip) RealAllocateName(name, skip, true)
 #define AllocateName(name, skip) RealAllocateName(name, skip, false)
 StringID RealAllocateName(const char *name, byte skip, bool check_double);
+void ConvertNameArray(void);
 
 /* misc functions */
 void MarkTileDirty(int x, int y);
--- a/gfx.c	Thu Nov 16 20:57:23 2006 +0000
+++ b/gfx.c	Thu Nov 16 22:05:33 2006 +0000
@@ -12,6 +12,8 @@
 #include "table/sprites.h"
 #include "hal.h"
 #include "variables.h"
+#include "table/control_codes.h"
+#include "fontcache.h"
 #include "genworld.h"
 
 #ifdef _DEBUG
@@ -244,40 +246,6 @@
 }
 
 
-static inline SpriteID GetFontBase(FontSize size)
-{
-	switch (size) {
-		default: NOT_REACHED();
-		case FS_NORMAL: return SPR_ASCII_SPACE;
-		case FS_SMALL:  return SPR_ASCII_SPACE_SMALL;
-		case FS_LARGE:  return SPR_ASCII_SPACE_BIG;
-	}
-}
-
-
-// ASSIGNMENT OF ASCII LETTERS < 32
-// 0 - end of string
-// 1 - SETX <BYTE>
-// 2 - SETXY <BYTE> <BYTE>
-// 3-7 -
-// 8 - TINYFONT
-// 9 - BIGFONT
-// 10 - newline
-// 11-14 -
-// 15-31 - 17 colors
-
-
-enum {
-	ASCII_SETX       =  1,
-	ASCII_SETXY      =  2,
-
-	ASCII_TINYFONT   =  8,
-	ASCII_BIGFONT    =  9,
-	ASCII_NL         = 10,
-
-	ASCII_COLORSTART = 15,
-};
-
 /** Truncate a given string to a maximum width if neccessary.
  * If the string is truncated, add three dots ('...') to show this.
  * @param *dest string that is checked and possibly truncated
@@ -289,13 +257,13 @@
 	FontSize size = _cur_fontsize;
 	int ddd, ddd_w;
 
-	byte c;
+	WChar c;
 	char *ddd_pos;
 
 	ddd_w = ddd = GetCharacterWidth(size, '.') * 3;
 
-	for (ddd_pos = str; (c = *str++) != '\0'; ) {
-		if (c >= ASCII_LETTERSTART) {
+	for (ddd_pos = str; (c = Utf8Consume((const char **)&str)) != '\0'; ) {
+		if (IsPrintable(c)) {
 			w += GetCharacterWidth(size, c);
 
 			if (w >= maxw) {
@@ -305,12 +273,12 @@
 				return ddd_w;
 			}
 		} else {
-			if (c == ASCII_SETX) str++;
-			else if (c == ASCII_SETXY) str += 2;
-			else if (c == ASCII_TINYFONT) {
+			if (c == SCC_SETX) str++;
+			else if (c == SCC_SETXY) str += 2;
+			else if (c == SCC_TINYFONT) {
 				size = FS_SMALL;
 				ddd = GetCharacterWidth(size, '.') * 3;
-			} else if (c == ASCII_BIGFONT) {
+			} else if (c == SCC_BIGFONT) {
 				size = FS_LARGE;
 				ddd = GetCharacterWidth(size, '.') * 3;
 			}
@@ -443,11 +411,11 @@
 		int w = 0;
 
 		for (;;) {
-			byte c = *str++;
+			WChar c = Utf8Consume((const char **)&str);
 			/* whitespace is where we will insert the line-break */
-			if (c == ASCII_LETTERSTART) last_space = str;
+			if (c == ' ') last_space = str;
 
-			if (c >= ASCII_LETTERSTART) {
+			if (IsPrintable(c)) {
 				w += GetCharacterWidth(size, c);
 				/* string is longer than maximum width so we need to decide what to
 				 * do. We can do two things:
@@ -465,11 +433,11 @@
 			} else {
 				switch (c) {
 					case '\0': return num + (size << 16); break;
-					case ASCII_SETX:  str++; break;
-					case ASCII_SETXY: str +=2; break;
-					case ASCII_TINYFONT: size = FS_SMALL; break;
-					case ASCII_BIGFONT:  size = FS_LARGE; break;
-					case ASCII_NL: goto end_of_inner_loop;
+					case SCC_SETX:  str++; break;
+					case SCC_SETXY: str +=2; break;
+					case SCC_TINYFONT: size = FS_SMALL; break;
+					case SCC_BIGFONT:  size = FS_LARGE; break;
+					case '\n': goto end_of_inner_loop;
 				}
 			}
 		}
@@ -486,7 +454,7 @@
 	uint32 tmp;
 	int num, w, mt;
 	const char *src;
-	byte c;
+	WChar c;
 
 	GetString(buffer, str, lastof(buffer));
 
@@ -505,7 +473,7 @@
 		_cur_fontsize = _last_fontsize;
 
 		for (;;) {
-			c = *src++;
+			c = Utf8Consume(&src);
 			if (c == 0) {
 				y += mt;
 				if (--num < 0) {
@@ -513,9 +481,9 @@
 					return;
 				}
 				break;
-			} else if (c == ASCII_SETX) {
+			} else if (c == SCC_SETX) {
 				src++;
-			} else if (c == ASCII_SETXY) {
+			} else if (c == SCC_SETXY) {
 				src+=2;
 			}
 		}
@@ -530,7 +498,7 @@
 	int num, mt;
 	uint total_height;
 	const char *src;
-	byte c;
+	WChar c;
 
 	GetString(buffer, str, lastof(buffer));
 
@@ -547,7 +515,7 @@
 		_cur_fontsize = _last_fontsize;
 
 		for (;;) {
-			c = *src++;
+			c = Utf8Consume(&src);
 			if (c == 0) {
 				y += mt;
 				if (--num < 0) {
@@ -555,9 +523,9 @@
 					return total_height;
 				}
 				break;
-			} else if (c == ASCII_SETX) {
+			} else if (c == SCC_SETX) {
 				src++;
-			} else if (c == ASCII_SETXY) {
+			} else if (c == SCC_SETXY) {
 				src+=2;
 			}
 		}
@@ -576,22 +544,24 @@
 	FontSize size = _cur_fontsize;
 	BoundingRect br;
 	int max_width;
-	byte c;
+	WChar c;
 
 	br.width = br.height = max_width = 0;
-	for (c = *str; c != '\0'; c = *(++str)) {
-		if (c >= ASCII_LETTERSTART) {
+	for (;;) {
+		c = Utf8Consume(&str);
+		if (c == 0) break;
+		if (IsPrintable(c)) {
 			br.width += GetCharacterWidth(size, c);
 		} else {
 			switch (c) {
-				case ASCII_SETX: br.width += (byte)*++str; break;
-				case ASCII_SETXY:
+				case SCC_SETX: br.width += (byte)*++str; break;
+				case SCC_SETXY:
 					br.width += (byte)*++str;
 					br.height += (byte)*++str;
 					break;
-				case ASCII_TINYFONT: size = FS_SMALL; break;
-				case ASCII_BIGFONT:  size = FS_LARGE; break;
-				case ASCII_NL:
+				case SCC_TINYFONT: size = FS_SMALL; break;
+				case SCC_BIGFONT:  size = FS_LARGE; break;
+				case '\n':
 					br.height += GetCharacterHeight(size);
 					if (br.width > max_width) max_width = br.width;
 					br.width = 0;
@@ -617,7 +587,7 @@
 {
 	DrawPixelInfo *dpi = _cur_dpi;
 	FontSize size = _cur_fontsize;
-	byte c;
+	WChar c;
 	byte color;
 	int xo = x, yo = y;
 
@@ -647,39 +617,39 @@
 	if (y + 19 <= dpi->top || dpi->top + dpi->height <= y) {
 skip_char:;
 		for (;;) {
-			c = *string++;
-			if (c < ASCII_LETTERSTART) goto skip_cont;
+			c = Utf8Consume(&string);
+			if (!IsPrintable(c)) goto skip_cont;
 		}
 	}
 
 	for (;;) {
-		c = *string++;
+		c = Utf8Consume(&string);
 skip_cont:;
 		if (c == 0) {
 			_last_fontsize = size;
 			return x;
 		}
-		if (c >= ASCII_LETTERSTART) {
+		if (IsPrintable(c)) {
 			if (x >= dpi->left + dpi->width) goto skip_char;
 			if (x + 26 >= dpi->left) {
-				GfxMainBlitter(GetSprite(GetFontBase(size) + c - ASCII_LETTERSTART), x, y, 1);
+				GfxMainBlitter(GetGlyph(size, c), x, y, 1);
 			}
 			x += GetCharacterWidth(size, c);
-		} else if (c == ASCII_NL) { // newline = {}
+		} else if (c == '\n') { // newline = {}
 			x = xo;
 			y += GetCharacterHeight(size);
 			goto check_bounds;
-		} else if (c >= ASCII_COLORSTART) { // change color?
-			color = (byte)(c - ASCII_COLORSTART);
+		} else if (c >= SCC_BLUE && c <= SCC_BLACK) { // change color?
+			color = (byte)(c - SCC_BLUE);
 			goto switch_color;
-		} else if (c == ASCII_SETX) { // {SETX}
+		} else if (c == SCC_SETX) { // {SETX}
 			x = xo + (byte)*string++;
-		} else if (c == ASCII_SETXY) {// {SETXY}
+		} else if (c == SCC_SETXY) {// {SETXY}
 			x = xo + (byte)*string++;
 			y = yo + (byte)*string++;
-		} else if (c == ASCII_TINYFONT) { // {TINYFONT}
+		} else if (c == SCC_TINYFONT) { // {TINYFONT}
 			size = FS_SMALL;
-		} else if (c == ASCII_BIGFONT) { // {BIGFONT}
+		} else if (c == SCC_BIGFONT) { // {BIGFONT}
 			size = FS_LARGE;
 		} else {
 			printf("Unknown string command character %d\n", c);
@@ -1641,28 +1611,33 @@
 
 void LoadStringWidthTable(void)
 {
-	SpriteID base;
 	uint i;
 
 	/* Normal font */
-	base = GetFontBase(FS_NORMAL);
 	for (i = 0; i != 224; i++) {
-		_stringwidth_table[FS_NORMAL][i] = SpriteExists(base + i) ? GetSprite(base + i)->width : 0;
+		_stringwidth_table[FS_NORMAL][i] = GetGlyphWidth(FS_NORMAL, i + 32);
 	}
 
 	/* Small font */
-	base = GetFontBase(FS_SMALL);
 	for (i = 0; i != 224; i++) {
-		_stringwidth_table[FS_SMALL][i] = SpriteExists(base + i) ? GetSprite(base + i)->width + 1 : 0;
+		_stringwidth_table[FS_SMALL][i] = GetGlyphWidth(FS_SMALL, i + 32);
 	}
 
 	/* Large font */
-	base = GetFontBase(FS_LARGE);
 	for (i = 0; i != 224; i++) {
-		_stringwidth_table[FS_LARGE][i] = SpriteExists(base + i) ? GetSprite(base + i)->width + 1 : 0;
+		_stringwidth_table[FS_LARGE][i] = GetGlyphWidth(FS_LARGE, i + 32);
 	}
 }
 
+
+byte GetCharacterWidth(FontSize size, WChar key)
+{
+	if (key >= 32 && key < 256) return _stringwidth_table[size][key - 32];
+
+	return GetGlyphWidth(size, key);
+}
+
+
 void ScreenSizeChanged(void)
 {
 	// check the dirty rect
--- a/gfx.h	Thu Nov 16 20:57:23 2006 +0000
+++ b/gfx.h	Thu Nov 16 22:05:33 2006 +0000
@@ -43,8 +43,8 @@
 
 // XXX doesn't really belong here, but the only
 // consumers always use it in conjunction with DoDrawString()
-#define UPARROW   "\x80"
-#define DOWNARROW "\xAA"
+#define UPARROW   "\xEE\x8A\x80"
+#define DOWNARROW "\xEE\x8A\xAA"
 
 
 int DrawStringCentered(int x, int y, StringID str, uint16 color);
@@ -96,13 +96,8 @@
 /* gfx.c */
 #define ASCII_LETTERSTART 32
 extern FontSize _cur_fontsize;
-extern byte _stringwidth_table[FS_END][224];
 
-static inline byte GetCharacterWidth(FontSize size, byte key)
-{
-	assert(key >= ASCII_LETTERSTART);
-	return _stringwidth_table[size][key - ASCII_LETTERSTART];
-}
+byte GetCharacterWidth(FontSize size, uint32 key);
 
 static inline byte GetCharacterHeight(FontSize size)
 {
--- a/gui.h	Thu Nov 16 20:57:23 2006 +0000
+++ b/gui.h	Thu Nov 16 22:05:33 2006 +0000
@@ -109,7 +109,7 @@
 
 void DeleteTextBufferAll(Textbuf *tb);
 bool DeleteTextBufferChar(Textbuf *tb, int delmode);
-bool InsertTextBufferChar(Textbuf *tb, byte key);
+bool InsertTextBufferChar(Textbuf *tb, uint32 key);
 bool InsertTextBufferClipboard(Textbuf *tb);
 bool MoveTextBufferPos(Textbuf *tb, int navmode);
 void InitializeTextBuffer(Textbuf *tb, const char *buf, uint16 maxlength, uint16 maxwidth);
--- a/lang/english.txt	Thu Nov 16 20:57:23 2006 +0000
+++ b/lang/english.txt	Thu Nov 16 22:05:33 2006 +0000
@@ -2001,8 +2001,8 @@
 STR_6816_LOW                                                    :Low
 STR_6817_NORMAL                                                 :Normal
 STR_6818_HIGH                                                   :High
-STR_6819                                                        :{BLACK}<
-STR_681A                                                        :{BLACK}>
+STR_6819                                                        :{BLACK}{SMALLLEFTARROW}
+STR_681A                                                        :{BLACK}{SMALLRIGHTARROW}
 STR_681B_VERY_SLOW                                              :Very Slow
 STR_681C_SLOW                                                   :Slow
 STR_681D_MEDIUM                                                 :Medium
--- a/main_gui.c	Thu Nov 16 20:57:23 2006 +0000
+++ b/main_gui.c	Thu Nov 16 22:05:33 2006 +0000
@@ -29,6 +29,7 @@
 #include "variables.h"
 #include "train.h"
 #include "unmovable_map.h"
+#include "string.h"
 #include "screenshot.h"
 #include "genworld.h"
 #include "settings.h"
@@ -2085,15 +2086,16 @@
 	s = buf;
 	d = buffer;
 
-	for (;; s++) {
-		if (*s == '\0') {
+	for (;;) {
+		WChar c = Utf8Consume(&s);
+		if (c == 0) {
 			*d = '\0';
 			break;
 		} else if (*s == 0x0D) {
 			d[0] = d[1] = d[2] = d[3] = ' ';
 			d += 4;
-		} else if ((byte)*s >= ' ' && ((byte)*s < 0x88 || (byte)*s >= 0x99)) {
-			*d++ = *s;
+		} else if (IsPrintable(c)) {
+			d += Utf8Encode(d, c);
 		}
 	}
 
--- a/makefiledir/Makefile.config_writer	Thu Nov 16 20:57:23 2006 +0000
+++ b/makefiledir/Makefile.config_writer	Thu Nov 16 22:05:33 2006 +0000
@@ -73,6 +73,7 @@
 	$(call CONFIG_LINE,WITH_ICONV_PATH:=$(WITH_ICONV_PATH))
 	$(call CONFIG_LINE,STATIC_ZLIB_PATH:=$(STATIC_ZLIB_PATH))
 	$(call CONFIG_LINE,WITH_COCOA:=$(WITH_COCOA))
+	$(call CONFIG_LINE,WITH_FREETYPE:=$(WITH_FREETYPE))
 	$(call CONFIG_LINE,)
 
 	$(call CONFIG_LINE,\# OS flags)
@@ -100,6 +101,7 @@
 	$(call CONFIG_LINE,\# misc)
 	$(call CONFIG_LINE,SDL_CONFIG:=$(SDL_CONFIG))
 	$(call CONFIG_LINE,LIBPNG_CONFIG:=$(LIBPNG_CONFIG))
+	$(call CONFIG_LINE,FREETYPE_CONFIG:=$(FREETYPE_CONFIG))
 	$(call CONFIG_LINE,BEOS_NET_SERVER:=$(BEOS_NET_SERVER))
 	$(call CONFIG_LINE,CONFIG_INCLUDED:=yes)
 	$(call CONFIG_LINE,PATH_SET:=$(PATH_SET))
--- a/makefiledir/Makefile.libdetection	Thu Nov 16 20:57:23 2006 +0000
+++ b/makefiledir/Makefile.libdetection	Thu Nov 16 22:05:33 2006 +0000
@@ -66,6 +66,9 @@
 # set libpng-config to the default value
 LIBPNG_CONFIG :=libpng-config
 
+# set freetype-config to the default value
+FREETYPE_CONFIG:=freetype-config
+
 # Networking, enabled by default
 WITH_NETWORK:=1
 
@@ -75,6 +78,9 @@
 # libpng detection
 WITH_PNG:=$(shell $(LIBPNG_CONFIG) --version 2>/dev/null)
 
+# Freetype detection
+WITH_FREETYPE:=$(shell $(FREETYPE_CONFIG) --ftversion 2>/dev/null)
+
 ifdef WITH_PNG
 	# LibPNG depends on Zlib
 	WITH_ZLIB:=1
--- a/misc.c	Thu Nov 16 20:57:23 2006 +0000
+++ b/misc.c	Thu Nov 16 22:05:33 2006 +0000
@@ -202,6 +202,38 @@
 	}
 }
 
+void ConvertNameArray(void)
+{
+	uint i;
+
+	for (i = 0; i < lengthof(_name_array); i++) {
+		const char *strfrom = _name_array[i];
+		char tmp[sizeof(*_name_array)];
+		char *strto = tmp;
+
+		for (; *strfrom != '\0'; strfrom++) {
+			WChar c = (byte)*strfrom;
+			switch (c) {
+				case 0xA4: c = 0x20AC; break; // Euro
+				case 0xA6: c = 0x0160; break; // S with caron
+				case 0xA8: c = 0x0161; break; // s with caron
+				case 0xB4: c = 0x017D; break; // Z with caron
+				case 0xB8: c = 0x017E; break; // z with caron
+				case 0xBC: c = 0x0152; break; // OE ligature
+				case 0xBD: c = 0x0153; break; // oe ligature
+				case 0xBE: c = 0x0178; break; // Y with diaresis
+				default: break;
+			}
+			if (strto + Utf8CharLen(c) > lastof(tmp)) break;
+			strto += Utf8Encode(strto, c);
+		}
+
+		/* Terminate the new string and copy it back to the name array */
+		*strto = '\0';
+		memcpy(_name_array[i], tmp, sizeof(*_name_array));
+	}
+}
+
 // Calculate constants that depend on the landscape type.
 void InitializeLandscapeVariables(bool only_constants)
 {
--- a/misc_gui.c	Thu Nov 16 20:57:23 2006 +0000
+++ b/misc_gui.c	Thu Nov 16 22:05:33 2006 +0000
@@ -205,8 +205,8 @@
 	"  Bjarni Corfitzen (Bjarni) - MacOSX port, coder",
 	"  Matthijs Kooijman (blathijs) - Pathfinder-god",
 	"  Victor Fischer (Celestar) - Programming everywhere you need him to",
-	"  Tams Farag (Darkvater) - Lead coder",
-	"  Attila Bn (MiHaMiX) - WebTranslator, Nightlies, Wiki and bugtracker host",
+	"  Tamás Faragó (Darkvater) - Lead coder",
+	"  Attila Bán (MiHaMiX) - WebTranslator, Nightlies, Wiki and bugtracker host",
 	"  Owen Rudge (orudge) - Forum- and masterserver host, OS/2 port",
 	"  Peter Nelson (peter1138) - Spiritual descendant from newgrf gods",
 	"  Christoph Mallon (Tron) - Programmer, code correctness police",
@@ -221,13 +221,13 @@
 	"  Josef Drexler - For his great work on TTDPatch",
 	"  Marcin Grzegorczyk - For his documentation of TTD internals",
 	"  Petr Baudis (pasky) - Many patches, newgrf support",
-	"  Stefan Meiner (sign_de) - For his work on the console",
+	"  Stefan Meißner (sign_de) - For his work on the console",
 	"  Simon Sasburg (HackyKid) - Many bugfixes he has blessed us with (and PBS)",
 	"  Cian Duffy (MYOB) - BeOS port / manual writing",
 	"  Christian Rosentreter (tokai) - MorphOS / AmigaOS port",
 	"",
-	"  Michael Blunck - Pre-Signals and Semaphores  2003",
-	"  George - Canal/Lock graphics  2003-2004",
+	"  Michael Blunck - Pre-Signals and Semaphores © 2003",
+	"  George - Canal/Lock graphics © 2003-2004",
 	"  Marcin Grzegorczyk - Foundations for Tracks on Slopes",
 	"  All Translators - Who made OpenTTD a truly international game",
 	"  Bug Reporters - Without whom OpenTTD would still be full of bugs!",
@@ -782,11 +782,30 @@
 	if (num < w->hscroll.pos) w->hscroll.pos = num;
 }
 
-static void DelChar(Textbuf *tb)
+/* Delete a character at the caret position in a text buf.
+ * If backspace is set, delete the character before the caret,
+ * else delete the character after it. */
+static void DelChar(Textbuf *tb, bool backspace)
 {
-	tb->width -= GetCharacterWidth(FS_NORMAL, (byte)tb->buf[tb->caretpos]);
-	memmove(tb->buf + tb->caretpos, tb->buf + tb->caretpos + 1, tb->length - tb->caretpos);
-	tb->length--;
+	WChar c;
+	uint width;
+	size_t len;
+
+	if (backspace) {
+		do {
+			tb->caretpos--;
+		} while (IsUtf8Part(*(tb->buf + tb->caretpos)));
+	}
+
+	len = Utf8Decode(&c, tb->buf + tb->caretpos);
+	width = GetCharacterWidth(FS_NORMAL, c);
+
+	tb->width  -= width;
+	if (backspace) tb->caretxoffs -= width;
+
+	/* Move the remaining characters over the marker */
+	memmove(tb->buf + tb->caretpos, tb->buf + tb->caretpos + len, tb->length - tb->caretpos - len + 1);
+	tb->length -= len;
 }
 
 /**
@@ -799,13 +818,10 @@
 bool DeleteTextBufferChar(Textbuf *tb, int delmode)
 {
 	if (delmode == WKC_BACKSPACE && tb->caretpos != 0) {
-		tb->caretpos--;
-		tb->caretxoffs -= GetCharacterWidth(FS_NORMAL, (byte)tb->buf[tb->caretpos]);
-
-		DelChar(tb);
+		DelChar(tb, true);
 		return true;
 	} else if (delmode == WKC_DELETE && tb->caretpos < tb->length) {
-		DelChar(tb);
+		DelChar(tb, false);
 		return true;
 	}
 
@@ -831,16 +847,17 @@
  * @param key Character to be inserted
  * @return Return true on successfull change of Textbuf, or false otherwise
  */
-bool InsertTextBufferChar(Textbuf *tb, byte key)
+bool InsertTextBufferChar(Textbuf *tb, WChar key)
 {
 	const byte charwidth = GetCharacterWidth(FS_NORMAL, key);
-	if (tb->length < (tb->maxlength - 1) && (tb->maxwidth == 0 || tb->width + charwidth <= tb->maxwidth)) {
-		memmove(tb->buf + tb->caretpos + 1, tb->buf + tb->caretpos, (tb->length - tb->caretpos) + 1);
-		tb->buf[tb->caretpos] = key;
-		tb->length++;
-		tb->width += charwidth;
+	size_t len = Utf8CharLen(key);
+	if (tb->length < (tb->maxlength - len) && (tb->maxwidth == 0 || tb->width + charwidth <= tb->maxwidth)) {
+		memmove(tb->buf + tb->caretpos + len, tb->buf + tb->caretpos, tb->length - tb->caretpos + 1);
+		Utf8Encode(tb->buf + tb->caretpos, key);
+		tb->length += len;
+		tb->width  += charwidth;
 
-		tb->caretpos++;
+		tb->caretpos   += len;
 		tb->caretxoffs += charwidth;
 		return true;
 	}
@@ -859,15 +876,25 @@
 	switch (navmode) {
 	case WKC_LEFT:
 		if (tb->caretpos != 0) {
-			tb->caretpos--;
-			tb->caretxoffs -= GetCharacterWidth(FS_NORMAL, (byte)tb->buf[tb->caretpos]);
+			WChar c;
+
+			do {
+				tb->caretpos--;
+			} while (IsUtf8Part(*(tb->buf + tb->caretpos)));
+
+			Utf8Decode(&c, tb->buf + tb->caretpos);
+			tb->caretxoffs -= GetCharacterWidth(FS_NORMAL, c);
+
 			return true;
 		}
 		break;
 	case WKC_RIGHT:
 		if (tb->caretpos < tb->length) {
-			tb->caretxoffs += GetCharacterWidth(FS_NORMAL, (byte)tb->buf[tb->caretpos]);
-			tb->caretpos++;
+			WChar c;
+
+			tb->caretpos   += Utf8Decode(&c, tb->buf + tb->caretpos);
+			tb->caretxoffs += GetCharacterWidth(FS_NORMAL, c);
+
 			return true;
 		}
 		break;
@@ -910,16 +937,16 @@
  */
 void UpdateTextBufferSize(Textbuf *tb)
 {
-	const char *buf;
+	const char *buf = tb->buf;
+	WChar c = Utf8Consume(&buf);
 
-	tb->length = 0;
 	tb->width = 0;
 
-	for (buf = tb->buf; *buf != '\0' && tb->length < (tb->maxlength - 1); buf++) {
-		tb->length++;
-		tb->width += GetCharacterWidth(FS_NORMAL, (byte)*buf);
+	for (; c != '\0' && tb->length < (tb->maxlength - 1); c = Utf8Consume(&buf)) {
+		tb->width += GetCharacterWidth(FS_NORMAL, c);
 	}
 
+	tb->length = buf - tb->buf - 1;
 	tb->caretpos = tb->length;
 	tb->caretxoffs = tb->width;
 }
@@ -948,9 +975,10 @@
 			InvalidateWidget(w, wid);
 		break;
 	default:
-		if (IsValidAsciiChar(e->we.keypress.ascii, string->afilter)) {
-			if (InsertTextBufferChar(&string->text, e->we.keypress.ascii))
+		if (IsValidChar(e->we.keypress.key, string->afilter)) {
+			if (InsertTextBufferChar(&string->text, e->we.keypress.key)) {
 				InvalidateWidget(w, wid);
+			}
 		} else { // key wasn't caught. Continue only if standard entry specified
 			e->we.keypress.cont = (string->afilter == CS_ALPHANUMERAL);
 		}
--- a/namegen.c	Thu Nov 16 20:57:23 2006 +0000
+++ b/namegen.c	Thu Nov 16 22:05:33 2006 +0000
@@ -480,13 +480,15 @@
 
 		strecat(buf, name_czech_adj[prefix].name, last);
 		endpos = strlen(buf) - 1;
+		/* Find the first character in a UTF-8 sequence */
+		while (GB(buf[endpos], 6, 2) == 2) endpos--;
 		if (gender == CZG_SMASC && pattern == CZP_PRIVL) {
 			/* -ovX -> -uv */
 			buf[endpos - 2] = 'u';
 			assert(buf[endpos - 1] == 'v');
 			buf[endpos] = '\0';
 		} else {
-			buf[endpos] = name_czech_patmod[gender][pattern];
+			strecpy(buf + endpos, name_czech_patmod[gender][pattern], last);
 		}
 
 		strecat(buf, " ", last);
--- a/network.c	Thu Nov 16 20:57:23 2006 +0000
+++ b/network.c	Thu Nov 16 22:05:33 2006 +0000
@@ -526,7 +526,7 @@
 		if (*p == '#') {
 			*p = '\0';
 			*player = ++p;
-			while (IsValidAsciiChar(*p, CS_NUMERAL)) p++;
+			while (IsValidChar(*p, CS_NUMERAL)) p++;
 			if (*p == '\0') break;
 		} else if (*p == ':') {
 			*port = p + 1;
--- a/newgrf.c	Thu Nov 16 20:57:23 2006 +0000
+++ b/newgrf.c	Thu Nov 16 22:05:33 2006 +0000
@@ -23,6 +23,7 @@
 #include "vehicle.h"
 #include "newgrf_text.h"
 #include "table/sprites.h"
+#include "fontcache.h"
 #include "date.h"
 #include "currency.h"
 #include "sound.h"
@@ -3039,6 +3040,42 @@
 	}
 }
 
+/* Action 0x12 */
+static void LoadFontGlyph(byte *buf, int len)
+{
+	/* <12> <num_def> <font_size> <num_char> <base_char>
+	 *
+	 * B num_def      Number of definitions
+	 * B font_size    Size of font (0 = normal, 1 = small, 2 = large)
+	 * B num_char     Number of consecutive glyphs
+	 * W base_char    First character index */
+
+	uint8 num_def;
+	uint i;
+
+	buf++; len--;
+	check_length(len, 1, "LoadFontGlyph");
+
+	num_def = grf_load_byte(&buf);
+
+	check_length(len, 1 + num_def * 4, "LoadFontGlyph");
+
+	for (i = 0; i < num_def; i++) {
+		FontSize size    = grf_load_byte(&buf);
+		uint8  num_char  = grf_load_byte(&buf);
+		uint16 base_char = grf_load_word(&buf);
+		uint c;
+
+		DEBUG(grf, 7) ("LoadFontGlyph: Loading %u glyph(s) at 0x%04X for size %u", num_char, base_char, size);
+
+		for (c = 0; c < num_char; c++) {
+			SetUnicodeGlyph(size, base_char + c, _cur_spriteid);
+			LoadNextSprite(_cur_spriteid++, _file_index);
+			_nfo_line++;
+		}
+	}
+}
+
 /* 'Action 0xFF' */
 static void GRFDataBlock(byte *buf, int len)
 {
@@ -3421,6 +3458,7 @@
 		/* 0x0F */ { NULL,            NULL,       NULL, },
 		/* 0x10 */ { DefineGotoLabel, NULL,       NULL, },
 		/* 0x11 */ { NULL,            NULL,       GRFSound, },
+		/* 0x12 */ { NULL,            NULL,       LoadFontGlyph, },
 	};
 
 	byte* buf;
--- a/newgrf_text.c	Thu Nov 16 20:57:23 2006 +0000
+++ b/newgrf_text.c	Thu Nov 16 22:05:33 2006 +0000
@@ -18,6 +18,7 @@
 #include "macros.h"
 #include "table/strings.h"
 #include "newgrf_text.h"
+#include "table/control_codes.h"
 
 #define GRFTAB  28
 #define TABSIZE 11
@@ -153,46 +154,104 @@
 static byte _currentLangID = GRFLX_ENGLISH;  //by default, english is used.
 
 
-static void TranslateTTDPatchCodes(char *str)
+static char *TranslateTTDPatchCodes(const char *str)
 {
-	char *c;
+	char *tmp = malloc(strlen(str) * 10); /* Allocate space to allow for expansion */
+	char *d = tmp;
+	bool unicode = false;
+	WChar c = Utf8Consume(&str);
 
-	for (c = str; *c != '\0'; c++) {
-		switch ((byte)*c) {
-			case 0x01: c++; break;
-			case 0x0D: *c = 10; break;
-			case 0x0E: *c = 8; break;
-			case 0x0F: *c = 9; break;
-			case 0x1F: *c = 2; c += 2; break;
+	if (c == 0x00DE) {
+		/* The thorn ('þ') indicates a unicode string to TTDPatch */
+		unicode = true;
+	} else {
+		str--;
+	}
+
+	for (;;) {
+		const char *tmp = str; /* Used for UTF-8 decoding */
+
+		c = (byte)*str++;
+		if (c == 0) break;
+
+		switch (c) {
+			case 0x01:
+				d += Utf8Encode(d, SCC_SETX);
+				*d++ = *str++;
+				break;
+			case 0x0D: *d++ = 10; break;
+			case 0x0E: d += Utf8Encode(d, SCC_TINYFONT); break;
+			case 0x0F: d += Utf8Encode(d, SCC_BIGFONT); break;
+			case 0x1F:
+				d += Utf8Encode(d, SCC_SETXY);
+				*d++ = *str++;
+				*d++ = *str++;
+				break;
 			case 0x7B:
 			case 0x7C:
 			case 0x7D:
-			case 0x7E: *c = 0x8E; break;
-			case 0x81: c += 2; break;
-			case 0x85: *c = 0x86; break;
-			case 0x88: *c = 15; break;
-			case 0x89: *c = 16; break;
-			case 0x8A: *c = 17; break;
-			case 0x8B: *c = 18; break;
-			case 0x8C: *c = 19; break;
-			case 0x8D: *c = 20; break;
-			case 0x8E: *c = 21; break;
-			case 0x8F: *c = 22; break;
-			case 0x90: *c = 23; break;
-			case 0x91: *c = 24; break;
-			case 0x92: *c = 25; break;
-			case 0x93: *c = 26; break;
-			case 0x94: *c = 27; break;
-			case 0x95: *c = 28; break;
-			case 0x96: *c = 29; break;
-			case 0x97: *c = 30; break;
-			case 0x98: *c = 31; break;
+			case 0x7E: d += Utf8Encode(d, SCC_NUM); break;
+			case 0x7F: d += Utf8Encode(d, SCC_CURRENCY); break;
+			case 0x80: d += Utf8Encode(d, SCC_STRING); break;
+			case 0x81: {
+				StringID string;
+				string  = *str++;
+				string |= *str++ << 8;
+				d += Utf8Encode(d, SCC_STRING_ID);
+				d += Utf8Encode(d, string);
+				break;
+			}
+			case 0x82: d += Utf8Encode(d, SCC_DATE_TINY); break;
+			case 0x83: d += Utf8Encode(d, SCC_DATE_SHORT); break;
+			case 0x84: d += Utf8Encode(d, SCC_VELOCITY); break;
+			case 0x85: d += Utf8Encode(d, SCC_SKIP);    break;
+			case 0x86: /* "Rotate down top 4 words on stack" */ break;
+			case 0x87: d += Utf8Encode(d, SCC_VOLUME);  break;
+			case 0x88: d += Utf8Encode(d, SCC_BLUE);    break;
+			case 0x89: d += Utf8Encode(d, SCC_SILVER);  break;
+			case 0x8A: d += Utf8Encode(d, SCC_GOLD);    break;
+			case 0x8B: d += Utf8Encode(d, SCC_RED);     break;
+			case 0x8C: d += Utf8Encode(d, SCC_PURPLE);  break;
+			case 0x8D: d += Utf8Encode(d, SCC_LTBROWN); break;
+			case 0x8E: d += Utf8Encode(d, SCC_ORANGE);  break;
+			case 0x8F: d += Utf8Encode(d, SCC_GREEN);   break;
+			case 0x90: d += Utf8Encode(d, SCC_YELLOW);  break;
+			case 0x91: d += Utf8Encode(d, SCC_DKGREEN); break;
+			case 0x92: d += Utf8Encode(d, SCC_CREAM);   break;
+			case 0x93: d += Utf8Encode(d, SCC_BROWN);   break;
+			case 0x94: d += Utf8Encode(d, SCC_WHITE);   break;
+			case 0x95: d += Utf8Encode(d, SCC_LTBLUE);  break;
+			case 0x96: d += Utf8Encode(d, SCC_GRAY);    break;
+			case 0x97: d += Utf8Encode(d, SCC_DKBLUE);  break;
+			case 0x98: d += Utf8Encode(d, SCC_BLACK);   break;
+			case 0x9E: d += Utf8Encode(d, 0x20AC); break; // Euro
+			case 0x9F: d += Utf8Encode(d, 0x0178); break; // Y with diaeresis
+			case 0xA0: d += Utf8Encode(d, SCC_UPARROW); break;
+			case 0xAA: d += Utf8Encode(d, SCC_DOWNARROW); break;
+			case 0xAC: d += Utf8Encode(d, SCC_CHECKMARK); break;
+			case 0xAD: d += Utf8Encode(d, SCC_CROSS); break;
+			case 0xAF: d += Utf8Encode(d, SCC_RIGHTARROW); break;
+			case 0xB4: d += Utf8Encode(d, SCC_TRAIN); break;
+			case 0xB5: d += Utf8Encode(d, SCC_LORRY); break;
+			case 0xB6: d += Utf8Encode(d, SCC_BUS); break;
+			case 0xB7: d += Utf8Encode(d, SCC_PLANE); break;
+			case 0xB8: d += Utf8Encode(d, SCC_SHIP); break;
 			default:
+				if (unicode) {
+					d += Utf8Encode(d, Utf8Consume(&tmp));
+					str = tmp;
+					break;
+				}
+
 				/* Validate any unhandled character */
-				if (!IsValidAsciiChar(*c, CS_ALPHANUMERAL)) *c = '?';
+				if (!IsValidChar(c, CS_ALPHANUMERAL)) c = '?';
+				d += Utf8Encode(d, c);
 				break;
 		}
 	}
+
+	*d = '\0';
+	return realloc(tmp, strlen(tmp) + 1);
 }
 
 
@@ -201,6 +260,7 @@
  */
 StringID AddGRFString(uint32 grfid, uint16 stringid, byte langid_to_add, bool new_scheme, const char *text_to_add, StringID def_string)
 {
+	char *translatedtext;
 	GRFText *newtext;
 	uint id;
 
@@ -231,12 +291,14 @@
 	/* Too many strings allocated, return empty */
 	if (id == lengthof(_grf_text)) return STR_EMPTY;
 
-	newtext = malloc(sizeof(*newtext) + strlen(text_to_add) + 1);
+	translatedtext = TranslateTTDPatchCodes(text_to_add);
+
+	newtext = malloc(sizeof(*newtext) + strlen(translatedtext) + 1);
 	newtext->next   = NULL;
 	newtext->langid = langid_to_add;
-	strcpy(newtext->text, text_to_add);
+	strcpy(newtext->text, translatedtext);
 
-	TranslateTTDPatchCodes(newtext->text);
+	free(translatedtext);
 
 	/* If we didn't find our stringid and grfid in the list, allocate a new id */
 	if (id == _num_grf_texts) _num_grf_texts++;
--- a/news_gui.c	Thu Nov 16 20:57:23 2006 +0000
+++ b/news_gui.c	Thu Nov 16 22:05:33 2006 +0000
@@ -15,6 +15,7 @@
 #include "sound.h"
 #include "variables.h"
 #include "date.h"
+#include "string.h"
 
 /* News system
  * News system is realized as a FIFO queue (in an array)
@@ -569,7 +570,8 @@
 static void DrawNewsString(int x, int y, uint16 color, const NewsItem *ni, uint maxw)
 {
 	char buffer[512], buffer2[512];
-	char *ptr, *dest;
+	const char *ptr;
+	char *dest;
 	StringID str;
 
 	if (ni->display_mode == 3) {
@@ -582,12 +584,16 @@
 	GetString(buffer, str, lastof(buffer));
 	/* Copy the just gotten string to another buffer to remove any formatting
 	 * from it such as big fonts, etc. */
-	for (ptr = buffer, dest = buffer2; *ptr != '\0'; ptr++) {
-		if (*ptr == '\r') {
+	ptr  = buffer;
+	dest = buffer2;
+	for (;;) {
+		WChar c = Utf8Consume(&ptr);
+		if (c == 0) break;
+		if (c == '\r') {
 			dest[0] = dest[1] = dest[2] = dest[3] = ' ';
 			dest += 4;
-		} else if ((byte)*ptr >= ' ' && ((byte)*ptr < 0x88 || (byte)*ptr >= 0x99)) {
-			*dest++ = *ptr;
+		} else if (IsPrintable(c)) {
+			dest += Utf8Encode(dest, c);
 		}
 	}
 
--- a/openttd.c	Thu Nov 16 20:57:23 2006 +0000
+++ b/openttd.c	Thu Nov 16 22:05:33 2006 +0000
@@ -52,6 +52,7 @@
 #include "genworld.h"
 #include "date.h"
 #include "clear_map.h"
+#include "fontcache.h"
 
 #include <stdarg.h>
 
@@ -432,10 +433,15 @@
 	MxInitialize(11025);
 	SoundInitialize("sample.cat");
 
+	/* Initialize FreeType */
+	InitFreeType();
+
 	// This must be done early, since functions use the InvalidateWindow* calls
 	InitWindowSystem();
 
 	GfxLoadSprites();
+	/* Initialize the unicode to sprite mapping table */
+	InitializeUnicodeGlyphMap();
 	LoadStringWidthTable();
 
 	DEBUG(driver, 1) ("Loading drivers...");
@@ -1526,5 +1532,9 @@
 		}
 	}
 
+	if (CheckSavegameVersion(37)) {
+		ConvertNameArray();
+	}
+
 	return true;
 }
--- a/openttd.h	Thu Nov 16 20:57:23 2006 +0000
+++ b/openttd.h	Thu Nov 16 22:05:33 2006 +0000
@@ -464,6 +464,10 @@
 	EXPENSES_OTHER        = 12,
 };
 
+enum {
+	MAX_LANG = 64,
+};
+
 // special string constants
 enum SpecialStrings {
 
@@ -506,17 +510,17 @@
 	SPECSTR_PRESIDENT_NAME     = 0x70E7,
 	SPECSTR_SONGNAME           = 0x70E8,
 
-	// reserve 32 strings for the *.lng files
+	// reserve MAX_LANG strings for the *.lng files
 	SPECSTR_LANGUAGE_START     = 0x7100,
-	SPECSTR_LANGUAGE_END       = 0x711f,
+	SPECSTR_LANGUAGE_END       = SPECSTR_LANGUAGE_START + MAX_LANG - 1,
 
 	// reserve 32 strings for various screen resolutions
-	SPECSTR_RESOLUTION_START   = 0x7120,
-	SPECSTR_RESOLUTION_END     = 0x713f,
+	SPECSTR_RESOLUTION_START   = SPECSTR_LANGUAGE_END + 1,
+	SPECSTR_RESOLUTION_END     = SPECSTR_RESOLUTION_START + 0x1F,
 
 	// reserve 32 strings for screenshot formats
-	SPECSTR_SCREENSHOT_START   = 0x7140,
-	SPECSTR_SCREENSHOT_END     = 0x715F,
+	SPECSTR_SCREENSHOT_START   = SPECSTR_RESOLUTION_END + 1,
+	SPECSTR_SCREENSHOT_END     = SPECSTR_SCREENSHOT_START + 0x1F,
 
 	// Used to implement SetDParamStr
 	STR_SPEC_DYNSTRING         = 0xF800,
--- a/openttd.vcproj	Thu Nov 16 20:57:23 2006 +0000
+++ b/openttd.vcproj	Thu Nov 16 22:05:33 2006 +0000
@@ -235,6 +235,9 @@
 				RelativePath=".\fios.c">
 			</File>
 			<File
+				RelativePath=".\fontcache.c">
+			</File>
+			<File
 				RelativePath=".\genworld.c">
 			</File>
 			<File
@@ -479,6 +482,9 @@
 				RelativePath=".\fileio.h">
 			</File>
 			<File
+				RelativePath=".\fontcache.h">
+			</File>
+			<File
 				RelativePath=".\functions.h">
 			</File>
 			<File
@@ -854,6 +860,9 @@
 				RelativePath=".\table\clear_land.h">
 			</File>
 			<File
+				RelativePath=".\table\control_codes.h">
+			</File>
+			<File
 				RelativePath=".\table\elrail_data.h">
 			</File>
 			<File
@@ -911,6 +920,9 @@
 				RelativePath=".\table\tunnel_land.h">
 			</File>
 			<File
+				RelativePath=".\table\unicode.h">
+			</File>
+			<File
 				RelativePath=".\table\unmovable_land.h">
 			</File>
 			<File
--- a/openttd_vs80.vcproj	Thu Nov 16 20:57:23 2006 +0000
+++ b/openttd_vs80.vcproj	Thu Nov 16 22:05:33 2006 +0000
@@ -561,6 +561,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\fontcache.c"
+				>
+			</File>
+			<File
 				RelativePath=".\genworld.c"
 				>
 			</File>
@@ -932,6 +936,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\fontcache.h"
+				>
+			</File>
+			<File
 				RelativePath=".\functions.h"
 				>
 			</File>
@@ -1436,6 +1444,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\table\control_codes.h"
+				>
+			</File>
+			<File
 				RelativePath=".\table\elrail_data.h"
 				>
 			</File>
@@ -1512,6 +1524,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\table\unicode.h"
+				>
+			</File>
+			<File
 				RelativePath=".\table\unmovable_land.h"
 				>
 			</File>
--- a/saveload.c	Thu Nov 16 20:57:23 2006 +0000
+++ b/saveload.c	Thu Nov 16 22:05:33 2006 +0000
@@ -30,7 +30,7 @@
 #include "variables.h"
 #include <setjmp.h>
 
-const uint16 SAVEGAME_VERSION = 36;
+const uint16 SAVEGAME_VERSION = 37;
 uint16 _sl_version;       /// the major savegame version identifier
 byte   _sl_minor_version; /// the minor savegame version, DO NOT USE!
 
--- a/settings.c	Thu Nov 16 20:57:23 2006 +0000
+++ b/settings.c	Thu Nov 16 22:05:33 2006 +0000
@@ -38,6 +38,10 @@
 #include "newgrf.h"
 #include "genworld.h"
 #include "date.h"
+#ifdef WITH_FREETYPE
+#include "gfx.h"
+#include "fontcache.h"
+#endif
 
 /** The patch values that are used for new games and/or modified in config file */
 Patches _patches_newgame;
@@ -1186,6 +1190,14 @@
 	  SDTG_STR("screenshot_format",SLE_STRB, S, 0, _screenshot_format_name,NULL,    STR_NULL, NULL),
 	  SDTG_STR("savegame_format",  SLE_STRB, S, 0, _savegame_format,       NULL,    STR_NULL, NULL),
 	 SDTG_BOOL("rightclick_emulate",         S, 0, _rightclick_emulate,   false,    STR_NULL, NULL),
+#ifdef WITH_FREETYPE
+	  SDTG_STR("small_font",       SLE_STRB, S, 0, _freetype.small_font,   NULL,    STR_NULL, NULL),
+	  SDTG_STR("medium_font",      SLE_STRB, S, 0, _freetype.medium_font,  NULL,    STR_NULL, NULL),
+	  SDTG_STR("large_font",       SLE_STRB, S, 0, _freetype.large_font,   NULL,    STR_NULL, NULL),
+	  SDTG_VAR("small_size",       SLE_UINT, S, 0, _freetype.small_size,   6, 0, 72, 0, STR_NULL, NULL),
+	  SDTG_VAR("medium_size",      SLE_UINT, S, 0, _freetype.medium_size, 10, 0, 72, 0, STR_NULL, NULL),
+	  SDTG_VAR("large_size",       SLE_UINT, S, 0, _freetype.large_size,  16, 0, 72, 0, STR_NULL, NULL),
+#endif
 	  SDTG_END()
 };
 
--- a/strgen/strgen.c	Thu Nov 16 20:57:23 2006 +0000
+++ b/strgen/strgen.c	Thu Nov 16 22:05:33 2006 +0000
@@ -3,6 +3,7 @@
 #include "../stdafx.h"
 #include "../macros.h"
 #include "../string.h"
+#include "../table/control_codes.h"
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
@@ -199,26 +200,41 @@
 }
 
 
-static void EmitSingleByte(char *buf, int value)
+static void PutUtf8(uint32 value)
 {
-	if (*buf != '\0') warning("Ignoring trailing letters in command");
-	PutByte((byte)value);
+	if (value < 0x80) {
+		PutByte(value);
+	} else if (value < 0x800) {
+		PutByte(0xC0 + GB(value,  6, 5));
+		PutByte(0x80 + GB(value,  0, 6));
+	} else if (value < 0x10000) {
+		PutByte(0xE0 + GB(value, 12, 4));
+		PutByte(0x80 + GB(value,  6, 6));
+		PutByte(0x80 + GB(value,  0, 6));
+	} else if (value < 0x110000) {
+		PutByte(0xF0 + GB(value, 18, 3));
+		PutByte(0x80 + GB(value, 12, 6));
+		PutByte(0x80 + GB(value,  6, 6));
+		PutByte(0x80 + GB(value,  0, 6));
+	} else {
+		warning("Invalid unicode value U+0x%X\n", value);
+	}
 }
 
 
-static void EmitEscapedByte(char *buf, int value)
+static void EmitSingleChar(char *buf, int value)
 {
 	if (*buf != '\0') warning("Ignoring trailing letters in command");
-	PutByte(0x85);
-	PutByte((byte)value);
+	PutUtf8(value);
 }
 
+
 static void EmitSetX(char *buf, int value)
 {
 	char *err;
 	int x = strtol(buf, &err, 0);
 	if (*err != 0) fatal("SetX param invalid");
-	PutByte(1);
+	PutUtf8(SCC_SETX);
 	PutByte((byte)x);
 }
 
@@ -234,7 +250,7 @@
 	y = strtol(err + 1, &err, 0);
 	if (*err != 0) fatal("SetXY param invalid");
 
-	PutByte(2);
+	PutUtf8(SCC_SETXY);
 	PutByte((byte)x);
 	PutByte((byte)y);
 }
@@ -352,7 +368,7 @@
 		}
 	}
 
-	PutByte(0x8D);
+	PutUtf8(SCC_PLURAL_LIST);
 	PutByte(TranslateArgumentIdx(argidx));
 	EmitWordList(words, nw);
 }
@@ -372,7 +388,7 @@
 			if (strcmp(buf, _genders[nw]) == 0) break;
 		}
 		// now nw contains the gender index
-		PutByte(0x87);
+		PutUtf8(SCC_GENDER_INDEX);
 		PutByte(nw);
 	} else {
 		const char* words[8];
@@ -386,8 +402,7 @@
 			if (words[nw] == NULL) break;
 		}
 		if (nw != _numgenders) fatal("Bad # of arguments for gender command");
-		PutByte(0x85);
-		PutByte(13);
+		PutUtf8(SCC_GENDER_LIST);
 		PutByte(TranslateArgumentIdx(argidx));
 		EmitWordList(words, nw);
 	}
@@ -396,109 +411,108 @@
 
 static const CmdStruct _cmd_structs[] = {
 	// Update position
-	{"SETX",  EmitSetX,  1, 0, 0},
-	{"SETXY", EmitSetXY, 2, 0, 0},
+	{"SETX",  EmitSetX,  SCC_SETX,  0, 0},
+	{"SETXY", EmitSetXY, SCC_SETXY, 0, 0},
 
 	// Font size
-	{"TINYFONT", EmitSingleByte, 8, 0, 0},
-	{"BIGFONT",  EmitSingleByte, 9, 0, 0},
+	{"TINYFONT", EmitSingleChar, SCC_TINYFONT, 0, 0},
+	{"BIGFONT",  EmitSingleChar, SCC_BIGFONT,  0, 0},
 
 	// Colors
-	{"BLUE",    EmitSingleByte, 15, 0, 0},
-	{"SILVER",  EmitSingleByte, 16, 0, 0},
-	{"GOLD",    EmitSingleByte, 17, 0, 0},
-	{"RED",     EmitSingleByte, 18, 0, 0},
-	{"PURPLE",  EmitSingleByte, 19, 0, 0},
-	{"LTBROWN", EmitSingleByte, 20, 0, 0},
-	{"ORANGE",  EmitSingleByte, 21, 0, 0},
-	{"GREEN",   EmitSingleByte, 22, 0, 0},
-	{"YELLOW",  EmitSingleByte, 23, 0, 0},
-	{"DKGREEN", EmitSingleByte, 24, 0, 0},
-	{"CREAM",   EmitSingleByte, 25, 0, 0},
-	{"BROWN",   EmitSingleByte, 26, 0, 0},
-	{"WHITE",   EmitSingleByte, 27, 0, 0},
-	{"LTBLUE",  EmitSingleByte, 28, 0, 0},
-	{"GRAY",    EmitSingleByte, 29, 0, 0},
-	{"DKBLUE",  EmitSingleByte, 30, 0, 0},
-	{"BLACK",   EmitSingleByte, 31, 0, 0},
+	{"BLUE",    EmitSingleChar, SCC_BLUE,    0, 0},
+	{"SILVER",  EmitSingleChar, SCC_SILVER,  0, 0},
+	{"GOLD",    EmitSingleChar, SCC_GOLD,    0, 0},
+	{"RED",     EmitSingleChar, SCC_RED,     0, 0},
+	{"PURPLE",  EmitSingleChar, SCC_PURPLE,  0, 0},
+	{"LTBROWN", EmitSingleChar, SCC_LTBROWN, 0, 0},
+	{"ORANGE",  EmitSingleChar, SCC_ORANGE,  0, 0},
+	{"GREEN",   EmitSingleChar, SCC_GREEN,   0, 0},
+	{"YELLOW",  EmitSingleChar, SCC_YELLOW,  0, 0},
+	{"DKGREEN", EmitSingleChar, SCC_DKGREEN, 0, 0},
+	{"CREAM",   EmitSingleChar, SCC_CREAM,   0, 0},
+	{"BROWN",   EmitSingleChar, SCC_BROWN,   0, 0},
+	{"WHITE",   EmitSingleChar, SCC_WHITE,   0, 0},
+	{"LTBLUE",  EmitSingleChar, SCC_LTBLUE,  0, 0},
+	{"GRAY",    EmitSingleChar, SCC_GRAY,    0, 0},
+	{"DKBLUE",  EmitSingleChar, SCC_DKBLUE,  0, 0},
+	{"BLACK",   EmitSingleChar, SCC_BLACK,   0, 0},
 
-	{"CURRCOMPACT",   EmitEscapedByte, 0, 1, 0}, // compact currency (32 bits)
-	{"REV",           EmitEscapedByte, 2, 0, 0}, // openttd revision string
-	{"SHORTCARGO",    EmitEscapedByte, 3, 2, 0}, // short cargo description, only ### tons, or ### litres
-	{"CURRCOMPACT64", EmitEscapedByte, 4, 2, 0}, // compact currency 64 bits
+	{"CURRCOMPACT",   EmitSingleChar, SCC_CURRENCY_COMPACT,    1, 0}, // compact currency (32 bits)
+	{"REV",           EmitSingleChar, SCC_REVISION,            0, 0}, // openttd revision string
+	{"SHORTCARGO",    EmitSingleChar, SCC_CARGO_SHORT,         2, 0}, // short cargo description, only ### tons, or ### litres
+	{"CURRCOMPACT64", EmitSingleChar, SCC_CURRENCY_COMPACT_64, 2, 0}, // compact currency 64 bits
 
 	// These are special versions of {STRING1}
 	// The first string includes the second string.
-	{"COMPANY",    EmitEscapedByte, 5, 1, 0},
-	{"PLAYERNAME", EmitEscapedByte, 5, 1, 0},
-	{"VEHICLE",    EmitEscapedByte, 5, 1, 0},
-
-	{"STRING1", EmitEscapedByte, 5, 1, C_CASE}, // included string that consumes ONE argument
-	{"STRING2", EmitEscapedByte, 6, 2, C_CASE}, // included string that consumes TWO arguments
-	{"STRING3", EmitEscapedByte, 7, 3, C_CASE}, // included string that consumes THREE arguments
-	{"STRING4", EmitEscapedByte, 8, 4, C_CASE}, // included string that consumes FOUR arguments
-	{"STRING5", EmitEscapedByte, 9, 5, C_CASE}, // included string that consumes FIVE arguments
+	{"COMPANY",    EmitSingleChar, SCC_STRING1, 1, 0},
+	{"PLAYERNAME", EmitSingleChar, SCC_STRING1, 1, 0},
+	{"VEHICLE",    EmitSingleChar, SCC_STRING1, 1, 0},
 
-	{"STATIONFEATURES", EmitEscapedByte, 10, 1, 0}, // station features string, icons of the features
-	{"INDUSTRY",        EmitEscapedByte, 11, 1, 0}, // industry, takes an industry #
-	{"VOLUME",          EmitEscapedByte, 12, 1, 0},
-	{"DATE_TINY",       EmitEscapedByte, 14, 1, 0},
-	{"CARGO",           EmitEscapedByte, 15, 2, 0},
-	{"POWER",           EmitEscapedByte, 16, 1, 0},
-	{"VOLUME_S",        EmitEscapedByte, 17, 1, 0},
-	{"WEIGHT",          EmitEscapedByte, 18, 1, 0},
-	{"WEIGHT_S",        EmitEscapedByte, 19, 1, 0},
-	{"FORCE",           EmitEscapedByte, 20, 1, 0},
+	{"STRING1", EmitSingleChar, SCC_STRING1, 1, C_CASE}, // included string that consumes ONE argument
+	{"STRING2", EmitSingleChar, SCC_STRING2, 2, C_CASE}, // included string that consumes TWO arguments
+	{"STRING3", EmitSingleChar, SCC_STRING3, 3, C_CASE}, // included string that consumes THREE arguments
+	{"STRING4", EmitSingleChar, SCC_STRING4, 4, C_CASE}, // included string that consumes FOUR arguments
+	{"STRING5", EmitSingleChar, SCC_STRING5, 5, C_CASE}, // included string that consumes FIVE arguments
+
+	{"STATIONFEATURES", EmitSingleChar, SCC_STATION_FEATURES, 1, 0}, // station features string, icons of the features
+	{"INDUSTRY",        EmitSingleChar, SCC_INDUSTRY_NAME,    1, 0}, // industry, takes an industry #
+	{"CARGO",           EmitSingleChar, SCC_CARGO,            2, 0},
+	{"POWER",           EmitSingleChar, SCC_POWER,            1, 0},
+	{"VOLUME",          EmitSingleChar, SCC_VOLUME,           1, 0},
+	{"VOLUME_S",        EmitSingleChar, SCC_VOLUME_SHORT,     1, 0},
+	{"WEIGHT",          EmitSingleChar, SCC_WEIGHT,           1, 0},
+	{"WEIGHT_S",        EmitSingleChar, SCC_WEIGHT_SHORT,     1, 0},
+	{"FORCE",           EmitSingleChar, SCC_FORCE,            1, 0},
+	{"VELOCITY",        EmitSingleChar, SCC_VELOCITY,         1, 0},
 
 	{"P", EmitPlural, 0, 0, C_DONTCOUNT}, // plural specifier
 	{"G", EmitGender, 0, 0, C_DONTCOUNT}, // gender specifier
 
-	{"DATE_LONG",  EmitSingleByte, 0x82, 1, 0},
-	{"DATE_SHORT", EmitSingleByte, 0x83, 1, 0},
-
-	{"VELOCITY", EmitSingleByte, 0x84, 1, 0},
+	{"DATE_TINY",  EmitSingleChar, SCC_DATE_TINY, 1, 0},
+	{"DATE_SHORT", EmitSingleChar, SCC_DATE_SHORT, 1, 0},
+	{"DATE_LONG",  EmitSingleChar, SCC_DATE_LONG, 1, 0},
 
-	// 0x85 is the marker for escaped commands
+	{"SKIP", EmitSingleChar, SCC_SKIP, 1, 0},
 
-	{"SKIP", EmitSingleByte, 0x86, 1, 0},
-
-	{"STRING", EmitSingleByte, 0x88, 1, C_CASE},
+	{"STRING", EmitSingleChar, SCC_STRING, 1, C_CASE},
 
 	// Numbers
-	{"COMMA", EmitSingleByte, 0x8B, 1, 0}, // Number with comma
-	{"NUM",   EmitSingleByte, 0x8E, 1, 0}, // Signed number
+	{"COMMA", EmitSingleChar, SCC_COMMA, 1, 0}, // Number with comma
+	{"NUM",   EmitSingleChar, SCC_NUM,   1, 0}, // Signed number
 
-	{"CURRENCY",   EmitSingleByte, 0x8F, 1, 0},
-	{"CURRENCY64", EmitSingleByte, 0x9C, 2, 0},
+	{"CURRENCY",   EmitSingleChar, SCC_CURRENCY,    1, 0},
+	{"CURRENCY64", EmitSingleChar, SCC_CURRENCY_64, 2, 0},
 
-	{"WAYPOINT", EmitSingleByte, 0x99, 1, 0}, // waypoint name
-	{"STATION",  EmitSingleByte, 0x9A, 1, 0},
-	{"TOWN",     EmitSingleByte, 0x9B, 1, 0},
+	{"WAYPOINT", EmitSingleChar, SCC_WAYPOINT_NAME, 1, 0}, // waypoint name
+	{"STATION",  EmitSingleChar, SCC_STATION_NAME,  1, 0},
+	{"TOWN",     EmitSingleChar, SCC_TOWN_NAME,     1, 0},
 
 	// 0x9D is used for the pseudo command SETCASE
 	// 0x9E is used for case switching
 
-	{"",               EmitSingleByte, '\n', 0, C_DONTCOUNT},
-	{"{",              EmitSingleByte, '{',  0, C_DONTCOUNT},
-	{"UPARROW",        EmitSingleByte, 0x80, 0, 0},
-	{"SMALLUPARROW",   EmitSingleByte, 0x90, 0, 0},
-	{"SMALLDOWNARROW", EmitSingleByte, 0x91, 0, 0},
-	{"TRAIN",          EmitSingleByte, 0x94, 0, 0},
-	{"LORRY",          EmitSingleByte, 0x95, 0, 0},
-	{"BUS",            EmitSingleByte, 0x96, 0, 0},
-	{"PLANE",          EmitSingleByte, 0x97, 0, 0},
-	{"SHIP",           EmitSingleByte, 0x98, 0, 0},
-	{"NBSP",           EmitSingleByte, 0xA0, 0, C_DONTCOUNT},
-	{"CENT",           EmitSingleByte, '',  0, C_DONTCOUNT},
-	{"POUNDSIGN",      EmitSingleByte, '',  0, C_DONTCOUNT},
-	{"EURO",           EmitSingleByte, '',  0, C_DONTCOUNT},
-	{"YENSIGN",        EmitSingleByte, '',  0, C_DONTCOUNT},
-	{"COPYRIGHT",      EmitSingleByte, '',  0, C_DONTCOUNT},
-	{"DOWNARROW",      EmitSingleByte, 0xAA, 0, C_DONTCOUNT},
-	{"CHECKMARK",      EmitSingleByte, 0xAC, 0, C_DONTCOUNT},
-	{"CROSS",          EmitSingleByte, 0xAD, 0, C_DONTCOUNT},
-	{"REGISTERED",     EmitSingleByte, '',  0, C_DONTCOUNT},
-	{"RIGHTARROW",     EmitSingleByte, 0xAF, 0, C_DONTCOUNT},
+	{"",               EmitSingleChar, '\n',               0, C_DONTCOUNT},
+	{"{",              EmitSingleChar, '{',                0, C_DONTCOUNT},
+	{"UPARROW",        EmitSingleChar, SCC_UPARROW,        0, 0},
+	{"SMALLUPARROW",   EmitSingleChar, SCC_SMALLUPARROW,   0, 0},
+	{"SMALLDOWNARROW", EmitSingleChar, SCC_SMALLDOWNARROW, 0, 0},
+	{"TRAIN",          EmitSingleChar, SCC_TRAIN,          0, 0},
+	{"LORRY",          EmitSingleChar, SCC_LORRY,          0, 0},
+	{"BUS",            EmitSingleChar, SCC_BUS,            0, 0},
+	{"PLANE",          EmitSingleChar, SCC_PLANE,          0, 0},
+	{"SHIP",           EmitSingleChar, SCC_SHIP,           0, 0},
+	{"NBSP",           EmitSingleChar, 0xA0,               0, C_DONTCOUNT},
+	{"CENT",           EmitSingleChar, 0xA2,               0, C_DONTCOUNT},
+	{"POUNDSIGN",      EmitSingleChar, 0xA3,               0, C_DONTCOUNT},
+	{"EURO",           EmitSingleChar, 0x20AC,             0, C_DONTCOUNT},
+	{"YENSIGN",        EmitSingleChar, 0xA5,               0, C_DONTCOUNT},
+	{"COPYRIGHT",      EmitSingleChar, 0xA9,               0, C_DONTCOUNT},
+	{"DOWNARROW",      EmitSingleChar, SCC_DOWNARROW,      0, C_DONTCOUNT},
+	{"CHECKMARK",      EmitSingleChar, SCC_CHECKMARK,      0, C_DONTCOUNT},
+	{"CROSS",          EmitSingleChar, SCC_CROSS,          0, C_DONTCOUNT},
+	{"REGISTERED",     EmitSingleChar, 0xAE,               0, C_DONTCOUNT},
+	{"RIGHTARROW",     EmitSingleChar, SCC_RIGHTARROW,     0, C_DONTCOUNT},
+	{"SMALLLEFTARROW", EmitSingleChar, SCC_LESSTHAN,       0, C_DONTCOUNT},
+	{"SMALLRIGHTARROW",EmitSingleChar, SCC_GREATERTHAN,    0, C_DONTCOUNT},
 };
 
 
@@ -1028,7 +1042,7 @@
 
 static void PutArgidxCommand(void)
 {
-	PutByte(0x8C);
+	PutUtf8(SCC_ARG_INDEX);
 	PutByte(TranslateArgumentIdx(_cur_argidx));
 }
 
@@ -1052,7 +1066,7 @@
 		if (cs == NULL) break;
 
 		if (casei != -1) {
-			PutByte(0x9D); // {SETCASE}
+			PutUtf8(SCC_SETCASE); // {SETCASE}
 			PutByte(casei);
 		}
 
@@ -1163,7 +1177,7 @@
 				// It has this format
 				// <0x9E> <NUM CASES> <CASE1> <LEN1> <STRING1> <CASE2> <LEN2> <STRING2> <CASE3> <LEN3> <STRING3> <STRINGDEFAULT>
 				// Each LEN is printed using 2 bytes in big endian order.
-				PutByte(0x9E);
+				PutUtf8(SCC_SWITCH_CASE);
 				// Count the number of cases
 				for (num = 0, c = casep; c; c = c->next) num++;
 				PutByte(num);
--- a/string.c	Thu Nov 16 20:57:23 2006 +0000
+++ b/string.c	Thu Nov 16 22:05:33 2006 +0000
@@ -4,6 +4,8 @@
 #include "openttd.h"
 #include "functions.h"
 #include "string.h"
+#include "macros.h"
+#include "table/control_codes.h"
 
 #include <stdarg.h>
 #include <ctype.h> // required for tolower()
@@ -68,8 +70,27 @@
 
 void str_validate(char *str)
 {
-	for (; *str != '\0'; str++)
-		if (!IsValidAsciiChar(*str, CS_ALPHANUMERAL)) *str = '?';
+	char *dst = str;
+	WChar c;
+	size_t len = Utf8Decode(&c, str);
+
+	for (; c != '\0'; len = Utf8Decode(&c, str)) {
+		if (IsPrintable(c) && (c < SCC_SPRITE_START || c > SCC_SPRITE_END ||
+			IsValidChar(c - SCC_SPRITE_START, CS_ALPHANUMERAL))) {
+			/* Copy the character back. Even if dst is current the same as str
+			 * (i.e. no characters have been changed) this is quicker than
+			 * moving the pointers ahead by len */
+			do {
+				*dst++ = *str++;
+			} while (--len);
+		} else {
+			/* Replace the undesirable character with a question mark */
+			str += len;
+			*dst++ = '?';
+		}
+	}
+
+	*dst = '\0';
 }
 
 void str_strip_colours(char *str)
@@ -92,29 +113,15 @@
  * @param afilter the filter to use
  * @return true or false depending if the character is printable/valid or not
  */
-bool IsValidAsciiChar(byte key, CharSetFilter afilter)
+bool IsValidChar(WChar key, CharSetFilter afilter)
 {
-	bool firsttest = false;
-
 	switch (afilter) {
-		case CS_ALPHANUMERAL:
-			firsttest = (key >= ' ' && key < 127);
-			break;
-
-		/* We are very strict here */
-		case CS_NUMERAL:
-			return (key >= '0' && key <= '9');
-
-		case CS_ALPHA:
-		default:
-			firsttest = ((key >= 'A' && key <= 'Z') || (key >= 'a' && key <= 'z'));
-			break;
+		case CS_ALPHANUMERAL: return IsPrintable(key);
+		case CS_NUMERAL:      return (key >= '0' && key <= '9');
+		case CS_ALPHA:        return IsPrintable(key) && !(key >= '0' && key <= '9');
 	}
 
-	/* Allow some special chars too that are non-ASCII but still valid (like '^' above 'a') */
-	return (firsttest || (key >= 160 &&
-		key != 0xAA && key != 0xAC && key != 0xAD && key != 0xAF &&
-		key != 0xB5 && key != 0xB6 && key != 0xB7 && key != 0xB9));
+	return false;
 }
 
 void strtolower(char *str)
@@ -145,3 +152,78 @@
 #endif /* _MSC_VER */
 
 #endif /* WIN32 */
+
+
+/* UTF-8 handling routines */
+
+
+/* Decode and consume the next UTF-8 encoded character
+ * @param c Buffer to place decoded character.
+ * @param s Character stream to retrieve character from.
+ * @return Number of characters in the sequence.
+ */
+size_t Utf8Decode(WChar *c, const char *s)
+{
+	assert(c != NULL);
+
+	if (!HASBIT(s[0], 7)) {
+		/* Single byte character: 0xxxxxxx */
+		*c = s[0];
+		return 1;
+	} else if (GB(s[0], 5, 3) == 6) {
+		if (IsUtf8Part(s[1])) {
+			/* Double byte character: 110xxxxx 10xxxxxx */
+			*c = GB(s[0], 0, 5) << 6 | GB(s[1], 0, 6);
+			if (*c >= 0x80) return 2;
+		}
+	} else if (GB(s[0], 4, 4) == 14) {
+		if (IsUtf8Part(s[1]) && IsUtf8Part(s[2])) {
+			/* Triple byte character: 1110xxxx 10xxxxxx 10xxxxxx */
+			*c = GB(s[0], 0, 4) << 12 | GB(s[1], 0, 6) << 6 | GB(s[2], 0, 6);
+			if (*c >= 0x800) return 3;
+		}
+	} else if (GB(s[0], 3, 5) == 30) {
+		if (IsUtf8Part(s[1]) && IsUtf8Part(s[2]) && IsUtf8Part(s[3])) {
+			/* 4 byte character: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
+			*c = GB(s[0], 0, 3) << 18 | GB(s[1], 0, 6) << 12 | GB(s[2], 0, 6) << 6 | GB(s[3], 0, 6);
+			if (*c >= 0x10000 && *c <= 0x10FFFF) return 4;
+		}
+	}
+
+	//DEBUG(misc, 1) ("Invalid UTF-8 sequence");
+	*c = '?';
+	return 1;
+}
+
+
+/* Encode a unicode character and place it in the buffer
+ * @param buf Buffer to place character.
+ * @param c   Unicode character to encode.
+ * @return Number of characters in the encoded sequence.
+ */
+size_t Utf8Encode(char *buf, WChar c)
+{
+	if (c < 0x80) {
+		*buf = c;
+		return 1;
+	} else if (c < 0x800) {
+		*buf++ = 0xC0 + GB(c,  6, 5);
+		*buf   = 0x80 + GB(c,  0, 6);
+		return 2;
+	} else if (c < 0x10000) {
+		*buf++ = 0xE0 + GB(c, 12, 4);
+		*buf++ = 0x80 + GB(c,  6, 6);
+		*buf   = 0x80 + GB(c,  0, 6);
+		return 3;
+	} else if (c < 0x110000) {
+		*buf++ = 0xF0 + GB(c, 18, 3);
+		*buf++ = 0x80 + GB(c, 12, 6);
+		*buf++ = 0x80 + GB(c,  6, 6);
+		*buf   = 0x80 + GB(c,  0, 6);
+		return 4;
+	}
+
+	//DEBUG(misc, 1) ("Can't UTF-8 encode value 0x%X", c);
+	*buf = '?';
+	return 1;
+}
--- a/string.h	Thu Nov 16 20:57:23 2006 +0000
+++ b/string.h	Thu Nov 16 22:05:33 2006 +0000
@@ -3,6 +3,8 @@
 #ifndef STRING_H
 #define STRING_H
 
+#include "macros.h"
+
 /*
  * dst: destination buffer
  * src: string to copy/concatenate
@@ -33,7 +35,7 @@
 void str_strip_colours(char *str);
 
 /**
- * Valid filter types for IsValidAsciiChar.
+ * Valid filter types for IsValidChar.
  */
 typedef enum CharSetFilter {
 	CS_ALPHANUMERAL,      //! Both numeric and alphabetic and spaces and stuff
@@ -41,6 +43,11 @@
 	CS_ALPHA,             //! Only alphabetic values
 } CharSetFilter;
 
+/** Convert the given string to lowercase */
+void strtolower(char *str);
+
+typedef uint32 WChar;
+
 /**
  * Only allow certain keys. You can define the filter to be used. This makes
  *  sure no invalid keys can get into an editbox, like BELL.
@@ -48,9 +55,50 @@
  * @param afilter the filter to use
  * @return true or false depending if the character is printable/valid or not
  */
-bool IsValidAsciiChar(byte key, CharSetFilter afilter);
+bool IsValidChar(WChar key, CharSetFilter afilter);
 
-/** Convert the given string to lowercase */
-void strtolower(char *str);
+size_t Utf8Decode(WChar *c, const char *s);
+size_t Utf8Encode(char *buf, WChar c);
+
+
+static inline WChar Utf8Consume(const char **s)
+{
+	WChar c;
+	*s += Utf8Decode(&c, *s);
+	return c;
+}
+
+
+/** Return the length of a UTF-8 encoded character.
+ * @param c Unicode character.
+ * @return Length of UTF-8 encoding for character.
+ */
+static inline size_t Utf8CharLen(WChar c)
+{
+	if (c < 0x80)       return 1;
+	if (c < 0x800)      return 2;
+	if (c < 0x10000)    return 3;
+	if (c < 0x110000)   return 4;
+
+	/* Invalid valid, we encode as a '?' */
+	return 1;
+}
+
+
+/* Check if the given character is part of a UTF8 sequence */
+static inline bool IsUtf8Part(char c)
+{
+	return GB(c, 6, 2) == 2;
+}
+
+
+static inline bool IsPrintable(WChar c)
+{
+	if (c < 0x20)   return false;
+	if (c < 0xE000) return true;
+	if (c < 0xE200) return false;
+	return true;
+}
+
 
 #endif /* STRING_H */
--- a/strings.c	Thu Nov 16 20:57:23 2006 +0000
+++ b/strings.c	Thu Nov 16 22:05:33 2006 +0000
@@ -18,6 +18,7 @@
 #include "variables.h"
 #include "newgrf_text.h"
 #include "table/landscape_const.h"
+#include "table/control_codes.h"
 #include "music.h"
 #include "date.h"
 #include "industry.h"
@@ -236,6 +237,14 @@
 }
 
 
+char *InlineString(char *buf, StringID string)
+{
+	buf += Utf8Encode(buf, SCC_STRING_ID);
+	buf += Utf8Encode(buf, string);
+	return buf;
+}
+
+
 // This function takes a C-string and allocates a temporary string ID.
 // The duration of the bound string is valid only until the next GetString,
 // so be careful.
@@ -564,54 +573,57 @@
 static char* FormatString(char* buff, const char* str, const int32* argv, uint casei, const char* last)
 {
 	extern const char _openttd_revision[];
-	byte b;
+	WChar b;
 	const int32 *argv_orig = argv;
 	uint modifier = 0;
 
-	while ((b = *str++) != '\0') {
+	while ((b = Utf8Consume(&str)) != '\0') {
 		switch (b) {
-		case 0x1: // {SETX}
-			if (buff != last && buff + 1 != last) {
-				*buff++ = b;
-				*buff++ = *str++;
-			}
-			break;
-		case 0x2: // {SETXY}
-			if (buff != last && buff + 1 != last && buff + 2 != last) {
-				*buff++ = b;
-				*buff++ = *str++;
-				*buff++ = *str++;
-			}
-			break;
+			case SCC_SETX: // {SETX}
+				if (buff + Utf8CharLen(SCC_SETX) + 1 < last) {
+					buff += Utf8Encode(buff, SCC_SETX);
+					*buff++ = *str++;
+				}
+				break;
 
-		case 0x81: // {STRINL}
-			buff = GetStringWithArgs(buff, ReadLE16Unaligned(str), argv, last);
-			str += 2;
-			break;
-		case 0x82: // {DATE_LONG}
-			buff = FormatYmdString(buff, GetInt32(&argv), last);
-			break;
-		case 0x83: // {DATE_SHORT}
-			buff = FormatMonthAndYear(buff, GetInt32(&argv), last);
-			break;
-		case 0x84: {// {VELOCITY}
-			int32 args[1];
-			assert(_opt_ptr->units < lengthof(units));
-			args[0] = GetInt32(&argv) * units[_opt_ptr->units].s_m >> units[_opt_ptr->units].s_s;
-			buff = FormatString(buff, GetStringPtr(units[_opt_ptr->units].velocity), args, modifier >> 24, last);
-			modifier = 0;
-			break;
-		}
-		// 0x85 is used as escape character..
-		case 0x85:
-			switch (*str++) {
-			case 0: /* {CURRCOMPACT} */
+			case SCC_SETXY: // {SETXY}
+				if (buff + Utf8CharLen(SCC_SETXY) + 2 < last) {
+					buff += Utf8Encode(buff, SCC_SETXY);
+					*buff++ = *str++;
+					*buff++ = *str++;
+				}
+				break;
+
+			case SCC_STRING_ID: // {STRINL}
+				buff = GetStringWithArgs(buff, Utf8Consume(&str), argv, last);
+				break;
+
+			case SCC_DATE_LONG: // {DATE_LONG}
+				buff = FormatYmdString(buff, GetInt32(&argv), last);
+				break;
+
+			case SCC_DATE_SHORT: // {DATE_SHORT}
+				buff = FormatMonthAndYear(buff, GetInt32(&argv), last);
+				break;
+
+			case SCC_VELOCITY: {// {VELOCITY}
+				int32 args[1];
+				assert(_opt_ptr->units < lengthof(units));
+				args[0] = GetInt32(&argv) * units[_opt_ptr->units].s_m >> units[_opt_ptr->units].s_s;
+				buff = FormatString(buff, GetStringPtr(units[_opt_ptr->units].velocity), args, modifier >> 24, last);
+				modifier = 0;
+				break;
+			}
+
+			case SCC_CURRENCY_COMPACT: /* {CURRCOMPACT} */
 				buff = FormatGenericCurrency(buff, _currency, GetInt32(&argv), true, last);
 				break;
-			case 2: /* {REV} */
+
+			case SCC_REVISION: /* {REV} */
 				buff = strecpy(buff, _openttd_revision, last);
 				break;
-			case 3: { /* {SHORTCARGO} */
+
+			case SCC_CARGO_SHORT: { /* {SHORTCARGO} */
 				// Short description of cargotypes. Layout:
 				// 8-bit = cargo type
 				// 16-bit = cargo count
@@ -642,40 +654,46 @@
 						break;
 				}
 			} break;
-			case 4: {/* {CURRCOMPACT64} */
+
+			case SCC_CURRENCY_COMPACT_64: { /* {CURRCOMPACT64} */
 				// 64 bit compact currency-unit
 				buff = FormatGenericCurrency(buff, _currency, GetInt64(&argv), true, last);
 				break;
 			}
-			case 5: { /* {STRING1} */
+
+			case SCC_STRING1: { /* {STRING1} */
 				// String that consumes ONE argument
 				uint str = modifier + GetInt32(&argv);
 				buff = GetStringWithArgs(buff, str, GetArgvPtr(&argv, 1), last);
 				modifier = 0;
 				break;
 			}
-			case 6: { /* {STRING2} */
+
+			case SCC_STRING2: { /* {STRING2} */
 				// String that consumes TWO arguments
 				uint str = modifier + GetInt32(&argv);
 				buff = GetStringWithArgs(buff, str, GetArgvPtr(&argv, 2), last);
 				modifier = 0;
 				break;
 			}
-			case 7: { /* {STRING3} */
+
+			case SCC_STRING3: { /* {STRING3} */
 				// String that consumes THREE arguments
 				uint str = modifier + GetInt32(&argv);
 				buff = GetStringWithArgs(buff, str, GetArgvPtr(&argv, 3), last);
 				modifier = 0;
 				break;
 			}
-			case 8: { /* {STRING4} */
+
+			case SCC_STRING4: { /* {STRING4} */
 				// String that consumes FOUR arguments
 				uint str = modifier + GetInt32(&argv);
 				buff = GetStringWithArgs(buff, str, GetArgvPtr(&argv, 4), last);
 				modifier = 0;
 				break;
 			}
-			case 9: { /* {STRING5} */
+
+			case SCC_STRING5: { /* {STRING5} */
 				// String that consumes FIVE arguments
 				uint str = modifier + GetInt32(&argv);
 				buff = GetStringWithArgs(buff, str, GetArgvPtr(&argv, 5), last);
@@ -683,12 +701,12 @@
 				break;
 			}
 
-			case 10: { /* {STATIONFEATURES} */
+			case SCC_STATION_FEATURES: { /* {STATIONFEATURES} */
 				buff = StationGetSpecialString(buff, GetInt32(&argv), last);
 				break;
 			}
 
-			case 11: { /* {INDUSTRY} */
+			case SCC_INDUSTRY_NAME: { /* {INDUSTRY} */
 				const Industry* i = GetIndustry(GetInt32(&argv));
 				int32 args[2];
 
@@ -704,7 +722,7 @@
 				break;
 			}
 
-			case 12: { // {VOLUME}
+			case SCC_VOLUME: { // {VOLUME}
 				int32 args[1];
 				assert(_opt_ptr->units < lengthof(units));
 				args[0] = GetInt32(&argv) * units[_opt_ptr->units].v_m >> units[_opt_ptr->units].v_s;
@@ -713,22 +731,22 @@
 				break;
 			}
 
-			case 13: { // {G 0 Der Die Das}
-				const byte* s = (const byte*)GetStringPtr(argv_orig[(byte)*str++]); // contains the string that determines gender.
+			case SCC_GENDER_LIST: { // {G 0 Der Die Das}
+				const char* s = GetStringPtr(argv_orig[(byte)*str++]); // contains the string that determines gender.
 				int len;
 				int gender = 0;
-				if (s != NULL && s[0] == 0x87) gender = s[1];
+				if (s != NULL && Utf8Consume(&s) == SCC_GENDER_INDEX) gender = (byte)s[0];
 				str = ParseStringChoice(str, gender, buff, &len);
 				buff += len;
 				break;
 			}
 
-			case 14: { // {DATE_TINY}
+			case SCC_DATE_TINY: { // {DATE_TINY}
 				buff = FormatTinyDate(buff, GetInt32(&argv), last);
 				break;
 			}
 
-			case 15: { // {CARGO}
+			case SCC_CARGO: { // {CARGO}
 				// Layout now is:
 				//   8bit   - cargo type
 				//   16-bit - cargo count
@@ -738,7 +756,7 @@
 				break;
 			}
 
-			case 16: { // {POWER}
+			case SCC_POWER: { // {POWER}
 				int32 args[1];
 				assert(_opt_ptr->units < lengthof(units));
 				args[0] = GetInt32(&argv) * units[_opt_ptr->units].p_m >> units[_opt_ptr->units].p_s;
@@ -747,7 +765,7 @@
 				break;
 			}
 
-			case 17: { // {VOLUME_S}
+			case SCC_VOLUME_SHORT: { // {VOLUME_S}
 				int32 args[1];
 				assert(_opt_ptr->units < lengthof(units));
 				args[0] = GetInt32(&argv) * units[_opt_ptr->units].v_m >> units[_opt_ptr->units].v_s;
@@ -756,7 +774,7 @@
 				break;
 			}
 
-			case 18: { // {WEIGHT}
+			case SCC_WEIGHT: { // {WEIGHT}
 				int32 args[1];
 				assert(_opt_ptr->units < lengthof(units));
 				args[0] = GetInt32(&argv) * units[_opt_ptr->units].w_m >> units[_opt_ptr->units].w_s;
@@ -765,7 +783,7 @@
 				break;
 			}
 
-			case 19: { // {WEIGHT_S}
+			case SCC_WEIGHT_SHORT: { // {WEIGHT_S}
 				int32 args[1];
 				assert(_opt_ptr->units < lengthof(units));
 				args[0] = GetInt32(&argv) * units[_opt_ptr->units].w_m >> units[_opt_ptr->units].w_s;
@@ -774,7 +792,7 @@
 				break;
 			}
 
-			case 20: { // {FORCE}
+			case SCC_FORCE: { // {FORCE}
 				int32 args[1];
 				assert(_opt_ptr->units < lengthof(units));
 				args[0] = GetInt32(&argv) * units[_opt_ptr->units].f_m >> units[_opt_ptr->units].f_s;
@@ -783,124 +801,122 @@
 				break;
 			}
 
-			default:
-				error("!invalid escape sequence in string");
-			}
-			break;
-
-		case 0x86: // {SKIP}
-			argv++;
-			break;
-
-		// This sets up the gender for the string.
-		// We just ignore this one. It's used in {G 0 Der Die Das} to determine the case.
-		case 0x87: // {GENDER 0}
-			str++;
-			break;
-
-		case 0x88: {// {STRING}
-			uint str = modifier + GetInt32(&argv);
-			// WARNING. It's prohibited for the included string to consume any arguments.
-			// For included strings that consume argument, you should use STRING1, STRING2 etc.
-			// To debug stuff you can set argv to NULL and it will tell you
-			buff = GetStringWithArgs(buff, str, argv, last);
-			modifier = 0;
-			break;
-		}
-
-		case 0x8B: // {COMMA}
-			buff = FormatCommaNumber(buff, GetInt32(&argv), last);
-			break;
-
-		case 0x8C: // Move argument pointer
-			argv = argv_orig + (byte)*str++;
-			break;
-
-		case 0x8D: { // {P}
-			int32 v = argv_orig[(byte)*str++]; // contains the number that determines plural
-			int len;
-			str = ParseStringChoice(str, DeterminePluralForm(v), buff, &len);
-			buff += len;
-			break;
-		}
-
-		case 0x8E: // {NUM}
-			buff = FormatNoCommaNumber(buff, GetInt32(&argv), last);
-			break;
-
-		case 0x8F: // {CURRENCY}
-			buff = FormatGenericCurrency(buff, _currency, GetInt32(&argv), false, last);
-			break;
+			case SCC_SKIP: // {SKIP}
+				argv++;
+				break;
 
-		case 0x99: { // {WAYPOINT}
-			int32 temp[2];
-			Waypoint *wp = GetWaypoint(GetInt32(&argv));
-			StringID str;
-			if (wp->string != STR_NULL) {
-				str = wp->string;
-			} else {
-				temp[0] = wp->town_index;
-				temp[1] = wp->town_cn + 1;
-				str = wp->town_cn == 0 ? STR_WAYPOINTNAME_CITY : STR_WAYPOINTNAME_CITY_SERIAL;
-			}
-			buff = GetStringWithArgs(buff, str, temp, last);
-		} break;
-
-		case 0x9A: { // {STATION}
-			const Station* st = GetStation(GetInt32(&argv));
-
-			if (!IsValidStation(st)) { // station doesn't exist anymore
-				buff = GetStringWithArgs(buff, STR_UNKNOWN_DESTINATION, NULL, last);
-			} else {
-				int32 temp[2];
-				temp[0] = st->town->townnametype;
-				temp[1] = st->town->townnameparts;
-				buff = GetStringWithArgs(buff, st->string_id, temp, last);
-			}
-			break;
-		}
-		case 0x9B: { // {TOWN}
-			const Town* t = GetTown(GetInt32(&argv));
-			int32 temp[1];
-
-			assert(IsValidTown(t));
+			// This sets up the gender for the string.
+			// We just ignore this one. It's used in {G 0 Der Die Das} to determine the case.
+			case SCC_GENDER_INDEX: // {GENDER 0}
+				str++;
+				break;
 
-			temp[0] = t->townnameparts;
-			buff = GetStringWithArgs(buff, t->townnametype, temp, last);
-			break;
-		}
-
-		case 0x9C: { // {CURRENCY64}
-			buff = FormatGenericCurrency(buff, _currency, GetInt64(&argv), false, last);
-			break;
-		}
-
-		case 0x9D: { // {SETCASE}
-			// This is a pseudo command, it's outputted when someone does {STRING.ack}
-			// The modifier is added to all subsequent GetStringWithArgs that accept the modifier.
-			modifier = (byte)*str++ << 24;
-			break;
-		}
+			case SCC_STRING: {// {STRING}
+				uint str = modifier + GetInt32(&argv);
+				// WARNING. It's prohibited for the included string to consume any arguments.
+				// For included strings that consume argument, you should use STRING1, STRING2 etc.
+				// To debug stuff you can set argv to NULL and it will tell you
+				buff = GetStringWithArgs(buff, str, argv, last);
+				modifier = 0;
+				break;
+			}
 
-		case 0x9E: { // {Used to implement case switching}
-			// <0x9E> <NUM CASES> <CASE1> <LEN1> <STRING1> <CASE2> <LEN2> <STRING2> <CASE3> <LEN3> <STRING3> <STRINGDEFAULT>
-			// Each LEN is printed using 2 bytes in big endian order.
-			uint num = (byte)*str++;
-			while (num) {
-				if ((byte)str[0] == casei) {
-					// Found the case, adjust str pointer and continue
-					str += 3;
-					break;
+			case SCC_COMMA: // {COMMA}
+				buff = FormatCommaNumber(buff, GetInt32(&argv), last);
+				break;
+
+			case SCC_ARG_INDEX: // Move argument pointer
+				argv = argv_orig + (byte)*str++;
+				break;
+
+			case SCC_PLURAL_LIST: { // {P}
+				int32 v = argv_orig[(byte)*str++]; // contains the number that determines plural
+				int len;
+				str = ParseStringChoice(str, DeterminePluralForm(v), buff, &len);
+				buff += len;
+				break;
+			}
+
+			case SCC_NUM: // {NUM}
+				buff = FormatNoCommaNumber(buff, GetInt32(&argv), last);
+				break;
+
+			case SCC_CURRENCY: // {CURRENCY}
+				buff = FormatGenericCurrency(buff, _currency, GetInt32(&argv), false, last);
+				break;
+
+			case SCC_WAYPOINT_NAME: { // {WAYPOINT}
+				int32 temp[2];
+				Waypoint *wp = GetWaypoint(GetInt32(&argv));
+				StringID str;
+				if (wp->string != STR_NULL) {
+					str = wp->string;
+				} else {
+					temp[0] = wp->town_index;
+					temp[1] = wp->town_cn + 1;
+					str = wp->town_cn == 0 ? STR_WAYPOINTNAME_CITY : STR_WAYPOINTNAME_CITY_SERIAL;
 				}
-				// Otherwise skip to the next case
-				str += 3 + (str[1] << 8) + str[2];
-				num--;
+				buff = GetStringWithArgs(buff, str, temp, last);
+				break;
 			}
-			break;
-		}
 
-		default:
-			if (buff != last) *buff++ = b;
+			case SCC_STATION_NAME: { // {STATION}
+				const Station* st = GetStation(GetInt32(&argv));
+
+				if (!IsValidStation(st)) { // station doesn't exist anymore
+					buff = GetStringWithArgs(buff, STR_UNKNOWN_DESTINATION, NULL, last);
+				} else {
+					int32 temp[2];
+					temp[0] = st->town->townnametype;
+					temp[1] = st->town->townnameparts;
+					buff = GetStringWithArgs(buff, st->string_id, temp, last);
+				}
+				break;
+			}
+
+			case SCC_TOWN_NAME: { // {TOWN}
+				const Town* t = GetTown(GetInt32(&argv));
+				int32 temp[1];
+
+				assert(IsValidTown(t));
+
+				temp[0] = t->townnameparts;
+				buff = GetStringWithArgs(buff, t->townnametype, temp, last);
+				break;
+			}
+
+			case SCC_CURRENCY_64: { // {CURRENCY64}
+				buff = FormatGenericCurrency(buff, _currency, GetInt64(&argv), false, last);
+				break;
+			}
+
+			case SCC_SETCASE: { // {SETCASE}
+				// This is a pseudo command, it's outputted when someone does {STRING.ack}
+				// The modifier is added to all subsequent GetStringWithArgs that accept the modifier.
+				modifier = (byte)*str++ << 24;
+				break;
+			}
+
+			case SCC_SWITCH_CASE: { // {Used to implement case switching}
+				// <0x9E> <NUM CASES> <CASE1> <LEN1> <STRING1> <CASE2> <LEN2> <STRING2> <CASE3> <LEN3> <STRING3> <STRINGDEFAULT>
+				// Each LEN is printed using 2 bytes in big endian order.
+				uint num = (byte)*str++;
+				while (num) {
+					if ((byte)str[0] == casei) {
+						// Found the case, adjust str pointer and continue
+						str += 3;
+						break;
+					}
+					// Otherwise skip to the next case
+					str += 3 + (str[1] << 8) + str[2];
+					num--;
+				}
+				break;
+			}
+
+			default:
+				if (buff + Utf8CharLen(b) < last) buff += Utf8Encode(buff, b);
+				break;
 		}
 	}
 	*buff = '\0';
@@ -910,11 +926,12 @@
 
 static char *StationGetSpecialString(char *buff, int x, const char* last)
 {
-	if (x & 0x01) buff = strecpy(buff, "\x94", last);
-	if (x & 0x02) buff = strecpy(buff, "\x95", last);
-	if (x & 0x04) buff = strecpy(buff, "\x96", last);
-	if (x & 0x08) buff = strecpy(buff, "\x97", last);
-	if (x & 0x10) buff = strecpy(buff, "\x98", last);
+	if ((x & 0x01) && (buff + Utf8CharLen(SCC_TRAIN) < last)) buff += Utf8Encode(buff, SCC_TRAIN);
+	if ((x & 0x02) && (buff + Utf8CharLen(SCC_LORRY) < last)) buff += Utf8Encode(buff, SCC_LORRY);
+	if ((x & 0x04) && (buff + Utf8CharLen(SCC_BUS)   < last)) buff += Utf8Encode(buff, SCC_BUS);
+	if ((x & 0x08) && (buff + Utf8CharLen(SCC_PLANE) < last)) buff += Utf8Encode(buff, SCC_PLANE);
+	if ((x & 0x10) && (buff + Utf8CharLen(SCC_SHIP)  < last)) buff += Utf8Encode(buff, SCC_SHIP);
+	*buff = '\0';
 	return buff;
 }
 
@@ -1122,7 +1139,7 @@
 
 	{
 		char *lang = str_fmt("%s%s", _path.lang_dir, _dynlang.ent[lang_index].file);
-		lang_pack = ReadFileToMem(lang, &len, 100000);
+		lang_pack = ReadFileToMem(lang, &len, 200000);
 		free(lang);
 	}
 	if (lang_pack == NULL) return false;
@@ -1237,7 +1254,7 @@
 	int fallback;
 	LanguagePack hdr;
 	FILE *in;
-	char *files[32];
+	char *files[MAX_LANG];
 	const char* lang;
 
 	lang = GetCurrentLocale("LC_MESSAGES");
--- a/strings.h	Thu Nov 16 20:57:23 2006 +0000
+++ b/strings.h	Thu Nov 16 22:05:33 2006 +0000
@@ -3,14 +3,7 @@
 #ifndef STRINGS_H
 #define STRINGS_H
 
-static inline char* InlineString(char* buf, uint16 string)
-{
-	*buf++ = '\x81';
-	*buf++ = string & 0xFF;
-	*buf++ = string >> 8;
-	return buf;
-}
-
+char *InlineString(char *buf, uint16 string);
 char *GetString(char *buffr, uint16 string, const char* last);
 
 extern char _userstring[128];
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/table/control_codes.h	Thu Nov 16 22:05:33 2006 +0000
@@ -0,0 +1,105 @@
+/* $Id$ */
+
+#ifndef CONTROL_CODES_H
+#define CONTROL_CODES_H
+
+/* List of string control codes used for string formatting, displaying, and
+ * by strgen to generate the language files. */
+
+enum {
+	SCC_CONTROL_START = 0xE000,
+	SCC_CONTROL_END   = 0xE1FF,
+
+	SCC_SPRITE_START  = 0xE200,
+	SCC_SPRITE_END    = SCC_SPRITE_START + 0xFF,
+
+	/* Display control codes */
+	SCC_SETX = SCC_CONTROL_START,
+	SCC_SETXY,
+	SCC_TINYFONT,
+	SCC_BIGFONT,
+
+	/* Formatting control codes */
+	SCC_REVISION,
+	SCC_STATION_FEATURES,
+	SCC_INDUSTRY_NAME,
+	SCC_WAYPOINT_NAME,
+	SCC_STATION_NAME,
+	SCC_TOWN_NAME,
+
+	SCC_CURRENCY_COMPACT,
+	SCC_CURRENCY_COMPACT_64,
+	SCC_CURRENCY,
+	SCC_CURRENCY_64,
+
+	SCC_CARGO,
+	SCC_CARGO_SHORT,
+	SCC_POWER,
+	SCC_VOLUME,
+	SCC_VOLUME_SHORT,
+	SCC_WEIGHT,
+	SCC_WEIGHT_SHORT,
+	SCC_FORCE,
+	SCC_VELOCITY,
+
+	SCC_DATE_TINY,
+	SCC_DATE_SHORT,
+	SCC_DATE_LONG,
+
+	SCC_STRING1,
+	SCC_STRING2,
+	SCC_STRING3,
+	SCC_STRING4,
+	SCC_STRING5,
+
+	SCC_SKIP,
+	SCC_STRING,
+	SCC_COMMA,
+	SCC_NUM,
+
+	SCC_STRING_ID,
+	SCC_PLURAL_LIST,
+	SCC_GENDER_LIST,
+	SCC_GENDER_INDEX,
+	SCC_ARG_INDEX,
+	SCC_SETCASE,
+	SCC_SWITCH_CASE,
+
+	/* Colour codes */
+	SCC_BLUE,
+	SCC_SILVER,
+	SCC_GOLD,
+	SCC_RED,
+	SCC_PURPLE,
+	SCC_LTBROWN,
+	SCC_ORANGE,
+	SCC_GREEN,
+	SCC_YELLOW,
+	SCC_DKGREEN,
+	SCC_CREAM,
+	SCC_BROWN,
+	SCC_WHITE,
+	SCC_LTBLUE,
+	SCC_GRAY,
+	SCC_DKBLUE,
+	SCC_BLACK,
+
+	/* Special printable symbols.
+	 * These are mapped to the original glyphs */
+	SCC_LESSTHAN       = SCC_SPRITE_START + 0x3C,
+	SCC_GREATERTHAN    = SCC_SPRITE_START + 0x3E,
+	SCC_UPARROW        = SCC_SPRITE_START + 0x80,
+	SCC_SMALLUPARROW   = SCC_SPRITE_START + 0x90,
+	SCC_SMALLDOWNARROW = SCC_SPRITE_START + 0x91,
+	SCC_TRAIN          = SCC_SPRITE_START + 0x94,
+	SCC_LORRY          = SCC_SPRITE_START + 0x95,
+	SCC_BUS            = SCC_SPRITE_START + 0x96,
+	SCC_PLANE          = SCC_SPRITE_START + 0x97,
+	SCC_SHIP           = SCC_SPRITE_START + 0x98,
+	SCC_DOWNARROW      = SCC_SPRITE_START + 0xAA,
+	SCC_CHECKMARK      = SCC_SPRITE_START + 0xAC,
+	SCC_CROSS          = SCC_SPRITE_START + 0xAD,
+	SCC_RIGHTARROW     = SCC_SPRITE_START + 0xAF,
+};
+
+#endif /* CONTROL_CODES_H */
--- a/table/namegen.h	Thu Nov 16 20:57:23 2006 +0000
+++ b/table/namegen.h	Thu Nov 16 22:05:33 2006 +0000
@@ -304,7 +304,7 @@
 	"Aus",
 	"Alten",
 	"Braun",
-	"Vsl",
+	"Vösl",
 	"Mittern",
 	"Nuss",
 	"Neu",
@@ -326,9 +326,9 @@
 	"Frauen",
 	"Herren",
 	"Hof",
-	"Htt",
+	"Hütt",
 	"Kaisers",
-	"Knigs",
+	"Königs",
 	"Knittel",
 	"Lang",
 	"Ober",
@@ -340,7 +340,7 @@
 	"Stocker",
 	"Unter",
 	"Utten",
-	"Vsen",
+	"Vösen",
 	"Vill",
 	"Weissen"
 };
@@ -386,7 +386,7 @@
 
 static const char *name_austrian_a6[] = {
 	"Aegyd",
-	"Andr",
+	"Andrä",
 	"Georgen",
 	"Jakob",
 	"Johann",
@@ -399,7 +399,7 @@
 	"Nikolai",
 	"Oswald",
 	"Peter",
-	"Plten",
+	"Pölten",
 	"Stefan",
 	"Stephan",
 	"Thomas",
@@ -460,10 +460,10 @@
 	"Gera",
 	"Kassel",
 	"Kiel",
-	"Kln",
-	"Lbeck",
+	"Köln",
+	"Lübeck",
 	"Magdeburg",
-	"Mnchen",
+	"München",
 	"Potsdam",
 	"Stuttgart",
 	"Wiesbaden"
@@ -486,7 +486,7 @@
 	"Cloppen",
 	"Co",
 	"Duis",
-	"Dssel",
+	"Düssel",
 	"Dannen",
 	"Elb",
 	"Els",
@@ -500,7 +500,7 @@
 	"Frei",
 	"Freuden",
 	"Fried",
-	"Frsten",
+	"Fürsten",
 	"Hahn",
 	"Ham",
 	"Harz",
@@ -521,10 +521,10 @@
 	"Langen",
 	"Lim",
 	"Lohr",
-	"Lne",
+	"Lüne",
 	"Mel",
 	"Michels",
-	"Mhl",
+	"Mühl",
 	"Naum",
 	"Nest",
 	"Nord",
@@ -532,7 +532,7 @@
 	"Nien",
 	"Nidda",
 	"Nieder",
-	"Nrn",
+	"Nürn",
 	"Ober",
 	"Offen",
 	"Osna",
@@ -546,18 +546,18 @@
 	"Regens",
 	"Rott",
 	"Ros",
-	"Rssels",
+	"Rüssels",
 	"Saal",
 	"Saar",
 	"Salz",
-	"Schne",
+	"Schöne",
 	"Schwein",
 	"Sonder",
 	"Sonnen",
 	"Stein",
 	"Strals",
 	"Straus",
-	"Sd",
+	"Süd",
 	"Ton",
 	"Unter",
 	"Ur",
@@ -568,14 +568,14 @@
 	"Wester",
 	"Witten",
 	"Wolfs",
-	"Wrz"
+	"Würz"
 };
 
 static const char *name_german_2[] = {
 	"bach",
 	"berg",
-	"brck",
-	"brcken",
+	"brück",
+	"brücken",
 	"burg",
 	"dorf",
 	"feld",
@@ -585,7 +585,7 @@
 	"heim",
 	"horst",
 	"mund",
-	"mnster",
+	"münster",
 	"stadt",
 	"wald"
 };
@@ -717,7 +717,7 @@
 	"St. Tropez",
 	"Marseilles",
 	"Narbonne",
-	"Ste",
+	"Sète",
 	"Aurillac",
 	"Gueret",
 	"Le Creusot",
@@ -725,8 +725,8 @@
 	"Auxerre",
 	"Versailles",
 	"Meaux",
-	"Chlons",
-	"Compigne",
+	"Châlons",
+	"Compiègne",
 	"Metz",
 	"Chaumont",
 	"Langres",
@@ -739,8 +739,8 @@
 	"Le Mans",
 	"Angers",
 	"Nantes",
-	"Chteauroux",
-	"Orlans",
+	"Châteauroux",
+	"Orléans",
 	"Lisieux",
 	"Cherbourg",
 	"Morlaix",
@@ -751,7 +751,7 @@
 	"Troyes",
 	"Charolles",
 	"Grenoble",
-	"Chambry",
+	"Chambéry",
 	"Tours",
 	"St. Brieuc",
 	"St. Malo",
@@ -765,11 +765,11 @@
 	"Albi",
 	"St. Valery",
 	"Biarritz",
-	"Bziers",
-	"Nmes",
+	"Béziers",
+	"Nîmes",
 	"Chamonix",
-	"Angoulme",
-	"Alenon"
+	"Angoulème",
+	"Alençon"
 };
 
 static const char *name_silly_1[] = {
@@ -897,7 +897,7 @@
 	"Es",
 	"Fin",
 	"Fisk",
-	"Grn",
+	"Grön",
 	"Hag",
 	"Halm",
 	"Karl",
@@ -916,17 +916,17 @@
 	"Skog",
 	"Stock",
 	"Stor",
-	"Strm",
+	"Ström",
 	"Sund",
-	"Sder",
+	"Söder",
 	"Tall",
 	"Tratt",
 	"Troll",
 	"Upp",
 	"Var",
-	"Vster",
-	"ngel",
-	"ster"
+	"Väster",
+	"Ängel",
+	"Öster"
 };
 
 static const char *name_swedish_2a[] = {
@@ -981,9 +981,9 @@
 	"o",
 	"u",
 	"y",
-	"",
-	"",
-	""
+	"å",
+	"ä",
+	"ö"
 };
 
 static const char *name_swedish_2c[] = {
@@ -1029,25 +1029,25 @@
 	"hamn",
 	"holm",
 	"hus",
-	"httan",
+	"hättan",
 	"kulle",
-	"kping",
+	"köping",
 	"lund",
-	"lv",
+	"löv",
 	"sala",
 	"skrona",
-	"sltt",
-	"spng",
+	"slätt",
+	"spång",
 	"stad",
 	"sund",
 	"svall",
 	"svik",
-	"sker",
+	"såker",
 	"udde",
 	"valla",
 	"viken",
-	"lv",
-	"s"
+	"älv",
+	"ås"
 };
 
 static const char *name_dutch_1[] = {
@@ -1214,7 +1214,7 @@
 	"Espoo",
 	"Helsinki",
 	"Tapiola",
-	"Jrvel",
+	"Järvelä",
 	"Lahti",
 	"Kotka",
 	"Hamina",
@@ -1249,26 +1249,26 @@
 	"Sauna",
 	"Uusi",
 	"Vanha",
-	"Kes",
+	"Kesä",
 	"Kuusi",
 	"Pelto",
 	"Tuomi",
 	"Terva",
 	"Olki",
-	"Hein",
-	"Sein",
+	"Heinä",
+	"Seinä",
 	"Rova",
 	"Koivu",
 	"Kokko",
-	"Mnty",
+	"Mänty",
 	"Pihlaja",
-	"Petj",
+	"Petäjä",
 	"Kielo",
 	"Kauha",
 	"Viita",
 	"Kivi",
 	"Riihi",
-	"ne",
+	"Ääne",
 	"Niini"
 };
 
@@ -1277,27 +1277,27 @@
 	"Lohjan",
 	"Savon",
 	"Lapin",
-	"Pitjn",
+	"Pitäjän",
 	"Martin",
 	"Kuusan",
 	"Kemi",
 	"Keri",
-	"Hmeen",
+	"Hämeen",
 	"Kangas"
 };
 
 static const char *name_finnish_3[] = {
 	"harju",
 	"linna",
-	"jrvi",
+	"järvi",
 	"kallio",
-	"mki",
+	"mäki",
 	"nummi",
 	"joki",
-	"kyl",
+	"kylä",
 	"lampi",
 	"lahti",
-	"mets",
+	"metsä",
 	"suo",
 	"laakso",
 	"niitty",
@@ -1606,61 +1606,61 @@
 	"Blansko",
 	"Breclav",
 	"Brno",
-	"Bruntl",
-	"Cesk Lpa",
-	"Cesk Budejovice",
-	"Cesk Krumlov",
-	"Decn",
+	"Bruntál",
+	"Ceská Lípa",
+	"Ceské Budejovice",
+	"Ceský Krumlov",
+	"Decín",
 	"Domazlice",
-	"Dub",
-	"Frdek-Mstek",
-	"Havlckuv Brod",
-	"Hodonn",
-	"Hradec Krlov",
+	"Dubí",
+	"Frýdek-Místek",
+	"Havlíckuv Brod",
+	"Hodonín",
+	"Hradec Králové",
 	"Humpolec",
 	"Cheb",
 	"Chomutov",
 	"Chrudim",
 	"Jablonec nad Nisou",
-	"Jesenk",
-	"Jicn",
+	"Jeseník",
+	"Jicín",
 	"Jihlava",
 	"Jindrichuv Hradec",
 	"Karlovy Vary",
-	"Karvin",
+	"Karviná",
 	"Kladno",
 	"Klatovy",
-	"Koln",
+	"Kolín",
 	"Kosmonosy",
-	"Kromerz",
-	"Kutn Hora",
+	"Kromeríz",
+	"Kutná Hora",
 	"Liberec",
 	"Litomerice",
 	"Louny",
-	"Manetn",
-	"Melnk",
-	"Mlad Boleslav",
+	"Manetín",
+	"Melník",
+	"Mladá Boleslav",
 	"Most",
-	"Nchod",
-	"Nov Jicn",
+	"Náchod",
+	"Nový Jicín",
 	"Nymburk",
 	"Olomouc",
 	"Opava",
-	"Orcov",
+	"Orácov",
 	"Ostrava",
 	"Pardubice",
 	"Pelhrimov",
 	"Polzice",
-	"Psek",
+	"Písek",
 	"Plzen",
 	"Praha",
 	"Prachatice",
 	"Prerov",
-	"Prbram",
+	"Príbram",
 	"Prostejov",
-	"Rakovnk",
+	"Rakovník",
 	"Rokycany",
-	"Rudn",
+	"Rudná",
 	"Rychnov nad Kneznou",
 	"Semily",
 	"Sokolov",
@@ -1668,18 +1668,18 @@
 	"Stredokluky",
 	"Sumperk",
 	"Svitavy",
-	"Tbor",
+	"Tábor",
 	"Tachov",
 	"Teplice",
-	"Trebc",
+	"Trebíc",
 	"Trutnov",
-	"Uhersk Hradiste",
-	"st nad Labem",
-	"st nad Orlic",
-	"Vsetn",
+	"Uherské Hradiste",
+	"Ústí nad Labem",
+	"Ústí nad Orlicí",
+	"Vsetín",
 	"Vyskov",
-	"Zdr nad Szavou",
-	"Zln",
+	"Zdár nad Sázavou",
+	"Zlín",
 	"Znojmo"
 };
 
@@ -1710,13 +1710,13 @@
 /* [CzechGender][CzechPattern] - replaces the last character of the adjective
  * by this. */
 // XXX: [CZG_SMASC][CZP_PRIVL] needs special handling: -ovX -> -uv.
-static const char name_czech_patmod[][3] = {
-	/* CZG_SMASC */ { '', '', 'X' },
-	/* CZG_SFEM */  { '', '', 'a' },
-	/* CZG_SNEUT */ { '', '', 'o' },
-	/* CZG_PMASC */ { '', '', 'y' },
-	/* CZG_PFEM */  { '', '', 'y' },
-	/* CZG_PNEUT */ { '', '', 'a' }
+static const char *name_czech_patmod[][3] = {
+	/* CZG_SMASC */ { "í", "ý", "X" },
+	/* CZG_SFEM */  { "í", "á", "a" },
+	/* CZG_SNEUT */ { "í", "é", "o" },
+	/* CZG_PMASC */ { "í", "é", "y" },
+	/* CZG_PFEM */  { "í", "é", "y" },
+	/* CZG_PNEUT */ { "í", "á", "a" }
 };
 
 // This way the substantives can choose only some adjectives/endings:
@@ -1751,52 +1751,52 @@
 
 // Some of items which should be common are doubled.
 static const CzechNameAdj name_czech_adj[] = {
-	{ CZP_JARNI, CZC_ANY, "Horn" },
-	{ CZP_JARNI, CZC_ANY, "Horn" },
-	{ CZP_JARNI, CZC_ANY, "Doln" },
-	{ CZP_JARNI, CZC_ANY, "Doln" },
-	{ CZP_JARNI, CZC_ANY, "Predn" },
-	{ CZP_JARNI, CZC_ANY, "Zadn" },
-	{ CZP_JARNI, CZC_ANY, "Kosteln" },
-	{ CZP_JARNI, CZC_ANY, "Havran" },
-	{ CZP_JARNI, CZC_ANY, "Rcn" },
-	{ CZP_JARNI, CZC_ANY, "Jezern" },
-	{ CZP_MLADY, CZC_ANY, "Velk" },
-	{ CZP_MLADY, CZC_ANY, "Velk" },
-	{ CZP_MLADY, CZC_ANY, "Mal" },
-	{ CZP_MLADY, CZC_ANY, "Mal" },
-	{ CZP_MLADY, CZC_ANY, "Vysok" },
-	{ CZP_MLADY, CZC_ANY, "Cesk" },
-	{ CZP_MLADY, CZC_ANY, "Moravsk" },
-	{ CZP_MLADY, CZC_ANY, "Slovck" },
-	{ CZP_MLADY, CZC_ANY, "Slezsk" },
-	{ CZP_MLADY, CZC_ANY, "Uhersk" },
-	{ CZP_MLADY, CZC_ANY, "Star" },
-	{ CZP_MLADY, CZC_ANY, "Star" },
-	{ CZP_MLADY, CZC_ANY, "Nov" },
-	{ CZP_MLADY, CZC_ANY, "Nov" },
-	{ CZP_MLADY, CZC_ANY, "Mlad" },
-	{ CZP_MLADY, CZC_ANY, "Krlovsk" },
-	{ CZP_MLADY, CZC_ANY, "Kamenn" },
-	{ CZP_MLADY, CZC_ANY, "Cihlov" },
-	{ CZP_MLADY, CZC_ANY, "Divn" },
-	{ CZP_MLADY, CZC_COLOR, "Cerven" },
-	{ CZP_MLADY, CZC_COLOR, "Cerven" },
-	{ CZP_MLADY, CZC_COLOR, "Cerven" },
-	{ CZP_MLADY, CZC_COLOR, "Zelen" },
-	{ CZP_MLADY, CZC_COLOR, "Zlut" },
-	{ CZP_MLADY, CZC_COLOR, "Siv" },
-	{ CZP_MLADY, CZC_COLOR, "Sed" },
-	{ CZP_MLADY, CZC_COLOR, "Bl" },
-	{ CZP_MLADY, CZC_COLOR, "Bl" },
-	{ CZP_MLADY, CZC_COLOR, "Modr" },
-	{ CZP_MLADY, CZC_COLOR, "Ruzov" },
-	{ CZP_MLADY, CZC_COLOR, "Cern" },
-	{ CZP_PRIVL, CZC_ANY, "Krlova" },
+	{ CZP_JARNI, CZC_ANY, "Horní" },
+	{ CZP_JARNI, CZC_ANY, "Horní" },
+	{ CZP_JARNI, CZC_ANY, "Dolní" },
+	{ CZP_JARNI, CZC_ANY, "Dolní" },
+	{ CZP_JARNI, CZC_ANY, "Prední" },
+	{ CZP_JARNI, CZC_ANY, "Zadní" },
+	{ CZP_JARNI, CZC_ANY, "Kostelní" },
+	{ CZP_JARNI, CZC_ANY, "Havraní" },
+	{ CZP_JARNI, CZC_ANY, "Rícní" },
+	{ CZP_JARNI, CZC_ANY, "Jezerní" },
+	{ CZP_MLADY, CZC_ANY, "Velký" },
+	{ CZP_MLADY, CZC_ANY, "Velký" },
+	{ CZP_MLADY, CZC_ANY, "Malý" },
+	{ CZP_MLADY, CZC_ANY, "Malý" },
+	{ CZP_MLADY, CZC_ANY, "Vysoký" },
+	{ CZP_MLADY, CZC_ANY, "Ceský" },
+	{ CZP_MLADY, CZC_ANY, "Moravský" },
+	{ CZP_MLADY, CZC_ANY, "Slovácký" },
+	{ CZP_MLADY, CZC_ANY, "Slezský" },
+	{ CZP_MLADY, CZC_ANY, "Uherský" },
+	{ CZP_MLADY, CZC_ANY, "Starý" },
+	{ CZP_MLADY, CZC_ANY, "Starý" },
+	{ CZP_MLADY, CZC_ANY, "Nový" },
+	{ CZP_MLADY, CZC_ANY, "Nový" },
+	{ CZP_MLADY, CZC_ANY, "Mladý" },
+	{ CZP_MLADY, CZC_ANY, "Královský" },
+	{ CZP_MLADY, CZC_ANY, "Kamenný" },
+	{ CZP_MLADY, CZC_ANY, "Cihlový" },
+	{ CZP_MLADY, CZC_ANY, "Divný" },
+	{ CZP_MLADY, CZC_COLOR, "Cervená" },
+	{ CZP_MLADY, CZC_COLOR, "Cervená" },
+	{ CZP_MLADY, CZC_COLOR, "Cervená" },
+	{ CZP_MLADY, CZC_COLOR, "Zelená" },
+	{ CZP_MLADY, CZC_COLOR, "Zlutá" },
+	{ CZP_MLADY, CZC_COLOR, "Sivá" },
+	{ CZP_MLADY, CZC_COLOR, "Sedá" },
+	{ CZP_MLADY, CZC_COLOR, "Bílá" },
+	{ CZP_MLADY, CZC_COLOR, "Bílá" },
+	{ CZP_MLADY, CZC_COLOR, "Modrá" },
+	{ CZP_MLADY, CZC_COLOR, "Ruzová" },
+	{ CZP_MLADY, CZC_COLOR, "Cerná" },
+	{ CZP_PRIVL, CZC_ANY, "Králova" },
 	{ CZP_PRIVL, CZC_ANY, "Janova" },
 	{ CZP_PRIVL, CZC_ANY, "Karlova" },
 	{ CZP_PRIVL, CZC_ANY, "Krystofova" },
-	{ CZP_PRIVL, CZC_ANY, "Jirkova" },
+	{ CZP_PRIVL, CZC_ANY, "Jiríkova" },
 	{ CZP_PRIVL, CZC_ANY, "Petrova" },
 	{ CZP_PRIVL, CZC_ANY, "Sudovo" },
 };
@@ -1806,17 +1806,17 @@
 	{ CZG_SMASC, CZA_ALL, CZC_COLOR, "Sedlec" },
 	{ CZG_SMASC, CZA_ALL, CZC_COLOR, "Brod" },
 	{ CZG_SMASC, CZA_ALL, CZC_COLOR, "Brod" },
-	{ CZG_SMASC, CZA_ALL, CZC_NONE, "val" },
-	{ CZG_SMASC, CZA_ALL, CZC_COLOR, "Zdr" },
+	{ CZG_SMASC, CZA_ALL, CZC_NONE, "Úval" },
+	{ CZG_SMASC, CZA_ALL, CZC_COLOR, "Zdár" },
 	{ CZG_SMASC, CZA_ALL, CZC_COLOR, "Smrk" },
 	{ CZG_SFEM,  CZA_ALL, CZC_COLOR, "Hora" },
 	{ CZG_SFEM,  CZA_ALL, CZC_COLOR, "Lhota" },
 	{ CZG_SFEM,  CZA_ALL, CZC_COLOR, "Lhota" },
 	{ CZG_SFEM,  CZA_ALL, CZC_COLOR, "Hlava" },
-	{ CZG_SFEM,  CZA_ALL, CZC_COLOR, "Lpa" },
+	{ CZG_SFEM,  CZA_ALL, CZC_COLOR, "Lípa" },
 	{ CZG_SNEUT, CZA_ALL, CZC_COLOR, "Pole" },
-	{ CZG_SNEUT, CZA_ALL, CZC_COLOR, "dol" },
-	{ CZG_PMASC, CZA_ALL, CZC_NONE, "valy" },
+	{ CZG_SNEUT, CZA_ALL, CZC_COLOR, "Údolí" },
+	{ CZG_PMASC, CZA_ALL, CZC_NONE, "Úvaly" },
 	{ CZG_PFEM,  CZA_ALL, CZC_COLOR, "Luka" },
 	{ CZG_PNEUT, CZA_ALL, CZC_COLOR, "Pole" },
 };
@@ -1824,7 +1824,7 @@
 // TODO: More stems needed. --pasky
 static const CzechNameSubst name_czech_subst_stem[] = {
 	{ CZG_SMASC,             CZA_MIDDLE,            CZC_COLOR, "Kostel" },
-	{ CZG_SMASC,             CZA_MIDDLE,            CZC_COLOR, "Klster" },
+	{ CZG_SMASC,             CZA_MIDDLE,            CZC_COLOR, "Kláster" },
 	{ CZG_SMASC, CZA_SHORT,                         CZC_COLOR, "Lhot" },
 	{ CZG_SFEM,  CZA_SHORT,                         CZC_COLOR, "Lhot" },
 	{ CZG_SFEM,  CZA_SHORT,                         CZC_COLOR, "Hur" },
@@ -1848,7 +1848,7 @@
 	{ CZG_NFREE,                          CZA_LONG, CZC_NONE, "Harv" },
 	{ CZG_NFREE,                          CZA_LONG, CZC_NONE, "Pruh" },
 	{ CZG_NFREE,                          CZA_LONG, CZC_NONE, "Tach" },
-	{ CZG_NFREE,                          CZA_LONG, CZC_NONE, "Psn" },
+	{ CZG_NFREE,                          CZA_LONG, CZC_NONE, "Písn" },
 	{ CZG_NFREE,                          CZA_LONG, CZC_NONE, "Jin" },
 	{ CZG_NFREE,                          CZA_LONG, CZC_NONE, "Jes" },
 	{ CZG_NFREE,                          CZA_LONG, CZC_NONE, "Jar" },
@@ -1877,17 +1877,17 @@
 // This array must have the both neutral genders at the end!
 static const CzechNameSubst name_czech_subst_ending[] = {
 	{ CZG_SMASC, CZA_SHORT | CZA_MIDDLE,            CZC_ANY, "ec" },
-	{ CZG_SMASC, CZA_SHORT | CZA_MIDDLE,            CZC_ANY, "n" },
+	{ CZG_SMASC, CZA_SHORT | CZA_MIDDLE,            CZC_ANY, "ín" },
 	{ CZG_SMASC, CZA_SHORT | CZA_MIDDLE | CZA_LONG, CZC_ANY, "ov" },
 	{ CZG_SMASC, CZA_SHORT       |        CZA_LONG, CZC_ANY, "kov" },
-	{ CZG_SMASC,                          CZA_LONG, CZC_POSTFIX, "n" },
-	{ CZG_SMASC,                          CZA_LONG, CZC_POSTFIX, "nk" },
+	{ CZG_SMASC,                          CZA_LONG, CZC_POSTFIX, "ín" },
+	{ CZG_SMASC,                          CZA_LONG, CZC_POSTFIX, "ník" },
 	{ CZG_SMASC,                          CZA_LONG, CZC_ANY, "burk" },
 	{ CZG_SFEM,  CZA_SHORT,                         CZC_ANY, "ka" },
 	{ CZG_SFEM,              CZA_MIDDLE,            CZC_ANY, "inka" },
-	{ CZG_SFEM,              CZA_MIDDLE,            CZC_ANY, "n" },
+	{ CZG_SFEM,              CZA_MIDDLE,            CZC_ANY, "ná" },
 	{ CZG_SFEM,                           CZA_LONG, CZC_ANY, "ava" },
-	{ CZG_PMASC,                          CZA_LONG, CZC_POSTFIX, "ky" },
+	{ CZG_PMASC,                          CZA_LONG, CZC_POSTFIX, "íky" },
 	{ CZG_PMASC,                          CZA_LONG, CZC_ANY, "upy" },
 	{ CZG_PMASC,                          CZA_LONG, CZC_ANY, "olupy" },
 	{ CZG_PFEM,                           CZA_LONG, CZC_ANY, "avy" },
@@ -1900,21 +1900,21 @@
 
 static const char *name_czech_suffix[] = {
 	"nad Cydlinou",
-	"nad Dyj",
+	"nad Dyjí",
 	"nad Jihlavou",
 	"nad Labem",
 	"nad Lesy",
 	"nad Moravou",
 	"nad Nisou",
 	"nad Odrou",
-	"nad Ostravic",
-	"nad Szavou",
+	"nad Ostravicí",
+	"nad Sázavou",
 	"nad Vltavou",
 	"pod Pradedem",
 	"pod Radhostem",
-	"pod Rpem",
+	"pod Rípem",
 	"pod Snezkou",
-	"pod Spickem",
+	"pod Spicákem",
 	"pod Sedlem",
 	"v Cechach",
 	"na Morave",
@@ -1927,24 +1927,24 @@
 	"Alba Iulia",
 	"Alexandria",
 	"Babadag",
-	"Bacu",
+	"Bacãu",
 	"Baia Mare",
-	"Bile Herculane",
-	"Bilesti",
-	"Brlad",
+	"Bãile Herculane",
+	"Bãilesti",
+	"Bârlad",
 	"Bicaz",
 	"Bistrita",
 	"Blaj",
 	"Borsec",
 	"Botosani",
-	"Brila",
+	"Brãila",
 	"Brasov",
 	"Bucuresti",
 	"Buftea",
-	"Buzu",
-	"Clrasi",
+	"Buzãu",
+	"Cãlãrasi",
 	"Caransebes",
-	"Cernavod",
+	"Cernavodã",
 	"Cluj-Napoca",
 	"Constanta",
 	"Covasna",
@@ -1953,29 +1953,29 @@
 	"Deva",
 	"Dorohoi",
 	"Dr.-Tr. Severin",
-	"Drgsani",
-	"Fgras",
-	"Flticeni",
+	"Drãgãsani",
+	"Fãgãras",
+	"Fãlticeni",
 	"Fetesti",
 	"Focsani",
 	"Galati",
 	"Gheorgheni",
 	"Giurgiu",
-	"Hrsova",
+	"Hârsova",
 	"Hunedoara",
 	"Husi",
 	"Iasi",
 	"Isaccea",
 	"Lugoj",
-	"Mcin",
+	"Mãcin",
 	"Mangalia",
 	"Medgidia",
 	"Medias",
 	"Miercurea Ciuc",
 	"Mizil",
 	"Motru",
-	"Nsud",
-	"Nvodari",
+	"Nãsãud",
+	"Nãvodari",
 	"Odobesti",
 	"Oltenita",
 	"Onesti",
@@ -1986,14 +1986,14 @@
 	"Pitesti",
 	"Ploiesti",
 	"Predeal",
-	"Rmnicu Vlcea",
+	"Râmnicu Vâlcea",
 	"Reghin",
 	"Resita",
 	"Roman",
 	"Rosiorii de Vede",
 	"Satu Mare",
 	"Sebes",
-	"Sfntu Gheorghe",
+	"Sfântu Gheorghe",
 	"Sibiu",
 	"Sighisoara",
 	"Sinaia",
@@ -2002,21 +2002,21 @@
 	"Sovata",
 	"Suceava",
 	"Sulina",
-	"Tndrei",
-	"Trgoviste",
-	"Trgu Jiu",
-	"Trgu Mures",
+	"Tãndãrei",
+	"Târgoviste",
+	"Târgu Jiu",
+	"Târgu Mures",
 	"Tecuci",
 	"Timisoara",
 	"Tulcea",
 	"Turda",
-	"Turnu Mgurele",
+	"Turnu Mãgurele",
 	"Urziceni",
 	"Vaslui",
 	"Vatra Dornei",
 	"Victoria",
 	"Videle",
-	"Zalu"
+	"Zalãu"
 };
 
 static const char *name_slovak_real[] = {
@@ -2112,12 +2112,12 @@
 static const char *name_norwegian_1[] = {
 	"Arna",
 	"Aust",
-	"Bjrk",
-	"Bjrn",
+	"Bjørk",
+	"Bjørn",
 	"Brand",
-	"Bver",
+	"Bøver",
 	"Drag",
-	"Dr",
+	"Drø",
 	"Eids",
 	"Egge",
 	"Fager",
@@ -2130,12 +2130,12 @@
 	"Gaus",
 	"Galte",
 	"Geir",
-	"Gls",
+	"Gløs",
 	"Gran",
 	"Grind",
 	"Grims",
-	"Grn",
-	"Grt",
+	"Grøn",
+	"Grøt",
 	"Gulle",
 	"Haka",
 	"Hammer",
@@ -2150,7 +2150,7 @@
 	"Kjerring",
 	"Knatte",
 	"Krok",
-	"Ky",
+	"Køy",
 	"Lang",
 	"Lauv",
 	"Leir",
@@ -2158,7 +2158,7 @@
 	"Logn",
 	"Lo",
 	"Lyng",
-	"Ln",
+	"Løn",
 	"Mesna",
 	"Mel",
 	"Mo",
@@ -2178,9 +2178,9 @@
 	"Sel",
 	"Sol",
 	"Sjur",
-	"Skr",
-	"Sltt",
-	"Stjr",
+	"Skår",
+	"Slått",
+	"Stjør",
 	"Stor",
 	"Svart",
 	"Svens",
@@ -2193,7 +2193,7 @@
 	"Vest",
 	"Vesle",
 	"Vik",
-	"Vg"
+	"Våg"
 };
 
 static const char *name_norwegian_2[] = {
@@ -2208,7 +2208,7 @@
 	"bugen",
 	"by",
 	"bygd",
-	"b",
+	"bø",
 	"dal",
 	"egga",
 	"eid",
@@ -2226,10 +2226,10 @@
 	"heim",
 	"hella",
 	"hovda",
-	"ha",
-	"hgda",
+	"høa",
+	"høgda",
 	"kampen",
-	"kjlen",
+	"kjølen",
 	"kollen",
 	"kroken",
 	"land",
@@ -2244,7 +2244,7 @@
 	"rud",
 	"sand",
 	"set",
-	"sjen",
+	"sjøen",
 	"skogen",
 	"slette",
 	"snipa",
@@ -2253,7 +2253,7 @@
 	"stulen",
 	"sund",
 	"svingen",
-	"stra",
+	"sætra",
 	"tinden",
 	"tun",
 	"vang",
@@ -2261,9 +2261,9 @@
 	"veid",
 	"vik",
 	"voll",
-	"vg",
+	"våg",
 	"um",
-	"sen"
+	"åsen"
 };
 
 static const char *name_norwegian_real[] = {
@@ -2271,12 +2271,12 @@
 	"Arendal",
 	"Askim",
 	"Bergen",
-	"Bod",
+	"Bodø",
 	"Brevik",
 	"Bryne",
-	"Brnnysund",
+	"Brønnøysund",
 	"Drammen",
-	"Drbak",
+	"Drøbak",
 	"Egersund",
 	"Elverum",
 	"Farsund",
@@ -2284,10 +2284,10 @@
 	"Finnsnes",
 	"Flekkefjord",
 	"Flora",
-	"Fosnavg",
+	"Fosnavåg",
 	"Fredrikstad",
-	"Frde",
-	"Gjvik",
+	"Førde",
+	"Gjøvik",
 	"Grimstad",
 	"Halden",
 	"Hamar",
@@ -2296,13 +2296,13 @@
 	"Haugesund",
 	"Holmestrand",
 	"Horten",
-	"Jrpeland",
+	"Jørpeland",
 	"Kirkenes",
 	"Kolvereid",
 	"Kongsberg",
 	"Kongsvinger",
 	"Kopervik",
-	"Krager",
+	"Kragerø",
 	"Kristiansand",
 	"Kristiansund",
 	"Langesund",
@@ -2312,16 +2312,16 @@
 	"Levanger",
 	"Lillehammer",
 	"Lillesand",
-	"Lillestrm",
+	"Lillestrøm",
 	"Lyngdal",
-	"Lrenskog",
+	"Lørenskog",
 	"Mandal",
 	"Mo i Rana",
 	"Molde",
-	"Mosjen",
+	"Mosjøen",
 	"Moss",
 	"Mysen",
-	"Mly",
+	"Måløy",
 	"Namsos",
 	"Narvik",
 	"Notodden",
@@ -2330,11 +2330,11 @@
 	"Otta",
 	"Porsgrunn",
 	"Ringerike",
-	"Risr",
+	"Risør",
 	"Rjukan",
 	"Sandefjord",
 	"Sandnes",
-	"Sandnessjen",
+	"Sandnessjøen",
 	"Sandvika",
 	"Sarpsborg",
 	"Sauda",
@@ -2345,152 +2345,152 @@
 	"Stathelle",
 	"Stavanger",
 	"Steinkjer",
-	"Stjrdal",
+	"Stjørdal",
 	"Stokmarknes",
 	"Stord",
 	"Svelvik",
-	"Svolvr",
-	"Troms",
+	"Svolvær",
+	"Tromsø",
 	"Trondheim",
 	"Tvedestrand",
-	"Tnsberg",
+	"Tønsberg",
 	"Ulsteinvik",
-	"Vads",
-	"Vard",
-	"Verdalsra",
-	"krehamn",
-	"lesund",
-	"ndalsnes"
+	"Vadsø",
+	"Vardø",
+	"Verdalsøra",
+	"Åkrehamn",
+	"Ålesund",
+	"Åndalsnes"
 };
 
 static const char *name_hungarian_1[] = {
 	"Nagy-",
 	"Kis-",
-	"Fels-",
-	"Als-",
-	"j-"
+	"Felsõ-",
+	"Alsó-",
+	"Új-"
 };
 
 static const char *name_hungarian_2[] = {
 	"Bodrog",
-	"Drva",
+	"Dráva",
 	"Duna",
-	"Hej",
-	"Hernd",
-	"Rba",
-	"Saj",
+	"Hejõ",
+	"Hernád",
+	"Rába",
+	"Sajó",
 	"Szamos",
 	"Tisza",
 	"Zala",
 	"Balaton",
-	"Fert",
+	"Fertõ",
 	"Bakony",
-	"Cserht",
+	"Cserhát",
 	"Bihar",
-	"Hajd",
-	"Jsz",
+	"Hajdú",
+	"Jász",
 	"Kun",
 	"Magyar",
-	"Ngrd",
-	"Nyr",
+	"Nógrád",
+	"Nyír",
 	"Somogy",
-	"Szkely",
+	"Székely",
 	"Buda",
-	"Gyr",
+	"Gyõr",
 	"Pest",
-	"Fehr",
-	"Cserp",
-	"Erd",
+	"Fehér",
+	"Cserép",
+	"Erdõ",
 	"Hegy",
 	"Homok",
-	"Mez",
+	"Mezõ",
 	"Puszta",
-	"Sr",
-	"Csszr",
+	"Sár",
+	"Császár",
 	"Herceg",
-	"Kirly",
+	"Király",
 	"Nemes",
-	"Pspk",
+	"Püspök",
 	"Szent",
-	"Alms",
-	"Szilvs",
+	"Almás",
+	"Szilvás",
 	"Agg",
 	"Aranyos",
-	"Bks",
-	"Egyhzas",
+	"Békés",
+	"Egyházas",
 	"Gagy",
 	"Heves",
 	"Kapos",
-	"Tpi",
+	"Tápió",
 	"Torna",
 	"Vas",
-	"Vmos",
-	"Vsros"
+	"Vámos",
+	"Vásáros"
 };
 
 static const char *name_hungarian_3[] = {
-	"apti",
-	"bba",
+	"apáti",
+	"bába",
 	"bikk",
 	"dob",
 	"fa",
-	"fld",
+	"föld",
 	"hegyes",
 	"kak",
 	"kereszt",
-	"krt",
-	"ladny",
-	"mrges",
+	"kürt",
+	"ladány",
+	"mérges",
 	"szalonta",
 	"telek",
 	"vas",
-	"vlgy"
+	"völgy"
 };
 
 static const char *name_hungarian_4[] = {
 	"alja",
-	"egyhza",
-	"hza",
-	"r",
-	"vr"
+	"egyháza",
+	"háza",
+	"úr",
+	"vár"
 };
 
 static const char *name_hungarian_real[] = {
 	"Ajka",
-	"Aszd",
+	"Aszód",
 	"Badacsony",
 	"Baja",
 	"Budapest",
 	"Debrecen",
 	"Eger",
-	"Fonyd",
-	"Gdll",
-	"Gyr",
+	"Fonyód",
+	"Gödöllõ",
+	"Gyõr",
 	"Gyula",
 	"Karcag",
-	"Kecskemt",
+	"Kecskemét",
 	"Keszthely",
-	"Kiskre",
+	"Kisköre",
 	"Kocsord",
-	"Komrom",
-	"Kszeg",
-	"Mak",
-	"Mohcs",
+	"Komárom",
+	"Kõszeg",
+	"Makó",
+	"Mohács",
 	"Miskolc",
-	"zd",
+	"Ózd",
 	"Paks",
-	"Ppa",
-	"Pcs",
-	"Polgr",
+	"Pápa",
+	"Pécs",
+	"Polgár",
 	"Sarkad",
-	"Sifok",
+	"Siófok",
 	"Szeged",
 	"Szentes",
 	"Szolnok",
 	"Tihany",
 	"Tokaj",
-	"Vc",
-	"Zhony",
+	"Vác",
+	"Záhony",
 	"Zirc"
 };
 
@@ -2501,7 +2501,7 @@
 	"Arosa",
 	"Appenzell",
 	"Arbon",
-	"Altsttten",
+	"Altstätten",
 	"Baar",
 	"Baden",
 	"Bellinzona",
@@ -2512,20 +2512,20 @@
 	"Burgdorf",
 	"Bern",
 	"Basel",
-	"Blach",
+	"Bülach",
 	"Carouge",
 	"Cham",
 	"Chiasso",
 	"Chur",
 	"Davos",
-	"Delmont",
+	"Delémont",
 	"Dietikon",
-	"Dbendorf",
+	"Dübendorf",
 	"Emmen",
-	"Freienbach-Pfffikon",
+	"Freienbach-Pfäffikon",
 	"Fribourg",
 	"Frauenfeld",
-	"Genve",
+	"Genève",
 	"Glarus",
 	"Gossau",
 	"Grenchen",
@@ -2537,9 +2537,9 @@
 	"Jona",
 	"Kriens",
 	"Kloten",
-	"Kniz",
+	"Köniz",
 	"Kreuzlingen",
-	"Ksnacht",
+	"Küsnacht",
 	"Agen",
 	"Lancy",
 	"La Chaux-de-Fonds",
@@ -2556,7 +2556,7 @@
 	"Lyss",
 	"Luzern",
 	"Martigny",
-	"Mnchenstein",
+	"Münchenstein",
 	"Meyrin",
 	"Montreux",
 	"Monthey",
@@ -2564,7 +2564,7 @@
 	"Murten",
 	"Moutier",
 	"Muttenz",
-	"Neuchtel",
+	"Neuchâtel",
 	"Neuhausen am Rheinfall",
 	"Nyon",
 	"Olten",
@@ -2593,11 +2593,11 @@
 	"St. Moritz",
 	"Sion",
 	"Spiez",
-	"Stfa",
+	"Stäfa",
 	"Sursee",
 	"Schwyz",
 	"Thalwil",
-	"Thnex",
+	"Thônex",
 	"Thun",
 	"Uster",
 	"Uzwil",
@@ -2605,7 +2605,7 @@
 	"Volketswil",
 	"Versoix",
 	"Vevey",
-	"Wdenswil",
+	"Wädenswil",
 	"Wettingen",
 	"Wil",
 	"Wallisellen",
@@ -2616,7 +2616,7 @@
 	"Yverdon-les-Bains",
 	"Zollikon",
 	"Zofingen",
-	"Zrich",
+	"Zürich",
 	"Zug",
 };
 
@@ -2626,12 +2626,12 @@
 	"Nye ",
 	"Store ",
 	"Kirke ",
-	"Nrre ",
+	"Nørre ",
 	"Vester ",
-	"Snder ",
-	"ster ",
+	"Sønder ",
+	"Øster ",
 	"Hvide ",
-	"Hje ",
+	"Høje ",
 	"Kongens ",
 };
 
@@ -2643,7 +2643,7 @@
 	"Bede",
 	"Birke",
 	"Bjerring",
-	"Bjver",
+	"Bjæver",
 	"Blommens",
 	"Blok",
 	"Bolder",
@@ -2656,7 +2656,7 @@
 	"Fredens",
 	"Frederiks",
 	"Fugle",
-	"Fre",
+	"Fåre",
 	"Gille",
 	"Gis",
 	"Givs",
@@ -2673,11 +2673,11 @@
 	"Hol",
 	"Horn",
 	"Humle",
-	"Hj",
-	"Hr",
+	"Høj",
+	"Hør",
 	"Is",
 	"Jyde",
-	"Jgers",
+	"Jægers",
 	"Karls",
 	"Klov",
 	"Kokke",
@@ -2689,24 +2689,24 @@
 	"Ny",
 	"Oks",
 	"Ring",
-	"Rde",
+	"Røde",
 	"Rung",
-	"Rr",
+	"Rør",
 	"Rud",
 	"Saks",
 	"Salt",
 	"Skam",
 	"Silke",
 	"Skod",
-	"Skl",
-	"Skr",
+	"Skæl",
+	"Skær",
 	"Sol",
 	"Svend",
 	"Svine",
 	"Strand",
 	"Stubbe",
 	"Ting",
-	"Tjre",
+	"Tjære",
 	"Tore",
 	"Uger",
 	"Ulf",
@@ -2714,9 +2714,9 @@
 	"Vand",
 	"Vej",
 	"Vor",
-	"Vr",
-	"r",
-	"l"
+	"Vær",
+	"Ør",
+	"Ål"
 };
 
 static const char *name_danish_3[] = {
@@ -2729,21 +2729,21 @@
 	"strup",
 	"holm",
 	"hus",
-	"kbing",
+	"købing",
 	"lund",
 	"lunde",
 	"sund",
 	"ovre",
-	"hj",
+	"høj",
 	"dal",
 	"sted",
 	"sten",
-	"lse",
-	"rd",
+	"løse",
+	"rød",
 	"magle",
-	"s",
+	"sø",
 	"bjerg",
-	"bk",
+	"bæk",
 	"drup",
 	"lev",
 	"bo",
@@ -2753,17 +2753,17 @@
 };
 
 static const char *name_turkish_prefix[] = {
-	"Aka",
+	"Akça",
 	"Altin",
-	"Bahe",
+	"Bahçe",
 	"Boz",
-	"Byk",
-	"ay",
+	"Büyük",
+	"Çay",
 	"Dogu",
 	"Eski",
-	"Gzel",
+	"Güzel",
 	"Kizil",
-	"Kk",
+	"Küçük",
 	"Orta",
 	"Sari",
 	"Sultan",
@@ -2772,9 +2772,9 @@
 };
 
 static const char *name_turkish_middle[] = {
-	"aga",
+	"agaç",
 	"ayva",
-	"am",
+	"çam",
 	"elma",
 	"kurt",
 	"pazar",
@@ -2787,10 +2787,10 @@
 	"kale",
 	"kaya",
 	"kent",
-	"ky",
+	"köy",
 	"ova",
-	"z",
-	"ren",
+	"özü",
+	"ören",
 	"pazar",
 	"saray",
 	"tepe",
@@ -2812,8 +2812,8 @@
 	"Bolu",
 	"Burdur",
 	"Bursa",
-	"anakkale",
-	"ankiri",
+	"Çanakkale",
+	"Çankiri",
 	"Denizli",
 	"Diyarbakir",
 	"Edirne",
@@ -2821,10 +2821,10 @@
 	"Erzurum",
 	"Eskisehir",
 	"Giresun",
-	"Gmshane",
+	"Gümüshane",
 	"Hatay",
 	"Isparta",
-	"iel",
+	"içel",
 	"istanbul",
 	"izmir",
 	"Kars",
@@ -2833,7 +2833,7 @@
 	"Kirklareli",
 	"Kocaeli",
 	"Konya",
-	"Ktahya",
+	"Kütahya",
 	"Malatya",
 	"Manisa",
 	"Kahramanmaras",
@@ -2861,9 +2861,9 @@
 	"Ardahan",
 	"Igdir",
 	"Yalova",
-	"Karabk",
+	"Karabük",
 	"Osmaniye",
-	"Dzce"
+	"Düzce"
 };
 
 static const char *name_italian_real[] = {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/table/unicode.h	Thu Nov 16 22:05:33 2006 +0000
@@ -0,0 +1,21 @@
+/* $Id$ */
+
+
+typedef struct DefaultUnicodeMapping {
+	WChar code; ///< Unicode value
+	byte key;   ///< Character index of sprite
+} DefaultUnicodeMapping;
+
+
+/* Default unicode mapping table for sprite based glyphs.
+ * This table allows us use unicode characters even though the glyphs don't
+ * exist, or are in the wrong place, in the standard sprite fonts.
+ * This is not used for FreeType rendering */
+
+static DefaultUnicodeMapping _default_unicode_map[] = {
+	{ 0x010D, 0x63 }, /* Small letter c with caron */
+	{ 0x0160, 0xA6 }, /* Capital letter s with caron */
+	{ 0x0161, 0xA8 }, /* Small letter s with caron */
+	{ 0x017E, 0xB8 }, /* Small letter z with caron */
+	{ 0x20AC, 0xA4 }, /* Euro symbol */
+};
--- a/unix.c	Thu Nov 16 20:57:23 2006 +0000
+++ b/unix.c	Thu Nov 16 22:05:33 2006 +0000
@@ -291,7 +291,7 @@
 #include <errno.h>
 #include "debug.h"
 
-#define INTERNALCODE "ISO-8859-15"
+#define INTERNALCODE "UTF-8"
 
 /** Try and try to decipher the current locale from environmental
  * variables. MacOSX is hardcoded, other OS's are dynamic. If no suitable
--- a/variables.h	Thu Nov 16 20:57:23 2006 +0000
+++ b/variables.h	Thu Nov 16 22:05:33 2006 +0000
@@ -323,12 +323,12 @@
 typedef struct {
 	int num; // number of languages
 	int curr; // currently selected language index
-	char curr_file[32]; // currently selected language file
-	StringID dropdown[32 + 1]; // used in settings dialog
+	char curr_file[MAX_LANG]; // currently selected language file
+	StringID dropdown[MAX_LANG + 1]; // used in settings dialog
 	struct {
 		char *name;
 		char *file;
-	} ent[32];
+	} ent[MAX_LANG];
 } DynamicLanguages;
 
 VARDEF DynamicLanguages _dynlang;
--- a/video/win32_v.c	Thu Nov 16 20:57:23 2006 +0000
+++ b/video/win32_v.c	Thu Nov 16 22:05:33 2006 +0000
@@ -349,14 +349,15 @@
 		case WM_KEYDOWN: {
 			// this is the rewritten ascii input function
 			// it disables windows deadkey handling --> more linux like :D
-			WORD w = 0;
+			wchar_t w = 0;
 			byte ks[256];
 			uint scancode;
 			uint32 pressed_key;
 
 			GetKeyboardState(ks);
-			if (ToAscii(wParam, 0, ks, &w, 0) == 0) {
-				w = 0; // no translation was possible
+			if (ToUnicode(wParam, 0, ks, &w, 1, 0) == 0) {
+				/* On win9x ToUnicode always fails, so fall back to ToAscii */
+				if (ToAscii(wParam, 0, ks, &w, 0) == 0) w = 0; // no translation was possible
 			}
 
 			pressed_key = w | MapWindowsKey(wParam) << 16;
--- a/win32.c	Thu Nov 16 20:57:23 2006 +0000
+++ b/win32.c	Thu Nov 16 22:05:33 2006 +0000
@@ -926,39 +926,67 @@
  */
 bool InsertTextBufferClipboard(Textbuf *tb)
 {
-	if (IsClipboardFormatAvailable(CF_TEXT)) {
-		HGLOBAL cbuf;
-		const byte *data, *dataptr;
-		uint16 width = 0;
-		uint16 length = 0;
+	HGLOBAL cbuf;
+	char utf8_buf[512];
+	const char *ptr;
 
+	WChar c;
+	uint16 width, length;
+
+	if (IsClipboardFormatAvailable(CF_UNICODETEXT)) {
+		int bytec;
+
+		OpenClipboard(NULL);
+		cbuf = GetClipboardData(CF_UNICODETEXT);
+
+		ptr = GlobalLock(cbuf);
+		bytec = WideCharToMultiByte(CP_UTF8, 0, (wchar_t*)ptr, -1, utf8_buf, lengthof(utf8_buf), NULL, NULL);
+		GlobalUnlock(cbuf);
+		CloseClipboard();
+
+		if (bytec == 0) {
+			DEBUG(misc, 0) ("[utf8] Error converting '%s'. Errno %d", ptr, GetLastError());
+			return false;
+		}
+	} else if (IsClipboardFormatAvailable(CF_TEXT)) {
 		OpenClipboard(NULL);
 		cbuf = GetClipboardData(CF_TEXT);
-		data = GlobalLock(cbuf); // clipboard data
-		dataptr = data;
-
-		for (; IsValidAsciiChar(*dataptr, CS_ALPHANUMERAL) && (tb->length + length) < (tb->maxlength - 1) &&
-				(tb->maxwidth == 0 || width + tb->width + GetCharacterWidth(FS_NORMAL, (byte)*dataptr) <= tb->maxwidth); dataptr++) {
-					width += GetCharacterWidth(FS_NORMAL, (byte)*dataptr);
-			length++;
-		}
 
-		if (length == 0) return false;
-
-		memmove(tb->buf + tb->caretpos + length, tb->buf + tb->caretpos, tb->length - tb->caretpos);
-		memcpy(tb->buf + tb->caretpos, data, length);
-		tb->width += width;
-		tb->caretxoffs += width;
-
-		tb->length += length;
-		tb->caretpos += length;
-		tb->buf[tb->length] = '\0'; // terminating zero
-
+		ptr = GlobalLock(cbuf);
+		ttd_strlcpy(utf8_buf, ptr, lengthof(utf8_buf));
 		GlobalUnlock(cbuf);
 		CloseClipboard();
-		return true;
+	} else {
+		return false;
 	}
-	return false;
+
+	width = length = 0;
+
+	for (ptr = utf8_buf; (c = Utf8Consume(&ptr)) != '\0';) {
+		byte charwidth;
+
+		if (!IsPrintable(c)) break;
+		if (tb->length + length >= tb->maxlength - 1) break;
+		charwidth = GetCharacterWidth(FS_NORMAL, c);
+
+		if (tb->maxwidth != 0 && width + tb->width + charwidth > tb->maxwidth) break;
+
+		width += charwidth;
+		length += Utf8CharLen(c);
+	}
+
+	if (length == 0) return false;
+
+	memmove(tb->buf + tb->caretpos + length, tb->buf + tb->caretpos, tb->length - tb->caretpos);
+	memcpy(tb->buf + tb->caretpos, utf8_buf, length);
+	tb->width += width;
+	tb->caretxoffs += width;
+
+	tb->length += length;
+	tb->caretpos += length;
+	tb->buf[tb->length] = '\0'; // terminating zero
+
+	return true;
 }
 
 
--- a/window.c	Thu Nov 16 20:57:23 2006 +0000
+++ b/window.c	Thu Nov 16 22:05:33 2006 +0000
@@ -1422,7 +1422,7 @@
 
 	// Setup event
 	e.event = WE_KEYPRESS;
-	e.we.keypress.ascii = GB(key, 0, 8);
+	e.we.keypress.key     = GB(key,  0, 16);
 	e.we.keypress.keycode = GB(key, 16, 16);
 	e.we.keypress.cont = true;
 
--- a/window.h	Thu Nov 16 20:57:23 2006 +0000
+++ b/window.h	Thu Nov 16 22:05:33 2006 +0000
@@ -160,7 +160,7 @@
 
 		struct {
 			bool cont;     // continue the search? (default true)
-			byte ascii;    // 8-bit ASCII-value of the key
+			uint16 key;    // 16-bit Unicode value of the key
 			uint16 keycode;// untranslated key (including shift-state)
 		} keypress;