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

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

Fix reading and writing chunk padding

You cannot use riff_read() to read the chunk padding, because it's not
part of the chunk data proper. This fixes libgfxfont's unit test, which
could not read back the TPF it wrote.

Also, it seems prudent to actually write the padding, not just seek
over it. (Might not be a problem but, just in case…)

  • Property mode set to 100644
File size: 11.4 KB
RevLine 
[d53af3c8]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>
[aaf962e6]41#include <stdbool.h>
[d53af3c8]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 */
[0ee3157]52errno_t riff_wopen(const char *fname, riffw_t **rrw)
[d53af3c8]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 */
[0ee3157]76errno_t riff_wclose(riffw_t *rw)
[d53af3c8]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 */
[0ee3157]92errno_t riff_write_uint32(riffw_t *rw, uint32_t v)
[d53af3c8]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 */
[0ee3157]111errno_t riff_wchunk_start(riffw_t *rw, riff_ckid_t ckid, riff_wchunk_t *wchunk)
[d53af3c8]112{
113 long pos;
[0ee3157]114 errno_t rc;
[d53af3c8]115
116 pos = ftell(rw->f);
117 if (pos < 0)
118 return EIO;
119
[003c413]120 wchunk->ckstart = pos + 2 * sizeof(uint32_t);
[d53af3c8]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 */
[0ee3157]144errno_t riff_wchunk_end(riffw_t *rw, riff_wchunk_t *wchunk)
[d53af3c8]145{
146 long pos;
147 long cksize;
[bad765a]148 uint8_t pad;
149 size_t nw;
[0ee3157]150 errno_t rc;
[d53af3c8]151
152 pos = ftell(rw->f);
153 if (pos < 0)
154 return EIO;
155
[003c413]156 cksize = pos - wchunk->ckstart;
[bad765a]157 if (pos % 2 != 0) {
[d145ecb]158 ++pos;
[bad765a]159 pad = 0;
160 nw = fwrite(&pad, 1, sizeof(pad), rw->f);
161 if (nw != sizeof(pad))
162 return EIO;
163 }
[d53af3c8]164
[003c413]165 if (fseek(rw->f, wchunk->ckstart - 4, SEEK_SET) < 0)
[d53af3c8]166 return EIO;
167
168 rc = riff_write_uint32(rw, cksize);
169 if (rc != EOK) {
170 assert(rc == EIO);
171 return EIO;
172 }
173
174 if (fseek(rw->f, pos, SEEK_SET) < 0)
175 return EIO;
176
177 return EOK;
178}
179
180/** Write data into RIFF file.
181 *
182 * @param rw RIFF writer
183 * @param data Pointer to data
184 * @param bytes Number of bytes to write
185 *
186 * @return EOK on success, EIO on error.
187 */
[d145ecb]188errno_t riff_write(riffw_t *rw, void *data, size_t bytes)
[d53af3c8]189{
190 size_t nw;
191
192 nw = fwrite(data, 1, bytes, rw->f);
193 if (nw != bytes)
194 return EIO;
195
196 return EOK;
197}
198
199/** Open RIFF file for reading.
200 *
201 * @param fname File name
[003c413]202 * @param riffck Place to store root (RIFF) chunk
[d53af3c8]203 * @param rrr Place to store pointer to RIFF reader
204 *
205 * @return EOK on success, ENOMEM if out of memory, EIO if failed to open
206 * file..
207 */
[003c413]208errno_t riff_ropen(const char *fname, riff_rchunk_t *riffck, riffr_t **rrr)
[d53af3c8]209{
210 riffr_t *rr;
[003c413]211 riff_rchunk_t fchunk;
212 long fsize;
[0ee3157]213 errno_t rc;
[003c413]214 int rv;
[d53af3c8]215
216 rr = calloc(1, sizeof(riffr_t));
217 if (rr == NULL) {
218 rc = ENOMEM;
219 goto error;
220 }
221
[20667af]222 rr->pos = 0;
223
[d53af3c8]224 rr->f = fopen(fname, "rb");
225 if (rr->f == NULL) {
226 rc = EIO;
227 goto error;
228 }
229
[003c413]230 rv = fseek(rr->f, 0, SEEK_END);
231 if (rv < 0) {
232 rc = EIO;
233 goto error;
234 }
235
236 fsize = ftell(rr->f);
237 if (fsize < 0) {
238 rc = EIO;
239 goto error;
240 }
241
242 rv = fseek(rr->f, 0, SEEK_SET);
243 if (rv < 0) {
244 rc = EIO;
245 goto error;
246 }
247
248 fchunk.riffr = rr;
249 fchunk.ckstart = 0;
250 fchunk.cksize = fsize;
251
252 rc = riff_rchunk_start(&fchunk, riffck);
253 if (rc != EOK)
254 goto error;
255
[d53af3c8]256 *rrr = rr;
257 return EOK;
258error:
259 if (rr != NULL && rr->f != NULL)
260 fclose(rr->f);
261 free(rr);
262 return rc;
263}
264
265/** Close RIFF for reading.
266 *
267 * @param rr RIFF reader
268 * @return EOK on success, EIO on error.
269 */
[0ee3157]270errno_t riff_rclose(riffr_t *rr)
[d53af3c8]271{
[0ee3157]272 errno_t rc;
[d53af3c8]273
274 rc = fclose(rr->f);
275 free(rr);
276 return rc == 0 ? EOK : EIO;
277}
278
279/** Read uint32_t from RIFF file.
280 *
[003c413]281 * @param rchunk RIFF chunk
[d53af3c8]282 * @param v Place to store value
[aaf962e6]283 * @return EOK on success, ELIMIT if at the end of parent chunk, EIO on error.
[d53af3c8]284 */
[003c413]285errno_t riff_read_uint32(riff_rchunk_t *rchunk, uint32_t *v)
[d53af3c8]286{
287 uint32_t vle;
[003c413]288 errno_t rc;
289 size_t nread;
[d53af3c8]290
[003c413]291 rc = riff_read(rchunk, &vle, sizeof(vle), &nread);
292 if (rc != EOK)
293 return rc;
[d53af3c8]294
[d145ecb]295 if (nread != sizeof(vle))
296 return ELIMIT;
297
[d53af3c8]298 *v = uint32_t_le2host(vle);
299 return EOK;
300}
301
302/** Start reading RIFF chunk.
303 *
[003c413]304 * @param parent Parent chunk
[d53af3c8]305 * @param rchunk Pointer to chunk structure to fill in
306 *
[aaf962e6]307 * @return EOK on success, ELIMIT if at the end of parent chunk,
308 * EIO on error.
[d53af3c8]309 */
[003c413]310errno_t riff_rchunk_start(riff_rchunk_t *parent, riff_rchunk_t *rchunk)
[d53af3c8]311{
[0ee3157]312 errno_t rc;
[d53af3c8]313
[003c413]314 rchunk->riffr = parent->riffr;
[20667af]315 rchunk->ckstart = parent->riffr->pos + 8;
[003c413]316 rc = riff_read_uint32(parent, &rchunk->ckid);
[d53af3c8]317 if (rc != EOK)
318 goto error;
[003c413]319 rc = riff_read_uint32(parent, &rchunk->cksize);
[d53af3c8]320 if (rc != EOK)
321 goto error;
322
323 return EOK;
324error:
325 return rc;
326}
327
[aaf962e6]328/** Find and start reading RIFF chunk of with specific chunk ID.
329 * Other types of chunks are skipped.
330 *
331 * @param parent Parent chunk
332 * @param rchunk Pointer to chunk structure to fill in
333 *
334 * @return EOK on success, ENOENT chunk was not found and end was reached
335 * EIO on error.
336 */
337errno_t riff_rchunk_match(riff_rchunk_t *parent, riff_ckid_t ckid,
338 riff_rchunk_t *rchunk)
339{
340 errno_t rc;
341
342 while (true) {
343 rc = riff_rchunk_start(parent, rchunk);
344 if (rc == ELIMIT)
345 return ENOENT;
346 if (rc != EOK)
347 return rc;
348
349 if (rchunk->ckid == ckid)
350 break;
351
352 rc = riff_rchunk_end(rchunk);
353 if (rc != EOK)
354 return rc;
355 }
356
357 return EOK;
358}
359
360/** Find and start reading RIFF LIST chunk of specified type.
361 * Other chunks or LIST chunks of other type are skipped.
362 *
363 * @param parent Parent chunk
364 * @param rchunk Pointer to chunk structure to fill in
365 *
366 * @return EOK on success, ENOENT chunk was not found and end was reached
367 * EIO on error.
368 */
369errno_t riff_rchunk_list_match(riff_rchunk_t *parent, riff_ltype_t ltype,
370 riff_rchunk_t *rchunk)
371{
372 errno_t rc;
373 riff_ltype_t rltype;
374
375 while (true) {
376 rc = riff_rchunk_match(parent, CKID_LIST, rchunk);
377 if (rc == ELIMIT)
378 return ENOENT;
379 if (rc != EOK)
380 return rc;
381
382 rc = riff_read_uint32(parent, &rltype);
383 if (rc != EOK)
384 return rc;
385
386 if (rltype == ltype)
387 break;
388
389 rc = riff_rchunk_end(rchunk);
390 if (rc != EOK)
391 return rc;
392 }
393
394 return EOK;
395}
396
[ea459d4]397/** Seek to position in chunk.
398 *
399 * @param rchunk RIFF chunk
400 * @param offset Offset
401 * @param whence SEEK_SET, SEEK_CUR or SEEK_END
402 * @return EOK on success or an error code
403 */
404errno_t riff_rchunk_seek(riff_rchunk_t *rchunk, long offset, int whence)
405{
406 long dest;
407 int rv;
408
409 switch (whence) {
410 case SEEK_SET:
411 dest = rchunk->ckstart + offset;
412 break;
413 case SEEK_END:
414 dest = rchunk->ckstart + rchunk->cksize + offset;
415 break;
416 case SEEK_CUR:
[20667af]417 dest = rchunk->riffr->pos + offset;
[ea459d4]418 break;
419 default:
420 return EINVAL;
421 }
422
[57d923e1]423 if (dest < rchunk->ckstart || (unsigned long) dest >
424 (unsigned long) rchunk->ckstart + rchunk->cksize) {
[ea459d4]425 return ELIMIT;
[efe0881]426 }
[ea459d4]427
428 rv = fseek(rchunk->riffr->f, dest, SEEK_SET);
429 if (rv < 0)
430 return EIO;
431
[20667af]432 rchunk->riffr->pos = dest;
[ea459d4]433 return EOK;
434}
435
[d145ecb]436/** Return chunk data size.
437 *
438 * @param rchunk RIFF chunk
439 * @return Pure data size (excluding type+size header) in bytes
440 */
441uint32_t riff_rchunk_size(riff_rchunk_t *rchunk)
442{
443 return rchunk->cksize;
444}
445
[d53af3c8]446/** Return file offset where chunk ends
447 *
448 * @param rchunk RIFF chunk
449 * @return File offset just after last data byte of the chunk
450 */
451static long riff_rchunk_get_end(riff_rchunk_t *rchunk)
452{
[003c413]453 return rchunk->ckstart + rchunk->cksize;
[d53af3c8]454}
455
456/** Return file offset of first (non-padding) byte after end of chunk.
457 *
458 * @param rchunk RIFF chunk
459 * @return File offset of first non-padding byte after end of chunk
460 */
461static long riff_rchunk_get_ndpos(riff_rchunk_t *rchunk)
462{
463 long ckend;
464
465 ckend = riff_rchunk_get_end(rchunk);
466 if ((ckend % 2) != 0)
467 return ckend + 1;
468 else
469 return ckend;
470}
471
472/** Finish reading RIFF chunk.
473 *
[ea459d4]474 * Seek to the first byte after end of chunk. It is allowed, though,
475 * to return to the chunk later, e.g. using riff_rchunk_seek(@a rchunk, ..).
[d53af3c8]476 *
477 * @param rchunk Chunk structure
478 * @return EOK on success, EIO on error.
479 */
[003c413]480errno_t riff_rchunk_end(riff_rchunk_t *rchunk)
[d53af3c8]481{
482 long ckend;
[20667af]483 uint8_t byte;
484 size_t nread;
[d53af3c8]485
486 ckend = riff_rchunk_get_ndpos(rchunk);
[20667af]487 if (rchunk->riffr->pos < ckend &&
488 ckend <= rchunk->riffr->pos + 512) {
489 /* (Buffered) reading is faster than seeking */
490 while (rchunk->riffr->pos < ckend) {
[bad765a]491 nread = fread(&byte, 1, sizeof(byte), rchunk->riffr->f);
[20667af]492 if (nread != sizeof(byte))
493 return EIO;
[bad765a]494
495 rchunk->riffr->pos += sizeof(byte);
[20667af]496 }
497
498 } else if (rchunk->riffr->pos != ckend) {
499 /* Need to seek (backwards or too far) */
500 if (fseek(rchunk->riffr->f, ckend, SEEK_SET) < 0)
501 return EIO;
[bad765a]502
[20667af]503 rchunk->riffr->pos = ckend;
504 }
[d53af3c8]505
506 return EOK;
507}
508
509/** Read data from RIFF chunk.
510 *
511 * Attempt to read @a bytes bytes from the chunk. If there is less data
512 * left until the end of the chunk, less will be read. The actual number
513 * of bytes read is returned in @a *nbytes (can even be 0).
514 *
[003c413]515 * @param rchunk RIFF chunk for reading
[d53af3c8]516 * @param buf Buffer to read to
517 * @param bytes Number of bytes to read
518 * @param nread Place to store number of bytes actually read
519 *
520 * @return EOK on success, ELIMIT if file position is not within @a rchunk,
521 * EIO on I/O error.
522 */
[003c413]523errno_t riff_read(riff_rchunk_t *rchunk, void *buf, size_t bytes,
524 size_t *nread)
[d53af3c8]525{
526 long pos;
527 long ckend;
528 long toread;
529
[20667af]530 pos = rchunk->riffr->pos;
[d53af3c8]531
532 ckend = riff_rchunk_get_end(rchunk);
533 if (pos < rchunk->ckstart || pos > ckend)
534 return ELIMIT;
535
536 toread = min(bytes, (size_t)(ckend - pos));
537 if (toread == 0) {
538 *nread = 0;
539 return EOK;
540 }
541
[003c413]542 *nread = fread(buf, 1, toread, rchunk->riffr->f);
[d53af3c8]543 if (*nread == 0)
544 return EIO;
545
[20667af]546 rchunk->riffr->pos += *nread;
[d53af3c8]547 return EOK;
548}
549
550/** @}
551 */
Note: See TracBrowser for help on using the repository browser.