diff options
-rw-r--r-- | scumm/resource.cpp | 176 | ||||
-rw-r--r-- | scumm/scumm.cpp | 21 | ||||
-rw-r--r-- | scumm/scumm.h | 40 | ||||
-rw-r--r-- | scumm/sound.cpp | 44 |
4 files changed, 247 insertions, 34 deletions
diff --git a/scumm/resource.cpp b/scumm/resource.cpp index e5e7ed9c22..315379dbc2 100644 --- a/scumm/resource.cpp +++ b/scumm/resource.cpp @@ -37,6 +37,167 @@ namespace Scumm { static uint16 newTag2Old(uint32 oldTag); static const char *resTypeFromId(int id); + + +ScummFile::ScummFile() : _encbyte(0), _subFileStart(0), _subFileLen(0) { +} + +void ScummFile::setEnc(byte value) { + _encbyte = value; +} + +void ScummFile::setSubfileRange(uint32 start, uint32 len) { + // TODO: Add sanity checks + const uint32 fileSize = File::size(); + assert(start <= fileSize); + assert(start + len <= fileSize); + _subFileStart = start; + _subFileLen = len; + seek(0, SEEK_SET); +} + +void ScummFile::resetSubfile() { + _subFileStart = 0; + _subFileLen = 0; + seek(0, SEEK_SET); +} + +bool ScummFile::open(const char *filename, AccessMode mode, const char *directory) { + if (File::open(filename, mode, directory)) { + resetSubfile(); + return true; + } else { + return false; + } +} + +bool ScummFile::openSubFile(const char *filename) { + assert(isOpen()); + + // Disable the XOR encryption and reset any current subfile range + setEnc(0); + resetSubfile(); + + // Read in the filename table and look for the specified file + + unsigned long file_off, file_len; + char file_name[0x20+1]; + unsigned long i; + + // Get the length of the data file to use for consistency checks + const uint32 data_file_len = size(); + + // Read offset and length to the file records */ + const uint32 file_record_off = readUint32BE(); + const uint32 file_record_len = readUint32BE(); + + // Do a quick check to make sure the offset and length are good + if (file_record_off + file_record_len > data_file_len) { + return false; + } + + // Do a little consistancy check on file_record_length + if (file_record_len % 0x28) { + return false; + } + + // Scan through the files + for (i = 0; i < file_record_len; i += 0x28) { + // read a file record + seek(file_record_off + i, SEEK_SET); + file_off = readUint32BE(); + file_len = readUint32BE(); + read(file_name, 0x20); + file_name[0x20] = 0; + + assert(file_name[0]); + //debug(0, "extracting \'%s\'", file_name); + + // Consistency check. make sure the file data is in the file + if (file_off + file_len > data_file_len) { + return false; + } + + if (scumm_stricmp(file_name, filename) == 0) { + // We got a match! + _subFileName = file_name; + setSubfileRange(file_off, file_len); + return true; + } + } + + return false; +} + + +bool ScummFile::eof() { + return _subFileLen ? (pos() >= _subFileLen) : File::eof(); +} + +uint32 ScummFile::pos() { + return File::pos() - _subFileStart; +} + +uint32 ScummFile::size() { + return _subFileLen ? _subFileLen : File::size(); +} + +void ScummFile::seek(int32 offs, int whence) { + if (_subFileLen) { + // Constrain the seek to the subfile + switch (whence) { + case SEEK_END: + offs = _subFileStart + _subFileLen - offs; + break; + case SEEK_SET: + offs += _subFileStart; + break; + case SEEK_CUR: + offs += File::pos(); + break; + } + assert((int32)_subFileStart <= offs && offs <= (int32)(_subFileStart + _subFileLen)); + whence = SEEK_SET; + } + File::seek(offs, whence); +} + +uint32 ScummFile::read(void *ptr, uint32 len) { + uint32 realLen; + + if (_subFileLen) { + // Limit the amount we read by the subfile boundaries. + const uint32 curPos = pos(); + assert(_subFileLen >= curPos); + uint32 newPos = curPos + len; + if (newPos > _subFileLen) { + len = _subFileLen - curPos; + _ioFailed = true; + } + } + + realLen = File::read(ptr, len); + + + // If an encryption byte was specified, XOR the data we just read by it. + // This simple kind of "encryption" was used by some of the older SCUMM + // games. + if (_encbyte) { + byte *p = (byte *)ptr; + byte *end = p + realLen; + while (p < end) + *p++ ^= _encbyte; + } + + return realLen; +} + +uint32 ScummFile::write(const void *, uint32) { + error("ScummFile does not support writing!"); +} + + + /* Open a room */ void ScummEngine::openRoom(int room) { int room_offs, roomlimit; @@ -217,15 +378,24 @@ void ScummEngine::readRoomsOffsets() { bool ScummEngine::openResourceFile(const char *filename, byte encByte) { debugC(DEBUG_GENERAL, "openResourceFile(%s)", filename); + bool result = false; - if (_fileHandle.isOpen()) { + if (!_containerFile.isEmpty()) { + if (!_fileHandle.isOpen()) + _fileHandle.open(_containerFile.c_str()); + assert(_fileHandle.isOpen()); + + result = _fileHandle.openSubFile(filename); + } + + if (!result) { _fileHandle.close(); + result = _fileHandle.open(filename); } - _fileHandle.open(filename); _fileHandle.setEnc(encByte); - return _fileHandle.isOpen(); + return result; } void ScummEngine::askForDisk(const char *filename, int disknum) { diff --git a/scumm/scumm.cpp b/scumm/scumm.cpp index c43ca59122..67fd8a842d 100644 --- a/scumm/scumm.cpp +++ b/scumm/scumm.cpp @@ -1009,6 +1009,27 @@ void ScummEngine::go() { void ScummEngine::launch() { + // The mac versions of Sam&Max and DOTT use a special container file to + // store the actual SCUMM data files. The rescumm utility used to be used + // to extract those files. While that is still possible, we now support + // reading those files directly. The first step is to check whether one + // of them is present (what we do here); the rest is handled by the + // ScummFile class and code in openResourceFile() (and in the Sound class, + // for MONSTER.SOU handling). + if (_gameId == GID_SAMNMAX) { + const char *samDataFile = "Sam & Max Data"; + if (_fileHandle.open(samDataFile)) { + _containerFile = samDataFile; + } + } + if (_gameId == GID_TENTACLE) { + const char *dottDataFile = "Day of the Tentacle Data"; + if (_fileHandle.open(dottDataFile)) { + _containerFile = dottDataFile; + } + } + + #ifdef __PALM_OS__ if (_features & GF_NEW_COSTUMES) _maxHeapThreshold = gVars->memory[kMemScummNewCostGames]; diff --git a/scumm/scumm.h b/scumm/scumm.h index 82ba9704a5..6736e974ff 100644 --- a/scumm/scumm.h +++ b/scumm/scumm.h @@ -61,23 +61,28 @@ struct ScummGameSettings; typedef Common::Map<Common::String, int> ObjectIDMap; -class XORFile : public File { +class ScummFile : public File { private: byte _encbyte; + uint32 _subFileStart; + uint32 _subFileLen; + Common::String _subFileName; public: - XORFile() : _encbyte(0) {} - void setEnc(byte value) { _encbyte = value; } - - uint32 read(void *ptr, uint32 len) { - uint32 realLen = File::read(ptr, len); - if (_encbyte) { - byte *p = (byte *)ptr; - byte *end = p + realLen; - while (p < end) - *p++ ^= _encbyte; - } - return realLen; - } + ScummFile(); + void setEnc(byte value); + + void setSubfileRange(uint32 start, uint32 len); + void resetSubfile(); + + bool open(const char *filename, AccessMode mode = kFileReadMode, const char *directory = NULL); + bool openSubFile(const char *filename); + + bool eof(); + uint32 pos(); + uint32 size(); + void seek(int32 offs, int whence = SEEK_SET); + uint32 read(void *ptr, uint32 size); + uint32 write(const void *ptr, uint32 size); }; // Use g_scumm from error() ONLY @@ -619,8 +624,13 @@ protected: void doSentence(int c, int b, int a); /* Should be in Resource class */ - XORFile _fileHandle; + ScummFile _fileHandle; uint32 _fileOffset; +public: + /** The name of the (macintosh/rescumm style) container file, if any. */ + Common::String _containerFile; + +protected: int _resourceHeaderSize; Common::String _gameName; // This is the name we use for opening resource files Common::String _targetName; // This is the game the user calls it, so use for saving diff --git a/scumm/sound.cpp b/scumm/sound.cpp index e6006f8893..1fc1c16d96 100644 --- a/scumm/sound.cpp +++ b/scumm/sound.cpp @@ -969,40 +969,50 @@ void Sound::startSfxSound(File *file, int file_size, PlayingSoundHandle *handle, } File *Sound::openSfxFile() { - char buf[256]; - XORFile *file = new XORFile(); - _offsetTable = NULL; - struct SoundFileExtensions { const char *ext; SoundMode mode; }; - const SoundFileExtensions extensions[] = { -#ifdef USE_FLAC + static const SoundFileExtensions extensions[] = { + #ifdef USE_FLAC { "sof", kFlacMode }, -#endif -#ifdef USE_MAD + #endif + #ifdef USE_MAD { "so3", kMP3Mode }, -#endif -#ifdef USE_VORBIS + #endif + #ifdef USE_VORBIS { "sog", kVorbisMode }, -#endif + #endif { "sou", kVOCMode }, { 0, kVOCMode } }; + + - /* Try opening the file <_gameName>.sou first, eg tentacle.sou. + char buf[256]; + ScummFile *file = new ScummFile(); + _offsetTable = NULL; + + + if (!_vm->_containerFile.isEmpty() && file->open(_vm->_containerFile.c_str())) { + if (file->openSubFile("monster.sou")) { + _soundMode = kVOCMode; + } else { + file->close(); + } + } + + /* Try opening the file <_gameName>.sou first, e.g. tentacle.sou. * That way, you can keep .sou files for multiple games in the * same directory */ - int i, j; const char *basename[3] = { 0, 0, 0 }; basename[0] = _vm->getGameName(); basename[1] = "monster"; - for (j = 0; basename[j] && !file->isOpen(); ++j) { - for (i = 0; extensions[i].ext; ++i) { + for (int j = 0; basename[j] && !file->isOpen(); ++j) { + for (int i = 0; extensions[i].ext; ++i) { sprintf(buf, "%s.%s", basename[j], extensions[i].ext); if (file->open(buf)) { _soundMode = extensions[i].mode; @@ -1019,7 +1029,9 @@ File *Sound::openSfxFile() { if (file->open(buf)) file->setEnc(0x69); _soundMode = kVOCMode; - } else if (_soundMode != kVOCMode) { + } + + if (_soundMode != kVOCMode) { /* Now load the 'offset' index in memory to be able to find the MP3 data The format of the .SO3 file is easy : |