405 lines
11 KiB
C++
405 lines
11 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include "lastexpress/sound/subtitle.h"
|
|
#include "lastexpress/sound/sound.h"
|
|
#include "lastexpress/data/archive.h"
|
|
|
|
#include "lastexpress/helpers.h"
|
|
#include "lastexpress/lastexpress.h"
|
|
|
|
#include "common/memstream.h"
|
|
#include "subtitle.h"
|
|
|
|
namespace LastExpress {
|
|
|
|
Subtitle::Subtitle(LastExpressEngine *engine, const char *filename, Slot *slot) {
|
|
_engine = engine;
|
|
|
|
memset(_filename, 0, sizeof(_filename));
|
|
_slot = slot;
|
|
|
|
if (_engine->getSubtitleManager()->_subtitlesQueue) {
|
|
Subtitle *i;
|
|
for (i = _engine->getSubtitleManager()->_subtitlesQueue; i->_next; i = i->_next);
|
|
|
|
i->_next = this;
|
|
} else {
|
|
_engine->getSubtitleManager()->_subtitlesQueue = this;
|
|
}
|
|
|
|
// Original bug: the _filename was 12 chars long, but sometimes
|
|
// the sound files are a couple of characters longer. This would trigger
|
|
// a truncated string warning within sprintf_s; we have raised the size
|
|
// to be able to make it work like the original did (since it used plain sprintf).
|
|
Common::sprintf_s(_filename, sizeof(_filename), "%s.SBE", filename);
|
|
|
|
HPF *archive = _engine->getArchiveManager()->openHPF(_filename);
|
|
if (archive) {
|
|
_engine->getArchiveManager()->closeHPF(archive);
|
|
|
|
if ((_engine->getSubtitleManager()->_flags & kSubFlagLoaded) == 0) {
|
|
load();
|
|
}
|
|
} else {
|
|
_status = kSubFlagStatusKilled;
|
|
}
|
|
}
|
|
|
|
Subtitle::~Subtitle() {
|
|
if (_engine->getSubtitleManager()->_subtitlesQueue) {
|
|
if (_engine->getSubtitleManager()->_subtitlesQueue == this) {
|
|
_engine->getSubtitleManager()->_subtitlesQueue = _engine->getSubtitleManager()->_subtitlesQueue->_next;
|
|
} else {
|
|
Subtitle *next;
|
|
Subtitle *queue = _engine->getSubtitleManager()->_subtitlesQueue;
|
|
|
|
if (_engine->getSubtitleManager()->_subtitlesQueue->_next == this) {
|
|
queue->_next = _next;
|
|
} else {
|
|
do {
|
|
next = queue->_next;
|
|
if (!next)
|
|
break;
|
|
|
|
queue = queue->_next;
|
|
} while (next->_next != this);
|
|
|
|
if (queue->_next == this)
|
|
queue->_next = _next;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
if (_engine->getSubtitleManager()->_currentSubtitle == this) {
|
|
if (!_engine->shouldQuit())
|
|
_engine->getSubtitleManager()->clearSubArea();
|
|
|
|
_engine->getSubtitleManager()->_currentSubtitle = nullptr;
|
|
_engine->getSubtitleManager()->_flags = 0;
|
|
}
|
|
}
|
|
|
|
void Subtitle::load() {
|
|
HPF *archive;
|
|
|
|
archive = _engine->getArchiveManager()->openHPF(_filename);
|
|
_data = _engine->getSubtitleManager()->_subtitlesData + 1;
|
|
_engine->getSubtitleManager()->_subtitleIndex = -1;
|
|
|
|
if (archive) {
|
|
_engine->getArchiveManager()->readHPF(archive, _engine->getSubtitleManager()->_subtitlesData, archive->size);
|
|
_engine->getArchiveManager()->closeHPF(archive);
|
|
|
|
for (int i = 0; i < (archive->size * MEM_PAGE_SIZE) / 2; i++) {
|
|
_engine->getSubtitleManager()->_subtitlesData[i] = READ_LE_UINT16(&_engine->getSubtitleManager()->_subtitlesData[i]);
|
|
}
|
|
|
|
if (_engine->getSubtitleManager()->_subtitlesData[0]) {
|
|
for (int i = 0; i < _engine->getSubtitleManager()->_subtitlesData[0]; i++) {
|
|
if (!_data[1])
|
|
_data[1] = _data[_data[3] + 4 + _data[2]];
|
|
|
|
_data += _data[3] + _data[2] + 4;
|
|
}
|
|
}
|
|
|
|
_engine->getSubtitleManager()->_flags |= kSubFlagLoaded;
|
|
_engine->getSubtitleManager()->_currentSubtitle = this;
|
|
}
|
|
}
|
|
|
|
void Subtitle::update() {
|
|
int32 count = 0;
|
|
|
|
_data = _engine->getSubtitleManager()->_subtitlesData + 1;
|
|
|
|
if (_data[1] <= _slot->getTime()) {
|
|
do {
|
|
if (_engine->getSubtitleManager()->_subtitlesData[0] <= count)
|
|
break;
|
|
|
|
count++;
|
|
_data = &_data[_data[3] + 4 + _data[2]];
|
|
} while (_data[1] <= _slot->getTime());
|
|
}
|
|
|
|
if (_engine->getSubtitleManager()->_subtitlesData[0] <= count) {
|
|
_status = kSubFlagStatusKilled;
|
|
|
|
if ((_engine->getSubtitleManager()->_flags & kSubFlagDrawOnScreen) != 0)
|
|
_engine->getSubtitleManager()->clearSubArea();
|
|
} else {
|
|
if (_data[0] > _slot->getTime() || _data[1] <= _slot->getTime()) {
|
|
if ((_engine->getSubtitleManager()->_flags & kSubFlagDrawOnScreen) != 0) {
|
|
_engine->getSubtitleManager()->clearSubArea();
|
|
_engine->getSubtitleManager()->_currentSubtitle = this;
|
|
|
|
return;
|
|
}
|
|
} else if (count != _engine->getSubtitleManager()->_subtitleIndex) {
|
|
_engine->getSubtitleManager()->drawSubArea(&_data[2]);
|
|
_engine->getSubtitleManager()->_subtitleIndex = count;
|
|
_engine->getSubtitleManager()->_currentSubtitle = this;
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
_engine->getSubtitleManager()->_currentSubtitle = this;
|
|
}
|
|
|
|
void Subtitle::kill() {
|
|
_status = kSubFlagStatusKilled;
|
|
}
|
|
|
|
SubtitleManager::SubtitleManager(LastExpressEngine *engine) {
|
|
_engine = engine;
|
|
|
|
memset(_upperLineCharWidths, 0, sizeof(_upperLineCharWidths));
|
|
memset(_lowerLineCharWidths, 0, sizeof(_lowerLineCharWidths));
|
|
memset(_upperLineChars, 0, sizeof(_upperLineChars));
|
|
memset(_lowerLineChars, 0, sizeof(_lowerLineChars));
|
|
}
|
|
|
|
SubtitleManager::~SubtitleManager() {
|
|
SAFE_DELETE(_font);
|
|
}
|
|
|
|
void SubtitleManager::initSubtitles() {
|
|
HPF *archive = _engine->getArchiveManager()->openHPF("FONT.DAT");
|
|
|
|
if (archive) {
|
|
if (_font->fontData) {
|
|
free(_font->fontData);
|
|
_font->fontData = nullptr;
|
|
}
|
|
|
|
byte *fontData = (byte *)malloc(MEM_PAGE_SIZE * archive->size);
|
|
|
|
if (fontData) {
|
|
_engine->getArchiveManager()->readHPF(archive, fontData, archive->size);
|
|
_engine->getArchiveManager()->closeHPF(archive);
|
|
|
|
Common::MemoryReadStream *fontStream = new Common::MemoryReadStream(fontData, MEM_PAGE_SIZE * archive->size, DisposeAfterUse::YES);
|
|
|
|
for (int i = 0; i < 16; i++) {
|
|
_font->palette[i] = fontStream->readUint16LE();
|
|
}
|
|
|
|
for (int i = 0; i < 256; i++) {
|
|
_font->charMap[i] = fontStream->readByte();
|
|
}
|
|
|
|
for (int i = 0; i < 256; i++) {
|
|
_font->charKerning[i] = fontStream->readByte();
|
|
}
|
|
|
|
uint32 sizeOfData = MEM_PAGE_SIZE * archive->size - (16 * sizeof(uint16) + 256 + 256);
|
|
_font->fontData = (byte *)malloc(sizeOfData);
|
|
|
|
assert(_font->fontData);
|
|
|
|
for (uint i = 0; !fontStream->eos() && i < sizeOfData; i++) {
|
|
_font->fontData[i] = fontStream->readByte();
|
|
}
|
|
|
|
delete fontStream;
|
|
} else {
|
|
_font->fontData = nullptr;
|
|
}
|
|
} else {
|
|
_font->fontData = nullptr;
|
|
}
|
|
|
|
_engine->getGraphicsManager()->modifyPalette(_font->palette, 16);
|
|
}
|
|
|
|
void SubtitleManager::storeVArea(PixMap *pixels) {
|
|
if (_engine->getGraphicsManager()->acquireSurface()) {
|
|
PixMap *screenSurface = (PixMap *)((byte *)_engine->getGraphicsManager()->_screenSurface.getPixels() + 537760);
|
|
|
|
for (int i = 38; i > 0; i--) {
|
|
for (int j = 480; j > 0; j--) {
|
|
*pixels++ = *screenSurface++;
|
|
}
|
|
|
|
screenSurface += 160;
|
|
}
|
|
|
|
_engine->getGraphicsManager()->unlockSurface();
|
|
}
|
|
}
|
|
|
|
void SubtitleManager::restoreVArea(PixMap *pixels) {
|
|
if (_engine->getGraphicsManager()->acquireSurface()) {
|
|
PixMap *screenSurface = (PixMap *)((byte *)_engine->getGraphicsManager()->_screenSurface.getPixels() + 537760);
|
|
|
|
for (int i = 38; i > 0; i--) {
|
|
for (int j = 480; j > 0; j--) {
|
|
*screenSurface++ = *pixels++;
|
|
}
|
|
|
|
screenSurface += 160;
|
|
}
|
|
|
|
_engine->getGraphicsManager()->unlockSurface();
|
|
}
|
|
}
|
|
|
|
void SubtitleManager::vSubOn() {
|
|
storeVArea(_engine->getGraphicsManager()->_subtitlesBackBuffer);
|
|
|
|
if (_font->fontData[0]) {
|
|
if (_engine->getGraphicsManager()->acquireSurface()) {
|
|
PixMap *surfaceLine1 = (PixMap *)((byte *)_engine->getGraphicsManager()->_screenSurface.getPixels() + ((640 - _upperLineXStart) & ~1) + 537600);
|
|
for (int i = 0; i < _upperLineLength; ++i) {
|
|
drawChar(surfaceLine1, _upperLineChars[i]);
|
|
surfaceLine1 += _upperLineCharWidths[i];
|
|
}
|
|
|
|
PixMap *surfaceLine2 = (PixMap *)((byte *)_engine->getGraphicsManager()->_screenSurface.getPixels() + ((640 - _lowerLineXStart) & ~1) + 563200);
|
|
for (int i = 0; i < _lowerLineLength; ++i) {
|
|
drawChar(surfaceLine2, _lowerLineChars[i]);
|
|
surfaceLine2 += _lowerLineCharWidths[i];
|
|
}
|
|
|
|
_engine->getGraphicsManager()->unlockSurface();
|
|
}
|
|
}
|
|
}
|
|
|
|
void SubtitleManager::vSubOff() {
|
|
restoreVArea(_engine->getGraphicsManager()->_subtitlesBackBuffer);
|
|
}
|
|
|
|
void SubtitleManager::clearSubArea() {
|
|
_flags &= ~kSubFlagDrawOnScreen;
|
|
_engine->getGraphicsManager()->burstBox(80, 420, 480, 38);
|
|
}
|
|
|
|
void SubtitleManager::drawChar(PixMap *destBuf, uint8 whichChar) {
|
|
byte *fontPtr = &_font->fontData[(288 * _font->charMap[whichChar] >> 1) + 2];
|
|
|
|
for (int row = 0; row < 18; row++) {
|
|
for (int col = 0; col < 8; col++) {
|
|
uint8 pixelByte = *fontPtr;
|
|
|
|
uint8 upperNibble = pixelByte >> 4;
|
|
if (upperNibble != 0) {
|
|
*destBuf = _font->palette[upperNibble];
|
|
}
|
|
|
|
destBuf++;
|
|
|
|
uint8 lowerNibble = pixelByte & 0x0F;
|
|
if (lowerNibble != 0) {
|
|
*destBuf = _font->palette[lowerNibble];
|
|
}
|
|
|
|
destBuf++;
|
|
|
|
fontPtr++;
|
|
}
|
|
|
|
destBuf += 624;
|
|
}
|
|
}
|
|
|
|
void SubtitleManager::drawSubArea(uint16 *subtitleData) {
|
|
uint16 *data = subtitleData + 2;
|
|
_upperLineLength = subtitleData[0];
|
|
_lowerLineLength = subtitleData[1];
|
|
|
|
_upperLineXStart = 0;
|
|
for (int i = 0; i < _upperLineLength; i++) {
|
|
_upperLineChars[i] = *data;
|
|
_upperLineCharWidths[i] = _font->charKerning[_upperLineChars[i]] + 1;
|
|
_upperLineXStart += _font->charKerning[_upperLineChars[i]] + 1;
|
|
data++;
|
|
}
|
|
|
|
_lowerLineXStart = 0;
|
|
for (int i = 0; i < _lowerLineLength; i++) {
|
|
_lowerLineChars[i] = *data;
|
|
_lowerLineCharWidths[i] = _font->charKerning[_lowerLineChars[i]] + 1;
|
|
_lowerLineXStart += _font->charKerning[_lowerLineChars[i]] + 1;
|
|
data++;
|
|
}
|
|
|
|
_flags |= kSubFlagDrawOnScreen;
|
|
_engine->getGraphicsManager()->burstBox(80, 420, 480, 38);
|
|
}
|
|
|
|
void SubtitleManager::subThread() {
|
|
int32 maxPriority;
|
|
Subtitle *queueElement;
|
|
Subtitle *selectedSubtitle;
|
|
Slot *slot;
|
|
int32 curPriority;
|
|
|
|
maxPriority = 0;
|
|
queueElement = _subtitlesQueue;
|
|
|
|
for (selectedSubtitle = 0; queueElement; queueElement = queueElement->_next) {
|
|
slot = queueElement->_slot;
|
|
|
|
if ((slot->getStatusFlags() & kSoundFlagPlaying) == 0 ||
|
|
(slot->getStatusFlags() & kSoundFlagMute) != 0 ||
|
|
!slot->getTime() ||
|
|
((slot->getStatusFlags() & kSoundVolumeMask) < kVolume6) ||
|
|
((_engine->getNISManager()->getNISFlag() & kNisFlagSoundFade) != 0 && slot->getPriority() < 90)) {
|
|
|
|
curPriority = 0;
|
|
} else {
|
|
curPriority = slot->getPriority() + (slot->getStatusFlags() & kSoundVolumeMask);
|
|
|
|
if (_currentSubtitle == queueElement)
|
|
curPriority += 4;
|
|
}
|
|
|
|
if (maxPriority < curPriority) {
|
|
maxPriority = curPriority;
|
|
selectedSubtitle = queueElement;
|
|
}
|
|
}
|
|
|
|
if (_currentSubtitle == selectedSubtitle) {
|
|
if (selectedSubtitle)
|
|
selectedSubtitle->update();
|
|
|
|
return;
|
|
}
|
|
|
|
if ((_flags & kSubFlagDrawOnScreen) != 0)
|
|
clearSubArea();
|
|
|
|
if (selectedSubtitle) {
|
|
selectedSubtitle->load();
|
|
|
|
if (selectedSubtitle)
|
|
selectedSubtitle->update();
|
|
}
|
|
}
|
|
|
|
} // End of namespace LastExpress
|