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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2685 - (hide annotations) (download)
Sat Jan 3 21:44:42 2015 UTC (9 years, 3 months ago) by schoenebeck
File size: 86938 byte(s)
* RIFF: Fixed embarrassing old bug: POSIX read() errors were never detected
  on Chunk::Read() calls due to signment incompatible variable.
* Added new command line tool "gig2stereo" (and a man page for it).
* Bumped version (v3.3.0.svn23).

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

  ViewVC Help
Powered by ViewVC