aboutsummaryrefslogtreecommitdiff
path: root/engines/sword1/credits.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/sword1/credits.cpp')
-rw-r--r--engines/sword1/credits.cpp346
1 files changed, 346 insertions, 0 deletions
diff --git a/engines/sword1/credits.cpp b/engines/sword1/credits.cpp
new file mode 100644
index 0000000000..117430b96f
--- /dev/null
+++ b/engines/sword1/credits.cpp
@@ -0,0 +1,346 @@
+/* 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 "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/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) {
+ AudioStream *bgSoundStream = 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);
+ _system->updateScreen();
+
+ // 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) {
+ _system->updateScreen();
+ 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) {
+ _system->updateScreen();
+ 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);
+ _system->updateScreen();
+ 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) {
+
+ OSystem::Event event;
+ uint32 start = _system->getMillis();
+ do {
+ while (_system->pollEvent(event)) {
+ switch (event.type) {
+ case OSystem::EVENT_QUIT:
+ SwordEngine::_systemVars.engineQuit = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ 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