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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1459 - (show annotations) (download)
Fri Oct 26 10:58:41 2007 UTC (16 years, 5 months ago) by schoenebeck
File size: 74879 byte(s)
* avoid Windows to perform unnecessary file stream caching which would
  decrease disk streaming performance on Windows systems otherwise

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 if (CurrentChunkSize != NewChunkSize) 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 return pNewChunk;
1156 }
1157
1158 /** @brief Moves a sub chunk.
1159 *
1160 * Moves a sub chunk from one position in a list to another
1161 * position in the same list. The pSrc chunk is placed before the
1162 * pDst chunk.
1163 *
1164 * @param pSrc - sub chunk to be moved
1165 * @param pDst - the position to move to. pSrc will be placed
1166 * before pDst. If pDst is 0, pSrc will be placed
1167 * last in list.
1168 */
1169 void List::MoveSubChunk(Chunk* pSrc, Chunk* pDst) {
1170 if (!pSubChunks) LoadSubChunks();
1171 pSubChunks->remove(pSrc);
1172 ChunkList::iterator iter = find(pSubChunks->begin(), pSubChunks->end(), pDst);
1173 pSubChunks->insert(iter, pSrc);
1174 }
1175
1176 /** @brief Creates a new list sub chunk.
1177 *
1178 * Creates and adds a new list sub chunk to this list chunk. Note that
1179 * you have to add sub chunks / sub list chunks to the new created chunk
1180 * <b>before</b> trying to make this change persisten to the actual
1181 * file with File::Save()!
1182 *
1183 * @param uiListType - list ID of the new list chunk
1184 */
1185 List* List::AddSubList(uint32_t uiListType) {
1186 if (!pSubChunks) LoadSubChunks();
1187 List* pNewListChunk = new List(pFile, this, uiListType);
1188 pSubChunks->push_back(pNewListChunk);
1189 (*pSubChunksMap)[CHUNK_ID_LIST] = pNewListChunk;
1190 return pNewListChunk;
1191 }
1192
1193 /** @brief Removes a sub chunk.
1194 *
1195 * Removes the sub chunk given by \a pSubChunk from this list and frees
1196 * it completely from RAM. The given chunk can either be a normal sub
1197 * chunk or a list sub chunk. In case the given chunk is a list chunk,
1198 * all its subchunks (if any) will be removed recursively as well. You
1199 * should call File::Save() to make this change persistent at any time.
1200 *
1201 * @param pSubChunk - sub chunk or sub list chunk to be removed
1202 */
1203 void List::DeleteSubChunk(Chunk* pSubChunk) {
1204 if (!pSubChunks) LoadSubChunks();
1205 pSubChunks->remove(pSubChunk);
1206 if ((*pSubChunksMap)[pSubChunk->GetChunkID()] == pSubChunk) {
1207 pSubChunksMap->erase(pSubChunk->GetChunkID());
1208 // try to find another chunk of the same chunk ID
1209 ChunkList::iterator iter = pSubChunks->begin();
1210 ChunkList::iterator end = pSubChunks->end();
1211 for (; iter != end; ++iter) {
1212 if ((*iter)->GetChunkID() == pSubChunk->GetChunkID()) {
1213 (*pSubChunksMap)[pSubChunk->GetChunkID()] = *iter;
1214 break; // we're done, stop search
1215 }
1216 }
1217 }
1218 delete pSubChunk;
1219 }
1220
1221 void List::ReadHeader(unsigned long fPos) {
1222 #if DEBUG
1223 std::cout << "List::Readheader(ulong) ";
1224 #endif // DEBUG
1225 Chunk::ReadHeader(fPos);
1226 NewChunkSize = CurrentChunkSize -= 4;
1227 #if POSIX
1228 lseek(pFile->hFileRead, fPos + CHUNK_HEADER_SIZE, SEEK_SET);
1229 read(pFile->hFileRead, &ListType, 4);
1230 #elif defined(WIN32)
1231 SetFilePointer(pFile->hFileRead, fPos + CHUNK_HEADER_SIZE, NULL/*32 bit*/, FILE_BEGIN);
1232 DWORD dwBytesRead;
1233 ReadFile(pFile->hFileRead, &ListType, 4, &dwBytesRead, NULL);
1234 #else
1235 fseek(pFile->hFileRead, fPos + CHUNK_HEADER_SIZE, SEEK_SET);
1236 fread(&ListType, 4, 1, pFile->hFileRead);
1237 #endif // POSIX
1238 #if DEBUG
1239 std::cout << "listType=" << convertToString(ListType) << std::endl;
1240 #endif // DEBUG
1241 if (!pFile->bEndianNative) {
1242 //swapBytes_32(&ListType);
1243 }
1244 }
1245
1246 void List::WriteHeader(unsigned long fPos) {
1247 // the four list type bytes officially belong the chunk's body in the RIFF format
1248 NewChunkSize += 4;
1249 Chunk::WriteHeader(fPos);
1250 NewChunkSize -= 4; // just revert the +4 incrementation
1251 #if POSIX
1252 lseek(pFile->hFileWrite, fPos + CHUNK_HEADER_SIZE, SEEK_SET);
1253 write(pFile->hFileWrite, &ListType, 4);
1254 #elif defined(WIN32)
1255 SetFilePointer(pFile->hFileWrite, fPos + CHUNK_HEADER_SIZE, NULL/*32 bit*/, FILE_BEGIN);
1256 DWORD dwBytesWritten;
1257 WriteFile(pFile->hFileWrite, &ListType, 4, &dwBytesWritten, NULL);
1258 #else
1259 fseek(pFile->hFileWrite, fPos + CHUNK_HEADER_SIZE, SEEK_SET);
1260 fwrite(&ListType, 4, 1, pFile->hFileWrite);
1261 #endif // POSIX
1262 }
1263
1264 void List::LoadSubChunks() {
1265 #if DEBUG
1266 std::cout << "List::LoadSubChunks()";
1267 #endif // DEBUG
1268 if (!pSubChunks) {
1269 pSubChunks = new ChunkList();
1270 pSubChunksMap = new ChunkMap();
1271 #if defined(WIN32)
1272 if (pFile->hFileRead == INVALID_HANDLE_VALUE) return;
1273 #else
1274 if (!pFile->hFileRead) return;
1275 #endif
1276 unsigned long uiOriginalPos = GetPos();
1277 SetPos(0); // jump to beginning of list chunk body
1278 while (RemainingBytes() >= CHUNK_HEADER_SIZE) {
1279 Chunk* ck;
1280 uint32_t ckid;
1281 Read(&ckid, 4, 1);
1282 #if DEBUG
1283 std::cout << " ckid=" << convertToString(ckid) << std::endl;
1284 #endif // DEBUG
1285 if (ckid == CHUNK_ID_LIST) {
1286 ck = new RIFF::List(pFile, ulStartPos + ulPos - 4, this);
1287 SetPos(ck->GetSize() + LIST_HEADER_SIZE - 4, RIFF::stream_curpos);
1288 }
1289 else { // simple chunk
1290 ck = new RIFF::Chunk(pFile, ulStartPos + ulPos - 4, this);
1291 SetPos(ck->GetSize() + CHUNK_HEADER_SIZE - 4, RIFF::stream_curpos);
1292 }
1293 pSubChunks->push_back(ck);
1294 (*pSubChunksMap)[ckid] = ck;
1295 if (GetPos() % 2 != 0) SetPos(1, RIFF::stream_curpos); // jump over pad byte
1296 }
1297 SetPos(uiOriginalPos); // restore position before this call
1298 }
1299 }
1300
1301 void List::LoadSubChunksRecursively() {
1302 for (List* pList = GetFirstSubList(); pList; pList = GetNextSubList())
1303 pList->LoadSubChunksRecursively();
1304 }
1305
1306 /** @brief Write list chunk persistently e.g. to disk.
1307 *
1308 * Stores the list chunk persistently to its actual "physical" file. All
1309 * subchunks (including sub list chunks) will be stored recursively as
1310 * well.
1311 *
1312 * @param ulWritePos - position within the "physical" file where this
1313 * list chunk should be written to
1314 * @param ulCurrentDataOffset - offset of current (old) data within
1315 * the file
1316 * @returns new write position in the "physical" file, that is
1317 * \a ulWritePos incremented by this list chunk's new size
1318 * (including its header size of course)
1319 */
1320 unsigned long List::WriteChunk(unsigned long ulWritePos, unsigned long ulCurrentDataOffset) {
1321 const unsigned long ulOriginalPos = ulWritePos;
1322 ulWritePos += LIST_HEADER_SIZE;
1323
1324 if (pFile->Mode != stream_mode_read_write)
1325 throw Exception("Cannot write list chunk, file has to be opened in read+write mode");
1326
1327 // write all subchunks (including sub list chunks) recursively
1328 if (pSubChunks) {
1329 for (ChunkList::iterator iter = pSubChunks->begin(), end = pSubChunks->end(); iter != end; ++iter) {
1330 ulWritePos = (*iter)->WriteChunk(ulWritePos, ulCurrentDataOffset);
1331 }
1332 }
1333
1334 // update this list chunk's header
1335 CurrentChunkSize = NewChunkSize = ulWritePos - ulOriginalPos - LIST_HEADER_SIZE;
1336 WriteHeader(ulOriginalPos);
1337
1338 // offset of this list chunk in new written file may have changed
1339 ulStartPos = ulOriginalPos + LIST_HEADER_SIZE;
1340
1341 return ulWritePos;
1342 }
1343
1344 void List::__resetPos() {
1345 Chunk::__resetPos();
1346 if (pSubChunks) {
1347 for (ChunkList::iterator iter = pSubChunks->begin(), end = pSubChunks->end(); iter != end; ++iter) {
1348 (*iter)->__resetPos();
1349 }
1350 }
1351 }
1352
1353 /**
1354 * Returns string representation of the lists's id
1355 */
1356 String List::GetListTypeString() {
1357 return convertToString(ListType);
1358 }
1359
1360
1361
1362 // *************** File ***************
1363 // *
1364
1365 /** @brief Create new RIFF file.
1366 *
1367 * Use this constructor if you want to create a new RIFF file completely
1368 * "from scratch". Note: there must be no empty chunks or empty list
1369 * chunks when trying to make the new RIFF file persistent with Save()!
1370 *
1371 * Note: by default, the RIFF file will be saved in native endian
1372 * format; that is, as a RIFF file on little-endian machines and
1373 * as a RIFX file on big-endian. To change this behaviour, call
1374 * SetByteOrder() before calling Save().
1375 *
1376 * @param FileType - four-byte identifier of the RIFF file type
1377 * @see AddSubChunk(), AddSubList(), SetByteOrder()
1378 */
1379 File::File(uint32_t FileType) : List(this) {
1380 #if defined(WIN32)
1381 hFileRead = hFileWrite = INVALID_HANDLE_VALUE;
1382 #else
1383 hFileRead = hFileWrite = 0;
1384 #endif
1385 Mode = stream_mode_closed;
1386 bEndianNative = true;
1387 ulStartPos = RIFF_HEADER_SIZE;
1388 ListType = FileType;
1389 }
1390
1391 /** @brief Load existing RIFF file.
1392 *
1393 * Loads an existing RIFF file with all its chunks.
1394 *
1395 * @param path - path and file name of the RIFF file to open
1396 * @throws RIFF::Exception if error occured while trying to load the
1397 * given RIFF file
1398 */
1399 File::File(const String& path) : List(this), Filename(path) {
1400 #if DEBUG
1401 std::cout << "File::File("<<path<<")" << std::endl;
1402 #endif // DEBUG
1403 bEndianNative = true;
1404 #if POSIX
1405 hFileRead = hFileWrite = open(path.c_str(), O_RDONLY | O_NONBLOCK);
1406 if (hFileRead <= 0) {
1407 hFileRead = hFileWrite = 0;
1408 throw RIFF::Exception("Can't open \"" + path + "\"");
1409 }
1410 #elif defined(WIN32)
1411 hFileRead = hFileWrite = CreateFile(
1412 path.c_str(), GENERIC_READ,
1413 FILE_SHARE_READ | FILE_SHARE_WRITE,
1414 NULL, OPEN_EXISTING,
1415 FILE_ATTRIBUTE_NORMAL |
1416 FILE_FLAG_RANDOM_ACCESS, NULL
1417 );
1418 if (hFileRead == INVALID_HANDLE_VALUE) {
1419 hFileRead = hFileWrite = INVALID_HANDLE_VALUE;
1420 throw RIFF::Exception("Can't open \"" + path + "\"");
1421 }
1422 #else
1423 hFileRead = hFileWrite = fopen(path.c_str(), "rb");
1424 if (!hFileRead) throw RIFF::Exception("Can't open \"" + path + "\"");
1425 #endif // POSIX
1426 Mode = stream_mode_read;
1427 ulStartPos = RIFF_HEADER_SIZE;
1428 ReadHeader(0);
1429 if (ChunkID != CHUNK_ID_RIFF) {
1430 throw RIFF::Exception("Not a RIFF file");
1431 }
1432 }
1433
1434 String File::GetFileName() {
1435 return Filename;
1436 }
1437
1438 stream_mode_t File::GetMode() {
1439 return Mode;
1440 }
1441
1442 /** @brief Change file access mode.
1443 *
1444 * Changes files access mode either to read-only mode or to read/write
1445 * mode.
1446 *
1447 * @param NewMode - new file access mode
1448 * @returns true if mode was changed, false if current mode already
1449 * equals new mode
1450 * @throws RIFF::Exception if new file access mode is unknown
1451 */
1452 bool File::SetMode(stream_mode_t NewMode) {
1453 if (NewMode != Mode) {
1454 switch (NewMode) {
1455 case stream_mode_read:
1456 #if POSIX
1457 if (hFileRead) close(hFileRead);
1458 hFileRead = hFileWrite = open(Filename.c_str(), O_RDONLY | O_NONBLOCK);
1459 if (hFileRead < 0) {
1460 hFileRead = hFileWrite = 0;
1461 throw Exception("Could not (re)open file \"" + Filename + "\" in read mode");
1462 }
1463 #elif defined(WIN32)
1464 if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
1465 hFileRead = hFileWrite = CreateFile(
1466 Filename.c_str(), GENERIC_READ,
1467 FILE_SHARE_READ | FILE_SHARE_WRITE,
1468 NULL, OPEN_EXISTING,
1469 FILE_ATTRIBUTE_NORMAL |
1470 FILE_FLAG_RANDOM_ACCESS,
1471 NULL
1472 );
1473 if (hFileRead == INVALID_HANDLE_VALUE) {
1474 hFileRead = hFileWrite = INVALID_HANDLE_VALUE;
1475 throw Exception("Could not (re)open file \"" + Filename + "\" in read mode");
1476 }
1477 #else
1478 if (hFileRead) fclose(hFileRead);
1479 hFileRead = hFileWrite = fopen(Filename.c_str(), "rb");
1480 if (!hFileRead) throw Exception("Could not (re)open file \"" + Filename + "\" in read mode");
1481 #endif
1482 __resetPos(); // reset read/write position of ALL 'Chunk' objects
1483 break;
1484 case stream_mode_read_write:
1485 #if POSIX
1486 if (hFileRead) close(hFileRead);
1487 hFileRead = hFileWrite = open(Filename.c_str(), O_RDWR | O_NONBLOCK);
1488 if (hFileRead < 0) {
1489 hFileRead = hFileWrite = open(Filename.c_str(), O_RDONLY | O_NONBLOCK);
1490 throw Exception("Could not open file \"" + Filename + "\" in read+write mode");
1491 }
1492 #elif defined(WIN32)
1493 if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
1494 hFileRead = hFileWrite = CreateFile(
1495 Filename.c_str(),
1496 GENERIC_READ | GENERIC_WRITE,
1497 FILE_SHARE_READ,
1498 NULL, OPEN_ALWAYS,
1499 FILE_ATTRIBUTE_NORMAL |
1500 FILE_FLAG_RANDOM_ACCESS,
1501 NULL
1502 );
1503 if (hFileRead == INVALID_HANDLE_VALUE) {
1504 hFileRead = hFileWrite = CreateFile(
1505 Filename.c_str(), GENERIC_READ,
1506 FILE_SHARE_READ | FILE_SHARE_WRITE,
1507 NULL, OPEN_EXISTING,
1508 FILE_ATTRIBUTE_NORMAL |
1509 FILE_FLAG_RANDOM_ACCESS,
1510 NULL
1511 );
1512 throw Exception("Could not (re)open file \"" + Filename + "\" in read+write mode");
1513 }
1514 #else
1515 if (hFileRead) fclose(hFileRead);
1516 hFileRead = hFileWrite = fopen(Filename.c_str(), "r+b");
1517 if (!hFileRead) {
1518 hFileRead = hFileWrite = fopen(Filename.c_str(), "rb");
1519 throw Exception("Could not open file \"" + Filename + "\" in read+write mode");
1520 }
1521 #endif
1522 __resetPos(); // reset read/write position of ALL 'Chunk' objects
1523 break;
1524 case stream_mode_closed:
1525 #if POSIX
1526 if (hFileRead) close(hFileRead);
1527 if (hFileWrite) close(hFileWrite);
1528 #elif defined(WIN32)
1529 if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
1530 if (hFileWrite != INVALID_HANDLE_VALUE) CloseHandle(hFileWrite);
1531 #else
1532 if (hFileRead) fclose(hFileRead);
1533 if (hFileWrite) fclose(hFileWrite);
1534 #endif
1535 hFileRead = hFileWrite = 0;
1536 break;
1537 default:
1538 throw Exception("Unknown file access mode");
1539 }
1540 Mode = NewMode;
1541 return true;
1542 }
1543 return false;
1544 }
1545
1546 /** @brief Set the byte order to be used when saving.
1547 *
1548 * Set the byte order to be used in the file. A value of
1549 * endian_little will create a RIFF file, endian_big a RIFX file
1550 * and endian_native will create a RIFF file on little-endian
1551 * machines and RIFX on big-endian machines.
1552 *
1553 * @param Endian - endianess to use when file is saved.
1554 */
1555 void File::SetByteOrder(endian_t Endian) {
1556 #if WORDS_BIGENDIAN
1557 bEndianNative = Endian != endian_little;
1558 #else
1559 bEndianNative = Endian != endian_big;
1560 #endif
1561 }
1562
1563 /** @brief Save changes to same file.
1564 *
1565 * Make all changes of all chunks persistent by writing them to the
1566 * actual (same) file. The file might temporarily grow to a higher size
1567 * than it will have at the end of the saving process, in case chunks
1568 * were grown.
1569 *
1570 * @throws RIFF::Exception if there is an empty chunk or empty list
1571 * chunk or any kind of IO error occured
1572 */
1573 void File::Save() {
1574 // make sure the RIFF tree is built (from the original file)
1575 LoadSubChunksRecursively();
1576
1577 // reopen file in write mode
1578 SetMode(stream_mode_read_write);
1579
1580 // to be able to save the whole file without loading everything into
1581 // RAM and without having to store the data in a temporary file, we
1582 // enlarge the file with the sum of all _positive_ chunk size
1583 // changes, move current data towards the end of the file with the
1584 // calculated sum and finally update / rewrite the file by copying
1585 // the old data back to the right position at the beginning of the file
1586
1587 // first we sum up all positive chunk size changes (and skip all negative ones)
1588 unsigned long ulPositiveSizeDiff = 0;
1589 for (ChunkList::iterator iter = ResizedChunks.begin(), end = ResizedChunks.end(); iter != end; ++iter) {
1590 if ((*iter)->GetNewSize() == 0) {
1591 throw Exception("There is at least one empty chunk (zero size): " + __resolveChunkPath(*iter));
1592 }
1593 if ((*iter)->GetNewSize() + 1L > (*iter)->GetSize()) {
1594 unsigned long ulDiff = (*iter)->GetNewSize() - (*iter)->GetSize() + 1L; // +1 in case we have to add a pad byte
1595 ulPositiveSizeDiff += ulDiff;
1596 }
1597 }
1598
1599 unsigned long ulWorkingFileSize = GetFileSize();
1600
1601 // if there are positive size changes...
1602 if (ulPositiveSizeDiff > 0) {
1603 // ... we enlarge this file first ...
1604 ulWorkingFileSize += ulPositiveSizeDiff;
1605 ResizeFile(ulWorkingFileSize);
1606 // ... and move current data by the same amount towards end of file.
1607 int8_t* pCopyBuffer = new int8_t[4096];
1608 const unsigned long ulFileSize = GetSize() + RIFF_HEADER_SIZE;
1609 #if defined(WIN32)
1610 DWORD iBytesMoved = 1; // we have to pass it via pointer to the Windows API, thus the correct size must be ensured
1611 #else
1612 int iBytesMoved = 1;
1613 #endif
1614 for (unsigned long ulPos = 0; iBytesMoved > 0; ulPos += iBytesMoved) {
1615 const unsigned long ulToMove = ulFileSize - ulPos;
1616 iBytesMoved = (ulToMove < 4096) ? ulToMove : 4096;
1617 #if POSIX
1618 lseek(hFileRead, ulPos, SEEK_SET);
1619 iBytesMoved = read(hFileRead, pCopyBuffer, iBytesMoved);
1620 lseek(hFileWrite, ulPos + ulPositiveSizeDiff, SEEK_SET);
1621 iBytesMoved = write(hFileWrite, pCopyBuffer, iBytesMoved);
1622 #elif defined(WIN32)
1623 SetFilePointer(hFileRead, ulPos, NULL/*32 bit*/, FILE_BEGIN);
1624 ReadFile(hFileRead, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
1625 SetFilePointer(hFileWrite, ulPos + ulPositiveSizeDiff, NULL/*32 bit*/, FILE_BEGIN);
1626 WriteFile(hFileWrite, pCopyBuffer, iBytesMoved, &iBytesMoved, NULL);
1627 #else
1628 fseek(hFileRead, ulPos, SEEK_SET);
1629 iBytesMoved = fread(pCopyBuffer, 1, iBytesMoved, hFileRead);
1630 fseek(hFileWrite, ulPos + ulPositiveSizeDiff, SEEK_SET);
1631 iBytesMoved = fwrite(pCopyBuffer, 1, iBytesMoved, hFileWrite);
1632 #endif
1633 }
1634 delete[] pCopyBuffer;
1635 if (iBytesMoved < 0) throw Exception("Could not modify file while trying to enlarge it");
1636 }
1637
1638 // rebuild / rewrite complete RIFF tree
1639 unsigned long ulTotalSize = WriteChunk(0, ulPositiveSizeDiff);
1640 unsigned long ulActualSize = __GetFileSize(hFileWrite);
1641
1642 // resize file to the final size
1643 if (ulTotalSize < ulActualSize) ResizeFile(ulTotalSize);
1644
1645 // forget all resized chunks
1646 ResizedChunks.clear();
1647 }
1648
1649 /** @brief Save changes to another file.
1650 *
1651 * Make all changes of all chunks persistent by writing them to another
1652 * file. <b>Caution:</b> this method is optimized for writing to
1653 * <b>another</b> file, do not use it to save the changes to the same
1654 * file! Use File::Save() in that case instead! Ignoring this might
1655 * result in a corrupted file, especially in case chunks were resized!
1656 *
1657 * After calling this method, this File object will be associated with
1658 * the new file (given by \a path) afterwards.
1659 *
1660 * @param path - path and file name where everything should be written to
1661 */
1662 void File::Save(const String& path) {
1663 //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
1664
1665 // make sure the RIFF tree is built (from the original file)
1666 LoadSubChunksRecursively();
1667
1668 if (Filename.length() > 0) SetMode(stream_mode_read);
1669 // open the other (new) file for writing and truncate it to zero size
1670 #if POSIX
1671 hFileWrite = open(path.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP);
1672 if (hFileWrite < 0) {
1673 hFileWrite = hFileRead;
1674 throw Exception("Could not open file \"" + path + "\" for writing");
1675 }
1676 #elif defined(WIN32)
1677 hFileWrite = CreateFile(
1678 path.c_str(), GENERIC_WRITE, FILE_SHARE_READ,
1679 NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL |
1680 FILE_FLAG_RANDOM_ACCESS, NULL
1681 );
1682 if (hFileWrite == INVALID_HANDLE_VALUE) {
1683 hFileWrite = hFileRead;
1684 throw Exception("Could not open file \"" + path + "\" for writing");
1685 }
1686 #else
1687 hFileWrite = fopen(path.c_str(), "w+b");
1688 if (!hFileWrite) {
1689 hFileWrite = hFileRead;
1690 throw Exception("Could not open file \"" + path + "\" for writing");
1691 }
1692 #endif // POSIX
1693 Mode = stream_mode_read_write;
1694
1695 // write complete RIFF tree to the other (new) file
1696 unsigned long ulTotalSize = WriteChunk(0, 0);
1697 unsigned long ulActualSize = __GetFileSize(hFileWrite);
1698
1699 // resize file to the final size (if the file was originally larger)
1700 if (ulTotalSize < ulActualSize) ResizeFile(ulTotalSize);
1701
1702 // forget all resized chunks
1703 ResizedChunks.clear();
1704
1705 #if POSIX
1706 if (hFileWrite) close(hFileWrite);
1707 #elif defined(WIN32)
1708 if (hFileWrite != INVALID_HANDLE_VALUE) CloseHandle(hFileWrite);
1709 #else
1710 if (hFileWrite) fclose(hFileWrite);
1711 #endif
1712 hFileWrite = hFileRead;
1713
1714 // associate new file with this File object from now on
1715 Filename = path;
1716 Mode = (stream_mode_t) -1; // Just set it to an undefined mode ...
1717 SetMode(stream_mode_read_write); // ... so SetMode() has to reopen the file handles.
1718 }
1719
1720 void File::ResizeFile(unsigned long ulNewSize) {
1721 #if POSIX
1722 if (ftruncate(hFileWrite, ulNewSize) < 0)
1723 throw Exception("Could not resize file \"" + Filename + "\"");
1724 #elif defined(WIN32)
1725 if (
1726 SetFilePointer(hFileWrite, ulNewSize, NULL/*32 bit*/, FILE_BEGIN) == INVALID_SET_FILE_POINTER ||
1727 !SetEndOfFile(hFileWrite)
1728 ) throw Exception("Could not resize file \"" + Filename + "\"");
1729 #else
1730 # error Sorry, this version of libgig only supports POSIX and Windows systems yet.
1731 # error Reason: portable implementation of RIFF::File::ResizeFile() is missing (yet)!
1732 #endif
1733 }
1734
1735 File::~File() {
1736 #if DEBUG
1737 std::cout << "File::~File()" << std::endl;
1738 #endif // DEBUG
1739 #if POSIX
1740 if (hFileRead) close(hFileRead);
1741 #elif defined(WIN32)
1742 if (hFileRead != INVALID_HANDLE_VALUE) CloseHandle(hFileRead);
1743 #else
1744 if (hFileRead) fclose(hFileRead);
1745 #endif // POSIX
1746 }
1747
1748 void File::LogAsResized(Chunk* pResizedChunk) {
1749 ResizedChunks.push_back(pResizedChunk);
1750 }
1751
1752 void File::UnlogResized(Chunk* pResizedChunk) {
1753 ResizedChunks.remove(pResizedChunk);
1754 }
1755
1756 unsigned long File::GetFileSize() {
1757 return __GetFileSize(hFileRead);
1758 }
1759
1760 #if POSIX
1761 unsigned long File::__GetFileSize(int hFile) {
1762 struct stat filestat;
1763 fstat(hFile, &filestat);
1764 long size = filestat.st_size;
1765 return size;
1766 }
1767 #elif defined(WIN32)
1768 unsigned long File::__GetFileSize(HANDLE hFile) {
1769 DWORD dwSize = ::GetFileSize(hFile, NULL /*32bit*/);
1770 if (dwSize == INVALID_FILE_SIZE)
1771 throw Exception("Windows FS error: could not determine file size");
1772 return dwSize;
1773 }
1774 #else // standard C functions
1775 unsigned long File::__GetFileSize(FILE* hFile) {
1776 long curpos = ftell(hFile);
1777 fseek(hFile, 0, SEEK_END);
1778 long size = ftell(hFile);
1779 fseek(hFile, curpos, SEEK_SET);
1780 return size;
1781 }
1782 #endif
1783
1784
1785 // *************** Exception ***************
1786 // *
1787
1788 void Exception::PrintMessage() {
1789 std::cout << "RIFF::Exception: " << Message << std::endl;
1790 }
1791
1792
1793 // *************** functions ***************
1794 // *
1795
1796 /**
1797 * Returns the name of this C++ library. This is usually "libgig" of
1798 * course. This call is equivalent to DLS::libraryName() and
1799 * gig::libraryName().
1800 */
1801 String libraryName() {
1802 return PACKAGE;
1803 }
1804
1805 /**
1806 * Returns version of this C++ library. This call is equivalent to
1807 * DLS::libraryVersion() and gig::libraryVersion().
1808 */
1809 String libraryVersion() {
1810 return VERSION;
1811 }
1812
1813 } // namespace RIFF

  ViewVC Help
Powered by ViewVC