[Scummvm-cvs-logs] SF.net SVN: scummvm:[41671] tools/trunk/compress_gob.cpp
strangerke at users.sourceforge.net
strangerke at users.sourceforge.net
Fri Jun 19 13:16:58 CEST 2009
Revision: 41671
http://scummvm.svn.sourceforge.net/scummvm/?rev=41671&view=rev
Author: strangerke
Date: 2009-06-19 11:16:57 +0000 (Fri, 19 Jun 2009)
Log Message:
-----------
- Add archive filename to the 'gob' config file
- some cleanup
- Doxygen comments
Modified Paths:
--------------
tools/trunk/compress_gob.cpp
Modified: tools/trunk/compress_gob.cpp
===================================================================
--- tools/trunk/compress_gob.cpp 2009-06-19 11:12:57 UTC (rev 41670)
+++ tools/trunk/compress_gob.cpp 2009-06-19 11:16:57 UTC (rev 41671)
@@ -35,10 +35,10 @@
~Chunk() { delete next; }
};
-Chunk *readChunkConf(FILE *gobconf, uint16 &chunkCount);
+Chunk *readChunkConf(FILE *gobconf, char *stkName, uint16 &chunkCount);
void writeEmptyHeader(FILE *stk, uint16 chunkCount);
-void writeBody(FILE *stk, uint16 chunkcount, Chunk *chunks);
-uint32 writeBodyFile(FILE *stk, FILE *src);
+void writeBody(FILE *stk, Chunk *chunks);
+uint32 writeBodyStoreFile(FILE *stk, FILE *src);
uint32 writeBodyPackFile(FILE *stk, FILE *src);
void rewriteHeader(FILE *stk, uint16 chunkCount, Chunk *chunks);
bool filcmp(FILE *src1, Chunk *compChunk);
@@ -47,12 +47,11 @@
byte *packData(byte *src, uint32 &size);
int main(int argc, char **argv) {
- char *outFilename;
- char *tmpStr;
Chunk *chunks;
FILE *stk;
FILE *gobConf;
uint16 chunkCount;
+ char stkName[256];
if ((argc < 2) || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
printf("Usage: %s <Conf file>\n\n", argv[0]);
@@ -63,25 +62,13 @@
if (!(gobConf = fopen(argv[1], "r")))
error("Couldn't open conf file \"%s\"", argv[1]);
- outFilename = new char[strlen(argv[1]) + 5];
- getFilename(argv[1], outFilename);
-
- tmpStr = strstr(outFilename, ".");
-
-// TODO : other extensions ? (ITK, LTK)
- if (tmpStr != 0)
- strcpy(tmpStr, ".stk");
- else
- strcat(outFilename, ".stk");
-
- if (!(stk = fopen(outFilename, "wb")))
- error("Couldn't create file \"%s\"", outFilename);
-
- chunks = readChunkConf(gobConf, chunkCount);
+ chunks = readChunkConf(gobConf, stkName, chunkCount);
fclose(gobConf);
+ if (!(stk = fopen(stkName, "wb")))
+ error("Couldn't create file \"%s\"", stkName);
writeEmptyHeader (stk, chunkCount);
- writeBody(stk, chunkCount, chunks);
+ writeBody(stk, chunks);
rewriteHeader(stk, chunkCount, chunks);
fflush(stk);
@@ -100,7 +87,19 @@
error(msg);
}
-Chunk *readChunkConf(FILE *gobConf, uint16 &chunkCount) {
+/*! \brief Config file parser
+ * \param gobConf Config file to parse
+ * \param stk STK/ITK archive file to be created
+ * \param chunkCount Number of chunks to be written in the archive file
+ * \return A list of chunks
+ *
+ * This function reads the '.gob' config file (generated by extract_gob_stk).
+ * It creates the output archive file and a list of chunks containing the file
+ * and compression information.
+ * In order to have a slightly better compression ration in some cases (Playtoons), it
+ * also detects duplicate files.
+ */
+Chunk *readChunkConf(FILE *gobConf, char *stkName, uint16 &chunkCount) {
Chunk *chunks = new Chunk;
Chunk *curChunk = chunks;
Chunk *parseChunk;
@@ -109,7 +108,10 @@
chunkCount = 1;
-// first read: signature
+// First read: Output filename
+ fscanf(gobConf, "%s", stkName);
+
+// Second read: signature
fscanf(gobConf, "%s", buffer);
if (!strcmp(buffer, confSTK21))
error("STK21 not yet handled");
@@ -161,23 +163,36 @@
return chunks;
}
+/*! \brief Write an empty header to the STK archive
+ * \param stk STK/ITK archive file
+ * \param chunkCount Number of chunks to be written in the archive file
+ *
+ * This function writes an empty header in the STK archive. This is required as
+ * the header length is variable and depends on the number of chunks to be written
+ * in the archive file.
+ *
+ * This header will be overwritten just before the end of the program execution
+ */
void writeEmptyHeader(FILE *stk, uint16 chunkCount) {
- int count;
-
-// Write empty header dummy header, which will be overwritten
-// at the end of the program execution.
- for (count = 0; count < 2 + (chunkCount * 22); count++)
+ for (uint32 count = 0; count < 2 + (uint32) (chunkCount * 22); count++)
fputc(0, stk);
return;
}
-void writeBody(FILE *stk, uint16 chunkCount, Chunk *chunks) {
+/*! \brief Write the body of the STK archive
+ * \param stk STK/ITK archive file
+ * \param chunks Chunk list
+ *
+ * This function writes the body of the STK archive by storing or compressing
+ * (or skipping duplicate files) the files. It also updates the chunk information
+ * with the size of the chunk in the archive, the compression method (if modified),
+ * ...
+ */
+void writeBody(FILE *stk, Chunk *chunks) {
Chunk *curChunk = chunks;
FILE *src;
uint32 tmpSize;
- int count;
- char buffer[4096];
while(curChunk) {
if (!(src = fopen(curChunk->name, "rb")))
@@ -205,13 +220,8 @@
if (curChunk->packed == 0) {
tmpSize = 0;
printf("Storing %12s\t", curChunk->name);
- do {
- count = fread(buffer, 1, 4096, src);
- fwrite(buffer, 1, count, stk);
- tmpSize += count;
- } while (count == 4096);
- curChunk->size = tmpSize;
- printf("%d bytes\n", tmpSize);
+ curChunk->size = writeBodyStoreFile(stk, src);
+ printf("%d bytes\n", curChunk->size);
}
fclose(src);
@@ -220,21 +230,25 @@
return;
}
-uint32 writeBodyFile(FILE *stk, FILE *src) {
- int count;
- char buffer[4096];
- uint32 tmpSize;
-
- tmpSize = 0;
- do {
- count = fread(buffer, 1, 4096, src);
- fwrite(buffer, 1, count, stk);
- tmpSize += count;
- } while (count == 4096);
- return tmpSize;
-}
-
-
+/*! \brief Rewrites the header of the archive file
+ * \param stk STK/ITK archive file
+ * \param chunkCount Number of chunks
+ * \param chunks List of chunks
+ *
+ * This function rewrites the header of the archive, replacing dummy values
+ * by the one computed during execution.
+ * The structure of the header is the following :
+ * + 2 bytes : numbers of files archived in the .stk/.itk
+ * Then, for each files :
+ * + 13 bytes : the filename, terminated by '\0'. In original, there's
+ * garbage after if the filename has not the maximum length
+ * + 4 bytes : size of the chunk
+ * + 4 bytes : start position of the chunk in the file
+ * + 1 byte : If 0 : not compressed, if 1 : compressed
+ *
+ * The duplicate files are defined using the same information
+ * as the one of the replacement file.
+*/
void rewriteHeader(FILE *stk, uint16 chunkCount, Chunk *chunks) {
uint16 i;
char buffer[1024];
@@ -242,14 +256,6 @@
rewind(stk);
-// The structure of the header is the following :
-//+ 2 bytes : numbers of files archived in the .stk/.itk
-// Then, for each files :
-//+ 13 bytes : the filename, terminated by '\0'. In original, there's
-// garbage after if the filename has not the maximum length
-//+ 4 bytes : size of the chunk
-//+ 4 bytes : start position of the chunk in the file
-//+ 1 byte : If 0 : not compressed, if 1 : compressed
buffer[0] = chunkCount & 0xFF;
buffer[1] = chunkCount >> 8;
fwrite(buffer, 1, 2, stk);
@@ -290,6 +296,33 @@
return;
}
+/*! \brief Stores a file in the archive file
+ * \param stk STK/ITK archive file
+ * \param src File to be stored
+ * \return Size of the file stored
+ *
+ * This function stores a file in the STK archive
+ */
+uint32 writeBodyStoreFile(FILE *stk, FILE *src) {
+ int count;
+ char buffer[4096];
+ uint32 tmpSize = 0;
+
+ do {
+ count = fread(buffer, 1, 4096, src);
+ fwrite(buffer, 1, count, stk);
+ tmpSize += count;
+ } while (count == 4096);
+ return tmpSize;
+}
+
+/*! \brief Compress a file in the archive file
+ * \param stk STK/ITK archive file
+ * \param src File to be stored
+ * \return Size of the resulting compressed chunk
+ *
+ * This function compress a file in the STK archive
+ */
uint32 writeBodyPackFile(FILE *stk, FILE *src) {
byte dico[4114];
byte writeBuffer[17];
@@ -315,24 +348,25 @@
writeBuffer[3] = size >> 24;
fwrite(writeBuffer, 1, 4, stk);
-// TODO : check size, if too small, handle correctly
-
+// Size is already checked : small files (less than 8 characters)
+// are not compressed, so copying the first three bytes is safe.
dicoIndex = 4078;
dico[dicoIndex] = unpacked[0];
dico[dicoIndex+1] = unpacked[1];
dico[dicoIndex+2] = unpacked[2];
dicoIndex += 3;
-//writeBuffer[0] is reserved for the command byte
+// writeBuffer[0] is reserved for the command byte
writeBuffer[1] = unpacked[0];
writeBuffer[2] = unpacked[1];
writeBuffer[3] = unpacked[2];
+// Force the 3 first operation bits to 'copy character'
+ cmd = (1 << 3) - 1;
counter = size - 3;
unpackedIndex = 3;
cpt = 3;
buffIndex = 4;
- cmd = (1 << 3) - 1;
size=4;
resultcheckpos = 0;
@@ -342,6 +376,7 @@
if (!checkDico(unpacked, unpackedIndex, counter, dico, dicoIndex, resultcheckpos, resultchecklength)) {
dico[dicoIndex] = unpacked[unpackedIndex];
writeBuffer[buffIndex] = unpacked[unpackedIndex];
+// set the operation bit : copy character
cmd |= (1 << cpt);
unpackedIndex++;
dicoIndex = (dicoIndex + 1) % 4096;
@@ -356,6 +391,9 @@
writeBuffer[buffIndex] = resultcheckpos & 0xFF;
writeBuffer[buffIndex + 1] = ((resultcheckpos & 0x0F00) >> 4) + (resultchecklength - 3);
+// Do not set the operation bit : copy string from dictionary
+// cmd |= (0 << cpt);
+
unpackedIndex += resultchecklength;
dicoIndex = (dicoIndex + resultchecklength) % 4096;
resultcheckpos = (resultcheckpos + resultchecklength) % 4096;
@@ -364,6 +402,8 @@
counter -= resultchecklength;
}
+// The command byte is complete when the file is entirely compressed, or
+// when the 8 operation bits are set.
if ((cpt == 7) | (counter == 0)) {
writeBuffer[0] = cmd;
fwrite(writeBuffer, 1, buffIndex, stk);
@@ -379,6 +419,14 @@
return size;
}
+/*! \brief Compare a file to a file defined in a chunk
+ * \param src1 File to be compared
+ * \param compChunk Chunk containing information on second file to be compared
+ * \return whether they are identical or not.
+ *
+ * This function compares a file to another defined in a chunk. The file sizes
+ * are already tested outside the function.
+ */
bool filcmp(FILE *src1, Chunk *compChunk) {
uint16 readCount;
bool checkFl = true;
@@ -402,6 +450,21 @@
return checkFl;
}
+/*! \brief Compare a file to a file defined in a chunk
+ * \param unpacked Buffer being compressed
+ * \param unpackedIndex Current 'read' position in this buffer
+ * \param counter Number of bytes still to be compressed in the file
+ * \param dico Dictionary
+ * \param currIndex Current 'write' position in the dictionary (used to avoid dictionary collision)
+ * \param pos Position of the better match found, if any
+ * \param length Length of the better match found, if any
+ * \return whether a match has been found or not or not.
+ *
+ * This function search in the dictionary for matches with the characters still to be compressed.
+ * 'A match' is when at least three characters of the buffer (comparing from the current 'read' position)
+ * are found in the dictionary. The match lengths are limited to 18 characters, as the
+ * length (minus 3) is stored on 4 bits.
+ */
bool checkDico(byte *unpacked, uint32 unpackedIndex, int32 counter, byte *dico, uint16 currIndex, uint16 &pos, uint8 &length) {
uint16 tmpPos, bestPos;
uint8 tmpLength, bestLength, i;
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