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-2011 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; ulToMove > 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         try {
01432             bEndianNative = true;
01433             //HACK: see _GET_RESIZED_CHUNKS() comment
01434             ResizedChunks.push_back(reinterpret_cast<Chunk*>(new std::set<Chunk*>));
01435             #if POSIX
01436             hFileRead = hFileWrite = open(path.c_str(), O_RDONLY | O_NONBLOCK);
01437             if (hFileRead <= 0) {
01438                 hFileRead = hFileWrite = 0;
01439                 throw RIFF::Exception("Can't open \"" + path + "\"");
01440             }
01441             #elif defined(WIN32)
01442             hFileRead = hFileWrite = CreateFile(
01443                                          path.c_str(), GENERIC_READ,
01444                                          FILE_SHARE_READ | FILE_SHARE_WRITE,
01445                                          NULL, OPEN_EXISTING,
01446                                          FILE_ATTRIBUTE_NORMAL |
01447                                          FILE_FLAG_RANDOM_ACCESS, NULL
01448                                      );
01449             if (hFileRead == INVALID_HANDLE_VALUE) {
01450                 hFileRead = hFileWrite = INVALID_HANDLE_VALUE;
01451                 throw RIFF::Exception("Can't open \"" + path + "\"");
01452             }
01453             #else
01454             hFileRead = hFileWrite = fopen(path.c_str(), "rb");
01455             if (!hFileRead) throw RIFF::Exception("Can't open \"" + path + "\"");
01456             #endif // POSIX
01457             Mode = stream_mode_read;
01458             ulStartPos = RIFF_HEADER_SIZE;
01459             ReadHeader(0);
01460             if (ChunkID != CHUNK_ID_RIFF && ChunkID != CHUNK_ID_RIFX) {
01461                 throw RIFF::Exception("Not a RIFF file");
01462             }
01463         }
01464         catch (...) {
01465             Cleanup();
01466             throw;
01467         }
01468     }
01469 
01470     String File::GetFileName() {
01471         return Filename;
01472     }
01473 
01474     stream_mode_t File::GetMode() {
01475         return Mode;
01476     }
01477 
01488     bool File::SetMode(stream_mode_t NewMode) {
01489         if (NewMode != Mode) {
01490             switch (NewMode) {
01491                 case stream_mode_read:
01492                     #if POSIX
01493                     if (hFileRead) close(hFileRead);
01494                     hFileRead = hFileWrite = open(Filename.c_str(), O_RDONLY | O_NONBLOCK);
01495                     if (hFileRead < 0) {
01496                         hFileRead = hFileWrite = 0;
01497                         throw Exception("Could not (re)open file \"" + Filename + "\" in read mode");
01498                     }
01499                     #elif defined(WIN32)
01500                     if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
01501                     hFileRead = hFileWrite = CreateFile(
01502                                                  Filename.c_str(), GENERIC_READ,
01503                                                  FILE_SHARE_READ | FILE_SHARE_WRITE,
01504                                                  NULL, OPEN_EXISTING,
01505                                                  FILE_ATTRIBUTE_NORMAL |
01506                                                  FILE_FLAG_RANDOM_ACCESS,
01507                                                  NULL
01508                                              );
01509                     if (hFileRead == INVALID_HANDLE_VALUE) {
01510                         hFileRead = hFileWrite = INVALID_HANDLE_VALUE;
01511                         throw Exception("Could not (re)open file \"" + Filename + "\" in read mode");
01512                     }
01513                     #else
01514                     if (hFileRead) fclose(hFileRead);
01515                     hFileRead = hFileWrite = fopen(Filename.c_str(), "rb");
01516                     if (!hFileRead) throw Exception("Could not (re)open file \"" + Filename + "\" in read mode");
01517                     #endif
01518                     __resetPos(); // reset read/write position of ALL 'Chunk' objects
01519                     break;
01520                 case stream_mode_read_write:
01521                     #if POSIX
01522                     if (hFileRead) close(hFileRead);
01523                     hFileRead = hFileWrite = open(Filename.c_str(), O_RDWR | O_NONBLOCK);
01524                     if (hFileRead < 0) {
01525                         hFileRead = hFileWrite = open(Filename.c_str(), O_RDONLY | O_NONBLOCK);
01526                         throw Exception("Could not open file \"" + Filename + "\" in read+write mode");
01527                     }
01528                     #elif defined(WIN32)
01529                     if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
01530                     hFileRead = hFileWrite = CreateFile(
01531                                                  Filename.c_str(),
01532                                                  GENERIC_READ | GENERIC_WRITE,
01533                                                  FILE_SHARE_READ,
01534                                                  NULL, OPEN_ALWAYS,
01535                                                  FILE_ATTRIBUTE_NORMAL |
01536                                                  FILE_FLAG_RANDOM_ACCESS,
01537                                                  NULL
01538                                              );
01539                     if (hFileRead == INVALID_HANDLE_VALUE) {
01540                         hFileRead = hFileWrite = CreateFile(
01541                                                      Filename.c_str(), GENERIC_READ,
01542                                                      FILE_SHARE_READ | FILE_SHARE_WRITE,
01543                                                      NULL, OPEN_EXISTING,
01544                                                      FILE_ATTRIBUTE_NORMAL |
01545                                                      FILE_FLAG_RANDOM_ACCESS,
01546                                                      NULL
01547                                                  );
01548                         throw Exception("Could not (re)open file \"" + Filename + "\" in read+write mode");
01549                     }
01550                     #else
01551                     if (hFileRead) fclose(hFileRead);
01552                     hFileRead = hFileWrite = fopen(Filename.c_str(), "r+b");
01553                     if (!hFileRead) {
01554                         hFileRead = hFileWrite = fopen(Filename.c_str(), "rb");
01555                         throw Exception("Could not open file \"" + Filename + "\" in read+write mode");
01556                     }
01557                     #endif
01558                     __resetPos(); // reset read/write position of ALL 'Chunk' objects
01559                     break;
01560                 case stream_mode_closed:
01561                     #if POSIX
01562                     if (hFileRead)  close(hFileRead);
01563                     if (hFileWrite) close(hFileWrite);
01564                     #elif defined(WIN32)
01565                     if (hFileRead  != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
01566                     if (hFileWrite != INVALID_HANDLE_VALUE) CloseHandle(hFileWrite);
01567                     #else
01568                     if (hFileRead)  fclose(hFileRead);
01569                     if (hFileWrite) fclose(hFileWrite);
01570                     #endif
01571                     hFileRead = hFileWrite = 0;
01572                     break;
01573                 default:
01574                     throw Exception("Unknown file access mode");
01575             }
01576             Mode = NewMode;
01577             return true;
01578         }
01579         return false;
01580     }
01581 
01591     void File::SetByteOrder(endian_t Endian) {
01592         #if WORDS_BIGENDIAN
01593         bEndianNative = Endian != endian_little;
01594         #else
01595         bEndianNative = Endian != endian_big;
01596         #endif
01597     }
01598 
01609     void File::Save() {
01610         // make sure the RIFF tree is built (from the original file)
01611         LoadSubChunksRecursively();
01612 
01613         // reopen file in write mode
01614         SetMode(stream_mode_read_write);
01615 
01616         // to be able to save the whole file without loading everything into
01617         // RAM and without having to store the data in a temporary file, we
01618         // enlarge the file with the sum of all _positive_ chunk size
01619         // changes, move current data towards the end of the file with the
01620         // calculated sum and finally update / rewrite the file by copying
01621         // the old data back to the right position at the beginning of the file
01622 
01623         // first we sum up all positive chunk size changes (and skip all negative ones)
01624         unsigned long ulPositiveSizeDiff = 0;
01625         std::set<Chunk*>* resizedChunks = _GET_RESIZED_CHUNKS();
01626         for (std::set<Chunk*>::const_iterator iter = resizedChunks->begin(), end = resizedChunks->end(); iter != end; ++iter) {
01627             if ((*iter)->GetNewSize() == 0) {
01628                 throw Exception("There is at least one empty chunk (zero size): " + __resolveChunkPath(*iter));
01629             }
01630             unsigned long newSizePadded = (*iter)->GetNewSize() + (*iter)->GetNewSize() % 2;
01631             unsigned long oldSizePadded = (*iter)->GetSize() + (*iter)->GetSize() % 2;
01632             if (newSizePadded > oldSizePadded) ulPositiveSizeDiff += newSizePadded - oldSizePadded;
01633         }
01634 
01635         unsigned long ulWorkingFileSize = GetFileSize();
01636 
01637         // if there are positive size changes...
01638         if (ulPositiveSizeDiff > 0) {
01639             // ... we enlarge this file first ...
01640             ulWorkingFileSize += ulPositiveSizeDiff;
01641             ResizeFile(ulWorkingFileSize);
01642             // ... and move current data by the same amount towards end of file.
01643             int8_t* pCopyBuffer = new int8_t[4096];
01644             const unsigned long ulFileSize = GetSize() + RIFF_HEADER_SIZE;
01645             #if defined(WIN32)
01646             DWORD iBytesMoved = 1; // we have to pass it via pointer to the Windows API, thus the correct size must be ensured
01647             #else
01648             int iBytesMoved = 1;
01649             #endif
01650             for (unsigned long ulPos = ulFileSize; iBytesMoved > 0; ) {
01651                 iBytesMoved = (ulPos < 4096) ? ulPos : 4096;
01652                 ulPos -= iBytesMoved;
01653                 #if POSIX
01654                 lseek(hFileRead, ulPos, SEEK_SET);
01655                 iBytesMoved = read(hFileRead, pCopyBuffer, iBytesMoved);
01656                 lseek(hFileWrite, ulPos + ulPositiveSizeDiff, SEEK_SET);
01657                 iBytesMoved = write(hFileWrite, pCopyBuffer, iBytesMoved);
01658                 #elif defined(WIN32)
01659                 SetFilePointer(hFileRead, ulPos, NULL/*32 bit*/, FILE_BEGIN);
01660                 ReadFile(hFileRead, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
01661                 SetFilePointer(hFileWrite, ulPos + ulPositiveSizeDiff, NULL/*32 bit*/, FILE_BEGIN);
01662                 WriteFile(hFileWrite, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
01663                 #else
01664                 fseek(hFileRead, ulPos, SEEK_SET);
01665                 iBytesMoved = fread(pCopyBuffer, 1, iBytesMoved, hFileRead);
01666                 fseek(hFileWrite, ulPos + ulPositiveSizeDiff, SEEK_SET);
01667                 iBytesMoved = fwrite(pCopyBuffer, 1, iBytesMoved, hFileWrite);
01668                 #endif
01669             }
01670             delete[] pCopyBuffer;
01671             if (iBytesMoved < 0) throw Exception("Could not modify file while trying to enlarge it");
01672         }
01673 
01674         // rebuild / rewrite complete RIFF tree
01675         unsigned long ulTotalSize  = WriteChunk(0, ulPositiveSizeDiff);
01676         unsigned long ulActualSize = __GetFileSize(hFileWrite);
01677 
01678         // resize file to the final size
01679         if (ulTotalSize < ulActualSize) ResizeFile(ulTotalSize);
01680 
01681         // forget all resized chunks
01682         resizedChunks->clear();
01683     }
01684 
01698     void File::Save(const String& path) {
01699         //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
01700 
01701         // make sure the RIFF tree is built (from the original file)
01702         LoadSubChunksRecursively();
01703 
01704         if (Filename.length() > 0) SetMode(stream_mode_read);
01705         // open the other (new) file for writing and truncate it to zero size
01706         #if POSIX
01707         hFileWrite = open(path.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP);
01708         if (hFileWrite < 0) {
01709             hFileWrite = hFileRead;
01710             throw Exception("Could not open file \"" + path + "\" for writing");
01711         }
01712         #elif defined(WIN32)
01713         hFileWrite = CreateFile(
01714                          path.c_str(), GENERIC_WRITE, FILE_SHARE_READ,
01715                          NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL |
01716                          FILE_FLAG_RANDOM_ACCESS, NULL
01717                      );
01718         if (hFileWrite == INVALID_HANDLE_VALUE) {
01719             hFileWrite = hFileRead;
01720             throw Exception("Could not open file \"" + path + "\" for writing");
01721         }
01722         #else
01723         hFileWrite = fopen(path.c_str(), "w+b");
01724         if (!hFileWrite) {
01725             hFileWrite = hFileRead;
01726             throw Exception("Could not open file \"" + path + "\" for writing");
01727         }
01728         #endif // POSIX
01729         Mode = stream_mode_read_write;
01730 
01731         // write complete RIFF tree to the other (new) file
01732         unsigned long ulTotalSize  = WriteChunk(0, 0);
01733         unsigned long ulActualSize = __GetFileSize(hFileWrite);
01734 
01735         // resize file to the final size (if the file was originally larger)
01736         if (ulTotalSize < ulActualSize) ResizeFile(ulTotalSize);
01737 
01738         // forget all resized chunks
01739         _GET_RESIZED_CHUNKS()->clear();
01740 
01741         #if POSIX
01742         if (hFileWrite) close(hFileWrite);
01743         #elif defined(WIN32)
01744         if (hFileWrite != INVALID_HANDLE_VALUE) CloseHandle(hFileWrite);
01745         #else
01746         if (hFileWrite) fclose(hFileWrite);
01747         #endif
01748         hFileWrite = hFileRead;
01749 
01750         // associate new file with this File object from now on
01751         Filename = path;
01752         Mode = (stream_mode_t) -1;       // Just set it to an undefined mode ...
01753         SetMode(stream_mode_read_write); // ... so SetMode() has to reopen the file handles.
01754     }
01755 
01756     void File::ResizeFile(unsigned long ulNewSize) {
01757         #if POSIX
01758         if (ftruncate(hFileWrite, ulNewSize) < 0)
01759             throw Exception("Could not resize file \"" + Filename + "\"");
01760         #elif defined(WIN32)
01761         if (
01762             SetFilePointer(hFileWrite, ulNewSize, NULL/*32 bit*/, FILE_BEGIN) == INVALID_SET_FILE_POINTER ||
01763             !SetEndOfFile(hFileWrite)
01764         ) throw Exception("Could not resize file \"" + Filename + "\"");
01765         #else
01766         # error Sorry, this version of libgig only supports POSIX and Windows systems yet.
01767         # error Reason: portable implementation of RIFF::File::ResizeFile() is missing (yet)!
01768         #endif
01769     }
01770 
01771     File::~File() {
01772        #if DEBUG
01773        std::cout << "File::~File()" << std::endl;
01774        #endif // DEBUG
01775         Cleanup();
01776     }
01777 
01778     void File::Cleanup() {
01779         #if POSIX
01780         if (hFileRead) close(hFileRead);
01781         #elif defined(WIN32)
01782         if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
01783         #else
01784         if (hFileRead) fclose(hFileRead);
01785         #endif // POSIX
01786         DeleteChunkList();
01787         pFile = NULL;
01788         //HACK: see _GET_RESIZED_CHUNKS() comment
01789         delete _GET_RESIZED_CHUNKS();
01790     }
01791 
01792     void File::LogAsResized(Chunk* pResizedChunk) {
01793         _GET_RESIZED_CHUNKS()->insert(pResizedChunk);
01794     }
01795 
01796     void File::UnlogResized(Chunk* pResizedChunk) {
01797         _GET_RESIZED_CHUNKS()->erase(pResizedChunk);
01798     }
01799 
01800     unsigned long File::GetFileSize() {
01801         return __GetFileSize(hFileRead);
01802     }
01803 
01804     #if POSIX
01805     unsigned long File::__GetFileSize(int hFile) {
01806         struct stat filestat;
01807         fstat(hFile, &filestat);
01808         long size = filestat.st_size;
01809         return size;
01810     }
01811     #elif defined(WIN32)
01812     unsigned long File::__GetFileSize(HANDLE hFile) {
01813         DWORD dwSize = ::GetFileSize(hFile, NULL /*32bit*/);
01814         if (dwSize == INVALID_FILE_SIZE)
01815             throw Exception("Windows FS error: could not determine file size");
01816         return dwSize;
01817     }
01818     #else // standard C functions
01819     unsigned long File::__GetFileSize(FILE* hFile) {
01820         long curpos = ftell(hFile);
01821         fseek(hFile, 0, SEEK_END);
01822         long size = ftell(hFile);
01823         fseek(hFile, curpos, SEEK_SET);
01824         return size;
01825     }
01826     #endif
01827 
01828 
01829 // *************** Exception ***************
01830 // *
01831 
01832     void Exception::PrintMessage() {
01833         std::cout << "RIFF::Exception: " << Message << std::endl;
01834     }
01835 
01836 
01837 // *************** functions ***************
01838 // *
01839 
01845     String libraryName() {
01846         return PACKAGE;
01847     }
01848 
01853     String libraryVersion() {
01854         return VERSION;
01855     }
01856 
01857 } // namespace RIFF

Generated on Sun May 1 03:22:46 2011 for libgig by  doxygen 1.5.2