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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 833 - (hide annotations) (download)
Sun Feb 5 17:30:13 2006 UTC (18 years, 2 months ago) by schoenebeck
File size: 63348 byte(s)
* src/RIFF.cpp: bugfix in File::Save() methods which did not ensure that
  the whole RIFF tree was already built before writing which caused opening
  an existing file and saving it afterwards to write just an empty file

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

  ViewVC Help
Powered by ViewVC