libgig  4.1.0.svn8
RIFF.cpp
1 /***************************************************************************
2  * *
3  * libgig - C++ cross-platform Gigasampler format file access library *
4  * *
5  * Copyright (C) 2003-2018 by Christian Schoenebeck *
6  * <cuse@users.sourceforge.net> *
7  * *
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 
24 #include <algorithm>
25 #include <set>
26 #include <string.h>
27 
28 #include "RIFF.h"
29 
30 #include "helper.h"
31 
32 #if POSIX
33 # include <errno.h>
34 #endif
35 
36 namespace RIFF {
37 
38 // *************** Internal functions **************
39 // *
40 
42  static String __resolveChunkPath(Chunk* pCk) {
43  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 // *************** progress_t ***************
58 // *
59 
60  progress_t::progress_t() {
61  callback = NULL;
62  custom = NULL;
63  __range_min = 0.0f;
64  __range_max = 1.0f;
65  }
66 
67 
68 
69 // *************** Chunk **************
70 // *
71 
72  Chunk::Chunk(File* pFile) {
73  #if DEBUG_RIFF
74  std::cout << "Chunk::Chunk(File* pFile)" << std::endl;
75  #endif // DEBUG_RIFF
76  ullPos = 0;
77  pParent = NULL;
78  pChunkData = NULL;
79  ullCurrentChunkSize = 0;
80  ullNewChunkSize = 0;
81  ullChunkDataSize = 0;
82  ChunkID = CHUNK_ID_RIFF;
83  this->pFile = pFile;
84  }
85 
86  Chunk::Chunk(File* pFile, file_offset_t StartPos, List* Parent) {
87  #if DEBUG_RIFF
88  std::cout << "Chunk::Chunk(File*,file_offset_t,List*),StartPos=" << StartPos << std::endl;
89  #endif // DEBUG_RIFF
90  this->pFile = pFile;
91  ullStartPos = StartPos + CHUNK_HEADER_SIZE(pFile->FileOffsetSize);
92  pParent = Parent;
93  ullPos = 0;
94  pChunkData = NULL;
95  ullCurrentChunkSize = 0;
96  ullNewChunkSize = 0;
97  ullChunkDataSize = 0;
98  ReadHeader(StartPos);
99  }
100 
101  Chunk::Chunk(File* pFile, List* pParent, uint32_t uiChunkID, file_offset_t ullBodySize) {
102  this->pFile = pFile;
103  ullStartPos = 0; // arbitrary usually, since it will be updated when we write the chunk
104  this->pParent = pParent;
105  ullPos = 0;
106  pChunkData = NULL;
107  ChunkID = uiChunkID;
108  ullChunkDataSize = 0;
109  ullCurrentChunkSize = 0;
110  ullNewChunkSize = ullBodySize;
111  }
112 
113  Chunk::~Chunk() {
114  if (pChunkData) delete[] pChunkData;
115  }
116 
117  void Chunk::ReadHeader(file_offset_t filePos) {
118  #if DEBUG_RIFF
119  std::cout << "Chunk::Readheader(" << filePos << ") ";
120  #endif // DEBUG_RIFF
121  ChunkID = 0;
122  ullNewChunkSize = ullCurrentChunkSize = 0;
123  #if POSIX
124  if (lseek(pFile->hFileRead, filePos, SEEK_SET) != -1) {
125  read(pFile->hFileRead, &ChunkID, 4);
126  read(pFile->hFileRead, &ullCurrentChunkSize, pFile->FileOffsetSize);
127  #elif defined(WIN32)
128  LARGE_INTEGER liFilePos;
129  liFilePos.QuadPart = filePos;
130  if (SetFilePointerEx(pFile->hFileRead, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN)) {
131  DWORD dwBytesRead;
132  ReadFile(pFile->hFileRead, &ChunkID, 4, &dwBytesRead, NULL);
133  ReadFile(pFile->hFileRead, &ullCurrentChunkSize, pFile->FileOffsetSize, &dwBytesRead, NULL);
134  #else
135  if (!fseeko(pFile->hFileRead, filePos, SEEK_SET)) {
136  fread(&ChunkID, 4, 1, pFile->hFileRead);
137  fread(&ullCurrentChunkSize, pFile->FileOffsetSize, 1, pFile->hFileRead);
138  #endif // POSIX
139  #if WORDS_BIGENDIAN
140  if (ChunkID == CHUNK_ID_RIFF) {
141  pFile->bEndianNative = false;
142  }
143  #else // little endian
144  if (ChunkID == CHUNK_ID_RIFX) {
145  pFile->bEndianNative = false;
146  ChunkID = CHUNK_ID_RIFF;
147  }
148  #endif // WORDS_BIGENDIAN
149  if (!pFile->bEndianNative) {
150  //swapBytes_32(&ChunkID);
151  if (pFile->FileOffsetSize == 4)
152  swapBytes_32(&ullCurrentChunkSize);
153  else
154  swapBytes_64(&ullCurrentChunkSize);
155  }
156  #if DEBUG_RIFF
157  std::cout << "ckID=" << convertToString(ChunkID) << " ";
158  std::cout << "ckSize=" << ullCurrentChunkSize << " ";
159  std::cout << "bEndianNative=" << pFile->bEndianNative << std::endl;
160  #endif // DEBUG_RIFF
161  ullNewChunkSize = ullCurrentChunkSize;
162  }
163  }
164 
165  void Chunk::WriteHeader(file_offset_t filePos) {
166  uint32_t uiNewChunkID = ChunkID;
167  if (ChunkID == CHUNK_ID_RIFF) {
168  #if WORDS_BIGENDIAN
169  if (pFile->bEndianNative) uiNewChunkID = CHUNK_ID_RIFX;
170  #else // little endian
171  if (!pFile->bEndianNative) uiNewChunkID = CHUNK_ID_RIFX;
172  #endif // WORDS_BIGENDIAN
173  }
174 
175  uint64_t ullNewChunkSize = this->ullNewChunkSize;
176  if (!pFile->bEndianNative) {
177  if (pFile->FileOffsetSize == 4)
178  swapBytes_32(&ullNewChunkSize);
179  else
180  swapBytes_64(&ullNewChunkSize);
181  }
182 
183  #if POSIX
184  if (lseek(pFile->hFileWrite, filePos, SEEK_SET) != -1) {
185  write(pFile->hFileWrite, &uiNewChunkID, 4);
186  write(pFile->hFileWrite, &ullNewChunkSize, pFile->FileOffsetSize);
187  }
188  #elif defined(WIN32)
189  LARGE_INTEGER liFilePos;
190  liFilePos.QuadPart = filePos;
191  if (SetFilePointerEx(pFile->hFileWrite, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN)) {
192  DWORD dwBytesWritten;
193  WriteFile(pFile->hFileWrite, &uiNewChunkID, 4, &dwBytesWritten, NULL);
194  WriteFile(pFile->hFileWrite, &ullNewChunkSize, pFile->FileOffsetSize, &dwBytesWritten, NULL);
195  }
196  #else
197  if (!fseeko(pFile->hFileWrite, filePos, SEEK_SET)) {
198  fwrite(&uiNewChunkID, 4, 1, pFile->hFileWrite);
199  fwrite(&ullNewChunkSize, pFile->FileOffsetSize, 1, pFile->hFileWrite);
200  }
201  #endif // POSIX
202  }
203 
208  String Chunk::GetChunkIDString() const {
209  return convertToString(ChunkID);
210  }
211 
225  #if DEBUG_RIFF
226  std::cout << "Chunk::SetPos(file_offset_t,stream_whence_t)" << std::endl;
227  #endif // DEBUG_RIFF
228  switch (Whence) {
229  case stream_curpos:
230  ullPos += Where;
231  break;
232  case stream_end:
233  ullPos = ullCurrentChunkSize - 1 - Where;
234  break;
235  case stream_backward:
236  ullPos -= Where;
237  break;
238  case stream_start: default:
239  ullPos = Where;
240  break;
241  }
242  if (ullPos > ullCurrentChunkSize) ullPos = ullCurrentChunkSize;
243  return ullPos;
244  }
245 
257  #if DEBUG_RIFF
258  std::cout << "Chunk::Remainingbytes()=" << ullCurrentChunkSize - ullPos << std::endl;
259  #endif // DEBUG_RIFF
260  return (ullCurrentChunkSize > ullPos) ? ullCurrentChunkSize - ullPos : 0;
261  }
262 
271  return CHUNK_HEADER_SIZE(fileOffsetSize) + // RIFF chunk header
272  ullNewChunkSize + // chunks's actual data body
273  ullNewChunkSize % 2; // optional pad byte
274  }
275 
288  #if DEBUG_RIFF
289  std::cout << "Chunk::GetState()" << std::endl;
290  #endif // DEBUG_RIFF
291  #if POSIX
292  if (pFile->hFileRead == 0) return stream_closed;
293  #elif defined (WIN32)
294  if (pFile->hFileRead == INVALID_HANDLE_VALUE)
295  return stream_closed;
296  #else
297  if (pFile->hFileRead == NULL) return stream_closed;
298  #endif // POSIX
299  if (ullPos < ullCurrentChunkSize) return stream_ready;
300  else return stream_end_reached;
301  }
302 
318  file_offset_t Chunk::Read(void* pData, file_offset_t WordCount, file_offset_t WordSize) {
319  #if DEBUG_RIFF
320  std::cout << "Chunk::Read(void*,file_offset_t,file_offset_t)" << std::endl;
321  #endif // DEBUG_RIFF
322  //if (ulStartPos == 0) return 0; // is only 0 if this is a new chunk, so nothing to read (yet)
323  if (ullPos >= ullCurrentChunkSize) return 0;
324  if (ullPos + WordCount * WordSize >= ullCurrentChunkSize) WordCount = (ullCurrentChunkSize - ullPos) / WordSize;
325  #if POSIX
326  if (lseek(pFile->hFileRead, ullStartPos + ullPos, SEEK_SET) < 0) return 0;
327  ssize_t readWords = read(pFile->hFileRead, pData, WordCount * WordSize);
328  if (readWords < 1) {
329  #if DEBUG_RIFF
330  std::cerr << "POSIX read() failed: " << strerror(errno) << std::endl << std::flush;
331  #endif // DEBUG_RIFF
332  return 0;
333  }
334  readWords /= WordSize;
335  #elif defined(WIN32)
336  LARGE_INTEGER liFilePos;
337  liFilePos.QuadPart = ullStartPos + ullPos;
338  if (!SetFilePointerEx(pFile->hFileRead, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN))
339  return 0;
340  DWORD readWords;
341  ReadFile(pFile->hFileRead, pData, WordCount * WordSize, &readWords, NULL); //FIXME: does not work for reading buffers larger than 2GB (even though this should rarely be the case in practice)
342  if (readWords < 1) return 0;
343  readWords /= WordSize;
344  #else // standard C functions
345  if (fseeko(pFile->hFileRead, ullStartPos + ullPos, SEEK_SET)) return 0;
346  file_offset_t readWords = fread(pData, WordSize, WordCount, pFile->hFileRead);
347  #endif // POSIX
348  if (!pFile->bEndianNative && WordSize != 1) {
349  switch (WordSize) {
350  case 2:
351  for (file_offset_t iWord = 0; iWord < readWords; iWord++)
352  swapBytes_16((uint16_t*) pData + iWord);
353  break;
354  case 4:
355  for (file_offset_t iWord = 0; iWord < readWords; iWord++)
356  swapBytes_32((uint32_t*) pData + iWord);
357  break;
358  case 8:
359  for (file_offset_t iWord = 0; iWord < readWords; iWord++)
360  swapBytes_64((uint64_t*) pData + iWord);
361  break;
362  default:
363  for (file_offset_t iWord = 0; iWord < readWords; iWord++)
364  swapBytes((uint8_t*) pData + iWord * WordSize, WordSize);
365  break;
366  }
367  }
368  SetPos(readWords * WordSize, stream_curpos);
369  return readWords;
370  }
371 
388  file_offset_t Chunk::Write(void* pData, file_offset_t WordCount, file_offset_t WordSize) {
389  if (pFile->Mode != stream_mode_read_write)
390  throw Exception("Cannot write data to chunk, file has to be opened in read+write mode first");
391  if (ullPos >= ullCurrentChunkSize || ullPos + WordCount * WordSize > ullCurrentChunkSize)
392  throw Exception("End of chunk reached while trying to write data");
393  if (!pFile->bEndianNative && WordSize != 1) {
394  switch (WordSize) {
395  case 2:
396  for (file_offset_t iWord = 0; iWord < WordCount; iWord++)
397  swapBytes_16((uint16_t*) pData + iWord);
398  break;
399  case 4:
400  for (file_offset_t iWord = 0; iWord < WordCount; iWord++)
401  swapBytes_32((uint32_t*) pData + iWord);
402  break;
403  case 8:
404  for (file_offset_t iWord = 0; iWord < WordCount; iWord++)
405  swapBytes_64((uint64_t*) pData + iWord);
406  break;
407  default:
408  for (file_offset_t iWord = 0; iWord < WordCount; iWord++)
409  swapBytes((uint8_t*) pData + iWord * WordSize, WordSize);
410  break;
411  }
412  }
413  #if POSIX
414  if (lseek(pFile->hFileWrite, ullStartPos + ullPos, SEEK_SET) < 0) {
415  throw Exception("Could not seek to position " + ToString(ullPos) +
416  " in chunk (" + ToString(ullStartPos + ullPos) + " in file)");
417  }
418  ssize_t writtenWords = write(pFile->hFileWrite, pData, WordCount * WordSize);
419  if (writtenWords < 1) throw Exception("POSIX IO Error while trying to write chunk data");
420  writtenWords /= WordSize;
421  #elif defined(WIN32)
422  LARGE_INTEGER liFilePos;
423  liFilePos.QuadPart = ullStartPos + ullPos;
424  if (!SetFilePointerEx(pFile->hFileWrite, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN)) {
425  throw Exception("Could not seek to position " + ToString(ullPos) +
426  " in chunk (" + ToString(ullStartPos + ullPos) + " in file)");
427  }
428  DWORD writtenWords;
429  WriteFile(pFile->hFileWrite, pData, WordCount * WordSize, &writtenWords, NULL); //FIXME: does not work for writing buffers larger than 2GB (even though this should rarely be the case in practice)
430  if (writtenWords < 1) throw Exception("Windows IO Error while trying to write chunk data");
431  writtenWords /= WordSize;
432  #else // standard C functions
433  if (fseeko(pFile->hFileWrite, ullStartPos + ullPos, SEEK_SET)) {
434  throw Exception("Could not seek to position " + ToString(ullPos) +
435  " in chunk (" + ToString(ullStartPos + ullPos) + " in file)");
436  }
437  file_offset_t writtenWords = fwrite(pData, WordSize, WordCount, pFile->hFileWrite);
438  #endif // POSIX
439  SetPos(writtenWords * WordSize, stream_curpos);
440  return writtenWords;
441  }
442 
444  file_offset_t Chunk::ReadSceptical(void* pData, file_offset_t WordCount, file_offset_t WordSize) {
445  file_offset_t readWords = Read(pData, WordCount, WordSize);
446  if (readWords != WordCount) throw RIFF::Exception("End of chunk data reached.");
447  return readWords;
448  }
449 
461  file_offset_t Chunk::ReadInt8(int8_t* pData, file_offset_t WordCount) {
462  #if DEBUG_RIFF
463  std::cout << "Chunk::ReadInt8(int8_t*,file_offset_t)" << std::endl;
464  #endif // DEBUG_RIFF
465  return ReadSceptical(pData, WordCount, 1);
466  }
467 
482  file_offset_t Chunk::WriteInt8(int8_t* pData, file_offset_t WordCount) {
483  return Write(pData, WordCount, 1);
484  }
485 
498  file_offset_t Chunk::ReadUint8(uint8_t* pData, file_offset_t WordCount) {
499  #if DEBUG_RIFF
500  std::cout << "Chunk::ReadUint8(uint8_t*,file_offset_t)" << std::endl;
501  #endif // DEBUG_RIFF
502  return ReadSceptical(pData, WordCount, 1);
503  }
504 
519  file_offset_t Chunk::WriteUint8(uint8_t* pData, file_offset_t WordCount) {
520  return Write(pData, WordCount, 1);
521  }
522 
535  file_offset_t Chunk::ReadInt16(int16_t* pData, file_offset_t WordCount) {
536  #if DEBUG_RIFF
537  std::cout << "Chunk::ReadInt16(int16_t*,file_offset_t)" << std::endl;
538  #endif // DEBUG_RIFF
539  return ReadSceptical(pData, WordCount, 2);
540  }
541 
556  file_offset_t Chunk::WriteInt16(int16_t* pData, file_offset_t WordCount) {
557  return Write(pData, WordCount, 2);
558  }
559 
572  file_offset_t Chunk::ReadUint16(uint16_t* pData, file_offset_t WordCount) {
573  #if DEBUG_RIFF
574  std::cout << "Chunk::ReadUint16(uint16_t*,file_offset_t)" << std::endl;
575  #endif // DEBUG_RIFF
576  return ReadSceptical(pData, WordCount, 2);
577  }
578 
593  file_offset_t Chunk::WriteUint16(uint16_t* pData, file_offset_t WordCount) {
594  return Write(pData, WordCount, 2);
595  }
596 
609  file_offset_t Chunk::ReadInt32(int32_t* pData, file_offset_t WordCount) {
610  #if DEBUG_RIFF
611  std::cout << "Chunk::ReadInt32(int32_t*,file_offset_t)" << std::endl;
612  #endif // DEBUG_RIFF
613  return ReadSceptical(pData, WordCount, 4);
614  }
615 
630  file_offset_t Chunk::WriteInt32(int32_t* pData, file_offset_t WordCount) {
631  return Write(pData, WordCount, 4);
632  }
633 
646  file_offset_t Chunk::ReadUint32(uint32_t* pData, file_offset_t WordCount) {
647  #if DEBUG_RIFF
648  std::cout << "Chunk::ReadUint32(uint32_t*,file_offset_t)" << std::endl;
649  #endif // DEBUG_RIFF
650  return ReadSceptical(pData, WordCount, 4);
651  }
652 
663  void Chunk::ReadString(String& s, int size) {
664  char* buf = new char[size];
665  ReadSceptical(buf, 1, size);
666  s.assign(buf, std::find(buf, buf + size, '\0'));
667  delete[] buf;
668  }
669 
684  file_offset_t Chunk::WriteUint32(uint32_t* pData, file_offset_t WordCount) {
685  return Write(pData, WordCount, 4);
686  }
687 
695  int8_t Chunk::ReadInt8() {
696  #if DEBUG_RIFF
697  std::cout << "Chunk::ReadInt8()" << std::endl;
698  #endif // DEBUG_RIFF
699  int8_t word;
700  ReadSceptical(&word,1,1);
701  return word;
702  }
703 
711  uint8_t Chunk::ReadUint8() {
712  #if DEBUG_RIFF
713  std::cout << "Chunk::ReadUint8()" << std::endl;
714  #endif // DEBUG_RIFF
715  uint8_t word;
716  ReadSceptical(&word,1,1);
717  return word;
718  }
719 
728  int16_t Chunk::ReadInt16() {
729  #if DEBUG_RIFF
730  std::cout << "Chunk::ReadInt16()" << std::endl;
731  #endif // DEBUG_RIFF
732  int16_t word;
733  ReadSceptical(&word,1,2);
734  return word;
735  }
736 
745  uint16_t Chunk::ReadUint16() {
746  #if DEBUG_RIFF
747  std::cout << "Chunk::ReadUint16()" << std::endl;
748  #endif // DEBUG_RIFF
749  uint16_t word;
750  ReadSceptical(&word,1,2);
751  return word;
752  }
753 
762  int32_t Chunk::ReadInt32() {
763  #if DEBUG_RIFF
764  std::cout << "Chunk::ReadInt32()" << std::endl;
765  #endif // DEBUG_RIFF
766  int32_t word;
767  ReadSceptical(&word,1,4);
768  return word;
769  }
770 
779  uint32_t Chunk::ReadUint32() {
780  #if DEBUG_RIFF
781  std::cout << "Chunk::ReadUint32()" << std::endl;
782  #endif // DEBUG_RIFF
783  uint32_t word;
784  ReadSceptical(&word,1,4);
785  return word;
786  }
787 
810  if (!pChunkData && pFile->Filename != "" /*&& ulStartPos != 0*/) {
811  #if POSIX
812  if (lseek(pFile->hFileRead, ullStartPos, SEEK_SET) == -1) return NULL;
813  #elif defined(WIN32)
814  LARGE_INTEGER liFilePos;
815  liFilePos.QuadPart = ullStartPos;
816  if (!SetFilePointerEx(pFile->hFileRead, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN)) return NULL;
817  #else
818  if (fseeko(pFile->hFileRead, ullStartPos, SEEK_SET)) return NULL;
819  #endif // POSIX
820  file_offset_t ullBufferSize = (ullCurrentChunkSize > ullNewChunkSize) ? ullCurrentChunkSize : ullNewChunkSize;
821  pChunkData = new uint8_t[ullBufferSize];
822  if (!pChunkData) return NULL;
823  memset(pChunkData, 0, ullBufferSize);
824  #if POSIX
825  file_offset_t readWords = read(pFile->hFileRead, pChunkData, GetSize());
826  #elif defined(WIN32)
827  DWORD readWords;
828  ReadFile(pFile->hFileRead, pChunkData, GetSize(), &readWords, NULL); //FIXME: won't load chunks larger than 2GB !
829  #else
830  file_offset_t readWords = fread(pChunkData, 1, GetSize(), pFile->hFileRead);
831  #endif // POSIX
832  if (readWords != GetSize()) {
833  delete[] pChunkData;
834  return (pChunkData = NULL);
835  }
836  ullChunkDataSize = ullBufferSize;
837  } else if (ullNewChunkSize > ullChunkDataSize) {
838  uint8_t* pNewBuffer = new uint8_t[ullNewChunkSize];
839  if (!pNewBuffer) throw Exception("Could not enlarge chunk data buffer to " + ToString(ullNewChunkSize) + " bytes");
840  memset(pNewBuffer, 0 , ullNewChunkSize);
841  memcpy(pNewBuffer, pChunkData, ullChunkDataSize);
842  delete[] pChunkData;
843  pChunkData = pNewBuffer;
844  ullChunkDataSize = ullNewChunkSize;
845  }
846  return pChunkData;
847  }
848 
856  if (pChunkData) {
857  delete[] pChunkData;
858  pChunkData = NULL;
859  }
860  }
861 
880  void Chunk::Resize(file_offset_t NewSize) {
881  if (NewSize == 0)
882  throw Exception("There is at least one empty chunk (zero size): " + __resolveChunkPath(this));
883  if ((NewSize >> 48) != 0)
884  throw Exception("Unrealistic high chunk size detected: " + __resolveChunkPath(this));
885  if (ullNewChunkSize == NewSize) return;
886  ullNewChunkSize = NewSize;
887  }
888 
902  file_offset_t Chunk::WriteChunk(file_offset_t ullWritePos, file_offset_t ullCurrentDataOffset, progress_t* pProgress) {
903  const file_offset_t ullOriginalPos = ullWritePos;
904  ullWritePos += CHUNK_HEADER_SIZE(pFile->FileOffsetSize);
905 
906  if (pFile->Mode != stream_mode_read_write)
907  throw Exception("Cannot write list chunk, file has to be opened in read+write mode");
908 
909  // if the whole chunk body was loaded into RAM
910  if (pChunkData) {
911  // make sure chunk data buffer in RAM is at least as large as the new chunk size
912  LoadChunkData();
913  // write chunk data from RAM persistently to the file
914  #if POSIX
915  lseek(pFile->hFileWrite, ullWritePos, SEEK_SET);
916  if (write(pFile->hFileWrite, pChunkData, ullNewChunkSize) != ullNewChunkSize) {
917  throw Exception("Writing Chunk data (from RAM) failed");
918  }
919  #elif defined(WIN32)
920  LARGE_INTEGER liFilePos;
921  liFilePos.QuadPart = ullWritePos;
922  SetFilePointerEx(pFile->hFileWrite, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN);
923  DWORD dwBytesWritten;
924  WriteFile(pFile->hFileWrite, pChunkData, ullNewChunkSize, &dwBytesWritten, NULL); //FIXME: won't save chunks larger than 2GB !
925  if (dwBytesWritten != ullNewChunkSize) {
926  throw Exception("Writing Chunk data (from RAM) failed");
927  }
928  #else
929  fseeko(pFile->hFileWrite, ullWritePos, SEEK_SET);
930  if (fwrite(pChunkData, 1, ullNewChunkSize, pFile->hFileWrite) != ullNewChunkSize) {
931  throw Exception("Writing Chunk data (from RAM) failed");
932  }
933  #endif // POSIX
934  } else {
935  // move chunk data from the end of the file to the appropriate position
936  int8_t* pCopyBuffer = new int8_t[4096];
937  file_offset_t ullToMove = (ullNewChunkSize < ullCurrentChunkSize) ? ullNewChunkSize : ullCurrentChunkSize;
938  #if defined(WIN32)
939  DWORD iBytesMoved = 1; // we have to pass it via pointer to the Windows API, thus the correct size must be ensured
940  #else
941  int iBytesMoved = 1;
942  #endif
943  for (file_offset_t ullOffset = 0; ullToMove > 0 && iBytesMoved > 0; ullOffset += iBytesMoved, ullToMove -= iBytesMoved) {
944  iBytesMoved = (ullToMove < 4096) ? int(ullToMove) : 4096;
945  #if POSIX
946  lseek(pFile->hFileRead, ullStartPos + ullCurrentDataOffset + ullOffset, SEEK_SET);
947  iBytesMoved = (int) read(pFile->hFileRead, pCopyBuffer, (size_t) iBytesMoved);
948  lseek(pFile->hFileWrite, ullWritePos + ullOffset, SEEK_SET);
949  iBytesMoved = (int) write(pFile->hFileWrite, pCopyBuffer, (size_t) iBytesMoved);
950  #elif defined(WIN32)
951  LARGE_INTEGER liFilePos;
952  liFilePos.QuadPart = ullStartPos + ullCurrentDataOffset + ullOffset;
953  SetFilePointerEx(pFile->hFileRead, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN);
954  ReadFile(pFile->hFileRead, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
955  liFilePos.QuadPart = ullWritePos + ullOffset;
956  SetFilePointerEx(pFile->hFileWrite, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN);
957  WriteFile(pFile->hFileWrite, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
958  #else
959  fseeko(pFile->hFileRead, ullStartPos + ullCurrentDataOffset + ullOffset, SEEK_SET);
960  iBytesMoved = fread(pCopyBuffer, 1, iBytesMoved, pFile->hFileRead);
961  fseeko(pFile->hFileWrite, ullWritePos + ullOffset, SEEK_SET);
962  iBytesMoved = fwrite(pCopyBuffer, 1, iBytesMoved, pFile->hFileWrite);
963  #endif
964  }
965  delete[] pCopyBuffer;
966  if (iBytesMoved < 0) throw Exception("Writing Chunk data (from file) failed");
967  }
968 
969  // update this chunk's header
970  ullCurrentChunkSize = ullNewChunkSize;
971  WriteHeader(ullOriginalPos);
972 
973  __notify_progress(pProgress, 1.0); // notify done
974 
975  // update chunk's position pointers
976  ullStartPos = ullOriginalPos + CHUNK_HEADER_SIZE(pFile->FileOffsetSize);
977  ullPos = 0;
978 
979  // add pad byte if needed
980  if ((ullStartPos + ullNewChunkSize) % 2 != 0) {
981  const char cPadByte = 0;
982  #if POSIX
983  lseek(pFile->hFileWrite, ullStartPos + ullNewChunkSize, SEEK_SET);
984  write(pFile->hFileWrite, &cPadByte, 1);
985  #elif defined(WIN32)
986  LARGE_INTEGER liFilePos;
987  liFilePos.QuadPart = ullStartPos + ullNewChunkSize;
988  SetFilePointerEx(pFile->hFileWrite, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN);
989  DWORD dwBytesWritten;
990  WriteFile(pFile->hFileWrite, &cPadByte, 1, &dwBytesWritten, NULL);
991  #else
992  fseeko(pFile->hFileWrite, ullStartPos + ullNewChunkSize, SEEK_SET);
993  fwrite(&cPadByte, 1, 1, pFile->hFileWrite);
994  #endif
995  return ullStartPos + ullNewChunkSize + 1;
996  }
997 
998  return ullStartPos + ullNewChunkSize;
999  }
1000 
1002  ullPos = 0;
1003  }
1004 
1005 
1006 
1007 // *************** List ***************
1008 // *
1009 
1010  List::List(File* pFile) : Chunk(pFile) {
1011  #if DEBUG_RIFF
1012  std::cout << "List::List(File* pFile)" << std::endl;
1013  #endif // DEBUG_RIFF
1014  pSubChunks = NULL;
1015  pSubChunksMap = NULL;
1016  }
1017 
1018  List::List(File* pFile, file_offset_t StartPos, List* Parent)
1019  : Chunk(pFile, StartPos, Parent) {
1020  #if DEBUG_RIFF
1021  std::cout << "List::List(File*,file_offset_t,List*)" << std::endl;
1022  #endif // DEBUG_RIFF
1023  pSubChunks = NULL;
1024  pSubChunksMap = NULL;
1025  ReadHeader(StartPos);
1026  ullStartPos = StartPos + LIST_HEADER_SIZE(pFile->FileOffsetSize);
1027  }
1028 
1029  List::List(File* pFile, List* pParent, uint32_t uiListID)
1030  : Chunk(pFile, pParent, CHUNK_ID_LIST, 0) {
1031  pSubChunks = NULL;
1032  pSubChunksMap = NULL;
1033  ListType = uiListID;
1034  }
1035 
1036  List::~List() {
1037  #if DEBUG_RIFF
1038  std::cout << "List::~List()" << std::endl;
1039  #endif // DEBUG_RIFF
1040  DeleteChunkList();
1041  }
1042 
1043  void List::DeleteChunkList() {
1044  if (pSubChunks) {
1045  ChunkList::iterator iter = pSubChunks->begin();
1046  ChunkList::iterator end = pSubChunks->end();
1047  while (iter != end) {
1048  delete *iter;
1049  iter++;
1050  }
1051  delete pSubChunks;
1052  pSubChunks = NULL;
1053  }
1054  if (pSubChunksMap) {
1055  delete pSubChunksMap;
1056  pSubChunksMap = NULL;
1057  }
1058  }
1059 
1071  Chunk* List::GetSubChunk(uint32_t ChunkID) {
1072  #if DEBUG_RIFF
1073  std::cout << "List::GetSubChunk(uint32_t)" << std::endl;
1074  #endif // DEBUG_RIFF
1075  if (!pSubChunksMap) LoadSubChunks();
1076  return (*pSubChunksMap)[ChunkID];
1077  }
1078 
1090  List* List::GetSubList(uint32_t ListType) {
1091  #if DEBUG_RIFF
1092  std::cout << "List::GetSubList(uint32_t)" << std::endl;
1093  #endif // DEBUG_RIFF
1094  if (!pSubChunks) LoadSubChunks();
1095  ChunkList::iterator iter = pSubChunks->begin();
1096  ChunkList::iterator end = pSubChunks->end();
1097  while (iter != end) {
1098  if ((*iter)->GetChunkID() == CHUNK_ID_LIST) {
1099  List* l = (List*) *iter;
1100  if (l->GetListType() == ListType) return l;
1101  }
1102  iter++;
1103  }
1104  return NULL;
1105  }
1106 
1117  #if DEBUG_RIFF
1118  std::cout << "List::GetFirstSubChunk()" << std::endl;
1119  #endif // DEBUG_RIFF
1120  if (!pSubChunks) LoadSubChunks();
1121  ChunksIterator = pSubChunks->begin();
1122  return (ChunksIterator != pSubChunks->end()) ? *ChunksIterator : NULL;
1123  }
1124 
1134  #if DEBUG_RIFF
1135  std::cout << "List::GetNextSubChunk()" << std::endl;
1136  #endif // DEBUG_RIFF
1137  if (!pSubChunks) return NULL;
1138  ChunksIterator++;
1139  return (ChunksIterator != pSubChunks->end()) ? *ChunksIterator : NULL;
1140  }
1141 
1152  #if DEBUG_RIFF
1153  std::cout << "List::GetFirstSubList()" << std::endl;
1154  #endif // DEBUG_RIFF
1155  if (!pSubChunks) LoadSubChunks();
1156  ListIterator = pSubChunks->begin();
1157  ChunkList::iterator end = pSubChunks->end();
1158  while (ListIterator != end) {
1159  if ((*ListIterator)->GetChunkID() == CHUNK_ID_LIST) return (List*) *ListIterator;
1160  ListIterator++;
1161  }
1162  return NULL;
1163  }
1164 
1174  #if DEBUG_RIFF
1175  std::cout << "List::GetNextSubList()" << std::endl;
1176  #endif // DEBUG_RIFF
1177  if (!pSubChunks) return NULL;
1178  if (ListIterator == pSubChunks->end()) return NULL;
1179  ListIterator++;
1180  ChunkList::iterator end = pSubChunks->end();
1181  while (ListIterator != end) {
1182  if ((*ListIterator)->GetChunkID() == CHUNK_ID_LIST) return (List*) *ListIterator;
1183  ListIterator++;
1184  }
1185  return NULL;
1186  }
1187 
1192  if (!pSubChunks) LoadSubChunks();
1193  return pSubChunks->size();
1194  }
1195 
1200  size_t List::CountSubChunks(uint32_t ChunkID) {
1201  size_t result = 0;
1202  if (!pSubChunks) LoadSubChunks();
1203  ChunkList::iterator iter = pSubChunks->begin();
1204  ChunkList::iterator end = pSubChunks->end();
1205  while (iter != end) {
1206  if ((*iter)->GetChunkID() == ChunkID) {
1207  result++;
1208  }
1209  iter++;
1210  }
1211  return result;
1212  }
1213 
1218  return CountSubChunks(CHUNK_ID_LIST);
1219  }
1220 
1225  size_t List::CountSubLists(uint32_t ListType) {
1226  size_t result = 0;
1227  if (!pSubChunks) LoadSubChunks();
1228  ChunkList::iterator iter = pSubChunks->begin();
1229  ChunkList::iterator end = pSubChunks->end();
1230  while (iter != end) {
1231  if ((*iter)->GetChunkID() == CHUNK_ID_LIST) {
1232  List* l = (List*) *iter;
1233  if (l->GetListType() == ListType) result++;
1234  }
1235  iter++;
1236  }
1237  return result;
1238  }
1239 
1253  Chunk* List::AddSubChunk(uint32_t uiChunkID, file_offset_t ullBodySize) {
1254  if (ullBodySize == 0) throw Exception("Chunk body size must be at least 1 byte");
1255  if (!pSubChunks) LoadSubChunks();
1256  Chunk* pNewChunk = new Chunk(pFile, this, uiChunkID, 0);
1257  pSubChunks->push_back(pNewChunk);
1258  (*pSubChunksMap)[uiChunkID] = pNewChunk;
1259  pNewChunk->Resize(ullBodySize);
1260  ullNewChunkSize += CHUNK_HEADER_SIZE(pFile->FileOffsetSize);
1261  return pNewChunk;
1262  }
1263 
1275  void List::MoveSubChunk(Chunk* pSrc, Chunk* pDst) {
1276  if (!pSubChunks) LoadSubChunks();
1277  pSubChunks->remove(pSrc);
1278  ChunkList::iterator iter = find(pSubChunks->begin(), pSubChunks->end(), pDst);
1279  pSubChunks->insert(iter, pSrc);
1280  }
1281 
1290  void List::MoveSubChunk(Chunk* pSrc, List* pNewParent) {
1291  if (pNewParent == this || !pNewParent) return;
1292  if (!pSubChunks) LoadSubChunks();
1293  if (!pNewParent->pSubChunks) pNewParent->LoadSubChunks();
1294  pSubChunks->remove(pSrc);
1295  pNewParent->pSubChunks->push_back(pSrc);
1296  // update chunk id map of this List
1297  if ((*pSubChunksMap)[pSrc->GetChunkID()] == pSrc) {
1298  pSubChunksMap->erase(pSrc->GetChunkID());
1299  // try to find another chunk of the same chunk ID
1300  ChunkList::iterator iter = pSubChunks->begin();
1301  ChunkList::iterator end = pSubChunks->end();
1302  for (; iter != end; ++iter) {
1303  if ((*iter)->GetChunkID() == pSrc->GetChunkID()) {
1304  (*pSubChunksMap)[pSrc->GetChunkID()] = *iter;
1305  break; // we're done, stop search
1306  }
1307  }
1308  }
1309  // update chunk id map of other list
1310  if (!(*pNewParent->pSubChunksMap)[pSrc->GetChunkID()])
1311  (*pNewParent->pSubChunksMap)[pSrc->GetChunkID()] = pSrc;
1312  }
1313 
1323  List* List::AddSubList(uint32_t uiListType) {
1324  if (!pSubChunks) LoadSubChunks();
1325  List* pNewListChunk = new List(pFile, this, uiListType);
1326  pSubChunks->push_back(pNewListChunk);
1327  (*pSubChunksMap)[CHUNK_ID_LIST] = pNewListChunk;
1328  ullNewChunkSize += LIST_HEADER_SIZE(pFile->FileOffsetSize);
1329  return pNewListChunk;
1330  }
1331 
1342  void List::DeleteSubChunk(Chunk* pSubChunk) {
1343  if (!pSubChunks) LoadSubChunks();
1344  pSubChunks->remove(pSubChunk);
1345  if ((*pSubChunksMap)[pSubChunk->GetChunkID()] == pSubChunk) {
1346  pSubChunksMap->erase(pSubChunk->GetChunkID());
1347  // try to find another chunk of the same chunk ID
1348  ChunkList::iterator iter = pSubChunks->begin();
1349  ChunkList::iterator end = pSubChunks->end();
1350  for (; iter != end; ++iter) {
1351  if ((*iter)->GetChunkID() == pSubChunk->GetChunkID()) {
1352  (*pSubChunksMap)[pSubChunk->GetChunkID()] = *iter;
1353  break; // we're done, stop search
1354  }
1355  }
1356  }
1357  delete pSubChunk;
1358  }
1359 
1368  if (!pSubChunks) LoadSubChunks();
1369  file_offset_t size = LIST_HEADER_SIZE(fileOffsetSize);
1370  ChunkList::iterator iter = pSubChunks->begin();
1371  ChunkList::iterator end = pSubChunks->end();
1372  for (; iter != end; ++iter)
1373  size += (*iter)->RequiredPhysicalSize(fileOffsetSize);
1374  return size;
1375  }
1376 
1377  void List::ReadHeader(file_offset_t filePos) {
1378  #if DEBUG_RIFF
1379  std::cout << "List::Readheader(file_offset_t) ";
1380  #endif // DEBUG_RIFF
1381  Chunk::ReadHeader(filePos);
1382  if (ullCurrentChunkSize < 4) return;
1383  ullNewChunkSize = ullCurrentChunkSize -= 4;
1384  #if POSIX
1385  lseek(pFile->hFileRead, filePos + CHUNK_HEADER_SIZE(pFile->FileOffsetSize), SEEK_SET);
1386  read(pFile->hFileRead, &ListType, 4);
1387  #elif defined(WIN32)
1388  LARGE_INTEGER liFilePos;
1389  liFilePos.QuadPart = filePos + CHUNK_HEADER_SIZE(pFile->FileOffsetSize);
1390  SetFilePointerEx(pFile->hFileRead, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN);
1391  DWORD dwBytesRead;
1392  ReadFile(pFile->hFileRead, &ListType, 4, &dwBytesRead, NULL);
1393  #else
1394  fseeko(pFile->hFileRead, filePos + CHUNK_HEADER_SIZE(pFile->FileOffsetSize), SEEK_SET);
1395  fread(&ListType, 4, 1, pFile->hFileRead);
1396  #endif // POSIX
1397  #if DEBUG_RIFF
1398  std::cout << "listType=" << convertToString(ListType) << std::endl;
1399  #endif // DEBUG_RIFF
1400  if (!pFile->bEndianNative) {
1401  //swapBytes_32(&ListType);
1402  }
1403  }
1404 
1405  void List::WriteHeader(file_offset_t filePos) {
1406  // the four list type bytes officially belong the chunk's body in the RIFF format
1407  ullNewChunkSize += 4;
1408  Chunk::WriteHeader(filePos);
1409  ullNewChunkSize -= 4; // just revert the +4 incrementation
1410  #if POSIX
1411  lseek(pFile->hFileWrite, filePos + CHUNK_HEADER_SIZE(pFile->FileOffsetSize), SEEK_SET);
1412  write(pFile->hFileWrite, &ListType, 4);
1413  #elif defined(WIN32)
1414  LARGE_INTEGER liFilePos;
1415  liFilePos.QuadPart = filePos + CHUNK_HEADER_SIZE(pFile->FileOffsetSize);
1416  SetFilePointerEx(pFile->hFileWrite, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN);
1417  DWORD dwBytesWritten;
1418  WriteFile(pFile->hFileWrite, &ListType, 4, &dwBytesWritten, NULL);
1419  #else
1420  fseeko(pFile->hFileWrite, filePos + CHUNK_HEADER_SIZE(pFile->FileOffsetSize), SEEK_SET);
1421  fwrite(&ListType, 4, 1, pFile->hFileWrite);
1422  #endif // POSIX
1423  }
1424 
1425  void List::LoadSubChunks(progress_t* pProgress) {
1426  #if DEBUG_RIFF
1427  std::cout << "List::LoadSubChunks()";
1428  #endif // DEBUG_RIFF
1429  if (!pSubChunks) {
1430  pSubChunks = new ChunkList();
1431  pSubChunksMap = new ChunkMap();
1432  #if defined(WIN32)
1433  if (pFile->hFileRead == INVALID_HANDLE_VALUE) return;
1434  #else
1435  if (!pFile->hFileRead) return;
1436  #endif
1437  file_offset_t ullOriginalPos = GetPos();
1438  SetPos(0); // jump to beginning of list chunk body
1439  while (RemainingBytes() >= CHUNK_HEADER_SIZE(pFile->FileOffsetSize)) {
1440  Chunk* ck;
1441  uint32_t ckid;
1442  Read(&ckid, 4, 1);
1443  #if DEBUG_RIFF
1444  std::cout << " ckid=" << convertToString(ckid) << std::endl;
1445  #endif // DEBUG_RIFF
1446  if (ckid == CHUNK_ID_LIST) {
1447  ck = new RIFF::List(pFile, ullStartPos + ullPos - 4, this);
1448  SetPos(ck->GetSize() + LIST_HEADER_SIZE(pFile->FileOffsetSize) - 4, RIFF::stream_curpos);
1449  }
1450  else { // simple chunk
1451  ck = new RIFF::Chunk(pFile, ullStartPos + ullPos - 4, this);
1452  SetPos(ck->GetSize() + CHUNK_HEADER_SIZE(pFile->FileOffsetSize) - 4, RIFF::stream_curpos);
1453  }
1454  pSubChunks->push_back(ck);
1455  (*pSubChunksMap)[ckid] = ck;
1456  if (GetPos() % 2 != 0) SetPos(1, RIFF::stream_curpos); // jump over pad byte
1457  }
1458  SetPos(ullOriginalPos); // restore position before this call
1459  }
1460  __notify_progress(pProgress, 1.0); // notify done
1461  }
1462 
1463  void List::LoadSubChunksRecursively(progress_t* pProgress) {
1464  const int n = (int) CountSubLists();
1465  int i = 0;
1466  for (List* pList = GetFirstSubList(); pList; pList = GetNextSubList(), ++i) {
1467  // divide local progress into subprogress
1468  progress_t subprogress;
1469  __divide_progress(pProgress, &subprogress, n, i);
1470  // do the actual work
1471  pList->LoadSubChunksRecursively(&subprogress);
1472  }
1473  __notify_progress(pProgress, 1.0); // notify done
1474  }
1475 
1491  file_offset_t List::WriteChunk(file_offset_t ullWritePos, file_offset_t ullCurrentDataOffset, progress_t* pProgress) {
1492  const file_offset_t ullOriginalPos = ullWritePos;
1493  ullWritePos += LIST_HEADER_SIZE(pFile->FileOffsetSize);
1494 
1495  if (pFile->Mode != stream_mode_read_write)
1496  throw Exception("Cannot write list chunk, file has to be opened in read+write mode");
1497 
1498  // write all subchunks (including sub list chunks) recursively
1499  if (pSubChunks) {
1500  size_t i = 0;
1501  const size_t n = pSubChunks->size();
1502  for (ChunkList::iterator iter = pSubChunks->begin(), end = pSubChunks->end(); iter != end; ++iter, ++i) {
1503  // divide local progress into subprogress for loading current Instrument
1504  progress_t subprogress;
1505  __divide_progress(pProgress, &subprogress, n, i);
1506  // do the actual work
1507  ullWritePos = (*iter)->WriteChunk(ullWritePos, ullCurrentDataOffset, &subprogress);
1508  }
1509  }
1510 
1511  // update this list chunk's header
1512  ullCurrentChunkSize = ullNewChunkSize = ullWritePos - ullOriginalPos - LIST_HEADER_SIZE(pFile->FileOffsetSize);
1513  WriteHeader(ullOriginalPos);
1514 
1515  // offset of this list chunk in new written file may have changed
1516  ullStartPos = ullOriginalPos + LIST_HEADER_SIZE(pFile->FileOffsetSize);
1517 
1518  __notify_progress(pProgress, 1.0); // notify done
1519 
1520  return ullWritePos;
1521  }
1522 
1525  if (pSubChunks) {
1526  for (ChunkList::iterator iter = pSubChunks->begin(), end = pSubChunks->end(); iter != end; ++iter) {
1527  (*iter)->__resetPos();
1528  }
1529  }
1530  }
1531 
1535  String List::GetListTypeString() const {
1536  return convertToString(ListType);
1537  }
1538 
1539 
1540 
1541 // *************** File ***************
1542 // *
1543 
1558  File::File(uint32_t FileType)
1559  : List(this), bIsNewFile(true), Layout(layout_standard),
1560  FileOffsetPreference(offset_size_auto)
1561  {
1562  #if defined(WIN32)
1563  hFileRead = hFileWrite = INVALID_HANDLE_VALUE;
1564  #else
1565  hFileRead = hFileWrite = 0;
1566  #endif
1567  Mode = stream_mode_closed;
1568  bEndianNative = true;
1569  ListType = FileType;
1570  FileOffsetSize = 4;
1571  ullStartPos = RIFF_HEADER_SIZE(FileOffsetSize);
1572  }
1573 
1582  File::File(const String& path)
1583  : List(this), Filename(path), bIsNewFile(false), Layout(layout_standard),
1584  FileOffsetPreference(offset_size_auto)
1585  {
1586  #if DEBUG_RIFF
1587  std::cout << "File::File("<<path<<")" << std::endl;
1588  #endif // DEBUG_RIFF
1589  bEndianNative = true;
1590  FileOffsetSize = 4;
1591  try {
1592  __openExistingFile(path);
1593  if (ChunkID != CHUNK_ID_RIFF && ChunkID != CHUNK_ID_RIFX) {
1594  throw RIFF::Exception("Not a RIFF file");
1595  }
1596  }
1597  catch (...) {
1598  Cleanup();
1599  throw;
1600  }
1601  }
1602 
1629  File::File(const String& path, uint32_t FileType, endian_t Endian, layout_t layout, offset_size_t fileOffsetSize)
1630  : List(this), Filename(path), bIsNewFile(false), Layout(layout),
1631  FileOffsetPreference(fileOffsetSize)
1632  {
1633  SetByteOrder(Endian);
1634  if (fileOffsetSize < offset_size_auto || fileOffsetSize > offset_size_64bit)
1635  throw Exception("Invalid RIFF::offset_size_t");
1636  FileOffsetSize = 4;
1637  try {
1638  __openExistingFile(path, &FileType);
1639  }
1640  catch (...) {
1641  Cleanup();
1642  throw;
1643  }
1644  }
1645 
1656  void File::__openExistingFile(const String& path, uint32_t* FileType) {
1657  #if POSIX
1658  hFileRead = hFileWrite = open(path.c_str(), O_RDONLY | O_NONBLOCK);
1659  if (hFileRead == -1) {
1660  hFileRead = hFileWrite = 0;
1661  String sError = strerror(errno);
1662  throw RIFF::Exception("Can't open \"" + path + "\": " + sError);
1663  }
1664  #elif defined(WIN32)
1665  hFileRead = hFileWrite = CreateFile(
1666  path.c_str(), GENERIC_READ,
1667  FILE_SHARE_READ | FILE_SHARE_WRITE,
1668  NULL, OPEN_EXISTING,
1669  FILE_ATTRIBUTE_NORMAL |
1670  FILE_FLAG_RANDOM_ACCESS, NULL
1671  );
1672  if (hFileRead == INVALID_HANDLE_VALUE) {
1673  hFileRead = hFileWrite = INVALID_HANDLE_VALUE;
1674  throw RIFF::Exception("Can't open \"" + path + "\"");
1675  }
1676  #else
1677  hFileRead = hFileWrite = fopen(path.c_str(), "rb");
1678  if (!hFileRead) throw RIFF::Exception("Can't open \"" + path + "\"");
1679  #endif // POSIX
1680  Mode = stream_mode_read;
1681 
1682  // determine RIFF file offset size to be used (in RIFF chunk headers)
1683  // according to the current file offset preference
1684  FileOffsetSize = FileOffsetSizeFor(GetCurrentFileSize());
1685 
1686  switch (Layout) {
1687  case layout_standard: // this is a normal RIFF file
1688  ullStartPos = RIFF_HEADER_SIZE(FileOffsetSize);
1689  ReadHeader(0);
1690  if (FileType && ChunkID != *FileType)
1691  throw RIFF::Exception("Invalid file container ID");
1692  break;
1693  case layout_flat: // non-standard RIFF-alike file
1694  ullStartPos = 0;
1695  ullNewChunkSize = ullCurrentChunkSize = GetCurrentFileSize();
1696  if (FileType) {
1697  uint32_t ckid;
1698  if (Read(&ckid, 4, 1) != 4) {
1699  throw RIFF::Exception("Invalid file header ID (premature end of header)");
1700  } else if (ckid != *FileType) {
1701  String s = " (expected '" + convertToString(*FileType) + "' but got '" + convertToString(ckid) + "')";
1702  throw RIFF::Exception("Invalid file header ID" + s);
1703  }
1704  SetPos(0); // reset to first byte of file
1705  }
1706  LoadSubChunks();
1707  break;
1708  }
1709  }
1710 
1711  String File::GetFileName() const {
1712  return Filename;
1713  }
1714 
1715  void File::SetFileName(const String& path) {
1716  Filename = path;
1717  }
1718 
1719  stream_mode_t File::GetMode() const {
1720  return Mode;
1721  }
1722 
1723  layout_t File::GetLayout() const {
1724  return Layout;
1725  }
1726 
1738  if (NewMode != Mode) {
1739  switch (NewMode) {
1740  case stream_mode_read:
1741  #if POSIX
1742  if (hFileRead) close(hFileRead);
1743  hFileRead = hFileWrite = open(Filename.c_str(), O_RDONLY | O_NONBLOCK);
1744  if (hFileRead == -1) {
1745  hFileRead = hFileWrite = 0;
1746  String sError = strerror(errno);
1747  throw Exception("Could not (re)open file \"" + Filename + "\" in read mode: " + sError);
1748  }
1749  #elif defined(WIN32)
1750  if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
1751  hFileRead = hFileWrite = CreateFile(
1752  Filename.c_str(), GENERIC_READ,
1753  FILE_SHARE_READ | FILE_SHARE_WRITE,
1754  NULL, OPEN_EXISTING,
1755  FILE_ATTRIBUTE_NORMAL |
1756  FILE_FLAG_RANDOM_ACCESS,
1757  NULL
1758  );
1759  if (hFileRead == INVALID_HANDLE_VALUE) {
1760  hFileRead = hFileWrite = INVALID_HANDLE_VALUE;
1761  throw Exception("Could not (re)open file \"" + Filename + "\" in read mode");
1762  }
1763  #else
1764  if (hFileRead) fclose(hFileRead);
1765  hFileRead = hFileWrite = fopen(Filename.c_str(), "rb");
1766  if (!hFileRead) throw Exception("Could not (re)open file \"" + Filename + "\" in read mode");
1767  #endif
1768  __resetPos(); // reset read/write position of ALL 'Chunk' objects
1769  break;
1770  case stream_mode_read_write:
1771  #if POSIX
1772  if (hFileRead) close(hFileRead);
1773  hFileRead = hFileWrite = open(Filename.c_str(), O_RDWR | O_NONBLOCK);
1774  if (hFileRead == -1) {
1775  hFileRead = hFileWrite = open(Filename.c_str(), O_RDONLY | O_NONBLOCK);
1776  String sError = strerror(errno);
1777  throw Exception("Could not open file \"" + Filename + "\" in read+write mode: " + sError);
1778  }
1779  #elif defined(WIN32)
1780  if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
1781  hFileRead = hFileWrite = CreateFile(
1782  Filename.c_str(),
1783  GENERIC_READ | GENERIC_WRITE,
1784  FILE_SHARE_READ,
1785  NULL, OPEN_ALWAYS,
1786  FILE_ATTRIBUTE_NORMAL |
1787  FILE_FLAG_RANDOM_ACCESS,
1788  NULL
1789  );
1790  if (hFileRead == INVALID_HANDLE_VALUE) {
1791  hFileRead = hFileWrite = CreateFile(
1792  Filename.c_str(), GENERIC_READ,
1793  FILE_SHARE_READ | FILE_SHARE_WRITE,
1794  NULL, OPEN_EXISTING,
1795  FILE_ATTRIBUTE_NORMAL |
1796  FILE_FLAG_RANDOM_ACCESS,
1797  NULL
1798  );
1799  throw Exception("Could not (re)open file \"" + Filename + "\" in read+write mode");
1800  }
1801  #else
1802  if (hFileRead) fclose(hFileRead);
1803  hFileRead = hFileWrite = fopen(Filename.c_str(), "r+b");
1804  if (!hFileRead) {
1805  hFileRead = hFileWrite = fopen(Filename.c_str(), "rb");
1806  throw Exception("Could not open file \"" + Filename + "\" in read+write mode");
1807  }
1808  #endif
1809  __resetPos(); // reset read/write position of ALL 'Chunk' objects
1810  break;
1811  case stream_mode_closed:
1812  #if POSIX
1813  if (hFileRead) close(hFileRead);
1814  if (hFileWrite) close(hFileWrite);
1815  hFileRead = hFileWrite = 0;
1816  #elif defined(WIN32)
1817  if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
1818  if (hFileWrite != INVALID_HANDLE_VALUE) CloseHandle(hFileWrite);
1819  hFileRead = hFileWrite = INVALID_HANDLE_VALUE;
1820  #else
1821  if (hFileRead) fclose(hFileRead);
1822  if (hFileWrite) fclose(hFileWrite);
1823  hFileRead = hFileWrite = NULL;
1824  #endif
1825  break;
1826  default:
1827  throw Exception("Unknown file access mode");
1828  }
1829  Mode = NewMode;
1830  return true;
1831  }
1832  return false;
1833  }
1834 
1845  #if WORDS_BIGENDIAN
1846  bEndianNative = Endian != endian_little;
1847  #else
1848  bEndianNative = Endian != endian_big;
1849  #endif
1850  }
1851 
1861  void File::Save(progress_t* pProgress) {
1862  //TODO: implementation for the case where first chunk is not a global container (List chunk) is not implemented yet (i.e. Korg files)
1863  if (Layout == layout_flat)
1864  throw Exception("Saving a RIFF file with layout_flat is not implemented yet");
1865 
1866  // make sure the RIFF tree is built (from the original file)
1867  {
1868  // divide progress into subprogress
1869  progress_t subprogress;
1870  __divide_progress(pProgress, &subprogress, 3.f, 0.f); // arbitrarily subdivided into 1/3 of total progress
1871  // do the actual work
1872  LoadSubChunksRecursively(&subprogress);
1873  // notify subprogress done
1874  __notify_progress(&subprogress, 1.f);
1875  }
1876 
1877  // reopen file in write mode
1878  SetMode(stream_mode_read_write);
1879 
1880  // get the current file size as it is now still physically stored on disk
1881  const file_offset_t workingFileSize = GetCurrentFileSize();
1882 
1883  // get the overall file size required to save this file
1884  const file_offset_t newFileSize = GetRequiredFileSize(FileOffsetPreference);
1885 
1886  // determine whether this file will yield in a large file (>=4GB) and
1887  // the RIFF file offset size to be used accordingly for all chunks
1888  FileOffsetSize = FileOffsetSizeFor(newFileSize);
1889 
1890  // to be able to save the whole file without loading everything into
1891  // RAM and without having to store the data in a temporary file, we
1892  // enlarge the file with the overall positive file size change,
1893  // then move current data towards the end of the file by the calculated
1894  // positive file size difference and finally update / rewrite the file
1895  // by copying the old data back to the right position at the beginning
1896  // of the file
1897 
1898  // if there are positive size changes...
1899  file_offset_t positiveSizeDiff = 0;
1900  if (newFileSize > workingFileSize) {
1901  positiveSizeDiff = newFileSize - workingFileSize;
1902 
1903  // divide progress into subprogress
1904  progress_t subprogress;
1905  __divide_progress(pProgress, &subprogress, 3.f, 1.f); // arbitrarily subdivided into 1/3 of total progress
1906 
1907  // ... we enlarge this file first ...
1908  ResizeFile(newFileSize);
1909 
1910  // ... and move current data by the same amount towards end of file.
1911  int8_t* pCopyBuffer = new int8_t[4096];
1912  #if defined(WIN32)
1913  DWORD iBytesMoved = 1; // we have to pass it via pointer to the Windows API, thus the correct size must be ensured
1914  #else
1915  ssize_t iBytesMoved = 1;
1916  #endif
1917  for (file_offset_t ullPos = workingFileSize, iNotif = 0; iBytesMoved > 0; ++iNotif) {
1918  iBytesMoved = (ullPos < 4096) ? ullPos : 4096;
1919  ullPos -= iBytesMoved;
1920  #if POSIX
1921  lseek(hFileRead, ullPos, SEEK_SET);
1922  iBytesMoved = read(hFileRead, pCopyBuffer, iBytesMoved);
1923  lseek(hFileWrite, ullPos + positiveSizeDiff, SEEK_SET);
1924  iBytesMoved = write(hFileWrite, pCopyBuffer, iBytesMoved);
1925  #elif defined(WIN32)
1926  LARGE_INTEGER liFilePos;
1927  liFilePos.QuadPart = ullPos;
1928  SetFilePointerEx(hFileRead, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN);
1929  ReadFile(hFileRead, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
1930  liFilePos.QuadPart = ullPos + positiveSizeDiff;
1931  SetFilePointerEx(hFileWrite, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN);
1932  WriteFile(hFileWrite, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
1933  #else
1934  fseeko(hFileRead, ullPos, SEEK_SET);
1935  iBytesMoved = fread(pCopyBuffer, 1, iBytesMoved, hFileRead);
1936  fseeko(hFileWrite, ullPos + positiveSizeDiff, SEEK_SET);
1937  iBytesMoved = fwrite(pCopyBuffer, 1, iBytesMoved, hFileWrite);
1938  #endif
1939  if (!(iNotif % 8) && iBytesMoved > 0)
1940  __notify_progress(&subprogress, float(workingFileSize - ullPos) / float(workingFileSize));
1941  }
1942  delete[] pCopyBuffer;
1943  if (iBytesMoved < 0) throw Exception("Could not modify file while trying to enlarge it");
1944 
1945  __notify_progress(&subprogress, 1.f); // notify subprogress done
1946  }
1947 
1948  // rebuild / rewrite complete RIFF tree ...
1949 
1950  // divide progress into subprogress
1951  progress_t subprogress;
1952  __divide_progress(pProgress, &subprogress, 3.f, 2.f); // arbitrarily subdivided into 1/3 of total progress
1953  // do the actual work
1954  const file_offset_t finalSize = WriteChunk(0, positiveSizeDiff, &subprogress);
1955  const file_offset_t finalActualSize = __GetFileSize(hFileWrite);
1956  // notify subprogress done
1957  __notify_progress(&subprogress, 1.f);
1958 
1959  // resize file to the final size
1960  if (finalSize < finalActualSize) ResizeFile(finalSize);
1961 
1962  __notify_progress(pProgress, 1.0); // notify done
1963  }
1964 
1979  void File::Save(const String& path, progress_t* pProgress) {
1980  //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
1981 
1982  //TODO: implementation for the case where first chunk is not a global container (List chunk) is not implemented yet (i.e. Korg files)
1983  if (Layout == layout_flat)
1984  throw Exception("Saving a RIFF file with layout_flat is not implemented yet");
1985 
1986  // make sure the RIFF tree is built (from the original file)
1987  {
1988  // divide progress into subprogress
1989  progress_t subprogress;
1990  __divide_progress(pProgress, &subprogress, 2.f, 0.f); // arbitrarily subdivided into 1/2 of total progress
1991  // do the actual work
1992  LoadSubChunksRecursively(&subprogress);
1993  // notify subprogress done
1994  __notify_progress(&subprogress, 1.f);
1995  }
1996 
1997  if (!bIsNewFile) SetMode(stream_mode_read);
1998  // open the other (new) file for writing and truncate it to zero size
1999  #if POSIX
2000  hFileWrite = open(path.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP);
2001  if (hFileWrite == -1) {
2003  String sError = strerror(errno);
2004  throw Exception("Could not open file \"" + path + "\" for writing: " + sError);
2005  }
2006  #elif defined(WIN32)
2007  hFileWrite = CreateFile(
2008  path.c_str(), GENERIC_WRITE, FILE_SHARE_READ,
2009  NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL |
2010  FILE_FLAG_RANDOM_ACCESS, NULL
2011  );
2012  if (hFileWrite == INVALID_HANDLE_VALUE) {
2014  throw Exception("Could not open file \"" + path + "\" for writing");
2015  }
2016  #else
2017  hFileWrite = fopen(path.c_str(), "w+b");
2018  if (!hFileWrite) {
2020  throw Exception("Could not open file \"" + path + "\" for writing");
2021  }
2022  #endif // POSIX
2023  Mode = stream_mode_read_write;
2024 
2025  // get the overall file size required to save this file
2026  const file_offset_t newFileSize = GetRequiredFileSize(FileOffsetPreference);
2027 
2028  // determine whether this file will yield in a large file (>=4GB) and
2029  // the RIFF file offset size to be used accordingly for all chunks
2030  FileOffsetSize = FileOffsetSizeFor(newFileSize);
2031 
2032  // write complete RIFF tree to the other (new) file
2033  file_offset_t ullTotalSize;
2034  {
2035  // divide progress into subprogress
2036  progress_t subprogress;
2037  __divide_progress(pProgress, &subprogress, 2.f, 1.f); // arbitrarily subdivided into 1/2 of total progress
2038  // do the actual work
2039  ullTotalSize = WriteChunk(0, 0, &subprogress);
2040  // notify subprogress done
2041  __notify_progress(&subprogress, 1.f);
2042  }
2043  file_offset_t ullActualSize = __GetFileSize(hFileWrite);
2044 
2045  // resize file to the final size (if the file was originally larger)
2046  if (ullActualSize > ullTotalSize) ResizeFile(ullTotalSize);
2047 
2048  #if POSIX
2049  if (hFileWrite) close(hFileWrite);
2050  #elif defined(WIN32)
2051  if (hFileWrite != INVALID_HANDLE_VALUE) CloseHandle(hFileWrite);
2052  #else
2053  if (hFileWrite) fclose(hFileWrite);
2054  #endif
2056 
2057  // associate new file with this File object from now on
2058  Filename = path;
2059  bIsNewFile = false;
2060  Mode = (stream_mode_t) -1; // Just set it to an undefined mode ...
2061  SetMode(stream_mode_read_write); // ... so SetMode() has to reopen the file handles.
2062 
2063  __notify_progress(pProgress, 1.0); // notify done
2064  }
2065 
2066  void File::ResizeFile(file_offset_t ullNewSize) {
2067  #if POSIX
2068  if (ftruncate(hFileWrite, ullNewSize) < 0)
2069  throw Exception("Could not resize file \"" + Filename + "\"");
2070  #elif defined(WIN32)
2071  LARGE_INTEGER liFilePos;
2072  liFilePos.QuadPart = ullNewSize;
2073  if (
2074  !SetFilePointerEx(hFileWrite, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN) ||
2075  !SetEndOfFile(hFileWrite)
2076  ) throw Exception("Could not resize file \"" + Filename + "\"");
2077  #else
2078  # error Sorry, this version of libgig only supports POSIX and Windows systems yet.
2079  # error Reason: portable implementation of RIFF::File::ResizeFile() is missing (yet)!
2080  #endif
2081  }
2082 
2083  File::~File() {
2084  #if DEBUG_RIFF
2085  std::cout << "File::~File()" << std::endl;
2086  #endif // DEBUG_RIFF
2087  Cleanup();
2088  }
2089 
2094  bool File::IsNew() const {
2095  return bIsNewFile;
2096  }
2097 
2098  void File::Cleanup() {
2099  #if POSIX
2100  if (hFileRead) close(hFileRead);
2101  #elif defined(WIN32)
2102  if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
2103  #else
2104  if (hFileRead) fclose(hFileRead);
2105  #endif // POSIX
2106  DeleteChunkList();
2107  pFile = NULL;
2108  }
2109 
2117  file_offset_t size = 0;
2118  try {
2119  size = __GetFileSize(hFileRead);
2120  } catch (...) {
2121  size = 0;
2122  }
2123  return size;
2124  }
2125 
2146  return GetRequiredFileSize(FileOffsetPreference);
2147  }
2148 
2161  switch (fileOffsetSize) {
2162  case offset_size_auto: {
2164  if (fileSize >> 32)
2166  else
2167  return fileSize;
2168  }
2169  case offset_size_32bit: break;
2170  case offset_size_64bit: break;
2171  default: throw Exception("Internal error: Invalid RIFF::offset_size_t");
2172  }
2174  }
2175 
2176  int File::FileOffsetSizeFor(file_offset_t fileSize) const {
2177  switch (FileOffsetPreference) {
2178  case offset_size_auto:
2179  return (fileSize >> 32) ? 8 : 4;
2180  case offset_size_32bit:
2181  return 4;
2182  case offset_size_64bit:
2183  return 8;
2184  default:
2185  throw Exception("Internal error: Invalid RIFF::offset_size_t");
2186  }
2187  }
2188 
2210  return FileOffsetSize;
2211  }
2212 
2224  return FileOffsetSizeFor(GetCurrentFileSize());
2225  }
2226 
2227  #if POSIX
2228  file_offset_t File::__GetFileSize(int hFile) const {
2229  struct stat filestat;
2230  if (fstat(hFile, &filestat) == -1)
2231  throw Exception("POSIX FS error: could not determine file size");
2232  return filestat.st_size;
2233  }
2234  #elif defined(WIN32)
2235  file_offset_t File::__GetFileSize(HANDLE hFile) const {
2236  LARGE_INTEGER size;
2237  if (!GetFileSizeEx(hFile, &size))
2238  throw Exception("Windows FS error: could not determine file size");
2239  return size.QuadPart;
2240  }
2241  #else // standard C functions
2242  file_offset_t File::__GetFileSize(FILE* hFile) const {
2243  off_t curpos = ftello(hFile);
2244  if (fseeko(hFile, 0, SEEK_END) == -1)
2245  throw Exception("FS error: could not determine file size");
2246  off_t size = ftello(hFile);
2247  fseeko(hFile, curpos, SEEK_SET);
2248  return size;
2249  }
2250  #endif
2251 
2252 
2253 // *************** Exception ***************
2254 // *
2255 
2256  Exception::Exception() {
2257  }
2258 
2259  Exception::Exception(String format, ...) {
2260  va_list arg;
2261  va_start(arg, format);
2262  Message = assemble(format, arg);
2263  va_end(arg);
2264  }
2265 
2266  Exception::Exception(String format, va_list arg) {
2267  Message = assemble(format, arg);
2268  }
2269 
2270  void Exception::PrintMessage() {
2271  std::cout << "RIFF::Exception: " << Message << std::endl;
2272  }
2273 
2274  String Exception::assemble(String format, va_list arg) {
2275  char* buf = NULL;
2276  vasprintf(&buf, format.c_str(), arg);
2277  String s = buf;
2278  free(buf);
2279  return s;
2280  }
2281 
2282 
2283 // *************** functions ***************
2284 // *
2285 
2291  String libraryName() {
2292  return PACKAGE;
2293  }
2294 
2299  String libraryVersion() {
2300  return VERSION;
2301  }
2302 
2303 } // namespace RIFF
file_offset_t WriteUint32(uint32_t *pData, file_offset_t WordCount=1)
Writes WordCount number of 32 Bit unsigned integer words from the buffer pointed by pData to the chun...
Definition: RIFF.cpp:684
bool IsNew() const
Returns true if this file has been created new from scratch and has not been stored to disk yet.
Definition: RIFF.cpp:2094
int16_t ReadInt16()
Reads one 16 Bit signed integer word and increments the position within the chunk.
Definition: RIFF.cpp:728
virtual file_offset_t RequiredPhysicalSize(int fileOffsetSize)
Returns the actual total size in bytes (including List chunk header and all subchunks) of this List C...
Definition: RIFF.cpp:1367
int FileOffsetSize
Size of file offsets (in bytes) when this file was opened (or saved the last time).
Definition: RIFF.h:381
file_offset_t WriteUint16(uint16_t *pData, file_offset_t WordCount=1)
Writes WordCount number of 16 Bit unsigned integer words from the buffer pointed by pData to the chun...
Definition: RIFF.cpp:593
size_t CountSubLists()
Returns number of sublists within the list.
Definition: RIFF.cpp:1217
stream_whence_t
File stream position dependent to these relations.
Definition: RIFF.h:165
Chunk * GetFirstSubChunk()
Returns the first subchunk within the list (which may be an ordinary chunk as well as a list chunk).
Definition: RIFF.cpp:1116
String libraryName()
Returns the name of this C++ library.
Definition: RIFF.cpp:2291
File(uint32_t FileType)
Create new RIFF file.
Definition: RIFF.cpp:1558
file_offset_t WriteInt32(int32_t *pData, file_offset_t WordCount=1)
Writes WordCount number of 32 Bit signed integer words from the buffer pointed by pData to the chunk'...
Definition: RIFF.cpp:630
file_offset_t GetSize() const
Chunk size in bytes (without header, thus the chunk data body)
Definition: RIFF.h:225
layout_t Layout
An ordinary RIFF file is always set to layout_standard.
Definition: RIFF.h:379
void(* callback)(progress_t *)
Callback function pointer which has to be assigned to a function for progress notification.
Definition: RIFF.h:205
stream_state_t
Current state of the file stream.
Definition: RIFF.h:158
void ReadString(String &s, int size)
Reads a null-padded string of size characters and copies it into the string s.
Definition: RIFF.cpp:663
String libraryVersion()
Returns version of this C++ library.
Definition: RIFF.cpp:2299
List * GetSubList(uint32_t ListType)
Returns sublist chunk with list type ListType within this chunk list.
Definition: RIFF.cpp:1090
void DeleteSubChunk(Chunk *pSubChunk)
Removes a sub chunk.
Definition: RIFF.cpp:1342
Use 32 bit offsets for files smaller than 4 GB, use 64 bit offsets for files equal or larger than 4 G...
Definition: RIFF.h:187
int hFileWrite
handle / descriptor for writing to (some) file
Definition: RIFF.h:368
List * GetFirstSubList()
Returns the first sublist within the list (that is a subchunk with chunk ID "LIST").
Definition: RIFF.cpp:1151
virtual file_offset_t WriteChunk(file_offset_t ullWritePos, file_offset_t ullCurrentDataOffset, progress_t *pProgress=NULL)
Write chunk persistently e.g.
Definition: RIFF.cpp:902
stream_mode_t
Whether file stream is open in read or in read/write mode.
Definition: RIFF.h:151
String GetListTypeString() const
Returns string representation of the lists's id.
Definition: RIFF.cpp:1535
void SetByteOrder(endian_t Endian)
Set the byte order to be used when saving.
Definition: RIFF.cpp:1844
RIFF List Chunk.
Definition: RIFF.h:294
file_offset_t WriteInt16(int16_t *pData, file_offset_t WordCount=1)
Writes WordCount number of 16 Bit signed integer words from the buffer pointed by pData to the chunk'...
Definition: RIFF.cpp:556
file_offset_t ReadSceptical(void *pData, file_offset_t WordCount, file_offset_t WordSize)
Just an internal wrapper for the main Read() method with additional Exception throwing on errors.
Definition: RIFF.cpp:444
file_offset_t Read(void *pData, file_offset_t WordCount, file_offset_t WordSize)
Reads WordCount number of data words with given WordSize and copies it into a buffer pointed by pData...
Definition: RIFF.cpp:318
file_offset_t WriteInt8(int8_t *pData, file_offset_t WordCount=1)
Writes WordCount number of 8 Bit signed integer words from the buffer pointed by pData to the chunk's...
Definition: RIFF.cpp:482
file_offset_t SetPos(file_offset_t Where, stream_whence_t Whence=stream_start)
Sets the position within the chunk body, thus within the data portion of the chunk (in bytes).
Definition: RIFF.cpp:224
int8_t ReadInt8()
Reads one 8 Bit signed integer word and increments the position within the chunk.
Definition: RIFF.cpp:695
virtual file_offset_t WriteChunk(file_offset_t ullWritePos, file_offset_t ullCurrentDataOffset, progress_t *pProgress=NULL)
Write list chunk persistently e.g.
Definition: RIFF.cpp:1491
offset_size_t
Size of RIFF file offsets used in all RIFF chunks' headers.
Definition: RIFF.h:186
uint64_t file_offset_t
Type used by libgig for handling file positioning during file I/O tasks.
Definition: RIFF.h:148
float __range_min
Only for internal usage, do not modify!
Definition: RIFF.h:208
Chunk * GetSubChunk(uint32_t ChunkID)
Returns subchunk with chunk ID ChunkID within this chunk list.
Definition: RIFF.cpp:1071
Chunk * GetNextSubChunk()
Returns the next subchunk within the list (which may be an ordinary chunk as well as a list chunk).
Definition: RIFF.cpp:1133
int32_t ReadInt32()
Reads one 32 Bit signed integer word and increments the position within the chunk.
Definition: RIFF.cpp:762
virtual void Save(progress_t *pProgress=NULL)
Save changes to same file.
Definition: RIFF.cpp:1861
layout_t
General RIFF chunk structure of a RIFF file.
Definition: RIFF.h:180
virtual file_offset_t RequiredPhysicalSize(int fileOffsetSize)
Returns the actual total size in bytes (including header) of this Chunk if being stored to a file.
Definition: RIFF.cpp:270
Ordinary RIFF Chunk.
Definition: RIFF.h:218
file_offset_t GetRequiredFileSize()
Returns the required size (in bytes) for this RIFF File to be saved to disk.
Definition: RIFF.cpp:2145
uint32_t ReadUint32()
Reads one 32 Bit unsigned integer word and increments the position within the chunk.
Definition: RIFF.cpp:779
int GetFileOffsetSize() const
Returns the current size (in bytes) of file offsets stored in the headers of all chunks of this file.
Definition: RIFF.cpp:2209
uint32_t GetChunkID() const
Chunk ID in unsigned integer representation.
Definition: RIFF.h:222
void * custom
This pointer can be used for arbitrary data.
Definition: RIFF.h:207
file_offset_t RemainingBytes() const
Returns the number of bytes left to read in the chunk body.
Definition: RIFF.cpp:256
Used for indicating the progress of a certain task.
Definition: RIFF.h:204
file_offset_t Write(void *pData, file_offset_t WordCount, file_offset_t WordSize)
Writes WordCount number of data words with given WordSize from the buffer pointed by pData.
Definition: RIFF.cpp:388
int hFileRead
handle / descriptor for reading from file
Definition: RIFF.h:367
uint32_t GetListType() const
Returns unsigned integer representation of the list's ID.
Definition: RIFF.h:298
Not a "real" RIFF file: First chunk in file is an ordinary data chunk, not a List chunk,...
Definition: RIFF.h:182
virtual void __resetPos()
Sets Chunk's read/write position to zero.
Definition: RIFF.cpp:1001
uint16_t ReadUint16()
Reads one 16 Bit unsigned integer word and increments the position within the chunk.
Definition: RIFF.cpp:745
virtual void __resetPos()
Sets List Chunk's read/write position to zero and causes all sub chunks to do the same.
Definition: RIFF.cpp:1523
endian_t
Alignment of data bytes in memory (system dependant).
Definition: RIFF.h:173
void * LoadChunkData()
Load chunk body into RAM.
Definition: RIFF.cpp:809
file_offset_t WriteUint8(uint8_t *pData, file_offset_t WordCount=1)
Writes WordCount number of 8 Bit unsigned integer words from the buffer pointed by pData to the chunk...
Definition: RIFF.cpp:519
Standard RIFF file layout: First chunk in file is a List chunk which contains all other chunks and th...
Definition: RIFF.h:181
void Resize(file_offset_t NewSize)
Resize chunk.
Definition: RIFF.cpp:880
RIFF File.
Definition: RIFF.h:344
List * AddSubList(uint32_t uiListType)
Creates a new list sub chunk.
Definition: RIFF.cpp:1323
RIFF specific classes and definitions.
Definition: RIFF.h:138
void MoveSubChunk(Chunk *pSrc, Chunk *pDst)
Moves a sub chunk witin this list.
Definition: RIFF.cpp:1275
Always use 32 bit offsets (even for files larger than 4 GB).
Definition: RIFF.h:188
size_t CountSubChunks()
Returns number of subchunks within the list (including list chunks).
Definition: RIFF.cpp:1191
stream_state_t GetState() const
Returns the current state of the chunk object.
Definition: RIFF.cpp:287
bool SetMode(stream_mode_t NewMode)
Change file access mode.
Definition: RIFF.cpp:1737
float __range_max
Only for internal usage, do not modify!
Definition: RIFF.h:209
String GetChunkIDString() const
Returns the String representation of the chunk's ID (e.g.
Definition: RIFF.cpp:208
int GetRequiredFileOffsetSize()
Returns the required size (in bytes) of file offsets stored in the headers of all chunks of this file...
Definition: RIFF.cpp:2223
Will be thrown whenever an error occurs while handling a RIFF file.
Definition: RIFF.h:404
Always use 64 bit offsets (even for files smaller than 4 GB).
Definition: RIFF.h:189
void ReleaseChunkData()
Free loaded chunk body from RAM.
Definition: RIFF.cpp:855
file_offset_t GetCurrentFileSize() const
Returns the current size of this file (in bytes) as it is currently yet stored on disk.
Definition: RIFF.cpp:2116
file_offset_t GetPos() const
Position within the chunk data body (starting with 0).
Definition: RIFF.h:227
List * GetNextSubList()
Returns the next sublist (that is a subchunk with chunk ID "LIST") within the list.
Definition: RIFF.cpp:1173
Chunk * AddSubChunk(uint32_t uiChunkID, file_offset_t ullBodySize)
Creates a new sub chunk.
Definition: RIFF.cpp:1253
uint8_t ReadUint8()
Reads one 8 Bit unsigned integer word and increments the position within the chunk.
Definition: RIFF.cpp:711