(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
ifdef WITH_FREETYPE
ifndef FREETYPE_CONFIG
$(error WITH_FREETYPE can't be used when FREETYPE_CONFIG is not set. Edit Makefile.config to correct this)
endif
endif
##############################################################################
#
# Compiler configuration
@ -493,6 +499,15 @@ ifndef MINGW
LIBS += -lc
endif
# freetype config
ifdef WITH_FREETYPE
CDEFS += -DWITH_FREETYPE
CCFLAGS_FREETYPE := $(shell $(FREETYPE_CONFIG) --cflags)
LDFLAGS_FREETYPE := $(shell $(FREETYPE_CONFIG) --libs)
CFLAGS += $(CCFLAGS_FREETYPE)
LIBS += $(LDFLAGS_FREETYPE)
endif
# iconv is enabled defaultly on OSX >= 10.3
ifdef OSX
ifndef JAGUAR
@ -670,6 +685,7 @@ SRCS += engine.c
SRCS += engine_gui.c
SRCS += fileio.c
SRCS += fios.c
SRCS += fontcache.c
SRCS += genworld.c
SRCS += genworld_gui.c
SRCS += gfx.c
@ -884,7 +900,7 @@ $(TTD): $(OBJS) $(MAKE_CONFIG)
$(Q)$(CXX_TARGET) $(LDFLAGS) $(TTDLDFLAGS) $(OBJS) $(LIBS) -o $@
endif
$(STRGEN): strgen/strgen.c string.c endian_host.h
$(STRGEN): strgen/strgen.c string.c endian_host.h table/control_codes.h
@echo '===> Compiling and Linking $@'
$(Q)$(CC_HOST) $(CFLAGS_HOST) -DSTRGEN strgen/strgen.c string.c -o $@

14
configure vendored
View File

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

View File

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

View File

@ -13,14 +13,14 @@
// | | Euro year | | | name
// | | | | | | |
static const CurrencySpec origin_currency_specs[NUM_CURRENCY] = {
{ 1, ',', CF_NOEURO, "\xA3", "", 0, STR_CURR_GBP }, // british pounds
{ 1, ',', CF_NOEURO, "£", "", 0, STR_CURR_GBP }, // british pounds
{ 2, ',', CF_NOEURO, "$", "", 0, STR_CURR_USD }, // us dollars
{ 2, ',', CF_ISEURO, "¤", "", 0, STR_CURR_EUR }, // Euro
{ 220, ',', CF_NOEURO, "\xA5", "", 0, STR_CURR_YEN }, // yen
{ 2, ',', CF_ISEURO, "", "", 0, STR_CURR_EUR }, // Euro
{ 220, ',', CF_NOEURO, "¥", "", 0, STR_CURR_YEN }, // yen
{ 20, ',', 2002, "", " S.", 1, STR_CURR_ATS }, // austrian schilling
{ 59, ',', 2002, "BEF ", "", 0, STR_CURR_BEF }, // belgian franc
{ 2, ',', CF_NOEURO, "CHF ", "", 0, STR_CURR_CHF }, // swiss franc
{ 41, ',', CF_NOEURO, "", " Kc", 1, STR_CURR_CZK }, // czech koruna // TODO: Should use the "c" with an upside down "^"
{ 41, ',', CF_NOEURO, "", " Kč", 1, STR_CURR_CZK }, // czech koruna
{ 3, '.', 2002, "DM ", "", 0, STR_CURR_DEM }, // deutsche mark
{ 11, '.', CF_NOEURO, "", " kr", 1, STR_CURR_DKK }, // danish krone
{ 245, '.', 2002, "Pts ", "", 0, STR_CURR_ESP }, // spanish pesetas

View File

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

View File

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

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 AllocateName(name, skip) RealAllocateName(name, skip, false)
StringID RealAllocateName(const char *name, byte skip, bool check_double);
void ConvertNameArray(void);
/* misc functions */
void MarkTileDirty(int x, int y);

145
gfx.c
View File

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

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

2
gui.h
View File

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

View File

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

View File

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

View File

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

View File

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

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.
void InitializeLandscapeVariables(bool only_constants)
{

View File

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

View File

@ -480,13 +480,15 @@ static byte MakeCzechTownName(char *buf, uint32 seed, const char *last)
strecat(buf, name_czech_adj[prefix].name, last);
endpos = strlen(buf) - 1;
/* Find the first character in a UTF-8 sequence */
while (GB(buf[endpos], 6, 2) == 2) endpos--;
if (gender == CZG_SMASC && pattern == CZP_PRIVL) {
/* -ovX -> -uv */
buf[endpos - 2] = 'u';
assert(buf[endpos - 1] == 'v');
buf[endpos] = '\0';
} else {
buf[endpos] = name_czech_patmod[gender][pattern];
strecpy(buf + endpos, name_czech_patmod[gender][pattern], last);
}
strecat(buf, " ", last);

View File

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

View File

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

View File

@ -18,6 +18,7 @@
#include "macros.h"
#include "table/strings.h"
#include "newgrf_text.h"
#include "table/control_codes.h"
#define GRFTAB 28
#define TABSIZE 11
@ -153,46 +154,104 @@ static GRFTextEntry _grf_text[(1 << TABSIZE) * 3];
static byte _currentLangID = GRFLX_ENGLISH; //by default, english is used.
static void TranslateTTDPatchCodes(char *str)
static char *TranslateTTDPatchCodes(const char *str)
{
char *c;
char *tmp = malloc(strlen(str) * 10); /* Allocate space to allow for expansion */
char *d = tmp;
bool unicode = false;
WChar c = Utf8Consume(&str);
for (c = str; *c != '\0'; c++) {
switch ((byte)*c) {
case 0x01: c++; break;
case 0x0D: *c = 10; break;
case 0x0E: *c = 8; break;
case 0x0F: *c = 9; break;
case 0x1F: *c = 2; c += 2; break;
if (c == 0x00DE) {
/* The thorn ('þ') indicates a unicode string to TTDPatch */
unicode = true;
} else {
str--;
}
for (;;) {
const char *tmp = str; /* Used for UTF-8 decoding */
c = (byte)*str++;
if (c == 0) break;
switch (c) {
case 0x01:
d += Utf8Encode(d, SCC_SETX);
*d++ = *str++;
break;
case 0x0D: *d++ = 10; break;
case 0x0E: d += Utf8Encode(d, SCC_TINYFONT); break;
case 0x0F: d += Utf8Encode(d, SCC_BIGFONT); break;
case 0x1F:
d += Utf8Encode(d, SCC_SETXY);
*d++ = *str++;
*d++ = *str++;
break;
case 0x7B:
case 0x7C:
case 0x7D:
case 0x7E: *c = 0x8E; break;
case 0x81: c += 2; break;
case 0x85: *c = 0x86; break;
case 0x88: *c = 15; break;
case 0x89: *c = 16; break;
case 0x8A: *c = 17; break;
case 0x8B: *c = 18; break;
case 0x8C: *c = 19; break;
case 0x8D: *c = 20; break;
case 0x8E: *c = 21; break;
case 0x8F: *c = 22; break;
case 0x90: *c = 23; break;
case 0x91: *c = 24; break;
case 0x92: *c = 25; break;
case 0x93: *c = 26; break;
case 0x94: *c = 27; break;
case 0x95: *c = 28; break;
case 0x96: *c = 29; break;
case 0x97: *c = 30; break;
case 0x98: *c = 31; break;
case 0x7E: d += Utf8Encode(d, SCC_NUM); break;
case 0x7F: d += Utf8Encode(d, SCC_CURRENCY); break;
case 0x80: d += Utf8Encode(d, SCC_STRING); break;
case 0x81: {
StringID string;
string = *str++;
string |= *str++ << 8;
d += Utf8Encode(d, SCC_STRING_ID);
d += Utf8Encode(d, string);
break;
}
case 0x82: d += Utf8Encode(d, SCC_DATE_TINY); break;
case 0x83: d += Utf8Encode(d, SCC_DATE_SHORT); break;
case 0x84: d += Utf8Encode(d, SCC_VELOCITY); break;
case 0x85: d += Utf8Encode(d, SCC_SKIP); break;
case 0x86: /* "Rotate down top 4 words on stack" */ break;
case 0x87: d += Utf8Encode(d, SCC_VOLUME); break;
case 0x88: d += Utf8Encode(d, SCC_BLUE); break;
case 0x89: d += Utf8Encode(d, SCC_SILVER); break;
case 0x8A: d += Utf8Encode(d, SCC_GOLD); break;
case 0x8B: d += Utf8Encode(d, SCC_RED); break;
case 0x8C: d += Utf8Encode(d, SCC_PURPLE); break;
case 0x8D: d += Utf8Encode(d, SCC_LTBROWN); break;
case 0x8E: d += Utf8Encode(d, SCC_ORANGE); break;
case 0x8F: d += Utf8Encode(d, SCC_GREEN); break;
case 0x90: d += Utf8Encode(d, SCC_YELLOW); break;
case 0x91: d += Utf8Encode(d, SCC_DKGREEN); break;
case 0x92: d += Utf8Encode(d, SCC_CREAM); break;
case 0x93: d += Utf8Encode(d, SCC_BROWN); break;
case 0x94: d += Utf8Encode(d, SCC_WHITE); break;
case 0x95: d += Utf8Encode(d, SCC_LTBLUE); break;
case 0x96: d += Utf8Encode(d, SCC_GRAY); break;
case 0x97: d += Utf8Encode(d, SCC_DKBLUE); break;
case 0x98: d += Utf8Encode(d, SCC_BLACK); break;
case 0x9E: d += Utf8Encode(d, 0x20AC); break; // Euro
case 0x9F: d += Utf8Encode(d, 0x0178); break; // Y with diaeresis
case 0xA0: d += Utf8Encode(d, SCC_UPARROW); break;
case 0xAA: d += Utf8Encode(d, SCC_DOWNARROW); break;
case 0xAC: d += Utf8Encode(d, SCC_CHECKMARK); break;
case 0xAD: d += Utf8Encode(d, SCC_CROSS); break;
case 0xAF: d += Utf8Encode(d, SCC_RIGHTARROW); break;
case 0xB4: d += Utf8Encode(d, SCC_TRAIN); break;
case 0xB5: d += Utf8Encode(d, SCC_LORRY); break;
case 0xB6: d += Utf8Encode(d, SCC_BUS); break;
case 0xB7: d += Utf8Encode(d, SCC_PLANE); break;
case 0xB8: d += Utf8Encode(d, SCC_SHIP); break;
default:
if (unicode) {
d += Utf8Encode(d, Utf8Consume(&tmp));
str = tmp;
break;
}
/* Validate any unhandled character */
if (!IsValidAsciiChar(*c, CS_ALPHANUMERAL)) *c = '?';
if (!IsValidChar(c, CS_ALPHANUMERAL)) c = '?';
d += Utf8Encode(d, c);
break;
}
}
*d = '\0';
return realloc(tmp, strlen(tmp) + 1);
}
@ -201,6 +260,7 @@ static void TranslateTTDPatchCodes(char *str)
*/
StringID AddGRFString(uint32 grfid, uint16 stringid, byte langid_to_add, bool new_scheme, const char *text_to_add, StringID def_string)
{
char *translatedtext;
GRFText *newtext;
uint id;
@ -231,12 +291,14 @@ StringID AddGRFString(uint32 grfid, uint16 stringid, byte langid_to_add, bool ne
/* Too many strings allocated, return empty */
if (id == lengthof(_grf_text)) return STR_EMPTY;
newtext = malloc(sizeof(*newtext) + strlen(text_to_add) + 1);
translatedtext = TranslateTTDPatchCodes(text_to_add);
newtext = malloc(sizeof(*newtext) + strlen(translatedtext) + 1);
newtext->next = NULL;
newtext->langid = langid_to_add;
strcpy(newtext->text, text_to_add);
strcpy(newtext->text, translatedtext);
TranslateTTDPatchCodes(newtext->text);
free(translatedtext);
/* If we didn't find our stringid and grfid in the list, allocate a new id */
if (id == _num_grf_texts) _num_grf_texts++;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -38,6 +38,10 @@
#include "newgrf.h"
#include "genworld.h"
#include "date.h"
#ifdef WITH_FREETYPE
#include "gfx.h"
#include "fontcache.h"
#endif
/** The patch values that are used for new games and/or modified in config file */
Patches _patches_newgame;
@ -1186,6 +1190,14 @@ static const SettingDescGlobVarList _misc_settings[] = {
SDTG_STR("screenshot_format",SLE_STRB, S, 0, _screenshot_format_name,NULL, STR_NULL, NULL),
SDTG_STR("savegame_format", SLE_STRB, S, 0, _savegame_format, NULL, STR_NULL, NULL),
SDTG_BOOL("rightclick_emulate", S, 0, _rightclick_emulate, false, STR_NULL, NULL),
#ifdef WITH_FREETYPE
SDTG_STR("small_font", SLE_STRB, S, 0, _freetype.small_font, NULL, STR_NULL, NULL),
SDTG_STR("medium_font", SLE_STRB, S, 0, _freetype.medium_font, NULL, STR_NULL, NULL),
SDTG_STR("large_font", SLE_STRB, S, 0, _freetype.large_font, NULL, STR_NULL, NULL),
SDTG_VAR("small_size", SLE_UINT, S, 0, _freetype.small_size, 6, 0, 72, 0, STR_NULL, NULL),
SDTG_VAR("medium_size", SLE_UINT, S, 0, _freetype.medium_size, 10, 0, 72, 0, STR_NULL, NULL),
SDTG_VAR("large_size", SLE_UINT, S, 0, _freetype.large_size, 16, 0, 72, 0, STR_NULL, NULL),
#endif
SDTG_END()
};

View File

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

124
string.c
View File

@ -4,6 +4,8 @@
#include "openttd.h"
#include "functions.h"
#include "string.h"
#include "macros.h"
#include "table/control_codes.h"
#include <stdarg.h>
#include <ctype.h> // required for tolower()
@ -68,8 +70,27 @@ char* CDECL str_fmt(const char* str, ...)
void str_validate(char *str)
{
for (; *str != '\0'; str++)
if (!IsValidAsciiChar(*str, CS_ALPHANUMERAL)) *str = '?';
char *dst = str;
WChar c;
size_t len = Utf8Decode(&c, str);
for (; c != '\0'; len = Utf8Decode(&c, str)) {
if (IsPrintable(c) && (c < SCC_SPRITE_START || c > SCC_SPRITE_END ||
IsValidChar(c - SCC_SPRITE_START, CS_ALPHANUMERAL))) {
/* Copy the character back. Even if dst is current the same as str
* (i.e. no characters have been changed) this is quicker than
* moving the pointers ahead by len */
do {
*dst++ = *str++;
} while (--len);
} else {
/* Replace the undesirable character with a question mark */
str += len;
*dst++ = '?';
}
}
*dst = '\0';
}
void str_strip_colours(char *str)
@ -92,29 +113,15 @@ void str_strip_colours(char *str)
* @param afilter the filter to use
* @return true or false depending if the character is printable/valid or not
*/
bool IsValidAsciiChar(byte key, CharSetFilter afilter)
bool IsValidChar(WChar key, CharSetFilter afilter)
{
bool firsttest = false;
switch (afilter) {
case CS_ALPHANUMERAL:
firsttest = (key >= ' ' && key < 127);
break;
/* We are very strict here */
case CS_NUMERAL:
return (key >= '0' && key <= '9');
case CS_ALPHA:
default:
firsttest = ((key >= 'A' && key <= 'Z') || (key >= 'a' && key <= 'z'));
break;
case CS_ALPHANUMERAL: return IsPrintable(key);
case CS_NUMERAL: return (key >= '0' && key <= '9');
case CS_ALPHA: return IsPrintable(key) && !(key >= '0' && key <= '9');
}
/* Allow some special chars too that are non-ASCII but still valid (like '^' above 'a') */
return (firsttest || (key >= 160 &&
key != 0xAA && key != 0xAC && key != 0xAD && key != 0xAF &&
key != 0xB5 && key != 0xB6 && key != 0xB7 && key != 0xB9));
return false;
}
void strtolower(char *str)
@ -145,3 +152,78 @@ int CDECL vsnprintf(char *str, size_t size, const char *format, va_list ap)
#endif /* _MSC_VER */
#endif /* WIN32 */
/* UTF-8 handling routines */
/* Decode and consume the next UTF-8 encoded character
* @param c Buffer to place decoded character.
* @param s Character stream to retrieve character from.
* @return Number of characters in the sequence.
*/
size_t Utf8Decode(WChar *c, const char *s)
{
assert(c != NULL);
if (!HASBIT(s[0], 7)) {
/* Single byte character: 0xxxxxxx */
*c = s[0];
return 1;
} else if (GB(s[0], 5, 3) == 6) {
if (IsUtf8Part(s[1])) {
/* Double byte character: 110xxxxx 10xxxxxx */
*c = GB(s[0], 0, 5) << 6 | GB(s[1], 0, 6);
if (*c >= 0x80) return 2;
}
} else if (GB(s[0], 4, 4) == 14) {
if (IsUtf8Part(s[1]) && IsUtf8Part(s[2])) {
/* Triple byte character: 1110xxxx 10xxxxxx 10xxxxxx */
*c = GB(s[0], 0, 4) << 12 | GB(s[1], 0, 6) << 6 | GB(s[2], 0, 6);
if (*c >= 0x800) return 3;
}
} else if (GB(s[0], 3, 5) == 30) {
if (IsUtf8Part(s[1]) && IsUtf8Part(s[2]) && IsUtf8Part(s[3])) {
/* 4 byte character: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
*c = GB(s[0], 0, 3) << 18 | GB(s[1], 0, 6) << 12 | GB(s[2], 0, 6) << 6 | GB(s[3], 0, 6);
if (*c >= 0x10000 && *c <= 0x10FFFF) return 4;
}
}
//DEBUG(misc, 1) ("Invalid UTF-8 sequence");
*c = '?';
return 1;
}
/* Encode a unicode character and place it in the buffer
* @param buf Buffer to place character.
* @param c Unicode character to encode.
* @return Number of characters in the encoded sequence.
*/
size_t Utf8Encode(char *buf, WChar c)
{
if (c < 0x80) {
*buf = c;
return 1;
} else if (c < 0x800) {
*buf++ = 0xC0 + GB(c, 6, 5);
*buf = 0x80 + GB(c, 0, 6);
return 2;
} else if (c < 0x10000) {
*buf++ = 0xE0 + GB(c, 12, 4);
*buf++ = 0x80 + GB(c, 6, 6);
*buf = 0x80 + GB(c, 0, 6);
return 3;
} else if (c < 0x110000) {
*buf++ = 0xF0 + GB(c, 18, 3);
*buf++ = 0x80 + GB(c, 12, 6);
*buf++ = 0x80 + GB(c, 6, 6);
*buf = 0x80 + GB(c, 0, 6);
return 4;
}
//DEBUG(misc, 1) ("Can't UTF-8 encode value 0x%X", c);
*buf = '?';
return 1;
}

View File

@ -3,6 +3,8 @@
#ifndef STRING_H
#define STRING_H
#include "macros.h"
/*
* dst: destination buffer
* src: string to copy/concatenate
@ -33,7 +35,7 @@ void str_validate(char *str);
void str_strip_colours(char *str);
/**
* Valid filter types for IsValidAsciiChar.
* Valid filter types for IsValidChar.
*/
typedef enum CharSetFilter {
CS_ALPHANUMERAL, //! Both numeric and alphabetic and spaces and stuff
@ -41,6 +43,11 @@ typedef enum CharSetFilter {
CS_ALPHA, //! Only alphabetic values
} CharSetFilter;
/** Convert the given string to lowercase */
void strtolower(char *str);
typedef uint32 WChar;
/**
* Only allow certain keys. You can define the filter to be used. This makes
* sure no invalid keys can get into an editbox, like BELL.
@ -48,9 +55,50 @@ typedef enum CharSetFilter {
* @param afilter the filter to use
* @return true or false depending if the character is printable/valid or not
*/
bool IsValidAsciiChar(byte key, CharSetFilter afilter);
bool IsValidChar(WChar key, CharSetFilter afilter);
size_t Utf8Decode(WChar *c, const char *s);
size_t Utf8Encode(char *buf, WChar c);
static inline WChar Utf8Consume(const char **s)
{
WChar c;
*s += Utf8Decode(&c, *s);
return c;
}
/** Return the length of a UTF-8 encoded character.
* @param c Unicode character.
* @return Length of UTF-8 encoding for character.
*/
static inline size_t Utf8CharLen(WChar c)
{
if (c < 0x80) return 1;
if (c < 0x800) return 2;
if (c < 0x10000) return 3;
if (c < 0x110000) return 4;
/* Invalid valid, we encode as a '?' */
return 1;
}
/* Check if the given character is part of a UTF8 sequence */
static inline bool IsUtf8Part(char c)
{
return GB(c, 6, 2) == 2;
}
static inline bool IsPrintable(WChar c)
{
if (c < 0x20) return false;
if (c < 0xE000) return true;
if (c < 0xE200) return false;
return true;
}
/** Convert the given string to lowercase */
void strtolower(char *str);
#endif /* STRING_H */

361
strings.c
View File

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

View File

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

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 "debug.h"
#define INTERNALCODE "ISO-8859-15"
#define INTERNALCODE "UTF-8"
/** Try and try to decipher the current locale from environmental
* variables. MacOSX is hardcoded, other OS's are dynamic. If no suitable

View File

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

View File

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

82
win32.c
View File

@ -926,39 +926,67 @@ void DeterminePaths(void)
*/
bool InsertTextBufferClipboard(Textbuf *tb)
{
if (IsClipboardFormatAvailable(CF_TEXT)) {
HGLOBAL cbuf;
const byte *data, *dataptr;
uint16 width = 0;
uint16 length = 0;
HGLOBAL cbuf;
char utf8_buf[512];
const char *ptr;
WChar c;
uint16 width, length;
if (IsClipboardFormatAvailable(CF_UNICODETEXT)) {
int bytec;
OpenClipboard(NULL);
cbuf = GetClipboardData(CF_TEXT);
data = GlobalLock(cbuf); // clipboard data
dataptr = data;
for (; IsValidAsciiChar(*dataptr, CS_ALPHANUMERAL) && (tb->length + length) < (tb->maxlength - 1) &&
(tb->maxwidth == 0 || width + tb->width + GetCharacterWidth(FS_NORMAL, (byte)*dataptr) <= tb->maxwidth); dataptr++) {
width += GetCharacterWidth(FS_NORMAL, (byte)*dataptr);
length++;
}
if (length == 0) return false;
memmove(tb->buf + tb->caretpos + length, tb->buf + tb->caretpos, tb->length - tb->caretpos);
memcpy(tb->buf + tb->caretpos, data, length);
tb->width += width;
tb->caretxoffs += width;
tb->length += length;
tb->caretpos += length;
tb->buf[tb->length] = '\0'; // terminating zero
cbuf = GetClipboardData(CF_UNICODETEXT);
ptr = GlobalLock(cbuf);
bytec = WideCharToMultiByte(CP_UTF8, 0, (wchar_t*)ptr, -1, utf8_buf, lengthof(utf8_buf), NULL, NULL);
GlobalUnlock(cbuf);
CloseClipboard();
return true;
if (bytec == 0) {
DEBUG(misc, 0) ("[utf8] Error converting '%s'. Errno %d", ptr, GetLastError());
return false;
}
} else if (IsClipboardFormatAvailable(CF_TEXT)) {
OpenClipboard(NULL);
cbuf = GetClipboardData(CF_TEXT);
ptr = GlobalLock(cbuf);
ttd_strlcpy(utf8_buf, ptr, lengthof(utf8_buf));
GlobalUnlock(cbuf);
CloseClipboard();
} else {
return false;
}
return false;
width = length = 0;
for (ptr = utf8_buf; (c = Utf8Consume(&ptr)) != '\0';) {
byte charwidth;
if (!IsPrintable(c)) break;
if (tb->length + length >= tb->maxlength - 1) break;
charwidth = GetCharacterWidth(FS_NORMAL, c);
if (tb->maxwidth != 0 && width + tb->width + charwidth > tb->maxwidth) break;
width += charwidth;
length += Utf8CharLen(c);
}
if (length == 0) return false;
memmove(tb->buf + tb->caretpos + length, tb->buf + tb->caretpos, tb->length - tb->caretpos);
memcpy(tb->buf + tb->caretpos, utf8_buf, length);
tb->width += width;
tb->caretxoffs += width;
tb->length += length;
tb->caretpos += length;
tb->buf[tb->length] = '\0'; // terminating zero
return true;
}

View File

@ -1422,7 +1422,7 @@ void HandleKeypress(uint32 key)
// Setup event
e.event = WE_KEYPRESS;
e.we.keypress.ascii = GB(key, 0, 8);
e.we.keypress.key = GB(key, 0, 16);
e.we.keypress.keycode = GB(key, 16, 16);
e.we.keypress.cont = true;

View File

@ -160,7 +160,7 @@ struct WindowEvent {
struct {
bool cont; // continue the search? (default true)
byte ascii; // 8-bit ASCII-value of the key
uint16 key; // 16-bit Unicode value of the key
uint16 keycode;// untranslated key (including shift-state)
} keypress;