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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 780 - (hide annotations) (download)
Sun Sep 25 13:40:37 2005 UTC (14 years, 9 months ago) by schoenebeck
File size: 60073 byte(s)
* added write support (only to the RIFF classes yet)

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

  ViewVC Help
Powered by ViewVC