]> icculus.org git repositories - taylor/freespace2.git/blob - src/localization/localize.cpp
use a better multi_sw_ok_to_commit() check
[taylor/freespace2.git] / src / localization / localize.cpp
1 /*
2  * Copyright (C) Volition, Inc. 1999.  All rights reserved.
3  *
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
6  * the source.
7  */
8
9 /*
10  * $Logfile: /Freespace2/code/Localization/localize.cpp $
11  * $Revision$
12  * $Date$
13  * $Author$
14  *
15  *
16  * $Log$
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
19  *
20  * Revision 1.9  2005/10/01 21:35:51  taylor
21  * various FS1 related changes, removes need to extra fs1.vp file
22  *
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
29  *
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
32  *
33  * Revision 1.6  2003/06/11 18:30:32  taylor
34  * plug memory leaks
35  *
36  * Revision 1.5  2003/06/03 04:00:40  taylor
37  * Polish language support (Janusz Dziemidowicz)
38  *
39  * Revision 1.4  2003/05/18 03:55:30  taylor
40  * automatic language selection support
41  *
42  * Revision 1.3  2002/06/09 04:41:22  relnev
43  * added copyright header
44  *
45  * Revision 1.2  2002/05/07 03:16:46  theoddone33
46  * The Great Newline Fix
47  *
48  * Revision 1.1.1.1  2002/05/03 03:28:09  root
49  * Initial import.
50  *
51  * 
52  * 91    6/16/00 3:16p Jefff
53  * sim of the year dvd version changes, a few german soty localization
54  * fixes
55  * 
56  * 90    11/02/99 8:20p Jefff
57  * more translation changes/additions
58  * 
59  * 89    11/02/99 3:24p Jefff
60  * added translation functions for a few key instances where english was
61  * showing up
62  * 
63  * 88    10/29/99 10:40p Jefff
64  * mo stirngs
65  * 
66  * 87    10/28/99 2:05a Jefff
67  * safety fix in localization stuff
68  * 
69  * 86    10/26/99 10:53a Jefff
70  * German builds now correctly default to German on first run
71  * 
72  * 85    10/25/99 11:22p Jefff
73  * mo' strings (launcher)
74  * 
75  * 84    10/25/99 5:46p Jefff
76  * Many localization fixes/changes for German builds
77  * 
78  * 83    10/14/99 3:25p Jefff
79  * soo...many....xstrs....
80  * 
81  * 82    10/14/99 2:52p Jefff
82  * localization fixes.  added support for hi-res specific xstr offsets
83  * 
84  * 81    10/13/99 3:58p Jefff
85  * added a bunch of missed XSTRs
86  * 
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.
90  * 
91  * 79    9/08/99 9:59a Jefff
92  * 
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.
97  * 
98  * 77    9/07/99 1:54p Jefff
99  * skip mission strings added
100  * 
101  * 76    9/03/99 10:55a Jefff
102  * xstr_size++
103  * 
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
108  * main hall.
109  * 
110  * 74    8/27/99 12:04a Dave
111  * Campaign loop screen.
112  * 
113  * 73    8/26/99 12:48p Jefff
114  * 
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.
118  * 
119  * 71    8/22/99 1:55p Dave
120  * Cleaned up host/team-captain leaving code.
121  * 
122  * 70    8/17/99 7:32p Jefff
123  * mo' strings
124  * 
125  * 69    8/16/99 5:15p Dave
126  * Upped string count.
127  * 
128  * 68    8/16/99 4:04p Dave
129  * Big honking checkin.
130  * 
131  * 67    8/11/99 2:09p Jefff
132  * ill give you three guesses.
133  * 
134  * 66    8/10/99 7:57p Jefff
135  * even more strings
136  * 
137  * 65    8/05/99 2:40p Jefff
138  * added string
139  * 
140  * 64    8/02/99 9:13p Dave
141  * Added popup tips.
142  * 
143  * 63    8/02/99 12:01p Jefff
144  * added a string
145  * 
146  * 62    7/29/99 10:47p Dave
147  * Standardized D3D fogging using vertex fog. Shook out Savage 4 bugs.
148  * 
149  * 61    7/29/99 9:44a Jefff
150  * added some strings for ingame objectives screen
151  * 
152  * 60    7/27/99 7:15p Jefff
153  * 3 new interface strings
154  * 
155  * 59    7/27/99 6:53p Dave
156  * Hi-res main hall support.
157  * 
158  * 58    7/24/99 1:54p Dave
159  * Hud text flash gauge. Reworked dead popup to use 4 buttons in red-alert
160  * missions.
161  * 
162  * 57    7/22/99 4:08p Jefff
163  * some new strings
164  * 
165  * 56    7/19/99 7:20p Dave
166  * Beam tooling. Specialized player-killed-self messages. Fixed d3d nebula
167  * pre-rendering.
168  * 
169  * 55    7/19/99 2:13p Dave
170  * Added some new strings for Heiko.
171  * 
172  * 54    7/15/99 2:37p Jefff
173  * 
174  * 53    7/13/99 6:07p Jefff
175  * Added support for localization string offsets.
176  * 
177  * 52    7/09/99 10:32p Dave
178  * Command brief and red alert screens.
179  * 
180  * 51    6/25/99 2:51p Jasons
181  * New string.
182  * 
183  * 50    6/25/99 11:59a Dave
184  * Multi options screen.
185  * 
186  * 49    6/24/99 12:34a Dave
187  * Main options screen.
188  * 
189  * 48    6/19/99 3:58p Dave
190  * More new strings.
191  * 
192  * 47    6/14/99 5:19p Dave
193  * 
194  * 46    6/04/99 11:32a Dave
195  * Added reset text to ship select screen. Fixed minor xstr bug in ui
196  * window
197  * 
198  * 45    6/01/99 6:07p Dave
199  * New loading/pause/please wait bar.
200  * 
201  * 44    6/01/99 4:03p Dave
202  * Changed some popup flag #defines. Upped string count to 1336
203  * 
204  * 43    5/26/99 11:46a Dave
205  * Added ship-blasting lighting and made the randomization of lighting
206  * much more customizable.
207  * 
208  * 42    5/22/99 6:05p Dave
209  * Fixed a few localization # problems.
210  * 
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.
214  * 
215  * 40    5/17/99 2:52p Dave
216  * Fixed dumb localization problem.
217  * 
218  * 39    5/17/99 10:07a Dave
219  * Updated string count.
220  * 
221  * 38    5/04/99 6:38p Dave
222  * Finished multi join-wait screen.
223  * 
224  * 37    5/04/99 5:20p Dave
225  * Fixed up multiplayer join screen and host options screen. Should both
226  * be at 100% now.
227  * 
228  * 36    5/03/99 11:04p Dave
229  * Most of the way done with the multi join screen.
230  * 
231  * 35    5/03/99 8:32p Dave
232  * New version of multi host options screen.
233  * 
234  * 34    4/09/99 2:28p Dave
235  * Upped Xstring count.
236  * 
237  * 33    4/09/99 2:21p Dave
238  * Multiplayer beta stuff. CD checking.
239  * 
240  * 32    4/08/99 12:50p Neilk
241  * changed number of xstr constant
242  * 
243  * 31    4/07/99 5:54p Dave
244  * Fixes for encryption in updatelauncher.
245  * 
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.
250  * 
251  * 29    3/23/99 3:35p Andsager
252  * Add error check for duplicate string indices
253  * 
254  * 28    3/22/99 2:56p Andsager
255  * Localize Launcher Updater
256  * 
257  * 27    2/23/99 11:18a Andsager
258  * Localize launcher using strings.tbl
259  * 
260  * 26    2/22/99 9:35p Andsager
261  * Add lcl_get_language_name() returns string with current lang.  Added
262  * localization for launcher.
263  * 
264  * 25    2/21/99 6:02p Dave
265  * Fixed standalone WSS packets. 
266  * 
267  * 24    2/17/99 2:11p Dave
268  * First full run of squad war. All freespace and tracker side stuff
269  * works.
270  * 
271  * 23    2/12/99 6:15p Dave
272  * Pre-mission Squad War code is 95% done.
273  * 
274  * 22    2/02/99 7:27p Neilk
275  * implemented lc_close() to free up some string memory when shutting down
276  * 
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.
280  * 
281  * 20    1/29/99 7:56p Neilk
282  * Added new mission log interface
283  * 
284  * 19    1/29/99 4:17p Dave
285  * New interface screens.
286  * 
287  * 18    1/29/99 12:47a Dave
288  * Put in sounds for beam weapon. A bunch of interface screens (tech
289  * database stuff).
290  * 
291  * 17    1/13/99 2:10p Andsager
292  * added string
293  * 
294  * 16    12/14/98 12:13p Dave
295  * Spiffed up xfer system a bit. Put in support for squad logo file xfer.
296  * Need to test now.
297  * 
298  * 15    12/03/98 5:22p Dave
299  * Ported over Freespace 1 multiplayer ships.tbl and weapons.tbl
300  * checksumming.
301  * 
302  * 14    12/02/98 5:47p Dave
303  * Put in interface xstr code. Converted barracks screen to new format.
304  * 
305  * 13    12/01/98 4:46p Dave
306  * Put in targa bitmap support (16 bit).
307  *  
308  * $NoKeywords: $
309  */
310
311
312 #include <ctype.h>
313 #include "cfile.h"
314 #include "localize.h"
315 #include "parselo.h"
316 #include "osregistry.h"
317 #include "encrypt.h"
318 #include "2d.h"
319
320 // ------------------------------------------------------------------------------------------------------------
321 // LOCALIZE DEFINES/VARS
322 //
323
324 // general language/localization data ---------------------
325
326 // current language
327 int Lcl_current_lang = LCL_ENGLISH;
328
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
333 } lang_info;
334
335 lang_info Lcl_languages[LCL_NUM_LANGUAGES] = {
336         { "English",            "" },                                                                   // english
337         { "German",                     "gr" },                                                         // german
338         { "French",                     "fr" },                                                         // french
339         { "Polish",                     "pl" },                                                         // polish
340 };
341
342 //#if defined(GERMAN_BUILD)
343 //#define DEFAULT_LANGUAGE                                                      "German"
344 //#else
345 #define DEFAULT_LANGUAGE                                                        "English"
346 //#endif
347
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;
354
355 // use these to replace *_BUILD, values before
356 // only 1 will be active at a time
357 int Lcl_fr = 0;
358 int Lcl_gr = 0 ;
359 int Lcl_pl = 0;
360 int Lcl_english = 1;
361
362
363 // executable string localization data --------------------
364
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
371
372
373 // struct to allow for strings.tbl-determined x offset
374 // offset is 0 for english, by default
375 typedef struct {
376         char *str;
377         int  offset_x;                          // string offset in 640
378         int  offset_x_hi;                       // string offset in 1024
379 } lcl_xstr;
380
381 //char *Xstr_table[XSTR_SIZE];
382 lcl_xstr Xstr_table[XSTR_SIZE];
383 int Xstr_inited = 0;
384
385
386 // table/mission externalization stuff --------------------
387
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;
392
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
403 int Ts_text_size;
404 int Ts_id_text_size;
405
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;
417
418 #ifdef MAKE_FS1
419 // strings.tbl for FS1, hard coded
420 extern const char *FS1_trans[LCL_NUM_LANGUAGES_FS1][LCL_NUM_STRINGS_FS1];
421 #endif
422
423 // ------------------------------------------------------------------------------------------------------------
424 // LOCALIZE FORWARD DECLARATIONS
425 //
426
427 // associate table file externalization with the specified input file
428 void lcl_ext_associate(const char *filename);
429
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);
432
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);
435
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);
438
439 // if the char is a valid char for a signed integer value string
440 int lcl_is_valid_numeric_char(char c);
441
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
447 //
448 int lcl_ext_lookup_sub(char *text, char *out, const int max_out, int id);
449
450 // initialize the pointer array into tstrings.tbl (call from lcl_ext_open() ONLY)
451 void lcl_ext_setup_pointers();
452
453
454 // ------------------------------------------------------------------------------------------------------------
455 // LOCALIZE FUNCTIONS
456 //
457
458 // initialize localization, if no language is passed - use the language specified in the registry
459 void lcl_init(int lang_init)
460 {
461         char lang_string[128];
462         const char *ret;
463         int lang, idx;
464
465         // initialize encryption
466         encrypt_init();
467
468         // read the language from the registry
469         if(lang_init < 0){
470                 memset(lang_string, 0, 128);
471
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);
475
476                 if(ret == NULL){
477                         Int3();
478                         SDL_strlcpy(lang_string, DEFAULT_LANGUAGE, SDL_arraysize(lang_string));
479                 } else {
480                         SDL_strlcpy(lang_string, ret, SDL_arraysize(lang_string));
481                 }
482
483                 // look it up
484                 lang = -1;
485                 for(idx=0; idx<LCL_NUM_LANGUAGES; idx++){
486                         if(!SDL_strcasecmp(Lcl_languages[idx].lang_name, lang_string)){
487                                 lang = idx;
488                                 break;
489                         }
490                 }
491                 if(lang < 0){
492                         lang = 0;
493                 }       
494         } else {
495                 SDL_assert((lang_init >= 0) && (lang_init < LCL_NUM_LANGUAGES));
496                 lang = lang_init;
497         }
498
499 #ifdef MAKE_FS1
500         if (lang >= LCL_NUM_LANGUAGES_FS1)
501                 lang = 0;
502 #endif
503
504         // language markers
505         Lcl_pointer_count = 0;
506
507         // associate the table string file
508         lcl_ext_associate(TABLE_STRING_FILENAME);               
509
510         // set the language (this function takes care of setting up file pointers)
511         lcl_set_language(lang);         
512 }
513
514 // added 2.2.99 by NeilK to take care of fs2 launcher memory leaks
515 // shutdown localization
516 void lcl_close()
517 {
518         // if the filename exists, free it up
519         if(Lcl_ext_filename != NULL){
520                 free(Lcl_ext_filename);
521         }
522
523         // free the Xstr_table
524         lcl_xstr_close();
525 };
526
527 // determine what language we're running in, see LCL_* defines above
528 int lcl_get_language()
529 {
530         return Lcl_current_lang;
531 }
532
533 // initialize the xstr table
534 void lcl_xstr_init()
535 {
536 #ifndef MAKE_FS1
537         int i;
538         char chr, buf[4096];
539         char language_tag[512]; 
540         int z, index;
541         char *p_offset = NULL;
542         int offset_lo = 0, offset_hi = 0;
543         int num_offsets_on_this_line = 0;
544
545         for (i=0; i<XSTR_SIZE; i++){
546                 Xstr_table[i].str = NULL;
547         }
548
549         // make sure localization is NOT running
550         lcl_ext_close();
551
552         try {
553                 read_file_text("strings.tbl");
554                 reset_parse();
555
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"));
562                 }               
563
564                 // parse all the strings in this section of the table
565                 while (!check_for_string("#")) {
566                         stuff_int(&index);
567                         stuff_string(buf, F_NAME, NULL, 4096);
568
569                         if(Lcl_pl) {
570                             lcl_fix_polish(buf);
571                         }
572
573                         i = strlen(buf);
574                         while (i--) {
575                                 if (!isspace(buf[i])) {
576                                         break;
577                                 }
578                         }
579
580                         // trim unneccesary end of string
581                         if (i >= 0) {
582                                 // SDL_assert(buf[i] == '"');
583                                 if (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;
589                                         if (buf[i] != '"') {
590                                                 // could have a 2nd offset value (one for 640, one for 1024)
591                                                 // so back up again
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;
595                                         }
596
597                                         p_offset = &buf[i+1];                   // get ptr to string section with offset in it
598                                         if (buf[i] != '"') {
599                                                 Error(LOCATION, NOX("Strings.tbl is corrupt"));         // now its an error
600                                         }
601                                 }
602
603                                 buf[i] = 0;
604                         }
605
606                         // copy string into buf
607                         z = 0;
608                         for (i=1; buf[i]; i++) {
609                                 chr = buf[i];
610                                 if (chr == '\\') {
611                                         chr = buf[++i];
612                                         if (chr == 'n') {
613                                                 chr = '\n';
614                                         } else if (chr == 'r') {
615                                                 chr = '\r';
616                                         }
617
618                                 }
619
620                                 buf[z++] = chr;
621                         }
622
623                         // null terminator on buf
624                         buf[z] = 0;
625
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);
630                                 }
631                                 Xstr_table[index].str = strdup(buf);
632                         }
633
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"));
639                                 }
640                         }
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;
644                         } else {
645                                 Xstr_table[index].offset_x_hi = offset_lo;                                              
646                         }
647
648                         // clear out our vars
649                         p_offset = NULL;
650                         offset_lo = 0;
651                         offset_hi = 0;
652                         num_offsets_on_this_line = 0;
653                 }
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));
657         }
658 #else
659         int i;
660
661         SDL_assert(XSTR_SIZE == LCL_NUM_STRINGS_FS1);
662         SDL_assert(Lcl_current_lang < LCL_NUM_LANGUAGES_FS1);
663         
664         for (i=0; i<XSTR_SIZE; i++) {
665                 if ( !strlen(FS1_trans[Lcl_current_lang][i]) ) {
666                         Xstr_table[i].str = NULL;
667                 } else {
668                         Xstr_table[i].str = (char *)FS1_trans[Lcl_current_lang][i];
669                 }
670
671                 Xstr_table[i].offset_x = 0;
672                 Xstr_table[i].offset_x_hi = 0;
673         }
674 #endif
675
676         Xstr_inited = 1;
677 }
678
679
680 // free Xstr table
681 void lcl_xstr_close()
682 {
683 #ifndef MAKE_FS1
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;
688                 }
689         }
690 #endif
691 }
692
693
694 // set our current language
695 void lcl_set_language(int lang)
696 {
697         Lcl_current_lang = lang;
698
699         nprintf(("General", "Setting language to %s\n", Lcl_languages[lang].lang_name));
700
701         // flag the proper language as being active
702         Lcl_fr = 0;
703         Lcl_gr = 0;
704         Lcl_pl = 0;
705         Lcl_english = 0;
706         switch(lang){
707         case LCL_ENGLISH:
708                 Lcl_english = 1;                
709                 Lcl_special_chars = LCL_SPECIAL_CHARS;
710                 break;
711         case LCL_FRENCH:
712                 Lcl_fr = 1;
713                 Lcl_special_chars = LCL_SPECIAL_CHARS_FR;
714                 break;
715         case LCL_GERMAN:
716                 Lcl_gr = 1;
717                 Lcl_special_chars = LCL_SPECIAL_CHARS_GR;
718                 break;
719         case LCL_POLISH:
720                 Lcl_pl = 1;
721                 Lcl_special_chars = LCL_SPECIAL_CHARS_PL;
722                 break;
723         }
724
725         // set to 0, so lcl_ext_open() knows to reset file pointers
726         Lcl_pointer_count = 0;
727
728         // reset file pointers to the proper language-section
729         if(Lcl_current_lang != LCL_DEFAULT_LANGUAGE){
730                 lcl_ext_setup_pointers();
731         }
732 }
733
734 // maybe add on an appropriate subdirectory when opening a localized file
735 void lcl_add_dir(char *current_path, const int max_len)
736 {
737         char last_char;
738         int path_len;
739
740         // if the disk extension is 0 length, don't add enything
741         if (strlen(Lcl_languages[Lcl_current_lang].lang_ext) <= 0) {
742                 return;
743         }
744
745         // get the length of the string so far
746         path_len = strlen(current_path);
747         if (path_len <= 0) {
748                 return;
749         }
750         
751         // get the current last char
752         last_char = current_path[path_len - 1];
753
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);
758         } 
759         // otherwise add a slash, then copy in the disk extension
760         else {
761                 SDL_strlcat(current_path, DIR_SEPARATOR_STR, max_len);
762                 SDL_strlcat(current_path, Lcl_languages[Lcl_current_lang].lang_ext, max_len);
763         }
764 }
765
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)
768 {
769         char temp[MAX_PATH_LEN];
770
771         // if the disk extension is 0 length, don't add enything
772         if (strlen(Lcl_languages[Lcl_current_lang].lang_ext) <= 0) {
773                 return;
774         }
775
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';
782         } else {
783                 SDL_strlcpy(temp, last_slash+1, SDL_arraysize(temp));
784                 last_slash[1] = '\0';
785         }
786
787         // add extension
788         SDL_strlcat(current_path, Lcl_languages[Lcl_current_lang].lang_ext, path_len);
789         SDL_strlcat(current_path, DIR_SEPARATOR_STR, path_len);
790
791         // copy rest of filename from temp
792         SDL_strlcat(current_path, temp, path_len);
793 }
794
795
796 // externalization of table/mission files ----------------------- 
797
798 // open the externalization file for use during parsing (call before parsing a given file)
799 void lcl_ext_open()
800 {
801         // if the file is already open, do nothing
802         SDL_assert(Lcl_ext_file == NULL);       
803
804         // if we're running in the default language, do nothing
805         if(Lcl_current_lang == LCL_DEFAULT_LANGUAGE){
806                 return;
807         }
808
809 #ifndef MAKE_FS1
810         // otherwise open the file
811         Lcl_ext_file = cfopen(Lcl_ext_filename, "rt");
812         if(Lcl_ext_file == NULL){
813                 return;
814         }
815 #endif
816 }
817
818 // close the externalization file (call after parsing a given file)
819 void lcl_ext_close()
820 {
821         // if the file is not open, do nothing
822         if(Lcl_ext_file == NULL){
823                 return;
824         }
825
826         // if we're running in the default language, do nothing
827         if(Lcl_current_lang == LCL_DEFAULT_LANGUAGE){
828                 return;
829         }
830                 
831         // otherwise close it
832         cfclose(Lcl_ext_file);
833         Lcl_ext_file = NULL;
834 }
835
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)
840 // XSTR("whee", 20)
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)
844 {                       
845         char first_four[5];
846         char text_str[2048]="";
847         char lookup_str[2048]="";
848         int str_id;     
849         int str_len;    
850
851         SDL_assert(in);
852         SDL_assert(out);
853
854         // default (non-external string) value
855         if(id != NULL){
856                 *id = -2;
857         }       
858
859         str_len = strlen(in);
860
861         // if the string is < 9 chars, it can't be an XSTR("",) tag, so just copy it
862         if(str_len < 9){
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);
865                         return;
866                 }               
867                 SDL_strlcpy(out, in, max_len);
868                 if(id != NULL){
869                         *id = -2;
870                 }
871                 return;
872         }
873
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")){
878                 // NOT an XSTR() tag
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);
881                         return;
882                 }               
883                 SDL_strlcpy(out, in, max_len);
884                 if(id != NULL){
885                         *id = -2;
886                 }
887                 return;
888         }
889
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)){
892                 Int3();
893                 SDL_strlcpy(out, in, max_len);
894                 if(id != NULL){
895                         *id = -1;
896                 }
897                 return;
898         }
899         if(!lcl_ext_get_id(in, &str_id)){
900                 SDL_strlcpy(out, in, max_len);
901                 if(id != NULL){
902                         *id = -1;
903                 }
904                 return;
905         }
906         
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);
910                 if(id != NULL){
911                         *id = str_id;
912                 }
913                 return;
914         }               
915
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));
920
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);
924                 } else {
925                         SDL_strlcpy(out, lookup_str, max_len);
926                 }
927
928         }
929         // otherwise use what we have - probably should Int3() or assert here
930         else {
931                 SDL_strlcpy(out, text_str, max_len);
932         }       
933
934         // set the id #
935         if(id != NULL){
936                 *id = str_id;
937         }
938 }
939
940 // translate the specified string based upon the current language
941 const char *XSTR(const char *str, int index)
942 {
943         if(!Xstr_inited){
944                 return str;
945         }
946
947         // perform a lookup
948         if (index >= 0 && index < XSTR_SIZE) {
949                 if (Xstr_table[index].str){
950                         return Xstr_table[index].str;  // return translation of string
951                 }
952         }
953
954         // can't translate, return original english string
955         return str;
956 }
957
958 // retrieve the offset for a localized string
959 int lcl_get_xstr_offset(int index, int res)
960 {
961         if (res == GR_640) {
962                 return Xstr_table[index].offset_x;
963         } else {
964                 return Xstr_table[index].offset_x_hi;
965         }
966 }
967
968
969 // ------------------------------------------------------------------------------------------------------------
970 // LOCALIZE FORWARD DEFINITIONS
971 //
972
973 // associate table file externalization with the specified input file
974 void lcl_ext_associate(const char *filename)
975 {
976 #ifndef MAKE_FS1
977         // if the filename already exists, free it up
978         if(Lcl_ext_filename != NULL){
979                 free(Lcl_ext_filename);
980         }
981
982         // set the new filename
983         Lcl_ext_filename = strdup(filename);
984 #endif
985 }
986
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)
989 {
990         int str_start = 0, str_end = 0;
991         int str_len;
992         char *p, *p2;
993
994         SDL_assert(xstr != NULL);
995         SDL_assert(out != NULL);
996         str_len = strlen(xstr);
997         
998         // this is some crazy wack-ass code.
999         // look for the open quote
1000         p = strstr(xstr, "\"");
1001         if(p == NULL){
1002                 error_display(0, "Error parsing XSTR() tag %s\n", xstr);                
1003                 return 0;
1004         } else {
1005                 str_start = p - xstr + 1;               
1006         }
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);                
1010                 return 0;
1011         }
1012
1013         // look for the close quote
1014         p2 = strstr(p+1, "\"");
1015         if(p2 == NULL){
1016                 error_display(0, "Error parsing XSTR() tag %s\n", xstr);                
1017                 return 0;
1018         } else {
1019                 str_end = p2 - xstr;
1020         }
1021
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);     
1024
1025         if(Lcl_pl) {
1026             lcl_fix_polish(out);
1027         }
1028
1029         // success
1030         return 1;
1031 }
1032
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)
1035 {
1036         char *p, *pnext;
1037         int str_len;
1038
1039         SDL_assert(xstr != NULL);
1040         SDL_assert(out != NULL);
1041         
1042         str_len = strlen(xstr);
1043
1044         // find the first quote
1045         p = strstr(xstr, "\"");
1046         if(p == NULL){
1047                 error_display(0, "Error parsing id# in XSTR() tag %s\n", xstr);
1048                 return 0;
1049         }
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);
1053                 return 0;
1054         }
1055         p++;
1056
1057         // continue searching until we find the close quote
1058         while(1){
1059                 pnext = strstr(p, "\"");
1060                 if(pnext == NULL){
1061                         error_display(0, "Error parsing id# in XSTR() tag %s\n", xstr);
1062                         return 0;
1063                 }
1064
1065                 // if the previous char is a \, we know its not the "end-of-string" quote
1066                 if(*(pnext - 1) != '\\'){
1067                         p = pnext;
1068                         break;
1069                 }
1070
1071                 // continue
1072                 p = pnext;
1073         }
1074
1075         // search until we find a ,     
1076         pnext = strstr(p, ",");
1077         if(pnext == NULL){
1078                 error_display(0, "Error parsing id# in XSTR() tag %s\n", xstr);
1079                 return 0;
1080         }
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);
1084                 return 0;
1085         }
1086         pnext++;
1087         
1088         // now get the id string
1089         p = pnext;
1090         pnext = strtok(p, ")");
1091         if(pnext == NULL){
1092                 error_display(0, "Error parsing id# in XSTR() tag %s\n", xstr);
1093                 return 0;
1094         }
1095
1096         // get the value and we're done
1097         *out = atoi(pnext);
1098
1099         // success
1100         return 1;
1101 }
1102
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)
1105 {
1106         char text[1024];
1107         int ret;
1108         int pointer;
1109         
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);
1115
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);
1119
1120         // reset parsing vars and go to town
1121         Ts_current_state = TS_SCANNING;
1122         Ts_id_text_size = 0;
1123         Ts_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);
1128                         
1129                 // run the line parse function          
1130                 switch(ret & 0x0fffffff){
1131                 // error
1132                 case 0 :
1133                         Int3();                 // should never get here - it means the string doens't exist in the table!!
1134                         return 0;
1135
1136                 // success parsing the line - please continue
1137                 case 1 :
1138                         break;
1139
1140                 // found a matching string/id pair
1141                 case 2 :                        
1142                         // success
1143                         if (Lcl_gr) {
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);             
1147                         }       
1148                         return 1;
1149
1150                 // end of language found
1151                 case 3 :
1152                         Int3();                 // should never get here - it means the string doens't exist in the table!!
1153                         return 0;               
1154                 }
1155         }
1156         
1157         Int3();                 // should never get here - it means the string doens't exist in the table!!
1158         return 0;
1159 }
1160
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
1165 //
1166 int lcl_ext_lookup_sub(char *text, char *out, const int max_out, int id)
1167 {
1168         char *p;                                        // current ptr
1169         int len = strlen(text);
1170         int count;      
1171         char text_copy[1024];   
1172         char *tok;
1173         int found_new_string_id = 0;
1174
1175         p = text;
1176         count = 0;                      
1177         while(count < len){
1178                 // do something useful
1179                 switch(Ts_current_state){               
1180                 // scanning for a line of text
1181                 case TS_SCANNING:
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")){
1186                                 return 3;
1187                         }
1188                         // if its a commented line, skip it
1189                         else if((text[0] == ';') || (text[0] == ' ') || (text[0] == '\n')){
1190                                 return 1;
1191                         }
1192                         // otherwise we should have an ID #, so stuff it and move to the proper state
1193                         else {
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;
1199
1200                                         found_new_string_id = 1;
1201                                 }
1202                                 // error
1203                                 else {
1204                                         Int3();
1205                                         return 0;
1206                                 }
1207                         }
1208                         break;
1209
1210                 // scanning in an id string
1211                 case TS_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;
1215                         }
1216                         // if we found a comma, our id# is finished, look for the open quote
1217                         else if(*p == ','){
1218                                 Ts_current_state = TS_OPEN_QUOTE;
1219                         } else {
1220                                 Int3();
1221                                 return 0;
1222                         }
1223                         break;
1224
1225                 case TS_OPEN_QUOTE:
1226                         // valid space or an open quote
1227                         if((*p == ' ') || (*p == '\"')){
1228                                 if(*p == '\"'){
1229                                         memset(Ts_text, 0, PARSE_TEXT_STRING_LEN);
1230                                         Ts_text_size = 0;
1231                                         Ts_current_state = TS_STRING;
1232                                 }
1233                         } else {
1234                                 Int3();
1235                                 return 0;
1236                         }
1237                         break;
1238
1239                 case 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;
1244
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);
1248
1249                                         return found_new_string_id ? (1<<1) | (1<<31) : (1<<1);                                 
1250                                 }
1251                                 
1252                                 // otherwise, just continue parsing                             
1253                                 return found_new_string_id ? (1<<0) | (1<<31) : (1<<0);
1254                         } 
1255                         // otherwise add to the string
1256                         else {
1257                                 Ts_text[Ts_text_size++] = *p;
1258                         }                                                                               
1259                         break;
1260                 }               
1261
1262                 // if we have a newline, return success, we're done with this line
1263                 if(*p == '\n'){
1264                         return found_new_string_id ? (1<<0) | (1<<31) : (1<<0);
1265                 }
1266
1267                 // next char in the line
1268                 p++;
1269                 count++;
1270         }       
1271
1272         // success
1273         return found_new_string_id ? (1<<0) | (1<<31) : (1<<0);
1274 }
1275
1276 // if the char is a valid char for a signed integer value
1277 int lcl_is_valid_numeric_char(char c)
1278 {
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;
1281 }
1282
1283 // initialize the pointer array into tstrings.tbl (call from lcl_ext_open() ONLY)
1284 void lcl_ext_setup_pointers()
1285 {
1286         char language_string[128];
1287         char line[1024];
1288         char *tok;      
1289         int string_count;
1290         int ret;
1291         int found_start = 0;
1292
1293         // open the localization file
1294         lcl_ext_open();
1295         if(Lcl_ext_file == NULL){
1296 #ifndef MAKE_FS1
1297                 error_display(0, "Error opening externalization file! File likely does not exist or could not be found");
1298 #endif
1299                 return;
1300         }
1301
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));
1307         } else {
1308                 SDL_strlcat(language_string, Lcl_languages[Lcl_current_lang].lang_name, SDL_arraysize(language_string));
1309         }
1310         memset(line, 0, 1024);
1311
1312         // reset seek variables and begin               
1313         Lcl_pointer_count = 0;
1314         while(cfgets(line, 1024, Lcl_ext_file)){
1315                 tok = strtok(line, " \n");
1316                 if(tok == NULL){
1317                         continue;                       
1318                 }
1319                 
1320                 // if the language matches, we're good to start parsing strings
1321                 if(!SDL_strcasecmp(language_string, tok)){
1322                         found_start = 1;                        
1323                         break;
1324                 }               
1325         }
1326
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");
1330                 lcl_ext_close();
1331                 return;
1332         }
1333
1334         string_count = 0;       
1335         while(cfgets(line, 1024, Lcl_ext_file)){
1336                 ret = lcl_ext_lookup_sub(line, NULL, 0, -1);
1337
1338                 // do stuff
1339                 switch(ret & 0x0fffffff){
1340                 // error
1341                 case 0 :
1342                         lcl_ext_close();
1343                         return;         
1344
1345                 // end of language found
1346                 case 3 :
1347                         // mark one final pointer
1348                         Lcl_pointers[Lcl_pointer_count++] = cftell(Lcl_ext_file) - strlen(line) - 1;
1349                         lcl_ext_close();
1350                         return;
1351                 }
1352
1353                 // the only other case we care about is the beginning of a new id#
1354                 if(ret & (1<<31)){              
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;
1358
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");
1362                                         lcl_ext_close();
1363                                         return;
1364                                 }
1365                         }
1366                         // increment string count
1367                         string_count++;                 
1368                 }
1369         }
1370
1371         // should never get here. we should always be exiting through case 3 (end of language section) of the above switch
1372         // statement
1373         Int3();
1374         lcl_ext_close();
1375 }
1376
1377 void lcl_get_language_name(char *lang_name, const int max_len)
1378 {
1379         SDL_assert(LCL_NUM_LANGUAGES == 3);
1380
1381         SDL_strlcpy(lang_name, Lcl_languages[Lcl_current_lang].lang_name, max_len);
1382 }
1383
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)
1390 {
1391         int i=0;
1392
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') {
1397                         switch (str[i]) {
1398                         case '\x81':
1399                                 // lower umlaut u
1400                                 str[i] = '\xFC';
1401                                 break;
1402                         case '\x84':
1403                                 // lower umlaut a
1404                                 str[i] = '\xE4';
1405                                 break;
1406                         case '\x94':
1407                                 // lower umlaut o
1408                                 str[i] = '\xF6';
1409                                 break;
1410                         case '\x9A':
1411                                 // upper umlaut u
1412                                 str[i] = '\xDC';
1413                                 break;
1414                         case '\x8E':
1415                                 // upper umlaut a
1416                                 str[i] = '\xC4';
1417                                 break;
1418                         case '\x99':
1419                                 // upper umlaut o
1420                                 str[i] = '\xD6';
1421                                 break;
1422                         case '\xE1':
1423                                 // beta-lookin thing that means "ss"
1424                                 str[i] = '\xDF';
1425                                 break;
1426                         }
1427
1428                         i++;
1429                 }
1430         } else {
1431                 // moving to ASCII charset
1432                 // run thru string and perform appropriate conversions
1433                 while (str[i] != '\0') {
1434                         switch (str[i]) {
1435                         case '\xFC':
1436                                 // lower umlaut u
1437                                 str[i] = '\x81';
1438                                 break;
1439                         case '\xE4':
1440                                 // lower umlaut a
1441                                 str[i] = '\x84';
1442                                 break;
1443                         case '\xF6':
1444                                 // lower umlaut o
1445                                 str[i] = '\x94';
1446                                 break;
1447                         case '\xDC':
1448                                 // upper umlaut u
1449                                 str[i] = '\x9A';
1450                                 break;
1451                         case '\xC4':
1452                                 // upper umlaut a
1453                                 str[i] = '\x8E';
1454                                 break;
1455                         case '\xD6':
1456                                 // upper umlaut o
1457                                 str[i] = '\x99';
1458                                 break;
1459                         case '\xDF':
1460                                 // beta-lookin thing that means "ss"
1461                                 str[i] = '\xE1';
1462                                 break;
1463                         }
1464
1465                         i++;
1466                 }
1467         }
1468
1469         return str;
1470 }
1471
1472 // convert some of the polish characters
1473 void lcl_fix_polish(char *str)
1474 {
1475     for(;*str;str++) {
1476         if(*str == '\xA2') *str = '\xF3';
1477         else if(*str == '\x88') *str = '\xEA';
1478     }
1479 }
1480
1481 // ------------------------------------------------------------------
1482 // lcl_translate_wep_name()
1483 //
1484 // For displaying weapon names in german version
1485 // since we cant actually just change them outright.
1486 //
1487 void lcl_translate_wep_name(char *name, const int max_len)
1488 {
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);
1497         }
1498 }
1499
1500 // ------------------------------------------------------------------
1501 // lcl_translate_brief_icon_name()
1502 //
1503 // For displaying ship names in german version
1504 // since we cant actually just change them outright.
1505 //
1506 void lcl_translate_brief_icon_name(char *name, const int max_len)
1507 {
1508         char *pos;
1509         char buf[128];
1510
1511         if (!SDL_strcasecmp(name, "Subspace Portal")) { 
1512                 SDL_strlcpy(name, "Subraum Portal", max_len);
1513
1514         } else if (!SDL_strcasecmp(name, "Alpha Wing")) {
1515                 SDL_strlcpy(name, "Alpha", max_len);
1516
1517         } else if (!SDL_strcasecmp(name, "Beta Wing")) {
1518                 SDL_strlcpy(name, "Beta", max_len);
1519
1520         } else if (!SDL_strcasecmp(name, "Zeta Wing")) {
1521                 SDL_strlcpy(name, "Zeta", max_len);
1522
1523         } else if (!SDL_strcasecmp(name, "Capella Node")) {
1524                 SDL_strlcpy(name, "Capella", max_len);
1525
1526         } else if (!SDL_strcasecmp(name, "Hostile")) {
1527                 SDL_strlcpy(name, "Gegner", max_len);
1528
1529         } else if (!SDL_strcasecmp(name, "Hostile Craft")) {
1530                 SDL_strlcpy(name, "Gegner", max_len);
1531
1532         } else if (!SDL_strcasecmp(name, "Rebel Wing")) {
1533                 SDL_strlcpy(name, "Rebellen", max_len);
1534
1535         } else if (!SDL_strcasecmp(name, "Rebel Fleet")) {
1536                 SDL_strlcpy(name, "Rebellenflotte", max_len);
1537
1538         } else if (!SDL_strcasecmp(name, "Sentry Gun")) {
1539                 SDL_strlcpy(name, "Gesch\x81tz", max_len);
1540
1541         } else if (!SDL_strcasecmp(name, "Cargo")) {
1542                 SDL_strlcpy(name, "Fracht", max_len);
1543
1544         } else if (!SDL_strcasecmp(name, "Knossos Device")) {
1545                 SDL_strlcpy(name, "Knossosger\x84t", max_len);
1546         
1547         } else if (!SDL_strcasecmp(name, "Support")) {
1548                 SDL_strlcpy(name, "Versorger", max_len);
1549
1550         } else if (!SDL_strcasecmp(name, "Unknown")) {
1551                 SDL_strlcpy(name, "Unbekannt", max_len);
1552
1553         } else if (!SDL_strcasecmp(name, "Instructor")) {
1554                 SDL_strlcpy(name, "Ausbilder", max_len);
1555         
1556         } else if (!SDL_strcasecmp(name, "Jump Node")) {
1557                 SDL_strlcpy(name, "Sprungknoten", max_len);
1558
1559         } else if (!SDL_strcasecmp(name, "Escort")) {
1560                 SDL_strlcpy(name, "Geleitschutz", max_len);
1561
1562         } else if (!SDL_strcasecmp(name, "Asteroid Field")) {
1563                 SDL_strlcpy(name, "Asteroidenfeld", max_len);
1564
1565         } else if (!SDL_strcasecmp(name, "Enif Station")) {
1566                 SDL_strlcpy(name, "Station Enif", max_len);
1567
1568         } else if (!SDL_strcasecmp(name, "Rally Point")) {
1569                 SDL_strlcpy(name, "Sammelpunkt", max_len);
1570
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);
1576
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);
1582         
1583         } else if (!SDL_strcasecmp(name, "Orion under repair")) {
1584                 SDL_strlcpy(name, "Orion wird repariert", max_len);
1585
1586         // SOTY-specific ones below!
1587         
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);
1644         }
1645 }
1646
1647 // ------------------------------------------------------------------
1648 // lcl_translate_brief_icon_name_pl()
1649 //
1650 // For displaying ship names in polish version
1651 // since we cant actually just change them outright.
1652 //
1653 void lcl_translate_brief_icon_name_pl(char *name, const int max_len)
1654 {
1655         char *pos;
1656         char buf[128];
1657
1658         if (!SDL_strcasecmp(name, "Subspace Portal")) { 
1659                 SDL_strlcpy(name, "Portal podprz.", max_len);
1660
1661         } else if (!SDL_strcasecmp(name, "Alpha Wing")) {
1662                 SDL_strlcpy(name, "Alfa", max_len);
1663
1664         } else if (!SDL_strcasecmp(name, "Beta Wing")) {
1665                 SDL_strlcpy(name, "Beta", max_len);
1666
1667         } else if (!SDL_strcasecmp(name, "Zeta Wing")) {
1668                 SDL_strlcpy(name, "Zeta", max_len);
1669
1670         } else if (!SDL_strcasecmp(name, "Capella Node")) {
1671                 SDL_strlcpy(name, "Capella", max_len);
1672
1673         } else if (!SDL_strcasecmp(name, "Hostile")) {
1674                 SDL_strlcpy(name, "Wr\xF3g", max_len);
1675
1676         } else if (!SDL_strcasecmp(name, "Hostile Craft")) {
1677                 SDL_strlcpy(name, "Wr\xF3g", max_len);
1678
1679         } else if (!SDL_strcasecmp(name, "Rebel Wing")) {
1680                 SDL_strlcpy(name, "Rebelianci", max_len);
1681
1682         } else if (!SDL_strcasecmp(name, "Rebel Fleet")) {
1683                 SDL_strlcpy(name, "Flota Rebelii", max_len);
1684
1685         } else if (!SDL_strcasecmp(name, "Sentry Gun")) {
1686                 SDL_strlcpy(name, "Dzia\xB3o str.", max_len);
1687
1688         } else if (!SDL_strcasecmp(name, "Cargo")) {
1689                 SDL_strlcpy(name, "\xA3\x61\x64unek", max_len);
1690
1691         } else if (!SDL_strcasecmp(name, "Knossos Device")) {
1692                 SDL_strlcpy(name, "Urz. Knossos, max_len", max_len);
1693         
1694         } else if (!SDL_strcasecmp(name, "Support")) {
1695                 SDL_strlcpy(name, "Wsparcie", max_len);
1696
1697         } else if (!SDL_strcasecmp(name, "Unknown")) {
1698                 SDL_strlcpy(name, "Nieznany", max_len);
1699
1700         } else if (!SDL_strcasecmp(name, "Instructor")) {
1701                 SDL_strlcpy(name, "Instruktor", max_len);
1702         
1703         } else if (!SDL_strcasecmp(name, "Jump Node")) {
1704                 SDL_strlcpy(name, "W\xEAze\xB3 skokowy", max_len);
1705
1706         } else if (!SDL_strcasecmp(name, "Escort")) {
1707                 SDL_strlcpy(name, "Eskorta", max_len);
1708
1709         } else if (!SDL_strcasecmp(name, "Asteroid Field")) {
1710                 SDL_strlcpy(name, "Pole asteroid", max_len);
1711
1712         } else if (!SDL_strcasecmp(name, "Enif Station")) {
1713                 SDL_strlcpy(name, "Stacja Enif", max_len);
1714
1715         } else if (!SDL_strcasecmp(name, "Rally Point")) {
1716                 SDL_strlcpy(name, "Pkt zborny", max_len);
1717
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);
1723
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);
1729         
1730         } else if (!SDL_strcasecmp(name, "Orion under repair")) {
1731                 SDL_strlcpy(name, "Naprawiany Orion", max_len);
1732         }
1733 }
1734
1735 // ------------------------------------------------------------------
1736 // lcl_translate_ship_name()
1737 //
1738 // For displaying ship names in german version in the briefing
1739 // since we cant actually just change them outright.
1740 //
1741 void lcl_translate_ship_name(char *name, const int max_len)
1742 {
1743         if (!strcmp(name, "GTDR Amazon Advanced")) {    
1744                 SDL_strlcpy(name, "GTDR Amazon VII", max_len);
1745         } 
1746 }
1747
1748 // ------------------------------------------------------------------
1749 // lcl_translate_targetbox_name()
1750 //
1751 // For displaying ship names in german version in the targetbox
1752 // since we cant actually just change them outright.
1753 //
1754 void lcl_translate_targetbox_name(char *name, const int max_len)
1755 {
1756         char *pos;
1757         char buf[128];
1758         
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);
1764
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);
1770
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);
1776
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);
1782
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);
1788
1789         } else if (!SDL_strcasecmp(name, "Instructor")) {
1790                 SDL_strlcpy(name, "Ausbilder", max_len);
1791
1792         } else if (!SDL_strcasecmp(name, "NTF Vessel")) {
1793                 SDL_strlcpy(name, "NTF-Schiff", max_len);
1794
1795         } else if (!SDL_strcasecmp(name, "Enif Station")) {
1796                 SDL_strlcpy(name, "Station Enif", max_len);
1797         }
1798 }
1799
1800 // ------------------------------------------------------------------
1801 // lcl_translate_targetbox_name_pl()
1802 //
1803 // For displaying ship names in polish version in the targetbox
1804 // since we cant actually just change them outright.
1805 //
1806 void lcl_translate_targetbox_name_pl(char *name, const int max_len)
1807 {
1808         char *pos;
1809         char buf[128];
1810         
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);
1816
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);
1822
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);
1828
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);
1834
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);
1840
1841         } else if (!SDL_strcasecmp(name, "Instructor")) {
1842                 SDL_strlcpy(name, "Instruktor", max_len);
1843
1844         } else if (!SDL_strcasecmp(name, "NTF Vessel")) {
1845                 SDL_strlcpy(name, "Okr\xEAt NTF", max_len);
1846
1847         } else if (!SDL_strcasecmp(name, "Enif Station")) {
1848                 SDL_strlcpy(name, "Stacja Enif", max_len);
1849         }
1850 }