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