RIFF.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002  *                                                                         *
00003  *   libgig - C++ cross-platform Gigasampler format file access library    *
00004  *                                                                         *
00005  *   Copyright (C) 2003-2006 by Christian Schoenebeck                      *
00006  *                              <cuse@users.sourceforge.net>               *
00007  *                                                                         *
00008  *   This library is free software; you can redistribute it and/or modify  *
00009  *   it under the terms of the GNU General Public License as published by  *
00010  *   the Free Software Foundation; either version 2 of the License, or     *
00011  *   (at your option) any later version.                                   *
00012  *                                                                         *
00013  *   This library is distributed in the hope that it will be useful,       *
00014  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00015  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
00016  *   GNU General Public License for more details.                          *
00017  *                                                                         *
00018  *   You should have received a copy of the GNU General Public License     *
00019  *   along with this library; if not, write to the Free Software           *
00020  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston,                 *
00021  *   MA  02111-1307  USA                                                   *
00022  ***************************************************************************/
00023 
00024 #include <string.h>
00025 
00026 #include "RIFF.h"
00027 
00028 #include "helper.h"
00029 
00030 namespace RIFF {
00031 
00032 // *************** Chunk **************
00033 // *
00034 
00035     Chunk::Chunk(File* pFile) {
00036         #if DEBUG
00037         std::cout << "Chunk::Chunk(File* pFile)" << std::endl;
00038         #endif // DEBUG
00039         ulPos      = 0;
00040         pParent    = NULL;
00041         pChunkData = NULL;
00042         ulChunkDataSize = 0;
00043         ChunkID    = CHUNK_ID_RIFF;
00044         this->pFile = pFile;
00045     }
00046 
00047     Chunk::Chunk(File* pFile, unsigned long StartPos, List* Parent) {
00048         #if DEBUG
00049         std::cout << "Chunk::Chunk(File*,ulong,bool,List*),StartPos=" << StartPos << std::endl;
00050         #endif // DEBUG
00051         this->pFile   = pFile;
00052         ulStartPos    = StartPos + CHUNK_HEADER_SIZE;
00053         pParent       = Parent;
00054         ulPos         = 0;
00055         pChunkData    = NULL;
00056         ulChunkDataSize = 0;
00057         ReadHeader(StartPos);
00058     }
00059 
00060     Chunk::Chunk(File* pFile, List* pParent, uint32_t uiChunkID, uint uiBodySize) {
00061         this->pFile      = pFile;
00062         ulStartPos       = 0; // arbitrary usually, since it will be updated when we write the chunk
00063         this->pParent    = pParent;
00064         ulPos            = 0;
00065         pChunkData       = NULL;
00066         ulChunkDataSize  = 0;
00067         ChunkID          = uiChunkID;
00068         CurrentChunkSize = 0;
00069         NewChunkSize     = uiBodySize;
00070     }
00071 
00072     Chunk::~Chunk() {
00073         if (pChunkData) delete[] pChunkData;
00074     }
00075 
00076     void Chunk::ReadHeader(unsigned long fPos) {
00077         #if DEBUG
00078         std::cout << "Chunk::Readheader(" << fPos << ") ";
00079         #endif // DEBUG
00080         #if POSIX
00081         if (lseek(pFile->hFileRead, fPos, SEEK_SET) != -1) {
00082             read(pFile->hFileRead, &ChunkID, 4);
00083             read(pFile->hFileRead, &CurrentChunkSize, 4);
00084         #else
00085         if (!fseek(pFile->hFileRead, fPos, SEEK_SET)) {
00086             fread(&ChunkID, 4, 1, pFile->hFileRead);
00087             fread(&CurrentChunkSize, 4, 1, pFile->hFileRead);
00088         #endif // POSIX
00089             #if WORDS_BIGENDIAN
00090             if (ChunkID == CHUNK_ID_RIFF) {
00091                 pFile->bEndianNative = false;
00092             }
00093             #else // little endian
00094             if (ChunkID == CHUNK_ID_RIFX) {
00095                 pFile->bEndianNative = false;
00096                 ChunkID = CHUNK_ID_RIFF;
00097             }
00098             #endif // WORDS_BIGENDIAN
00099             if (!pFile->bEndianNative) {
00100                 //swapBytes_32(&ChunkID);
00101                 swapBytes_32(&CurrentChunkSize);
00102             }
00103             #if DEBUG
00104             std::cout << "ckID=" << convertToString(ChunkID) << " ";
00105             std::cout << "ckSize=" << ChunkSize << " ";
00106             std::cout << "bEndianNative=" << bEndianNative << std::endl;
00107             #endif // DEBUG
00108             NewChunkSize = CurrentChunkSize;
00109         }
00110     }
00111 
00112     void Chunk::WriteHeader(unsigned long fPos) {
00113         uint32_t uiNewChunkID = ChunkID;
00114         if (ChunkID == CHUNK_ID_RIFF) {
00115             #if WORDS_BIGENDIAN
00116             if (pFile->bEndianNative) uiNewChunkID = CHUNK_ID_RIFX;
00117             #else // little endian
00118             if (!pFile->bEndianNative) uiNewChunkID = CHUNK_ID_RIFX;
00119             #endif // WORDS_BIGENDIAN
00120         }
00121 
00122         uint32_t uiNewChunkSize = NewChunkSize;
00123         if (!pFile->bEndianNative) {
00124             swapBytes_32(&uiNewChunkSize);
00125         }
00126 
00127         #if POSIX
00128         if (lseek(pFile->hFileWrite, fPos, SEEK_SET) != -1) {
00129             write(pFile->hFileWrite, &uiNewChunkID, 4);
00130             write(pFile->hFileWrite, &uiNewChunkSize, 4);
00131         }
00132         #else
00133         if (!fseek(pFile->hFileWrite, fPos, SEEK_SET)) {
00134             fwrite(&uiNewChunkID, 4, 1, pFile->hFileWrite);
00135             fwrite(&uiNewChunkSize, 4, 1, pFile->hFileWrite);
00136         }
00137         #endif // POSIX
00138     }
00139 
00144     String Chunk::GetChunkIDString() {
00145         return convertToString(ChunkID);
00146     }
00147 
00160     unsigned long Chunk::SetPos(unsigned long Where, stream_whence_t Whence) {
00161      #if DEBUG
00162      std::cout << "Chunk::SetPos(ulong)" << std::endl;
00163      #endif // DEBUG
00164         switch (Whence) {
00165             case stream_curpos:
00166                 ulPos += Where;
00167                 break;
00168             case stream_end:
00169                 ulPos = CurrentChunkSize - 1 - Where;
00170                 break;
00171             case stream_backward:
00172                 ulPos -= Where;
00173                 break;
00174             case stream_start: default:
00175                 ulPos = Where;
00176                 break;
00177         }
00178         if (ulPos > CurrentChunkSize) ulPos = CurrentChunkSize;
00179         return ulPos;
00180     }
00181 
00192     unsigned long Chunk::RemainingBytes() {
00193        #if DEBUG
00194        std::cout << "Chunk::Remainingbytes()=" << CurrentChunkSize - ulPos << std::endl;
00195        #endif // DEBUG
00196         return CurrentChunkSize - ulPos;
00197     }
00198 
00210     stream_state_t Chunk::GetState() {
00211       #if DEBUG
00212       std::cout << "Chunk::GetState()" << std::endl;
00213       #endif // DEBUG
00214         #if POSIX
00215         if (pFile->hFileRead == 0)    return stream_closed;
00216         #else
00217         if (pFile->hFileRead == NULL) return stream_closed;
00218         #endif // POSIX
00219         if (ulPos < CurrentChunkSize) return stream_ready;
00220         else                          return stream_end_reached;
00221     }
00222 
00238     unsigned long Chunk::Read(void* pData, unsigned long WordCount, unsigned long WordSize) {
00239        #if DEBUG
00240        std::cout << "Chunk::Read(void*,ulong,ulong)" << std::endl;
00241        #endif // DEBUG
00242         if (ulPos >= CurrentChunkSize) return 0;
00243         if (ulPos + WordCount * WordSize >= CurrentChunkSize) WordCount = (CurrentChunkSize - ulPos) / WordSize;
00244         #if POSIX
00245         if (lseek(pFile->hFileRead, ulStartPos + ulPos, SEEK_SET) < 0) return 0;
00246         unsigned long readWords = read(pFile->hFileRead, pData, WordCount * WordSize);
00247         if (readWords < 1) return 0;
00248         readWords /= WordSize;
00249         #else // standard C functions
00250         if (fseek(pFile->hFileRead, ulStartPos + ulPos, SEEK_SET)) return 0;
00251         unsigned long readWords = fread(pData, WordSize, WordCount, pFile->hFileRead);
00252         #endif // POSIX
00253         if (!pFile->bEndianNative && WordSize != 1) {
00254             switch (WordSize) {
00255                 case 2:
00256                     for (unsigned long iWord = 0; iWord < readWords; iWord++)
00257                         swapBytes_16((uint16_t*) pData + iWord);
00258                     break;
00259                 case 4:
00260                     for (unsigned long iWord = 0; iWord < readWords; iWord++)
00261                         swapBytes_32((uint32_t*) pData + iWord);
00262                     break;
00263                 default:
00264                     for (unsigned long iWord = 0; iWord < readWords; iWord++)
00265                         swapBytes((uint8_t*) pData + iWord * WordSize, WordSize);
00266                     break;
00267             }
00268         }
00269         SetPos(readWords * WordSize, stream_curpos);
00270         return readWords;
00271     }
00272 
00289     unsigned long Chunk::Write(void* pData, unsigned long WordCount, unsigned long WordSize) {
00290         if (pFile->Mode != stream_mode_read_write)
00291             throw Exception("Cannot write data to chunk, file has to be opened in read+write mode first");
00292         if (ulPos >= CurrentChunkSize || ulPos + WordCount * WordSize > CurrentChunkSize)
00293             throw Exception("End of chunk reached while trying to write data");
00294         if (!pFile->bEndianNative && WordSize != 1) {
00295             switch (WordSize) {
00296                 case 2:
00297                     for (unsigned long iWord = 0; iWord < WordCount; iWord++)
00298                         swapBytes_16((uint16_t*) pData + iWord);
00299                     break;
00300                 case 4:
00301                     for (unsigned long iWord = 0; iWord < WordCount; iWord++)
00302                         swapBytes_32((uint32_t*) pData + iWord);
00303                     break;
00304                 default:
00305                     for (unsigned long iWord = 0; iWord < WordCount; iWord++)
00306                         swapBytes((uint8_t*) pData + iWord * WordSize, WordSize);
00307                     break;
00308             }
00309         }
00310         #if POSIX
00311         if (lseek(pFile->hFileWrite, ulStartPos + ulPos, SEEK_SET) < 0) {
00312             throw Exception("Could not seek to position " + ToString(ulPos) +
00313                             " in chunk (" + ToString(ulStartPos + ulPos) + " in file)");
00314         }
00315         unsigned long writtenWords = write(pFile->hFileWrite, pData, WordCount * WordSize);
00316         if (writtenWords < 1) throw Exception("POSIX IO Error while trying to write chunk data");
00317         writtenWords /= WordSize;
00318         #else // standard C functions
00319         if (fseek(pFile->hFileWrite, ulStartPos + ulPos, SEEK_SET)) {
00320             throw Exception("Could not seek to position " + ToString(ulPos) +
00321                             " in chunk (" + ToString(ulStartPos + ulPos) + " in file)");
00322         }
00323         unsigned long writtenWords = fwrite(pData, WordSize, WordCount, pFile->hFileWrite);
00324         #endif // POSIX
00325         SetPos(writtenWords * WordSize, stream_curpos);
00326         return writtenWords;
00327     }
00328 
00330     unsigned long Chunk::ReadSceptical(void* pData, unsigned long WordCount, unsigned long WordSize) {
00331         unsigned long readWords = Read(pData, WordCount, WordSize);
00332         if (readWords != WordCount) throw RIFF::Exception("End of chunk data reached.");
00333         return readWords;
00334     }
00335 
00347     unsigned long Chunk::ReadInt8(int8_t* pData, unsigned long WordCount) {
00348        #if DEBUG
00349        std::cout << "Chunk::ReadInt8(int8_t*,ulong)" << std::endl;
00350        #endif // DEBUG
00351         return ReadSceptical(pData, WordCount, 1);
00352     }
00353 
00368     unsigned long Chunk::WriteInt8(int8_t* pData, unsigned long WordCount) {
00369         return Write(pData, WordCount, 1);
00370     }
00371 
00384     unsigned long Chunk::ReadUint8(uint8_t* pData, unsigned long WordCount) {
00385        #if DEBUG
00386        std::cout << "Chunk::ReadUint8(uint8_t*,ulong)" << std::endl;
00387        #endif // DEBUG
00388         return ReadSceptical(pData, WordCount, 1);
00389     }
00390 
00405     unsigned long Chunk::WriteUint8(uint8_t* pData, unsigned long WordCount) {
00406         return Write(pData, WordCount, 1);
00407     }
00408 
00421     unsigned long Chunk::ReadInt16(int16_t* pData, unsigned long WordCount) {
00422       #if DEBUG
00423       std::cout << "Chunk::ReadInt16(int16_t*,ulong)" << std::endl;
00424       #endif // DEBUG
00425         return ReadSceptical(pData, WordCount, 2);
00426     }
00427 
00442     unsigned long Chunk::WriteInt16(int16_t* pData, unsigned long WordCount) {
00443         return Write(pData, WordCount, 2);
00444     }
00445 
00458     unsigned long Chunk::ReadUint16(uint16_t* pData, unsigned long WordCount) {
00459       #if DEBUG
00460       std::cout << "Chunk::ReadUint16(uint16_t*,ulong)" << std::endl;
00461       #endif // DEBUG
00462         return ReadSceptical(pData, WordCount, 2);
00463     }
00464 
00479     unsigned long Chunk::WriteUint16(uint16_t* pData, unsigned long WordCount) {
00480         return Write(pData, WordCount, 2);
00481     }
00482 
00495     unsigned long Chunk::ReadInt32(int32_t* pData, unsigned long WordCount) {
00496        #if DEBUG
00497        std::cout << "Chunk::ReadInt32(int32_t*,ulong)" << std::endl;
00498        #endif // DEBUG
00499         return ReadSceptical(pData, WordCount, 4);
00500     }
00501 
00516     unsigned long Chunk::WriteInt32(int32_t* pData, unsigned long WordCount) {
00517         return Write(pData, WordCount, 4);
00518     }
00519 
00532     unsigned long Chunk::ReadUint32(uint32_t* pData, unsigned long WordCount) {
00533        #if DEBUG
00534        std::cout << "Chunk::ReadUint32(uint32_t*,ulong)" << std::endl;
00535        #endif // DEBUG
00536         return ReadSceptical(pData, WordCount, 4);
00537     }
00538 
00553     unsigned long Chunk::WriteUint32(uint32_t* pData, unsigned long WordCount) {
00554         return Write(pData, WordCount, 4);
00555     }
00556 
00564     int8_t Chunk::ReadInt8() {
00565       #if DEBUG
00566       std::cout << "Chunk::ReadInt8()" << std::endl;
00567       #endif // DEBUG
00568         int8_t word;
00569         ReadSceptical(&word,1,1);
00570         return word;
00571     }
00572 
00580     uint8_t Chunk::ReadUint8() {
00581       #if DEBUG
00582       std::cout << "Chunk::ReadUint8()" << std::endl;
00583       #endif // DEBUG
00584         uint8_t word;
00585         ReadSceptical(&word,1,1);
00586         return word;
00587     }
00588 
00597     int16_t Chunk::ReadInt16() {
00598       #if DEBUG
00599       std::cout << "Chunk::ReadInt16()" << std::endl;
00600       #endif // DEBUG
00601         int16_t word;
00602         ReadSceptical(&word,1,2);
00603         return word;
00604     }
00605 
00614     uint16_t Chunk::ReadUint16() {
00615       #if DEBUG
00616       std::cout << "Chunk::ReadUint16()" << std::endl;
00617       #endif // DEBUG
00618         uint16_t word;
00619         ReadSceptical(&word,1,2);
00620         return word;
00621     }
00622 
00631     int32_t Chunk::ReadInt32() {
00632       #if DEBUG
00633       std::cout << "Chunk::ReadInt32()" << std::endl;
00634       #endif // DEBUG
00635         int32_t word;
00636         ReadSceptical(&word,1,4);
00637         return word;
00638     }
00639 
00648     uint32_t Chunk::ReadUint32() {
00649       #if DEBUG
00650       std::cout << "Chunk::ReadUint32()" << std::endl;
00651       #endif // DEBUG
00652         uint32_t word;
00653         ReadSceptical(&word,1,4);
00654         return word;
00655     }
00656 
00678     void* Chunk::LoadChunkData() {
00679         if (!pChunkData && pFile->Filename != "") {
00680             #if POSIX
00681             if (lseek(pFile->hFileRead, ulStartPos, SEEK_SET) == -1) return NULL;
00682             #else
00683             if (fseek(pFile->hFileRead, ulStartPos, SEEK_SET)) return NULL;
00684             #endif // POSIX
00685             unsigned long ulBufferSize = (CurrentChunkSize > NewChunkSize) ? CurrentChunkSize : NewChunkSize;
00686             pChunkData = new uint8_t[ulBufferSize];
00687             if (!pChunkData) return NULL;
00688             memset(pChunkData, 0, ulBufferSize);
00689             #if POSIX
00690             unsigned long readWords = read(pFile->hFileRead, pChunkData, GetSize());
00691             #else
00692             unsigned long readWords = fread(pChunkData, 1, GetSize(), pFile->hFileRead);
00693             #endif // POSIX
00694             if (readWords != GetSize()) {
00695                 delete[] pChunkData;
00696                 return (pChunkData = NULL);
00697             }
00698             ulChunkDataSize = ulBufferSize;
00699         } else if (NewChunkSize > ulChunkDataSize) {
00700             uint8_t* pNewBuffer = new uint8_t[NewChunkSize];
00701             if (!pNewBuffer) throw Exception("Could not enlarge chunk data buffer to " + ToString(NewChunkSize) + " bytes");
00702             memset(pNewBuffer, 0 , NewChunkSize);
00703             memcpy(pNewBuffer, pChunkData, ulChunkDataSize);
00704             delete[] pChunkData;
00705             pChunkData      = pNewBuffer;
00706             ulChunkDataSize = NewChunkSize;
00707         }
00708         return pChunkData;
00709     }
00710 
00717     void Chunk::ReleaseChunkData() {
00718         if (pChunkData) {
00719             delete[] pChunkData;
00720             pChunkData = NULL;
00721         }
00722     }
00723 
00742     void Chunk::Resize(int iNewSize) {
00743         if (iNewSize <= 0) throw Exception("Chunk size must be at least one byte");
00744         if (NewChunkSize == iNewSize) return;
00745         NewChunkSize = iNewSize;
00746         pFile->LogAsResized(this);
00747     }
00748 
00761     unsigned long Chunk::WriteChunk(unsigned long ulWritePos, unsigned long ulCurrentDataOffset) {
00762         const unsigned long ulOriginalPos = ulWritePos;
00763         ulWritePos += CHUNK_HEADER_SIZE;
00764 
00765         if (pFile->Mode != stream_mode_read_write)
00766             throw Exception("Cannot write list chunk, file has to be opened in read+write mode");
00767 
00768         // if the whole chunk body was loaded into RAM
00769         if (pChunkData) {
00770             // make sure chunk data buffer in RAM is at least as large as the new chunk size
00771             LoadChunkData();
00772             // write chunk data from RAM persistently to the file
00773             #if POSIX
00774             lseek(pFile->hFileWrite, ulWritePos, SEEK_SET);
00775             if (write(pFile->hFileWrite, pChunkData, NewChunkSize) != NewChunkSize) {
00776                 throw Exception("Writing Chunk data (from RAM) failed");
00777             }
00778             #else
00779             fseek(pFile->hFileWrite, ulWritePos, SEEK_SET);
00780             if (fwrite(pChunkData, 1, NewChunkSize, pFile->hFileWrite) != NewChunkSize) {
00781                 throw Exception("Writing Chunk data (from RAM) failed");
00782             }
00783             #endif // POSIX
00784         } else {
00785             // move chunk data from the end of the file to the appropriate position
00786             int8_t* pCopyBuffer = new int8_t[4096];
00787             unsigned long ulToMove = (NewChunkSize < CurrentChunkSize) ? NewChunkSize : CurrentChunkSize;
00788             int iBytesMoved = 1;
00789             for (unsigned long ulOffset = 0; iBytesMoved > 0; ulOffset += iBytesMoved, ulToMove -= iBytesMoved) {
00790                 iBytesMoved = (ulToMove < 4096) ? ulToMove : 4096;
00791                 #if POSIX
00792                 lseek(pFile->hFileRead, ulStartPos + ulCurrentDataOffset + ulOffset, SEEK_SET);
00793                 iBytesMoved = read(pFile->hFileRead, pCopyBuffer, iBytesMoved);
00794                 lseek(pFile->hFileWrite, ulWritePos + ulOffset, SEEK_SET);
00795                 iBytesMoved = write(pFile->hFileWrite, pCopyBuffer, iBytesMoved);
00796                 #else
00797                 fseek(pFile->hFileRead, ulStartPos + ulCurrentDataOffset + ulOffset, SEEK_SET);
00798                 iBytesMoved = fread(pCopyBuffer, 1, iBytesMoved, pFile->hFileRead);
00799                 fseek(pFile->hFileWrite, ulWritePos + ulOffset, SEEK_SET);
00800                 iBytesMoved = fwrite(pCopyBuffer, 1, iBytesMoved, pFile->hFileWrite);
00801                 #endif
00802             }
00803             delete[] pCopyBuffer;
00804             if (iBytesMoved < 0) throw Exception("Writing Chunk data (from file) failed");
00805         }
00806 
00807         // update this chunk's header
00808         CurrentChunkSize = NewChunkSize;
00809         WriteHeader(ulOriginalPos);
00810 
00811         // update chunk's position pointers
00812         ulStartPos = ulOriginalPos + CHUNK_HEADER_SIZE;
00813         ulPos      = 0;
00814 
00815         // add pad byte if needed
00816         if ((ulStartPos + NewChunkSize) % 2 != 0) {
00817             const char cPadByte = 0;
00818             #if POSIX
00819             lseek(pFile->hFileWrite, ulStartPos + NewChunkSize, SEEK_SET);
00820             write(pFile->hFileWrite, &cPadByte, 1);
00821             #else
00822             fseek(pFile->hFileWrite, ulStartPos + NewChunkSize, SEEK_SET);
00823             fwrite(&cPadByte, 1, 1, pFile->hFileWrite);
00824             #endif
00825             return ulStartPos + NewChunkSize + 1;
00826         }
00827 
00828         return ulStartPos + NewChunkSize;
00829     }
00830 
00831     void Chunk::__resetPos() {
00832         ulPos = 0;
00833     }
00834 
00835 
00836 
00837 // *************** List ***************
00838 // *
00839 
00840     List::List(File* pFile) : Chunk(pFile) {
00841       #if DEBUG
00842       std::cout << "List::List(File* pFile)" << std::endl;
00843       #endif // DEBUG
00844         pSubChunks    = NULL;
00845         pSubChunksMap = NULL;
00846     }
00847 
00848     List::List(File* pFile, unsigned long StartPos, List* Parent)
00849       : Chunk(pFile, StartPos, Parent) {
00850         #if DEBUG
00851         std::cout << "List::List(File*,ulong,bool,List*)" << std::endl;
00852         #endif // DEBUG
00853         pSubChunks    = NULL;
00854         pSubChunksMap = NULL;
00855         ReadHeader(StartPos);
00856         ulStartPos    = StartPos + LIST_HEADER_SIZE;
00857     }
00858 
00859     List::List(File* pFile, List* pParent, uint32_t uiListID)
00860       : Chunk(pFile, pParent, CHUNK_ID_LIST, 0) {
00861         pSubChunks    = NULL;
00862         pSubChunksMap = NULL;
00863         ListType      = uiListID;
00864     }
00865 
00866     List::~List() {
00867       #if DEBUG
00868       std::cout << "List::~List()" << std::endl;
00869       #endif // DEBUG
00870         if (pSubChunks) {
00871             ChunkList::iterator iter = pSubChunks->begin();
00872             ChunkList::iterator end  = pSubChunks->end();
00873             while (iter != end) {
00874                 delete *iter;
00875                 iter++;
00876             }
00877             delete pSubChunks;
00878         }
00879         if (pSubChunksMap) delete pSubChunksMap;
00880     }
00881 
00893     Chunk* List::GetSubChunk(uint32_t ChunkID) {
00894       #if DEBUG
00895       std::cout << "List::GetSubChunk(uint32_t)" << std::endl;
00896       #endif // DEBUG
00897         if (!pSubChunksMap) LoadSubChunks();
00898         return (*pSubChunksMap)[ChunkID];
00899     }
00900 
00912     List* List::GetSubList(uint32_t ListType) {
00913         #if DEBUG
00914         std::cout << "List::GetSubList(uint32_t)" << std::endl;
00915         #endif // DEBUG
00916         if (!pSubChunks) LoadSubChunks();
00917         ChunkList::iterator iter = pSubChunks->begin();
00918         ChunkList::iterator end  = pSubChunks->end();
00919         while (iter != end) {
00920             if ((*iter)->GetChunkID() == CHUNK_ID_LIST) {
00921                 List* l = (List*) *iter;
00922                 if (l->GetListType() == ListType) return l;
00923             }
00924             iter++;
00925         }
00926         return NULL;
00927     }
00928 
00937     Chunk* List::GetFirstSubChunk() {
00938         #if DEBUG
00939         std::cout << "List::GetFirstSubChunk()" << std::endl;
00940         #endif // DEBUG
00941         if (!pSubChunks) LoadSubChunks();
00942         ChunksIterator = pSubChunks->begin();
00943         return (ChunksIterator != pSubChunks->end()) ? *ChunksIterator : NULL;
00944     }
00945 
00953     Chunk* List::GetNextSubChunk() {
00954         #if DEBUG
00955         std::cout << "List::GetNextSubChunk()" << std::endl;
00956         #endif // DEBUG
00957         if (!pSubChunks) return NULL;
00958         ChunksIterator++;
00959         return (ChunksIterator != pSubChunks->end()) ? *ChunksIterator : NULL;
00960     }
00961 
00971     List* List::GetFirstSubList() {
00972         #if DEBUG
00973         std::cout << "List::GetFirstSubList()" << std::endl;
00974         #endif // DEBUG
00975         if (!pSubChunks) LoadSubChunks();
00976         ListIterator            = pSubChunks->begin();
00977         ChunkList::iterator end = pSubChunks->end();
00978         while (ListIterator != end) {
00979             if ((*ListIterator)->GetChunkID() == CHUNK_ID_LIST) return (List*) *ListIterator;
00980             ListIterator++;
00981         }
00982         return NULL;
00983     }
00984 
00993     List* List::GetNextSubList() {
00994         #if DEBUG
00995         std::cout << "List::GetNextSubList()" << std::endl;
00996         #endif // DEBUG
00997         if (!pSubChunks) return NULL;
00998         if (ListIterator == pSubChunks->end()) return NULL;
00999         ListIterator++;
01000         ChunkList::iterator end = pSubChunks->end();
01001         while (ListIterator != end) {
01002             if ((*ListIterator)->GetChunkID() == CHUNK_ID_LIST) return (List*) *ListIterator;
01003             ListIterator++;
01004         }
01005         return NULL;
01006     }
01007 
01011     unsigned int List::CountSubChunks() {
01012         if (!pSubChunks) LoadSubChunks();
01013         return pSubChunks->size();
01014     }
01015 
01020     unsigned int List::CountSubChunks(uint32_t ChunkID) {
01021         unsigned int result = 0;
01022         if (!pSubChunks) LoadSubChunks();
01023         ChunkList::iterator iter = pSubChunks->begin();
01024         ChunkList::iterator end  = pSubChunks->end();
01025         while (iter != end) {
01026             if ((*iter)->GetChunkID() == ChunkID) {
01027                 result++;
01028             }
01029             iter++;
01030         }
01031         return result;
01032     }
01033 
01037     unsigned int List::CountSubLists() {
01038         return CountSubChunks(CHUNK_ID_LIST);
01039     }
01040 
01045     unsigned int List::CountSubLists(uint32_t ListType) {
01046         unsigned int result = 0;
01047         if (!pSubChunks) LoadSubChunks();
01048         ChunkList::iterator iter = pSubChunks->begin();
01049         ChunkList::iterator end  = pSubChunks->end();
01050         while (iter != end) {
01051             if ((*iter)->GetChunkID() == CHUNK_ID_LIST) {
01052                 List* l = (List*) *iter;
01053                 if (l->GetListType() == ListType) result++;
01054             }
01055             iter++;
01056         }
01057         return result;
01058     }
01059 
01073     Chunk* List::AddSubChunk(uint32_t uiChunkID, uint uiBodySize) {
01074         if (uiBodySize == 0) throw Exception("Chunk body size must be at least 1 byte");
01075         if (!pSubChunks) LoadSubChunks();
01076         Chunk* pNewChunk = new Chunk(pFile, this, uiChunkID, 0);
01077         pSubChunks->push_back(pNewChunk);
01078         (*pSubChunksMap)[uiChunkID] = pNewChunk;
01079         pNewChunk->Resize(uiBodySize);
01080         return pNewChunk;
01081     }
01082 
01092     List* List::AddSubList(uint32_t uiListType) {
01093         if (!pSubChunks) LoadSubChunks();
01094         List* pNewListChunk = new List(pFile, this, uiListType);
01095         pSubChunks->push_back(pNewListChunk);
01096         (*pSubChunksMap)[CHUNK_ID_LIST] = pNewListChunk;
01097         return pNewListChunk;
01098     }
01099 
01109     void List::DeleteSubChunk(Chunk* pSubChunk) {
01110         if (!pSubChunks) LoadSubChunks();
01111         pSubChunks->remove(pSubChunk);
01112         if ((*pSubChunksMap)[pSubChunk->GetChunkID()] == pSubChunk) {
01113             pSubChunksMap->erase(pSubChunk->GetChunkID());
01114             // try to find another chunk of the same chunk ID
01115             ChunkList::iterator iter = pSubChunks->begin();
01116             ChunkList::iterator end  = pSubChunks->end();
01117             for (; iter != end; ++iter) {
01118                 if ((*iter)->GetChunkID() == pSubChunk->GetChunkID()) {
01119                     (*pSubChunksMap)[pSubChunk->GetChunkID()] = *iter;
01120                     break; // we're done, stop search
01121                 }
01122             }
01123         }
01124         delete pSubChunk;
01125     }
01126 
01127     void List::ReadHeader(unsigned long fPos) {
01128       #if DEBUG
01129       std::cout << "List::Readheader(ulong) ";
01130       #endif // DEBUG
01131         Chunk::ReadHeader(fPos);
01132         NewChunkSize = CurrentChunkSize -= 4;
01133         #if POSIX
01134         lseek(pFile->hFileRead, fPos + CHUNK_HEADER_SIZE, SEEK_SET);
01135         read(pFile->hFileRead, &ListType, 4);
01136         #else
01137         fseek(pFile->hFileRead, fPos + CHUNK_HEADER_SIZE, SEEK_SET);
01138         fread(&ListType, 4, 1, pFile->hFileRead);
01139         #endif // POSIX
01140       #if DEBUG
01141       std::cout << "listType=" << convertToString(ListType) << std::endl;
01142       #endif // DEBUG
01143         if (!pFile->bEndianNative) {
01144             //swapBytes_32(&ListType);
01145         }
01146     }
01147 
01148     void List::WriteHeader(unsigned long fPos) {
01149         // the four list type bytes officially belong the chunk's body in the RIFF format
01150         NewChunkSize += 4;
01151         Chunk::WriteHeader(fPos);
01152         NewChunkSize -= 4; // just revert the +4 incrementation
01153         #if POSIX
01154         lseek(pFile->hFileWrite, fPos + CHUNK_HEADER_SIZE, SEEK_SET);
01155         write(pFile->hFileWrite, &ListType, 4);
01156         #else
01157         fseek(pFile->hFileWrite, fPos + CHUNK_HEADER_SIZE, SEEK_SET);
01158         fwrite(&ListType, 4, 1, pFile->hFileWrite);
01159         #endif // POSIX
01160     }
01161 
01162     void List::LoadSubChunks() {
01163        #if DEBUG
01164        std::cout << "List::LoadSubChunks()";
01165        #endif // DEBUG
01166         if (!pSubChunks) {
01167             pSubChunks    = new ChunkList();
01168             pSubChunksMap = new ChunkMap();
01169             if (!pFile->hFileRead) return;
01170             unsigned long uiOriginalPos = GetPos();
01171             SetPos(0); // jump to beginning of list chunk body
01172             while (RemainingBytes() >= CHUNK_HEADER_SIZE) {
01173                 Chunk* ck;
01174                 uint32_t ckid;
01175                 Read(&ckid, 4, 1);
01176        #if DEBUG
01177        std::cout << " ckid=" << convertToString(ckid) << std::endl;
01178        #endif // DEBUG
01179                 if (ckid == CHUNK_ID_LIST) {
01180                     ck = new RIFF::List(pFile, ulStartPos + ulPos - 4, this);
01181                     SetPos(ck->GetSize() + LIST_HEADER_SIZE - 4, RIFF::stream_curpos);
01182                 }
01183                 else { // simple chunk
01184                     ck = new RIFF::Chunk(pFile, ulStartPos + ulPos - 4, this);
01185                     SetPos(ck->GetSize() + CHUNK_HEADER_SIZE - 4, RIFF::stream_curpos);
01186                 }
01187                 pSubChunks->push_back(ck);
01188                 (*pSubChunksMap)[ckid] = ck;
01189                 if (GetPos() % 2 != 0) SetPos(1, RIFF::stream_curpos); // jump over pad byte
01190             }
01191             SetPos(uiOriginalPos); // restore position before this call
01192         }
01193     }
01194 
01195     void List::LoadSubChunksRecursively() {
01196         for (List* pList = GetFirstSubList(); pList; pList = GetNextSubList())
01197             pList->LoadSubChunksRecursively();
01198     }
01199 
01214     unsigned long List::WriteChunk(unsigned long ulWritePos, unsigned long ulCurrentDataOffset) {
01215         const unsigned long ulOriginalPos = ulWritePos;
01216         ulWritePos += LIST_HEADER_SIZE;
01217 
01218         if (pFile->Mode != stream_mode_read_write)
01219             throw Exception("Cannot write list chunk, file has to be opened in read+write mode");
01220 
01221         // write all subchunks (including sub list chunks) recursively
01222         if (pSubChunks) {
01223             for (ChunkList::iterator iter = pSubChunks->begin(), end = pSubChunks->end(); iter != end; ++iter) {
01224                 ulWritePos = (*iter)->WriteChunk(ulWritePos, ulCurrentDataOffset);
01225             }
01226         }
01227 
01228         // update this list chunk's header
01229         CurrentChunkSize = NewChunkSize = ulWritePos - ulOriginalPos - LIST_HEADER_SIZE;
01230         WriteHeader(ulOriginalPos);
01231 
01232         // offset of this list chunk in new written file may have changed
01233         ulStartPos = ulOriginalPos + LIST_HEADER_SIZE;
01234 
01235         return ulWritePos;
01236     }
01237 
01238     void List::__resetPos() {
01239         Chunk::__resetPos();
01240         if (pSubChunks) {
01241             for (ChunkList::iterator iter = pSubChunks->begin(), end = pSubChunks->end(); iter != end; ++iter) {
01242                 (*iter)->__resetPos();
01243             }
01244         }
01245     }
01246 
01250     String List::GetListTypeString() {
01251         return convertToString(ListType);
01252     }
01253 
01254 
01255 
01256 // *************** File ***************
01257 // *
01258 
01268     File::File(uint32_t FileType) : List(this) {
01269         hFileRead = hFileWrite = 0;
01270         Mode = stream_mode_closed;
01271         bEndianNative = true;
01272         ulStartPos = RIFF_HEADER_SIZE;
01273         ListType = FileType;
01274     }
01275 
01284     File::File(const String& path) : List(this), Filename(path) {
01285       #if DEBUG
01286       std::cout << "File::File("<<path<<")" << std::endl;
01287       #endif // DEBUG
01288         bEndianNative = true;
01289         #if POSIX
01290         hFileRead = hFileWrite = open(path.c_str(), O_RDONLY | O_NONBLOCK);
01291         if (hFileRead <= 0) {
01292             hFileRead = hFileWrite = 0;
01293             throw RIFF::Exception("Can't open \"" + path + "\"");
01294         }
01295         #else
01296         hFileRead = hFileWrite = fopen(path.c_str(), "rb");
01297         if (!hFile) throw RIFF::Exception("Can't open \"" + path + "\"");
01298         #endif // POSIX
01299         Mode = stream_mode_read;
01300         ulStartPos = RIFF_HEADER_SIZE;
01301         ReadHeader(0);
01302         if (ChunkID != CHUNK_ID_RIFF) {
01303             throw RIFF::Exception("Not a RIFF file");
01304         }
01305     }
01306 
01307     String File::GetFileName() {
01308         return Filename;
01309     }
01310 
01311     stream_mode_t File::GetMode() {
01312         return Mode;
01313     }
01314 
01325     bool File::SetMode(stream_mode_t NewMode) {
01326         if (NewMode != Mode) {
01327             switch (NewMode) {
01328                 case stream_mode_read:
01329                     #if POSIX
01330                     if (hFileRead) close(hFileRead);
01331                     hFileRead = hFileWrite = open(Filename.c_str(), O_RDONLY | O_NONBLOCK);
01332                     if (hFileRead < 0) {
01333                         hFileRead = hFileWrite = 0;
01334                         throw Exception("Could not (re)open file \"" + Filename + "\" in read mode");
01335                     }
01336                     #else
01337                     if (hFileRead) fclose(hFileRead);
01338                     hFileRead = hFileWrite = fopen(path.c_str(), "rb");
01339                     if (!hFileRead) throw Exception("Could not (re)open file \"" + Filename + "\" in read mode");
01340                     #endif
01341                     __resetPos(); // reset read/write position of ALL 'Chunk' objects
01342                     break;
01343                 case stream_mode_read_write:
01344                     #if POSIX
01345                     if (hFileRead) close(hFileRead);
01346                     hFileRead = hFileWrite = open(Filename.c_str(), O_RDWR | O_NONBLOCK);
01347                     if (hFileRead < 0) {
01348                         hFileRead = hFileWrite = open(Filename.c_str(), O_RDONLY | O_NONBLOCK);
01349                         throw Exception("Could not open file \"" + Filename + "\" in read+write mode");
01350                     }
01351                     #else
01352                     if (hFileRead) fclose(hFileRead);
01353                     hFileRead = hFileWrite = fopen(path.c_str(), "r+b");
01354                     if (!hFileRead) {
01355                         hFileRead = hFileWrite = fopen(path.c_str(), "rb");
01356                         throw Exception("Could not open file \"" + Filename + "\" in read+write mode");
01357                     }
01358                     #endif
01359                     __resetPos(); // reset read/write position of ALL 'Chunk' objects
01360                     break;
01361                 case stream_mode_closed:
01362                     #if POSIX
01363                     if (hFileRead)  close(hFileRead);
01364                     if (hFileWrite) close(hFileWrite);
01365                     #else
01366                     if (hFileRead)  fclose(hFileRead);
01367                     if (hFileWrite) fclose(hFileWrite);
01368                     #endif
01369                     hFileRead = hFileWrite = 0;
01370                     break;
01371                 default:
01372                     throw Exception("Unknown file access mode");
01373             }
01374             Mode = NewMode;
01375             return true;
01376         }
01377         return false;
01378     }
01379 
01390     void File::Save() {
01391         // make sure the RIFF tree is built (from the original file)
01392         LoadSubChunksRecursively();
01393 
01394         // reopen file in write mode
01395         SetMode(stream_mode_read_write);
01396 
01397         // to be able to save the whole file without loading everything into
01398         // RAM and without having to store the data in a temporary file, we
01399         // enlarge the file with the sum of all _positive_ chunk size
01400         // changes, move current data towards the end of the file with the
01401         // calculated sum and finally update / rewrite the file by copying
01402         // the old data back to the right position at the beginning of the file
01403 
01404         // first we sum up all positive chunk size changes (and skip all negative ones)
01405         unsigned long ulPositiveSizeDiff = 0;
01406         for (ChunkList::iterator iter = ResizedChunks.begin(), end = ResizedChunks.end(); iter != end; ++iter) {
01407             if ((*iter)->GetNewSize() == 0) throw Exception("There is at least one empty chunk (zero size)");
01408             if ((*iter)->GetNewSize() + 1L > (*iter)->GetSize()) {
01409                 unsigned long ulDiff = (*iter)->GetNewSize() - (*iter)->GetSize() + 1L; // +1 in case we have to add a pad byte
01410                 ulPositiveSizeDiff += ulDiff;
01411             }
01412         }
01413 
01414         unsigned long ulWorkingFileSize = GetFileSize();
01415 
01416         // if there are positive size changes...
01417         if (ulPositiveSizeDiff > 0) {
01418             // ... we enlarge this file first ...
01419             ulWorkingFileSize += ulPositiveSizeDiff;
01420             ResizeFile(ulWorkingFileSize);
01421             // ... and move current data by the same amount towards end of file.
01422             int8_t* pCopyBuffer = new int8_t[4096];
01423             const unsigned long ulFileSize = GetSize() + RIFF_HEADER_SIZE;
01424             int iBytesMoved = 1;
01425             for (unsigned long ulPos = 0; iBytesMoved > 0; ulPos += iBytesMoved) {
01426                 const unsigned long ulToMove = ulFileSize - ulPos;
01427                 iBytesMoved = (ulToMove < 4096) ? ulToMove : 4096;
01428                 #if POSIX
01429                 lseek(hFileRead, ulPos, SEEK_SET);
01430                 iBytesMoved = read(hFileRead, pCopyBuffer, iBytesMoved);
01431                 lseek(hFileWrite, ulPos + ulPositiveSizeDiff, SEEK_SET);
01432                 iBytesMoved = write(hFileWrite, pCopyBuffer, iBytesMoved);
01433                 #else
01434                 fseek(hFileRead, ulPos, SEEK_SET);
01435                 iBytesMoved = fread(pCopyBuffer, 1, iBytesMoved, hFileRead);
01436                 fseek(hFileWrite, ulPos + ulPositiveSizeDiff, SEEK_SET);
01437                 iBytesMoved = fwrite(pCopyBuffer, 1, iBytesMoved, hFileWrite);
01438                 #endif
01439             }
01440             delete[] pCopyBuffer;
01441             if (iBytesMoved < 0) throw Exception("Could not modify file while trying to enlarge it");
01442         }
01443 
01444         // rebuild / rewrite complete RIFF tree
01445         unsigned long ulTotalSize  = WriteChunk(0, ulPositiveSizeDiff);
01446         unsigned long ulActualSize = __GetFileSize(hFileWrite);
01447 
01448         // resize file to the final size
01449         if (ulTotalSize < ulActualSize) ResizeFile(ulTotalSize);
01450 
01451         // forget all resized chunks
01452         ResizedChunks.clear();
01453     }
01454 
01468     void File::Save(const String& path) {
01469         //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
01470 
01471         // make sure the RIFF tree is built (from the original file)
01472         LoadSubChunksRecursively();
01473 
01474         if (Filename.length() > 0) SetMode(stream_mode_read);
01475         // open the other (new) file for writing and truncate it to zero size
01476         #if POSIX
01477         hFileWrite = open(path.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP);
01478         if (hFileWrite < 0) {
01479             hFileWrite = hFileRead;
01480             throw Exception("Could not open file \"" + path + "\" for writing");
01481         }
01482         #else
01483         hFileWrite = fopen(path.c_str(), "w+b");
01484         if (!hFileWrite) {
01485             hFileWrite = hFileRead;
01486             throw Exception("Could not open file \"" + path + "\" for writing");
01487         }
01488         #endif // POSIX
01489         Mode = stream_mode_read_write;
01490 
01491         // write complete RIFF tree to the other (new) file
01492         unsigned long ulTotalSize  = WriteChunk(0, 0);
01493         unsigned long ulActualSize = __GetFileSize(hFileWrite);
01494 
01495         // resize file to the final size (if the file was originally larger)
01496         if (ulTotalSize < ulActualSize) ResizeFile(ulTotalSize);
01497 
01498         // forget all resized chunks
01499         ResizedChunks.clear();
01500 
01501         if (Filename.length() > 0) {
01502             #if POSIX
01503             close(hFileWrite);
01504             #else
01505             fclose(hFileWrite);
01506             #endif
01507             hFileWrite = hFileRead;
01508         }
01509 
01510         // associate new file with this File object from now on
01511         Filename = path;
01512         Mode = (stream_mode_t) -1;       // Just set it to an undefined mode ...
01513         SetMode(stream_mode_read_write); // ... so SetMode() has to reopen the file handles.
01514     }
01515 
01516     void File::ResizeFile(unsigned long ulNewSize) {
01517         #if POSIX
01518         if (ftruncate(hFileWrite, ulNewSize) < 0)
01519             throw Exception("Could not resize file \"" + Filename + "\"");
01520         #else
01521         # error Sorry, this version of libgig only supports POSIX systems yet.
01522         # error Reason: portable implementation of RIFF::File::ResizeFile() is missing (yet)!
01523         #endif
01524     }
01525 
01526     File::~File() {
01527        #if DEBUG
01528        std::cout << "File::~File()" << std::endl;
01529        #endif // DEBUG
01530         #if POSIX
01531         if (hFileRead) close(hFileRead);
01532         #else
01533         if (hFileRead) fclose(hFileRead);
01534         #endif // POSIX
01535     }
01536 
01537     void File::LogAsResized(Chunk* pResizedChunk) {
01538         ResizedChunks.push_back(pResizedChunk);
01539     }
01540 
01541     unsigned long File::GetFileSize() {
01542         return __GetFileSize(hFileRead);
01543     }
01544 
01545     #if POSIX
01546     unsigned long File::__GetFileSize(int hFile) {
01547         struct stat filestat;
01548         fstat(hFile, &filestat);
01549         long size = filestat.st_size;
01550         return size;
01551     }
01552     #else // standard C functions
01553     unsigned long File::__GetFileSize(FILE* hFile) {
01554         long curpos = ftell(hFile);
01555         fseek(hFile, 0, SEEK_END);
01556         long size = ftell(hFile);
01557         fseek(hFile, curpos, SEEK_SET);
01558         return size;
01559     }
01560     #endif
01561 
01562 
01563 // *************** Exception ***************
01564 // *
01565 
01566     void Exception::PrintMessage() {
01567         std::cout << "RIFF::Exception: " << Message << std::endl;
01568     }
01569 
01570 
01571 // *************** functions ***************
01572 // *
01573 
01579     String libraryName() {
01580         return PACKAGE;
01581     }
01582 
01587     String libraryVersion() {
01588         return VERSION;
01589     }
01590 
01591 } // namespace RIFF

Generated on Wed Dec 6 19:25:57 2006 for libgig by  doxygen 1.5.1