summaryrefslogtreecommitdiff
path: root/src/libs/video
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/video')
-rw-r--r--src/libs/video/Makeinfo3
-rw-r--r--src/libs/video/dukvid.c748
-rw-r--r--src/libs/video/dukvid.h36
-rw-r--r--src/libs/video/legacyplayer.c81
-rw-r--r--src/libs/video/vfileins.c28
-rw-r--r--src/libs/video/video.c190
-rw-r--r--src/libs/video/video.h56
-rw-r--r--src/libs/video/videodec.c363
-rw-r--r--src/libs/video/videodec.h124
-rw-r--r--src/libs/video/vidintrn.h41
-rw-r--r--src/libs/video/vidplayer.c481
-rw-r--r--src/libs/video/vidplayer.h31
-rw-r--r--src/libs/video/vresins.c186
13 files changed, 2368 insertions, 0 deletions
diff --git a/src/libs/video/Makeinfo b/src/libs/video/Makeinfo
new file mode 100644
index 0000000..1282e49
--- /dev/null
+++ b/src/libs/video/Makeinfo
@@ -0,0 +1,3 @@
+uqm_CFILES="vfileins.c vresins.c video.c videodec.c vidplayer.c dukvid.c \
+ legacyplayer.c"
+uqm_HFILES="dukvid.h videodec.h video.h vidintrn.h vidplayer.h"
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;
+}
diff --git a/src/libs/video/dukvid.h b/src/libs/video/dukvid.h
new file mode 100644
index 0000000..a6d70b0
--- /dev/null
+++ b/src/libs/video/dukvid.h
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+#ifndef LIBS_VIDEO_DUKVID_H_
+#define LIBS_VIDEO_DUKVID_H_
+
+#include "libs/video/videodec.h"
+
+extern TFB_VideoDecoderFuncs dukv_DecoderVtbl;
+
+typedef enum
+{
+ // positive values are the same as in errno
+ dukve_None = 0,
+ dukve_Unknown = -1,
+ dukve_BadFile = -2,
+ dukve_BadArg = -3,
+ dukve_OutOfBuf = -4,
+ dukve_EOF = -5,
+ dukve_Other = -1000,
+} DukVid_Error;
+
+#endif // LIBS_VIDEO_DUKVID_H_
diff --git a/src/libs/video/legacyplayer.c b/src/libs/video/legacyplayer.c
new file mode 100644
index 0000000..5dfddad
--- /dev/null
+++ b/src/libs/video/legacyplayer.c
@@ -0,0 +1,81 @@
+/*
+ * 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.
+ */
+
+#include "vidintrn.h"
+#include "video.h"
+#include "vidplayer.h"
+#include "libs/memlib.h"
+
+
+LEGACY_VIDEO_REF
+PlayLegacyVideo (LEGACY_VIDEO vid)
+{
+ const char *name, *audname, *speechname;
+ uint32 loopframe;
+ LEGACY_VIDEO_REF ref;
+ VIDEO_TYPE type;
+
+ if (!vid)
+ return NULL;
+ ref = HCalloc (sizeof (*ref));
+ if (!ref)
+ return NULL;
+ name = vid->video;
+ audname = vid->audio;
+ speechname = vid->speech;
+ loopframe = vid->loop;
+
+ ref->vidref = LoadVideoFile (name);
+ if (!ref->vidref)
+ return NULL;
+ if (audname)
+ ref->audref = LoadMusicFile (audname);
+ if (speechname)
+ ref->speechref = LoadMusicFile (speechname);
+
+ type = VidPlayEx (ref->vidref, ref->audref, ref->speechref, loopframe);
+ if (type == NO_FMV)
+ { // Video failed to start
+ StopLegacyVideo (ref);
+ return NULL;
+ }
+
+ return ref;
+}
+
+void
+StopLegacyVideo (LEGACY_VIDEO_REF ref)
+{
+ if (!ref)
+ return;
+ VidStop ();
+
+ DestroyVideo (ref->vidref);
+ if (ref->speechref)
+ DestroyMusic (ref->speechref);
+ if (ref->audref)
+ DestroyMusic (ref->audref);
+
+ HFree (ref);
+}
+
+BOOLEAN
+PlayingLegacyVideo (LEGACY_VIDEO_REF ref)
+{
+ if (!ref)
+ return FALSE;
+ return TFB_VideoPlaying (ref->vidref);
+}
diff --git a/src/libs/video/vfileins.c b/src/libs/video/vfileins.c
new file mode 100644
index 0000000..26cb7c1
--- /dev/null
+++ b/src/libs/video/vfileins.c
@@ -0,0 +1,28 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * 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.
+ */
+
+#include "libs/vidlib.h"
+#include "video.h"
+
+VIDEO_REF
+LoadVideoFile (const char *pStr)
+{
+ return _init_video_file (pStr);
+}
+
+
diff --git a/src/libs/video/video.c b/src/libs/video/video.c
new file mode 100644
index 0000000..dd4cd46
--- /dev/null
+++ b/src/libs/video/video.c
@@ -0,0 +1,190 @@
+/*
+ * 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.
+ */
+
+#include "video.h"
+
+#include "vidintrn.h"
+#include "options.h"
+#include "vidplayer.h"
+#include "libs/memlib.h"
+#include "libs/sndlib.h"
+
+
+#define NULL_VIDEO_REF (0)
+static VIDEO_REF _cur_video = NULL_VIDEO_REF;
+static MUSIC_REF _cur_speech = 0;
+
+BOOLEAN
+InitVideoPlayer (BOOLEAN useCDROM)
+ //useCDROM doesn't really apply to us
+{
+ TFB_PixelFormat fmt;
+
+ TFB_DrawCanvas_GetScreenFormat (&fmt);
+ if (!VideoDecoder_Init (0, fmt.BitsPerPixel, fmt.Rmask,
+ fmt.Gmask, fmt.Bmask, 0))
+ return FALSE;
+
+ return TFB_InitVideoPlayer ();
+
+ (void)useCDROM; /* dodge compiler warning */
+}
+
+void
+UninitVideoPlayer (void)
+{
+ TFB_UninitVideoPlayer ();
+ VideoDecoder_Uninit ();
+}
+
+void
+VidStop (void)
+{
+ if (_cur_speech)
+ snd_StopSpeech ();
+ if (_cur_video)
+ TFB_StopVideo (_cur_video);
+ _cur_speech = 0;
+ _cur_video = NULL_VIDEO_REF;
+}
+
+VIDEO_REF
+VidPlaying (void)
+ // this should just probably return BOOLEAN
+{
+ if (!_cur_video)
+ return NULL_VIDEO_REF;
+
+ if (TFB_VideoPlaying (_cur_video))
+ return _cur_video;
+
+ return NULL_VIDEO_REF;
+}
+
+BOOLEAN
+VidProcessFrame (void)
+{
+ if (!_cur_video)
+ return FALSE;
+ return TFB_ProcessVideoFrame (_cur_video);
+}
+
+// return current video position in milliseconds
+DWORD
+VidGetPosition (void)
+{
+ if (!VidPlaying ())
+ return 0;
+ return TFB_GetVideoPosition (_cur_video);
+}
+
+BOOLEAN
+VidSeek (DWORD pos)
+ // pos in milliseconds
+{
+ if (!VidPlaying ())
+ return FALSE;
+ return TFB_SeekVideo (_cur_video, pos);
+}
+
+VIDEO_TYPE
+VidPlayEx (VIDEO_REF vid, MUSIC_REF AudRef, MUSIC_REF SpeechRef,
+ DWORD LoopFrame)
+{
+ VIDEO_TYPE ret;
+
+ if (!vid)
+ return NO_FMV;
+
+ if (AudRef)
+ {
+ if (vid->hAudio)
+ DestroyMusic (vid->hAudio);
+ vid->hAudio = AudRef;
+ vid->decoder->audio_synced = FALSE;
+ }
+
+ vid->loop_frame = LoopFrame;
+ vid->loop_to = 0;
+
+ if (_cur_speech)
+ snd_StopSpeech ();
+ if (_cur_video)
+ TFB_StopVideo (_cur_video);
+ _cur_speech = 0;
+ _cur_video = NULL_VIDEO_REF;
+
+ // play video in the center of the screen
+ if (TFB_PlayVideo (vid, (ScreenWidth - vid->w) / 2,
+ (ScreenHeight - vid->h) / 2))
+ {
+ _cur_video = vid;
+ ret = SOFTWARE_FMV;
+ if (SpeechRef)
+ {
+ snd_PlaySpeech (SpeechRef);
+ _cur_speech = SpeechRef;
+ }
+ }
+ else
+ {
+ ret = NO_FMV;
+ }
+
+ return ret;
+}
+
+VIDEO_TYPE
+VidPlay (VIDEO_REF VidRef)
+{
+ return VidPlayEx (VidRef, 0, 0, VID_NO_LOOP);
+}
+
+VIDEO_REF
+_init_video_file (const char *pStr)
+{
+ TFB_VideoClip* vid;
+ TFB_VideoDecoder* dec;
+
+ dec = VideoDecoder_Load (contentDir, pStr);
+ if (!dec)
+ return NULL_VIDEO_REF;
+
+ vid = HCalloc (sizeof (*vid));
+ vid->decoder = dec;
+ vid->length = dec->length;
+ vid->w = vid->decoder->w;
+ vid->h = vid->decoder->h;
+ vid->guard = CreateMutex ("video guard", SYNC_CLASS_VIDEO);
+
+ return (VIDEO_REF) vid;
+}
+
+BOOLEAN
+DestroyVideo (VIDEO_REF vid)
+{
+ if (!vid)
+ return FALSE;
+
+ // just some armouring; should already be stopped
+ TFB_StopVideo (vid);
+
+ VideoDecoder_Free (vid->decoder);
+ DestroyMutex (vid->guard);
+ HFree (vid);
+
+ return TRUE;
+}
diff --git a/src/libs/video/video.h b/src/libs/video/video.h
new file mode 100644
index 0000000..5e76aa4
--- /dev/null
+++ b/src/libs/video/video.h
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+#ifndef LIBS_VIDEO_VIDEO_H_
+#define LIBS_VIDEO_VIDEO_H_
+
+#include "libs/vidlib.h"
+#include "libs/sndlib.h"
+#include "libs/graphics/tfb_draw.h"
+#include "types.h"
+#include "videodec.h"
+#include "libs/sound/sound.h"
+
+
+typedef struct tfb_videoclip
+{
+ TFB_VideoDecoder *decoder; // decoder to read from
+ float length; // total length of clip seconds
+ uint32 w, h;
+
+ // video player data
+ RECT dst_rect; // destination screen rect
+ RECT src_rect; // source rect
+ MUSIC_REF hAudio;
+ uint32 frame_time; // time when next frame should be rendered
+ TFB_Image* frame; // frame preped and optimized for rendering
+ uint32 cur_frame; // index of frame currently displayed
+ bool playing;
+ bool own_audio;
+ uint32 loop_frame; // frame index to loop from
+ uint32 loop_to; // frame index to loop to
+
+ Mutex guard;
+ uint32 want_frame; // audio-signaled desired frame index
+ int lag_cnt; // N of frames video is behind or ahead of audio
+
+ void* data; // user-defined data
+
+} TFB_VideoClip;
+
+extern VIDEO_REF _init_video_file(const char *pStr);
+
+#endif // LIBS_VIDEO_VIDEO_H_
diff --git a/src/libs/video/videodec.c b/src/libs/video/videodec.c
new file mode 100644
index 0000000..b99a442
--- /dev/null
+++ b/src/libs/video/videodec.c
@@ -0,0 +1,363 @@
+/*
+ * 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.
+ */
+
+#include <string.h>
+#include "video.h"
+#include "videodec.h"
+#include "dukvid.h"
+#include "libs/log.h"
+#include "libs/memlib.h"
+
+#define MAX_REG_DECODERS 31
+
+static bool vd_inited = false;
+static TFB_PixelFormat vd_vidfmt;
+static int vd_flags = 0;
+
+struct TFB_RegVideoDecoder
+{
+ bool builtin;
+ bool used; // ever used indicator
+ const char* ext;
+ const TFB_VideoDecoderFuncs* funcs;
+};
+static TFB_RegVideoDecoder vd_decoders[MAX_REG_DECODERS + 1] =
+{
+ {true, true, "duk", &dukv_DecoderVtbl},
+ {false, false, NULL, NULL}, // null term
+};
+
+static void vd_computeMasks (uint32 mask, DWORD* shift, DWORD* loss);
+
+const char*
+VideoDecoder_GetName (TFB_VideoDecoder *decoder)
+{
+ if (!decoder || !decoder->funcs)
+ return "(Null)";
+ return decoder->funcs->GetName ();
+}
+
+bool
+VideoDecoder_Init (int flags, int depth, uint32 Rmask, uint32 Gmask,
+ uint32 Bmask, uint32 Amask)
+{
+ TFB_RegVideoDecoder* info;
+
+ vd_inited = false;
+
+ if (depth < 15 || depth > 32)
+ {
+ log_add (log_Error, "VideoDecoder_Init: "
+ "Unsupported video depth %d", depth);
+ return false;
+ }
+
+ if ((Rmask & Gmask) || (Rmask & Bmask) || (Rmask & Amask) ||
+ (Gmask & Bmask) || (Gmask & Amask) || (Bmask & Amask))
+ {
+ log_add (log_Error, "VideoDecoder_Init: Invalid channel masks");
+ return false;
+ }
+
+ // BEGIN: adapted from SDL
+ vd_vidfmt.BitsPerPixel = depth;
+ vd_vidfmt.BytesPerPixel = (depth + 7) / 8;
+ vd_vidfmt.Rmask = Rmask;
+ vd_vidfmt.Gmask = Gmask;
+ vd_vidfmt.Bmask = Bmask;
+ vd_vidfmt.Amask = Amask;
+ vd_computeMasks (Rmask, &vd_vidfmt.Rshift, &vd_vidfmt.Rloss);
+ vd_computeMasks (Gmask, &vd_vidfmt.Gshift, &vd_vidfmt.Gloss);
+ vd_computeMasks (Bmask, &vd_vidfmt.Bshift, &vd_vidfmt.Bloss);
+ vd_computeMasks (Amask, &vd_vidfmt.Ashift, &vd_vidfmt.Aloss);
+ // END: adapted from SDL
+
+ // init built-in decoders
+ for (info = vd_decoders; info->ext; info++)
+ {
+ if (!info->funcs->InitModule (flags))
+ {
+ log_add (log_Error, "VideoDecoder_Init(): "
+ "%s video decoder init failed",
+ info->funcs->GetName ());
+ }
+ }
+
+ vd_flags = flags;
+ vd_inited = true;
+
+ return true;
+}
+
+void
+VideoDecoder_Uninit (void)
+{
+ TFB_RegVideoDecoder* info;
+
+ // uninit all decoders
+ // and unregister loaded decoders
+ for (info = vd_decoders; info->used; info++)
+ {
+ if (info->ext) // check if present
+ info->funcs->TermModule ();
+
+ if (!info->builtin)
+ {
+ info->used = false;
+ info->ext = NULL;
+ }
+ }
+
+ vd_inited = false;
+}
+
+TFB_RegVideoDecoder*
+VideoDecoder_Register (const char* fileext, TFB_VideoDecoderFuncs* decvtbl)
+{
+ TFB_RegVideoDecoder* info;
+ TFB_RegVideoDecoder* newslot = NULL;
+
+ if (!decvtbl)
+ {
+ log_add (log_Warning, "VideoDecoder_Register(): Null decoder table");
+ return NULL;
+ }
+ if (!fileext)
+ {
+ log_add (log_Warning, "VideoDecoder_Register(): Bad file type for %s",
+ decvtbl->GetName ());
+ return NULL;
+ }
+
+ // check if extension already registered
+ for (info = vd_decoders; info->used &&
+ (!info->ext || strcmp (info->ext, fileext) != 0);
+ ++info)
+ {
+ // and pick up an empty slot (where available)
+ if (!newslot && !info->ext)
+ newslot = info;
+ }
+
+ if (info >= vd_decoders + MAX_REG_DECODERS)
+ {
+ log_add (log_Warning, "VideoDecoder_Register(): Decoders limit reached");
+ return NULL;
+ }
+ else if (info->ext)
+ {
+ log_add (log_Warning, "VideoDecoder_Register(): "
+ "'%s' decoder already registered (%s denied)",
+ fileext, decvtbl->GetName ());
+ return NULL;
+ }
+
+ if (!decvtbl->InitModule (vd_flags))
+ {
+ log_add (log_Warning, "VideoDecoder_Register(): %s decoder init failed",
+ decvtbl->GetName ());
+ return NULL;
+ }
+
+ if (!newslot)
+ {
+ newslot = info;
+ newslot->used = true;
+ // make next one a term
+ info[1].builtin = false;
+ info[1].used = false;
+ info[1].ext = NULL;
+ }
+
+ newslot->ext = fileext;
+ newslot->funcs = decvtbl;
+
+ return newslot;
+}
+
+void
+VideoDecoder_Unregister (TFB_RegVideoDecoder* regdec)
+{
+ if (regdec < vd_decoders || regdec >= vd_decoders + MAX_REG_DECODERS ||
+ !regdec->ext || !regdec->funcs)
+ {
+ log_add (log_Warning, "VideoDecoder_Unregister(): "
+ "Invalid or expired decoder passed");
+ return;
+ }
+
+ regdec->funcs->TermModule ();
+ regdec->ext = NULL;
+ regdec->funcs = NULL;
+}
+
+const TFB_VideoDecoderFuncs*
+VideoDecoder_Lookup (const char* fileext)
+{
+ TFB_RegVideoDecoder* info;
+
+ for (info = vd_decoders; info->used &&
+ (!info->ext || strcmp (info->ext, fileext) != 0);
+ ++info)
+ ;
+ return info->ext ? info->funcs : NULL;
+}
+
+TFB_VideoDecoder*
+VideoDecoder_Load (uio_DirHandle *dir, const char *filename)
+{
+ const char* pext;
+ TFB_RegVideoDecoder* info;
+ TFB_VideoDecoder* decoder;
+
+
+ if (!vd_inited)
+ return NULL;
+
+ pext = strrchr (filename, '.');
+ if (!pext)
+ {
+ log_add (log_Warning, "VideoDecoder_Load: Unknown file type");
+ return NULL;
+ }
+ ++pext;
+
+ for (info = vd_decoders; info->used &&
+ (!info->ext || strcmp (info->ext, pext) != 0);
+ ++info)
+ ;
+ if (!info->ext)
+ {
+ log_add (log_Warning, "VideoDecoder_Load: Unsupported file type");
+ return NULL;
+ }
+
+ decoder = HCalloc (info->funcs->GetStructSize ());
+ decoder->funcs = info->funcs;
+ if (!decoder->funcs->Init (decoder, &vd_vidfmt))
+ {
+ log_add (log_Warning, "VideoDecoder_Load: "
+ "Cannot init '%s' decoder, code %d",
+ decoder->funcs->GetName (),
+ decoder->funcs->GetError (decoder));
+ HFree (decoder);
+ return NULL;
+ }
+
+ decoder->dir = dir;
+ decoder->filename = (char *) HMalloc (strlen (filename) + 1);
+ strcpy (decoder->filename, filename);
+ decoder->error = VIDEODECODER_OK;
+
+ if (!decoder->funcs->Open (decoder, dir, filename))
+ {
+ log_add (log_Warning, "VideoDecoder_Load: "
+ "'%s' decoder did not load %s, code %d",
+ decoder->funcs->GetName (), filename,
+ decoder->funcs->GetError (decoder));
+
+ VideoDecoder_Free (decoder);
+ return NULL;
+ }
+
+ return decoder;
+}
+
+// return: >0 = OK, 0 = EOF, <0 = Error
+int
+VideoDecoder_Decode (TFB_VideoDecoder *decoder)
+{
+ int ret;
+
+ if (!decoder)
+ return 0;
+
+ decoder->cur_frame = decoder->funcs->GetFrame (decoder);
+ decoder->pos = decoder->funcs->GetTime (decoder);
+
+ ret = decoder->funcs->DecodeNext (decoder);
+ if (ret == 0)
+ decoder->error = VIDEODECODER_EOF;
+ else if (ret < 0)
+ decoder->error = VIDEODECODER_ERROR;
+ else
+ decoder->error = VIDEODECODER_OK;
+
+ return ret;
+}
+
+float
+VideoDecoder_Seek (TFB_VideoDecoder *decoder, float pos)
+{
+ if (!decoder)
+ return 0.0;
+
+ decoder->pos = decoder->funcs->SeekTime (decoder, pos);
+ decoder->cur_frame = decoder->funcs->GetFrame (decoder);
+
+ return decoder->pos;
+}
+
+uint32
+VideoDecoder_SeekFrame (TFB_VideoDecoder *decoder, uint32 frame)
+{
+ if (!decoder)
+ return 0;
+
+ decoder->cur_frame = decoder->funcs->SeekFrame (decoder, frame);
+ decoder->pos = decoder->funcs->GetTime (decoder);
+
+ return decoder->cur_frame;
+}
+
+void
+VideoDecoder_Rewind (TFB_VideoDecoder *decoder)
+{
+ if (!decoder)
+ return;
+
+ VideoDecoder_Seek (decoder, 0);
+}
+
+void
+VideoDecoder_Free (TFB_VideoDecoder *decoder)
+{
+ if (!decoder)
+ return;
+
+ decoder->funcs->Close (decoder);
+ decoder->funcs->Term (decoder);
+
+ HFree (decoder->filename);
+ HFree (decoder);
+}
+
+// BEGIN: adapted from SDL
+static void
+vd_computeMasks (uint32 mask, DWORD* shift, DWORD* loss)
+{
+ *shift = 0;
+ *loss = 8;
+ if (mask)
+ {
+ for (; !(mask & 1); mask >>= 1 )
+ ++*shift;
+
+ for (; (mask & 1); mask >>= 1 )
+ --*loss;
+ }
+}
+// END: adapted from SDL
diff --git a/src/libs/video/videodec.h b/src/libs/video/videodec.h
new file mode 100644
index 0000000..2d75c98
--- /dev/null
+++ b/src/libs/video/videodec.h
@@ -0,0 +1,124 @@
+/*
+ * 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.
+ */
+
+#ifndef LIBS_VIDEO_VIDEODEC_H_
+#define LIBS_VIDEO_VIDEODEC_H_
+
+#include "libs/vidlib.h"
+#include "libs/video/video.h"
+#include "libs/reslib.h"
+
+// forward-declare
+typedef struct tfb_videodecoder TFB_VideoDecoder;
+
+#define THIS_PTR TFB_VideoDecoder*
+
+typedef struct tfb_videodecoderfunc
+{
+ const char* (* GetName) (void);
+ bool (* InitModule) (int flags);
+ void (* TermModule) (void);
+ uint32 (* GetStructSize) (void);
+ int (* GetError) (THIS_PTR);
+ bool (* Init) (THIS_PTR, TFB_PixelFormat* fmt);
+ void (* Term) (THIS_PTR);
+ bool (* Open) (THIS_PTR, uio_DirHandle *dir, const char *filename);
+ void (* Close) (THIS_PTR);
+ int (* DecodeNext) (THIS_PTR);
+ uint32 (* SeekFrame) (THIS_PTR, uint32 frame);
+ float (* SeekTime) (THIS_PTR, float time);
+ uint32 (* GetFrame) (THIS_PTR);
+ float (* GetTime) (THIS_PTR);
+
+} TFB_VideoDecoderFuncs;
+
+// decoder will call these to get info
+// from the player
+typedef struct tfb_videocallbacks
+{
+ // any decoder calls these
+ void (* BeginFrame) (THIS_PTR);
+ void (* EndFrame) (THIS_PTR);
+ void* (* GetCanvasLine) (THIS_PTR, uint32 line);
+ // non-audio-driven decoders call this to figure out
+ // when the next frame should be drawn
+ uint32 (* GetTicks) (THIS_PTR);
+ // non-audio-driven decoders call this to inform
+ // the player when the next frame should be drawn
+ bool (* SetTimer) (THIS_PTR, uint32 msecs);
+
+} TFB_VideoCallbacks;
+
+#undef THIS_PTR
+
+struct tfb_videodecoder
+{
+ // decoder virtual funcs - R/O
+ const TFB_VideoDecoderFuncs *funcs;
+ // video formats - R/O
+ const TFB_PixelFormat *format;
+ // decoder-set data - R/O
+ uint32 w, h;
+ float length; // total length in seconds
+ uint32 frame_count;
+ uint32 interframe_wait; // nominal interframe delay in msecs
+ bool audio_synced;
+ // decoder callbacks
+ TFB_VideoCallbacks callbacks;
+
+ // other - public
+ bool looping;
+ void* data; // user-defined data
+ // info - public R/O
+ sint32 error;
+ float pos; // position in seconds
+ uint32 cur_frame;
+
+ // semi-private
+ uio_DirHandle *dir;
+ char *filename;
+
+};
+
+// return values
+enum
+{
+ VIDEODECODER_OK,
+ VIDEODECODER_ERROR,
+ VIDEODECODER_EOF,
+};
+
+typedef struct TFB_RegVideoDecoder TFB_RegVideoDecoder;
+
+TFB_RegVideoDecoder* VideoDecoder_Register (const char* fileext,
+ TFB_VideoDecoderFuncs* decvtbl);
+void VideoDecoder_Unregister (TFB_RegVideoDecoder* regdec);
+const TFB_VideoDecoderFuncs* VideoDecoder_Lookup (const char* fileext);
+
+bool VideoDecoder_Init (int flags, int depth, uint32 Rmask, uint32 Gmask,
+ uint32 Bmask, uint32 Amask);
+void VideoDecoder_Uninit (void);
+TFB_VideoDecoder* VideoDecoder_Load (uio_DirHandle *dir,
+ const char *filename);
+int VideoDecoder_Decode (TFB_VideoDecoder *decoder);
+float VideoDecoder_Seek (TFB_VideoDecoder *decoder, float time_pos);
+uint32 VideoDecoder_SeekFrame (TFB_VideoDecoder *decoder, uint32 frame_pos);
+void VideoDecoder_Rewind (TFB_VideoDecoder *decoder);
+void VideoDecoder_Free (TFB_VideoDecoder *decoder);
+const char* VideoDecoder_GetName (TFB_VideoDecoder *decoder);
+
+
+#endif // LIBS_VIDEO_VIDEODEC_H_
diff --git a/src/libs/video/vidintrn.h b/src/libs/video/vidintrn.h
new file mode 100644
index 0000000..40a19e4
--- /dev/null
+++ b/src/libs/video/vidintrn.h
@@ -0,0 +1,41 @@
+// Copyright 2008 Michael Martin
+
+/*
+ * 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.
+ */
+
+#ifndef VIDINTERN_H_
+#define VIDINTERN_H_
+
+#include "types.h"
+#include "libs/vidlib.h"
+#include "libs/threadlib.h"
+
+struct legacy_video_desc
+{
+ char *video, *audio, *speech;
+ uint32 loop;
+};
+
+typedef struct legacy_video_desc LEGACY_VIDEO_DESC;
+
+struct legacy_video_ref
+{
+ VIDEO_REF vidref;
+ MUSIC_REF audref;
+ MUSIC_REF speechref;
+};
+
+#endif
diff --git a/src/libs/video/vidplayer.c b/src/libs/video/vidplayer.c
new file mode 100644
index 0000000..09a506d
--- /dev/null
+++ b/src/libs/video/vidplayer.c
@@ -0,0 +1,481 @@
+/*
+ * 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.
+ */
+
+#include "vidplayer.h"
+
+#include "vidintrn.h"
+#include "libs/graphics/gfx_common.h"
+#include "libs/graphics/tfb_draw.h"
+#include "libs/log.h"
+#include "libs/memlib.h"
+#include "libs/sndlib.h"
+
+// video callbacks
+static void vp_BeginFrame (TFB_VideoDecoder*);
+static void vp_EndFrame (TFB_VideoDecoder*);
+static void* vp_GetCanvasLine (TFB_VideoDecoder*, uint32 line);
+static uint32 vp_GetTicks (TFB_VideoDecoder*);
+static bool vp_SetTimer (TFB_VideoDecoder*, uint32 msecs);
+
+
+static const TFB_VideoCallbacks vp_DecoderCBs =
+{
+ vp_BeginFrame,
+ vp_EndFrame,
+ vp_GetCanvasLine,
+ vp_GetTicks,
+ vp_SetTimer
+};
+
+// audio stream callbacks
+static bool vp_AudioStart (TFB_SoundSample* sample);
+static void vp_AudioEnd (TFB_SoundSample* sample);
+static void vp_BufferTag (TFB_SoundSample* sample, TFB_SoundTag* tag);
+static void vp_QueueBuffer (TFB_SoundSample* sample, audio_Object buffer);
+
+static const TFB_SoundCallbacks vp_AudioCBs =
+{
+ vp_AudioStart,
+ NULL,
+ vp_AudioEnd,
+ vp_BufferTag,
+ vp_QueueBuffer
+};
+
+
+bool
+TFB_InitVideoPlayer (void)
+{
+ // now just a stub
+ return true;
+}
+
+void
+TFB_UninitVideoPlayer (void)
+{
+ // now just a stub
+}
+
+static inline sint32
+msecToTimeCount (sint32 msec)
+{
+ return msec * ONE_SECOND / 1000;
+}
+
+// audio-synced video playback frame function
+// the frame rate and timing is dictated by the audio
+static bool
+processAudioSyncedFrame (VIDEO_REF vid)
+{
+#define MAX_FRAME_LAG 8
+#define LAG_FRACTION 6
+#define SYNC_BIAS 1 / 3
+ int ret;
+ uint32 want_frame;
+ uint32 prev_want_frame;
+ sint32 wait_msec;
+ CONTEXT oldContext;
+ TimeCount Now = GetTimeCounter ();
+
+ if (!vid->playing)
+ return false;
+
+ if (Now < vid->frame_time)
+ return true; // not time yet
+
+ LockMutex (vid->guard);
+ want_frame = vid->want_frame;
+ UnlockMutex (vid->guard);
+
+ if (want_frame >= vid->decoder->frame_count)
+ {
+ vid->playing = false;
+ return false;
+ }
+
+ // this works like so (audio-synced):
+ // 1. you call VideoDecoder_Seek() [when necessary] and
+ // VideoDecoder_Decode()
+ // 2. wait till it's time for this frame to be drawn
+ // the timeout is necessary because the audio signaling is not
+ // precise (see vp_AudioStart, vp_AudioEnd, vp_BufferTag)
+ // 3. output the frame; if the audio is behind, the lag counter
+ // goes up; if the video is behind, the lag counter goes down
+ // 4. set the next frame timeout; lag counter increases or
+ // decreases the timeout to allow audio or video to catch up
+ // 5. on a seek operation, the audio stream is moved to the
+ // correct position and then the audio signals the frame
+ // that should be rendered
+ // The system of timeouts and lag counts should make the video
+ // *relatively* smooth
+ //
+ prev_want_frame = vid->cur_frame - vid->lag_cnt;
+ if (want_frame > prev_want_frame - MAX_FRAME_LAG
+ && want_frame <= prev_want_frame + MAX_FRAME_LAG)
+ {
+ // we will draw the next frame right now, thus +1
+ vid->lag_cnt = vid->cur_frame + 1 - want_frame;
+ }
+ else
+ { // out of sequence frame, let's get it
+ vid->lag_cnt = 0;
+ vid->cur_frame = VideoDecoder_SeekFrame (vid->decoder, want_frame);
+ ret = VideoDecoder_Decode (vid->decoder);
+ if (ret < 0)
+ { // decoder returned a failure
+ vid->playing = false;
+ return false;
+ }
+ }
+ vid->cur_frame = vid->decoder->cur_frame;
+
+ // draw the frame
+ // We have the cliprect precalculated and don't need the rest
+ oldContext = SetContext (NULL);
+ TFB_DrawScreen_Image (vid->frame,
+ vid->dst_rect.corner.x, vid->dst_rect.corner.y, 0, 0,
+ NULL, DRAW_REPLACE_MODE, TFB_SCREEN_MAIN);
+ SetContext (oldContext);
+ FlushGraphics (); // needed to prevent half-frame updates
+
+ // increase interframe with positive lag-count to allow audio to catch up
+ // decrease interframe with negative lag-count to allow video to catch up
+ wait_msec = vid->decoder->interframe_wait
+ - (int)vid->decoder->interframe_wait * SYNC_BIAS
+ + (int)vid->decoder->interframe_wait * vid->lag_cnt / LAG_FRACTION;
+ vid->frame_time = Now + msecToTimeCount (wait_msec);
+
+ ret = VideoDecoder_Decode (vid->decoder);
+ if (ret < 0)
+ {
+ // TODO: decide what to do on error
+ }
+
+ return vid->playing;
+}
+
+// audio-independent video playback frame function
+// the frame rate and timing is dictated by the video decoder
+static bool
+processMuteFrame (VIDEO_REF vid)
+{
+ int ret;
+ TimeCount Now = GetTimeCounter ();
+
+ if (!vid->playing)
+ return false;
+
+ // this works like so:
+ // 1. you call VideoDecoder_Seek() [when necessary] and
+ // VideoDecoder_Decode()
+ // 2. the decoder calls back vp_GetTicks() and vp_SetTimer()
+ // to figure out and tell you when to render the frame
+ // being decoded
+ // On a seek operation, the decoder should reset its internal
+ // clock and call vp_GetTicks() again
+ //
+ if (Now >= vid->frame_time)
+ {
+ CONTEXT oldContext;
+
+ vid->cur_frame = vid->decoder->cur_frame;
+
+ // We have the cliprect precalculated and don't need the rest
+ oldContext = SetContext (NULL);
+ TFB_DrawScreen_Image (vid->frame,
+ vid->dst_rect.corner.x, vid->dst_rect.corner.y, 0, 0,
+ NULL, DRAW_REPLACE_MODE, TFB_SCREEN_MAIN);
+ SetContext (oldContext);
+ FlushGraphics (); // needed to prevent half-frame updates
+
+ if (vid->cur_frame == vid->loop_frame)
+ VideoDecoder_SeekFrame (vid->decoder, vid->loop_to);
+
+ ret = VideoDecoder_Decode (vid->decoder);
+ if (ret <= 0)
+ vid->playing = false;
+ }
+
+ return vid->playing;
+}
+
+bool
+TFB_PlayVideo (VIDEO_REF vid, uint32 x, uint32 y)
+{
+ RECT scrn_r;
+ RECT clip_r = {{0, 0}, {vid->w, vid->h}};
+ RECT vid_r = {{0, 0}, {ScreenWidth, ScreenHeight}};
+ RECT dr = {{x, y}, {vid->w, vid->h}};
+ RECT sr;
+ bool loop_music = false;
+ int ret;
+
+ if (!vid)
+ return false;
+
+ // calculate the frame-source and screen-destination rects
+ GetContextClipRect (&scrn_r);
+ if (!BoxIntersect(&scrn_r, &vid_r, &scrn_r))
+ return false; // drawing outside visible
+
+ sr = dr;
+ sr.corner.x = -sr.corner.x;
+ sr.corner.y = -sr.corner.y;
+ if (!BoxIntersect (&clip_r, &sr, &sr))
+ return false; // drawing outside visible
+
+ dr.corner.x += scrn_r.corner.x;
+ dr.corner.y += scrn_r.corner.y;
+ if (!BoxIntersect (&scrn_r, &dr, &vid->dst_rect))
+ return false; // drawing outside visible
+
+ vid->src_rect = vid->dst_rect;
+ vid->src_rect.corner.x = sr.corner.x;
+ vid->src_rect.corner.y = sr.corner.y;
+
+ vid->decoder->callbacks = vp_DecoderCBs;
+ vid->decoder->data = vid;
+
+ vid->frame = TFB_DrawImage_CreateForScreen (vid->w, vid->h, FALSE);
+ vid->cur_frame = -1;
+ vid->want_frame = -1;
+
+ if (!vid->hAudio)
+ {
+ vid->hAudio = LoadMusicFile (vid->decoder->filename);
+ vid->own_audio = true;
+ }
+
+ if (vid->decoder->audio_synced)
+ {
+ if (!vid->hAudio)
+ {
+ log_add (log_Warning, "TFB_PlayVideo: "
+ "Cannot load sound-track for audio-synced video");
+ return false;
+ }
+
+ TFB_SetSoundSampleCallbacks (*vid->hAudio, &vp_AudioCBs);
+ TFB_SetSoundSampleData (*vid->hAudio, vid);
+ }
+
+ // get the first frame
+ ret = VideoDecoder_Decode (vid->decoder);
+ if (ret < 0)
+ return false;
+
+ vid->playing = true;
+
+ loop_music = !vid->decoder->audio_synced && vid->loop_frame != VID_NO_LOOP;
+ if (vid->hAudio)
+ PLRPlaySong (vid->hAudio, loop_music, 1);
+
+ if (vid->decoder->audio_synced)
+ {
+ // draw the first frame now
+ vid->frame_time = GetTimeCounter ();
+ }
+
+ return true;
+}
+
+void
+TFB_StopVideo (VIDEO_REF vid)
+{
+ if (!vid)
+ return;
+
+ vid->playing = false;
+
+ if (vid->hAudio)
+ {
+ PLRStop (vid->hAudio);
+ if (vid->own_audio)
+ {
+ DestroyMusic (vid->hAudio);
+ vid->hAudio = 0;
+ vid->own_audio = false;
+ }
+ }
+ if (vid->frame)
+ {
+ TFB_DrawScreen_DeleteImage (vid->frame);
+ vid->frame = NULL;
+ }
+}
+
+bool
+TFB_VideoPlaying (VIDEO_REF vid)
+{
+ if (!vid)
+ return false;
+
+ return vid->playing;
+}
+
+bool
+TFB_ProcessVideoFrame (VIDEO_REF vid)
+{
+ if (!vid)
+ return false;
+
+ if (vid->decoder->audio_synced)
+ return processAudioSyncedFrame (vid);
+ else
+ return processMuteFrame (vid);
+}
+
+uint32
+TFB_GetVideoPosition (VIDEO_REF vid)
+{
+ uint32 pos;
+
+ if (!TFB_VideoPlaying (vid))
+ return 0;
+
+ LockMutex (vid->guard);
+ pos = (uint32) (vid->decoder->pos * 1000);
+ UnlockMutex (vid->guard);
+
+ return pos;
+}
+
+bool
+TFB_SeekVideo (VIDEO_REF vid, uint32 pos)
+{
+ if (!TFB_VideoPlaying (vid))
+ return false;
+
+ if (vid->decoder->audio_synced)
+ {
+ PLRSeek (vid->hAudio, pos);
+ TaskSwitch ();
+ return true;
+ }
+ else
+ { // TODO: Non-a/s decoder seeking is not supported yet
+ // Decide what to do with these. Seeking this kind of
+ // video is trivial, but we may not want to do it.
+ // The only non-a/s videos right now are ship spins.
+ return false;
+ }
+}
+
+static void
+vp_BeginFrame (TFB_VideoDecoder* decoder)
+{
+ TFB_VideoClip* vid = decoder->data;
+
+ if (vid)
+ TFB_DrawCanvas_Lock (vid->frame->NormalImg);
+}
+
+static void
+vp_EndFrame (TFB_VideoDecoder* decoder)
+{
+ TFB_VideoClip* vid = decoder->data;
+
+ if (vid)
+ TFB_DrawCanvas_Unlock (vid->frame->NormalImg);
+}
+
+static void*
+vp_GetCanvasLine (TFB_VideoDecoder* decoder, uint32 line)
+{
+ TFB_VideoClip* vid = decoder->data;
+
+ if (!vid)
+ return NULL;
+
+ return TFB_DrawCanvas_GetLine (vid->frame->NormalImg, line);
+}
+
+static uint32
+vp_GetTicks (TFB_VideoDecoder* decoder)
+{
+ uint32 ctr = GetTimeCounter ();
+ return (ctr / ONE_SECOND) * 1000 + ((ctr % ONE_SECOND) * 1000) / ONE_SECOND;
+
+ (void)decoder; // gobble up compiler warning
+}
+
+static bool
+vp_SetTimer (TFB_VideoDecoder* decoder, uint32 msecs)
+{
+ TFB_VideoClip* vid = decoder->data;
+
+ if (!vid)
+ return false;
+
+ // time when next frame should be displayed
+ vid->frame_time = GetTimeCounter () + msecs * ONE_SECOND / 1000;
+ return true;
+}
+
+static bool
+vp_AudioStart (TFB_SoundSample* sample)
+{
+ TFB_VideoClip* vid = TFB_GetSoundSampleData (sample);
+ TFB_SoundDecoder *decoder;
+
+ assert (sizeof (intptr_t) >= sizeof (vid));
+ assert (vid != NULL);
+
+ decoder = TFB_GetSoundSampleDecoder (sample);
+
+ LockMutex (vid->guard);
+ vid->want_frame = SoundDecoder_GetFrame (decoder);
+ UnlockMutex (vid->guard);
+
+ return true;
+}
+
+static void
+vp_AudioEnd (TFB_SoundSample* sample)
+{
+ TFB_VideoClip* vid = TFB_GetSoundSampleData (sample);
+
+ assert (vid != NULL);
+
+ LockMutex (vid->guard);
+ vid->want_frame = vid->decoder->frame_count; // end it
+ UnlockMutex (vid->guard);
+}
+
+static void
+vp_BufferTag (TFB_SoundSample* sample, TFB_SoundTag* tag)
+{
+ TFB_VideoClip* vid = TFB_GetSoundSampleData (sample);
+ uint32 frame = (uint32) tag->data;
+
+ assert (sizeof (tag->data) >= sizeof (frame));
+ assert (vid != NULL);
+
+ LockMutex (vid->guard);
+ vid->want_frame = frame; // let it go!
+ UnlockMutex (vid->guard);
+}
+
+static void
+vp_QueueBuffer (TFB_SoundSample* sample, audio_Object buffer)
+{
+ //TFB_VideoClip* vid = (TFB_VideoClip*) TFB_GetSoundSampleData (sample);
+ TFB_SoundDecoder *decoder = TFB_GetSoundSampleDecoder (sample);
+
+ TFB_TagBuffer (sample, buffer,
+ (intptr_t) SoundDecoder_GetFrame (decoder));
+}
+
diff --git a/src/libs/video/vidplayer.h b/src/libs/video/vidplayer.h
new file mode 100644
index 0000000..78e4edb
--- /dev/null
+++ b/src/libs/video/vidplayer.h
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+#ifndef LIBS_VIDEO_VIDPLAYER_H_
+#define LIBS_VIDEO_VIDPLAYER_H_
+
+#include "video.h"
+
+extern bool TFB_InitVideoPlayer (void);
+extern void TFB_UninitVideoPlayer (void);
+extern bool TFB_PlayVideo (VIDEO_REF VidRef, uint32 x, uint32 y);
+extern void TFB_StopVideo (VIDEO_REF VidRef);
+extern bool TFB_VideoPlaying (VIDEO_REF VidRef);
+extern bool TFB_ProcessVideoFrame (VIDEO_REF vid);
+extern uint32 TFB_GetVideoPosition (VIDEO_REF VidRef);
+extern bool TFB_SeekVideo (VIDEO_REF VidRef, uint32 pos);
+
+#endif // LIBS_VIDEO_VIDPLAYER_H_
diff --git a/src/libs/video/vresins.c b/src/libs/video/vresins.c
new file mode 100644
index 0000000..dbb18e0
--- /dev/null
+++ b/src/libs/video/vresins.c
@@ -0,0 +1,186 @@
+// Copyright 2008 Michael Martin
+
+/*
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "vidintrn.h"
+#include "libs/log.h"
+#include "libs/memlib.h"
+
+
+static BOOLEAN
+FreeLegacyVideoData (void *data)
+{
+ LEGACY_VIDEO pLV;
+ if (!data)
+ return FALSE;
+
+ pLV = (LEGACY_VIDEO) data;
+ if (pLV->video)
+ HFree (pLV->video);
+ if (pLV->audio)
+ HFree (pLV->audio);
+ if (pLV->speech)
+ HFree (pLV->speech);
+ HFree (pLV);
+
+ return TRUE;
+}
+
+static void
+GetLegacyVideoData (const char *path, RESOURCE_DATA *resdata)
+{
+ void *result = NULL;
+ char paths[1024], *audio_path, *speech_path, *loop_str;
+ uint32 LoopFrame = VID_NO_LOOP;
+
+ /* Parse out the video components. */
+ strncpy (paths, path, 1023);
+ paths[1023] = '\0';
+ audio_path = strchr (paths, ':');
+ if (audio_path == NULL)
+ {
+ speech_path = NULL;
+ loop_str = NULL;
+ }
+ else
+ {
+ *audio_path = '\0';
+ audio_path++;
+
+ speech_path = strchr (audio_path, ':');
+ if (speech_path == NULL)
+ {
+ loop_str = NULL;
+ }
+ else
+ {
+ *speech_path = '\0';
+ speech_path++;
+
+ loop_str = strchr (speech_path, ':');
+ if (loop_str != NULL) {
+ *loop_str = '\0';
+ loop_str++;
+ }
+ }
+ }
+
+ log_add (log_Info, "\t'%s' -- video", paths);
+ if (audio_path)
+ log_add (log_Info, "\t'%s' -- audio", audio_path);
+ else
+ log_add (log_Info, "\tNo associated audio");
+ if (speech_path)
+ log_add (log_Info, "\t'%s' -- speech path", speech_path);
+ else
+ log_add (log_Info, "\tNo associated speech");
+ if (loop_str)
+ {
+ char *end;
+ LoopFrame = strtol (loop_str, &end, 10);
+ // We allow whitespace at the end, but nothing printable.
+ if (*end > 32) {
+ log_add (log_Warning, "Warning: Unparsable loop frame '%s'. Disabling loop.", loop_str);
+ LoopFrame = VID_NO_LOOP;
+ }
+ log_add (log_Info, "\tLoop frame is %u", LoopFrame);
+ }
+ else
+ log_add (log_Info, "\tNo specified loop frame");
+
+ result = HMalloc (sizeof (LEGACY_VIDEO_DESC));
+ if (result)
+ {
+ LEGACY_VIDEO pLV = (LEGACY_VIDEO) result;
+ int len;
+ pLV->video = NULL;
+ pLV->audio = NULL;
+ pLV->speech = NULL;
+ pLV->loop = LoopFrame;
+
+ len = strlen(paths)+1;
+ pLV->video = (char *)HMalloc (len);
+ if (!pLV->video)
+ {
+ log_add (log_Warning, "Warning: Couldn't allocate space for '%s'", paths);
+ goto err;
+ }
+ strncpy(pLV->video, paths, len);
+
+ if (audio_path)
+ {
+ len = strlen(audio_path)+1;
+ pLV->audio = (char *)HMalloc (len);
+ if (!pLV->audio)
+ {
+ log_add (log_Warning, "Warning: Couldn't allocate space for '%s'", audio_path);
+ goto err;
+ }
+ strncpy(pLV->audio, audio_path, len);
+ }
+
+ if (speech_path)
+ {
+ len = strlen(speech_path)+1;
+ pLV->speech = (char *)HMalloc (len);
+ if (!pLV->speech)
+ {
+ log_add (log_Warning, "Warning: Couldn't allocate space for '%s'", speech_path);
+ goto err;
+ }
+ strncpy(pLV->speech, speech_path, len);
+ }
+
+ resdata->ptr = result;
+ }
+ return;
+err:
+ if (result)
+ FreeLegacyVideoData ((LEGACY_VIDEO)result);
+
+ resdata->ptr = NULL;
+ return;
+}
+
+BOOLEAN
+InstallVideoResType (void)
+{
+ InstallResTypeVectors ("3DOVID", GetLegacyVideoData, FreeLegacyVideoData, NULL);
+ return TRUE;
+}
+
+LEGACY_VIDEO
+LoadLegacyVideoInstance (RESOURCE res)
+{
+ void *data;
+
+ data = res_GetResource (res);
+ if (data)
+ {
+ res_DetachResource (res);
+ }
+
+ return (LEGACY_VIDEO)data;
+}
+
+BOOLEAN
+DestroyLegacyVideo (LEGACY_VIDEO vid)
+{
+ return FreeLegacyVideoData (vid);
+}