mirror of
https://github.com/kevinbentley/Descent3.git
synced 2025-01-22 11:28:56 +00:00
1766 lines
50 KiB
C++
1766 lines
50 KiB
C++
/*
|
|
** mvelibl.cpp
|
|
**
|
|
** Interplay Movie File (MVE) Player Library (32-Bit Linux Version)
|
|
** Written by Paul Allen Edelstein. Partial Linux port by Jeff Slutter.
|
|
**
|
|
** (c) 1997 Interplay Productions. All Rights Reserved.
|
|
** This file is confidential and consists of proprietary information
|
|
** of Interplay Productions. This file and associated libraries
|
|
** may not, in whole or in part, be disclosed to third parties,
|
|
** incorporated into any software product which is not being created
|
|
** for Interplay Productions, copied or duplicated in any form,
|
|
** without the prior written permission of Interplay Productions.
|
|
** Further, you may not reverse engineer, decompile or otherwise
|
|
** attempt to derive source code of this material.
|
|
**
|
|
*/
|
|
|
|
/*
|
|
** Linux Specific Notes:
|
|
** This library is dependant off of:
|
|
** -POSIX Threads (-lpthread)
|
|
** -X11 Library (-lX11)
|
|
** -Xext Library (-Xext)
|
|
**
|
|
** Only HiColor mode movies are supported. Others could
|
|
** be supported, but the nf*, etc. functions need to be
|
|
** implemented to do so. See mveasm.cpp.
|
|
**
|
|
** No gfx or resolution changing functions are implemented in
|
|
** here. See the xf86vm X11 extension on how to do this.
|
|
*/
|
|
static char notice1[] = "(c) 1997 Interplay Productions. All Rights Reserved.\n"
|
|
"This file is confidential and consists of proprietary information\n"
|
|
"of Interplay Productions. This file and associated libraries\n"
|
|
"may not, in whole or in part, be disclosed to third parties,\n"
|
|
"incorporated into any software product which is not being created\n"
|
|
"for Interplay Productions, copied or duplicated in any form,\n"
|
|
"without the prior written permission of Interplay Productions.\n"
|
|
"Further, you may not reverse engineer, decompile or otherwise\n"
|
|
"attempt to derive source code of this material.\n";
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "platform.h"
|
|
#include "mvelibi.h"
|
|
#include "mvegfx.h"
|
|
#include "mvelibl.h"
|
|
|
|
static unsigned opt_fastmode = 0; // 0:normal, 1:even lines only, 2:dither between even/odd lines |4 to spread lines
|
|
|
|
unsigned opt_hscale_step = 4; // 3: 4/3, 4:4/4, 5:4/5 (not fully implemented)
|
|
unsigned opt_hscale_adj;
|
|
extern unsigned sf_ScreenHeight;
|
|
extern unsigned sf_ScreenWidth;
|
|
|
|
#define logLabel(x)
|
|
|
|
#include "snd8to16.h"
|
|
// len always specifies length of destination in bytes.
|
|
unsigned sndDecompM16(unsigned short *dst, unsigned char *src, unsigned len, unsigned state);
|
|
unsigned sndDecompS16(unsigned short *dst, unsigned char *src, unsigned len, unsigned state);
|
|
|
|
//----------------------------------------------------------------------
|
|
// Memory Management
|
|
//--------------------
|
|
|
|
static void *(*mem_alloc)(unsigned size);
|
|
static void (*mem_free)(void *p);
|
|
|
|
typedef struct _mem {
|
|
void *ptr;
|
|
unsigned size;
|
|
bool dynamic;
|
|
} MemRec, *MemPtr;
|
|
|
|
static void MemInit(MemPtr m, unsigned size, void *p);
|
|
static void *MemAlloc(MemPtr m, unsigned size);
|
|
static void MemFree(MemPtr m);
|
|
|
|
void MVE_memCallbacks(void *(*fn_alloc)(unsigned size), void (*fn_free)(void *p)) {
|
|
mem_alloc = fn_alloc;
|
|
mem_free = fn_free;
|
|
}
|
|
|
|
static void MemFree(MemPtr m) {
|
|
if (m->dynamic && mem_free) {
|
|
(*mem_free)(m->ptr);
|
|
m->dynamic = false; // prevent from being freed again!
|
|
}
|
|
m->size = 0;
|
|
}
|
|
|
|
static void MemInit(MemPtr m, unsigned size, void *p) {
|
|
if (!p)
|
|
return;
|
|
|
|
MemFree(m);
|
|
m->ptr = p;
|
|
m->size = size;
|
|
m->dynamic = false;
|
|
}
|
|
|
|
static void *MemAlloc(MemPtr m, unsigned size) {
|
|
if (size <= m->size)
|
|
return m->ptr;
|
|
|
|
if (mem_alloc) {
|
|
void *p;
|
|
MemFree(m);
|
|
size += 100; // Add some pad to reduce chance of another realloc.
|
|
p = (*mem_alloc)(size);
|
|
if (!p)
|
|
return (void *)NULL;
|
|
MemInit(m, size, p);
|
|
m->dynamic = true;
|
|
return m->ptr;
|
|
}
|
|
|
|
return (void *)NULL;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Synchronization Management
|
|
//-----------------------------
|
|
|
|
static bool sync_active = false;
|
|
static int sync_time = 0;
|
|
static int sync_wait_quanta = 0;
|
|
|
|
static bool sync_late = false;
|
|
static bool sync_FrameDropped = false;
|
|
|
|
static void syncReset(unsigned long wait_quanta);
|
|
static void syncRelease(void);
|
|
static bool syncInit(unsigned long period, unsigned wait_quanta);
|
|
static bool syncWait(void);
|
|
static void syncSync(void);
|
|
|
|
static void syncReset(unsigned long wait_quanta) {
|
|
sync_time = wait_quanta - platform_timeGetTime() * 1000;
|
|
sync_active = true;
|
|
}
|
|
|
|
static void syncRelease(void) { sync_active = false; }
|
|
|
|
static bool syncInit(unsigned long period, unsigned wait_quanta) {
|
|
int new_wait_quanta = -(long)(period * wait_quanta + (wait_quanta >> 1));
|
|
|
|
// If timer is still running and has same timing
|
|
// characteristics, assume we are trying to continue smoothly
|
|
// with another movie and ignore new syncInit() call.
|
|
if (sync_active && sync_wait_quanta == new_wait_quanta)
|
|
return true;
|
|
|
|
syncWait();
|
|
sync_wait_quanta = new_wait_quanta;
|
|
syncReset(sync_wait_quanta);
|
|
return true;
|
|
}
|
|
|
|
// Returns true if actually waited, false if called too late to wait.
|
|
static bool syncWait(void) {
|
|
bool waited = false;
|
|
if (!sync_active)
|
|
return false;
|
|
|
|
while ((int)(sync_time + platform_timeGetTime() * 1000) < 0) {
|
|
waited = true;
|
|
}
|
|
|
|
sync_time += sync_wait_quanta;
|
|
return waited;
|
|
}
|
|
|
|
// Returns true if actually waited, false if called too late to wait.
|
|
static int syncWaitLevel(int level) {
|
|
int waited;
|
|
if (!sync_active)
|
|
return 0;
|
|
|
|
level += sync_time;
|
|
|
|
for (;;) {
|
|
waited = level + platform_timeGetTime() * 1000;
|
|
if (waited >= 0)
|
|
break;
|
|
}
|
|
|
|
sync_time += sync_wait_quanta;
|
|
return waited;
|
|
}
|
|
|
|
static void syncSync(void) {
|
|
if (sync_active) {
|
|
while ((int)(sync_time + platform_timeGetTime() * 1000) < 0)
|
|
;
|
|
}
|
|
}
|
|
|
|
int MVE_syncTime(void) {
|
|
if (sync_active)
|
|
return sync_time + platform_timeGetTime() * 1000;
|
|
return 0;
|
|
}
|
|
|
|
void MVE_logDumpStats(void) {}
|
|
|
|
//----------------------------------------------------------------------
|
|
// I/O Management
|
|
//-----------------
|
|
|
|
static unsigned (*io_read)(int handle, void *buf, unsigned count);
|
|
|
|
static MemRec io_mem_buf;
|
|
|
|
static int io_handle;
|
|
static ioHdrRec io_next_hdr;
|
|
|
|
static bool ioReset(int h);
|
|
static unsigned char *ioRead(unsigned qty);
|
|
static unsigned char *ioNextRecord(void);
|
|
static void ioRelease(void);
|
|
|
|
void MVE_ioCallbacks(unsigned (*fn_read)(int handle, void *buf, unsigned count)) { io_read = fn_read; }
|
|
|
|
static bool ioReset(int h) {
|
|
mve_hdr *hdr;
|
|
|
|
io_handle = h;
|
|
hdr = (mve_hdr *)ioRead(sizeof(mve_hdr) + sizeof(ioHdrRec));
|
|
if (!hdr)
|
|
return false;
|
|
hdr->SwapBytes();
|
|
if (strcmp(hdr->FileType, MVE_FILE_TYPE) != 0 || hdr->id != ~hdr->version + 0x1234 ||
|
|
// The following two checks may eventually be weakened.
|
|
hdr->version != MVE_FILE_VERSION || hdr->HdrSize != sizeof(mve_hdr))
|
|
return false;
|
|
|
|
io_next_hdr = *(ioHdrRec *)(hdr + 1);
|
|
io_next_hdr.SwapBytes();
|
|
return true;
|
|
}
|
|
|
|
void MVE_memIO(void *p, unsigned size) { MemInit(&io_mem_buf, size, p); }
|
|
|
|
static unsigned char *ioRead(unsigned len) {
|
|
unsigned char *buf;
|
|
|
|
buf = (unsigned char *)MemAlloc(&io_mem_buf, len);
|
|
if (!buf)
|
|
return (unsigned char *)NULL;
|
|
|
|
if (!(*io_read)(io_handle, buf, len))
|
|
return (unsigned char *)NULL;
|
|
|
|
return buf;
|
|
}
|
|
|
|
static unsigned char *ioNextRecord(void) {
|
|
unsigned char *buf;
|
|
logLabel("StartRead");
|
|
buf = ioRead(io_next_hdr.len + sizeof(ioHdrRec));
|
|
logLabel("EndRead");
|
|
if (!buf)
|
|
return (unsigned char *)NULL;
|
|
io_next_hdr = *(ioHdrRec *)(buf + io_next_hdr.len);
|
|
io_next_hdr.SwapBytes();
|
|
return buf;
|
|
}
|
|
|
|
static void ioRelease(void) { MemFree(&io_mem_buf); }
|
|
|
|
//----------------------------------------------------------------------
|
|
// Sound Management
|
|
//-----------------------
|
|
|
|
#define SOUND_SUPPORT 1
|
|
|
|
#if SOUND_SUPPORT
|
|
|
|
static ISoundDevice *snd_ds = NULL;
|
|
static ISysSoundBuffer *snd_buffer = NULL;
|
|
static SysSoundCaps snd_buffer_caps;
|
|
static unsigned int snd_write_cursor = 0;
|
|
|
|
enum { snd_queue_max = 60 };
|
|
|
|
static struct _snd_queue {
|
|
unsigned ptr;
|
|
} snd_queue[snd_queue_max];
|
|
static unsigned snd_fill = 0;
|
|
static unsigned snd_empty = 0;
|
|
static int snd_pad = 0;
|
|
static unsigned snd_stereo = 0;
|
|
static unsigned snd_comp16 = 0;
|
|
static unsigned snd_bits16 = 0;
|
|
static long snd_volume = 0;
|
|
static long snd_pan = 0;
|
|
|
|
#endif
|
|
|
|
void MVE_sndInit(ISoundDevice *lpDS) {
|
|
#if SOUND_SUPPORT
|
|
snd_ds = lpDS;
|
|
#endif
|
|
}
|
|
|
|
void MVE_dsbSetVolume(long lVolume) {
|
|
#if SOUND_SUPPORT
|
|
snd_volume = lVolume;
|
|
if (snd_buffer) {
|
|
snd_buffer->SetVolume(snd_volume);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void MVE_dsbSetPan(long lPan) {
|
|
#if SOUND_SUPPORT
|
|
snd_pan = lPan;
|
|
if (snd_buffer) {
|
|
snd_buffer->SetPan(snd_pan);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void sndReset(void) {
|
|
#if SOUND_SUPPORT
|
|
if (snd_buffer) {
|
|
snd_buffer->Stop();
|
|
snd_buffer->Release();
|
|
snd_buffer = NULL;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static bool sndConfigure(unsigned rate, unsigned buflen, unsigned stereo, unsigned frequency, unsigned bits16,
|
|
unsigned comp16) {
|
|
#if SOUND_SUPPORT
|
|
if (!snd_ds)
|
|
return true;
|
|
|
|
syncSync();
|
|
sndReset();
|
|
|
|
snd_stereo = stereo;
|
|
snd_bits16 = bits16;
|
|
snd_comp16 = comp16;
|
|
snd_fill = 0;
|
|
snd_empty = 0;
|
|
|
|
SoundWAVEFormatEx snd_wfx;
|
|
snd_wfx.wFormatTag = SOUND_WAVE_FORMAT_PCM;
|
|
snd_wfx.nChannels = stereo ? 2 : 1;
|
|
snd_wfx.nSamplesPerSec = frequency;
|
|
snd_wfx.nBlockAlign = (stereo ? 2 : 1) * (bits16 ? 2 : 1);
|
|
snd_wfx.nAvgBytesPerSec = frequency * snd_wfx.nBlockAlign;
|
|
snd_wfx.wBitsPerSample = bits16 ? 16 : 8;
|
|
|
|
SysSoundBufferDesc snd_bufferdesc;
|
|
snd_bufferdesc.dwFlags = LNXSND_CAPS_CTRLDEFAULT;
|
|
snd_bufferdesc.dwBufferBytes = (buflen + (buflen >> 1)) & ~3;
|
|
snd_bufferdesc.lpwfxFormat = &snd_wfx;
|
|
|
|
int dsrval = snd_ds->CreateSoundBuffer(&snd_bufferdesc, &snd_buffer);
|
|
if (dsrval != 0)
|
|
return false;
|
|
|
|
snd_buffer->SetVolume(snd_volume);
|
|
snd_buffer->SetPan(snd_pan);
|
|
|
|
snd_write_cursor = 0;
|
|
|
|
dsrval = snd_buffer->GetCaps(&snd_buffer_caps);
|
|
if (dsrval != 0)
|
|
return false;
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
static void sndSync(void) {
|
|
// Better frame dropping using more flexible synchronization
|
|
sync_late = syncWaitLevel(sync_wait_quanta >> 2) > (-sync_wait_quanta >> 1) && !sync_FrameDropped;
|
|
sync_FrameDropped = false;
|
|
|
|
#if SOUND_SUPPORT
|
|
if (!snd_ds || !snd_buffer)
|
|
return;
|
|
|
|
unsigned int dsbstatus;
|
|
unsigned int target;
|
|
unsigned int play_cursor, write_cursor;
|
|
#define set_target(t) \
|
|
(target = (snd_queue[snd_empty].ptr + (t) + snd_buffer_caps.dwBufferBytes) % snd_buffer_caps.dwBufferBytes)
|
|
#define target_pending() \
|
|
((play_cursor <= write_cursor) ? (play_cursor <= target && target < write_cursor) \
|
|
: (play_cursor <= target || target < write_cursor))
|
|
|
|
bool need_resync = false;
|
|
for (;;) {
|
|
int dsrval = snd_buffer->GetStatus(&dsbstatus);
|
|
if (dsrval != 0)
|
|
return;
|
|
|
|
dsrval = snd_buffer->GetCurrentPosition(&play_cursor, &write_cursor);
|
|
if (dsrval != 0)
|
|
return;
|
|
write_cursor = snd_write_cursor;
|
|
|
|
// Don't get too far ahead of sound (target-pad not yet played)
|
|
set_target(-snd_pad);
|
|
if (target_pending() && (dsbstatus & LNXSND_PLAYING)) {
|
|
need_resync = true;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (need_resync) {
|
|
syncReset(sync_wait_quanta + (sync_wait_quanta >> 2));
|
|
}
|
|
|
|
if (!(dsbstatus & LNXSND_PLAYING)) // If currently not playing
|
|
{
|
|
// Don't restart too soon (restart when target hasn't been played)
|
|
set_target(0);
|
|
if (target_pending()) {
|
|
int dsrval = snd_buffer->SetCurrentPosition(target);
|
|
if (dsrval != 0)
|
|
return;
|
|
|
|
dsrval = snd_buffer->Play(LNXSND_LOOPING);
|
|
if (dsrval != 0)
|
|
return;
|
|
|
|
syncReset(sync_wait_quanta);
|
|
}
|
|
}
|
|
// Because DirectSound can consume an unpredictable amount into primary, this won't always be invoked when it should
|
|
else {
|
|
// Don't get too far behind sound (has target+pad already been played?)
|
|
set_target(snd_pad);
|
|
{
|
|
int amt = write_cursor - play_cursor;
|
|
if (amt < 0)
|
|
amt += snd_buffer_caps.dwBufferBytes;
|
|
amt = snd_buffer_caps.dwBufferBytes - amt - 1;
|
|
if (amt > (int)snd_buffer_caps.dwBufferBytes / 2) {
|
|
amt = snd_buffer_caps.dwBufferBytes / 2;
|
|
}
|
|
play_cursor = (play_cursor - amt + snd_buffer_caps.dwBufferBytes) % snd_buffer_caps.dwBufferBytes;
|
|
}
|
|
|
|
if (!target_pending()) {
|
|
snd_buffer->Stop();
|
|
// dsrval = snd_buffer->GetCurrentPosition( &snd_write_cursor, &write_cursor );
|
|
}
|
|
}
|
|
|
|
if (snd_empty != snd_fill) {
|
|
if (snd_empty == snd_queue_max - 1) {
|
|
snd_empty = 0;
|
|
} else {
|
|
++snd_empty;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// For compressed streams, assumes len (which is in bytes) will be in multiples
|
|
// of 2 for mono and 4 for stereo.
|
|
static unsigned sndAddHelper(unsigned char *dst, unsigned char **pSrc, unsigned len, unsigned state, bool init) {
|
|
#if SOUND_SUPPORT
|
|
unsigned char *src;
|
|
src = *pSrc;
|
|
if (!src) {
|
|
memset(dst, (snd_bits16 ? 0 : 0x80), len);
|
|
} else {
|
|
if (snd_comp16) {
|
|
if (!snd_stereo) {
|
|
if (init) {
|
|
state = IntelSwapper(*(unsigned short *)src);
|
|
*(unsigned short *)dst = state;
|
|
src += 2;
|
|
dst += 2;
|
|
len -= 2;
|
|
}
|
|
|
|
state = sndDecompM16((unsigned short *)dst, src, len >> 1, state);
|
|
|
|
src += len >> 1;
|
|
} else {
|
|
if (init) {
|
|
state = IntelSwapper(*(unsigned long *)src);
|
|
*(unsigned long *)dst = state;
|
|
src += 4;
|
|
dst += 4;
|
|
len -= 4;
|
|
}
|
|
state = sndDecompS16((unsigned short *)dst, src, len >> 2, state);
|
|
src += len >> 1;
|
|
}
|
|
} else {
|
|
memcpy(dst, src, len);
|
|
src += len;
|
|
}
|
|
}
|
|
|
|
*pSrc = src;
|
|
return state;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
static void sndAdd(unsigned char *buf, unsigned len) {
|
|
#if SOUND_SUPPORT
|
|
int dsrval;
|
|
unsigned int play_cursor, write_cursor;
|
|
unsigned int len1, len2;
|
|
|
|
unsigned state = 0;
|
|
bool init = true;
|
|
unsigned char *ptr1, *ptr2;
|
|
|
|
snd_pad = len;
|
|
|
|
if (!snd_buffer || snd_fill + 1 == (snd_empty ? snd_empty : snd_queue_max))
|
|
return;
|
|
|
|
dsrval = snd_buffer->GetCurrentPosition(&play_cursor, &write_cursor);
|
|
if (dsrval != 0)
|
|
return;
|
|
write_cursor = snd_write_cursor;
|
|
|
|
dsrval = snd_buffer->Lock(write_cursor, len, (void **)&ptr1, &len1, (void **)&ptr2, &len2,
|
|
0 /*LNXSND_LOCK_FROMWRITECURSOR*/ /*flags*/);
|
|
if (dsrval != 0)
|
|
return;
|
|
|
|
if (len1) {
|
|
state = sndAddHelper(ptr1, &buf, len1, state, init);
|
|
init = false;
|
|
snd_write_cursor += len1;
|
|
}
|
|
|
|
if (len2) {
|
|
sndAddHelper(ptr2, &buf, len2, state, init);
|
|
snd_write_cursor = len2;
|
|
}
|
|
|
|
if (snd_write_cursor == snd_buffer_caps.dwBufferBytes) {
|
|
snd_write_cursor = 0;
|
|
}
|
|
|
|
snd_buffer->Unlock(ptr1, len1, ptr2, len2);
|
|
|
|
snd_queue[snd_fill].ptr = write_cursor;
|
|
if (snd_fill == snd_queue_max - 1) {
|
|
snd_fill = 0;
|
|
} else {
|
|
++snd_fill;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void sndRelease(void) {
|
|
#if SOUND_SUPPORT
|
|
// Nothing to free
|
|
#endif
|
|
}
|
|
|
|
static void sndPause(void) {
|
|
#if SOUND_SUPPORT
|
|
if (snd_buffer) {
|
|
snd_buffer->Stop();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void sndResume(void) {
|
|
// Nothing need be done here to resume sound
|
|
// The next call to sndSync will automatically resume the sound.
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
// NextFrame (Video Decompression)
|
|
//----------------------------------
|
|
|
|
// static removed from most nf_ vars to support mveliba.asm
|
|
|
|
static bool nf_memory_mode = false;
|
|
|
|
// NextFrame working storage
|
|
unsigned char *nf_buf_cur = NULL;
|
|
unsigned char *nf_buf_prv = NULL;
|
|
static MemRec nf_mem_buf1;
|
|
static MemRec nf_mem_buf2;
|
|
|
|
// NextFrame parameters
|
|
unsigned char nf_wqty = 0; // (width/SWIDTH)
|
|
unsigned char nf_hqty = 0; // (height/SHEIGHT)
|
|
unsigned char nf_fqty = 0; // Number of fields
|
|
unsigned nf_hicolor = 0; // HiColor (0:none,1:normal,2:swapped)
|
|
// <derived quantities>
|
|
unsigned nf_width = 0; // wqty * SWIDTH
|
|
unsigned nf_height = 0; // hqty * SHEIGHT;
|
|
unsigned nf_new_line = 0; // width - SWIDTH
|
|
unsigned nf_new_row0 = 0; // SHEIGHT*width*2-width
|
|
unsigned nf_back_right = 0; // (SHEIGHT-1)*width
|
|
|
|
// Frame parameters
|
|
// Portion of current frame which has been updated
|
|
// and needs to be sent to screen.
|
|
//
|
|
unsigned nf_new_x = 0;
|
|
unsigned nf_new_y = 0;
|
|
unsigned nf_new_w = 0;
|
|
unsigned nf_new_h = 0;
|
|
|
|
// Hicolor format translation tables
|
|
unsigned short nf_trans16_lo[256];
|
|
unsigned short nf_trans16_hi[256];
|
|
|
|
void MVE_memVID(void *p1, void *p2, unsigned size) {
|
|
MemInit(&nf_mem_buf1, size, p1);
|
|
MemInit(&nf_mem_buf2, size, p2);
|
|
}
|
|
|
|
void nfPkConfig(void);
|
|
|
|
// ffs()
|
|
// Returns position of most significant bit set (0 to 31).
|
|
// Assumes bits is nonzero.
|
|
static int ffs(unsigned bits) {
|
|
int pos;
|
|
unsigned t;
|
|
t = bits & 0xFFFF0000;
|
|
if (t)
|
|
bits = t, pos = 16;
|
|
else
|
|
pos = 0;
|
|
t = bits & 0xFF00FF00;
|
|
if (t)
|
|
bits = t, pos |= 8;
|
|
t = bits & 0xF0F0F0F0;
|
|
if (t)
|
|
bits = t, pos |= 4;
|
|
t = bits & 0xCCCCCCCC;
|
|
if (t)
|
|
bits = t, pos |= 2;
|
|
if (bits & 0xAAAAAAAA)
|
|
pos |= 1;
|
|
return pos;
|
|
}
|
|
|
|
static bool nfConfig(int wqty, int hqty, int fqty, int hicolor) {
|
|
unsigned size;
|
|
|
|
if (!nf_memory_mode) {
|
|
if (nf_buf_cur) {
|
|
free(nf_buf_cur);
|
|
nf_buf_cur = NULL;
|
|
}
|
|
if (nf_buf_prv) {
|
|
free(nf_buf_prv);
|
|
nf_buf_prv = NULL;
|
|
}
|
|
}
|
|
|
|
nf_wqty = (unsigned char)wqty;
|
|
nf_hqty = (unsigned char)hqty;
|
|
nf_fqty = (unsigned char)fqty;
|
|
nf_width = wqty * SWIDTH;
|
|
nf_height = hqty * fqty * SHEIGHT;
|
|
if (opt_fastmode) {
|
|
nf_height >>= 1;
|
|
}
|
|
|
|
{
|
|
if (hicolor) {
|
|
const unsigned int pal_rmask = 0x001F;
|
|
const unsigned int pal_bmask = 0xF800;
|
|
const unsigned int pal_gmask = 0x07E0;
|
|
|
|
const int pal_rshift = ffs(pal_rmask) - 4;
|
|
const int pal_gshift = ffs(pal_gmask) - 4;
|
|
const int pal_bshift = ffs(pal_bmask) - 4;
|
|
unsigned i, r, g, b;
|
|
for (i = 0, r = 0, g = 0; g < 8; ++g) {
|
|
for (b = 0; b < 32; ++b, ++i) {
|
|
nf_trans16_lo[i] = (((pal_rshift > 0 ? r << pal_rshift : r >> -pal_rshift) & pal_rmask) |
|
|
((pal_gshift > 0 ? g << pal_gshift : g >> -pal_gshift) & pal_gmask) |
|
|
((pal_bshift > 0 ? b << pal_bshift : b >> -pal_bshift) & pal_bmask));
|
|
}
|
|
}
|
|
|
|
for (i = 0, r = 0, b = 0; r < 32; ++r) {
|
|
for (g = 0; g < 32; g += 8, ++i) {
|
|
nf_trans16_hi[i + 128] = nf_trans16_hi[i] =
|
|
(((pal_rshift > 0 ? r << pal_rshift : r >> -pal_rshift) & pal_rmask) |
|
|
((pal_gshift > 0 ? g << pal_gshift : g >> -pal_gshift) & pal_gmask) |
|
|
((pal_bshift > 0 ? b << pal_bshift : b >> -pal_bshift) & pal_bmask));
|
|
}
|
|
}
|
|
}
|
|
int size = nf_width * nf_height << 1;
|
|
|
|
nf_buf_cur = (unsigned char *)malloc(size);
|
|
nf_buf_prv = (unsigned char *)malloc(size);
|
|
}
|
|
|
|
nf_new_line = nf_width * fqty - SWIDTH;
|
|
nf_hicolor = hicolor;
|
|
if (hicolor) {
|
|
nf_width <<= 1;
|
|
nf_new_line <<= 1;
|
|
}
|
|
|
|
nf_new_row0 = fqty * SHEIGHT * nf_width;
|
|
nf_back_right = fqty * (SHEIGHT - 1) * nf_width;
|
|
size = nf_width * nf_height;
|
|
|
|
nfPkConfig();
|
|
|
|
return true;
|
|
}
|
|
|
|
static void nfRelease(void) {
|
|
free(nf_buf_cur);
|
|
free(nf_buf_prv);
|
|
nf_buf_cur = NULL;
|
|
nf_buf_prv = NULL;
|
|
}
|
|
|
|
static void nfAdvance(void) {
|
|
unsigned char *tmp = nf_buf_prv;
|
|
nf_buf_prv = nf_buf_cur;
|
|
nf_buf_cur = tmp;
|
|
}
|
|
|
|
// Decompress into subsection of current buffer specified
|
|
// by x,y,w,h in units of SWIDTHxSHEIGHT (8x8).
|
|
//
|
|
void nfHiColorDecomp(const unsigned char *comp, unsigned x, unsigned y, unsigned w, unsigned h);
|
|
|
|
// Decompress into subsection of current buffer specified
|
|
// by x,y,w,h in units of SWIDTHxSHEIGHT (8x8).
|
|
// Chgs specifies which squares to update.
|
|
// Parms are motion parms for squares to update.
|
|
//
|
|
void nfHiColorDecompChg(const unsigned short *chgs, const unsigned short *parms, const unsigned char *comp, unsigned x,
|
|
unsigned y, unsigned w, unsigned h);
|
|
|
|
// Non-HiColor versions
|
|
|
|
// Decompress into subsection of current buffer specified
|
|
// by x,y,w,h in units of SWIDTHxSHEIGHT (8x8).
|
|
//
|
|
void nfDecomp(const unsigned char *comp, unsigned x, unsigned y, unsigned w, unsigned h);
|
|
|
|
void nfPkDecomp(unsigned char *ops, unsigned char *comp, unsigned x, unsigned y, unsigned w, unsigned h);
|
|
void nfPkDecompH(unsigned char *ops, unsigned char *comp, unsigned x, unsigned y, unsigned w, unsigned h);
|
|
|
|
void nfHPkDecomp(unsigned char *ops, unsigned char *comp, unsigned x, unsigned y, unsigned w, unsigned h);
|
|
|
|
// Decompress into subsection of current buffer specified
|
|
// by x,y,w,h in units of SWIDTHxSHEIGHT (8x8).
|
|
// Chgs specifies which squares to update.
|
|
// Parms are motion parms for squares to update.
|
|
//
|
|
void nfDecompChg(const unsigned short *chgs, const unsigned short *parms, const unsigned char *comp, unsigned x,
|
|
unsigned y, unsigned w, unsigned h);
|
|
|
|
//---------------------------------------------------------------------
|
|
// ShowFrame
|
|
//------------
|
|
|
|
// TODO:
|
|
// Support for software cursor.
|
|
// Issues & Considerations:
|
|
// 1. Current code only deals with writing to screen, not reading it.
|
|
// 2. To prevent flicker, cursor should be drawn into current buffer
|
|
// before it is sent to screen, first saving area overwritten.
|
|
// After current before is sent to screen, the area modified
|
|
// by the cursor in the current buffer should be restored.
|
|
// 3. Screen must also be updated in outside areas if cursor
|
|
// appears there. This requires read/modify/write to screen,
|
|
// because current buffer may not exist or be valid for area
|
|
// of screen. Also, contents of screen must be saved so that
|
|
// if next frame doesn't modify that area of screen, it can be
|
|
// restored when mouse moves away.
|
|
// In other words:
|
|
// (a):
|
|
// Save area on screen where mouse will go.
|
|
// Draw mouse onto screen.
|
|
// When mouse moves, restore previous contents of screen and
|
|
// loop back to (a).
|
|
// When screen is to be redrawn with video, draw mouse clipped
|
|
// into buffer, saving previous contents. Draw video, then restore
|
|
// buffer. Note that saving previous contents saves into same
|
|
// area that was used when area under mouse was originally saved,
|
|
// but it may only be a subrectangle.
|
|
// Question: Should I implement VESA read from screen code?
|
|
// Or work with caller to implement?
|
|
// With caller:
|
|
// Caller provides pointer to mouse image and save area buffers,
|
|
// and screen location. Caller informs us when mouse changes
|
|
// (position or contents). We deal with drawing mouse into internal
|
|
// buffer and updating save area. Caller deals with drawing
|
|
// mouse on screen and saving areas from screen. Color zero will
|
|
// be assumed transparent. If old and new locations are within
|
|
// area we are about to draw, caller need do nothing other than
|
|
// call us (we can return a bool to let him know that). We can
|
|
// call him to draw cursor just before showframe (whereas
|
|
// call back from pausing would be just after showframe).
|
|
// Without special support?
|
|
// Everytime frame is shown, redraw mouse on screen, saving previous
|
|
// contents. If mouse moves, restore previous contents and redraw.
|
|
// Result will be a flickering mouse (mostly on, briefly off).
|
|
|
|
static mve_cb_ShowFrame sf_ShowFrame = NULL;
|
|
|
|
static unsigned sf_ResolutionHeight = 0; // Height of screen
|
|
static unsigned sf_ResolutionWidth = 0; // Width of screen
|
|
unsigned sf_ScreenHeight = 0; // Height of modifiable screen
|
|
unsigned sf_ScreenWidth = 0; // Width of modifiable screen
|
|
// Private, see mveliba.asm :
|
|
unsigned sf_LineWidth = 0; // Distance between lines in memory
|
|
static unsigned sf_hicolor = 0; // Hicolor mode (0:none,1:normal,2:swapped)
|
|
|
|
// Banked screen parameters, Private, see mveliba.asm
|
|
void *sf_SetBank = NULL;
|
|
unsigned sf_WinGran = 0;
|
|
unsigned long sf_WinSize = 0;
|
|
unsigned sf_WinGranPerSize = 0;
|
|
//{sf_WriteWinPtr and sf_WriteWinLimit replace sf_WriteWinSeg, see mveliba.asm}
|
|
unsigned char *sf_WriteWinPtr = NULL;
|
|
unsigned char *sf_WriteWinLimit = NULL;
|
|
unsigned sf_WriteWin = 0;
|
|
|
|
//
|
|
static bool sf_auto = true; // True if mode can be set from movie
|
|
// (mode not specified by caller).
|
|
static int sf_auto_mode = 0; // Current sf_auto mode.
|
|
|
|
static void sfVGA(unsigned w, unsigned h, unsigned resw, unsigned resh);
|
|
static void sfShowFrame(int dx, int dy, unsigned field);
|
|
|
|
void mve_ShowFrameField(unsigned char *buf, unsigned bufw, unsigned bufh, unsigned sx, unsigned sy, unsigned w,
|
|
unsigned h, unsigned dstx, unsigned dsty, unsigned field);
|
|
void mve_ShowFrameField(unsigned char *buf, unsigned bufw, unsigned bufh, unsigned sx, unsigned sy, unsigned w,
|
|
unsigned h, unsigned dstx, unsigned dsty, unsigned field);
|
|
|
|
void mve_ShowFrameFieldHi(unsigned char *buf, unsigned bufw, unsigned bufh, unsigned sx, unsigned sy, unsigned w,
|
|
unsigned h, unsigned dstx, unsigned dsty, unsigned field);
|
|
|
|
// Restrictions/Assumptions:
|
|
// 64K >= WinSize >= WinGran
|
|
// WinSize % WinGran == 0
|
|
// WinGran of 64K is represented by 0
|
|
// SetBank is address of function with following protocol:
|
|
// bh: 0=Set window, 1=Get Window
|
|
// bl: Window number (0 or 1)
|
|
// dx: Window position in video memory in units of WinGran.
|
|
// on return, registers AX and DX are destroyed.
|
|
void MVE_sfSVGA(unsigned w, unsigned h, unsigned LineWidth, unsigned WriteWin, unsigned char *WriteWinPtr,
|
|
unsigned long WinSize, unsigned WinGran, void *SetBank, unsigned hicolor) {
|
|
sf_ScreenWidth = w;
|
|
sf_ScreenHeight = h;
|
|
sf_ResolutionWidth = w;
|
|
sf_ResolutionHeight = h;
|
|
sf_LineWidth = LineWidth;
|
|
if (opt_fastmode & 4) {
|
|
sf_LineWidth <<= 1;
|
|
}
|
|
sf_WriteWin = WriteWin;
|
|
sf_WinSize = WinSize;
|
|
sf_WriteWinPtr = WriteWinPtr;
|
|
sf_WriteWinLimit = sf_WriteWinPtr + sf_WinSize;
|
|
sf_WinGran = WinGran;
|
|
sf_SetBank = SetBank;
|
|
if (WinGran) {
|
|
// Assumes WinGran divides evenly into WinSize.
|
|
sf_WinGranPerSize = (unsigned)WinSize / WinGran;
|
|
} else {
|
|
// Assumes WinSize is also 64K.
|
|
sf_WinGranPerSize = 1;
|
|
}
|
|
sf_auto = false;
|
|
sf_hicolor = hicolor;
|
|
}
|
|
|
|
// QUESTION: Should sfShowFrame also take x,y,w,h as command args?
|
|
// The issue is, will it always be true that area to update on
|
|
// screen matches area updated in memory, since previous contents
|
|
// of memory will be from two frames back, not one!
|
|
// Answer: By having compressor compare previous screen to desired screen,
|
|
// areas that are identical can be located. Only areas that change
|
|
// need to be built in memory. Remaining areas will not be correct,
|
|
// but can still be used for source data for next screen.
|
|
// Therefore, additional x,y,w,h args are not needed.
|
|
// However, should this prove to be wrong, a minor opcode variant
|
|
// can always be added which supplies the additional arguments.
|
|
static void sfShowFrame(int dx, int dy, unsigned field) {
|
|
unsigned scaled_width = nf_width * 4 / opt_hscale_step;
|
|
scaled_width = ((scaled_width - 12) & ~0xf) + 12; // Round down to a multiple of 16 + 12.
|
|
opt_hscale_adj = nf_width - (scaled_width / 4 * opt_hscale_step);
|
|
|
|
logLabel("StartShow");
|
|
if (dx < 0) {
|
|
if (nf_hicolor) {
|
|
dx = (sf_ScreenWidth - (scaled_width >> 1)) >> 1;
|
|
} else {
|
|
dx = (sf_ScreenWidth - scaled_width) >> 1;
|
|
}
|
|
}
|
|
|
|
if (nf_hicolor) {
|
|
dx <<= 1;
|
|
}
|
|
|
|
if (dy < 0) {
|
|
if (opt_fastmode & 4) // HACK
|
|
{
|
|
dy = (sf_ScreenHeight - nf_height * 2) >> 1;
|
|
} else {
|
|
dy = (sf_ScreenHeight - nf_height) >> 1;
|
|
}
|
|
}
|
|
|
|
dx &= ~3; // Force to a multiple of 4 boundary for performance!
|
|
|
|
if (opt_fastmode & 4) // HACK
|
|
{
|
|
dy >>= 1;
|
|
}
|
|
|
|
if (field) {
|
|
mve_ShowFrameField(nf_buf_cur, nf_width, nf_height, nf_new_x, nf_new_y, nf_new_w, nf_new_h, dx, dy, field);
|
|
} else {
|
|
if (opt_hscale_step != 4) {
|
|
sf_ShowFrame(nf_buf_cur, nf_width, nf_height, 0, nf_new_y, scaled_width, nf_new_h, dx, dy, nf_hicolor);
|
|
} else {
|
|
sf_ShowFrame(nf_buf_cur, nf_width, nf_height, nf_new_x, nf_new_y, nf_new_w, nf_new_h, dx, dy, nf_hicolor);
|
|
}
|
|
}
|
|
|
|
logLabel("EndShow");
|
|
}
|
|
|
|
void MVE_sfCallbacks(mve_cb_ShowFrame fn_ShowFrame) { sf_ShowFrame = fn_ShowFrame; }
|
|
|
|
// Restriction: w must be a multiple of 4.
|
|
// Strong Recommendation: sx and dstx should be multiples of 4.
|
|
// dstx & dsty are deltas relative to sx & sy.
|
|
// <WIP> Field is a hack and should be removed. Instead, nfConfig
|
|
// should specify interlace mode and cause linestep to be twice
|
|
// sf_LineWidth. dstx/dsty should be made absolute to allow interlace
|
|
// field to be determined by dst.
|
|
// ON THE OTHER HAND -- All this makes user clipping quite complex...
|
|
// A caller would probably like to deal in ordinary coordinates,
|
|
// but then we still need 'field'.
|
|
// Also note that when field is on, the actual height of the image
|
|
// on the screen is 2*h alternate lines.
|
|
//
|
|
|
|
void mve_sfHiColorShowFrameChg(bool prvbuf, unsigned x, unsigned y, unsigned w, unsigned h, unsigned short *chgs,
|
|
unsigned dstx, unsigned dsty);
|
|
|
|
void mve_sfShowFrameChg(bool prvbuf, unsigned x, unsigned y, unsigned w, unsigned h, unsigned short *chgs,
|
|
unsigned dstx, unsigned dsty);
|
|
|
|
static void sfShowFrameChg(int dx, int dy, unsigned short *chgs) { logLabel("StartShowChg"); }
|
|
|
|
//---------------------------------------------------------------------
|
|
// Palette Management
|
|
//---------------------
|
|
|
|
static void (*pal_SetPalette)(unsigned char *p, unsigned start, unsigned count) = MVE_SetPalette;
|
|
|
|
unsigned char pal_tbl[3 * 256]; // Private, see mveliba.asm
|
|
#if DBL_DBG
|
|
unsigned char pal_tbl_old[3 * 256];
|
|
#endif
|
|
unsigned short pal15_tbl[256]; // Private, see mveliba.asm
|
|
|
|
void MVE_palCallbacks(void (*fn_SetPalette)(unsigned char *p, unsigned start, unsigned count)) {
|
|
pal_SetPalette = fn_SetPalette;
|
|
}
|
|
|
|
void MVE_SetPalette(unsigned char *p, unsigned start, unsigned count);
|
|
|
|
static void palSetPalette(unsigned start, unsigned count) {
|
|
if (!nf_hicolor) {
|
|
(*pal_SetPalette)((unsigned char *)pal_tbl, start, count);
|
|
}
|
|
}
|
|
|
|
static void palClrPalette(unsigned start, unsigned count) {
|
|
static unsigned char clr_pal_tbl[256 * 3];
|
|
if (!nf_hicolor) {
|
|
(*pal_SetPalette)((unsigned char *)clr_pal_tbl, start, count);
|
|
}
|
|
}
|
|
|
|
// Old Synth: 0,21,6
|
|
// 128,32,4
|
|
//
|
|
// New Synth: 129,21,6
|
|
// 17,28,4
|
|
//
|
|
static void palMakeSynthPalette(int base_r, int range_r, int range_rb, int base_g, int range_g, int range_gb) {
|
|
unsigned char(*SynthPal)[3] = (unsigned char(*)[3])pal_tbl;
|
|
int i, j;
|
|
|
|
for (i = 0; i < range_r; ++i) {
|
|
for (j = 0; j < range_rb; ++j) {
|
|
int idx = base_r + i * range_rb + j;
|
|
SynthPal[idx][0] = (char)(i * 63 / (range_r - 1));
|
|
SynthPal[idx][1] = 0;
|
|
SynthPal[idx][2] = (char)((j * 63 / (range_rb - 1)) * 5 / 8);
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < range_g; ++i) {
|
|
for (j = 0; j < range_gb; ++j) {
|
|
int idx = base_g + i * range_gb + j;
|
|
SynthPal[idx][0] = 0;
|
|
SynthPal[idx][1] = (char)(i * 63 / (range_g - 1));
|
|
SynthPal[idx][2] = (char)((j * 63 / (range_gb - 1)) * 5 / 8);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void palLoadPalette(unsigned char *buf, unsigned start, unsigned count) {
|
|
#if DBL_DBG
|
|
memcpy(pal_tbl_old, pal_tbl, 256 * 3);
|
|
#endif
|
|
memcpy(&pal_tbl[start * 3], buf, count * 3);
|
|
}
|
|
|
|
// If at least 11 palette entries aren't changed, this is more compact
|
|
// than uncompressed 256 entry palette.
|
|
//
|
|
void palLoadCompPalette(unsigned char *buf);
|
|
|
|
//-----------------------------------------------------------------------
|
|
// Graphics
|
|
//----------
|
|
|
|
void gfxVres(unsigned char misc, unsigned char *crtc);
|
|
|
|
static void ForceVres2(unsigned char misc, unsigned vt, unsigned height, unsigned vrs /*vpos*/, unsigned _2t4,
|
|
unsigned msl, unsigned bdr_b, unsigned bdr_t) {}
|
|
|
|
void MVE_ForceVres350(void) {}
|
|
|
|
void MVE_ForceVresHalf(void) {}
|
|
|
|
// If auto is set, and this function is being called to change mode
|
|
// for movie, be sure to turn auto back on after call!
|
|
//
|
|
bool MVE_gfxMode(short mode) { return false; }
|
|
|
|
// Restore a text mode
|
|
void MVE_gfxReset(void) {
|
|
sf_auto = true;
|
|
sf_auto_mode = 0;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Movie Interpreter
|
|
//--------------------
|
|
|
|
static int (*rm_ctl)(void);
|
|
|
|
static int rm_dx = 0;
|
|
static int rm_dy = 0;
|
|
static unsigned rm_track_bit = 0;
|
|
static unsigned char *rm_p = NULL;
|
|
static unsigned rm_len = 0;
|
|
static bool rm_hold = false;
|
|
static bool rm_active = false;
|
|
static unsigned rm_FrameCount = 0;
|
|
static unsigned rm_FrameDropCount = 0;
|
|
|
|
void MVE_rmCallbacks(int (*fn_ctl)(void)) { rm_ctl = fn_ctl; }
|
|
|
|
void MVE_rmFastMode(int mode) { opt_fastmode = mode; }
|
|
|
|
void MVE_rmHScale(int hscale) {
|
|
if (hscale == 3)
|
|
opt_hscale_step = 3;
|
|
else
|
|
opt_hscale_step = 4;
|
|
}
|
|
|
|
void MVE_rmFrameCounts(unsigned *FrameCount, unsigned *FrameDropCount) {
|
|
*FrameCount = rm_FrameCount;
|
|
*FrameDropCount = rm_FrameDropCount;
|
|
}
|
|
|
|
int MVE_rmPrepMovie(int hFile, int dx, int dy, unsigned track) {
|
|
rm_dx = dx;
|
|
rm_dy = dy;
|
|
|
|
rm_track_bit = 1 << track;
|
|
if (!rm_track_bit)
|
|
rm_track_bit = 1;
|
|
|
|
if (!ioReset(hFile)) {
|
|
MVE_rmEndMovie();
|
|
return MVE_ERR_BADFMT;
|
|
}
|
|
|
|
rm_p = ioNextRecord();
|
|
rm_len = 0;
|
|
if (!rm_p) {
|
|
MVE_rmEndMovie();
|
|
return MVE_ERR_IO;
|
|
}
|
|
|
|
rm_active = true;
|
|
rm_hold = false;
|
|
rm_FrameCount = 0;
|
|
rm_FrameDropCount = 0;
|
|
return 0;
|
|
}
|
|
|
|
int MVE_rmHoldMovie(void) {
|
|
if (!rm_hold) {
|
|
sndPause();
|
|
rm_hold = true;
|
|
}
|
|
syncWait();
|
|
return 0;
|
|
}
|
|
|
|
int dbgcount = 0;
|
|
int MVE_rmStepMovie(void) {
|
|
unsigned len = rm_len;
|
|
unsigned char *p = rm_p;
|
|
|
|
int result;
|
|
|
|
if (!rm_active)
|
|
return MVE_ERR_PREP;
|
|
|
|
if (rm_hold) {
|
|
sndResume();
|
|
rm_hold = false;
|
|
}
|
|
|
|
for (;; p = ioNextRecord(), len = 0) {
|
|
unsigned short *DecompChg_chgs = (unsigned short *)NULL;
|
|
unsigned short *DecompChg_parms = (unsigned short *)NULL;
|
|
|
|
if (!p) {
|
|
result = MVE_ERR_IO;
|
|
goto done;
|
|
}
|
|
|
|
for (;;) {
|
|
mcmd_hdr hdr;
|
|
p += len; // Advance past data of previous command
|
|
hdr = *(mcmd_hdr *)p;
|
|
hdr.SwapBytes();
|
|
p += sizeof(hdr);
|
|
len = hdr.len;
|
|
/*
|
|
FILE *logf = fopen("mve.log","a");
|
|
if(logf)
|
|
{
|
|
fprintf(logf,"hdr len=%d,major=%d,minor=%d\n",hdr.len,hdr.major,hdr.minor);
|
|
fclose(logf);
|
|
}
|
|
|
|
if(hdr.len==14896 &&
|
|
hdr.major==17 &&
|
|
hdr.minor==3)
|
|
{
|
|
dbgcount++;
|
|
if(dbgcount >= 3)
|
|
{
|
|
printf("***");
|
|
}
|
|
|
|
}
|
|
*/
|
|
switch (hdr.major) {
|
|
default:
|
|
continue;
|
|
|
|
case mcmd_end:
|
|
result = MVE_ERR_EOF;
|
|
goto movie_eof;
|
|
case mcmd_next:
|
|
break;
|
|
case mcmd_syncInit: {
|
|
marg_syncInit *arg = (marg_syncInit *)p;
|
|
arg->SwapBytes();
|
|
if (!syncInit(arg->period, arg->wait_quanta)) {
|
|
result = MVE_ERR_SYNC;
|
|
goto done;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
case mcmd_sndConfigure: {
|
|
marg_sndConfigure *arg = (marg_sndConfigure *)p;
|
|
arg->SwapBytes();
|
|
unsigned comp16 = hdr.minor >= 1 ? arg->comp16 : 0;
|
|
unsigned buflen = arg->buflen;
|
|
if (hdr.minor == 0)
|
|
buflen &= 0xFFFF;
|
|
if (!sndConfigure(arg->rate, buflen, arg->stereo, arg->frequency, arg->bits16, comp16)) {
|
|
result = MVE_ERR_SND;
|
|
goto done;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
case mcmd_sndSync: {
|
|
sndSync();
|
|
continue;
|
|
}
|
|
|
|
case mcmd_nfConfig: {
|
|
marg_nfConfig *arg = (marg_nfConfig *)p;
|
|
arg->SwapBytes();
|
|
unsigned hicolor = hdr.minor >= 2 ? arg->hicolor : 0;
|
|
if (!nfConfig(arg->wqty, arg->hqty, (hdr.minor >= 1 ? arg->fqty : 1), hicolor)) {
|
|
result = MVE_ERR_NF;
|
|
goto done;
|
|
}
|
|
// <WIP> To handle interlace mode, we need the following:
|
|
// A window width/height in addition to nf_width/height.
|
|
// Window width/height should be used here and in centering
|
|
// code in sfShowFrame.
|
|
{
|
|
unsigned scaled_width = nf_width * 4 / opt_hscale_step;
|
|
scaled_width &= ~0xf; // Round down to a multiple of 16.
|
|
if (nf_hicolor)
|
|
scaled_width >>= 1; /*HACK*/
|
|
if (scaled_width + (rm_dx < 0 ? 0 : rm_dx) > sf_ScreenWidth ||
|
|
nf_height + (rm_dy < 0 ? 0 : rm_dy) > sf_ScreenHeight) {
|
|
result = MVE_ERR_GFX_FIT;
|
|
goto done;
|
|
}
|
|
}
|
|
// NOTE: Eventually, change this to allow sf_hicolor when !nf_hicolor and
|
|
// have show function convert from 256-color to hicolor format.
|
|
// HiColor will also need to disable hardware palette changes and
|
|
// maintain 15-bit software palette along with 24-bit palette.
|
|
if (nf_hicolor && !sf_hicolor) {
|
|
result = MVE_ERR_GFX_FIT;
|
|
goto done;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
case mcmd_nfDecomp: {
|
|
marg_nfDecomp *arg = (marg_nfDecomp *)p;
|
|
arg->SwapBytes();
|
|
if (arg->advance) {
|
|
nfAdvance();
|
|
}
|
|
|
|
nfDecomp(MCMD_DATA(arg), arg->x, arg->y, arg->w, arg->h);
|
|
continue;
|
|
}
|
|
|
|
case mcmd_sfShowFrame: {
|
|
unsigned field = 0;
|
|
marg_sfShowFrame *arg = (marg_sfShowFrame *)p;
|
|
arg->SwapBytes();
|
|
|
|
++rm_FrameCount;
|
|
|
|
if (hdr.minor >= 1) {
|
|
field = arg->field;
|
|
}
|
|
|
|
if (arg->pal_count && !DecompChg_chgs && !sf_hicolor) {
|
|
palClrPalette(arg->pal_start, arg->pal_count);
|
|
} else {
|
|
palSetPalette(arg->pal_start, arg->pal_count);
|
|
}
|
|
|
|
if (DecompChg_chgs) {
|
|
sfShowFrameChg(rm_dx, rm_dy, DecompChg_chgs);
|
|
} else {
|
|
if (sync_late && arg->pal_count == 0) {
|
|
sync_FrameDropped = true;
|
|
++rm_FrameDropCount;
|
|
} else {
|
|
sfShowFrame(rm_dx, rm_dy, field);
|
|
}
|
|
}
|
|
|
|
if (arg->pal_count && !DecompChg_chgs && !sf_hicolor) {
|
|
palSetPalette(arg->pal_start, arg->pal_count);
|
|
}
|
|
|
|
goto FrameDone;
|
|
}
|
|
|
|
case mcmd_sndAdd:
|
|
case mcmd_sndSilence: {
|
|
marg_sndAdd *arg = (marg_sndAdd *)p;
|
|
arg->SwapBytes();
|
|
if (arg->TrackMask & rm_track_bit)
|
|
sndAdd((hdr.major == mcmd_sndAdd ? MCMD_DATA(arg) : (unsigned char *)NULL), arg->qty);
|
|
continue;
|
|
}
|
|
|
|
case mcmd_gfxMode: {
|
|
marg_gfxMode *arg = (marg_gfxMode *)p;
|
|
arg->SwapBytes();
|
|
if (sf_auto) {
|
|
short mode = arg->mode;
|
|
if (opt_fastmode && (opt_fastmode & 4) == 0)
|
|
mode |= 0x8000;
|
|
if (sf_auto_mode != mode)
|
|
if (!MVE_gfxMode(mode)) {
|
|
result = MVE_ERR_GFX_FAIL;
|
|
goto done;
|
|
}
|
|
sf_auto = true;
|
|
sf_auto_mode = mode;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
case mcmd_palMakeSynthPalette: {
|
|
marg_palMakeSynthPalette *arg = (marg_palMakeSynthPalette *)p;
|
|
arg->SwapBytes();
|
|
palMakeSynthPalette(arg->base_r, arg->range_r, arg->range_rb, arg->base_g, arg->range_g, arg->range_gb);
|
|
continue;
|
|
}
|
|
|
|
case mcmd_palLoadPalette: {
|
|
marg_palLoadPalette *arg = (marg_palLoadPalette *)p;
|
|
arg->SwapBytes();
|
|
palLoadPalette(MCMD_DATA(arg), arg->start, arg->count);
|
|
continue;
|
|
}
|
|
|
|
case mcmd_palLoadCompPalette: {
|
|
palLoadCompPalette(p);
|
|
continue;
|
|
}
|
|
|
|
case mcmd_nfChanges: {
|
|
DecompChg_chgs = (unsigned short *)p;
|
|
continue;
|
|
}
|
|
case mcmd_nfParms: {
|
|
DecompChg_parms = (unsigned short *)p;
|
|
continue;
|
|
}
|
|
case mcmd_nfDecompChg: {
|
|
marg_nfDecomp *arg = (marg_nfDecomp *)p;
|
|
arg->SwapBytes();
|
|
if (arg->advance) {
|
|
nfAdvance();
|
|
}
|
|
|
|
nfDecompChg(DecompChg_chgs, DecompChg_parms, MCMD_DATA(arg), arg->x, arg->y, arg->w, arg->h);
|
|
continue;
|
|
}
|
|
case mcmd_nfPkDecomp: {
|
|
marg_nfDecomp *arg = (marg_nfDecomp *)p;
|
|
arg->SwapBytes();
|
|
if (hdr.minor < 3) {
|
|
result = MVE_ERR_BADFMT;
|
|
goto done;
|
|
}
|
|
|
|
if (arg->advance) {
|
|
nfAdvance();
|
|
}
|
|
|
|
if (nf_hicolor) {
|
|
if (opt_fastmode) {
|
|
result = MVE_ERR_BADFMT;
|
|
goto done;
|
|
}
|
|
|
|
nfHPkDecomp((unsigned char *)DecompChg_parms, MCMD_DATA(arg), arg->x, arg->y, arg->w, arg->h);
|
|
} else if ((opt_fastmode & 3) == 1) {
|
|
// Half mode (half height, even lines only)
|
|
nfPkDecompH((unsigned char *)DecompChg_parms, MCMD_DATA(arg), arg->x, arg->y, arg->w, arg->h);
|
|
} else if ((opt_fastmode & 3) == 2) {
|
|
// Support for dithered mode disabled...
|
|
// so just use half mode instead
|
|
nfPkDecompH((unsigned char *)DecompChg_parms, MCMD_DATA(arg), arg->x, arg->y, arg->w, arg->h);
|
|
} else // opt_fastmode==0
|
|
{
|
|
// Normal mode
|
|
nfPkDecomp((unsigned char *)DecompChg_parms, MCMD_DATA(arg), arg->x, arg->y, arg->w, arg->h);
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
done: // Error
|
|
MVE_rmEndMovie();
|
|
|
|
movie_eof: // End of movie
|
|
return result;
|
|
|
|
FrameDone: // Done with frame, return to caller for between frame processing
|
|
rm_p = p;
|
|
rm_len = len;
|
|
return 0;
|
|
}
|
|
|
|
void MVE_rmEndMovie(void) {
|
|
if (rm_active) {
|
|
syncWait();
|
|
syncRelease();
|
|
sndReset();
|
|
rm_active = false;
|
|
}
|
|
}
|
|
|
|
int MVE_RunMovie(int hFile, int dx, int dy, unsigned track) {
|
|
int result;
|
|
result = MVE_rmPrepMovie(hFile, dx, dy, track);
|
|
while (!result) {
|
|
result = MVE_rmStepMovie();
|
|
logLabel("StartUser");
|
|
while (!result) {
|
|
result = (*rm_ctl)();
|
|
if (result != MVE_CTL_HOLD)
|
|
break;
|
|
result = MVE_rmHoldMovie();
|
|
}
|
|
logLabel("EndUser");
|
|
}
|
|
MVE_rmEndMovie();
|
|
return (result == MVE_ERR_EOF) ? 0 : result;
|
|
}
|
|
|
|
int MVE_RunMovieContinue(int hFile, int dx, int dy, unsigned track) {
|
|
int result;
|
|
result = MVE_rmPrepMovie(hFile, dx, dy, track);
|
|
while (!result) {
|
|
result = MVE_rmStepMovie();
|
|
logLabel("StartUser");
|
|
while (!result) {
|
|
result = (*rm_ctl)();
|
|
if (result != MVE_CTL_HOLD)
|
|
break;
|
|
result = MVE_rmHoldMovie();
|
|
}
|
|
logLabel("EndUser");
|
|
}
|
|
// Continue version doesn't call MVE_rmEndMovie;
|
|
return (result == MVE_ERR_EOF) ? 0 : result;
|
|
}
|
|
|
|
void MVE_ReleaseMem(void) {
|
|
MVE_rmEndMovie();
|
|
ioRelease();
|
|
sndRelease();
|
|
nfRelease();
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
char *MVE_strerror(int code) {
|
|
char *errors[] = {"Movie aborted with special code",
|
|
"Movie aborted",
|
|
"Movie completed normally", // 0
|
|
"Movie completed normally", //-1
|
|
"File I/O error or Unable to allocate I/O buffers",
|
|
"Unable to create timer",
|
|
"Unable to allocate sound buffers",
|
|
"Unable to allocate video buffers",
|
|
"Insufficient screen resolution for movie",
|
|
"Unable to setup graphics mode used by movie",
|
|
"Invalid movie file",
|
|
"Incorrect screen color mode",
|
|
"StepMovie() without PrepMovie()",
|
|
"Unable to initialize Linux Draw System",
|
|
"Unable to lock window surface",
|
|
"Unknown movie error code"};
|
|
|
|
if (code >= MVE_CTL_EXIT + 1)
|
|
code = MVE_CTL_EXIT + 1;
|
|
if (code <= MVE_ERR_LAST - 1)
|
|
code = MVE_ERR_LAST - 1;
|
|
return errors[MVE_CTL_EXIT + 1 - code];
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Frame Reader
|
|
//--------------
|
|
|
|
typedef struct _MVE_frstream {
|
|
int (*callback)(unsigned op, unsigned subop, void *buf);
|
|
|
|
// I/O Stream state
|
|
unsigned (*io_read)(int handle, void *buf, unsigned count);
|
|
MemRec io_mem_buf;
|
|
int io_handle;
|
|
ioHdrRec io_next_hdr;
|
|
unsigned char *p;
|
|
unsigned len;
|
|
|
|
// NextFrame working storage
|
|
bool nf_memory_mode;
|
|
|
|
MemRec nf_mem_buf1;
|
|
MemRec nf_mem_buf2;
|
|
unsigned char *nf_buf_cur;
|
|
unsigned char *nf_buf_prv;
|
|
// NextFrame parameters
|
|
unsigned char nf_wqty; // (width/SWIDTH)
|
|
unsigned char nf_hqty; // (height/SHEIGHT)
|
|
unsigned char nf_fqty; // Number of fields
|
|
unsigned nf_hicolor; // HiColor (0:none,1:normal,2:swapped)
|
|
// NextFrame derived quantities
|
|
unsigned nf_width; // wqty * SWIDTH
|
|
unsigned nf_height; // hqty * SHEIGHT;
|
|
unsigned nf_new_line; // width - SWIDTH
|
|
unsigned nf_new_row0; // SHEIGHT*width*2-width
|
|
unsigned nf_back_right; // (SHEIGHT-1)*width
|
|
// Palette
|
|
unsigned char pal_tbl[3 * 256];
|
|
unsigned pal_start, pal_count;
|
|
|
|
} MVE_frStreamRec;
|
|
|
|
static void frLoad(MVE_frStream frs) {
|
|
io_read = frs->io_read;
|
|
io_mem_buf = frs->io_mem_buf;
|
|
io_handle = frs->io_handle;
|
|
io_next_hdr = frs->io_next_hdr;
|
|
|
|
nf_memory_mode = frs->nf_memory_mode;
|
|
nf_mem_buf1 = frs->nf_mem_buf1;
|
|
nf_mem_buf2 = frs->nf_mem_buf2;
|
|
nf_buf_cur = frs->nf_buf_cur;
|
|
nf_buf_prv = frs->nf_buf_prv;
|
|
nf_wqty = frs->nf_wqty;
|
|
nf_hqty = frs->nf_hqty;
|
|
nf_fqty = frs->nf_fqty;
|
|
nf_hicolor = frs->nf_hicolor;
|
|
nf_width = frs->nf_width;
|
|
nf_height = frs->nf_height;
|
|
nf_new_line = frs->nf_new_line;
|
|
nf_new_row0 = frs->nf_new_row0;
|
|
nf_back_right = frs->nf_back_right;
|
|
}
|
|
|
|
static void frSave(MVE_frStream frs) {
|
|
frs->io_read = io_read;
|
|
frs->io_mem_buf = io_mem_buf;
|
|
frs->io_handle = io_handle;
|
|
frs->io_next_hdr = io_next_hdr;
|
|
|
|
frs->nf_memory_mode = nf_memory_mode;
|
|
frs->nf_mem_buf1 = nf_mem_buf1;
|
|
frs->nf_mem_buf2 = nf_mem_buf2;
|
|
frs->nf_buf_cur = nf_buf_cur;
|
|
frs->nf_buf_prv = nf_buf_prv;
|
|
frs->nf_wqty = nf_wqty;
|
|
frs->nf_hqty = nf_hqty;
|
|
frs->nf_fqty = nf_fqty;
|
|
frs->nf_hicolor = nf_hicolor;
|
|
frs->nf_width = nf_width;
|
|
frs->nf_height = nf_height;
|
|
frs->nf_new_line = nf_new_line;
|
|
frs->nf_new_row0 = nf_new_row0;
|
|
frs->nf_back_right = nf_back_right;
|
|
}
|
|
|
|
MVE_frStream MVE_frOpen(unsigned (*fn_read)(int handle, void *buf, unsigned count), int handle,
|
|
int (*fr_callback)(unsigned, unsigned, void *buf)) {
|
|
MVE_frStream frs;
|
|
MVE_frStreamRec save;
|
|
bool failed = false;
|
|
|
|
if (!mem_alloc)
|
|
return (MVE_frStream)NULL;
|
|
|
|
frs = (struct _MVE_frstream *)(*mem_alloc)(sizeof(*frs));
|
|
if (!frs)
|
|
return frs;
|
|
memset(frs, 0, sizeof(*frs));
|
|
|
|
frSave(&save);
|
|
frLoad(frs);
|
|
|
|
MVE_ioCallbacks(fn_read);
|
|
failed = !ioReset(handle);
|
|
frs->callback = fr_callback;
|
|
|
|
if (!failed) {
|
|
frs->p = ioNextRecord();
|
|
frs->len = 0;
|
|
}
|
|
|
|
frSave(frs);
|
|
frLoad(&save);
|
|
|
|
if (failed) {
|
|
MVE_frClose(frs);
|
|
return (MVE_frStream)NULL;
|
|
}
|
|
|
|
return frs;
|
|
}
|
|
|
|
int MVE_frGet(MVE_frStream frs, unsigned char **pBuf, unsigned int *width, unsigned int *height,
|
|
unsigned int *hicolor) {
|
|
MVE_frStreamRec save;
|
|
unsigned char *p;
|
|
unsigned len;
|
|
int result = 0;
|
|
|
|
frSave(&save);
|
|
frLoad(frs);
|
|
p = frs->p;
|
|
len = frs->len;
|
|
|
|
for (;; p = ioNextRecord(), len = 0) {
|
|
unsigned short *DecompChg_parms = (unsigned short *)NULL;
|
|
|
|
if (!p) {
|
|
result = MVE_ERR_IO;
|
|
goto done;
|
|
}
|
|
for (;;) {
|
|
mcmd_hdr hdr;
|
|
p += len; // Advance past data of previous command
|
|
hdr = *(mcmd_hdr *)p;
|
|
hdr.SwapBytes();
|
|
p += sizeof(hdr);
|
|
len = hdr.len;
|
|
switch (hdr.major) {
|
|
default:
|
|
if (frs->callback) {
|
|
result = (*frs->callback)(hdr.major, hdr.minor, p);
|
|
if (result)
|
|
goto done;
|
|
}
|
|
continue;
|
|
|
|
case mcmd_end:
|
|
result = MVE_ERR_EOF;
|
|
goto done;
|
|
|
|
case mcmd_next:
|
|
break;
|
|
|
|
case mcmd_nfConfig: {
|
|
marg_nfConfig *arg = (marg_nfConfig *)p;
|
|
arg->SwapBytes();
|
|
unsigned hicolor = hdr.minor >= 2 ? arg->hicolor : 0;
|
|
unsigned opt_fastmode_save = opt_fastmode;
|
|
opt_fastmode = 0;
|
|
if (hicolor || (hdr.minor >= 1 && arg->fqty != 1) ||
|
|
!nfConfig(arg->wqty, arg->hqty, (hdr.minor >= 1) ? arg->fqty : 1, hicolor)) {
|
|
opt_fastmode = opt_fastmode_save;
|
|
result = MVE_ERR_NF;
|
|
goto done;
|
|
}
|
|
opt_fastmode = opt_fastmode_save;
|
|
continue;
|
|
}
|
|
|
|
case mcmd_sfShowFrame: {
|
|
unsigned field = 0;
|
|
marg_sfShowFrame *arg = (marg_sfShowFrame *)p;
|
|
arg->SwapBytes();
|
|
if (hdr.minor >= 1)
|
|
field = arg->field;
|
|
if (field) {
|
|
result = MVE_ERR_BADFMT;
|
|
goto done;
|
|
}
|
|
*pBuf = nf_buf_cur;
|
|
*width = nf_width;
|
|
*height = nf_height;
|
|
*hicolor = nf_hicolor;
|
|
frs->pal_start = arg->pal_start;
|
|
frs->pal_count = arg->pal_count;
|
|
goto done;
|
|
}
|
|
|
|
case mcmd_nfParms: {
|
|
DecompChg_parms = (unsigned short *)p;
|
|
continue;
|
|
}
|
|
|
|
case mcmd_nfPkDecomp: {
|
|
marg_nfDecomp *arg = (marg_nfDecomp *)p;
|
|
arg->SwapBytes();
|
|
if (hdr.minor < 3) {
|
|
result = MVE_ERR_BADFMT;
|
|
goto done;
|
|
}
|
|
if (arg->advance) {
|
|
nfAdvance();
|
|
}
|
|
if (nf_hicolor) {
|
|
result = MVE_ERR_BADFMT;
|
|
goto done;
|
|
}
|
|
nfPkConfig();
|
|
nfPkDecomp((unsigned char *)DecompChg_parms, MCMD_DATA(arg), arg->x, arg->y, arg->w, arg->h);
|
|
continue;
|
|
}
|
|
case mcmd_palLoadPalette: {
|
|
marg_palLoadPalette *arg = (marg_palLoadPalette *)p;
|
|
arg->SwapBytes();
|
|
memcpy(&frs->pal_tbl[arg->start * 3], MCMD_DATA(arg), arg->count * 3);
|
|
continue;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
done:
|
|
frSave(frs);
|
|
frs->p = p;
|
|
frs->len = len;
|
|
frLoad(&save);
|
|
nfPkConfig();
|
|
if (result)
|
|
frs->pal_start = frs->pal_count = 0;
|
|
return result;
|
|
}
|
|
|
|
void MVE_frPal(MVE_frStream frs, unsigned char **p, unsigned *start, unsigned *count) {
|
|
*p = frs->pal_tbl;
|
|
*start = frs->pal_start;
|
|
*count = frs->pal_count;
|
|
}
|
|
|
|
void MVE_frClose(MVE_frStream frs) {
|
|
MVE_frStreamRec save;
|
|
|
|
frSave(&save);
|
|
frLoad(frs);
|
|
|
|
ioRelease();
|
|
nfRelease();
|
|
|
|
frLoad(&save);
|
|
if (mem_free) {
|
|
(*mem_free)(frs);
|
|
}
|
|
}
|