libgig  3.3.0
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-2009 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 
611  unsigned long Chunk::WriteUint32(uint32_t* pData, unsigned long WordCount) {
612  return Write(pData, WordCount, 4);
613  }
614 
622  int8_t Chunk::ReadInt8() {
623  #if DEBUG
624  std::cout << "Chunk::ReadInt8()" << std::endl;
625  #endif // DEBUG
626  int8_t word;
627  ReadSceptical(&word,1,1);
628  return word;
629  }
630 
638  uint8_t Chunk::ReadUint8() {
639  #if DEBUG
640  std::cout << "Chunk::ReadUint8()" << std::endl;
641  #endif // DEBUG
642  uint8_t word;
643  ReadSceptical(&word,1,1);
644  return word;
645  }
646 
655  int16_t Chunk::ReadInt16() {
656  #if DEBUG
657  std::cout << "Chunk::ReadInt16()" << std::endl;
658  #endif // DEBUG
659  int16_t word;
660  ReadSceptical(&word,1,2);
661  return word;
662  }
663 
672  uint16_t Chunk::ReadUint16() {
673  #if DEBUG
674  std::cout << "Chunk::ReadUint16()" << std::endl;
675  #endif // DEBUG
676  uint16_t word;
677  ReadSceptical(&word,1,2);
678  return word;
679  }
680 
689  int32_t Chunk::ReadInt32() {
690  #if DEBUG
691  std::cout << "Chunk::ReadInt32()" << std::endl;
692  #endif // DEBUG
693  int32_t word;
694  ReadSceptical(&word,1,4);
695  return word;
696  }
697 
706  uint32_t Chunk::ReadUint32() {
707  #if DEBUG
708  std::cout << "Chunk::ReadUint32()" << std::endl;
709  #endif // DEBUG
710  uint32_t word;
711  ReadSceptical(&word,1,4);
712  return word;
713  }
714 
737  if (!pChunkData && pFile->Filename != "" && ulStartPos != 0) {
738  #if POSIX
739  if (lseek(pFile->hFileRead, ulStartPos, SEEK_SET) == -1) return NULL;
740  #elif defined(WIN32)
741  if (SetFilePointer(pFile->hFileRead, ulStartPos, NULL/*32 bit*/, FILE_BEGIN) == INVALID_SET_FILE_POINTER) return NULL;
742  #else
743  if (fseek(pFile->hFileRead, ulStartPos, SEEK_SET)) return NULL;
744  #endif // POSIX
745  unsigned long ulBufferSize = (CurrentChunkSize > NewChunkSize) ? CurrentChunkSize : NewChunkSize;
746  pChunkData = new uint8_t[ulBufferSize];
747  if (!pChunkData) return NULL;
748  memset(pChunkData, 0, ulBufferSize);
749  #if POSIX
750  unsigned long readWords = read(pFile->hFileRead, pChunkData, GetSize());
751  #elif defined(WIN32)
752  DWORD readWords;
753  ReadFile(pFile->hFileRead, pChunkData, GetSize(), &readWords, NULL);
754  #else
755  unsigned long readWords = fread(pChunkData, 1, GetSize(), pFile->hFileRead);
756  #endif // POSIX
757  if (readWords != GetSize()) {
758  delete[] pChunkData;
759  return (pChunkData = NULL);
760  }
761  ulChunkDataSize = ulBufferSize;
762  } else if (NewChunkSize > ulChunkDataSize) {
763  uint8_t* pNewBuffer = new uint8_t[NewChunkSize];
764  if (!pNewBuffer) throw Exception("Could not enlarge chunk data buffer to " + ToString(NewChunkSize) + " bytes");
765  memset(pNewBuffer, 0 , NewChunkSize);
766  memcpy(pNewBuffer, pChunkData, ulChunkDataSize);
767  delete[] pChunkData;
768  pChunkData = pNewBuffer;
770  }
771  return pChunkData;
772  }
773 
781  if (pChunkData) {
782  delete[] pChunkData;
783  pChunkData = NULL;
784  }
785  }
786 
805  void Chunk::Resize(int iNewSize) {
806  if (iNewSize <= 0)
807  throw Exception("There is at least one empty chunk (zero size): " + __resolveChunkPath(this));
808  if (NewChunkSize == iNewSize) return;
809  NewChunkSize = iNewSize;
810  pFile->LogAsResized(this);
811  }
812 
825  unsigned long Chunk::WriteChunk(unsigned long ulWritePos, unsigned long ulCurrentDataOffset) {
826  const unsigned long ulOriginalPos = ulWritePos;
827  ulWritePos += CHUNK_HEADER_SIZE;
828 
829  if (pFile->Mode != stream_mode_read_write)
830  throw Exception("Cannot write list chunk, file has to be opened in read+write mode");
831 
832  // if the whole chunk body was loaded into RAM
833  if (pChunkData) {
834  // make sure chunk data buffer in RAM is at least as large as the new chunk size
835  LoadChunkData();
836  // write chunk data from RAM persistently to the file
837  #if POSIX
838  lseek(pFile->hFileWrite, ulWritePos, SEEK_SET);
840  throw Exception("Writing Chunk data (from RAM) failed");
841  }
842  #elif defined(WIN32)
843  SetFilePointer(pFile->hFileWrite, ulWritePos, NULL/*32 bit*/, FILE_BEGIN);
844  DWORD dwBytesWritten;
845  WriteFile(pFile->hFileWrite, pChunkData, NewChunkSize, &dwBytesWritten, NULL);
846  if (dwBytesWritten != NewChunkSize) {
847  throw Exception("Writing Chunk data (from RAM) failed");
848  }
849  #else
850  fseek(pFile->hFileWrite, ulWritePos, SEEK_SET);
851  if (fwrite(pChunkData, 1, NewChunkSize, pFile->hFileWrite) != NewChunkSize) {
852  throw Exception("Writing Chunk data (from RAM) failed");
853  }
854  #endif // POSIX
855  } else {
856  // move chunk data from the end of the file to the appropriate position
857  int8_t* pCopyBuffer = new int8_t[4096];
858  unsigned long ulToMove = (NewChunkSize < CurrentChunkSize) ? NewChunkSize : CurrentChunkSize;
859  #if defined(WIN32)
860  DWORD iBytesMoved = 1; // we have to pass it via pointer to the Windows API, thus the correct size must be ensured
861  #else
862  int iBytesMoved = 1;
863  #endif
864  for (unsigned long ulOffset = 0; iBytesMoved > 0; ulOffset += iBytesMoved, ulToMove -= iBytesMoved) {
865  iBytesMoved = (ulToMove < 4096) ? ulToMove : 4096;
866  #if POSIX
867  lseek(pFile->hFileRead, ulStartPos + ulCurrentDataOffset + ulOffset, SEEK_SET);
868  iBytesMoved = read(pFile->hFileRead, pCopyBuffer, iBytesMoved);
869  lseek(pFile->hFileWrite, ulWritePos + ulOffset, SEEK_SET);
870  iBytesMoved = write(pFile->hFileWrite, pCopyBuffer, iBytesMoved);
871  #elif defined(WIN32)
872  SetFilePointer(pFile->hFileRead, ulStartPos + ulCurrentDataOffset + ulOffset, NULL/*32 bit*/, FILE_BEGIN);
873  ReadFile(pFile->hFileRead, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
874  SetFilePointer(pFile->hFileWrite, ulWritePos + ulOffset, NULL/*32 bit*/, FILE_BEGIN);
875  WriteFile(pFile->hFileWrite, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
876  #else
877  fseek(pFile->hFileRead, ulStartPos + ulCurrentDataOffset + ulOffset, SEEK_SET);
878  iBytesMoved = fread(pCopyBuffer, 1, iBytesMoved, pFile->hFileRead);
879  fseek(pFile->hFileWrite, ulWritePos + ulOffset, SEEK_SET);
880  iBytesMoved = fwrite(pCopyBuffer, 1, iBytesMoved, pFile->hFileWrite);
881  #endif
882  }
883  delete[] pCopyBuffer;
884  if (iBytesMoved < 0) throw Exception("Writing Chunk data (from file) failed");
885  }
886 
887  // update this chunk's header
889  WriteHeader(ulOriginalPos);
890 
891  // update chunk's position pointers
892  ulStartPos = ulOriginalPos + CHUNK_HEADER_SIZE;
893  ulPos = 0;
894 
895  // add pad byte if needed
896  if ((ulStartPos + NewChunkSize) % 2 != 0) {
897  const char cPadByte = 0;
898  #if POSIX
899  lseek(pFile->hFileWrite, ulStartPos + NewChunkSize, SEEK_SET);
900  write(pFile->hFileWrite, &cPadByte, 1);
901  #elif defined(WIN32)
902  SetFilePointer(pFile->hFileWrite, ulStartPos + NewChunkSize, NULL/*32 bit*/, FILE_BEGIN);
903  DWORD dwBytesWritten;
904  WriteFile(pFile->hFileWrite, &cPadByte, 1, &dwBytesWritten, NULL);
905  #else
906  fseek(pFile->hFileWrite, ulStartPos + NewChunkSize, SEEK_SET);
907  fwrite(&cPadByte, 1, 1, pFile->hFileWrite);
908  #endif
909  return ulStartPos + NewChunkSize + 1;
910  }
911 
912  return ulStartPos + NewChunkSize;
913  }
914 
916  ulPos = 0;
917  }
918 
919 
920 
921 // *************** List ***************
922 // *
923 
924  List::List(File* pFile) : Chunk(pFile) {
925  #if DEBUG
926  std::cout << "List::List(File* pFile)" << std::endl;
927  #endif // DEBUG
928  pSubChunks = NULL;
929  pSubChunksMap = NULL;
930  }
931 
932  List::List(File* pFile, unsigned long StartPos, List* Parent)
933  : Chunk(pFile, StartPos, Parent) {
934  #if DEBUG
935  std::cout << "List::List(File*,ulong,bool,List*)" << std::endl;
936  #endif // DEBUG
937  pSubChunks = NULL;
938  pSubChunksMap = NULL;
939  ReadHeader(StartPos);
940  ulStartPos = StartPos + LIST_HEADER_SIZE;
941  }
942 
943  List::List(File* pFile, List* pParent, uint32_t uiListID)
944  : Chunk(pFile, pParent, CHUNK_ID_LIST, 0) {
945  pSubChunks = NULL;
946  pSubChunksMap = NULL;
947  ListType = uiListID;
948  }
949 
951  #if DEBUG
952  std::cout << "List::~List()" << std::endl;
953  #endif // DEBUG
954  DeleteChunkList();
955  }
956 
958  if (pSubChunks) {
959  pSubChunks->clear();
960  pSubChunks = NULL;
961  }
962  if (pSubChunksMap) {
963  delete pSubChunksMap;
964  pSubChunksMap = NULL;
965  }
966  }
967 
979  Chunk* List::GetSubChunk(uint32_t ChunkID) {
980  #if DEBUG
981  std::cout << "List::GetSubChunk(uint32_t)" << std::endl;
982  #endif // DEBUG
984  return (*pSubChunksMap)[ChunkID];
985  }
986 
998  List* List::GetSubList(uint32_t ListType) {
999  #if DEBUG
1000  std::cout << "List::GetSubList(uint32_t)" << std::endl;
1001  #endif // DEBUG
1002  if (!pSubChunks) LoadSubChunks();
1003  ChunkList::iterator iter = pSubChunks->begin();
1004  ChunkList::iterator end = pSubChunks->end();
1005  while (iter != end) {
1006  if ((*iter)->GetChunkID() == CHUNK_ID_LIST) {
1007  List* l = (List*) *iter;
1008  if (l->GetListType() == ListType) return l;
1009  }
1010  iter++;
1011  }
1012  return NULL;
1013  }
1014 
1024  #if DEBUG
1025  std::cout << "List::GetFirstSubChunk()" << std::endl;
1026  #endif // DEBUG
1027  if (!pSubChunks) LoadSubChunks();
1028  ChunksIterator = pSubChunks->begin();
1029  return (ChunksIterator != pSubChunks->end()) ? *ChunksIterator : NULL;
1030  }
1031 
1040  #if DEBUG
1041  std::cout << "List::GetNextSubChunk()" << std::endl;
1042  #endif // DEBUG
1043  if (!pSubChunks) return NULL;
1044  ChunksIterator++;
1045  return (ChunksIterator != pSubChunks->end()) ? *ChunksIterator : NULL;
1046  }
1047 
1058  #if DEBUG
1059  std::cout << "List::GetFirstSubList()" << std::endl;
1060  #endif // DEBUG
1061  if (!pSubChunks) LoadSubChunks();
1062  ListIterator = pSubChunks->begin();
1063  ChunkList::iterator end = pSubChunks->end();
1064  while (ListIterator != end) {
1065  if ((*ListIterator)->GetChunkID() == CHUNK_ID_LIST) return (List*) *ListIterator;
1066  ListIterator++;
1067  }
1068  return NULL;
1069  }
1070 
1080  #if DEBUG
1081  std::cout << "List::GetNextSubList()" << std::endl;
1082  #endif // DEBUG
1083  if (!pSubChunks) return NULL;
1084  if (ListIterator == pSubChunks->end()) return NULL;
1085  ListIterator++;
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 
1097  unsigned int List::CountSubChunks() {
1098  if (!pSubChunks) LoadSubChunks();
1099  return pSubChunks->size();
1100  }
1101 
1106  unsigned int List::CountSubChunks(uint32_t ChunkID) {
1107  unsigned int result = 0;
1108  if (!pSubChunks) LoadSubChunks();
1109  ChunkList::iterator iter = pSubChunks->begin();
1110  ChunkList::iterator end = pSubChunks->end();
1111  while (iter != end) {
1112  if ((*iter)->GetChunkID() == ChunkID) {
1113  result++;
1114  }
1115  iter++;
1116  }
1117  return result;
1118  }
1119 
1123  unsigned int List::CountSubLists() {
1124  return CountSubChunks(CHUNK_ID_LIST);
1125  }
1126 
1131  unsigned int List::CountSubLists(uint32_t ListType) {
1132  unsigned int result = 0;
1133  if (!pSubChunks) LoadSubChunks();
1134  ChunkList::iterator iter = pSubChunks->begin();
1135  ChunkList::iterator end = pSubChunks->end();
1136  while (iter != end) {
1137  if ((*iter)->GetChunkID() == CHUNK_ID_LIST) {
1138  List* l = (List*) *iter;
1139  if (l->GetListType() == ListType) result++;
1140  }
1141  iter++;
1142  }
1143  return result;
1144  }
1145 
1159  Chunk* List::AddSubChunk(uint32_t uiChunkID, uint uiBodySize) {
1160  if (uiBodySize == 0) throw Exception("Chunk body size must be at least 1 byte");
1161  if (!pSubChunks) LoadSubChunks();
1162  Chunk* pNewChunk = new Chunk(pFile, this, uiChunkID, 0);
1163  pSubChunks->push_back(pNewChunk);
1164  (*pSubChunksMap)[uiChunkID] = pNewChunk;
1165  pNewChunk->Resize(uiBodySize);
1167  pFile->LogAsResized(this);
1168  return pNewChunk;
1169  }
1170 
1182  void List::MoveSubChunk(Chunk* pSrc, Chunk* pDst) {
1183  if (!pSubChunks) LoadSubChunks();
1184  pSubChunks->remove(pSrc);
1185  ChunkList::iterator iter = find(pSubChunks->begin(), pSubChunks->end(), pDst);
1186  pSubChunks->insert(iter, pSrc);
1187  }
1188 
1198  List* List::AddSubList(uint32_t uiListType) {
1199  if (!pSubChunks) LoadSubChunks();
1200  List* pNewListChunk = new List(pFile, this, uiListType);
1201  pSubChunks->push_back(pNewListChunk);
1202  (*pSubChunksMap)[CHUNK_ID_LIST] = pNewListChunk;
1204  pFile->LogAsResized(this);
1205  return pNewListChunk;
1206  }
1207 
1218  void List::DeleteSubChunk(Chunk* pSubChunk) {
1219  if (!pSubChunks) LoadSubChunks();
1220  pSubChunks->remove(pSubChunk);
1221  if ((*pSubChunksMap)[pSubChunk->GetChunkID()] == pSubChunk) {
1222  pSubChunksMap->erase(pSubChunk->GetChunkID());
1223  // try to find another chunk of the same chunk ID
1224  ChunkList::iterator iter = pSubChunks->begin();
1225  ChunkList::iterator end = pSubChunks->end();
1226  for (; iter != end; ++iter) {
1227  if ((*iter)->GetChunkID() == pSubChunk->GetChunkID()) {
1228  (*pSubChunksMap)[pSubChunk->GetChunkID()] = *iter;
1229  break; // we're done, stop search
1230  }
1231  }
1232  }
1233  delete pSubChunk;
1234  }
1235 
1236  void List::ReadHeader(unsigned long fPos) {
1237  #if DEBUG
1238  std::cout << "List::Readheader(ulong) ";
1239  #endif // DEBUG
1240  Chunk::ReadHeader(fPos);
1241  if (CurrentChunkSize < 4) return;
1243  #if POSIX
1244  lseek(pFile->hFileRead, fPos + CHUNK_HEADER_SIZE, SEEK_SET);
1245  read(pFile->hFileRead, &ListType, 4);
1246  #elif defined(WIN32)
1247  SetFilePointer(pFile->hFileRead, fPos + CHUNK_HEADER_SIZE, NULL/*32 bit*/, FILE_BEGIN);
1248  DWORD dwBytesRead;
1249  ReadFile(pFile->hFileRead, &ListType, 4, &dwBytesRead, NULL);
1250  #else
1251  fseek(pFile->hFileRead, fPos + CHUNK_HEADER_SIZE, SEEK_SET);
1252  fread(&ListType, 4, 1, pFile->hFileRead);
1253  #endif // POSIX
1254  #if DEBUG
1255  std::cout << "listType=" << convertToString(ListType) << std::endl;
1256  #endif // DEBUG
1257  if (!pFile->bEndianNative) {
1258  //swapBytes_32(&ListType);
1259  }
1260  }
1261 
1262  void List::WriteHeader(unsigned long fPos) {
1263  // the four list type bytes officially belong the chunk's body in the RIFF format
1264  NewChunkSize += 4;
1265  Chunk::WriteHeader(fPos);
1266  NewChunkSize -= 4; // just revert the +4 incrementation
1267  #if POSIX
1268  lseek(pFile->hFileWrite, fPos + CHUNK_HEADER_SIZE, SEEK_SET);
1269  write(pFile->hFileWrite, &ListType, 4);
1270  #elif defined(WIN32)
1271  SetFilePointer(pFile->hFileWrite, fPos + CHUNK_HEADER_SIZE, NULL/*32 bit*/, FILE_BEGIN);
1272  DWORD dwBytesWritten;
1273  WriteFile(pFile->hFileWrite, &ListType, 4, &dwBytesWritten, NULL);
1274  #else
1275  fseek(pFile->hFileWrite, fPos + CHUNK_HEADER_SIZE, SEEK_SET);
1276  fwrite(&ListType, 4, 1, pFile->hFileWrite);
1277  #endif // POSIX
1278  }
1279 
1281  #if DEBUG
1282  std::cout << "List::LoadSubChunks()";
1283  #endif // DEBUG
1284  if (!pSubChunks) {
1285  pSubChunks = new ChunkList();
1286  pSubChunksMap = new ChunkMap();
1287  #if defined(WIN32)
1288  if (pFile->hFileRead == INVALID_HANDLE_VALUE) return;
1289  #else
1290  if (!pFile->hFileRead) return;
1291  #endif
1292  unsigned long uiOriginalPos = GetPos();
1293  SetPos(0); // jump to beginning of list chunk body
1294  while (RemainingBytes() >= CHUNK_HEADER_SIZE) {
1295  Chunk* ck;
1296  uint32_t ckid;
1297  Read(&ckid, 4, 1);
1298  #if DEBUG
1299  std::cout << " ckid=" << convertToString(ckid) << std::endl;
1300  #endif // DEBUG
1301  if (ckid == CHUNK_ID_LIST) {
1302  ck = new RIFF::List(pFile, ulStartPos + ulPos - 4, this);
1304  }
1305  else { // simple chunk
1306  ck = new RIFF::Chunk(pFile, ulStartPos + ulPos - 4, this);
1308  }
1309  pSubChunks->push_back(ck);
1310  (*pSubChunksMap)[ckid] = ck;
1311  if (GetPos() % 2 != 0) SetPos(1, RIFF::stream_curpos); // jump over pad byte
1312  }
1313  SetPos(uiOriginalPos); // restore position before this call
1314  }
1315  }
1316 
1318  for (List* pList = GetFirstSubList(); pList; pList = GetNextSubList())
1319  pList->LoadSubChunksRecursively();
1320  }
1321 
1336  unsigned long List::WriteChunk(unsigned long ulWritePos, unsigned long ulCurrentDataOffset) {
1337  const unsigned long ulOriginalPos = ulWritePos;
1338  ulWritePos += LIST_HEADER_SIZE;
1339 
1340  if (pFile->Mode != stream_mode_read_write)
1341  throw Exception("Cannot write list chunk, file has to be opened in read+write mode");
1342 
1343  // write all subchunks (including sub list chunks) recursively
1344  if (pSubChunks) {
1345  for (ChunkList::iterator iter = pSubChunks->begin(), end = pSubChunks->end(); iter != end; ++iter) {
1346  ulWritePos = (*iter)->WriteChunk(ulWritePos, ulCurrentDataOffset);
1347  }
1348  }
1349 
1350  // update this list chunk's header
1351  CurrentChunkSize = NewChunkSize = ulWritePos - ulOriginalPos - LIST_HEADER_SIZE;
1352  WriteHeader(ulOriginalPos);
1353 
1354  // offset of this list chunk in new written file may have changed
1355  ulStartPos = ulOriginalPos + LIST_HEADER_SIZE;
1356 
1357  return ulWritePos;
1358  }
1359 
1362  if (pSubChunks) {
1363  for (ChunkList::iterator iter = pSubChunks->begin(), end = pSubChunks->end(); iter != end; ++iter) {
1364  (*iter)->__resetPos();
1365  }
1366  }
1367  }
1368 
1373  return convertToString(ListType);
1374  }
1375 
1376 
1377 
1378 // *************** File ***************
1379 // *
1380 
1381 //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
1382 #define _GET_RESIZED_CHUNKS() \
1383  (reinterpret_cast<std::set<Chunk*>*>(ResizedChunks.front()))
1384 
1399  File::File(uint32_t FileType) : List(this) {
1400  //HACK: see _GET_RESIZED_CHUNKS() comment
1401  ResizedChunks.push_back(reinterpret_cast<Chunk*>(new std::set<Chunk*>));
1402  #if defined(WIN32)
1403  hFileRead = hFileWrite = INVALID_HANDLE_VALUE;
1404  #else
1405  hFileRead = hFileWrite = 0;
1406  #endif
1407  Mode = stream_mode_closed;
1408  bEndianNative = true;
1410  ListType = FileType;
1411  }
1412 
1421  File::File(const String& path) : List(this), Filename(path) {
1422  #if DEBUG
1423  std::cout << "File::File("<<path<<")" << std::endl;
1424  #endif // DEBUG
1425  bEndianNative = true;
1426  //HACK: see _GET_RESIZED_CHUNKS() comment
1427  ResizedChunks.push_back(reinterpret_cast<Chunk*>(new std::set<Chunk*>));
1428  #if POSIX
1429  hFileRead = hFileWrite = open(path.c_str(), O_RDONLY | O_NONBLOCK);
1430  if (hFileRead <= 0) {
1431  hFileRead = hFileWrite = 0;
1432  throw RIFF::Exception("Can't open \"" + path + "\"");
1433  }
1434  #elif defined(WIN32)
1435  hFileRead = hFileWrite = CreateFile(
1436  path.c_str(), GENERIC_READ,
1437  FILE_SHARE_READ | FILE_SHARE_WRITE,
1438  NULL, OPEN_EXISTING,
1439  FILE_ATTRIBUTE_NORMAL |
1440  FILE_FLAG_RANDOM_ACCESS, NULL
1441  );
1442  if (hFileRead == INVALID_HANDLE_VALUE) {
1443  hFileRead = hFileWrite = INVALID_HANDLE_VALUE;
1444  throw RIFF::Exception("Can't open \"" + path + "\"");
1445  }
1446  #else
1447  hFileRead = hFileWrite = fopen(path.c_str(), "rb");
1448  if (!hFileRead) throw RIFF::Exception("Can't open \"" + path + "\"");
1449  #endif // POSIX
1450  Mode = stream_mode_read;
1452  ReadHeader(0);
1453  if (ChunkID != CHUNK_ID_RIFF && ChunkID != CHUNK_ID_RIFX) {
1454  throw RIFF::Exception("Not a RIFF file");
1455  }
1456  }
1457 
1459  return Filename;
1460  }
1461 
1463  return Mode;
1464  }
1465 
1477  if (NewMode != Mode) {
1478  switch (NewMode) {
1479  case stream_mode_read:
1480  #if POSIX
1481  if (hFileRead) close(hFileRead);
1482  hFileRead = hFileWrite = open(Filename.c_str(), O_RDONLY | O_NONBLOCK);
1483  if (hFileRead < 0) {
1484  hFileRead = hFileWrite = 0;
1485  throw Exception("Could not (re)open file \"" + Filename + "\" in read mode");
1486  }
1487  #elif defined(WIN32)
1488  if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
1489  hFileRead = hFileWrite = CreateFile(
1490  Filename.c_str(), GENERIC_READ,
1491  FILE_SHARE_READ | FILE_SHARE_WRITE,
1492  NULL, OPEN_EXISTING,
1493  FILE_ATTRIBUTE_NORMAL |
1494  FILE_FLAG_RANDOM_ACCESS,
1495  NULL
1496  );
1497  if (hFileRead == INVALID_HANDLE_VALUE) {
1498  hFileRead = hFileWrite = INVALID_HANDLE_VALUE;
1499  throw Exception("Could not (re)open file \"" + Filename + "\" in read mode");
1500  }
1501  #else
1502  if (hFileRead) fclose(hFileRead);
1503  hFileRead = hFileWrite = fopen(Filename.c_str(), "rb");
1504  if (!hFileRead) throw Exception("Could not (re)open file \"" + Filename + "\" in read mode");
1505  #endif
1506  __resetPos(); // reset read/write position of ALL 'Chunk' objects
1507  break;
1509  #if POSIX
1510  if (hFileRead) close(hFileRead);
1511  hFileRead = hFileWrite = open(Filename.c_str(), O_RDWR | O_NONBLOCK);
1512  if (hFileRead < 0) {
1513  hFileRead = hFileWrite = open(Filename.c_str(), O_RDONLY | O_NONBLOCK);
1514  throw Exception("Could not open file \"" + Filename + "\" in read+write mode");
1515  }
1516  #elif defined(WIN32)
1517  if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
1518  hFileRead = hFileWrite = CreateFile(
1519  Filename.c_str(),
1520  GENERIC_READ | GENERIC_WRITE,
1521  FILE_SHARE_READ,
1522  NULL, OPEN_ALWAYS,
1523  FILE_ATTRIBUTE_NORMAL |
1524  FILE_FLAG_RANDOM_ACCESS,
1525  NULL
1526  );
1527  if (hFileRead == INVALID_HANDLE_VALUE) {
1528  hFileRead = hFileWrite = CreateFile(
1529  Filename.c_str(), GENERIC_READ,
1530  FILE_SHARE_READ | FILE_SHARE_WRITE,
1531  NULL, OPEN_EXISTING,
1532  FILE_ATTRIBUTE_NORMAL |
1533  FILE_FLAG_RANDOM_ACCESS,
1534  NULL
1535  );
1536  throw Exception("Could not (re)open file \"" + Filename + "\" in read+write mode");
1537  }
1538  #else
1539  if (hFileRead) fclose(hFileRead);
1540  hFileRead = hFileWrite = fopen(Filename.c_str(), "r+b");
1541  if (!hFileRead) {
1542  hFileRead = hFileWrite = fopen(Filename.c_str(), "rb");
1543  throw Exception("Could not open file \"" + Filename + "\" in read+write mode");
1544  }
1545  #endif
1546  __resetPos(); // reset read/write position of ALL 'Chunk' objects
1547  break;
1548  case stream_mode_closed:
1549  #if POSIX
1550  if (hFileRead) close(hFileRead);
1551  if (hFileWrite) close(hFileWrite);
1552  #elif defined(WIN32)
1553  if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
1554  if (hFileWrite != INVALID_HANDLE_VALUE) CloseHandle(hFileWrite);
1555  #else
1556  if (hFileRead) fclose(hFileRead);
1557  if (hFileWrite) fclose(hFileWrite);
1558  #endif
1559  hFileRead = hFileWrite = 0;
1560  break;
1561  default:
1562  throw Exception("Unknown file access mode");
1563  }
1564  Mode = NewMode;
1565  return true;
1566  }
1567  return false;
1568  }
1569 
1580  #if WORDS_BIGENDIAN
1581  bEndianNative = Endian != endian_little;
1582  #else
1583  bEndianNative = Endian != endian_big;
1584  #endif
1585  }
1586 
1597  void File::Save() {
1598  // make sure the RIFF tree is built (from the original file)
1600 
1601  // reopen file in write mode
1603 
1604  // to be able to save the whole file without loading everything into
1605  // RAM and without having to store the data in a temporary file, we
1606  // enlarge the file with the sum of all _positive_ chunk size
1607  // changes, move current data towards the end of the file with the
1608  // calculated sum and finally update / rewrite the file by copying
1609  // the old data back to the right position at the beginning of the file
1610 
1611  // first we sum up all positive chunk size changes (and skip all negative ones)
1612  unsigned long ulPositiveSizeDiff = 0;
1613  std::set<Chunk*>* resizedChunks = _GET_RESIZED_CHUNKS();
1614  for (std::set<Chunk*>::const_iterator iter = resizedChunks->begin(), end = resizedChunks->end(); iter != end; ++iter) {
1615  if ((*iter)->GetNewSize() == 0) {
1616  throw Exception("There is at least one empty chunk (zero size): " + __resolveChunkPath(*iter));
1617  }
1618  unsigned long newSizePadded = (*iter)->GetNewSize() + (*iter)->GetNewSize() % 2;
1619  unsigned long oldSizePadded = (*iter)->GetSize() + (*iter)->GetSize() % 2;
1620  if (newSizePadded > oldSizePadded) ulPositiveSizeDiff += newSizePadded - oldSizePadded;
1621  }
1622 
1623  unsigned long ulWorkingFileSize = GetFileSize();
1624 
1625  // if there are positive size changes...
1626  if (ulPositiveSizeDiff > 0) {
1627  // ... we enlarge this file first ...
1628  ulWorkingFileSize += ulPositiveSizeDiff;
1629  ResizeFile(ulWorkingFileSize);
1630  // ... and move current data by the same amount towards end of file.
1631  int8_t* pCopyBuffer = new int8_t[4096];
1632  const unsigned long ulFileSize = GetSize() + RIFF_HEADER_SIZE;
1633  #if defined(WIN32)
1634  DWORD iBytesMoved = 1; // we have to pass it via pointer to the Windows API, thus the correct size must be ensured
1635  #else
1636  int iBytesMoved = 1;
1637  #endif
1638  for (unsigned long ulPos = ulFileSize; iBytesMoved > 0; ) {
1639  iBytesMoved = (ulPos < 4096) ? ulPos : 4096;
1640  ulPos -= iBytesMoved;
1641  #if POSIX
1642  lseek(hFileRead, ulPos, SEEK_SET);
1643  iBytesMoved = read(hFileRead, pCopyBuffer, iBytesMoved);
1644  lseek(hFileWrite, ulPos + ulPositiveSizeDiff, SEEK_SET);
1645  iBytesMoved = write(hFileWrite, pCopyBuffer, iBytesMoved);
1646  #elif defined(WIN32)
1647  SetFilePointer(hFileRead, ulPos, NULL/*32 bit*/, FILE_BEGIN);
1648  ReadFile(hFileRead, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
1649  SetFilePointer(hFileWrite, ulPos + ulPositiveSizeDiff, NULL/*32 bit*/, FILE_BEGIN);
1650  WriteFile(hFileWrite, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
1651  #else
1652  fseek(hFileRead, ulPos, SEEK_SET);
1653  iBytesMoved = fread(pCopyBuffer, 1, iBytesMoved, hFileRead);
1654  fseek(hFileWrite, ulPos + ulPositiveSizeDiff, SEEK_SET);
1655  iBytesMoved = fwrite(pCopyBuffer, 1, iBytesMoved, hFileWrite);
1656  #endif
1657  }
1658  delete[] pCopyBuffer;
1659  if (iBytesMoved < 0) throw Exception("Could not modify file while trying to enlarge it");
1660  }
1661 
1662  // rebuild / rewrite complete RIFF tree
1663  unsigned long ulTotalSize = WriteChunk(0, ulPositiveSizeDiff);
1664  unsigned long ulActualSize = __GetFileSize(hFileWrite);
1665 
1666  // resize file to the final size
1667  if (ulTotalSize < ulActualSize) ResizeFile(ulTotalSize);
1668 
1669  // forget all resized chunks
1670  resizedChunks->clear();
1671  }
1672 
1686  void File::Save(const String& path) {
1687  //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
1688 
1689  // make sure the RIFF tree is built (from the original file)
1691 
1692  if (Filename.length() > 0) SetMode(stream_mode_read);
1693  // open the other (new) file for writing and truncate it to zero size
1694  #if POSIX
1695  hFileWrite = open(path.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP);
1696  if (hFileWrite < 0) {
1698  throw Exception("Could not open file \"" + path + "\" for writing");
1699  }
1700  #elif defined(WIN32)
1701  hFileWrite = CreateFile(
1702  path.c_str(), GENERIC_WRITE, FILE_SHARE_READ,
1703  NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL |
1704  FILE_FLAG_RANDOM_ACCESS, NULL
1705  );
1706  if (hFileWrite == INVALID_HANDLE_VALUE) {
1708  throw Exception("Could not open file \"" + path + "\" for writing");
1709  }
1710  #else
1711  hFileWrite = fopen(path.c_str(), "w+b");
1712  if (!hFileWrite) {
1714  throw Exception("Could not open file \"" + path + "\" for writing");
1715  }
1716  #endif // POSIX
1717  Mode = stream_mode_read_write;
1718 
1719  // write complete RIFF tree to the other (new) file
1720  unsigned long ulTotalSize = WriteChunk(0, 0);
1721  unsigned long ulActualSize = __GetFileSize(hFileWrite);
1722 
1723  // resize file to the final size (if the file was originally larger)
1724  if (ulTotalSize < ulActualSize) ResizeFile(ulTotalSize);
1725 
1726  // forget all resized chunks
1727  _GET_RESIZED_CHUNKS()->clear();
1728 
1729  #if POSIX
1730  if (hFileWrite) close(hFileWrite);
1731  #elif defined(WIN32)
1732  if (hFileWrite != INVALID_HANDLE_VALUE) CloseHandle(hFileWrite);
1733  #else
1734  if (hFileWrite) fclose(hFileWrite);
1735  #endif
1737 
1738  // associate new file with this File object from now on
1739  Filename = path;
1740  Mode = (stream_mode_t) -1; // Just set it to an undefined mode ...
1741  SetMode(stream_mode_read_write); // ... so SetMode() has to reopen the file handles.
1742  }
1743 
1744  void File::ResizeFile(unsigned long ulNewSize) {
1745  #if POSIX
1746  if (ftruncate(hFileWrite, ulNewSize) < 0)
1747  throw Exception("Could not resize file \"" + Filename + "\"");
1748  #elif defined(WIN32)
1749  if (
1750  SetFilePointer(hFileWrite, ulNewSize, NULL/*32 bit*/, FILE_BEGIN) == INVALID_SET_FILE_POINTER ||
1751  !SetEndOfFile(hFileWrite)
1752  ) throw Exception("Could not resize file \"" + Filename + "\"");
1753  #else
1754  # error Sorry, this version of libgig only supports POSIX and Windows systems yet.
1755  # error Reason: portable implementation of RIFF::File::ResizeFile() is missing (yet)!
1756  #endif
1757  }
1758 
1760  #if DEBUG
1761  std::cout << "File::~File()" << std::endl;
1762  #endif // DEBUG
1763  #if POSIX
1764  if (hFileRead) close(hFileRead);
1765  #elif defined(WIN32)
1766  if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
1767  #else
1768  if (hFileRead) fclose(hFileRead);
1769  #endif // POSIX
1770  DeleteChunkList();
1771  pFile = NULL;
1772  //HACK: see _GET_RESIZED_CHUNKS() comment
1773  delete _GET_RESIZED_CHUNKS();
1774  }
1775 
1776  void File::LogAsResized(Chunk* pResizedChunk) {
1777  _GET_RESIZED_CHUNKS()->insert(pResizedChunk);
1778  }
1779 
1780  void File::UnlogResized(Chunk* pResizedChunk) {
1781  _GET_RESIZED_CHUNKS()->erase(pResizedChunk);
1782  }
1783 
1784  unsigned long File::GetFileSize() {
1785  return __GetFileSize(hFileRead);
1786  }
1787 
1788  #if POSIX
1789  unsigned long File::__GetFileSize(int hFile) {
1790  struct stat filestat;
1791  fstat(hFile, &filestat);
1792  long size = filestat.st_size;
1793  return size;
1794  }
1795  #elif defined(WIN32)
1796  unsigned long File::__GetFileSize(HANDLE hFile) {
1797  DWORD dwSize = ::GetFileSize(hFile, NULL /*32bit*/);
1798  if (dwSize == INVALID_FILE_SIZE)
1799  throw Exception("Windows FS error: could not determine file size");
1800  return dwSize;
1801  }
1802  #else // standard C functions
1803  unsigned long File::__GetFileSize(FILE* hFile) {
1804  long curpos = ftell(hFile);
1805  fseek(hFile, 0, SEEK_END);
1806  long size = ftell(hFile);
1807  fseek(hFile, curpos, SEEK_SET);
1808  return size;
1809  }
1810  #endif
1811 
1812 
1813 // *************** Exception ***************
1814 // *
1815 
1817  std::cout << "RIFF::Exception: " << Message << std::endl;
1818  }
1819 
1820 
1821 // *************** functions ***************
1822 // *
1823 
1830  return PACKAGE;
1831  }
1832 
1838  return VERSION;
1839  }
1840 
1841 } // namespace RIFF
virtual unsigned long WriteChunk(unsigned long ulWritePos, unsigned long ulCurrentDataOffset)
Write chunk persistently e.g.
Definition: RIFF.cpp:825
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:611
#define _GET_RESIZED_CHUNKS()
Definition: RIFF.cpp:1382
int16_t ReadInt16()
Reads one 16 Bit signed integer word and increments the position within the chunk.
Definition: RIFF.cpp:655
bool bEndianNative
Definition: RIFF.h:318
void UnlogResized(Chunk *pResizedChunk)
Definition: RIFF.cpp:1780
void swapBytes_16(void *Word)
Definition: RIFF.h:206
List * pParent
Definition: RIFF.h:194
#define CHUNK_ID_RIFX
Definition: RIFF.h:82
stream_whence_t
File stream position dependent to these relations.
Definition: RIFF.h:134
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:1023
String libraryName()
Returns the name of this C++ library.
Definition: RIFF.cpp:1829
File(uint32_t FileType)
Create new RIFF file.
Definition: RIFF.cpp:1399
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:157
String GetFileName()
Definition: RIFF.cpp:1458
void ReadHeader(unsigned long fPos)
Definition: RIFF.cpp:1236
void swapBytes(void *Word, unsigned long WordSize)
Definition: RIFF.h:219
stream_state_t
Current state of the file stream.
Definition: RIFF.h:127
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
#define LIST_HEADER_SIZE
Definition: RIFF.h:87
void WriteHeader(unsigned long fPos)
Definition: RIFF.cpp:1262
String libraryVersion()
Returns version of this C++ library.
Definition: RIFF.cpp:1837
List * GetSubList(uint32_t ListType)
Returns sublist chunk with list type ListType within this chunk list.
Definition: RIFF.cpp:998
void DeleteSubChunk(Chunk *pSubChunk)
Removes a sub chunk.
Definition: RIFF.cpp:1218
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'...
Definition: RIFF.cpp:500
#define CHUNK_HEADER_SIZE
Definition: RIFF.h:86
int hFileWrite
handle / descriptor for writing to (some) file
Definition: RIFF.h:309
String Filename
Definition: RIFF.h:317
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 "LIST").
Definition: RIFF.cpp:1057
stream_mode_t
Whether file stream is open in read or in read/write mode.
Definition: RIFF.h:120
std::list< Chunk * > ChunkList
Definition: RIFF.h:270
std::string String
Definition: RIFF.h:115
unsigned long GetPos()
Position within the chunk data body.
Definition: RIFF.h:161
void SetByteOrder(endian_t Endian)
Set the byte order to be used when saving.
Definition: RIFF.cpp:1579
unsigned long GetSize()
Chunk size in bytes (without header, thus the chunk data body)
Definition: RIFF.h:159
RIFF List Chunk.
Definition: RIFF.h:248
#define CHUNK_ID_RIFF
Definition: RIFF.h:81
File * pFile
Definition: RIFF.h:195
int8_t ReadInt8()
Reads one 8 Bit signed integer word and increments the position within the chunk. ...
Definition: RIFF.cpp:622
void ReadHeader(unsigned long fPos)
Definition: RIFF.cpp:102
String GetListTypeString()
Returns string representation of the lists's id.
Definition: RIFF.cpp:1372
ChunkList::iterator ListIterator
Definition: RIFF.h:276
String Message
Definition: RIFF.h:344
unsigned long ulPos
Definition: RIFF.h:197
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'...
Definition: RIFF.cpp:574
void DeleteChunkList()
Definition: RIFF.cpp:957
Chunk * GetSubChunk(uint32_t ChunkID)
Returns subchunk with chunk ID ChunkID within this chunk list.
Definition: RIFF.cpp:979
Chunk * GetNextSubChunk()
Returns the next subchunk within the list.
Definition: RIFF.cpp:1039
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:689
stream_state_t GetState()
Returns the current state of the chunk object.
Definition: RIFF.cpp:249
void LoadSubChunks()
Definition: RIFF.cpp:1280
Ordinary RIFF Chunk.
Definition: RIFF.h:153
uint32_t GetListType()
Returns unsigned integer representation of the list's ID.
Definition: RIFF.h:252
#define RIFF_HEADER_SIZE
Definition: RIFF.h:88
uint32_t ReadUint32()
Reads one 32 Bit unsigned integer word and increments the position within the chunk.
Definition: RIFF.cpp:706
ChunkList::iterator ChunksIterator
Definition: RIFF.h:275
unsigned long ulStartPos
Definition: RIFF.h:196
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:273
void swapBytes_32(void *Word)
Definition: RIFF.h:211
Chunk * AddSubChunk(uint32_t uiChunkID, uint uiBodySize)
Creates a new sub chunk.
Definition: RIFF.cpp:1159
void PrintMessage()
Definition: RIFF.cpp:1816
int hFileRead
handle / descriptor for reading from file
Definition: RIFF.h:308
ChunkMap * pSubChunksMap
Definition: RIFF.h:274
virtual ~Chunk()
Definition: RIFF.cpp:97
void LogAsResized(Chunk *pResizedChunk)
Definition: RIFF.cpp:1776
virtual void __resetPos()
Sets Chunk's read/write position to zero.
Definition: RIFF.cpp:915
uint16_t ReadUint16()
Reads one 16 Bit unsigned integer word and increments the position within the chunk.
Definition: RIFF.cpp:672
virtual void __resetPos()
Sets List Chunk's read/write position to zero and causes all sub chunks to do the same...
Definition: RIFF.cpp:1360
uint8_t * pChunkData
Definition: RIFF.h:198
uint32_t CurrentChunkSize
Definition: RIFF.h:192
endian_t
Alignment of data bytes in memory (system dependant).
Definition: RIFF.h:142
void * LoadChunkData()
Load chunk body into RAM.
Definition: RIFF.cpp:736
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's...
Definition: RIFF.cpp:426
unsigned long ulChunkDataSize
Definition: RIFF.h:199
RIFF File.
Definition: RIFF.h:295
List * AddSubList(uint32_t uiListType)
Creates a new list sub chunk.
Definition: RIFF.cpp:1198
#define CHUNK_ID_LIST
Definition: RIFF.h:83
void LoadSubChunksRecursively()
Definition: RIFF.cpp:1317
List(File *pFile, unsigned long StartPos, List *Parent)
Definition: RIFF.cpp:932
virtual unsigned long WriteChunk(unsigned long ulWritePos, unsigned long ulCurrentDataOffset)
Write list chunk persistently e.g.
Definition: RIFF.cpp:1336
void MoveSubChunk(Chunk *pSrc, Chunk *pDst)
Moves a sub chunk.
Definition: RIFF.cpp:1182
unsigned int CountSubChunks()
Returns number of subchunks within the list.
Definition: RIFF.cpp:1097
virtual ~List()
Definition: RIFF.cpp:950
String convertToString(uint32_t word)
Definition: RIFF.h:228
bool SetMode(stream_mode_t NewMode)
Change file access mode.
Definition: RIFF.cpp:1476
virtual void Save()
Save changes to same file.
Definition: RIFF.cpp:1597
Will be thrown whenever an error occurs while handling a RIFF file.
Definition: RIFF.h:342
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:1462
void ReleaseChunkData()
Free loaded chunk body from RAM.
Definition: RIFF.cpp:780
virtual ~File()
Definition: RIFF.cpp:1759
unsigned int CountSubLists()
Returns number of sublists within the list.
Definition: RIFF.cpp:1123
String GetChunkIDString()
Returns the String representation of the chunk's ID (e.g.
Definition: RIFF.cpp:183
uint32_t NewChunkSize
Definition: RIFF.h:193
void WriteHeader(unsigned long fPos)
Definition: RIFF.cpp:145
uint32_t ChunkID
Definition: RIFF.h:191
std::map< uint32_t, RIFF::Chunk * > ChunkMap
Definition: RIFF.h:269
void Resize(int iNewSize)
Resize chunk.
Definition: RIFF.cpp:805
List * GetNextSubList()
Returns the next sublist (that is a subchunk with chunk ID "LIST") within the list.
Definition: RIFF.cpp:1079
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:638
uint32_t ListType
Definition: RIFF.h:272