Merged branch utf8.
authorparasti <parasti@78b8d119-cf0a-0410-b17c-f493084dd1d7>
Mon, 3 Dec 2007 00:37:16 +0000 (00:37 +0000)
committerparasti <parasti@78b8d119-cf0a-0410-b17c-f493084dd1d7>
Mon, 3 Dec 2007 00:37:16 +0000 (00:37 +0000)
Some changes:

 * In-game text editor handles non-Latin keyboard layouts and
   multi-byte characters. (Closes tickets #104 and #89.)
 * The localised date from strftime is properly converted to UTF-8
   before display. (Closes #98.)
 * File names are appropriately converted between the system's
   encoding and UTF-8.
 * Error messages are converted to the system's encoding before being
   sent off to standard streams. The new gettext-style macro L_
   provides a way to retrieve translations in the locale's encoding.
   (Closes #93.)
 * Updated DejaVu font.

All this shiny stuff comes with a new dependency: libiconv.  To build
the game without it, pass ENABLE_NLS=0 to make.

git-svn-id: https://s.snth.net/svn/neverball/trunk@1228 78b8d119-cf0a-0410-b17c-f493084dd1d7

21 files changed:
Makefile
ball/demo.c
ball/level.c
ball/main.c
ball/set.c
ball/st_name.c
ball/st_save.c
data/ttf/DejaVuSans-Bold.ttf
doc/AUTHORS
po/Makefile
putt/main.c
share/audio.c
share/binary.c
share/binary.h
share/gui.c
share/lang.c
share/lang.h
share/text.c [new file with mode: 0644]
share/text.h [new file with mode: 0644]
tools/README
tools/src/democonv.c

index 12e67e9..a485df9 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -33,7 +33,7 @@ PNG_LIBS := $(shell libpng-config --libs)
 
 ifdef MINGW
 ifneq ($(ENABLE_NLS),0)
-    INTL_LIBS := -lintl
+    INTL_LIBS := -lintl -liconv
 endif
     OGL_LIBS  := -lopengl32 -lm
     BASE_LIBS := -lSDL -lSDL_image $(INTL_LIBS)
@@ -84,6 +84,7 @@ BALL_OBJS := \
        share/binary.o      \
        share/state.o       \
        share/audio.o       \
+       share/text.o        \
        ball/hud.o          \
        ball/mode.o         \
        ball/game.o         \
@@ -128,6 +129,7 @@ PUTT_OBJS := \
        share/audio.o       \
        share/state.o       \
        share/gui.o         \
+       share/text.o        \
        putt/hud.o          \
        putt/game.o         \
        putt/hole.o         \
index 3f32db5..9368943 100644 (file)
 #include "solid.h"
 #include "config.h"
 #include "binary.h"
+#include "text.h"
 
 /*---------------------------------------------------------------------------*/
 
 #define MAGIC           0x52424EAF
-#define DEMO_VERSION    3
+#define DEMO_VERSION    4
 
 #define DATELEN 20
 
@@ -107,9 +108,9 @@ static int demo_header_read(FILE *fp, struct demo *d)
         get_index(fp, &d->status);
         get_index(fp, &d->mode);
 
-        fread(d->player, 1, MAXNAM, fp);
+        get_string(fp, d->player, MAXNAM);
 
-        fread(datestr, 1, DATELEN, fp);
+        get_string(fp, datestr, DATELEN);
         sscanf(datestr,
                "%d-%d-%dT%d:%d:%d",
                &date.tm_year,
@@ -126,8 +127,8 @@ static int demo_header_read(FILE *fp, struct demo *d)
 
         d->date = make_time_from_utc(&date);
 
-        fread(d->shot, 1, PATHMAX, fp);
-        fread(d->file, 1, PATHMAX, fp);
+        get_string(fp, d->shot, PATHMAX);
+        get_string(fp, d->file, PATHMAX);
 
         get_index(fp, &d->time);
         get_index(fp, &d->goal);
@@ -188,11 +189,11 @@ static void demo_header_write(FILE *fp, struct demo *d)
     put_index(fp, &zero);
     put_index(fp, &d->mode);
 
-    fwrite(d->player, 1, MAXNAM, fp);
-    fwrite(datestr, 1, DATELEN, fp);
+    put_string(fp, d->player);
+    put_string(fp, datestr);
 
-    fwrite(d->shot, 1, PATHMAX, fp);
-    fwrite(d->file, 1, PATHMAX, fp);
+    put_string(fp, d->shot);
+    put_string(fp, d->file);
 
     put_index(fp, &d->time);
     put_index(fp, &d->goal);
@@ -214,8 +215,9 @@ static void demo_scan_file(const char *filename)
     {
         if (demo_header_read(fp, d))
         {
-            strncpy(d->filename, config_user(filename),       MAXSTR);
-            strncpy(d->name,     bname(filename, REPLAY_EXT), PATHMAX);
+            strncpy(d->filename, config_user(filename), MAXSTR);
+            strncpy(d->name, bname(text_from_locale(d->filename), REPLAY_EXT),
+                    PATHMAX);
             d->name[PATHMAX - 1] = '\0';
 
             count++;
@@ -284,17 +286,17 @@ const struct demo *demo_get(int i)
 const char *date_to_str(time_t i)
 {
     static char str[MAXSTR];
+    const char *fmt;
 
     /* TRANSLATORS:  here is the format of the date shown at the
-       replay selection screen.  The default will work in most cases, so
-       you should only change it if something's horribly wrong, like,
-       for instance, the GUI layout is broken.  See strftime(3) for
+       replay selection screen (and possibly elsewhere).  The default
+       format is necessarily locale-independent.  See strftime(3) for
        details on the format.
      */
 
-    strftime(str, MAXSTR, /* xgettext:no-c-format */ _("%c"), localtime(&i));
-
-    return str;
+    fmt = /* xgettext:no-c-format */ L_("%Y-%m-%d %H:%M:%S");
+    strftime(str, MAXSTR, fmt, localtime(&i));
+    return text_from_locale(str);
 }
 
 /*---------------------------------------------------------------------------*/
@@ -460,8 +462,9 @@ int demo_replay_init(const char *name, struct level_game *lg)
 
     if (demo_fp && demo_header_read(demo_fp, &demo_replay))
     {
-        strncpy(demo_replay.filename, name,                    MAXSTR);
-        strncpy(demo_replay.name,     bname(name, REPLAY_EXT), PATHMAX);
+        strncpy(demo_replay.filename, name, MAXSTR);
+        strncpy(demo_replay.name, bname(text_from_locale(demo_replay.filename),
+                REPLAY_EXT), PATHMAX);
 
         if (!demo_load_level(&demo_replay, &demo_level_replay))
             return 0;
index e9a76b2..983cde2 100644 (file)
@@ -20,6 +20,7 @@
 
 #include "config.h"
 #include "demo.h"
+#include "text.h"
 #include "level.h"
 #include "mode.h"
 #include "set.h"
@@ -88,14 +89,21 @@ int level_load(const char *filename, struct level *level)
     memset(level, 0, sizeof (struct level));
     memset(&sol,  0, sizeof (sol));
 
+#define format \
+    L_("Error while loading level file '%s': %s\n")
+#define default_error \
+    L_("Not a valid level file")
+
     if (!sol_load_only_head(&sol, config_data(filename)))
     {
-        fprintf(stderr,
-                _("Error while loading level file '%s': %s\n"), filename,
-                errno ? strerror(errno) : _("Not a valid level file"));
+        const char *error = errno ? strerror(errno) : default_error;
+        fprintf(stderr, format, filename, error);
         return 0;
     }
 
+#undef format
+#undef default_error
+
     strcpy(level->file, filename);
 
     /* Init hs with default values */
index ac54d56..ba4845f 100644 (file)
@@ -29,6 +29,7 @@
 #include "game.h"
 #include "gui.h"
 #include "set.h"
+#include "text.h"
 
 #include "st_conf.h"
 #include "st_title.h"
@@ -216,24 +217,25 @@ static char *demo_path = NULL;
 static unsigned int display_info = 0;
 static unsigned int replay_demo  = 0;
 
+#define usage \
+    L_(                                                                   \
+        "Usage: %s [options ...]\n"                                       \
+        "Options:\n"                                                      \
+        "  -h, --help                show this usage message.\n"          \
+        "  -v, --version             show version.\n"                     \
+        "  -d, --data <dir>          use 'dir' as game data directory.\n" \
+        "  -r, --replay <file>       play the replay 'file'.\n"           \
+        "  -i, --info                display info about a replay.\n"      \
+    )
+
 #define argument_error(option) { \
-    fprintf(stderr, _("Option '%s' requires an argument.\n"), option); \
+    fprintf(stderr, L_("Option '%s' requires an argument.\n"),  option); \
 }
 
 static void parse_args(int argc, char **argv)
 {
     int i;
 
-    const char *usage = _(
-        "Usage: %s [options ...]\n"
-        "Options:\n"
-        "  -h, --help                show this usage message.\n"
-        "  -v, --version             show version.\n"
-        "  -d, --data <dir>          use 'dir' as game data directory.\n"
-        "  -r, --replay <file>       play the replay 'file'.\n"
-        "  -i, --info                display info about a replay.\n"
-    );
-
     /* Scan argument list. */
 
     for (i = 1; i < argc; i++)
@@ -287,11 +289,12 @@ static void parse_args(int argc, char **argv)
         if (display_info)
         {
             /* FIXME, I'm a required option. */
-            fputs(_("Option '--info' requires '--replay'.\n"), stderr);
+            fputs(L_("Option '--info' requires '--replay'.\n"), stderr);
             exit(EXIT_FAILURE);
         }
 }
 
+#undef usage
 #undef argument_error
 
 /*---------------------------------------------------------------------------*/
@@ -305,17 +308,19 @@ int main(int argc, char *argv[])
 
     lang_init("neverball", CONFIG_LOCALE);
 
+    text_init();
+
     parse_args(argc, argv);
 
     if (!config_data_path(data_path, SET_FILE))
     {
-        fprintf(stderr, _("Failure to establish game data directory\n"));
+        fputs(L_("Failure to establish game data directory\n"), stderr);
         return 1;
     }
 
     if (!config_user_path(NULL))
     {
-        fprintf(stderr, _("Failure to establish config directory\n"));
+        fputs(L_("Failure to establish config directory\n"), stderr);
         return 1;
     }
 
@@ -338,8 +343,8 @@ int main(int argc, char *argv[])
     {
         if (!level_replay(demo_path))
         {
-            fprintf(stderr, _("Replay file '%s': %s\n"), demo_path,
-                    errno ? strerror(errno) : _("Not a replay file"));
+            fprintf(stderr, L_("Replay file '%s': %s\n"), demo_path,
+                    errno ?  strerror(errno) : L_("Not a replay file"));
             return 1;
         }
         demo_replay_dump_info();
@@ -450,6 +455,8 @@ int main(int argc, char *argv[])
 
     config_save();
 
+    text_quit();
+
     return 0;
 }
 
index 89c558e..40e1427 100644 (file)
@@ -20,6 +20,7 @@
 #include "glext.h"
 #include "config.h"
 #include "image.h"
+#include "text.h"
 #include "set.h"
 #include "game.h"
 
@@ -152,8 +153,8 @@ static void set_load_hs(void)
     if (!res && errno != ENOENT)
     {
         fprintf(stderr,
-                _("Error while loading user high-score file '%s': %s\n"),
-                fn, errno ? strerror(errno) : _("Incorrect format"));
+                L_("Error while loading user high-score file '%s': %s\n"),
+                fn, errno ? strerror(errno) : L_("Incorrect format"));
     }
 }
 
@@ -177,7 +178,7 @@ static int set_load(struct set *s, const char *filename)
 
     if (!fin)
     {
-        fprintf(stderr, _("Cannot load the set file '%s': %s\n"),
+        fprintf(stderr, L_("Cannot load the set file '%s': %s\n"),
                 filename, strerror(errno));
         return 0;
     }
index 828b170..7b57d73 100644 (file)
@@ -20,6 +20,7 @@
 #include "audio.h"
 #include "config.h"
 #include "game.h"
+#include "text.h"
 
 #include "st_name.h"
 #include "st_shared.h"
@@ -51,14 +52,12 @@ static int name_id;
 
 static int name_action(int i)
 {
-    size_t l = strlen(player);
-
     audio_play(AUD_MENU, 1.0f);
 
     switch (i)
     {
     case NAME_OK:
-        if (l == 0)
+        if (strlen(player) == 0)
            return 1;
 
         config_set_s(CONFIG_PLAYER, player);
@@ -73,21 +72,13 @@ static int name_action(int i)
         break;
 
     case GUI_BS:
-        if (l > 0)
-        {
-            player[l - 1] = '\0';
+        if (text_del_char(player))
             gui_set_label(name_id, player);
-        }
         break;
 
     default:
-        if (l < MAXNAM - 1)
-        {
-            player[l + 0] = (char) i;
-            player[l + 1] = '\0';
-
+        if (text_add_char(i, player, MAXNAM, 17))
             gui_set_label(name_id, player);
-        }
     }
     return 1;
 }
@@ -131,7 +122,7 @@ static void name_leave(int id)
 
 static int name_keybd(int c, int d)
 {
-    if (d && (c & 0xFF80) == 0)
+    if (d)
     {
         gui_focus(enter_id);
 
index 33aa272..9ea2efd 100644 (file)
@@ -22,6 +22,7 @@
 #include "config.h"
 #include "demo.h"
 #include "levels.h"
+#include "text.h"
 
 #include "st_shared.h"
 #include "st_save.h"
@@ -55,21 +56,23 @@ static int file_id;
 
 static int save_action(int i)
 {
-    size_t l = strlen(filename);
+    char *n;
 
     audio_play(AUD_MENU, 1.0f);
 
     switch (i)
     {
     case SAVE_SAVE:
-        if (strlen(filename) == 0)
+        n = text_to_locale(filename);
+
+        if (strlen(n) == 0)
             return 1;
 
-        if (demo_exists(filename))
+        if (demo_exists(n))
             return goto_state(&st_clobber);
         else
         {
-            demo_rename(filename);
+            demo_rename(n);
             return goto_state(ok_state);
         }
 
@@ -81,20 +84,13 @@ static int save_action(int i)
         break;
 
     case GUI_BS:
-        if (l > 0)
-        {
-            filename[l - 1] = 0;
+        if (text_del_char(filename))
             gui_set_label(file_id, filename);
-        }
         break;
 
     default:
-        if (l < MAXNAM - 1)
-        {
-            filename[l + 0] = (char) i;
-            filename[l + 1] = 0;
+        if (text_add_char(i, filename, MAXNAM, 17))
             gui_set_label(file_id, filename);
-        }
     }
     return 1;
 }
@@ -139,7 +135,7 @@ static void save_leave(int id)
 
 static int save_keybd(int c, int d)
 {
-    if (d && (c & 0xFF80) == 0)
+    if (d)
     {
         gui_focus(enter_id);
 
@@ -177,7 +173,7 @@ static int clobber_action(int i)
 
     if (i == SAVE_SAVE)
     {
-        demo_rename(filename);
+        demo_rename(text_to_locale(filename));
         return goto_state(ok_state);
     }
     return goto_state(&st_save);
index ee395ac..77d1c39 100644 (file)
Binary files a/data/ttf/DejaVuSans-Bold.ttf and b/data/ttf/DejaVuSans-Bold.ttf differ
index 555d85c..93b597f 100644 (file)
@@ -63,6 +63,7 @@
       - Menu navigation improvements
       - Neverputt joystick and keyboard support
       - New pause screen
+      - I18n improvements
       - Various bug fixes
     Jeremy Messenger
       - FreeBSD port
index 9f1434d..40cffbf 100644 (file)
@@ -29,7 +29,8 @@ BUGADDR   := robert.kooima@gmail.com
 XGETTEXT := xgettext
 XGETTEXT_FLAGS := \
     --add-comments=TRANSLATORS --from-code=UTF-8 \
-    --keyword=_ --keyword=N_ --keyword=sgettext  \
+    --keyword=_ --keyword=N_ --keyword=L_        \
+    --keyword=sgettext                           \
     --default-domain="$(DOMAIN)"                 \
     --copyright-holder="$(COPYRIGHT)"            \
     --msgid-bugs-address="$(BUGADDR)"
index c14965d..bc7c48b 100644 (file)
@@ -33,6 +33,7 @@
 #include "hole.h"
 #include "game.h"
 #include "gui.h"
+#include "text.h"
 
 #include "st_conf.h"
 #include "st_all.h"
@@ -307,9 +308,9 @@ int main(int argc, char *argv[])
             }
             else fprintf(stderr, "%s: %s\n", argv[0], SDL_GetError());
         }
-        else fprintf(stderr, _("Failure to establish config directory\n"));
+        else fprintf(stderr, L_("Failure to establish config directory\n"));
     }
-    else fprintf(stderr, _("Failure to establish game data directory\n"));
+    else fprintf(stderr, L_("Failure to establish game data directory\n"));
 
     return 0;
 }
index 15761fa..640f2cf 100644 (file)
@@ -16,6 +16,7 @@
 #include <SDL_mixer.h>
 #include <string.h>
 
+#include "text.h"
 #include "config.h"
 #include "audio.h"
 
@@ -60,7 +61,7 @@ void audio_init(void)
         }
         else
         {
-            fprintf(stderr, _("Sound disabled\n"));
+            fprintf(stderr, L_("Sound disabled\n"));
             audio_state = 0;
         }
     }
index 4681356..19a15b6 100644 (file)
@@ -108,3 +108,21 @@ void get_array(FILE *fin, float *v, size_t n)
 }
 
 /*---------------------------------------------------------------------------*/
+
+void put_string(FILE *fout, const char *s)
+{
+    fputs(s, fout);
+    fputc('\0', fout);
+}
+
+void get_string(FILE *fin, char *s, int max)
+{
+    do
+        *s = (char) fgetc(fin);
+    while (*s++ != '\0' && max-- > 0);
+
+    if(*(s - 1) != '\0')
+        *(s - 1) = '\0';
+}
+
+/*---------------------------------------------------------------------------*/
index 775d7ff..3b0cf63 100644 (file)
@@ -14,6 +14,9 @@ void get_float(FILE *, float *);
 void get_index(FILE *, int   *);
 void get_array(FILE *, float *, size_t);
 
+void put_string(FILE *fout, const char *);
+void get_string(FILE *fin, char *, int );
+
 /*---------------------------------------------------------------------------*/
 
 #endif
index 7f25de0..3c199c6 100644 (file)
@@ -20,6 +20,7 @@
 #include "glext.h"
 #include "image.h"
 #include "vec3.h"
+#include "text.h"
 #include "gui.h"
 
 /*---------------------------------------------------------------------------*/
@@ -379,7 +380,7 @@ static int gui_widget(int pd, int type)
             return id;
         }
 
-    fprintf(stderr, _("Out of widget IDs\n"));
+    fprintf(stderr, "Out of widget IDs\n");
 
     return 0;
 }
index 39d3720..3d294d4 100644 (file)
  */
 
 #include <string.h>
-#include <stdlib.h>
 #include <locale.h>
+#include <stdlib.h>
+#include <stdio.h>
 
 #include "lang.h"
+#include "text.h"
 
 /*---------------------------------------------------------------------------*/
 
@@ -53,4 +55,13 @@ const char *sgettext(const char *msgid)
     return msgval;
 }
 
+const char *get_local_text(const char *msgid)
+{
+#if ENABLE_NLS
+    return text_to_locale(gettext(msgid));
+#else
+    return msgid;
+#endif
+}
+
 /*---------------------------------------------------------------------------*/
index 83983e5..6ab8410 100644 (file)
 
 #if ENABLE_NLS
 #include <libintl.h>
+
 #define _(String)   gettext(String)
+#define L_(String)  get_local_text(String)
+
 #else
+
 #define _(String)   (String)
-#endif
+#define L_(String)  (String)
+
+#endif /* ENABLE_NLS */
 
 /* No-op, useful for marking up strings for extraction-only. */
 #define N_(String)  (String)
@@ -30,6 +36,7 @@
 void lang_init(const char *domain, const char *locale_dir);
 
 const char *sgettext(const char *);
+const char *get_local_text(const char *);
 
 /*---------------------------------------------------------------------------*/
 
diff --git a/share/text.c b/share/text.c
new file mode 100644 (file)
index 0000000..8bdfd64
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2003 Robert Kooima
+ *
+ * NEVERBALL is  free software; you can redistribute  it and/or modify
+ * it under the  terms of the GNU General  Public License as published
+ * by the Free  Software Foundation; either version 2  of the License,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT  ANY  WARRANTY;  without   even  the  implied  warranty  of
+ * MERCHANTABILITY or  FITNESS FOR A PARTICULAR PURPOSE.   See the GNU
+ * General Public License for more details.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#if ENABLE_NLS
+#include <iconv.h>
+#endif
+
+#include "text.h"
+
+/*---------------------------------------------------------------------------*/
+
+#define MAXSTR 256
+
+/*---------------------------------------------------------------------------*/
+
+#if ENABLE_NLS
+static iconv_t conv_from_locale = 0;
+static iconv_t conv_to_locale = 0;
+#endif
+
+void text_init(void)
+{
+#if ENABLE_NLS
+    if ((conv_from_locale = iconv_open("UTF-8", "")) == (iconv_t) -1)
+        fprintf(stderr, "Error: %s\n", strerror(errno));
+
+    if ((conv_to_locale = iconv_open("", "UTF-8")) == (iconv_t) -1)
+        fprintf(stderr, "Error: %s\n", strerror(errno));
+#else
+    return;
+#endif
+}
+
+void text_quit(void)
+{
+#if ENABLE_NLS
+    if (conv_from_locale != (iconv_t) -1)
+        iconv_close(conv_from_locale);
+
+    if (conv_to_locale != (iconv_t) -1)
+        iconv_close(conv_to_locale);
+#else
+    return;
+#endif
+}
+
+/*---------------------------------------------------------------------------*/
+
+char *text_from_locale(char *str0)
+{
+#if ENABLE_NLS
+    static char buffer[MAXSTR * 2];
+
+    char *str0p = str0;
+    char *str1p = buffer;
+
+    size_t l0 = strlen(str0);
+    size_t l1 = sizeof (buffer);
+
+    if (conv_from_locale == (iconv_t) -1)
+        return str0;
+
+    if (iconv(conv_from_locale, &str0p, &l0, &str1p, &l1) == (size_t) -1)
+        fprintf(stderr, "Error while converting to UTF-8: %s\n",
+                strerror(errno));
+
+    *str1p = '\0';
+
+    return buffer;
+#else
+    return str0;
+#endif
+}
+
+char *text_to_locale(char *str0)
+{
+#if ENABLE_NLS
+    static char buffer[MAXSTR * 2];
+
+    char *str0p = str0;
+    char *str1p = buffer;
+
+    size_t l0 = strlen(str0);
+    size_t l1 = sizeof (buffer);
+
+    if (conv_to_locale == (iconv_t) -1)
+        return str0;
+
+    if (iconv(conv_to_locale, &str0p, &l0, &str1p, &l1) == (size_t) -1)
+        fprintf(stderr, "Error while converting from UTF-8: %s\n",
+                strerror(errno));
+
+    *str1p = '\0';
+
+    return buffer;
+#else
+    return str0;
+#endif
+}
+
+/*---------------------------------------------------------------------------*/
+
+int text_add_char(Uint32 unicode, char *string, int maxbytes, int maxchars)
+{
+    size_t pos = strlen(string);
+    int l;
+
+    if      (unicode < 0x80)    l = 1;
+    else if (unicode < 0x0800)  l = 2;
+    else if (unicode < 0x10000) l = 3;
+    else                        l = 4;
+
+    if ((pos + l >= maxbytes) || (text_length(string) + 1 >= maxchars))
+        return 0;
+
+    if (unicode < 0x80)
+        string[pos++] = (char) unicode;
+    else if (unicode < 0x0800)
+    {
+        string[pos++] = (char) ((unicode >> 6) | 0xC0);
+        string[pos++] = (char) ((unicode & 0x3F) | 0x80);
+    }
+    else if (unicode < 0x10000)
+    {
+        string[pos++] = (char) ((unicode >> 12) | 0xE0);
+        string[pos++] = (char) (((unicode >> 6) & 0x3F) | 0x80);
+        string[pos++] = (char) ((unicode & 0x3F) | 0x80);
+    }
+    else
+    {
+        string[pos++] = (char) ((unicode >> 18) | 0xF0);
+        string[pos++] = (char) (((unicode >> 12) & 0x3F) | 0x80);
+        string[pos++] = (char) (((unicode >> 6) & 0x3F) | 0x80);
+        string[pos++] = (char) ((unicode & 0x3F) | 0x80);
+    }
+
+    string[pos++] = 0;
+
+    return l;
+}
+
+int text_del_char(char *string)
+{
+    size_t pos = strlen(string) - 1;
+
+    while (pos >= 0 && ((string[pos] & 0xC0) == 0x80))
+        string[pos--] = 0;
+
+    if (pos >= 0)
+    {
+        string[pos] = 0;
+        return 1;
+    }
+
+    return 0;
+}
+
+int text_length(const char *string)
+{
+    int result = 0;
+
+    while (*string != '\0')
+        if ((*string++ & 0xC0) != 0x80)
+            result++;
+
+    return result;
+}
+
+/*---------------------------------------------------------------------------*/
diff --git a/share/text.h b/share/text.h
new file mode 100644 (file)
index 0000000..56937b1
--- /dev/null
@@ -0,0 +1,21 @@
+#ifndef TEXT_H
+#define TEXT_H
+
+#include <SDL.h>
+
+/*---------------------------------------------------------------------------*/
+
+void text_init(void);
+
+char *text_from_locale(char *);
+char *text_to_locale(char *);
+
+int text_add_char(Uint32, char *, int, int);
+int text_del_char(char *);
+int text_length(const char *);
+
+void text_quit(void);
+
+/*---------------------------------------------------------------------------*/
+
+#endif
index 222343f..91b570a 100644 (file)
@@ -5,7 +5,7 @@ command shell script.
 
   * democonv
 
-    A bare-bones Neverball 1.4.0 to v3 (Neverball 1.5.0) format replay
+    A bare-bones Neverball 1.4.0 to v4 (Neverball 1.5.0) format replay
     converter.  Reads a replay from standard input and writes a
     converted replay to standard output.  See below for examples.
 
index efe9370..6ab2e78 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * democonv -- Neverball 1.4.0 to v3 format replay converter.
+ * democonv -- Neverball 1.4.0 to v4 format replay converter.
  *
  * Copyright (C) 2006 Jānis Rūcis
  *
@@ -61,7 +61,7 @@ static void get_short(FILE *fin, short *s)
 #define MAG_OLD     0x4E425250
 
 #define MAG_NEW     0x52424EAF
-#define VER_NEW     3
+#define VER_NEW     4
 
 /*---------------------------------------------------------------------------*/
 
@@ -100,7 +100,7 @@ static int read_demo(FILE *fin, int *timer, int *coins,
         *goal  = (int) sg;
         *score = (int) ss;
         *balls = (int) sb;
-        
+
         return 1;
     }
     return 0;
@@ -243,11 +243,11 @@ int main(int argc, char *argv[])
             put_index(fout, &state);
             put_index(fout, &mode);
 
-            fwrite(player, 1, MAXNAM,  fout);
-            fwrite(date,   1, DATELEN, fout);
+            put_string(fout, player);
+            put_string(fout, date);
 
-            fwrite(shot, 1, PATHMAX, fout);
-            fwrite(file, 1, PATHMAX, fout);
+            put_string(fout, shot);
+            put_string(fout, file);
 
             put_index(fout, &time);
             put_index(fout, &goal);