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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1102 - (show annotations) (download)
Sun Mar 18 07:13:06 2007 UTC (17 years, 1 month ago) by persson
File size: 73334 byte(s)
* added MoveRegion and MoveSubChunk
* fixed initialization in AddRegion

1 /***************************************************************************
2 * *
3 * libgig - C++ cross-platform Gigasampler format file access library *
4 * *
5 * Copyright (C) 2003-2007 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 <string.h>
25
26 #include "RIFF.h"
27
28 #include "helper.h"
29
30 namespace RIFF {
31
32 // *************** Chunk **************
33 // *
34
35 Chunk::Chunk(File* pFile) {
36 #if DEBUG
37 std::cout << "Chunk::Chunk(File* pFile)" << std::endl;
38 #endif // DEBUG
39 ulPos = 0;
40 pParent = NULL;
41 pChunkData = NULL;
42 ulChunkDataSize = 0;
43 ChunkID = CHUNK_ID_RIFF;
44 this->pFile = pFile;
45 }
46
47 Chunk::Chunk(File* pFile, unsigned long StartPos, List* Parent) {
48 #if DEBUG
49 std::cout << "Chunk::Chunk(File*,ulong,bool,List*),StartPos=" << StartPos << std::endl;
50 #endif // DEBUG
51 this->pFile = pFile;
52 ulStartPos = StartPos + CHUNK_HEADER_SIZE;
53 pParent = Parent;
54 ulPos = 0;
55 pChunkData = NULL;
56 ulChunkDataSize = 0;
57 ReadHeader(StartPos);
58 }
59
60 Chunk::Chunk(File* pFile, List* pParent, uint32_t uiChunkID, uint uiBodySize) {
61 this->pFile = pFile;
62 ulStartPos = 0; // arbitrary usually, since it will be updated when we write the chunk
63 this->pParent = pParent;
64 ulPos = 0;
65 pChunkData = NULL;
66 ulChunkDataSize = 0;
67 ChunkID = uiChunkID;
68 CurrentChunkSize = 0;
69 NewChunkSize = uiBodySize;
70 }
71
72 Chunk::~Chunk() {
73 if (CurrentChunkSize != NewChunkSize) pFile->UnlogResized(this);
74 if (pChunkData) delete[] pChunkData;
75 }
76
77 void Chunk::ReadHeader(unsigned long fPos) {
78 #if DEBUG
79 std::cout << "Chunk::Readheader(" << fPos << ") ";
80 #endif // DEBUG
81 #if POSIX
82 if (lseek(pFile->hFileRead, fPos, SEEK_SET) != -1) {
83 read(pFile->hFileRead, &ChunkID, 4);
84 read(pFile->hFileRead, &CurrentChunkSize, 4);
85 #elif defined(WIN32)
86 if (SetFilePointer(pFile->hFileRead, fPos, NULL/*32 bit*/, FILE_BEGIN) != INVALID_SET_FILE_POINTER) {
87 DWORD dwBytesRead;
88 ReadFile(pFile->hFileRead, &ChunkID, 4, &dwBytesRead, NULL);
89 ReadFile(pFile->hFileRead, &CurrentChunkSize, 4, &dwBytesRead, NULL);
90 #else
91 if (!fseek(pFile->hFileRead, fPos, SEEK_SET)) {
92 fread(&ChunkID, 4, 1, pFile->hFileRead);
93 fread(&CurrentChunkSize, 4, 1, pFile->hFileRead);
94 #endif // POSIX
95 #if WORDS_BIGENDIAN
96 if (ChunkID == CHUNK_ID_RIFF) {
97 pFile->bEndianNative = false;
98 }
99 #else // little endian
100 if (ChunkID == CHUNK_ID_RIFX) {
101 pFile->bEndianNative = false;
102 ChunkID = CHUNK_ID_RIFF;
103 }
104 #endif // WORDS_BIGENDIAN
105 if (!pFile->bEndianNative) {
106 //swapBytes_32(&ChunkID);
107 swapBytes_32(&CurrentChunkSize);
108 }
109 #if DEBUG
110 std::cout << "ckID=" << convertToString(ChunkID) << " ";
111 std::cout << "ckSize=" << ChunkSize << " ";
112 std::cout << "bEndianNative=" << bEndianNative << std::endl;
113 #endif // DEBUG
114 NewChunkSize = CurrentChunkSize;
115 }
116 }
117
118 void Chunk::WriteHeader(unsigned long fPos) {
119 uint32_t uiNewChunkID = ChunkID;
120 if (ChunkID == CHUNK_ID_RIFF) {
121 #if WORDS_BIGENDIAN
122 if (pFile->bEndianNative) uiNewChunkID = CHUNK_ID_RIFX;
123 #else // little endian
124 if (!pFile->bEndianNative) uiNewChunkID = CHUNK_ID_RIFX;
125 #endif // WORDS_BIGENDIAN
126 }
127
128 uint32_t uiNewChunkSize = NewChunkSize;
129 if (!pFile->bEndianNative) {
130 swapBytes_32(&uiNewChunkSize);
131 }
132
133 #if POSIX
134 if (lseek(pFile->hFileWrite, fPos, SEEK_SET) != -1) {
135 write(pFile->hFileWrite, &uiNewChunkID, 4);
136 write(pFile->hFileWrite, &uiNewChunkSize, 4);
137 }
138 #elif defined(WIN32)
139 if (SetFilePointer(pFile->hFileWrite, fPos, NULL/*32 bit*/, FILE_BEGIN) != INVALID_SET_FILE_POINTER) {
140 DWORD dwBytesWritten;
141 WriteFile(pFile->hFileWrite, &uiNewChunkID, 4, &dwBytesWritten, NULL);
142 WriteFile(pFile->hFileWrite, &uiNewChunkSize, 4, &dwBytesWritten, NULL);
143 }
144 #else
145 if (!fseek(pFile->hFileWrite, fPos, SEEK_SET)) {
146 fwrite(&uiNewChunkID, 4, 1, pFile->hFileWrite);
147 fwrite(&uiNewChunkSize, 4, 1, pFile->hFileWrite);
148 }
149 #endif // POSIX
150 }
151
152 /**
153 * Returns the String representation of the chunk's ID (e.g. "RIFF",
154 * "LIST").
155 */
156 String Chunk::GetChunkIDString() {
157 return convertToString(ChunkID);
158 }
159
160 /**
161 * Sets the position within the chunk body, thus within the data portion
162 * of the chunk (in bytes).
163 *
164 * <b>Caution:</b> the position will be reset to zero whenever
165 * File::Save() was called.
166 *
167 * @param Where - position offset (in bytes)
168 * @param Whence - optional: defines to what <i>\a Where</i> relates to,
169 * if omitted \a Where relates to beginning of the chunk
170 * data
171 */
172 unsigned long Chunk::SetPos(unsigned long Where, stream_whence_t Whence) {
173 #if DEBUG
174 std::cout << "Chunk::SetPos(ulong)" << std::endl;
175 #endif // DEBUG
176 switch (Whence) {
177 case stream_curpos:
178 ulPos += Where;
179 break;
180 case stream_end:
181 ulPos = CurrentChunkSize - 1 - Where;
182 break;
183 case stream_backward:
184 ulPos -= Where;
185 break;
186 case stream_start: default:
187 ulPos = Where;
188 break;
189 }
190 if (ulPos > CurrentChunkSize) ulPos = CurrentChunkSize;
191 return ulPos;
192 }
193
194 /**
195 * Returns the number of bytes left to read in the chunk body.
196 * When reading data from the chunk using the Read*() Methods, the
197 * position within the chunk data (that is the chunk body) will be
198 * incremented by the number of read bytes and RemainingBytes() returns
199 * how much data is left to read from the current position to the end
200 * of the chunk data.
201 *
202 * @returns number of bytes left to read
203 */
204 unsigned long Chunk::RemainingBytes() {
205 #if DEBUG
206 std::cout << "Chunk::Remainingbytes()=" << CurrentChunkSize - ulPos << std::endl;
207 #endif // DEBUG
208 return CurrentChunkSize - ulPos;
209 }
210
211 /**
212 * Returns the current state of the chunk object.
213 * Following values are possible:
214 * - RIFF::stream_ready :
215 * chunk data can be read (this is the usual case)
216 * - RIFF::stream_closed :
217 * the data stream was closed somehow, no more reading possible
218 * - RIFF::stream_end_reached :
219 * alreaady reached the end of the chunk data, no more reading
220 * possible without SetPos()
221 */
222 stream_state_t Chunk::GetState() {
223 #if DEBUG
224 std::cout << "Chunk::GetState()" << std::endl;
225 #endif // DEBUG
226 #if POSIX
227 if (pFile->hFileRead == 0) return stream_closed;
228 #elif defined (WIN32)
229 if (pFile->hFileRead == INVALID_HANDLE_VALUE)
230 return stream_closed;
231 #else
232 if (pFile->hFileRead == NULL) return stream_closed;
233 #endif // POSIX
234 if (ulPos < CurrentChunkSize) return stream_ready;
235 else return stream_end_reached;
236 }
237
238 /**
239 * Reads \a WordCount number of data words with given \a WordSize and
240 * copies it into a buffer pointed by \a pData. The buffer has to be
241 * allocated and be sure to provide the correct \a WordSize, as this
242 * will be important and taken into account for eventual endian
243 * correction (swapping of bytes due to different native byte order of
244 * a system). The position within the chunk will automatically be
245 * incremented.
246 *
247 * @param pData destination buffer
248 * @param WordCount number of data words to read
249 * @param WordSize size of each data word to read
250 * @returns number of successfully read data words or 0 if end
251 * of file reached or error occured
252 */
253 unsigned long Chunk::Read(void* pData, unsigned long WordCount, unsigned long WordSize) {
254 #if DEBUG
255 std::cout << "Chunk::Read(void*,ulong,ulong)" << std::endl;
256 #endif // DEBUG
257 if (ulPos >= CurrentChunkSize) return 0;
258 if (ulPos + WordCount * WordSize >= CurrentChunkSize) WordCount = (CurrentChunkSize - ulPos) / WordSize;
259 #if POSIX
260 if (lseek(pFile->hFileRead, ulStartPos + ulPos, SEEK_SET) < 0) return 0;
261 unsigned long readWords = read(pFile->hFileRead, pData, WordCount * WordSize);
262 if (readWords < 1) return 0;
263 readWords /= WordSize;
264 #elif defined(WIN32)
265 if (SetFilePointer(pFile->hFileRead, ulStartPos + ulPos, NULL/*32 bit*/, FILE_BEGIN) == INVALID_SET_FILE_POINTER) return 0;
266 DWORD readWords;
267 ReadFile(pFile->hFileRead, pData, WordCount * WordSize, &readWords, NULL);
268 if (readWords < 1) return 0;
269 readWords /= WordSize;
270 #else // standard C functions
271 if (fseek(pFile->hFileRead, ulStartPos + ulPos, SEEK_SET)) return 0;
272 unsigned long readWords = fread(pData, WordSize, WordCount, pFile->hFileRead);
273 #endif // POSIX
274 if (!pFile->bEndianNative && WordSize != 1) {
275 switch (WordSize) {
276 case 2:
277 for (unsigned long iWord = 0; iWord < readWords; iWord++)
278 swapBytes_16((uint16_t*) pData + iWord);
279 break;
280 case 4:
281 for (unsigned long iWord = 0; iWord < readWords; iWord++)
282 swapBytes_32((uint32_t*) pData + iWord);
283 break;
284 default:
285 for (unsigned long iWord = 0; iWord < readWords; iWord++)
286 swapBytes((uint8_t*) pData + iWord * WordSize, WordSize);
287 break;
288 }
289 }
290 SetPos(readWords * WordSize, stream_curpos);
291 return readWords;
292 }
293
294 /**
295 * Writes \a WordCount number of data words with given \a WordSize from
296 * the buffer pointed by \a pData. Be sure to provide the correct
297 * \a WordSize, as this will be important and taken into account for
298 * eventual endian correction (swapping of bytes due to different
299 * native byte order of a system). The position within the chunk will
300 * automatically be incremented.
301 *
302 * @param pData source buffer (containing the data)
303 * @param WordCount number of data words to write
304 * @param WordSize size of each data word to write
305 * @returns number of successfully written data words
306 * @throws RIFF::Exception if write operation would exceed current
307 * chunk size or any IO error occured
308 * @see Resize()
309 */
310 unsigned long Chunk::Write(void* pData, unsigned long WordCount, unsigned long WordSize) {
311 if (pFile->Mode != stream_mode_read_write)
312 throw Exception("Cannot write data to chunk, file has to be opened in read+write mode first");
313 if (ulPos >= CurrentChunkSize || ulPos + WordCount * WordSize > CurrentChunkSize)
314 throw Exception("End of chunk reached while trying to write data");
315 if (!pFile->bEndianNative && WordSize != 1) {
316 switch (WordSize) {
317 case 2:
318 for (unsigned long iWord = 0; iWord < WordCount; iWord++)
319 swapBytes_16((uint16_t*) pData + iWord);
320 break;
321 case 4:
322 for (unsigned long iWord = 0; iWord < WordCount; iWord++)
323 swapBytes_32((uint32_t*) pData + iWord);
324 break;
325 default:
326 for (unsigned long iWord = 0; iWord < WordCount; iWord++)
327 swapBytes((uint8_t*) pData + iWord * WordSize, WordSize);
328 break;
329 }
330 }
331 #if POSIX
332 if (lseek(pFile->hFileWrite, ulStartPos + ulPos, SEEK_SET) < 0) {
333 throw Exception("Could not seek to position " + ToString(ulPos) +
334 " in chunk (" + ToString(ulStartPos + ulPos) + " in file)");
335 }
336 unsigned long writtenWords = write(pFile->hFileWrite, pData, WordCount * WordSize);
337 if (writtenWords < 1) throw Exception("POSIX IO Error while trying to write chunk data");
338 writtenWords /= WordSize;
339 #elif defined(WIN32)
340 if (SetFilePointer(pFile->hFileWrite, ulStartPos + ulPos, NULL/*32 bit*/, FILE_BEGIN) == INVALID_SET_FILE_POINTER) {
341 throw Exception("Could not seek to position " + ToString(ulPos) +
342 " in chunk (" + ToString(ulStartPos + ulPos) + " in file)");
343 }
344 DWORD writtenWords;
345 WriteFile(pFile->hFileWrite, pData, WordCount * WordSize, &writtenWords, NULL);
346 if (writtenWords < 1) throw Exception("Windows IO Error while trying to write chunk data");
347 writtenWords /= WordSize;
348 #else // standard C functions
349 if (fseek(pFile->hFileWrite, ulStartPos + ulPos, SEEK_SET)) {
350 throw Exception("Could not seek to position " + ToString(ulPos) +
351 " in chunk (" + ToString(ulStartPos + ulPos) + " in file)");
352 }
353 unsigned long writtenWords = fwrite(pData, WordSize, WordCount, pFile->hFileWrite);
354 #endif // POSIX
355 SetPos(writtenWords * WordSize, stream_curpos);
356 return writtenWords;
357 }
358
359 /** Just an internal wrapper for the main <i>Read()</i> method with additional Exception throwing on errors. */
360 unsigned long Chunk::ReadSceptical(void* pData, unsigned long WordCount, unsigned long WordSize) {
361 unsigned long readWords = Read(pData, WordCount, WordSize);
362 if (readWords != WordCount) throw RIFF::Exception("End of chunk data reached.");
363 return readWords;
364 }
365
366 /**
367 * Reads \a WordCount number of 8 Bit signed integer words and copies it
368 * into the buffer pointed by \a pData. The buffer has to be allocated.
369 * The position within the chunk will automatically be incremented.
370 *
371 * @param pData destination buffer
372 * @param WordCount number of 8 Bit signed integers to read
373 * @returns number of read integers
374 * @throws RIFF::Exception if an error occured or less than
375 * \a WordCount integers could be read!
376 */
377 unsigned long Chunk::ReadInt8(int8_t* pData, unsigned long WordCount) {
378 #if DEBUG
379 std::cout << "Chunk::ReadInt8(int8_t*,ulong)" << std::endl;
380 #endif // DEBUG
381 return ReadSceptical(pData, WordCount, 1);
382 }
383
384 /**
385 * Writes \a WordCount number of 8 Bit signed integer words from the
386 * buffer pointed by \a pData to the chunk's body, directly to the
387 * actual "physical" file. The position within the chunk will
388 * automatically be incremented. Note: you cannot write beyond the
389 * boundaries of the chunk, to append data to the chunk call Resize()
390 * before.
391 *
392 * @param pData source buffer (containing the data)
393 * @param WordCount number of 8 Bit signed integers to write
394 * @returns number of written integers
395 * @throws RIFF::Exception if an IO error occured
396 * @see Resize()
397 */
398 unsigned long Chunk::WriteInt8(int8_t* pData, unsigned long WordCount) {
399 return Write(pData, WordCount, 1);
400 }
401
402 /**
403 * Reads \a WordCount number of 8 Bit unsigned integer words and copies
404 * it into the buffer pointed by \a pData. The buffer has to be
405 * allocated. The position within the chunk will automatically be
406 * incremented.
407 *
408 * @param pData destination buffer
409 * @param WordCount number of 8 Bit unsigned integers to read
410 * @returns number of read integers
411 * @throws RIFF::Exception if an error occured or less than
412 * \a WordCount integers could be read!
413 */
414 unsigned long Chunk::ReadUint8(uint8_t* pData, unsigned long WordCount) {
415 #if DEBUG
416 std::cout << "Chunk::ReadUint8(uint8_t*,ulong)" << std::endl;
417 #endif // DEBUG
418 return ReadSceptical(pData, WordCount, 1);
419 }
420
421 /**
422 * Writes \a WordCount number of 8 Bit unsigned integer words from the
423 * buffer pointed by \a pData to the chunk's body, directly to the
424 * actual "physical" file. The position within the chunk will
425 * automatically be incremented. Note: you cannot write beyond the
426 * boundaries of the chunk, to append data to the chunk call Resize()
427 * before.
428 *
429 * @param pData source buffer (containing the data)
430 * @param WordCount number of 8 Bit unsigned integers to write
431 * @returns number of written integers
432 * @throws RIFF::Exception if an IO error occured
433 * @see Resize()
434 */
435 unsigned long Chunk::WriteUint8(uint8_t* pData, unsigned long WordCount) {
436 return Write(pData, WordCount, 1);
437 }
438
439 /**
440 * Reads \a WordCount number of 16 Bit signed integer words and copies
441 * it into the buffer pointed by \a pData. The buffer has to be
442 * allocated. Endian correction will automatically be done if needed.
443 * The position within the chunk will automatically be incremented.
444 *
445 * @param pData destination buffer
446 * @param WordCount number of 16 Bit signed integers to read
447 * @returns number of read integers
448 * @throws RIFF::Exception if an error occured or less than
449 * \a WordCount integers could be read!
450 */
451 unsigned long Chunk::ReadInt16(int16_t* pData, unsigned long WordCount) {
452 #if DEBUG
453 std::cout << "Chunk::ReadInt16(int16_t*,ulong)" << std::endl;
454 #endif // DEBUG
455 return ReadSceptical(pData, WordCount, 2);
456 }
457
458 /**
459 * Writes \a WordCount number of 16 Bit signed integer words from the
460 * buffer pointed by \a pData to the chunk's body, directly to the
461 * actual "physical" file. The position within the chunk will
462 * automatically be incremented. Note: you cannot write beyond the
463 * boundaries of the chunk, to append data to the chunk call Resize()
464 * before.
465 *
466 * @param pData source buffer (containing the data)
467 * @param WordCount number of 16 Bit signed integers to write
468 * @returns number of written integers
469 * @throws RIFF::Exception if an IO error occured
470 * @see Resize()
471 */
472 unsigned long Chunk::WriteInt16(int16_t* pData, unsigned long WordCount) {
473 return Write(pData, WordCount, 2);
474 }
475
476 /**
477 * Reads \a WordCount number of 16 Bit unsigned integer words and copies
478 * it into the buffer pointed by \a pData. The buffer has to be
479 * allocated. Endian correction will automatically be done if needed.
480 * The position within the chunk will automatically be incremented.
481 *
482 * @param pData destination buffer
483 * @param WordCount number of 8 Bit unsigned integers to read
484 * @returns number of read integers
485 * @throws RIFF::Exception if an error occured or less than
486 * \a WordCount integers could be read!
487 */
488 unsigned long Chunk::ReadUint16(uint16_t* pData, unsigned long WordCount) {
489 #if DEBUG
490 std::cout << "Chunk::ReadUint16(uint16_t*,ulong)" << std::endl;
491 #endif // DEBUG
492 return ReadSceptical(pData, WordCount, 2);
493 }
494
495 /**
496 * Writes \a WordCount number of 16 Bit unsigned integer words from the
497 * buffer pointed by \a pData to the chunk's body, directly to the
498 * actual "physical" file. The position within the chunk will
499 * automatically be incremented. Note: you cannot write beyond the
500 * boundaries of the chunk, to append data to the chunk call Resize()
501 * before.
502 *
503 * @param pData source buffer (containing the data)
504 * @param WordCount number of 16 Bit unsigned integers to write
505 * @returns number of written integers
506 * @throws RIFF::Exception if an IO error occured
507 * @see Resize()
508 */
509 unsigned long Chunk::WriteUint16(uint16_t* pData, unsigned long WordCount) {
510 return Write(pData, WordCount, 2);
511 }
512
513 /**
514 * Reads \a WordCount number of 32 Bit signed integer words and copies
515 * it into the buffer pointed by \a pData. The buffer has to be
516 * allocated. Endian correction will automatically be done if needed.
517 * The position within the chunk will automatically be incremented.
518 *
519 * @param pData destination buffer
520 * @param WordCount number of 32 Bit signed integers to read
521 * @returns number of read integers
522 * @throws RIFF::Exception if an error occured or less than
523 * \a WordCount integers could be read!
524 */
525 unsigned long Chunk::ReadInt32(int32_t* pData, unsigned long WordCount) {
526 #if DEBUG
527 std::cout << "Chunk::ReadInt32(int32_t*,ulong)" << std::endl;
528 #endif // DEBUG
529 return ReadSceptical(pData, WordCount, 4);
530 }
531
532 /**
533 * Writes \a WordCount number of 32 Bit signed integer words from the
534 * buffer pointed by \a pData to the chunk's body, directly to the
535 * actual "physical" file. The position within the chunk will
536 * automatically be incremented. Note: you cannot write beyond the
537 * boundaries of the chunk, to append data to the chunk call Resize()
538 * before.
539 *
540 * @param pData source buffer (containing the data)
541 * @param WordCount number of 32 Bit signed integers to write
542 * @returns number of written integers
543 * @throws RIFF::Exception if an IO error occured
544 * @see Resize()
545 */
546 unsigned long Chunk::WriteInt32(int32_t* pData, unsigned long WordCount) {
547 return Write(pData, WordCount, 4);
548 }
549
550 /**
551 * Reads \a WordCount number of 32 Bit unsigned integer words and copies
552 * it into the buffer pointed by \a pData. The buffer has to be
553 * allocated. Endian correction will automatically be done if needed.
554 * The position within the chunk will automatically be incremented.
555 *
556 * @param pData destination buffer
557 * @param WordCount number of 32 Bit unsigned integers to read
558 * @returns number of read integers
559 * @throws RIFF::Exception if an error occured or less than
560 * \a WordCount integers could be read!
561 */
562 unsigned long Chunk::ReadUint32(uint32_t* pData, unsigned long WordCount) {
563 #if DEBUG
564 std::cout << "Chunk::ReadUint32(uint32_t*,ulong)" << std::endl;
565 #endif // DEBUG
566 return ReadSceptical(pData, WordCount, 4);
567 }
568
569 /**
570 * Writes \a WordCount number of 32 Bit unsigned integer words from the
571 * buffer pointed by \a pData to the chunk's body, directly to the
572 * actual "physical" file. The position within the chunk will
573 * automatically be incremented. Note: you cannot write beyond the
574 * boundaries of the chunk, to append data to the chunk call Resize()
575 * before.
576 *
577 * @param pData source buffer (containing the data)
578 * @param WordCount number of 32 Bit unsigned integers to write
579 * @returns number of written integers
580 * @throws RIFF::Exception if an IO error occured
581 * @see Resize()
582 */
583 unsigned long Chunk::WriteUint32(uint32_t* pData, unsigned long WordCount) {
584 return Write(pData, WordCount, 4);
585 }
586
587 /**
588 * Reads one 8 Bit signed integer word and increments the position within
589 * the chunk.
590 *
591 * @returns read integer word
592 * @throws RIFF::Exception if an error occured
593 */
594 int8_t Chunk::ReadInt8() {
595 #if DEBUG
596 std::cout << "Chunk::ReadInt8()" << std::endl;
597 #endif // DEBUG
598 int8_t word;
599 ReadSceptical(&word,1,1);
600 return word;
601 }
602
603 /**
604 * Reads one 8 Bit unsigned integer word and increments the position
605 * within the chunk.
606 *
607 * @returns read integer word
608 * @throws RIFF::Exception if an error occured
609 */
610 uint8_t Chunk::ReadUint8() {
611 #if DEBUG
612 std::cout << "Chunk::ReadUint8()" << std::endl;
613 #endif // DEBUG
614 uint8_t word;
615 ReadSceptical(&word,1,1);
616 return word;
617 }
618
619 /**
620 * Reads one 16 Bit signed integer word and increments the position
621 * within the chunk. Endian correction will automatically be done if
622 * needed.
623 *
624 * @returns read integer word
625 * @throws RIFF::Exception if an error occured
626 */
627 int16_t Chunk::ReadInt16() {
628 #if DEBUG
629 std::cout << "Chunk::ReadInt16()" << std::endl;
630 #endif // DEBUG
631 int16_t word;
632 ReadSceptical(&word,1,2);
633 return word;
634 }
635
636 /**
637 * Reads one 16 Bit unsigned integer word and increments the position
638 * within the chunk. Endian correction will automatically be done if
639 * needed.
640 *
641 * @returns read integer word
642 * @throws RIFF::Exception if an error occured
643 */
644 uint16_t Chunk::ReadUint16() {
645 #if DEBUG
646 std::cout << "Chunk::ReadUint16()" << std::endl;
647 #endif // DEBUG
648 uint16_t word;
649 ReadSceptical(&word,1,2);
650 return word;
651 }
652
653 /**
654 * Reads one 32 Bit signed integer word and increments the position
655 * within the chunk. Endian correction will automatically be done if
656 * needed.
657 *
658 * @returns read integer word
659 * @throws RIFF::Exception if an error occured
660 */
661 int32_t Chunk::ReadInt32() {
662 #if DEBUG
663 std::cout << "Chunk::ReadInt32()" << std::endl;
664 #endif // DEBUG
665 int32_t word;
666 ReadSceptical(&word,1,4);
667 return word;
668 }
669
670 /**
671 * Reads one 32 Bit unsigned integer word and increments the position
672 * within the chunk. Endian correction will automatically be done if
673 * needed.
674 *
675 * @returns read integer word
676 * @throws RIFF::Exception if an error occured
677 */
678 uint32_t Chunk::ReadUint32() {
679 #if DEBUG
680 std::cout << "Chunk::ReadUint32()" << std::endl;
681 #endif // DEBUG
682 uint32_t word;
683 ReadSceptical(&word,1,4);
684 return word;
685 }
686
687 /** @brief Load chunk body into RAM.
688 *
689 * Loads the whole chunk body into memory. You can modify the data in
690 * RAM and save the data by calling File::Save() afterwards.
691 *
692 * <b>Caution:</b> the buffer pointer will be invalidated once
693 * File::Save() was called. You have to call LoadChunkData() again to
694 * get a new, valid pointer whenever File::Save() was called.
695 *
696 * You can call LoadChunkData() again if you previously scheduled to
697 * enlarge this chunk with a Resize() call. In that case the buffer will
698 * be enlarged to the new, scheduled chunk size and you can already
699 * place the new chunk data to the buffer and finally call File::Save()
700 * to enlarge the chunk physically and write the new data in one rush.
701 * This approach is definitely recommended if you have to enlarge and
702 * write new data to a lot of chunks.
703 *
704 * @returns a pointer to the data in RAM on success, NULL otherwise
705 * @throws Exception if data buffer could not be enlarged
706 * @see ReleaseChunkData()
707 */
708 void* Chunk::LoadChunkData() {
709 if (!pChunkData && pFile->Filename != "") {
710 #if POSIX
711 if (lseek(pFile->hFileRead, ulStartPos, SEEK_SET) == -1) return NULL;
712 #elif defined(WIN32)
713 if (SetFilePointer(pFile->hFileRead, ulStartPos, NULL/*32 bit*/, FILE_BEGIN) == INVALID_SET_FILE_POINTER) return NULL;
714 #else
715 if (fseek(pFile->hFileRead, ulStartPos, SEEK_SET)) return NULL;
716 #endif // POSIX
717 unsigned long ulBufferSize = (CurrentChunkSize > NewChunkSize) ? CurrentChunkSize : NewChunkSize;
718 pChunkData = new uint8_t[ulBufferSize];
719 if (!pChunkData) return NULL;
720 memset(pChunkData, 0, ulBufferSize);
721 #if POSIX
722 unsigned long readWords = read(pFile->hFileRead, pChunkData, GetSize());
723 #elif defined(WIN32)
724 DWORD readWords;
725 ReadFile(pFile->hFileRead, pChunkData, GetSize(), &readWords, NULL);
726 #else
727 unsigned long readWords = fread(pChunkData, 1, GetSize(), pFile->hFileRead);
728 #endif // POSIX
729 if (readWords != GetSize()) {
730 delete[] pChunkData;
731 return (pChunkData = NULL);
732 }
733 ulChunkDataSize = ulBufferSize;
734 } else if (NewChunkSize > ulChunkDataSize) {
735 uint8_t* pNewBuffer = new uint8_t[NewChunkSize];
736 if (!pNewBuffer) throw Exception("Could not enlarge chunk data buffer to " + ToString(NewChunkSize) + " bytes");
737 memset(pNewBuffer, 0 , NewChunkSize);
738 memcpy(pNewBuffer, pChunkData, ulChunkDataSize);
739 delete[] pChunkData;
740 pChunkData = pNewBuffer;
741 ulChunkDataSize = NewChunkSize;
742 }
743 return pChunkData;
744 }
745
746 /** @brief Free loaded chunk body from RAM.
747 *
748 * Frees loaded chunk body data from memory (RAM). You should call
749 * File::Save() before calling this method if you modified the data to
750 * make the changes persistent.
751 */
752 void Chunk::ReleaseChunkData() {
753 if (pChunkData) {
754 delete[] pChunkData;
755 pChunkData = NULL;
756 }
757 }
758
759 /** @brief Resize chunk.
760 *
761 * Resizes this chunk's body, that is the actual size of data possible
762 * to be written to this chunk. This call will return immediately and
763 * just schedule the resize operation. You should call File::Save() to
764 * actually perform the resize operation(s) "physically" to the file.
765 * As this can take a while on large files, it is recommended to call
766 * Resize() first on all chunks which have to be resized and finally to
767 * call File::Save() to perform all those resize operations in one rush.
768 *
769 * <b>Caution:</b> You cannot directly write to enlarged chunks before
770 * calling File::Save() as this might exceed the current chunk's body
771 * boundary!
772 *
773 * @param iNewSize - new chunk body size in bytes (must be greater than zero)
774 * @throws RIFF::Exception if \a iNewSize is less than 1
775 * @see File::Save()
776 */
777 void Chunk::Resize(int iNewSize) {
778 if (iNewSize <= 0) throw Exception("Chunk size must be at least one byte");
779 if (NewChunkSize == iNewSize) return;
780 NewChunkSize = iNewSize;
781 pFile->LogAsResized(this);
782 }
783
784 /** @brief Write chunk persistently e.g. to disk.
785 *
786 * Stores the chunk persistently to its actual "physical" file.
787 *
788 * @param ulWritePos - position within the "physical" file where this
789 * chunk should be written to
790 * @param ulCurrentDataOffset - offset of current (old) data within
791 * the file
792 * @returns new write position in the "physical" file, that is
793 * \a ulWritePos incremented by this chunk's new size
794 * (including its header size of course)
795 */
796 unsigned long Chunk::WriteChunk(unsigned long ulWritePos, unsigned long ulCurrentDataOffset) {
797 const unsigned long ulOriginalPos = ulWritePos;
798 ulWritePos += CHUNK_HEADER_SIZE;
799
800 if (pFile->Mode != stream_mode_read_write)
801 throw Exception("Cannot write list chunk, file has to be opened in read+write mode");
802
803 // if the whole chunk body was loaded into RAM
804 if (pChunkData) {
805 // make sure chunk data buffer in RAM is at least as large as the new chunk size
806 LoadChunkData();
807 // write chunk data from RAM persistently to the file
808 #if POSIX
809 lseek(pFile->hFileWrite, ulWritePos, SEEK_SET);
810 if (write(pFile->hFileWrite, pChunkData, NewChunkSize) != NewChunkSize) {
811 throw Exception("Writing Chunk data (from RAM) failed");
812 }
813 #elif defined(WIN32)
814 SetFilePointer(pFile->hFileWrite, ulWritePos, NULL/*32 bit*/, FILE_BEGIN);
815 DWORD dwBytesWritten;
816 WriteFile(pFile->hFileWrite, pChunkData, NewChunkSize, &dwBytesWritten, NULL);
817 if (dwBytesWritten != NewChunkSize) {
818 throw Exception("Writing Chunk data (from RAM) failed");
819 }
820 #else
821 fseek(pFile->hFileWrite, ulWritePos, SEEK_SET);
822 if (fwrite(pChunkData, 1, NewChunkSize, pFile->hFileWrite) != NewChunkSize) {
823 throw Exception("Writing Chunk data (from RAM) failed");
824 }
825 #endif // POSIX
826 } else {
827 // move chunk data from the end of the file to the appropriate position
828 int8_t* pCopyBuffer = new int8_t[4096];
829 unsigned long ulToMove = (NewChunkSize < CurrentChunkSize) ? NewChunkSize : CurrentChunkSize;
830 #if defined(WIN32)
831 DWORD iBytesMoved = 1; // we have to pass it via pointer to the Windows API, thus the correct size must be ensured
832 #else
833 int iBytesMoved = 1;
834 #endif
835 for (unsigned long ulOffset = 0; iBytesMoved > 0; ulOffset += iBytesMoved, ulToMove -= iBytesMoved) {
836 iBytesMoved = (ulToMove < 4096) ? ulToMove : 4096;
837 #if POSIX
838 lseek(pFile->hFileRead, ulStartPos + ulCurrentDataOffset + ulOffset, SEEK_SET);
839 iBytesMoved = read(pFile->hFileRead, pCopyBuffer, iBytesMoved);
840 lseek(pFile->hFileWrite, ulWritePos + ulOffset, SEEK_SET);
841 iBytesMoved = write(pFile->hFileWrite, pCopyBuffer, iBytesMoved);
842 #elif defined(WIN32)
843 SetFilePointer(pFile->hFileRead, ulStartPos + ulCurrentDataOffset + ulOffset, NULL/*32 bit*/, FILE_BEGIN);
844 ReadFile(pFile->hFileRead, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
845 SetFilePointer(pFile->hFileWrite, ulWritePos + ulOffset, NULL/*32 bit*/, FILE_BEGIN);
846 WriteFile(pFile->hFileWrite, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
847 #else
848 fseek(pFile->hFileRead, ulStartPos + ulCurrentDataOffset + ulOffset, SEEK_SET);
849 iBytesMoved = fread(pCopyBuffer, 1, iBytesMoved, pFile->hFileRead);
850 fseek(pFile->hFileWrite, ulWritePos + ulOffset, SEEK_SET);
851 iBytesMoved = fwrite(pCopyBuffer, 1, iBytesMoved, pFile->hFileWrite);
852 #endif
853 }
854 delete[] pCopyBuffer;
855 if (iBytesMoved < 0) throw Exception("Writing Chunk data (from file) failed");
856 }
857
858 // update this chunk's header
859 CurrentChunkSize = NewChunkSize;
860 WriteHeader(ulOriginalPos);
861
862 // update chunk's position pointers
863 ulStartPos = ulOriginalPos + CHUNK_HEADER_SIZE;
864 ulPos = 0;
865
866 // add pad byte if needed
867 if ((ulStartPos + NewChunkSize) % 2 != 0) {
868 const char cPadByte = 0;
869 #if POSIX
870 lseek(pFile->hFileWrite, ulStartPos + NewChunkSize, SEEK_SET);
871 write(pFile->hFileWrite, &cPadByte, 1);
872 #elif defined(WIN32)
873 SetFilePointer(pFile->hFileWrite, ulStartPos + NewChunkSize, NULL/*32 bit*/, FILE_BEGIN);
874 DWORD dwBytesWritten;
875 WriteFile(pFile->hFileWrite, &cPadByte, 1, &dwBytesWritten, NULL);
876 #else
877 fseek(pFile->hFileWrite, ulStartPos + NewChunkSize, SEEK_SET);
878 fwrite(&cPadByte, 1, 1, pFile->hFileWrite);
879 #endif
880 return ulStartPos + NewChunkSize + 1;
881 }
882
883 return ulStartPos + NewChunkSize;
884 }
885
886 void Chunk::__resetPos() {
887 ulPos = 0;
888 }
889
890
891
892 // *************** List ***************
893 // *
894
895 List::List(File* pFile) : Chunk(pFile) {
896 #if DEBUG
897 std::cout << "List::List(File* pFile)" << std::endl;
898 #endif // DEBUG
899 pSubChunks = NULL;
900 pSubChunksMap = NULL;
901 }
902
903 List::List(File* pFile, unsigned long StartPos, List* Parent)
904 : Chunk(pFile, StartPos, Parent) {
905 #if DEBUG
906 std::cout << "List::List(File*,ulong,bool,List*)" << std::endl;
907 #endif // DEBUG
908 pSubChunks = NULL;
909 pSubChunksMap = NULL;
910 ReadHeader(StartPos);
911 ulStartPos = StartPos + LIST_HEADER_SIZE;
912 }
913
914 List::List(File* pFile, List* pParent, uint32_t uiListID)
915 : Chunk(pFile, pParent, CHUNK_ID_LIST, 0) {
916 pSubChunks = NULL;
917 pSubChunksMap = NULL;
918 ListType = uiListID;
919 }
920
921 List::~List() {
922 #if DEBUG
923 std::cout << "List::~List()" << std::endl;
924 #endif // DEBUG
925 if (pSubChunks) {
926 ChunkList::iterator iter = pSubChunks->begin();
927 ChunkList::iterator end = pSubChunks->end();
928 while (iter != end) {
929 delete *iter;
930 iter++;
931 }
932 delete pSubChunks;
933 }
934 if (pSubChunksMap) delete pSubChunksMap;
935 }
936
937 /**
938 * Returns subchunk with chunk ID <i>\a ChunkID</i> within this chunk
939 * list. Use this method if you expect only one subchunk of that type in
940 * the list. It there are more than one, it's undetermined which one of
941 * them will be returned! If there are no subchunks with that desired
942 * chunk ID, NULL will be returned.
943 *
944 * @param ChunkID - chunk ID of the sought subchunk
945 * @returns pointer to the subchunk or NULL if there is none of
946 * that ID
947 */
948 Chunk* List::GetSubChunk(uint32_t ChunkID) {
949 #if DEBUG
950 std::cout << "List::GetSubChunk(uint32_t)" << std::endl;
951 #endif // DEBUG
952 if (!pSubChunksMap) LoadSubChunks();
953 return (*pSubChunksMap)[ChunkID];
954 }
955
956 /**
957 * Returns sublist chunk with list type <i>\a ListType</i> within this
958 * chunk list. Use this method if you expect only one sublist chunk of
959 * that type in the list. It there are more than one, it's undetermined
960 * which one of them will be returned! If there are no sublists with
961 * that desired list type, NULL will be returned.
962 *
963 * @param ListType - list type of the sought sublist
964 * @returns pointer to the sublist or NULL if there is none of
965 * that type
966 */
967 List* List::GetSubList(uint32_t ListType) {
968 #if DEBUG
969 std::cout << "List::GetSubList(uint32_t)" << std::endl;
970 #endif // DEBUG
971 if (!pSubChunks) LoadSubChunks();
972 ChunkList::iterator iter = pSubChunks->begin();
973 ChunkList::iterator end = pSubChunks->end();
974 while (iter != end) {
975 if ((*iter)->GetChunkID() == CHUNK_ID_LIST) {
976 List* l = (List*) *iter;
977 if (l->GetListType() == ListType) return l;
978 }
979 iter++;
980 }
981 return NULL;
982 }
983
984 /**
985 * Returns the first subchunk within the list. You have to call this
986 * method before you can call GetNextSubChunk(). Recall it when you want
987 * to start from the beginning of the list again.
988 *
989 * @returns pointer to the first subchunk within the list, NULL
990 * otherwise
991 */
992 Chunk* List::GetFirstSubChunk() {
993 #if DEBUG
994 std::cout << "List::GetFirstSubChunk()" << std::endl;
995 #endif // DEBUG
996 if (!pSubChunks) LoadSubChunks();
997 ChunksIterator = pSubChunks->begin();
998 return (ChunksIterator != pSubChunks->end()) ? *ChunksIterator : NULL;
999 }
1000
1001 /**
1002 * Returns the next subchunk within the list. You have to call
1003 * GetFirstSubChunk() before you can use this method!
1004 *
1005 * @returns pointer to the next subchunk within the list or NULL if
1006 * end of list is reached
1007 */
1008 Chunk* List::GetNextSubChunk() {
1009 #if DEBUG
1010 std::cout << "List::GetNextSubChunk()" << std::endl;
1011 #endif // DEBUG
1012 if (!pSubChunks) return NULL;
1013 ChunksIterator++;
1014 return (ChunksIterator != pSubChunks->end()) ? *ChunksIterator : NULL;
1015 }
1016
1017 /**
1018 * Returns the first sublist within the list (that is a subchunk with
1019 * chunk ID "LIST"). You have to call this method before you can call
1020 * GetNextSubList(). Recall it when you want to start from the beginning
1021 * of the list again.
1022 *
1023 * @returns pointer to the first sublist within the list, NULL
1024 * otherwise
1025 */
1026 List* List::GetFirstSubList() {
1027 #if DEBUG
1028 std::cout << "List::GetFirstSubList()" << std::endl;
1029 #endif // DEBUG
1030 if (!pSubChunks) LoadSubChunks();
1031 ListIterator = pSubChunks->begin();
1032 ChunkList::iterator end = pSubChunks->end();
1033 while (ListIterator != end) {
1034 if ((*ListIterator)->GetChunkID() == CHUNK_ID_LIST) return (List*) *ListIterator;
1035 ListIterator++;
1036 }
1037 return NULL;
1038 }
1039
1040 /**
1041 * Returns the next sublist (that is a subchunk with chunk ID "LIST")
1042 * within the list. You have to call GetFirstSubList() before you can
1043 * use this method!
1044 *
1045 * @returns pointer to the next sublist within the list, NULL if
1046 * end of list is reached
1047 */
1048 List* List::GetNextSubList() {
1049 #if DEBUG
1050 std::cout << "List::GetNextSubList()" << std::endl;
1051 #endif // DEBUG
1052 if (!pSubChunks) return NULL;
1053 if (ListIterator == pSubChunks->end()) return NULL;
1054 ListIterator++;
1055 ChunkList::iterator end = pSubChunks->end();
1056 while (ListIterator != end) {
1057 if ((*ListIterator)->GetChunkID() == CHUNK_ID_LIST) return (List*) *ListIterator;
1058 ListIterator++;
1059 }
1060 return NULL;
1061 }
1062
1063 /**
1064 * Returns number of subchunks within the list.
1065 */
1066 unsigned int List::CountSubChunks() {
1067 if (!pSubChunks) LoadSubChunks();
1068 return pSubChunks->size();
1069 }
1070
1071 /**
1072 * Returns number of subchunks within the list with chunk ID
1073 * <i>\a ChunkId</i>.
1074 */
1075 unsigned int List::CountSubChunks(uint32_t ChunkID) {
1076 unsigned int result = 0;
1077 if (!pSubChunks) LoadSubChunks();
1078 ChunkList::iterator iter = pSubChunks->begin();
1079 ChunkList::iterator end = pSubChunks->end();
1080 while (iter != end) {
1081 if ((*iter)->GetChunkID() == ChunkID) {
1082 result++;
1083 }
1084 iter++;
1085 }
1086 return result;
1087 }
1088
1089 /**
1090 * Returns number of sublists within the list.
1091 */
1092 unsigned int List::CountSubLists() {
1093 return CountSubChunks(CHUNK_ID_LIST);
1094 }
1095
1096 /**
1097 * Returns number of sublists within the list with list type
1098 * <i>\a ListType</i>
1099 */
1100 unsigned int List::CountSubLists(uint32_t ListType) {
1101 unsigned int result = 0;
1102 if (!pSubChunks) LoadSubChunks();
1103 ChunkList::iterator iter = pSubChunks->begin();
1104 ChunkList::iterator end = pSubChunks->end();
1105 while (iter != end) {
1106 if ((*iter)->GetChunkID() == CHUNK_ID_LIST) {
1107 List* l = (List*) *iter;
1108 if (l->GetListType() == ListType) result++;
1109 }
1110 iter++;
1111 }
1112 return result;
1113 }
1114
1115 /** @brief Creates a new sub chunk.
1116 *
1117 * Creates and adds a new sub chunk to this list chunk. Note that the
1118 * chunk's body size given by \a uiBodySize must be greater than zero.
1119 * You have to call File::Save() to make this change persistent to the
1120 * actual file and <b>before</b> performing any data write operations
1121 * on the new chunk!
1122 *
1123 * @param uiChunkID - chunk ID of the new chunk
1124 * @param uiBodySize - size of the new chunk's body, that is its actual
1125 * data size (without header)
1126 * @throws RIFF::Exception if \a uiBodySize equals zero
1127 */
1128 Chunk* List::AddSubChunk(uint32_t uiChunkID, uint uiBodySize) {
1129 if (uiBodySize == 0) throw Exception("Chunk body size must be at least 1 byte");
1130 if (!pSubChunks) LoadSubChunks();
1131 Chunk* pNewChunk = new Chunk(pFile, this, uiChunkID, 0);
1132 pSubChunks->push_back(pNewChunk);
1133 (*pSubChunksMap)[uiChunkID] = pNewChunk;
1134 pNewChunk->Resize(uiBodySize);
1135 return pNewChunk;
1136 }
1137
1138 /** @brief Moves a sub chunk.
1139 *
1140 * Moves a sub chunk from one position in a list to another
1141 * position in the same list. The pSrc chunk is placed before the
1142 * pDst chunk.
1143 *
1144 * @param pSrc - sub chunk to be moved
1145 * @param pDst - the position to move to. pSrc will be placed
1146 * before pDst. If pDst is 0, pSrc will be placed
1147 * last in list.
1148 */
1149 void List::MoveSubChunk(Chunk* pSrc, Chunk* pDst) {
1150 if (!pSubChunks) LoadSubChunks();
1151 pSubChunks->remove(pSrc);
1152 ChunkList::iterator iter = find(pSubChunks->begin(), pSubChunks->end(), pDst);
1153 pSubChunks->insert(iter, pSrc);
1154 }
1155
1156 /** @brief Creates a new list sub chunk.
1157 *
1158 * Creates and adds a new list sub chunk to this list chunk. Note that
1159 * you have to add sub chunks / sub list chunks to the new created chunk
1160 * <b>before</b> trying to make this change persisten to the actual
1161 * file with File::Save()!
1162 *
1163 * @param uiListType - list ID of the new list chunk
1164 */
1165 List* List::AddSubList(uint32_t uiListType) {
1166 if (!pSubChunks) LoadSubChunks();
1167 List* pNewListChunk = new List(pFile, this, uiListType);
1168 pSubChunks->push_back(pNewListChunk);
1169 (*pSubChunksMap)[CHUNK_ID_LIST] = pNewListChunk;
1170 return pNewListChunk;
1171 }
1172
1173 /** @brief Removes a sub chunk.
1174 *
1175 * Removes the sub chunk given by \a pSubChunk from this list and frees
1176 * it completely from RAM. The given chunk can either be a normal sub
1177 * chunk or a list sub chunk. In case the given chunk is a list chunk,
1178 * all its subchunks (if any) will be removed recursively as well. You
1179 * should call File::Save() to make this change persistent at any time.
1180 *
1181 * @param pSubChunk - sub chunk or sub list chunk to be removed
1182 */
1183 void List::DeleteSubChunk(Chunk* pSubChunk) {
1184 if (!pSubChunks) LoadSubChunks();
1185 pSubChunks->remove(pSubChunk);
1186 if ((*pSubChunksMap)[pSubChunk->GetChunkID()] == pSubChunk) {
1187 pSubChunksMap->erase(pSubChunk->GetChunkID());
1188 // try to find another chunk of the same chunk ID
1189 ChunkList::iterator iter = pSubChunks->begin();
1190 ChunkList::iterator end = pSubChunks->end();
1191 for (; iter != end; ++iter) {
1192 if ((*iter)->GetChunkID() == pSubChunk->GetChunkID()) {
1193 (*pSubChunksMap)[pSubChunk->GetChunkID()] = *iter;
1194 break; // we're done, stop search
1195 }
1196 }
1197 }
1198 delete pSubChunk;
1199 }
1200
1201 void List::ReadHeader(unsigned long fPos) {
1202 #if DEBUG
1203 std::cout << "List::Readheader(ulong) ";
1204 #endif // DEBUG
1205 Chunk::ReadHeader(fPos);
1206 NewChunkSize = CurrentChunkSize -= 4;
1207 #if POSIX
1208 lseek(pFile->hFileRead, fPos + CHUNK_HEADER_SIZE, SEEK_SET);
1209 read(pFile->hFileRead, &ListType, 4);
1210 #elif defined(WIN32)
1211 SetFilePointer(pFile->hFileRead, fPos + CHUNK_HEADER_SIZE, NULL/*32 bit*/, FILE_BEGIN);
1212 DWORD dwBytesRead;
1213 ReadFile(pFile->hFileRead, &ListType, 4, &dwBytesRead, NULL);
1214 #else
1215 fseek(pFile->hFileRead, fPos + CHUNK_HEADER_SIZE, SEEK_SET);
1216 fread(&ListType, 4, 1, pFile->hFileRead);
1217 #endif // POSIX
1218 #if DEBUG
1219 std::cout << "listType=" << convertToString(ListType) << std::endl;
1220 #endif // DEBUG
1221 if (!pFile->bEndianNative) {
1222 //swapBytes_32(&ListType);
1223 }
1224 }
1225
1226 void List::WriteHeader(unsigned long fPos) {
1227 // the four list type bytes officially belong the chunk's body in the RIFF format
1228 NewChunkSize += 4;
1229 Chunk::WriteHeader(fPos);
1230 NewChunkSize -= 4; // just revert the +4 incrementation
1231 #if POSIX
1232 lseek(pFile->hFileWrite, fPos + CHUNK_HEADER_SIZE, SEEK_SET);
1233 write(pFile->hFileWrite, &ListType, 4);
1234 #elif defined(WIN32)
1235 SetFilePointer(pFile->hFileWrite, fPos + CHUNK_HEADER_SIZE, NULL/*32 bit*/, FILE_BEGIN);
1236 DWORD dwBytesWritten;
1237 WriteFile(pFile->hFileWrite, &ListType, 4, &dwBytesWritten, NULL);
1238 #else
1239 fseek(pFile->hFileWrite, fPos + CHUNK_HEADER_SIZE, SEEK_SET);
1240 fwrite(&ListType, 4, 1, pFile->hFileWrite);
1241 #endif // POSIX
1242 }
1243
1244 void List::LoadSubChunks() {
1245 #if DEBUG
1246 std::cout << "List::LoadSubChunks()";
1247 #endif // DEBUG
1248 if (!pSubChunks) {
1249 pSubChunks = new ChunkList();
1250 pSubChunksMap = new ChunkMap();
1251 if (!pFile->hFileRead) return;
1252 unsigned long uiOriginalPos = GetPos();
1253 SetPos(0); // jump to beginning of list chunk body
1254 while (RemainingBytes() >= CHUNK_HEADER_SIZE) {
1255 Chunk* ck;
1256 uint32_t ckid;
1257 Read(&ckid, 4, 1);
1258 #if DEBUG
1259 std::cout << " ckid=" << convertToString(ckid) << std::endl;
1260 #endif // DEBUG
1261 if (ckid == CHUNK_ID_LIST) {
1262 ck = new RIFF::List(pFile, ulStartPos + ulPos - 4, this);
1263 SetPos(ck->GetSize() + LIST_HEADER_SIZE - 4, RIFF::stream_curpos);
1264 }
1265 else { // simple chunk
1266 ck = new RIFF::Chunk(pFile, ulStartPos + ulPos - 4, this);
1267 SetPos(ck->GetSize() + CHUNK_HEADER_SIZE - 4, RIFF::stream_curpos);
1268 }
1269 pSubChunks->push_back(ck);
1270 (*pSubChunksMap)[ckid] = ck;
1271 if (GetPos() % 2 != 0) SetPos(1, RIFF::stream_curpos); // jump over pad byte
1272 }
1273 SetPos(uiOriginalPos); // restore position before this call
1274 }
1275 }
1276
1277 void List::LoadSubChunksRecursively() {
1278 for (List* pList = GetFirstSubList(); pList; pList = GetNextSubList())
1279 pList->LoadSubChunksRecursively();
1280 }
1281
1282 /** @brief Write list chunk persistently e.g. to disk.
1283 *
1284 * Stores the list chunk persistently to its actual "physical" file. All
1285 * subchunks (including sub list chunks) will be stored recursively as
1286 * well.
1287 *
1288 * @param ulWritePos - position within the "physical" file where this
1289 * list chunk should be written to
1290 * @param ulCurrentDataOffset - offset of current (old) data within
1291 * the file
1292 * @returns new write position in the "physical" file, that is
1293 * \a ulWritePos incremented by this list chunk's new size
1294 * (including its header size of course)
1295 */
1296 unsigned long List::WriteChunk(unsigned long ulWritePos, unsigned long ulCurrentDataOffset) {
1297 const unsigned long ulOriginalPos = ulWritePos;
1298 ulWritePos += LIST_HEADER_SIZE;
1299
1300 if (pFile->Mode != stream_mode_read_write)
1301 throw Exception("Cannot write list chunk, file has to be opened in read+write mode");
1302
1303 // write all subchunks (including sub list chunks) recursively
1304 if (pSubChunks) {
1305 for (ChunkList::iterator iter = pSubChunks->begin(), end = pSubChunks->end(); iter != end; ++iter) {
1306 ulWritePos = (*iter)->WriteChunk(ulWritePos, ulCurrentDataOffset);
1307 }
1308 }
1309
1310 // update this list chunk's header
1311 CurrentChunkSize = NewChunkSize = ulWritePos - ulOriginalPos - LIST_HEADER_SIZE;
1312 WriteHeader(ulOriginalPos);
1313
1314 // offset of this list chunk in new written file may have changed
1315 ulStartPos = ulOriginalPos + LIST_HEADER_SIZE;
1316
1317 return ulWritePos;
1318 }
1319
1320 void List::__resetPos() {
1321 Chunk::__resetPos();
1322 if (pSubChunks) {
1323 for (ChunkList::iterator iter = pSubChunks->begin(), end = pSubChunks->end(); iter != end; ++iter) {
1324 (*iter)->__resetPos();
1325 }
1326 }
1327 }
1328
1329 /**
1330 * Returns string representation of the lists's id
1331 */
1332 String List::GetListTypeString() {
1333 return convertToString(ListType);
1334 }
1335
1336
1337
1338 // *************** File ***************
1339 // *
1340
1341 /** @brief Create new RIFF file.
1342 *
1343 * Use this constructor if you want to create a new RIFF file completely
1344 * "from scratch". Note: there must be no empty chunks or empty list
1345 * chunks when trying to make the new RIFF file persistent with Save()!
1346 *
1347 * @param FileType - four-byte identifier of the RIFF file type
1348 * @see AddSubChunk(), AddSubList()
1349 */
1350 File::File(uint32_t FileType) : List(this) {
1351 #if defined(WIN32)
1352 hFileRead = hFileWrite = INVALID_HANDLE_VALUE;
1353 #else
1354 hFileRead = hFileWrite = 0;
1355 #endif
1356 Mode = stream_mode_closed;
1357 bEndianNative = true;
1358 ulStartPos = RIFF_HEADER_SIZE;
1359 ListType = FileType;
1360 }
1361
1362 /** @brief Load existing RIFF file.
1363 *
1364 * Loads an existing RIFF file with all its chunks.
1365 *
1366 * @param path - path and file name of the RIFF file to open
1367 * @throws RIFF::Exception if error occured while trying to load the
1368 * given RIFF file
1369 */
1370 File::File(const String& path) : List(this), Filename(path) {
1371 #if DEBUG
1372 std::cout << "File::File("<<path<<")" << std::endl;
1373 #endif // DEBUG
1374 bEndianNative = true;
1375 #if POSIX
1376 hFileRead = hFileWrite = open(path.c_str(), O_RDONLY | O_NONBLOCK);
1377 if (hFileRead <= 0) {
1378 hFileRead = hFileWrite = 0;
1379 throw RIFF::Exception("Can't open \"" + path + "\"");
1380 }
1381 #elif defined(WIN32)
1382 hFileRead = hFileWrite = CreateFile(
1383 path.c_str(), GENERIC_READ,
1384 FILE_SHARE_READ | FILE_SHARE_WRITE,
1385 NULL, OPEN_EXISTING,
1386 FILE_ATTRIBUTE_NORMAL, NULL
1387 );
1388 if (hFileRead == INVALID_HANDLE_VALUE) {
1389 hFileRead = hFileWrite = INVALID_HANDLE_VALUE;
1390 throw RIFF::Exception("Can't open \"" + path + "\"");
1391 }
1392 #else
1393 hFileRead = hFileWrite = fopen(path.c_str(), "rb");
1394 if (!hFileRead) throw RIFF::Exception("Can't open \"" + path + "\"");
1395 #endif // POSIX
1396 Mode = stream_mode_read;
1397 ulStartPos = RIFF_HEADER_SIZE;
1398 ReadHeader(0);
1399 if (ChunkID != CHUNK_ID_RIFF) {
1400 throw RIFF::Exception("Not a RIFF file");
1401 }
1402 }
1403
1404 String File::GetFileName() {
1405 return Filename;
1406 }
1407
1408 stream_mode_t File::GetMode() {
1409 return Mode;
1410 }
1411
1412 /** @brief Change file access mode.
1413 *
1414 * Changes files access mode either to read-only mode or to read/write
1415 * mode.
1416 *
1417 * @param NewMode - new file access mode
1418 * @returns true if mode was changed, false if current mode already
1419 * equals new mode
1420 * @throws RIFF::Exception if new file access mode is unknown
1421 */
1422 bool File::SetMode(stream_mode_t NewMode) {
1423 if (NewMode != Mode) {
1424 switch (NewMode) {
1425 case stream_mode_read:
1426 #if POSIX
1427 if (hFileRead) close(hFileRead);
1428 hFileRead = hFileWrite = open(Filename.c_str(), O_RDONLY | O_NONBLOCK);
1429 if (hFileRead < 0) {
1430 hFileRead = hFileWrite = 0;
1431 throw Exception("Could not (re)open file \"" + Filename + "\" in read mode");
1432 }
1433 #elif defined(WIN32)
1434 if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
1435 hFileRead = hFileWrite = CreateFile(
1436 Filename.c_str(), GENERIC_READ,
1437 FILE_SHARE_READ | FILE_SHARE_WRITE,
1438 NULL, OPEN_EXISTING,
1439 FILE_ATTRIBUTE_NORMAL, NULL
1440 );
1441 if (hFileRead == INVALID_HANDLE_VALUE) {
1442 hFileRead = hFileWrite = INVALID_HANDLE_VALUE;
1443 throw Exception("Could not (re)open file \"" + Filename + "\" in read mode");
1444 }
1445 #else
1446 if (hFileRead) fclose(hFileRead);
1447 hFileRead = hFileWrite = fopen(Filename.c_str(), "rb");
1448 if (!hFileRead) throw Exception("Could not (re)open file \"" + Filename + "\" in read mode");
1449 #endif
1450 __resetPos(); // reset read/write position of ALL 'Chunk' objects
1451 break;
1452 case stream_mode_read_write:
1453 #if POSIX
1454 if (hFileRead) close(hFileRead);
1455 hFileRead = hFileWrite = open(Filename.c_str(), O_RDWR | O_NONBLOCK);
1456 if (hFileRead < 0) {
1457 hFileRead = hFileWrite = open(Filename.c_str(), O_RDONLY | O_NONBLOCK);
1458 throw Exception("Could not open file \"" + Filename + "\" in read+write mode");
1459 }
1460 #elif defined(WIN32)
1461 if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
1462 hFileRead = hFileWrite = CreateFile(
1463 Filename.c_str(),
1464 GENERIC_READ | GENERIC_WRITE,
1465 FILE_SHARE_READ,
1466 NULL, OPEN_ALWAYS,
1467 FILE_ATTRIBUTE_NORMAL, NULL
1468 );
1469 if (hFileRead == INVALID_HANDLE_VALUE) {
1470 hFileRead = hFileWrite = CreateFile(
1471 Filename.c_str(), GENERIC_READ,
1472 FILE_SHARE_READ | FILE_SHARE_WRITE,
1473 NULL, OPEN_EXISTING,
1474 FILE_ATTRIBUTE_NORMAL, NULL
1475 );
1476 throw Exception("Could not (re)open file \"" + Filename + "\" in read mode");
1477 }
1478 #else
1479 if (hFileRead) fclose(hFileRead);
1480 hFileRead = hFileWrite = fopen(Filename.c_str(), "r+b");
1481 if (!hFileRead) {
1482 hFileRead = hFileWrite = fopen(Filename.c_str(), "rb");
1483 throw Exception("Could not open file \"" + Filename + "\" in read+write mode");
1484 }
1485 #endif
1486 __resetPos(); // reset read/write position of ALL 'Chunk' objects
1487 break;
1488 case stream_mode_closed:
1489 #if POSIX
1490 if (hFileRead) close(hFileRead);
1491 if (hFileWrite) close(hFileWrite);
1492 #elif defined(WIN32)
1493 if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
1494 if (hFileWrite != INVALID_HANDLE_VALUE) CloseHandle(hFileWrite);
1495 #else
1496 if (hFileRead) fclose(hFileRead);
1497 if (hFileWrite) fclose(hFileWrite);
1498 #endif
1499 hFileRead = hFileWrite = 0;
1500 break;
1501 default:
1502 throw Exception("Unknown file access mode");
1503 }
1504 Mode = NewMode;
1505 return true;
1506 }
1507 return false;
1508 }
1509
1510 /** @brief Save changes to same file.
1511 *
1512 * Make all changes of all chunks persistent by writing them to the
1513 * actual (same) file. The file might temporarily grow to a higher size
1514 * than it will have at the end of the saving process, in case chunks
1515 * were grown.
1516 *
1517 * @throws RIFF::Exception if there is an empty chunk or empty list
1518 * chunk or any kind of IO error occured
1519 */
1520 void File::Save() {
1521 // make sure the RIFF tree is built (from the original file)
1522 LoadSubChunksRecursively();
1523
1524 // reopen file in write mode
1525 SetMode(stream_mode_read_write);
1526
1527 // to be able to save the whole file without loading everything into
1528 // RAM and without having to store the data in a temporary file, we
1529 // enlarge the file with the sum of all _positive_ chunk size
1530 // changes, move current data towards the end of the file with the
1531 // calculated sum and finally update / rewrite the file by copying
1532 // the old data back to the right position at the beginning of the file
1533
1534 // first we sum up all positive chunk size changes (and skip all negative ones)
1535 unsigned long ulPositiveSizeDiff = 0;
1536 for (ChunkList::iterator iter = ResizedChunks.begin(), end = ResizedChunks.end(); iter != end; ++iter) {
1537 if ((*iter)->GetNewSize() == 0) {
1538 // just to make the exception message a bit more verbose: resolve the chunk's path
1539 String sChunkPath;
1540 for (Chunk* pChunk = *iter; pChunk; pChunk = pChunk->GetParent()) {
1541 if (pChunk->GetChunkID() == CHUNK_ID_LIST) {
1542 List* pList = (List*) pChunk;
1543 sChunkPath = "->'" + pList->GetListTypeString() + "'" + sChunkPath;
1544 } else {
1545 sChunkPath = "->'" + pChunk->GetChunkIDString() + "'" + sChunkPath;
1546 }
1547 }
1548 throw Exception("There is at least one empty chunk (zero size): " + sChunkPath);
1549 }
1550 if ((*iter)->GetNewSize() + 1L > (*iter)->GetSize()) {
1551 unsigned long ulDiff = (*iter)->GetNewSize() - (*iter)->GetSize() + 1L; // +1 in case we have to add a pad byte
1552 ulPositiveSizeDiff += ulDiff;
1553 }
1554 }
1555
1556 unsigned long ulWorkingFileSize = GetFileSize();
1557
1558 // if there are positive size changes...
1559 if (ulPositiveSizeDiff > 0) {
1560 // ... we enlarge this file first ...
1561 ulWorkingFileSize += ulPositiveSizeDiff;
1562 ResizeFile(ulWorkingFileSize);
1563 // ... and move current data by the same amount towards end of file.
1564 int8_t* pCopyBuffer = new int8_t[4096];
1565 const unsigned long ulFileSize = GetSize() + RIFF_HEADER_SIZE;
1566 #if defined(WIN32)
1567 DWORD iBytesMoved = 1; // we have to pass it via pointer to the Windows API, thus the correct size must be ensured
1568 #else
1569 int iBytesMoved = 1;
1570 #endif
1571 for (unsigned long ulPos = 0; iBytesMoved > 0; ulPos += iBytesMoved) {
1572 const unsigned long ulToMove = ulFileSize - ulPos;
1573 iBytesMoved = (ulToMove < 4096) ? ulToMove : 4096;
1574 #if POSIX
1575 lseek(hFileRead, ulPos, SEEK_SET);
1576 iBytesMoved = read(hFileRead, pCopyBuffer, iBytesMoved);
1577 lseek(hFileWrite, ulPos + ulPositiveSizeDiff, SEEK_SET);
1578 iBytesMoved = write(hFileWrite, pCopyBuffer, iBytesMoved);
1579 #elif defined(WIN32)
1580 SetFilePointer(hFileRead, ulPos, NULL/*32 bit*/, FILE_BEGIN);
1581 ReadFile(hFileRead, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
1582 SetFilePointer(hFileWrite, ulPos + ulPositiveSizeDiff, NULL/*32 bit*/, FILE_BEGIN);
1583 WriteFile(hFileWrite, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
1584 #else
1585 fseek(hFileRead, ulPos, SEEK_SET);
1586 iBytesMoved = fread(pCopyBuffer, 1, iBytesMoved, hFileRead);
1587 fseek(hFileWrite, ulPos + ulPositiveSizeDiff, SEEK_SET);
1588 iBytesMoved = fwrite(pCopyBuffer, 1, iBytesMoved, hFileWrite);
1589 #endif
1590 }
1591 delete[] pCopyBuffer;
1592 if (iBytesMoved < 0) throw Exception("Could not modify file while trying to enlarge it");
1593 }
1594
1595 // rebuild / rewrite complete RIFF tree
1596 unsigned long ulTotalSize = WriteChunk(0, ulPositiveSizeDiff);
1597 unsigned long ulActualSize = __GetFileSize(hFileWrite);
1598
1599 // resize file to the final size
1600 if (ulTotalSize < ulActualSize) ResizeFile(ulTotalSize);
1601
1602 // forget all resized chunks
1603 ResizedChunks.clear();
1604 }
1605
1606 /** @brief Save changes to another file.
1607 *
1608 * Make all changes of all chunks persistent by writing them to another
1609 * file. <b>Caution:</b> this method is optimized for writing to
1610 * <b>another</b> file, do not use it to save the changes to the same
1611 * file! Use File::Save() in that case instead! Ignoring this might
1612 * result in a corrupted file, especially in case chunks were resized!
1613 *
1614 * After calling this method, this File object will be associated with
1615 * the new file (given by \a path) afterwards.
1616 *
1617 * @param path - path and file name where everything should be written to
1618 */
1619 void File::Save(const String& path) {
1620 //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
1621
1622 // make sure the RIFF tree is built (from the original file)
1623 LoadSubChunksRecursively();
1624
1625 if (Filename.length() > 0) SetMode(stream_mode_read);
1626 // open the other (new) file for writing and truncate it to zero size
1627 #if POSIX
1628 hFileWrite = open(path.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP);
1629 if (hFileWrite < 0) {
1630 hFileWrite = hFileRead;
1631 throw Exception("Could not open file \"" + path + "\" for writing");
1632 }
1633 #elif defined(WIN32)
1634 hFileWrite = CreateFile(
1635 path.c_str(), GENERIC_WRITE, FILE_SHARE_READ,
1636 NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL
1637 );
1638 if (hFileWrite == INVALID_HANDLE_VALUE) {
1639 hFileWrite = hFileRead;
1640 throw Exception("Could not open file \"" + path + "\" for writing");
1641 }
1642 #else
1643 hFileWrite = fopen(path.c_str(), "w+b");
1644 if (!hFileWrite) {
1645 hFileWrite = hFileRead;
1646 throw Exception("Could not open file \"" + path + "\" for writing");
1647 }
1648 #endif // POSIX
1649 Mode = stream_mode_read_write;
1650
1651 // write complete RIFF tree to the other (new) file
1652 unsigned long ulTotalSize = WriteChunk(0, 0);
1653 unsigned long ulActualSize = __GetFileSize(hFileWrite);
1654
1655 // resize file to the final size (if the file was originally larger)
1656 if (ulTotalSize < ulActualSize) ResizeFile(ulTotalSize);
1657
1658 // forget all resized chunks
1659 ResizedChunks.clear();
1660
1661 if (Filename.length() > 0) {
1662 #if POSIX
1663 close(hFileWrite);
1664 #elif defined(WIN32)
1665 CloseHandle(hFileWrite);
1666 #else
1667 fclose(hFileWrite);
1668 #endif
1669 hFileWrite = hFileRead;
1670 }
1671
1672 // associate new file with this File object from now on
1673 Filename = path;
1674 Mode = (stream_mode_t) -1; // Just set it to an undefined mode ...
1675 SetMode(stream_mode_read_write); // ... so SetMode() has to reopen the file handles.
1676 }
1677
1678 void File::ResizeFile(unsigned long ulNewSize) {
1679 #if POSIX
1680 if (ftruncate(hFileWrite, ulNewSize) < 0)
1681 throw Exception("Could not resize file \"" + Filename + "\"");
1682 #elif defined(WIN32)
1683 if (
1684 SetFilePointer(hFileWrite, ulNewSize, NULL/*32 bit*/, FILE_BEGIN) == INVALID_SET_FILE_POINTER ||
1685 !SetEndOfFile(hFileWrite)
1686 ) throw Exception("Could not resize file \"" + Filename + "\"");
1687 #else
1688 # error Sorry, this version of libgig only supports POSIX and Windows systems yet.
1689 # error Reason: portable implementation of RIFF::File::ResizeFile() is missing (yet)!
1690 #endif
1691 }
1692
1693 File::~File() {
1694 #if DEBUG
1695 std::cout << "File::~File()" << std::endl;
1696 #endif // DEBUG
1697 #if POSIX
1698 if (hFileRead) close(hFileRead);
1699 #elif defined(WIN32)
1700 if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
1701 #else
1702 if (hFileRead) fclose(hFileRead);
1703 #endif // POSIX
1704 }
1705
1706 void File::LogAsResized(Chunk* pResizedChunk) {
1707 ResizedChunks.push_back(pResizedChunk);
1708 }
1709
1710 void File::UnlogResized(Chunk* pResizedChunk) {
1711 ResizedChunks.remove(pResizedChunk);
1712 }
1713
1714 unsigned long File::GetFileSize() {
1715 return __GetFileSize(hFileRead);
1716 }
1717
1718 #if POSIX
1719 unsigned long File::__GetFileSize(int hFile) {
1720 struct stat filestat;
1721 fstat(hFile, &filestat);
1722 long size = filestat.st_size;
1723 return size;
1724 }
1725 #elif defined(WIN32)
1726 unsigned long File::__GetFileSize(HANDLE hFile) {
1727 DWORD dwSize = ::GetFileSize(hFile, NULL /*32bit*/);
1728 if (dwSize == INVALID_FILE_SIZE)
1729 throw Exception("Windows FS error: could not determine file size");
1730 return dwSize;
1731 }
1732 #else // standard C functions
1733 unsigned long File::__GetFileSize(FILE* hFile) {
1734 long curpos = ftell(hFile);
1735 fseek(hFile, 0, SEEK_END);
1736 long size = ftell(hFile);
1737 fseek(hFile, curpos, SEEK_SET);
1738 return size;
1739 }
1740 #endif
1741
1742
1743 // *************** Exception ***************
1744 // *
1745
1746 void Exception::PrintMessage() {
1747 std::cout << "RIFF::Exception: " << Message << std::endl;
1748 }
1749
1750
1751 // *************** functions ***************
1752 // *
1753
1754 /**
1755 * Returns the name of this C++ library. This is usually "libgig" of
1756 * course. This call is equivalent to DLS::libraryName() and
1757 * gig::libraryName().
1758 */
1759 String libraryName() {
1760 return PACKAGE;
1761 }
1762
1763 /**
1764 * Returns version of this C++ library. This call is equivalent to
1765 * DLS::libraryVersion() and gig::libraryVersion().
1766 */
1767 String libraryVersion() {
1768 return VERSION;
1769 }
1770
1771 } // namespace RIFF

  ViewVC Help
Powered by ViewVC