/[svn]/libgig/trunk/src/RIFF.cpp
ViewVC logotype

Annotation of /libgig/trunk/src/RIFF.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3478 - (hide annotations) (download)
Thu Feb 21 20:10:08 2019 UTC (5 years, 2 months ago) by schoenebeck
File size: 97625 byte(s)
* Fix: Don't automatically delete RIFF chunks from DLS/gig classes'
  destructors. Added new virtual method DeleteChunks() to those classes
  for this which must be explicitly called instead to remove their RIFF
  chunks.
* Fix: Many methods of DLS/gig classes assumed a RIFF chunk read position
  of zero; which is unsafe per se.
* Added C++11 "override" keyword where appropriate.
* DLS.cpp, DLS.h: Added new abstract interface base class DLS::Storage
  which is derived by the respective classes for implementing (the old)
  UpdateChunks() and the new DeleteChunks() method.
* RIFF.cpp, RIFF.h: Added new method progress_t::subdivide().
* Bumped version (4.1.0.svn13).

1 schoenebeck 2 /***************************************************************************
2     * *
3 schoenebeck 933 * libgig - C++ cross-platform Gigasampler format file access library *
4 schoenebeck 2 * *
5 schoenebeck 3478 * Copyright (C) 2003-2019 by Christian Schoenebeck *
6 schoenebeck 384 * <cuse@users.sourceforge.net> *
7 schoenebeck 2 * *
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 schoenebeck 780
24 persson 1713 #include <algorithm>
25 schoenebeck 1953 #include <set>
26 schoenebeck 780 #include <string.h>
27    
28 schoenebeck 2 #include "RIFF.h"
29    
30 schoenebeck 800 #include "helper.h"
31    
32 schoenebeck 2675 #if POSIX
33     # include <errno.h>
34     #endif
35    
36 schoenebeck 2 namespace RIFF {
37    
38 schoenebeck 1105 // *************** Internal functions **************
39     // *
40    
41     /// Returns a human readable path of the given chunk.
42 schoenebeck 1383 static String __resolveChunkPath(Chunk* pCk) {
43 schoenebeck 1105 String sPath;
44     for (Chunk* pChunk = pCk; pChunk; pChunk = pChunk->GetParent()) {
45     if (pChunk->GetChunkID() == CHUNK_ID_LIST) {
46     List* pList = (List*) pChunk;
47     sPath = "->'" + pList->GetListTypeString() + "'" + sPath;
48     } else {
49     sPath = "->'" + pChunk->GetChunkIDString() + "'" + sPath;
50     }
51     }
52     return sPath;
53     }
54    
55    
56    
57 schoenebeck 2682 // *************** progress_t ***************
58     // *
59    
60     progress_t::progress_t() {
61     callback = NULL;
62     custom = NULL;
63     __range_min = 0.0f;
64     __range_max = 1.0f;
65     }
66    
67 schoenebeck 3478 /**
68     * Divides this progress task into the requested amount of sub-progress
69     * tasks and returns a vector with those subprogress tasks.
70     *
71     * @param iSubtasks - total amount sub tasks this task should be subdivided
72     * @returns subtasks
73     */
74     std::vector<progress_t> progress_t::subdivide(int iSubtasks) {
75     std::vector<progress_t> v;
76     for (int i = 0; i < iSubtasks; ++i) {
77     progress_t p;
78     __divide_progress(this, &p, iSubtasks, i);
79     v.push_back(p);
80     }
81     return v;
82     }
83 schoenebeck 2682
84    
85 schoenebeck 3478
86 schoenebeck 2 // *************** Chunk **************
87     // *
88    
89 schoenebeck 780 Chunk::Chunk(File* pFile) {
90 schoenebeck 3057 #if DEBUG_RIFF
91 schoenebeck 780 std::cout << "Chunk::Chunk(File* pFile)" << std::endl;
92 schoenebeck 3057 #endif // DEBUG_RIFF
93 schoenebeck 2912 ullPos = 0;
94 schoenebeck 2 pParent = NULL;
95     pChunkData = NULL;
96 schoenebeck 2912 ullCurrentChunkSize = 0;
97     ullNewChunkSize = 0;
98     ullChunkDataSize = 0;
99 schoenebeck 798 ChunkID = CHUNK_ID_RIFF;
100 schoenebeck 780 this->pFile = pFile;
101 schoenebeck 2 }
102    
103 schoenebeck 2912 Chunk::Chunk(File* pFile, file_offset_t StartPos, List* Parent) {
104 schoenebeck 3057 #if DEBUG_RIFF
105 schoenebeck 2912 std::cout << "Chunk::Chunk(File*,file_offset_t,List*),StartPos=" << StartPos << std::endl;
106 schoenebeck 3057 #endif // DEBUG_RIFF
107 schoenebeck 780 this->pFile = pFile;
108 schoenebeck 2912 ullStartPos = StartPos + CHUNK_HEADER_SIZE(pFile->FileOffsetSize);
109 schoenebeck 2 pParent = Parent;
110 schoenebeck 2912 ullPos = 0;
111 schoenebeck 2 pChunkData = NULL;
112 schoenebeck 2912 ullCurrentChunkSize = 0;
113     ullNewChunkSize = 0;
114     ullChunkDataSize = 0;
115 schoenebeck 2 ReadHeader(StartPos);
116     }
117    
118 schoenebeck 2912 Chunk::Chunk(File* pFile, List* pParent, uint32_t uiChunkID, file_offset_t ullBodySize) {
119 schoenebeck 780 this->pFile = pFile;
120 schoenebeck 2912 ullStartPos = 0; // arbitrary usually, since it will be updated when we write the chunk
121 schoenebeck 780 this->pParent = pParent;
122 schoenebeck 2912 ullPos = 0;
123 schoenebeck 780 pChunkData = NULL;
124 schoenebeck 1863 ChunkID = uiChunkID;
125 schoenebeck 2912 ullChunkDataSize = 0;
126     ullCurrentChunkSize = 0;
127     ullNewChunkSize = ullBodySize;
128 schoenebeck 780 }
129    
130 schoenebeck 2 Chunk::~Chunk() {
131     if (pChunkData) delete[] pChunkData;
132     }
133    
134 schoenebeck 2912 void Chunk::ReadHeader(file_offset_t filePos) {
135 schoenebeck 3057 #if DEBUG_RIFF
136 schoenebeck 2912 std::cout << "Chunk::Readheader(" << filePos << ") ";
137 schoenebeck 3057 #endif // DEBUG_RIFF
138 schoenebeck 1863 ChunkID = 0;
139 schoenebeck 2912 ullNewChunkSize = ullCurrentChunkSize = 0;
140 schoenebeck 2 #if POSIX
141 schoenebeck 2912 if (lseek(pFile->hFileRead, filePos, SEEK_SET) != -1) {
142 schoenebeck 780 read(pFile->hFileRead, &ChunkID, 4);
143 schoenebeck 2912 read(pFile->hFileRead, &ullCurrentChunkSize, pFile->FileOffsetSize);
144 schoenebeck 1050 #elif defined(WIN32)
145 schoenebeck 2915 LARGE_INTEGER liFilePos;
146     liFilePos.QuadPart = filePos;
147 schoenebeck 2914 if (SetFilePointerEx(pFile->hFileRead, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN)) {
148 schoenebeck 1050 DWORD dwBytesRead;
149     ReadFile(pFile->hFileRead, &ChunkID, 4, &dwBytesRead, NULL);
150 schoenebeck 2912 ReadFile(pFile->hFileRead, &ullCurrentChunkSize, pFile->FileOffsetSize, &dwBytesRead, NULL);
151 schoenebeck 2 #else
152 schoenebeck 2912 if (!fseeko(pFile->hFileRead, filePos, SEEK_SET)) {
153 schoenebeck 780 fread(&ChunkID, 4, 1, pFile->hFileRead);
154 schoenebeck 2912 fread(&ullCurrentChunkSize, pFile->FileOffsetSize, 1, pFile->hFileRead);
155 schoenebeck 2 #endif // POSIX
156     #if WORDS_BIGENDIAN
157     if (ChunkID == CHUNK_ID_RIFF) {
158 schoenebeck 808 pFile->bEndianNative = false;
159 schoenebeck 2 }
160     #else // little endian
161     if (ChunkID == CHUNK_ID_RIFX) {
162 schoenebeck 780 pFile->bEndianNative = false;
163 schoenebeck 2 ChunkID = CHUNK_ID_RIFF;
164     }
165     #endif // WORDS_BIGENDIAN
166 schoenebeck 780 if (!pFile->bEndianNative) {
167 schoenebeck 11 //swapBytes_32(&ChunkID);
168 schoenebeck 2912 if (pFile->FileOffsetSize == 4)
169     swapBytes_32(&ullCurrentChunkSize);
170     else
171     swapBytes_64(&ullCurrentChunkSize);
172 schoenebeck 2 }
173 schoenebeck 3057 #if DEBUG_RIFF
174 schoenebeck 11 std::cout << "ckID=" << convertToString(ChunkID) << " ";
175 schoenebeck 2912 std::cout << "ckSize=" << ullCurrentChunkSize << " ";
176 schoenebeck 1863 std::cout << "bEndianNative=" << pFile->bEndianNative << std::endl;
177 schoenebeck 3057 #endif // DEBUG_RIFF
178 schoenebeck 2912 ullNewChunkSize = ullCurrentChunkSize;
179 schoenebeck 2 }
180     }
181    
182 schoenebeck 2912 void Chunk::WriteHeader(file_offset_t filePos) {
183 schoenebeck 780 uint32_t uiNewChunkID = ChunkID;
184     if (ChunkID == CHUNK_ID_RIFF) {
185     #if WORDS_BIGENDIAN
186     if (pFile->bEndianNative) uiNewChunkID = CHUNK_ID_RIFX;
187     #else // little endian
188     if (!pFile->bEndianNative) uiNewChunkID = CHUNK_ID_RIFX;
189     #endif // WORDS_BIGENDIAN
190     }
191    
192 schoenebeck 2912 uint64_t ullNewChunkSize = this->ullNewChunkSize;
193 schoenebeck 780 if (!pFile->bEndianNative) {
194 schoenebeck 2912 if (pFile->FileOffsetSize == 4)
195     swapBytes_32(&ullNewChunkSize);
196     else
197     swapBytes_64(&ullNewChunkSize);
198 schoenebeck 780 }
199    
200     #if POSIX
201 schoenebeck 2912 if (lseek(pFile->hFileWrite, filePos, SEEK_SET) != -1) {
202 schoenebeck 780 write(pFile->hFileWrite, &uiNewChunkID, 4);
203 schoenebeck 2912 write(pFile->hFileWrite, &ullNewChunkSize, pFile->FileOffsetSize);
204 schoenebeck 780 }
205 schoenebeck 1050 #elif defined(WIN32)
206 schoenebeck 2915 LARGE_INTEGER liFilePos;
207     liFilePos.QuadPart = filePos;
208 schoenebeck 2914 if (SetFilePointerEx(pFile->hFileWrite, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN)) {
209 schoenebeck 1050 DWORD dwBytesWritten;
210     WriteFile(pFile->hFileWrite, &uiNewChunkID, 4, &dwBytesWritten, NULL);
211 schoenebeck 2912 WriteFile(pFile->hFileWrite, &ullNewChunkSize, pFile->FileOffsetSize, &dwBytesWritten, NULL);
212 schoenebeck 1050 }
213 schoenebeck 780 #else
214 schoenebeck 2912 if (!fseeko(pFile->hFileWrite, filePos, SEEK_SET)) {
215 schoenebeck 780 fwrite(&uiNewChunkID, 4, 1, pFile->hFileWrite);
216 schoenebeck 2912 fwrite(&ullNewChunkSize, pFile->FileOffsetSize, 1, pFile->hFileWrite);
217 schoenebeck 780 }
218     #endif // POSIX
219     }
220    
221 schoenebeck 11 /**
222     * Returns the String representation of the chunk's ID (e.g. "RIFF",
223     * "LIST").
224     */
225 schoenebeck 2912 String Chunk::GetChunkIDString() const {
226 schoenebeck 2 return convertToString(ChunkID);
227     }
228    
229 schoenebeck 11 /**
230     * Sets the position within the chunk body, thus within the data portion
231     * of the chunk (in bytes).
232     *
233 schoenebeck 780 * <b>Caution:</b> the position will be reset to zero whenever
234     * File::Save() was called.
235     *
236 schoenebeck 11 * @param Where - position offset (in bytes)
237     * @param Whence - optional: defines to what <i>\a Where</i> relates to,
238     * if omitted \a Where relates to beginning of the chunk
239     * data
240     */
241 schoenebeck 2912 file_offset_t Chunk::SetPos(file_offset_t Where, stream_whence_t Whence) {
242 schoenebeck 3057 #if DEBUG_RIFF
243 schoenebeck 2912 std::cout << "Chunk::SetPos(file_offset_t,stream_whence_t)" << std::endl;
244 schoenebeck 3057 #endif // DEBUG_RIFF
245 schoenebeck 2 switch (Whence) {
246     case stream_curpos:
247 schoenebeck 2912 ullPos += Where;
248 schoenebeck 2 break;
249     case stream_end:
250 schoenebeck 2912 ullPos = ullCurrentChunkSize - 1 - Where;
251 schoenebeck 2 break;
252     case stream_backward:
253 schoenebeck 2912 ullPos -= Where;
254 schoenebeck 2 break;
255     case stream_start: default:
256 schoenebeck 2912 ullPos = Where;
257 schoenebeck 2 break;
258     }
259 schoenebeck 2912 if (ullPos > ullCurrentChunkSize) ullPos = ullCurrentChunkSize;
260     return ullPos;
261 schoenebeck 2 }
262    
263 schoenebeck 11 /**
264     * Returns the number of bytes left to read in the chunk body.
265     * When reading data from the chunk using the Read*() Methods, the
266     * position within the chunk data (that is the chunk body) will be
267     * incremented by the number of read bytes and RemainingBytes() returns
268     * how much data is left to read from the current position to the end
269     * of the chunk data.
270     *
271     * @returns number of bytes left to read
272     */
273 schoenebeck 2912 file_offset_t Chunk::RemainingBytes() const {
274 schoenebeck 3057 #if DEBUG_RIFF
275 schoenebeck 2912 std::cout << "Chunk::Remainingbytes()=" << ullCurrentChunkSize - ullPos << std::endl;
276 schoenebeck 3057 #endif // DEBUG_RIFF
277 schoenebeck 2912 return (ullCurrentChunkSize > ullPos) ? ullCurrentChunkSize - ullPos : 0;
278 schoenebeck 2 }
279    
280 schoenebeck 11 /**
281 schoenebeck 2912 * Returns the actual total size in bytes (including header) of this Chunk
282     * if being stored to a file.
283     *
284     * @param fileOffsetSize - RIFF file offset size (in bytes) assumed when
285     * being saved to a file
286     */
287     file_offset_t Chunk::RequiredPhysicalSize(int fileOffsetSize) {
288     return CHUNK_HEADER_SIZE(fileOffsetSize) + // RIFF chunk header
289     ullNewChunkSize + // chunks's actual data body
290     ullNewChunkSize % 2; // optional pad byte
291     }
292    
293     /**
294 schoenebeck 11 * Returns the current state of the chunk object.
295     * Following values are possible:
296     * - RIFF::stream_ready :
297     * chunk data can be read (this is the usual case)
298     * - RIFF::stream_closed :
299     * the data stream was closed somehow, no more reading possible
300     * - RIFF::stream_end_reached :
301 schoenebeck 1383 * already reached the end of the chunk data, no more reading
302 schoenebeck 11 * possible without SetPos()
303     */
304 schoenebeck 2912 stream_state_t Chunk::GetState() const {
305 schoenebeck 3057 #if DEBUG_RIFF
306 schoenebeck 2912 std::cout << "Chunk::GetState()" << std::endl;
307 schoenebeck 3057 #endif // DEBUG_RIFF
308 schoenebeck 2 #if POSIX
309 schoenebeck 1050 if (pFile->hFileRead == 0) return stream_closed;
310     #elif defined (WIN32)
311     if (pFile->hFileRead == INVALID_HANDLE_VALUE)
312     return stream_closed;
313 schoenebeck 2 #else
314 schoenebeck 780 if (pFile->hFileRead == NULL) return stream_closed;
315 schoenebeck 2 #endif // POSIX
316 schoenebeck 2912 if (ullPos < ullCurrentChunkSize) return stream_ready;
317     else return stream_end_reached;
318 schoenebeck 2 }
319    
320     /**
321     * Reads \a WordCount number of data words with given \a WordSize and
322     * copies it into a buffer pointed by \a pData. The buffer has to be
323     * allocated and be sure to provide the correct \a WordSize, as this
324     * will be important and taken into account for eventual endian
325     * correction (swapping of bytes due to different native byte order of
326     * a system). The position within the chunk will automatically be
327     * incremented.
328     *
329     * @param pData destination buffer
330     * @param WordCount number of data words to read
331     * @param WordSize size of each data word to read
332     * @returns number of successfully read data words or 0 if end
333 schoenebeck 3048 * of file reached or error occurred
334 schoenebeck 2 */
335 schoenebeck 2912 file_offset_t Chunk::Read(void* pData, file_offset_t WordCount, file_offset_t WordSize) {
336 schoenebeck 3057 #if DEBUG_RIFF
337 schoenebeck 2912 std::cout << "Chunk::Read(void*,file_offset_t,file_offset_t)" << std::endl;
338 schoenebeck 3057 #endif // DEBUG_RIFF
339 schoenebeck 2543 //if (ulStartPos == 0) return 0; // is only 0 if this is a new chunk, so nothing to read (yet)
340 schoenebeck 2912 if (ullPos >= ullCurrentChunkSize) return 0;
341     if (ullPos + WordCount * WordSize >= ullCurrentChunkSize) WordCount = (ullCurrentChunkSize - ullPos) / WordSize;
342 schoenebeck 2 #if POSIX
343 schoenebeck 2912 if (lseek(pFile->hFileRead, ullStartPos + ullPos, SEEK_SET) < 0) return 0;
344 schoenebeck 2685 ssize_t readWords = read(pFile->hFileRead, pData, WordCount * WordSize);
345     if (readWords < 1) {
346 schoenebeck 3057 #if DEBUG_RIFF
347 schoenebeck 2685 std::cerr << "POSIX read() failed: " << strerror(errno) << std::endl << std::flush;
348 schoenebeck 3057 #endif // DEBUG_RIFF
349 schoenebeck 2685 return 0;
350     }
351 schoenebeck 2 readWords /= WordSize;
352 schoenebeck 1050 #elif defined(WIN32)
353 schoenebeck 2915 LARGE_INTEGER liFilePos;
354     liFilePos.QuadPart = ullStartPos + ullPos;
355 schoenebeck 2914 if (!SetFilePointerEx(pFile->hFileRead, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN))
356 schoenebeck 2912 return 0;
357 schoenebeck 1050 DWORD readWords;
358 schoenebeck 2912 ReadFile(pFile->hFileRead, 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)
359 schoenebeck 1050 if (readWords < 1) return 0;
360     readWords /= WordSize;
361 schoenebeck 2 #else // standard C functions
362 schoenebeck 2912 if (fseeko(pFile->hFileRead, ullStartPos + ullPos, SEEK_SET)) return 0;
363     file_offset_t readWords = fread(pData, WordSize, WordCount, pFile->hFileRead);
364 schoenebeck 2 #endif // POSIX
365 schoenebeck 780 if (!pFile->bEndianNative && WordSize != 1) {
366 schoenebeck 2 switch (WordSize) {
367     case 2:
368 schoenebeck 2912 for (file_offset_t iWord = 0; iWord < readWords; iWord++)
369 schoenebeck 2 swapBytes_16((uint16_t*) pData + iWord);
370     break;
371     case 4:
372 schoenebeck 2912 for (file_offset_t iWord = 0; iWord < readWords; iWord++)
373 schoenebeck 2 swapBytes_32((uint32_t*) pData + iWord);
374     break;
375 schoenebeck 2912 case 8:
376     for (file_offset_t iWord = 0; iWord < readWords; iWord++)
377     swapBytes_64((uint64_t*) pData + iWord);
378     break;
379 schoenebeck 2 default:
380 schoenebeck 2912 for (file_offset_t iWord = 0; iWord < readWords; iWord++)
381 schoenebeck 2 swapBytes((uint8_t*) pData + iWord * WordSize, WordSize);
382     break;
383     }
384     }
385     SetPos(readWords * WordSize, stream_curpos);
386     return readWords;
387     }
388    
389 schoenebeck 780 /**
390     * Writes \a WordCount number of data words with given \a WordSize from
391     * the buffer pointed by \a pData. Be sure to provide the correct
392     * \a WordSize, as this will be important and taken into account for
393     * eventual endian correction (swapping of bytes due to different
394     * native byte order of a system). The position within the chunk will
395     * automatically be incremented.
396     *
397     * @param pData source buffer (containing the data)
398     * @param WordCount number of data words to write
399     * @param WordSize size of each data word to write
400     * @returns number of successfully written data words
401     * @throws RIFF::Exception if write operation would exceed current
402 schoenebeck 3048 * chunk size or any IO error occurred
403 schoenebeck 780 * @see Resize()
404     */
405 schoenebeck 2912 file_offset_t Chunk::Write(void* pData, file_offset_t WordCount, file_offset_t WordSize) {
406 schoenebeck 798 if (pFile->Mode != stream_mode_read_write)
407     throw Exception("Cannot write data to chunk, file has to be opened in read+write mode first");
408 schoenebeck 2912 if (ullPos >= ullCurrentChunkSize || ullPos + WordCount * WordSize > ullCurrentChunkSize)
409 schoenebeck 780 throw Exception("End of chunk reached while trying to write data");
410     if (!pFile->bEndianNative && WordSize != 1) {
411     switch (WordSize) {
412     case 2:
413 schoenebeck 2912 for (file_offset_t iWord = 0; iWord < WordCount; iWord++)
414 schoenebeck 780 swapBytes_16((uint16_t*) pData + iWord);
415     break;
416     case 4:
417 schoenebeck 2912 for (file_offset_t iWord = 0; iWord < WordCount; iWord++)
418 schoenebeck 780 swapBytes_32((uint32_t*) pData + iWord);
419     break;
420 schoenebeck 2912 case 8:
421     for (file_offset_t iWord = 0; iWord < WordCount; iWord++)
422     swapBytes_64((uint64_t*) pData + iWord);
423     break;
424 schoenebeck 780 default:
425 schoenebeck 2912 for (file_offset_t iWord = 0; iWord < WordCount; iWord++)
426 schoenebeck 780 swapBytes((uint8_t*) pData + iWord * WordSize, WordSize);
427     break;
428     }
429     }
430     #if POSIX
431 schoenebeck 2912 if (lseek(pFile->hFileWrite, ullStartPos + ullPos, SEEK_SET) < 0) {
432     throw Exception("Could not seek to position " + ToString(ullPos) +
433     " in chunk (" + ToString(ullStartPos + ullPos) + " in file)");
434 schoenebeck 780 }
435 schoenebeck 2912 ssize_t writtenWords = write(pFile->hFileWrite, pData, WordCount * WordSize);
436 schoenebeck 780 if (writtenWords < 1) throw Exception("POSIX IO Error while trying to write chunk data");
437     writtenWords /= WordSize;
438 schoenebeck 1050 #elif defined(WIN32)
439 schoenebeck 2915 LARGE_INTEGER liFilePos;
440     liFilePos.QuadPart = ullStartPos + ullPos;
441 schoenebeck 2914 if (!SetFilePointerEx(pFile->hFileWrite, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN)) {
442 schoenebeck 2912 throw Exception("Could not seek to position " + ToString(ullPos) +
443     " in chunk (" + ToString(ullStartPos + ullPos) + " in file)");
444 schoenebeck 1050 }
445     DWORD writtenWords;
446 schoenebeck 2912 WriteFile(pFile->hFileWrite, 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)
447 schoenebeck 1050 if (writtenWords < 1) throw Exception("Windows IO Error while trying to write chunk data");
448     writtenWords /= WordSize;
449 schoenebeck 780 #else // standard C functions
450 schoenebeck 2912 if (fseeko(pFile->hFileWrite, ullStartPos + ullPos, SEEK_SET)) {
451     throw Exception("Could not seek to position " + ToString(ullPos) +
452     " in chunk (" + ToString(ullStartPos + ullPos) + " in file)");
453 schoenebeck 780 }
454 schoenebeck 2912 file_offset_t writtenWords = fwrite(pData, WordSize, WordCount, pFile->hFileWrite);
455 schoenebeck 780 #endif // POSIX
456     SetPos(writtenWords * WordSize, stream_curpos);
457     return writtenWords;
458     }
459    
460 schoenebeck 2 /** Just an internal wrapper for the main <i>Read()</i> method with additional Exception throwing on errors. */
461 schoenebeck 2912 file_offset_t Chunk::ReadSceptical(void* pData, file_offset_t WordCount, file_offset_t WordSize) {
462     file_offset_t readWords = Read(pData, WordCount, WordSize);
463 schoenebeck 2 if (readWords != WordCount) throw RIFF::Exception("End of chunk data reached.");
464     return readWords;
465     }
466    
467     /**
468     * Reads \a WordCount number of 8 Bit signed integer words and copies it
469     * into the buffer pointed by \a pData. The buffer has to be allocated.
470     * The position within the chunk will automatically be incremented.
471     *
472     * @param pData destination buffer
473     * @param WordCount number of 8 Bit signed integers to read
474     * @returns number of read integers
475 schoenebeck 3048 * @throws RIFF::Exception if an error occurred or less than
476 schoenebeck 2 * \a WordCount integers could be read!
477     */
478 schoenebeck 2912 file_offset_t Chunk::ReadInt8(int8_t* pData, file_offset_t WordCount) {
479 schoenebeck 3057 #if DEBUG_RIFF
480 schoenebeck 2912 std::cout << "Chunk::ReadInt8(int8_t*,file_offset_t)" << std::endl;
481 schoenebeck 3057 #endif // DEBUG_RIFF
482 schoenebeck 2 return ReadSceptical(pData, WordCount, 1);
483     }
484    
485     /**
486 schoenebeck 780 * Writes \a WordCount number of 8 Bit signed integer words from the
487     * buffer pointed by \a pData to the chunk's body, directly to the
488     * actual "physical" file. The position within the chunk will
489     * automatically be incremented. Note: you cannot write beyond the
490     * boundaries of the chunk, to append data to the chunk call Resize()
491     * before.
492     *
493     * @param pData source buffer (containing the data)
494     * @param WordCount number of 8 Bit signed integers to write
495     * @returns number of written integers
496 schoenebeck 3048 * @throws RIFF::Exception if an IO error occurred
497 schoenebeck 780 * @see Resize()
498     */
499 schoenebeck 2912 file_offset_t Chunk::WriteInt8(int8_t* pData, file_offset_t WordCount) {
500 schoenebeck 780 return Write(pData, WordCount, 1);
501     }
502    
503     /**
504 schoenebeck 2 * Reads \a WordCount number of 8 Bit unsigned integer words and copies
505     * it into the buffer pointed by \a pData. The buffer has to be
506     * allocated. The position within the chunk will automatically be
507     * incremented.
508     *
509     * @param pData destination buffer
510     * @param WordCount number of 8 Bit unsigned integers to read
511     * @returns number of read integers
512 schoenebeck 3048 * @throws RIFF::Exception if an error occurred or less than
513 schoenebeck 2 * \a WordCount integers could be read!
514     */
515 schoenebeck 2912 file_offset_t Chunk::ReadUint8(uint8_t* pData, file_offset_t WordCount) {
516 schoenebeck 3057 #if DEBUG_RIFF
517 schoenebeck 2912 std::cout << "Chunk::ReadUint8(uint8_t*,file_offset_t)" << std::endl;
518 schoenebeck 3057 #endif // DEBUG_RIFF
519 schoenebeck 2 return ReadSceptical(pData, WordCount, 1);
520     }
521    
522     /**
523 schoenebeck 780 * Writes \a WordCount number of 8 Bit unsigned integer words from the
524     * buffer pointed by \a pData to the chunk's body, directly to the
525     * actual "physical" file. The position within the chunk will
526     * automatically be incremented. Note: you cannot write beyond the
527     * boundaries of the chunk, to append data to the chunk call Resize()
528     * before.
529     *
530     * @param pData source buffer (containing the data)
531     * @param WordCount number of 8 Bit unsigned integers to write
532     * @returns number of written integers
533 schoenebeck 3048 * @throws RIFF::Exception if an IO error occurred
534 schoenebeck 780 * @see Resize()
535     */
536 schoenebeck 2912 file_offset_t Chunk::WriteUint8(uint8_t* pData, file_offset_t WordCount) {
537 schoenebeck 780 return Write(pData, WordCount, 1);
538     }
539    
540     /**
541 schoenebeck 2 * Reads \a WordCount number of 16 Bit signed integer words and copies
542     * it into the buffer pointed by \a pData. The buffer has to be
543     * allocated. Endian correction will automatically be done if needed.
544     * The position within the chunk will automatically be incremented.
545     *
546     * @param pData destination buffer
547     * @param WordCount number of 16 Bit signed integers to read
548     * @returns number of read integers
549 schoenebeck 3048 * @throws RIFF::Exception if an error occurred or less than
550 schoenebeck 2 * \a WordCount integers could be read!
551     */
552 schoenebeck 2912 file_offset_t Chunk::ReadInt16(int16_t* pData, file_offset_t WordCount) {
553 schoenebeck 3057 #if DEBUG_RIFF
554 schoenebeck 2912 std::cout << "Chunk::ReadInt16(int16_t*,file_offset_t)" << std::endl;
555 schoenebeck 3057 #endif // DEBUG_RIFF
556 schoenebeck 2 return ReadSceptical(pData, WordCount, 2);
557     }
558    
559     /**
560 schoenebeck 780 * Writes \a WordCount number of 16 Bit signed integer words from the
561     * buffer pointed by \a pData to the chunk's body, directly to the
562     * actual "physical" file. The position within the chunk will
563     * automatically be incremented. Note: you cannot write beyond the
564     * boundaries of the chunk, to append data to the chunk call Resize()
565     * before.
566     *
567     * @param pData source buffer (containing the data)
568     * @param WordCount number of 16 Bit signed integers to write
569     * @returns number of written integers
570 schoenebeck 3048 * @throws RIFF::Exception if an IO error occurred
571 schoenebeck 780 * @see Resize()
572     */
573 schoenebeck 2912 file_offset_t Chunk::WriteInt16(int16_t* pData, file_offset_t WordCount) {
574 schoenebeck 780 return Write(pData, WordCount, 2);
575     }
576    
577     /**
578 schoenebeck 2 * Reads \a WordCount number of 16 Bit unsigned integer words and copies
579     * it into the buffer pointed by \a pData. The buffer has to be
580     * allocated. Endian correction will automatically be done if needed.
581     * The position within the chunk will automatically be incremented.
582     *
583     * @param pData destination buffer
584     * @param WordCount number of 8 Bit unsigned integers to read
585     * @returns number of read integers
586 schoenebeck 3048 * @throws RIFF::Exception if an error occurred or less than
587 schoenebeck 2 * \a WordCount integers could be read!
588     */
589 schoenebeck 2912 file_offset_t Chunk::ReadUint16(uint16_t* pData, file_offset_t WordCount) {
590 schoenebeck 3057 #if DEBUG_RIFF
591 schoenebeck 2912 std::cout << "Chunk::ReadUint16(uint16_t*,file_offset_t)" << std::endl;
592 schoenebeck 3057 #endif // DEBUG_RIFF
593 schoenebeck 2 return ReadSceptical(pData, WordCount, 2);
594     }
595    
596     /**
597 schoenebeck 780 * Writes \a WordCount number of 16 Bit unsigned integer words from the
598     * buffer pointed by \a pData to the chunk's body, directly to the
599     * actual "physical" file. The position within the chunk will
600     * automatically be incremented. Note: you cannot write beyond the
601     * boundaries of the chunk, to append data to the chunk call Resize()
602     * before.
603     *
604     * @param pData source buffer (containing the data)
605     * @param WordCount number of 16 Bit unsigned integers to write
606     * @returns number of written integers
607 schoenebeck 3048 * @throws RIFF::Exception if an IO error occurred
608 schoenebeck 780 * @see Resize()
609     */
610 schoenebeck 2912 file_offset_t Chunk::WriteUint16(uint16_t* pData, file_offset_t WordCount) {
611 schoenebeck 780 return Write(pData, WordCount, 2);
612     }
613    
614     /**
615 schoenebeck 2 * Reads \a WordCount number of 32 Bit signed integer words and copies
616     * it into the buffer pointed by \a pData. The buffer has to be
617     * allocated. Endian correction will automatically be done if needed.
618     * The position within the chunk will automatically be incremented.
619     *
620     * @param pData destination buffer
621     * @param WordCount number of 32 Bit signed integers to read
622     * @returns number of read integers
623 schoenebeck 3048 * @throws RIFF::Exception if an error occurred or less than
624 schoenebeck 2 * \a WordCount integers could be read!
625     */
626 schoenebeck 2912 file_offset_t Chunk::ReadInt32(int32_t* pData, file_offset_t WordCount) {
627 schoenebeck 3057 #if DEBUG_RIFF
628 schoenebeck 2912 std::cout << "Chunk::ReadInt32(int32_t*,file_offset_t)" << std::endl;
629 schoenebeck 3057 #endif // DEBUG_RIFF
630 schoenebeck 2 return ReadSceptical(pData, WordCount, 4);
631     }
632    
633     /**
634 schoenebeck 780 * Writes \a WordCount number of 32 Bit signed integer words from the
635     * buffer pointed by \a pData to the chunk's body, directly to the
636     * actual "physical" file. The position within the chunk will
637     * automatically be incremented. Note: you cannot write beyond the
638     * boundaries of the chunk, to append data to the chunk call Resize()
639     * before.
640     *
641     * @param pData source buffer (containing the data)
642     * @param WordCount number of 32 Bit signed integers to write
643     * @returns number of written integers
644 schoenebeck 3048 * @throws RIFF::Exception if an IO error occurred
645 schoenebeck 780 * @see Resize()
646     */
647 schoenebeck 2912 file_offset_t Chunk::WriteInt32(int32_t* pData, file_offset_t WordCount) {
648 schoenebeck 780 return Write(pData, WordCount, 4);
649     }
650    
651     /**
652 schoenebeck 2 * Reads \a WordCount number of 32 Bit unsigned integer words and copies
653     * it into the buffer pointed by \a pData. The buffer has to be
654     * allocated. Endian correction will automatically be done if needed.
655     * The position within the chunk will automatically be incremented.
656     *
657     * @param pData destination buffer
658     * @param WordCount number of 32 Bit unsigned integers to read
659     * @returns number of read integers
660 schoenebeck 3048 * @throws RIFF::Exception if an error occurred or less than
661 schoenebeck 2 * \a WordCount integers could be read!
662     */
663 schoenebeck 2912 file_offset_t Chunk::ReadUint32(uint32_t* pData, file_offset_t WordCount) {
664 schoenebeck 3057 #if DEBUG_RIFF
665 schoenebeck 2912 std::cout << "Chunk::ReadUint32(uint32_t*,file_offset_t)" << std::endl;
666 schoenebeck 3057 #endif // DEBUG_RIFF
667 schoenebeck 2 return ReadSceptical(pData, WordCount, 4);
668     }
669    
670     /**
671 persson 2450 * Reads a null-padded string of size characters and copies it
672     * into the string \a s. The position within the chunk will
673     * automatically be incremented.
674     *
675     * @param s destination string
676     * @param size number of characters to read
677 schoenebeck 3048 * @throws RIFF::Exception if an error occurred or less than
678 persson 2450 * \a size characters could be read!
679     */
680     void Chunk::ReadString(String& s, int size) {
681     char* buf = new char[size];
682     ReadSceptical(buf, 1, size);
683     s.assign(buf, std::find(buf, buf + size, '\0'));
684     delete[] buf;
685     }
686    
687     /**
688 schoenebeck 780 * Writes \a WordCount number of 32 Bit unsigned integer words from the
689     * buffer pointed by \a pData to the chunk's body, directly to the
690     * actual "physical" file. The position within the chunk will
691     * automatically be incremented. Note: you cannot write beyond the
692     * boundaries of the chunk, to append data to the chunk call Resize()
693     * before.
694     *
695     * @param pData source buffer (containing the data)
696     * @param WordCount number of 32 Bit unsigned integers to write
697     * @returns number of written integers
698 schoenebeck 3048 * @throws RIFF::Exception if an IO error occurred
699 schoenebeck 780 * @see Resize()
700     */
701 schoenebeck 2912 file_offset_t Chunk::WriteUint32(uint32_t* pData, file_offset_t WordCount) {
702 schoenebeck 780 return Write(pData, WordCount, 4);
703     }
704    
705     /**
706 schoenebeck 2 * Reads one 8 Bit signed integer word and increments the position within
707     * the chunk.
708     *
709     * @returns read integer word
710 schoenebeck 3048 * @throws RIFF::Exception if an error occurred
711 schoenebeck 2 */
712     int8_t Chunk::ReadInt8() {
713 schoenebeck 3057 #if DEBUG_RIFF
714 schoenebeck 2912 std::cout << "Chunk::ReadInt8()" << std::endl;
715 schoenebeck 3057 #endif // DEBUG_RIFF
716 schoenebeck 2 int8_t word;
717     ReadSceptical(&word,1,1);
718     return word;
719     }
720    
721     /**
722     * Reads one 8 Bit unsigned integer word and increments the position
723     * within the chunk.
724     *
725     * @returns read integer word
726 schoenebeck 3048 * @throws RIFF::Exception if an error occurred
727 schoenebeck 2 */
728     uint8_t Chunk::ReadUint8() {
729 schoenebeck 3057 #if DEBUG_RIFF
730 schoenebeck 2912 std::cout << "Chunk::ReadUint8()" << std::endl;
731 schoenebeck 3057 #endif // DEBUG_RIFF
732 schoenebeck 2 uint8_t word;
733     ReadSceptical(&word,1,1);
734     return word;
735     }
736    
737     /**
738     * Reads one 16 Bit signed integer word and increments the position
739     * within the chunk. Endian correction will automatically be done if
740     * needed.
741     *
742     * @returns read integer word
743 schoenebeck 3048 * @throws RIFF::Exception if an error occurred
744 schoenebeck 2 */
745     int16_t Chunk::ReadInt16() {
746 schoenebeck 3057 #if DEBUG_RIFF
747 schoenebeck 2912 std::cout << "Chunk::ReadInt16()" << std::endl;
748 schoenebeck 3057 #endif // DEBUG_RIFF
749 schoenebeck 2 int16_t word;
750     ReadSceptical(&word,1,2);
751     return word;
752     }
753    
754     /**
755     * Reads one 16 Bit unsigned integer word and increments the position
756     * within the chunk. Endian correction will automatically be done if
757     * needed.
758     *
759     * @returns read integer word
760 schoenebeck 3048 * @throws RIFF::Exception if an error occurred
761 schoenebeck 2 */
762     uint16_t Chunk::ReadUint16() {
763 schoenebeck 3057 #if DEBUG_RIFF
764 schoenebeck 2912 std::cout << "Chunk::ReadUint16()" << std::endl;
765 schoenebeck 3057 #endif // DEBUG_RIFF
766 schoenebeck 2 uint16_t word;
767     ReadSceptical(&word,1,2);
768     return word;
769     }
770    
771     /**
772     * Reads one 32 Bit signed integer word and increments the position
773     * within the chunk. Endian correction will automatically be done if
774     * needed.
775     *
776     * @returns read integer word
777 schoenebeck 3048 * @throws RIFF::Exception if an error occurred
778 schoenebeck 2 */
779     int32_t Chunk::ReadInt32() {
780 schoenebeck 3057 #if DEBUG_RIFF
781 schoenebeck 2912 std::cout << "Chunk::ReadInt32()" << std::endl;
782 schoenebeck 3057 #endif // DEBUG_RIFF
783 schoenebeck 2 int32_t word;
784     ReadSceptical(&word,1,4);
785     return word;
786     }
787    
788     /**
789     * Reads one 32 Bit unsigned integer word and increments the position
790     * within the chunk. Endian correction will automatically be done if
791     * needed.
792     *
793     * @returns read integer word
794 schoenebeck 3048 * @throws RIFF::Exception if an error occurred
795 schoenebeck 2 */
796     uint32_t Chunk::ReadUint32() {
797 schoenebeck 3057 #if DEBUG_RIFF
798 schoenebeck 2912 std::cout << "Chunk::ReadUint32()" << std::endl;
799 schoenebeck 3057 #endif // DEBUG_RIFF
800 schoenebeck 2 uint32_t word;
801     ReadSceptical(&word,1,4);
802     return word;
803     }
804    
805 schoenebeck 780 /** @brief Load chunk body into RAM.
806     *
807     * Loads the whole chunk body into memory. You can modify the data in
808     * RAM and save the data by calling File::Save() afterwards.
809     *
810     * <b>Caution:</b> the buffer pointer will be invalidated once
811     * File::Save() was called. You have to call LoadChunkData() again to
812 schoenebeck 800 * get a new, valid pointer whenever File::Save() was called.
813 schoenebeck 780 *
814 schoenebeck 800 * You can call LoadChunkData() again if you previously scheduled to
815     * enlarge this chunk with a Resize() call. In that case the buffer will
816     * be enlarged to the new, scheduled chunk size and you can already
817     * place the new chunk data to the buffer and finally call File::Save()
818     * to enlarge the chunk physically and write the new data in one rush.
819     * This approach is definitely recommended if you have to enlarge and
820     * write new data to a lot of chunks.
821     *
822 schoenebeck 780 * @returns a pointer to the data in RAM on success, NULL otherwise
823 schoenebeck 800 * @throws Exception if data buffer could not be enlarged
824 schoenebeck 780 * @see ReleaseChunkData()
825     */
826 schoenebeck 2 void* Chunk::LoadChunkData() {
827 schoenebeck 2543 if (!pChunkData && pFile->Filename != "" /*&& ulStartPos != 0*/) {
828 schoenebeck 2 #if POSIX
829 schoenebeck 2912 if (lseek(pFile->hFileRead, ullStartPos, SEEK_SET) == -1) return NULL;
830 schoenebeck 1050 #elif defined(WIN32)
831 schoenebeck 2915 LARGE_INTEGER liFilePos;
832     liFilePos.QuadPart = ullStartPos;
833 schoenebeck 2914 if (!SetFilePointerEx(pFile->hFileRead, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN)) return NULL;
834 schoenebeck 800 #else
835 schoenebeck 2912 if (fseeko(pFile->hFileRead, ullStartPos, SEEK_SET)) return NULL;
836 schoenebeck 800 #endif // POSIX
837 schoenebeck 2912 file_offset_t ullBufferSize = (ullCurrentChunkSize > ullNewChunkSize) ? ullCurrentChunkSize : ullNewChunkSize;
838     pChunkData = new uint8_t[ullBufferSize];
839 schoenebeck 2 if (!pChunkData) return NULL;
840 schoenebeck 2912 memset(pChunkData, 0, ullBufferSize);
841 schoenebeck 800 #if POSIX
842 schoenebeck 2912 file_offset_t readWords = read(pFile->hFileRead, pChunkData, GetSize());
843 schoenebeck 1050 #elif defined(WIN32)
844     DWORD readWords;
845 schoenebeck 2912 ReadFile(pFile->hFileRead, pChunkData, GetSize(), &readWords, NULL); //FIXME: won't load chunks larger than 2GB !
846 schoenebeck 2 #else
847 schoenebeck 2912 file_offset_t readWords = fread(pChunkData, 1, GetSize(), pFile->hFileRead);
848 schoenebeck 2 #endif // POSIX
849     if (readWords != GetSize()) {
850     delete[] pChunkData;
851     return (pChunkData = NULL);
852     }
853 schoenebeck 2912 ullChunkDataSize = ullBufferSize;
854     } else if (ullNewChunkSize > ullChunkDataSize) {
855     uint8_t* pNewBuffer = new uint8_t[ullNewChunkSize];
856     if (!pNewBuffer) throw Exception("Could not enlarge chunk data buffer to " + ToString(ullNewChunkSize) + " bytes");
857     memset(pNewBuffer, 0 , ullNewChunkSize);
858     memcpy(pNewBuffer, pChunkData, ullChunkDataSize);
859 schoenebeck 800 delete[] pChunkData;
860 schoenebeck 2912 pChunkData = pNewBuffer;
861     ullChunkDataSize = ullNewChunkSize;
862 schoenebeck 2 }
863     return pChunkData;
864     }
865    
866 schoenebeck 780 /** @brief Free loaded chunk body from RAM.
867     *
868     * Frees loaded chunk body data from memory (RAM). You should call
869     * File::Save() before calling this method if you modified the data to
870     * make the changes persistent.
871     */
872 schoenebeck 2 void Chunk::ReleaseChunkData() {
873     if (pChunkData) {
874     delete[] pChunkData;
875     pChunkData = NULL;
876     }
877     }
878    
879 schoenebeck 780 /** @brief Resize chunk.
880     *
881     * Resizes this chunk's body, that is the actual size of data possible
882     * to be written to this chunk. This call will return immediately and
883     * just schedule the resize operation. You should call File::Save() to
884     * actually perform the resize operation(s) "physically" to the file.
885     * As this can take a while on large files, it is recommended to call
886     * Resize() first on all chunks which have to be resized and finally to
887     * call File::Save() to perform all those resize operations in one rush.
888     *
889     * <b>Caution:</b> You cannot directly write to enlarged chunks before
890     * calling File::Save() as this might exceed the current chunk's body
891 schoenebeck 800 * boundary!
892 schoenebeck 780 *
893 schoenebeck 2912 * @param NewSize - new chunk body size in bytes (must be greater than zero)
894 schoenebeck 2922 * @throws RIFF::Exception if \a NewSize is less than 1 or unrealistic large
895 schoenebeck 780 * @see File::Save()
896     */
897 schoenebeck 2912 void Chunk::Resize(file_offset_t NewSize) {
898     if (NewSize == 0)
899 schoenebeck 1105 throw Exception("There is at least one empty chunk (zero size): " + __resolveChunkPath(this));
900 schoenebeck 2912 if ((NewSize >> 48) != 0)
901     throw Exception("Unrealistic high chunk size detected: " + __resolveChunkPath(this));
902     if (ullNewChunkSize == NewSize) return;
903     ullNewChunkSize = NewSize;
904 schoenebeck 780 }
905 schoenebeck 2
906 schoenebeck 780 /** @brief Write chunk persistently e.g. to disk.
907     *
908     * Stores the chunk persistently to its actual "physical" file.
909     *
910 schoenebeck 2912 * @param ullWritePos - position within the "physical" file where this
911 schoenebeck 780 * chunk should be written to
912 schoenebeck 2912 * @param ullCurrentDataOffset - offset of current (old) data within
913 schoenebeck 780 * the file
914 schoenebeck 2682 * @param pProgress - optional: callback function for progress notification
915 schoenebeck 780 * @returns new write position in the "physical" file, that is
916 schoenebeck 2912 * \a ullWritePos incremented by this chunk's new size
917 schoenebeck 780 * (including its header size of course)
918     */
919 schoenebeck 2912 file_offset_t Chunk::WriteChunk(file_offset_t ullWritePos, file_offset_t ullCurrentDataOffset, progress_t* pProgress) {
920     const file_offset_t ullOriginalPos = ullWritePos;
921     ullWritePos += CHUNK_HEADER_SIZE(pFile->FileOffsetSize);
922 schoenebeck 2
923 schoenebeck 798 if (pFile->Mode != stream_mode_read_write)
924     throw Exception("Cannot write list chunk, file has to be opened in read+write mode");
925    
926 schoenebeck 780 // if the whole chunk body was loaded into RAM
927     if (pChunkData) {
928 schoenebeck 800 // make sure chunk data buffer in RAM is at least as large as the new chunk size
929     LoadChunkData();
930 schoenebeck 780 // write chunk data from RAM persistently to the file
931     #if POSIX
932 schoenebeck 2912 lseek(pFile->hFileWrite, ullWritePos, SEEK_SET);
933     if (write(pFile->hFileWrite, pChunkData, ullNewChunkSize) != ullNewChunkSize) {
934 schoenebeck 780 throw Exception("Writing Chunk data (from RAM) failed");
935     }
936 schoenebeck 1050 #elif defined(WIN32)
937 schoenebeck 2915 LARGE_INTEGER liFilePos;
938     liFilePos.QuadPart = ullWritePos;
939 schoenebeck 2914 SetFilePointerEx(pFile->hFileWrite, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN);
940 schoenebeck 1050 DWORD dwBytesWritten;
941 schoenebeck 2912 WriteFile(pFile->hFileWrite, pChunkData, ullNewChunkSize, &dwBytesWritten, NULL); //FIXME: won't save chunks larger than 2GB !
942     if (dwBytesWritten != ullNewChunkSize) {
943 schoenebeck 1050 throw Exception("Writing Chunk data (from RAM) failed");
944     }
945 schoenebeck 780 #else
946 schoenebeck 2912 fseeko(pFile->hFileWrite, ullWritePos, SEEK_SET);
947     if (fwrite(pChunkData, 1, ullNewChunkSize, pFile->hFileWrite) != ullNewChunkSize) {
948 schoenebeck 780 throw Exception("Writing Chunk data (from RAM) failed");
949     }
950     #endif // POSIX
951     } else {
952     // move chunk data from the end of the file to the appropriate position
953     int8_t* pCopyBuffer = new int8_t[4096];
954 schoenebeck 2912 file_offset_t ullToMove = (ullNewChunkSize < ullCurrentChunkSize) ? ullNewChunkSize : ullCurrentChunkSize;
955 schoenebeck 1050 #if defined(WIN32)
956     DWORD iBytesMoved = 1; // we have to pass it via pointer to the Windows API, thus the correct size must be ensured
957     #else
958 schoenebeck 780 int iBytesMoved = 1;
959 schoenebeck 1050 #endif
960 schoenebeck 2912 for (file_offset_t ullOffset = 0; ullToMove > 0 && iBytesMoved > 0; ullOffset += iBytesMoved, ullToMove -= iBytesMoved) {
961 schoenebeck 3053 iBytesMoved = (ullToMove < 4096) ? int(ullToMove) : 4096;
962 schoenebeck 780 #if POSIX
963 schoenebeck 2912 lseek(pFile->hFileRead, ullStartPos + ullCurrentDataOffset + ullOffset, SEEK_SET);
964 schoenebeck 3053 iBytesMoved = (int) read(pFile->hFileRead, pCopyBuffer, (size_t) iBytesMoved);
965 schoenebeck 2912 lseek(pFile->hFileWrite, ullWritePos + ullOffset, SEEK_SET);
966 schoenebeck 3053 iBytesMoved = (int) write(pFile->hFileWrite, pCopyBuffer, (size_t) iBytesMoved);
967 schoenebeck 1050 #elif defined(WIN32)
968 schoenebeck 2915 LARGE_INTEGER liFilePos;
969     liFilePos.QuadPart = ullStartPos + ullCurrentDataOffset + ullOffset;
970 schoenebeck 2914 SetFilePointerEx(pFile->hFileRead, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN);
971 schoenebeck 1050 ReadFile(pFile->hFileRead, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
972 schoenebeck 2914 liFilePos.QuadPart = ullWritePos + ullOffset;
973     SetFilePointerEx(pFile->hFileWrite, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN);
974 schoenebeck 1050 WriteFile(pFile->hFileWrite, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
975 schoenebeck 780 #else
976 schoenebeck 2912 fseeko(pFile->hFileRead, ullStartPos + ullCurrentDataOffset + ullOffset, SEEK_SET);
977 schoenebeck 780 iBytesMoved = fread(pCopyBuffer, 1, iBytesMoved, pFile->hFileRead);
978 schoenebeck 2912 fseeko(pFile->hFileWrite, ullWritePos + ullOffset, SEEK_SET);
979 schoenebeck 780 iBytesMoved = fwrite(pCopyBuffer, 1, iBytesMoved, pFile->hFileWrite);
980     #endif
981     }
982     delete[] pCopyBuffer;
983     if (iBytesMoved < 0) throw Exception("Writing Chunk data (from file) failed");
984     }
985    
986     // update this chunk's header
987 schoenebeck 2912 ullCurrentChunkSize = ullNewChunkSize;
988     WriteHeader(ullOriginalPos);
989 schoenebeck 780
990 schoenebeck 2682 __notify_progress(pProgress, 1.0); // notify done
991    
992 schoenebeck 780 // update chunk's position pointers
993 schoenebeck 2912 ullStartPos = ullOriginalPos + CHUNK_HEADER_SIZE(pFile->FileOffsetSize);
994     ullPos = 0;
995 schoenebeck 780
996     // add pad byte if needed
997 schoenebeck 2912 if ((ullStartPos + ullNewChunkSize) % 2 != 0) {
998 schoenebeck 798 const char cPadByte = 0;
999 schoenebeck 780 #if POSIX
1000 schoenebeck 2912 lseek(pFile->hFileWrite, ullStartPos + ullNewChunkSize, SEEK_SET);
1001 schoenebeck 780 write(pFile->hFileWrite, &cPadByte, 1);
1002 schoenebeck 1050 #elif defined(WIN32)
1003 schoenebeck 2915 LARGE_INTEGER liFilePos;
1004     liFilePos.QuadPart = ullStartPos + ullNewChunkSize;
1005 schoenebeck 2914 SetFilePointerEx(pFile->hFileWrite, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN);
1006 schoenebeck 1050 DWORD dwBytesWritten;
1007     WriteFile(pFile->hFileWrite, &cPadByte, 1, &dwBytesWritten, NULL);
1008 schoenebeck 780 #else
1009 schoenebeck 2912 fseeko(pFile->hFileWrite, ullStartPos + ullNewChunkSize, SEEK_SET);
1010 schoenebeck 780 fwrite(&cPadByte, 1, 1, pFile->hFileWrite);
1011     #endif
1012 schoenebeck 2912 return ullStartPos + ullNewChunkSize + 1;
1013 schoenebeck 780 }
1014    
1015 schoenebeck 2912 return ullStartPos + ullNewChunkSize;
1016 schoenebeck 780 }
1017    
1018     void Chunk::__resetPos() {
1019 schoenebeck 2912 ullPos = 0;
1020 schoenebeck 780 }
1021    
1022    
1023    
1024 schoenebeck 2 // *************** List ***************
1025     // *
1026    
1027 schoenebeck 780 List::List(File* pFile) : Chunk(pFile) {
1028 schoenebeck 3057 #if DEBUG_RIFF
1029 schoenebeck 2912 std::cout << "List::List(File* pFile)" << std::endl;
1030 schoenebeck 3057 #endif // DEBUG_RIFF
1031 schoenebeck 2 pSubChunks = NULL;
1032     pSubChunksMap = NULL;
1033     }
1034    
1035 schoenebeck 2912 List::List(File* pFile, file_offset_t StartPos, List* Parent)
1036 schoenebeck 780 : Chunk(pFile, StartPos, Parent) {
1037 schoenebeck 3057 #if DEBUG_RIFF
1038 schoenebeck 2912 std::cout << "List::List(File*,file_offset_t,List*)" << std::endl;
1039 schoenebeck 3057 #endif // DEBUG_RIFF
1040 schoenebeck 2 pSubChunks = NULL;
1041     pSubChunksMap = NULL;
1042     ReadHeader(StartPos);
1043 schoenebeck 2912 ullStartPos = StartPos + LIST_HEADER_SIZE(pFile->FileOffsetSize);
1044 schoenebeck 2 }
1045    
1046 schoenebeck 780 List::List(File* pFile, List* pParent, uint32_t uiListID)
1047     : Chunk(pFile, pParent, CHUNK_ID_LIST, 0) {
1048     pSubChunks = NULL;
1049     pSubChunksMap = NULL;
1050     ListType = uiListID;
1051     }
1052    
1053 schoenebeck 2 List::~List() {
1054 schoenebeck 3057 #if DEBUG_RIFF
1055 schoenebeck 2912 std::cout << "List::~List()" << std::endl;
1056 schoenebeck 3057 #endif // DEBUG_RIFF
1057 persson 1869 DeleteChunkList();
1058     }
1059    
1060     void List::DeleteChunkList() {
1061 schoenebeck 2 if (pSubChunks) {
1062     ChunkList::iterator iter = pSubChunks->begin();
1063     ChunkList::iterator end = pSubChunks->end();
1064     while (iter != end) {
1065     delete *iter;
1066     iter++;
1067     }
1068     delete pSubChunks;
1069 persson 1869 pSubChunks = NULL;
1070 schoenebeck 2 }
1071 persson 1869 if (pSubChunksMap) {
1072     delete pSubChunksMap;
1073     pSubChunksMap = NULL;
1074     }
1075 schoenebeck 2 }
1076    
1077 schoenebeck 11 /**
1078     * Returns subchunk with chunk ID <i>\a ChunkID</i> within this chunk
1079     * list. Use this method if you expect only one subchunk of that type in
1080     * the list. It there are more than one, it's undetermined which one of
1081     * them will be returned! If there are no subchunks with that desired
1082     * chunk ID, NULL will be returned.
1083     *
1084     * @param ChunkID - chunk ID of the sought subchunk
1085     * @returns pointer to the subchunk or NULL if there is none of
1086     * that ID
1087     */
1088 schoenebeck 2 Chunk* List::GetSubChunk(uint32_t ChunkID) {
1089 schoenebeck 3057 #if DEBUG_RIFF
1090 schoenebeck 2912 std::cout << "List::GetSubChunk(uint32_t)" << std::endl;
1091 schoenebeck 3057 #endif // DEBUG_RIFF
1092 schoenebeck 2 if (!pSubChunksMap) LoadSubChunks();
1093     return (*pSubChunksMap)[ChunkID];
1094     }
1095    
1096 schoenebeck 11 /**
1097     * Returns sublist chunk with list type <i>\a ListType</i> within this
1098     * chunk list. Use this method if you expect only one sublist chunk of
1099 schoenebeck 2922 * that type in the list. If there are more than one, it's undetermined
1100 schoenebeck 11 * which one of them will be returned! If there are no sublists with
1101     * that desired list type, NULL will be returned.
1102     *
1103     * @param ListType - list type of the sought sublist
1104     * @returns pointer to the sublist or NULL if there is none of
1105     * that type
1106     */
1107 schoenebeck 2 List* List::GetSubList(uint32_t ListType) {
1108 schoenebeck 3057 #if DEBUG_RIFF
1109 schoenebeck 2 std::cout << "List::GetSubList(uint32_t)" << std::endl;
1110 schoenebeck 3057 #endif // DEBUG_RIFF
1111 schoenebeck 2 if (!pSubChunks) LoadSubChunks();
1112     ChunkList::iterator iter = pSubChunks->begin();
1113     ChunkList::iterator end = pSubChunks->end();
1114     while (iter != end) {
1115     if ((*iter)->GetChunkID() == CHUNK_ID_LIST) {
1116     List* l = (List*) *iter;
1117     if (l->GetListType() == ListType) return l;
1118     }
1119     iter++;
1120     }
1121     return NULL;
1122     }
1123    
1124 schoenebeck 11 /**
1125 schoenebeck 2922 * Returns the first subchunk within the list (which may be an ordinary
1126     * chunk as well as a list chunk). You have to call this
1127 schoenebeck 11 * method before you can call GetNextSubChunk(). Recall it when you want
1128     * to start from the beginning of the list again.
1129     *
1130     * @returns pointer to the first subchunk within the list, NULL
1131     * otherwise
1132     */
1133 schoenebeck 2 Chunk* List::GetFirstSubChunk() {
1134 schoenebeck 3057 #if DEBUG_RIFF
1135 schoenebeck 2 std::cout << "List::GetFirstSubChunk()" << std::endl;
1136 schoenebeck 3057 #endif // DEBUG_RIFF
1137 schoenebeck 2 if (!pSubChunks) LoadSubChunks();
1138     ChunksIterator = pSubChunks->begin();
1139     return (ChunksIterator != pSubChunks->end()) ? *ChunksIterator : NULL;
1140     }
1141    
1142 schoenebeck 11 /**
1143 schoenebeck 2922 * Returns the next subchunk within the list (which may be an ordinary
1144     * chunk as well as a list chunk). You have to call
1145 schoenebeck 11 * GetFirstSubChunk() before you can use this method!
1146     *
1147     * @returns pointer to the next subchunk within the list or NULL if
1148     * end of list is reached
1149     */
1150 schoenebeck 2 Chunk* List::GetNextSubChunk() {
1151 schoenebeck 3057 #if DEBUG_RIFF
1152 schoenebeck 2 std::cout << "List::GetNextSubChunk()" << std::endl;
1153 schoenebeck 3057 #endif // DEBUG_RIFF
1154 schoenebeck 2 if (!pSubChunks) return NULL;
1155     ChunksIterator++;
1156     return (ChunksIterator != pSubChunks->end()) ? *ChunksIterator : NULL;
1157     }
1158    
1159 schoenebeck 11 /**
1160     * Returns the first sublist within the list (that is a subchunk with
1161     * chunk ID "LIST"). You have to call this method before you can call
1162     * GetNextSubList(). Recall it when you want to start from the beginning
1163     * of the list again.
1164     *
1165     * @returns pointer to the first sublist within the list, NULL
1166     * otherwise
1167     */
1168 schoenebeck 2 List* List::GetFirstSubList() {
1169 schoenebeck 3057 #if DEBUG_RIFF
1170 schoenebeck 2 std::cout << "List::GetFirstSubList()" << std::endl;
1171 schoenebeck 3057 #endif // DEBUG_RIFF
1172 schoenebeck 2 if (!pSubChunks) LoadSubChunks();
1173     ListIterator = pSubChunks->begin();
1174     ChunkList::iterator end = pSubChunks->end();
1175     while (ListIterator != end) {
1176     if ((*ListIterator)->GetChunkID() == CHUNK_ID_LIST) return (List*) *ListIterator;
1177     ListIterator++;
1178     }
1179     return NULL;
1180     }
1181    
1182 schoenebeck 11 /**
1183     * Returns the next sublist (that is a subchunk with chunk ID "LIST")
1184     * within the list. You have to call GetFirstSubList() before you can
1185     * use this method!
1186     *
1187     * @returns pointer to the next sublist within the list, NULL if
1188     * end of list is reached
1189     */
1190 schoenebeck 2 List* List::GetNextSubList() {
1191 schoenebeck 3057 #if DEBUG_RIFF
1192 schoenebeck 2 std::cout << "List::GetNextSubList()" << std::endl;
1193 schoenebeck 3057 #endif // DEBUG_RIFF
1194 schoenebeck 2 if (!pSubChunks) return NULL;
1195     if (ListIterator == pSubChunks->end()) return NULL;
1196     ListIterator++;
1197     ChunkList::iterator end = pSubChunks->end();
1198     while (ListIterator != end) {
1199     if ((*ListIterator)->GetChunkID() == CHUNK_ID_LIST) return (List*) *ListIterator;
1200     ListIterator++;
1201     }
1202     return NULL;
1203     }
1204    
1205 schoenebeck 11 /**
1206 schoenebeck 2922 * Returns number of subchunks within the list (including list chunks).
1207 schoenebeck 11 */
1208 schoenebeck 2922 size_t List::CountSubChunks() {
1209 schoenebeck 2 if (!pSubChunks) LoadSubChunks();
1210     return pSubChunks->size();
1211     }
1212    
1213 schoenebeck 11 /**
1214     * Returns number of subchunks within the list with chunk ID
1215     * <i>\a ChunkId</i>.
1216     */
1217 schoenebeck 2922 size_t List::CountSubChunks(uint32_t ChunkID) {
1218     size_t result = 0;
1219 schoenebeck 2 if (!pSubChunks) LoadSubChunks();
1220     ChunkList::iterator iter = pSubChunks->begin();
1221     ChunkList::iterator end = pSubChunks->end();
1222     while (iter != end) {
1223     if ((*iter)->GetChunkID() == ChunkID) {
1224     result++;
1225     }
1226     iter++;
1227     }
1228     return result;
1229     }
1230    
1231 schoenebeck 11 /**
1232     * Returns number of sublists within the list.
1233     */
1234 schoenebeck 2922 size_t List::CountSubLists() {
1235 schoenebeck 2 return CountSubChunks(CHUNK_ID_LIST);
1236     }
1237    
1238 schoenebeck 11 /**
1239     * Returns number of sublists within the list with list type
1240     * <i>\a ListType</i>
1241     */
1242 schoenebeck 2922 size_t List::CountSubLists(uint32_t ListType) {
1243     size_t result = 0;
1244 schoenebeck 2 if (!pSubChunks) LoadSubChunks();
1245     ChunkList::iterator iter = pSubChunks->begin();
1246     ChunkList::iterator end = pSubChunks->end();
1247     while (iter != end) {
1248     if ((*iter)->GetChunkID() == CHUNK_ID_LIST) {
1249     List* l = (List*) *iter;
1250     if (l->GetListType() == ListType) result++;
1251     }
1252     iter++;
1253     }
1254     return result;
1255     }
1256    
1257 schoenebeck 780 /** @brief Creates a new sub chunk.
1258     *
1259     * Creates and adds a new sub chunk to this list chunk. Note that the
1260 schoenebeck 2912 * chunk's body size given by \a ullBodySize must be greater than zero.
1261 schoenebeck 780 * You have to call File::Save() to make this change persistent to the
1262     * actual file and <b>before</b> performing any data write operations
1263     * on the new chunk!
1264     *
1265     * @param uiChunkID - chunk ID of the new chunk
1266 schoenebeck 2912 * @param ullBodySize - size of the new chunk's body, that is its actual
1267     * data size (without header)
1268     * @throws RIFF::Exception if \a ullBodySize equals zero
1269 schoenebeck 780 */
1270 schoenebeck 2912 Chunk* List::AddSubChunk(uint32_t uiChunkID, file_offset_t ullBodySize) {
1271     if (ullBodySize == 0) throw Exception("Chunk body size must be at least 1 byte");
1272 schoenebeck 780 if (!pSubChunks) LoadSubChunks();
1273 schoenebeck 798 Chunk* pNewChunk = new Chunk(pFile, this, uiChunkID, 0);
1274 schoenebeck 780 pSubChunks->push_back(pNewChunk);
1275     (*pSubChunksMap)[uiChunkID] = pNewChunk;
1276 schoenebeck 2912 pNewChunk->Resize(ullBodySize);
1277     ullNewChunkSize += CHUNK_HEADER_SIZE(pFile->FileOffsetSize);
1278 schoenebeck 780 return pNewChunk;
1279     }
1280    
1281 schoenebeck 2584 /** @brief Moves a sub chunk witin this list.
1282 persson 1102 *
1283 schoenebeck 2584 * Moves a sub chunk from one position in this list to another
1284 persson 1102 * position in the same list. The pSrc chunk is placed before the
1285     * pDst chunk.
1286     *
1287     * @param pSrc - sub chunk to be moved
1288     * @param pDst - the position to move to. pSrc will be placed
1289     * before pDst. If pDst is 0, pSrc will be placed
1290     * last in list.
1291     */
1292     void List::MoveSubChunk(Chunk* pSrc, Chunk* pDst) {
1293     if (!pSubChunks) LoadSubChunks();
1294     pSubChunks->remove(pSrc);
1295     ChunkList::iterator iter = find(pSubChunks->begin(), pSubChunks->end(), pDst);
1296     pSubChunks->insert(iter, pSrc);
1297     }
1298    
1299 schoenebeck 2584 /** @brief Moves a sub chunk from this list to another list.
1300     *
1301     * Moves a sub chunk from this list list to the end of another
1302     * list.
1303     *
1304     * @param pSrc - sub chunk to be moved
1305     * @param pDst - destination list where the chunk shall be moved to
1306     */
1307     void List::MoveSubChunk(Chunk* pSrc, List* pNewParent) {
1308     if (pNewParent == this || !pNewParent) return;
1309     if (!pSubChunks) LoadSubChunks();
1310     if (!pNewParent->pSubChunks) pNewParent->LoadSubChunks();
1311     pSubChunks->remove(pSrc);
1312     pNewParent->pSubChunks->push_back(pSrc);
1313     // update chunk id map of this List
1314     if ((*pSubChunksMap)[pSrc->GetChunkID()] == pSrc) {
1315     pSubChunksMap->erase(pSrc->GetChunkID());
1316     // try to find another chunk of the same chunk ID
1317     ChunkList::iterator iter = pSubChunks->begin();
1318     ChunkList::iterator end = pSubChunks->end();
1319     for (; iter != end; ++iter) {
1320     if ((*iter)->GetChunkID() == pSrc->GetChunkID()) {
1321     (*pSubChunksMap)[pSrc->GetChunkID()] = *iter;
1322     break; // we're done, stop search
1323     }
1324     }
1325     }
1326     // update chunk id map of other list
1327     if (!(*pNewParent->pSubChunksMap)[pSrc->GetChunkID()])
1328     (*pNewParent->pSubChunksMap)[pSrc->GetChunkID()] = pSrc;
1329     }
1330    
1331 schoenebeck 780 /** @brief Creates a new list sub chunk.
1332     *
1333     * Creates and adds a new list sub chunk to this list chunk. Note that
1334     * you have to add sub chunks / sub list chunks to the new created chunk
1335     * <b>before</b> trying to make this change persisten to the actual
1336     * file with File::Save()!
1337     *
1338     * @param uiListType - list ID of the new list chunk
1339     */
1340     List* List::AddSubList(uint32_t uiListType) {
1341     if (!pSubChunks) LoadSubChunks();
1342     List* pNewListChunk = new List(pFile, this, uiListType);
1343     pSubChunks->push_back(pNewListChunk);
1344     (*pSubChunksMap)[CHUNK_ID_LIST] = pNewListChunk;
1345 schoenebeck 2912 ullNewChunkSize += LIST_HEADER_SIZE(pFile->FileOffsetSize);
1346 schoenebeck 780 return pNewListChunk;
1347     }
1348    
1349     /** @brief Removes a sub chunk.
1350     *
1351     * Removes the sub chunk given by \a pSubChunk from this list and frees
1352     * it completely from RAM. The given chunk can either be a normal sub
1353 schoenebeck 1093 * chunk or a list sub chunk. In case the given chunk is a list chunk,
1354     * all its subchunks (if any) will be removed recursively as well. You
1355     * should call File::Save() to make this change persistent at any time.
1356 schoenebeck 780 *
1357     * @param pSubChunk - sub chunk or sub list chunk to be removed
1358     */
1359     void List::DeleteSubChunk(Chunk* pSubChunk) {
1360     if (!pSubChunks) LoadSubChunks();
1361     pSubChunks->remove(pSubChunk);
1362     if ((*pSubChunksMap)[pSubChunk->GetChunkID()] == pSubChunk) {
1363     pSubChunksMap->erase(pSubChunk->GetChunkID());
1364     // try to find another chunk of the same chunk ID
1365     ChunkList::iterator iter = pSubChunks->begin();
1366     ChunkList::iterator end = pSubChunks->end();
1367     for (; iter != end; ++iter) {
1368     if ((*iter)->GetChunkID() == pSubChunk->GetChunkID()) {
1369     (*pSubChunksMap)[pSubChunk->GetChunkID()] = *iter;
1370     break; // we're done, stop search
1371     }
1372     }
1373     }
1374     delete pSubChunk;
1375     }
1376    
1377 schoenebeck 2912 /**
1378     * Returns the actual total size in bytes (including List chunk header and
1379     * all subchunks) of this List Chunk if being stored to a file.
1380     *
1381     * @param fileOffsetSize - RIFF file offset size (in bytes) assumed when
1382     * being saved to a file
1383     */
1384     file_offset_t List::RequiredPhysicalSize(int fileOffsetSize) {
1385     if (!pSubChunks) LoadSubChunks();
1386     file_offset_t size = LIST_HEADER_SIZE(fileOffsetSize);
1387     ChunkList::iterator iter = pSubChunks->begin();
1388     ChunkList::iterator end = pSubChunks->end();
1389     for (; iter != end; ++iter)
1390     size += (*iter)->RequiredPhysicalSize(fileOffsetSize);
1391     return size;
1392     }
1393    
1394     void List::ReadHeader(file_offset_t filePos) {
1395 schoenebeck 3057 #if DEBUG_RIFF
1396 schoenebeck 2912 std::cout << "List::Readheader(file_offset_t) ";
1397 schoenebeck 3057 #endif // DEBUG_RIFF
1398 schoenebeck 2912 Chunk::ReadHeader(filePos);
1399     if (ullCurrentChunkSize < 4) return;
1400     ullNewChunkSize = ullCurrentChunkSize -= 4;
1401 schoenebeck 2 #if POSIX
1402 schoenebeck 2912 lseek(pFile->hFileRead, filePos + CHUNK_HEADER_SIZE(pFile->FileOffsetSize), SEEK_SET);
1403 schoenebeck 780 read(pFile->hFileRead, &ListType, 4);
1404 schoenebeck 1050 #elif defined(WIN32)
1405 schoenebeck 2915 LARGE_INTEGER liFilePos;
1406     liFilePos.QuadPart = filePos + CHUNK_HEADER_SIZE(pFile->FileOffsetSize);
1407 schoenebeck 2914 SetFilePointerEx(pFile->hFileRead, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN);
1408 schoenebeck 1050 DWORD dwBytesRead;
1409     ReadFile(pFile->hFileRead, &ListType, 4, &dwBytesRead, NULL);
1410 schoenebeck 2 #else
1411 schoenebeck 2912 fseeko(pFile->hFileRead, filePos + CHUNK_HEADER_SIZE(pFile->FileOffsetSize), SEEK_SET);
1412 schoenebeck 780 fread(&ListType, 4, 1, pFile->hFileRead);
1413 schoenebeck 2 #endif // POSIX
1414 schoenebeck 3057 #if DEBUG_RIFF
1415 schoenebeck 2912 std::cout << "listType=" << convertToString(ListType) << std::endl;
1416 schoenebeck 3057 #endif // DEBUG_RIFF
1417 schoenebeck 780 if (!pFile->bEndianNative) {
1418 schoenebeck 11 //swapBytes_32(&ListType);
1419 schoenebeck 2 }
1420     }
1421    
1422 schoenebeck 2912 void List::WriteHeader(file_offset_t filePos) {
1423 schoenebeck 780 // the four list type bytes officially belong the chunk's body in the RIFF format
1424 schoenebeck 2912 ullNewChunkSize += 4;
1425     Chunk::WriteHeader(filePos);
1426     ullNewChunkSize -= 4; // just revert the +4 incrementation
1427 schoenebeck 780 #if POSIX
1428 schoenebeck 2912 lseek(pFile->hFileWrite, filePos + CHUNK_HEADER_SIZE(pFile->FileOffsetSize), SEEK_SET);
1429 schoenebeck 780 write(pFile->hFileWrite, &ListType, 4);
1430 schoenebeck 1050 #elif defined(WIN32)
1431 schoenebeck 2915 LARGE_INTEGER liFilePos;
1432     liFilePos.QuadPart = filePos + CHUNK_HEADER_SIZE(pFile->FileOffsetSize);
1433 schoenebeck 2914 SetFilePointerEx(pFile->hFileWrite, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN);
1434 schoenebeck 1050 DWORD dwBytesWritten;
1435     WriteFile(pFile->hFileWrite, &ListType, 4, &dwBytesWritten, NULL);
1436 schoenebeck 780 #else
1437 schoenebeck 2912 fseeko(pFile->hFileWrite, filePos + CHUNK_HEADER_SIZE(pFile->FileOffsetSize), SEEK_SET);
1438 schoenebeck 780 fwrite(&ListType, 4, 1, pFile->hFileWrite);
1439     #endif // POSIX
1440     }
1441    
1442 schoenebeck 2682 void List::LoadSubChunks(progress_t* pProgress) {
1443 schoenebeck 3057 #if DEBUG_RIFF
1444 schoenebeck 2912 std::cout << "List::LoadSubChunks()";
1445 schoenebeck 3057 #endif // DEBUG_RIFF
1446 schoenebeck 2 if (!pSubChunks) {
1447     pSubChunks = new ChunkList();
1448     pSubChunksMap = new ChunkMap();
1449 persson 1301 #if defined(WIN32)
1450     if (pFile->hFileRead == INVALID_HANDLE_VALUE) return;
1451     #else
1452 schoenebeck 780 if (!pFile->hFileRead) return;
1453 persson 1301 #endif
1454 schoenebeck 2912 file_offset_t ullOriginalPos = GetPos();
1455 schoenebeck 515 SetPos(0); // jump to beginning of list chunk body
1456 schoenebeck 2912 while (RemainingBytes() >= CHUNK_HEADER_SIZE(pFile->FileOffsetSize)) {
1457 schoenebeck 2 Chunk* ck;
1458 schoenebeck 11 uint32_t ckid;
1459     Read(&ckid, 4, 1);
1460 schoenebeck 3057 #if DEBUG_RIFF
1461 schoenebeck 2912 std::cout << " ckid=" << convertToString(ckid) << std::endl;
1462 schoenebeck 3057 #endif // DEBUG_RIFF
1463 schoenebeck 2 if (ckid == CHUNK_ID_LIST) {
1464 schoenebeck 2912 ck = new RIFF::List(pFile, ullStartPos + ullPos - 4, this);
1465     SetPos(ck->GetSize() + LIST_HEADER_SIZE(pFile->FileOffsetSize) - 4, RIFF::stream_curpos);
1466 schoenebeck 2 }
1467     else { // simple chunk
1468 schoenebeck 2912 ck = new RIFF::Chunk(pFile, ullStartPos + ullPos - 4, this);
1469     SetPos(ck->GetSize() + CHUNK_HEADER_SIZE(pFile->FileOffsetSize) - 4, RIFF::stream_curpos);
1470 schoenebeck 2 }
1471     pSubChunks->push_back(ck);
1472     (*pSubChunksMap)[ckid] = ck;
1473     if (GetPos() % 2 != 0) SetPos(1, RIFF::stream_curpos); // jump over pad byte
1474     }
1475 schoenebeck 2912 SetPos(ullOriginalPos); // restore position before this call
1476 schoenebeck 2 }
1477 schoenebeck 2682 __notify_progress(pProgress, 1.0); // notify done
1478 schoenebeck 2 }
1479    
1480 schoenebeck 2682 void List::LoadSubChunksRecursively(progress_t* pProgress) {
1481 schoenebeck 3053 const int n = (int) CountSubLists();
1482 schoenebeck 2682 int i = 0;
1483     for (List* pList = GetFirstSubList(); pList; pList = GetNextSubList(), ++i) {
1484     // divide local progress into subprogress
1485     progress_t subprogress;
1486     __divide_progress(pProgress, &subprogress, n, i);
1487     // do the actual work
1488     pList->LoadSubChunksRecursively(&subprogress);
1489     }
1490     __notify_progress(pProgress, 1.0); // notify done
1491 schoenebeck 833 }
1492    
1493 schoenebeck 780 /** @brief Write list chunk persistently e.g. to disk.
1494     *
1495     * Stores the list chunk persistently to its actual "physical" file. All
1496     * subchunks (including sub list chunks) will be stored recursively as
1497     * well.
1498     *
1499 schoenebeck 2912 * @param ullWritePos - position within the "physical" file where this
1500 schoenebeck 780 * list chunk should be written to
1501 schoenebeck 2912 * @param ullCurrentDataOffset - offset of current (old) data within
1502 schoenebeck 780 * the file
1503 schoenebeck 2682 * @param pProgress - optional: callback function for progress notification
1504 schoenebeck 780 * @returns new write position in the "physical" file, that is
1505 schoenebeck 2912 * \a ullWritePos incremented by this list chunk's new size
1506 schoenebeck 780 * (including its header size of course)
1507     */
1508 schoenebeck 2912 file_offset_t List::WriteChunk(file_offset_t ullWritePos, file_offset_t ullCurrentDataOffset, progress_t* pProgress) {
1509     const file_offset_t ullOriginalPos = ullWritePos;
1510     ullWritePos += LIST_HEADER_SIZE(pFile->FileOffsetSize);
1511 schoenebeck 780
1512 schoenebeck 798 if (pFile->Mode != stream_mode_read_write)
1513     throw Exception("Cannot write list chunk, file has to be opened in read+write mode");
1514    
1515 schoenebeck 780 // write all subchunks (including sub list chunks) recursively
1516     if (pSubChunks) {
1517 schoenebeck 2922 size_t i = 0;
1518     const size_t n = pSubChunks->size();
1519 schoenebeck 2682 for (ChunkList::iterator iter = pSubChunks->begin(), end = pSubChunks->end(); iter != end; ++iter, ++i) {
1520     // divide local progress into subprogress for loading current Instrument
1521     progress_t subprogress;
1522     __divide_progress(pProgress, &subprogress, n, i);
1523     // do the actual work
1524 schoenebeck 2912 ullWritePos = (*iter)->WriteChunk(ullWritePos, ullCurrentDataOffset, &subprogress);
1525 schoenebeck 780 }
1526     }
1527    
1528     // update this list chunk's header
1529 schoenebeck 2912 ullCurrentChunkSize = ullNewChunkSize = ullWritePos - ullOriginalPos - LIST_HEADER_SIZE(pFile->FileOffsetSize);
1530     WriteHeader(ullOriginalPos);
1531 schoenebeck 780
1532 schoenebeck 798 // offset of this list chunk in new written file may have changed
1533 schoenebeck 2912 ullStartPos = ullOriginalPos + LIST_HEADER_SIZE(pFile->FileOffsetSize);
1534 schoenebeck 798
1535 schoenebeck 2682 __notify_progress(pProgress, 1.0); // notify done
1536    
1537 schoenebeck 2912 return ullWritePos;
1538 schoenebeck 780 }
1539    
1540     void List::__resetPos() {
1541     Chunk::__resetPos();
1542     if (pSubChunks) {
1543     for (ChunkList::iterator iter = pSubChunks->begin(), end = pSubChunks->end(); iter != end; ++iter) {
1544     (*iter)->__resetPos();
1545     }
1546     }
1547     }
1548    
1549 schoenebeck 11 /**
1550     * Returns string representation of the lists's id
1551     */
1552 schoenebeck 2912 String List::GetListTypeString() const {
1553 schoenebeck 2 return convertToString(ListType);
1554     }
1555    
1556    
1557    
1558     // *************** File ***************
1559     // *
1560    
1561 schoenebeck 780 /** @brief Create new RIFF file.
1562     *
1563     * Use this constructor if you want to create a new RIFF file completely
1564     * "from scratch". Note: there must be no empty chunks or empty list
1565     * chunks when trying to make the new RIFF file persistent with Save()!
1566     *
1567 persson 1207 * Note: by default, the RIFF file will be saved in native endian
1568     * format; that is, as a RIFF file on little-endian machines and
1569     * as a RIFX file on big-endian. To change this behaviour, call
1570     * SetByteOrder() before calling Save().
1571     *
1572 schoenebeck 798 * @param FileType - four-byte identifier of the RIFF file type
1573 persson 1207 * @see AddSubChunk(), AddSubList(), SetByteOrder()
1574 schoenebeck 780 */
1575 schoenebeck 2543 File::File(uint32_t FileType)
1576 schoenebeck 2912 : List(this), bIsNewFile(true), Layout(layout_standard),
1577     FileOffsetPreference(offset_size_auto)
1578 schoenebeck 2543 {
1579 schoenebeck 1050 #if defined(WIN32)
1580     hFileRead = hFileWrite = INVALID_HANDLE_VALUE;
1581     #else
1582 schoenebeck 780 hFileRead = hFileWrite = 0;
1583 schoenebeck 1050 #endif
1584 schoenebeck 798 Mode = stream_mode_closed;
1585 schoenebeck 780 bEndianNative = true;
1586 schoenebeck 798 ListType = FileType;
1587 schoenebeck 2912 FileOffsetSize = 4;
1588     ullStartPos = RIFF_HEADER_SIZE(FileOffsetSize);
1589 schoenebeck 780 }
1590    
1591     /** @brief Load existing RIFF file.
1592     *
1593     * Loads an existing RIFF file with all its chunks.
1594     *
1595     * @param path - path and file name of the RIFF file to open
1596 schoenebeck 3048 * @throws RIFF::Exception if error occurred while trying to load the
1597 schoenebeck 780 * given RIFF file
1598     */
1599 schoenebeck 2543 File::File(const String& path)
1600 schoenebeck 2912 : List(this), Filename(path), bIsNewFile(false), Layout(layout_standard),
1601     FileOffsetPreference(offset_size_auto)
1602 schoenebeck 2543 {
1603 schoenebeck 3057 #if DEBUG_RIFF
1604 schoenebeck 2912 std::cout << "File::File("<<path<<")" << std::endl;
1605 schoenebeck 3057 #endif // DEBUG_RIFF
1606 schoenebeck 2543 bEndianNative = true;
1607 schoenebeck 2912 FileOffsetSize = 4;
1608 persson 2155 try {
1609 schoenebeck 2543 __openExistingFile(path);
1610 persson 2155 if (ChunkID != CHUNK_ID_RIFF && ChunkID != CHUNK_ID_RIFX) {
1611     throw RIFF::Exception("Not a RIFF file");
1612     }
1613 schoenebeck 2 }
1614 persson 2155 catch (...) {
1615     Cleanup();
1616     throw;
1617 schoenebeck 1050 }
1618 schoenebeck 2 }
1619    
1620 schoenebeck 2543 /** @brief Load existing RIFF-like file.
1621     *
1622     * Loads an existing file, which is not a "real" RIFF file, but similar to
1623     * an ordinary RIFF file.
1624     *
1625     * A "real" RIFF file contains at top level a List chunk either with chunk
1626     * ID "RIFF" or "RIFX". The simple constructor above expects this to be
1627     * case, and if it finds the toplevel List chunk to have another chunk ID
1628     * than one of those two expected ones, it would throw an Exception and
1629     * would refuse to load the file accordingly.
1630     *
1631     * Since there are however a lot of file formats which use the same simple
1632     * principles of the RIFF format, with another toplevel List chunk ID
1633     * though, you can use this alternative constructor here to be able to load
1634     * and handle those files in the same way as you would do with "real" RIFF
1635     * files.
1636     *
1637     * @param path - path and file name of the RIFF-alike file to be opened
1638     * @param FileType - expected toplevel List chunk ID (this is the very
1639     * first chunk found in the file)
1640     * @param Endian - whether the file uses little endian or big endian layout
1641     * @param layout - general file structure type
1642 schoenebeck 2912 * @param fileOffsetSize - (optional) preference how to deal with large files
1643 schoenebeck 3048 * @throws RIFF::Exception if error occurred while trying to load the
1644 schoenebeck 2543 * given RIFF-alike file
1645     */
1646 schoenebeck 2912 File::File(const String& path, uint32_t FileType, endian_t Endian, layout_t layout, offset_size_t fileOffsetSize)
1647     : List(this), Filename(path), bIsNewFile(false), Layout(layout),
1648     FileOffsetPreference(fileOffsetSize)
1649 schoenebeck 2543 {
1650     SetByteOrder(Endian);
1651 schoenebeck 2912 if (fileOffsetSize < offset_size_auto || fileOffsetSize > offset_size_64bit)
1652     throw Exception("Invalid RIFF::offset_size_t");
1653     FileOffsetSize = 4;
1654 schoenebeck 2543 try {
1655     __openExistingFile(path, &FileType);
1656     }
1657     catch (...) {
1658     Cleanup();
1659     throw;
1660     }
1661     }
1662    
1663     /**
1664     * Opens an already existing RIFF file or RIFF-alike file. This method
1665     * shall only be called once (in a File class constructor).
1666     *
1667     * @param path - path and file name of the RIFF file or RIFF-alike file to
1668     * be opened
1669     * @param FileType - (optional) expected chunk ID of first chunk in file
1670 schoenebeck 3048 * @throws RIFF::Exception if error occurred while trying to load the
1671 schoenebeck 2543 * given RIFF file or RIFF-alike file
1672     */
1673     void File::__openExistingFile(const String& path, uint32_t* FileType) {
1674     #if POSIX
1675     hFileRead = hFileWrite = open(path.c_str(), O_RDONLY | O_NONBLOCK);
1676 schoenebeck 2675 if (hFileRead == -1) {
1677 schoenebeck 2543 hFileRead = hFileWrite = 0;
1678 schoenebeck 2675 String sError = strerror(errno);
1679     throw RIFF::Exception("Can't open \"" + path + "\": " + sError);
1680 schoenebeck 2543 }
1681     #elif defined(WIN32)
1682     hFileRead = hFileWrite = CreateFile(
1683     path.c_str(), GENERIC_READ,
1684     FILE_SHARE_READ | FILE_SHARE_WRITE,
1685     NULL, OPEN_EXISTING,
1686     FILE_ATTRIBUTE_NORMAL |
1687     FILE_FLAG_RANDOM_ACCESS, NULL
1688     );
1689     if (hFileRead == INVALID_HANDLE_VALUE) {
1690     hFileRead = hFileWrite = INVALID_HANDLE_VALUE;
1691     throw RIFF::Exception("Can't open \"" + path + "\"");
1692     }
1693     #else
1694     hFileRead = hFileWrite = fopen(path.c_str(), "rb");
1695     if (!hFileRead) throw RIFF::Exception("Can't open \"" + path + "\"");
1696     #endif // POSIX
1697     Mode = stream_mode_read;
1698 schoenebeck 2912
1699     // determine RIFF file offset size to be used (in RIFF chunk headers)
1700     // according to the current file offset preference
1701     FileOffsetSize = FileOffsetSizeFor(GetCurrentFileSize());
1702    
1703 schoenebeck 2543 switch (Layout) {
1704     case layout_standard: // this is a normal RIFF file
1705 schoenebeck 2912 ullStartPos = RIFF_HEADER_SIZE(FileOffsetSize);
1706 schoenebeck 2543 ReadHeader(0);
1707     if (FileType && ChunkID != *FileType)
1708     throw RIFF::Exception("Invalid file container ID");
1709     break;
1710     case layout_flat: // non-standard RIFF-alike file
1711 schoenebeck 2912 ullStartPos = 0;
1712     ullNewChunkSize = ullCurrentChunkSize = GetCurrentFileSize();
1713 schoenebeck 2543 if (FileType) {
1714     uint32_t ckid;
1715     if (Read(&ckid, 4, 1) != 4) {
1716     throw RIFF::Exception("Invalid file header ID (premature end of header)");
1717     } else if (ckid != *FileType) {
1718     String s = " (expected '" + convertToString(*FileType) + "' but got '" + convertToString(ckid) + "')";
1719     throw RIFF::Exception("Invalid file header ID" + s);
1720     }
1721     SetPos(0); // reset to first byte of file
1722     }
1723     LoadSubChunks();
1724     break;
1725     }
1726     }
1727    
1728 schoenebeck 2912 String File::GetFileName() const {
1729 schoenebeck 780 return Filename;
1730     }
1731 schoenebeck 2482
1732     void File::SetFileName(const String& path) {
1733     Filename = path;
1734     }
1735 schoenebeck 780
1736 schoenebeck 2912 stream_mode_t File::GetMode() const {
1737 schoenebeck 780 return Mode;
1738     }
1739    
1740 schoenebeck 2543 layout_t File::GetLayout() const {
1741     return Layout;
1742     }
1743    
1744 schoenebeck 780 /** @brief Change file access mode.
1745     *
1746     * Changes files access mode either to read-only mode or to read/write
1747     * mode.
1748     *
1749     * @param NewMode - new file access mode
1750     * @returns true if mode was changed, false if current mode already
1751     * equals new mode
1752     * @throws RIFF::Exception if new file access mode is unknown
1753     */
1754     bool File::SetMode(stream_mode_t NewMode) {
1755     if (NewMode != Mode) {
1756     switch (NewMode) {
1757     case stream_mode_read:
1758     #if POSIX
1759     if (hFileRead) close(hFileRead);
1760     hFileRead = hFileWrite = open(Filename.c_str(), O_RDONLY | O_NONBLOCK);
1761 schoenebeck 2675 if (hFileRead == -1) {
1762 schoenebeck 780 hFileRead = hFileWrite = 0;
1763 schoenebeck 2675 String sError = strerror(errno);
1764     throw Exception("Could not (re)open file \"" + Filename + "\" in read mode: " + sError);
1765 schoenebeck 780 }
1766 schoenebeck 1050 #elif defined(WIN32)
1767     if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
1768     hFileRead = hFileWrite = CreateFile(
1769     Filename.c_str(), GENERIC_READ,
1770     FILE_SHARE_READ | FILE_SHARE_WRITE,
1771     NULL, OPEN_EXISTING,
1772 schoenebeck 1459 FILE_ATTRIBUTE_NORMAL |
1773     FILE_FLAG_RANDOM_ACCESS,
1774     NULL
1775 schoenebeck 1050 );
1776     if (hFileRead == INVALID_HANDLE_VALUE) {
1777     hFileRead = hFileWrite = INVALID_HANDLE_VALUE;
1778     throw Exception("Could not (re)open file \"" + Filename + "\" in read mode");
1779     }
1780 schoenebeck 780 #else
1781     if (hFileRead) fclose(hFileRead);
1782 schoenebeck 1050 hFileRead = hFileWrite = fopen(Filename.c_str(), "rb");
1783 schoenebeck 780 if (!hFileRead) throw Exception("Could not (re)open file \"" + Filename + "\" in read mode");
1784     #endif
1785     __resetPos(); // reset read/write position of ALL 'Chunk' objects
1786 schoenebeck 798 break;
1787 schoenebeck 780 case stream_mode_read_write:
1788     #if POSIX
1789     if (hFileRead) close(hFileRead);
1790     hFileRead = hFileWrite = open(Filename.c_str(), O_RDWR | O_NONBLOCK);
1791 schoenebeck 2675 if (hFileRead == -1) {
1792 schoenebeck 780 hFileRead = hFileWrite = open(Filename.c_str(), O_RDONLY | O_NONBLOCK);
1793 schoenebeck 2675 String sError = strerror(errno);
1794     throw Exception("Could not open file \"" + Filename + "\" in read+write mode: " + sError);
1795 schoenebeck 780 }
1796 schoenebeck 1050 #elif defined(WIN32)
1797     if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
1798     hFileRead = hFileWrite = CreateFile(
1799     Filename.c_str(),
1800     GENERIC_READ | GENERIC_WRITE,
1801     FILE_SHARE_READ,
1802     NULL, OPEN_ALWAYS,
1803 schoenebeck 1459 FILE_ATTRIBUTE_NORMAL |
1804     FILE_FLAG_RANDOM_ACCESS,
1805     NULL
1806 schoenebeck 1050 );
1807     if (hFileRead == INVALID_HANDLE_VALUE) {
1808     hFileRead = hFileWrite = CreateFile(
1809     Filename.c_str(), GENERIC_READ,
1810     FILE_SHARE_READ | FILE_SHARE_WRITE,
1811     NULL, OPEN_EXISTING,
1812 schoenebeck 1459 FILE_ATTRIBUTE_NORMAL |
1813     FILE_FLAG_RANDOM_ACCESS,
1814     NULL
1815 schoenebeck 1050 );
1816 persson 1301 throw Exception("Could not (re)open file \"" + Filename + "\" in read+write mode");
1817 schoenebeck 1050 }
1818 schoenebeck 780 #else
1819     if (hFileRead) fclose(hFileRead);
1820 schoenebeck 1050 hFileRead = hFileWrite = fopen(Filename.c_str(), "r+b");
1821 schoenebeck 780 if (!hFileRead) {
1822 schoenebeck 1050 hFileRead = hFileWrite = fopen(Filename.c_str(), "rb");
1823 schoenebeck 780 throw Exception("Could not open file \"" + Filename + "\" in read+write mode");
1824     }
1825     #endif
1826     __resetPos(); // reset read/write position of ALL 'Chunk' objects
1827 schoenebeck 798 break;
1828     case stream_mode_closed:
1829     #if POSIX
1830     if (hFileRead) close(hFileRead);
1831     if (hFileWrite) close(hFileWrite);
1832 schoenebeck 3412 hFileRead = hFileWrite = 0;
1833 schoenebeck 1050 #elif defined(WIN32)
1834     if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
1835     if (hFileWrite != INVALID_HANDLE_VALUE) CloseHandle(hFileWrite);
1836 schoenebeck 3412 hFileRead = hFileWrite = INVALID_HANDLE_VALUE;
1837 schoenebeck 798 #else
1838     if (hFileRead) fclose(hFileRead);
1839     if (hFileWrite) fclose(hFileWrite);
1840 schoenebeck 3412 hFileRead = hFileWrite = NULL;
1841 schoenebeck 798 #endif
1842     break;
1843 schoenebeck 780 default:
1844     throw Exception("Unknown file access mode");
1845     }
1846 schoenebeck 798 Mode = NewMode;
1847     return true;
1848 schoenebeck 780 }
1849     return false;
1850     }
1851    
1852 persson 1184 /** @brief Set the byte order to be used when saving.
1853     *
1854     * Set the byte order to be used in the file. A value of
1855     * endian_little will create a RIFF file, endian_big a RIFX file
1856     * and endian_native will create a RIFF file on little-endian
1857     * machines and RIFX on big-endian machines.
1858     *
1859     * @param Endian - endianess to use when file is saved.
1860     */
1861     void File::SetByteOrder(endian_t Endian) {
1862     #if WORDS_BIGENDIAN
1863     bEndianNative = Endian != endian_little;
1864     #else
1865     bEndianNative = Endian != endian_big;
1866     #endif
1867     }
1868    
1869 schoenebeck 780 /** @brief Save changes to same file.
1870     *
1871     * Make all changes of all chunks persistent by writing them to the
1872 schoenebeck 2912 * actual (same) file.
1873 schoenebeck 780 *
1874 schoenebeck 2682 * @param pProgress - optional: callback function for progress notification
1875 schoenebeck 780 * @throws RIFF::Exception if there is an empty chunk or empty list
1876 schoenebeck 3048 * chunk or any kind of IO error occurred
1877 schoenebeck 780 */
1878 schoenebeck 2682 void File::Save(progress_t* pProgress) {
1879 schoenebeck 2543 //TODO: implementation for the case where first chunk is not a global container (List chunk) is not implemented yet (i.e. Korg files)
1880     if (Layout == layout_flat)
1881     throw Exception("Saving a RIFF file with layout_flat is not implemented yet");
1882    
1883 schoenebeck 833 // make sure the RIFF tree is built (from the original file)
1884 schoenebeck 2682 {
1885     // divide progress into subprogress
1886     progress_t subprogress;
1887     __divide_progress(pProgress, &subprogress, 3.f, 0.f); // arbitrarily subdivided into 1/3 of total progress
1888     // do the actual work
1889     LoadSubChunksRecursively(&subprogress);
1890     // notify subprogress done
1891     __notify_progress(&subprogress, 1.f);
1892     }
1893 schoenebeck 833
1894 schoenebeck 780 // reopen file in write mode
1895 schoenebeck 798 SetMode(stream_mode_read_write);
1896 schoenebeck 780
1897 schoenebeck 2912 // get the current file size as it is now still physically stored on disk
1898     const file_offset_t workingFileSize = GetCurrentFileSize();
1899    
1900     // get the overall file size required to save this file
1901     const file_offset_t newFileSize = GetRequiredFileSize(FileOffsetPreference);
1902    
1903     // determine whether this file will yield in a large file (>=4GB) and
1904     // the RIFF file offset size to be used accordingly for all chunks
1905     FileOffsetSize = FileOffsetSizeFor(newFileSize);
1906    
1907 schoenebeck 780 // to be able to save the whole file without loading everything into
1908     // RAM and without having to store the data in a temporary file, we
1909 schoenebeck 2912 // enlarge the file with the overall positive file size change,
1910     // then move current data towards the end of the file by the calculated
1911     // positive file size difference and finally update / rewrite the file
1912     // by copying the old data back to the right position at the beginning
1913     // of the file
1914 schoenebeck 780
1915 schoenebeck 2912 // if there are positive size changes...
1916     file_offset_t positiveSizeDiff = 0;
1917     if (newFileSize > workingFileSize) {
1918     positiveSizeDiff = newFileSize - workingFileSize;
1919 schoenebeck 780
1920 schoenebeck 2682 // divide progress into subprogress
1921     progress_t subprogress;
1922     __divide_progress(pProgress, &subprogress, 3.f, 1.f); // arbitrarily subdivided into 1/3 of total progress
1923    
1924 schoenebeck 780 // ... we enlarge this file first ...
1925 schoenebeck 2912 ResizeFile(newFileSize);
1926    
1927 schoenebeck 780 // ... and move current data by the same amount towards end of file.
1928     int8_t* pCopyBuffer = new int8_t[4096];
1929 schoenebeck 1050 #if defined(WIN32)
1930     DWORD iBytesMoved = 1; // we have to pass it via pointer to the Windows API, thus the correct size must be ensured
1931     #else
1932 schoenebeck 2922 ssize_t iBytesMoved = 1;
1933 schoenebeck 1050 #endif
1934 schoenebeck 2912 for (file_offset_t ullPos = workingFileSize, iNotif = 0; iBytesMoved > 0; ++iNotif) {
1935     iBytesMoved = (ullPos < 4096) ? ullPos : 4096;
1936     ullPos -= iBytesMoved;
1937 schoenebeck 780 #if POSIX
1938 schoenebeck 2912 lseek(hFileRead, ullPos, SEEK_SET);
1939 schoenebeck 780 iBytesMoved = read(hFileRead, pCopyBuffer, iBytesMoved);
1940 schoenebeck 2912 lseek(hFileWrite, ullPos + positiveSizeDiff, SEEK_SET);
1941 schoenebeck 780 iBytesMoved = write(hFileWrite, pCopyBuffer, iBytesMoved);
1942 schoenebeck 1050 #elif defined(WIN32)
1943 schoenebeck 2915 LARGE_INTEGER liFilePos;
1944     liFilePos.QuadPart = ullPos;
1945 schoenebeck 2914 SetFilePointerEx(hFileRead, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN);
1946 schoenebeck 1050 ReadFile(hFileRead, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
1947 schoenebeck 2914 liFilePos.QuadPart = ullPos + positiveSizeDiff;
1948     SetFilePointerEx(hFileWrite, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN);
1949 schoenebeck 1050 WriteFile(hFileWrite, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
1950 schoenebeck 780 #else
1951 schoenebeck 2912 fseeko(hFileRead, ullPos, SEEK_SET);
1952 schoenebeck 780 iBytesMoved = fread(pCopyBuffer, 1, iBytesMoved, hFileRead);
1953 schoenebeck 2912 fseeko(hFileWrite, ullPos + positiveSizeDiff, SEEK_SET);
1954 schoenebeck 780 iBytesMoved = fwrite(pCopyBuffer, 1, iBytesMoved, hFileWrite);
1955     #endif
1956 schoenebeck 2682 if (!(iNotif % 8) && iBytesMoved > 0)
1957 schoenebeck 2912 __notify_progress(&subprogress, float(workingFileSize - ullPos) / float(workingFileSize));
1958 schoenebeck 780 }
1959     delete[] pCopyBuffer;
1960     if (iBytesMoved < 0) throw Exception("Could not modify file while trying to enlarge it");
1961 schoenebeck 2682
1962     __notify_progress(&subprogress, 1.f); // notify subprogress done
1963 schoenebeck 780 }
1964    
1965 schoenebeck 2682 // rebuild / rewrite complete RIFF tree ...
1966    
1967     // divide progress into subprogress
1968     progress_t subprogress;
1969     __divide_progress(pProgress, &subprogress, 3.f, 2.f); // arbitrarily subdivided into 1/3 of total progress
1970     // do the actual work
1971 schoenebeck 2912 const file_offset_t finalSize = WriteChunk(0, positiveSizeDiff, &subprogress);
1972     const file_offset_t finalActualSize = __GetFileSize(hFileWrite);
1973 schoenebeck 2682 // notify subprogress done
1974     __notify_progress(&subprogress, 1.f);
1975 schoenebeck 780
1976     // resize file to the final size
1977 schoenebeck 2912 if (finalSize < finalActualSize) ResizeFile(finalSize);
1978 schoenebeck 780
1979 schoenebeck 2682 __notify_progress(pProgress, 1.0); // notify done
1980 schoenebeck 780 }
1981    
1982     /** @brief Save changes to another file.
1983     *
1984     * Make all changes of all chunks persistent by writing them to another
1985     * file. <b>Caution:</b> this method is optimized for writing to
1986     * <b>another</b> file, do not use it to save the changes to the same
1987 schoenebeck 798 * file! Use File::Save() in that case instead! Ignoring this might
1988     * result in a corrupted file, especially in case chunks were resized!
1989 schoenebeck 780 *
1990     * After calling this method, this File object will be associated with
1991     * the new file (given by \a path) afterwards.
1992     *
1993     * @param path - path and file name where everything should be written to
1994 schoenebeck 2682 * @param pProgress - optional: callback function for progress notification
1995 schoenebeck 780 */
1996 schoenebeck 2682 void File::Save(const String& path, progress_t* pProgress) {
1997 schoenebeck 798 //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
1998    
1999 schoenebeck 2543 //TODO: implementation for the case where first chunk is not a global container (List chunk) is not implemented yet (i.e. Korg files)
2000     if (Layout == layout_flat)
2001     throw Exception("Saving a RIFF file with layout_flat is not implemented yet");
2002    
2003 schoenebeck 833 // make sure the RIFF tree is built (from the original file)
2004 schoenebeck 2682 {
2005     // divide progress into subprogress
2006     progress_t subprogress;
2007     __divide_progress(pProgress, &subprogress, 2.f, 0.f); // arbitrarily subdivided into 1/2 of total progress
2008     // do the actual work
2009     LoadSubChunksRecursively(&subprogress);
2010     // notify subprogress done
2011     __notify_progress(&subprogress, 1.f);
2012     }
2013 schoenebeck 833
2014 schoenebeck 2482 if (!bIsNewFile) SetMode(stream_mode_read);
2015 schoenebeck 780 // open the other (new) file for writing and truncate it to zero size
2016     #if POSIX
2017 schoenebeck 798 hFileWrite = open(path.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP);
2018 schoenebeck 2675 if (hFileWrite == -1) {
2019 schoenebeck 780 hFileWrite = hFileRead;
2020 schoenebeck 2675 String sError = strerror(errno);
2021     throw Exception("Could not open file \"" + path + "\" for writing: " + sError);
2022 schoenebeck 780 }
2023 schoenebeck 1050 #elif defined(WIN32)
2024     hFileWrite = CreateFile(
2025     path.c_str(), GENERIC_WRITE, FILE_SHARE_READ,
2026 schoenebeck 1459 NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL |
2027     FILE_FLAG_RANDOM_ACCESS, NULL
2028 schoenebeck 1050 );
2029     if (hFileWrite == INVALID_HANDLE_VALUE) {
2030     hFileWrite = hFileRead;
2031     throw Exception("Could not open file \"" + path + "\" for writing");
2032     }
2033 schoenebeck 780 #else
2034     hFileWrite = fopen(path.c_str(), "w+b");
2035     if (!hFileWrite) {
2036     hFileWrite = hFileRead;
2037     throw Exception("Could not open file \"" + path + "\" for writing");
2038     }
2039     #endif // POSIX
2040 schoenebeck 798 Mode = stream_mode_read_write;
2041 schoenebeck 780
2042 schoenebeck 2912 // get the overall file size required to save this file
2043     const file_offset_t newFileSize = GetRequiredFileSize(FileOffsetPreference);
2044    
2045     // determine whether this file will yield in a large file (>=4GB) and
2046     // the RIFF file offset size to be used accordingly for all chunks
2047     FileOffsetSize = FileOffsetSizeFor(newFileSize);
2048    
2049 schoenebeck 780 // write complete RIFF tree to the other (new) file
2050 schoenebeck 2912 file_offset_t ullTotalSize;
2051 schoenebeck 2682 {
2052     // divide progress into subprogress
2053     progress_t subprogress;
2054     __divide_progress(pProgress, &subprogress, 2.f, 1.f); // arbitrarily subdivided into 1/2 of total progress
2055     // do the actual work
2056 schoenebeck 2912 ullTotalSize = WriteChunk(0, 0, &subprogress);
2057 schoenebeck 2682 // notify subprogress done
2058     __notify_progress(&subprogress, 1.f);
2059     }
2060 schoenebeck 2912 file_offset_t ullActualSize = __GetFileSize(hFileWrite);
2061 schoenebeck 780
2062 schoenebeck 798 // resize file to the final size (if the file was originally larger)
2063 schoenebeck 2912 if (ullActualSize > ullTotalSize) ResizeFile(ullTotalSize);
2064 schoenebeck 798
2065 persson 1301 #if POSIX
2066     if (hFileWrite) close(hFileWrite);
2067     #elif defined(WIN32)
2068     if (hFileWrite != INVALID_HANDLE_VALUE) CloseHandle(hFileWrite);
2069     #else
2070     if (hFileWrite) fclose(hFileWrite);
2071     #endif
2072     hFileWrite = hFileRead;
2073 schoenebeck 798
2074 schoenebeck 780 // associate new file with this File object from now on
2075     Filename = path;
2076 schoenebeck 2482 bIsNewFile = false;
2077 schoenebeck 798 Mode = (stream_mode_t) -1; // Just set it to an undefined mode ...
2078     SetMode(stream_mode_read_write); // ... so SetMode() has to reopen the file handles.
2079 schoenebeck 2682
2080     __notify_progress(pProgress, 1.0); // notify done
2081 schoenebeck 780 }
2082    
2083 schoenebeck 2912 void File::ResizeFile(file_offset_t ullNewSize) {
2084 schoenebeck 780 #if POSIX
2085 schoenebeck 2912 if (ftruncate(hFileWrite, ullNewSize) < 0)
2086 schoenebeck 780 throw Exception("Could not resize file \"" + Filename + "\"");
2087 schoenebeck 1050 #elif defined(WIN32)
2088 schoenebeck 2915 LARGE_INTEGER liFilePos;
2089     liFilePos.QuadPart = ullNewSize;
2090 schoenebeck 1050 if (
2091 schoenebeck 2914 !SetFilePointerEx(hFileWrite, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN) ||
2092 schoenebeck 1050 !SetEndOfFile(hFileWrite)
2093     ) throw Exception("Could not resize file \"" + Filename + "\"");
2094 schoenebeck 780 #else
2095 schoenebeck 1050 # error Sorry, this version of libgig only supports POSIX and Windows systems yet.
2096 schoenebeck 798 # error Reason: portable implementation of RIFF::File::ResizeFile() is missing (yet)!
2097 schoenebeck 780 #endif
2098     }
2099    
2100 schoenebeck 2 File::~File() {
2101 schoenebeck 3057 #if DEBUG_RIFF
2102 schoenebeck 2912 std::cout << "File::~File()" << std::endl;
2103 schoenebeck 3057 #endif // DEBUG_RIFF
2104 persson 2155 Cleanup();
2105     }
2106 schoenebeck 2912
2107 schoenebeck 2482 /**
2108     * Returns @c true if this file has been created new from scratch and
2109     * has not been stored to disk yet.
2110     */
2111     bool File::IsNew() const {
2112     return bIsNewFile;
2113     }
2114 persson 2155
2115     void File::Cleanup() {
2116 schoenebeck 2 #if POSIX
2117 schoenebeck 780 if (hFileRead) close(hFileRead);
2118 schoenebeck 1050 #elif defined(WIN32)
2119     if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
2120 schoenebeck 2 #else
2121 schoenebeck 780 if (hFileRead) fclose(hFileRead);
2122 schoenebeck 2 #endif // POSIX
2123 persson 1869 DeleteChunkList();
2124     pFile = NULL;
2125 schoenebeck 2 }
2126    
2127 schoenebeck 2912 /**
2128     * Returns the current size of this file (in bytes) as it is currently
2129     * yet stored on disk. If this file does not yet exist on disk (i.e. when
2130     * this RIFF File has just been created from scratch and Save() has not
2131     * been called yet) then this method returns 0.
2132     */
2133     file_offset_t File::GetCurrentFileSize() const {
2134     file_offset_t size = 0;
2135     try {
2136     size = __GetFileSize(hFileRead);
2137     } catch (...) {
2138     size = 0;
2139     }
2140     return size;
2141 schoenebeck 780 }
2142    
2143 schoenebeck 2912 /**
2144     * Returns the required size (in bytes) for this RIFF File to be saved to
2145     * disk. The precise size of the final file on disk depends on the RIFF
2146     * file offset size actually used internally in all headers of the RIFF
2147     * chunks. By default libgig handles the required file offset size
2148     * automatically for you; that means it is using 32 bit offsets for files
2149     * smaller than 4 GB and 64 bit offsets for files equal or larger than
2150     * 4 GB. You may however also override this default behavior by passing the
2151     * respective option to the RIFF File constructor to force one particular
2152     * offset size. In the latter case this method will return the file size
2153     * for the requested forced file offset size that will be used when calling
2154     * Save() later on.
2155     *
2156     * You may also use the overridden method below to get the file size for
2157     * an arbitrary other file offset size instead.
2158     *
2159     * @see offset_size_t
2160     * @see GetFileOffsetSize()
2161     */
2162     file_offset_t File::GetRequiredFileSize() {
2163     return GetRequiredFileSize(FileOffsetPreference);
2164 schoenebeck 1095 }
2165    
2166 schoenebeck 2912 /**
2167     * Returns the rquired size (in bytes) for this RIFF file to be saved to
2168     * disk, assuming the passed @a fileOffsestSize would be used for the
2169     * Save() operation.
2170     *
2171     * This overridden method essentialy behaves like the above method, with
2172     * the difference that you must provide a specific RIFF @a fileOffsetSize
2173     * for calculating the theoretical final file size.
2174     *
2175     * @see GetFileOffsetSize()
2176     */
2177     file_offset_t File::GetRequiredFileSize(offset_size_t fileOffsetSize) {
2178     switch (fileOffsetSize) {
2179     case offset_size_auto: {
2180     file_offset_t fileSize = GetRequiredFileSize(offset_size_32bit);
2181     if (fileSize >> 32)
2182     return GetRequiredFileSize(offset_size_64bit);
2183     else
2184     return fileSize;
2185     }
2186     case offset_size_32bit: break;
2187     case offset_size_64bit: break;
2188     default: throw Exception("Internal error: Invalid RIFF::offset_size_t");
2189     }
2190     return RequiredPhysicalSize(FileOffsetSize);
2191 schoenebeck 798 }
2192    
2193 schoenebeck 2912 int File::FileOffsetSizeFor(file_offset_t fileSize) const {
2194     switch (FileOffsetPreference) {
2195     case offset_size_auto:
2196     return (fileSize >> 32) ? 8 : 4;
2197     case offset_size_32bit:
2198     return 4;
2199     case offset_size_64bit:
2200     return 8;
2201     default:
2202     throw Exception("Internal error: Invalid RIFF::offset_size_t");
2203     }
2204     }
2205    
2206     /**
2207     * Returns the current size (in bytes) of file offsets stored in the
2208     * headers of all chunks of this file.
2209     *
2210     * Most RIFF files are using 32 bit file offsets internally, which limits
2211     * them to a maximum file size of less than 4 GB though. In contrast to the
2212     * common standard, this RIFF File class implementation supports handling of
2213     * RIFF files equal or larger than 4 GB. In such cases 64 bit file offsets
2214     * have to be used in all headers of all RIFF Chunks when being stored to a
2215     * physical file. libgig by default automatically selects the correct file
2216     * offset size for you. You may however also force one particular file
2217     * offset size by supplying the respective option to the RIFF::File
2218     * constructor.
2219     *
2220     * This method can be used to check which RIFF file offset size is currently
2221     * being used for this RIFF File.
2222     *
2223     * @returns current RIFF file offset size used (in bytes)
2224     * @see offset_size_t
2225     */
2226     int File::GetFileOffsetSize() const {
2227     return FileOffsetSize;
2228     }
2229    
2230     /**
2231     * Returns the required size (in bytes) of file offsets stored in the
2232     * headers of all chunks of this file if the current RIFF tree would be
2233     * saved to disk by calling Save().
2234     *
2235     * See GetFileOffsetSize() for mor details about RIFF file offsets.
2236     *
2237     * @returns RIFF file offset size required (in bytes) if being saved
2238     * @see offset_size_t
2239     */
2240     int File::GetRequiredFileOffsetSize() {
2241     return FileOffsetSizeFor(GetCurrentFileSize());
2242     }
2243    
2244 schoenebeck 798 #if POSIX
2245 schoenebeck 2912 file_offset_t File::__GetFileSize(int hFile) const {
2246 schoenebeck 2 struct stat filestat;
2247 schoenebeck 2912 if (fstat(hFile, &filestat) == -1)
2248     throw Exception("POSIX FS error: could not determine file size");
2249     return filestat.st_size;
2250 schoenebeck 2 }
2251 schoenebeck 1050 #elif defined(WIN32)
2252 schoenebeck 2912 file_offset_t File::__GetFileSize(HANDLE hFile) const {
2253 schoenebeck 2914 LARGE_INTEGER size;
2254 schoenebeck 2912 if (!GetFileSizeEx(hFile, &size))
2255 schoenebeck 1063 throw Exception("Windows FS error: could not determine file size");
2256 schoenebeck 2914 return size.QuadPart;
2257 schoenebeck 1050 }
2258 schoenebeck 798 #else // standard C functions
2259 schoenebeck 2912 file_offset_t File::__GetFileSize(FILE* hFile) const {
2260     off_t curpos = ftello(hFile);
2261     if (fseeko(hFile, 0, SEEK_END) == -1)
2262     throw Exception("FS error: could not determine file size");
2263     off_t size = ftello(hFile);
2264     fseeko(hFile, curpos, SEEK_SET);
2265 schoenebeck 798 return size;
2266     }
2267     #endif
2268 schoenebeck 2
2269    
2270     // *************** Exception ***************
2271     // *
2272    
2273 schoenebeck 3198 Exception::Exception() {
2274     }
2275    
2276     Exception::Exception(String format, ...) {
2277     va_list arg;
2278     va_start(arg, format);
2279     Message = assemble(format, arg);
2280     va_end(arg);
2281     }
2282    
2283     Exception::Exception(String format, va_list arg) {
2284     Message = assemble(format, arg);
2285     }
2286    
2287 schoenebeck 2 void Exception::PrintMessage() {
2288     std::cout << "RIFF::Exception: " << Message << std::endl;
2289     }
2290    
2291 schoenebeck 3198 String Exception::assemble(String format, va_list arg) {
2292     char* buf = NULL;
2293     vasprintf(&buf, format.c_str(), arg);
2294     String s = buf;
2295     free(buf);
2296     return s;
2297     }
2298 schoenebeck 518
2299 schoenebeck 3198
2300 schoenebeck 518 // *************** functions ***************
2301     // *
2302    
2303     /**
2304     * Returns the name of this C++ library. This is usually "libgig" of
2305     * course. This call is equivalent to DLS::libraryName() and
2306     * gig::libraryName().
2307     */
2308     String libraryName() {
2309     return PACKAGE;
2310     }
2311    
2312     /**
2313     * Returns version of this C++ library. This call is equivalent to
2314     * DLS::libraryVersion() and gig::libraryVersion().
2315     */
2316     String libraryVersion() {
2317     return VERSION;
2318     }
2319    
2320 schoenebeck 2 } // namespace RIFF

  ViewVC Help
Powered by ViewVC