mirror of
https://github.com/kevinbentley/Descent3.git
synced 2026-04-06 17:00:05 -04:00
Add D2X implementation of MVE
Added D2X implementation licensed under GPLv3 terms.
Cut from c030c4531a
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
716
libmve/decoder16.c
Normal 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
871
libmve/decoder8.c
Normal 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
16
libmve/decoders.h
Normal 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
44
libmve/libmve.h
Normal 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
55
libmve/mve_audio.c
Normal 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
6
libmve/mve_audio.h
Normal 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
520
libmve/mvelib.c
Normal 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
113
libmve/mvelib.h
Normal 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
749
libmve/mveplay.c
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user