aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMax Horn2004-07-26 17:14:57 +0000
committerMax Horn2004-07-26 17:14:57 +0000
commitf045cbf17c8e489e37f9a2f5f72137e82c1c9a42 (patch)
treea8f1c5df73f14523d8da28c237dc0300ea39d1d7
parent17ec7c5d6abdbaba0fa55023ac896ff560315e46 (diff)
downloadscummvm-rg350-f045cbf17c8e489e37f9a2f5f72137e82c1c9a42.tar.gz
scummvm-rg350-f045cbf17c8e489e37f9a2f5f72137e82c1c9a42.tar.bz2
scummvm-rg350-f045cbf17c8e489e37f9a2f5f72137e82c1c9a42.zip
Add support for the mac (rescumm) container file format used by Sam&Max and DOTT, making it possible to play those directly from the original CDs (no game detector support yet, so you have to add them to your config file manually for now)
svn-id: r14342
-rw-r--r--scumm/resource.cpp176
-rw-r--r--scumm/scumm.cpp21
-rw-r--r--scumm/scumm.h40
-rw-r--r--scumm/sound.cpp44
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 :