libgig 3.3.0

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-2009 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 <set>
00026 #include <string.h>
00027 
00028 #include "RIFF.h"
00029 
00030 #include "helper.h"
00031 
00032 namespace RIFF {
00033 
00034 // *************** Internal functions **************
00035 // *
00036 
00038     static String __resolveChunkPath(Chunk* pCk) {
00039         String sPath;
00040         for (Chunk* pChunk = pCk; pChunk; pChunk = pChunk->GetParent()) {
00041             if (pChunk->GetChunkID() == CHUNK_ID_LIST) {
00042                 List* pList = (List*) pChunk;
00043                 sPath = "->'" + pList->GetListTypeString() + "'" + sPath;
00044             } else {
00045                 sPath = "->'" + pChunk->GetChunkIDString() + "'" + sPath;
00046             }
00047         }
00048         return sPath;
00049     }
00050 
00051 
00052 
00053 // *************** Chunk **************
00054 // *
00055 
00056     Chunk::Chunk(File* pFile) {
00057         #if DEBUG
00058         std::cout << "Chunk::Chunk(File* pFile)" << std::endl;
00059         #endif // DEBUG
00060         ulPos      = 0;
00061         pParent    = NULL;
00062         pChunkData = NULL;
00063         CurrentChunkSize = 0;
00064         NewChunkSize = 0;
00065         ulChunkDataSize = 0;
00066         ChunkID    = CHUNK_ID_RIFF;
00067         this->pFile = pFile;
00068     }
00069 
00070     Chunk::Chunk(File* pFile, unsigned long StartPos, List* Parent) {
00071         #if DEBUG
00072         std::cout << "Chunk::Chunk(File*,ulong,bool,List*),StartPos=" << StartPos << std::endl;
00073         #endif // DEBUG
00074         this->pFile   = pFile;
00075         ulStartPos    = StartPos + CHUNK_HEADER_SIZE;
00076         pParent       = Parent;
00077         ulPos         = 0;
00078         pChunkData    = NULL;
00079         CurrentChunkSize = 0;
00080         NewChunkSize = 0;
00081         ulChunkDataSize = 0;
00082         ReadHeader(StartPos);
00083     }
00084 
00085     Chunk::Chunk(File* pFile, List* pParent, uint32_t uiChunkID, uint uiBodySize) {
00086         this->pFile      = pFile;
00087         ulStartPos       = 0; // arbitrary usually, since it will be updated when we write the chunk
00088         this->pParent    = pParent;
00089         ulPos            = 0;
00090         pChunkData       = NULL;
00091         ChunkID          = uiChunkID;
00092         ulChunkDataSize  = 0;
00093         CurrentChunkSize = 0;
00094         NewChunkSize     = uiBodySize;
00095     }
00096 
00097     Chunk::~Chunk() {
00098         if (pFile) pFile->UnlogResized(this);
00099         if (pChunkData) delete[] pChunkData;
00100     }
00101 
00102     void Chunk::ReadHeader(unsigned long fPos) {
00103         #if DEBUG
00104         std::cout << "Chunk::Readheader(" << fPos << ") ";
00105         #endif // DEBUG
00106         ChunkID = 0;
00107         NewChunkSize = CurrentChunkSize = 0;
00108         #if POSIX
00109         if (lseek(pFile->hFileRead, fPos, SEEK_SET) != -1) {
00110             read(pFile->hFileRead, &ChunkID, 4);
00111             read(pFile->hFileRead, &CurrentChunkSize, 4);
00112         #elif defined(WIN32)
00113         if (SetFilePointer(pFile->hFileRead, fPos, NULL/*32 bit*/, FILE_BEGIN) != INVALID_SET_FILE_POINTER) {
00114             DWORD dwBytesRead;
00115             ReadFile(pFile->hFileRead, &ChunkID, 4, &dwBytesRead, NULL);
00116             ReadFile(pFile->hFileRead, &CurrentChunkSize, 4, &dwBytesRead, NULL);
00117         #else
00118         if (!fseek(pFile->hFileRead, fPos, SEEK_SET)) {
00119             fread(&ChunkID, 4, 1, pFile->hFileRead);
00120             fread(&CurrentChunkSize, 4, 1, pFile->hFileRead);
00121         #endif // POSIX
00122             #if WORDS_BIGENDIAN
00123             if (ChunkID == CHUNK_ID_RIFF) {
00124                 pFile->bEndianNative = false;
00125             }
00126             #else // little endian
00127             if (ChunkID == CHUNK_ID_RIFX) {
00128                 pFile->bEndianNative = false;
00129                 ChunkID = CHUNK_ID_RIFF;
00130             }
00131             #endif // WORDS_BIGENDIAN
00132             if (!pFile->bEndianNative) {
00133                 //swapBytes_32(&ChunkID);
00134                 swapBytes_32(&CurrentChunkSize);
00135             }
00136             #if DEBUG
00137             std::cout << "ckID=" << convertToString(ChunkID) << " ";
00138             std::cout << "ckSize=" << CurrentChunkSize << " ";
00139             std::cout << "bEndianNative=" << pFile->bEndianNative << std::endl;
00140             #endif // DEBUG
00141             NewChunkSize = CurrentChunkSize;
00142         }
00143     }
00144 
00145     void Chunk::WriteHeader(unsigned long fPos) {
00146         uint32_t uiNewChunkID = ChunkID;
00147         if (ChunkID == CHUNK_ID_RIFF) {
00148             #if WORDS_BIGENDIAN
00149             if (pFile->bEndianNative) uiNewChunkID = CHUNK_ID_RIFX;
00150             #else // little endian
00151             if (!pFile->bEndianNative) uiNewChunkID = CHUNK_ID_RIFX;
00152             #endif // WORDS_BIGENDIAN
00153         }
00154 
00155         uint32_t uiNewChunkSize = NewChunkSize;
00156         if (!pFile->bEndianNative) {
00157             swapBytes_32(&uiNewChunkSize);
00158         }
00159 
00160         #if POSIX
00161         if (lseek(pFile->hFileWrite, fPos, SEEK_SET) != -1) {
00162             write(pFile->hFileWrite, &uiNewChunkID, 4);
00163             write(pFile->hFileWrite, &uiNewChunkSize, 4);
00164         }
00165         #elif defined(WIN32)
00166         if (SetFilePointer(pFile->hFileWrite, fPos, NULL/*32 bit*/, FILE_BEGIN) != INVALID_SET_FILE_POINTER) {
00167             DWORD dwBytesWritten;
00168             WriteFile(pFile->hFileWrite, &uiNewChunkID, 4, &dwBytesWritten, NULL);
00169             WriteFile(pFile->hFileWrite, &uiNewChunkSize, 4, &dwBytesWritten, NULL);
00170         }
00171         #else
00172         if (!fseek(pFile->hFileWrite, fPos, SEEK_SET)) {
00173             fwrite(&uiNewChunkID, 4, 1, pFile->hFileWrite);
00174             fwrite(&uiNewChunkSize, 4, 1, pFile->hFileWrite);
00175         }
00176         #endif // POSIX
00177     }
00178 
00183     String Chunk::GetChunkIDString() {
00184         return convertToString(ChunkID);
00185     }
00186 
00199     unsigned long Chunk::SetPos(unsigned long Where, stream_whence_t Whence) {
00200      #if DEBUG
00201      std::cout << "Chunk::SetPos(ulong)" << std::endl;
00202      #endif // DEBUG
00203         switch (Whence) {
00204             case stream_curpos:
00205                 ulPos += Where;
00206                 break;
00207             case stream_end:
00208                 ulPos = CurrentChunkSize - 1 - Where;
00209                 break;
00210             case stream_backward:
00211                 ulPos -= Where;
00212                 break;
00213             case stream_start: default:
00214                 ulPos = Where;
00215                 break;
00216         }
00217         if (ulPos > CurrentChunkSize) ulPos = CurrentChunkSize;
00218         return ulPos;
00219     }
00220 
00231     unsigned long Chunk::RemainingBytes() {
00232        #if DEBUG
00233        std::cout << "Chunk::Remainingbytes()=" << CurrentChunkSize - ulPos << std::endl;
00234        #endif // DEBUG
00235         return (CurrentChunkSize > ulPos) ? CurrentChunkSize - ulPos : 0;
00236     }
00237 
00249     stream_state_t Chunk::GetState() {
00250       #if DEBUG
00251       std::cout << "Chunk::GetState()" << std::endl;
00252       #endif // DEBUG
00253         #if POSIX
00254         if (pFile->hFileRead == 0) return stream_closed;
00255         #elif defined (WIN32)
00256         if (pFile->hFileRead == INVALID_HANDLE_VALUE)
00257             return stream_closed;
00258         #else
00259         if (pFile->hFileRead == NULL) return stream_closed;
00260         #endif // POSIX
00261         if (ulPos < CurrentChunkSize) return stream_ready;
00262         else                          return stream_end_reached;
00263     }
00264 
00280     unsigned long Chunk::Read(void* pData, unsigned long WordCount, unsigned long WordSize) {
00281        #if DEBUG
00282        std::cout << "Chunk::Read(void*,ulong,ulong)" << std::endl;
00283        #endif // DEBUG
00284         if (ulStartPos == 0) return 0; // is only 0 if this is a new chunk, so nothing to read (yet)
00285         if (ulPos >= CurrentChunkSize) return 0;
00286         if (ulPos + WordCount * WordSize >= CurrentChunkSize) WordCount = (CurrentChunkSize - ulPos) / WordSize;
00287         #if POSIX
00288         if (lseek(pFile->hFileRead, ulStartPos + ulPos, SEEK_SET) < 0) return 0;
00289         unsigned long readWords = read(pFile->hFileRead, pData, WordCount * WordSize);
00290         if (readWords < 1) return 0;
00291         readWords /= WordSize;
00292         #elif defined(WIN32)
00293         if (SetFilePointer(pFile->hFileRead, ulStartPos + ulPos, NULL/*32 bit*/, FILE_BEGIN) == INVALID_SET_FILE_POINTER) return 0;
00294         DWORD readWords;
00295         ReadFile(pFile->hFileRead, pData, WordCount * WordSize, &readWords, NULL);
00296         if (readWords < 1) return 0;
00297         readWords /= WordSize;
00298         #else // standard C functions
00299         if (fseek(pFile->hFileRead, ulStartPos + ulPos, SEEK_SET)) return 0;
00300         unsigned long readWords = fread(pData, WordSize, WordCount, pFile->hFileRead);
00301         #endif // POSIX
00302         if (!pFile->bEndianNative && WordSize != 1) {
00303             switch (WordSize) {
00304                 case 2:
00305                     for (unsigned long iWord = 0; iWord < readWords; iWord++)
00306                         swapBytes_16((uint16_t*) pData + iWord);
00307                     break;
00308                 case 4:
00309                     for (unsigned long iWord = 0; iWord < readWords; iWord++)
00310                         swapBytes_32((uint32_t*) pData + iWord);
00311                     break;
00312                 default:
00313                     for (unsigned long iWord = 0; iWord < readWords; iWord++)
00314                         swapBytes((uint8_t*) pData + iWord * WordSize, WordSize);
00315                     break;
00316             }
00317         }
00318         SetPos(readWords * WordSize, stream_curpos);
00319         return readWords;
00320     }
00321 
00338     unsigned long Chunk::Write(void* pData, unsigned long WordCount, unsigned long WordSize) {
00339         if (pFile->Mode != stream_mode_read_write)
00340             throw Exception("Cannot write data to chunk, file has to be opened in read+write mode first");
00341         if (ulPos >= CurrentChunkSize || ulPos + WordCount * WordSize > CurrentChunkSize)
00342             throw Exception("End of chunk reached while trying to write data");
00343         if (!pFile->bEndianNative && WordSize != 1) {
00344             switch (WordSize) {
00345                 case 2:
00346                     for (unsigned long iWord = 0; iWord < WordCount; iWord++)
00347                         swapBytes_16((uint16_t*) pData + iWord);
00348                     break;
00349                 case 4:
00350                     for (unsigned long iWord = 0; iWord < WordCount; iWord++)
00351                         swapBytes_32((uint32_t*) pData + iWord);
00352                     break;
00353                 default:
00354                     for (unsigned long iWord = 0; iWord < WordCount; iWord++)
00355                         swapBytes((uint8_t*) pData + iWord * WordSize, WordSize);
00356                     break;
00357             }
00358         }
00359         #if POSIX
00360         if (lseek(pFile->hFileWrite, ulStartPos + ulPos, SEEK_SET) < 0) {
00361             throw Exception("Could not seek to position " + ToString(ulPos) +
00362                             " in chunk (" + ToString(ulStartPos + ulPos) + " in file)");
00363         }
00364         unsigned long writtenWords = write(pFile->hFileWrite, pData, WordCount * WordSize);
00365         if (writtenWords < 1) throw Exception("POSIX IO Error while trying to write chunk data");
00366         writtenWords /= WordSize;
00367         #elif defined(WIN32)
00368         if (SetFilePointer(pFile->hFileWrite, ulStartPos + ulPos, NULL/*32 bit*/, FILE_BEGIN) == INVALID_SET_FILE_POINTER) {
00369             throw Exception("Could not seek to position " + ToString(ulPos) +
00370                             " in chunk (" + ToString(ulStartPos + ulPos) + " in file)");
00371         }
00372         DWORD writtenWords;
00373         WriteFile(pFile->hFileWrite, pData, WordCount * WordSize, &writtenWords, NULL);
00374         if (writtenWords < 1) throw Exception("Windows IO Error while trying to write chunk data");
00375         writtenWords /= WordSize;
00376         #else // standard C functions
00377         if (fseek(pFile->hFileWrite, ulStartPos + ulPos, SEEK_SET)) {
00378             throw Exception("Could not seek to position " + ToString(ulPos) +
00379                             " in chunk (" + ToString(ulStartPos + ulPos) + " in file)");
00380         }
00381         unsigned long writtenWords = fwrite(pData, WordSize, WordCount, pFile->hFileWrite);
00382         #endif // POSIX
00383         SetPos(writtenWords * WordSize, stream_curpos);
00384         return writtenWords;
00385     }
00386 
00388     unsigned long Chunk::ReadSceptical(void* pData, unsigned long WordCount, unsigned long WordSize) {
00389         unsigned long readWords = Read(pData, WordCount, WordSize);
00390         if (readWords != WordCount) throw RIFF::Exception("End of chunk data reached.");
00391         return readWords;
00392     }
00393 
00405     unsigned long Chunk::ReadInt8(int8_t* pData, unsigned long WordCount) {
00406        #if DEBUG
00407        std::cout << "Chunk::ReadInt8(int8_t*,ulong)" << std::endl;
00408        #endif // DEBUG
00409         return ReadSceptical(pData, WordCount, 1);
00410     }
00411 
00426     unsigned long Chunk::WriteInt8(int8_t* pData, unsigned long WordCount) {
00427         return Write(pData, WordCount, 1);
00428     }
00429 
00442     unsigned long Chunk::ReadUint8(uint8_t* pData, unsigned long WordCount) {
00443        #if DEBUG
00444        std::cout << "Chunk::ReadUint8(uint8_t*,ulong)" << std::endl;
00445        #endif // DEBUG
00446         return ReadSceptical(pData, WordCount, 1);
00447     }
00448 
00463     unsigned long Chunk::WriteUint8(uint8_t* pData, unsigned long WordCount) {
00464         return Write(pData, WordCount, 1);
00465     }
00466 
00479     unsigned long Chunk::ReadInt16(int16_t* pData, unsigned long WordCount) {
00480       #if DEBUG
00481       std::cout << "Chunk::ReadInt16(int16_t*,ulong)" << std::endl;
00482       #endif // DEBUG
00483         return ReadSceptical(pData, WordCount, 2);
00484     }
00485 
00500     unsigned long Chunk::WriteInt16(int16_t* pData, unsigned long WordCount) {
00501         return Write(pData, WordCount, 2);
00502     }
00503 
00516     unsigned long Chunk::ReadUint16(uint16_t* pData, unsigned long WordCount) {
00517       #if DEBUG
00518       std::cout << "Chunk::ReadUint16(uint16_t*,ulong)" << std::endl;
00519       #endif // DEBUG
00520         return ReadSceptical(pData, WordCount, 2);
00521     }
00522 
00537     unsigned long Chunk::WriteUint16(uint16_t* pData, unsigned long WordCount) {
00538         return Write(pData, WordCount, 2);
00539     }
00540 
00553     unsigned long Chunk::ReadInt32(int32_t* pData, unsigned long WordCount) {
00554        #if DEBUG
00555        std::cout << "Chunk::ReadInt32(int32_t*,ulong)" << std::endl;
00556        #endif // DEBUG
00557         return ReadSceptical(pData, WordCount, 4);
00558     }
00559 
00574     unsigned long Chunk::WriteInt32(int32_t* pData, unsigned long WordCount) {
00575         return Write(pData, WordCount, 4);
00576     }
00577 
00590     unsigned long Chunk::ReadUint32(uint32_t* pData, unsigned long WordCount) {
00591        #if DEBUG
00592        std::cout << "Chunk::ReadUint32(uint32_t*,ulong)" << std::endl;
00593        #endif // DEBUG
00594         return ReadSceptical(pData, WordCount, 4);
00595     }
00596 
00611     unsigned long Chunk::WriteUint32(uint32_t* pData, unsigned long WordCount) {
00612         return Write(pData, WordCount, 4);
00613     }
00614 
00622     int8_t Chunk::ReadInt8() {
00623       #if DEBUG
00624       std::cout << "Chunk::ReadInt8()" << std::endl;
00625       #endif // DEBUG
00626         int8_t word;
00627         ReadSceptical(&word,1,1);
00628         return word;
00629     }
00630 
00638     uint8_t Chunk::ReadUint8() {
00639       #if DEBUG
00640       std::cout << "Chunk::ReadUint8()" << std::endl;
00641       #endif // DEBUG
00642         uint8_t word;
00643         ReadSceptical(&word,1,1);
00644         return word;
00645     }
00646 
00655     int16_t Chunk::ReadInt16() {
00656       #if DEBUG
00657       std::cout << "Chunk::ReadInt16()" << std::endl;
00658       #endif // DEBUG
00659         int16_t word;
00660         ReadSceptical(&word,1,2);
00661         return word;
00662     }
00663 
00672     uint16_t Chunk::ReadUint16() {
00673       #if DEBUG
00674       std::cout << "Chunk::ReadUint16()" << std::endl;
00675       #endif // DEBUG
00676         uint16_t word;
00677         ReadSceptical(&word,1,2);
00678         return word;
00679     }
00680 
00689     int32_t Chunk::ReadInt32() {
00690       #if DEBUG
00691       std::cout << "Chunk::ReadInt32()" << std::endl;
00692       #endif // DEBUG
00693         int32_t word;
00694         ReadSceptical(&word,1,4);
00695         return word;
00696     }
00697 
00706     uint32_t Chunk::ReadUint32() {
00707       #if DEBUG
00708       std::cout << "Chunk::ReadUint32()" << std::endl;
00709       #endif // DEBUG
00710         uint32_t word;
00711         ReadSceptical(&word,1,4);
00712         return word;
00713     }
00714 
00736     void* Chunk::LoadChunkData() {
00737         if (!pChunkData && pFile->Filename != "" && ulStartPos != 0) {
00738             #if POSIX
00739             if (lseek(pFile->hFileRead, ulStartPos, SEEK_SET) == -1) return NULL;
00740             #elif defined(WIN32)
00741             if (SetFilePointer(pFile->hFileRead, ulStartPos, NULL/*32 bit*/, FILE_BEGIN) == INVALID_SET_FILE_POINTER) return NULL;
00742             #else
00743             if (fseek(pFile->hFileRead, ulStartPos, SEEK_SET)) return NULL;
00744             #endif // POSIX
00745             unsigned long ulBufferSize = (CurrentChunkSize > NewChunkSize) ? CurrentChunkSize : NewChunkSize;
00746             pChunkData = new uint8_t[ulBufferSize];
00747             if (!pChunkData) return NULL;
00748             memset(pChunkData, 0, ulBufferSize);
00749             #if POSIX
00750             unsigned long readWords = read(pFile->hFileRead, pChunkData, GetSize());
00751             #elif defined(WIN32)
00752             DWORD readWords;
00753             ReadFile(pFile->hFileRead, pChunkData, GetSize(), &readWords, NULL);
00754             #else
00755             unsigned long readWords = fread(pChunkData, 1, GetSize(), pFile->hFileRead);
00756             #endif // POSIX
00757             if (readWords != GetSize()) {
00758                 delete[] pChunkData;
00759                 return (pChunkData = NULL);
00760             }
00761             ulChunkDataSize = ulBufferSize;
00762         } else if (NewChunkSize > ulChunkDataSize) {
00763             uint8_t* pNewBuffer = new uint8_t[NewChunkSize];
00764             if (!pNewBuffer) throw Exception("Could not enlarge chunk data buffer to " + ToString(NewChunkSize) + " bytes");
00765             memset(pNewBuffer, 0 , NewChunkSize);
00766             memcpy(pNewBuffer, pChunkData, ulChunkDataSize);
00767             delete[] pChunkData;
00768             pChunkData      = pNewBuffer;
00769             ulChunkDataSize = NewChunkSize;
00770         }
00771         return pChunkData;
00772     }
00773 
00780     void Chunk::ReleaseChunkData() {
00781         if (pChunkData) {
00782             delete[] pChunkData;
00783             pChunkData = NULL;
00784         }
00785     }
00786 
00805     void Chunk::Resize(int iNewSize) {
00806         if (iNewSize <= 0)
00807             throw Exception("There is at least one empty chunk (zero size): " + __resolveChunkPath(this));
00808         if (NewChunkSize == iNewSize) return;
00809         NewChunkSize = iNewSize;
00810         pFile->LogAsResized(this);
00811     }
00812 
00825     unsigned long Chunk::WriteChunk(unsigned long ulWritePos, unsigned long ulCurrentDataOffset) {
00826         const unsigned long ulOriginalPos = ulWritePos;
00827         ulWritePos += CHUNK_HEADER_SIZE;
00828 
00829         if (pFile->Mode != stream_mode_read_write)
00830             throw Exception("Cannot write list chunk, file has to be opened in read+write mode");
00831 
00832         // if the whole chunk body was loaded into RAM
00833         if (pChunkData) {
00834             // make sure chunk data buffer in RAM is at least as large as the new chunk size
00835             LoadChunkData();
00836             // write chunk data from RAM persistently to the file
00837             #if POSIX
00838             lseek(pFile->hFileWrite, ulWritePos, SEEK_SET);
00839             if (write(pFile->hFileWrite, pChunkData, NewChunkSize) != NewChunkSize) {
00840                 throw Exception("Writing Chunk data (from RAM) failed");
00841             }
00842             #elif defined(WIN32)
00843             SetFilePointer(pFile->hFileWrite, ulWritePos, NULL/*32 bit*/, FILE_BEGIN);
00844             DWORD dwBytesWritten;
00845             WriteFile(pFile->hFileWrite, pChunkData, NewChunkSize, &dwBytesWritten, NULL);
00846             if (dwBytesWritten != NewChunkSize) {
00847                 throw Exception("Writing Chunk data (from RAM) failed");
00848             }
00849             #else
00850             fseek(pFile->hFileWrite, ulWritePos, SEEK_SET);
00851             if (fwrite(pChunkData, 1, NewChunkSize, pFile->hFileWrite) != NewChunkSize) {
00852                 throw Exception("Writing Chunk data (from RAM) failed");
00853             }
00854             #endif // POSIX
00855         } else {
00856             // move chunk data from the end of the file to the appropriate position
00857             int8_t* pCopyBuffer = new int8_t[4096];
00858             unsigned long ulToMove = (NewChunkSize < CurrentChunkSize) ? NewChunkSize : CurrentChunkSize;
00859             #if defined(WIN32)
00860             DWORD iBytesMoved = 1; // we have to pass it via pointer to the Windows API, thus the correct size must be ensured
00861             #else
00862             int iBytesMoved = 1;
00863             #endif
00864             for (unsigned long ulOffset = 0; iBytesMoved > 0; ulOffset += iBytesMoved, ulToMove -= iBytesMoved) {
00865                 iBytesMoved = (ulToMove < 4096) ? ulToMove : 4096;
00866                 #if POSIX
00867                 lseek(pFile->hFileRead, ulStartPos + ulCurrentDataOffset + ulOffset, SEEK_SET);
00868                 iBytesMoved = read(pFile->hFileRead, pCopyBuffer, iBytesMoved);
00869                 lseek(pFile->hFileWrite, ulWritePos + ulOffset, SEEK_SET);
00870                 iBytesMoved = write(pFile->hFileWrite, pCopyBuffer, iBytesMoved);
00871                 #elif defined(WIN32)
00872                 SetFilePointer(pFile->hFileRead, ulStartPos + ulCurrentDataOffset + ulOffset, NULL/*32 bit*/, FILE_BEGIN);
00873                 ReadFile(pFile->hFileRead, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
00874                 SetFilePointer(pFile->hFileWrite, ulWritePos + ulOffset, NULL/*32 bit*/, FILE_BEGIN);
00875                 WriteFile(pFile->hFileWrite, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
00876                 #else
00877                 fseek(pFile->hFileRead, ulStartPos + ulCurrentDataOffset + ulOffset, SEEK_SET);
00878                 iBytesMoved = fread(pCopyBuffer, 1, iBytesMoved, pFile->hFileRead);
00879                 fseek(pFile->hFileWrite, ulWritePos + ulOffset, SEEK_SET);
00880                 iBytesMoved = fwrite(pCopyBuffer, 1, iBytesMoved, pFile->hFileWrite);
00881                 #endif
00882             }
00883             delete[] pCopyBuffer;
00884             if (iBytesMoved < 0) throw Exception("Writing Chunk data (from file) failed");
00885         }
00886 
00887         // update this chunk's header
00888         CurrentChunkSize = NewChunkSize;
00889         WriteHeader(ulOriginalPos);
00890 
00891         // update chunk's position pointers
00892         ulStartPos = ulOriginalPos + CHUNK_HEADER_SIZE;
00893         ulPos      = 0;
00894 
00895         // add pad byte if needed
00896         if ((ulStartPos + NewChunkSize) % 2 != 0) {
00897             const char cPadByte = 0;
00898             #if POSIX
00899             lseek(pFile->hFileWrite, ulStartPos + NewChunkSize, SEEK_SET);
00900             write(pFile->hFileWrite, &cPadByte, 1);
00901             #elif defined(WIN32)
00902             SetFilePointer(pFile->hFileWrite, ulStartPos + NewChunkSize, NULL/*32 bit*/, FILE_BEGIN);
00903             DWORD dwBytesWritten;
00904             WriteFile(pFile->hFileWrite, &cPadByte, 1, &dwBytesWritten, NULL);
00905             #else
00906             fseek(pFile->hFileWrite, ulStartPos + NewChunkSize, SEEK_SET);
00907             fwrite(&cPadByte, 1, 1, pFile->hFileWrite);
00908             #endif
00909             return ulStartPos + NewChunkSize + 1;
00910         }
00911 
00912         return ulStartPos + NewChunkSize;
00913     }
00914 
00915     void Chunk::__resetPos() {
00916         ulPos = 0;
00917     }
00918 
00919 
00920 
00921 // *************** List ***************
00922 // *
00923 
00924     List::List(File* pFile) : Chunk(pFile) {
00925       #if DEBUG
00926       std::cout << "List::List(File* pFile)" << std::endl;
00927       #endif // DEBUG
00928         pSubChunks    = NULL;
00929         pSubChunksMap = NULL;
00930     }
00931 
00932     List::List(File* pFile, unsigned long StartPos, List* Parent)
00933       : Chunk(pFile, StartPos, Parent) {
00934         #if DEBUG
00935         std::cout << "List::List(File*,ulong,bool,List*)" << std::endl;
00936         #endif // DEBUG
00937         pSubChunks    = NULL;
00938         pSubChunksMap = NULL;
00939         ReadHeader(StartPos);
00940         ulStartPos    = StartPos + LIST_HEADER_SIZE;
00941     }
00942 
00943     List::List(File* pFile, List* pParent, uint32_t uiListID)
00944       : Chunk(pFile, pParent, CHUNK_ID_LIST, 0) {
00945         pSubChunks    = NULL;
00946         pSubChunksMap = NULL;
00947         ListType      = uiListID;
00948     }
00949 
00950     List::~List() {
00951       #if DEBUG
00952       std::cout << "List::~List()" << std::endl;
00953       #endif // DEBUG
00954         DeleteChunkList();
00955     }
00956 
00957     void List::DeleteChunkList() {
00958         if (pSubChunks) {
00959             ChunkList::iterator iter = pSubChunks->begin();
00960             ChunkList::iterator end  = pSubChunks->end();
00961             while (iter != end) {
00962                 delete *iter;
00963                 iter++;
00964             }
00965             delete pSubChunks;
00966             pSubChunks = NULL;
00967         }
00968         if (pSubChunksMap) {
00969             delete pSubChunksMap;
00970             pSubChunksMap = NULL;
00971         }
00972     }
00973 
00985     Chunk* List::GetSubChunk(uint32_t ChunkID) {
00986       #if DEBUG
00987       std::cout << "List::GetSubChunk(uint32_t)" << std::endl;
00988       #endif // DEBUG
00989         if (!pSubChunksMap) LoadSubChunks();
00990         return (*pSubChunksMap)[ChunkID];
00991     }
00992 
01004     List* List::GetSubList(uint32_t ListType) {
01005         #if DEBUG
01006         std::cout << "List::GetSubList(uint32_t)" << std::endl;
01007         #endif // DEBUG
01008         if (!pSubChunks) LoadSubChunks();
01009         ChunkList::iterator iter = pSubChunks->begin();
01010         ChunkList::iterator end  = pSubChunks->end();
01011         while (iter != end) {
01012             if ((*iter)->GetChunkID() == CHUNK_ID_LIST) {
01013                 List* l = (List*) *iter;
01014                 if (l->GetListType() == ListType) return l;
01015             }
01016             iter++;
01017         }
01018         return NULL;
01019     }
01020 
01029     Chunk* List::GetFirstSubChunk() {
01030         #if DEBUG
01031         std::cout << "List::GetFirstSubChunk()" << std::endl;
01032         #endif // DEBUG
01033         if (!pSubChunks) LoadSubChunks();
01034         ChunksIterator = pSubChunks->begin();
01035         return (ChunksIterator != pSubChunks->end()) ? *ChunksIterator : NULL;
01036     }
01037 
01045     Chunk* List::GetNextSubChunk() {
01046         #if DEBUG
01047         std::cout << "List::GetNextSubChunk()" << std::endl;
01048         #endif // DEBUG
01049         if (!pSubChunks) return NULL;
01050         ChunksIterator++;
01051         return (ChunksIterator != pSubChunks->end()) ? *ChunksIterator : NULL;
01052     }
01053 
01063     List* List::GetFirstSubList() {
01064         #if DEBUG
01065         std::cout << "List::GetFirstSubList()" << std::endl;
01066         #endif // DEBUG
01067         if (!pSubChunks) LoadSubChunks();
01068         ListIterator            = pSubChunks->begin();
01069         ChunkList::iterator end = pSubChunks->end();
01070         while (ListIterator != end) {
01071             if ((*ListIterator)->GetChunkID() == CHUNK_ID_LIST) return (List*) *ListIterator;
01072             ListIterator++;
01073         }
01074         return NULL;
01075     }
01076 
01085     List* List::GetNextSubList() {
01086         #if DEBUG
01087         std::cout << "List::GetNextSubList()" << std::endl;
01088         #endif // DEBUG
01089         if (!pSubChunks) return NULL;
01090         if (ListIterator == pSubChunks->end()) return NULL;
01091         ListIterator++;
01092         ChunkList::iterator end = pSubChunks->end();
01093         while (ListIterator != end) {
01094             if ((*ListIterator)->GetChunkID() == CHUNK_ID_LIST) return (List*) *ListIterator;
01095             ListIterator++;
01096         }
01097         return NULL;
01098     }
01099 
01103     unsigned int List::CountSubChunks() {
01104         if (!pSubChunks) LoadSubChunks();
01105         return pSubChunks->size();
01106     }
01107 
01112     unsigned int List::CountSubChunks(uint32_t ChunkID) {
01113         unsigned int result = 0;
01114         if (!pSubChunks) LoadSubChunks();
01115         ChunkList::iterator iter = pSubChunks->begin();
01116         ChunkList::iterator end  = pSubChunks->end();
01117         while (iter != end) {
01118             if ((*iter)->GetChunkID() == ChunkID) {
01119                 result++;
01120             }
01121             iter++;
01122         }
01123         return result;
01124     }
01125 
01129     unsigned int List::CountSubLists() {
01130         return CountSubChunks(CHUNK_ID_LIST);
01131     }
01132 
01137     unsigned int List::CountSubLists(uint32_t ListType) {
01138         unsigned int result = 0;
01139         if (!pSubChunks) LoadSubChunks();
01140         ChunkList::iterator iter = pSubChunks->begin();
01141         ChunkList::iterator end  = pSubChunks->end();
01142         while (iter != end) {
01143             if ((*iter)->GetChunkID() == CHUNK_ID_LIST) {
01144                 List* l = (List*) *iter;
01145                 if (l->GetListType() == ListType) result++;
01146             }
01147             iter++;
01148         }
01149         return result;
01150     }
01151 
01165     Chunk* List::AddSubChunk(uint32_t uiChunkID, uint uiBodySize) {
01166         if (uiBodySize == 0) throw Exception("Chunk body size must be at least 1 byte");
01167         if (!pSubChunks) LoadSubChunks();
01168         Chunk* pNewChunk = new Chunk(pFile, this, uiChunkID, 0);
01169         pSubChunks->push_back(pNewChunk);
01170         (*pSubChunksMap)[uiChunkID] = pNewChunk;
01171         pNewChunk->Resize(uiBodySize);
01172         NewChunkSize += CHUNK_HEADER_SIZE;
01173         pFile->LogAsResized(this);
01174         return pNewChunk;
01175     }
01176 
01188     void List::MoveSubChunk(Chunk* pSrc, Chunk* pDst) {
01189         if (!pSubChunks) LoadSubChunks();
01190         pSubChunks->remove(pSrc);
01191         ChunkList::iterator iter = find(pSubChunks->begin(), pSubChunks->end(), pDst);
01192         pSubChunks->insert(iter, pSrc);
01193     }
01194 
01204     List* List::AddSubList(uint32_t uiListType) {
01205         if (!pSubChunks) LoadSubChunks();
01206         List* pNewListChunk = new List(pFile, this, uiListType);
01207         pSubChunks->push_back(pNewListChunk);
01208         (*pSubChunksMap)[CHUNK_ID_LIST] = pNewListChunk;
01209         NewChunkSize += LIST_HEADER_SIZE;
01210         pFile->LogAsResized(this);
01211         return pNewListChunk;
01212     }
01213 
01224     void List::DeleteSubChunk(Chunk* pSubChunk) {
01225         if (!pSubChunks) LoadSubChunks();
01226         pSubChunks->remove(pSubChunk);
01227         if ((*pSubChunksMap)[pSubChunk->GetChunkID()] == pSubChunk) {
01228             pSubChunksMap->erase(pSubChunk->GetChunkID());
01229             // try to find another chunk of the same chunk ID
01230             ChunkList::iterator iter = pSubChunks->begin();
01231             ChunkList::iterator end  = pSubChunks->end();
01232             for (; iter != end; ++iter) {
01233                 if ((*iter)->GetChunkID() == pSubChunk->GetChunkID()) {
01234                     (*pSubChunksMap)[pSubChunk->GetChunkID()] = *iter;
01235                     break; // we're done, stop search
01236                 }
01237             }
01238         }
01239         delete pSubChunk;
01240     }
01241 
01242     void List::ReadHeader(unsigned long fPos) {
01243       #if DEBUG
01244       std::cout << "List::Readheader(ulong) ";
01245       #endif // DEBUG
01246         Chunk::ReadHeader(fPos);
01247         if (CurrentChunkSize < 4) return;
01248         NewChunkSize = CurrentChunkSize -= 4;
01249         #if POSIX
01250         lseek(pFile->hFileRead, fPos + CHUNK_HEADER_SIZE, SEEK_SET);
01251         read(pFile->hFileRead, &ListType, 4);
01252         #elif defined(WIN32)
01253         SetFilePointer(pFile->hFileRead, fPos + CHUNK_HEADER_SIZE, NULL/*32 bit*/, FILE_BEGIN);
01254         DWORD dwBytesRead;
01255         ReadFile(pFile->hFileRead, &ListType, 4, &dwBytesRead, NULL);
01256         #else
01257         fseek(pFile->hFileRead, fPos + CHUNK_HEADER_SIZE, SEEK_SET);
01258         fread(&ListType, 4, 1, pFile->hFileRead);
01259         #endif // POSIX
01260       #if DEBUG
01261       std::cout << "listType=" << convertToString(ListType) << std::endl;
01262       #endif // DEBUG
01263         if (!pFile->bEndianNative) {
01264             //swapBytes_32(&ListType);
01265         }
01266     }
01267 
01268     void List::WriteHeader(unsigned long fPos) {
01269         // the four list type bytes officially belong the chunk's body in the RIFF format
01270         NewChunkSize += 4;
01271         Chunk::WriteHeader(fPos);
01272         NewChunkSize -= 4; // just revert the +4 incrementation
01273         #if POSIX
01274         lseek(pFile->hFileWrite, fPos + CHUNK_HEADER_SIZE, SEEK_SET);
01275         write(pFile->hFileWrite, &ListType, 4);
01276         #elif defined(WIN32)
01277         SetFilePointer(pFile->hFileWrite, fPos + CHUNK_HEADER_SIZE, NULL/*32 bit*/, FILE_BEGIN);
01278         DWORD dwBytesWritten;
01279         WriteFile(pFile->hFileWrite, &ListType, 4, &dwBytesWritten, NULL);
01280         #else
01281         fseek(pFile->hFileWrite, fPos + CHUNK_HEADER_SIZE, SEEK_SET);
01282         fwrite(&ListType, 4, 1, pFile->hFileWrite);
01283         #endif // POSIX
01284     }
01285 
01286     void List::LoadSubChunks() {
01287        #if DEBUG
01288        std::cout << "List::LoadSubChunks()";
01289        #endif // DEBUG
01290         if (!pSubChunks) {
01291             pSubChunks    = new ChunkList();
01292             pSubChunksMap = new ChunkMap();
01293             #if defined(WIN32)
01294             if (pFile->hFileRead == INVALID_HANDLE_VALUE) return;
01295             #else
01296             if (!pFile->hFileRead) return;
01297             #endif
01298             unsigned long uiOriginalPos = GetPos();
01299             SetPos(0); // jump to beginning of list chunk body
01300             while (RemainingBytes() >= CHUNK_HEADER_SIZE) {
01301                 Chunk* ck;
01302                 uint32_t ckid;
01303                 Read(&ckid, 4, 1);
01304        #if DEBUG
01305        std::cout << " ckid=" << convertToString(ckid) << std::endl;
01306        #endif // DEBUG
01307                 if (ckid == CHUNK_ID_LIST) {
01308                     ck = new RIFF::List(pFile, ulStartPos + ulPos - 4, this);
01309                     SetPos(ck->GetSize() + LIST_HEADER_SIZE - 4, RIFF::stream_curpos);
01310                 }
01311                 else { // simple chunk
01312                     ck = new RIFF::Chunk(pFile, ulStartPos + ulPos - 4, this);
01313                     SetPos(ck->GetSize() + CHUNK_HEADER_SIZE - 4, RIFF::stream_curpos);
01314                 }
01315                 pSubChunks->push_back(ck);
01316                 (*pSubChunksMap)[ckid] = ck;
01317                 if (GetPos() % 2 != 0) SetPos(1, RIFF::stream_curpos); // jump over pad byte
01318             }
01319             SetPos(uiOriginalPos); // restore position before this call
01320         }
01321     }
01322 
01323     void List::LoadSubChunksRecursively() {
01324         for (List* pList = GetFirstSubList(); pList; pList = GetNextSubList())
01325             pList->LoadSubChunksRecursively();
01326     }
01327 
01342     unsigned long List::WriteChunk(unsigned long ulWritePos, unsigned long ulCurrentDataOffset) {
01343         const unsigned long ulOriginalPos = ulWritePos;
01344         ulWritePos += LIST_HEADER_SIZE;
01345 
01346         if (pFile->Mode != stream_mode_read_write)
01347             throw Exception("Cannot write list chunk, file has to be opened in read+write mode");
01348 
01349         // write all subchunks (including sub list chunks) recursively
01350         if (pSubChunks) {
01351             for (ChunkList::iterator iter = pSubChunks->begin(), end = pSubChunks->end(); iter != end; ++iter) {
01352                 ulWritePos = (*iter)->WriteChunk(ulWritePos, ulCurrentDataOffset);
01353             }
01354         }
01355 
01356         // update this list chunk's header
01357         CurrentChunkSize = NewChunkSize = ulWritePos - ulOriginalPos - LIST_HEADER_SIZE;
01358         WriteHeader(ulOriginalPos);
01359 
01360         // offset of this list chunk in new written file may have changed
01361         ulStartPos = ulOriginalPos + LIST_HEADER_SIZE;
01362 
01363         return ulWritePos;
01364     }
01365 
01366     void List::__resetPos() {
01367         Chunk::__resetPos();
01368         if (pSubChunks) {
01369             for (ChunkList::iterator iter = pSubChunks->begin(), end = pSubChunks->end(); iter != end; ++iter) {
01370                 (*iter)->__resetPos();
01371             }
01372         }
01373     }
01374 
01378     String List::GetListTypeString() {
01379         return convertToString(ListType);
01380     }
01381 
01382 
01383 
01384 // *************** File ***************
01385 // *
01386 
01387 //HACK: to avoid breaking DLL compatibility to older versions of libgig we roll the new std::set<Chunk*> into the old std::list<Chunk*> container, should be replaced on member variable level soon though
01388 #define _GET_RESIZED_CHUNKS() \
01389         (reinterpret_cast<std::set<Chunk*>*>(ResizedChunks.front()))
01390 
01405     File::File(uint32_t FileType) : List(this) {
01406         //HACK: see _GET_RESIZED_CHUNKS() comment
01407         ResizedChunks.push_back(reinterpret_cast<Chunk*>(new std::set<Chunk*>));
01408         #if defined(WIN32)
01409         hFileRead = hFileWrite = INVALID_HANDLE_VALUE;
01410         #else
01411         hFileRead = hFileWrite = 0;
01412         #endif
01413         Mode = stream_mode_closed;
01414         bEndianNative = true;
01415         ulStartPos = RIFF_HEADER_SIZE;
01416         ListType = FileType;
01417     }
01418 
01427     File::File(const String& path) : List(this), Filename(path) {
01428       #if DEBUG
01429       std::cout << "File::File("<<path<<")" << std::endl;
01430       #endif // DEBUG
01431         bEndianNative = true;
01432         //HACK: see _GET_RESIZED_CHUNKS() comment
01433         ResizedChunks.push_back(reinterpret_cast<Chunk*>(new std::set<Chunk*>));
01434         #if POSIX
01435         hFileRead = hFileWrite = open(path.c_str(), O_RDONLY | O_NONBLOCK);
01436         if (hFileRead <= 0) {
01437             hFileRead = hFileWrite = 0;
01438             throw RIFF::Exception("Can't open \"" + path + "\"");
01439         }
01440         #elif defined(WIN32)
01441         hFileRead = hFileWrite = CreateFile(
01442                                      path.c_str(), GENERIC_READ,
01443                                      FILE_SHARE_READ | FILE_SHARE_WRITE,
01444                                      NULL, OPEN_EXISTING,
01445                                      FILE_ATTRIBUTE_NORMAL |
01446                                      FILE_FLAG_RANDOM_ACCESS, NULL
01447                                  );
01448         if (hFileRead == INVALID_HANDLE_VALUE) {
01449             hFileRead = hFileWrite = INVALID_HANDLE_VALUE;
01450             throw RIFF::Exception("Can't open \"" + path + "\"");
01451         }
01452         #else
01453         hFileRead = hFileWrite = fopen(path.c_str(), "rb");
01454         if (!hFileRead) throw RIFF::Exception("Can't open \"" + path + "\"");
01455         #endif // POSIX
01456         Mode = stream_mode_read;
01457         ulStartPos = RIFF_HEADER_SIZE;
01458         ReadHeader(0);
01459         if (ChunkID != CHUNK_ID_RIFF && ChunkID != CHUNK_ID_RIFX) {
01460             throw RIFF::Exception("Not a RIFF file");
01461         }
01462     }
01463 
01464     String File::GetFileName() {
01465         return Filename;
01466     }
01467 
01468     stream_mode_t File::GetMode() {
01469         return Mode;
01470     }
01471 
01482     bool File::SetMode(stream_mode_t NewMode) {
01483         if (NewMode != Mode) {
01484             switch (NewMode) {
01485                 case stream_mode_read:
01486                     #if POSIX
01487                     if (hFileRead) close(hFileRead);
01488                     hFileRead = hFileWrite = open(Filename.c_str(), O_RDONLY | O_NONBLOCK);
01489                     if (hFileRead < 0) {
01490                         hFileRead = hFileWrite = 0;
01491                         throw Exception("Could not (re)open file \"" + Filename + "\" in read mode");
01492                     }
01493                     #elif defined(WIN32)
01494                     if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
01495                     hFileRead = hFileWrite = CreateFile(
01496                                                  Filename.c_str(), GENERIC_READ,
01497                                                  FILE_SHARE_READ | FILE_SHARE_WRITE,
01498                                                  NULL, OPEN_EXISTING,
01499                                                  FILE_ATTRIBUTE_NORMAL |
01500                                                  FILE_FLAG_RANDOM_ACCESS,
01501                                                  NULL
01502                                              );
01503                     if (hFileRead == INVALID_HANDLE_VALUE) {
01504                         hFileRead = hFileWrite = INVALID_HANDLE_VALUE;
01505                         throw Exception("Could not (re)open file \"" + Filename + "\" in read mode");
01506                     }
01507                     #else
01508                     if (hFileRead) fclose(hFileRead);
01509                     hFileRead = hFileWrite = fopen(Filename.c_str(), "rb");
01510                     if (!hFileRead) throw Exception("Could not (re)open file \"" + Filename + "\" in read mode");
01511                     #endif
01512                     __resetPos(); // reset read/write position of ALL 'Chunk' objects
01513                     break;
01514                 case stream_mode_read_write:
01515                     #if POSIX
01516                     if (hFileRead) close(hFileRead);
01517                     hFileRead = hFileWrite = open(Filename.c_str(), O_RDWR | O_NONBLOCK);
01518                     if (hFileRead < 0) {
01519                         hFileRead = hFileWrite = open(Filename.c_str(), O_RDONLY | O_NONBLOCK);
01520                         throw Exception("Could not open file \"" + Filename + "\" in read+write mode");
01521                     }
01522                     #elif defined(WIN32)
01523                     if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
01524                     hFileRead = hFileWrite = CreateFile(
01525                                                  Filename.c_str(),
01526                                                  GENERIC_READ | GENERIC_WRITE,
01527                                                  FILE_SHARE_READ,
01528                                                  NULL, OPEN_ALWAYS,
01529                                                  FILE_ATTRIBUTE_NORMAL |
01530                                                  FILE_FLAG_RANDOM_ACCESS,
01531                                                  NULL
01532                                              );
01533                     if (hFileRead == INVALID_HANDLE_VALUE) {
01534                         hFileRead = hFileWrite = CreateFile(
01535                                                      Filename.c_str(), GENERIC_READ,
01536                                                      FILE_SHARE_READ | FILE_SHARE_WRITE,
01537                                                      NULL, OPEN_EXISTING,
01538                                                      FILE_ATTRIBUTE_NORMAL |
01539                                                      FILE_FLAG_RANDOM_ACCESS,
01540                                                      NULL
01541                                                  );
01542                         throw Exception("Could not (re)open file \"" + Filename + "\" in read+write mode");
01543                     }
01544                     #else
01545                     if (hFileRead) fclose(hFileRead);
01546                     hFileRead = hFileWrite = fopen(Filename.c_str(), "r+b");
01547                     if (!hFileRead) {
01548                         hFileRead = hFileWrite = fopen(Filename.c_str(), "rb");
01549                         throw Exception("Could not open file \"" + Filename + "\" in read+write mode");
01550                     }
01551                     #endif
01552                     __resetPos(); // reset read/write position of ALL 'Chunk' objects
01553                     break;
01554                 case stream_mode_closed:
01555                     #if POSIX
01556                     if (hFileRead)  close(hFileRead);
01557                     if (hFileWrite) close(hFileWrite);
01558                     #elif defined(WIN32)
01559                     if (hFileRead  != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
01560                     if (hFileWrite != INVALID_HANDLE_VALUE) CloseHandle(hFileWrite);
01561                     #else
01562                     if (hFileRead)  fclose(hFileRead);
01563                     if (hFileWrite) fclose(hFileWrite);
01564                     #endif
01565                     hFileRead = hFileWrite = 0;
01566                     break;
01567                 default:
01568                     throw Exception("Unknown file access mode");
01569             }
01570             Mode = NewMode;
01571             return true;
01572         }
01573         return false;
01574     }
01575 
01585     void File::SetByteOrder(endian_t Endian) {
01586         #if WORDS_BIGENDIAN
01587         bEndianNative = Endian != endian_little;
01588         #else
01589         bEndianNative = Endian != endian_big;
01590         #endif
01591     }
01592 
01603     void File::Save() {
01604         // make sure the RIFF tree is built (from the original file)
01605         LoadSubChunksRecursively();
01606 
01607         // reopen file in write mode
01608         SetMode(stream_mode_read_write);
01609 
01610         // to be able to save the whole file without loading everything into
01611         // RAM and without having to store the data in a temporary file, we
01612         // enlarge the file with the sum of all _positive_ chunk size
01613         // changes, move current data towards the end of the file with the
01614         // calculated sum and finally update / rewrite the file by copying
01615         // the old data back to the right position at the beginning of the file
01616 
01617         // first we sum up all positive chunk size changes (and skip all negative ones)
01618         unsigned long ulPositiveSizeDiff = 0;
01619         std::set<Chunk*>* resizedChunks = _GET_RESIZED_CHUNKS();
01620         for (std::set<Chunk*>::const_iterator iter = resizedChunks->begin(), end = resizedChunks->end(); iter != end; ++iter) {
01621             if ((*iter)->GetNewSize() == 0) {
01622                 throw Exception("There is at least one empty chunk (zero size): " + __resolveChunkPath(*iter));
01623             }
01624             unsigned long newSizePadded = (*iter)->GetNewSize() + (*iter)->GetNewSize() % 2;
01625             unsigned long oldSizePadded = (*iter)->GetSize() + (*iter)->GetSize() % 2;
01626             if (newSizePadded > oldSizePadded) ulPositiveSizeDiff += newSizePadded - oldSizePadded;
01627         }
01628 
01629         unsigned long ulWorkingFileSize = GetFileSize();
01630 
01631         // if there are positive size changes...
01632         if (ulPositiveSizeDiff > 0) {
01633             // ... we enlarge this file first ...
01634             ulWorkingFileSize += ulPositiveSizeDiff;
01635             ResizeFile(ulWorkingFileSize);
01636             // ... and move current data by the same amount towards end of file.
01637             int8_t* pCopyBuffer = new int8_t[4096];
01638             const unsigned long ulFileSize = GetSize() + RIFF_HEADER_SIZE;
01639             #if defined(WIN32)
01640             DWORD iBytesMoved = 1; // we have to pass it via pointer to the Windows API, thus the correct size must be ensured
01641             #else
01642             int iBytesMoved = 1;
01643             #endif
01644             for (unsigned long ulPos = ulFileSize; iBytesMoved > 0; ) {
01645                 iBytesMoved = (ulPos < 4096) ? ulPos : 4096;
01646                 ulPos -= iBytesMoved;
01647                 #if POSIX
01648                 lseek(hFileRead, ulPos, SEEK_SET);
01649                 iBytesMoved = read(hFileRead, pCopyBuffer, iBytesMoved);
01650                 lseek(hFileWrite, ulPos + ulPositiveSizeDiff, SEEK_SET);
01651                 iBytesMoved = write(hFileWrite, pCopyBuffer, iBytesMoved);
01652                 #elif defined(WIN32)
01653                 SetFilePointer(hFileRead, ulPos, NULL/*32 bit*/, FILE_BEGIN);
01654                 ReadFile(hFileRead, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
01655                 SetFilePointer(hFileWrite, ulPos + ulPositiveSizeDiff, NULL/*32 bit*/, FILE_BEGIN);
01656                 WriteFile(hFileWrite, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
01657                 #else
01658                 fseek(hFileRead, ulPos, SEEK_SET);
01659                 iBytesMoved = fread(pCopyBuffer, 1, iBytesMoved, hFileRead);
01660                 fseek(hFileWrite, ulPos + ulPositiveSizeDiff, SEEK_SET);
01661                 iBytesMoved = fwrite(pCopyBuffer, 1, iBytesMoved, hFileWrite);
01662                 #endif
01663             }
01664             delete[] pCopyBuffer;
01665             if (iBytesMoved < 0) throw Exception("Could not modify file while trying to enlarge it");
01666         }
01667 
01668         // rebuild / rewrite complete RIFF tree
01669         unsigned long ulTotalSize  = WriteChunk(0, ulPositiveSizeDiff);
01670         unsigned long ulActualSize = __GetFileSize(hFileWrite);
01671 
01672         // resize file to the final size
01673         if (ulTotalSize < ulActualSize) ResizeFile(ulTotalSize);
01674 
01675         // forget all resized chunks
01676         resizedChunks->clear();
01677     }
01678 
01692     void File::Save(const String& path) {
01693         //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
01694 
01695         // make sure the RIFF tree is built (from the original file)
01696         LoadSubChunksRecursively();
01697 
01698         if (Filename.length() > 0) SetMode(stream_mode_read);
01699         // open the other (new) file for writing and truncate it to zero size
01700         #if POSIX
01701         hFileWrite = open(path.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP);
01702         if (hFileWrite < 0) {
01703             hFileWrite = hFileRead;
01704             throw Exception("Could not open file \"" + path + "\" for writing");
01705         }
01706         #elif defined(WIN32)
01707         hFileWrite = CreateFile(
01708                          path.c_str(), GENERIC_WRITE, FILE_SHARE_READ,
01709                          NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL |
01710                          FILE_FLAG_RANDOM_ACCESS, NULL
01711                      );
01712         if (hFileWrite == INVALID_HANDLE_VALUE) {
01713             hFileWrite = hFileRead;
01714             throw Exception("Could not open file \"" + path + "\" for writing");
01715         }
01716         #else
01717         hFileWrite = fopen(path.c_str(), "w+b");
01718         if (!hFileWrite) {
01719             hFileWrite = hFileRead;
01720             throw Exception("Could not open file \"" + path + "\" for writing");
01721         }
01722         #endif // POSIX
01723         Mode = stream_mode_read_write;
01724 
01725         // write complete RIFF tree to the other (new) file
01726         unsigned long ulTotalSize  = WriteChunk(0, 0);
01727         unsigned long ulActualSize = __GetFileSize(hFileWrite);
01728 
01729         // resize file to the final size (if the file was originally larger)
01730         if (ulTotalSize < ulActualSize) ResizeFile(ulTotalSize);
01731 
01732         // forget all resized chunks
01733         _GET_RESIZED_CHUNKS()->clear();
01734 
01735         #if POSIX
01736         if (hFileWrite) close(hFileWrite);
01737         #elif defined(WIN32)
01738         if (hFileWrite != INVALID_HANDLE_VALUE) CloseHandle(hFileWrite);
01739         #else
01740         if (hFileWrite) fclose(hFileWrite);
01741         #endif
01742         hFileWrite = hFileRead;
01743 
01744         // associate new file with this File object from now on
01745         Filename = path;
01746         Mode = (stream_mode_t) -1;       // Just set it to an undefined mode ...
01747         SetMode(stream_mode_read_write); // ... so SetMode() has to reopen the file handles.
01748     }
01749 
01750     void File::ResizeFile(unsigned long ulNewSize) {
01751         #if POSIX
01752         if (ftruncate(hFileWrite, ulNewSize) < 0)
01753             throw Exception("Could not resize file \"" + Filename + "\"");
01754         #elif defined(WIN32)
01755         if (
01756             SetFilePointer(hFileWrite, ulNewSize, NULL/*32 bit*/, FILE_BEGIN) == INVALID_SET_FILE_POINTER ||
01757             !SetEndOfFile(hFileWrite)
01758         ) throw Exception("Could not resize file \"" + Filename + "\"");
01759         #else
01760         # error Sorry, this version of libgig only supports POSIX and Windows systems yet.
01761         # error Reason: portable implementation of RIFF::File::ResizeFile() is missing (yet)!
01762         #endif
01763     }
01764 
01765     File::~File() {
01766        #if DEBUG
01767        std::cout << "File::~File()" << std::endl;
01768        #endif // DEBUG
01769         #if POSIX
01770         if (hFileRead) close(hFileRead);
01771         #elif defined(WIN32)
01772         if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
01773         #else
01774         if (hFileRead) fclose(hFileRead);
01775         #endif // POSIX
01776         DeleteChunkList();
01777         pFile = NULL;
01778         //HACK: see _GET_RESIZED_CHUNKS() comment
01779         delete _GET_RESIZED_CHUNKS();
01780     }
01781 
01782     void File::LogAsResized(Chunk* pResizedChunk) {
01783         _GET_RESIZED_CHUNKS()->insert(pResizedChunk);
01784     }
01785 
01786     void File::UnlogResized(Chunk* pResizedChunk) {
01787         _GET_RESIZED_CHUNKS()->erase(pResizedChunk);
01788     }
01789 
01790     unsigned long File::GetFileSize() {
01791         return __GetFileSize(hFileRead);
01792     }
01793 
01794     #if POSIX
01795     unsigned long File::__GetFileSize(int hFile) {
01796         struct stat filestat;
01797         fstat(hFile, &filestat);
01798         long size = filestat.st_size;
01799         return size;
01800     }
01801     #elif defined(WIN32)
01802     unsigned long File::__GetFileSize(HANDLE hFile) {
01803         DWORD dwSize = ::GetFileSize(hFile, NULL /*32bit*/);
01804         if (dwSize == INVALID_FILE_SIZE)
01805             throw Exception("Windows FS error: could not determine file size");
01806         return dwSize;
01807     }
01808     #else // standard C functions
01809     unsigned long File::__GetFileSize(FILE* hFile) {
01810         long curpos = ftell(hFile);
01811         fseek(hFile, 0, SEEK_END);
01812         long size = ftell(hFile);
01813         fseek(hFile, curpos, SEEK_SET);
01814         return size;
01815     }
01816     #endif
01817 
01818 
01819 // *************** Exception ***************
01820 // *
01821 
01822     void Exception::PrintMessage() {
01823         std::cout << "RIFF::Exception: " << Message << std::endl;
01824     }
01825 
01826 
01827 // *************** functions ***************
01828 // *
01829 
01835     String libraryName() {
01836         return PACKAGE;
01837     }
01838 
01843     String libraryVersion() {
01844         return VERSION;
01845     }
01846 
01847 } // namespace RIFF