-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathriff.h
643 lines (574 loc) · 14.4 KB
/
riff.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
/*
libriff
Author/copyright: Markus Wolf, alexmush
License: zlib (https://opensource.org/licenses/Zlib)
To read any RIFF files.
Not specialized to specific types like AVI or WAV.
Special chunks (e.g. "LIST") can contain a nested sub list of chunks
Example structure of RIFF file:
chunk list start ("RIFF") - ID,size,type
type (of parent chunk header)
chunk block - list level 0
chunk block ("LIST") - level 0 - ID,size,type
type (of parent chunk header)
chunk block - level 1
chunk block - level 1
chunk block - level 0
Usage:
Use a default open-function (file, mem) or create your own
The required function pointers for reading and seeking are set here
When creating your own open-function, take a look at the default function code as template
After the file is opened we are in the first chunk at list level 0
You can freely read and seek within the data of the current chunk
Use riff_nextChunk() to go to the first data byte of the next chunk (chunk header is read already then)
If the current chunk contains a list of sub level chunks:
Call riff_seekLevelSub() to be positioned at the first data byte of the first chunk in the sub list level
Call riff_levelParent() to leave the sub list without changing the file position
Read members of the riff_handle to get all info about current file position, current chunk, etc.
Due to the size fields being 4 bytes, this library may not work for RIFF files larger than 2GB.
*/
#ifndef _RIFF_H_
#define _RIFF_H_
#include <stddef.h>
#include <stdio.h>
#include <stdint.h>
/**
* @brief Size of RIFF file header and RIFF/LIST chunks that contain subchunks.
*/
#define RIFF_HEADER_SIZE 12
/**
* @brief The offset of data compared to the start of the chunk, equals size of chunk ID + chunk size field.
*/
#define RIFF_CHUNK_DATA_OFFSET 8
/**
* @defgroup Errors Error codes
*
* Error codes yielded by most `riff_...()` functions.
*
* @note Do not hardcode these in your software as the value mapping may change in the future.
* @{
*/
/**
* @name Non-critical errors
* @{
*/
/**
* @brief No error.
*/
#define RIFF_ERROR_NONE 0
/**
* @brief End of current chunk.
*
* Occurs when trying to read/seek beyond end of current chunk data.
*/
#define RIFF_ERROR_EOC 1
/**
* @brief End of chunk list.
*
* Occurs when trying to seek the next chunk while already at the last chunk in a chunk list.
*/
#define RIFF_ERROR_EOCL 2
/**
* @brief Excess bytes at end of chunk list level.
*
* Not critical, the excess data is simply ignored (1-7 bytes inside list, otherwise a following chunk is expected - more at file level possible), should never occur.
*/
#define RIFF_ERROR_EXDAT 3
///@}
/**
* @name Critical errors
* @{
*/
/**
* @brief First critical error code.
*
* A way to check if an error is critical - just use @c ( @c >= @c RIFF_ERROR_CRITICAL @c ).
*/
#define RIFF_ERROR_CRITICAL 4
/**
* @brief Illegal ID.
*
* Occurs when ID (or type) contains not printable or non-ASCII characters or is wrong.
*/
#define RIFF_ERROR_ILLID 4
/**
* @brief Invalid chunk size value in chunk header.
*
* Occurs when the chunk size is too small or exceeds list level or file.
*
* Indicates corruption or cut off file.
*/
#define RIFF_ERROR_ICSIZE 5
/**
* @brief Unexpected end of RIFF file.
*
* Indicates corruption (wrong chunk size field), a cut off file or the size argument for the opening function having been wrong (too small).
*/
#define RIFF_ERROR_EOF 6
/**
* @brief Access error.
*
* Indicates that the file is not accessible (could happen due to permissions, invalid file handle, etc.).
*/
#define RIFF_ERROR_ACCESS 7
/**
* @brief riff_handle is not set up or is NULL.
*/
#define RIFF_ERROR_INVALID_HANDLE 8
///@}
/**
* @brief The last RIFF_ERROR code.
*/
#define RIFF_ERROR_MAX 8
///@}
/**
* @brief Level stack entry struct.
*
* Needed to retrace from sublevel (list) chunks.
*
* (sorta) Contains header info of the parent level.
*/
struct riff_levelStackE {
/**
* @brief Absolute parent chunk position in file stream.
*/
size_t c_pos_start;
/**
* @brief ID of parent chunk.
*
* The first 4 bytes of RIFF/LIST chunk data.
*/
char c_id[5];
/**
* @brief Parent chunk size.
*
* Without header (contains value as stored in RIFF file).
*/
size_t c_size;
/**
* @brief Type ID of parent chunk.
*
* Should either be RIFF, LIST or BW64.
*/
char c_type[5];
};
/**
* @defgroup riff_handle The RIFF handle
* @{
*/
/**
* @brief The RIFF handle.
*
* Members are public and intended for read access (to avoid a plethora of get-functions).
*
* Be careful with the level stack, check riff_handle::ls_size first.
*
* @todo Show the current level's info instead of the header (2.0)
* @todo Rename riff_handle struct to _riff_handle (2.0)
*/
typedef struct riff_handle {
/**
* @name RIFF file header info.
*
* Available after the file has been opened.
*/
///@{
/**
* @brief Header ID, should be `"RIFF"`.
*
* Contains terminator to be printable.
*/
char h_id[5];
/**
* @brief Size value given in header.
*
* h_size + 8 equals to the file size.
*/
size_t h_size;
/**
* @brief Type ID of file.
*
* Should contain 4 ASCII characters.
*
* Also contains terminator to be printable.
*/
char h_type[5];
/**
* @brief Start position of RIFF file.
*/
size_t pos_start;
///@}
/**
* @brief Total size of RIFF file.
*
* 0 means unspecified.
*/
size_t size;
/**
* @brief Current position in data stream.
*/
size_t pos;
/**
* @name Current chunk's data.
*/
///@{
/**
* @brief Absolute start position of current chunk.
*/
size_t c_pos_start;
/**
* @brief Position in current chunk.
*
* Relative to the start of the chunk's data block.
*/
size_t c_pos;
/**
* @brief ID of current chunk.
*
* Contains terminator to be printable.
*/
char c_id[5];
/**
* @brief Size of current chunk.
*
* Excludes chunk header - same value as stored in RIFF file.
*/
size_t c_size;
/**
* @brief Pad byte.
*
* 1 if c_size is odd, else 0 (indicates unused extra byte at end of chunk).
*/
uint8_t pad;
///@}
/**
* @name Level stack data.
*/
///@{
/**
* @brief Level stack pointer.
*
* Resizes dynamically.
*
* To access the parent chunk data use `ls[ls_level-1]`.
*/
struct riff_levelStackE *ls;
/**
* @brief Size of stack in entries.
*
* Stack extends automatically if needed.
*/
size_t ls_size;
/**
* @brief Current stack level.
*
* Starts at 0.
*/
int ls_level;
///@}
/**
* @brief Data access handle.
*
* File handle or memory address.
*
* Only accessed by user FP functions.
*/
void *fh;
/**
* @name Internal functions
*
* Function pointers for e.g. defining your own input methods
*/
///@{
/**
* @brief Read bytes.
*
* @note Required for proper operation.
*/
size_t (*fp_read)(struct riff_handle *rh, void *ptr, size_t size);
/**
* @brief Seek position relative to start pos.
*
* @note Required for proper operation.
*/
size_t (*fp_seek)(struct riff_handle *rh, size_t pos);
/**
* @brief Print error.
*
* riff_allocate maps it to `vfprintf(stderr, ...)` by default.\n
* Set this pointer to NULL right after allocation to disable any printing.\n
* This pointer should only be modified right after allocation, and before any other `riff_...()` functions.
*/
int (*fp_printf)(const char * format, ... );
///@}
} riff_handle;
///@}
/**
* @defgroup RIFF_C C RIFF functions
* @{
*/
/**
* @name riff_handle allocation/deallocation functions
* @{
*/
/**
* @brief Allocate, initialize and return a riff_handle.
*
* @return Pointer to the intialized riff_handle.
*/
riff_handle *riff_handleAllocate();
/**
* @brief Free the memory allocated to a riff_handle.
*
* @param rh The riff_handle to free.
*/
void riff_handleFree(riff_handle *rh);
///@}
/**
* @name Parsing functions
* @{
*/
/**
* @brief Read to memory block.
*
* @param rh The riff_handle to use.
* @param to The pointer to read data to.
* @param size The amount of data to read.
*
* @return Amount of successfully read bytes.
*
* Does not read beyond end of chunk.
*
* Does not read the pad byte.
*/
size_t riff_readInChunk(riff_handle *rh, void *to, size_t size);
/**
* @brief Seek in current chunk.
*
* @param rh The riff_handle to use.
* @param c_pos The chunk position to seek to.
*
* @return RIFF error code.
*
* @note Only counts the actual data section of the chunk - position 0 is first byte after chunk size (chunk offset 8).
*/
int riff_seekInChunk(riff_handle *rh, size_t c_pos);
///@}
/**
* @name Chunk seeking functions
* @{
*/
/**
* @brief Seek to start of next chunk within current level.
*
* ID and size are read automatically.
*
* @param rh The riff_handle to use.
*
* @return RIFF error code.
*/
int riff_seekNextChunk(struct riff_handle *rh);
//int riff_seekNextChunkID(struct riff_handle *rh, char *id); //find and go to next chunk with id (4 byte) in current level, fails if not found - position is invalid then -> maybe not needed, the user can do it via simple loop
/**
* @brief Seek to data start of the current chunk.
*
* @param rh The riff_handle to use.
*
* @return RIFF error code.
*/
int riff_seekChunkStart(struct riff_handle *rh);
/**
* @brief Seek to the very first chunk of the file.
*
* Rewinds to the same position as just after opening via `riff_open_...()`.
*
* @param rh The riff_handle to use.
*
* @return RIFF error code.
*/
int riff_rewind(struct riff_handle *rh);
/**
* @brief Seek to the beginning of the current level.
*
* Seeks to the first data byte of the first chunk in current level.
*
* @param rh The riff_handle to use.
*
* @return RIFF error code.
*/
int riff_seekLevelStart(struct riff_handle *rh);
///@}
/**
* @name Level seeking functions
* @{
*/
/**
* @brief Go to sub level, load first chunk.
*
* @note Automatically seeks to the start of parent chunk's data.
*
* @param rh The riff_handle to use.
*
* @return RIFF error code.
*/
int riff_seekLevelSub(struct riff_handle *rh);
/**
* @brief Step back from sub level.
*
* @note The position does not change, you are still inside the data section of the parent list chunk!
*
* @param rh The riff_handle to use.
*
* @return RIFF error code.
*/
int riff_levelParent(struct riff_handle *rh);
/**
* @brief Step back from sub level, seek to start of current chunk
*
* @param rh The riff_handle to use.
*
* @return RIFF error code.
*/
int riff_seekLevelParentStart(struct riff_handle *rh);
/**
* @brief Step back from sub level, seek to start of next chunk
*
* @param rh The riff_handle to use.
*
* @return RIFF error code.
*/
int riff_seekLevelParentNext(struct riff_handle *rh);
///@}
/**
* @name Validation functions
*
* By the way, *you* are valid too ;)
*
* @{
*/
/**
* @brief Validate chunk level structure.
*
* Seeks to the first byte of the current level, then from header to header inside of the current chunk level.
*
* @note File position is changed by this function.
*
* @param rh The riff_handle to use.
*
* @return RIFF error code.
*/
int riff_levelValidate(struct riff_handle *rh);
/**
* @brief Validate file structure.
*
* Rewinds to the first chunk of the file, then from header to header inside of the current chunk level. If a level can contain subchunks, it is recursively checked.
*
* @note File position is changed by this function.
*
* @param rh The riff_handle to use.
*
* @return RIFF error code.
*/
int riff_fileValidate(struct riff_handle *rh);
///@}
/**
* @name Chunk counting functions
* @{
*/
/**
* @brief Count chunks in current level.
*
* Seeks back to the first chunk of the level, then header to header, counting the chunks. Does not recursively count subchunks.
*
* @note File position is changed by this function.
*
* @param rh The riff_handle to use.
*
* @return The amount of chunks in the current level, or -1 if an error occured.
*/
int32_t riff_amountOfChunksInLevel(struct riff_handle *rh);
/**
* @brief Count chunks with a certain ID in current level.
*
* Seeks back to the first chunk of the level, then header to header, counting the chunks with the matching ID. Does not recursively count subchunks.
*
* @note File position is changed by this function.
*
* @param rh The riff_handle to use.
* @param id The chunk ID to match against.
*
* @return The amount of chunks with the id in the current level, or -1 if an error occured.
*/
int32_t riff_amountOfChunksInLevelWithID(struct riff_handle *rh, const char * id);
///@}
/**
* @brief Return error string.
*
* @note The current position (riff_handle::pos) tells you where the problem occured in the file.
*
* @param e The error code.
*
* @return Pointer to string with the name of the error.
*/
const char *riff_errorToString(int e);
/**
* @name I/O Init functions
*
* Functions used by built-in open functions \n
* (To be used in your user-made open functions)
*
* @{
*/
/**
* @brief Read RIFF file header.
*
* To be caled from user I/O functions.
*
* @param rh The riff_handle to use.
*
* @return RIFF error code.
*/
int riff_readHeader(riff_handle *rh);
///@}
/**
* @name RIFF Open functions
*
* Use the following built in open-functions or make your own.
*
* Only pass a fresh allocated handle.
*
* @{
*/
/**
* @brief Initialize RIFF handle and set up FPs for C FILE access.
*
* @note File position must be at the start of the RIFF data (it can be nested in another file).
* @note Since the file was opened by the user, it must be closed by the user.
* @note The file size must be exact if > 0, use 0 for unknown size \n (the correct size helps to identify file corruption).
*
* @param rh The riff_handle to initialize.
* @param f The FILE pointer to read from.
* @param size The file size.
*
* @return RIFF error code.
*/
int riff_open_file(riff_handle *rh, FILE *f, size_t size);
/**
* @brief Initialize RIFF handle and set up FPs for memory access.
*
* @note Since the memory was allocated by the user, it must be deallocated by the user.
*
* @param rh The riff_handle to initialize.
* @param memptr The pointer to the memory area to read from.
* @param size The file size, must be > 0.
*
* @return RIFF error code.
*/
int riff_open_mem(riff_handle *rh, const void *memptr, size_t size);
//user open - must handle "riff_handle" allocation and setup
// e.g. for file access via network socket
// see and use "riff_open_file()" definition as template
// "int open_user(riff_handle *h, FOO, size_t size)";
///@}
///@}
#endif // _RIFF_H_