1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  * This is a utility for extracting needed resource data from different language
22  * version of the Lure of the Temptress lure.exe executable files into a new file
23  * lure.dat - this file is required for the ScummVM  Lure of the Temptress module
24  * to work properly
25  */
26 
27 // Disable symbol overrides so that we can use system headers.
28 #define FORBIDDEN_SYMBOL_ALLOW_ALL
29 
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 
34 #include "common/endian.h"
35 
36 enum AccessMode {
37 	kFileReadMode  = 1,
38 	kFileWriteMode = 2
39 };
40 
41 class File {
42 private:
43 	FILE *f;
44 public:
open(const char * filename,AccessMode mode=kFileReadMode)45 	bool open(const char *filename, AccessMode mode = kFileReadMode) {
46 		f = fopen(filename, (mode == kFileReadMode) ? "rb" : "wb");
47 		return (f != NULL);
48 	}
close()49 	void close() {
50 		fclose(f);
51 		f = NULL;
52 	}
seek(int32 offset,int whence=SEEK_SET)53 	int seek(int32 offset, int whence = SEEK_SET) {
54 		return fseek(f, offset, whence);
55 	}
read(void * buffer,int len)56 	long read(void *buffer, int len) {
57 		return fread(buffer, 1, len, f);
58 	}
write(const void * buffer,int len)59 	void write(const void *buffer, int len) {
60 		fwrite(buffer, 1, len, f);
61 	}
readByte()62 	byte readByte() {
63 		byte v;
64 		read(&v, sizeof(byte));
65 		return v;
66 	}
readWord()67 	uint16 readWord() {
68 		uint16 v;
69 		read(&v, sizeof(uint16));
70 		return FROM_LE_16(v);
71 	}
readLong()72 	uint32 readLong() {
73 		uint32 v;
74 		read(&v, sizeof(uint32));
75 		return FROM_LE_32(v);
76 	}
readString(char * sLine)77 	void readString(char *sLine) {
78 		while ((*sLine = readByte()) != '\n')
79 			++sLine;
80 
81 		*sLine = '\0';
82 	}
writeByte(byte v)83 	void writeByte(byte v) {
84 		write(&v, sizeof(byte));
85 	}
writeWord(uint16 v)86 	void writeWord(uint16 v) {
87 		uint16 vTemp = TO_LE_16(v);
88 		write(&vTemp, sizeof(uint16));
89 	}
writeLong(uint32 v)90 	void writeLong(uint32 v) {
91 		uint32 vTemp = TO_LE_32(v);
92 		write(&vTemp, sizeof(uint32));
93 	}
writeString(const char * s)94 	void writeString(const char *s) {
95 		fprintf(f, "%s", s);
96 	}
pos()97 	uint32 pos() {
98 		return ftell(f);
99 	}
size()100 	uint32 size() {
101 		uint32 position = ftell(f);
102 		fseek (f, 0, SEEK_END);
103 		uint32 end = ftell(f);
104 		fseek (f, position, SEEK_SET);
105 
106 		return end;
107 	}
108 };
109 
110 File textFile, txxInp, txxNtp;
111 int _version;
112 
113 /*-------------------------------------------------------------------------*/
114 
115 #define BUFFER_SIZE 32768
116 
117 const byte tabdrFr[32] = {
118 	32, 101, 115,  97, 114, 105, 110,
119 	117, 116, 111, 108,  13, 100,  99,
120 	112, 109,  46, 118, 130,  39, 102,
121 	98,  44, 113, 104, 103,  33,  76,
122 	85, 106,  30,  31
123 };
124 
125 const byte tab30Fr[32] = {
126 	69,  67,  74, 138, 133, 120,  77, 122,
127 	121,  68,  65,  63,  73,  80,  83,  82,
128 	156,  45,  58,  79,  49,  86,  78,  84,
129 	71,  81,  64,  66, 135,  34, 136,  91
130 };
131 
132 const byte tab31Fr[32]= {
133 	93,  47,  48,  53,  50,  70, 124,  75,
134 	72, 147, 140, 150, 151,  57,  56,  51,
135 	107, 139,  55,  89, 131,  37,  54,  88,
136 	119,   0,   0,   0,   0,   0,   0,   0
137 };
138 
139 const byte tabdrDe[32] = {
140 	0x20, 0x65, 0x6E, 0x69, 0x73, 0x72, 0x74,
141 	0x68, 0x61, 0x75, 0x0D, 0x63, 0x6C, 0x64,
142 	0x6D, 0x6F, 0x67, 0x2E, 0x62, 0x66, 0x53,
143 	0x2C, 0x77, 0x45, 0x7A, 0x6B, 0x44, 0x76,
144 	0x9C, 0x47, 0x1E, 0x1F
145 };
146 
147 const byte tab30De[32] = {
148 	0x49, 0x4D, 0x21, 0x42, 0x4C, 0x70, 0x41, 0x52,
149 	0x57, 0x4E, 0x48, 0x3F, 0x46, 0x50, 0x55, 0x4B,
150 	0x5A, 0x4A, 0x54, 0x31, 0x4F, 0x56, 0x79, 0x3A,
151 	0x6A, 0x5B, 0x5D, 0x40, 0x22, 0x2F, 0x30, 0x35
152 };
153 
154 const byte tab31De[32]= {
155 	0x78, 0x2D, 0x32, 0x82, 0x43, 0x39, 0x33, 0x38,
156 	0x7C, 0x27, 0x37, 0x3B, 0x25, 0x28, 0x29, 0x36,
157 	0x51, 0x59, 0x71, 0x81, 0x87, 0x88, 0x93, 0,
158 	0,    0,    0,    0,    0,    0,    0,    0
159 };
160 
161 const byte *tabdr, *tab30, *tab31;
162 uint16 ctrlChar;
163 
164 /**
165  * Extracts a single character from the game data
166  */
extractCharacter(unsigned char & c,uint & idx,uint & pt,bool & the_end,const uint16 * strData)167 static void extractCharacter(unsigned char &c, uint &idx, uint &pt, bool &the_end, const uint16 *strData) {
168 	uint16 oct, ocd;
169 
170 	/* 5-8 */
171 	oct = FROM_LE_16(strData[idx]);
172 
173 	oct = ((uint16)(oct << (16 - pt))) >> (16 - pt);
174 	if (pt < 6) {
175 		idx = idx + 1;
176 		oct = oct << (5 - pt);
177 		pt = pt + 11;
178 		oct = oct | (FROM_LE_16(strData[idx]) >> pt);
179 	} else {
180 		pt = pt - 5;
181 		oct = (uint)oct >> pt;
182 	}
183 
184 	if (oct == ctrlChar) {
185 		c = '$';
186 		the_end = true;
187 	} else if (oct == 30 || oct == 31) {
188 		ocd = FROM_LE_16(strData[idx]);
189 		ocd = (uint16)(ocd << (16 - pt)) >> (16 - pt);
190 		if (pt < 6) {
191 			idx = idx + 1;
192 			ocd = ocd << (5 - pt);
193 			pt = pt + 11;
194 			ocd = ocd | (FROM_LE_16(strData[idx]) >> pt);
195 		} else {
196 			pt = pt - 5;
197 			ocd = (uint)ocd >> pt;
198 		}
199 		if (oct == 30)
200 			c = (char)tab30[ocd];
201 		else
202 			c = (char)tab31[ocd];
203 
204 		if (c == '\0')
205 			the_end = true;
206 	} else {
207 		c = (char)tabdr[oct];
208 	}
209 }
210 
211 /**
212  * Puts a compressed 5-bit value into the string data buffer
213  */
addCompressedValue(int oct,int & indis,int & point,uint16 * strData)214 static void addCompressedValue(int oct, int &indis, int &point, uint16 *strData) {
215 	// Write out the part of the value that fits into the current word
216 	if (point < 5)
217 		strData[indis] |= oct >> (5 - point);
218 	else
219 		strData[indis] |= oct << (point - 5);
220 
221 	// Handling of there's any overlap into the next word
222 	if (point < 5) {
223 		// Overlapping into next word
224 		++indis;
225 
226 		// Get the bits that fall into the next word and set it
227 		int remainder = oct & ((1 << (5 - point)) - 1);
228 		strData[indis] |= remainder << (16 - (5 - point));
229 
230 		point += -5 + 16;
231 	} else {
232 		point -= 5;
233 		if (point == 0) {
234 			point = 16;
235 			++indis;
236 		}
237 	}
238 }
239 
240 /**
241  * Compresses a single passed character and stores it in the compressed strings buffer
242  */
compressCharacter(unsigned char ch,int & indis,int & point,uint16 * strData)243 static void compressCharacter(unsigned char ch, int &indis, int &point, uint16 *strData) {
244 	if (ch == '$') {
245 		// End of string
246 		addCompressedValue(11, indis, point, strData);
247 		return;
248 	}
249 
250 	// Scan through the tabdr array for a match
251 	for (int idx = 0; idx < 30; ++idx) {
252 		if ((idx != 11) && (tabdr[idx] == ch)) {
253 			addCompressedValue(idx, indis, point, strData);
254 			return;
255 		}
256 	}
257 
258 	// Scan through the tab30 array
259 	for (int idx = 0; idx < 32; ++idx) {
260 		if (tab30[idx] == ch) {
261 			addCompressedValue(30, indis, point, strData);
262 			addCompressedValue(idx, indis, point, strData);
263 			return;
264 		}
265 	}
266 
267 	// Scan through the tab31 array
268 	for (int idx = 0; idx < 32; ++idx) {
269 		if (tab31[idx] == ch) {
270 			addCompressedValue(31, indis, point, strData);
271 			addCompressedValue(idx, indis, point, strData);
272 			return;
273 		}
274 	}
275 
276 	printf("Encountered invalid character '%c' when compressing strings\n", ch);
277 	exit(1);
278 }
279 
280 /**
281  * string extractor
282  */
export_strings(const char * textFilename)283 static void export_strings(const char *textFilename) {
284 	char buffer[BUFFER_SIZE];
285 	uint16 *strData;
286 
287 	// Open input and output files
288 	if (!txxInp.open("TXX.INP", kFileReadMode)) {
289 		if (!txxInp.open("TXX.MOR", kFileReadMode)) {
290 			printf("Missing TXX.INP/MOR");
291 			exit(-1);
292 		}
293 	}
294 	if (!txxNtp.open("TXX.NTP", kFileReadMode)) {
295 		if (!txxNtp.open("TXX.IND", kFileReadMode)) {
296 			printf("Missing TXX.NTP/IND");
297 			exit(-1);
298 		}
299 	}
300 	textFile.open(textFilename, kFileWriteMode);
301 
302 	// Read all the compressed string data into a buffer
303 	printf("%d %d", txxInp.size(), txxNtp.size());
304 	strData = (uint16 *)malloc(txxInp.size());
305 	txxInp.read(strData, txxInp.size());
306 
307 	// Loop through getting each string
308 	for (unsigned int strIndex = 0; strIndex < (txxNtp.size() / 3); ++strIndex) {
309 		uint indis = txxNtp.readWord();
310 		uint point = txxNtp.readByte();
311 
312 		// Extract the string
313 		int charIndex = 0;
314 		unsigned char ch;
315 		bool endFlag = false;
316 		do {
317 			extractCharacter(ch, indis, point, endFlag, strData);
318 			buffer[charIndex++] = ch;
319 			if (charIndex == BUFFER_SIZE) {
320 				printf("Extracted string exceeded allowed buffer size.\n");
321 				exit(1);
322 			}
323 
324 			if (indis >= (txxInp.size() / 2))
325 				endFlag = true;
326 		} while (!endFlag);
327 
328 		// Write out the string
329 		buffer[charIndex++] = '\n';
330 		buffer[charIndex] = '\0';
331 		textFile.writeString(buffer);
332 	}
333 
334 	// Close the files and free the buffer
335 	free(strData);
336 	txxInp.close();
337 	txxNtp.close();
338 	textFile.close();
339 }
340 
341 /**
342  * string importer
343  */
import_strings(const char * textFilename)344 static void import_strings(const char *textFilename) {
345 	// Open input and output files
346 	if (!txxInp.open("TXX.INP", kFileWriteMode)) {
347 		printf("Missing TXX data file");
348 		exit(-1);
349 	}
350 	if (!txxNtp.open("TXX.NTP", kFileWriteMode)) {
351 		printf("Missing TXX index file");
352 		exit(-1);
353 	}
354 	textFile.open(textFilename, kFileReadMode);
355 
356 	// Set up a buffer for the output compressed strings
357 	uint16 strData[BUFFER_SIZE];
358 	memset(strData, 0, BUFFER_SIZE*sizeof(uint16));
359 	char sLine[BUFFER_SIZE];
360 
361 	int indis = 0;
362 	int point = 16;
363 
364 	while (textFile.pos() < textFile.size()) {
365 		// Read in the next source line
366 		textFile.readString(sLine);
367 
368 		// Write out the index entry for the string
369 		txxNtp.writeWord(indis);
370 		txxNtp.writeByte(point);
371 
372 		// Loop through writing out the characters to the compressed data buffer
373 		char *s = sLine;
374 		while (*s) {
375 			compressCharacter(*s, indis, point, strData);
376 			++s;
377 		}
378 	}
379 
380 	// Write out the compressed data
381 	if (point != 16)
382 		++indis;
383 	txxInp.write(strData, indis * 2);
384 
385 	// Close the files
386 	txxInp.close();
387 	txxNtp.close();
388 	textFile.close();
389 }
390 
391 
main(int argc,char * argv[])392 int main(int argc, char *argv[]) {
393 	if (argc != 4) {
394 		printf("Format: %s export|import v1|v2 output_file\n", argv[0]);
395 		printf("where:\nv1: French DOS version\nv2: German DOS version\n");
396 		printf("The program must be run from the directory with the Mortville Manor game files.\n");
397 		exit(0);
398 	}
399 
400 	if (!strcmp(argv[2], "v1")) {
401 		tab30 = tab30Fr;
402 		tab31 = tab31Fr;
403 		tabdr = tabdrFr;
404 		ctrlChar = 11;
405 	} else if (!strcmp(argv[2], "v2")) {
406 		tab30 = tab30De;
407 		tab31 = tab31De;
408 		tabdr = tabdrDe;
409 		ctrlChar = 10;
410 	} else {
411 		printf("Unknown version");
412 		exit(-1);
413 	}
414 
415 	// Do the processing
416 	if (!strcmp(argv[1], "export"))
417 		export_strings(argv[3]);
418 	else if (!strcmp(argv[1], "import"))
419 		import_strings(argv[3]);
420 	else
421 		printf("Unknown operation specified\n");
422 }
423