/[svn]/libgig/trunk/src/RIFF.cpp
ViewVC logotype

Annotation of /libgig/trunk/src/RIFF.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1102 - (hide annotations) (download)
Sun Mar 18 07:13:06 2007 UTC (17 years ago) by persson
File size: 73334 byte(s)
* added MoveRegion and MoveSubChunk
* fixed initialization in AddRegion

1 schoenebeck 2 /***************************************************************************
2     * *
3 schoenebeck 933 * libgig - C++ cross-platform Gigasampler format file access library *
4 schoenebeck 2 * *
5 schoenebeck 1050 * Copyright (C) 2003-2007 by Christian Schoenebeck *
6 schoenebeck 384 * <cuse@users.sourceforge.net> *
7 schoenebeck 2 * *
8     * This library is free software; you can redistribute it and/or modify *
9     * it under the terms of the GNU General Public License as published by *
10     * the Free Software Foundation; either version 2 of the License, or *
11     * (at your option) any later version. *
12     * *
13     * This library is distributed in the hope that it will be useful, *
14     * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16     * GNU General Public License for more details. *
17     * *
18     * You should have received a copy of the GNU General Public License *
19     * along with this library; if not, write to the Free Software *
20     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, *
21     * MA 02111-1307 USA *
22     ***************************************************************************/
23 schoenebeck 780
24     #include <string.h>
25    
26 schoenebeck 2 #include "RIFF.h"
27    
28 schoenebeck 800 #include "helper.h"
29    
30 schoenebeck 2 namespace RIFF {
31    
32     // *************** Chunk **************
33     // *
34    
35 schoenebeck 780 Chunk::Chunk(File* pFile) {
36 schoenebeck 2 #if DEBUG
37 schoenebeck 780 std::cout << "Chunk::Chunk(File* pFile)" << std::endl;
38 schoenebeck 2 #endif // DEBUG
39     ulPos = 0;
40     pParent = NULL;
41     pChunkData = NULL;
42 schoenebeck 800 ulChunkDataSize = 0;
43 schoenebeck 798 ChunkID = CHUNK_ID_RIFF;
44 schoenebeck 780 this->pFile = pFile;
45 schoenebeck 2 }
46    
47 schoenebeck 780 Chunk::Chunk(File* pFile, unsigned long StartPos, List* Parent) {
48 schoenebeck 2 #if DEBUG
49 schoenebeck 780 std::cout << "Chunk::Chunk(File*,ulong,bool,List*),StartPos=" << StartPos << std::endl;
50 schoenebeck 2 #endif // DEBUG
51 schoenebeck 780 this->pFile = pFile;
52 schoenebeck 2 ulStartPos = StartPos + CHUNK_HEADER_SIZE;
53     pParent = Parent;
54     ulPos = 0;
55     pChunkData = NULL;
56 schoenebeck 800 ulChunkDataSize = 0;
57 schoenebeck 2 ReadHeader(StartPos);
58     }
59    
60 schoenebeck 780 Chunk::Chunk(File* pFile, List* pParent, uint32_t uiChunkID, uint uiBodySize) {
61     this->pFile = pFile;
62     ulStartPos = 0; // arbitrary usually, since it will be updated when we write the chunk
63     this->pParent = pParent;
64     ulPos = 0;
65     pChunkData = NULL;
66 schoenebeck 800 ulChunkDataSize = 0;
67 schoenebeck 780 ChunkID = uiChunkID;
68     CurrentChunkSize = 0;
69     NewChunkSize = uiBodySize;
70     }
71    
72 schoenebeck 2 Chunk::~Chunk() {
73 schoenebeck 1095 if (CurrentChunkSize != NewChunkSize) pFile->UnlogResized(this);
74 schoenebeck 2 if (pChunkData) delete[] pChunkData;
75     }
76    
77     void Chunk::ReadHeader(unsigned long fPos) {
78     #if DEBUG
79     std::cout << "Chunk::Readheader(" << fPos << ") ";
80     #endif // DEBUG
81     #if POSIX
82 schoenebeck 780 if (lseek(pFile->hFileRead, fPos, SEEK_SET) != -1) {
83     read(pFile->hFileRead, &ChunkID, 4);
84     read(pFile->hFileRead, &CurrentChunkSize, 4);
85 schoenebeck 1050 #elif defined(WIN32)
86     if (SetFilePointer(pFile->hFileRead, fPos, NULL/*32 bit*/, FILE_BEGIN) != INVALID_SET_FILE_POINTER) {
87     DWORD dwBytesRead;
88     ReadFile(pFile->hFileRead, &ChunkID, 4, &dwBytesRead, NULL);
89     ReadFile(pFile->hFileRead, &CurrentChunkSize, 4, &dwBytesRead, NULL);
90 schoenebeck 2 #else
91 schoenebeck 780 if (!fseek(pFile->hFileRead, fPos, SEEK_SET)) {
92     fread(&ChunkID, 4, 1, pFile->hFileRead);
93     fread(&CurrentChunkSize, 4, 1, pFile->hFileRead);
94 schoenebeck 2 #endif // POSIX
95     #if WORDS_BIGENDIAN
96     if (ChunkID == CHUNK_ID_RIFF) {
97 schoenebeck 808 pFile->bEndianNative = false;
98 schoenebeck 2 }
99     #else // little endian
100     if (ChunkID == CHUNK_ID_RIFX) {
101 schoenebeck 780 pFile->bEndianNative = false;
102 schoenebeck 2 ChunkID = CHUNK_ID_RIFF;
103     }
104     #endif // WORDS_BIGENDIAN
105 schoenebeck 780 if (!pFile->bEndianNative) {
106 schoenebeck 11 //swapBytes_32(&ChunkID);
107 schoenebeck 780 swapBytes_32(&CurrentChunkSize);
108 schoenebeck 2 }
109 schoenebeck 11 #if DEBUG
110     std::cout << "ckID=" << convertToString(ChunkID) << " ";
111     std::cout << "ckSize=" << ChunkSize << " ";
112     std::cout << "bEndianNative=" << bEndianNative << std::endl;
113     #endif // DEBUG
114 schoenebeck 780 NewChunkSize = CurrentChunkSize;
115 schoenebeck 2 }
116     }
117    
118 schoenebeck 780 void Chunk::WriteHeader(unsigned long fPos) {
119     uint32_t uiNewChunkID = ChunkID;
120     if (ChunkID == CHUNK_ID_RIFF) {
121     #if WORDS_BIGENDIAN
122     if (pFile->bEndianNative) uiNewChunkID = CHUNK_ID_RIFX;
123     #else // little endian
124     if (!pFile->bEndianNative) uiNewChunkID = CHUNK_ID_RIFX;
125     #endif // WORDS_BIGENDIAN
126     }
127    
128     uint32_t uiNewChunkSize = NewChunkSize;
129     if (!pFile->bEndianNative) {
130     swapBytes_32(&uiNewChunkSize);
131     }
132    
133     #if POSIX
134     if (lseek(pFile->hFileWrite, fPos, SEEK_SET) != -1) {
135     write(pFile->hFileWrite, &uiNewChunkID, 4);
136     write(pFile->hFileWrite, &uiNewChunkSize, 4);
137     }
138 schoenebeck 1050 #elif defined(WIN32)
139     if (SetFilePointer(pFile->hFileWrite, fPos, NULL/*32 bit*/, FILE_BEGIN) != INVALID_SET_FILE_POINTER) {
140     DWORD dwBytesWritten;
141     WriteFile(pFile->hFileWrite, &uiNewChunkID, 4, &dwBytesWritten, NULL);
142     WriteFile(pFile->hFileWrite, &uiNewChunkSize, 4, &dwBytesWritten, NULL);
143     }
144 schoenebeck 780 #else
145     if (!fseek(pFile->hFileWrite, fPos, SEEK_SET)) {
146     fwrite(&uiNewChunkID, 4, 1, pFile->hFileWrite);
147     fwrite(&uiNewChunkSize, 4, 1, pFile->hFileWrite);
148     }
149     #endif // POSIX
150     }
151    
152 schoenebeck 11 /**
153     * Returns the String representation of the chunk's ID (e.g. "RIFF",
154     * "LIST").
155     */
156 schoenebeck 2 String Chunk::GetChunkIDString() {
157     return convertToString(ChunkID);
158     }
159    
160 schoenebeck 11 /**
161     * Sets the position within the chunk body, thus within the data portion
162     * of the chunk (in bytes).
163     *
164 schoenebeck 780 * <b>Caution:</b> the position will be reset to zero whenever
165     * File::Save() was called.
166     *
167 schoenebeck 11 * @param Where - position offset (in bytes)
168     * @param Whence - optional: defines to what <i>\a Where</i> relates to,
169     * if omitted \a Where relates to beginning of the chunk
170     * data
171     */
172 schoenebeck 2 unsigned long Chunk::SetPos(unsigned long Where, stream_whence_t Whence) {
173     #if DEBUG
174     std::cout << "Chunk::SetPos(ulong)" << std::endl;
175     #endif // DEBUG
176     switch (Whence) {
177     case stream_curpos:
178     ulPos += Where;
179     break;
180     case stream_end:
181 schoenebeck 780 ulPos = CurrentChunkSize - 1 - Where;
182 schoenebeck 2 break;
183     case stream_backward:
184     ulPos -= Where;
185     break;
186     case stream_start: default:
187     ulPos = Where;
188     break;
189     }
190 schoenebeck 780 if (ulPos > CurrentChunkSize) ulPos = CurrentChunkSize;
191 schoenebeck 2 return ulPos;
192     }
193    
194 schoenebeck 11 /**
195     * Returns the number of bytes left to read in the chunk body.
196     * When reading data from the chunk using the Read*() Methods, the
197     * position within the chunk data (that is the chunk body) will be
198     * incremented by the number of read bytes and RemainingBytes() returns
199     * how much data is left to read from the current position to the end
200     * of the chunk data.
201     *
202     * @returns number of bytes left to read
203     */
204 schoenebeck 2 unsigned long Chunk::RemainingBytes() {
205     #if DEBUG
206 schoenebeck 780 std::cout << "Chunk::Remainingbytes()=" << CurrentChunkSize - ulPos << std::endl;
207 schoenebeck 2 #endif // DEBUG
208 schoenebeck 780 return CurrentChunkSize - ulPos;
209 schoenebeck 2 }
210    
211 schoenebeck 11 /**
212     * Returns the current state of the chunk object.
213     * Following values are possible:
214     * - RIFF::stream_ready :
215     * chunk data can be read (this is the usual case)
216     * - RIFF::stream_closed :
217     * the data stream was closed somehow, no more reading possible
218     * - RIFF::stream_end_reached :
219     * alreaady reached the end of the chunk data, no more reading
220     * possible without SetPos()
221     */
222 schoenebeck 2 stream_state_t Chunk::GetState() {
223     #if DEBUG
224     std::cout << "Chunk::GetState()" << std::endl;
225     #endif // DEBUG
226     #if POSIX
227 schoenebeck 1050 if (pFile->hFileRead == 0) return stream_closed;
228     #elif defined (WIN32)
229     if (pFile->hFileRead == INVALID_HANDLE_VALUE)
230     return stream_closed;
231 schoenebeck 2 #else
232 schoenebeck 780 if (pFile->hFileRead == NULL) return stream_closed;
233 schoenebeck 2 #endif // POSIX
234 schoenebeck 780 if (ulPos < CurrentChunkSize) return stream_ready;
235     else return stream_end_reached;
236 schoenebeck 2 }
237    
238     /**
239     * Reads \a WordCount number of data words with given \a WordSize and
240     * copies it into a buffer pointed by \a pData. The buffer has to be
241     * allocated and be sure to provide the correct \a WordSize, as this
242     * will be important and taken into account for eventual endian
243     * correction (swapping of bytes due to different native byte order of
244     * a system). The position within the chunk will automatically be
245     * incremented.
246     *
247     * @param pData destination buffer
248     * @param WordCount number of data words to read
249     * @param WordSize size of each data word to read
250     * @returns number of successfully read data words or 0 if end
251     * of file reached or error occured
252     */
253     unsigned long Chunk::Read(void* pData, unsigned long WordCount, unsigned long WordSize) {
254     #if DEBUG
255     std::cout << "Chunk::Read(void*,ulong,ulong)" << std::endl;
256     #endif // DEBUG
257 schoenebeck 780 if (ulPos >= CurrentChunkSize) return 0;
258     if (ulPos + WordCount * WordSize >= CurrentChunkSize) WordCount = (CurrentChunkSize - ulPos) / WordSize;
259 schoenebeck 2 #if POSIX
260 schoenebeck 780 if (lseek(pFile->hFileRead, ulStartPos + ulPos, SEEK_SET) < 0) return 0;
261     unsigned long readWords = read(pFile->hFileRead, pData, WordCount * WordSize);
262 schoenebeck 2 if (readWords < 1) return 0;
263     readWords /= WordSize;
264 schoenebeck 1050 #elif defined(WIN32)
265     if (SetFilePointer(pFile->hFileRead, ulStartPos + ulPos, NULL/*32 bit*/, FILE_BEGIN) == INVALID_SET_FILE_POINTER) return 0;
266     DWORD readWords;
267     ReadFile(pFile->hFileRead, pData, WordCount * WordSize, &readWords, NULL);
268     if (readWords < 1) return 0;
269     readWords /= WordSize;
270 schoenebeck 2 #else // standard C functions
271 schoenebeck 780 if (fseek(pFile->hFileRead, ulStartPos + ulPos, SEEK_SET)) return 0;
272     unsigned long readWords = fread(pData, WordSize, WordCount, pFile->hFileRead);
273 schoenebeck 2 #endif // POSIX
274 schoenebeck 780 if (!pFile->bEndianNative && WordSize != 1) {
275 schoenebeck 2 switch (WordSize) {
276     case 2:
277     for (unsigned long iWord = 0; iWord < readWords; iWord++)
278     swapBytes_16((uint16_t*) pData + iWord);
279     break;
280     case 4:
281     for (unsigned long iWord = 0; iWord < readWords; iWord++)
282     swapBytes_32((uint32_t*) pData + iWord);
283     break;
284     default:
285     for (unsigned long iWord = 0; iWord < readWords; iWord++)
286     swapBytes((uint8_t*) pData + iWord * WordSize, WordSize);
287     break;
288     }
289     }
290     SetPos(readWords * WordSize, stream_curpos);
291     return readWords;
292     }
293    
294 schoenebeck 780 /**
295     * Writes \a WordCount number of data words with given \a WordSize from
296     * the buffer pointed by \a pData. Be sure to provide the correct
297     * \a WordSize, as this will be important and taken into account for
298     * eventual endian correction (swapping of bytes due to different
299     * native byte order of a system). The position within the chunk will
300     * automatically be incremented.
301     *
302     * @param pData source buffer (containing the data)
303     * @param WordCount number of data words to write
304     * @param WordSize size of each data word to write
305     * @returns number of successfully written data words
306     * @throws RIFF::Exception if write operation would exceed current
307     * chunk size or any IO error occured
308     * @see Resize()
309     */
310     unsigned long Chunk::Write(void* pData, unsigned long WordCount, unsigned long WordSize) {
311 schoenebeck 798 if (pFile->Mode != stream_mode_read_write)
312     throw Exception("Cannot write data to chunk, file has to be opened in read+write mode first");
313     if (ulPos >= CurrentChunkSize || ulPos + WordCount * WordSize > CurrentChunkSize)
314 schoenebeck 780 throw Exception("End of chunk reached while trying to write data");
315     if (!pFile->bEndianNative && WordSize != 1) {
316     switch (WordSize) {
317     case 2:
318     for (unsigned long iWord = 0; iWord < WordCount; iWord++)
319     swapBytes_16((uint16_t*) pData + iWord);
320     break;
321     case 4:
322     for (unsigned long iWord = 0; iWord < WordCount; iWord++)
323     swapBytes_32((uint32_t*) pData + iWord);
324     break;
325     default:
326     for (unsigned long iWord = 0; iWord < WordCount; iWord++)
327     swapBytes((uint8_t*) pData + iWord * WordSize, WordSize);
328     break;
329     }
330     }
331     #if POSIX
332     if (lseek(pFile->hFileWrite, ulStartPos + ulPos, SEEK_SET) < 0) {
333     throw Exception("Could not seek to position " + ToString(ulPos) +
334     " in chunk (" + ToString(ulStartPos + ulPos) + " in file)");
335     }
336     unsigned long writtenWords = write(pFile->hFileWrite, pData, WordCount * WordSize);
337     if (writtenWords < 1) throw Exception("POSIX IO Error while trying to write chunk data");
338     writtenWords /= WordSize;
339 schoenebeck 1050 #elif defined(WIN32)
340     if (SetFilePointer(pFile->hFileWrite, ulStartPos + ulPos, NULL/*32 bit*/, FILE_BEGIN) == INVALID_SET_FILE_POINTER) {
341     throw Exception("Could not seek to position " + ToString(ulPos) +
342     " in chunk (" + ToString(ulStartPos + ulPos) + " in file)");
343     }
344     DWORD writtenWords;
345     WriteFile(pFile->hFileWrite, pData, WordCount * WordSize, &writtenWords, NULL);
346     if (writtenWords < 1) throw Exception("Windows IO Error while trying to write chunk data");
347     writtenWords /= WordSize;
348 schoenebeck 780 #else // standard C functions
349     if (fseek(pFile->hFileWrite, ulStartPos + ulPos, SEEK_SET)) {
350     throw Exception("Could not seek to position " + ToString(ulPos) +
351     " in chunk (" + ToString(ulStartPos + ulPos) + " in file)");
352     }
353     unsigned long writtenWords = fwrite(pData, WordSize, WordCount, pFile->hFileWrite);
354     #endif // POSIX
355     SetPos(writtenWords * WordSize, stream_curpos);
356     return writtenWords;
357     }
358    
359 schoenebeck 2 /** Just an internal wrapper for the main <i>Read()</i> method with additional Exception throwing on errors. */
360     unsigned long Chunk::ReadSceptical(void* pData, unsigned long WordCount, unsigned long WordSize) {
361     unsigned long readWords = Read(pData, WordCount, WordSize);
362     if (readWords != WordCount) throw RIFF::Exception("End of chunk data reached.");
363     return readWords;
364     }
365    
366     /**
367     * Reads \a WordCount number of 8 Bit signed integer words and copies it
368     * into the buffer pointed by \a pData. The buffer has to be allocated.
369     * The position within the chunk will automatically be incremented.
370     *
371     * @param pData destination buffer
372     * @param WordCount number of 8 Bit signed integers to read
373     * @returns number of read integers
374     * @throws RIFF::Exception if an error occured or less than
375     * \a WordCount integers could be read!
376     */
377     unsigned long Chunk::ReadInt8(int8_t* pData, unsigned long WordCount) {
378     #if DEBUG
379     std::cout << "Chunk::ReadInt8(int8_t*,ulong)" << std::endl;
380     #endif // DEBUG
381     return ReadSceptical(pData, WordCount, 1);
382     }
383    
384     /**
385 schoenebeck 780 * Writes \a WordCount number of 8 Bit signed integer words from the
386     * buffer pointed by \a pData to the chunk's body, directly to the
387     * actual "physical" file. The position within the chunk will
388     * automatically be incremented. Note: you cannot write beyond the
389     * boundaries of the chunk, to append data to the chunk call Resize()
390     * before.
391     *
392     * @param pData source buffer (containing the data)
393     * @param WordCount number of 8 Bit signed integers to write
394     * @returns number of written integers
395     * @throws RIFF::Exception if an IO error occured
396     * @see Resize()
397     */
398     unsigned long Chunk::WriteInt8(int8_t* pData, unsigned long WordCount) {
399     return Write(pData, WordCount, 1);
400     }
401    
402     /**
403 schoenebeck 2 * Reads \a WordCount number of 8 Bit unsigned integer words and copies
404     * it into the buffer pointed by \a pData. The buffer has to be
405     * allocated. The position within the chunk will automatically be
406     * incremented.
407     *
408     * @param pData destination buffer
409     * @param WordCount number of 8 Bit unsigned integers to read
410     * @returns number of read integers
411     * @throws RIFF::Exception if an error occured or less than
412     * \a WordCount integers could be read!
413     */
414     unsigned long Chunk::ReadUint8(uint8_t* pData, unsigned long WordCount) {
415     #if DEBUG
416     std::cout << "Chunk::ReadUint8(uint8_t*,ulong)" << std::endl;
417     #endif // DEBUG
418     return ReadSceptical(pData, WordCount, 1);
419     }
420    
421     /**
422 schoenebeck 780 * Writes \a WordCount number of 8 Bit unsigned integer words from the
423     * buffer pointed by \a pData to the chunk's body, directly to the
424     * actual "physical" file. The position within the chunk will
425     * automatically be incremented. Note: you cannot write beyond the
426     * boundaries of the chunk, to append data to the chunk call Resize()
427     * before.
428     *
429     * @param pData source buffer (containing the data)
430     * @param WordCount number of 8 Bit unsigned integers to write
431     * @returns number of written integers
432     * @throws RIFF::Exception if an IO error occured
433     * @see Resize()
434     */
435     unsigned long Chunk::WriteUint8(uint8_t* pData, unsigned long WordCount) {
436     return Write(pData, WordCount, 1);
437     }
438    
439     /**
440 schoenebeck 2 * Reads \a WordCount number of 16 Bit signed integer words and copies
441     * it into the buffer pointed by \a pData. The buffer has to be
442     * allocated. Endian correction will automatically be done if needed.
443     * The position within the chunk will automatically be incremented.
444     *
445     * @param pData destination buffer
446     * @param WordCount number of 16 Bit signed integers to read
447     * @returns number of read integers
448     * @throws RIFF::Exception if an error occured or less than
449     * \a WordCount integers could be read!
450     */
451     unsigned long Chunk::ReadInt16(int16_t* pData, unsigned long WordCount) {
452     #if DEBUG
453     std::cout << "Chunk::ReadInt16(int16_t*,ulong)" << std::endl;
454     #endif // DEBUG
455     return ReadSceptical(pData, WordCount, 2);
456     }
457    
458     /**
459 schoenebeck 780 * Writes \a WordCount number of 16 Bit signed integer words from the
460     * buffer pointed by \a pData to the chunk's body, directly to the
461     * actual "physical" file. The position within the chunk will
462     * automatically be incremented. Note: you cannot write beyond the
463     * boundaries of the chunk, to append data to the chunk call Resize()
464     * before.
465     *
466     * @param pData source buffer (containing the data)
467     * @param WordCount number of 16 Bit signed integers to write
468     * @returns number of written integers
469     * @throws RIFF::Exception if an IO error occured
470     * @see Resize()
471     */
472     unsigned long Chunk::WriteInt16(int16_t* pData, unsigned long WordCount) {
473     return Write(pData, WordCount, 2);
474     }
475    
476     /**
477 schoenebeck 2 * Reads \a WordCount number of 16 Bit unsigned integer words and copies
478     * it into the buffer pointed by \a pData. The buffer has to be
479     * allocated. Endian correction will automatically be done if needed.
480     * The position within the chunk will automatically be incremented.
481     *
482     * @param pData destination buffer
483     * @param WordCount number of 8 Bit unsigned integers to read
484     * @returns number of read integers
485     * @throws RIFF::Exception if an error occured or less than
486     * \a WordCount integers could be read!
487     */
488     unsigned long Chunk::ReadUint16(uint16_t* pData, unsigned long WordCount) {
489     #if DEBUG
490     std::cout << "Chunk::ReadUint16(uint16_t*,ulong)" << std::endl;
491     #endif // DEBUG
492     return ReadSceptical(pData, WordCount, 2);
493     }
494    
495     /**
496 schoenebeck 780 * Writes \a WordCount number of 16 Bit unsigned integer words from the
497     * buffer pointed by \a pData to the chunk's body, directly to the
498     * actual "physical" file. The position within the chunk will
499     * automatically be incremented. Note: you cannot write beyond the
500     * boundaries of the chunk, to append data to the chunk call Resize()
501     * before.
502     *
503     * @param pData source buffer (containing the data)
504     * @param WordCount number of 16 Bit unsigned integers to write
505     * @returns number of written integers
506     * @throws RIFF::Exception if an IO error occured
507     * @see Resize()
508     */
509     unsigned long Chunk::WriteUint16(uint16_t* pData, unsigned long WordCount) {
510     return Write(pData, WordCount, 2);
511     }
512    
513     /**
514 schoenebeck 2 * Reads \a WordCount number of 32 Bit signed integer words and copies
515     * it into the buffer pointed by \a pData. The buffer has to be
516     * allocated. Endian correction will automatically be done if needed.
517     * The position within the chunk will automatically be incremented.
518     *
519     * @param pData destination buffer
520     * @param WordCount number of 32 Bit signed integers to read
521     * @returns number of read integers
522     * @throws RIFF::Exception if an error occured or less than
523     * \a WordCount integers could be read!
524     */
525     unsigned long Chunk::ReadInt32(int32_t* pData, unsigned long WordCount) {
526     #if DEBUG
527     std::cout << "Chunk::ReadInt32(int32_t*,ulong)" << std::endl;
528     #endif // DEBUG
529     return ReadSceptical(pData, WordCount, 4);
530     }
531    
532     /**
533 schoenebeck 780 * Writes \a WordCount number of 32 Bit signed integer words from the
534     * buffer pointed by \a pData to the chunk's body, directly to the
535     * actual "physical" file. The position within the chunk will
536     * automatically be incremented. Note: you cannot write beyond the
537     * boundaries of the chunk, to append data to the chunk call Resize()
538     * before.
539     *
540     * @param pData source buffer (containing the data)
541     * @param WordCount number of 32 Bit signed integers to write
542     * @returns number of written integers
543     * @throws RIFF::Exception if an IO error occured
544     * @see Resize()
545     */
546     unsigned long Chunk::WriteInt32(int32_t* pData, unsigned long WordCount) {
547     return Write(pData, WordCount, 4);
548     }
549    
550     /**
551 schoenebeck 2 * Reads \a WordCount number of 32 Bit unsigned integer words and copies
552     * it into the buffer pointed by \a pData. The buffer has to be
553     * allocated. Endian correction will automatically be done if needed.
554     * The position within the chunk will automatically be incremented.
555     *
556     * @param pData destination buffer
557     * @param WordCount number of 32 Bit unsigned integers to read
558     * @returns number of read integers
559     * @throws RIFF::Exception if an error occured or less than
560     * \a WordCount integers could be read!
561     */
562     unsigned long Chunk::ReadUint32(uint32_t* pData, unsigned long WordCount) {
563     #if DEBUG
564     std::cout << "Chunk::ReadUint32(uint32_t*,ulong)" << std::endl;
565     #endif // DEBUG
566     return ReadSceptical(pData, WordCount, 4);
567     }
568    
569     /**
570 schoenebeck 780 * Writes \a WordCount number of 32 Bit unsigned integer words from the
571     * buffer pointed by \a pData to the chunk's body, directly to the
572     * actual "physical" file. The position within the chunk will
573     * automatically be incremented. Note: you cannot write beyond the
574     * boundaries of the chunk, to append data to the chunk call Resize()
575     * before.
576     *
577     * @param pData source buffer (containing the data)
578     * @param WordCount number of 32 Bit unsigned integers to write
579     * @returns number of written integers
580     * @throws RIFF::Exception if an IO error occured
581     * @see Resize()
582     */
583     unsigned long Chunk::WriteUint32(uint32_t* pData, unsigned long WordCount) {
584     return Write(pData, WordCount, 4);
585     }
586    
587     /**
588 schoenebeck 2 * Reads one 8 Bit signed integer word and increments the position within
589     * the chunk.
590     *
591     * @returns read integer word
592     * @throws RIFF::Exception if an error occured
593     */
594     int8_t Chunk::ReadInt8() {
595     #if DEBUG
596     std::cout << "Chunk::ReadInt8()" << std::endl;
597     #endif // DEBUG
598     int8_t word;
599     ReadSceptical(&word,1,1);
600     return word;
601     }
602    
603     /**
604     * Reads one 8 Bit unsigned integer word and increments the position
605     * within the chunk.
606     *
607     * @returns read integer word
608     * @throws RIFF::Exception if an error occured
609     */
610     uint8_t Chunk::ReadUint8() {
611     #if DEBUG
612     std::cout << "Chunk::ReadUint8()" << std::endl;
613     #endif // DEBUG
614     uint8_t word;
615     ReadSceptical(&word,1,1);
616     return word;
617     }
618    
619     /**
620     * Reads one 16 Bit signed integer word and increments the position
621     * within the chunk. Endian correction will automatically be done if
622     * needed.
623     *
624     * @returns read integer word
625     * @throws RIFF::Exception if an error occured
626     */
627     int16_t Chunk::ReadInt16() {
628     #if DEBUG
629     std::cout << "Chunk::ReadInt16()" << std::endl;
630     #endif // DEBUG
631     int16_t word;
632     ReadSceptical(&word,1,2);
633     return word;
634     }
635    
636     /**
637     * Reads one 16 Bit unsigned integer word and increments the position
638     * within the chunk. Endian correction will automatically be done if
639     * needed.
640     *
641     * @returns read integer word
642     * @throws RIFF::Exception if an error occured
643     */
644     uint16_t Chunk::ReadUint16() {
645     #if DEBUG
646     std::cout << "Chunk::ReadUint16()" << std::endl;
647     #endif // DEBUG
648     uint16_t word;
649     ReadSceptical(&word,1,2);
650     return word;
651     }
652    
653     /**
654     * Reads one 32 Bit signed integer word and increments the position
655     * within the chunk. Endian correction will automatically be done if
656     * needed.
657     *
658     * @returns read integer word
659     * @throws RIFF::Exception if an error occured
660     */
661     int32_t Chunk::ReadInt32() {
662     #if DEBUG
663     std::cout << "Chunk::ReadInt32()" << std::endl;
664     #endif // DEBUG
665     int32_t word;
666     ReadSceptical(&word,1,4);
667     return word;
668     }
669    
670     /**
671     * Reads one 32 Bit unsigned integer word and increments the position
672     * within the chunk. Endian correction will automatically be done if
673     * needed.
674     *
675     * @returns read integer word
676     * @throws RIFF::Exception if an error occured
677     */
678     uint32_t Chunk::ReadUint32() {
679     #if DEBUG
680     std::cout << "Chunk::ReadUint32()" << std::endl;
681     #endif // DEBUG
682     uint32_t word;
683     ReadSceptical(&word,1,4);
684     return word;
685     }
686    
687 schoenebeck 780 /** @brief Load chunk body into RAM.
688     *
689     * Loads the whole chunk body into memory. You can modify the data in
690     * RAM and save the data by calling File::Save() afterwards.
691     *
692     * <b>Caution:</b> the buffer pointer will be invalidated once
693     * File::Save() was called. You have to call LoadChunkData() again to
694 schoenebeck 800 * get a new, valid pointer whenever File::Save() was called.
695 schoenebeck 780 *
696 schoenebeck 800 * You can call LoadChunkData() again if you previously scheduled to
697     * enlarge this chunk with a Resize() call. In that case the buffer will
698     * be enlarged to the new, scheduled chunk size and you can already
699     * place the new chunk data to the buffer and finally call File::Save()
700     * to enlarge the chunk physically and write the new data in one rush.
701     * This approach is definitely recommended if you have to enlarge and
702     * write new data to a lot of chunks.
703     *
704 schoenebeck 780 * @returns a pointer to the data in RAM on success, NULL otherwise
705 schoenebeck 800 * @throws Exception if data buffer could not be enlarged
706 schoenebeck 780 * @see ReleaseChunkData()
707     */
708 schoenebeck 2 void* Chunk::LoadChunkData() {
709 schoenebeck 802 if (!pChunkData && pFile->Filename != "") {
710 schoenebeck 2 #if POSIX
711 schoenebeck 780 if (lseek(pFile->hFileRead, ulStartPos, SEEK_SET) == -1) return NULL;
712 schoenebeck 1050 #elif defined(WIN32)
713     if (SetFilePointer(pFile->hFileRead, ulStartPos, NULL/*32 bit*/, FILE_BEGIN) == INVALID_SET_FILE_POINTER) return NULL;
714 schoenebeck 800 #else
715     if (fseek(pFile->hFileRead, ulStartPos, SEEK_SET)) return NULL;
716     #endif // POSIX
717     unsigned long ulBufferSize = (CurrentChunkSize > NewChunkSize) ? CurrentChunkSize : NewChunkSize;
718     pChunkData = new uint8_t[ulBufferSize];
719 schoenebeck 2 if (!pChunkData) return NULL;
720 schoenebeck 800 memset(pChunkData, 0, ulBufferSize);
721     #if POSIX
722 schoenebeck 780 unsigned long readWords = read(pFile->hFileRead, pChunkData, GetSize());
723 schoenebeck 1050 #elif defined(WIN32)
724     DWORD readWords;
725     ReadFile(pFile->hFileRead, pChunkData, GetSize(), &readWords, NULL);
726 schoenebeck 2 #else
727 schoenebeck 780 unsigned long readWords = fread(pChunkData, 1, GetSize(), pFile->hFileRead);
728 schoenebeck 2 #endif // POSIX
729     if (readWords != GetSize()) {
730     delete[] pChunkData;
731     return (pChunkData = NULL);
732     }
733 schoenebeck 800 ulChunkDataSize = ulBufferSize;
734     } else if (NewChunkSize > ulChunkDataSize) {
735     uint8_t* pNewBuffer = new uint8_t[NewChunkSize];
736     if (!pNewBuffer) throw Exception("Could not enlarge chunk data buffer to " + ToString(NewChunkSize) + " bytes");
737     memset(pNewBuffer, 0 , NewChunkSize);
738     memcpy(pNewBuffer, pChunkData, ulChunkDataSize);
739     delete[] pChunkData;
740     pChunkData = pNewBuffer;
741     ulChunkDataSize = NewChunkSize;
742 schoenebeck 2 }
743     return pChunkData;
744     }
745    
746 schoenebeck 780 /** @brief Free loaded chunk body from RAM.
747     *
748     * Frees loaded chunk body data from memory (RAM). You should call
749     * File::Save() before calling this method if you modified the data to
750     * make the changes persistent.
751     */
752 schoenebeck 2 void Chunk::ReleaseChunkData() {
753     if (pChunkData) {
754     delete[] pChunkData;
755     pChunkData = NULL;
756     }
757     }
758    
759 schoenebeck 780 /** @brief Resize chunk.
760     *
761     * Resizes this chunk's body, that is the actual size of data possible
762     * to be written to this chunk. This call will return immediately and
763     * just schedule the resize operation. You should call File::Save() to
764     * actually perform the resize operation(s) "physically" to the file.
765     * As this can take a while on large files, it is recommended to call
766     * Resize() first on all chunks which have to be resized and finally to
767     * call File::Save() to perform all those resize operations in one rush.
768     *
769     * <b>Caution:</b> You cannot directly write to enlarged chunks before
770     * calling File::Save() as this might exceed the current chunk's body
771 schoenebeck 800 * boundary!
772 schoenebeck 780 *
773     * @param iNewSize - new chunk body size in bytes (must be greater than zero)
774     * @throws RIFF::Exception if \a iNewSize is less than 1
775     * @see File::Save()
776     */
777     void Chunk::Resize(int iNewSize) {
778     if (iNewSize <= 0) throw Exception("Chunk size must be at least one byte");
779 schoenebeck 800 if (NewChunkSize == iNewSize) return;
780 schoenebeck 780 NewChunkSize = iNewSize;
781     pFile->LogAsResized(this);
782     }
783 schoenebeck 2
784 schoenebeck 780 /** @brief Write chunk persistently e.g. to disk.
785     *
786     * Stores the chunk persistently to its actual "physical" file.
787     *
788     * @param ulWritePos - position within the "physical" file where this
789     * chunk should be written to
790     * @param ulCurrentDataOffset - offset of current (old) data within
791     * the file
792     * @returns new write position in the "physical" file, that is
793     * \a ulWritePos incremented by this chunk's new size
794     * (including its header size of course)
795     */
796     unsigned long Chunk::WriteChunk(unsigned long ulWritePos, unsigned long ulCurrentDataOffset) {
797 schoenebeck 798 const unsigned long ulOriginalPos = ulWritePos;
798 schoenebeck 780 ulWritePos += CHUNK_HEADER_SIZE;
799 schoenebeck 2
800 schoenebeck 798 if (pFile->Mode != stream_mode_read_write)
801     throw Exception("Cannot write list chunk, file has to be opened in read+write mode");
802    
803 schoenebeck 780 // if the whole chunk body was loaded into RAM
804     if (pChunkData) {
805 schoenebeck 800 // make sure chunk data buffer in RAM is at least as large as the new chunk size
806     LoadChunkData();
807 schoenebeck 780 // write chunk data from RAM persistently to the file
808     #if POSIX
809     lseek(pFile->hFileWrite, ulWritePos, SEEK_SET);
810     if (write(pFile->hFileWrite, pChunkData, NewChunkSize) != NewChunkSize) {
811     throw Exception("Writing Chunk data (from RAM) failed");
812     }
813 schoenebeck 1050 #elif defined(WIN32)
814     SetFilePointer(pFile->hFileWrite, ulWritePos, NULL/*32 bit*/, FILE_BEGIN);
815     DWORD dwBytesWritten;
816     WriteFile(pFile->hFileWrite, pChunkData, NewChunkSize, &dwBytesWritten, NULL);
817     if (dwBytesWritten != NewChunkSize) {
818     throw Exception("Writing Chunk data (from RAM) failed");
819     }
820 schoenebeck 780 #else
821     fseek(pFile->hFileWrite, ulWritePos, SEEK_SET);
822     if (fwrite(pChunkData, 1, NewChunkSize, pFile->hFileWrite) != NewChunkSize) {
823     throw Exception("Writing Chunk data (from RAM) failed");
824     }
825     #endif // POSIX
826     } else {
827     // move chunk data from the end of the file to the appropriate position
828     int8_t* pCopyBuffer = new int8_t[4096];
829     unsigned long ulToMove = (NewChunkSize < CurrentChunkSize) ? NewChunkSize : CurrentChunkSize;
830 schoenebeck 1050 #if defined(WIN32)
831     DWORD iBytesMoved = 1; // we have to pass it via pointer to the Windows API, thus the correct size must be ensured
832     #else
833 schoenebeck 780 int iBytesMoved = 1;
834 schoenebeck 1050 #endif
835 schoenebeck 780 for (unsigned long ulOffset = 0; iBytesMoved > 0; ulOffset += iBytesMoved, ulToMove -= iBytesMoved) {
836     iBytesMoved = (ulToMove < 4096) ? ulToMove : 4096;
837     #if POSIX
838     lseek(pFile->hFileRead, ulStartPos + ulCurrentDataOffset + ulOffset, SEEK_SET);
839     iBytesMoved = read(pFile->hFileRead, pCopyBuffer, iBytesMoved);
840     lseek(pFile->hFileWrite, ulWritePos + ulOffset, SEEK_SET);
841     iBytesMoved = write(pFile->hFileWrite, pCopyBuffer, iBytesMoved);
842 schoenebeck 1050 #elif defined(WIN32)
843     SetFilePointer(pFile->hFileRead, ulStartPos + ulCurrentDataOffset + ulOffset, NULL/*32 bit*/, FILE_BEGIN);
844     ReadFile(pFile->hFileRead, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
845     SetFilePointer(pFile->hFileWrite, ulWritePos + ulOffset, NULL/*32 bit*/, FILE_BEGIN);
846     WriteFile(pFile->hFileWrite, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
847 schoenebeck 780 #else
848     fseek(pFile->hFileRead, ulStartPos + ulCurrentDataOffset + ulOffset, SEEK_SET);
849     iBytesMoved = fread(pCopyBuffer, 1, iBytesMoved, pFile->hFileRead);
850     fseek(pFile->hFileWrite, ulWritePos + ulOffset, SEEK_SET);
851     iBytesMoved = fwrite(pCopyBuffer, 1, iBytesMoved, pFile->hFileWrite);
852     #endif
853     }
854     delete[] pCopyBuffer;
855     if (iBytesMoved < 0) throw Exception("Writing Chunk data (from file) failed");
856     }
857    
858     // update this chunk's header
859     CurrentChunkSize = NewChunkSize;
860     WriteHeader(ulOriginalPos);
861    
862     // update chunk's position pointers
863     ulStartPos = ulOriginalPos + CHUNK_HEADER_SIZE;
864     ulPos = 0;
865    
866     // add pad byte if needed
867     if ((ulStartPos + NewChunkSize) % 2 != 0) {
868 schoenebeck 798 const char cPadByte = 0;
869 schoenebeck 780 #if POSIX
870 schoenebeck 798 lseek(pFile->hFileWrite, ulStartPos + NewChunkSize, SEEK_SET);
871 schoenebeck 780 write(pFile->hFileWrite, &cPadByte, 1);
872 schoenebeck 1050 #elif defined(WIN32)
873     SetFilePointer(pFile->hFileWrite, ulStartPos + NewChunkSize, NULL/*32 bit*/, FILE_BEGIN);
874     DWORD dwBytesWritten;
875     WriteFile(pFile->hFileWrite, &cPadByte, 1, &dwBytesWritten, NULL);
876 schoenebeck 780 #else
877 schoenebeck 798 fseek(pFile->hFileWrite, ulStartPos + NewChunkSize, SEEK_SET);
878 schoenebeck 780 fwrite(&cPadByte, 1, 1, pFile->hFileWrite);
879     #endif
880     return ulStartPos + NewChunkSize + 1;
881     }
882    
883     return ulStartPos + NewChunkSize;
884     }
885    
886     void Chunk::__resetPos() {
887     ulPos = 0;
888     }
889    
890    
891    
892 schoenebeck 2 // *************** List ***************
893     // *
894    
895 schoenebeck 780 List::List(File* pFile) : Chunk(pFile) {
896 schoenebeck 2 #if DEBUG
897 schoenebeck 780 std::cout << "List::List(File* pFile)" << std::endl;
898 schoenebeck 2 #endif // DEBUG
899     pSubChunks = NULL;
900     pSubChunksMap = NULL;
901     }
902    
903 schoenebeck 780 List::List(File* pFile, unsigned long StartPos, List* Parent)
904     : Chunk(pFile, StartPos, Parent) {
905 schoenebeck 2 #if DEBUG
906 schoenebeck 780 std::cout << "List::List(File*,ulong,bool,List*)" << std::endl;
907 schoenebeck 2 #endif // DEBUG
908     pSubChunks = NULL;
909     pSubChunksMap = NULL;
910     ReadHeader(StartPos);
911     ulStartPos = StartPos + LIST_HEADER_SIZE;
912     }
913    
914 schoenebeck 780 List::List(File* pFile, List* pParent, uint32_t uiListID)
915     : Chunk(pFile, pParent, CHUNK_ID_LIST, 0) {
916     pSubChunks = NULL;
917     pSubChunksMap = NULL;
918     ListType = uiListID;
919     }
920    
921 schoenebeck 2 List::~List() {
922     #if DEBUG
923     std::cout << "List::~List()" << std::endl;
924     #endif // DEBUG
925     if (pSubChunks) {
926     ChunkList::iterator iter = pSubChunks->begin();
927     ChunkList::iterator end = pSubChunks->end();
928     while (iter != end) {
929     delete *iter;
930     iter++;
931     }
932     delete pSubChunks;
933     }
934     if (pSubChunksMap) delete pSubChunksMap;
935     }
936    
937 schoenebeck 11 /**
938     * Returns subchunk with chunk ID <i>\a ChunkID</i> within this chunk
939     * list. Use this method if you expect only one subchunk of that type in
940     * the list. It there are more than one, it's undetermined which one of
941     * them will be returned! If there are no subchunks with that desired
942     * chunk ID, NULL will be returned.
943     *
944     * @param ChunkID - chunk ID of the sought subchunk
945     * @returns pointer to the subchunk or NULL if there is none of
946     * that ID
947     */
948 schoenebeck 2 Chunk* List::GetSubChunk(uint32_t ChunkID) {
949     #if DEBUG
950     std::cout << "List::GetSubChunk(uint32_t)" << std::endl;
951     #endif // DEBUG
952     if (!pSubChunksMap) LoadSubChunks();
953     return (*pSubChunksMap)[ChunkID];
954     }
955    
956 schoenebeck 11 /**
957     * Returns sublist chunk with list type <i>\a ListType</i> within this
958     * chunk list. Use this method if you expect only one sublist chunk of
959     * that type in the list. It there are more than one, it's undetermined
960     * which one of them will be returned! If there are no sublists with
961     * that desired list type, NULL will be returned.
962     *
963     * @param ListType - list type of the sought sublist
964     * @returns pointer to the sublist or NULL if there is none of
965     * that type
966     */
967 schoenebeck 2 List* List::GetSubList(uint32_t ListType) {
968     #if DEBUG
969     std::cout << "List::GetSubList(uint32_t)" << std::endl;
970     #endif // DEBUG
971     if (!pSubChunks) LoadSubChunks();
972     ChunkList::iterator iter = pSubChunks->begin();
973     ChunkList::iterator end = pSubChunks->end();
974     while (iter != end) {
975     if ((*iter)->GetChunkID() == CHUNK_ID_LIST) {
976     List* l = (List*) *iter;
977     if (l->GetListType() == ListType) return l;
978     }
979     iter++;
980     }
981     return NULL;
982     }
983    
984 schoenebeck 11 /**
985     * Returns the first subchunk within the list. You have to call this
986     * method before you can call GetNextSubChunk(). Recall it when you want
987     * to start from the beginning of the list again.
988     *
989     * @returns pointer to the first subchunk within the list, NULL
990     * otherwise
991     */
992 schoenebeck 2 Chunk* List::GetFirstSubChunk() {
993     #if DEBUG
994     std::cout << "List::GetFirstSubChunk()" << std::endl;
995     #endif // DEBUG
996     if (!pSubChunks) LoadSubChunks();
997     ChunksIterator = pSubChunks->begin();
998     return (ChunksIterator != pSubChunks->end()) ? *ChunksIterator : NULL;
999     }
1000    
1001 schoenebeck 11 /**
1002     * Returns the next subchunk within the list. You have to call
1003     * GetFirstSubChunk() before you can use this method!
1004     *
1005     * @returns pointer to the next subchunk within the list or NULL if
1006     * end of list is reached
1007     */
1008 schoenebeck 2 Chunk* List::GetNextSubChunk() {
1009     #if DEBUG
1010     std::cout << "List::GetNextSubChunk()" << std::endl;
1011     #endif // DEBUG
1012     if (!pSubChunks) return NULL;
1013     ChunksIterator++;
1014     return (ChunksIterator != pSubChunks->end()) ? *ChunksIterator : NULL;
1015     }
1016    
1017 schoenebeck 11 /**
1018     * Returns the first sublist within the list (that is a subchunk with
1019     * chunk ID "LIST"). You have to call this method before you can call
1020     * GetNextSubList(). Recall it when you want to start from the beginning
1021     * of the list again.
1022     *
1023     * @returns pointer to the first sublist within the list, NULL
1024     * otherwise
1025     */
1026 schoenebeck 2 List* List::GetFirstSubList() {
1027     #if DEBUG
1028     std::cout << "List::GetFirstSubList()" << std::endl;
1029     #endif // DEBUG
1030     if (!pSubChunks) LoadSubChunks();
1031     ListIterator = pSubChunks->begin();
1032     ChunkList::iterator end = pSubChunks->end();
1033     while (ListIterator != end) {
1034     if ((*ListIterator)->GetChunkID() == CHUNK_ID_LIST) return (List*) *ListIterator;
1035     ListIterator++;
1036     }
1037     return NULL;
1038     }
1039    
1040 schoenebeck 11 /**
1041     * Returns the next sublist (that is a subchunk with chunk ID "LIST")
1042     * within the list. You have to call GetFirstSubList() before you can
1043     * use this method!
1044     *
1045     * @returns pointer to the next sublist within the list, NULL if
1046     * end of list is reached
1047     */
1048 schoenebeck 2 List* List::GetNextSubList() {
1049     #if DEBUG
1050     std::cout << "List::GetNextSubList()" << std::endl;
1051     #endif // DEBUG
1052     if (!pSubChunks) return NULL;
1053     if (ListIterator == pSubChunks->end()) return NULL;
1054     ListIterator++;
1055     ChunkList::iterator end = pSubChunks->end();
1056     while (ListIterator != end) {
1057     if ((*ListIterator)->GetChunkID() == CHUNK_ID_LIST) return (List*) *ListIterator;
1058     ListIterator++;
1059     }
1060     return NULL;
1061     }
1062    
1063 schoenebeck 11 /**
1064 schoenebeck 515 * Returns number of subchunks within the list.
1065 schoenebeck 11 */
1066 schoenebeck 2 unsigned int List::CountSubChunks() {
1067     if (!pSubChunks) LoadSubChunks();
1068     return pSubChunks->size();
1069     }
1070    
1071 schoenebeck 11 /**
1072     * Returns number of subchunks within the list with chunk ID
1073     * <i>\a ChunkId</i>.
1074     */
1075 schoenebeck 2 unsigned int List::CountSubChunks(uint32_t ChunkID) {
1076     unsigned int result = 0;
1077     if (!pSubChunks) LoadSubChunks();
1078     ChunkList::iterator iter = pSubChunks->begin();
1079     ChunkList::iterator end = pSubChunks->end();
1080     while (iter != end) {
1081     if ((*iter)->GetChunkID() == ChunkID) {
1082     result++;
1083     }
1084     iter++;
1085     }
1086     return result;
1087     }
1088    
1089 schoenebeck 11 /**
1090     * Returns number of sublists within the list.
1091     */
1092 schoenebeck 2 unsigned int List::CountSubLists() {
1093     return CountSubChunks(CHUNK_ID_LIST);
1094     }
1095    
1096 schoenebeck 11 /**
1097     * Returns number of sublists within the list with list type
1098     * <i>\a ListType</i>
1099     */
1100 schoenebeck 2 unsigned int List::CountSubLists(uint32_t ListType) {
1101     unsigned int result = 0;
1102     if (!pSubChunks) LoadSubChunks();
1103     ChunkList::iterator iter = pSubChunks->begin();
1104     ChunkList::iterator end = pSubChunks->end();
1105     while (iter != end) {
1106     if ((*iter)->GetChunkID() == CHUNK_ID_LIST) {
1107     List* l = (List*) *iter;
1108     if (l->GetListType() == ListType) result++;
1109     }
1110     iter++;
1111     }
1112     return result;
1113     }
1114    
1115 schoenebeck 780 /** @brief Creates a new sub chunk.
1116     *
1117     * Creates and adds a new sub chunk to this list chunk. Note that the
1118     * chunk's body size given by \a uiBodySize must be greater than zero.
1119     * You have to call File::Save() to make this change persistent to the
1120     * actual file and <b>before</b> performing any data write operations
1121     * on the new chunk!
1122     *
1123     * @param uiChunkID - chunk ID of the new chunk
1124     * @param uiBodySize - size of the new chunk's body, that is its actual
1125     * data size (without header)
1126     * @throws RIFF::Exception if \a uiBodySize equals zero
1127     */
1128     Chunk* List::AddSubChunk(uint32_t uiChunkID, uint uiBodySize) {
1129     if (uiBodySize == 0) throw Exception("Chunk body size must be at least 1 byte");
1130     if (!pSubChunks) LoadSubChunks();
1131 schoenebeck 798 Chunk* pNewChunk = new Chunk(pFile, this, uiChunkID, 0);
1132 schoenebeck 780 pSubChunks->push_back(pNewChunk);
1133     (*pSubChunksMap)[uiChunkID] = pNewChunk;
1134 schoenebeck 798 pNewChunk->Resize(uiBodySize);
1135 schoenebeck 780 return pNewChunk;
1136     }
1137    
1138 persson 1102 /** @brief Moves a sub chunk.
1139     *
1140     * Moves a sub chunk from one position in a list to another
1141     * position in the same list. The pSrc chunk is placed before the
1142     * pDst chunk.
1143     *
1144     * @param pSrc - sub chunk to be moved
1145     * @param pDst - the position to move to. pSrc will be placed
1146     * before pDst. If pDst is 0, pSrc will be placed
1147     * last in list.
1148     */
1149     void List::MoveSubChunk(Chunk* pSrc, Chunk* pDst) {
1150     if (!pSubChunks) LoadSubChunks();
1151     pSubChunks->remove(pSrc);
1152     ChunkList::iterator iter = find(pSubChunks->begin(), pSubChunks->end(), pDst);
1153     pSubChunks->insert(iter, pSrc);
1154     }
1155    
1156 schoenebeck 780 /** @brief Creates a new list sub chunk.
1157     *
1158     * Creates and adds a new list sub chunk to this list chunk. Note that
1159     * you have to add sub chunks / sub list chunks to the new created chunk
1160     * <b>before</b> trying to make this change persisten to the actual
1161     * file with File::Save()!
1162     *
1163     * @param uiListType - list ID of the new list chunk
1164     */
1165     List* List::AddSubList(uint32_t uiListType) {
1166     if (!pSubChunks) LoadSubChunks();
1167     List* pNewListChunk = new List(pFile, this, uiListType);
1168     pSubChunks->push_back(pNewListChunk);
1169     (*pSubChunksMap)[CHUNK_ID_LIST] = pNewListChunk;
1170     return pNewListChunk;
1171     }
1172    
1173     /** @brief Removes a sub chunk.
1174     *
1175     * Removes the sub chunk given by \a pSubChunk from this list and frees
1176     * it completely from RAM. The given chunk can either be a normal sub
1177 schoenebeck 1093 * chunk or a list sub chunk. In case the given chunk is a list chunk,
1178     * all its subchunks (if any) will be removed recursively as well. You
1179     * should call File::Save() to make this change persistent at any time.
1180 schoenebeck 780 *
1181     * @param pSubChunk - sub chunk or sub list chunk to be removed
1182     */
1183     void List::DeleteSubChunk(Chunk* pSubChunk) {
1184     if (!pSubChunks) LoadSubChunks();
1185     pSubChunks->remove(pSubChunk);
1186     if ((*pSubChunksMap)[pSubChunk->GetChunkID()] == pSubChunk) {
1187     pSubChunksMap->erase(pSubChunk->GetChunkID());
1188     // try to find another chunk of the same chunk ID
1189     ChunkList::iterator iter = pSubChunks->begin();
1190     ChunkList::iterator end = pSubChunks->end();
1191     for (; iter != end; ++iter) {
1192     if ((*iter)->GetChunkID() == pSubChunk->GetChunkID()) {
1193     (*pSubChunksMap)[pSubChunk->GetChunkID()] = *iter;
1194     break; // we're done, stop search
1195     }
1196     }
1197     }
1198     delete pSubChunk;
1199     }
1200    
1201 schoenebeck 2 void List::ReadHeader(unsigned long fPos) {
1202     #if DEBUG
1203     std::cout << "List::Readheader(ulong) ";
1204     #endif // DEBUG
1205     Chunk::ReadHeader(fPos);
1206 schoenebeck 780 NewChunkSize = CurrentChunkSize -= 4;
1207 schoenebeck 2 #if POSIX
1208 schoenebeck 780 lseek(pFile->hFileRead, fPos + CHUNK_HEADER_SIZE, SEEK_SET);
1209     read(pFile->hFileRead, &ListType, 4);
1210 schoenebeck 1050 #elif defined(WIN32)
1211     SetFilePointer(pFile->hFileRead, fPos + CHUNK_HEADER_SIZE, NULL/*32 bit*/, FILE_BEGIN);
1212     DWORD dwBytesRead;
1213     ReadFile(pFile->hFileRead, &ListType, 4, &dwBytesRead, NULL);
1214 schoenebeck 2 #else
1215 schoenebeck 780 fseek(pFile->hFileRead, fPos + CHUNK_HEADER_SIZE, SEEK_SET);
1216     fread(&ListType, 4, 1, pFile->hFileRead);
1217 schoenebeck 2 #endif // POSIX
1218     #if DEBUG
1219     std::cout << "listType=" << convertToString(ListType) << std::endl;
1220     #endif // DEBUG
1221 schoenebeck 780 if (!pFile->bEndianNative) {
1222 schoenebeck 11 //swapBytes_32(&ListType);
1223 schoenebeck 2 }
1224     }
1225    
1226 schoenebeck 780 void List::WriteHeader(unsigned long fPos) {
1227     // the four list type bytes officially belong the chunk's body in the RIFF format
1228     NewChunkSize += 4;
1229     Chunk::WriteHeader(fPos);
1230     NewChunkSize -= 4; // just revert the +4 incrementation
1231     #if POSIX
1232     lseek(pFile->hFileWrite, fPos + CHUNK_HEADER_SIZE, SEEK_SET);
1233     write(pFile->hFileWrite, &ListType, 4);
1234 schoenebeck 1050 #elif defined(WIN32)
1235     SetFilePointer(pFile->hFileWrite, fPos + CHUNK_HEADER_SIZE, NULL/*32 bit*/, FILE_BEGIN);
1236     DWORD dwBytesWritten;
1237     WriteFile(pFile->hFileWrite, &ListType, 4, &dwBytesWritten, NULL);
1238 schoenebeck 780 #else
1239     fseek(pFile->hFileWrite, fPos + CHUNK_HEADER_SIZE, SEEK_SET);
1240     fwrite(&ListType, 4, 1, pFile->hFileWrite);
1241     #endif // POSIX
1242     }
1243    
1244 schoenebeck 2 void List::LoadSubChunks() {
1245     #if DEBUG
1246     std::cout << "List::LoadSubChunks()";
1247     #endif // DEBUG
1248     if (!pSubChunks) {
1249     pSubChunks = new ChunkList();
1250     pSubChunksMap = new ChunkMap();
1251 schoenebeck 780 if (!pFile->hFileRead) return;
1252 schoenebeck 515 unsigned long uiOriginalPos = GetPos();
1253     SetPos(0); // jump to beginning of list chunk body
1254 schoenebeck 2 while (RemainingBytes() >= CHUNK_HEADER_SIZE) {
1255     Chunk* ck;
1256 schoenebeck 11 uint32_t ckid;
1257     Read(&ckid, 4, 1);
1258 schoenebeck 2 #if DEBUG
1259     std::cout << " ckid=" << convertToString(ckid) << std::endl;
1260     #endif // DEBUG
1261     if (ckid == CHUNK_ID_LIST) {
1262 schoenebeck 780 ck = new RIFF::List(pFile, ulStartPos + ulPos - 4, this);
1263 schoenebeck 2 SetPos(ck->GetSize() + LIST_HEADER_SIZE - 4, RIFF::stream_curpos);
1264     }
1265     else { // simple chunk
1266 schoenebeck 780 ck = new RIFF::Chunk(pFile, ulStartPos + ulPos - 4, this);
1267 schoenebeck 2 SetPos(ck->GetSize() + CHUNK_HEADER_SIZE - 4, RIFF::stream_curpos);
1268     }
1269     pSubChunks->push_back(ck);
1270     (*pSubChunksMap)[ckid] = ck;
1271     if (GetPos() % 2 != 0) SetPos(1, RIFF::stream_curpos); // jump over pad byte
1272     }
1273 schoenebeck 515 SetPos(uiOriginalPos); // restore position before this call
1274 schoenebeck 2 }
1275     }
1276    
1277 schoenebeck 833 void List::LoadSubChunksRecursively() {
1278     for (List* pList = GetFirstSubList(); pList; pList = GetNextSubList())
1279     pList->LoadSubChunksRecursively();
1280     }
1281    
1282 schoenebeck 780 /** @brief Write list chunk persistently e.g. to disk.
1283     *
1284     * Stores the list chunk persistently to its actual "physical" file. All
1285     * subchunks (including sub list chunks) will be stored recursively as
1286     * well.
1287     *
1288     * @param ulWritePos - position within the "physical" file where this
1289     * list chunk should be written to
1290     * @param ulCurrentDataOffset - offset of current (old) data within
1291     * the file
1292     * @returns new write position in the "physical" file, that is
1293     * \a ulWritePos incremented by this list chunk's new size
1294     * (including its header size of course)
1295     */
1296     unsigned long List::WriteChunk(unsigned long ulWritePos, unsigned long ulCurrentDataOffset) {
1297 schoenebeck 798 const unsigned long ulOriginalPos = ulWritePos;
1298 schoenebeck 780 ulWritePos += LIST_HEADER_SIZE;
1299    
1300 schoenebeck 798 if (pFile->Mode != stream_mode_read_write)
1301     throw Exception("Cannot write list chunk, file has to be opened in read+write mode");
1302    
1303 schoenebeck 780 // write all subchunks (including sub list chunks) recursively
1304     if (pSubChunks) {
1305     for (ChunkList::iterator iter = pSubChunks->begin(), end = pSubChunks->end(); iter != end; ++iter) {
1306     ulWritePos = (*iter)->WriteChunk(ulWritePos, ulCurrentDataOffset);
1307     }
1308     }
1309    
1310     // update this list chunk's header
1311     CurrentChunkSize = NewChunkSize = ulWritePos - ulOriginalPos - LIST_HEADER_SIZE;
1312     WriteHeader(ulOriginalPos);
1313    
1314 schoenebeck 798 // offset of this list chunk in new written file may have changed
1315     ulStartPos = ulOriginalPos + LIST_HEADER_SIZE;
1316    
1317 schoenebeck 780 return ulWritePos;
1318     }
1319    
1320     void List::__resetPos() {
1321     Chunk::__resetPos();
1322     if (pSubChunks) {
1323     for (ChunkList::iterator iter = pSubChunks->begin(), end = pSubChunks->end(); iter != end; ++iter) {
1324     (*iter)->__resetPos();
1325     }
1326     }
1327     }
1328    
1329 schoenebeck 11 /**
1330     * Returns string representation of the lists's id
1331     */
1332 schoenebeck 2 String List::GetListTypeString() {
1333     return convertToString(ListType);
1334     }
1335    
1336    
1337    
1338     // *************** File ***************
1339     // *
1340    
1341 schoenebeck 780 /** @brief Create new RIFF file.
1342     *
1343     * Use this constructor if you want to create a new RIFF file completely
1344     * "from scratch". Note: there must be no empty chunks or empty list
1345     * chunks when trying to make the new RIFF file persistent with Save()!
1346     *
1347 schoenebeck 798 * @param FileType - four-byte identifier of the RIFF file type
1348 schoenebeck 780 * @see AddSubChunk(), AddSubList()
1349     */
1350 schoenebeck 798 File::File(uint32_t FileType) : List(this) {
1351 schoenebeck 1050 #if defined(WIN32)
1352     hFileRead = hFileWrite = INVALID_HANDLE_VALUE;
1353     #else
1354 schoenebeck 780 hFileRead = hFileWrite = 0;
1355 schoenebeck 1050 #endif
1356 schoenebeck 798 Mode = stream_mode_closed;
1357 schoenebeck 780 bEndianNative = true;
1358     ulStartPos = RIFF_HEADER_SIZE;
1359 schoenebeck 798 ListType = FileType;
1360 schoenebeck 780 }
1361    
1362     /** @brief Load existing RIFF file.
1363     *
1364     * Loads an existing RIFF file with all its chunks.
1365     *
1366     * @param path - path and file name of the RIFF file to open
1367     * @throws RIFF::Exception if error occured while trying to load the
1368     * given RIFF file
1369     */
1370     File::File(const String& path) : List(this), Filename(path) {
1371 schoenebeck 2 #if DEBUG
1372     std::cout << "File::File("<<path<<")" << std::endl;
1373     #endif // DEBUG
1374     bEndianNative = true;
1375     #if POSIX
1376 schoenebeck 780 hFileRead = hFileWrite = open(path.c_str(), O_RDONLY | O_NONBLOCK);
1377     if (hFileRead <= 0) {
1378     hFileRead = hFileWrite = 0;
1379 schoenebeck 2 throw RIFF::Exception("Can't open \"" + path + "\"");
1380     }
1381 schoenebeck 1050 #elif defined(WIN32)
1382     hFileRead = hFileWrite = CreateFile(
1383     path.c_str(), GENERIC_READ,
1384     FILE_SHARE_READ | FILE_SHARE_WRITE,
1385     NULL, OPEN_EXISTING,
1386     FILE_ATTRIBUTE_NORMAL, NULL
1387     );
1388     if (hFileRead == INVALID_HANDLE_VALUE) {
1389     hFileRead = hFileWrite = INVALID_HANDLE_VALUE;
1390     throw RIFF::Exception("Can't open \"" + path + "\"");
1391     }
1392 schoenebeck 2 #else
1393 schoenebeck 780 hFileRead = hFileWrite = fopen(path.c_str(), "rb");
1394 schoenebeck 1050 if (!hFileRead) throw RIFF::Exception("Can't open \"" + path + "\"");
1395 schoenebeck 2 #endif // POSIX
1396 schoenebeck 798 Mode = stream_mode_read;
1397 schoenebeck 2 ulStartPos = RIFF_HEADER_SIZE;
1398     ReadHeader(0);
1399     if (ChunkID != CHUNK_ID_RIFF) {
1400     throw RIFF::Exception("Not a RIFF file");
1401     }
1402     }
1403    
1404 schoenebeck 780 String File::GetFileName() {
1405     return Filename;
1406     }
1407    
1408     stream_mode_t File::GetMode() {
1409     return Mode;
1410     }
1411    
1412     /** @brief Change file access mode.
1413     *
1414     * Changes files access mode either to read-only mode or to read/write
1415     * mode.
1416     *
1417     * @param NewMode - new file access mode
1418     * @returns true if mode was changed, false if current mode already
1419     * equals new mode
1420     * @throws RIFF::Exception if new file access mode is unknown
1421     */
1422     bool File::SetMode(stream_mode_t NewMode) {
1423     if (NewMode != Mode) {
1424     switch (NewMode) {
1425     case stream_mode_read:
1426     #if POSIX
1427     if (hFileRead) close(hFileRead);
1428     hFileRead = hFileWrite = open(Filename.c_str(), O_RDONLY | O_NONBLOCK);
1429     if (hFileRead < 0) {
1430     hFileRead = hFileWrite = 0;
1431     throw Exception("Could not (re)open file \"" + Filename + "\" in read mode");
1432     }
1433 schoenebeck 1050 #elif defined(WIN32)
1434     if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
1435     hFileRead = hFileWrite = CreateFile(
1436     Filename.c_str(), GENERIC_READ,
1437     FILE_SHARE_READ | FILE_SHARE_WRITE,
1438     NULL, OPEN_EXISTING,
1439     FILE_ATTRIBUTE_NORMAL, NULL
1440     );
1441     if (hFileRead == INVALID_HANDLE_VALUE) {
1442     hFileRead = hFileWrite = INVALID_HANDLE_VALUE;
1443     throw Exception("Could not (re)open file \"" + Filename + "\" in read mode");
1444     }
1445 schoenebeck 780 #else
1446     if (hFileRead) fclose(hFileRead);
1447 schoenebeck 1050 hFileRead = hFileWrite = fopen(Filename.c_str(), "rb");
1448 schoenebeck 780 if (!hFileRead) throw Exception("Could not (re)open file \"" + Filename + "\" in read mode");
1449     #endif
1450     __resetPos(); // reset read/write position of ALL 'Chunk' objects
1451 schoenebeck 798 break;
1452 schoenebeck 780 case stream_mode_read_write:
1453     #if POSIX
1454     if (hFileRead) close(hFileRead);
1455     hFileRead = hFileWrite = open(Filename.c_str(), O_RDWR | O_NONBLOCK);
1456     if (hFileRead < 0) {
1457     hFileRead = hFileWrite = open(Filename.c_str(), O_RDONLY | O_NONBLOCK);
1458     throw Exception("Could not open file \"" + Filename + "\" in read+write mode");
1459     }
1460 schoenebeck 1050 #elif defined(WIN32)
1461     if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
1462     hFileRead = hFileWrite = CreateFile(
1463     Filename.c_str(),
1464     GENERIC_READ | GENERIC_WRITE,
1465     FILE_SHARE_READ,
1466     NULL, OPEN_ALWAYS,
1467     FILE_ATTRIBUTE_NORMAL, NULL
1468     );
1469     if (hFileRead == INVALID_HANDLE_VALUE) {
1470     hFileRead = hFileWrite = CreateFile(
1471     Filename.c_str(), GENERIC_READ,
1472     FILE_SHARE_READ | FILE_SHARE_WRITE,
1473     NULL, OPEN_EXISTING,
1474     FILE_ATTRIBUTE_NORMAL, NULL
1475     );
1476     throw Exception("Could not (re)open file \"" + Filename + "\" in read mode");
1477     }
1478 schoenebeck 780 #else
1479     if (hFileRead) fclose(hFileRead);
1480 schoenebeck 1050 hFileRead = hFileWrite = fopen(Filename.c_str(), "r+b");
1481 schoenebeck 780 if (!hFileRead) {
1482 schoenebeck 1050 hFileRead = hFileWrite = fopen(Filename.c_str(), "rb");
1483 schoenebeck 780 throw Exception("Could not open file \"" + Filename + "\" in read+write mode");
1484     }
1485     #endif
1486     __resetPos(); // reset read/write position of ALL 'Chunk' objects
1487 schoenebeck 798 break;
1488     case stream_mode_closed:
1489     #if POSIX
1490     if (hFileRead) close(hFileRead);
1491     if (hFileWrite) close(hFileWrite);
1492 schoenebeck 1050 #elif defined(WIN32)
1493     if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
1494     if (hFileWrite != INVALID_HANDLE_VALUE) CloseHandle(hFileWrite);
1495 schoenebeck 798 #else
1496     if (hFileRead) fclose(hFileRead);
1497     if (hFileWrite) fclose(hFileWrite);
1498     #endif
1499     hFileRead = hFileWrite = 0;
1500     break;
1501 schoenebeck 780 default:
1502     throw Exception("Unknown file access mode");
1503     }
1504 schoenebeck 798 Mode = NewMode;
1505     return true;
1506 schoenebeck 780 }
1507     return false;
1508     }
1509    
1510     /** @brief Save changes to same file.
1511     *
1512     * Make all changes of all chunks persistent by writing them to the
1513     * actual (same) file. The file might temporarily grow to a higher size
1514     * than it will have at the end of the saving process, in case chunks
1515     * were grown.
1516     *
1517     * @throws RIFF::Exception if there is an empty chunk or empty list
1518     * chunk or any kind of IO error occured
1519     */
1520     void File::Save() {
1521 schoenebeck 833 // make sure the RIFF tree is built (from the original file)
1522     LoadSubChunksRecursively();
1523    
1524 schoenebeck 780 // reopen file in write mode
1525 schoenebeck 798 SetMode(stream_mode_read_write);
1526 schoenebeck 780
1527     // to be able to save the whole file without loading everything into
1528     // RAM and without having to store the data in a temporary file, we
1529     // enlarge the file with the sum of all _positive_ chunk size
1530     // changes, move current data towards the end of the file with the
1531     // calculated sum and finally update / rewrite the file by copying
1532     // the old data back to the right position at the beginning of the file
1533    
1534     // first we sum up all positive chunk size changes (and skip all negative ones)
1535     unsigned long ulPositiveSizeDiff = 0;
1536     for (ChunkList::iterator iter = ResizedChunks.begin(), end = ResizedChunks.end(); iter != end; ++iter) {
1537 schoenebeck 1095 if ((*iter)->GetNewSize() == 0) {
1538     // just to make the exception message a bit more verbose: resolve the chunk's path
1539     String sChunkPath;
1540     for (Chunk* pChunk = *iter; pChunk; pChunk = pChunk->GetParent()) {
1541     if (pChunk->GetChunkID() == CHUNK_ID_LIST) {
1542     List* pList = (List*) pChunk;
1543     sChunkPath = "->'" + pList->GetListTypeString() + "'" + sChunkPath;
1544     } else {
1545     sChunkPath = "->'" + pChunk->GetChunkIDString() + "'" + sChunkPath;
1546     }
1547     }
1548     throw Exception("There is at least one empty chunk (zero size): " + sChunkPath);
1549     }
1550 schoenebeck 798 if ((*iter)->GetNewSize() + 1L > (*iter)->GetSize()) {
1551     unsigned long ulDiff = (*iter)->GetNewSize() - (*iter)->GetSize() + 1L; // +1 in case we have to add a pad byte
1552     ulPositiveSizeDiff += ulDiff;
1553     }
1554 schoenebeck 780 }
1555    
1556     unsigned long ulWorkingFileSize = GetFileSize();
1557    
1558     // if there are positive size changes...
1559     if (ulPositiveSizeDiff > 0) {
1560     // ... we enlarge this file first ...
1561     ulWorkingFileSize += ulPositiveSizeDiff;
1562     ResizeFile(ulWorkingFileSize);
1563     // ... and move current data by the same amount towards end of file.
1564     int8_t* pCopyBuffer = new int8_t[4096];
1565     const unsigned long ulFileSize = GetSize() + RIFF_HEADER_SIZE;
1566 schoenebeck 1050 #if defined(WIN32)
1567     DWORD iBytesMoved = 1; // we have to pass it via pointer to the Windows API, thus the correct size must be ensured
1568     #else
1569     int iBytesMoved = 1;
1570     #endif
1571 schoenebeck 780 for (unsigned long ulPos = 0; iBytesMoved > 0; ulPos += iBytesMoved) {
1572     const unsigned long ulToMove = ulFileSize - ulPos;
1573     iBytesMoved = (ulToMove < 4096) ? ulToMove : 4096;
1574     #if POSIX
1575     lseek(hFileRead, ulPos, SEEK_SET);
1576     iBytesMoved = read(hFileRead, pCopyBuffer, iBytesMoved);
1577     lseek(hFileWrite, ulPos + ulPositiveSizeDiff, SEEK_SET);
1578     iBytesMoved = write(hFileWrite, pCopyBuffer, iBytesMoved);
1579 schoenebeck 1050 #elif defined(WIN32)
1580     SetFilePointer(hFileRead, ulPos, NULL/*32 bit*/, FILE_BEGIN);
1581     ReadFile(hFileRead, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
1582     SetFilePointer(hFileWrite, ulPos + ulPositiveSizeDiff, NULL/*32 bit*/, FILE_BEGIN);
1583     WriteFile(hFileWrite, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
1584 schoenebeck 780 #else
1585     fseek(hFileRead, ulPos, SEEK_SET);
1586     iBytesMoved = fread(pCopyBuffer, 1, iBytesMoved, hFileRead);
1587     fseek(hFileWrite, ulPos + ulPositiveSizeDiff, SEEK_SET);
1588     iBytesMoved = fwrite(pCopyBuffer, 1, iBytesMoved, hFileWrite);
1589     #endif
1590     }
1591     delete[] pCopyBuffer;
1592     if (iBytesMoved < 0) throw Exception("Could not modify file while trying to enlarge it");
1593     }
1594    
1595     // rebuild / rewrite complete RIFF tree
1596 schoenebeck 798 unsigned long ulTotalSize = WriteChunk(0, ulPositiveSizeDiff);
1597     unsigned long ulActualSize = __GetFileSize(hFileWrite);
1598 schoenebeck 780
1599     // resize file to the final size
1600 schoenebeck 798 if (ulTotalSize < ulActualSize) ResizeFile(ulTotalSize);
1601 schoenebeck 780
1602     // forget all resized chunks
1603     ResizedChunks.clear();
1604     }
1605    
1606     /** @brief Save changes to another file.
1607     *
1608     * Make all changes of all chunks persistent by writing them to another
1609     * file. <b>Caution:</b> this method is optimized for writing to
1610     * <b>another</b> file, do not use it to save the changes to the same
1611 schoenebeck 798 * file! Use File::Save() in that case instead! Ignoring this might
1612     * result in a corrupted file, especially in case chunks were resized!
1613 schoenebeck 780 *
1614     * After calling this method, this File object will be associated with
1615     * the new file (given by \a path) afterwards.
1616     *
1617     * @param path - path and file name where everything should be written to
1618     */
1619     void File::Save(const String& path) {
1620 schoenebeck 798 //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
1621    
1622 schoenebeck 833 // make sure the RIFF tree is built (from the original file)
1623     LoadSubChunksRecursively();
1624    
1625 schoenebeck 798 if (Filename.length() > 0) SetMode(stream_mode_read);
1626 schoenebeck 780 // open the other (new) file for writing and truncate it to zero size
1627     #if POSIX
1628 schoenebeck 798 hFileWrite = open(path.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP);
1629 schoenebeck 780 if (hFileWrite < 0) {
1630     hFileWrite = hFileRead;
1631     throw Exception("Could not open file \"" + path + "\" for writing");
1632     }
1633 schoenebeck 1050 #elif defined(WIN32)
1634     hFileWrite = CreateFile(
1635     path.c_str(), GENERIC_WRITE, FILE_SHARE_READ,
1636     NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL
1637     );
1638     if (hFileWrite == INVALID_HANDLE_VALUE) {
1639     hFileWrite = hFileRead;
1640     throw Exception("Could not open file \"" + path + "\" for writing");
1641     }
1642 schoenebeck 780 #else
1643     hFileWrite = fopen(path.c_str(), "w+b");
1644     if (!hFileWrite) {
1645     hFileWrite = hFileRead;
1646     throw Exception("Could not open file \"" + path + "\" for writing");
1647     }
1648     #endif // POSIX
1649 schoenebeck 798 Mode = stream_mode_read_write;
1650 schoenebeck 780
1651     // write complete RIFF tree to the other (new) file
1652 schoenebeck 798 unsigned long ulTotalSize = WriteChunk(0, 0);
1653     unsigned long ulActualSize = __GetFileSize(hFileWrite);
1654 schoenebeck 780
1655 schoenebeck 798 // resize file to the final size (if the file was originally larger)
1656     if (ulTotalSize < ulActualSize) ResizeFile(ulTotalSize);
1657    
1658 schoenebeck 780 // forget all resized chunks
1659     ResizedChunks.clear();
1660    
1661 schoenebeck 798 if (Filename.length() > 0) {
1662     #if POSIX
1663     close(hFileWrite);
1664 schoenebeck 1050 #elif defined(WIN32)
1665     CloseHandle(hFileWrite);
1666 schoenebeck 798 #else
1667     fclose(hFileWrite);
1668     #endif
1669     hFileWrite = hFileRead;
1670     }
1671    
1672 schoenebeck 780 // associate new file with this File object from now on
1673     Filename = path;
1674 schoenebeck 798 Mode = (stream_mode_t) -1; // Just set it to an undefined mode ...
1675     SetMode(stream_mode_read_write); // ... so SetMode() has to reopen the file handles.
1676 schoenebeck 780 }
1677    
1678     void File::ResizeFile(unsigned long ulNewSize) {
1679     #if POSIX
1680     if (ftruncate(hFileWrite, ulNewSize) < 0)
1681     throw Exception("Could not resize file \"" + Filename + "\"");
1682 schoenebeck 1050 #elif defined(WIN32)
1683     if (
1684     SetFilePointer(hFileWrite, ulNewSize, NULL/*32 bit*/, FILE_BEGIN) == INVALID_SET_FILE_POINTER ||
1685     !SetEndOfFile(hFileWrite)
1686     ) throw Exception("Could not resize file \"" + Filename + "\"");
1687 schoenebeck 780 #else
1688 schoenebeck 1050 # error Sorry, this version of libgig only supports POSIX and Windows systems yet.
1689 schoenebeck 798 # error Reason: portable implementation of RIFF::File::ResizeFile() is missing (yet)!
1690 schoenebeck 780 #endif
1691     }
1692    
1693 schoenebeck 2 File::~File() {
1694     #if DEBUG
1695     std::cout << "File::~File()" << std::endl;
1696     #endif // DEBUG
1697     #if POSIX
1698 schoenebeck 780 if (hFileRead) close(hFileRead);
1699 schoenebeck 1050 #elif defined(WIN32)
1700     if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
1701 schoenebeck 2 #else
1702 schoenebeck 780 if (hFileRead) fclose(hFileRead);
1703 schoenebeck 2 #endif // POSIX
1704     }
1705    
1706 schoenebeck 780 void File::LogAsResized(Chunk* pResizedChunk) {
1707     ResizedChunks.push_back(pResizedChunk);
1708     }
1709    
1710 schoenebeck 1095 void File::UnlogResized(Chunk* pResizedChunk) {
1711     ResizedChunks.remove(pResizedChunk);
1712     }
1713    
1714 schoenebeck 2 unsigned long File::GetFileSize() {
1715 schoenebeck 798 return __GetFileSize(hFileRead);
1716     }
1717    
1718     #if POSIX
1719     unsigned long File::__GetFileSize(int hFile) {
1720 schoenebeck 2 struct stat filestat;
1721 schoenebeck 798 fstat(hFile, &filestat);
1722 schoenebeck 2 long size = filestat.st_size;
1723     return size;
1724     }
1725 schoenebeck 1050 #elif defined(WIN32)
1726     unsigned long File::__GetFileSize(HANDLE hFile) {
1727 schoenebeck 1063 DWORD dwSize = ::GetFileSize(hFile, NULL /*32bit*/);
1728     if (dwSize == INVALID_FILE_SIZE)
1729     throw Exception("Windows FS error: could not determine file size");
1730     return dwSize;
1731 schoenebeck 1050 }
1732 schoenebeck 798 #else // standard C functions
1733     unsigned long File::__GetFileSize(FILE* hFile) {
1734     long curpos = ftell(hFile);
1735     fseek(hFile, 0, SEEK_END);
1736     long size = ftell(hFile);
1737     fseek(hFile, curpos, SEEK_SET);
1738     return size;
1739     }
1740     #endif
1741 schoenebeck 2
1742    
1743     // *************** Exception ***************
1744     // *
1745    
1746     void Exception::PrintMessage() {
1747     std::cout << "RIFF::Exception: " << Message << std::endl;
1748     }
1749    
1750 schoenebeck 518
1751     // *************** functions ***************
1752     // *
1753    
1754     /**
1755     * Returns the name of this C++ library. This is usually "libgig" of
1756     * course. This call is equivalent to DLS::libraryName() and
1757     * gig::libraryName().
1758     */
1759     String libraryName() {
1760     return PACKAGE;
1761     }
1762    
1763     /**
1764     * Returns version of this C++ library. This call is equivalent to
1765     * DLS::libraryVersion() and gig::libraryVersion().
1766     */
1767     String libraryVersion() {
1768     return VERSION;
1769     }
1770    
1771 schoenebeck 2 } // namespace RIFF

  ViewVC Help
Powered by ViewVC