]> git.xonotic.org Git - xonotic/darkplaces.git/blob - dpvsimpledecode.c
you can now (try to) play in maps you don't have, and models you don't have are shown...
[xonotic/darkplaces.git] / dpvsimpledecode.c
1
2 #include <stdlib.h>
3 #include <stdio.h>
4 #include <math.h>
5 #include <string.h>
6 #include "dpvsimpledecode.h"
7 #include "wavefile.h"
8
9 #define EMBEDDEDHZREAD 1
10
11 #ifndef EMBEDDEDHZREAD
12 #include "hz_read.h"
13 #include "hz_read.c"
14 #else
15 #define HZREADERROR_OK 0
16 #define HZREADERROR_EOF 1
17 #define HZREADERROR_MALLOCFAILED 2
18
19 #define HZREADBLOCKSIZE 16000
20
21 typedef struct
22 {
23         FILE *file;
24         int endoffile;
25 }
26 hz_bitstream_read_t;
27
28 typedef struct hz_bitstream_readblock_s
29 {
30         struct hz_bitstream_readblock_s *next;
31         unsigned int size;
32         unsigned char data[HZREADBLOCKSIZE];
33 }
34 hz_bitstream_readblock_t;
35
36 typedef struct
37 {
38         hz_bitstream_readblock_t *blocks;
39         hz_bitstream_readblock_t *current;
40         unsigned int position;
41         unsigned int store;
42         int count;
43 }
44 hz_bitstream_readblocks_t;
45
46 hz_bitstream_read_t *hz_bitstream_read_open(char *filename)
47 {
48         FILE *file;
49         hz_bitstream_read_t *stream;
50         if ((file = fopen(filename, "rb")))
51         {
52                 stream = malloc(sizeof(hz_bitstream_read_t));
53                 memset(stream, 0, sizeof(*stream));
54                 stream->file = file;
55                 return stream;
56         }
57         else
58                 return NULL;
59 }
60
61 void hz_bitstream_read_close(hz_bitstream_read_t *stream)
62 {
63         if (stream)
64         {
65                 fclose(stream->file);
66                 free(stream);
67         }
68 }
69
70 unsigned int hz_bitstream_read_currentbyte(hz_bitstream_read_t *stream)
71 {
72         return ftell(stream->file);
73 }
74
75 int hz_bitstream_read_seek(hz_bitstream_read_t *stream, unsigned int position)
76 {
77         stream->endoffile = 0;
78         return fseek(stream->file, position, SEEK_SET) != 0;
79 }
80
81 hz_bitstream_readblocks_t *hz_bitstream_read_blocks_new(void)
82 {
83         hz_bitstream_readblocks_t *blocks;
84         blocks = malloc(sizeof(hz_bitstream_readblocks_t));
85         if (blocks == NULL)
86                 return NULL;
87         memset(blocks, 0, sizeof(hz_bitstream_readblocks_t));
88         return blocks;
89 }
90
91 void hz_bitstream_read_blocks_free(hz_bitstream_readblocks_t *blocks)
92 {
93         hz_bitstream_readblock_t *b, *n;
94         if (blocks == NULL)
95                 return;
96         for (b = blocks->blocks;b;b = n)
97         {
98                 n = b->next;
99                 free(b);
100         }
101         free(blocks);
102 }
103
104 void hz_bitstream_read_flushbits(hz_bitstream_readblocks_t *blocks)
105 {
106         blocks->store = 0;
107         blocks->count = 0;
108 }
109
110 int hz_bitstream_read_blocks_read(hz_bitstream_readblocks_t *blocks, hz_bitstream_read_t *stream, unsigned int size)
111 {
112         int s;
113         hz_bitstream_readblock_t *b, *p;
114         s = size;
115         p = NULL;
116         b = blocks->blocks;
117         while (s > 0)
118         {
119                 if (b == NULL)
120                 {
121                         b = malloc(sizeof(hz_bitstream_readblock_t));
122                         if (b == NULL)
123                                 return HZREADERROR_MALLOCFAILED;
124                         b->next = NULL;
125                         b->size = 0;
126                         if (p != NULL)
127                                 p->next = b;
128                         else
129                                 blocks->blocks = b;
130                 }
131                 if (s > HZREADBLOCKSIZE)
132                         b->size = HZREADBLOCKSIZE;
133                 else
134                         b->size = s;
135                 s -= b->size;
136                 if (fread(b->data, 1, b->size, stream->file) != b->size)
137                 {
138                         stream->endoffile = 1;
139                         break;
140                 }
141                 p = b;
142                 b = b->next;
143         }
144         while (b)
145         {
146                 b->size = 0;
147                 b = b->next;
148         }
149         blocks->current = blocks->blocks;
150         blocks->position = 0;
151         hz_bitstream_read_flushbits(blocks);
152         if (stream->endoffile)
153                 return HZREADERROR_EOF;
154         return HZREADERROR_OK;
155 }
156
157 unsigned int hz_bitstream_read_blocks_getbyte(hz_bitstream_readblocks_t *blocks)
158 {
159         while (blocks->current != NULL && blocks->position >= blocks->current->size)
160         {
161                 blocks->position = 0;
162                 blocks->current = blocks->current->next;
163         }
164         if (blocks->current == NULL)
165                 return 0;
166         return blocks->current->data[blocks->position++];
167 }
168
169 int hz_bitstream_read_bit(hz_bitstream_readblocks_t *blocks)
170 {
171         if (!blocks->count)
172         {
173                 blocks->count += 8;
174                 blocks->store <<= 8;
175                 blocks->store |= hz_bitstream_read_blocks_getbyte(blocks) & 0xFF;
176         }
177         blocks->count--;
178         return (blocks->store >> blocks->count) & 1;
179 }
180
181 unsigned int hz_bitstream_read_bits(hz_bitstream_readblocks_t *blocks, int size)
182 {
183         unsigned int num = 0;
184         // we can only handle about 24 bits at a time safely
185         // (there might be up to 7 bits more than we need in the bit store)
186         if (size > 24)
187         {
188                 size -= 8;
189                 num |= hz_bitstream_read_bits(blocks, 8) << size;
190         }
191         while (blocks->count < size)
192         {
193                 blocks->count += 8;
194                 blocks->store <<= 8;
195                 blocks->store |= hz_bitstream_read_blocks_getbyte(blocks) & 0xFF;
196         }
197         blocks->count -= size;
198         num |= (blocks->store >> blocks->count) & ((1 << size) - 1);
199         return num;
200 }
201
202 unsigned int hz_bitstream_read_byte(hz_bitstream_readblocks_t *blocks)
203 {
204         return hz_bitstream_read_blocks_getbyte(blocks);
205 }
206
207 unsigned int hz_bitstream_read_short(hz_bitstream_readblocks_t *blocks)
208 {
209         return (hz_bitstream_read_byte(blocks) << 8)
210              | (hz_bitstream_read_byte(blocks));
211 }
212
213 unsigned int hz_bitstream_read_int(hz_bitstream_readblocks_t *blocks)
214 {
215         return (hz_bitstream_read_byte(blocks) << 24)
216              | (hz_bitstream_read_byte(blocks) << 16)
217              | (hz_bitstream_read_byte(blocks) << 8)
218              | (hz_bitstream_read_byte(blocks));
219 }
220
221 void hz_bitstream_read_bytes(hz_bitstream_readblocks_t *blocks, void *outdata, unsigned int size)
222 {
223         unsigned char *out;
224         out = outdata;
225         while (size--)
226                 *out++ = hz_bitstream_read_byte(blocks);
227 }
228 #endif
229
230 #define BLOCKSIZE 8
231
232 typedef struct dpvsimpledecodestream_s
233 {
234         hz_bitstream_read_t *bitstream;
235         hz_bitstream_readblocks_t *framedatablocks;
236
237         int error;
238
239         double info_framerate;
240         unsigned int info_frames;
241
242         unsigned int info_imagewidth;
243         unsigned int info_imageheight;
244         unsigned int info_imagebpp;
245         unsigned int info_imageRloss;
246         unsigned int info_imageRmask;
247         unsigned int info_imageRshift;
248         unsigned int info_imageGloss;
249         unsigned int info_imageGmask;
250         unsigned int info_imageGshift;
251         unsigned int info_imageBloss;
252         unsigned int info_imageBmask;
253         unsigned int info_imageBshift;
254         unsigned int info_imagesize;
255
256         // current video frame (needed because of delta compression)
257         int videoframenum;
258         // current video frame data (needed because of delta compression)
259         unsigned int *videopixels;
260
261         // wav file the sound is being read from
262         wavefile_t *wavefile;
263 }
264 dpvsimpledecodestream_t;
265
266 static int dpvsimpledecode_setpixelformat(dpvsimpledecodestream_t *s, unsigned int Rmask, unsigned int Gmask, unsigned int Bmask, unsigned int bytesperpixel)
267 {
268         int Rshift, Rbits, Gshift, Gbits, Bshift, Bbits;
269         if (!Rmask)
270         {
271                 s->error = DPVSIMPLEDECODEERROR_INVALIDRMASK;
272                 return s->error;
273         }
274         if (!Gmask)
275         {
276                 s->error = DPVSIMPLEDECODEERROR_INVALIDGMASK;
277                 return s->error;
278         }
279         if (!Bmask)
280         {
281                 s->error = DPVSIMPLEDECODEERROR_INVALIDBMASK;
282                 return s->error;
283         }
284         if (Rmask & Gmask || Rmask & Bmask || Gmask & Bmask)
285         {
286                 s->error = DPVSIMPLEDECODEERROR_COLORMASKSOVERLAP;
287                 return s->error;
288         }
289         switch (bytesperpixel)
290         {
291         case 2:
292                 if ((Rmask | Gmask | Bmask) > 65536)
293                 {
294                         s->error = DPVSIMPLEDECODEERROR_COLORMASKSEXCEEDBPP;
295                         return s->error;
296                 }
297                 break;
298         case 4:
299                 break;
300         default:
301                 s->error = DPVSIMPLEDECODEERROR_UNSUPPORTEDBPP;
302                 return s->error;
303                 break;
304         }
305         for (Rshift = 0;!(Rmask & 1);Rshift++, Rmask >>= 1);
306         for (Gshift = 0;!(Gmask & 1);Gshift++, Gmask >>= 1);
307         for (Bshift = 0;!(Bmask & 1);Bshift++, Bmask >>= 1);
308         if (((Rmask + 1) & Rmask) != 0)
309         {
310                 s->error = DPVSIMPLEDECODEERROR_INVALIDRMASK;
311                 return s->error;
312         }
313         if (((Gmask + 1) & Gmask) != 0)
314         {
315                 s->error = DPVSIMPLEDECODEERROR_INVALIDGMASK;
316                 return s->error;
317         }
318         if (((Bmask + 1) & Bmask) != 0)
319         {
320                 s->error = DPVSIMPLEDECODEERROR_INVALIDBMASK;
321                 return s->error;
322         }
323         for (Rbits = 0;Rmask & 1;Rbits++, Rmask >>= 1);
324         for (Gbits = 0;Gmask & 1;Gbits++, Gmask >>= 1);
325         for (Bbits = 0;Bmask & 1;Bbits++, Bmask >>= 1);
326         if (Rbits > 8)
327         {
328                 Rshift += (Rbits - 8);
329                 Rbits = 8;
330         }
331         if (Gbits > 8)
332         {
333                 Gshift += (Gbits - 8);
334                 Gbits = 8;
335         }
336         if (Bbits > 8)
337         {
338                 Bshift += (Bbits - 8);
339                 Bbits = 8;
340         }
341         s->info_imagebpp = bytesperpixel;
342         s->info_imageRloss = 16 + (8 - Rbits);
343         s->info_imageGloss =  8 + (8 - Gbits);
344         s->info_imageBloss =  0 + (8 - Bbits);
345         s->info_imageRmask = (1 << Rbits) - 1;
346         s->info_imageGmask = (1 << Gbits) - 1;
347         s->info_imageBmask = (1 << Bbits) - 1;
348         s->info_imageRshift = Rshift;
349         s->info_imageGshift = Gshift;
350         s->info_imageBshift = Bshift;
351         s->info_imagesize = s->info_imagewidth * s->info_imageheight * s->info_imagebpp;
352         return s->error;
353 }
354
355 // opening and closing streams
356
357 static void StripExtension(char *in, char *out)
358 {
359         char *dot, *c;
360         dot = NULL;
361         for (c = in;*c;c++)
362         {
363                 if (*c == ':' || *c == '\\' || *c == '/')
364                         dot = NULL;
365                 if (*c == '.')
366                         dot = c;
367         }
368         if (dot == NULL)
369         {
370                 // nothing to remove
371                 strcpy(out, in);
372                 return;
373         }
374         else
375         {
376                 memcpy(out, in, dot - in);
377                 out[dot - in] = 0;
378         }
379 }
380
381 // opens a stream
382 void *dpvsimpledecode_open(char *filename, char **errorstring)
383 {
384         dpvsimpledecodestream_t *s;
385         char t[8], *wavename;
386         if (errorstring != NULL)
387                 *errorstring = NULL;
388         s = malloc(sizeof(dpvsimpledecodestream_t));
389         if (s != NULL)
390         {
391                 s->bitstream = hz_bitstream_read_open(filename);
392                 if (s->bitstream != NULL)
393                 {
394                         // check file identification
395                         s->framedatablocks = hz_bitstream_read_blocks_new();
396                         if (s->framedatablocks != NULL)
397                         {
398                                 hz_bitstream_read_blocks_read(s->framedatablocks, s->bitstream, 8);
399                                 hz_bitstream_read_bytes(s->framedatablocks, t, 8);
400                                 if (!memcmp(t, "DPVideo", 8))
401                                 {
402                                         // check version number
403                                         hz_bitstream_read_blocks_read(s->framedatablocks, s->bitstream, 2);
404                                         if (hz_bitstream_read_short(s->framedatablocks) == 1)
405                                         {
406                                                 hz_bitstream_read_blocks_read(s->framedatablocks, s->bitstream, 12);
407                                                 s->info_imagewidth = hz_bitstream_read_short(s->framedatablocks);
408                                                 s->info_imageheight = hz_bitstream_read_short(s->framedatablocks);
409                                                 s->info_framerate = (double) hz_bitstream_read_int(s->framedatablocks) * (1.0 / 65536.0);
410
411                                                 if (s->info_framerate > 0.0)
412                                                 {
413                                                         s->videopixels = malloc(s->info_imagewidth * s->info_imageheight * sizeof(*s->videopixels));
414                                                         if (s->videopixels != NULL)
415                                                         {
416                                                                 wavename = malloc(strlen(filename) + 10);
417                                                                 if (wavename)
418                                                                 {
419                                                                         StripExtension(filename, wavename);
420                                                                         strcat(wavename, ".wav");
421                                                                         s->wavefile = waveopen(wavename, NULL);
422                                                                         free(wavename);
423                                                                 }
424                                                                 // all is well...
425                                                                 s->videoframenum = -10000;
426                                                                 return s;
427                                                         }
428                                                         else if (errorstring != NULL)
429                                                                 *errorstring = "unable to allocate video image buffer";
430                                                 }
431                                                 else if (errorstring != NULL)
432                                                         *errorstring = "error in video info chunk";
433                                         }
434                                         else if (errorstring != NULL)
435                                                 *errorstring = "read error";
436                                 }
437                                 else if (errorstring != NULL)
438                                         *errorstring = "not a dpvideo file";
439                                 hz_bitstream_read_blocks_free(s->framedatablocks);
440                         }
441                         else if (errorstring != NULL)
442                                 *errorstring = "unable to allocate memory for reading buffer";
443                         hz_bitstream_read_close(s->bitstream);
444                 }
445                 else if (errorstring != NULL)
446                         *errorstring = "unable to open file";
447                 free(s);
448         }
449         else if (errorstring != NULL)
450                 *errorstring = "unable to allocate memory for stream info structure";
451         return NULL;
452 }
453
454 // closes a stream
455 void dpvsimpledecode_close(void *stream)
456 {
457         dpvsimpledecodestream_t *s = stream;
458         if (s == NULL)
459                 return;
460         if (s->videopixels)
461                 free(s->videopixels);
462         if (s->wavefile)
463                 waveclose(s->wavefile);
464         if (s->framedatablocks)
465                 hz_bitstream_read_blocks_free(s->framedatablocks);
466         if (s->bitstream)
467                 hz_bitstream_read_close(s->bitstream);
468         free(s);
469 }
470
471 // utilitarian functions
472
473 // returns the current error number for the stream, and resets the error
474 // number to DPVSIMPLEDECODEERROR_NONE
475 // if the supplied string pointer variable is not NULL, it will be set to the
476 // error message
477 int dpvsimpledecode_error(void *stream, char **errorstring)
478 {
479         dpvsimpledecodestream_t *s = stream;
480         int e;
481         e = s->error;
482         s->error = 0;
483         if (errorstring)
484         {
485                 switch (e)
486                 {
487                         case DPVSIMPLEDECODEERROR_NONE:
488                                 *errorstring = "no error";
489                                 break;
490                         case DPVSIMPLEDECODEERROR_EOF:
491                                 *errorstring = "end of file reached (this is not an error)";
492                                 break;
493                         case DPVSIMPLEDECODEERROR_READERROR:
494                                 *errorstring = "read error (corrupt or incomplete file)";
495                                 break;
496                         case DPVSIMPLEDECODEERROR_SOUNDBUFFERTOOSMALL:
497                                 *errorstring = "sound buffer is too small for decoding frame (please allocate it as large as dpvsimpledecode_getneededsoundbufferlength suggests)";
498                                 break;
499                         case DPVSIMPLEDECODEERROR_INVALIDRMASK:
500                                 *errorstring = "invalid red bits mask";
501                                 break;
502                         case DPVSIMPLEDECODEERROR_INVALIDGMASK:
503                                 *errorstring = "invalid green bits mask";
504                                 break;
505                         case DPVSIMPLEDECODEERROR_INVALIDBMASK:
506                                 *errorstring = "invalid blue bits mask";
507                                 break;
508                         case DPVSIMPLEDECODEERROR_COLORMASKSOVERLAP:
509                                 *errorstring = "color bit masks overlap";
510                                 break;
511                         case DPVSIMPLEDECODEERROR_COLORMASKSEXCEEDBPP:
512                                 *errorstring = "color masks too big for specified bytes per pixel";
513                                 break;
514                         case DPVSIMPLEDECODEERROR_UNSUPPORTEDBPP:
515                                 *errorstring = "unsupported bytes per pixel (must be 2 for 16bit, or 4 for 32bit)";
516                                 break;
517                         default:
518                                 *errorstring = "unknown error";
519                                 break;
520                 }
521         }
522         return e;
523 }
524
525 // returns the width of the image data
526 unsigned int dpvsimpledecode_getwidth(void *stream)
527 {
528         dpvsimpledecodestream_t *s = stream;
529         return s->info_imagewidth;
530 }
531
532 // returns the height of the image data
533 unsigned int dpvsimpledecode_getheight(void *stream)
534 {
535         dpvsimpledecodestream_t *s = stream;
536         return s->info_imageheight;
537 }
538
539 // returns the sound sample rate of the stream
540 unsigned int dpvsimpledecode_getsoundrate(void *stream)
541 {
542         dpvsimpledecodestream_t *s = stream;
543         if (s->wavefile)
544                 return s->wavefile->info_rate;
545         else
546                 return 0;
547 }
548
549 // returns the framerate of the stream
550 double dpvsimpledecode_getframerate(void *stream)
551 {
552         dpvsimpledecodestream_t *s = stream;
553         return s->info_framerate;
554 }
555
556
557
558
559
560 static int dpvsimpledecode_convertpixels(dpvsimpledecodestream_t *s, void *imagedata, int imagebytesperrow)
561 {
562         unsigned int a, x, y, width, height;
563         unsigned int Rloss, Rmask, Rshift, Gloss, Gmask, Gshift, Bloss, Bmask, Bshift;
564         unsigned int *in;
565
566         width = s->info_imagewidth;
567         height = s->info_imageheight;
568
569         Rloss = s->info_imageRloss;
570         Rmask = s->info_imageRmask;
571         Rshift = s->info_imageRshift;
572         Gloss = s->info_imageGloss;
573         Gmask = s->info_imageGmask;
574         Gshift = s->info_imageGshift;
575         Bloss = s->info_imageBloss;
576         Bmask = s->info_imageBmask;
577         Bshift = s->info_imageBshift;
578
579         in = s->videopixels;
580         if (s->info_imagebpp == 4)
581         {
582                 unsigned int *outrow;
583                 for (y = 0;y < height;y++)
584                 {
585                         outrow = (void *)((unsigned char *)imagedata + y * imagebytesperrow);
586                         for (x = 0;x < width;x++)
587                         {
588                                 a = *in++;
589                                 outrow[x] = (((a >> Rloss) & Rmask) << Rshift) | (((a >> Gloss) & Gmask) << Gshift) | (((a >> Bloss) & Bmask) << Bshift);
590                         }
591                 }
592         }
593         else
594         {
595                 unsigned short *outrow;
596                 for (y = 0;y < height;y++)
597                 {
598                         outrow = (void *)((unsigned char *)imagedata + y * imagebytesperrow);
599                         if (Rloss == 19 && Gloss == 10 && Bloss == 3 && Rshift == 11 && Gshift == 5 && Bshift == 0)
600                         {
601                                 // optimized
602                                 for (x = 0;x < width;x++)
603                                 {
604                                         a = *in++;
605                                         outrow[x] = ((a >> 8) & 0xF800) | ((a >> 5) & 0x07E0) | ((a >> 3) & 0x001F);
606                                 }
607                         }
608                         else
609                         {
610                                 for (x = 0;x < width;x++)
611                                 {
612                                         a = *in++;
613                                         outrow[x] = (((a >> Rloss) & Rmask) << Rshift) | (((a >> Gloss) & Gmask) << Gshift) | (((a >> Bloss) & Bmask) << Bshift);
614                                 }
615                         }
616                 }
617         }
618         return s->error;
619 }
620
621 static int dpvsimpledecode_decompressimage(dpvsimpledecodestream_t *s)
622 {
623         int i, a, b, colors, g, x1, y1, bw, bh, width, height, palettebits;
624         unsigned int palette[256], *outrow, *out;
625         g = BLOCKSIZE;
626         width = s->info_imagewidth;
627         height = s->info_imageheight;
628         for (y1 = 0;y1 < height;y1 += g)
629         {
630                 outrow = s->videopixels + y1 * width;
631                 bh = g;
632                 if (y1 + bh > height)
633                         bh = height - y1;
634                 for (x1 = 0;x1 < width;x1 += g)
635                 {
636                         out = outrow + x1;
637                         bw = g;
638                         if (x1 + bw > width)
639                                 bw = width - x1;
640                         if (hz_bitstream_read_bit(s->framedatablocks))
641                         {
642                                 // updated block
643                                 palettebits = hz_bitstream_read_bits(s->framedatablocks, 3);
644                                 colors = 1 << palettebits;
645                                 for (i = 0;i < colors;i++)
646                                         palette[i] = hz_bitstream_read_bits(s->framedatablocks, 24);
647                                 if (palettebits)
648                                 {
649                                         for (b = 0;b < bh;b++, out += width)
650                                                 for (a = 0;a < bw;a++)
651                                                         out[a] = palette[hz_bitstream_read_bits(s->framedatablocks, palettebits)];
652                                 }
653                                 else
654                                 {
655                                         for (b = 0;b < bh;b++, out += width)
656                                                 for (a = 0;a < bw;a++)
657                                                         out[a] = palette[0];
658                                 }
659                         }
660                 }
661         }
662         return s->error;
663 }
664
665 // decodes a video frame to the supplied output pixels
666 int dpvsimpledecode_video(void *stream, void *imagedata, unsigned int Rmask, unsigned int Gmask, unsigned int Bmask, unsigned int bytesperpixel, int imagebytesperrow)
667 {
668         dpvsimpledecodestream_t *s = stream;
669         unsigned int framedatasize;
670         char t[4];
671         s->error = DPVSIMPLEDECODEERROR_NONE;
672         if (dpvsimpledecode_setpixelformat(s, Rmask, Gmask, Bmask, bytesperpixel))
673                 return s->error;
674
675         hz_bitstream_read_blocks_read(s->framedatablocks, s->bitstream, 8);
676         hz_bitstream_read_bytes(s->framedatablocks, t, 4);
677         if (memcmp(t, "VID0", 4))
678         {
679                 if (t[0] == 0)
680                         return (s->error = DPVSIMPLEDECODEERROR_EOF);
681                 else
682                         return (s->error = DPVSIMPLEDECODEERROR_READERROR);
683         }
684         framedatasize = hz_bitstream_read_int(s->framedatablocks);
685         hz_bitstream_read_blocks_read(s->framedatablocks, s->bitstream, framedatasize);
686         if (dpvsimpledecode_decompressimage(s))
687                 return s->error;
688
689         dpvsimpledecode_convertpixels(s, imagedata, imagebytesperrow);
690         return s->error;
691 }
692
693 // (note: sound is 16bit stereo native-endian, left channel first)
694 int dpvsimpledecode_audio(void *stream, short *soundbuffer, int requestedlength)
695 {
696         int samples;
697         dpvsimpledecodestream_t *s = stream;
698         s->error = DPVSIMPLEDECODEERROR_NONE;
699         if (requestedlength)
700         {
701                 samples = 0;
702                 if (s->wavefile && requestedlength)
703                         samples = waveread16stereo(s->wavefile, soundbuffer, requestedlength);
704                 if (samples < requestedlength)
705                         memset(soundbuffer + samples * 2, 0, (requestedlength - samples) * sizeof(short[2]));
706         }
707         return s->error;
708 }