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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1713 - (show annotations) (download)
Thu Mar 6 20:42:22 2008 UTC (16 years, 1 month ago) by persson
File size: 74915 byte(s)
* fixed compilation with gcc 4.3

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

  ViewVC Help
Powered by ViewVC