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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1851 - (show annotations) (download)
Sun Mar 1 22:08:32 2009 UTC (15 years, 1 month ago) by schoenebeck
File size: 75035 byte(s)
* bugfix: refuse RIFF::Chunk::Read() in case chunk has just been added,
  that is not written physically yet (#82)
* bugfix in gig::Sample::LoadSampleData*(): reset sample read position
  to sample start before trying to (re)load sample data from file (#82)

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

  ViewVC Help
Powered by ViewVC