/* * REminiscence - Flashback interpreter * Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net) */ #include "file.h" #include "fs.h" #include "mixer.h" #include "seq_player.h" #include "systemstub.h" #include "util.h" bool SeqDemuxer::open(File *f) { _f = f; _fileSize = _f->size(); memset(_buffers, 0, sizeof(_buffers)); _frameOffset = 0; return readHeader(); } void SeqDemuxer::close() { _f = 0; for (int i = 0; i < kBuffersCount; ++i) { free(_buffers[i].data); } memset(_buffers, 0, sizeof(_buffers)); } bool SeqDemuxer::readHeader() { for (int i = 0; i < 256; i += 4) { if (_f->readUint32LE() != 0) { return false; } } for (int i = 0; i < kBuffersCount; ++i) { const int size = _f->readUint16LE(); if (size != 0) { _buffers[i].size = 0; _buffers[i].avail = size; _buffers[i].data = (uint8_t *)malloc(size); if (!_buffers[i].data) { error("Unable to allocate %d bytes for SEQ buffer %d", size, i); } } } for (int i = 1; i <= 100; ++i) { readFrameData(); } return true; } bool SeqDemuxer::readFrameData() { _frameOffset += kFrameSize; if (_frameOffset >= _fileSize) { return false; } _f->seek(_frameOffset); _audioDataOffset = _f->readUint16LE(); _paletteDataOffset = _f->readUint16LE(); uint8_t num[4]; for (int i = 0; i < 4; ++i) { num[i] = _f->readByte(); } uint16_t offsets[4]; for (int i = 0; i < 4; ++i) { offsets[i] = _f->readUint16LE(); } for (int i = 0; i < 3; ++i) { if (offsets[i] != 0) { int e = i + 1; while (e < 3 && offsets[e] == 0) { ++e; } fillBuffer(num[i + 1], offsets[i], offsets[e] - offsets[i]); } } if (num[0] != 255) { assert(num[0] < kBuffersCount); _videoData = num[0]; } else { _videoData = -1; } return !_f->ioErr(); } void SeqDemuxer::fillBuffer(int num, int offset, int size) { assert(num < kBuffersCount); _f->seek(_frameOffset + offset); assert(_buffers[num].size + size <= _buffers[num].avail); _f->read(_buffers[num].data + _buffers[num].size, size); _buffers[num].size += size; } void SeqDemuxer::clearBuffer(int num) { _buffers[num].size = 0; } void SeqDemuxer::readPalette(uint8_t *dst) { _f->seek(_frameOffset + _paletteDataOffset); _f->read(dst, 256 * 3); } void SeqDemuxer::readAudio(int16_t *dst) { _f->seek(_frameOffset + _audioDataOffset); for (int i = 0; i < kAudioBufferSize; ++i) { dst[i] = _f->readUint16BE(); } } struct BitStream { BitStream(const uint8_t *src) : _src(src) { _bits = READ_LE_UINT16(_src); _src += 2; _len = 16; } int getBits(int count) { if (count > _len) { _bits |= READ_LE_UINT16(_src) << _len; _src += 2; _len += 16; } assert(count <= _len); const int x = _bits & ((1 << count) - 1); _bits >>= count; _len -= count; return x; } int getSignedBits(int count) { const int32_t x = getBits(count); return (x << (32 - count)) >> (32 - count); } const uint8_t *_src; int _len; uint32_t _bits; }; static const uint8_t *decodeSeqOp1Helper(const uint8_t *src, uint8_t *dst, int dstSize) { int codes[64], count = 0; BitStream bs(src); for (int i = 0, sz = 0; i < 64 && sz < 64; ++i) { codes[i] = bs.getSignedBits(4); sz += ABS(codes[i]); count += 4; } src += (count + 7) / 8; for (int i = 0; i < 64 && dstSize > 0; ++i) { int len = codes[i]; if (len < 0) { len = -len; memset(dst, *src++, MIN(len, dstSize)); } else { memcpy(dst, src, MIN(len, dstSize)); src += len; } dst += len; dstSize -= len; } return src; } static const uint8_t *decodeSeqOp1(uint8_t *dst, int pitch, const uint8_t *src) { const int len = *src++; if (len & 0x80) { uint8_t buf[8 * 8]; switch (len & 3) { case 1: src = decodeSeqOp1Helper(src, buf, sizeof(buf)); for (int y = 0; y < 8; ++y) { memcpy(dst, buf + y * 8, 8); dst += pitch; } break; case 2: src = decodeSeqOp1Helper(src, buf, sizeof(buf)); for (int i = 0; i < 8; i++) { for (int y = 0; y < 8; ++y) { dst[y * pitch] = buf[i * 8 + y]; } ++dst; } break; } } else { static const uint8_t log2_16[] = { 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3 }; BitStream bs(src + len); assert(len <= 16); const int bits = log2_16[len - 1] + 1; for (int y = 0; y < 8; ++y) { for (int x = 0; x < 8; ++x) { dst[y * pitch + x] = src[bs.getBits(bits)]; } } src += len + bits * 8; } return src; } static const uint8_t *decodeSeqOp2(uint8_t *dst, int pitch, const uint8_t *src) { for (int y = 0; y < 8; ++y) { memcpy(dst + y * pitch, src, 8); src += 8; } return src; } static const uint8_t *decodeSeqOp3(uint8_t *dst, int pitch, const uint8_t *src) { int pos; do { pos = *src++; const int offset = ((pos >> 3) & 7) * pitch + (pos & 7); dst[offset] = *src++; } while ((pos & 0x80) == 0); return src; } SeqPlayer::SeqPlayer(SystemStub *stub, Mixer *mixer) : _stub(stub), _buf(0), _mix(mixer) { _soundQueuePreloadSize = 0; _soundQueue = 0; _soundQueueTail = 0; } SeqPlayer::~SeqPlayer() { } void SeqPlayer::play(File *f) { if (_demux.open(f)) { uint8_t palette[256 * 3]; _stub->getPalette(palette, 256); _mix->setPremixHook(mixCallback, this); memset(_buf, 0, 256 * 224); bool clearScreen = true; while (true) { const uint32_t nextFrameTimeStamp = _stub->getTimeStamp() + 1000 / 25; _stub->processEvents(); if (_stub->_pi.quit || _stub->_pi.backspace) { _stub->_pi.backspace = false; break; } if (!_demux.readFrameData()) { break; } if (_demux._audioDataOffset != 0) { SoundBufferQueue *sbq = (SoundBufferQueue *)malloc(sizeof(SoundBufferQueue)); if (sbq) { sbq->data = (int16_t *)calloc(SeqDemuxer::kAudioBufferSize, sizeof(int16_t)); if (sbq->data) { _demux.readAudio(sbq->data); sbq->size = SeqDemuxer::kAudioBufferSize; sbq->read = 0; sbq->next = 0; } else { free(sbq); sbq = 0; } } if (sbq) { LockAudioStack las(_stub); if (_soundQueueTail) { _soundQueueTail->next = sbq; } else { assert(!_soundQueue); _soundQueue = sbq; } _soundQueueTail = sbq; if (_soundQueuePreloadSize < kSoundPreloadSize) { ++_soundQueuePreloadSize; } } } if (_demux._paletteDataOffset != 0) { uint8_t buf[256 * 3]; _demux.readPalette(buf); for (int i = 0; i < 256 * 3; ++i) { buf[i] = (buf[i] << 2) | (buf[i] >> 4); } _stub->setPalette(buf, 256); } if (_demux._videoData != -1) { const int y0 = (224 - kVideoHeight) / 2; const uint8_t *src = _demux._buffers[_demux._videoData].data; _demux.clearBuffer(_demux._videoData); BitStream bs(src); src += 128; for (int y = 0; y < kVideoHeight; y += 8) { for (int x = 0; x < kVideoWidth; x += 8) { const int offset = (y0 + y) * 256 + x; switch (bs.getBits(2)) { case 1: src = decodeSeqOp1(_buf + offset, 256, src); break; case 2: src = decodeSeqOp2(_buf + offset, 256, src); break; case 3: src = decodeSeqOp3(_buf + offset, 256, src); break; } } } if (clearScreen) { clearScreen = false; _stub->copyRect(0, 0, kVideoWidth, 224, _buf, 256); } else { _stub->copyRect(0, y0, kVideoWidth, kVideoHeight, _buf, 256); } _stub->updateScreen(0); } const int diff = nextFrameTimeStamp - _stub->getTimeStamp(); if (diff > 0) { _stub->sleep(diff); } } // restore level palette _stub->setPalette(palette, 256); _mix->setPremixHook(0, 0); _demux.close(); // flush sound queue LockAudioStack las(_stub); while (_soundQueue) { SoundBufferQueue *next = _soundQueue->next; free(_soundQueue->data); free(_soundQueue); _soundQueue = next; } _soundQueueTail = 0; _soundQueuePreloadSize = 0; } } bool SeqPlayer::mix(int16_t *buf, int samples) { if (_soundQueuePreloadSize < kSoundPreloadSize) { return true; } while (_soundQueue && samples > 0) { const int count = MIN(samples, _soundQueue->size - _soundQueue->read); memcpy(buf, _soundQueue->data + _soundQueue->read, count * sizeof(int16_t)); buf += count; _soundQueue->read += count; if (_soundQueue->read == _soundQueue->size) { SoundBufferQueue *next = _soundQueue->next; free(_soundQueue->data); free(_soundQueue); _soundQueue = next; } samples -= count; } if (!_soundQueue) { _soundQueueTail = 0; } return true; } bool SeqPlayer::mixCallback(void *param, int16_t *buf, int len) { return ((SeqPlayer *)param)->mix(buf, len); }