]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - cl_video_jamdecode.c
Fix engine not starting on Windows if linked against SDL > 2.0.5
[xonotic/darkplaces.git] / cl_video_jamdecode.c
index eb6f922ec64c87e1c92a4ad65dce78122871d549..04bd63489dfe7253df7ebc91b387e1474809aee8 100644 (file)
-// JAM format decoder, used by Blood Omnicide\r
-\r
-typedef struct jamdecodestream_s\r
-{\r
-       int error;\r
-\r
-       qfile_t *file;\r
-       double info_framerate;\r
-       unsigned int info_frames;\r
-       unsigned int info_imagewidth;\r
-       unsigned int info_imageheight;\r
-       int doubleres;\r
-       float colorscale;\r
-       unsigned char colorsub;\r
-       float stipple;\r
-\r
-       // info used durign decoding\r
-       unsigned char *videopixels;\r
-       unsigned char *compressed;\r
-       unsigned char *framedata;\r
-       unsigned char *prevframedata;\r
-       unsigned char colormap[768];\r
-       unsigned int framesize;\r
-       unsigned int framenum;\r
-\r
-       // channel the sound file is being played on\r
-       int sndchan;\r
-}\r
-jamdecodestream_t;\r
-\r
-#define JAMDECODEERROR_NONE                0\r
-#define JAMDECODEERROR_EOF                 1\r
-#define JAMDECODEERROR_READERROR           2\r
-#define JAMDECODEERROR_BAD_FRAME_HEADER    3\r
-#define JAMDECODEERROR_BAD_OUTPUT_SIZE     4\r
-#define JAMDECODEERROR_BAD_COLORMAP        5\r
-\r
-// opens a stream\r
-void jam_close(void *stream);\r
-unsigned int jam_getwidth(void *stream);\r
-unsigned int jam_getheight(void *stream);\r
-double jam_getframerate(void *stream);\r
-int jam_video(void *stream, void *imagedata, unsigned int Rmask, unsigned int Gmask, unsigned int Bmask, unsigned int bytesperpixel, int imagebytesperrow);\r
-void *jam_open(clvideo_t *video, char *filename, const char **errorstring)\r
-{\r
-       unsigned char jamHead[16];\r
-       char *wavename;\r
-       jamdecodestream_t *s;\r
-       qfile_t *file;\r
-\r
-       s = (jamdecodestream_t *)Z_Malloc(sizeof(jamdecodestream_t));\r
-       if (s != NULL)\r
-       {\r
-               if ((file = FS_OpenVirtualFile(filename, false)))\r
-               {\r
-                       s->file = file;\r
-                       if (FS_Read(s->file, &jamHead, 16))\r
-                       {\r
-                               if (!memcmp(jamHead, "JAM", 3))\r
-                               {\r
-                                       s->info_imagewidth = LittleLong(*(jamHead + 4));\r
-                                       s->info_imageheight = LittleLong(*(jamHead + 8));\r
-                                       s->info_frames = LittleLong(*(jamHead + 12));\r
-                                       s->info_framerate = 15;\r
-                                       s->doubleres = 0;\r
-                                       s->colorscale = 0.70;\r
-                                       s->colorsub = 8;\r
-                                       s->stipple = 0.4;\r
-                                       s->framesize = s->info_imagewidth * s->info_imageheight;\r
-                                       if (s->framesize > 0)\r
-                                       {\r
-                                               s->compressed = (unsigned char *)Z_Malloc(s->framesize);\r
-                                               s->framedata = (unsigned char *)Z_Malloc(s->framesize * 2);\r
-                                               s->prevframedata = (unsigned char *)Z_Malloc(s->framesize * 2);\r
-                                               s->videopixels = (unsigned char *)Z_Malloc(s->framesize * 4); // bgra, doubleres\r
-                                               if (s->compressed != NULL && s->framedata != NULL && s->prevframedata != NULL && s->videopixels != NULL)\r
-                                               {\r
-                                                       size_t namelen;\r
-\r
-                                                       namelen = strlen(filename) + 10;\r
-                                                       wavename = (char *)Z_Malloc(namelen);\r
-                                                       if (wavename)\r
-                                                       {\r
-                                                               sfx_t* sfx;\r
-\r
-                                                               FS_StripExtension(filename, wavename, namelen);\r
-                                                               strlcat(wavename, ".wav", namelen);\r
-                                                               sfx = S_PrecacheSound(wavename, false, false);\r
-                                                               if (sfx != NULL)\r
-                                                                       s->sndchan = S_StartSound (-1, 0, sfx, vec3_origin, 1.0f, 0);\r
-                                                               else\r
-                                                                       s->sndchan = -1;\r
-                                                               Z_Free(wavename);\r
-                                                       }\r
-                                                       // all is well...\r
-                                                       // set the module functions\r
-                                                       s->framenum = 0;\r
-                                                       video->close = jam_close;\r
-                                                       video->getwidth = jam_getwidth;\r
-                                                       video->getheight = jam_getheight;\r
-                                                       video->getframerate = jam_getframerate;\r
-                                                       video->decodeframe = jam_video;\r
-                                                       return s;\r
-                                               }\r
-                                               else if (errorstring != NULL)\r
-                                                       *errorstring = "unable to allocate memory for stream info structure";\r
-                                               if (s->compressed != NULL)\r
-                                                       Z_Free(s->compressed);\r
-                                               if (s->framedata != NULL)\r
-                                                       Z_Free(s->framedata);\r
-                                               if (s->prevframedata != NULL)\r
-                                                       Z_Free(s->prevframedata);\r
-                                               if (s->videopixels != NULL)\r
-                                                       Z_Free(s->videopixels);\r
-                                       }\r
-                                       else if (errorstring != NULL)\r
-                                               *errorstring = "bad framesize";\r
-                               }\r
-                               else if (errorstring != NULL)\r
-                                       *errorstring = "not JAM videofile";\r
-                       }\r
-                       else if (errorstring != NULL)\r
-                               *errorstring = "unexpected EOF";\r
-                       FS_Close(file);\r
-               }\r
-               else if (errorstring != NULL)\r
-                       *errorstring = "unable to open videofile";\r
-               Z_Free(s);\r
-       }\r
-       else if (errorstring != NULL)\r
-               *errorstring = "unable to allocate memory for stream info structure";\r
-       return NULL;\r
-}\r
-\r
-// closes a stream\r
-void jam_close(void *stream)\r
-{\r
-       jamdecodestream_t *s = (jamdecodestream_t *)stream;\r
-       if (s == NULL)\r
-               return;\r
-       Z_Free(s->compressed);\r
-       Z_Free(s->framedata);\r
-       Z_Free(s->prevframedata);\r
-       Z_Free(s->videopixels);\r
-       if (s->sndchan != -1)\r
-               S_StopChannel(s->sndchan, true);\r
-       if (s->file)\r
-               FS_Close(s->file);\r
-       Z_Free(s);\r
-}\r
-\r
-// returns the width of the image data\r
-unsigned int jam_getwidth(void *stream)\r
-{\r
-       jamdecodestream_t *s = (jamdecodestream_t *)stream;\r
-       if (s->doubleres)\r
-               return s->info_imagewidth * 2;\r
-       return s->info_imagewidth;\r
-}\r
-\r
-// returns the height of the image data\r
-unsigned int jam_getheight(void *stream)\r
-{\r
-       jamdecodestream_t *s = (jamdecodestream_t *)stream;\r
-       if (s->doubleres)\r
-               return s->info_imageheight * 2;\r
-       return s->info_imageheight;\r
-}\r
-\r
-// returns the framerate of the stream\r
-double jam_getframerate(void *stream)\r
-{\r
-       jamdecodestream_t *s = (jamdecodestream_t *)stream;\r
-       return s->info_framerate;\r
-}\r
-\r
-\r
-// decode JAM frame\r
-void jam_decodeframe(unsigned char *inbuf, unsigned char *outbuf, unsigned char *prevbuf, int outsize, int frametype)\r
-{\r
-       unsigned char *srcptr, *destptr, *prevptr;\r
-       int bytesleft;\r
-       unsigned int mark;\r
-       unsigned short int bits;\r
-       int rep;\r
-       int backoffs;\r
-       unsigned char *back;\r
-       int i;\r
-\r
-       srcptr = inbuf;\r
-       destptr = outbuf;\r
-       prevptr = prevbuf;\r
-       bytesleft = outsize;\r
-\r
-       if (frametype == 2)\r
-       {\r
-               memcpy(outbuf, inbuf, outsize);\r
-               return;\r
-       }\r
-       while(bytesleft > 0)\r
-       {\r
-               memcpy(&mark, srcptr, 4);\r
-               srcptr += 4;\r
-               for(i=0; i<32 && bytesleft > 0; i++,mark=mark>>1)\r
-               {\r
-                       if(mark & 1)\r
-                       {\r
-                               *destptr = *srcptr;\r
-                               destptr ++;\r
-                               prevptr ++;\r
-                               srcptr ++;\r
-                               bytesleft --;\r
-                       }\r
-                       else\r
-                       {\r
-                               bits = srcptr[0] + 256*srcptr[1];\r
-                               rep = (bits >> 11) + 3;\r
-                               if(frametype == 1)\r
-                               {\r
-                                       backoffs = 0x821 - (bits & 0x7ff);\r
-                                       back = destptr - backoffs;\r
-                               }\r
-                               else\r
-                               {\r
-                                       backoffs = 0x400 - (bits & 0x7ff);\r
-                                       back = prevptr - backoffs;\r
-                               }\r
-                               srcptr += 2;\r
-                               memcpy(destptr, back, rep);\r
-                               destptr += rep;\r
-                               prevptr += rep;\r
-                               bytesleft -= rep;\r
-                       }\r
-               }\r
-       }\r
-}\r
-\r
-// decodes a video frame to the supplied output pixels\r
-int jam_video(void *stream, void *imagedata, unsigned int Rmask, unsigned int Gmask, unsigned int Bmask, unsigned int bytesperpixel, int imagebytesperrow)\r
-{\r
-       unsigned char frameHead[16], *b;\r
-       unsigned int compsize, outsize, i, j;\r
-       jamdecodestream_t *s = (jamdecodestream_t *)stream;\r
-\r
-       s->error = DPVSIMPLEDECODEERROR_NONE;\r
-       if (s->framenum < s->info_frames)\r
-       {\r
-readframe:\r
-               if (FS_Read(s->file, &frameHead, 16))\r
-               {\r
-                       compsize = LittleLong(*(frameHead + 8)) - 16;\r
-                       outsize = LittleLong(*(frameHead + 12));\r
-                       if (compsize < 0 || compsize > s->framesize || outsize < 0 || outsize > s->framesize)\r
-                               s->error = JAMDECODEERROR_BAD_FRAME_HEADER;\r
-                       else if (FS_Read(s->file, s->compressed, compsize))\r
-                       {\r
-                               // palette goes interleaved with special flag\r
-                               if (frameHead[0] == 2)\r
-                               {\r
-                                       if (compsize == 768)\r
-                                       {\r
-                                               memcpy(s->colormap, s->compressed, 768);\r
-                                               for(i = 0; i < 768; i++)\r
-                                                       s->colormap[i] = (unsigned char)(bound(0, (s->colormap[i] * s->colorscale) - s->colorsub, 255));\r
-                                               goto readframe;\r
-                                       }\r
-                                       //else\r
-                                       //      s->error = JAMDECODEERROR_BAD_COLORMAP;\r
-                               }\r
-                               else\r
-                               {\r
-                                       // decode frame\r
-                                       // shift buffers to provide current and previous one, decode\r
-                                       b = s->prevframedata;\r
-                                       s->prevframedata = s->framedata;\r
-                                       s->framedata = b;\r
-                                       jam_decodeframe(s->compressed, s->framedata, s->prevframedata, outsize, frameHead[4]);\r
-                                       // make 32bit imagepixels from 8bit palettized frame\r
-                                       if (s->doubleres)\r
-                                               b = s->videopixels;\r
-                                       else\r
-                                               b = (unsigned char *)imagedata;\r
-                                       for(i = 0; i < s->framesize; i++)\r
-                                       {\r
-                                               // bgra\r
-                                               *b++ = s->colormap[s->framedata[i]*3 + 2];\r
-                                               *b++ = s->colormap[s->framedata[i]*3 + 1];\r
-                                               *b++ = s->colormap[s->framedata[i]*3];\r
-                                               *b++ = 255;\r
-                                       }\r
-                                       // nearest 2x\r
-                                       if (s->doubleres)\r
-                                       {\r
-                                               for (i = 0; i < s->info_imageheight; i++)\r
-                                               {\r
-                                                       b = (unsigned char *)imagedata + (s->info_imagewidth*2*4)*(i*2);\r
-                                                       for (j = 0; j < s->info_imagewidth; j++)\r
-                                                       {\r
-                                                               *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4];\r
-                                                               *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 1];\r
-                                                               *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 2];\r
-                                                               *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 3];\r
-                                                               //\r
-                                                               *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4];\r
-                                                               *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 1];\r
-                                                               *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 2];\r
-                                                               *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 3];\r
-                                                       }\r
-                                                       b = (unsigned char *)imagedata + (s->info_imagewidth*2*4)*(i*2 + 1);\r
-                                                       for (j = 0; j < s->info_imagewidth; j++)\r
-                                                       {\r
-                                                               *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4];\r
-                                                               *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 1];\r
-                                                               *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 2];\r
-                                                               *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 3];\r
-                                                               //\r
-                                                               *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4];\r
-                                                               *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 1];\r
-                                                               *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 2];\r
-                                                               *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 3];\r
-                                                       }\r
-                                               }\r
-                                               // do stippling\r
-                                               if (s->stipple)\r
-                                               {\r
-                                                       for (i = 0; i < s->info_imageheight; i++)\r
-                                                       {\r
-                                                               b = (unsigned char *)imagedata + (s->info_imagewidth * 4 * 2 * 2 * i);\r
-                                                               for (j = 0; j < s->info_imagewidth; j++)\r
-                                                               {\r
-                                                                       b[0] = b[0] * s->stipple;\r
-                                                                       b[1] = b[1] * s->stipple;\r
-                                                                       b[2] = b[2] * s->stipple;\r
-                                                                       b += 4;\r
-                                                                       b[0] = b[0] * s->stipple;\r
-                                                                       b[1] = b[1] * s->stipple;\r
-                                                                       b[2] = b[2] * s->stipple;\r
-                                                                       b += 4;\r
-                                                               }\r
-                                                       }\r
-                                               }\r
-                                       }\r
-\r
-                               }\r
-                       }\r
-                       else\r
-                               s->error = JAMDECODEERROR_READERROR;\r
-               }\r
-               else\r
-                       s->error = JAMDECODEERROR_READERROR;\r
-       }\r
-       else\r
-               s->error = DPVSIMPLEDECODEERROR_EOF;\r
-       return s->error;\r
-}
\ No newline at end of file
+// JAM format decoder, used by Blood Omnicide
+
+#ifdef LIBAVCODEC
+//#define JAM_USELIBAVCODECSCALE
+#endif
+
+typedef struct jamdecodestream_s
+{
+       qfile_t       *file;
+       double         info_framerate;
+       unsigned int   info_frames;
+       unsigned int   info_imagewidth;
+       unsigned int   info_imageheight;
+       double         info_aspectratio;
+       float          colorscale;
+       unsigned char  colorsub;
+
+       // info used during decoding
+       unsigned char *frame;
+       unsigned char *frame_prev;
+       unsigned char  frame_palette[768];
+       unsigned char *frame_compressed;
+       unsigned int   framesize;
+       unsigned int   framenum;
+
+       // libavcodec scaling
+#ifdef JAM_USELIBAVCODECSCALE
+       unsigned char *frame_output_buffer;
+       AVFrame       *frame_output;
+       AVFrame       *frame_output_scale;
+       unsigned int   framewidth;
+       unsigned int   frameheight;
+#endif
+
+       // channel the sound file is being played on
+       int sndchan;
+}
+jamdecodestream_t;
+
+// opens a stream
+void jam_close(void *stream);
+unsigned int jam_getwidth(void *stream);
+unsigned int jam_getheight(void *stream);
+double jam_getframerate(void *stream);
+double jam_getaspectratio(void *stream);
+int jam_video(void *stream, void *imagedata, unsigned int Rmask, unsigned int Gmask, unsigned int Bmask, unsigned int bytesperpixel, int imagebytesperrow);
+void *jam_open(clvideo_t *video, char *filename, const char **errorstring)
+{
+       char jamHead[16];
+       jamdecodestream_t *s;
+       char *wavename;
+
+       // allocate stream structure
+       s = (jamdecodestream_t *)Z_Malloc(sizeof(jamdecodestream_t));
+       memset(s, 0, sizeof(jamdecodestream_t));
+       if (s == NULL)
+       {
+               *errorstring = "unable to allocate memory for stream info structure";
+               return NULL;
+       }
+       s->sndchan = -1;
+
+       // open file
+       s->file = FS_OpenVirtualFile(filename, true);
+       if (!s->file)
+       {
+               *errorstring = "unable to open videofile";
+               jam_close(s);
+               return NULL;
+       }
+
+       // read header
+       if (!FS_Read(s->file, jamHead, 16))
+       {
+               *errorstring = "JamDecoder: unexpected EOF reading header";
+               jam_close(s);
+               return NULL;
+       }
+       if (memcmp(jamHead, "JAM", 4))
+       {
+               *errorstring = "JamDecoder: not a JAM file";
+               jam_close(s);
+               return NULL;
+       }
+
+       s->info_imagewidth = LittleLong(*(jamHead + 4));
+       s->info_imageheight = LittleLong(*(jamHead + 8));
+       s->info_frames = LittleLong(*(jamHead + 12)) - 1;
+       s->info_framerate = 15;
+       s->info_aspectratio = (double)s->info_imagewidth / (double)s->info_imageheight;
+       s->colorscale = 0.90;
+       s->colorsub = 4;
+       s->framesize = s->info_imagewidth * s->info_imageheight;
+
+       // allocate frame input/output
+       if (s->framesize < 0)
+       {
+               *errorstring = "JamDecoder: bad framesize";
+               jam_close(s);
+               return NULL;
+       }
+       s->frame = (unsigned char *)Z_Malloc(s->framesize * 2);
+       s->frame_prev = (unsigned char *)Z_Malloc(s->framesize * 2);
+       s->frame_compressed = (unsigned char *)Z_Malloc(s->framesize);
+       if (s->frame_compressed == NULL || s->frame == NULL || s->frame_prev == NULL)
+       {
+               *errorstring = "JamDecoder: unable to allocate memory for video decoder";
+               jam_close(s);
+               return NULL;
+       }
+
+       // scale support provided by libavcodec
+#ifdef JAM_USELIBAVCODECSCALE
+       s->framewidth = s->info_imagewidth;
+       s->frameheight = s->info_imageheight;
+
+       // min size
+       if (cl_video_libavcodec_minwidth.integer > 0)
+               s->info_imagewidth = max(s->info_imagewidth, (unsigned int)cl_video_libavcodec_minwidth.integer);
+       if (cl_video_libavcodec_minheight.integer > 0)
+               s->info_imageheight = max(s->info_imageheight, (unsigned int)cl_video_libavcodec_minheight.integer);
+
+       // allocate output
+       s->frame_output_buffer = (unsigned char *)Z_Malloc(s->framesize * 4);
+       s->frame_output = AvCodec_AllocFrame();
+       s->frame_output_scale = AvCodec_AllocFrame();
+       if (!s->frame_output_buffer || !s->frame_output || !s->frame_output_scale)
+    {
+               *errorstring = "JamDecoder: failed to allocate LibAvcodec frame";
+               jam_close(s);
+               Z_Free(s);
+        return NULL;
+       }
+#endif
+
+       // everything is ok
+       // set the module functions
+       s->framenum = 0;
+       video->close = jam_close;
+       video->getwidth = jam_getwidth;
+       video->getheight = jam_getheight;
+       video->getframerate = jam_getframerate;
+       video->decodeframe = jam_video;
+       video->getaspectratio = jam_getaspectratio;
+
+       // set sound
+       size_t namelen;
+       namelen = strlen(filename) + 10;
+       wavename = (char *)Z_Malloc(namelen);
+       if (wavename)
+       {
+               sfx_t* sfx;
+               FS_StripExtension(filename, wavename, namelen);
+               strlcat(wavename, ".wav", namelen);
+               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);
+       }
+
+       return s;
+}
+
+// closes a stream
+void jam_close(void *stream)
+{
+       jamdecodestream_t *s = (jamdecodestream_t *)stream;
+       if (s == NULL)
+               return;
+       if (s->frame_compressed)
+               Z_Free(s->frame_compressed);
+       s->frame_compressed = NULL;
+       if (s->frame)
+               Z_Free(s->frame);
+       s->frame = NULL;
+       if (s->frame_prev)
+               Z_Free(s->frame_prev);
+       s->frame_prev = NULL;
+       if (s->sndchan != -1)
+               S_StopChannel(s->sndchan, true, true);
+       s->sndchan = -1;
+       if (s->file)
+               FS_Close(s->file);
+       s->file = NULL;
+#ifdef JAM_USELIBAVCODECSCALE
+       if (s->frame_output_buffer)
+               Z_Free(s->frame_output_buffer);
+       s->frame_output_buffer = NULL;
+       if (s->frame_output)
+               AvUtil_Free(s->frame_output);
+       s->frame_output = NULL;
+       if (s->frame_output_scale)
+               AvUtil_Free(s->frame_output_scale);
+       s->frame_output_scale = NULL;
+#endif
+       Z_Free(s);
+}
+
+// returns the width of the image data
+unsigned int jam_getwidth(void *stream)
+{
+       jamdecodestream_t *s = (jamdecodestream_t *)stream;
+       return s->info_imagewidth;
+}
+
+// returns the height of the image data
+unsigned int jam_getheight(void *stream)
+{
+       jamdecodestream_t *s = (jamdecodestream_t *)stream;
+       return s->info_imageheight;
+}
+
+// returns the framerate of the stream
+double jam_getframerate(void *stream)
+{
+       jamdecodestream_t *s = (jamdecodestream_t *)stream;
+       return s->info_framerate;
+}
+
+// returns aspect ration of the stream
+double jam_getaspectratio(void *stream)
+{
+       jamdecodestream_t *s = (jamdecodestream_t *)stream;
+       return s->info_aspectratio;
+}
+
+// decode JAM frame
+static void jam_decodeframe(unsigned char *inbuf, unsigned char *outbuf, unsigned char *prevbuf, int outsize, int frametype)
+{
+       unsigned char *srcptr, *destptr, *prevptr;
+       int bytesleft;
+       unsigned int mark;
+       unsigned short int bits;
+       int rep;
+       int backoffs;
+       unsigned char *back;
+       int i;
+
+       srcptr = inbuf;
+       destptr = outbuf;
+       prevptr = prevbuf;
+       bytesleft = outsize;
+
+       if (frametype == 2)
+       {
+               memcpy(outbuf, inbuf, outsize);
+               return;
+       }
+       while(bytesleft > 0)
+       {
+               memcpy(&mark, srcptr, 4);
+               srcptr += 4;
+               for(i=0; i<32 && bytesleft > 0; i++,mark=mark>>1)
+               {
+                       if(mark & 1)
+                       {
+                               *destptr = *srcptr;
+                               destptr ++;
+                               prevptr ++;
+                               srcptr ++;
+                               bytesleft --;
+                       }
+                       else
+                       {
+                               bits = srcptr[0] + 256*srcptr[1];
+                               rep = (bits >> 11) + 3;
+                               if(frametype == 1)
+                               {
+                                       backoffs = 0x821 - (bits & 0x7ff);
+                                       back = destptr - backoffs;
+                               }
+                               else
+                               {
+                                       backoffs = 0x400 - (bits & 0x7ff);
+                                       back = prevptr - backoffs;
+                               }
+                               srcptr += 2;
+                               memcpy(destptr, back, rep);
+                               destptr += rep;
+                               prevptr += rep;
+                               bytesleft -= rep;
+                       }
+               }
+       }
+}
+
+// decodes a video frame to the supplied output pixels
+int jam_video(void *stream, void *imagedata, unsigned int Rmask, unsigned int Gmask, unsigned int Bmask, unsigned int bytesperpixel, int imagebytesperrow)
+{
+       unsigned char frameHead[16], *b;
+       unsigned int compsize, outsize, i;
+       jamdecodestream_t *s = (jamdecodestream_t *)stream;
+
+       // EOF
+       if (s->framenum >= s->info_frames)
+               return 1;
+       s->framenum++;
+
+readframe:
+       // read frame header
+       if (!FS_Read(s->file, &frameHead, 16))
+       {
+               Con_Printf("JamDecoder: unexpected EOF on frame %i\n", s->framenum);
+               return 1;
+       }
+       compsize = LittleLong(*(frameHead + 8)) - 16;
+       outsize = LittleLong(*(frameHead + 12));
+       if (compsize > s->framesize || outsize > s->framesize)
+       {
+               Con_Printf("JamDecoder: got bogus header on frame %i\n", s->framenum);
+               return 1;
+       }
+
+       // read frame contents
+       if (!FS_Read(s->file, s->frame_compressed, compsize))
+       {
+               Con_Printf("JamDecoder: unexpected EOF on frame %i\n", s->framenum);
+               return 1;
+       }
+
+       // palette goes interleaved with special flag
+       if (frameHead[0] == 2)
+       {
+               if (compsize == 768)
+               {
+                       memcpy(s->frame_palette, s->frame_compressed, 768);
+                       for(i = 0; i < 768; i++)
+                               s->frame_palette[i] = (unsigned char)(bound(0, (s->frame_palette[i] * s->colorscale) - s->colorsub, 255));
+                       goto readframe;
+               }
+       }
+       else
+       {
+               // decode frame
+               // shift buffers to provide current and previous one, decode
+               b = s->frame_prev;
+               s->frame_prev = s->frame;
+               s->frame = b;
+               jam_decodeframe(s->frame_compressed, s->frame, s->frame_prev, outsize, frameHead[4]);
+#ifdef JAM_USELIBAVCODECSCALE
+               // make BGRA imagepixels from 8bit palettized frame
+               b = (unsigned char *)s->frame_output_buffer;
+               for(i = 0; i < s->framesize; i++)
+               {
+                       *b++ = s->frame_palette[s->frame[i]*3 + 2];
+                       *b++ = s->frame_palette[s->frame[i]*3 + 1];
+                       *b++ = s->frame_palette[s->frame[i]*3];
+                       *b++ = 255;
+               }
+               // scale
+               AvCodec_FillPicture((AVPicture *)s->frame_output, (uint8_t *)s->frame_output_buffer, PIX_FMT_BGRA, s->framewidth, s->frameheight);
+               AvCodec_FillPicture((AVPicture *)s->frame_output_scale, (uint8_t *)imagedata, PIX_FMT_BGRA, s->info_imagewidth, s->info_imageheight);
+               SwsContext *scale_context = SwScale_GetCachedContext(NULL, s->framewidth, s->frameheight, PIX_FMT_BGRA, s->info_imagewidth, s->info_imageheight, PIX_FMT_BGRA, libavcodec_scalers[max(0, min(LIBAVCODEC_SCALERS, cl_video_libavcodec_scaler.integer))], NULL, NULL, NULL); 
+               if (!scale_context)
+               {
+                       Con_Printf("JamDecoder: LibAvcodec: error creating scale context frame %i\n", s->framenum);
+                       return 1;
+               }
+               if (!SwScale_Scale(scale_context, s->frame_output->data, s->frame_output->linesize, 0, s->frameheight, s->frame_output_scale->data, s->frame_output_scale->linesize))
+                       Con_Printf("JamDecoder: LibAvcodec : error scaling frame\n", s->framenum);
+               SwScale_FreeContext(scale_context); 
+#else
+               // make BGRA imagepixels from 8bit palettized frame
+               b = (unsigned char *)imagedata;
+               for(i = 0; i < s->framesize; i++)
+               {
+                       // bgra
+                       *b++ = s->frame_palette[s->frame[i]*3 + 2];
+                       *b++ = s->frame_palette[s->frame[i]*3 + 1];
+                       *b++ = s->frame_palette[s->frame[i]*3];
+                       *b++ = 255;
+               }
+#endif
+       }
+       return 0;
+}