diff --git a/THIRD_PARTY.md b/THIRD_PARTY.md
index 0d973546..59d44fa9 100644
--- a/THIRD_PARTY.md
+++ b/THIRD_PARTY.md
@@ -28,6 +28,40 @@ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
```
+## libmve
+
+libmve - Interplay MVE implementation from D2X project. https://github.com/btb/d2x
+
+[@c030c453](https://github.com/btb/d2x/tree/c030c4531ad19f1658ea9635ff4ee6861e1d15e0)
+
+* libmve/decoder8.c
+* libmve/decoder16.c
+* libmve/decoders.h
+* libmve/mve_audio.c
+* libmve/mve_audio.h
+* libmve/mvelib.c
+* libmve/mvelib.h
+* libmve/mveplay.c
+
+The libmve code is licensed under GPL-3 license.
+
+```
+Copyright (C) 2002-2024 D2X Project
+
+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 3 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, see .
+```
+
## stb
Single-file public domain (or MIT licensed) libraries for C/C++. https://github.com/nothings/stb
diff --git a/libmve/CMakeLists.txt b/libmve/CMakeLists.txt
index 6c20a973..5def0543 100644
--- a/libmve/CMakeLists.txt
+++ b/libmve/CMakeLists.txt
@@ -1,16 +1,16 @@
-set(HEADERS
- mvegfx.h
- mvelibi.h
- mvelibl.h
- platform.h
- snd8to16.h
- SystemInterfaces.h)
-
set(CPPS
+ lnxdsound.cpp
mveasm.cpp
mvelibl.cpp
platform.cpp
- lnxdsound.cpp)
-add_library(libmve STATIC ${HEADERS} ${CPPS} ${PLATFORM_CPPS})
+ # d2x implementation
+ decoder8.c
+ decoder16.c
+ mve_audio.c
+ mvelib.c
+ mveplay.c
+)
+
+add_library(libmve STATIC ${CPPS})
target_link_libraries(libmve PRIVATE SDL2::SDL2)
diff --git a/libmve/decoder16.c b/libmve/decoder16.c
new file mode 100644
index 00000000..26ecb83d
--- /dev/null
+++ b/libmve/decoder16.c
@@ -0,0 +1,716 @@
+/* 16 bit decoding routines */
+
+#include
+#include
+#include
+
+#include "decoders.h"
+
+static unsigned short *backBuf1, *backBuf2;
+static int lookup_initialized;
+
+static void dispatchDecoder16(unsigned short **pFrame, unsigned char codeType, unsigned char **pData, unsigned char **pOffData, int *pDataRemain, int *curXb, int *curYb);
+static void genLoopkupTable(void);
+
+void decodeFrame16(unsigned char *pFrame, unsigned char *pMap, int mapRemain, unsigned char *pData, int dataRemain)
+{
+ unsigned char *pOrig;
+ unsigned char *pOffData, *pEnd;
+ unsigned short offset;
+ unsigned short *FramePtr = (unsigned short *)pFrame;
+ int length;
+ int op;
+ int i, j;
+ int xb, yb;
+
+ if (!lookup_initialized) {
+ genLoopkupTable();
+ }
+
+ backBuf1 = (unsigned short *)g_vBackBuf1;
+ backBuf2 = (unsigned short *)g_vBackBuf2;
+
+ xb = g_width >> 3;
+ yb = g_height >> 3;
+
+ offset = pData[0]|(pData[1]<<8);
+
+ pOffData = pData + offset;
+ pEnd = pData + offset;
+
+ pData += 2;
+
+ pOrig = pData;
+ length = offset - 2; /*dataRemain-2;*/
+
+ for (j=0; j= backBuf1 + g_width*g_height)
+ fprintf(stderr, "danger! pointing out of bounds above after dispatch decoder: %d, %d (1) [%x]\n", i, j, (*pMap) & 0xf);
+ */
+
+ op = ((*pMap) >> 4) & 0xf;
+ dispatchDecoder16(&FramePtr, op, &pData, &pOffData, &dataRemain, &i, &j);
+
+ /*
+ if (FramePtr < backBuf1)
+ fprintf(stderr, "danger! pointing out of bounds below after dispatch decoder: %d, %d (2) [%x]\n", i, j, (*pMap) >> 4);
+ else if (FramePtr >= backBuf1 + g_width*g_height)
+ fprintf(stderr, "danger! pointing out of bounds above after dispatch decoder: %d, %d (2) [%x]\n", i, j, (*pMap) >> 4);
+ */
+
+ ++pMap;
+ --mapRemain;
+ }
+
+ FramePtr += 7*g_width;
+ }
+
+ if ( (length - (pData - pOrig)) != 0 )
+ fprintf(stderr, "DEBUG: junk left over: %d,%d,%d\n", (int)(pData-pOrig), length, (int)(length-(pData-pOrig)));
+}
+
+static unsigned short GETPIXEL(unsigned char **buf, int off)
+{
+ unsigned short val = (*buf)[0+off] | ((*buf)[1+off] << 8);
+ return val;
+}
+
+static unsigned short GETPIXELI(unsigned char **buf, int off)
+{
+ unsigned short val = (*buf)[0+off] | ((*buf)[1+off] << 8);
+ (*buf) += 2;
+ return val;
+}
+
+static void relClose(int i, int *x, int *y)
+{
+ int ma, mi;
+
+ ma = i >> 4;
+ mi = i & 0xf;
+
+ *x = mi - 8;
+ *y = ma - 8;
+}
+
+static void relFar(int i, int sign, int *x, int *y)
+{
+ if (i < 56)
+ {
+ *x = sign * (8 + (i % 7));
+ *y = sign * (i / 7);
+ }
+ else
+ {
+ *x = sign * (-14 + (i - 56) % 29);
+ *y = sign * (8 + (i - 56) / 29);
+ }
+}
+
+static int close_table[512];
+static int far_p_table[512];
+static int far_n_table[512];
+
+static void genLoopkupTable()
+{
+ int i;
+ int x, y;
+
+ for (i = 0; i < 256; i++) {
+ relClose(i, &x, &y);
+
+ close_table[i*2+0] = x;
+ close_table[i*2+1] = y;
+
+ relFar(i, 1, &x, &y);
+
+ far_p_table[i*2+0] = x;
+ far_p_table[i*2+1] = y;
+
+ relFar(i, -1, &x, &y);
+
+ far_n_table[i*2+0] = x;
+ far_n_table[i*2+1] = y;
+ }
+
+ lookup_initialized = 1;
+}
+
+static void copyFrame(unsigned short *pDest, unsigned short *pSrc)
+{
+ int i;
+
+ for (i=0; i<8; i++)
+ {
+ memcpy(pDest, pSrc, 16);
+ pDest += g_width;
+ pSrc += g_width;
+ }
+}
+
+static void patternRow4Pixels(unsigned short *pFrame,
+ unsigned char pat0, unsigned char pat1,
+ unsigned short *p)
+{
+ unsigned short mask=0x0003;
+ unsigned short shift=0;
+ unsigned short pattern = (pat1 << 8) | pat0;
+
+ while (mask != 0)
+ {
+ *pFrame++ = p[(mask & pattern) >> shift];
+ mask <<= 2;
+ shift += 2;
+ }
+}
+
+static void patternRow4Pixels2(unsigned short *pFrame,
+ unsigned char pat0,
+ unsigned short *p)
+{
+ unsigned char mask=0x03;
+ unsigned char shift=0;
+ unsigned short pel;
+ /* ORIGINAL VERSION IS BUGGY
+ int skip=1;
+
+ while (mask != 0)
+ {
+ pel = p[(mask & pat0) >> shift];
+ pFrame[0] = pel;
+ pFrame[2] = pel;
+ pFrame[g_width + 0] = pel;
+ pFrame[g_width + 2] = pel;
+ pFrame += skip;
+ skip = 4 - skip;
+ mask <<= 2;
+ shift += 2;
+ }
+ */
+ while (mask != 0)
+ {
+ pel = p[(mask & pat0) >> shift];
+ pFrame[0] = pel;
+ pFrame[1] = pel;
+ pFrame[g_width + 0] = pel;
+ pFrame[g_width + 1] = pel;
+ pFrame += 2;
+ mask <<= 2;
+ shift += 2;
+ }
+}
+
+static void patternRow4Pixels2x1(unsigned short *pFrame, unsigned char pat,
+ unsigned short *p)
+{
+ unsigned char mask=0x03;
+ unsigned char shift=0;
+ unsigned short pel;
+
+ while (mask != 0)
+ {
+ pel = p[(mask & pat) >> shift];
+ pFrame[0] = pel;
+ pFrame[1] = pel;
+ pFrame += 2;
+ mask <<= 2;
+ shift += 2;
+ }
+}
+
+static void patternQuadrant4Pixels(unsigned short *pFrame,
+ unsigned char pat0, unsigned char pat1, unsigned char pat2,
+ unsigned char pat3, unsigned short *p)
+{
+ unsigned long mask = 0x00000003UL;
+ int shift=0;
+ int i;
+ unsigned long pat = (pat3 << 24) | (pat2 << 16) | (pat1 << 8) | pat0;
+
+ for (i=0; i<16; i++)
+ {
+ pFrame[i&3] = p[(pat & mask) >> shift];
+
+ if ((i&3) == 3)
+ pFrame += g_width;
+
+ mask <<= 2;
+ shift += 2;
+ }
+}
+
+
+static void patternRow2Pixels(unsigned short *pFrame, unsigned char pat,
+ unsigned short *p)
+{
+ unsigned char mask=0x01;
+
+ while (mask != 0)
+ {
+ *pFrame++ = p[(mask & pat) ? 1 : 0];
+ mask <<= 1;
+ }
+}
+
+static void patternRow2Pixels2(unsigned short *pFrame, unsigned char pat,
+ unsigned short *p)
+{
+ unsigned short pel;
+ unsigned char mask=0x1;
+
+ /* ORIGINAL VERSION IS BUGGY
+ int skip=1;
+ while (mask != 0x10)
+ {
+ pel = p[(mask & pat) ? 1 : 0];
+ pFrame[0] = pel;
+ pFrame[2] = pel;
+ pFrame[g_width + 0] = pel;
+ pFrame[g_width + 2] = pel;
+ pFrame += skip;
+ skip = 4 - skip;
+ mask <<= 1;
+ }
+ */
+ while (mask != 0x10) {
+ pel = p[(mask & pat) ? 1 : 0];
+
+ pFrame[0] = pel;
+ pFrame[1] = pel;
+ pFrame[g_width + 0] = pel;
+ pFrame[g_width + 1] = pel;
+ pFrame += 2;
+
+ mask <<= 1;
+ }
+}
+
+static void patternQuadrant2Pixels(unsigned short *pFrame, unsigned char pat0,
+ unsigned char pat1, unsigned short *p)
+{
+ unsigned short mask = 0x0001;
+ int i;
+ unsigned short pat = (pat1 << 8) | pat0;
+
+ for (i=0; i<16; i++)
+ {
+ pFrame[i&3] = p[(pat & mask) ? 1 : 0];
+
+ if ((i&3) == 3)
+ pFrame += g_width;
+
+ mask <<= 1;
+ }
+}
+
+static void dispatchDecoder16(unsigned short **pFrame, unsigned char codeType, unsigned char **pData, unsigned char **pOffData, int *pDataRemain, int *curXb, int *curYb)
+{
+ unsigned short p[4];
+ unsigned char pat[16];
+ int i, j, k;
+ int x, y;
+ unsigned short *pDstBak;
+
+ pDstBak = *pFrame;
+
+ switch(codeType)
+ {
+ case 0x0:
+ copyFrame(*pFrame, *pFrame + (backBuf2 - backBuf1));
+ case 0x1:
+ break;
+ case 0x2: /*
+ relFar(*(*pOffData)++, 1, &x, &y);
+ */
+
+ k = *(*pOffData)++;
+ x = far_p_table[k*2+0];
+ y = far_p_table[k*2+1];
+
+ copyFrame(*pFrame, *pFrame + x + y*g_width);
+ --*pDataRemain;
+ break;
+ case 0x3: /*
+ relFar(*(*pOffData)++, -1, &x, &y);
+ */
+
+ k = *(*pOffData)++;
+ x = far_n_table[k*2+0];
+ y = far_n_table[k*2+1];
+
+ copyFrame(*pFrame, *pFrame + x + y*g_width);
+ --*pDataRemain;
+ break;
+ case 0x4: /*
+ relClose(*(*pOffData)++, &x, &y);
+ */
+
+ k = *(*pOffData)++;
+ x = close_table[k*2+0];
+ y = close_table[k*2+1];
+
+ copyFrame(*pFrame, *pFrame + (backBuf2 - backBuf1) + x + y*g_width);
+ --*pDataRemain;
+ break;
+ case 0x5:
+ x = (char)*(*pData)++;
+ y = (char)*(*pData)++;
+ copyFrame(*pFrame, *pFrame + (backBuf2 - backBuf1) + x + y*g_width);
+ *pDataRemain -= 2;
+ break;
+ case 0x6:
+ fprintf(stderr, "STUB: encoding 6 not tested\n");
+ for (i=0; i<2; i++)
+ {
+ *pFrame += 16;
+ if (++*curXb == (g_width >> 3))
+ {
+ *pFrame += 7*g_width;
+ *curXb = 0;
+ if (++*curYb == (g_height >> 3))
+ return;
+ }
+ }
+ break;
+
+ case 0x7:
+ p[0] = GETPIXELI(pData, 0);
+ p[1] = GETPIXELI(pData, 0);
+
+ if (!((p[0]/*|p[1]*/)&0x8000))
+ {
+ for (i=0; i<8; i++)
+ {
+ patternRow2Pixels(*pFrame, *(*pData), p);
+ (*pData)++;
+
+ *pFrame += g_width;
+ }
+ }
+ else
+ {
+ for (i=0; i<2; i++)
+ {
+ patternRow2Pixels2(*pFrame, *(*pData) & 0xf, p);
+ *pFrame += 2*g_width;
+ patternRow2Pixels2(*pFrame, *(*pData) >> 4, p);
+ (*pData)++;
+
+ *pFrame += 2*g_width;
+ }
+ }
+ break;
+
+ case 0x8:
+ p[0] = GETPIXEL(pData, 0);
+
+ if (!(p[0] & 0x8000))
+ {
+ for (i=0; i<4; i++)
+ {
+ p[0] = GETPIXELI(pData, 0);
+ p[1] = GETPIXELI(pData, 0);
+
+ pat[0] = (*pData)[0];
+ pat[1] = (*pData)[1];
+ (*pData) += 2;
+
+ patternQuadrant2Pixels(*pFrame, pat[0], pat[1], p);
+
+ if (i & 1)
+ *pFrame -= (4*g_width - 4);
+ else
+ *pFrame += 4*g_width;
+ }
+
+
+ } else {
+ p[2] = GETPIXEL(pData, 8);
+
+ if (!(p[2]&0x8000)) {
+ for (i=0; i<4; i++)
+ {
+ if ((i & 1) == 0)
+ {
+ p[0] = GETPIXELI(pData, 0);
+ p[1] = GETPIXELI(pData, 0);
+ }
+ pat[0] = *(*pData)++;
+ pat[1] = *(*pData)++;
+ patternQuadrant2Pixels(*pFrame, pat[0], pat[1], p);
+
+ if (i & 1)
+ *pFrame -= (4*g_width - 4);
+ else
+ *pFrame += 4*g_width;
+ }
+ } else {
+ for (i=0; i<8; i++)
+ {
+ if ((i & 3) == 0)
+ {
+ p[0] = GETPIXELI(pData, 0);
+ p[1] = GETPIXELI(pData, 0);
+ }
+ patternRow2Pixels(*pFrame, *(*pData), p);
+ (*pData)++;
+
+ *pFrame += g_width;
+ }
+ }
+ }
+ break;
+
+ case 0x9:
+ p[0] = GETPIXELI(pData, 0);
+ p[1] = GETPIXELI(pData, 0);
+ p[2] = GETPIXELI(pData, 0);
+ p[3] = GETPIXELI(pData, 0);
+
+ *pDataRemain -= 8;
+
+ if (!(p[0] & 0x8000))
+ {
+ if (!(p[2] & 0x8000))
+ {
+
+ for (i=0; i<8; i++)
+ {
+ pat[0] = (*pData)[0];
+ pat[1] = (*pData)[1];
+ (*pData) += 2;
+ patternRow4Pixels(*pFrame, pat[0], pat[1], p);
+ *pFrame += g_width;
+ }
+ *pDataRemain -= 16;
+
+ }
+ else
+ {
+ patternRow4Pixels2(*pFrame, (*pData)[0], p);
+ *pFrame += 2*g_width;
+ patternRow4Pixels2(*pFrame, (*pData)[1], p);
+ *pFrame += 2*g_width;
+ patternRow4Pixels2(*pFrame, (*pData)[2], p);
+ *pFrame += 2*g_width;
+ patternRow4Pixels2(*pFrame, (*pData)[3], p);
+
+ (*pData) += 4;
+ *pDataRemain -= 4;
+
+ }
+ }
+ else
+ {
+ if (!(p[2] & 0x8000))
+ {
+ for (i=0; i<8; i++)
+ {
+ pat[0] = (*pData)[0];
+ (*pData) += 1;
+ patternRow4Pixels2x1(*pFrame, pat[0], p);
+ *pFrame += g_width;
+ }
+ *pDataRemain -= 8;
+ }
+ else
+ {
+ for (i=0; i<4; i++)
+ {
+ pat[0] = (*pData)[0];
+ pat[1] = (*pData)[1];
+
+ (*pData) += 2;
+
+ patternRow4Pixels(*pFrame, pat[0], pat[1], p);
+ *pFrame += g_width;
+ patternRow4Pixels(*pFrame, pat[0], pat[1], p);
+ *pFrame += g_width;
+ }
+ *pDataRemain -= 8;
+ }
+ }
+ break;
+
+ case 0xa:
+ p[0] = GETPIXEL(pData, 0);
+
+ if (!(p[0] & 0x8000))
+ {
+ for (i=0; i<4; i++)
+ {
+ p[0] = GETPIXELI(pData, 0);
+ p[1] = GETPIXELI(pData, 0);
+ p[2] = GETPIXELI(pData, 0);
+ p[3] = GETPIXELI(pData, 0);
+ pat[0] = (*pData)[0];
+ pat[1] = (*pData)[1];
+ pat[2] = (*pData)[2];
+ pat[3] = (*pData)[3];
+
+ (*pData) += 4;
+
+ patternQuadrant4Pixels(*pFrame, pat[0], pat[1], pat[2], pat[3], p);
+
+ if (i & 1)
+ *pFrame -= (4*g_width - 4);
+ else
+ *pFrame += 4*g_width;
+ }
+ }
+ else
+ {
+ p[0] = GETPIXEL(pData, 16);
+
+ if (!(p[0] & 0x8000))
+ {
+ for (i=0; i<4; i++)
+ {
+ if ((i&1) == 0)
+ {
+ p[0] = GETPIXELI(pData, 0);
+ p[1] = GETPIXELI(pData, 0);
+ p[2] = GETPIXELI(pData, 0);
+ p[3] = GETPIXELI(pData, 0);
+ }
+
+ pat[0] = (*pData)[0];
+ pat[1] = (*pData)[1];
+ pat[2] = (*pData)[2];
+ pat[3] = (*pData)[3];
+
+ (*pData) += 4;
+
+ patternQuadrant4Pixels(*pFrame, pat[0], pat[1], pat[2], pat[3], p);
+
+ if (i & 1)
+ *pFrame -= (4*g_width - 4);
+ else
+ *pFrame += 4*g_width;
+ }
+ }
+ else
+ {
+ for (i=0; i<8; i++)
+ {
+ if ((i&3) == 0)
+ {
+ p[0] = GETPIXELI(pData, 0);
+ p[1] = GETPIXELI(pData, 0);
+ p[2] = GETPIXELI(pData, 0);
+ p[3] = GETPIXELI(pData, 0);
+ }
+
+ pat[0] = (*pData)[0];
+ pat[1] = (*pData)[1];
+ patternRow4Pixels(*pFrame, pat[0], pat[1], p);
+ *pFrame += g_width;
+
+ (*pData) += 2;
+ }
+ }
+ }
+ break;
+
+ case 0xb:
+ for (i=0; i<8; i++)
+ {
+ memcpy(*pFrame, *pData, 16);
+ *pFrame += g_width;
+ *pData += 16;
+ *pDataRemain -= 16;
+ }
+ break;
+
+ case 0xc:
+ for (i=0; i<4; i++)
+ {
+ p[0] = GETPIXEL(pData, 0);
+ p[1] = GETPIXEL(pData, 2);
+ p[2] = GETPIXEL(pData, 4);
+ p[3] = GETPIXEL(pData, 6);
+
+ for (j=0; j<2; j++)
+ {
+ for (k=0; k<4; k++)
+ {
+ (*pFrame)[j+2*k] = p[k];
+ (*pFrame)[g_width+j+2*k] = p[k];
+ }
+ *pFrame += g_width;
+ }
+ *pData += 8;
+ *pDataRemain -= 8;
+ }
+ break;
+
+ case 0xd:
+ for (i=0; i<2; i++)
+ {
+ p[0] = GETPIXEL(pData, 0);
+ p[1] = GETPIXEL(pData, 2);
+
+ for (j=0; j<4; j++)
+ {
+ for (k=0; k<4; k++)
+ {
+ (*pFrame)[k*g_width+j] = p[0];
+ (*pFrame)[k*g_width+j+4] = p[1];
+ }
+ }
+
+ *pFrame += 4*g_width;
+
+ *pData += 4;
+ *pDataRemain -= 4;
+ }
+ break;
+
+ case 0xe:
+ p[0] = GETPIXEL(pData, 0);
+
+ for (i = 0; i < 8; i++) {
+ for (j = 0; j < 8; j++) {
+ (*pFrame)[j] = p[0];
+ }
+
+ *pFrame += g_width;
+ }
+
+ *pData += 2;
+ *pDataRemain -= 2;
+
+ break;
+
+ case 0xf:
+ p[0] = GETPIXEL(pData, 0);
+ p[1] = GETPIXEL(pData, 1);
+
+ for (i=0; i<8; i++)
+ {
+ for (j=0; j<8; j++)
+ {
+ (*pFrame)[j] = p[(i+j)&1];
+ }
+ *pFrame += g_width;
+ }
+
+ *pData += 4;
+ *pDataRemain -= 4;
+ break;
+
+ default:
+ break;
+ }
+
+ *pFrame = pDstBak+8;
+}
diff --git a/libmve/decoder8.c b/libmve/decoder8.c
new file mode 100644
index 00000000..a676e00b
--- /dev/null
+++ b/libmve/decoder8.c
@@ -0,0 +1,871 @@
+/* 8 bit decoding routines */
+
+#include
+#include
+
+#include "decoders.h"
+
+static void dispatchDecoder(unsigned char **pFrame, unsigned char codeType, unsigned char **pData, int *pDataRemain, int *curXb, int *curYb);
+
+void decodeFrame8(unsigned char *pFrame, unsigned char *pMap, int mapRemain, unsigned char *pData, int dataRemain)
+{
+ int i, j;
+ int xb, yb;
+
+ xb = g_width >> 3;
+ yb = g_height >> 3;
+ for (j=0; j= ((unsigned char *)g_vBackBuf1) + g_width*g_height)
+ fprintf(stderr, "danger! pointing out of bounds above after dispatch decoder: %d, %d (1) [%x]\n", i, j, (*pMap) & 0xf);
+ dispatchDecoder(&pFrame, (*pMap) >> 4, &pData, &dataRemain, &i, &j);
+ if (pFrame < (unsigned char *)g_vBackBuf1)
+ fprintf(stderr, "danger! pointing out of bounds below after dispatch decoder: %d, %d (2) [%x]\n", i, j, (*pMap) >> 4);
+ else if (pFrame >= ((unsigned char *)g_vBackBuf1) + g_width*g_height)
+ fprintf(stderr, "danger! pointing out of bounds above after dispatch decoder: %d, %d (2) [%x]\n", i, j, (*pMap) >> 4);
+
+ ++pMap;
+ --mapRemain;
+ }
+
+ pFrame += 7*g_width;
+ }
+}
+
+static void relClose(int i, int *x, int *y)
+{
+ int ma, mi;
+
+ ma = i >> 4;
+ mi = i & 0xf;
+
+ *x = mi - 8;
+ *y = ma - 8;
+}
+
+static void relFar(int i, int sign, int *x, int *y)
+{
+ if (i < 56)
+ {
+ *x = sign * (8 + (i % 7));
+ *y = sign * (i / 7);
+ }
+ else
+ {
+ *x = sign * (-14 + (i - 56) % 29);
+ *y = sign * (8 + (i - 56) / 29);
+ }
+}
+
+/* copies an 8x8 block from pSrc to pDest.
+ pDest and pSrc are both g_width bytes wide */
+static void copyFrame(unsigned char *pDest, unsigned char *pSrc)
+{
+ int i;
+
+ for (i=0; i<8; i++)
+ {
+ memcpy(pDest, pSrc, 8);
+ pDest += g_width;
+ pSrc += g_width;
+ }
+}
+
+// Fill in the next eight bytes with p[0], p[1], p[2], or p[3],
+// depending on the corresponding two-bit value in pat0 and pat1
+static void patternRow4Pixels(unsigned char *pFrame,
+ unsigned char pat0, unsigned char pat1,
+ unsigned char *p)
+{
+ unsigned short mask=0x0003;
+ unsigned short shift=0;
+ unsigned short pattern = (pat1 << 8) | pat0;
+
+ while (mask != 0)
+ {
+ *pFrame++ = p[(mask & pattern) >> shift];
+ mask <<= 2;
+ shift += 2;
+ }
+}
+
+// Fill in the next four 2x2 pixel blocks with p[0], p[1], p[2], or p[3],
+// depending on the corresponding two-bit value in pat0.
+static void patternRow4Pixels2(unsigned char *pFrame,
+ unsigned char pat0,
+ unsigned char *p)
+{
+ unsigned char mask=0x03;
+ unsigned char shift=0;
+ unsigned char pel;
+
+ while (mask != 0)
+ {
+ pel = p[(mask & pat0) >> shift];
+ pFrame[0] = pel;
+ pFrame[1] = pel;
+ pFrame[g_width + 0] = pel;
+ pFrame[g_width + 1] = pel;
+ pFrame += 2;
+ mask <<= 2;
+ shift += 2;
+ }
+}
+
+// Fill in the next four 2x1 pixel blocks with p[0], p[1], p[2], or p[3],
+// depending on the corresponding two-bit value in pat.
+static void patternRow4Pixels2x1(unsigned char *pFrame, unsigned char pat, unsigned char *p)
+{
+ unsigned char mask=0x03;
+ unsigned char shift=0;
+ unsigned char pel;
+
+ while (mask != 0)
+ {
+ pel = p[(mask & pat) >> shift];
+ pFrame[0] = pel;
+ pFrame[1] = pel;
+ pFrame += 2;
+ mask <<= 2;
+ shift += 2;
+ }
+}
+
+// Fill in the next 4x4 pixel block with p[0], p[1], p[2], or p[3],
+// depending on the corresponding two-bit value in pat0, pat1, pat2, and pat3.
+static void patternQuadrant4Pixels(unsigned char *pFrame, unsigned char pat0, unsigned char pat1, unsigned char pat2, unsigned char pat3, unsigned char *p)
+{
+ unsigned long mask = 0x00000003UL;
+ int shift=0;
+ int i;
+ unsigned long pat = (pat3 << 24) | (pat2 << 16) | (pat1 << 8) | pat0;
+
+ for (i=0; i<16; i++)
+ {
+ pFrame[i&3] = p[(pat & mask) >> shift];
+
+ if ((i&3) == 3)
+ pFrame += g_width;
+
+ mask <<= 2;
+ shift += 2;
+ }
+}
+
+// fills the next 8 pixels with either p[0] or p[1], depending on pattern
+static void patternRow2Pixels(unsigned char *pFrame, unsigned char pat, unsigned char *p)
+{
+ unsigned char mask=0x01;
+
+ while (mask != 0)
+ {
+ *pFrame++ = p[(mask & pat) ? 1 : 0];
+ mask <<= 1;
+ }
+}
+
+// fills the next four 2 x 2 pixel boxes with either p[0] or p[1], depending on pattern
+static void patternRow2Pixels2(unsigned char *pFrame, unsigned char pat, unsigned char *p)
+{
+ unsigned char pel;
+ unsigned char mask=0x1;
+
+ while (mask != 0x10)
+ {
+ pel = p[(mask & pat) ? 1 : 0];
+
+ pFrame[0] = pel; // upper-left
+ pFrame[1] = pel; // upper-right
+ pFrame[g_width + 0] = pel; // lower-left
+ pFrame[g_width + 1] = pel; // lower-right
+ pFrame += 2;
+
+ mask <<= 1;
+ }
+}
+
+// fills pixels in the next 4 x 4 pixel boxes with either p[0] or p[1], depending on pat0 and pat1.
+static void patternQuadrant2Pixels(unsigned char *pFrame, unsigned char pat0, unsigned char pat1, unsigned char *p)
+{
+ unsigned char pel;
+ unsigned short mask = 0x0001;
+ int i, j;
+ unsigned short pat = (pat1 << 8) | pat0;
+
+ for (i=0; i<4; i++)
+ {
+ for (j=0; j<4; j++)
+ {
+ pel = p[(pat & mask) ? 1 : 0];
+
+ pFrame[j + i * g_width] = pel;
+
+ mask <<= 1;
+ }
+ }
+}
+
+static void dispatchDecoder(unsigned char **pFrame, unsigned char codeType, unsigned char **pData, int *pDataRemain, int *curXb, int *curYb)
+{
+ unsigned char p[4];
+ unsigned char pat[16];
+ int i, j, k;
+ int x, y;
+
+ /* Data is processed in 8x8 pixel blocks.
+ There are 16 ways to encode each block.
+ */
+
+ switch(codeType)
+ {
+ case 0x0:
+ /* block is copied from block in current frame */
+ copyFrame(*pFrame, *pFrame + ((unsigned char *)g_vBackBuf2 - (unsigned char *)g_vBackBuf1));
+ case 0x1:
+ /* block is unchanged from two frames ago */
+ *pFrame += 8;
+ break;
+
+ case 0x2:
+ /* Block is copied from nearby (below and/or to the right) within the
+ new frame. The offset within the buffer from which to grab the
+ patch of 8 pixels is given by grabbing a byte B from the data
+ stream, which is broken into a positive x and y offset according
+ to the following mapping:
+
+ if B < 56:
+ x = 8 + (B % 7)
+ y = B / 7
+ else
+ x = -14 + ((B - 56) % 29)
+ y = 8 + ((B - 56) / 29)
+ */
+ relFar(*(*pData)++, 1, &x, &y);
+ copyFrame(*pFrame, *pFrame + x + y*g_width);
+ *pFrame += 8;
+ --*pDataRemain;
+ break;
+
+ case 0x3:
+ /* Block is copied from nearby (above and/or to the left) within the
+ new frame.
+
+ if B < 56:
+ x = -(8 + (B % 7))
+ y = -(B / 7)
+ else
+ x = -(-14 + ((B - 56) % 29))
+ y = -( 8 + ((B - 56) / 29))
+ */
+ relFar(*(*pData)++, -1, &x, &y);
+ copyFrame(*pFrame, *pFrame + x + y*g_width);
+ *pFrame += 8;
+ --*pDataRemain;
+ break;
+
+ case 0x4:
+ /* Similar to 0x2 and 0x3, except this method copies from the
+ "current" frame, rather than the "new" frame, and instead of the
+ lopsided mapping they use, this one uses one which is symmetric
+ and centered around the top-left corner of the block. This uses
+ only 1 byte still, though, so the range is decreased, since we
+ have to encode all directions in a single byte. The byte we pull
+ from the data stream, I'll call B. Call the highest 4 bits of B
+ BH and the lowest 4 bytes BL. Then the offset from which to copy
+ the data is:
+
+ x = -8 + BL
+ y = -8 + BH
+ */
+ relClose(*(*pData)++, &x, &y);
+ copyFrame(*pFrame, *pFrame + ((unsigned char *)g_vBackBuf2 - (unsigned char *)g_vBackBuf1) + x + y*g_width);
+ *pFrame += 8;
+ --*pDataRemain;
+ break;
+
+ case 0x5:
+ /* Similar to 0x4, but instead of one byte for the offset, this uses
+ two bytes to encode a larger range, the first being the x offset
+ as a signed 8-bit value, and the second being the y offset as a
+ signed 8-bit value.
+ */
+ x = (signed char)*(*pData)++;
+ y = (signed char)*(*pData)++;
+ copyFrame(*pFrame, *pFrame + ((unsigned char *)g_vBackBuf2 - (unsigned char *)g_vBackBuf1) + x + y*g_width);
+ *pFrame += 8;
+ *pDataRemain -= 2;
+ break;
+
+ case 0x6:
+ /* I can't figure out how any file containing a block of this type
+ could still be playable, since it appears that it would leave the
+ internal bookkeeping in an inconsistent state in the BG player
+ code. Ahh, well. Perhaps it was a bug in the BG player code that
+ just didn't happen to be exposed by any of the included movies.
+ Anyway, this skips the next two blocks, doing nothing to them.
+ Note that if you've reached the end of a row, this means going on
+ to the next row.
+ */
+ for (i=0; i<2; i++)
+ {
+ *pFrame += 16;
+ if (++*curXb == (g_width >> 3))
+ {
+ *pFrame += 7*g_width;
+ *curXb = 0;
+ if (++*curYb == (g_height >> 3))
+ return;
+ }
+ }
+ break;
+
+ case 0x7:
+ /* Ok, here's where it starts to get really...interesting. This is,
+ incidentally, the part where they started using self-modifying
+ code. So, most of the following encodings are "patterned" blocks,
+ where we are given a number of pixel values and then bitmapped
+ values to specify which pixel values belong to which squares. For
+ this encoding, we are given the following in the data stream:
+
+ P0 P1
+
+ These are pixel values (i.e. 8-bit indices into the palette). If
+ P0 <= P1, we then get 8 more bytes from the data stream, one for
+ each row in the block:
+
+ B0 B1 B2 B3 B4 B5 B6 B7
+
+ For each row, the leftmost pixel is represented by the low-order
+ bit, and the rightmost by the high-order bit. Use your imagination
+ in between. If a bit is set, the pixel value is P1 and if it is
+ unset, the pixel value is P0.
+
+ So, for example, if we had:
+
+ 11 22 fe 83 83 83 83 83 83 fe
+
+ This would represent the following layout:
+
+ 11 22 22 22 22 22 22 22 ; fe == 11111110
+ 22 22 11 11 11 11 11 22 ; 83 == 10000011
+ 22 22 11 11 11 11 11 22 ; 83 == 10000011
+ 22 22 11 11 11 11 11 22 ; 83 == 10000011
+ 22 22 11 11 11 11 11 22 ; 83 == 10000011
+ 22 22 11 11 11 11 11 22 ; 83 == 10000011
+ 22 22 11 11 11 11 11 22 ; 83 == 10000011
+ 11 22 22 22 22 22 22 22 ; fe == 11111110
+
+ If, on the other hand, P0 > P1, we get two more bytes from the
+ data stream:
+
+ B0 B1
+
+ Each of these bytes contains two 4-bit patterns. These patterns
+ work like the patterns above with 8 bytes, except each bit
+ represents a 2x2 pixel region.
+
+ B0 contains the pattern for the top two rows and B1 contains
+ the pattern for the bottom two rows. Note that the low-order
+ nibble of each byte contains the pattern for the upper of the
+ two rows that that byte controls.
+
+ So if we had:
+
+ 22 11 7e 83
+
+ The output would be:
+
+ 11 11 22 22 22 22 22 22 ; e == 1 1 1 0
+ 11 11 22 22 22 22 22 22 ;
+ 22 22 22 22 22 22 11 11 ; 7 == 0 1 1 1
+ 22 22 22 22 22 22 11 11 ;
+ 11 11 11 11 11 11 22 22 ; 3 == 1 0 0 0
+ 11 11 11 11 11 11 22 22 ;
+ 22 22 22 22 11 11 11 11 ; 8 == 0 0 1 1
+ 22 22 22 22 11 11 11 11 ;
+ */
+ p[0] = *(*pData)++;
+ p[1] = *(*pData)++;
+ if (p[0] <= p[1])
+ {
+ for (i=0; i<8; i++)
+ {
+ patternRow2Pixels(*pFrame, *(*pData)++, p);
+ *pFrame += g_width;
+ }
+ }
+ else
+ {
+ for (i=0; i<2; i++)
+ {
+ patternRow2Pixels2(*pFrame, *(*pData) & 0xf, p);
+ *pFrame += 2*g_width;
+ patternRow2Pixels2(*pFrame, *(*pData)++ >> 4, p);
+ *pFrame += 2*g_width;
+ }
+ }
+ *pFrame -= (8*g_width - 8);
+ break;
+
+ case 0x8:
+ /* Ok, this one is basically like encoding 0x7, only more
+ complicated. Again, we start out by getting two bytes on the data
+ stream:
+
+ P0 P1
+
+ if P0 <= P1 then we get the following from the data stream:
+
+ B0 B1
+ P2 P3 B2 B3
+ P4 P5 B4 B5
+ P6 P7 B6 B7
+
+ P0 P1 and B0 B1 are used for the top-left corner, P2 P3 B2 B3 for
+ the bottom-left corner, P4 P5 B4 B5 for the top-right, P6 P7 B6 B7
+ for the bottom-right. (So, each codes for a 4x4 pixel array.)
+ Since we have 16 bits in B0 B1, there is one bit for each pixel in
+ the array. The convention for the bit-mapping is, again, left to
+ right and top to bottom.
+
+ So, basically, the top-left quarter of the block is an arbitrary
+ pattern with 2 pixels, the bottom-left a different arbitrary
+ pattern with 2 different pixels, and so on.
+
+ For example if the next 16 bytes were:
+
+ 00 22 f9 9f 44 55 aa 55 11 33 cc 33 66 77 01 ef
+
+ We'd draw:
+
+ 22 22 22 22 | 11 11 33 33 ; f = 1111, c = 1100
+ 22 00 00 22 | 11 11 33 33 ; 9 = 1001, c = 1100
+ 22 00 00 22 | 33 33 11 11 ; 9 = 1001, 3 = 0011
+ 22 22 22 22 | 33 33 11 11 ; f = 1111, 3 = 0011
+ ------------+------------
+ 44 55 44 55 | 66 66 66 66 ; a = 1010, 0 = 0000
+ 44 55 44 55 | 77 66 66 66 ; a = 1010, 1 = 0001
+ 55 44 55 44 | 66 77 77 77 ; 5 = 0101, e = 1110
+ 55 44 55 44 | 77 77 77 77 ; 5 = 0101, f = 1111
+
+ I've added a dividing line in the above to clearly delineate the
+ quadrants.
+
+
+ Now, if P0 > P1 then we get 10 more bytes from the data stream:
+
+ B0 B1 B2 B3 P2 P3 B4 B5 B6 B7
+
+ Now, if P2 <= P3, then the first six bytes [P0 P1 B0 B1 B2 B3]
+ represent the left half of the block and the latter six bytes
+ [P2 P3 B4 B5 B6 B7] represent the right half.
+
+ For example:
+
+ 22 00 01 37 f7 31 11 66 8c e6 73 31
+
+ yeilds:
+
+ 22 22 22 22 | 11 11 11 66 ; 0: 0000 | 8: 1000
+ 00 22 22 22 | 11 11 66 66 ; 1: 0001 | C: 1100
+ 00 00 22 22 | 11 66 66 66 ; 3: 0011 | e: 1110
+ 00 00 00 22 | 11 66 11 66 ; 7: 0111 | 6: 0101
+ 00 00 00 00 | 66 66 66 11 ; f: 1111 | 7: 0111
+ 00 00 00 22 | 66 66 11 11 ; 7: 0111 | 3: 0011
+ 00 00 22 22 | 66 66 11 11 ; 3: 0011 | 3: 0011
+ 00 22 22 22 | 66 11 11 11 ; 1: 0001 | 1: 0001
+
+
+ On the other hand, if P0 > P1 and P2 > P3, then
+ [P0 P1 B0 B1 B2 B3] represent the top half of the
+ block and [P2 P3 B4 B5 B6 B7] represent the bottom half.
+
+ For example:
+
+ 22 00 cc 66 33 19 66 11 18 24 42 81
+
+ yeilds:
+
+ 22 22 00 00 22 22 00 00 ; cc: 11001100
+ 22 00 00 22 22 00 00 22 ; 66: 01100110
+ 00 00 22 22 00 00 22 22 ; 33: 00110011
+ 00 22 22 00 00 22 22 22 ; 19: 00011001
+ -----------------------
+ 66 66 66 11 11 66 66 66 ; 18: 00011000
+ 66 66 11 66 66 11 66 66 ; 24: 00100100
+ 66 11 66 66 66 66 11 66 ; 42: 01000010
+ 11 66 66 66 66 66 66 11 ; 81: 10000001
+ */
+ if ( (*pData)[0] <= (*pData)[1])
+ {
+ // four quadrant case
+ for (i=0; i<4; i++)
+ {
+ p[0] = *(*pData)++;
+ p[1] = *(*pData)++;
+ pat[0] = *(*pData)++;
+ pat[1] = *(*pData)++;
+ patternQuadrant2Pixels(*pFrame, pat[0], pat[1], p);
+
+ // alternate between moving down and moving up and right
+ if (i & 1)
+ *pFrame += 4 - 4*g_width; // up and right
+ else
+ *pFrame += 4*g_width; // down
+ }
+ }
+ else if ( (*pData)[6] <= (*pData)[7])
+ {
+ // split horizontal
+ for (i=0; i<4; i++)
+ {
+ if ((i & 1) == 0)
+ {
+ p[0] = *(*pData)++;
+ p[1] = *(*pData)++;
+ }
+ pat[0] = *(*pData)++;
+ pat[1] = *(*pData)++;
+ patternQuadrant2Pixels(*pFrame, pat[0], pat[1], p);
+
+ if (i & 1)
+ *pFrame -= (4*g_width - 4);
+ else
+ *pFrame += 4*g_width;
+ }
+ }
+ else
+ {
+ // split vertical
+ for (i=0; i<8; i++)
+ {
+ if ((i & 3) == 0)
+ {
+ p[0] = *(*pData)++;
+ p[1] = *(*pData)++;
+ }
+ patternRow2Pixels(*pFrame, *(*pData)++, p);
+ *pFrame += g_width;
+ }
+ *pFrame -= (8*g_width - 8);
+ }
+ break;
+
+ case 0x9:
+ /* Similar to the previous 2 encodings, only more complicated. And
+ it will get worse before it gets better. No longer are we dealing
+ with patterns over two pixel values. Now we are dealing with
+ patterns over 4 pixel values with 2 bits assigned to each pixel
+ (or block of pixels).
+
+ So, first on the data stream are our 4 pixel values:
+
+ P0 P1 P2 P3
+
+ Now, if P0 <= P1 AND P2 <= P3, we get 16 bytes of pattern, each
+ 2 bits representing a 1x1 pixel (00=P0, 01=P1, 10=P2, 11=P3). The
+ ordering is again left to right and top to bottom. The most
+ significant bits represent the left side at the top, and so on.
+
+ If P0 <= P1 AND P2 > P3, we get 4 bytes of pattern, each 2 bits
+ representing a 2x2 pixel. Ordering is left to right and top to
+ bottom.
+
+ if P0 > P1 AND P2 <= P3, we get 8 bytes of pattern, each 2 bits
+ representing a 2x1 pixel (i.e. 2 pixels wide, and 1 high).
+
+ if P0 > P1 AND P2 > P3, we get 8 bytes of pattern, each 2 bits
+ representing a 1x2 pixel (i.e. 1 pixel wide, and 2 high).
+ */
+ if ( (*pData)[0] <= (*pData)[1])
+ {
+ if ( (*pData)[2] <= (*pData)[3])
+ {
+ p[0] = *(*pData)++;
+ p[1] = *(*pData)++;
+ p[2] = *(*pData)++;
+ p[3] = *(*pData)++;
+
+ for (i=0; i<8; i++)
+ {
+ pat[0] = *(*pData)++;
+ pat[1] = *(*pData)++;
+ patternRow4Pixels(*pFrame, pat[0], pat[1], p);
+ *pFrame += g_width;
+ }
+
+ *pFrame -= (8*g_width - 8);
+ }
+ else
+ {
+ p[0] = *(*pData)++;
+ p[1] = *(*pData)++;
+ p[2] = *(*pData)++;
+ p[3] = *(*pData)++;
+
+ patternRow4Pixels2(*pFrame, *(*pData)++, p);
+ *pFrame += 2*g_width;
+ patternRow4Pixels2(*pFrame, *(*pData)++, p);
+ *pFrame += 2*g_width;
+ patternRow4Pixels2(*pFrame, *(*pData)++, p);
+ *pFrame += 2*g_width;
+ patternRow4Pixels2(*pFrame, *(*pData)++, p);
+ *pFrame -= (6*g_width - 8);
+ }
+ }
+ else
+ {
+ if ( (*pData)[2] <= (*pData)[3])
+ {
+ // draw 2x1 strips
+ p[0] = *(*pData)++;
+ p[1] = *(*pData)++;
+ p[2] = *(*pData)++;
+ p[3] = *(*pData)++;
+
+ for (i=0; i<8; i++)
+ {
+ pat[0] = *(*pData)++;
+ patternRow4Pixels2x1(*pFrame, pat[0], p);
+ *pFrame += g_width;
+ }
+
+ *pFrame -= (8*g_width - 8);
+ }
+ else
+ {
+ // draw 1x2 strips
+ p[0] = *(*pData)++;
+ p[1] = *(*pData)++;
+ p[2] = *(*pData)++;
+ p[3] = *(*pData)++;
+
+ for (i=0; i<4; i++)
+ {
+ pat[0] = *(*pData)++;
+ pat[1] = *(*pData)++;
+ patternRow4Pixels(*pFrame, pat[0], pat[1], p);
+ *pFrame += g_width;
+ patternRow4Pixels(*pFrame, pat[0], pat[1], p);
+ *pFrame += g_width;
+ }
+
+ *pFrame -= (8*g_width - 8);
+ }
+ }
+ break;
+
+ case 0xa:
+ /* Similar to the previous, only a little more complicated.
+
+ We are still dealing with patterns over 4 pixel values with 2 bits
+ assigned to each pixel (or block of pixels).
+
+ So, first on the data stream are our 4 pixel values:
+
+ P0 P1 P2 P3
+
+ Now, if P0 <= P1, the block is divided into 4 quadrants, ordered
+ (as with opcode 0x8) TL, BL, TR, BR. In this case the next data
+ in the data stream should be:
+
+ B0 B1 B2 B3
+ P4 P5 P6 P7 B4 B5 B6 B7
+ P8 P9 P10 P11 B8 B9 B10 B11
+ P12 P13 P14 P15 B12 B13 B14 B15
+
+ Each 2 bits represent a 1x1 pixel (00=P0, 01=P1, 10=P2, 11=P3).
+ The ordering is again left to right and top to bottom. The most
+ significant bits represent the right side at the top, and so on.
+
+ If P0 > P1 then the next data on the data stream is:
+
+ B0 B1 B2 B3 B4 B5 B6 B7
+ P4 P5 P6 P7 B8 B9 B10 B11 B12 B13 B14 B15
+
+ Now, in this case, if P4 <= P5,
+ [P0 P1 P2 P3 B0 B1 B2 B3 B4 B5 B6 B7] represent the left half of
+ the block and the other bytes represent the right half. If P4 >
+ P5, then [P0 P1 P2 P3 B0 B1 B2 B3 B4 B5 B6 B7] represent the top
+ half of the block and the other bytes represent the bottom half.
+ */
+ if ( (*pData)[0] <= (*pData)[1])
+ {
+ for (i=0; i<4; i++)
+ {
+ p[0] = *(*pData)++;
+ p[1] = *(*pData)++;
+ p[2] = *(*pData)++;
+ p[3] = *(*pData)++;
+ pat[0] = *(*pData)++;
+ pat[1] = *(*pData)++;
+ pat[2] = *(*pData)++;
+ pat[3] = *(*pData)++;
+
+ patternQuadrant4Pixels(*pFrame, pat[0], pat[1], pat[2], pat[3], p);
+
+ if (i & 1)
+ *pFrame -= (4*g_width - 4);
+ else
+ *pFrame += 4*g_width;
+ }
+ }
+ else
+ {
+ if ( (*pData)[12] <= (*pData)[13])
+ {
+ // split vertical
+ for (i=0; i<4; i++)
+ {
+ if ((i&1) == 0)
+ {
+ p[0] = *(*pData)++;
+ p[1] = *(*pData)++;
+ p[2] = *(*pData)++;
+ p[3] = *(*pData)++;
+ }
+
+ pat[0] = *(*pData)++;
+ pat[1] = *(*pData)++;
+ pat[2] = *(*pData)++;
+ pat[3] = *(*pData)++;
+
+ patternQuadrant4Pixels(*pFrame, pat[0], pat[1], pat[2], pat[3], p);
+
+ if (i & 1)
+ *pFrame -= (4*g_width - 4);
+ else
+ *pFrame += 4*g_width;
+ }
+ }
+ else
+ {
+ // split horizontal
+ for (i=0; i<8; i++)
+ {
+ if ((i&3) == 0)
+ {
+ p[0] = *(*pData)++;
+ p[1] = *(*pData)++;
+ p[2] = *(*pData)++;
+ p[3] = *(*pData)++;
+ }
+
+ pat[0] = *(*pData)++;
+ pat[1] = *(*pData)++;
+ patternRow4Pixels(*pFrame, pat[0], pat[1], p);
+ *pFrame += g_width;
+ }
+
+ *pFrame -= (8*g_width - 8);
+ }
+ }
+ break;
+
+ case 0xb:
+ /* In this encoding we get raw pixel data in the data stream -- 64
+ bytes of pixel data. 1 byte for each pixel, and in the standard
+ order (l->r, t->b).
+ */
+ for (i=0; i<8; i++)
+ {
+ memcpy(*pFrame, *pData, 8);
+ *pFrame += g_width;
+ *pData += 8;
+ *pDataRemain -= 8;
+ }
+ *pFrame -= (8*g_width - 8);
+ break;
+
+ case 0xc:
+ /* In this encoding we get raw pixel data in the data stream -- 16
+ bytes of pixel data. 1 byte for each block of 2x2 pixels, and in
+ the standard order (l->r, t->b).
+ */
+ for (i=0; i<4; i++)
+ {
+ for (j=0; j<2; j++)
+ {
+ for (k=0; k<4; k++)
+ {
+ (*pFrame)[2*k] = (*pData)[k];
+ (*pFrame)[2*k+1] = (*pData)[k];
+ }
+ *pFrame += g_width;
+ }
+ *pData += 4;
+ *pDataRemain -= 4;
+ }
+ *pFrame -= (8*g_width - 8);
+ break;
+
+ case 0xd:
+ /* In this encoding we get raw pixel data in the data stream -- 4
+ bytes of pixel data. 1 byte for each block of 4x4 pixels, and in
+ the standard order (l->r, t->b).
+ */
+ for (i=0; i<2; i++)
+ {
+ for (j=0; j<4; j++)
+ {
+ for (k=0; k<4; k++)
+ {
+ (*pFrame)[k*g_width+j] = (*pData)[0];
+ (*pFrame)[k*g_width+j+4] = (*pData)[1];
+ }
+ }
+ *pFrame += 4*g_width;
+ *pData += 2;
+ *pDataRemain -= 2;
+ }
+ *pFrame -= (8*g_width - 8);
+ break;
+
+ case 0xe:
+ /* This encoding represents a solid 8x8 frame. We get 1 byte of pixel
+ data from the data stream.
+ */
+ for (i=0; i<8; i++)
+ {
+ memset(*pFrame, **pData, 8);
+ *pFrame += g_width;
+ }
+ ++*pData;
+ --*pDataRemain;
+ *pFrame -= (8*g_width - 8);
+ break;
+
+ case 0xf:
+ /* This encoding represents a "dithered" frame, which is
+ checkerboarded with alternate pixels of two colors. We get 2
+ bytes of pixel data from the data stream, and these bytes are
+ alternated:
+
+ P0 P1 P0 P1 P0 P1 P0 P1
+ P1 P0 P1 P0 P1 P0 P1 P0
+ ...
+ P0 P1 P0 P1 P0 P1 P0 P1
+ P1 P0 P1 P0 P1 P0 P1 P0
+ */
+ for (i=0; i<8; i++)
+ {
+ for (j=0; j<8; j++)
+ {
+ (*pFrame)[j] = (*pData)[(i+j)&1];
+ }
+ *pFrame += g_width;
+ }
+ *pData += 2;
+ *pDataRemain -= 2;
+ *pFrame -= (8*g_width - 8);
+ break;
+
+ default:
+ break;
+ }
+}
diff --git a/libmve/decoders.h b/libmve/decoders.h
new file mode 100644
index 00000000..9cff2f25
--- /dev/null
+++ b/libmve/decoders.h
@@ -0,0 +1,16 @@
+/*
+ *
+ * INTERNAL header - not to be included outside of libmve
+ *
+ */
+
+#ifndef _DECODERS_H
+#define _DECODERS_H
+
+extern int g_width, g_height;
+extern void *g_vBackBuf1, *g_vBackBuf2;
+
+extern void decodeFrame8(unsigned char *pFrame, unsigned char *pMap, int mapRemain, unsigned char *pData, int dataRemain);
+extern void decodeFrame16(unsigned char *pFrame, unsigned char *pMap, int mapRemain, unsigned char *pData, int dataRemain);
+
+#endif // _DECODERS_H
diff --git a/libmve/libmve.h b/libmve/libmve.h
new file mode 100644
index 00000000..15895e9e
--- /dev/null
+++ b/libmve/libmve.h
@@ -0,0 +1,44 @@
+#ifndef _LIBMVE_H
+#define _LIBMVE_H
+
+#define MVE_ERR_EOF 1
+
+typedef struct{
+ int screenWidth;
+ int screenHeight;
+ int width;
+ int height;
+ int truecolor;
+} MVE_videoSpec;
+
+int MVE_rmPrepMovie(void *stream, int x, int y, int track);
+int MVE_rmStepMovie(void);
+void MVE_rmHoldMovie(void);
+void MVE_rmEndMovie(void);
+
+void MVE_getVideoSpec(MVE_videoSpec *vSpec);
+
+void MVE_sndInit(int x);
+
+typedef unsigned int (*mve_cb_Read)(void *stream,
+ void *buffer,
+ unsigned int count);
+
+typedef void *(*mve_cb_Alloc)(unsigned int size);
+typedef void (*mve_cb_Free)(void *ptr);
+
+typedef void (*mve_cb_ShowFrame)(unsigned char *buffer,
+ unsigned int bufw, unsigned int bufh,
+ unsigned int sx, unsigned int sy,
+ unsigned int w, unsigned int h,
+ unsigned int dstx, unsigned int dsty);
+
+typedef void (*mve_cb_SetPalette)(unsigned char *p,
+ unsigned int start, unsigned int count);
+
+void MVE_ioCallbacks(mve_cb_Read io_read);
+void MVE_memCallbacks(mve_cb_Alloc mem_alloc, mve_cb_Free mem_free);
+void MVE_sfCallbacks(mve_cb_ShowFrame showframe);
+void MVE_palCallbacks(mve_cb_SetPalette setpalette);
+
+#endif /* _LIBMVE_H */
diff --git a/libmve/mve_audio.c b/libmve/mve_audio.c
new file mode 100644
index 00000000..4495a035
--- /dev/null
+++ b/libmve/mve_audio.c
@@ -0,0 +1,55 @@
+static int audio_exp_table[256] =
+{
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 47, 51, 56, 61,
+ 66, 72, 79, 86, 94, 102, 112, 122, 133, 145, 158, 173, 189, 206, 225, 245,
+ 267, 292, 318, 348, 379, 414, 452, 493, 538, 587, 640, 699, 763, 832, 908, 991,
+ 1081, 1180, 1288, 1405, 1534, 1673, 1826, 1993, 2175, 2373, 2590, 2826, 3084, 3365, 3672, 4008,
+ 4373, 4772, 5208, 5683, 6202, 6767, 7385, 8059, 8794, 9597, 10472, 11428, 12471, 13609, 14851, 16206,
+ 17685, 19298, 21060, 22981, 25078, 27367, 29864, 32589, -29973, -26728, -23186, -19322, -15105, -10503, -5481, -1,
+ 1, 1, 5481, 10503, 15105, 19322, 23186, 26728, 29973, -32589, -29864, -27367, -25078, -22981, -21060, -19298,
+ -17685, -16206, -14851, -13609, -12471, -11428, -10472, -9597, -8794, -8059, -7385, -6767, -6202, -5683, -5208, -4772,
+ -4373, -4008, -3672, -3365, -3084, -2826, -2590, -2373, -2175, -1993, -1826, -1673, -1534, -1405, -1288, -1180,
+ -1081, -991, -908, -832, -763, -699, -640, -587, -538, -493, -452, -414, -379, -348, -318, -292,
+ -267, -245, -225, -206, -189, -173, -158, -145, -133, -122, -112, -102, -94, -86, -79, -72,
+ -66, -61, -56, -51, -47, -43, -42, -41, -40, -39, -38, -37, -36, -35, -34, -33,
+ -32, -31, -30, -29, -28, -27, -26, -25, -24, -23, -22, -21, -20, -19, -18, -17,
+ -16, -15, -14, -13, -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1
+};
+
+static int getWord(unsigned char **fin)
+{
+ int value = ((*fin)[1] << 8) | (*fin)[0];
+ *fin += 2;
+ return value;
+}
+
+static void sendWord(short **fout, int nOffset)
+{
+ *(*fout)++ = nOffset;
+}
+
+static void processSwath(short *fout, unsigned char *data, int swath, int *offsets)
+{
+ int i;
+ for (i=0; i // for mem* functions
+#if !defined(_WIN32) && !defined(macintosh)
+#include
+#include
+#include
+#include
+#endif
+
+#include "mvelib.h"
+
+static const char MVE_HEADER[] = "Interplay MVE File\x1A";
+static const short MVE_HDRCONST1 = 0x001A;
+static const short MVE_HDRCONST2 = 0x0100;
+static const short MVE_HDRCONST3 = 0x1133;
+
+mve_cb_Read mve_read;
+mve_cb_Alloc mve_alloc;
+mve_cb_Free mve_free;
+mve_cb_ShowFrame mve_showframe;
+mve_cb_SetPalette mve_setpalette;
+
+/*
+ * private utility functions
+ */
+static short _mve_get_short(unsigned char *data);
+static unsigned short _mve_get_ushort(unsigned char *data);
+
+/*
+ * private functions for mvefile
+ */
+static MVEFILE *_mvefile_alloc(void);
+static void _mvefile_free(MVEFILE *movie);
+static int _mvefile_open(MVEFILE *movie, void *stream);
+static void _mvefile_reset(MVEFILE *movie);
+static int _mvefile_read_header(MVEFILE *movie);
+static void _mvefile_set_buffer_size(MVEFILE *movie, int buf_size);
+static int _mvefile_fetch_next_chunk(MVEFILE *movie);
+
+/*
+ * private functions for mvestream
+ */
+static MVESTREAM *_mvestream_alloc(void);
+static void _mvestream_free(MVESTREAM *movie);
+static int _mvestream_open(MVESTREAM *movie, void *stream);
+static void _mvestream_reset(MVESTREAM *movie);
+
+/************************************************************
+ * public MVEFILE functions
+ ************************************************************/
+
+/*
+ * open an MVE file
+ */
+MVEFILE *mvefile_open(void *stream)
+{
+ MVEFILE *file;
+
+ /* create the file */
+ file = _mvefile_alloc();
+ if (! _mvefile_open(file, stream))
+ {
+ _mvefile_free(file);
+ return NULL;
+ }
+
+ /* initialize the file */
+ _mvefile_set_buffer_size(file, 1024);
+
+ /* verify the file's header */
+ if (! _mvefile_read_header(file))
+ {
+ _mvefile_free(file);
+ return NULL;
+ }
+
+ /* now, prefetch the next chunk */
+ _mvefile_fetch_next_chunk(file);
+
+ return file;
+}
+
+/*
+ * close a MVE file
+ */
+void mvefile_close(MVEFILE *movie)
+{
+ _mvefile_free(movie);
+}
+
+/*
+ * reset a MVE file
+ */
+void mvefile_reset(MVEFILE *file)
+{
+ _mvefile_reset(file);
+
+ /* initialize the file */
+ _mvefile_set_buffer_size(file, 1024);
+
+ /* verify the file's header */
+ if (! _mvefile_read_header(file))
+ {
+ _mvefile_free(file);
+ //return NULL;
+ }
+
+ /* now, prefetch the next chunk */
+ _mvefile_fetch_next_chunk(file);
+}
+
+/*
+ * get the size of the next segment
+ */
+int mvefile_get_next_segment_size(MVEFILE *movie)
+{
+ /* if nothing is cached, fail */
+ if (movie->cur_chunk == NULL || movie->next_segment >= movie->cur_fill)
+ return -1;
+
+ /* if we don't have enough data to get a segment, fail */
+ if (movie->cur_fill - movie->next_segment < 4)
+ return -1;
+
+ /* otherwise, get the data length */
+ return _mve_get_short(movie->cur_chunk + movie->next_segment);
+}
+
+/*
+ * get type of next segment in chunk (0xff if no more segments in chunk)
+ */
+unsigned char mvefile_get_next_segment_major(MVEFILE *movie)
+{
+ /* if nothing is cached, fail */
+ if (movie->cur_chunk == NULL || movie->next_segment >= movie->cur_fill)
+ return 0xff;
+
+ /* if we don't have enough data to get a segment, fail */
+ if (movie->cur_fill - movie->next_segment < 4)
+ return 0xff;
+
+ /* otherwise, get the data length */
+ return movie->cur_chunk[movie->next_segment + 2];
+}
+
+/*
+ * get subtype (version) of next segment in chunk (0xff if no more segments in
+ * chunk)
+ */
+unsigned char mvefile_get_next_segment_minor(MVEFILE *movie)
+{
+ /* if nothing is cached, fail */
+ if (movie->cur_chunk == NULL || movie->next_segment >= movie->cur_fill)
+ return 0xff;
+
+ /* if we don't have enough data to get a segment, fail */
+ if (movie->cur_fill - movie->next_segment < 4)
+ return 0xff;
+
+ /* otherwise, get the data length */
+ return movie->cur_chunk[movie->next_segment + 3];
+}
+
+/*
+ * see next segment (return NULL if no next segment)
+ */
+unsigned char *mvefile_get_next_segment(MVEFILE *movie)
+{
+ /* if nothing is cached, fail */
+ if (movie->cur_chunk == NULL || movie->next_segment >= movie->cur_fill)
+ return NULL;
+
+ /* if we don't have enough data to get a segment, fail */
+ if (movie->cur_fill - movie->next_segment < 4)
+ return NULL;
+
+ /* otherwise, get the data length */
+ return movie->cur_chunk + movie->next_segment + 4;
+}
+
+/*
+ * advance to next segment
+ */
+void mvefile_advance_segment(MVEFILE *movie)
+{
+ /* if nothing is cached, fail */
+ if (movie->cur_chunk == NULL || movie->next_segment >= movie->cur_fill)
+ return;
+
+ /* if we don't have enough data to get a segment, fail */
+ if (movie->cur_fill - movie->next_segment < 4)
+ return;
+
+ /* else, advance to next segment */
+ movie->next_segment +=
+ (4 + _mve_get_ushort(movie->cur_chunk + movie->next_segment));
+}
+
+/*
+ * fetch the next chunk (return 0 if at end of stream)
+ */
+int mvefile_fetch_next_chunk(MVEFILE *movie)
+{
+ return _mvefile_fetch_next_chunk(movie);
+}
+
+/************************************************************
+ * public MVESTREAM functions
+ ************************************************************/
+
+/*
+ * open an MVE stream
+ */
+MVESTREAM *mve_open(void *stream)
+{
+ MVESTREAM *movie;
+
+ /* allocate */
+ movie = _mvestream_alloc();
+
+ /* open */
+ if (! _mvestream_open(movie, stream))
+ {
+ _mvestream_free(movie);
+ return NULL;
+ }
+
+ return movie;
+}
+
+/*
+ * close an MVE stream
+ */
+void mve_close(MVESTREAM *movie)
+{
+ _mvestream_free(movie);
+}
+
+/*
+ * reset an MVE stream
+ */
+void mve_reset(MVESTREAM *movie)
+{
+ _mvestream_reset(movie);
+}
+
+/*
+ * set segment type handler
+ */
+void mve_set_handler(MVESTREAM *movie, unsigned char major, MVESEGMENTHANDLER handler)
+{
+ if (major < 32)
+ movie->handlers[major] = handler;
+}
+
+/*
+ * set segment handler context
+ */
+void mve_set_handler_context(MVESTREAM *movie, void *context)
+{
+ movie->context = context;
+}
+
+/*
+ * play next chunk
+ */
+int mve_play_next_chunk(MVESTREAM *movie)
+{
+ unsigned char major, minor;
+ unsigned char *data;
+ int len;
+
+ /* loop over segments */
+ major = mvefile_get_next_segment_major(movie->movie);
+ while (major != 0xff)
+ {
+ /* check whether to handle the segment */
+ if (major < 32 && movie->handlers[major] != NULL)
+ {
+ minor = mvefile_get_next_segment_minor(movie->movie);
+ len = mvefile_get_next_segment_size(movie->movie);
+ data = mvefile_get_next_segment(movie->movie);
+
+ if (! movie->handlers[major](major, minor, data, len, movie->context))
+ return 0;
+ }
+
+ /* advance to next segment */
+ mvefile_advance_segment(movie->movie);
+ major = mvefile_get_next_segment_major(movie->movie);
+ }
+
+ if (! mvefile_fetch_next_chunk(movie->movie))
+ return 0;
+
+ /* return status */
+ return 1;
+}
+
+/************************************************************
+ * private functions
+ ************************************************************/
+
+/*
+ * allocate an MVEFILE
+ */
+static MVEFILE *_mvefile_alloc(void)
+{
+ MVEFILE *file = (MVEFILE *)mve_alloc(sizeof(MVEFILE));
+ file->stream = NULL;
+ file->cur_chunk = NULL;
+ file->buf_size = 0;
+ file->cur_fill = 0;
+ file->next_segment = 0;
+
+ return file;
+}
+
+/*
+ * free an MVE file
+ */
+static void _mvefile_free(MVEFILE *movie)
+{
+ /* free the stream */
+ movie->stream = NULL;
+
+ /* free the buffer */
+ if (movie->cur_chunk)
+ mve_free(movie->cur_chunk);
+ movie->cur_chunk = NULL;
+
+ /* not strictly necessary */
+ movie->buf_size = 0;
+ movie->cur_fill = 0;
+ movie->next_segment = 0;
+
+ /* free the struct */
+ mve_free(movie);
+}
+
+/*
+ * open the file stream in thie object
+ */
+static int _mvefile_open(MVEFILE *file, void *stream)
+{
+ file->stream = stream;
+ if (! file->stream)
+ return 0;
+
+ return 1;
+}
+
+/*
+ * allocate an MVEFILE
+ */
+static void _mvefile_reset(MVEFILE *file)
+{
+#if 0
+ file->cur_chunk = NULL;
+ file->buf_size = 0;
+ file->cur_fill = 0;
+ file->next_segment = 0;
+#endif
+}
+
+/*
+ * read and verify the header of the recently opened file
+ */
+static int _mvefile_read_header(MVEFILE *movie)
+{
+ unsigned char buffer[26];
+
+ /* check the file is open */
+ if (! movie->stream)
+ return 0;
+
+ /* check the file is long enough */
+ if (! mve_read(movie->stream, buffer, 26))
+ return 0;
+
+ /* check the signature */
+ if (memcmp(buffer, MVE_HEADER, 20))
+ return 0;
+
+ /* check the hard-coded constants */
+ if (_mve_get_short(buffer+20) != MVE_HDRCONST1)
+ return 0;
+ if (_mve_get_short(buffer+22) != MVE_HDRCONST2)
+ return 0;
+ if (_mve_get_short(buffer+24) != MVE_HDRCONST3)
+ return 0;
+
+ return 1;
+}
+
+static void _mvefile_set_buffer_size(MVEFILE *movie, int buf_size)
+{
+ unsigned char *new_buffer;
+ int new_len;
+
+ /* check if this would be a redundant operation */
+ if (buf_size <= movie->buf_size)
+ return;
+
+ /* allocate new buffer */
+ new_len = 100 + buf_size;
+ new_buffer = (unsigned char *)mve_alloc(new_len);
+
+ /* copy old data */
+ if (movie->cur_chunk && movie->cur_fill)
+ memcpy(new_buffer, movie->cur_chunk, movie->cur_fill);
+
+ /* free old buffer */
+ if (movie->cur_chunk)
+ {
+ mve_free(movie->cur_chunk);
+ movie->cur_chunk = 0;
+ }
+
+ /* install new buffer */
+ movie->cur_chunk = new_buffer;
+ movie->buf_size = new_len;
+}
+
+static int _mvefile_fetch_next_chunk(MVEFILE *movie)
+{
+ unsigned char buffer[4];
+ unsigned short length;
+
+ /* fail if not open */
+ if (! movie->stream)
+ return 0;
+
+ /* fail if we can't read the next segment descriptor */
+ if (! mve_read(movie->stream, buffer, 4))
+ return 0;
+
+ /* pull out the next length */
+ length = _mve_get_short(buffer);
+
+ /* make sure we've got sufficient space */
+ _mvefile_set_buffer_size(movie, length);
+
+ /* read the chunk */
+ if (! mve_read(movie->stream, movie->cur_chunk, length))
+ return 0;
+ movie->cur_fill = length;
+ movie->next_segment = 0;
+
+ return 1;
+}
+
+static short _mve_get_short(unsigned char *data)
+{
+ short value;
+ value = data[0] | (data[1] << 8);
+ return value;
+}
+
+static unsigned short _mve_get_ushort(unsigned char *data)
+{
+ unsigned short value;
+ value = data[0] | (data[1] << 8);
+ return value;
+}
+
+/*
+ * allocate an MVESTREAM
+ */
+static MVESTREAM *_mvestream_alloc(void)
+{
+ MVESTREAM *movie;
+
+ /* allocate and zero-initialize everything */
+ movie = (MVESTREAM *)mve_alloc(sizeof(MVESTREAM));
+ movie->movie = NULL;
+ movie->context = 0;
+ memset(movie->handlers, 0, sizeof(movie->handlers));
+
+ return movie;
+}
+
+/*
+ * free an MVESTREAM
+ */
+static void _mvestream_free(MVESTREAM *movie)
+{
+ /* close MVEFILE */
+ if (movie->movie)
+ mvefile_close(movie->movie);
+ movie->movie = NULL;
+
+ /* clear context and handlers */
+ movie->context = NULL;
+ memset(movie->handlers, 0, sizeof(movie->handlers));
+
+ /* free the struct */
+ mve_free(movie);
+}
+
+/*
+ * open an MVESTREAM object
+ */
+static int _mvestream_open(MVESTREAM *movie, void *stream)
+{
+ movie->movie = mvefile_open(stream);
+
+ return (movie->movie == NULL) ? 0 : 1;
+}
+
+/*
+ * reset an MVESTREAM
+ */
+static void _mvestream_reset(MVESTREAM *movie)
+{
+ mvefile_reset(movie->movie);
+}
diff --git a/libmve/mvelib.h b/libmve/mvelib.h
new file mode 100644
index 00000000..720dd001
--- /dev/null
+++ b/libmve/mvelib.h
@@ -0,0 +1,113 @@
+#ifndef INCLUDED_MVELIB_H
+#define INCLUDED_MVELIB_H
+
+#include
+#include
+
+#include "libmve.h"
+
+extern mve_cb_Read mve_read;
+extern mve_cb_Alloc mve_alloc;
+extern mve_cb_Free mve_free;
+extern mve_cb_ShowFrame mve_showframe;
+extern mve_cb_SetPalette mve_setpalette;
+
+/*
+ * structure for maintaining info on a MVEFILE stream
+ */
+typedef struct MVEFILE
+{
+ void *stream;
+ unsigned char *cur_chunk;
+ int buf_size;
+ int cur_fill;
+ int next_segment;
+} MVEFILE;
+
+/*
+ * open a .MVE file
+ */
+MVEFILE *mvefile_open(void *stream);
+
+/*
+ * close a .MVE file
+ */
+void mvefile_close(MVEFILE *movie);
+
+/*
+ * get size of next segment in chunk (-1 if no more segments in chunk)
+ */
+int mvefile_get_next_segment_size(MVEFILE *movie);
+
+/*
+ * get type of next segment in chunk (0xff if no more segments in chunk)
+ */
+unsigned char mvefile_get_next_segment_major(MVEFILE *movie);
+
+/*
+ * get subtype (version) of next segment in chunk (0xff if no more segments in
+ * chunk)
+ */
+unsigned char mvefile_get_next_segment_minor(MVEFILE *movie);
+
+/*
+ * see next segment (return NULL if no next segment)
+ */
+unsigned char *mvefile_get_next_segment(MVEFILE *movie);
+
+/*
+ * advance to next segment
+ */
+void mvefile_advance_segment(MVEFILE *movie);
+
+/*
+ * fetch the next chunk (return 0 if at end of stream)
+ */
+int mvefile_fetch_next_chunk(MVEFILE *movie);
+
+/*
+ * callback for segment type
+ */
+typedef int (*MVESEGMENTHANDLER)(unsigned char major, unsigned char minor, unsigned char *data, int len, void *context);
+
+/*
+ * structure for maintaining an MVE stream
+ */
+typedef struct MVESTREAM
+{
+ MVEFILE *movie;
+ void *context;
+ MVESEGMENTHANDLER handlers[32];
+} MVESTREAM;
+
+/*
+ * open an MVE stream
+ */
+MVESTREAM *mve_open(void *stream);
+
+/*
+ * close an MVE stream
+ */
+void mve_close(MVESTREAM *movie);
+
+/*
+ * reset an MVE stream
+ */
+void mve_reset(MVESTREAM *movie);
+
+/*
+ * set segment type handler
+ */
+void mve_set_handler(MVESTREAM *movie, unsigned char major, MVESEGMENTHANDLER handler);
+
+/*
+ * set segment handler context
+ */
+void mve_set_handler_context(MVESTREAM *movie, void *context);
+
+/*
+ * play next chunk
+ */
+int mve_play_next_chunk(MVESTREAM *movie);
+
+#endif /* INCLUDED_MVELIB_H */
diff --git a/libmve/mveplay.c b/libmve/mveplay.c
new file mode 100644
index 00000000..7be40090
--- /dev/null
+++ b/libmve/mveplay.c
@@ -0,0 +1,749 @@
+#ifdef HAVE_CONFIG_H
+#include
+#endif
+
+#ifndef __MSDOS__
+#define AUDIO
+#endif
+//#define DEBUG
+
+#include
+#ifdef _WIN32
+# include
+#else
+# include
+# include
+# include
+# ifdef macintosh
+# include
+# include
+# else
+# include
+# include
+# include
+# include
+# endif // macintosh
+#endif // _WIN32
+
+#if defined(AUDIO)
+#include
+#include "SDL_mixer.h"
+#endif
+
+#include "mvelib.h"
+#include "mve_audio.h"
+
+#include "decoders.h"
+
+#include "libmve.h"
+
+#define MVE_OPCODE_ENDOFSTREAM 0x00
+#define MVE_OPCODE_ENDOFCHUNK 0x01
+#define MVE_OPCODE_CREATETIMER 0x02
+#define MVE_OPCODE_INITAUDIOBUFFERS 0x03
+#define MVE_OPCODE_STARTSTOPAUDIO 0x04
+#define MVE_OPCODE_INITVIDEOBUFFERS 0x05
+
+#define MVE_OPCODE_DISPLAYVIDEO 0x07
+#define MVE_OPCODE_AUDIOFRAMEDATA 0x08
+#define MVE_OPCODE_AUDIOFRAMESILENCE 0x09
+#define MVE_OPCODE_INITVIDEOMODE 0x0A
+
+#define MVE_OPCODE_SETPALETTE 0x0C
+#define MVE_OPCODE_SETPALETTECOMPRESSED 0x0D
+
+#define MVE_OPCODE_SETDECODINGMAP 0x0F
+
+#define MVE_OPCODE_VIDEODATA 0x11
+
+#define MVE_AUDIO_FLAGS_STEREO 1
+#define MVE_AUDIO_FLAGS_16BIT 2
+#define MVE_AUDIO_FLAGS_COMPRESSED 4
+
+int g_spdFactorNum=0;
+static int g_spdFactorDenom=10;
+static int g_frameUpdated = 0;
+
+static short get_short(unsigned char *data)
+{
+ short value;
+ value = data[0] | (data[1] << 8);
+ return value;
+}
+
+static unsigned short get_ushort(unsigned char *data)
+{
+ unsigned short value;
+ value = data[0] | (data[1] << 8);
+ return value;
+}
+
+static int get_int(unsigned char *data)
+{
+ int value;
+ value = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
+ return value;
+}
+
+static unsigned int unhandled_chunks[32*256];
+
+static int default_seg_handler(unsigned char major, unsigned char minor, unsigned char *data, int len, void *context)
+{
+ unhandled_chunks[major<<8|minor]++;
+ //fprintf(stderr, "unknown chunk type %02x/%02x\n", major, minor);
+ return 1;
+}
+
+
+/*************************
+ * general handlers
+ *************************/
+static int end_movie_handler(unsigned char major, unsigned char minor, unsigned char *data, int len, void *context)
+{
+ return 0;
+}
+
+/*************************
+ * timer handlers
+ *************************/
+
+/*
+ * timer variables
+ */
+static int timer_created = 0;
+static int micro_frame_delay=0;
+static int timer_started=0;
+static unsigned long int timer_expire = 0;
+
+#if defined(_WIN32) || defined(macintosh)
+
+unsigned long int timer_getmicroseconds()
+{
+ static int counter = 0;
+#ifdef _WIN32
+ DWORD now = GetTickCount();
+#else
+ long now = TickCount();
+#endif
+ counter++;
+
+ return now * 1000 + counter;
+}
+
+#else
+
+unsigned long int timer_getmicroseconds()
+{
+ struct timeval tv;
+ static time_t starttime = 0;
+
+ gettimeofday(&tv, NULL);
+
+ if (!starttime)
+ starttime = tv.tv_sec;
+
+ return (tv.tv_sec - starttime) * 1000000 + tv.tv_usec;
+}
+
+#endif
+
+void timer_sleepmicroseconds(unsigned long int usec)
+{
+#ifdef _WIN32
+ Sleep(usec / 1000);
+#elif defined(macintosh)
+ Delay(usec / 1000);
+#else
+ struct timespec ts;
+ ts.tv_sec = usec / 1000000;
+ ts.tv_nsec = usec % 1000000 * 1000;
+ nanosleep(&ts, NULL);
+#endif
+}
+
+static int create_timer_handler(unsigned char major, unsigned char minor, unsigned char *data, int len, void *context)
+{
+
+#if !defined(_WIN32) && !defined(macintosh) // FIXME
+ __extension__ long long temp;
+#else
+ long temp;
+#endif
+
+ if (timer_created)
+ return 1;
+ else
+ timer_created = 1;
+
+ micro_frame_delay = get_int(data) * (int)get_short(data+4);
+ if (g_spdFactorNum != 0)
+ {
+ temp = micro_frame_delay;
+ temp *= g_spdFactorNum;
+ temp /= g_spdFactorDenom;
+ micro_frame_delay = (int)temp;
+ }
+
+ return 1;
+}
+
+static void timer_stop(void)
+{
+ timer_expire = 0;
+ timer_started = 0;
+}
+
+static void timer_start(void)
+{
+ timer_expire = timer_getmicroseconds();
+ timer_expire += micro_frame_delay;
+ timer_started=1;
+}
+
+static void do_timer_wait(void)
+{
+ unsigned long int ts;
+ unsigned long int tv;
+
+ if (! timer_started)
+ return;
+
+ tv = timer_getmicroseconds();
+ if (tv > timer_expire)
+ goto end;
+
+ ts = timer_expire - tv;
+
+ timer_sleepmicroseconds(ts);
+
+ end:
+ timer_expire += micro_frame_delay;
+}
+
+/*************************
+ * audio handlers
+ *************************/
+#ifdef AUDIO
+#define TOTAL_AUDIO_BUFFERS 64
+
+static int audiobuf_created = 0;
+static void mve_audio_callback(void *userdata, unsigned char *stream, int len);
+static short *mve_audio_buffers[TOTAL_AUDIO_BUFFERS];
+static int mve_audio_buflens[TOTAL_AUDIO_BUFFERS];
+static int mve_audio_curbuf_curpos=0;
+static int mve_audio_bufhead=0;
+static int mve_audio_buftail=0;
+static int mve_audio_playing=0;
+static int mve_audio_canplay=0;
+static int mve_audio_compressed=0;
+static int mve_audio_enabled = 1;
+
+
+static void mve_audio_callback(void *userdata, unsigned char *stream, int len)
+{
+ int total=0;
+ int length;
+ if (mve_audio_bufhead == mve_audio_buftail)
+ return /* 0 */;
+
+ //fprintf(stderr, "+ <%d (%d), %d, %d>\n", mve_audio_bufhead, mve_audio_curbuf_curpos, mve_audio_buftail, len);
+
+ while (mve_audio_bufhead != mve_audio_buftail /* while we have more buffers */
+ && len > (mve_audio_buflens[mve_audio_bufhead]-mve_audio_curbuf_curpos)) /* and while we need more data */
+ {
+ length = mve_audio_buflens[mve_audio_bufhead]-mve_audio_curbuf_curpos;
+ memcpy(stream, /* cur output position */
+ ((unsigned char *)mve_audio_buffers[mve_audio_bufhead])+mve_audio_curbuf_curpos, /* cur input position */
+ length); /* cur input length */
+
+ total += length;
+ stream += length; /* advance output */
+ len -= length; /* decrement avail ospace */
+ mve_free(mve_audio_buffers[mve_audio_bufhead]); /* free the buffer */
+ mve_audio_buffers[mve_audio_bufhead]=NULL; /* free the buffer */
+ mve_audio_buflens[mve_audio_bufhead]=0; /* free the buffer */
+
+ if (++mve_audio_bufhead == TOTAL_AUDIO_BUFFERS) /* next buffer */
+ mve_audio_bufhead = 0;
+ mve_audio_curbuf_curpos = 0;
+ }
+
+ //fprintf(stderr, "= <%d (%d), %d, %d>: %d\n", mve_audio_bufhead, mve_audio_curbuf_curpos, mve_audio_buftail, len, total);
+ /* return total; */
+
+ if (len != 0 /* ospace remaining */
+ && mve_audio_bufhead != mve_audio_buftail) /* buffers remaining */
+ {
+ memcpy(stream, /* dest */
+ ((unsigned char *)mve_audio_buffers[mve_audio_bufhead]) + mve_audio_curbuf_curpos, /* src */
+ len); /* length */
+
+ mve_audio_curbuf_curpos += len; /* advance input */
+ stream += len; /* advance output (unnecessary) */
+ len -= len; /* advance output (unnecessary) */
+
+ if (mve_audio_curbuf_curpos >= mve_audio_buflens[mve_audio_bufhead]) /* if this ends the current chunk */
+ {
+ mve_free(mve_audio_buffers[mve_audio_bufhead]); /* free buffer */
+ mve_audio_buffers[mve_audio_bufhead]=NULL;
+ mve_audio_buflens[mve_audio_bufhead]=0;
+
+ if (++mve_audio_bufhead == TOTAL_AUDIO_BUFFERS) /* next buffer */
+ mve_audio_bufhead = 0;
+ mve_audio_curbuf_curpos = 0;
+ }
+ }
+
+ //fprintf(stderr, "- <%d (%d), %d, %d>\n", mve_audio_bufhead, mve_audio_curbuf_curpos, mve_audio_buftail, len);
+}
+#endif
+
+static int create_audiobuf_handler(unsigned char major, unsigned char minor, unsigned char *data, int len, void *context)
+{
+#ifdef AUDIO
+ int flags;
+ int sample_rate;
+ int desired_buffer;
+
+ int stereo;
+ int bitsize;
+ int compressed;
+
+ int format;
+
+ if (!mve_audio_enabled)
+ return 1;
+
+ if (audiobuf_created)
+ return 1;
+ else
+ audiobuf_created = 1;
+
+ flags = get_ushort(data + 2);
+ sample_rate = get_ushort(data + 4);
+ desired_buffer = get_int(data + 6);
+
+ stereo = (flags & MVE_AUDIO_FLAGS_STEREO) ? 1 : 0;
+ bitsize = (flags & MVE_AUDIO_FLAGS_16BIT) ? 1 : 0;
+
+ if (minor > 0) {
+ compressed = flags & MVE_AUDIO_FLAGS_COMPRESSED ? 1 : 0;
+ } else {
+ compressed = 0;
+ }
+
+ mve_audio_compressed = compressed;
+
+ if (bitsize == 1) {
+#ifdef WORDS_BIGENDIAN
+ format = AUDIO_S16MSB;
+#else
+ format = AUDIO_S16LSB;
+#endif
+ } else {
+ format = AUDIO_U8;
+ }
+
+ fprintf(stderr, "creating audio buffers:\n");
+ fprintf(stderr, "sample rate = %d, stereo = %d, bitsize = %d, compressed = %d\n",
+ sample_rate, stereo, bitsize ? 16 : 8, compressed);
+
+ if (Mix_OpenAudio(sample_rate, format, stereo ? 2 : 1, 4096) == 0)
+ {
+ fprintf(stderr, " success\n");
+ mve_audio_canplay = 1;
+ }
+ else
+ {
+ fprintf(stderr, " failure : %s\n", Mix_GetError());
+ mve_audio_canplay = 0;
+ }
+
+ Mix_SetPostMix(mve_audio_callback, NULL);
+ mve_audio_canplay = 1;
+
+ memset(mve_audio_buffers, 0, sizeof(mve_audio_buffers));
+ memset(mve_audio_buflens, 0, sizeof(mve_audio_buflens));
+#endif
+
+ return 1;
+}
+
+static int play_audio_handler(unsigned char major, unsigned char minor, unsigned char *data, int len, void *context)
+{
+#ifdef AUDIO
+ if (mve_audio_canplay && !mve_audio_playing && mve_audio_bufhead != mve_audio_buftail)
+ {
+ Mix_Resume(-1);
+ mve_audio_playing = 1;
+ }
+#endif
+ return 1;
+}
+
+static int audio_data_handler(unsigned char major, unsigned char minor, unsigned char *data, int len, void *context)
+{
+#ifdef AUDIO
+ static const int selected_chan=1;
+ int chan;
+ int nsamp;
+ if (mve_audio_canplay)
+ {
+ chan = get_ushort(data + 2);
+ nsamp = get_ushort(data + 4);
+ if (chan & selected_chan)
+ {
+ /* HACK: +4 mveaudio_uncompress adds 4 more bytes */
+ if (major == MVE_OPCODE_AUDIOFRAMEDATA) {
+ if (mve_audio_compressed) {
+ nsamp += 4;
+
+ mve_audio_buflens[mve_audio_buftail] = nsamp;
+ mve_audio_buffers[mve_audio_buftail] = (short *)mve_alloc(nsamp);
+ mveaudio_uncompress(mve_audio_buffers[mve_audio_buftail], data, -1); /* XXX */
+ } else {
+ nsamp -= 8;
+ data += 8;
+
+ mve_audio_buflens[mve_audio_buftail] = nsamp;
+ mve_audio_buffers[mve_audio_buftail] = (short *)mve_alloc(nsamp);
+ memcpy(mve_audio_buffers[mve_audio_buftail], data, nsamp);
+ }
+ } else {
+ mve_audio_buflens[mve_audio_buftail] = nsamp;
+ mve_audio_buffers[mve_audio_buftail] = (short *)mve_alloc(nsamp);
+
+ memset(mve_audio_buffers[mve_audio_buftail], 0, nsamp); /* XXX */
+ }
+
+ if (++mve_audio_buftail == TOTAL_AUDIO_BUFFERS)
+ mve_audio_buftail = 0;
+
+ if (mve_audio_buftail == mve_audio_bufhead)
+ fprintf(stderr, "d'oh! buffer ring overrun (%d)\n", mve_audio_bufhead);
+ }
+ }
+#endif
+
+ return 1;
+}
+
+/*************************
+ * video handlers
+ *************************/
+
+static int videobuf_created = 0;
+static int video_initialized = 0;
+int g_width, g_height;
+void *g_vBuffers = NULL, *g_vBackBuf1, *g_vBackBuf2;
+
+static int g_destX, g_destY;
+static int g_screenWidth, g_screenHeight;
+static unsigned char *g_pCurMap=NULL;
+static int g_nMapLength=0;
+static int g_truecolor;
+
+static int create_videobuf_handler(unsigned char major, unsigned char minor, unsigned char *data, int len, void *context)
+{
+ short w, h;
+ short count, truecolor;
+
+ if (videobuf_created)
+ return 1;
+ else
+ videobuf_created = 1;
+
+ w = get_short(data);
+ h = get_short(data+2);
+
+ if (minor > 0) {
+ count = get_short(data+4);
+ } else {
+ count = 1;
+ }
+
+ if (minor > 1) {
+ truecolor = get_short(data+6);
+ } else {
+ truecolor = 0;
+ }
+
+ g_width = w << 3;
+ g_height = h << 3;
+
+ /* TODO: * 4 causes crashes on some files */
+ /* only malloc once */
+ if (g_vBuffers == NULL)
+ g_vBackBuf1 = g_vBuffers = mve_alloc(g_width * g_height * 8);
+ if (truecolor) {
+ g_vBackBuf2 = (unsigned short *)g_vBackBuf1 + (g_width * g_height);
+ } else {
+ g_vBackBuf2 = (unsigned char *)g_vBackBuf1 + (g_width * g_height);
+ }
+
+ memset(g_vBackBuf1, 0, g_width * g_height * 4);
+
+#ifdef DEBUG
+ fprintf(stderr, "DEBUG: w,h=%d,%d count=%d, tc=%d\n", w, h, count, truecolor);
+#endif
+
+ g_truecolor = truecolor;
+
+ return 1;
+}
+
+static int display_video_handler(unsigned char major, unsigned char minor, unsigned char *data, int len, void *context)
+{
+ if (g_destX == -1) // center it
+ g_destX = (g_screenWidth - g_width) >> 1;
+ if (g_destY == -1) // center it
+ g_destY = (g_screenHeight - g_height) >> 1;
+
+ mve_showframe(g_vBackBuf1, g_width, g_height, 0, 0,
+ g_width, g_height, g_destX, g_destY);
+
+ g_frameUpdated = 1;
+
+ return 1;
+}
+
+static int init_video_handler(unsigned char major, unsigned char minor, unsigned char *data, int len, void *context)
+{
+ short width, height;
+
+ if (video_initialized)
+ return 1; /* maybe we actually need to change width/height here? */
+ else
+ video_initialized = 1;
+
+ width = get_short(data);
+ height = get_short(data+2);
+ g_screenWidth = width;
+ g_screenHeight = height;
+
+ return 1;
+}
+
+static int video_palette_handler(unsigned char major, unsigned char minor, unsigned char *data, int len, void *context)
+{
+ short start, count;
+ unsigned char *p;
+
+ start = get_short(data);
+ count = get_short(data+2);
+
+ p = data + 4;
+
+ mve_setpalette(p - 3*start, start, count);
+
+ return 1;
+}
+
+static int video_codemap_handler(unsigned char major, unsigned char minor, unsigned char *data, int len, void *context)
+{
+ g_pCurMap = data;
+ g_nMapLength = len;
+ return 1;
+}
+
+static int video_data_handler(unsigned char major, unsigned char minor, unsigned char *data, int len, void *context)
+{
+ short nFrameHot, nFrameCold;
+ short nXoffset, nYoffset;
+ short nXsize, nYsize;
+ unsigned short nFlags;
+ unsigned char *temp;
+
+ nFrameHot = get_short(data);
+ nFrameCold = get_short(data+2);
+ nXoffset = get_short(data+4);
+ nYoffset = get_short(data+6);
+ nXsize = get_short(data+8);
+ nYsize = get_short(data+10);
+ nFlags = get_ushort(data+12);
+
+ if (nFlags & 1)
+ {
+ temp = (unsigned char *)g_vBackBuf1;
+ g_vBackBuf1 = g_vBackBuf2;
+ g_vBackBuf2 = temp;
+ }
+
+ /* convert the frame */
+ if (g_truecolor) {
+ decodeFrame16((unsigned char *)g_vBackBuf1, g_pCurMap, g_nMapLength, data+14, len-14);
+ } else {
+ decodeFrame8(g_vBackBuf1, g_pCurMap, g_nMapLength, data+14, len-14);
+ }
+
+ return 1;
+}
+
+static int end_chunk_handler(unsigned char major, unsigned char minor, unsigned char *data, int len, void *context)
+{
+ g_pCurMap=NULL;
+ return 1;
+}
+
+
+static MVESTREAM *mve = NULL;
+
+void MVE_ioCallbacks(mve_cb_Read io_read)
+{
+ mve_read = io_read;
+}
+
+void MVE_memCallbacks(mve_cb_Alloc mem_alloc, mve_cb_Free mem_free)
+{
+ mve_alloc = mem_alloc;
+ mve_free = mem_free;
+}
+
+void MVE_sfCallbacks(mve_cb_ShowFrame showframe)
+{
+ mve_showframe = showframe;
+}
+
+void MVE_palCallbacks(mve_cb_SetPalette setpalette)
+{
+ mve_setpalette = setpalette;
+}
+
+int MVE_rmPrepMovie(void *src, int x, int y, int track)
+{
+ int i;
+
+ if (mve) {
+ mve_reset(mve);
+ return 0;
+ }
+
+ mve = mve_open(src);
+
+ if (!mve)
+ return 1;
+
+ g_destX = x;
+ g_destY = y;
+
+ for (i = 0; i < 32; i++)
+ mve_set_handler(mve, i, default_seg_handler);
+
+ mve_set_handler(mve, MVE_OPCODE_ENDOFSTREAM, end_movie_handler);
+ mve_set_handler(mve, MVE_OPCODE_ENDOFCHUNK, end_chunk_handler);
+ mve_set_handler(mve, MVE_OPCODE_CREATETIMER, create_timer_handler);
+ mve_set_handler(mve, MVE_OPCODE_INITAUDIOBUFFERS, create_audiobuf_handler);
+ mve_set_handler(mve, MVE_OPCODE_STARTSTOPAUDIO, play_audio_handler);
+ mve_set_handler(mve, MVE_OPCODE_INITVIDEOBUFFERS, create_videobuf_handler);
+
+ mve_set_handler(mve, MVE_OPCODE_DISPLAYVIDEO, display_video_handler);
+ mve_set_handler(mve, MVE_OPCODE_AUDIOFRAMEDATA, audio_data_handler);
+ mve_set_handler(mve, MVE_OPCODE_AUDIOFRAMESILENCE, audio_data_handler);
+ mve_set_handler(mve, MVE_OPCODE_INITVIDEOMODE, init_video_handler);
+
+ mve_set_handler(mve, MVE_OPCODE_SETPALETTE, video_palette_handler);
+ mve_set_handler(mve, MVE_OPCODE_SETPALETTECOMPRESSED, default_seg_handler);
+
+ mve_set_handler(mve, MVE_OPCODE_SETDECODINGMAP, video_codemap_handler);
+
+ mve_set_handler(mve, MVE_OPCODE_VIDEODATA, video_data_handler);
+
+ mve_play_next_chunk(mve); /* video initialization chunk */
+ if (mve_audio_enabled)
+ mve_play_next_chunk(mve); /* audio initialization chunk */
+
+ return 0;
+}
+
+
+void MVE_getVideoSpec(MVE_videoSpec *vSpec)
+{
+ vSpec->screenWidth = g_screenWidth;
+ vSpec->screenHeight = g_screenHeight;
+ vSpec->width = g_width;
+ vSpec->height = g_height;
+ vSpec->truecolor = g_truecolor;
+}
+
+
+int MVE_rmStepMovie()
+{
+ static int init_timer=0;
+ int cont=1;
+
+ if (!timer_started)
+ timer_start();
+
+ while (cont && !g_frameUpdated) // make a "step" be a frame, not a chunk...
+ cont = mve_play_next_chunk(mve);
+ g_frameUpdated = 0;
+
+ if (!cont)
+ return MVE_ERR_EOF;
+
+ if (micro_frame_delay && !init_timer) {
+ timer_start();
+ init_timer = 1;
+ }
+
+ do_timer_wait();
+
+ return 0;
+}
+
+void MVE_rmEndMovie()
+{
+#ifdef AUDIO
+ int i;
+#endif
+
+ timer_stop();
+ timer_created = 0;
+
+#ifdef AUDIO
+ if (mve_audio_canplay) {
+ // only close audio if we opened it
+ Mix_CloseAudio();
+ mve_audio_canplay = 0;
+ }
+ for (i = 0; i < TOTAL_AUDIO_BUFFERS; i++)
+ if (mve_audio_buffers[i] != NULL)
+ mve_free(mve_audio_buffers[i]);
+ memset(mve_audio_buffers, 0, sizeof(mve_audio_buffers));
+ memset(mve_audio_buflens, 0, sizeof(mve_audio_buflens));
+ mve_audio_curbuf_curpos=0;
+ mve_audio_bufhead=0;
+ mve_audio_buftail=0;
+ mve_audio_playing=0;
+ mve_audio_canplay=0;
+ mve_audio_compressed=0;
+ audiobuf_created = 0;
+#endif
+
+ mve_free(g_vBuffers);
+ g_vBuffers = NULL;
+ g_pCurMap=NULL;
+ g_nMapLength=0;
+ videobuf_created = 0;
+ video_initialized = 0;
+
+ mve_close(mve);
+ mve = NULL;
+}
+
+
+void MVE_rmHoldMovie()
+{
+ timer_started = 0;
+}
+
+
+void MVE_sndInit(int x)
+{
+#ifdef AUDIO
+ if (x == -1)
+ mve_audio_enabled = 0;
+ else
+ mve_audio_enabled = 1;
+#endif
+}