1 /*
2 * Portions of this file are copyright Rebirth contributors and licensed as
3 * described in COPYING.txt.
4 * Portions of this file are copyright Parallax Software and licensed
5 * according to the Parallax license below.
6 * See COPYING.txt for license details.
7
8 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
9 SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO
10 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
11 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
12 IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
13 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
14 FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
15 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS
16 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
17 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED.
18 */
19
20 /*
21 *
22 * Code for localizable text
23 *
24 */
25
26
27 #include <stdlib.h>
28 #include <string.h>
29
30 #include "physfsx.h"
31 #include "pstypes.h"
32 #include "dxxerror.h"
33 #include "text.h"
34 #include "strutil.h"
35 #include "args.h"
36 #include <memory>
37
38 #ifdef GENERATE_BUILTIN_TEXT_TABLE
39 #include <ctype.h>
40 #endif
41
42 #if defined(DXX_BUILD_DESCENT_I)
43 #define IDX_TEXT_OVERWRITTEN 330
44 #elif defined(DXX_BUILD_DESCENT_II)
45 #define IDX_TEXT_OVERWRITTEN 350
46 #define SHAREWARE_TEXTSIZE 14677
47 #endif
48
49 static std::unique_ptr<char[]> text;
50 static std::unique_ptr<char[]> overwritten_text;
51
52 // rotates a byte left one bit, preserving the bit falling off the right
encode_rotate_left(const uint8_t v)53 static uint8_t encode_rotate_left(const uint8_t v)
54 {
55 return (v >> 7) | (v << 1);
56 }
57
58 constexpr std::integral_constant<uint8_t, 0xd3> BITMAP_TBL_XOR{};
59
decode_char(const uint8_t c)60 static uint8_t decode_char(const uint8_t c)
61 {
62 const auto c1 = encode_rotate_left(c);
63 const auto c2 = c1 ^ BITMAP_TBL_XOR;
64 return encode_rotate_left(c2);
65 }
66
67 //decode an encoded line of text of bitmaps.tbl
decode_text_line(char * p)68 void decode_text_line(char *p)
69 {
70 for (; const char c = *p; p++)
71 *p = decode_char(c);
72 }
73
74 // decode buffer of text, preserves newlines
decode_text(char * ptr,unsigned len)75 void decode_text(char *ptr, unsigned len)
76 {
77 for (; len--; ptr++)
78 {
79 const char c = *ptr;
80 if (c != '\n')
81 *ptr = decode_char(c);
82 }
83 }
84
85 //load all the text strings for Descent
86 namespace dsx {
87
88 #ifdef USE_BUILTIN_ENGLISH_TEXT_STRINGS
89 static
90 #endif
91 std::array<const char *, N_TEXT_STRINGS> Text_string;
92
load_text()93 void load_text()
94 {
95 int len,i, have_binary = 0;
96 char *tptr;
97 const char *filename="descent.tex";
98 #if defined(DXX_BUILD_DESCENT_I)
99 static const char *const extra_strings[] = {
100 "done",
101 "I am a",
102 "CHEATER!",
103 "Loading Data",
104 "ALT-F2\t Save Game",
105 "ALT-F3\t Load Game",
106 "Only in Registered version!",
107 "Concussion",
108 "Homing",
109 "ProxBomb",
110 "Smart",
111 "Mega",
112 "Mission '%s' not found.\nYou must have this mission\nfile in order to playback\nthis demo.",
113 "All player callsigns on screen",
114 "There is already a game\nin progress with that name",
115 "This mission is designated\nAnarchy-only",
116 "Force level start",
117 "Quitting now means ending the\nentire netgame\n\nAre you sure?",
118 "The mission for that netgame\nis not installed on your\nsystem. Cannot join.",
119 "Start Multiplayer Game\n\nSelect mission",
120 "Error loading mission file",
121 "Custom (return to set)",
122 "Base address (in Hex)",
123 "IRQ Number",
124 "Reset to Default",
125 "Valid IRQ values are 2-7",
126 "No UART was detected\nat those settings",
127 "You will pay dearly for that!",
128 "Revenge is mine!!",
129 "Man I'm good!",
130 "Its almost too easy!",
131 " Mission:",
132 "+/- Changes viewing distance",
133 "Alternate exit found!\n\nProceeding to Secret Level!",
134 "Show all players on automap",
135 "Killed by a robot",
136 "Baud",
137 "A consistency error has been\ndetected in your network connection.\nCheck you hardware and re-join",
138 "Press any key to continue (Print Screen to save screenshot)",
139 "An error occured while writing\ndemo. Demo is likely corrupted.\nEnter demo name now or\npress ESC to delete demo.",
140 "The main reactor is invulnerable for",
141 "The level being loaded is not\navailable in Destination Saturn.\nUnable to continue demo playback.\n\nPress any key to continue.",
142 "Reactor life",
143 "min",
144 "Current IPX Socket is default",
145 "This program requires MS-DOS 5.0 or higher.\nYou are using MS-DOS",
146 "You can use the -nodoscheck command line\nswitch to override this check, but it\nmay have unpredictable results, namely\nwith DOS file error handling.\n",
147 "Not enough file handles!",
148 "of the necessary file handles\nthat Descent requires to execute properly. You will\nneed to increase the FILES=n line in your config.sys.",
149 "If you are running with a clean boot, then you will need\nto create a CONFIG.SYS file in your root directory, with\nthe line FILES=15 in it. If you need help with this,\ncontact Interplay technical support.",
150 "You may also run with the -nofilecheck command line option\nthat will disable this check, but you might get errors\nwhen loading saved games or playing demos.",
151 "Available memory",
152 "more bytes of DOS memory needed!",
153 "more bytes of virtual memory needed. Reconfigure VMM.",
154 "more bytes of extended/expanded memory needed!",
155 "Or else you you need to use virtual memory (See README.TXT)",
156 "more bytes of physical memory needed!",
157 "Check to see that your virtual memory settings allow\nyou to use all of your physical memory (See README.TXT)",
158 "Initializing DPMI services",
159 "Initializing critical error handler",
160 "Enables Virtual I/O Iglasses! stereo display",
161 "Enables Iglasses! head tracking via COM port",
162 "Enables Kasan's 3dMax stereo display in low res.",
163 "3DBios must be installed for 3dMax operation.",
164 "Enables Kasan's 3dMax stereo display in high res",
165 "Press any key for more options...",
166 "Enables dynamic socket changing",
167 "Disables the file handles check",
168 "Getting settings from DESCENT.CFG...",
169 "Initializing timer system...",
170 "Initializing keyboard handler...",
171 "Initializing mouse handler...",
172 "Mouse support disabled...",
173 "Initializing joystick handler...",
174 "Slow joystick reading enabled...",
175 "Polled joystick reading enabled...",
176 "BIOS joystick reading enabled...",
177 "Joystick support disabled...",
178 "Initializing divide by zero handler...",
179 "Initializing network...",
180 "Using IPX network support on channel",
181 "No IPX compatible network found.",
182 "Error opening socket",
183 "Not enough low memory for IPX buffers.",
184 "Error initializing IPX. Error code:",
185 "Network support disabled...",
186 "Initializing graphics system...",
187 "SOUND: Error opening",
188 "SOUND: Error locking down instruments",
189 "SOUND: (HMI)",
190 "SOUND: Error locking down drums",
191 "SOUND: Error locking midi track map!",
192 "SOUND: Error locking midi callback function!",
193 "Using external control:",
194 "Invalid serial port parameter for -itrak!",
195 "Initializing i-glasses! head tracking on serial port %d",
196 "Make sure the glasses are turned on!",
197 "Press ESC to abort",
198 "Failed to open serial port. Status =",
199 "Message",
200 "Macro",
201 "Error locking serial interrupt routine!",
202 "Error locking serial port data!",
203 "Robots are normal",
204 "Robots move fast, fire seldom",
205 "Robot painting OFF",
206 "Robot painting with texture %d"
207 };
208 #endif
209
210 if (!CGameArg.DbgAltTex.empty())
211 filename = CGameArg.DbgAltTex.c_str();
212
213 if (auto &&[tfile, physfserr] = PHYSFSX_openReadBuffered(filename); !tfile)
214 {
215 const auto texfilename = filename;
216 filename="descent.txb";
217 auto &&[ifile, physfserr2] = PHYSFSX_openReadBuffered(filename);
218 if (!ifile)
219 {
220 Error("Failed to open file %s, DESCENT.TXB: \"%s\", \"%s\"", texfilename, PHYSFS_getErrorByCode(physfserr), PHYSFS_getErrorByCode(physfserr2));
221 return;
222 }
223 have_binary = 1;
224
225 len = PHYSFS_fileLength(ifile);
226
227 //edited 05/17/99 Matt Mueller - malloc an extra byte, and null terminate.
228 text = std::make_unique<char[]>(len + 1);
229 PHYSFS_read(ifile,text,1,len);
230 text[len]=0;
231 //end edit -MM
232 } else {
233 int c;
234 char * p;
235
236 len = PHYSFS_fileLength(tfile);
237
238 //edited 05/17/99 Matt Mueller - malloc an extra byte, and null terminate.
239 text = std::make_unique<char[]>(len + 1);
240 //fread(text,1,len,tfile);
241 p = text.get();
242 do {
243 c = PHYSFSX_fgetc( tfile );
244 if ( c != 13 )
245 *p++ = c;
246 } while ( c!=EOF );
247 *p=0;
248 //end edit -MM
249 }
250
251 for (i=0,tptr=text.get();i<N_TEXT_STRINGS;i++) {
252 char *p;
253
254 #if defined(DXX_BUILD_DESCENT_I)
255 if (!tptr && i >= N_TEXT_STRINGS_MIN) // account for non-registered 1.4/1.5 text files
256 {
257 Text_string[i-1] = extra_strings[i - N_TEXT_STRINGS_MIN - 1];
258 Text_string[i] = extra_strings[i - N_TEXT_STRINGS_MIN];
259 continue;
260 }
261 else if (!tptr && i < N_TEXT_STRINGS_MIN)
262 {
263 Error("Not enough strings in text file - expecting %d (or at least %d), found %d",N_TEXT_STRINGS,N_TEXT_STRINGS_MIN,i);
264 }
265 #endif
266 Text_string[i] = tptr;
267 char *ts = tptr;
268
269 tptr = strchr(tptr,'\n');
270
271 #if defined(DXX_BUILD_DESCENT_II)
272 if (!tptr)
273 {
274 if (i == 644) break; /* older datafiles */
275
276 Error("Not enough strings in text file - expecting %d, found %d\n", N_TEXT_STRINGS, i);
277 }
278 #endif
279 if ( tptr ) *tptr++ = 0;
280
281 if (have_binary)
282 decode_text_line(ts);
283
284 //scan for special chars (like \n)
285 if ((p = strchr(ts, '\\')) != NULL) {
286 for (char *q = p; assert(*p == '\\'), *p;) {
287 char newchar;
288
289 if (p[1] == 'n') newchar = '\n';
290 else if (p[1] == 't') newchar = '\t';
291 else if (p[1] == '\\') newchar = '\\';
292 else
293 Error("Unsupported key sequence <\\%c> on line %d of file <%s>", p[1], i + 1, filename);
294
295 *q++ = newchar;
296 p += 2;
297 char *r = strchr(p, '\\');
298 if (!r)
299 {
300 r = tptr;
301 assert(!r[-1]);
302 assert(r == 1 + p + strlen(p));
303 memmove(q, p, r - p);
304 break;
305 }
306 memmove(q, p, r - p);
307 q += (r - p);
308 p = r;
309 }
310 }
311
312 switch(i) {
313 #if defined(DXX_BUILD_DESCENT_I)
314 case 116:
315 if (!d_stricmp(ts, "SPREADFIRE")) // This string is too long to fit in the cockpit-box
316 {
317 ts[6] = 0;
318 }
319 break;
320 #endif
321
322 case IDX_TEXT_OVERWRITTEN:
323 {
324 static const char extra[] = "\n<Ctrl-C> converts format\nIntel <-> PowerPC";
325 std::size_t l = strlen(ts);
326 overwritten_text = std::make_unique<char[]>(l + sizeof(extra));
327 char *o = overwritten_text.get();
328 std::copy_n(extra, sizeof(extra), std::copy_n(ts, l, o));
329 Text_string[i] = o;
330 break;
331 }
332 }
333
334 }
335
336 #if defined(DXX_BUILD_DESCENT_II)
337 if (i == 644)
338 {
339 if (len == SHAREWARE_TEXTSIZE)
340 {
341 Text_string[173] = Text_string[172];
342 Text_string[172] = Text_string[171];
343 Text_string[171] = Text_string[170];
344 Text_string[170] = Text_string[169];
345 Text_string[169] = "Windows Joystick";
346 }
347
348 Text_string[644] = "Z1";
349 Text_string[645] = "UN";
350 Text_string[646] = "P1";
351 Text_string[647] = "R1";
352 Text_string[648] = "Y1";
353 }
354 #endif
355
356 #ifdef GENERATE_BUILTIN_TEXT_TABLE
357 for (unsigned u = 0; u < std::size(Text_string); ++u)
358 {
359 printf("\t%u\t\"", u);
360 for (char *px = Text_string[u]; *px; ++px) {
361 unsigned x = (unsigned)*px;
362 if (isprint(x))
363 putchar(x);
364 else if (x == '\t')
365 printf("\\t");
366 else if (x == '\r')
367 printf("\\r");
368 else if (x == '\n')
369 printf("\\n");
370 else
371 printf("\\x%.2x", x);
372 }
373 printf("\"\n");
374 }
375 #endif
376
377 //Assert(tptr==text+len || tptr==text+len-2);
378
379 }
380 }
381
382
383