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