--- libgig/trunk/src/RIFF.cpp 2005/09/25 13:40:37 780
+++ libgig/trunk/src/RIFF.cpp 2005/11/03 23:49:11 798
@@ -49,6 +49,7 @@
ulPos = 0;
pParent = NULL;
pChunkData = NULL;
+ ChunkID = CHUNK_ID_RIFF;
this->pFile = pFile;
}
@@ -293,9 +294,9 @@
* @see Resize()
*/
unsigned long Chunk::Write(void* pData, unsigned long WordCount, unsigned long WordSize) {
- if (pFile->Mode == stream_mode_read)
- throw Exception("Writing chunk data in read-only file access mode was attempted");
- if (ulPos >= CurrentChunkSize || ulPos + WordCount * WordSize >= CurrentChunkSize)
+ if (pFile->Mode != stream_mode_read_write)
+ throw Exception("Cannot write data to chunk, file has to be opened in read+write mode first");
+ if (ulPos >= CurrentChunkSize || ulPos + WordCount * WordSize > CurrentChunkSize)
throw Exception("End of chunk reached while trying to write data");
if (!pFile->bEndianNative && WordSize != 1) {
switch (WordSize) {
@@ -743,9 +744,12 @@
* (including its header size of course)
*/
unsigned long Chunk::WriteChunk(unsigned long ulWritePos, unsigned long ulCurrentDataOffset) {
- unsigned long ulOriginalPos = ulWritePos;
+ const unsigned long ulOriginalPos = ulWritePos;
ulWritePos += CHUNK_HEADER_SIZE;
+ if (pFile->Mode != stream_mode_read_write)
+ throw Exception("Cannot write list chunk, file has to be opened in read+write mode");
+
// if the whole chunk body was loaded into RAM
if (pChunkData) {
// in case the chunk size was changed, reallocate the data in RAM with the chunk's new size
@@ -806,10 +810,12 @@
// add pad byte if needed
if ((ulStartPos + NewChunkSize) % 2 != 0) {
- char cPadByte = 0;
+ const char cPadByte = 0;
#if POSIX
+ lseek(pFile->hFileWrite, ulStartPos + NewChunkSize, SEEK_SET);
write(pFile->hFileWrite, &cPadByte, 1);
#else
+ fseek(pFile->hFileWrite, ulStartPos + NewChunkSize, SEEK_SET);
fwrite(&cPadByte, 1, 1, pFile->hFileWrite);
#endif
return ulStartPos + NewChunkSize + 1;
@@ -1063,10 +1069,10 @@
Chunk* List::AddSubChunk(uint32_t uiChunkID, uint uiBodySize) {
if (uiBodySize == 0) throw Exception("Chunk body size must be at least 1 byte");
if (!pSubChunks) LoadSubChunks();
- Chunk* pNewChunk = new Chunk(pFile, this, uiChunkID, uiBodySize);
+ Chunk* pNewChunk = new Chunk(pFile, this, uiChunkID, 0);
pSubChunks->push_back(pNewChunk);
(*pSubChunksMap)[uiChunkID] = pNewChunk;
- pFile->ResizedChunks.push_back(pNewChunk);
+ pNewChunk->Resize(uiBodySize);
return pNewChunk;
}
@@ -1197,9 +1203,12 @@
* (including its header size of course)
*/
unsigned long List::WriteChunk(unsigned long ulWritePos, unsigned long ulCurrentDataOffset) {
- unsigned long ulOriginalPos = ulWritePos;
+ const unsigned long ulOriginalPos = ulWritePos;
ulWritePos += LIST_HEADER_SIZE;
+ if (pFile->Mode != stream_mode_read_write)
+ throw Exception("Cannot write list chunk, file has to be opened in read+write mode");
+
// write all subchunks (including sub list chunks) recursively
if (pSubChunks) {
for (ChunkList::iterator iter = pSubChunks->begin(), end = pSubChunks->end(); iter != end; ++iter) {
@@ -1211,6 +1220,9 @@
CurrentChunkSize = NewChunkSize = ulWritePos - ulOriginalPos - LIST_HEADER_SIZE;
WriteHeader(ulOriginalPos);
+ // offset of this list chunk in new written file may have changed
+ ulStartPos = ulOriginalPos + LIST_HEADER_SIZE;
+
return ulWritePos;
}
@@ -1241,12 +1253,15 @@
* "from scratch". Note: there must be no empty chunks or empty list
* chunks when trying to make the new RIFF file persistent with Save()!
*
+ * @param FileType - four-byte identifier of the RIFF file type
* @see AddSubChunk(), AddSubList()
*/
- File::File() : List(this) {
+ File::File(uint32_t FileType) : List(this) {
hFileRead = hFileWrite = 0;
+ Mode = stream_mode_closed;
bEndianNative = true;
ulStartPos = RIFF_HEADER_SIZE;
+ ListType = FileType;
}
/** @brief Load existing RIFF file.
@@ -1272,6 +1287,7 @@
hFileRead = hFileWrite = fopen(path.c_str(), "rb");
if (!hFile) throw RIFF::Exception("Can't open \"" + path + "\"");
#endif // POSIX
+ Mode = stream_mode_read;
ulStartPos = RIFF_HEADER_SIZE;
ReadHeader(0);
if (ChunkID != CHUNK_ID_RIFF) {
@@ -1314,7 +1330,7 @@
if (!hFileRead) throw Exception("Could not (re)open file \"" + Filename + "\" in read mode");
#endif
__resetPos(); // reset read/write position of ALL 'Chunk' objects
- return true;
+ break;
case stream_mode_read_write:
#if POSIX
if (hFileRead) close(hFileRead);
@@ -1332,10 +1348,22 @@
}
#endif
__resetPos(); // reset read/write position of ALL 'Chunk' objects
- return true;
+ break;
+ case stream_mode_closed:
+ #if POSIX
+ if (hFileRead) close(hFileRead);
+ if (hFileWrite) close(hFileWrite);
+ #else
+ if (hFileRead) fclose(hFileRead);
+ if (hFileWrite) fclose(hFileWrite);
+ #endif
+ hFileRead = hFileWrite = 0;
+ break;
default:
throw Exception("Unknown file access mode");
}
+ Mode = NewMode;
+ return true;
}
return false;
}
@@ -1352,7 +1380,7 @@
*/
void File::Save() {
// reopen file in write mode
- bool bModeWasChanged = SetMode(stream_mode_read_write);
+ SetMode(stream_mode_read_write);
// to be able to save the whole file without loading everything into
// RAM and without having to store the data in a temporary file, we
@@ -1365,8 +1393,10 @@
unsigned long ulPositiveSizeDiff = 0;
for (ChunkList::iterator iter = ResizedChunks.begin(), end = ResizedChunks.end(); iter != end; ++iter) {
if ((*iter)->GetNewSize() == 0) throw Exception("There is at least one empty chunk (zero size)");
- unsigned long ulDiff = (*iter)->GetNewSize() - (*iter)->GetSize() + 1L; // +1 in case we have to add a pad byte
- if (ulDiff > 0) ulPositiveSizeDiff += ulDiff;
+ if ((*iter)->GetNewSize() + 1L > (*iter)->GetSize()) {
+ unsigned long ulDiff = (*iter)->GetNewSize() - (*iter)->GetSize() + 1L; // +1 in case we have to add a pad byte
+ ulPositiveSizeDiff += ulDiff;
+ }
}
unsigned long ulWorkingFileSize = GetFileSize();
@@ -1400,16 +1430,14 @@
}
// rebuild / rewrite complete RIFF tree
- unsigned long ulTotalSize = WriteChunk(0, ulPositiveSizeDiff);
+ unsigned long ulTotalSize = WriteChunk(0, ulPositiveSizeDiff);
+ unsigned long ulActualSize = __GetFileSize(hFileWrite);
// resize file to the final size
- if (ulTotalSize < ulWorkingFileSize) ResizeFile(ulTotalSize);
+ if (ulTotalSize < ulActualSize) ResizeFile(ulTotalSize);
// forget all resized chunks
ResizedChunks.clear();
-
- // reopen file in read mode
- if (bModeWasChanged) SetMode(stream_mode_read);
}
/** @brief Save changes to another file.
@@ -1417,7 +1445,8 @@
* Make all changes of all chunks persistent by writing them to another
* file. Caution: this method is optimized for writing to
* another file, do not use it to save the changes to the same
- * file! Use File::Save() in that case instead!
+ * file! Use File::Save() in that case instead! Ignoring this might
+ * result in a corrupted file, especially in case chunks were resized!
*
* After calling this method, this File object will be associated with
* the new file (given by \a path) afterwards.
@@ -1425,9 +1454,12 @@
* @param path - path and file name where everything should be written to
*/
void File::Save(const String& path) {
+ //TODO: we should make a check here if somebody tries to write to the same file and automatically call the other Save() method in that case
+
+ if (Filename.length() > 0) SetMode(stream_mode_read);
// open the other (new) file for writing and truncate it to zero size
#if POSIX
- hFileWrite = open(path.c_str(), O_RDWR | O_CREAT | O_TRUNC);
+ hFileWrite = open(path.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP);
if (hFileWrite < 0) {
hFileWrite = hFileRead;
throw Exception("Could not open file \"" + path + "\" for writing");
@@ -1439,18 +1471,31 @@
throw Exception("Could not open file \"" + path + "\" for writing");
}
#endif // POSIX
+ Mode = stream_mode_read_write;
// write complete RIFF tree to the other (new) file
- WriteChunk(0, 0);
+ unsigned long ulTotalSize = WriteChunk(0, 0);
+ unsigned long ulActualSize = __GetFileSize(hFileWrite);
+
+ // resize file to the final size (if the file was originally larger)
+ if (ulTotalSize < ulActualSize) ResizeFile(ulTotalSize);
// forget all resized chunks
ResizedChunks.clear();
+ if (Filename.length() > 0) {
+ #if POSIX
+ close(hFileWrite);
+ #else
+ fclose(hFileWrite);
+ #endif
+ hFileWrite = hFileRead;
+ }
+
// associate new file with this File object from now on
Filename = path;
- stream_mode_t oldMode = Mode;
- Mode = (stream_mode_t) -1; // Just set it to an undefined mode ...
- SetMode(oldMode); // ... so SetMode() has to reopen the file handles.
+ Mode = (stream_mode_t) -1; // Just set it to an undefined mode ...
+ SetMode(stream_mode_read_write); // ... so SetMode() has to reopen the file handles.
}
void File::ResizeFile(unsigned long ulNewSize) {
@@ -1459,7 +1504,7 @@
throw Exception("Could not resize file \"" + Filename + "\"");
#else
# error Sorry, this version of libgig only supports POSIX systems yet.
- # error Reason: portable implementation of RIFF::File::ResizeFile() is missing!
+ # error Reason: portable implementation of RIFF::File::ResizeFile() is missing (yet)!
#endif
}
@@ -1479,19 +1524,25 @@
}
unsigned long File::GetFileSize() {
- #if POSIX
+ return __GetFileSize(hFileRead);
+ }
+
+ #if POSIX
+ unsigned long File::__GetFileSize(int hFile) {
struct stat filestat;
- fstat(hFileRead, &filestat);
+ fstat(hFile, &filestat);
long size = filestat.st_size;
- #else // standard C functions
- long curpos = ftell(hFileRead);
- fseek(hFileRead, 0, SEEK_END);
- long size = ftell(hFileRead);
- fseek(hFileRead, curpos, SEEK_SET);
- #endif // POSIX
return size;
}
-
+ #else // standard C functions
+ unsigned long File::__GetFileSize(FILE* hFile) {
+ long curpos = ftell(hFile);
+ fseek(hFile, 0, SEEK_END);
+ long size = ftell(hFile);
+ fseek(hFile, curpos, SEEK_SET);
+ return size;
+ }
+ #endif
// *************** Exception ***************