--- libgig/trunk/src/RIFF.cpp 2007/08/25 09:59:53 1301 +++ libgig/trunk/src/RIFF.cpp 2013/11/25 02:22:38 2482 @@ -2,7 +2,7 @@ * * * libgig - C++ cross-platform Gigasampler format file access library * * * - * Copyright (C) 2003-2007 by Christian Schoenebeck * + * Copyright (C) 2003-2013 by Christian Schoenebeck * * * * * * This library is free software; you can redistribute it and/or modify * @@ -21,6 +21,8 @@ * MA 02111-1307 USA * ***************************************************************************/ +#include +#include #include #include "RIFF.h" @@ -33,7 +35,7 @@ // * /// Returns a human readable path of the given chunk. - String __resolveChunkPath(Chunk* pCk) { + static String __resolveChunkPath(Chunk* pCk) { String sPath; for (Chunk* pChunk = pCk; pChunk; pChunk = pChunk->GetParent()) { if (pChunk->GetChunkID() == CHUNK_ID_LIST) { @@ -58,6 +60,8 @@ ulPos = 0; pParent = NULL; pChunkData = NULL; + CurrentChunkSize = 0; + NewChunkSize = 0; ulChunkDataSize = 0; ChunkID = CHUNK_ID_RIFF; this->pFile = pFile; @@ -72,6 +76,8 @@ pParent = Parent; ulPos = 0; pChunkData = NULL; + CurrentChunkSize = 0; + NewChunkSize = 0; ulChunkDataSize = 0; ReadHeader(StartPos); } @@ -82,14 +88,14 @@ this->pParent = pParent; ulPos = 0; pChunkData = NULL; - ulChunkDataSize = 0; ChunkID = uiChunkID; + ulChunkDataSize = 0; CurrentChunkSize = 0; NewChunkSize = uiBodySize; } Chunk::~Chunk() { - if (CurrentChunkSize != NewChunkSize) pFile->UnlogResized(this); + if (pFile) pFile->UnlogResized(this); if (pChunkData) delete[] pChunkData; } @@ -97,6 +103,8 @@ #if DEBUG std::cout << "Chunk::Readheader(" << fPos << ") "; #endif // DEBUG + ChunkID = 0; + NewChunkSize = CurrentChunkSize = 0; #if POSIX if (lseek(pFile->hFileRead, fPos, SEEK_SET) != -1) { read(pFile->hFileRead, &ChunkID, 4); @@ -127,8 +135,8 @@ } #if DEBUG std::cout << "ckID=" << convertToString(ChunkID) << " "; - std::cout << "ckSize=" << ChunkSize << " "; - std::cout << "bEndianNative=" << bEndianNative << std::endl; + std::cout << "ckSize=" << CurrentChunkSize << " "; + std::cout << "bEndianNative=" << pFile->bEndianNative << std::endl; #endif // DEBUG NewChunkSize = CurrentChunkSize; } @@ -224,7 +232,7 @@ #if DEBUG std::cout << "Chunk::Remainingbytes()=" << CurrentChunkSize - ulPos << std::endl; #endif // DEBUG - return CurrentChunkSize - ulPos; + return (CurrentChunkSize > ulPos) ? CurrentChunkSize - ulPos : 0; } /** @@ -235,7 +243,7 @@ * - RIFF::stream_closed : * the data stream was closed somehow, no more reading possible * - RIFF::stream_end_reached : - * alreaady reached the end of the chunk data, no more reading + * already reached the end of the chunk data, no more reading * possible without SetPos() */ stream_state_t Chunk::GetState() { @@ -273,6 +281,7 @@ #if DEBUG std::cout << "Chunk::Read(void*,ulong,ulong)" << std::endl; #endif // DEBUG + if (ulStartPos == 0) return 0; // is only 0 if this is a new chunk, so nothing to read (yet) if (ulPos >= CurrentChunkSize) return 0; if (ulPos + WordCount * WordSize >= CurrentChunkSize) WordCount = (CurrentChunkSize - ulPos) / WordSize; #if POSIX @@ -358,7 +367,7 @@ #elif defined(WIN32) if (SetFilePointer(pFile->hFileWrite, ulStartPos + ulPos, NULL/*32 bit*/, FILE_BEGIN) == INVALID_SET_FILE_POINTER) { throw Exception("Could not seek to position " + ToString(ulPos) + - " in chunk (" + ToString(ulStartPos + ulPos) + " in file)"); + " in chunk (" + ToString(ulStartPos + ulPos) + " in file)"); } DWORD writtenWords; WriteFile(pFile->hFileWrite, pData, WordCount * WordSize, &writtenWords, NULL); @@ -586,6 +595,23 @@ } /** + * Reads a null-padded string of size characters and copies it + * into the string \a s. The position within the chunk will + * automatically be incremented. + * + * @param s destination string + * @param size number of characters to read + * @throws RIFF::Exception if an error occured or less than + * \a size characters could be read! + */ + void Chunk::ReadString(String& s, int size) { + char* buf = new char[size]; + ReadSceptical(buf, 1, size); + s.assign(buf, std::find(buf, buf + size, '\0')); + delete[] buf; + } + + /** * Writes \a WordCount number of 32 Bit unsigned integer words from the * buffer pointed by \a pData to the chunk's body, directly to the * actual "physical" file. The position within the chunk will @@ -725,7 +751,7 @@ * @see ReleaseChunkData() */ void* Chunk::LoadChunkData() { - if (!pChunkData && pFile->Filename != "") { + if (!pChunkData && pFile->Filename != "" && ulStartPos != 0) { #if POSIX if (lseek(pFile->hFileRead, ulStartPos, SEEK_SET) == -1) return NULL; #elif defined(WIN32) @@ -852,7 +878,7 @@ #else int iBytesMoved = 1; #endif - for (unsigned long ulOffset = 0; iBytesMoved > 0; ulOffset += iBytesMoved, ulToMove -= iBytesMoved) { + for (unsigned long ulOffset = 0; ulToMove > 0 && iBytesMoved > 0; ulOffset += iBytesMoved, ulToMove -= iBytesMoved) { iBytesMoved = (ulToMove < 4096) ? ulToMove : 4096; #if POSIX lseek(pFile->hFileRead, ulStartPos + ulCurrentDataOffset + ulOffset, SEEK_SET); @@ -942,6 +968,10 @@ #if DEBUG std::cout << "List::~List()" << std::endl; #endif // DEBUG + DeleteChunkList(); + } + + void List::DeleteChunkList() { if (pSubChunks) { ChunkList::iterator iter = pSubChunks->begin(); ChunkList::iterator end = pSubChunks->end(); @@ -950,8 +980,12 @@ iter++; } delete pSubChunks; + pSubChunks = NULL; + } + if (pSubChunksMap) { + delete pSubChunksMap; + pSubChunksMap = NULL; } - if (pSubChunksMap) delete pSubChunksMap; } /** @@ -1152,6 +1186,8 @@ pSubChunks->push_back(pNewChunk); (*pSubChunksMap)[uiChunkID] = pNewChunk; pNewChunk->Resize(uiBodySize); + NewChunkSize += CHUNK_HEADER_SIZE; + pFile->LogAsResized(this); return pNewChunk; } @@ -1187,6 +1223,8 @@ List* pNewListChunk = new List(pFile, this, uiListType); pSubChunks->push_back(pNewListChunk); (*pSubChunksMap)[CHUNK_ID_LIST] = pNewListChunk; + NewChunkSize += LIST_HEADER_SIZE; + pFile->LogAsResized(this); return pNewListChunk; } @@ -1223,6 +1261,7 @@ std::cout << "List::Readheader(ulong) "; #endif // DEBUG Chunk::ReadHeader(fPos); + if (CurrentChunkSize < 4) return; NewChunkSize = CurrentChunkSize -= 4; #if POSIX lseek(pFile->hFileRead, fPos + CHUNK_HEADER_SIZE, SEEK_SET); @@ -1362,6 +1401,10 @@ // *************** File *************** // * +//HACK: to avoid breaking DLL compatibility to older versions of libgig we roll the new std::set into the old std::list container, should be replaced on member variable level soon though +#define _GET_RESIZED_CHUNKS() \ + (reinterpret_cast*>(ResizedChunks.front())) + /** @brief Create new RIFF file. * * Use this constructor if you want to create a new RIFF file completely @@ -1376,7 +1419,9 @@ * @param FileType - four-byte identifier of the RIFF file type * @see AddSubChunk(), AddSubList(), SetByteOrder() */ - File::File(uint32_t FileType) : List(this) { + File::File(uint32_t FileType) : List(this), bIsNewFile(true) { + //HACK: see _GET_RESIZED_CHUNKS() comment + ResizedChunks.push_back(reinterpret_cast(new std::set)); #if defined(WIN32) hFileRead = hFileWrite = INVALID_HANDLE_VALUE; #else @@ -1396,43 +1441,56 @@ * @throws RIFF::Exception if error occured while trying to load the * given RIFF file */ - File::File(const String& path) : List(this), Filename(path) { - #if DEBUG - std::cout << "File::File("<(new std::set)); + #if POSIX + hFileRead = hFileWrite = open(path.c_str(), O_RDONLY | O_NONBLOCK); + if (hFileRead <= 0) { + hFileRead = hFileWrite = 0; + throw RIFF::Exception("Can't open \"" + path + "\""); + } + #elif defined(WIN32) + hFileRead = hFileWrite = CreateFile( + path.c_str(), GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | + FILE_FLAG_RANDOM_ACCESS, NULL + ); + if (hFileRead == INVALID_HANDLE_VALUE) { + hFileRead = hFileWrite = INVALID_HANDLE_VALUE; + throw RIFF::Exception("Can't open \"" + path + "\""); + } + #else + hFileRead = hFileWrite = fopen(path.c_str(), "rb"); + if (!hFileRead) throw RIFF::Exception("Can't open \"" + path + "\""); + #endif // POSIX + Mode = stream_mode_read; + ulStartPos = RIFF_HEADER_SIZE; + ReadHeader(0); + if (ChunkID != CHUNK_ID_RIFF && ChunkID != CHUNK_ID_RIFX) { + throw RIFF::Exception("Not a RIFF file"); + } } - #else - hFileRead = hFileWrite = fopen(path.c_str(), "rb"); - if (!hFileRead) throw RIFF::Exception("Can't open \"" + path + "\""); - #endif // POSIX - Mode = stream_mode_read; - ulStartPos = RIFF_HEADER_SIZE; - ReadHeader(0); - if (ChunkID != CHUNK_ID_RIFF) { - throw RIFF::Exception("Not a RIFF file"); + catch (...) { + Cleanup(); + throw; } } String File::GetFileName() { return Filename; } + + void File::SetFileName(const String& path) { + Filename = path; + } stream_mode_t File::GetMode() { return Mode; @@ -1465,7 +1523,9 @@ Filename.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, NULL + FILE_ATTRIBUTE_NORMAL | + FILE_FLAG_RANDOM_ACCESS, + NULL ); if (hFileRead == INVALID_HANDLE_VALUE) { hFileRead = hFileWrite = INVALID_HANDLE_VALUE; @@ -1493,14 +1553,18 @@ GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, - FILE_ATTRIBUTE_NORMAL, NULL + FILE_ATTRIBUTE_NORMAL | + FILE_FLAG_RANDOM_ACCESS, + NULL ); if (hFileRead == INVALID_HANDLE_VALUE) { hFileRead = hFileWrite = CreateFile( Filename.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, NULL + FILE_ATTRIBUTE_NORMAL | + FILE_FLAG_RANDOM_ACCESS, + NULL ); throw Exception("Could not (re)open file \"" + Filename + "\" in read+write mode"); } @@ -1579,14 +1643,14 @@ // first we sum up all positive chunk size changes (and skip all negative ones) unsigned long ulPositiveSizeDiff = 0; - for (ChunkList::iterator iter = ResizedChunks.begin(), end = ResizedChunks.end(); iter != end; ++iter) { + std::set* resizedChunks = _GET_RESIZED_CHUNKS(); + for (std::set::const_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): " + __resolveChunkPath(*iter)); } - 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 newSizePadded = (*iter)->GetNewSize() + (*iter)->GetNewSize() % 2; + unsigned long oldSizePadded = (*iter)->GetSize() + (*iter)->GetSize() % 2; + if (newSizePadded > oldSizePadded) ulPositiveSizeDiff += newSizePadded - oldSizePadded; } unsigned long ulWorkingFileSize = GetFileSize(); @@ -1602,11 +1666,11 @@ #if defined(WIN32) DWORD iBytesMoved = 1; // we have to pass it via pointer to the Windows API, thus the correct size must be ensured #else - int iBytesMoved = 1; + int iBytesMoved = 1; #endif - for (unsigned long ulPos = 0; iBytesMoved > 0; ulPos += iBytesMoved) { - const unsigned long ulToMove = ulFileSize - ulPos; - iBytesMoved = (ulToMove < 4096) ? ulToMove : 4096; + for (unsigned long ulPos = ulFileSize; iBytesMoved > 0; ) { + iBytesMoved = (ulPos < 4096) ? ulPos : 4096; + ulPos -= iBytesMoved; #if POSIX lseek(hFileRead, ulPos, SEEK_SET); iBytesMoved = read(hFileRead, pCopyBuffer, iBytesMoved); @@ -1636,7 +1700,7 @@ if (ulTotalSize < ulActualSize) ResizeFile(ulTotalSize); // forget all resized chunks - ResizedChunks.clear(); + resizedChunks->clear(); } /** @brief Save changes to another file. @@ -1658,7 +1722,7 @@ // make sure the RIFF tree is built (from the original file) LoadSubChunksRecursively(); - if (Filename.length() > 0) SetMode(stream_mode_read); + if (!bIsNewFile) 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, S_IRUSR | S_IWUSR | S_IRGRP); @@ -1669,7 +1733,8 @@ #elif defined(WIN32) hFileWrite = CreateFile( path.c_str(), GENERIC_WRITE, FILE_SHARE_READ, - NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL + NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL | + FILE_FLAG_RANDOM_ACCESS, NULL ); if (hFileWrite == INVALID_HANDLE_VALUE) { hFileWrite = hFileRead; @@ -1692,7 +1757,7 @@ if (ulTotalSize < ulActualSize) ResizeFile(ulTotalSize); // forget all resized chunks - ResizedChunks.clear(); + _GET_RESIZED_CHUNKS()->clear(); #if POSIX if (hFileWrite) close(hFileWrite); @@ -1705,6 +1770,7 @@ // associate new file with this File object from now on Filename = path; + bIsNewFile = false; 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. } @@ -1728,6 +1794,18 @@ #if DEBUG std::cout << "File::~File()" << std::endl; #endif // DEBUG + Cleanup(); + } + + /** + * Returns @c true if this file has been created new from scratch and + * has not been stored to disk yet. + */ + bool File::IsNew() const { + return bIsNewFile; + } + + void File::Cleanup() { #if POSIX if (hFileRead) close(hFileRead); #elif defined(WIN32) @@ -1735,14 +1813,18 @@ #else if (hFileRead) fclose(hFileRead); #endif // POSIX + DeleteChunkList(); + pFile = NULL; + //HACK: see _GET_RESIZED_CHUNKS() comment + delete _GET_RESIZED_CHUNKS(); } void File::LogAsResized(Chunk* pResizedChunk) { - ResizedChunks.push_back(pResizedChunk); + _GET_RESIZED_CHUNKS()->insert(pResizedChunk); } void File::UnlogResized(Chunk* pResizedChunk) { - ResizedChunks.remove(pResizedChunk); + _GET_RESIZED_CHUNKS()->erase(pResizedChunk); } unsigned long File::GetFileSize() {