mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-21 17:29:39 +00:00
ed54ea9155
svn-id: r26180
348 lines
10 KiB
C++
348 lines
10 KiB
C++
/* ScummVM - Scumm Interpreter
|
|
* Copyright (C) 2003-2006 The ScummVM project
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* $URL$
|
|
* $Id$
|
|
*
|
|
*/
|
|
|
|
#include "common/stdafx.h"
|
|
#include "common/endian.h"
|
|
|
|
#include "sword1/credits.h"
|
|
#include "sword1/screen.h"
|
|
#include "sword1/sword1.h"
|
|
|
|
#include "sound/audiostream.h"
|
|
#include "sound/mixer.h"
|
|
|
|
#include "common/file.h"
|
|
#include "common/util.h"
|
|
#include "common/events.h"
|
|
#include "common/system.h"
|
|
|
|
|
|
#define CREDITS_X 480
|
|
#define CREDITS_Y 300
|
|
#define BUFSIZE_Y 640
|
|
|
|
#define START_X ((640 - CREDITS_X) / 2)
|
|
#define START_Y ((480 - CREDITS_Y) / 2)
|
|
|
|
#define SCROLL_TIMING (2000 / 59) // 29.5 frames per second
|
|
|
|
#define LOGO_FADEUP_TIME (133 * 1000)
|
|
#define LOGO_FADEDOWN_TIME (163 * 1000)
|
|
|
|
namespace Sword1 {
|
|
|
|
enum {
|
|
FONT_PAL = 0,
|
|
FONT,
|
|
TEXT,
|
|
REVO_PAL,
|
|
REVO_LOGO,
|
|
F_EOF
|
|
};
|
|
|
|
enum {
|
|
FNT_LFT = 0, // left column
|
|
FNT_RGT, // right column
|
|
FNT_CEN, // centered
|
|
FNT_BIG = 64, // big font
|
|
FNT_EOL = 128, // linebreak
|
|
FNT_EOB = 255 // end of textblock
|
|
};
|
|
|
|
|
|
CreditsPlayer::CreditsPlayer(OSystem *pSystem, Audio::Mixer *pMixer) {
|
|
_system = pSystem;
|
|
_mixer = pMixer;
|
|
_smlFont = _bigFont = NULL;
|
|
}
|
|
|
|
bool spaceInBuf(uint16 blitSta, uint16 blitEnd, uint16 renderDest) {
|
|
if (blitEnd > blitSta) {
|
|
if ((renderDest > blitEnd) || (renderDest + 15 < blitSta))
|
|
return true;
|
|
} else {
|
|
if ((renderDest > blitEnd) && (renderDest + 15 < blitSta))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void CreditsPlayer::play(void) {
|
|
Audio::AudioStream *bgSoundStream = Audio::AudioStream::openStreamFile("credits");
|
|
if (bgSoundStream == NULL) {
|
|
warning("\"credits.ogg\" not found, skipping credits sequence");
|
|
return;
|
|
}
|
|
ArcFile credFile;
|
|
if (!credFile.open("credits.dat")) {
|
|
warning("\"credits.dat\" not found, skipping credits sequence");
|
|
return;
|
|
}
|
|
|
|
uint8 *palSrc = credFile.fetchFile(FONT_PAL, &_palLen);
|
|
for (uint32 cnt = 0; cnt < _palLen; cnt++)
|
|
_palette[(cnt / 3) * 4 + cnt % 3] = palSrc[cnt];
|
|
_palLen /= 3;
|
|
|
|
generateFonts(&credFile);
|
|
|
|
uint8 *textData = credFile.fetchFile(TEXT);
|
|
textData += READ_LE_UINT32(textData + SwordEngine::_systemVars.language * 4);
|
|
|
|
uint8 *screenBuf = (uint8*)malloc(CREDITS_X * BUFSIZE_Y);
|
|
memset(screenBuf, 0, CREDITS_X * BUFSIZE_Y);
|
|
_system->copyRectToScreen(screenBuf, 640, 0, 0, 640, 480);
|
|
_system->setPalette(_palette, 0, _palLen);
|
|
|
|
// everything's initialized, time to render and show the credits.
|
|
Audio::SoundHandle bgSound;
|
|
_mixer->playInputStream(Audio::Mixer::kMusicSoundType, &bgSound, bgSoundStream, 0);
|
|
|
|
int relDelay = 0;
|
|
uint16 scrollY = 0;
|
|
uint16 renderY = BUFSIZE_Y / 2;
|
|
uint16 clearY = 0xFFFF;
|
|
bool clearLine = false;
|
|
while (((*textData != FNT_EOB) || (scrollY != renderY)) && !SwordEngine::_systemVars.engineQuit) {
|
|
if ((int32)_mixer->getSoundElapsedTime(bgSound) - relDelay < (SCROLL_TIMING * 2)) { // sync to audio
|
|
if (scrollY < BUFSIZE_Y - CREDITS_Y)
|
|
_system->copyRectToScreen(screenBuf + scrollY * CREDITS_X, CREDITS_X, START_X, START_Y, CREDITS_X, CREDITS_Y);
|
|
else {
|
|
_system->copyRectToScreen(screenBuf + scrollY * CREDITS_X, CREDITS_X, START_X, START_Y, CREDITS_X, BUFSIZE_Y - scrollY);
|
|
_system->copyRectToScreen(screenBuf, CREDITS_X, START_X, START_Y + BUFSIZE_Y - scrollY, CREDITS_X, CREDITS_Y - (BUFSIZE_Y - scrollY));
|
|
}
|
|
_system->updateScreen();
|
|
} else
|
|
warning("frame skipped");
|
|
|
|
while (spaceInBuf(scrollY, (scrollY + CREDITS_Y) % BUFSIZE_Y, renderY) && (*textData != FNT_EOB)) {
|
|
if (*textData & FNT_EOL) {
|
|
renderY = (renderY + 16) % BUFSIZE_Y; // linebreak
|
|
clearLine = true;
|
|
*textData &= ~FNT_EOL;
|
|
}
|
|
if (spaceInBuf(scrollY, (scrollY + CREDITS_Y) % BUFSIZE_Y, renderY)) {
|
|
if (clearLine)
|
|
memset(screenBuf + renderY * CREDITS_X, 0, 16 * CREDITS_X);
|
|
clearLine = false;
|
|
renderLine(screenBuf, textData + 1, renderY, *textData);
|
|
if (*textData & FNT_BIG)
|
|
renderY += 16;
|
|
while (*++textData != 0) // search for the start of next string
|
|
;
|
|
textData++;
|
|
}
|
|
if (*textData == FNT_EOB)
|
|
clearY = renderY;
|
|
}
|
|
if ((*textData == FNT_EOB) && spaceInBuf(scrollY, (scrollY + CREDITS_Y) % BUFSIZE_Y, clearY)) {
|
|
memset(screenBuf + clearY * CREDITS_X, 0, 16 * CREDITS_X);
|
|
clearY = (clearY + 16) % BUFSIZE_Y;
|
|
}
|
|
|
|
relDelay += SCROLL_TIMING;
|
|
delay(relDelay - (int32)_mixer->getSoundElapsedTime(bgSound));
|
|
scrollY = (scrollY + 1) % BUFSIZE_Y;
|
|
}
|
|
free(_smlFont);
|
|
free(_bigFont);
|
|
_smlFont = _bigFont = NULL;
|
|
free(screenBuf);
|
|
|
|
// credits done, now show the revolution logo
|
|
uint8 *revoBuf = credFile.decompressFile(REVO_LOGO);
|
|
uint8 *revoPal = credFile.fetchFile(REVO_PAL, &_palLen);
|
|
_palLen /= 3;
|
|
while ((_mixer->getSoundElapsedTime(bgSound) < LOGO_FADEUP_TIME) && !SwordEngine::_systemVars.engineQuit) {
|
|
delay(100);
|
|
}
|
|
memset(_palette, 0, 256 * 4);
|
|
_system->setPalette(_palette, 0, 256);
|
|
_system->copyRectToScreen(revoBuf, 480, START_X, START_Y, CREDITS_X, CREDITS_Y);
|
|
_system->updateScreen();
|
|
|
|
fadePalette(revoPal, true, _palLen);
|
|
while ((_mixer->getSoundElapsedTime(bgSound) < LOGO_FADEDOWN_TIME) && !SwordEngine::_systemVars.engineQuit) {
|
|
delay(100);
|
|
}
|
|
fadePalette(revoPal, false, _palLen);
|
|
delay(3000);
|
|
|
|
if (SwordEngine::_systemVars.engineQuit)
|
|
_mixer->stopAll();
|
|
free(revoBuf);
|
|
}
|
|
|
|
void CreditsPlayer::fadePalette(uint8 *srcPal, bool fadeup, uint16 len) {
|
|
int8 fadeDir = fadeup ? 1 : -1;
|
|
int fadeStart = fadeup ? 0 : 12;
|
|
|
|
int relDelay = _system->getMillis();
|
|
for (int fadeStep = fadeStart; (fadeStep >= 0) && (fadeStep <= 12) && !SwordEngine::_systemVars.engineQuit; fadeStep += fadeDir) {
|
|
for (uint16 cnt = 0; cnt < len * 3; cnt++)
|
|
_palette[(cnt / 3) * 4 + (cnt % 3)] = (srcPal[cnt] * fadeStep) / 12;
|
|
_system->setPalette(_palette, 0, 256);
|
|
relDelay += 1000 / 12;
|
|
delay(relDelay - _system->getMillis());
|
|
}
|
|
}
|
|
|
|
void CreditsPlayer::renderLine(uint8 *screenBuf, uint8 *line, uint16 yBufPos, uint8 flags) {
|
|
uint8 *font;
|
|
uint16 fntSize = 16;
|
|
if (flags & FNT_BIG) {
|
|
font = _bigFont;
|
|
fntSize = 32;
|
|
flags &= ~FNT_BIG;
|
|
} else
|
|
font = _smlFont;
|
|
|
|
uint16 width = getWidth(font, line);
|
|
uint16 xBufPos = (flags == FNT_CEN) ? (CREDITS_X - width) / 2 : ((flags == FNT_LFT) ? (234 - width) : 255);
|
|
uint8 *bufDest = screenBuf + yBufPos * CREDITS_X + xBufPos;
|
|
while (*line) {
|
|
uint8 *chrSrc = font + _numChars + (*line - 1) * fntSize * fntSize;
|
|
for (uint16 cnty = 0; cnty < fntSize; cnty++) {
|
|
for (uint16 cntx = 0; cntx < fntSize; cntx++)
|
|
bufDest[cnty * CREDITS_X + cntx] = chrSrc[cntx];
|
|
chrSrc += fntSize;
|
|
}
|
|
bufDest += font[*line++ - 1];
|
|
}
|
|
}
|
|
|
|
uint16 CreditsPlayer::getWidth(uint8 *font, uint8 *line) {
|
|
uint16 width = 0;
|
|
while (*line)
|
|
width += font[*line++ - 1];
|
|
return width;
|
|
}
|
|
|
|
void CreditsPlayer::generateFonts(ArcFile *arcFile) {
|
|
_bigFont = arcFile->decompressFile(FONT);
|
|
_numChars = *_bigFont;
|
|
memmove(_bigFont, _bigFont + 1, _numChars * (32 * 32 + 1));
|
|
_smlFont = (uint8*)malloc(_numChars * (32 * 32 + 1));
|
|
uint8 *src = _bigFont + _numChars;
|
|
uint8 *dst = _smlFont + _numChars;
|
|
for (uint16 cnt = 0; cnt < _numChars; cnt++) {
|
|
_smlFont[cnt] = (_bigFont[cnt]++ + 1) / 2; // width table
|
|
for (uint16 cnty = 0; cnty < 16; cnty++) {
|
|
for (uint16 cntx = 0; cntx < 16; cntx++) {
|
|
uint8 resR = (uint8)((_palette[src[0] * 4 + 0] + _palette[src[1] * 4 + 0] + _palette[src[32] * 4 + 0] + _palette[src[33] * 4 + 0]) >> 2);
|
|
uint8 resG = (uint8)((_palette[src[0] * 4 + 1] + _palette[src[1] * 4 + 1] + _palette[src[32] * 4 + 1] + _palette[src[33] * 4 + 1]) >> 2);
|
|
uint8 resB = (uint8)((_palette[src[0] * 4 + 2] + _palette[src[1] * 4 + 2] + _palette[src[32] * 4 + 2] + _palette[src[33] * 4 + 2]) >> 2);
|
|
*dst++ = getPalIdx(resR, resG, resB);
|
|
src += 2;
|
|
}
|
|
src += 32;
|
|
}
|
|
}
|
|
}
|
|
|
|
uint8 CreditsPlayer::getPalIdx(uint8 r, uint8 g, uint8 b) {
|
|
for (uint16 cnt = 0; cnt < _palLen; cnt++)
|
|
if ((_palette[cnt * 4 + 0] == r) && (_palette[cnt * 4 + 1] == g) && (_palette[cnt * 4 + 2] == b))
|
|
return (uint8)cnt;
|
|
assert(_palLen < 256);
|
|
_palette[_palLen * 4 + 0] = r;
|
|
_palette[_palLen * 4 + 1] = g;
|
|
_palette[_palLen * 4 + 2] = b;
|
|
return (uint8)_palLen++;
|
|
}
|
|
|
|
void CreditsPlayer::delay(int msecs) {
|
|
|
|
Common::Event event;
|
|
uint32 start = _system->getMillis();
|
|
do {
|
|
Common::EventManager *eventMan = _system->getEventManager();
|
|
while (eventMan->pollEvent(event)) {
|
|
switch (event.type) {
|
|
case Common::EVENT_QUIT:
|
|
SwordEngine::_systemVars.engineQuit = true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
_system->updateScreen();
|
|
|
|
if (msecs > 0)
|
|
_system->delayMillis(10);
|
|
|
|
} while ((_system->getMillis() < start + msecs) && !SwordEngine::_systemVars.engineQuit);
|
|
}
|
|
|
|
ArcFile::ArcFile(void) {
|
|
_buf = NULL;
|
|
}
|
|
|
|
ArcFile::~ArcFile(void) {
|
|
if (_buf)
|
|
free(_buf);
|
|
}
|
|
|
|
bool ArcFile::open(const char *name) {
|
|
Common::File arc;
|
|
if (!arc.open(name))
|
|
return false;
|
|
_bufPos = _buf = (uint8*)malloc(arc.size());
|
|
arc.read(_buf, arc.size());
|
|
arc.close();
|
|
return true;
|
|
}
|
|
|
|
void ArcFile::enterPath(uint32 id) {
|
|
_bufPos += READ_LE_UINT32(_bufPos + id * 4);
|
|
}
|
|
|
|
uint8 *ArcFile::fetchFile(uint32 fileId, uint32 *size) {
|
|
if (size)
|
|
*size = READ_LE_UINT32(_bufPos + (fileId + 1) * 4) - READ_LE_UINT32(_bufPos + fileId * 4);
|
|
return _bufPos + READ_LE_UINT32(_bufPos + fileId * 4);
|
|
}
|
|
|
|
uint8 *ArcFile::decompressFile(uint32 fileId) {
|
|
uint32 size;
|
|
uint8 *srcBuf = fetchFile(fileId, &size);
|
|
uint8 *dstBuf = (uint8*)malloc(READ_LE_UINT32(srcBuf));
|
|
uint8 *srcPos = srcBuf + 4;
|
|
uint8 *dstPos = dstBuf;
|
|
while (srcPos < srcBuf + size) {
|
|
uint16 len = READ_LE_UINT16(srcPos);
|
|
memset(dstPos, 0, len);
|
|
dstPos += len;
|
|
srcPos += 2;
|
|
if (srcPos < srcBuf + size) {
|
|
len = *srcPos++;
|
|
memcpy(dstPos, srcPos, len);
|
|
dstPos += len;
|
|
srcPos += len;
|
|
}
|
|
}
|
|
return dstBuf;
|
|
}
|
|
|
|
} // end of namespace Sword1
|