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 <string.h>
00025
00026 #include "RIFF.h"
00027
00028 #include "helper.h"
00029
00030 namespace RIFF {
00031
00032
00033
00034
00035 Chunk::Chunk(File* pFile) {
00036 #if DEBUG
00037 std::cout << "Chunk::Chunk(File* pFile)" << std::endl;
00038 #endif // DEBUG
00039 ulPos = 0;
00040 pParent = NULL;
00041 pChunkData = NULL;
00042 ulChunkDataSize = 0;
00043 ChunkID = CHUNK_ID_RIFF;
00044 this->pFile = pFile;
00045 }
00046
00047 Chunk::Chunk(File* pFile, unsigned long StartPos, List* Parent) {
00048 #if DEBUG
00049 std::cout << "Chunk::Chunk(File*,ulong,bool,List*),StartPos=" << StartPos << std::endl;
00050 #endif // DEBUG
00051 this->pFile = pFile;
00052 ulStartPos = StartPos + CHUNK_HEADER_SIZE;
00053 pParent = Parent;
00054 ulPos = 0;
00055 pChunkData = NULL;
00056 ulChunkDataSize = 0;
00057 ReadHeader(StartPos);
00058 }
00059
00060 Chunk::Chunk(File* pFile, List* pParent, uint32_t uiChunkID, uint uiBodySize) {
00061 this->pFile = pFile;
00062 ulStartPos = 0;
00063 this->pParent = pParent;
00064 ulPos = 0;
00065 pChunkData = NULL;
00066 ulChunkDataSize = 0;
00067 ChunkID = uiChunkID;
00068 CurrentChunkSize = 0;
00069 NewChunkSize = uiBodySize;
00070 }
00071
00072 Chunk::~Chunk() {
00073 if (pChunkData) delete[] pChunkData;
00074 }
00075
00076 void Chunk::ReadHeader(unsigned long fPos) {
00077 #if DEBUG
00078 std::cout << "Chunk::Readheader(" << fPos << ") ";
00079 #endif // DEBUG
00080 #if POSIX
00081 if (lseek(pFile->hFileRead, fPos, SEEK_SET) != -1) {
00082 read(pFile->hFileRead, &ChunkID, 4);
00083 read(pFile->hFileRead, &CurrentChunkSize, 4);
00084 #else
00085 if (!fseek(pFile->hFileRead, fPos, SEEK_SET)) {
00086 fread(&ChunkID, 4, 1, pFile->hFileRead);
00087 fread(&CurrentChunkSize, 4, 1, pFile->hFileRead);
00088 #endif // POSIX
00089 #if WORDS_BIGENDIAN
00090 if (ChunkID == CHUNK_ID_RIFF) {
00091 pFile->bEndianNative = false;
00092 }
00093 #else // little endian
00094 if (ChunkID == CHUNK_ID_RIFX) {
00095 pFile->bEndianNative = false;
00096 ChunkID = CHUNK_ID_RIFF;
00097 }
00098 #endif // WORDS_BIGENDIAN
00099 if (!pFile->bEndianNative) {
00100
00101 swapBytes_32(&CurrentChunkSize);
00102 }
00103 #if DEBUG
00104 std::cout << "ckID=" << convertToString(ChunkID) << " ";
00105 std::cout << "ckSize=" << ChunkSize << " ";
00106 std::cout << "bEndianNative=" << bEndianNative << std::endl;
00107 #endif // DEBUG
00108 NewChunkSize = CurrentChunkSize;
00109 }
00110 }
00111
00112 void Chunk::WriteHeader(unsigned long fPos) {
00113 uint32_t uiNewChunkID = ChunkID;
00114 if (ChunkID == CHUNK_ID_RIFF) {
00115 #if WORDS_BIGENDIAN
00116 if (pFile->bEndianNative) uiNewChunkID = CHUNK_ID_RIFX;
00117 #else // little endian
00118 if (!pFile->bEndianNative) uiNewChunkID = CHUNK_ID_RIFX;
00119 #endif // WORDS_BIGENDIAN
00120 }
00121
00122 uint32_t uiNewChunkSize = NewChunkSize;
00123 if (!pFile->bEndianNative) {
00124 swapBytes_32(&uiNewChunkSize);
00125 }
00126
00127 #if POSIX
00128 if (lseek(pFile->hFileWrite, fPos, SEEK_SET) != -1) {
00129 write(pFile->hFileWrite, &uiNewChunkID, 4);
00130 write(pFile->hFileWrite, &uiNewChunkSize, 4);
00131 }
00132 #else
00133 if (!fseek(pFile->hFileWrite, fPos, SEEK_SET)) {
00134 fwrite(&uiNewChunkID, 4, 1, pFile->hFileWrite);
00135 fwrite(&uiNewChunkSize, 4, 1, pFile->hFileWrite);
00136 }
00137 #endif // POSIX
00138 }
00139
00144 String Chunk::GetChunkIDString() {
00145 return convertToString(ChunkID);
00146 }
00147
00160 unsigned long Chunk::SetPos(unsigned long Where, stream_whence_t Whence) {
00161 #if DEBUG
00162 std::cout << "Chunk::SetPos(ulong)" << std::endl;
00163 #endif // DEBUG
00164 switch (Whence) {
00165 case stream_curpos:
00166 ulPos += Where;
00167 break;
00168 case stream_end:
00169 ulPos = CurrentChunkSize - 1 - Where;
00170 break;
00171 case stream_backward:
00172 ulPos -= Where;
00173 break;
00174 case stream_start: default:
00175 ulPos = Where;
00176 break;
00177 }
00178 if (ulPos > CurrentChunkSize) ulPos = CurrentChunkSize;
00179 return ulPos;
00180 }
00181
00192 unsigned long Chunk::RemainingBytes() {
00193 #if DEBUG
00194 std::cout << "Chunk::Remainingbytes()=" << CurrentChunkSize - ulPos << std::endl;
00195 #endif // DEBUG
00196 return CurrentChunkSize - ulPos;
00197 }
00198
00210 stream_state_t Chunk::GetState() {
00211 #if DEBUG
00212 std::cout << "Chunk::GetState()" << std::endl;
00213 #endif // DEBUG
00214 #if POSIX
00215 if (pFile->hFileRead == 0) return stream_closed;
00216 #else
00217 if (pFile->hFileRead == NULL) return stream_closed;
00218 #endif // POSIX
00219 if (ulPos < CurrentChunkSize) return stream_ready;
00220 else return stream_end_reached;
00221 }
00222
00238 unsigned long Chunk::Read(void* pData, unsigned long WordCount, unsigned long WordSize) {
00239 #if DEBUG
00240 std::cout << "Chunk::Read(void*,ulong,ulong)" << std::endl;
00241 #endif // DEBUG
00242 if (ulPos >= CurrentChunkSize) return 0;
00243 if (ulPos + WordCount * WordSize >= CurrentChunkSize) WordCount = (CurrentChunkSize - ulPos) / WordSize;
00244 #if POSIX
00245 if (lseek(pFile->hFileRead, ulStartPos + ulPos, SEEK_SET) < 0) return 0;
00246 unsigned long readWords = read(pFile->hFileRead, pData, WordCount * WordSize);
00247 if (readWords < 1) return 0;
00248 readWords /= WordSize;
00249 #else // standard C functions
00250 if (fseek(pFile->hFileRead, ulStartPos + ulPos, SEEK_SET)) return 0;
00251 unsigned long readWords = fread(pData, WordSize, WordCount, pFile->hFileRead);
00252 #endif // POSIX
00253 if (!pFile->bEndianNative && WordSize != 1) {
00254 switch (WordSize) {
00255 case 2:
00256 for (unsigned long iWord = 0; iWord < readWords; iWord++)
00257 swapBytes_16((uint16_t*) pData + iWord);
00258 break;
00259 case 4:
00260 for (unsigned long iWord = 0; iWord < readWords; iWord++)
00261 swapBytes_32((uint32_t*) pData + iWord);
00262 break;
00263 default:
00264 for (unsigned long iWord = 0; iWord < readWords; iWord++)
00265 swapBytes((uint8_t*) pData + iWord * WordSize, WordSize);
00266 break;
00267 }
00268 }
00269 SetPos(readWords * WordSize, stream_curpos);
00270 return readWords;
00271 }
00272
00289 unsigned long Chunk::Write(void* pData, unsigned long WordCount, unsigned long WordSize) {
00290 if (pFile->Mode != stream_mode_read_write)
00291 throw Exception("Cannot write data to chunk, file has to be opened in read+write mode first");
00292 if (ulPos >= CurrentChunkSize || ulPos + WordCount * WordSize > CurrentChunkSize)
00293 throw Exception("End of chunk reached while trying to write data");
00294 if (!pFile->bEndianNative && WordSize != 1) {
00295 switch (WordSize) {
00296 case 2:
00297 for (unsigned long iWord = 0; iWord < WordCount; iWord++)
00298 swapBytes_16((uint16_t*) pData + iWord);
00299 break;
00300 case 4:
00301 for (unsigned long iWord = 0; iWord < WordCount; iWord++)
00302 swapBytes_32((uint32_t*) pData + iWord);
00303 break;
00304 default:
00305 for (unsigned long iWord = 0; iWord < WordCount; iWord++)
00306 swapBytes((uint8_t*) pData + iWord * WordSize, WordSize);
00307 break;
00308 }
00309 }
00310 #if POSIX
00311 if (lseek(pFile->hFileWrite, ulStartPos + ulPos, SEEK_SET) < 0) {
00312 throw Exception("Could not seek to position " + ToString(ulPos) +
00313 " in chunk (" + ToString(ulStartPos + ulPos) + " in file)");
00314 }
00315 unsigned long writtenWords = write(pFile->hFileWrite, pData, WordCount * WordSize);
00316 if (writtenWords < 1) throw Exception("POSIX IO Error while trying to write chunk data");
00317 writtenWords /= WordSize;
00318 #else // standard C functions
00319 if (fseek(pFile->hFileWrite, ulStartPos + ulPos, SEEK_SET)) {
00320 throw Exception("Could not seek to position " + ToString(ulPos) +
00321 " in chunk (" + ToString(ulStartPos + ulPos) + " in file)");
00322 }
00323 unsigned long writtenWords = fwrite(pData, WordSize, WordCount, pFile->hFileWrite);
00324 #endif // POSIX
00325 SetPos(writtenWords * WordSize, stream_curpos);
00326 return writtenWords;
00327 }
00328
00330 unsigned long Chunk::ReadSceptical(void* pData, unsigned long WordCount, unsigned long WordSize) {
00331 unsigned long readWords = Read(pData, WordCount, WordSize);
00332 if (readWords != WordCount) throw RIFF::Exception("End of chunk data reached.");
00333 return readWords;
00334 }
00335
00347 unsigned long Chunk::ReadInt8(int8_t* pData, unsigned long WordCount) {
00348 #if DEBUG
00349 std::cout << "Chunk::ReadInt8(int8_t*,ulong)" << std::endl;
00350 #endif // DEBUG
00351 return ReadSceptical(pData, WordCount, 1);
00352 }
00353
00368 unsigned long Chunk::WriteInt8(int8_t* pData, unsigned long WordCount) {
00369 return Write(pData, WordCount, 1);
00370 }
00371
00384 unsigned long Chunk::ReadUint8(uint8_t* pData, unsigned long WordCount) {
00385 #if DEBUG
00386 std::cout << "Chunk::ReadUint8(uint8_t*,ulong)" << std::endl;
00387 #endif // DEBUG
00388 return ReadSceptical(pData, WordCount, 1);
00389 }
00390
00405 unsigned long Chunk::WriteUint8(uint8_t* pData, unsigned long WordCount) {
00406 return Write(pData, WordCount, 1);
00407 }
00408
00421 unsigned long Chunk::ReadInt16(int16_t* pData, unsigned long WordCount) {
00422 #if DEBUG
00423 std::cout << "Chunk::ReadInt16(int16_t*,ulong)" << std::endl;
00424 #endif // DEBUG
00425 return ReadSceptical(pData, WordCount, 2);
00426 }
00427
00442 unsigned long Chunk::WriteInt16(int16_t* pData, unsigned long WordCount) {
00443 return Write(pData, WordCount, 2);
00444 }
00445
00458 unsigned long Chunk::ReadUint16(uint16_t* pData, unsigned long WordCount) {
00459 #if DEBUG
00460 std::cout << "Chunk::ReadUint16(uint16_t*,ulong)" << std::endl;
00461 #endif // DEBUG
00462 return ReadSceptical(pData, WordCount, 2);
00463 }
00464
00479 unsigned long Chunk::WriteUint16(uint16_t* pData, unsigned long WordCount) {
00480 return Write(pData, WordCount, 2);
00481 }
00482
00495 unsigned long Chunk::ReadInt32(int32_t* pData, unsigned long WordCount) {
00496 #if DEBUG
00497 std::cout << "Chunk::ReadInt32(int32_t*,ulong)" << std::endl;
00498 #endif // DEBUG
00499 return ReadSceptical(pData, WordCount, 4);
00500 }
00501
00516 unsigned long Chunk::WriteInt32(int32_t* pData, unsigned long WordCount) {
00517 return Write(pData, WordCount, 4);
00518 }
00519
00532 unsigned long Chunk::ReadUint32(uint32_t* pData, unsigned long WordCount) {
00533 #if DEBUG
00534 std::cout << "Chunk::ReadUint32(uint32_t*,ulong)" << std::endl;
00535 #endif // DEBUG
00536 return ReadSceptical(pData, WordCount, 4);
00537 }
00538
00553 unsigned long Chunk::WriteUint32(uint32_t* pData, unsigned long WordCount) {
00554 return Write(pData, WordCount, 4);
00555 }
00556
00564 int8_t Chunk::ReadInt8() {
00565 #if DEBUG
00566 std::cout << "Chunk::ReadInt8()" << std::endl;
00567 #endif // DEBUG
00568 int8_t word;
00569 ReadSceptical(&word,1,1);
00570 return word;
00571 }
00572
00580 uint8_t Chunk::ReadUint8() {
00581 #if DEBUG
00582 std::cout << "Chunk::ReadUint8()" << std::endl;
00583 #endif // DEBUG
00584 uint8_t word;
00585 ReadSceptical(&word,1,1);
00586 return word;
00587 }
00588
00597 int16_t Chunk::ReadInt16() {
00598 #if DEBUG
00599 std::cout << "Chunk::ReadInt16()" << std::endl;
00600 #endif // DEBUG
00601 int16_t word;
00602 ReadSceptical(&word,1,2);
00603 return word;
00604 }
00605
00614 uint16_t Chunk::ReadUint16() {
00615 #if DEBUG
00616 std::cout << "Chunk::ReadUint16()" << std::endl;
00617 #endif // DEBUG
00618 uint16_t word;
00619 ReadSceptical(&word,1,2);
00620 return word;
00621 }
00622
00631 int32_t Chunk::ReadInt32() {
00632 #if DEBUG
00633 std::cout << "Chunk::ReadInt32()" << std::endl;
00634 #endif // DEBUG
00635 int32_t word;
00636 ReadSceptical(&word,1,4);
00637 return word;
00638 }
00639
00648 uint32_t Chunk::ReadUint32() {
00649 #if DEBUG
00650 std::cout << "Chunk::ReadUint32()" << std::endl;
00651 #endif // DEBUG
00652 uint32_t word;
00653 ReadSceptical(&word,1,4);
00654 return word;
00655 }
00656
00678 void* Chunk::LoadChunkData() {
00679 if (!pChunkData && pFile->Filename != "") {
00680 #if POSIX
00681 if (lseek(pFile->hFileRead, ulStartPos, SEEK_SET) == -1) return NULL;
00682 #else
00683 if (fseek(pFile->hFileRead, ulStartPos, SEEK_SET)) return NULL;
00684 #endif // POSIX
00685 unsigned long ulBufferSize = (CurrentChunkSize > NewChunkSize) ? CurrentChunkSize : NewChunkSize;
00686 pChunkData = new uint8_t[ulBufferSize];
00687 if (!pChunkData) return NULL;
00688 memset(pChunkData, 0, ulBufferSize);
00689 #if POSIX
00690 unsigned long readWords = read(pFile->hFileRead, pChunkData, GetSize());
00691 #else
00692 unsigned long readWords = fread(pChunkData, 1, GetSize(), pFile->hFileRead);
00693 #endif // POSIX
00694 if (readWords != GetSize()) {
00695 delete[] pChunkData;
00696 return (pChunkData = NULL);
00697 }
00698 ulChunkDataSize = ulBufferSize;
00699 } else if (NewChunkSize > ulChunkDataSize) {
00700 uint8_t* pNewBuffer = new uint8_t[NewChunkSize];
00701 if (!pNewBuffer) throw Exception("Could not enlarge chunk data buffer to " + ToString(NewChunkSize) + " bytes");
00702 memset(pNewBuffer, 0 , NewChunkSize);
00703 memcpy(pNewBuffer, pChunkData, ulChunkDataSize);
00704 delete[] pChunkData;
00705 pChunkData = pNewBuffer;
00706 ulChunkDataSize = NewChunkSize;
00707 }
00708 return pChunkData;
00709 }
00710
00717 void Chunk::ReleaseChunkData() {
00718 if (pChunkData) {
00719 delete[] pChunkData;
00720 pChunkData = NULL;
00721 }
00722 }
00723
00742 void Chunk::Resize(int iNewSize) {
00743 if (iNewSize <= 0) throw Exception("Chunk size must be at least one byte");
00744 if (NewChunkSize == iNewSize) return;
00745 NewChunkSize = iNewSize;
00746 pFile->LogAsResized(this);
00747 }
00748
00761 unsigned long Chunk::WriteChunk(unsigned long ulWritePos, unsigned long ulCurrentDataOffset) {
00762 const unsigned long ulOriginalPos = ulWritePos;
00763 ulWritePos += CHUNK_HEADER_SIZE;
00764
00765 if (pFile->Mode != stream_mode_read_write)
00766 throw Exception("Cannot write list chunk, file has to be opened in read+write mode");
00767
00768
00769 if (pChunkData) {
00770
00771 LoadChunkData();
00772
00773 #if POSIX
00774 lseek(pFile->hFileWrite, ulWritePos, SEEK_SET);
00775 if (write(pFile->hFileWrite, pChunkData, NewChunkSize) != NewChunkSize) {
00776 throw Exception("Writing Chunk data (from RAM) failed");
00777 }
00778 #else
00779 fseek(pFile->hFileWrite, ulWritePos, SEEK_SET);
00780 if (fwrite(pChunkData, 1, NewChunkSize, pFile->hFileWrite) != NewChunkSize) {
00781 throw Exception("Writing Chunk data (from RAM) failed");
00782 }
00783 #endif // POSIX
00784 } else {
00785
00786 int8_t* pCopyBuffer = new int8_t[4096];
00787 unsigned long ulToMove = (NewChunkSize < CurrentChunkSize) ? NewChunkSize : CurrentChunkSize;
00788 int iBytesMoved = 1;
00789 for (unsigned long ulOffset = 0; iBytesMoved > 0; ulOffset += iBytesMoved, ulToMove -= iBytesMoved) {
00790 iBytesMoved = (ulToMove < 4096) ? ulToMove : 4096;
00791 #if POSIX
00792 lseek(pFile->hFileRead, ulStartPos + ulCurrentDataOffset + ulOffset, SEEK_SET);
00793 iBytesMoved = read(pFile->hFileRead, pCopyBuffer, iBytesMoved);
00794 lseek(pFile->hFileWrite, ulWritePos + ulOffset, SEEK_SET);
00795 iBytesMoved = write(pFile->hFileWrite, pCopyBuffer, iBytesMoved);
00796 #else
00797 fseek(pFile->hFileRead, ulStartPos + ulCurrentDataOffset + ulOffset, SEEK_SET);
00798 iBytesMoved = fread(pCopyBuffer, 1, iBytesMoved, pFile->hFileRead);
00799 fseek(pFile->hFileWrite, ulWritePos + ulOffset, SEEK_SET);
00800 iBytesMoved = fwrite(pCopyBuffer, 1, iBytesMoved, pFile->hFileWrite);
00801 #endif
00802 }
00803 delete[] pCopyBuffer;
00804 if (iBytesMoved < 0) throw Exception("Writing Chunk data (from file) failed");
00805 }
00806
00807
00808 CurrentChunkSize = NewChunkSize;
00809 WriteHeader(ulOriginalPos);
00810
00811
00812 ulStartPos = ulOriginalPos + CHUNK_HEADER_SIZE;
00813 ulPos = 0;
00814
00815
00816 if ((ulStartPos + NewChunkSize) % 2 != 0) {
00817 const char cPadByte = 0;
00818 #if POSIX
00819 lseek(pFile->hFileWrite, ulStartPos + NewChunkSize, SEEK_SET);
00820 write(pFile->hFileWrite, &cPadByte, 1);
00821 #else
00822 fseek(pFile->hFileWrite, ulStartPos + NewChunkSize, SEEK_SET);
00823 fwrite(&cPadByte, 1, 1, pFile->hFileWrite);
00824 #endif
00825 return ulStartPos + NewChunkSize + 1;
00826 }
00827
00828 return ulStartPos + NewChunkSize;
00829 }
00830
00831 void Chunk::__resetPos() {
00832 ulPos = 0;
00833 }
00834
00835
00836
00837
00838
00839
00840 List::List(File* pFile) : Chunk(pFile) {
00841 #if DEBUG
00842 std::cout << "List::List(File* pFile)" << std::endl;
00843 #endif // DEBUG
00844 pSubChunks = NULL;
00845 pSubChunksMap = NULL;
00846 }
00847
00848 List::List(File* pFile, unsigned long StartPos, List* Parent)
00849 : Chunk(pFile, StartPos, Parent) {
00850 #if DEBUG
00851 std::cout << "List::List(File*,ulong,bool,List*)" << std::endl;
00852 #endif // DEBUG
00853 pSubChunks = NULL;
00854 pSubChunksMap = NULL;
00855 ReadHeader(StartPos);
00856 ulStartPos = StartPos + LIST_HEADER_SIZE;
00857 }
00858
00859 List::List(File* pFile, List* pParent, uint32_t uiListID)
00860 : Chunk(pFile, pParent, CHUNK_ID_LIST, 0) {
00861 pSubChunks = NULL;
00862 pSubChunksMap = NULL;
00863 ListType = uiListID;
00864 }
00865
00866 List::~List() {
00867 #if DEBUG
00868 std::cout << "List::~List()" << std::endl;
00869 #endif // DEBUG
00870 if (pSubChunks) {
00871 ChunkList::iterator iter = pSubChunks->begin();
00872 ChunkList::iterator end = pSubChunks->end();
00873 while (iter != end) {
00874 delete *iter;
00875 iter++;
00876 }
00877 delete pSubChunks;
00878 }
00879 if (pSubChunksMap) delete pSubChunksMap;
00880 }
00881
00893 Chunk* List::GetSubChunk(uint32_t ChunkID) {
00894 #if DEBUG
00895 std::cout << "List::GetSubChunk(uint32_t)" << std::endl;
00896 #endif // DEBUG
00897 if (!pSubChunksMap) LoadSubChunks();
00898 return (*pSubChunksMap)[ChunkID];
00899 }
00900
00912 List* List::GetSubList(uint32_t ListType) {
00913 #if DEBUG
00914 std::cout << "List::GetSubList(uint32_t)" << std::endl;
00915 #endif // DEBUG
00916 if (!pSubChunks) LoadSubChunks();
00917 ChunkList::iterator iter = pSubChunks->begin();
00918 ChunkList::iterator end = pSubChunks->end();
00919 while (iter != end) {
00920 if ((*iter)->GetChunkID() == CHUNK_ID_LIST) {
00921 List* l = (List*) *iter;
00922 if (l->GetListType() == ListType) return l;
00923 }
00924 iter++;
00925 }
00926 return NULL;
00927 }
00928
00937 Chunk* List::GetFirstSubChunk() {
00938 #if DEBUG
00939 std::cout << "List::GetFirstSubChunk()" << std::endl;
00940 #endif // DEBUG
00941 if (!pSubChunks) LoadSubChunks();
00942 ChunksIterator = pSubChunks->begin();
00943 return (ChunksIterator != pSubChunks->end()) ? *ChunksIterator : NULL;
00944 }
00945
00953 Chunk* List::GetNextSubChunk() {
00954 #if DEBUG
00955 std::cout << "List::GetNextSubChunk()" << std::endl;
00956 #endif // DEBUG
00957 if (!pSubChunks) return NULL;
00958 ChunksIterator++;
00959 return (ChunksIterator != pSubChunks->end()) ? *ChunksIterator : NULL;
00960 }
00961
00971 List* List::GetFirstSubList() {
00972 #if DEBUG
00973 std::cout << "List::GetFirstSubList()" << std::endl;
00974 #endif // DEBUG
00975 if (!pSubChunks) LoadSubChunks();
00976 ListIterator = pSubChunks->begin();
00977 ChunkList::iterator end = pSubChunks->end();
00978 while (ListIterator != end) {
00979 if ((*ListIterator)->GetChunkID() == CHUNK_ID_LIST) return (List*) *ListIterator;
00980 ListIterator++;
00981 }
00982 return NULL;
00983 }
00984
00993 List* List::GetNextSubList() {
00994 #if DEBUG
00995 std::cout << "List::GetNextSubList()" << std::endl;
00996 #endif // DEBUG
00997 if (!pSubChunks) return NULL;
00998 if (ListIterator == pSubChunks->end()) return NULL;
00999 ListIterator++;
01000 ChunkList::iterator end = pSubChunks->end();
01001 while (ListIterator != end) {
01002 if ((*ListIterator)->GetChunkID() == CHUNK_ID_LIST) return (List*) *ListIterator;
01003 ListIterator++;
01004 }
01005 return NULL;
01006 }
01007
01011 unsigned int List::CountSubChunks() {
01012 if (!pSubChunks) LoadSubChunks();
01013 return pSubChunks->size();
01014 }
01015
01020 unsigned int List::CountSubChunks(uint32_t ChunkID) {
01021 unsigned int result = 0;
01022 if (!pSubChunks) LoadSubChunks();
01023 ChunkList::iterator iter = pSubChunks->begin();
01024 ChunkList::iterator end = pSubChunks->end();
01025 while (iter != end) {
01026 if ((*iter)->GetChunkID() == ChunkID) {
01027 result++;
01028 }
01029 iter++;
01030 }
01031 return result;
01032 }
01033
01037 unsigned int List::CountSubLists() {
01038 return CountSubChunks(CHUNK_ID_LIST);
01039 }
01040
01045 unsigned int List::CountSubLists(uint32_t ListType) {
01046 unsigned int result = 0;
01047 if (!pSubChunks) LoadSubChunks();
01048 ChunkList::iterator iter = pSubChunks->begin();
01049 ChunkList::iterator end = pSubChunks->end();
01050 while (iter != end) {
01051 if ((*iter)->GetChunkID() == CHUNK_ID_LIST) {
01052 List* l = (List*) *iter;
01053 if (l->GetListType() == ListType) result++;
01054 }
01055 iter++;
01056 }
01057 return result;
01058 }
01059
01073 Chunk* List::AddSubChunk(uint32_t uiChunkID, uint uiBodySize) {
01074 if (uiBodySize == 0) throw Exception("Chunk body size must be at least 1 byte");
01075 if (!pSubChunks) LoadSubChunks();
01076 Chunk* pNewChunk = new Chunk(pFile, this, uiChunkID, 0);
01077 pSubChunks->push_back(pNewChunk);
01078 (*pSubChunksMap)[uiChunkID] = pNewChunk;
01079 pNewChunk->Resize(uiBodySize);
01080 return pNewChunk;
01081 }
01082
01092 List* List::AddSubList(uint32_t uiListType) {
01093 if (!pSubChunks) LoadSubChunks();
01094 List* pNewListChunk = new List(pFile, this, uiListType);
01095 pSubChunks->push_back(pNewListChunk);
01096 (*pSubChunksMap)[CHUNK_ID_LIST] = pNewListChunk;
01097 return pNewListChunk;
01098 }
01099
01109 void List::DeleteSubChunk(Chunk* pSubChunk) {
01110 if (!pSubChunks) LoadSubChunks();
01111 pSubChunks->remove(pSubChunk);
01112 if ((*pSubChunksMap)[pSubChunk->GetChunkID()] == pSubChunk) {
01113 pSubChunksMap->erase(pSubChunk->GetChunkID());
01114
01115 ChunkList::iterator iter = pSubChunks->begin();
01116 ChunkList::iterator end = pSubChunks->end();
01117 for (; iter != end; ++iter) {
01118 if ((*iter)->GetChunkID() == pSubChunk->GetChunkID()) {
01119 (*pSubChunksMap)[pSubChunk->GetChunkID()] = *iter;
01120 break;
01121 }
01122 }
01123 }
01124 delete pSubChunk;
01125 }
01126
01127 void List::ReadHeader(unsigned long fPos) {
01128 #if DEBUG
01129 std::cout << "List::Readheader(ulong) ";
01130 #endif // DEBUG
01131 Chunk::ReadHeader(fPos);
01132 NewChunkSize = CurrentChunkSize -= 4;
01133 #if POSIX
01134 lseek(pFile->hFileRead, fPos + CHUNK_HEADER_SIZE, SEEK_SET);
01135 read(pFile->hFileRead, &ListType, 4);
01136 #else
01137 fseek(pFile->hFileRead, fPos + CHUNK_HEADER_SIZE, SEEK_SET);
01138 fread(&ListType, 4, 1, pFile->hFileRead);
01139 #endif // POSIX
01140 #if DEBUG
01141 std::cout << "listType=" << convertToString(ListType) << std::endl;
01142 #endif // DEBUG
01143 if (!pFile->bEndianNative) {
01144
01145 }
01146 }
01147
01148 void List::WriteHeader(unsigned long fPos) {
01149
01150 NewChunkSize += 4;
01151 Chunk::WriteHeader(fPos);
01152 NewChunkSize -= 4;
01153 #if POSIX
01154 lseek(pFile->hFileWrite, fPos + CHUNK_HEADER_SIZE, SEEK_SET);
01155 write(pFile->hFileWrite, &ListType, 4);
01156 #else
01157 fseek(pFile->hFileWrite, fPos + CHUNK_HEADER_SIZE, SEEK_SET);
01158 fwrite(&ListType, 4, 1, pFile->hFileWrite);
01159 #endif // POSIX
01160 }
01161
01162 void List::LoadSubChunks() {
01163 #if DEBUG
01164 std::cout << "List::LoadSubChunks()";
01165 #endif // DEBUG
01166 if (!pSubChunks) {
01167 pSubChunks = new ChunkList();
01168 pSubChunksMap = new ChunkMap();
01169 if (!pFile->hFileRead) return;
01170 unsigned long uiOriginalPos = GetPos();
01171 SetPos(0);
01172 while (RemainingBytes() >= CHUNK_HEADER_SIZE) {
01173 Chunk* ck;
01174 uint32_t ckid;
01175 Read(&ckid, 4, 1);
01176 #if DEBUG
01177 std::cout << " ckid=" << convertToString(ckid) << std::endl;
01178 #endif // DEBUG
01179 if (ckid == CHUNK_ID_LIST) {
01180 ck = new RIFF::List(pFile, ulStartPos + ulPos - 4, this);
01181 SetPos(ck->GetSize() + LIST_HEADER_SIZE - 4, RIFF::stream_curpos);
01182 }
01183 else {
01184 ck = new RIFF::Chunk(pFile, ulStartPos + ulPos - 4, this);
01185 SetPos(ck->GetSize() + CHUNK_HEADER_SIZE - 4, RIFF::stream_curpos);
01186 }
01187 pSubChunks->push_back(ck);
01188 (*pSubChunksMap)[ckid] = ck;
01189 if (GetPos() % 2 != 0) SetPos(1, RIFF::stream_curpos);
01190 }
01191 SetPos(uiOriginalPos);
01192 }
01193 }
01194
01195 void List::LoadSubChunksRecursively() {
01196 for (List* pList = GetFirstSubList(); pList; pList = GetNextSubList())
01197 pList->LoadSubChunksRecursively();
01198 }
01199
01214 unsigned long List::WriteChunk(unsigned long ulWritePos, unsigned long ulCurrentDataOffset) {
01215 const unsigned long ulOriginalPos = ulWritePos;
01216 ulWritePos += LIST_HEADER_SIZE;
01217
01218 if (pFile->Mode != stream_mode_read_write)
01219 throw Exception("Cannot write list chunk, file has to be opened in read+write mode");
01220
01221
01222 if (pSubChunks) {
01223 for (ChunkList::iterator iter = pSubChunks->begin(), end = pSubChunks->end(); iter != end; ++iter) {
01224 ulWritePos = (*iter)->WriteChunk(ulWritePos, ulCurrentDataOffset);
01225 }
01226 }
01227
01228
01229 CurrentChunkSize = NewChunkSize = ulWritePos - ulOriginalPos - LIST_HEADER_SIZE;
01230 WriteHeader(ulOriginalPos);
01231
01232
01233 ulStartPos = ulOriginalPos + LIST_HEADER_SIZE;
01234
01235 return ulWritePos;
01236 }
01237
01238 void List::__resetPos() {
01239 Chunk::__resetPos();
01240 if (pSubChunks) {
01241 for (ChunkList::iterator iter = pSubChunks->begin(), end = pSubChunks->end(); iter != end; ++iter) {
01242 (*iter)->__resetPos();
01243 }
01244 }
01245 }
01246
01250 String List::GetListTypeString() {
01251 return convertToString(ListType);
01252 }
01253
01254
01255
01256
01257
01258
01268 File::File(uint32_t FileType) : List(this) {
01269 hFileRead = hFileWrite = 0;
01270 Mode = stream_mode_closed;
01271 bEndianNative = true;
01272 ulStartPos = RIFF_HEADER_SIZE;
01273 ListType = FileType;
01274 }
01275
01284 File::File(const String& path) : List(this), Filename(path) {
01285 #if DEBUG
01286 std::cout << "File::File("<<path<<")" << std::endl;
01287 #endif // DEBUG
01288 bEndianNative = true;
01289 #if POSIX
01290 hFileRead = hFileWrite = open(path.c_str(), O_RDONLY | O_NONBLOCK);
01291 if (hFileRead <= 0) {
01292 hFileRead = hFileWrite = 0;
01293 throw RIFF::Exception("Can't open \"" + path + "\"");
01294 }
01295 #else
01296 hFileRead = hFileWrite = fopen(path.c_str(), "rb");
01297 if (!hFile) throw RIFF::Exception("Can't open \"" + path + "\"");
01298 #endif // POSIX
01299 Mode = stream_mode_read;
01300 ulStartPos = RIFF_HEADER_SIZE;
01301 ReadHeader(0);
01302 if (ChunkID != CHUNK_ID_RIFF) {
01303 throw RIFF::Exception("Not a RIFF file");
01304 }
01305 }
01306
01307 String File::GetFileName() {
01308 return Filename;
01309 }
01310
01311 stream_mode_t File::GetMode() {
01312 return Mode;
01313 }
01314
01325 bool File::SetMode(stream_mode_t NewMode) {
01326 if (NewMode != Mode) {
01327 switch (NewMode) {
01328 case stream_mode_read:
01329 #if POSIX
01330 if (hFileRead) close(hFileRead);
01331 hFileRead = hFileWrite = open(Filename.c_str(), O_RDONLY | O_NONBLOCK);
01332 if (hFileRead < 0) {
01333 hFileRead = hFileWrite = 0;
01334 throw Exception("Could not (re)open file \"" + Filename + "\" in read mode");
01335 }
01336 #else
01337 if (hFileRead) fclose(hFileRead);
01338 hFileRead = hFileWrite = fopen(path.c_str(), "rb");
01339 if (!hFileRead) throw Exception("Could not (re)open file \"" + Filename + "\" in read mode");
01340 #endif
01341 __resetPos();
01342 break;
01343 case stream_mode_read_write:
01344 #if POSIX
01345 if (hFileRead) close(hFileRead);
01346 hFileRead = hFileWrite = open(Filename.c_str(), O_RDWR | O_NONBLOCK);
01347 if (hFileRead < 0) {
01348 hFileRead = hFileWrite = open(Filename.c_str(), O_RDONLY | O_NONBLOCK);
01349 throw Exception("Could not open file \"" + Filename + "\" in read+write mode");
01350 }
01351 #else
01352 if (hFileRead) fclose(hFileRead);
01353 hFileRead = hFileWrite = fopen(path.c_str(), "r+b");
01354 if (!hFileRead) {
01355 hFileRead = hFileWrite = fopen(path.c_str(), "rb");
01356 throw Exception("Could not open file \"" + Filename + "\" in read+write mode");
01357 }
01358 #endif
01359 __resetPos();
01360 break;
01361 case stream_mode_closed:
01362 #if POSIX
01363 if (hFileRead) close(hFileRead);
01364 if (hFileWrite) close(hFileWrite);
01365 #else
01366 if (hFileRead) fclose(hFileRead);
01367 if (hFileWrite) fclose(hFileWrite);
01368 #endif
01369 hFileRead = hFileWrite = 0;
01370 break;
01371 default:
01372 throw Exception("Unknown file access mode");
01373 }
01374 Mode = NewMode;
01375 return true;
01376 }
01377 return false;
01378 }
01379
01390 void File::Save() {
01391
01392 LoadSubChunksRecursively();
01393
01394
01395 SetMode(stream_mode_read_write);
01396
01397
01398
01399
01400
01401
01402
01403
01404
01405 unsigned long ulPositiveSizeDiff = 0;
01406 for (ChunkList::iterator iter = ResizedChunks.begin(), end = ResizedChunks.end(); iter != end; ++iter) {
01407 if ((*iter)->GetNewSize() == 0) throw Exception("There is at least one empty chunk (zero size)");
01408 if ((*iter)->GetNewSize() + 1L > (*iter)->GetSize()) {
01409 unsigned long ulDiff = (*iter)->GetNewSize() - (*iter)->GetSize() + 1L;
01410 ulPositiveSizeDiff += ulDiff;
01411 }
01412 }
01413
01414 unsigned long ulWorkingFileSize = GetFileSize();
01415
01416
01417 if (ulPositiveSizeDiff > 0) {
01418
01419 ulWorkingFileSize += ulPositiveSizeDiff;
01420 ResizeFile(ulWorkingFileSize);
01421
01422 int8_t* pCopyBuffer = new int8_t[4096];
01423 const unsigned long ulFileSize = GetSize() + RIFF_HEADER_SIZE;
01424 int iBytesMoved = 1;
01425 for (unsigned long ulPos = 0; iBytesMoved > 0; ulPos += iBytesMoved) {
01426 const unsigned long ulToMove = ulFileSize - ulPos;
01427 iBytesMoved = (ulToMove < 4096) ? ulToMove : 4096;
01428 #if POSIX
01429 lseek(hFileRead, ulPos, SEEK_SET);
01430 iBytesMoved = read(hFileRead, pCopyBuffer, iBytesMoved);
01431 lseek(hFileWrite, ulPos + ulPositiveSizeDiff, SEEK_SET);
01432 iBytesMoved = write(hFileWrite, pCopyBuffer, iBytesMoved);
01433 #else
01434 fseek(hFileRead, ulPos, SEEK_SET);
01435 iBytesMoved = fread(pCopyBuffer, 1, iBytesMoved, hFileRead);
01436 fseek(hFileWrite, ulPos + ulPositiveSizeDiff, SEEK_SET);
01437 iBytesMoved = fwrite(pCopyBuffer, 1, iBytesMoved, hFileWrite);
01438 #endif
01439 }
01440 delete[] pCopyBuffer;
01441 if (iBytesMoved < 0) throw Exception("Could not modify file while trying to enlarge it");
01442 }
01443
01444
01445 unsigned long ulTotalSize = WriteChunk(0, ulPositiveSizeDiff);
01446 unsigned long ulActualSize = __GetFileSize(hFileWrite);
01447
01448
01449 if (ulTotalSize < ulActualSize) ResizeFile(ulTotalSize);
01450
01451
01452 ResizedChunks.clear();
01453 }
01454
01468 void File::Save(const String& path) {
01469
01470
01471
01472 LoadSubChunksRecursively();
01473
01474 if (Filename.length() > 0) SetMode(stream_mode_read);
01475
01476 #if POSIX
01477 hFileWrite = open(path.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP);
01478 if (hFileWrite < 0) {
01479 hFileWrite = hFileRead;
01480 throw Exception("Could not open file \"" + path + "\" for writing");
01481 }
01482 #else
01483 hFileWrite = fopen(path.c_str(), "w+b");
01484 if (!hFileWrite) {
01485 hFileWrite = hFileRead;
01486 throw Exception("Could not open file \"" + path + "\" for writing");
01487 }
01488 #endif // POSIX
01489 Mode = stream_mode_read_write;
01490
01491
01492 unsigned long ulTotalSize = WriteChunk(0, 0);
01493 unsigned long ulActualSize = __GetFileSize(hFileWrite);
01494
01495
01496 if (ulTotalSize < ulActualSize) ResizeFile(ulTotalSize);
01497
01498
01499 ResizedChunks.clear();
01500
01501 if (Filename.length() > 0) {
01502 #if POSIX
01503 close(hFileWrite);
01504 #else
01505 fclose(hFileWrite);
01506 #endif
01507 hFileWrite = hFileRead;
01508 }
01509
01510
01511 Filename = path;
01512 Mode = (stream_mode_t) -1;
01513 SetMode(stream_mode_read_write);
01514 }
01515
01516 void File::ResizeFile(unsigned long ulNewSize) {
01517 #if POSIX
01518 if (ftruncate(hFileWrite, ulNewSize) < 0)
01519 throw Exception("Could not resize file \"" + Filename + "\"");
01520 #else
01521 # error Sorry, this version of libgig only supports POSIX systems yet.
01522 # error Reason: portable implementation of RIFF::File::ResizeFile() is missing (yet)!
01523 #endif
01524 }
01525
01526 File::~File() {
01527 #if DEBUG
01528 std::cout << "File::~File()" << std::endl;
01529 #endif // DEBUG
01530 #if POSIX
01531 if (hFileRead) close(hFileRead);
01532 #else
01533 if (hFileRead) fclose(hFileRead);
01534 #endif // POSIX
01535 }
01536
01537 void File::LogAsResized(Chunk* pResizedChunk) {
01538 ResizedChunks.push_back(pResizedChunk);
01539 }
01540
01541 unsigned long File::GetFileSize() {
01542 return __GetFileSize(hFileRead);
01543 }
01544
01545 #if POSIX
01546 unsigned long File::__GetFileSize(int hFile) {
01547 struct stat filestat;
01548 fstat(hFile, &filestat);
01549 long size = filestat.st_size;
01550 return size;
01551 }
01552 #else // standard C functions
01553 unsigned long File::__GetFileSize(FILE* hFile) {
01554 long curpos = ftell(hFile);
01555 fseek(hFile, 0, SEEK_END);
01556 long size = ftell(hFile);
01557 fseek(hFile, curpos, SEEK_SET);
01558 return size;
01559 }
01560 #endif
01561
01562
01563
01564
01565
01566 void Exception::PrintMessage() {
01567 std::cout << "RIFF::Exception: " << Message << std::endl;
01568 }
01569
01570
01571
01572
01573
01579 String libraryName() {
01580 return PACKAGE;
01581 }
01582
01587 String libraryVersion() {
01588 return VERSION;
01589 }
01590
01591 }