]> icculus.org git repositories - taylor/freespace2.git/blob - src/localization/localize.cpp
Various 64-bit platform fixes
[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.8  2005/03/29 02:18:47  taylor
18  * Various 64-bit platform fixes
19  * Fix compiler errors with MAKE_FS1 and fix gr_set_bitmap() too
20  * Make sure that turrets can fire at asteroids for FS1 (needed for a couple missions)
21  * Streaming audio support (big thanks to Pierre Willenbrock!!)
22  * Removed dependance on strings.tbl for FS1 since we don't actually need it now
23  *
24  * Revision 1.7  2004/07/04 11:39:06  taylor
25  * fix missing debrief text, crash on exit, path separator's, warning fixes, no GR_SOFT
26  *
27  * Revision 1.6  2003/06/11 18:30:32  taylor
28  * plug memory leaks
29  *
30  * Revision 1.5  2003/06/03 04:00:40  taylor
31  * Polish language support (Janusz Dziemidowicz)
32  *
33  * Revision 1.4  2003/05/18 03:55:30  taylor
34  * automatic language selection support
35  *
36  * Revision 1.3  2002/06/09 04:41:22  relnev
37  * added copyright header
38  *
39  * Revision 1.2  2002/05/07 03:16:46  theoddone33
40  * The Great Newline Fix
41  *
42  * Revision 1.1.1.1  2002/05/03 03:28:09  root
43  * Initial import.
44  *
45  * 
46  * 91    6/16/00 3:16p Jefff
47  * sim of the year dvd version changes, a few german soty localization
48  * fixes
49  * 
50  * 90    11/02/99 8:20p Jefff
51  * more translation changes/additions
52  * 
53  * 89    11/02/99 3:24p Jefff
54  * added translation functions for a few key instances where english was
55  * showing up
56  * 
57  * 88    10/29/99 10:40p Jefff
58  * mo stirngs
59  * 
60  * 87    10/28/99 2:05a Jefff
61  * safety fix in localization stuff
62  * 
63  * 86    10/26/99 10:53a Jefff
64  * German builds now correctly default to German on first run
65  * 
66  * 85    10/25/99 11:22p Jefff
67  * mo' strings (launcher)
68  * 
69  * 84    10/25/99 5:46p Jefff
70  * Many localization fixes/changes for German builds
71  * 
72  * 83    10/14/99 3:25p Jefff
73  * soo...many....xstrs....
74  * 
75  * 82    10/14/99 2:52p Jefff
76  * localization fixes.  added support for hi-res specific xstr offsets
77  * 
78  * 81    10/13/99 3:58p Jefff
79  * added a bunch of missed XSTRs
80  * 
81  * 80    9/13/99 11:30a Dave
82  * Added checkboxes and functionality for disabling PXO banners as well as
83  * disabling d3d zbuffer biasing.
84  * 
85  * 79    9/08/99 9:59a Jefff
86  * 
87  * 78    9/07/99 4:01p Dave
88  * Fixed up a string.tbl paroblem (self destruct message). Make sure IPX
89  * does everything properly (setting up address when binding). Remove
90  * black rectangle background from UI_INPUTBOX.
91  * 
92  * 77    9/07/99 1:54p Jefff
93  * skip mission strings added
94  * 
95  * 76    9/03/99 10:55a Jefff
96  * xstr_size++
97  * 
98  * 75    9/03/99 1:31a Dave
99  * CD checking by act. Added support to play 2 cutscenes in a row
100  * seamlessly. Fixed super low level cfile bug related to files in the
101  * root directory of a CD. Added cheat code to set campaign mission # in
102  * main hall.
103  * 
104  * 74    8/27/99 12:04a Dave
105  * Campaign loop screen.
106  * 
107  * 73    8/26/99 12:48p Jefff
108  * 
109  * 72    8/22/99 5:53p Dave
110  * Scoring fixes. Added self destruct key. Put callsigns in the logfile
111  * instead of ship designations for multiplayer players.
112  * 
113  * 71    8/22/99 1:55p Dave
114  * Cleaned up host/team-captain leaving code.
115  * 
116  * 70    8/17/99 7:32p Jefff
117  * mo' strings
118  * 
119  * 69    8/16/99 5:15p Dave
120  * Upped string count.
121  * 
122  * 68    8/16/99 4:04p Dave
123  * Big honking checkin.
124  * 
125  * 67    8/11/99 2:09p Jefff
126  * ill give you three guesses.
127  * 
128  * 66    8/10/99 7:57p Jefff
129  * even more strings
130  * 
131  * 65    8/05/99 2:40p Jefff
132  * added string
133  * 
134  * 64    8/02/99 9:13p Dave
135  * Added popup tips.
136  * 
137  * 63    8/02/99 12:01p Jefff
138  * added a string
139  * 
140  * 62    7/29/99 10:47p Dave
141  * Standardized D3D fogging using vertex fog. Shook out Savage 4 bugs.
142  * 
143  * 61    7/29/99 9:44a Jefff
144  * added some strings for ingame objectives screen
145  * 
146  * 60    7/27/99 7:15p Jefff
147  * 3 new interface strings
148  * 
149  * 59    7/27/99 6:53p Dave
150  * Hi-res main hall support.
151  * 
152  * 58    7/24/99 1:54p Dave
153  * Hud text flash gauge. Reworked dead popup to use 4 buttons in red-alert
154  * missions.
155  * 
156  * 57    7/22/99 4:08p Jefff
157  * some new strings
158  * 
159  * 56    7/19/99 7:20p Dave
160  * Beam tooling. Specialized player-killed-self messages. Fixed d3d nebula
161  * pre-rendering.
162  * 
163  * 55    7/19/99 2:13p Dave
164  * Added some new strings for Heiko.
165  * 
166  * 54    7/15/99 2:37p Jefff
167  * 
168  * 53    7/13/99 6:07p Jefff
169  * Added support for localization string offsets.
170  * 
171  * 52    7/09/99 10:32p Dave
172  * Command brief and red alert screens.
173  * 
174  * 51    6/25/99 2:51p Jasons
175  * New string.
176  * 
177  * 50    6/25/99 11:59a Dave
178  * Multi options screen.
179  * 
180  * 49    6/24/99 12:34a Dave
181  * Main options screen.
182  * 
183  * 48    6/19/99 3:58p Dave
184  * More new strings.
185  * 
186  * 47    6/14/99 5:19p Dave
187  * 
188  * 46    6/04/99 11:32a Dave
189  * Added reset text to ship select screen. Fixed minor xstr bug in ui
190  * window
191  * 
192  * 45    6/01/99 6:07p Dave
193  * New loading/pause/please wait bar.
194  * 
195  * 44    6/01/99 4:03p Dave
196  * Changed some popup flag #defines. Upped string count to 1336
197  * 
198  * 43    5/26/99 11:46a Dave
199  * Added ship-blasting lighting and made the randomization of lighting
200  * much more customizable.
201  * 
202  * 42    5/22/99 6:05p Dave
203  * Fixed a few localization # problems.
204  * 
205  * 41    5/21/99 6:45p Dave
206  * Sped up ui loading a bit. Sped up localization disk access stuff. Multi
207  * start game screen, multi password, and multi pxo-help screen.
208  * 
209  * 40    5/17/99 2:52p Dave
210  * Fixed dumb localization problem.
211  * 
212  * 39    5/17/99 10:07a Dave
213  * Updated string count.
214  * 
215  * 38    5/04/99 6:38p Dave
216  * Finished multi join-wait screen.
217  * 
218  * 37    5/04/99 5:20p Dave
219  * Fixed up multiplayer join screen and host options screen. Should both
220  * be at 100% now.
221  * 
222  * 36    5/03/99 11:04p Dave
223  * Most of the way done with the multi join screen.
224  * 
225  * 35    5/03/99 8:32p Dave
226  * New version of multi host options screen.
227  * 
228  * 34    4/09/99 2:28p Dave
229  * Upped Xstring count.
230  * 
231  * 33    4/09/99 2:21p Dave
232  * Multiplayer beta stuff. CD checking.
233  * 
234  * 32    4/08/99 12:50p Neilk
235  * changed number of xstr constant
236  * 
237  * 31    4/07/99 5:54p Dave
238  * Fixes for encryption in updatelauncher.
239  * 
240  * 30    3/24/99 4:05p Dave
241  * Put in support for assigning the player to a specific squadron with a
242  * specific logo. Preliminary work for doing pos/orient checksumming in
243  * multiplayer to reduce bandwidth.
244  * 
245  * 29    3/23/99 3:35p Andsager
246  * Add error check for duplicate string indices
247  * 
248  * 28    3/22/99 2:56p Andsager
249  * Localize Launcher Updater
250  * 
251  * 27    2/23/99 11:18a Andsager
252  * Localize launcher using strings.tbl
253  * 
254  * 26    2/22/99 9:35p Andsager
255  * Add lcl_get_language_name() returns string with current lang.  Added
256  * localization for launcher.
257  * 
258  * 25    2/21/99 6:02p Dave
259  * Fixed standalone WSS packets. 
260  * 
261  * 24    2/17/99 2:11p Dave
262  * First full run of squad war. All freespace and tracker side stuff
263  * works.
264  * 
265  * 23    2/12/99 6:15p Dave
266  * Pre-mission Squad War code is 95% done.
267  * 
268  * 22    2/02/99 7:27p Neilk
269  * implemented lc_close() to free up some string memory when shutting down
270  * 
271  * 21    1/30/99 1:29a Dave
272  * Fixed nebula thumbnail problem. Full support for 1024x768 choose pilot
273  * screen.  Fixed beam weapon death messages.
274  * 
275  * 20    1/29/99 7:56p Neilk
276  * Added new mission log interface
277  * 
278  * 19    1/29/99 4:17p Dave
279  * New interface screens.
280  * 
281  * 18    1/29/99 12:47a Dave
282  * Put in sounds for beam weapon. A bunch of interface screens (tech
283  * database stuff).
284  * 
285  * 17    1/13/99 2:10p Andsager
286  * added string
287  * 
288  * 16    12/14/98 12:13p Dave
289  * Spiffed up xfer system a bit. Put in support for squad logo file xfer.
290  * Need to test now.
291  * 
292  * 15    12/03/98 5:22p Dave
293  * Ported over Freespace 1 multiplayer ships.tbl and weapons.tbl
294  * checksumming.
295  * 
296  * 14    12/02/98 5:47p Dave
297  * Put in interface xstr code. Converted barracks screen to new format.
298  * 
299  * 13    12/01/98 4:46p Dave
300  * Put in targa bitmap support (16 bit).
301  *  
302  * $NoKeywords: $
303  */
304
305
306 #include <ctype.h>
307 #include "cfile.h"
308 #include "localize.h"
309 #include "parselo.h"
310 #include "osregistry.h"
311 #include "encrypt.h"
312 #include "2d.h"
313
314 // ------------------------------------------------------------------------------------------------------------
315 // LOCALIZE DEFINES/VARS
316 //
317
318 // general language/localization data ---------------------
319
320 // current language
321 int Lcl_current_lang = LCL_ENGLISH;
322
323 // language info table
324 typedef struct lang_info {
325         char lang_name[LCL_LANG_NAME_LEN + 1];                          // literal name of the language
326         char lang_ext[LCL_LANG_NAME_LEN + 1];                           // for adding to names on disk access
327 } lang_info;
328
329 lang_info Lcl_languages[LCL_NUM_LANGUAGES] = {
330         { "English",            "" },                                                                   // english
331         { "German",                     "gr" },                                                         // german
332         { "French",                     "fr" },                                                         // french
333         { "Polish",                     "pl" },                                                         // polish
334 };
335
336 //#if defined(GERMAN_BUILD)
337 //#define DEFAULT_LANGUAGE                                                      "German"
338 //#else
339 #define DEFAULT_LANGUAGE                                                        "English"
340 //#endif
341
342 // following is the offset where special characters start in our font
343 #define LCL_SPECIAL_CHARS_FR    164
344 #define LCL_SPECIAL_CHARS_GR    164
345 #define LCL_SPECIAL_CHARS_PL    127
346 #define LCL_SPECIAL_CHARS               127
347 int Lcl_special_chars;
348
349 // use these to replace *_BUILD, values before
350 // only 1 will be active at a time
351 int Lcl_fr = 0;
352 int Lcl_gr = 0 ;
353 int Lcl_pl = 0;
354 int Lcl_english = 1;
355
356
357 // executable string localization data --------------------
358
359 // XSTR_SIZE is the total count of unique XSTR index numbers.  An index is used to
360 // reference an entry in strings.tbl.  This is used for translation of strings from
361 // the english version (in the code) to a foreign version (in the table).  Thus, if you
362 // add a new string to the code, you must assign it a new index.  Use the number below for
363 // that index and increase the number below by one.
364 #define XSTR_SIZE       1570
365
366
367 // struct to allow for strings.tbl-determined x offset
368 // offset is 0 for english, by default
369 typedef struct {
370         char *str;
371         int  offset_x;                          // string offset in 640
372         int  offset_x_hi;                       // string offset in 1024
373 } lcl_xstr;
374
375 //char *Xstr_table[XSTR_SIZE];
376 lcl_xstr Xstr_table[XSTR_SIZE];
377 int Xstr_inited = 0;
378
379
380 // table/mission externalization stuff --------------------
381
382 #define TABLE_STRING_FILENAME                                           "tstrings.tbl"
383 // filename of the file to use when localizing table strings
384 char *Lcl_ext_filename = NULL;
385 CFILE *Lcl_ext_file = NULL;
386
387 // for scanning/parsing tstrings.tbl (from ExStr)
388 #define PARSE_TEXT_STRING_LEN                   2048
389 #define PARSE_ID_STRING_LEN                     128
390 #define TS_SCANNING                                             0                               // scanning for a line of text
391 #define TS_ID_STRING                                            1                               // reading in an id string
392 #define TS_OPEN_QUOTE                                   2                               // looking for an open quote
393 #define TS_STRING                                                       3                               // reading in the text string itself
394 int Ts_current_state = 0;
395 char Ts_text[PARSE_TEXT_STRING_LEN];                            // string we're currently working with
396 char Ts_id_text[PARSE_ID_STRING_LEN];                           // id string we're currently working with
397 int Ts_text_size;
398 int Ts_id_text_size;
399
400 // file pointers for optimized string lookups
401 // some example times for Freespace2 startup with granularities (mostly .tbl files, ~500 strings in the table file, many looked up more than once)
402 // granularity 20                       :               13 secs
403 // granularity 10                       :               11 secs
404 // granularity 5                        :               9 secs
405 // granularity 2                        :               7-8 secs
406 #define LCL_GRANULARITY                                 1                               // how many strings between each pointer (lower granularities should give faster lookup times)
407 #define LCL_MAX_POINTERS                                4500                    // max # of pointers
408 #define LCL_MAX_STRINGS                                 (LCL_GRANULARITY * LCL_MAX_POINTERS)
409 int Lcl_pointers[LCL_MAX_POINTERS];
410 int Lcl_pointer_count = 0;
411
412
413 // ------------------------------------------------------------------------------------------------------------
414 // LOCALIZE FORWARD DECLARATIONS
415 //
416
417 // associate table file externalization with the specified input file
418 void lcl_ext_associate(char *filename);
419
420 // given a valid XSTR() tag piece of text, extract the string portion, return it in out, nonzero on success
421 int lcl_ext_get_text(char *xstr, char *out);
422
423 // given a valid XSTR() tag piece of text, extract the id# portion, return the value in out, nonzero on success
424 int lcl_ext_get_id(char *xstr, int *out);
425
426 // given a valid XSTR() id#, lookup the string in tstrings.tbl, filling in out if found, nonzero on success
427 int lcl_ext_lookup(char *out, int id);
428
429 // if the char is a valid char for a signed integer value string
430 int lcl_is_valid_numeric_char(char c);
431
432 // sub-parse function for individual lines of tstrings.tbl (from Exstr)
433 // returns : integer with the low bits having the following values :
434 // 0 on fail, 1 on success, 2 if found a matching id/string pair, 3 if end of language has been found
435 // 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
436 // so be sure to mask this value out to get the low portion of the return value
437 //
438 int lcl_ext_lookup_sub(char *text, char *out, int id);
439
440 // initialize the pointer array into tstrings.tbl (call from lcl_ext_open() ONLY)
441 void lcl_ext_setup_pointers();
442
443
444 // ------------------------------------------------------------------------------------------------------------
445 // LOCALIZE FUNCTIONS
446 //
447
448 // initialize localization, if no language is passed - use the language specified in the registry
449 void lcl_init(int lang_init)
450 {
451         char lang_string[128];
452         char *ret;
453         int lang, idx;
454
455         // initialize encryption
456         encrypt_init();
457
458         // read the language from the registry
459         if(lang_init < 0){
460                 memset(lang_string, 0, 128);
461
462                 // default to DEFAULT_LANGUAGE (which should be english so we dont have to put German text 
463                 // in tstrings in the #default section
464                 ret = os_config_read_string(NULL, "Language", DEFAULT_LANGUAGE);
465
466                 if(ret == NULL){
467                         Int3();
468                         strcpy(lang_string, DEFAULT_LANGUAGE);
469                 } else {
470                         strcpy(lang_string, ret);
471                 }
472
473                 // look it up
474                 lang = -1;
475                 for(idx=0; idx<LCL_NUM_LANGUAGES; idx++){
476                         if(!stricmp(Lcl_languages[idx].lang_name, lang_string)){
477                                 lang = idx;
478                                 break;
479                         }
480                 }
481                 if(lang < 0){
482                         lang = 0;
483                 }       
484         } else {
485                 Assert((lang_init >= 0) && (lang_init < LCL_NUM_LANGUAGES));
486                 lang = lang_init;
487         }
488
489         // language markers
490         Lcl_pointer_count = 0;
491
492         // associate the table string file
493         lcl_ext_associate(TABLE_STRING_FILENAME);               
494
495         // set the language (this function takes care of setting up file pointers)
496         lcl_set_language(lang);         
497 }
498
499 // added 2.2.99 by NeilK to take care of fs2 launcher memory leaks
500 // shutdown localization
501 void lcl_close()
502 {
503         int i;
504
505         // if the filename exists, free it up
506         if(Lcl_ext_filename != NULL){
507                 free(Lcl_ext_filename);
508         }
509
510         // free the Xstr_table
511         for (i=0; i<XSTR_SIZE; i++) {
512                 if (Xstr_table[i].str != NULL) {
513                         free(Xstr_table[i].str);
514                 }
515         }
516 };
517
518 // determine what language we're running in, see LCL_* defines above
519 int lcl_get_language()
520 {
521         return Lcl_current_lang;
522 }
523
524 // initialize the xstr table
525 void lcl_xstr_init()
526 {
527         int i;
528 #ifndef MAKE_FS1
529         char chr, buf[4096];
530         char language_tag[512]; 
531         int z, index, rval;
532         char *p_offset = NULL;
533         int offset_lo = 0, offset_hi = 0;
534 #endif
535
536         for (i=0; i<XSTR_SIZE; i++){
537                 Xstr_table[i].str = NULL;
538         }
539
540 #ifndef MAKE_FS1
541         if ((rval = setjmp(parse_abort)) != 0) {
542                 mprintf(("Error parsing 'strings.tbl'\nError code = %i.\n", rval));
543         } else {
544                 // make sure localization is NOT running
545                 lcl_ext_close();
546
547                 read_file_text("strings.tbl");
548                 reset_parse();
549
550                 // move down to the proper section              
551                 memset(language_tag, 0, 512);
552                 strcpy(language_tag, "#");
553                 strcat(language_tag, Lcl_languages[Lcl_current_lang].lang_name);
554                 if(skip_to_string(language_tag) != 1){
555                         Error(LOCATION, NOX("Strings.tbl is corrupt"));
556                 }               
557
558                 // parse all the strings in this section of the table
559                 while (!check_for_string("#")) {
560                         int num_offsets_on_this_line = 0;
561
562                         stuff_int(&index);
563                         stuff_string(buf, F_NAME, NULL, 4096);
564
565                         if(Lcl_pl) {
566                             lcl_fix_polish(buf);
567                         }
568
569                         i = strlen(buf);
570                         while (i--) {
571                                 if (!isspace(buf[i])) {
572                                         break;
573                                 }
574                         }
575
576                         // trim unneccesary end of string
577                         if (i >= 0) {
578                                 // Assert(buf[i] == '"');
579                                 if (buf[i] != '"') {
580                                         // probably an offset on this entry
581                                         buf[i+1] = 0;                                           // drop down a null terminator (prolly unnecessary)
582                                         while(!is_white_space(buf[i])) i--;     // back up over the potential offset
583                                         while(is_white_space(buf[i])) i--;      // now back up over intervening spaces
584                                         num_offsets_on_this_line = 1;
585                                         if (buf[i] != '"') {
586                                                 // could have a 2nd offset value (one for 640, one for 1024)
587                                                 // so back up again
588                                                 while(!is_white_space(buf[i])) i--;     // back up over the 2nd offset
589                                                 while(is_white_space(buf[i])) i--;      // now back up over intervening spaces
590                                                 num_offsets_on_this_line = 2;
591                                         }
592
593                                         p_offset = &buf[i+1];                   // get ptr to string section with offset in it
594                                         if (buf[i] != '"') {
595                                                 Error(LOCATION, NOX("Strings.tbl is corrupt"));         // now its an error
596                                         }
597                                 }
598
599                                 buf[i] = 0;
600                         }
601
602                         // copy string into buf
603                         z = 0;
604                         for (i=1; buf[i]; i++) {
605                                 chr = buf[i];
606                                 if (chr == '\\') {
607                                         chr = buf[++i];
608                                         if (chr == 'n') {
609                                                 chr = '\n';
610                                         } else if (chr == 'r') {
611                                                 chr = '\r';
612                                         }
613
614                                 }
615
616                                 buf[z++] = chr;
617                         }
618
619                         // null terminator on buf
620                         buf[z] = 0;
621
622                         // write into Xstr_table
623                         if (index >= 0 && index < XSTR_SIZE) {
624                                 if (Xstr_table[index].str != NULL) {
625                                         Warning(LOCATION, "Strings table index %d used more than once", index);
626                                 }
627                                 Xstr_table[index].str = strdup(buf);
628                         }
629
630                         // read offset information, assume 0 if nonexistant
631                         if (p_offset != NULL) {
632                                 if (sscanf(p_offset, "%d%d", &offset_lo, &offset_hi) < num_offsets_on_this_line) {
633                                         // whatever is in the file ain't a proper offset
634                                         Error(LOCATION, NOX("Strings.tbl is corrupt"));
635                                 }
636                         }
637                         Xstr_table[index].offset_x = offset_lo;
638                         if (num_offsets_on_this_line == 2) {
639                                 Xstr_table[index].offset_x_hi = offset_hi;
640                         } else {
641                                 Xstr_table[index].offset_x_hi = offset_lo;                                              
642                         }
643
644                         // clear out our vars
645                         p_offset = NULL;
646                         offset_lo = 0;
647                         offset_hi = 0;
648                         num_offsets_on_this_line = 0;
649                 }
650         }
651
652         Xstr_inited = 1;
653 #endif
654 }
655
656
657 // free Xstr table
658 void lcl_xstr_close()
659 {
660         for (int i=0; i<XSTR_SIZE; i++){
661                 if (Xstr_table[i].str != NULL) {
662                         free(Xstr_table[i].str);
663                         Xstr_table[i].str = NULL;
664                 }
665         }
666 }
667
668
669 // set our current language
670 void lcl_set_language(int lang)
671 {
672         Lcl_current_lang = lang;
673
674         nprintf(("General", "Setting language to %s\n", Lcl_languages[lang].lang_name));
675
676         // flag the proper language as being active
677         Lcl_fr = 0;
678         Lcl_gr = 0;
679         Lcl_pl = 0;
680         Lcl_english = 0;
681         switch(lang){
682         case LCL_ENGLISH:
683                 Lcl_english = 1;                
684                 Lcl_special_chars = LCL_SPECIAL_CHARS;
685                 break;
686         case LCL_FRENCH:
687                 Lcl_fr = 1;
688                 Lcl_special_chars = LCL_SPECIAL_CHARS_FR;
689                 break;
690         case LCL_GERMAN:
691                 Lcl_gr = 1;
692                 Lcl_special_chars = LCL_SPECIAL_CHARS_GR;
693                 break;
694         case LCL_POLISH:
695                 Lcl_pl = 1;
696                 Lcl_special_chars = LCL_SPECIAL_CHARS_PL;
697                 break;
698         }
699
700         // set to 0, so lcl_ext_open() knows to reset file pointers
701         Lcl_pointer_count = 0;
702
703         // reset file pointers to the proper language-section
704         if(Lcl_current_lang != LCL_DEFAULT_LANGUAGE){
705                 lcl_ext_setup_pointers();
706         }
707 }
708
709 // maybe add on an appropriate subdirectory when opening a localized file
710 void lcl_add_dir(char *current_path)
711 {
712         char last_char;
713         int path_len;
714
715         // if the disk extension is 0 length, don't add enything
716         if (strlen(Lcl_languages[Lcl_current_lang].lang_ext) <= 0) {
717                 return;
718         }
719
720         // get the length of the string so far
721         path_len = strlen(current_path);
722         if (path_len <= 0) {
723                 return;
724         }
725         
726         // get the current last char
727         last_char = current_path[path_len - 1];
728
729         // if the last char is a slash, just copy in the disk extension
730         if (last_char == DIR_SEPARATOR_CHAR) {
731                 strcat(current_path, Lcl_languages[Lcl_current_lang].lang_ext);
732                 strcat(current_path, DIR_SEPARATOR_STR);
733         } 
734         // otherwise add a slash, then copy in the disk extension
735         else {
736                 strcat(current_path, DIR_SEPARATOR_STR);
737                 strcat(current_path, Lcl_languages[Lcl_current_lang].lang_ext);
738         }
739 }
740
741 // maybe add localized directory to full path with file name when opening a localized file
742 void lcl_add_dir_to_path_with_filename(char *current_path)
743 {
744         char temp[MAX_PATH_LEN];
745
746         // if the disk extension is 0 length, don't add enything
747         if (strlen(Lcl_languages[Lcl_current_lang].lang_ext) <= 0) {
748                 return;
749         }
750
751         // find position of last slash and copy rest of filename (not counting slash) to temp
752         // mark end of current path with '\0', so strcat will work
753         char *last_slash = strrchr(current_path, DIR_SEPARATOR_CHAR);
754         if (last_slash == NULL) {
755                 strcpy(temp, current_path);
756                 current_path[0] = '\0';
757         } else {
758                 strcpy(temp, last_slash+1);
759                 last_slash[1] = '\0';
760         }
761
762         // add extension
763         strcat(current_path, Lcl_languages[Lcl_current_lang].lang_ext);
764         strcat(current_path, DIR_SEPARATOR_STR);
765
766         // copy rest of filename from temp
767         strcat(current_path, temp);
768 }
769
770
771 // externalization of table/mission files ----------------------- 
772
773 // open the externalization file for use during parsing (call before parsing a given file)
774 void lcl_ext_open()
775 {
776         // if the file is already open, do nothing
777         Assert(Lcl_ext_file == NULL);   
778
779         // if we're running in the default language, do nothing
780         if(Lcl_current_lang == LCL_DEFAULT_LANGUAGE){
781                 return;
782         }
783
784         // otherwise open the file
785         Lcl_ext_file = cfopen(Lcl_ext_filename, "rt");
786         if(Lcl_ext_file == NULL){
787                 return;
788         }               
789 }
790
791 // close the externalization file (call after parsing a given file)
792 void lcl_ext_close()
793 {
794         // if the file is not open, do nothing
795         if(Lcl_ext_file == NULL){
796                 return;
797         }
798
799         // if we're running in the default language, do nothing
800         if(Lcl_current_lang == LCL_DEFAULT_LANGUAGE){
801                 return;
802         }
803                 
804         // otherwise close it
805         cfclose(Lcl_ext_file);
806         Lcl_ext_file = NULL;
807 }
808
809 // get the localized version of the string. if none exists, return the original string
810 // valid input to this function includes :
811 // "this is some text"
812 // XSTR("wheeee", -1)
813 // XSTR("whee", 20)
814 // and these should cover all the externalized string cases
815 // fills in id if non-NULL. a value of -2 indicates it is not an external string
816 void lcl_ext_localize(char *in, char *out, int max_len, int *id)
817 {                       
818         char first_four[5];
819         char text_str[2048]="";
820         char lookup_str[2048]="";
821         int str_id;     
822         int str_len;    
823
824         Assert(in);
825         Assert(out);
826
827         // default (non-external string) value
828         if(id != NULL){
829                 *id = -2;
830         }       
831
832         str_len = strlen(in);
833
834         // if the string is < 9 chars, it can't be an XSTR("",) tag, so just copy it
835         if(str_len < 9){
836                 if(str_len > max_len){
837                         error_display(0, "Token too long: [%s].  Length = %i.  Max is %i.\n", in, str_len, max_len);
838                         return;
839                 }               
840                 strcpy(out, in);
841                 if(id != NULL){
842                         *id = -2;
843                 }
844                 return;
845         }
846
847         // otherwise, check to see if it's an XSTR() tag
848         memset(first_four, 0, 5);
849         strncpy(first_four, in, 4);
850         if(stricmp(first_four, "XSTR")){
851                 // NOT an XSTR() tag
852                 if(str_len > max_len){
853                         error_display(0, "Token too long: [%s].  Length = %i.  Max is %i.\n", in, str_len, max_len);
854                         return;
855                 }               
856                 strcpy(out, in);
857                 if(id != NULL){
858                         *id = -2;
859                 }
860                 return;
861         }
862
863         // at this point we _know_ its an XSTR() tag, so split off the strings and id sections          
864         if(!lcl_ext_get_text(in, text_str)){
865                 Int3();
866                 strcpy(out, in);
867                 if(id != NULL){
868                         *id = -1;
869                 }
870                 return;
871         }
872         if(!lcl_ext_get_id(in, &str_id)){
873                 strcpy(out, in);
874                 if(id != NULL){
875                         *id = -1;
876                 }
877                 return;
878         }
879         
880         // if the localization file is not open, or we're running in the default language, return the original string
881         if((Lcl_ext_file == NULL) || (str_id < 0) || (Lcl_current_lang == LCL_DEFAULT_LANGUAGE)){
882                 strcpy(out, text_str);
883                 if(id != NULL){
884                         *id = str_id;
885                 }
886                 return;
887         }               
888
889         // attempt to find the string
890         if(lcl_ext_lookup(lookup_str, str_id)){
891                 // copy to the outgoing string
892                 Assert(strlen(lookup_str) <= (unsigned int)(max_len - 1));
893
894                 if (strlen(lookup_str) > (unsigned int)(max_len-1)) {
895                         // be safe and truncate string to fit
896                         strncpy(out, lookup_str, (size_t) (max_len-1));
897                         out[max_len-1] = '\0';          // ensure null terminator, since strncpy(...) doesnt.
898                 } else {
899                         strcpy(out, lookup_str);
900                 }
901
902         }
903         // otherwise use what we have - probably should Int3() or assert here
904         else {
905                 strcpy(out, text_str);
906         }       
907
908         // set the id #
909         if(id != NULL){
910                 *id = str_id;
911         }
912 }
913
914 // translate the specified string based upon the current language
915 char *XSTR(char *str, int index)
916 {
917         if(!Xstr_inited){
918                 return str;
919         }
920
921         // perform a lookup
922         if (index >= 0 && index < XSTR_SIZE) {
923                 if (Xstr_table[index].str){
924                         return Xstr_table[index].str;  // return translation of string
925                 }
926         }
927
928         // can't translate, return original english string
929         return str;
930 }
931
932 // retrieve the offset for a localized string
933 int lcl_get_xstr_offset(int index, int res)
934 {
935         if (res == GR_640) {
936                 return Xstr_table[index].offset_x;
937         } else {
938                 return Xstr_table[index].offset_x_hi;
939         }
940 }
941
942
943 // ------------------------------------------------------------------------------------------------------------
944 // LOCALIZE FORWARD DEFINITIONS
945 //
946
947 // associate table file externalization with the specified input file
948 void lcl_ext_associate(char *filename)
949 {
950         // if the filename already exists, free it up
951         if(Lcl_ext_filename != NULL){
952                 free(Lcl_ext_filename);
953         }
954
955         // set the new filename
956         Lcl_ext_filename = strdup(filename);
957 }
958
959 // given a valid XSTR() tag piece of text, extract the string portion, return it in out, nonzero on success
960 int lcl_ext_get_text(char *xstr, char *out)
961 {
962         int str_start, str_end;
963         int str_len;
964         char *p, *p2;
965
966         Assert(xstr != NULL);
967         Assert(out != NULL);
968         str_len = strlen(xstr);
969         
970         // this is some crazy wack-ass code.
971         // look for the open quote
972         str_start = str_end = 0;
973         p = strstr(xstr, "\"");
974         if(p == NULL){
975                 error_display(0, "Error parsing XSTR() tag %s\n", xstr);                
976                 return 0;
977         } else {
978                 str_start = p - xstr + 1;               
979         }
980         // make sure we're not about to walk past the end of the string
981         if((p - xstr) >= str_len){
982                 error_display(0, "Error parsing XSTR() tag %s\n", xstr);                
983                 return 0;
984         }
985
986         // look for the close quote
987         p2 = strstr(p+1, "\"");
988         if(p2 == NULL){
989                 error_display(0, "Error parsing XSTR() tag %s\n", xstr);                
990                 return 0;
991         } else {
992                 str_end = p2 - xstr;
993         }
994
995         // now that we know the boundaries of the actual string in the XSTR() tag. copy it
996         memcpy(out, xstr + str_start, str_end - str_start);     
997
998         if(Lcl_pl) {
999             lcl_fix_polish(out);
1000         }
1001
1002         // success
1003         return 1;
1004 }
1005
1006 // given a valid XSTR() tag piece of text, extract the id# portion, return the value in out, nonzero on success
1007 int lcl_ext_get_id(char *xstr, int *out)
1008 {
1009         char *p, *pnext;
1010         int str_len;
1011
1012         Assert(xstr != NULL);
1013         Assert(out != NULL);
1014         
1015         str_len = strlen(xstr);
1016
1017         // find the first quote
1018         p = strstr(xstr, "\"");
1019         if(p == NULL){
1020                 error_display(0, "Error parsing id# in XSTR() tag %s\n", xstr);
1021                 return 0;
1022         }
1023         // make sure we're not about to walk off the end of the string
1024         if((p - xstr) >= str_len){
1025                 error_display(0, "Error parsing id# in XSTR() tag %s\n", xstr);
1026                 return 0;
1027         }
1028         p++;
1029
1030         // continue searching until we find the close quote
1031         while(1){
1032                 pnext = strstr(p, "\"");
1033                 if(pnext == NULL){
1034                         error_display(0, "Error parsing id# in XSTR() tag %s\n", xstr);
1035                         return 0;
1036                 }
1037
1038                 // if the previous char is a \, we know its not the "end-of-string" quote
1039                 if(*(pnext - 1) != '\\'){
1040                         p = pnext;
1041                         break;
1042                 }
1043
1044                 // continue
1045                 p = pnext;
1046         }
1047
1048         // search until we find a ,     
1049         pnext = strstr(p, ",");
1050         if(pnext == NULL){
1051                 error_display(0, "Error parsing id# in XSTR() tag %s\n", xstr);
1052                 return 0;
1053         }
1054         // make sure we're not about to walk off the end of the string
1055         if((pnext - xstr) >= str_len){
1056                 error_display(0, "Error parsing id# in XSTR() tag %s\n", xstr);
1057                 return 0;
1058         }
1059         pnext++;
1060         
1061         // now get the id string
1062         p = pnext;
1063         pnext = strtok(p, ")");
1064         if(pnext == NULL){
1065                 error_display(0, "Error parsing id# in XSTR() tag %s\n", xstr);
1066                 return 0;
1067         }
1068
1069         // get the value and we're done
1070         *out = atoi(pnext);
1071
1072         // success
1073         return 1;
1074 }
1075
1076 // given a valid XSTR() id#, lookup the string in tstrings.tbl, filling in out if found, nonzero on success
1077 int lcl_ext_lookup(char *out, int id)
1078 {
1079         char text[1024];
1080         int ret;
1081         int pointer;
1082         
1083         Assert(Lcl_pointer_count >= 0);
1084         Assert(Lcl_pointers[0] >= 0);
1085         Assert(Lcl_pointers[Lcl_pointer_count - 1] >= 0);
1086         Assert(Lcl_ext_file != NULL);
1087         Assert(id >= 0);
1088
1089         // seek to the closest pointer <= the id# we're looking for
1090         pointer = id / LCL_GRANULARITY;
1091         cfseek(Lcl_ext_file, Lcl_pointers[pointer], CF_SEEK_SET);
1092
1093         // reset parsing vars and go to town
1094         Ts_current_state = TS_SCANNING;
1095         Ts_id_text_size = 0;
1096         Ts_text_size = 0;
1097         memset(Ts_text, 0, PARSE_TEXT_STRING_LEN);
1098         memset(Ts_id_text, 0, PARSE_ID_STRING_LEN);
1099         while((cftell(Lcl_ext_file) < Lcl_pointers[Lcl_pointer_count - 1]) && cfgets(text, 1024, Lcl_ext_file)){
1100                 ret = lcl_ext_lookup_sub(text, out, id);
1101                         
1102                 // run the line parse function          
1103                 switch(ret & 0x0fffffff){
1104                 // error
1105                 case 0 :
1106                         Int3();                 // should never get here - it means the string doens't exist in the table!!
1107                         return 0;
1108
1109                 // success parsing the line - please continue
1110                 case 1 :
1111                         break;
1112
1113                 // found a matching string/id pair
1114                 case 2 :                        
1115                         // success
1116                         if (Lcl_gr) {
1117                                 // this is because tstrings.tbl reads in as ANSI for some reason
1118                                 // opening tstrings with "rb" mode didnt seem to help, so its now still "rt" like before
1119                                 lcl_fix_umlauts(out, LCL_TO_ASCII);             
1120                         }       
1121                         return 1;
1122
1123                 // end of language found
1124                 case 3 :
1125                         Int3();                 // should never get here - it means the string doens't exist in the table!!
1126                         return 0;               
1127                 }
1128         }
1129         
1130         Int3();                 // should never get here - it means the string doens't exist in the table!!
1131         return 0;
1132 }
1133
1134 // sub-parse function for individual lines of tstrings.tbl (from Exstr)
1135 // returns : integer with the low bits having the following values :
1136 // 0 on fail, 1 on success, 2 if found a matching id/string pair, 3 if end of language has been found
1137 // 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
1138 //
1139 int lcl_ext_lookup_sub(char *text, char *out, int id)
1140 {
1141         char *front;                    // front of the line
1142         char *p;                                        // current ptr
1143         int len = strlen(text);
1144         int count;      
1145         char text_copy[1024];   
1146         char *tok;
1147         int found_new_string_id = 0;
1148
1149         front = text;
1150         p = text;
1151         count = 0;                      
1152         while(count < len){
1153                 // do something useful
1154                 switch(Ts_current_state){               
1155                 // scanning for a line of text
1156                 case TS_SCANNING:
1157                         // if the first word is #end, we're done with the file altogether
1158                         strcpy(text_copy, text);
1159                         tok = strtok(text_copy, " \n");
1160                         if((tok != NULL) && !stricmp(tok, "#end")){
1161                                 return 3;
1162                         }
1163                         // if its a commented line, skip it
1164                         else if((text[0] == ';') || (text[0] == ' ') || (text[0] == '\n')){
1165                                 return 1;
1166                         }
1167                         // otherwise we should have an ID #, so stuff it and move to the proper state
1168                         else {
1169                                 if(lcl_is_valid_numeric_char(*p)){
1170                                         memset(Ts_id_text, 0, PARSE_ID_STRING_LEN);
1171                                         Ts_id_text_size = 0;
1172                                         Ts_id_text[Ts_id_text_size++] = *p;
1173                                         Ts_current_state = TS_ID_STRING;
1174
1175                                         found_new_string_id = 1;
1176                                 }
1177                                 // error
1178                                 else {
1179                                         Int3();
1180                                         return 0;
1181                                 }
1182                         }
1183                         break;
1184
1185                 // scanning in an id string
1186                 case TS_ID_STRING:
1187                         // if we have another valid char
1188                         if(lcl_is_valid_numeric_char(*p)){
1189                                 Ts_id_text[Ts_id_text_size++] = *p;
1190                         }
1191                         // if we found a comma, our id# is finished, look for the open quote
1192                         else if(*p == ','){
1193                                 Ts_current_state = TS_OPEN_QUOTE;
1194                         } else {
1195                                 Int3();
1196                                 return 0;
1197                         }
1198                         break;
1199
1200                 case TS_OPEN_QUOTE:
1201                         // valid space or an open quote
1202                         if((*p == ' ') || (*p == '\"')){
1203                                 if(*p == '\"'){
1204                                         memset(Ts_text, 0, PARSE_TEXT_STRING_LEN);
1205                                         Ts_text_size = 0;
1206                                         Ts_current_state = TS_STRING;
1207                                 }
1208                         } else {
1209                                 Int3();
1210                                 return 0;
1211                         }
1212                         break;
1213
1214                 case TS_STRING:
1215                         // if we have an end quote, we need to look for a comma
1216                         if((*p == '\"') /*&& (Ts_text_size > 0)*/ && (Ts_text[Ts_text_size - 1] != '\\')){
1217                                 // we're now done - we have a string
1218                                 Ts_current_state = TS_SCANNING;
1219
1220                                 // if the id#'s match, copy the string and return "string found"
1221                                 if((atoi(Ts_id_text) == id) && (out != NULL)){
1222                                         strcpy(out, Ts_text);
1223
1224                                         return found_new_string_id ? (1<<1) | (1<<31) : (1<<1);                                 
1225                                 }
1226                                 
1227                                 // otherwise, just continue parsing                             
1228                                 return found_new_string_id ? (1<<0) | (1<<31) : (1<<0);
1229                         } 
1230                         // otherwise add to the string
1231                         else {
1232                                 Ts_text[Ts_text_size++] = *p;
1233                         }                                                                               
1234                         break;
1235                 }               
1236
1237                 // if we have a newline, return success, we're done with this line
1238                 if(*p == '\n'){
1239                         return found_new_string_id ? (1<<0) | (1<<31) : (1<<0);
1240                 }
1241
1242                 // next char in the line
1243                 p++;
1244                 count++;
1245         }       
1246
1247         // success
1248         return found_new_string_id ? (1<<0) | (1<<31) : (1<<0);
1249 }
1250
1251 // if the char is a valid char for a signed integer value
1252 int lcl_is_valid_numeric_char(char c)
1253 {
1254         return ( (c == '-') || (c == '0') || (c == '1') || (c == '2') || (c == '3') || (c == '4') ||
1255                                 (c == '5') || (c == '6') || (c == '7') || (c == '8') || (c == '9') ) ? 1 : 0;
1256 }
1257
1258 // initialize the pointer array into tstrings.tbl (call from lcl_ext_open() ONLY)
1259 void lcl_ext_setup_pointers()
1260 {
1261         char language_string[128];
1262         char line[1024];
1263         char *tok;      
1264         int string_count;
1265         int ret;
1266         int found_start = 0;
1267
1268         // open the localization file
1269         lcl_ext_open();
1270         if(Lcl_ext_file == NULL){
1271                 error_display(0, "Error opening externalization file! File likely does not exist or could not be found");
1272                 return;
1273         }
1274
1275         // seek to the currently active language
1276         memset(language_string, 0, 128);
1277         strcpy(language_string, "#");
1278         if(!stricmp(DEFAULT_LANGUAGE, Lcl_languages[Lcl_current_lang].lang_name)){
1279                 strcat(language_string, "default");
1280         } else {
1281                 strcat(language_string, Lcl_languages[Lcl_current_lang].lang_name);
1282         }
1283         memset(line, 0, 1024);
1284
1285         // reset seek variables and begin               
1286         Lcl_pointer_count = 0;
1287         while(cfgets(line, 1024, Lcl_ext_file)){
1288                 tok = strtok(line, " \n");
1289                 if(tok == NULL){
1290                         continue;                       
1291                 }
1292                 
1293                 // if the language matches, we're good to start parsing strings
1294                 if(!stricmp(language_string, tok)){
1295                         found_start = 1;                        
1296                         break;
1297                 }               
1298         }
1299
1300         // if we didn't find the language specified, error
1301         if(found_start <= 0){
1302                 error_display(0, "Could not find specified langauge in tstrings.tbl!\n");
1303                 lcl_ext_close();
1304                 return;
1305         }
1306
1307         string_count = 0;       
1308         while(cfgets(line, 1024, Lcl_ext_file)){
1309                 ret = lcl_ext_lookup_sub(line, NULL, -1);
1310
1311                 // do stuff
1312                 switch(ret & 0x0fffffff){
1313                 // error
1314                 case 0 :
1315                         lcl_ext_close();
1316                         return;         
1317
1318                 // end of language found
1319                 case 3 :
1320                         // mark one final pointer
1321                         Lcl_pointers[Lcl_pointer_count++] = cftell(Lcl_ext_file) - strlen(line) - 1;
1322                         lcl_ext_close();
1323                         return;
1324                 }
1325
1326                 // the only other case we care about is the beginning of a new id#
1327                 if(ret & (1<<31)){              
1328                         if((string_count % LCL_GRANULARITY) == 0){
1329                                 // mark the pointer down
1330                                 Lcl_pointers[Lcl_pointer_count++] = cftell(Lcl_ext_file) - strlen(line) - 1;
1331
1332                                 // if we're out of pointer slots
1333                                 if(Lcl_pointer_count >= LCL_MAX_POINTERS){
1334                                         error_display(0, "Out of pointer for tstrings.tbl lookup. Please increment LCL_MAX_POINTERS in localize.cpp");
1335                                         lcl_ext_close();
1336                                         return;
1337                                 }
1338                         }
1339                         // increment string count
1340                         string_count++;                 
1341                 }
1342         }
1343
1344         // should never get here. we should always be exiting through case 3 (end of language section) of the above switch
1345         // statement
1346         Int3();
1347         lcl_ext_close();
1348 }
1349
1350 void lcl_get_language_name(char *lang_name)
1351 {
1352         Assert(LCL_NUM_LANGUAGES == 3);
1353
1354         strcpy(lang_name, Lcl_languages[Lcl_current_lang].lang_name);
1355 }
1356
1357 // converts german umlauted chars from ASCII to ANSI
1358 // so they appear in the launcher
1359 // how friggin lame is this
1360 // pass in a null terminated string, foo!
1361 // returns ptr to string you sent in
1362 char* lcl_fix_umlauts(char *str, int which_way)
1363 {
1364         int i=0;
1365
1366         if (which_way == LCL_TO_ANSI) {
1367                 // moving to ANSI charset
1368                 // run thru string and perform appropriate conversions
1369                 while (str[i] != '\0') {
1370                         switch (str[i]) {
1371                         case '\x81':
1372                                 // lower umlaut u
1373                                 str[i] = '\xFC';
1374                                 break;
1375                         case '\x84':
1376                                 // lower umlaut a
1377                                 str[i] = '\xE4';
1378                                 break;
1379                         case '\x94':
1380                                 // lower umlaut o
1381                                 str[i] = '\xF6';
1382                                 break;
1383                         case '\x9A':
1384                                 // upper umlaut u
1385                                 str[i] = '\xDC';
1386                                 break;
1387                         case '\x8E':
1388                                 // upper umlaut a
1389                                 str[i] = '\xC4';
1390                                 break;
1391                         case '\x99':
1392                                 // upper umlaut o
1393                                 str[i] = '\xD6';
1394                                 break;
1395                         case '\xE1':
1396                                 // beta-lookin thing that means "ss"
1397                                 str[i] = '\xDF';
1398                                 break;
1399                         }
1400
1401                         i++;
1402                 }
1403         } else {
1404                 // moving to ASCII charset
1405                 // run thru string and perform appropriate conversions
1406                 while (str[i] != '\0') {
1407                         switch (str[i]) {
1408                         case '\xFC':
1409                                 // lower umlaut u
1410                                 str[i] = '\x81';
1411                                 break;
1412                         case '\xE4':
1413                                 // lower umlaut a
1414                                 str[i] = '\x84';
1415                                 break;
1416                         case '\xF6':
1417                                 // lower umlaut o
1418                                 str[i] = '\x94';
1419                                 break;
1420                         case '\xDC':
1421                                 // upper umlaut u
1422                                 str[i] = '\x9A';
1423                                 break;
1424                         case '\xC4':
1425                                 // upper umlaut a
1426                                 str[i] = '\x8E';
1427                                 break;
1428                         case '\xD6':
1429                                 // upper umlaut o
1430                                 str[i] = '\x99';
1431                                 break;
1432                         case '\xDF':
1433                                 // beta-lookin thing that means "ss"
1434                                 str[i] = '\xE1';
1435                                 break;
1436                         }
1437
1438                         i++;
1439                 }
1440         }
1441
1442         return str;
1443 }
1444
1445 // convert some of the polish characters
1446 void lcl_fix_polish(char *str)
1447 {
1448     for(;*str;str++) {
1449         if(*str == '\xA2') *str = '\xF3';
1450         else if(*str == '\x88') *str = '\xEA';
1451     }
1452 }
1453
1454 // ------------------------------------------------------------------
1455 // lcl_translate_wep_name()
1456 //
1457 // For displaying weapon names in german version
1458 // since we cant actually just change them outright.
1459 //
1460 void lcl_translate_wep_name(char *name)
1461 {
1462         if (!strcmp(name, "Morning Star")) {    
1463                 strcpy(name, "Morgenstern");
1464         } else if (!strcmp(name, "MorningStar")) {
1465                 strcpy(name, "Morgenstern D");
1466         } else if (!strcmp(name, "UD-8 Kayser")) {
1467                 strcpy(name, "Kayserstrahl");
1468         } else if (!strcmp(name, "UD-D Kayser")) {
1469                 strcpy(name, "Kayserstrahl");
1470         }
1471 }
1472
1473 // ------------------------------------------------------------------
1474 // lcl_translate_brief_icon_name()
1475 //
1476 // For displaying ship names in german version
1477 // since we cant actually just change them outright.
1478 //
1479 void lcl_translate_brief_icon_name(char *name)
1480 {
1481         char *pos;
1482         char buf[128];
1483
1484         if (!stricmp(name, "Subspace Portal")) {        
1485                 strcpy(name, "Subraum Portal");
1486
1487         } else if (!stricmp(name, "Alpha Wing")) {
1488                 strcpy(name, "Alpha");
1489
1490         } else if (!stricmp(name, "Beta Wing")) {
1491                 strcpy(name, "Beta");
1492
1493         } else if (!stricmp(name, "Zeta Wing")) {
1494                 strcpy(name, "Zeta");
1495
1496         } else if (!stricmp(name, "Capella Node")) {
1497                 strcpy(name, "Capella");
1498
1499         } else if (!stricmp(name, "Hostile")) {
1500                 strcpy(name, "Gegner");
1501
1502         } else if (!stricmp(name, "Hostile Craft")) {
1503                 strcpy(name, "Gegner");
1504
1505         } else if (!stricmp(name, "Rebel Wing")) {
1506                 strcpy(name, "Rebellen");
1507
1508         } else if (!stricmp(name, "Rebel Fleet")) {
1509                 strcpy(name, "Rebellenflotte");
1510
1511         } else if (!stricmp(name, "Sentry Gun")) {
1512                 strcpy(name, "Gesch\x81tz");
1513
1514         } else if (!stricmp(name, "Cargo")) {
1515                 strcpy(name, "Fracht");
1516
1517         } else if (!stricmp(name, "Knossos Device")) {
1518                 strcpy(name, "Knossosger\x84t");
1519         
1520         } else if (!stricmp(name, "Support")) {
1521                 strcpy(name, "Versorger");
1522
1523         } else if (!stricmp(name, "Unknown")) {
1524                 strcpy(name, "Unbekannt");
1525
1526         } else if (!stricmp(name, "Instructor")) {
1527                 strcpy(name, "Ausbilder");
1528         
1529         } else if (!stricmp(name, "Jump Node")) {
1530                 strcpy(name, "Sprungknoten");
1531
1532         } else if (!stricmp(name, "Escort")) {
1533                 strcpy(name, "Geleitschutz");
1534
1535         } else if (!stricmp(name, "Asteroid Field")) {
1536                 strcpy(name, "Asteroidenfeld");
1537
1538         } else if (!stricmp(name, "Enif Station")) {
1539                 strcpy(name, "Station Enif");
1540
1541         } else if (!stricmp(name, "Rally Point")) {
1542                 strcpy(name, "Sammelpunkt");
1543
1544         } else if ((pos = strstr(name, "Transport")) != NULL) {
1545                 pos += 9;               // strlen of "transport"
1546                 strcpy(buf, "Transporter");
1547                 strcat(buf, pos);
1548                 strcpy(name, buf);
1549
1550         } else if ((pos = strstr(name, "Jump Node")) != NULL) {
1551                 pos += 9;               // strlen of "jump node"
1552                 strcpy(buf, "Sprungknoten");
1553                 strcat(buf, pos);
1554                 strcpy(name, buf);
1555         
1556         } else if (!stricmp(name, "Orion under repair")) {
1557                 strcpy(name, "Orion wird repariert");
1558
1559         // SOTY-specific ones below!
1560         
1561         } else if (!stricmp(name, "Wayfarer Station")) {
1562                 strcpy(name, "Station Wayfarer");
1563         } else if (!stricmp(name, "Enemy")) {
1564                 strcpy(name, "Gegner");
1565         } else if (!stricmp(name, "Supply Depot")) {
1566                 strcpy(name, "Nachschubdepot");
1567         } else if (!stricmp(name, "Fighter Escort")) {
1568                 strcpy(name, "Jagdschutz");
1569         } else if (!stricmp(name, "Shivans")) {
1570                 strcpy(name, "Shivaner");
1571         } else if (!stricmp(name, "NTF Base of Operations")) {
1572                 strcpy(name, "NTF-Operationsbasis");
1573         } else if (!stricmp(name, "NTF Bombers")) {
1574                 strcpy(name, "NTF-Bomber");
1575         } else if (!stricmp(name, "NTF Fighters")) {
1576                 strcpy(name, "NTF-J\x84ger");
1577         } else if (!stricmp(name, "Sentry")) {
1578                 strcpy(name, "Sperrgesch\x81tz");
1579         } else if (!stricmp(name, "Cargo Containers")) {
1580                 strcpy(name, "Frachtbeh\x84lter");
1581         } else if (!stricmp(name, "NTF Reinforcements")) {
1582                 strcpy(name, "NTF-Verst\x84rkungen");
1583         } else if (!stricmp(name, "NTF Base")) {
1584                 strcpy(name, "NTF-St\x81tzpunkt");
1585         } else if (!stricmp(name, "Refugee Convoy")) {
1586                 strcpy(name, "Fl\x81""chtlingskonvoi");
1587         } else if (!stricmp(name, "Food Convoy")) {
1588                 strcpy(name, "Nachschubkonvoi");
1589         } else if (!stricmp(name, "Governor's Shuttle")) {
1590                 strcpy(name, "F\x84hre des Gouverneurs");
1591         } else if (!stricmp(name, "GTVA Patrol")) {
1592                 strcpy(name, "GTVA-Patrouille");
1593         } else if (!stricmp(name, "Escort fighters")) {
1594                 strcpy(name, "Geleitschutz");
1595         } else if (!stricmp(name, "Nagada Outpost")) {
1596                 strcpy(name, "Nagada-Aussenposten");
1597         } else if (!stricmp(name, "Fighters")) {
1598                 strcpy(name, "J\x84ger");
1599         } else if (!stricmp(name, "Bombers")) {
1600                 strcpy(name, "Bomber");
1601         } else if (!stricmp(name, "Enemy Destroyers")) {
1602                 strcpy(name, "Feindliche Zerst\x94rer");
1603         } else if (!stricmp(name, "Ross 128 System")) {
1604                 strcpy(name, "System Ross 128");
1605         } else if (!stricmp(name, "Knossos Station")) {
1606                 strcpy(name, "Knossos-Station");
1607         } else if (!stricmp(name, "Transporters")) {
1608                 strcpy(name, "Transporter");
1609         } else if (!stricmp(name, "Pirates?")) {
1610                 strcpy(name, "Piraten?");
1611         } else if (!stricmp(name, "Escorts")) {
1612                 strcpy(name, "Geleitschutz");
1613         } else if (!stricmp(name, "Shivan Fighters")) {
1614                 strcpy(name, "J\x84ger");
1615         } else if (!stricmp(name, "Shivan Territory")) {
1616                 strcpy(name, "Shivaner");
1617         }
1618 }
1619
1620 // ------------------------------------------------------------------
1621 // lcl_translate_brief_icon_name_pl()
1622 //
1623 // For displaying ship names in polish version
1624 // since we cant actually just change them outright.
1625 //
1626 void lcl_translate_brief_icon_name_pl(char *name)
1627 {
1628         char *pos;
1629         char buf[128];
1630
1631         if (!stricmp(name, "Subspace Portal")) {        
1632                 strcpy(name, "Portal podprz.");
1633
1634         } else if (!stricmp(name, "Alpha Wing")) {
1635                 strcpy(name, "Alfa");
1636
1637         } else if (!stricmp(name, "Beta Wing")) {
1638                 strcpy(name, "Beta");
1639
1640         } else if (!stricmp(name, "Zeta Wing")) {
1641                 strcpy(name, "Zeta");
1642
1643         } else if (!stricmp(name, "Capella Node")) {
1644                 strcpy(name, "Capella");
1645
1646         } else if (!stricmp(name, "Hostile")) {
1647                 strcpy(name, "Wr\xF3g");
1648
1649         } else if (!stricmp(name, "Hostile Craft")) {
1650                 strcpy(name, "Wr\xF3g");
1651
1652         } else if (!stricmp(name, "Rebel Wing")) {
1653                 strcpy(name, "Rebelianci");
1654
1655         } else if (!stricmp(name, "Rebel Fleet")) {
1656                 strcpy(name, "Flota Rebelii");
1657
1658         } else if (!stricmp(name, "Sentry Gun")) {
1659                 strcpy(name, "Dzia\xB3o str.");
1660
1661         } else if (!stricmp(name, "Cargo")) {
1662                 strcpy(name, "\xA3\x61\x64unek");
1663
1664         } else if (!stricmp(name, "Knossos Device")) {
1665                 strcpy(name, "Urz. Knossos");
1666         
1667         } else if (!stricmp(name, "Support")) {
1668                 strcpy(name, "Wsparcie");
1669
1670         } else if (!stricmp(name, "Unknown")) {
1671                 strcpy(name, "Nieznany");
1672
1673         } else if (!stricmp(name, "Instructor")) {
1674                 strcpy(name, "Instruktor");
1675         
1676         } else if (!stricmp(name, "Jump Node")) {
1677                 strcpy(name, "W\xEAze\xB3 skokowy");
1678
1679         } else if (!stricmp(name, "Escort")) {
1680                 strcpy(name, "Eskorta");
1681
1682         } else if (!stricmp(name, "Asteroid Field")) {
1683                 strcpy(name, "Pole asteroid");
1684
1685         } else if (!stricmp(name, "Enif Station")) {
1686                 strcpy(name, "Stacja Enif");
1687
1688         } else if (!stricmp(name, "Rally Point")) {
1689                 strcpy(name, "Pkt zborny");
1690
1691         } else if ((pos = strstr(name, "Transport")) != NULL) {
1692                 pos += 9;               // strlen of "transport"
1693                 strcpy(buf, "Transporter");
1694                 strcat(buf, pos);
1695                 strcpy(name, buf);
1696
1697         } else if ((pos = strstr(name, "Jump Node")) != NULL) {
1698                 pos += 9;               // strlen of "jump node"
1699                 strcpy(buf, "W\xEAze\xB3 skokowy");
1700                 strcat(buf, pos);
1701                 strcpy(name, buf);
1702         
1703         } else if (!stricmp(name, "Orion under repair")) {
1704                 strcpy(name, "Naprawiany Orion");
1705         }
1706 }
1707
1708 // ------------------------------------------------------------------
1709 // lcl_translate_ship_name()
1710 //
1711 // For displaying ship names in german version in the briefing
1712 // since we cant actually just change them outright.
1713 //
1714 void lcl_translate_ship_name(char *name)
1715 {
1716         if (!strcmp(name, "GTDR Amazon Advanced")) {    
1717                 strcpy(name, "GTDR Amazon VII");
1718         } 
1719 }
1720
1721 // ------------------------------------------------------------------
1722 // lcl_translate_targetbox_name()
1723 //
1724 // For displaying ship names in german version in the targetbox
1725 // since we cant actually just change them outright.
1726 //
1727 void lcl_translate_targetbox_name(char *name)
1728 {
1729         char *pos;
1730         char buf[128];
1731         
1732         if ((pos = strstr(name, "Sentry")) != NULL) {
1733                 pos += 6;               // strlen of "sentry"
1734                 strcpy(buf, "Sperrgesch\x81tz");
1735                 strcat(buf, pos);
1736                 strcpy(name, buf);
1737
1738         } else if ((pos = strstr(name, "Support")) != NULL) {
1739                 pos += 7;               // strlen of "support"
1740                 strcpy(buf, "Versorger");
1741                 strcat(buf, pos);
1742                 strcpy(name, buf);
1743
1744         } else if ((pos = strstr(name, "Unknown")) != NULL) {
1745                 pos += 7;               // strlen of "unknown"
1746                 strcpy(buf, "Unbekannt");
1747                 strcat(buf, pos);
1748                 strcpy(name, buf);
1749
1750         } else if ((pos = strstr(name, "Drone")) != NULL) {
1751                 pos += 5;               // strlen of "drone"
1752                 strcpy(buf, "Drohne");
1753                 strcat(buf, pos);
1754                 strcpy(name, buf);
1755
1756         } else if ((pos = strstr(name, "Jump Node")) != NULL) {
1757                 pos += 9;               // strlen of "jump node"
1758                 strcpy(buf, "Sprungknoten");
1759                 strcat(buf, pos);
1760                 strcpy(name, buf);
1761
1762         } else if (!stricmp(name, "Instructor")) {
1763                 strcpy(name, "Ausbilder");
1764
1765         } else if (!stricmp(name, "NTF Vessel")) {
1766                 strcpy(name, "NTF-Schiff");
1767
1768         } else if (!stricmp(name, "Enif Station")) {
1769                 strcpy(name, "Station Enif");
1770         }
1771 }
1772
1773 // ------------------------------------------------------------------
1774 // lcl_translate_targetbox_name_pl()
1775 //
1776 // For displaying ship names in polish version in the targetbox
1777 // since we cant actually just change them outright.
1778 //
1779 void lcl_translate_targetbox_name_pl(char *name)
1780 {
1781         char *pos;
1782         char buf[128];
1783         
1784         if ((pos = strstr(name, "Sentry")) != NULL) {
1785                 pos += 6;               // strlen of "sentry"
1786                 strcpy(buf, "Stra\xBFnik");
1787                 strcat(buf, pos);
1788                 strcpy(name, buf);
1789
1790         } else if ((pos = strstr(name, "Support")) != NULL) {
1791                 pos += 7;               // strlen of "support"
1792                 strcpy(buf, "Wsparcie");
1793                 strcat(buf, pos);
1794                 strcpy(name, buf);
1795
1796         } else if ((pos = strstr(name, "Unknown")) != NULL) {
1797                 pos += 7;               // strlen of "unknown"
1798                 strcpy(buf, "Nieznany");
1799                 strcat(buf, pos);
1800                 strcpy(name, buf);
1801
1802         } else if ((pos = strstr(name, "Drone")) != NULL) {
1803                 pos += 5;               // strlen of "drone"
1804                 strcpy(buf, "Sonda");
1805                 strcat(buf, pos);
1806                 strcpy(name, buf);
1807
1808         } else if ((pos = strstr(name, "Jump Node")) != NULL) {
1809                 pos += 9;               // strlen of "jump node"
1810                 strcpy(buf, "W\xEAze\xB3 skokowy");
1811                 strcat(buf, pos);
1812                 strcpy(name, buf);
1813
1814         } else if (!stricmp(name, "Instructor")) {
1815                 strcpy(name, "Instruktor");
1816
1817         } else if (!stricmp(name, "NTF Vessel")) {
1818                 strcpy(name, "Okr\xEAt NTF");
1819
1820         } else if (!stricmp(name, "Enif Station")) {
1821                 strcpy(name, "Stacja Enif");
1822         }
1823 }