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, NULL
01416                                  );
01417         if (hFileRead == INVALID_HANDLE_VALUE) {
01418             hFileRead = hFileWrite = INVALID_HANDLE_VALUE;
01419             throw RIFF::Exception("Can't open \"" + path + "\"");
01420         }
01421         #else
01422         hFileRead = hFileWrite = fopen(path.c_str(), "rb");
01423         if (!hFileRead) throw RIFF::Exception("Can't open \"" + path + "\"");
01424         #endif // POSIX
01425         Mode = stream_mode_read;
01426         ulStartPos = RIFF_HEADER_SIZE;
01427         ReadHeader(0);
01428         if (ChunkID != CHUNK_ID_RIFF) {
01429             throw RIFF::Exception("Not a RIFF file");
01430         }
01431     }
01432 
01433     String File::GetFileName() {
01434         return Filename;
01435     }
01436 
01437     stream_mode_t File::GetMode() {
01438         return Mode;
01439     }
01440 
01451     bool File::SetMode(stream_mode_t NewMode) {
01452         if (NewMode != Mode) {
01453             switch (NewMode) {
01454                 case stream_mode_read:
01455                     #if POSIX
01456                     if (hFileRead) close(hFileRead);
01457                     hFileRead = hFileWrite = open(Filename.c_str(), O_RDONLY | O_NONBLOCK);
01458                     if (hFileRead < 0) {
01459                         hFileRead = hFileWrite = 0;
01460                         throw Exception("Could not (re)open file \"" + Filename + "\" in read mode");
01461                     }
01462                     #elif defined(WIN32)
01463                     if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
01464                     hFileRead = hFileWrite = CreateFile(
01465                                                  Filename.c_str(), GENERIC_READ,
01466                                                  FILE_SHARE_READ | FILE_SHARE_WRITE,
01467                                                  NULL, OPEN_EXISTING,
01468                                                  FILE_ATTRIBUTE_NORMAL, NULL
01469                                              );
01470                     if (hFileRead == INVALID_HANDLE_VALUE) {
01471                         hFileRead = hFileWrite = INVALID_HANDLE_VALUE;
01472                         throw Exception("Could not (re)open file \"" + Filename + "\" in read mode");
01473                     }
01474                     #else
01475                     if (hFileRead) fclose(hFileRead);
01476                     hFileRead = hFileWrite = fopen(Filename.c_str(), "rb");
01477                     if (!hFileRead) throw Exception("Could not (re)open file \"" + Filename + "\" in read mode");
01478                     #endif
01479                     __resetPos(); // reset read/write position of ALL 'Chunk' objects
01480                     break;
01481                 case stream_mode_read_write:
01482                     #if POSIX
01483                     if (hFileRead) close(hFileRead);
01484                     hFileRead = hFileWrite = open(Filename.c_str(), O_RDWR | O_NONBLOCK);
01485                     if (hFileRead < 0) {
01486                         hFileRead = hFileWrite = open(Filename.c_str(), O_RDONLY | O_NONBLOCK);
01487                         throw Exception("Could not open file \"" + Filename + "\" in read+write mode");
01488                     }
01489                     #elif defined(WIN32)
01490                     if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
01491                     hFileRead = hFileWrite = CreateFile(
01492                                                  Filename.c_str(),
01493                                                  GENERIC_READ | GENERIC_WRITE,
01494                                                  FILE_SHARE_READ,
01495                                                  NULL, OPEN_ALWAYS,
01496                                                  FILE_ATTRIBUTE_NORMAL, NULL
01497                                              );
01498                     if (hFileRead == INVALID_HANDLE_VALUE) {
01499                         hFileRead = hFileWrite = CreateFile(
01500                                                      Filename.c_str(), GENERIC_READ,
01501                                                      FILE_SHARE_READ | FILE_SHARE_WRITE,
01502                                                      NULL, OPEN_EXISTING,
01503                                                      FILE_ATTRIBUTE_NORMAL, NULL
01504                                                  );
01505                         throw Exception("Could not (re)open file \"" + Filename + "\" in read+write mode");
01506                     }
01507                     #else
01508                     if (hFileRead) fclose(hFileRead);
01509                     hFileRead = hFileWrite = fopen(Filename.c_str(), "r+b");
01510                     if (!hFileRead) {
01511                         hFileRead = hFileWrite = fopen(Filename.c_str(), "rb");
01512                         throw Exception("Could not open file \"" + Filename + "\" in read+write mode");
01513                     }
01514                     #endif
01515                     __resetPos(); // reset read/write position of ALL 'Chunk' objects
01516                     break;
01517                 case stream_mode_closed:
01518                     #if POSIX
01519                     if (hFileRead)  close(hFileRead);
01520                     if (hFileWrite) close(hFileWrite);
01521                     #elif defined(WIN32)
01522                     if (hFileRead  != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
01523                     if (hFileWrite != INVALID_HANDLE_VALUE) CloseHandle(hFileWrite);
01524                     #else
01525                     if (hFileRead)  fclose(hFileRead);
01526                     if (hFileWrite) fclose(hFileWrite);
01527                     #endif
01528                     hFileRead = hFileWrite = 0;
01529                     break;
01530                 default:
01531                     throw Exception("Unknown file access mode");
01532             }
01533             Mode = NewMode;
01534             return true;
01535         }
01536         return false;
01537     }
01538 
01548     void File::SetByteOrder(endian_t Endian) {
01549         #if WORDS_BIGENDIAN
01550         bEndianNative = Endian != endian_little;
01551         #else
01552         bEndianNative = Endian != endian_big;
01553         #endif
01554     }
01555 
01566     void File::Save() {
01567         // make sure the RIFF tree is built (from the original file)
01568         LoadSubChunksRecursively();
01569 
01570         // reopen file in write mode
01571         SetMode(stream_mode_read_write);
01572 
01573         // to be able to save the whole file without loading everything into
01574         // RAM and without having to store the data in a temporary file, we
01575         // enlarge the file with the sum of all _positive_ chunk size
01576         // changes, move current data towards the end of the file with the
01577         // calculated sum and finally update / rewrite the file by copying
01578         // the old data back to the right position at the beginning of the file
01579 
01580         // first we sum up all positive chunk size changes (and skip all negative ones)
01581         unsigned long ulPositiveSizeDiff = 0;
01582         for (ChunkList::iterator iter = ResizedChunks.begin(), end = ResizedChunks.end(); iter != end; ++iter) {
01583             if ((*iter)->GetNewSize() == 0) {
01584                 throw Exception("There is at least one empty chunk (zero size): " + __resolveChunkPath(*iter));
01585             }
01586             if ((*iter)->GetNewSize() + 1L > (*iter)->GetSize()) {
01587                 unsigned long ulDiff = (*iter)->GetNewSize() - (*iter)->GetSize() + 1L; // +1 in case we have to add a pad byte
01588                 ulPositiveSizeDiff += ulDiff;
01589             }
01590         }
01591 
01592         unsigned long ulWorkingFileSize = GetFileSize();
01593 
01594         // if there are positive size changes...
01595         if (ulPositiveSizeDiff > 0) {
01596             // ... we enlarge this file first ...
01597             ulWorkingFileSize += ulPositiveSizeDiff;
01598             ResizeFile(ulWorkingFileSize);
01599             // ... and move current data by the same amount towards end of file.
01600             int8_t* pCopyBuffer = new int8_t[4096];
01601             const unsigned long ulFileSize = GetSize() + RIFF_HEADER_SIZE;
01602             #if defined(WIN32)
01603             DWORD iBytesMoved = 1; // we have to pass it via pointer to the Windows API, thus the correct size must be ensured
01604             #else
01605             int iBytesMoved = 1; 
01606             #endif
01607             for (unsigned long ulPos = 0; iBytesMoved > 0; ulPos += iBytesMoved) {
01608                 const unsigned long ulToMove = ulFileSize - ulPos;
01609                 iBytesMoved = (ulToMove < 4096) ? ulToMove : 4096;
01610                 #if POSIX
01611                 lseek(hFileRead, ulPos, SEEK_SET);
01612                 iBytesMoved = read(hFileRead, pCopyBuffer, iBytesMoved);
01613                 lseek(hFileWrite, ulPos + ulPositiveSizeDiff, SEEK_SET);
01614                 iBytesMoved = write(hFileWrite, pCopyBuffer, iBytesMoved);
01615                 #elif defined(WIN32)
01616                 SetFilePointer(hFileRead, ulPos, NULL/*32 bit*/, FILE_BEGIN);
01617                 ReadFile(hFileRead, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
01618                 SetFilePointer(hFileWrite, ulPos + ulPositiveSizeDiff, NULL/*32 bit*/, FILE_BEGIN);
01619                 WriteFile(hFileWrite, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
01620                 #else
01621                 fseek(hFileRead, ulPos, SEEK_SET);
01622                 iBytesMoved = fread(pCopyBuffer, 1, iBytesMoved, hFileRead);
01623                 fseek(hFileWrite, ulPos + ulPositiveSizeDiff, SEEK_SET);
01624                 iBytesMoved = fwrite(pCopyBuffer, 1, iBytesMoved, hFileWrite);
01625                 #endif
01626             }
01627             delete[] pCopyBuffer;
01628             if (iBytesMoved < 0) throw Exception("Could not modify file while trying to enlarge it");
01629         }
01630 
01631         // rebuild / rewrite complete RIFF tree
01632         unsigned long ulTotalSize  = WriteChunk(0, ulPositiveSizeDiff);
01633         unsigned long ulActualSize = __GetFileSize(hFileWrite);
01634 
01635         // resize file to the final size
01636         if (ulTotalSize < ulActualSize) ResizeFile(ulTotalSize);
01637 
01638         // forget all resized chunks
01639         ResizedChunks.clear();
01640     }
01641 
01655     void File::Save(const String& path) {
01656         //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
01657 
01658         // make sure the RIFF tree is built (from the original file)
01659         LoadSubChunksRecursively();
01660 
01661         if (Filename.length() > 0) SetMode(stream_mode_read);
01662         // open the other (new) file for writing and truncate it to zero size
01663         #if POSIX
01664         hFileWrite = open(path.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP);
01665         if (hFileWrite < 0) {
01666             hFileWrite = hFileRead;
01667             throw Exception("Could not open file \"" + path + "\" for writing");
01668         }
01669         #elif defined(WIN32)
01670         hFileWrite = CreateFile(
01671                          path.c_str(), GENERIC_WRITE, FILE_SHARE_READ,
01672                          NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL
01673                      );
01674         if (hFileWrite == INVALID_HANDLE_VALUE) {
01675             hFileWrite = hFileRead;
01676             throw Exception("Could not open file \"" + path + "\" for writing");
01677         }
01678         #else
01679         hFileWrite = fopen(path.c_str(), "w+b");
01680         if (!hFileWrite) {
01681             hFileWrite = hFileRead;
01682             throw Exception("Could not open file \"" + path + "\" for writing");
01683         }
01684         #endif // POSIX
01685         Mode = stream_mode_read_write;
01686 
01687         // write complete RIFF tree to the other (new) file
01688         unsigned long ulTotalSize  = WriteChunk(0, 0);
01689         unsigned long ulActualSize = __GetFileSize(hFileWrite);
01690 
01691         // resize file to the final size (if the file was originally larger)
01692         if (ulTotalSize < ulActualSize) ResizeFile(ulTotalSize);
01693 
01694         // forget all resized chunks
01695         ResizedChunks.clear();
01696 
01697         #if POSIX
01698         if (hFileWrite) close(hFileWrite);
01699         #elif defined(WIN32)
01700         if (hFileWrite != INVALID_HANDLE_VALUE) CloseHandle(hFileWrite);
01701         #else
01702         if (hFileWrite) fclose(hFileWrite);
01703         #endif
01704         hFileWrite = hFileRead;
01705 
01706         // associate new file with this File object from now on
01707         Filename = path;
01708         Mode = (stream_mode_t) -1;       // Just set it to an undefined mode ...
01709         SetMode(stream_mode_read_write); // ... so SetMode() has to reopen the file handles.
01710     }
01711 
01712     void File::ResizeFile(unsigned long ulNewSize) {
01713         #if POSIX
01714         if (ftruncate(hFileWrite, ulNewSize) < 0)
01715             throw Exception("Could not resize file \"" + Filename + "\"");
01716         #elif defined(WIN32)
01717         if (
01718             SetFilePointer(hFileWrite, ulNewSize, NULL/*32 bit*/, FILE_BEGIN) == INVALID_SET_FILE_POINTER ||
01719             !SetEndOfFile(hFileWrite)
01720         ) throw Exception("Could not resize file \"" + Filename + "\"");
01721         #else
01722         # error Sorry, this version of libgig only supports POSIX and Windows systems yet.
01723         # error Reason: portable implementation of RIFF::File::ResizeFile() is missing (yet)!
01724         #endif
01725     }
01726 
01727     File::~File() {
01728        #if DEBUG
01729        std::cout << "File::~File()" << std::endl;
01730        #endif // DEBUG
01731         #if POSIX
01732         if (hFileRead) close(hFileRead);
01733         #elif defined(WIN32)
01734         if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
01735         #else
01736         if (hFileRead) fclose(hFileRead);
01737         #endif // POSIX
01738     }
01739 
01740     void File::LogAsResized(Chunk* pResizedChunk) {
01741         ResizedChunks.push_back(pResizedChunk);
01742     }
01743 
01744     void File::UnlogResized(Chunk* pResizedChunk) {
01745         ResizedChunks.remove(pResizedChunk);
01746     }
01747 
01748     unsigned long File::GetFileSize() {
01749         return __GetFileSize(hFileRead);
01750     }
01751 
01752     #if POSIX
01753     unsigned long File::__GetFileSize(int hFile) {
01754         struct stat filestat;
01755         fstat(hFile, &filestat);
01756         long size = filestat.st_size;
01757         return size;
01758     }
01759     #elif defined(WIN32)
01760     unsigned long File::__GetFileSize(HANDLE hFile) {
01761         DWORD dwSize = ::GetFileSize(hFile, NULL /*32bit*/);
01762         if (dwSize == INVALID_FILE_SIZE)
01763             throw Exception("Windows FS error: could not determine file size");
01764         return dwSize;
01765     }
01766     #else // standard C functions
01767     unsigned long File::__GetFileSize(FILE* hFile) {
01768         long curpos = ftell(hFile);
01769         fseek(hFile, 0, SEEK_END);
01770         long size = ftell(hFile);
01771         fseek(hFile, curpos, SEEK_SET);
01772         return size;
01773     }
01774     #endif
01775 
01776 
01777 // *************** Exception ***************
01778 // *
01779 
01780     void Exception::PrintMessage() {
01781         std::cout << "RIFF::Exception: " << Message << std::endl;
01782     }
01783 
01784 
01785 // *************** functions ***************
01786 // *
01787 
01793     String libraryName() {
01794         return PACKAGE;
01795     }
01796 
01801     String libraryVersion() {
01802         return VERSION;
01803     }
01804 
01805 } // namespace RIFF

Generated on Mon Mar 10 17:07:57 2008 for libgig by  doxygen 1.4.7