From ef60434f725fdf3e95de75fa88d584cc751eefb0 Mon Sep 17 00:00:00 2001 From: "Azamat H. Hackimov" Date: Wed, 1 May 2024 14:33:32 +0300 Subject: [PATCH] Add D2X implementation of MVE Added D2X implementation licensed under GPLv3 terms. Cut from https://github.com/btb/d2x/tree/c030c4531ad19f1658ea9635ff4ee6861e1d15e0 --- THIRD_PARTY.md | 34 ++ libmve/CMakeLists.txt | 20 +- libmve/decoder16.c | 716 ++++++++++++++++++++++++++++++++++ libmve/decoder8.c | 871 ++++++++++++++++++++++++++++++++++++++++++ libmve/decoders.h | 16 + libmve/libmve.h | 44 +++ libmve/mve_audio.c | 55 +++ libmve/mve_audio.h | 6 + libmve/mvelib.c | 520 +++++++++++++++++++++++++ libmve/mvelib.h | 113 ++++++ libmve/mveplay.c | 749 ++++++++++++++++++++++++++++++++++++ 11 files changed, 3134 insertions(+), 10 deletions(-) create mode 100644 libmve/decoder16.c create mode 100644 libmve/decoder8.c create mode 100644 libmve/decoders.h create mode 100644 libmve/libmve.h create mode 100644 libmve/mve_audio.c create mode 100644 libmve/mve_audio.h create mode 100644 libmve/mvelib.c create mode 100644 libmve/mvelib.h create mode 100644 libmve/mveplay.c 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 +}