]> git.xonotic.org Git - xonotic/darkplaces.git/blob - cl_video_jamdecode.c
Added JAM video decoder plugin, used by Blood Omnicide
[xonotic/darkplaces.git] / cl_video_jamdecode.c
1 // JAM format decoder, used by Blood Omnicide\r
2 \r
3 typedef struct jamdecodestream_s\r
4 {\r
5         int error;\r
6 \r
7         qfile_t *file;\r
8         double info_framerate;\r
9         unsigned int info_frames;\r
10         unsigned int info_imagewidth;\r
11         unsigned int info_imageheight;\r
12         int doubleres;\r
13         float colorscale;\r
14         unsigned char colorsub;\r
15         float stipple;\r
16 \r
17         // info used durign decoding\r
18         unsigned char *videopixels;\r
19         unsigned char *compressed;\r
20         unsigned char *framedata;\r
21         unsigned char *prevframedata;\r
22         unsigned char colormap[768];\r
23         unsigned int framesize;\r
24         unsigned int framenum;\r
25 \r
26         // channel the sound file is being played on\r
27         int sndchan;\r
28 }\r
29 jamdecodestream_t;\r
30 \r
31 #define JAMDECODEERROR_NONE                0\r
32 #define JAMDECODEERROR_EOF                 1\r
33 #define JAMDECODEERROR_READERROR           2\r
34 #define JAMDECODEERROR_BAD_FRAME_HEADER    3\r
35 #define JAMDECODEERROR_BAD_OUTPUT_SIZE     4\r
36 #define JAMDECODEERROR_BAD_COLORMAP        5\r
37 \r
38 // opens a stream\r
39 void jam_close(void *stream);\r
40 unsigned int jam_getwidth(void *stream);\r
41 unsigned int jam_getheight(void *stream);\r
42 double jam_getframerate(void *stream);\r
43 int jam_video(void *stream, void *imagedata, unsigned int Rmask, unsigned int Gmask, unsigned int Bmask, unsigned int bytesperpixel, int imagebytesperrow);\r
44 void *jam_open(clvideo_t *video, char *filename, const char **errorstring)\r
45 {\r
46         unsigned char jamHead[16];\r
47         char *wavename;\r
48         jamdecodestream_t *s;\r
49         qfile_t *file;\r
50 \r
51         s = (jamdecodestream_t *)Z_Malloc(sizeof(jamdecodestream_t));\r
52         if (s != NULL)\r
53         {\r
54                 if ((file = FS_OpenVirtualFile(filename, false)))\r
55                 {\r
56                         s->file = file;\r
57                         if (FS_Read(s->file, &jamHead, 16))\r
58                         {\r
59                                 if (!memcmp(jamHead, "JAM", 3))\r
60                                 {\r
61                                         s->info_imagewidth = LittleLong(*(jamHead + 4));\r
62                                         s->info_imageheight = LittleLong(*(jamHead + 8));\r
63                                         s->info_frames = LittleLong(*(jamHead + 12));\r
64                                         s->info_framerate = 15;\r
65                                         s->doubleres = 0;\r
66                                         s->colorscale = 0.70;\r
67                                         s->colorsub = 8;\r
68                                         s->stipple = 0.4;\r
69                                         s->framesize = s->info_imagewidth * s->info_imageheight;\r
70                                         if (s->framesize > 0)\r
71                                         {\r
72                                                 s->compressed = (unsigned char *)Z_Malloc(s->framesize);\r
73                                                 s->framedata = (unsigned char *)Z_Malloc(s->framesize * 2);\r
74                                                 s->prevframedata = (unsigned char *)Z_Malloc(s->framesize * 2);\r
75                                                 s->videopixels = (unsigned char *)Z_Malloc(s->framesize * 4); // bgra, doubleres\r
76                                                 if (s->compressed != NULL && s->framedata != NULL && s->prevframedata != NULL && s->videopixels != NULL)\r
77                                                 {\r
78                                                         size_t namelen;\r
79 \r
80                                                         namelen = strlen(filename) + 10;\r
81                                                         wavename = (char *)Z_Malloc(namelen);\r
82                                                         if (wavename)\r
83                                                         {\r
84                                                                 sfx_t* sfx;\r
85 \r
86                                                                 FS_StripExtension(filename, wavename, namelen);\r
87                                                                 strlcat(wavename, ".wav", namelen);\r
88                                                                 sfx = S_PrecacheSound(wavename, false, false);\r
89                                                                 if (sfx != NULL)\r
90                                                                         s->sndchan = S_StartSound (-1, 0, sfx, vec3_origin, 1.0f, 0);\r
91                                                                 else\r
92                                                                         s->sndchan = -1;\r
93                                                                 Z_Free(wavename);\r
94                                                         }\r
95                                                         // all is well...\r
96                                                         // set the module functions\r
97                                                         s->framenum = 0;\r
98                                                         video->close = jam_close;\r
99                                                         video->getwidth = jam_getwidth;\r
100                                                         video->getheight = jam_getheight;\r
101                                                         video->getframerate = jam_getframerate;\r
102                                                         video->decodeframe = jam_video;\r
103                                                         return s;\r
104                                                 }\r
105                                                 else if (errorstring != NULL)\r
106                                                         *errorstring = "unable to allocate memory for stream info structure";\r
107                                                 if (s->compressed != NULL)\r
108                                                         Z_Free(s->compressed);\r
109                                                 if (s->framedata != NULL)\r
110                                                         Z_Free(s->framedata);\r
111                                                 if (s->prevframedata != NULL)\r
112                                                         Z_Free(s->prevframedata);\r
113                                                 if (s->videopixels != NULL)\r
114                                                         Z_Free(s->videopixels);\r
115                                         }\r
116                                         else if (errorstring != NULL)\r
117                                                 *errorstring = "bad framesize";\r
118                                 }\r
119                                 else if (errorstring != NULL)\r
120                                         *errorstring = "not JAM videofile";\r
121                         }\r
122                         else if (errorstring != NULL)\r
123                                 *errorstring = "unexpected EOF";\r
124                         FS_Close(file);\r
125                 }\r
126                 else if (errorstring != NULL)\r
127                         *errorstring = "unable to open videofile";\r
128                 Z_Free(s);\r
129         }\r
130         else if (errorstring != NULL)\r
131                 *errorstring = "unable to allocate memory for stream info structure";\r
132         return NULL;\r
133 }\r
134 \r
135 // closes a stream\r
136 void jam_close(void *stream)\r
137 {\r
138         jamdecodestream_t *s = (jamdecodestream_t *)stream;\r
139         if (s == NULL)\r
140                 return;\r
141         Z_Free(s->compressed);\r
142         Z_Free(s->framedata);\r
143         Z_Free(s->prevframedata);\r
144         Z_Free(s->videopixels);\r
145         if (s->sndchan != -1)\r
146                 S_StopChannel(s->sndchan, true);\r
147         if (s->file)\r
148                 FS_Close(s->file);\r
149         Z_Free(s);\r
150 }\r
151 \r
152 // returns the width of the image data\r
153 unsigned int jam_getwidth(void *stream)\r
154 {\r
155         jamdecodestream_t *s = (jamdecodestream_t *)stream;\r
156         if (s->doubleres)\r
157                 return s->info_imagewidth * 2;\r
158         return s->info_imagewidth;\r
159 }\r
160 \r
161 // returns the height of the image data\r
162 unsigned int jam_getheight(void *stream)\r
163 {\r
164         jamdecodestream_t *s = (jamdecodestream_t *)stream;\r
165         if (s->doubleres)\r
166                 return s->info_imageheight * 2;\r
167         return s->info_imageheight;\r
168 }\r
169 \r
170 // returns the framerate of the stream\r
171 double jam_getframerate(void *stream)\r
172 {\r
173         jamdecodestream_t *s = (jamdecodestream_t *)stream;\r
174         return s->info_framerate;\r
175 }\r
176 \r
177 \r
178 // decode JAM frame\r
179 void jam_decodeframe(unsigned char *inbuf, unsigned char *outbuf, unsigned char *prevbuf, int outsize, int frametype)\r
180 {\r
181         unsigned char *srcptr, *destptr, *prevptr;\r
182         int bytesleft;\r
183         unsigned int mark;\r
184         unsigned short int bits;\r
185         int rep;\r
186         int backoffs;\r
187         unsigned char *back;\r
188         int i;\r
189 \r
190         srcptr = inbuf;\r
191         destptr = outbuf;\r
192         prevptr = prevbuf;\r
193         bytesleft = outsize;\r
194 \r
195         if (frametype == 2)\r
196         {\r
197                 memcpy(outbuf, inbuf, outsize);\r
198                 return;\r
199         }\r
200         while(bytesleft > 0)\r
201         {\r
202                 memcpy(&mark, srcptr, 4);\r
203                 srcptr += 4;\r
204                 for(i=0; i<32 && bytesleft > 0; i++,mark=mark>>1)\r
205                 {\r
206                         if(mark & 1)\r
207                         {\r
208                                 *destptr = *srcptr;\r
209                                 destptr ++;\r
210                                 prevptr ++;\r
211                                 srcptr ++;\r
212                                 bytesleft --;\r
213                         }\r
214                         else\r
215                         {\r
216                                 bits = srcptr[0] + 256*srcptr[1];\r
217                                 rep = (bits >> 11) + 3;\r
218                                 if(frametype == 1)\r
219                                 {\r
220                                         backoffs = 0x821 - (bits & 0x7ff);\r
221                                         back = destptr - backoffs;\r
222                                 }\r
223                                 else\r
224                                 {\r
225                                         backoffs = 0x400 - (bits & 0x7ff);\r
226                                         back = prevptr - backoffs;\r
227                                 }\r
228                                 srcptr += 2;\r
229                                 memcpy(destptr, back, rep);\r
230                                 destptr += rep;\r
231                                 prevptr += rep;\r
232                                 bytesleft -= rep;\r
233                         }\r
234                 }\r
235         }\r
236 }\r
237 \r
238 // decodes a video frame to the supplied output pixels\r
239 int jam_video(void *stream, void *imagedata, unsigned int Rmask, unsigned int Gmask, unsigned int Bmask, unsigned int bytesperpixel, int imagebytesperrow)\r
240 {\r
241         unsigned char frameHead[16], *b;\r
242         unsigned int compsize, outsize, i, j;\r
243         jamdecodestream_t *s = (jamdecodestream_t *)stream;\r
244 \r
245         s->error = DPVSIMPLEDECODEERROR_NONE;\r
246         if (s->framenum < s->info_frames)\r
247         {\r
248 readframe:\r
249                 if (FS_Read(s->file, &frameHead, 16))\r
250                 {\r
251                         compsize = LittleLong(*(frameHead + 8)) - 16;\r
252                         outsize = LittleLong(*(frameHead + 12));\r
253                         if (compsize < 0 || compsize > s->framesize || outsize < 0 || outsize > s->framesize)\r
254                                 s->error = JAMDECODEERROR_BAD_FRAME_HEADER;\r
255                         else if (FS_Read(s->file, s->compressed, compsize))\r
256                         {\r
257                                 // palette goes interleaved with special flag\r
258                                 if (frameHead[0] == 2)\r
259                                 {\r
260                                         if (compsize == 768)\r
261                                         {\r
262                                                 memcpy(s->colormap, s->compressed, 768);\r
263                                                 for(i = 0; i < 768; i++)\r
264                                                         s->colormap[i] = (unsigned char)(bound(0, (s->colormap[i] * s->colorscale) - s->colorsub, 255));\r
265                                                 goto readframe;\r
266                                         }\r
267                                         //else\r
268                                         //      s->error = JAMDECODEERROR_BAD_COLORMAP;\r
269                                 }\r
270                                 else\r
271                                 {\r
272                                         // decode frame\r
273                                         // shift buffers to provide current and previous one, decode\r
274                                         b = s->prevframedata;\r
275                                         s->prevframedata = s->framedata;\r
276                                         s->framedata = b;\r
277                                         jam_decodeframe(s->compressed, s->framedata, s->prevframedata, outsize, frameHead[4]);\r
278                                         // make 32bit imagepixels from 8bit palettized frame\r
279                                         if (s->doubleres)\r
280                                                 b = s->videopixels;\r
281                                         else\r
282                                                 b = (unsigned char *)imagedata;\r
283                                         for(i = 0; i < s->framesize; i++)\r
284                                         {\r
285                                                 // bgra\r
286                                                 *b++ = s->colormap[s->framedata[i]*3 + 2];\r
287                                                 *b++ = s->colormap[s->framedata[i]*3 + 1];\r
288                                                 *b++ = s->colormap[s->framedata[i]*3];\r
289                                                 *b++ = 255;\r
290                                         }\r
291                                         // nearest 2x\r
292                                         if (s->doubleres)\r
293                                         {\r
294                                                 for (i = 0; i < s->info_imageheight; i++)\r
295                                                 {\r
296                                                         b = (unsigned char *)imagedata + (s->info_imagewidth*2*4)*(i*2);\r
297                                                         for (j = 0; j < s->info_imagewidth; j++)\r
298                                                         {\r
299                                                                 *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4];\r
300                                                                 *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 1];\r
301                                                                 *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 2];\r
302                                                                 *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 3];\r
303                                                                 //\r
304                                                                 *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4];\r
305                                                                 *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 1];\r
306                                                                 *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 2];\r
307                                                                 *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 3];\r
308                                                         }\r
309                                                         b = (unsigned char *)imagedata + (s->info_imagewidth*2*4)*(i*2 + 1);\r
310                                                         for (j = 0; j < s->info_imagewidth; j++)\r
311                                                         {\r
312                                                                 *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4];\r
313                                                                 *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 1];\r
314                                                                 *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 2];\r
315                                                                 *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 3];\r
316                                                                 //\r
317                                                                 *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4];\r
318                                                                 *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 1];\r
319                                                                 *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 2];\r
320                                                                 *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 3];\r
321                                                         }\r
322                                                 }\r
323                                                 // do stippling\r
324                                                 if (s->stipple)\r
325                                                 {\r
326                                                         for (i = 0; i < s->info_imageheight; i++)\r
327                                                         {\r
328                                                                 b = (unsigned char *)imagedata + (s->info_imagewidth * 4 * 2 * 2 * i);\r
329                                                                 for (j = 0; j < s->info_imagewidth; j++)\r
330                                                                 {\r
331                                                                         b[0] = b[0] * s->stipple;\r
332                                                                         b[1] = b[1] * s->stipple;\r
333                                                                         b[2] = b[2] * s->stipple;\r
334                                                                         b += 4;\r
335                                                                         b[0] = b[0] * s->stipple;\r
336                                                                         b[1] = b[1] * s->stipple;\r
337                                                                         b[2] = b[2] * s->stipple;\r
338                                                                         b += 4;\r
339                                                                 }\r
340                                                         }\r
341                                                 }\r
342                                         }\r
343 \r
344                                 }\r
345                         }\r
346                         else\r
347                                 s->error = JAMDECODEERROR_READERROR;\r
348                 }\r
349                 else\r
350                         s->error = JAMDECODEERROR_READERROR;\r
351         }\r
352         else\r
353                 s->error = DPVSIMPLEDECODEERROR_EOF;\r
354         return s->error;\r
355 }