scummvm/engines/myst3/subtitles.cpp
Bastien Bouclet 555afa8136 MYST3: Add support for the Japanese version subtitles
Iconv is used to convert from CP 932 to Unicode.
Since iconv support is not yet included in ResidualVM,
this does not work properly, but at least it does not
crash anymore.

The game does not provide the TTF font required to render
the Japanese characters. The user must provide a font
named "msgothic.ttf".
2014-09-05 07:22:48 +02:00

255 lines
6.8 KiB
C++

/* ResidualVM - A 3D game interpreter
*
* ResidualVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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 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.
*
*/
#include "engines/myst3/subtitles.h"
#include "engines/myst3/myst3.h"
#include "engines/myst3/state.h"
#ifdef USE_ICONV
#include "common/iconv.h"
#endif
#include "graphics/fontman.h"
#include "graphics/font.h"
#include "graphics/fonts/ttf.h"
namespace Myst3 {
Subtitles *Subtitles::create(Myst3Engine *vm, uint32 id) {
Subtitles *s = new Subtitles(vm);
s->loadFontSettings(1100);
if (!s->loadSubtitles(id)) {
delete s;
return 0;
}
s->loadFont();
s->createTexture();
return s;
}
Subtitles::Subtitles(Myst3Engine *vm) :
_vm(vm),
_surface(0),
_texture(0),
_frame(-1),
_font(0) {
}
Subtitles::~Subtitles() {
if (_surface) {
_surface->free();
delete _surface;
}
if (_texture) {
_vm->_gfx->freeTexture(_texture);
}
delete _font;
}
void Subtitles::loadFontSettings(int32 id) {
// Load font settings
const DirectorySubEntry *fontNums = _vm->getFileDescription("NUMB", id, 0, DirectorySubEntry::kNumMetadata);
if (!fontNums)
error("Unable to load font settings values");
_fontSize = fontNums->getMiscData(0);
_fontBold = fontNums->getMiscData(1);
_surfaceHeight = fontNums->getMiscData(2);
_singleLineTop = fontNums->getMiscData(3);
_line1Top = fontNums->getMiscData(4);
_line2Top = fontNums->getMiscData(5);
_surfaceTop = fontNums->getMiscData(6) + Renderer::kTopBorderHeight + Renderer::kFrameHeight;
_fontCharsetCode = fontNums->getMiscData(7);
// We draw the subtitles in the adequate resolution so that they are not
// scaled up. This is the scale factor of the current resolution
// compared to the original
Common::Rect screen = _vm->_gfx->viewport();
_scale = screen.width() / Renderer::kOriginalWidth;
const DirectorySubEntry *fontText = _vm->getFileDescription("TEXT", id, 0, DirectorySubEntry::kTextMetadata);
if (!fontText)
error("Unable to load font face");
_fontFace = fontText->getTextData(0);
if (_fontCharsetCode == 0) {
// No game-provided charset for the Japanese version
const DirectorySubEntry *fontCharset = _vm->getFileDescription("CHAR", id, 0, DirectorySubEntry::kRawData);
if (!fontCharset)
error("Unable to load font charset");
Common::MemoryReadStream *data = fontCharset->getData();
data->read(_charset, sizeof(_charset));
delete data;
}
}
void Subtitles::loadFont() {
#ifdef USE_FREETYPE2
Common::String ttfFile;
if (_fontFace == "Arial Narrow") {
// Use the TTF font provided by the game if TTF support is available
ttfFile = "arir67w.ttf";
} else if (_fontFace == "MS Gothic") {
// The Japanese font has to be supplied by the user
ttfFile = "msgothic.ttf";
} else {
error("Unknown subtitles font face '%s'", _fontFace.c_str());
}
Common::SeekableReadStream *s = SearchMan.createReadStreamForMember(ttfFile);
if (s) {
_font = Graphics::loadTTFFont(*s, _fontSize * _scale);
delete s;
} else {
warning("Unable to load the subtitles font '%s'", ttfFile.c_str());
}
#endif
}
bool Subtitles::loadSubtitles(int32 id) {
// Subtitles may be overridden using a variable
const DirectorySubEntry *desc;
if (_vm->_state->getMovieOverrideSubtitles()) {
id = _vm->_state->getMovieOverrideSubtitles();
_vm->_state->setMovieOverrideSubtitles(0);
desc = _vm->getFileDescription("IMGR", 100000 + id, 0, DirectorySubEntry::kText);
} else {
desc = _vm->getFileDescription(0, 100000 + id, 0, DirectorySubEntry::kText);
}
if (!desc)
return false;
Common::MemoryReadStream *crypted = desc->getData();
// Read the frames and associated text offsets
while (true) {
Phrase s;
s.frame = crypted->readUint32LE();
s.offset = crypted->readUint32LE();
if (!s.frame)
break;
_phrases.push_back(s);
}
// Read and decrypt the frames subtitles
for (uint i = 0; i < _phrases.size(); i++) {
crypted->seek(_phrases[i].offset);
uint8 key = 35;
while (true) {
uint8 c = crypted->readByte() ^ key++;
if (c >= 32 && _fontCharsetCode == 0)
c = _charset[c - 32];
if (!c)
break;
_phrases[i].string += c;
}
}
delete crypted;
return true;
}
void Subtitles::createTexture() {
// Create a surface to draw the subtitles on
// Use RGB 565 to allow use of BDF fonts
_surface = new Graphics::Surface();
_surface->create(Renderer::kOriginalWidth * _scale, _surfaceHeight * _scale, Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0));
_texture = _vm->_gfx->createTexture(_surface);
}
void Subtitles::setFrame(int32 frame) {
const Phrase *phrase = 0;
for (uint i = 0; i < _phrases.size(); i++) {
if (_phrases[i].frame > frame)
break;
phrase = &_phrases[i];
}
if (phrase == 0
|| phrase->frame == _frame)
return;
_frame = phrase->frame;
const Graphics::Font *font;
if (_font)
font = _font;
else
font = FontMan.getFontByUsage(Graphics::FontManager::kLocalizedFont);
if (!font)
error("No available font");
// Draw the new text
memset(_surface->getPixels(), 0, _surface->pitch * _surface->h);
if (_fontCharsetCode == 0) {
font->drawString(_surface, phrase->string, 0, _singleLineTop * _scale, _surface->w, 0xFFFFFFFF, Graphics::kTextAlignCenter);
} else if (_fontCharsetCode == 1) {
// The Japanese subtitles are encoded in CP 932 / Shift JIS
#ifdef USE_ICONV
Common::U32String unicode = Common::convertToU32String("cp932", phrase->string);
font->drawString(_surface, unicode, 0, _singleLineTop * _scale, _surface->w, 0xFFFFFFFF, Graphics::kTextAlignCenter);
#else
warning("Unable to display Japanese subtitles, iconv support is not compiled in.");
#endif
} else {
error("Unknown font charset code '%d', fontface '%s'", _fontCharsetCode, _fontFace.c_str());
}
// Update the texture
_texture->update(_surface);
}
void Subtitles::drawOverlay() {
Common::Rect textureRect = Common::Rect(_texture->width, _texture->height);
Common::Rect bottomBorder = Common::Rect(Renderer::kOriginalWidth, _surfaceHeight);
bottomBorder.translate(0, _surfaceTop);
_vm->_gfx->drawTexturedRect2D(bottomBorder, textureRect, _texture);
}
} // End of namespace Myst3