libgig  3.3.0.svn21
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 #if POSIX
33 # include <errno.h>
34 #endif
35 
36 namespace RIFF {
37 
38 // *************** Internal functions **************
39 // *
40 
42  static String __resolveChunkPath(Chunk* pCk) {
43  String sPath;
44  for (Chunk* pChunk = pCk; pChunk; pChunk = pChunk->GetParent()) {
45  if (pChunk->GetChunkID() == CHUNK_ID_LIST) {
46  List* pList = (List*) pChunk;
47  sPath = "->'" + pList->GetListTypeString() + "'" + sPath;
48  } else {
49  sPath = "->'" + pChunk->GetChunkIDString() + "'" + sPath;
50  }
51  }
52  return sPath;
53  }
54 
55 
56 
57 // *************** Chunk **************
58 // *
59 
60  Chunk::Chunk(File* pFile) {
61  #if DEBUG
62  std::cout << "Chunk::Chunk(File* pFile)" << std::endl;
63  #endif // DEBUG
64  ulPos = 0;
65  pParent = NULL;
66  pChunkData = NULL;
67  CurrentChunkSize = 0;
68  NewChunkSize = 0;
69  ulChunkDataSize = 0;
71  this->pFile = pFile;
72  }
73 
74  Chunk::Chunk(File* pFile, unsigned long StartPos, List* Parent) {
75  #if DEBUG
76  std::cout << "Chunk::Chunk(File*,ulong,bool,List*),StartPos=" << StartPos << std::endl;
77  #endif // DEBUG
78  this->pFile = pFile;
79  ulStartPos = StartPos + CHUNK_HEADER_SIZE;
80  pParent = Parent;
81  ulPos = 0;
82  pChunkData = NULL;
83  CurrentChunkSize = 0;
84  NewChunkSize = 0;
85  ulChunkDataSize = 0;
86  ReadHeader(StartPos);
87  }
88 
89  Chunk::Chunk(File* pFile, List* pParent, uint32_t uiChunkID, uint uiBodySize) {
90  this->pFile = pFile;
91  ulStartPos = 0; // arbitrary usually, since it will be updated when we write the chunk
92  this->pParent = pParent;
93  ulPos = 0;
94  pChunkData = NULL;
95  ChunkID = uiChunkID;
96  ulChunkDataSize = 0;
97  CurrentChunkSize = 0;
98  NewChunkSize = uiBodySize;
99  }
100 
102  if (pFile) pFile->UnlogResized(this);
103  if (pChunkData) delete[] pChunkData;
104  }
105 
106  void Chunk::ReadHeader(unsigned long fPos) {
107  #if DEBUG
108  std::cout << "Chunk::Readheader(" << fPos << ") ";
109  #endif // DEBUG
110  ChunkID = 0;
112  #if POSIX
113  if (lseek(pFile->hFileRead, fPos, SEEK_SET) != -1) {
114  read(pFile->hFileRead, &ChunkID, 4);
115  read(pFile->hFileRead, &CurrentChunkSize, 4);
116  #elif defined(WIN32)
117  if (SetFilePointer(pFile->hFileRead, fPos, NULL/*32 bit*/, FILE_BEGIN) != INVALID_SET_FILE_POINTER) {
118  DWORD dwBytesRead;
119  ReadFile(pFile->hFileRead, &ChunkID, 4, &dwBytesRead, NULL);
120  ReadFile(pFile->hFileRead, &CurrentChunkSize, 4, &dwBytesRead, NULL);
121  #else
122  if (!fseek(pFile->hFileRead, fPos, SEEK_SET)) {
123  fread(&ChunkID, 4, 1, pFile->hFileRead);
124  fread(&CurrentChunkSize, 4, 1, pFile->hFileRead);
125  #endif // POSIX
126  #if WORDS_BIGENDIAN
127  if (ChunkID == CHUNK_ID_RIFF) {
128  pFile->bEndianNative = false;
129  }
130  #else // little endian
131  if (ChunkID == CHUNK_ID_RIFX) {
132  pFile->bEndianNative = false;
134  }
135  #endif // WORDS_BIGENDIAN
136  if (!pFile->bEndianNative) {
137  //swapBytes_32(&ChunkID);
139  }
140  #if DEBUG
141  std::cout << "ckID=" << convertToString(ChunkID) << " ";
142  std::cout << "ckSize=" << CurrentChunkSize << " ";
143  std::cout << "bEndianNative=" << pFile->bEndianNative << std::endl;
144  #endif // DEBUG
146  }
147  }
148 
149  void Chunk::WriteHeader(unsigned long fPos) {
150  uint32_t uiNewChunkID = ChunkID;
151  if (ChunkID == CHUNK_ID_RIFF) {
152  #if WORDS_BIGENDIAN
153  if (pFile->bEndianNative) uiNewChunkID = CHUNK_ID_RIFX;
154  #else // little endian
155  if (!pFile->bEndianNative) uiNewChunkID = CHUNK_ID_RIFX;
156  #endif // WORDS_BIGENDIAN
157  }
158 
159  uint32_t uiNewChunkSize = NewChunkSize;
160  if (!pFile->bEndianNative) {
161  swapBytes_32(&uiNewChunkSize);
162  }
163 
164  #if POSIX
165  if (lseek(pFile->hFileWrite, fPos, SEEK_SET) != -1) {
166  write(pFile->hFileWrite, &uiNewChunkID, 4);
167  write(pFile->hFileWrite, &uiNewChunkSize, 4);
168  }
169  #elif defined(WIN32)
170  if (SetFilePointer(pFile->hFileWrite, fPos, NULL/*32 bit*/, FILE_BEGIN) != INVALID_SET_FILE_POINTER) {
171  DWORD dwBytesWritten;
172  WriteFile(pFile->hFileWrite, &uiNewChunkID, 4, &dwBytesWritten, NULL);
173  WriteFile(pFile->hFileWrite, &uiNewChunkSize, 4, &dwBytesWritten, NULL);
174  }
175  #else
176  if (!fseek(pFile->hFileWrite, fPos, SEEK_SET)) {
177  fwrite(&uiNewChunkID, 4, 1, pFile->hFileWrite);
178  fwrite(&uiNewChunkSize, 4, 1, pFile->hFileWrite);
179  }
180  #endif // POSIX
181  }
182 
188  return convertToString(ChunkID);
189  }
190 
203  unsigned long Chunk::SetPos(unsigned long Where, stream_whence_t Whence) {
204  #if DEBUG
205  std::cout << "Chunk::SetPos(ulong)" << std::endl;
206  #endif // DEBUG
207  switch (Whence) {
208  case stream_curpos:
209  ulPos += Where;
210  break;
211  case stream_end:
212  ulPos = CurrentChunkSize - 1 - Where;
213  break;
214  case stream_backward:
215  ulPos -= Where;
216  break;
217  case stream_start: default:
218  ulPos = Where;
219  break;
220  }
222  return ulPos;
223  }
224 
235  unsigned long Chunk::RemainingBytes() {
236  #if DEBUG
237  std::cout << "Chunk::Remainingbytes()=" << CurrentChunkSize - ulPos << std::endl;
238  #endif // DEBUG
239  return (CurrentChunkSize > ulPos) ? CurrentChunkSize - ulPos : 0;
240  }
241 
254  #if DEBUG
255  std::cout << "Chunk::GetState()" << std::endl;
256  #endif // DEBUG
257  #if POSIX
258  if (pFile->hFileRead == 0) return stream_closed;
259  #elif defined (WIN32)
260  if (pFile->hFileRead == INVALID_HANDLE_VALUE)
261  return stream_closed;
262  #else
263  if (pFile->hFileRead == NULL) return stream_closed;
264  #endif // POSIX
265  if (ulPos < CurrentChunkSize) return stream_ready;
266  else return stream_end_reached;
267  }
268 
284  unsigned long Chunk::Read(void* pData, unsigned long WordCount, unsigned long WordSize) {
285  #if DEBUG
286  std::cout << "Chunk::Read(void*,ulong,ulong)" << std::endl;
287  #endif // DEBUG
288  //if (ulStartPos == 0) return 0; // is only 0 if this is a new chunk, so nothing to read (yet)
289  if (ulPos >= CurrentChunkSize) return 0;
290  if (ulPos + WordCount * WordSize >= CurrentChunkSize) WordCount = (CurrentChunkSize - ulPos) / WordSize;
291  #if POSIX
292  if (lseek(pFile->hFileRead, ulStartPos + ulPos, SEEK_SET) < 0) return 0;
293  unsigned long readWords = read(pFile->hFileRead, pData, WordCount * WordSize);
294  if (readWords < 1) return 0;
295  readWords /= WordSize;
296  #elif defined(WIN32)
297  if (SetFilePointer(pFile->hFileRead, ulStartPos + ulPos, NULL/*32 bit*/, FILE_BEGIN) == INVALID_SET_FILE_POINTER) return 0;
298  DWORD readWords;
299  ReadFile(pFile->hFileRead, pData, WordCount * WordSize, &readWords, NULL);
300  if (readWords < 1) return 0;
301  readWords /= WordSize;
302  #else // standard C functions
303  if (fseek(pFile->hFileRead, ulStartPos + ulPos, SEEK_SET)) return 0;
304  unsigned long readWords = fread(pData, WordSize, WordCount, pFile->hFileRead);
305  #endif // POSIX
306  if (!pFile->bEndianNative && WordSize != 1) {
307  switch (WordSize) {
308  case 2:
309  for (unsigned long iWord = 0; iWord < readWords; iWord++)
310  swapBytes_16((uint16_t*) pData + iWord);
311  break;
312  case 4:
313  for (unsigned long iWord = 0; iWord < readWords; iWord++)
314  swapBytes_32((uint32_t*) pData + iWord);
315  break;
316  default:
317  for (unsigned long iWord = 0; iWord < readWords; iWord++)
318  swapBytes((uint8_t*) pData + iWord * WordSize, WordSize);
319  break;
320  }
321  }
322  SetPos(readWords * WordSize, stream_curpos);
323  return readWords;
324  }
325 
342  unsigned long Chunk::Write(void* pData, unsigned long WordCount, unsigned long WordSize) {
343  if (pFile->Mode != stream_mode_read_write)
344  throw Exception("Cannot write data to chunk, file has to be opened in read+write mode first");
345  if (ulPos >= CurrentChunkSize || ulPos + WordCount * WordSize > CurrentChunkSize)
346  throw Exception("End of chunk reached while trying to write data");
347  if (!pFile->bEndianNative && WordSize != 1) {
348  switch (WordSize) {
349  case 2:
350  for (unsigned long iWord = 0; iWord < WordCount; iWord++)
351  swapBytes_16((uint16_t*) pData + iWord);
352  break;
353  case 4:
354  for (unsigned long iWord = 0; iWord < WordCount; iWord++)
355  swapBytes_32((uint32_t*) pData + iWord);
356  break;
357  default:
358  for (unsigned long iWord = 0; iWord < WordCount; iWord++)
359  swapBytes((uint8_t*) pData + iWord * WordSize, WordSize);
360  break;
361  }
362  }
363  #if POSIX
364  if (lseek(pFile->hFileWrite, ulStartPos + ulPos, SEEK_SET) < 0) {
365  throw Exception("Could not seek to position " + ToString(ulPos) +
366  " in chunk (" + ToString(ulStartPos + ulPos) + " in file)");
367  }
368  unsigned long writtenWords = write(pFile->hFileWrite, pData, WordCount * WordSize);
369  if (writtenWords < 1) throw Exception("POSIX IO Error while trying to write chunk data");
370  writtenWords /= WordSize;
371  #elif defined(WIN32)
372  if (SetFilePointer(pFile->hFileWrite, ulStartPos + ulPos, NULL/*32 bit*/, FILE_BEGIN) == INVALID_SET_FILE_POINTER) {
373  throw Exception("Could not seek to position " + ToString(ulPos) +
374  " in chunk (" + ToString(ulStartPos + ulPos) + " in file)");
375  }
376  DWORD writtenWords;
377  WriteFile(pFile->hFileWrite, pData, WordCount * WordSize, &writtenWords, NULL);
378  if (writtenWords < 1) throw Exception("Windows IO Error while trying to write chunk data");
379  writtenWords /= WordSize;
380  #else // standard C functions
381  if (fseek(pFile->hFileWrite, ulStartPos + ulPos, SEEK_SET)) {
382  throw Exception("Could not seek to position " + ToString(ulPos) +
383  " in chunk (" + ToString(ulStartPos + ulPos) + " in file)");
384  }
385  unsigned long writtenWords = fwrite(pData, WordSize, WordCount, pFile->hFileWrite);
386  #endif // POSIX
387  SetPos(writtenWords * WordSize, stream_curpos);
388  return writtenWords;
389  }
390 
392  unsigned long Chunk::ReadSceptical(void* pData, unsigned long WordCount, unsigned long WordSize) {
393  unsigned long readWords = Read(pData, WordCount, WordSize);
394  if (readWords != WordCount) throw RIFF::Exception("End of chunk data reached.");
395  return readWords;
396  }
397 
409  unsigned long Chunk::ReadInt8(int8_t* pData, unsigned long WordCount) {
410  #if DEBUG
411  std::cout << "Chunk::ReadInt8(int8_t*,ulong)" << std::endl;
412  #endif // DEBUG
413  return ReadSceptical(pData, WordCount, 1);
414  }
415 
430  unsigned long Chunk::WriteInt8(int8_t* pData, unsigned long WordCount) {
431  return Write(pData, WordCount, 1);
432  }
433 
446  unsigned long Chunk::ReadUint8(uint8_t* pData, unsigned long WordCount) {
447  #if DEBUG
448  std::cout << "Chunk::ReadUint8(uint8_t*,ulong)" << std::endl;
449  #endif // DEBUG
450  return ReadSceptical(pData, WordCount, 1);
451  }
452 
467  unsigned long Chunk::WriteUint8(uint8_t* pData, unsigned long WordCount) {
468  return Write(pData, WordCount, 1);
469  }
470 
483  unsigned long Chunk::ReadInt16(int16_t* pData, unsigned long WordCount) {
484  #if DEBUG
485  std::cout << "Chunk::ReadInt16(int16_t*,ulong)" << std::endl;
486  #endif // DEBUG
487  return ReadSceptical(pData, WordCount, 2);
488  }
489 
504  unsigned long Chunk::WriteInt16(int16_t* pData, unsigned long WordCount) {
505  return Write(pData, WordCount, 2);
506  }
507 
520  unsigned long Chunk::ReadUint16(uint16_t* pData, unsigned long WordCount) {
521  #if DEBUG
522  std::cout << "Chunk::ReadUint16(uint16_t*,ulong)" << std::endl;
523  #endif // DEBUG
524  return ReadSceptical(pData, WordCount, 2);
525  }
526 
541  unsigned long Chunk::WriteUint16(uint16_t* pData, unsigned long WordCount) {
542  return Write(pData, WordCount, 2);
543  }
544 
557  unsigned long Chunk::ReadInt32(int32_t* pData, unsigned long WordCount) {
558  #if DEBUG
559  std::cout << "Chunk::ReadInt32(int32_t*,ulong)" << std::endl;
560  #endif // DEBUG
561  return ReadSceptical(pData, WordCount, 4);
562  }
563 
578  unsigned long Chunk::WriteInt32(int32_t* pData, unsigned long WordCount) {
579  return Write(pData, WordCount, 4);
580  }
581 
594  unsigned long Chunk::ReadUint32(uint32_t* pData, unsigned long WordCount) {
595  #if DEBUG
596  std::cout << "Chunk::ReadUint32(uint32_t*,ulong)" << std::endl;
597  #endif // DEBUG
598  return ReadSceptical(pData, WordCount, 4);
599  }
600 
611  void Chunk::ReadString(String& s, int size) {
612  char* buf = new char[size];
613  ReadSceptical(buf, 1, size);
614  s.assign(buf, std::find(buf, buf + size, '\0'));
615  delete[] buf;
616  }
617 
632  unsigned long Chunk::WriteUint32(uint32_t* pData, unsigned long WordCount) {
633  return Write(pData, WordCount, 4);
634  }
635 
643  int8_t Chunk::ReadInt8() {
644  #if DEBUG
645  std::cout << "Chunk::ReadInt8()" << std::endl;
646  #endif // DEBUG
647  int8_t word;
648  ReadSceptical(&word,1,1);
649  return word;
650  }
651 
659  uint8_t Chunk::ReadUint8() {
660  #if DEBUG
661  std::cout << "Chunk::ReadUint8()" << std::endl;
662  #endif // DEBUG
663  uint8_t word;
664  ReadSceptical(&word,1,1);
665  return word;
666  }
667 
676  int16_t Chunk::ReadInt16() {
677  #if DEBUG
678  std::cout << "Chunk::ReadInt16()" << std::endl;
679  #endif // DEBUG
680  int16_t word;
681  ReadSceptical(&word,1,2);
682  return word;
683  }
684 
693  uint16_t Chunk::ReadUint16() {
694  #if DEBUG
695  std::cout << "Chunk::ReadUint16()" << std::endl;
696  #endif // DEBUG
697  uint16_t word;
698  ReadSceptical(&word,1,2);
699  return word;
700  }
701 
710  int32_t Chunk::ReadInt32() {
711  #if DEBUG
712  std::cout << "Chunk::ReadInt32()" << std::endl;
713  #endif // DEBUG
714  int32_t word;
715  ReadSceptical(&word,1,4);
716  return word;
717  }
718 
727  uint32_t Chunk::ReadUint32() {
728  #if DEBUG
729  std::cout << "Chunk::ReadUint32()" << std::endl;
730  #endif // DEBUG
731  uint32_t word;
732  ReadSceptical(&word,1,4);
733  return word;
734  }
735 
758  if (!pChunkData && pFile->Filename != "" /*&& ulStartPos != 0*/) {
759  #if POSIX
760  if (lseek(pFile->hFileRead, ulStartPos, SEEK_SET) == -1) return NULL;
761  #elif defined(WIN32)
762  if (SetFilePointer(pFile->hFileRead, ulStartPos, NULL/*32 bit*/, FILE_BEGIN) == INVALID_SET_FILE_POINTER) return NULL;
763  #else
764  if (fseek(pFile->hFileRead, ulStartPos, SEEK_SET)) return NULL;
765  #endif // POSIX
766  unsigned long ulBufferSize = (CurrentChunkSize > NewChunkSize) ? CurrentChunkSize : NewChunkSize;
767  pChunkData = new uint8_t[ulBufferSize];
768  if (!pChunkData) return NULL;
769  memset(pChunkData, 0, ulBufferSize);
770  #if POSIX
771  unsigned long readWords = read(pFile->hFileRead, pChunkData, GetSize());
772  #elif defined(WIN32)
773  DWORD readWords;
774  ReadFile(pFile->hFileRead, pChunkData, GetSize(), &readWords, NULL);
775  #else
776  unsigned long readWords = fread(pChunkData, 1, GetSize(), pFile->hFileRead);
777  #endif // POSIX
778  if (readWords != GetSize()) {
779  delete[] pChunkData;
780  return (pChunkData = NULL);
781  }
782  ulChunkDataSize = ulBufferSize;
783  } else if (NewChunkSize > ulChunkDataSize) {
784  uint8_t* pNewBuffer = new uint8_t[NewChunkSize];
785  if (!pNewBuffer) throw Exception("Could not enlarge chunk data buffer to " + ToString(NewChunkSize) + " bytes");
786  memset(pNewBuffer, 0 , NewChunkSize);
787  memcpy(pNewBuffer, pChunkData, ulChunkDataSize);
788  delete[] pChunkData;
789  pChunkData = pNewBuffer;
791  }
792  return pChunkData;
793  }
794 
802  if (pChunkData) {
803  delete[] pChunkData;
804  pChunkData = NULL;
805  }
806  }
807 
826  void Chunk::Resize(int iNewSize) {
827  if (iNewSize <= 0)
828  throw Exception("There is at least one empty chunk (zero size): " + __resolveChunkPath(this));
829  if (NewChunkSize == iNewSize) return;
830  NewChunkSize = iNewSize;
831  pFile->LogAsResized(this);
832  }
833 
846  unsigned long Chunk::WriteChunk(unsigned long ulWritePos, unsigned long ulCurrentDataOffset) {
847  const unsigned long ulOriginalPos = ulWritePos;
848  ulWritePos += CHUNK_HEADER_SIZE;
849 
850  if (pFile->Mode != stream_mode_read_write)
851  throw Exception("Cannot write list chunk, file has to be opened in read+write mode");
852 
853  // if the whole chunk body was loaded into RAM
854  if (pChunkData) {
855  // make sure chunk data buffer in RAM is at least as large as the new chunk size
856  LoadChunkData();
857  // write chunk data from RAM persistently to the file
858  #if POSIX
859  lseek(pFile->hFileWrite, ulWritePos, SEEK_SET);
861  throw Exception("Writing Chunk data (from RAM) failed");
862  }
863  #elif defined(WIN32)
864  SetFilePointer(pFile->hFileWrite, ulWritePos, NULL/*32 bit*/, FILE_BEGIN);
865  DWORD dwBytesWritten;
866  WriteFile(pFile->hFileWrite, pChunkData, NewChunkSize, &dwBytesWritten, NULL);
867  if (dwBytesWritten != NewChunkSize) {
868  throw Exception("Writing Chunk data (from RAM) failed");
869  }
870  #else
871  fseek(pFile->hFileWrite, ulWritePos, SEEK_SET);
872  if (fwrite(pChunkData, 1, NewChunkSize, pFile->hFileWrite) != NewChunkSize) {
873  throw Exception("Writing Chunk data (from RAM) failed");
874  }
875  #endif // POSIX
876  } else {
877  // move chunk data from the end of the file to the appropriate position
878  int8_t* pCopyBuffer = new int8_t[4096];
879  unsigned long ulToMove = (NewChunkSize < CurrentChunkSize) ? NewChunkSize : CurrentChunkSize;
880  #if defined(WIN32)
881  DWORD iBytesMoved = 1; // we have to pass it via pointer to the Windows API, thus the correct size must be ensured
882  #else
883  int iBytesMoved = 1;
884  #endif
885  for (unsigned long ulOffset = 0; ulToMove > 0 && iBytesMoved > 0; ulOffset += iBytesMoved, ulToMove -= iBytesMoved) {
886  iBytesMoved = (ulToMove < 4096) ? ulToMove : 4096;
887  #if POSIX
888  lseek(pFile->hFileRead, ulStartPos + ulCurrentDataOffset + ulOffset, SEEK_SET);
889  iBytesMoved = read(pFile->hFileRead, pCopyBuffer, iBytesMoved);
890  lseek(pFile->hFileWrite, ulWritePos + ulOffset, SEEK_SET);
891  iBytesMoved = write(pFile->hFileWrite, pCopyBuffer, iBytesMoved);
892  #elif defined(WIN32)
893  SetFilePointer(pFile->hFileRead, ulStartPos + ulCurrentDataOffset + ulOffset, NULL/*32 bit*/, FILE_BEGIN);
894  ReadFile(pFile->hFileRead, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
895  SetFilePointer(pFile->hFileWrite, ulWritePos + ulOffset, NULL/*32 bit*/, FILE_BEGIN);
896  WriteFile(pFile->hFileWrite, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
897  #else
898  fseek(pFile->hFileRead, ulStartPos + ulCurrentDataOffset + ulOffset, SEEK_SET);
899  iBytesMoved = fread(pCopyBuffer, 1, iBytesMoved, pFile->hFileRead);
900  fseek(pFile->hFileWrite, ulWritePos + ulOffset, SEEK_SET);
901  iBytesMoved = fwrite(pCopyBuffer, 1, iBytesMoved, pFile->hFileWrite);
902  #endif
903  }
904  delete[] pCopyBuffer;
905  if (iBytesMoved < 0) throw Exception("Writing Chunk data (from file) failed");
906  }
907 
908  // update this chunk's header
910  WriteHeader(ulOriginalPos);
911 
912  // update chunk's position pointers
913  ulStartPos = ulOriginalPos + CHUNK_HEADER_SIZE;
914  ulPos = 0;
915 
916  // add pad byte if needed
917  if ((ulStartPos + NewChunkSize) % 2 != 0) {
918  const char cPadByte = 0;
919  #if POSIX
920  lseek(pFile->hFileWrite, ulStartPos + NewChunkSize, SEEK_SET);
921  write(pFile->hFileWrite, &cPadByte, 1);
922  #elif defined(WIN32)
923  SetFilePointer(pFile->hFileWrite, ulStartPos + NewChunkSize, NULL/*32 bit*/, FILE_BEGIN);
924  DWORD dwBytesWritten;
925  WriteFile(pFile->hFileWrite, &cPadByte, 1, &dwBytesWritten, NULL);
926  #else
927  fseek(pFile->hFileWrite, ulStartPos + NewChunkSize, SEEK_SET);
928  fwrite(&cPadByte, 1, 1, pFile->hFileWrite);
929  #endif
930  return ulStartPos + NewChunkSize + 1;
931  }
932 
933  return ulStartPos + NewChunkSize;
934  }
935 
937  ulPos = 0;
938  }
939 
940 
941 
942 // *************** List ***************
943 // *
944 
945  List::List(File* pFile) : Chunk(pFile) {
946  #if DEBUG
947  std::cout << "List::List(File* pFile)" << std::endl;
948  #endif // DEBUG
949  pSubChunks = NULL;
950  pSubChunksMap = NULL;
951  }
952 
953  List::List(File* pFile, unsigned long StartPos, List* Parent)
954  : Chunk(pFile, StartPos, Parent) {
955  #if DEBUG
956  std::cout << "List::List(File*,ulong,bool,List*)" << std::endl;
957  #endif // DEBUG
958  pSubChunks = NULL;
959  pSubChunksMap = NULL;
960  ReadHeader(StartPos);
961  ulStartPos = StartPos + LIST_HEADER_SIZE;
962  }
963 
964  List::List(File* pFile, List* pParent, uint32_t uiListID)
965  : Chunk(pFile, pParent, CHUNK_ID_LIST, 0) {
966  pSubChunks = NULL;
967  pSubChunksMap = NULL;
968  ListType = uiListID;
969  }
970 
972  #if DEBUG
973  std::cout << "List::~List()" << std::endl;
974  #endif // DEBUG
975  DeleteChunkList();
976  }
977 
979  if (pSubChunks) {
980  ChunkList::iterator iter = pSubChunks->begin();
981  ChunkList::iterator end = pSubChunks->end();
982  while (iter != end) {
983  delete *iter;
984  iter++;
985  }
986  delete pSubChunks;
987  pSubChunks = NULL;
988  }
989  if (pSubChunksMap) {
990  delete pSubChunksMap;
991  pSubChunksMap = NULL;
992  }
993  }
994 
1007  #if DEBUG
1008  std::cout << "List::GetSubChunk(uint32_t)" << std::endl;
1009  #endif // DEBUG
1010  if (!pSubChunksMap) LoadSubChunks();
1011  return (*pSubChunksMap)[ChunkID];
1012  }
1013 
1026  #if DEBUG
1027  std::cout << "List::GetSubList(uint32_t)" << std::endl;
1028  #endif // DEBUG
1029  if (!pSubChunks) LoadSubChunks();
1030  ChunkList::iterator iter = pSubChunks->begin();
1031  ChunkList::iterator end = pSubChunks->end();
1032  while (iter != end) {
1033  if ((*iter)->GetChunkID() == CHUNK_ID_LIST) {
1034  List* l = (List*) *iter;
1035  if (l->GetListType() == ListType) return l;
1036  }
1037  iter++;
1038  }
1039  return NULL;
1040  }
1041 
1051  #if DEBUG
1052  std::cout << "List::GetFirstSubChunk()" << std::endl;
1053  #endif // DEBUG
1054  if (!pSubChunks) LoadSubChunks();
1055  ChunksIterator = pSubChunks->begin();
1056  return (ChunksIterator != pSubChunks->end()) ? *ChunksIterator : NULL;
1057  }
1058 
1067  #if DEBUG
1068  std::cout << "List::GetNextSubChunk()" << std::endl;
1069  #endif // DEBUG
1070  if (!pSubChunks) return NULL;
1071  ChunksIterator++;
1072  return (ChunksIterator != pSubChunks->end()) ? *ChunksIterator : NULL;
1073  }
1074 
1085  #if DEBUG
1086  std::cout << "List::GetFirstSubList()" << std::endl;
1087  #endif // DEBUG
1088  if (!pSubChunks) LoadSubChunks();
1089  ListIterator = pSubChunks->begin();
1090  ChunkList::iterator end = pSubChunks->end();
1091  while (ListIterator != end) {
1092  if ((*ListIterator)->GetChunkID() == CHUNK_ID_LIST) return (List*) *ListIterator;
1093  ListIterator++;
1094  }
1095  return NULL;
1096  }
1097 
1107  #if DEBUG
1108  std::cout << "List::GetNextSubList()" << std::endl;
1109  #endif // DEBUG
1110  if (!pSubChunks) return NULL;
1111  if (ListIterator == pSubChunks->end()) return NULL;
1112  ListIterator++;
1113  ChunkList::iterator end = pSubChunks->end();
1114  while (ListIterator != end) {
1115  if ((*ListIterator)->GetChunkID() == CHUNK_ID_LIST) return (List*) *ListIterator;
1116  ListIterator++;
1117  }
1118  return NULL;
1119  }
1120 
1124  unsigned int List::CountSubChunks() {
1125  if (!pSubChunks) LoadSubChunks();
1126  return pSubChunks->size();
1127  }
1128 
1133  unsigned int List::CountSubChunks(uint32_t ChunkID) {
1134  unsigned int result = 0;
1135  if (!pSubChunks) LoadSubChunks();
1136  ChunkList::iterator iter = pSubChunks->begin();
1137  ChunkList::iterator end = pSubChunks->end();
1138  while (iter != end) {
1139  if ((*iter)->GetChunkID() == ChunkID) {
1140  result++;
1141  }
1142  iter++;
1143  }
1144  return result;
1145  }
1146 
1150  unsigned int List::CountSubLists() {
1151  return CountSubChunks(CHUNK_ID_LIST);
1152  }
1153 
1158  unsigned int List::CountSubLists(uint32_t ListType) {
1159  unsigned int result = 0;
1160  if (!pSubChunks) LoadSubChunks();
1161  ChunkList::iterator iter = pSubChunks->begin();
1162  ChunkList::iterator end = pSubChunks->end();
1163  while (iter != end) {
1164  if ((*iter)->GetChunkID() == CHUNK_ID_LIST) {
1165  List* l = (List*) *iter;
1166  if (l->GetListType() == ListType) result++;
1167  }
1168  iter++;
1169  }
1170  return result;
1171  }
1172 
1186  Chunk* List::AddSubChunk(uint32_t uiChunkID, uint uiBodySize) {
1187  if (uiBodySize == 0) throw Exception("Chunk body size must be at least 1 byte");
1188  if (!pSubChunks) LoadSubChunks();
1189  Chunk* pNewChunk = new Chunk(pFile, this, uiChunkID, 0);
1190  pSubChunks->push_back(pNewChunk);
1191  (*pSubChunksMap)[uiChunkID] = pNewChunk;
1192  pNewChunk->Resize(uiBodySize);
1194  pFile->LogAsResized(this);
1195  return pNewChunk;
1196  }
1197 
1209  void List::MoveSubChunk(Chunk* pSrc, Chunk* pDst) {
1210  if (!pSubChunks) LoadSubChunks();
1211  pSubChunks->remove(pSrc);
1212  ChunkList::iterator iter = find(pSubChunks->begin(), pSubChunks->end(), pDst);
1213  pSubChunks->insert(iter, pSrc);
1214  }
1215 
1224  void List::MoveSubChunk(Chunk* pSrc, List* pNewParent) {
1225  if (pNewParent == this || !pNewParent) return;
1226  if (!pSubChunks) LoadSubChunks();
1227  if (!pNewParent->pSubChunks) pNewParent->LoadSubChunks();
1228  pSubChunks->remove(pSrc);
1229  pNewParent->pSubChunks->push_back(pSrc);
1230  // update chunk id map of this List
1231  if ((*pSubChunksMap)[pSrc->GetChunkID()] == pSrc) {
1232  pSubChunksMap->erase(pSrc->GetChunkID());
1233  // try to find another chunk of the same chunk ID
1234  ChunkList::iterator iter = pSubChunks->begin();
1235  ChunkList::iterator end = pSubChunks->end();
1236  for (; iter != end; ++iter) {
1237  if ((*iter)->GetChunkID() == pSrc->GetChunkID()) {
1238  (*pSubChunksMap)[pSrc->GetChunkID()] = *iter;
1239  break; // we're done, stop search
1240  }
1241  }
1242  }
1243  // update chunk id map of other list
1244  if (!(*pNewParent->pSubChunksMap)[pSrc->GetChunkID()])
1245  (*pNewParent->pSubChunksMap)[pSrc->GetChunkID()] = pSrc;
1246  }
1247 
1257  List* List::AddSubList(uint32_t uiListType) {
1258  if (!pSubChunks) LoadSubChunks();
1259  List* pNewListChunk = new List(pFile, this, uiListType);
1260  pSubChunks->push_back(pNewListChunk);
1261  (*pSubChunksMap)[CHUNK_ID_LIST] = pNewListChunk;
1263  pFile->LogAsResized(this);
1264  return pNewListChunk;
1265  }
1266 
1277  void List::DeleteSubChunk(Chunk* pSubChunk) {
1278  if (!pSubChunks) LoadSubChunks();
1279  pSubChunks->remove(pSubChunk);
1280  if ((*pSubChunksMap)[pSubChunk->GetChunkID()] == pSubChunk) {
1281  pSubChunksMap->erase(pSubChunk->GetChunkID());
1282  // try to find another chunk of the same chunk ID
1283  ChunkList::iterator iter = pSubChunks->begin();
1284  ChunkList::iterator end = pSubChunks->end();
1285  for (; iter != end; ++iter) {
1286  if ((*iter)->GetChunkID() == pSubChunk->GetChunkID()) {
1287  (*pSubChunksMap)[pSubChunk->GetChunkID()] = *iter;
1288  break; // we're done, stop search
1289  }
1290  }
1291  }
1292  delete pSubChunk;
1293  }
1294 
1295  void List::ReadHeader(unsigned long fPos) {
1296  #if DEBUG
1297  std::cout << "List::Readheader(ulong) ";
1298  #endif // DEBUG
1299  Chunk::ReadHeader(fPos);
1300  if (CurrentChunkSize < 4) return;
1302  #if POSIX
1303  lseek(pFile->hFileRead, fPos + CHUNK_HEADER_SIZE, SEEK_SET);
1304  read(pFile->hFileRead, &ListType, 4);
1305  #elif defined(WIN32)
1306  SetFilePointer(pFile->hFileRead, fPos + CHUNK_HEADER_SIZE, NULL/*32 bit*/, FILE_BEGIN);
1307  DWORD dwBytesRead;
1308  ReadFile(pFile->hFileRead, &ListType, 4, &dwBytesRead, NULL);
1309  #else
1310  fseek(pFile->hFileRead, fPos + CHUNK_HEADER_SIZE, SEEK_SET);
1311  fread(&ListType, 4, 1, pFile->hFileRead);
1312  #endif // POSIX
1313  #if DEBUG
1314  std::cout << "listType=" << convertToString(ListType) << std::endl;
1315  #endif // DEBUG
1316  if (!pFile->bEndianNative) {
1317  //swapBytes_32(&ListType);
1318  }
1319  }
1320 
1321  void List::WriteHeader(unsigned long fPos) {
1322  // the four list type bytes officially belong the chunk's body in the RIFF format
1323  NewChunkSize += 4;
1324  Chunk::WriteHeader(fPos);
1325  NewChunkSize -= 4; // just revert the +4 incrementation
1326  #if POSIX
1327  lseek(pFile->hFileWrite, fPos + CHUNK_HEADER_SIZE, SEEK_SET);
1328  write(pFile->hFileWrite, &ListType, 4);
1329  #elif defined(WIN32)
1330  SetFilePointer(pFile->hFileWrite, fPos + CHUNK_HEADER_SIZE, NULL/*32 bit*/, FILE_BEGIN);
1331  DWORD dwBytesWritten;
1332  WriteFile(pFile->hFileWrite, &ListType, 4, &dwBytesWritten, NULL);
1333  #else
1334  fseek(pFile->hFileWrite, fPos + CHUNK_HEADER_SIZE, SEEK_SET);
1335  fwrite(&ListType, 4, 1, pFile->hFileWrite);
1336  #endif // POSIX
1337  }
1338 
1340  #if DEBUG
1341  std::cout << "List::LoadSubChunks()";
1342  #endif // DEBUG
1343  if (!pSubChunks) {
1344  pSubChunks = new ChunkList();
1345  pSubChunksMap = new ChunkMap();
1346  #if defined(WIN32)
1347  if (pFile->hFileRead == INVALID_HANDLE_VALUE) return;
1348  #else
1349  if (!pFile->hFileRead) return;
1350  #endif
1351  unsigned long uiOriginalPos = GetPos();
1352  SetPos(0); // jump to beginning of list chunk body
1353  while (RemainingBytes() >= CHUNK_HEADER_SIZE) {
1354  Chunk* ck;
1355  uint32_t ckid;
1356  Read(&ckid, 4, 1);
1357  #if DEBUG
1358  std::cout << " ckid=" << convertToString(ckid) << std::endl;
1359  #endif // DEBUG
1360  if (ckid == CHUNK_ID_LIST) {
1361  ck = new RIFF::List(pFile, ulStartPos + ulPos - 4, this);
1363  }
1364  else { // simple chunk
1365  ck = new RIFF::Chunk(pFile, ulStartPos + ulPos - 4, this);
1367  }
1368  pSubChunks->push_back(ck);
1369  (*pSubChunksMap)[ckid] = ck;
1370  if (GetPos() % 2 != 0) SetPos(1, RIFF::stream_curpos); // jump over pad byte
1371  }
1372  SetPos(uiOriginalPos); // restore position before this call
1373  }
1374  }
1375 
1377  for (List* pList = GetFirstSubList(); pList; pList = GetNextSubList())
1378  pList->LoadSubChunksRecursively();
1379  }
1380 
1395  unsigned long List::WriteChunk(unsigned long ulWritePos, unsigned long ulCurrentDataOffset) {
1396  const unsigned long ulOriginalPos = ulWritePos;
1397  ulWritePos += LIST_HEADER_SIZE;
1398 
1399  if (pFile->Mode != stream_mode_read_write)
1400  throw Exception("Cannot write list chunk, file has to be opened in read+write mode");
1401 
1402  // write all subchunks (including sub list chunks) recursively
1403  if (pSubChunks) {
1404  for (ChunkList::iterator iter = pSubChunks->begin(), end = pSubChunks->end(); iter != end; ++iter) {
1405  ulWritePos = (*iter)->WriteChunk(ulWritePos, ulCurrentDataOffset);
1406  }
1407  }
1408 
1409  // update this list chunk's header
1410  CurrentChunkSize = NewChunkSize = ulWritePos - ulOriginalPos - LIST_HEADER_SIZE;
1411  WriteHeader(ulOriginalPos);
1412 
1413  // offset of this list chunk in new written file may have changed
1414  ulStartPos = ulOriginalPos + LIST_HEADER_SIZE;
1415 
1416  return ulWritePos;
1417  }
1418 
1421  if (pSubChunks) {
1422  for (ChunkList::iterator iter = pSubChunks->begin(), end = pSubChunks->end(); iter != end; ++iter) {
1423  (*iter)->__resetPos();
1424  }
1425  }
1426  }
1427 
1432  return convertToString(ListType);
1433  }
1434 
1435 
1436 
1437 // *************** File ***************
1438 // *
1439 
1440 //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
1441 #define _GET_RESIZED_CHUNKS() \
1442  (reinterpret_cast<std::set<Chunk*>*>(ResizedChunks.front()))
1443 
1458  File::File(uint32_t FileType)
1459  : List(this), bIsNewFile(true), Layout(layout_standard)
1460  {
1461  //HACK: see _GET_RESIZED_CHUNKS() comment
1462  ResizedChunks.push_back(reinterpret_cast<Chunk*>(new std::set<Chunk*>));
1463  #if defined(WIN32)
1464  hFileRead = hFileWrite = INVALID_HANDLE_VALUE;
1465  #else
1466  hFileRead = hFileWrite = 0;
1467  #endif
1468  Mode = stream_mode_closed;
1469  bEndianNative = true;
1471  ListType = FileType;
1472  }
1473 
1482  File::File(const String& path)
1483  : List(this), Filename(path), bIsNewFile(false), Layout(layout_standard)
1484  {
1485  #if DEBUG
1486  std::cout << "File::File("<<path<<")" << std::endl;
1487  #endif // DEBUG
1488  bEndianNative = true;
1489  try {
1490  __openExistingFile(path);
1491  if (ChunkID != CHUNK_ID_RIFF && ChunkID != CHUNK_ID_RIFX) {
1492  throw RIFF::Exception("Not a RIFF file");
1493  }
1494  }
1495  catch (...) {
1496  Cleanup();
1497  throw;
1498  }
1499  }
1500 
1526  File::File(const String& path, uint32_t FileType, endian_t Endian, layout_t layout)
1527  : List(this), Filename(path), bIsNewFile(false), Layout(layout)
1528  {
1529  SetByteOrder(Endian);
1530  try {
1531  __openExistingFile(path, &FileType);
1532  }
1533  catch (...) {
1534  Cleanup();
1535  throw;
1536  }
1537  }
1538 
1549  void File::__openExistingFile(const String& path, uint32_t* FileType) {
1550  //HACK: see _GET_RESIZED_CHUNKS() comment
1551  ResizedChunks.push_back(reinterpret_cast<Chunk*>(new std::set<Chunk*>));
1552  #if POSIX
1553  hFileRead = hFileWrite = open(path.c_str(), O_RDONLY | O_NONBLOCK);
1554  if (hFileRead == -1) {
1555  hFileRead = hFileWrite = 0;
1556  String sError = strerror(errno);
1557  throw RIFF::Exception("Can't open \"" + path + "\": " + sError);
1558  }
1559  #elif defined(WIN32)
1560  hFileRead = hFileWrite = CreateFile(
1561  path.c_str(), GENERIC_READ,
1562  FILE_SHARE_READ | FILE_SHARE_WRITE,
1563  NULL, OPEN_EXISTING,
1564  FILE_ATTRIBUTE_NORMAL |
1565  FILE_FLAG_RANDOM_ACCESS, NULL
1566  );
1567  if (hFileRead == INVALID_HANDLE_VALUE) {
1568  hFileRead = hFileWrite = INVALID_HANDLE_VALUE;
1569  throw RIFF::Exception("Can't open \"" + path + "\"");
1570  }
1571  #else
1572  hFileRead = hFileWrite = fopen(path.c_str(), "rb");
1573  if (!hFileRead) throw RIFF::Exception("Can't open \"" + path + "\"");
1574  #endif // POSIX
1575  Mode = stream_mode_read;
1576  switch (Layout) {
1577  case layout_standard: // this is a normal RIFF file
1579  ReadHeader(0);
1580  if (FileType && ChunkID != *FileType)
1581  throw RIFF::Exception("Invalid file container ID");
1582  break;
1583  case layout_flat: // non-standard RIFF-alike file
1584  ulStartPos = 0;
1585  NewChunkSize = CurrentChunkSize = GetFileSize();
1586  if (FileType) {
1587  uint32_t ckid;
1588  if (Read(&ckid, 4, 1) != 4) {
1589  throw RIFF::Exception("Invalid file header ID (premature end of header)");
1590  } else if (ckid != *FileType) {
1591  String s = " (expected '" + convertToString(*FileType) + "' but got '" + convertToString(ckid) + "')";
1592  throw RIFF::Exception("Invalid file header ID" + s);
1593  }
1594  SetPos(0); // reset to first byte of file
1595  }
1596  LoadSubChunks();
1597  break;
1598  }
1599  }
1600 
1602  return Filename;
1603  }
1604 
1605  void File::SetFileName(const String& path) {
1606  Filename = path;
1607  }
1608 
1610  return Mode;
1611  }
1612 
1614  return Layout;
1615  }
1616 
1628  if (NewMode != Mode) {
1629  switch (NewMode) {
1630  case stream_mode_read:
1631  #if POSIX
1632  if (hFileRead) close(hFileRead);
1633  hFileRead = hFileWrite = open(Filename.c_str(), O_RDONLY | O_NONBLOCK);
1634  if (hFileRead == -1) {
1635  hFileRead = hFileWrite = 0;
1636  String sError = strerror(errno);
1637  throw Exception("Could not (re)open file \"" + Filename + "\" in read mode: " + sError);
1638  }
1639  #elif defined(WIN32)
1640  if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
1641  hFileRead = hFileWrite = CreateFile(
1642  Filename.c_str(), GENERIC_READ,
1643  FILE_SHARE_READ | FILE_SHARE_WRITE,
1644  NULL, OPEN_EXISTING,
1645  FILE_ATTRIBUTE_NORMAL |
1646  FILE_FLAG_RANDOM_ACCESS,
1647  NULL
1648  );
1649  if (hFileRead == INVALID_HANDLE_VALUE) {
1650  hFileRead = hFileWrite = INVALID_HANDLE_VALUE;
1651  throw Exception("Could not (re)open file \"" + Filename + "\" in read mode");
1652  }
1653  #else
1654  if (hFileRead) fclose(hFileRead);
1655  hFileRead = hFileWrite = fopen(Filename.c_str(), "rb");
1656  if (!hFileRead) throw Exception("Could not (re)open file \"" + Filename + "\" in read mode");
1657  #endif
1658  __resetPos(); // reset read/write position of ALL 'Chunk' objects
1659  break;
1661  #if POSIX
1662  if (hFileRead) close(hFileRead);
1663  hFileRead = hFileWrite = open(Filename.c_str(), O_RDWR | O_NONBLOCK);
1664  if (hFileRead == -1) {
1665  hFileRead = hFileWrite = open(Filename.c_str(), O_RDONLY | O_NONBLOCK);
1666  String sError = strerror(errno);
1667  throw Exception("Could not open file \"" + Filename + "\" in read+write mode: " + sError);
1668  }
1669  #elif defined(WIN32)
1670  if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
1671  hFileRead = hFileWrite = CreateFile(
1672  Filename.c_str(),
1673  GENERIC_READ | GENERIC_WRITE,
1674  FILE_SHARE_READ,
1675  NULL, OPEN_ALWAYS,
1676  FILE_ATTRIBUTE_NORMAL |
1677  FILE_FLAG_RANDOM_ACCESS,
1678  NULL
1679  );
1680  if (hFileRead == INVALID_HANDLE_VALUE) {
1681  hFileRead = hFileWrite = CreateFile(
1682  Filename.c_str(), GENERIC_READ,
1683  FILE_SHARE_READ | FILE_SHARE_WRITE,
1684  NULL, OPEN_EXISTING,
1685  FILE_ATTRIBUTE_NORMAL |
1686  FILE_FLAG_RANDOM_ACCESS,
1687  NULL
1688  );
1689  throw Exception("Could not (re)open file \"" + Filename + "\" in read+write mode");
1690  }
1691  #else
1692  if (hFileRead) fclose(hFileRead);
1693  hFileRead = hFileWrite = fopen(Filename.c_str(), "r+b");
1694  if (!hFileRead) {
1695  hFileRead = hFileWrite = fopen(Filename.c_str(), "rb");
1696  throw Exception("Could not open file \"" + Filename + "\" in read+write mode");
1697  }
1698  #endif
1699  __resetPos(); // reset read/write position of ALL 'Chunk' objects
1700  break;
1701  case stream_mode_closed:
1702  #if POSIX
1703  if (hFileRead) close(hFileRead);
1704  if (hFileWrite) close(hFileWrite);
1705  #elif defined(WIN32)
1706  if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
1707  if (hFileWrite != INVALID_HANDLE_VALUE) CloseHandle(hFileWrite);
1708  #else
1709  if (hFileRead) fclose(hFileRead);
1710  if (hFileWrite) fclose(hFileWrite);
1711  #endif
1712  hFileRead = hFileWrite = 0;
1713  break;
1714  default:
1715  throw Exception("Unknown file access mode");
1716  }
1717  Mode = NewMode;
1718  return true;
1719  }
1720  return false;
1721  }
1722 
1733  #if WORDS_BIGENDIAN
1734  bEndianNative = Endian != endian_little;
1735  #else
1736  bEndianNative = Endian != endian_big;
1737  #endif
1738  }
1739 
1750  void File::Save() {
1751  //TODO: implementation for the case where first chunk is not a global container (List chunk) is not implemented yet (i.e. Korg files)
1752  if (Layout == layout_flat)
1753  throw Exception("Saving a RIFF file with layout_flat is not implemented yet");
1754 
1755  // make sure the RIFF tree is built (from the original file)
1757 
1758  // reopen file in write mode
1760 
1761  // to be able to save the whole file without loading everything into
1762  // RAM and without having to store the data in a temporary file, we
1763  // enlarge the file with the sum of all _positive_ chunk size
1764  // changes, move current data towards the end of the file with the
1765  // calculated sum and finally update / rewrite the file by copying
1766  // the old data back to the right position at the beginning of the file
1767 
1768  // first we sum up all positive chunk size changes (and skip all negative ones)
1769  unsigned long ulPositiveSizeDiff = 0;
1770  std::set<Chunk*>* resizedChunks = _GET_RESIZED_CHUNKS();
1771  for (std::set<Chunk*>::const_iterator iter = resizedChunks->begin(), end = resizedChunks->end(); iter != end; ++iter) {
1772  if ((*iter)->GetNewSize() == 0) {
1773  throw Exception("There is at least one empty chunk (zero size): " + __resolveChunkPath(*iter));
1774  }
1775  unsigned long newSizePadded = (*iter)->GetNewSize() + (*iter)->GetNewSize() % 2;
1776  unsigned long oldSizePadded = (*iter)->GetSize() + (*iter)->GetSize() % 2;
1777  if (newSizePadded > oldSizePadded) ulPositiveSizeDiff += newSizePadded - oldSizePadded;
1778  }
1779 
1780  unsigned long ulWorkingFileSize = GetFileSize();
1781 
1782  // if there are positive size changes...
1783  if (ulPositiveSizeDiff > 0) {
1784  // ... we enlarge this file first ...
1785  ulWorkingFileSize += ulPositiveSizeDiff;
1786  ResizeFile(ulWorkingFileSize);
1787  // ... and move current data by the same amount towards end of file.
1788  int8_t* pCopyBuffer = new int8_t[4096];
1789  const unsigned long ulFileSize = GetSize() + RIFF_HEADER_SIZE;
1790  #if defined(WIN32)
1791  DWORD iBytesMoved = 1; // we have to pass it via pointer to the Windows API, thus the correct size must be ensured
1792  #else
1793  int iBytesMoved = 1;
1794  #endif
1795  for (unsigned long ulPos = ulFileSize; iBytesMoved > 0; ) {
1796  iBytesMoved = (ulPos < 4096) ? ulPos : 4096;
1797  ulPos -= iBytesMoved;
1798  #if POSIX
1799  lseek(hFileRead, ulPos, SEEK_SET);
1800  iBytesMoved = read(hFileRead, pCopyBuffer, iBytesMoved);
1801  lseek(hFileWrite, ulPos + ulPositiveSizeDiff, SEEK_SET);
1802  iBytesMoved = write(hFileWrite, pCopyBuffer, iBytesMoved);
1803  #elif defined(WIN32)
1804  SetFilePointer(hFileRead, ulPos, NULL/*32 bit*/, FILE_BEGIN);
1805  ReadFile(hFileRead, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
1806  SetFilePointer(hFileWrite, ulPos + ulPositiveSizeDiff, NULL/*32 bit*/, FILE_BEGIN);
1807  WriteFile(hFileWrite, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
1808  #else
1809  fseek(hFileRead, ulPos, SEEK_SET);
1810  iBytesMoved = fread(pCopyBuffer, 1, iBytesMoved, hFileRead);
1811  fseek(hFileWrite, ulPos + ulPositiveSizeDiff, SEEK_SET);
1812  iBytesMoved = fwrite(pCopyBuffer, 1, iBytesMoved, hFileWrite);
1813  #endif
1814  }
1815  delete[] pCopyBuffer;
1816  if (iBytesMoved < 0) throw Exception("Could not modify file while trying to enlarge it");
1817  }
1818 
1819  // rebuild / rewrite complete RIFF tree
1820  unsigned long ulTotalSize = WriteChunk(0, ulPositiveSizeDiff);
1821  unsigned long ulActualSize = __GetFileSize(hFileWrite);
1822 
1823  // resize file to the final size
1824  if (ulTotalSize < ulActualSize) ResizeFile(ulTotalSize);
1825 
1826  // forget all resized chunks
1827  resizedChunks->clear();
1828  }
1829 
1843  void File::Save(const String& path) {
1844  //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
1845 
1846  //TODO: implementation for the case where first chunk is not a global container (List chunk) is not implemented yet (i.e. Korg files)
1847  if (Layout == layout_flat)
1848  throw Exception("Saving a RIFF file with layout_flat is not implemented yet");
1849 
1850  // make sure the RIFF tree is built (from the original file)
1852 
1854  // open the other (new) file for writing and truncate it to zero size
1855  #if POSIX
1856  hFileWrite = open(path.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP);
1857  if (hFileWrite == -1) {
1859  String sError = strerror(errno);
1860  throw Exception("Could not open file \"" + path + "\" for writing: " + sError);
1861  }
1862  #elif defined(WIN32)
1863  hFileWrite = CreateFile(
1864  path.c_str(), GENERIC_WRITE, FILE_SHARE_READ,
1865  NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL |
1866  FILE_FLAG_RANDOM_ACCESS, NULL
1867  );
1868  if (hFileWrite == INVALID_HANDLE_VALUE) {
1870  throw Exception("Could not open file \"" + path + "\" for writing");
1871  }
1872  #else
1873  hFileWrite = fopen(path.c_str(), "w+b");
1874  if (!hFileWrite) {
1876  throw Exception("Could not open file \"" + path + "\" for writing");
1877  }
1878  #endif // POSIX
1879  Mode = stream_mode_read_write;
1880 
1881  // write complete RIFF tree to the other (new) file
1882  unsigned long ulTotalSize = WriteChunk(0, 0);
1883  unsigned long ulActualSize = __GetFileSize(hFileWrite);
1884 
1885  // resize file to the final size (if the file was originally larger)
1886  if (ulTotalSize < ulActualSize) ResizeFile(ulTotalSize);
1887 
1888  // forget all resized chunks
1889  _GET_RESIZED_CHUNKS()->clear();
1890 
1891  #if POSIX
1892  if (hFileWrite) close(hFileWrite);
1893  #elif defined(WIN32)
1894  if (hFileWrite != INVALID_HANDLE_VALUE) CloseHandle(hFileWrite);
1895  #else
1896  if (hFileWrite) fclose(hFileWrite);
1897  #endif
1899 
1900  // associate new file with this File object from now on
1901  Filename = path;
1902  bIsNewFile = false;
1903  Mode = (stream_mode_t) -1; // Just set it to an undefined mode ...
1904  SetMode(stream_mode_read_write); // ... so SetMode() has to reopen the file handles.
1905  }
1906 
1907  void File::ResizeFile(unsigned long ulNewSize) {
1908  #if POSIX
1909  if (ftruncate(hFileWrite, ulNewSize) < 0)
1910  throw Exception("Could not resize file \"" + Filename + "\"");
1911  #elif defined(WIN32)
1912  if (
1913  SetFilePointer(hFileWrite, ulNewSize, NULL/*32 bit*/, FILE_BEGIN) == INVALID_SET_FILE_POINTER ||
1914  !SetEndOfFile(hFileWrite)
1915  ) throw Exception("Could not resize file \"" + Filename + "\"");
1916  #else
1917  # error Sorry, this version of libgig only supports POSIX and Windows systems yet.
1918  # error Reason: portable implementation of RIFF::File::ResizeFile() is missing (yet)!
1919  #endif
1920  }
1921 
1923  #if DEBUG
1924  std::cout << "File::~File()" << std::endl;
1925  #endif // DEBUG
1926  Cleanup();
1927  }
1928 
1933  bool File::IsNew() const {
1934  return bIsNewFile;
1935  }
1936 
1937  void File::Cleanup() {
1938  #if POSIX
1939  if (hFileRead) close(hFileRead);
1940  #elif defined(WIN32)
1941  if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
1942  #else
1943  if (hFileRead) fclose(hFileRead);
1944  #endif // POSIX
1945  DeleteChunkList();
1946  pFile = NULL;
1947  //HACK: see _GET_RESIZED_CHUNKS() comment
1948  delete _GET_RESIZED_CHUNKS();
1949  }
1950 
1951  void File::LogAsResized(Chunk* pResizedChunk) {
1952  _GET_RESIZED_CHUNKS()->insert(pResizedChunk);
1953  }
1954 
1955  void File::UnlogResized(Chunk* pResizedChunk) {
1956  _GET_RESIZED_CHUNKS()->erase(pResizedChunk);
1957  }
1958 
1959  unsigned long File::GetFileSize() {
1960  return __GetFileSize(hFileRead);
1961  }
1962 
1963  #if POSIX
1964  unsigned long File::__GetFileSize(int hFile) {
1965  struct stat filestat;
1966  fstat(hFile, &filestat);
1967  long size = filestat.st_size;
1968  return size;
1969  }
1970  #elif defined(WIN32)
1971  unsigned long File::__GetFileSize(HANDLE hFile) {
1972  DWORD dwSize = ::GetFileSize(hFile, NULL /*32bit*/);
1973  if (dwSize == INVALID_FILE_SIZE)
1974  throw Exception("Windows FS error: could not determine file size");
1975  return dwSize;
1976  }
1977  #else // standard C functions
1978  unsigned long File::__GetFileSize(FILE* hFile) {
1979  long curpos = ftell(hFile);
1980  fseek(hFile, 0, SEEK_END);
1981  long size = ftell(hFile);
1982  fseek(hFile, curpos, SEEK_SET);
1983  return size;
1984  }
1985  #endif
1986 
1987 
1988 // *************** Exception ***************
1989 // *
1990 
1992  std::cout << "RIFF::Exception: " << Message << std::endl;
1993  }
1994 
1995 
1996 // *************** functions ***************
1997 // *
1998 
2005  return PACKAGE;
2006  }
2007 
2013  return VERSION;
2014  }
2015 
2016 } // namespace RIFF
virtual unsigned long WriteChunk(unsigned long ulWritePos, unsigned long ulCurrentDataOffset)
Write chunk persistently e.g.
Definition: RIFF.cpp:846
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:632
#define _GET_RESIZED_CHUNKS()
Definition: RIFF.cpp:1441
int16_t ReadInt16()
Reads one 16 Bit signed integer word and increments the position within the chunk.
Definition: RIFF.cpp:676
bool bEndianNative
Definition: RIFF.h:355
void UnlogResized(Chunk *pResizedChunk)
Definition: RIFF.cpp:1955
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:284
Chunk * GetFirstSubChunk()
Returns the first subchunk within the list.
Definition: RIFF.cpp:1050
String libraryName()
Returns the name of this C++ library.
Definition: RIFF.cpp:2004
File(uint32_t FileType)
Create new RIFF file.
Definition: RIFF.cpp:1458
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:541
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:1613
String GetFileName()
Definition: RIFF.cpp:1601
void ReadHeader(unsigned long fPos)
Definition: RIFF.cpp:1295
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:203
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:611
#define LIST_HEADER_SIZE
Definition: RIFF.h:111
void WriteHeader(unsigned long fPos)
Definition: RIFF.cpp:1321
String libraryVersion()
Returns version of this C++ library.
Definition: RIFF.cpp:2012
List * GetSubList(uint32_t ListType)
Returns sublist chunk with list type ListType within this chunk list.
Definition: RIFF.cpp:1025
void DeleteSubChunk(Chunk *pSubChunk)
Removes a sub chunk.
Definition: RIFF.cpp:1277
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:504
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:235
List * GetFirstSubList()
Returns the first sublist within the list (that is a subchunk with chunk ID "LIST").
Definition: RIFF.cpp:1084
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:1732
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:643
void ReadHeader(unsigned long fPos)
Definition: RIFF.cpp:106
String GetListTypeString()
Returns string representation of the lists&#39;s id.
Definition: RIFF.cpp:1431
ChunkList::iterator ListIterator
Definition: RIFF.h:309
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:578
void DeleteChunkList()
Definition: RIFF.cpp:978
Chunk * GetSubChunk(uint32_t ChunkID)
Returns subchunk with chunk ID ChunkID within this chunk list.
Definition: RIFF.cpp:1006
Chunk * GetNextSubChunk()
Returns the next subchunk within the list.
Definition: RIFF.cpp:1066
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:392
int32_t ReadInt32()
Reads one 32 Bit signed integer word and increments the position within the chunk.
Definition: RIFF.cpp:710
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:253
void LoadSubChunks()
Definition: RIFF.cpp:1339
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:727
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:342
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:1186
void PrintMessage()
Definition: RIFF.cpp:1991
int hFileRead
handle / descriptor for reading from file
Definition: RIFF.h:345
Not a "real" 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:101
void LogAsResized(Chunk *pResizedChunk)
Definition: RIFF.cpp:1951
virtual void __resetPos()
Sets Chunk&#39;s read/write position to zero.
Definition: RIFF.cpp:936
uint16_t ReadUint16()
Reads one 16 Bit unsigned integer word and increments the position within the chunk.
Definition: RIFF.cpp:693
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:1419
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:1605
void * LoadChunkData()
Load chunk body into RAM.
Definition: RIFF.cpp:757
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:430
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:1257
#define CHUNK_ID_LIST
Definition: RIFF.h:95
void LoadSubChunksRecursively()
Definition: RIFF.cpp:1376
RIFF specific classes and definitions.
Definition: RIFF.h:134
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:1933
List(File *pFile, unsigned long StartPos, List *Parent)
Definition: RIFF.cpp:953
virtual unsigned long WriteChunk(unsigned long ulWritePos, unsigned long ulCurrentDataOffset)
Write list chunk persistently e.g.
Definition: RIFF.cpp:1395
void MoveSubChunk(Chunk *pSrc, Chunk *pDst)
Moves a sub chunk witin this list.
Definition: RIFF.cpp:1209
unsigned int CountSubChunks()
Returns number of subchunks within the list.
Definition: RIFF.cpp:1124
virtual ~List()
Definition: RIFF.cpp:971
String convertToString(uint32_t word)
Definition: RIFF.h:260
bool SetMode(stream_mode_t NewMode)
Change file access mode.
Definition: RIFF.cpp:1627
virtual void Save()
Save changes to same file.
Definition: RIFF.cpp:1750
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:467
stream_mode_t GetMode()
Definition: RIFF.cpp:1609
void ReleaseChunkData()
Free loaded chunk body from RAM.
Definition: RIFF.cpp:801
virtual ~File()
Definition: RIFF.cpp:1922
unsigned int CountSubLists()
Returns number of sublists within the list.
Definition: RIFF.cpp:1150
String GetChunkIDString()
Returns the String representation of the chunk&#39;s ID (e.g.
Definition: RIFF.cpp:187
uint32_t NewChunkSize
Definition: RIFF.h:225
void WriteHeader(unsigned long fPos)
Definition: RIFF.cpp:149
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:826
List * GetNextSubList()
Returns the next sublist (that is a subchunk with chunk ID "LIST") within the list.
Definition: RIFF.cpp:1106
Chunk(File *pFile, unsigned long StartPos, List *Parent)
Definition: RIFF.cpp:74
uint8_t ReadUint8()
Reads one 8 Bit unsigned integer word and increments the position within the chunk.
Definition: RIFF.cpp:659
uint32_t ListType
Definition: RIFF.h:305