summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpeter1138 <peter1138@openttd.org>2006-11-16 22:05:33 +0000
committerpeter1138 <peter1138@openttd.org>2006-11-16 22:05:33 +0000
commit1a4f1c8177f7ee351cb0096e3456d055b97dc60a (patch)
tree4fb6c0fac873efffc85cef437baa70d50d51fdfb
parent40d647ddde652bb8f1c7b4215279cc82d01ca38f (diff)
downloadopenttd-1a4f1c8177f7ee351cb0096e3456d055b97dc60a.tar.xz
(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.
-rw-r--r--Makefile18
-rwxr-xr-xconfigure14
-rw-r--r--console.c6
-rw-r--r--currency.c8
-rw-r--r--debug.c4
-rw-r--r--debug.h1
-rw-r--r--fontcache.c310
-rw-r--r--fontcache.h56
-rw-r--r--functions.h1
-rw-r--r--gfx.c145
-rw-r--r--gfx.h11
-rw-r--r--gui.h2
-rw-r--r--lang/english.txt4
-rw-r--r--main_gui.c10
-rw-r--r--makefiledir/Makefile.config_writer2
-rw-r--r--makefiledir/Makefile.libdetection6
-rw-r--r--misc.c32
-rw-r--r--misc_gui.c94
-rw-r--r--namegen.c4
-rw-r--r--network.c2
-rw-r--r--newgrf.c38
-rw-r--r--newgrf_text.c130
-rw-r--r--news_gui.c16
-rw-r--r--openttd.c10
-rw-r--r--openttd.h16
-rw-r--r--openttd.vcproj12
-rw-r--r--openttd_vs80.vcproj16
-rw-r--r--saveload.c2
-rw-r--r--settings.c12
-rw-r--r--strgen/strgen.c208
-rw-r--r--string.c124
-rw-r--r--string.h56
-rw-r--r--strings.c341
-rw-r--r--strings.h9
-rw-r--r--table/control_codes.h105
-rw-r--r--table/namegen.h688
-rw-r--r--table/unicode.h21
-rw-r--r--unix.c2
-rw-r--r--variables.h6
-rw-r--r--video/win32_v.c7
-rw-r--r--win32.c76
-rw-r--r--window.c2
-rw-r--r--window.h2
43 files changed, 1770 insertions, 859 deletions
diff --git a/Makefile b/Makefile
index 4b73d5fde..62d420dba 100644
--- a/Makefile
+++ b/Makefile
@@ -235,6 +235,12 @@ $(error WITH_PNG can't be used when LIBPNG_CONFIG is not set. Edit Makefile.conf
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 @@ ifndef MINGW
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.c
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 @@ $(TTD): $(OBJS) $(MAKE_CONFIG)
$(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 $@
diff --git a/configure b/configure
index 05301d805..4c62a84f8 100755
--- a/configure
+++ b/configure
@@ -35,11 +35,13 @@ function showhelp() {
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 @@ do
--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 @@ do
--libpng-config)
ITEM="LIBPNG_CONFIG"
;;
+ --freetype-config=*)
+ handle FREETYPE_CONFIG "$n"
+ ;;
+ --freetype-config)
+ ITEM="FREETYPE_CONFIG"
+ ;;
--*=*)
echo -n "Unknown switch "
diff --git a/console.c b/console.c
index 1b5ef1c64..98f6619b2 100644
--- a/console.c
+++ b/console.c
@@ -181,9 +181,9 @@ static void IConsoleWndProc(Window *w, WindowEvent *e)
}
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 @@ void IConsoleCmdExec(const char *cmdstr)
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;
diff --git a/currency.c b/currency.c
index 2ea5bda16..7bc81139a 100644
--- a/currency.c
+++ b/currency.c
@@ -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
diff --git a/debug.c b/debug.c
index ba4be2eeb..60865473a 100644
--- a/debug.c
+++ b/debug.c
@@ -21,6 +21,7 @@ int _debug_oldloader_level;
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 @@ typedef struct DebugLevel {
DEBUG_LEVEL(oldloader),
DEBUG_LEVEL(ntp),
DEBUG_LEVEL(npf),
- DEBUG_LEVEL(yapf)
+ DEBUG_LEVEL(yapf),
+ DEBUG_LEVEL(freetype)
};
#undef DEBUG_LEVEL
diff --git a/debug.h b/debug.h
index 1a3516249..c0005f8db 100644
--- a/debug.h
+++ b/debug.h
@@ -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, ...);
diff --git a/fontcache.c b/fontcache.c
new file mode 100644
index 000000000..4cfba83e8
--- /dev/null
+++ b/fontcache.c
@@ -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);
+ }
+ }
+}
+
diff --git a/fontcache.h b/fontcache.h
new file mode 100644
index 000000000..249f35cd0
--- /dev/null
+++ b/fontcache.h
@@ -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 */
diff --git a/functions.h b/functions.h
index 415f7a0cc..0529c1646 100644
--- a/functions.h
+++ b/functions.h
@@ -147,6 +147,7 @@ char *GetName(char *buff, StringID id, const char* last);
#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);
diff --git a/gfx.c b/gfx.c
index 0f446abbd..314de65d7 100644
--- a/gfx.c
+++ b/gfx.c
@@ -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 @@ void GfxDrawLine(int x, int y, int x2, int y2, int color)
}
-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 @@ static int TruncateString(char *str, int maxw)
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 @@ static int TruncateString(char *str, int maxw)
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 @@ uint32 FormatStringLinebreaks(char *str, int maxw)
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 @@ uint32 FormatStringLinebreaks(char *str, int maxw)
} 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 @@ void DrawStringMultiCenter(int x, int y, StringID str, int maxw)
uint32 tmp;
int num, w, mt;
const char *src;
- byte c;
+ WChar c;
GetString(buffer, str, lastof(buffer));
@@ -505,7 +473,7 @@ void DrawStringMultiCenter(int x, int y, StringID str, int maxw)
_cur_fontsize = _last_fontsize;
for (;;) {
- c = *src++;
+ c = Utf8Consume(&src);
if (c == 0) {
y += mt;
if (--num < 0) {
@@ -513,9 +481,9 @@ void DrawStringMultiCenter(int x, int y, StringID str, int maxw)
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 @@ uint DrawStringMultiLine(int x, int y, StringID str, int maxw)
int num, mt;
uint total_height;
const char *src;
- byte c;
+ WChar c;
GetString(buffer, str, lastof(buffer));
@@ -547,7 +515,7 @@ uint DrawStringMultiLine(int x, int y, StringID str, int maxw)
_cur_fontsize = _last_fontsize;
for (;;) {
- c = *src++;
+ c = Utf8Consume(&src);
if (c == 0) {
y += mt;
if (--num < 0) {
@@ -555,9 +523,9 @@ uint DrawStringMultiLine(int x, int y, StringID str, int maxw)
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 @@ BoundingRect GetStringBoundingBox(const char *str)
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 @@ int DoDrawString(const char *string, int x, int y, uint16 real_color)
{
DrawPixelInfo *dpi = _cur_dpi;
FontSize size = _cur_fontsize;
- byte c;
+ WChar c;
byte color;
int xo = x, yo = y;
@@ -647,39 +617,39 @@ check_bounds:
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 DoPaletteAnimations(void)
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
diff --git a/gfx.h b/gfx.h
index 0d63d2287..8636a36a6 100644
--- a/gfx.h
+++ b/gfx.h
@@ -43,8 +43,8 @@ void GfxScroll(int left, int top, int width, int height, int xo, int yo);
// 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 @@ void ToggleFullScreen(bool fs);
/* 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)
{
diff --git a/gui.h b/gui.h
index 277fbb856..376a969aa 100644
--- a/gui.h
+++ b/gui.h
@@ -109,7 +109,7 @@ bool HandleCaret(Textbuf *tb);
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);
diff --git a/lang/english.txt b/lang/english.txt
index 9b1433685..5c2664085 100644
--- a/lang/english.txt
+++ b/lang/english.txt
@@ -2001,8 +2001,8 @@ STR_26816_NONE :None
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
diff --git a/main_gui.c b/main_gui.c
index 0c759a39d..40ddcad60 100644
--- a/main_gui.c
+++ b/main_gui.c
@@ -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 @@ static bool DrawScrollingStatusText(const NewsItem *ni, int pos)
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);
}
}
diff --git a/makefiledir/Makefile.config_writer b/makefiledir/Makefile.config_writer
index 166973b53..fad5c78f2 100644
--- a/makefiledir/Makefile.config_writer
+++ b/makefiledir/Makefile.config_writer
@@ -73,6 +73,7 @@ $(MAKE_CONFIG):
$(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 @@ $(MAKE_CONFIG):
$(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))
diff --git a/makefiledir/Makefile.libdetection b/makefiledir/Makefile.libdetection
index c85ad0acc..a5160a309 100644
--- a/makefiledir/Makefile.libdetection
+++ b/makefiledir/Makefile.libdetection
@@ -66,6 +66,9 @@ SDL_CONFIG:=sdl-config
# 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 @@ WITH_SDL:=$(shell $(SDL_CONFIG) --version 2>/dev/null)
# 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
diff --git a/misc.c b/misc.c
index c164422d8..ca7b368fd 100644
--- a/misc.c
+++ b/misc.c
@@ -202,6 +202,38 @@ StringID RealAllocateName(const char *name, byte skip, bool check_double)
}
}
+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)
{
diff --git a/misc_gui.c b/misc_gui.c
index 2acc7da22..6969312c3 100644
--- a/misc_gui.c
+++ b/misc_gui.c
@@ -205,8 +205,8 @@ static const char *credits[] = {
" 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 @@ static const char *credits[] = {
" 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 @@ void SetHScrollCount(Window *w, int num)
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 @@ static void DelChar(Textbuf *tb)
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 @@ void DeleteTextBufferAll(Textbuf *tb)
* @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;
-
- tb->caretpos++;
+ 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 += len;
tb->caretxoffs += charwidth;
return true;
}
@@ -859,15 +876,25 @@ bool MoveTextBufferPos(Textbuf *tb, int navmode)
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 InitializeTextBuffer(Textbuf *tb, const char *buf, uint16 maxlength, uint16
*/
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 @@ int HandleEditBoxKey(Window *w, querystr_d *string, int wid, WindowEvent *e)
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);
}
diff --git a/namegen.c b/namegen.c
index 4e1946a9c..8403bec68 100644
--- a/namegen.c
+++ b/namegen.c
@@ -480,13 +480,15 @@ static byte MakeCzechTownName(char *buf, uint32 seed, const char *last)
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);
diff --git a/network.c b/network.c
index a90923966..73d7e7a0a 100644
--- a/network.c
+++ b/network.c
@@ -526,7 +526,7 @@ void ParseConnectionString(const char **player, const char **port, char *connect
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;
diff --git a/newgrf.c b/newgrf.c
index 3277d9a95..c04e3dd62 100644
--- a/newgrf.c
+++ b/newgrf.c
@@ -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 @@ static void LoadGRFSound(byte *buf, int len)
}
}
+/* 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 @@ static void DecodeSpecialSprite(uint num, GrfLoadingStage stage)
/* 0x0F */ { NULL, NULL, NULL, },
/* 0x10 */ { DefineGotoLabel, NULL, NULL, },
/* 0x11 */ { NULL, NULL, GRFSound, },
+ /* 0x12 */ { NULL, NULL, LoadFontGlyph, },
};
byte* buf;
diff --git a/newgrf_text.c b/newgrf_text.c
index 870ee73fb..6596c82d2 100644
--- a/newgrf_text.c
+++ b/newgrf_text.c
@@ -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 GRFTextEntry _grf_text[(1 << TABSIZE) * 3];
static byte _currentLangID = GRFLX_ENGLISH; //by default, english is used.
-static void TranslateTTDPatchCodes(char *str)
+static char *TranslateTTDPatchCodes(const char *str)
{
- char *c;
-
- 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;
+ char *tmp = malloc(strlen(str) * 10); /* Allocate space to allow for expansion */
+ char *d = tmp;
+ bool unicode = false;
+ WChar c = Utf8Consume(&str);
+
+ 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 @@ static void TranslateTTDPatchCodes(char *str)
*/
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 @@ StringID AddGRFString(uint32 grfid, uint16 stringid, byte langid_to_add, bool ne
/* 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++;
diff --git a/news_gui.c b/news_gui.c
index 4651ca20a..00b603047 100644
--- a/news_gui.c
+++ b/news_gui.c
@@ -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 byte getNews(byte i)
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 @@ static void DrawNewsString(int x, int y, uint16 color, const NewsItem *ni, uint
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);
}
}
diff --git a/openttd.c b/openttd.c
index fceee47d2..0103c279d 100644
--- a/openttd.c
+++ b/openttd.c
@@ -52,6 +52,7 @@
#include "genworld.h"
#include "date.h"
#include "clear_map.h"
+#include "fontcache.h"
#include <stdarg.h>
@@ -432,10 +433,15 @@ int ttd_main(int argc, char *argv[])
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 @@ bool AfterLoadGame(void)
}
}
+ if (CheckSavegameVersion(37)) {
+ ConvertNameArray();
+ }
+
return true;
}
diff --git a/openttd.h b/openttd.h
index 307c2aee4..204392741 100644
--- a/openttd.h
+++ b/openttd.h
@@ -464,6 +464,10 @@ enum {
EXPENSES_OTHER = 12,
};
+enum {
+ MAX_LANG = 64,
+};
+
// special string constants
enum SpecialStrings {
@@ -506,17 +510,17 @@ enum SpecialStrings {
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,
diff --git a/openttd.vcproj b/openttd.vcproj
index 6ff5dc66a..4399b9f35 100644
--- a/openttd.vcproj
+++ b/openttd.vcproj
@@ -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
diff --git a/openttd_vs80.vcproj b/openttd_vs80.vcproj
index b0bad38d4..4c5ebbae3 100644
--- a/openttd_vs80.vcproj
+++ b/openttd_vs80.vcproj
@@ -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>
diff --git a/saveload.c b/saveload.c
index 92a9112ff..811c85da5 100644
--- a/saveload.c
+++ b/saveload.c
@@ -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!
diff --git a/settings.c b/settings.c
index 91213d02a..86d73191c 100644
--- a/settings.c
+++ b/settings.c
@@ -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 @@ static const SettingDescGlobVarList _misc_settings[] = {
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()
};
diff --git a/strgen/strgen.c b/strgen/strgen.c
index ca477785b..487cc9eff 100644
--- a/strgen/strgen.c
+++ b/strgen/strgen.c
@@ -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 PutByte(byte c)
}
-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 @@ static void EmitSetXY(char *buf, int value)
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 @@ static void EmitPlural(char *buf, int value)
}
}
- PutByte(0x8D);
+ PutUtf8(SCC_PLURAL_LIST);
PutByte(TranslateArgumentIdx(argidx));
EmitWordList(words, nw);
}
@@ -372,7 +388,7 @@ static void EmitGender(char *buf, int value)
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 @@ static void EmitGender(char *buf, int value)
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 void EmitGender(char *buf, int value)
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},
-
- {"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
+ {"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", 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
-
- {"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},
+ {"COMPANY", EmitSingleChar, SCC_STRING1, 1, 0},
+ {"PLAYERNAME", EmitSingleChar, SCC_STRING1, 1, 0},
+ {"VEHICLE", EmitSingleChar, SCC_STRING1, 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},
-
- // 0x85 is the marker for escaped commands
+ {"DATE_TINY", EmitSingleChar, SCC_DATE_TINY, 1, 0},
+ {"DATE_SHORT", EmitSingleChar, SCC_DATE_SHORT, 1, 0},
+ {"DATE_LONG", EmitSingleChar, SCC_DATE_LONG, 1, 0},
- {"SKIP", EmitSingleByte, 0x86, 1, 0},
+ {"SKIP", EmitSingleChar, SCC_SKIP, 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 int TranslateArgumentIdx(int argidx)
static void PutArgidxCommand(void)
{
- PutByte(0x8C);
+ PutUtf8(SCC_ARG_INDEX);
PutByte(TranslateArgumentIdx(_cur_argidx));
}
@@ -1052,7 +1066,7 @@ static void PutCommandString(const char *str)
if (cs == NULL) break;
if (casei != -1) {
- PutByte(0x9D); // {SETCASE}
+ PutUtf8(SCC_SETCASE); // {SETCASE}
PutByte(casei);
}
@@ -1163,7 +1177,7 @@ static void WriteLangfile(const char *filename, int show_todo)
// 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);
diff --git a/string.c b/string.c
index f6f10de13..865aa1ff9 100644
--- a/string.c
+++ b/string.c
@@ -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 @@ char* CDECL str_fmt(const char* str, ...)
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 @@ void str_strip_colours(char *str)
* @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 @@ int CDECL vsnprintf(char *str, size_t size, const char *format, va_list ap)
#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;
+}
diff --git a/string.h b/string.h
index f86854a88..c795b3336 100644
--- a/string.h
+++ b/string.h
@@ -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_validate(char *str);
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 @@ typedef enum CharSetFilter {
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 @@ typedef enum CharSetFilter {
* @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);
+
+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;
+}
-/** Convert the given string to lowercase */
-void strtolower(char *str);
#endif /* STRING_H */
diff --git a/strings.c b/strings.c
index ef6bd90c8..a38febca5 100644
--- a/strings.c
+++ b/strings.c
@@ -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 *GetString(char *buffr, StringID string, const char* last)
}
+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 const Units units[] = {
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++;
+ case SCC_SETX: // {SETX}
+ if (buff + Utf8CharLen(SCC_SETX) + 1 < last) {
+ buff += Utf8Encode(buff, SCC_SETX);
+ *buff++ = *str++;
+ }
+ break;
+
+ 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;
}
- 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_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 @@ static char* FormatString(char* buff, const char* str, const int32* argv, uint c
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 @@ static char* FormatString(char* buff, const char* str, const int32* argv, uint c
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 @@ static char* FormatString(char* buff, const char* str, const int32* argv, uint c
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 @@ static char* FormatString(char* buff, const char* str, const int32* argv, uint c
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 @@ static char* FormatString(char* buff, const char* str, const int32* argv, uint c
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 @@ static char* FormatString(char* buff, const char* str, const int32* argv, uint c
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 @@ static char* FormatString(char* buff, const char* str, const int32* argv, uint c
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 @@ static char* FormatString(char* buff, const char* str, const int32* argv, uint c
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 @@ static char* FormatString(char* buff, const char* str, const int32* argv, uint c
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 @@ static char* FormatString(char* buff, const char* str, const int32* argv, uint c
break;
}
- default:
- error("!invalid escape sequence in string");
- }
- break;
-
- case 0x86: // {SKIP}
- argv++;
- break;
+ case SCC_SKIP: // {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;
+ // 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;
- 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 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 0x8B: // {COMMA}
- buff = FormatCommaNumber(buff, GetInt32(&argv), last);
- break;
+ case SCC_COMMA: // {COMMA}
+ buff = FormatCommaNumber(buff, GetInt32(&argv), last);
+ break;
- case 0x8C: // Move argument pointer
- argv = argv_orig + (byte)*str++;
- break;
+ case SCC_ARG_INDEX: // 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 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 0x8E: // {NUM}
- buff = FormatNoCommaNumber(buff, GetInt32(&argv), last);
- break;
+ case SCC_NUM: // {NUM}
+ buff = FormatNoCommaNumber(buff, GetInt32(&argv), last);
+ break;
- case 0x8F: // {CURRENCY}
- buff = FormatGenericCurrency(buff, _currency, GetInt32(&argv), false, last);
- break;
+ case SCC_CURRENCY: // {CURRENCY}
+ buff = FormatGenericCurrency(buff, _currency, GetInt32(&argv), false, last);
+ 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;
+ 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;
+ }
+ buff = GetStringWithArgs(buff, str, temp, last);
+ break;
}
- buff = GetStringWithArgs(buff, str, temp, last);
- } break;
- case 0x9A: { // {STATION}
- const Station* st = GetStation(GetInt32(&argv));
+ 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);
+ 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;
}
- break;
- }
- case 0x9B: { // {TOWN}
- const Town* t = GetTown(GetInt32(&argv));
- int32 temp[1];
- assert(IsValidTown(t));
+ case SCC_TOWN_NAME: { // {TOWN}
+ const Town* t = GetTown(GetInt32(&argv));
+ int32 temp[1];
- temp[0] = t->townnameparts;
- buff = GetStringWithArgs(buff, t->townnametype, temp, last);
- break;
- }
+ assert(IsValidTown(t));
- case 0x9C: { // {CURRENCY64}
- buff = FormatGenericCurrency(buff, _currency, GetInt64(&argv), false, last);
- break;
- }
+ temp[0] = t->townnameparts;
+ buff = GetStringWithArgs(buff, t->townnametype, temp, 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_CURRENCY_64: { // {CURRENCY64}
+ buff = FormatGenericCurrency(buff, _currency, GetInt64(&argv), false, last);
+ 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_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--;
}
- // Otherwise skip to the next case
- str += 3 + (str[1] << 8) + str[2];
- num--;
+ break;
}
- break;
- }
- default:
- if (buff != last) *buff++ = b;
+ default:
+ if (buff + Utf8CharLen(b) < last) buff += Utf8Encode(buff, b);
+ break;
}
}
*buff = '\0';
@@ -910,11 +926,12 @@ static char* FormatString(char* buff, const char* str, const int32* argv, uint c
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 @@ bool ReadLanguagePack(int lang_index)
{
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 @@ void InitializeLanguagePacks(void)
int fallback;
LanguagePack hdr;
FILE *in;
- char *files[32];
+ char *files[MAX_LANG];
const char* lang;
lang = GetCurrentLocale("LC_MESSAGES");
diff --git a/strings.h b/strings.h
index 0aa105545..dd4fd179d 100644
--- a/strings.h
+++ b/strings.h
@@ -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];
diff --git a/table/control_codes.h b/table/control_codes.h
new file mode 100644
index 000000000..a1ff42a89
--- /dev/null
+++ b/table/control_codes.h
@@ -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 */
diff --git a/table/namegen.h b/table/namegen.h
index 4a591de39..40497e017 100644
--- a/table/namegen.h
+++ b/table/namegen.h
@@ -304,7 +304,7 @@ static const char *name_austrian_a2[] = {
"Aus",
"Alten",
"Braun",
- "Vsl",
+ "Vösl",
"Mittern",
"Nuss",
"Neu",
@@ -326,9 +326,9 @@ static const char *name_austrian_a2[] = {
"Frauen",
"Herren",
"Hof",
- "Htt",
+ "Hütt",
"Kaisers",
- "Knigs",
+ "Königs",
"Knittel",
"Lang",
"Ober",
@@ -340,7 +340,7 @@ static const char *name_austrian_a2[] = {
"Stocker",
"Unter",
"Utten",
- "Vsen",
+ "Vösen",
"Vill",
"Weissen"
};
@@ -386,7 +386,7 @@ static const char *name_austrian_a5[] = {
static const char *name_austrian_a6[] = {
"Aegyd",
- "Andr",
+ "Andrä",
"Georgen",
"Jakob",
"Johann",
@@ -399,7 +399,7 @@ static const char *name_austrian_a6[] = {
"Nikolai",
"Oswald",
"Peter",
- "Plten",
+ "Pölten",
"Stefan",
"Stephan",
"Thomas",
@@ -460,10 +460,10 @@ static const char *name_german_real[] = {
"Gera",
"Kassel",
"Kiel",
- "Kln",
- "Lbeck",
+ "Köln",
+ "Lübeck",
"Magdeburg",
- "Mnchen",
+ "München",
"Potsdam",
"Stuttgart",
"Wiesbaden"
@@ -486,7 +486,7 @@ static const char *name_german_1[] = {
"Cloppen",
"Co",
"Duis",
- "Dssel",
+ "Düssel",
"Dannen",
"Elb",
"Els",
@@ -500,7 +500,7 @@ static const char *name_german_1[] = {
"Frei",
"Freuden",
"Fried",
- "Frsten",
+ "Fürsten",
"Hahn",
"Ham",
"Harz",
@@ -521,10 +521,10 @@ static const char *name_german_1[] = {
"Langen",
"Lim",
"Lohr",
- "Lne",
+ "Lüne",
"Mel",
"Michels",
- "Mhl",
+ "Mühl",
"Naum",
"Nest",
"Nord",
@@ -532,7 +532,7 @@ static const char *name_german_1[] = {
"Nien",
"Nidda",
"Nieder",
- "Nrn",
+ "Nürn",
"Ober",
"Offen",
"Osna",
@@ -546,18 +546,18 @@ static const char *name_german_1[] = {
"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 @@ static const char *name_german_1[] = {
"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 @@ static const char *name_german_2[] = {
"heim",
"horst",
"mund",
- "mnster",
+ "münster",
"stadt",
"wald"
};
@@ -717,7 +717,7 @@ static const char *name_french_real[] = {
"St. Tropez",
"Marseilles",
"Narbonne",
- "Ste",
+ "Sète",
"Aurillac",
"Gueret",
"Le Creusot",
@@ -725,8 +725,8 @@ static const char *name_french_real[] = {
"Auxerre",
"Versailles",
"Meaux",
- "Chlons",
- "Compigne",
+ "Châlons",
+ "Compiègne",
"Metz",
"Chaumont",
"Langres",
@@ -739,8 +739,8 @@ static const char *name_french_real[] = {
"Le Mans",
"Angers",
"Nantes",
- "Chteauroux",
- "Orlans",
+ "Châteauroux",
+ "Orléans",
"Lisieux",
"Cherbourg",
"Morlaix",
@@ -751,7 +751,7 @@ static const char *name_french_real[] = {
"Troyes",
"Charolles",
"Grenoble",
- "Chambry",
+ "Chambéry",
"Tours",
"St. Brieuc",
"St. Malo",
@@ -765,11 +765,11 @@ static const char *name_french_real[] = {
"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 @@ static const char *name_swedish_2[] = {
"Es",
"Fin",
"Fisk",
- "Grn",
+ "Grön",
"Hag",
"Halm",
"Karl",
@@ -916,17 +916,17 @@ static const char *name_swedish_2[] = {
"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 @@ static const char *name_swedish_2b[] = {
"o",
"u",
"y",
- "",
- "",
- ""
+ "å",
+ "ä",
+ "ö"
};
static const char *name_swedish_2c[] = {
@@ -1029,25 +1029,25 @@ static const char *name_swedish_3[] = {
"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 @@ static const char *name_finnish_real[] = {
"Espoo",
"Helsinki",
"Tapiola",
- "Jrvel",
+ "Järvelä",
"Lahti",
"Kotka",
"Hamina",
@@ -1249,26 +1249,26 @@ static const char *name_finnish_1[] = {
"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 @@ static const char *name_finnish_2[] = {
"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 @@ static const char *name_czech_real[] = {
"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 @@ static const char *name_czech_real[] = {
"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 @@ typedef enum CzechPattern {
/* [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 @@ typedef struct CzechNameAdj {
// 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 @@ static const CzechNameSubst name_czech_subst_full[] = {
{ 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 @@ static const CzechNameSubst name_czech_subst_full[] = {
// 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 @@ static const CzechNameSubst name_czech_subst_stem[] = {
{ 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 @@ static const char *name_czech_subst_postfix[] = {
// 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 CzechNameSubst name_czech_subst_ending[] = {
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 @@ static const char *name_romanian_real[]= {
"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 @@ static const char *name_romanian_real[]= {
"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 @@ static const char *name_romanian_real[]= {
"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 @@ static const char *name_romanian_real[]= {
"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_slovak_real[] = {
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 @@ static const char *name_norwegian_1[] = {
"Gaus",
"Galte",
"Geir",
- "Gls",
+ "Gløs",
"Gran",
"Grind",
"Grims",
- "Grn",
- "Grt",
+ "Grøn",
+ "Grøt",
"Gulle",
"Haka",
"Hammer",
@@ -2150,7 +2150,7 @@ static const char *name_norwegian_1[] = {
"Kjerring",
"Knatte",
"Krok",
- "Ky",
+ "Køy",
"Lang",
"Lauv",
"Leir",
@@ -2158,7 +2158,7 @@ static const char *name_norwegian_1[] = {
"Logn",
"Lo",
"Lyng",
- "Ln",
+ "Løn",
"Mesna",
"Mel",
"Mo",
@@ -2178,9 +2178,9 @@ static const char *name_norwegian_1[] = {
"Sel",
"Sol",
"Sjur",
- "Skr",
- "Sltt",
- "Stjr",
+ "Skår",
+ "Slått",
+ "Stjør",
"Stor",
"Svart",
"Svens",
@@ -2193,7 +2193,7 @@ static const char *name_norwegian_1[] = {
"Vest",
"Vesle",
"Vik",
- "Vg"
+ "Våg"
};
static const char *name_norwegian_2[] = {
@@ -2208,7 +2208,7 @@ static const char *name_norwegian_2[] = {
"bugen",
"by",
"bygd",
- "b",
+ "bø",
"dal",
"egga",
"eid",
@@ -2226,10 +2226,10 @@ static const char *name_norwegian_2[] = {
"heim",
"hella",
"hovda",
- "ha",
- "hgda",
+ "høa",
+ "høgda",
"kampen",
- "kjlen",
+ "kjølen",
"kollen",
"kroken",
"land",
@@ -2244,7 +2244,7 @@ static const char *name_norwegian_2[] = {
"rud",
"sand",
"set",
- "sjen",
+ "sjøen",
"skogen",
"slette",
"snipa",
@@ -2253,7 +2253,7 @@ static const char *name_norwegian_2[] = {
"stulen",
"sund",
"svingen",
- "stra",
+ "sætra",
"tinden",
"tun",
"vang",
@@ -2261,9 +2261,9 @@ static const char *name_norwegian_2[] = {
"veid",
"vik",
"voll",
- "vg",
+ "våg",
"um",
- "sen"
+ "åsen"
};
static const char *name_norwegian_real[] = {
@@ -2271,12 +2271,12 @@ static const char *name_norwegian_real[] = {
"Arendal",
"Askim",
"Bergen",
- "Bod",
+ "Bodø",
"Brevik",
"Bryne",
- "Brnnysund",
+ "Brønnøysund",
"Drammen",
- "Drbak",
+ "Drøbak",
"Egersund",
"Elverum",
"Farsund",
@@ -2284,10 +2284,10 @@ static const char *name_norwegian_real[] = {
"Finnsnes",
"Flekkefjord",
"Flora",
- "Fosnavg",
+ "Fosnavåg",
"Fredrikstad",
- "Frde",
- "Gjvik",
+ "Førde",
+ "Gjøvik",
"Grimstad",
"Halden",
"Hamar",
@@ -2296,13 +2296,13 @@ static const char *name_norwegian_real[] = {
"Haugesund",
"Holmestrand",
"Horten",
- "Jrpeland",
+ "Jørpeland",
"Kirkenes",
"Kolvereid",
"Kongsberg",
"Kongsvinger",
"Kopervik",
- "Krager",
+ "Kragerø",
"Kristiansand",
"Kristiansund",
"Langesund",
@@ -2312,16 +2312,16 @@ static const char *name_norwegian_real[] = {
"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 @@ static const char *name_norwegian_real[] = {
"Otta",
"Porsgrunn",
"Ringerike",
- "Risr",
+ "Risør",
"Rjukan",
"Sandefjord",
"Sandnes",
- "Sandnessjen",
+ "Sandnessjøen",
"Sandvika",
"Sarpsborg",
"Sauda",
@@ -2345,152 +2345,152 @@ static const char *name_norwegian_real[] = {
"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 @@ static const char *name_swiss_real[] = {
"Arosa",
"Appenzell",
"Arbon",
- "Altsttten",
+ "Altstätten",
"Baar",
"Baden",
"Bellinzona",
@@ -2512,20 +2512,20 @@ static const char *name_swiss_real[] = {
"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 @@ static const char *name_swiss_real[] = {
"Jona",
"Kriens",
"Kloten",
- "Kniz",
+ "Köniz",
"Kreuzlingen",
- "Ksnacht",
+ "Küsnacht",
"Agen",
"Lancy",
"La Chaux-de-Fonds",
@@ -2556,7 +2556,7 @@ static const char *name_swiss_real[] = {
"Lyss",
"Luzern",
"Martigny",
- "Mnchenstein",
+ "Münchenstein",
"Meyrin",
"Montreux",
"Monthey",
@@ -2564,7 +2564,7 @@ static const char *name_swiss_real[] = {
"Murten",
"Moutier",
"Muttenz",
- "Neuchtel",
+ "Neuchâtel",
"Neuhausen am Rheinfall",
"Nyon",
"Olten",
@@ -2593,11 +2593,11 @@ static const char *name_swiss_real[] = {
"St. Moritz",
"Sion",
"Spiez",
- "Stfa",
+ "Stäfa",
"Sursee",
"Schwyz",
"Thalwil",
- "Thnex",
+ "Thônex",
"Thun",
"Uster",
"Uzwil",
@@ -2605,7 +2605,7 @@ static const char *name_swiss_real[] = {
"Volketswil",
"Versoix",
"Vevey",
- "Wdenswil",
+ "Wädenswil",
"Wettingen",
"Wil",
"Wallisellen",
@@ -2616,7 +2616,7 @@ static const char *name_swiss_real[] = {
"Yverdon-les-Bains",
"Zollikon",
"Zofingen",
- "Zrich",
+ "Zürich",
"Zug",
};
@@ -2626,12 +2626,12 @@ static const char *name_danish_1[] = {
"Nye ",
"Store ",
"Kirke ",
- "Nrre ",
+ "Nørre ",
"Vester ",
- "Snder ",
- "ster ",
+ "Sønder ",
+ "Øster ",
"Hvide ",
- "Hje ",
+ "Høje ",
"Kongens ",
};
@@ -2643,7 +2643,7 @@ static const char *name_danish_2[] = {
"Bede",
"Birke",
"Bjerring",
- "Bjver",
+ "Bjæver",
"Blommens",
"Blok",
"Bolder",
@@ -2656,7 +2656,7 @@ static const char *name_danish_2[] = {
"Fredens",
"Frederiks",
"Fugle",
- "Fre",
+ "Fåre",
"Gille",
"Gis",
"Givs",
@@ -2673,11 +2673,11 @@ static const char *name_danish_2[] = {
"Hol",
"Horn",
"Humle",
- "Hj",
- "Hr",
+ "Høj",
+ "Hør",
"Is",
"Jyde",
- "Jgers",
+ "Jægers",
"Karls",
"Klov",
"Kokke",
@@ -2689,24 +2689,24 @@ static const char *name_danish_2[] = {
"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 @@ static const char *name_danish_2[] = {
"Vand",
"Vej",
"Vor",
- "Vr",
- "r",
- "l"
+ "Vær",
+ "Ør",
+ "Ål"
};
static const char *name_danish_3[] = {
@@ -2729,21 +2729,21 @@ static const char *name_danish_3[] = {
"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_danish_3[] = {
};
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_prefix[] = {
};
static const char *name_turkish_middle[] = {
- "aga",
+ "agaç",
"ayva",
- "am",
+ "çam",
"elma",
"kurt",
"pazar",
@@ -2787,10 +2787,10 @@ static const char *name_turkish_suffix[] = {
"kale",
"kaya",
"kent",
- "ky",
+ "köy",
"ova",
- "z",
- "ren",
+ "özü",
+ "ören",
"pazar",
"saray",
"tepe",
@@ -2812,8 +2812,8 @@ static const char *name_turkish_real[] = {
"Bolu",
"Burdur",
"Bursa",
- "anakkale",
- "ankiri",
+ "Çanakkale",
+ "Çankiri",
"Denizli",
"Diyarbakir",
"Edirne",
@@ -2821,10 +2821,10 @@ static const char *name_turkish_real[] = {
"Erzurum",
"Eskisehir",
"Giresun",
- "Gmshane",
+ "Gümüshane",
"Hatay",
"Isparta",
- "iel",
+ "içel",
"istanbul",
"izmir",
"Kars",
@@ -2833,7 +2833,7 @@ static const char *name_turkish_real[] = {
"Kirklareli",
"Kocaeli",
"Konya",
- "Ktahya",
+ "Kütahya",
"Malatya",
"Manisa",
"Kahramanmaras",
@@ -2861,9 +2861,9 @@ static const char *name_turkish_real[] = {
"Ardahan",
"Igdir",
"Yalova",
- "Karabk",
+ "Karabük",
"Osmaniye",
- "Dzce"
+ "Düzce"
};
static const char *name_italian_real[] = {
diff --git a/table/unicode.h b/table/unicode.h
new file mode 100644
index 000000000..2bbd6624c
--- /dev/null
+++ b/table/unicode.h
@@ -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 */
+};
diff --git a/unix.c b/unix.c
index 97d287bea..a5fc71c10 100644
--- a/unix.c
+++ b/unix.c
@@ -291,7 +291,7 @@ void CSleep(int milliseconds)
#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
diff --git a/variables.h b/variables.h
index d1f213a62..2ea03c740 100644
--- a/variables.h
+++ b/variables.h
@@ -323,12 +323,12 @@ VARDEF char _ini_videodriver[16], _ini_musicdriver[16], _ini_sounddriver[16];
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;
diff --git a/video/win32_v.c b/video/win32_v.c
index 1894e7a24..7511df597 100644
--- a/video/win32_v.c
+++ b/video/win32_v.c
@@ -349,14 +349,15 @@ static LRESULT CALLBACK WndProcGdi(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lP
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;
diff --git a/win32.c b/win32.c
index 4070f8546..b8e34c4d5 100644
--- a/win32.c
+++ b/win32.c
@@ -926,39 +926,67 @@ void DeterminePaths(void)
*/
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;
- OpenClipboard(NULL);
- cbuf = GetClipboardData(CF_TEXT);
- data = GlobalLock(cbuf); // clipboard data
- dataptr = data;
+ WChar c;
+ uint16 width, length;
- 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 (IsClipboardFormatAvailable(CF_UNICODETEXT)) {
+ int bytec;
- if (length == 0) return false;
+ OpenClipboard(NULL);
+ cbuf = GetClipboardData(CF_UNICODETEXT);
- 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;
+ ptr = GlobalLock(cbuf);
+ bytec = WideCharToMultiByte(CP_UTF8, 0, (wchar_t*)ptr, -1, utf8_buf, lengthof(utf8_buf), NULL, NULL);
+ GlobalUnlock(cbuf);
+ CloseClipboard();
- tb->length += length;
- tb->caretpos += length;
- tb->buf[tb->length] = '\0'; // terminating zero
+ 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);
+ 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;
}
diff --git a/window.c b/window.c
index 939ca49ba..5f20555ff 100644
--- a/window.c
+++ b/window.c
@@ -1422,7 +1422,7 @@ void HandleKeypress(uint32 key)
// 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;
diff --git a/window.h b/window.h
index a7927c51a..03f3589bd 100644
--- a/window.h
+++ b/window.h
@@ -160,7 +160,7 @@ struct WindowEvent {
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;