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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1678 - (show annotations) (download)
Sun Feb 10 16:07:22 2008 UTC (16 years, 1 month ago) by persson
File size: 74894 byte(s)
* bugfix: saving to the same file after the file size had been
  increased made the file corrupt (#82)
* bugfix: removed another iterator invalidation in DeleteSample
* changed the functions for midi rules, to get rid of the iterator

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

  ViewVC Help
Powered by ViewVC