--- libgig/trunk/src/RIFF.cpp 2006/02/05 17:30:13 833 +++ libgig/trunk/src/RIFF.cpp 2007/03/18 19:32:42 1105 @@ -1,8 +1,8 @@ /*************************************************************************** * * - * libgig - C++ cross-platform Gigasampler format file loader library * + * libgig - C++ cross-platform Gigasampler format file access library * * * - * Copyright (C) 2003-2005 by Christian Schoenebeck * + * Copyright (C) 2003-2007 by Christian Schoenebeck * * * * * * This library is free software; you can redistribute it and/or modify * @@ -29,6 +29,25 @@ namespace RIFF { +// *************** Internal functions ************** +// * + + /// Returns a human readable path of the given chunk. + String __resolveChunkPath(Chunk* pCk) { + String sPath; + for (Chunk* pChunk = pCk; pChunk; pChunk = pChunk->GetParent()) { + if (pChunk->GetChunkID() == CHUNK_ID_LIST) { + List* pList = (List*) pChunk; + sPath = "->'" + pList->GetListTypeString() + "'" + sPath; + } else { + sPath = "->'" + pChunk->GetChunkIDString() + "'" + sPath; + } + } + return sPath; + } + + + // *************** Chunk ************** // * @@ -70,6 +89,7 @@ } Chunk::~Chunk() { + if (CurrentChunkSize != NewChunkSize) pFile->UnlogResized(this); if (pChunkData) delete[] pChunkData; } @@ -81,6 +101,11 @@ if (lseek(pFile->hFileRead, fPos, SEEK_SET) != -1) { read(pFile->hFileRead, &ChunkID, 4); read(pFile->hFileRead, &CurrentChunkSize, 4); + #elif defined(WIN32) + if (SetFilePointer(pFile->hFileRead, fPos, NULL/*32 bit*/, FILE_BEGIN) != INVALID_SET_FILE_POINTER) { + DWORD dwBytesRead; + ReadFile(pFile->hFileRead, &ChunkID, 4, &dwBytesRead, NULL); + ReadFile(pFile->hFileRead, &CurrentChunkSize, 4, &dwBytesRead, NULL); #else if (!fseek(pFile->hFileRead, fPos, SEEK_SET)) { fread(&ChunkID, 4, 1, pFile->hFileRead); @@ -129,6 +154,12 @@ write(pFile->hFileWrite, &uiNewChunkID, 4); write(pFile->hFileWrite, &uiNewChunkSize, 4); } + #elif defined(WIN32) + if (SetFilePointer(pFile->hFileWrite, fPos, NULL/*32 bit*/, FILE_BEGIN) != INVALID_SET_FILE_POINTER) { + DWORD dwBytesWritten; + WriteFile(pFile->hFileWrite, &uiNewChunkID, 4, &dwBytesWritten, NULL); + WriteFile(pFile->hFileWrite, &uiNewChunkSize, 4, &dwBytesWritten, NULL); + } #else if (!fseek(pFile->hFileWrite, fPos, SEEK_SET)) { fwrite(&uiNewChunkID, 4, 1, pFile->hFileWrite); @@ -212,7 +243,10 @@ std::cout << "Chunk::GetState()" << std::endl; #endif // DEBUG #if POSIX - if (pFile->hFileRead == 0) return stream_closed; + if (pFile->hFileRead == 0) return stream_closed; + #elif defined (WIN32) + if (pFile->hFileRead == INVALID_HANDLE_VALUE) + return stream_closed; #else if (pFile->hFileRead == NULL) return stream_closed; #endif // POSIX @@ -246,6 +280,12 @@ unsigned long readWords = read(pFile->hFileRead, pData, WordCount * WordSize); if (readWords < 1) return 0; readWords /= WordSize; + #elif defined(WIN32) + if (SetFilePointer(pFile->hFileRead, ulStartPos + ulPos, NULL/*32 bit*/, FILE_BEGIN) == INVALID_SET_FILE_POINTER) return 0; + DWORD readWords; + ReadFile(pFile->hFileRead, pData, WordCount * WordSize, &readWords, NULL); + if (readWords < 1) return 0; + readWords /= WordSize; #else // standard C functions if (fseek(pFile->hFileRead, ulStartPos + ulPos, SEEK_SET)) return 0; unsigned long readWords = fread(pData, WordSize, WordCount, pFile->hFileRead); @@ -315,6 +355,15 @@ unsigned long writtenWords = write(pFile->hFileWrite, pData, WordCount * WordSize); if (writtenWords < 1) throw Exception("POSIX IO Error while trying to write chunk data"); writtenWords /= WordSize; + #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)"); + } + DWORD writtenWords; + WriteFile(pFile->hFileWrite, pData, WordCount * WordSize, &writtenWords, NULL); + if (writtenWords < 1) throw Exception("Windows IO Error while trying to write chunk data"); + writtenWords /= WordSize; #else // standard C functions if (fseek(pFile->hFileWrite, ulStartPos + ulPos, SEEK_SET)) { throw Exception("Could not seek to position " + ToString(ulPos) + @@ -679,6 +728,8 @@ if (!pChunkData && pFile->Filename != "") { #if POSIX if (lseek(pFile->hFileRead, ulStartPos, SEEK_SET) == -1) return NULL; + #elif defined(WIN32) + if (SetFilePointer(pFile->hFileRead, ulStartPos, NULL/*32 bit*/, FILE_BEGIN) == INVALID_SET_FILE_POINTER) return NULL; #else if (fseek(pFile->hFileRead, ulStartPos, SEEK_SET)) return NULL; #endif // POSIX @@ -688,6 +739,9 @@ memset(pChunkData, 0, ulBufferSize); #if POSIX unsigned long readWords = read(pFile->hFileRead, pChunkData, GetSize()); + #elif defined(WIN32) + DWORD readWords; + ReadFile(pFile->hFileRead, pChunkData, GetSize(), &readWords, NULL); #else unsigned long readWords = fread(pChunkData, 1, GetSize(), pFile->hFileRead); #endif // POSIX @@ -740,7 +794,8 @@ * @see File::Save() */ void Chunk::Resize(int iNewSize) { - if (iNewSize <= 0) throw Exception("Chunk size must be at least one byte"); + if (iNewSize <= 0) + throw Exception("There is at least one empty chunk (zero size): " + __resolveChunkPath(this)); if (NewChunkSize == iNewSize) return; NewChunkSize = iNewSize; pFile->LogAsResized(this); @@ -775,6 +830,13 @@ if (write(pFile->hFileWrite, pChunkData, NewChunkSize) != NewChunkSize) { throw Exception("Writing Chunk data (from RAM) failed"); } + #elif defined(WIN32) + SetFilePointer(pFile->hFileWrite, ulWritePos, NULL/*32 bit*/, FILE_BEGIN); + DWORD dwBytesWritten; + WriteFile(pFile->hFileWrite, pChunkData, NewChunkSize, &dwBytesWritten, NULL); + if (dwBytesWritten != NewChunkSize) { + throw Exception("Writing Chunk data (from RAM) failed"); + } #else fseek(pFile->hFileWrite, ulWritePos, SEEK_SET); if (fwrite(pChunkData, 1, NewChunkSize, pFile->hFileWrite) != NewChunkSize) { @@ -785,7 +847,11 @@ // move chunk data from the end of the file to the appropriate position int8_t* pCopyBuffer = new int8_t[4096]; unsigned long ulToMove = (NewChunkSize < CurrentChunkSize) ? NewChunkSize : CurrentChunkSize; + #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; + #endif for (unsigned long ulOffset = 0; iBytesMoved > 0; ulOffset += iBytesMoved, ulToMove -= iBytesMoved) { iBytesMoved = (ulToMove < 4096) ? ulToMove : 4096; #if POSIX @@ -793,6 +859,11 @@ iBytesMoved = read(pFile->hFileRead, pCopyBuffer, iBytesMoved); lseek(pFile->hFileWrite, ulWritePos + ulOffset, SEEK_SET); iBytesMoved = write(pFile->hFileWrite, pCopyBuffer, iBytesMoved); + #elif defined(WIN32) + SetFilePointer(pFile->hFileRead, ulStartPos + ulCurrentDataOffset + ulOffset, NULL/*32 bit*/, FILE_BEGIN); + ReadFile(pFile->hFileRead, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL); + SetFilePointer(pFile->hFileWrite, ulWritePos + ulOffset, NULL/*32 bit*/, FILE_BEGIN); + WriteFile(pFile->hFileWrite, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL); #else fseek(pFile->hFileRead, ulStartPos + ulCurrentDataOffset + ulOffset, SEEK_SET); iBytesMoved = fread(pCopyBuffer, 1, iBytesMoved, pFile->hFileRead); @@ -818,6 +889,10 @@ #if POSIX lseek(pFile->hFileWrite, ulStartPos + NewChunkSize, SEEK_SET); write(pFile->hFileWrite, &cPadByte, 1); + #elif defined(WIN32) + SetFilePointer(pFile->hFileWrite, ulStartPos + NewChunkSize, NULL/*32 bit*/, FILE_BEGIN); + DWORD dwBytesWritten; + WriteFile(pFile->hFileWrite, &cPadByte, 1, &dwBytesWritten, NULL); #else fseek(pFile->hFileWrite, ulStartPos + NewChunkSize, SEEK_SET); fwrite(&cPadByte, 1, 1, pFile->hFileWrite); @@ -1080,6 +1155,24 @@ return pNewChunk; } + /** @brief Moves a sub chunk. + * + * Moves a sub chunk from one position in a list to another + * position in the same list. The pSrc chunk is placed before the + * pDst chunk. + * + * @param pSrc - sub chunk to be moved + * @param pDst - the position to move to. pSrc will be placed + * before pDst. If pDst is 0, pSrc will be placed + * last in list. + */ + void List::MoveSubChunk(Chunk* pSrc, Chunk* pDst) { + if (!pSubChunks) LoadSubChunks(); + pSubChunks->remove(pSrc); + ChunkList::iterator iter = find(pSubChunks->begin(), pSubChunks->end(), pDst); + pSubChunks->insert(iter, pSrc); + } + /** @brief Creates a new list sub chunk. * * Creates and adds a new list sub chunk to this list chunk. Note that @@ -1101,8 +1194,9 @@ * * Removes the sub chunk given by \a pSubChunk from this list and frees * it completely from RAM. The given chunk can either be a normal sub - * chunk or a list sub chunk. You should call File::Save() to make this - * change persistent at any time. + * chunk or a list sub chunk. In case the given chunk is a list chunk, + * all its subchunks (if any) will be removed recursively as well. You + * should call File::Save() to make this change persistent at any time. * * @param pSubChunk - sub chunk or sub list chunk to be removed */ @@ -1133,6 +1227,10 @@ #if POSIX lseek(pFile->hFileRead, fPos + CHUNK_HEADER_SIZE, SEEK_SET); read(pFile->hFileRead, &ListType, 4); + #elif defined(WIN32) + SetFilePointer(pFile->hFileRead, fPos + CHUNK_HEADER_SIZE, NULL/*32 bit*/, FILE_BEGIN); + DWORD dwBytesRead; + ReadFile(pFile->hFileRead, &ListType, 4, &dwBytesRead, NULL); #else fseek(pFile->hFileRead, fPos + CHUNK_HEADER_SIZE, SEEK_SET); fread(&ListType, 4, 1, pFile->hFileRead); @@ -1153,6 +1251,10 @@ #if POSIX lseek(pFile->hFileWrite, fPos + CHUNK_HEADER_SIZE, SEEK_SET); write(pFile->hFileWrite, &ListType, 4); + #elif defined(WIN32) + SetFilePointer(pFile->hFileWrite, fPos + CHUNK_HEADER_SIZE, NULL/*32 bit*/, FILE_BEGIN); + DWORD dwBytesWritten; + WriteFile(pFile->hFileWrite, &ListType, 4, &dwBytesWritten, NULL); #else fseek(pFile->hFileWrite, fPos + CHUNK_HEADER_SIZE, SEEK_SET); fwrite(&ListType, 4, 1, pFile->hFileWrite); @@ -1266,7 +1368,11 @@ * @see AddSubChunk(), AddSubList() */ File::File(uint32_t FileType) : List(this) { + #if defined(WIN32) + hFileRead = hFileWrite = INVALID_HANDLE_VALUE; + #else hFileRead = hFileWrite = 0; + #endif Mode = stream_mode_closed; bEndianNative = true; ulStartPos = RIFF_HEADER_SIZE; @@ -1292,9 +1398,20 @@ 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, 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 (!hFile) throw RIFF::Exception("Can't open \"" + path + "\""); + if (!hFileRead) throw RIFF::Exception("Can't open \"" + path + "\""); #endif // POSIX Mode = stream_mode_read; ulStartPos = RIFF_HEADER_SIZE; @@ -1333,9 +1450,21 @@ hFileRead = hFileWrite = 0; throw Exception("Could not (re)open file \"" + Filename + "\" in read mode"); } + #elif defined(WIN32) + if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead); + hFileRead = hFileWrite = CreateFile( + Filename.c_str(), GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, NULL + ); + if (hFileRead == INVALID_HANDLE_VALUE) { + hFileRead = hFileWrite = INVALID_HANDLE_VALUE; + throw Exception("Could not (re)open file \"" + Filename + "\" in read mode"); + } #else if (hFileRead) fclose(hFileRead); - hFileRead = hFileWrite = fopen(path.c_str(), "rb"); + hFileRead = hFileWrite = fopen(Filename.c_str(), "rb"); if (!hFileRead) throw Exception("Could not (re)open file \"" + Filename + "\" in read mode"); #endif __resetPos(); // reset read/write position of ALL 'Chunk' objects @@ -1348,11 +1477,29 @@ hFileRead = hFileWrite = open(Filename.c_str(), O_RDONLY | O_NONBLOCK); throw Exception("Could not open file \"" + Filename + "\" in read+write mode"); } + #elif defined(WIN32) + if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead); + hFileRead = hFileWrite = CreateFile( + Filename.c_str(), + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ, + NULL, OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, 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 + ); + throw Exception("Could not (re)open file \"" + Filename + "\" in read mode"); + } #else if (hFileRead) fclose(hFileRead); - hFileRead = hFileWrite = fopen(path.c_str(), "r+b"); + hFileRead = hFileWrite = fopen(Filename.c_str(), "r+b"); if (!hFileRead) { - hFileRead = hFileWrite = fopen(path.c_str(), "rb"); + hFileRead = hFileWrite = fopen(Filename.c_str(), "rb"); throw Exception("Could not open file \"" + Filename + "\" in read+write mode"); } #endif @@ -1362,6 +1509,9 @@ #if POSIX if (hFileRead) close(hFileRead); if (hFileWrite) close(hFileWrite); + #elif defined(WIN32) + if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead); + if (hFileWrite != INVALID_HANDLE_VALUE) CloseHandle(hFileWrite); #else if (hFileRead) fclose(hFileRead); if (hFileWrite) fclose(hFileWrite); @@ -1404,7 +1554,9 @@ // 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) { - if ((*iter)->GetNewSize() == 0) throw Exception("There is at least one empty chunk (zero size)"); + 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; @@ -1421,7 +1573,11 @@ // ... and move current data by the same amount towards end of file. int8_t* pCopyBuffer = new int8_t[4096]; const unsigned long ulFileSize = GetSize() + RIFF_HEADER_SIZE; - int iBytesMoved = 1; + #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; + #endif for (unsigned long ulPos = 0; iBytesMoved > 0; ulPos += iBytesMoved) { const unsigned long ulToMove = ulFileSize - ulPos; iBytesMoved = (ulToMove < 4096) ? ulToMove : 4096; @@ -1430,6 +1586,11 @@ iBytesMoved = read(hFileRead, pCopyBuffer, iBytesMoved); lseek(hFileWrite, ulPos + ulPositiveSizeDiff, SEEK_SET); iBytesMoved = write(hFileWrite, pCopyBuffer, iBytesMoved); + #elif defined(WIN32) + SetFilePointer(hFileRead, ulPos, NULL/*32 bit*/, FILE_BEGIN); + ReadFile(hFileRead, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL); + SetFilePointer(hFileWrite, ulPos + ulPositiveSizeDiff, NULL/*32 bit*/, FILE_BEGIN); + WriteFile(hFileWrite, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL); #else fseek(hFileRead, ulPos, SEEK_SET); iBytesMoved = fread(pCopyBuffer, 1, iBytesMoved, hFileRead); @@ -1479,6 +1640,15 @@ hFileWrite = hFileRead; throw Exception("Could not open file \"" + path + "\" for writing"); } + #elif defined(WIN32) + hFileWrite = CreateFile( + path.c_str(), GENERIC_WRITE, FILE_SHARE_READ, + NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL + ); + if (hFileWrite == INVALID_HANDLE_VALUE) { + hFileWrite = hFileRead; + throw Exception("Could not open file \"" + path + "\" for writing"); + } #else hFileWrite = fopen(path.c_str(), "w+b"); if (!hFileWrite) { @@ -1501,6 +1671,8 @@ if (Filename.length() > 0) { #if POSIX close(hFileWrite); + #elif defined(WIN32) + CloseHandle(hFileWrite); #else fclose(hFileWrite); #endif @@ -1517,8 +1689,13 @@ #if POSIX if (ftruncate(hFileWrite, ulNewSize) < 0) throw Exception("Could not resize file \"" + Filename + "\""); + #elif defined(WIN32) + if ( + SetFilePointer(hFileWrite, ulNewSize, NULL/*32 bit*/, FILE_BEGIN) == INVALID_SET_FILE_POINTER || + !SetEndOfFile(hFileWrite) + ) throw Exception("Could not resize file \"" + Filename + "\""); #else - # error Sorry, this version of libgig only supports POSIX systems yet. + # error Sorry, this version of libgig only supports POSIX and Windows systems yet. # error Reason: portable implementation of RIFF::File::ResizeFile() is missing (yet)! #endif } @@ -1529,6 +1706,8 @@ #endif // DEBUG #if POSIX if (hFileRead) close(hFileRead); + #elif defined(WIN32) + if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead); #else if (hFileRead) fclose(hFileRead); #endif // POSIX @@ -1538,6 +1717,10 @@ ResizedChunks.push_back(pResizedChunk); } + void File::UnlogResized(Chunk* pResizedChunk) { + ResizedChunks.remove(pResizedChunk); + } + unsigned long File::GetFileSize() { return __GetFileSize(hFileRead); } @@ -1549,6 +1732,13 @@ long size = filestat.st_size; return size; } + #elif defined(WIN32) + unsigned long File::__GetFileSize(HANDLE hFile) { + DWORD dwSize = ::GetFileSize(hFile, NULL /*32bit*/); + if (dwSize == INVALID_FILE_SIZE) + throw Exception("Windows FS error: could not determine file size"); + return dwSize; + } #else // standard C functions unsigned long File::__GetFileSize(FILE* hFile) { long curpos = ftell(hFile);