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     static 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 defined(WIN32)
01272             if (pFile->hFileRead == INVALID_HANDLE_VALUE) return;
01273             #else
01274             if (!pFile->hFileRead) return;
01275             #endif
01276             unsigned long uiOriginalPos = GetPos();
01277             SetPos(0); // jump to beginning of list chunk body
01278             while (RemainingBytes() >= CHUNK_HEADER_SIZE) {
01279                 Chunk* ck;
01280                 uint32_t ckid;
01281                 Read(&ckid, 4, 1);
01282        #if DEBUG
01283        std::cout << " ckid=" << convertToString(ckid) << std::endl;
01284        #endif // DEBUG
01285                 if (ckid == CHUNK_ID_LIST) {
01286                     ck = new RIFF::List(pFile, ulStartPos + ulPos - 4, this);
01287                     SetPos(ck->GetSize() + LIST_HEADER_SIZE - 4, RIFF::stream_curpos);
01288                 }
01289                 else { // simple chunk
01290                     ck = new RIFF::Chunk(pFile, ulStartPos + ulPos - 4, this);
01291                     SetPos(ck->GetSize() + CHUNK_HEADER_SIZE - 4, RIFF::stream_curpos);
01292                 }
01293                 pSubChunks->push_back(ck);
01294                 (*pSubChunksMap)[ckid] = ck;
01295                 if (GetPos() % 2 != 0) SetPos(1, RIFF::stream_curpos); // jump over pad byte
01296             }
01297             SetPos(uiOriginalPos); // restore position before this call
01298         }
01299     }
01300 
01301     void List::LoadSubChunksRecursively() {
01302         for (List* pList = GetFirstSubList(); pList; pList = GetNextSubList())
01303             pList->LoadSubChunksRecursively();
01304     }
01305 
01320     unsigned long List::WriteChunk(unsigned long ulWritePos, unsigned long ulCurrentDataOffset) {
01321         const unsigned long ulOriginalPos = ulWritePos;
01322         ulWritePos += LIST_HEADER_SIZE;
01323 
01324         if (pFile->Mode != stream_mode_read_write)
01325             throw Exception("Cannot write list chunk, file has to be opened in read+write mode");
01326 
01327         // write all subchunks (including sub list chunks) recursively
01328         if (pSubChunks) {
01329             for (ChunkList::iterator iter = pSubChunks->begin(), end = pSubChunks->end(); iter != end; ++iter) {
01330                 ulWritePos = (*iter)->WriteChunk(ulWritePos, ulCurrentDataOffset);
01331             }
01332         }
01333 
01334         // update this list chunk's header
01335         CurrentChunkSize = NewChunkSize = ulWritePos - ulOriginalPos - LIST_HEADER_SIZE;
01336         WriteHeader(ulOriginalPos);
01337 
01338         // offset of this list chunk in new written file may have changed
01339         ulStartPos = ulOriginalPos + LIST_HEADER_SIZE;
01340 
01341         return ulWritePos;
01342     }
01343 
01344     void List::__resetPos() {
01345         Chunk::__resetPos();
01346         if (pSubChunks) {
01347             for (ChunkList::iterator iter = pSubChunks->begin(), end = pSubChunks->end(); iter != end; ++iter) {
01348                 (*iter)->__resetPos();
01349             }
01350         }
01351     }
01352 
01356     String List::GetListTypeString() {
01357         return convertToString(ListType);
01358     }
01359 
01360 
01361 
01362 // *************** File ***************
01363 // *
01364 
01379     File::File(uint32_t FileType) : List(this) {
01380         #if defined(WIN32)
01381         hFileRead = hFileWrite = INVALID_HANDLE_VALUE;
01382         #else
01383         hFileRead = hFileWrite = 0;
01384         #endif
01385         Mode = stream_mode_closed;
01386         bEndianNative = true;
01387         ulStartPos = RIFF_HEADER_SIZE;
01388         ListType = FileType;
01389     }
01390 
01399     File::File(const String& path) : List(this), Filename(path) {
01400       #if DEBUG
01401       std::cout << "File::File("<<path<<")" << std::endl;
01402       #endif // DEBUG
01403         bEndianNative = true;
01404         #if POSIX
01405         hFileRead = hFileWrite = open(path.c_str(), O_RDONLY | O_NONBLOCK);
01406         if (hFileRead <= 0) {
01407             hFileRead = hFileWrite = 0;
01408             throw RIFF::Exception("Can't open \"" + path + "\"");
01409         }
01410         #elif defined(WIN32)
01411         hFileRead = hFileWrite = CreateFile(
01412                                      path.c_str(), GENERIC_READ,
01413                                      FILE_SHARE_READ | FILE_SHARE_WRITE,
01414                                      NULL, OPEN_EXISTING,
01415                                      FILE_ATTRIBUTE_NORMAL |
01416                                      FILE_FLAG_RANDOM_ACCESS, NULL
01417                                  );
01418         if (hFileRead == INVALID_HANDLE_VALUE) {
01419             hFileRead = hFileWrite = INVALID_HANDLE_VALUE;
01420             throw RIFF::Exception("Can't open \"" + path + "\"");
01421         }
01422         #else
01423         hFileRead = hFileWrite = fopen(path.c_str(), "rb");
01424         if (!hFileRead) throw RIFF::Exception("Can't open \"" + path + "\"");
01425         #endif // POSIX
01426         Mode = stream_mode_read;
01427         ulStartPos = RIFF_HEADER_SIZE;
01428         ReadHeader(0);
01429         if (ChunkID != CHUNK_ID_RIFF) {
01430             throw RIFF::Exception("Not a RIFF file");
01431         }
01432     }
01433 
01434     String File::GetFileName() {
01435         return Filename;
01436     }
01437 
01438     stream_mode_t File::GetMode() {
01439         return Mode;
01440     }
01441 
01452     bool File::SetMode(stream_mode_t NewMode) {
01453         if (NewMode != Mode) {
01454             switch (NewMode) {
01455                 case stream_mode_read:
01456                     #if POSIX
01457                     if (hFileRead) close(hFileRead);
01458                     hFileRead = hFileWrite = open(Filename.c_str(), O_RDONLY | O_NONBLOCK);
01459                     if (hFileRead < 0) {
01460                         hFileRead = hFileWrite = 0;
01461                         throw Exception("Could not (re)open file \"" + Filename + "\" in read mode");
01462                     }
01463                     #elif defined(WIN32)
01464                     if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
01465                     hFileRead = hFileWrite = CreateFile(
01466                                                  Filename.c_str(), GENERIC_READ,
01467                                                  FILE_SHARE_READ | FILE_SHARE_WRITE,
01468                                                  NULL, OPEN_EXISTING,
01469                                                  FILE_ATTRIBUTE_NORMAL |
01470                                                  FILE_FLAG_RANDOM_ACCESS,
01471                                                  NULL
01472                                              );
01473                     if (hFileRead == INVALID_HANDLE_VALUE) {
01474                         hFileRead = hFileWrite = INVALID_HANDLE_VALUE;
01475                         throw Exception("Could not (re)open file \"" + Filename + "\" in read mode");
01476                     }
01477                     #else
01478                     if (hFileRead) fclose(hFileRead);
01479                     hFileRead = hFileWrite = fopen(Filename.c_str(), "rb");
01480                     if (!hFileRead) throw Exception("Could not (re)open file \"" + Filename + "\" in read mode");
01481                     #endif
01482                     __resetPos(); // reset read/write position of ALL 'Chunk' objects
01483                     break;
01484                 case stream_mode_read_write:
01485                     #if POSIX
01486                     if (hFileRead) close(hFileRead);
01487                     hFileRead = hFileWrite = open(Filename.c_str(), O_RDWR | O_NONBLOCK);
01488                     if (hFileRead < 0) {
01489                         hFileRead = hFileWrite = open(Filename.c_str(), O_RDONLY | O_NONBLOCK);
01490                         throw Exception("Could not open file \"" + Filename + "\" in read+write mode");
01491                     }
01492                     #elif defined(WIN32)
01493                     if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
01494                     hFileRead = hFileWrite = CreateFile(
01495                                                  Filename.c_str(),
01496                                                  GENERIC_READ | GENERIC_WRITE,
01497                                                  FILE_SHARE_READ,
01498                                                  NULL, OPEN_ALWAYS,
01499                                                  FILE_ATTRIBUTE_NORMAL |
01500                                                  FILE_FLAG_RANDOM_ACCESS,
01501                                                  NULL
01502                                              );
01503                     if (hFileRead == INVALID_HANDLE_VALUE) {
01504                         hFileRead = hFileWrite = CreateFile(
01505                                                      Filename.c_str(), GENERIC_READ,
01506                                                      FILE_SHARE_READ | FILE_SHARE_WRITE,
01507                                                      NULL, OPEN_EXISTING,
01508                                                      FILE_ATTRIBUTE_NORMAL |
01509                                                      FILE_FLAG_RANDOM_ACCESS,
01510                                                      NULL
01511                                                  );
01512                         throw Exception("Could not (re)open file \"" + Filename + "\" in read+write mode");
01513                     }
01514                     #else
01515                     if (hFileRead) fclose(hFileRead);
01516                     hFileRead = hFileWrite = fopen(Filename.c_str(), "r+b");
01517                     if (!hFileRead) {
01518                         hFileRead = hFileWrite = fopen(Filename.c_str(), "rb");
01519                         throw Exception("Could not open file \"" + Filename + "\" in read+write mode");
01520                     }
01521                     #endif
01522                     __resetPos(); // reset read/write position of ALL 'Chunk' objects
01523                     break;
01524                 case stream_mode_closed:
01525                     #if POSIX
01526                     if (hFileRead)  close(hFileRead);
01527                     if (hFileWrite) close(hFileWrite);
01528                     #elif defined(WIN32)
01529                     if (hFileRead  != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
01530                     if (hFileWrite != INVALID_HANDLE_VALUE) CloseHandle(hFileWrite);
01531                     #else
01532                     if (hFileRead)  fclose(hFileRead);
01533                     if (hFileWrite) fclose(hFileWrite);
01534                     #endif
01535                     hFileRead = hFileWrite = 0;
01536                     break;
01537                 default:
01538                     throw Exception("Unknown file access mode");
01539             }
01540             Mode = NewMode;
01541             return true;
01542         }
01543         return false;
01544     }
01545 
01555     void File::SetByteOrder(endian_t Endian) {
01556         #if WORDS_BIGENDIAN
01557         bEndianNative = Endian != endian_little;
01558         #else
01559         bEndianNative = Endian != endian_big;
01560         #endif
01561     }
01562 
01573     void File::Save() {
01574         // make sure the RIFF tree is built (from the original file)
01575         LoadSubChunksRecursively();
01576 
01577         // reopen file in write mode
01578         SetMode(stream_mode_read_write);
01579 
01580         // to be able to save the whole file without loading everything into
01581         // RAM and without having to store the data in a temporary file, we
01582         // enlarge the file with the sum of all _positive_ chunk size
01583         // changes, move current data towards the end of the file with the
01584         // calculated sum and finally update / rewrite the file by copying
01585         // the old data back to the right position at the beginning of the file
01586 
01587         // first we sum up all positive chunk size changes (and skip all negative ones)
01588         unsigned long ulPositiveSizeDiff = 0;
01589         for (ChunkList::iterator iter = ResizedChunks.begin(), end = ResizedChunks.end(); iter != end; ++iter) {
01590             if ((*iter)->GetNewSize() == 0) {
01591                 throw Exception("There is at least one empty chunk (zero size): " + __resolveChunkPath(*iter));
01592             }
01593             if ((*iter)->GetNewSize() + 1L > (*iter)->GetSize()) {
01594                 unsigned long ulDiff = (*iter)->GetNewSize() - (*iter)->GetSize() + 1L; // +1 in case we have to add a pad byte
01595                 ulPositiveSizeDiff += ulDiff;
01596             }
01597         }
01598 
01599         unsigned long ulWorkingFileSize = GetFileSize();
01600 
01601         // if there are positive size changes...
01602         if (ulPositiveSizeDiff > 0) {
01603             // ... we enlarge this file first ...
01604             ulWorkingFileSize += ulPositiveSizeDiff;
01605             ResizeFile(ulWorkingFileSize);
01606             // ... and move current data by the same amount towards end of file.
01607             int8_t* pCopyBuffer = new int8_t[4096];
01608             const unsigned long ulFileSize = GetSize() + RIFF_HEADER_SIZE;
01609             #if defined(WIN32)
01610             DWORD iBytesMoved = 1; // we have to pass it via pointer to the Windows API, thus the correct size must be ensured
01611             #else
01612             int iBytesMoved = 1;
01613             #endif
01614             for (unsigned long ulPos = 0; iBytesMoved > 0; ulPos += iBytesMoved) {
01615                 const unsigned long ulToMove = ulFileSize - ulPos;
01616                 iBytesMoved = (ulToMove < 4096) ? ulToMove : 4096;
01617                 #if POSIX
01618                 lseek(hFileRead, ulPos, SEEK_SET);
01619                 iBytesMoved = read(hFileRead, pCopyBuffer, iBytesMoved);
01620                 lseek(hFileWrite, ulPos + ulPositiveSizeDiff, SEEK_SET);
01621                 iBytesMoved = write(hFileWrite, pCopyBuffer, iBytesMoved);
01622                 #elif defined(WIN32)
01623                 SetFilePointer(hFileRead, ulPos, NULL/*32 bit*/, FILE_BEGIN);
01624                 ReadFile(hFileRead, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
01625                 SetFilePointer(hFileWrite, ulPos + ulPositiveSizeDiff, NULL/*32 bit*/, FILE_BEGIN);
01626                 WriteFile(hFileWrite, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
01627                 #else
01628                 fseek(hFileRead, ulPos, SEEK_SET);
01629                 iBytesMoved = fread(pCopyBuffer, 1, iBytesMoved, hFileRead);
01630                 fseek(hFileWrite, ulPos + ulPositiveSizeDiff, SEEK_SET);
01631                 iBytesMoved = fwrite(pCopyBuffer, 1, iBytesMoved, hFileWrite);
01632                 #endif
01633             }
01634             delete[] pCopyBuffer;
01635             if (iBytesMoved < 0) throw Exception("Could not modify file while trying to enlarge it");
01636         }
01637 
01638         // rebuild / rewrite complete RIFF tree
01639         unsigned long ulTotalSize  = WriteChunk(0, ulPositiveSizeDiff);
01640         unsigned long ulActualSize = __GetFileSize(hFileWrite);
01641 
01642         // resize file to the final size
01643         if (ulTotalSize < ulActualSize) ResizeFile(ulTotalSize);
01644 
01645         // forget all resized chunks
01646         ResizedChunks.clear();
01647     }
01648 
01662     void File::Save(const String& path) {
01663         //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
01664 
01665         // make sure the RIFF tree is built (from the original file)
01666         LoadSubChunksRecursively();
01667 
01668         if (Filename.length() > 0) SetMode(stream_mode_read);
01669         // open the other (new) file for writing and truncate it to zero size
01670         #if POSIX
01671         hFileWrite = open(path.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP);
01672         if (hFileWrite < 0) {
01673             hFileWrite = hFileRead;
01674             throw Exception("Could not open file \"" + path + "\" for writing");
01675         }
01676         #elif defined(WIN32)
01677         hFileWrite = CreateFile(
01678                          path.c_str(), GENERIC_WRITE, FILE_SHARE_READ,
01679                          NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL |
01680                          FILE_FLAG_RANDOM_ACCESS, NULL
01681                      );
01682         if (hFileWrite == INVALID_HANDLE_VALUE) {
01683             hFileWrite = hFileRead;
01684             throw Exception("Could not open file \"" + path + "\" for writing");
01685         }
01686         #else
01687         hFileWrite = fopen(path.c_str(), "w+b");
01688         if (!hFileWrite) {
01689             hFileWrite = hFileRead;
01690             throw Exception("Could not open file \"" + path + "\" for writing");
01691         }
01692         #endif // POSIX
01693         Mode = stream_mode_read_write;
01694 
01695         // write complete RIFF tree to the other (new) file
01696         unsigned long ulTotalSize  = WriteChunk(0, 0);
01697         unsigned long ulActualSize = __GetFileSize(hFileWrite);
01698 
01699         // resize file to the final size (if the file was originally larger)
01700         if (ulTotalSize < ulActualSize) ResizeFile(ulTotalSize);
01701 
01702         // forget all resized chunks
01703         ResizedChunks.clear();
01704 
01705         #if POSIX
01706         if (hFileWrite) close(hFileWrite);
01707         #elif defined(WIN32)
01708         if (hFileWrite != INVALID_HANDLE_VALUE) CloseHandle(hFileWrite);
01709         #else
01710         if (hFileWrite) fclose(hFileWrite);
01711         #endif
01712         hFileWrite = hFileRead;
01713 
01714         // associate new file with this File object from now on
01715         Filename = path;
01716         Mode = (stream_mode_t) -1;       // Just set it to an undefined mode ...
01717         SetMode(stream_mode_read_write); // ... so SetMode() has to reopen the file handles.
01718     }
01719 
01720     void File::ResizeFile(unsigned long ulNewSize) {
01721         #if POSIX
01722         if (ftruncate(hFileWrite, ulNewSize) < 0)
01723             throw Exception("Could not resize file \"" + Filename + "\"");
01724         #elif defined(WIN32)
01725         if (
01726             SetFilePointer(hFileWrite, ulNewSize, NULL/*32 bit*/, FILE_BEGIN) == INVALID_SET_FILE_POINTER ||
01727             !SetEndOfFile(hFileWrite)
01728         ) throw Exception("Could not resize file \"" + Filename + "\"");
01729         #else
01730         # error Sorry, this version of libgig only supports POSIX and Windows systems yet.
01731         # error Reason: portable implementation of RIFF::File::ResizeFile() is missing (yet)!
01732         #endif
01733     }
01734 
01735     File::~File() {
01736        #if DEBUG
01737        std::cout << "File::~File()" << std::endl;
01738        #endif // DEBUG
01739         #if POSIX
01740         if (hFileRead) close(hFileRead);
01741         #elif defined(WIN32)
01742         if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
01743         #else
01744         if (hFileRead) fclose(hFileRead);
01745         #endif // POSIX
01746     }
01747 
01748     void File::LogAsResized(Chunk* pResizedChunk) {
01749         ResizedChunks.push_back(pResizedChunk);
01750     }
01751 
01752     void File::UnlogResized(Chunk* pResizedChunk) {
01753         ResizedChunks.remove(pResizedChunk);
01754     }
01755 
01756     unsigned long File::GetFileSize() {
01757         return __GetFileSize(hFileRead);
01758     }
01759 
01760     #if POSIX
01761     unsigned long File::__GetFileSize(int hFile) {
01762         struct stat filestat;
01763         fstat(hFile, &filestat);
01764         long size = filestat.st_size;
01765         return size;
01766     }
01767     #elif defined(WIN32)
01768     unsigned long File::__GetFileSize(HANDLE hFile) {
01769         DWORD dwSize = ::GetFileSize(hFile, NULL /*32bit*/);
01770         if (dwSize == INVALID_FILE_SIZE)
01771             throw Exception("Windows FS error: could not determine file size");
01772         return dwSize;
01773     }
01774     #else // standard C functions
01775     unsigned long File::__GetFileSize(FILE* hFile) {
01776         long curpos = ftell(hFile);
01777         fseek(hFile, 0, SEEK_END);
01778         long size = ftell(hFile);
01779         fseek(hFile, curpos, SEEK_SET);
01780         return size;
01781     }
01782     #endif
01783 
01784 
01785 // *************** Exception ***************
01786 // *
01787 
01788     void Exception::PrintMessage() {
01789         std::cout << "RIFF::Exception: " << Message << std::endl;
01790     }
01791 
01792 
01793 // *************** functions ***************
01794 // *
01795 
01801     String libraryName() {
01802         return PACKAGE;
01803     }
01804 
01809     String libraryVersion() {
01810         return VERSION;
01811     }
01812 
01813 } // namespace RIFF

Generated on Tue Dec 18 07:51:26 2007 for libgig by  doxygen 1.5.4