summaryrefslogtreecommitdiff
path: root/src/libs/video/dukvid.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/video/dukvid.c')
-rw-r--r--src/libs/video/dukvid.c748
1 files changed, 748 insertions, 0 deletions
diff --git a/src/libs/video/dukvid.c b/src/libs/video/dukvid.c
new file mode 100644
index 0000000..d9cb8fa
--- /dev/null
+++ b/src/libs/video/dukvid.c
@@ -0,0 +1,748 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* DUCK video player
+ *
+ * Status: fully functional
+ */
+
+#include "video.h"
+#include "dukvid.h"
+#include <stdio.h>
+#include <string.h>
+#include "libs/uio.h"
+#include "libs/memlib.h"
+#include "endian_uqm.h"
+
+#define THIS_PTR TFB_VideoDecoder* This
+
+static const char* dukv_GetName (void);
+static bool dukv_InitModule (int flags);
+static void dukv_TermModule (void);
+static uint32 dukv_GetStructSize (void);
+static int dukv_GetError (THIS_PTR);
+static bool dukv_Init (THIS_PTR, TFB_PixelFormat* fmt);
+static void dukv_Term (THIS_PTR);
+static bool dukv_Open (THIS_PTR, uio_DirHandle *dir, const char *filename);
+static void dukv_Close (THIS_PTR);
+static int dukv_DecodeNext (THIS_PTR);
+static uint32 dukv_SeekFrame (THIS_PTR, uint32 frame);
+static float dukv_SeekTime (THIS_PTR, float time);
+static uint32 dukv_GetFrame (THIS_PTR);
+static float dukv_GetTime (THIS_PTR);
+
+TFB_VideoDecoderFuncs dukv_DecoderVtbl =
+{
+ dukv_GetName,
+ dukv_InitModule,
+ dukv_TermModule,
+ dukv_GetStructSize,
+ dukv_GetError,
+ dukv_Init,
+ dukv_Term,
+ dukv_Open,
+ dukv_Close,
+ dukv_DecodeNext,
+ dukv_SeekFrame,
+ dukv_SeekTime,
+ dukv_GetFrame,
+ dukv_GetTime,
+};
+
+typedef struct tfb_duckvideoheader
+{
+ uint32 version;
+ uint32 scrn_x_ofs; // horz screen offset in pixels
+ uint32 scrn_y_ofs; // vert screen offset in pixels
+ uint16 wb, hb; // width + height in blocks
+ sint16 lumas[8]; // future luminance deltas
+ sint16 chromas[8]; // future chrominance deltas
+
+} TFB_DuckVideoHeader;
+
+#define NUM_VEC_ITEMS 0x010
+#define NUM_VECTORS 0x100
+
+typedef struct tfb_duckvideodeltas
+{
+ sint32 lumas[NUM_VECTORS][NUM_VEC_ITEMS];
+ sint32 chromas[NUM_VECTORS][NUM_VEC_ITEMS];
+
+} TFB_DuckVideoDeltas;
+
+// specific video decoder struct derived from TFB_VideoDecoder
+// the only sane way in C one can :)
+typedef struct tfb_duckvideodecoder
+{
+ // always the first member
+ TFB_VideoDecoder decoder;
+
+ sint32 last_error;
+ uio_DirHandle* basedir;
+ char* basename;
+ uio_Stream *stream;
+
+// loaded from disk
+ uint32* frames;
+ uint32 cframes;
+ uint32 iframe;
+ uint32 version;
+ uint32 wb, hb; // width, height in blocks
+
+// generated
+ TFB_DuckVideoDeltas d;
+
+ uint8* inbuf;
+ uint32* decbuf;
+
+} TFB_DuckVideoDecoder;
+
+#define DUCK_GENERAL_FPS 14.622f
+#define DUCK_MAX_FRAME_SIZE 0x8000U
+#define DUCK_END_OF_SEQUENCE 1
+
+static void
+dukv_DecodeFrame (uint8* src_p, uint32* dst_p, uint32 wb, uint32 hb,
+ TFB_DuckVideoDeltas* deltas)
+{
+ int iVec;
+ int iSeq;
+ uint32 x, y;
+ sint32 w;
+ uint32 *d_p0, *d_p1;
+ int i;
+
+ w = wb * 4;
+
+ iVec = *(src_p++);
+ iSeq = 0;
+
+ for (y = 0; y < hb; ++y)
+ {
+ sint32 accum0, accum1, corr, corr0, corr1, delta;
+ sint32 pix[4];
+
+ d_p0 = dst_p + y * w * 2;
+ d_p1 = d_p0 + w;
+
+ accum0 = 0;
+ accum1 = 0;
+ corr0 = 0;
+ corr1 = 0;
+
+ for (x = 0; x < wb; ++x)
+ {
+ if (y == 0)
+ {
+ pix[0] = pix[1] = pix[2] = pix[3] = 0;
+ }
+ else
+ {
+ uint32* p_p = d_p0 - w;
+ pix[0] = p_p[0];
+ pix[1] = p_p[1];
+ pix[2] = p_p[2];
+ pix[3] = p_p[3];
+ }
+
+ // start with chroma delta
+ delta = deltas->chromas[iVec][iSeq++];
+ iSeq++; // correctors ignored
+
+ accum0 += delta >> 1;
+ if (delta & 1)
+ {
+ iVec = *(src_p++);
+ iSeq = 0;
+ }
+
+ // line 0
+ for (i = 0; i < 4; ++i, ++d_p0)
+ {
+ delta = deltas->lumas[iVec][iSeq++];
+ corr = deltas->lumas[iVec][iSeq++];
+
+ accum0 += delta >> 1;
+ corr0 ^= corr;
+ pix[i] += accum0;
+ pix[i] ^= corr0;
+
+ if (delta & 1)
+ {
+ iVec = *(src_p++);
+ iSeq = 0;
+ }
+
+ *d_p0 = pix[i];
+ }
+
+ // line 1
+ for (i = 0; i < 4; ++i, ++d_p1)
+ {
+ delta = deltas->lumas[iVec][iSeq++];
+ corr = deltas->lumas[iVec][iSeq++];
+
+ accum1 += delta >> 1;
+ corr1 ^= corr;
+ pix[i] += accum1;
+ pix[i] ^= corr1;
+
+ if (delta & 1)
+ {
+ iVec = *(src_p++);
+ iSeq = 0;
+ }
+
+ *d_p1 = pix[i];
+ }
+ }
+ }
+}
+
+static void
+dukv_DecodeFrameV3 (uint8* src_p, uint32* dst_p, uint32 wb, uint32 hb,
+ TFB_DuckVideoDeltas* deltas)
+{
+ int iVec;
+ int iSeq;
+ uint32 x, y;
+ sint32 w;
+ uint32* d_p;
+ int i;
+
+ iVec = *(src_p++);
+ iSeq = 0;
+
+ hb *= 2;
+ w = wb * 4;
+
+ for (y = 0; y < hb; ++y)
+ {
+ sint32 accum, delta, pix;
+
+ d_p = dst_p + y * w;
+
+ accum = 0;
+
+ for (x = 0; x < wb; ++x)
+ {
+ // start with chroma delta
+ delta = deltas->chromas[iVec][iSeq];
+ iSeq += 2; // correctors ignored
+
+ accum += delta >> 1;
+
+ if (delta & DUCK_END_OF_SEQUENCE)
+ {
+ iVec = *(src_p++);
+ iSeq = 0;
+ }
+
+ for (i = 0; i < 4; ++i, ++d_p)
+ {
+ if (y == 0)
+ pix = 0;
+ else
+ pix = d_p[-w];
+
+ // get next luma delta
+ delta = deltas->lumas[iVec][iSeq];
+ iSeq += 2; // correctors ignored
+
+ accum += delta >> 1;
+ pix += accum;
+
+ if (delta & DUCK_END_OF_SEQUENCE)
+ {
+ iVec = *(src_p++);
+ iSeq = 0;
+ }
+
+ *d_p = pix;
+ }
+ }
+ }
+}
+
+static bool
+dukv_OpenStream (TFB_DuckVideoDecoder* dukv)
+{
+ char filename[280];
+
+ strcat (strcpy (filename, dukv->basename), ".duk");
+
+ return (dukv->stream =
+ uio_fopen (dukv->basedir, filename, "rb")) != NULL;
+}
+
+static bool
+dukv_ReadFrames (TFB_DuckVideoDecoder* dukv)
+{
+ char filename[280];
+ uint32 i;
+ uio_Stream *fp;
+
+ strcat (strcpy (filename, dukv->basename), ".frm");
+
+ if (!(fp = uio_fopen (dukv->basedir, filename, "rb")))
+ return false;
+
+ // get number of frames
+ uio_fseek (fp, 0, SEEK_END);
+ dukv->cframes = uio_ftell (fp) / sizeof (uint32);
+ uio_fseek (fp, 0, SEEK_SET);
+ dukv->frames = (uint32*) HMalloc (dukv->cframes * sizeof (uint32));
+
+ if (uio_fread (dukv->frames, sizeof (uint32), dukv->cframes,
+ fp) != dukv->cframes)
+ {
+ HFree (dukv->frames);
+ dukv->frames = 0;
+ return 0;
+ }
+ uio_fclose (fp);
+
+ for (i = 0; i < dukv->cframes; ++i)
+ dukv->frames[i] = UQM_SwapBE32 (dukv->frames[i]);
+
+ return true;
+}
+
+static bool
+dukv_ReadVectors (TFB_DuckVideoDecoder* dukv, uint8* vectors)
+{
+ uio_Stream *fp;
+ char filename[280];
+ int ret;
+
+ strcat (strcpy (filename, dukv->basename), ".tbl");
+
+ if (!(fp = uio_fopen (dukv->basedir, filename, "rb")))
+ return false;
+
+ ret = uio_fread (vectors, NUM_VEC_ITEMS, NUM_VECTORS, fp);
+ uio_fclose (fp);
+
+ return ret == NUM_VECTORS;
+}
+
+static bool
+dukv_ReadHeader (TFB_DuckVideoDecoder* dukv, sint32* pl, sint32* pc)
+{
+ uio_Stream *fp;
+ char filename[280];
+ int ret;
+ int i;
+ TFB_DuckVideoHeader hdr;
+
+ strcat (strcpy (filename, dukv->basename), ".hdr");
+
+ if (!(fp = uio_fopen (dukv->basedir, filename, "rb")))
+ return false;
+
+ ret = uio_fread (&hdr, sizeof (hdr), 1, fp);
+ uio_fclose (fp);
+ if (!ret)
+ return false;
+
+ dukv->version = UQM_SwapBE32 (hdr.version);
+ dukv->wb = UQM_SwapBE16 (hdr.wb);
+ dukv->hb = UQM_SwapBE16 (hdr.hb);
+
+ for (i = 0; i < 8; ++i)
+ {
+ pl[i] = (sint16) UQM_SwapBE16 (hdr.lumas[i]);
+ pc[i] = (sint16) UQM_SwapBE16 (hdr.chromas[i]);
+ }
+
+ dukv->decoder.w = dukv->wb * 4;
+ dukv->decoder.h = dukv->hb * 4;
+
+ return true;
+}
+
+static sint32
+dukv_make_delta (sint32* protos, bool is_chroma, int i1, int i2)
+{
+ sint32 d1, d2;
+
+ if (!is_chroma)
+ {
+ // 0x421 is (r,g,b)=(1,1,1) in 15bit pixel coding
+ d1 = (protos[i1] >> 1) * 0x421;
+ d2 = (protos[i2] >> 1) * 0x421;
+ return ((d1 << 16) + d2) << 1;
+ }
+ else
+ {
+ d1 = (protos[i1] << 10) + protos[i2];
+ return ((d1 << 16) + d1) << 1;
+ }
+}
+
+static sint32
+dukv_make_corr (sint32* protos, bool is_chroma, int i1, int i2)
+{
+ sint32 d1, d2;
+
+ if (!is_chroma)
+ {
+ d1 = (protos[i1] & 1) << 15;
+ d2 = (protos[i2] & 1) << 15;
+ return (d1 << 16) + d2;
+ }
+ else
+ {
+ return (i1 << 3) + i2;
+ }
+}
+
+static void
+dukv_DecodeVector (uint8* vec, sint32* p, bool is_chroma, sint32* deltas)
+{
+ int citems = vec[0];
+ int i;
+
+ for (i = 0; i < citems; i += 2, vec += 2, deltas += 2)
+ {
+ sint32 d = dukv_make_delta (p, is_chroma, vec[1], vec[2]);
+
+ if (i == citems - 2)
+ d |= DUCK_END_OF_SEQUENCE;
+
+ deltas[0] = d;
+ deltas[1] = dukv_make_corr (p, is_chroma, vec[1], vec[2]);
+ }
+
+}
+
+static void
+dukv_InitDeltas (TFB_DuckVideoDecoder* dukv, uint8* vectors,
+ sint32* pl, sint32* pc)
+{
+ int i;
+
+ for (i = 0; i < NUM_VECTORS; ++i)
+ {
+ uint8* vector = vectors + i * NUM_VEC_ITEMS;
+ dukv_DecodeVector (vector, pl, false, dukv->d.lumas[i]);
+ dukv_DecodeVector (vector, pc, true, dukv->d.chromas[i]);
+ }
+}
+
+static inline uint32
+dukv_PixelConv (uint16 pix, const TFB_PixelFormat* fmt)
+{
+ uint32 r, g, b;
+
+ r = (pix >> 7) & 0xf8;
+ g = (pix >> 2) & 0xf8;
+ b = (pix << 3) & 0xf8;
+
+ return
+ ((r >> fmt->Rloss) << fmt->Rshift) |
+ ((g >> fmt->Gloss) << fmt->Gshift) |
+ ((b >> fmt->Bloss) << fmt->Bshift);
+}
+
+static void
+dukv_RenderFrame (THIS_PTR)
+{
+ TFB_DuckVideoDecoder* dukv = (TFB_DuckVideoDecoder*) This;
+ const TFB_PixelFormat* fmt = This->format;
+ uint32 h, x, y;
+ uint32* dec = dukv->decbuf;
+
+ h = dukv->decoder.h / 2;
+
+ // separate bpp versions for speed
+ switch (fmt->BytesPerPixel)
+ {
+ case 2:
+ {
+ for (y = 0; y < h; ++y)
+ {
+ uint16 *dst0, *dst1;
+
+ dst0 = (uint16*) This->callbacks.GetCanvasLine (This, y * 2);
+ dst1 = (uint16*) This->callbacks.GetCanvasLine (This, y * 2 + 1);
+
+ for (x = 0; x < dukv->decoder.w; ++x, ++dec, ++dst0, ++dst1)
+ {
+ uint32 pair = *dec;
+ *dst0 = dukv_PixelConv ((uint16)(pair >> 16), fmt);
+ *dst1 = dukv_PixelConv ((uint16)(pair & 0xffff), fmt);
+ }
+ }
+ break;
+ }
+ case 3:
+ {
+ for (y = 0; y < h; ++y)
+ {
+ uint8 *dst0, *dst1;
+
+ dst0 = (uint8*) This->callbacks.GetCanvasLine (This, y * 2);
+ dst1 = (uint8*) This->callbacks.GetCanvasLine (This, y * 2 + 1);
+
+ for (x = 0; x < dukv->decoder.w;
+ ++x, ++dec, dst0 += 3, dst1 += 3)
+ {
+ uint32 pair = *dec;
+ *(uint32*)dst0 =
+ dukv_PixelConv ((uint16)(pair >> 16), fmt);
+ *(uint32*)dst1 =
+ dukv_PixelConv ((uint16)(pair & 0xffff), fmt);
+ }
+ }
+ break;
+ }
+ case 4:
+ {
+ for (y = 0; y < h; ++y)
+ {
+ uint32 *dst0, *dst1;
+
+ dst0 = (uint32*) This->callbacks.GetCanvasLine (This, y * 2);
+ dst1 = (uint32*) This->callbacks.GetCanvasLine (This, y * 2 + 1);
+
+ for (x = 0; x < dukv->decoder.w; ++x, ++dec, ++dst0, ++dst1)
+ {
+ uint32 pair = *dec;
+ *dst0 = dukv_PixelConv ((uint16)(pair >> 16), fmt);
+ *dst1 = dukv_PixelConv ((uint16)(pair & 0xffff), fmt);
+ }
+ }
+ break;
+ }
+ default:
+ ;
+ }
+}
+
+static const char*
+dukv_GetName (void)
+{
+ return "DukVid";
+}
+
+static bool
+dukv_InitModule (int flags)
+{
+ // no flags are defined for now
+ return true;
+
+ (void)flags; // dodge compiler warning
+}
+
+static void
+dukv_TermModule (void)
+{
+ // do an extensive search on the word 'nothing'
+}
+
+static uint32
+dukv_GetStructSize (void)
+{
+ return sizeof (TFB_DuckVideoDecoder);
+}
+
+static int
+dukv_GetError (THIS_PTR)
+{
+ return This->error;
+}
+
+static bool
+dukv_Init (THIS_PTR, TFB_PixelFormat* fmt)
+{
+ This->format = fmt;
+ This->audio_synced = true;
+ return true;
+}
+
+static void
+dukv_Term (THIS_PTR)
+{
+ //TFB_DuckVideoDecoder* dukv = (TFB_DuckVideoDecoder*) This;
+
+ dukv_Close (This);
+}
+
+static bool
+dukv_Open (THIS_PTR, uio_DirHandle *dir, const char *filename)
+{
+ TFB_DuckVideoDecoder* dukv = (TFB_DuckVideoDecoder*) This;
+ char* pext;
+ sint32 lumas[8], chromas[8];
+ uint8* vectors;
+
+ dukv->basedir = dir;
+ dukv->basename = HMalloc (strlen (filename) + 1);
+ strcpy (dukv->basename, filename);
+ pext = strrchr (dukv->basename, '.');
+ if (pext) // strip extension
+ *pext = 0;
+
+ vectors = HMalloc (NUM_VEC_ITEMS * NUM_VECTORS);
+
+ if (!dukv_OpenStream (dukv)
+ || !dukv_ReadFrames (dukv)
+ || !dukv_ReadHeader (dukv, lumas, chromas)
+ || !dukv_ReadVectors (dukv, vectors))
+ {
+ HFree (vectors);
+ dukv_Close (This);
+ dukv->last_error = dukve_BadFile;
+ return false;
+ }
+
+ dukv_InitDeltas (dukv, vectors, lumas, chromas);
+ HFree (vectors);
+
+ This->length = (float) dukv->cframes / DUCK_GENERAL_FPS;
+ This->frame_count = dukv->cframes;
+ This->interframe_wait = (uint32) (1000.0 / DUCK_GENERAL_FPS);
+
+ dukv->inbuf = HMalloc (DUCK_MAX_FRAME_SIZE);
+ dukv->decbuf = HMalloc (
+ dukv->decoder.w * dukv->decoder.h * sizeof (uint16));
+
+ return true;
+}
+
+static void
+dukv_Close (THIS_PTR)
+{
+ TFB_DuckVideoDecoder* dukv = (TFB_DuckVideoDecoder*) This;
+
+ if (dukv->basename)
+ {
+ HFree (dukv->basename);
+ dukv->basename = NULL;
+ }
+ if (dukv->frames)
+ {
+ HFree (dukv->frames);
+ dukv->frames = NULL;
+ }
+ if (dukv->stream)
+ {
+ uio_fclose (dukv->stream);
+ dukv->stream = NULL;
+ }
+ if (dukv->inbuf)
+ {
+ HFree (dukv->inbuf);
+ dukv->inbuf = NULL;
+ }
+ if (dukv->decbuf)
+ {
+ HFree (dukv->decbuf);
+ dukv->decbuf = NULL;
+ }
+}
+
+static int
+dukv_DecodeNext (THIS_PTR)
+{
+ TFB_DuckVideoDecoder* dukv = (TFB_DuckVideoDecoder*) This;
+ uint32 fh[2];
+ uint32 vofs;
+ uint32 vsize;
+ uint16 ver;
+
+ if (!dukv->stream || dukv->iframe >= dukv->cframes)
+ return 0;
+
+ uio_fseek (dukv->stream, dukv->frames[dukv->iframe], SEEK_SET);
+ if (uio_fread (&fh, sizeof (fh), 1, dukv->stream) != 1)
+ {
+ dukv->last_error = dukve_EOF;
+ return 0;
+ }
+
+ vofs = UQM_SwapBE32 (fh[0]);
+ vsize = UQM_SwapBE32 (fh[1]);
+ if (vsize > DUCK_MAX_FRAME_SIZE)
+ {
+ dukv->last_error = dukve_OutOfBuf;
+ return -1;
+ }
+
+ uio_fseek (dukv->stream, vofs, SEEK_CUR);
+ if (uio_fread (dukv->inbuf, 1, vsize, dukv->stream) != vsize)
+ {
+ dukv->last_error = dukve_EOF;
+ return 0;
+ }
+
+ ver = UQM_SwapBE16 (*(uint16*)dukv->inbuf);
+ if (ver == 0x0300)
+ dukv_DecodeFrameV3 (dukv->inbuf + 0x10, dukv->decbuf,
+ dukv->wb, dukv->hb, &dukv->d);
+ else
+ dukv_DecodeFrame (dukv->inbuf + 0x10, dukv->decbuf,
+ dukv->wb, dukv->hb, &dukv->d);
+
+ dukv->iframe++;
+
+ This->callbacks.BeginFrame (This);
+ dukv_RenderFrame (This);
+ This->callbacks.EndFrame (This);
+
+ if (!This->audio_synced)
+ This->callbacks.SetTimer (This, (uint32) (1000.0f / DUCK_GENERAL_FPS));
+
+ return 1;
+}
+
+static uint32
+dukv_SeekFrame (THIS_PTR, uint32 frame)
+{
+ TFB_DuckVideoDecoder* dukv = (TFB_DuckVideoDecoder*) This;
+
+ if (frame > dukv->cframes)
+ frame = dukv->cframes; // EOS
+
+ return dukv->iframe = frame;
+}
+
+static float
+dukv_SeekTime (THIS_PTR, float time)
+{
+ //TFB_DuckVideoDecoder* dukv = (TFB_DuckVideoDecoder*) This;
+ uint32 frame = (uint32) (time * DUCK_GENERAL_FPS);
+
+ // Note that DUCK_GENERAL_FPS is a float constant
+ return dukv_SeekFrame (This, frame) / DUCK_GENERAL_FPS;
+}
+
+static uint32
+dukv_GetFrame (THIS_PTR)
+{
+ TFB_DuckVideoDecoder* dukv = (TFB_DuckVideoDecoder*) This;
+
+ return dukv->iframe;
+}
+
+static float
+dukv_GetTime (THIS_PTR)
+{
+ TFB_DuckVideoDecoder* dukv = (TFB_DuckVideoDecoder*) This;
+
+ return (float) dukv->iframe / DUCK_GENERAL_FPS;
+}