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