DIRECTOR: Refactor movie loaders to allow loading from executables

This commit is contained in:
Scott Percival 2023-01-18 01:44:24 +08:00 committed by Eugene Sandulenko
parent 05ca29629a
commit d76b359ac2
3 changed files with 138 additions and 83 deletions

View file

@ -52,12 +52,8 @@ Common::Error Window::loadInitialMovie() {
debug(0, "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n");
Common::String movie = (_vm->getGameGID() == GID_TESTALL) ? getNextMovieFromQueue().movie : _vm->getEXEName();
if (g_director->getPlatform() == Common::kPlatformWindows) {
loadEXE(movie);
} else {
probeProjector(movie);
loadMac(movie);
}
loadINIStream();
_mainArchive = openArchive(movie);
if (!_mainArchive) {
warning("Cannot open main movie");
@ -152,8 +148,10 @@ void Window::probeMacBinary(MacArchive *archive) {
_nextMovie.movie = moviePath;
warning("Replaced score name with: %s (from %s)", _nextMovie.movie.c_str(), sname.c_str());
delete _currentMovie;
_currentMovie = nullptr;
if (_currentMovie) {
delete _currentMovie;
_currentMovie = nullptr;
}
probeProjector(moviePath);
} else {
@ -193,23 +191,34 @@ void Window::probeMacBinary(MacArchive *archive) {
g_director->_allOpenResFiles.setVal(archive->getPathName(), archive);
}
Archive *Window::openMainArchive(const Common::String movie) {
debug(1, "openMainArchive(\"%s\")", movie.c_str());
Archive *Window::openArchive(const Common::String movie) {
debug(1, "openArchive(\"%s\")", movie.c_str());
_mainArchive = g_director->createArchive();
if (!_mainArchive->openFile(movie)) {
delete _mainArchive;
_mainArchive = nullptr;
warning("openMainArchive(): Could not open '%s'", movie.c_str());
return nullptr;
// If the archive is already open, don't reopen it;
// just init from the existing archive. This prevents errors that
// can happen when trying to load the same archive more than once.
if (g_director->_allOpenResFiles.contains(movie) && SearchMan.hasFile(movie)) {
return g_director->_allOpenResFiles.getVal(movie);
}
return _mainArchive;
Archive *result = nullptr;
if (g_director->getPlatform() == Common::kPlatformWindows) {
result = loadEXE(movie);
} else {
probeProjector(movie);
result = loadMac(movie);
}
if (!result) {
result = g_director->createArchive();
if (!result->openFile(movie)) {
delete result;
result = nullptr;
}
}
return result;
}
void Window::loadEXE(const Common::String movie) {
void Window::loadINIStream() {
Common::SeekableReadStream *iniStream = SearchMan.createReadStreamForMember("LINGO.INI");
if (iniStream) {
char *script = (char *)calloc(iniStream->size() + 1, 1);
@ -226,31 +235,42 @@ void Window::loadEXE(const Common::String movie) {
} else {
warning("No LINGO.INI");
}
}
Archive *Window::loadEXE(const Common::String movie) {
Common::SeekableReadStream *exeStream = SearchMan.createReadStreamForMember(Common::Path(movie, g_director->_dirSeparator));
if (!exeStream)
error("Failed to open EXE '%s'", g_director->getEXEName().c_str());
if (!exeStream) {
warning("Window::loadEXE(): Failed to open EXE '%s'", g_director->getEXEName().c_str());
return nullptr;
}
Archive *result = nullptr;
uint32 initialTag = exeStream->readUint32LE();
if (initialTag == MKTAG('R', 'I', 'F', 'X') || initialTag == MKTAG('X', 'F', 'I', 'R')) {
// we've encountered a movie saved from Director, not a projector.
loadEXERIFX(exeStream, 0);
result = loadEXERIFX(exeStream, 0);
} else if (initialTag == MKTAG('R', 'I', 'F', 'F') || initialTag == MKTAG('F', 'F', 'I', 'R')) { // This is just a normal movie
_mainArchive = new RIFFArchive();
result = new RIFFArchive();
if (!_mainArchive->openStream(exeStream, 0))
error("Failed to load RIFF");
if (!result->openStream(exeStream, 0)) {
warning("Window::loadEXE(): Failed to load RIFF");
delete result;
return nullptr;
}
} else {
Common::WinResources *exe = Common::WinResources::createFromEXE(movie);
if (!exe)
error("Failed to open EXE '%s'", g_director->getEXEName().c_str());
if (!exe) {
warning("Window::loadEXE(): Failed to open EXE '%s'", g_director->getEXEName().c_str());
return nullptr;
}
const Common::Array<Common::WinResourceID> versions = exe->getIDList(Common::kWinVersion);
for (uint i = 0; i < versions.size(); i++) {
Common::WinResources::VersionInfo *info = exe->getVersionResource(versions[i]);
for (Common::WinResources::VersionHash::const_iterator it = info->hash.begin(); it != info->hash.end(); ++it)
warning("info <%s>: <%s>", it->_key.c_str(), it->_value.encode().c_str());
warning("Window::loadEXE(): info <%s>: <%s>", it->_key.c_str(), it->_value.encode().c_str());
delete info;
@ -268,23 +288,25 @@ void Window::loadEXE(const Common::String movie) {
exeStream->seek(exeStream->readUint32LE());
if (g_director->getVersion() >= 700) {
loadEXEv7(exeStream);
result = loadEXEv7(exeStream);
} else if (g_director->getVersion() >= 500) {
loadEXEv5(exeStream);
result = loadEXEv5(exeStream);
} else if (g_director->getVersion() >= 400) {
loadEXEv4(exeStream);
result = loadEXEv4(exeStream);
} else if (g_director->getVersion() >= 200) {
loadEXEv3(exeStream);
result = loadEXEv3(exeStream);
} else {
error("Unhandled Windows EXE version %d", g_director->getVersion());
warning("Window::loadEXE(): Unhandled Windows EXE version %d", g_director->getVersion());
return nullptr;
}
}
if (_mainArchive)
_mainArchive->setPathName(movie);
if (result)
result->setPathName(movie);
return result;
}
void Window::loadEXEv3(Common::SeekableReadStream *stream) {
Archive *Window::loadEXEv3(Common::SeekableReadStream *stream) {
uint32 mmmSize = 0;
Common::String mmmFileName;
Common::String directoryName;
@ -308,12 +330,12 @@ void Window::loadEXEv3(Common::SeekableReadStream *stream) {
directoryName = directoryName_;
} else {
if (!SearchMan.hasFile(Common::Path(mmmFileName_, g_director->_dirSeparator)))
warning("Failed to find MMM '%s'", mmmFileName_.c_str());
warning("Window::loadEXEv3(): Failed to find MMM '%s'", mmmFileName_.c_str());
else {
Common::SeekableReadStream *const mmmFile_ = SearchMan.createReadStreamForMember(Common::Path(mmmFileName_, g_director->_dirSeparator));
uint32 mmmFileSize_ = mmmFile_->size();
if (mmmSize_ != mmmFileSize_)
warning("File size for '%s' doesn't match. Got %d (0x%x), want %d (0x%x)", mmmFileName_.c_str(), mmmFileSize_, mmmFileSize_, mmmSize_, mmmSize_);
warning("Window::loadEXEv3(): File size for '%s' doesn't match. Got %d (0x%x), want %d (0x%x)", mmmFileName_.c_str(), mmmFileSize_, mmmFileSize_, mmmSize_, mmmSize_);
delete mmmFile_;
}
}
@ -321,6 +343,7 @@ void Window::loadEXEv3(Common::SeekableReadStream *stream) {
debugC(1, kDebugLoading, "%s", "");
}
Archive *result = nullptr;
if (mmmSize) {
uint32 riffOffset = stream->pos();
@ -347,25 +370,34 @@ void Window::loadEXEv3(Common::SeekableReadStream *stream) {
}
_mainArchive = new RIFFArchive();
result = new RIFFArchive();
if (_mainArchive->openStream(stream, riffOffset))
return;
if (result->openStream(stream, riffOffset))
return result;
warning("Failed to load RIFF from EXE");
delete _mainArchive;
_mainArchive = nullptr;
warning("Window::loadEXEv3(): Failed to load RIFF from EXE");
delete result;
result = nullptr;
delete stream;
}
openMainArchive(mmmFileName);
result = g_director->createArchive();
if (!result->openFile(mmmFileName)) {
warning("Window::loadEXEv3(): Could not open '%s'", mmmFileName.c_str());
delete result;
result = nullptr;
}
return result;
}
void Window::loadEXEv4(Common::SeekableReadStream *stream) {
Archive *Window::loadEXEv4(Common::SeekableReadStream *stream) {
uint32 ver = stream->readUint32BE();
if (ver != MKTAG('P', 'J', '9', '3'))
error("Invalid projector tag found in v4 EXE [%s]", tag2str(ver));
if (ver != MKTAG('P', 'J', '9', '3')) {
warning("Window::loadEXEv4(): Invalid projector tag found in v4 EXE [%s]", tag2str(ver));
return nullptr;
}
uint32 rifxOffset = stream->readUint32LE();
/* uint32 fontMapOffset = */ stream->readUint32LE();
@ -376,16 +408,18 @@ void Window::loadEXEv4(Common::SeekableReadStream *stream) {
/* uint32 rifxOffsetAlt = */ stream->readUint32LE(); // equivalent to rifxOffset
uint32 flags = stream->readUint32LE();
warning("PJ93 projector flags: %08x", flags);
warning("Window::loadEXEv4(): PJ93 projector flags: %08x", flags);
loadEXERIFX(stream, rifxOffset);
return loadEXERIFX(stream, rifxOffset);
}
void Window::loadEXEv5(Common::SeekableReadStream *stream) {
Archive *Window::loadEXEv5(Common::SeekableReadStream *stream) {
uint32 ver = stream->readUint32LE();
if (ver != MKTAG('P', 'J', '9', '5'))
error("Invalid projector tag found in v5 EXE [%s]", tag2str(ver));
if (ver != MKTAG('P', 'J', '9', '5')) {
warning("Window::loadEXEv5(): Invalid projector tag found in v5 EXE [%s]", tag2str(ver));
return nullptr;
}
uint32 rifxOffset = stream->readUint32LE();
uint32 pflags = stream->readUint32LE();
@ -398,16 +432,18 @@ void Window::loadEXEv5(Common::SeekableReadStream *stream) {
stream->readUint32LE(); // number of driver files
stream->readUint32LE(); // fontMapOffset
warning("PJ95 projector pflags: %08x flags: %08x", pflags, flags);
warning("Window::loadEXEv5(): PJ95 projector pflags: %08x flags: %08x", pflags, flags);
loadEXERIFX(stream, rifxOffset);
return loadEXERIFX(stream, rifxOffset);
}
void Window::loadEXEv7(Common::SeekableReadStream *stream) {
Archive *Window::loadEXEv7(Common::SeekableReadStream *stream) {
uint32 ver = stream->readUint32LE();
if (ver != MKTAG('P', 'J', '0', '0') && ver != MKTAG('P', 'J', '0', '1'))
error("Invalid projector tag found in v7 EXE [%s]", tag2str(ver));
if (ver != MKTAG('P', 'J', '0', '0') && ver != MKTAG('P', 'J', '0', '1')) {
warning("Window::loadEXEv7(): Invalid projector tag found in v7 EXE [%s]", tag2str(ver));
return nullptr;
}
uint32 rifxOffset = stream->readUint32LE();
stream->readUint32LE(); // unknown
@ -416,27 +452,40 @@ void Window::loadEXEv7(Common::SeekableReadStream *stream) {
stream->readUint32LE(); // unknown
stream->readUint32LE(); // some DLL offset
loadEXERIFX(stream, rifxOffset);
return loadEXERIFX(stream, rifxOffset);
}
void Window::loadEXERIFX(Common::SeekableReadStream *stream, uint32 offset) {
_mainArchive = new RIFXArchive();
Archive *Window::loadEXERIFX(Common::SeekableReadStream *stream, uint32 offset) {
Archive *result = new RIFXArchive();
if (!_mainArchive->openStream(stream, offset))
error("Failed to load RIFX from EXE");
if (!result->openStream(stream, offset)) {
warning("Window::loadEXERIFX(): Failed to load RIFX from EXE");
delete result;
result = nullptr;
}
return result;
}
void Window::loadMac(const Common::String movie) {
Archive *Window::loadMac(const Common::String movie) {
Archive *result = nullptr;
if (g_director->getVersion() < 400) {
// The data is part of the resource fork of the executable
openMainArchive(movie);
result = g_director->createArchive();
if (!result->openFile(movie)) {
delete result;
result = nullptr;
warning("Window::loadMac(): Could not open '%s'", movie.c_str());
}
} else {
// The RIFX is located in the data fork of the executable
Common::SeekableReadStream *dataFork = Common::MacResManager::openFileOrDataFork(Common::Path(movie, g_director->_dirSeparator));
if (!dataFork)
error("Failed to open Mac binary '%s'", movie.c_str());
_mainArchive = new RIFXArchive();
_mainArchive->setPathName(movie);
if (!dataFork) {
warning("Window::loadMac(): Failed to open Mac binary '%s'", movie.c_str());
return nullptr;
}
result = new RIFXArchive();
result->setPathName(movie);
// First we need to detect PPC vs. 68k
@ -451,12 +500,17 @@ void Window::loadMac(const Common::String movie) {
startOffset = 0;
}
if (!_mainArchive->openStream(dataFork, startOffset)) {
warning("Failed to load RIFX from Mac binary");
delete _currentMovie;
_currentMovie = nullptr;
if (!result->openStream(dataFork, startOffset)) {
warning("Window::loadMac(): Failed to load RIFX from Mac binary");
delete result;
result = nullptr;
if (_currentMovie) {
delete _currentMovie;
_currentMovie = nullptr;
}
}
}
return result;
}
void Window::loadStartMovieXLibs() {

View file

@ -313,7 +313,7 @@ bool Window::loadNextMovie() {
delete _currentMovie;
_currentMovie = nullptr;
Archive *mov = openMainArchive(_currentPath + Common::lastPathComponent(_nextMovie.movie, g_director->_dirSeparator));
Archive *mov = openArchive(_currentPath + Common::lastPathComponent(_nextMovie.movie, g_director->_dirSeparator));
if (!mov)
return false;

View file

@ -171,14 +171,15 @@ public:
Common::Error loadInitialMovie();
void probeProjector(const Common::String &movie);
void probeMacBinary(MacArchive *archive);
Archive *openMainArchive(const Common::String movie);
void loadEXE(const Common::String movie);
void loadEXEv3(Common::SeekableReadStream *stream);
void loadEXEv4(Common::SeekableReadStream *stream);
void loadEXEv5(Common::SeekableReadStream *stream);
void loadEXEv7(Common::SeekableReadStream *stream);
void loadEXERIFX(Common::SeekableReadStream *stream, uint32 offset);
void loadMac(const Common::String movie);
void loadINIStream();
Archive *openArchive(const Common::String movie);
Archive *loadEXE(const Common::String movie);
Archive *loadEXEv3(Common::SeekableReadStream *stream);
Archive *loadEXEv4(Common::SeekableReadStream *stream);
Archive *loadEXEv5(Common::SeekableReadStream *stream);
Archive *loadEXEv7(Common::SeekableReadStream *stream);
Archive *loadEXERIFX(Common::SeekableReadStream *stream, uint32 offset);
Archive *loadMac(const Common::String movie);
void loadStartMovieXLibs();
// lingo/lingo-object.cpp