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

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

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 11 by schoenebeck, Sun Nov 16 17:47:00 2003 UTC revision 798 by schoenebeck, Thu Nov 3 23:49:11 2005 UTC
# Line 2  Line 2 
2   *                                                                         *   *                                                                         *
3   *   libgig - C++ cross-platform Gigasampler format file loader library    *   *   libgig - C++ cross-platform Gigasampler format file loader library    *
4   *                                                                         *   *                                                                         *
5   *   Copyright (C) 2003 by Christian Schoenebeck                           *   *   Copyright (C) 2003-2005 by Christian Schoenebeck                      *
6   *                         <cuse@users.sourceforge.net>                    *   *                              <cuse@users.sourceforge.net>               *
7   *                                                                         *   *                                                                         *
8   *   This library is free software; you can redistribute it and/or modify  *   *   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  *   *   it under the terms of the GNU General Public License as published by  *
# Line 20  Line 20 
20   *   Foundation, Inc., 59 Temple Place, Suite 330, Boston,                 *   *   Foundation, Inc., 59 Temple Place, Suite 330, Boston,                 *
21   *   MA  02111-1307  USA                                                   *   *   MA  02111-1307  USA                                                   *
22   ***************************************************************************/   ***************************************************************************/
23  #if 1  
24    #include <string.h>
25    #include <sstream>
26    
27  #include "RIFF.h"  #include "RIFF.h"
28    
29  namespace RIFF {  namespace RIFF {
30    
31    // *************** Helper Functions **************
32    // *
33    
34        template<class T> inline String ToString(T o) {
35            std::stringstream ss;
36            ss << o;
37            return ss.str();
38        }
39    
40    
41    
42  // *************** Chunk **************  // *************** Chunk **************
43  // *  // *
44    
45      Chunk::Chunk() {      Chunk::Chunk(File* pFile) {
46          #if DEBUG          #if DEBUG
47          std::cout << "Chunk::Chunk()" << std::endl;          std::cout << "Chunk::Chunk(File* pFile)" << std::endl;
48          #endif // DEBUG          #endif // DEBUG
49          ulPos      = 0;          ulPos      = 0;
50          pParent    = NULL;          pParent    = NULL;
51          pChunkData = NULL;          pChunkData = NULL;
52            ChunkID    = CHUNK_ID_RIFF;
53            this->pFile = pFile;
54      }      }
55    
56      #if POSIX      Chunk::Chunk(File* pFile, unsigned long StartPos, List* Parent) {
     Chunk::Chunk(int hFile, unsigned long StartPos, bool EndianNative, List* Parent) {  
     #else  
     Chunk::Chunk(FILE* hFile, unsigned long StartPos, bool EndianNative, List* Parent) {  
     #endif // POSIX  
57          #if DEBUG          #if DEBUG
58          std::cout << "Chunk::Chunk(FILE,ulong,bool,List*),StartPos=" << StartPos << std::endl;          std::cout << "Chunk::Chunk(File*,ulong,bool,List*),StartPos=" << StartPos << std::endl;
59          #endif // DEBUG          #endif // DEBUG
60          Chunk::hFile  = hFile;          this->pFile   = pFile;
61          ulStartPos    = StartPos + CHUNK_HEADER_SIZE;          ulStartPos    = StartPos + CHUNK_HEADER_SIZE;
         bEndianNative = EndianNative;  
62          pParent       = Parent;          pParent       = Parent;
63          ulPos         = 0;          ulPos         = 0;
64          pChunkData    = NULL;          pChunkData    = NULL;
65          ReadHeader(StartPos);          ReadHeader(StartPos);
66      }      }
67    
68        Chunk::Chunk(File* pFile, List* pParent, uint32_t uiChunkID, uint uiBodySize) {
69            this->pFile      = pFile;
70            ulStartPos       = 0; // arbitrary usually, since it will be updated when we write the chunk
71            this->pParent    = pParent;
72            ulPos            = 0;
73            pChunkData       = NULL;
74            ChunkID          = uiChunkID;
75            CurrentChunkSize = 0;
76            NewChunkSize     = uiBodySize;
77        }
78    
79      Chunk::~Chunk() {      Chunk::~Chunk() {
80          if (pChunkData) delete[] pChunkData;          if (pChunkData) delete[] pChunkData;
81      }      }
# Line 63  namespace RIFF { Line 85  namespace RIFF {
85          std::cout << "Chunk::Readheader(" << fPos << ") ";          std::cout << "Chunk::Readheader(" << fPos << ") ";
86          #endif // DEBUG          #endif // DEBUG
87          #if POSIX          #if POSIX
88          if (lseek(hFile, fPos, SEEK_SET) != -1) {          if (lseek(pFile->hFileRead, fPos, SEEK_SET) != -1) {
89              read(hFile, &ChunkID, 4);              read(pFile->hFileRead, &ChunkID, 4);
90              read(hFile, &ChunkSize, 4);              read(pFile->hFileRead, &CurrentChunkSize, 4);
91          #else          #else
92          if (!fseek(hFile, fPos, SEEK_SET)) {          if (!fseek(pFile->hFileRead, fPos, SEEK_SET)) {
93              fread(&ChunkID, 4, 1, hFile);              fread(&ChunkID, 4, 1, pFile->hFileRead);
94              fread(&ChunkSize, 4, 1, hFile);              fread(&CurrentChunkSize, 4, 1, pFile->hFileRead);
95          #endif // POSIX          #endif // POSIX
96              #if WORDS_BIGENDIAN              #if WORDS_BIGENDIAN
97              if (ChunkID == CHUNK_ID_RIFF) {              if (ChunkID == CHUNK_ID_RIFF) {
# Line 77  namespace RIFF { Line 99  namespace RIFF {
99              }              }
100              #else // little endian              #else // little endian
101              if (ChunkID == CHUNK_ID_RIFX) {              if (ChunkID == CHUNK_ID_RIFX) {
102                  bEndianNative = false;                  pFile->bEndianNative = false;
103                  ChunkID = CHUNK_ID_RIFF;                  ChunkID = CHUNK_ID_RIFF;
104              }              }
105              #endif // WORDS_BIGENDIAN              #endif // WORDS_BIGENDIAN
106              if (!bEndianNative) {              if (!pFile->bEndianNative) {
107                  //swapBytes_32(&ChunkID);                  //swapBytes_32(&ChunkID);
108                  swapBytes_32(&ChunkSize);                  swapBytes_32(&CurrentChunkSize);
109              }              }
110              #if DEBUG              #if DEBUG
111              std::cout << "ckID=" << convertToString(ChunkID) << " ";              std::cout << "ckID=" << convertToString(ChunkID) << " ";
112              std::cout << "ckSize=" << ChunkSize << " ";              std::cout << "ckSize=" << ChunkSize << " ";
113              std::cout << "bEndianNative=" << bEndianNative << std::endl;              std::cout << "bEndianNative=" << bEndianNative << std::endl;
114              #endif // DEBUG              #endif // DEBUG
115                NewChunkSize = CurrentChunkSize;
116          }          }
117      }      }
118    
119        void Chunk::WriteHeader(unsigned long fPos) {
120            uint32_t uiNewChunkID = ChunkID;
121            if (ChunkID == CHUNK_ID_RIFF) {
122                #if WORDS_BIGENDIAN
123                if (pFile->bEndianNative) uiNewChunkID = CHUNK_ID_RIFX;
124                #else // little endian
125                if (!pFile->bEndianNative) uiNewChunkID = CHUNK_ID_RIFX;
126                #endif // WORDS_BIGENDIAN
127            }
128    
129            uint32_t uiNewChunkSize = NewChunkSize;
130            if (!pFile->bEndianNative) {
131                swapBytes_32(&uiNewChunkSize);
132            }
133    
134            #if POSIX
135            if (lseek(pFile->hFileWrite, fPos, SEEK_SET) != -1) {
136                write(pFile->hFileWrite, &uiNewChunkID, 4);
137                write(pFile->hFileWrite, &uiNewChunkSize, 4);
138            }
139            #else
140            if (!fseek(pFile->hFileWrite, fPos, SEEK_SET)) {
141                fwrite(&uiNewChunkID, 4, 1, pFile->hFileWrite);
142                fwrite(&uiNewChunkSize, 4, 1, pFile->hFileWrite);
143            }
144            #endif // POSIX
145        }
146    
147      /**      /**
148       *  Returns the String representation of the chunk's ID (e.g. "RIFF",       *  Returns the String representation of the chunk's ID (e.g. "RIFF",
149       *  "LIST").       *  "LIST").
# Line 105  namespace RIFF { Line 156  namespace RIFF {
156       *  Sets the position within the chunk body, thus within the data portion       *  Sets the position within the chunk body, thus within the data portion
157       *  of the chunk (in bytes).       *  of the chunk (in bytes).
158       *       *
159         *  <b>Caution:</b> the position will be reset to zero whenever
160         *  File::Save() was called.
161         *
162       *  @param Where  - position offset (in bytes)       *  @param Where  - position offset (in bytes)
163       *  @param Whence - optional: defines to what <i>\a Where</i> relates to,       *  @param Whence - optional: defines to what <i>\a Where</i> relates to,
164       *                  if omitted \a Where relates to beginning of the chunk       *                  if omitted \a Where relates to beginning of the chunk
# Line 119  namespace RIFF { Line 173  namespace RIFF {
173                  ulPos += Where;                  ulPos += Where;
174                  break;                  break;
175              case stream_end:              case stream_end:
176                  ulPos = ChunkSize - 1 - Where;                  ulPos = CurrentChunkSize - 1 - Where;
177                  break;                  break;
178              case stream_backward:              case stream_backward:
179                  ulPos -= Where;                  ulPos -= Where;
# Line 128  namespace RIFF { Line 182  namespace RIFF {
182                  ulPos = Where;                  ulPos = Where;
183                  break;                  break;
184          }          }
185          if (ulPos > ChunkSize) ulPos = ChunkSize;          if (ulPos > CurrentChunkSize) ulPos = CurrentChunkSize;
186          return ulPos;          return ulPos;
187      }      }
188    
# Line 144  namespace RIFF { Line 198  namespace RIFF {
198       */       */
199      unsigned long Chunk::RemainingBytes() {      unsigned long Chunk::RemainingBytes() {
200         #if DEBUG         #if DEBUG
201         std::cout << "Chunk::Remainingbytes()=" << ChunkSize - ulPos << std::endl;         std::cout << "Chunk::Remainingbytes()=" << CurrentChunkSize - ulPos << std::endl;
202         #endif // DEBUG         #endif // DEBUG
203          return ChunkSize - ulPos;          return CurrentChunkSize - ulPos;
204      }      }
205    
206      /**      /**
# Line 165  namespace RIFF { Line 219  namespace RIFF {
219        std::cout << "Chunk::GetState()" << std::endl;        std::cout << "Chunk::GetState()" << std::endl;
220        #endif // DEBUG        #endif // DEBUG
221          #if POSIX          #if POSIX
222          if (hFile == 0)        return stream_closed;          if (pFile->hFileRead == 0)    return stream_closed;
223          #else          #else
224          if (hFile == NULL)     return stream_closed;          if (pFile->hFileRead == NULL) return stream_closed;
225          #endif // POSIX          #endif // POSIX
226          if (ulPos < ChunkSize) return stream_ready;          if (ulPos < CurrentChunkSize) return stream_ready;
227          else                   return stream_end_reached;          else                          return stream_end_reached;
228      }      }
229    
230      /**      /**
# Line 192  namespace RIFF { Line 246  namespace RIFF {
246         #if DEBUG         #if DEBUG
247         std::cout << "Chunk::Read(void*,ulong,ulong)" << std::endl;         std::cout << "Chunk::Read(void*,ulong,ulong)" << std::endl;
248         #endif // DEBUG         #endif // DEBUG
249          if (ChunkSize - ulPos <= 0) return 0;          if (ulPos >= CurrentChunkSize) return 0;
250          if (ulPos + WordCount * WordSize >= ChunkSize) WordCount = (ChunkSize - ulPos) / WordSize;          if (ulPos + WordCount * WordSize >= CurrentChunkSize) WordCount = (CurrentChunkSize - ulPos) / WordSize;
251          #if POSIX          #if POSIX
252          if (lseek(hFile, ulStartPos + ulPos, SEEK_SET) < 0) return 0;          if (lseek(pFile->hFileRead, ulStartPos + ulPos, SEEK_SET) < 0) return 0;
253          unsigned long readWords = read(hFile, pData, WordCount * WordSize);          unsigned long readWords = read(pFile->hFileRead, pData, WordCount * WordSize);
254          if (readWords < 1) return 0;          if (readWords < 1) return 0;
255          readWords /= WordSize;          readWords /= WordSize;
256          #else // standard C functions          #else // standard C functions
257          if (fseek(hFile, ulStartPos + ulPos, SEEK_SET)) return 0;          if (fseek(pFile->hFileRead, ulStartPos + ulPos, SEEK_SET)) return 0;
258          unsigned long readWords = fread(pData, WordSize, WordCount, hFile);          unsigned long readWords = fread(pData, WordSize, WordCount, pFile->hFileRead);
259          #endif // POSIX          #endif // POSIX
260          if (!bEndianNative && WordSize != 1) {          if (!pFile->bEndianNative && WordSize != 1) {
261              switch (WordSize) {              switch (WordSize) {
262                  case 2:                  case 2:
263                      for (unsigned long iWord = 0; iWord < readWords; iWord++)                      for (unsigned long iWord = 0; iWord < readWords; iWord++)
# Line 223  namespace RIFF { Line 277  namespace RIFF {
277          return readWords;          return readWords;
278      }      }
279    
280        /**
281         *  Writes \a WordCount number of data words with given \a WordSize from
282         *  the buffer pointed by \a pData. Be sure to provide the correct
283         *  \a WordSize, as this will be important and taken into account for
284         *  eventual endian correction (swapping of bytes due to different
285         *  native byte order of a system). The position within the chunk will
286         *  automatically be incremented.
287         *
288         *  @param pData      source buffer (containing the data)
289         *  @param WordCount  number of data words to write
290         *  @param WordSize   size of each data word to write
291         *  @returns          number of successfully written data words
292         *  @throws RIFF::Exception  if write operation would exceed current
293         *                           chunk size or any IO error occured
294         *  @see Resize()
295         */
296        unsigned long Chunk::Write(void* pData, unsigned long WordCount, unsigned long WordSize) {
297            if (pFile->Mode != stream_mode_read_write)
298                throw Exception("Cannot write data to chunk, file has to be opened in read+write mode first");
299            if (ulPos >= CurrentChunkSize || ulPos + WordCount * WordSize > CurrentChunkSize)
300                throw Exception("End of chunk reached while trying to write data");
301            if (!pFile->bEndianNative && WordSize != 1) {
302                switch (WordSize) {
303                    case 2:
304                        for (unsigned long iWord = 0; iWord < WordCount; iWord++)
305                            swapBytes_16((uint16_t*) pData + iWord);
306                        break;
307                    case 4:
308                        for (unsigned long iWord = 0; iWord < WordCount; iWord++)
309                            swapBytes_32((uint32_t*) pData + iWord);
310                        break;
311                    default:
312                        for (unsigned long iWord = 0; iWord < WordCount; iWord++)
313                            swapBytes((uint8_t*) pData + iWord * WordSize, WordSize);
314                        break;
315                }
316            }
317            #if POSIX
318            if (lseek(pFile->hFileWrite, ulStartPos + ulPos, SEEK_SET) < 0) {
319                throw Exception("Could not seek to position " + ToString(ulPos) +
320                                " in chunk (" + ToString(ulStartPos + ulPos) + " in file)");
321            }
322            unsigned long writtenWords = write(pFile->hFileWrite, pData, WordCount * WordSize);
323            if (writtenWords < 1) throw Exception("POSIX IO Error while trying to write chunk data");
324            writtenWords /= WordSize;
325            #else // standard C functions
326            if (fseek(pFile->hFileWrite, ulStartPos + ulPos, SEEK_SET)) {
327                throw Exception("Could not seek to position " + ToString(ulPos) +
328                                " in chunk (" + ToString(ulStartPos + ulPos) + " in file)");
329            }
330            unsigned long writtenWords = fwrite(pData, WordSize, WordCount, pFile->hFileWrite);
331            #endif // POSIX
332            SetPos(writtenWords * WordSize, stream_curpos);
333            return writtenWords;
334        }
335    
336      /** Just an internal wrapper for the main <i>Read()</i> method with additional Exception throwing on errors. */      /** Just an internal wrapper for the main <i>Read()</i> method with additional Exception throwing on errors. */
337      unsigned long Chunk::ReadSceptical(void* pData, unsigned long WordCount, unsigned long WordSize) {      unsigned long Chunk::ReadSceptical(void* pData, unsigned long WordCount, unsigned long WordSize) {
338          unsigned long readWords = Read(pData, WordCount, WordSize);          unsigned long readWords = Read(pData, WordCount, WordSize);
# Line 249  namespace RIFF { Line 359  namespace RIFF {
359      }      }
360    
361      /**      /**
362         * Writes \a WordCount number of 8 Bit signed integer words from the
363         * buffer pointed by \a pData to the chunk's body, directly to the
364         * actual "physical" file. The position within the chunk will
365         * automatically be incremented. Note: you cannot write beyond the
366         * boundaries of the chunk, to append data to the chunk call Resize()
367         * before.
368         *
369         * @param pData             source buffer (containing the data)
370         * @param WordCount         number of 8 Bit signed integers to write
371         * @returns                 number of written integers
372         * @throws RIFF::Exception  if an IO error occured
373         * @see Resize()
374         */
375        unsigned long Chunk::WriteInt8(int8_t* pData, unsigned long WordCount) {
376            return Write(pData, WordCount, 1);
377        }
378    
379        /**
380       * Reads \a WordCount number of 8 Bit unsigned integer words and copies       * Reads \a WordCount number of 8 Bit unsigned integer words and copies
381       * it into the buffer pointed by \a pData. The buffer has to be       * it into the buffer pointed by \a pData. The buffer has to be
382       * allocated. The position within the chunk will automatically be       * allocated. The position within the chunk will automatically be
# Line 268  namespace RIFF { Line 396  namespace RIFF {
396      }      }
397    
398      /**      /**
399         * Writes \a WordCount number of 8 Bit unsigned integer words from the
400         * buffer pointed by \a pData to the chunk's body, directly to the
401         * actual "physical" file. The position within the chunk will
402         * automatically be incremented. Note: you cannot write beyond the
403         * boundaries of the chunk, to append data to the chunk call Resize()
404         * before.
405         *
406         * @param pData             source buffer (containing the data)
407         * @param WordCount         number of 8 Bit unsigned integers to write
408         * @returns                 number of written integers
409         * @throws RIFF::Exception  if an IO error occured
410         * @see Resize()
411         */
412        unsigned long Chunk::WriteUint8(uint8_t* pData, unsigned long WordCount) {
413            return Write(pData, WordCount, 1);
414        }
415    
416        /**
417       * Reads \a WordCount number of 16 Bit signed integer words and copies       * Reads \a WordCount number of 16 Bit signed integer words and copies
418       * it into the buffer pointed by \a pData. The buffer has to be       * it into the buffer pointed by \a pData. The buffer has to be
419       * allocated. Endian correction will automatically be done if needed.       * allocated. Endian correction will automatically be done if needed.
# Line 287  namespace RIFF { Line 433  namespace RIFF {
433      }      }
434    
435      /**      /**
436         * Writes \a WordCount number of 16 Bit signed integer words from the
437         * buffer pointed by \a pData to the chunk's body, directly to the
438         * actual "physical" file. The position within the chunk will
439         * automatically be incremented. Note: you cannot write beyond the
440         * boundaries of the chunk, to append data to the chunk call Resize()
441         * before.
442         *
443         * @param pData             source buffer (containing the data)
444         * @param WordCount         number of 16 Bit signed integers to write
445         * @returns                 number of written integers
446         * @throws RIFF::Exception  if an IO error occured
447         * @see Resize()
448         */
449        unsigned long Chunk::WriteInt16(int16_t* pData, unsigned long WordCount) {
450            return Write(pData, WordCount, 2);
451        }
452    
453        /**
454       * Reads \a WordCount number of 16 Bit unsigned integer words and copies       * Reads \a WordCount number of 16 Bit unsigned integer words and copies
455       * it into the buffer pointed by \a pData. The buffer has to be       * it into the buffer pointed by \a pData. The buffer has to be
456       * allocated. Endian correction will automatically be done if needed.       * allocated. Endian correction will automatically be done if needed.
# Line 306  namespace RIFF { Line 470  namespace RIFF {
470      }      }
471    
472      /**      /**
473         * Writes \a WordCount number of 16 Bit unsigned integer words from the
474         * buffer pointed by \a pData to the chunk's body, directly to the
475         * actual "physical" file. The position within the chunk will
476         * automatically be incremented. Note: you cannot write beyond the
477         * boundaries of the chunk, to append data to the chunk call Resize()
478         * before.
479         *
480         * @param pData             source buffer (containing the data)
481         * @param WordCount         number of 16 Bit unsigned integers to write
482         * @returns                 number of written integers
483         * @throws RIFF::Exception  if an IO error occured
484         * @see Resize()
485         */
486        unsigned long Chunk::WriteUint16(uint16_t* pData, unsigned long WordCount) {
487            return Write(pData, WordCount, 2);
488        }
489    
490        /**
491       * Reads \a WordCount number of 32 Bit signed integer words and copies       * Reads \a WordCount number of 32 Bit signed integer words and copies
492       * it into the buffer pointed by \a pData. The buffer has to be       * it into the buffer pointed by \a pData. The buffer has to be
493       * allocated. Endian correction will automatically be done if needed.       * allocated. Endian correction will automatically be done if needed.
# Line 325  namespace RIFF { Line 507  namespace RIFF {
507      }      }
508    
509      /**      /**
510         * Writes \a WordCount number of 32 Bit signed integer words from the
511         * buffer pointed by \a pData to the chunk's body, directly to the
512         * actual "physical" file. The position within the chunk will
513         * automatically be incremented. Note: you cannot write beyond the
514         * boundaries of the chunk, to append data to the chunk call Resize()
515         * before.
516         *
517         * @param pData             source buffer (containing the data)
518         * @param WordCount         number of 32 Bit signed integers to write
519         * @returns                 number of written integers
520         * @throws RIFF::Exception  if an IO error occured
521         * @see Resize()
522         */
523        unsigned long Chunk::WriteInt32(int32_t* pData, unsigned long WordCount) {
524            return Write(pData, WordCount, 4);
525        }
526    
527        /**
528       * Reads \a WordCount number of 32 Bit unsigned integer words and copies       * Reads \a WordCount number of 32 Bit unsigned integer words and copies
529       * it into the buffer pointed by \a pData. The buffer has to be       * it into the buffer pointed by \a pData. The buffer has to be
530       * allocated. Endian correction will automatically be done if needed.       * allocated. Endian correction will automatically be done if needed.
# Line 344  namespace RIFF { Line 544  namespace RIFF {
544      }      }
545    
546      /**      /**
547         * Writes \a WordCount number of 32 Bit unsigned integer words from the
548         * buffer pointed by \a pData to the chunk's body, directly to the
549         * actual "physical" file. The position within the chunk will
550         * automatically be incremented. Note: you cannot write beyond the
551         * boundaries of the chunk, to append data to the chunk call Resize()
552         * before.
553         *
554         * @param pData             source buffer (containing the data)
555         * @param WordCount         number of 32 Bit unsigned integers to write
556         * @returns                 number of written integers
557         * @throws RIFF::Exception  if an IO error occured
558         * @see Resize()
559         */
560        unsigned long Chunk::WriteUint32(uint32_t* pData, unsigned long WordCount) {
561            return Write(pData, WordCount, 4);
562        }
563    
564        /**
565       * Reads one 8 Bit signed integer word and increments the position within       * Reads one 8 Bit signed integer word and increments the position within
566       * the chunk.       * the chunk.
567       *       *
# Line 443  namespace RIFF { Line 661  namespace RIFF {
661          return word;          return word;
662      }      }
663    
664        /** @brief Load chunk body into RAM.
665         *
666         * Loads the whole chunk body into memory. You can modify the data in
667         * RAM and save the data by calling File::Save() afterwards.
668         *
669         * <b>Caution:</b> the buffer pointer will be invalidated once
670         * File::Save() was called. You have to call LoadChunkData() again to
671         * get a new pointer whenever File::Save() was called.
672         *
673         * @returns a pointer to the data in RAM on success, NULL otherwise
674         * @see ReleaseChunkData()
675         */
676      void* Chunk::LoadChunkData() {      void* Chunk::LoadChunkData() {
677          if (!pChunkData) {          if (!pChunkData) {
678              #if POSIX              #if POSIX
679              if (lseek(hFile, ulStartPos, SEEK_SET) == -1) return NULL;              if (lseek(pFile->hFileRead, ulStartPos, SEEK_SET) == -1) return NULL;
680              pChunkData = new uint8_t[GetSize()];              pChunkData = new uint8_t[GetSize()];
681              if (!pChunkData) return NULL;              if (!pChunkData) return NULL;
682              unsigned long readWords = read(hFile, pChunkData, GetSize());              unsigned long readWords = read(pFile->hFileRead, pChunkData, GetSize());
683              #else              #else
684              if (fseek(hFile, ulStartPos, SEEK_SET)) return NULL;              if (fseek(pFile->hFileRead, ulStartPos, SEEK_SET)) return NULL;
685              pChunkData = new uint8_t[GetSize()];              pChunkData = new uint8_t[GetSize()];
686              if (!pChunkData) return NULL;              if (!pChunkData) return NULL;
687              unsigned long readWords = fread(pChunkData, 1, GetSize(), hFile);              unsigned long readWords = fread(pChunkData, 1, GetSize(), pFile->hFileRead);
688              #endif // POSIX              #endif // POSIX
689              if (readWords != GetSize()) {              if (readWords != GetSize()) {
690                  delete[] pChunkData;                  delete[] pChunkData;
# Line 464  namespace RIFF { Line 694  namespace RIFF {
694          return pChunkData;          return pChunkData;
695      }      }
696    
697        /** @brief Free loaded chunk body from RAM.
698         *
699         * Frees loaded chunk body data from memory (RAM). You should call
700         * File::Save() before calling this method if you modified the data to
701         * make the changes persistent.
702         */
703      void Chunk::ReleaseChunkData() {      void Chunk::ReleaseChunkData() {
704          if (pChunkData) {          if (pChunkData) {
705              delete[] pChunkData;              delete[] pChunkData;
# Line 471  namespace RIFF { Line 707  namespace RIFF {
707          }          }
708      }      }
709    
710        /** @brief Resize chunk.
711         *
712         * Resizes this chunk's body, that is the actual size of data possible
713         * to be written to this chunk. This call will return immediately and
714         * just schedule the resize operation. You should call File::Save() to
715         * actually perform the resize operation(s) "physically" to the file.
716         * As this can take a while on large files, it is recommended to call
717         * Resize() first on all chunks which have to be resized and finally to
718         * call File::Save() to perform all those resize operations in one rush.
719         *
720         * <b>Caution:</b> You cannot directly write to enlarged chunks before
721         * calling File::Save() as this might exceed the current chunk's body
722         * boundary.
723         *
724         * @param iNewSize - new chunk body size in bytes (must be greater than zero)
725         * @throws RIFF::Exception  if \a iNewSize is less than 1
726         * @see File::Save()
727         */
728        void Chunk::Resize(int iNewSize) {
729            if (iNewSize <= 0) throw Exception("Chunk size must be at least one byte");
730            NewChunkSize = iNewSize;
731            pFile->LogAsResized(this);
732        }
733    
734        /** @brief Write chunk persistently e.g. to disk.
735         *
736         * Stores the chunk persistently to its actual "physical" file.
737         *
738         * @param ulWritePos - position within the "physical" file where this
739         *                     chunk should be written to
740         * @param ulCurrentDataOffset - offset of current (old) data within
741         *                              the file
742         * @returns new write position in the "physical" file, that is
743         *          \a ulWritePos incremented by this chunk's new size
744         *          (including its header size of course)
745         */
746        unsigned long Chunk::WriteChunk(unsigned long ulWritePos, unsigned long ulCurrentDataOffset) {
747            const unsigned long ulOriginalPos = ulWritePos;
748            ulWritePos += CHUNK_HEADER_SIZE;
749    
750            if (pFile->Mode != stream_mode_read_write)
751                throw Exception("Cannot write list chunk, file has to be opened in read+write mode");
752    
753            // if the whole chunk body was loaded into RAM
754            if (pChunkData) {
755                // in case the chunk size was changed, reallocate the data in RAM with the chunk's new size
756                if (NewChunkSize != CurrentChunkSize) {
757                    uint8_t* pNewBuffer = new uint8_t[NewChunkSize];
758                    if (NewChunkSize > CurrentChunkSize) {
759                        memcpy(pNewBuffer, pChunkData, CurrentChunkSize);
760                        memset(pNewBuffer + CurrentChunkSize, 0, NewChunkSize - CurrentChunkSize);
761                    } else {
762                        memcpy(pNewBuffer, pChunkData, NewChunkSize);
763                    }
764                    delete[] pChunkData;
765                    pChunkData = pNewBuffer;
766                }
767    
768                // write chunk data from RAM persistently to the file
769                #if POSIX
770                lseek(pFile->hFileWrite, ulWritePos, SEEK_SET);
771                if (write(pFile->hFileWrite, pChunkData, NewChunkSize) != NewChunkSize) {
772                    throw Exception("Writing Chunk data (from RAM) failed");
773                }
774                #else
775                fseek(pFile->hFileWrite, ulWritePos, SEEK_SET);
776                if (fwrite(pChunkData, 1, NewChunkSize, pFile->hFileWrite) != NewChunkSize) {
777                    throw Exception("Writing Chunk data (from RAM) failed");
778                }
779                #endif // POSIX
780            } else {
781                // move chunk data from the end of the file to the appropriate position
782                int8_t* pCopyBuffer = new int8_t[4096];
783                unsigned long ulToMove = (NewChunkSize < CurrentChunkSize) ? NewChunkSize : CurrentChunkSize;
784                int iBytesMoved = 1;
785                for (unsigned long ulOffset = 0; iBytesMoved > 0; ulOffset += iBytesMoved, ulToMove -= iBytesMoved) {
786                    iBytesMoved = (ulToMove < 4096) ? ulToMove : 4096;
787                    #if POSIX
788                    lseek(pFile->hFileRead, ulStartPos + ulCurrentDataOffset + ulOffset, SEEK_SET);
789                    iBytesMoved = read(pFile->hFileRead, pCopyBuffer, iBytesMoved);
790                    lseek(pFile->hFileWrite, ulWritePos + ulOffset, SEEK_SET);
791                    iBytesMoved = write(pFile->hFileWrite, pCopyBuffer, iBytesMoved);
792                    #else
793                    fseek(pFile->hFileRead, ulStartPos + ulCurrentDataOffset + ulOffset, SEEK_SET);
794                    iBytesMoved = fread(pCopyBuffer, 1, iBytesMoved, pFile->hFileRead);
795                    fseek(pFile->hFileWrite, ulWritePos + ulOffset, SEEK_SET);
796                    iBytesMoved = fwrite(pCopyBuffer, 1, iBytesMoved, pFile->hFileWrite);
797                    #endif
798                }
799                delete[] pCopyBuffer;
800                if (iBytesMoved < 0) throw Exception("Writing Chunk data (from file) failed");
801            }
802    
803            // update this chunk's header
804            CurrentChunkSize = NewChunkSize;
805            WriteHeader(ulOriginalPos);
806    
807            // update chunk's position pointers
808            ulStartPos = ulOriginalPos + CHUNK_HEADER_SIZE;
809            ulPos      = 0;
810    
811            // add pad byte if needed
812            if ((ulStartPos + NewChunkSize) % 2 != 0) {
813                const char cPadByte = 0;
814                #if POSIX
815                lseek(pFile->hFileWrite, ulStartPos + NewChunkSize, SEEK_SET);
816                write(pFile->hFileWrite, &cPadByte, 1);
817                #else
818                fseek(pFile->hFileWrite, ulStartPos + NewChunkSize, SEEK_SET);
819                fwrite(&cPadByte, 1, 1, pFile->hFileWrite);
820                #endif
821                return ulStartPos + NewChunkSize + 1;
822            }
823    
824            return ulStartPos + NewChunkSize;
825        }
826    
827        void Chunk::__resetPos() {
828            ulPos = 0;
829        }
830    
831    
832    
833  // *************** List ***************  // *************** List ***************
834  // *  // *
835    
836      List::List() : Chunk() {      List::List(File* pFile) : Chunk(pFile) {
837        #if DEBUG        #if DEBUG
838        std::cout << "List::List()" << std::endl;        std::cout << "List::List(File* pFile)" << std::endl;
839        #endif // DEBUG        #endif // DEBUG
840          pSubChunks    = NULL;          pSubChunks    = NULL;
841          pSubChunksMap = NULL;          pSubChunksMap = NULL;
842      }      }
843    
844      #if POSIX      List::List(File* pFile, unsigned long StartPos, List* Parent)
845      List::List(int hFile, unsigned long StartPos, bool EndianNative, List* Parent)        : Chunk(pFile, StartPos, Parent) {
     #else  
     List::List(FILE* hFile, unsigned long StartPos, bool EndianNative, List* Parent)  
     #endif // POSIX  
       : Chunk(hFile, StartPos, EndianNative, Parent) {  
846          #if DEBUG          #if DEBUG
847          std::cout << "List::List(FILE*,ulong,bool,List*)" << std::endl;          std::cout << "List::List(File*,ulong,bool,List*)" << std::endl;
848          #endif // DEBUG          #endif // DEBUG
849          pSubChunks    = NULL;          pSubChunks    = NULL;
850          pSubChunksMap = NULL;          pSubChunksMap = NULL;
# Line 499  namespace RIFF { Line 852  namespace RIFF {
852          ulStartPos    = StartPos + LIST_HEADER_SIZE;          ulStartPos    = StartPos + LIST_HEADER_SIZE;
853      }      }
854    
855        List::List(File* pFile, List* pParent, uint32_t uiListID)
856          : Chunk(pFile, pParent, CHUNK_ID_LIST, 0) {
857            pSubChunks    = NULL;
858            pSubChunksMap = NULL;
859            ListType      = uiListID;
860        }
861    
862      List::~List() {      List::~List() {
863        #if DEBUG        #if DEBUG
864        std::cout << "List::~List()" << std::endl;        std::cout << "List::~List()" << std::endl;
# Line 642  namespace RIFF { Line 1002  namespace RIFF {
1002      }      }
1003    
1004      /**      /**
1005       *  Returns number subchunks within the list.       *  Returns number of subchunks within the list.
1006       */       */
1007      unsigned int List::CountSubChunks() {      unsigned int List::CountSubChunks() {
1008          if (!pSubChunks) LoadSubChunks();          if (!pSubChunks) LoadSubChunks();
# Line 693  namespace RIFF { Line 1053  namespace RIFF {
1053          return result;          return result;
1054      }      }
1055    
1056        /** @brief Creates a new sub chunk.
1057         *
1058         * Creates and adds a new sub chunk to this list chunk. Note that the
1059         * chunk's body size given by \a uiBodySize must be greater than zero.
1060         * You have to call File::Save() to make this change persistent to the
1061         * actual file and <b>before</b> performing any data write operations
1062         * on the new chunk!
1063         *
1064         * @param uiChunkID  - chunk ID of the new chunk
1065         * @param uiBodySize - size of the new chunk's body, that is its actual
1066         *                     data size (without header)
1067         * @throws RIFF::Exception if \a uiBodySize equals zero
1068         */
1069        Chunk* List::AddSubChunk(uint32_t uiChunkID, uint uiBodySize) {
1070            if (uiBodySize == 0) throw Exception("Chunk body size must be at least 1 byte");
1071            if (!pSubChunks) LoadSubChunks();
1072            Chunk* pNewChunk = new Chunk(pFile, this, uiChunkID, 0);
1073            pSubChunks->push_back(pNewChunk);
1074            (*pSubChunksMap)[uiChunkID] = pNewChunk;
1075            pNewChunk->Resize(uiBodySize);
1076            return pNewChunk;
1077        }
1078    
1079        /** @brief Creates a new list sub chunk.
1080         *
1081         * Creates and adds a new list sub chunk to this list chunk. Note that
1082         * you have to add sub chunks / sub list chunks to the new created chunk
1083         * <b>before</b> trying to make this change persisten to the actual
1084         * file with File::Save()!
1085         *
1086         * @param uiListType - list ID of the new list chunk
1087         */
1088        List* List::AddSubList(uint32_t uiListType) {
1089            if (!pSubChunks) LoadSubChunks();
1090            List* pNewListChunk = new List(pFile, this, uiListType);
1091            pSubChunks->push_back(pNewListChunk);
1092            (*pSubChunksMap)[CHUNK_ID_LIST] = pNewListChunk;
1093            return pNewListChunk;
1094        }
1095    
1096        /** @brief Removes a sub chunk.
1097         *
1098         * Removes the sub chunk given by \a pSubChunk from this list and frees
1099         * it completely from RAM. The given chunk can either be a normal sub
1100         * chunk or a list sub chunk. You should call File::Save() to make this
1101         * change persistent at any time.
1102         *
1103         * @param pSubChunk - sub chunk or sub list chunk to be removed
1104         */
1105        void List::DeleteSubChunk(Chunk* pSubChunk) {
1106            if (!pSubChunks) LoadSubChunks();
1107            pSubChunks->remove(pSubChunk);
1108            if ((*pSubChunksMap)[pSubChunk->GetChunkID()] == pSubChunk) {
1109                pSubChunksMap->erase(pSubChunk->GetChunkID());
1110                // try to find another chunk of the same chunk ID
1111                ChunkList::iterator iter = pSubChunks->begin();
1112                ChunkList::iterator end  = pSubChunks->end();
1113                for (; iter != end; ++iter) {
1114                    if ((*iter)->GetChunkID() == pSubChunk->GetChunkID()) {
1115                        (*pSubChunksMap)[pSubChunk->GetChunkID()] = *iter;
1116                        break; // we're done, stop search
1117                    }
1118                }
1119            }
1120            delete pSubChunk;
1121        }
1122    
1123      void List::ReadHeader(unsigned long fPos) {      void List::ReadHeader(unsigned long fPos) {
1124        #if DEBUG        #if DEBUG
1125        std::cout << "List::Readheader(ulong) ";        std::cout << "List::Readheader(ulong) ";
1126        #endif // DEBUG        #endif // DEBUG
1127          Chunk::ReadHeader(fPos);          Chunk::ReadHeader(fPos);
1128          ChunkSize -= 4;          NewChunkSize = CurrentChunkSize -= 4;
1129          #if POSIX          #if POSIX
1130          lseek(hFile, fPos + CHUNK_HEADER_SIZE, SEEK_SET);          lseek(pFile->hFileRead, fPos + CHUNK_HEADER_SIZE, SEEK_SET);
1131          read(hFile, &ListType, 4);          read(pFile->hFileRead, &ListType, 4);
1132          #else          #else
1133          fseek(hFile, fPos + CHUNK_HEADER_SIZE, SEEK_SET);          fseek(pFile->hFileRead, fPos + CHUNK_HEADER_SIZE, SEEK_SET);
1134          fread(&ListType, 4, 1, hFile);          fread(&ListType, 4, 1, pFile->hFileRead);
1135          #endif // POSIX          #endif // POSIX
1136        #if DEBUG        #if DEBUG
1137        std::cout << "listType=" << convertToString(ListType) << std::endl;        std::cout << "listType=" << convertToString(ListType) << std::endl;
1138        #endif // DEBUG        #endif // DEBUG
1139          if (!bEndianNative) {          if (!pFile->bEndianNative) {
1140              //swapBytes_32(&ListType);              //swapBytes_32(&ListType);
1141          }          }
1142      }      }
1143    
1144        void List::WriteHeader(unsigned long fPos) {
1145            // the four list type bytes officially belong the chunk's body in the RIFF format
1146            NewChunkSize += 4;
1147            Chunk::WriteHeader(fPos);
1148            NewChunkSize -= 4; // just revert the +4 incrementation
1149            #if POSIX
1150            lseek(pFile->hFileWrite, fPos + CHUNK_HEADER_SIZE, SEEK_SET);
1151            write(pFile->hFileWrite, &ListType, 4);
1152            #else
1153            fseek(pFile->hFileWrite, fPos + CHUNK_HEADER_SIZE, SEEK_SET);
1154            fwrite(&ListType, 4, 1, pFile->hFileWrite);
1155            #endif // POSIX
1156        }
1157    
1158      void List::LoadSubChunks() {      void List::LoadSubChunks() {
1159         #if DEBUG         #if DEBUG
1160         std::cout << "List::LoadSubChunks()";         std::cout << "List::LoadSubChunks()";
# Line 721  namespace RIFF { Line 1162  namespace RIFF {
1162          if (!pSubChunks) {          if (!pSubChunks) {
1163              pSubChunks    = new ChunkList();              pSubChunks    = new ChunkList();
1164              pSubChunksMap = new ChunkMap();              pSubChunksMap = new ChunkMap();
1165                if (!pFile->hFileRead) return;
1166                unsigned long uiOriginalPos = GetPos();
1167                SetPos(0); // jump to beginning of list chunk body
1168              while (RemainingBytes() >= CHUNK_HEADER_SIZE) {              while (RemainingBytes() >= CHUNK_HEADER_SIZE) {
1169                  Chunk* ck;                  Chunk* ck;
1170                  uint32_t ckid;                  uint32_t ckid;
# Line 729  namespace RIFF { Line 1173  namespace RIFF {
1173         std::cout << " ckid=" << convertToString(ckid) << std::endl;         std::cout << " ckid=" << convertToString(ckid) << std::endl;
1174         #endif // DEBUG         #endif // DEBUG
1175                  if (ckid == CHUNK_ID_LIST) {                  if (ckid == CHUNK_ID_LIST) {
1176                      ck = new RIFF::List(hFile, ulStartPos + ulPos - 4, bEndianNative, this);                      ck = new RIFF::List(pFile, ulStartPos + ulPos - 4, this);
1177                      SetPos(ck->GetSize() + LIST_HEADER_SIZE - 4, RIFF::stream_curpos);                      SetPos(ck->GetSize() + LIST_HEADER_SIZE - 4, RIFF::stream_curpos);
1178                  }                  }
1179                  else { // simple chunk                  else { // simple chunk
1180                      ck = new RIFF::Chunk(hFile, ulStartPos + ulPos - 4, bEndianNative, this);                      ck = new RIFF::Chunk(pFile, ulStartPos + ulPos - 4, this);
1181                      SetPos(ck->GetSize() + CHUNK_HEADER_SIZE - 4, RIFF::stream_curpos);                      SetPos(ck->GetSize() + CHUNK_HEADER_SIZE - 4, RIFF::stream_curpos);
1182                  }                  }
1183                  pSubChunks->push_back(ck);                  pSubChunks->push_back(ck);
1184                  (*pSubChunksMap)[ckid] = ck;                  (*pSubChunksMap)[ckid] = ck;
1185                  if (GetPos() % 2 != 0) SetPos(1, RIFF::stream_curpos); // jump over pad byte                  if (GetPos() % 2 != 0) SetPos(1, RIFF::stream_curpos); // jump over pad byte
1186              }              }
1187                SetPos(uiOriginalPos); // restore position before this call
1188            }
1189        }
1190    
1191        /** @brief Write list chunk persistently e.g. to disk.
1192         *
1193         * Stores the list chunk persistently to its actual "physical" file. All
1194         * subchunks (including sub list chunks) will be stored recursively as
1195         * well.
1196         *
1197         * @param ulWritePos - position within the "physical" file where this
1198         *                     list chunk should be written to
1199         * @param ulCurrentDataOffset - offset of current (old) data within
1200         *                              the file
1201         * @returns new write position in the "physical" file, that is
1202         *          \a ulWritePos incremented by this list chunk's new size
1203         *          (including its header size of course)
1204         */
1205        unsigned long List::WriteChunk(unsigned long ulWritePos, unsigned long ulCurrentDataOffset) {
1206            const unsigned long ulOriginalPos = ulWritePos;
1207            ulWritePos += LIST_HEADER_SIZE;
1208    
1209            if (pFile->Mode != stream_mode_read_write)
1210                throw Exception("Cannot write list chunk, file has to be opened in read+write mode");
1211    
1212            // write all subchunks (including sub list chunks) recursively
1213            if (pSubChunks) {
1214                for (ChunkList::iterator iter = pSubChunks->begin(), end = pSubChunks->end(); iter != end; ++iter) {
1215                    ulWritePos = (*iter)->WriteChunk(ulWritePos, ulCurrentDataOffset);
1216                }
1217            }
1218    
1219            // update this list chunk's header
1220            CurrentChunkSize = NewChunkSize = ulWritePos - ulOriginalPos - LIST_HEADER_SIZE;
1221            WriteHeader(ulOriginalPos);
1222    
1223            // offset of this list chunk in new written file may have changed
1224            ulStartPos = ulOriginalPos + LIST_HEADER_SIZE;
1225    
1226            return ulWritePos;
1227        }
1228    
1229        void List::__resetPos() {
1230            Chunk::__resetPos();
1231            if (pSubChunks) {
1232                for (ChunkList::iterator iter = pSubChunks->begin(), end = pSubChunks->end(); iter != end; ++iter) {
1233                    (*iter)->__resetPos();
1234                }
1235          }          }
1236      }      }
1237    
# Line 755  namespace RIFF { Line 1247  namespace RIFF {
1247  // *************** File ***************  // *************** File ***************
1248  // *  // *
1249    
1250      File::File(const String& path) : List() {      /** @brief Create new RIFF file.
1251         *
1252         * Use this constructor if you want to create a new RIFF file completely
1253         * "from scratch". Note: there must be no empty chunks or empty list
1254         * chunks when trying to make the new RIFF file persistent with Save()!
1255         *
1256         * @param FileType - four-byte identifier of the RIFF file type
1257         * @see AddSubChunk(), AddSubList()
1258         */
1259        File::File(uint32_t FileType) : List(this) {
1260            hFileRead = hFileWrite = 0;
1261            Mode = stream_mode_closed;
1262            bEndianNative = true;
1263            ulStartPos = RIFF_HEADER_SIZE;
1264            ListType = FileType;
1265        }
1266    
1267        /** @brief Load existing RIFF file.
1268         *
1269         * Loads an existing RIFF file with all its chunks.
1270         *
1271         * @param path - path and file name of the RIFF file to open
1272         * @throws RIFF::Exception if error occured while trying to load the
1273         *                         given RIFF file
1274         */
1275        File::File(const String& path) : List(this), Filename(path) {
1276        #if DEBUG        #if DEBUG
1277        std::cout << "File::File("<<path<<")" << std::endl;        std::cout << "File::File("<<path<<")" << std::endl;
1278        #endif // DEBUG        #endif // DEBUG
1279          bEndianNative = true;          bEndianNative = true;
1280          #if POSIX          #if POSIX
1281          hFile = open(path.c_str(), O_RDONLY | O_NONBLOCK);          hFileRead = hFileWrite = open(path.c_str(), O_RDONLY | O_NONBLOCK);
1282          if (hFile <= 0) {          if (hFileRead <= 0) {
1283              hFile = 0;              hFileRead = hFileWrite = 0;
1284              throw RIFF::Exception("Can't open \"" + path + "\"");              throw RIFF::Exception("Can't open \"" + path + "\"");
1285          }          }
1286          #else          #else
1287          hFile = fopen(path.c_str(), "rb");          hFileRead = hFileWrite = fopen(path.c_str(), "rb");
1288          if (!hFile) throw RIFF::Exception("Can't open \"" + path + "\"");          if (!hFile) throw RIFF::Exception("Can't open \"" + path + "\"");
1289          #endif // POSIX          #endif // POSIX
1290            Mode = stream_mode_read;
1291          ulStartPos = RIFF_HEADER_SIZE;          ulStartPos = RIFF_HEADER_SIZE;
1292          ReadHeader(0);          ReadHeader(0);
1293          if (ChunkID != CHUNK_ID_RIFF) {          if (ChunkID != CHUNK_ID_RIFF) {
# Line 777  namespace RIFF { Line 1295  namespace RIFF {
1295          }          }
1296      }      }
1297    
1298        String File::GetFileName() {
1299            return Filename;
1300        }
1301    
1302        stream_mode_t File::GetMode() {
1303            return Mode;
1304        }
1305    
1306        /** @brief Change file access mode.
1307         *
1308         * Changes files access mode either to read-only mode or to read/write
1309         * mode.
1310         *
1311         * @param NewMode - new file access mode
1312         * @returns true if mode was changed, false if current mode already
1313         *          equals new mode
1314         * @throws RIFF::Exception if new file access mode is unknown
1315         */
1316        bool File::SetMode(stream_mode_t NewMode) {
1317            if (NewMode != Mode) {
1318                switch (NewMode) {
1319                    case stream_mode_read:
1320                        #if POSIX
1321                        if (hFileRead) close(hFileRead);
1322                        hFileRead = hFileWrite = open(Filename.c_str(), O_RDONLY | O_NONBLOCK);
1323                        if (hFileRead < 0) {
1324                            hFileRead = hFileWrite = 0;
1325                            throw Exception("Could not (re)open file \"" + Filename + "\" in read mode");
1326                        }
1327                        #else
1328                        if (hFileRead) fclose(hFileRead);
1329                        hFileRead = hFileWrite = fopen(path.c_str(), "rb");
1330                        if (!hFileRead) throw Exception("Could not (re)open file \"" + Filename + "\" in read mode");
1331                        #endif
1332                        __resetPos(); // reset read/write position of ALL 'Chunk' objects
1333                        break;
1334                    case stream_mode_read_write:
1335                        #if POSIX
1336                        if (hFileRead) close(hFileRead);
1337                        hFileRead = hFileWrite = open(Filename.c_str(), O_RDWR | O_NONBLOCK);
1338                        if (hFileRead < 0) {
1339                            hFileRead = hFileWrite = open(Filename.c_str(), O_RDONLY | O_NONBLOCK);
1340                            throw Exception("Could not open file \"" + Filename + "\" in read+write mode");
1341                        }
1342                        #else
1343                        if (hFileRead) fclose(hFileRead);
1344                        hFileRead = hFileWrite = fopen(path.c_str(), "r+b");
1345                        if (!hFileRead) {
1346                            hFileRead = hFileWrite = fopen(path.c_str(), "rb");
1347                            throw Exception("Could not open file \"" + Filename + "\" in read+write mode");
1348                        }
1349                        #endif
1350                        __resetPos(); // reset read/write position of ALL 'Chunk' objects
1351                        break;
1352                    case stream_mode_closed:
1353                        #if POSIX
1354                        if (hFileRead)  close(hFileRead);
1355                        if (hFileWrite) close(hFileWrite);
1356                        #else
1357                        if (hFileRead)  fclose(hFileRead);
1358                        if (hFileWrite) fclose(hFileWrite);
1359                        #endif
1360                        hFileRead = hFileWrite = 0;
1361                        break;
1362                    default:
1363                        throw Exception("Unknown file access mode");
1364                }
1365                Mode = NewMode;
1366                return true;
1367            }
1368            return false;
1369        }
1370    
1371        /** @brief Save changes to same file.
1372         *
1373         * Make all changes of all chunks persistent by writing them to the
1374         * actual (same) file. The file might temporarily grow to a higher size
1375         * than it will have at the end of the saving process, in case chunks
1376         * were grown.
1377         *
1378         * @throws RIFF::Exception if there is an empty chunk or empty list
1379         *                         chunk or any kind of IO error occured
1380         */
1381        void File::Save() {
1382            // reopen file in write mode
1383            SetMode(stream_mode_read_write);
1384    
1385            // to be able to save the whole file without loading everything into
1386            // RAM and without having to store the data in a temporary file, we
1387            // enlarge the file with the sum of all _positive_ chunk size
1388            // changes, move current data towards the end of the file with the
1389            // calculated sum and finally update / rewrite the file by copying
1390            // the old data back to the right position at the beginning of the file
1391    
1392            // first we sum up all positive chunk size changes (and skip all negative ones)
1393            unsigned long ulPositiveSizeDiff = 0;
1394            for (ChunkList::iterator iter = ResizedChunks.begin(), end = ResizedChunks.end(); iter != end; ++iter) {
1395                if ((*iter)->GetNewSize() == 0) throw Exception("There is at least one empty chunk (zero size)");
1396                if ((*iter)->GetNewSize() + 1L > (*iter)->GetSize()) {
1397                    unsigned long ulDiff = (*iter)->GetNewSize() - (*iter)->GetSize() + 1L; // +1 in case we have to add a pad byte
1398                    ulPositiveSizeDiff += ulDiff;
1399                }
1400            }
1401    
1402            unsigned long ulWorkingFileSize = GetFileSize();
1403    
1404            // if there are positive size changes...
1405            if (ulPositiveSizeDiff > 0) {
1406                // ... we enlarge this file first ...
1407                ulWorkingFileSize += ulPositiveSizeDiff;
1408                ResizeFile(ulWorkingFileSize);
1409                // ... and move current data by the same amount towards end of file.
1410                int8_t* pCopyBuffer = new int8_t[4096];
1411                const unsigned long ulFileSize = GetSize() + RIFF_HEADER_SIZE;
1412                int iBytesMoved = 1;
1413                for (unsigned long ulPos = 0; iBytesMoved > 0; ulPos += iBytesMoved) {
1414                    const unsigned long ulToMove = ulFileSize - ulPos;
1415                    iBytesMoved = (ulToMove < 4096) ? ulToMove : 4096;
1416                    #if POSIX
1417                    lseek(hFileRead, ulPos, SEEK_SET);
1418                    iBytesMoved = read(hFileRead, pCopyBuffer, iBytesMoved);
1419                    lseek(hFileWrite, ulPos + ulPositiveSizeDiff, SEEK_SET);
1420                    iBytesMoved = write(hFileWrite, pCopyBuffer, iBytesMoved);
1421                    #else
1422                    fseek(hFileRead, ulPos, SEEK_SET);
1423                    iBytesMoved = fread(pCopyBuffer, 1, iBytesMoved, hFileRead);
1424                    fseek(hFileWrite, ulPos + ulPositiveSizeDiff, SEEK_SET);
1425                    iBytesMoved = fwrite(pCopyBuffer, 1, iBytesMoved, hFileWrite);
1426                    #endif
1427                }
1428                delete[] pCopyBuffer;
1429                if (iBytesMoved < 0) throw Exception("Could not modify file while trying to enlarge it");
1430            }
1431    
1432            // rebuild / rewrite complete RIFF tree
1433            unsigned long ulTotalSize  = WriteChunk(0, ulPositiveSizeDiff);
1434            unsigned long ulActualSize = __GetFileSize(hFileWrite);
1435    
1436            // resize file to the final size
1437            if (ulTotalSize < ulActualSize) ResizeFile(ulTotalSize);
1438    
1439            // forget all resized chunks
1440            ResizedChunks.clear();
1441        }
1442    
1443        /** @brief Save changes to another file.
1444         *
1445         * Make all changes of all chunks persistent by writing them to another
1446         * file. <b>Caution:</b> this method is optimized for writing to
1447         * <b>another</b> file, do not use it to save the changes to the same
1448         * file! Use File::Save() in that case instead! Ignoring this might
1449         * result in a corrupted file, especially in case chunks were resized!
1450         *
1451         * After calling this method, this File object will be associated with
1452         * the new file (given by \a path) afterwards.
1453         *
1454         * @param path - path and file name where everything should be written to
1455         */
1456        void File::Save(const String& path) {
1457            //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
1458    
1459            if (Filename.length() > 0) SetMode(stream_mode_read);
1460            // open the other (new) file for writing and truncate it to zero size
1461            #if POSIX
1462            hFileWrite = open(path.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP);
1463            if (hFileWrite < 0) {
1464                hFileWrite = hFileRead;
1465                throw Exception("Could not open file \"" + path + "\" for writing");
1466            }
1467            #else
1468            hFileWrite = fopen(path.c_str(), "w+b");
1469            if (!hFileWrite) {
1470                hFileWrite = hFileRead;
1471                throw Exception("Could not open file \"" + path + "\" for writing");
1472            }
1473            #endif // POSIX
1474            Mode = stream_mode_read_write;
1475    
1476            // write complete RIFF tree to the other (new) file
1477            unsigned long ulTotalSize  = WriteChunk(0, 0);
1478            unsigned long ulActualSize = __GetFileSize(hFileWrite);
1479    
1480            // resize file to the final size (if the file was originally larger)
1481            if (ulTotalSize < ulActualSize) ResizeFile(ulTotalSize);
1482    
1483            // forget all resized chunks
1484            ResizedChunks.clear();
1485    
1486            if (Filename.length() > 0) {
1487                #if POSIX
1488                close(hFileWrite);
1489                #else
1490                fclose(hFileWrite);
1491                #endif
1492                hFileWrite = hFileRead;
1493            }
1494    
1495            // associate new file with this File object from now on
1496            Filename = path;
1497            Mode = (stream_mode_t) -1;       // Just set it to an undefined mode ...
1498            SetMode(stream_mode_read_write); // ... so SetMode() has to reopen the file handles.
1499        }
1500    
1501        void File::ResizeFile(unsigned long ulNewSize) {
1502            #if POSIX
1503            if (ftruncate(hFileWrite, ulNewSize) < 0)
1504                throw Exception("Could not resize file \"" + Filename + "\"");
1505            #else
1506            # error Sorry, this version of libgig only supports POSIX systems yet.
1507            # error Reason: portable implementation of RIFF::File::ResizeFile() is missing (yet)!
1508            #endif
1509        }
1510    
1511      File::~File() {      File::~File() {
1512         #if DEBUG         #if DEBUG
1513         std::cout << "File::~File()" << std::endl;         std::cout << "File::~File()" << std::endl;
1514         #endif // DEBUG         #endif // DEBUG
1515          #if POSIX          #if POSIX
1516          if (hFile) close(hFile);          if (hFileRead) close(hFileRead);
1517          #else          #else
1518          if (hFile) fclose(hFile);          if (hFileRead) fclose(hFileRead);
1519          #endif // POSIX          #endif // POSIX
1520      }      }
1521    
1522        void File::LogAsResized(Chunk* pResizedChunk) {
1523            ResizedChunks.push_back(pResizedChunk);
1524        }
1525    
1526      unsigned long File::GetFileSize() {      unsigned long File::GetFileSize() {
1527          #if POSIX          return __GetFileSize(hFileRead);
1528        }
1529    
1530        #if POSIX
1531        unsigned long File::__GetFileSize(int hFile) {
1532          struct stat filestat;          struct stat filestat;
1533          fstat(hFile, &filestat);          fstat(hFile, &filestat);
1534          long size = filestat.st_size;          long size = filestat.st_size;
1535          #else // standard C functions          return size;
1536        }
1537        #else // standard C functions
1538        unsigned long File::__GetFileSize(FILE* hFile) {
1539          long curpos = ftell(hFile);          long curpos = ftell(hFile);
1540          fseek(hFile, 0, SEEK_END);          fseek(hFile, 0, SEEK_END);
1541          long size = ftell(hFile);          long size = ftell(hFile);
1542          fseek(hFile, curpos, SEEK_SET);          fseek(hFile, curpos, SEEK_SET);
         #endif // POSIX  
1543          return size;          return size;
1544      }      }
1545        #endif
1546    
1547    
1548  // *************** Exception ***************  // *************** Exception ***************
# Line 811  namespace RIFF { Line 1552  namespace RIFF {
1552          std::cout << "RIFF::Exception: " << Message << std::endl;          std::cout << "RIFF::Exception: " << Message << std::endl;
1553      }      }
1554    
1555    
1556    // *************** functions ***************
1557    // *
1558    
1559        /**
1560         * Returns the name of this C++ library. This is usually "libgig" of
1561         * course. This call is equivalent to DLS::libraryName() and
1562         * gig::libraryName().
1563         */
1564        String libraryName() {
1565            return PACKAGE;
1566        }
1567    
1568        /**
1569         * Returns version of this C++ library. This call is equivalent to
1570         * DLS::libraryVersion() and gig::libraryVersion().
1571         */
1572        String libraryVersion() {
1573            return VERSION;
1574        }
1575    
1576  } // namespace RIFF  } // namespace RIFF
 #endif  

Legend:
Removed from v.11  
changed lines
  Added in v.798

  ViewVC Help
Powered by ViewVC