diff --git a/Makefile b/Makefile index 4b73d5fdeb..62d420dbae 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 05301d8054..4c62a84f8c 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 1b5ef1c645..98f6619b29 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 2ea5bda165..7bc81139a7 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 ba4be2eeb3..60865473ae 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 1a35162494..c0005f8dbf 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 0000000000..4cfba83e81 --- /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 +#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 0000000000..249f35cd0f --- /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 415f7a0cc5..0529c1646a 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 0f446abbd6..314de65d7f 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 -// 2 - SETXY -// 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 0d63d22873..8636a36a61 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 277fbb8566..376a969aae 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 9b14336855..5c26640851 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 0c759a39d6..40ddcad60c 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 166973b53a..fad5c78f25 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 c85ad0accb..a5160a309b 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 c164422d85..ca7b368fdb 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 2acc7da223..6969312c3c 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; + size_t len = Utf8CharLen(key); + if (tb->length < (tb->maxlength - len) && (tb->maxwidth == 0 || tb->width + charwidth <= tb->maxwidth)) { + memmove(tb->buf + tb->caretpos + len, tb->buf + tb->caretpos, tb->length - tb->caretpos + 1); + Utf8Encode(tb->buf + tb->caretpos, key); + tb->length += len; + tb->width += charwidth; - tb->caretpos++; + tb->caretpos += len; tb->caretxoffs += charwidth; return true; } @@ -859,15 +876,25 @@ 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 4e1946a9c9..8403bec681 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 a909239661..73d7e7a0ad 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 3277d9a950..c04e3dd620 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> + * + * 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 870ee73fb7..6596c82d2f 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; + char *tmp = malloc(strlen(str) * 10); /* Allocate space to allow for expansion */ + char *d = tmp; + bool unicode = false; + WChar c = Utf8Consume(&str); - for (c = str; *c != '\0'; c++) { - switch ((byte)*c) { - case 0x01: c++; break; - case 0x0D: *c = 10; break; - case 0x0E: *c = 8; break; - case 0x0F: *c = 9; break; - case 0x1F: *c = 2; c += 2; break; + if (c == 0x00DE) { + /* The thorn ('þ') indicates a unicode string to TTDPatch */ + unicode = true; + } else { + str--; + } + + for (;;) { + const char *tmp = str; /* Used for UTF-8 decoding */ + + c = (byte)*str++; + if (c == 0) break; + + switch (c) { + case 0x01: + d += Utf8Encode(d, SCC_SETX); + *d++ = *str++; + break; + case 0x0D: *d++ = 10; break; + case 0x0E: d += Utf8Encode(d, SCC_TINYFONT); break; + case 0x0F: d += Utf8Encode(d, SCC_BIGFONT); break; + case 0x1F: + d += Utf8Encode(d, SCC_SETXY); + *d++ = *str++; + *d++ = *str++; + break; case 0x7B: case 0x7C: case 0x7D: - case 0x7E: *c = 0x8E; break; - case 0x81: c += 2; break; - case 0x85: *c = 0x86; break; - case 0x88: *c = 15; break; - case 0x89: *c = 16; break; - case 0x8A: *c = 17; break; - case 0x8B: *c = 18; break; - case 0x8C: *c = 19; break; - case 0x8D: *c = 20; break; - case 0x8E: *c = 21; break; - case 0x8F: *c = 22; break; - case 0x90: *c = 23; break; - case 0x91: *c = 24; break; - case 0x92: *c = 25; break; - case 0x93: *c = 26; break; - case 0x94: *c = 27; break; - case 0x95: *c = 28; break; - case 0x96: *c = 29; break; - case 0x97: *c = 30; break; - case 0x98: *c = 31; break; + case 0x7E: d += Utf8Encode(d, SCC_NUM); break; + case 0x7F: d += Utf8Encode(d, SCC_CURRENCY); break; + case 0x80: d += Utf8Encode(d, SCC_STRING); break; + case 0x81: { + StringID string; + string = *str++; + string |= *str++ << 8; + d += Utf8Encode(d, SCC_STRING_ID); + d += Utf8Encode(d, string); + break; + } + case 0x82: d += Utf8Encode(d, SCC_DATE_TINY); break; + case 0x83: d += Utf8Encode(d, SCC_DATE_SHORT); break; + case 0x84: d += Utf8Encode(d, SCC_VELOCITY); break; + case 0x85: d += Utf8Encode(d, SCC_SKIP); break; + case 0x86: /* "Rotate down top 4 words on stack" */ break; + case 0x87: d += Utf8Encode(d, SCC_VOLUME); break; + case 0x88: d += Utf8Encode(d, SCC_BLUE); break; + case 0x89: d += Utf8Encode(d, SCC_SILVER); break; + case 0x8A: d += Utf8Encode(d, SCC_GOLD); break; + case 0x8B: d += Utf8Encode(d, SCC_RED); break; + case 0x8C: d += Utf8Encode(d, SCC_PURPLE); break; + case 0x8D: d += Utf8Encode(d, SCC_LTBROWN); break; + case 0x8E: d += Utf8Encode(d, SCC_ORANGE); break; + case 0x8F: d += Utf8Encode(d, SCC_GREEN); break; + case 0x90: d += Utf8Encode(d, SCC_YELLOW); break; + case 0x91: d += Utf8Encode(d, SCC_DKGREEN); break; + case 0x92: d += Utf8Encode(d, SCC_CREAM); break; + case 0x93: d += Utf8Encode(d, SCC_BROWN); break; + case 0x94: d += Utf8Encode(d, SCC_WHITE); break; + case 0x95: d += Utf8Encode(d, SCC_LTBLUE); break; + case 0x96: d += Utf8Encode(d, SCC_GRAY); break; + case 0x97: d += Utf8Encode(d, SCC_DKBLUE); break; + case 0x98: d += Utf8Encode(d, SCC_BLACK); break; + case 0x9E: d += Utf8Encode(d, 0x20AC); break; // Euro + case 0x9F: d += Utf8Encode(d, 0x0178); break; // Y with diaeresis + case 0xA0: d += Utf8Encode(d, SCC_UPARROW); break; + case 0xAA: d += Utf8Encode(d, SCC_DOWNARROW); break; + case 0xAC: d += Utf8Encode(d, SCC_CHECKMARK); break; + case 0xAD: d += Utf8Encode(d, SCC_CROSS); break; + case 0xAF: d += Utf8Encode(d, SCC_RIGHTARROW); break; + case 0xB4: d += Utf8Encode(d, SCC_TRAIN); break; + case 0xB5: d += Utf8Encode(d, SCC_LORRY); break; + case 0xB6: d += Utf8Encode(d, SCC_BUS); break; + case 0xB7: d += Utf8Encode(d, SCC_PLANE); break; + case 0xB8: d += Utf8Encode(d, SCC_SHIP); break; default: + if (unicode) { + d += Utf8Encode(d, Utf8Consume(&tmp)); + str = tmp; + break; + } + /* Validate any unhandled character */ - if (!IsValidAsciiChar(*c, CS_ALPHANUMERAL)) *c = '?'; + if (!IsValidChar(c, CS_ALPHANUMERAL)) c = '?'; + d += Utf8Encode(d, c); break; } } + + *d = '\0'; + return realloc(tmp, strlen(tmp) + 1); } @@ -201,6 +260,7 @@ 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 4651ca20a5..00b6030478 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 fceee47d2f..0103c279d7 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 @@ -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 307c2aee45..204392741f 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 6ff5dc66aa..4399b9f352 100644 --- a/openttd.vcproj +++ b/openttd.vcproj @@ -234,6 +234,9 @@ + + @@ -478,6 +481,9 @@ + + @@ -853,6 +859,9 @@ + + @@ -910,6 +919,9 @@ + + diff --git a/openttd_vs80.vcproj b/openttd_vs80.vcproj index b0bad38d4e..4c5ebbae3b 100644 --- a/openttd_vs80.vcproj +++ b/openttd_vs80.vcproj @@ -560,6 +560,10 @@ RelativePath=".\fios.c" > + + @@ -931,6 +935,10 @@ RelativePath=".\fileio.h" > + + @@ -1435,6 +1443,10 @@ RelativePath=".\table\clear_land.h" > + + @@ -1511,6 +1523,10 @@ RelativePath=".\table\tunnel_land.h" > + + diff --git a/saveload.c b/saveload.c index 92a9112ffe..811c85da57 100644 --- a/saveload.c +++ b/saveload.c @@ -30,7 +30,7 @@ #include "variables.h" #include -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 91213d02a8..86d73191cf 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 ca477785b5..487cc9eff6 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 #include #include @@ -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}, + {"BLUE", EmitSingleChar, SCC_BLUE, 0, 0}, + {"SILVER", EmitSingleChar, SCC_SILVER, 0, 0}, + {"GOLD", EmitSingleChar, SCC_GOLD, 0, 0}, + {"RED", EmitSingleChar, SCC_RED, 0, 0}, + {"PURPLE", EmitSingleChar, SCC_PURPLE, 0, 0}, + {"LTBROWN", EmitSingleChar, SCC_LTBROWN, 0, 0}, + {"ORANGE", EmitSingleChar, SCC_ORANGE, 0, 0}, + {"GREEN", EmitSingleChar, SCC_GREEN, 0, 0}, + {"YELLOW", EmitSingleChar, SCC_YELLOW, 0, 0}, + {"DKGREEN", EmitSingleChar, SCC_DKGREEN, 0, 0}, + {"CREAM", EmitSingleChar, SCC_CREAM, 0, 0}, + {"BROWN", EmitSingleChar, SCC_BROWN, 0, 0}, + {"WHITE", EmitSingleChar, SCC_WHITE, 0, 0}, + {"LTBLUE", EmitSingleChar, SCC_LTBLUE, 0, 0}, + {"GRAY", EmitSingleChar, SCC_GRAY, 0, 0}, + {"DKBLUE", EmitSingleChar, SCC_DKBLUE, 0, 0}, + {"BLACK", EmitSingleChar, SCC_BLACK, 0, 0}, - {"CURRCOMPACT", EmitEscapedByte, 0, 1, 0}, // compact currency (32 bits) - {"REV", EmitEscapedByte, 2, 0, 0}, // openttd revision string - {"SHORTCARGO", EmitEscapedByte, 3, 2, 0}, // short cargo description, only ### tons, or ### litres - {"CURRCOMPACT64", EmitEscapedByte, 4, 2, 0}, // compact currency 64 bits + {"CURRCOMPACT", EmitSingleChar, SCC_CURRENCY_COMPACT, 1, 0}, // compact currency (32 bits) + {"REV", EmitSingleChar, SCC_REVISION, 0, 0}, // openttd revision string + {"SHORTCARGO", EmitSingleChar, SCC_CARGO_SHORT, 2, 0}, // short cargo description, only ### tons, or ### litres + {"CURRCOMPACT64", EmitSingleChar, SCC_CURRENCY_COMPACT_64, 2, 0}, // compact currency 64 bits // These are special versions of {STRING1} // The first string includes the second string. - {"COMPANY", EmitEscapedByte, 5, 1, 0}, - {"PLAYERNAME", EmitEscapedByte, 5, 1, 0}, - {"VEHICLE", EmitEscapedByte, 5, 1, 0}, + {"COMPANY", EmitSingleChar, SCC_STRING1, 1, 0}, + {"PLAYERNAME", EmitSingleChar, SCC_STRING1, 1, 0}, + {"VEHICLE", EmitSingleChar, SCC_STRING1, 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 + {"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", 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}, + {"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}, + {"DATE_TINY", EmitSingleChar, SCC_DATE_TINY, 1, 0}, + {"DATE_SHORT", EmitSingleChar, SCC_DATE_SHORT, 1, 0}, + {"DATE_LONG", EmitSingleChar, SCC_DATE_LONG, 1, 0}, - {"VELOCITY", EmitSingleByte, 0x84, 1, 0}, + {"SKIP", EmitSingleChar, SCC_SKIP, 1, 0}, - // 0x85 is the marker for escaped commands - - {"SKIP", EmitSingleByte, 0x86, 1, 0}, - - {"STRING", EmitSingleByte, 0x88, 1, C_CASE}, + {"STRING", EmitSingleChar, SCC_STRING, 1, C_CASE}, // Numbers - {"COMMA", EmitSingleByte, 0x8B, 1, 0}, // Number with comma - {"NUM", EmitSingleByte, 0x8E, 1, 0}, // Signed number + {"COMMA", EmitSingleChar, SCC_COMMA, 1, 0}, // Number with comma + {"NUM", EmitSingleChar, SCC_NUM, 1, 0}, // Signed number - {"CURRENCY", EmitSingleByte, 0x8F, 1, 0}, - {"CURRENCY64", EmitSingleByte, 0x9C, 2, 0}, + {"CURRENCY", EmitSingleChar, SCC_CURRENCY, 1, 0}, + {"CURRENCY64", EmitSingleChar, SCC_CURRENCY_64, 2, 0}, - {"WAYPOINT", EmitSingleByte, 0x99, 1, 0}, // waypoint name - {"STATION", EmitSingleByte, 0x9A, 1, 0}, - {"TOWN", EmitSingleByte, 0x9B, 1, 0}, + {"WAYPOINT", EmitSingleChar, SCC_WAYPOINT_NAME, 1, 0}, // waypoint name + {"STATION", EmitSingleChar, SCC_STATION_NAME, 1, 0}, + {"TOWN", EmitSingleChar, SCC_TOWN_NAME, 1, 0}, // 0x9D is used for the pseudo command SETCASE // 0x9E is used for case switching - {"", EmitSingleByte, '\n', 0, C_DONTCOUNT}, - {"{", EmitSingleByte, '{', 0, C_DONTCOUNT}, - {"UPARROW", EmitSingleByte, 0x80, 0, 0}, - {"SMALLUPARROW", EmitSingleByte, 0x90, 0, 0}, - {"SMALLDOWNARROW", EmitSingleByte, 0x91, 0, 0}, - {"TRAIN", EmitSingleByte, 0x94, 0, 0}, - {"LORRY", EmitSingleByte, 0x95, 0, 0}, - {"BUS", EmitSingleByte, 0x96, 0, 0}, - {"PLANE", EmitSingleByte, 0x97, 0, 0}, - {"SHIP", EmitSingleByte, 0x98, 0, 0}, - {"NBSP", EmitSingleByte, 0xA0, 0, C_DONTCOUNT}, - {"CENT", EmitSingleByte, '', 0, C_DONTCOUNT}, - {"POUNDSIGN", EmitSingleByte, '', 0, C_DONTCOUNT}, - {"EURO", EmitSingleByte, '', 0, C_DONTCOUNT}, - {"YENSIGN", EmitSingleByte, '', 0, C_DONTCOUNT}, - {"COPYRIGHT", EmitSingleByte, '', 0, C_DONTCOUNT}, - {"DOWNARROW", EmitSingleByte, 0xAA, 0, C_DONTCOUNT}, - {"CHECKMARK", EmitSingleByte, 0xAC, 0, C_DONTCOUNT}, - {"CROSS", EmitSingleByte, 0xAD, 0, C_DONTCOUNT}, - {"REGISTERED", EmitSingleByte, '', 0, C_DONTCOUNT}, - {"RIGHTARROW", EmitSingleByte, 0xAF, 0, C_DONTCOUNT}, + {"", EmitSingleChar, '\n', 0, C_DONTCOUNT}, + {"{", EmitSingleChar, '{', 0, C_DONTCOUNT}, + {"UPARROW", EmitSingleChar, SCC_UPARROW, 0, 0}, + {"SMALLUPARROW", EmitSingleChar, SCC_SMALLUPARROW, 0, 0}, + {"SMALLDOWNARROW", EmitSingleChar, SCC_SMALLDOWNARROW, 0, 0}, + {"TRAIN", EmitSingleChar, SCC_TRAIN, 0, 0}, + {"LORRY", EmitSingleChar, SCC_LORRY, 0, 0}, + {"BUS", EmitSingleChar, SCC_BUS, 0, 0}, + {"PLANE", EmitSingleChar, SCC_PLANE, 0, 0}, + {"SHIP", EmitSingleChar, SCC_SHIP, 0, 0}, + {"NBSP", EmitSingleChar, 0xA0, 0, C_DONTCOUNT}, + {"CENT", EmitSingleChar, 0xA2, 0, C_DONTCOUNT}, + {"POUNDSIGN", EmitSingleChar, 0xA3, 0, C_DONTCOUNT}, + {"EURO", EmitSingleChar, 0x20AC, 0, C_DONTCOUNT}, + {"YENSIGN", EmitSingleChar, 0xA5, 0, C_DONTCOUNT}, + {"COPYRIGHT", EmitSingleChar, 0xA9, 0, C_DONTCOUNT}, + {"DOWNARROW", EmitSingleChar, SCC_DOWNARROW, 0, C_DONTCOUNT}, + {"CHECKMARK", EmitSingleChar, SCC_CHECKMARK, 0, C_DONTCOUNT}, + {"CROSS", EmitSingleChar, SCC_CROSS, 0, C_DONTCOUNT}, + {"REGISTERED", EmitSingleChar, 0xAE, 0, C_DONTCOUNT}, + {"RIGHTARROW", EmitSingleChar, SCC_RIGHTARROW, 0, C_DONTCOUNT}, + {"SMALLLEFTARROW", EmitSingleChar, SCC_LESSTHAN, 0, C_DONTCOUNT}, + {"SMALLRIGHTARROW",EmitSingleChar, SCC_GREATERTHAN, 0, C_DONTCOUNT}, }; @@ -1028,7 +1042,7 @@ static 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> // 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 f6f10de136..865aa1ff9f 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 #include // 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 f86854a88b..c795b3336d 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 ef6bd90c81..a38febca52 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++; - } - break; + case SCC_SETX: // {SETX} + if (buff + Utf8CharLen(SCC_SETX) + 1 < last) { + buff += Utf8Encode(buff, SCC_SETX); + *buff++ = *str++; + } + break; - case 0x81: // {STRINL} - buff = GetStringWithArgs(buff, ReadLE16Unaligned(str), argv, last); - str += 2; - break; - case 0x82: // {DATE_LONG} - buff = FormatYmdString(buff, GetInt32(&argv), last); - break; - case 0x83: // {DATE_SHORT} - buff = FormatMonthAndYear(buff, GetInt32(&argv), last); - break; - case 0x84: {// {VELOCITY} - int32 args[1]; - assert(_opt_ptr->units < lengthof(units)); - args[0] = GetInt32(&argv) * units[_opt_ptr->units].s_m >> units[_opt_ptr->units].s_s; - buff = FormatString(buff, GetStringPtr(units[_opt_ptr->units].velocity), args, modifier >> 24, last); - modifier = 0; - break; - } - // 0x85 is used as escape character.. - case 0x85: - switch (*str++) { - case 0: /* {CURRCOMPACT} */ + case SCC_SETXY: // {SETXY} + if (buff + Utf8CharLen(SCC_SETXY) + 2 < last) { + buff += Utf8Encode(buff, SCC_SETXY); + *buff++ = *str++; + *buff++ = *str++; + } + break; + + case SCC_STRING_ID: // {STRINL} + buff = GetStringWithArgs(buff, Utf8Consume(&str), argv, last); + break; + + case SCC_DATE_LONG: // {DATE_LONG} + buff = FormatYmdString(buff, GetInt32(&argv), last); + break; + + case SCC_DATE_SHORT: // {DATE_SHORT} + buff = FormatMonthAndYear(buff, GetInt32(&argv), last); + break; + + case SCC_VELOCITY: {// {VELOCITY} + int32 args[1]; + assert(_opt_ptr->units < lengthof(units)); + args[0] = GetInt32(&argv) * units[_opt_ptr->units].s_m >> units[_opt_ptr->units].s_s; + buff = FormatString(buff, GetStringPtr(units[_opt_ptr->units].velocity), args, modifier >> 24, last); + modifier = 0; + break; + } + + case SCC_CURRENCY_COMPACT: /* {CURRCOMPACT} */ buff = FormatGenericCurrency(buff, _currency, GetInt32(&argv), true, last); break; - case 2: /* {REV} */ + + case SCC_REVISION: /* {REV} */ buff = strecpy(buff, _openttd_revision, last); break; - case 3: { /* {SHORTCARGO} */ + + case SCC_CARGO_SHORT: { /* {SHORTCARGO} */ // Short description of cargotypes. Layout: // 8-bit = cargo type // 16-bit = cargo count @@ -642,40 +654,46 @@ 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"); + 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 SCC_GENDER_INDEX: // {GENDER 0} + str++; + 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; } - break; - case 0x86: // {SKIP} - argv++; - break; + case SCC_COMMA: // {COMMA} + buff = FormatCommaNumber(buff, GetInt32(&argv), last); + break; - // This sets up the gender for the string. - // We just ignore this one. It's used in {G 0 Der Die Das} to determine the case. - case 0x87: // {GENDER 0} - str++; - break; + case SCC_ARG_INDEX: // Move argument pointer + argv = argv_orig + (byte)*str++; + break; - case 0x88: {// {STRING} - uint str = modifier + GetInt32(&argv); - // WARNING. It's prohibited for the included string to consume any arguments. - // For included strings that consume argument, you should use STRING1, STRING2 etc. - // To debug stuff you can set argv to NULL and it will tell you - buff = GetStringWithArgs(buff, str, argv, last); - modifier = 0; - break; - } - - case 0x8B: // {COMMA} - buff = FormatCommaNumber(buff, GetInt32(&argv), last); - break; - - case 0x8C: // Move argument pointer - argv = argv_orig + (byte)*str++; - break; - - case 0x8D: { // {P} - int32 v = argv_orig[(byte)*str++]; // contains the number that determines plural - int len; - str = ParseStringChoice(str, DeterminePluralForm(v), buff, &len); - buff += len; - break; - } - - case 0x8E: // {NUM} - buff = FormatNoCommaNumber(buff, GetInt32(&argv), last); - break; - - case 0x8F: // {CURRENCY} - buff = FormatGenericCurrency(buff, _currency, GetInt32(&argv), false, last); - break; - - case 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_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; } - buff = GetStringWithArgs(buff, str, temp, last); - } break; - case 0x9A: { // {STATION} - const Station* st = GetStation(GetInt32(&argv)); + case SCC_NUM: // {NUM} + buff = FormatNoCommaNumber(buff, GetInt32(&argv), last); + break; - if (!IsValidStation(st)) { // station doesn't exist anymore - buff = GetStringWithArgs(buff, STR_UNKNOWN_DESTINATION, NULL, last); - } else { + case SCC_CURRENCY: // {CURRENCY} + buff = FormatGenericCurrency(buff, _currency, GetInt32(&argv), false, last); + break; + + case SCC_WAYPOINT_NAME: { // {WAYPOINT} int32 temp[2]; - temp[0] = st->town->townnametype; - temp[1] = st->town->townnameparts; - buff = GetStringWithArgs(buff, st->string_id, temp, last); - } - break; - } - case 0x9B: { // {TOWN} - const Town* t = GetTown(GetInt32(&argv)); - int32 temp[1]; - - assert(IsValidTown(t)); - - temp[0] = t->townnameparts; - buff = GetStringWithArgs(buff, t->townnametype, temp, last); - break; - } - - case 0x9C: { // {CURRENCY64} - buff = FormatGenericCurrency(buff, _currency, GetInt64(&argv), false, last); - break; - } - - case 0x9D: { // {SETCASE} - // This is a pseudo command, it's outputted when someone does {STRING.ack} - // The modifier is added to all subsequent GetStringWithArgs that accept the modifier. - modifier = (byte)*str++ << 24; - break; - } - - case 0x9E: { // {Used to implement case switching} - // <0x9E> - // 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; + Waypoint *wp = GetWaypoint(GetInt32(&argv)); + StringID str; + if (wp->string != STR_NULL) { + str = wp->string; + } else { + temp[0] = wp->town_index; + temp[1] = wp->town_cn + 1; + str = wp->town_cn == 0 ? STR_WAYPOINTNAME_CITY : STR_WAYPOINTNAME_CITY_SERIAL; } - // Otherwise skip to the next case - str += 3 + (str[1] << 8) + str[2]; - num--; + buff = GetStringWithArgs(buff, str, temp, last); + break; } - break; - } - default: - if (buff != last) *buff++ = b; + case SCC_STATION_NAME: { // {STATION} + const Station* st = GetStation(GetInt32(&argv)); + + if (!IsValidStation(st)) { // station doesn't exist anymore + buff = GetStringWithArgs(buff, STR_UNKNOWN_DESTINATION, NULL, last); + } else { + int32 temp[2]; + temp[0] = st->town->townnametype; + temp[1] = st->town->townnameparts; + buff = GetStringWithArgs(buff, st->string_id, temp, last); + } + break; + } + + case SCC_TOWN_NAME: { // {TOWN} + const Town* t = GetTown(GetInt32(&argv)); + int32 temp[1]; + + assert(IsValidTown(t)); + + temp[0] = t->townnameparts; + buff = GetStringWithArgs(buff, t->townnametype, temp, last); + break; + } + + case SCC_CURRENCY_64: { // {CURRENCY64} + buff = FormatGenericCurrency(buff, _currency, GetInt64(&argv), false, last); + break; + } + + case SCC_SETCASE: { // {SETCASE} + // This is a pseudo command, it's outputted when someone does {STRING.ack} + // The modifier is added to all subsequent GetStringWithArgs that accept the modifier. + modifier = (byte)*str++ << 24; + break; + } + + case SCC_SWITCH_CASE: { // {Used to implement case switching} + // <0x9E> + // Each LEN is printed using 2 bytes in big endian order. + uint num = (byte)*str++; + while (num) { + if ((byte)str[0] == casei) { + // Found the case, adjust str pointer and continue + str += 3; + break; + } + // Otherwise skip to the next case + str += 3 + (str[1] << 8) + str[2]; + num--; + } + break; + } + + default: + if (buff + Utf8CharLen(b) < last) buff += Utf8Encode(buff, b); + break; } } *buff = '\0'; @@ -910,11 +926,12 @@ static char* 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 0aa105545f..dd4fd179d8 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 0000000000..a1ff42a899 --- /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 4a591de393..40497e0179 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 0000000000..2bbd6624c8 --- /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 97d287bea3..a5fc71c106 100644 --- a/unix.c +++ b/unix.c @@ -291,7 +291,7 @@ void CSleep(int milliseconds) #include #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 d1f213a626..2ea03c7407 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 1894e7a24b..7511df5977 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 4070f8546c..b8e34c4d5d 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; + + WChar c; + uint16 width, length; + + if (IsClipboardFormatAvailable(CF_UNICODETEXT)) { + int bytec; OpenClipboard(NULL); - cbuf = GetClipboardData(CF_TEXT); - data = GlobalLock(cbuf); // clipboard data - dataptr = data; - - for (; IsValidAsciiChar(*dataptr, CS_ALPHANUMERAL) && (tb->length + length) < (tb->maxlength - 1) && - (tb->maxwidth == 0 || width + tb->width + GetCharacterWidth(FS_NORMAL, (byte)*dataptr) <= tb->maxwidth); dataptr++) { - width += GetCharacterWidth(FS_NORMAL, (byte)*dataptr); - length++; - } - - if (length == 0) return false; - - memmove(tb->buf + tb->caretpos + length, tb->buf + tb->caretpos, tb->length - tb->caretpos); - memcpy(tb->buf + tb->caretpos, data, length); - tb->width += width; - tb->caretxoffs += width; - - tb->length += length; - tb->caretpos += length; - tb->buf[tb->length] = '\0'; // terminating zero + cbuf = GetClipboardData(CF_UNICODETEXT); + ptr = GlobalLock(cbuf); + bytec = WideCharToMultiByte(CP_UTF8, 0, (wchar_t*)ptr, -1, utf8_buf, lengthof(utf8_buf), NULL, NULL); GlobalUnlock(cbuf); CloseClipboard(); - return true; + + 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(); + } 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 939ca49ba9..5f20555fff 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 a7927c51aa..03f3589bd8 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;