From e1c12d4452180b9a6414158ef9fa119caedfb901 Mon Sep 17 00:00:00 2001 From: Bradley Bell Date: Wed, 12 Feb 2003 08:58:38 +0000 Subject: [PATCH] lotsa movie cleanups, added truecolor mve support, standalone mveplayer --- include/mve_audio.h | 4 - include/mvelib.h | 8 +- main/decoder16.c | 717 +++++++++++++++++ main/decoder8.c | 872 +++++++++++++++++++++ main/movie.c | 10 +- main/mve_main.c | 51 ++ main/mvelib.c | 151 +++- main/mveplay.c | 1775 ++++++++++++++----------------------------- 8 files changed, 2373 insertions(+), 1215 deletions(-) create mode 100644 main/decoder16.c create mode 100644 main/decoder8.c create mode 100644 main/mve_main.c diff --git a/include/mve_audio.h b/include/mve_audio.h index 5459b9d4..a42b60fe 100644 --- a/include/mve_audio.h +++ b/include/mve_audio.h @@ -1,10 +1,6 @@ #ifndef INCLUDED_MVE_AUDIO_H #define INCLUDED_MVE_AUDIO_H -#define MVE_AUDIO_FLAGS_STEREO 1 -#define MVE_AUDIO_FLAGS_16BIT 2 -#define MVE_AUDIO_FLAGS_COMPRESSED 4 - void mveaudio_uncompress(short *buffer, unsigned char *data, int length); #endif /* INCLUDED_MVE_AUDIO_H */ diff --git a/include/mvelib.h b/include/mvelib.h index 84b0b7a8..e5188a89 100644 --- a/include/mvelib.h +++ b/include/mvelib.h @@ -19,12 +19,14 @@ typedef struct MVEFILE /* * open a .MVE file */ -MVEFILE *mvefile_open(int filehandle); +MVEFILE *mvefile_open(const char *filename); +MVEFILE *mvefile_open_filehandle(int filehandle); /* * close a .MVE file */ void mvefile_close(MVEFILE *movie); +void mvefile_close_filehandle(MVEFILE *movie); /* * get size of next segment in chunk (-1 if no more segments in chunk) @@ -75,12 +77,14 @@ typedef struct MVESTREAM /* * open an MVE stream */ -MVESTREAM *mve_open(int filehandle); +MVESTREAM *mve_open(const char *filename); +MVESTREAM *mve_open_filehandle(int filehandle); /* * close an MVE stream */ void mve_close(MVESTREAM *movie); +void mve_close_filehandle(MVESTREAM *movie); /* * set segment type handler diff --git a/main/decoder16.c b/main/decoder16.c new file mode 100644 index 00000000..bd04fbd6 --- /dev/null +++ b/main/decoder16.c @@ -0,0 +1,717 @@ +#include +#include +#include + +extern int g_width, g_height; +extern void *g_vBackBuf1, *g_vBackBuf2; + +/* 16 bit decoding routines */ + +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 decodeFrame16(unsigned char *pFrame, unsigned char *pMap, int mapRemain, unsigned char *pData, int dataRemain) +{ + unsigned char *pOrig; + unsigned char *pOffData, *pEnd; + unsigned short offset; + 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((unsigned short **)&pFrame, op, &pData, &pOffData, &dataRemain, &i, &j); + + /* + if ((unsigned short *)pFrame < backBuf1) + fprintf(stderr, "danger! pointing out of bounds below after dispatch decoder: %d, %d (2) [%x]\n", i, j, (*pMap) >> 4); + else if ((unsigned short *)pFrame >= 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; + } + + pFrame += 7*g_width*2; + } + + if ((length-(pData-pOrig)) != 0) { + fprintf(stderr, "DEBUG: junk left over: %d,%d,%d\n", (pData-pOrig), length, (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/main/decoder8.c b/main/decoder8.c new file mode 100644 index 00000000..6323d737 --- /dev/null +++ b/main/decoder8.c @@ -0,0 +1,872 @@ +#include +#include + +extern int g_width, g_height; +extern void *g_vBackBuf1, *g_vBackBuf2; + +/* 8 bit decoding routines */ + +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 + (g_vBackBuf2 - 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 + (g_vBackBuf2 - 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 + (g_vBackBuf2 - 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/main/movie.c b/main/movie.c index ad894592..0af82bae 100644 --- a/main/movie.c +++ b/main/movie.c @@ -1,4 +1,4 @@ -/* $Id: movie.c,v 1.14 2003-01-09 00:57:42 btb Exp $ */ +/* $Id: movie.c,v 1.15 2003-02-12 08:58:38 btb Exp $ */ /* THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO @@ -17,7 +17,7 @@ COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED. #endif #ifdef RCS -static char rcsid[] = "$Id: movie.c,v 1.14 2003-01-09 00:57:42 btb Exp $"; +static char rcsid[] = "$Id: movie.c,v 1.15 2003-02-12 08:58:38 btb Exp $"; #endif #define DEBUG_LEVEL CON_NORMAL @@ -282,7 +282,7 @@ int RunMovie(char *filename, int hires_flag, int must_have,int dx,int dy) FontHires = FontHiresAvailable && hires_flag; - mve = mve_open(filehndl); + mve = mve_open_filehandle(filehndl); mve_bitmap = gr_create_bitmap(w, h); // w, h must match the mve exactly! @@ -364,7 +364,7 @@ int RotateRobot() mveplay_shutdownMovie(Robo_mve); mve_close(Robo_mve); lseek(RoboFile, Robo_filepos, SEEK_SET); - Robo_mve = mve_open(RoboFile); + Robo_mve = mve_open_filehandle(RoboFile); mveplay_initializeMovie(Robo_mve, Robo_bitmap); } @@ -394,7 +394,7 @@ int InitRobotMovie(char *filename) Robo_filepos = lseek(RoboFile, 0, SEEK_CUR); - Robo_mve = mve_open(RoboFile); + Robo_mve = mve_open_filehandle(RoboFile); Robo_bitmap = gr_create_bitmap(MenuHires?320:160, MenuHires?200:88); diff --git a/main/mve_main.c b/main/mve_main.c new file mode 100644 index 00000000..595175ff --- /dev/null +++ b/main/mve_main.c @@ -0,0 +1,51 @@ +#include + +#include "mvelib.h" + +extern int g_spdFactorNum; + +void initializeMovie(MVESTREAM *mve); +void playMovie(MVESTREAM *mve); +void shutdownMovie(MVESTREAM *mve); + +static void usage(void) +{ + fprintf(stderr, "usage: mveplay filename\n"); + exit(1); +} + +static int doPlay(const char *filename) +{ + MVESTREAM *mve = mve_open(filename); + if (mve == NULL) + { + fprintf(stderr, "can't open MVE file '%s'\n", filename); + return 1; + } + + initializeMovie(mve); + playMovie(mve); + shutdownMovie(mve); + + mve_close(mve); + + return 0; +} + +int main(int c, char **v) +{ + if (c != 2 && c != 3) + usage(); + + if (c == 3) + g_spdFactorNum = atoi(v[2]); + + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) + { + fprintf(stderr, "Couldn't initialize SDL: %s\n",SDL_GetError()); + exit(1); + } + atexit(SDL_Quit); + + return doPlay(v[1]); +} diff --git a/main/mvelib.c b/main/mvelib.c index d509777a..54930821 100644 --- a/main/mvelib.c +++ b/main/mvelib.c @@ -1,5 +1,8 @@ #include // for mem* functions #include +#include +#include +#include #include "mvelib.h" @@ -12,13 +15,16 @@ static const short MVE_HDRCONST3 = 0x1133; * 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, int filehandle); +static void _mvefile_free_filehandle(MVEFILE *movie); +static int _mvefile_open(MVEFILE *movie, const char *filename); +static int _mvefile_open_filehandle(MVEFILE *movie, int filehandle); 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); @@ -28,7 +34,9 @@ static int _mvefile_fetch_next_chunk(MVEFILE *movie); */ static MVESTREAM *_mvestream_alloc(void); static void _mvestream_free(MVESTREAM *movie); -static int _mvestream_open(MVESTREAM *movie, int filehandle); +static void _mvestream_free_filehandle(MVESTREAM *movie); +static int _mvestream_open(MVESTREAM *movie, const char *filename); +static int _mvestream_open_filehandle(MVESTREAM *movie, int filehandle); /************************************************************ * public MVEFILE functions @@ -37,13 +45,13 @@ static int _mvestream_open(MVESTREAM *movie, int filehandle); /* * open an MVE file */ -MVEFILE *mvefile_open(filehandle) +MVEFILE *mvefile_open(const char *filename) { MVEFILE *file; /* create the file */ file = _mvefile_alloc(); - if (! _mvefile_open(file, filehandle)) + if (! _mvefile_open(file, filename)) { _mvefile_free(file); return NULL; @@ -65,6 +73,34 @@ MVEFILE *mvefile_open(filehandle) return file; } +MVEFILE *mvefile_open_filehandle(int filehandle) +{ + MVEFILE *file; + + /* create the file */ + file = _mvefile_alloc(); + if (! _mvefile_open_filehandle(file, filehandle)) + { + _mvefile_free_filehandle(file); + return NULL; + } + + /* initialize the file */ + _mvefile_set_buffer_size(file, 1024); + + /* verify the file's header */ + if (! _mvefile_read_header(file)) + { + _mvefile_free_filehandle(file); + return NULL; + } + + /* now, prefetch the next chunk */ + _mvefile_fetch_next_chunk(file); + + return file; +} + /* * close a MVE file */ @@ -73,6 +109,11 @@ void mvefile_close(MVEFILE *movie) _mvefile_free(movie); } +void mvefile_close_filehandle(MVEFILE *movie) +{ + _mvefile_free_filehandle(movie); +} + /* * get the size of the next segment */ @@ -157,7 +198,7 @@ void mvefile_advance_segment(MVEFILE *movie) /* else, advance to next segment */ movie->next_segment += - (4 + _mve_get_short(movie->cur_chunk + movie->next_segment)); + (4 + _mve_get_ushort(movie->cur_chunk + movie->next_segment)); } /* @@ -175,7 +216,7 @@ int mvefile_fetch_next_chunk(MVEFILE *movie) /* * open an MVE stream */ -MVESTREAM *mve_open(int filehandle) +MVESTREAM *mve_open(const char *filename) { MVESTREAM *movie; @@ -183,7 +224,7 @@ MVESTREAM *mve_open(int filehandle) movie = _mvestream_alloc(); /* open */ - if (! _mvestream_open(movie, filehandle)) + if (! _mvestream_open(movie, filename)) { _mvestream_free(movie); return NULL; @@ -192,6 +233,23 @@ MVESTREAM *mve_open(int filehandle) return movie; } +MVESTREAM *mve_open_filehandle(int filehandle) +{ + MVESTREAM *movie; + + /* allocate */ + movie = _mvestream_alloc(); + + /* open */ + if (! _mvestream_open_filehandle(movie, filehandle)) + { + _mvestream_free_filehandle(movie); + return NULL; + } + + return movie; +} + /* * close an MVE stream */ @@ -200,6 +258,11 @@ void mve_close(MVESTREAM *movie) _mvestream_free(movie); } +void mve_close_filehandle(MVESTREAM *movie) +{ + _mvestream_free_filehandle(movie); +} + /* * set segment type handler */ @@ -263,7 +326,7 @@ int mve_play_next_chunk(MVESTREAM *movie) static MVEFILE *_mvefile_alloc(void) { MVEFILE *file = (MVEFILE *)malloc(sizeof(MVEFILE)); - file->stream = 0; + file->stream = -1; file->cur_chunk = NULL; file->buf_size = 0; file->cur_fill = 0; @@ -278,7 +341,28 @@ static MVEFILE *_mvefile_alloc(void) static void _mvefile_free(MVEFILE *movie) { /* free the stream */ - movie->stream = 0; + if (movie->stream != -1) + close(movie->stream); + movie->stream = -1; + + /* free the buffer */ + if (movie->cur_chunk) + 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 */ + free(movie); +} + +static void _mvefile_free_filehandle(MVEFILE *movie) +{ + /* free the stream */ + movie->stream = -1; /* free the buffer */ if (movie->cur_chunk) @@ -297,9 +381,20 @@ static void _mvefile_free(MVEFILE *movie) /* * open the file stream in thie object */ -static int _mvefile_open(MVEFILE *file, int filehandle) +static int _mvefile_open(MVEFILE *file, const char *filename) +{ + file->stream = open(filename, O_RDONLY); + if (file->stream == -1) + return 0; + + return 1; +} + +static int _mvefile_open_filehandle(MVEFILE *file, int filehandle) { file->stream = filehandle; + if (file->stream == -1) + return 0; return 1; } @@ -312,7 +407,7 @@ static int _mvefile_read_header(MVEFILE *movie) unsigned char buffer[26]; /* check the file is open */ - if (movie->stream == 0) + if (movie->stream == -1) return 0; /* check the file is long enough */ @@ -345,7 +440,7 @@ static void _mvefile_set_buffer_size(MVEFILE *movie, int buf_size) /* allocate new buffer */ new_len = 100 + buf_size; - new_buffer = malloc(new_len); + new_buffer = (unsigned char *)malloc(new_len); /* copy old data */ if (movie->cur_chunk && movie->cur_fill) @@ -369,7 +464,7 @@ static int _mvefile_fetch_next_chunk(MVEFILE *movie) unsigned short length; /* fail if not open */ - if (movie->stream == 0) + if (movie->stream == -1) return 0; /* fail if we can't read the next segment descriptor */ @@ -398,6 +493,13 @@ static short _mve_get_short(unsigned char *data) 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 */ @@ -429,12 +531,31 @@ static void _mvestream_free(MVESTREAM *movie) memset(movie->handlers, 0, sizeof(movie->handlers)); } +static void _mvestream_free_filehandle(MVESTREAM *movie) +{ + /* close MVEFILE */ + if (movie->movie) + mvefile_close_filehandle(movie->movie); + movie->movie = NULL; + + /* clear context and handlers */ + movie->context = NULL; + memset(movie->handlers, 0, sizeof(movie->handlers)); +} + /* * open an MVESTREAM object */ -static int _mvestream_open(MVESTREAM *movie, int filehandle) +static int _mvestream_open(MVESTREAM *movie, const char *filename) +{ + movie->movie = mvefile_open(filename); + + return (movie->movie == NULL) ? 0 : 1; +} + +static int _mvestream_open_filehandle(MVESTREAM *movie, int filehandle) { - movie->movie = mvefile_open(filehandle); + movie->movie = mvefile_open_filehandle(filehandle); return (movie->movie == NULL) ? 0 : 1; } diff --git a/main/mveplay.c b/main/mveplay.c index beb33eab..dd73b04f 100644 --- a/main/mveplay.c +++ b/main/mveplay.c @@ -1,4 +1,6 @@ -/* $Id: mveplay.c,v 1.9 2003-01-15 02:44:31 btb Exp $ */ +/* $Id: mveplay.c,v 1.10 2003-02-12 08:58:38 btb Exp $ */ +#define AUDIO +//#define DEBUG #ifdef HAVE_CONFIG_H #include @@ -15,15 +17,16 @@ #include +#include "mvelib.h" +#include "mve_audio.h" + +#ifndef STANDALONE #include "mveplay.h" #include "error.h" #include "u_mem.h" -#include "mvelib.h" -#include "mve_audio.h" #include "gr.h" #include "palette.h" -#include "fix.h" -#include "timer.h" +#endif #ifndef MIN #define MIN(a,b) ((a)<(b)?(a):(b)) @@ -35,46 +38,59 @@ #define MVE_OPCODE_INITAUDIOBUFFERS 0x03 #define MVE_OPCODE_STARTSTOPAUDIO 0x04 #define MVE_OPCODE_INITVIDEOBUFFERS 0x05 -#define MVE_OPCODE_UNKNOWN 0x06 + #define MVE_OPCODE_DISPLAYVIDEO 0x07 #define MVE_OPCODE_AUDIOFRAMEDATA 0x08 #define MVE_OPCODE_AUDIOFRAMESILENCE 0x09 #define MVE_OPCODE_INITVIDEOMODE 0x0A -#define MVE_OPCODE_CREATEGRADIENT 0x0B + #define MVE_OPCODE_SETPALETTE 0x0C #define MVE_OPCODE_SETPALETTECOMPRESSED 0x0D -#define MVE_OPCODE_UNKNOWN2 0x0E + #define MVE_OPCODE_SETDECODINGMAP 0x0F -#define MVE_OPCODE_UNKNOWN3 0x10 + #define MVE_OPCODE_VIDEODATA 0x11 -#define MVE_OPCODE_UNKNOWN4 0x12 -#define MVE_OPCODE_UNKNOWN5 0x13 -#define MVE_OPCODE_UNKNOWN6 0x14 -#define MVE_OPCODE_UNKNOWN7 0x15 -//#define DEBUG +#define MVE_AUDIO_FLAGS_STEREO 1 +#define MVE_AUDIO_FLAGS_16BIT 2 +#define MVE_AUDIO_FLAGS_COMPRESSED 4 -static int g_spdFactorNum=0; +int g_spdFactorNum=0; static int g_spdFactorDenom=10; +static int playing = 1; + +void initializeMovie(MVESTREAM *mve); +void playMovie(MVESTREAM *mve); +void shutdownMovie(MVESTREAM *mve); static short get_short(unsigned char *data) { - short value; - value = data[0] | (data[1] << 8); - return value; + 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; + 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) { - Warning("mveplay: unknown chunk type %02x/%02x\n", major, minor); - return 1; + unhandled_chunks[major<<8|minor]++; + //fprintf(stderr, "unknown chunk type %02x/%02x\n", major, minor); + return 1; } /************************* @@ -82,7 +98,7 @@ static int default_seg_handler(unsigned char major, unsigned char minor, unsigne *************************/ static int end_movie_handler(unsigned char major, unsigned char minor, unsigned char *data, int len, void *context) { - return 0; + return 0; } /************************* @@ -92,68 +108,91 @@ static int end_movie_handler(unsigned char major, unsigned char minor, unsigned /* * timer variables */ -static fix fix_frame_delay = F0_0; -static int timer_started = 0; -static fix timer_expire = F0_0; +static int micro_frame_delay=0; +static int timer_started=0; +static struct timeval timer_expire = {0, 0}; static int create_timer_handler(unsigned char major, unsigned char minor, unsigned char *data, int len, void *context) { - long long temp; - int 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; - } - fix_frame_delay = approx_usec_to_fsec(micro_frame_delay); - - return 1; + __extension__ long long temp; + 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 = F0_0; + timer_expire.tv_sec = 0; + timer_expire.tv_usec = 0; timer_started = 0; } static void timer_start(void) { - timer_expire = timer_get_fixed_seconds(); - - timer_expire += fix_frame_delay; - - timer_started=1; + int nsec=0; + gettimeofday(&timer_expire, NULL); + timer_expire.tv_usec += micro_frame_delay; + if (timer_expire.tv_usec > 1000000) + { + nsec = timer_expire.tv_usec / 1000000; + timer_expire.tv_sec += nsec; + timer_expire.tv_usec -= nsec*1000000; + } + timer_started=1; } static void do_timer_wait(void) { - fix ts, tv; - - if (! timer_started) - return; + int nsec=0; + struct timespec ts; + struct timeval tv; + if (! timer_started) + return; - tv = timer_get_fixed_seconds(); - - if (tv > timer_expire) + gettimeofday(&tv, NULL); + if (tv.tv_sec > timer_expire.tv_sec) + goto end; + else if (tv.tv_sec == timer_expire.tv_sec && tv.tv_usec >= timer_expire.tv_usec) goto end; - ts = timer_expire - tv; - - timer_delay(ts); + ts.tv_sec = timer_expire.tv_sec - tv.tv_sec; + ts.tv_nsec = 1000 * (timer_expire.tv_usec - tv.tv_usec); + if (ts.tv_nsec < 0) + { + ts.tv_nsec += 1000000000UL; + --ts.tv_sec; + } +#ifdef __CYGWIN__ + usleep(ts.tv_sec * 1000000 + ts.tv_nsec / 1000); +#else + if (nanosleep(&ts, NULL) == -1 && errno == EINTR) + exit(1); +#endif -end: - timer_expire += fix_frame_delay; + end: + timer_expire.tv_usec += micro_frame_delay; + if (timer_expire.tv_usec > 1000000) + { + nsec = timer_expire.tv_usec / 1000000; + timer_expire.tv_sec += nsec; + timer_expire.tv_usec -= nsec*1000000; + } } /************************* * audio handlers *************************/ +#ifdef AUDIO #define TOTAL_AUDIO_BUFFERS 64 -static void mve_audio_callback(void *userdata, Uint8 *stream, int len); +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; @@ -164,324 +203,553 @@ static int mve_audio_canplay=0; static int mve_audio_compressed=0; static SDL_AudioSpec *mve_audio_spec=NULL; -static void mve_audio_callback(void *userdata, Uint8 *stream, int len) -{ - int total=0; - int length; - if (mve_audio_bufhead == mve_audio_buftail) - return /* 0 */; - -#ifdef DEBUG - fprintf(stderr, "+ <%d (%d), %d, %d>\n", mve_audio_bufhead, mve_audio_curbuf_curpos, mve_audio_buftail, len); -#endif - - 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; - __builtin_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 */ - d_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; - } - -#ifdef DEBUG - fprintf(stderr, "= <%d (%d), %d, %d>: %d\n", mve_audio_bufhead, mve_audio_curbuf_curpos, mve_audio_buftail, len, total); -#endif -/* return total; */ - - if (len != 0 /* ospace remaining */ - && mve_audio_bufhead != mve_audio_buftail) /* buffers remaining */ - { - __builtin_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 */ - { - d_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; - } - } - -#ifdef DEBUG - fprintf(stderr, "- <%d (%d), %d, %d>\n", mve_audio_bufhead, mve_audio_curbuf_curpos, mve_audio_buftail, len); -#endif +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 */ + 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 */ + { + 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) { - int flags; - int sample_rate; - int desired_buffer; +#ifdef AUDIO + int flags; + int sample_rate; + int desired_buffer; -#ifdef DEBUG - fprintf(stderr, "creating audio buffers\n"); -#endif + int stereo; + int bitsize; + int compressed; - flags = get_short(data + 2); - sample_rate = get_short(data + 4); - desired_buffer = get_int(data + 6); + int format; -#ifdef DEBUG - fprintf(stderr, "stereo=%d 16bit=%d compressed=%d sample_rate=%d desired_buffer=%d\n", - flags & 1, (flags >> 1) & 1, (flags >> 2) & 1, sample_rate, desired_buffer); -#endif + flags = get_ushort(data + 2); + sample_rate = get_ushort(data + 4); + desired_buffer = get_int(data + 6); - mve_audio_compressed = flags & MVE_AUDIO_FLAGS_COMPRESSED; - if (!mve_audio_spec) - mve_audio_spec = (SDL_AudioSpec *)d_malloc(sizeof(SDL_AudioSpec)); - mve_audio_spec->freq = sample_rate; -#ifdef WORDS_BIGENDIAN - mve_audio_spec->format = (flags & MVE_AUDIO_FLAGS_16BIT)?AUDIO_S16MSB:AUDIO_U8; -#else - mve_audio_spec->format = (flags & MVE_AUDIO_FLAGS_16BIT)?AUDIO_S16LSB:AUDIO_U8; -#endif - mve_audio_spec->channels = (flags & MVE_AUDIO_FLAGS_STEREO)?2:1; - mve_audio_spec->samples = 32768; - mve_audio_spec->callback = mve_audio_callback; - mve_audio_spec->userdata = NULL; - if (SDL_OpenAudio(mve_audio_spec, NULL) >= 0) - { -#ifdef DEBUG - fprintf(stderr, " success\n"); -#endif - mve_audio_canplay = 1; - } - else - { -#ifdef DEBUG - fprintf(stderr, " failure : %s\n", SDL_GetError()); -#endif - Warning("mveplay: failed to create audio buffers\n"); - mve_audio_canplay = 0; - } + stereo = (flags & MVE_AUDIO_FLAGS_STEREO) ? 1 : 0; + bitsize = (flags & MVE_AUDIO_FLAGS_16BIT) ? 1 : 0; - memset(mve_audio_buffers, 0, sizeof(mve_audio_buffers)); - memset(mve_audio_buflens, 0, sizeof(mve_audio_buflens)); + if (minor > 0) { + compressed = flags & MVE_AUDIO_FLAGS_COMPRESSED ? 1 : 0; + } else { + compressed = 0; + } - return 1; + mve_audio_compressed = compressed; + + if (bitsize == 1) { + format = AUDIO_S16LSB; + } 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); + + mve_audio_spec = (SDL_AudioSpec *)malloc(sizeof(SDL_AudioSpec)); + mve_audio_spec->freq = sample_rate; + mve_audio_spec->format = format; + mve_audio_spec->channels = (stereo) ? 2 : 1; + mve_audio_spec->samples = 4096; + mve_audio_spec->callback = mve_audio_callback; + mve_audio_spec->userdata = NULL; + if (SDL_OpenAudio(mve_audio_spec, NULL) >= 0) + { + fprintf(stderr, " success\n"); + mve_audio_canplay = 1; + } + else + { + fprintf(stderr, " failure : %s\n", SDL_GetError()); + mve_audio_canplay = 0; + } + + 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) { - if (mve_audio_canplay && !mve_audio_playing && mve_audio_bufhead != mve_audio_buftail) - { - SDL_PauseAudio(0); - mve_audio_playing = 1; - } - - return 1; +#ifdef AUDIO + if (mve_audio_canplay && !mve_audio_playing && mve_audio_bufhead != mve_audio_buftail) + { + SDL_PauseAudio(0); + 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) { - static const int selected_chan=1; - int chan; - int nsamp; - if (mve_audio_canplay) - { - if (mve_audio_playing) - SDL_LockAudio(); - - chan = get_short(data + 2); - nsamp = get_short(data + 4); - if (chan & selected_chan) - { - mve_audio_buflens[mve_audio_buftail] = nsamp; - if (mve_audio_buffers[mve_audio_buftail]) - d_free(mve_audio_buffers[mve_audio_buftail]); - mve_audio_buffers[mve_audio_buftail] = (short *)d_malloc(nsamp+4); - if (major == MVE_OPCODE_AUDIOFRAMEDATA) - if (mve_audio_compressed) - mveaudio_uncompress(mve_audio_buffers[mve_audio_buftail], data, -1); /* XXX */ - else - memcpy(mve_audio_buffers[mve_audio_buftail], data + 6, nsamp); - else - // silence - 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) - Warning("mveplay: d'oh! buffer ring overrun (%d)\n", mve_audio_bufhead); - } - - if (mve_audio_playing) - SDL_UnlockAudio(); - } - - return 1; +#ifdef AUDIO + static const int selected_chan=1; + int chan; + int nsamp; + if (mve_audio_canplay) + { + if (mve_audio_playing) + SDL_LockAudio(); + + 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 *)malloc(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 *)malloc(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 *)malloc(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); + } + + if (mve_audio_playing) + SDL_UnlockAudio(); + } +#endif + + return 1; } /************************* * video handlers *************************/ +int g_width, g_height; +void *g_vBackBuf1, *g_vBackBuf2 = NULL; + +#ifdef STANDALONE +static SDL_Surface *g_screen; +#else static grs_bitmap *g_screen; +#endif static int g_screenWidth, g_screenHeight; -static int g_width, g_height; static unsigned char g_palette[768]; -static unsigned char *g_vBackBuf1, *g_vBackBuf2 = NULL; static unsigned char *g_pCurMap=NULL; static int g_nMapLength=0; +static int g_truecolor; +static int g_palette_changed = 0; static int create_videobuf_handler(unsigned char major, unsigned char minor, unsigned char *data, int len, void *context) { - short w, h; - w = get_short(data); - h = get_short(data+2); - g_width = w << 3; - g_height = h << 3; -#ifdef DEBUG - fprintf(stderr, "g_width, g_height: %d, %d\n", g_width, g_height); + short w, h; + short count, truecolor; + + 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; + +#ifndef STANDALONE + Assert((g_width == g_screen->bm_w) && (g_height == g_screen->bm_h)); #endif - Assert((g_width == g_screen->bm_w) && (g_height == g_screen->bm_h)); - if (!g_vBackBuf1) - g_vBackBuf1 = d_malloc(g_width * g_height); - if (!g_vBackBuf2) - g_vBackBuf2 = d_malloc(g_width * g_height); - memset(g_vBackBuf1, 0, g_width * g_height); - memset(g_vBackBuf2, 0, g_width * g_height); - return 1; + + /* TODO: * 4 causes crashes on some files */ + g_vBackBuf1 = malloc(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); + + fprintf(stderr, "DEBUG: w,h=%d,%d count=%d, tc=%d\n", w, h, count, truecolor); + + g_truecolor = truecolor; + + return 1; +} + +#ifdef STANDALONE +static int do_sdl_events() +{ + SDL_Event event; + int retr = 0; + while (SDL_PollEvent(&event)) { + switch(event.type) { + case SDL_QUIT: + //exit(0); + playing=0; + break; + case SDL_KEYDOWN: + if (event.key.keysym.sym == SDLK_ESCAPE) + //exit(0); + playing=0; + break; + case SDL_KEYUP: + retr = 1; + break; + case SDL_MOUSEBUTTONDOWN: + /* + if (event.button.button == SDL_BUTTON_LEFT) { + printf("GRID: %d,%d (pix:%d,%d)\n", + event.button.x / 16, event.button.y / 8, + event.button.x, event.button.y); + } + */ + break; + default: + break; + } + } + + return retr; +} + +static void ConvertAndDraw() +{ + int i; + unsigned char *pal = g_palette; + unsigned char *pDest; + unsigned char *pixels = g_vBackBuf1; + SDL_Surface *screenSprite, *initSprite; + SDL_Rect renderArea; + int x, y; + + initSprite = SDL_CreateRGBSurface(SDL_SWSURFACE, g_width, g_height, g_truecolor?16:8, 0x7C00, 0x03E0, 0x001F, 0); + + if (!g_truecolor) { + for(i = 0; i < 256; i++) { + initSprite->format->palette->colors[i].r = (*pal++) << 2; + initSprite->format->palette->colors[i].g = (*pal++) << 2; + initSprite->format->palette->colors[i].b = (*pal++) << 2; + initSprite->format->palette->colors[i].unused = 0; + } + } + + pDest = initSprite->pixels; + + if (0 /*g_truecolor*/) { + + unsigned short *pSrcs, *pDests; + + pSrcs = (unsigned short *)pixels; + pDests = (unsigned short *)pDest; + + for (y=0; ypitch; + } + } + + screenSprite = SDL_DisplayFormat(initSprite); + SDL_FreeSurface(initSprite); + + if (g_screenWidth > screenSprite->w) + x = (g_screenWidth - screenSprite->w) >> 1; + else + x=0; + if (g_screenHeight > screenSprite->h) + y = (g_screenHeight - screenSprite->h) >> 1; + else + y=0; + renderArea.x = x; + renderArea.y = y; + renderArea.w = MIN(g_screenWidth - x, screenSprite->w); + renderArea.h = MIN(g_screenHeight - y, screenSprite->h); + SDL_BlitSurface(screenSprite, NULL, g_screen, &renderArea); + + SDL_FreeSurface(screenSprite); } +#endif static int display_video_handler(unsigned char major, unsigned char minor, unsigned char *data, int len, void *context) { - memcpy(g_screen->bm_data, g_vBackBuf1, g_width * g_height); +#ifdef STANDALONE + ConvertAndDraw(); + + SDL_Flip(g_screen); + + do_sdl_events(); +#else + if (g_palette_changed) + { + gr_palette_load(g_palette); + g_palette_changed = 0; + } - return 1; + memcpy(g_screen->bm_data, g_vBackBuf1, g_width * g_height); +#endif + + return 1; } static int init_video_handler(unsigned char major, unsigned char minor, unsigned char *data, int len, void *context) { - short width, height; - width = get_short(data); - height = get_short(data+2); - g_screenWidth = width; - g_screenHeight = height; - memset(g_palette, 0, 765); + short width, height; + width = get_short(data); + height = get_short(data+2); +#ifdef STANDALONE + g_screen = SDL_SetVideoMode(width, height, 16, SDL_ANYFORMAT|SDL_DOUBLEBUF); +#endif + g_screenWidth = width; + g_screenHeight = height; + memset(g_palette, 0, 765); // 255 needs to default to white, for subtitles, etc memset(g_palette + 765, 63, 3); - return 1; + return 1; } static int video_palette_handler(unsigned char major, unsigned char minor, unsigned char *data, int len, void *context) { - short start, count; - start = get_short(data); - count = get_short(data+2); - memcpy(g_palette + 3*start, data+4, 3*count); - gr_palette_load(g_palette); + short start, count; + start = get_short(data); + count = get_short(data+2); + memcpy(g_palette + 3*start, data+4, 3*count); + g_palette_changed = 1; - return 1; + 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; + g_pCurMap = data; + g_nMapLength = len; + return 1; } -static void decodeFrame(unsigned char *pFrame, unsigned char *pMap, int mapRemain, unsigned char *pData, int dataRemain); +void decodeFrame16(unsigned char *pFrame, unsigned char *pMap, int mapRemain, unsigned char *pData, int dataRemain); +void decodeFrame8(unsigned char *pFrame, unsigned char *pMap, int mapRemain, unsigned char *pData, int dataRemain); 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; - 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_short(data+12); - - if (nFlags & 1) - { - temp = g_vBackBuf1; - g_vBackBuf1 = g_vBackBuf2; - g_vBackBuf2 = temp; - } - - /* convert the frame */ - decodeFrame(g_vBackBuf1, g_pCurMap, g_nMapLength, data+14, len-14); - - return 1; + 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; + g_pCurMap=NULL; + return 1; } -/************************************************************ - * public mveplay functions - ************************************************************/ +void initializeMovie(MVESTREAM *mve) +{ + int i; + + for (i = 0; i < 32; i++) + mve_set_handler(mve, i, default_seg_handler); + + memset(unhandled_chunks, 0, 32*256); + + 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, video_palette_handler); + mve_set_handler(mve, MVE_OPCODE_SETDECODINGMAP, video_codemap_handler); + + mve_set_handler(mve, MVE_OPCODE_VIDEODATA, video_data_handler); +} + +void playMovie(MVESTREAM *mve) +{ + int init_timer=0; + int cont=1; + while (cont && playing) + { + cont = mve_play_next_chunk(mve); + if (micro_frame_delay && !init_timer) + { + timer_start(); + init_timer = 1; + } + + do_timer_wait(); + } +} + +void shutdownMovie(MVESTREAM *mve) +{ + int i; + for (i = 0; i < 32*256; i++) { + if (unhandled_chunks[i]) { + fprintf(stderr, "unhandled chunks of type %02x/%02x: %d\n", i>>8, i&0xFF, unhandled_chunks[i]); + } + } +} + +#ifndef STANDALONE void mveplay_initializeMovie(MVESTREAM *mve, grs_bitmap *mve_bitmap) { - g_screen = mve_bitmap; - - 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_UNKNOWN, default_seg_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_CREATEGRADIENT, default_seg_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_UNKNOWN2, default_seg_handler); - mve_set_handler(mve, MVE_OPCODE_SETDECODINGMAP, video_codemap_handler); - mve_set_handler(mve, MVE_OPCODE_UNKNOWN3, default_seg_handler); - mve_set_handler(mve, MVE_OPCODE_VIDEODATA, video_data_handler); - mve_set_handler(mve, MVE_OPCODE_UNKNOWN4, default_seg_handler); - //mve_set_handler(mve, MVE_OPCODE_UNKNOWN5, default_seg_handler); - mve_set_handler(mve, MVE_OPCODE_UNKNOWN6, default_seg_handler); - //mve_set_handler(mve, MVE_OPCODE_UNKNOWN7, default_seg_handler); + int i; + + g_screen = mve_bitmap; + + 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); + } int mveplay_stepMovie(MVESTREAM *mve) { - static int init_timer=0; - int cont=1; + static int init_timer=0; + int cont=1; if (!timer_started) timer_start(); cont = mve_play_next_chunk(mve); - if (fix_frame_delay && !init_timer) { + if (micro_frame_delay && !init_timer) { timer_start(); init_timer = 1; } @@ -498,20 +766,20 @@ void mveplay_restartTimer(MVESTREAM *mve) void mveplay_shutdownMovie(MVESTREAM *mve) { - int i; + int i; timer_stop(); - - if (mve_audio_canplay) { - // only close audio if we opened it - SDL_CloseAudio(); - mve_audio_canplay = 0; - } - for (i = 0; i < TOTAL_AUDIO_BUFFERS; i++) - if (mve_audio_buffers[i] != NULL) - d_free(mve_audio_buffers[i]); - memset(mve_audio_buffers, 0, sizeof(mve_audio_buffers)); - memset(mve_audio_buflens, 0, sizeof(mve_audio_buflens)); + + SDL_CloseAudio(); + if (mve_audio_canplay) { + // only close audio if we opened it + mve_audio_canplay = 0; + } + for (i = 0; i < TOTAL_AUDIO_BUFFERS; i++) + if (mve_audio_buffers[i] != NULL) + 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; @@ -519,885 +787,14 @@ void mveplay_shutdownMovie(MVESTREAM *mve) mve_audio_canplay=0; mve_audio_compressed=0; if (mve_audio_spec) - d_free(mve_audio_spec); + free(mve_audio_spec); mve_audio_spec=NULL; - d_free(g_vBackBuf1); + free(g_vBackBuf1); g_vBackBuf1 = NULL; - d_free(g_vBackBuf2); + free(g_vBackBuf2); g_vBackBuf2 = NULL; g_pCurMap=NULL; g_nMapLength=0; } - - -/************************************************************ - * internal decoding functions - ************************************************************/ - -static void dispatchDecoder(unsigned char **pFrame, unsigned char codeType, unsigned char **pData, int *pDataRemain, int *curXb, int *curYb); - -static void decodeFrame(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= g_vBackBuf1 + g_width*g_height) - Warning("mveplay: 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 < g_vBackBuf1) - Warning("mveplay: danger! pointing out of bounds below after dispatch decoder: %d, %d (2) [%x]\n", i, j, (*pMap) >> 4); - else if (pFrame >= g_vBackBuf1 + g_width*g_height) - Warning("mveplay: 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 pattern, unsigned char *p) -{ - unsigned char mask=0x01; - - while (mask != 0) - { - *pFrame++ = p[(mask & pattern) ? 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 pattern, unsigned char *p) -{ - unsigned char pel; - unsigned char mask=0x1; - - while (mask != 0x10) - { - pel = p[(mask & pattern) ? 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; - int 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 + (g_vBackBuf2 - 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 + (g_vBackBuf2 - 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 + (g_vBackBuf2 - 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; - } -} +#endif -- 2.39.2