From 7fb3b4f83e9348c87924dd258afdb51a724467b7 Mon Sep 17 00:00:00 2001 From: rubidium Date: Sat, 17 Mar 2007 22:21:05 +0000 Subject: (svn r9271) -Codechange: make the language pack initialisation a little more clear and extendable (more language paths). --- src/fileio.cpp | 1 + src/functions.h | 2 - src/strings.cpp | 200 ++++++++++++++++++++++++++++++++++---------------------- src/strings.h | 3 + src/variables.h | 23 ++++--- 5 files changed, 140 insertions(+), 89 deletions(-) diff --git a/src/fileio.cpp b/src/fileio.cpp index 3e718e6da..a578d05fe 100644 --- a/src/fileio.cpp +++ b/src/fileio.cpp @@ -362,6 +362,7 @@ void DeterminePaths(const char *exe) /* Sets the search path for lng files to the custom one */ _paths.lang_dir = MallocT(MAX_PATH); ttd_strlcpy(_paths.lang_dir, CUSTOM_LANG_DIR, MAX_PATH); + AppendPathSeparator(_paths.lang_dir, MAX_PATH); #else _paths.lang_dir = str_fmt("%slang" PATHSEP, _paths.game_data_dir); #endif diff --git a/src/functions.h b/src/functions.h index 2fb154b7f..2d3bcc998 100644 --- a/src/functions.h +++ b/src/functions.h @@ -205,8 +205,6 @@ void ShowSaveLoadDialog(int mode); /* callback from drivers that is called if the game size changes dynamically */ void GameSizeChanged(); bool FileExists(const char *filename); -bool ReadLanguagePack(int index); -void InitializeLanguagePacks(); const char *GetCurrentLocale(const char *param); void *ReadFileToMem(const char *filename, size_t *lenp, size_t maxsize); diff --git a/src/strings.cpp b/src/strings.cpp index 0dd6fcad8..fd4b5b0f8 100644 --- a/src/strings.cpp +++ b/src/strings.cpp @@ -27,6 +27,7 @@ /* for opendir/readdir/closedir */ # include "fios.h" +DynamicLanguages _dynlang; char _userstring[128]; static char *StationGetSpecialString(char *buff, int x, const char* last); @@ -1068,16 +1069,12 @@ StringID RemapOldStringID(StringID s) bool ReadLanguagePack(int lang_index) { int tot_count, i; - LanguagePack *lang_pack; size_t len; char **langpack_offs; char *s; - { - char *lang = str_fmt("%s%s", _paths.lang_dir, _dynlang.ent[lang_index].file); - lang_pack = (LanguagePack*)ReadFileToMem(lang, &len, 200000); - free(lang); - } + LanguagePack *lang_pack = (LanguagePack*)ReadFileToMem(_dynlang.ent[lang_index].file, &len, 200000); + if (lang_pack == NULL) return false; if (len < sizeof(LanguagePack) || lang_pack->ident != TO_LE32(LANGUAGE_PACK_IDENT) || @@ -1119,7 +1116,7 @@ bool ReadLanguagePack(int lang_index) free(_langpack_offs); _langpack_offs = langpack_offs; - ttd_strlcpy(_dynlang.curr_file, _dynlang.ent[lang_index].file, sizeof(_dynlang.curr_file)); + ttd_strlcpy(_dynlang.curr_file, _dynlang.ent[lang_index].file, lengthof(_dynlang.curr_file)); _dynlang.curr = lang_index; SetCurrentGrfLangID(_langpack->isocode); @@ -1152,96 +1149,145 @@ const char *GetCurrentLocale(const char *param) static int CDECL LanguageCompareFunc(const void *a, const void *b) { - return strcmp(*(const char* const *)a, *(const char* const *)b); + const Language *cmp1 = (const Language*)a; + const Language *cmp2 = (const Language*)b; + + return strcmp(cmp1->file, cmp2->file); +} + +/** + * Checks whether the given language is already found. + * @param langs languages we've found so fa + * @param max the length of the language list + * @param language name of the language to check + * @return true if and only if a language file with the same name has not been found + */ +static bool UniqueLanguageFile(const Language *langs, uint max, const char *language) +{ + for (uint i = 0; i < max; i++) { + const char *f_name = strrchr(langs[i].file, PATHSEPCHAR); + if (strcmp(f_name, language) == 0) return false; // duplicates + } + + return true; +} + +/** + * Reads the language file header and checks compatability. + * @param file the file to read + * @param hdr the place to write the header information to + * @return true if and only if the language file is of a compatible version + */ +static bool GetLanguageFileHeader(const char *file, LanguagePack *hdr) +{ + FILE *f = fopen(file, "rb"); + if (f == NULL) return false; + + size_t read = fread(hdr, sizeof(*hdr), 1, f); + fclose(f); + + return read == 1 && + hdr->ident == TO_LE32(LANGUAGE_PACK_IDENT) && + hdr->version == TO_LE32(LANGUAGE_PACK_VERSION); } -static int GetLanguageList(char **languages, int max) +/** + * Gets a list of languages from the given directory. + * @param langs the list to write to + * @param start the initial offset in the list + * @param max the length of the language list + * @param path the base directory to search in + * @return the number of added languages + */ +static int GetLanguageList(Language *langs, int start, int max, const char *path) { - DIR *dir; - struct dirent *dirent; - int num = 0; + int i = start; - dir = ttd_opendir(_paths.lang_dir); + DIR *dir = ttd_opendir(path); if (dir != NULL) { - while ((dirent = readdir(dir)) != NULL) { - const char *d_name = FS2OTTD(dirent->d_name); - const char *t = strrchr(d_name, '.'); + struct dirent *dirent; + while ((dirent = readdir(dir)) != NULL && i < max) { + const char *d_name = FS2OTTD(dirent->d_name); + const char *extension = strrchr(d_name, '.'); - if (t != NULL && strcmp(t, ".lng") == 0) { - languages[num++] = strdup(d_name); - if (num == max) break; + /* Not a language file */ + if (extension == NULL || strcmp(extension, ".lng") != 0) continue; + + /* Filter any duplicate language-files, first-come first-serve */ + if (!UniqueLanguageFile(langs, i, d_name)) continue; + + langs[i].file = str_fmt("%s%s", path, d_name); + + /* Check whether the file is of the correct version */ + LanguagePack hdr; + if (!GetLanguageFileHeader(langs[i].file, &hdr)) { + free(langs[i].file); + continue; } + + i++; } closedir(dir); } - - qsort(languages, num, sizeof(char*), LanguageCompareFunc); - return num; + return i - start; } -// make a list of the available language packs. put the data in _dynlang struct. +/** + * Make a list of the available language packs. put the data in + * _dynlang struct. + */ void InitializeLanguagePacks() { - DynamicLanguages *dl = &_dynlang; - int i; - int n; - int m; - int def; - int def2; - int fallback; - LanguagePack hdr; - FILE *in; - char *files[MAX_LANG]; - const char* lang; - - lang = GetCurrentLocale("LC_MESSAGES"); - if (lang == NULL) lang = "en_GB"; + Language files[MAX_LANG]; + uint language_count = GetLanguageList(files, 0, lengthof(files), _paths.lang_dir); + if (language_count == 0) error("No available language packs (invalid versions?)"); - n = GetLanguageList(files, lengthof(files)); - - def = -1; - def2 = -1; - fallback = 0; - - // go through the language files and make sure that they are valid. - for (i = m = 0; i != n; i++) { - size_t j; - - char *s = str_fmt("%s%s", _paths.lang_dir, files[i]); - in = fopen(s, "rb"); - free(s); - if (in == NULL || - (j = fread(&hdr, sizeof(hdr), 1, in), fclose(in), j) != 1 || - hdr.ident != TO_LE32(LANGUAGE_PACK_IDENT) || - hdr.version != TO_LE32(LANGUAGE_PACK_VERSION)) { - free(files[i]); - continue; - } + /* Sort the language names alphabetically */ + qsort(files, language_count, sizeof(Language), LanguageCompareFunc); - dl->ent[m].file = files[i]; - dl->ent[m].name = strdup(hdr.name); + /* Acquire the locale of the current system */ + const char *lang = GetCurrentLocale("LC_MESSAGES"); + if (lang == NULL) lang = "en_GB"; - if (strcmp(hdr.isocode, "en_GB") == 0) fallback = m; - if (strncmp(hdr.isocode, lang, 2) == 0) def2 = m; - if (strncmp(hdr.isocode, lang, 5) == 0) def = m; + int chosen_language = -1; ///< Matching the language in the configuartion file or the current locale + int language_fallback = -1; ///< Using pt_PT for pt_BR locale when pt_BR is not available + int en_GB_fallback = 0; ///< Fallback when no locale-matching language has been found - m++; - } - if (def == -1) def = (def2 != -1 ? def2 : fallback); + DynamicLanguages *dl = &_dynlang; + dl->num = 0; + /* Fill the dynamic languages structures */ + for (uint i = 0; i < language_count; i++) { + /* File read the language header */ + LanguagePack hdr; + if (!GetLanguageFileHeader(files[i].file, &hdr)) continue; + + dl->ent[dl->num].file = files[i].file; + dl->ent[dl->num].name = strdup(hdr.name); + dl->dropdown[dl->num] = SPECSTR_LANGUAGE_START + dl->num; + + /* We are trying to find a default language. The priority is by + * configuration file, local environment and last, if nothing found, + * english. If def equals -1, we have not picked a default language */ + if (strcmp(dl->ent[dl->num].file, dl->curr_file) == 0) chosen_language = dl->num; + + if (chosen_language == -1) { + if (strcmp (hdr.isocode, "en_GB") == 0) en_GB_fallback = dl->num; + if (strncmp(hdr.isocode, lang, 5) == 0) chosen_language = dl->num; + if (strncmp(hdr.isocode, lang, 2) == 0) language_fallback = dl->num; + } - if (m == 0) - error(n == 0 ? "No available language packs" : "Invalid version of language packs"); + dl->num++; + } + /* Terminate the dropdown list */ + dl->dropdown[dl->num] = INVALID_STRING_ID; - dl->num = m; - for (i = 0; i != dl->num; i++) dl->dropdown[i] = SPECSTR_LANGUAGE_START + i; - dl->dropdown[i] = INVALID_STRING_ID; + if (dl->num == 0) error("Invalid version of language packs"); - for (i = 0; i != dl->num; i++) - if (strcmp(dl->ent[i].file, dl->curr_file) == 0) { - def = i; - break; - } + /* We haven't found the language in the config nor the one in the locale. + * Now we set it to one of the fallback languages */ + if (chosen_language == -1) { + chosen_language = (language_fallback != -1) ? language_fallback : en_GB_fallback; + } - if (!ReadLanguagePack(def)) - error("can't read language pack '%s'", dl->ent[def].file); + if (!ReadLanguagePack(chosen_language)) error("Can't read language pack '%s'", dl->ent[chosen_language].file); } diff --git a/src/strings.h b/src/strings.h index fb2c3ed88..56893346c 100644 --- a/src/strings.h +++ b/src/strings.h @@ -11,4 +11,7 @@ extern char _userstring[128]; void InjectDParam(int amount); int32 GetParamInt32(); +bool ReadLanguagePack(int index); +void InitializeLanguagePacks(); + #endif /* STRINGS_H */ diff --git a/src/variables.h b/src/variables.h index ef5e9b73c..7f8771a87 100644 --- a/src/variables.h +++ b/src/variables.h @@ -315,19 +315,22 @@ VARDEF Vehicle *_place_clicked_vehicle; VARDEF char _ini_videodriver[32], _ini_musicdriver[32], _ini_sounddriver[32]; -// Used for dynamic language support +/** Information about a language */ +struct Language { + char *name; ///< The internal name of the language + char *file; ///< The name of the language as it appears on disk +}; + +/** Used for dynamic language support */ struct DynamicLanguages { - int num; // number of languages - int curr; // currently selected language index - char curr_file[MAX_LANG]; // currently selected language file - StringID dropdown[MAX_LANG + 1]; // used in settings dialog - struct { - char *name; - char *file; - } ent[MAX_LANG]; + int num; ///< Number of languages + int curr; ///< Currently selected language index + char curr_file[MAX_PATH]; ///< Currently selected language file (needed for saving the filename of the loaded language + StringID dropdown[MAX_LANG + 1]; ///< List of languages in the settings gui + Language ent[MAX_LANG]; ///< Information about the languages }; -VARDEF DynamicLanguages _dynlang; +extern DynamicLanguages _dynlang; // defined in strings.cpp VARDEF int _num_resolutions; VARDEF uint16 _resolutions[32][2]; -- cgit v1.2.3-54-g00ecf