aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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 :