diff --git a/common.mk b/common.mk index 08216df..3defbaa 100644 --- a/common.mk +++ b/common.mk @@ -1,4 +1,4 @@ -VERSION = 0.5 +VERSION = 0.6 REVISION = $(shell date +"%y%m%d") SEARCHPATH += src/ src/battle src/draw src/game src/galaxy src/json src/system src/test @@ -15,7 +15,7 @@ OBJS += effects.o entities.o extractionPoint.o OBJS += fighters.o OBJS += galacticMap.o game.o OBJS += hud.o -OBJS += init.o input.o io.o items.o +OBJS += i18n.o init.o input.o io.o items.o OBJS += load.o locations.o lookup.o OBJS += main.o messageBox.o mission.o missionInfo.o modalDialog.o OBJS += objectives.o options.o diff --git a/makefile b/makefile index aaab585..465f22e 100644 --- a/makefile +++ b/makefile @@ -2,13 +2,14 @@ PROG = tbftss CC = gcc BIN_DIR = /usr/bin DATA_DIR = /opt/tbftss +LOCALE_DIR = /usr/share/locale SEARCHPATH += src/plat/unix OBJS += unixInit.o include common.mk -CXXFLAGS += `sdl2-config --cflags` -DVERSION=$(VERSION) -DREVISION=$(REVISION) -DDATA_DIR=\"$(DATA_DIR)\" +CXXFLAGS += `sdl2-config --cflags` -DVERSION=$(VERSION) -DREVISION=$(REVISION) -DDATA_DIR=\"$(DATA_DIR)\" -DLOCALE_DIR=\"$(LOCALE_DIR)\" CXXFLAGS += -Wall -ansi -pedantic -Werror -Wstrict-prototypes CXXFLAGS += -g -lefence diff --git a/makefile.win32 b/makefile.win32 index 8fc0df6..b70a058 100644 --- a/makefile.win32 +++ b/makefile.win32 @@ -2,11 +2,12 @@ PROG = tbftss.exe CC = x86_64-w64-mingw32-gcc SDLC = /usr/x86_64-w64-mingw32/bin/sdl2-config LIBPATH = /usr/x86_64-w64-mingw32/lib +LOCALE_DIR = locale SEARCHPATH += src/plat/win32 OBJS += win32Init.o -CXXFLAGS += `$(SDLC) --cflags` -DVERSION=$(VERSION) -DREVISION=$(REVISION) -DDATA_DIR=\"$(DATA_DIR)\" +CXXFLAGS += `$(SDLC) --cflags` -DVERSION=$(VERSION) -DREVISION=$(REVISION) -DDATA_DIR=\"$(DATA_DIR)\" -DLOCALE_DIR=\"$(LOCALE_DIR)\" CXXFLAGS += -Wall -ansi -pedantic -Werror -Wstrict-prototypes CXXFLAGS += -g -lefence diff --git a/src/defs.h b/src/defs.h index 8ddb8ff..a7645e7 100644 --- a/src/defs.h +++ b/src/defs.h @@ -46,6 +46,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define MAX_NAME_LENGTH 32 #define MAX_DESCRIPTION_LENGTH 512 +#define MAX_LINE_LENGTH 1024 #define MAX_FILENAME_LENGTH 1024 #define NUM_TEXTURE_BUCKETS 32 diff --git a/src/structs.h b/src/structs.h index 0ed2fcf..b07dde5 100644 --- a/src/structs.h +++ b/src/structs.h @@ -37,6 +37,7 @@ typedef struct MessageBox MessageBox; typedef struct GridCell GridCell; typedef struct ScriptRunner ScriptRunner; typedef struct Location Location; +typedef struct Bucket Bucket; typedef struct { int debug; @@ -287,6 +288,7 @@ typedef struct { int numInitialEnemies; int status; int epic; + int isChallenge; int epicFighterLimit; int playerSelect; int manualComplete; @@ -321,7 +323,8 @@ struct ScriptRunner { }; typedef struct { - StarSystem starSystemHead, *starSystemTail; + StarSystem starSystemHead; + Mission challengeMissionHead; Mission *currentMission; char selectedStarSystem[MAX_NAME_LENGTH]; int completedMissions; @@ -407,3 +410,26 @@ typedef struct { SDL_Color lightGrey; SDL_Color darkGrey; } Colors; + +struct Bucket +{ + char *key, *value; + Bucket *next; +}; + +typedef struct HashTable +{ + Bucket **bucket; + int *bucketCount; +} HashTable; + +typedef struct MOHeader +{ + int32_t magicNumber, version, stringCount; + int32_t originalOffset, translationOffset; +} MOHeader; + +typedef struct MOEntry +{ + int32_t length, offset; +} MOEntry; diff --git a/src/system/i18n.c b/src/system/i18n.c new file mode 100644 index 0000000..6f17493 --- /dev/null +++ b/src/system/i18n.c @@ -0,0 +1,388 @@ +/* +Copyright (C) 2009-2016 Parallel Realities + +This program 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. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. +*/ + +#include "i18n.h" + +static HashTable table; + +static int hashCode(char *); +static void put(char *, char *); +static void initTable(void); + +void setLanguage(char *applicationName, char *languageCode) +{ + char language[MAX_LINE_LENGTH], c[MAX_LINE_LENGTH]; + char *lang, **key, **value; + int i, swap; + FILE *fp; + MOHeader header; + MOEntry *original, *translation; + #if DEV == 1 + int read; + #endif + + initTable(); + + language[0] = '\0'; + + if (languageCode == NULL) + { + #ifdef _WIN32 + GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SISO639LANGNAME, c, MAX_LINE_LENGTH); + + if (c[0] != '\0') + { + STRNCPY(language, c, MAX_LINE_LENGTH); + + GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SISO3166CTRYNAME, c, MAX_LINE_LENGTH); + + if (c[0] != '\0') + { + strncat(language, "_", MAX_MESSAGE_LENGTH - strlen(language) - 1); + + strncat(language, c, MAX_MESSAGE_LENGTH - strlen(language) - 1); + } + } + #else + if ((lang = getenv("LC_ALL")) || (lang = getenv("LC_CTYPE")) || (lang = getenv("LANG"))) + { + STRNCPY(language, lang, MAX_LINE_LENGTH); + } + #endif + } + + else + { + STRNCPY(language, languageCode, MAX_LINE_LENGTH); + } + + if (strstr(language, ".") != NULL) + { + lang = strtok(language, "."); + + STRNCPY(language, lang, MAX_LINE_LENGTH); + } + + printf("Locale is %s\n", language); + + sprintf(c, "%s/%s/LC_MESSAGES/%s.mo", LOCALE_DIR, language, applicationName); + + #if DEV == 1 + printf("Opening %s\n", c); + #endif + + fp = fopen(c, "rb"); + + if (fp == NULL) + { + #if DEV == 1 + printf("Failed to open %s/%s/LC_MESSAGES/%s.mo\n", LOCALE_DIR, language, applicationName); + #endif + + if (strstr(language, "_") == NULL) + { + return; + } + + lang = strtok(language, "_"); + + STRNCPY(language, lang, MAX_LINE_LENGTH); + + sprintf(c, "%s/%s/LC_MESSAGES/%s.mo", LOCALE_DIR, language, applicationName); + + #if DEV == 1 + printf("Opening %s\n", c); + #endif + + fp = fopen(c, "rb"); + + if (fp == NULL) + { + #if DEV == 1 + printf("Failed to open %s/%s/LC_MESSAGES/%s.mo\n", LOCALE_DIR, language, applicationName); + #endif + + return; + } + } + + fread(&header, sizeof(header), 1, fp); + + swap = header.magicNumber == 0x950412de ? 0 : 1; + + if (swap) + { + header.stringCount = SDL_Swap32(header.stringCount); + header.originalOffset = SDL_Swap32(header.originalOffset); + header.translationOffset = SDL_Swap32(header.translationOffset); + } + + original = malloc(sizeof(MOEntry) * header.stringCount); + + translation = malloc(sizeof(MOEntry) * header.stringCount); + + if (original == NULL || translation == NULL) + { + printf("Failed to allocate %d bytes for translation strings\n", (int)sizeof(MOEntry) * header.stringCount); + + exit(1); + } + + #if DEV == 1 + printf("MO file has %d entries\n", header.stringCount); + #endif + + fseek(fp, header.originalOffset, SEEK_SET); + + key = malloc(sizeof(char *) * header.stringCount); + + value = malloc(sizeof(char *) * header.stringCount); + + if (key == NULL || value == NULL) + { + printf("Failed to allocate a whole %d bytes for translation strings\n", (int)sizeof(char *) * header.stringCount); + + exit(1); + } + + for (i=0;inext = NULL; + + table.bucketCount[i] = 0; + } +} + +static void put(char *key, char *value) +{ + Bucket *bucket, *newBucket; + unsigned int hash = hashCode(key); + + #if DEV == 1 + printf("%s = %d\n", key, hash); + #endif + + bucket = table.bucket[hash]; + + while (bucket->next != NULL) + { + bucket = bucket->next; + } + + newBucket = malloc(sizeof(Bucket)); + + if (newBucket == NULL) + { + printf("Failed to allocate a whole %d bytes for a HashTable bucket\n", (int)sizeof(Bucket)); + + exit(1); + } + + newBucket->key = malloc(strlen(key) + 1); + newBucket->value = malloc(strlen(value) + 1); + + if (newBucket->key == NULL || newBucket->value == NULL) + { + printf("Failed to allocate a whole %d bytes for a translation\n", (int)strlen(newBucket->key) + 1); + + exit(1); + } + + STRNCPY(newBucket->key, key, strlen(key) + 1); + STRNCPY(newBucket->value, value, strlen(value) + 1); + + newBucket->next = NULL; + + bucket->next = newBucket; + + table.bucketCount[hash]++; +} + +char *getTranslatedString(char *key) +{ + Bucket *bucket; + unsigned int hash = hashCode(key); + + bucket = table.bucket[hash]->next; + + for (;bucket!=NULL;bucket=bucket->next) + { + if (strcasecmp(key, bucket->key) == 0) + { + return strlen(bucket->value) == 0 ? key : bucket->value; + } + } + + return key; +} + +void cleanupLanguage() +{ + int i; + Bucket *bucket, *p, *q; + + for (i=0;inext;p!=NULL;p=q) + { + free(p->key); + free(p->value); + + q = p->next; + + free(p); + } + + free(bucket); + } + + table.bucket = NULL; +} diff --git a/src/system/i18n.h b/src/system/i18n.h new file mode 100644 index 0000000..a3263e1 --- /dev/null +++ b/src/system/i18n.h @@ -0,0 +1,26 @@ +/* +Copyright (C) 2009-2016 Parallel Realities + +This program 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. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. +*/ + +#include "../common.h" + +#define TABLE_SIZE 255 + +char *getTranslatedString(char *); +void setLanguage(char *, char *); +void cleanupLanguage(void);