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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2675 - (hide annotations) (download)
Sun Sep 14 16:07:34 2014 UTC (9 years, 7 months ago) by schoenebeck
File size: 83293 byte(s)
* RIFF.cpp, POSIX: only assume -1 result value as error on open() calls.
* RIFF.cpp, POSIX: show operating system's error reason if opening a file
  failed.
* Bumped version (3.3.0.svn21).

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

  ViewVC Help
Powered by ViewVC