[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