/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * This is a utility for extracting needed resource data from different language * version of the Lure of the Temptress lure.exe executable files into a new file * lure.dat - this file is required for the ScummVM Lure of the Temptress module * to work properly */ // Disable symbol overrides so that we can use system headers. #define FORBIDDEN_SYMBOL_ALLOW_ALL // HACK to allow building with the SDL backend on MinGW // see bug #1800764 "TOOLS: MinGW tools building broken" #ifdef main #undef main #endif // main #include #include #include #include "common/endian.h" enum AccessMode { kFileReadMode = 1, kFileWriteMode = 2 }; class File { private: FILE *f; public: bool open(const char *filename, AccessMode mode = kFileReadMode) { f = fopen(filename, (mode == kFileReadMode) ? "rb" : "wb"); return (f != NULL); } void close() { fclose(f); f = NULL; } int seek(int32 offset, int whence = SEEK_SET) { return fseek(f, offset, whence); } long read(void *buffer, int len) { return fread(buffer, 1, len, f); } void write(const void *buffer, int len) { fwrite(buffer, 1, len, f); } byte readByte() { byte v; read(&v, sizeof(byte)); return v; } uint16 readWord() { uint16 v; read(&v, sizeof(uint16)); return FROM_LE_16(v); } uint32 readLong() { uint32 v; read(&v, sizeof(uint32)); return FROM_LE_32(v); } void readString(char *sLine) { while ((*sLine = readByte()) != '\n') ++sLine; *sLine = '\0'; } void writeByte(byte v) { write(&v, sizeof(byte)); } void writeWord(uint16 v) { uint16 vTemp = TO_LE_16(v); write(&vTemp, sizeof(uint16)); } void writeLong(uint32 v) { uint32 vTemp = TO_LE_32(v); write(&vTemp, sizeof(uint32)); } void writeString(const char *s) { fprintf(f, "%s", s); } uint32 pos() { return ftell(f); } uint32 size() { uint32 position = ftell(f); fseek (f, 0, SEEK_END); uint32 end = ftell(f); fseek (f, position, SEEK_SET); return end; } }; File textFile, txxInp, txxNtp; int _version; /*-------------------------------------------------------------------------*/ #define BUFFER_SIZE 32768 const byte tabdrFr[32] = { 32, 101, 115, 97, 114, 105, 110, 117, 116, 111, 108, 13, 100, 99, 112, 109, 46, 118, 130, 39, 102, 98, 44, 113, 104, 103, 33, 76, 85, 106, 30, 31 }; const byte tab30Fr[32] = { 69, 67, 74, 138, 133, 120, 77, 122, 121, 68, 65, 63, 73, 80, 83, 82, 156, 45, 58, 79, 49, 86, 78, 84, 71, 81, 64, 66, 135, 34, 136, 91 }; const byte tab31Fr[32]= { 93, 47, 48, 53, 50, 70, 124, 75, 72, 147, 140, 150, 151, 57, 56, 51, 107, 139, 55, 89, 131, 37, 54, 88, 119, 0, 0, 0, 0, 0, 0, 0 }; const byte tabdrDe[32] = { 0x20, 0x65, 0x6E, 0x69, 0x73, 0x72, 0x74, 0x68, 0x61, 0x75, 0x0D, 0x63, 0x6C, 0x64, 0x6D, 0x6F, 0x67, 0x2E, 0x62, 0x66, 0x53, 0x2C, 0x77, 0x45, 0x7A, 0x6B, 0x44, 0x76, 0x9C, 0x47, 0x1E, 0x1F }; const byte tab30De[32] = { 0x49, 0x4D, 0x21, 0x42, 0x4C, 0x70, 0x41, 0x52, 0x57, 0x4E, 0x48, 0x3F, 0x46, 0x50, 0x55, 0x4B, 0x5A, 0x4A, 0x54, 0x31, 0x4F, 0x56, 0x79, 0x3A, 0x6A, 0x5B, 0x5D, 0x40, 0x22, 0x2F, 0x30, 0x35 }; const byte tab31De[32]= { 0x78, 0x2D, 0x32, 0x82, 0x43, 0x39, 0x33, 0x38, 0x7C, 0x27, 0x37, 0x3B, 0x25, 0x28, 0x29, 0x36, 0x51, 0x59, 0x71, 0x81, 0x87, 0x88, 0x93, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; const byte *tabdr, *tab30, *tab31; uint16 ctrlChar; /** * Extracts a single character from the game data */ static void extractCharacter(unsigned char &c, uint &idx, uint &pt, bool &the_end, const uint16 *strData) { uint16 oct, ocd; /* 5-8 */ oct = FROM_LE_16(strData[idx]); oct = ((uint16)(oct << (16 - pt))) >> (16 - pt); if (pt < 6) { idx = idx + 1; oct = oct << (5 - pt); pt = pt + 11; oct = oct | (FROM_LE_16(strData[idx]) >> pt); } else { pt = pt - 5; oct = (uint)oct >> pt; } if (oct == ctrlChar) { c = '$'; the_end = true; } else if (oct == 30 || oct == 31) { ocd = FROM_LE_16(strData[idx]); ocd = (uint16)(ocd << (16 - pt)) >> (16 - pt); if (pt < 6) { idx = idx + 1; ocd = ocd << (5 - pt); pt = pt + 11; ocd = ocd | (FROM_LE_16(strData[idx]) >> pt); } else { pt = pt - 5; ocd = (uint)ocd >> pt; } if (oct == 30) c = (char)tab30[ocd]; else c = (char)tab31[ocd]; if (c == '\0') the_end = true; } else { c = (char)tabdr[oct]; } } /** * Puts a compressed 5-bit value into the string data buffer */ static void addCompressedValue(int oct, int &indis, int &point, uint16 *strData) { // Write out the part of the value that fits into the current word if (point < 5) strData[indis] |= oct >> (5 - point); else strData[indis] |= oct << (point - 5); // Handling of there's any overlap into the next word if (point < 5) { // Overlapping into next word ++indis; // Get the bits that fall into the next word and set it int remainder = oct & ((1 << (5 - point)) - 1); strData[indis] |= remainder << (16 - (5 - point)); point += -5 + 16; } else { point -= 5; if (point == 0) { point = 16; ++indis; } } } /** * Compresses a single passed character and stores it in the compressed strings buffer */ static void compressCharacter(unsigned char ch, int &indis, int &point, uint16 *strData) { if (ch == '$') { // End of string addCompressedValue(11, indis, point, strData); return; } // Scan through the tabdr array for a match for (int idx = 0; idx < 30; ++idx) { if ((idx != 11) && (tabdr[idx] == ch)) { addCompressedValue(idx, indis, point, strData); return; } } // Scan through the tab30 array for (int idx = 0; idx < 32; ++idx) { if (tab30[idx] == ch) { addCompressedValue(30, indis, point, strData); addCompressedValue(idx, indis, point, strData); return; } } // Scan through the tab31 array for (int idx = 0; idx < 32; ++idx) { if (tab31[idx] == ch) { addCompressedValue(31, indis, point, strData); addCompressedValue(idx, indis, point, strData); return; } } printf("Encountered invalid character '%c' when compressing strings\n", ch); exit(1); } /** * string extractor */ static void export_strings(const char *textFilename) { char buffer[BUFFER_SIZE]; uint16 *strData; // Open input and output files if (!txxInp.open("TXX.INP", kFileReadMode)) { if (!txxInp.open("TXX.MOR", kFileReadMode)) { printf("Missing TXX.INP/MOR"); exit(-1); } } if (!txxNtp.open("TXX.NTP", kFileReadMode)) { if (!txxNtp.open("TXX.IND", kFileReadMode)) { printf("Missing TXX.NTP/IND"); exit(-1); } } textFile.open(textFilename, kFileWriteMode); // Read all the compressed string data into a buffer printf("%d %d", txxInp.size(), txxNtp.size()); strData = (uint16 *)malloc(txxInp.size()); txxInp.read(strData, txxInp.size()); // Loop through getting each string for (unsigned int strIndex = 0; strIndex < (txxNtp.size() / 3); ++strIndex) { uint indis = txxNtp.readWord(); uint point = txxNtp.readByte(); // Extract the string int charIndex = 0; unsigned char ch; bool endFlag = false; do { extractCharacter(ch, indis, point, endFlag, strData); buffer[charIndex++] = ch; if (charIndex == BUFFER_SIZE) { printf("Extracted string exceeded allowed buffer size.\n"); exit(1); } if (indis >= (txxInp.size() / 2)) endFlag = true; } while (!endFlag); // Write out the string buffer[charIndex++] = '\n'; buffer[charIndex] = '\0'; textFile.writeString(buffer); } // Close the files and free the buffer free(strData); txxInp.close(); txxNtp.close(); textFile.close(); } /** * string importer */ static void import_strings(const char *textFilename) { // Open input and output files if (!txxInp.open("TXX.INP", kFileWriteMode)) { printf("Missing TXX data file"); exit(-1); } if (!txxNtp.open("TXX.NTP", kFileWriteMode)) { printf("Missing TXX index file"); exit(-1); } textFile.open(textFilename, kFileReadMode); // Set up a buffer for the output compressed strings uint16 strData[BUFFER_SIZE]; memset(strData, 0, BUFFER_SIZE); char sLine[BUFFER_SIZE]; int indis = 0; int point = 16; while (textFile.pos() < textFile.size()) { // Read in the next source line textFile.readString(sLine); // Write out the index entry for the string txxNtp.writeWord(indis); txxNtp.writeByte(point); // Loop through writing out the characters to the compressed data buffer char *s = sLine; while (*s) { compressCharacter(*s, indis, point, strData); ++s; } } // Write out the compressed data if (point != 16) ++indis; txxInp.write(strData, indis * 2); // Close the files txxInp.close(); txxNtp.close(); textFile.close(); } int main(int argc, char *argv[]) { if (argc != 4) { printf("Format: %s export|import v1|v2 output_file\n", argv[0]); printf("where:\nv1: French DOS version\nv2: German DOS version\n"); printf("The program must be run from the directory with the Mortville Manor game files.\n"); exit(0); } if (!strcmp(argv[2], "v1")) { tab30 = tab30Fr; tab31 = tab31Fr; tabdr = tabdrFr; ctrlChar = 11; } else if (!strcmp(argv[2], "v2")) { tab30 = tab30De; tab31 = tab31De; tabdr = tabdrDe; ctrlChar = 10; } else { printf("Unknown version"); exit(-1); } // Do the processing if (!strcmp(argv[1], "export")) export_strings(argv[3]); else if (!strcmp(argv[1], "import")) import_strings(argv[3]); else printf("Unknown operation specified\n"); }