libgig  4.6.0.svn9
RIFF.cpp
1 /***************************************************************************
2  * *
3  * libgig - C++ cross-platform Gigasampler format file access library *
4  * *
5  * Copyright (C) 2003-2026 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 #if POSIX
37 # define RIFF_INVALID_FILE_HANDLE -1 /* not 0 as 0 refers to stdin on POSIX */
38 #elif defined(WIN32)
39 # define RIFF_INVALID_FILE_HANDLE INVALID_HANDLE_VALUE
40 #else
41 # define RIFF_INVALID_FILE_HANDLE NULL
42 #endif
43 
44 namespace RIFF {
45 
46 // *************** Internal functions **************
47 // *
48 
50  static String __resolveChunkPath(Chunk* pCk) {
51  String sPath;
52  for (Chunk* pChunk = pCk; pChunk; pChunk = pChunk->GetParent()) {
53  if (pChunk->GetChunkID() == CHUNK_ID_LIST) {
54  List* pList = (List*) pChunk;
55  sPath = "->'" + pList->GetListTypeString() + "'" + sPath;
56  } else {
57  sPath = "->'" + pChunk->GetChunkIDString() + "'" + sPath;
58  }
59  }
60  return sPath;
61  }
62 
63  inline static bool _isValidHandle(File::Handle handle) {
64  return handle != RIFF_INVALID_FILE_HANDLE;
65  }
66 
67  inline static void _close(File::Handle handle) {
68  if (!_isValidHandle(handle)) return;
69  #if POSIX
70  close(handle);
71  #elif defined(WIN32)
72  CloseHandle(handle);
73  #else
74  fclose(handle);
75  #endif
76  }
77 
78 
79 
80 // *************** progress_t ***************
81 // *
82 
83  progress_t::progress_t() {
84  callback = NULL;
85  custom = NULL;
86  activity = "";
87  __range_min = 0.0f;
88  __range_max = 1.0f;
89  __private = NULL;
90  }
91 
92  progress_t::~progress_t() {
93  }
94 
102  std::vector<progress_t> progress_t::subdivide(int iSubtasks) const {
103  std::vector<progress_t> v;
104  for (int i = 0; i < iSubtasks; ++i) {
105  progress_t p;
106  __divide_progress(this, &p, iSubtasks, i);
107  v.push_back(p);
108  }
109  return v;
110  }
111 
134  std::vector<progress_t> progress_t::subdivide(std::vector<float> vSubTaskPortions) const {
135  float fTotal = 0.f; // usually 1.0, but we sum the portions up below to be sure
136  for (int i = 0; i < vSubTaskPortions.size(); ++i)
137  fTotal += vSubTaskPortions[i];
138 
139  float fLow = 0.f, fHigh = 0.f;
140  std::vector<progress_t> v;
141  for (int i = 0; i < vSubTaskPortions.size(); ++i) {
142  fLow = fHigh;
143  fHigh = vSubTaskPortions[i];
144  progress_t p;
145  __divide_progress(this, &p, fTotal, fLow, fHigh);
146  v.push_back(p);
147  }
148  return v;
149  }
150 
151 
152 
153 // *************** _FileProgress **************
154 // *
155 
156 struct _FileProgress {
157  file_offset_t current;
158  file_offset_t total;
159 
160  void advance(progress_t* progress, file_offset_t bytes) {
161  current += bytes;
162  __notify_progress(progress, float(current) / float(total));
163  }
164 };
165 
166 
167 
168 // *************** Chunk **************
169 // *
170 
171  Chunk::Chunk(File* pFile) {
172  #if DEBUG_RIFF
173  std::cout << "Chunk::Chunk(File* pFile)" << std::endl;
174  #endif // DEBUG_RIFF
175  chunkPos.ullPos = 0;
176  pParent = NULL;
177  pChunkData = NULL;
178  ullCurrentChunkSize = 0;
179  ullNewChunkSize = 0;
180  ullChunkDataSize = 0;
181  ChunkID = CHUNK_ID_RIFF;
182  this->pFile = pFile;
183  }
184 
185  Chunk::Chunk(File* pFile, file_offset_t StartPos, List* Parent) {
186  #if DEBUG_RIFF
187  std::cout << "Chunk::Chunk(File*,file_offset_t,List*),StartPos=" << StartPos << std::endl;
188  #endif // DEBUG_RIFF
189  this->pFile = pFile;
190  ullStartPos = StartPos + CHUNK_HEADER_SIZE(pFile->FileOffsetSize);
191  pParent = Parent;
192  chunkPos.ullPos = 0;
193  pChunkData = NULL;
194  ullCurrentChunkSize = 0;
195  ullNewChunkSize = 0;
196  ullChunkDataSize = 0;
197  ReadHeader(StartPos);
198  }
199 
200  Chunk::Chunk(File* pFile, List* pParent, uint32_t uiChunkID, file_offset_t ullBodySize) {
201  this->pFile = pFile;
202  ullStartPos = 0; // arbitrary usually, since it will be updated when we write the chunk
203  this->pParent = pParent;
204  chunkPos.ullPos = 0;
205  pChunkData = NULL;
206  ChunkID = uiChunkID;
207  ullChunkDataSize = 0;
208  ullCurrentChunkSize = 0;
209  ullNewChunkSize = ullBodySize;
210  }
211 
212  Chunk::~Chunk() {
213  if (pChunkData) delete[] pChunkData;
214  }
215 
216  void Chunk::ReadHeader(file_offset_t filePos) {
217  #if DEBUG_RIFF
218  std::cout << "Chunk::Readheader(" << filePos << ") ";
219  #endif // DEBUG_RIFF
220  ChunkID = 0;
221  ullNewChunkSize = ullCurrentChunkSize = 0;
222 
223  const File::Handle hRead = pFile->FileHandle();
224 
225  #if POSIX
226  if (lseek(hRead, filePos, SEEK_SET) != -1) {
227  read(hRead, &ChunkID, 4);
228  read(hRead, &ullCurrentChunkSize, pFile->FileOffsetSize);
229  #elif defined(WIN32)
230  LARGE_INTEGER liFilePos;
231  liFilePos.QuadPart = filePos;
232  if (SetFilePointerEx(hRead, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN)) {
233  DWORD dwBytesRead;
234  ReadFile(hRead, &ChunkID, 4, &dwBytesRead, NULL);
235  ReadFile(hRead, &ullCurrentChunkSize, pFile->FileOffsetSize, &dwBytesRead, NULL);
236  #else
237  if (!fseeko(hRead, filePos, SEEK_SET)) {
238  fread(&ChunkID, 4, 1, hRead);
239  fread(&ullCurrentChunkSize, pFile->FileOffsetSize, 1, hRead);
240  #endif // POSIX
241  #if WORDS_BIGENDIAN
242  if (ChunkID == CHUNK_ID_RIFF) {
243  pFile->bEndianNative = false;
244  }
245  #else // little endian
246  if (ChunkID == CHUNK_ID_RIFX) {
247  pFile->bEndianNative = false;
248  ChunkID = CHUNK_ID_RIFF;
249  }
250  #endif // WORDS_BIGENDIAN
251  if (!pFile->bEndianNative) {
252  //swapBytes_32(&ChunkID);
253  if (pFile->FileOffsetSize == 4)
254  swapBytes_32(&ullCurrentChunkSize);
255  else
256  swapBytes_64(&ullCurrentChunkSize);
257  }
258  #if WORDS_BIGENDIAN
259  // shift after byte-swap so that byte-swap touched the actual data
260  if (pFile->FileOffsetSize == 4)
261  ullCurrentChunkSize >>= 32;
262  #endif // WORDS_BIGENDIAN
263  #if DEBUG_RIFF
264  std::cout << "ckID=" << convertToString(ChunkID) << " ";
265  std::cout << "ckSize=" << ullCurrentChunkSize << " ";
266  std::cout << "bEndianNative=" << pFile->bEndianNative << std::endl;
267  #endif // DEBUG_RIFF
268  ullNewChunkSize = ullCurrentChunkSize;
269  }
270  }
271 
272  void Chunk::WriteHeader(file_offset_t filePos) {
273  uint32_t uiNewChunkID = ChunkID;
274  if (ChunkID == CHUNK_ID_RIFF) {
275  #if WORDS_BIGENDIAN
276  if (pFile->bEndianNative) uiNewChunkID = CHUNK_ID_RIFX;
277  #else // little endian
278  if (!pFile->bEndianNative) uiNewChunkID = CHUNK_ID_RIFX;
279  #endif // WORDS_BIGENDIAN
280  }
281 
282  uint64_t ullNewChunkSize = this->ullNewChunkSize;
283  #if WORDS_BIGENDIAN
284  // shift before byte-swap so that byte-swap touches the actual data
285  if (pFile->FileOffsetSize == 4)
286  ullNewChunkSize <<= 32;
287  #endif
288  if (!pFile->bEndianNative) {
289  if (pFile->FileOffsetSize == 4)
290  swapBytes_32(&ullNewChunkSize);
291  else
292  swapBytes_64(&ullNewChunkSize);
293  }
294 
295  const File::Handle hWrite = pFile->FileWriteHandle();
296 
297  #if POSIX
298  if (lseek(hWrite, filePos, SEEK_SET) != -1) {
299  write(hWrite, &uiNewChunkID, 4);
300  write(hWrite, &ullNewChunkSize, pFile->FileOffsetSize);
301  }
302  #elif defined(WIN32)
303  LARGE_INTEGER liFilePos;
304  liFilePos.QuadPart = filePos;
305  if (SetFilePointerEx(hWrite, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN)) {
306  DWORD dwBytesWritten;
307  WriteFile(hWrite, &uiNewChunkID, 4, &dwBytesWritten, NULL);
308  WriteFile(hWrite, &ullNewChunkSize, pFile->FileOffsetSize, &dwBytesWritten, NULL);
309  }
310  #else
311  if (!fseeko(hWrite, filePos, SEEK_SET)) {
312  fwrite(&uiNewChunkID, 4, 1, hWrite);
313  fwrite(&ullNewChunkSize, pFile->FileOffsetSize, 1, hWrite);
314  }
315  #endif // POSIX
316  }
317 
322  String Chunk::GetChunkIDString() const {
323  return convertToString(ChunkID);
324  }
325 
334  file_offset_t& Chunk::GetPosUnsafeRef() {
335  if (!pFile->IsIOPerThread()) return chunkPos.ullPos;
336  const std::thread::id tid = std::this_thread::get_id();
337  return chunkPos.byThread[tid];
338  }
339 
346  if (!pFile->IsIOPerThread()) return chunkPos.ullPos;
347  const std::thread::id tid = std::this_thread::get_id();
348  std::lock_guard<std::mutex> lock(chunkPos.mutex);
349  return chunkPos.byThread[tid];
350  }
351 
359  return ullStartPos + GetPos();
360  }
361 
376  #if DEBUG_RIFF
377  std::cout << "Chunk::SetPos(file_offset_t,stream_whence_t)" << std::endl;
378  #endif // DEBUG_RIFF
379  std::lock_guard<std::mutex> lock(chunkPos.mutex);
380  file_offset_t& pos = GetPosUnsafeRef();
381  switch (Whence) {
382  case stream_curpos:
383  pos += Where;
384  break;
385  case stream_end:
386  pos = ullCurrentChunkSize - 1 - Where;
387  break;
388  case stream_backward:
389  pos -= Where;
390  break;
391  case stream_start: default:
392  pos = Where;
393  break;
394  }
395  if (pos > ullCurrentChunkSize) pos = ullCurrentChunkSize;
396  return pos;
397  }
398 
411  #if DEBUG_RIFF
412  std::cout << "Chunk::Remainingbytes()=" << ullCurrentChunkSize - ullPos << std::endl;
413  #endif // DEBUG_RIFF
414  const file_offset_t pos = GetPos();
415  return (ullCurrentChunkSize > pos) ? ullCurrentChunkSize - pos : 0;
416  }
417 
426  return CHUNK_HEADER_SIZE(fileOffsetSize) + // RIFF chunk header
427  ullNewChunkSize + // chunks's actual data body
428  ullNewChunkSize % 2; // optional pad byte
429  }
430 
445  #if DEBUG_RIFF
446  std::cout << "Chunk::GetState()" << std::endl;
447  #endif // DEBUG_RIFF
448 
449  const File::Handle hRead = pFile->FileHandle();
450 
451  if (!_isValidHandle(hRead))
452  return stream_closed;
453 
454  const file_offset_t pos = GetPos();
455  if (pos < ullCurrentChunkSize) return stream_ready;
456  else return stream_end_reached;
457  }
458 
475  file_offset_t Chunk::Read(void* pData, file_offset_t WordCount, file_offset_t WordSize) {
476  #if DEBUG_RIFF
477  std::cout << "Chunk::Read(void*,file_offset_t,file_offset_t)" << std::endl;
478  #endif // DEBUG_RIFF
479  //if (ulStartPos == 0) return 0; // is only 0 if this is a new chunk, so nothing to read (yet)
480  const file_offset_t pos = GetPos();
481  if (pos >= ullCurrentChunkSize) return 0;
482  if (pos + WordCount * WordSize >= ullCurrentChunkSize)
483  WordCount = (ullCurrentChunkSize - pos) / WordSize;
484 
485  const File::Handle hRead = pFile->FileHandle();
486 
487  #if POSIX
488  if (lseek(hRead, ullStartPos + pos, SEEK_SET) < 0) return 0;
489  ssize_t readWords = read(hRead, pData, WordCount * WordSize);
490  if (readWords < 1) {
491  #if DEBUG_RIFF
492  std::cerr << "POSIX read() failed: " << strerror(errno) << std::endl << std::flush;
493  #endif // DEBUG_RIFF
494  return 0;
495  }
496  readWords /= WordSize;
497  #elif defined(WIN32)
498  LARGE_INTEGER liFilePos;
499  liFilePos.QuadPart = ullStartPos + pos;
500  if (!SetFilePointerEx(hRead, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN))
501  return 0;
502  DWORD readWords;
503  ReadFile(hRead, pData, WordCount * WordSize, &readWords, NULL); //FIXME: does not work for reading buffers larger than 2GB (even though this should rarely be the case in practice)
504  if (readWords < 1) return 0;
505  readWords /= WordSize;
506  #else // standard C functions
507  if (fseeko(hRead, ullStartPos + pos, SEEK_SET)) return 0;
508  file_offset_t readWords = fread(pData, WordSize, WordCount, hRead);
509  #endif // POSIX
510  if (!pFile->bEndianNative && WordSize != 1)
511  swapBytesOfWords(pData, readWords, WordSize);
512  SetPos(readWords * WordSize, stream_curpos);
513  return readWords;
514  }
515 
533  file_offset_t Chunk::Write(void* pData, file_offset_t WordCount, file_offset_t WordSize) {
534  const File::HandlePair io = pFile->FileHandlePair();
535  if (io.Mode != stream_mode_read_write)
536  throw Exception("Cannot write data to chunk, file has to be opened in read+write mode first");
537  const file_offset_t pos = GetPos();
538  if (pos >= ullCurrentChunkSize || pos + WordCount * WordSize > ullCurrentChunkSize)
539  throw Exception("End of chunk reached while trying to write data");
540  if (!pFile->bEndianNative && WordSize != 1)
541  // temporarily swap bytes for non-native endian
542  swapBytesOfWords(pData, WordCount, WordSize);
543  #if POSIX
544  if (lseek(io.hWrite, ullStartPos + pos, SEEK_SET) < 0) {
545  throw Exception("Could not seek to position " + ToString(pos) +
546  " in chunk (" + ToString(ullStartPos + pos) + " in file)");
547  }
548  ssize_t writtenWords = write(io.hWrite, pData, WordCount * WordSize);
549  if (writtenWords < 1) throw Exception("POSIX IO Error while trying to write chunk data");
550  writtenWords /= WordSize;
551  #elif defined(WIN32)
552  LARGE_INTEGER liFilePos;
553  liFilePos.QuadPart = ullStartPos + pos;
554  if (!SetFilePointerEx(io.hWrite, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN)) {
555  throw Exception("Could not seek to position " + ToString(pos) +
556  " in chunk (" + ToString(ullStartPos + pos) + " in file)");
557  }
558  DWORD writtenWords;
559  WriteFile(io.hWrite, pData, WordCount * WordSize, &writtenWords, NULL); //FIXME: does not work for writing buffers larger than 2GB (even though this should rarely be the case in practice)
560  if (writtenWords < 1) throw Exception("Windows IO Error while trying to write chunk data");
561  writtenWords /= WordSize;
562  #else // standard C functions
563  if (fseeko(io.hWrite, ullStartPos + pos, SEEK_SET)) {
564  throw Exception("Could not seek to position " + ToString(pos) +
565  " in chunk (" + ToString(ullStartPos + pos) + " in file)");
566  }
567  file_offset_t writtenWords = fwrite(pData, WordSize, WordCount, io.hWrite);
568  #endif // POSIX
569  if (!pFile->bEndianNative && WordSize != 1)
570  // revert temporary byte swapping
571  swapBytesOfWords(pData, WordCount, WordSize);
572  SetPos(writtenWords * WordSize, stream_curpos);
573  return writtenWords;
574  }
575 
577  file_offset_t Chunk::ReadSceptical(void* pData, file_offset_t WordCount, file_offset_t WordSize) {
578  file_offset_t readWords = Read(pData, WordCount, WordSize);
579  if (readWords != WordCount) throw RIFF::Exception("End of chunk data reached.");
580  return readWords;
581  }
582 
595  file_offset_t Chunk::ReadInt8(int8_t* pData, file_offset_t WordCount) {
596  #if DEBUG_RIFF
597  std::cout << "Chunk::ReadInt8(int8_t*,file_offset_t)" << std::endl;
598  #endif // DEBUG_RIFF
599  return ReadSceptical(pData, WordCount, 1);
600  }
601 
617  file_offset_t Chunk::WriteInt8(int8_t* pData, file_offset_t WordCount) {
618  return Write(pData, WordCount, 1);
619  }
620 
634  file_offset_t Chunk::ReadUint8(uint8_t* pData, file_offset_t WordCount) {
635  #if DEBUG_RIFF
636  std::cout << "Chunk::ReadUint8(uint8_t*,file_offset_t)" << std::endl;
637  #endif // DEBUG_RIFF
638  return ReadSceptical(pData, WordCount, 1);
639  }
640 
656  file_offset_t Chunk::WriteUint8(uint8_t* pData, file_offset_t WordCount) {
657  return Write(pData, WordCount, 1);
658  }
659 
673  file_offset_t Chunk::ReadInt16(int16_t* pData, file_offset_t WordCount) {
674  #if DEBUG_RIFF
675  std::cout << "Chunk::ReadInt16(int16_t*,file_offset_t)" << std::endl;
676  #endif // DEBUG_RIFF
677  return ReadSceptical(pData, WordCount, 2);
678  }
679 
695  file_offset_t Chunk::WriteInt16(int16_t* pData, file_offset_t WordCount) {
696  return Write(pData, WordCount, 2);
697  }
698 
712  file_offset_t Chunk::ReadUint16(uint16_t* pData, file_offset_t WordCount) {
713  #if DEBUG_RIFF
714  std::cout << "Chunk::ReadUint16(uint16_t*,file_offset_t)" << std::endl;
715  #endif // DEBUG_RIFF
716  return ReadSceptical(pData, WordCount, 2);
717  }
718 
734  file_offset_t Chunk::WriteUint16(uint16_t* pData, file_offset_t WordCount) {
735  return Write(pData, WordCount, 2);
736  }
737 
751  file_offset_t Chunk::ReadInt32(int32_t* pData, file_offset_t WordCount) {
752  #if DEBUG_RIFF
753  std::cout << "Chunk::ReadInt32(int32_t*,file_offset_t)" << std::endl;
754  #endif // DEBUG_RIFF
755  return ReadSceptical(pData, WordCount, 4);
756  }
757 
773  file_offset_t Chunk::WriteInt32(int32_t* pData, file_offset_t WordCount) {
774  return Write(pData, WordCount, 4);
775  }
776 
790  file_offset_t Chunk::ReadUint32(uint32_t* pData, file_offset_t WordCount) {
791  #if DEBUG_RIFF
792  std::cout << "Chunk::ReadUint32(uint32_t*,file_offset_t)" << std::endl;
793  #endif // DEBUG_RIFF
794  return ReadSceptical(pData, WordCount, 4);
795  }
796 
808  void Chunk::ReadString(String& s, int size) {
809  char* buf = new char[size];
810  ReadSceptical(buf, 1, size);
811  s.assign(buf, std::find(buf, buf + size, '\0'));
812  delete[] buf;
813  }
814 
830  file_offset_t Chunk::WriteUint32(uint32_t* pData, file_offset_t WordCount) {
831  return Write(pData, WordCount, 4);
832  }
833 
842  int8_t Chunk::ReadInt8() {
843  #if DEBUG_RIFF
844  std::cout << "Chunk::ReadInt8()" << std::endl;
845  #endif // DEBUG_RIFF
846  int8_t word;
847  ReadSceptical(&word,1,1);
848  return word;
849  }
850 
859  uint8_t Chunk::ReadUint8() {
860  #if DEBUG_RIFF
861  std::cout << "Chunk::ReadUint8()" << std::endl;
862  #endif // DEBUG_RIFF
863  uint8_t word;
864  ReadSceptical(&word,1,1);
865  return word;
866  }
867 
877  int16_t Chunk::ReadInt16() {
878  #if DEBUG_RIFF
879  std::cout << "Chunk::ReadInt16()" << std::endl;
880  #endif // DEBUG_RIFF
881  int16_t word;
882  ReadSceptical(&word,1,2);
883  return word;
884  }
885 
895  uint16_t Chunk::ReadUint16() {
896  #if DEBUG_RIFF
897  std::cout << "Chunk::ReadUint16()" << std::endl;
898  #endif // DEBUG_RIFF
899  uint16_t word;
900  ReadSceptical(&word,1,2);
901  return word;
902  }
903 
913  int32_t Chunk::ReadInt32() {
914  #if DEBUG_RIFF
915  std::cout << "Chunk::ReadInt32()" << std::endl;
916  #endif // DEBUG_RIFF
917  int32_t word;
918  ReadSceptical(&word,1,4);
919  return word;
920  }
921 
931  uint32_t Chunk::ReadUint32() {
932  #if DEBUG_RIFF
933  std::cout << "Chunk::ReadUint32()" << std::endl;
934  #endif // DEBUG_RIFF
935  uint32_t word;
936  ReadSceptical(&word,1,4);
937  return word;
938  }
939 
963  if (!pChunkData && pFile->Filename != "" /*&& ulStartPos != 0*/) {
964  File::Handle hRead = pFile->FileHandle();
965  #if POSIX
966  if (lseek(hRead, ullStartPos, SEEK_SET) == -1) return NULL;
967  #elif defined(WIN32)
968  LARGE_INTEGER liFilePos;
969  liFilePos.QuadPart = ullStartPos;
970  if (!SetFilePointerEx(hRead, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN)) return NULL;
971  #else
972  if (fseeko(hRead, ullStartPos, SEEK_SET)) return NULL;
973  #endif // POSIX
974  file_offset_t ullBufferSize = (ullCurrentChunkSize > ullNewChunkSize) ? ullCurrentChunkSize : ullNewChunkSize;
975  pChunkData = new uint8_t[ullBufferSize];
976  if (!pChunkData) return NULL;
977  memset(pChunkData, 0, ullBufferSize);
978  #if POSIX
979  file_offset_t readWords = read(hRead, pChunkData, GetSize());
980  #elif defined(WIN32)
981  DWORD readWords;
982  ReadFile(hRead, pChunkData, GetSize(), &readWords, NULL); //FIXME: won't load chunks larger than 2GB !
983  #else
984  file_offset_t readWords = fread(pChunkData, 1, GetSize(), hRead);
985  #endif // POSIX
986  if (readWords != GetSize()) {
987  delete[] pChunkData;
988  return (pChunkData = NULL);
989  }
990  ullChunkDataSize = ullBufferSize;
991  } else if (ullNewChunkSize > ullChunkDataSize) {
992  uint8_t* pNewBuffer = new uint8_t[ullNewChunkSize];
993  if (!pNewBuffer) throw Exception("Could not enlarge chunk data buffer to " + ToString(ullNewChunkSize) + " bytes");
994  memset(pNewBuffer, 0 , ullNewChunkSize);
995  if (pChunkData) {
996  memcpy(pNewBuffer, pChunkData, ullChunkDataSize);
997  delete[] pChunkData;
998  }
999  pChunkData = pNewBuffer;
1000  ullChunkDataSize = ullNewChunkSize;
1001  }
1002  return pChunkData;
1003  }
1004 
1012  if (pChunkData) {
1013  delete[] pChunkData;
1014  pChunkData = NULL;
1015  }
1016  }
1017 
1037  if (NewSize == 0)
1038  throw Exception("There is at least one empty chunk (zero size): " + __resolveChunkPath(this));
1039  if ((NewSize >> 48) != 0)
1040  throw Exception("Unrealistic high chunk size detected: " + __resolveChunkPath(this));
1041  if (ullNewChunkSize == NewSize) return;
1042  ullNewChunkSize = NewSize;
1043  }
1044 
1059  file_offset_t Chunk::WriteChunk(file_offset_t ullWritePos, file_offset_t ullCurrentDataOffset, progress_t* pProgress) {
1060  const file_offset_t ullOriginalPos = ullWritePos;
1061  ullWritePos += CHUNK_HEADER_SIZE(pFile->FileOffsetSize);
1062 
1063  const File::HandlePair io = pFile->FileHandlePair();
1064 
1065  if (io.Mode != stream_mode_read_write)
1066  throw Exception("Cannot write list chunk, file has to be opened in read+write mode");
1067 
1068  // if the whole chunk body was loaded into RAM
1069  if (pChunkData) {
1070  // make sure chunk data buffer in RAM is at least as large as the new chunk size
1071  LoadChunkData();
1072  // write chunk data from RAM persistently to the file
1073  #if POSIX
1074  lseek(io.hWrite, ullWritePos, SEEK_SET);
1075  if (write(io.hWrite, pChunkData, ullNewChunkSize) != ullNewChunkSize) {
1076  throw Exception("Writing Chunk data (from RAM) failed");
1077  }
1078  #elif defined(WIN32)
1079  LARGE_INTEGER liFilePos;
1080  liFilePos.QuadPart = ullWritePos;
1081  SetFilePointerEx(io.hWrite, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN);
1082  DWORD dwBytesWritten;
1083  WriteFile(io.hWrite, pChunkData, ullNewChunkSize, &dwBytesWritten, NULL); //FIXME: won't save chunks larger than 2GB !
1084  if (dwBytesWritten != ullNewChunkSize) {
1085  throw Exception("Writing Chunk data (from RAM) failed");
1086  }
1087  #else
1088  fseeko(io.hWrite, ullWritePos, SEEK_SET);
1089  if (fwrite(pChunkData, 1, ullNewChunkSize, io.hWrite) != ullNewChunkSize) {
1090  throw Exception("Writing Chunk data (from RAM) failed");
1091  }
1092  #endif // POSIX
1093  } else {
1094  // move chunk data from the end of the file to the appropriate position
1095  int8_t* pCopyBuffer = new int8_t[4096];
1096  file_offset_t ullToMove = (ullNewChunkSize < ullCurrentChunkSize) ? ullNewChunkSize : ullCurrentChunkSize;
1097  #if defined(WIN32)
1098  DWORD iBytesMoved = 1; // we have to pass it via pointer to the Windows API, thus the correct size must be ensured
1099  #else
1100  int iBytesMoved = 1;
1101  #endif
1102  for (file_offset_t ullOffset = 0; ullToMove > 0 && iBytesMoved > 0; ullOffset += iBytesMoved, ullToMove -= iBytesMoved) {
1103  iBytesMoved = (ullToMove < 4096) ? int(ullToMove) : 4096;
1104  #if POSIX
1105  lseek(io.hRead, ullStartPos + ullCurrentDataOffset + ullOffset, SEEK_SET);
1106  iBytesMoved = (int) read(io.hRead, pCopyBuffer, (size_t) iBytesMoved);
1107  lseek(io.hWrite, ullWritePos + ullOffset, SEEK_SET);
1108  iBytesMoved = (int) write(io.hWrite, pCopyBuffer, (size_t) iBytesMoved);
1109  #elif defined(WIN32)
1110  LARGE_INTEGER liFilePos;
1111  liFilePos.QuadPart = ullStartPos + ullCurrentDataOffset + ullOffset;
1112  SetFilePointerEx(io.hRead, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN);
1113  ReadFile(io.hRead, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
1114  liFilePos.QuadPart = ullWritePos + ullOffset;
1115  SetFilePointerEx(io.hWrite, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN);
1116  WriteFile(io.hWrite, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
1117  #else
1118  fseeko(io.hRead, ullStartPos + ullCurrentDataOffset + ullOffset, SEEK_SET);
1119  iBytesMoved = fread(pCopyBuffer, 1, iBytesMoved, io.hRead);
1120  fseeko(io.hWrite, ullWritePos + ullOffset, SEEK_SET);
1121  iBytesMoved = fwrite(pCopyBuffer, 1, iBytesMoved, io.hWrite);
1122  #endif
1123  }
1124  delete[] pCopyBuffer;
1125  if (iBytesMoved < 0) throw Exception("Writing Chunk data (from file) failed");
1126  }
1127 
1128  // update this chunk's header
1129  ullCurrentChunkSize = ullNewChunkSize;
1130  WriteHeader(ullOriginalPos);
1131 
1132  // update chunk's position pointers
1133  ullStartPos = ullOriginalPos + CHUNK_HEADER_SIZE(pFile->FileOffsetSize);
1135 
1136  if (pProgress && pProgress->__private) {
1137  _FileProgress* fileProgress = (_FileProgress*) pProgress->__private;
1138  fileProgress->advance(pProgress, ullNewChunkSize +
1139  CHUNK_HEADER_SIZE(pFile->FileOffsetSize));
1140  }
1141 
1142  // add pad byte if needed
1143  if ((ullStartPos + ullNewChunkSize) % 2 != 0) {
1144  const char cPadByte = 0;
1145  #if POSIX
1146  lseek(io.hWrite, ullStartPos + ullNewChunkSize, SEEK_SET);
1147  write(io.hWrite, &cPadByte, 1);
1148  #elif defined(WIN32)
1149  LARGE_INTEGER liFilePos;
1150  liFilePos.QuadPart = ullStartPos + ullNewChunkSize;
1151  SetFilePointerEx(io.hWrite, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN);
1152  DWORD dwBytesWritten;
1153  WriteFile(io.hWrite, &cPadByte, 1, &dwBytesWritten, NULL);
1154  #else
1155  fseeko(io.hWrite, ullStartPos + ullNewChunkSize, SEEK_SET);
1156  fwrite(&cPadByte, 1, 1, io.hWrite);
1157  #endif
1158  return ullStartPos + ullNewChunkSize + 1;
1159  }
1160 
1161  return ullStartPos + ullNewChunkSize;
1162  }
1163 
1165  std::lock_guard<std::mutex> lock(chunkPos.mutex);
1166  chunkPos.ullPos = 0;
1167  chunkPos.byThread.clear();
1168  }
1169 
1170 
1171 
1172 // *************** List ***************
1173 // *
1174 
1175  List::List(File* pFile) : Chunk(pFile) {
1176  #if DEBUG_RIFF
1177  std::cout << "List::List(File* pFile)" << std::endl;
1178  #endif // DEBUG_RIFF
1179  pSubChunks = NULL;
1180  pSubChunksMap = NULL;
1181  }
1182 
1183  List::List(File* pFile, file_offset_t StartPos, List* Parent)
1184  : Chunk(pFile, StartPos, Parent) {
1185  #if DEBUG_RIFF
1186  std::cout << "List::List(File*,file_offset_t,List*)" << std::endl;
1187  #endif // DEBUG_RIFF
1188  pSubChunks = NULL;
1189  pSubChunksMap = NULL;
1190  ReadHeader(StartPos);
1191  ullStartPos = StartPos + LIST_HEADER_SIZE(pFile->FileOffsetSize);
1192  }
1193 
1194  List::List(File* pFile, List* pParent, uint32_t uiListID)
1195  : Chunk(pFile, pParent, CHUNK_ID_LIST, 0) {
1196  pSubChunks = NULL;
1197  pSubChunksMap = NULL;
1198  ListType = uiListID;
1199  }
1200 
1201  List::~List() {
1202  #if DEBUG_RIFF
1203  std::cout << "List::~List()" << std::endl;
1204  #endif // DEBUG_RIFF
1205  DeleteChunkList();
1206  }
1207 
1208  void List::DeleteChunkList() {
1209  if (pSubChunks) {
1210  ChunkList::iterator iter = pSubChunks->begin();
1211  ChunkList::iterator end = pSubChunks->end();
1212  while (iter != end) {
1213  delete *iter;
1214  iter++;
1215  }
1216  delete pSubChunks;
1217  pSubChunks = NULL;
1218  }
1219  if (pSubChunksMap) {
1220  delete pSubChunksMap;
1221  pSubChunksMap = NULL;
1222  }
1223  }
1224 
1235  if (!pSubChunks) LoadSubChunks();
1236  if (pos >= pSubChunks->size()) return NULL;
1237  return (*pSubChunks)[pos];
1238  }
1239 
1251  Chunk* List::GetSubChunk(uint32_t ChunkID) {
1252  #if DEBUG_RIFF
1253  std::cout << "List::GetSubChunk(uint32_t)" << std::endl;
1254  #endif // DEBUG_RIFF
1255  if (!pSubChunksMap) LoadSubChunks();
1256  return (*pSubChunksMap)[ChunkID];
1257  }
1258 
1269  List* List::GetSubListAt(size_t pos) {
1270  if (!pSubChunks) LoadSubChunks();
1271  if (pos >= pSubChunks->size()) return NULL;
1272  for (size_t iCk = 0, iLst = 0; iCk < pSubChunks->size(); ++iCk) {
1273  Chunk* pChunk = (*pSubChunks)[iCk];
1274  if (pChunk->GetChunkID() != CHUNK_ID_LIST) continue;
1275  if (iLst == pos) return (List*) pChunk;
1276  ++iLst;
1277  }
1278  return NULL;
1279  }
1280 
1292  List* List::GetSubList(uint32_t ListType) {
1293  #if DEBUG_RIFF
1294  std::cout << "List::GetSubList(uint32_t)" << std::endl;
1295  #endif // DEBUG_RIFF
1296  if (!pSubChunks) LoadSubChunks();
1297  ChunkList::iterator iter = pSubChunks->begin();
1298  ChunkList::iterator end = pSubChunks->end();
1299  while (iter != end) {
1300  if ((*iter)->GetChunkID() == CHUNK_ID_LIST) {
1301  List* l = (List*) *iter;
1302  if (l->GetListType() == ListType) return l;
1303  }
1304  iter++;
1305  }
1306  return NULL;
1307  }
1308 
1321  #if DEBUG_RIFF
1322  std::cout << "List::GetFirstSubChunk()" << std::endl;
1323  #endif // DEBUG_RIFF
1324  if (!pSubChunks) LoadSubChunks();
1325  ChunksIterator = pSubChunks->begin();
1326  return (ChunksIterator != pSubChunks->end()) ? *ChunksIterator : NULL;
1327  }
1328 
1340  #if DEBUG_RIFF
1341  std::cout << "List::GetNextSubChunk()" << std::endl;
1342  #endif // DEBUG_RIFF
1343  if (!pSubChunks) return NULL;
1344  ChunksIterator++;
1345  return (ChunksIterator != pSubChunks->end()) ? *ChunksIterator : NULL;
1346  }
1347 
1360  #if DEBUG_RIFF
1361  std::cout << "List::GetFirstSubList()" << std::endl;
1362  #endif // DEBUG_RIFF
1363  if (!pSubChunks) LoadSubChunks();
1364  ListIterator = pSubChunks->begin();
1365  ChunkList::iterator end = pSubChunks->end();
1366  while (ListIterator != end) {
1367  if ((*ListIterator)->GetChunkID() == CHUNK_ID_LIST) return (List*) *ListIterator;
1368  ListIterator++;
1369  }
1370  return NULL;
1371  }
1372 
1384  #if DEBUG_RIFF
1385  std::cout << "List::GetNextSubList()" << std::endl;
1386  #endif // DEBUG_RIFF
1387  if (!pSubChunks) return NULL;
1388  if (ListIterator == pSubChunks->end()) return NULL;
1389  ListIterator++;
1390  ChunkList::iterator end = pSubChunks->end();
1391  while (ListIterator != end) {
1392  if ((*ListIterator)->GetChunkID() == CHUNK_ID_LIST) return (List*) *ListIterator;
1393  ListIterator++;
1394  }
1395  return NULL;
1396  }
1397 
1402  if (!pSubChunks) LoadSubChunks();
1403  return pSubChunks->size();
1404  }
1405 
1410  size_t List::CountSubChunks(uint32_t ChunkID) {
1411  size_t result = 0;
1412  if (!pSubChunks) LoadSubChunks();
1413  ChunkList::iterator iter = pSubChunks->begin();
1414  ChunkList::iterator end = pSubChunks->end();
1415  while (iter != end) {
1416  if ((*iter)->GetChunkID() == ChunkID) {
1417  result++;
1418  }
1419  iter++;
1420  }
1421  return result;
1422  }
1423 
1428  return CountSubChunks(CHUNK_ID_LIST);
1429  }
1430 
1435  size_t List::CountSubLists(uint32_t ListType) {
1436  size_t result = 0;
1437  if (!pSubChunks) LoadSubChunks();
1438  ChunkList::iterator iter = pSubChunks->begin();
1439  ChunkList::iterator end = pSubChunks->end();
1440  while (iter != end) {
1441  if ((*iter)->GetChunkID() == CHUNK_ID_LIST) {
1442  List* l = (List*) *iter;
1443  if (l->GetListType() == ListType) result++;
1444  }
1445  iter++;
1446  }
1447  return result;
1448  }
1449 
1463  Chunk* List::AddSubChunk(uint32_t uiChunkID, file_offset_t ullBodySize) {
1464  if (ullBodySize == 0) throw Exception("Chunk body size must be at least 1 byte");
1465  if (!pSubChunks) LoadSubChunks();
1466 
1467  Chunk* pNewChunk = new Chunk(pFile, this, uiChunkID, 0);
1468  pSubChunks->push_back(pNewChunk);
1469  (*pSubChunksMap)[uiChunkID] = pNewChunk;
1470  pNewChunk->Resize(ullBodySize);
1471  ullNewChunkSize += CHUNK_HEADER_SIZE(pFile->FileOffsetSize);
1472 
1473  pFile->nDataChunkCount++;
1474 
1475  return pNewChunk;
1476  }
1477 
1489  void List::MoveSubChunk(Chunk* pSrc, Chunk* pDst) {
1490  if (!pSubChunks) LoadSubChunks();
1491  for (size_t i = 0; i < pSubChunks->size(); ++i) {
1492  if ((*pSubChunks)[i] == pSrc) {
1493  pSubChunks->erase(pSubChunks->begin() + i);
1494  ChunkList::iterator iter =
1495  find(pSubChunks->begin(), pSubChunks->end(), pDst);
1496  pSubChunks->insert(iter, pSrc);
1497  return;
1498  }
1499  }
1500  }
1501 
1510  void List::MoveSubChunk(Chunk* pSrc, List* pNewParent) {
1511  if (pNewParent == this || !pNewParent) return;
1512  if (!pSubChunks) LoadSubChunks();
1513  if (!pNewParent->pSubChunks) pNewParent->LoadSubChunks();
1514  ChunkList::iterator iter =
1515  find(pSubChunks->begin(), pSubChunks->end(), pSrc);
1516  if (iter == pSubChunks->end()) return;
1517  pSubChunks->erase(iter);
1518  pNewParent->pSubChunks->push_back(pSrc);
1519  // update chunk id map of this List
1520  if ((*pSubChunksMap)[pSrc->GetChunkID()] == pSrc) {
1521  pSubChunksMap->erase(pSrc->GetChunkID());
1522  // try to find another chunk of the same chunk ID
1523  ChunkList::iterator iter = pSubChunks->begin();
1524  ChunkList::iterator end = pSubChunks->end();
1525  for (; iter != end; ++iter) {
1526  if ((*iter)->GetChunkID() == pSrc->GetChunkID()) {
1527  (*pSubChunksMap)[pSrc->GetChunkID()] = *iter;
1528  break; // we're done, stop search
1529  }
1530  }
1531  }
1532  // update chunk id map of other list
1533  if (!(*pNewParent->pSubChunksMap)[pSrc->GetChunkID()])
1534  (*pNewParent->pSubChunksMap)[pSrc->GetChunkID()] = pSrc;
1535  }
1536 
1546  List* List::AddSubList(uint32_t uiListType) {
1547  if (!pSubChunks) LoadSubChunks();
1548 
1549  List* pNewListChunk = new List(pFile, this, uiListType);
1550  pSubChunks->push_back(pNewListChunk);
1551  (*pSubChunksMap)[CHUNK_ID_LIST] = pNewListChunk;
1552  ullNewChunkSize += LIST_HEADER_SIZE(pFile->FileOffsetSize);
1553 
1554  pFile->nListChunkCount++;
1555 
1556  return pNewListChunk;
1557  }
1558 
1569  void List::DeleteSubChunk(Chunk* pSubChunk) {
1570  if (!pSubChunks) LoadSubChunks();
1571  ChunkList::iterator iter =
1572  find(pSubChunks->begin(), pSubChunks->end(), pSubChunk);
1573  if (iter == pSubChunks->end()) return;
1574  pSubChunks->erase(iter);
1575 
1576  if (dynamic_cast<List*>(pSubChunk))
1577  pFile->nListChunkCount--;
1578  else
1579  pFile->nDataChunkCount--;
1580 
1581  if ((*pSubChunksMap)[pSubChunk->GetChunkID()] == pSubChunk) {
1582  pSubChunksMap->erase(pSubChunk->GetChunkID());
1583  // try to find another chunk of the same chunk ID
1584  ChunkList::iterator iter = pSubChunks->begin();
1585  ChunkList::iterator end = pSubChunks->end();
1586  for (; iter != end; ++iter) {
1587  if ((*iter)->GetChunkID() == pSubChunk->GetChunkID()) {
1588  (*pSubChunksMap)[pSubChunk->GetChunkID()] = *iter;
1589  break; // we're done, stop search
1590  }
1591  }
1592  }
1593  delete pSubChunk;
1594  }
1595 
1604  if (!pSubChunks) LoadSubChunks();
1605  file_offset_t size = LIST_HEADER_SIZE(fileOffsetSize);
1606  ChunkList::iterator iter = pSubChunks->begin();
1607  ChunkList::iterator end = pSubChunks->end();
1608  for (; iter != end; ++iter)
1609  size += (*iter)->RequiredPhysicalSize(fileOffsetSize);
1610  return size;
1611  }
1612 
1613  void List::ReadHeader(file_offset_t filePos) {
1614  #if DEBUG_RIFF
1615  std::cout << "List::Readheader(file_offset_t) ";
1616  #endif // DEBUG_RIFF
1617  Chunk::ReadHeader(filePos);
1618  if (ullCurrentChunkSize < 4) return;
1619  ullNewChunkSize = ullCurrentChunkSize -= 4;
1620 
1621  const File::Handle hRead = pFile->FileHandle();
1622 
1623  #if POSIX
1624  lseek(hRead, filePos + CHUNK_HEADER_SIZE(pFile->FileOffsetSize), SEEK_SET);
1625  read(hRead, &ListType, 4);
1626  #elif defined(WIN32)
1627  LARGE_INTEGER liFilePos;
1628  liFilePos.QuadPart = filePos + CHUNK_HEADER_SIZE(pFile->FileOffsetSize);
1629  SetFilePointerEx(hRead, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN);
1630  DWORD dwBytesRead;
1631  ReadFile(hRead, &ListType, 4, &dwBytesRead, NULL);
1632  #else
1633  fseeko(hRead, filePos + CHUNK_HEADER_SIZE(pFile->FileOffsetSize), SEEK_SET);
1634  fread(&ListType, 4, 1, hRead);
1635  #endif // POSIX
1636  #if DEBUG_RIFF
1637  std::cout << "listType=" << convertToString(ListType) << std::endl;
1638  #endif // DEBUG_RIFF
1639  if (!pFile->bEndianNative) {
1640  //swapBytes_32(&ListType);
1641  }
1642  }
1643 
1644  void List::WriteHeader(file_offset_t filePos) {
1645  // the four list type bytes officially belong the chunk's body in the RIFF format
1646  ullNewChunkSize += 4;
1647  Chunk::WriteHeader(filePos);
1648  ullNewChunkSize -= 4; // just revert the +4 incrementation
1649 
1650  const File::Handle hWrite = pFile->FileWriteHandle();
1651 
1652  #if POSIX
1653  lseek(hWrite, filePos + CHUNK_HEADER_SIZE(pFile->FileOffsetSize), SEEK_SET);
1654  write(hWrite, &ListType, 4);
1655  #elif defined(WIN32)
1656  LARGE_INTEGER liFilePos;
1657  liFilePos.QuadPart = filePos + CHUNK_HEADER_SIZE(pFile->FileOffsetSize);
1658  SetFilePointerEx(hWrite, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN);
1659  DWORD dwBytesWritten;
1660  WriteFile(hWrite, &ListType, 4, &dwBytesWritten, NULL);
1661  #else
1662  fseeko(hWrite, filePos + CHUNK_HEADER_SIZE(pFile->FileOffsetSize), SEEK_SET);
1663  fwrite(&ListType, 4, 1, hWrite);
1664  #endif // POSIX
1665  }
1666 
1667  void List::LoadSubChunks(progress_t* pProgress) {
1668  #if DEBUG_RIFF
1669  std::cout << "List::LoadSubChunks()";
1670  #endif // DEBUG_RIFF
1671  if (!pSubChunks) {
1672  pSubChunks = new ChunkList();
1673  pSubChunksMap = new ChunkMap();
1674 
1675  const File::Handle hRead = pFile->FileHandle();
1676  if (!_isValidHandle(hRead)) return;
1677 
1678  const file_offset_t ullOriginalPos = GetPos();
1679  SetPos(0); // jump to beginning of list chunk body
1680  while (RemainingBytes() >= CHUNK_HEADER_SIZE(pFile->FileOffsetSize)) {
1681  Chunk* ck;
1682  uint32_t ckid;
1683  // return value check is required here to prevent a potential
1684  // garbage data use of 'ckid' below in case Read() failed
1685  if (Read(&ckid, 4, 1) != 4)
1686  throw Exception("LoadSubChunks(): Failed reading RIFF chunk ID");
1687  #if DEBUG_RIFF
1688  std::cout << " ckid=" << convertToString(ckid) << std::endl;
1689  #endif // DEBUG_RIFF
1690  const file_offset_t pos = GetPos();
1691  if (ckid == CHUNK_ID_LIST) {
1692  ck = new RIFF::List(pFile, ullStartPos + pos - 4, this);
1693  SetPos(ck->GetSize() + LIST_HEADER_SIZE(pFile->FileOffsetSize) - 4, RIFF::stream_curpos);
1694 
1695  pFile->nListChunkCount++;
1696  }
1697  else { // simple chunk
1698  ck = new RIFF::Chunk(pFile, ullStartPos + pos - 4, this);
1699  SetPos(ck->GetSize() + CHUNK_HEADER_SIZE(pFile->FileOffsetSize) - 4, RIFF::stream_curpos);
1700 
1701  pFile->nDataChunkCount++;
1702  }
1703  pSubChunks->push_back(ck);
1704  (*pSubChunksMap)[ckid] = ck;
1705  if (GetPos() % 2 != 0) SetPos(1, RIFF::stream_curpos); // jump over pad byte
1706  }
1707  SetPos(ullOriginalPos); // restore position before this call
1708  }
1709  if (pProgress)
1710  __notify_progress(pProgress, 1.0); // notify done
1711  }
1712 
1713  void List::LoadSubChunksRecursively(progress_t* pProgress) {
1714  const bool alreadyLoaded = pSubChunks;
1715  const int n = (int) CountSubLists();
1716  int i = 0;
1717  for (List* pList = GetSubListAt(i); pList; pList = GetSubListAt(++i)) {
1718  if (pProgress) {
1719  // divide local progress into subprogress
1720  progress_t subprogress;
1721  __divide_progress(pProgress, &subprogress, n, i);
1722  // do the actual work
1723  pList->LoadSubChunksRecursively(&subprogress);
1724  } else
1725  pList->LoadSubChunksRecursively(NULL);
1726  }
1727  if (pProgress && !alreadyLoaded)
1728  __notify_progress(pProgress, 1.0); // notify done
1729  }
1730 
1746  file_offset_t List::WriteChunk(file_offset_t ullWritePos, file_offset_t ullCurrentDataOffset, progress_t* pProgress) {
1747  const file_offset_t ullOriginalPos = ullWritePos;
1748  ullWritePos += LIST_HEADER_SIZE(pFile->FileOffsetSize);
1749 
1750  if (pFile->GetMode() != stream_mode_read_write)
1751  throw Exception("Cannot write list chunk, file has to be opened in read+write mode");
1752 
1753  // write all subchunks (including sub list chunks) recursively
1754  if (pSubChunks) {
1755  for (ChunkList::iterator iter = pSubChunks->begin(), end = pSubChunks->end(); iter != end; ++iter) {
1756  ullWritePos = (*iter)->WriteChunk(ullWritePos, ullCurrentDataOffset, pProgress);
1757  }
1758  }
1759 
1760  // update this list chunk's header
1761  ullCurrentChunkSize = ullNewChunkSize = ullWritePos - ullOriginalPos - LIST_HEADER_SIZE(pFile->FileOffsetSize);
1762  WriteHeader(ullOriginalPos);
1763 
1764  // offset of this list chunk in new written file may have changed
1765  ullStartPos = ullOriginalPos + LIST_HEADER_SIZE(pFile->FileOffsetSize);
1766 
1767  if (pProgress && pProgress->__private) {
1768  _FileProgress* fileProgress = (_FileProgress*) pProgress->__private;
1769  fileProgress->advance(pProgress,
1770  LIST_HEADER_SIZE(pFile->FileOffsetSize));
1771  }
1772 
1773  return ullWritePos;
1774  }
1775 
1778  if (pSubChunks) {
1779  for (ChunkList::iterator iter = pSubChunks->begin(), end = pSubChunks->end(); iter != end; ++iter) {
1780  (*iter)->__resetPos();
1781  }
1782  }
1783  }
1784 
1788  String List::GetListTypeString() const {
1789  return convertToString(ListType);
1790  }
1791 
1792 
1793 
1794 // *************** File ***************
1795 // *
1796 
1811  File::File(uint32_t FileType)
1812  : List(this), bIsNewFile(true), Layout(layout_standard),
1813  FileOffsetPreference(offset_size_auto)
1814  {
1815  io.isPerThread = false;
1816  io.hRead = io.hWrite = RIFF_INVALID_FILE_HANDLE;
1817  io.Mode = stream_mode_closed;
1818  bEndianNative = true;
1819  ListType = FileType;
1820  FileOffsetSize = 4;
1821  nDataChunkCount = 0;
1822  nListChunkCount = 0;
1823  ullStartPos = RIFF_HEADER_SIZE(FileOffsetSize);
1824  }
1825 
1834  File::File(const String& path)
1835  : List(this), Filename(path), bIsNewFile(false), Layout(layout_standard),
1836  FileOffsetPreference(offset_size_auto)
1837  {
1838  #if DEBUG_RIFF
1839  std::cout << "File::File("<<path<<")" << std::endl;
1840  #endif // DEBUG_RIFF
1841  bEndianNative = true;
1842  FileOffsetSize = 4;
1843  nDataChunkCount = 0;
1844  nListChunkCount = 0;
1845  try {
1846  __openExistingFile(path);
1847  if (ChunkID != CHUNK_ID_RIFF && ChunkID != CHUNK_ID_RIFX) {
1848  throw RIFF::Exception("Not a RIFF file");
1849  }
1850  }
1851  catch (...) {
1852  Cleanup();
1853  throw;
1854  }
1855  }
1856 
1883  File::File(const String& path, uint32_t FileType, endian_t Endian, layout_t layout, offset_size_t fileOffsetSize)
1884  : List(this), Filename(path), bIsNewFile(false), Layout(layout),
1885  FileOffsetPreference(fileOffsetSize)
1886  {
1887  SetByteOrder(Endian);
1888  if (fileOffsetSize < offset_size_auto || fileOffsetSize > offset_size_64bit)
1889  throw Exception("Invalid RIFF::offset_size_t");
1890  FileOffsetSize = 4;
1891  nDataChunkCount = 0;
1892  nListChunkCount = 0;
1893  try {
1894  __openExistingFile(path, &FileType);
1895  }
1896  catch (...) {
1897  Cleanup();
1898  throw;
1899  }
1900  }
1901 
1912  void File::__openExistingFile(const String& path, uint32_t* FileType) {
1913  io.isPerThread = false;
1914  #if POSIX
1915  io.hRead = io.hWrite = open(path.c_str(), O_RDONLY | O_NONBLOCK);
1916  if (!_isValidHandle(io.hRead)) {
1917  String sError = strerror(errno);
1918  throw RIFF::Exception("Can't open \"" + path + "\": " + sError);
1919  }
1920  #elif defined(WIN32)
1921  io.hRead = io.hWrite = CreateFile(
1922  path.c_str(), GENERIC_READ,
1923  FILE_SHARE_READ | FILE_SHARE_WRITE,
1924  NULL, OPEN_EXISTING,
1925  FILE_ATTRIBUTE_NORMAL |
1926  FILE_FLAG_RANDOM_ACCESS, NULL
1927  );
1928  if (!_isValidHandle(io.hRead)) {
1929  throw RIFF::Exception("Can't open \"" + path + "\"");
1930  }
1931  #else
1932  io.hRead = io.hWrite = fopen(path.c_str(), "rb");
1933  if (!_isValidHandle(io.hRead))
1934  throw RIFF::Exception("Can't open \"" + path + "\"");
1935  #endif // POSIX
1936  io.Mode = stream_mode_read;
1937 
1938  // determine RIFF file offset size to be used (in RIFF chunk headers)
1939  // according to the current file offset preference
1940  FileOffsetSize = FileOffsetSizeFor(GetCurrentFileSize());
1941 
1942  switch (Layout) {
1943  case layout_standard: // this is a normal RIFF file
1944  ullStartPos = RIFF_HEADER_SIZE(FileOffsetSize);
1945  ReadHeader(0);
1946  if (FileType && ChunkID != *FileType)
1947  throw RIFF::Exception("Invalid file container ID");
1948  break;
1949  case layout_flat: // non-standard RIFF-alike file
1950  ullStartPos = 0;
1951  ullNewChunkSize = ullCurrentChunkSize = GetCurrentFileSize();
1952  if (FileType) {
1953  uint32_t ckid;
1954  if (Read(&ckid, 4, 1) != 4) {
1955  throw RIFF::Exception("Invalid file header ID (premature end of header)");
1956  } else if (ckid != *FileType) {
1957  String s = " (expected '" + convertToString(*FileType) + "' but got '" + convertToString(ckid) + "')";
1958  throw RIFF::Exception("Invalid file header ID" + s);
1959  }
1960  SetPos(0); // reset to first byte of file
1961  }
1962  LoadSubChunks();
1963  break;
1964  }
1965  }
1966 
1967  String File::GetFileName() const {
1968  return Filename;
1969  }
1970 
1971  void File::SetFileName(const String& path) {
1972  Filename = path;
1973  }
1974 
1983  File::HandlePair& File::FileHandlePairUnsafeRef() {
1984  if (io.byThread.empty()) return io;
1985  const std::thread::id tid = std::this_thread::get_id();
1986  const auto it = io.byThread.find(tid);
1987  return (it != io.byThread.end()) ?
1988  it->second :
1989  io.byThread[tid] = {
1990  // designated initializers require C++20, so commented for now
1991  /* .hRead = */ RIFF_INVALID_FILE_HANDLE,
1992  /* .hWrite = */ RIFF_INVALID_FILE_HANDLE,
1993  /* .Mode = */ stream_mode_closed
1994  };
1995  }
1996 
2003  File::HandlePair File::FileHandlePair() const {
2004  std::lock_guard<std::mutex> lock(io.mutex);
2005  if (io.byThread.empty()) return io;
2006  const std::thread::id tid = std::this_thread::get_id();
2007  const auto it = io.byThread.find(tid);
2008  return (it != io.byThread.end()) ?
2009  it->second :
2010  io.byThread[tid] = {
2011  // designated initializers require C++20, so commented for now
2012  /* .hRead = */ RIFF_INVALID_FILE_HANDLE,
2013  /* .hWrite = */ RIFF_INVALID_FILE_HANDLE,
2014  /* .Mode = */ stream_mode_closed
2015  };
2016  }
2017 
2025  return FileHandlePair().hRead;
2026  }
2027 
2035  return FileHandlePair().hWrite;
2036  }
2037 
2045  return FileHandlePair().Mode;
2046  }
2047 
2048  layout_t File::GetLayout() const {
2049  return Layout;
2050  }
2051 
2065  bool bResetPos = false;
2066  bool res = SetModeInternal(NewMode, &bResetPos);
2067  // resetting position must be handled outside above's call to avoid any
2068  // potential dead lock, as SetModeInternal() acquires a lock and
2069  // __resetPos() acquires a lock by itself (not a theoretical issue!)
2070  if (bResetPos)
2071  __resetPos(); // reset read/write position of ALL 'Chunk' objects
2072  return res;
2073  }
2074 
2075  bool File::SetModeInternal(stream_mode_t NewMode, bool* pResetPos) {
2076  std::lock_guard<std::mutex> lock(io.mutex);
2077  HandlePair& io = FileHandlePairUnsafeRef();
2078  if (NewMode != io.Mode) {
2079  switch (NewMode) {
2080  case stream_mode_read:
2081  if (_isValidHandle(io.hRead)) _close(io.hRead);
2082  #if POSIX
2083  io.hRead = io.hWrite = open(Filename.c_str(), O_RDONLY | O_NONBLOCK);
2084  if (!_isValidHandle(io.hRead)) {
2085  String sError = strerror(errno);
2086  throw Exception("Could not (re)open file \"" + Filename + "\" in read mode: " + sError);
2087  }
2088  #elif defined(WIN32)
2089  io.hRead = io.hWrite = CreateFile(
2090  Filename.c_str(), GENERIC_READ,
2091  FILE_SHARE_READ | FILE_SHARE_WRITE,
2092  NULL, OPEN_EXISTING,
2093  FILE_ATTRIBUTE_NORMAL |
2094  FILE_FLAG_RANDOM_ACCESS,
2095  NULL
2096  );
2097  if (!_isValidHandle(io.hRead)) {
2098  throw Exception("Could not (re)open file \"" + Filename + "\" in read mode");
2099  }
2100  #else
2101  io.hRead = io.hWrite = fopen(Filename.c_str(), "rb");
2102  if (!_isValidHandle(io.hRead))
2103  throw Exception("Could not (re)open file \"" + Filename + "\" in read mode");
2104  #endif
2105  *pResetPos = true;
2106  break;
2107  case stream_mode_read_write:
2108  if (_isValidHandle(io.hRead)) _close(io.hRead);
2109  #if POSIX
2110  io.hRead = io.hWrite = open(Filename.c_str(), O_RDWR | O_NONBLOCK);
2111  if (!_isValidHandle(io.hRead)) {
2112  io.hRead = io.hWrite = open(Filename.c_str(), O_RDONLY | O_NONBLOCK);
2113  String sError = strerror(errno);
2114  throw Exception("Could not open file \"" + Filename + "\" in read+write mode: " + sError);
2115  }
2116  #elif defined(WIN32)
2117  io.hRead = io.hWrite = CreateFile(
2118  Filename.c_str(),
2119  GENERIC_READ | GENERIC_WRITE,
2120  FILE_SHARE_READ,
2121  NULL, OPEN_ALWAYS,
2122  FILE_ATTRIBUTE_NORMAL |
2123  FILE_FLAG_RANDOM_ACCESS,
2124  NULL
2125  );
2126  if (!_isValidHandle(io.hRead)) {
2127  io.hRead = io.hWrite = CreateFile(
2128  Filename.c_str(), GENERIC_READ,
2129  FILE_SHARE_READ | FILE_SHARE_WRITE,
2130  NULL, OPEN_EXISTING,
2131  FILE_ATTRIBUTE_NORMAL |
2132  FILE_FLAG_RANDOM_ACCESS,
2133  NULL
2134  );
2135  throw Exception("Could not (re)open file \"" + Filename + "\" in read+write mode");
2136  }
2137  #else
2138  io.hRead = io.hWrite = fopen(Filename.c_str(), "r+b");
2139  if (!_isValidHandle(io.hRead)) {
2140  io.hRead = io.hWrite = fopen(Filename.c_str(), "rb");
2141  throw Exception("Could not open file \"" + Filename + "\" in read+write mode");
2142  }
2143  #endif
2144  *pResetPos = true;
2145  break;
2146  case stream_mode_closed:
2147  if (_isValidHandle(io.hRead)) _close(io.hRead);
2148  if (_isValidHandle(io.hWrite)) _close(io.hWrite);
2149  io.hRead = io.hWrite = RIFF_INVALID_FILE_HANDLE;
2150  break;
2151  default:
2152  throw Exception("Unknown file access mode");
2153  }
2154  io.Mode = NewMode;
2155  return true;
2156  }
2157  return false;
2158  }
2159 
2170  #if WORDS_BIGENDIAN
2171  bEndianNative = Endian != endian_little;
2172  #else
2173  bEndianNative = Endian != endian_big;
2174  #endif
2175  }
2176 
2187  void File::Save(progress_t* pProgress) {
2188  //TODO: implementation for the case where first chunk is not a global container (List chunk) is not implemented yet (i.e. Korg files)
2189  if (Layout == layout_flat)
2190  throw Exception("Saving a RIFF file with layout_flat is not implemented yet");
2191 
2192  const bool bNeedLoadSubChunks = !pSubChunks;
2193 
2194  // make sure the RIFF tree is built (from the original file)
2195  if (pProgress) {
2196  // divide progress into subprogress
2197  progress_t subprogress;
2198  __divide_progress(pProgress, &subprogress, 1.f, 0.f, 0.2f); // assume 20% of time
2199  // do the actual work
2200  LoadSubChunksRecursively(&subprogress);
2201  // notify subprogress done
2202  __notify_progress(&subprogress, 1.f);
2203  } else
2204  LoadSubChunksRecursively(NULL);
2205 
2206  // reopen file in write mode
2207  SetMode(stream_mode_read_write);
2208 
2209  // get the current file size as it is now still physically stored on disk
2210  const file_offset_t workingFileSize = GetCurrentFileSize();
2211 
2212  // get the overall file size required to save this file
2213  const file_offset_t newFileSize = GetRequiredFileSize(FileOffsetPreference);
2214 
2215  // determine whether this file will yield in a large file (>=4GB) and
2216  // the RIFF file offset size to be used accordingly for all chunks
2217  FileOffsetSize = FileOffsetSizeFor(newFileSize);
2218 
2219  const HandlePair io = FileHandlePair();
2220  const Handle hRead = io.hRead;
2221  const Handle hWrite = io.hWrite;
2222 
2223  // to be able to save the whole file without loading everything into
2224  // RAM and without having to store the data in a temporary file, we
2225  // enlarge the file with the overall positive file size change,
2226  // then move current data towards the end of the file by the calculated
2227  // positive file size difference and finally update / rewrite the file
2228  // by copying the old data back to the right position at the beginning
2229  // of the file
2230 
2231  // if there are positive size changes...
2232  file_offset_t positiveSizeDiff = 0;
2233  if (newFileSize > workingFileSize) {
2234  positiveSizeDiff = newFileSize - workingFileSize;
2235 
2236  // divide progress into subprogress
2237  progress_t subprogress;
2238  if (pProgress)
2239  __divide_progress(pProgress, &subprogress, 1.f,
2240  bNeedLoadSubChunks ? 0.2f : 0.f,
2241  bNeedLoadSubChunks ? 0.7f : 0.6f);
2242 
2243  // ... we enlarge this file first ...
2244  ResizeFile(newFileSize);
2245 
2246  // ... and move current data by the same amount towards end of file.
2247  int8_t* pCopyBuffer = new int8_t[4096];
2248  #if defined(WIN32)
2249  DWORD iBytesMoved = 1; // we have to pass it via pointer to the Windows API, thus the correct size must be ensured
2250  #else
2251  ssize_t iBytesMoved = 1;
2252  #endif
2253  for (file_offset_t ullPos = workingFileSize, iNotif = 0; iBytesMoved > 0; ++iNotif) {
2254  iBytesMoved = (ullPos < 4096) ? ullPos : 4096;
2255  ullPos -= iBytesMoved;
2256  #if POSIX
2257  lseek(hRead, ullPos, SEEK_SET);
2258  iBytesMoved = read(hRead, pCopyBuffer, iBytesMoved);
2259  lseek(hWrite, ullPos + positiveSizeDiff, SEEK_SET);
2260  iBytesMoved = write(hWrite, pCopyBuffer, iBytesMoved);
2261  #elif defined(WIN32)
2262  LARGE_INTEGER liFilePos;
2263  liFilePos.QuadPart = ullPos;
2264  SetFilePointerEx(hRead, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN);
2265  ReadFile(hRead, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
2266  liFilePos.QuadPart = ullPos + positiveSizeDiff;
2267  SetFilePointerEx(hWrite, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN);
2268  WriteFile(hWrite, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
2269  #else
2270  fseeko(hRead, ullPos, SEEK_SET);
2271  iBytesMoved = fread(pCopyBuffer, 1, iBytesMoved, hRead);
2272  fseeko(hWrite, ullPos + positiveSizeDiff, SEEK_SET);
2273  iBytesMoved = fwrite(pCopyBuffer, 1, iBytesMoved, hWrite);
2274  #endif
2275  if (pProgress && !(iNotif % 8) && iBytesMoved > 0)
2276  __notify_progress(&subprogress, float(workingFileSize - ullPos) / float(workingFileSize));
2277  }
2278  delete[] pCopyBuffer;
2279  if (iBytesMoved < 0) throw Exception("Could not modify file while trying to enlarge it");
2280 
2281  if (pProgress)
2282  __notify_progress(&subprogress, 1.f); // notify subprogress done
2283  }
2284 
2285  // rebuild / rewrite complete RIFF tree ...
2286 
2287  // divide progress into subprogress
2288  progress_t subprogress;
2289  _FileProgress fileProgress = { 0, newFileSize };
2290  if (pProgress) {
2291  __divide_progress(pProgress, &subprogress, 1.f,
2292  bNeedLoadSubChunks ? 0.7f : 0.6f, 1.f);
2293  subprogress.__private = &fileProgress;
2294  }
2295  // do the actual work
2296  const file_offset_t finalSize = WriteChunk(0, positiveSizeDiff, pProgress ? &subprogress : NULL);
2297  const file_offset_t finalActualSize = __GetFileSize(hWrite);
2298  // notify subprogress done
2299  if (pProgress)
2300  __notify_progress(&subprogress, 1.f);
2301 
2302  // resize file to the final size
2303  if (finalSize < finalActualSize) ResizeFile(finalSize);
2304 
2305  if (pProgress)
2306  __notify_progress(pProgress, 1.0); // notify done
2307  }
2308 
2324  void File::Save(const String& path, progress_t* pProgress) {
2325  //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
2326 
2327  //TODO: implementation for the case where first chunk is not a global container (List chunk) is not implemented yet (i.e. Korg files)
2328  if (Layout == layout_flat)
2329  throw Exception("Saving a RIFF file with layout_flat is not implemented yet");
2330 
2331  const bool bNeedLoadSubChunks = !pSubChunks;
2332 
2333  // make sure the RIFF tree is built (from the original file)
2334  if (pProgress) {
2335  // divide progress into subprogress
2336  progress_t subprogress;
2337  __divide_progress(pProgress, &subprogress, 1.f, 0.f, 0.3f); // assume 30% of time
2338  // do the actual work
2339  LoadSubChunksRecursively(&subprogress);
2340  // notify subprogress done
2341  if (bNeedLoadSubChunks)
2342  __notify_progress(&subprogress, 1.f);
2343  } else
2344  LoadSubChunksRecursively(NULL);
2345 
2346  if (!bIsNewFile) SetMode(stream_mode_read);
2347 
2348  {
2349  std::lock_guard<std::mutex> lock(io.mutex);
2350  HandlePair& io = FileHandlePairUnsafeRef();
2351 
2352  // open the other (new) file for writing and truncate it to zero size
2353  #if POSIX
2354  io.hWrite = open(path.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP);
2355  if (!_isValidHandle(io.hWrite)) {
2356  io.hWrite = io.hRead;
2357  String sError = strerror(errno);
2358  throw Exception("Could not open file \"" + path + "\" for writing: " + sError);
2359  }
2360  #elif defined(WIN32)
2361  io.hWrite = CreateFile(
2362  path.c_str(), GENERIC_WRITE, FILE_SHARE_READ,
2363  NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL |
2364  FILE_FLAG_RANDOM_ACCESS, NULL
2365  );
2366  if (!_isValidHandle(io.hWrite)) {
2367  io.hWrite = io.hRead;
2368  throw Exception("Could not open file \"" + path + "\" for writing");
2369  }
2370  #else
2371  io.hWrite = fopen(path.c_str(), "w+b");
2372  if (!_isValidHandle(io.hWrite)) {
2373  io.hWrite = io.hRead;
2374  throw Exception("Could not open file \"" + path + "\" for writing");
2375  }
2376  #endif // POSIX
2377  io.Mode = stream_mode_read_write;
2378  }
2379 
2380  // get the overall file size required to save this file
2381  const file_offset_t newFileSize = GetRequiredFileSize(FileOffsetPreference);
2382 
2383  // determine whether this file will yield in a large file (>=4GB) and
2384  // the RIFF file offset size to be used accordingly for all chunks
2385  FileOffsetSize = FileOffsetSizeFor(newFileSize);
2386 
2387  // write complete RIFF tree to the other (new) file
2388  file_offset_t ullTotalSize;
2389  if (pProgress) {
2390  // divide progress into subprogress
2391  progress_t subprogress;
2392  __divide_progress(pProgress, &subprogress, 1.f,
2393  bNeedLoadSubChunks ? 0.3f : 0.f, 1.f);
2394  _FileProgress fileProgress = { 0, newFileSize };
2395  subprogress.__private = &fileProgress;
2396  // do the actual work
2397  ullTotalSize = WriteChunk(0, 0, &subprogress);
2398  // notify subprogress done
2399  __notify_progress(&subprogress, 1.f);
2400  } else
2401  ullTotalSize = WriteChunk(0, 0, NULL);
2402 
2403  const file_offset_t ullActualSize = __GetFileSize(FileWriteHandle());
2404 
2405  // resize file to the final size (if the file was originally larger)
2406  if (ullActualSize > ullTotalSize) ResizeFile(ullTotalSize);
2407 
2408  {
2409  std::lock_guard<std::mutex> lock(io.mutex);
2410  HandlePair& io = FileHandlePairUnsafeRef();
2411 
2412  if (_isValidHandle(io.hWrite)) _close(io.hWrite);
2413  io.hWrite = io.hRead;
2414 
2415  // associate new file with this File object from now on
2416  Filename = path;
2417  bIsNewFile = false;
2418  io.Mode = (stream_mode_t) -1; // Just set it to an undefined mode ...
2419  }
2420  SetMode(stream_mode_read_write); // ... so SetMode() has to reopen the file handles.
2421 
2422  if (pProgress)
2423  __notify_progress(pProgress, 1.0); // notify done
2424  }
2425 
2426  void File::ResizeFile(file_offset_t ullNewSize) {
2427  const Handle hWrite = FileWriteHandle();
2428  #if POSIX
2429  if (ftruncate(hWrite, ullNewSize) < 0)
2430  throw Exception("Could not resize file \"" + Filename + "\"");
2431  #elif defined(WIN32)
2432  LARGE_INTEGER liFilePos;
2433  liFilePos.QuadPart = ullNewSize;
2434  if (
2435  !SetFilePointerEx(hWrite, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN) ||
2436  !SetEndOfFile(hWrite)
2437  ) throw Exception("Could not resize file \"" + Filename + "\"");
2438  #else
2439  # error Sorry, this version of libgig only supports POSIX and Windows systems yet.
2440  # error Reason: portable implementation of RIFF::File::ResizeFile() is missing (yet)!
2441  #endif
2442  }
2443 
2444  File::~File() {
2445  #if DEBUG_RIFF
2446  std::cout << "File::~File()" << std::endl;
2447  #endif // DEBUG_RIFF
2448  Cleanup();
2449  }
2450 
2455  bool File::IsNew() const {
2456  return bIsNewFile;
2457  }
2458 
2459  void File::Cleanup() {
2460  if (IsIOPerThread()) {
2461  for (auto it = io.byThread.begin(); it != io.byThread.end(); ++it) {
2462  _close(it->second.hRead);
2463  }
2464  } else {
2465  _close(io.hRead);
2466  }
2467  DeleteChunkList();
2468  pFile = NULL;
2469  }
2470 
2478  file_offset_t size = 0;
2479  const Handle hRead = FileHandle();
2480  try {
2481  size = __GetFileSize(hRead);
2482  } catch (...) {
2483  size = 0;
2484  }
2485  return size;
2486  }
2487 
2508  return GetRequiredFileSize(FileOffsetPreference);
2509  }
2510 
2523  switch (fileOffsetSize) {
2524  case offset_size_auto: {
2526  if (fileSize >> 32)
2528  else
2529  return fileSize;
2530  }
2531  case offset_size_32bit: break;
2532  case offset_size_64bit: break;
2533  default: throw Exception("Internal error: Invalid RIFF::offset_size_t");
2534  }
2536  }
2537 
2538  int File::FileOffsetSizeFor(file_offset_t fileSize) const {
2539  switch (FileOffsetPreference) {
2540  case offset_size_auto:
2541  return (fileSize >> 32) ? 8 : 4;
2542  case offset_size_32bit:
2543  return 4;
2544  case offset_size_64bit:
2545  return 8;
2546  default:
2547  throw Exception("Internal error: Invalid RIFF::offset_size_t");
2548  }
2549  }
2550 
2572  return FileOffsetSize;
2573  }
2574 
2586  return FileOffsetSizeFor(GetCurrentFileSize());
2587  }
2588 
2615  bool File::IsIOPerThread() const {
2616  //NOTE: Not caring about atomicity here at all, for three reasons:
2617  // 1. SetIOPerThread() is assumed to be called only once for the entire
2618  // life time of a RIFF::File, usually very early at its lifetime, and
2619  // hence a change to isPerThread should already safely be propagated
2620  // before any other thread would actually read this boolean flag.
2621  // 2. This method is called very frequently, and therefore any
2622  // synchronization techique would hurt runtime efficiency.
2623  // 3. Using even a mutex lock here might easily cause a deadlock due to
2624  // other locks been taken in this .cpp file, i.e. at a higher call
2625  // stack level (and this is the main reason why I removed it here).
2626  return io.isPerThread;
2627  }
2628 
2644  void File::SetIOPerThread(bool enable) {
2645  std::lock_guard<std::mutex> lock(io.mutex);
2646  if (!io.byThread.empty() == enable) return;
2647  io.isPerThread = enable;
2648  if (enable) {
2649  const std::thread::id tid = std::this_thread::get_id();
2650  io.byThread[tid] = io;
2651  } else {
2652  // retain an arbitrary handle pair, close all other handle pairs
2653  for (auto it = io.byThread.begin(); it != io.byThread.end(); ++it) {
2654  if (it == io.byThread.begin()) {
2655  io.hRead = it->second.hRead;
2656  io.hWrite = it->second.hWrite;
2657  } else {
2658  _close(it->second.hRead);
2659  _close(it->second.hWrite);
2660  }
2661  }
2662  io.byThread.clear();
2663  }
2664  }
2665 
2674  size_t File::totalDataChunkCount() const {
2675  return nDataChunkCount;
2676  }
2677 
2686  size_t File::totalListChunkCount() const {
2687  return nListChunkCount;
2688  }
2689 
2702  size_t File::totalChunkCount() const {
2703  return nDataChunkCount + nListChunkCount;
2704  }
2705 
2706  #if POSIX
2707  file_offset_t File::__GetFileSize(int hFile) const {
2708  struct stat filestat;
2709  if (fstat(hFile, &filestat) == -1)
2710  throw Exception("POSIX FS error: could not determine file size");
2711  return filestat.st_size;
2712  }
2713  #elif defined(WIN32)
2714  file_offset_t File::__GetFileSize(HANDLE hFile) const {
2715  LARGE_INTEGER size;
2716  if (!GetFileSizeEx(hFile, &size))
2717  throw Exception("Windows FS error: could not determine file size");
2718  return size.QuadPart;
2719  }
2720  #else // standard C functions
2721  file_offset_t File::__GetFileSize(FILE* hFile) const {
2722  off_t curpos = ftello(hFile);
2723  if (fseeko(hFile, 0, SEEK_END) == -1)
2724  throw Exception("FS error: could not determine file size");
2725  off_t size = ftello(hFile);
2726  fseeko(hFile, curpos, SEEK_SET);
2727  return size;
2728  }
2729  #endif
2730 
2731 
2732 // *************** Exception ***************
2733 // *
2734 
2735  Exception::Exception() {
2736  }
2737 
2738  Exception::Exception(String format, ...) {
2739  va_list arg;
2740  va_start(arg, format);
2741  Message = assemble(format, arg);
2742  va_end(arg);
2743  }
2744 
2745  Exception::Exception(String format, va_list arg) {
2746  Message = assemble(format, arg);
2747  }
2748 
2749  Exception::~Exception() {
2750  }
2751 
2752  void Exception::PrintMessage() const {
2753  std::cout << "RIFF::Exception: " << Message << std::endl;
2754  }
2755 
2756  String Exception::assemble(String format, va_list arg) {
2757  char* buf = NULL;
2758  vasprintf(&buf, format.c_str(), arg);
2759  String s = buf;
2760  free(buf);
2761  return s;
2762  }
2763 
2764 
2765 // *************** functions ***************
2766 // *
2767 
2773  String libraryName() {
2774  return PACKAGE;
2775  }
2776 
2781  String libraryVersion() {
2782  return VERSION;
2783  }
2784 
2785 } // namespace RIFF
Ordinary RIFF Chunk.
Definition: RIFF.h:186
file_offset_t WriteInt16(int16_t *pData, file_offset_t WordCount=1)
Writes WordCount number of 16 Bit signed integer words from the buffer pointed by pData to the chunk'...
Definition: RIFF.cpp:695
virtual void __resetPos()
Sets Chunk's read/write position to zero.
Definition: RIFF.cpp:1164
void Resize(file_offset_t NewSize)
Resize chunk.
Definition: RIFF.cpp:1036
int8_t ReadInt8()
Reads one 8 Bit signed integer word and increments the position within the chunk.
Definition: RIFF.cpp:842
uint32_t ReadUint32()
Reads one 32 Bit unsigned integer word and increments the position within the chunk.
Definition: RIFF.cpp:931
stream_state_t GetState() const
Returns the current state of the chunk object.
Definition: RIFF.cpp:444
file_offset_t SetPos(file_offset_t 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:375
file_offset_t GetFilePos() const
Current, actual offset in file of current chunk data body read/write position.
Definition: RIFF.cpp:358
void ReleaseChunkData()
Free loaded chunk body from RAM.
Definition: RIFF.cpp:1011
file_offset_t Write(void *pData, file_offset_t WordCount, file_offset_t WordSize)
Writes WordCount number of data words with given WordSize from the buffer pointed by pData.
Definition: RIFF.cpp:533
file_offset_t RemainingBytes() const
Returns the number of bytes left to read in the chunk body.
Definition: RIFF.cpp:410
int16_t ReadInt16()
Reads one 16 Bit signed integer word and increments the position within the chunk.
Definition: RIFF.cpp:877
virtual file_offset_t WriteChunk(file_offset_t ullWritePos, file_offset_t ullCurrentDataOffset, progress_t *pProgress=NULL)
Write chunk persistently e.g.
Definition: RIFF.cpp:1059
file_offset_t GetPos() const
Current read/write position within the chunk data body (starting with 0).
Definition: RIFF.cpp:345
file_offset_t WriteInt32(int32_t *pData, file_offset_t WordCount=1)
Writes WordCount number of 32 Bit signed integer words from the buffer pointed by pData to the chunk'...
Definition: RIFF.cpp:773
file_offset_t ReadSceptical(void *pData, file_offset_t WordCount, file_offset_t WordSize)
Just an internal wrapper for the main Read() method with additional Exception throwing on errors.
Definition: RIFF.cpp:577
virtual file_offset_t RequiredPhysicalSize(int fileOffsetSize)
Returns the actual total size in bytes (including header) of this Chunk if being stored to a file.
Definition: RIFF.cpp:425
file_offset_t Read(void *pData, file_offset_t WordCount, file_offset_t WordSize)
Reads WordCount number of data words with given WordSize and copies it into a buffer pointed by pData...
Definition: RIFF.cpp:475
uint32_t GetChunkID() const
Chunk ID in unsigned integer representation.
Definition: RIFF.h:190
void * LoadChunkData()
Load chunk body into RAM.
Definition: RIFF.cpp:962
String GetChunkIDString() const
Returns the String representation of the chunk's ID (e.g.
Definition: RIFF.cpp:322
uint8_t ReadUint8()
Reads one 8 Bit unsigned integer word and increments the position within the chunk.
Definition: RIFF.cpp:859
file_offset_t WriteInt8(int8_t *pData, file_offset_t WordCount=1)
Writes WordCount number of 8 Bit signed integer words from the buffer pointed by pData to the chunk's...
Definition: RIFF.cpp:617
file_offset_t WriteUint16(uint16_t *pData, file_offset_t WordCount=1)
Writes WordCount number of 16 Bit unsigned integer words from the buffer pointed by pData to the chun...
Definition: RIFF.cpp:734
file_offset_t WriteUint32(uint32_t *pData, file_offset_t WordCount=1)
Writes WordCount number of 32 Bit unsigned integer words from the buffer pointed by pData to the chun...
Definition: RIFF.cpp:830
void ReadString(String &s, int size)
Reads a null-padded string of size characters and copies it into the string s.
Definition: RIFF.cpp:808
uint16_t ReadUint16()
Reads one 16 Bit unsigned integer word and increments the position within the chunk.
Definition: RIFF.cpp:895
file_offset_t GetSize() const
Chunk size in bytes (without header, thus the chunk data body)
Definition: RIFF.h:193
int32_t ReadInt32()
Reads one 32 Bit signed integer word and increments the position within the chunk.
Definition: RIFF.cpp:913
file_offset_t WriteUint8(uint8_t *pData, file_offset_t WordCount=1)
Writes WordCount number of 8 Bit unsigned integer words from the buffer pointed by pData to the chunk...
Definition: RIFF.cpp:656
Will be thrown whenever an error occurs while handling a RIFF file.
Definition: RIFF.h:397
RIFF File.
Definition: RIFF.h:314
Handle FileWriteHandle() const
Returns the OS dependent file I/O write handle intended to be used by the calling thread.
Definition: RIFF.cpp:2034
HandlePair FileHandlePair() const
Returns the OS dependent file I/O read and write handles intended to be used by the calling thread.
Definition: RIFF.cpp:2003
bool SetMode(stream_mode_t NewMode)
Change file access mode.
Definition: RIFF.cpp:2064
int GetRequiredFileOffsetSize()
Returns the required size (in bytes) of file offsets stored in the headers of all chunks of this file...
Definition: RIFF.cpp:2585
int GetFileOffsetSize() const
Returns the current size (in bytes) of file offsets stored in the headers of all chunks of this file.
Definition: RIFF.cpp:2571
File(uint32_t FileType)
Create new RIFF file.
Definition: RIFF.cpp:1811
file_offset_t GetCurrentFileSize() const
Returns the current size of this file (in bytes) as it is currently yet stored on disk.
Definition: RIFF.cpp:2477
void SetByteOrder(endian_t Endian)
Set the byte order to be used when saving.
Definition: RIFF.cpp:2169
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:2455
file_offset_t GetRequiredFileSize()
Returns the required size (in bytes) for this RIFF File to be saved to disk.
Definition: RIFF.cpp:2507
size_t totalDataChunkCount() const
File's total amount of ordinary (data) chunks.
Definition: RIFF.cpp:2674
Handle FileHandle() const
Returns the OS dependent file I/O read handle intended to be used by the calling thread.
Definition: RIFF.cpp:2024
bool IsIOPerThread() const
Whether file streams are independent for each thread.
Definition: RIFF.cpp:2615
size_t totalChunkCount() const
File's total amount of data chunks and list chunks.
Definition: RIFF.cpp:2702
stream_mode_t GetMode() const
Returns the file I/O mode currently being available for the calling thread for this RIFF file (either...
Definition: RIFF.cpp:2044
size_t totalListChunkCount() const
File's total amount of list chunks.
Definition: RIFF.cpp:2686
virtual void Save(progress_t *pProgress=NULL)
Save changes to same file.
Definition: RIFF.cpp:2187
layout_t Layout
An ordinary RIFF file is always set to layout_standard.
Definition: RIFF.h:367
int Handle
OS dependent type serving as file handle / descriptor for OS dependent file I/O operations.
Definition: RIFF.h:321
int FileOffsetSize
Size of file offsets (in bytes) when this file was opened (or saved the last time).
Definition: RIFF.h:369
void SetIOPerThread(bool enable)
Enable/disable file streams being independent for each thread.
Definition: RIFF.cpp:2644
RIFF List Chunk.
Definition: RIFF.h:262
size_t CountSubLists()
Returns number of sublists within the list.
Definition: RIFF.cpp:1427
Chunk * GetSubChunk(uint32_t ChunkID)
Returns subchunk with chunk ID ChunkID within this chunk list.
Definition: RIFF.cpp:1251
virtual file_offset_t RequiredPhysicalSize(int fileOffsetSize)
Returns the actual total size in bytes (including List chunk header and all subchunks) of this List C...
Definition: RIFF.cpp:1603
void MoveSubChunk(Chunk *pSrc, Chunk *pDst)
Moves a sub chunk witin this list.
Definition: RIFF.cpp:1489
List * GetFirstSubList()
Returns the first sublist within the list (that is a subchunk with chunk ID "LIST").
Definition: RIFF.cpp:1359
List * AddSubList(uint32_t uiListType)
Creates a new list sub chunk.
Definition: RIFF.cpp:1546
Chunk * GetFirstSubChunk()
Returns the first subchunk within the list (which may be an ordinary chunk as well as a list chunk).
Definition: RIFF.cpp:1320
virtual file_offset_t WriteChunk(file_offset_t ullWritePos, file_offset_t ullCurrentDataOffset, progress_t *pProgress=NULL)
Write list chunk persistently e.g.
Definition: RIFF.cpp:1746
void DeleteSubChunk(Chunk *pSubChunk)
Removes a sub chunk.
Definition: RIFF.cpp:1569
List * GetSubListAt(size_t pos)
Returns sublist chunk with list type ListType at supplied pos position among all subchunks of type Li...
Definition: RIFF.cpp:1269
size_t CountSubChunks()
Returns number of subchunks within the list (including list chunks).
Definition: RIFF.cpp:1401
List * GetSubList(uint32_t ListType)
Returns sublist chunk with list type ListType within this chunk list.
Definition: RIFF.cpp:1292
Chunk * GetNextSubChunk()
Returns the next subchunk within the list (which may be an ordinary chunk as well as a list chunk).
Definition: RIFF.cpp:1339
uint32_t GetListType() const
Returns unsigned integer representation of the list's ID.
Definition: RIFF.h:266
String GetListTypeString() const
Returns string representation of the lists's id.
Definition: RIFF.cpp:1788
List * GetNextSubList()
Returns the next sublist (that is a subchunk with chunk ID "LIST") within the list.
Definition: RIFF.cpp:1383
Chunk * AddSubChunk(uint32_t uiChunkID, file_offset_t ullBodySize)
Creates a new sub chunk.
Definition: RIFF.cpp:1463
virtual void __resetPos()
Sets List Chunk's read/write position to zero and causes all sub chunks to do the same.
Definition: RIFF.cpp:1776
Chunk * GetSubChunkAt(size_t pos)
Returns subchunk at supplied pos position within this chunk list.
Definition: RIFF.cpp:1234
RIFF specific classes and definitions.
Definition: RIFF.h:101
String libraryVersion()
Returns version of this C++ library.
Definition: RIFF.cpp:2781
stream_whence_t
File stream position dependent to these relations.
Definition: RIFF.h:128
stream_state_t
Current state of the file stream.
Definition: RIFF.h:121
String libraryName()
Returns the name of this C++ library.
Definition: RIFF.cpp:2773
offset_size_t
Size of RIFF file offsets used in all RIFF chunks' headers.
Definition: RIFF.h:149
@ offset_size_64bit
Always use 64 bit offsets (even for files smaller than 4 GB).
Definition: RIFF.h:152
@ offset_size_auto
Use 32 bit offsets for files smaller than 4 GB, use 64 bit offsets for files equal or larger than 4 G...
Definition: RIFF.h:150
@ offset_size_32bit
Always use 32 bit offsets (even for files larger than 4 GB).
Definition: RIFF.h:151
stream_mode_t
Whether file stream is open in read or in read/write mode.
Definition: RIFF.h:114
layout_t
General RIFF chunk structure of a RIFF file.
Definition: RIFF.h:143
@ layout_standard
Standard RIFF file layout: First chunk in file is a List chunk which contains all other chunks and th...
Definition: RIFF.h:144
@ layout_flat
Not a "real" RIFF file: First chunk in file is an ordinary data chunk, not a List chunk,...
Definition: RIFF.h:145
endian_t
Alignment of data bytes in memory (system dependant).
Definition: RIFF.h:136
uint64_t file_offset_t
Type used by libgig for handling file positioning during file I/O tasks.
Definition: RIFF.h:111
Used for indicating the progress of a certain task.
Definition: RIFF.h:167
float __range_min
Only for internal usage, do not modify!
Definition: RIFF.h:172
void * __private
Only for internal usage, do not modify!
Definition: RIFF.h:174
void(* callback)(progress_t *)
Callback function pointer which has to be assigned to a function for progress notification.
Definition: RIFF.h:168
void * custom
This pointer can be used for arbitrary data.
Definition: RIFF.h:170
const char * activity
Text which describes current ongoing action (e.g. to be displayed along a progress bar).
Definition: RIFF.h:171
std::vector< progress_t > subdivide(int iSubtasks) const
Divides this progress task into the requested amount of equal weighted sub-progress tasks and returns...
Definition: RIFF.cpp:102
float __range_max
Only for internal usage, do not modify!
Definition: RIFF.h:173