[Scummvm-cvs-logs] SF.net SVN: scummvm: [32514] tools/trunk

lordhoto at users.sourceforge.net lordhoto at users.sourceforge.net
Tue Jun 3 19:14:14 CEST 2008


Revision: 32514
          http://scummvm.svn.sourceforge.net/scummvm/?rev=32514&view=rev
Author:   lordhoto
Date:     2008-06-03 10:14:14 -0700 (Tue, 03 Jun 2008)

Log Message:
-----------
Implemented support for (re)compressing kyra3 TLK and AUD files. (not supported by ScummVM yet)

Modified Paths:
--------------
    tools/trunk/compress_kyra.cpp
    tools/trunk/extract_kyra.h
    tools/trunk/kyra_pak.cpp
    tools/trunk/kyra_pak.h

Modified: tools/trunk/compress_kyra.cpp
===================================================================
--- tools/trunk/compress_kyra.cpp	2008-06-03 09:23:56 UTC (rev 32513)
+++ tools/trunk/compress_kyra.cpp	2008-06-03 17:14:14 UTC (rev 32514)
@@ -25,6 +25,7 @@
 
 static void showhelp(const char *exename);
 static void process(const char *infile, const char *output);
+static void processKyra3(const char *infile, const char *output);
 
 #define OUTPUT_MP3 ".VO3"
 #define OUTPUT_OGG ".VOG"
@@ -44,21 +45,22 @@
 	int i = 0;
 
 	/* Compression mode */
+	bool isKyra3 = false;
 	gCompMode = kMP3Mode;
 	i = 1;
 
-	if (strcmp(argv[1], "--mp3") == 0) {
-		gCompMode = kMP3Mode;
-		i++;
+	for (; i < argc - 2; ++i) {
+		if (strcmp(argv[i], "--mp3") == 0)
+			gCompMode = kMP3Mode;
+		else if (strcmp(argv[i], "--vorbis") == 0)
+			gCompMode = kVorbisMode;
+		else if (strcmp(argv[i], "--flac") == 0)
+			gCompMode = kFlacMode;
+		else if (strcmp(argv[i], "--kyra3") == 0)
+			isKyra3 = true;
+		else
+			break;
 	}
-	else if (strcmp(argv[1], "--vorbis") == 0) {
-		gCompMode = kVorbisMode;
-		i++;
-	}
-	else if (strcmp(argv[1], "--flac") == 0) {
-		gCompMode = kFlacMode;
-		i++;
-	}
 
 	switch (gCompMode) {
 	case kMP3Mode:
@@ -84,19 +86,23 @@
 	sprintf(inputFile, "%s/%s", argv[argc - 2], argv[argc - 3]);
 	sprintf(outputFile, "%s/%s", argv[argc - 1], argv[argc - 3]);
 
-	if (scumm_stricmp(inputFile, outputFile) == 0) {
+	if (scumm_stricmp(inputFile, outputFile) == 0)
 		error("infile and outfile are the same file");
-	}
 
-	process(inputFile, outputFile);
+	if (!isKyra3)
+		process(inputFile, outputFile);
+	else
+		processKyra3(inputFile, outputFile);
 
 	return 0;
 }
 
 static void showhelp(const char *exename) {
-	printf("\nUsage: %s [params] <file> <inputdir> <outputdir>\n", exename);
+	printf("\nUsage: %s [params] [mode params] <file> <inputdir> <outputdir>\n", exename);
 
 	printf("\nParams:\n");
+	printf(" --kyra3      compress files from The Legend of Kyrandia: Book Three: Malcolm's Revenge\n");
+	printf("\n");
 	printf(" --mp3        encode to MP3 format (default)\n");
 	printf(" --vorbis     encode to Vorbis format\n");
 	printf(" --flac       encode to Flac format\n");
@@ -149,21 +155,18 @@
 static void process(const char *infile, const char *outfile) {
 	PAKFile input, output;
 
-	if (!input.loadFile(infile, false)) {
+	if (!input.loadFile(infile, false))
 		return;
-	}
 
-	if (!output.loadFile(0, false)) {
+	if (!output.loadFile(0, false))
 		return;
-	}
 
 	PAKFile::cFileList *list = input.getFileList();
 	char outputName[32];
 
 	for (; list; list = list->next) {
-		if (!hasSuffix(list->filename, ".VOC")) {
+		if (!hasSuffix(list->filename, ".VOC"))
 			continue;
-		}
 
 		if (list->data[26] != 1) {
 			warning("broken VOC file '%s' skipping it...", list->filename);
@@ -179,9 +182,8 @@
 		fclose(tempFile);
 
 		char *vocStart = strstr(outputName, ".VOC");
-		for (unsigned int i = 0; i < strlen(outputExt); ++i) {
+		for (unsigned int i = 0; i < strlen(outputExt); ++i)
 			vocStart[i] = outputExt[i];
-		}
 
 		output.addFile(outputName, tempEncoded);
 
@@ -190,9 +192,312 @@
 		unlink(tempEncoded);
 	}
 
-	if (output.getFileList()) {
+	if (output.getFileList())
 		output.saveFile(outfile);
-	} else {
+	else
 		printf("file '%s' doesn't contain any .voc files\n", infile);
+}
+
+// Kyra3 specifc code
+
+static uint16 clip8BitSample(int16 sample) {
+	if (sample > 255)
+		return 255;
+	if (sample < 0)
+		return 0;
+	return sample;
+}
+
+static int decodeChunk(FILE *in, FILE *out) {
+	uint16 size = readUint16LE(in);
+	uint16 outSize = readUint16LE(in);
+	uint32 id = readUint32LE(in);
+	byte *inputBuffer, *outputBuffer;
+	int bytesRead = 0;
+
+	int16 curSample;
+	uint8 code;
+	int8 count;
+	uint16 input;
+	int i, j;
+
+	uint16 remaining;
+
+	const int8 WSTable2Bit[] = { -2, -1, 0, 1 };
+	const int8 WSTable4Bit[] = {
+		-9, -8, -6, -5, -4, -3, -2, -1,
+		 0,  1,  2,  3,  4,  5,  6,  8
+	};
+
+	assert(id == 0x0000DEAF);
+
+	bytesRead += (8 + size);
+
+	outputBuffer = (byte *)malloc(outSize);
+	assert(outputBuffer);
+
+	if (size == outSize) {
+		int readSize = size;
+		while (readSize > 0) {
+			int read = fread(outputBuffer, 1, readSize, in);
+			if (read <= 0)
+				error("[1] Couldn't read data");
+			readSize -= read;
+		}
+		while (size > 0)  {
+			int written = fwrite(outputBuffer, 1, size, out);
+			if (written <= 0)
+				error("[1] Couldn't write data");
+			size -= written;
+		}
+		free(outputBuffer);
+		return bytesRead;
 	}
+
+	inputBuffer = (byte *)malloc(size);
+	assert(inputBuffer);
+
+	int readSize = size;
+	while (readSize > 0) {
+		int read = fread(inputBuffer, 1, readSize, in);
+		if (read <= 0)
+			error("[2] Couldn't read data");
+		readSize -= read;
+	}
+
+	curSample = 0x80;
+	i = 0;
+	j = 0;
+
+	remaining = outSize;
+
+	while (remaining > 0) {
+		input = inputBuffer[i++] << 2;
+		code = (input >> 8) & 0xff;
+		count = (input & 0xff) >> 2;
+
+		switch (code) {
+		case 2:
+			if (count & 0x20) {
+				/* NOTE: count is signed! */
+				count <<= 3;
+				curSample += (count >> 3);
+				outputBuffer[j++] = curSample;
+				remaining--;
+			} else {
+				for (; count >= 0; count--) {
+					outputBuffer[j++] = inputBuffer[i++];
+					remaining--;
+				}
+				curSample = inputBuffer[i - 1];
+			}
+			break;
+		case 1:
+			for (; count >= 0; count--) {
+				code = inputBuffer[i++];
+
+				curSample += WSTable4Bit[code & 0x0f];
+				curSample = clip8BitSample(curSample);
+				outputBuffer[j++] = curSample;
+
+				curSample += WSTable4Bit[code >> 4];
+				curSample = clip8BitSample(curSample);
+				outputBuffer[j++] = curSample;
+
+				remaining -= 2;
+			}
+			break;
+		case 0:
+			for (; count >= 0; count--) {
+				code = inputBuffer[i++];
+
+				curSample += WSTable2Bit[code & 0x03];
+				curSample = clip8BitSample(curSample);
+				outputBuffer[j++] = curSample;
+
+				curSample += WSTable2Bit[(code >> 2) & 0x03];
+				curSample = clip8BitSample(curSample);
+				outputBuffer[j++] = curSample;
+
+				curSample += WSTable2Bit[(code >> 4) & 0x03];
+				curSample = clip8BitSample(curSample);
+				outputBuffer[j++] = curSample;
+
+				curSample += WSTable2Bit[(code >> 6) & 0x03];
+				curSample = clip8BitSample(curSample);
+				outputBuffer[j++] = curSample;
+
+				remaining -= 4;
+			}
+			break;
+		default:
+			for (; count >= 0; count--) {
+				outputBuffer[j++] = curSample;
+				remaining--;
+			}
+		}
+	}
+
+	while (outSize > 0)  {
+		int written = fwrite(outputBuffer, 1, outSize, out);
+		if (written <= 0)
+			error("[2] Couldn't write data");
+		outSize -= written;
+	}
+
+	free(inputBuffer);
+	free(outputBuffer);
+
+	return bytesRead;
 }
+
+typedef struct {
+	uint16 freq;
+	uint32 size;
+	byte flags;
+	byte type;
+} AUDHeader;
+
+static void compressAUDFile(FILE *input, const char *outfile) {
+	AUDHeader header;
+
+	header.freq = readUint16LE(input);
+	header.size = readUint32LE(input);
+	header.flags = readByte(input);
+	header.type = readByte(input);
+	//printf("%d Hz, %d bytes, type %d (%08X)\n", header.freq, header.size, header.type, header.flags);
+
+	FILE *output = fopen(TEMP_RAW, "wb");
+
+	if (!output)
+		error("Couldn't create temporary file '%s'", TEMP_RAW);
+
+	uint32 remaining = header.size;
+	while (remaining > 0)
+		remaining -= decodeChunk(input, output);
+
+	fclose(output);
+
+	encodeAudio(TEMP_RAW, true, header.freq, outfile, gCompMode);
+
+	unlink(TEMP_RAW);
+}
+
+static void changeFileExt(char *filename) {
+	char *str = filename + strlen(filename) - 4;
+
+	if (*str != '.')
+		error("Invalid filename '%s'", filename);
+
+	++str;
+
+	switch (gCompMode) {
+	case kMP3Mode:
+		*str++ = 'm';
+		*str++ = 'p';
+		*str++ = '3';
+		break;
+
+	case kVorbisMode:
+		*str++ = 'o';
+		*str++ = 'g';
+		*str++ = 'g';
+		break;
+
+	case kFlacMode:
+		*str++ = 'f';
+		*str++ = 'l';
+		*str++ = 'a';
+		break;
+
+	default:
+		error("Unknown compression mode");
+	}
+
+	*str = 0;
+}
+
+struct DuplicatedFile {
+	uint32 resFilename;
+	uint32 resOffset;
+};
+
+static const DuplicatedFile *findDuplicatedFile(uint32 resOffset, const DuplicatedFile *list, const uint maxEntries) {
+	for (uint i = 0; i < maxEntries; ++i) {
+		if (list[i].resOffset == resOffset && list[i].resOffset != 0)
+			return &list[i];
+	}
+
+	return 0;
+}
+
+static void processKyra3(const char *infile, const char *outfile) {
+	if (hasSuffix(infile, ".AUD")) {
+		char outname[1024];
+
+		strncpy(outname, outfile, sizeof(outname));
+		changeFileExt(outname);
+
+		FILE *input = fopen(infile, "rb");
+		if (!input)
+			error("Couldn't open file '%s'", infile);
+
+		compressAUDFile(input, outname);
+
+		fclose(input);
+	} else if (hasSuffix(infile, ".TLK")) {
+		PAKFile output;
+
+		FILE *input = fopen(infile, "rb");
+		if (!input)
+			error("Couldn't open file '%s'", infile);
+
+		if (!output.loadFile(0, false))
+			return;
+
+		uint16 files = readUint16LE(input);
+		DuplicatedFile *red = new DuplicatedFile[files];
+		memset(red, 0, sizeof(DuplicatedFile)*files);
+
+		for (uint i = 0; i < files; ++i) {
+			uint32 resFilename = readUint32LE(input);
+			uint32 resOffset = readUint32LE(input);
+
+			char outname[16];
+			snprintf(outname, 16, "%.08u.AUD", resFilename);
+			changeFileExt(outname);
+
+			const DuplicatedFile *file = findDuplicatedFile(resOffset, red, files);
+			if (file) {
+				char linkname[16];
+				snprintf(linkname, 16, "%.08u.AUD", file->resFilename);
+				changeFileExt(linkname);
+
+				output.linkFiles(outname, linkname);
+			} else {
+				red[i].resFilename = resFilename;
+				red[i].resOffset = resOffset;
+
+				uint32 pos = (uint32)ftell(input);
+				fseek(input, resOffset + 4, SEEK_SET);
+
+				compressAUDFile(input, outname);
+
+				output.addFile(outname, outname);
+
+				unlink(outname);
+
+				fseek(input, pos, SEEK_SET);
+			}
+		}
+
+		delete[] red;
+		fclose(input);
+
+		if (output.getFileList())
+			output.saveFile(outfile);
+	} else {
+		error("Unsupported file '%s'", infile);
+	}
+}
+

Modified: tools/trunk/extract_kyra.h
===================================================================
--- tools/trunk/extract_kyra.h	2008-06-03 09:23:56 UTC (rev 32513)
+++ tools/trunk/extract_kyra.h	2008-06-03 17:14:14 UTC (rev 32514)
@@ -47,18 +47,16 @@
 
 		FileList *findEntry(const char *f) {
 			for (FileList *cur = this; cur; cur = cur->next) {
-				if (scumm_stricmp(cur->filename, f) != 0)
-					continue;
-				return cur;
+				if (scumm_stricmp(cur->filename, f) == 0)
+					return cur;
 			}
 			return 0;
 		}
 
 		const FileList *findEntry(const char *f) const {
 			for (const FileList *cur = this; cur; cur = cur->next) {
-				if (scumm_stricmp(cur->filename, f) != 0)
-					continue;
-				return cur;
+				if (scumm_stricmp(cur->filename, f) == 0)
+					return cur;
 			}
 			return 0;
 		}

Modified: tools/trunk/kyra_pak.cpp
===================================================================
--- tools/trunk/kyra_pak.cpp	2008-06-03 09:23:56 UTC (rev 32513)
+++ tools/trunk/kyra_pak.cpp	2008-06-03 17:14:14 UTC (rev 32514)
@@ -79,12 +79,14 @@
 	}
 
 	delete[] buffer;
+	loadLinkEntry();
 	return true;
 }
 
 bool PAKFile::saveFile(const char *file) {
 	if (!_fileList)
 		return true;
+	generateLinkEntry();
 
 	FILE *f = fopen(file, "wb");
 	if (!f) {
@@ -119,6 +121,12 @@
 }
 
 const uint8 *PAKFile::getFileData(const char *file, uint32 *size) {
+	if (_links) {
+		LinkList *entry = _links->findSrcEntry(file);
+		if (entry)
+			file = entry->linksTo;
+	}
+
 	FileList *cur = (_fileList != 0) ? _fileList->findEntry(file) : 0;
 
 	if (!cur)
@@ -130,7 +138,7 @@
 }
 
 bool PAKFile::addFile(const char *name, const char *file) {
-	if (_fileList && _fileList->findEntry(name)) {
+	if ((_fileList && _fileList->findEntry(name)) || (_links && _links->findSrcEntry(name))) {
 		error("entry '%s' already exists");
 		return false;
 	}
@@ -153,7 +161,7 @@
 }
 
 bool PAKFile::addFile(const char *name, uint8 *data, uint32 size) {
-	if (_fileList && _fileList->findEntry(name)) {
+	if (_fileList && _fileList->findEntry(name) || (_links && _links->findSrcEntry(name))) {
 		error("entry '%s' already exists");
 		return false;
 	}
@@ -173,7 +181,165 @@
 	return true;
 }
 
+bool PAKFile::linkFiles(const char *name, const char *linkTo) {
+	if (!_fileList)
+		error("Can't find file '%s' in file list", linkTo);
+	if (!_fileList->findEntry(linkTo))
+		error("Can't find file '%s' in file list", linkTo);
+	if ((_fileList && _fileList->findEntry(name)) || (_links && _links->findSrcEntry(name)))
+		error("entry '%s' already exists");
+
+	LinkList *entry = new LinkList;
+	assert(entry);
+
+	entry->filename = new char[strlen(name)+1];
+	assert(entry->filename);
+	strncpy(entry->filename, name, strlen(name)+1);
+	entry->linksTo = _fileList->findEntry(linkTo)->filename;
+
+	if (!_links)
+		_links = entry;
+	else
+		_links->addEntry(entry);
+	
+	return true;	
+}
+
+static bool isInList(const char * const *linkList, const char *linkTo, const int maxSize) {
+	for (int i = 0; i < maxSize; ++i) {
+		if (scumm_stricmp(linkList[i], linkTo) == 0)
+			return true;
+	}
+
+	return false;
+}
+
+void PAKFile::generateLinkEntry() {
+	removeFile("LINKLIST");
+	if (!_links)
+		return;
+
+	const int countLinks = _links->size();
+	FILE *output = fopen("LINKLIST.TMP", "wb");
+	if (!output)
+		error("Couldn't open file 'LINKLIST.TMP'");
+	
+	const char **linkList = new const char *[countLinks];
+	int usedLinks = 0;
+
+	const LinkList *entry = _links;
+	for (int i = 0; i < countLinks && entry; ++i, entry = entry->next) {
+		if (isInList(linkList, entry->linksTo, usedLinks))
+			continue;
+		
+		linkList[usedLinks++] = entry->linksTo;
+	}
+
+	writeUint32BE(output, usedLinks);
+	for (int i = 0; i < usedLinks; ++i) {
+		int count = 0;
+		entry = _links;		
+		while (entry) {
+			if (scumm_stricmp(entry->linksTo, linkList[i]) == 0)
+				++count;
+			entry = entry->next;
+		}
+
+		const char *p = linkList[i];
+		while (*p)
+			writeByte(output, *p++);
+		writeByte(output, 0);
+
+		writeUint32BE(output, count);
+		for (entry = _links; entry; entry = entry->next) {
+			if (scumm_stricmp(entry->linksTo, linkList[i]) != 0)
+				continue;
+
+			p = entry->filename;
+			while (*p)
+				writeByte(output, *p++);
+			writeByte(output, 0);
+		}
+	}
+
+	fclose(output);
+
+	addFile("LINKLIST", "LINKLIST.TMP");
+
+	unlink("LINKLIST.TMP");
+	delete[] linkList;
+}
+
+void PAKFile::loadLinkEntry() {
+	delete _links; _links = 0;
+
+	if (_fileList && _fileList->findEntry("LINKLIST")) {
+		const FileList *entry = _fileList->findEntry("LINKLIST");
+		const uint8 *src = entry->data;
+
+		uint32 links = READ_BE_UINT32(src); src += 4;
+		for (uint i = 0; i < links; ++i) {
+			const char *linksTo = (const char *)src;
+
+			if (!_fileList->findEntry(linksTo))
+				error("Couldn't find link destination '%s'", linksTo);
+			src += strlen(linksTo) + 1;
+
+			uint32 sources = READ_BE_UINT32(src); src += 4;
+			for (uint j = 0; j < sources; ++j) {
+				LinkList *newEntry = new LinkList;
+				assert(newEntry);
+
+				newEntry->linksTo = _fileList->findEntry(linksTo)->filename;
+				newEntry->filename = new char[strlen((const char *)src) + 1];
+				assert(newEntry->filename);
+				strcpy(newEntry->filename, (const char *)src);
+				src += strlen((const char *)src) + 1;
+
+				if (_links)
+					_links->addEntry(newEntry);
+				else
+					_links = newEntry;
+			}
+		}
+	}
+}
+
 bool PAKFile::removeFile(const char *name) {
+	if (_links) {
+		LinkList *link = _links->findEntry(name);
+		while (link) {
+			warning("Implicitly removing link '%s' to file '%s'", link->filename, name);
+
+			LinkList *last = _links;
+			while (last != link)
+				last = last->next;
+
+			if (last == _links)
+				_links = link->next;
+			else
+				last->next = link->next;
+
+			link->next = 0;
+			delete link;
+		}
+
+		if ((link = _links->findSrcEntry(name)) != 0) {
+			LinkList *last = _links;
+			while (last != link)
+				last = last->next;
+
+			if (last == _links)
+				_links = link->next;
+			else
+				last->next = link->next;
+
+			link->next = 0;
+			delete link;
+			return true;
+		}
+	}
+
 	for (FileList *cur = _fileList, *last = 0; cur; last = cur, cur = cur->next) {
 		if (scumm_stricmp(cur->filename, name) == 0) {
 			FileList *next = cur->next;
@@ -189,6 +355,41 @@
 	return false;
 }
 
+void PAKFile::drawFileList() {
+	Extractor::drawFileList();
+
+	if (_links) {
+		printf("Linked files (count: %d):\n", _links->size());
+		for (const LinkList *i = _links; i; i = i->next)
+			printf("Filename: '%s' -> '%s'\n", i->filename, i->linksTo);
+	}
+}
+
+bool PAKFile::outputAllFiles(const char *outputPath) {
+	if (!Extractor::outputAllFiles(outputPath))
+		return false;
+
+	char outputFilename[1024];
+	for (const LinkList *entry = _links; entry; entry = entry->next) {
+		sprintf(outputFilename, "%s/%s", outputPath, entry->filename);
+		if (!outputFileAs(entry->linksTo, outputPath))
+			return false;
+	}
+
+	return true;
+}
+
+bool PAKFile::outputFileAs(const char *file, const char *outputName) {
+	for (const LinkList *entry = _links; entry; entry = entry->next) {
+		if (scumm_stricmp(entry->filename, file) == 0) {
+			file = entry->linksTo;
+			break;
+		}
+	}
+
+	return Extractor::outputFileAs(file, outputName);
+}
+
 //HACK: move this to another file
 
 void Extractor::drawFileList() {

Modified: tools/trunk/kyra_pak.h
===================================================================
--- tools/trunk/kyra_pak.h	2008-06-03 09:23:56 UTC (rev 32513)
+++ tools/trunk/kyra_pak.h	2008-06-03 17:14:14 UTC (rev 32514)
@@ -28,7 +28,7 @@
 
 class PAKFile : public Extractor {
 public:
-	PAKFile() : _fileList(0), _isAmiga(false) {}
+	PAKFile() : _fileList(0), _isAmiga(false), _links(0) {}
 	~PAKFile() { delete _fileList; }
 
 	bool loadFile(const char *file, const bool isAmiga);
@@ -42,12 +42,59 @@
 	bool addFile(const char *name, const char *file);
 	bool addFile(const char *name, uint8 *data, uint32 size);
 
+	bool linkFiles(const char *name, const char *linkTo);
+
 	bool removeFile(const char *name);
 
 	cFileList *getFileList() const { return _fileList; }
+
+	void drawFileList();
+	bool outputAllFiles(const char *outputPath);
+	bool outputFileAs(const char *file, const char *outputName);
 private:
 	FileList *_fileList;
 	bool _isAmiga;
+
+	struct LinkList {
+		LinkList() : filename(0), linksTo(0), next(0) {}
+		~LinkList() { delete[] filename; delete next; }
+
+		char *filename;
+		const char *linksTo;
+
+		LinkList *next;
+
+		LinkList *findEntry(const char *linkDest) {
+			for (LinkList *cur = this; cur; cur = cur->next) {
+				if (scumm_stricmp(cur->linksTo, linkDest) == 0)
+					return cur;
+			}
+			return 0;
+		}
+
+		LinkList *findSrcEntry(const char *f) {
+			for (LinkList *cur = this; cur; cur = cur->next) {
+				if (scumm_stricmp(cur->filename, f) == 0)
+					return cur;
+			}
+			return 0;
+		}
+
+		void addEntry(LinkList *e) {
+			if (next)
+				next->addEntry(e);
+			else
+				next = e;
+		}
+
+		int size() const {
+			return 1 + (next ? next->size() : 0);
+		}
+	};
+
+	LinkList *_links;
+	void generateLinkEntry();
+	void loadLinkEntry();
 };
 
 #endif


This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.




More information about the Scummvm-git-logs mailing list