1 |
/*************************************************************************** |
2 |
* * |
3 |
* libgig - C++ cross-platform Gigasampler format file loader library * |
4 |
* * |
5 |
* Copyright (C) 2003, 2004 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 |
#if 1 |
24 |
#include "RIFF.h" |
25 |
|
26 |
namespace RIFF { |
27 |
|
28 |
// *************** Chunk ************** |
29 |
// * |
30 |
|
31 |
Chunk::Chunk() { |
32 |
#if DEBUG |
33 |
std::cout << "Chunk::Chunk()" << std::endl; |
34 |
#endif // DEBUG |
35 |
ulPos = 0; |
36 |
pParent = NULL; |
37 |
pChunkData = NULL; |
38 |
} |
39 |
|
40 |
#if POSIX |
41 |
Chunk::Chunk(int hFile, unsigned long StartPos, bool EndianNative, List* Parent) { |
42 |
#else |
43 |
Chunk::Chunk(FILE* hFile, unsigned long StartPos, bool EndianNative, List* Parent) { |
44 |
#endif // POSIX |
45 |
#if DEBUG |
46 |
std::cout << "Chunk::Chunk(FILE,ulong,bool,List*),StartPos=" << StartPos << std::endl; |
47 |
#endif // DEBUG |
48 |
Chunk::hFile = hFile; |
49 |
ulStartPos = StartPos + CHUNK_HEADER_SIZE; |
50 |
bEndianNative = EndianNative; |
51 |
pParent = Parent; |
52 |
ulPos = 0; |
53 |
pChunkData = NULL; |
54 |
ReadHeader(StartPos); |
55 |
} |
56 |
|
57 |
Chunk::~Chunk() { |
58 |
if (pChunkData) delete[] pChunkData; |
59 |
} |
60 |
|
61 |
void Chunk::ReadHeader(unsigned long fPos) { |
62 |
#if DEBUG |
63 |
std::cout << "Chunk::Readheader(" << fPos << ") "; |
64 |
#endif // DEBUG |
65 |
#if POSIX |
66 |
if (lseek(hFile, fPos, SEEK_SET) != -1) { |
67 |
read(hFile, &ChunkID, 4); |
68 |
read(hFile, &ChunkSize, 4); |
69 |
#else |
70 |
if (!fseek(hFile, fPos, SEEK_SET)) { |
71 |
fread(&ChunkID, 4, 1, hFile); |
72 |
fread(&ChunkSize, 4, 1, hFile); |
73 |
#endif // POSIX |
74 |
#if WORDS_BIGENDIAN |
75 |
if (ChunkID == CHUNK_ID_RIFF) { |
76 |
bEndianNative = false; |
77 |
} |
78 |
#else // little endian |
79 |
if (ChunkID == CHUNK_ID_RIFX) { |
80 |
bEndianNative = false; |
81 |
ChunkID = CHUNK_ID_RIFF; |
82 |
} |
83 |
#endif // WORDS_BIGENDIAN |
84 |
if (!bEndianNative) { |
85 |
//swapBytes_32(&ChunkID); |
86 |
swapBytes_32(&ChunkSize); |
87 |
} |
88 |
#if DEBUG |
89 |
std::cout << "ckID=" << convertToString(ChunkID) << " "; |
90 |
std::cout << "ckSize=" << ChunkSize << " "; |
91 |
std::cout << "bEndianNative=" << bEndianNative << std::endl; |
92 |
#endif // DEBUG |
93 |
} |
94 |
} |
95 |
|
96 |
/** |
97 |
* Returns the String representation of the chunk's ID (e.g. "RIFF", |
98 |
* "LIST"). |
99 |
*/ |
100 |
String Chunk::GetChunkIDString() { |
101 |
return convertToString(ChunkID); |
102 |
} |
103 |
|
104 |
/** |
105 |
* Sets the position within the chunk body, thus within the data portion |
106 |
* of the chunk (in bytes). |
107 |
* |
108 |
* @param Where - position offset (in bytes) |
109 |
* @param Whence - optional: defines to what <i>\a Where</i> relates to, |
110 |
* if omitted \a Where relates to beginning of the chunk |
111 |
* data |
112 |
*/ |
113 |
unsigned long Chunk::SetPos(unsigned long Where, stream_whence_t Whence) { |
114 |
#if DEBUG |
115 |
std::cout << "Chunk::SetPos(ulong)" << std::endl; |
116 |
#endif // DEBUG |
117 |
switch (Whence) { |
118 |
case stream_curpos: |
119 |
ulPos += Where; |
120 |
break; |
121 |
case stream_end: |
122 |
ulPos = ChunkSize - 1 - Where; |
123 |
break; |
124 |
case stream_backward: |
125 |
ulPos -= Where; |
126 |
break; |
127 |
case stream_start: default: |
128 |
ulPos = Where; |
129 |
break; |
130 |
} |
131 |
if (ulPos > ChunkSize) ulPos = ChunkSize; |
132 |
return ulPos; |
133 |
} |
134 |
|
135 |
/** |
136 |
* Returns the number of bytes left to read in the chunk body. |
137 |
* When reading data from the chunk using the Read*() Methods, the |
138 |
* position within the chunk data (that is the chunk body) will be |
139 |
* incremented by the number of read bytes and RemainingBytes() returns |
140 |
* how much data is left to read from the current position to the end |
141 |
* of the chunk data. |
142 |
* |
143 |
* @returns number of bytes left to read |
144 |
*/ |
145 |
unsigned long Chunk::RemainingBytes() { |
146 |
#if DEBUG |
147 |
std::cout << "Chunk::Remainingbytes()=" << ChunkSize - ulPos << std::endl; |
148 |
#endif // DEBUG |
149 |
return ChunkSize - ulPos; |
150 |
} |
151 |
|
152 |
/** |
153 |
* Returns the current state of the chunk object. |
154 |
* Following values are possible: |
155 |
* - RIFF::stream_ready : |
156 |
* chunk data can be read (this is the usual case) |
157 |
* - RIFF::stream_closed : |
158 |
* the data stream was closed somehow, no more reading possible |
159 |
* - RIFF::stream_end_reached : |
160 |
* alreaady reached the end of the chunk data, no more reading |
161 |
* possible without SetPos() |
162 |
*/ |
163 |
stream_state_t Chunk::GetState() { |
164 |
#if DEBUG |
165 |
std::cout << "Chunk::GetState()" << std::endl; |
166 |
#endif // DEBUG |
167 |
#if POSIX |
168 |
if (hFile == 0) return stream_closed; |
169 |
#else |
170 |
if (hFile == NULL) return stream_closed; |
171 |
#endif // POSIX |
172 |
if (ulPos < ChunkSize) return stream_ready; |
173 |
else return stream_end_reached; |
174 |
} |
175 |
|
176 |
/** |
177 |
* Reads \a WordCount number of data words with given \a WordSize and |
178 |
* copies it into a buffer pointed by \a pData. The buffer has to be |
179 |
* allocated and be sure to provide the correct \a WordSize, as this |
180 |
* will be important and taken into account for eventual endian |
181 |
* correction (swapping of bytes due to different native byte order of |
182 |
* a system). The position within the chunk will automatically be |
183 |
* incremented. |
184 |
* |
185 |
* @param pData destination buffer |
186 |
* @param WordCount number of data words to read |
187 |
* @param WordSize size of each data word to read |
188 |
* @returns number of successfully read data words or 0 if end |
189 |
* of file reached or error occured |
190 |
*/ |
191 |
unsigned long Chunk::Read(void* pData, unsigned long WordCount, unsigned long WordSize) { |
192 |
#if DEBUG |
193 |
std::cout << "Chunk::Read(void*,ulong,ulong)" << std::endl; |
194 |
#endif // DEBUG |
195 |
if (ulPos >= ChunkSize) return 0; |
196 |
if (ulPos + WordCount * WordSize >= ChunkSize) WordCount = (ChunkSize - ulPos) / WordSize; |
197 |
#if POSIX |
198 |
if (lseek(hFile, ulStartPos + ulPos, SEEK_SET) < 0) return 0; |
199 |
unsigned long readWords = read(hFile, pData, WordCount * WordSize); |
200 |
if (readWords < 1) return 0; |
201 |
readWords /= WordSize; |
202 |
#else // standard C functions |
203 |
if (fseek(hFile, ulStartPos + ulPos, SEEK_SET)) return 0; |
204 |
unsigned long readWords = fread(pData, WordSize, WordCount, hFile); |
205 |
#endif // POSIX |
206 |
if (!bEndianNative && WordSize != 1) { |
207 |
switch (WordSize) { |
208 |
case 2: |
209 |
for (unsigned long iWord = 0; iWord < readWords; iWord++) |
210 |
swapBytes_16((uint16_t*) pData + iWord); |
211 |
break; |
212 |
case 4: |
213 |
for (unsigned long iWord = 0; iWord < readWords; iWord++) |
214 |
swapBytes_32((uint32_t*) pData + iWord); |
215 |
break; |
216 |
default: |
217 |
for (unsigned long iWord = 0; iWord < readWords; iWord++) |
218 |
swapBytes((uint8_t*) pData + iWord * WordSize, WordSize); |
219 |
break; |
220 |
} |
221 |
} |
222 |
SetPos(readWords * WordSize, stream_curpos); |
223 |
return readWords; |
224 |
} |
225 |
|
226 |
/** Just an internal wrapper for the main <i>Read()</i> method with additional Exception throwing on errors. */ |
227 |
unsigned long Chunk::ReadSceptical(void* pData, unsigned long WordCount, unsigned long WordSize) { |
228 |
unsigned long readWords = Read(pData, WordCount, WordSize); |
229 |
if (readWords != WordCount) throw RIFF::Exception("End of chunk data reached."); |
230 |
return readWords; |
231 |
} |
232 |
|
233 |
/** |
234 |
* Reads \a WordCount number of 8 Bit signed integer words and copies it |
235 |
* into the buffer pointed by \a pData. The buffer has to be allocated. |
236 |
* The position within the chunk will automatically be incremented. |
237 |
* |
238 |
* @param pData destination buffer |
239 |
* @param WordCount number of 8 Bit signed integers to read |
240 |
* @returns number of read integers |
241 |
* @throws RIFF::Exception if an error occured or less than |
242 |
* \a WordCount integers could be read! |
243 |
*/ |
244 |
unsigned long Chunk::ReadInt8(int8_t* pData, unsigned long WordCount) { |
245 |
#if DEBUG |
246 |
std::cout << "Chunk::ReadInt8(int8_t*,ulong)" << std::endl; |
247 |
#endif // DEBUG |
248 |
return ReadSceptical(pData, WordCount, 1); |
249 |
} |
250 |
|
251 |
/** |
252 |
* Reads \a WordCount number of 8 Bit unsigned integer words and copies |
253 |
* it into the buffer pointed by \a pData. The buffer has to be |
254 |
* allocated. The position within the chunk will automatically be |
255 |
* incremented. |
256 |
* |
257 |
* @param pData destination buffer |
258 |
* @param WordCount number of 8 Bit unsigned integers to read |
259 |
* @returns number of read integers |
260 |
* @throws RIFF::Exception if an error occured or less than |
261 |
* \a WordCount integers could be read! |
262 |
*/ |
263 |
unsigned long Chunk::ReadUint8(uint8_t* pData, unsigned long WordCount) { |
264 |
#if DEBUG |
265 |
std::cout << "Chunk::ReadUint8(uint8_t*,ulong)" << std::endl; |
266 |
#endif // DEBUG |
267 |
return ReadSceptical(pData, WordCount, 1); |
268 |
} |
269 |
|
270 |
/** |
271 |
* Reads \a WordCount number of 16 Bit signed integer words and copies |
272 |
* it into the buffer pointed by \a pData. The buffer has to be |
273 |
* allocated. Endian correction will automatically be done if needed. |
274 |
* The position within the chunk will automatically be incremented. |
275 |
* |
276 |
* @param pData destination buffer |
277 |
* @param WordCount number of 16 Bit signed integers to read |
278 |
* @returns number of read integers |
279 |
* @throws RIFF::Exception if an error occured or less than |
280 |
* \a WordCount integers could be read! |
281 |
*/ |
282 |
unsigned long Chunk::ReadInt16(int16_t* pData, unsigned long WordCount) { |
283 |
#if DEBUG |
284 |
std::cout << "Chunk::ReadInt16(int16_t*,ulong)" << std::endl; |
285 |
#endif // DEBUG |
286 |
return ReadSceptical(pData, WordCount, 2); |
287 |
} |
288 |
|
289 |
/** |
290 |
* Reads \a WordCount number of 16 Bit unsigned integer words and copies |
291 |
* it into the buffer pointed by \a pData. The buffer has to be |
292 |
* allocated. Endian correction will automatically be done if needed. |
293 |
* The position within the chunk will automatically be incremented. |
294 |
* |
295 |
* @param pData destination buffer |
296 |
* @param WordCount number of 8 Bit unsigned integers to read |
297 |
* @returns number of read integers |
298 |
* @throws RIFF::Exception if an error occured or less than |
299 |
* \a WordCount integers could be read! |
300 |
*/ |
301 |
unsigned long Chunk::ReadUint16(uint16_t* pData, unsigned long WordCount) { |
302 |
#if DEBUG |
303 |
std::cout << "Chunk::ReadUint16(uint16_t*,ulong)" << std::endl; |
304 |
#endif // DEBUG |
305 |
return ReadSceptical(pData, WordCount, 2); |
306 |
} |
307 |
|
308 |
/** |
309 |
* Reads \a WordCount number of 32 Bit signed integer words and copies |
310 |
* it into the buffer pointed by \a pData. The buffer has to be |
311 |
* allocated. Endian correction will automatically be done if needed. |
312 |
* The position within the chunk will automatically be incremented. |
313 |
* |
314 |
* @param pData destination buffer |
315 |
* @param WordCount number of 32 Bit signed integers to read |
316 |
* @returns number of read integers |
317 |
* @throws RIFF::Exception if an error occured or less than |
318 |
* \a WordCount integers could be read! |
319 |
*/ |
320 |
unsigned long Chunk::ReadInt32(int32_t* pData, unsigned long WordCount) { |
321 |
#if DEBUG |
322 |
std::cout << "Chunk::ReadInt32(int32_t*,ulong)" << std::endl; |
323 |
#endif // DEBUG |
324 |
return ReadSceptical(pData, WordCount, 4); |
325 |
} |
326 |
|
327 |
/** |
328 |
* Reads \a WordCount number of 32 Bit unsigned integer words and copies |
329 |
* it into the buffer pointed by \a pData. The buffer has to be |
330 |
* allocated. Endian correction will automatically be done if needed. |
331 |
* The position within the chunk will automatically be incremented. |
332 |
* |
333 |
* @param pData destination buffer |
334 |
* @param WordCount number of 32 Bit unsigned integers to read |
335 |
* @returns number of read integers |
336 |
* @throws RIFF::Exception if an error occured or less than |
337 |
* \a WordCount integers could be read! |
338 |
*/ |
339 |
unsigned long Chunk::ReadUint32(uint32_t* pData, unsigned long WordCount) { |
340 |
#if DEBUG |
341 |
std::cout << "Chunk::ReadUint32(uint32_t*,ulong)" << std::endl; |
342 |
#endif // DEBUG |
343 |
return ReadSceptical(pData, WordCount, 4); |
344 |
} |
345 |
|
346 |
/** |
347 |
* Reads one 8 Bit signed integer word and increments the position within |
348 |
* the chunk. |
349 |
* |
350 |
* @returns read integer word |
351 |
* @throws RIFF::Exception if an error occured |
352 |
*/ |
353 |
int8_t Chunk::ReadInt8() { |
354 |
#if DEBUG |
355 |
std::cout << "Chunk::ReadInt8()" << std::endl; |
356 |
#endif // DEBUG |
357 |
int8_t word; |
358 |
ReadSceptical(&word,1,1); |
359 |
return word; |
360 |
} |
361 |
|
362 |
/** |
363 |
* Reads one 8 Bit unsigned integer word and increments the position |
364 |
* within the chunk. |
365 |
* |
366 |
* @returns read integer word |
367 |
* @throws RIFF::Exception if an error occured |
368 |
*/ |
369 |
uint8_t Chunk::ReadUint8() { |
370 |
#if DEBUG |
371 |
std::cout << "Chunk::ReadUint8()" << std::endl; |
372 |
#endif // DEBUG |
373 |
uint8_t word; |
374 |
ReadSceptical(&word,1,1); |
375 |
return word; |
376 |
} |
377 |
|
378 |
/** |
379 |
* Reads one 16 Bit signed integer word and increments the position |
380 |
* within the chunk. Endian correction will automatically be done if |
381 |
* needed. |
382 |
* |
383 |
* @returns read integer word |
384 |
* @throws RIFF::Exception if an error occured |
385 |
*/ |
386 |
int16_t Chunk::ReadInt16() { |
387 |
#if DEBUG |
388 |
std::cout << "Chunk::ReadInt16()" << std::endl; |
389 |
#endif // DEBUG |
390 |
int16_t word; |
391 |
ReadSceptical(&word,1,2); |
392 |
return word; |
393 |
} |
394 |
|
395 |
/** |
396 |
* Reads one 16 Bit unsigned integer word and increments the position |
397 |
* within the chunk. Endian correction will automatically be done if |
398 |
* needed. |
399 |
* |
400 |
* @returns read integer word |
401 |
* @throws RIFF::Exception if an error occured |
402 |
*/ |
403 |
uint16_t Chunk::ReadUint16() { |
404 |
#if DEBUG |
405 |
std::cout << "Chunk::ReadUint16()" << std::endl; |
406 |
#endif // DEBUG |
407 |
uint16_t word; |
408 |
ReadSceptical(&word,1,2); |
409 |
return word; |
410 |
} |
411 |
|
412 |
/** |
413 |
* Reads one 32 Bit signed integer word and increments the position |
414 |
* within the chunk. Endian correction will automatically be done if |
415 |
* needed. |
416 |
* |
417 |
* @returns read integer word |
418 |
* @throws RIFF::Exception if an error occured |
419 |
*/ |
420 |
int32_t Chunk::ReadInt32() { |
421 |
#if DEBUG |
422 |
std::cout << "Chunk::ReadInt32()" << std::endl; |
423 |
#endif // DEBUG |
424 |
int32_t word; |
425 |
ReadSceptical(&word,1,4); |
426 |
return word; |
427 |
} |
428 |
|
429 |
/** |
430 |
* Reads one 32 Bit unsigned integer word and increments the position |
431 |
* within the chunk. Endian correction will automatically be done if |
432 |
* needed. |
433 |
* |
434 |
* @returns read integer word |
435 |
* @throws RIFF::Exception if an error occured |
436 |
*/ |
437 |
uint32_t Chunk::ReadUint32() { |
438 |
#if DEBUG |
439 |
std::cout << "Chunk::ReadUint32()" << std::endl; |
440 |
#endif // DEBUG |
441 |
uint32_t word; |
442 |
ReadSceptical(&word,1,4); |
443 |
return word; |
444 |
} |
445 |
|
446 |
void* Chunk::LoadChunkData() { |
447 |
if (!pChunkData) { |
448 |
#if POSIX |
449 |
if (lseek(hFile, ulStartPos, SEEK_SET) == -1) return NULL; |
450 |
pChunkData = new uint8_t[GetSize()]; |
451 |
if (!pChunkData) return NULL; |
452 |
unsigned long readWords = read(hFile, pChunkData, GetSize()); |
453 |
#else |
454 |
if (fseek(hFile, ulStartPos, SEEK_SET)) return NULL; |
455 |
pChunkData = new uint8_t[GetSize()]; |
456 |
if (!pChunkData) return NULL; |
457 |
unsigned long readWords = fread(pChunkData, 1, GetSize(), hFile); |
458 |
#endif // POSIX |
459 |
if (readWords != GetSize()) { |
460 |
delete[] pChunkData; |
461 |
return (pChunkData = NULL); |
462 |
} |
463 |
} |
464 |
return pChunkData; |
465 |
} |
466 |
|
467 |
void Chunk::ReleaseChunkData() { |
468 |
if (pChunkData) { |
469 |
delete[] pChunkData; |
470 |
pChunkData = NULL; |
471 |
} |
472 |
} |
473 |
|
474 |
|
475 |
|
476 |
// *************** List *************** |
477 |
// * |
478 |
|
479 |
List::List() : Chunk() { |
480 |
#if DEBUG |
481 |
std::cout << "List::List()" << std::endl; |
482 |
#endif // DEBUG |
483 |
pSubChunks = NULL; |
484 |
pSubChunksMap = NULL; |
485 |
} |
486 |
|
487 |
#if POSIX |
488 |
List::List(int hFile, unsigned long StartPos, bool EndianNative, List* Parent) |
489 |
#else |
490 |
List::List(FILE* hFile, unsigned long StartPos, bool EndianNative, List* Parent) |
491 |
#endif // POSIX |
492 |
: Chunk(hFile, StartPos, EndianNative, Parent) { |
493 |
#if DEBUG |
494 |
std::cout << "List::List(FILE*,ulong,bool,List*)" << std::endl; |
495 |
#endif // DEBUG |
496 |
pSubChunks = NULL; |
497 |
pSubChunksMap = NULL; |
498 |
ReadHeader(StartPos); |
499 |
ulStartPos = StartPos + LIST_HEADER_SIZE; |
500 |
} |
501 |
|
502 |
List::~List() { |
503 |
#if DEBUG |
504 |
std::cout << "List::~List()" << std::endl; |
505 |
#endif // DEBUG |
506 |
if (pSubChunks) { |
507 |
ChunkList::iterator iter = pSubChunks->begin(); |
508 |
ChunkList::iterator end = pSubChunks->end(); |
509 |
while (iter != end) { |
510 |
delete *iter; |
511 |
iter++; |
512 |
} |
513 |
delete pSubChunks; |
514 |
} |
515 |
if (pSubChunksMap) delete pSubChunksMap; |
516 |
} |
517 |
|
518 |
/** |
519 |
* Returns subchunk with chunk ID <i>\a ChunkID</i> within this chunk |
520 |
* list. Use this method if you expect only one subchunk of that type in |
521 |
* the list. It there are more than one, it's undetermined which one of |
522 |
* them will be returned! If there are no subchunks with that desired |
523 |
* chunk ID, NULL will be returned. |
524 |
* |
525 |
* @param ChunkID - chunk ID of the sought subchunk |
526 |
* @returns pointer to the subchunk or NULL if there is none of |
527 |
* that ID |
528 |
*/ |
529 |
Chunk* List::GetSubChunk(uint32_t ChunkID) { |
530 |
#if DEBUG |
531 |
std::cout << "List::GetSubChunk(uint32_t)" << std::endl; |
532 |
#endif // DEBUG |
533 |
if (!pSubChunksMap) LoadSubChunks(); |
534 |
return (*pSubChunksMap)[ChunkID]; |
535 |
} |
536 |
|
537 |
/** |
538 |
* Returns sublist chunk with list type <i>\a ListType</i> within this |
539 |
* chunk list. Use this method if you expect only one sublist chunk of |
540 |
* that type in the list. It there are more than one, it's undetermined |
541 |
* which one of them will be returned! If there are no sublists with |
542 |
* that desired list type, NULL will be returned. |
543 |
* |
544 |
* @param ListType - list type of the sought sublist |
545 |
* @returns pointer to the sublist or NULL if there is none of |
546 |
* that type |
547 |
*/ |
548 |
List* List::GetSubList(uint32_t ListType) { |
549 |
#if DEBUG |
550 |
std::cout << "List::GetSubList(uint32_t)" << std::endl; |
551 |
#endif // DEBUG |
552 |
if (!pSubChunks) LoadSubChunks(); |
553 |
ChunkList::iterator iter = pSubChunks->begin(); |
554 |
ChunkList::iterator end = pSubChunks->end(); |
555 |
while (iter != end) { |
556 |
if ((*iter)->GetChunkID() == CHUNK_ID_LIST) { |
557 |
List* l = (List*) *iter; |
558 |
if (l->GetListType() == ListType) return l; |
559 |
} |
560 |
iter++; |
561 |
} |
562 |
return NULL; |
563 |
} |
564 |
|
565 |
/** |
566 |
* Returns the first subchunk within the list. You have to call this |
567 |
* method before you can call GetNextSubChunk(). Recall it when you want |
568 |
* to start from the beginning of the list again. |
569 |
* |
570 |
* @returns pointer to the first subchunk within the list, NULL |
571 |
* otherwise |
572 |
*/ |
573 |
Chunk* List::GetFirstSubChunk() { |
574 |
#if DEBUG |
575 |
std::cout << "List::GetFirstSubChunk()" << std::endl; |
576 |
#endif // DEBUG |
577 |
if (!pSubChunks) LoadSubChunks(); |
578 |
ChunksIterator = pSubChunks->begin(); |
579 |
return (ChunksIterator != pSubChunks->end()) ? *ChunksIterator : NULL; |
580 |
} |
581 |
|
582 |
/** |
583 |
* Returns the next subchunk within the list. You have to call |
584 |
* GetFirstSubChunk() before you can use this method! |
585 |
* |
586 |
* @returns pointer to the next subchunk within the list or NULL if |
587 |
* end of list is reached |
588 |
*/ |
589 |
Chunk* List::GetNextSubChunk() { |
590 |
#if DEBUG |
591 |
std::cout << "List::GetNextSubChunk()" << std::endl; |
592 |
#endif // DEBUG |
593 |
if (!pSubChunks) return NULL; |
594 |
ChunksIterator++; |
595 |
return (ChunksIterator != pSubChunks->end()) ? *ChunksIterator : NULL; |
596 |
} |
597 |
|
598 |
/** |
599 |
* Returns the first sublist within the list (that is a subchunk with |
600 |
* chunk ID "LIST"). You have to call this method before you can call |
601 |
* GetNextSubList(). Recall it when you want to start from the beginning |
602 |
* of the list again. |
603 |
* |
604 |
* @returns pointer to the first sublist within the list, NULL |
605 |
* otherwise |
606 |
*/ |
607 |
List* List::GetFirstSubList() { |
608 |
#if DEBUG |
609 |
std::cout << "List::GetFirstSubList()" << std::endl; |
610 |
#endif // DEBUG |
611 |
if (!pSubChunks) LoadSubChunks(); |
612 |
ListIterator = pSubChunks->begin(); |
613 |
ChunkList::iterator end = pSubChunks->end(); |
614 |
while (ListIterator != end) { |
615 |
if ((*ListIterator)->GetChunkID() == CHUNK_ID_LIST) return (List*) *ListIterator; |
616 |
ListIterator++; |
617 |
} |
618 |
return NULL; |
619 |
} |
620 |
|
621 |
/** |
622 |
* Returns the next sublist (that is a subchunk with chunk ID "LIST") |
623 |
* within the list. You have to call GetFirstSubList() before you can |
624 |
* use this method! |
625 |
* |
626 |
* @returns pointer to the next sublist within the list, NULL if |
627 |
* end of list is reached |
628 |
*/ |
629 |
List* List::GetNextSubList() { |
630 |
#if DEBUG |
631 |
std::cout << "List::GetNextSubList()" << std::endl; |
632 |
#endif // DEBUG |
633 |
if (!pSubChunks) return NULL; |
634 |
if (ListIterator == pSubChunks->end()) return NULL; |
635 |
ListIterator++; |
636 |
ChunkList::iterator end = pSubChunks->end(); |
637 |
while (ListIterator != end) { |
638 |
if ((*ListIterator)->GetChunkID() == CHUNK_ID_LIST) return (List*) *ListIterator; |
639 |
ListIterator++; |
640 |
} |
641 |
return NULL; |
642 |
} |
643 |
|
644 |
/** |
645 |
* Returns number subchunks within the list. |
646 |
*/ |
647 |
unsigned int List::CountSubChunks() { |
648 |
if (!pSubChunks) LoadSubChunks(); |
649 |
return pSubChunks->size(); |
650 |
} |
651 |
|
652 |
/** |
653 |
* Returns number of subchunks within the list with chunk ID |
654 |
* <i>\a ChunkId</i>. |
655 |
*/ |
656 |
unsigned int List::CountSubChunks(uint32_t ChunkID) { |
657 |
unsigned int result = 0; |
658 |
if (!pSubChunks) LoadSubChunks(); |
659 |
ChunkList::iterator iter = pSubChunks->begin(); |
660 |
ChunkList::iterator end = pSubChunks->end(); |
661 |
while (iter != end) { |
662 |
if ((*iter)->GetChunkID() == ChunkID) { |
663 |
result++; |
664 |
} |
665 |
iter++; |
666 |
} |
667 |
return result; |
668 |
} |
669 |
|
670 |
/** |
671 |
* Returns number of sublists within the list. |
672 |
*/ |
673 |
unsigned int List::CountSubLists() { |
674 |
return CountSubChunks(CHUNK_ID_LIST); |
675 |
} |
676 |
|
677 |
/** |
678 |
* Returns number of sublists within the list with list type |
679 |
* <i>\a ListType</i> |
680 |
*/ |
681 |
unsigned int List::CountSubLists(uint32_t ListType) { |
682 |
unsigned int result = 0; |
683 |
if (!pSubChunks) LoadSubChunks(); |
684 |
ChunkList::iterator iter = pSubChunks->begin(); |
685 |
ChunkList::iterator end = pSubChunks->end(); |
686 |
while (iter != end) { |
687 |
if ((*iter)->GetChunkID() == CHUNK_ID_LIST) { |
688 |
List* l = (List*) *iter; |
689 |
if (l->GetListType() == ListType) result++; |
690 |
} |
691 |
iter++; |
692 |
} |
693 |
return result; |
694 |
} |
695 |
|
696 |
void List::ReadHeader(unsigned long fPos) { |
697 |
#if DEBUG |
698 |
std::cout << "List::Readheader(ulong) "; |
699 |
#endif // DEBUG |
700 |
Chunk::ReadHeader(fPos); |
701 |
ChunkSize -= 4; |
702 |
#if POSIX |
703 |
lseek(hFile, fPos + CHUNK_HEADER_SIZE, SEEK_SET); |
704 |
read(hFile, &ListType, 4); |
705 |
#else |
706 |
fseek(hFile, fPos + CHUNK_HEADER_SIZE, SEEK_SET); |
707 |
fread(&ListType, 4, 1, hFile); |
708 |
#endif // POSIX |
709 |
#if DEBUG |
710 |
std::cout << "listType=" << convertToString(ListType) << std::endl; |
711 |
#endif // DEBUG |
712 |
if (!bEndianNative) { |
713 |
//swapBytes_32(&ListType); |
714 |
} |
715 |
} |
716 |
|
717 |
void List::LoadSubChunks() { |
718 |
#if DEBUG |
719 |
std::cout << "List::LoadSubChunks()"; |
720 |
#endif // DEBUG |
721 |
if (!pSubChunks) { |
722 |
pSubChunks = new ChunkList(); |
723 |
pSubChunksMap = new ChunkMap(); |
724 |
while (RemainingBytes() >= CHUNK_HEADER_SIZE) { |
725 |
Chunk* ck; |
726 |
uint32_t ckid; |
727 |
Read(&ckid, 4, 1); |
728 |
#if DEBUG |
729 |
std::cout << " ckid=" << convertToString(ckid) << std::endl; |
730 |
#endif // DEBUG |
731 |
if (ckid == CHUNK_ID_LIST) { |
732 |
ck = new RIFF::List(hFile, ulStartPos + ulPos - 4, bEndianNative, this); |
733 |
SetPos(ck->GetSize() + LIST_HEADER_SIZE - 4, RIFF::stream_curpos); |
734 |
} |
735 |
else { // simple chunk |
736 |
ck = new RIFF::Chunk(hFile, ulStartPos + ulPos - 4, bEndianNative, this); |
737 |
SetPos(ck->GetSize() + CHUNK_HEADER_SIZE - 4, RIFF::stream_curpos); |
738 |
} |
739 |
pSubChunks->push_back(ck); |
740 |
(*pSubChunksMap)[ckid] = ck; |
741 |
if (GetPos() % 2 != 0) SetPos(1, RIFF::stream_curpos); // jump over pad byte |
742 |
} |
743 |
} |
744 |
} |
745 |
|
746 |
/** |
747 |
* Returns string representation of the lists's id |
748 |
*/ |
749 |
String List::GetListTypeString() { |
750 |
return convertToString(ListType); |
751 |
} |
752 |
|
753 |
|
754 |
|
755 |
// *************** File *************** |
756 |
// * |
757 |
|
758 |
File::File(const String& path) : List() { |
759 |
#if DEBUG |
760 |
std::cout << "File::File("<<path<<")" << std::endl; |
761 |
#endif // DEBUG |
762 |
bEndianNative = true; |
763 |
#if POSIX |
764 |
hFile = open(path.c_str(), O_RDONLY | O_NONBLOCK); |
765 |
if (hFile <= 0) { |
766 |
hFile = 0; |
767 |
throw RIFF::Exception("Can't open \"" + path + "\""); |
768 |
} |
769 |
#else |
770 |
hFile = fopen(path.c_str(), "rb"); |
771 |
if (!hFile) throw RIFF::Exception("Can't open \"" + path + "\""); |
772 |
#endif // POSIX |
773 |
ulStartPos = RIFF_HEADER_SIZE; |
774 |
ReadHeader(0); |
775 |
if (ChunkID != CHUNK_ID_RIFF) { |
776 |
throw RIFF::Exception("Not a RIFF file"); |
777 |
} |
778 |
} |
779 |
|
780 |
File::~File() { |
781 |
#if DEBUG |
782 |
std::cout << "File::~File()" << std::endl; |
783 |
#endif // DEBUG |
784 |
#if POSIX |
785 |
if (hFile) close(hFile); |
786 |
#else |
787 |
if (hFile) fclose(hFile); |
788 |
#endif // POSIX |
789 |
} |
790 |
|
791 |
unsigned long File::GetFileSize() { |
792 |
#if POSIX |
793 |
struct stat filestat; |
794 |
fstat(hFile, &filestat); |
795 |
long size = filestat.st_size; |
796 |
#else // standard C functions |
797 |
long curpos = ftell(hFile); |
798 |
fseek(hFile, 0, SEEK_END); |
799 |
long size = ftell(hFile); |
800 |
fseek(hFile, curpos, SEEK_SET); |
801 |
#endif // POSIX |
802 |
return size; |
803 |
} |
804 |
|
805 |
|
806 |
|
807 |
// *************** Exception *************** |
808 |
// * |
809 |
|
810 |
void Exception::PrintMessage() { |
811 |
std::cout << "RIFF::Exception: " << Message << std::endl; |
812 |
} |
813 |
|
814 |
} // namespace RIFF |
815 |
#endif |