mirror of
https://github.com/ZDoom/Raze.git
synced 2024-11-15 08:51:24 +00:00
- adapted the MVE player.
Video appears to work, but has quite severe artifacting I cannot tell if it's the video or the decoder which causes it.
This commit is contained in:
parent
f046c5eb6b
commit
928a16983d
8 changed files with 409 additions and 400 deletions
|
@ -780,6 +780,7 @@ set (PCH_SOURCES
|
||||||
build/src/timer.cpp
|
build/src/timer.cpp
|
||||||
build/src/voxmodel.cpp
|
build/src/voxmodel.cpp
|
||||||
|
|
||||||
|
core/movie/playmve.cpp
|
||||||
core/cheathandler.cpp
|
core/cheathandler.cpp
|
||||||
core/mathutil.cpp
|
core/mathutil.cpp
|
||||||
core/rts.cpp
|
core/rts.cpp
|
||||||
|
|
|
@ -95,6 +95,23 @@ void S_SetMusicCallbacks(MusicCallbacks* cb)
|
||||||
|
|
||||||
static std::unique_ptr<SoundStream> musicStream;
|
static std::unique_ptr<SoundStream> musicStream;
|
||||||
|
|
||||||
|
SoundStream *S_CreateCustomStream(size_t size, int samplerate, int numchannels, StreamCallback cb, void *userdata)
|
||||||
|
{
|
||||||
|
int flags = SoundStream::Float;
|
||||||
|
if (numchannels < 2) flags |= SoundStream::Mono;
|
||||||
|
auto stream = GSnd->CreateStream(cb, size, flags, samplerate, userdata);
|
||||||
|
if (stream) stream->Play(true, 1);
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
void S_StopCustomStream(SoundStream *stream)
|
||||||
|
{
|
||||||
|
stream->Stop();
|
||||||
|
delete stream;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static bool FillStream(SoundStream* stream, void* buff, int len, void* userdata)
|
static bool FillStream(SoundStream* stream, void* buff, int len, void* userdata)
|
||||||
{
|
{
|
||||||
bool written = ZMusic_FillStream(mus_playing.handle, buff, len);
|
bool written = ZMusic_FillStream(mus_playing.handle, buff, len);
|
||||||
|
@ -123,6 +140,7 @@ void S_CreateStream()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void S_PauseStream(bool paused)
|
void S_PauseStream(bool paused)
|
||||||
{
|
{
|
||||||
if (musicStream) musicStream->SetPaused(paused);
|
if (musicStream) musicStream->SetPaused(paused);
|
||||||
|
|
|
@ -8,6 +8,12 @@
|
||||||
#include <zmusic.h>
|
#include <zmusic.h>
|
||||||
|
|
||||||
class FileReader;
|
class FileReader;
|
||||||
|
class SoundStream;
|
||||||
|
|
||||||
|
|
||||||
|
typedef bool(*StreamCallback)(SoundStream* stream, void* buff, int len, void* userdata);
|
||||||
|
SoundStream *S_CreateCustomStream(size_t size, int samplerate, int numchannels, StreamCallback cb, void *userdata);
|
||||||
|
void S_StopCustomStream(SoundStream* stream);
|
||||||
|
|
||||||
struct MusicCallbacks
|
struct MusicCallbacks
|
||||||
{
|
{
|
||||||
|
|
|
@ -51,8 +51,8 @@ void AnimTexture::SetFrameSize(int format, int width, int height)
|
||||||
|
|
||||||
void AnimTexture::SetFrame(const uint8_t* palette, const void* data_)
|
void AnimTexture::SetFrame(const uint8_t* palette, const void* data_)
|
||||||
{
|
{
|
||||||
memcpy(Palette, palette, 768);
|
if (palette) memcpy(Palette, palette, 768);
|
||||||
memcpy(Image.Data(), data_, Width * Height * (pixelformat == Paletted ? 1 : 3));
|
if (data_) memcpy(Image.Data(), data_, Width * Height * (pixelformat == Paletted ? 1 : 3));
|
||||||
CleanHardwareTextures();
|
CleanHardwareTextures();
|
||||||
pixelformat = Paletted;
|
pixelformat = Paletted;
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,10 +43,12 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include "playmve.h"
|
#include "playmve.h"
|
||||||
#include "filestream.h"
|
#include "printf.h"
|
||||||
|
#include "v_draw.h"
|
||||||
|
#include "s_music.h"
|
||||||
|
|
||||||
#define kAudioBlocks 20 // alloc a lot of blocks - need to store lots of audio data before video frames start.
|
|
||||||
|
|
||||||
|
|
||||||
static const int16_t delta_table[] = {
|
static const int16_t delta_table[] = {
|
||||||
|
@ -84,93 +86,11 @@ static const int16_t delta_table[] = {
|
||||||
-8, -7, -6, -5, -4, -3, -2, -1
|
-8, -7, -6, -5, -4, -3, -2, -1
|
||||||
};
|
};
|
||||||
|
|
||||||
class InterplayDecoder
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
InterplayDecoder();
|
|
||||||
~InterplayDecoder();
|
|
||||||
|
|
||||||
bool Open(const char* fileName);
|
|
||||||
void Close();
|
|
||||||
|
|
||||||
struct AudioBlock
|
|
||||||
{
|
|
||||||
int16_t buf[6000];
|
|
||||||
uint32_t size;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct AudioData
|
|
||||||
{
|
|
||||||
int hFx;
|
|
||||||
int nChannels;
|
|
||||||
uint16_t nSampleRate;
|
|
||||||
uint8_t nBitDepth;
|
|
||||||
|
|
||||||
AudioBlock block[kAudioBlocks];
|
|
||||||
int nWrite;
|
|
||||||
int nRead;
|
|
||||||
};
|
|
||||||
|
|
||||||
AudioData audio;
|
|
||||||
mutex_t mutex;
|
|
||||||
|
|
||||||
private:
|
|
||||||
struct DecodeMap
|
|
||||||
{
|
|
||||||
uint8_t* pData;
|
|
||||||
uint32_t nSize;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Palette
|
|
||||||
{
|
|
||||||
uint8_t r;
|
|
||||||
uint8_t g;
|
|
||||||
uint8_t b;
|
|
||||||
};
|
|
||||||
|
|
||||||
bool Run();
|
|
||||||
uint8_t* GetCurrentFrame();
|
|
||||||
uint8_t* GetPreviousFrame();
|
|
||||||
void SwapFrames();
|
|
||||||
void CopyBlock(uint8_t* pDest, uint8_t* pSrc);
|
|
||||||
void DecodeBlock0(int32_t offset);
|
|
||||||
void DecodeBlock1(int32_t offset);
|
|
||||||
void DecodeBlock2(int32_t offset);
|
|
||||||
void DecodeBlock3(int32_t offset);
|
|
||||||
void DecodeBlock4(int32_t offset);
|
|
||||||
void DecodeBlock5(int32_t offset);
|
|
||||||
void DecodeBlock7(int32_t offset);
|
|
||||||
void DecodeBlock8(int32_t offset);
|
|
||||||
void DecodeBlock9(int32_t offset);
|
|
||||||
void DecodeBlock10(int32_t offset);
|
|
||||||
void DecodeBlock11(int32_t offset);
|
|
||||||
void DecodeBlock12(int32_t offset);
|
|
||||||
void DecodeBlock13(int32_t offset);
|
|
||||||
void DecodeBlock14(int32_t offset);
|
|
||||||
void DecodeBlock15(int32_t offset);
|
|
||||||
|
|
||||||
RedNukem::FileStream file;
|
|
||||||
|
|
||||||
bool bIsPlaying, bAudioStarted;
|
|
||||||
|
|
||||||
uint32_t nTimerRate, nTimerDiv;
|
|
||||||
uint32_t nWidth, nHeight, nFrame;
|
|
||||||
double nFps;
|
|
||||||
uint32_t nFrameDuration;
|
|
||||||
|
|
||||||
uint8_t* pVideoBuffers[2];
|
|
||||||
uint32_t nCurrentVideoBuffer, nPreviousVideoBuffer;
|
|
||||||
int32_t videoStride;
|
|
||||||
|
|
||||||
DecodeMap decodeMap;
|
|
||||||
|
|
||||||
Palette palette[256];
|
|
||||||
};
|
|
||||||
|
|
||||||
// macro to fetch 16-bit little-endian words from a bytestream
|
// macro to fetch 16-bit little-endian words from a bytestream
|
||||||
#define LE_16(x) ((*x) | ((*(x+1)) << 8))
|
#define LE_16(x) ((*x) | ((*(x+1)) << 8))
|
||||||
|
|
||||||
int ClipRange(int val, int min, int max)
|
static int ClipRange(int val, int min, int max)
|
||||||
{
|
{
|
||||||
if (val < min)
|
if (val < min)
|
||||||
return min;
|
return min;
|
||||||
|
@ -180,22 +100,27 @@ int ClipRange(int val, int min, int max)
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServeAudioSample(const char** ptr, uint32_t* length, void *userdata)
|
static bool StreamCallbackFunc(SoundStream* stream, void* buff, int len, void* userdata)
|
||||||
{
|
{
|
||||||
InterplayDecoder* pId = (InterplayDecoder*)userdata;
|
InterplayDecoder* pId = (InterplayDecoder*)userdata;
|
||||||
|
uint8_t* buf = (uint8_t*)buff;
|
||||||
mutex_lock(&pId->mutex);
|
while (len > 0)
|
||||||
|
{
|
||||||
uint32_t nSize = pId->audio.block[pId->audio.nRead].size;
|
uint32_t nSize = pId->audio.block[pId->audio.nRead].size;
|
||||||
|
auto ptr = (uint8_t*)pId->audio.block[pId->audio.nRead].buf;
|
||||||
*ptr = (char*)pId->audio.block[pId->audio.nRead].buf;
|
auto copyofs = pId->audio.nSubIndex;
|
||||||
*length = nSize / 2;
|
auto copylen = std::min<unsigned>(len, nSize - copyofs);
|
||||||
|
memcpy(buf, ptr, copylen);
|
||||||
pId->audio.nRead++;
|
pId->audio.nSubIndex += copylen;
|
||||||
if (pId->audio.nRead >= kAudioBlocks)
|
buf += copylen;
|
||||||
pId->audio.nRead = 0;
|
len -= copylen;
|
||||||
|
if (pId->audio.nSubIndex == nSize)
|
||||||
mutex_unlock(&pId->mutex);
|
{
|
||||||
|
pId->audio.nSubIndex = 0;
|
||||||
|
pId->audio.nRead++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
InterplayDecoder::InterplayDecoder()
|
InterplayDecoder::InterplayDecoder()
|
||||||
|
@ -203,7 +128,6 @@ InterplayDecoder::InterplayDecoder()
|
||||||
bIsPlaying = false;
|
bIsPlaying = false;
|
||||||
bAudioStarted = false;
|
bAudioStarted = false;
|
||||||
|
|
||||||
mutex = 0;
|
|
||||||
nWidth = 0;
|
nWidth = 0;
|
||||||
nHeight = 0;
|
nHeight = 0;
|
||||||
nFrame = 0;
|
nFrame = 0;
|
||||||
|
@ -226,6 +150,7 @@ InterplayDecoder::InterplayDecoder()
|
||||||
audio.nRead = 0;
|
audio.nRead = 0;
|
||||||
audio.nWrite = 0;
|
audio.nWrite = 0;
|
||||||
audio.hFx = 0;
|
audio.hFx = 0;
|
||||||
|
audio.nSubIndex = 0;
|
||||||
|
|
||||||
pVideoBuffers[0] = nullptr;
|
pVideoBuffers[0] = nullptr;
|
||||||
pVideoBuffers[1] = nullptr;
|
pVideoBuffers[1] = nullptr;
|
||||||
|
@ -237,35 +162,11 @@ InterplayDecoder::InterplayDecoder()
|
||||||
nPreviousVideoBuffer = 1;
|
nPreviousVideoBuffer = 1;
|
||||||
|
|
||||||
videoStride = 0;
|
videoStride = 0;
|
||||||
|
|
||||||
mutex_init(&mutex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
InterplayDecoder::~InterplayDecoder()
|
InterplayDecoder::~InterplayDecoder()
|
||||||
{
|
{
|
||||||
file.Close();
|
Close();
|
||||||
mutex_destroy(&mutex);
|
|
||||||
|
|
||||||
if (decodeMap.pData) {
|
|
||||||
delete[] decodeMap.pData;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pVideoBuffers[0]) {
|
|
||||||
delete[] pVideoBuffers[0];
|
|
||||||
}
|
|
||||||
if (pVideoBuffers[1]) {
|
|
||||||
delete[] pVideoBuffers[1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool playmve(const char* filename)
|
|
||||||
{
|
|
||||||
InterplayDecoder *id = new InterplayDecoder;
|
|
||||||
id->Open(filename);
|
|
||||||
|
|
||||||
delete id;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void InterplayDecoder::SwapFrames()
|
void InterplayDecoder::SwapFrames()
|
||||||
|
@ -277,42 +178,51 @@ void InterplayDecoder::SwapFrames()
|
||||||
|
|
||||||
void InterplayDecoder::Close()
|
void InterplayDecoder::Close()
|
||||||
{
|
{
|
||||||
|
fr.Close();
|
||||||
bIsPlaying = false;
|
bIsPlaying = false;
|
||||||
|
if (stream)
|
||||||
|
S_StopCustomStream(stream);
|
||||||
|
stream = nullptr;
|
||||||
|
|
||||||
if (audio.hFx > 0) {
|
if (decodeMap.pData) {
|
||||||
FX_StopSound(audio.hFx);
|
delete[] decodeMap.pData;
|
||||||
|
decodeMap.pData = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pVideoBuffers[0]) {
|
||||||
|
delete[] pVideoBuffers[0];
|
||||||
|
pVideoBuffers[0] = nullptr;
|
||||||
|
}
|
||||||
|
if (pVideoBuffers[1]) {
|
||||||
|
delete[] pVideoBuffers[1];
|
||||||
|
pVideoBuffers[1] = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InterplayDecoder::Open(const char* fileName)
|
bool InterplayDecoder::Open(FileReader &fr_)
|
||||||
{
|
{
|
||||||
// open the file (read only)
|
// open the file (read only)
|
||||||
file.Open(fileName);
|
|
||||||
if (!file.Is_Open())
|
|
||||||
{
|
|
||||||
initprintf("InterplayDecoder: Can't open file %s\n", fileName);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
char lsig[20];
|
char lsig[20];
|
||||||
|
|
||||||
// check the file signature
|
// check the file signature
|
||||||
file.ReadBytes((uint8_t*)lsig, sizeof(lsig));
|
fr_.Read((uint8_t*)lsig, sizeof(lsig));
|
||||||
if (memcmp(lsig, "Interplay MVE File\x1A\0", sizeof(lsig)) != 0)
|
if (memcmp(lsig, "Interplay MVE File\x1A\0", sizeof(lsig)) != 0)
|
||||||
{
|
{
|
||||||
initprintf("InterplayDecoder: Unknown MVE signature\n ");
|
Printf(TEXTCOLOR_RED "InterplayDecoder: Unknown MVE signature\n ");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// skip the next 6 bytes
|
// skip the next 6 bytes
|
||||||
file.Skip(6);
|
fr_.Seek(6, FileReader::SeekCur);
|
||||||
|
fr = std::move(fr_);
|
||||||
|
|
||||||
Run();
|
//Run();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InterplayDecoder::Run()
|
bool InterplayDecoder::RunFrame(uint64_t clock)
|
||||||
{
|
{
|
||||||
uint8_t chunkPreamble[CHUNK_PREAMBLE_SIZE];
|
uint8_t chunkPreamble[CHUNK_PREAMBLE_SIZE];
|
||||||
uint8_t opcodePreamble[OPCODE_PREAMBLE_SIZE];
|
uint8_t opcodePreamble[OPCODE_PREAMBLE_SIZE];
|
||||||
|
@ -321,70 +231,31 @@ bool InterplayDecoder::Run()
|
||||||
int opcodeSize, chunkSize;
|
int opcodeSize, chunkSize;
|
||||||
int chunkType = 0;
|
int chunkType = 0;
|
||||||
|
|
||||||
auto const oyxaspect = yxaspect;
|
|
||||||
|
|
||||||
int nScale = tabledivide32(scale(65536, ydim << 2, xdim * 3), ((max(nHeight, 240 + 1u) + 239) / 240));
|
|
||||||
int nStat = 2|4|8|64|1024;
|
|
||||||
renderSetAspect(viewingrange, 65536);
|
|
||||||
|
|
||||||
uint32_t nNextFrameTime = (uint32_t)totalclock + nFrameDuration;
|
|
||||||
|
|
||||||
bIsPlaying = true;
|
|
||||||
|
|
||||||
// iterate through the chunks in the file
|
// iterate through the chunks in the file
|
||||||
while (chunkType != CHUNK_END && bIsPlaying)
|
do
|
||||||
{
|
{
|
||||||
handleevents();
|
|
||||||
|
|
||||||
// handle timing - wait until we're ready to process the next frame.
|
// handle timing - wait until we're ready to process the next frame.
|
||||||
if (nNextFrameTime > (uint32_t)totalclock) {
|
if (nNextFrameTime > clock) {
|
||||||
continue;
|
return true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
nNextFrameTime = (uint32_t)totalclock + nFrameDuration;
|
nNextFrameTime = clock + nFrameDuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file.ReadBytes(chunkPreamble, CHUNK_PREAMBLE_SIZE) != CHUNK_PREAMBLE_SIZE) {
|
if (fr.Read(chunkPreamble, CHUNK_PREAMBLE_SIZE) != CHUNK_PREAMBLE_SIZE) {
|
||||||
initprintf("InterplayDecoder: could not read from file (EOF?)\n");
|
Printf(TEXTCOLOR_RED "InterplayDecoder: could not read from file (EOF?)\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
chunkSize = LE_16(&chunkPreamble[0]);
|
chunkSize = LE_16(&chunkPreamble[0]);
|
||||||
chunkType = LE_16(&chunkPreamble[2]);
|
chunkType = LE_16(&chunkPreamble[2]);
|
||||||
|
|
||||||
switch (chunkType)
|
|
||||||
{
|
|
||||||
case CHUNK_INIT_AUDIO:
|
|
||||||
break;
|
|
||||||
case CHUNK_AUDIO_ONLY:
|
|
||||||
break;
|
|
||||||
case CHUNK_INIT_VIDEO:
|
|
||||||
break;
|
|
||||||
case CHUNK_VIDEO:
|
|
||||||
break;
|
|
||||||
case CHUNK_SHUTDOWN:
|
|
||||||
break;
|
|
||||||
case CHUNK_END:
|
|
||||||
bIsPlaying = false;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// iterate through individual opcodes
|
// iterate through individual opcodes
|
||||||
while (chunkSize > 0)
|
while (chunkSize > 0)
|
||||||
{
|
{
|
||||||
handleevents();
|
if (fr.Read(opcodePreamble, OPCODE_PREAMBLE_SIZE) != OPCODE_PREAMBLE_SIZE)
|
||||||
|
|
||||||
if (KB_KeyWaiting()) {
|
|
||||||
renderSetAspect(viewingrange, oyxaspect);
|
|
||||||
Close();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (file.ReadBytes(opcodePreamble, OPCODE_PREAMBLE_SIZE) != OPCODE_PREAMBLE_SIZE)
|
|
||||||
{
|
{
|
||||||
initprintf("InterplayDecoder: could not read from file (EOF?)\n");
|
Printf(TEXTCOLOR_RED "InterplayDecoder: could not read from file (EOF?)\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -399,38 +270,37 @@ bool InterplayDecoder::Run()
|
||||||
{
|
{
|
||||||
case OPCODE_END_OF_STREAM:
|
case OPCODE_END_OF_STREAM:
|
||||||
{
|
{
|
||||||
file.Skip(opcodeSize);
|
fr.Seek(opcodeSize, FileReader::SeekCur);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case OPCODE_END_OF_CHUNK:
|
case OPCODE_END_OF_CHUNK:
|
||||||
{
|
{
|
||||||
file.Skip(opcodeSize);
|
fr.Seek(opcodeSize, FileReader::SeekCur);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case OPCODE_CREATE_TIMER:
|
case OPCODE_CREATE_TIMER:
|
||||||
{
|
{
|
||||||
nTimerRate = file.ReadUint32LE();
|
nTimerRate = fr.ReadUInt32();
|
||||||
nTimerDiv = file.ReadUint16LE();
|
nTimerDiv = fr.ReadUInt16();
|
||||||
nFps = 1000000.0f / ((double)nTimerRate * nTimerDiv);
|
nFrameDuration = ((double)nTimerRate * nTimerDiv) * 1000;
|
||||||
nFrameDuration = 120.0f / nFps;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case OPCODE_INIT_AUDIO_BUFFERS:
|
case OPCODE_INIT_AUDIO_BUFFERS:
|
||||||
{
|
{
|
||||||
file.Skip(2);
|
fr.Seek(2, FileReader::SeekCur);
|
||||||
uint16_t flags = file.ReadUint16LE();
|
uint16_t flags = fr.ReadUInt16();
|
||||||
audio.nSampleRate = file.ReadUint16LE();
|
audio.nSampleRate = fr.ReadUInt16();
|
||||||
|
|
||||||
uint32_t nBufferBytes;
|
uint32_t nBufferBytes;
|
||||||
|
|
||||||
if (opcodeVersion == 0) {
|
if (opcodeVersion == 0) {
|
||||||
nBufferBytes = file.ReadUint16LE();
|
nBufferBytes = fr.ReadUInt16();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
nBufferBytes = file.ReadUint32LE();
|
nBufferBytes = fr.ReadUInt32();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flags & 0x1) {
|
if (flags & 0x1) {
|
||||||
|
@ -453,22 +323,22 @@ bool InterplayDecoder::Run()
|
||||||
if (!bAudioStarted)
|
if (!bAudioStarted)
|
||||||
{
|
{
|
||||||
// start audio playback
|
// start audio playback
|
||||||
audio.hFx = FX_StartDemandFeedPlayback(ServeAudioSample, audio.nBitDepth, audio.nChannels, audio.nSampleRate, 0, 128, 128, 128, FX_MUSIC_PRIORITY, fix16_one, -1, this);
|
stream = S_CreateCustomStream(6000, audio.nSampleRate, audio.nChannels, StreamCallbackFunc, this);
|
||||||
bAudioStarted = true;
|
bAudioStarted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
file.Skip(opcodeSize);
|
fr.Seek(opcodeSize, FileReader::SeekCur);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case OPCODE_INIT_VIDEO_BUFFERS:
|
case OPCODE_INIT_VIDEO_BUFFERS:
|
||||||
{
|
{
|
||||||
assert(opcodeSize == 8);
|
assert(opcodeSize == 8);
|
||||||
nWidth = file.ReadUint16LE() * 8;
|
nWidth = fr.ReadUInt16() * 8;
|
||||||
nHeight = file.ReadUint16LE() * 8;
|
nHeight = fr.ReadUInt16() * 8;
|
||||||
|
|
||||||
int count = file.ReadUint16LE();
|
int count = fr.ReadUInt16();
|
||||||
int truecolour = file.ReadUint16LE();
|
int truecolour = fr.ReadUInt16();
|
||||||
assert(truecolour == 0);
|
assert(truecolour == 0);
|
||||||
|
|
||||||
pVideoBuffers[0] = new uint8_t[nWidth * nHeight];
|
pVideoBuffers[0] = new uint8_t[nWidth * nHeight];
|
||||||
|
@ -476,11 +346,7 @@ bool InterplayDecoder::Run()
|
||||||
|
|
||||||
videoStride = nWidth;
|
videoStride = nWidth;
|
||||||
|
|
||||||
uint8_t* pFrame = (uint8_t*)Xmalloc(nWidth * nHeight);
|
animtex.SetSize(AnimTexture::Paletted, nWidth, nHeight);
|
||||||
memset(pFrame, 0, nWidth * nHeight);
|
|
||||||
walock[kMVETile] = CACHE1D_PERMANENT;
|
|
||||||
waloff[kMVETile] = (intptr_t)pFrame;
|
|
||||||
tileSetSize(kMVETile, nHeight, nWidth);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -492,42 +358,39 @@ bool InterplayDecoder::Run()
|
||||||
case OPCODE_UNKNOWN_14:
|
case OPCODE_UNKNOWN_14:
|
||||||
case OPCODE_UNKNOWN_15:
|
case OPCODE_UNKNOWN_15:
|
||||||
{
|
{
|
||||||
file.Skip(opcodeSize);
|
fr.Seek(opcodeSize, FileReader::SeekCur);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case OPCODE_SEND_BUFFER:
|
case OPCODE_SEND_BUFFER:
|
||||||
{
|
{
|
||||||
int nPalStart = file.ReadUint16LE();
|
int nPalStart = fr.ReadUInt16();
|
||||||
int nPalCount = file.ReadUint16LE();
|
int nPalCount = fr.ReadUInt16();
|
||||||
|
|
||||||
memcpy((char*)waloff[kMVETile], GetCurrentFrame(), nWidth * nHeight);
|
animtex.SetFrame(&palette[0].r , GetCurrentFrame());
|
||||||
tileInvalidate(kMVETile, -1, -1);
|
|
||||||
|
|
||||||
nFrame++;
|
nFrame++;
|
||||||
SwapFrames();
|
SwapFrames();
|
||||||
|
|
||||||
file.Skip(opcodeSize - 4);
|
fr.Seek(opcodeSize-4, FileReader::SeekCur);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case OPCODE_AUDIO_FRAME:
|
case OPCODE_AUDIO_FRAME:
|
||||||
{
|
{
|
||||||
int nStart = file.GetPosition();
|
int nStart = fr.Tell();
|
||||||
uint16_t seqIndex = file.ReadUint16LE();
|
uint16_t seqIndex = fr.ReadUInt16();
|
||||||
uint16_t streamMask = file.ReadUint16LE();
|
uint16_t streamMask = fr.ReadUInt16();
|
||||||
uint16_t nSamples = file.ReadUint16LE(); // number of samples this chunk
|
uint16_t nSamples = fr.ReadUInt16(); // number of samples this chunk
|
||||||
|
|
||||||
int predictor[2];
|
int predictor[2];
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
mutex_lock(&mutex);
|
|
||||||
|
|
||||||
int16_t* pBuf = audio.block[audio.nWrite].buf;
|
int16_t* pBuf = audio.block[audio.nWrite].buf;
|
||||||
|
|
||||||
for (int ch = 0; ch < audio.nChannels; ch++)
|
for (int ch = 0; ch < audio.nChannels; ch++)
|
||||||
{
|
{
|
||||||
predictor[ch] = file.ReadUint16LE();
|
predictor[ch] = fr.ReadUInt16();
|
||||||
i++;
|
i++;
|
||||||
|
|
||||||
if (predictor[ch] & 0x8000) {
|
if (predictor[ch] & 0x8000) {
|
||||||
|
@ -540,7 +403,7 @@ bool InterplayDecoder::Run()
|
||||||
int ch = 0;
|
int ch = 0;
|
||||||
for (; i < (nSamples / 2); i++)
|
for (; i < (nSamples / 2); i++)
|
||||||
{
|
{
|
||||||
predictor[ch] += delta_table[file.ReadByte()];
|
predictor[ch] += delta_table[fr.ReadUInt8()];
|
||||||
predictor[ch] = ClipRange(predictor[ch], -32768, 32768);
|
predictor[ch] = ClipRange(predictor[ch], -32768, 32768);
|
||||||
|
|
||||||
*pBuf++ = predictor[ch];
|
*pBuf++ = predictor[ch];
|
||||||
|
@ -555,62 +418,57 @@ bool InterplayDecoder::Run()
|
||||||
if (audio.nWrite >= kAudioBlocks)
|
if (audio.nWrite >= kAudioBlocks)
|
||||||
audio.nWrite = 0;
|
audio.nWrite = 0;
|
||||||
|
|
||||||
int nEnd = file.GetPosition();
|
int nEnd = fr.Tell();
|
||||||
int nRead = nEnd - nStart;
|
int nRead = nEnd - nStart;
|
||||||
assert(opcodeSize == nRead);
|
assert(opcodeSize == nRead);
|
||||||
|
|
||||||
mutex_unlock(&mutex);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case OPCODE_SILENCE_FRAME:
|
case OPCODE_SILENCE_FRAME:
|
||||||
{
|
{
|
||||||
uint16_t seqIndex = file.ReadUint16LE();
|
uint16_t seqIndex = fr.ReadUInt16();
|
||||||
uint16_t streamMask = file.ReadUint16LE();
|
uint16_t streamMask = fr.ReadUInt16();
|
||||||
uint16_t nStreamLen = file.ReadUint16LE();
|
uint16_t nStreamLen = fr.ReadUInt16();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case OPCODE_INIT_VIDEO_MODE:
|
case OPCODE_INIT_VIDEO_MODE:
|
||||||
{
|
{
|
||||||
file.Skip(opcodeSize);
|
fr.Seek(opcodeSize, FileReader::SeekCur);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case OPCODE_CREATE_GRADIENT:
|
case OPCODE_CREATE_GRADIENT:
|
||||||
{
|
{
|
||||||
file.Skip(opcodeSize);
|
fr.Seek(opcodeSize, FileReader::SeekCur);
|
||||||
initprintf("InterplayDecoder: Create gradient not supported.\n");
|
Printf("InterplayDecoder: Create gradient not supported.\n");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case OPCODE_SET_PALETTE:
|
case OPCODE_SET_PALETTE:
|
||||||
{
|
{
|
||||||
if (opcodeSize > 0x304 || opcodeSize < 4) {
|
if (opcodeSize > 0x304 || opcodeSize < 4) {
|
||||||
printf("set_palette opcode with invalid size\n");
|
Printf("set_palette opcode with invalid size\n");
|
||||||
chunkType = CHUNK_BAD;
|
chunkType = CHUNK_BAD;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
int nPalStart = file.ReadUint16LE();
|
int nPalStart = fr.ReadUInt16();
|
||||||
int nPalCount = file.ReadUint16LE();
|
int nPalCount = fr.ReadUInt16();
|
||||||
|
|
||||||
for (int i = nPalStart; i <= nPalCount; i++)
|
for (int i = nPalStart; i <= nPalCount; i++)
|
||||||
{
|
{
|
||||||
palette[i].r = file.ReadByte() << 2;
|
palette[i].r = fr.ReadUInt8() << 2;
|
||||||
palette[i].g = file.ReadByte() << 2;
|
palette[i].g = fr.ReadUInt8() << 2;
|
||||||
palette[i].b = file.ReadByte() << 2;
|
palette[i].b = fr.ReadUInt8() << 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
paletteSetColorTable(kMVEPal, (uint8_t*)palette);
|
|
||||||
videoSetPalette(0, kMVEPal, 0);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case OPCODE_SET_PALETTE_COMPRESSED:
|
case OPCODE_SET_PALETTE_COMPRESSED:
|
||||||
{
|
{
|
||||||
file.Skip(opcodeSize);
|
fr.Seek(opcodeSize, FileReader::SeekCur);
|
||||||
initprintf("InterplayDecoder: Set palette compressed not supported.\n");
|
Printf("InterplayDecoder: Set palette compressed not supported.\n");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -630,17 +488,17 @@ bool InterplayDecoder::Run()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int nRead = file.ReadBytes(decodeMap.pData, opcodeSize);
|
int nRead = fr.Read(decodeMap.pData, opcodeSize);
|
||||||
assert(nRead == opcodeSize);
|
assert(nRead == opcodeSize);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case OPCODE_VIDEO_DATA:
|
case OPCODE_VIDEO_DATA:
|
||||||
{
|
{
|
||||||
int nStart = file.GetPosition();
|
int nStart = fr.Tell();
|
||||||
|
|
||||||
// need to skip 14 bytes
|
// need to skip 14 bytes
|
||||||
file.Skip(14);
|
fr.Seek(14, FileReader::SeekCur);
|
||||||
|
|
||||||
if (decodeMap.nSize)
|
if (decodeMap.nSize)
|
||||||
{
|
{
|
||||||
|
@ -717,11 +575,11 @@ bool InterplayDecoder::Run()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int nEnd = file.GetPosition();
|
int nEnd = fr.Tell();
|
||||||
int nSkipBytes = opcodeSize - (nEnd - nStart); // we can end up with 1 byte left we need to skip
|
int nSkipBytes = opcodeSize - (nEnd - nStart); // we can end up with 1 byte left we need to skip
|
||||||
assert(nSkipBytes <= 1);
|
assert(nSkipBytes <= 1);
|
||||||
|
|
||||||
file.Skip(nSkipBytes);
|
fr.Seek(nSkipBytes, FileReader::SeekCur);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -730,17 +588,9 @@ bool InterplayDecoder::Run()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
videoClearScreen(0);
|
|
||||||
|
|
||||||
rotatesprite_fs(160 << 16, 100 << 16, nScale, 512, kMVETile, 0, 0, nStat);
|
|
||||||
videoNextPage();
|
|
||||||
}
|
}
|
||||||
|
while (chunkType < CHUNK_VIDEO && bIsPlaying);
|
||||||
renderSetAspect(viewingrange, oyxaspect);
|
return chunkType != CHUNK_END;
|
||||||
|
|
||||||
Close();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void InterplayDecoder::CopyBlock(uint8_t* pDest, uint8_t* pSrc)
|
void InterplayDecoder::CopyBlock(uint8_t* pDest, uint8_t* pSrc)
|
||||||
|
@ -770,7 +620,7 @@ void InterplayDecoder::DecodeBlock1(int32_t offset)
|
||||||
void InterplayDecoder::DecodeBlock2(int32_t offset)
|
void InterplayDecoder::DecodeBlock2(int32_t offset)
|
||||||
{
|
{
|
||||||
// copy block from 2 frames ago using a motion vector; need 1 more byte
|
// copy block from 2 frames ago using a motion vector; need 1 more byte
|
||||||
uint8_t B = file.ReadByte();
|
uint8_t B = fr.ReadUInt8();
|
||||||
|
|
||||||
int x, y;
|
int x, y;
|
||||||
|
|
||||||
|
@ -792,7 +642,7 @@ void InterplayDecoder::DecodeBlock2(int32_t offset)
|
||||||
void InterplayDecoder::DecodeBlock3(int32_t offset)
|
void InterplayDecoder::DecodeBlock3(int32_t offset)
|
||||||
{
|
{
|
||||||
// copy 8x8 block from current frame from an up/left block
|
// copy 8x8 block from current frame from an up/left block
|
||||||
uint8_t B = file.ReadByte();
|
uint8_t B = fr.ReadUInt8();
|
||||||
|
|
||||||
int x, y;
|
int x, y;
|
||||||
|
|
||||||
|
@ -818,7 +668,7 @@ void InterplayDecoder::DecodeBlock4(int32_t offset)
|
||||||
int x, y;
|
int x, y;
|
||||||
uint8_t B, BL, BH;
|
uint8_t B, BL, BH;
|
||||||
|
|
||||||
B = file.ReadByte();
|
B = fr.ReadUInt8();
|
||||||
|
|
||||||
BL = B & 0x0F;
|
BL = B & 0x0F;
|
||||||
BH = (B >> 4) & 0x0F;
|
BH = (B >> 4) & 0x0F;
|
||||||
|
@ -834,8 +684,8 @@ void InterplayDecoder::DecodeBlock4(int32_t offset)
|
||||||
void InterplayDecoder::DecodeBlock5(int32_t offset)
|
void InterplayDecoder::DecodeBlock5(int32_t offset)
|
||||||
{
|
{
|
||||||
// copy a block from the previous frame using an expanded range; need 2 more bytes
|
// copy a block from the previous frame using an expanded range; need 2 more bytes
|
||||||
int8_t x = file.ReadByte();
|
int8_t x = fr.ReadUInt8();
|
||||||
int8_t y = file.ReadByte();
|
int8_t y = fr.ReadUInt8();
|
||||||
|
|
||||||
uint8_t* pDest = GetCurrentFrame() + (intptr_t)offset;
|
uint8_t* pDest = GetCurrentFrame() + (intptr_t)offset;
|
||||||
uint8_t* pSrc = GetPreviousFrame() + (intptr_t)(int64_t)offset + (int64_t)x + (int64_t(y) * (int64_t)videoStride);
|
uint8_t* pSrc = GetPreviousFrame() + (intptr_t)(int64_t)offset + (int64_t)x + (int64_t(y) * (int64_t)videoStride);
|
||||||
|
@ -851,8 +701,8 @@ void InterplayDecoder::DecodeBlock7(int32_t offset)
|
||||||
uint32_t flags = 0;
|
uint32_t flags = 0;
|
||||||
|
|
||||||
uint8_t P[2];
|
uint8_t P[2];
|
||||||
P[0] = file.ReadByte();
|
P[0] = fr.ReadUInt8();
|
||||||
P[1] = file.ReadByte();
|
P[1] = fr.ReadUInt8();
|
||||||
|
|
||||||
// 2-color encoding
|
// 2-color encoding
|
||||||
if (P[0] <= P[1])
|
if (P[0] <= P[1])
|
||||||
|
@ -860,7 +710,7 @@ void InterplayDecoder::DecodeBlock7(int32_t offset)
|
||||||
// need 8 more bytes from the stream
|
// need 8 more bytes from the stream
|
||||||
for (int y = 0; y < 8; y++)
|
for (int y = 0; y < 8; y++)
|
||||||
{
|
{
|
||||||
flags = file.ReadByte() | 0x100;
|
flags = fr.ReadUInt8() | 0x100;
|
||||||
for (; flags != 1; flags >>= 1) {
|
for (; flags != 1; flags >>= 1) {
|
||||||
*pBuffer++ = P[flags & 1];
|
*pBuffer++ = P[flags & 1];
|
||||||
}
|
}
|
||||||
|
@ -870,7 +720,7 @@ void InterplayDecoder::DecodeBlock7(int32_t offset)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// need 2 more bytes from the stream
|
// need 2 more bytes from the stream
|
||||||
flags = file.ReadUint16LE();
|
flags = fr.ReadUInt16();
|
||||||
|
|
||||||
for (int y = 0; y < 8; y += 2)
|
for (int y = 0; y < 8; y += 2)
|
||||||
{
|
{
|
||||||
|
@ -893,8 +743,8 @@ void InterplayDecoder::DecodeBlock8(int32_t offset)
|
||||||
uint8_t P[4];
|
uint8_t P[4];
|
||||||
|
|
||||||
// 2-color encoding for each 4x4 quadrant, or 2-color encoding on either top and bottom or left and right halves
|
// 2-color encoding for each 4x4 quadrant, or 2-color encoding on either top and bottom or left and right halves
|
||||||
P[0] = file.ReadByte();
|
P[0] = fr.ReadUInt8();
|
||||||
P[1] = file.ReadByte();
|
P[1] = fr.ReadUInt8();
|
||||||
|
|
||||||
if (P[0] <= P[1])
|
if (P[0] <= P[1])
|
||||||
{
|
{
|
||||||
|
@ -904,10 +754,10 @@ void InterplayDecoder::DecodeBlock8(int32_t offset)
|
||||||
if (!(y & 3))
|
if (!(y & 3))
|
||||||
{
|
{
|
||||||
if (y) {
|
if (y) {
|
||||||
P[0] = file.ReadByte();
|
P[0] = fr.ReadUInt8();
|
||||||
P[1] = file.ReadByte();
|
P[1] = fr.ReadUInt8();
|
||||||
}
|
}
|
||||||
flags = file.ReadUint16LE();
|
flags = fr.ReadUInt16();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int x = 0; x < 4; x++, flags >>= 1) {
|
for (int x = 0; x < 4; x++, flags >>= 1) {
|
||||||
|
@ -921,9 +771,9 @@ void InterplayDecoder::DecodeBlock8(int32_t offset)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
flags = file.ReadUint32LE();
|
flags = fr.ReadUInt32();
|
||||||
P[2] = file.ReadByte();
|
P[2] = fr.ReadUInt8();
|
||||||
P[3] = file.ReadByte();
|
P[3] = fr.ReadUInt8();
|
||||||
|
|
||||||
if (P[2] <= P[3])
|
if (P[2] <= P[3])
|
||||||
{
|
{
|
||||||
|
@ -941,7 +791,7 @@ void InterplayDecoder::DecodeBlock8(int32_t offset)
|
||||||
pBuffer -= 8 * videoStride - 4;
|
pBuffer -= 8 * videoStride - 4;
|
||||||
P[0] = P[2];
|
P[0] = P[2];
|
||||||
P[1] = P[3];
|
P[1] = P[3];
|
||||||
flags = file.ReadUint32LE();
|
flags = fr.ReadUInt32();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -953,7 +803,7 @@ void InterplayDecoder::DecodeBlock8(int32_t offset)
|
||||||
if (y == 4) {
|
if (y == 4) {
|
||||||
P[0] = P[2];
|
P[0] = P[2];
|
||||||
P[1] = P[3];
|
P[1] = P[3];
|
||||||
flags = file.ReadUint32LE();
|
flags = fr.ReadUInt32();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int x = 0; x < 8; x++, flags >>= 1)
|
for (int x = 0; x < 8; x++, flags >>= 1)
|
||||||
|
@ -970,7 +820,7 @@ void InterplayDecoder::DecodeBlock9(int32_t offset)
|
||||||
uint8_t* pBuffer = GetCurrentFrame() + (intptr_t)offset;
|
uint8_t* pBuffer = GetCurrentFrame() + (intptr_t)offset;
|
||||||
uint8_t P[4];
|
uint8_t P[4];
|
||||||
|
|
||||||
file.ReadBytes(P, 4);
|
fr.Read(P, 4);
|
||||||
|
|
||||||
// 4-color encoding
|
// 4-color encoding
|
||||||
if (P[0] <= P[1])
|
if (P[0] <= P[1])
|
||||||
|
@ -981,7 +831,7 @@ void InterplayDecoder::DecodeBlock9(int32_t offset)
|
||||||
for (int y = 0; y < 8; y++)
|
for (int y = 0; y < 8; y++)
|
||||||
{
|
{
|
||||||
// get the next set of 8 2-bit flags
|
// get the next set of 8 2-bit flags
|
||||||
int flags = file.ReadUint16LE();
|
int flags = fr.ReadUInt16();
|
||||||
|
|
||||||
for (int x = 0; x < 8; x++, flags >>= 2) {
|
for (int x = 0; x < 8; x++, flags >>= 2) {
|
||||||
*pBuffer++ = P[flags & 0x03];
|
*pBuffer++ = P[flags & 0x03];
|
||||||
|
@ -993,7 +843,7 @@ void InterplayDecoder::DecodeBlock9(int32_t offset)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// 1 of 4 colors for each 2x2 block, need 4 more bytes
|
// 1 of 4 colors for each 2x2 block, need 4 more bytes
|
||||||
uint32_t flags = file.ReadUint32LE();
|
uint32_t flags = fr.ReadUInt32();
|
||||||
|
|
||||||
for (int y = 0; y < 8; y += 2)
|
for (int y = 0; y < 8; y += 2)
|
||||||
{
|
{
|
||||||
|
@ -1012,7 +862,7 @@ void InterplayDecoder::DecodeBlock9(int32_t offset)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// 1 of 4 colors for each 2x1 or 1x2 block, need 8 more bytes
|
// 1 of 4 colors for each 2x1 or 1x2 block, need 8 more bytes
|
||||||
uint64_t flags = file.ReadUint64LE();
|
uint64_t flags = fr.ReadUInt64();
|
||||||
|
|
||||||
if (P[2] <= P[3])
|
if (P[2] <= P[3])
|
||||||
{
|
{
|
||||||
|
@ -1046,7 +896,7 @@ void InterplayDecoder::DecodeBlock10(int32_t offset)
|
||||||
uint8_t* pBuffer = GetCurrentFrame() + (intptr_t)offset;
|
uint8_t* pBuffer = GetCurrentFrame() + (intptr_t)offset;
|
||||||
uint8_t P[8];
|
uint8_t P[8];
|
||||||
|
|
||||||
file.ReadBytes(P, 4);
|
fr.Read(P, 4);
|
||||||
|
|
||||||
// 4-color encoding for each 4x4 quadrant, or 4-color encoding on either top and bottom or left and right halves
|
// 4-color encoding for each 4x4 quadrant, or 4-color encoding on either top and bottom or left and right halves
|
||||||
if (P[0] <= P[1])
|
if (P[0] <= P[1])
|
||||||
|
@ -1058,8 +908,8 @@ void InterplayDecoder::DecodeBlock10(int32_t offset)
|
||||||
{
|
{
|
||||||
// new values for each 4x4 block
|
// new values for each 4x4 block
|
||||||
if (!(y & 3)) {
|
if (!(y & 3)) {
|
||||||
if (y) file.ReadBytes(P, 4);
|
if (y) fr.Read(P, 4);
|
||||||
flags = file.ReadUint32LE();
|
flags = fr.ReadUInt32();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int x = 0; x < 4; x++, flags >>= 2) {
|
for (int x = 0; x < 4; x++, flags >>= 2) {
|
||||||
|
@ -1075,9 +925,9 @@ void InterplayDecoder::DecodeBlock10(int32_t offset)
|
||||||
{
|
{
|
||||||
// vertical split?
|
// vertical split?
|
||||||
int vert;
|
int vert;
|
||||||
uint64_t flags = file.ReadUint64LE();
|
uint64_t flags = fr.ReadUInt64();
|
||||||
|
|
||||||
file.ReadBytes(P + 4, 4);
|
fr.Read(P + 4, 4);
|
||||||
vert = P[4] <= P[5];
|
vert = P[4] <= P[5];
|
||||||
|
|
||||||
// 4-color encoding for either left and right or top and bottom halves
|
// 4-color encoding for either left and right or top and bottom halves
|
||||||
|
@ -1097,7 +947,7 @@ void InterplayDecoder::DecodeBlock10(int32_t offset)
|
||||||
// load values for second half
|
// load values for second half
|
||||||
if (y == 7) {
|
if (y == 7) {
|
||||||
memcpy(P, P + 4, 4);
|
memcpy(P, P + 4, 4);
|
||||||
flags = file.ReadUint64LE();
|
flags = fr.ReadUInt64();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1110,7 +960,7 @@ void InterplayDecoder::DecodeBlock11(int32_t offset)
|
||||||
|
|
||||||
for (int y = 0; y < 8; y++)
|
for (int y = 0; y < 8; y++)
|
||||||
{
|
{
|
||||||
file.ReadBytes(pBuffer, 8);
|
fr.Read(pBuffer, 8);
|
||||||
pBuffer += videoStride;
|
pBuffer += videoStride;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1127,7 +977,7 @@ void InterplayDecoder::DecodeBlock12(int32_t offset)
|
||||||
pBuffer[x] =
|
pBuffer[x] =
|
||||||
pBuffer[x + 1] =
|
pBuffer[x + 1] =
|
||||||
pBuffer[x + videoStride] =
|
pBuffer[x + videoStride] =
|
||||||
pBuffer[x + 1 + videoStride] = file.ReadByte();
|
pBuffer[x + 1 + videoStride] = fr.ReadUInt8();
|
||||||
}
|
}
|
||||||
pBuffer += videoStride * 2;
|
pBuffer += videoStride * 2;
|
||||||
}
|
}
|
||||||
|
@ -1143,8 +993,8 @@ void InterplayDecoder::DecodeBlock13(int32_t offset)
|
||||||
{
|
{
|
||||||
if (!(y & 3))
|
if (!(y & 3))
|
||||||
{
|
{
|
||||||
P[0] = file.ReadByte();
|
P[0] = fr.ReadUInt8();
|
||||||
P[1] = file.ReadByte();
|
P[1] = fr.ReadUInt8();
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(pBuffer, P[0], 4);
|
memset(pBuffer, P[0], 4);
|
||||||
|
@ -1157,7 +1007,7 @@ void InterplayDecoder::DecodeBlock14(int32_t offset)
|
||||||
{
|
{
|
||||||
// 1-color encoding : the whole block is 1 solid color
|
// 1-color encoding : the whole block is 1 solid color
|
||||||
uint8_t* pBuffer = GetCurrentFrame() + (intptr_t)offset;
|
uint8_t* pBuffer = GetCurrentFrame() + (intptr_t)offset;
|
||||||
uint8_t pix = file.ReadByte();
|
uint8_t pix = fr.ReadUInt8();
|
||||||
|
|
||||||
for (int y = 0; y < 8; y++)
|
for (int y = 0; y < 8; y++)
|
||||||
{
|
{
|
||||||
|
@ -1172,8 +1022,8 @@ void InterplayDecoder::DecodeBlock15(int32_t offset)
|
||||||
uint8_t* pBuffer = GetCurrentFrame() + (intptr_t)offset;
|
uint8_t* pBuffer = GetCurrentFrame() + (intptr_t)offset;
|
||||||
uint8_t P[2];
|
uint8_t P[2];
|
||||||
|
|
||||||
P[0] = file.ReadByte();
|
P[0] = fr.ReadUInt8();
|
||||||
P[1] = file.ReadByte();
|
P[1] = fr.ReadUInt8();
|
||||||
|
|
||||||
for (int y = 0; y < 8; y++)
|
for (int y = 0; y < 8; y++)
|
||||||
{
|
{
|
186
source/core/movie/playmve.h
Normal file
186
source/core/movie/playmve.h
Normal file
|
@ -0,0 +1,186 @@
|
||||||
|
/*
|
||||||
|
* InterplayDecoder
|
||||||
|
* Copyright (C) 2020 sirlemonhead
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library 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
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* This code is based on interplayvideo.c, dpcm.c and ipmovie.c from the FFmpeg project which can be obtained
|
||||||
|
* from http://www.ffmpeg.org/. Below is the license from interplayvideo.c
|
||||||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Interplay MVE Video Decoder
|
||||||
|
* Copyright (C) 2003 The FFmpeg project
|
||||||
|
*
|
||||||
|
* This file is part of FFmpeg.
|
||||||
|
*
|
||||||
|
* FFmpeg is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* FFmpeg 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
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with FFmpeg; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "files.h"
|
||||||
|
#include "animtexture.h"
|
||||||
|
#include "s_music.h"
|
||||||
|
|
||||||
|
#ifndef playmve_h_
|
||||||
|
#define playmve_h_
|
||||||
|
|
||||||
|
class InterplayDecoder
|
||||||
|
{
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
CHUNK_PREAMBLE_SIZE = 4,
|
||||||
|
OPCODE_PREAMBLE_SIZE = 4,
|
||||||
|
|
||||||
|
CHUNK_INIT_AUDIO = 0x0000,
|
||||||
|
CHUNK_AUDIO_ONLY = 0x0001,
|
||||||
|
CHUNK_INIT_VIDEO = 0x0002,
|
||||||
|
CHUNK_VIDEO = 0x0003,
|
||||||
|
CHUNK_SHUTDOWN = 0x0004,
|
||||||
|
CHUNK_END = 0x0005,
|
||||||
|
/* these last types are used internally */
|
||||||
|
CHUNK_DONE = 0xFFFC,
|
||||||
|
CHUNK_NOMEM = 0xFFFD,
|
||||||
|
CHUNK_EOF = 0xFFFE,
|
||||||
|
CHUNK_BAD = 0xFFFF,
|
||||||
|
|
||||||
|
OPCODE_END_OF_STREAM = 0x00,
|
||||||
|
OPCODE_END_OF_CHUNK = 0x01,
|
||||||
|
OPCODE_CREATE_TIMER = 0x02,
|
||||||
|
OPCODE_INIT_AUDIO_BUFFERS = 0x03,
|
||||||
|
OPCODE_START_STOP_AUDIO = 0x04,
|
||||||
|
OPCODE_INIT_VIDEO_BUFFERS = 0x05,
|
||||||
|
OPCODE_UNKNOWN_06 = 0x06,
|
||||||
|
OPCODE_SEND_BUFFER = 0x07,
|
||||||
|
OPCODE_AUDIO_FRAME = 0x08,
|
||||||
|
OPCODE_SILENCE_FRAME = 0x09,
|
||||||
|
OPCODE_INIT_VIDEO_MODE = 0x0A,
|
||||||
|
OPCODE_CREATE_GRADIENT = 0x0B,
|
||||||
|
OPCODE_SET_PALETTE = 0x0C,
|
||||||
|
OPCODE_SET_PALETTE_COMPRESSED = 0x0D,
|
||||||
|
OPCODE_UNKNOWN_0E = 0x0E,
|
||||||
|
OPCODE_SET_DECODING_MAP = 0x0F,
|
||||||
|
OPCODE_UNKNOWN_10 = 0x10,
|
||||||
|
OPCODE_VIDEO_DATA = 0x11,
|
||||||
|
OPCODE_UNKNOWN_12 = 0x12,
|
||||||
|
OPCODE_UNKNOWN_13 = 0x13,
|
||||||
|
OPCODE_UNKNOWN_14 = 0x14,
|
||||||
|
OPCODE_UNKNOWN_15 = 0x15,
|
||||||
|
|
||||||
|
PALETTE_COUNT = 256,
|
||||||
|
kAudioBlocks = 20 // alloc a lot of blocks - need to store lots of audio data before video frames start.
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
InterplayDecoder();
|
||||||
|
~InterplayDecoder();
|
||||||
|
|
||||||
|
bool Open(FileReader &fr);
|
||||||
|
void Close();
|
||||||
|
bool RunFrame(uint64_t clock);
|
||||||
|
|
||||||
|
struct AudioBlock
|
||||||
|
{
|
||||||
|
int16_t buf[6000];
|
||||||
|
uint32_t size;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AudioData
|
||||||
|
{
|
||||||
|
int hFx;
|
||||||
|
int nChannels;
|
||||||
|
uint16_t nSampleRate;
|
||||||
|
uint8_t nBitDepth;
|
||||||
|
|
||||||
|
AudioBlock block[kAudioBlocks];
|
||||||
|
int nWrite;
|
||||||
|
int nRead;
|
||||||
|
int nSubIndex;
|
||||||
|
};
|
||||||
|
|
||||||
|
AudioData audio;
|
||||||
|
AnimTextures animtex;
|
||||||
|
|
||||||
|
AnimTextures& animTex() { return animtex; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct DecodeMap
|
||||||
|
{
|
||||||
|
uint8_t* pData;
|
||||||
|
uint32_t nSize;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Palette
|
||||||
|
{
|
||||||
|
uint8_t r;
|
||||||
|
uint8_t g;
|
||||||
|
uint8_t b;
|
||||||
|
};
|
||||||
|
|
||||||
|
uint8_t* GetCurrentFrame();
|
||||||
|
uint8_t* GetPreviousFrame();
|
||||||
|
void SwapFrames();
|
||||||
|
void CopyBlock(uint8_t* pDest, uint8_t* pSrc);
|
||||||
|
void DecodeBlock0(int32_t offset);
|
||||||
|
void DecodeBlock1(int32_t offset);
|
||||||
|
void DecodeBlock2(int32_t offset);
|
||||||
|
void DecodeBlock3(int32_t offset);
|
||||||
|
void DecodeBlock4(int32_t offset);
|
||||||
|
void DecodeBlock5(int32_t offset);
|
||||||
|
void DecodeBlock7(int32_t offset);
|
||||||
|
void DecodeBlock8(int32_t offset);
|
||||||
|
void DecodeBlock9(int32_t offset);
|
||||||
|
void DecodeBlock10(int32_t offset);
|
||||||
|
void DecodeBlock11(int32_t offset);
|
||||||
|
void DecodeBlock12(int32_t offset);
|
||||||
|
void DecodeBlock13(int32_t offset);
|
||||||
|
void DecodeBlock14(int32_t offset);
|
||||||
|
void DecodeBlock15(int32_t offset);
|
||||||
|
|
||||||
|
FileReader fr;
|
||||||
|
|
||||||
|
bool bIsPlaying, bAudioStarted;
|
||||||
|
|
||||||
|
uint32_t nTimerRate, nTimerDiv;
|
||||||
|
uint32_t nWidth, nHeight, nFrame;
|
||||||
|
double nFps;
|
||||||
|
uint64_t nFrameDuration;
|
||||||
|
|
||||||
|
uint8_t* pVideoBuffers[2];
|
||||||
|
uint32_t nCurrentVideoBuffer, nPreviousVideoBuffer;
|
||||||
|
int32_t videoStride;
|
||||||
|
|
||||||
|
DecodeMap decodeMap;
|
||||||
|
|
||||||
|
Palette palette[256];
|
||||||
|
uint64_t nNextFrameTime = 0;
|
||||||
|
SoundStream* stream = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -44,6 +44,7 @@
|
||||||
#include "animtexture.h"
|
#include "animtexture.h"
|
||||||
#include "gamestate.h"
|
#include "gamestate.h"
|
||||||
#include "menu.h"
|
#include "menu.h"
|
||||||
|
#include "movie/playmve.h"
|
||||||
|
|
||||||
|
|
||||||
IMPLEMENT_CLASS(DScreenJob, true, false)
|
IMPLEMENT_CLASS(DScreenJob, true, false)
|
||||||
|
@ -175,7 +176,48 @@ public:
|
||||||
//
|
//
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
DScreenJob *PlayVideo(const char* filename, const AnimSound* ans, const int* frameticks)
|
class DMvePlayer : public DScreenJob
|
||||||
|
{
|
||||||
|
InterplayDecoder decoder;
|
||||||
|
bool failed = false;
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool isvalid() { return !failed; }
|
||||||
|
|
||||||
|
DMvePlayer(FileReader& fr)
|
||||||
|
{
|
||||||
|
failed = !decoder.Open(fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
int Frame(uint64_t clock, bool skiprequest) override
|
||||||
|
{
|
||||||
|
if (failed) return -1;
|
||||||
|
bool playon = decoder.RunFrame(clock);
|
||||||
|
twod->ClearScreen();
|
||||||
|
DrawTexture(twod, decoder.animTex().GetFrame(), 0, 0, DTA_FullscreenEx, 3, TAG_DONE);
|
||||||
|
|
||||||
|
return skiprequest ? -1 : playon ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnDestroy() override
|
||||||
|
{
|
||||||
|
decoder.Close();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
DScreenJob* PlayVideo(const char* filename, const AnimSound* ans, const int* frameticks)
|
||||||
{
|
{
|
||||||
auto nothing = []()->DScreenJob* { return Create<DScreenJob>(); };
|
auto nothing = []()->DScreenJob* { return Create<DScreenJob>(); };
|
||||||
if (!filename)
|
if (!filename)
|
||||||
|
@ -200,7 +242,7 @@ DScreenJob *PlayVideo(const char* filename, const AnimSound* ans, const int* fra
|
||||||
{
|
{
|
||||||
Printf("%s: invalid ANM file.\n", filename);
|
Printf("%s: invalid ANM file.\n", filename);
|
||||||
anm->Destroy();
|
anm->Destroy();
|
||||||
return nothing();
|
return nothing();
|
||||||
}
|
}
|
||||||
return anm;
|
return anm;
|
||||||
}
|
}
|
||||||
|
@ -208,9 +250,15 @@ DScreenJob *PlayVideo(const char* filename, const AnimSound* ans, const int* fra
|
||||||
{
|
{
|
||||||
// todo
|
// todo
|
||||||
}
|
}
|
||||||
else if (!memcmp(id, "Interplay MVE file", 18))
|
else if (!memcmp(id, "Interplay MVE File", 18))
|
||||||
{
|
{
|
||||||
// todo
|
auto anm = Create<DMvePlayer>(fr);
|
||||||
|
if (!anm->isvalid())
|
||||||
|
{
|
||||||
|
anm->Destroy();
|
||||||
|
return nothing();
|
||||||
|
}
|
||||||
|
return anm;
|
||||||
}
|
}
|
||||||
// add more formats here.
|
// add more formats here.
|
||||||
else
|
else
|
||||||
|
|
|
@ -1,100 +0,0 @@
|
||||||
/*
|
|
||||||
* InterplayDecoder
|
|
||||||
* Copyright (C) 2020 sirlemonhead
|
|
||||||
*
|
|
||||||
* This library is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
|
||||||
* License as published by the Free Software Foundation; either
|
|
||||||
* version 2.1 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This library 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
|
|
||||||
* Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public
|
|
||||||
* License along with this library; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* This code is based on interplayvideo.c, dpcm.c and ipmovie.c from the FFmpeg project which can be obtained
|
|
||||||
* from http://www.ffmpeg.org/. Below is the license from interplayvideo.c
|
|
||||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Interplay MVE Video Decoder
|
|
||||||
* Copyright (C) 2003 The FFmpeg project
|
|
||||||
*
|
|
||||||
* This file is part of FFmpeg.
|
|
||||||
*
|
|
||||||
* FFmpeg is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
|
||||||
* License as published by the Free Software Foundation; either
|
|
||||||
* version 2.1 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* FFmpeg 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
|
|
||||||
* Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public
|
|
||||||
* License along with FFmpeg; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifndef playmve_h_
|
|
||||||
#define playmve_h_
|
|
||||||
|
|
||||||
#include "baselayer.h"
|
|
||||||
#include "build.h"
|
|
||||||
#include "compat.h"
|
|
||||||
#include "pragmas.h"
|
|
||||||
|
|
||||||
bool playmve(const char* filename);
|
|
||||||
|
|
||||||
#define kMVETile (MAXTILES-1)
|
|
||||||
#define kMVEPal 5
|
|
||||||
|
|
||||||
#define CHUNK_PREAMBLE_SIZE 4
|
|
||||||
#define OPCODE_PREAMBLE_SIZE 4
|
|
||||||
|
|
||||||
#define CHUNK_INIT_AUDIO 0x0000
|
|
||||||
#define CHUNK_AUDIO_ONLY 0x0001
|
|
||||||
#define CHUNK_INIT_VIDEO 0x0002
|
|
||||||
#define CHUNK_VIDEO 0x0003
|
|
||||||
#define CHUNK_SHUTDOWN 0x0004
|
|
||||||
#define CHUNK_END 0x0005
|
|
||||||
/* these last types are used internally */
|
|
||||||
#define CHUNK_DONE 0xFFFC
|
|
||||||
#define CHUNK_NOMEM 0xFFFD
|
|
||||||
#define CHUNK_EOF 0xFFFE
|
|
||||||
#define CHUNK_BAD 0xFFFF
|
|
||||||
|
|
||||||
#define OPCODE_END_OF_STREAM 0x00
|
|
||||||
#define OPCODE_END_OF_CHUNK 0x01
|
|
||||||
#define OPCODE_CREATE_TIMER 0x02
|
|
||||||
#define OPCODE_INIT_AUDIO_BUFFERS 0x03
|
|
||||||
#define OPCODE_START_STOP_AUDIO 0x04
|
|
||||||
#define OPCODE_INIT_VIDEO_BUFFERS 0x05
|
|
||||||
#define OPCODE_UNKNOWN_06 0x06
|
|
||||||
#define OPCODE_SEND_BUFFER 0x07
|
|
||||||
#define OPCODE_AUDIO_FRAME 0x08
|
|
||||||
#define OPCODE_SILENCE_FRAME 0x09
|
|
||||||
#define OPCODE_INIT_VIDEO_MODE 0x0A
|
|
||||||
#define OPCODE_CREATE_GRADIENT 0x0B
|
|
||||||
#define OPCODE_SET_PALETTE 0x0C
|
|
||||||
#define OPCODE_SET_PALETTE_COMPRESSED 0x0D
|
|
||||||
#define OPCODE_UNKNOWN_0E 0x0E
|
|
||||||
#define OPCODE_SET_DECODING_MAP 0x0F
|
|
||||||
#define OPCODE_UNKNOWN_10 0x10
|
|
||||||
#define OPCODE_VIDEO_DATA 0x11
|
|
||||||
#define OPCODE_UNKNOWN_12 0x12
|
|
||||||
#define OPCODE_UNKNOWN_13 0x13
|
|
||||||
#define OPCODE_UNKNOWN_14 0x14
|
|
||||||
#define OPCODE_UNKNOWN_15 0x15
|
|
||||||
|
|
||||||
#define PALETTE_COUNT 256
|
|
||||||
|
|
||||||
#endif
|
|
Loading…
Reference in a new issue