libgig  4.4.1.svn7
RIFF.cpp
1 /***************************************************************************
2  * *
3  * libgig - C++ cross-platform Gigasampler format file access library *
4  * *
5  * Copyright (C) 2003-2025 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 DEBUG_RIFF
259  std::cout << "ckID=" << convertToString(ChunkID) << " ";
260  std::cout << "ckSize=" << ullCurrentChunkSize << " ";
261  std::cout << "bEndianNative=" << pFile->bEndianNative << std::endl;
262  #endif // DEBUG_RIFF
263  ullNewChunkSize = ullCurrentChunkSize;
264  }
265  }
266 
267  void Chunk::WriteHeader(file_offset_t filePos) {
268  uint32_t uiNewChunkID = ChunkID;
269  if (ChunkID == CHUNK_ID_RIFF) {
270  #if WORDS_BIGENDIAN
271  if (pFile->bEndianNative) uiNewChunkID = CHUNK_ID_RIFX;
272  #else // little endian
273  if (!pFile->bEndianNative) uiNewChunkID = CHUNK_ID_RIFX;
274  #endif // WORDS_BIGENDIAN
275  }
276 
277  uint64_t ullNewChunkSize = this->ullNewChunkSize;
278  if (!pFile->bEndianNative) {
279  if (pFile->FileOffsetSize == 4)
280  swapBytes_32(&ullNewChunkSize);
281  else
282  swapBytes_64(&ullNewChunkSize);
283  }
284 
285  const File::Handle hWrite = pFile->FileWriteHandle();
286 
287  #if POSIX
288  if (lseek(hWrite, filePos, SEEK_SET) != -1) {
289  write(hWrite, &uiNewChunkID, 4);
290  write(hWrite, &ullNewChunkSize, pFile->FileOffsetSize);
291  }
292  #elif defined(WIN32)
293  LARGE_INTEGER liFilePos;
294  liFilePos.QuadPart = filePos;
295  if (SetFilePointerEx(hWrite, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN)) {
296  DWORD dwBytesWritten;
297  WriteFile(hWrite, &uiNewChunkID, 4, &dwBytesWritten, NULL);
298  WriteFile(hWrite, &ullNewChunkSize, pFile->FileOffsetSize, &dwBytesWritten, NULL);
299  }
300  #else
301  if (!fseeko(hWrite, filePos, SEEK_SET)) {
302  fwrite(&uiNewChunkID, 4, 1, hWrite);
303  fwrite(&ullNewChunkSize, pFile->FileOffsetSize, 1, hWrite);
304  }
305  #endif // POSIX
306  }
307 
312  String Chunk::GetChunkIDString() const {
313  return convertToString(ChunkID);
314  }
315 
324  file_offset_t& Chunk::GetPosUnsafeRef() {
325  if (!pFile->IsIOPerThread()) return chunkPos.ullPos;
326  const std::thread::id tid = std::this_thread::get_id();
327  return chunkPos.byThread[tid];
328  }
329 
336  if (!pFile->IsIOPerThread()) return chunkPos.ullPos;
337  const std::thread::id tid = std::this_thread::get_id();
338  std::lock_guard<std::mutex> lock(chunkPos.mutex);
339  return chunkPos.byThread[tid];
340  }
341 
349  return ullStartPos + GetPos();
350  }
351 
366  #if DEBUG_RIFF
367  std::cout << "Chunk::SetPos(file_offset_t,stream_whence_t)" << std::endl;
368  #endif // DEBUG_RIFF
369  std::lock_guard<std::mutex> lock(chunkPos.mutex);
370  file_offset_t& pos = GetPosUnsafeRef();
371  switch (Whence) {
372  case stream_curpos:
373  pos += Where;
374  break;
375  case stream_end:
376  pos = ullCurrentChunkSize - 1 - Where;
377  break;
378  case stream_backward:
379  pos -= Where;
380  break;
381  case stream_start: default:
382  pos = Where;
383  break;
384  }
385  if (pos > ullCurrentChunkSize) pos = ullCurrentChunkSize;
386  return pos;
387  }
388 
401  #if DEBUG_RIFF
402  std::cout << "Chunk::Remainingbytes()=" << ullCurrentChunkSize - ullPos << std::endl;
403  #endif // DEBUG_RIFF
404  const file_offset_t pos = GetPos();
405  return (ullCurrentChunkSize > pos) ? ullCurrentChunkSize - pos : 0;
406  }
407 
416  return CHUNK_HEADER_SIZE(fileOffsetSize) + // RIFF chunk header
417  ullNewChunkSize + // chunks's actual data body
418  ullNewChunkSize % 2; // optional pad byte
419  }
420 
435  #if DEBUG_RIFF
436  std::cout << "Chunk::GetState()" << std::endl;
437  #endif // DEBUG_RIFF
438 
439  const File::Handle hRead = pFile->FileHandle();
440 
441  if (!_isValidHandle(hRead))
442  return stream_closed;
443 
444  const file_offset_t pos = GetPos();
445  if (pos < ullCurrentChunkSize) return stream_ready;
446  else return stream_end_reached;
447  }
448 
465  file_offset_t Chunk::Read(void* pData, file_offset_t WordCount, file_offset_t WordSize) {
466  #if DEBUG_RIFF
467  std::cout << "Chunk::Read(void*,file_offset_t,file_offset_t)" << std::endl;
468  #endif // DEBUG_RIFF
469  //if (ulStartPos == 0) return 0; // is only 0 if this is a new chunk, so nothing to read (yet)
470  const file_offset_t pos = GetPos();
471  if (pos >= ullCurrentChunkSize) return 0;
472  if (pos + WordCount * WordSize >= ullCurrentChunkSize)
473  WordCount = (ullCurrentChunkSize - pos) / WordSize;
474 
475  const File::Handle hRead = pFile->FileHandle();
476 
477  #if POSIX
478  if (lseek(hRead, ullStartPos + pos, SEEK_SET) < 0) return 0;
479  ssize_t readWords = read(hRead, pData, WordCount * WordSize);
480  if (readWords < 1) {
481  #if DEBUG_RIFF
482  std::cerr << "POSIX read() failed: " << strerror(errno) << std::endl << std::flush;
483  #endif // DEBUG_RIFF
484  return 0;
485  }
486  readWords /= WordSize;
487  #elif defined(WIN32)
488  LARGE_INTEGER liFilePos;
489  liFilePos.QuadPart = ullStartPos + pos;
490  if (!SetFilePointerEx(hRead, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN))
491  return 0;
492  DWORD readWords;
493  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)
494  if (readWords < 1) return 0;
495  readWords /= WordSize;
496  #else // standard C functions
497  if (fseeko(hRead, ullStartPos + pos, SEEK_SET)) return 0;
498  file_offset_t readWords = fread(pData, WordSize, WordCount, hRead);
499  #endif // POSIX
500  if (!pFile->bEndianNative && WordSize != 1) {
501  switch (WordSize) {
502  case 2:
503  for (file_offset_t iWord = 0; iWord < readWords; iWord++)
504  swapBytes_16((uint16_t*) pData + iWord);
505  break;
506  case 4:
507  for (file_offset_t iWord = 0; iWord < readWords; iWord++)
508  swapBytes_32((uint32_t*) pData + iWord);
509  break;
510  case 8:
511  for (file_offset_t iWord = 0; iWord < readWords; iWord++)
512  swapBytes_64((uint64_t*) pData + iWord);
513  break;
514  default:
515  for (file_offset_t iWord = 0; iWord < readWords; iWord++)
516  swapBytes((uint8_t*) pData + iWord * WordSize, WordSize);
517  break;
518  }
519  }
520  SetPos(readWords * WordSize, stream_curpos);
521  return readWords;
522  }
523 
541  file_offset_t Chunk::Write(void* pData, file_offset_t WordCount, file_offset_t WordSize) {
542  const File::HandlePair io = pFile->FileHandlePair();
543  if (io.Mode != stream_mode_read_write)
544  throw Exception("Cannot write data to chunk, file has to be opened in read+write mode first");
545  const file_offset_t pos = GetPos();
546  if (pos >= ullCurrentChunkSize || pos + WordCount * WordSize > ullCurrentChunkSize)
547  throw Exception("End of chunk reached while trying to write data");
548  if (!pFile->bEndianNative && WordSize != 1) {
549  switch (WordSize) {
550  case 2:
551  for (file_offset_t iWord = 0; iWord < WordCount; iWord++)
552  swapBytes_16((uint16_t*) pData + iWord);
553  break;
554  case 4:
555  for (file_offset_t iWord = 0; iWord < WordCount; iWord++)
556  swapBytes_32((uint32_t*) pData + iWord);
557  break;
558  case 8:
559  for (file_offset_t iWord = 0; iWord < WordCount; iWord++)
560  swapBytes_64((uint64_t*) pData + iWord);
561  break;
562  default:
563  for (file_offset_t iWord = 0; iWord < WordCount; iWord++)
564  swapBytes((uint8_t*) pData + iWord * WordSize, WordSize);
565  break;
566  }
567  }
568  #if POSIX
569  if (lseek(io.hWrite, ullStartPos + pos, SEEK_SET) < 0) {
570  throw Exception("Could not seek to position " + ToString(pos) +
571  " in chunk (" + ToString(ullStartPos + pos) + " in file)");
572  }
573  ssize_t writtenWords = write(io.hWrite, pData, WordCount * WordSize);
574  if (writtenWords < 1) throw Exception("POSIX IO Error while trying to write chunk data");
575  writtenWords /= WordSize;
576  #elif defined(WIN32)
577  LARGE_INTEGER liFilePos;
578  liFilePos.QuadPart = ullStartPos + pos;
579  if (!SetFilePointerEx(io.hWrite, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN)) {
580  throw Exception("Could not seek to position " + ToString(pos) +
581  " in chunk (" + ToString(ullStartPos + pos) + " in file)");
582  }
583  DWORD writtenWords;
584  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)
585  if (writtenWords < 1) throw Exception("Windows IO Error while trying to write chunk data");
586  writtenWords /= WordSize;
587  #else // standard C functions
588  if (fseeko(io.hWrite, ullStartPos + pos, SEEK_SET)) {
589  throw Exception("Could not seek to position " + ToString(pos) +
590  " in chunk (" + ToString(ullStartPos + pos) + " in file)");
591  }
592  file_offset_t writtenWords = fwrite(pData, WordSize, WordCount, io.hWrite);
593  #endif // POSIX
594  SetPos(writtenWords * WordSize, stream_curpos);
595  return writtenWords;
596  }
597 
599  file_offset_t Chunk::ReadSceptical(void* pData, file_offset_t WordCount, file_offset_t WordSize) {
600  file_offset_t readWords = Read(pData, WordCount, WordSize);
601  if (readWords != WordCount) throw RIFF::Exception("End of chunk data reached.");
602  return readWords;
603  }
604 
617  file_offset_t Chunk::ReadInt8(int8_t* pData, file_offset_t WordCount) {
618  #if DEBUG_RIFF
619  std::cout << "Chunk::ReadInt8(int8_t*,file_offset_t)" << std::endl;
620  #endif // DEBUG_RIFF
621  return ReadSceptical(pData, WordCount, 1);
622  }
623 
639  file_offset_t Chunk::WriteInt8(int8_t* pData, file_offset_t WordCount) {
640  return Write(pData, WordCount, 1);
641  }
642 
656  file_offset_t Chunk::ReadUint8(uint8_t* pData, file_offset_t WordCount) {
657  #if DEBUG_RIFF
658  std::cout << "Chunk::ReadUint8(uint8_t*,file_offset_t)" << std::endl;
659  #endif // DEBUG_RIFF
660  return ReadSceptical(pData, WordCount, 1);
661  }
662 
678  file_offset_t Chunk::WriteUint8(uint8_t* pData, file_offset_t WordCount) {
679  return Write(pData, WordCount, 1);
680  }
681 
695  file_offset_t Chunk::ReadInt16(int16_t* pData, file_offset_t WordCount) {
696  #if DEBUG_RIFF
697  std::cout << "Chunk::ReadInt16(int16_t*,file_offset_t)" << std::endl;
698  #endif // DEBUG_RIFF
699  return ReadSceptical(pData, WordCount, 2);
700  }
701 
717  file_offset_t Chunk::WriteInt16(int16_t* pData, file_offset_t WordCount) {
718  return Write(pData, WordCount, 2);
719  }
720 
734  file_offset_t Chunk::ReadUint16(uint16_t* pData, file_offset_t WordCount) {
735  #if DEBUG_RIFF
736  std::cout << "Chunk::ReadUint16(uint16_t*,file_offset_t)" << std::endl;
737  #endif // DEBUG_RIFF
738  return ReadSceptical(pData, WordCount, 2);
739  }
740 
756  file_offset_t Chunk::WriteUint16(uint16_t* pData, file_offset_t WordCount) {
757  return Write(pData, WordCount, 2);
758  }
759 
773  file_offset_t Chunk::ReadInt32(int32_t* pData, file_offset_t WordCount) {
774  #if DEBUG_RIFF
775  std::cout << "Chunk::ReadInt32(int32_t*,file_offset_t)" << std::endl;
776  #endif // DEBUG_RIFF
777  return ReadSceptical(pData, WordCount, 4);
778  }
779 
795  file_offset_t Chunk::WriteInt32(int32_t* pData, file_offset_t WordCount) {
796  return Write(pData, WordCount, 4);
797  }
798 
812  file_offset_t Chunk::ReadUint32(uint32_t* pData, file_offset_t WordCount) {
813  #if DEBUG_RIFF
814  std::cout << "Chunk::ReadUint32(uint32_t*,file_offset_t)" << std::endl;
815  #endif // DEBUG_RIFF
816  return ReadSceptical(pData, WordCount, 4);
817  }
818 
830  void Chunk::ReadString(String& s, int size) {
831  char* buf = new char[size];
832  ReadSceptical(buf, 1, size);
833  s.assign(buf, std::find(buf, buf + size, '\0'));
834  delete[] buf;
835  }
836 
852  file_offset_t Chunk::WriteUint32(uint32_t* pData, file_offset_t WordCount) {
853  return Write(pData, WordCount, 4);
854  }
855 
864  int8_t Chunk::ReadInt8() {
865  #if DEBUG_RIFF
866  std::cout << "Chunk::ReadInt8()" << std::endl;
867  #endif // DEBUG_RIFF
868  int8_t word;
869  ReadSceptical(&word,1,1);
870  return word;
871  }
872 
881  uint8_t Chunk::ReadUint8() {
882  #if DEBUG_RIFF
883  std::cout << "Chunk::ReadUint8()" << std::endl;
884  #endif // DEBUG_RIFF
885  uint8_t word;
886  ReadSceptical(&word,1,1);
887  return word;
888  }
889 
899  int16_t Chunk::ReadInt16() {
900  #if DEBUG_RIFF
901  std::cout << "Chunk::ReadInt16()" << std::endl;
902  #endif // DEBUG_RIFF
903  int16_t word;
904  ReadSceptical(&word,1,2);
905  return word;
906  }
907 
917  uint16_t Chunk::ReadUint16() {
918  #if DEBUG_RIFF
919  std::cout << "Chunk::ReadUint16()" << std::endl;
920  #endif // DEBUG_RIFF
921  uint16_t word;
922  ReadSceptical(&word,1,2);
923  return word;
924  }
925 
935  int32_t Chunk::ReadInt32() {
936  #if DEBUG_RIFF
937  std::cout << "Chunk::ReadInt32()" << std::endl;
938  #endif // DEBUG_RIFF
939  int32_t word;
940  ReadSceptical(&word,1,4);
941  return word;
942  }
943 
953  uint32_t Chunk::ReadUint32() {
954  #if DEBUG_RIFF
955  std::cout << "Chunk::ReadUint32()" << std::endl;
956  #endif // DEBUG_RIFF
957  uint32_t word;
958  ReadSceptical(&word,1,4);
959  return word;
960  }
961 
985  if (!pChunkData && pFile->Filename != "" /*&& ulStartPos != 0*/) {
986  File::Handle hRead = pFile->FileHandle();
987  #if POSIX
988  if (lseek(hRead, ullStartPos, SEEK_SET) == -1) return NULL;
989  #elif defined(WIN32)
990  LARGE_INTEGER liFilePos;
991  liFilePos.QuadPart = ullStartPos;
992  if (!SetFilePointerEx(hRead, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN)) return NULL;
993  #else
994  if (fseeko(hRead, ullStartPos, SEEK_SET)) return NULL;
995  #endif // POSIX
996  file_offset_t ullBufferSize = (ullCurrentChunkSize > ullNewChunkSize) ? ullCurrentChunkSize : ullNewChunkSize;
997  pChunkData = new uint8_t[ullBufferSize];
998  if (!pChunkData) return NULL;
999  memset(pChunkData, 0, ullBufferSize);
1000  #if POSIX
1001  file_offset_t readWords = read(hRead, pChunkData, GetSize());
1002  #elif defined(WIN32)
1003  DWORD readWords;
1004  ReadFile(hRead, pChunkData, GetSize(), &readWords, NULL); //FIXME: won't load chunks larger than 2GB !
1005  #else
1006  file_offset_t readWords = fread(pChunkData, 1, GetSize(), hRead);
1007  #endif // POSIX
1008  if (readWords != GetSize()) {
1009  delete[] pChunkData;
1010  return (pChunkData = NULL);
1011  }
1012  ullChunkDataSize = ullBufferSize;
1013  } else if (ullNewChunkSize > ullChunkDataSize) {
1014  uint8_t* pNewBuffer = new uint8_t[ullNewChunkSize];
1015  if (!pNewBuffer) throw Exception("Could not enlarge chunk data buffer to " + ToString(ullNewChunkSize) + " bytes");
1016  memset(pNewBuffer, 0 , ullNewChunkSize);
1017  if (pChunkData) {
1018  memcpy(pNewBuffer, pChunkData, ullChunkDataSize);
1019  delete[] pChunkData;
1020  }
1021  pChunkData = pNewBuffer;
1022  ullChunkDataSize = ullNewChunkSize;
1023  }
1024  return pChunkData;
1025  }
1026 
1034  if (pChunkData) {
1035  delete[] pChunkData;
1036  pChunkData = NULL;
1037  }
1038  }
1039 
1059  if (NewSize == 0)
1060  throw Exception("There is at least one empty chunk (zero size): " + __resolveChunkPath(this));
1061  if ((NewSize >> 48) != 0)
1062  throw Exception("Unrealistic high chunk size detected: " + __resolveChunkPath(this));
1063  if (ullNewChunkSize == NewSize) return;
1064  ullNewChunkSize = NewSize;
1065  }
1066 
1081  file_offset_t Chunk::WriteChunk(file_offset_t ullWritePos, file_offset_t ullCurrentDataOffset, progress_t* pProgress) {
1082  const file_offset_t ullOriginalPos = ullWritePos;
1083  ullWritePos += CHUNK_HEADER_SIZE(pFile->FileOffsetSize);
1084 
1085  const File::HandlePair io = pFile->FileHandlePair();
1086 
1087  if (io.Mode != stream_mode_read_write)
1088  throw Exception("Cannot write list chunk, file has to be opened in read+write mode");
1089 
1090  // if the whole chunk body was loaded into RAM
1091  if (pChunkData) {
1092  // make sure chunk data buffer in RAM is at least as large as the new chunk size
1093  LoadChunkData();
1094  // write chunk data from RAM persistently to the file
1095  #if POSIX
1096  lseek(io.hWrite, ullWritePos, SEEK_SET);
1097  if (write(io.hWrite, pChunkData, ullNewChunkSize) != ullNewChunkSize) {
1098  throw Exception("Writing Chunk data (from RAM) failed");
1099  }
1100  #elif defined(WIN32)
1101  LARGE_INTEGER liFilePos;
1102  liFilePos.QuadPart = ullWritePos;
1103  SetFilePointerEx(io.hWrite, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN);
1104  DWORD dwBytesWritten;
1105  WriteFile(io.hWrite, pChunkData, ullNewChunkSize, &dwBytesWritten, NULL); //FIXME: won't save chunks larger than 2GB !
1106  if (dwBytesWritten != ullNewChunkSize) {
1107  throw Exception("Writing Chunk data (from RAM) failed");
1108  }
1109  #else
1110  fseeko(io.hWrite, ullWritePos, SEEK_SET);
1111  if (fwrite(pChunkData, 1, ullNewChunkSize, io.hWrite) != ullNewChunkSize) {
1112  throw Exception("Writing Chunk data (from RAM) failed");
1113  }
1114  #endif // POSIX
1115  } else {
1116  // move chunk data from the end of the file to the appropriate position
1117  int8_t* pCopyBuffer = new int8_t[4096];
1118  file_offset_t ullToMove = (ullNewChunkSize < ullCurrentChunkSize) ? ullNewChunkSize : ullCurrentChunkSize;
1119  #if defined(WIN32)
1120  DWORD iBytesMoved = 1; // we have to pass it via pointer to the Windows API, thus the correct size must be ensured
1121  #else
1122  int iBytesMoved = 1;
1123  #endif
1124  for (file_offset_t ullOffset = 0; ullToMove > 0 && iBytesMoved > 0; ullOffset += iBytesMoved, ullToMove -= iBytesMoved) {
1125  iBytesMoved = (ullToMove < 4096) ? int(ullToMove) : 4096;
1126  #if POSIX
1127  lseek(io.hRead, ullStartPos + ullCurrentDataOffset + ullOffset, SEEK_SET);
1128  iBytesMoved = (int) read(io.hRead, pCopyBuffer, (size_t) iBytesMoved);
1129  lseek(io.hWrite, ullWritePos + ullOffset, SEEK_SET);
1130  iBytesMoved = (int) write(io.hWrite, pCopyBuffer, (size_t) iBytesMoved);
1131  #elif defined(WIN32)
1132  LARGE_INTEGER liFilePos;
1133  liFilePos.QuadPart = ullStartPos + ullCurrentDataOffset + ullOffset;
1134  SetFilePointerEx(io.hRead, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN);
1135  ReadFile(io.hRead, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
1136  liFilePos.QuadPart = ullWritePos + ullOffset;
1137  SetFilePointerEx(io.hWrite, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN);
1138  WriteFile(io.hWrite, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
1139  #else
1140  fseeko(io.hRead, ullStartPos + ullCurrentDataOffset + ullOffset, SEEK_SET);
1141  iBytesMoved = fread(pCopyBuffer, 1, iBytesMoved, io.hRead);
1142  fseeko(io.hWrite, ullWritePos + ullOffset, SEEK_SET);
1143  iBytesMoved = fwrite(pCopyBuffer, 1, iBytesMoved, io.hWrite);
1144  #endif
1145  }
1146  delete[] pCopyBuffer;
1147  if (iBytesMoved < 0) throw Exception("Writing Chunk data (from file) failed");
1148  }
1149 
1150  // update this chunk's header
1151  ullCurrentChunkSize = ullNewChunkSize;
1152  WriteHeader(ullOriginalPos);
1153 
1154  // update chunk's position pointers
1155  ullStartPos = ullOriginalPos + CHUNK_HEADER_SIZE(pFile->FileOffsetSize);
1157 
1158  if (pProgress && pProgress->__private) {
1159  _FileProgress* fileProgress = (_FileProgress*) pProgress->__private;
1160  fileProgress->advance(pProgress, ullNewChunkSize +
1161  CHUNK_HEADER_SIZE(pFile->FileOffsetSize));
1162  }
1163 
1164  // add pad byte if needed
1165  if ((ullStartPos + ullNewChunkSize) % 2 != 0) {
1166  const char cPadByte = 0;
1167  #if POSIX
1168  lseek(io.hWrite, ullStartPos + ullNewChunkSize, SEEK_SET);
1169  write(io.hWrite, &cPadByte, 1);
1170  #elif defined(WIN32)
1171  LARGE_INTEGER liFilePos;
1172  liFilePos.QuadPart = ullStartPos + ullNewChunkSize;
1173  SetFilePointerEx(io.hWrite, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN);
1174  DWORD dwBytesWritten;
1175  WriteFile(io.hWrite, &cPadByte, 1, &dwBytesWritten, NULL);
1176  #else
1177  fseeko(io.hWrite, ullStartPos + ullNewChunkSize, SEEK_SET);
1178  fwrite(&cPadByte, 1, 1, io.hWrite);
1179  #endif
1180  return ullStartPos + ullNewChunkSize + 1;
1181  }
1182 
1183  return ullStartPos + ullNewChunkSize;
1184  }
1185 
1187  std::lock_guard<std::mutex> lock(chunkPos.mutex);
1188  chunkPos.ullPos = 0;
1189  chunkPos.byThread.clear();
1190  }
1191 
1192 
1193 
1194 // *************** List ***************
1195 // *
1196 
1197  List::List(File* pFile) : Chunk(pFile) {
1198  #if DEBUG_RIFF
1199  std::cout << "List::List(File* pFile)" << std::endl;
1200  #endif // DEBUG_RIFF
1201  pSubChunks = NULL;
1202  pSubChunksMap = NULL;
1203  }
1204 
1205  List::List(File* pFile, file_offset_t StartPos, List* Parent)
1206  : Chunk(pFile, StartPos, Parent) {
1207  #if DEBUG_RIFF
1208  std::cout << "List::List(File*,file_offset_t,List*)" << std::endl;
1209  #endif // DEBUG_RIFF
1210  pSubChunks = NULL;
1211  pSubChunksMap = NULL;
1212  ReadHeader(StartPos);
1213  ullStartPos = StartPos + LIST_HEADER_SIZE(pFile->FileOffsetSize);
1214  }
1215 
1216  List::List(File* pFile, List* pParent, uint32_t uiListID)
1217  : Chunk(pFile, pParent, CHUNK_ID_LIST, 0) {
1218  pSubChunks = NULL;
1219  pSubChunksMap = NULL;
1220  ListType = uiListID;
1221  }
1222 
1223  List::~List() {
1224  #if DEBUG_RIFF
1225  std::cout << "List::~List()" << std::endl;
1226  #endif // DEBUG_RIFF
1227  DeleteChunkList();
1228  }
1229 
1230  void List::DeleteChunkList() {
1231  if (pSubChunks) {
1232  ChunkList::iterator iter = pSubChunks->begin();
1233  ChunkList::iterator end = pSubChunks->end();
1234  while (iter != end) {
1235  delete *iter;
1236  iter++;
1237  }
1238  delete pSubChunks;
1239  pSubChunks = NULL;
1240  }
1241  if (pSubChunksMap) {
1242  delete pSubChunksMap;
1243  pSubChunksMap = NULL;
1244  }
1245  }
1246 
1257  if (!pSubChunks) LoadSubChunks();
1258  if (pos >= pSubChunks->size()) return NULL;
1259  return (*pSubChunks)[pos];
1260  }
1261 
1273  Chunk* List::GetSubChunk(uint32_t ChunkID) {
1274  #if DEBUG_RIFF
1275  std::cout << "List::GetSubChunk(uint32_t)" << std::endl;
1276  #endif // DEBUG_RIFF
1277  if (!pSubChunksMap) LoadSubChunks();
1278  return (*pSubChunksMap)[ChunkID];
1279  }
1280 
1291  List* List::GetSubListAt(size_t pos) {
1292  if (!pSubChunks) LoadSubChunks();
1293  if (pos >= pSubChunks->size()) return NULL;
1294  for (size_t iCk = 0, iLst = 0; iCk < pSubChunks->size(); ++iCk) {
1295  Chunk* pChunk = (*pSubChunks)[iCk];
1296  if (pChunk->GetChunkID() != CHUNK_ID_LIST) continue;
1297  if (iLst == pos) return (List*) pChunk;
1298  ++iLst;
1299  }
1300  return NULL;
1301  }
1302 
1314  List* List::GetSubList(uint32_t ListType) {
1315  #if DEBUG_RIFF
1316  std::cout << "List::GetSubList(uint32_t)" << std::endl;
1317  #endif // DEBUG_RIFF
1318  if (!pSubChunks) LoadSubChunks();
1319  ChunkList::iterator iter = pSubChunks->begin();
1320  ChunkList::iterator end = pSubChunks->end();
1321  while (iter != end) {
1322  if ((*iter)->GetChunkID() == CHUNK_ID_LIST) {
1323  List* l = (List*) *iter;
1324  if (l->GetListType() == ListType) return l;
1325  }
1326  iter++;
1327  }
1328  return NULL;
1329  }
1330 
1343  #if DEBUG_RIFF
1344  std::cout << "List::GetFirstSubChunk()" << std::endl;
1345  #endif // DEBUG_RIFF
1346  if (!pSubChunks) LoadSubChunks();
1347  ChunksIterator = pSubChunks->begin();
1348  return (ChunksIterator != pSubChunks->end()) ? *ChunksIterator : NULL;
1349  }
1350 
1362  #if DEBUG_RIFF
1363  std::cout << "List::GetNextSubChunk()" << std::endl;
1364  #endif // DEBUG_RIFF
1365  if (!pSubChunks) return NULL;
1366  ChunksIterator++;
1367  return (ChunksIterator != pSubChunks->end()) ? *ChunksIterator : NULL;
1368  }
1369 
1382  #if DEBUG_RIFF
1383  std::cout << "List::GetFirstSubList()" << std::endl;
1384  #endif // DEBUG_RIFF
1385  if (!pSubChunks) LoadSubChunks();
1386  ListIterator = pSubChunks->begin();
1387  ChunkList::iterator end = pSubChunks->end();
1388  while (ListIterator != end) {
1389  if ((*ListIterator)->GetChunkID() == CHUNK_ID_LIST) return (List*) *ListIterator;
1390  ListIterator++;
1391  }
1392  return NULL;
1393  }
1394 
1406  #if DEBUG_RIFF
1407  std::cout << "List::GetNextSubList()" << std::endl;
1408  #endif // DEBUG_RIFF
1409  if (!pSubChunks) return NULL;
1410  if (ListIterator == pSubChunks->end()) return NULL;
1411  ListIterator++;
1412  ChunkList::iterator end = pSubChunks->end();
1413  while (ListIterator != end) {
1414  if ((*ListIterator)->GetChunkID() == CHUNK_ID_LIST) return (List*) *ListIterator;
1415  ListIterator++;
1416  }
1417  return NULL;
1418  }
1419 
1424  if (!pSubChunks) LoadSubChunks();
1425  return pSubChunks->size();
1426  }
1427 
1432  size_t List::CountSubChunks(uint32_t ChunkID) {
1433  size_t result = 0;
1434  if (!pSubChunks) LoadSubChunks();
1435  ChunkList::iterator iter = pSubChunks->begin();
1436  ChunkList::iterator end = pSubChunks->end();
1437  while (iter != end) {
1438  if ((*iter)->GetChunkID() == ChunkID) {
1439  result++;
1440  }
1441  iter++;
1442  }
1443  return result;
1444  }
1445 
1450  return CountSubChunks(CHUNK_ID_LIST);
1451  }
1452 
1457  size_t List::CountSubLists(uint32_t ListType) {
1458  size_t result = 0;
1459  if (!pSubChunks) LoadSubChunks();
1460  ChunkList::iterator iter = pSubChunks->begin();
1461  ChunkList::iterator end = pSubChunks->end();
1462  while (iter != end) {
1463  if ((*iter)->GetChunkID() == CHUNK_ID_LIST) {
1464  List* l = (List*) *iter;
1465  if (l->GetListType() == ListType) result++;
1466  }
1467  iter++;
1468  }
1469  return result;
1470  }
1471 
1485  Chunk* List::AddSubChunk(uint32_t uiChunkID, file_offset_t ullBodySize) {
1486  if (ullBodySize == 0) throw Exception("Chunk body size must be at least 1 byte");
1487  if (!pSubChunks) LoadSubChunks();
1488 
1489  Chunk* pNewChunk = new Chunk(pFile, this, uiChunkID, 0);
1490  pSubChunks->push_back(pNewChunk);
1491  (*pSubChunksMap)[uiChunkID] = pNewChunk;
1492  pNewChunk->Resize(ullBodySize);
1493  ullNewChunkSize += CHUNK_HEADER_SIZE(pFile->FileOffsetSize);
1494 
1495  pFile->nDataChunkCount++;
1496 
1497  return pNewChunk;
1498  }
1499 
1511  void List::MoveSubChunk(Chunk* pSrc, Chunk* pDst) {
1512  if (!pSubChunks) LoadSubChunks();
1513  for (size_t i = 0; i < pSubChunks->size(); ++i) {
1514  if ((*pSubChunks)[i] == pSrc) {
1515  pSubChunks->erase(pSubChunks->begin() + i);
1516  ChunkList::iterator iter =
1517  find(pSubChunks->begin(), pSubChunks->end(), pDst);
1518  pSubChunks->insert(iter, pSrc);
1519  return;
1520  }
1521  }
1522  }
1523 
1532  void List::MoveSubChunk(Chunk* pSrc, List* pNewParent) {
1533  if (pNewParent == this || !pNewParent) return;
1534  if (!pSubChunks) LoadSubChunks();
1535  if (!pNewParent->pSubChunks) pNewParent->LoadSubChunks();
1536  ChunkList::iterator iter =
1537  find(pSubChunks->begin(), pSubChunks->end(), pSrc);
1538  if (iter == pSubChunks->end()) return;
1539  pSubChunks->erase(iter);
1540  pNewParent->pSubChunks->push_back(pSrc);
1541  // update chunk id map of this List
1542  if ((*pSubChunksMap)[pSrc->GetChunkID()] == pSrc) {
1543  pSubChunksMap->erase(pSrc->GetChunkID());
1544  // try to find another chunk of the same chunk ID
1545  ChunkList::iterator iter = pSubChunks->begin();
1546  ChunkList::iterator end = pSubChunks->end();
1547  for (; iter != end; ++iter) {
1548  if ((*iter)->GetChunkID() == pSrc->GetChunkID()) {
1549  (*pSubChunksMap)[pSrc->GetChunkID()] = *iter;
1550  break; // we're done, stop search
1551  }
1552  }
1553  }
1554  // update chunk id map of other list
1555  if (!(*pNewParent->pSubChunksMap)[pSrc->GetChunkID()])
1556  (*pNewParent->pSubChunksMap)[pSrc->GetChunkID()] = pSrc;
1557  }
1558 
1568  List* List::AddSubList(uint32_t uiListType) {
1569  if (!pSubChunks) LoadSubChunks();
1570 
1571  List* pNewListChunk = new List(pFile, this, uiListType);
1572  pSubChunks->push_back(pNewListChunk);
1573  (*pSubChunksMap)[CHUNK_ID_LIST] = pNewListChunk;
1574  ullNewChunkSize += LIST_HEADER_SIZE(pFile->FileOffsetSize);
1575 
1576  pFile->nListChunkCount++;
1577 
1578  return pNewListChunk;
1579  }
1580 
1591  void List::DeleteSubChunk(Chunk* pSubChunk) {
1592  if (!pSubChunks) LoadSubChunks();
1593  ChunkList::iterator iter =
1594  find(pSubChunks->begin(), pSubChunks->end(), pSubChunk);
1595  if (iter == pSubChunks->end()) return;
1596  pSubChunks->erase(iter);
1597 
1598  if (dynamic_cast<List*>(pSubChunk))
1599  pFile->nListChunkCount--;
1600  else
1601  pFile->nDataChunkCount--;
1602 
1603  if ((*pSubChunksMap)[pSubChunk->GetChunkID()] == pSubChunk) {
1604  pSubChunksMap->erase(pSubChunk->GetChunkID());
1605  // try to find another chunk of the same chunk ID
1606  ChunkList::iterator iter = pSubChunks->begin();
1607  ChunkList::iterator end = pSubChunks->end();
1608  for (; iter != end; ++iter) {
1609  if ((*iter)->GetChunkID() == pSubChunk->GetChunkID()) {
1610  (*pSubChunksMap)[pSubChunk->GetChunkID()] = *iter;
1611  break; // we're done, stop search
1612  }
1613  }
1614  }
1615  delete pSubChunk;
1616  }
1617 
1626  if (!pSubChunks) LoadSubChunks();
1627  file_offset_t size = LIST_HEADER_SIZE(fileOffsetSize);
1628  ChunkList::iterator iter = pSubChunks->begin();
1629  ChunkList::iterator end = pSubChunks->end();
1630  for (; iter != end; ++iter)
1631  size += (*iter)->RequiredPhysicalSize(fileOffsetSize);
1632  return size;
1633  }
1634 
1635  void List::ReadHeader(file_offset_t filePos) {
1636  #if DEBUG_RIFF
1637  std::cout << "List::Readheader(file_offset_t) ";
1638  #endif // DEBUG_RIFF
1639  Chunk::ReadHeader(filePos);
1640  if (ullCurrentChunkSize < 4) return;
1641  ullNewChunkSize = ullCurrentChunkSize -= 4;
1642 
1643  const File::Handle hRead = pFile->FileHandle();
1644 
1645  #if POSIX
1646  lseek(hRead, filePos + CHUNK_HEADER_SIZE(pFile->FileOffsetSize), SEEK_SET);
1647  read(hRead, &ListType, 4);
1648  #elif defined(WIN32)
1649  LARGE_INTEGER liFilePos;
1650  liFilePos.QuadPart = filePos + CHUNK_HEADER_SIZE(pFile->FileOffsetSize);
1651  SetFilePointerEx(hRead, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN);
1652  DWORD dwBytesRead;
1653  ReadFile(hRead, &ListType, 4, &dwBytesRead, NULL);
1654  #else
1655  fseeko(hRead, filePos + CHUNK_HEADER_SIZE(pFile->FileOffsetSize), SEEK_SET);
1656  fread(&ListType, 4, 1, hRead);
1657  #endif // POSIX
1658  #if DEBUG_RIFF
1659  std::cout << "listType=" << convertToString(ListType) << std::endl;
1660  #endif // DEBUG_RIFF
1661  if (!pFile->bEndianNative) {
1662  //swapBytes_32(&ListType);
1663  }
1664  }
1665 
1666  void List::WriteHeader(file_offset_t filePos) {
1667  // the four list type bytes officially belong the chunk's body in the RIFF format
1668  ullNewChunkSize += 4;
1669  Chunk::WriteHeader(filePos);
1670  ullNewChunkSize -= 4; // just revert the +4 incrementation
1671 
1672  const File::Handle hWrite = pFile->FileWriteHandle();
1673 
1674  #if POSIX
1675  lseek(hWrite, filePos + CHUNK_HEADER_SIZE(pFile->FileOffsetSize), SEEK_SET);
1676  write(hWrite, &ListType, 4);
1677  #elif defined(WIN32)
1678  LARGE_INTEGER liFilePos;
1679  liFilePos.QuadPart = filePos + CHUNK_HEADER_SIZE(pFile->FileOffsetSize);
1680  SetFilePointerEx(hWrite, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN);
1681  DWORD dwBytesWritten;
1682  WriteFile(hWrite, &ListType, 4, &dwBytesWritten, NULL);
1683  #else
1684  fseeko(hWrite, filePos + CHUNK_HEADER_SIZE(pFile->FileOffsetSize), SEEK_SET);
1685  fwrite(&ListType, 4, 1, hWrite);
1686  #endif // POSIX
1687  }
1688 
1689  void List::LoadSubChunks(progress_t* pProgress) {
1690  #if DEBUG_RIFF
1691  std::cout << "List::LoadSubChunks()";
1692  #endif // DEBUG_RIFF
1693  if (!pSubChunks) {
1694  pSubChunks = new ChunkList();
1695  pSubChunksMap = new ChunkMap();
1696 
1697  const File::Handle hRead = pFile->FileHandle();
1698  if (!_isValidHandle(hRead)) return;
1699 
1700  const file_offset_t ullOriginalPos = GetPos();
1701  SetPos(0); // jump to beginning of list chunk body
1702  while (RemainingBytes() >= CHUNK_HEADER_SIZE(pFile->FileOffsetSize)) {
1703  Chunk* ck;
1704  uint32_t ckid;
1705  // return value check is required here to prevent a potential
1706  // garbage data use of 'ckid' below in case Read() failed
1707  if (Read(&ckid, 4, 1) != 4)
1708  throw Exception("LoadSubChunks(): Failed reading RIFF chunk ID");
1709  #if DEBUG_RIFF
1710  std::cout << " ckid=" << convertToString(ckid) << std::endl;
1711  #endif // DEBUG_RIFF
1712  const file_offset_t pos = GetPos();
1713  if (ckid == CHUNK_ID_LIST) {
1714  ck = new RIFF::List(pFile, ullStartPos + pos - 4, this);
1715  SetPos(ck->GetSize() + LIST_HEADER_SIZE(pFile->FileOffsetSize) - 4, RIFF::stream_curpos);
1716 
1717  pFile->nListChunkCount++;
1718  }
1719  else { // simple chunk
1720  ck = new RIFF::Chunk(pFile, ullStartPos + pos - 4, this);
1721  SetPos(ck->GetSize() + CHUNK_HEADER_SIZE(pFile->FileOffsetSize) - 4, RIFF::stream_curpos);
1722 
1723  pFile->nDataChunkCount++;
1724  }
1725  pSubChunks->push_back(ck);
1726  (*pSubChunksMap)[ckid] = ck;
1727  if (GetPos() % 2 != 0) SetPos(1, RIFF::stream_curpos); // jump over pad byte
1728  }
1729  SetPos(ullOriginalPos); // restore position before this call
1730  }
1731  if (pProgress)
1732  __notify_progress(pProgress, 1.0); // notify done
1733  }
1734 
1735  void List::LoadSubChunksRecursively(progress_t* pProgress) {
1736  const int n = (int) CountSubLists();
1737  int i = 0;
1738  for (List* pList = GetSubListAt(i); pList; pList = GetSubListAt(++i)) {
1739  if (pProgress) {
1740  // divide local progress into subprogress
1741  progress_t subprogress;
1742  __divide_progress(pProgress, &subprogress, n, i);
1743  // do the actual work
1744  pList->LoadSubChunksRecursively(&subprogress);
1745  } else
1746  pList->LoadSubChunksRecursively(NULL);
1747  }
1748  if (pProgress)
1749  __notify_progress(pProgress, 1.0); // notify done
1750  }
1751 
1767  file_offset_t List::WriteChunk(file_offset_t ullWritePos, file_offset_t ullCurrentDataOffset, progress_t* pProgress) {
1768  const file_offset_t ullOriginalPos = ullWritePos;
1769  ullWritePos += LIST_HEADER_SIZE(pFile->FileOffsetSize);
1770 
1771  if (pFile->GetMode() != stream_mode_read_write)
1772  throw Exception("Cannot write list chunk, file has to be opened in read+write mode");
1773 
1774  // write all subchunks (including sub list chunks) recursively
1775  if (pSubChunks) {
1776  for (ChunkList::iterator iter = pSubChunks->begin(), end = pSubChunks->end(); iter != end; ++iter) {
1777  ullWritePos = (*iter)->WriteChunk(ullWritePos, ullCurrentDataOffset, pProgress);
1778  }
1779  }
1780 
1781  // update this list chunk's header
1782  ullCurrentChunkSize = ullNewChunkSize = ullWritePos - ullOriginalPos - LIST_HEADER_SIZE(pFile->FileOffsetSize);
1783  WriteHeader(ullOriginalPos);
1784 
1785  // offset of this list chunk in new written file may have changed
1786  ullStartPos = ullOriginalPos + LIST_HEADER_SIZE(pFile->FileOffsetSize);
1787 
1788  if (pProgress && pProgress->__private) {
1789  _FileProgress* fileProgress = (_FileProgress*) pProgress->__private;
1790  fileProgress->advance(pProgress,
1791  LIST_HEADER_SIZE(pFile->FileOffsetSize));
1792  }
1793 
1794  return ullWritePos;
1795  }
1796 
1799  if (pSubChunks) {
1800  for (ChunkList::iterator iter = pSubChunks->begin(), end = pSubChunks->end(); iter != end; ++iter) {
1801  (*iter)->__resetPos();
1802  }
1803  }
1804  }
1805 
1809  String List::GetListTypeString() const {
1810  return convertToString(ListType);
1811  }
1812 
1813 
1814 
1815 // *************** File ***************
1816 // *
1817 
1832  File::File(uint32_t FileType)
1833  : List(this), bIsNewFile(true), Layout(layout_standard),
1834  FileOffsetPreference(offset_size_auto)
1835  {
1836  io.isPerThread = false;
1837  io.hRead = io.hWrite = RIFF_INVALID_FILE_HANDLE;
1838  io.Mode = stream_mode_closed;
1839  bEndianNative = true;
1840  ListType = FileType;
1841  FileOffsetSize = 4;
1842  nDataChunkCount = 0;
1843  nListChunkCount = 0;
1844  ullStartPos = RIFF_HEADER_SIZE(FileOffsetSize);
1845  }
1846 
1855  File::File(const String& path)
1856  : List(this), Filename(path), bIsNewFile(false), Layout(layout_standard),
1857  FileOffsetPreference(offset_size_auto)
1858  {
1859  #if DEBUG_RIFF
1860  std::cout << "File::File("<<path<<")" << std::endl;
1861  #endif // DEBUG_RIFF
1862  bEndianNative = true;
1863  FileOffsetSize = 4;
1864  nDataChunkCount = 0;
1865  nListChunkCount = 0;
1866  try {
1867  __openExistingFile(path);
1868  if (ChunkID != CHUNK_ID_RIFF && ChunkID != CHUNK_ID_RIFX) {
1869  throw RIFF::Exception("Not a RIFF file");
1870  }
1871  }
1872  catch (...) {
1873  Cleanup();
1874  throw;
1875  }
1876  }
1877 
1904  File::File(const String& path, uint32_t FileType, endian_t Endian, layout_t layout, offset_size_t fileOffsetSize)
1905  : List(this), Filename(path), bIsNewFile(false), Layout(layout),
1906  FileOffsetPreference(fileOffsetSize)
1907  {
1908  SetByteOrder(Endian);
1909  if (fileOffsetSize < offset_size_auto || fileOffsetSize > offset_size_64bit)
1910  throw Exception("Invalid RIFF::offset_size_t");
1911  FileOffsetSize = 4;
1912  nDataChunkCount = 0;
1913  nListChunkCount = 0;
1914  try {
1915  __openExistingFile(path, &FileType);
1916  }
1917  catch (...) {
1918  Cleanup();
1919  throw;
1920  }
1921  }
1922 
1933  void File::__openExistingFile(const String& path, uint32_t* FileType) {
1934  io.isPerThread = false;
1935  #if POSIX
1936  io.hRead = io.hWrite = open(path.c_str(), O_RDONLY | O_NONBLOCK);
1937  if (!_isValidHandle(io.hRead)) {
1938  String sError = strerror(errno);
1939  throw RIFF::Exception("Can't open \"" + path + "\": " + sError);
1940  }
1941  #elif defined(WIN32)
1942  io.hRead = io.hWrite = CreateFile(
1943  path.c_str(), GENERIC_READ,
1944  FILE_SHARE_READ | FILE_SHARE_WRITE,
1945  NULL, OPEN_EXISTING,
1946  FILE_ATTRIBUTE_NORMAL |
1947  FILE_FLAG_RANDOM_ACCESS, NULL
1948  );
1949  if (!_isValidHandle(io.hRead)) {
1950  throw RIFF::Exception("Can't open \"" + path + "\"");
1951  }
1952  #else
1953  io.hRead = io.hWrite = fopen(path.c_str(), "rb");
1954  if (!_isValidHandle(io.hRead))
1955  throw RIFF::Exception("Can't open \"" + path + "\"");
1956  #endif // POSIX
1957  io.Mode = stream_mode_read;
1958 
1959  // determine RIFF file offset size to be used (in RIFF chunk headers)
1960  // according to the current file offset preference
1961  FileOffsetSize = FileOffsetSizeFor(GetCurrentFileSize());
1962 
1963  switch (Layout) {
1964  case layout_standard: // this is a normal RIFF file
1965  ullStartPos = RIFF_HEADER_SIZE(FileOffsetSize);
1966  ReadHeader(0);
1967  if (FileType && ChunkID != *FileType)
1968  throw RIFF::Exception("Invalid file container ID");
1969  break;
1970  case layout_flat: // non-standard RIFF-alike file
1971  ullStartPos = 0;
1972  ullNewChunkSize = ullCurrentChunkSize = GetCurrentFileSize();
1973  if (FileType) {
1974  uint32_t ckid;
1975  if (Read(&ckid, 4, 1) != 4) {
1976  throw RIFF::Exception("Invalid file header ID (premature end of header)");
1977  } else if (ckid != *FileType) {
1978  String s = " (expected '" + convertToString(*FileType) + "' but got '" + convertToString(ckid) + "')";
1979  throw RIFF::Exception("Invalid file header ID" + s);
1980  }
1981  SetPos(0); // reset to first byte of file
1982  }
1983  LoadSubChunks();
1984  break;
1985  }
1986  }
1987 
1988  String File::GetFileName() const {
1989  return Filename;
1990  }
1991 
1992  void File::SetFileName(const String& path) {
1993  Filename = path;
1994  }
1995 
2004  File::HandlePair& File::FileHandlePairUnsafeRef() {
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 
2024  File::HandlePair File::FileHandlePair() const {
2025  std::lock_guard<std::mutex> lock(io.mutex);
2026  if (io.byThread.empty()) return io;
2027  const std::thread::id tid = std::this_thread::get_id();
2028  const auto it = io.byThread.find(tid);
2029  return (it != io.byThread.end()) ?
2030  it->second :
2031  io.byThread[tid] = {
2032  // designated initializers require C++20, so commented for now
2033  /* .hRead = */ RIFF_INVALID_FILE_HANDLE,
2034  /* .hWrite = */ RIFF_INVALID_FILE_HANDLE,
2035  /* .Mode = */ stream_mode_closed
2036  };
2037  }
2038 
2046  return FileHandlePair().hRead;
2047  }
2048 
2056  return FileHandlePair().hWrite;
2057  }
2058 
2066  return FileHandlePair().Mode;
2067  }
2068 
2069  layout_t File::GetLayout() const {
2070  return Layout;
2071  }
2072 
2086  bool bResetPos = false;
2087  bool res = SetModeInternal(NewMode, &bResetPos);
2088  // resetting position must be handled outside above's call to avoid any
2089  // potential dead lock, as SetModeInternal() acquires a lock and
2090  // __resetPos() acquires a lock by itself (not a theoretical issue!)
2091  if (bResetPos)
2092  __resetPos(); // reset read/write position of ALL 'Chunk' objects
2093  return res;
2094  }
2095 
2096  bool File::SetModeInternal(stream_mode_t NewMode, bool* pResetPos) {
2097  std::lock_guard<std::mutex> lock(io.mutex);
2098  HandlePair& io = FileHandlePairUnsafeRef();
2099  if (NewMode != io.Mode) {
2100  switch (NewMode) {
2101  case stream_mode_read:
2102  if (_isValidHandle(io.hRead)) _close(io.hRead);
2103  #if POSIX
2104  io.hRead = io.hWrite = open(Filename.c_str(), O_RDONLY | O_NONBLOCK);
2105  if (!_isValidHandle(io.hRead)) {
2106  String sError = strerror(errno);
2107  throw Exception("Could not (re)open file \"" + Filename + "\" in read mode: " + sError);
2108  }
2109  #elif defined(WIN32)
2110  io.hRead = io.hWrite = CreateFile(
2111  Filename.c_str(), GENERIC_READ,
2112  FILE_SHARE_READ | FILE_SHARE_WRITE,
2113  NULL, OPEN_EXISTING,
2114  FILE_ATTRIBUTE_NORMAL |
2115  FILE_FLAG_RANDOM_ACCESS,
2116  NULL
2117  );
2118  if (!_isValidHandle(io.hRead)) {
2119  throw Exception("Could not (re)open file \"" + Filename + "\" in read mode");
2120  }
2121  #else
2122  io.hRead = io.hWrite = fopen(Filename.c_str(), "rb");
2123  if (!_isValidHandle(io.hRead))
2124  throw Exception("Could not (re)open file \"" + Filename + "\" in read mode");
2125  #endif
2126  *pResetPos = true;
2127  break;
2128  case stream_mode_read_write:
2129  if (_isValidHandle(io.hRead)) _close(io.hRead);
2130  #if POSIX
2131  io.hRead = io.hWrite = open(Filename.c_str(), O_RDWR | O_NONBLOCK);
2132  if (!_isValidHandle(io.hRead)) {
2133  io.hRead = io.hWrite = open(Filename.c_str(), O_RDONLY | O_NONBLOCK);
2134  String sError = strerror(errno);
2135  throw Exception("Could not open file \"" + Filename + "\" in read+write mode: " + sError);
2136  }
2137  #elif defined(WIN32)
2138  io.hRead = io.hWrite = CreateFile(
2139  Filename.c_str(),
2140  GENERIC_READ | GENERIC_WRITE,
2141  FILE_SHARE_READ,
2142  NULL, OPEN_ALWAYS,
2143  FILE_ATTRIBUTE_NORMAL |
2144  FILE_FLAG_RANDOM_ACCESS,
2145  NULL
2146  );
2147  if (!_isValidHandle(io.hRead)) {
2148  io.hRead = io.hWrite = CreateFile(
2149  Filename.c_str(), GENERIC_READ,
2150  FILE_SHARE_READ | FILE_SHARE_WRITE,
2151  NULL, OPEN_EXISTING,
2152  FILE_ATTRIBUTE_NORMAL |
2153  FILE_FLAG_RANDOM_ACCESS,
2154  NULL
2155  );
2156  throw Exception("Could not (re)open file \"" + Filename + "\" in read+write mode");
2157  }
2158  #else
2159  io.hRead = io.hWrite = fopen(Filename.c_str(), "r+b");
2160  if (!_isValidHandle(io.hRead)) {
2161  io.hRead = io.hWrite = fopen(Filename.c_str(), "rb");
2162  throw Exception("Could not open file \"" + Filename + "\" in read+write mode");
2163  }
2164  #endif
2165  *pResetPos = true;
2166  break;
2167  case stream_mode_closed:
2168  if (_isValidHandle(io.hRead)) _close(io.hRead);
2169  if (_isValidHandle(io.hWrite)) _close(io.hWrite);
2170  io.hRead = io.hWrite = RIFF_INVALID_FILE_HANDLE;
2171  break;
2172  default:
2173  throw Exception("Unknown file access mode");
2174  }
2175  io.Mode = NewMode;
2176  return true;
2177  }
2178  return false;
2179  }
2180 
2191  #if WORDS_BIGENDIAN
2192  bEndianNative = Endian != endian_little;
2193  #else
2194  bEndianNative = Endian != endian_big;
2195  #endif
2196  }
2197 
2208  void File::Save(progress_t* pProgress) {
2209  //TODO: implementation for the case where first chunk is not a global container (List chunk) is not implemented yet (i.e. Korg files)
2210  if (Layout == layout_flat)
2211  throw Exception("Saving a RIFF file with layout_flat is not implemented yet");
2212 
2213  const bool bNeedLoadSubChunks = !pSubChunks;
2214 
2215  // make sure the RIFF tree is built (from the original file)
2216  if (pProgress) {
2217  // divide progress into subprogress
2218  progress_t subprogress;
2219  __divide_progress(pProgress, &subprogress, 1.f, 0.f, 0.2f); // assume 20% of time
2220  // do the actual work
2221  LoadSubChunksRecursively(&subprogress);
2222  // notify subprogress done
2223  __notify_progress(&subprogress, 1.f);
2224  } else
2225  LoadSubChunksRecursively(NULL);
2226 
2227  // reopen file in write mode
2228  SetMode(stream_mode_read_write);
2229 
2230  // get the current file size as it is now still physically stored on disk
2231  const file_offset_t workingFileSize = GetCurrentFileSize();
2232 
2233  // get the overall file size required to save this file
2234  const file_offset_t newFileSize = GetRequiredFileSize(FileOffsetPreference);
2235 
2236  // determine whether this file will yield in a large file (>=4GB) and
2237  // the RIFF file offset size to be used accordingly for all chunks
2238  FileOffsetSize = FileOffsetSizeFor(newFileSize);
2239 
2240  const HandlePair io = FileHandlePair();
2241  const Handle hRead = io.hRead;
2242  const Handle hWrite = io.hWrite;
2243 
2244  // to be able to save the whole file without loading everything into
2245  // RAM and without having to store the data in a temporary file, we
2246  // enlarge the file with the overall positive file size change,
2247  // then move current data towards the end of the file by the calculated
2248  // positive file size difference and finally update / rewrite the file
2249  // by copying the old data back to the right position at the beginning
2250  // of the file
2251 
2252  // if there are positive size changes...
2253  file_offset_t positiveSizeDiff = 0;
2254  if (newFileSize > workingFileSize) {
2255  positiveSizeDiff = newFileSize - workingFileSize;
2256 
2257  // divide progress into subprogress
2258  progress_t subprogress;
2259  if (pProgress)
2260  __divide_progress(pProgress, &subprogress, 1.f,
2261  bNeedLoadSubChunks ? 0.2f : 0.f,
2262  bNeedLoadSubChunks ? 0.7f : 0.6f);
2263 
2264  // ... we enlarge this file first ...
2265  ResizeFile(newFileSize);
2266 
2267  // ... and move current data by the same amount towards end of file.
2268  int8_t* pCopyBuffer = new int8_t[4096];
2269  #if defined(WIN32)
2270  DWORD iBytesMoved = 1; // we have to pass it via pointer to the Windows API, thus the correct size must be ensured
2271  #else
2272  ssize_t iBytesMoved = 1;
2273  #endif
2274  for (file_offset_t ullPos = workingFileSize, iNotif = 0; iBytesMoved > 0; ++iNotif) {
2275  iBytesMoved = (ullPos < 4096) ? ullPos : 4096;
2276  ullPos -= iBytesMoved;
2277  #if POSIX
2278  lseek(hRead, ullPos, SEEK_SET);
2279  iBytesMoved = read(hRead, pCopyBuffer, iBytesMoved);
2280  lseek(hWrite, ullPos + positiveSizeDiff, SEEK_SET);
2281  iBytesMoved = write(hWrite, pCopyBuffer, iBytesMoved);
2282  #elif defined(WIN32)
2283  LARGE_INTEGER liFilePos;
2284  liFilePos.QuadPart = ullPos;
2285  SetFilePointerEx(hRead, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN);
2286  ReadFile(hRead, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
2287  liFilePos.QuadPart = ullPos + positiveSizeDiff;
2288  SetFilePointerEx(hWrite, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN);
2289  WriteFile(hWrite, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
2290  #else
2291  fseeko(hRead, ullPos, SEEK_SET);
2292  iBytesMoved = fread(pCopyBuffer, 1, iBytesMoved, hRead);
2293  fseeko(hWrite, ullPos + positiveSizeDiff, SEEK_SET);
2294  iBytesMoved = fwrite(pCopyBuffer, 1, iBytesMoved, hWrite);
2295  #endif
2296  if (pProgress && !(iNotif % 8) && iBytesMoved > 0)
2297  __notify_progress(&subprogress, float(workingFileSize - ullPos) / float(workingFileSize));
2298  }
2299  delete[] pCopyBuffer;
2300  if (iBytesMoved < 0) throw Exception("Could not modify file while trying to enlarge it");
2301 
2302  if (pProgress)
2303  __notify_progress(&subprogress, 1.f); // notify subprogress done
2304  }
2305 
2306  // rebuild / rewrite complete RIFF tree ...
2307 
2308  // divide progress into subprogress
2309  progress_t subprogress;
2310  _FileProgress fileProgress = { 0, newFileSize };
2311  if (pProgress) {
2312  __divide_progress(pProgress, &subprogress, 1.f,
2313  bNeedLoadSubChunks ? 0.7f : 0.6f, 1.f);
2314  subprogress.__private = &fileProgress;
2315  }
2316  // do the actual work
2317  const file_offset_t finalSize = WriteChunk(0, positiveSizeDiff, pProgress ? &subprogress : NULL);
2318  const file_offset_t finalActualSize = __GetFileSize(hWrite);
2319  // notify subprogress done
2320  if (pProgress)
2321  __notify_progress(&subprogress, 1.f);
2322 
2323  // resize file to the final size
2324  if (finalSize < finalActualSize) ResizeFile(finalSize);
2325 
2326  if (pProgress)
2327  __notify_progress(pProgress, 1.0); // notify done
2328  }
2329 
2345  void File::Save(const String& path, progress_t* pProgress) {
2346  //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
2347 
2348  //TODO: implementation for the case where first chunk is not a global container (List chunk) is not implemented yet (i.e. Korg files)
2349  if (Layout == layout_flat)
2350  throw Exception("Saving a RIFF file with layout_flat is not implemented yet");
2351 
2352  const bool bNeedLoadSubChunks = !pSubChunks;
2353 
2354  // make sure the RIFF tree is built (from the original file)
2355  if (pProgress) {
2356  // divide progress into subprogress
2357  progress_t subprogress;
2358  __divide_progress(pProgress, &subprogress, 1.f, 0.f, 0.3f); // assume 30% of time
2359  // do the actual work
2360  LoadSubChunksRecursively(&subprogress);
2361  // notify subprogress done
2362  __notify_progress(&subprogress, 1.f);
2363  } else
2364  LoadSubChunksRecursively(NULL);
2365 
2366  if (!bIsNewFile) SetMode(stream_mode_read);
2367 
2368  {
2369  std::lock_guard<std::mutex> lock(io.mutex);
2370  HandlePair& io = FileHandlePairUnsafeRef();
2371 
2372  // open the other (new) file for writing and truncate it to zero size
2373  #if POSIX
2374  io.hWrite = open(path.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP);
2375  if (!_isValidHandle(io.hWrite)) {
2376  io.hWrite = io.hRead;
2377  String sError = strerror(errno);
2378  throw Exception("Could not open file \"" + path + "\" for writing: " + sError);
2379  }
2380  #elif defined(WIN32)
2381  io.hWrite = CreateFile(
2382  path.c_str(), GENERIC_WRITE, FILE_SHARE_READ,
2383  NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL |
2384  FILE_FLAG_RANDOM_ACCESS, NULL
2385  );
2386  if (!_isValidHandle(io.hWrite)) {
2387  io.hWrite = io.hRead;
2388  throw Exception("Could not open file \"" + path + "\" for writing");
2389  }
2390  #else
2391  io.hWrite = fopen(path.c_str(), "w+b");
2392  if (!_isValidHandle(io.hWrite)) {
2393  io.hWrite = io.hRead;
2394  throw Exception("Could not open file \"" + path + "\" for writing");
2395  }
2396  #endif // POSIX
2397  io.Mode = stream_mode_read_write;
2398  }
2399 
2400  // get the overall file size required to save this file
2401  const file_offset_t newFileSize = GetRequiredFileSize(FileOffsetPreference);
2402 
2403  // determine whether this file will yield in a large file (>=4GB) and
2404  // the RIFF file offset size to be used accordingly for all chunks
2405  FileOffsetSize = FileOffsetSizeFor(newFileSize);
2406 
2407  // write complete RIFF tree to the other (new) file
2408  file_offset_t ullTotalSize;
2409  if (pProgress) {
2410  // divide progress into subprogress
2411  progress_t subprogress;
2412  __divide_progress(pProgress, &subprogress, 1.f,
2413  bNeedLoadSubChunks ? 0.3f : 0.f, 1.f);
2414  _FileProgress fileProgress = { 0, newFileSize };
2415  subprogress.__private = &fileProgress;
2416  // do the actual work
2417  ullTotalSize = WriteChunk(0, 0, &subprogress);
2418  // notify subprogress done
2419  __notify_progress(&subprogress, 1.f);
2420  } else
2421  ullTotalSize = WriteChunk(0, 0, NULL);
2422 
2423  const file_offset_t ullActualSize = __GetFileSize(FileWriteHandle());
2424 
2425  // resize file to the final size (if the file was originally larger)
2426  if (ullActualSize > ullTotalSize) ResizeFile(ullTotalSize);
2427 
2428  {
2429  std::lock_guard<std::mutex> lock(io.mutex);
2430  HandlePair& io = FileHandlePairUnsafeRef();
2431 
2432  if (_isValidHandle(io.hWrite)) _close(io.hWrite);
2433  io.hWrite = io.hRead;
2434 
2435  // associate new file with this File object from now on
2436  Filename = path;
2437  bIsNewFile = false;
2438  io.Mode = (stream_mode_t) -1; // Just set it to an undefined mode ...
2439  }
2440  SetMode(stream_mode_read_write); // ... so SetMode() has to reopen the file handles.
2441 
2442  if (pProgress)
2443  __notify_progress(pProgress, 1.0); // notify done
2444  }
2445 
2446  void File::ResizeFile(file_offset_t ullNewSize) {
2447  const Handle hWrite = FileWriteHandle();
2448  #if POSIX
2449  if (ftruncate(hWrite, ullNewSize) < 0)
2450  throw Exception("Could not resize file \"" + Filename + "\"");
2451  #elif defined(WIN32)
2452  LARGE_INTEGER liFilePos;
2453  liFilePos.QuadPart = ullNewSize;
2454  if (
2455  !SetFilePointerEx(hWrite, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN) ||
2456  !SetEndOfFile(hWrite)
2457  ) throw Exception("Could not resize file \"" + Filename + "\"");
2458  #else
2459  # error Sorry, this version of libgig only supports POSIX and Windows systems yet.
2460  # error Reason: portable implementation of RIFF::File::ResizeFile() is missing (yet)!
2461  #endif
2462  }
2463 
2464  File::~File() {
2465  #if DEBUG_RIFF
2466  std::cout << "File::~File()" << std::endl;
2467  #endif // DEBUG_RIFF
2468  Cleanup();
2469  }
2470 
2475  bool File::IsNew() const {
2476  return bIsNewFile;
2477  }
2478 
2479  void File::Cleanup() {
2480  if (IsIOPerThread()) {
2481  for (auto it = io.byThread.begin(); it != io.byThread.end(); ++it) {
2482  _close(it->second.hRead);
2483  }
2484  } else {
2485  _close(io.hRead);
2486  }
2487  DeleteChunkList();
2488  pFile = NULL;
2489  }
2490 
2498  file_offset_t size = 0;
2499  const Handle hRead = FileHandle();
2500  try {
2501  size = __GetFileSize(hRead);
2502  } catch (...) {
2503  size = 0;
2504  }
2505  return size;
2506  }
2507 
2528  return GetRequiredFileSize(FileOffsetPreference);
2529  }
2530 
2543  switch (fileOffsetSize) {
2544  case offset_size_auto: {
2546  if (fileSize >> 32)
2548  else
2549  return fileSize;
2550  }
2551  case offset_size_32bit: break;
2552  case offset_size_64bit: break;
2553  default: throw Exception("Internal error: Invalid RIFF::offset_size_t");
2554  }
2556  }
2557 
2558  int File::FileOffsetSizeFor(file_offset_t fileSize) const {
2559  switch (FileOffsetPreference) {
2560  case offset_size_auto:
2561  return (fileSize >> 32) ? 8 : 4;
2562  case offset_size_32bit:
2563  return 4;
2564  case offset_size_64bit:
2565  return 8;
2566  default:
2567  throw Exception("Internal error: Invalid RIFF::offset_size_t");
2568  }
2569  }
2570 
2592  return FileOffsetSize;
2593  }
2594 
2606  return FileOffsetSizeFor(GetCurrentFileSize());
2607  }
2608 
2635  bool File::IsIOPerThread() const {
2636  //NOTE: Not caring about atomicity here at all, for three reasons:
2637  // 1. SetIOPerThread() is assumed to be called only once for the entire
2638  // life time of a RIFF::File, usually very early at its lifetime, and
2639  // hence a change to isPerThread should already safely be propagated
2640  // before any other thread would actually read this boolean flag.
2641  // 2. This method is called very frequently, and therefore any
2642  // synchronization techique would hurt runtime efficiency.
2643  // 3. Using even a mutex lock here might easily cause a deadlock due to
2644  // other locks been taken in this .cpp file, i.e. at a higher call
2645  // stack level (and this is the main reason why I removed it here).
2646  return io.isPerThread;
2647  }
2648 
2664  void File::SetIOPerThread(bool enable) {
2665  std::lock_guard<std::mutex> lock(io.mutex);
2666  if (!io.byThread.empty() == enable) return;
2667  io.isPerThread = enable;
2668  if (enable) {
2669  const std::thread::id tid = std::this_thread::get_id();
2670  io.byThread[tid] = io;
2671  } else {
2672  // retain an arbitrary handle pair, close all other handle pairs
2673  for (auto it = io.byThread.begin(); it != io.byThread.end(); ++it) {
2674  if (it == io.byThread.begin()) {
2675  io.hRead = it->second.hRead;
2676  io.hWrite = it->second.hWrite;
2677  } else {
2678  _close(it->second.hRead);
2679  _close(it->second.hWrite);
2680  }
2681  }
2682  io.byThread.clear();
2683  }
2684  }
2685 
2694  size_t File::totalDataChunkCount() const {
2695  return nDataChunkCount;
2696  }
2697 
2706  size_t File::totalListChunkCount() const {
2707  return nListChunkCount;
2708  }
2709 
2722  size_t File::totalChunkCount() const {
2723  return nDataChunkCount + nListChunkCount;
2724  }
2725 
2726  #if POSIX
2727  file_offset_t File::__GetFileSize(int hFile) const {
2728  struct stat filestat;
2729  if (fstat(hFile, &filestat) == -1)
2730  throw Exception("POSIX FS error: could not determine file size");
2731  return filestat.st_size;
2732  }
2733  #elif defined(WIN32)
2734  file_offset_t File::__GetFileSize(HANDLE hFile) const {
2735  LARGE_INTEGER size;
2736  if (!GetFileSizeEx(hFile, &size))
2737  throw Exception("Windows FS error: could not determine file size");
2738  return size.QuadPart;
2739  }
2740  #else // standard C functions
2741  file_offset_t File::__GetFileSize(FILE* hFile) const {
2742  off_t curpos = ftello(hFile);
2743  if (fseeko(hFile, 0, SEEK_END) == -1)
2744  throw Exception("FS error: could not determine file size");
2745  off_t size = ftello(hFile);
2746  fseeko(hFile, curpos, SEEK_SET);
2747  return size;
2748  }
2749  #endif
2750 
2751 
2752 // *************** Exception ***************
2753 // *
2754 
2755  Exception::Exception() {
2756  }
2757 
2758  Exception::Exception(String format, ...) {
2759  va_list arg;
2760  va_start(arg, format);
2761  Message = assemble(format, arg);
2762  va_end(arg);
2763  }
2764 
2765  Exception::Exception(String format, va_list arg) {
2766  Message = assemble(format, arg);
2767  }
2768 
2769  Exception::~Exception() {
2770  }
2771 
2772  void Exception::PrintMessage() const {
2773  std::cout << "RIFF::Exception: " << Message << std::endl;
2774  }
2775 
2776  String Exception::assemble(String format, va_list arg) {
2777  char* buf = NULL;
2778  vasprintf(&buf, format.c_str(), arg);
2779  String s = buf;
2780  free(buf);
2781  return s;
2782  }
2783 
2784 
2785 // *************** functions ***************
2786 // *
2787 
2793  String libraryName() {
2794  return PACKAGE;
2795  }
2796 
2801  String libraryVersion() {
2802  return VERSION;
2803  }
2804 
2805 } // 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:717
virtual void __resetPos()
Sets Chunk's read/write position to zero.
Definition: RIFF.cpp:1186
void Resize(file_offset_t NewSize)
Resize chunk.
Definition: RIFF.cpp:1058
int8_t ReadInt8()
Reads one 8 Bit signed integer word and increments the position within the chunk.
Definition: RIFF.cpp:864
uint32_t ReadUint32()
Reads one 32 Bit unsigned integer word and increments the position within the chunk.
Definition: RIFF.cpp:953
stream_state_t GetState() const
Returns the current state of the chunk object.
Definition: RIFF.cpp:434
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:365
file_offset_t GetFilePos() const
Current, actual offset in file of current chunk data body read/write position.
Definition: RIFF.cpp:348
void ReleaseChunkData()
Free loaded chunk body from RAM.
Definition: RIFF.cpp:1033
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:541
file_offset_t RemainingBytes() const
Returns the number of bytes left to read in the chunk body.
Definition: RIFF.cpp:400
int16_t ReadInt16()
Reads one 16 Bit signed integer word and increments the position within the chunk.
Definition: RIFF.cpp:899
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:1081
file_offset_t GetPos() const
Current read/write position within the chunk data body (starting with 0).
Definition: RIFF.cpp:335
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:795
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:599
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:415
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:465
uint32_t GetChunkID() const
Chunk ID in unsigned integer representation.
Definition: RIFF.h:190
void * LoadChunkData()
Load chunk body into RAM.
Definition: RIFF.cpp:984
String GetChunkIDString() const
Returns the String representation of the chunk's ID (e.g.
Definition: RIFF.cpp:312
uint8_t ReadUint8()
Reads one 8 Bit unsigned integer word and increments the position within the chunk.
Definition: RIFF.cpp:881
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:639
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:756
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:852
void ReadString(String &s, int size)
Reads a null-padded string of size characters and copies it into the string s.
Definition: RIFF.cpp:830
uint16_t ReadUint16()
Reads one 16 Bit unsigned integer word and increments the position within the chunk.
Definition: RIFF.cpp:917
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:935
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:678
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:2055
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:2024
bool SetMode(stream_mode_t NewMode)
Change file access mode.
Definition: RIFF.cpp:2085
int GetRequiredFileOffsetSize()
Returns the required size (in bytes) of file offsets stored in the headers of all chunks of this file...
Definition: RIFF.cpp:2605
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:2591
File(uint32_t FileType)
Create new RIFF file.
Definition: RIFF.cpp:1832
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:2497
void SetByteOrder(endian_t Endian)
Set the byte order to be used when saving.
Definition: RIFF.cpp:2190
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:2475
file_offset_t GetRequiredFileSize()
Returns the required size (in bytes) for this RIFF File to be saved to disk.
Definition: RIFF.cpp:2527
size_t totalDataChunkCount() const
File's total amount of ordinary (data) chunks.
Definition: RIFF.cpp:2694
Handle FileHandle() const
Returns the OS dependent file I/O read handle intended to be used by the calling thread.
Definition: RIFF.cpp:2045
bool IsIOPerThread() const
Whether file streams are independent for each thread.
Definition: RIFF.cpp:2635
size_t totalChunkCount() const
File's total amount of data chunks and list chunks.
Definition: RIFF.cpp:2722
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:2065
size_t totalListChunkCount() const
File's total amount of list chunks.
Definition: RIFF.cpp:2706
virtual void Save(progress_t *pProgress=NULL)
Save changes to same file.
Definition: RIFF.cpp:2208
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:2664
RIFF List Chunk.
Definition: RIFF.h:262
size_t CountSubLists()
Returns number of sublists within the list.
Definition: RIFF.cpp:1449
Chunk * GetSubChunk(uint32_t ChunkID)
Returns subchunk with chunk ID ChunkID within this chunk list.
Definition: RIFF.cpp:1273
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:1625
void MoveSubChunk(Chunk *pSrc, Chunk *pDst)
Moves a sub chunk witin this list.
Definition: RIFF.cpp:1511
List * GetFirstSubList()
Returns the first sublist within the list (that is a subchunk with chunk ID "LIST").
Definition: RIFF.cpp:1381
List * AddSubList(uint32_t uiListType)
Creates a new list sub chunk.
Definition: RIFF.cpp:1568
Chunk * GetFirstSubChunk()
Returns the first subchunk within the list (which may be an ordinary chunk as well as a list chunk).
Definition: RIFF.cpp:1342
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:1767
void DeleteSubChunk(Chunk *pSubChunk)
Removes a sub chunk.
Definition: RIFF.cpp:1591
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:1291
size_t CountSubChunks()
Returns number of subchunks within the list (including list chunks).
Definition: RIFF.cpp:1423
List * GetSubList(uint32_t ListType)
Returns sublist chunk with list type ListType within this chunk list.
Definition: RIFF.cpp:1314
Chunk * GetNextSubChunk()
Returns the next subchunk within the list (which may be an ordinary chunk as well as a list chunk).
Definition: RIFF.cpp:1361
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:1809
List * GetNextSubList()
Returns the next sublist (that is a subchunk with chunk ID "LIST") within the list.
Definition: RIFF.cpp:1405
Chunk * AddSubChunk(uint32_t uiChunkID, file_offset_t ullBodySize)
Creates a new sub chunk.
Definition: RIFF.cpp:1485
virtual void __resetPos()
Sets List Chunk's read/write position to zero and causes all sub chunks to do the same.
Definition: RIFF.cpp:1797
Chunk * GetSubChunkAt(size_t pos)
Returns subchunk at supplied pos position within this chunk list.
Definition: RIFF.cpp:1256
RIFF specific classes and definitions.
Definition: RIFF.h:101
String libraryVersion()
Returns version of this C++ library.
Definition: RIFF.cpp:2801
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:2793
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