ULTIMA8: Add Crusader subtitle support

This commit is contained in:
Matthew Duggan 2021-04-04 20:23:17 +09:00
parent 5b5f134be7
commit 72e8f4de0b
4 changed files with 102 additions and 16 deletions

View File

@ -124,5 +124,9 @@ void AVIPlayer::run() {
} }
} }
int AVIPlayer::getFrameNo() const {
return _decoder->getCurFrame();
}
} // End of namespace Ultima8 } // End of namespace Ultima8
} // End of namespace Ultima } // End of namespace Ultima

View File

@ -53,6 +53,8 @@ public:
return _playing; return _playing;
} }
int getFrameNo() const;
private: private:
bool _playing; bool _playing;

View File

@ -33,6 +33,7 @@
#include "ultima/ultima8/world/get_object.h" #include "ultima/ultima8/world/get_object.h"
#include "ultima/ultima8/gumps/gump_notify_process.h" #include "ultima/ultima8/gumps/gump_notify_process.h"
#include "ultima/ultima8/gumps/cru_status_gump.h" #include "ultima/ultima8/gumps/cru_status_gump.h"
#include "ultima/ultima8/gumps/widgets/text_widget.h"
#include "ultima/ultima8/filesys/file_system.h" #include "ultima/ultima8/filesys/file_system.h"
@ -48,7 +49,7 @@ MovieGump::MovieGump() : ModalGump(), _player(nullptr) {
MovieGump::MovieGump(int width, int height, Common::SeekableReadStream *rs, MovieGump::MovieGump(int width, int height, Common::SeekableReadStream *rs,
bool introMusicHack, bool noScale, const byte *overridePal, bool introMusicHack, bool noScale, const byte *overridePal,
uint32 flags, int32 layer) uint32 flags, int32 layer)
: ModalGump(50, 50, width, height, 0, flags, layer) { : ModalGump(50, 50, width, height, 0, flags, layer), _subtitleWidget(0) {
uint32 stream_id = rs->readUint32BE(); uint32 stream_id = rs->readUint32BE();
rs->seek(-4, SEEK_CUR); rs->seek(-4, SEEK_CUR);
if (stream_id == 0x52494646) {// 'RIFF' - crusader AVIs if (stream_id == 0x52494646) {// 'RIFF' - crusader AVIs
@ -93,6 +94,24 @@ void MovieGump::run() {
ModalGump::run(); ModalGump::run();
_player->run(); _player->run();
AVIPlayer *aviplayer = dynamic_cast<AVIPlayer *>(_player);
if (aviplayer) {
const int frameno = aviplayer->getFrameNo();
if (_subtitles.contains(frameno)) {
TextWidget *subtitle = dynamic_cast<TextWidget *>(getGump(_subtitleWidget));
if (subtitle)
subtitle->Close();
// Create a new TextWidget
TextWidget *widget = new TextWidget(0, 0, _subtitles[frameno], true, 4, 640, 10);
widget->InitGump(this);
widget->setRelativePosition(BOTTOM_CENTER, 0, -10);
// Subtitles should be white.
widget->setBlendColour(0xffffffff);
_subtitleWidget = widget->getObjId();
}
}
if (!_player->isPlaying()) { if (!_player->isPlaying()) {
Close(); Close();
} }
@ -137,6 +156,36 @@ ProcId MovieGump::U8MovieViewer(Common::SeekableReadStream *rs, bool fade, bool
} }
} }
void MovieGump::loadSubtitles(Common::SeekableReadStream *rs) {
const uint32 id = rs->readUint32BE();
rs->seek(0);
if (id == 0x464F524D) { // 'FORM'
loadIFFSubs(rs);
} else {
loadTXTSubs(rs);
}
}
void MovieGump::loadTXTSubs(Common::SeekableReadStream *rs) {
int frameno = 0;
Common::String subtitles;
while (!rs->eos()) {
Common::String line = rs->readLine();
if (line.hasPrefix("@frame ")) {
frameno = atoi(line.c_str() + 7);
subtitles += '\n';
} else {
_subtitles[frameno] = line;
subtitles += line;
}
}
}
void MovieGump::loadIFFSubs(Common::SeekableReadStream *rs) {
warning("TODO: load IFF subtitle data");
}
bool MovieGump::loadData(Common::ReadStream *rs) { bool MovieGump::loadData(Common::ReadStream *rs) {
return false; return false;
} }
@ -168,22 +217,36 @@ static Std::string _fixCrusaderMovieName(const Std::string &s) {
return s; return s;
} }
static Common::SeekableReadStream *_tryLoadCruMovie(const Std::string &filename) { static Common::SeekableReadStream *_tryLoadCruMovieFile(const Std::string &filename, const char *extn) {
const Std::string path = Std::string::format("flics/%s.avi", filename.c_str()); const Std::string path = Std::string::format("flics/%s.%s", filename.c_str(), extn);
FileSystem *filesys = FileSystem::get_instance(); FileSystem *filesys = FileSystem::get_instance();
Common::SeekableReadStream *rs = filesys->ReadFile(path); Common::SeekableReadStream *rs = filesys->ReadFile(path);
if (!rs) { if (!rs) {
// Try with a "0" in the name // Try with a "0" in the name
const Std::string adjustedfn = Std::string::format("flics/0%s.avi", filename.c_str()); const Std::string adjustedfn = Std::string::format("flics/0%s.%s", filename.c_str(), extn);
rs = filesys->ReadFile(adjustedfn); rs = filesys->ReadFile(adjustedfn);
if (!rs) { if (!rs)
warning("movie %s not found", filename.c_str()); return nullptr;
return 0;
}
} }
return rs; return rs;
} }
static Common::SeekableReadStream *_tryLoadCruAVI(const Std::string &filename) {
Common::SeekableReadStream *rs = _tryLoadCruMovieFile(filename, "avi");
if (!rs)
warning("movie %s not found", filename.c_str());
return rs;
}
// Convenience function that tries to open both TXT (No Remorse)
// and IFF (No Regret) subtitle formats.
static Common::SeekableReadStream *_tryLoadCruSubtitle(const Std::string &filename) {
Common::SeekableReadStream *txtfile = _tryLoadCruMovieFile(filename, "txt");
if (txtfile)
return txtfile;
return _tryLoadCruMovieFile(filename, "iff");
}
uint32 MovieGump::I_playMovieOverlay(const uint8 *args, uint32 MovieGump::I_playMovieOverlay(const uint8 *args,
unsigned int /*argsize*/) { unsigned int /*argsize*/) {
ARG_ITEM_FROM_PTR(item); ARG_ITEM_FROM_PTR(item);
@ -201,11 +264,12 @@ uint32 MovieGump::I_playMovieOverlay(const uint8 *args,
const Palette *pal = palman->getPalette(PaletteManager::Pal_Game); const Palette *pal = palman->getPalette(PaletteManager::Pal_Game);
assert(pal); assert(pal);
Common::SeekableReadStream *rs = _tryLoadCruMovie(name); Common::SeekableReadStream *rs = _tryLoadCruAVI(name);
if (rs) { if (rs) {
Gump *gump = new MovieGump(x, y, rs, false, false, pal->_palette); MovieGump *gump = new MovieGump(x, y, rs, false, false, pal->_palette);
gump->InitGump(nullptr, true); gump->InitGump(nullptr, true);
gump->setRelativePosition(CENTER); gump->setRelativePosition(CENTER);
gump->loadSubtitles(_tryLoadCruSubtitle(name));
} }
} }
@ -219,11 +283,12 @@ uint32 MovieGump::I_playMovieCutscene(const uint8 *args, unsigned int /*argsize*
ARG_UINT16(y); ARG_UINT16(y);
if (item) { if (item) {
Common::SeekableReadStream *rs = _tryLoadCruMovie(name); Common::SeekableReadStream *rs = _tryLoadCruAVI(name);
if (rs) { if (rs) {
Gump *gump = new MovieGump(x * 3, y * 3, rs, false, false); MovieGump *gump = new MovieGump(x * 3, y * 3, rs, false, false);
gump->InitGump(nullptr, true); gump->InitGump(nullptr, true);
gump->setRelativePosition(CENTER); gump->setRelativePosition(CENTER);
gump->loadSubtitles(_tryLoadCruSubtitle(name));
} }
} }
@ -239,11 +304,12 @@ uint32 MovieGump::I_playMovieCutsceneAlt(const uint8 *args, unsigned int /*argsi
warning("MovieGump::I_playMovieCutsceneAlt: TODO: This intrinsic should pause and fade the background to grey"); warning("MovieGump::I_playMovieCutsceneAlt: TODO: This intrinsic should pause and fade the background to grey");
if (item) { if (item) {
Common::SeekableReadStream *rs = _tryLoadCruMovie(name); Common::SeekableReadStream *rs = _tryLoadCruAVI(name);
if (rs) { if (rs) {
Gump *gump = new MovieGump(x * 3, y * 3, rs, false, false); MovieGump *gump = new MovieGump(x * 3, y * 3, rs, false, false);
gump->InitGump(nullptr, true); gump->InitGump(nullptr, true);
gump->setRelativePosition(CENTER); gump->setRelativePosition(CENTER);
gump->loadSubtitles(_tryLoadCruSubtitle(name));
} }
} }
@ -256,11 +322,12 @@ uint32 MovieGump::I_playMovieCutsceneRegret(const uint8 *args, unsigned int /*ar
warning("MovieGump::I_playMovieCutsceneRegret: TODO: use fade argument %d", fade); warning("MovieGump::I_playMovieCutsceneRegret: TODO: use fade argument %d", fade);
Common::SeekableReadStream *rs = _tryLoadCruMovie(name); Common::SeekableReadStream *rs = _tryLoadCruAVI(name);
if (rs) { if (rs) {
Gump *gump = new MovieGump(640, 480, rs, false, false); MovieGump *gump = new MovieGump(640, 480, rs, false, false);
gump->InitGump(nullptr, true); gump->InitGump(nullptr, true);
gump->setRelativePosition(CENTER); gump->setRelativePosition(CENTER);
gump->loadSubtitles(_tryLoadCruSubtitle(name));
} }
return 0; return 0;

View File

@ -67,6 +67,19 @@ public:
protected: protected:
MoviePlayer *_player; MoviePlayer *_player;
// Load subtitles with format detection
void loadSubtitles(Common::SeekableReadStream *rs);
// Load subtitles from a txt file (No Remorse format)
void loadTXTSubs(Common::SeekableReadStream *rs);
// Load subtitles from a iff file (No Regret format)
void loadIFFSubs(Common::SeekableReadStream *rs);
Common::HashMap<int, Common::String> _subtitles;
uint16 _subtitleWidget;
}; };
} // End of namespace Ultima8 } // End of namespace Ultima8