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