From 98f28a6cdf275c2ad0274229a6bdad48c166bbfc Mon Sep 17 00:00:00 2001 From: Cydh Ramdh Date: Sat, 23 Apr 2016 18:43:18 +0700 Subject: [PATCH 1/6] Initial release NPC Translations. * Further info can be found in lang/README.md * Credits to https://github.com/HerculesWS/Hercules/commit/330e31cc71ece055908acb1eb967b4009ebc9c46 @aleos89 Signed-off-by: Cydh Ramdh --- .gitignore | 1 + conf/languages.conf | 39 ++ conf/map_athena.conf | 3 + conf/msg_conf/map_msg.conf | 22 +- lang/README.md | 107 ++++++ src/common/cli.c | 20 + src/common/cli.h | 4 + src/map/atcommand.c | 170 +++++++-- src/map/atcommand.h | 17 + src/map/map.c | 116 +----- src/map/map.h | 25 +- src/map/npc.c | 4 + src/map/pc.c | 31 +- src/map/pc.h | 3 +- src/map/script.c | 754 +++++++++++++++++++++++++++++++++++-- src/map/script.h | 40 ++ 16 files changed, 1164 insertions(+), 192 deletions(-) create mode 100644 conf/languages.conf create mode 100644 lang/README.md diff --git a/.gitignore b/.gitignore index 1f3e0bf9723..eedaaad242d 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,7 @@ Thumbs.db /config.status /core /ipch +/lang/* /login-server /Makefile /Makefile.cache diff --git a/conf/languages.conf b/conf/languages.conf new file mode 100644 index 00000000000..2e6d12c975c --- /dev/null +++ b/conf/languages.conf @@ -0,0 +1,39 @@ +//===== rAthena Configuration File =========================== +// +// Translations generated with '—-generate-translations' should be pointed to here +//============================================================ + +// When employing more than one language, this setting is used as a fallback. +default_language: "English" + +// List of languages. +languages: { + //===== Structure ============================================ + // + //LanguageName: { // Name of language, used for looking up user's language on @langtype. + // lang: ( // List of translations file(s) for NPC Script dialogs, + // "path/to/translation.po", // use comma (,) to separated next file, don't forget the quotes, and + // "path/to/translation2a.po // DO NOT use comma (,) for last file. + // ) + // motd: "path/to/motd.file" // For MOTD file, alternative conf/motd.txt for this language. TODO: Use PO. + // help: "path/to/help.file" // For @Help file, alternative conf/help.txt for this language. TODO: Use PO. + //} + //============================================================ + // Examples: + // + //Spanish: { + // lang: ( + // "lang/Spanish.po" + // ) + //} + //Indonesian: { + // motd: "lang/Indonesian/motd.txt" + // help: "lang/Indonesian/help.txt" + // lang: ( + // "lang/Indonesian/msg_conf.po", + // "lang/Indonesian/npc/airports/airship.po", + // "lang/Indonesian/npc/re/cities/malangdo.po", + // "lang/Indonesian/npc/re/jobs/novice/novice.po" + // ) + //} +} diff --git a/conf/map_athena.conf b/conf/map_athena.conf index 5df09c33b29..2f084d51fb4 100644 --- a/conf/map_athena.conf +++ b/conf/map_athena.conf @@ -119,6 +119,9 @@ help_txt: conf/help.txt help2_txt: conf/help2.txt charhelp_txt: conf/charhelp.txt +// Translation file +language_conf: conf/languages.conf + // Maps: import: conf/maps_athena.conf diff --git a/conf/msg_conf/map_msg.conf b/conf/msg_conf/map_msg.conf index 2e50a20434b..1cac0e75739 100644 --- a/conf/msg_conf/map_msg.conf +++ b/conf/msg_conf/map_msg.conf @@ -805,7 +805,7 @@ 900: Usage: 901: @send len 902: @send {}* -903: Value: or S"" +903: Value: or S\"\" 904: Packet 0x%x length: %d 905: Unknown packet: 0x%x 906: Not a string: @@ -1162,8 +1162,8 @@ 1193: You're currently not autolooting this item. 1194: Removed item: '%s'/'%s' {%hu} from your autolootitem list. 1195: You can have %d items on your autolootitem list. -1196: To add an item to the list, use "@alootid +". To remove an item, use "@alootid -". -1197: "@alootid reset" will clear your autolootitem list. +1196: To add an item to the list, use \"@alootid +\". To remove an item, use \"@alootid -\". +1197: \"@alootid reset\" will clear your autolootitem list. 1198: Your autolootitem list is empty. 1199: Items on your autolootitem list: 1200: Your autolootitem list has been reset. @@ -1368,8 +1368,8 @@ // @mapflag 1311: Enabled Mapflags in this map: -1312: Usage: "@mapflag monster_noteleport 1" (0=Off | 1=On) -1313: Type "@mapflag available" to list the available mapflags. +1312: Usage: \"@mapflag monster_noteleport 1\" (0=Off | 1=On) +1313: Type \"@mapflag available\" to list the available mapflags. 1314: Invalid flag name or flag. 1315: Available Flags: @@ -1448,13 +1448,13 @@ // @accinfo 1365: Usage: @accinfo/@accountinfo -1366: You may search partial name by making use of '%' in the search, ex. "@accinfo %Mario%" lists all characters whose name contains "Mario". +1366: You may search partial name by making use of '%' in the search, ex. \"@accinfo %Mario%\" lists all characters whose name contains \"Mario\". // @set 1367: Usage: @set -1368: Usage: ex. "@set PoringCharVar 50" -1369: Usage: ex. "@set PoringCharVarSTR$ Super Duper String" -1370: Usage: ex. "@set PoringCharVarSTR$" outputs its value, Super Duper String. +1368: Usage: ex. \"@set PoringCharVar 50\" +1369: Usage: ex. \"@set PoringCharVarSTR$ Super Duper String\" +1370: Usage: ex. \"@set PoringCharVarSTR$\" outputs its value, Super Duper String. 1371: NPC variables may not be used with @set. 1372: Instance variables may not be used with @set. 1373: %s value is now: %d @@ -1596,9 +1596,9 @@ 1483: Autolooting item type: '%s' {%d} 1484: You're currently not autolooting this item type. 1485: Removed item type: '%s' {%d} from your autoloottype list. -1486: To add an item type to the list, use "@aloottype +". To remove an item type, use "@aloottype -". +1486: To add an item type to the list, use \"@aloottype +\". To remove an item type, use \"@aloottype -\". 1487: Type List: healing = 0, usable = 2, etc = 3, armor = 4, weapon = 5, card = 6, petegg = 7, petarmor = 8, ammo = 10 -1488: "@aloottype reset" will clear your autoloottype list. +1488: \"@aloottype reset\" will clear your autoloottype list. 1489: Your autoloottype list is empty. 1490: Item types on your autoloottype list: 1491: Your autoloottype list has been reset. diff --git a/lang/README.md b/lang/README.md new file mode 100644 index 00000000000..62637bcc292 --- /dev/null +++ b/lang/README.md @@ -0,0 +1,107 @@ +# rAthena Language Library +========= + +### Table of Contents +--------- +1. Generating Translation (.pot) File +2. Translating Language (.po) File +3. Adding New Language +4. Set Default Language +5. Change Language +6. Other Translation +7. Contributing +8. Known Translation Project + +## 1. Generating Translation (.pot) File +--------- +Please makes sure map-server was compiled. +### Windows: +```` +map-server.exe --generate-translations [path/to/generated.file] +```` +### Linux: +```` +map-server --generate-translations [path/to/generated.file] +```` + +## 2. Translating Language (.po) File +--------- +* After generating the translation/template .pot file, rename it to "My_Translation.po" +* Open the file, then try find this dialog. +```` +#: npc/re/jobs/novice/novice.txt +# mes "Hello there! Welcome to the World of Ragnarok Online. My name is Sprakki and I'm in charge of giving you basic gameplay tips."; +msgctxt "Sprakki#newbe01::NvSprakkiA" +msgid "Hello there! Welcome to the World of Ragnarok Online. My name is Sprakki and I'm in charge of giving you basic gameplay tips." +msgstr "" +```` +* Add/edit the dialog inside `msgstr ""` (Example in Indonesian from idRO) ```` +#: npc/re/jobs/novice/novice.txt +# mes "Hello there! Welcome to the World of Ragnarok Online. My name is Sprakki and I'm in charge of giving you basic gameplay tips."; +msgctxt "Sprakki#newbe01::NvSprakkiA" +msgid "Hello there! Welcome to the World of Ragnarok Online. My name is Sprakki and I'm in charge of giving you basic gameplay tips." +msgstr "Halo! Selamat datang di Ragnarok Online Indonesia. Namaku adalah Sprakki dan aku disini untuk membantumu mengenai pengetahuan dasar bermain." +```` +* Save the file. +**PS:** Steps above are translating manual using text editor. + +## 3. Adding New Language +--------- +1. Open conf/translation.conf +2. Add your language name and the file for translations. +```` + My_Language: { // Langauge Name + lang: ( + "lang/My_Translation.po" // Translation file + ) + } +```` + +Notes: +* More than 1 language can be added +* More than 1 file can be listed for a language, use comma to separated the file pathname. + + +## 4. Set Default Language +--------- +1. Before changing default language, amke sure the language is added. +2. Change the default language value in +```` +default_language: "My_Language" +```` + + +## 5. Change Language +--------- +Use **@langtype** in-game. Example: `@langtype My_Language`. +The language changed will be stored stored as player's account variable. + + +## 6. Other Translation +--------- +Besides NPC dialogs, translation is also available for: +* Atcommand messages (since multilanguage of msg_conf was depreciated). +* Atcommand Help message (@help) +* Message of The Day (MoTD) +```` +translations: { + My_Language: { // Langauge Name + motd: "lang/Indonesian/motd.txt" // MOTD Translation + help: "lang/Indonesian/help.txt" // Help Translation + lang: ( + "lang/My_Translation/msg_conf.po" // map_athena.conf Translation + ) + } +} +```` + +## 7. Contributing +--------- +* rAthena **DOES NOT** officially manage the translation files. Please contact respective community that provide translation project. +* rAthena **ONLY** will lists the translation project that fit our regulations. +* Try visit [rAthena International Forums](https://rathena.org/board/forum/6-international-forums/) to dig informations. +* Originally by @Hercules https://github.com/HerculesWS/Hercules/commit/330e31cc71ece055908acb1eb967b4009ebc9c46, first ported to rAthena by @aleos89, restructured by @cydh. + +## 8. Known Translation Project +--------- +* *No informations yet* diff --git a/src/common/cli.c b/src/common/cli.c index 2c9318ad1b6..ba5528f93b0 100644 --- a/src/common/cli.c +++ b/src/common/cli.c @@ -20,6 +20,7 @@ #include "showmsg.h" #include "core.h" #include "cli.h" +#include "malloc.h" //map confs char* MAP_CONF_NAME; @@ -37,6 +38,9 @@ char* LOGIN_CONF_NAME; //common conf (used by multiple serv) char* LAN_CONF_NAME; //char-login char* MSG_CONF_NAME_EN; //all +char* TRANSLATION_CONF_FILE; +FILE *lang_export_fp; +char *lang_export_file; /** * Function to check if the specified option has an argument following it. @@ -56,6 +60,12 @@ bool opt_has_next_value(const char* option, int i, int argc){ return true; } +bool opt_has_next_value_(const char* option, int i, int argc){ + if (i >= argc - 1) + return false; + return true; +} + /** * Display some information about the emulator, such as: * svn version @@ -171,6 +181,16 @@ int cli_get_options(int argc, char ** argv) { else if (strcmp(arg, "log-config") == 0) { if (opt_has_next_value(arg, i, argc)) LOG_CONF_NAME = argv[++i]; + } else if (strcmp(arg, "generate-translations") == 0) { + if (opt_has_next_value_(arg, i, argc)) + lang_export_file = aStrdup(argv[++i]); + else + lang_export_file = aStrdup("./lang/generated_translations.pot"); + + if (!(lang_export_fp = fopen(lang_export_file,"wb"))) + ShowError("export-dialog: failed to open '%s' for writing!\n", lang_export_file); + + runflag = CORE_ST_STOP; } else { ShowError("Unknown option '%s'.\n", argv[i]); diff --git a/src/common/cli.h b/src/common/cli.h index 4819dfa4947..7f28cdc63bf 100644 --- a/src/common/cli.h +++ b/src/common/cli.h @@ -24,6 +24,9 @@ extern "C" { extern char* ATCOMMAND_CONF_FILENAME; extern char* SCRIPT_CONF_NAME; extern char* GRF_PATH_FILENAME; +// Set during startup when attempting to export the lang, unset after server initialization is over + extern FILE *lang_export_fp; + extern char *lang_export_file; // for lang_export_fp //char extern char* CHAR_CONF_NAME; extern char* SQL_CONF_NAME; @@ -37,6 +40,7 @@ extern void display_helpscreen(bool exit); bool cli_hasevent(); void display_versionscreen(bool do_exit); bool opt_has_next_value(const char* option, int i, int argc); +bool opt_has_next_value_(const char* option, int i, int argc); int cli_get_options(int argc, char ** argv); int parse_console_timer(int tid, unsigned int tick, int id, intptr_t data); extern int parse_console(const char* buf); //particular for each serv diff --git a/src/map/atcommand.c b/src/map/atcommand.c index deb6a7315c8..4545dd6801c 100644 --- a/src/map/atcommand.c +++ b/src/map/atcommand.c @@ -67,6 +67,8 @@ struct AliasInfo { char alias[ATCOMMAND_LENGTH]; }; +char*** msg_table; +uint8 max_message_table; char atcommand_symbol = '@'; // first char of the commands char charcommand_symbol = '#'; @@ -96,6 +98,123 @@ struct atcmd_binding_data* get_atcommandbind_byname(const char* name) { return ( i < atcmd_binding_count ) ? atcmd_binding[i] : NULL; } +const char* atcommand_msgsd(struct map_session_data *sd, int msg_number) +{ + if (!(msg_number >= 0 && msg_number < MAX_MSG)) + return "??"; + if (!sd || sd->lang_id >= max_message_table || !msg_table[sd->lang_id][msg_number] ) + return msg_table[0][msg_number]; + return msg_table[sd->lang_id][msg_number]; +} + +/** + * Return the message string of the specified number by [Yor] + */ +const char* atcommand_msg(int msg_number) +{ + if (msg_number >= 0 && msg_number < MAX_MSG) { + if(msg_table[default_lang_id][msg_number] != NULL && msg_table[default_lang_id][msg_number][0] != '\0') + return msg_table[default_lang_id][msg_number]; + + if(msg_table[0][msg_number] != NULL && msg_table[0][msg_number][0] != '\0') + return msg_table[0][msg_number]; + } + + return "??"; +} + +/** + * Reads Message Data + * + * @param[in] cfg_name configuration filename to read. + * @param[in] allow_override whether to allow duplicate message IDs to override the original value. + * @return success state. + */ +bool msg_config_read(const char *cfg_name, bool allow_override) +{ + int msg_number, msg_count = 0; + char line[1024], w1[16], w2[1024]; + FILE *fp; + static int called = 0; + + if ((fp = fopen(cfg_name, "r")) == NULL) { + ShowError("Messages file not found: %s\n", cfg_name); + return false; + } + + if (!max_message_table) + atcommand_expand_message_table(); + + while(fgets(line, sizeof(line), fp)) { + if (line[0] == '/' && line[1] == '/') + continue; + if (sscanf(line, "%15[^:]: %1023[^\r\n]", w1, w2) != 2) + continue; + + if (strcmpi(w1, "import") == 0) { + msg_config_read(w2, true); + } else { + msg_number = atoi(w1); + if (msg_number >= 0 && msg_number < MAX_MSG) { + if (msg_table[0][msg_number] != NULL) { + if (!allow_override) { + ShowError("Duplicate message: ID '%d' was already used for '%s'. Message '%s' will be ignored.\n", + msg_number, w2, msg_table[0][msg_number]); + continue; + } + aFree(msg_table[0][msg_number]); + } + // This could easily become consecutive memory like get_str() and save the malloc overhead for over 1k calls [Ind] + msg_table[0][msg_number] = (char *)aMalloc((strlen(w2) + 1)*sizeof (char)); + strcpy(msg_table[0][msg_number],w2); + msg_count++; + } + } + } + + ShowInfo("Done reading "CL_WHITE"'%d'"CL_RESET" messages in "CL_WHITE"'%s'"CL_RESET".\n",msg_count,cfg_name); + fclose(fp); + + if (++called == 1) { // Original + if (lang_export_fp) { + int i; + + for(i = 0; i < MAX_MSG;i++) { + if (msg_table[0][i] != NULL ) { + fprintf(lang_export_fp, "#: map_msg.conf::[%d]\n" + "msgctxt \"map_msg.conf\"\n" + "msgid \"%s\"\n" + "msgstr \"\"\n", + i, + msg_table[0][i] + ); + } + } + } + } + + return true; +} + +/** + * Cleanup Message Data + */ +void do_final_msg(void) +{ + int i, j; + + for(i = 0; i < max_message_table; i++) { + for (j = 0; j < MAX_MSG; j++) { + if (msg_table[i][j]) + aFree(msg_table[i][j]); + } + aFree(msg_table[i]); + } + + if (msg_table) + aFree(msg_table); +} + /** * retrieves the help string associated with a given command. * @@ -3869,7 +3988,8 @@ ACMD_FUNC(reload) { clif_displaymessage(fd, msg_txt(sd,100)); // Scripts have been reloaded. } else if (strstr(command, "msgconf") || strncmp(message, "msgconf", 3) == 0) { - map_msg_reload(); + do_final_msg(); + msg_config_read(MSG_CONF_NAME_EN, true); clif_displaymessage(fd, msg_txt(sd,463)); // Message configuration has been reloaded. } else if (strstr(command, "questdb") || strncmp(message, "questdb", 3) == 0) { do_reload_quest(); @@ -9424,38 +9544,31 @@ ACMD_FUNC(fontcolor) return 0; } -ACMD_FUNC(langtype) -{ - char langstr[8]; - int i=0, fail=0; +ACMD_FUNC(langtype) { + char langstr[16]; + int i = 0; memset(langstr, '\0', sizeof(langstr)); - if(sscanf(message, "%3s", langstr) >= 1){ - int lang=0; - lang = msg_langstr2langtype(langstr); //Switch langstr to associated langtype - if( msg_checklangtype(lang,false) == 1 ){ //Verify it's enabled and set it - char output[100]; - pc_setaccountreg(sd, add_str("#langtype"), lang); //For login/char - sd->langtype = lang; - sprintf(output,msg_txt(sd,461),msg_langtype2langstr(lang)); // Language is now set to %s. - clif_displaymessage(fd,output); - return 0; - } else if (lang != -1) { //defined langage but failed check - clif_displaymessage(fd,msg_txt(sd,462)); // This langage is currently disabled. - return -1; + if(sscanf(message, "%15s", langstr) >= 1){ + for (i = 0; i < max_lang_id; i++) { + if (strcmpi(languages[i], langstr) == 0) + break; } + return pc_set_language(sd, i) ? 0 : -1; } //wrong or no entry clif_displaymessage(fd,msg_txt(sd,460)); // Please enter a valid language (usage: @langtype ). clif_displaymessage(fd,msg_txt(sd,464)); // ---- Available languages: - while(fail != -1){ //out of range - fail = msg_checklangtype(i,false); - if(fail == 1) - clif_displaymessage(fd,msg_langtype2langstr(i)); - i++; + for (i = 0; i < max_lang_id; i++) { + char output[CHAT_SIZE_MAX]; + sprintf(output, "- %s", languages[i]); + if (sd->lang_id == i) + strcat(output, "*"); + clif_displaymessage(fd, output); } + return -1; } @@ -10550,6 +10663,17 @@ void atcommand_doload(void) { atcommand_config_read(ATCOMMAND_CONF_FILENAME); } +void atcommand_expand_message_table(void) { + RECREATE(msg_table, char **, ++max_message_table); + CREATE(msg_table[max_message_table - 1], char *, MAX_MSG); +} + +void atcommand_msg_set(uint8 lang_id, uint16 num, char *ptr) { + if (msg_table[lang_id][num] != NULL) + aFree(msg_table[lang_id][num]); + msg_table[lang_id][num] = aStrdup(ptr); +} + void do_init_atcommand(void) { atcommand_doload(); } diff --git a/src/map/atcommand.h b/src/map/atcommand.h index e1f02e828c7..210bcb9ce60 100644 --- a/src/map/atcommand.h +++ b/src/map/atcommand.h @@ -12,11 +12,20 @@ struct map_session_data; //Note: The range is unlimited unless this define is set. //#define AUTOLOOT_DISTANCE AREA_SIZE +#define MAX_MSG 1500 +#define msg_txt(sd, msg_number) atcommand_msgsd((sd), (msg_number)) + //global var extern char atcommand_symbol; extern char charcommand_symbol; extern int atcmd_binding_count; +/** + * msg_table[lang_id][msg_id] + **/ +extern char*** msg_table; +extern uint8 max_message_table; + typedef enum { COMMAND_ATCOMMAND = 1, COMMAND_CHARCOMMAND = 2, @@ -42,4 +51,12 @@ struct atcmd_binding_data { struct atcmd_binding_data** atcmd_binding; struct atcmd_binding_data* get_atcommandbind_byname(const char* name); +const char* atcommand_msg(int msg_number); +const char* atcommand_msgsd(struct map_session_data *sd, int msg_number); +void atcommand_expand_message_table(void); +void atcommand_msg_set(uint8 lang_id, uint16 num, char *ptr); + +bool msg_config_read(const char *cfg_name, bool allow_override); +void do_final_msg(void); + #endif /* _ATCOMMAND_H_ */ diff --git a/src/map/map.c b/src/map/map.c index 13ebb5e7d23..b4b228213aa 100644 --- a/src/map/map.c +++ b/src/map/map.c @@ -15,6 +15,7 @@ #include "../common/cli.h" #include "../common/ers.h" +#include "atcommand.h" #include "map.h" #include "path.h" #include "chrif.h" @@ -53,6 +54,9 @@ char map_server_db[32] = "ragnarok"; Sql* mmysql_handle; Sql* qsmysql_handle; /// For query_sql +const char *default_lang_str = "English"; +uint8 default_lang_id = 0; + int db_use_sqldbs = 0; char buyingstores_db[32] = "buyingstores"; char buyingstore_items_db[32] = "buyingstore_items"; @@ -102,8 +106,6 @@ static int block_free_count = 0, block_free_lock = 0; static struct block_list *bl_list[BL_LIST_MAX]; static int bl_list_count = 0; -#define MAP_MAX_MSG 1500 - struct map_data map[MAX_MAP_PER_SERVER]; int map_num = 0; int map_port=0; @@ -146,6 +148,7 @@ char motd_txt[256] = "conf/motd.txt"; char help_txt[256] = "conf/help.txt"; char help2_txt[256] = "conf/help2.txt"; char charhelp_txt[256] = "conf/charhelp.txt"; +char language_conf[256] = "conf/languages.conf"; char wisp_server_name[NAME_LENGTH] = "Server"; // can be modified in char-server configuration file @@ -3882,6 +3885,8 @@ int map_config_read(char *cfgName) console_msg_log = atoi(w2);//[Ind] else if (strcmpi(w1, "console_log_filepath") == 0) safestrncpy(console_log_filepath, w2, sizeof(console_log_filepath)); + else if (strcmpi(w1, "language_conf") == 0) + safestrncpy(language_conf, w2, sizeof(language_conf)); else if (strcmpi(w1, "import") == 0) map_config_read(w2); else @@ -4503,6 +4508,7 @@ void display_helpscreen(bool do_exit) ShowInfo(" --grf-path \t\tAlternative GRF path configuration.\n"); ShowInfo(" --inter-config \t\tAlternative inter-server configuration.\n"); ShowInfo(" --log-config \t\tAlternative logging configuration.\n"); + ShowInfo(" --generate-translations \tCreates 'outfile' (or default is './lang/generated_translations.pot') file with all translateable strings from scripts, server terminates afterwards."); if( do_exit ) exit(EXIT_SUCCESS); } @@ -4515,86 +4521,6 @@ void set_server_type(void) SERVER_TYPE = ATHENA_SERVER_MAP; } -/*====================================================== - * Message System - *------------------------------------------------------*/ -struct msg_data { - char* msg[MAP_MAX_MSG]; -}; -struct msg_data *map_lang2msgdb(uint8 lang){ - return (struct msg_data*)idb_get(map_msg_db, lang); -} - -void map_do_init_msg(void){ - int test=0, i=0, size; - char * listelang[] = { - MSG_CONF_NAME_EN, //default - MSG_CONF_NAME_RUS, - MSG_CONF_NAME_SPN, - MSG_CONF_NAME_GRM, - MSG_CONF_NAME_CHN, - MSG_CONF_NAME_MAL, - MSG_CONF_NAME_IDN, - MSG_CONF_NAME_FRN, - MSG_CONF_NAME_POR, - MSG_CONF_NAME_THA - }; - - map_msg_db = idb_alloc(DB_OPT_BASE); - size = ARRAYLENGTH(listelang); //avoid recalc - while(test!=-1 && size>i){ //for all enable lang +(English default) - test = msg_checklangtype(i,false); - if(test == 1) msg_config_read(listelang[i],i); //if enabled read it and assign i to langtype - i++; - } -} -void map_do_final_msg(void){ - DBIterator *iter = db_iterator(map_msg_db); - struct msg_data *mdb; - - for (mdb = (struct msg_data *)dbi_first(iter); dbi_exists(iter); mdb = (struct msg_data *)dbi_next(iter)) { - _do_final_msg(MAP_MAX_MSG,mdb->msg); - aFree(mdb); - } - dbi_destroy(iter); - map_msg_db->destroy(map_msg_db, NULL); -} -void map_msg_reload(void){ - map_do_final_msg(); //clear data - map_do_init_msg(); -} -int map_msg_config_read(char *cfgName, int lang){ - struct msg_data *mdb; - - if( (mdb = map_lang2msgdb(lang)) == NULL ) - CREATE(mdb, struct msg_data, 1); - else - idb_remove(map_msg_db, lang); - idb_put(map_msg_db, lang, mdb); - - if(_msg_config_read(cfgName,MAP_MAX_MSG,mdb->msg)!=0){ //an error occur - idb_remove(map_msg_db, lang); //@TRYME - aFree(mdb); - } - return 0; -} -const char* map_msg_txt(struct map_session_data *sd, int msg_number){ - struct msg_data *mdb; - uint8 lang = 0; //default - if(sd && sd->langtype) lang = sd->langtype; - - if( (mdb = map_lang2msgdb(lang)) != NULL){ - const char *tmp = _msg_txt(msg_number,MAP_MAX_MSG,mdb->msg); - if(strcmp(tmp,"??")) //to verify result - return tmp; - ShowDebug("Message #%d not found for langtype %d.\n",msg_number,lang); - } - ShowDebug("Selected langtype %d not loaded, trying fallback...\n",lang); - if(lang != 0 && (mdb = map_lang2msgdb(0)) != NULL) //fallback - return _msg_txt(msg_number,MAP_MAX_MSG,mdb->msg); - return "??"; -} - /// Called when a terminate signal is received. void do_shutdown(void) @@ -4628,22 +4554,10 @@ int do_init(int argc, char *argv[]) BATTLE_CONF_FILENAME = "conf/battle_athena.conf"; ATCOMMAND_CONF_FILENAME = "conf/atcommand_athena.conf"; SCRIPT_CONF_NAME = "conf/script_athena.conf"; + MSG_CONF_NAME_EN = "conf/msg_conf/map_msg.conf"; GRF_PATH_FILENAME = "conf/grf-files.txt"; safestrncpy(console_log_filepath, "./log/map-msg_log.log", sizeof(console_log_filepath)); - /* Multilanguage */ - MSG_CONF_NAME_EN = "conf/msg_conf/map_msg.conf"; // English (default) - MSG_CONF_NAME_RUS = "conf/msg_conf/map_msg_rus.conf"; // Russian - MSG_CONF_NAME_SPN = "conf/msg_conf/map_msg_spn.conf"; // Spanish - MSG_CONF_NAME_GRM = "conf/msg_conf/map_msg_grm.conf"; // German - MSG_CONF_NAME_CHN = "conf/msg_conf/map_msg_chn.conf"; // Chinese - MSG_CONF_NAME_MAL = "conf/msg_conf/map_msg_mal.conf"; // Malaysian - MSG_CONF_NAME_IDN = "conf/msg_conf/map_msg_idn.conf"; // Indonesian - MSG_CONF_NAME_FRN = "conf/msg_conf/map_msg_frn.conf"; // French - MSG_CONF_NAME_POR = "conf/msg_conf/map_msg_por.conf"; // Brazilian Portuguese - MSG_CONF_NAME_THA = "conf/msg_conf/map_msg_tha.conf"; // Thai - /* Multilanguage */ - // Default map safestrncpy(map_default.mapname, "prontera", MAP_NAME_LENGTH); map_default.x = 156; @@ -4681,6 +4595,7 @@ int do_init(int argc, char *argv[]) chrif_setip(ip_str); } + msg_config_read(MSG_CONF_NAME_EN, false); battle_config_read(BATTLE_CONF_FILENAME); script_config_read(SCRIPT_CONF_NAME); inter_config_read(INTER_CONF_NAME); @@ -4714,8 +4629,7 @@ int do_init(int argc, char *argv[]) add_timer_func_list(map_clearflooritem_timer, "map_clearflooritem_timer"); add_timer_func_list(map_removemobs_timer, "map_removemobs_timer"); add_timer_interval(gettick()+1000, map_freeblock_timer, 0, 0, 60*1000); - - map_do_init_msg(); + do_init_atcommand(); do_init_battle(); do_init_instance(); @@ -4746,6 +4660,14 @@ int do_init(int argc, char *argv[]) npc_event_do_oninit(); // Init npcs (OnInit) + if (lang_export_fp) { + ShowInfo("Lang exported to '"CL_WHITE"%s"CL_RESET"'\n", lang_export_file); + fclose(lang_export_fp); + aFree(lang_export_file); + lang_export_file= NULL; + lang_export_fp = NULL; + } + if (battle_config.pk_mode) ShowNotice("Server is running on '"CL_WHITE"PK Mode"CL_RESET"'.\n"); diff --git a/src/map/map.h b/src/map/map.h index 6bfa24d2416..daad808b2a0 100644 --- a/src/map/map.h +++ b/src/map/map.h @@ -9,7 +9,6 @@ #include "../common/mmo.h" #include "../common/mapindex.h" #include "../common/db.h" -#include "../common/msg_conf.h" #include "../config/core.h" @@ -26,14 +25,6 @@ enum E_MAPSERVER_ST { MAPSERVER_ST_LAST }; -#define msg_config_read(cfgName,isnew) map_msg_config_read(cfgName,isnew) -#define msg_txt(sd,msg_number) map_msg_txt(sd,msg_number) -#define do_final_msg() map_do_final_msg() -int map_msg_config_read(char *cfgName,int lang); -const char* map_msg_txt(struct map_session_data *sd,int msg_number); -void map_do_final_msg(void); -void map_msg_reload(void); - #define MAX_NPC_PER_MAP 512 #define AREA_SIZE battle_config.area_size #define DAMAGELOG_SIZE 30 @@ -731,6 +722,7 @@ extern char motd_txt[]; extern char help_txt[]; extern char help2_txt[]; extern char charhelp_txt[]; +extern char language_conf[]; extern char wisp_server_name[]; @@ -906,16 +898,8 @@ extern char *ATCOMMAND_CONF_FILENAME; extern char *SCRIPT_CONF_NAME; extern char *MSG_CONF_NAME_EN; extern char *GRF_PATH_FILENAME; -//Other languages supported -char *MSG_CONF_NAME_RUS; -char *MSG_CONF_NAME_SPN; -char *MSG_CONF_NAME_GRM; -char *MSG_CONF_NAME_CHN; -char *MSG_CONF_NAME_MAL; -char *MSG_CONF_NAME_IDN; -char *MSG_CONF_NAME_FRN; -char *MSG_CONF_NAME_POR; -char *MSG_CONF_NAME_THA; +extern FILE *lang_export_fp; +extern char *lang_export_file; //Useful typedefs from jA [Skotlex] typedef struct map_session_data TBL_PC; @@ -958,6 +942,9 @@ extern Sql* mmysql_handle; extern Sql* qsmysql_handle; extern Sql* logmysql_handle; +extern const char *default_lang_str; +extern uint8 default_lang_id; + extern char buyingstores_db[32]; extern char buyingstore_items_db[32]; extern char item_db_db[32]; diff --git a/src/map/npc.c b/src/map/npc.c index 7f9f224799c..8d6331a91e6 100644 --- a/src/map/npc.c +++ b/src/map/npc.c @@ -2906,7 +2906,9 @@ static const char* npc_parse_script(char* w1, char* w2, char* w3, char* w4, cons if( end == NULL ) return NULL;// (simple) parse error, don't continue + parser_current_npc_name = w3; script = parse_script(script_start, filepath, strline(buffer,script_start-buffer), SCRIPT_USE_LABEL_DB); + parser_current_npc_name = NULL; label_list = NULL; label_list_num = 0; if( script ) @@ -3613,7 +3615,9 @@ static const char* npc_parse_function(char* w1, char* w2, char* w3, char* w4, co if( end == NULL ) return NULL;// (simple) parse error, don't continue + parser_current_npc_name = w3; script = parse_script(script_start, filepath, strline(buffer,start-buffer), SCRIPT_RETURN_EMPTY_SCRIPT); + parser_current_npc_name = NULL; if( script == NULL )// parse error, continue return end; diff --git a/src/map/pc.c b/src/map/pc.c index d411e35eb79..90cc1efc5ea 100755 --- a/src/map/pc.c +++ b/src/map/pc.c @@ -1338,9 +1338,7 @@ void pc_reg_received(struct map_session_data *sd) sd->change_level_3rd = pc_readglobalreg(sd, add_str("jobchange_level_3rd")); sd->die_counter = pc_readglobalreg(sd, add_str("PC_DIE_COUNTER")); - sd->langtype = pc_readaccountreg(sd, add_str("#langtype")); - if (msg_checklangtype(sd->langtype,true) < 0) - sd->langtype = 0; //invalid langtype reset to default + pc_set_language(sd, pc_readaccountreg(sd, add_str("#langtype"))); // Cash shop sd->cashPoints = pc_readaccountreg(sd, add_str("#CASHPOINTS")); @@ -12025,6 +12023,33 @@ void pc_show_questinfo_reinit(struct map_session_data *sd) { #endif } +/** + * Set language for player + * @param sd + * @param lang_id + * @return True if changed successfully, False if failed. + **/ +bool pc_set_language(struct map_session_data *sd, uint8 lang_id) { + nullpo_retr(false, sd); + + if (lang_id < max_lang_id) { + char output[CHAT_SIZE_MAX]; + pc_setaccountreg(sd, add_str("#langtype"), lang_id); + sd->lang_id = lang_id; + sprintf(output,msg_txt(sd,461),languages[lang_id]); // Language is now set to %s. + clif_displaymessage(sd->fd,output); + return true; + } + + // On invalid + if (sd->lang_id != default_lang_id) { + pc_setaccountreg(sd, add_str("#langtype"), lang_id); + sd->lang_id = lang_id; + } + + clif_displaymessage(sd->fd,msg_txt(sd,462)); // This language is currently disabled. + return false; +} /*========================================== * pc Init/Terminate diff --git a/src/map/pc.h b/src/map/pc.h index 8bed5a3718e..b68302e4052 100644 --- a/src/map/pc.h +++ b/src/map/pc.h @@ -267,7 +267,7 @@ struct map_session_data { unsigned int permissions;/* group permissions */ int count_rewarp; //count how many time we being rewarped - int langtype; + uint8 lang_id; uint32 packet_ver; // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04, 11: 21sept04, 12: 18oct04, 13: 25oct04 ... 18 struct mmo_charstatus status; @@ -1234,6 +1234,7 @@ bool pc_is_same_equip_index(enum equip_index eqi, short *equip_index, short inde int pc_autotrade_timer(int tid, unsigned int tick, int id, intptr_t data); void pc_validate_skill(struct map_session_data *sd); +bool pc_set_language(struct map_session_data *sd, uint8 lang_id); void pc_show_questinfo(struct map_session_data *sd); void pc_show_questinfo_reinit(struct map_session_data *sd); diff --git a/src/map/script.c b/src/map/script.c index 9441d781fe6..3598839f5ec 100644 --- a/src/map/script.c +++ b/src/map/script.c @@ -12,6 +12,7 @@ #endif #include "../common/cbasetypes.h" +#include "../common/conf.h" #include "../common/malloc.h" #include "../common/md5calc.h" #include "../common/nullpo.h" @@ -156,6 +157,9 @@ static char *str_buf; static int str_size = 0; // size of the buffer static int str_pos = 0; // next position to be assigned +char **languages; +char **lang_motd_txt; +char **lang_help_txt; // Using a prime number for SCRIPT_HASH_SIZE should give better distributions #define SCRIPT_HASH_SIZE 1021 @@ -176,6 +180,12 @@ static int buildin_set_ref = 0; static int buildin_callsub_ref = 0; static int buildin_callfunc_ref = 0; static int buildin_getelementofarray_ref = 0; +static int buildin_mes_offset = 0; +static int buildin_select_offset = 0; +static int buildin_menu_offset = 0; +static int buildin_dispbottom_offset = 0; +static int buildin_message_offset = 0; +static int buildin_lang_macro_offset = 0; // Caches compiled autoscript item code. // Note: This is not cleared when reloading itemdb. @@ -234,9 +244,14 @@ static struct { int count; int flag; struct linkdb_node *case_label; - } curly[256]; // Information right parenthesis - int curly_count; // The number of right brackets - int index; // Number of the syntax used in the script + } curly[256]; // Information right parenthesis + int curly_count; // The number of right brackets + int index; // Number of the syntax used in the script + int last_func; // buildin index of the last parsed function + unsigned int nested_call; // Don't really know what to call this + bool lang_macro_active; + DBMap *strings; // string map parsed (used when exporting strings only) + DBMap *translation_db; // non-null if this NPC has any translated strings to be linked } syntax; const char* parse_curly_close(const char* p); @@ -364,6 +379,33 @@ enum { MF_SKILL_DAMAGE //60 }; +static inline void script_string_buf_ensure(struct script_string_buf *buf, size_t ensure) +{ + if (buf->pos + ensure >= buf->size) { + do { + buf->size += 512; + } while (buf->pos+ensure >= buf->size); + RECREATE(buf->ptr, char, buf->size); + } +} + +static inline void script_string_buf_addb(struct script_string_buf *buf, uint8 b) +{ + if (buf->pos + 1 >= buf->size) { + buf->size += 512; + RECREATE(buf->ptr, char, buf->size); + } + + buf->ptr[buf->pos++] = b; +} + +static inline void script_string_buf_destroy(struct script_string_buf *buf) +{ + if (buf->ptr) + aFree(buf->ptr); + memset(buf, 0, sizeof(struct script_string_buf)); +} + const char* script_op2name(int op) { #define RETURN_OP_NAME(type) case type: return #type @@ -382,6 +424,7 @@ const char* script_op2name(int op) RETURN_OP_NAME(C_RETINFO); RETURN_OP_NAME(C_USERFUNC); RETURN_OP_NAME(C_USERFUNC_POS); + RETURN_OP_NAME(C_LSTR); // operators RETURN_OP_NAME(C_OP3); @@ -912,15 +955,32 @@ static int add_word(const char* p) static const char* parse_callfunc(const char* p, int require_paren, int is_custom) { - const char* p2; - const char* arg=NULL; + const char *p2; + const char *arg = NULL; int func; + bool nested_call = false, macro = false; func = add_word(p); - if( str_data[func].type == C_FUNC ){ - // buildin function - add_scriptl(func); - add_scriptc(C_ARG); + if (str_data[func].type == C_FUNC) { + /** only when unset (-1), valid values are >= 0 **/ + if (syntax.last_func == -1 ) + syntax.last_func = str_data[func].val; + else { //Nested function call + syntax.nested_call++; + nested_call = true; + + if (str_data[func].val == buildin_lang_macro_offset) { + syntax.lang_macro_active = true; + macro = true; + } + } + + if (!macro) { + // buildin function + add_scriptl(func); + add_scriptc(C_ARG); + } + arg = buildin_func[str_data[func].val].arg; } else if( str_data[func].type == C_USERFUNC || str_data[func].type == C_USERFUNC_POS ){ // script defined function @@ -1002,8 +1062,19 @@ const char* parse_callfunc(const char* p, int require_paren, int is_custom) if( *p != ')' ) disp_error_message("parse_callfunc: expected ')' to close argument list",p); ++p; + + if (str_data[func].val == buildin_lang_macro_offset) + syntax.lang_macro_active = false; } - add_scriptc(C_FUNC); + + if (nested_call) + syntax.nested_call--; + + if (!syntax.nested_call) + syntax.last_func = -1; + + if (!macro) + add_scriptc(C_FUNC); return p; } @@ -1199,6 +1270,25 @@ bool is_number(const char *p) { return false; } +/** + * + **/ +int script_string_dup(char *str) +{ + size_t len = strlen(str); + int pos = string_list_pos; + + while(pos + len + 1 >= string_list_size) { + string_list_size += (1024 * 1024) / 2; + RECREATE(string_list, char, string_list_size); + } + + safestrncpy(string_list + pos, str, len + 1); + string_list_pos += len + 1; + + return pos; +} + /*========================================== * Analysis section *------------------------------------------*/ @@ -1240,28 +1330,147 @@ const char* parse_simpleexpr(const char *p) add_scripti((int)i); p=np; } else if(*p=='"'){ - add_scriptc(C_STR); - p++; - while( *p && *p != '"' ){ - if( (unsigned char)p[-1] <= 0x7e && *p == '\\' ) - { - char buf[8]; - size_t len = skip_escaped_c(p) - p; - size_t n = sv_unescape_c(buf, p, len); - if( n != 1 ) - ShowDebug("parse_simpleexpr: unexpected length %d after unescape (\"%.*s\" -> %.*s)\n", (int)n, (int)len, p, (int)n, buf); - p += len; - add_scriptb(*buf); - continue; + struct string_translation *st = NULL; + const char *start_point = p; + bool duplicate = true; + struct script_string_buf *sbuf = &parse_simpleexpr_str; + + do { + p++; + while(*p && *p != '"') { + if ((unsigned char)p[-1] <= 0x7e && *p == '\\') { + char buf[8]; + size_t len = skip_escaped_c(p) - p; + size_t n = sv_unescape_c(buf, p, len); + if (n != 1) + ShowDebug("parse_simpleexpr: unexpected length %d after unescape (\"%.*s\" -> %.*s)\n", (int)n, (int)len, p, (int)n, buf); + p += len; + script_string_buf_addb(sbuf, *buf); + continue; + } else if( *p == '\n' ) { + disp_error_message("parse_simpleexpr: unexpected newline @ string",p); + } + script_string_buf_addb(sbuf, *p++); + } + if (!*p) + disp_error_message("parse_simpleexpr: unexpected eof @ string",p); + p++; //'"' + p = skip_space(p); + } while(*p && *p == '"'); + + script_string_buf_addb(sbuf, 0); + + if (!(syntax.translation_db && (st = strdb_get(syntax.translation_db, sbuf->ptr))) ) { + add_scriptc(C_STR); + + if (script_pos + sbuf->pos >= script_size) { + do { + script_size += SCRIPT_BLOCK_SIZE; + } while(script_pos + sbuf->pos >= script_size ); + RECREATE(script_buf, unsigned char, script_size); } - else if( *p == '\n' ) - disp_error_message("parse_simpleexpr: unexpected newline @ string",p); - add_scriptb(*p++); - } - if(!*p) - disp_error_message("parse_simpleexpr: unexpected eof @ string",p); - add_scriptb(0); - p++; //'"' + + memcpy(script_buf + script_pos, sbuf->ptr, sbuf->pos); + script_pos += sbuf->pos; + } else { + int expand = sizeof(int) + sizeof(uint8); + unsigned char j; + unsigned int st_cursor = 0; + + add_scriptc(C_LSTR); + + expand += (sizeof(char*) + sizeof(uint8)) * st->translations; + + while(script_pos + expand >= script_size) { + script_size += SCRIPT_BLOCK_SIZE; + RECREATE(script_buf, unsigned char, script_size); + } + + *((int *)(&script_buf[script_pos])) = st->string_id; + *((uint8 *)(&script_buf[script_pos + sizeof(int)])) = st->translations; + + script_pos += sizeof(int) + sizeof(uint8); + + for(j = 0; j < st->translations; j++) { + *((uint8 *)(&script_buf[script_pos])) = RBUFB(st->buf, st_cursor); + *((char **)(&script_buf[script_pos+sizeof(uint8)])) = &st->buf[st_cursor + sizeof(uint8)]; + script_pos += sizeof(char*) + sizeof(uint8); + st_cursor += sizeof(uint8); + while(st->buf[st_cursor++]); + st_cursor += sizeof(uint8); + } + } + + //* When exporting we don't know what is a translation and what isn't + if (lang_export_fp && sbuf->pos > 1) { //sbuf->pos will always be at least 1 because of the '\0' + if (!syntax.strings) { + syntax.strings = strdb_alloc(DB_OPT_DUP_KEY|DB_OPT_ALLOW_NULL_DATA, 0); + } + + if (!strdb_exists(syntax.strings, sbuf->ptr)) { + strdb_put(syntax.strings, sbuf->ptr, NULL); + duplicate = false; + } + } + + if (lang_export_fp && !duplicate && + (((syntax.last_func == buildin_mes_offset || + syntax.last_func == buildin_select_offset || + syntax.last_func == buildin_menu_offset || + syntax.last_func == buildin_message_offset || + syntax.last_func == buildin_dispbottom_offset) && !syntax.nested_call + ) || syntax.lang_macro_active)) { + const char *line_start = start_point; + const char *line_end = start_point; + struct script_string_buf *lbuf = &lang_export_line_buf; + struct script_string_buf *ubuf = &lang_export_unescaped_buf; + size_t line_length, cursor; + + while(line_start > parser_current_src) { + if (*line_start != '\n') + line_start--; + else + break; + } + + while(*line_end != '\n' && *line_end != '\0') + line_end++; + + line_length = (size_t)(line_end - line_start); + + if (line_length > 0) { + script_string_buf_ensure(lbuf,line_length + 1); + + memcpy(lbuf->ptr, line_start, line_length); + lbuf->pos = line_length; + script_string_buf_addb(lbuf, 0); + + normalize_name(lbuf->ptr, "\r\n\t "); + } + + for(cursor = 0; cursor < sbuf->pos; cursor++) { + if (sbuf->ptr[cursor] == '"') + script_string_buf_addb(ubuf, '\\'); + script_string_buf_addb(ubuf, sbuf->ptr[cursor]); + } + script_string_buf_addb(ubuf, 0); + + fprintf(lang_export_fp, "#: %s\n" + "# %s\n" + "msgctxt \"%s\"\n" + "msgid \"%s\"\n" + "msgstr \"\"\n", + parser_current_file ? parser_current_file : "Unknown File", + lbuf->ptr, + parser_current_npc_name ? parser_current_npc_name : "Unknown NPC", + ubuf->ptr + ); + + lbuf->pos = 0; + ubuf->pos = 0; + } + + sbuf->pos = 0; } else { int l; const char* pv; @@ -2151,8 +2360,14 @@ static void add_buildin_func(void) if (!strcmp(buildin_func[i].name, "set")) buildin_set_ref = n; else if (!strcmp(buildin_func[i].name, "callsub")) buildin_callsub_ref = n; else if (!strcmp(buildin_func[i].name, "callfunc")) buildin_callfunc_ref = n; - else if( !strcmp(buildin_func[i].name, "getelementofarray") ) buildin_getelementofarray_ref = n; - } + else if( !strcmp(buildin_func[i].name, "getelementofarray")) buildin_getelementofarray_ref = n; + else if( !strcmp(buildin_func[i].name, "mes")) buildin_mes_offset = i; + else if( !strcmp(buildin_func[i].name, "select")) buildin_select_offset = i; + else if( !strcmp(buildin_func[i].name, "menu")) buildin_menu_offset = i; + else if( !strcmp(buildin_func[i].name, "dispbottom")) buildin_dispbottom_offset = i; + else if( !strcmp(buildin_func[i].name, "message")) buildin_message_offset = i; + else if( !strcmp(buildin_func[i].name, "_")) buildin_lang_macro_offset = i; + } } } @@ -2333,14 +2548,28 @@ struct script_code* parse_script(const char *src,const char *file,int line,int o if( src == NULL ) return NULL;// empty script + if (parse_cleanup_timer_id == INVALID_TIMER) + parse_cleanup_timer_id = add_timer(gettick() + 10, script_parse_cleanup_timer, 0, 0); + + if (syntax.strings) // Used only when generating translation file + db_destroy(syntax.strings); + memset(&syntax,0,sizeof(syntax)); - if(first){ + if (first) { add_buildin_func(); read_constdb(); script_hardcoded_constants(); first=0; } + syntax.last_func = -1; // As valid values are >= 0 + if (parser_current_npc_name) { + if (!translation_db) + script_load_translations(); + if (translation_db) + syntax.translation_db = strdb_get(translation_db, parser_current_npc_name); + } + script_buf=(unsigned char *)aMalloc(SCRIPT_BLOCK_SIZE*sizeof(unsigned char)); script_pos=0; script_size=SCRIPT_BLOCK_SIZE; @@ -4054,14 +4283,40 @@ void run_script_main(struct script_state *st) push_str(stack,C_CONSTSTR,(char*)(st->script->script_buf+st->pos)); while(st->script->script_buf[st->pos++]); break; + case C_LSTR: + { + int string_id = *((int *)(&st->script->script_buf[st->pos])); + uint8 translations = *((uint8 *)(&st->script->script_buf[st->pos+sizeof(int)])); + struct map_session_data *lsd = NULL; + + st->pos += sizeof(int) + sizeof(uint8); + if( (!st->rid || !(lsd = map_id2sd(st->rid)) || !lsd->lang_id) && !default_lang_id ) + script_pushconststr(st, string_list + string_id); + else { + uint8 k, wlang_id = lsd ? lsd->lang_id : default_lang_id; + int offset = st->pos; + + for(k = 0; k < translations; k++) { + uint8 lang_id = *(uint8 *)(&st->script->script_buf[offset]); + offset += sizeof(uint8); + if (lang_id == wlang_id) + break; + offset += sizeof(char*); + } + + script_pushconststr(st, (k == translations) ? string_list + string_id : *(char**)(&st->script->script_buf[offset])); + } + st->pos += ((sizeof(char*) + sizeof(uint8)) * translations); + } + break; case C_FUNC: run_func(st); - if(st->state==GOTO){ + if (st->state == GOTO) { st->state = RUN; - if( !st->freeloop && gotocount>0 && (--gotocount)<=0 ){ - ShowError("script:run_script_main: infinity loop !\n"); + if (!st->freeloop && gotocount>0 && (--gotocount)<=0) { + ShowError("run_script: infinity loop !\n"); script_reportsrc(st); - st->state=END; + st->state = END; } } break; @@ -4653,7 +4908,412 @@ void do_final_script() { aFree(logThreadData.entry); #endif + + script_clear_translations(false); + + script_parser_clean_leftovers(); + + if (lang_export_file) + aFree(lang_export_file); +} + +/** + * + **/ +uint8 script_add_language(const char *name) +{ + uint8 lang_id = max_lang_id; + + RECREATE(languages, char *, ++max_lang_id); + RECREATE(lang_motd_txt, char *, max_lang_id); + RECREATE(lang_help_txt, char *, max_lang_id); + + ShowInfo("Lang: '"CL_WHITE"%d"CL_RESET"': '"CL_WHITE"%s"CL_RESET"'.\n", lang_id, name); + languages[lang_id] = aStrdup(name); + lang_motd_txt[lang_id] = NULL; + lang_help_txt[lang_id] = NULL; + + return lang_id; +} + +static void script_add_motd_language(uint8 lang_id, const char *filepath) { + lang_motd_txt[lang_id] = aStrdup(filepath); } + +static void script_add_help_language(uint8 lang_id, const char *filepath) { + lang_help_txt[lang_id] = aStrdup(filepath); +} + +/** + * Parses conf/translations.conf file + **/ +void script_load_translations(void) { + config_t translations_conf; + const char *config_filename = language_conf; + config_setting_t *languages_t = NULL; + int i, size; + uint32 total = 0; + uint8 k; + + translation_db = strdb_alloc(DB_OPT_DUP_KEY, NAME_LENGTH * 2 + 1); + + if (languages) { + for(i = 0; i < max_lang_id; i++) + aFree(languages[i]); + aFree(languages); + } + if (lang_motd_txt) { + for(i = 0; i < max_lang_id; i++) + aFree(lang_motd_txt[i]); + aFree(lang_motd_txt); + } + if (lang_help_txt) { + for(i = 0; i < max_lang_id; i++) + aFree(lang_help_txt[i]); + aFree(lang_help_txt); + } + languages = NULL; + lang_motd_txt = NULL; + lang_help_txt = NULL; + max_lang_id = 0; + + script_add_language("English"); // 0 is default, which is whatever is in the NPC files hardcoded (in our case, English) + + if (conf_read_file(&translations_conf, config_filename)) { + ShowError("script_load_translations: Can't read '%s'\n", config_filename); + return; + } + + if (config_lookup_string(&translations_conf, "default_language", &default_lang_str)) { + ShowInfo("Default language '%s'.\n", default_lang_str); + } + + if (!(languages_t = config_lookup(&translations_conf, "languages")) ) { + ShowError("script_load_translations: Invalid 'languages' format on '%s'\n", config_filename); + return; + } + + if (string_list) + aFree(string_list); + + string_list = NULL; + string_list_pos = 0; + string_list_size = 0; + + size = config_setting_length(languages_t); + + for(i = 0; i < size; i++) { + uint8 j, lang_id = 0; + int count = 0; + config_setting_t *language_t = NULL, *lang_files = NULL; + const char *lang_name = NULL, *str = NULL; + + if (!(language_t = config_setting_get_elem(languages_t, i))) + continue; + + lang_name = config_setting_name(language_t); + lang_id = script_add_language(lang_name); + + // MOTD + if (config_setting_lookup_string(language_t, "motd", &str)) { + script_add_motd_language(lang_id, str); + //ShowInfo("Added MOTD for %s: '"CL_WHITE"%s"CL_RESET"'\n", lang_name, str); + } + + // @help + if (config_setting_lookup_string(language_t, "help", &str)) { + script_add_help_language(lang_id, str); + //ShowInfo("Added @help for %s: '"CL_WHITE"%s"CL_RESET"'\n", lang_name, str); + } + + // Translations + if (!(lang_files = config_setting_get_member(language_t, "lang")) ) { + ShowError("script_load_translations: Invalid 'lang' format on '%s'\n", config_filename); + continue; + } + + count = config_setting_length(lang_files); + if (!count) { + ShowWarning("script_load_translations: Language '%s' is defined with no translation file. Skipping...\n", lang_name); + continue; + } + + for (j = 0; j < count; j++) { + const char *translation_file = config_setting_get_string_elem(lang_files, j);; + total += script_load_translation(lang_id, translation_file); + } + } + + if (total) { + DBIterator *main_iter; + DBIterator *sub_iter; + DBMap *string_db; + struct string_translation *st = NULL; + uint32 j = 0; + + CREATE(translation_buf, char *, total); + translation_buf_size = total; + + main_iter = db_iterator(translation_db); + + for(string_db = dbi_first(main_iter); dbi_exists(main_iter); string_db = dbi_next(main_iter) ) { + sub_iter = db_iterator(string_db); + + for(st = dbi_first(sub_iter); dbi_exists(sub_iter); st = dbi_next(sub_iter) ) { + translation_buf[j++] = st->buf; + } + + dbi_destroy(sub_iter); + } + + dbi_destroy(main_iter); + } + + for(k = 0; k < max_lang_id; k++) { + if( !strcmpi(languages[k], default_lang_str) ) { + break; + } + } + + if (k == max_lang_id) { + ShowError("script_load_translations: Map server 'default_language' setting '%s' is not a loaded language.\n", default_lang_str); + default_lang_id = 0; + } else + default_lang_id = k; + + config_destroy(&translations_conf); +} + +/** + * Parses a individual translation file + **/ +uint32 script_load_translation(uint8 lang_id, const char *file) { + uint32 translations = 0; + char line[1024]; + char msgctxt[NAME_LENGTH * 2 + 1] = { 0 }; + DBMap *string_db; + size_t i; + FILE *fp; + struct script_string_buf msgid = { 0 }, msgstr = { 0 }; + + if (file == NULL) + return 0; + + if (!(fp = fopen(file,"rb")) ) { + ShowError("script_load_translation: Failed to open '%s' for reading.\n", file); + return 0; + } + + if (lang_id >= max_message_table) + atcommand_expand_message_table(); + + while(fgets(line, sizeof(line), fp)) { + size_t len = strlen(line), cursor = 0; + + if (len <= 1) + continue; + + if (line[0] == '#') + continue; + + if (strncasecmp(line,"msgctxt \"", 9) == 0) { + msgctxt[0] = '\0'; + for(i = 9; i < len - 2; i++) { + if (line[i] == '\\' && line[i+1] == '"') { + msgctxt[cursor] = '"'; + i++; + } else + msgctxt[cursor] = line[i]; + if (++cursor >= sizeof(msgctxt) - 1) + break; + } + msgctxt[cursor] = '\0'; + } else if (strncasecmp(line, "msgid \"", 7) == 0) { + msgid.pos = 0; + for(i = 7; i < len - 2; i++) { + if (line[i] == '\\' && line[i+1] == '"') { + script_string_buf_addb(&msgid, '"'); + i++; + } else + script_string_buf_addb(&msgid, line[i]); + } + script_string_buf_addb(&msgid,0); + } else if (len > 9 && line[9] != '"' && strncasecmp(line, "msgstr \"",8) == 0) { + msgstr.pos = 0; + for(i = 8; i < len - 2; i++) { + if (line[i] == '\\' && line[i+1] == '"') { + script_string_buf_addb(&msgstr, '"'); + i++; + } else + script_string_buf_addb(&msgstr, line[i]); + } + script_string_buf_addb(&msgstr,0); + } + + if( msgctxt[0] && msgid.pos > 1 && msgstr.pos > 1 ) { + size_t msgstr_len = msgstr.pos; + unsigned int inner_len = 1 + (uint32)msgstr_len + 1; //uint8 lang_id + msgstr_len + '\0' + + if (strcasecmp(msgctxt, "map_msg.conf") == 0) { + int k; + + for(k = 0; k < MAX_MSG; k++) { + if (msgid.ptr != NULL && msg_table[0][k] && strcmpi(msg_table[0][k],msgid.ptr) == 0) { + atcommand_msg_set(lang_id,k,msgstr.ptr); + break; + } + } + } else { + struct string_translation *st = NULL; + + if (!( string_db = strdb_get(translation_db, msgctxt))) { + string_db = strdb_alloc(DB_OPT_DUP_KEY, 0); + + strdb_put(translation_db, msgctxt, string_db); + } + + if (!(st = strdb_get(string_db, msgid.ptr))) { + CREATE(st, struct string_translation, 1); + + st->string_id = script_string_dup(msgid.ptr); + + strdb_put(string_db, msgid.ptr, st); + } + + RECREATE(st->buf, char, st->len + inner_len); + + WBUFB(st->buf, st->len) = lang_id; + safestrncpy((char*)WBUFP(st->buf, st->len + 1), msgstr.ptr, msgstr_len + 1); + + st->translations++; + st->len += inner_len; + } + msgctxt[0] = '\0'; + msgid.pos = msgstr.pos = 0; + translations++; + } + } + + fclose(fp); + + script_string_buf_destroy(&msgid); + script_string_buf_destroy(&msgstr); + + ShowStatus("Done reading '"CL_WHITE"%u"CL_RESET"' translations in '"CL_WHITE"%s"CL_RESET"'.\n", translations, file); + return translations; +} + +/** + * + **/ +void script_clear_translations(bool reload) +{ + uint32 i; + + if (string_list) + aFree(string_list); + + string_list = NULL; + string_list_pos = 0; + string_list_size = 0; + + if (translation_buf) { + for(i = 0; i < translation_buf_size; i++) { + aFree(translation_buf[i]); + } + aFree(translation_buf); + } + + translation_buf = NULL; + translation_buf_size = 0; + + if (languages) { + for(i = 0; i < max_lang_id; i++) + aFree(languages[i]); + aFree(languages); + } + if (lang_motd_txt) { + for(i = 0; i < max_lang_id; i++) + aFree(lang_motd_txt[i]); + aFree(lang_motd_txt); + } + if (lang_help_txt) { + for(i = 0; i < max_lang_id; i++) + aFree(lang_help_txt[i]); + aFree(lang_help_txt); + } + languages = NULL; + lang_motd_txt = NULL; + lang_help_txt = NULL; + max_lang_id = 0; + + if (translation_db) { + translation_db->clear(translation_db, script_translation_db_destroyer); + } + + if (reload) + script_load_translations(); +} + +/** + * + **/ +int script_translation_db_destroyer(DBKey key, DBData *data, va_list ap) +{ + DBMap *string_db = db_data2ptr(data); + + if (db_size(string_db)) { + DBIterator *iter = db_iterator(string_db); + struct string_translation *st = NULL; + + for(st = dbi_first(iter); dbi_exists(iter); st = dbi_next(iter)) { + aFree(st); + } + + dbi_destroy(iter); + } + + db_destroy(string_db); + return 0; +} + +/** + * + **/ +void script_parser_clean_leftovers(void) { + + //if (script_buf) + // aFree(script_buf); + + script_buf = NULL; + script_size = 0; + + if (translation_db) { + translation_db->destroy(translation_db, script_translation_db_destroyer); + translation_db = NULL; + } + + if (syntax.strings) { // Used only when generating translation file + db_destroy(syntax.strings); + syntax.strings = NULL; + } + + script_string_buf_destroy(&parse_simpleexpr_str); + script_string_buf_destroy(&lang_export_line_buf); + script_string_buf_destroy(&lang_export_unescaped_buf); +} + +/** + * Performs cleanup after all parsing is processed + **/ +int script_parse_cleanup_timer(int tid, unsigned int tick, int id, intptr_t data) +{ + script_parser_clean_leftovers(); + parse_cleanup_timer_id = INVALID_TIMER; + + return 0; +} + /*========================================== * Initialization *------------------------------------------*/ @@ -4662,6 +5322,7 @@ void do_init_script(void) { userfunc_db = strdb_alloc(DB_OPT_DUP_KEY,0); scriptlabel_db = strdb_alloc(DB_OPT_DUP_KEY,50); autobonus_db = strdb_alloc(DB_OPT_DUP_KEY,0); + parse_cleanup_timer_id = INVALID_TIMER; st_ers = ers_new(sizeof(struct script_state), "script.c::st_ers", ERS_CACHE_OPTIONS); stack_ers = ers_new(sizeof(struct script_stack), "script.c::script_stack", ERS_OPT_FLEX_CHUNK); @@ -4697,6 +5358,8 @@ void do_init_script(void) { add_timer_func_list(queryThread_timer, "queryThread_timer"); #endif + + script_load_translations(); } void script_reload(void) { @@ -4741,6 +5404,14 @@ void script_reload(void) { dbi_destroy(iter); db_clear(st_db); + + script_clear_translations(true); + + if (parse_cleanup_timer_id != INVALID_TIMER) { + delete_timer(parse_cleanup_timer_id, script_parse_cleanup_timer); + parse_cleanup_timer_id = INVALID_TIMER; + } + mapreg_reload(); } @@ -21302,6 +21973,11 @@ BUILDIN_FUNC(getexp2) { #include "../custom/script.inc" +/** place holder for the translation macro **/ +BUILDIN_FUNC(_) { + return true; +} + // declarations that were supposed to be exported from npc_chat.c #ifdef PCRE_SUPPORT BUILDIN_FUNC(defpattern); @@ -21877,5 +22553,7 @@ struct script_function buildin_func[] = { #include "../custom/script_def.inc" + BUILDIN_DEF(_, "s"), + {NULL,NULL,NULL}, }; diff --git a/src/map/script.h b/src/map/script.h index b121c6ca6f6..eea37de89a9 100644 --- a/src/map/script.h +++ b/src/map/script.h @@ -177,6 +177,7 @@ typedef enum c_op { C_USERFUNC, // internal script function C_USERFUNC_POS, // internal script function label C_REF, // the next call to c_op2 should push back a ref to the left operand + C_LSTR, // operators C_OP3, // a ? b : c @@ -291,6 +292,18 @@ struct script_array { unsigned int *members; ///< member list }; +struct script_string_buf { + char *ptr; + size_t pos,size; +}; + +struct string_translation { + int string_id; + uint8 translations; + unsigned int len; + char *buf; +}; + enum script_parse_options { SCRIPT_USE_LABEL_DB = 0x1,// records labels in scriptlabel_db SCRIPT_IGNORE_EXTERNAL_BRACKETS = 0x2,// ignores the check for {} brackets around the script @@ -644,6 +657,24 @@ unsigned int next_id; struct eri *st_ers; struct eri *stack_ers; +/// Script String Storage +char *string_list; +int string_list_size; +int string_list_pos; +// Set and unset on npc_parse_script +char *parser_current_npc_name; +DBMap *translation_db; // npc_name => DBMap (strings) +char **translation_buf; +uint32 translation_buf_size; +extern char **languages; +extern char **lang_motd_txt; +extern char **lang_help_txt; +uint8 max_lang_id; +struct script_string_buf parse_simpleexpr_str; +struct script_string_buf lang_export_line_buf; +struct script_string_buf lang_export_unescaped_buf; +int parse_cleanup_timer_id; + const char* skip_space(const char* p); void script_error(const char* src, const char* file, int start_line, const char* error_msg, const char* error_pos); void script_warning(const char* src, const char* file, int start_line, const char* error_msg, const char* error_pos); @@ -685,6 +716,15 @@ int add_str(const char* p); const char* get_str(int id); void script_reload(void); +int string_dup(char *str); +void script_load_translations(void); +uint32 script_load_translation(uint8 lang_id, const char *file); +int script_translation_db_destroyer(DBKey key, DBData *data, va_list ap); +void script_clear_translations(bool reload); +int script_parse_cleanup_timer(int tid, unsigned int tick, int id, intptr_t data); +uint8 script_add_language(const char *name); +void script_parser_clean_leftovers(void); + // @commands (script based) void setd_sub(struct script_state *st, TBL_PC *sd, const char *varname, int elem, void *value, struct reg_db *ref); From 565741ea587cebc02fc0c098ba1df4e02efe41ef Mon Sep 17 00:00:00 2001 From: Cydh Ramdh Date: Sat, 23 Apr 2016 18:49:38 +0700 Subject: [PATCH 2/6] Follow up 98f28a6cdf275c2ad0274229a6bdad48c166bbfc * Updated README.md Signed-off-by: Cydh Ramdh --- lang/README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lang/README.md b/lang/README.md index 62637bcc292..aaf84720d77 100644 --- a/lang/README.md +++ b/lang/README.md @@ -35,7 +35,8 @@ msgctxt "Sprakki#newbe01::NvSprakkiA" msgid "Hello there! Welcome to the World of Ragnarok Online. My name is Sprakki and I'm in charge of giving you basic gameplay tips." msgstr "" ```` -* Add/edit the dialog inside `msgstr ""` (Example in Indonesian from idRO) ```` +* Add/edit the dialog inside `msgstr ""` (Example in Indonesian from idRO) +```` #: npc/re/jobs/novice/novice.txt # mes "Hello there! Welcome to the World of Ragnarok Online. My name is Sprakki and I'm in charge of giving you basic gameplay tips."; msgctxt "Sprakki#newbe01::NvSprakkiA" @@ -43,7 +44,7 @@ msgid "Hello there! Welcome to the World of Ragnarok Online. My name is Sprakki msgstr "Halo! Selamat datang di Ragnarok Online Indonesia. Namaku adalah Sprakki dan aku disini untuk membantumu mengenai pengetahuan dasar bermain." ```` * Save the file. -**PS:** Steps above are translating manual using text editor. +**NOTE:** Steps above are translating manual using text editor. ## 3. Adding New Language --------- From dd65a92fb92fd518b2ea58584bde6ef60dd24590 Mon Sep 17 00:00:00 2001 From: Cydh Ramdh Date: Fri, 30 Sep 2016 10:50:24 +0700 Subject: [PATCH 3/6] * Added sanity check in translation functions * Updated translation's README.md Signed-off-by: Cydh Ramdh --- lang/README.md | 11 ++++++++--- src/map/script.c | 33 +++++++++++++++++++++++++++++---- 2 files changed, 37 insertions(+), 7 deletions(-) diff --git a/lang/README.md b/lang/README.md index aaf84720d77..29dc7fbe9ea 100644 --- a/lang/README.md +++ b/lang/README.md @@ -44,6 +44,8 @@ msgid "Hello there! Welcome to the World of Ragnarok Online. My name is Sprakki msgstr "Halo! Selamat datang di Ragnarok Online Indonesia. Namaku adalah Sprakki dan aku disini untuk membantumu mengenai pengetahuan dasar bermain." ```` * Save the file. +* Make sure the EOL as UNIX. + **NOTE:** Steps above are translating manual using text editor. ## 3. Adding New Language @@ -74,7 +76,7 @@ default_language: "My_Language" ## 5. Change Language --------- -Use **@langtype** in-game. Example: `@langtype My_Language`. +Use **`@langtype`** in-game. Example: `@langtype My_Language`. The language changed will be stored stored as player's account variable. @@ -82,7 +84,7 @@ The language changed will be stored stored as player's account variable. --------- Besides NPC dialogs, translation is also available for: * Atcommand messages (since multilanguage of msg_conf was depreciated). -* Atcommand Help message (@help) +* Atcommand Help message (`@help`) * Message of The Day (MoTD) ```` translations: { @@ -101,7 +103,10 @@ translations: { * rAthena **DOES NOT** officially manage the translation files. Please contact respective community that provide translation project. * rAthena **ONLY** will lists the translation project that fit our regulations. * Try visit [rAthena International Forums](https://rathena.org/board/forum/6-international-forums/) to dig informations. -* Originally by @Hercules https://github.com/HerculesWS/Hercules/commit/330e31cc71ece055908acb1eb967b4009ebc9c46, first ported to rAthena by @aleos89, restructured by @cydh. +* Credits: + * Originally by [@HerculesWS](https://github.com/HerculesWS) on https://github.com/HerculesWS/Hercules/commit/330e31cc71ece055908acb1eb967b4009ebc9c46. + * [@aleos89](https://github.com/aleos89), adapted to [@rAthena](https://github.com/rAthena). + * [@cydh](https://github.com/cydh), restructured. ## 8. Known Translation Project --------- diff --git a/src/map/script.c b/src/map/script.c index a62b1bbcb58..ff53a31b3be 100644 --- a/src/map/script.c +++ b/src/map/script.c @@ -4945,12 +4945,23 @@ void do_final_script() { } /** - * + * Add language name to library + * @param name Language name + * @return The Language ID **/ -uint8 script_add_language(const char *name) -{ +uint8 script_add_language(const char *name) { uint8 lang_id = max_lang_id; - + + if (languages) { + int i; + for (i = 0; i < max_lang_id; i++) { + if (languages[i] && strcmpi(languages[i], name) == 0) { + //ShowInfo("script_add_language: Found '%s' in %d\n", name, i); + return i; + } + } + } + RECREATE(languages, char *, ++max_lang_id); RECREATE(lang_motd_txt, char *, max_lang_id); RECREATE(lang_help_txt, char *, max_lang_id); @@ -4963,11 +4974,25 @@ uint8 script_add_language(const char *name) return lang_id; } +/** + * Add MOTD file for specified language + * @param lang_id ID of the language in the library + * @param filepath MOTD file path + **/ static void script_add_motd_language(uint8 lang_id, const char *filepath) { + if (!languages || lang_id > max_lang_id || lang_motd_txt[lang_id] == NULL) + return; lang_motd_txt[lang_id] = aStrdup(filepath); } +/** + * Add Help message file for specified language + * @param lang_id ID of the language in the library + * @param filepath Help file path + **/ static void script_add_help_language(uint8 lang_id, const char *filepath) { + if (!languages || lang_id > max_lang_id || lang_help_txt[lang_id] == NULL) + return; lang_help_txt[lang_id] = aStrdup(filepath); } From 960f5ae975cab6b2c0c25b164df9482c86629737 Mon Sep 17 00:00:00 2001 From: Cydh Ramdh Date: Thu, 13 Oct 2016 09:07:38 +0700 Subject: [PATCH 4/6] try to fix `*FILE` data type --- src/map/map.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/map/map.h b/src/map/map.h index fb2a97dec04..8a2e28ac476 100644 --- a/src/map/map.h +++ b/src/map/map.h @@ -9,6 +9,7 @@ #include "../common/mmo.h" #include "../common/mapindex.h" #include "../common/db.h" +#include "../common/utils.h" #include "../config/core.h" From 2a4eadb8d3a9b9f4bc9ab6f690aadb3dcb2aa76c Mon Sep 17 00:00:00 2001 From: Cydh Ramdh Date: Thu, 13 Oct 2016 09:12:14 +0700 Subject: [PATCH 5/6] try to fix `*FILE` data type --- src/common/cli.c | 5 ----- src/common/cli.h | 1 - src/map/map.c | 5 ++++- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/common/cli.c b/src/common/cli.c index ba5528f93b0..c49e6d1bc98 100644 --- a/src/common/cli.c +++ b/src/common/cli.c @@ -39,7 +39,6 @@ char* LOGIN_CONF_NAME; char* LAN_CONF_NAME; //char-login char* MSG_CONF_NAME_EN; //all char* TRANSLATION_CONF_FILE; -FILE *lang_export_fp; char *lang_export_file; /** @@ -186,10 +185,6 @@ int cli_get_options(int argc, char ** argv) { lang_export_file = aStrdup(argv[++i]); else lang_export_file = aStrdup("./lang/generated_translations.pot"); - - if (!(lang_export_fp = fopen(lang_export_file,"wb"))) - ShowError("export-dialog: failed to open '%s' for writing!\n", lang_export_file); - runflag = CORE_ST_STOP; } else { diff --git a/src/common/cli.h b/src/common/cli.h index 7f28cdc63bf..372e8f3e0e9 100644 --- a/src/common/cli.h +++ b/src/common/cli.h @@ -25,7 +25,6 @@ extern "C" { extern char* SCRIPT_CONF_NAME; extern char* GRF_PATH_FILENAME; // Set during startup when attempting to export the lang, unset after server initialization is over - extern FILE *lang_export_fp; extern char *lang_export_file; // for lang_export_fp //char extern char* CHAR_CONF_NAME; diff --git a/src/map/map.c b/src/map/map.c index 6ab3f9853b8..c8921b7ccc6 100644 --- a/src/map/map.c +++ b/src/map/map.c @@ -54,6 +54,7 @@ char map_server_db[32] = "ragnarok"; Sql* mmysql_handle; Sql* qsmysql_handle; /// For query_sql +FILE *lang_export_fp; const char *default_lang_str = "English"; uint8 default_lang_id = 0; @@ -4663,7 +4664,9 @@ int do_init(int argc, char *argv[]) npc_event_do_oninit(); // Init npcs (OnInit) - if (lang_export_fp) { + if (!(lang_export_fp = fopen(lang_export_file,"wb"))) + ShowError("export-dialog: failed to open '%s' for writing!\n", lang_export_file); + else { ShowInfo("Lang exported to '"CL_WHITE"%s"CL_RESET"'\n", lang_export_file); fclose(lang_export_fp); aFree(lang_export_file); From af4c28b57966ff5be48721c97a4c32b72d5b28d4 Mon Sep 17 00:00:00 2001 From: Cydh Ramdh Date: Thu, 13 Oct 2016 09:18:00 +0700 Subject: [PATCH 6/6] Removed unused var `map_msg_db` Signed-off-by: Cydh Ramdh --- src/map/map.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/map/map.c b/src/map/map.c index c8921b7ccc6..7fdd1b92a72 100644 --- a/src/map/map.c +++ b/src/map/map.c @@ -94,7 +94,6 @@ static DBMap* map_db=NULL; /// unsigned int mapindex -> struct map_data* static DBMap* nick_db=NULL; /// uint32 char_id -> struct charid2nick* (requested names of offline characters) static DBMap* charid_db=NULL; /// uint32 char_id -> struct map_session_data* static DBMap* regen_db=NULL; /// int id -> struct block_list* (status_natural_heal processing) -static DBMap* map_msg_db=NULL; static int map_users=0;