#include "quakedef.h" #include "dpvsimpledecode.h" #define HZREADERROR_OK 0 #define HZREADERROR_EOF 1 #define HZREADERROR_MALLOCFAILED 2 //#define HZREADBLOCKSIZE 16000 #define HZREADBLOCKSIZE 1048576 typedef struct hz_bitstream_read_s { qfile_t *file; int endoffile; } hz_bitstream_read_t; typedef struct hz_bitstream_readblock_s { struct hz_bitstream_readblock_s *next; unsigned int size; unsigned char data[HZREADBLOCKSIZE]; } hz_bitstream_readblock_t; typedef struct hz_bitstream_readblocks_s { hz_bitstream_readblock_t *blocks; hz_bitstream_readblock_t *current; unsigned int position; unsigned int store; int count; } hz_bitstream_readblocks_t; hz_bitstream_read_t *hz_bitstream_read_open(char *filename) { qfile_t *file; hz_bitstream_read_t *stream; if ((file = FS_Open (filename, "rb", false, false))) { stream = (hz_bitstream_read_t *)Z_Malloc(sizeof(hz_bitstream_read_t)); memset(stream, 0, sizeof(*stream)); stream->file = file; return stream; } else return NULL; } void hz_bitstream_read_close(hz_bitstream_read_t *stream) { if (stream) { FS_Close(stream->file); Z_Free(stream); } } hz_bitstream_readblocks_t *hz_bitstream_read_blocks_new(void) { hz_bitstream_readblocks_t *blocks; blocks = (hz_bitstream_readblocks_t *)Z_Malloc(sizeof(hz_bitstream_readblocks_t)); if (blocks == NULL) return NULL; memset(blocks, 0, sizeof(hz_bitstream_readblocks_t)); return blocks; } void hz_bitstream_read_blocks_free(hz_bitstream_readblocks_t *blocks) { hz_bitstream_readblock_t *b, *n; if (blocks == NULL) return; for (b = blocks->blocks;b;b = n) { n = b->next; Z_Free(b); } Z_Free(blocks); } void hz_bitstream_read_flushbits(hz_bitstream_readblocks_t *blocks) { blocks->store = 0; blocks->count = 0; } int hz_bitstream_read_blocks_read(hz_bitstream_readblocks_t *blocks, hz_bitstream_read_t *stream, unsigned int size) { int s; hz_bitstream_readblock_t *b, *p; s = size; p = NULL; b = blocks->blocks; while (s > 0) { if (b == NULL) { b = (hz_bitstream_readblock_t *)Z_Malloc(sizeof(hz_bitstream_readblock_t)); if (b == NULL) return HZREADERROR_MALLOCFAILED; b->next = NULL; b->size = 0; if (p != NULL) p->next = b; else blocks->blocks = b; } if (s > HZREADBLOCKSIZE) b->size = HZREADBLOCKSIZE; else b->size = s; s -= b->size; if (FS_Read(stream->file, b->data, b->size) != (fs_offset_t)b->size) { stream->endoffile = 1; break; } p = b; b = b->next; } while (b) { b->size = 0; b = b->next; } blocks->current = blocks->blocks; blocks->position = 0; hz_bitstream_read_flushbits(blocks); if (stream->endoffile) return HZREADERROR_EOF; return HZREADERROR_OK; } unsigned int hz_bitstream_read_blocks_getbyte(hz_bitstream_readblocks_t *blocks) { while (blocks->current != NULL && blocks->position >= blocks->current->size) { blocks->position = 0; blocks->current = blocks->current->next; } if (blocks->current == NULL) return 0; return blocks->current->data[blocks->position++]; } int hz_bitstream_read_bit(hz_bitstream_readblocks_t *blocks) { if (!blocks->count) { blocks->count += 8; blocks->store <<= 8; blocks->store |= hz_bitstream_read_blocks_getbyte(blocks) & 0xFF; } blocks->count--; return (blocks->store >> blocks->count) & 1; } unsigned int hz_bitstream_read_bits(hz_bitstream_readblocks_t *blocks, int size) { unsigned int num = 0; // we can only handle about 24 bits at a time safely // (there might be up to 7 bits more than we need in the bit store) if (size > 24) { size -= 8; num |= hz_bitstream_read_bits(blocks, 8) << size; } while (blocks->count < size) { blocks->count += 8; blocks->store <<= 8; blocks->store |= hz_bitstream_read_blocks_getbyte(blocks) & 0xFF; } blocks->count -= size; num |= (blocks->store >> blocks->count) & ((1 << size) - 1); return num; } unsigned int hz_bitstream_read_byte(hz_bitstream_readblocks_t *blocks) { return hz_bitstream_read_blocks_getbyte(blocks); } unsigned int hz_bitstream_read_short(hz_bitstream_readblocks_t *blocks) { return (hz_bitstream_read_byte(blocks) << 8) | (hz_bitstream_read_byte(blocks)); } unsigned int hz_bitstream_read_int(hz_bitstream_readblocks_t *blocks) { return (hz_bitstream_read_byte(blocks) << 24) | (hz_bitstream_read_byte(blocks) << 16) | (hz_bitstream_read_byte(blocks) << 8) | (hz_bitstream_read_byte(blocks)); } void hz_bitstream_read_bytes(hz_bitstream_readblocks_t *blocks, void *outdata, unsigned int size) { unsigned char *out; out = (unsigned char *)outdata; while (size--) *out++ = hz_bitstream_read_byte(blocks); } #define BLOCKSIZE 8 typedef struct dpvsimpledecodestream_s { hz_bitstream_read_t *bitstream; hz_bitstream_readblocks_t *framedatablocks; int error; double info_framerate; unsigned int info_frames; unsigned int info_imagewidth; unsigned int info_imageheight; unsigned int info_imagebpp; unsigned int info_imageRloss; unsigned int info_imageRmask; unsigned int info_imageRshift; unsigned int info_imageGloss; unsigned int info_imageGmask; unsigned int info_imageGshift; unsigned int info_imageBloss; unsigned int info_imageBmask; unsigned int info_imageBshift; unsigned int info_imagesize; // current video frame (needed because of delta compression) int videoframenum; // current video frame data (needed because of delta compression) unsigned int *videopixels; // channel the sound file is being played on int sndchan; } dpvsimpledecodestream_t; static int dpvsimpledecode_setpixelformat(dpvsimpledecodestream_t *s, unsigned int Rmask, unsigned int Gmask, unsigned int Bmask, unsigned int bytesperpixel) { int Rshift, Rbits, Gshift, Gbits, Bshift, Bbits; if (!Rmask) { s->error = DPVSIMPLEDECODEERROR_INVALIDRMASK; return s->error; } if (!Gmask) { s->error = DPVSIMPLEDECODEERROR_INVALIDGMASK; return s->error; } if (!Bmask) { s->error = DPVSIMPLEDECODEERROR_INVALIDBMASK; return s->error; } if (Rmask & Gmask || Rmask & Bmask || Gmask & Bmask) { s->error = DPVSIMPLEDECODEERROR_COLORMASKSOVERLAP; return s->error; } switch (bytesperpixel) { case 2: if ((Rmask | Gmask | Bmask) > 65536) { s->error = DPVSIMPLEDECODEERROR_COLORMASKSEXCEEDBPP; return s->error; } break; case 4: break; default: s->error = DPVSIMPLEDECODEERROR_UNSUPPORTEDBPP; return s->error; break; } for (Rshift = 0;!(Rmask & 1);Rshift++, Rmask >>= 1); for (Gshift = 0;!(Gmask & 1);Gshift++, Gmask >>= 1); for (Bshift = 0;!(Bmask & 1);Bshift++, Bmask >>= 1); if (((Rmask + 1) & Rmask) != 0) { s->error = DPVSIMPLEDECODEERROR_INVALIDRMASK; return s->error; } if (((Gmask + 1) & Gmask) != 0) { s->error = DPVSIMPLEDECODEERROR_INVALIDGMASK; return s->error; } if (((Bmask + 1) & Bmask) != 0) { s->error = DPVSIMPLEDECODEERROR_INVALIDBMASK; return s->error; } for (Rbits = 0;Rmask & 1;Rbits++, Rmask >>= 1); for (Gbits = 0;Gmask & 1;Gbits++, Gmask >>= 1); for (Bbits = 0;Bmask & 1;Bbits++, Bmask >>= 1); if (Rbits > 8) { Rshift += (Rbits - 8); Rbits = 8; } if (Gbits > 8) { Gshift += (Gbits - 8); Gbits = 8; } if (Bbits > 8) { Bshift += (Bbits - 8); Bbits = 8; } s->info_imagebpp = bytesperpixel; s->info_imageRloss = 16 + (8 - Rbits); s->info_imageGloss = 8 + (8 - Gbits); s->info_imageBloss = 0 + (8 - Bbits); s->info_imageRmask = (1 << Rbits) - 1; s->info_imageGmask = (1 << Gbits) - 1; s->info_imageBmask = (1 << Bbits) - 1; s->info_imageRshift = Rshift; s->info_imageGshift = Gshift; s->info_imageBshift = Bshift; s->info_imagesize = s->info_imagewidth * s->info_imageheight * s->info_imagebpp; return s->error; } // opening and closing streams static void StripExtension(char *in, char *out) { char *dot, *c; dot = NULL; for (c = in;*c;c++) { if (*c == ':' || *c == '\\' || *c == '/') dot = NULL; if (*c == '.') dot = c; } if (dot == NULL) { // nothing to remove strcpy(out, in); return; } else { memcpy(out, in, dot - in); out[dot - in] = 0; } } // opens a stream void *dpvsimpledecode_open(char *filename, char **errorstring) { dpvsimpledecodestream_t *s; char t[8], *wavename; if (errorstring != NULL) *errorstring = NULL; s = (dpvsimpledecodestream_t *)Z_Malloc(sizeof(dpvsimpledecodestream_t)); if (s != NULL) { s->bitstream = hz_bitstream_read_open(filename); if (s->bitstream != NULL) { // check file identification s->framedatablocks = hz_bitstream_read_blocks_new(); if (s->framedatablocks != NULL) { hz_bitstream_read_blocks_read(s->framedatablocks, s->bitstream, 8); hz_bitstream_read_bytes(s->framedatablocks, t, 8); if (!memcmp(t, "DPVideo", 8)) { // check version number hz_bitstream_read_blocks_read(s->framedatablocks, s->bitstream, 2); if (hz_bitstream_read_short(s->framedatablocks) == 1) { hz_bitstream_read_blocks_read(s->framedatablocks, s->bitstream, 12); s->info_imagewidth = hz_bitstream_read_short(s->framedatablocks); s->info_imageheight = hz_bitstream_read_short(s->framedatablocks); s->info_framerate = (double) hz_bitstream_read_int(s->framedatablocks) * (1.0 / 65536.0); if (s->info_framerate > 0.0) { s->videopixels = (unsigned int *)Z_Malloc(s->info_imagewidth * s->info_imageheight * sizeof(*s->videopixels)); if (s->videopixels != NULL) { wavename = (char *)Z_Malloc(strlen(filename) + 10); if (wavename) { sfx_t* sfx; StripExtension(filename, wavename); strcat(wavename, ".wav"); sfx = S_PrecacheSound (wavename, false, false); if (sfx != NULL) s->sndchan = S_StartSound (-1, 0, sfx, vec3_origin, 1.0f, 0); else s->sndchan = -1; Z_Free(wavename); } // all is well... s->videoframenum = -10000; return s; } else if (errorstring != NULL) *errorstring = "unable to allocate video image buffer"; } else if (errorstring != NULL) *errorstring = "error in video info chunk"; } else if (errorstring != NULL) *errorstring = "read error"; } else if (errorstring != NULL) *errorstring = "not a dpvideo file"; hz_bitstream_read_blocks_free(s->framedatablocks); } else if (errorstring != NULL) *errorstring = "unable to allocate memory for reading buffer"; hz_bitstream_read_close(s->bitstream); } else if (errorstring != NULL) *errorstring = "unable to open file"; Z_Free(s); } else if (errorstring != NULL) *errorstring = "unable to allocate memory for stream info structure"; return NULL; } // closes a stream void dpvsimpledecode_close(void *stream) { dpvsimpledecodestream_t *s = (dpvsimpledecodestream_t *)stream; if (s == NULL) return; if (s->videopixels) Z_Free(s->videopixels); if (s->sndchan != -1) S_StopChannel (s->sndchan); if (s->framedatablocks) hz_bitstream_read_blocks_free(s->framedatablocks); if (s->bitstream) hz_bitstream_read_close(s->bitstream); Z_Free(s); } // utilitarian functions // returns the current error number for the stream, and resets the error // number to DPVSIMPLEDECODEERROR_NONE // if the supplied string pointer variable is not NULL, it will be set to the // error message int dpvsimpledecode_error(void *stream, char **errorstring) { dpvsimpledecodestream_t *s = (dpvsimpledecodestream_t *)stream; int e; e = s->error; s->error = 0; if (errorstring) { switch (e) { case DPVSIMPLEDECODEERROR_NONE: *errorstring = "no error"; break; case DPVSIMPLEDECODEERROR_EOF: *errorstring = "end of file reached (this is not an error)"; break; case DPVSIMPLEDECODEERROR_READERROR: *errorstring = "read error (corrupt or incomplete file)"; break; case DPVSIMPLEDECODEERROR_SOUNDBUFFERTOOSMALL: *errorstring = "sound buffer is too small for decoding frame (please allocate it as large as dpvsimpledecode_getneededsoundbufferlength suggests)"; break; case DPVSIMPLEDECODEERROR_INVALIDRMASK: *errorstring = "invalid red bits mask"; break; case DPVSIMPLEDECODEERROR_INVALIDGMASK: *errorstring = "invalid green bits mask"; break; case DPVSIMPLEDECODEERROR_INVALIDBMASK: *errorstring = "invalid blue bits mask"; break; case DPVSIMPLEDECODEERROR_COLORMASKSOVERLAP: *errorstring = "color bit masks overlap"; break; case DPVSIMPLEDECODEERROR_COLORMASKSEXCEEDBPP: *errorstring = "color masks too big for specified bytes per pixel"; break; case DPVSIMPLEDECODEERROR_UNSUPPORTEDBPP: *errorstring = "unsupported bytes per pixel (must be 2 for 16bit, or 4 for 32bit)"; break; default: *errorstring = "unknown error"; break; } } return e; } // returns the width of the image data unsigned int dpvsimpledecode_getwidth(void *stream) { dpvsimpledecodestream_t *s = (dpvsimpledecodestream_t *)stream; return s->info_imagewidth; } // returns the height of the image data unsigned int dpvsimpledecode_getheight(void *stream) { dpvsimpledecodestream_t *s = (dpvsimpledecodestream_t *)stream; return s->info_imageheight; } // returns the framerate of the stream double dpvsimpledecode_getframerate(void *stream) { dpvsimpledecodestream_t *s = (dpvsimpledecodestream_t *)stream; return s->info_framerate; } static int dpvsimpledecode_convertpixels(dpvsimpledecodestream_t *s, void *imagedata, int imagebytesperrow) { unsigned int a, x, y, width, height; unsigned int Rloss, Rmask, Rshift, Gloss, Gmask, Gshift, Bloss, Bmask, Bshift; unsigned int *in; width = s->info_imagewidth; height = s->info_imageheight; Rloss = s->info_imageRloss; Rmask = s->info_imageRmask; Rshift = s->info_imageRshift; Gloss = s->info_imageGloss; Gmask = s->info_imageGmask; Gshift = s->info_imageGshift; Bloss = s->info_imageBloss; Bmask = s->info_imageBmask; Bshift = s->info_imageBshift; in = s->videopixels; if (s->info_imagebpp == 4) { unsigned int *outrow; for (y = 0;y < height;y++) { outrow = (unsigned int *)((unsigned char *)imagedata + y * imagebytesperrow); for (x = 0;x < width;x++) { a = *in++; outrow[x] = (((a >> Rloss) & Rmask) << Rshift) | (((a >> Gloss) & Gmask) << Gshift) | (((a >> Bloss) & Bmask) << Bshift); } } } else { unsigned short *outrow; for (y = 0;y < height;y++) { outrow = (unsigned short *)((unsigned char *)imagedata + y * imagebytesperrow); if (Rloss == 19 && Gloss == 10 && Bloss == 3 && Rshift == 11 && Gshift == 5 && Bshift == 0) { // optimized for (x = 0;x < width;x++) { a = *in++; outrow[x] = ((a >> 8) & 0xF800) | ((a >> 5) & 0x07E0) | ((a >> 3) & 0x001F); } } else { for (x = 0;x < width;x++) { a = *in++; outrow[x] = (((a >> Rloss) & Rmask) << Rshift) | (((a >> Gloss) & Gmask) << Gshift) | (((a >> Bloss) & Bmask) << Bshift); } } } } return s->error; } static int dpvsimpledecode_decompressimage(dpvsimpledecodestream_t *s) { int i, a, b, colors, g, x1, y1, bw, bh, width, height, palettebits; unsigned int palette[256], *outrow, *out; g = BLOCKSIZE; width = s->info_imagewidth; height = s->info_imageheight; for (y1 = 0;y1 < height;y1 += g) { outrow = s->videopixels + y1 * width; bh = g; if (y1 + bh > height) bh = height - y1; for (x1 = 0;x1 < width;x1 += g) { out = outrow + x1; bw = g; if (x1 + bw > width) bw = width - x1; if (hz_bitstream_read_bit(s->framedatablocks)) { // updated block palettebits = hz_bitstream_read_bits(s->framedatablocks, 3); colors = 1 << palettebits; for (i = 0;i < colors;i++) palette[i] = hz_bitstream_read_bits(s->framedatablocks, 24); if (palettebits) { for (b = 0;b < bh;b++, out += width) for (a = 0;a < bw;a++) out[a] = palette[hz_bitstream_read_bits(s->framedatablocks, palettebits)]; } else { for (b = 0;b < bh;b++, out += width) for (a = 0;a < bw;a++) out[a] = palette[0]; } } } } return s->error; } // decodes a video frame to the supplied output pixels int dpvsimpledecode_video(void *stream, void *imagedata, unsigned int Rmask, unsigned int Gmask, unsigned int Bmask, unsigned int bytesperpixel, int imagebytesperrow) { dpvsimpledecodestream_t *s = (dpvsimpledecodestream_t *)stream; unsigned int framedatasize; char t[4]; s->error = DPVSIMPLEDECODEERROR_NONE; if (dpvsimpledecode_setpixelformat(s, Rmask, Gmask, Bmask, bytesperpixel)) return s->error; hz_bitstream_read_blocks_read(s->framedatablocks, s->bitstream, 8); hz_bitstream_read_bytes(s->framedatablocks, t, 4); if (memcmp(t, "VID0", 4)) { if (t[0] == 0) return (s->error = DPVSIMPLEDECODEERROR_EOF); else return (s->error = DPVSIMPLEDECODEERROR_READERROR); } framedatasize = hz_bitstream_read_int(s->framedatablocks); hz_bitstream_read_blocks_read(s->framedatablocks, s->bitstream, framedatasize); if (dpvsimpledecode_decompressimage(s)) return s->error; dpvsimpledecode_convertpixels(s, imagedata, imagebytesperrow); return s->error; }