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-2007 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 // *************** Internal functions **************
00033 // *
00034 
00036     String __resolveChunkPath(Chunk* pCk) {
00037         String sPath;
00038         for (Chunk* pChunk = pCk; pChunk; pChunk = pChunk->GetParent()) {
00039             if (pChunk->GetChunkID() == CHUNK_ID_LIST) {
00040                 List* pList = (List*) pChunk;
00041                 sPath = "->'" + pList->GetListTypeString() + "'" + sPath;
00042             } else {
00043                 sPath = "->'" + pChunk->GetChunkIDString() + "'" + sPath;
00044             }
00045         }
00046         return sPath;
00047     }
00048 
00049 
00050 
00051 // *************** Chunk **************
00052 // *
00053 
00054     Chunk::Chunk(File* pFile) {
00055         #if DEBUG
00056         std::cout << "Chunk::Chunk(File* pFile)" << std::endl;
00057         #endif // DEBUG
00058         ulPos      = 0;
00059         pParent    = NULL;
00060         pChunkData = NULL;
00061         ulChunkDataSize = 0;
00062         ChunkID    = CHUNK_ID_RIFF;
00063         this->pFile = pFile;
00064     }
00065 
00066     Chunk::Chunk(File* pFile, unsigned long StartPos, List* Parent) {
00067         #if DEBUG
00068         std::cout << "Chunk::Chunk(File*,ulong,bool,List*),StartPos=" << StartPos << std::endl;
00069         #endif // DEBUG
00070         this->pFile   = pFile;
00071         ulStartPos    = StartPos + CHUNK_HEADER_SIZE;
00072         pParent       = Parent;
00073         ulPos         = 0;
00074         pChunkData    = NULL;
00075         ulChunkDataSize = 0;
00076         ReadHeader(StartPos);
00077     }
00078 
00079     Chunk::Chunk(File* pFile, List* pParent, uint32_t uiChunkID, uint uiBodySize) {
00080         this->pFile      = pFile;
00081         ulStartPos       = 0; // arbitrary usually, since it will be updated when we write the chunk
00082         this->pParent    = pParent;
00083         ulPos            = 0;
00084         pChunkData       = NULL;
00085         ulChunkDataSize  = 0;
00086         ChunkID          = uiChunkID;
00087         CurrentChunkSize = 0;
00088         NewChunkSize     = uiBodySize;
00089     }
00090 
00091     Chunk::~Chunk() {
00092         if (CurrentChunkSize != NewChunkSize) pFile->UnlogResized(this);
00093         if (pChunkData) delete[] pChunkData;
00094     }
00095 
00096     void Chunk::ReadHeader(unsigned long fPos) {
00097         #if DEBUG
00098         std::cout << "Chunk::Readheader(" << fPos << ") ";
00099         #endif // DEBUG
00100         #if POSIX
00101         if (lseek(pFile->hFileRead, fPos, SEEK_SET) != -1) {
00102             read(pFile->hFileRead, &ChunkID, 4);
00103             read(pFile->hFileRead, &CurrentChunkSize, 4);
00104         #elif defined(WIN32)
00105         if (SetFilePointer(pFile->hFileRead, fPos, NULL/*32 bit*/, FILE_BEGIN) != INVALID_SET_FILE_POINTER) {
00106             DWORD dwBytesRead;
00107             ReadFile(pFile->hFileRead, &ChunkID, 4, &dwBytesRead, NULL);
00108             ReadFile(pFile->hFileRead, &CurrentChunkSize, 4, &dwBytesRead, NULL);
00109         #else
00110         if (!fseek(pFile->hFileRead, fPos, SEEK_SET)) {
00111             fread(&ChunkID, 4, 1, pFile->hFileRead);
00112             fread(&CurrentChunkSize, 4, 1, pFile->hFileRead);
00113         #endif // POSIX
00114             #if WORDS_BIGENDIAN
00115             if (ChunkID == CHUNK_ID_RIFF) {
00116                 pFile->bEndianNative = false;
00117             }
00118             #else // little endian
00119             if (ChunkID == CHUNK_ID_RIFX) {
00120                 pFile->bEndianNative = false;
00121                 ChunkID = CHUNK_ID_RIFF;
00122             }
00123             #endif // WORDS_BIGENDIAN
00124             if (!pFile->bEndianNative) {
00125                 //swapBytes_32(&ChunkID);
00126                 swapBytes_32(&CurrentChunkSize);
00127             }
00128             #if DEBUG
00129             std::cout << "ckID=" << convertToString(ChunkID) << " ";
00130             std::cout << "ckSize=" << ChunkSize << " ";
00131             std::cout << "bEndianNative=" << bEndianNative << std::endl;
00132             #endif // DEBUG
00133             NewChunkSize = CurrentChunkSize;
00134         }
00135     }
00136 
00137     void Chunk::WriteHeader(unsigned long fPos) {
00138         uint32_t uiNewChunkID = ChunkID;
00139         if (ChunkID == CHUNK_ID_RIFF) {
00140             #if WORDS_BIGENDIAN
00141             if (pFile->bEndianNative) uiNewChunkID = CHUNK_ID_RIFX;
00142             #else // little endian
00143             if (!pFile->bEndianNative) uiNewChunkID = CHUNK_ID_RIFX;
00144             #endif // WORDS_BIGENDIAN
00145         }
00146 
00147         uint32_t uiNewChunkSize = NewChunkSize;
00148         if (!pFile->bEndianNative) {
00149             swapBytes_32(&uiNewChunkSize);
00150         }
00151 
00152         #if POSIX
00153         if (lseek(pFile->hFileWrite, fPos, SEEK_SET) != -1) {
00154             write(pFile->hFileWrite, &uiNewChunkID, 4);
00155             write(pFile->hFileWrite, &uiNewChunkSize, 4);
00156         }
00157         #elif defined(WIN32)
00158         if (SetFilePointer(pFile->hFileWrite, fPos, NULL/*32 bit*/, FILE_BEGIN) != INVALID_SET_FILE_POINTER) {
00159             DWORD dwBytesWritten;
00160             WriteFile(pFile->hFileWrite, &uiNewChunkID, 4, &dwBytesWritten, NULL);
00161             WriteFile(pFile->hFileWrite, &uiNewChunkSize, 4, &dwBytesWritten, NULL);
00162         }
00163         #else
00164         if (!fseek(pFile->hFileWrite, fPos, SEEK_SET)) {
00165             fwrite(&uiNewChunkID, 4, 1, pFile->hFileWrite);
00166             fwrite(&uiNewChunkSize, 4, 1, pFile->hFileWrite);
00167         }
00168         #endif // POSIX
00169     }
00170 
00175     String Chunk::GetChunkIDString() {
00176         return convertToString(ChunkID);
00177     }
00178 
00191     unsigned long Chunk::SetPos(unsigned long Where, stream_whence_t Whence) {
00192      #if DEBUG
00193      std::cout << "Chunk::SetPos(ulong)" << std::endl;
00194      #endif // DEBUG
00195         switch (Whence) {
00196             case stream_curpos:
00197                 ulPos += Where;
00198                 break;
00199             case stream_end:
00200                 ulPos = CurrentChunkSize - 1 - Where;
00201                 break;
00202             case stream_backward:
00203                 ulPos -= Where;
00204                 break;
00205             case stream_start: default:
00206                 ulPos = Where;
00207                 break;
00208         }
00209         if (ulPos > CurrentChunkSize) ulPos = CurrentChunkSize;
00210         return ulPos;
00211     }
00212 
00223     unsigned long Chunk::RemainingBytes() {
00224        #if DEBUG
00225        std::cout << "Chunk::Remainingbytes()=" << CurrentChunkSize - ulPos << std::endl;
00226        #endif // DEBUG
00227         return CurrentChunkSize - ulPos;
00228     }
00229 
00241     stream_state_t Chunk::GetState() {
00242       #if DEBUG
00243       std::cout << "Chunk::GetState()" << std::endl;
00244       #endif // DEBUG
00245         #if POSIX
00246         if (pFile->hFileRead == 0) return stream_closed;
00247         #elif defined (WIN32)
00248         if (pFile->hFileRead == INVALID_HANDLE_VALUE)
00249             return stream_closed;
00250         #else
00251         if (pFile->hFileRead == NULL) return stream_closed;
00252         #endif // POSIX
00253         if (ulPos < CurrentChunkSize) return stream_ready;
00254         else                          return stream_end_reached;
00255     }
00256 
00272     unsigned long Chunk::Read(void* pData, unsigned long WordCount, unsigned long WordSize) {
00273        #if DEBUG
00274        std::cout << "Chunk::Read(void*,ulong,ulong)" << std::endl;
00275        #endif // DEBUG
00276         if (ulPos >= CurrentChunkSize) return 0;
00277         if (ulPos + WordCount * WordSize >= CurrentChunkSize) WordCount = (CurrentChunkSize - ulPos) / WordSize;
00278         #if POSIX
00279         if (lseek(pFile->hFileRead, ulStartPos + ulPos, SEEK_SET) < 0) return 0;
00280         unsigned long readWords = read(pFile->hFileRead, pData, WordCount * WordSize);
00281         if (readWords < 1) return 0;
00282         readWords /= WordSize;
00283         #elif defined(WIN32)
00284         if (SetFilePointer(pFile->hFileRead, ulStartPos + ulPos, NULL/*32 bit*/, FILE_BEGIN) == INVALID_SET_FILE_POINTER) return 0;
00285         DWORD readWords;
00286         ReadFile(pFile->hFileRead, pData, WordCount * WordSize, &readWords, NULL);
00287         if (readWords < 1) return 0;
00288         readWords /= WordSize;
00289         #else // standard C functions
00290         if (fseek(pFile->hFileRead, ulStartPos + ulPos, SEEK_SET)) return 0;
00291         unsigned long readWords = fread(pData, WordSize, WordCount, pFile->hFileRead);
00292         #endif // POSIX
00293         if (!pFile->bEndianNative && WordSize != 1) {
00294             switch (WordSize) {
00295                 case 2:
00296                     for (unsigned long iWord = 0; iWord < readWords; iWord++)
00297                         swapBytes_16((uint16_t*) pData + iWord);
00298                     break;
00299                 case 4:
00300                     for (unsigned long iWord = 0; iWord < readWords; iWord++)
00301                         swapBytes_32((uint32_t*) pData + iWord);
00302                     break;
00303                 default:
00304                     for (unsigned long iWord = 0; iWord < readWords; iWord++)
00305                         swapBytes((uint8_t*) pData + iWord * WordSize, WordSize);
00306                     break;
00307             }
00308         }
00309         SetPos(readWords * WordSize, stream_curpos);
00310         return readWords;
00311     }
00312 
00329     unsigned long Chunk::Write(void* pData, unsigned long WordCount, unsigned long WordSize) {
00330         if (pFile->Mode != stream_mode_read_write)
00331             throw Exception("Cannot write data to chunk, file has to be opened in read+write mode first");
00332         if (ulPos >= CurrentChunkSize || ulPos + WordCount * WordSize > CurrentChunkSize)
00333             throw Exception("End of chunk reached while trying to write data");
00334         if (!pFile->bEndianNative && WordSize != 1) {
00335             switch (WordSize) {
00336                 case 2:
00337                     for (unsigned long iWord = 0; iWord < WordCount; iWord++)
00338                         swapBytes_16((uint16_t*) pData + iWord);
00339                     break;
00340                 case 4:
00341                     for (unsigned long iWord = 0; iWord < WordCount; iWord++)
00342                         swapBytes_32((uint32_t*) pData + iWord);
00343                     break;
00344                 default:
00345                     for (unsigned long iWord = 0; iWord < WordCount; iWord++)
00346                         swapBytes((uint8_t*) pData + iWord * WordSize, WordSize);
00347                     break;
00348             }
00349         }
00350         #if POSIX
00351         if (lseek(pFile->hFileWrite, ulStartPos + ulPos, SEEK_SET) < 0) {
00352             throw Exception("Could not seek to position " + ToString(ulPos) +
00353                             " in chunk (" + ToString(ulStartPos + ulPos) + " in file)");
00354         }
00355         unsigned long writtenWords = write(pFile->hFileWrite, pData, WordCount * WordSize);
00356         if (writtenWords < 1) throw Exception("POSIX IO Error while trying to write chunk data");
00357         writtenWords /= WordSize;
00358         #elif defined(WIN32)
00359         if (SetFilePointer(pFile->hFileWrite, ulStartPos + ulPos, NULL/*32 bit*/, FILE_BEGIN) == INVALID_SET_FILE_POINTER) {
00360             throw Exception("Could not seek to position " + ToString(ulPos) +
00361                             " in chunk (" + ToString(ulStartPos + ulPos) + " in file)");            
00362         }
00363         DWORD writtenWords;
00364         WriteFile(pFile->hFileWrite, pData, WordCount * WordSize, &writtenWords, NULL);
00365         if (writtenWords < 1) throw Exception("Windows IO Error while trying to write chunk data");
00366         writtenWords /= WordSize;
00367         #else // standard C functions
00368         if (fseek(pFile->hFileWrite, ulStartPos + ulPos, SEEK_SET)) {
00369             throw Exception("Could not seek to position " + ToString(ulPos) +
00370                             " in chunk (" + ToString(ulStartPos + ulPos) + " in file)");
00371         }
00372         unsigned long writtenWords = fwrite(pData, WordSize, WordCount, pFile->hFileWrite);
00373         #endif // POSIX
00374         SetPos(writtenWords * WordSize, stream_curpos);
00375         return writtenWords;
00376     }
00377 
00379     unsigned long Chunk::ReadSceptical(void* pData, unsigned long WordCount, unsigned long WordSize) {
00380         unsigned long readWords = Read(pData, WordCount, WordSize);
00381         if (readWords != WordCount) throw RIFF::Exception("End of chunk data reached.");
00382         return readWords;
00383     }
00384 
00396     unsigned long Chunk::ReadInt8(int8_t* pData, unsigned long WordCount) {
00397        #if DEBUG
00398        std::cout << "Chunk::ReadInt8(int8_t*,ulong)" << std::endl;
00399        #endif // DEBUG
00400         return ReadSceptical(pData, WordCount, 1);
00401     }
00402 
00417     unsigned long Chunk::WriteInt8(int8_t* pData, unsigned long WordCount) {
00418         return Write(pData, WordCount, 1);
00419     }
00420 
00433     unsigned long Chunk::ReadUint8(uint8_t* pData, unsigned long WordCount) {
00434        #if DEBUG
00435        std::cout << "Chunk::ReadUint8(uint8_t*,ulong)" << std::endl;
00436        #endif // DEBUG
00437         return ReadSceptical(pData, WordCount, 1);
00438     }
00439 
00454     unsigned long Chunk::WriteUint8(uint8_t* pData, unsigned long WordCount) {
00455         return Write(pData, WordCount, 1);
00456     }
00457 
00470     unsigned long Chunk::ReadInt16(int16_t* pData, unsigned long WordCount) {
00471       #if DEBUG
00472       std::cout << "Chunk::ReadInt16(int16_t*,ulong)" << std::endl;
00473       #endif // DEBUG
00474         return ReadSceptical(pData, WordCount, 2);
00475     }
00476 
00491     unsigned long Chunk::WriteInt16(int16_t* pData, unsigned long WordCount) {
00492         return Write(pData, WordCount, 2);
00493     }
00494 
00507     unsigned long Chunk::ReadUint16(uint16_t* pData, unsigned long WordCount) {
00508       #if DEBUG
00509       std::cout << "Chunk::ReadUint16(uint16_t*,ulong)" << std::endl;
00510       #endif // DEBUG
00511         return ReadSceptical(pData, WordCount, 2);
00512     }
00513 
00528     unsigned long Chunk::WriteUint16(uint16_t* pData, unsigned long WordCount) {
00529         return Write(pData, WordCount, 2);
00530     }
00531 
00544     unsigned long Chunk::ReadInt32(int32_t* pData, unsigned long WordCount) {
00545        #if DEBUG
00546        std::cout << "Chunk::ReadInt32(int32_t*,ulong)" << std::endl;
00547        #endif // DEBUG
00548         return ReadSceptical(pData, WordCount, 4);
00549     }
00550 
00565     unsigned long Chunk::WriteInt32(int32_t* pData, unsigned long WordCount) {
00566         return Write(pData, WordCount, 4);
00567     }
00568 
00581     unsigned long Chunk::ReadUint32(uint32_t* pData, unsigned long WordCount) {
00582        #if DEBUG
00583        std::cout << "Chunk::ReadUint32(uint32_t*,ulong)" << std::endl;
00584        #endif // DEBUG
00585         return ReadSceptical(pData, WordCount, 4);
00586     }
00587 
00602     unsigned long Chunk::WriteUint32(uint32_t* pData, unsigned long WordCount) {
00603         return Write(pData, WordCount, 4);
00604     }
00605 
00613     int8_t Chunk::ReadInt8() {
00614       #if DEBUG
00615       std::cout << "Chunk::ReadInt8()" << std::endl;
00616       #endif // DEBUG
00617         int8_t word;
00618         ReadSceptical(&word,1,1);
00619         return word;
00620     }
00621 
00629     uint8_t Chunk::ReadUint8() {
00630       #if DEBUG
00631       std::cout << "Chunk::ReadUint8()" << std::endl;
00632       #endif // DEBUG
00633         uint8_t word;
00634         ReadSceptical(&word,1,1);
00635         return word;
00636     }
00637 
00646     int16_t Chunk::ReadInt16() {
00647       #if DEBUG
00648       std::cout << "Chunk::ReadInt16()" << std::endl;
00649       #endif // DEBUG
00650         int16_t word;
00651         ReadSceptical(&word,1,2);
00652         return word;
00653     }
00654 
00663     uint16_t Chunk::ReadUint16() {
00664       #if DEBUG
00665       std::cout << "Chunk::ReadUint16()" << std::endl;
00666       #endif // DEBUG
00667         uint16_t word;
00668         ReadSceptical(&word,1,2);
00669         return word;
00670     }
00671 
00680     int32_t Chunk::ReadInt32() {
00681       #if DEBUG
00682       std::cout << "Chunk::ReadInt32()" << std::endl;
00683       #endif // DEBUG
00684         int32_t word;
00685         ReadSceptical(&word,1,4);
00686         return word;
00687     }
00688 
00697     uint32_t Chunk::ReadUint32() {
00698       #if DEBUG
00699       std::cout << "Chunk::ReadUint32()" << std::endl;
00700       #endif // DEBUG
00701         uint32_t word;
00702         ReadSceptical(&word,1,4);
00703         return word;
00704     }
00705 
00727     void* Chunk::LoadChunkData() {
00728         if (!pChunkData && pFile->Filename != "") {
00729             #if POSIX
00730             if (lseek(pFile->hFileRead, ulStartPos, SEEK_SET) == -1) return NULL;
00731             #elif defined(WIN32)
00732             if (SetFilePointer(pFile->hFileRead, ulStartPos, NULL/*32 bit*/, FILE_BEGIN) == INVALID_SET_FILE_POINTER) return NULL;
00733             #else
00734             if (fseek(pFile->hFileRead, ulStartPos, SEEK_SET)) return NULL;
00735             #endif // POSIX
00736             unsigned long ulBufferSize = (CurrentChunkSize > NewChunkSize) ? CurrentChunkSize : NewChunkSize;
00737             pChunkData = new uint8_t[ulBufferSize];
00738             if (!pChunkData) return NULL;
00739             memset(pChunkData, 0, ulBufferSize);
00740             #if POSIX
00741             unsigned long readWords = read(pFile->hFileRead, pChunkData, GetSize());
00742             #elif defined(WIN32)
00743             DWORD readWords;
00744             ReadFile(pFile->hFileRead, pChunkData, GetSize(), &readWords, NULL);
00745             #else
00746             unsigned long readWords = fread(pChunkData, 1, GetSize(), pFile->hFileRead);
00747             #endif // POSIX
00748             if (readWords != GetSize()) {
00749                 delete[] pChunkData;
00750                 return (pChunkData = NULL);
00751             }
00752             ulChunkDataSize = ulBufferSize;
00753         } else if (NewChunkSize > ulChunkDataSize) {
00754             uint8_t* pNewBuffer = new uint8_t[NewChunkSize];
00755             if (!pNewBuffer) throw Exception("Could not enlarge chunk data buffer to " + ToString(NewChunkSize) + " bytes");
00756             memset(pNewBuffer, 0 , NewChunkSize);
00757             memcpy(pNewBuffer, pChunkData, ulChunkDataSize);
00758             delete[] pChunkData;
00759             pChunkData      = pNewBuffer;
00760             ulChunkDataSize = NewChunkSize;
00761         }
00762         return pChunkData;
00763     }
00764 
00771     void Chunk::ReleaseChunkData() {
00772         if (pChunkData) {
00773             delete[] pChunkData;
00774             pChunkData = NULL;
00775         }
00776     }
00777 
00796     void Chunk::Resize(int iNewSize) {
00797         if (iNewSize <= 0)
00798             throw Exception("There is at least one empty chunk (zero size): " + __resolveChunkPath(this));
00799         if (NewChunkSize == iNewSize) return;
00800         NewChunkSize = iNewSize;
00801         pFile->LogAsResized(this);
00802     }
00803 
00816     unsigned long Chunk::WriteChunk(unsigned long ulWritePos, unsigned long ulCurrentDataOffset) {
00817         const unsigned long ulOriginalPos = ulWritePos;
00818         ulWritePos += CHUNK_HEADER_SIZE;
00819 
00820         if (pFile->Mode != stream_mode_read_write)
00821             throw Exception("Cannot write list chunk, file has to be opened in read+write mode");
00822 
00823         // if the whole chunk body was loaded into RAM
00824         if (pChunkData) {
00825             // make sure chunk data buffer in RAM is at least as large as the new chunk size
00826             LoadChunkData();
00827             // write chunk data from RAM persistently to the file
00828             #if POSIX
00829             lseek(pFile->hFileWrite, ulWritePos, SEEK_SET);
00830             if (write(pFile->hFileWrite, pChunkData, NewChunkSize) != NewChunkSize) {
00831                 throw Exception("Writing Chunk data (from RAM) failed");
00832             }
00833             #elif defined(WIN32)
00834             SetFilePointer(pFile->hFileWrite, ulWritePos, NULL/*32 bit*/, FILE_BEGIN);
00835             DWORD dwBytesWritten;
00836             WriteFile(pFile->hFileWrite, pChunkData, NewChunkSize, &dwBytesWritten, NULL);
00837             if (dwBytesWritten != NewChunkSize) {
00838                 throw Exception("Writing Chunk data (from RAM) failed");
00839             }
00840             #else
00841             fseek(pFile->hFileWrite, ulWritePos, SEEK_SET);
00842             if (fwrite(pChunkData, 1, NewChunkSize, pFile->hFileWrite) != NewChunkSize) {
00843                 throw Exception("Writing Chunk data (from RAM) failed");
00844             }
00845             #endif // POSIX
00846         } else {
00847             // move chunk data from the end of the file to the appropriate position
00848             int8_t* pCopyBuffer = new int8_t[4096];
00849             unsigned long ulToMove = (NewChunkSize < CurrentChunkSize) ? NewChunkSize : CurrentChunkSize;
00850             #if defined(WIN32)
00851             DWORD iBytesMoved = 1; // we have to pass it via pointer to the Windows API, thus the correct size must be ensured
00852             #else
00853             int iBytesMoved = 1;
00854             #endif
00855             for (unsigned long ulOffset = 0; iBytesMoved > 0; ulOffset += iBytesMoved, ulToMove -= iBytesMoved) {
00856                 iBytesMoved = (ulToMove < 4096) ? ulToMove : 4096;
00857                 #if POSIX
00858                 lseek(pFile->hFileRead, ulStartPos + ulCurrentDataOffset + ulOffset, SEEK_SET);
00859                 iBytesMoved = read(pFile->hFileRead, pCopyBuffer, iBytesMoved);
00860                 lseek(pFile->hFileWrite, ulWritePos + ulOffset, SEEK_SET);
00861                 iBytesMoved = write(pFile->hFileWrite, pCopyBuffer, iBytesMoved);
00862                 #elif defined(WIN32)
00863                 SetFilePointer(pFile->hFileRead, ulStartPos + ulCurrentDataOffset + ulOffset, NULL/*32 bit*/, FILE_BEGIN);
00864                 ReadFile(pFile->hFileRead, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
00865                 SetFilePointer(pFile->hFileWrite, ulWritePos + ulOffset, NULL/*32 bit*/, FILE_BEGIN);
00866                 WriteFile(pFile->hFileWrite, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
00867                 #else
00868                 fseek(pFile->hFileRead, ulStartPos + ulCurrentDataOffset + ulOffset, SEEK_SET);
00869                 iBytesMoved = fread(pCopyBuffer, 1, iBytesMoved, pFile->hFileRead);
00870                 fseek(pFile->hFileWrite, ulWritePos + ulOffset, SEEK_SET);
00871                 iBytesMoved = fwrite(pCopyBuffer, 1, iBytesMoved, pFile->hFileWrite);
00872                 #endif
00873             }
00874             delete[] pCopyBuffer;
00875             if (iBytesMoved < 0) throw Exception("Writing Chunk data (from file) failed");
00876         }
00877 
00878         // update this chunk's header
00879         CurrentChunkSize = NewChunkSize;
00880         WriteHeader(ulOriginalPos);
00881 
00882         // update chunk's position pointers
00883         ulStartPos = ulOriginalPos + CHUNK_HEADER_SIZE;
00884         ulPos      = 0;
00885 
00886         // add pad byte if needed
00887         if ((ulStartPos + NewChunkSize) % 2 != 0) {
00888             const char cPadByte = 0;
00889             #if POSIX
00890             lseek(pFile->hFileWrite, ulStartPos + NewChunkSize, SEEK_SET);
00891             write(pFile->hFileWrite, &cPadByte, 1);
00892             #elif defined(WIN32)
00893             SetFilePointer(pFile->hFileWrite, ulStartPos + NewChunkSize, NULL/*32 bit*/, FILE_BEGIN);
00894             DWORD dwBytesWritten;
00895             WriteFile(pFile->hFileWrite, &cPadByte, 1, &dwBytesWritten, NULL);
00896             #else
00897             fseek(pFile->hFileWrite, ulStartPos + NewChunkSize, SEEK_SET);
00898             fwrite(&cPadByte, 1, 1, pFile->hFileWrite);
00899             #endif
00900             return ulStartPos + NewChunkSize + 1;
00901         }
00902 
00903         return ulStartPos + NewChunkSize;
00904     }
00905 
00906     void Chunk::__resetPos() {
00907         ulPos = 0;
00908     }
00909 
00910 
00911 
00912 // *************** List ***************
00913 // *
00914 
00915     List::List(File* pFile) : Chunk(pFile) {
00916       #if DEBUG
00917       std::cout << "List::List(File* pFile)" << std::endl;
00918       #endif // DEBUG
00919         pSubChunks    = NULL;
00920         pSubChunksMap = NULL;
00921     }
00922 
00923     List::List(File* pFile, unsigned long StartPos, List* Parent)
00924       : Chunk(pFile, StartPos, Parent) {
00925         #if DEBUG
00926         std::cout << "List::List(File*,ulong,bool,List*)" << std::endl;
00927         #endif // DEBUG
00928         pSubChunks    = NULL;
00929         pSubChunksMap = NULL;
00930         ReadHeader(StartPos);
00931         ulStartPos    = StartPos + LIST_HEADER_SIZE;
00932     }
00933 
00934     List::List(File* pFile, List* pParent, uint32_t uiListID)
00935       : Chunk(pFile, pParent, CHUNK_ID_LIST, 0) {
00936         pSubChunks    = NULL;
00937         pSubChunksMap = NULL;
00938         ListType      = uiListID;
00939     }
00940 
00941     List::~List() {
00942       #if DEBUG
00943       std::cout << "List::~List()" << std::endl;
00944       #endif // DEBUG
00945         if (pSubChunks) {
00946             ChunkList::iterator iter = pSubChunks->begin();
00947             ChunkList::iterator end  = pSubChunks->end();
00948             while (iter != end) {
00949                 delete *iter;
00950                 iter++;
00951             }
00952             delete pSubChunks;
00953         }
00954         if (pSubChunksMap) delete pSubChunksMap;
00955     }
00956 
00968     Chunk* List::GetSubChunk(uint32_t ChunkID) {
00969       #if DEBUG
00970       std::cout << "List::GetSubChunk(uint32_t)" << std::endl;
00971       #endif // DEBUG
00972         if (!pSubChunksMap) LoadSubChunks();
00973         return (*pSubChunksMap)[ChunkID];
00974     }
00975 
00987     List* List::GetSubList(uint32_t ListType) {
00988         #if DEBUG
00989         std::cout << "List::GetSubList(uint32_t)" << std::endl;
00990         #endif // DEBUG
00991         if (!pSubChunks) LoadSubChunks();
00992         ChunkList::iterator iter = pSubChunks->begin();
00993         ChunkList::iterator end  = pSubChunks->end();
00994         while (iter != end) {
00995             if ((*iter)->GetChunkID() == CHUNK_ID_LIST) {
00996                 List* l = (List*) *iter;
00997                 if (l->GetListType() == ListType) return l;
00998             }
00999             iter++;
01000         }
01001         return NULL;
01002     }
01003 
01012     Chunk* List::GetFirstSubChunk() {
01013         #if DEBUG
01014         std::cout << "List::GetFirstSubChunk()" << std::endl;
01015         #endif // DEBUG
01016         if (!pSubChunks) LoadSubChunks();
01017         ChunksIterator = pSubChunks->begin();
01018         return (ChunksIterator != pSubChunks->end()) ? *ChunksIterator : NULL;
01019     }
01020 
01028     Chunk* List::GetNextSubChunk() {
01029         #if DEBUG
01030         std::cout << "List::GetNextSubChunk()" << std::endl;
01031         #endif // DEBUG
01032         if (!pSubChunks) return NULL;
01033         ChunksIterator++;
01034         return (ChunksIterator != pSubChunks->end()) ? *ChunksIterator : NULL;
01035     }
01036 
01046     List* List::GetFirstSubList() {
01047         #if DEBUG
01048         std::cout << "List::GetFirstSubList()" << std::endl;
01049         #endif // DEBUG
01050         if (!pSubChunks) LoadSubChunks();
01051         ListIterator            = pSubChunks->begin();
01052         ChunkList::iterator end = pSubChunks->end();
01053         while (ListIterator != end) {
01054             if ((*ListIterator)->GetChunkID() == CHUNK_ID_LIST) return (List*) *ListIterator;
01055             ListIterator++;
01056         }
01057         return NULL;
01058     }
01059 
01068     List* List::GetNextSubList() {
01069         #if DEBUG
01070         std::cout << "List::GetNextSubList()" << std::endl;
01071         #endif // DEBUG
01072         if (!pSubChunks) return NULL;
01073         if (ListIterator == pSubChunks->end()) return NULL;
01074         ListIterator++;
01075         ChunkList::iterator end = pSubChunks->end();
01076         while (ListIterator != end) {
01077             if ((*ListIterator)->GetChunkID() == CHUNK_ID_LIST) return (List*) *ListIterator;
01078             ListIterator++;
01079         }
01080         return NULL;
01081     }
01082 
01086     unsigned int List::CountSubChunks() {
01087         if (!pSubChunks) LoadSubChunks();
01088         return pSubChunks->size();
01089     }
01090 
01095     unsigned int List::CountSubChunks(uint32_t ChunkID) {
01096         unsigned int result = 0;
01097         if (!pSubChunks) LoadSubChunks();
01098         ChunkList::iterator iter = pSubChunks->begin();
01099         ChunkList::iterator end  = pSubChunks->end();
01100         while (iter != end) {
01101             if ((*iter)->GetChunkID() == ChunkID) {
01102                 result++;
01103             }
01104             iter++;
01105         }
01106         return result;
01107     }
01108 
01112     unsigned int List::CountSubLists() {
01113         return CountSubChunks(CHUNK_ID_LIST);
01114     }
01115 
01120     unsigned int List::CountSubLists(uint32_t ListType) {
01121         unsigned int result = 0;
01122         if (!pSubChunks) LoadSubChunks();
01123         ChunkList::iterator iter = pSubChunks->begin();
01124         ChunkList::iterator end  = pSubChunks->end();
01125         while (iter != end) {
01126             if ((*iter)->GetChunkID() == CHUNK_ID_LIST) {
01127                 List* l = (List*) *iter;
01128                 if (l->GetListType() == ListType) result++;
01129             }
01130             iter++;
01131         }
01132         return result;
01133     }
01134 
01148     Chunk* List::AddSubChunk(uint32_t uiChunkID, uint uiBodySize) {
01149         if (uiBodySize == 0) throw Exception("Chunk body size must be at least 1 byte");
01150         if (!pSubChunks) LoadSubChunks();
01151         Chunk* pNewChunk = new Chunk(pFile, this, uiChunkID, 0);
01152         pSubChunks->push_back(pNewChunk);
01153         (*pSubChunksMap)[uiChunkID] = pNewChunk;
01154         pNewChunk->Resize(uiBodySize);
01155         return pNewChunk;
01156     }
01157 
01169     void List::MoveSubChunk(Chunk* pSrc, Chunk* pDst) {
01170         if (!pSubChunks) LoadSubChunks();
01171         pSubChunks->remove(pSrc);
01172         ChunkList::iterator iter = find(pSubChunks->begin(), pSubChunks->end(), pDst);
01173         pSubChunks->insert(iter, pSrc);
01174     }
01175 
01185     List* List::AddSubList(uint32_t uiListType) {
01186         if (!pSubChunks) LoadSubChunks();
01187         List* pNewListChunk = new List(pFile, this, uiListType);
01188         pSubChunks->push_back(pNewListChunk);
01189         (*pSubChunksMap)[CHUNK_ID_LIST] = pNewListChunk;
01190         return pNewListChunk;
01191     }
01192 
01203     void List::DeleteSubChunk(Chunk* pSubChunk) {
01204         if (!pSubChunks) LoadSubChunks();
01205         pSubChunks->remove(pSubChunk);
01206         if ((*pSubChunksMap)[pSubChunk->GetChunkID()] == pSubChunk) {
01207             pSubChunksMap->erase(pSubChunk->GetChunkID());
01208             // try to find another chunk of the same chunk ID
01209             ChunkList::iterator iter = pSubChunks->begin();
01210             ChunkList::iterator end  = pSubChunks->end();
01211             for (; iter != end; ++iter) {
01212                 if ((*iter)->GetChunkID() == pSubChunk->GetChunkID()) {
01213                     (*pSubChunksMap)[pSubChunk->GetChunkID()] = *iter;
01214                     break; // we're done, stop search
01215                 }
01216             }
01217         }
01218         delete pSubChunk;
01219     }
01220 
01221     void List::ReadHeader(unsigned long fPos) {
01222       #if DEBUG
01223       std::cout << "List::Readheader(ulong) ";
01224       #endif // DEBUG
01225         Chunk::ReadHeader(fPos);
01226         NewChunkSize = CurrentChunkSize -= 4;
01227         #if POSIX
01228         lseek(pFile->hFileRead, fPos + CHUNK_HEADER_SIZE, SEEK_SET);
01229         read(pFile->hFileRead, &ListType, 4);
01230         #elif defined(WIN32)
01231         SetFilePointer(pFile->hFileRead, fPos + CHUNK_HEADER_SIZE, NULL/*32 bit*/, FILE_BEGIN);
01232         DWORD dwBytesRead;
01233         ReadFile(pFile->hFileRead, &ListType, 4, &dwBytesRead, NULL);
01234         #else
01235         fseek(pFile->hFileRead, fPos + CHUNK_HEADER_SIZE, SEEK_SET);
01236         fread(&ListType, 4, 1, pFile->hFileRead);
01237         #endif // POSIX
01238       #if DEBUG
01239       std::cout << "listType=" << convertToString(ListType) << std::endl;
01240       #endif // DEBUG
01241         if (!pFile->bEndianNative) {
01242             //swapBytes_32(&ListType);
01243         }
01244     }
01245 
01246     void List::WriteHeader(unsigned long fPos) {
01247         // the four list type bytes officially belong the chunk's body in the RIFF format
01248         NewChunkSize += 4;
01249         Chunk::WriteHeader(fPos);
01250         NewChunkSize -= 4; // just revert the +4 incrementation
01251         #if POSIX
01252         lseek(pFile->hFileWrite, fPos + CHUNK_HEADER_SIZE, SEEK_SET);
01253         write(pFile->hFileWrite, &ListType, 4);
01254         #elif defined(WIN32)
01255         SetFilePointer(pFile->hFileWrite, fPos + CHUNK_HEADER_SIZE, NULL/*32 bit*/, FILE_BEGIN);
01256         DWORD dwBytesWritten;
01257         WriteFile(pFile->hFileWrite, &ListType, 4, &dwBytesWritten, NULL);
01258         #else
01259         fseek(pFile->hFileWrite, fPos + CHUNK_HEADER_SIZE, SEEK_SET);
01260         fwrite(&ListType, 4, 1, pFile->hFileWrite);
01261         #endif // POSIX
01262     }
01263 
01264     void List::LoadSubChunks() {
01265        #if DEBUG
01266        std::cout << "List::LoadSubChunks()";
01267        #endif // DEBUG
01268         if (!pSubChunks) {
01269             pSubChunks    = new ChunkList();
01270             pSubChunksMap = new ChunkMap();
01271             if (!pFile->hFileRead) return;
01272             unsigned long uiOriginalPos = GetPos();
01273             SetPos(0); // jump to beginning of list chunk body
01274             while (RemainingBytes() >= CHUNK_HEADER_SIZE) {
01275                 Chunk* ck;
01276                 uint32_t ckid;
01277                 Read(&ckid, 4, 1);
01278        #if DEBUG
01279        std::cout << " ckid=" << convertToString(ckid) << std::endl;
01280        #endif // DEBUG
01281                 if (ckid == CHUNK_ID_LIST) {
01282                     ck = new RIFF::List(pFile, ulStartPos + ulPos - 4, this);
01283                     SetPos(ck->GetSize() + LIST_HEADER_SIZE - 4, RIFF::stream_curpos);
01284                 }
01285                 else { // simple chunk
01286                     ck = new RIFF::Chunk(pFile, ulStartPos + ulPos - 4, this);
01287                     SetPos(ck->GetSize() + CHUNK_HEADER_SIZE - 4, RIFF::stream_curpos);
01288                 }
01289                 pSubChunks->push_back(ck);
01290                 (*pSubChunksMap)[ckid] = ck;
01291                 if (GetPos() % 2 != 0) SetPos(1, RIFF::stream_curpos); // jump over pad byte
01292             }
01293             SetPos(uiOriginalPos); // restore position before this call
01294         }
01295     }
01296 
01297     void List::LoadSubChunksRecursively() {
01298         for (List* pList = GetFirstSubList(); pList; pList = GetNextSubList())
01299             pList->LoadSubChunksRecursively();
01300     }
01301 
01316     unsigned long List::WriteChunk(unsigned long ulWritePos, unsigned long ulCurrentDataOffset) {
01317         const unsigned long ulOriginalPos = ulWritePos;
01318         ulWritePos += LIST_HEADER_SIZE;
01319 
01320         if (pFile->Mode != stream_mode_read_write)
01321             throw Exception("Cannot write list chunk, file has to be opened in read+write mode");
01322 
01323         // write all subchunks (including sub list chunks) recursively
01324         if (pSubChunks) {
01325             for (ChunkList::iterator iter = pSubChunks->begin(), end = pSubChunks->end(); iter != end; ++iter) {
01326                 ulWritePos = (*iter)->WriteChunk(ulWritePos, ulCurrentDataOffset);
01327             }
01328         }
01329 
01330         // update this list chunk's header
01331         CurrentChunkSize = NewChunkSize = ulWritePos - ulOriginalPos - LIST_HEADER_SIZE;
01332         WriteHeader(ulOriginalPos);
01333 
01334         // offset of this list chunk in new written file may have changed
01335         ulStartPos = ulOriginalPos + LIST_HEADER_SIZE;
01336 
01337         return ulWritePos;
01338     }
01339 
01340     void List::__resetPos() {
01341         Chunk::__resetPos();
01342         if (pSubChunks) {
01343             for (ChunkList::iterator iter = pSubChunks->begin(), end = pSubChunks->end(); iter != end; ++iter) {
01344                 (*iter)->__resetPos();
01345             }
01346         }
01347     }
01348 
01352     String List::GetListTypeString() {
01353         return convertToString(ListType);
01354     }
01355 
01356 
01357 
01358 // *************** File ***************
01359 // *
01360 
01370     File::File(uint32_t FileType) : List(this) {
01371         #if defined(WIN32)
01372         hFileRead = hFileWrite = INVALID_HANDLE_VALUE;
01373         #else
01374         hFileRead = hFileWrite = 0;
01375         #endif
01376         Mode = stream_mode_closed;
01377         bEndianNative = true;
01378         ulStartPos = RIFF_HEADER_SIZE;
01379         ListType = FileType;
01380     }
01381 
01390     File::File(const String& path) : List(this), Filename(path) {
01391       #if DEBUG
01392       std::cout << "File::File("<<path<<")" << std::endl;
01393       #endif // DEBUG
01394         bEndianNative = true;
01395         #if POSIX
01396         hFileRead = hFileWrite = open(path.c_str(), O_RDONLY | O_NONBLOCK);
01397         if (hFileRead <= 0) {
01398             hFileRead = hFileWrite = 0;
01399             throw RIFF::Exception("Can't open \"" + path + "\"");
01400         }
01401         #elif defined(WIN32)
01402         hFileRead = hFileWrite = CreateFile(
01403                                      path.c_str(), GENERIC_READ,
01404                                      FILE_SHARE_READ | FILE_SHARE_WRITE,
01405                                      NULL, OPEN_EXISTING,
01406                                      FILE_ATTRIBUTE_NORMAL, NULL
01407                                  );
01408         if (hFileRead == INVALID_HANDLE_VALUE) {
01409             hFileRead = hFileWrite = INVALID_HANDLE_VALUE;
01410             throw RIFF::Exception("Can't open \"" + path + "\"");
01411         }
01412         #else
01413         hFileRead = hFileWrite = fopen(path.c_str(), "rb");
01414         if (!hFileRead) throw RIFF::Exception("Can't open \"" + path + "\"");
01415         #endif // POSIX
01416         Mode = stream_mode_read;
01417         ulStartPos = RIFF_HEADER_SIZE;
01418         ReadHeader(0);
01419         if (ChunkID != CHUNK_ID_RIFF) {
01420             throw RIFF::Exception("Not a RIFF file");
01421         }
01422     }
01423 
01424     String File::GetFileName() {
01425         return Filename;
01426     }
01427 
01428     stream_mode_t File::GetMode() {
01429         return Mode;
01430     }
01431 
01442     bool File::SetMode(stream_mode_t NewMode) {
01443         if (NewMode != Mode) {
01444             switch (NewMode) {
01445                 case stream_mode_read:
01446                     #if POSIX
01447                     if (hFileRead) close(hFileRead);
01448                     hFileRead = hFileWrite = open(Filename.c_str(), O_RDONLY | O_NONBLOCK);
01449                     if (hFileRead < 0) {
01450                         hFileRead = hFileWrite = 0;
01451                         throw Exception("Could not (re)open file \"" + Filename + "\" in read mode");
01452                     }
01453                     #elif defined(WIN32)
01454                     if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
01455                     hFileRead = hFileWrite = CreateFile(
01456                                                  Filename.c_str(), GENERIC_READ,
01457                                                  FILE_SHARE_READ | FILE_SHARE_WRITE,
01458                                                  NULL, OPEN_EXISTING,
01459                                                  FILE_ATTRIBUTE_NORMAL, NULL
01460                                              );
01461                     if (hFileRead == INVALID_HANDLE_VALUE) {
01462                         hFileRead = hFileWrite = INVALID_HANDLE_VALUE;
01463                         throw Exception("Could not (re)open file \"" + Filename + "\" in read mode");
01464                     }
01465                     #else
01466                     if (hFileRead) fclose(hFileRead);
01467                     hFileRead = hFileWrite = fopen(Filename.c_str(), "rb");
01468                     if (!hFileRead) throw Exception("Could not (re)open file \"" + Filename + "\" in read mode");
01469                     #endif
01470                     __resetPos(); // reset read/write position of ALL 'Chunk' objects
01471                     break;
01472                 case stream_mode_read_write:
01473                     #if POSIX
01474                     if (hFileRead) close(hFileRead);
01475                     hFileRead = hFileWrite = open(Filename.c_str(), O_RDWR | O_NONBLOCK);
01476                     if (hFileRead < 0) {
01477                         hFileRead = hFileWrite = open(Filename.c_str(), O_RDONLY | O_NONBLOCK);
01478                         throw Exception("Could not open file \"" + Filename + "\" in read+write mode");
01479                     }
01480                     #elif defined(WIN32)
01481                     if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
01482                     hFileRead = hFileWrite = CreateFile(
01483                                                  Filename.c_str(),
01484                                                  GENERIC_READ | GENERIC_WRITE,
01485                                                  FILE_SHARE_READ,
01486                                                  NULL, OPEN_ALWAYS,
01487                                                  FILE_ATTRIBUTE_NORMAL, NULL
01488                                              );
01489                     if (hFileRead == INVALID_HANDLE_VALUE) {
01490                         hFileRead = hFileWrite = CreateFile(
01491                                                      Filename.c_str(), GENERIC_READ,
01492                                                      FILE_SHARE_READ | FILE_SHARE_WRITE,
01493                                                      NULL, OPEN_EXISTING,
01494                                                      FILE_ATTRIBUTE_NORMAL, NULL
01495                                                  );
01496                         throw Exception("Could not (re)open file \"" + Filename + "\" in read mode");
01497                     }
01498                     #else
01499                     if (hFileRead) fclose(hFileRead);
01500                     hFileRead = hFileWrite = fopen(Filename.c_str(), "r+b");
01501                     if (!hFileRead) {
01502                         hFileRead = hFileWrite = fopen(Filename.c_str(), "rb");
01503                         throw Exception("Could not open file \"" + Filename + "\" in read+write mode");
01504                     }
01505                     #endif
01506                     __resetPos(); // reset read/write position of ALL 'Chunk' objects
01507                     break;
01508                 case stream_mode_closed:
01509                     #if POSIX
01510                     if (hFileRead)  close(hFileRead);
01511                     if (hFileWrite) close(hFileWrite);
01512                     #elif defined(WIN32)
01513                     if (hFileRead  != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
01514                     if (hFileWrite != INVALID_HANDLE_VALUE) CloseHandle(hFileWrite);
01515                     #else
01516                     if (hFileRead)  fclose(hFileRead);
01517                     if (hFileWrite) fclose(hFileWrite);
01518                     #endif
01519                     hFileRead = hFileWrite = 0;
01520                     break;
01521                 default:
01522                     throw Exception("Unknown file access mode");
01523             }
01524             Mode = NewMode;
01525             return true;
01526         }
01527         return false;
01528     }
01529 
01540     void File::Save() {
01541         // make sure the RIFF tree is built (from the original file)
01542         LoadSubChunksRecursively();
01543 
01544         // reopen file in write mode
01545         SetMode(stream_mode_read_write);
01546 
01547         // to be able to save the whole file without loading everything into
01548         // RAM and without having to store the data in a temporary file, we
01549         // enlarge the file with the sum of all _positive_ chunk size
01550         // changes, move current data towards the end of the file with the
01551         // calculated sum and finally update / rewrite the file by copying
01552         // the old data back to the right position at the beginning of the file
01553 
01554         // first we sum up all positive chunk size changes (and skip all negative ones)
01555         unsigned long ulPositiveSizeDiff = 0;
01556         for (ChunkList::iterator iter = ResizedChunks.begin(), end = ResizedChunks.end(); iter != end; ++iter) {
01557             if ((*iter)->GetNewSize() == 0) {
01558                 throw Exception("There is at least one empty chunk (zero size): " + __resolveChunkPath(*iter));
01559             }
01560             if ((*iter)->GetNewSize() + 1L > (*iter)->GetSize()) {
01561                 unsigned long ulDiff = (*iter)->GetNewSize() - (*iter)->GetSize() + 1L; // +1 in case we have to add a pad byte
01562                 ulPositiveSizeDiff += ulDiff;
01563             }
01564         }
01565 
01566         unsigned long ulWorkingFileSize = GetFileSize();
01567 
01568         // if there are positive size changes...
01569         if (ulPositiveSizeDiff > 0) {
01570             // ... we enlarge this file first ...
01571             ulWorkingFileSize += ulPositiveSizeDiff;
01572             ResizeFile(ulWorkingFileSize);
01573             // ... and move current data by the same amount towards end of file.
01574             int8_t* pCopyBuffer = new int8_t[4096];
01575             const unsigned long ulFileSize = GetSize() + RIFF_HEADER_SIZE;
01576             #if defined(WIN32)
01577             DWORD iBytesMoved = 1; // we have to pass it via pointer to the Windows API, thus the correct size must be ensured
01578             #else
01579             int iBytesMoved = 1; 
01580             #endif
01581             for (unsigned long ulPos = 0; iBytesMoved > 0; ulPos += iBytesMoved) {
01582                 const unsigned long ulToMove = ulFileSize - ulPos;
01583                 iBytesMoved = (ulToMove < 4096) ? ulToMove : 4096;
01584                 #if POSIX
01585                 lseek(hFileRead, ulPos, SEEK_SET);
01586                 iBytesMoved = read(hFileRead, pCopyBuffer, iBytesMoved);
01587                 lseek(hFileWrite, ulPos + ulPositiveSizeDiff, SEEK_SET);
01588                 iBytesMoved = write(hFileWrite, pCopyBuffer, iBytesMoved);
01589                 #elif defined(WIN32)
01590                 SetFilePointer(hFileRead, ulPos, NULL/*32 bit*/, FILE_BEGIN);
01591                 ReadFile(hFileRead, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
01592                 SetFilePointer(hFileWrite, ulPos + ulPositiveSizeDiff, NULL/*32 bit*/, FILE_BEGIN);
01593                 WriteFile(hFileWrite, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
01594                 #else
01595                 fseek(hFileRead, ulPos, SEEK_SET);
01596                 iBytesMoved = fread(pCopyBuffer, 1, iBytesMoved, hFileRead);
01597                 fseek(hFileWrite, ulPos + ulPositiveSizeDiff, SEEK_SET);
01598                 iBytesMoved = fwrite(pCopyBuffer, 1, iBytesMoved, hFileWrite);
01599                 #endif
01600             }
01601             delete[] pCopyBuffer;
01602             if (iBytesMoved < 0) throw Exception("Could not modify file while trying to enlarge it");
01603         }
01604 
01605         // rebuild / rewrite complete RIFF tree
01606         unsigned long ulTotalSize  = WriteChunk(0, ulPositiveSizeDiff);
01607         unsigned long ulActualSize = __GetFileSize(hFileWrite);
01608 
01609         // resize file to the final size
01610         if (ulTotalSize < ulActualSize) ResizeFile(ulTotalSize);
01611 
01612         // forget all resized chunks
01613         ResizedChunks.clear();
01614     }
01615 
01629     void File::Save(const String& path) {
01630         //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
01631 
01632         // make sure the RIFF tree is built (from the original file)
01633         LoadSubChunksRecursively();
01634 
01635         if (Filename.length() > 0) SetMode(stream_mode_read);
01636         // open the other (new) file for writing and truncate it to zero size
01637         #if POSIX
01638         hFileWrite = open(path.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP);
01639         if (hFileWrite < 0) {
01640             hFileWrite = hFileRead;
01641             throw Exception("Could not open file \"" + path + "\" for writing");
01642         }
01643         #elif defined(WIN32)
01644         hFileWrite = CreateFile(
01645                          path.c_str(), GENERIC_WRITE, FILE_SHARE_READ,
01646                          NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL
01647                      );
01648         if (hFileWrite == INVALID_HANDLE_VALUE) {
01649             hFileWrite = hFileRead;
01650             throw Exception("Could not open file \"" + path + "\" for writing");
01651         }
01652         #else
01653         hFileWrite = fopen(path.c_str(), "w+b");
01654         if (!hFileWrite) {
01655             hFileWrite = hFileRead;
01656             throw Exception("Could not open file \"" + path + "\" for writing");
01657         }
01658         #endif // POSIX
01659         Mode = stream_mode_read_write;
01660 
01661         // write complete RIFF tree to the other (new) file
01662         unsigned long ulTotalSize  = WriteChunk(0, 0);
01663         unsigned long ulActualSize = __GetFileSize(hFileWrite);
01664 
01665         // resize file to the final size (if the file was originally larger)
01666         if (ulTotalSize < ulActualSize) ResizeFile(ulTotalSize);
01667 
01668         // forget all resized chunks
01669         ResizedChunks.clear();
01670 
01671         if (Filename.length() > 0) {
01672             #if POSIX
01673             close(hFileWrite);
01674             #elif defined(WIN32)
01675             CloseHandle(hFileWrite);
01676             #else
01677             fclose(hFileWrite);
01678             #endif
01679             hFileWrite = hFileRead;
01680         }
01681 
01682         // associate new file with this File object from now on
01683         Filename = path;
01684         Mode = (stream_mode_t) -1;       // Just set it to an undefined mode ...
01685         SetMode(stream_mode_read_write); // ... so SetMode() has to reopen the file handles.
01686     }
01687 
01688     void File::ResizeFile(unsigned long ulNewSize) {
01689         #if POSIX
01690         if (ftruncate(hFileWrite, ulNewSize) < 0)
01691             throw Exception("Could not resize file \"" + Filename + "\"");
01692         #elif defined(WIN32)
01693         if (
01694             SetFilePointer(hFileWrite, ulNewSize, NULL/*32 bit*/, FILE_BEGIN) == INVALID_SET_FILE_POINTER ||
01695             !SetEndOfFile(hFileWrite)
01696         ) throw Exception("Could not resize file \"" + Filename + "\"");
01697         #else
01698         # error Sorry, this version of libgig only supports POSIX and Windows systems yet.
01699         # error Reason: portable implementation of RIFF::File::ResizeFile() is missing (yet)!
01700         #endif
01701     }
01702 
01703     File::~File() {
01704        #if DEBUG
01705        std::cout << "File::~File()" << std::endl;
01706        #endif // DEBUG
01707         #if POSIX
01708         if (hFileRead) close(hFileRead);
01709         #elif defined(WIN32)
01710         if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
01711         #else
01712         if (hFileRead) fclose(hFileRead);
01713         #endif // POSIX
01714     }
01715 
01716     void File::LogAsResized(Chunk* pResizedChunk) {
01717         ResizedChunks.push_back(pResizedChunk);
01718     }
01719 
01720     void File::UnlogResized(Chunk* pResizedChunk) {
01721         ResizedChunks.remove(pResizedChunk);
01722     }
01723 
01724     unsigned long File::GetFileSize() {
01725         return __GetFileSize(hFileRead);
01726     }
01727 
01728     #if POSIX
01729     unsigned long File::__GetFileSize(int hFile) {
01730         struct stat filestat;
01731         fstat(hFile, &filestat);
01732         long size = filestat.st_size;
01733         return size;
01734     }
01735     #elif defined(WIN32)
01736     unsigned long File::__GetFileSize(HANDLE hFile) {
01737         DWORD dwSize = ::GetFileSize(hFile, NULL /*32bit*/);
01738         if (dwSize == INVALID_FILE_SIZE)
01739             throw Exception("Windows FS error: could not determine file size");
01740         return dwSize;
01741     }
01742     #else // standard C functions
01743     unsigned long File::__GetFileSize(FILE* hFile) {
01744         long curpos = ftell(hFile);
01745         fseek(hFile, 0, SEEK_END);
01746         long size = ftell(hFile);
01747         fseek(hFile, curpos, SEEK_SET);
01748         return size;
01749     }
01750     #endif
01751 
01752 
01753 // *************** Exception ***************
01754 // *
01755 
01756     void Exception::PrintMessage() {
01757         std::cout << "RIFF::Exception: " << Message << std::endl;
01758     }
01759 
01760 
01761 // *************** functions ***************
01762 // *
01763 
01769     String libraryName() {
01770         return PACKAGE;
01771     }
01772 
01777     String libraryVersion() {
01778         return VERSION;
01779     }
01780 
01781 } // namespace RIFF

Generated on Mon Jul 2 14:59:22 2007 for libgig by  doxygen 1.5.1