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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1063 - (hide annotations) (download)
Sat Mar 3 21:45:25 2007 UTC (17 years ago) by schoenebeck
File size: 71721 byte(s)
* fixed libgig's Dev-C++ project file to produce an actually working
  Windows DLL file (mandatory symbols were not exported so far)
* fixed native Windows implementation of RIFF::File::__GetFileSize() to
  work with younger versions than XP as well
* added Dev-C++ project files for the demo / example applications as well
* added instructions in README for how to compile libgig and its tools for
  Windows

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

  ViewVC Help
Powered by ViewVC