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

Generated on Tue Jul 8 10:08:18 2008 for libgig by  doxygen 1.4.7