libgig  3.3.0.svn4
RIFF.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * *
3  * libgig - C++ cross-platform Gigasampler format file access library *
4  * *
5  * Copyright (C) 2003-2013 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 namespace RIFF {
33 
34 // *************** Internal functions **************
35 // *
36 
38  static String __resolveChunkPath(Chunk* pCk) {
39  String sPath;
40  for (Chunk* pChunk = pCk; pChunk; pChunk = pChunk->GetParent()) {
41  if (pChunk->GetChunkID() == CHUNK_ID_LIST) {
42  List* pList = (List*) pChunk;
43  sPath = "->'" + pList->GetListTypeString() + "'" + sPath;
44  } else {
45  sPath = "->'" + pChunk->GetChunkIDString() + "'" + sPath;
46  }
47  }
48  return sPath;
49  }
50 
51 
52 
53 // *************** Chunk **************
54 // *
55 
56  Chunk::Chunk(File* pFile) {
57  #if DEBUG
58  std::cout << "Chunk::Chunk(File* pFile)" << std::endl;
59  #endif // DEBUG
60  ulPos = 0;
61  pParent = NULL;
62  pChunkData = NULL;
63  CurrentChunkSize = 0;
64  NewChunkSize = 0;
65  ulChunkDataSize = 0;
67  this->pFile = pFile;
68  }
69 
70  Chunk::Chunk(File* pFile, unsigned long StartPos, List* Parent) {
71  #if DEBUG
72  std::cout << "Chunk::Chunk(File*,ulong,bool,List*),StartPos=" << StartPos << std::endl;
73  #endif // DEBUG
74  this->pFile = pFile;
75  ulStartPos = StartPos + CHUNK_HEADER_SIZE;
76  pParent = Parent;
77  ulPos = 0;
78  pChunkData = NULL;
79  CurrentChunkSize = 0;
80  NewChunkSize = 0;
81  ulChunkDataSize = 0;
82  ReadHeader(StartPos);
83  }
84 
85  Chunk::Chunk(File* pFile, List* pParent, uint32_t uiChunkID, uint uiBodySize) {
86  this->pFile = pFile;
87  ulStartPos = 0; // arbitrary usually, since it will be updated when we write the chunk
88  this->pParent = pParent;
89  ulPos = 0;
90  pChunkData = NULL;
91  ChunkID = uiChunkID;
92  ulChunkDataSize = 0;
93  CurrentChunkSize = 0;
94  NewChunkSize = uiBodySize;
95  }
96 
98  if (pFile) pFile->UnlogResized(this);
99  if (pChunkData) delete[] pChunkData;
100  }
101 
102  void Chunk::ReadHeader(unsigned long fPos) {
103  #if DEBUG
104  std::cout << "Chunk::Readheader(" << fPos << ") ";
105  #endif // DEBUG
106  ChunkID = 0;
108  #if POSIX
109  if (lseek(pFile->hFileRead, fPos, SEEK_SET) != -1) {
110  read(pFile->hFileRead, &ChunkID, 4);
111  read(pFile->hFileRead, &CurrentChunkSize, 4);
112  #elif defined(WIN32)
113  if (SetFilePointer(pFile->hFileRead, fPos, NULL/*32 bit*/, FILE_BEGIN) != INVALID_SET_FILE_POINTER) {
114  DWORD dwBytesRead;
115  ReadFile(pFile->hFileRead, &ChunkID, 4, &dwBytesRead, NULL);
116  ReadFile(pFile->hFileRead, &CurrentChunkSize, 4, &dwBytesRead, NULL);
117  #else
118  if (!fseek(pFile->hFileRead, fPos, SEEK_SET)) {
119  fread(&ChunkID, 4, 1, pFile->hFileRead);
120  fread(&CurrentChunkSize, 4, 1, pFile->hFileRead);
121  #endif // POSIX
122  #if WORDS_BIGENDIAN
123  if (ChunkID == CHUNK_ID_RIFF) {
124  pFile->bEndianNative = false;
125  }
126  #else // little endian
127  if (ChunkID == CHUNK_ID_RIFX) {
128  pFile->bEndianNative = false;
130  }
131  #endif // WORDS_BIGENDIAN
132  if (!pFile->bEndianNative) {
133  //swapBytes_32(&ChunkID);
135  }
136  #if DEBUG
137  std::cout << "ckID=" << convertToString(ChunkID) << " ";
138  std::cout << "ckSize=" << CurrentChunkSize << " ";
139  std::cout << "bEndianNative=" << pFile->bEndianNative << std::endl;
140  #endif // DEBUG
142  }
143  }
144 
145  void Chunk::WriteHeader(unsigned long fPos) {
146  uint32_t uiNewChunkID = ChunkID;
147  if (ChunkID == CHUNK_ID_RIFF) {
148  #if WORDS_BIGENDIAN
149  if (pFile->bEndianNative) uiNewChunkID = CHUNK_ID_RIFX;
150  #else // little endian
151  if (!pFile->bEndianNative) uiNewChunkID = CHUNK_ID_RIFX;
152  #endif // WORDS_BIGENDIAN
153  }
154 
155  uint32_t uiNewChunkSize = NewChunkSize;
156  if (!pFile->bEndianNative) {
157  swapBytes_32(&uiNewChunkSize);
158  }
159 
160  #if POSIX
161  if (lseek(pFile->hFileWrite, fPos, SEEK_SET) != -1) {
162  write(pFile->hFileWrite, &uiNewChunkID, 4);
163  write(pFile->hFileWrite, &uiNewChunkSize, 4);
164  }
165  #elif defined(WIN32)
166  if (SetFilePointer(pFile->hFileWrite, fPos, NULL/*32 bit*/, FILE_BEGIN) != INVALID_SET_FILE_POINTER) {
167  DWORD dwBytesWritten;
168  WriteFile(pFile->hFileWrite, &uiNewChunkID, 4, &dwBytesWritten, NULL);
169  WriteFile(pFile->hFileWrite, &uiNewChunkSize, 4, &dwBytesWritten, NULL);
170  }
171  #else
172  if (!fseek(pFile->hFileWrite, fPos, SEEK_SET)) {
173  fwrite(&uiNewChunkID, 4, 1, pFile->hFileWrite);
174  fwrite(&uiNewChunkSize, 4, 1, pFile->hFileWrite);
175  }
176  #endif // POSIX
177  }
178 
184  return convertToString(ChunkID);
185  }
186 
199  unsigned long Chunk::SetPos(unsigned long Where, stream_whence_t Whence) {
200  #if DEBUG
201  std::cout << "Chunk::SetPos(ulong)" << std::endl;
202  #endif // DEBUG
203  switch (Whence) {
204  case stream_curpos:
205  ulPos += Where;
206  break;
207  case stream_end:
208  ulPos = CurrentChunkSize - 1 - Where;
209  break;
210  case stream_backward:
211  ulPos -= Where;
212  break;
213  case stream_start: default:
214  ulPos = Where;
215  break;
216  }
218  return ulPos;
219  }
220 
231  unsigned long Chunk::RemainingBytes() {
232  #if DEBUG
233  std::cout << "Chunk::Remainingbytes()=" << CurrentChunkSize - ulPos << std::endl;
234  #endif // DEBUG
235  return (CurrentChunkSize > ulPos) ? CurrentChunkSize - ulPos : 0;
236  }
237 
250  #if DEBUG
251  std::cout << "Chunk::GetState()" << std::endl;
252  #endif // DEBUG
253  #if POSIX
254  if (pFile->hFileRead == 0) return stream_closed;
255  #elif defined (WIN32)
256  if (pFile->hFileRead == INVALID_HANDLE_VALUE)
257  return stream_closed;
258  #else
259  if (pFile->hFileRead == NULL) return stream_closed;
260  #endif // POSIX
261  if (ulPos < CurrentChunkSize) return stream_ready;
262  else return stream_end_reached;
263  }
264 
280  unsigned long Chunk::Read(void* pData, unsigned long WordCount, unsigned long WordSize) {
281  #if DEBUG
282  std::cout << "Chunk::Read(void*,ulong,ulong)" << std::endl;
283  #endif // DEBUG
284  if (ulStartPos == 0) return 0; // is only 0 if this is a new chunk, so nothing to read (yet)
285  if (ulPos >= CurrentChunkSize) return 0;
286  if (ulPos + WordCount * WordSize >= CurrentChunkSize) WordCount = (CurrentChunkSize - ulPos) / WordSize;
287  #if POSIX
288  if (lseek(pFile->hFileRead, ulStartPos + ulPos, SEEK_SET) < 0) return 0;
289  unsigned long readWords = read(pFile->hFileRead, pData, WordCount * WordSize);
290  if (readWords < 1) return 0;
291  readWords /= WordSize;
292  #elif defined(WIN32)
293  if (SetFilePointer(pFile->hFileRead, ulStartPos + ulPos, NULL/*32 bit*/, FILE_BEGIN) == INVALID_SET_FILE_POINTER) return 0;
294  DWORD readWords;
295  ReadFile(pFile->hFileRead, pData, WordCount * WordSize, &readWords, NULL);
296  if (readWords < 1) return 0;
297  readWords /= WordSize;
298  #else // standard C functions
299  if (fseek(pFile->hFileRead, ulStartPos + ulPos, SEEK_SET)) return 0;
300  unsigned long readWords = fread(pData, WordSize, WordCount, pFile->hFileRead);
301  #endif // POSIX
302  if (!pFile->bEndianNative && WordSize != 1) {
303  switch (WordSize) {
304  case 2:
305  for (unsigned long iWord = 0; iWord < readWords; iWord++)
306  swapBytes_16((uint16_t*) pData + iWord);
307  break;
308  case 4:
309  for (unsigned long iWord = 0; iWord < readWords; iWord++)
310  swapBytes_32((uint32_t*) pData + iWord);
311  break;
312  default:
313  for (unsigned long iWord = 0; iWord < readWords; iWord++)
314  swapBytes((uint8_t*) pData + iWord * WordSize, WordSize);
315  break;
316  }
317  }
318  SetPos(readWords * WordSize, stream_curpos);
319  return readWords;
320  }
321 
338  unsigned long Chunk::Write(void* pData, unsigned long WordCount, unsigned long WordSize) {
339  if (pFile->Mode != stream_mode_read_write)
340  throw Exception("Cannot write data to chunk, file has to be opened in read+write mode first");
341  if (ulPos >= CurrentChunkSize || ulPos + WordCount * WordSize > CurrentChunkSize)
342  throw Exception("End of chunk reached while trying to write data");
343  if (!pFile->bEndianNative && WordSize != 1) {
344  switch (WordSize) {
345  case 2:
346  for (unsigned long iWord = 0; iWord < WordCount; iWord++)
347  swapBytes_16((uint16_t*) pData + iWord);
348  break;
349  case 4:
350  for (unsigned long iWord = 0; iWord < WordCount; iWord++)
351  swapBytes_32((uint32_t*) pData + iWord);
352  break;
353  default:
354  for (unsigned long iWord = 0; iWord < WordCount; iWord++)
355  swapBytes((uint8_t*) pData + iWord * WordSize, WordSize);
356  break;
357  }
358  }
359  #if POSIX
360  if (lseek(pFile->hFileWrite, ulStartPos + ulPos, SEEK_SET) < 0) {
361  throw Exception("Could not seek to position " + ToString(ulPos) +
362  " in chunk (" + ToString(ulStartPos + ulPos) + " in file)");
363  }
364  unsigned long writtenWords = write(pFile->hFileWrite, pData, WordCount * WordSize);
365  if (writtenWords < 1) throw Exception("POSIX IO Error while trying to write chunk data");
366  writtenWords /= WordSize;
367  #elif defined(WIN32)
368  if (SetFilePointer(pFile->hFileWrite, ulStartPos + ulPos, NULL/*32 bit*/, FILE_BEGIN) == INVALID_SET_FILE_POINTER) {
369  throw Exception("Could not seek to position " + ToString(ulPos) +
370  " in chunk (" + ToString(ulStartPos + ulPos) + " in file)");
371  }
372  DWORD writtenWords;
373  WriteFile(pFile->hFileWrite, pData, WordCount * WordSize, &writtenWords, NULL);
374  if (writtenWords < 1) throw Exception("Windows IO Error while trying to write chunk data");
375  writtenWords /= WordSize;
376  #else // standard C functions
377  if (fseek(pFile->hFileWrite, ulStartPos + ulPos, SEEK_SET)) {
378  throw Exception("Could not seek to position " + ToString(ulPos) +
379  " in chunk (" + ToString(ulStartPos + ulPos) + " in file)");
380  }
381  unsigned long writtenWords = fwrite(pData, WordSize, WordCount, pFile->hFileWrite);
382  #endif // POSIX
383  SetPos(writtenWords * WordSize, stream_curpos);
384  return writtenWords;
385  }
386 
388  unsigned long Chunk::ReadSceptical(void* pData, unsigned long WordCount, unsigned long WordSize) {
389  unsigned long readWords = Read(pData, WordCount, WordSize);
390  if (readWords != WordCount) throw RIFF::Exception("End of chunk data reached.");
391  return readWords;
392  }
393 
405  unsigned long Chunk::ReadInt8(int8_t* pData, unsigned long WordCount) {
406  #if DEBUG
407  std::cout << "Chunk::ReadInt8(int8_t*,ulong)" << std::endl;
408  #endif // DEBUG
409  return ReadSceptical(pData, WordCount, 1);
410  }
411 
426  unsigned long Chunk::WriteInt8(int8_t* pData, unsigned long WordCount) {
427  return Write(pData, WordCount, 1);
428  }
429 
442  unsigned long Chunk::ReadUint8(uint8_t* pData, unsigned long WordCount) {
443  #if DEBUG
444  std::cout << "Chunk::ReadUint8(uint8_t*,ulong)" << std::endl;
445  #endif // DEBUG
446  return ReadSceptical(pData, WordCount, 1);
447  }
448 
463  unsigned long Chunk::WriteUint8(uint8_t* pData, unsigned long WordCount) {
464  return Write(pData, WordCount, 1);
465  }
466 
479  unsigned long Chunk::ReadInt16(int16_t* pData, unsigned long WordCount) {
480  #if DEBUG
481  std::cout << "Chunk::ReadInt16(int16_t*,ulong)" << std::endl;
482  #endif // DEBUG
483  return ReadSceptical(pData, WordCount, 2);
484  }
485 
500  unsigned long Chunk::WriteInt16(int16_t* pData, unsigned long WordCount) {
501  return Write(pData, WordCount, 2);
502  }
503 
516  unsigned long Chunk::ReadUint16(uint16_t* pData, unsigned long WordCount) {
517  #if DEBUG
518  std::cout << "Chunk::ReadUint16(uint16_t*,ulong)" << std::endl;
519  #endif // DEBUG
520  return ReadSceptical(pData, WordCount, 2);
521  }
522 
537  unsigned long Chunk::WriteUint16(uint16_t* pData, unsigned long WordCount) {
538  return Write(pData, WordCount, 2);
539  }
540 
553  unsigned long Chunk::ReadInt32(int32_t* pData, unsigned long WordCount) {
554  #if DEBUG
555  std::cout << "Chunk::ReadInt32(int32_t*,ulong)" << std::endl;
556  #endif // DEBUG
557  return ReadSceptical(pData, WordCount, 4);
558  }
559 
574  unsigned long Chunk::WriteInt32(int32_t* pData, unsigned long WordCount) {
575  return Write(pData, WordCount, 4);
576  }
577 
590  unsigned long Chunk::ReadUint32(uint32_t* pData, unsigned long WordCount) {
591  #if DEBUG
592  std::cout << "Chunk::ReadUint32(uint32_t*,ulong)" << std::endl;
593  #endif // DEBUG
594  return ReadSceptical(pData, WordCount, 4);
595  }
596 
607  void Chunk::ReadString(String& s, int size) {
608  char* buf = new char[size];
609  ReadSceptical(buf, 1, size);
610  s.assign(buf, std::find(buf, buf + size, '\0'));
611  delete[] buf;
612  }
613 
628  unsigned long Chunk::WriteUint32(uint32_t* pData, unsigned long WordCount) {
629  return Write(pData, WordCount, 4);
630  }
631 
639  int8_t Chunk::ReadInt8() {
640  #if DEBUG
641  std::cout << "Chunk::ReadInt8()" << std::endl;
642  #endif // DEBUG
643  int8_t word;
644  ReadSceptical(&word,1,1);
645  return word;
646  }
647 
655  uint8_t Chunk::ReadUint8() {
656  #if DEBUG
657  std::cout << "Chunk::ReadUint8()" << std::endl;
658  #endif // DEBUG
659  uint8_t word;
660  ReadSceptical(&word,1,1);
661  return word;
662  }
663 
672  int16_t Chunk::ReadInt16() {
673  #if DEBUG
674  std::cout << "Chunk::ReadInt16()" << std::endl;
675  #endif // DEBUG
676  int16_t word;
677  ReadSceptical(&word,1,2);
678  return word;
679  }
680 
689  uint16_t Chunk::ReadUint16() {
690  #if DEBUG
691  std::cout << "Chunk::ReadUint16()" << std::endl;
692  #endif // DEBUG
693  uint16_t word;
694  ReadSceptical(&word,1,2);
695  return word;
696  }
697 
706  int32_t Chunk::ReadInt32() {
707  #if DEBUG
708  std::cout << "Chunk::ReadInt32()" << std::endl;
709  #endif // DEBUG
710  int32_t word;
711  ReadSceptical(&word,1,4);
712  return word;
713  }
714 
723  uint32_t Chunk::ReadUint32() {
724  #if DEBUG
725  std::cout << "Chunk::ReadUint32()" << std::endl;
726  #endif // DEBUG
727  uint32_t word;
728  ReadSceptical(&word,1,4);
729  return word;
730  }
731 
754  if (!pChunkData && pFile->Filename != "" && ulStartPos != 0) {
755  #if POSIX
756  if (lseek(pFile->hFileRead, ulStartPos, SEEK_SET) == -1) return NULL;
757  #elif defined(WIN32)
758  if (SetFilePointer(pFile->hFileRead, ulStartPos, NULL/*32 bit*/, FILE_BEGIN) == INVALID_SET_FILE_POINTER) return NULL;
759  #else
760  if (fseek(pFile->hFileRead, ulStartPos, SEEK_SET)) return NULL;
761  #endif // POSIX
762  unsigned long ulBufferSize = (CurrentChunkSize > NewChunkSize) ? CurrentChunkSize : NewChunkSize;
763  pChunkData = new uint8_t[ulBufferSize];
764  if (!pChunkData) return NULL;
765  memset(pChunkData, 0, ulBufferSize);
766  #if POSIX
767  unsigned long readWords = read(pFile->hFileRead, pChunkData, GetSize());
768  #elif defined(WIN32)
769  DWORD readWords;
770  ReadFile(pFile->hFileRead, pChunkData, GetSize(), &readWords, NULL);
771  #else
772  unsigned long readWords = fread(pChunkData, 1, GetSize(), pFile->hFileRead);
773  #endif // POSIX
774  if (readWords != GetSize()) {
775  delete[] pChunkData;
776  return (pChunkData = NULL);
777  }
778  ulChunkDataSize = ulBufferSize;
779  } else if (NewChunkSize > ulChunkDataSize) {
780  uint8_t* pNewBuffer = new uint8_t[NewChunkSize];
781  if (!pNewBuffer) throw Exception("Could not enlarge chunk data buffer to " + ToString(NewChunkSize) + " bytes");
782  memset(pNewBuffer, 0 , NewChunkSize);
783  memcpy(pNewBuffer, pChunkData, ulChunkDataSize);
784  delete[] pChunkData;
785  pChunkData = pNewBuffer;
787  }
788  return pChunkData;
789  }
790 
798  if (pChunkData) {
799  delete[] pChunkData;
800  pChunkData = NULL;
801  }
802  }
803 
822  void Chunk::Resize(int iNewSize) {
823  if (iNewSize <= 0)
824  throw Exception("There is at least one empty chunk (zero size): " + __resolveChunkPath(this));
825  if (NewChunkSize == iNewSize) return;
826  NewChunkSize = iNewSize;
827  pFile->LogAsResized(this);
828  }
829 
842  unsigned long Chunk::WriteChunk(unsigned long ulWritePos, unsigned long ulCurrentDataOffset) {
843  const unsigned long ulOriginalPos = ulWritePos;
844  ulWritePos += CHUNK_HEADER_SIZE;
845 
846  if (pFile->Mode != stream_mode_read_write)
847  throw Exception("Cannot write list chunk, file has to be opened in read+write mode");
848 
849  // if the whole chunk body was loaded into RAM
850  if (pChunkData) {
851  // make sure chunk data buffer in RAM is at least as large as the new chunk size
852  LoadChunkData();
853  // write chunk data from RAM persistently to the file
854  #if POSIX
855  lseek(pFile->hFileWrite, ulWritePos, SEEK_SET);
857  throw Exception("Writing Chunk data (from RAM) failed");
858  }
859  #elif defined(WIN32)
860  SetFilePointer(pFile->hFileWrite, ulWritePos, NULL/*32 bit*/, FILE_BEGIN);
861  DWORD dwBytesWritten;
862  WriteFile(pFile->hFileWrite, pChunkData, NewChunkSize, &dwBytesWritten, NULL);
863  if (dwBytesWritten != NewChunkSize) {
864  throw Exception("Writing Chunk data (from RAM) failed");
865  }
866  #else
867  fseek(pFile->hFileWrite, ulWritePos, SEEK_SET);
868  if (fwrite(pChunkData, 1, NewChunkSize, pFile->hFileWrite) != NewChunkSize) {
869  throw Exception("Writing Chunk data (from RAM) failed");
870  }
871  #endif // POSIX
872  } else {
873  // move chunk data from the end of the file to the appropriate position
874  int8_t* pCopyBuffer = new int8_t[4096];
875  unsigned long ulToMove = (NewChunkSize < CurrentChunkSize) ? NewChunkSize : CurrentChunkSize;
876  #if defined(WIN32)
877  DWORD iBytesMoved = 1; // we have to pass it via pointer to the Windows API, thus the correct size must be ensured
878  #else
879  int iBytesMoved = 1;
880  #endif
881  for (unsigned long ulOffset = 0; ulToMove > 0 && iBytesMoved > 0; ulOffset += iBytesMoved, ulToMove -= iBytesMoved) {
882  iBytesMoved = (ulToMove < 4096) ? ulToMove : 4096;
883  #if POSIX
884  lseek(pFile->hFileRead, ulStartPos + ulCurrentDataOffset + ulOffset, SEEK_SET);
885  iBytesMoved = read(pFile->hFileRead, pCopyBuffer, iBytesMoved);
886  lseek(pFile->hFileWrite, ulWritePos + ulOffset, SEEK_SET);
887  iBytesMoved = write(pFile->hFileWrite, pCopyBuffer, iBytesMoved);
888  #elif defined(WIN32)
889  SetFilePointer(pFile->hFileRead, ulStartPos + ulCurrentDataOffset + ulOffset, NULL/*32 bit*/, FILE_BEGIN);
890  ReadFile(pFile->hFileRead, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
891  SetFilePointer(pFile->hFileWrite, ulWritePos + ulOffset, NULL/*32 bit*/, FILE_BEGIN);
892  WriteFile(pFile->hFileWrite, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
893  #else
894  fseek(pFile->hFileRead, ulStartPos + ulCurrentDataOffset + ulOffset, SEEK_SET);
895  iBytesMoved = fread(pCopyBuffer, 1, iBytesMoved, pFile->hFileRead);
896  fseek(pFile->hFileWrite, ulWritePos + ulOffset, SEEK_SET);
897  iBytesMoved = fwrite(pCopyBuffer, 1, iBytesMoved, pFile->hFileWrite);
898  #endif
899  }
900  delete[] pCopyBuffer;
901  if (iBytesMoved < 0) throw Exception("Writing Chunk data (from file) failed");
902  }
903 
904  // update this chunk's header
906  WriteHeader(ulOriginalPos);
907 
908  // update chunk's position pointers
909  ulStartPos = ulOriginalPos + CHUNK_HEADER_SIZE;
910  ulPos = 0;
911 
912  // add pad byte if needed
913  if ((ulStartPos + NewChunkSize) % 2 != 0) {
914  const char cPadByte = 0;
915  #if POSIX
916  lseek(pFile->hFileWrite, ulStartPos + NewChunkSize, SEEK_SET);
917  write(pFile->hFileWrite, &cPadByte, 1);
918  #elif defined(WIN32)
919  SetFilePointer(pFile->hFileWrite, ulStartPos + NewChunkSize, NULL/*32 bit*/, FILE_BEGIN);
920  DWORD dwBytesWritten;
921  WriteFile(pFile->hFileWrite, &cPadByte, 1, &dwBytesWritten, NULL);
922  #else
923  fseek(pFile->hFileWrite, ulStartPos + NewChunkSize, SEEK_SET);
924  fwrite(&cPadByte, 1, 1, pFile->hFileWrite);
925  #endif
926  return ulStartPos + NewChunkSize + 1;
927  }
928 
929  return ulStartPos + NewChunkSize;
930  }
931 
933  ulPos = 0;
934  }
935 
936 
937 
938 // *************** List ***************
939 // *
940 
941  List::List(File* pFile) : Chunk(pFile) {
942  #if DEBUG
943  std::cout << "List::List(File* pFile)" << std::endl;
944  #endif // DEBUG
945  pSubChunks = NULL;
946  pSubChunksMap = NULL;
947  }
948 
949  List::List(File* pFile, unsigned long StartPos, List* Parent)
950  : Chunk(pFile, StartPos, Parent) {
951  #if DEBUG
952  std::cout << "List::List(File*,ulong,bool,List*)" << std::endl;
953  #endif // DEBUG
954  pSubChunks = NULL;
955  pSubChunksMap = NULL;
956  ReadHeader(StartPos);
957  ulStartPos = StartPos + LIST_HEADER_SIZE;
958  }
959 
960  List::List(File* pFile, List* pParent, uint32_t uiListID)
961  : Chunk(pFile, pParent, CHUNK_ID_LIST, 0) {
962  pSubChunks = NULL;
963  pSubChunksMap = NULL;
964  ListType = uiListID;
965  }
966 
968  #if DEBUG
969  std::cout << "List::~List()" << std::endl;
970  #endif // DEBUG
971  DeleteChunkList();
972  }
973 
975  if (pSubChunks) {
976  ChunkList::iterator iter = pSubChunks->begin();
977  ChunkList::iterator end = pSubChunks->end();
978  while (iter != end) {
979  delete *iter;
980  iter++;
981  }
982  delete pSubChunks;
983  pSubChunks = NULL;
984  }
985  if (pSubChunksMap) {
986  delete pSubChunksMap;
987  pSubChunksMap = NULL;
988  }
989  }
990 
1002  Chunk* List::GetSubChunk(uint32_t ChunkID) {
1003  #if DEBUG
1004  std::cout << "List::GetSubChunk(uint32_t)" << std::endl;
1005  #endif // DEBUG
1006  if (!pSubChunksMap) LoadSubChunks();
1007  return (*pSubChunksMap)[ChunkID];
1008  }
1009 
1021  List* List::GetSubList(uint32_t ListType) {
1022  #if DEBUG
1023  std::cout << "List::GetSubList(uint32_t)" << std::endl;
1024  #endif // DEBUG
1025  if (!pSubChunks) LoadSubChunks();
1026  ChunkList::iterator iter = pSubChunks->begin();
1027  ChunkList::iterator end = pSubChunks->end();
1028  while (iter != end) {
1029  if ((*iter)->GetChunkID() == CHUNK_ID_LIST) {
1030  List* l = (List*) *iter;
1031  if (l->GetListType() == ListType) return l;
1032  }
1033  iter++;
1034  }
1035  return NULL;
1036  }
1037 
1047  #if DEBUG
1048  std::cout << "List::GetFirstSubChunk()" << std::endl;
1049  #endif // DEBUG
1050  if (!pSubChunks) LoadSubChunks();
1051  ChunksIterator = pSubChunks->begin();
1052  return (ChunksIterator != pSubChunks->end()) ? *ChunksIterator : NULL;
1053  }
1054 
1063  #if DEBUG
1064  std::cout << "List::GetNextSubChunk()" << std::endl;
1065  #endif // DEBUG
1066  if (!pSubChunks) return NULL;
1067  ChunksIterator++;
1068  return (ChunksIterator != pSubChunks->end()) ? *ChunksIterator : NULL;
1069  }
1070 
1081  #if DEBUG
1082  std::cout << "List::GetFirstSubList()" << std::endl;
1083  #endif // DEBUG
1084  if (!pSubChunks) LoadSubChunks();
1085  ListIterator = pSubChunks->begin();
1086  ChunkList::iterator end = pSubChunks->end();
1087  while (ListIterator != end) {
1088  if ((*ListIterator)->GetChunkID() == CHUNK_ID_LIST) return (List*) *ListIterator;
1089  ListIterator++;
1090  }
1091  return NULL;
1092  }
1093 
1103  #if DEBUG
1104  std::cout << "List::GetNextSubList()" << std::endl;
1105  #endif // DEBUG
1106  if (!pSubChunks) return NULL;
1107  if (ListIterator == pSubChunks->end()) return NULL;
1108  ListIterator++;
1109  ChunkList::iterator end = pSubChunks->end();
1110  while (ListIterator != end) {
1111  if ((*ListIterator)->GetChunkID() == CHUNK_ID_LIST) return (List*) *ListIterator;
1112  ListIterator++;
1113  }
1114  return NULL;
1115  }
1116 
1120  unsigned int List::CountSubChunks() {
1121  if (!pSubChunks) LoadSubChunks();
1122  return pSubChunks->size();
1123  }
1124 
1129  unsigned int List::CountSubChunks(uint32_t ChunkID) {
1130  unsigned int result = 0;
1131  if (!pSubChunks) LoadSubChunks();
1132  ChunkList::iterator iter = pSubChunks->begin();
1133  ChunkList::iterator end = pSubChunks->end();
1134  while (iter != end) {
1135  if ((*iter)->GetChunkID() == ChunkID) {
1136  result++;
1137  }
1138  iter++;
1139  }
1140  return result;
1141  }
1142 
1146  unsigned int List::CountSubLists() {
1147  return CountSubChunks(CHUNK_ID_LIST);
1148  }
1149 
1154  unsigned int List::CountSubLists(uint32_t ListType) {
1155  unsigned int result = 0;
1156  if (!pSubChunks) LoadSubChunks();
1157  ChunkList::iterator iter = pSubChunks->begin();
1158  ChunkList::iterator end = pSubChunks->end();
1159  while (iter != end) {
1160  if ((*iter)->GetChunkID() == CHUNK_ID_LIST) {
1161  List* l = (List*) *iter;
1162  if (l->GetListType() == ListType) result++;
1163  }
1164  iter++;
1165  }
1166  return result;
1167  }
1168 
1182  Chunk* List::AddSubChunk(uint32_t uiChunkID, uint uiBodySize) {
1183  if (uiBodySize == 0) throw Exception("Chunk body size must be at least 1 byte");
1184  if (!pSubChunks) LoadSubChunks();
1185  Chunk* pNewChunk = new Chunk(pFile, this, uiChunkID, 0);
1186  pSubChunks->push_back(pNewChunk);
1187  (*pSubChunksMap)[uiChunkID] = pNewChunk;
1188  pNewChunk->Resize(uiBodySize);
1190  pFile->LogAsResized(this);
1191  return pNewChunk;
1192  }
1193 
1205  void List::MoveSubChunk(Chunk* pSrc, Chunk* pDst) {
1206  if (!pSubChunks) LoadSubChunks();
1207  pSubChunks->remove(pSrc);
1208  ChunkList::iterator iter = find(pSubChunks->begin(), pSubChunks->end(), pDst);
1209  pSubChunks->insert(iter, pSrc);
1210  }
1211 
1221  List* List::AddSubList(uint32_t uiListType) {
1222  if (!pSubChunks) LoadSubChunks();
1223  List* pNewListChunk = new List(pFile, this, uiListType);
1224  pSubChunks->push_back(pNewListChunk);
1225  (*pSubChunksMap)[CHUNK_ID_LIST] = pNewListChunk;
1227  pFile->LogAsResized(this);
1228  return pNewListChunk;
1229  }
1230 
1241  void List::DeleteSubChunk(Chunk* pSubChunk) {
1242  if (!pSubChunks) LoadSubChunks();
1243  pSubChunks->remove(pSubChunk);
1244  if ((*pSubChunksMap)[pSubChunk->GetChunkID()] == pSubChunk) {
1245  pSubChunksMap->erase(pSubChunk->GetChunkID());
1246  // try to find another chunk of the same chunk ID
1247  ChunkList::iterator iter = pSubChunks->begin();
1248  ChunkList::iterator end = pSubChunks->end();
1249  for (; iter != end; ++iter) {
1250  if ((*iter)->GetChunkID() == pSubChunk->GetChunkID()) {
1251  (*pSubChunksMap)[pSubChunk->GetChunkID()] = *iter;
1252  break; // we're done, stop search
1253  }
1254  }
1255  }
1256  delete pSubChunk;
1257  }
1258 
1259  void List::ReadHeader(unsigned long fPos) {
1260  #if DEBUG
1261  std::cout << "List::Readheader(ulong) ";
1262  #endif // DEBUG
1263  Chunk::ReadHeader(fPos);
1264  if (CurrentChunkSize < 4) return;
1266  #if POSIX
1267  lseek(pFile->hFileRead, fPos + CHUNK_HEADER_SIZE, SEEK_SET);
1268  read(pFile->hFileRead, &ListType, 4);
1269  #elif defined(WIN32)
1270  SetFilePointer(pFile->hFileRead, fPos + CHUNK_HEADER_SIZE, NULL/*32 bit*/, FILE_BEGIN);
1271  DWORD dwBytesRead;
1272  ReadFile(pFile->hFileRead, &ListType, 4, &dwBytesRead, NULL);
1273  #else
1274  fseek(pFile->hFileRead, fPos + CHUNK_HEADER_SIZE, SEEK_SET);
1275  fread(&ListType, 4, 1, pFile->hFileRead);
1276  #endif // POSIX
1277  #if DEBUG
1278  std::cout << "listType=" << convertToString(ListType) << std::endl;
1279  #endif // DEBUG
1280  if (!pFile->bEndianNative) {
1281  //swapBytes_32(&ListType);
1282  }
1283  }
1284 
1285  void List::WriteHeader(unsigned long fPos) {
1286  // the four list type bytes officially belong the chunk's body in the RIFF format
1287  NewChunkSize += 4;
1288  Chunk::WriteHeader(fPos);
1289  NewChunkSize -= 4; // just revert the +4 incrementation
1290  #if POSIX
1291  lseek(pFile->hFileWrite, fPos + CHUNK_HEADER_SIZE, SEEK_SET);
1292  write(pFile->hFileWrite, &ListType, 4);
1293  #elif defined(WIN32)
1294  SetFilePointer(pFile->hFileWrite, fPos + CHUNK_HEADER_SIZE, NULL/*32 bit*/, FILE_BEGIN);
1295  DWORD dwBytesWritten;
1296  WriteFile(pFile->hFileWrite, &ListType, 4, &dwBytesWritten, NULL);
1297  #else
1298  fseek(pFile->hFileWrite, fPos + CHUNK_HEADER_SIZE, SEEK_SET);
1299  fwrite(&ListType, 4, 1, pFile->hFileWrite);
1300  #endif // POSIX
1301  }
1302 
1304  #if DEBUG
1305  std::cout << "List::LoadSubChunks()";
1306  #endif // DEBUG
1307  if (!pSubChunks) {
1308  pSubChunks = new ChunkList();
1309  pSubChunksMap = new ChunkMap();
1310  #if defined(WIN32)
1311  if (pFile->hFileRead == INVALID_HANDLE_VALUE) return;
1312  #else
1313  if (!pFile->hFileRead) return;
1314  #endif
1315  unsigned long uiOriginalPos = GetPos();
1316  SetPos(0); // jump to beginning of list chunk body
1317  while (RemainingBytes() >= CHUNK_HEADER_SIZE) {
1318  Chunk* ck;
1319  uint32_t ckid;
1320  Read(&ckid, 4, 1);
1321  #if DEBUG
1322  std::cout << " ckid=" << convertToString(ckid) << std::endl;
1323  #endif // DEBUG
1324  if (ckid == CHUNK_ID_LIST) {
1325  ck = new RIFF::List(pFile, ulStartPos + ulPos - 4, this);
1327  }
1328  else { // simple chunk
1329  ck = new RIFF::Chunk(pFile, ulStartPos + ulPos - 4, this);
1331  }
1332  pSubChunks->push_back(ck);
1333  (*pSubChunksMap)[ckid] = ck;
1334  if (GetPos() % 2 != 0) SetPos(1, RIFF::stream_curpos); // jump over pad byte
1335  }
1336  SetPos(uiOriginalPos); // restore position before this call
1337  }
1338  }
1339 
1341  for (List* pList = GetFirstSubList(); pList; pList = GetNextSubList())
1342  pList->LoadSubChunksRecursively();
1343  }
1344 
1359  unsigned long List::WriteChunk(unsigned long ulWritePos, unsigned long ulCurrentDataOffset) {
1360  const unsigned long ulOriginalPos = ulWritePos;
1361  ulWritePos += LIST_HEADER_SIZE;
1362 
1363  if (pFile->Mode != stream_mode_read_write)
1364  throw Exception("Cannot write list chunk, file has to be opened in read+write mode");
1365 
1366  // write all subchunks (including sub list chunks) recursively
1367  if (pSubChunks) {
1368  for (ChunkList::iterator iter = pSubChunks->begin(), end = pSubChunks->end(); iter != end; ++iter) {
1369  ulWritePos = (*iter)->WriteChunk(ulWritePos, ulCurrentDataOffset);
1370  }
1371  }
1372 
1373  // update this list chunk's header
1374  CurrentChunkSize = NewChunkSize = ulWritePos - ulOriginalPos - LIST_HEADER_SIZE;
1375  WriteHeader(ulOriginalPos);
1376 
1377  // offset of this list chunk in new written file may have changed
1378  ulStartPos = ulOriginalPos + LIST_HEADER_SIZE;
1379 
1380  return ulWritePos;
1381  }
1382 
1385  if (pSubChunks) {
1386  for (ChunkList::iterator iter = pSubChunks->begin(), end = pSubChunks->end(); iter != end; ++iter) {
1387  (*iter)->__resetPos();
1388  }
1389  }
1390  }
1391 
1396  return convertToString(ListType);
1397  }
1398 
1399 
1400 
1401 // *************** File ***************
1402 // *
1403 
1404 //HACK: to avoid breaking DLL compatibility to older versions of libgig we roll the new std::set<Chunk*> into the old std::list<Chunk*> container, should be replaced on member variable level soon though
1405 #define _GET_RESIZED_CHUNKS() \
1406  (reinterpret_cast<std::set<Chunk*>*>(ResizedChunks.front()))
1407 
1422  File::File(uint32_t FileType) : List(this) {
1423  //HACK: see _GET_RESIZED_CHUNKS() comment
1424  ResizedChunks.push_back(reinterpret_cast<Chunk*>(new std::set<Chunk*>));
1425  #if defined(WIN32)
1426  hFileRead = hFileWrite = INVALID_HANDLE_VALUE;
1427  #else
1428  hFileRead = hFileWrite = 0;
1429  #endif
1430  Mode = stream_mode_closed;
1431  bEndianNative = true;
1433  ListType = FileType;
1434  }
1435 
1444  File::File(const String& path) : List(this), Filename(path) {
1445  #if DEBUG
1446  std::cout << "File::File("<<path<<")" << std::endl;
1447  #endif // DEBUG
1448  try {
1449  bEndianNative = true;
1450  //HACK: see _GET_RESIZED_CHUNKS() comment
1451  ResizedChunks.push_back(reinterpret_cast<Chunk*>(new std::set<Chunk*>));
1452  #if POSIX
1453  hFileRead = hFileWrite = open(path.c_str(), O_RDONLY | O_NONBLOCK);
1454  if (hFileRead <= 0) {
1455  hFileRead = hFileWrite = 0;
1456  throw RIFF::Exception("Can't open \"" + path + "\"");
1457  }
1458  #elif defined(WIN32)
1459  hFileRead = hFileWrite = CreateFile(
1460  path.c_str(), GENERIC_READ,
1461  FILE_SHARE_READ | FILE_SHARE_WRITE,
1462  NULL, OPEN_EXISTING,
1463  FILE_ATTRIBUTE_NORMAL |
1464  FILE_FLAG_RANDOM_ACCESS, NULL
1465  );
1466  if (hFileRead == INVALID_HANDLE_VALUE) {
1467  hFileRead = hFileWrite = INVALID_HANDLE_VALUE;
1468  throw RIFF::Exception("Can't open \"" + path + "\"");
1469  }
1470  #else
1471  hFileRead = hFileWrite = fopen(path.c_str(), "rb");
1472  if (!hFileRead) throw RIFF::Exception("Can't open \"" + path + "\"");
1473  #endif // POSIX
1474  Mode = stream_mode_read;
1476  ReadHeader(0);
1477  if (ChunkID != CHUNK_ID_RIFF && ChunkID != CHUNK_ID_RIFX) {
1478  throw RIFF::Exception("Not a RIFF file");
1479  }
1480  }
1481  catch (...) {
1482  Cleanup();
1483  throw;
1484  }
1485  }
1486 
1488  return Filename;
1489  }
1490 
1492  return Mode;
1493  }
1494 
1506  if (NewMode != Mode) {
1507  switch (NewMode) {
1508  case stream_mode_read:
1509  #if POSIX
1510  if (hFileRead) close(hFileRead);
1511  hFileRead = hFileWrite = open(Filename.c_str(), O_RDONLY | O_NONBLOCK);
1512  if (hFileRead < 0) {
1513  hFileRead = hFileWrite = 0;
1514  throw Exception("Could not (re)open file \"" + Filename + "\" in read mode");
1515  }
1516  #elif defined(WIN32)
1517  if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
1518  hFileRead = hFileWrite = CreateFile(
1519  Filename.c_str(), GENERIC_READ,
1520  FILE_SHARE_READ | FILE_SHARE_WRITE,
1521  NULL, OPEN_EXISTING,
1522  FILE_ATTRIBUTE_NORMAL |
1523  FILE_FLAG_RANDOM_ACCESS,
1524  NULL
1525  );
1526  if (hFileRead == INVALID_HANDLE_VALUE) {
1527  hFileRead = hFileWrite = INVALID_HANDLE_VALUE;
1528  throw Exception("Could not (re)open file \"" + Filename + "\" in read mode");
1529  }
1530  #else
1531  if (hFileRead) fclose(hFileRead);
1532  hFileRead = hFileWrite = fopen(Filename.c_str(), "rb");
1533  if (!hFileRead) throw Exception("Could not (re)open file \"" + Filename + "\" in read mode");
1534  #endif
1535  __resetPos(); // reset read/write position of ALL 'Chunk' objects
1536  break;
1538  #if POSIX
1539  if (hFileRead) close(hFileRead);
1540  hFileRead = hFileWrite = open(Filename.c_str(), O_RDWR | O_NONBLOCK);
1541  if (hFileRead < 0) {
1542  hFileRead = hFileWrite = open(Filename.c_str(), O_RDONLY | O_NONBLOCK);
1543  throw Exception("Could not open file \"" + Filename + "\" in read+write mode");
1544  }
1545  #elif defined(WIN32)
1546  if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
1547  hFileRead = hFileWrite = CreateFile(
1548  Filename.c_str(),
1549  GENERIC_READ | GENERIC_WRITE,
1550  FILE_SHARE_READ,
1551  NULL, OPEN_ALWAYS,
1552  FILE_ATTRIBUTE_NORMAL |
1553  FILE_FLAG_RANDOM_ACCESS,
1554  NULL
1555  );
1556  if (hFileRead == INVALID_HANDLE_VALUE) {
1557  hFileRead = hFileWrite = CreateFile(
1558  Filename.c_str(), GENERIC_READ,
1559  FILE_SHARE_READ | FILE_SHARE_WRITE,
1560  NULL, OPEN_EXISTING,
1561  FILE_ATTRIBUTE_NORMAL |
1562  FILE_FLAG_RANDOM_ACCESS,
1563  NULL
1564  );
1565  throw Exception("Could not (re)open file \"" + Filename + "\" in read+write mode");
1566  }
1567  #else
1568  if (hFileRead) fclose(hFileRead);
1569  hFileRead = hFileWrite = fopen(Filename.c_str(), "r+b");
1570  if (!hFileRead) {
1571  hFileRead = hFileWrite = fopen(Filename.c_str(), "rb");
1572  throw Exception("Could not open file \"" + Filename + "\" in read+write mode");
1573  }
1574  #endif
1575  __resetPos(); // reset read/write position of ALL 'Chunk' objects
1576  break;
1577  case stream_mode_closed:
1578  #if POSIX
1579  if (hFileRead) close(hFileRead);
1580  if (hFileWrite) close(hFileWrite);
1581  #elif defined(WIN32)
1582  if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
1583  if (hFileWrite != INVALID_HANDLE_VALUE) CloseHandle(hFileWrite);
1584  #else
1585  if (hFileRead) fclose(hFileRead);
1586  if (hFileWrite) fclose(hFileWrite);
1587  #endif
1588  hFileRead = hFileWrite = 0;
1589  break;
1590  default:
1591  throw Exception("Unknown file access mode");
1592  }
1593  Mode = NewMode;
1594  return true;
1595  }
1596  return false;
1597  }
1598 
1609  #if WORDS_BIGENDIAN
1610  bEndianNative = Endian != endian_little;
1611  #else
1612  bEndianNative = Endian != endian_big;
1613  #endif
1614  }
1615 
1626  void File::Save() {
1627  // make sure the RIFF tree is built (from the original file)
1629 
1630  // reopen file in write mode
1632 
1633  // to be able to save the whole file without loading everything into
1634  // RAM and without having to store the data in a temporary file, we
1635  // enlarge the file with the sum of all _positive_ chunk size
1636  // changes, move current data towards the end of the file with the
1637  // calculated sum and finally update / rewrite the file by copying
1638  // the old data back to the right position at the beginning of the file
1639 
1640  // first we sum up all positive chunk size changes (and skip all negative ones)
1641  unsigned long ulPositiveSizeDiff = 0;
1642  std::set<Chunk*>* resizedChunks = _GET_RESIZED_CHUNKS();
1643  for (std::set<Chunk*>::const_iterator iter = resizedChunks->begin(), end = resizedChunks->end(); iter != end; ++iter) {
1644  if ((*iter)->GetNewSize() == 0) {
1645  throw Exception("There is at least one empty chunk (zero size): " + __resolveChunkPath(*iter));
1646  }
1647  unsigned long newSizePadded = (*iter)->GetNewSize() + (*iter)->GetNewSize() % 2;
1648  unsigned long oldSizePadded = (*iter)->GetSize() + (*iter)->GetSize() % 2;
1649  if (newSizePadded > oldSizePadded) ulPositiveSizeDiff += newSizePadded - oldSizePadded;
1650  }
1651 
1652  unsigned long ulWorkingFileSize = GetFileSize();
1653 
1654  // if there are positive size changes...
1655  if (ulPositiveSizeDiff > 0) {
1656  // ... we enlarge this file first ...
1657  ulWorkingFileSize += ulPositiveSizeDiff;
1658  ResizeFile(ulWorkingFileSize);
1659  // ... and move current data by the same amount towards end of file.
1660  int8_t* pCopyBuffer = new int8_t[4096];
1661  const unsigned long ulFileSize = GetSize() + RIFF_HEADER_SIZE;
1662  #if defined(WIN32)
1663  DWORD iBytesMoved = 1; // we have to pass it via pointer to the Windows API, thus the correct size must be ensured
1664  #else
1665  int iBytesMoved = 1;
1666  #endif
1667  for (unsigned long ulPos = ulFileSize; iBytesMoved > 0; ) {
1668  iBytesMoved = (ulPos < 4096) ? ulPos : 4096;
1669  ulPos -= iBytesMoved;
1670  #if POSIX
1671  lseek(hFileRead, ulPos, SEEK_SET);
1672  iBytesMoved = read(hFileRead, pCopyBuffer, iBytesMoved);
1673  lseek(hFileWrite, ulPos + ulPositiveSizeDiff, SEEK_SET);
1674  iBytesMoved = write(hFileWrite, pCopyBuffer, iBytesMoved);
1675  #elif defined(WIN32)
1676  SetFilePointer(hFileRead, ulPos, NULL/*32 bit*/, FILE_BEGIN);
1677  ReadFile(hFileRead, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
1678  SetFilePointer(hFileWrite, ulPos + ulPositiveSizeDiff, NULL/*32 bit*/, FILE_BEGIN);
1679  WriteFile(hFileWrite, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
1680  #else
1681  fseek(hFileRead, ulPos, SEEK_SET);
1682  iBytesMoved = fread(pCopyBuffer, 1, iBytesMoved, hFileRead);
1683  fseek(hFileWrite, ulPos + ulPositiveSizeDiff, SEEK_SET);
1684  iBytesMoved = fwrite(pCopyBuffer, 1, iBytesMoved, hFileWrite);
1685  #endif
1686  }
1687  delete[] pCopyBuffer;
1688  if (iBytesMoved < 0) throw Exception("Could not modify file while trying to enlarge it");
1689  }
1690 
1691  // rebuild / rewrite complete RIFF tree
1692  unsigned long ulTotalSize = WriteChunk(0, ulPositiveSizeDiff);
1693  unsigned long ulActualSize = __GetFileSize(hFileWrite);
1694 
1695  // resize file to the final size
1696  if (ulTotalSize < ulActualSize) ResizeFile(ulTotalSize);
1697 
1698  // forget all resized chunks
1699  resizedChunks->clear();
1700  }
1701 
1715  void File::Save(const String& path) {
1716  //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
1717 
1718  // make sure the RIFF tree is built (from the original file)
1720 
1721  if (Filename.length() > 0) SetMode(stream_mode_read);
1722  // open the other (new) file for writing and truncate it to zero size
1723  #if POSIX
1724  hFileWrite = open(path.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP);
1725  if (hFileWrite < 0) {
1727  throw Exception("Could not open file \"" + path + "\" for writing");
1728  }
1729  #elif defined(WIN32)
1730  hFileWrite = CreateFile(
1731  path.c_str(), GENERIC_WRITE, FILE_SHARE_READ,
1732  NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL |
1733  FILE_FLAG_RANDOM_ACCESS, NULL
1734  );
1735  if (hFileWrite == INVALID_HANDLE_VALUE) {
1737  throw Exception("Could not open file \"" + path + "\" for writing");
1738  }
1739  #else
1740  hFileWrite = fopen(path.c_str(), "w+b");
1741  if (!hFileWrite) {
1743  throw Exception("Could not open file \"" + path + "\" for writing");
1744  }
1745  #endif // POSIX
1746  Mode = stream_mode_read_write;
1747 
1748  // write complete RIFF tree to the other (new) file
1749  unsigned long ulTotalSize = WriteChunk(0, 0);
1750  unsigned long ulActualSize = __GetFileSize(hFileWrite);
1751 
1752  // resize file to the final size (if the file was originally larger)
1753  if (ulTotalSize < ulActualSize) ResizeFile(ulTotalSize);
1754 
1755  // forget all resized chunks
1756  _GET_RESIZED_CHUNKS()->clear();
1757 
1758  #if POSIX
1759  if (hFileWrite) close(hFileWrite);
1760  #elif defined(WIN32)
1761  if (hFileWrite != INVALID_HANDLE_VALUE) CloseHandle(hFileWrite);
1762  #else
1763  if (hFileWrite) fclose(hFileWrite);
1764  #endif
1766 
1767  // associate new file with this File object from now on
1768  Filename = path;
1769  Mode = (stream_mode_t) -1; // Just set it to an undefined mode ...
1770  SetMode(stream_mode_read_write); // ... so SetMode() has to reopen the file handles.
1771  }
1772 
1773  void File::ResizeFile(unsigned long ulNewSize) {
1774  #if POSIX
1775  if (ftruncate(hFileWrite, ulNewSize) < 0)
1776  throw Exception("Could not resize file \"" + Filename + "\"");
1777  #elif defined(WIN32)
1778  if (
1779  SetFilePointer(hFileWrite, ulNewSize, NULL/*32 bit*/, FILE_BEGIN) == INVALID_SET_FILE_POINTER ||
1780  !SetEndOfFile(hFileWrite)
1781  ) throw Exception("Could not resize file \"" + Filename + "\"");
1782  #else
1783  # error Sorry, this version of libgig only supports POSIX and Windows systems yet.
1784  # error Reason: portable implementation of RIFF::File::ResizeFile() is missing (yet)!
1785  #endif
1786  }
1787 
1789  #if DEBUG
1790  std::cout << "File::~File()" << std::endl;
1791  #endif // DEBUG
1792  Cleanup();
1793  }
1794 
1795  void File::Cleanup() {
1796  #if POSIX
1797  if (hFileRead) close(hFileRead);
1798  #elif defined(WIN32)
1799  if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
1800  #else
1801  if (hFileRead) fclose(hFileRead);
1802  #endif // POSIX
1803  DeleteChunkList();
1804  pFile = NULL;
1805  //HACK: see _GET_RESIZED_CHUNKS() comment
1806  delete _GET_RESIZED_CHUNKS();
1807  }
1808 
1809  void File::LogAsResized(Chunk* pResizedChunk) {
1810  _GET_RESIZED_CHUNKS()->insert(pResizedChunk);
1811  }
1812 
1813  void File::UnlogResized(Chunk* pResizedChunk) {
1814  _GET_RESIZED_CHUNKS()->erase(pResizedChunk);
1815  }
1816 
1817  unsigned long File::GetFileSize() {
1818  return __GetFileSize(hFileRead);
1819  }
1820 
1821  #if POSIX
1822  unsigned long File::__GetFileSize(int hFile) {
1823  struct stat filestat;
1824  fstat(hFile, &filestat);
1825  long size = filestat.st_size;
1826  return size;
1827  }
1828  #elif defined(WIN32)
1829  unsigned long File::__GetFileSize(HANDLE hFile) {
1830  DWORD dwSize = ::GetFileSize(hFile, NULL /*32bit*/);
1831  if (dwSize == INVALID_FILE_SIZE)
1832  throw Exception("Windows FS error: could not determine file size");
1833  return dwSize;
1834  }
1835  #else // standard C functions
1836  unsigned long File::__GetFileSize(FILE* hFile) {
1837  long curpos = ftell(hFile);
1838  fseek(hFile, 0, SEEK_END);
1839  long size = ftell(hFile);
1840  fseek(hFile, curpos, SEEK_SET);
1841  return size;
1842  }
1843  #endif
1844 
1845 
1846 // *************** Exception ***************
1847 // *
1848 
1850  std::cout << "RIFF::Exception: " << Message << std::endl;
1851  }
1852 
1853 
1854 // *************** functions ***************
1855 // *
1856 
1863  return PACKAGE;
1864  }
1865 
1871  return VERSION;
1872  }
1873 
1874 } // namespace RIFF
virtual unsigned long WriteChunk(unsigned long ulWritePos, unsigned long ulCurrentDataOffset)
Write chunk persistently e.g.
Definition: RIFF.cpp:842
unsigned long WriteUint32(uint32_t *pData, unsigned long WordCount=1)
Writes WordCount number of 32 Bit unsigned integer words from the buffer pointed by pData to the chun...
Definition: RIFF.cpp:628
#define _GET_RESIZED_CHUNKS()
Definition: RIFF.cpp:1405
int16_t ReadInt16()
Reads one 16 Bit signed integer word and increments the position within the chunk.
Definition: RIFF.cpp:672
bool bEndianNative
Definition: RIFF.h:343
void UnlogResized(Chunk *pResizedChunk)
Definition: RIFF.cpp:1813
void swapBytes_16(void *Word)
Definition: RIFF.h:231
List * pParent
Definition: RIFF.h:219
#define CHUNK_ID_RIFX
Definition: RIFF.h:94
stream_whence_t
File stream position dependent to these relations.
Definition: RIFF.h:158
unsigned long Read(void *pData, unsigned long WordCount, unsigned long WordSize)
Reads WordCount number of data words with given WordSize and copies it into a buffer pointed by pData...
Definition: RIFF.cpp:280
Chunk * GetFirstSubChunk()
Returns the first subchunk within the list.
Definition: RIFF.cpp:1046
String libraryName()
Returns the name of this C++ library.
Definition: RIFF.cpp:1862
File(uint32_t FileType)
Create new RIFF file.
Definition: RIFF.cpp:1422
unsigned long WriteUint16(uint16_t *pData, unsigned long WordCount=1)
Writes WordCount number of 16 Bit unsigned integer words from the buffer pointed by pData to the chun...
Definition: RIFF.cpp:537
uint32_t GetChunkID()
Chunk ID in unsigned integer representation.
Definition: RIFF.h:181
String GetFileName()
Definition: RIFF.cpp:1487
void ReadHeader(unsigned long fPos)
Definition: RIFF.cpp:1259
void swapBytes(void *Word, unsigned long WordSize)
Definition: RIFF.h:244
stream_state_t
Current state of the file stream.
Definition: RIFF.h:151
unsigned long SetPos(unsigned long 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:199
void ReadString(String &s, int size)
Reads a null-padded string of size characters and copies it into the string s.
Definition: RIFF.cpp:607
#define LIST_HEADER_SIZE
Definition: RIFF.h:111
void WriteHeader(unsigned long fPos)
Definition: RIFF.cpp:1285
String libraryVersion()
Returns version of this C++ library.
Definition: RIFF.cpp:1870
List * GetSubList(uint32_t ListType)
Returns sublist chunk with list type ListType within this chunk list.
Definition: RIFF.cpp:1021
void DeleteSubChunk(Chunk *pSubChunk)
Removes a sub chunk.
Definition: RIFF.cpp:1241
unsigned long WriteInt16(int16_t *pData, unsigned long WordCount=1)
Writes WordCount number of 16 Bit signed integer words from the buffer pointed by pData to the chunk&#39;...
Definition: RIFF.cpp:500
#define CHUNK_HEADER_SIZE
Definition: RIFF.h:110
int hFileWrite
handle / descriptor for writing to (some) file
Definition: RIFF.h:334
String Filename
Definition: RIFF.h:342
unsigned long RemainingBytes()
Returns the number of bytes left to read in the chunk body.
Definition: RIFF.cpp:231
List * GetFirstSubList()
Returns the first sublist within the list (that is a subchunk with chunk ID &quot;LIST&quot;).
Definition: RIFF.cpp:1080
stream_mode_t
Whether file stream is open in read or in read/write mode.
Definition: RIFF.h:144
std::list< Chunk * > ChunkList
Definition: RIFF.h:295
std::string String
Definition: RIFF.h:139
unsigned long GetPos()
Position within the chunk data body.
Definition: RIFF.h:185
void SetByteOrder(endian_t Endian)
Set the byte order to be used when saving.
Definition: RIFF.cpp:1608
unsigned long GetSize()
Chunk size in bytes (without header, thus the chunk data body)
Definition: RIFF.h:183
RIFF List Chunk.
Definition: RIFF.h:273
#define CHUNK_ID_RIFF
Definition: RIFF.h:93
File * pFile
Definition: RIFF.h:220
int8_t ReadInt8()
Reads one 8 Bit signed integer word and increments the position within the chunk. ...
Definition: RIFF.cpp:639
void ReadHeader(unsigned long fPos)
Definition: RIFF.cpp:102
String GetListTypeString()
Returns string representation of the lists&#39;s id.
Definition: RIFF.cpp:1395
ChunkList::iterator ListIterator
Definition: RIFF.h:301
String Message
Definition: RIFF.h:370
unsigned long ulPos
Definition: RIFF.h:222
unsigned long WriteInt32(int32_t *pData, unsigned long WordCount=1)
Writes WordCount number of 32 Bit signed integer words from the buffer pointed by pData to the chunk&#39;...
Definition: RIFF.cpp:574
void DeleteChunkList()
Definition: RIFF.cpp:974
Chunk * GetSubChunk(uint32_t ChunkID)
Returns subchunk with chunk ID ChunkID within this chunk list.
Definition: RIFF.cpp:1002
Chunk * GetNextSubChunk()
Returns the next subchunk within the list.
Definition: RIFF.cpp:1062
unsigned long ReadSceptical(void *pData, unsigned long WordCount, unsigned long WordSize)
Just an internal wrapper for the main Read() method with additional Exception throwing on errors...
Definition: RIFF.cpp:388
int32_t ReadInt32()
Reads one 32 Bit signed integer word and increments the position within the chunk.
Definition: RIFF.cpp:706
stream_state_t GetState()
Returns the current state of the chunk object.
Definition: RIFF.cpp:249
void LoadSubChunks()
Definition: RIFF.cpp:1303
Ordinary RIFF Chunk.
Definition: RIFF.h:177
uint32_t GetListType()
Returns unsigned integer representation of the list&#39;s ID.
Definition: RIFF.h:277
#define RIFF_HEADER_SIZE
Definition: RIFF.h:112
uint32_t ReadUint32()
Reads one 32 Bit unsigned integer word and increments the position within the chunk.
Definition: RIFF.cpp:723
ChunkList::iterator ChunksIterator
Definition: RIFF.h:300
unsigned long ulStartPos
Definition: RIFF.h:221
unsigned long Write(void *pData, unsigned long WordCount, unsigned long WordSize)
Writes WordCount number of data words with given WordSize from the buffer pointed by pData...
Definition: RIFF.cpp:338
ChunkList * pSubChunks
Definition: RIFF.h:298
void swapBytes_32(void *Word)
Definition: RIFF.h:236
Chunk * AddSubChunk(uint32_t uiChunkID, uint uiBodySize)
Creates a new sub chunk.
Definition: RIFF.cpp:1182
void PrintMessage()
Definition: RIFF.cpp:1849
int hFileRead
handle / descriptor for reading from file
Definition: RIFF.h:333
ChunkMap * pSubChunksMap
Definition: RIFF.h:299
virtual ~Chunk()
Definition: RIFF.cpp:97
void LogAsResized(Chunk *pResizedChunk)
Definition: RIFF.cpp:1809
virtual void __resetPos()
Sets Chunk&#39;s read/write position to zero.
Definition: RIFF.cpp:932
uint16_t ReadUint16()
Reads one 16 Bit unsigned integer word and increments the position within the chunk.
Definition: RIFF.cpp:689
virtual void __resetPos()
Sets List Chunk&#39;s read/write position to zero and causes all sub chunks to do the same...
Definition: RIFF.cpp:1383
uint8_t * pChunkData
Definition: RIFF.h:223
uint32_t CurrentChunkSize
Definition: RIFF.h:217
endian_t
Alignment of data bytes in memory (system dependant).
Definition: RIFF.h:166
void * LoadChunkData()
Load chunk body into RAM.
Definition: RIFF.cpp:753
unsigned long WriteInt8(int8_t *pData, unsigned long WordCount=1)
Writes WordCount number of 8 Bit signed integer words from the buffer pointed by pData to the chunk&#39;s...
Definition: RIFF.cpp:426
unsigned long ulChunkDataSize
Definition: RIFF.h:224
RIFF File.
Definition: RIFF.h:320
List * AddSubList(uint32_t uiListType)
Creates a new list sub chunk.
Definition: RIFF.cpp:1221
#define CHUNK_ID_LIST
Definition: RIFF.h:95
void LoadSubChunksRecursively()
Definition: RIFF.cpp:1340
List(File *pFile, unsigned long StartPos, List *Parent)
Definition: RIFF.cpp:949
virtual unsigned long WriteChunk(unsigned long ulWritePos, unsigned long ulCurrentDataOffset)
Write list chunk persistently e.g.
Definition: RIFF.cpp:1359
void MoveSubChunk(Chunk *pSrc, Chunk *pDst)
Moves a sub chunk.
Definition: RIFF.cpp:1205
unsigned int CountSubChunks()
Returns number of subchunks within the list.
Definition: RIFF.cpp:1120
virtual ~List()
Definition: RIFF.cpp:967
String convertToString(uint32_t word)
Definition: RIFF.h:253
bool SetMode(stream_mode_t NewMode)
Change file access mode.
Definition: RIFF.cpp:1505
virtual void Save()
Save changes to same file.
Definition: RIFF.cpp:1626
Will be thrown whenever an error occurs while handling a RIFF file.
Definition: RIFF.h:368
unsigned long WriteUint8(uint8_t *pData, unsigned long WordCount=1)
Writes WordCount number of 8 Bit unsigned integer words from the buffer pointed by pData to the chunk...
Definition: RIFF.cpp:463
stream_mode_t GetMode()
Definition: RIFF.cpp:1491
void ReleaseChunkData()
Free loaded chunk body from RAM.
Definition: RIFF.cpp:797
virtual ~File()
Definition: RIFF.cpp:1788
unsigned int CountSubLists()
Returns number of sublists within the list.
Definition: RIFF.cpp:1146
String GetChunkIDString()
Returns the String representation of the chunk&#39;s ID (e.g.
Definition: RIFF.cpp:183
uint32_t NewChunkSize
Definition: RIFF.h:218
void WriteHeader(unsigned long fPos)
Definition: RIFF.cpp:145
uint32_t ChunkID
Definition: RIFF.h:216
std::map< uint32_t, RIFF::Chunk * > ChunkMap
Definition: RIFF.h:294
void Resize(int iNewSize)
Resize chunk.
Definition: RIFF.cpp:822
List * GetNextSubList()
Returns the next sublist (that is a subchunk with chunk ID &quot;LIST&quot;) within the list.
Definition: RIFF.cpp:1102
Chunk(File *pFile, unsigned long StartPos, List *Parent)
Definition: RIFF.cpp:70
uint8_t ReadUint8()
Reads one 8 Bit unsigned integer word and increments the position within the chunk.
Definition: RIFF.cpp:655
uint32_t ListType
Definition: RIFF.h:297