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