libgig 3.3.0
|
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