Add D2X implementation of MVE

Added D2X implementation licensed under GPLv3 terms.
Cut from c030c4531a
This commit is contained in:
Azamat H. Hackimov
2024-05-01 14:33:32 +03:00
parent d85aa5c158
commit ef60434f72
11 changed files with 3134 additions and 10 deletions

View File

@@ -28,6 +28,40 @@ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
```
## libmve
libmve - Interplay MVE implementation from D2X project. https://github.com/btb/d2x
[@c030c453](https://github.com/btb/d2x/tree/c030c4531ad19f1658ea9635ff4ee6861e1d15e0)
* libmve/decoder8.c
* libmve/decoder16.c
* libmve/decoders.h
* libmve/mve_audio.c
* libmve/mve_audio.h
* libmve/mvelib.c
* libmve/mvelib.h
* libmve/mveplay.c
The libmve code is licensed under GPL-3 license.
```
Copyright (C) 2002-2024 D2X Project
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
```
## stb
Single-file public domain (or MIT licensed) libraries for C/C++. https://github.com/nothings/stb

View File

@@ -1,16 +1,16 @@
set(HEADERS
mvegfx.h
mvelibi.h
mvelibl.h
platform.h
snd8to16.h
SystemInterfaces.h)
set(CPPS
lnxdsound.cpp
mveasm.cpp
mvelibl.cpp
platform.cpp
lnxdsound.cpp)
add_library(libmve STATIC ${HEADERS} ${CPPS} ${PLATFORM_CPPS})
# d2x implementation
decoder8.c
decoder16.c
mve_audio.c
mvelib.c
mveplay.c
)
add_library(libmve STATIC ${CPPS})
target_link_libraries(libmve PRIVATE SDL2::SDL2)

716
libmve/decoder16.c Normal file
View File

@@ -0,0 +1,716 @@
/* 16 bit decoding routines */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "decoders.h"
static unsigned short *backBuf1, *backBuf2;
static int lookup_initialized;
static void dispatchDecoder16(unsigned short **pFrame, unsigned char codeType, unsigned char **pData, unsigned char **pOffData, int *pDataRemain, int *curXb, int *curYb);
static void genLoopkupTable(void);
void decodeFrame16(unsigned char *pFrame, unsigned char *pMap, int mapRemain, unsigned char *pData, int dataRemain)
{
unsigned char *pOrig;
unsigned char *pOffData, *pEnd;
unsigned short offset;
unsigned short *FramePtr = (unsigned short *)pFrame;
int length;
int op;
int i, j;
int xb, yb;
if (!lookup_initialized) {
genLoopkupTable();
}
backBuf1 = (unsigned short *)g_vBackBuf1;
backBuf2 = (unsigned short *)g_vBackBuf2;
xb = g_width >> 3;
yb = g_height >> 3;
offset = pData[0]|(pData[1]<<8);
pOffData = pData + offset;
pEnd = pData + offset;
pData += 2;
pOrig = pData;
length = offset - 2; /*dataRemain-2;*/
for (j=0; j<yb; j++)
{
for (i=0; i<xb/2; i++)
{
op = (*pMap) & 0xf;
dispatchDecoder16(&FramePtr, op, &pData, &pOffData, &dataRemain, &i, &j);
/*
if (FramePtr < backBuf1)
fprintf(stderr, "danger! pointing out of bounds below after dispatch decoder: %d, %d (1) [%x]\n", i, j, (*pMap) & 0xf);
else if (FramePtr >= backBuf1 + g_width*g_height)
fprintf(stderr, "danger! pointing out of bounds above after dispatch decoder: %d, %d (1) [%x]\n", i, j, (*pMap) & 0xf);
*/
op = ((*pMap) >> 4) & 0xf;
dispatchDecoder16(&FramePtr, op, &pData, &pOffData, &dataRemain, &i, &j);
/*
if (FramePtr < backBuf1)
fprintf(stderr, "danger! pointing out of bounds below after dispatch decoder: %d, %d (2) [%x]\n", i, j, (*pMap) >> 4);
else if (FramePtr >= backBuf1 + g_width*g_height)
fprintf(stderr, "danger! pointing out of bounds above after dispatch decoder: %d, %d (2) [%x]\n", i, j, (*pMap) >> 4);
*/
++pMap;
--mapRemain;
}
FramePtr += 7*g_width;
}
if ( (length - (pData - pOrig)) != 0 )
fprintf(stderr, "DEBUG: junk left over: %d,%d,%d\n", (int)(pData-pOrig), length, (int)(length-(pData-pOrig)));
}
static unsigned short GETPIXEL(unsigned char **buf, int off)
{
unsigned short val = (*buf)[0+off] | ((*buf)[1+off] << 8);
return val;
}
static unsigned short GETPIXELI(unsigned char **buf, int off)
{
unsigned short val = (*buf)[0+off] | ((*buf)[1+off] << 8);
(*buf) += 2;
return val;
}
static void relClose(int i, int *x, int *y)
{
int ma, mi;
ma = i >> 4;
mi = i & 0xf;
*x = mi - 8;
*y = ma - 8;
}
static void relFar(int i, int sign, int *x, int *y)
{
if (i < 56)
{
*x = sign * (8 + (i % 7));
*y = sign * (i / 7);
}
else
{
*x = sign * (-14 + (i - 56) % 29);
*y = sign * (8 + (i - 56) / 29);
}
}
static int close_table[512];
static int far_p_table[512];
static int far_n_table[512];
static void genLoopkupTable()
{
int i;
int x, y;
for (i = 0; i < 256; i++) {
relClose(i, &x, &y);
close_table[i*2+0] = x;
close_table[i*2+1] = y;
relFar(i, 1, &x, &y);
far_p_table[i*2+0] = x;
far_p_table[i*2+1] = y;
relFar(i, -1, &x, &y);
far_n_table[i*2+0] = x;
far_n_table[i*2+1] = y;
}
lookup_initialized = 1;
}
static void copyFrame(unsigned short *pDest, unsigned short *pSrc)
{
int i;
for (i=0; i<8; i++)
{
memcpy(pDest, pSrc, 16);
pDest += g_width;
pSrc += g_width;
}
}
static void patternRow4Pixels(unsigned short *pFrame,
unsigned char pat0, unsigned char pat1,
unsigned short *p)
{
unsigned short mask=0x0003;
unsigned short shift=0;
unsigned short pattern = (pat1 << 8) | pat0;
while (mask != 0)
{
*pFrame++ = p[(mask & pattern) >> shift];
mask <<= 2;
shift += 2;
}
}
static void patternRow4Pixels2(unsigned short *pFrame,
unsigned char pat0,
unsigned short *p)
{
unsigned char mask=0x03;
unsigned char shift=0;
unsigned short pel;
/* ORIGINAL VERSION IS BUGGY
int skip=1;
while (mask != 0)
{
pel = p[(mask & pat0) >> shift];
pFrame[0] = pel;
pFrame[2] = pel;
pFrame[g_width + 0] = pel;
pFrame[g_width + 2] = pel;
pFrame += skip;
skip = 4 - skip;
mask <<= 2;
shift += 2;
}
*/
while (mask != 0)
{
pel = p[(mask & pat0) >> shift];
pFrame[0] = pel;
pFrame[1] = pel;
pFrame[g_width + 0] = pel;
pFrame[g_width + 1] = pel;
pFrame += 2;
mask <<= 2;
shift += 2;
}
}
static void patternRow4Pixels2x1(unsigned short *pFrame, unsigned char pat,
unsigned short *p)
{
unsigned char mask=0x03;
unsigned char shift=0;
unsigned short pel;
while (mask != 0)
{
pel = p[(mask & pat) >> shift];
pFrame[0] = pel;
pFrame[1] = pel;
pFrame += 2;
mask <<= 2;
shift += 2;
}
}
static void patternQuadrant4Pixels(unsigned short *pFrame,
unsigned char pat0, unsigned char pat1, unsigned char pat2,
unsigned char pat3, unsigned short *p)
{
unsigned long mask = 0x00000003UL;
int shift=0;
int i;
unsigned long pat = (pat3 << 24) | (pat2 << 16) | (pat1 << 8) | pat0;
for (i=0; i<16; i++)
{
pFrame[i&3] = p[(pat & mask) >> shift];
if ((i&3) == 3)
pFrame += g_width;
mask <<= 2;
shift += 2;
}
}
static void patternRow2Pixels(unsigned short *pFrame, unsigned char pat,
unsigned short *p)
{
unsigned char mask=0x01;
while (mask != 0)
{
*pFrame++ = p[(mask & pat) ? 1 : 0];
mask <<= 1;
}
}
static void patternRow2Pixels2(unsigned short *pFrame, unsigned char pat,
unsigned short *p)
{
unsigned short pel;
unsigned char mask=0x1;
/* ORIGINAL VERSION IS BUGGY
int skip=1;
while (mask != 0x10)
{
pel = p[(mask & pat) ? 1 : 0];
pFrame[0] = pel;
pFrame[2] = pel;
pFrame[g_width + 0] = pel;
pFrame[g_width + 2] = pel;
pFrame += skip;
skip = 4 - skip;
mask <<= 1;
}
*/
while (mask != 0x10) {
pel = p[(mask & pat) ? 1 : 0];
pFrame[0] = pel;
pFrame[1] = pel;
pFrame[g_width + 0] = pel;
pFrame[g_width + 1] = pel;
pFrame += 2;
mask <<= 1;
}
}
static void patternQuadrant2Pixels(unsigned short *pFrame, unsigned char pat0,
unsigned char pat1, unsigned short *p)
{
unsigned short mask = 0x0001;
int i;
unsigned short pat = (pat1 << 8) | pat0;
for (i=0; i<16; i++)
{
pFrame[i&3] = p[(pat & mask) ? 1 : 0];
if ((i&3) == 3)
pFrame += g_width;
mask <<= 1;
}
}
static void dispatchDecoder16(unsigned short **pFrame, unsigned char codeType, unsigned char **pData, unsigned char **pOffData, int *pDataRemain, int *curXb, int *curYb)
{
unsigned short p[4];
unsigned char pat[16];
int i, j, k;
int x, y;
unsigned short *pDstBak;
pDstBak = *pFrame;
switch(codeType)
{
case 0x0:
copyFrame(*pFrame, *pFrame + (backBuf2 - backBuf1));
case 0x1:
break;
case 0x2: /*
relFar(*(*pOffData)++, 1, &x, &y);
*/
k = *(*pOffData)++;
x = far_p_table[k*2+0];
y = far_p_table[k*2+1];
copyFrame(*pFrame, *pFrame + x + y*g_width);
--*pDataRemain;
break;
case 0x3: /*
relFar(*(*pOffData)++, -1, &x, &y);
*/
k = *(*pOffData)++;
x = far_n_table[k*2+0];
y = far_n_table[k*2+1];
copyFrame(*pFrame, *pFrame + x + y*g_width);
--*pDataRemain;
break;
case 0x4: /*
relClose(*(*pOffData)++, &x, &y);
*/
k = *(*pOffData)++;
x = close_table[k*2+0];
y = close_table[k*2+1];
copyFrame(*pFrame, *pFrame + (backBuf2 - backBuf1) + x + y*g_width);
--*pDataRemain;
break;
case 0x5:
x = (char)*(*pData)++;
y = (char)*(*pData)++;
copyFrame(*pFrame, *pFrame + (backBuf2 - backBuf1) + x + y*g_width);
*pDataRemain -= 2;
break;
case 0x6:
fprintf(stderr, "STUB: encoding 6 not tested\n");
for (i=0; i<2; i++)
{
*pFrame += 16;
if (++*curXb == (g_width >> 3))
{
*pFrame += 7*g_width;
*curXb = 0;
if (++*curYb == (g_height >> 3))
return;
}
}
break;
case 0x7:
p[0] = GETPIXELI(pData, 0);
p[1] = GETPIXELI(pData, 0);
if (!((p[0]/*|p[1]*/)&0x8000))
{
for (i=0; i<8; i++)
{
patternRow2Pixels(*pFrame, *(*pData), p);
(*pData)++;
*pFrame += g_width;
}
}
else
{
for (i=0; i<2; i++)
{
patternRow2Pixels2(*pFrame, *(*pData) & 0xf, p);
*pFrame += 2*g_width;
patternRow2Pixels2(*pFrame, *(*pData) >> 4, p);
(*pData)++;
*pFrame += 2*g_width;
}
}
break;
case 0x8:
p[0] = GETPIXEL(pData, 0);
if (!(p[0] & 0x8000))
{
for (i=0; i<4; i++)
{
p[0] = GETPIXELI(pData, 0);
p[1] = GETPIXELI(pData, 0);
pat[0] = (*pData)[0];
pat[1] = (*pData)[1];
(*pData) += 2;
patternQuadrant2Pixels(*pFrame, pat[0], pat[1], p);
if (i & 1)
*pFrame -= (4*g_width - 4);
else
*pFrame += 4*g_width;
}
} else {
p[2] = GETPIXEL(pData, 8);
if (!(p[2]&0x8000)) {
for (i=0; i<4; i++)
{
if ((i & 1) == 0)
{
p[0] = GETPIXELI(pData, 0);
p[1] = GETPIXELI(pData, 0);
}
pat[0] = *(*pData)++;
pat[1] = *(*pData)++;
patternQuadrant2Pixels(*pFrame, pat[0], pat[1], p);
if (i & 1)
*pFrame -= (4*g_width - 4);
else
*pFrame += 4*g_width;
}
} else {
for (i=0; i<8; i++)
{
if ((i & 3) == 0)
{
p[0] = GETPIXELI(pData, 0);
p[1] = GETPIXELI(pData, 0);
}
patternRow2Pixels(*pFrame, *(*pData), p);
(*pData)++;
*pFrame += g_width;
}
}
}
break;
case 0x9:
p[0] = GETPIXELI(pData, 0);
p[1] = GETPIXELI(pData, 0);
p[2] = GETPIXELI(pData, 0);
p[3] = GETPIXELI(pData, 0);
*pDataRemain -= 8;
if (!(p[0] & 0x8000))
{
if (!(p[2] & 0x8000))
{
for (i=0; i<8; i++)
{
pat[0] = (*pData)[0];
pat[1] = (*pData)[1];
(*pData) += 2;
patternRow4Pixels(*pFrame, pat[0], pat[1], p);
*pFrame += g_width;
}
*pDataRemain -= 16;
}
else
{
patternRow4Pixels2(*pFrame, (*pData)[0], p);
*pFrame += 2*g_width;
patternRow4Pixels2(*pFrame, (*pData)[1], p);
*pFrame += 2*g_width;
patternRow4Pixels2(*pFrame, (*pData)[2], p);
*pFrame += 2*g_width;
patternRow4Pixels2(*pFrame, (*pData)[3], p);
(*pData) += 4;
*pDataRemain -= 4;
}
}
else
{
if (!(p[2] & 0x8000))
{
for (i=0; i<8; i++)
{
pat[0] = (*pData)[0];
(*pData) += 1;
patternRow4Pixels2x1(*pFrame, pat[0], p);
*pFrame += g_width;
}
*pDataRemain -= 8;
}
else
{
for (i=0; i<4; i++)
{
pat[0] = (*pData)[0];
pat[1] = (*pData)[1];
(*pData) += 2;
patternRow4Pixels(*pFrame, pat[0], pat[1], p);
*pFrame += g_width;
patternRow4Pixels(*pFrame, pat[0], pat[1], p);
*pFrame += g_width;
}
*pDataRemain -= 8;
}
}
break;
case 0xa:
p[0] = GETPIXEL(pData, 0);
if (!(p[0] & 0x8000))
{
for (i=0; i<4; i++)
{
p[0] = GETPIXELI(pData, 0);
p[1] = GETPIXELI(pData, 0);
p[2] = GETPIXELI(pData, 0);
p[3] = GETPIXELI(pData, 0);
pat[0] = (*pData)[0];
pat[1] = (*pData)[1];
pat[2] = (*pData)[2];
pat[3] = (*pData)[3];
(*pData) += 4;
patternQuadrant4Pixels(*pFrame, pat[0], pat[1], pat[2], pat[3], p);
if (i & 1)
*pFrame -= (4*g_width - 4);
else
*pFrame += 4*g_width;
}
}
else
{
p[0] = GETPIXEL(pData, 16);
if (!(p[0] & 0x8000))
{
for (i=0; i<4; i++)
{
if ((i&1) == 0)
{
p[0] = GETPIXELI(pData, 0);
p[1] = GETPIXELI(pData, 0);
p[2] = GETPIXELI(pData, 0);
p[3] = GETPIXELI(pData, 0);
}
pat[0] = (*pData)[0];
pat[1] = (*pData)[1];
pat[2] = (*pData)[2];
pat[3] = (*pData)[3];
(*pData) += 4;
patternQuadrant4Pixels(*pFrame, pat[0], pat[1], pat[2], pat[3], p);
if (i & 1)
*pFrame -= (4*g_width - 4);
else
*pFrame += 4*g_width;
}
}
else
{
for (i=0; i<8; i++)
{
if ((i&3) == 0)
{
p[0] = GETPIXELI(pData, 0);
p[1] = GETPIXELI(pData, 0);
p[2] = GETPIXELI(pData, 0);
p[3] = GETPIXELI(pData, 0);
}
pat[0] = (*pData)[0];
pat[1] = (*pData)[1];
patternRow4Pixels(*pFrame, pat[0], pat[1], p);
*pFrame += g_width;
(*pData) += 2;
}
}
}
break;
case 0xb:
for (i=0; i<8; i++)
{
memcpy(*pFrame, *pData, 16);
*pFrame += g_width;
*pData += 16;
*pDataRemain -= 16;
}
break;
case 0xc:
for (i=0; i<4; i++)
{
p[0] = GETPIXEL(pData, 0);
p[1] = GETPIXEL(pData, 2);
p[2] = GETPIXEL(pData, 4);
p[3] = GETPIXEL(pData, 6);
for (j=0; j<2; j++)
{
for (k=0; k<4; k++)
{
(*pFrame)[j+2*k] = p[k];
(*pFrame)[g_width+j+2*k] = p[k];
}
*pFrame += g_width;
}
*pData += 8;
*pDataRemain -= 8;
}
break;
case 0xd:
for (i=0; i<2; i++)
{
p[0] = GETPIXEL(pData, 0);
p[1] = GETPIXEL(pData, 2);
for (j=0; j<4; j++)
{
for (k=0; k<4; k++)
{
(*pFrame)[k*g_width+j] = p[0];
(*pFrame)[k*g_width+j+4] = p[1];
}
}
*pFrame += 4*g_width;
*pData += 4;
*pDataRemain -= 4;
}
break;
case 0xe:
p[0] = GETPIXEL(pData, 0);
for (i = 0; i < 8; i++) {
for (j = 0; j < 8; j++) {
(*pFrame)[j] = p[0];
}
*pFrame += g_width;
}
*pData += 2;
*pDataRemain -= 2;
break;
case 0xf:
p[0] = GETPIXEL(pData, 0);
p[1] = GETPIXEL(pData, 1);
for (i=0; i<8; i++)
{
for (j=0; j<8; j++)
{
(*pFrame)[j] = p[(i+j)&1];
}
*pFrame += g_width;
}
*pData += 4;
*pDataRemain -= 4;
break;
default:
break;
}
*pFrame = pDstBak+8;
}

871
libmve/decoder8.c Normal file
View File

@@ -0,0 +1,871 @@
/* 8 bit decoding routines */
#include <stdio.h>
#include <string.h>
#include "decoders.h"
static void dispatchDecoder(unsigned char **pFrame, unsigned char codeType, unsigned char **pData, int *pDataRemain, int *curXb, int *curYb);
void decodeFrame8(unsigned char *pFrame, unsigned char *pMap, int mapRemain, unsigned char *pData, int dataRemain)
{
int i, j;
int xb, yb;
xb = g_width >> 3;
yb = g_height >> 3;
for (j=0; j<yb; j++)
{
for (i=0; i<xb/2; i++)
{
dispatchDecoder(&pFrame, (*pMap) & 0xf, &pData, &dataRemain, &i, &j);
if (pFrame < (unsigned char *)g_vBackBuf1)
fprintf(stderr, "danger! pointing out of bounds below after dispatch decoder: %d, %d (1) [%x]\n", i, j, (*pMap) & 0xf);
else if (pFrame >= ((unsigned char *)g_vBackBuf1) + g_width*g_height)
fprintf(stderr, "danger! pointing out of bounds above after dispatch decoder: %d, %d (1) [%x]\n", i, j, (*pMap) & 0xf);
dispatchDecoder(&pFrame, (*pMap) >> 4, &pData, &dataRemain, &i, &j);
if (pFrame < (unsigned char *)g_vBackBuf1)
fprintf(stderr, "danger! pointing out of bounds below after dispatch decoder: %d, %d (2) [%x]\n", i, j, (*pMap) >> 4);
else if (pFrame >= ((unsigned char *)g_vBackBuf1) + g_width*g_height)
fprintf(stderr, "danger! pointing out of bounds above after dispatch decoder: %d, %d (2) [%x]\n", i, j, (*pMap) >> 4);
++pMap;
--mapRemain;
}
pFrame += 7*g_width;
}
}
static void relClose(int i, int *x, int *y)
{
int ma, mi;
ma = i >> 4;
mi = i & 0xf;
*x = mi - 8;
*y = ma - 8;
}
static void relFar(int i, int sign, int *x, int *y)
{
if (i < 56)
{
*x = sign * (8 + (i % 7));
*y = sign * (i / 7);
}
else
{
*x = sign * (-14 + (i - 56) % 29);
*y = sign * (8 + (i - 56) / 29);
}
}
/* copies an 8x8 block from pSrc to pDest.
pDest and pSrc are both g_width bytes wide */
static void copyFrame(unsigned char *pDest, unsigned char *pSrc)
{
int i;
for (i=0; i<8; i++)
{
memcpy(pDest, pSrc, 8);
pDest += g_width;
pSrc += g_width;
}
}
// Fill in the next eight bytes with p[0], p[1], p[2], or p[3],
// depending on the corresponding two-bit value in pat0 and pat1
static void patternRow4Pixels(unsigned char *pFrame,
unsigned char pat0, unsigned char pat1,
unsigned char *p)
{
unsigned short mask=0x0003;
unsigned short shift=0;
unsigned short pattern = (pat1 << 8) | pat0;
while (mask != 0)
{
*pFrame++ = p[(mask & pattern) >> shift];
mask <<= 2;
shift += 2;
}
}
// Fill in the next four 2x2 pixel blocks with p[0], p[1], p[2], or p[3],
// depending on the corresponding two-bit value in pat0.
static void patternRow4Pixels2(unsigned char *pFrame,
unsigned char pat0,
unsigned char *p)
{
unsigned char mask=0x03;
unsigned char shift=0;
unsigned char pel;
while (mask != 0)
{
pel = p[(mask & pat0) >> shift];
pFrame[0] = pel;
pFrame[1] = pel;
pFrame[g_width + 0] = pel;
pFrame[g_width + 1] = pel;
pFrame += 2;
mask <<= 2;
shift += 2;
}
}
// Fill in the next four 2x1 pixel blocks with p[0], p[1], p[2], or p[3],
// depending on the corresponding two-bit value in pat.
static void patternRow4Pixels2x1(unsigned char *pFrame, unsigned char pat, unsigned char *p)
{
unsigned char mask=0x03;
unsigned char shift=0;
unsigned char pel;
while (mask != 0)
{
pel = p[(mask & pat) >> shift];
pFrame[0] = pel;
pFrame[1] = pel;
pFrame += 2;
mask <<= 2;
shift += 2;
}
}
// Fill in the next 4x4 pixel block with p[0], p[1], p[2], or p[3],
// depending on the corresponding two-bit value in pat0, pat1, pat2, and pat3.
static void patternQuadrant4Pixels(unsigned char *pFrame, unsigned char pat0, unsigned char pat1, unsigned char pat2, unsigned char pat3, unsigned char *p)
{
unsigned long mask = 0x00000003UL;
int shift=0;
int i;
unsigned long pat = (pat3 << 24) | (pat2 << 16) | (pat1 << 8) | pat0;
for (i=0; i<16; i++)
{
pFrame[i&3] = p[(pat & mask) >> shift];
if ((i&3) == 3)
pFrame += g_width;
mask <<= 2;
shift += 2;
}
}
// fills the next 8 pixels with either p[0] or p[1], depending on pattern
static void patternRow2Pixels(unsigned char *pFrame, unsigned char pat, unsigned char *p)
{
unsigned char mask=0x01;
while (mask != 0)
{
*pFrame++ = p[(mask & pat) ? 1 : 0];
mask <<= 1;
}
}
// fills the next four 2 x 2 pixel boxes with either p[0] or p[1], depending on pattern
static void patternRow2Pixels2(unsigned char *pFrame, unsigned char pat, unsigned char *p)
{
unsigned char pel;
unsigned char mask=0x1;
while (mask != 0x10)
{
pel = p[(mask & pat) ? 1 : 0];
pFrame[0] = pel; // upper-left
pFrame[1] = pel; // upper-right
pFrame[g_width + 0] = pel; // lower-left
pFrame[g_width + 1] = pel; // lower-right
pFrame += 2;
mask <<= 1;
}
}
// fills pixels in the next 4 x 4 pixel boxes with either p[0] or p[1], depending on pat0 and pat1.
static void patternQuadrant2Pixels(unsigned char *pFrame, unsigned char pat0, unsigned char pat1, unsigned char *p)
{
unsigned char pel;
unsigned short mask = 0x0001;
int i, j;
unsigned short pat = (pat1 << 8) | pat0;
for (i=0; i<4; i++)
{
for (j=0; j<4; j++)
{
pel = p[(pat & mask) ? 1 : 0];
pFrame[j + i * g_width] = pel;
mask <<= 1;
}
}
}
static void dispatchDecoder(unsigned char **pFrame, unsigned char codeType, unsigned char **pData, int *pDataRemain, int *curXb, int *curYb)
{
unsigned char p[4];
unsigned char pat[16];
int i, j, k;
int x, y;
/* Data is processed in 8x8 pixel blocks.
There are 16 ways to encode each block.
*/
switch(codeType)
{
case 0x0:
/* block is copied from block in current frame */
copyFrame(*pFrame, *pFrame + ((unsigned char *)g_vBackBuf2 - (unsigned char *)g_vBackBuf1));
case 0x1:
/* block is unchanged from two frames ago */
*pFrame += 8;
break;
case 0x2:
/* Block is copied from nearby (below and/or to the right) within the
new frame. The offset within the buffer from which to grab the
patch of 8 pixels is given by grabbing a byte B from the data
stream, which is broken into a positive x and y offset according
to the following mapping:
if B < 56:
x = 8 + (B % 7)
y = B / 7
else
x = -14 + ((B - 56) % 29)
y = 8 + ((B - 56) / 29)
*/
relFar(*(*pData)++, 1, &x, &y);
copyFrame(*pFrame, *pFrame + x + y*g_width);
*pFrame += 8;
--*pDataRemain;
break;
case 0x3:
/* Block is copied from nearby (above and/or to the left) within the
new frame.
if B < 56:
x = -(8 + (B % 7))
y = -(B / 7)
else
x = -(-14 + ((B - 56) % 29))
y = -( 8 + ((B - 56) / 29))
*/
relFar(*(*pData)++, -1, &x, &y);
copyFrame(*pFrame, *pFrame + x + y*g_width);
*pFrame += 8;
--*pDataRemain;
break;
case 0x4:
/* Similar to 0x2 and 0x3, except this method copies from the
"current" frame, rather than the "new" frame, and instead of the
lopsided mapping they use, this one uses one which is symmetric
and centered around the top-left corner of the block. This uses
only 1 byte still, though, so the range is decreased, since we
have to encode all directions in a single byte. The byte we pull
from the data stream, I'll call B. Call the highest 4 bits of B
BH and the lowest 4 bytes BL. Then the offset from which to copy
the data is:
x = -8 + BL
y = -8 + BH
*/
relClose(*(*pData)++, &x, &y);
copyFrame(*pFrame, *pFrame + ((unsigned char *)g_vBackBuf2 - (unsigned char *)g_vBackBuf1) + x + y*g_width);
*pFrame += 8;
--*pDataRemain;
break;
case 0x5:
/* Similar to 0x4, but instead of one byte for the offset, this uses
two bytes to encode a larger range, the first being the x offset
as a signed 8-bit value, and the second being the y offset as a
signed 8-bit value.
*/
x = (signed char)*(*pData)++;
y = (signed char)*(*pData)++;
copyFrame(*pFrame, *pFrame + ((unsigned char *)g_vBackBuf2 - (unsigned char *)g_vBackBuf1) + x + y*g_width);
*pFrame += 8;
*pDataRemain -= 2;
break;
case 0x6:
/* I can't figure out how any file containing a block of this type
could still be playable, since it appears that it would leave the
internal bookkeeping in an inconsistent state in the BG player
code. Ahh, well. Perhaps it was a bug in the BG player code that
just didn't happen to be exposed by any of the included movies.
Anyway, this skips the next two blocks, doing nothing to them.
Note that if you've reached the end of a row, this means going on
to the next row.
*/
for (i=0; i<2; i++)
{
*pFrame += 16;
if (++*curXb == (g_width >> 3))
{
*pFrame += 7*g_width;
*curXb = 0;
if (++*curYb == (g_height >> 3))
return;
}
}
break;
case 0x7:
/* Ok, here's where it starts to get really...interesting. This is,
incidentally, the part where they started using self-modifying
code. So, most of the following encodings are "patterned" blocks,
where we are given a number of pixel values and then bitmapped
values to specify which pixel values belong to which squares. For
this encoding, we are given the following in the data stream:
P0 P1
These are pixel values (i.e. 8-bit indices into the palette). If
P0 <= P1, we then get 8 more bytes from the data stream, one for
each row in the block:
B0 B1 B2 B3 B4 B5 B6 B7
For each row, the leftmost pixel is represented by the low-order
bit, and the rightmost by the high-order bit. Use your imagination
in between. If a bit is set, the pixel value is P1 and if it is
unset, the pixel value is P0.
So, for example, if we had:
11 22 fe 83 83 83 83 83 83 fe
This would represent the following layout:
11 22 22 22 22 22 22 22 ; fe == 11111110
22 22 11 11 11 11 11 22 ; 83 == 10000011
22 22 11 11 11 11 11 22 ; 83 == 10000011
22 22 11 11 11 11 11 22 ; 83 == 10000011
22 22 11 11 11 11 11 22 ; 83 == 10000011
22 22 11 11 11 11 11 22 ; 83 == 10000011
22 22 11 11 11 11 11 22 ; 83 == 10000011
11 22 22 22 22 22 22 22 ; fe == 11111110
If, on the other hand, P0 > P1, we get two more bytes from the
data stream:
B0 B1
Each of these bytes contains two 4-bit patterns. These patterns
work like the patterns above with 8 bytes, except each bit
represents a 2x2 pixel region.
B0 contains the pattern for the top two rows and B1 contains
the pattern for the bottom two rows. Note that the low-order
nibble of each byte contains the pattern for the upper of the
two rows that that byte controls.
So if we had:
22 11 7e 83
The output would be:
11 11 22 22 22 22 22 22 ; e == 1 1 1 0
11 11 22 22 22 22 22 22 ;
22 22 22 22 22 22 11 11 ; 7 == 0 1 1 1
22 22 22 22 22 22 11 11 ;
11 11 11 11 11 11 22 22 ; 3 == 1 0 0 0
11 11 11 11 11 11 22 22 ;
22 22 22 22 11 11 11 11 ; 8 == 0 0 1 1
22 22 22 22 11 11 11 11 ;
*/
p[0] = *(*pData)++;
p[1] = *(*pData)++;
if (p[0] <= p[1])
{
for (i=0; i<8; i++)
{
patternRow2Pixels(*pFrame, *(*pData)++, p);
*pFrame += g_width;
}
}
else
{
for (i=0; i<2; i++)
{
patternRow2Pixels2(*pFrame, *(*pData) & 0xf, p);
*pFrame += 2*g_width;
patternRow2Pixels2(*pFrame, *(*pData)++ >> 4, p);
*pFrame += 2*g_width;
}
}
*pFrame -= (8*g_width - 8);
break;
case 0x8:
/* Ok, this one is basically like encoding 0x7, only more
complicated. Again, we start out by getting two bytes on the data
stream:
P0 P1
if P0 <= P1 then we get the following from the data stream:
B0 B1
P2 P3 B2 B3
P4 P5 B4 B5
P6 P7 B6 B7
P0 P1 and B0 B1 are used for the top-left corner, P2 P3 B2 B3 for
the bottom-left corner, P4 P5 B4 B5 for the top-right, P6 P7 B6 B7
for the bottom-right. (So, each codes for a 4x4 pixel array.)
Since we have 16 bits in B0 B1, there is one bit for each pixel in
the array. The convention for the bit-mapping is, again, left to
right and top to bottom.
So, basically, the top-left quarter of the block is an arbitrary
pattern with 2 pixels, the bottom-left a different arbitrary
pattern with 2 different pixels, and so on.
For example if the next 16 bytes were:
00 22 f9 9f 44 55 aa 55 11 33 cc 33 66 77 01 ef
We'd draw:
22 22 22 22 | 11 11 33 33 ; f = 1111, c = 1100
22 00 00 22 | 11 11 33 33 ; 9 = 1001, c = 1100
22 00 00 22 | 33 33 11 11 ; 9 = 1001, 3 = 0011
22 22 22 22 | 33 33 11 11 ; f = 1111, 3 = 0011
------------+------------
44 55 44 55 | 66 66 66 66 ; a = 1010, 0 = 0000
44 55 44 55 | 77 66 66 66 ; a = 1010, 1 = 0001
55 44 55 44 | 66 77 77 77 ; 5 = 0101, e = 1110
55 44 55 44 | 77 77 77 77 ; 5 = 0101, f = 1111
I've added a dividing line in the above to clearly delineate the
quadrants.
Now, if P0 > P1 then we get 10 more bytes from the data stream:
B0 B1 B2 B3 P2 P3 B4 B5 B6 B7
Now, if P2 <= P3, then the first six bytes [P0 P1 B0 B1 B2 B3]
represent the left half of the block and the latter six bytes
[P2 P3 B4 B5 B6 B7] represent the right half.
For example:
22 00 01 37 f7 31 11 66 8c e6 73 31
yeilds:
22 22 22 22 | 11 11 11 66 ; 0: 0000 | 8: 1000
00 22 22 22 | 11 11 66 66 ; 1: 0001 | C: 1100
00 00 22 22 | 11 66 66 66 ; 3: 0011 | e: 1110
00 00 00 22 | 11 66 11 66 ; 7: 0111 | 6: 0101
00 00 00 00 | 66 66 66 11 ; f: 1111 | 7: 0111
00 00 00 22 | 66 66 11 11 ; 7: 0111 | 3: 0011
00 00 22 22 | 66 66 11 11 ; 3: 0011 | 3: 0011
00 22 22 22 | 66 11 11 11 ; 1: 0001 | 1: 0001
On the other hand, if P0 > P1 and P2 > P3, then
[P0 P1 B0 B1 B2 B3] represent the top half of the
block and [P2 P3 B4 B5 B6 B7] represent the bottom half.
For example:
22 00 cc 66 33 19 66 11 18 24 42 81
yeilds:
22 22 00 00 22 22 00 00 ; cc: 11001100
22 00 00 22 22 00 00 22 ; 66: 01100110
00 00 22 22 00 00 22 22 ; 33: 00110011
00 22 22 00 00 22 22 22 ; 19: 00011001
-----------------------
66 66 66 11 11 66 66 66 ; 18: 00011000
66 66 11 66 66 11 66 66 ; 24: 00100100
66 11 66 66 66 66 11 66 ; 42: 01000010
11 66 66 66 66 66 66 11 ; 81: 10000001
*/
if ( (*pData)[0] <= (*pData)[1])
{
// four quadrant case
for (i=0; i<4; i++)
{
p[0] = *(*pData)++;
p[1] = *(*pData)++;
pat[0] = *(*pData)++;
pat[1] = *(*pData)++;
patternQuadrant2Pixels(*pFrame, pat[0], pat[1], p);
// alternate between moving down and moving up and right
if (i & 1)
*pFrame += 4 - 4*g_width; // up and right
else
*pFrame += 4*g_width; // down
}
}
else if ( (*pData)[6] <= (*pData)[7])
{
// split horizontal
for (i=0; i<4; i++)
{
if ((i & 1) == 0)
{
p[0] = *(*pData)++;
p[1] = *(*pData)++;
}
pat[0] = *(*pData)++;
pat[1] = *(*pData)++;
patternQuadrant2Pixels(*pFrame, pat[0], pat[1], p);
if (i & 1)
*pFrame -= (4*g_width - 4);
else
*pFrame += 4*g_width;
}
}
else
{
// split vertical
for (i=0; i<8; i++)
{
if ((i & 3) == 0)
{
p[0] = *(*pData)++;
p[1] = *(*pData)++;
}
patternRow2Pixels(*pFrame, *(*pData)++, p);
*pFrame += g_width;
}
*pFrame -= (8*g_width - 8);
}
break;
case 0x9:
/* Similar to the previous 2 encodings, only more complicated. And
it will get worse before it gets better. No longer are we dealing
with patterns over two pixel values. Now we are dealing with
patterns over 4 pixel values with 2 bits assigned to each pixel
(or block of pixels).
So, first on the data stream are our 4 pixel values:
P0 P1 P2 P3
Now, if P0 <= P1 AND P2 <= P3, we get 16 bytes of pattern, each
2 bits representing a 1x1 pixel (00=P0, 01=P1, 10=P2, 11=P3). The
ordering is again left to right and top to bottom. The most
significant bits represent the left side at the top, and so on.
If P0 <= P1 AND P2 > P3, we get 4 bytes of pattern, each 2 bits
representing a 2x2 pixel. Ordering is left to right and top to
bottom.
if P0 > P1 AND P2 <= P3, we get 8 bytes of pattern, each 2 bits
representing a 2x1 pixel (i.e. 2 pixels wide, and 1 high).
if P0 > P1 AND P2 > P3, we get 8 bytes of pattern, each 2 bits
representing a 1x2 pixel (i.e. 1 pixel wide, and 2 high).
*/
if ( (*pData)[0] <= (*pData)[1])
{
if ( (*pData)[2] <= (*pData)[3])
{
p[0] = *(*pData)++;
p[1] = *(*pData)++;
p[2] = *(*pData)++;
p[3] = *(*pData)++;
for (i=0; i<8; i++)
{
pat[0] = *(*pData)++;
pat[1] = *(*pData)++;
patternRow4Pixels(*pFrame, pat[0], pat[1], p);
*pFrame += g_width;
}
*pFrame -= (8*g_width - 8);
}
else
{
p[0] = *(*pData)++;
p[1] = *(*pData)++;
p[2] = *(*pData)++;
p[3] = *(*pData)++;
patternRow4Pixels2(*pFrame, *(*pData)++, p);
*pFrame += 2*g_width;
patternRow4Pixels2(*pFrame, *(*pData)++, p);
*pFrame += 2*g_width;
patternRow4Pixels2(*pFrame, *(*pData)++, p);
*pFrame += 2*g_width;
patternRow4Pixels2(*pFrame, *(*pData)++, p);
*pFrame -= (6*g_width - 8);
}
}
else
{
if ( (*pData)[2] <= (*pData)[3])
{
// draw 2x1 strips
p[0] = *(*pData)++;
p[1] = *(*pData)++;
p[2] = *(*pData)++;
p[3] = *(*pData)++;
for (i=0; i<8; i++)
{
pat[0] = *(*pData)++;
patternRow4Pixels2x1(*pFrame, pat[0], p);
*pFrame += g_width;
}
*pFrame -= (8*g_width - 8);
}
else
{
// draw 1x2 strips
p[0] = *(*pData)++;
p[1] = *(*pData)++;
p[2] = *(*pData)++;
p[3] = *(*pData)++;
for (i=0; i<4; i++)
{
pat[0] = *(*pData)++;
pat[1] = *(*pData)++;
patternRow4Pixels(*pFrame, pat[0], pat[1], p);
*pFrame += g_width;
patternRow4Pixels(*pFrame, pat[0], pat[1], p);
*pFrame += g_width;
}
*pFrame -= (8*g_width - 8);
}
}
break;
case 0xa:
/* Similar to the previous, only a little more complicated.
We are still dealing with patterns over 4 pixel values with 2 bits
assigned to each pixel (or block of pixels).
So, first on the data stream are our 4 pixel values:
P0 P1 P2 P3
Now, if P0 <= P1, the block is divided into 4 quadrants, ordered
(as with opcode 0x8) TL, BL, TR, BR. In this case the next data
in the data stream should be:
B0 B1 B2 B3
P4 P5 P6 P7 B4 B5 B6 B7
P8 P9 P10 P11 B8 B9 B10 B11
P12 P13 P14 P15 B12 B13 B14 B15
Each 2 bits represent a 1x1 pixel (00=P0, 01=P1, 10=P2, 11=P3).
The ordering is again left to right and top to bottom. The most
significant bits represent the right side at the top, and so on.
If P0 > P1 then the next data on the data stream is:
B0 B1 B2 B3 B4 B5 B6 B7
P4 P5 P6 P7 B8 B9 B10 B11 B12 B13 B14 B15
Now, in this case, if P4 <= P5,
[P0 P1 P2 P3 B0 B1 B2 B3 B4 B5 B6 B7] represent the left half of
the block and the other bytes represent the right half. If P4 >
P5, then [P0 P1 P2 P3 B0 B1 B2 B3 B4 B5 B6 B7] represent the top
half of the block and the other bytes represent the bottom half.
*/
if ( (*pData)[0] <= (*pData)[1])
{
for (i=0; i<4; i++)
{
p[0] = *(*pData)++;
p[1] = *(*pData)++;
p[2] = *(*pData)++;
p[3] = *(*pData)++;
pat[0] = *(*pData)++;
pat[1] = *(*pData)++;
pat[2] = *(*pData)++;
pat[3] = *(*pData)++;
patternQuadrant4Pixels(*pFrame, pat[0], pat[1], pat[2], pat[3], p);
if (i & 1)
*pFrame -= (4*g_width - 4);
else
*pFrame += 4*g_width;
}
}
else
{
if ( (*pData)[12] <= (*pData)[13])
{
// split vertical
for (i=0; i<4; i++)
{
if ((i&1) == 0)
{
p[0] = *(*pData)++;
p[1] = *(*pData)++;
p[2] = *(*pData)++;
p[3] = *(*pData)++;
}
pat[0] = *(*pData)++;
pat[1] = *(*pData)++;
pat[2] = *(*pData)++;
pat[3] = *(*pData)++;
patternQuadrant4Pixels(*pFrame, pat[0], pat[1], pat[2], pat[3], p);
if (i & 1)
*pFrame -= (4*g_width - 4);
else
*pFrame += 4*g_width;
}
}
else
{
// split horizontal
for (i=0; i<8; i++)
{
if ((i&3) == 0)
{
p[0] = *(*pData)++;
p[1] = *(*pData)++;
p[2] = *(*pData)++;
p[3] = *(*pData)++;
}
pat[0] = *(*pData)++;
pat[1] = *(*pData)++;
patternRow4Pixels(*pFrame, pat[0], pat[1], p);
*pFrame += g_width;
}
*pFrame -= (8*g_width - 8);
}
}
break;
case 0xb:
/* In this encoding we get raw pixel data in the data stream -- 64
bytes of pixel data. 1 byte for each pixel, and in the standard
order (l->r, t->b).
*/
for (i=0; i<8; i++)
{
memcpy(*pFrame, *pData, 8);
*pFrame += g_width;
*pData += 8;
*pDataRemain -= 8;
}
*pFrame -= (8*g_width - 8);
break;
case 0xc:
/* In this encoding we get raw pixel data in the data stream -- 16
bytes of pixel data. 1 byte for each block of 2x2 pixels, and in
the standard order (l->r, t->b).
*/
for (i=0; i<4; i++)
{
for (j=0; j<2; j++)
{
for (k=0; k<4; k++)
{
(*pFrame)[2*k] = (*pData)[k];
(*pFrame)[2*k+1] = (*pData)[k];
}
*pFrame += g_width;
}
*pData += 4;
*pDataRemain -= 4;
}
*pFrame -= (8*g_width - 8);
break;
case 0xd:
/* In this encoding we get raw pixel data in the data stream -- 4
bytes of pixel data. 1 byte for each block of 4x4 pixels, and in
the standard order (l->r, t->b).
*/
for (i=0; i<2; i++)
{
for (j=0; j<4; j++)
{
for (k=0; k<4; k++)
{
(*pFrame)[k*g_width+j] = (*pData)[0];
(*pFrame)[k*g_width+j+4] = (*pData)[1];
}
}
*pFrame += 4*g_width;
*pData += 2;
*pDataRemain -= 2;
}
*pFrame -= (8*g_width - 8);
break;
case 0xe:
/* This encoding represents a solid 8x8 frame. We get 1 byte of pixel
data from the data stream.
*/
for (i=0; i<8; i++)
{
memset(*pFrame, **pData, 8);
*pFrame += g_width;
}
++*pData;
--*pDataRemain;
*pFrame -= (8*g_width - 8);
break;
case 0xf:
/* This encoding represents a "dithered" frame, which is
checkerboarded with alternate pixels of two colors. We get 2
bytes of pixel data from the data stream, and these bytes are
alternated:
P0 P1 P0 P1 P0 P1 P0 P1
P1 P0 P1 P0 P1 P0 P1 P0
...
P0 P1 P0 P1 P0 P1 P0 P1
P1 P0 P1 P0 P1 P0 P1 P0
*/
for (i=0; i<8; i++)
{
for (j=0; j<8; j++)
{
(*pFrame)[j] = (*pData)[(i+j)&1];
}
*pFrame += g_width;
}
*pData += 2;
*pDataRemain -= 2;
*pFrame -= (8*g_width - 8);
break;
default:
break;
}
}

16
libmve/decoders.h Normal file
View File

@@ -0,0 +1,16 @@
/*
*
* INTERNAL header - not to be included outside of libmve
*
*/
#ifndef _DECODERS_H
#define _DECODERS_H
extern int g_width, g_height;
extern void *g_vBackBuf1, *g_vBackBuf2;
extern void decodeFrame8(unsigned char *pFrame, unsigned char *pMap, int mapRemain, unsigned char *pData, int dataRemain);
extern void decodeFrame16(unsigned char *pFrame, unsigned char *pMap, int mapRemain, unsigned char *pData, int dataRemain);
#endif // _DECODERS_H

44
libmve/libmve.h Normal file
View File

@@ -0,0 +1,44 @@
#ifndef _LIBMVE_H
#define _LIBMVE_H
#define MVE_ERR_EOF 1
typedef struct{
int screenWidth;
int screenHeight;
int width;
int height;
int truecolor;
} MVE_videoSpec;
int MVE_rmPrepMovie(void *stream, int x, int y, int track);
int MVE_rmStepMovie(void);
void MVE_rmHoldMovie(void);
void MVE_rmEndMovie(void);
void MVE_getVideoSpec(MVE_videoSpec *vSpec);
void MVE_sndInit(int x);
typedef unsigned int (*mve_cb_Read)(void *stream,
void *buffer,
unsigned int count);
typedef void *(*mve_cb_Alloc)(unsigned int size);
typedef void (*mve_cb_Free)(void *ptr);
typedef void (*mve_cb_ShowFrame)(unsigned char *buffer,
unsigned int bufw, unsigned int bufh,
unsigned int sx, unsigned int sy,
unsigned int w, unsigned int h,
unsigned int dstx, unsigned int dsty);
typedef void (*mve_cb_SetPalette)(unsigned char *p,
unsigned int start, unsigned int count);
void MVE_ioCallbacks(mve_cb_Read io_read);
void MVE_memCallbacks(mve_cb_Alloc mem_alloc, mve_cb_Free mem_free);
void MVE_sfCallbacks(mve_cb_ShowFrame showframe);
void MVE_palCallbacks(mve_cb_SetPalette setpalette);
#endif /* _LIBMVE_H */

55
libmve/mve_audio.c Normal file
View File

@@ -0,0 +1,55 @@
static int audio_exp_table[256] =
{
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 47, 51, 56, 61,
66, 72, 79, 86, 94, 102, 112, 122, 133, 145, 158, 173, 189, 206, 225, 245,
267, 292, 318, 348, 379, 414, 452, 493, 538, 587, 640, 699, 763, 832, 908, 991,
1081, 1180, 1288, 1405, 1534, 1673, 1826, 1993, 2175, 2373, 2590, 2826, 3084, 3365, 3672, 4008,
4373, 4772, 5208, 5683, 6202, 6767, 7385, 8059, 8794, 9597, 10472, 11428, 12471, 13609, 14851, 16206,
17685, 19298, 21060, 22981, 25078, 27367, 29864, 32589, -29973, -26728, -23186, -19322, -15105, -10503, -5481, -1,
1, 1, 5481, 10503, 15105, 19322, 23186, 26728, 29973, -32589, -29864, -27367, -25078, -22981, -21060, -19298,
-17685, -16206, -14851, -13609, -12471, -11428, -10472, -9597, -8794, -8059, -7385, -6767, -6202, -5683, -5208, -4772,
-4373, -4008, -3672, -3365, -3084, -2826, -2590, -2373, -2175, -1993, -1826, -1673, -1534, -1405, -1288, -1180,
-1081, -991, -908, -832, -763, -699, -640, -587, -538, -493, -452, -414, -379, -348, -318, -292,
-267, -245, -225, -206, -189, -173, -158, -145, -133, -122, -112, -102, -94, -86, -79, -72,
-66, -61, -56, -51, -47, -43, -42, -41, -40, -39, -38, -37, -36, -35, -34, -33,
-32, -31, -30, -29, -28, -27, -26, -25, -24, -23, -22, -21, -20, -19, -18, -17,
-16, -15, -14, -13, -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1
};
static int getWord(unsigned char **fin)
{
int value = ((*fin)[1] << 8) | (*fin)[0];
*fin += 2;
return value;
}
static void sendWord(short **fout, int nOffset)
{
*(*fout)++ = nOffset;
}
static void processSwath(short *fout, unsigned char *data, int swath, int *offsets)
{
int i;
for (i=0; i<swath; i++)
{
offsets[i&1] += audio_exp_table[data[i]];
sendWord(&fout, offsets[i&1]);
}
}
void mveaudio_uncompress(short *buffer, unsigned char *data, int length)
{
int nCurOffsets[2];
int swath;
data += 4;
swath = getWord(&data) / 2;
nCurOffsets[0] = getWord(&data);
nCurOffsets[1] = getWord(&data);
sendWord(&buffer, nCurOffsets[0]);
sendWord(&buffer, nCurOffsets[1]);
processSwath(buffer, data, swath, nCurOffsets);
}

6
libmve/mve_audio.h Normal file
View File

@@ -0,0 +1,6 @@
#ifndef INCLUDED_MVE_AUDIO_H
#define INCLUDED_MVE_AUDIO_H
void mveaudio_uncompress(short *buffer, unsigned char *data, int length);
#endif /* INCLUDED_MVE_AUDIO_H */

520
libmve/mvelib.c Normal file
View File

@@ -0,0 +1,520 @@
#ifdef HAVE_CONFIG_H
#include "conf.h"
#endif
#include <string.h> // for mem* functions
#if !defined(_WIN32) && !defined(macintosh)
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#endif
#include "mvelib.h"
static const char MVE_HEADER[] = "Interplay MVE File\x1A";
static const short MVE_HDRCONST1 = 0x001A;
static const short MVE_HDRCONST2 = 0x0100;
static const short MVE_HDRCONST3 = 0x1133;
mve_cb_Read mve_read;
mve_cb_Alloc mve_alloc;
mve_cb_Free mve_free;
mve_cb_ShowFrame mve_showframe;
mve_cb_SetPalette mve_setpalette;
/*
* private utility functions
*/
static short _mve_get_short(unsigned char *data);
static unsigned short _mve_get_ushort(unsigned char *data);
/*
* private functions for mvefile
*/
static MVEFILE *_mvefile_alloc(void);
static void _mvefile_free(MVEFILE *movie);
static int _mvefile_open(MVEFILE *movie, void *stream);
static void _mvefile_reset(MVEFILE *movie);
static int _mvefile_read_header(MVEFILE *movie);
static void _mvefile_set_buffer_size(MVEFILE *movie, int buf_size);
static int _mvefile_fetch_next_chunk(MVEFILE *movie);
/*
* private functions for mvestream
*/
static MVESTREAM *_mvestream_alloc(void);
static void _mvestream_free(MVESTREAM *movie);
static int _mvestream_open(MVESTREAM *movie, void *stream);
static void _mvestream_reset(MVESTREAM *movie);
/************************************************************
* public MVEFILE functions
************************************************************/
/*
* open an MVE file
*/
MVEFILE *mvefile_open(void *stream)
{
MVEFILE *file;
/* create the file */
file = _mvefile_alloc();
if (! _mvefile_open(file, stream))
{
_mvefile_free(file);
return NULL;
}
/* initialize the file */
_mvefile_set_buffer_size(file, 1024);
/* verify the file's header */
if (! _mvefile_read_header(file))
{
_mvefile_free(file);
return NULL;
}
/* now, prefetch the next chunk */
_mvefile_fetch_next_chunk(file);
return file;
}
/*
* close a MVE file
*/
void mvefile_close(MVEFILE *movie)
{
_mvefile_free(movie);
}
/*
* reset a MVE file
*/
void mvefile_reset(MVEFILE *file)
{
_mvefile_reset(file);
/* initialize the file */
_mvefile_set_buffer_size(file, 1024);
/* verify the file's header */
if (! _mvefile_read_header(file))
{
_mvefile_free(file);
//return NULL;
}
/* now, prefetch the next chunk */
_mvefile_fetch_next_chunk(file);
}
/*
* get the size of the next segment
*/
int mvefile_get_next_segment_size(MVEFILE *movie)
{
/* if nothing is cached, fail */
if (movie->cur_chunk == NULL || movie->next_segment >= movie->cur_fill)
return -1;
/* if we don't have enough data to get a segment, fail */
if (movie->cur_fill - movie->next_segment < 4)
return -1;
/* otherwise, get the data length */
return _mve_get_short(movie->cur_chunk + movie->next_segment);
}
/*
* get type of next segment in chunk (0xff if no more segments in chunk)
*/
unsigned char mvefile_get_next_segment_major(MVEFILE *movie)
{
/* if nothing is cached, fail */
if (movie->cur_chunk == NULL || movie->next_segment >= movie->cur_fill)
return 0xff;
/* if we don't have enough data to get a segment, fail */
if (movie->cur_fill - movie->next_segment < 4)
return 0xff;
/* otherwise, get the data length */
return movie->cur_chunk[movie->next_segment + 2];
}
/*
* get subtype (version) of next segment in chunk (0xff if no more segments in
* chunk)
*/
unsigned char mvefile_get_next_segment_minor(MVEFILE *movie)
{
/* if nothing is cached, fail */
if (movie->cur_chunk == NULL || movie->next_segment >= movie->cur_fill)
return 0xff;
/* if we don't have enough data to get a segment, fail */
if (movie->cur_fill - movie->next_segment < 4)
return 0xff;
/* otherwise, get the data length */
return movie->cur_chunk[movie->next_segment + 3];
}
/*
* see next segment (return NULL if no next segment)
*/
unsigned char *mvefile_get_next_segment(MVEFILE *movie)
{
/* if nothing is cached, fail */
if (movie->cur_chunk == NULL || movie->next_segment >= movie->cur_fill)
return NULL;
/* if we don't have enough data to get a segment, fail */
if (movie->cur_fill - movie->next_segment < 4)
return NULL;
/* otherwise, get the data length */
return movie->cur_chunk + movie->next_segment + 4;
}
/*
* advance to next segment
*/
void mvefile_advance_segment(MVEFILE *movie)
{
/* if nothing is cached, fail */
if (movie->cur_chunk == NULL || movie->next_segment >= movie->cur_fill)
return;
/* if we don't have enough data to get a segment, fail */
if (movie->cur_fill - movie->next_segment < 4)
return;
/* else, advance to next segment */
movie->next_segment +=
(4 + _mve_get_ushort(movie->cur_chunk + movie->next_segment));
}
/*
* fetch the next chunk (return 0 if at end of stream)
*/
int mvefile_fetch_next_chunk(MVEFILE *movie)
{
return _mvefile_fetch_next_chunk(movie);
}
/************************************************************
* public MVESTREAM functions
************************************************************/
/*
* open an MVE stream
*/
MVESTREAM *mve_open(void *stream)
{
MVESTREAM *movie;
/* allocate */
movie = _mvestream_alloc();
/* open */
if (! _mvestream_open(movie, stream))
{
_mvestream_free(movie);
return NULL;
}
return movie;
}
/*
* close an MVE stream
*/
void mve_close(MVESTREAM *movie)
{
_mvestream_free(movie);
}
/*
* reset an MVE stream
*/
void mve_reset(MVESTREAM *movie)
{
_mvestream_reset(movie);
}
/*
* set segment type handler
*/
void mve_set_handler(MVESTREAM *movie, unsigned char major, MVESEGMENTHANDLER handler)
{
if (major < 32)
movie->handlers[major] = handler;
}
/*
* set segment handler context
*/
void mve_set_handler_context(MVESTREAM *movie, void *context)
{
movie->context = context;
}
/*
* play next chunk
*/
int mve_play_next_chunk(MVESTREAM *movie)
{
unsigned char major, minor;
unsigned char *data;
int len;
/* loop over segments */
major = mvefile_get_next_segment_major(movie->movie);
while (major != 0xff)
{
/* check whether to handle the segment */
if (major < 32 && movie->handlers[major] != NULL)
{
minor = mvefile_get_next_segment_minor(movie->movie);
len = mvefile_get_next_segment_size(movie->movie);
data = mvefile_get_next_segment(movie->movie);
if (! movie->handlers[major](major, minor, data, len, movie->context))
return 0;
}
/* advance to next segment */
mvefile_advance_segment(movie->movie);
major = mvefile_get_next_segment_major(movie->movie);
}
if (! mvefile_fetch_next_chunk(movie->movie))
return 0;
/* return status */
return 1;
}
/************************************************************
* private functions
************************************************************/
/*
* allocate an MVEFILE
*/
static MVEFILE *_mvefile_alloc(void)
{
MVEFILE *file = (MVEFILE *)mve_alloc(sizeof(MVEFILE));
file->stream = NULL;
file->cur_chunk = NULL;
file->buf_size = 0;
file->cur_fill = 0;
file->next_segment = 0;
return file;
}
/*
* free an MVE file
*/
static void _mvefile_free(MVEFILE *movie)
{
/* free the stream */
movie->stream = NULL;
/* free the buffer */
if (movie->cur_chunk)
mve_free(movie->cur_chunk);
movie->cur_chunk = NULL;
/* not strictly necessary */
movie->buf_size = 0;
movie->cur_fill = 0;
movie->next_segment = 0;
/* free the struct */
mve_free(movie);
}
/*
* open the file stream in thie object
*/
static int _mvefile_open(MVEFILE *file, void *stream)
{
file->stream = stream;
if (! file->stream)
return 0;
return 1;
}
/*
* allocate an MVEFILE
*/
static void _mvefile_reset(MVEFILE *file)
{
#if 0
file->cur_chunk = NULL;
file->buf_size = 0;
file->cur_fill = 0;
file->next_segment = 0;
#endif
}
/*
* read and verify the header of the recently opened file
*/
static int _mvefile_read_header(MVEFILE *movie)
{
unsigned char buffer[26];
/* check the file is open */
if (! movie->stream)
return 0;
/* check the file is long enough */
if (! mve_read(movie->stream, buffer, 26))
return 0;
/* check the signature */
if (memcmp(buffer, MVE_HEADER, 20))
return 0;
/* check the hard-coded constants */
if (_mve_get_short(buffer+20) != MVE_HDRCONST1)
return 0;
if (_mve_get_short(buffer+22) != MVE_HDRCONST2)
return 0;
if (_mve_get_short(buffer+24) != MVE_HDRCONST3)
return 0;
return 1;
}
static void _mvefile_set_buffer_size(MVEFILE *movie, int buf_size)
{
unsigned char *new_buffer;
int new_len;
/* check if this would be a redundant operation */
if (buf_size <= movie->buf_size)
return;
/* allocate new buffer */
new_len = 100 + buf_size;
new_buffer = (unsigned char *)mve_alloc(new_len);
/* copy old data */
if (movie->cur_chunk && movie->cur_fill)
memcpy(new_buffer, movie->cur_chunk, movie->cur_fill);
/* free old buffer */
if (movie->cur_chunk)
{
mve_free(movie->cur_chunk);
movie->cur_chunk = 0;
}
/* install new buffer */
movie->cur_chunk = new_buffer;
movie->buf_size = new_len;
}
static int _mvefile_fetch_next_chunk(MVEFILE *movie)
{
unsigned char buffer[4];
unsigned short length;
/* fail if not open */
if (! movie->stream)
return 0;
/* fail if we can't read the next segment descriptor */
if (! mve_read(movie->stream, buffer, 4))
return 0;
/* pull out the next length */
length = _mve_get_short(buffer);
/* make sure we've got sufficient space */
_mvefile_set_buffer_size(movie, length);
/* read the chunk */
if (! mve_read(movie->stream, movie->cur_chunk, length))
return 0;
movie->cur_fill = length;
movie->next_segment = 0;
return 1;
}
static short _mve_get_short(unsigned char *data)
{
short value;
value = data[0] | (data[1] << 8);
return value;
}
static unsigned short _mve_get_ushort(unsigned char *data)
{
unsigned short value;
value = data[0] | (data[1] << 8);
return value;
}
/*
* allocate an MVESTREAM
*/
static MVESTREAM *_mvestream_alloc(void)
{
MVESTREAM *movie;
/* allocate and zero-initialize everything */
movie = (MVESTREAM *)mve_alloc(sizeof(MVESTREAM));
movie->movie = NULL;
movie->context = 0;
memset(movie->handlers, 0, sizeof(movie->handlers));
return movie;
}
/*
* free an MVESTREAM
*/
static void _mvestream_free(MVESTREAM *movie)
{
/* close MVEFILE */
if (movie->movie)
mvefile_close(movie->movie);
movie->movie = NULL;
/* clear context and handlers */
movie->context = NULL;
memset(movie->handlers, 0, sizeof(movie->handlers));
/* free the struct */
mve_free(movie);
}
/*
* open an MVESTREAM object
*/
static int _mvestream_open(MVESTREAM *movie, void *stream)
{
movie->movie = mvefile_open(stream);
return (movie->movie == NULL) ? 0 : 1;
}
/*
* reset an MVESTREAM
*/
static void _mvestream_reset(MVESTREAM *movie)
{
mvefile_reset(movie->movie);
}

113
libmve/mvelib.h Normal file
View File

@@ -0,0 +1,113 @@
#ifndef INCLUDED_MVELIB_H
#define INCLUDED_MVELIB_H
#include <stdio.h>
#include <stdlib.h>
#include "libmve.h"
extern mve_cb_Read mve_read;
extern mve_cb_Alloc mve_alloc;
extern mve_cb_Free mve_free;
extern mve_cb_ShowFrame mve_showframe;
extern mve_cb_SetPalette mve_setpalette;
/*
* structure for maintaining info on a MVEFILE stream
*/
typedef struct MVEFILE
{
void *stream;
unsigned char *cur_chunk;
int buf_size;
int cur_fill;
int next_segment;
} MVEFILE;
/*
* open a .MVE file
*/
MVEFILE *mvefile_open(void *stream);
/*
* close a .MVE file
*/
void mvefile_close(MVEFILE *movie);
/*
* get size of next segment in chunk (-1 if no more segments in chunk)
*/
int mvefile_get_next_segment_size(MVEFILE *movie);
/*
* get type of next segment in chunk (0xff if no more segments in chunk)
*/
unsigned char mvefile_get_next_segment_major(MVEFILE *movie);
/*
* get subtype (version) of next segment in chunk (0xff if no more segments in
* chunk)
*/
unsigned char mvefile_get_next_segment_minor(MVEFILE *movie);
/*
* see next segment (return NULL if no next segment)
*/
unsigned char *mvefile_get_next_segment(MVEFILE *movie);
/*
* advance to next segment
*/
void mvefile_advance_segment(MVEFILE *movie);
/*
* fetch the next chunk (return 0 if at end of stream)
*/
int mvefile_fetch_next_chunk(MVEFILE *movie);
/*
* callback for segment type
*/
typedef int (*MVESEGMENTHANDLER)(unsigned char major, unsigned char minor, unsigned char *data, int len, void *context);
/*
* structure for maintaining an MVE stream
*/
typedef struct MVESTREAM
{
MVEFILE *movie;
void *context;
MVESEGMENTHANDLER handlers[32];
} MVESTREAM;
/*
* open an MVE stream
*/
MVESTREAM *mve_open(void *stream);
/*
* close an MVE stream
*/
void mve_close(MVESTREAM *movie);
/*
* reset an MVE stream
*/
void mve_reset(MVESTREAM *movie);
/*
* set segment type handler
*/
void mve_set_handler(MVESTREAM *movie, unsigned char major, MVESEGMENTHANDLER handler);
/*
* set segment handler context
*/
void mve_set_handler_context(MVESTREAM *movie, void *context);
/*
* play next chunk
*/
int mve_play_next_chunk(MVESTREAM *movie);
#endif /* INCLUDED_MVELIB_H */

749
libmve/mveplay.c Normal file
View File

@@ -0,0 +1,749 @@
#ifdef HAVE_CONFIG_H
#include <conf.h>
#endif
#ifndef __MSDOS__
#define AUDIO
#endif
//#define DEBUG
#include <string.h>
#ifdef _WIN32
# include <windows.h>
#else
# include <errno.h>
# include <time.h>
# include <fcntl.h>
# ifdef macintosh
# include <types.h>
# include <OSUtils.h>
# else
# include <sys/time.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <unistd.h>
# endif // macintosh
#endif // _WIN32
#if defined(AUDIO)
#include <SDL.h>
#include "SDL_mixer.h"
#endif
#include "mvelib.h"
#include "mve_audio.h"
#include "decoders.h"
#include "libmve.h"
#define MVE_OPCODE_ENDOFSTREAM 0x00
#define MVE_OPCODE_ENDOFCHUNK 0x01
#define MVE_OPCODE_CREATETIMER 0x02
#define MVE_OPCODE_INITAUDIOBUFFERS 0x03
#define MVE_OPCODE_STARTSTOPAUDIO 0x04
#define MVE_OPCODE_INITVIDEOBUFFERS 0x05
#define MVE_OPCODE_DISPLAYVIDEO 0x07
#define MVE_OPCODE_AUDIOFRAMEDATA 0x08
#define MVE_OPCODE_AUDIOFRAMESILENCE 0x09
#define MVE_OPCODE_INITVIDEOMODE 0x0A
#define MVE_OPCODE_SETPALETTE 0x0C
#define MVE_OPCODE_SETPALETTECOMPRESSED 0x0D
#define MVE_OPCODE_SETDECODINGMAP 0x0F
#define MVE_OPCODE_VIDEODATA 0x11
#define MVE_AUDIO_FLAGS_STEREO 1
#define MVE_AUDIO_FLAGS_16BIT 2
#define MVE_AUDIO_FLAGS_COMPRESSED 4
int g_spdFactorNum=0;
static int g_spdFactorDenom=10;
static int g_frameUpdated = 0;
static short get_short(unsigned char *data)
{
short value;
value = data[0] | (data[1] << 8);
return value;
}
static unsigned short get_ushort(unsigned char *data)
{
unsigned short value;
value = data[0] | (data[1] << 8);
return value;
}
static int get_int(unsigned char *data)
{
int value;
value = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
return value;
}
static unsigned int unhandled_chunks[32*256];
static int default_seg_handler(unsigned char major, unsigned char minor, unsigned char *data, int len, void *context)
{
unhandled_chunks[major<<8|minor]++;
//fprintf(stderr, "unknown chunk type %02x/%02x\n", major, minor);
return 1;
}
/*************************
* general handlers
*************************/
static int end_movie_handler(unsigned char major, unsigned char minor, unsigned char *data, int len, void *context)
{
return 0;
}
/*************************
* timer handlers
*************************/
/*
* timer variables
*/
static int timer_created = 0;
static int micro_frame_delay=0;
static int timer_started=0;
static unsigned long int timer_expire = 0;
#if defined(_WIN32) || defined(macintosh)
unsigned long int timer_getmicroseconds()
{
static int counter = 0;
#ifdef _WIN32
DWORD now = GetTickCount();
#else
long now = TickCount();
#endif
counter++;
return now * 1000 + counter;
}
#else
unsigned long int timer_getmicroseconds()
{
struct timeval tv;
static time_t starttime = 0;
gettimeofday(&tv, NULL);
if (!starttime)
starttime = tv.tv_sec;
return (tv.tv_sec - starttime) * 1000000 + tv.tv_usec;
}
#endif
void timer_sleepmicroseconds(unsigned long int usec)
{
#ifdef _WIN32
Sleep(usec / 1000);
#elif defined(macintosh)
Delay(usec / 1000);
#else
struct timespec ts;
ts.tv_sec = usec / 1000000;
ts.tv_nsec = usec % 1000000 * 1000;
nanosleep(&ts, NULL);
#endif
}
static int create_timer_handler(unsigned char major, unsigned char minor, unsigned char *data, int len, void *context)
{
#if !defined(_WIN32) && !defined(macintosh) // FIXME
__extension__ long long temp;
#else
long temp;
#endif
if (timer_created)
return 1;
else
timer_created = 1;
micro_frame_delay = get_int(data) * (int)get_short(data+4);
if (g_spdFactorNum != 0)
{
temp = micro_frame_delay;
temp *= g_spdFactorNum;
temp /= g_spdFactorDenom;
micro_frame_delay = (int)temp;
}
return 1;
}
static void timer_stop(void)
{
timer_expire = 0;
timer_started = 0;
}
static void timer_start(void)
{
timer_expire = timer_getmicroseconds();
timer_expire += micro_frame_delay;
timer_started=1;
}
static void do_timer_wait(void)
{
unsigned long int ts;
unsigned long int tv;
if (! timer_started)
return;
tv = timer_getmicroseconds();
if (tv > timer_expire)
goto end;
ts = timer_expire - tv;
timer_sleepmicroseconds(ts);
end:
timer_expire += micro_frame_delay;
}
/*************************
* audio handlers
*************************/
#ifdef AUDIO
#define TOTAL_AUDIO_BUFFERS 64
static int audiobuf_created = 0;
static void mve_audio_callback(void *userdata, unsigned char *stream, int len);
static short *mve_audio_buffers[TOTAL_AUDIO_BUFFERS];
static int mve_audio_buflens[TOTAL_AUDIO_BUFFERS];
static int mve_audio_curbuf_curpos=0;
static int mve_audio_bufhead=0;
static int mve_audio_buftail=0;
static int mve_audio_playing=0;
static int mve_audio_canplay=0;
static int mve_audio_compressed=0;
static int mve_audio_enabled = 1;
static void mve_audio_callback(void *userdata, unsigned char *stream, int len)
{
int total=0;
int length;
if (mve_audio_bufhead == mve_audio_buftail)
return /* 0 */;
//fprintf(stderr, "+ <%d (%d), %d, %d>\n", mve_audio_bufhead, mve_audio_curbuf_curpos, mve_audio_buftail, len);
while (mve_audio_bufhead != mve_audio_buftail /* while we have more buffers */
&& len > (mve_audio_buflens[mve_audio_bufhead]-mve_audio_curbuf_curpos)) /* and while we need more data */
{
length = mve_audio_buflens[mve_audio_bufhead]-mve_audio_curbuf_curpos;
memcpy(stream, /* cur output position */
((unsigned char *)mve_audio_buffers[mve_audio_bufhead])+mve_audio_curbuf_curpos, /* cur input position */
length); /* cur input length */
total += length;
stream += length; /* advance output */
len -= length; /* decrement avail ospace */
mve_free(mve_audio_buffers[mve_audio_bufhead]); /* free the buffer */
mve_audio_buffers[mve_audio_bufhead]=NULL; /* free the buffer */
mve_audio_buflens[mve_audio_bufhead]=0; /* free the buffer */
if (++mve_audio_bufhead == TOTAL_AUDIO_BUFFERS) /* next buffer */
mve_audio_bufhead = 0;
mve_audio_curbuf_curpos = 0;
}
//fprintf(stderr, "= <%d (%d), %d, %d>: %d\n", mve_audio_bufhead, mve_audio_curbuf_curpos, mve_audio_buftail, len, total);
/* return total; */
if (len != 0 /* ospace remaining */
&& mve_audio_bufhead != mve_audio_buftail) /* buffers remaining */
{
memcpy(stream, /* dest */
((unsigned char *)mve_audio_buffers[mve_audio_bufhead]) + mve_audio_curbuf_curpos, /* src */
len); /* length */
mve_audio_curbuf_curpos += len; /* advance input */
stream += len; /* advance output (unnecessary) */
len -= len; /* advance output (unnecessary) */
if (mve_audio_curbuf_curpos >= mve_audio_buflens[mve_audio_bufhead]) /* if this ends the current chunk */
{
mve_free(mve_audio_buffers[mve_audio_bufhead]); /* free buffer */
mve_audio_buffers[mve_audio_bufhead]=NULL;
mve_audio_buflens[mve_audio_bufhead]=0;
if (++mve_audio_bufhead == TOTAL_AUDIO_BUFFERS) /* next buffer */
mve_audio_bufhead = 0;
mve_audio_curbuf_curpos = 0;
}
}
//fprintf(stderr, "- <%d (%d), %d, %d>\n", mve_audio_bufhead, mve_audio_curbuf_curpos, mve_audio_buftail, len);
}
#endif
static int create_audiobuf_handler(unsigned char major, unsigned char minor, unsigned char *data, int len, void *context)
{
#ifdef AUDIO
int flags;
int sample_rate;
int desired_buffer;
int stereo;
int bitsize;
int compressed;
int format;
if (!mve_audio_enabled)
return 1;
if (audiobuf_created)
return 1;
else
audiobuf_created = 1;
flags = get_ushort(data + 2);
sample_rate = get_ushort(data + 4);
desired_buffer = get_int(data + 6);
stereo = (flags & MVE_AUDIO_FLAGS_STEREO) ? 1 : 0;
bitsize = (flags & MVE_AUDIO_FLAGS_16BIT) ? 1 : 0;
if (minor > 0) {
compressed = flags & MVE_AUDIO_FLAGS_COMPRESSED ? 1 : 0;
} else {
compressed = 0;
}
mve_audio_compressed = compressed;
if (bitsize == 1) {
#ifdef WORDS_BIGENDIAN
format = AUDIO_S16MSB;
#else
format = AUDIO_S16LSB;
#endif
} else {
format = AUDIO_U8;
}
fprintf(stderr, "creating audio buffers:\n");
fprintf(stderr, "sample rate = %d, stereo = %d, bitsize = %d, compressed = %d\n",
sample_rate, stereo, bitsize ? 16 : 8, compressed);
if (Mix_OpenAudio(sample_rate, format, stereo ? 2 : 1, 4096) == 0)
{
fprintf(stderr, " success\n");
mve_audio_canplay = 1;
}
else
{
fprintf(stderr, " failure : %s\n", Mix_GetError());
mve_audio_canplay = 0;
}
Mix_SetPostMix(mve_audio_callback, NULL);
mve_audio_canplay = 1;
memset(mve_audio_buffers, 0, sizeof(mve_audio_buffers));
memset(mve_audio_buflens, 0, sizeof(mve_audio_buflens));
#endif
return 1;
}
static int play_audio_handler(unsigned char major, unsigned char minor, unsigned char *data, int len, void *context)
{
#ifdef AUDIO
if (mve_audio_canplay && !mve_audio_playing && mve_audio_bufhead != mve_audio_buftail)
{
Mix_Resume(-1);
mve_audio_playing = 1;
}
#endif
return 1;
}
static int audio_data_handler(unsigned char major, unsigned char minor, unsigned char *data, int len, void *context)
{
#ifdef AUDIO
static const int selected_chan=1;
int chan;
int nsamp;
if (mve_audio_canplay)
{
chan = get_ushort(data + 2);
nsamp = get_ushort(data + 4);
if (chan & selected_chan)
{
/* HACK: +4 mveaudio_uncompress adds 4 more bytes */
if (major == MVE_OPCODE_AUDIOFRAMEDATA) {
if (mve_audio_compressed) {
nsamp += 4;
mve_audio_buflens[mve_audio_buftail] = nsamp;
mve_audio_buffers[mve_audio_buftail] = (short *)mve_alloc(nsamp);
mveaudio_uncompress(mve_audio_buffers[mve_audio_buftail], data, -1); /* XXX */
} else {
nsamp -= 8;
data += 8;
mve_audio_buflens[mve_audio_buftail] = nsamp;
mve_audio_buffers[mve_audio_buftail] = (short *)mve_alloc(nsamp);
memcpy(mve_audio_buffers[mve_audio_buftail], data, nsamp);
}
} else {
mve_audio_buflens[mve_audio_buftail] = nsamp;
mve_audio_buffers[mve_audio_buftail] = (short *)mve_alloc(nsamp);
memset(mve_audio_buffers[mve_audio_buftail], 0, nsamp); /* XXX */
}
if (++mve_audio_buftail == TOTAL_AUDIO_BUFFERS)
mve_audio_buftail = 0;
if (mve_audio_buftail == mve_audio_bufhead)
fprintf(stderr, "d'oh! buffer ring overrun (%d)\n", mve_audio_bufhead);
}
}
#endif
return 1;
}
/*************************
* video handlers
*************************/
static int videobuf_created = 0;
static int video_initialized = 0;
int g_width, g_height;
void *g_vBuffers = NULL, *g_vBackBuf1, *g_vBackBuf2;
static int g_destX, g_destY;
static int g_screenWidth, g_screenHeight;
static unsigned char *g_pCurMap=NULL;
static int g_nMapLength=0;
static int g_truecolor;
static int create_videobuf_handler(unsigned char major, unsigned char minor, unsigned char *data, int len, void *context)
{
short w, h;
short count, truecolor;
if (videobuf_created)
return 1;
else
videobuf_created = 1;
w = get_short(data);
h = get_short(data+2);
if (minor > 0) {
count = get_short(data+4);
} else {
count = 1;
}
if (minor > 1) {
truecolor = get_short(data+6);
} else {
truecolor = 0;
}
g_width = w << 3;
g_height = h << 3;
/* TODO: * 4 causes crashes on some files */
/* only malloc once */
if (g_vBuffers == NULL)
g_vBackBuf1 = g_vBuffers = mve_alloc(g_width * g_height * 8);
if (truecolor) {
g_vBackBuf2 = (unsigned short *)g_vBackBuf1 + (g_width * g_height);
} else {
g_vBackBuf2 = (unsigned char *)g_vBackBuf1 + (g_width * g_height);
}
memset(g_vBackBuf1, 0, g_width * g_height * 4);
#ifdef DEBUG
fprintf(stderr, "DEBUG: w,h=%d,%d count=%d, tc=%d\n", w, h, count, truecolor);
#endif
g_truecolor = truecolor;
return 1;
}
static int display_video_handler(unsigned char major, unsigned char minor, unsigned char *data, int len, void *context)
{
if (g_destX == -1) // center it
g_destX = (g_screenWidth - g_width) >> 1;
if (g_destY == -1) // center it
g_destY = (g_screenHeight - g_height) >> 1;
mve_showframe(g_vBackBuf1, g_width, g_height, 0, 0,
g_width, g_height, g_destX, g_destY);
g_frameUpdated = 1;
return 1;
}
static int init_video_handler(unsigned char major, unsigned char minor, unsigned char *data, int len, void *context)
{
short width, height;
if (video_initialized)
return 1; /* maybe we actually need to change width/height here? */
else
video_initialized = 1;
width = get_short(data);
height = get_short(data+2);
g_screenWidth = width;
g_screenHeight = height;
return 1;
}
static int video_palette_handler(unsigned char major, unsigned char minor, unsigned char *data, int len, void *context)
{
short start, count;
unsigned char *p;
start = get_short(data);
count = get_short(data+2);
p = data + 4;
mve_setpalette(p - 3*start, start, count);
return 1;
}
static int video_codemap_handler(unsigned char major, unsigned char minor, unsigned char *data, int len, void *context)
{
g_pCurMap = data;
g_nMapLength = len;
return 1;
}
static int video_data_handler(unsigned char major, unsigned char minor, unsigned char *data, int len, void *context)
{
short nFrameHot, nFrameCold;
short nXoffset, nYoffset;
short nXsize, nYsize;
unsigned short nFlags;
unsigned char *temp;
nFrameHot = get_short(data);
nFrameCold = get_short(data+2);
nXoffset = get_short(data+4);
nYoffset = get_short(data+6);
nXsize = get_short(data+8);
nYsize = get_short(data+10);
nFlags = get_ushort(data+12);
if (nFlags & 1)
{
temp = (unsigned char *)g_vBackBuf1;
g_vBackBuf1 = g_vBackBuf2;
g_vBackBuf2 = temp;
}
/* convert the frame */
if (g_truecolor) {
decodeFrame16((unsigned char *)g_vBackBuf1, g_pCurMap, g_nMapLength, data+14, len-14);
} else {
decodeFrame8(g_vBackBuf1, g_pCurMap, g_nMapLength, data+14, len-14);
}
return 1;
}
static int end_chunk_handler(unsigned char major, unsigned char minor, unsigned char *data, int len, void *context)
{
g_pCurMap=NULL;
return 1;
}
static MVESTREAM *mve = NULL;
void MVE_ioCallbacks(mve_cb_Read io_read)
{
mve_read = io_read;
}
void MVE_memCallbacks(mve_cb_Alloc mem_alloc, mve_cb_Free mem_free)
{
mve_alloc = mem_alloc;
mve_free = mem_free;
}
void MVE_sfCallbacks(mve_cb_ShowFrame showframe)
{
mve_showframe = showframe;
}
void MVE_palCallbacks(mve_cb_SetPalette setpalette)
{
mve_setpalette = setpalette;
}
int MVE_rmPrepMovie(void *src, int x, int y, int track)
{
int i;
if (mve) {
mve_reset(mve);
return 0;
}
mve = mve_open(src);
if (!mve)
return 1;
g_destX = x;
g_destY = y;
for (i = 0; i < 32; i++)
mve_set_handler(mve, i, default_seg_handler);
mve_set_handler(mve, MVE_OPCODE_ENDOFSTREAM, end_movie_handler);
mve_set_handler(mve, MVE_OPCODE_ENDOFCHUNK, end_chunk_handler);
mve_set_handler(mve, MVE_OPCODE_CREATETIMER, create_timer_handler);
mve_set_handler(mve, MVE_OPCODE_INITAUDIOBUFFERS, create_audiobuf_handler);
mve_set_handler(mve, MVE_OPCODE_STARTSTOPAUDIO, play_audio_handler);
mve_set_handler(mve, MVE_OPCODE_INITVIDEOBUFFERS, create_videobuf_handler);
mve_set_handler(mve, MVE_OPCODE_DISPLAYVIDEO, display_video_handler);
mve_set_handler(mve, MVE_OPCODE_AUDIOFRAMEDATA, audio_data_handler);
mve_set_handler(mve, MVE_OPCODE_AUDIOFRAMESILENCE, audio_data_handler);
mve_set_handler(mve, MVE_OPCODE_INITVIDEOMODE, init_video_handler);
mve_set_handler(mve, MVE_OPCODE_SETPALETTE, video_palette_handler);
mve_set_handler(mve, MVE_OPCODE_SETPALETTECOMPRESSED, default_seg_handler);
mve_set_handler(mve, MVE_OPCODE_SETDECODINGMAP, video_codemap_handler);
mve_set_handler(mve, MVE_OPCODE_VIDEODATA, video_data_handler);
mve_play_next_chunk(mve); /* video initialization chunk */
if (mve_audio_enabled)
mve_play_next_chunk(mve); /* audio initialization chunk */
return 0;
}
void MVE_getVideoSpec(MVE_videoSpec *vSpec)
{
vSpec->screenWidth = g_screenWidth;
vSpec->screenHeight = g_screenHeight;
vSpec->width = g_width;
vSpec->height = g_height;
vSpec->truecolor = g_truecolor;
}
int MVE_rmStepMovie()
{
static int init_timer=0;
int cont=1;
if (!timer_started)
timer_start();
while (cont && !g_frameUpdated) // make a "step" be a frame, not a chunk...
cont = mve_play_next_chunk(mve);
g_frameUpdated = 0;
if (!cont)
return MVE_ERR_EOF;
if (micro_frame_delay && !init_timer) {
timer_start();
init_timer = 1;
}
do_timer_wait();
return 0;
}
void MVE_rmEndMovie()
{
#ifdef AUDIO
int i;
#endif
timer_stop();
timer_created = 0;
#ifdef AUDIO
if (mve_audio_canplay) {
// only close audio if we opened it
Mix_CloseAudio();
mve_audio_canplay = 0;
}
for (i = 0; i < TOTAL_AUDIO_BUFFERS; i++)
if (mve_audio_buffers[i] != NULL)
mve_free(mve_audio_buffers[i]);
memset(mve_audio_buffers, 0, sizeof(mve_audio_buffers));
memset(mve_audio_buflens, 0, sizeof(mve_audio_buflens));
mve_audio_curbuf_curpos=0;
mve_audio_bufhead=0;
mve_audio_buftail=0;
mve_audio_playing=0;
mve_audio_canplay=0;
mve_audio_compressed=0;
audiobuf_created = 0;
#endif
mve_free(g_vBuffers);
g_vBuffers = NULL;
g_pCurMap=NULL;
g_nMapLength=0;
videobuf_created = 0;
video_initialized = 0;
mve_close(mve);
mve = NULL;
}
void MVE_rmHoldMovie()
{
timer_started = 0;
}
void MVE_sndInit(int x)
{
#ifdef AUDIO
if (x == -1)
mve_audio_enabled = 0;
else
mve_audio_enabled = 1;
#endif
}