source: mainline/uspace/lib/riff/src/chunk.c@ 8bf9058

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 8bf9058 was 57d923e1, checked in by Jiri Svoboda <jiri@…>, 5 years ago

Fix BOTH 32-bit and 64-bit build

Clearly more work is needed to make libriff work with >2 GB files
on 32-bit platforms. However, we couldn't use standard fseek then.

  • Property mode set to 100644
File size: 10.9 KB
Line 
1/*
2 * Copyright (c) 2015 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup libriff
30 * @{
31 */
32/**
33 * @file RIFF chunk.
34 */
35
36#include <assert.h>
37#include <byteorder.h>
38#include <errno.h>
39#include <macros.h>
40#include <riff/chunk.h>
41#include <stdbool.h>
42#include <stdlib.h>
43
44/** Open RIFF file for writing
45 *
46 * @param fname File name
47 * @param rrw Place to store pointer to RIFF writer
48 *
49 * @return EOK on success, ENOMEM if out of memory, EIO if failed to open
50 * file.
51 */
52errno_t riff_wopen(const char *fname, riffw_t **rrw)
53{
54 riffw_t *rw;
55
56 rw = calloc(1, sizeof(riffw_t));
57 if (rw == NULL)
58 return ENOMEM;
59
60 rw->f = fopen(fname, "wb");
61 if (rw->f == NULL) {
62 free(rw);
63 return EIO;
64 }
65
66 *rrw = rw;
67 return EOK;
68}
69
70/** Close RIFF for writing.
71 *
72 * @param rw RIFF writer
73 * @return EOK on success. On write error EIO is returned and RIFF writer
74 * is destroyed anyway.
75 */
76errno_t riff_wclose(riffw_t *rw)
77{
78 int rv;
79
80 rv = fclose(rw->f);
81 free(rw);
82
83 return (rv == 0) ? EOK : EIO;
84}
85
86/** Write uint32_t value into RIFF file
87 *
88 * @param rw RIFF writer
89 * @param v Value
90 * @return EOK on success, EIO on error.
91 */
92errno_t riff_write_uint32(riffw_t *rw, uint32_t v)
93{
94 uint32_t vle;
95
96 vle = host2uint32_t_le(v);
97 if (fwrite(&vle, 1, sizeof(vle), rw->f) < sizeof(vle))
98 return EIO;
99
100 return EOK;
101}
102
103/** Begin writing chunk.
104 *
105 * @param rw RIFF writer
106 * @param ckid Chunk ID
107 * @param wchunk Pointer to chunk structure to fill in
108 *
109 * @return EOK on success, EIO on write error
110 */
111errno_t riff_wchunk_start(riffw_t *rw, riff_ckid_t ckid, riff_wchunk_t *wchunk)
112{
113 long pos;
114 errno_t rc;
115
116 pos = ftell(rw->f);
117 if (pos < 0)
118 return EIO;
119
120 wchunk->ckstart = pos + 2 * sizeof(uint32_t);
121
122 rc = riff_write_uint32(rw, ckid);
123 if (rc != EOK) {
124 assert(rc == EIO);
125 return EIO;
126 }
127
128 rc = riff_write_uint32(rw, 0);
129 if (rc != EOK) {
130 assert(rc == EIO);
131 return EIO;
132 }
133
134 return EOK;
135}
136
137/** Finish writing chunk.
138 *
139 * @param rw RIFF writer
140 * @param wchunk Pointer to chunk structure
141 *
142 * @return EOK on success, EIO error.
143 */
144errno_t riff_wchunk_end(riffw_t *rw, riff_wchunk_t *wchunk)
145{
146 long pos;
147 long cksize;
148 errno_t rc;
149
150 pos = ftell(rw->f);
151 if (pos < 0)
152 return EIO;
153
154 cksize = pos - wchunk->ckstart;
155 if (pos % 2 != 0)
156 ++pos;
157
158 if (fseek(rw->f, wchunk->ckstart - 4, SEEK_SET) < 0)
159 return EIO;
160
161 rc = riff_write_uint32(rw, cksize);
162 if (rc != EOK) {
163 assert(rc == EIO);
164 return EIO;
165 }
166
167 if (fseek(rw->f, pos, SEEK_SET) < 0)
168 return EIO;
169
170 return EOK;
171}
172
173/** Write data into RIFF file.
174 *
175 * @param rw RIFF writer
176 * @param data Pointer to data
177 * @param bytes Number of bytes to write
178 *
179 * @return EOK on success, EIO on error.
180 */
181errno_t riff_write(riffw_t *rw, void *data, size_t bytes)
182{
183 size_t nw;
184
185 nw = fwrite(data, 1, bytes, rw->f);
186 if (nw != bytes)
187 return EIO;
188
189 return EOK;
190}
191
192/** Open RIFF file for reading.
193 *
194 * @param fname File name
195 * @param riffck Place to store root (RIFF) chunk
196 * @param rrr Place to store pointer to RIFF reader
197 *
198 * @return EOK on success, ENOMEM if out of memory, EIO if failed to open
199 * file..
200 */
201errno_t riff_ropen(const char *fname, riff_rchunk_t *riffck, riffr_t **rrr)
202{
203 riffr_t *rr;
204 riff_rchunk_t fchunk;
205 long fsize;
206 errno_t rc;
207 int rv;
208
209 rr = calloc(1, sizeof(riffr_t));
210 if (rr == NULL) {
211 rc = ENOMEM;
212 goto error;
213 }
214
215 rr->f = fopen(fname, "rb");
216 if (rr->f == NULL) {
217 rc = EIO;
218 goto error;
219 }
220
221 rv = fseek(rr->f, 0, SEEK_END);
222 if (rv < 0) {
223 rc = EIO;
224 goto error;
225 }
226
227 fsize = ftell(rr->f);
228 if (fsize < 0) {
229 rc = EIO;
230 goto error;
231 }
232
233 rv = fseek(rr->f, 0, SEEK_SET);
234 if (rv < 0) {
235 rc = EIO;
236 goto error;
237 }
238
239 fchunk.riffr = rr;
240 fchunk.ckstart = 0;
241 fchunk.cksize = fsize;
242
243 rc = riff_rchunk_start(&fchunk, riffck);
244 if (rc != EOK)
245 goto error;
246
247 *rrr = rr;
248 return EOK;
249error:
250 if (rr != NULL && rr->f != NULL)
251 fclose(rr->f);
252 free(rr);
253 return rc;
254}
255
256/** Close RIFF for reading.
257 *
258 * @param rr RIFF reader
259 * @return EOK on success, EIO on error.
260 */
261errno_t riff_rclose(riffr_t *rr)
262{
263 errno_t rc;
264
265 rc = fclose(rr->f);
266 free(rr);
267 return rc == 0 ? EOK : EIO;
268}
269
270/** Read uint32_t from RIFF file.
271 *
272 * @param rchunk RIFF chunk
273 * @param v Place to store value
274 * @return EOK on success, ELIMIT if at the end of parent chunk, EIO on error.
275 */
276errno_t riff_read_uint32(riff_rchunk_t *rchunk, uint32_t *v)
277{
278 uint32_t vle;
279 errno_t rc;
280 size_t nread;
281
282 rc = riff_read(rchunk, &vle, sizeof(vle), &nread);
283 if (rc != EOK)
284 return rc;
285
286 if (nread != sizeof(vle))
287 return ELIMIT;
288
289 *v = uint32_t_le2host(vle);
290 return EOK;
291}
292
293/** Start reading RIFF chunk.
294 *
295 * @param parent Parent chunk
296 * @param rchunk Pointer to chunk structure to fill in
297 *
298 * @return EOK on success, ELIMIT if at the end of parent chunk,
299 * EIO on error.
300 */
301errno_t riff_rchunk_start(riff_rchunk_t *parent, riff_rchunk_t *rchunk)
302{
303 errno_t rc;
304 long pos;
305
306 pos = ftell(parent->riffr->f);
307 if (pos < 0) {
308 rc = EIO;
309 goto error;
310 }
311
312 rchunk->riffr = parent->riffr;
313 rchunk->ckstart = pos + 8;
314 rc = riff_read_uint32(parent, &rchunk->ckid);
315 if (rc != EOK)
316 goto error;
317 rc = riff_read_uint32(parent, &rchunk->cksize);
318 if (rc != EOK)
319 goto error;
320
321 return EOK;
322error:
323 return rc;
324}
325
326/** Find and start reading RIFF chunk of with specific chunk ID.
327 * Other types of chunks are skipped.
328 *
329 * @param parent Parent chunk
330 * @param rchunk Pointer to chunk structure to fill in
331 *
332 * @return EOK on success, ENOENT chunk was not found and end was reached
333 * EIO on error.
334 */
335errno_t riff_rchunk_match(riff_rchunk_t *parent, riff_ckid_t ckid,
336 riff_rchunk_t *rchunk)
337{
338 errno_t rc;
339
340 while (true) {
341 rc = riff_rchunk_start(parent, rchunk);
342 if (rc == ELIMIT)
343 return ENOENT;
344 if (rc != EOK)
345 return rc;
346
347 if (rchunk->ckid == ckid)
348 break;
349
350 rc = riff_rchunk_end(rchunk);
351 if (rc != EOK)
352 return rc;
353 }
354
355 return EOK;
356}
357
358/** Find and start reading RIFF LIST chunk of specified type.
359 * Other chunks or LIST chunks of other type are skipped.
360 *
361 * @param parent Parent chunk
362 * @param rchunk Pointer to chunk structure to fill in
363 *
364 * @return EOK on success, ENOENT chunk was not found and end was reached
365 * EIO on error.
366 */
367errno_t riff_rchunk_list_match(riff_rchunk_t *parent, riff_ltype_t ltype,
368 riff_rchunk_t *rchunk)
369{
370 errno_t rc;
371 riff_ltype_t rltype;
372
373 while (true) {
374 rc = riff_rchunk_match(parent, CKID_LIST, rchunk);
375 if (rc == ELIMIT)
376 return ENOENT;
377 if (rc != EOK)
378 return rc;
379
380 rc = riff_read_uint32(parent, &rltype);
381 if (rc != EOK)
382 return rc;
383
384 if (rltype == ltype)
385 break;
386
387 rc = riff_rchunk_end(rchunk);
388 if (rc != EOK)
389 return rc;
390 }
391
392 return EOK;
393}
394
395/** Seek to position in chunk.
396 *
397 * @param rchunk RIFF chunk
398 * @param offset Offset
399 * @param whence SEEK_SET, SEEK_CUR or SEEK_END
400 * @return EOK on success or an error code
401 */
402errno_t riff_rchunk_seek(riff_rchunk_t *rchunk, long offset, int whence)
403{
404 long pos;
405 long dest;
406 int rv;
407
408 switch (whence) {
409 case SEEK_SET:
410 dest = rchunk->ckstart + offset;
411 break;
412 case SEEK_END:
413 dest = rchunk->ckstart + rchunk->cksize + offset;
414 break;
415 case SEEK_CUR:
416 pos = ftell(rchunk->riffr->f);
417 if (pos < 0)
418 return EIO;
419 dest = pos + offset;
420 break;
421 default:
422 return EINVAL;
423 }
424
425 if (dest < rchunk->ckstart || (unsigned long) dest >
426 (unsigned long) rchunk->ckstart + rchunk->cksize) {
427 return ELIMIT;
428 }
429
430 rv = fseek(rchunk->riffr->f, dest, SEEK_SET);
431 if (rv < 0)
432 return EIO;
433
434 return EOK;
435}
436
437/** Return chunk data size.
438 *
439 * @param rchunk RIFF chunk
440 * @return Pure data size (excluding type+size header) in bytes
441 */
442uint32_t riff_rchunk_size(riff_rchunk_t *rchunk)
443{
444 return rchunk->cksize;
445}
446
447/** Return file offset where chunk ends
448 *
449 * @param rchunk RIFF chunk
450 * @return File offset just after last data byte of the chunk
451 */
452static long riff_rchunk_get_end(riff_rchunk_t *rchunk)
453{
454 return rchunk->ckstart + rchunk->cksize;
455}
456
457/** Return file offset of first (non-padding) byte after end of chunk.
458 *
459 * @param rchunk RIFF chunk
460 * @return File offset of first non-padding byte after end of chunk
461 */
462static long riff_rchunk_get_ndpos(riff_rchunk_t *rchunk)
463{
464 long ckend;
465
466 ckend = riff_rchunk_get_end(rchunk);
467 if ((ckend % 2) != 0)
468 return ckend + 1;
469 else
470 return ckend;
471}
472
473/** Finish reading RIFF chunk.
474 *
475 * Seek to the first byte after end of chunk. It is allowed, though,
476 * to return to the chunk later, e.g. using riff_rchunk_seek(@a rchunk, ..).
477 *
478 * @param rchunk Chunk structure
479 * @return EOK on success, EIO on error.
480 */
481errno_t riff_rchunk_end(riff_rchunk_t *rchunk)
482{
483 long ckend;
484
485 ckend = riff_rchunk_get_ndpos(rchunk);
486 if (fseek(rchunk->riffr->f, ckend, SEEK_SET) < 0)
487 return EIO;
488
489 return EOK;
490}
491
492/** Read data from RIFF chunk.
493 *
494 * Attempt to read @a bytes bytes from the chunk. If there is less data
495 * left until the end of the chunk, less will be read. The actual number
496 * of bytes read is returned in @a *nbytes (can even be 0).
497 *
498 * @param rchunk RIFF chunk for reading
499 * @param buf Buffer to read to
500 * @param bytes Number of bytes to read
501 * @param nread Place to store number of bytes actually read
502 *
503 * @return EOK on success, ELIMIT if file position is not within @a rchunk,
504 * EIO on I/O error.
505 */
506errno_t riff_read(riff_rchunk_t *rchunk, void *buf, size_t bytes,
507 size_t *nread)
508{
509 long pos;
510 long ckend;
511 long toread;
512
513 pos = ftell(rchunk->riffr->f);
514 if (pos < 0)
515 return EIO;
516
517 ckend = riff_rchunk_get_end(rchunk);
518 if (pos < rchunk->ckstart || pos > ckend)
519 return ELIMIT;
520
521 toread = min(bytes, (size_t)(ckend - pos));
522 if (toread == 0) {
523 *nread = 0;
524 return EOK;
525 }
526
527 *nread = fread(buf, 1, toread, rchunk->riffr->f);
528 if (*nread == 0)
529 return EIO;
530
531 return EOK;
532}
533
534/** @}
535 */
Note: See TracBrowser for help on using the repository browser.