2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
4 * All source code herein is the property of Volition, Inc. You may not sell
5 * or otherwise commercially exploit the source or things you created based on
10 * $Logfile: /Freespace2/code/Localization/localize.cpp $
17 * Revision 1.10 2005/10/03 11:23:14 taylor
18 * don't display error message in FS1 for missing tstrings.tbl, it's ok to be missing in that case
20 * Revision 1.9 2005/10/01 21:35:51 taylor
21 * various FS1 related changes, removes need to extra fs1.vp file
23 * Revision 1.8 2005/03/29 02:18:47 taylor
24 * Various 64-bit platform fixes
25 * Fix compiler errors with MAKE_FS1 and fix gr_set_bitmap() too
26 * Make sure that turrets can fire at asteroids for FS1 (needed for a couple missions)
27 * Streaming audio support (big thanks to Pierre Willenbrock!!)
28 * Removed dependance on strings.tbl for FS1 since we don't actually need it now
30 * Revision 1.7 2004/07/04 11:39:06 taylor
31 * fix missing debrief text, crash on exit, path separator's, warning fixes, no GR_SOFT
33 * Revision 1.6 2003/06/11 18:30:32 taylor
36 * Revision 1.5 2003/06/03 04:00:40 taylor
37 * Polish language support (Janusz Dziemidowicz)
39 * Revision 1.4 2003/05/18 03:55:30 taylor
40 * automatic language selection support
42 * Revision 1.3 2002/06/09 04:41:22 relnev
43 * added copyright header
45 * Revision 1.2 2002/05/07 03:16:46 theoddone33
46 * The Great Newline Fix
48 * Revision 1.1.1.1 2002/05/03 03:28:09 root
52 * 91 6/16/00 3:16p Jefff
53 * sim of the year dvd version changes, a few german soty localization
56 * 90 11/02/99 8:20p Jefff
57 * more translation changes/additions
59 * 89 11/02/99 3:24p Jefff
60 * added translation functions for a few key instances where english was
63 * 88 10/29/99 10:40p Jefff
66 * 87 10/28/99 2:05a Jefff
67 * safety fix in localization stuff
69 * 86 10/26/99 10:53a Jefff
70 * German builds now correctly default to German on first run
72 * 85 10/25/99 11:22p Jefff
73 * mo' strings (launcher)
75 * 84 10/25/99 5:46p Jefff
76 * Many localization fixes/changes for German builds
78 * 83 10/14/99 3:25p Jefff
79 * soo...many....xstrs....
81 * 82 10/14/99 2:52p Jefff
82 * localization fixes. added support for hi-res specific xstr offsets
84 * 81 10/13/99 3:58p Jefff
85 * added a bunch of missed XSTRs
87 * 80 9/13/99 11:30a Dave
88 * Added checkboxes and functionality for disabling PXO banners as well as
89 * disabling d3d zbuffer biasing.
91 * 79 9/08/99 9:59a Jefff
93 * 78 9/07/99 4:01p Dave
94 * Fixed up a string.tbl paroblem (self destruct message). Make sure IPX
95 * does everything properly (setting up address when binding). Remove
96 * black rectangle background from UI_INPUTBOX.
98 * 77 9/07/99 1:54p Jefff
99 * skip mission strings added
101 * 76 9/03/99 10:55a Jefff
104 * 75 9/03/99 1:31a Dave
105 * CD checking by act. Added support to play 2 cutscenes in a row
106 * seamlessly. Fixed super low level cfile bug related to files in the
107 * root directory of a CD. Added cheat code to set campaign mission # in
110 * 74 8/27/99 12:04a Dave
111 * Campaign loop screen.
113 * 73 8/26/99 12:48p Jefff
115 * 72 8/22/99 5:53p Dave
116 * Scoring fixes. Added self destruct key. Put callsigns in the logfile
117 * instead of ship designations for multiplayer players.
119 * 71 8/22/99 1:55p Dave
120 * Cleaned up host/team-captain leaving code.
122 * 70 8/17/99 7:32p Jefff
125 * 69 8/16/99 5:15p Dave
126 * Upped string count.
128 * 68 8/16/99 4:04p Dave
129 * Big honking checkin.
131 * 67 8/11/99 2:09p Jefff
132 * ill give you three guesses.
134 * 66 8/10/99 7:57p Jefff
137 * 65 8/05/99 2:40p Jefff
140 * 64 8/02/99 9:13p Dave
143 * 63 8/02/99 12:01p Jefff
146 * 62 7/29/99 10:47p Dave
147 * Standardized D3D fogging using vertex fog. Shook out Savage 4 bugs.
149 * 61 7/29/99 9:44a Jefff
150 * added some strings for ingame objectives screen
152 * 60 7/27/99 7:15p Jefff
153 * 3 new interface strings
155 * 59 7/27/99 6:53p Dave
156 * Hi-res main hall support.
158 * 58 7/24/99 1:54p Dave
159 * Hud text flash gauge. Reworked dead popup to use 4 buttons in red-alert
162 * 57 7/22/99 4:08p Jefff
165 * 56 7/19/99 7:20p Dave
166 * Beam tooling. Specialized player-killed-self messages. Fixed d3d nebula
169 * 55 7/19/99 2:13p Dave
170 * Added some new strings for Heiko.
172 * 54 7/15/99 2:37p Jefff
174 * 53 7/13/99 6:07p Jefff
175 * Added support for localization string offsets.
177 * 52 7/09/99 10:32p Dave
178 * Command brief and red alert screens.
180 * 51 6/25/99 2:51p Jasons
183 * 50 6/25/99 11:59a Dave
184 * Multi options screen.
186 * 49 6/24/99 12:34a Dave
187 * Main options screen.
189 * 48 6/19/99 3:58p Dave
192 * 47 6/14/99 5:19p Dave
194 * 46 6/04/99 11:32a Dave
195 * Added reset text to ship select screen. Fixed minor xstr bug in ui
198 * 45 6/01/99 6:07p Dave
199 * New loading/pause/please wait bar.
201 * 44 6/01/99 4:03p Dave
202 * Changed some popup flag #defines. Upped string count to 1336
204 * 43 5/26/99 11:46a Dave
205 * Added ship-blasting lighting and made the randomization of lighting
206 * much more customizable.
208 * 42 5/22/99 6:05p Dave
209 * Fixed a few localization # problems.
211 * 41 5/21/99 6:45p Dave
212 * Sped up ui loading a bit. Sped up localization disk access stuff. Multi
213 * start game screen, multi password, and multi pxo-help screen.
215 * 40 5/17/99 2:52p Dave
216 * Fixed dumb localization problem.
218 * 39 5/17/99 10:07a Dave
219 * Updated string count.
221 * 38 5/04/99 6:38p Dave
222 * Finished multi join-wait screen.
224 * 37 5/04/99 5:20p Dave
225 * Fixed up multiplayer join screen and host options screen. Should both
228 * 36 5/03/99 11:04p Dave
229 * Most of the way done with the multi join screen.
231 * 35 5/03/99 8:32p Dave
232 * New version of multi host options screen.
234 * 34 4/09/99 2:28p Dave
235 * Upped Xstring count.
237 * 33 4/09/99 2:21p Dave
238 * Multiplayer beta stuff. CD checking.
240 * 32 4/08/99 12:50p Neilk
241 * changed number of xstr constant
243 * 31 4/07/99 5:54p Dave
244 * Fixes for encryption in updatelauncher.
246 * 30 3/24/99 4:05p Dave
247 * Put in support for assigning the player to a specific squadron with a
248 * specific logo. Preliminary work for doing pos/orient checksumming in
249 * multiplayer to reduce bandwidth.
251 * 29 3/23/99 3:35p Andsager
252 * Add error check for duplicate string indices
254 * 28 3/22/99 2:56p Andsager
255 * Localize Launcher Updater
257 * 27 2/23/99 11:18a Andsager
258 * Localize launcher using strings.tbl
260 * 26 2/22/99 9:35p Andsager
261 * Add lcl_get_language_name() returns string with current lang. Added
262 * localization for launcher.
264 * 25 2/21/99 6:02p Dave
265 * Fixed standalone WSS packets.
267 * 24 2/17/99 2:11p Dave
268 * First full run of squad war. All freespace and tracker side stuff
271 * 23 2/12/99 6:15p Dave
272 * Pre-mission Squad War code is 95% done.
274 * 22 2/02/99 7:27p Neilk
275 * implemented lc_close() to free up some string memory when shutting down
277 * 21 1/30/99 1:29a Dave
278 * Fixed nebula thumbnail problem. Full support for 1024x768 choose pilot
279 * screen. Fixed beam weapon death messages.
281 * 20 1/29/99 7:56p Neilk
282 * Added new mission log interface
284 * 19 1/29/99 4:17p Dave
285 * New interface screens.
287 * 18 1/29/99 12:47a Dave
288 * Put in sounds for beam weapon. A bunch of interface screens (tech
291 * 17 1/13/99 2:10p Andsager
294 * 16 12/14/98 12:13p Dave
295 * Spiffed up xfer system a bit. Put in support for squad logo file xfer.
298 * 15 12/03/98 5:22p Dave
299 * Ported over Freespace 1 multiplayer ships.tbl and weapons.tbl
302 * 14 12/02/98 5:47p Dave
303 * Put in interface xstr code. Converted barracks screen to new format.
305 * 13 12/01/98 4:46p Dave
306 * Put in targa bitmap support (16 bit).
314 #include "localize.h"
316 #include "osregistry.h"
320 // ------------------------------------------------------------------------------------------------------------
321 // LOCALIZE DEFINES/VARS
324 // general language/localization data ---------------------
327 int Lcl_current_lang = LCL_ENGLISH;
329 // language info table
330 typedef struct lang_info {
331 char lang_name[LCL_LANG_NAME_LEN + 1]; // literal name of the language
332 char lang_ext[LCL_LANG_NAME_LEN + 1]; // for adding to names on disk access
335 lang_info Lcl_languages[LCL_NUM_LANGUAGES] = {
336 { "English", "" }, // english
337 { "German", "gr" }, // german
338 { "French", "fr" }, // french
339 { "Polish", "pl" }, // polish
342 //#if defined(GERMAN_BUILD)
343 //#define DEFAULT_LANGUAGE "German"
345 #define DEFAULT_LANGUAGE "English"
348 // following is the offset where special characters start in our font
349 #define LCL_SPECIAL_CHARS_FR 164
350 #define LCL_SPECIAL_CHARS_GR 164
351 #define LCL_SPECIAL_CHARS_PL 127
352 #define LCL_SPECIAL_CHARS 127
353 int Lcl_special_chars;
355 // use these to replace *_BUILD, values before
356 // only 1 will be active at a time
363 // executable string localization data --------------------
365 // XSTR_SIZE is the total count of unique XSTR index numbers. An index is used to
366 // reference an entry in strings.tbl. This is used for translation of strings from
367 // the english version (in the code) to a foreign version (in the table). Thus, if you
368 // add a new string to the code, you must assign it a new index. Use the number below for
369 // that index and increase the number below by one.
370 #define XSTR_SIZE 1570
373 // struct to allow for strings.tbl-determined x offset
374 // offset is 0 for english, by default
377 int offset_x; // string offset in 640
378 int offset_x_hi; // string offset in 1024
381 //char *Xstr_table[XSTR_SIZE];
382 lcl_xstr Xstr_table[XSTR_SIZE];
386 // table/mission externalization stuff --------------------
388 #define TABLE_STRING_FILENAME "tstrings.tbl"
389 // filename of the file to use when localizing table strings
390 char *Lcl_ext_filename = NULL;
391 CFILE *Lcl_ext_file = NULL;
393 // for scanning/parsing tstrings.tbl (from ExStr)
394 #define PARSE_TEXT_STRING_LEN 2048
395 #define PARSE_ID_STRING_LEN 128
396 #define TS_SCANNING 0 // scanning for a line of text
397 #define TS_ID_STRING 1 // reading in an id string
398 #define TS_OPEN_QUOTE 2 // looking for an open quote
399 #define TS_STRING 3 // reading in the text string itself
400 int Ts_current_state = 0;
401 char Ts_text[PARSE_TEXT_STRING_LEN]; // string we're currently working with
402 char Ts_id_text[PARSE_ID_STRING_LEN]; // id string we're currently working with
406 // file pointers for optimized string lookups
407 // some example times for Freespace2 startup with granularities (mostly .tbl files, ~500 strings in the table file, many looked up more than once)
408 // granularity 20 : 13 secs
409 // granularity 10 : 11 secs
410 // granularity 5 : 9 secs
411 // granularity 2 : 7-8 secs
412 #define LCL_GRANULARITY 1 // how many strings between each pointer (lower granularities should give faster lookup times)
413 #define LCL_MAX_POINTERS 4500 // max # of pointers
414 #define LCL_MAX_STRINGS (LCL_GRANULARITY * LCL_MAX_POINTERS)
415 int Lcl_pointers[LCL_MAX_POINTERS];
416 int Lcl_pointer_count = 0;
419 // strings.tbl for FS1, hard coded
420 extern const char *FS1_trans[LCL_NUM_LANGUAGES_FS1][LCL_NUM_STRINGS_FS1];
423 // ------------------------------------------------------------------------------------------------------------
424 // LOCALIZE FORWARD DECLARATIONS
427 // associate table file externalization with the specified input file
428 void lcl_ext_associate(const char *filename);
430 // given a valid XSTR() tag piece of text, extract the string portion, return it in out, nonzero on success
431 int lcl_ext_get_text(char *xstr, char *out);
433 // given a valid XSTR() tag piece of text, extract the id# portion, return the value in out, nonzero on success
434 int lcl_ext_get_id(char *xstr, int *out);
436 // given a valid XSTR() id#, lookup the string in tstrings.tbl, filling in out if found, nonzero on success
437 int lcl_ext_lookup(char *out, const int max_out, int id);
439 // if the char is a valid char for a signed integer value string
440 int lcl_is_valid_numeric_char(char c);
442 // sub-parse function for individual lines of tstrings.tbl (from Exstr)
443 // returns : integer with the low bits having the following values :
444 // 0 on fail, 1 on success, 2 if found a matching id/string pair, 3 if end of language has been found
445 // for cases 1 and 2 : the high bit (1<<31) will be set if the parser detected the beginning of a new string id on this line
446 // so be sure to mask this value out to get the low portion of the return value
448 int lcl_ext_lookup_sub(char *text, char *out, const int max_out, int id);
450 // initialize the pointer array into tstrings.tbl (call from lcl_ext_open() ONLY)
451 void lcl_ext_setup_pointers();
454 // ------------------------------------------------------------------------------------------------------------
455 // LOCALIZE FUNCTIONS
458 // initialize localization, if no language is passed - use the language specified in the registry
459 void lcl_init(int lang_init)
461 char lang_string[128];
465 // initialize encryption
468 // read the language from the registry
470 memset(lang_string, 0, 128);
472 // default to DEFAULT_LANGUAGE (which should be english so we dont have to put German text
473 // in tstrings in the #default section
474 ret = os_config_read_string(NULL, "Language", DEFAULT_LANGUAGE);
478 SDL_strlcpy(lang_string, DEFAULT_LANGUAGE, SDL_arraysize(lang_string));
480 SDL_strlcpy(lang_string, ret, SDL_arraysize(lang_string));
485 for(idx=0; idx<LCL_NUM_LANGUAGES; idx++){
486 if(!SDL_strcasecmp(Lcl_languages[idx].lang_name, lang_string)){
495 SDL_assert((lang_init >= 0) && (lang_init < LCL_NUM_LANGUAGES));
500 if (lang >= LCL_NUM_LANGUAGES_FS1)
505 Lcl_pointer_count = 0;
507 // associate the table string file
508 lcl_ext_associate(TABLE_STRING_FILENAME);
510 // set the language (this function takes care of setting up file pointers)
511 lcl_set_language(lang);
514 // added 2.2.99 by NeilK to take care of fs2 launcher memory leaks
515 // shutdown localization
518 // if the filename exists, free it up
519 if(Lcl_ext_filename != NULL){
520 free(Lcl_ext_filename);
523 // free the Xstr_table
527 // determine what language we're running in, see LCL_* defines above
528 int lcl_get_language()
530 return Lcl_current_lang;
533 // initialize the xstr table
539 char language_tag[512];
541 char *p_offset = NULL;
542 int offset_lo = 0, offset_hi = 0;
543 int num_offsets_on_this_line = 0;
545 for (i=0; i<XSTR_SIZE; i++){
546 Xstr_table[i].str = NULL;
549 // make sure localization is NOT running
553 read_file_text("strings.tbl");
556 // move down to the proper section
557 memset(language_tag, 0, 512);
558 SDL_strlcpy(language_tag, "#", SDL_arraysize(language_tag));
559 SDL_strlcat(language_tag, Lcl_languages[Lcl_current_lang].lang_name, SDL_arraysize(language_tag));
560 if(skip_to_string(language_tag) != 1){
561 Error(LOCATION, NOX("Strings.tbl is corrupt"));
564 // parse all the strings in this section of the table
565 while (!check_for_string("#")) {
567 stuff_string(buf, F_NAME, NULL, 4096);
575 if (!isspace(buf[i])) {
580 // trim unneccesary end of string
582 // SDL_assert(buf[i] == '"');
584 // probably an offset on this entry
585 buf[i+1] = 0; // drop down a null terminator (prolly unnecessary)
586 while(!is_white_space(buf[i])) i--; // back up over the potential offset
587 while(is_white_space(buf[i])) i--; // now back up over intervening spaces
588 num_offsets_on_this_line = 1;
590 // could have a 2nd offset value (one for 640, one for 1024)
592 while(!is_white_space(buf[i])) i--; // back up over the 2nd offset
593 while(is_white_space(buf[i])) i--; // now back up over intervening spaces
594 num_offsets_on_this_line = 2;
597 p_offset = &buf[i+1]; // get ptr to string section with offset in it
599 Error(LOCATION, NOX("Strings.tbl is corrupt")); // now its an error
606 // copy string into buf
608 for (i=1; buf[i]; i++) {
614 } else if (chr == 'r') {
623 // null terminator on buf
626 // write into Xstr_table
627 if (index >= 0 && index < XSTR_SIZE) {
628 if (Xstr_table[index].str != NULL) {
629 Warning(LOCATION, "Strings table index %d used more than once", index);
631 Xstr_table[index].str = strdup(buf);
634 // read offset information, assume 0 if nonexistant
635 if (p_offset != NULL) {
636 if (sscanf(p_offset, "%d%d", &offset_lo, &offset_hi) < num_offsets_on_this_line) {
637 // whatever is in the file ain't a proper offset
638 Error(LOCATION, NOX("Strings.tbl is corrupt"));
641 Xstr_table[index].offset_x = offset_lo;
642 if (num_offsets_on_this_line == 2) {
643 Xstr_table[index].offset_x_hi = offset_hi;
645 Xstr_table[index].offset_x_hi = offset_lo;
648 // clear out our vars
652 num_offsets_on_this_line = 0;
654 } catch (parse_error_t rval) {
655 (void)rval; // suppress unused warning in release build
656 mprintf(("Error parsing 'strings.tbl'\nError code = %i.\n", (int)rval));
661 SDL_assert(XSTR_SIZE == LCL_NUM_STRINGS_FS1);
662 SDL_assert(Lcl_current_lang < LCL_NUM_LANGUAGES_FS1);
664 for (i=0; i<XSTR_SIZE; i++) {
665 if ( !strlen(FS1_trans[Lcl_current_lang][i]) ) {
666 Xstr_table[i].str = NULL;
668 Xstr_table[i].str = (char *)FS1_trans[Lcl_current_lang][i];
671 Xstr_table[i].offset_x = 0;
672 Xstr_table[i].offset_x_hi = 0;
681 void lcl_xstr_close()
684 for (int i=0; i<XSTR_SIZE; i++){
685 if (Xstr_table[i].str != NULL) {
686 free(Xstr_table[i].str);
687 Xstr_table[i].str = NULL;
694 // set our current language
695 void lcl_set_language(int lang)
697 Lcl_current_lang = lang;
699 nprintf(("General", "Setting language to %s\n", Lcl_languages[lang].lang_name));
701 // flag the proper language as being active
709 Lcl_special_chars = LCL_SPECIAL_CHARS;
713 Lcl_special_chars = LCL_SPECIAL_CHARS_FR;
717 Lcl_special_chars = LCL_SPECIAL_CHARS_GR;
721 Lcl_special_chars = LCL_SPECIAL_CHARS_PL;
725 // set to 0, so lcl_ext_open() knows to reset file pointers
726 Lcl_pointer_count = 0;
728 // reset file pointers to the proper language-section
729 if(Lcl_current_lang != LCL_DEFAULT_LANGUAGE){
730 lcl_ext_setup_pointers();
734 // maybe add on an appropriate subdirectory when opening a localized file
735 void lcl_add_dir(char *current_path, const int max_len)
740 // if the disk extension is 0 length, don't add enything
741 if (strlen(Lcl_languages[Lcl_current_lang].lang_ext) <= 0) {
745 // get the length of the string so far
746 path_len = strlen(current_path);
751 // get the current last char
752 last_char = current_path[path_len - 1];
754 // if the last char is a slash, just copy in the disk extension
755 if (last_char == DIR_SEPARATOR_CHAR) {
756 SDL_strlcat(current_path, Lcl_languages[Lcl_current_lang].lang_ext, max_len);
757 SDL_strlcat(current_path, DIR_SEPARATOR_STR, max_len);
759 // otherwise add a slash, then copy in the disk extension
761 SDL_strlcat(current_path, DIR_SEPARATOR_STR, max_len);
762 SDL_strlcat(current_path, Lcl_languages[Lcl_current_lang].lang_ext, max_len);
766 // maybe add localized directory to full path with file name when opening a localized file
767 void lcl_add_dir_to_path_with_filename(char *current_path, const int path_len)
769 char temp[MAX_PATH_LEN];
771 // if the disk extension is 0 length, don't add enything
772 if (strlen(Lcl_languages[Lcl_current_lang].lang_ext) <= 0) {
776 // find position of last slash and copy rest of filename (not counting slash) to temp
777 // mark end of current path with '\0', so strcat will work
778 char *last_slash = strrchr(current_path, DIR_SEPARATOR_CHAR);
779 if (last_slash == NULL) {
780 SDL_strlcpy(temp, current_path, SDL_arraysize(temp));
781 current_path[0] = '\0';
783 SDL_strlcpy(temp, last_slash+1, SDL_arraysize(temp));
784 last_slash[1] = '\0';
788 SDL_strlcat(current_path, Lcl_languages[Lcl_current_lang].lang_ext, path_len);
789 SDL_strlcat(current_path, DIR_SEPARATOR_STR, path_len);
791 // copy rest of filename from temp
792 SDL_strlcat(current_path, temp, path_len);
796 // externalization of table/mission files -----------------------
798 // open the externalization file for use during parsing (call before parsing a given file)
801 // if the file is already open, do nothing
802 SDL_assert(Lcl_ext_file == NULL);
804 // if we're running in the default language, do nothing
805 if(Lcl_current_lang == LCL_DEFAULT_LANGUAGE){
810 // otherwise open the file
811 Lcl_ext_file = cfopen(Lcl_ext_filename, "rt");
812 if(Lcl_ext_file == NULL){
818 // close the externalization file (call after parsing a given file)
821 // if the file is not open, do nothing
822 if(Lcl_ext_file == NULL){
826 // if we're running in the default language, do nothing
827 if(Lcl_current_lang == LCL_DEFAULT_LANGUAGE){
831 // otherwise close it
832 cfclose(Lcl_ext_file);
836 // get the localized version of the string. if none exists, return the original string
837 // valid input to this function includes :
838 // "this is some text"
839 // XSTR("wheeee", -1)
841 // and these should cover all the externalized string cases
842 // fills in id if non-NULL. a value of -2 indicates it is not an external string
843 void lcl_ext_localize(char *in, char *out, int max_len, int *id)
846 char text_str[2048]="";
847 char lookup_str[2048]="";
854 // default (non-external string) value
859 str_len = strlen(in);
861 // if the string is < 9 chars, it can't be an XSTR("",) tag, so just copy it
863 if(str_len > max_len){
864 error_display(0, "Token too long: [%s]. Length = %i. Max is %i.\n", in, str_len, max_len);
867 SDL_strlcpy(out, in, max_len);
874 // otherwise, check to see if it's an XSTR() tag
875 memset(first_four, 0, 5);
876 SDL_strlcpy(first_four, in, SDL_arraysize(first_four));
877 if(SDL_strcasecmp(first_four, "XSTR")){
879 if(str_len > max_len){
880 error_display(0, "Token too long: [%s]. Length = %i. Max is %i.\n", in, str_len, max_len);
883 SDL_strlcpy(out, in, max_len);
890 // at this point we _know_ its an XSTR() tag, so split off the strings and id sections
891 if(!lcl_ext_get_text(in, text_str)){
893 SDL_strlcpy(out, in, max_len);
899 if(!lcl_ext_get_id(in, &str_id)){
900 SDL_strlcpy(out, in, max_len);
907 // if the localization file is not open, or we're running in the default language, return the original string
908 if((Lcl_ext_file == NULL) || (str_id < 0) || (Lcl_current_lang == LCL_DEFAULT_LANGUAGE)){
909 SDL_strlcpy(out, text_str, max_len);
916 // attempt to find the string
917 if(lcl_ext_lookup(lookup_str, SDL_arraysize(lookup_str), str_id)){
918 // copy to the outgoing string
919 SDL_assert(strlen(lookup_str) <= (unsigned int)(max_len - 1));
921 if (strlen(lookup_str) > (unsigned int)(max_len-1)) {
922 // be safe and truncate string to fit
923 SDL_strlcpy(out, lookup_str, max_len);
925 SDL_strlcpy(out, lookup_str, max_len);
929 // otherwise use what we have - probably should Int3() or assert here
931 SDL_strlcpy(out, text_str, max_len);
940 // translate the specified string based upon the current language
941 const char *XSTR(const char *str, int index)
948 if (index >= 0 && index < XSTR_SIZE) {
949 if (Xstr_table[index].str){
950 return Xstr_table[index].str; // return translation of string
954 // can't translate, return original english string
958 // retrieve the offset for a localized string
959 int lcl_get_xstr_offset(int index, int res)
962 return Xstr_table[index].offset_x;
964 return Xstr_table[index].offset_x_hi;
969 // ------------------------------------------------------------------------------------------------------------
970 // LOCALIZE FORWARD DEFINITIONS
973 // associate table file externalization with the specified input file
974 void lcl_ext_associate(const char *filename)
977 // if the filename already exists, free it up
978 if(Lcl_ext_filename != NULL){
979 free(Lcl_ext_filename);
982 // set the new filename
983 Lcl_ext_filename = strdup(filename);
987 // given a valid XSTR() tag piece of text, extract the string portion, return it in out, nonzero on success
988 int lcl_ext_get_text(char *xstr, char *out)
990 int str_start = 0, str_end = 0;
994 SDL_assert(xstr != NULL);
995 SDL_assert(out != NULL);
996 str_len = strlen(xstr);
998 // this is some crazy wack-ass code.
999 // look for the open quote
1000 p = strstr(xstr, "\"");
1002 error_display(0, "Error parsing XSTR() tag %s\n", xstr);
1005 str_start = p - xstr + 1;
1007 // make sure we're not about to walk past the end of the string
1008 if((p - xstr) >= str_len){
1009 error_display(0, "Error parsing XSTR() tag %s\n", xstr);
1013 // look for the close quote
1014 p2 = strstr(p+1, "\"");
1016 error_display(0, "Error parsing XSTR() tag %s\n", xstr);
1019 str_end = p2 - xstr;
1022 // now that we know the boundaries of the actual string in the XSTR() tag. copy it
1023 memcpy(out, xstr + str_start, str_end - str_start);
1026 lcl_fix_polish(out);
1033 // given a valid XSTR() tag piece of text, extract the id# portion, return the value in out, nonzero on success
1034 int lcl_ext_get_id(char *xstr, int *out)
1039 SDL_assert(xstr != NULL);
1040 SDL_assert(out != NULL);
1042 str_len = strlen(xstr);
1044 // find the first quote
1045 p = strstr(xstr, "\"");
1047 error_display(0, "Error parsing id# in XSTR() tag %s\n", xstr);
1050 // make sure we're not about to walk off the end of the string
1051 if((p - xstr) >= str_len){
1052 error_display(0, "Error parsing id# in XSTR() tag %s\n", xstr);
1057 // continue searching until we find the close quote
1059 pnext = strstr(p, "\"");
1061 error_display(0, "Error parsing id# in XSTR() tag %s\n", xstr);
1065 // if the previous char is a \, we know its not the "end-of-string" quote
1066 if(*(pnext - 1) != '\\'){
1075 // search until we find a ,
1076 pnext = strstr(p, ",");
1078 error_display(0, "Error parsing id# in XSTR() tag %s\n", xstr);
1081 // make sure we're not about to walk off the end of the string
1082 if((pnext - xstr) >= str_len){
1083 error_display(0, "Error parsing id# in XSTR() tag %s\n", xstr);
1088 // now get the id string
1090 pnext = strtok(p, ")");
1092 error_display(0, "Error parsing id# in XSTR() tag %s\n", xstr);
1096 // get the value and we're done
1103 // given a valid XSTR() id#, lookup the string in tstrings.tbl, filling in out if found, nonzero on success
1104 int lcl_ext_lookup(char *out, const int max_out, int id)
1110 SDL_assert(Lcl_pointer_count >= 0);
1111 SDL_assert(Lcl_pointers[0] >= 0);
1112 SDL_assert(Lcl_pointers[Lcl_pointer_count - 1] >= 0);
1113 SDL_assert(Lcl_ext_file != NULL);
1114 SDL_assert(id >= 0);
1116 // seek to the closest pointer <= the id# we're looking for
1117 pointer = id / LCL_GRANULARITY;
1118 cfseek(Lcl_ext_file, Lcl_pointers[pointer], CF_SEEK_SET);
1120 // reset parsing vars and go to town
1121 Ts_current_state = TS_SCANNING;
1122 Ts_id_text_size = 0;
1124 memset(Ts_text, 0, PARSE_TEXT_STRING_LEN);
1125 memset(Ts_id_text, 0, PARSE_ID_STRING_LEN);
1126 while((cftell(Lcl_ext_file) < Lcl_pointers[Lcl_pointer_count - 1]) && cfgets(text, 1024, Lcl_ext_file)){
1127 ret = lcl_ext_lookup_sub(text, out, max_out, id);
1129 // run the line parse function
1130 switch(ret & 0x0fffffff){
1133 Int3(); // should never get here - it means the string doens't exist in the table!!
1136 // success parsing the line - please continue
1140 // found a matching string/id pair
1144 // this is because tstrings.tbl reads in as ANSI for some reason
1145 // opening tstrings with "rb" mode didnt seem to help, so its now still "rt" like before
1146 lcl_fix_umlauts(out, LCL_TO_ASCII);
1150 // end of language found
1152 Int3(); // should never get here - it means the string doens't exist in the table!!
1157 Int3(); // should never get here - it means the string doens't exist in the table!!
1161 // sub-parse function for individual lines of tstrings.tbl (from Exstr)
1162 // returns : integer with the low bits having the following values :
1163 // 0 on fail, 1 on success, 2 if found a matching id/string pair, 3 if end of language has been found
1164 // for cases 1 and 2 : the high bit (1<<31) will be set if the parser detected the beginning of a new string id on this line
1166 int lcl_ext_lookup_sub(char *text, char *out, const int max_out, int id)
1168 char *p; // current ptr
1169 int len = strlen(text);
1171 char text_copy[1024];
1173 int found_new_string_id = 0;
1178 // do something useful
1179 switch(Ts_current_state){
1180 // scanning for a line of text
1182 // if the first word is #end, we're done with the file altogether
1183 SDL_strlcpy(text_copy, text, SDL_arraysize(text_copy));
1184 tok = strtok(text_copy, " \n");
1185 if((tok != NULL) && !SDL_strcasecmp(tok, "#end")){
1188 // if its a commented line, skip it
1189 else if((text[0] == ';') || (text[0] == ' ') || (text[0] == '\n')){
1192 // otherwise we should have an ID #, so stuff it and move to the proper state
1194 if(lcl_is_valid_numeric_char(*p)){
1195 memset(Ts_id_text, 0, PARSE_ID_STRING_LEN);
1196 Ts_id_text_size = 0;
1197 Ts_id_text[Ts_id_text_size++] = *p;
1198 Ts_current_state = TS_ID_STRING;
1200 found_new_string_id = 1;
1210 // scanning in an id string
1212 // if we have another valid char
1213 if(lcl_is_valid_numeric_char(*p)){
1214 Ts_id_text[Ts_id_text_size++] = *p;
1216 // if we found a comma, our id# is finished, look for the open quote
1218 Ts_current_state = TS_OPEN_QUOTE;
1226 // valid space or an open quote
1227 if((*p == ' ') || (*p == '\"')){
1229 memset(Ts_text, 0, PARSE_TEXT_STRING_LEN);
1231 Ts_current_state = TS_STRING;
1240 // if we have an end quote, we need to look for a comma
1241 if((*p == '\"') /*&& (Ts_text_size > 0)*/ && (Ts_text[Ts_text_size - 1] != '\\')){
1242 // we're now done - we have a string
1243 Ts_current_state = TS_SCANNING;
1245 // if the id#'s match, copy the string and return "string found"
1246 if((atoi(Ts_id_text) == id) && (out != NULL)){
1247 SDL_strlcpy(out, Ts_text, max_out);
1249 return found_new_string_id ? (1<<1) | (1<<31) : (1<<1);
1252 // otherwise, just continue parsing
1253 return found_new_string_id ? (1<<0) | (1<<31) : (1<<0);
1255 // otherwise add to the string
1257 Ts_text[Ts_text_size++] = *p;
1262 // if we have a newline, return success, we're done with this line
1264 return found_new_string_id ? (1<<0) | (1<<31) : (1<<0);
1267 // next char in the line
1273 return found_new_string_id ? (1<<0) | (1<<31) : (1<<0);
1276 // if the char is a valid char for a signed integer value
1277 int lcl_is_valid_numeric_char(char c)
1279 return ( (c == '-') || (c == '0') || (c == '1') || (c == '2') || (c == '3') || (c == '4') ||
1280 (c == '5') || (c == '6') || (c == '7') || (c == '8') || (c == '9') ) ? 1 : 0;
1283 // initialize the pointer array into tstrings.tbl (call from lcl_ext_open() ONLY)
1284 void lcl_ext_setup_pointers()
1286 char language_string[128];
1291 int found_start = 0;
1293 // open the localization file
1295 if(Lcl_ext_file == NULL){
1297 error_display(0, "Error opening externalization file! File likely does not exist or could not be found");
1302 // seek to the currently active language
1303 memset(language_string, 0, 128);
1304 SDL_strlcpy(language_string, "#", SDL_arraysize(language_string));
1305 if(!SDL_strcasecmp(DEFAULT_LANGUAGE, Lcl_languages[Lcl_current_lang].lang_name)){
1306 SDL_strlcat(language_string, "default", SDL_arraysize(language_string));
1308 SDL_strlcat(language_string, Lcl_languages[Lcl_current_lang].lang_name, SDL_arraysize(language_string));
1310 memset(line, 0, 1024);
1312 // reset seek variables and begin
1313 Lcl_pointer_count = 0;
1314 while(cfgets(line, 1024, Lcl_ext_file)){
1315 tok = strtok(line, " \n");
1320 // if the language matches, we're good to start parsing strings
1321 if(!SDL_strcasecmp(language_string, tok)){
1327 // if we didn't find the language specified, error
1328 if(found_start <= 0){
1329 error_display(0, "Could not find specified langauge in tstrings.tbl!\n");
1335 while(cfgets(line, 1024, Lcl_ext_file)){
1336 ret = lcl_ext_lookup_sub(line, NULL, 0, -1);
1339 switch(ret & 0x0fffffff){
1345 // end of language found
1347 // mark one final pointer
1348 Lcl_pointers[Lcl_pointer_count++] = cftell(Lcl_ext_file) - strlen(line) - 1;
1353 // the only other case we care about is the beginning of a new id#
1355 if((string_count % LCL_GRANULARITY) == 0){
1356 // mark the pointer down
1357 Lcl_pointers[Lcl_pointer_count++] = cftell(Lcl_ext_file) - strlen(line) - 1;
1359 // if we're out of pointer slots
1360 if(Lcl_pointer_count >= LCL_MAX_POINTERS){
1361 error_display(0, "Out of pointer for tstrings.tbl lookup. Please increment LCL_MAX_POINTERS in localize.cpp");
1366 // increment string count
1371 // should never get here. we should always be exiting through case 3 (end of language section) of the above switch
1377 void lcl_get_language_name(char *lang_name, const int max_len)
1379 SDL_assert(LCL_NUM_LANGUAGES == 3);
1381 SDL_strlcpy(lang_name, Lcl_languages[Lcl_current_lang].lang_name, max_len);
1384 // converts german umlauted chars from ASCII to ANSI
1385 // so they appear in the launcher
1386 // how friggin lame is this
1387 // pass in a null terminated string, foo!
1388 // returns ptr to string you sent in
1389 char* lcl_fix_umlauts(char *str, int which_way)
1393 if (which_way == LCL_TO_ANSI) {
1394 // moving to ANSI charset
1395 // run thru string and perform appropriate conversions
1396 while (str[i] != '\0') {
1423 // beta-lookin thing that means "ss"
1431 // moving to ASCII charset
1432 // run thru string and perform appropriate conversions
1433 while (str[i] != '\0') {
1460 // beta-lookin thing that means "ss"
1472 // convert some of the polish characters
1473 void lcl_fix_polish(char *str)
1476 if(*str == '\xA2') *str = '\xF3';
1477 else if(*str == '\x88') *str = '\xEA';
1481 // ------------------------------------------------------------------
1482 // lcl_translate_wep_name()
1484 // For displaying weapon names in german version
1485 // since we cant actually just change them outright.
1487 void lcl_translate_wep_name(char *name, const int max_len)
1489 if (!strcmp(name, "Morning Star")) {
1490 SDL_strlcpy(name, "Morgenstern", max_len);
1491 } else if (!strcmp(name, "MorningStar")) {
1492 SDL_strlcpy(name, "Morgenstern D", max_len);
1493 } else if (!strcmp(name, "UD-8 Kayser")) {
1494 SDL_strlcpy(name, "Kayserstrahl", max_len);
1495 } else if (!strcmp(name, "UD-D Kayser")) {
1496 SDL_strlcpy(name, "Kayserstrahl", max_len);
1500 // ------------------------------------------------------------------
1501 // lcl_translate_brief_icon_name()
1503 // For displaying ship names in german version
1504 // since we cant actually just change them outright.
1506 void lcl_translate_brief_icon_name(char *name, const int max_len)
1511 if (!SDL_strcasecmp(name, "Subspace Portal")) {
1512 SDL_strlcpy(name, "Subraum Portal", max_len);
1514 } else if (!SDL_strcasecmp(name, "Alpha Wing")) {
1515 SDL_strlcpy(name, "Alpha", max_len);
1517 } else if (!SDL_strcasecmp(name, "Beta Wing")) {
1518 SDL_strlcpy(name, "Beta", max_len);
1520 } else if (!SDL_strcasecmp(name, "Zeta Wing")) {
1521 SDL_strlcpy(name, "Zeta", max_len);
1523 } else if (!SDL_strcasecmp(name, "Capella Node")) {
1524 SDL_strlcpy(name, "Capella", max_len);
1526 } else if (!SDL_strcasecmp(name, "Hostile")) {
1527 SDL_strlcpy(name, "Gegner", max_len);
1529 } else if (!SDL_strcasecmp(name, "Hostile Craft")) {
1530 SDL_strlcpy(name, "Gegner", max_len);
1532 } else if (!SDL_strcasecmp(name, "Rebel Wing")) {
1533 SDL_strlcpy(name, "Rebellen", max_len);
1535 } else if (!SDL_strcasecmp(name, "Rebel Fleet")) {
1536 SDL_strlcpy(name, "Rebellenflotte", max_len);
1538 } else if (!SDL_strcasecmp(name, "Sentry Gun")) {
1539 SDL_strlcpy(name, "Gesch\x81tz", max_len);
1541 } else if (!SDL_strcasecmp(name, "Cargo")) {
1542 SDL_strlcpy(name, "Fracht", max_len);
1544 } else if (!SDL_strcasecmp(name, "Knossos Device")) {
1545 SDL_strlcpy(name, "Knossosger\x84t", max_len);
1547 } else if (!SDL_strcasecmp(name, "Support")) {
1548 SDL_strlcpy(name, "Versorger", max_len);
1550 } else if (!SDL_strcasecmp(name, "Unknown")) {
1551 SDL_strlcpy(name, "Unbekannt", max_len);
1553 } else if (!SDL_strcasecmp(name, "Instructor")) {
1554 SDL_strlcpy(name, "Ausbilder", max_len);
1556 } else if (!SDL_strcasecmp(name, "Jump Node")) {
1557 SDL_strlcpy(name, "Sprungknoten", max_len);
1559 } else if (!SDL_strcasecmp(name, "Escort")) {
1560 SDL_strlcpy(name, "Geleitschutz", max_len);
1562 } else if (!SDL_strcasecmp(name, "Asteroid Field")) {
1563 SDL_strlcpy(name, "Asteroidenfeld", max_len);
1565 } else if (!SDL_strcasecmp(name, "Enif Station")) {
1566 SDL_strlcpy(name, "Station Enif", max_len);
1568 } else if (!SDL_strcasecmp(name, "Rally Point")) {
1569 SDL_strlcpy(name, "Sammelpunkt", max_len);
1571 } else if ((pos = strstr(name, "Transport")) != NULL) {
1572 pos += 9; // strlen of "transport"
1573 SDL_strlcpy(buf, "Transporter", SDL_arraysize(buf));
1574 SDL_strlcat(buf, pos, SDL_arraysize(buf));
1575 SDL_strlcpy(name, buf, max_len);
1577 } else if ((pos = strstr(name, "Jump Node")) != NULL) {
1578 pos += 9; // strlen of "jump node"
1579 SDL_strlcpy(buf, "Sprungknoten", SDL_arraysize(buf));
1580 SDL_strlcat(buf, pos, SDL_arraysize(buf));
1581 SDL_strlcpy(name, buf, max_len);
1583 } else if (!SDL_strcasecmp(name, "Orion under repair")) {
1584 SDL_strlcpy(name, "Orion wird repariert", max_len);
1586 // SOTY-specific ones below!
1588 } else if (!SDL_strcasecmp(name, "Wayfarer Station")) {
1589 SDL_strlcpy(name, "Station Wayfarer", max_len);
1590 } else if (!SDL_strcasecmp(name, "Enemy")) {
1591 SDL_strlcpy(name, "Gegner", max_len);
1592 } else if (!SDL_strcasecmp(name, "Supply Depot")) {
1593 SDL_strlcpy(name, "Nachschubdepot", max_len);
1594 } else if (!SDL_strcasecmp(name, "Fighter Escort")) {
1595 SDL_strlcpy(name, "Jagdschutz", max_len);
1596 } else if (!SDL_strcasecmp(name, "Shivans")) {
1597 SDL_strlcpy(name, "Shivaner", max_len);
1598 } else if (!SDL_strcasecmp(name, "NTF Base of Operations")) {
1599 SDL_strlcpy(name, "NTF-Operationsbasis", max_len);
1600 } else if (!SDL_strcasecmp(name, "NTF Bombers")) {
1601 SDL_strlcpy(name, "NTF-Bomber", max_len);
1602 } else if (!SDL_strcasecmp(name, "NTF Fighters")) {
1603 SDL_strlcpy(name, "NTF-J\x84ger", max_len);
1604 } else if (!SDL_strcasecmp(name, "Sentry")) {
1605 SDL_strlcpy(name, "Sperrgesch\x81tz", max_len);
1606 } else if (!SDL_strcasecmp(name, "Cargo Containers")) {
1607 SDL_strlcpy(name, "Frachtbeh\x84lter", max_len);
1608 } else if (!SDL_strcasecmp(name, "NTF Reinforcements")) {
1609 SDL_strlcpy(name, "NTF-Verst\x84rkungen", max_len);
1610 } else if (!SDL_strcasecmp(name, "NTF Base")) {
1611 SDL_strlcpy(name, "NTF-St\x81tzpunkt", max_len);
1612 } else if (!SDL_strcasecmp(name, "Refugee Convoy")) {
1613 SDL_strlcpy(name, "Fl\x81""chtlingskonvoi", max_len);
1614 } else if (!SDL_strcasecmp(name, "Food Convoy")) {
1615 SDL_strlcpy(name, "Nachschubkonvoi", max_len);
1616 } else if (!SDL_strcasecmp(name, "Governor's Shuttle")) {
1617 SDL_strlcpy(name, "F\x84hre des Gouverneurs", max_len);
1618 } else if (!SDL_strcasecmp(name, "GTVA Patrol")) {
1619 SDL_strlcpy(name, "GTVA-Patrouille", max_len);
1620 } else if (!SDL_strcasecmp(name, "Escort fighters")) {
1621 SDL_strlcpy(name, "Geleitschutz", max_len);
1622 } else if (!SDL_strcasecmp(name, "Nagada Outpost")) {
1623 SDL_strlcpy(name, "Nagada-Aussenposten", max_len);
1624 } else if (!SDL_strcasecmp(name, "Fighters")) {
1625 SDL_strlcpy(name, "J\x84ger", max_len);
1626 } else if (!SDL_strcasecmp(name, "Bombers")) {
1627 SDL_strlcpy(name, "Bomber", max_len);
1628 } else if (!SDL_strcasecmp(name, "Enemy Destroyers")) {
1629 SDL_strlcpy(name, "Feindliche Zerst\x94rer", max_len);
1630 } else if (!SDL_strcasecmp(name, "Ross 128 System")) {
1631 SDL_strlcpy(name, "System Ross 128", max_len);
1632 } else if (!SDL_strcasecmp(name, "Knossos Station")) {
1633 SDL_strlcpy(name, "Knossos-Station", max_len);
1634 } else if (!SDL_strcasecmp(name, "Transporters")) {
1635 SDL_strlcpy(name, "Transporter", max_len);
1636 } else if (!SDL_strcasecmp(name, "Pirates?")) {
1637 SDL_strlcpy(name, "Piraten?", max_len);
1638 } else if (!SDL_strcasecmp(name, "Escorts")) {
1639 SDL_strlcpy(name, "Geleitschutz", max_len);
1640 } else if (!SDL_strcasecmp(name, "Shivan Fighters")) {
1641 SDL_strlcpy(name, "J\x84ger", max_len);
1642 } else if (!SDL_strcasecmp(name, "Shivan Territory")) {
1643 SDL_strlcpy(name, "Shivaner", max_len);
1647 // ------------------------------------------------------------------
1648 // lcl_translate_brief_icon_name_pl()
1650 // For displaying ship names in polish version
1651 // since we cant actually just change them outright.
1653 void lcl_translate_brief_icon_name_pl(char *name, const int max_len)
1658 if (!SDL_strcasecmp(name, "Subspace Portal")) {
1659 SDL_strlcpy(name, "Portal podprz.", max_len);
1661 } else if (!SDL_strcasecmp(name, "Alpha Wing")) {
1662 SDL_strlcpy(name, "Alfa", max_len);
1664 } else if (!SDL_strcasecmp(name, "Beta Wing")) {
1665 SDL_strlcpy(name, "Beta", max_len);
1667 } else if (!SDL_strcasecmp(name, "Zeta Wing")) {
1668 SDL_strlcpy(name, "Zeta", max_len);
1670 } else if (!SDL_strcasecmp(name, "Capella Node")) {
1671 SDL_strlcpy(name, "Capella", max_len);
1673 } else if (!SDL_strcasecmp(name, "Hostile")) {
1674 SDL_strlcpy(name, "Wr\xF3g", max_len);
1676 } else if (!SDL_strcasecmp(name, "Hostile Craft")) {
1677 SDL_strlcpy(name, "Wr\xF3g", max_len);
1679 } else if (!SDL_strcasecmp(name, "Rebel Wing")) {
1680 SDL_strlcpy(name, "Rebelianci", max_len);
1682 } else if (!SDL_strcasecmp(name, "Rebel Fleet")) {
1683 SDL_strlcpy(name, "Flota Rebelii", max_len);
1685 } else if (!SDL_strcasecmp(name, "Sentry Gun")) {
1686 SDL_strlcpy(name, "Dzia\xB3o str.", max_len);
1688 } else if (!SDL_strcasecmp(name, "Cargo")) {
1689 SDL_strlcpy(name, "\xA3\x61\x64unek", max_len);
1691 } else if (!SDL_strcasecmp(name, "Knossos Device")) {
1692 SDL_strlcpy(name, "Urz. Knossos, max_len", max_len);
1694 } else if (!SDL_strcasecmp(name, "Support")) {
1695 SDL_strlcpy(name, "Wsparcie", max_len);
1697 } else if (!SDL_strcasecmp(name, "Unknown")) {
1698 SDL_strlcpy(name, "Nieznany", max_len);
1700 } else if (!SDL_strcasecmp(name, "Instructor")) {
1701 SDL_strlcpy(name, "Instruktor", max_len);
1703 } else if (!SDL_strcasecmp(name, "Jump Node")) {
1704 SDL_strlcpy(name, "W\xEAze\xB3 skokowy", max_len);
1706 } else if (!SDL_strcasecmp(name, "Escort")) {
1707 SDL_strlcpy(name, "Eskorta", max_len);
1709 } else if (!SDL_strcasecmp(name, "Asteroid Field")) {
1710 SDL_strlcpy(name, "Pole asteroid", max_len);
1712 } else if (!SDL_strcasecmp(name, "Enif Station")) {
1713 SDL_strlcpy(name, "Stacja Enif", max_len);
1715 } else if (!SDL_strcasecmp(name, "Rally Point")) {
1716 SDL_strlcpy(name, "Pkt zborny", max_len);
1718 } else if ((pos = strstr(name, "Transport")) != NULL) {
1719 pos += 9; // strlen of "transport"
1720 SDL_strlcpy(buf, "Transporter", SDL_arraysize(buf));
1721 SDL_strlcat(buf, pos, SDL_arraysize(buf));
1722 SDL_strlcpy(name, buf, max_len);
1724 } else if ((pos = strstr(name, "Jump Node")) != NULL) {
1725 pos += 9; // strlen of "jump node"
1726 SDL_strlcpy(buf, "W\xEAze\xB3 skokowy", SDL_arraysize(buf));
1727 SDL_strlcat(buf, pos, SDL_arraysize(buf));
1728 SDL_strlcpy(name, buf, max_len);
1730 } else if (!SDL_strcasecmp(name, "Orion under repair")) {
1731 SDL_strlcpy(name, "Naprawiany Orion", max_len);
1735 // ------------------------------------------------------------------
1736 // lcl_translate_ship_name()
1738 // For displaying ship names in german version in the briefing
1739 // since we cant actually just change them outright.
1741 void lcl_translate_ship_name(char *name, const int max_len)
1743 if (!strcmp(name, "GTDR Amazon Advanced")) {
1744 SDL_strlcpy(name, "GTDR Amazon VII", max_len);
1748 // ------------------------------------------------------------------
1749 // lcl_translate_targetbox_name()
1751 // For displaying ship names in german version in the targetbox
1752 // since we cant actually just change them outright.
1754 void lcl_translate_targetbox_name(char *name, const int max_len)
1759 if ((pos = strstr(name, "Sentry")) != NULL) {
1760 pos += 6; // strlen of "sentry"
1761 SDL_strlcpy(buf, "Sperrgesch\x81tz", SDL_arraysize(buf));
1762 SDL_strlcat(buf, pos, SDL_arraysize(buf));
1763 SDL_strlcpy(name, buf, max_len);
1765 } else if ((pos = strstr(name, "Support")) != NULL) {
1766 pos += 7; // strlen of "support"
1767 SDL_strlcpy(buf, "Versorger", SDL_arraysize(buf));
1768 SDL_strlcat(buf, pos, SDL_arraysize(buf));
1769 SDL_strlcpy(name, buf, max_len);
1771 } else if ((pos = strstr(name, "Unknown")) != NULL) {
1772 pos += 7; // strlen of "unknown"
1773 SDL_strlcpy(buf, "Unbekannt", SDL_arraysize(buf));
1774 SDL_strlcat(buf, pos, SDL_arraysize(buf));
1775 SDL_strlcpy(name, buf, max_len);
1777 } else if ((pos = strstr(name, "Drone")) != NULL) {
1778 pos += 5; // strlen of "drone"
1779 SDL_strlcpy(buf, "Drohne", SDL_arraysize(buf));
1780 SDL_strlcat(buf, pos, SDL_arraysize(buf));
1781 SDL_strlcpy(name, buf, max_len);
1783 } else if ((pos = strstr(name, "Jump Node")) != NULL) {
1784 pos += 9; // strlen of "jump node"
1785 SDL_strlcpy(buf, "Sprungknoten", SDL_arraysize(buf));
1786 SDL_strlcat(buf, pos, SDL_arraysize(buf));
1787 SDL_strlcpy(name, buf, max_len);
1789 } else if (!SDL_strcasecmp(name, "Instructor")) {
1790 SDL_strlcpy(name, "Ausbilder", max_len);
1792 } else if (!SDL_strcasecmp(name, "NTF Vessel")) {
1793 SDL_strlcpy(name, "NTF-Schiff", max_len);
1795 } else if (!SDL_strcasecmp(name, "Enif Station")) {
1796 SDL_strlcpy(name, "Station Enif", max_len);
1800 // ------------------------------------------------------------------
1801 // lcl_translate_targetbox_name_pl()
1803 // For displaying ship names in polish version in the targetbox
1804 // since we cant actually just change them outright.
1806 void lcl_translate_targetbox_name_pl(char *name, const int max_len)
1811 if ((pos = strstr(name, "Sentry")) != NULL) {
1812 pos += 6; // strlen of "sentry"
1813 SDL_strlcpy(buf, "Stra\xBFnik", SDL_arraysize(buf));
1814 SDL_strlcat(buf, pos, SDL_arraysize(buf));
1815 SDL_strlcpy(name, buf, max_len);
1817 } else if ((pos = strstr(name, "Support")) != NULL) {
1818 pos += 7; // strlen of "support"
1819 SDL_strlcpy(buf, "Wsparcie", SDL_arraysize(buf));
1820 SDL_strlcat(buf, pos, SDL_arraysize(buf));
1821 SDL_strlcpy(name, buf, max_len);
1823 } else if ((pos = strstr(name, "Unknown")) != NULL) {
1824 pos += 7; // strlen of "unknown"
1825 SDL_strlcpy(buf, "Nieznany", SDL_arraysize(buf));
1826 SDL_strlcat(buf, pos, SDL_arraysize(buf));
1827 SDL_strlcpy(name, buf, max_len);
1829 } else if ((pos = strstr(name, "Drone")) != NULL) {
1830 pos += 5; // strlen of "drone"
1831 SDL_strlcpy(buf, "Sonda", SDL_arraysize(buf));
1832 SDL_strlcat(buf, pos, SDL_arraysize(buf));
1833 SDL_strlcpy(name, buf, max_len);
1835 } else if ((pos = strstr(name, "Jump Node")) != NULL) {
1836 pos += 9; // strlen of "jump node"
1837 SDL_strlcpy(buf, "W\xEAze\xB3 skokowy", SDL_arraysize(buf));
1838 SDL_strlcat(buf, pos, SDL_arraysize(buf));
1839 SDL_strlcpy(name, buf, max_len);
1841 } else if (!SDL_strcasecmp(name, "Instructor")) {
1842 SDL_strlcpy(name, "Instruktor", max_len);
1844 } else if (!SDL_strcasecmp(name, "NTF Vessel")) {
1845 SDL_strlcpy(name, "Okr\xEAt NTF", max_len);
1847 } else if (!SDL_strcasecmp(name, "Enif Station")) {
1848 SDL_strlcpy(name, "Stacja Enif", max_len);