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