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  ChunkList::iterator iter = pSubChunks->begin();
960  ChunkList::iterator end = pSubChunks->end();
961  while (iter != end) {
962  delete *iter;
963  iter++;
964  }
965  delete pSubChunks;
966  pSubChunks = NULL;
967  }
968  if (pSubChunksMap) {
969  delete pSubChunksMap;
970  pSubChunksMap = NULL;
971  }
972  }
973 
985  Chunk* List::GetSubChunk(uint32_t ChunkID) {
986  #if DEBUG
987  std::cout << "List::GetSubChunk(uint32_t)" << std::endl;
988  #endif // DEBUG
990  return (*pSubChunksMap)[ChunkID];
991  }
992 
1004  List* List::GetSubList(uint32_t ListType) {
1005  #if DEBUG
1006  std::cout << "List::GetSubList(uint32_t)" << std::endl;
1007  #endif // DEBUG
1008  if (!pSubChunks) LoadSubChunks();
1009  ChunkList::iterator iter = pSubChunks->begin();
1010  ChunkList::iterator end = pSubChunks->end();
1011  while (iter != end) {
1012  if ((*iter)->GetChunkID() == CHUNK_ID_LIST) {
1013  List* l = (List*) *iter;
1014  if (l->GetListType() == ListType) return l;
1015  }
1016  iter++;
1017  }
1018  return NULL;
1019  }
1020 
1030  #if DEBUG
1031  std::cout << "List::GetFirstSubChunk()" << std::endl;
1032  #endif // DEBUG
1033  if (!pSubChunks) LoadSubChunks();
1034  ChunksIterator = pSubChunks->begin();
1035  return (ChunksIterator != pSubChunks->end()) ? *ChunksIterator : NULL;
1036  }
1037 
1046  #if DEBUG
1047  std::cout << "List::GetNextSubChunk()" << std::endl;
1048  #endif // DEBUG
1049  if (!pSubChunks) return NULL;
1050  ChunksIterator++;
1051  return (ChunksIterator != pSubChunks->end()) ? *ChunksIterator : NULL;
1052  }
1053 
1064  #if DEBUG
1065  std::cout << "List::GetFirstSubList()" << std::endl;
1066  #endif // DEBUG
1067  if (!pSubChunks) LoadSubChunks();
1068  ListIterator = pSubChunks->begin();
1069  ChunkList::iterator end = pSubChunks->end();
1070  while (ListIterator != end) {
1071  if ((*ListIterator)->GetChunkID() == CHUNK_ID_LIST) return (List*) *ListIterator;
1072  ListIterator++;
1073  }
1074  return NULL;
1075  }
1076 
1086  #if DEBUG
1087  std::cout << "List::GetNextSubList()" << std::endl;
1088  #endif // DEBUG
1089  if (!pSubChunks) return NULL;
1090  if (ListIterator == pSubChunks->end()) return NULL;
1091  ListIterator++;
1092  ChunkList::iterator end = pSubChunks->end();
1093  while (ListIterator != end) {
1094  if ((*ListIterator)->GetChunkID() == CHUNK_ID_LIST) return (List*) *ListIterator;
1095  ListIterator++;
1096  }
1097  return NULL;
1098  }
1099 
1103  unsigned int List::CountSubChunks() {
1104  if (!pSubChunks) LoadSubChunks();
1105  return pSubChunks->size();
1106  }
1107 
1112  unsigned int List::CountSubChunks(uint32_t ChunkID) {
1113  unsigned int result = 0;
1114  if (!pSubChunks) LoadSubChunks();
1115  ChunkList::iterator iter = pSubChunks->begin();
1116  ChunkList::iterator end = pSubChunks->end();
1117  while (iter != end) {
1118  if ((*iter)->GetChunkID() == ChunkID) {
1119  result++;
1120  }
1121  iter++;
1122  }
1123  return result;
1124  }
1125 
1129  unsigned int List::CountSubLists() {
1130  return CountSubChunks(CHUNK_ID_LIST);
1131  }
1132 
1137  unsigned int List::CountSubLists(uint32_t ListType) {
1138  unsigned int result = 0;
1139  if (!pSubChunks) LoadSubChunks();
1140  ChunkList::iterator iter = pSubChunks->begin();
1141  ChunkList::iterator end = pSubChunks->end();
1142  while (iter != end) {
1143  if ((*iter)->GetChunkID() == CHUNK_ID_LIST) {
1144  List* l = (List*) *iter;
1145  if (l->GetListType() == ListType) result++;
1146  }
1147  iter++;
1148  }
1149  return result;
1150  }
1151 
1165  Chunk* List::AddSubChunk(uint32_t uiChunkID, uint uiBodySize) {
1166  if (uiBodySize == 0) throw Exception("Chunk body size must be at least 1 byte");
1167  if (!pSubChunks) LoadSubChunks();
1168  Chunk* pNewChunk = new Chunk(pFile, this, uiChunkID, 0);
1169  pSubChunks->push_back(pNewChunk);
1170  (*pSubChunksMap)[uiChunkID] = pNewChunk;
1171  pNewChunk->Resize(uiBodySize);
1173  pFile->LogAsResized(this);
1174  return pNewChunk;
1175  }
1176 
1188  void List::MoveSubChunk(Chunk* pSrc, Chunk* pDst) {
1189  if (!pSubChunks) LoadSubChunks();
1190  pSubChunks->remove(pSrc);
1191  ChunkList::iterator iter = find(pSubChunks->begin(), pSubChunks->end(), pDst);
1192  pSubChunks->insert(iter, pSrc);
1193  }
1194 
1204  List* List::AddSubList(uint32_t uiListType) {
1205  if (!pSubChunks) LoadSubChunks();
1206  List* pNewListChunk = new List(pFile, this, uiListType);
1207  pSubChunks->push_back(pNewListChunk);
1208  (*pSubChunksMap)[CHUNK_ID_LIST] = pNewListChunk;
1210  pFile->LogAsResized(this);
1211  return pNewListChunk;
1212  }
1213 
1224  void List::DeleteSubChunk(Chunk* pSubChunk) {
1225  if (!pSubChunks) LoadSubChunks();
1226  pSubChunks->remove(pSubChunk);
1227  if ((*pSubChunksMap)[pSubChunk->GetChunkID()] == pSubChunk) {
1228  pSubChunksMap->erase(pSubChunk->GetChunkID());
1229  // try to find another chunk of the same chunk ID
1230  ChunkList::iterator iter = pSubChunks->begin();
1231  ChunkList::iterator end = pSubChunks->end();
1232  for (; iter != end; ++iter) {
1233  if ((*iter)->GetChunkID() == pSubChunk->GetChunkID()) {
1234  (*pSubChunksMap)[pSubChunk->GetChunkID()] = *iter;
1235  break; // we're done, stop search
1236  }
1237  }
1238  }
1239  delete pSubChunk;
1240  }
1241 
1242  void List::ReadHeader(unsigned long fPos) {
1243  #if DEBUG
1244  std::cout << "List::Readheader(ulong) ";
1245  #endif // DEBUG
1246  Chunk::ReadHeader(fPos);
1247  if (CurrentChunkSize < 4) return;
1249  #if POSIX
1250  lseek(pFile->hFileRead, fPos + CHUNK_HEADER_SIZE, SEEK_SET);
1251  read(pFile->hFileRead, &ListType, 4);
1252  #elif defined(WIN32)
1253  SetFilePointer(pFile->hFileRead, fPos + CHUNK_HEADER_SIZE, NULL/*32 bit*/, FILE_BEGIN);
1254  DWORD dwBytesRead;
1255  ReadFile(pFile->hFileRead, &ListType, 4, &dwBytesRead, NULL);
1256  #else
1257  fseek(pFile->hFileRead, fPos + CHUNK_HEADER_SIZE, SEEK_SET);
1258  fread(&ListType, 4, 1, pFile->hFileRead);
1259  #endif // POSIX
1260  #if DEBUG
1261  std::cout << "listType=" << convertToString(ListType) << std::endl;
1262  #endif // DEBUG
1263  if (!pFile->bEndianNative) {
1264  //swapBytes_32(&ListType);
1265  }
1266  }
1267 
1268  void List::WriteHeader(unsigned long fPos) {
1269  // the four list type bytes officially belong the chunk's body in the RIFF format
1270  NewChunkSize += 4;
1271  Chunk::WriteHeader(fPos);
1272  NewChunkSize -= 4; // just revert the +4 incrementation
1273  #if POSIX
1274  lseek(pFile->hFileWrite, fPos + CHUNK_HEADER_SIZE, SEEK_SET);
1275  write(pFile->hFileWrite, &ListType, 4);
1276  #elif defined(WIN32)
1277  SetFilePointer(pFile->hFileWrite, fPos + CHUNK_HEADER_SIZE, NULL/*32 bit*/, FILE_BEGIN);
1278  DWORD dwBytesWritten;
1279  WriteFile(pFile->hFileWrite, &ListType, 4, &dwBytesWritten, NULL);
1280  #else
1281  fseek(pFile->hFileWrite, fPos + CHUNK_HEADER_SIZE, SEEK_SET);
1282  fwrite(&ListType, 4, 1, pFile->hFileWrite);
1283  #endif // POSIX
1284  }
1285 
1287  #if DEBUG
1288  std::cout << "List::LoadSubChunks()";
1289  #endif // DEBUG
1290  if (!pSubChunks) {
1291  pSubChunks = new ChunkList();
1292  pSubChunksMap = new ChunkMap();
1293  #if defined(WIN32)
1294  if (pFile->hFileRead == INVALID_HANDLE_VALUE) return;
1295  #else
1296  if (!pFile->hFileRead) return;
1297  #endif
1298  unsigned long uiOriginalPos = GetPos();
1299  SetPos(0); // jump to beginning of list chunk body
1300  while (RemainingBytes() >= CHUNK_HEADER_SIZE) {
1301  Chunk* ck;
1302  uint32_t ckid;
1303  Read(&ckid, 4, 1);
1304  #if DEBUG
1305  std::cout << " ckid=" << convertToString(ckid) << std::endl;
1306  #endif // DEBUG
1307  if (ckid == CHUNK_ID_LIST) {
1308  ck = new RIFF::List(pFile, ulStartPos + ulPos - 4, this);
1310  }
1311  else { // simple chunk
1312  ck = new RIFF::Chunk(pFile, ulStartPos + ulPos - 4, this);
1314  }
1315  pSubChunks->push_back(ck);
1316  (*pSubChunksMap)[ckid] = ck;
1317  if (GetPos() % 2 != 0) SetPos(1, RIFF::stream_curpos); // jump over pad byte
1318  }
1319  SetPos(uiOriginalPos); // restore position before this call
1320  }
1321  }
1322 
1324  for (List* pList = GetFirstSubList(); pList; pList = GetNextSubList())
1325  pList->LoadSubChunksRecursively();
1326  }
1327 
1342  unsigned long List::WriteChunk(unsigned long ulWritePos, unsigned long ulCurrentDataOffset) {
1343  const unsigned long ulOriginalPos = ulWritePos;
1344  ulWritePos += LIST_HEADER_SIZE;
1345 
1346  if (pFile->Mode != stream_mode_read_write)
1347  throw Exception("Cannot write list chunk, file has to be opened in read+write mode");
1348 
1349  // write all subchunks (including sub list chunks) recursively
1350  if (pSubChunks) {
1351  for (ChunkList::iterator iter = pSubChunks->begin(), end = pSubChunks->end(); iter != end; ++iter) {
1352  ulWritePos = (*iter)->WriteChunk(ulWritePos, ulCurrentDataOffset);
1353  }
1354  }
1355 
1356  // update this list chunk's header
1357  CurrentChunkSize = NewChunkSize = ulWritePos - ulOriginalPos - LIST_HEADER_SIZE;
1358  WriteHeader(ulOriginalPos);
1359 
1360  // offset of this list chunk in new written file may have changed
1361  ulStartPos = ulOriginalPos + LIST_HEADER_SIZE;
1362 
1363  return ulWritePos;
1364  }
1365 
1368  if (pSubChunks) {
1369  for (ChunkList::iterator iter = pSubChunks->begin(), end = pSubChunks->end(); iter != end; ++iter) {
1370  (*iter)->__resetPos();
1371  }
1372  }
1373  }
1374 
1379  return convertToString(ListType);
1380  }
1381 
1382 
1383 
1384 // *************** File ***************
1385 // *
1386 
1387 //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
1388 #define _GET_RESIZED_CHUNKS() \
1389  (reinterpret_cast<std::set<Chunk*>*>(ResizedChunks.front()))
1390 
1405  File::File(uint32_t FileType) : List(this) {
1406  //HACK: see _GET_RESIZED_CHUNKS() comment
1407  ResizedChunks.push_back(reinterpret_cast<Chunk*>(new std::set<Chunk*>));
1408  #if defined(WIN32)
1409  hFileRead = hFileWrite = INVALID_HANDLE_VALUE;
1410  #else
1411  hFileRead = hFileWrite = 0;
1412  #endif
1413  Mode = stream_mode_closed;
1414  bEndianNative = true;
1416  ListType = FileType;
1417  }
1418 
1427  File::File(const String& path) : List(this), Filename(path) {
1428  #if DEBUG
1429  std::cout << "File::File("<<path<<")" << std::endl;
1430  #endif // DEBUG
1431  bEndianNative = true;
1432  //HACK: see _GET_RESIZED_CHUNKS() comment
1433  ResizedChunks.push_back(reinterpret_cast<Chunk*>(new std::set<Chunk*>));
1434  #if POSIX
1435  hFileRead = hFileWrite = open(path.c_str(), O_RDONLY | O_NONBLOCK);
1436  if (hFileRead <= 0) {
1437  hFileRead = hFileWrite = 0;
1438  throw RIFF::Exception("Can't open \"" + path + "\"");
1439  }
1440  #elif defined(WIN32)
1441  hFileRead = hFileWrite = CreateFile(
1442  path.c_str(), GENERIC_READ,
1443  FILE_SHARE_READ | FILE_SHARE_WRITE,
1444  NULL, OPEN_EXISTING,
1445  FILE_ATTRIBUTE_NORMAL |
1446  FILE_FLAG_RANDOM_ACCESS, NULL
1447  );
1448  if (hFileRead == INVALID_HANDLE_VALUE) {
1449  hFileRead = hFileWrite = INVALID_HANDLE_VALUE;
1450  throw RIFF::Exception("Can't open \"" + path + "\"");
1451  }
1452  #else
1453  hFileRead = hFileWrite = fopen(path.c_str(), "rb");
1454  if (!hFileRead) throw RIFF::Exception("Can't open \"" + path + "\"");
1455  #endif // POSIX
1456  Mode = stream_mode_read;
1458  ReadHeader(0);
1459  if (ChunkID != CHUNK_ID_RIFF && ChunkID != CHUNK_ID_RIFX) {
1460  throw RIFF::Exception("Not a RIFF file");
1461  }
1462  }
1463 
1465  return Filename;
1466  }
1467 
1469  return Mode;
1470  }
1471 
1483  if (NewMode != Mode) {
1484  switch (NewMode) {
1485  case stream_mode_read:
1486  #if POSIX
1487  if (hFileRead) close(hFileRead);
1488  hFileRead = hFileWrite = open(Filename.c_str(), O_RDONLY | O_NONBLOCK);
1489  if (hFileRead < 0) {
1490  hFileRead = hFileWrite = 0;
1491  throw Exception("Could not (re)open file \"" + Filename + "\" in read mode");
1492  }
1493  #elif defined(WIN32)
1494  if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
1495  hFileRead = hFileWrite = CreateFile(
1496  Filename.c_str(), GENERIC_READ,
1497  FILE_SHARE_READ | FILE_SHARE_WRITE,
1498  NULL, OPEN_EXISTING,
1499  FILE_ATTRIBUTE_NORMAL |
1500  FILE_FLAG_RANDOM_ACCESS,
1501  NULL
1502  );
1503  if (hFileRead == INVALID_HANDLE_VALUE) {
1504  hFileRead = hFileWrite = INVALID_HANDLE_VALUE;
1505  throw Exception("Could not (re)open file \"" + Filename + "\" in read mode");
1506  }
1507  #else
1508  if (hFileRead) fclose(hFileRead);
1509  hFileRead = hFileWrite = fopen(Filename.c_str(), "rb");
1510  if (!hFileRead) throw Exception("Could not (re)open file \"" + Filename + "\" in read mode");
1511  #endif
1512  __resetPos(); // reset read/write position of ALL 'Chunk' objects
1513  break;
1515  #if POSIX
1516  if (hFileRead) close(hFileRead);
1517  hFileRead = hFileWrite = open(Filename.c_str(), O_RDWR | O_NONBLOCK);
1518  if (hFileRead < 0) {
1519  hFileRead = hFileWrite = open(Filename.c_str(), O_RDONLY | O_NONBLOCK);
1520  throw Exception("Could not open file \"" + Filename + "\" in read+write mode");
1521  }
1522  #elif defined(WIN32)
1523  if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
1524  hFileRead = hFileWrite = CreateFile(
1525  Filename.c_str(),
1526  GENERIC_READ | GENERIC_WRITE,
1527  FILE_SHARE_READ,
1528  NULL, OPEN_ALWAYS,
1529  FILE_ATTRIBUTE_NORMAL |
1530  FILE_FLAG_RANDOM_ACCESS,
1531  NULL
1532  );
1533  if (hFileRead == INVALID_HANDLE_VALUE) {
1534  hFileRead = hFileWrite = CreateFile(
1535  Filename.c_str(), GENERIC_READ,
1536  FILE_SHARE_READ | FILE_SHARE_WRITE,
1537  NULL, OPEN_EXISTING,
1538  FILE_ATTRIBUTE_NORMAL |
1539  FILE_FLAG_RANDOM_ACCESS,
1540  NULL
1541  );
1542  throw Exception("Could not (re)open file \"" + Filename + "\" in read+write mode");
1543  }
1544  #else
1545  if (hFileRead) fclose(hFileRead);
1546  hFileRead = hFileWrite = fopen(Filename.c_str(), "r+b");
1547  if (!hFileRead) {
1548  hFileRead = hFileWrite = fopen(Filename.c_str(), "rb");
1549  throw Exception("Could not open file \"" + Filename + "\" in read+write mode");
1550  }
1551  #endif
1552  __resetPos(); // reset read/write position of ALL 'Chunk' objects
1553  break;
1554  case stream_mode_closed:
1555  #if POSIX
1556  if (hFileRead) close(hFileRead);
1557  if (hFileWrite) close(hFileWrite);
1558  #elif defined(WIN32)
1559  if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
1560  if (hFileWrite != INVALID_HANDLE_VALUE) CloseHandle(hFileWrite);
1561  #else
1562  if (hFileRead) fclose(hFileRead);
1563  if (hFileWrite) fclose(hFileWrite);
1564  #endif
1565  hFileRead = hFileWrite = 0;
1566  break;
1567  default:
1568  throw Exception("Unknown file access mode");
1569  }
1570  Mode = NewMode;
1571  return true;
1572  }
1573  return false;
1574  }
1575 
1586  #if WORDS_BIGENDIAN
1587  bEndianNative = Endian != endian_little;
1588  #else
1589  bEndianNative = Endian != endian_big;
1590  #endif
1591  }
1592 
1603  void File::Save() {
1604  // make sure the RIFF tree is built (from the original file)
1606 
1607  // reopen file in write mode
1609 
1610  // to be able to save the whole file without loading everything into
1611  // RAM and without having to store the data in a temporary file, we
1612  // enlarge the file with the sum of all _positive_ chunk size
1613  // changes, move current data towards the end of the file with the
1614  // calculated sum and finally update / rewrite the file by copying
1615  // the old data back to the right position at the beginning of the file
1616 
1617  // first we sum up all positive chunk size changes (and skip all negative ones)
1618  unsigned long ulPositiveSizeDiff = 0;
1619  std::set<Chunk*>* resizedChunks = _GET_RESIZED_CHUNKS();
1620  for (std::set<Chunk*>::const_iterator iter = resizedChunks->begin(), end = resizedChunks->end(); iter != end; ++iter) {
1621  if ((*iter)->GetNewSize() == 0) {
1622  throw Exception("There is at least one empty chunk (zero size): " + __resolveChunkPath(*iter));
1623  }
1624  unsigned long newSizePadded = (*iter)->GetNewSize() + (*iter)->GetNewSize() % 2;
1625  unsigned long oldSizePadded = (*iter)->GetSize() + (*iter)->GetSize() % 2;
1626  if (newSizePadded > oldSizePadded) ulPositiveSizeDiff += newSizePadded - oldSizePadded;
1627  }
1628 
1629  unsigned long ulWorkingFileSize = GetFileSize();
1630 
1631  // if there are positive size changes...
1632  if (ulPositiveSizeDiff > 0) {
1633  // ... we enlarge this file first ...
1634  ulWorkingFileSize += ulPositiveSizeDiff;
1635  ResizeFile(ulWorkingFileSize);
1636  // ... and move current data by the same amount towards end of file.
1637  int8_t* pCopyBuffer = new int8_t[4096];
1638  const unsigned long ulFileSize = GetSize() + RIFF_HEADER_SIZE;
1639  #if defined(WIN32)
1640  DWORD iBytesMoved = 1; // we have to pass it via pointer to the Windows API, thus the correct size must be ensured
1641  #else
1642  int iBytesMoved = 1;
1643  #endif
1644  for (unsigned long ulPos = ulFileSize; iBytesMoved > 0; ) {
1645  iBytesMoved = (ulPos < 4096) ? ulPos : 4096;
1646  ulPos -= iBytesMoved;
1647  #if POSIX
1648  lseek(hFileRead, ulPos, SEEK_SET);
1649  iBytesMoved = read(hFileRead, pCopyBuffer, iBytesMoved);
1650  lseek(hFileWrite, ulPos + ulPositiveSizeDiff, SEEK_SET);
1651  iBytesMoved = write(hFileWrite, pCopyBuffer, iBytesMoved);
1652  #elif defined(WIN32)
1653  SetFilePointer(hFileRead, ulPos, NULL/*32 bit*/, FILE_BEGIN);
1654  ReadFile(hFileRead, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
1655  SetFilePointer(hFileWrite, ulPos + ulPositiveSizeDiff, NULL/*32 bit*/, FILE_BEGIN);
1656  WriteFile(hFileWrite, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
1657  #else
1658  fseek(hFileRead, ulPos, SEEK_SET);
1659  iBytesMoved = fread(pCopyBuffer, 1, iBytesMoved, hFileRead);
1660  fseek(hFileWrite, ulPos + ulPositiveSizeDiff, SEEK_SET);
1661  iBytesMoved = fwrite(pCopyBuffer, 1, iBytesMoved, hFileWrite);
1662  #endif
1663  }
1664  delete[] pCopyBuffer;
1665  if (iBytesMoved < 0) throw Exception("Could not modify file while trying to enlarge it");
1666  }
1667 
1668  // rebuild / rewrite complete RIFF tree
1669  unsigned long ulTotalSize = WriteChunk(0, ulPositiveSizeDiff);
1670  unsigned long ulActualSize = __GetFileSize(hFileWrite);
1671 
1672  // resize file to the final size
1673  if (ulTotalSize < ulActualSize) ResizeFile(ulTotalSize);
1674 
1675  // forget all resized chunks
1676  resizedChunks->clear();
1677  }
1678 
1692  void File::Save(const String& path) {
1693  //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
1694 
1695  // make sure the RIFF tree is built (from the original file)
1697 
1698  if (Filename.length() > 0) SetMode(stream_mode_read);
1699  // open the other (new) file for writing and truncate it to zero size
1700  #if POSIX
1701  hFileWrite = open(path.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP);
1702  if (hFileWrite < 0) {
1704  throw Exception("Could not open file \"" + path + "\" for writing");
1705  }
1706  #elif defined(WIN32)
1707  hFileWrite = CreateFile(
1708  path.c_str(), GENERIC_WRITE, FILE_SHARE_READ,
1709  NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL |
1710  FILE_FLAG_RANDOM_ACCESS, NULL
1711  );
1712  if (hFileWrite == INVALID_HANDLE_VALUE) {
1714  throw Exception("Could not open file \"" + path + "\" for writing");
1715  }
1716  #else
1717  hFileWrite = fopen(path.c_str(), "w+b");
1718  if (!hFileWrite) {
1720  throw Exception("Could not open file \"" + path + "\" for writing");
1721  }
1722  #endif // POSIX
1723  Mode = stream_mode_read_write;
1724 
1725  // write complete RIFF tree to the other (new) file
1726  unsigned long ulTotalSize = WriteChunk(0, 0);
1727  unsigned long ulActualSize = __GetFileSize(hFileWrite);
1728 
1729  // resize file to the final size (if the file was originally larger)
1730  if (ulTotalSize < ulActualSize) ResizeFile(ulTotalSize);
1731 
1732  // forget all resized chunks
1733  _GET_RESIZED_CHUNKS()->clear();
1734 
1735  #if POSIX
1736  if (hFileWrite) close(hFileWrite);
1737  #elif defined(WIN32)
1738  if (hFileWrite != INVALID_HANDLE_VALUE) CloseHandle(hFileWrite);
1739  #else
1740  if (hFileWrite) fclose(hFileWrite);
1741  #endif
1743 
1744  // associate new file with this File object from now on
1745  Filename = path;
1746  Mode = (stream_mode_t) -1; // Just set it to an undefined mode ...
1747  SetMode(stream_mode_read_write); // ... so SetMode() has to reopen the file handles.
1748  }
1749 
1750  void File::ResizeFile(unsigned long ulNewSize) {
1751  #if POSIX
1752  if (ftruncate(hFileWrite, ulNewSize) < 0)
1753  throw Exception("Could not resize file \"" + Filename + "\"");
1754  #elif defined(WIN32)
1755  if (
1756  SetFilePointer(hFileWrite, ulNewSize, NULL/*32 bit*/, FILE_BEGIN) == INVALID_SET_FILE_POINTER ||
1757  !SetEndOfFile(hFileWrite)
1758  ) throw Exception("Could not resize file \"" + Filename + "\"");
1759  #else
1760  # error Sorry, this version of libgig only supports POSIX and Windows systems yet.
1761  # error Reason: portable implementation of RIFF::File::ResizeFile() is missing (yet)!
1762  #endif
1763  }
1764 
1766  #if DEBUG
1767  std::cout << "File::~File()" << std::endl;
1768  #endif // DEBUG
1769  #if POSIX
1770  if (hFileRead) close(hFileRead);
1771  #elif defined(WIN32)
1772  if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
1773  #else
1774  if (hFileRead) fclose(hFileRead);
1775  #endif // POSIX
1776  DeleteChunkList();
1777  pFile = NULL;
1778  //HACK: see _GET_RESIZED_CHUNKS() comment
1779  delete _GET_RESIZED_CHUNKS();
1780  }
1781 
1782  void File::LogAsResized(Chunk* pResizedChunk) {
1783  _GET_RESIZED_CHUNKS()->insert(pResizedChunk);
1784  }
1785 
1786  void File::UnlogResized(Chunk* pResizedChunk) {
1787  _GET_RESIZED_CHUNKS()->erase(pResizedChunk);
1788  }
1789 
1790  unsigned long File::GetFileSize() {
1791  return __GetFileSize(hFileRead);
1792  }
1793 
1794  #if POSIX
1795  unsigned long File::__GetFileSize(int hFile) {
1796  struct stat filestat;
1797  fstat(hFile, &filestat);
1798  long size = filestat.st_size;
1799  return size;
1800  }
1801  #elif defined(WIN32)
1802  unsigned long File::__GetFileSize(HANDLE hFile) {
1803  DWORD dwSize = ::GetFileSize(hFile, NULL /*32bit*/);
1804  if (dwSize == INVALID_FILE_SIZE)
1805  throw Exception("Windows FS error: could not determine file size");
1806  return dwSize;
1807  }
1808  #else // standard C functions
1809  unsigned long File::__GetFileSize(FILE* hFile) {
1810  long curpos = ftell(hFile);
1811  fseek(hFile, 0, SEEK_END);
1812  long size = ftell(hFile);
1813  fseek(hFile, curpos, SEEK_SET);
1814  return size;
1815  }
1816  #endif
1817 
1818 
1819 // *************** Exception ***************
1820 // *
1821 
1823  std::cout << "RIFF::Exception: " << Message << std::endl;
1824  }
1825 
1826 
1827 // *************** functions ***************
1828 // *
1829 
1836  return PACKAGE;
1837  }
1838 
1844  return VERSION;
1845  }
1846 
1847 } // namespace RIFF