libgig  3.3.0.19svn2660
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-2014 by Christian Schoenebeck *
6  * <cuse@users.sourceforge.net> *
7  * *
8  * This library is free software; you can redistribute it and/or modify *
9  * it under the terms of the GNU General Public License as published by *
10  * the Free Software Foundation; either version 2 of the License, or *
11  * (at your option) any later version. *
12  * *
13  * This library is distributed in the hope that it will be useful, *
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16  * GNU General Public License for more details. *
17  * *
18  * You should have received a copy of the GNU General Public License *
19  * along with this library; if not, write to the Free Software *
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, *
21  * MA 02111-1307 USA *
22  ***************************************************************************/
23 
24 #include <algorithm>
25 #include <set>
26 #include <string.h>
27 
28 #include "RIFF.h"
29 
30 #include "helper.h"
31 
32 namespace RIFF {
33 
34 // *************** Internal functions **************
35 // *
36 
38  static String __resolveChunkPath(Chunk* pCk) {
39  String sPath;
40  for (Chunk* pChunk = pCk; pChunk; pChunk = pChunk->GetParent()) {
41  if (pChunk->GetChunkID() == CHUNK_ID_LIST) {
42  List* pList = (List*) pChunk;
43  sPath = "->'" + pList->GetListTypeString() + "'" + sPath;
44  } else {
45  sPath = "->'" + pChunk->GetChunkIDString() + "'" + sPath;
46  }
47  }
48  return sPath;
49  }
50 
51 
52 
53 // *************** Chunk **************
54 // *
55 
56  Chunk::Chunk(File* pFile) {
57  #if DEBUG
58  std::cout << "Chunk::Chunk(File* pFile)" << std::endl;
59  #endif // DEBUG
60  ulPos = 0;
61  pParent = NULL;
62  pChunkData = NULL;
63  CurrentChunkSize = 0;
64  NewChunkSize = 0;
65  ulChunkDataSize = 0;
67  this->pFile = pFile;
68  }
69 
70  Chunk::Chunk(File* pFile, unsigned long StartPos, List* Parent) {
71  #if DEBUG
72  std::cout << "Chunk::Chunk(File*,ulong,bool,List*),StartPos=" << StartPos << std::endl;
73  #endif // DEBUG
74  this->pFile = pFile;
75  ulStartPos = StartPos + CHUNK_HEADER_SIZE;
76  pParent = Parent;
77  ulPos = 0;
78  pChunkData = NULL;
79  CurrentChunkSize = 0;
80  NewChunkSize = 0;
81  ulChunkDataSize = 0;
82  ReadHeader(StartPos);
83  }
84 
85  Chunk::Chunk(File* pFile, List* pParent, uint32_t uiChunkID, uint uiBodySize) {
86  this->pFile = pFile;
87  ulStartPos = 0; // arbitrary usually, since it will be updated when we write the chunk
88  this->pParent = pParent;
89  ulPos = 0;
90  pChunkData = NULL;
91  ChunkID = uiChunkID;
92  ulChunkDataSize = 0;
93  CurrentChunkSize = 0;
94  NewChunkSize = uiBodySize;
95  }
96 
98  if (pFile) pFile->UnlogResized(this);
99  if (pChunkData) delete[] pChunkData;
100  }
101 
102  void Chunk::ReadHeader(unsigned long fPos) {
103  #if DEBUG
104  std::cout << "Chunk::Readheader(" << fPos << ") ";
105  #endif // DEBUG
106  ChunkID = 0;
108  #if POSIX
109  if (lseek(pFile->hFileRead, fPos, SEEK_SET) != -1) {
110  read(pFile->hFileRead, &ChunkID, 4);
111  read(pFile->hFileRead, &CurrentChunkSize, 4);
112  #elif defined(WIN32)
113  if (SetFilePointer(pFile->hFileRead, fPos, NULL/*32 bit*/, FILE_BEGIN) != INVALID_SET_FILE_POINTER) {
114  DWORD dwBytesRead;
115  ReadFile(pFile->hFileRead, &ChunkID, 4, &dwBytesRead, NULL);
116  ReadFile(pFile->hFileRead, &CurrentChunkSize, 4, &dwBytesRead, NULL);
117  #else
118  if (!fseek(pFile->hFileRead, fPos, SEEK_SET)) {
119  fread(&ChunkID, 4, 1, pFile->hFileRead);
120  fread(&CurrentChunkSize, 4, 1, pFile->hFileRead);
121  #endif // POSIX
122  #if WORDS_BIGENDIAN
123  if (ChunkID == CHUNK_ID_RIFF) {
124  pFile->bEndianNative = false;
125  }
126  #else // little endian
127  if (ChunkID == CHUNK_ID_RIFX) {
128  pFile->bEndianNative = false;
130  }
131  #endif // WORDS_BIGENDIAN
132  if (!pFile->bEndianNative) {
133  //swapBytes_32(&ChunkID);
135  }
136  #if DEBUG
137  std::cout << "ckID=" << convertToString(ChunkID) << " ";
138  std::cout << "ckSize=" << CurrentChunkSize << " ";
139  std::cout << "bEndianNative=" << pFile->bEndianNative << std::endl;
140  #endif // DEBUG
142  }
143  }
144 
145  void Chunk::WriteHeader(unsigned long fPos) {
146  uint32_t uiNewChunkID = ChunkID;
147  if (ChunkID == CHUNK_ID_RIFF) {
148  #if WORDS_BIGENDIAN
149  if (pFile->bEndianNative) uiNewChunkID = CHUNK_ID_RIFX;
150  #else // little endian
151  if (!pFile->bEndianNative) uiNewChunkID = CHUNK_ID_RIFX;
152  #endif // WORDS_BIGENDIAN
153  }
154 
155  uint32_t uiNewChunkSize = NewChunkSize;
156  if (!pFile->bEndianNative) {
157  swapBytes_32(&uiNewChunkSize);
158  }
159 
160  #if POSIX
161  if (lseek(pFile->hFileWrite, fPos, SEEK_SET) != -1) {
162  write(pFile->hFileWrite, &uiNewChunkID, 4);
163  write(pFile->hFileWrite, &uiNewChunkSize, 4);
164  }
165  #elif defined(WIN32)
166  if (SetFilePointer(pFile->hFileWrite, fPos, NULL/*32 bit*/, FILE_BEGIN) != INVALID_SET_FILE_POINTER) {
167  DWORD dwBytesWritten;
168  WriteFile(pFile->hFileWrite, &uiNewChunkID, 4, &dwBytesWritten, NULL);
169  WriteFile(pFile->hFileWrite, &uiNewChunkSize, 4, &dwBytesWritten, NULL);
170  }
171  #else
172  if (!fseek(pFile->hFileWrite, fPos, SEEK_SET)) {
173  fwrite(&uiNewChunkID, 4, 1, pFile->hFileWrite);
174  fwrite(&uiNewChunkSize, 4, 1, pFile->hFileWrite);
175  }
176  #endif // POSIX
177  }
178 
184  return convertToString(ChunkID);
185  }
186 
199  unsigned long Chunk::SetPos(unsigned long Where, stream_whence_t Whence) {
200  #if DEBUG
201  std::cout << "Chunk::SetPos(ulong)" << std::endl;
202  #endif // DEBUG
203  switch (Whence) {
204  case stream_curpos:
205  ulPos += Where;
206  break;
207  case stream_end:
208  ulPos = CurrentChunkSize - 1 - Where;
209  break;
210  case stream_backward:
211  ulPos -= Where;
212  break;
213  case stream_start: default:
214  ulPos = Where;
215  break;
216  }
218  return ulPos;
219  }
220 
231  unsigned long Chunk::RemainingBytes() {
232  #if DEBUG
233  std::cout << "Chunk::Remainingbytes()=" << CurrentChunkSize - ulPos << std::endl;
234  #endif // DEBUG
235  return (CurrentChunkSize > ulPos) ? CurrentChunkSize - ulPos : 0;
236  }
237 
250  #if DEBUG
251  std::cout << "Chunk::GetState()" << std::endl;
252  #endif // DEBUG
253  #if POSIX
254  if (pFile->hFileRead == 0) return stream_closed;
255  #elif defined (WIN32)
256  if (pFile->hFileRead == INVALID_HANDLE_VALUE)
257  return stream_closed;
258  #else
259  if (pFile->hFileRead == NULL) return stream_closed;
260  #endif // POSIX
261  if (ulPos < CurrentChunkSize) return stream_ready;
262  else return stream_end_reached;
263  }
264 
280  unsigned long Chunk::Read(void* pData, unsigned long WordCount, unsigned long WordSize) {
281  #if DEBUG
282  std::cout << "Chunk::Read(void*,ulong,ulong)" << std::endl;
283  #endif // DEBUG
284  //if (ulStartPos == 0) return 0; // is only 0 if this is a new chunk, so nothing to read (yet)
285  if (ulPos >= CurrentChunkSize) return 0;
286  if (ulPos + WordCount * WordSize >= CurrentChunkSize) WordCount = (CurrentChunkSize - ulPos) / WordSize;
287  #if POSIX
288  if (lseek(pFile->hFileRead, ulStartPos + ulPos, SEEK_SET) < 0) return 0;
289  unsigned long readWords = read(pFile->hFileRead, pData, WordCount * WordSize);
290  if (readWords < 1) return 0;
291  readWords /= WordSize;
292  #elif defined(WIN32)
293  if (SetFilePointer(pFile->hFileRead, ulStartPos + ulPos, NULL/*32 bit*/, FILE_BEGIN) == INVALID_SET_FILE_POINTER) return 0;
294  DWORD readWords;
295  ReadFile(pFile->hFileRead, pData, WordCount * WordSize, &readWords, NULL);
296  if (readWords < 1) return 0;
297  readWords /= WordSize;
298  #else // standard C functions
299  if (fseek(pFile->hFileRead, ulStartPos + ulPos, SEEK_SET)) return 0;
300  unsigned long readWords = fread(pData, WordSize, WordCount, pFile->hFileRead);
301  #endif // POSIX
302  if (!pFile->bEndianNative && WordSize != 1) {
303  switch (WordSize) {
304  case 2:
305  for (unsigned long iWord = 0; iWord < readWords; iWord++)
306  swapBytes_16((uint16_t*) pData + iWord);
307  break;
308  case 4:
309  for (unsigned long iWord = 0; iWord < readWords; iWord++)
310  swapBytes_32((uint32_t*) pData + iWord);
311  break;
312  default:
313  for (unsigned long iWord = 0; iWord < readWords; iWord++)
314  swapBytes((uint8_t*) pData + iWord * WordSize, WordSize);
315  break;
316  }
317  }
318  SetPos(readWords * WordSize, stream_curpos);
319  return readWords;
320  }
321 
338  unsigned long Chunk::Write(void* pData, unsigned long WordCount, unsigned long WordSize) {
339  if (pFile->Mode != stream_mode_read_write)
340  throw Exception("Cannot write data to chunk, file has to be opened in read+write mode first");
341  if (ulPos >= CurrentChunkSize || ulPos + WordCount * WordSize > CurrentChunkSize)
342  throw Exception("End of chunk reached while trying to write data");
343  if (!pFile->bEndianNative && WordSize != 1) {
344  switch (WordSize) {
345  case 2:
346  for (unsigned long iWord = 0; iWord < WordCount; iWord++)
347  swapBytes_16((uint16_t*) pData + iWord);
348  break;
349  case 4:
350  for (unsigned long iWord = 0; iWord < WordCount; iWord++)
351  swapBytes_32((uint32_t*) pData + iWord);
352  break;
353  default:
354  for (unsigned long iWord = 0; iWord < WordCount; iWord++)
355  swapBytes((uint8_t*) pData + iWord * WordSize, WordSize);
356  break;
357  }
358  }
359  #if POSIX
360  if (lseek(pFile->hFileWrite, ulStartPos + ulPos, SEEK_SET) < 0) {
361  throw Exception("Could not seek to position " + ToString(ulPos) +
362  " in chunk (" + ToString(ulStartPos + ulPos) + " in file)");
363  }
364  unsigned long writtenWords = write(pFile->hFileWrite, pData, WordCount * WordSize);
365  if (writtenWords < 1) throw Exception("POSIX IO Error while trying to write chunk data");
366  writtenWords /= WordSize;
367  #elif defined(WIN32)
368  if (SetFilePointer(pFile->hFileWrite, ulStartPos + ulPos, NULL/*32 bit*/, FILE_BEGIN) == INVALID_SET_FILE_POINTER) {
369  throw Exception("Could not seek to position " + ToString(ulPos) +
370  " in chunk (" + ToString(ulStartPos + ulPos) + " in file)");
371  }
372  DWORD writtenWords;
373  WriteFile(pFile->hFileWrite, pData, WordCount * WordSize, &writtenWords, NULL);
374  if (writtenWords < 1) throw Exception("Windows IO Error while trying to write chunk data");
375  writtenWords /= WordSize;
376  #else // standard C functions
377  if (fseek(pFile->hFileWrite, ulStartPos + ulPos, SEEK_SET)) {
378  throw Exception("Could not seek to position " + ToString(ulPos) +
379  " in chunk (" + ToString(ulStartPos + ulPos) + " in file)");
380  }
381  unsigned long writtenWords = fwrite(pData, WordSize, WordCount, pFile->hFileWrite);
382  #endif // POSIX
383  SetPos(writtenWords * WordSize, stream_curpos);
384  return writtenWords;
385  }
386 
388  unsigned long Chunk::ReadSceptical(void* pData, unsigned long WordCount, unsigned long WordSize) {
389  unsigned long readWords = Read(pData, WordCount, WordSize);
390  if (readWords != WordCount) throw RIFF::Exception("End of chunk data reached.");
391  return readWords;
392  }
393 
405  unsigned long Chunk::ReadInt8(int8_t* pData, unsigned long WordCount) {
406  #if DEBUG
407  std::cout << "Chunk::ReadInt8(int8_t*,ulong)" << std::endl;
408  #endif // DEBUG
409  return ReadSceptical(pData, WordCount, 1);
410  }
411 
426  unsigned long Chunk::WriteInt8(int8_t* pData, unsigned long WordCount) {
427  return Write(pData, WordCount, 1);
428  }
429 
442  unsigned long Chunk::ReadUint8(uint8_t* pData, unsigned long WordCount) {
443  #if DEBUG
444  std::cout << "Chunk::ReadUint8(uint8_t*,ulong)" << std::endl;
445  #endif // DEBUG
446  return ReadSceptical(pData, WordCount, 1);
447  }
448 
463  unsigned long Chunk::WriteUint8(uint8_t* pData, unsigned long WordCount) {
464  return Write(pData, WordCount, 1);
465  }
466 
479  unsigned long Chunk::ReadInt16(int16_t* pData, unsigned long WordCount) {
480  #if DEBUG
481  std::cout << "Chunk::ReadInt16(int16_t*,ulong)" << std::endl;
482  #endif // DEBUG
483  return ReadSceptical(pData, WordCount, 2);
484  }
485 
500  unsigned long Chunk::WriteInt16(int16_t* pData, unsigned long WordCount) {
501  return Write(pData, WordCount, 2);
502  }
503 
516  unsigned long Chunk::ReadUint16(uint16_t* pData, unsigned long WordCount) {
517  #if DEBUG
518  std::cout << "Chunk::ReadUint16(uint16_t*,ulong)" << std::endl;
519  #endif // DEBUG
520  return ReadSceptical(pData, WordCount, 2);
521  }
522 
537  unsigned long Chunk::WriteUint16(uint16_t* pData, unsigned long WordCount) {
538  return Write(pData, WordCount, 2);
539  }
540 
553  unsigned long Chunk::ReadInt32(int32_t* pData, unsigned long WordCount) {
554  #if DEBUG
555  std::cout << "Chunk::ReadInt32(int32_t*,ulong)" << std::endl;
556  #endif // DEBUG
557  return ReadSceptical(pData, WordCount, 4);
558  }
559 
574  unsigned long Chunk::WriteInt32(int32_t* pData, unsigned long WordCount) {
575  return Write(pData, WordCount, 4);
576  }
577 
590  unsigned long Chunk::ReadUint32(uint32_t* pData, unsigned long WordCount) {
591  #if DEBUG
592  std::cout << "Chunk::ReadUint32(uint32_t*,ulong)" << std::endl;
593  #endif // DEBUG
594  return ReadSceptical(pData, WordCount, 4);
595  }
596 
607  void Chunk::ReadString(String& s, int size) {
608  char* buf = new char[size];
609  ReadSceptical(buf, 1, size);
610  s.assign(buf, std::find(buf, buf + size, '\0'));
611  delete[] buf;
612  }
613 
628  unsigned long Chunk::WriteUint32(uint32_t* pData, unsigned long WordCount) {
629  return Write(pData, WordCount, 4);
630  }
631 
639  int8_t Chunk::ReadInt8() {
640  #if DEBUG
641  std::cout << "Chunk::ReadInt8()" << std::endl;
642  #endif // DEBUG
643  int8_t word;
644  ReadSceptical(&word,1,1);
645  return word;
646  }
647 
655  uint8_t Chunk::ReadUint8() {
656  #if DEBUG
657  std::cout << "Chunk::ReadUint8()" << std::endl;
658  #endif // DEBUG
659  uint8_t word;
660  ReadSceptical(&word,1,1);
661  return word;
662  }
663 
672  int16_t Chunk::ReadInt16() {
673  #if DEBUG
674  std::cout << "Chunk::ReadInt16()" << std::endl;
675  #endif // DEBUG
676  int16_t word;
677  ReadSceptical(&word,1,2);
678  return word;
679  }
680 
689  uint16_t Chunk::ReadUint16() {
690  #if DEBUG
691  std::cout << "Chunk::ReadUint16()" << std::endl;
692  #endif // DEBUG
693  uint16_t word;
694  ReadSceptical(&word,1,2);
695  return word;
696  }
697 
706  int32_t Chunk::ReadInt32() {
707  #if DEBUG
708  std::cout << "Chunk::ReadInt32()" << std::endl;
709  #endif // DEBUG
710  int32_t word;
711  ReadSceptical(&word,1,4);
712  return word;
713  }
714 
723  uint32_t Chunk::ReadUint32() {
724  #if DEBUG
725  std::cout << "Chunk::ReadUint32()" << std::endl;
726  #endif // DEBUG
727  uint32_t word;
728  ReadSceptical(&word,1,4);
729  return word;
730  }
731 
754  if (!pChunkData && pFile->Filename != "" /*&& ulStartPos != 0*/) {
755  #if POSIX
756  if (lseek(pFile->hFileRead, ulStartPos, SEEK_SET) == -1) return NULL;
757  #elif defined(WIN32)
758  if (SetFilePointer(pFile->hFileRead, ulStartPos, NULL/*32 bit*/, FILE_BEGIN) == INVALID_SET_FILE_POINTER) return NULL;
759  #else
760  if (fseek(pFile->hFileRead, ulStartPos, SEEK_SET)) return NULL;
761  #endif // POSIX
762  unsigned long ulBufferSize = (CurrentChunkSize > NewChunkSize) ? CurrentChunkSize : NewChunkSize;
763  pChunkData = new uint8_t[ulBufferSize];
764  if (!pChunkData) return NULL;
765  memset(pChunkData, 0, ulBufferSize);
766  #if POSIX
767  unsigned long readWords = read(pFile->hFileRead, pChunkData, GetSize());
768  #elif defined(WIN32)
769  DWORD readWords;
770  ReadFile(pFile->hFileRead, pChunkData, GetSize(), &readWords, NULL);
771  #else
772  unsigned long readWords = fread(pChunkData, 1, GetSize(), pFile->hFileRead);
773  #endif // POSIX
774  if (readWords != GetSize()) {
775  delete[] pChunkData;
776  return (pChunkData = NULL);
777  }
778  ulChunkDataSize = ulBufferSize;
779  } else if (NewChunkSize > ulChunkDataSize) {
780  uint8_t* pNewBuffer = new uint8_t[NewChunkSize];
781  if (!pNewBuffer) throw Exception("Could not enlarge chunk data buffer to " + ToString(NewChunkSize) + " bytes");
782  memset(pNewBuffer, 0 , NewChunkSize);
783  memcpy(pNewBuffer, pChunkData, ulChunkDataSize);
784  delete[] pChunkData;
785  pChunkData = pNewBuffer;
787  }
788  return pChunkData;
789  }
790 
798  if (pChunkData) {
799  delete[] pChunkData;
800  pChunkData = NULL;
801  }
802  }
803 
822  void Chunk::Resize(int iNewSize) {
823  if (iNewSize <= 0)
824  throw Exception("There is at least one empty chunk (zero size): " + __resolveChunkPath(this));
825  if (NewChunkSize == iNewSize) return;
826  NewChunkSize = iNewSize;
827  pFile->LogAsResized(this);
828  }
829 
842  unsigned long Chunk::WriteChunk(unsigned long ulWritePos, unsigned long ulCurrentDataOffset) {
843  const unsigned long ulOriginalPos = ulWritePos;
844  ulWritePos += CHUNK_HEADER_SIZE;
845 
846  if (pFile->Mode != stream_mode_read_write)
847  throw Exception("Cannot write list chunk, file has to be opened in read+write mode");
848 
849  // if the whole chunk body was loaded into RAM
850  if (pChunkData) {
851  // make sure chunk data buffer in RAM is at least as large as the new chunk size
852  LoadChunkData();
853  // write chunk data from RAM persistently to the file
854  #if POSIX
855  lseek(pFile->hFileWrite, ulWritePos, SEEK_SET);
857  throw Exception("Writing Chunk data (from RAM) failed");
858  }
859  #elif defined(WIN32)
860  SetFilePointer(pFile->hFileWrite, ulWritePos, NULL/*32 bit*/, FILE_BEGIN);
861  DWORD dwBytesWritten;
862  WriteFile(pFile->hFileWrite, pChunkData, NewChunkSize, &dwBytesWritten, NULL);
863  if (dwBytesWritten != NewChunkSize) {
864  throw Exception("Writing Chunk data (from RAM) failed");
865  }
866  #else
867  fseek(pFile->hFileWrite, ulWritePos, SEEK_SET);
868  if (fwrite(pChunkData, 1, NewChunkSize, pFile->hFileWrite) != NewChunkSize) {
869  throw Exception("Writing Chunk data (from RAM) failed");
870  }
871  #endif // POSIX
872  } else {
873  // move chunk data from the end of the file to the appropriate position
874  int8_t* pCopyBuffer = new int8_t[4096];
875  unsigned long ulToMove = (NewChunkSize < CurrentChunkSize) ? NewChunkSize : CurrentChunkSize;
876  #if defined(WIN32)
877  DWORD iBytesMoved = 1; // we have to pass it via pointer to the Windows API, thus the correct size must be ensured
878  #else
879  int iBytesMoved = 1;
880  #endif
881  for (unsigned long ulOffset = 0; ulToMove > 0 && iBytesMoved > 0; ulOffset += iBytesMoved, ulToMove -= iBytesMoved) {
882  iBytesMoved = (ulToMove < 4096) ? ulToMove : 4096;
883  #if POSIX
884  lseek(pFile->hFileRead, ulStartPos + ulCurrentDataOffset + ulOffset, SEEK_SET);
885  iBytesMoved = read(pFile->hFileRead, pCopyBuffer, iBytesMoved);
886  lseek(pFile->hFileWrite, ulWritePos + ulOffset, SEEK_SET);
887  iBytesMoved = write(pFile->hFileWrite, pCopyBuffer, iBytesMoved);
888  #elif defined(WIN32)
889  SetFilePointer(pFile->hFileRead, ulStartPos + ulCurrentDataOffset + ulOffset, NULL/*32 bit*/, FILE_BEGIN);
890  ReadFile(pFile->hFileRead, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
891  SetFilePointer(pFile->hFileWrite, ulWritePos + ulOffset, NULL/*32 bit*/, FILE_BEGIN);
892  WriteFile(pFile->hFileWrite, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
893  #else
894  fseek(pFile->hFileRead, ulStartPos + ulCurrentDataOffset + ulOffset, SEEK_SET);
895  iBytesMoved = fread(pCopyBuffer, 1, iBytesMoved, pFile->hFileRead);
896  fseek(pFile->hFileWrite, ulWritePos + ulOffset, SEEK_SET);
897  iBytesMoved = fwrite(pCopyBuffer, 1, iBytesMoved, pFile->hFileWrite);
898  #endif
899  }
900  delete[] pCopyBuffer;
901  if (iBytesMoved < 0) throw Exception("Writing Chunk data (from file) failed");
902  }
903 
904  // update this chunk's header
906  WriteHeader(ulOriginalPos);
907 
908  // update chunk's position pointers
909  ulStartPos = ulOriginalPos + CHUNK_HEADER_SIZE;
910  ulPos = 0;
911 
912  // add pad byte if needed
913  if ((ulStartPos + NewChunkSize) % 2 != 0) {
914  const char cPadByte = 0;
915  #if POSIX
916  lseek(pFile->hFileWrite, ulStartPos + NewChunkSize, SEEK_SET);
917  write(pFile->hFileWrite, &cPadByte, 1);
918  #elif defined(WIN32)
919  SetFilePointer(pFile->hFileWrite, ulStartPos + NewChunkSize, NULL/*32 bit*/, FILE_BEGIN);
920  DWORD dwBytesWritten;
921  WriteFile(pFile->hFileWrite, &cPadByte, 1, &dwBytesWritten, NULL);
922  #else
923  fseek(pFile->hFileWrite, ulStartPos + NewChunkSize, SEEK_SET);
924  fwrite(&cPadByte, 1, 1, pFile->hFileWrite);
925  #endif
926  return ulStartPos + NewChunkSize + 1;
927  }
928 
929  return ulStartPos + NewChunkSize;
930  }
931 
933  ulPos = 0;
934  }
935 
936 
937 
938 // *************** List ***************
939 // *
940 
941  List::List(File* pFile) : Chunk(pFile) {
942  #if DEBUG
943  std::cout << "List::List(File* pFile)" << std::endl;
944  #endif // DEBUG
945  pSubChunks = NULL;
946  pSubChunksMap = NULL;
947  }
948 
949  List::List(File* pFile, unsigned long StartPos, List* Parent)
950  : Chunk(pFile, StartPos, Parent) {
951  #if DEBUG
952  std::cout << "List::List(File*,ulong,bool,List*)" << std::endl;
953  #endif // DEBUG
954  pSubChunks = NULL;
955  pSubChunksMap = NULL;
956  ReadHeader(StartPos);
957  ulStartPos = StartPos + LIST_HEADER_SIZE;
958  }
959 
960  List::List(File* pFile, List* pParent, uint32_t uiListID)
961  : Chunk(pFile, pParent, CHUNK_ID_LIST, 0) {
962  pSubChunks = NULL;
963  pSubChunksMap = NULL;
964  ListType = uiListID;
965  }
966 
968  #if DEBUG
969  std::cout << "List::~List()" << std::endl;
970  #endif // DEBUG
971  DeleteChunkList();
972  }
973 
975  if (pSubChunks) {
976  ChunkList::iterator iter = pSubChunks->begin();
977  ChunkList::iterator end = pSubChunks->end();
978  while (iter != end) {
979  delete *iter;
980  iter++;
981  }
982  delete pSubChunks;
983  pSubChunks = NULL;
984  }
985  if (pSubChunksMap) {
986  delete pSubChunksMap;
987  pSubChunksMap = NULL;
988  }
989  }
990 
1002  Chunk* List::GetSubChunk(uint32_t ChunkID) {
1003  #if DEBUG
1004  std::cout << "List::GetSubChunk(uint32_t)" << std::endl;
1005  #endif // DEBUG
1006  if (!pSubChunksMap) LoadSubChunks();
1007  return (*pSubChunksMap)[ChunkID];
1008  }
1009 
1021  List* List::GetSubList(uint32_t ListType) {
1022  #if DEBUG
1023  std::cout << "List::GetSubList(uint32_t)" << std::endl;
1024  #endif // DEBUG
1025  if (!pSubChunks) LoadSubChunks();
1026  ChunkList::iterator iter = pSubChunks->begin();
1027  ChunkList::iterator end = pSubChunks->end();
1028  while (iter != end) {
1029  if ((*iter)->GetChunkID() == CHUNK_ID_LIST) {
1030  List* l = (List*) *iter;
1031  if (l->GetListType() == ListType) return l;
1032  }
1033  iter++;
1034  }
1035  return NULL;
1036  }
1037 
1047  #if DEBUG
1048  std::cout << "List::GetFirstSubChunk()" << std::endl;
1049  #endif // DEBUG
1050  if (!pSubChunks) LoadSubChunks();
1051  ChunksIterator = pSubChunks->begin();
1052  return (ChunksIterator != pSubChunks->end()) ? *ChunksIterator : NULL;
1053  }
1054 
1063  #if DEBUG
1064  std::cout << "List::GetNextSubChunk()" << std::endl;
1065  #endif // DEBUG
1066  if (!pSubChunks) return NULL;
1067  ChunksIterator++;
1068  return (ChunksIterator != pSubChunks->end()) ? *ChunksIterator : NULL;
1069  }
1070 
1081  #if DEBUG
1082  std::cout << "List::GetFirstSubList()" << std::endl;
1083  #endif // DEBUG
1084  if (!pSubChunks) LoadSubChunks();
1085  ListIterator = pSubChunks->begin();
1086  ChunkList::iterator end = pSubChunks->end();
1087  while (ListIterator != end) {
1088  if ((*ListIterator)->GetChunkID() == CHUNK_ID_LIST) return (List*) *ListIterator;
1089  ListIterator++;
1090  }
1091  return NULL;
1092  }
1093 
1103  #if DEBUG
1104  std::cout << "List::GetNextSubList()" << std::endl;
1105  #endif // DEBUG
1106  if (!pSubChunks) return NULL;
1107  if (ListIterator == pSubChunks->end()) return NULL;
1108  ListIterator++;
1109  ChunkList::iterator end = pSubChunks->end();
1110  while (ListIterator != end) {
1111  if ((*ListIterator)->GetChunkID() == CHUNK_ID_LIST) return (List*) *ListIterator;
1112  ListIterator++;
1113  }
1114  return NULL;
1115  }
1116 
1120  unsigned int List::CountSubChunks() {
1121  if (!pSubChunks) LoadSubChunks();
1122  return pSubChunks->size();
1123  }
1124 
1129  unsigned int List::CountSubChunks(uint32_t ChunkID) {
1130  unsigned int result = 0;
1131  if (!pSubChunks) LoadSubChunks();
1132  ChunkList::iterator iter = pSubChunks->begin();
1133  ChunkList::iterator end = pSubChunks->end();
1134  while (iter != end) {
1135  if ((*iter)->GetChunkID() == ChunkID) {
1136  result++;
1137  }
1138  iter++;
1139  }
1140  return result;
1141  }
1142 
1146  unsigned int List::CountSubLists() {
1147  return CountSubChunks(CHUNK_ID_LIST);
1148  }
1149 
1154  unsigned int List::CountSubLists(uint32_t ListType) {
1155  unsigned int result = 0;
1156  if (!pSubChunks) LoadSubChunks();
1157  ChunkList::iterator iter = pSubChunks->begin();
1158  ChunkList::iterator end = pSubChunks->end();
1159  while (iter != end) {
1160  if ((*iter)->GetChunkID() == CHUNK_ID_LIST) {
1161  List* l = (List*) *iter;
1162  if (l->GetListType() == ListType) result++;
1163  }
1164  iter++;
1165  }
1166  return result;
1167  }
1168 
1182  Chunk* List::AddSubChunk(uint32_t uiChunkID, uint uiBodySize) {
1183  if (uiBodySize == 0) throw Exception("Chunk body size must be at least 1 byte");
1184  if (!pSubChunks) LoadSubChunks();
1185  Chunk* pNewChunk = new Chunk(pFile, this, uiChunkID, 0);
1186  pSubChunks->push_back(pNewChunk);
1187  (*pSubChunksMap)[uiChunkID] = pNewChunk;
1188  pNewChunk->Resize(uiBodySize);
1190  pFile->LogAsResized(this);
1191  return pNewChunk;
1192  }
1193 
1205  void List::MoveSubChunk(Chunk* pSrc, Chunk* pDst) {
1206  if (!pSubChunks) LoadSubChunks();
1207  pSubChunks->remove(pSrc);
1208  ChunkList::iterator iter = find(pSubChunks->begin(), pSubChunks->end(), pDst);
1209  pSubChunks->insert(iter, pSrc);
1210  }
1211 
1220  void List::MoveSubChunk(Chunk* pSrc, List* pNewParent) {
1221  if (pNewParent == this || !pNewParent) return;
1222  if (!pSubChunks) LoadSubChunks();
1223  if (!pNewParent->pSubChunks) pNewParent->LoadSubChunks();
1224  pSubChunks->remove(pSrc);
1225  pNewParent->pSubChunks->push_back(pSrc);
1226  // update chunk id map of this List
1227  if ((*pSubChunksMap)[pSrc->GetChunkID()] == pSrc) {
1228  pSubChunksMap->erase(pSrc->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() == pSrc->GetChunkID()) {
1234  (*pSubChunksMap)[pSrc->GetChunkID()] = *iter;
1235  break; // we're done, stop search
1236  }
1237  }
1238  }
1239  // update chunk id map of other list
1240  if (!(*pNewParent->pSubChunksMap)[pSrc->GetChunkID()])
1241  (*pNewParent->pSubChunksMap)[pSrc->GetChunkID()] = pSrc;
1242  }
1243 
1253  List* List::AddSubList(uint32_t uiListType) {
1254  if (!pSubChunks) LoadSubChunks();
1255  List* pNewListChunk = new List(pFile, this, uiListType);
1256  pSubChunks->push_back(pNewListChunk);
1257  (*pSubChunksMap)[CHUNK_ID_LIST] = pNewListChunk;
1259  pFile->LogAsResized(this);
1260  return pNewListChunk;
1261  }
1262 
1273  void List::DeleteSubChunk(Chunk* pSubChunk) {
1274  if (!pSubChunks) LoadSubChunks();
1275  pSubChunks->remove(pSubChunk);
1276  if ((*pSubChunksMap)[pSubChunk->GetChunkID()] == pSubChunk) {
1277  pSubChunksMap->erase(pSubChunk->GetChunkID());
1278  // try to find another chunk of the same chunk ID
1279  ChunkList::iterator iter = pSubChunks->begin();
1280  ChunkList::iterator end = pSubChunks->end();
1281  for (; iter != end; ++iter) {
1282  if ((*iter)->GetChunkID() == pSubChunk->GetChunkID()) {
1283  (*pSubChunksMap)[pSubChunk->GetChunkID()] = *iter;
1284  break; // we're done, stop search
1285  }
1286  }
1287  }
1288  delete pSubChunk;
1289  }
1290 
1291  void List::ReadHeader(unsigned long fPos) {
1292  #if DEBUG
1293  std::cout << "List::Readheader(ulong) ";
1294  #endif // DEBUG
1295  Chunk::ReadHeader(fPos);
1296  if (CurrentChunkSize < 4) return;
1298  #if POSIX
1299  lseek(pFile->hFileRead, fPos + CHUNK_HEADER_SIZE, SEEK_SET);
1300  read(pFile->hFileRead, &ListType, 4);
1301  #elif defined(WIN32)
1302  SetFilePointer(pFile->hFileRead, fPos + CHUNK_HEADER_SIZE, NULL/*32 bit*/, FILE_BEGIN);
1303  DWORD dwBytesRead;
1304  ReadFile(pFile->hFileRead, &ListType, 4, &dwBytesRead, NULL);
1305  #else
1306  fseek(pFile->hFileRead, fPos + CHUNK_HEADER_SIZE, SEEK_SET);
1307  fread(&ListType, 4, 1, pFile->hFileRead);
1308  #endif // POSIX
1309  #if DEBUG
1310  std::cout << "listType=" << convertToString(ListType) << std::endl;
1311  #endif // DEBUG
1312  if (!pFile->bEndianNative) {
1313  //swapBytes_32(&ListType);
1314  }
1315  }
1316 
1317  void List::WriteHeader(unsigned long fPos) {
1318  // the four list type bytes officially belong the chunk's body in the RIFF format
1319  NewChunkSize += 4;
1320  Chunk::WriteHeader(fPos);
1321  NewChunkSize -= 4; // just revert the +4 incrementation
1322  #if POSIX
1323  lseek(pFile->hFileWrite, fPos + CHUNK_HEADER_SIZE, SEEK_SET);
1324  write(pFile->hFileWrite, &ListType, 4);
1325  #elif defined(WIN32)
1326  SetFilePointer(pFile->hFileWrite, fPos + CHUNK_HEADER_SIZE, NULL/*32 bit*/, FILE_BEGIN);
1327  DWORD dwBytesWritten;
1328  WriteFile(pFile->hFileWrite, &ListType, 4, &dwBytesWritten, NULL);
1329  #else
1330  fseek(pFile->hFileWrite, fPos + CHUNK_HEADER_SIZE, SEEK_SET);
1331  fwrite(&ListType, 4, 1, pFile->hFileWrite);
1332  #endif // POSIX
1333  }
1334 
1336  #if DEBUG
1337  std::cout << "List::LoadSubChunks()";
1338  #endif // DEBUG
1339  if (!pSubChunks) {
1340  pSubChunks = new ChunkList();
1341  pSubChunksMap = new ChunkMap();
1342  #if defined(WIN32)
1343  if (pFile->hFileRead == INVALID_HANDLE_VALUE) return;
1344  #else
1345  if (!pFile->hFileRead) return;
1346  #endif
1347  unsigned long uiOriginalPos = GetPos();
1348  SetPos(0); // jump to beginning of list chunk body
1349  while (RemainingBytes() >= CHUNK_HEADER_SIZE) {
1350  Chunk* ck;
1351  uint32_t ckid;
1352  Read(&ckid, 4, 1);
1353  #if DEBUG
1354  std::cout << " ckid=" << convertToString(ckid) << std::endl;
1355  #endif // DEBUG
1356  if (ckid == CHUNK_ID_LIST) {
1357  ck = new RIFF::List(pFile, ulStartPos + ulPos - 4, this);
1359  }
1360  else { // simple chunk
1361  ck = new RIFF::Chunk(pFile, ulStartPos + ulPos - 4, this);
1363  }
1364  pSubChunks->push_back(ck);
1365  (*pSubChunksMap)[ckid] = ck;
1366  if (GetPos() % 2 != 0) SetPos(1, RIFF::stream_curpos); // jump over pad byte
1367  }
1368  SetPos(uiOriginalPos); // restore position before this call
1369  }
1370  }
1371 
1373  for (List* pList = GetFirstSubList(); pList; pList = GetNextSubList())
1374  pList->LoadSubChunksRecursively();
1375  }
1376 
1391  unsigned long List::WriteChunk(unsigned long ulWritePos, unsigned long ulCurrentDataOffset) {
1392  const unsigned long ulOriginalPos = ulWritePos;
1393  ulWritePos += LIST_HEADER_SIZE;
1394 
1395  if (pFile->Mode != stream_mode_read_write)
1396  throw Exception("Cannot write list chunk, file has to be opened in read+write mode");
1397 
1398  // write all subchunks (including sub list chunks) recursively
1399  if (pSubChunks) {
1400  for (ChunkList::iterator iter = pSubChunks->begin(), end = pSubChunks->end(); iter != end; ++iter) {
1401  ulWritePos = (*iter)->WriteChunk(ulWritePos, ulCurrentDataOffset);
1402  }
1403  }
1404 
1405  // update this list chunk's header
1406  CurrentChunkSize = NewChunkSize = ulWritePos - ulOriginalPos - LIST_HEADER_SIZE;
1407  WriteHeader(ulOriginalPos);
1408 
1409  // offset of this list chunk in new written file may have changed
1410  ulStartPos = ulOriginalPos + LIST_HEADER_SIZE;
1411 
1412  return ulWritePos;
1413  }
1414 
1417  if (pSubChunks) {
1418  for (ChunkList::iterator iter = pSubChunks->begin(), end = pSubChunks->end(); iter != end; ++iter) {
1419  (*iter)->__resetPos();
1420  }
1421  }
1422  }
1423 
1428  return convertToString(ListType);
1429  }
1430 
1431 
1432 
1433 // *************** File ***************
1434 // *
1435 
1436 //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
1437 #define _GET_RESIZED_CHUNKS() \
1438  (reinterpret_cast<std::set<Chunk*>*>(ResizedChunks.front()))
1439 
1454  File::File(uint32_t FileType)
1455  : List(this), bIsNewFile(true), Layout(layout_standard)
1456  {
1457  //HACK: see _GET_RESIZED_CHUNKS() comment
1458  ResizedChunks.push_back(reinterpret_cast<Chunk*>(new std::set<Chunk*>));
1459  #if defined(WIN32)
1460  hFileRead = hFileWrite = INVALID_HANDLE_VALUE;
1461  #else
1462  hFileRead = hFileWrite = 0;
1463  #endif
1464  Mode = stream_mode_closed;
1465  bEndianNative = true;
1467  ListType = FileType;
1468  }
1469 
1478  File::File(const String& path)
1479  : List(this), Filename(path), bIsNewFile(false), Layout(layout_standard)
1480  {
1481  #if DEBUG
1482  std::cout << "File::File("<<path<<")" << std::endl;
1483  #endif // DEBUG
1484  bEndianNative = true;
1485  try {
1486  __openExistingFile(path);
1487  if (ChunkID != CHUNK_ID_RIFF && ChunkID != CHUNK_ID_RIFX) {
1488  throw RIFF::Exception("Not a RIFF file");
1489  }
1490  }
1491  catch (...) {
1492  Cleanup();
1493  throw;
1494  }
1495  }
1496 
1522  File::File(const String& path, uint32_t FileType, endian_t Endian, layout_t layout)
1523  : List(this), Filename(path), bIsNewFile(false), Layout(layout)
1524  {
1525  SetByteOrder(Endian);
1526  try {
1527  __openExistingFile(path, &FileType);
1528  }
1529  catch (...) {
1530  Cleanup();
1531  throw;
1532  }
1533  }
1534 
1545  void File::__openExistingFile(const String& path, uint32_t* FileType) {
1546  //HACK: see _GET_RESIZED_CHUNKS() comment
1547  ResizedChunks.push_back(reinterpret_cast<Chunk*>(new std::set<Chunk*>));
1548  #if POSIX
1549  hFileRead = hFileWrite = open(path.c_str(), O_RDONLY | O_NONBLOCK);
1550  if (hFileRead <= 0) {
1551  hFileRead = hFileWrite = 0;
1552  throw RIFF::Exception("Can't open \"" + path + "\"");
1553  }
1554  #elif defined(WIN32)
1555  hFileRead = hFileWrite = CreateFile(
1556  path.c_str(), GENERIC_READ,
1557  FILE_SHARE_READ | FILE_SHARE_WRITE,
1558  NULL, OPEN_EXISTING,
1559  FILE_ATTRIBUTE_NORMAL |
1560  FILE_FLAG_RANDOM_ACCESS, NULL
1561  );
1562  if (hFileRead == INVALID_HANDLE_VALUE) {
1563  hFileRead = hFileWrite = INVALID_HANDLE_VALUE;
1564  throw RIFF::Exception("Can't open \"" + path + "\"");
1565  }
1566  #else
1567  hFileRead = hFileWrite = fopen(path.c_str(), "rb");
1568  if (!hFileRead) throw RIFF::Exception("Can't open \"" + path + "\"");
1569  #endif // POSIX
1570  Mode = stream_mode_read;
1571  switch (Layout) {
1572  case layout_standard: // this is a normal RIFF file
1574  ReadHeader(0);
1575  if (FileType && ChunkID != *FileType)
1576  throw RIFF::Exception("Invalid file container ID");
1577  break;
1578  case layout_flat: // non-standard RIFF-alike file
1579  ulStartPos = 0;
1580  NewChunkSize = CurrentChunkSize = GetFileSize();
1581  if (FileType) {
1582  uint32_t ckid;
1583  if (Read(&ckid, 4, 1) != 4) {
1584  throw RIFF::Exception("Invalid file header ID (premature end of header)");
1585  } else if (ckid != *FileType) {
1586  String s = " (expected '" + convertToString(*FileType) + "' but got '" + convertToString(ckid) + "')";
1587  throw RIFF::Exception("Invalid file header ID" + s);
1588  }
1589  SetPos(0); // reset to first byte of file
1590  }
1591  LoadSubChunks();
1592  break;
1593  }
1594  }
1595 
1597  return Filename;
1598  }
1599 
1600  void File::SetFileName(const String& path) {
1601  Filename = path;
1602  }
1603 
1605  return Mode;
1606  }
1607 
1609  return Layout;
1610  }
1611 
1623  if (NewMode != Mode) {
1624  switch (NewMode) {
1625  case stream_mode_read:
1626  #if POSIX
1627  if (hFileRead) close(hFileRead);
1628  hFileRead = hFileWrite = open(Filename.c_str(), O_RDONLY | O_NONBLOCK);
1629  if (hFileRead < 0) {
1630  hFileRead = hFileWrite = 0;
1631  throw Exception("Could not (re)open file \"" + Filename + "\" in read mode");
1632  }
1633  #elif defined(WIN32)
1634  if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
1635  hFileRead = hFileWrite = CreateFile(
1636  Filename.c_str(), GENERIC_READ,
1637  FILE_SHARE_READ | FILE_SHARE_WRITE,
1638  NULL, OPEN_EXISTING,
1639  FILE_ATTRIBUTE_NORMAL |
1640  FILE_FLAG_RANDOM_ACCESS,
1641  NULL
1642  );
1643  if (hFileRead == INVALID_HANDLE_VALUE) {
1644  hFileRead = hFileWrite = INVALID_HANDLE_VALUE;
1645  throw Exception("Could not (re)open file \"" + Filename + "\" in read mode");
1646  }
1647  #else
1648  if (hFileRead) fclose(hFileRead);
1649  hFileRead = hFileWrite = fopen(Filename.c_str(), "rb");
1650  if (!hFileRead) throw Exception("Could not (re)open file \"" + Filename + "\" in read mode");
1651  #endif
1652  __resetPos(); // reset read/write position of ALL 'Chunk' objects
1653  break;
1655  #if POSIX
1656  if (hFileRead) close(hFileRead);
1657  hFileRead = hFileWrite = open(Filename.c_str(), O_RDWR | O_NONBLOCK);
1658  if (hFileRead < 0) {
1659  hFileRead = hFileWrite = open(Filename.c_str(), O_RDONLY | O_NONBLOCK);
1660  throw Exception("Could not open file \"" + Filename + "\" in read+write mode");
1661  }
1662  #elif defined(WIN32)
1663  if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
1664  hFileRead = hFileWrite = CreateFile(
1665  Filename.c_str(),
1666  GENERIC_READ | GENERIC_WRITE,
1667  FILE_SHARE_READ,
1668  NULL, OPEN_ALWAYS,
1669  FILE_ATTRIBUTE_NORMAL |
1670  FILE_FLAG_RANDOM_ACCESS,
1671  NULL
1672  );
1673  if (hFileRead == INVALID_HANDLE_VALUE) {
1674  hFileRead = hFileWrite = CreateFile(
1675  Filename.c_str(), GENERIC_READ,
1676  FILE_SHARE_READ | FILE_SHARE_WRITE,
1677  NULL, OPEN_EXISTING,
1678  FILE_ATTRIBUTE_NORMAL |
1679  FILE_FLAG_RANDOM_ACCESS,
1680  NULL
1681  );
1682  throw Exception("Could not (re)open file \"" + Filename + "\" in read+write mode");
1683  }
1684  #else
1685  if (hFileRead) fclose(hFileRead);
1686  hFileRead = hFileWrite = fopen(Filename.c_str(), "r+b");
1687  if (!hFileRead) {
1688  hFileRead = hFileWrite = fopen(Filename.c_str(), "rb");
1689  throw Exception("Could not open file \"" + Filename + "\" in read+write mode");
1690  }
1691  #endif
1692  __resetPos(); // reset read/write position of ALL 'Chunk' objects
1693  break;
1694  case stream_mode_closed:
1695  #if POSIX
1696  if (hFileRead) close(hFileRead);
1697  if (hFileWrite) close(hFileWrite);
1698  #elif defined(WIN32)
1699  if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
1700  if (hFileWrite != INVALID_HANDLE_VALUE) CloseHandle(hFileWrite);
1701  #else
1702  if (hFileRead) fclose(hFileRead);
1703  if (hFileWrite) fclose(hFileWrite);
1704  #endif
1705  hFileRead = hFileWrite = 0;
1706  break;
1707  default:
1708  throw Exception("Unknown file access mode");
1709  }
1710  Mode = NewMode;
1711  return true;
1712  }
1713  return false;
1714  }
1715 
1726  #if WORDS_BIGENDIAN
1727  bEndianNative = Endian != endian_little;
1728  #else
1729  bEndianNative = Endian != endian_big;
1730  #endif
1731  }
1732 
1743  void File::Save() {
1744  //TODO: implementation for the case where first chunk is not a global container (List chunk) is not implemented yet (i.e. Korg files)
1745  if (Layout == layout_flat)
1746  throw Exception("Saving a RIFF file with layout_flat is not implemented yet");
1747 
1748  // make sure the RIFF tree is built (from the original file)
1750 
1751  // reopen file in write mode
1753 
1754  // to be able to save the whole file without loading everything into
1755  // RAM and without having to store the data in a temporary file, we
1756  // enlarge the file with the sum of all _positive_ chunk size
1757  // changes, move current data towards the end of the file with the
1758  // calculated sum and finally update / rewrite the file by copying
1759  // the old data back to the right position at the beginning of the file
1760 
1761  // first we sum up all positive chunk size changes (and skip all negative ones)
1762  unsigned long ulPositiveSizeDiff = 0;
1763  std::set<Chunk*>* resizedChunks = _GET_RESIZED_CHUNKS();
1764  for (std::set<Chunk*>::const_iterator iter = resizedChunks->begin(), end = resizedChunks->end(); iter != end; ++iter) {
1765  if ((*iter)->GetNewSize() == 0) {
1766  throw Exception("There is at least one empty chunk (zero size): " + __resolveChunkPath(*iter));
1767  }
1768  unsigned long newSizePadded = (*iter)->GetNewSize() + (*iter)->GetNewSize() % 2;
1769  unsigned long oldSizePadded = (*iter)->GetSize() + (*iter)->GetSize() % 2;
1770  if (newSizePadded > oldSizePadded) ulPositiveSizeDiff += newSizePadded - oldSizePadded;
1771  }
1772 
1773  unsigned long ulWorkingFileSize = GetFileSize();
1774 
1775  // if there are positive size changes...
1776  if (ulPositiveSizeDiff > 0) {
1777  // ... we enlarge this file first ...
1778  ulWorkingFileSize += ulPositiveSizeDiff;
1779  ResizeFile(ulWorkingFileSize);
1780  // ... and move current data by the same amount towards end of file.
1781  int8_t* pCopyBuffer = new int8_t[4096];
1782  const unsigned long ulFileSize = GetSize() + RIFF_HEADER_SIZE;
1783  #if defined(WIN32)
1784  DWORD iBytesMoved = 1; // we have to pass it via pointer to the Windows API, thus the correct size must be ensured
1785  #else
1786  int iBytesMoved = 1;
1787  #endif
1788  for (unsigned long ulPos = ulFileSize; iBytesMoved > 0; ) {
1789  iBytesMoved = (ulPos < 4096) ? ulPos : 4096;
1790  ulPos -= iBytesMoved;
1791  #if POSIX
1792  lseek(hFileRead, ulPos, SEEK_SET);
1793  iBytesMoved = read(hFileRead, pCopyBuffer, iBytesMoved);
1794  lseek(hFileWrite, ulPos + ulPositiveSizeDiff, SEEK_SET);
1795  iBytesMoved = write(hFileWrite, pCopyBuffer, iBytesMoved);
1796  #elif defined(WIN32)
1797  SetFilePointer(hFileRead, ulPos, NULL/*32 bit*/, FILE_BEGIN);
1798  ReadFile(hFileRead, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
1799  SetFilePointer(hFileWrite, ulPos + ulPositiveSizeDiff, NULL/*32 bit*/, FILE_BEGIN);
1800  WriteFile(hFileWrite, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
1801  #else
1802  fseek(hFileRead, ulPos, SEEK_SET);
1803  iBytesMoved = fread(pCopyBuffer, 1, iBytesMoved, hFileRead);
1804  fseek(hFileWrite, ulPos + ulPositiveSizeDiff, SEEK_SET);
1805  iBytesMoved = fwrite(pCopyBuffer, 1, iBytesMoved, hFileWrite);
1806  #endif
1807  }
1808  delete[] pCopyBuffer;
1809  if (iBytesMoved < 0) throw Exception("Could not modify file while trying to enlarge it");
1810  }
1811 
1812  // rebuild / rewrite complete RIFF tree
1813  unsigned long ulTotalSize = WriteChunk(0, ulPositiveSizeDiff);
1814  unsigned long ulActualSize = __GetFileSize(hFileWrite);
1815 
1816  // resize file to the final size
1817  if (ulTotalSize < ulActualSize) ResizeFile(ulTotalSize);
1818 
1819  // forget all resized chunks
1820  resizedChunks->clear();
1821  }
1822 
1836  void File::Save(const String& path) {
1837  //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
1838 
1839  //TODO: implementation for the case where first chunk is not a global container (List chunk) is not implemented yet (i.e. Korg files)
1840  if (Layout == layout_flat)
1841  throw Exception("Saving a RIFF file with layout_flat is not implemented yet");
1842 
1843  // make sure the RIFF tree is built (from the original file)
1845 
1847  // open the other (new) file for writing and truncate it to zero size
1848  #if POSIX
1849  hFileWrite = open(path.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP);
1850  if (hFileWrite < 0) {
1852  throw Exception("Could not open file \"" + path + "\" for writing");
1853  }
1854  #elif defined(WIN32)
1855  hFileWrite = CreateFile(
1856  path.c_str(), GENERIC_WRITE, FILE_SHARE_READ,
1857  NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL |
1858  FILE_FLAG_RANDOM_ACCESS, NULL
1859  );
1860  if (hFileWrite == INVALID_HANDLE_VALUE) {
1862  throw Exception("Could not open file \"" + path + "\" for writing");
1863  }
1864  #else
1865  hFileWrite = fopen(path.c_str(), "w+b");
1866  if (!hFileWrite) {
1868  throw Exception("Could not open file \"" + path + "\" for writing");
1869  }
1870  #endif // POSIX
1871  Mode = stream_mode_read_write;
1872 
1873  // write complete RIFF tree to the other (new) file
1874  unsigned long ulTotalSize = WriteChunk(0, 0);
1875  unsigned long ulActualSize = __GetFileSize(hFileWrite);
1876 
1877  // resize file to the final size (if the file was originally larger)
1878  if (ulTotalSize < ulActualSize) ResizeFile(ulTotalSize);
1879 
1880  // forget all resized chunks
1881  _GET_RESIZED_CHUNKS()->clear();
1882 
1883  #if POSIX
1884  if (hFileWrite) close(hFileWrite);
1885  #elif defined(WIN32)
1886  if (hFileWrite != INVALID_HANDLE_VALUE) CloseHandle(hFileWrite);
1887  #else
1888  if (hFileWrite) fclose(hFileWrite);
1889  #endif
1891 
1892  // associate new file with this File object from now on
1893  Filename = path;
1894  bIsNewFile = false;
1895  Mode = (stream_mode_t) -1; // Just set it to an undefined mode ...
1896  SetMode(stream_mode_read_write); // ... so SetMode() has to reopen the file handles.
1897  }
1898 
1899  void File::ResizeFile(unsigned long ulNewSize) {
1900  #if POSIX
1901  if (ftruncate(hFileWrite, ulNewSize) < 0)
1902  throw Exception("Could not resize file \"" + Filename + "\"");
1903  #elif defined(WIN32)
1904  if (
1905  SetFilePointer(hFileWrite, ulNewSize, NULL/*32 bit*/, FILE_BEGIN) == INVALID_SET_FILE_POINTER ||
1906  !SetEndOfFile(hFileWrite)
1907  ) throw Exception("Could not resize file \"" + Filename + "\"");
1908  #else
1909  # error Sorry, this version of libgig only supports POSIX and Windows systems yet.
1910  # error Reason: portable implementation of RIFF::File::ResizeFile() is missing (yet)!
1911  #endif
1912  }
1913 
1915  #if DEBUG
1916  std::cout << "File::~File()" << std::endl;
1917  #endif // DEBUG
1918  Cleanup();
1919  }
1920 
1925  bool File::IsNew() const {
1926  return bIsNewFile;
1927  }
1928 
1929  void File::Cleanup() {
1930  #if POSIX
1931  if (hFileRead) close(hFileRead);
1932  #elif defined(WIN32)
1933  if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
1934  #else
1935  if (hFileRead) fclose(hFileRead);
1936  #endif // POSIX
1937  DeleteChunkList();
1938  pFile = NULL;
1939  //HACK: see _GET_RESIZED_CHUNKS() comment
1940  delete _GET_RESIZED_CHUNKS();
1941  }
1942 
1943  void File::LogAsResized(Chunk* pResizedChunk) {
1944  _GET_RESIZED_CHUNKS()->insert(pResizedChunk);
1945  }
1946 
1947  void File::UnlogResized(Chunk* pResizedChunk) {
1948  _GET_RESIZED_CHUNKS()->erase(pResizedChunk);
1949  }
1950 
1951  unsigned long File::GetFileSize() {
1952  return __GetFileSize(hFileRead);
1953  }
1954 
1955  #if POSIX
1956  unsigned long File::__GetFileSize(int hFile) {
1957  struct stat filestat;
1958  fstat(hFile, &filestat);
1959  long size = filestat.st_size;
1960  return size;
1961  }
1962  #elif defined(WIN32)
1963  unsigned long File::__GetFileSize(HANDLE hFile) {
1964  DWORD dwSize = ::GetFileSize(hFile, NULL /*32bit*/);
1965  if (dwSize == INVALID_FILE_SIZE)
1966  throw Exception("Windows FS error: could not determine file size");
1967  return dwSize;
1968  }
1969  #else // standard C functions
1970  unsigned long File::__GetFileSize(FILE* hFile) {
1971  long curpos = ftell(hFile);
1972  fseek(hFile, 0, SEEK_END);
1973  long size = ftell(hFile);
1974  fseek(hFile, curpos, SEEK_SET);
1975  return size;
1976  }
1977  #endif
1978 
1979 
1980 // *************** Exception ***************
1981 // *
1982 
1984  std::cout << "RIFF::Exception: " << Message << std::endl;
1985  }
1986 
1987 
1988 // *************** functions ***************
1989 // *
1990 
1997  return PACKAGE;
1998  }
1999 
2005  return VERSION;
2006  }
2007 
2008 } // namespace RIFF
virtual unsigned long WriteChunk(unsigned long ulWritePos, unsigned long ulCurrentDataOffset)
Write chunk persistently e.g.
Definition: RIFF.cpp:842
unsigned long WriteUint32(uint32_t *pData, unsigned long WordCount=1)
Writes WordCount number of 32 Bit unsigned integer words from the buffer pointed by pData to the chun...
Definition: RIFF.cpp:628
#define _GET_RESIZED_CHUNKS()
Definition: RIFF.cpp:1437
int16_t ReadInt16()
Reads one 16 Bit signed integer word and increments the position within the chunk.
Definition: RIFF.cpp:672
bool bEndianNative
Definition: RIFF.h:355
void UnlogResized(Chunk *pResizedChunk)
Definition: RIFF.cpp:1947
void swapBytes_16(void *Word)
Definition: RIFF.h:238
List * pParent
Definition: RIFF.h:226
#define CHUNK_ID_RIFX
Definition: RIFF.h:94
stream_whence_t
File stream position dependent to these relations.
Definition: RIFF.h:158
unsigned long Read(void *pData, unsigned long WordCount, unsigned long WordSize)
Reads WordCount number of data words with given WordSize and copies it into a buffer pointed by pData...
Definition: RIFF.cpp:280
Chunk * GetFirstSubChunk()
Returns the first subchunk within the list.
Definition: RIFF.cpp:1046
String libraryName()
Returns the name of this C++ library.
Definition: RIFF.cpp:1996
File(uint32_t FileType)
Create new RIFF file.
Definition: RIFF.cpp:1454
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:187
layout_t Layout
An ordinary RIFF file is always set to layout_standard.
Definition: RIFF.h:357
layout_t GetLayout() const
Definition: RIFF.cpp:1608
String GetFileName()
Definition: RIFF.cpp:1596
void ReadHeader(unsigned long fPos)
Definition: RIFF.cpp:1291
void swapBytes(void *Word, unsigned long WordSize)
Definition: RIFF.h:251
stream_state_t
Current state of the file stream.
Definition: RIFF.h:151
unsigned long SetPos(unsigned long Where, stream_whence_t Whence=stream_start)
Sets the position within the chunk body, thus within the data portion of the chunk (in bytes)...
Definition: RIFF.cpp:199
bool bIsNewFile
Definition: RIFF.h:356
void ReadString(String &s, int size)
Reads a null-padded string of size characters and copies it into the string s.
Definition: RIFF.cpp:607
#define LIST_HEADER_SIZE
Definition: RIFF.h:111
void WriteHeader(unsigned long fPos)
Definition: RIFF.cpp:1317
String libraryVersion()
Returns version of this C++ library.
Definition: RIFF.cpp:2004
List * GetSubList(uint32_t ListType)
Returns sublist chunk with list type ListType within this chunk list.
Definition: RIFF.cpp:1021
void DeleteSubChunk(Chunk *pSubChunk)
Removes a sub chunk.
Definition: RIFF.cpp:1273
unsigned long WriteInt16(int16_t *pData, unsigned long WordCount=1)
Writes WordCount number of 16 Bit signed integer words from the buffer pointed by pData to the chunk&#39;...
Definition: RIFF.cpp:500
std::string String
Definition: Akai.h:59
#define CHUNK_HEADER_SIZE
Definition: RIFF.h:110
int hFileWrite
handle / descriptor for writing to (some) file
Definition: RIFF.h:346
String Filename
Definition: RIFF.h:354
unsigned long RemainingBytes()
Returns the number of bytes left to read in the chunk body.
Definition: RIFF.cpp:231
List * GetFirstSubList()
Returns the first sublist within the list (that is a subchunk with chunk ID &quot;LIST&quot;).
Definition: RIFF.cpp:1080
stream_mode_t
Whether file stream is open in read or in read/write mode.
Definition: RIFF.h:144
std::list< Chunk * > ChunkList
Definition: RIFF.h:303
std::string String
Definition: RIFF.h:139
unsigned long GetPos()
Position within the chunk data body.
Definition: RIFF.h:192
void SetByteOrder(endian_t Endian)
Set the byte order to be used when saving.
Definition: RIFF.cpp:1725
RIFF List Chunk.
Definition: RIFF.h:280
#define CHUNK_ID_RIFF
Definition: RIFF.h:93
File * pFile
Definition: RIFF.h:227
int8_t ReadInt8()
Reads one 8 Bit signed integer word and increments the position within the chunk. ...
Definition: RIFF.cpp:639
void ReadHeader(unsigned long fPos)
Definition: RIFF.cpp:102
String GetListTypeString()
Returns string representation of the lists&#39;s id.
Definition: RIFF.cpp:1427
ChunkList::iterator ListIterator
Definition: RIFF.h:309
String Message
Definition: RIFF.h:385
unsigned long ulPos
Definition: RIFF.h:229
unsigned long WriteInt32(int32_t *pData, unsigned long WordCount=1)
Writes WordCount number of 32 Bit signed integer words from the buffer pointed by pData to the chunk&#39;...
Definition: RIFF.cpp:574
void DeleteChunkList()
Definition: RIFF.cpp:974
Chunk * GetSubChunk(uint32_t ChunkID)
Returns subchunk with chunk ID ChunkID within this chunk list.
Definition: RIFF.cpp:1002
Chunk * GetNextSubChunk()
Returns the next subchunk within the list.
Definition: RIFF.cpp:1062
unsigned long ReadSceptical(void *pData, unsigned long WordCount, unsigned long WordSize)
Just an internal wrapper for the main Read() method with additional Exception throwing on errors...
Definition: RIFF.cpp:388
int32_t ReadInt32()
Reads one 32 Bit signed integer word and increments the position within the chunk.
Definition: RIFF.cpp:706
layout_t
General chunk structure of a file.
Definition: RIFF.h:173
stream_state_t GetState()
Returns the current state of the chunk object.
Definition: RIFF.cpp:249
void LoadSubChunks()
Definition: RIFF.cpp:1335
Ordinary RIFF Chunk.
Definition: RIFF.h:183
uint32_t GetListType()
Returns unsigned integer representation of the list&#39;s ID.
Definition: RIFF.h:284
#define RIFF_HEADER_SIZE
Definition: RIFF.h:112
uint32_t ReadUint32()
Reads one 32 Bit unsigned integer word and increments the position within the chunk.
Definition: RIFF.cpp:723
ChunkList::iterator ChunksIterator
Definition: RIFF.h:308
unsigned long ulStartPos
Definition: RIFF.h:228
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:306
void swapBytes_32(void *Word)
Definition: RIFF.h:243
Chunk * AddSubChunk(uint32_t uiChunkID, uint uiBodySize)
Creates a new sub chunk.
Definition: RIFF.cpp:1182
void PrintMessage()
Definition: RIFF.cpp:1983
int hFileRead
handle / descriptor for reading from file
Definition: RIFF.h:345
Not a &quot;real&quot; RIFF file: First chunk in file is an ordinary data chunk, not a List chunk...
Definition: RIFF.h:175
ChunkMap * pSubChunksMap
Definition: RIFF.h:307
virtual ~Chunk()
Definition: RIFF.cpp:97
void LogAsResized(Chunk *pResizedChunk)
Definition: RIFF.cpp:1943
virtual void __resetPos()
Sets Chunk&#39;s read/write position to zero.
Definition: RIFF.cpp:932
uint16_t ReadUint16()
Reads one 16 Bit unsigned integer word and increments the position within the chunk.
Definition: RIFF.cpp:689
virtual void __resetPos()
Sets List Chunk&#39;s read/write position to zero and causes all sub chunks to do the same...
Definition: RIFF.cpp:1415
uint8_t * pChunkData
Definition: RIFF.h:230
uint32_t CurrentChunkSize
Definition: RIFF.h:224
endian_t
Alignment of data bytes in memory (system dependant).
Definition: RIFF.h:166
void SetFileName(const String &path)
Definition: RIFF.cpp:1600
void * LoadChunkData()
Load chunk body into RAM.
Definition: RIFF.cpp:753
unsigned long WriteInt8(int8_t *pData, unsigned long WordCount=1)
Writes WordCount number of 8 Bit signed integer words from the buffer pointed by pData to the chunk&#39;s...
Definition: RIFF.cpp:426
Standard RIFF file layout: First chunk in file is a List chunk which contains all other chunks and th...
Definition: RIFF.h:174
unsigned long GetSize() const
Chunk size in bytes (without header, thus the chunk data body)
Definition: RIFF.h:190
unsigned long ulChunkDataSize
Definition: RIFF.h:231
RIFF File.
Definition: RIFF.h:328
List * AddSubList(uint32_t uiListType)
Creates a new list sub chunk.
Definition: RIFF.cpp:1253
#define CHUNK_ID_LIST
Definition: RIFF.h:95
void LoadSubChunksRecursively()
Definition: RIFF.cpp:1372
bool IsNew() const
Returns true if this file has been created new from scratch and has not been stored to disk yet...
Definition: RIFF.cpp:1925
List(File *pFile, unsigned long StartPos, List *Parent)
Definition: RIFF.cpp:949
virtual unsigned long WriteChunk(unsigned long ulWritePos, unsigned long ulCurrentDataOffset)
Write list chunk persistently e.g.
Definition: RIFF.cpp:1391
void MoveSubChunk(Chunk *pSrc, Chunk *pDst)
Moves a sub chunk witin this list.
Definition: RIFF.cpp:1205
unsigned int CountSubChunks()
Returns number of subchunks within the list.
Definition: RIFF.cpp:1120
virtual ~List()
Definition: RIFF.cpp:967
String convertToString(uint32_t word)
Definition: RIFF.h:260
bool SetMode(stream_mode_t NewMode)
Change file access mode.
Definition: RIFF.cpp:1622
virtual void Save()
Save changes to same file.
Definition: RIFF.cpp:1743
Will be thrown whenever an error occurs while handling a RIFF file.
Definition: RIFF.h:383
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:1604
void ReleaseChunkData()
Free loaded chunk body from RAM.
Definition: RIFF.cpp:797
virtual ~File()
Definition: RIFF.cpp:1914
unsigned int CountSubLists()
Returns number of sublists within the list.
Definition: RIFF.cpp:1146
String GetChunkIDString()
Returns the String representation of the chunk&#39;s ID (e.g.
Definition: RIFF.cpp:183
uint32_t NewChunkSize
Definition: RIFF.h:225
void WriteHeader(unsigned long fPos)
Definition: RIFF.cpp:145
uint32_t ChunkID
Definition: RIFF.h:223
std::map< uint32_t, RIFF::Chunk * > ChunkMap
Definition: RIFF.h:302
void Resize(int iNewSize)
Resize chunk.
Definition: RIFF.cpp:822
List * GetNextSubList()
Returns the next sublist (that is a subchunk with chunk ID &quot;LIST&quot;) within the list.
Definition: RIFF.cpp:1102
Chunk(File *pFile, unsigned long StartPos, List *Parent)
Definition: RIFF.cpp:70
uint8_t ReadUint8()
Reads one 8 Bit unsigned integer word and increments the position within the chunk.
Definition: RIFF.cpp:655
uint32_t ListType
Definition: RIFF.h:305