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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3478 - (show 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 /***************************************************************************
2 * *
3 * libgig - C++ cross-platform Gigasampler format file access library *
4 * *
5 * Copyright (C) 2003-2019 by Christian Schoenebeck *
6 * <cuse@users.sourceforge.net> *
7 * *
8 * This library is free software; you can redistribute it and/or modify *
9 * it under the terms of the GNU General Public License as published by *
10 * the Free Software Foundation; either version 2 of the License, or *
11 * (at your option) any later version. *
12 * *
13 * This library is distributed in the hope that it will be useful, *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16 * GNU General Public License for more details. *
17 * *
18 * You should have received a copy of the GNU General Public License *
19 * along with this library; if not, write to the Free Software *
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, *
21 * MA 02111-1307 USA *
22 ***************************************************************************/
23
24 #include <algorithm>
25 #include <set>
26 #include <string.h>
27
28 #include "RIFF.h"
29
30 #include "helper.h"
31
32 #if POSIX
33 # include <errno.h>
34 #endif
35
36 namespace RIFF {
37
38 // *************** Internal functions **************
39 // *
40
41 /// Returns a human readable path of the given chunk.
42 static String __resolveChunkPath(Chunk* pCk) {
43 String sPath;
44 for (Chunk* pChunk = pCk; pChunk; pChunk = pChunk->GetParent()) {
45 if (pChunk->GetChunkID() == CHUNK_ID_LIST) {
46 List* pList = (List*) pChunk;
47 sPath = "->'" + pList->GetListTypeString() + "'" + sPath;
48 } else {
49 sPath = "->'" + pChunk->GetChunkIDString() + "'" + sPath;
50 }
51 }
52 return sPath;
53 }
54
55
56
57 // *************** 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 /**
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
84
85
86 // *************** Chunk **************
87 // *
88
89 Chunk::Chunk(File* pFile) {
90 #if DEBUG_RIFF
91 std::cout << "Chunk::Chunk(File* pFile)" << std::endl;
92 #endif // DEBUG_RIFF
93 ullPos = 0;
94 pParent = NULL;
95 pChunkData = NULL;
96 ullCurrentChunkSize = 0;
97 ullNewChunkSize = 0;
98 ullChunkDataSize = 0;
99 ChunkID = CHUNK_ID_RIFF;
100 this->pFile = pFile;
101 }
102
103 Chunk::Chunk(File* pFile, file_offset_t StartPos, List* Parent) {
104 #if DEBUG_RIFF
105 std::cout << "Chunk::Chunk(File*,file_offset_t,List*),StartPos=" << StartPos << std::endl;
106 #endif // DEBUG_RIFF
107 this->pFile = pFile;
108 ullStartPos = StartPos + CHUNK_HEADER_SIZE(pFile->FileOffsetSize);
109 pParent = Parent;
110 ullPos = 0;
111 pChunkData = NULL;
112 ullCurrentChunkSize = 0;
113 ullNewChunkSize = 0;
114 ullChunkDataSize = 0;
115 ReadHeader(StartPos);
116 }
117
118 Chunk::Chunk(File* pFile, List* pParent, uint32_t uiChunkID, file_offset_t ullBodySize) {
119 this->pFile = pFile;
120 ullStartPos = 0; // arbitrary usually, since it will be updated when we write the chunk
121 this->pParent = pParent;
122 ullPos = 0;
123 pChunkData = NULL;
124 ChunkID = uiChunkID;
125 ullChunkDataSize = 0;
126 ullCurrentChunkSize = 0;
127 ullNewChunkSize = ullBodySize;
128 }
129
130 Chunk::~Chunk() {
131 if (pChunkData) delete[] pChunkData;
132 }
133
134 void Chunk::ReadHeader(file_offset_t filePos) {
135 #if DEBUG_RIFF
136 std::cout << "Chunk::Readheader(" << filePos << ") ";
137 #endif // DEBUG_RIFF
138 ChunkID = 0;
139 ullNewChunkSize = ullCurrentChunkSize = 0;
140 #if POSIX
141 if (lseek(pFile->hFileRead, filePos, SEEK_SET) != -1) {
142 read(pFile->hFileRead, &ChunkID, 4);
143 read(pFile->hFileRead, &ullCurrentChunkSize, pFile->FileOffsetSize);
144 #elif defined(WIN32)
145 LARGE_INTEGER liFilePos;
146 liFilePos.QuadPart = filePos;
147 if (SetFilePointerEx(pFile->hFileRead, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN)) {
148 DWORD dwBytesRead;
149 ReadFile(pFile->hFileRead, &ChunkID, 4, &dwBytesRead, NULL);
150 ReadFile(pFile->hFileRead, &ullCurrentChunkSize, pFile->FileOffsetSize, &dwBytesRead, NULL);
151 #else
152 if (!fseeko(pFile->hFileRead, filePos, SEEK_SET)) {
153 fread(&ChunkID, 4, 1, pFile->hFileRead);
154 fread(&ullCurrentChunkSize, pFile->FileOffsetSize, 1, pFile->hFileRead);
155 #endif // POSIX
156 #if WORDS_BIGENDIAN
157 if (ChunkID == CHUNK_ID_RIFF) {
158 pFile->bEndianNative = false;
159 }
160 #else // little endian
161 if (ChunkID == CHUNK_ID_RIFX) {
162 pFile->bEndianNative = false;
163 ChunkID = CHUNK_ID_RIFF;
164 }
165 #endif // WORDS_BIGENDIAN
166 if (!pFile->bEndianNative) {
167 //swapBytes_32(&ChunkID);
168 if (pFile->FileOffsetSize == 4)
169 swapBytes_32(&ullCurrentChunkSize);
170 else
171 swapBytes_64(&ullCurrentChunkSize);
172 }
173 #if DEBUG_RIFF
174 std::cout << "ckID=" << convertToString(ChunkID) << " ";
175 std::cout << "ckSize=" << ullCurrentChunkSize << " ";
176 std::cout << "bEndianNative=" << pFile->bEndianNative << std::endl;
177 #endif // DEBUG_RIFF
178 ullNewChunkSize = ullCurrentChunkSize;
179 }
180 }
181
182 void Chunk::WriteHeader(file_offset_t filePos) {
183 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 uint64_t ullNewChunkSize = this->ullNewChunkSize;
193 if (!pFile->bEndianNative) {
194 if (pFile->FileOffsetSize == 4)
195 swapBytes_32(&ullNewChunkSize);
196 else
197 swapBytes_64(&ullNewChunkSize);
198 }
199
200 #if POSIX
201 if (lseek(pFile->hFileWrite, filePos, SEEK_SET) != -1) {
202 write(pFile->hFileWrite, &uiNewChunkID, 4);
203 write(pFile->hFileWrite, &ullNewChunkSize, pFile->FileOffsetSize);
204 }
205 #elif defined(WIN32)
206 LARGE_INTEGER liFilePos;
207 liFilePos.QuadPart = filePos;
208 if (SetFilePointerEx(pFile->hFileWrite, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN)) {
209 DWORD dwBytesWritten;
210 WriteFile(pFile->hFileWrite, &uiNewChunkID, 4, &dwBytesWritten, NULL);
211 WriteFile(pFile->hFileWrite, &ullNewChunkSize, pFile->FileOffsetSize, &dwBytesWritten, NULL);
212 }
213 #else
214 if (!fseeko(pFile->hFileWrite, filePos, SEEK_SET)) {
215 fwrite(&uiNewChunkID, 4, 1, pFile->hFileWrite);
216 fwrite(&ullNewChunkSize, pFile->FileOffsetSize, 1, pFile->hFileWrite);
217 }
218 #endif // POSIX
219 }
220
221 /**
222 * Returns the String representation of the chunk's ID (e.g. "RIFF",
223 * "LIST").
224 */
225 String Chunk::GetChunkIDString() const {
226 return convertToString(ChunkID);
227 }
228
229 /**
230 * Sets the position within the chunk body, thus within the data portion
231 * of the chunk (in bytes).
232 *
233 * <b>Caution:</b> the position will be reset to zero whenever
234 * File::Save() was called.
235 *
236 * @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 file_offset_t Chunk::SetPos(file_offset_t Where, stream_whence_t Whence) {
242 #if DEBUG_RIFF
243 std::cout << "Chunk::SetPos(file_offset_t,stream_whence_t)" << std::endl;
244 #endif // DEBUG_RIFF
245 switch (Whence) {
246 case stream_curpos:
247 ullPos += Where;
248 break;
249 case stream_end:
250 ullPos = ullCurrentChunkSize - 1 - Where;
251 break;
252 case stream_backward:
253 ullPos -= Where;
254 break;
255 case stream_start: default:
256 ullPos = Where;
257 break;
258 }
259 if (ullPos > ullCurrentChunkSize) ullPos = ullCurrentChunkSize;
260 return ullPos;
261 }
262
263 /**
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 file_offset_t Chunk::RemainingBytes() const {
274 #if DEBUG_RIFF
275 std::cout << "Chunk::Remainingbytes()=" << ullCurrentChunkSize - ullPos << std::endl;
276 #endif // DEBUG_RIFF
277 return (ullCurrentChunkSize > ullPos) ? ullCurrentChunkSize - ullPos : 0;
278 }
279
280 /**
281 * 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 * 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 * already reached the end of the chunk data, no more reading
302 * possible without SetPos()
303 */
304 stream_state_t Chunk::GetState() const {
305 #if DEBUG_RIFF
306 std::cout << "Chunk::GetState()" << std::endl;
307 #endif // DEBUG_RIFF
308 #if POSIX
309 if (pFile->hFileRead == 0) return stream_closed;
310 #elif defined (WIN32)
311 if (pFile->hFileRead == INVALID_HANDLE_VALUE)
312 return stream_closed;
313 #else
314 if (pFile->hFileRead == NULL) return stream_closed;
315 #endif // POSIX
316 if (ullPos < ullCurrentChunkSize) return stream_ready;
317 else return stream_end_reached;
318 }
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 * of file reached or error occurred
334 */
335 file_offset_t Chunk::Read(void* pData, file_offset_t WordCount, file_offset_t WordSize) {
336 #if DEBUG_RIFF
337 std::cout << "Chunk::Read(void*,file_offset_t,file_offset_t)" << std::endl;
338 #endif // DEBUG_RIFF
339 //if (ulStartPos == 0) return 0; // is only 0 if this is a new chunk, so nothing to read (yet)
340 if (ullPos >= ullCurrentChunkSize) return 0;
341 if (ullPos + WordCount * WordSize >= ullCurrentChunkSize) WordCount = (ullCurrentChunkSize - ullPos) / WordSize;
342 #if POSIX
343 if (lseek(pFile->hFileRead, ullStartPos + ullPos, SEEK_SET) < 0) return 0;
344 ssize_t readWords = read(pFile->hFileRead, pData, WordCount * WordSize);
345 if (readWords < 1) {
346 #if DEBUG_RIFF
347 std::cerr << "POSIX read() failed: " << strerror(errno) << std::endl << std::flush;
348 #endif // DEBUG_RIFF
349 return 0;
350 }
351 readWords /= WordSize;
352 #elif defined(WIN32)
353 LARGE_INTEGER liFilePos;
354 liFilePos.QuadPart = ullStartPos + ullPos;
355 if (!SetFilePointerEx(pFile->hFileRead, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN))
356 return 0;
357 DWORD readWords;
358 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 if (readWords < 1) return 0;
360 readWords /= WordSize;
361 #else // standard C functions
362 if (fseeko(pFile->hFileRead, ullStartPos + ullPos, SEEK_SET)) return 0;
363 file_offset_t readWords = fread(pData, WordSize, WordCount, pFile->hFileRead);
364 #endif // POSIX
365 if (!pFile->bEndianNative && WordSize != 1) {
366 switch (WordSize) {
367 case 2:
368 for (file_offset_t iWord = 0; iWord < readWords; iWord++)
369 swapBytes_16((uint16_t*) pData + iWord);
370 break;
371 case 4:
372 for (file_offset_t iWord = 0; iWord < readWords; iWord++)
373 swapBytes_32((uint32_t*) pData + iWord);
374 break;
375 case 8:
376 for (file_offset_t iWord = 0; iWord < readWords; iWord++)
377 swapBytes_64((uint64_t*) pData + iWord);
378 break;
379 default:
380 for (file_offset_t iWord = 0; iWord < readWords; iWord++)
381 swapBytes((uint8_t*) pData + iWord * WordSize, WordSize);
382 break;
383 }
384 }
385 SetPos(readWords * WordSize, stream_curpos);
386 return readWords;
387 }
388
389 /**
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 * chunk size or any IO error occurred
403 * @see Resize()
404 */
405 file_offset_t Chunk::Write(void* pData, file_offset_t WordCount, file_offset_t WordSize) {
406 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 if (ullPos >= ullCurrentChunkSize || ullPos + WordCount * WordSize > ullCurrentChunkSize)
409 throw Exception("End of chunk reached while trying to write data");
410 if (!pFile->bEndianNative && WordSize != 1) {
411 switch (WordSize) {
412 case 2:
413 for (file_offset_t iWord = 0; iWord < WordCount; iWord++)
414 swapBytes_16((uint16_t*) pData + iWord);
415 break;
416 case 4:
417 for (file_offset_t iWord = 0; iWord < WordCount; iWord++)
418 swapBytes_32((uint32_t*) pData + iWord);
419 break;
420 case 8:
421 for (file_offset_t iWord = 0; iWord < WordCount; iWord++)
422 swapBytes_64((uint64_t*) pData + iWord);
423 break;
424 default:
425 for (file_offset_t iWord = 0; iWord < WordCount; iWord++)
426 swapBytes((uint8_t*) pData + iWord * WordSize, WordSize);
427 break;
428 }
429 }
430 #if POSIX
431 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 }
435 ssize_t writtenWords = write(pFile->hFileWrite, pData, WordCount * WordSize);
436 if (writtenWords < 1) throw Exception("POSIX IO Error while trying to write chunk data");
437 writtenWords /= WordSize;
438 #elif defined(WIN32)
439 LARGE_INTEGER liFilePos;
440 liFilePos.QuadPart = ullStartPos + ullPos;
441 if (!SetFilePointerEx(pFile->hFileWrite, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN)) {
442 throw Exception("Could not seek to position " + ToString(ullPos) +
443 " in chunk (" + ToString(ullStartPos + ullPos) + " in file)");
444 }
445 DWORD writtenWords;
446 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 if (writtenWords < 1) throw Exception("Windows IO Error while trying to write chunk data");
448 writtenWords /= WordSize;
449 #else // standard C functions
450 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 }
454 file_offset_t writtenWords = fwrite(pData, WordSize, WordCount, pFile->hFileWrite);
455 #endif // POSIX
456 SetPos(writtenWords * WordSize, stream_curpos);
457 return writtenWords;
458 }
459
460 /** Just an internal wrapper for the main <i>Read()</i> method with additional Exception throwing on errors. */
461 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 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 * @throws RIFF::Exception if an error occurred or less than
476 * \a WordCount integers could be read!
477 */
478 file_offset_t Chunk::ReadInt8(int8_t* pData, file_offset_t WordCount) {
479 #if DEBUG_RIFF
480 std::cout << "Chunk::ReadInt8(int8_t*,file_offset_t)" << std::endl;
481 #endif // DEBUG_RIFF
482 return ReadSceptical(pData, WordCount, 1);
483 }
484
485 /**
486 * 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 * @throws RIFF::Exception if an IO error occurred
497 * @see Resize()
498 */
499 file_offset_t Chunk::WriteInt8(int8_t* pData, file_offset_t WordCount) {
500 return Write(pData, WordCount, 1);
501 }
502
503 /**
504 * 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 * @throws RIFF::Exception if an error occurred or less than
513 * \a WordCount integers could be read!
514 */
515 file_offset_t Chunk::ReadUint8(uint8_t* pData, file_offset_t WordCount) {
516 #if DEBUG_RIFF
517 std::cout << "Chunk::ReadUint8(uint8_t*,file_offset_t)" << std::endl;
518 #endif // DEBUG_RIFF
519 return ReadSceptical(pData, WordCount, 1);
520 }
521
522 /**
523 * 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 * @throws RIFF::Exception if an IO error occurred
534 * @see Resize()
535 */
536 file_offset_t Chunk::WriteUint8(uint8_t* pData, file_offset_t WordCount) {
537 return Write(pData, WordCount, 1);
538 }
539
540 /**
541 * 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 * @throws RIFF::Exception if an error occurred or less than
550 * \a WordCount integers could be read!
551 */
552 file_offset_t Chunk::ReadInt16(int16_t* pData, file_offset_t WordCount) {
553 #if DEBUG_RIFF
554 std::cout << "Chunk::ReadInt16(int16_t*,file_offset_t)" << std::endl;
555 #endif // DEBUG_RIFF
556 return ReadSceptical(pData, WordCount, 2);
557 }
558
559 /**
560 * 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 * @throws RIFF::Exception if an IO error occurred
571 * @see Resize()
572 */
573 file_offset_t Chunk::WriteInt16(int16_t* pData, file_offset_t WordCount) {
574 return Write(pData, WordCount, 2);
575 }
576
577 /**
578 * 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 * @throws RIFF::Exception if an error occurred or less than
587 * \a WordCount integers could be read!
588 */
589 file_offset_t Chunk::ReadUint16(uint16_t* pData, file_offset_t WordCount) {
590 #if DEBUG_RIFF
591 std::cout << "Chunk::ReadUint16(uint16_t*,file_offset_t)" << std::endl;
592 #endif // DEBUG_RIFF
593 return ReadSceptical(pData, WordCount, 2);
594 }
595
596 /**
597 * 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 * @throws RIFF::Exception if an IO error occurred
608 * @see Resize()
609 */
610 file_offset_t Chunk::WriteUint16(uint16_t* pData, file_offset_t WordCount) {
611 return Write(pData, WordCount, 2);
612 }
613
614 /**
615 * 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 * @throws RIFF::Exception if an error occurred or less than
624 * \a WordCount integers could be read!
625 */
626 file_offset_t Chunk::ReadInt32(int32_t* pData, file_offset_t WordCount) {
627 #if DEBUG_RIFF
628 std::cout << "Chunk::ReadInt32(int32_t*,file_offset_t)" << std::endl;
629 #endif // DEBUG_RIFF
630 return ReadSceptical(pData, WordCount, 4);
631 }
632
633 /**
634 * 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 * @throws RIFF::Exception if an IO error occurred
645 * @see Resize()
646 */
647 file_offset_t Chunk::WriteInt32(int32_t* pData, file_offset_t WordCount) {
648 return Write(pData, WordCount, 4);
649 }
650
651 /**
652 * 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 * @throws RIFF::Exception if an error occurred or less than
661 * \a WordCount integers could be read!
662 */
663 file_offset_t Chunk::ReadUint32(uint32_t* pData, file_offset_t WordCount) {
664 #if DEBUG_RIFF
665 std::cout << "Chunk::ReadUint32(uint32_t*,file_offset_t)" << std::endl;
666 #endif // DEBUG_RIFF
667 return ReadSceptical(pData, WordCount, 4);
668 }
669
670 /**
671 * 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 * @throws RIFF::Exception if an error occurred or less than
678 * \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 * 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 * @throws RIFF::Exception if an IO error occurred
699 * @see Resize()
700 */
701 file_offset_t Chunk::WriteUint32(uint32_t* pData, file_offset_t WordCount) {
702 return Write(pData, WordCount, 4);
703 }
704
705 /**
706 * Reads one 8 Bit signed integer word and increments the position within
707 * the chunk.
708 *
709 * @returns read integer word
710 * @throws RIFF::Exception if an error occurred
711 */
712 int8_t Chunk::ReadInt8() {
713 #if DEBUG_RIFF
714 std::cout << "Chunk::ReadInt8()" << std::endl;
715 #endif // DEBUG_RIFF
716 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 * @throws RIFF::Exception if an error occurred
727 */
728 uint8_t Chunk::ReadUint8() {
729 #if DEBUG_RIFF
730 std::cout << "Chunk::ReadUint8()" << std::endl;
731 #endif // DEBUG_RIFF
732 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 * @throws RIFF::Exception if an error occurred
744 */
745 int16_t Chunk::ReadInt16() {
746 #if DEBUG_RIFF
747 std::cout << "Chunk::ReadInt16()" << std::endl;
748 #endif // DEBUG_RIFF
749 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 * @throws RIFF::Exception if an error occurred
761 */
762 uint16_t Chunk::ReadUint16() {
763 #if DEBUG_RIFF
764 std::cout << "Chunk::ReadUint16()" << std::endl;
765 #endif // DEBUG_RIFF
766 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 * @throws RIFF::Exception if an error occurred
778 */
779 int32_t Chunk::ReadInt32() {
780 #if DEBUG_RIFF
781 std::cout << "Chunk::ReadInt32()" << std::endl;
782 #endif // DEBUG_RIFF
783 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 * @throws RIFF::Exception if an error occurred
795 */
796 uint32_t Chunk::ReadUint32() {
797 #if DEBUG_RIFF
798 std::cout << "Chunk::ReadUint32()" << std::endl;
799 #endif // DEBUG_RIFF
800 uint32_t word;
801 ReadSceptical(&word,1,4);
802 return word;
803 }
804
805 /** @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 * get a new, valid pointer whenever File::Save() was called.
813 *
814 * 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 * @returns a pointer to the data in RAM on success, NULL otherwise
823 * @throws Exception if data buffer could not be enlarged
824 * @see ReleaseChunkData()
825 */
826 void* Chunk::LoadChunkData() {
827 if (!pChunkData && pFile->Filename != "" /*&& ulStartPos != 0*/) {
828 #if POSIX
829 if (lseek(pFile->hFileRead, ullStartPos, SEEK_SET) == -1) return NULL;
830 #elif defined(WIN32)
831 LARGE_INTEGER liFilePos;
832 liFilePos.QuadPart = ullStartPos;
833 if (!SetFilePointerEx(pFile->hFileRead, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN)) return NULL;
834 #else
835 if (fseeko(pFile->hFileRead, ullStartPos, SEEK_SET)) return NULL;
836 #endif // POSIX
837 file_offset_t ullBufferSize = (ullCurrentChunkSize > ullNewChunkSize) ? ullCurrentChunkSize : ullNewChunkSize;
838 pChunkData = new uint8_t[ullBufferSize];
839 if (!pChunkData) return NULL;
840 memset(pChunkData, 0, ullBufferSize);
841 #if POSIX
842 file_offset_t readWords = read(pFile->hFileRead, pChunkData, GetSize());
843 #elif defined(WIN32)
844 DWORD readWords;
845 ReadFile(pFile->hFileRead, pChunkData, GetSize(), &readWords, NULL); //FIXME: won't load chunks larger than 2GB !
846 #else
847 file_offset_t readWords = fread(pChunkData, 1, GetSize(), pFile->hFileRead);
848 #endif // POSIX
849 if (readWords != GetSize()) {
850 delete[] pChunkData;
851 return (pChunkData = NULL);
852 }
853 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 delete[] pChunkData;
860 pChunkData = pNewBuffer;
861 ullChunkDataSize = ullNewChunkSize;
862 }
863 return pChunkData;
864 }
865
866 /** @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 void Chunk::ReleaseChunkData() {
873 if (pChunkData) {
874 delete[] pChunkData;
875 pChunkData = NULL;
876 }
877 }
878
879 /** @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 * boundary!
892 *
893 * @param NewSize - new chunk body size in bytes (must be greater than zero)
894 * @throws RIFF::Exception if \a NewSize is less than 1 or unrealistic large
895 * @see File::Save()
896 */
897 void Chunk::Resize(file_offset_t NewSize) {
898 if (NewSize == 0)
899 throw Exception("There is at least one empty chunk (zero size): " + __resolveChunkPath(this));
900 if ((NewSize >> 48) != 0)
901 throw Exception("Unrealistic high chunk size detected: " + __resolveChunkPath(this));
902 if (ullNewChunkSize == NewSize) return;
903 ullNewChunkSize = NewSize;
904 }
905
906 /** @brief Write chunk persistently e.g. to disk.
907 *
908 * Stores the chunk persistently to its actual "physical" file.
909 *
910 * @param ullWritePos - position within the "physical" file where this
911 * chunk should be written to
912 * @param ullCurrentDataOffset - offset of current (old) data within
913 * the file
914 * @param pProgress - optional: callback function for progress notification
915 * @returns new write position in the "physical" file, that is
916 * \a ullWritePos incremented by this chunk's new size
917 * (including its header size of course)
918 */
919 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
923 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 // if the whole chunk body was loaded into RAM
927 if (pChunkData) {
928 // make sure chunk data buffer in RAM is at least as large as the new chunk size
929 LoadChunkData();
930 // write chunk data from RAM persistently to the file
931 #if POSIX
932 lseek(pFile->hFileWrite, ullWritePos, SEEK_SET);
933 if (write(pFile->hFileWrite, pChunkData, ullNewChunkSize) != ullNewChunkSize) {
934 throw Exception("Writing Chunk data (from RAM) failed");
935 }
936 #elif defined(WIN32)
937 LARGE_INTEGER liFilePos;
938 liFilePos.QuadPart = ullWritePos;
939 SetFilePointerEx(pFile->hFileWrite, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN);
940 DWORD dwBytesWritten;
941 WriteFile(pFile->hFileWrite, pChunkData, ullNewChunkSize, &dwBytesWritten, NULL); //FIXME: won't save chunks larger than 2GB !
942 if (dwBytesWritten != ullNewChunkSize) {
943 throw Exception("Writing Chunk data (from RAM) failed");
944 }
945 #else
946 fseeko(pFile->hFileWrite, ullWritePos, SEEK_SET);
947 if (fwrite(pChunkData, 1, ullNewChunkSize, pFile->hFileWrite) != ullNewChunkSize) {
948 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 file_offset_t ullToMove = (ullNewChunkSize < ullCurrentChunkSize) ? ullNewChunkSize : ullCurrentChunkSize;
955 #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 int iBytesMoved = 1;
959 #endif
960 for (file_offset_t ullOffset = 0; ullToMove > 0 && iBytesMoved > 0; ullOffset += iBytesMoved, ullToMove -= iBytesMoved) {
961 iBytesMoved = (ullToMove < 4096) ? int(ullToMove) : 4096;
962 #if POSIX
963 lseek(pFile->hFileRead, ullStartPos + ullCurrentDataOffset + ullOffset, SEEK_SET);
964 iBytesMoved = (int) read(pFile->hFileRead, pCopyBuffer, (size_t) iBytesMoved);
965 lseek(pFile->hFileWrite, ullWritePos + ullOffset, SEEK_SET);
966 iBytesMoved = (int) write(pFile->hFileWrite, pCopyBuffer, (size_t) iBytesMoved);
967 #elif defined(WIN32)
968 LARGE_INTEGER liFilePos;
969 liFilePos.QuadPart = ullStartPos + ullCurrentDataOffset + ullOffset;
970 SetFilePointerEx(pFile->hFileRead, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN);
971 ReadFile(pFile->hFileRead, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
972 liFilePos.QuadPart = ullWritePos + ullOffset;
973 SetFilePointerEx(pFile->hFileWrite, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN);
974 WriteFile(pFile->hFileWrite, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
975 #else
976 fseeko(pFile->hFileRead, ullStartPos + ullCurrentDataOffset + ullOffset, SEEK_SET);
977 iBytesMoved = fread(pCopyBuffer, 1, iBytesMoved, pFile->hFileRead);
978 fseeko(pFile->hFileWrite, ullWritePos + ullOffset, SEEK_SET);
979 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 ullCurrentChunkSize = ullNewChunkSize;
988 WriteHeader(ullOriginalPos);
989
990 __notify_progress(pProgress, 1.0); // notify done
991
992 // update chunk's position pointers
993 ullStartPos = ullOriginalPos + CHUNK_HEADER_SIZE(pFile->FileOffsetSize);
994 ullPos = 0;
995
996 // add pad byte if needed
997 if ((ullStartPos + ullNewChunkSize) % 2 != 0) {
998 const char cPadByte = 0;
999 #if POSIX
1000 lseek(pFile->hFileWrite, ullStartPos + ullNewChunkSize, SEEK_SET);
1001 write(pFile->hFileWrite, &cPadByte, 1);
1002 #elif defined(WIN32)
1003 LARGE_INTEGER liFilePos;
1004 liFilePos.QuadPart = ullStartPos + ullNewChunkSize;
1005 SetFilePointerEx(pFile->hFileWrite, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN);
1006 DWORD dwBytesWritten;
1007 WriteFile(pFile->hFileWrite, &cPadByte, 1, &dwBytesWritten, NULL);
1008 #else
1009 fseeko(pFile->hFileWrite, ullStartPos + ullNewChunkSize, SEEK_SET);
1010 fwrite(&cPadByte, 1, 1, pFile->hFileWrite);
1011 #endif
1012 return ullStartPos + ullNewChunkSize + 1;
1013 }
1014
1015 return ullStartPos + ullNewChunkSize;
1016 }
1017
1018 void Chunk::__resetPos() {
1019 ullPos = 0;
1020 }
1021
1022
1023
1024 // *************** List ***************
1025 // *
1026
1027 List::List(File* pFile) : Chunk(pFile) {
1028 #if DEBUG_RIFF
1029 std::cout << "List::List(File* pFile)" << std::endl;
1030 #endif // DEBUG_RIFF
1031 pSubChunks = NULL;
1032 pSubChunksMap = NULL;
1033 }
1034
1035 List::List(File* pFile, file_offset_t StartPos, List* Parent)
1036 : Chunk(pFile, StartPos, Parent) {
1037 #if DEBUG_RIFF
1038 std::cout << "List::List(File*,file_offset_t,List*)" << std::endl;
1039 #endif // DEBUG_RIFF
1040 pSubChunks = NULL;
1041 pSubChunksMap = NULL;
1042 ReadHeader(StartPos);
1043 ullStartPos = StartPos + LIST_HEADER_SIZE(pFile->FileOffsetSize);
1044 }
1045
1046 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 List::~List() {
1054 #if DEBUG_RIFF
1055 std::cout << "List::~List()" << std::endl;
1056 #endif // DEBUG_RIFF
1057 DeleteChunkList();
1058 }
1059
1060 void List::DeleteChunkList() {
1061 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 pSubChunks = NULL;
1070 }
1071 if (pSubChunksMap) {
1072 delete pSubChunksMap;
1073 pSubChunksMap = NULL;
1074 }
1075 }
1076
1077 /**
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 Chunk* List::GetSubChunk(uint32_t ChunkID) {
1089 #if DEBUG_RIFF
1090 std::cout << "List::GetSubChunk(uint32_t)" << std::endl;
1091 #endif // DEBUG_RIFF
1092 if (!pSubChunksMap) LoadSubChunks();
1093 return (*pSubChunksMap)[ChunkID];
1094 }
1095
1096 /**
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 * that type in the list. If there are more than one, it's undetermined
1100 * 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 List* List::GetSubList(uint32_t ListType) {
1108 #if DEBUG_RIFF
1109 std::cout << "List::GetSubList(uint32_t)" << std::endl;
1110 #endif // DEBUG_RIFF
1111 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 /**
1125 * 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 * 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 Chunk* List::GetFirstSubChunk() {
1134 #if DEBUG_RIFF
1135 std::cout << "List::GetFirstSubChunk()" << std::endl;
1136 #endif // DEBUG_RIFF
1137 if (!pSubChunks) LoadSubChunks();
1138 ChunksIterator = pSubChunks->begin();
1139 return (ChunksIterator != pSubChunks->end()) ? *ChunksIterator : NULL;
1140 }
1141
1142 /**
1143 * 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 * 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 Chunk* List::GetNextSubChunk() {
1151 #if DEBUG_RIFF
1152 std::cout << "List::GetNextSubChunk()" << std::endl;
1153 #endif // DEBUG_RIFF
1154 if (!pSubChunks) return NULL;
1155 ChunksIterator++;
1156 return (ChunksIterator != pSubChunks->end()) ? *ChunksIterator : NULL;
1157 }
1158
1159 /**
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 List* List::GetFirstSubList() {
1169 #if DEBUG_RIFF
1170 std::cout << "List::GetFirstSubList()" << std::endl;
1171 #endif // DEBUG_RIFF
1172 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 /**
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 List* List::GetNextSubList() {
1191 #if DEBUG_RIFF
1192 std::cout << "List::GetNextSubList()" << std::endl;
1193 #endif // DEBUG_RIFF
1194 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 /**
1206 * Returns number of subchunks within the list (including list chunks).
1207 */
1208 size_t List::CountSubChunks() {
1209 if (!pSubChunks) LoadSubChunks();
1210 return pSubChunks->size();
1211 }
1212
1213 /**
1214 * Returns number of subchunks within the list with chunk ID
1215 * <i>\a ChunkId</i>.
1216 */
1217 size_t List::CountSubChunks(uint32_t ChunkID) {
1218 size_t result = 0;
1219 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 /**
1232 * Returns number of sublists within the list.
1233 */
1234 size_t List::CountSubLists() {
1235 return CountSubChunks(CHUNK_ID_LIST);
1236 }
1237
1238 /**
1239 * Returns number of sublists within the list with list type
1240 * <i>\a ListType</i>
1241 */
1242 size_t List::CountSubLists(uint32_t ListType) {
1243 size_t result = 0;
1244 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 /** @brief Creates a new sub chunk.
1258 *
1259 * Creates and adds a new sub chunk to this list chunk. Note that the
1260 * chunk's body size given by \a ullBodySize must be greater than zero.
1261 * 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 * @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 */
1270 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 if (!pSubChunks) LoadSubChunks();
1273 Chunk* pNewChunk = new Chunk(pFile, this, uiChunkID, 0);
1274 pSubChunks->push_back(pNewChunk);
1275 (*pSubChunksMap)[uiChunkID] = pNewChunk;
1276 pNewChunk->Resize(ullBodySize);
1277 ullNewChunkSize += CHUNK_HEADER_SIZE(pFile->FileOffsetSize);
1278 return pNewChunk;
1279 }
1280
1281 /** @brief Moves a sub chunk witin this list.
1282 *
1283 * Moves a sub chunk from one position in this list to another
1284 * 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 /** @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 /** @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 ullNewChunkSize += LIST_HEADER_SIZE(pFile->FileOffsetSize);
1346 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 * 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 *
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 /**
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 #if DEBUG_RIFF
1396 std::cout << "List::Readheader(file_offset_t) ";
1397 #endif // DEBUG_RIFF
1398 Chunk::ReadHeader(filePos);
1399 if (ullCurrentChunkSize < 4) return;
1400 ullNewChunkSize = ullCurrentChunkSize -= 4;
1401 #if POSIX
1402 lseek(pFile->hFileRead, filePos + CHUNK_HEADER_SIZE(pFile->FileOffsetSize), SEEK_SET);
1403 read(pFile->hFileRead, &ListType, 4);
1404 #elif defined(WIN32)
1405 LARGE_INTEGER liFilePos;
1406 liFilePos.QuadPart = filePos + CHUNK_HEADER_SIZE(pFile->FileOffsetSize);
1407 SetFilePointerEx(pFile->hFileRead, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN);
1408 DWORD dwBytesRead;
1409 ReadFile(pFile->hFileRead, &ListType, 4, &dwBytesRead, NULL);
1410 #else
1411 fseeko(pFile->hFileRead, filePos + CHUNK_HEADER_SIZE(pFile->FileOffsetSize), SEEK_SET);
1412 fread(&ListType, 4, 1, pFile->hFileRead);
1413 #endif // POSIX
1414 #if DEBUG_RIFF
1415 std::cout << "listType=" << convertToString(ListType) << std::endl;
1416 #endif // DEBUG_RIFF
1417 if (!pFile->bEndianNative) {
1418 //swapBytes_32(&ListType);
1419 }
1420 }
1421
1422 void List::WriteHeader(file_offset_t filePos) {
1423 // the four list type bytes officially belong the chunk's body in the RIFF format
1424 ullNewChunkSize += 4;
1425 Chunk::WriteHeader(filePos);
1426 ullNewChunkSize -= 4; // just revert the +4 incrementation
1427 #if POSIX
1428 lseek(pFile->hFileWrite, filePos + CHUNK_HEADER_SIZE(pFile->FileOffsetSize), SEEK_SET);
1429 write(pFile->hFileWrite, &ListType, 4);
1430 #elif defined(WIN32)
1431 LARGE_INTEGER liFilePos;
1432 liFilePos.QuadPart = filePos + CHUNK_HEADER_SIZE(pFile->FileOffsetSize);
1433 SetFilePointerEx(pFile->hFileWrite, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN);
1434 DWORD dwBytesWritten;
1435 WriteFile(pFile->hFileWrite, &ListType, 4, &dwBytesWritten, NULL);
1436 #else
1437 fseeko(pFile->hFileWrite, filePos + CHUNK_HEADER_SIZE(pFile->FileOffsetSize), SEEK_SET);
1438 fwrite(&ListType, 4, 1, pFile->hFileWrite);
1439 #endif // POSIX
1440 }
1441
1442 void List::LoadSubChunks(progress_t* pProgress) {
1443 #if DEBUG_RIFF
1444 std::cout << "List::LoadSubChunks()";
1445 #endif // DEBUG_RIFF
1446 if (!pSubChunks) {
1447 pSubChunks = new ChunkList();
1448 pSubChunksMap = new ChunkMap();
1449 #if defined(WIN32)
1450 if (pFile->hFileRead == INVALID_HANDLE_VALUE) return;
1451 #else
1452 if (!pFile->hFileRead) return;
1453 #endif
1454 file_offset_t ullOriginalPos = GetPos();
1455 SetPos(0); // jump to beginning of list chunk body
1456 while (RemainingBytes() >= CHUNK_HEADER_SIZE(pFile->FileOffsetSize)) {
1457 Chunk* ck;
1458 uint32_t ckid;
1459 Read(&ckid, 4, 1);
1460 #if DEBUG_RIFF
1461 std::cout << " ckid=" << convertToString(ckid) << std::endl;
1462 #endif // DEBUG_RIFF
1463 if (ckid == CHUNK_ID_LIST) {
1464 ck = new RIFF::List(pFile, ullStartPos + ullPos - 4, this);
1465 SetPos(ck->GetSize() + LIST_HEADER_SIZE(pFile->FileOffsetSize) - 4, RIFF::stream_curpos);
1466 }
1467 else { // simple chunk
1468 ck = new RIFF::Chunk(pFile, ullStartPos + ullPos - 4, this);
1469 SetPos(ck->GetSize() + CHUNK_HEADER_SIZE(pFile->FileOffsetSize) - 4, RIFF::stream_curpos);
1470 }
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 SetPos(ullOriginalPos); // restore position before this call
1476 }
1477 __notify_progress(pProgress, 1.0); // notify done
1478 }
1479
1480 void List::LoadSubChunksRecursively(progress_t* pProgress) {
1481 const int n = (int) CountSubLists();
1482 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 }
1492
1493 /** @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 * @param ullWritePos - position within the "physical" file where this
1500 * list chunk should be written to
1501 * @param ullCurrentDataOffset - offset of current (old) data within
1502 * the file
1503 * @param pProgress - optional: callback function for progress notification
1504 * @returns new write position in the "physical" file, that is
1505 * \a ullWritePos incremented by this list chunk's new size
1506 * (including its header size of course)
1507 */
1508 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
1512 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 // write all subchunks (including sub list chunks) recursively
1516 if (pSubChunks) {
1517 size_t i = 0;
1518 const size_t n = pSubChunks->size();
1519 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 ullWritePos = (*iter)->WriteChunk(ullWritePos, ullCurrentDataOffset, &subprogress);
1525 }
1526 }
1527
1528 // update this list chunk's header
1529 ullCurrentChunkSize = ullNewChunkSize = ullWritePos - ullOriginalPos - LIST_HEADER_SIZE(pFile->FileOffsetSize);
1530 WriteHeader(ullOriginalPos);
1531
1532 // offset of this list chunk in new written file may have changed
1533 ullStartPos = ullOriginalPos + LIST_HEADER_SIZE(pFile->FileOffsetSize);
1534
1535 __notify_progress(pProgress, 1.0); // notify done
1536
1537 return ullWritePos;
1538 }
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 /**
1550 * Returns string representation of the lists's id
1551 */
1552 String List::GetListTypeString() const {
1553 return convertToString(ListType);
1554 }
1555
1556
1557
1558 // *************** File ***************
1559 // *
1560
1561 /** @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 * 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 * @param FileType - four-byte identifier of the RIFF file type
1573 * @see AddSubChunk(), AddSubList(), SetByteOrder()
1574 */
1575 File::File(uint32_t FileType)
1576 : List(this), bIsNewFile(true), Layout(layout_standard),
1577 FileOffsetPreference(offset_size_auto)
1578 {
1579 #if defined(WIN32)
1580 hFileRead = hFileWrite = INVALID_HANDLE_VALUE;
1581 #else
1582 hFileRead = hFileWrite = 0;
1583 #endif
1584 Mode = stream_mode_closed;
1585 bEndianNative = true;
1586 ListType = FileType;
1587 FileOffsetSize = 4;
1588 ullStartPos = RIFF_HEADER_SIZE(FileOffsetSize);
1589 }
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 * @throws RIFF::Exception if error occurred while trying to load the
1597 * given RIFF file
1598 */
1599 File::File(const String& path)
1600 : List(this), Filename(path), bIsNewFile(false), Layout(layout_standard),
1601 FileOffsetPreference(offset_size_auto)
1602 {
1603 #if DEBUG_RIFF
1604 std::cout << "File::File("<<path<<")" << std::endl;
1605 #endif // DEBUG_RIFF
1606 bEndianNative = true;
1607 FileOffsetSize = 4;
1608 try {
1609 __openExistingFile(path);
1610 if (ChunkID != CHUNK_ID_RIFF && ChunkID != CHUNK_ID_RIFX) {
1611 throw RIFF::Exception("Not a RIFF file");
1612 }
1613 }
1614 catch (...) {
1615 Cleanup();
1616 throw;
1617 }
1618 }
1619
1620 /** @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 * @param fileOffsetSize - (optional) preference how to deal with large files
1643 * @throws RIFF::Exception if error occurred while trying to load the
1644 * given RIFF-alike file
1645 */
1646 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 {
1650 SetByteOrder(Endian);
1651 if (fileOffsetSize < offset_size_auto || fileOffsetSize > offset_size_64bit)
1652 throw Exception("Invalid RIFF::offset_size_t");
1653 FileOffsetSize = 4;
1654 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 * @throws RIFF::Exception if error occurred while trying to load the
1671 * 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 if (hFileRead == -1) {
1677 hFileRead = hFileWrite = 0;
1678 String sError = strerror(errno);
1679 throw RIFF::Exception("Can't open \"" + path + "\": " + sError);
1680 }
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
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 switch (Layout) {
1704 case layout_standard: // this is a normal RIFF file
1705 ullStartPos = RIFF_HEADER_SIZE(FileOffsetSize);
1706 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 ullStartPos = 0;
1712 ullNewChunkSize = ullCurrentChunkSize = GetCurrentFileSize();
1713 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 String File::GetFileName() const {
1729 return Filename;
1730 }
1731
1732 void File::SetFileName(const String& path) {
1733 Filename = path;
1734 }
1735
1736 stream_mode_t File::GetMode() const {
1737 return Mode;
1738 }
1739
1740 layout_t File::GetLayout() const {
1741 return Layout;
1742 }
1743
1744 /** @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 if (hFileRead == -1) {
1762 hFileRead = hFileWrite = 0;
1763 String sError = strerror(errno);
1764 throw Exception("Could not (re)open file \"" + Filename + "\" in read mode: " + sError);
1765 }
1766 #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 FILE_ATTRIBUTE_NORMAL |
1773 FILE_FLAG_RANDOM_ACCESS,
1774 NULL
1775 );
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 #else
1781 if (hFileRead) fclose(hFileRead);
1782 hFileRead = hFileWrite = fopen(Filename.c_str(), "rb");
1783 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 break;
1787 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 if (hFileRead == -1) {
1792 hFileRead = hFileWrite = open(Filename.c_str(), O_RDONLY | O_NONBLOCK);
1793 String sError = strerror(errno);
1794 throw Exception("Could not open file \"" + Filename + "\" in read+write mode: " + sError);
1795 }
1796 #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 FILE_ATTRIBUTE_NORMAL |
1804 FILE_FLAG_RANDOM_ACCESS,
1805 NULL
1806 );
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 FILE_ATTRIBUTE_NORMAL |
1813 FILE_FLAG_RANDOM_ACCESS,
1814 NULL
1815 );
1816 throw Exception("Could not (re)open file \"" + Filename + "\" in read+write mode");
1817 }
1818 #else
1819 if (hFileRead) fclose(hFileRead);
1820 hFileRead = hFileWrite = fopen(Filename.c_str(), "r+b");
1821 if (!hFileRead) {
1822 hFileRead = hFileWrite = fopen(Filename.c_str(), "rb");
1823 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 break;
1828 case stream_mode_closed:
1829 #if POSIX
1830 if (hFileRead) close(hFileRead);
1831 if (hFileWrite) close(hFileWrite);
1832 hFileRead = hFileWrite = 0;
1833 #elif defined(WIN32)
1834 if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
1835 if (hFileWrite != INVALID_HANDLE_VALUE) CloseHandle(hFileWrite);
1836 hFileRead = hFileWrite = INVALID_HANDLE_VALUE;
1837 #else
1838 if (hFileRead) fclose(hFileRead);
1839 if (hFileWrite) fclose(hFileWrite);
1840 hFileRead = hFileWrite = NULL;
1841 #endif
1842 break;
1843 default:
1844 throw Exception("Unknown file access mode");
1845 }
1846 Mode = NewMode;
1847 return true;
1848 }
1849 return false;
1850 }
1851
1852 /** @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 /** @brief Save changes to same file.
1870 *
1871 * Make all changes of all chunks persistent by writing them to the
1872 * actual (same) file.
1873 *
1874 * @param pProgress - optional: callback function for progress notification
1875 * @throws RIFF::Exception if there is an empty chunk or empty list
1876 * chunk or any kind of IO error occurred
1877 */
1878 void File::Save(progress_t* pProgress) {
1879 //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 // make sure the RIFF tree is built (from the original file)
1884 {
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
1894 // reopen file in write mode
1895 SetMode(stream_mode_read_write);
1896
1897 // 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 // 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 // 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
1915 // if there are positive size changes...
1916 file_offset_t positiveSizeDiff = 0;
1917 if (newFileSize > workingFileSize) {
1918 positiveSizeDiff = newFileSize - workingFileSize;
1919
1920 // 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 // ... we enlarge this file first ...
1925 ResizeFile(newFileSize);
1926
1927 // ... and move current data by the same amount towards end of file.
1928 int8_t* pCopyBuffer = new int8_t[4096];
1929 #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 ssize_t iBytesMoved = 1;
1933 #endif
1934 for (file_offset_t ullPos = workingFileSize, iNotif = 0; iBytesMoved > 0; ++iNotif) {
1935 iBytesMoved = (ullPos < 4096) ? ullPos : 4096;
1936 ullPos -= iBytesMoved;
1937 #if POSIX
1938 lseek(hFileRead, ullPos, SEEK_SET);
1939 iBytesMoved = read(hFileRead, pCopyBuffer, iBytesMoved);
1940 lseek(hFileWrite, ullPos + positiveSizeDiff, SEEK_SET);
1941 iBytesMoved = write(hFileWrite, pCopyBuffer, iBytesMoved);
1942 #elif defined(WIN32)
1943 LARGE_INTEGER liFilePos;
1944 liFilePos.QuadPart = ullPos;
1945 SetFilePointerEx(hFileRead, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN);
1946 ReadFile(hFileRead, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
1947 liFilePos.QuadPart = ullPos + positiveSizeDiff;
1948 SetFilePointerEx(hFileWrite, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN);
1949 WriteFile(hFileWrite, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
1950 #else
1951 fseeko(hFileRead, ullPos, SEEK_SET);
1952 iBytesMoved = fread(pCopyBuffer, 1, iBytesMoved, hFileRead);
1953 fseeko(hFileWrite, ullPos + positiveSizeDiff, SEEK_SET);
1954 iBytesMoved = fwrite(pCopyBuffer, 1, iBytesMoved, hFileWrite);
1955 #endif
1956 if (!(iNotif % 8) && iBytesMoved > 0)
1957 __notify_progress(&subprogress, float(workingFileSize - ullPos) / float(workingFileSize));
1958 }
1959 delete[] pCopyBuffer;
1960 if (iBytesMoved < 0) throw Exception("Could not modify file while trying to enlarge it");
1961
1962 __notify_progress(&subprogress, 1.f); // notify subprogress done
1963 }
1964
1965 // 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 const file_offset_t finalSize = WriteChunk(0, positiveSizeDiff, &subprogress);
1972 const file_offset_t finalActualSize = __GetFileSize(hFileWrite);
1973 // notify subprogress done
1974 __notify_progress(&subprogress, 1.f);
1975
1976 // resize file to the final size
1977 if (finalSize < finalActualSize) ResizeFile(finalSize);
1978
1979 __notify_progress(pProgress, 1.0); // notify done
1980 }
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 * file! Use File::Save() in that case instead! Ignoring this might
1988 * result in a corrupted file, especially in case chunks were resized!
1989 *
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 * @param pProgress - optional: callback function for progress notification
1995 */
1996 void File::Save(const String& path, progress_t* pProgress) {
1997 //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 //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 // make sure the RIFF tree is built (from the original file)
2004 {
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
2014 if (!bIsNewFile) SetMode(stream_mode_read);
2015 // open the other (new) file for writing and truncate it to zero size
2016 #if POSIX
2017 hFileWrite = open(path.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP);
2018 if (hFileWrite == -1) {
2019 hFileWrite = hFileRead;
2020 String sError = strerror(errno);
2021 throw Exception("Could not open file \"" + path + "\" for writing: " + sError);
2022 }
2023 #elif defined(WIN32)
2024 hFileWrite = CreateFile(
2025 path.c_str(), GENERIC_WRITE, FILE_SHARE_READ,
2026 NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL |
2027 FILE_FLAG_RANDOM_ACCESS, NULL
2028 );
2029 if (hFileWrite == INVALID_HANDLE_VALUE) {
2030 hFileWrite = hFileRead;
2031 throw Exception("Could not open file \"" + path + "\" for writing");
2032 }
2033 #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 Mode = stream_mode_read_write;
2041
2042 // 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 // write complete RIFF tree to the other (new) file
2050 file_offset_t ullTotalSize;
2051 {
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 ullTotalSize = WriteChunk(0, 0, &subprogress);
2057 // notify subprogress done
2058 __notify_progress(&subprogress, 1.f);
2059 }
2060 file_offset_t ullActualSize = __GetFileSize(hFileWrite);
2061
2062 // resize file to the final size (if the file was originally larger)
2063 if (ullActualSize > ullTotalSize) ResizeFile(ullTotalSize);
2064
2065 #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
2074 // associate new file with this File object from now on
2075 Filename = path;
2076 bIsNewFile = false;
2077 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
2080 __notify_progress(pProgress, 1.0); // notify done
2081 }
2082
2083 void File::ResizeFile(file_offset_t ullNewSize) {
2084 #if POSIX
2085 if (ftruncate(hFileWrite, ullNewSize) < 0)
2086 throw Exception("Could not resize file \"" + Filename + "\"");
2087 #elif defined(WIN32)
2088 LARGE_INTEGER liFilePos;
2089 liFilePos.QuadPart = ullNewSize;
2090 if (
2091 !SetFilePointerEx(hFileWrite, liFilePos, NULL/*new pos pointer*/, FILE_BEGIN) ||
2092 !SetEndOfFile(hFileWrite)
2093 ) throw Exception("Could not resize file \"" + Filename + "\"");
2094 #else
2095 # error Sorry, this version of libgig only supports POSIX and Windows systems yet.
2096 # error Reason: portable implementation of RIFF::File::ResizeFile() is missing (yet)!
2097 #endif
2098 }
2099
2100 File::~File() {
2101 #if DEBUG_RIFF
2102 std::cout << "File::~File()" << std::endl;
2103 #endif // DEBUG_RIFF
2104 Cleanup();
2105 }
2106
2107 /**
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
2115 void File::Cleanup() {
2116 #if POSIX
2117 if (hFileRead) close(hFileRead);
2118 #elif defined(WIN32)
2119 if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
2120 #else
2121 if (hFileRead) fclose(hFileRead);
2122 #endif // POSIX
2123 DeleteChunkList();
2124 pFile = NULL;
2125 }
2126
2127 /**
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 }
2142
2143 /**
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 }
2165
2166 /**
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 }
2192
2193 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 #if POSIX
2245 file_offset_t File::__GetFileSize(int hFile) const {
2246 struct stat filestat;
2247 if (fstat(hFile, &filestat) == -1)
2248 throw Exception("POSIX FS error: could not determine file size");
2249 return filestat.st_size;
2250 }
2251 #elif defined(WIN32)
2252 file_offset_t File::__GetFileSize(HANDLE hFile) const {
2253 LARGE_INTEGER size;
2254 if (!GetFileSizeEx(hFile, &size))
2255 throw Exception("Windows FS error: could not determine file size");
2256 return size.QuadPart;
2257 }
2258 #else // standard C functions
2259 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 return size;
2266 }
2267 #endif
2268
2269
2270 // *************** Exception ***************
2271 // *
2272
2273 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 void Exception::PrintMessage() {
2288 std::cout << "RIFF::Exception: " << Message << std::endl;
2289 }
2290
2291 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
2299
2300 // *************** 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 } // namespace RIFF

  ViewVC Help
Powered by ViewVC