(svn r7182) -Feature: Merge utf8 branch. This brings us support for Unicode/UTF-8 and the option for fonts rendered by FreeType. Language changes to come.

This commit is contained in:
peter1138 2006-11-16 22:05:33 +00:00
parent 40d647ddde
commit 1a4f1c8177
43 changed files with 1778 additions and 867 deletions

View File

@ -235,6 +235,12 @@ $(error WITH_PNG can't be used when LIBPNG_CONFIG is not set. Edit Makefile.conf
endif endif
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 # Compiler configuration
@ -493,6 +499,15 @@ ifndef MINGW
LIBS += -lc LIBS += -lc
endif 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 # iconv is enabled defaultly on OSX >= 10.3
ifdef OSX ifdef OSX
ifndef JAGUAR ifndef JAGUAR
@ -670,6 +685,7 @@ SRCS += engine.c
SRCS += engine_gui.c SRCS += engine_gui.c
SRCS += fileio.c SRCS += fileio.c
SRCS += fios.c SRCS += fios.c
SRCS += fontcache.c
SRCS += genworld.c SRCS += genworld.c
SRCS += genworld_gui.c SRCS += genworld_gui.c
SRCS += gfx.c SRCS += gfx.c
@ -884,7 +900,7 @@ $(TTD): $(OBJS) $(MAKE_CONFIG)
$(Q)$(CXX_TARGET) $(LDFLAGS) $(TTDLDFLAGS) $(OBJS) $(LIBS) -o $@ $(Q)$(CXX_TARGET) $(LDFLAGS) $(TTDLDFLAGS) $(OBJS) $(LIBS) -o $@
endif 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 $@' @echo '===> Compiling and Linking $@'
$(Q)$(CC_HOST) $(CFLAGS_HOST) -DSTRGEN strgen/strgen.c string.c -o $@ $(Q)$(CC_HOST) $(CFLAGS_HOST) -DSTRGEN strgen/strgen.c string.c -o $@

14
configure vendored
View File

@ -35,11 +35,13 @@ function showhelp() {
echo " iconv Do you want iconv-support? [no]" echo " iconv Do you want iconv-support? [no]"
echo " network Do you want network-support? [yes]" echo " network Do you want network-support? [yes]"
echo " cocoa Do you want cocoa-support? (MacOSX) [no]" echo " cocoa Do you want cocoa-support? (MacOSX) [no]"
echo " freetype Do you want freetype-support? [yes]"
echo "" echo ""
echo "Params used to configure external libs:" echo "Params used to configure external libs:"
echo " --static-zlib-path Set the path to your static zlib []" echo " --static-zlib-path Set the path to your static zlib []"
echo " --sdl-config Where is your sdl-config [sdl-config]" echo " --sdl-config Where is your sdl-config [sdl-config]"
echo " --libpng-config Where is your libpng-config [libpng-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 " --with-iconv Set the path to your iconv headers []"
echo " " echo " "
} }
@ -181,6 +183,12 @@ do
--without-cocoa) --without-cocoa)
PARAM="$PARAM WITH_COCOA=" PARAM="$PARAM WITH_COCOA="
;; ;;
--with-freetype)
PARAM="$PARAM WITH_FREETYPE=1"
;;
--without-freetype)
PARAM="$PARAM WITH_FREETYPE="
;;
--static-zlib-path=*) --static-zlib-path=*)
handle STATIC_ZLIB_PATH "$n" handle STATIC_ZLIB_PATH "$n"
;; ;;
@ -199,6 +207,12 @@ do
--libpng-config) --libpng-config)
ITEM="LIBPNG_CONFIG" ITEM="LIBPNG_CONFIG"
;; ;;
--freetype-config=*)
handle FREETYPE_CONFIG "$n"
;;
--freetype-config)
ITEM="FREETYPE_CONFIG"
;;
--*=*) --*=*)
echo -n "Unknown switch " echo -n "Unknown switch "

View File

@ -181,9 +181,9 @@ static void IConsoleWndProc(Window *w, WindowEvent *e)
} }
break; break;
default: default:
if (IsValidAsciiChar(e->we.keypress.ascii, CS_ALPHANUMERAL)) { if (IsValidChar(e->we.keypress.key, CS_ALPHANUMERAL)) {
_iconsole_scroll = ICON_BUFFER; _iconsole_scroll = ICON_BUFFER;
InsertTextBufferChar(&_iconsole_cmdline, e->we.keypress.ascii); InsertTextBufferChar(&_iconsole_cmdline, e->we.keypress.key);
IConsoleResetHistoryPos(); IConsoleResetHistoryPos();
SetWindowDirty(w); SetWindowDirty(w);
} else { } else {
@ -1057,7 +1057,7 @@ void IConsoleCmdExec(const char *cmdstr)
if (cmdstr[0] == '#') return; // comments if (cmdstr[0] == '#') return; // comments
for (cmdptr = cmdstr; *cmdptr != '\0'; cmdptr++) { for (cmdptr = cmdstr; *cmdptr != '\0'; cmdptr++) {
if (!IsValidAsciiChar(*cmdptr, CS_ALPHANUMERAL)) { if (!IsValidChar(*cmdptr, CS_ALPHANUMERAL)) {
IConsoleError("command contains malformed characters, aborting"); IConsoleError("command contains malformed characters, aborting");
IConsolePrintF(_icolour_err, "ERROR: command was: '%s'", cmdstr); IConsolePrintF(_icolour_err, "ERROR: command was: '%s'", cmdstr);
return; return;

View File

@ -13,14 +13,14 @@
// | | Euro year | | | name // | | Euro year | | | name
// | | | | | | | // | | | | | | |
static const CurrencySpec origin_currency_specs[NUM_CURRENCY] = { 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_NOEURO, "$", "", 0, STR_CURR_USD }, // us dollars
{ 2, ',', CF_ISEURO, "¤", "", 0, STR_CURR_EUR }, // Euro { 2, ',', CF_ISEURO, "", "", 0, STR_CURR_EUR }, // Euro
{ 220, ',', CF_NOEURO, "\xA5", "", 0, STR_CURR_YEN }, // yen { 220, ',', CF_NOEURO, "¥", "", 0, STR_CURR_YEN }, // yen
{ 20, ',', 2002, "", " S.", 1, STR_CURR_ATS }, // austrian schilling { 20, ',', 2002, "", " S.", 1, STR_CURR_ATS }, // austrian schilling
{ 59, ',', 2002, "BEF ", "", 0, STR_CURR_BEF }, // belgian franc { 59, ',', 2002, "BEF ", "", 0, STR_CURR_BEF }, // belgian franc
{ 2, ',', CF_NOEURO, "CHF ", "", 0, STR_CURR_CHF }, // swiss 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 { 3, '.', 2002, "DM ", "", 0, STR_CURR_DEM }, // deutsche mark
{ 11, '.', CF_NOEURO, "", " kr", 1, STR_CURR_DKK }, // danish krone { 11, '.', CF_NOEURO, "", " kr", 1, STR_CURR_DKK }, // danish krone
{ 245, '.', 2002, "Pts ", "", 0, STR_CURR_ESP }, // spanish pesetas { 245, '.', 2002, "Pts ", "", 0, STR_CURR_ESP }, // spanish pesetas

View File

@ -21,6 +21,7 @@ int _debug_oldloader_level;
int _debug_ntp_level; int _debug_ntp_level;
int _debug_npf_level; int _debug_npf_level;
int _debug_yapf_level; int _debug_yapf_level;
int _debug_freetype_level;
void CDECL debug(const char *s, ...) void CDECL debug(const char *s, ...)
@ -53,7 +54,8 @@ typedef struct DebugLevel {
DEBUG_LEVEL(oldloader), DEBUG_LEVEL(oldloader),
DEBUG_LEVEL(ntp), DEBUG_LEVEL(ntp),
DEBUG_LEVEL(npf), DEBUG_LEVEL(npf),
DEBUG_LEVEL(yapf) DEBUG_LEVEL(yapf),
DEBUG_LEVEL(freetype)
}; };
#undef DEBUG_LEVEL #undef DEBUG_LEVEL

View File

@ -20,6 +20,7 @@
extern int _debug_ntp_level; extern int _debug_ntp_level;
extern int _debug_npf_level; extern int _debug_npf_level;
extern int _debug_yapf_level; extern int _debug_yapf_level;
extern int _debug_freetype_level;
#endif #endif
void CDECL debug(const char *s, ...); void CDECL debug(const char *s, ...);

310
fontcache.c Normal file
View File

@ -0,0 +1,310 @@
/* $Id$ */
#include "stdafx.h"
#include "openttd.h"
#include "functions.h"
#include "macros.h"
#include "debug.h"
#include "table/sprites.h"
#include "table/control_codes.h"
#include "spritecache.h"
#include "gfx.h"
#include "string.h"
#include "fontcache.h"
#ifdef WITH_FREETYPE
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_GLYPH_H
static FT_Library _library = NULL;
static FT_Face _face_small = NULL;
static FT_Face _face_medium = NULL;
static FT_Face _face_large = NULL;
FreeTypeSettings _freetype;
enum {
FACE_COLOUR = 1,
SHADOW_COLOUR = 2,
};
static void LoadFreeTypeFont(const char *font_name, FT_Face *face, const char *type)
{
FT_Error error;
if (strlen(font_name) == 0) return;
error = FT_New_Face(_library, font_name, 0, face);
if (error == FT_Err_Ok) {
/* Attempt to select the unicode character map */
error = FT_Select_Charmap(*face, ft_encoding_unicode);
if (error == FT_Err_Ok) {
/* Success */
return;
} else if (error == FT_Err_Invalid_CharMap_Handle) {
/* Try to pick a different character map instead. We default to
* the first map, but platform_id 0 encoding_id 0 should also
* be unicode (strange system...) */
FT_CharMap found = (*face)->charmaps[0];
int i;
for (i = 0; i < (*face)->num_charmaps; i++) {
FT_CharMap charmap = (*face)->charmaps[i];
if (charmap->platform_id == 0 && charmap->encoding_id == 0) {
found = charmap;
}
}
if (found != NULL) {
error = FT_Set_Charmap(*face, found);
if (error == FT_Err_Ok) return;
}
}
FT_Done_Face(*face);
*face = NULL;
}
ShowInfoF("Unable to use '%s' for %s font, FreeType reported error 0x%X, using sprite font instead", font_name, type, error);
}
void InitFreeType(void)
{
if (strlen(_freetype.small_font) == 0 && strlen(_freetype.medium_font) == 0 && strlen(_freetype.large_font) == 0) {
DEBUG(freetype, 1) ("[FreeType] No font faces specified, using sprite fonts instead");
return;
}
if (FT_Init_FreeType(&_library) != FT_Err_Ok) {
ShowInfoF("Unable to initialize FreeType, using sprite fonts instead");
return;
}
DEBUG(freetype, 2) ("[FreeType] Initialized");
/* Load each font */
LoadFreeTypeFont(_freetype.small_font, &_face_small, "small");
LoadFreeTypeFont(_freetype.medium_font, &_face_medium, "medium");
LoadFreeTypeFont(_freetype.large_font, &_face_large, "large");
/* Set each font size */
if (_face_small != NULL) FT_Set_Pixel_Sizes(_face_small, 0, _freetype.small_size);
if (_face_medium != NULL) FT_Set_Pixel_Sizes(_face_medium, 0, _freetype.medium_size);
if (_face_large != NULL) FT_Set_Pixel_Sizes(_face_large, 0, _freetype.large_size);
}
static FT_Face GetFontFace(FontSize size)
{
switch (size) {
default: NOT_REACHED();
case FS_NORMAL: return _face_medium;
case FS_SMALL: return _face_small;
case FS_LARGE: return _face_large;
}
}
typedef struct GlyphEntry {
Sprite *sprite;
byte width;
} GlyphEntry;
/* The glyph cache. This is structured to reduce memory consumption.
* 1) There is a 'segment' table for each font size.
* 2) Each segment table is a discrete block of characters.
* 3) Each block contains 256 (aligned) characters sequential characters.
*
* The cache is accessed in the following way:
* For character 0x0041 ('A'): _glyph_ptr[FS_NORMAL][0x00][0x41]
* For character 0x20AC (Euro): _glyph_ptr[FS_NORMAL][0x20][0xAC]
*
* Currently only 256 segments are allocated, "limiting" us to 65536 characters.
* This can be simply changed in the two functions Get & SetGlyphPtr.
*/
static GlyphEntry **_glyph_ptr[FS_END];
static GlyphEntry *GetGlyphPtr(FontSize size, WChar key)
{
if (_glyph_ptr[size] == NULL) return NULL;
if (_glyph_ptr[size][GB(key, 8, 8)] == NULL) return NULL;
return &_glyph_ptr[size][GB(key, 8, 8)][GB(key, 0, 8)];
}
static void SetGlyphPtr(FontSize size, WChar key, const GlyphEntry *glyph)
{
if (_glyph_ptr[size] == NULL) {
DEBUG(freetype, 3) ("[FreeType] Allocating root glyph cache for size %u", size);
_glyph_ptr[size] = calloc(256, sizeof(**_glyph_ptr));
}
if (_glyph_ptr[size][GB(key, 8, 8)] == NULL) {
DEBUG(freetype, 3) ("[FreeType] Allocating glyph cache for range 0x%02X00, size %u", GB(key, 8, 8), size);
_glyph_ptr[size][GB(key, 8, 8)] = calloc(256, sizeof(***_glyph_ptr));
}
DEBUG(freetype, 4) ("[FreeType] Set glyph for unicode character 0x%04X, size %u", key, size);
_glyph_ptr[size][GB(key, 8, 8)][GB(key, 0, 8)].sprite = glyph->sprite;
_glyph_ptr[size][GB(key, 8, 8)][GB(key, 0, 8)].width = glyph->width;
}
const Sprite *GetGlyph(FontSize size, WChar key)
{
FT_Face face = GetFontFace(size);
FT_GlyphSlot slot;
GlyphEntry new_glyph;
GlyphEntry *glyph;
Sprite *sprite;
int width;
int height;
int x;
int y;
int y_adj;
assert(IsPrintable(key));
/* Bail out if no face loaded, or for our special characters */
if (face == NULL || (key >= SCC_SPRITE_START && key <= SCC_SPRITE_END)) {
SpriteID sprite = GetUnicodeGlyph(size, key);
if (sprite == 0) sprite = GetUnicodeGlyph(size, '?');
return GetSprite(sprite);
}
/* Check for the glyph in our cache */
glyph = GetGlyphPtr(size, key);
if (glyph != NULL && glyph->sprite != NULL) return glyph->sprite;
slot = face->glyph;
FT_Load_Char(face, key, FT_LOAD_DEFAULT);
FT_Render_Glyph(face->glyph, FT_RENDER_MODE_MONO);
/* Add 1 pixel for the shadow on the medium font. Our sprite must be at least 1x1 pixel */
width = max(1, slot->bitmap.width + (size == FS_NORMAL));
height = max(1, slot->bitmap.rows + (size == FS_NORMAL));
/* FreeType has rendered the glyph, now we allocate a sprite and copy the image into it */
sprite = calloc(width * height + 8, 1);
sprite->info = 1;
sprite->width = width;
sprite->height = height;
sprite->x_offs = slot->bitmap_left;
// XXX 2 should be determined somehow... it's right for the normal face
y_adj = (size == FS_NORMAL) ? 2 : 0;
sprite->y_offs = GetCharacterHeight(size) - slot->bitmap_top - y_adj;
/* Draw shadow for medium size */
if (size == FS_NORMAL) {
for (y = 0; y < slot->bitmap.rows; y++) {
for (x = 0; x < slot->bitmap.width; x++) {
if (HASBIT(slot->bitmap.buffer[(x / 8) + y * slot->bitmap.pitch], 7 - (x % 8))) {
sprite->data[1 + x + (1 + y) * sprite->width] = SHADOW_COLOUR;
}
}
}
}
for (y = 0; y < slot->bitmap.rows; y++) {
for (x = 0; x < slot->bitmap.width; x++) {
if (HASBIT(slot->bitmap.buffer[(x / 8) + y * slot->bitmap.pitch], 7 - (x % 8))) {
sprite->data[x + y * sprite->width] = FACE_COLOUR;
}
}
}
new_glyph.sprite = sprite;
new_glyph.width = (slot->advance.x >> 6) + (size != FS_NORMAL);
SetGlyphPtr(size, key, &new_glyph);
return sprite;
}
uint GetGlyphWidth(FontSize size, WChar key)
{
FT_Face face = GetFontFace(size);
GlyphEntry *glyph;
if (face == NULL || (key >= SCC_SPRITE_START && key <= SCC_SPRITE_END)) {
SpriteID sprite = GetUnicodeGlyph(size, key);
if (sprite == 0) sprite = GetUnicodeGlyph(size, '?');
return SpriteExists(sprite) ? GetSprite(sprite)->width + (size != FS_NORMAL) : 0;
}
glyph = GetGlyphPtr(size, key);
if (glyph == NULL || glyph->sprite == NULL) {
GetGlyph(size, key);
glyph = GetGlyphPtr(size, key);
}
return glyph->width;
}
#endif /* WITH_FREETYPE */
/* Sprite based glyph mapping */
#include "table/unicode.h"
static SpriteID **_unicode_glyph_map[FS_END];
/** Get the SpriteID of the first glyph for the given font size */
static SpriteID GetFontBase(FontSize size)
{
switch (size) {
default: NOT_REACHED();
case FS_NORMAL: return SPR_ASCII_SPACE;
case FS_SMALL: return SPR_ASCII_SPACE_SMALL;
case FS_LARGE: return SPR_ASCII_SPACE_BIG;
}
}
SpriteID GetUnicodeGlyph(FontSize size, uint32 key)
{
if (_unicode_glyph_map[size][GB(key, 8, 8)] == NULL) return 0;
return _unicode_glyph_map[size][GB(key, 8, 8)][GB(key, 0, 8)];
}
void SetUnicodeGlyph(FontSize size, uint32 key, SpriteID sprite)
{
if (_unicode_glyph_map[size] == NULL) _unicode_glyph_map[size] = calloc(256, sizeof(*_unicode_glyph_map[size]));
if (_unicode_glyph_map[size][GB(key, 8, 8)] == NULL) _unicode_glyph_map[size][GB(key, 8, 8)] = calloc(256, sizeof(**_unicode_glyph_map[size]));
_unicode_glyph_map[size][GB(key, 8, 8)][GB(key, 0, 8)] = sprite;
}
void InitializeUnicodeGlyphMap(void)
{
FontSize size;
SpriteID base;
SpriteID sprite;
uint i;
for (size = FS_NORMAL; size != FS_END; size++) {
base = GetFontBase(size);
for (i = ASCII_LETTERSTART; i < 256; i++) {
sprite = base + i - ASCII_LETTERSTART;
if (!SpriteExists(sprite)) continue;
SetUnicodeGlyph(size, i, sprite);
SetUnicodeGlyph(size, i + SCC_SPRITE_START, sprite);
}
for (i = 0; i < lengthof(_default_unicode_map); i++) {
sprite = base + _default_unicode_map[i].key - ASCII_LETTERSTART;
SetUnicodeGlyph(size, _default_unicode_map[i].code, sprite);
}
}
}

56
fontcache.h Normal file
View File

@ -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 */

View File

@ -147,6 +147,7 @@ char *GetName(char *buff, StringID id, const char* last);
#define AllocateNameUnique(name, skip) RealAllocateName(name, skip, true) #define AllocateNameUnique(name, skip) RealAllocateName(name, skip, true)
#define AllocateName(name, skip) RealAllocateName(name, skip, false) #define AllocateName(name, skip) RealAllocateName(name, skip, false)
StringID RealAllocateName(const char *name, byte skip, bool check_double); StringID RealAllocateName(const char *name, byte skip, bool check_double);
void ConvertNameArray(void);
/* misc functions */ /* misc functions */
void MarkTileDirty(int x, int y); void MarkTileDirty(int x, int y);

145
gfx.c
View File

@ -12,6 +12,8 @@
#include "table/sprites.h" #include "table/sprites.h"
#include "hal.h" #include "hal.h"
#include "variables.h" #include "variables.h"
#include "table/control_codes.h"
#include "fontcache.h"
#include "genworld.h" #include "genworld.h"
#ifdef _DEBUG #ifdef _DEBUG
@ -244,40 +246,6 @@ void GfxDrawLine(int x, int y, int x2, int y2, int color)
} }
static inline SpriteID GetFontBase(FontSize size)
{
switch (size) {
default: NOT_REACHED();
case FS_NORMAL: return SPR_ASCII_SPACE;
case FS_SMALL: return SPR_ASCII_SPACE_SMALL;
case FS_LARGE: return SPR_ASCII_SPACE_BIG;
}
}
// ASSIGNMENT OF ASCII LETTERS < 32
// 0 - end of string
// 1 - SETX <BYTE>
// 2 - SETXY <BYTE> <BYTE>
// 3-7 -
// 8 - TINYFONT
// 9 - BIGFONT
// 10 - newline
// 11-14 -
// 15-31 - 17 colors
enum {
ASCII_SETX = 1,
ASCII_SETXY = 2,
ASCII_TINYFONT = 8,
ASCII_BIGFONT = 9,
ASCII_NL = 10,
ASCII_COLORSTART = 15,
};
/** Truncate a given string to a maximum width if neccessary. /** Truncate a given string to a maximum width if neccessary.
* If the string is truncated, add three dots ('...') to show this. * If the string is truncated, add three dots ('...') to show this.
* @param *dest string that is checked and possibly truncated * @param *dest string that is checked and possibly truncated
@ -289,13 +257,13 @@ static int TruncateString(char *str, int maxw)
FontSize size = _cur_fontsize; FontSize size = _cur_fontsize;
int ddd, ddd_w; int ddd, ddd_w;
byte c; WChar c;
char *ddd_pos; char *ddd_pos;
ddd_w = ddd = GetCharacterWidth(size, '.') * 3; ddd_w = ddd = GetCharacterWidth(size, '.') * 3;
for (ddd_pos = str; (c = *str++) != '\0'; ) { for (ddd_pos = str; (c = Utf8Consume((const char **)&str)) != '\0'; ) {
if (c >= ASCII_LETTERSTART) { if (IsPrintable(c)) {
w += GetCharacterWidth(size, c); w += GetCharacterWidth(size, c);
if (w >= maxw) { if (w >= maxw) {
@ -305,12 +273,12 @@ static int TruncateString(char *str, int maxw)
return ddd_w; return ddd_w;
} }
} else { } else {
if (c == ASCII_SETX) str++; if (c == SCC_SETX) str++;
else if (c == ASCII_SETXY) str += 2; else if (c == SCC_SETXY) str += 2;
else if (c == ASCII_TINYFONT) { else if (c == SCC_TINYFONT) {
size = FS_SMALL; size = FS_SMALL;
ddd = GetCharacterWidth(size, '.') * 3; ddd = GetCharacterWidth(size, '.') * 3;
} else if (c == ASCII_BIGFONT) { } else if (c == SCC_BIGFONT) {
size = FS_LARGE; size = FS_LARGE;
ddd = GetCharacterWidth(size, '.') * 3; ddd = GetCharacterWidth(size, '.') * 3;
} }
@ -443,11 +411,11 @@ uint32 FormatStringLinebreaks(char *str, int maxw)
int w = 0; int w = 0;
for (;;) { for (;;) {
byte c = *str++; WChar c = Utf8Consume((const char **)&str);
/* whitespace is where we will insert the line-break */ /* 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); w += GetCharacterWidth(size, c);
/* string is longer than maximum width so we need to decide what to /* string is longer than maximum width so we need to decide what to
* do. We can do two things: * do. We can do two things:
@ -465,11 +433,11 @@ uint32 FormatStringLinebreaks(char *str, int maxw)
} else { } else {
switch (c) { switch (c) {
case '\0': return num + (size << 16); break; case '\0': return num + (size << 16); break;
case ASCII_SETX: str++; break; case SCC_SETX: str++; break;
case ASCII_SETXY: str +=2; break; case SCC_SETXY: str +=2; break;
case ASCII_TINYFONT: size = FS_SMALL; break; case SCC_TINYFONT: size = FS_SMALL; break;
case ASCII_BIGFONT: size = FS_LARGE; break; case SCC_BIGFONT: size = FS_LARGE; break;
case ASCII_NL: goto end_of_inner_loop; case '\n': goto end_of_inner_loop;
} }
} }
} }
@ -486,7 +454,7 @@ void DrawStringMultiCenter(int x, int y, StringID str, int maxw)
uint32 tmp; uint32 tmp;
int num, w, mt; int num, w, mt;
const char *src; const char *src;
byte c; WChar c;
GetString(buffer, str, lastof(buffer)); GetString(buffer, str, lastof(buffer));
@ -505,7 +473,7 @@ void DrawStringMultiCenter(int x, int y, StringID str, int maxw)
_cur_fontsize = _last_fontsize; _cur_fontsize = _last_fontsize;
for (;;) { for (;;) {
c = *src++; c = Utf8Consume(&src);
if (c == 0) { if (c == 0) {
y += mt; y += mt;
if (--num < 0) { if (--num < 0) {
@ -513,9 +481,9 @@ void DrawStringMultiCenter(int x, int y, StringID str, int maxw)
return; return;
} }
break; break;
} else if (c == ASCII_SETX) { } else if (c == SCC_SETX) {
src++; src++;
} else if (c == ASCII_SETXY) { } else if (c == SCC_SETXY) {
src+=2; src+=2;
} }
} }
@ -530,7 +498,7 @@ uint DrawStringMultiLine(int x, int y, StringID str, int maxw)
int num, mt; int num, mt;
uint total_height; uint total_height;
const char *src; const char *src;
byte c; WChar c;
GetString(buffer, str, lastof(buffer)); GetString(buffer, str, lastof(buffer));
@ -547,7 +515,7 @@ uint DrawStringMultiLine(int x, int y, StringID str, int maxw)
_cur_fontsize = _last_fontsize; _cur_fontsize = _last_fontsize;
for (;;) { for (;;) {
c = *src++; c = Utf8Consume(&src);
if (c == 0) { if (c == 0) {
y += mt; y += mt;
if (--num < 0) { if (--num < 0) {
@ -555,9 +523,9 @@ uint DrawStringMultiLine(int x, int y, StringID str, int maxw)
return total_height; return total_height;
} }
break; break;
} else if (c == ASCII_SETX) { } else if (c == SCC_SETX) {
src++; src++;
} else if (c == ASCII_SETXY) { } else if (c == SCC_SETXY) {
src+=2; src+=2;
} }
} }
@ -576,22 +544,24 @@ BoundingRect GetStringBoundingBox(const char *str)
FontSize size = _cur_fontsize; FontSize size = _cur_fontsize;
BoundingRect br; BoundingRect br;
int max_width; int max_width;
byte c; WChar c;
br.width = br.height = max_width = 0; br.width = br.height = max_width = 0;
for (c = *str; c != '\0'; c = *(++str)) { for (;;) {
if (c >= ASCII_LETTERSTART) { c = Utf8Consume(&str);
if (c == 0) break;
if (IsPrintable(c)) {
br.width += GetCharacterWidth(size, c); br.width += GetCharacterWidth(size, c);
} else { } else {
switch (c) { switch (c) {
case ASCII_SETX: br.width += (byte)*++str; break; case SCC_SETX: br.width += (byte)*++str; break;
case ASCII_SETXY: case SCC_SETXY:
br.width += (byte)*++str; br.width += (byte)*++str;
br.height += (byte)*++str; br.height += (byte)*++str;
break; break;
case ASCII_TINYFONT: size = FS_SMALL; break; case SCC_TINYFONT: size = FS_SMALL; break;
case ASCII_BIGFONT: size = FS_LARGE; break; case SCC_BIGFONT: size = FS_LARGE; break;
case ASCII_NL: case '\n':
br.height += GetCharacterHeight(size); br.height += GetCharacterHeight(size);
if (br.width > max_width) max_width = br.width; if (br.width > max_width) max_width = br.width;
br.width = 0; br.width = 0;
@ -617,7 +587,7 @@ int DoDrawString(const char *string, int x, int y, uint16 real_color)
{ {
DrawPixelInfo *dpi = _cur_dpi; DrawPixelInfo *dpi = _cur_dpi;
FontSize size = _cur_fontsize; FontSize size = _cur_fontsize;
byte c; WChar c;
byte color; byte color;
int xo = x, yo = y; int xo = x, yo = y;
@ -647,39 +617,39 @@ check_bounds:
if (y + 19 <= dpi->top || dpi->top + dpi->height <= y) { if (y + 19 <= dpi->top || dpi->top + dpi->height <= y) {
skip_char:; skip_char:;
for (;;) { for (;;) {
c = *string++; c = Utf8Consume(&string);
if (c < ASCII_LETTERSTART) goto skip_cont; if (!IsPrintable(c)) goto skip_cont;
} }
} }
for (;;) { for (;;) {
c = *string++; c = Utf8Consume(&string);
skip_cont:; skip_cont:;
if (c == 0) { if (c == 0) {
_last_fontsize = size; _last_fontsize = size;
return x; return x;
} }
if (c >= ASCII_LETTERSTART) { if (IsPrintable(c)) {
if (x >= dpi->left + dpi->width) goto skip_char; if (x >= dpi->left + dpi->width) goto skip_char;
if (x + 26 >= dpi->left) { 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); x += GetCharacterWidth(size, c);
} else if (c == ASCII_NL) { // newline = {} } else if (c == '\n') { // newline = {}
x = xo; x = xo;
y += GetCharacterHeight(size); y += GetCharacterHeight(size);
goto check_bounds; goto check_bounds;
} else if (c >= ASCII_COLORSTART) { // change color? } else if (c >= SCC_BLUE && c <= SCC_BLACK) { // change color?
color = (byte)(c - ASCII_COLORSTART); color = (byte)(c - SCC_BLUE);
goto switch_color; goto switch_color;
} else if (c == ASCII_SETX) { // {SETX} } else if (c == SCC_SETX) { // {SETX}
x = xo + (byte)*string++; x = xo + (byte)*string++;
} else if (c == ASCII_SETXY) {// {SETXY} } else if (c == SCC_SETXY) {// {SETXY}
x = xo + (byte)*string++; x = xo + (byte)*string++;
y = yo + (byte)*string++; y = yo + (byte)*string++;
} else if (c == ASCII_TINYFONT) { // {TINYFONT} } else if (c == SCC_TINYFONT) { // {TINYFONT}
size = FS_SMALL; size = FS_SMALL;
} else if (c == ASCII_BIGFONT) { // {BIGFONT} } else if (c == SCC_BIGFONT) { // {BIGFONT}
size = FS_LARGE; size = FS_LARGE;
} else { } else {
printf("Unknown string command character %d\n", c); printf("Unknown string command character %d\n", c);
@ -1641,28 +1611,33 @@ void DoPaletteAnimations(void)
void LoadStringWidthTable(void) void LoadStringWidthTable(void)
{ {
SpriteID base;
uint i; uint i;
/* Normal font */ /* Normal font */
base = GetFontBase(FS_NORMAL);
for (i = 0; i != 224; i++) { 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 */ /* Small font */
base = GetFontBase(FS_SMALL);
for (i = 0; i != 224; i++) { 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 */ /* Large font */
base = GetFontBase(FS_LARGE);
for (i = 0; i != 224; i++) { 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) void ScreenSizeChanged(void)
{ {
// check the dirty rect // check the dirty rect

11
gfx.h
View File

@ -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 // XXX doesn't really belong here, but the only
// consumers always use it in conjunction with DoDrawString() // consumers always use it in conjunction with DoDrawString()
#define UPARROW "\x80" #define UPARROW "\xEE\x8A\x80"
#define DOWNARROW "\xAA" #define DOWNARROW "\xEE\x8A\xAA"
int DrawStringCentered(int x, int y, StringID str, uint16 color); int DrawStringCentered(int x, int y, StringID str, uint16 color);
@ -96,13 +96,8 @@ void ToggleFullScreen(bool fs);
/* gfx.c */ /* gfx.c */
#define ASCII_LETTERSTART 32 #define ASCII_LETTERSTART 32
extern FontSize _cur_fontsize; extern FontSize _cur_fontsize;
extern byte _stringwidth_table[FS_END][224];
static inline byte GetCharacterWidth(FontSize size, byte key) byte GetCharacterWidth(FontSize size, uint32 key);
{
assert(key >= ASCII_LETTERSTART);
return _stringwidth_table[size][key - ASCII_LETTERSTART];
}
static inline byte GetCharacterHeight(FontSize size) static inline byte GetCharacterHeight(FontSize size)
{ {

2
gui.h
View File

@ -109,7 +109,7 @@ bool HandleCaret(Textbuf *tb);
void DeleteTextBufferAll(Textbuf *tb); void DeleteTextBufferAll(Textbuf *tb);
bool DeleteTextBufferChar(Textbuf *tb, int delmode); bool DeleteTextBufferChar(Textbuf *tb, int delmode);
bool InsertTextBufferChar(Textbuf *tb, byte key); bool InsertTextBufferChar(Textbuf *tb, uint32 key);
bool InsertTextBufferClipboard(Textbuf *tb); bool InsertTextBufferClipboard(Textbuf *tb);
bool MoveTextBufferPos(Textbuf *tb, int navmode); bool MoveTextBufferPos(Textbuf *tb, int navmode);
void InitializeTextBuffer(Textbuf *tb, const char *buf, uint16 maxlength, uint16 maxwidth); void InitializeTextBuffer(Textbuf *tb, const char *buf, uint16 maxlength, uint16 maxwidth);

View File

@ -2001,8 +2001,8 @@ STR_26816_NONE :None
STR_6816_LOW :Low STR_6816_LOW :Low
STR_6817_NORMAL :Normal STR_6817_NORMAL :Normal
STR_6818_HIGH :High STR_6818_HIGH :High
STR_6819 :{BLACK}< STR_6819 :{BLACK}{SMALLLEFTARROW}
STR_681A :{BLACK}> STR_681A :{BLACK}{SMALLRIGHTARROW}
STR_681B_VERY_SLOW :Very Slow STR_681B_VERY_SLOW :Very Slow
STR_681C_SLOW :Slow STR_681C_SLOW :Slow
STR_681D_MEDIUM :Medium STR_681D_MEDIUM :Medium

View File

@ -29,6 +29,7 @@
#include "variables.h" #include "variables.h"
#include "train.h" #include "train.h"
#include "unmovable_map.h" #include "unmovable_map.h"
#include "string.h"
#include "screenshot.h" #include "screenshot.h"
#include "genworld.h" #include "genworld.h"
#include "settings.h" #include "settings.h"
@ -2085,15 +2086,16 @@ static bool DrawScrollingStatusText(const NewsItem *ni, int pos)
s = buf; s = buf;
d = buffer; d = buffer;
for (;; s++) { for (;;) {
if (*s == '\0') { WChar c = Utf8Consume(&s);
if (c == 0) {
*d = '\0'; *d = '\0';
break; break;
} else if (*s == 0x0D) { } else if (*s == 0x0D) {
d[0] = d[1] = d[2] = d[3] = ' '; d[0] = d[1] = d[2] = d[3] = ' ';
d += 4; d += 4;
} else if ((byte)*s >= ' ' && ((byte)*s < 0x88 || (byte)*s >= 0x99)) { } else if (IsPrintable(c)) {
*d++ = *s; d += Utf8Encode(d, c);
} }
} }

View File

@ -73,6 +73,7 @@ $(MAKE_CONFIG):
$(call CONFIG_LINE,WITH_ICONV_PATH:=$(WITH_ICONV_PATH)) $(call CONFIG_LINE,WITH_ICONV_PATH:=$(WITH_ICONV_PATH))
$(call CONFIG_LINE,STATIC_ZLIB_PATH:=$(STATIC_ZLIB_PATH)) $(call CONFIG_LINE,STATIC_ZLIB_PATH:=$(STATIC_ZLIB_PATH))
$(call CONFIG_LINE,WITH_COCOA:=$(WITH_COCOA)) $(call CONFIG_LINE,WITH_COCOA:=$(WITH_COCOA))
$(call CONFIG_LINE,WITH_FREETYPE:=$(WITH_FREETYPE))
$(call CONFIG_LINE,) $(call CONFIG_LINE,)
$(call CONFIG_LINE,\# OS flags) $(call CONFIG_LINE,\# OS flags)
@ -100,6 +101,7 @@ $(MAKE_CONFIG):
$(call CONFIG_LINE,\# misc) $(call CONFIG_LINE,\# misc)
$(call CONFIG_LINE,SDL_CONFIG:=$(SDL_CONFIG)) $(call CONFIG_LINE,SDL_CONFIG:=$(SDL_CONFIG))
$(call CONFIG_LINE,LIBPNG_CONFIG:=$(LIBPNG_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,BEOS_NET_SERVER:=$(BEOS_NET_SERVER))
$(call CONFIG_LINE,CONFIG_INCLUDED:=yes) $(call CONFIG_LINE,CONFIG_INCLUDED:=yes)
$(call CONFIG_LINE,PATH_SET:=$(PATH_SET)) $(call CONFIG_LINE,PATH_SET:=$(PATH_SET))

View File

@ -66,6 +66,9 @@ SDL_CONFIG:=sdl-config
# set libpng-config to the default value # set libpng-config to the default value
LIBPNG_CONFIG :=libpng-config LIBPNG_CONFIG :=libpng-config
# set freetype-config to the default value
FREETYPE_CONFIG:=freetype-config
# Networking, enabled by default # Networking, enabled by default
WITH_NETWORK:=1 WITH_NETWORK:=1
@ -75,6 +78,9 @@ WITH_SDL:=$(shell $(SDL_CONFIG) --version 2>/dev/null)
# libpng detection # libpng detection
WITH_PNG:=$(shell $(LIBPNG_CONFIG) --version 2>/dev/null) WITH_PNG:=$(shell $(LIBPNG_CONFIG) --version 2>/dev/null)
# Freetype detection
WITH_FREETYPE:=$(shell $(FREETYPE_CONFIG) --ftversion 2>/dev/null)
ifdef WITH_PNG ifdef WITH_PNG
# LibPNG depends on Zlib # LibPNG depends on Zlib
WITH_ZLIB:=1 WITH_ZLIB:=1

32
misc.c
View File

@ -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. // Calculate constants that depend on the landscape type.
void InitializeLandscapeVariables(bool only_constants) void InitializeLandscapeVariables(bool only_constants)
{ {

View File

@ -205,8 +205,8 @@ static const char *credits[] = {
" Bjarni Corfitzen (Bjarni) - MacOSX port, coder", " Bjarni Corfitzen (Bjarni) - MacOSX port, coder",
" Matthijs Kooijman (blathijs) - Pathfinder-god", " Matthijs Kooijman (blathijs) - Pathfinder-god",
" Victor Fischer (Celestar) - Programming everywhere you need him to", " Victor Fischer (Celestar) - Programming everywhere you need him to",
" Tamás Faragó (Darkvater) - Lead coder", " Tamás Faragó (Darkvater) - Lead coder",
" Attila Bán (MiHaMiX) - WebTranslator, Nightlies, Wiki and bugtracker host", " Attila Bán (MiHaMiX) - WebTranslator, Nightlies, Wiki and bugtracker host",
" Owen Rudge (orudge) - Forum- and masterserver host, OS/2 port", " Owen Rudge (orudge) - Forum- and masterserver host, OS/2 port",
" Peter Nelson (peter1138) - Spiritual descendant from newgrf gods", " Peter Nelson (peter1138) - Spiritual descendant from newgrf gods",
" Christoph Mallon (Tron) - Programmer, code correctness police", " Christoph Mallon (Tron) - Programmer, code correctness police",
@ -221,13 +221,13 @@ static const char *credits[] = {
" Josef Drexler - For his great work on TTDPatch", " Josef Drexler - For his great work on TTDPatch",
" Marcin Grzegorczyk - For his documentation of TTD internals", " Marcin Grzegorczyk - For his documentation of TTD internals",
" Petr Baudis (pasky) - Many patches, newgrf support", " Petr Baudis (pasky) - Many patches, newgrf support",
" Stefan Meißner (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)", " Simon Sasburg (HackyKid) - Many bugfixes he has blessed us with (and PBS)",
" Cian Duffy (MYOB) - BeOS port / manual writing", " Cian Duffy (MYOB) - BeOS port / manual writing",
" Christian Rosentreter (tokai) - MorphOS / AmigaOS port", " Christian Rosentreter (tokai) - MorphOS / AmigaOS port",
"", "",
" Michael Blunck - Pre-Signals and Semaphores Š 2003", " Michael Blunck - Pre-Signals and Semaphores © 2003",
" George - Canal/Lock graphics Š 2003-2004", " George - Canal/Lock graphics © 2003-2004",
" Marcin Grzegorczyk - Foundations for Tracks on Slopes", " Marcin Grzegorczyk - Foundations for Tracks on Slopes",
" All Translators - Who made OpenTTD a truly international game", " All Translators - Who made OpenTTD a truly international game",
" Bug Reporters - Without whom OpenTTD would still be full of bugs!", " 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; 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]); WChar c;
memmove(tb->buf + tb->caretpos, tb->buf + tb->caretpos + 1, tb->length - tb->caretpos); uint width;
tb->length--; 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) bool DeleteTextBufferChar(Textbuf *tb, int delmode)
{ {
if (delmode == WKC_BACKSPACE && tb->caretpos != 0) { if (delmode == WKC_BACKSPACE && tb->caretpos != 0) {
tb->caretpos--; DelChar(tb, true);
tb->caretxoffs -= GetCharacterWidth(FS_NORMAL, (byte)tb->buf[tb->caretpos]);
DelChar(tb);
return true; return true;
} else if (delmode == WKC_DELETE && tb->caretpos < tb->length) { } else if (delmode == WKC_DELETE && tb->caretpos < tb->length) {
DelChar(tb); DelChar(tb, false);
return true; return true;
} }
@ -831,16 +847,17 @@ void DeleteTextBufferAll(Textbuf *tb)
* @param key Character to be inserted * @param key Character to be inserted
* @return Return true on successfull change of Textbuf, or false otherwise * @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); const byte charwidth = GetCharacterWidth(FS_NORMAL, key);
if (tb->length < (tb->maxlength - 1) && (tb->maxwidth == 0 || tb->width + charwidth <= tb->maxwidth)) { size_t len = Utf8CharLen(key);
memmove(tb->buf + tb->caretpos + 1, tb->buf + tb->caretpos, (tb->length - tb->caretpos) + 1); if (tb->length < (tb->maxlength - len) && (tb->maxwidth == 0 || tb->width + charwidth <= tb->maxwidth)) {
tb->buf[tb->caretpos] = key; memmove(tb->buf + tb->caretpos + len, tb->buf + tb->caretpos, tb->length - tb->caretpos + 1);
tb->length++; Utf8Encode(tb->buf + tb->caretpos, key);
tb->width += charwidth; tb->length += len;
tb->width += charwidth;
tb->caretpos++; tb->caretpos += len;
tb->caretxoffs += charwidth; tb->caretxoffs += charwidth;
return true; return true;
} }
@ -859,15 +876,25 @@ bool MoveTextBufferPos(Textbuf *tb, int navmode)
switch (navmode) { switch (navmode) {
case WKC_LEFT: case WKC_LEFT:
if (tb->caretpos != 0) { if (tb->caretpos != 0) {
tb->caretpos--; WChar c;
tb->caretxoffs -= GetCharacterWidth(FS_NORMAL, (byte)tb->buf[tb->caretpos]);
do {
tb->caretpos--;
} while (IsUtf8Part(*(tb->buf + tb->caretpos)));
Utf8Decode(&c, tb->buf + tb->caretpos);
tb->caretxoffs -= GetCharacterWidth(FS_NORMAL, c);
return true; return true;
} }
break; break;
case WKC_RIGHT: case WKC_RIGHT:
if (tb->caretpos < tb->length) { if (tb->caretpos < tb->length) {
tb->caretxoffs += GetCharacterWidth(FS_NORMAL, (byte)tb->buf[tb->caretpos]); WChar c;
tb->caretpos++;
tb->caretpos += Utf8Decode(&c, tb->buf + tb->caretpos);
tb->caretxoffs += GetCharacterWidth(FS_NORMAL, c);
return true; return true;
} }
break; break;
@ -910,16 +937,16 @@ void InitializeTextBuffer(Textbuf *tb, const char *buf, uint16 maxlength, uint16
*/ */
void UpdateTextBufferSize(Textbuf *tb) void UpdateTextBufferSize(Textbuf *tb)
{ {
const char *buf; const char *buf = tb->buf;
WChar c = Utf8Consume(&buf);
tb->length = 0;
tb->width = 0; tb->width = 0;
for (buf = tb->buf; *buf != '\0' && tb->length < (tb->maxlength - 1); buf++) { for (; c != '\0' && tb->length < (tb->maxlength - 1); c = Utf8Consume(&buf)) {
tb->length++; tb->width += GetCharacterWidth(FS_NORMAL, c);
tb->width += GetCharacterWidth(FS_NORMAL, (byte)*buf);
} }
tb->length = buf - tb->buf - 1;
tb->caretpos = tb->length; tb->caretpos = tb->length;
tb->caretxoffs = tb->width; tb->caretxoffs = tb->width;
} }
@ -948,9 +975,10 @@ int HandleEditBoxKey(Window *w, querystr_d *string, int wid, WindowEvent *e)
InvalidateWidget(w, wid); InvalidateWidget(w, wid);
break; break;
default: default:
if (IsValidAsciiChar(e->we.keypress.ascii, string->afilter)) { if (IsValidChar(e->we.keypress.key, string->afilter)) {
if (InsertTextBufferChar(&string->text, e->we.keypress.ascii)) if (InsertTextBufferChar(&string->text, e->we.keypress.key)) {
InvalidateWidget(w, wid); InvalidateWidget(w, wid);
}
} else { // key wasn't caught. Continue only if standard entry specified } else { // key wasn't caught. Continue only if standard entry specified
e->we.keypress.cont = (string->afilter == CS_ALPHANUMERAL); e->we.keypress.cont = (string->afilter == CS_ALPHANUMERAL);
} }

View File

@ -480,13 +480,15 @@ static byte MakeCzechTownName(char *buf, uint32 seed, const char *last)
strecat(buf, name_czech_adj[prefix].name, last); strecat(buf, name_czech_adj[prefix].name, last);
endpos = strlen(buf) - 1; 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) { if (gender == CZG_SMASC && pattern == CZP_PRIVL) {
/* -ovX -> -uv */ /* -ovX -> -uv */
buf[endpos - 2] = 'u'; buf[endpos - 2] = 'u';
assert(buf[endpos - 1] == 'v'); assert(buf[endpos - 1] == 'v');
buf[endpos] = '\0'; buf[endpos] = '\0';
} else { } else {
buf[endpos] = name_czech_patmod[gender][pattern]; strecpy(buf + endpos, name_czech_patmod[gender][pattern], last);
} }
strecat(buf, " ", last); strecat(buf, " ", last);

View File

@ -526,7 +526,7 @@ void ParseConnectionString(const char **player, const char **port, char *connect
if (*p == '#') { if (*p == '#') {
*p = '\0'; *p = '\0';
*player = ++p; *player = ++p;
while (IsValidAsciiChar(*p, CS_NUMERAL)) p++; while (IsValidChar(*p, CS_NUMERAL)) p++;
if (*p == '\0') break; if (*p == '\0') break;
} else if (*p == ':') { } else if (*p == ':') {
*port = p + 1; *port = p + 1;

View File

@ -23,6 +23,7 @@
#include "vehicle.h" #include "vehicle.h"
#include "newgrf_text.h" #include "newgrf_text.h"
#include "table/sprites.h" #include "table/sprites.h"
#include "fontcache.h"
#include "date.h" #include "date.h"
#include "currency.h" #include "currency.h"
#include "sound.h" #include "sound.h"
@ -3039,6 +3040,42 @@ static void LoadGRFSound(byte *buf, int len)
} }
} }
/* Action 0x12 */
static void LoadFontGlyph(byte *buf, int len)
{
/* <12> <num_def> <font_size> <num_char> <base_char>
*
* B num_def Number of definitions
* B font_size Size of font (0 = normal, 1 = small, 2 = large)
* B num_char Number of consecutive glyphs
* W base_char First character index */
uint8 num_def;
uint i;
buf++; len--;
check_length(len, 1, "LoadFontGlyph");
num_def = grf_load_byte(&buf);
check_length(len, 1 + num_def * 4, "LoadFontGlyph");
for (i = 0; i < num_def; i++) {
FontSize size = grf_load_byte(&buf);
uint8 num_char = grf_load_byte(&buf);
uint16 base_char = grf_load_word(&buf);
uint c;
DEBUG(grf, 7) ("LoadFontGlyph: Loading %u glyph(s) at 0x%04X for size %u", num_char, base_char, size);
for (c = 0; c < num_char; c++) {
SetUnicodeGlyph(size, base_char + c, _cur_spriteid);
LoadNextSprite(_cur_spriteid++, _file_index);
_nfo_line++;
}
}
}
/* 'Action 0xFF' */ /* 'Action 0xFF' */
static void GRFDataBlock(byte *buf, int len) static void GRFDataBlock(byte *buf, int len)
{ {
@ -3421,6 +3458,7 @@ static void DecodeSpecialSprite(uint num, GrfLoadingStage stage)
/* 0x0F */ { NULL, NULL, NULL, }, /* 0x0F */ { NULL, NULL, NULL, },
/* 0x10 */ { DefineGotoLabel, NULL, NULL, }, /* 0x10 */ { DefineGotoLabel, NULL, NULL, },
/* 0x11 */ { NULL, NULL, GRFSound, }, /* 0x11 */ { NULL, NULL, GRFSound, },
/* 0x12 */ { NULL, NULL, LoadFontGlyph, },
}; };
byte* buf; byte* buf;

View File

@ -18,6 +18,7 @@
#include "macros.h" #include "macros.h"
#include "table/strings.h" #include "table/strings.h"
#include "newgrf_text.h" #include "newgrf_text.h"
#include "table/control_codes.h"
#define GRFTAB 28 #define GRFTAB 28
#define TABSIZE 11 #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 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++) { if (c == 0x00DE) {
switch ((byte)*c) { /* The thorn ('þ') indicates a unicode string to TTDPatch */
case 0x01: c++; break; unicode = true;
case 0x0D: *c = 10; break; } else {
case 0x0E: *c = 8; break; str--;
case 0x0F: *c = 9; break; }
case 0x1F: *c = 2; c += 2; break;
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 0x7B:
case 0x7C: case 0x7C:
case 0x7D: case 0x7D:
case 0x7E: *c = 0x8E; break; case 0x7E: d += Utf8Encode(d, SCC_NUM); break;
case 0x81: c += 2; break; case 0x7F: d += Utf8Encode(d, SCC_CURRENCY); break;
case 0x85: *c = 0x86; break; case 0x80: d += Utf8Encode(d, SCC_STRING); break;
case 0x88: *c = 15; break; case 0x81: {
case 0x89: *c = 16; break; StringID string;
case 0x8A: *c = 17; break; string = *str++;
case 0x8B: *c = 18; break; string |= *str++ << 8;
case 0x8C: *c = 19; break; d += Utf8Encode(d, SCC_STRING_ID);
case 0x8D: *c = 20; break; d += Utf8Encode(d, string);
case 0x8E: *c = 21; break; break;
case 0x8F: *c = 22; break; }
case 0x90: *c = 23; break; case 0x82: d += Utf8Encode(d, SCC_DATE_TINY); break;
case 0x91: *c = 24; break; case 0x83: d += Utf8Encode(d, SCC_DATE_SHORT); break;
case 0x92: *c = 25; break; case 0x84: d += Utf8Encode(d, SCC_VELOCITY); break;
case 0x93: *c = 26; break; case 0x85: d += Utf8Encode(d, SCC_SKIP); break;
case 0x94: *c = 27; break; case 0x86: /* "Rotate down top 4 words on stack" */ break;
case 0x95: *c = 28; break; case 0x87: d += Utf8Encode(d, SCC_VOLUME); break;
case 0x96: *c = 29; break; case 0x88: d += Utf8Encode(d, SCC_BLUE); break;
case 0x97: *c = 30; break; case 0x89: d += Utf8Encode(d, SCC_SILVER); break;
case 0x98: *c = 31; 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: default:
if (unicode) {
d += Utf8Encode(d, Utf8Consume(&tmp));
str = tmp;
break;
}
/* Validate any unhandled character */ /* Validate any unhandled character */
if (!IsValidAsciiChar(*c, CS_ALPHANUMERAL)) *c = '?'; if (!IsValidChar(c, CS_ALPHANUMERAL)) c = '?';
d += Utf8Encode(d, c);
break; 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) 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; GRFText *newtext;
uint id; uint id;
@ -231,12 +291,14 @@ StringID AddGRFString(uint32 grfid, uint16 stringid, byte langid_to_add, bool ne
/* Too many strings allocated, return empty */ /* Too many strings allocated, return empty */
if (id == lengthof(_grf_text)) return STR_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->next = NULL;
newtext->langid = langid_to_add; 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 we didn't find our stringid and grfid in the list, allocate a new id */
if (id == _num_grf_texts) _num_grf_texts++; if (id == _num_grf_texts) _num_grf_texts++;

View File

@ -15,6 +15,7 @@
#include "sound.h" #include "sound.h"
#include "variables.h" #include "variables.h"
#include "date.h" #include "date.h"
#include "string.h"
/* News system /* News system
* News system is realized as a FIFO queue (in an array) * 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) static void DrawNewsString(int x, int y, uint16 color, const NewsItem *ni, uint maxw)
{ {
char buffer[512], buffer2[512]; char buffer[512], buffer2[512];
char *ptr, *dest; const char *ptr;
char *dest;
StringID str; StringID str;
if (ni->display_mode == 3) { 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)); GetString(buffer, str, lastof(buffer));
/* Copy the just gotten string to another buffer to remove any formatting /* Copy the just gotten string to another buffer to remove any formatting
* from it such as big fonts, etc. */ * from it such as big fonts, etc. */
for (ptr = buffer, dest = buffer2; *ptr != '\0'; ptr++) { ptr = buffer;
if (*ptr == '\r') { dest = buffer2;
for (;;) {
WChar c = Utf8Consume(&ptr);
if (c == 0) break;
if (c == '\r') {
dest[0] = dest[1] = dest[2] = dest[3] = ' '; dest[0] = dest[1] = dest[2] = dest[3] = ' ';
dest += 4; dest += 4;
} else if ((byte)*ptr >= ' ' && ((byte)*ptr < 0x88 || (byte)*ptr >= 0x99)) { } else if (IsPrintable(c)) {
*dest++ = *ptr; dest += Utf8Encode(dest, c);
} }
} }

View File

@ -52,6 +52,7 @@
#include "genworld.h" #include "genworld.h"
#include "date.h" #include "date.h"
#include "clear_map.h" #include "clear_map.h"
#include "fontcache.h"
#include <stdarg.h> #include <stdarg.h>
@ -432,10 +433,15 @@ int ttd_main(int argc, char *argv[])
MxInitialize(11025); MxInitialize(11025);
SoundInitialize("sample.cat"); SoundInitialize("sample.cat");
/* Initialize FreeType */
InitFreeType();
// This must be done early, since functions use the InvalidateWindow* calls // This must be done early, since functions use the InvalidateWindow* calls
InitWindowSystem(); InitWindowSystem();
GfxLoadSprites(); GfxLoadSprites();
/* Initialize the unicode to sprite mapping table */
InitializeUnicodeGlyphMap();
LoadStringWidthTable(); LoadStringWidthTable();
DEBUG(driver, 1) ("Loading drivers..."); DEBUG(driver, 1) ("Loading drivers...");
@ -1526,5 +1532,9 @@ bool AfterLoadGame(void)
} }
} }
if (CheckSavegameVersion(37)) {
ConvertNameArray();
}
return true; return true;
} }

View File

@ -464,6 +464,10 @@ enum {
EXPENSES_OTHER = 12, EXPENSES_OTHER = 12,
}; };
enum {
MAX_LANG = 64,
};
// special string constants // special string constants
enum SpecialStrings { enum SpecialStrings {
@ -506,17 +510,17 @@ enum SpecialStrings {
SPECSTR_PRESIDENT_NAME = 0x70E7, SPECSTR_PRESIDENT_NAME = 0x70E7,
SPECSTR_SONGNAME = 0x70E8, SPECSTR_SONGNAME = 0x70E8,
// reserve 32 strings for the *.lng files // reserve MAX_LANG strings for the *.lng files
SPECSTR_LANGUAGE_START = 0x7100, SPECSTR_LANGUAGE_START = 0x7100,
SPECSTR_LANGUAGE_END = 0x711f, SPECSTR_LANGUAGE_END = SPECSTR_LANGUAGE_START + MAX_LANG - 1,
// reserve 32 strings for various screen resolutions // reserve 32 strings for various screen resolutions
SPECSTR_RESOLUTION_START = 0x7120, SPECSTR_RESOLUTION_START = SPECSTR_LANGUAGE_END + 1,
SPECSTR_RESOLUTION_END = 0x713f, SPECSTR_RESOLUTION_END = SPECSTR_RESOLUTION_START + 0x1F,
// reserve 32 strings for screenshot formats // reserve 32 strings for screenshot formats
SPECSTR_SCREENSHOT_START = 0x7140, SPECSTR_SCREENSHOT_START = SPECSTR_RESOLUTION_END + 1,
SPECSTR_SCREENSHOT_END = 0x715F, SPECSTR_SCREENSHOT_END = SPECSTR_SCREENSHOT_START + 0x1F,
// Used to implement SetDParamStr // Used to implement SetDParamStr
STR_SPEC_DYNSTRING = 0xF800, STR_SPEC_DYNSTRING = 0xF800,

View File

@ -234,6 +234,9 @@
<File <File
RelativePath=".\fios.c"> RelativePath=".\fios.c">
</File> </File>
<File
RelativePath=".\fontcache.c">
</File>
<File <File
RelativePath=".\genworld.c"> RelativePath=".\genworld.c">
</File> </File>
@ -478,6 +481,9 @@
<File <File
RelativePath=".\fileio.h"> RelativePath=".\fileio.h">
</File> </File>
<File
RelativePath=".\fontcache.h">
</File>
<File <File
RelativePath=".\functions.h"> RelativePath=".\functions.h">
</File> </File>
@ -853,6 +859,9 @@
<File <File
RelativePath=".\table\clear_land.h"> RelativePath=".\table\clear_land.h">
</File> </File>
<File
RelativePath=".\table\control_codes.h">
</File>
<File <File
RelativePath=".\table\elrail_data.h"> RelativePath=".\table\elrail_data.h">
</File> </File>
@ -910,6 +919,9 @@
<File <File
RelativePath=".\table\tunnel_land.h"> RelativePath=".\table\tunnel_land.h">
</File> </File>
<File
RelativePath=".\table\unicode.h">
</File>
<File <File
RelativePath=".\table\unmovable_land.h"> RelativePath=".\table\unmovable_land.h">
</File> </File>

View File

@ -560,6 +560,10 @@
RelativePath=".\fios.c" RelativePath=".\fios.c"
> >
</File> </File>
<File
RelativePath=".\fontcache.c"
>
</File>
<File <File
RelativePath=".\genworld.c" RelativePath=".\genworld.c"
> >
@ -931,6 +935,10 @@
RelativePath=".\fileio.h" RelativePath=".\fileio.h"
> >
</File> </File>
<File
RelativePath=".\fontcache.h"
>
</File>
<File <File
RelativePath=".\functions.h" RelativePath=".\functions.h"
> >
@ -1435,6 +1443,10 @@
RelativePath=".\table\clear_land.h" RelativePath=".\table\clear_land.h"
> >
</File> </File>
<File
RelativePath=".\table\control_codes.h"
>
</File>
<File <File
RelativePath=".\table\elrail_data.h" RelativePath=".\table\elrail_data.h"
> >
@ -1511,6 +1523,10 @@
RelativePath=".\table\tunnel_land.h" RelativePath=".\table\tunnel_land.h"
> >
</File> </File>
<File
RelativePath=".\table\unicode.h"
>
</File>
<File <File
RelativePath=".\table\unmovable_land.h" RelativePath=".\table\unmovable_land.h"
> >

View File

@ -30,7 +30,7 @@
#include "variables.h" #include "variables.h"
#include <setjmp.h> #include <setjmp.h>
const uint16 SAVEGAME_VERSION = 36; const uint16 SAVEGAME_VERSION = 37;
uint16 _sl_version; /// the major savegame version identifier uint16 _sl_version; /// the major savegame version identifier
byte _sl_minor_version; /// the minor savegame version, DO NOT USE! byte _sl_minor_version; /// the minor savegame version, DO NOT USE!

View File

@ -38,6 +38,10 @@
#include "newgrf.h" #include "newgrf.h"
#include "genworld.h" #include "genworld.h"
#include "date.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 */ /** The patch values that are used for new games and/or modified in config file */
Patches _patches_newgame; 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("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_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), 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() SDTG_END()
}; };

View File

@ -3,6 +3,7 @@
#include "../stdafx.h" #include "../stdafx.h"
#include "../macros.h" #include "../macros.h"
#include "../string.h" #include "../string.h"
#include "../table/control_codes.h"
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
@ -199,26 +200,41 @@ static void PutByte(byte c)
} }
static void EmitSingleByte(char *buf, int value) static void PutUtf8(uint32 value)
{ {
if (*buf != '\0') warning("Ignoring trailing letters in command"); if (value < 0x80) {
PutByte((byte)value); 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"); if (*buf != '\0') warning("Ignoring trailing letters in command");
PutByte(0x85); PutUtf8(value);
PutByte((byte)value);
} }
static void EmitSetX(char *buf, int value) static void EmitSetX(char *buf, int value)
{ {
char *err; char *err;
int x = strtol(buf, &err, 0); int x = strtol(buf, &err, 0);
if (*err != 0) fatal("SetX param invalid"); if (*err != 0) fatal("SetX param invalid");
PutByte(1); PutUtf8(SCC_SETX);
PutByte((byte)x); PutByte((byte)x);
} }
@ -234,7 +250,7 @@ static void EmitSetXY(char *buf, int value)
y = strtol(err + 1, &err, 0); y = strtol(err + 1, &err, 0);
if (*err != 0) fatal("SetXY param invalid"); if (*err != 0) fatal("SetXY param invalid");
PutByte(2); PutUtf8(SCC_SETXY);
PutByte((byte)x); PutByte((byte)x);
PutByte((byte)y); PutByte((byte)y);
} }
@ -352,7 +368,7 @@ static void EmitPlural(char *buf, int value)
} }
} }
PutByte(0x8D); PutUtf8(SCC_PLURAL_LIST);
PutByte(TranslateArgumentIdx(argidx)); PutByte(TranslateArgumentIdx(argidx));
EmitWordList(words, nw); EmitWordList(words, nw);
} }
@ -372,7 +388,7 @@ static void EmitGender(char *buf, int value)
if (strcmp(buf, _genders[nw]) == 0) break; if (strcmp(buf, _genders[nw]) == 0) break;
} }
// now nw contains the gender index // now nw contains the gender index
PutByte(0x87); PutUtf8(SCC_GENDER_INDEX);
PutByte(nw); PutByte(nw);
} else { } else {
const char* words[8]; const char* words[8];
@ -386,8 +402,7 @@ static void EmitGender(char *buf, int value)
if (words[nw] == NULL) break; if (words[nw] == NULL) break;
} }
if (nw != _numgenders) fatal("Bad # of arguments for gender command"); if (nw != _numgenders) fatal("Bad # of arguments for gender command");
PutByte(0x85); PutUtf8(SCC_GENDER_LIST);
PutByte(13);
PutByte(TranslateArgumentIdx(argidx)); PutByte(TranslateArgumentIdx(argidx));
EmitWordList(words, nw); EmitWordList(words, nw);
} }
@ -396,109 +411,108 @@ static void EmitGender(char *buf, int value)
static const CmdStruct _cmd_structs[] = { static const CmdStruct _cmd_structs[] = {
// Update position // Update position
{"SETX", EmitSetX, 1, 0, 0}, {"SETX", EmitSetX, SCC_SETX, 0, 0},
{"SETXY", EmitSetXY, 2, 0, 0}, {"SETXY", EmitSetXY, SCC_SETXY, 0, 0},
// Font size // Font size
{"TINYFONT", EmitSingleByte, 8, 0, 0}, {"TINYFONT", EmitSingleChar, SCC_TINYFONT, 0, 0},
{"BIGFONT", EmitSingleByte, 9, 0, 0}, {"BIGFONT", EmitSingleChar, SCC_BIGFONT, 0, 0},
// Colors // Colors
{"BLUE", EmitSingleByte, 15, 0, 0}, {"BLUE", EmitSingleChar, SCC_BLUE, 0, 0},
{"SILVER", EmitSingleByte, 16, 0, 0}, {"SILVER", EmitSingleChar, SCC_SILVER, 0, 0},
{"GOLD", EmitSingleByte, 17, 0, 0}, {"GOLD", EmitSingleChar, SCC_GOLD, 0, 0},
{"RED", EmitSingleByte, 18, 0, 0}, {"RED", EmitSingleChar, SCC_RED, 0, 0},
{"PURPLE", EmitSingleByte, 19, 0, 0}, {"PURPLE", EmitSingleChar, SCC_PURPLE, 0, 0},
{"LTBROWN", EmitSingleByte, 20, 0, 0}, {"LTBROWN", EmitSingleChar, SCC_LTBROWN, 0, 0},
{"ORANGE", EmitSingleByte, 21, 0, 0}, {"ORANGE", EmitSingleChar, SCC_ORANGE, 0, 0},
{"GREEN", EmitSingleByte, 22, 0, 0}, {"GREEN", EmitSingleChar, SCC_GREEN, 0, 0},
{"YELLOW", EmitSingleByte, 23, 0, 0}, {"YELLOW", EmitSingleChar, SCC_YELLOW, 0, 0},
{"DKGREEN", EmitSingleByte, 24, 0, 0}, {"DKGREEN", EmitSingleChar, SCC_DKGREEN, 0, 0},
{"CREAM", EmitSingleByte, 25, 0, 0}, {"CREAM", EmitSingleChar, SCC_CREAM, 0, 0},
{"BROWN", EmitSingleByte, 26, 0, 0}, {"BROWN", EmitSingleChar, SCC_BROWN, 0, 0},
{"WHITE", EmitSingleByte, 27, 0, 0}, {"WHITE", EmitSingleChar, SCC_WHITE, 0, 0},
{"LTBLUE", EmitSingleByte, 28, 0, 0}, {"LTBLUE", EmitSingleChar, SCC_LTBLUE, 0, 0},
{"GRAY", EmitSingleByte, 29, 0, 0}, {"GRAY", EmitSingleChar, SCC_GRAY, 0, 0},
{"DKBLUE", EmitSingleByte, 30, 0, 0}, {"DKBLUE", EmitSingleChar, SCC_DKBLUE, 0, 0},
{"BLACK", EmitSingleByte, 31, 0, 0}, {"BLACK", EmitSingleChar, SCC_BLACK, 0, 0},
{"CURRCOMPACT", EmitEscapedByte, 0, 1, 0}, // compact currency (32 bits) {"CURRCOMPACT", EmitSingleChar, SCC_CURRENCY_COMPACT, 1, 0}, // compact currency (32 bits)
{"REV", EmitEscapedByte, 2, 0, 0}, // openttd revision string {"REV", EmitSingleChar, SCC_REVISION, 0, 0}, // openttd revision string
{"SHORTCARGO", EmitEscapedByte, 3, 2, 0}, // short cargo description, only ### tons, or ### litres {"SHORTCARGO", EmitSingleChar, SCC_CARGO_SHORT, 2, 0}, // short cargo description, only ### tons, or ### litres
{"CURRCOMPACT64", EmitEscapedByte, 4, 2, 0}, // compact currency 64 bits {"CURRCOMPACT64", EmitSingleChar, SCC_CURRENCY_COMPACT_64, 2, 0}, // compact currency 64 bits
// These are special versions of {STRING1} // These are special versions of {STRING1}
// The first string includes the second string. // The first string includes the second string.
{"COMPANY", EmitEscapedByte, 5, 1, 0}, {"COMPANY", EmitSingleChar, SCC_STRING1, 1, 0},
{"PLAYERNAME", EmitEscapedByte, 5, 1, 0}, {"PLAYERNAME", EmitSingleChar, SCC_STRING1, 1, 0},
{"VEHICLE", EmitEscapedByte, 5, 1, 0}, {"VEHICLE", EmitSingleChar, SCC_STRING1, 1, 0},
{"STRING1", EmitEscapedByte, 5, 1, C_CASE}, // included string that consumes ONE argument {"STRING1", EmitSingleChar, SCC_STRING1, 1, C_CASE}, // included string that consumes ONE argument
{"STRING2", EmitEscapedByte, 6, 2, C_CASE}, // included string that consumes TWO arguments {"STRING2", EmitSingleChar, SCC_STRING2, 2, C_CASE}, // included string that consumes TWO arguments
{"STRING3", EmitEscapedByte, 7, 3, C_CASE}, // included string that consumes THREE arguments {"STRING3", EmitSingleChar, SCC_STRING3, 3, C_CASE}, // included string that consumes THREE arguments
{"STRING4", EmitEscapedByte, 8, 4, C_CASE}, // included string that consumes FOUR arguments {"STRING4", EmitSingleChar, SCC_STRING4, 4, C_CASE}, // included string that consumes FOUR arguments
{"STRING5", EmitEscapedByte, 9, 5, C_CASE}, // included string that consumes FIVE 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 {"STATIONFEATURES", EmitSingleChar, SCC_STATION_FEATURES, 1, 0}, // station features string, icons of the features
{"INDUSTRY", EmitEscapedByte, 11, 1, 0}, // industry, takes an industry # {"INDUSTRY", EmitSingleChar, SCC_INDUSTRY_NAME, 1, 0}, // industry, takes an industry #
{"VOLUME", EmitEscapedByte, 12, 1, 0}, {"CARGO", EmitSingleChar, SCC_CARGO, 2, 0},
{"DATE_TINY", EmitEscapedByte, 14, 1, 0}, {"POWER", EmitSingleChar, SCC_POWER, 1, 0},
{"CARGO", EmitEscapedByte, 15, 2, 0}, {"VOLUME", EmitSingleChar, SCC_VOLUME, 1, 0},
{"POWER", EmitEscapedByte, 16, 1, 0}, {"VOLUME_S", EmitSingleChar, SCC_VOLUME_SHORT, 1, 0},
{"VOLUME_S", EmitEscapedByte, 17, 1, 0}, {"WEIGHT", EmitSingleChar, SCC_WEIGHT, 1, 0},
{"WEIGHT", EmitEscapedByte, 18, 1, 0}, {"WEIGHT_S", EmitSingleChar, SCC_WEIGHT_SHORT, 1, 0},
{"WEIGHT_S", EmitEscapedByte, 19, 1, 0}, {"FORCE", EmitSingleChar, SCC_FORCE, 1, 0},
{"FORCE", EmitEscapedByte, 20, 1, 0}, {"VELOCITY", EmitSingleChar, SCC_VELOCITY, 1, 0},
{"P", EmitPlural, 0, 0, C_DONTCOUNT}, // plural specifier {"P", EmitPlural, 0, 0, C_DONTCOUNT}, // plural specifier
{"G", EmitGender, 0, 0, C_DONTCOUNT}, // gender specifier {"G", EmitGender, 0, 0, C_DONTCOUNT}, // gender specifier
{"DATE_LONG", EmitSingleByte, 0x82, 1, 0}, {"DATE_TINY", EmitSingleChar, SCC_DATE_TINY, 1, 0},
{"DATE_SHORT", EmitSingleByte, 0x83, 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 {"STRING", EmitSingleChar, SCC_STRING, 1, C_CASE},
{"SKIP", EmitSingleByte, 0x86, 1, 0},
{"STRING", EmitSingleByte, 0x88, 1, C_CASE},
// Numbers // Numbers
{"COMMA", EmitSingleByte, 0x8B, 1, 0}, // Number with comma {"COMMA", EmitSingleChar, SCC_COMMA, 1, 0}, // Number with comma
{"NUM", EmitSingleByte, 0x8E, 1, 0}, // Signed number {"NUM", EmitSingleChar, SCC_NUM, 1, 0}, // Signed number
{"CURRENCY", EmitSingleByte, 0x8F, 1, 0}, {"CURRENCY", EmitSingleChar, SCC_CURRENCY, 1, 0},
{"CURRENCY64", EmitSingleByte, 0x9C, 2, 0}, {"CURRENCY64", EmitSingleChar, SCC_CURRENCY_64, 2, 0},
{"WAYPOINT", EmitSingleByte, 0x99, 1, 0}, // waypoint name {"WAYPOINT", EmitSingleChar, SCC_WAYPOINT_NAME, 1, 0}, // waypoint name
{"STATION", EmitSingleByte, 0x9A, 1, 0}, {"STATION", EmitSingleChar, SCC_STATION_NAME, 1, 0},
{"TOWN", EmitSingleByte, 0x9B, 1, 0}, {"TOWN", EmitSingleChar, SCC_TOWN_NAME, 1, 0},
// 0x9D is used for the pseudo command SETCASE // 0x9D is used for the pseudo command SETCASE
// 0x9E is used for case switching // 0x9E is used for case switching
{"", EmitSingleByte, '\n', 0, C_DONTCOUNT}, {"", EmitSingleChar, '\n', 0, C_DONTCOUNT},
{"{", EmitSingleByte, '{', 0, C_DONTCOUNT}, {"{", EmitSingleChar, '{', 0, C_DONTCOUNT},
{"UPARROW", EmitSingleByte, 0x80, 0, 0}, {"UPARROW", EmitSingleChar, SCC_UPARROW, 0, 0},
{"SMALLUPARROW", EmitSingleByte, 0x90, 0, 0}, {"SMALLUPARROW", EmitSingleChar, SCC_SMALLUPARROW, 0, 0},
{"SMALLDOWNARROW", EmitSingleByte, 0x91, 0, 0}, {"SMALLDOWNARROW", EmitSingleChar, SCC_SMALLDOWNARROW, 0, 0},
{"TRAIN", EmitSingleByte, 0x94, 0, 0}, {"TRAIN", EmitSingleChar, SCC_TRAIN, 0, 0},
{"LORRY", EmitSingleByte, 0x95, 0, 0}, {"LORRY", EmitSingleChar, SCC_LORRY, 0, 0},
{"BUS", EmitSingleByte, 0x96, 0, 0}, {"BUS", EmitSingleChar, SCC_BUS, 0, 0},
{"PLANE", EmitSingleByte, 0x97, 0, 0}, {"PLANE", EmitSingleChar, SCC_PLANE, 0, 0},
{"SHIP", EmitSingleByte, 0x98, 0, 0}, {"SHIP", EmitSingleChar, SCC_SHIP, 0, 0},
{"NBSP", EmitSingleByte, 0xA0, 0, C_DONTCOUNT}, {"NBSP", EmitSingleChar, 0xA0, 0, C_DONTCOUNT},
{"CENT", EmitSingleByte, '¢', 0, C_DONTCOUNT}, {"CENT", EmitSingleChar, 0xA2, 0, C_DONTCOUNT},
{"POUNDSIGN", EmitSingleByte, '£', 0, C_DONTCOUNT}, {"POUNDSIGN", EmitSingleChar, 0xA3, 0, C_DONTCOUNT},
{"EURO", EmitSingleByte, '¤', 0, C_DONTCOUNT}, {"EURO", EmitSingleChar, 0x20AC, 0, C_DONTCOUNT},
{"YENSIGN", EmitSingleByte, '¥', 0, C_DONTCOUNT}, {"YENSIGN", EmitSingleChar, 0xA5, 0, C_DONTCOUNT},
{"COPYRIGHT", EmitSingleByte, '©', 0, C_DONTCOUNT}, {"COPYRIGHT", EmitSingleChar, 0xA9, 0, C_DONTCOUNT},
{"DOWNARROW", EmitSingleByte, 0xAA, 0, C_DONTCOUNT}, {"DOWNARROW", EmitSingleChar, SCC_DOWNARROW, 0, C_DONTCOUNT},
{"CHECKMARK", EmitSingleByte, 0xAC, 0, C_DONTCOUNT}, {"CHECKMARK", EmitSingleChar, SCC_CHECKMARK, 0, C_DONTCOUNT},
{"CROSS", EmitSingleByte, 0xAD, 0, C_DONTCOUNT}, {"CROSS", EmitSingleChar, SCC_CROSS, 0, C_DONTCOUNT},
{"REGISTERED", EmitSingleByte, '®', 0, C_DONTCOUNT}, {"REGISTERED", EmitSingleChar, 0xAE, 0, C_DONTCOUNT},
{"RIGHTARROW", EmitSingleByte, 0xAF, 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) static void PutArgidxCommand(void)
{ {
PutByte(0x8C); PutUtf8(SCC_ARG_INDEX);
PutByte(TranslateArgumentIdx(_cur_argidx)); PutByte(TranslateArgumentIdx(_cur_argidx));
} }
@ -1052,7 +1066,7 @@ static void PutCommandString(const char *str)
if (cs == NULL) break; if (cs == NULL) break;
if (casei != -1) { if (casei != -1) {
PutByte(0x9D); // {SETCASE} PutUtf8(SCC_SETCASE); // {SETCASE}
PutByte(casei); PutByte(casei);
} }
@ -1163,7 +1177,7 @@ static void WriteLangfile(const char *filename, int show_todo)
// It has this format // It has this format
// <0x9E> <NUM CASES> <CASE1> <LEN1> <STRING1> <CASE2> <LEN2> <STRING2> <CASE3> <LEN3> <STRING3> <STRINGDEFAULT> // <0x9E> <NUM CASES> <CASE1> <LEN1> <STRING1> <CASE2> <LEN2> <STRING2> <CASE3> <LEN3> <STRING3> <STRINGDEFAULT>
// Each LEN is printed using 2 bytes in big endian order. // Each LEN is printed using 2 bytes in big endian order.
PutByte(0x9E); PutUtf8(SCC_SWITCH_CASE);
// Count the number of cases // Count the number of cases
for (num = 0, c = casep; c; c = c->next) num++; for (num = 0, c = casep; c; c = c->next) num++;
PutByte(num); PutByte(num);

124
string.c
View File

@ -4,6 +4,8 @@
#include "openttd.h" #include "openttd.h"
#include "functions.h" #include "functions.h"
#include "string.h" #include "string.h"
#include "macros.h"
#include "table/control_codes.h"
#include <stdarg.h> #include <stdarg.h>
#include <ctype.h> // required for tolower() #include <ctype.h> // required for tolower()
@ -68,8 +70,27 @@ char* CDECL str_fmt(const char* str, ...)
void str_validate(char *str) void str_validate(char *str)
{ {
for (; *str != '\0'; str++) char *dst = str;
if (!IsValidAsciiChar(*str, CS_ALPHANUMERAL)) *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) void str_strip_colours(char *str)
@ -92,29 +113,15 @@ void str_strip_colours(char *str)
* @param afilter the filter to use * @param afilter the filter to use
* @return true or false depending if the character is printable/valid or not * @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) { switch (afilter) {
case CS_ALPHANUMERAL: case CS_ALPHANUMERAL: return IsPrintable(key);
firsttest = (key >= ' ' && key < 127); case CS_NUMERAL: return (key >= '0' && key <= '9');
break; case CS_ALPHA: return IsPrintable(key) && !(key >= '0' && key <= '9');
/* 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;
} }
/* Allow some special chars too that are non-ASCII but still valid (like '^' above 'a') */ return false;
return (firsttest || (key >= 160 &&
key != 0xAA && key != 0xAC && key != 0xAD && key != 0xAF &&
key != 0xB5 && key != 0xB6 && key != 0xB7 && key != 0xB9));
} }
void strtolower(char *str) 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 /* _MSC_VER */
#endif /* WIN32 */ #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;
}

View File

@ -3,6 +3,8 @@
#ifndef STRING_H #ifndef STRING_H
#define STRING_H #define STRING_H
#include "macros.h"
/* /*
* dst: destination buffer * dst: destination buffer
* src: string to copy/concatenate * src: string to copy/concatenate
@ -33,7 +35,7 @@ void str_validate(char *str);
void str_strip_colours(char *str); void str_strip_colours(char *str);
/** /**
* Valid filter types for IsValidAsciiChar. * Valid filter types for IsValidChar.
*/ */
typedef enum CharSetFilter { typedef enum CharSetFilter {
CS_ALPHANUMERAL, //! Both numeric and alphabetic and spaces and stuff CS_ALPHANUMERAL, //! Both numeric and alphabetic and spaces and stuff
@ -41,6 +43,11 @@ typedef enum CharSetFilter {
CS_ALPHA, //! Only alphabetic values CS_ALPHA, //! Only alphabetic values
} CharSetFilter; } 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 * 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. * sure no invalid keys can get into an editbox, like BELL.
@ -48,9 +55,50 @@ typedef enum CharSetFilter {
* @param afilter the filter to use * @param afilter the filter to use
* @return true or false depending if the character is printable/valid or not * @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 */ #endif /* STRING_H */

361
strings.c
View File

@ -18,6 +18,7 @@
#include "variables.h" #include "variables.h"
#include "newgrf_text.h" #include "newgrf_text.h"
#include "table/landscape_const.h" #include "table/landscape_const.h"
#include "table/control_codes.h"
#include "music.h" #include "music.h"
#include "date.h" #include "date.h"
#include "industry.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. // 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, // The duration of the bound string is valid only until the next GetString,
// so be careful. // 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) static char* FormatString(char* buff, const char* str, const int32* argv, uint casei, const char* last)
{ {
extern const char _openttd_revision[]; extern const char _openttd_revision[];
byte b; WChar b;
const int32 *argv_orig = argv; const int32 *argv_orig = argv;
uint modifier = 0; uint modifier = 0;
while ((b = *str++) != '\0') { while ((b = Utf8Consume(&str)) != '\0') {
switch (b) { switch (b) {
case 0x1: // {SETX} case SCC_SETX: // {SETX}
if (buff != last && buff + 1 != last) { if (buff + Utf8CharLen(SCC_SETX) + 1 < last) {
*buff++ = b; buff += Utf8Encode(buff, SCC_SETX);
*buff++ = *str++; *buff++ = *str++;
} }
break; break;
case 0x2: // {SETXY}
if (buff != last && buff + 1 != last && buff + 2 != last) {
*buff++ = b;
*buff++ = *str++;
*buff++ = *str++;
}
break;
case 0x81: // {STRINL} case SCC_SETXY: // {SETXY}
buff = GetStringWithArgs(buff, ReadLE16Unaligned(str), argv, last); if (buff + Utf8CharLen(SCC_SETXY) + 2 < last) {
str += 2; buff += Utf8Encode(buff, SCC_SETXY);
break; *buff++ = *str++;
case 0x82: // {DATE_LONG} *buff++ = *str++;
buff = FormatYmdString(buff, GetInt32(&argv), last); }
break; break;
case 0x83: // {DATE_SHORT}
buff = FormatMonthAndYear(buff, GetInt32(&argv), last); case SCC_STRING_ID: // {STRINL}
break; buff = GetStringWithArgs(buff, Utf8Consume(&str), argv, last);
case 0x84: {// {VELOCITY} break;
int32 args[1];
assert(_opt_ptr->units < lengthof(units)); case SCC_DATE_LONG: // {DATE_LONG}
args[0] = GetInt32(&argv) * units[_opt_ptr->units].s_m >> units[_opt_ptr->units].s_s; buff = FormatYmdString(buff, GetInt32(&argv), last);
buff = FormatString(buff, GetStringPtr(units[_opt_ptr->units].velocity), args, modifier >> 24, last); break;
modifier = 0;
break; case SCC_DATE_SHORT: // {DATE_SHORT}
} buff = FormatMonthAndYear(buff, GetInt32(&argv), last);
// 0x85 is used as escape character.. break;
case 0x85:
switch (*str++) { case SCC_VELOCITY: {// {VELOCITY}
case 0: /* {CURRCOMPACT} */ 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); buff = FormatGenericCurrency(buff, _currency, GetInt32(&argv), true, last);
break; break;
case 2: /* {REV} */
case SCC_REVISION: /* {REV} */
buff = strecpy(buff, _openttd_revision, last); buff = strecpy(buff, _openttd_revision, last);
break; break;
case 3: { /* {SHORTCARGO} */
case SCC_CARGO_SHORT: { /* {SHORTCARGO} */
// Short description of cargotypes. Layout: // Short description of cargotypes. Layout:
// 8-bit = cargo type // 8-bit = cargo type
// 16-bit = cargo count // 16-bit = cargo count
@ -642,40 +654,46 @@ static char* FormatString(char* buff, const char* str, const int32* argv, uint c
break; break;
} }
} break; } break;
case 4: {/* {CURRCOMPACT64} */
case SCC_CURRENCY_COMPACT_64: { /* {CURRCOMPACT64} */
// 64 bit compact currency-unit // 64 bit compact currency-unit
buff = FormatGenericCurrency(buff, _currency, GetInt64(&argv), true, last); buff = FormatGenericCurrency(buff, _currency, GetInt64(&argv), true, last);
break; break;
} }
case 5: { /* {STRING1} */
case SCC_STRING1: { /* {STRING1} */
// String that consumes ONE argument // String that consumes ONE argument
uint str = modifier + GetInt32(&argv); uint str = modifier + GetInt32(&argv);
buff = GetStringWithArgs(buff, str, GetArgvPtr(&argv, 1), last); buff = GetStringWithArgs(buff, str, GetArgvPtr(&argv, 1), last);
modifier = 0; modifier = 0;
break; break;
} }
case 6: { /* {STRING2} */
case SCC_STRING2: { /* {STRING2} */
// String that consumes TWO arguments // String that consumes TWO arguments
uint str = modifier + GetInt32(&argv); uint str = modifier + GetInt32(&argv);
buff = GetStringWithArgs(buff, str, GetArgvPtr(&argv, 2), last); buff = GetStringWithArgs(buff, str, GetArgvPtr(&argv, 2), last);
modifier = 0; modifier = 0;
break; break;
} }
case 7: { /* {STRING3} */
case SCC_STRING3: { /* {STRING3} */
// String that consumes THREE arguments // String that consumes THREE arguments
uint str = modifier + GetInt32(&argv); uint str = modifier + GetInt32(&argv);
buff = GetStringWithArgs(buff, str, GetArgvPtr(&argv, 3), last); buff = GetStringWithArgs(buff, str, GetArgvPtr(&argv, 3), last);
modifier = 0; modifier = 0;
break; break;
} }
case 8: { /* {STRING4} */
case SCC_STRING4: { /* {STRING4} */
// String that consumes FOUR arguments // String that consumes FOUR arguments
uint str = modifier + GetInt32(&argv); uint str = modifier + GetInt32(&argv);
buff = GetStringWithArgs(buff, str, GetArgvPtr(&argv, 4), last); buff = GetStringWithArgs(buff, str, GetArgvPtr(&argv, 4), last);
modifier = 0; modifier = 0;
break; break;
} }
case 9: { /* {STRING5} */
case SCC_STRING5: { /* {STRING5} */
// String that consumes FIVE arguments // String that consumes FIVE arguments
uint str = modifier + GetInt32(&argv); uint str = modifier + GetInt32(&argv);
buff = GetStringWithArgs(buff, str, GetArgvPtr(&argv, 5), last); 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; break;
} }
case 10: { /* {STATIONFEATURES} */ case SCC_STATION_FEATURES: { /* {STATIONFEATURES} */
buff = StationGetSpecialString(buff, GetInt32(&argv), last); buff = StationGetSpecialString(buff, GetInt32(&argv), last);
break; break;
} }
case 11: { /* {INDUSTRY} */ case SCC_INDUSTRY_NAME: { /* {INDUSTRY} */
const Industry* i = GetIndustry(GetInt32(&argv)); const Industry* i = GetIndustry(GetInt32(&argv));
int32 args[2]; int32 args[2];
@ -704,7 +722,7 @@ static char* FormatString(char* buff, const char* str, const int32* argv, uint c
break; break;
} }
case 12: { // {VOLUME} case SCC_VOLUME: { // {VOLUME}
int32 args[1]; int32 args[1];
assert(_opt_ptr->units < lengthof(units)); assert(_opt_ptr->units < lengthof(units));
args[0] = GetInt32(&argv) * units[_opt_ptr->units].v_m >> units[_opt_ptr->units].v_s; 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; break;
} }
case 13: { // {G 0 Der Die Das} case SCC_GENDER_LIST: { // {G 0 Der Die Das}
const byte* s = (const byte*)GetStringPtr(argv_orig[(byte)*str++]); // contains the string that determines gender. const char* s = GetStringPtr(argv_orig[(byte)*str++]); // contains the string that determines gender.
int len; int len;
int gender = 0; 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); str = ParseStringChoice(str, gender, buff, &len);
buff += len; buff += len;
break; break;
} }
case 14: { // {DATE_TINY} case SCC_DATE_TINY: { // {DATE_TINY}
buff = FormatTinyDate(buff, GetInt32(&argv), last); buff = FormatTinyDate(buff, GetInt32(&argv), last);
break; break;
} }
case 15: { // {CARGO} case SCC_CARGO: { // {CARGO}
// Layout now is: // Layout now is:
// 8bit - cargo type // 8bit - cargo type
// 16-bit - cargo count // 16-bit - cargo count
@ -738,7 +756,7 @@ static char* FormatString(char* buff, const char* str, const int32* argv, uint c
break; break;
} }
case 16: { // {POWER} case SCC_POWER: { // {POWER}
int32 args[1]; int32 args[1];
assert(_opt_ptr->units < lengthof(units)); assert(_opt_ptr->units < lengthof(units));
args[0] = GetInt32(&argv) * units[_opt_ptr->units].p_m >> units[_opt_ptr->units].p_s; 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; break;
} }
case 17: { // {VOLUME_S} case SCC_VOLUME_SHORT: { // {VOLUME_S}
int32 args[1]; int32 args[1];
assert(_opt_ptr->units < lengthof(units)); assert(_opt_ptr->units < lengthof(units));
args[0] = GetInt32(&argv) * units[_opt_ptr->units].v_m >> units[_opt_ptr->units].v_s; 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; break;
} }
case 18: { // {WEIGHT} case SCC_WEIGHT: { // {WEIGHT}
int32 args[1]; int32 args[1];
assert(_opt_ptr->units < lengthof(units)); assert(_opt_ptr->units < lengthof(units));
args[0] = GetInt32(&argv) * units[_opt_ptr->units].w_m >> units[_opt_ptr->units].w_s; 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; break;
} }
case 19: { // {WEIGHT_S} case SCC_WEIGHT_SHORT: { // {WEIGHT_S}
int32 args[1]; int32 args[1];
assert(_opt_ptr->units < lengthof(units)); assert(_opt_ptr->units < lengthof(units));
args[0] = GetInt32(&argv) * units[_opt_ptr->units].w_m >> units[_opt_ptr->units].w_s; 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; break;
} }
case 20: { // {FORCE} case SCC_FORCE: { // {FORCE}
int32 args[1]; int32 args[1];
assert(_opt_ptr->units < lengthof(units)); assert(_opt_ptr->units < lengthof(units));
args[0] = GetInt32(&argv) * units[_opt_ptr->units].f_m >> units[_opt_ptr->units].f_s; 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; break;
} }
default: case SCC_SKIP: // {SKIP}
error("!invalid escape sequence in string"); 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} case SCC_COMMA: // {COMMA}
argv++; buff = FormatCommaNumber(buff, GetInt32(&argv), last);
break; break;
// This sets up the gender for the string. case SCC_ARG_INDEX: // Move argument pointer
// We just ignore this one. It's used in {G 0 Der Die Das} to determine the case. argv = argv_orig + (byte)*str++;
case 0x87: // {GENDER 0} break;
str++;
break;
case 0x88: {// {STRING} case SCC_PLURAL_LIST: { // {P}
uint str = modifier + GetInt32(&argv); int32 v = argv_orig[(byte)*str++]; // contains the number that determines plural
// WARNING. It's prohibited for the included string to consume any arguments. int len;
// For included strings that consume argument, you should use STRING1, STRING2 etc. str = ParseStringChoice(str, DeterminePluralForm(v), buff, &len);
// To debug stuff you can set argv to NULL and it will tell you buff += len;
buff = GetStringWithArgs(buff, str, argv, last); break;
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;
} }
buff = GetStringWithArgs(buff, str, temp, last);
} break;
case 0x9A: { // {STATION} case SCC_NUM: // {NUM}
const Station* st = GetStation(GetInt32(&argv)); buff = FormatNoCommaNumber(buff, GetInt32(&argv), last);
break;
if (!IsValidStation(st)) { // station doesn't exist anymore case SCC_CURRENCY: // {CURRENCY}
buff = GetStringWithArgs(buff, STR_UNKNOWN_DESTINATION, NULL, last); buff = FormatGenericCurrency(buff, _currency, GetInt32(&argv), false, last);
} else { break;
case SCC_WAYPOINT_NAME: { // {WAYPOINT}
int32 temp[2]; int32 temp[2];
temp[0] = st->town->townnametype; Waypoint *wp = GetWaypoint(GetInt32(&argv));
temp[1] = st->town->townnameparts; StringID str;
buff = GetStringWithArgs(buff, st->string_id, temp, last); if (wp->string != STR_NULL) {
} str = wp->string;
break; } else {
} temp[0] = wp->town_index;
case 0x9B: { // {TOWN} temp[1] = wp->town_cn + 1;
const Town* t = GetTown(GetInt32(&argv)); str = wp->town_cn == 0 ? STR_WAYPOINTNAME_CITY : STR_WAYPOINTNAME_CITY_SERIAL;
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> <NUM CASES> <CASE1> <LEN1> <STRING1> <CASE2> <LEN2> <STRING2> <CASE3> <LEN3> <STRING3> <STRINGDEFAULT>
// Each LEN is printed using 2 bytes in big endian order.
uint num = (byte)*str++;
while (num) {
if ((byte)str[0] == casei) {
// Found the case, adjust str pointer and continue
str += 3;
break;
} }
// Otherwise skip to the next case buff = GetStringWithArgs(buff, str, temp, last);
str += 3 + (str[1] << 8) + str[2]; break;
num--;
} }
break;
}
default: case SCC_STATION_NAME: { // {STATION}
if (buff != last) *buff++ = b; const Station* st = GetStation(GetInt32(&argv));
if (!IsValidStation(st)) { // station doesn't exist anymore
buff = GetStringWithArgs(buff, STR_UNKNOWN_DESTINATION, NULL, last);
} else {
int32 temp[2];
temp[0] = st->town->townnametype;
temp[1] = st->town->townnameparts;
buff = GetStringWithArgs(buff, st->string_id, temp, last);
}
break;
}
case SCC_TOWN_NAME: { // {TOWN}
const Town* t = GetTown(GetInt32(&argv));
int32 temp[1];
assert(IsValidTown(t));
temp[0] = t->townnameparts;
buff = GetStringWithArgs(buff, t->townnametype, temp, last);
break;
}
case SCC_CURRENCY_64: { // {CURRENCY64}
buff = FormatGenericCurrency(buff, _currency, GetInt64(&argv), false, last);
break;
}
case SCC_SETCASE: { // {SETCASE}
// This is a pseudo command, it's outputted when someone does {STRING.ack}
// The modifier is added to all subsequent GetStringWithArgs that accept the modifier.
modifier = (byte)*str++ << 24;
break;
}
case SCC_SWITCH_CASE: { // {Used to implement case switching}
// <0x9E> <NUM CASES> <CASE1> <LEN1> <STRING1> <CASE2> <LEN2> <STRING2> <CASE3> <LEN3> <STRING3> <STRINGDEFAULT>
// Each LEN is printed using 2 bytes in big endian order.
uint num = (byte)*str++;
while (num) {
if ((byte)str[0] == casei) {
// Found the case, adjust str pointer and continue
str += 3;
break;
}
// Otherwise skip to the next case
str += 3 + (str[1] << 8) + str[2];
num--;
}
break;
}
default:
if (buff + Utf8CharLen(b) < last) buff += Utf8Encode(buff, b);
break;
} }
} }
*buff = '\0'; *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) static char *StationGetSpecialString(char *buff, int x, const char* last)
{ {
if (x & 0x01) buff = strecpy(buff, "\x94", last); if ((x & 0x01) && (buff + Utf8CharLen(SCC_TRAIN) < last)) buff += Utf8Encode(buff, SCC_TRAIN);
if (x & 0x02) buff = strecpy(buff, "\x95", last); if ((x & 0x02) && (buff + Utf8CharLen(SCC_LORRY) < last)) buff += Utf8Encode(buff, SCC_LORRY);
if (x & 0x04) buff = strecpy(buff, "\x96", last); if ((x & 0x04) && (buff + Utf8CharLen(SCC_BUS) < last)) buff += Utf8Encode(buff, SCC_BUS);
if (x & 0x08) buff = strecpy(buff, "\x97", last); if ((x & 0x08) && (buff + Utf8CharLen(SCC_PLANE) < last)) buff += Utf8Encode(buff, SCC_PLANE);
if (x & 0x10) buff = strecpy(buff, "\x98", last); if ((x & 0x10) && (buff + Utf8CharLen(SCC_SHIP) < last)) buff += Utf8Encode(buff, SCC_SHIP);
*buff = '\0';
return buff; 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); 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); free(lang);
} }
if (lang_pack == NULL) return false; if (lang_pack == NULL) return false;
@ -1237,7 +1254,7 @@ void InitializeLanguagePacks(void)
int fallback; int fallback;
LanguagePack hdr; LanguagePack hdr;
FILE *in; FILE *in;
char *files[32]; char *files[MAX_LANG];
const char* lang; const char* lang;
lang = GetCurrentLocale("LC_MESSAGES"); lang = GetCurrentLocale("LC_MESSAGES");

View File

@ -3,14 +3,7 @@
#ifndef STRINGS_H #ifndef STRINGS_H
#define STRINGS_H #define STRINGS_H
static inline char* InlineString(char* buf, uint16 string) char *InlineString(char *buf, uint16 string);
{
*buf++ = '\x81';
*buf++ = string & 0xFF;
*buf++ = string >> 8;
return buf;
}
char *GetString(char *buffr, uint16 string, const char* last); char *GetString(char *buffr, uint16 string, const char* last);
extern char _userstring[128]; extern char _userstring[128];

105
table/control_codes.h Normal file
View File

@ -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 */

File diff suppressed because it is too large Load Diff

21
table/unicode.h Normal file
View File

@ -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 */
};

2
unix.c
View File

@ -291,7 +291,7 @@ void CSleep(int milliseconds)
#include <errno.h> #include <errno.h>
#include "debug.h" #include "debug.h"
#define INTERNALCODE "ISO-8859-15" #define INTERNALCODE "UTF-8"
/** Try and try to decipher the current locale from environmental /** Try and try to decipher the current locale from environmental
* variables. MacOSX is hardcoded, other OS's are dynamic. If no suitable * variables. MacOSX is hardcoded, other OS's are dynamic. If no suitable

View File

@ -323,12 +323,12 @@ VARDEF char _ini_videodriver[16], _ini_musicdriver[16], _ini_sounddriver[16];
typedef struct { typedef struct {
int num; // number of languages int num; // number of languages
int curr; // currently selected language index int curr; // currently selected language index
char curr_file[32]; // currently selected language file char curr_file[MAX_LANG]; // currently selected language file
StringID dropdown[32 + 1]; // used in settings dialog StringID dropdown[MAX_LANG + 1]; // used in settings dialog
struct { struct {
char *name; char *name;
char *file; char *file;
} ent[32]; } ent[MAX_LANG];
} DynamicLanguages; } DynamicLanguages;
VARDEF DynamicLanguages _dynlang; VARDEF DynamicLanguages _dynlang;

View File

@ -349,14 +349,15 @@ static LRESULT CALLBACK WndProcGdi(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lP
case WM_KEYDOWN: { case WM_KEYDOWN: {
// this is the rewritten ascii input function // this is the rewritten ascii input function
// it disables windows deadkey handling --> more linux like :D // it disables windows deadkey handling --> more linux like :D
WORD w = 0; wchar_t w = 0;
byte ks[256]; byte ks[256];
uint scancode; uint scancode;
uint32 pressed_key; uint32 pressed_key;
GetKeyboardState(ks); GetKeyboardState(ks);
if (ToAscii(wParam, 0, ks, &w, 0) == 0) { if (ToUnicode(wParam, 0, ks, &w, 1, 0) == 0) {
w = 0; // no translation was possible /* 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; pressed_key = w | MapWindowsKey(wParam) << 16;

82
win32.c
View File

@ -926,39 +926,67 @@ void DeterminePaths(void)
*/ */
bool InsertTextBufferClipboard(Textbuf *tb) bool InsertTextBufferClipboard(Textbuf *tb)
{ {
if (IsClipboardFormatAvailable(CF_TEXT)) { HGLOBAL cbuf;
HGLOBAL cbuf; char utf8_buf[512];
const byte *data, *dataptr; const char *ptr;
uint16 width = 0;
uint16 length = 0; WChar c;
uint16 width, length;
if (IsClipboardFormatAvailable(CF_UNICODETEXT)) {
int bytec;
OpenClipboard(NULL); OpenClipboard(NULL);
cbuf = GetClipboardData(CF_TEXT); cbuf = GetClipboardData(CF_UNICODETEXT);
data = GlobalLock(cbuf); // clipboard data
dataptr = data;
for (; IsValidAsciiChar(*dataptr, CS_ALPHANUMERAL) && (tb->length + length) < (tb->maxlength - 1) &&
(tb->maxwidth == 0 || width + tb->width + GetCharacterWidth(FS_NORMAL, (byte)*dataptr) <= tb->maxwidth); dataptr++) {
width += GetCharacterWidth(FS_NORMAL, (byte)*dataptr);
length++;
}
if (length == 0) return false;
memmove(tb->buf + tb->caretpos + length, tb->buf + tb->caretpos, tb->length - tb->caretpos);
memcpy(tb->buf + tb->caretpos, data, length);
tb->width += width;
tb->caretxoffs += width;
tb->length += length;
tb->caretpos += length;
tb->buf[tb->length] = '\0'; // terminating zero
ptr = GlobalLock(cbuf);
bytec = WideCharToMultiByte(CP_UTF8, 0, (wchar_t*)ptr, -1, utf8_buf, lengthof(utf8_buf), NULL, NULL);
GlobalUnlock(cbuf); GlobalUnlock(cbuf);
CloseClipboard(); 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;
} }

View File

@ -1422,7 +1422,7 @@ void HandleKeypress(uint32 key)
// Setup event // Setup event
e.event = WE_KEYPRESS; 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.keycode = GB(key, 16, 16);
e.we.keypress.cont = true; e.we.keypress.cont = true;

View File

@ -160,7 +160,7 @@ struct WindowEvent {
struct { struct {
bool cont; // continue the search? (default true) 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) uint16 keycode;// untranslated key (including shift-state)
} keypress; } keypress;