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