22 |
***************************************************************************/ |
***************************************************************************/ |
23 |
|
|
24 |
#include <string.h> |
#include <string.h> |
|
#include <sstream> |
|
25 |
|
|
26 |
#include "RIFF.h" |
#include "RIFF.h" |
27 |
|
|
28 |
namespace RIFF { |
#include "helper.h" |
|
|
|
|
// *************** Helper Functions ************** |
|
|
// * |
|
|
|
|
|
template<class T> inline String ToString(T o) { |
|
|
std::stringstream ss; |
|
|
ss << o; |
|
|
return ss.str(); |
|
|
} |
|
|
|
|
29 |
|
|
30 |
|
namespace RIFF { |
31 |
|
|
32 |
// *************** Chunk ************** |
// *************** Chunk ************** |
33 |
// * |
// * |
39 |
ulPos = 0; |
ulPos = 0; |
40 |
pParent = NULL; |
pParent = NULL; |
41 |
pChunkData = NULL; |
pChunkData = NULL; |
42 |
|
ulChunkDataSize = 0; |
43 |
ChunkID = CHUNK_ID_RIFF; |
ChunkID = CHUNK_ID_RIFF; |
44 |
this->pFile = pFile; |
this->pFile = pFile; |
45 |
} |
} |
53 |
pParent = Parent; |
pParent = Parent; |
54 |
ulPos = 0; |
ulPos = 0; |
55 |
pChunkData = NULL; |
pChunkData = NULL; |
56 |
|
ulChunkDataSize = 0; |
57 |
ReadHeader(StartPos); |
ReadHeader(StartPos); |
58 |
} |
} |
59 |
|
|
63 |
this->pParent = pParent; |
this->pParent = pParent; |
64 |
ulPos = 0; |
ulPos = 0; |
65 |
pChunkData = NULL; |
pChunkData = NULL; |
66 |
|
ulChunkDataSize = 0; |
67 |
ChunkID = uiChunkID; |
ChunkID = uiChunkID; |
68 |
CurrentChunkSize = 0; |
CurrentChunkSize = 0; |
69 |
NewChunkSize = uiBodySize; |
NewChunkSize = uiBodySize; |
88 |
#endif // POSIX |
#endif // POSIX |
89 |
#if WORDS_BIGENDIAN |
#if WORDS_BIGENDIAN |
90 |
if (ChunkID == CHUNK_ID_RIFF) { |
if (ChunkID == CHUNK_ID_RIFF) { |
91 |
bEndianNative = false; |
pFile->bEndianNative = false; |
92 |
} |
} |
93 |
#else // little endian |
#else // little endian |
94 |
if (ChunkID == CHUNK_ID_RIFX) { |
if (ChunkID == CHUNK_ID_RIFX) { |
661 |
* |
* |
662 |
* <b>Caution:</b> the buffer pointer will be invalidated once |
* <b>Caution:</b> the buffer pointer will be invalidated once |
663 |
* File::Save() was called. You have to call LoadChunkData() again to |
* File::Save() was called. You have to call LoadChunkData() again to |
664 |
* get a new pointer whenever File::Save() was called. |
* get a new, valid pointer whenever File::Save() was called. |
665 |
|
* |
666 |
|
* You can call LoadChunkData() again if you previously scheduled to |
667 |
|
* enlarge this chunk with a Resize() call. In that case the buffer will |
668 |
|
* be enlarged to the new, scheduled chunk size and you can already |
669 |
|
* place the new chunk data to the buffer and finally call File::Save() |
670 |
|
* to enlarge the chunk physically and write the new data in one rush. |
671 |
|
* This approach is definitely recommended if you have to enlarge and |
672 |
|
* write new data to a lot of chunks. |
673 |
* |
* |
674 |
* @returns a pointer to the data in RAM on success, NULL otherwise |
* @returns a pointer to the data in RAM on success, NULL otherwise |
675 |
|
* @throws Exception if data buffer could not be enlarged |
676 |
* @see ReleaseChunkData() |
* @see ReleaseChunkData() |
677 |
*/ |
*/ |
678 |
void* Chunk::LoadChunkData() { |
void* Chunk::LoadChunkData() { |
679 |
if (!pChunkData) { |
if (!pChunkData && pFile->Filename != "") { |
680 |
#if POSIX |
#if POSIX |
681 |
if (lseek(pFile->hFileRead, ulStartPos, SEEK_SET) == -1) return NULL; |
if (lseek(pFile->hFileRead, ulStartPos, SEEK_SET) == -1) return NULL; |
|
pChunkData = new uint8_t[GetSize()]; |
|
|
if (!pChunkData) return NULL; |
|
|
unsigned long readWords = read(pFile->hFileRead, pChunkData, GetSize()); |
|
682 |
#else |
#else |
683 |
if (fseek(pFile->hFileRead, ulStartPos, SEEK_SET)) return NULL; |
if (fseek(pFile->hFileRead, ulStartPos, SEEK_SET)) return NULL; |
684 |
pChunkData = new uint8_t[GetSize()]; |
#endif // POSIX |
685 |
|
unsigned long ulBufferSize = (CurrentChunkSize > NewChunkSize) ? CurrentChunkSize : NewChunkSize; |
686 |
|
pChunkData = new uint8_t[ulBufferSize]; |
687 |
if (!pChunkData) return NULL; |
if (!pChunkData) return NULL; |
688 |
|
memset(pChunkData, 0, ulBufferSize); |
689 |
|
#if POSIX |
690 |
|
unsigned long readWords = read(pFile->hFileRead, pChunkData, GetSize()); |
691 |
|
#else |
692 |
unsigned long readWords = fread(pChunkData, 1, GetSize(), pFile->hFileRead); |
unsigned long readWords = fread(pChunkData, 1, GetSize(), pFile->hFileRead); |
693 |
#endif // POSIX |
#endif // POSIX |
694 |
if (readWords != GetSize()) { |
if (readWords != GetSize()) { |
695 |
delete[] pChunkData; |
delete[] pChunkData; |
696 |
return (pChunkData = NULL); |
return (pChunkData = NULL); |
697 |
} |
} |
698 |
|
ulChunkDataSize = ulBufferSize; |
699 |
|
} else if (NewChunkSize > ulChunkDataSize) { |
700 |
|
uint8_t* pNewBuffer = new uint8_t[NewChunkSize]; |
701 |
|
if (!pNewBuffer) throw Exception("Could not enlarge chunk data buffer to " + ToString(NewChunkSize) + " bytes"); |
702 |
|
memset(pNewBuffer, 0 , NewChunkSize); |
703 |
|
memcpy(pNewBuffer, pChunkData, ulChunkDataSize); |
704 |
|
delete[] pChunkData; |
705 |
|
pChunkData = pNewBuffer; |
706 |
|
ulChunkDataSize = NewChunkSize; |
707 |
} |
} |
708 |
return pChunkData; |
return pChunkData; |
709 |
} |
} |
733 |
* |
* |
734 |
* <b>Caution:</b> You cannot directly write to enlarged chunks before |
* <b>Caution:</b> You cannot directly write to enlarged chunks before |
735 |
* calling File::Save() as this might exceed the current chunk's body |
* calling File::Save() as this might exceed the current chunk's body |
736 |
* boundary. |
* boundary! |
737 |
* |
* |
738 |
* @param iNewSize - new chunk body size in bytes (must be greater than zero) |
* @param iNewSize - new chunk body size in bytes (must be greater than zero) |
739 |
* @throws RIFF::Exception if \a iNewSize is less than 1 |
* @throws RIFF::Exception if \a iNewSize is less than 1 |
741 |
*/ |
*/ |
742 |
void Chunk::Resize(int iNewSize) { |
void Chunk::Resize(int iNewSize) { |
743 |
if (iNewSize <= 0) throw Exception("Chunk size must be at least one byte"); |
if (iNewSize <= 0) throw Exception("Chunk size must be at least one byte"); |
744 |
|
if (NewChunkSize == iNewSize) return; |
745 |
NewChunkSize = iNewSize; |
NewChunkSize = iNewSize; |
746 |
pFile->LogAsResized(this); |
pFile->LogAsResized(this); |
747 |
} |
} |
767 |
|
|
768 |
// if the whole chunk body was loaded into RAM |
// if the whole chunk body was loaded into RAM |
769 |
if (pChunkData) { |
if (pChunkData) { |
770 |
// in case the chunk size was changed, reallocate the data in RAM with the chunk's new size |
// make sure chunk data buffer in RAM is at least as large as the new chunk size |
771 |
if (NewChunkSize != CurrentChunkSize) { |
LoadChunkData(); |
|
uint8_t* pNewBuffer = new uint8_t[NewChunkSize]; |
|
|
if (NewChunkSize > CurrentChunkSize) { |
|
|
memcpy(pNewBuffer, pChunkData, CurrentChunkSize); |
|
|
memset(pNewBuffer + CurrentChunkSize, 0, NewChunkSize - CurrentChunkSize); |
|
|
} else { |
|
|
memcpy(pNewBuffer, pChunkData, NewChunkSize); |
|
|
} |
|
|
delete[] pChunkData; |
|
|
pChunkData = pNewBuffer; |
|
|
} |
|
|
|
|
772 |
// write chunk data from RAM persistently to the file |
// write chunk data from RAM persistently to the file |
773 |
#if POSIX |
#if POSIX |
774 |
lseek(pFile->hFileWrite, ulWritePos, SEEK_SET); |
lseek(pFile->hFileWrite, ulWritePos, SEEK_SET); |
1192 |
} |
} |
1193 |
} |
} |
1194 |
|
|
1195 |
|
void List::LoadSubChunksRecursively() { |
1196 |
|
for (List* pList = GetFirstSubList(); pList; pList = GetNextSubList()) |
1197 |
|
pList->LoadSubChunksRecursively(); |
1198 |
|
} |
1199 |
|
|
1200 |
/** @brief Write list chunk persistently e.g. to disk. |
/** @brief Write list chunk persistently e.g. to disk. |
1201 |
* |
* |
1202 |
* Stores the list chunk persistently to its actual "physical" file. All |
* Stores the list chunk persistently to its actual "physical" file. All |
1388 |
* chunk or any kind of IO error occured |
* chunk or any kind of IO error occured |
1389 |
*/ |
*/ |
1390 |
void File::Save() { |
void File::Save() { |
1391 |
|
// make sure the RIFF tree is built (from the original file) |
1392 |
|
LoadSubChunksRecursively(); |
1393 |
|
|
1394 |
// reopen file in write mode |
// reopen file in write mode |
1395 |
SetMode(stream_mode_read_write); |
SetMode(stream_mode_read_write); |
1396 |
|
|
1468 |
void File::Save(const String& path) { |
void File::Save(const String& path) { |
1469 |
//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 |
//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 |
1470 |
|
|
1471 |
|
// make sure the RIFF tree is built (from the original file) |
1472 |
|
LoadSubChunksRecursively(); |
1473 |
|
|
1474 |
if (Filename.length() > 0) SetMode(stream_mode_read); |
if (Filename.length() > 0) SetMode(stream_mode_read); |
1475 |
// open the other (new) file for writing and truncate it to zero size |
// open the other (new) file for writing and truncate it to zero size |
1476 |
#if POSIX |
#if POSIX |