[Scummvm-git-logs] scummvm master -> d2364cb9bb2cf846b35bf7a42dca47426fae2820
csnover
csnover at users.noreply.github.com
Sun Sep 24 04:02:17 CEST 2017
This automated email contains information about 15 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
50cfb7ad46 SCI32: Fix size for dereferencing non-raw SCI32 arrays
0ceab96e91 SCI32: Remove unused shrink-to-fit function for string arrays
b3c27c2159 SCI: Clean-ups to file handling code
d17ae077dd SCI: Use reference instead of pointer for required out-data in metadata save function
9298f6a81e SCI: Split save game metadata writing to separate function
b038432952 SCI32: Support RAMA's custom save games through kFileIO
a63f516df0 SCI32: Replace magic number with kAutoSaveId in QFG4 save code
475bb5f91a SCI32: Remove unnecessary use of strncmp
9c28c25b67 SCI32: Add guest additions save/restore support for RAMA
602de8363b SCI32: Fix infinite reload in RAMA when loading from launcher
6972c9ae46 SCI32: Fix bad default text scaling in RAMA
dced2fb9f5 SCI32: Fix handling of array copies
643a572610 Revert "SCI: Fix Warnings about Copy Constructor Failing to Call Base Class."
f83c61fcb6 SCI32: Fix relocation of locals in SCI3
d2364cb9bb SCI32: Fix bad data cube text priority calculation in RAMA
Commit: 50cfb7ad46ff65ab038f4694bafb4d817ef8529e
https://github.com/scummvm/scummvm/commit/50cfb7ad46ff65ab038f4694bafb4d817ef8529e
Author: Colin Snover (github.com at zetafleet.com)
Date: 2017-09-23T20:37:51-05:00
Commit Message:
SCI32: Fix size for dereferencing non-raw SCI32 arrays
The maxSize given when generating a SegmentRef is supposed to be
the maximum size of raw data that can be held inside the offset-
part of a reg_t, not the entire size including the segment.
This fixes a buffer overflow in "Inside the Chest", which still
does not work, but at least doesn't cause heap overflows anymore
with this change.
Changed paths:
engines/sci/engine/segment.h
diff --git a/engines/sci/engine/segment.h b/engines/sci/engine/segment.h
index 8eca671..ddb3458 100644
--- a/engines/sci/engine/segment.h
+++ b/engines/sci/engine/segment.h
@@ -505,10 +505,14 @@ public:
}
/**
- * Returns the size of the array, in bytes.
+ * Returns the maximum number of bytes that can be stored in the array.
*/
uint16 byteSize() const {
- return _size * _elementSize;
+ uint16 size = _size;
+ if (_type == kArrayTypeID || _type == kArrayTypeInt16) {
+ size *= sizeof(uint16);
+ }
+ return size;
}
/**
@@ -892,7 +896,7 @@ public:
break;
}
- return Common::String::format("type %s; %u entries; %u bytes", type, size(), byteSize());
+ return Common::String::format("type %s; %u entries", type, size());
}
protected:
Commit: 0ceab96e9193c94e21c91e595da1d05a792b3946
https://github.com/scummvm/scummvm/commit/0ceab96e9193c94e21c91e595da1d05a792b3946
Author: Colin Snover (github.com at zetafleet.com)
Date: 2017-09-23T20:37:51-05:00
Commit Message:
SCI32: Remove unused shrink-to-fit function for string arrays
Changed paths:
engines/sci/engine/segment.h
diff --git a/engines/sci/engine/segment.h b/engines/sci/engine/segment.h
index ddb3458..a0c9f7f 100644
--- a/engines/sci/engine/segment.h
+++ b/engines/sci/engine/segment.h
@@ -531,14 +531,6 @@ public:
}
/**
- * Shrinks a string array to its optimal size.
- */
- void snug() {
- assert(_type == kArrayTypeString || _type == kArrayTypeByte);
- resize(Common::strnlen((char *)_data, _size) + 1, true);
- }
-
- /**
* Returns a pointer to the array's raw data storage.
*/
void *getRawData() { return _data; }
Commit: b3c27c2159a1e6d5d775d76a98dd5f4ca4315616
https://github.com/scummvm/scummvm/commit/b3c27c2159a1e6d5d775d76a98dd5f4ca4315616
Author: Colin Snover (github.com at zetafleet.com)
Date: 2017-09-23T20:37:51-05:00
Commit Message:
SCI: Clean-ups to file handling code
Preparations for adding support for RAMA's custom save game format.
Changed paths:
engines/sci/engine/file.cpp
engines/sci/engine/file.h
engines/sci/engine/kfile.cpp
engines/sci/engine/kgraphics.cpp
diff --git a/engines/sci/engine/file.cpp b/engines/sci/engine/file.cpp
index 7e218bc..f3c04dd 100644
--- a/engines/sci/engine/file.cpp
+++ b/engines/sci/engine/file.cpp
@@ -34,28 +34,7 @@
namespace Sci {
#ifdef ENABLE_SCI32
-/**
- * A MemoryWriteStreamDynamic with additional read functionality.
- * The read and write functions share a single stream position.
- */
-class MemoryDynamicRWStream : public Common::MemoryWriteStreamDynamic, public Common::SeekableReadStream {
-protected:
- bool _eos;
-public:
- MemoryDynamicRWStream(DisposeAfterUse::Flag disposeMemory = DisposeAfterUse::NO) : MemoryWriteStreamDynamic(disposeMemory), _eos(false) { }
-
- uint32 read(void *dataPtr, uint32 dataSize);
-
- bool eos() const { return _eos; }
- int32 pos() const { return _pos; }
- int32 size() const { return _size; }
- void clearErr() { _eos = false; Common::MemoryWriteStreamDynamic::clearErr(); }
- bool seek(int32 offs, int whence = SEEK_SET) { return Common::MemoryWriteStreamDynamic::seek(offs, whence); }
-
-};
-
-uint32 MemoryDynamicRWStream::read(void *dataPtr, uint32 dataSize)
-{
+uint32 MemoryDynamicRWStream::read(void *dataPtr, uint32 dataSize) {
// Read at most as many bytes as are still available...
if (dataSize > _size - _pos) {
dataSize = _size - _pos;
@@ -69,40 +48,18 @@ uint32 MemoryDynamicRWStream::read(void *dataPtr, uint32 dataSize)
return dataSize;
}
-/**
- * A MemoryDynamicRWStream intended to re-write a file.
- * It reads the contents of `inFile` in the constructor, and writes back
- * the changes to `fileName` in the destructor (and when calling commit() ).
- */
-class SaveFileRewriteStream : public MemoryDynamicRWStream {
-public:
- SaveFileRewriteStream(Common::String fileName,
- Common::SeekableReadStream *inFile,
- kFileOpenMode mode, bool compress);
- virtual ~SaveFileRewriteStream();
-
- virtual uint32 write(const void *dataPtr, uint32 dataSize) { _changed = true; return MemoryDynamicRWStream::write(dataPtr, dataSize); }
-
- void commit(); //< Save back to disk
-
-protected:
- Common::String _fileName;
- bool _compress;
- bool _changed;
-};
-
-SaveFileRewriteStream::SaveFileRewriteStream(Common::String fileName,
+SaveFileRewriteStream::SaveFileRewriteStream(const Common::String &fileName,
Common::SeekableReadStream *inFile,
kFileOpenMode mode,
- bool compress)
-: MemoryDynamicRWStream(DisposeAfterUse::YES),
- _fileName(fileName), _compress(compress)
-{
- const bool truncate = mode == _K_FILE_MODE_CREATE;
- const bool seekToEnd = mode == _K_FILE_MODE_OPEN_OR_CREATE;
+ bool compress) :
+ MemoryDynamicRWStream(DisposeAfterUse::YES),
+ _fileName(fileName),
+ _compress(compress) {
+ const bool truncate = (mode == kFileOpenModeCreate);
+ const bool seekToEnd = (mode == kFileOpenModeOpenOrCreate);
if (!truncate && inFile) {
- unsigned int s = inFile->size();
+ const uint s = inFile->size();
ensureCapacity(s);
inFile->read(_data, s);
if (seekToEnd) {
@@ -119,16 +76,14 @@ SaveFileRewriteStream::~SaveFileRewriteStream() {
}
void SaveFileRewriteStream::commit() {
- // Write contents of buffer back to file
-
- if (_changed) {
- Common::WriteStream *outFile = g_sci->getSaveFileManager()->openForSaving(_fileName, _compress);
- outFile->write(_data, _size);
- delete outFile;
- _changed = false;
+ if (!_changed) {
+ return;
}
-}
+ Common::ScopedPtr<Common::WriteStream> outFile(g_sci->getSaveFileManager()->openForSaving(_fileName, _compress));
+ outFile->write(_data, _size);
+ _changed = false;
+}
#endif
uint findFreeFileHandle(EngineState *s) {
@@ -217,8 +172,19 @@ reg_t file_open(EngineState *s, const Common::String &filename, kFileOpenMode mo
}
#ifdef ENABLE_SCI32
- if ((g_sci->getGameId() == GID_PHANTASMAGORIA && (filename == "phantsg.dir" || filename == "chase.dat" || filename == "tmp.dat")) ||
- (g_sci->getGameId() == GID_PQSWAT && filename == "swat.dat")) {
+ bool isRewritableFile;
+ switch (g_sci->getGameId()) {
+ case GID_PHANTASMAGORIA:
+ isRewritableFile = (filename == "phantsg.dir" || filename == "chase.dat" || filename == "tmp.dat");
+ break;
+ case GID_PQSWAT:
+ isRewritableFile = (filename == "swat.dat");
+ break;
+ default:
+ isRewritableFile = false;
+ }
+
+ if (isRewritableFile) {
debugC(kDebugLevelFile, " -> file_open opening %s for rewriting", wrappedName.c_str());
inFile = saveFileMan->openForLoading(wrappedName);
@@ -227,8 +193,8 @@ reg_t file_open(EngineState *s, const Common::String &filename, kFileOpenMode mo
if (!inFile)
inFile = SearchMan.createReadStreamForMember(englishName);
- if (mode == _K_FILE_MODE_OPEN_OR_FAIL && !inFile) {
- debugC(kDebugLevelFile, " -> file_open(_K_FILE_MODE_OPEN_OR_FAIL): failed to open file '%s'", englishName.c_str());
+ if (mode == kFileOpenModeOpenOrFail && !inFile) {
+ debugC(kDebugLevelFile, " -> file_open(kFileOpenModeOpenOrFail): failed to open file '%s'", englishName.c_str());
return SIGNAL_REG;
}
@@ -241,7 +207,7 @@ reg_t file_open(EngineState *s, const Common::String &filename, kFileOpenMode mo
outFile = stream;
} else
#endif
- if (mode == _K_FILE_MODE_OPEN_OR_FAIL) {
+ if (mode == kFileOpenModeOpenOrFail) {
// Try to open file, abort if not possible
inFile = saveFileMan->openForLoading(wrappedName);
// If no matching savestate exists: fall back to reading from a regular
@@ -250,19 +216,19 @@ reg_t file_open(EngineState *s, const Common::String &filename, kFileOpenMode mo
inFile = SearchMan.createReadStreamForMember(englishName);
if (!inFile)
- debugC(kDebugLevelFile, " -> file_open(_K_FILE_MODE_OPEN_OR_FAIL): failed to open file '%s'", englishName.c_str());
- } else if (mode == _K_FILE_MODE_CREATE) {
+ debugC(kDebugLevelFile, " -> file_open(kFileOpenModeOpenOrFail): failed to open file '%s'", englishName.c_str());
+ } else if (mode == kFileOpenModeCreate) {
// Create the file, destroying any content it might have had
outFile = saveFileMan->openForSaving(wrappedName, isCompressed);
if (!outFile)
- debugC(kDebugLevelFile, " -> file_open(_K_FILE_MODE_CREATE): failed to create file '%s'", englishName.c_str());
- } else if (mode == _K_FILE_MODE_OPEN_OR_CREATE) {
+ debugC(kDebugLevelFile, " -> file_open(kFileOpenModeCreate): failed to create file '%s'", englishName.c_str());
+ } else if (mode == kFileOpenModeOpenOrCreate) {
// Try to open file, create it if it doesn't exist
outFile = saveFileMan->openForSaving(wrappedName, isCompressed);
if (!outFile)
- debugC(kDebugLevelFile, " -> file_open(_K_FILE_MODE_CREATE): failed to create file '%s'", englishName.c_str());
+ debugC(kDebugLevelFile, " -> file_open(kFileOpenModeCreate): failed to create file '%s'", englishName.c_str());
- // QfG1 opens the character export file with _K_FILE_MODE_CREATE first,
+ // QfG1 opens the character export file with kFileOpenModeCreate first,
// closes it immediately and opens it again with this here. Perhaps
// other games use this for read access as well. I guess changing this
// whole code into using virtual files and writing them after close
@@ -287,7 +253,7 @@ reg_t file_open(EngineState *s, const Common::String &filename, kFileOpenMode mo
}
FileHandle *getFileFromHandle(EngineState *s, uint handle) {
- if ((handle == 0) || ((handle >= VIRTUALFILE_HANDLE_START) && (handle <= VIRTUALFILE_HANDLE_END))) {
+ if ((handle == 0) || ((handle >= kVirtualFileHandleStart) && (handle <= kVirtualFileHandleEnd))) {
error("Attempt to use invalid file handle (%d)", handle);
return 0;
}
@@ -333,38 +299,34 @@ static bool _savegame_sort_byDate(const SavegameDesc &l, const SavegameDesc &r)
return (l.time > r.time);
}
-bool fillSavegameDesc(const Common::String &filename, SavegameDesc *desc) {
+bool fillSavegameDesc(const Common::String &filename, SavegameDesc &desc) {
Common::SaveFileManager *saveFileMan = g_sci->getSaveFileManager();
- Common::SeekableReadStream *in;
- if ((in = saveFileMan->openForLoading(filename)) == nullptr) {
+ Common::ScopedPtr<Common::SeekableReadStream> in(saveFileMan->openForLoading(filename));
+ if (!in) {
return false;
}
SavegameMetadata meta;
- if (!get_savegame_metadata(in, &meta) || meta.name.empty()) {
- // invalid
- delete in;
+ if (!get_savegame_metadata(in.get(), &meta) || meta.name.empty()) {
return false;
}
- delete in;
const int id = strtol(filename.end() - 3, NULL, 10);
- desc->id = id;
- desc->date = meta.saveDate;
+ desc.id = id;
// We need to fix date in here, because we save DDMMYYYY instead of
// YYYYMMDD, so sorting wouldn't work
- desc->date = ((desc->date & 0xFFFF) << 16) | ((desc->date & 0xFF0000) >> 8) | ((desc->date & 0xFF000000) >> 24);
- desc->time = meta.saveTime;
- desc->version = meta.version;
- desc->gameVersion = meta.gameVersion;
- desc->script0Size = meta.script0Size;
- desc->gameObjectOffset = meta.gameObjectOffset;
+ desc.date = ((meta.saveDate & 0xFFFF) << 16) | ((meta.saveDate & 0xFF0000) >> 8) | ((meta.saveDate & 0xFF000000) >> 24);
+ desc.time = meta.saveTime;
+ desc.version = meta.version;
+ desc.gameVersion = meta.gameVersion;
+ desc.script0Size = meta.script0Size;
+ desc.gameObjectOffset = meta.gameObjectOffset;
#ifdef ENABLE_SCI32
if (g_sci->getGameId() == GID_SHIVERS) {
- desc->lowScore = meta.lowScore;
- desc->highScore = meta.highScore;
+ desc.lowScore = meta.lowScore;
+ desc.highScore = meta.highScore;
} else if (g_sci->getGameId() == GID_MOTHERGOOSEHIRES) {
- desc->avatarId = meta.avatarId;
+ desc.avatarId = meta.avatarId;
}
#endif
@@ -372,10 +334,10 @@ bool fillSavegameDesc(const Common::String &filename, SavegameDesc *desc) {
meta.name.deleteLastChar();
// At least Phant2 requires use of strncpy, since it creates save game
- // names of exactly SCI_MAX_SAVENAME_LENGTH
- strncpy(desc->name, meta.name.c_str(), SCI_MAX_SAVENAME_LENGTH);
+ // names of exactly kMaxSaveNameLength
+ strncpy(desc.name, meta.name.c_str(), kMaxSaveNameLength);
- return desc;
+ return true;
}
// Create an array containing all found savedgames, sorted by creation date
@@ -394,8 +356,9 @@ void listSavegames(Common::Array<SavegameDesc> &saves) {
#endif
SavegameDesc desc;
- fillSavegameDesc(filename, &desc);
- saves.push_back(desc);
+ if (fillSavegameDesc(filename, desc)) {
+ saves.push_back(desc);
+ }
}
// Sort the list by creation date of the saves
diff --git a/engines/sci/engine/file.h b/engines/sci/engine/file.h
index 1657dd3..28a3c58 100644
--- a/engines/sci/engine/file.h
+++ b/engines/sci/engine/file.h
@@ -23,20 +23,21 @@
#ifndef SCI_ENGINE_FILE_H
#define SCI_ENGINE_FILE_H
+#include "common/memstream.h"
#include "common/str-array.h"
#include "common/stream.h"
namespace Sci {
enum kFileOpenMode {
- _K_FILE_MODE_OPEN_OR_CREATE = 0,
- _K_FILE_MODE_OPEN_OR_FAIL = 1,
- _K_FILE_MODE_CREATE = 2
+ kFileOpenModeOpenOrCreate = 0,
+ kFileOpenModeOpenOrFail = 1,
+ kFileOpenModeCreate = 2
};
enum {
- SCI_MAX_SAVENAME_LENGTH = 36, ///< Maximum length of a savegame name (including optional terminator character).
- MAX_SAVEGAME_NR = 20 ///< Maximum number of savegames
+ kMaxSaveNameLength = 36, ///< Maximum length of a savegame name (including optional terminator character).
+ kMaxNumSaveGames = 20 ///< Maximum number of savegames
};
#ifdef ENABLE_SCI32
@@ -51,10 +52,12 @@ enum {
};
#endif
-#define VIRTUALFILE_HANDLE_START 32000
-#define VIRTUALFILE_HANDLE_SCI32SAVE 32100
-#define VIRTUALFILE_HANDLE_SCIAUDIO 32300
-#define VIRTUALFILE_HANDLE_END 32300
+enum {
+ kVirtualFileHandleStart = 32000,
+ kVirtualFileHandleSci32Save = 32100,
+ kVirtualFileHandleSciAudio = 32300,
+ kVirtualFileHandleEnd = 32300
+};
struct SavegameDesc {
int16 id;
@@ -62,7 +65,7 @@ struct SavegameDesc {
int date;
int time;
int version;
- char name[SCI_MAX_SAVENAME_LENGTH];
+ char name[kMaxSaveNameLength];
Common::String gameVersion;
uint32 script0Size;
uint32 gameObjectOffset;
@@ -112,7 +115,58 @@ private:
void addAsVirtualFiles(Common::String title, Common::String fileMask);
};
+#ifdef ENABLE_SCI32
+/**
+ * A MemoryWriteStreamDynamic with additional read functionality.
+ * The read and write functions share a single stream position.
+ */
+class MemoryDynamicRWStream : public Common::MemoryWriteStreamDynamic, public Common::SeekableReadStream {
+public:
+ MemoryDynamicRWStream(DisposeAfterUse::Flag disposeMemory = DisposeAfterUse::NO) : MemoryWriteStreamDynamic(disposeMemory), _eos(false) { }
+
+ uint32 read(void *dataPtr, uint32 dataSize);
+
+ bool eos() const { return _eos; }
+ int32 pos() const { return _pos; }
+ int32 size() const { return _size; }
+ void clearErr() { _eos = false; Common::MemoryWriteStreamDynamic::clearErr(); }
+ bool seek(int32 offs, int whence = SEEK_SET) { return Common::MemoryWriteStreamDynamic::seek(offs, whence); }
+
+protected:
+ bool _eos;
+};
+
+/**
+ * A MemoryDynamicRWStream intended to re-write a file.
+ * It reads the contents of `inFile` in the constructor, and writes back
+ * the changes to `fileName` in the destructor (and when calling commit() ).
+ */
+class SaveFileRewriteStream : public MemoryDynamicRWStream {
+public:
+ SaveFileRewriteStream(const Common::String &fileName,
+ Common::SeekableReadStream *inFile,
+ kFileOpenMode mode, bool compress);
+ virtual ~SaveFileRewriteStream();
+
+ virtual uint32 write(const void *dataPtr, uint32 dataSize) { _changed = true; return MemoryDynamicRWStream::write(dataPtr, dataSize); }
+
+ void commit(); //< Save back to disk
+
+protected:
+ Common::String _fileName;
+ bool _compress;
+ bool _changed;
+};
+
+#endif
+
uint findFreeFileHandle(EngineState *s);
+reg_t file_open(EngineState *s, const Common::String &filename, kFileOpenMode mode, bool unwrapFilename);
+FileHandle *getFileFromHandle(EngineState *s, uint handle);
+int fgets_wrapper(EngineState *s, char *dest, int maxsize, int handle);
+void listSavegames(Common::Array<SavegameDesc> &saves);
+int findSavegame(Common::Array<SavegameDesc> &saves, int16 savegameId);
+bool fillSavegameDesc(const Common::String &filename, SavegameDesc &desc);
} // End of namespace Sci
diff --git a/engines/sci/engine/kfile.cpp b/engines/sci/engine/kfile.cpp
index 3b84208..b5aac43 100644
--- a/engines/sci/engine/kfile.cpp
+++ b/engines/sci/engine/kfile.cpp
@@ -48,13 +48,6 @@
namespace Sci {
-extern reg_t file_open(EngineState *s, const Common::String &filename, kFileOpenMode mode, bool unwrapFilename);
-extern FileHandle *getFileFromHandle(EngineState *s, uint handle);
-extern int fgets_wrapper(EngineState *s, char *dest, int maxsize, int handle);
-extern void listSavegames(Common::Array<SavegameDesc> &saves);
-extern int findSavegame(Common::Array<SavegameDesc> &saves, int16 savegameId);
-extern bool fillSavegameDesc(const Common::String &filename, SavegameDesc *desc);
-
/**
* Writes the cwd to the supplied address and returns the address in acc.
*/
@@ -319,11 +312,11 @@ reg_t kFileIOOpen(EngineState *s, int argc, reg_t *argv) {
if (g_sci->getGameId() == GID_SHIVERS && name.hasSuffix(".SG")) {
// Shivers stores the name and score of save games in separate %d.SG
// files, which are used by the save/load screen
- if (mode == _K_FILE_MODE_OPEN_OR_CREATE || mode == _K_FILE_MODE_CREATE) {
+ if (mode == kFileOpenModeOpenOrCreate || mode == kFileOpenModeCreate) {
// Suppress creation of the SG file, since it is not necessary
debugC(kDebugLevelFile, "Not creating unused file %s", name.c_str());
return SIGNAL_REG;
- } else if (mode == _K_FILE_MODE_OPEN_OR_FAIL) {
+ } else if (mode == kFileOpenModeOpenOrFail) {
// Create a virtual file containing the save game description
// and current score progress, as the game scripts expect.
int saveNo;
@@ -331,7 +324,9 @@ reg_t kFileIOOpen(EngineState *s, int argc, reg_t *argv) {
saveNo += kSaveIdShift;
SavegameDesc save;
- fillSavegameDesc(g_sci->getSavegameName(saveNo), &save);
+ if (!fillSavegameDesc(g_sci->getSavegameName(saveNo), save)) {
+ return SIGNAL_REG;
+ }
Common::String score;
if (!save.highScore) {
@@ -340,9 +335,9 @@ reg_t kFileIOOpen(EngineState *s, int argc, reg_t *argv) {
score = Common::String::format("%u%03u", save.highScore, save.lowScore);
}
- const uint nameLength = Common::strnlen(save.name, SCI_MAX_SAVENAME_LENGTH);
+ const uint nameLength = Common::strnlen(save.name, kMaxSaveNameLength);
const uint size = nameLength + /* \r\n */ 2 + score.size();
- char *buffer = (char *)malloc(size);
+ byte *buffer = (byte *)malloc(size);
memcpy(buffer, save.name, nameLength);
buffer[nameLength] = '\r';
buffer[nameLength + 1] = '\n';
@@ -350,7 +345,7 @@ reg_t kFileIOOpen(EngineState *s, int argc, reg_t *argv) {
const uint handle = findFreeFileHandle(s);
- s->_fileHandles[handle]._in = new Common::MemoryReadStream((byte *)buffer, size, DisposeAfterUse::YES);
+ s->_fileHandles[handle]._in = new Common::MemoryReadStream(buffer, size, DisposeAfterUse::YES);
s->_fileHandles[handle]._out = nullptr;
s->_fileHandles[handle]._name = "";
@@ -359,11 +354,11 @@ reg_t kFileIOOpen(EngineState *s, int argc, reg_t *argv) {
} else if (g_sci->getGameId() == GID_MOTHERGOOSEHIRES && name.hasSuffix(".DTA")) {
// MGDX stores the name and avatar ID in separate %d.DTA files, which
// are used by the save/load screen
- if (mode == _K_FILE_MODE_OPEN_OR_CREATE || mode == _K_FILE_MODE_CREATE) {
+ if (mode == kFileOpenModeOpenOrCreate || mode == kFileOpenModeCreate) {
// Suppress creation of the DTA file, since it is not necessary
debugC(kDebugLevelFile, "Not creating unused file %s", name.c_str());
return SIGNAL_REG;
- } else if (mode == _K_FILE_MODE_OPEN_OR_FAIL) {
+ } else if (mode == kFileOpenModeOpenOrFail) {
// Create a virtual file containing the save game description
// and avatar ID, as the game scripts expect.
int saveNo;
@@ -380,10 +375,12 @@ reg_t kFileIOOpen(EngineState *s, int argc, reg_t *argv) {
saveNo += kSaveIdShift;
SavegameDesc save;
- fillSavegameDesc(g_sci->getSavegameName(saveNo), &save);
+ if (!fillSavegameDesc(g_sci->getSavegameName(saveNo), save)) {
+ return SIGNAL_REG;
+ }
const Common::String avatarId = Common::String::format("%02d", save.avatarId);
- const uint nameLength = Common::strnlen(save.name, SCI_MAX_SAVENAME_LENGTH);
+ const uint nameLength = Common::strnlen(save.name, kMaxSaveNameLength);
const uint size = nameLength + /* \r\n */ 2 + avatarId.size() + 1;
char *buffer = (char *)malloc(size);
memcpy(buffer, save.name, nameLength);
@@ -404,21 +401,21 @@ reg_t kFileIOOpen(EngineState *s, int argc, reg_t *argv) {
// catalogue, but since we do not create catalogues for most SCI32
// games, ignore the write
if (name == "temp.tmp") {
- return make_reg(0, VIRTUALFILE_HANDLE_SCI32SAVE);
+ return make_reg(0, kVirtualFileHandleSci32Save);
}
// KQ7 tries to read out game information from catalogues directly
// instead of using the standard kSaveGetFiles function
if (name == "kq7cdsg.cat") {
- if (mode == _K_FILE_MODE_OPEN_OR_CREATE || mode == _K_FILE_MODE_CREATE) {
+ if (mode == kFileOpenModeOpenOrCreate || mode == kFileOpenModeCreate) {
// Suppress creation of the catalogue file, since it is not necessary
debugC(kDebugLevelFile, "Not creating unused file %s", name.c_str());
return SIGNAL_REG;
- } else if (mode == _K_FILE_MODE_OPEN_OR_FAIL) {
+ } else if (mode == kFileOpenModeOpenOrFail) {
Common::Array<SavegameDesc> saves;
listSavegames(saves);
- const uint recordSize = sizeof(int16) + SCI_MAX_SAVENAME_LENGTH;
+ const uint recordSize = sizeof(int16) + kMaxSaveNameLength;
const uint numSaves = MIN<uint>(saves.size(), 10);
const uint size = numSaves * recordSize + /* terminator */ 2;
byte *const buffer = (byte *)malloc(size);
@@ -426,7 +423,7 @@ reg_t kFileIOOpen(EngineState *s, int argc, reg_t *argv) {
byte *out = buffer;
for (uint i = 0; i < numSaves; ++i) {
WRITE_UINT16(out, saves[i].id - kSaveIdShift);
- strncpy((char *)out + sizeof(int16), saves[i].name, SCI_MAX_SAVENAME_LENGTH);
+ strncpy((char *)out + sizeof(int16), saves[i].name, kMaxSaveNameLength);
out += recordSize;
}
WRITE_UINT16(out, 0xFFFF);
@@ -453,7 +450,7 @@ reg_t kFileIOOpen(EngineState *s, int argc, reg_t *argv) {
// See kMakeSaveCatName
if (name == "fake.cat") {
- return make_reg(0, VIRTUALFILE_HANDLE_SCI32SAVE);
+ return make_reg(0, kVirtualFileHandleSci32Save);
}
if (isSaveCatalogue(name)) {
@@ -461,7 +458,7 @@ reg_t kFileIOOpen(EngineState *s, int argc, reg_t *argv) {
if (exists) {
// Dummy handle is used to represent the catalogue and ignore any
// direct game script writes
- return make_reg(0, VIRTUALFILE_HANDLE_SCI32SAVE);
+ return make_reg(0, kVirtualFileHandleSci32Save);
} else {
return SIGNAL_REG;
}
@@ -472,7 +469,7 @@ reg_t kFileIOOpen(EngineState *s, int argc, reg_t *argv) {
if (name.hasPrefix("sciAudio\\")) {
// fan-made sciAudio extension, don't create those files and instead return a virtual handle
- return make_reg(0, VIRTUALFILE_HANDLE_SCIAUDIO);
+ return make_reg(0, kVirtualFileHandleSciAudio);
}
// QFG import rooms get a virtual filelisting instead of an actual one
@@ -495,7 +492,7 @@ reg_t kFileIOClose(EngineState *s, int argc, reg_t *argv) {
uint16 handle = argv[0].toUint16();
- if (handle >= VIRTUALFILE_HANDLE_START) {
+ if (handle >= kVirtualFileHandleStart) {
// it's a virtual handle? ignore it
return getSciVersion() >= SCI_VERSION_2 ? TRUE_REG : SIGNAL_REG;
}
@@ -539,7 +536,7 @@ reg_t kFileIOWriteRaw(EngineState *s, int argc, reg_t *argv) {
uint16 size = argv[2].toUint16();
#ifdef ENABLE_SCI32
- if (handle == VIRTUALFILE_HANDLE_SCI32SAVE) {
+ if (handle == kVirtualFileHandleSci32Save) {
return make_reg(0, size);
}
#endif
@@ -673,7 +670,7 @@ reg_t kFileIOWriteString(EngineState *s, int argc, reg_t *argv) {
// We skip creating these files, and instead handle the calls
// directly. Since the sciAudio calls are only creating text files,
// this is probably the most straightforward place to handle them.
- if (handle == VIRTUALFILE_HANDLE_SCIAUDIO) {
+ if (handle == kVirtualFileHandleSciAudio) {
Common::List<ExecStack>::const_iterator iter = s->_executionStack.reverse_begin();
iter--; // sciAudio
iter--; // sciAudio child
@@ -872,7 +869,7 @@ reg_t kFileIOWriteWord(EngineState *s, int argc, reg_t *argv) {
uint16 handle = argv[0].toUint16();
#ifdef ENABLE_SCI32
- if (handle == VIRTUALFILE_HANDLE_SCI32SAVE) {
+ if (handle == kVirtualFileHandleSci32Save) {
return s->r_acc;
}
#endif
@@ -1174,7 +1171,7 @@ reg_t kGetSaveFiles(EngineState *s, int argc, reg_t *argv) {
Common::Array<SavegameDesc> saves;
listSavegames(saves);
- uint totalSaves = MIN<uint>(saves.size(), MAX_SAVEGAME_NR);
+ uint totalSaves = MIN<uint>(saves.size(), kMaxNumSaveGames);
Common::String game_id = s->_segMan->getString(argv[0]);
@@ -1187,14 +1184,14 @@ reg_t kGetSaveFiles(EngineState *s, int argc, reg_t *argv) {
totalSaves = 0;
}
- const uint bufSize = (totalSaves * SCI_MAX_SAVENAME_LENGTH) + 1;
+ const uint bufSize = (totalSaves * kMaxSaveNameLength) + 1;
char *saveNames = new char[bufSize];
char *saveNamePtr = saveNames;
for (uint i = 0; i < totalSaves; i++) {
*slot++ = make_reg(0, saves[i].id + SAVEGAMEID_OFFICIALRANGE_START); // Store the virtual savegame ID (see above)
strcpy(saveNamePtr, saves[i].name);
- saveNamePtr += SCI_MAX_SAVENAME_LENGTH;
+ saveNamePtr += kMaxSaveNameLength;
}
*saveNamePtr = 0; // Terminate list
@@ -1321,7 +1318,7 @@ reg_t kCheckSaveGame32(EngineState *s, int argc, reg_t *argv) {
}
SavegameDesc save;
- if (!fillSavegameDesc(g_sci->getSavegameName(saveNo), &save)) {
+ if (!fillSavegameDesc(g_sci->getSavegameName(saveNo), save)) {
return NULL_REG;
}
@@ -1369,19 +1366,19 @@ reg_t kGetSaveFiles32(EngineState *s, int argc, reg_t *argv) {
// Normally SSCI limits to 20 games per directory, but ScummVM allows more
// than that with games that use the standard save-load dialogue
- descriptions.resize(SCI_MAX_SAVENAME_LENGTH * saves.size() + 1, true);
+ descriptions.resize(kMaxSaveNameLength * saves.size() + 1, true);
saveIds.resize(saves.size() + 1, true);
for (uint i = 0; i < saves.size(); ++i) {
const SavegameDesc &save = saves[i];
- char *target = &descriptions.charAt(SCI_MAX_SAVENAME_LENGTH * i);
+ char *target = &descriptions.charAt(kMaxSaveNameLength * i);
// At least Phant2 requires use of strncpy, since it creates save game
- // names of exactly SCI_MAX_SAVENAME_LENGTH
- strncpy(target, save.name, SCI_MAX_SAVENAME_LENGTH);
+ // names of exactly kMaxSaveNameLength
+ strncpy(target, save.name, kMaxSaveNameLength);
saveIds.setFromInt16(i, save.id - kSaveIdShift);
}
- descriptions.charAt(SCI_MAX_SAVENAME_LENGTH * saves.size()) = '\0';
+ descriptions.charAt(kMaxSaveNameLength * saves.size()) = '\0';
saveIds.setFromInt16(saves.size(), 0);
return make_reg(0, saves.size());
diff --git a/engines/sci/engine/kgraphics.cpp b/engines/sci/engine/kgraphics.cpp
index 28e054b..e5336b3 100644
--- a/engines/sci/engine/kgraphics.cpp
+++ b/engines/sci/engine/kgraphics.cpp
@@ -932,7 +932,7 @@ void _k_GenericDrawControl(EngineState *s, reg_t controlObject, bool hilite) {
}
}
- debugC(kDebugLevelGraphics, "drawing list control %04x:%04x to %d,%d, diff %d", PRINT_REG(controlObject), x, y, SCI_MAX_SAVENAME_LENGTH);
+ debugC(kDebugLevelGraphics, "drawing list control %04x:%04x to %d,%d", PRINT_REG(controlObject), x, y);
g_sci->_gfxControls16->kernelDrawList(rect, controlObject, maxChars, listCount, listStrings, fontId, style, upperPos, cursorPos, isAlias, hilite);
delete[] listStrings;
return;
Commit: d17ae077dd014d380e8091161fe1f584a8cc29ba
https://github.com/scummvm/scummvm/commit/d17ae077dd014d380e8091161fe1f584a8cc29ba
Author: Colin Snover (github.com at zetafleet.com)
Date: 2017-09-23T20:56:48-05:00
Commit Message:
SCI: Use reference instead of pointer for required out-data in metadata save function
Changed paths:
engines/sci/detection.cpp
engines/sci/engine/file.cpp
engines/sci/engine/savegame.cpp
engines/sci/engine/savegame.h
diff --git a/engines/sci/detection.cpp b/engines/sci/detection.cpp
index 9ebdfa9..aa7a63d 100644
--- a/engines/sci/detection.cpp
+++ b/engines/sci/detection.cpp
@@ -812,7 +812,7 @@ SaveStateList SciMetaEngine::listSaves(const char *target) const {
Common::InSaveFile *in = saveFileMan->openForLoading(*file);
if (in) {
SavegameMetadata meta;
- if (!get_savegame_metadata(in, &meta)) {
+ if (!get_savegame_metadata(in, meta)) {
// invalid
delete in;
continue;
@@ -861,7 +861,7 @@ SaveStateDescriptor SciMetaEngine::querySaveMetaInfos(const char *target, int sl
if (in) {
SavegameMetadata meta;
- if (!get_savegame_metadata(in, &meta)) {
+ if (!get_savegame_metadata(in, meta)) {
// invalid
delete in;
diff --git a/engines/sci/engine/file.cpp b/engines/sci/engine/file.cpp
index f3c04dd..d1015cc 100644
--- a/engines/sci/engine/file.cpp
+++ b/engines/sci/engine/file.cpp
@@ -307,7 +307,7 @@ bool fillSavegameDesc(const Common::String &filename, SavegameDesc &desc) {
}
SavegameMetadata meta;
- if (!get_savegame_metadata(in.get(), &meta) || meta.name.empty()) {
+ if (!get_savegame_metadata(in.get(), meta) || meta.name.empty()) {
return false;
}
diff --git a/engines/sci/engine/savegame.cpp b/engines/sci/engine/savegame.cpp
index fd07e0c..8c89b90 100644
--- a/engines/sci/engine/savegame.cpp
+++ b/engines/sci/engine/savegame.cpp
@@ -1395,22 +1395,21 @@ void gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) {
s->gameIsRestarting = GAMEISRESTARTING_RESTORE;
}
-bool get_savegame_metadata(Common::SeekableReadStream *stream, SavegameMetadata *meta) {
+bool get_savegame_metadata(Common::SeekableReadStream *stream, SavegameMetadata &meta) {
assert(stream);
- assert(meta);
- Common::Serializer ser(stream, 0);
- sync_SavegameMetadata(ser, *meta);
+ Common::Serializer ser(stream, nullptr);
+ sync_SavegameMetadata(ser, meta);
if (stream->eos())
return false;
- if ((meta->version < MINIMUM_SAVEGAME_VERSION) ||
- (meta->version > CURRENT_SAVEGAME_VERSION)) {
- if (meta->version < MINIMUM_SAVEGAME_VERSION)
+ if ((meta.version < MINIMUM_SAVEGAME_VERSION) ||
+ (meta.version > CURRENT_SAVEGAME_VERSION)) {
+ if (meta.version < MINIMUM_SAVEGAME_VERSION)
warning("Old savegame version detected- can't load");
else
- warning("Savegame version is %d- maximum supported is %0d", meta->version, CURRENT_SAVEGAME_VERSION);
+ warning("Savegame version is %d- maximum supported is %0d", meta.version, CURRENT_SAVEGAME_VERSION);
return false;
}
diff --git a/engines/sci/engine/savegame.h b/engines/sci/engine/savegame.h
index 2993024..f862b96 100644
--- a/engines/sci/engine/savegame.h
+++ b/engines/sci/engine/savegame.h
@@ -118,7 +118,7 @@ void gamestate_restore(EngineState *s, Common::SeekableReadStream *save);
/**
* Read the header from a savegame.
*/
-bool get_savegame_metadata(Common::SeekableReadStream* stream, SavegameMetadata* meta);
+bool get_savegame_metadata(Common::SeekableReadStream *stream, SavegameMetadata &meta);
} // End of namespace Sci
Commit: 9298f6a81e6218e7745a0ab6e530ecb5fabc460b
https://github.com/scummvm/scummvm/commit/9298f6a81e6218e7745a0ab6e530ecb5fabc460b
Author: Colin Snover (github.com at zetafleet.com)
Date: 2017-09-23T20:56:48-05:00
Commit Message:
SCI: Split save game metadata writing to separate function
RAMA has its own custom save game format that game scripts write,
but we still want to be able to use these save game files from the
ScummVM launcher, so the metadata has to be able to be written
separately from the rest of the game saving.
Changed paths:
engines/sci/engine/savegame.cpp
engines/sci/engine/savegame.h
diff --git a/engines/sci/engine/savegame.cpp b/engines/sci/engine/savegame.cpp
index 8c89b90..60ad497 100644
--- a/engines/sci/engine/savegame.cpp
+++ b/engines/sci/engine/savegame.cpp
@@ -1184,30 +1184,8 @@ void SegManager::reconstructClones() {
bool gamestate_save(EngineState *s, Common::WriteStream *fh, const Common::String &savename, const Common::String &version) {
- TimeDate curTime;
- g_system->getTimeAndDate(curTime);
-
- SavegameMetadata meta;
- meta.version = CURRENT_SAVEGAME_VERSION;
- meta.name = savename;
- meta.gameVersion = version;
- meta.saveDate = ((curTime.tm_mday & 0xFF) << 24) | (((curTime.tm_mon + 1) & 0xFF) << 16) | ((curTime.tm_year + 1900) & 0xFFFF);
- meta.saveTime = ((curTime.tm_hour & 0xFF) << 16) | (((curTime.tm_min) & 0xFF) << 8) | ((curTime.tm_sec) & 0xFF);
-
- Resource *script0 = g_sci->getResMan()->findResource(ResourceId(kResourceTypeScript, 0), false);
- meta.script0Size = script0->size();
- meta.gameObjectOffset = g_sci->getGameObject().getOffset();
-
- // Checking here again
-// TODO: This breaks Torin autosave, is there actually any reason for it?
-// if (s->executionStackBase) {
-// warning("Cannot save from below kernel function");
-// return false;
-// }
-
- Common::Serializer ser(0, fh);
- sync_SavegameMetadata(ser, meta);
- Graphics::saveThumbnail(*fh);
+ set_savegame_metadata(fh, savename, version);
+ Common::Serializer ser(nullptr, fh);
s->saveLoadWithSerializer(ser); // FIXME: Error handling?
if (g_sci->_gfxPorts)
g_sci->_gfxPorts->saveLoadWithSerializer(ser);
@@ -1395,6 +1373,27 @@ void gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) {
s->gameIsRestarting = GAMEISRESTARTING_RESTORE;
}
+void set_savegame_metadata(Common::WriteStream *fh, const Common::String &savename, const Common::String &version) {
+ TimeDate curTime;
+ g_system->getTimeAndDate(curTime);
+
+ SavegameMetadata meta;
+ meta.version = CURRENT_SAVEGAME_VERSION;
+ meta.name = savename;
+ meta.gameVersion = version;
+ meta.saveDate = ((curTime.tm_mday & 0xFF) << 24) | (((curTime.tm_mon + 1) & 0xFF) << 16) | ((curTime.tm_year + 1900) & 0xFFFF);
+ meta.saveTime = ((curTime.tm_hour & 0xFF) << 16) | (((curTime.tm_min) & 0xFF) << 8) | ((curTime.tm_sec) & 0xFF);
+
+ Resource *script0 = g_sci->getResMan()->findResource(ResourceId(kResourceTypeScript, 0), false);
+ assert(script0);
+ meta.script0Size = script0->size();
+ meta.gameObjectOffset = g_sci->getGameObject().getOffset();
+
+ Common::Serializer ser(nullptr, fh);
+ sync_SavegameMetadata(ser, meta);
+ Graphics::saveThumbnail(*fh);
+}
+
bool get_savegame_metadata(Common::SeekableReadStream *stream, SavegameMetadata &meta) {
assert(stream);
diff --git a/engines/sci/engine/savegame.h b/engines/sci/engine/savegame.h
index f862b96..1ac25b2 100644
--- a/engines/sci/engine/savegame.h
+++ b/engines/sci/engine/savegame.h
@@ -120,6 +120,10 @@ void gamestate_restore(EngineState *s, Common::SeekableReadStream *save);
*/
bool get_savegame_metadata(Common::SeekableReadStream *stream, SavegameMetadata &meta);
+/**
+ * Write the header to a savegame.
+ */
+void set_savegame_metadata(Common::WriteStream *fh, const Common::String &savename, const Common::String &version);
} // End of namespace Sci
Commit: b038432952d84e605b121bf72ebde246a639b244
https://github.com/scummvm/scummvm/commit/b038432952d84e605b121bf72ebde246a639b244
Author: Colin Snover (github.com at zetafleet.com)
Date: 2017-09-23T20:56:48-05:00
Commit Message:
SCI32: Support RAMA's custom save games through kFileIO
Changed paths:
engines/sci/engine/file.cpp
engines/sci/engine/file.h
engines/sci/engine/kernel_tables.h
engines/sci/engine/kfile.cpp
engines/sci/engine/script_patches.cpp
engines/sci/engine/selector.cpp
engines/sci/engine/selector.h
engines/sci/engine/vm.h
diff --git a/engines/sci/engine/file.cpp b/engines/sci/engine/file.cpp
index d1015cc..7f73703 100644
--- a/engines/sci/engine/file.cpp
+++ b/engines/sci/engine/file.cpp
@@ -374,6 +374,74 @@ int findSavegame(Common::Array<SavegameDesc> &saves, int16 savegameId) {
return -1;
}
+#ifdef ENABLE_SCI32
+Common::MemoryReadStream *makeCatalogue(const uint maxNumSaves, const uint gameNameSize, const Common::String &fileNamePattern, const bool ramaFormat) {
+ enum {
+ kGameIdSize = sizeof(int16),
+ kNumSavesSize = sizeof(int16),
+ kFreeSlotSize = sizeof(int16),
+ kTerminatorSize = kGameIdSize,
+ kTerminator = 0xFFFF
+ };
+
+ Common::Array<SavegameDesc> games;
+ listSavegames(games);
+
+ const uint numSaves = MIN(games.size(), maxNumSaves);
+ const uint fileNameSize = fileNamePattern.empty() ? 0 : 12;
+ const uint entrySize = kGameIdSize + fileNameSize + gameNameSize;
+ uint dataSize = numSaves * entrySize + kTerminatorSize;
+ if (ramaFormat) {
+ dataSize += kNumSavesSize + kFreeSlotSize * maxNumSaves;
+ }
+
+ byte *out = (byte *)malloc(dataSize);
+ const byte *const data = out;
+
+ Common::Array<bool> usedSlots;
+ if (ramaFormat) {
+ WRITE_LE_UINT16(out, numSaves);
+ out += kNumSavesSize;
+ usedSlots.resize(maxNumSaves);
+ }
+
+ for (uint i = 0; i < numSaves; ++i) {
+ const SavegameDesc &save = games[i];
+ const uint16 id = save.id - kSaveIdShift;
+ if (!ramaFormat) {
+ WRITE_LE_UINT16(out, id);
+ out += kGameIdSize;
+ }
+ if (fileNameSize) {
+ const Common::String fileName = Common::String::format(fileNamePattern.c_str(), id);
+ strncpy(reinterpret_cast<char *>(out), fileName.c_str(), fileNameSize);
+ out += fileNameSize;
+ }
+ // Game names can be up to exactly gameNameSize
+ strncpy(reinterpret_cast<char *>(out), save.name, gameNameSize);
+ out += gameNameSize;
+ if (ramaFormat) {
+ WRITE_LE_UINT16(out, id);
+ out += kGameIdSize;
+
+ assert(id >= 0 && id < maxNumSaves);
+ usedSlots[id] = true;
+ }
+ }
+
+ if (ramaFormat) {
+ // A table indicating which save game slots are occupied
+ for (uint i = 0; i < usedSlots.size(); ++i) {
+ WRITE_LE_UINT16(out, !usedSlots[i]);
+ out += kFreeSlotSize;
+ }
+ }
+
+ WRITE_LE_UINT16(out, kTerminator);
+
+ return new Common::MemoryReadStream(data, dataSize, DisposeAfterUse::YES);
+}
+#endif
FileHandle::FileHandle() : _in(0), _out(0) {
}
diff --git a/engines/sci/engine/file.h b/engines/sci/engine/file.h
index 28a3c58..b94caa0 100644
--- a/engines/sci/engine/file.h
+++ b/engines/sci/engine/file.h
@@ -168,6 +168,14 @@ void listSavegames(Common::Array<SavegameDesc> &saves);
int findSavegame(Common::Array<SavegameDesc> &saves, int16 savegameId);
bool fillSavegameDesc(const Common::String &filename, SavegameDesc &desc);
+#ifdef ENABLE_SCI32
+/**
+ * Constructs an in-memory stream from the ScummVM save game list that is
+ * compatible with game scripts' game catalogue readers.
+ */
+Common::MemoryReadStream *makeCatalogue(const uint maxNumSaves, const uint gameNameSize, const Common::String &fileNamePattern, const bool ramaFormat);
+#endif
+
} // End of namespace Sci
#endif // SCI_ENGINE_FILE_H
diff --git a/engines/sci/engine/kernel_tables.h b/engines/sci/engine/kernel_tables.h
index d7a83d9..884a499 100644
--- a/engines/sci/engine/kernel_tables.h
+++ b/engines/sci/engine/kernel_tables.h
@@ -67,6 +67,7 @@ struct SciKernelMapSubEntry {
#define SIG_SINCE_SCI11 SCI_VERSION_1_1, SCI_VERSION_NONE
#define SIG_SCI2 SCI_VERSION_2, SCI_VERSION_2
#define SIG_SCI21EARLY SCI_VERSION_2_1_EARLY, SCI_VERSION_2_1_EARLY
+#define SIG_SCI21MID_LATE SCI_VERSION_2_1_MIDDLE, SCI_VERSION_2_1_LATE
#define SIG_THRU_SCI21EARLY SCI_VERSION_2, SCI_VERSION_2_1_EARLY
#define SIG_THRU_SCI21MID SCI_VERSION_2, SCI_VERSION_2_1_MIDDLE
#define SIG_SINCE_SCI21 SCI_VERSION_2_1_EARLY, SCI_VERSION_3
@@ -324,7 +325,8 @@ static const SciKernelMapSubEntry kFileIO_subops[] = {
{ SIG_SINCE_SCI21MID, 13, MAP_CALL(FileIOReadByte), "i", NULL },
{ SIG_SINCE_SCI21MID, 14, MAP_CALL(FileIOWriteByte), "ii", NULL },
{ SIG_SINCE_SCI21MID, 15, MAP_CALL(FileIOReadWord), "i", NULL },
- { SIG_SINCE_SCI21MID, 16, MAP_CALL(FileIOWriteWord), "ii", NULL },
+ { SIG_SCI21MID_LATE, 16, MAP_CALL(FileIOWriteWord), "ii", NULL },
+ { SIG_SCI3, 16, MAP_CALL(FileIOWriteWord), "i.", NULL },
{ SIG_SINCE_SCI21MID, 17, "FileIOCheckFreeSpace", kCheckFreeSpace, "i(r)", NULL },
{ SIG_SINCE_SCI21MID, 18, MAP_CALL(FileIOGetCWD), "r", NULL },
{ SIG_SINCE_SCI21MID, 19, MAP_CALL(FileIOIsValidDirectory), "[ro]", NULL },
diff --git a/engines/sci/engine/kfile.cpp b/engines/sci/engine/kfile.cpp
index b5aac43..d8ef919 100644
--- a/engines/sci/engine/kfile.cpp
+++ b/engines/sci/engine/kfile.cpp
@@ -41,6 +41,7 @@
#include "sci/sound/audio.h"
#include "sci/console.h"
#ifdef ENABLE_SCI32
+#include "graphics/thumbnail.h"
#include "sci/engine/guest_additions.h"
#include "sci/engine/message.h"
#include "sci/resource.h"
@@ -262,6 +263,34 @@ static bool saveCatalogueExists(const Common::String &name) {
return exists;
}
+
+static Common::String getRamaSaveName(EngineState *s, const uint saveNo) {
+ const reg_t catalogId = s->variables[VAR_GLOBAL][kGlobalVarRamaCatalogFile];
+ if (catalogId.isNull()) {
+ error("Could not find CatalogFile object to retrieve save game name");
+ }
+
+ const List *list = s->_segMan->lookupList(readSelector(s->_segMan, catalogId, SELECTOR(elements)));
+ if (!list) {
+ error("Could not read CatalogFile object list");
+ }
+
+ Node *node = s->_segMan->lookupNode(list->first);
+ while (node) {
+ const reg_t entryId = node->value;
+ if (readSelectorValue(s->_segMan, entryId, SELECTOR(fileNumber)) == saveNo) {
+ reg_t description = readSelector(s->_segMan, entryId, SELECTOR(description));
+ if (s->_segMan->isObject(description)) {
+ description = readSelector(s->_segMan, description, SELECTOR(data));
+ }
+ return s->_segMan->getString(description);
+ }
+
+ node = s->_segMan->lookupNode(node->succ);
+ }
+
+ error("Could not find a save name for save %u", saveNo);
+}
#endif
reg_t kFileIO(EngineState *s, int argc, reg_t *argv) {
@@ -403,39 +432,6 @@ reg_t kFileIOOpen(EngineState *s, int argc, reg_t *argv) {
if (name == "temp.tmp") {
return make_reg(0, kVirtualFileHandleSci32Save);
}
-
- // KQ7 tries to read out game information from catalogues directly
- // instead of using the standard kSaveGetFiles function
- if (name == "kq7cdsg.cat") {
- if (mode == kFileOpenModeOpenOrCreate || mode == kFileOpenModeCreate) {
- // Suppress creation of the catalogue file, since it is not necessary
- debugC(kDebugLevelFile, "Not creating unused file %s", name.c_str());
- return SIGNAL_REG;
- } else if (mode == kFileOpenModeOpenOrFail) {
- Common::Array<SavegameDesc> saves;
- listSavegames(saves);
-
- const uint recordSize = sizeof(int16) + kMaxSaveNameLength;
- const uint numSaves = MIN<uint>(saves.size(), 10);
- const uint size = numSaves * recordSize + /* terminator */ 2;
- byte *const buffer = (byte *)malloc(size);
-
- byte *out = buffer;
- for (uint i = 0; i < numSaves; ++i) {
- WRITE_UINT16(out, saves[i].id - kSaveIdShift);
- strncpy((char *)out + sizeof(int16), saves[i].name, kMaxSaveNameLength);
- out += recordSize;
- }
- WRITE_UINT16(out, 0xFFFF);
-
- const uint handle = findFreeFileHandle(s);
- s->_fileHandles[handle]._in = new Common::MemoryReadStream(buffer, size, DisposeAfterUse::YES);
- s->_fileHandles[handle]._out = nullptr;
- s->_fileHandles[handle]._name = "";
-
- return make_reg(0, handle);
- }
- }
} else if (g_sci->getGameId() == GID_PQSWAT) {
// PQSWAT tries to create subdirectories for each game profile
for (Common::String::iterator it = name.begin(); it != name.end(); ++it) {
@@ -446,6 +442,52 @@ reg_t kFileIOOpen(EngineState *s, int argc, reg_t *argv) {
} else if (g_sci->getGameId() == GID_PHANTASMAGORIA2 && name == "RESDUK.PAT") {
// Ignore the censorship password file in lieu of our game option
return SIGNAL_REG;
+ } else if (g_sci->getGameId() == GID_RAMA) {
+ int saveNo = -1;
+ if (name == "autorama.sg") {
+ saveNo = kAutoSaveId;
+ } else if (sscanf(name.c_str(), "ramasg.%i", &saveNo) == 1) {
+ saveNo += kSaveIdShift;
+ }
+
+ if (saveNo != -1) {
+ Common::SaveFileManager *saveFileMan = g_sci->getSaveFileManager();
+ const Common::String fileName = g_sci->getSavegameName(saveNo);
+ Common::SeekableReadStream *in = nullptr;
+ Common::OutSaveFile *out = nullptr;
+ bool valid = false;
+
+ if (mode == kFileOpenModeOpenOrFail) {
+ in = saveFileMan->openForLoading(fileName);
+ if (in) {
+ SavegameMetadata meta;
+ if (get_savegame_metadata(in, meta)) {
+ Graphics::skipThumbnail(*in);
+ valid = true;
+ }
+ }
+ } else {
+ out = saveFileMan->openForSaving(fileName);
+ if (out) {
+ Common::String saveName;
+ if (saveNo != kAutoSaveId) {
+ saveName = getRamaSaveName(s, saveNo - kSaveIdShift);
+ }
+ Common::ScopedPtr<Common::SeekableReadStream> versionFile(SearchMan.createReadStreamForMember("VERSION"));
+ const Common::String gameVersion = versionFile->readLine();
+ set_savegame_metadata(out, saveName, gameVersion);
+ valid = true;
+ }
+ }
+
+ if (valid) {
+ uint handle = findFreeFileHandle(s);
+ s->_fileHandles[handle]._in = in;
+ s->_fileHandles[handle]._out = out;
+ s->_fileHandles[handle]._name = "-scummvm-save-";
+ return make_reg(0, handle);
+ }
+ }
}
// See kMakeSaveCatName
@@ -456,6 +498,27 @@ reg_t kFileIOOpen(EngineState *s, int argc, reg_t *argv) {
if (isSaveCatalogue(name)) {
const bool exists = saveCatalogueExists(name);
if (exists) {
+ // KQ7 & RAMA read out game information from catalogues directly
+ // instead of using the standard kSaveGetFiles function
+ if (name == "kq7cdsg.cat" || name == "ramasg.cat") {
+ if (mode == kFileOpenModeOpenOrCreate || mode == kFileOpenModeCreate) {
+ // Suppress creation of the catalogue file, since it is not necessary
+ debugC(kDebugLevelFile, "Not creating unused file %s", name.c_str());
+ return SIGNAL_REG;
+ } else if (mode == kFileOpenModeOpenOrFail) {
+ const uint handle = findFreeFileHandle(s);
+
+ if (name == "kq7cdsg.cat") {
+ s->_fileHandles[handle]._in = makeCatalogue(10, kMaxSaveNameLength, "", false);
+ } else {
+ s->_fileHandles[handle]._in = makeCatalogue(100, 20, "ramasg.%d", true);
+ }
+ s->_fileHandles[handle]._out = nullptr;
+ s->_fileHandles[handle]._name = "";
+ return make_reg(0, handle);
+ }
+ }
+
// Dummy handle is used to represent the catalogue and ignore any
// direct game script writes
return make_reg(0, kVirtualFileHandleSci32Save);
@@ -753,10 +816,24 @@ reg_t kFileIOExists(EngineState *s, int argc, reg_t *argv) {
return saveCatalogueExists(name) ? TRUE_REG : NULL_REG;
}
- // LSL7 checks to see if the autosave save exists when deciding whether to
- // go to the main menu or not on startup
+ int findSaveNo = -1;
+
if (g_sci->getGameId() == GID_LSL7 && name == "autosvsg.000") {
- return g_sci->getSaveFileManager()->listSavefiles(g_sci->getSavegameName(0)).empty() ? NULL_REG : TRUE_REG;
+ // LSL7 checks to see if the autosave save exists when deciding whether
+ // to go to the main menu or not on startup
+ findSaveNo = kAutoSaveId;
+ } else if (g_sci->getGameId() == GID_RAMA) {
+ // RAMA checks to see if save game files exist before showing them in
+ // the native save/load dialogue
+ if (name == "autorama.sg") {
+ findSaveNo = kAutoSaveId;
+ } else if (sscanf(name.c_str(), "ramasg.%i", &findSaveNo) == 1) {
+ findSaveNo += kSaveIdShift;
+ }
+ }
+
+ if (findSaveNo != -1) {
+ return g_sci->getSaveFileManager()->listSavefiles(g_sci->getSavegameName(findSaveNo)).empty() ? NULL_REG : TRUE_REG;
}
#endif
@@ -859,24 +936,48 @@ reg_t kFileIOWriteByte(EngineState *s, int argc, reg_t *argv) {
}
reg_t kFileIOReadWord(EngineState *s, int argc, reg_t *argv) {
- FileHandle *f = getFileFromHandle(s, argv[0].toUint16());
+ const uint16 handle = argv[0].toUint16();
+ FileHandle *f = getFileFromHandle(s, handle);
if (!f)
- return NULL_REG;
- return make_reg(0, f->_in->readUint16LE());
+ return s->r_acc;
+
+ reg_t value;
+ if (s->_fileHandles[handle]._name == "-scummvm-save-") {
+ value._segment = f->_in->readUint16LE();
+ value._offset = f->_in->readUint16LE();
+ } else {
+ value = make_reg(0, f->_in->readUint16LE());
+ }
+
+ if (f->_in->err()) {
+ return s->r_acc;
+ }
+
+ return value;
}
reg_t kFileIOWriteWord(EngineState *s, int argc, reg_t *argv) {
- uint16 handle = argv[0].toUint16();
+ const uint16 handle = argv[0].toUint16();
-#ifdef ENABLE_SCI32
if (handle == kVirtualFileHandleSci32Save) {
return s->r_acc;
}
-#endif
FileHandle *f = getFileFromHandle(s, handle);
- if (f)
+ if (!f) {
+ return s->r_acc;
+ }
+
+ if (s->_fileHandles[handle]._name == "-scummvm-save-") {
+ f->_out->writeUint16LE(argv[1]._segment);
+ f->_out->writeUint16LE(argv[1]._offset);
+ } else {
+ if (argv[1].isPointer()) {
+ error("Attempt to write non-number %04x:%04x", PRINT_REG(argv[1]));
+ }
f->_out->writeUint16LE(argv[1].toUint16());
+ }
+
return s->r_acc;
}
diff --git a/engines/sci/engine/script_patches.cpp b/engines/sci/engine/script_patches.cpp
index a5d2a67..b12b63b 100644
--- a/engines/sci/engine/script_patches.cpp
+++ b/engines/sci/engine/script_patches.cpp
@@ -138,6 +138,8 @@ static const char *const selectorNameTable[] = {
"readWord", // LSL7, Phant1, Torin
"flag", // PQ4
"select", // PQ4
+ "handle", // RAMA
+ "saveFilePtr", // RAMA
#endif
NULL
};
@@ -199,7 +201,9 @@ enum ScriptPatcherSelectors {
SELECTOR_setScaler,
SELECTOR_readWord,
SELECTOR_flag,
- SELECTOR_select
+ SELECTOR_select,
+ SELECTOR_handle,
+ SELECTOR_saveFilePtr
#endif
};
@@ -6218,9 +6222,37 @@ static const uint16 ramaBenchmarkPatch[] = {
PATCH_END
};
-// script, description, signature patch
+// RAMA uses a custom save game format that game scripts read and write
+// manually. The save game format serialises object references, which in the
+// original engine could be done just by writing int16s (since object references
+// were just 16-bit indexes), but in ScummVM we have to write the full 32-bit
+// reg_t. We hijack kFileIOReadWord/kFileIOWriteWord to do this for us, but we
+// need the game to agree to use those kFileIO calls instead of doing raw reads
+// and creating its own numbers, as it tries to do here in
+// `SaveManager::readWord`.
+static const uint16 ramaSerializeRegTSignature1[] = {
+ SIG_MAGICDWORD,
+ 0x38, SIG_SELECTOR16(newWith), // pushi $10b (newWith)
+ 0x7a, // push2
+ 0x7a, // push2
+ 0x72, SIG_UINT16(0x00), // lofsa ""
+ 0x36, // push
+ 0x51, 0x0f, // class Str
+ SIG_END
+};
+
+static const uint16 ramaSerializeRegTPatch1[] = {
+ 0x38, PATCH_SELECTOR16(readWord), // pushi readWord
+ 0x76, // push0
+ 0x62, PATCH_SELECTOR16(saveFilePtr), // pToa saveFilePtr
+ 0x4a, PATCH_UINT16(0x04), // send 4
+ 0x48, // ret
+ PATCH_END
+};
+
static const SciScriptPatcherEntry ramaSignatures[] = {
- { true, 64908, "disable video benchmarking", 1, ramaBenchmarkSignature, ramaBenchmarkPatch },
+ { true, 85, "fix SaveManager to use normal readWord calls", 1, ramaSerializeRegTSignature1, ramaSerializeRegTPatch1 },
+ { true, 64908, "disable video benchmarking", 1, ramaBenchmarkSignature, ramaBenchmarkPatch },
SCI_SIGNATUREENTRY_TERMINATOR
};
diff --git a/engines/sci/engine/selector.cpp b/engines/sci/engine/selector.cpp
index d310b0f..d4d170f 100644
--- a/engines/sci/engine/selector.cpp
+++ b/engines/sci/engine/selector.cpp
@@ -227,6 +227,8 @@ void Kernel::mapSelectors() {
FIND_SELECTOR(num);
FIND_SELECTOR(reallyRestore);
FIND_SELECTOR(bookMark);
+ FIND_SELECTOR(fileNumber);
+ FIND_SELECTOR(description);
#endif
}
diff --git a/engines/sci/engine/selector.h b/engines/sci/engine/selector.h
index 9774114..98fa06c 100644
--- a/engines/sci/engine/selector.h
+++ b/engines/sci/engine/selector.h
@@ -184,6 +184,8 @@ struct SelectorCache {
Selector num; // for Phant2 restore from launcher
Selector reallyRestore; // for Phant2 restore from launcher
Selector bookMark; // for Phant2 auto-save
+ Selector fileNumber; // for RAMA save/load
+ Selector description; // for RAMA save/load
#endif
};
diff --git a/engines/sci/engine/vm.h b/engines/sci/engine/vm.h
index 792287b..83550f8 100644
--- a/engines/sci/engine/vm.h
+++ b/engines/sci/engine/vm.h
@@ -155,6 +155,7 @@ enum GlobalVar {
kGlobalVarTextSpeed = 94, // SCI32; 0 is fastest, 8 is slowest
kGlobalVarGK1Music1 = 102, // 0 to 127
kGlobalVarGK1Music2 = 103, // 0 to 127
+ kGlobalVarRamaCatalogFile = 130,
kGlobalVarLSL6HiresGameFlags = 137,
kGlobalVarGK1NarratorMode = 166, // 0 for text, 1 for speech
kGlobalVarPhant1MusicVolume = 187, // 0 to 15
Commit: a63f516df02b5879c51b923b2f1751a312ece26b
https://github.com/scummvm/scummvm/commit/a63f516df02b5879c51b923b2f1751a312ece26b
Author: Colin Snover (github.com at zetafleet.com)
Date: 2017-09-23T20:57:00-05:00
Commit Message:
SCI32: Replace magic number with kAutoSaveId in QFG4 save code
Changed paths:
engines/sci/engine/kfile.cpp
diff --git a/engines/sci/engine/kfile.cpp b/engines/sci/engine/kfile.cpp
index d8ef919..db7bbce 100644
--- a/engines/sci/engine/kfile.cpp
+++ b/engines/sci/engine/kfile.cpp
@@ -1337,7 +1337,7 @@ reg_t kSaveGame32(EngineState *s, int argc, reg_t *argv) {
s->_msgState->getMessage(0, autoSaveNameTuple, autoSaveNameId);
if (saveDescription == autoSaveName.toString()) {
- saveNo = 0;
+ saveNo = kAutoSaveId;
}
s->_segMan->freeArray(autoSaveNameId);
Commit: 475bb5f91a02899db9fb3ced0dc564709e5946c0
https://github.com/scummvm/scummvm/commit/475bb5f91a02899db9fb3ced0dc564709e5946c0
Author: Colin Snover (github.com at zetafleet.com)
Date: 2017-09-23T20:57:01-05:00
Commit Message:
SCI32: Remove unnecessary use of strncmp
One of the strings is a static string of the same size as the size
passed to strncmp so there is no point in not just using strcmp.
Changed paths:
engines/sci/engine/guest_additions.cpp
diff --git a/engines/sci/engine/guest_additions.cpp b/engines/sci/engine/guest_additions.cpp
index e74a5d6..6a1c510 100644
--- a/engines/sci/engine/guest_additions.cpp
+++ b/engines/sci/engine/guest_additions.cpp
@@ -446,7 +446,7 @@ void GuestAdditions::patchGameSaveRestorePhant2(Script &script) const {
for (ObjMap::const_iterator it = objects.begin(); it != objects.end(); ++it) {
const Object &obj = it->_value;
- if (strncmp(_segMan->derefString(obj.getNameSelector()), "srGetGame", 9) != 0) {
+ if (strcmp(_segMan->derefString(obj.getNameSelector()), "srGetGame") != 0) {
continue;
}
Commit: 9c28c25b67c82f9a4fae6b9e85b73506b5bb6226
https://github.com/scummvm/scummvm/commit/9c28c25b67c82f9a4fae6b9e85b73506b5bb6226
Author: Colin Snover (github.com at zetafleet.com)
Date: 2017-09-23T20:57:01-05:00
Commit Message:
SCI32: Add guest additions save/restore support for RAMA
Changed paths:
engines/sci/engine/guest_additions.cpp
engines/sci/engine/guest_additions.h
engines/sci/engine/selector.cpp
engines/sci/engine/selector.h
diff --git a/engines/sci/engine/guest_additions.cpp b/engines/sci/engine/guest_additions.cpp
index 6a1c510..6d7bbea 100644
--- a/engines/sci/engine/guest_additions.cpp
+++ b/engines/sci/engine/guest_additions.cpp
@@ -243,7 +243,12 @@ void GuestAdditions::instantiateScriptHook(Script &script, const bool ignoreDela
// segment table to change (versus save games that are not patched),
// breaking persistent objects (like the control panel in SQ6) which
// require reg_ts created during game startup to always be the same
- patchGameSaveRestoreSCI32(script);
+
+ if (g_sci->getGameId() == GID_RAMA) {
+ patchGameSaveRestoreRama(script);
+ } else {
+ patchGameSaveRestoreSCI32(script);
+ }
}
}
@@ -392,25 +397,7 @@ static const byte SRDialogPatch[] = {
};
void GuestAdditions::patchGameSaveRestoreSCI32(Script &script) const {
- const ObjMap &objMap = script.getObjectMap();
- for (ObjMap::const_iterator it = objMap.begin(); it != objMap.end(); ++it) {
- const Object &obj = it->_value;
- if (strncmp(_segMan->getObjectName(obj.getPos()), "SRDialog", 8) != 0) {
- continue;
- }
-
- const uint16 methodCount = obj.getMethodCount();
- for (uint16 methodNr = 0; methodNr < methodCount; ++methodNr) {
- const uint16 selectorId = obj.getFuncSelector(methodNr);
- const Common::String methodName = _kernel->getSelectorName(selectorId);
- if (methodName == "doit") {
- const reg_t methodAddress = obj.getFunction(methodNr);
- byte *patchPtr = const_cast<byte *>(script.getBuf(methodAddress.getOffset()));
- memcpy(patchPtr, SRDialogPatch, sizeof(SRDialogPatch));
- break;
- }
- }
- }
+ patchSRDialogDoit(script, "SRDialog", SRDialogPatch, sizeof(SRDialogPatch));
}
static const byte SRTorinPatch[] = {
@@ -461,6 +448,50 @@ void GuestAdditions::patchGameSaveRestorePhant2(Script &script) const {
}
}
+static const byte RamaSRDialogPatch[] = {
+ 0x78, // push1
+ 0x7c, // pushSelf
+ 0x43, kScummVMSaveLoadId, 0x02, 0x00, // callk kScummVMSaveLoad, 0
+ 0x48 // ret
+};
+
+static const int RamaSRDialogUint16Offsets[] = { 4 };
+
+void GuestAdditions::patchGameSaveRestoreRama(Script &script) const {
+ patchSRDialogDoit(script, "Save", RamaSRDialogPatch, sizeof(RamaSRDialogPatch), RamaSRDialogUint16Offsets, ARRAYSIZE(RamaSRDialogUint16Offsets));
+ patchSRDialogDoit(script, "Restore", RamaSRDialogPatch, sizeof(RamaSRDialogPatch), RamaSRDialogUint16Offsets, ARRAYSIZE(RamaSRDialogUint16Offsets));
+}
+
+void GuestAdditions::patchSRDialogDoit(Script &script, const char *const objectName, const byte *patchData, const int patchSize, const int *uint16Offsets, const uint numOffsets) const {
+ const ObjMap &objMap = script.getObjectMap();
+ for (ObjMap::const_iterator it = objMap.begin(); it != objMap.end(); ++it) {
+ const Object &obj = it->_value;
+ if (strcmp(_segMan->getObjectName(obj.getPos()), objectName) != 0) {
+ continue;
+ }
+
+ const uint16 methodCount = obj.getMethodCount();
+ for (uint16 methodNr = 0; methodNr < methodCount; ++methodNr) {
+ const uint16 selectorId = obj.getFuncSelector(methodNr);
+ const Common::String methodName = _kernel->getSelectorName(selectorId);
+ if (methodName == "doit") {
+ const reg_t methodAddress = obj.getFunction(methodNr);
+ byte *patchPtr = const_cast<byte *>(script.getBuf(methodAddress.getOffset()));
+ memcpy(patchPtr, patchData, patchSize);
+
+ if (g_sci->isBE()) {
+ for (uint i = 0; i < numOffsets; ++i) {
+ const int offset = uint16Offsets[i];
+ SWAP(patchPtr[offset], patchPtr[offset + 1]);
+ }
+ }
+
+ return;
+ }
+ }
+ }
+}
+
reg_t GuestAdditions::kScummVMSaveLoad(EngineState *s, int argc, reg_t *argv) const {
if (g_sci->getGameId() == GID_PHANTASMAGORIA2) {
return promptSaveRestorePhant2(s, argc, argv);
@@ -470,6 +501,10 @@ reg_t GuestAdditions::kScummVMSaveLoad(EngineState *s, int argc, reg_t *argv) co
return promptSaveRestoreTorin(s, argc, argv);
}
+ if (g_sci->getGameId() == GID_RAMA) {
+ return promptSaveRestoreRama(s, argc, argv);
+ }
+
return promptSaveRestoreDefault(s, argc, argv);
}
@@ -522,6 +557,87 @@ reg_t GuestAdditions::promptSaveRestorePhant2(EngineState *s, int argc, reg_t *a
return make_reg(0, saveNo);
}
+reg_t GuestAdditions::promptSaveRestoreRama(EngineState *s, int argc, reg_t *argv) const {
+ assert(argc == 1);
+ const bool isSave = (strcmp(_segMan->getObjectName(argv[0]), "Save") == 0);
+
+ const reg_t editor = _segMan->findObjectByName("editI");
+ reg_t outDescription = readSelector(_segMan, editor, SELECTOR(text));
+ if (!_segMan->isValidAddr(outDescription, SEG_TYPE_ARRAY)) {
+ _segMan->allocateArray(kArrayTypeString, 0, &outDescription);
+ writeSelector(_segMan, editor, SELECTOR(text), outDescription);
+ }
+
+ int saveNo = runSaveRestore(isSave, outDescription, s->_delayedRestoreGameId);
+ int saveIndex = -1;
+ if (saveNo != -1) {
+ // The save number returned by runSaveRestore is a SCI save number
+ // because normally SRDialogs return the save ID, but RAMA returns the
+ // save game's index in the save game list instead, so we need to
+ // convert back to the ScummVM save number here to find the correct
+ // index
+ saveNo += kSaveIdShift;
+
+ Common::Array<SavegameDesc> saves;
+ listSavegames(saves);
+ saveIndex = findSavegame(saves, saveNo);
+
+ if (isSave) {
+ bool resetCatalogFile = false;
+ const Common::String saveGameName = _segMan->getString(outDescription);
+
+ // The original game save/restore code returns index 0 when a game
+ // is created that does not already exist and then the scripts find
+ // the next hole and insert there, but the ScummVM GUI works
+ // differently and allows users to insert a game wherever they want,
+ // so we need to force the save game to exist in advance so RAMA's
+ // save code will successfully put it where we want it
+ if (saveIndex == -1) {
+ // We need to touch the save file just so it exists here, since
+ // otherwise the game will not let us save to the new save slot
+ // (it will try to come up with a brand new slot instead)
+ Common::ScopedPtr<Common::OutSaveFile> out(g_sci->getSaveFileManager()->openForSaving(g_sci->getSavegameName(saveNo)));
+ set_savegame_metadata(out.get(), saveGameName, "");
+
+ // We have to re-retrieve saves and find the index instead of
+ // assuming the newest save will be in index 0 because save game
+ // times are not guaranteed to be steady
+ saves.clear();
+ listSavegames(saves);
+ saveIndex = findSavegame(saves, saveNo);
+ if (saveIndex == -1) {
+ warning("Stub save not found when trying to save a new game to slot %d", saveNo);
+ } else {
+ // Kick the CatalogFile into believing that this new save
+ // game exists already, otherwise it the game will not
+ // actually save into the new save
+ resetCatalogFile = true;
+ }
+ } else if (strncmp(saveGameName.c_str(), saves[saveIndex].name, kMaxSaveNameLength) != 0) {
+ // The game doesn't let the save game name change for the same
+ // slot, but ScummVM's GUI does, so force the new name into the
+ // save file metadata if it has changed so it actually makes it
+ // into the save game
+ Common::ScopedPtr<Common::OutSaveFile> out(g_sci->getSaveFileManager()->openForSaving(g_sci->getSavegameName(saveNo)));
+ set_savegame_metadata(out.get(), saveGameName, "");
+ resetCatalogFile = true;
+ }
+
+ if (resetCatalogFile) {
+ const reg_t catalogFileId = _state->variables[VAR_GLOBAL][kGlobalVarRamaCatalogFile];
+ if (catalogFileId.isNull()) {
+ warning("Could not find CatalogFile when saving from launcher");
+ }
+ reg_t args[] = { NULL_REG };
+ invokeSelector(catalogFileId, SELECTOR(dispose));
+ invokeSelector(catalogFileId, SELECTOR(init), ARRAYSIZE(args), args);
+ }
+ }
+ }
+
+ return make_reg(0, saveIndex);
+}
+
int GuestAdditions::runSaveRestore(const bool isSave, reg_t outDescription, const int forcedSaveNo) const {
int saveNo;
Common::String descriptionString;
@@ -564,7 +680,7 @@ int GuestAdditions::runSaveRestore(const bool isSave, reg_t outDescription, cons
// number here to match what would come from the normal SCI save/restore
// dialog. There is additional special code for handling the autosave
// game inside of kRestoreGame32.
- --saveNo;
+ saveNo -= kSaveIdShift;
}
return saveNo;
diff --git a/engines/sci/engine/guest_additions.h b/engines/sci/engine/guest_additions.h
index 64bd667..a1043a8 100644
--- a/engines/sci/engine/guest_additions.h
+++ b/engines/sci/engine/guest_additions.h
@@ -220,6 +220,17 @@ private:
void patchGameSaveRestorePhant2(Script &script) const;
/**
+ * Patches the ScummVM save/load dialogue into RAMA.
+ */
+ void patchGameSaveRestoreRama(Script &script) const;
+
+ /**
+ * Patches the `doit` method of an SRDialog object with the given name
+ * using the given patch data.
+ */
+ void patchSRDialogDoit(Script &script, const char *const objectName, const byte *patchData, const int patchSize, const int *uint16Offsets = nullptr, const uint numOffsets = 0) const;
+
+ /**
* Prompts for a save game and returns it to game scripts using default
* SRDialog game class semantics.
*/
@@ -238,6 +249,12 @@ private:
reg_t promptSaveRestorePhant2(EngineState *s, int argc, reg_t *argv) const;
/**
+ * Prompts for a save game and returns it to game scripts using RAMA's
+ * custom SRDialog class semantics.
+ */
+ reg_t promptSaveRestoreRama(EngineState *s, int argc, reg_t *argv) const;
+
+ /**
* Prompts the user to save or load a game.
*
* @param isSave If true, the prompt is for saving.
diff --git a/engines/sci/engine/selector.cpp b/engines/sci/engine/selector.cpp
index d4d170f..1feb808 100644
--- a/engines/sci/engine/selector.cpp
+++ b/engines/sci/engine/selector.cpp
@@ -229,6 +229,7 @@ void Kernel::mapSelectors() {
FIND_SELECTOR(bookMark);
FIND_SELECTOR(fileNumber);
FIND_SELECTOR(description);
+ FIND_SELECTOR(dispose);
#endif
}
diff --git a/engines/sci/engine/selector.h b/engines/sci/engine/selector.h
index 98fa06c..1603ca9 100644
--- a/engines/sci/engine/selector.h
+++ b/engines/sci/engine/selector.h
@@ -186,6 +186,7 @@ struct SelectorCache {
Selector bookMark; // for Phant2 auto-save
Selector fileNumber; // for RAMA save/load
Selector description; // for RAMA save/load
+ Selector dispose; // for RAMA save/load save from launcher
#endif
};
Commit: 602de8363b0c35e5fa4525432cc176b1843b808d
https://github.com/scummvm/scummvm/commit/602de8363b0c35e5fa4525432cc176b1843b808d
Author: Colin Snover (github.com at zetafleet.com)
Date: 2017-09-23T20:57:02-05:00
Commit Message:
SCI32: Fix infinite reload in RAMA when loading from launcher
Changed paths:
engines/sci/engine/guest_additions.cpp
diff --git a/engines/sci/engine/guest_additions.cpp b/engines/sci/engine/guest_additions.cpp
index 6d7bbea..eee26ac 100644
--- a/engines/sci/engine/guest_additions.cpp
+++ b/engines/sci/engine/guest_additions.cpp
@@ -740,6 +740,14 @@ bool GuestAdditions::restoreFromLauncher() const {
// which will automatically return the `_delayedRestoreGameId` instead
// of prompting the user for a save game
invokeSelector(g_sci->getGameObject(), SELECTOR(restore));
+
+ // The normal save game system resets _delayedRestoreGameId with a
+ // call to `EngineState::reset`, but RAMA uses a custom save game
+ // system which does not reset the engine, so we need to clear the
+ // ID here or the engine will just try to restore the game forever
+ if (g_sci->getGameId() == GID_RAMA) {
+ _state->_delayedRestoreGameId = -1;
+ }
}
_restoring = false;
Commit: 6972c9ae46acba424bcdfb5a002ca03b0edf2874
https://github.com/scummvm/scummvm/commit/6972c9ae46acba424bcdfb5a002ca03b0edf2874
Author: Colin Snover (github.com at zetafleet.com)
Date: 2017-09-23T20:57:02-05:00
Commit Message:
SCI32: Fix bad default text scaling in RAMA
Changed paths:
engines/sci/engine/script_patches.cpp
diff --git a/engines/sci/engine/script_patches.cpp b/engines/sci/engine/script_patches.cpp
index b12b63b..0937e9f 100644
--- a/engines/sci/engine/script_patches.cpp
+++ b/engines/sci/engine/script_patches.cpp
@@ -6222,6 +6222,25 @@ static const uint16 ramaBenchmarkPatch[] = {
PATCH_END
};
+// RAMA initialises the font system with an incorrect text resolution (it uses
+// the resolution from Phant1) which causes text to be scaled incorrectly.
+static const uint16 ramaTextResolutionSignature[] = {
+ 0x39, 0x03, // pushi 3
+ 0x78, // push1
+ SIG_MAGICDWORD,
+ 0x38, SIG_UINT16(0x276), // pushi 630
+ 0x38, SIG_UINT16(0x1c2), // pushi 450
+ 0x43, 0x49, SIG_UINT16(0x06), // callk Font, 6
+ SIG_END
+};
+
+static const uint16 ramaTextResolutionPatch[] = {
+ PATCH_ADDTOOFFSET(+3), // pushi 3, push1
+ 0x38, PATCH_UINT16(0x280), // pushi 640
+ 0x38, PATCH_UINT16(0x1e0), // pushi 480
+ PATCH_END
+};
+
// RAMA uses a custom save game format that game scripts read and write
// manually. The save game format serialises object references, which in the
// original engine could be done just by writing int16s (since object references
@@ -6251,6 +6270,7 @@ static const uint16 ramaSerializeRegTPatch1[] = {
};
static const SciScriptPatcherEntry ramaSignatures[] = {
+ { true, 0, "fix bad text resolution", 1, ramaTextResolutionSignature, ramaTextResolutionPatch },
{ true, 85, "fix SaveManager to use normal readWord calls", 1, ramaSerializeRegTSignature1, ramaSerializeRegTPatch1 },
{ true, 64908, "disable video benchmarking", 1, ramaBenchmarkSignature, ramaBenchmarkPatch },
SCI_SIGNATUREENTRY_TERMINATOR
Commit: dced2fb9f54bafa0770e479e0088021f59fc0a16
https://github.com/scummvm/scummvm/commit/dced2fb9f54bafa0770e479e0088021f59fc0a16
Author: Colin Snover (github.com at zetafleet.com)
Date: 2017-09-23T20:57:03-05:00
Commit Message:
SCI32: Fix handling of array copies
kArrayCopy would perform an unnecessary memory copy of the source
array, the treatment of the count value as unsigned was clearly
not correct since it was being sign-extended and checked against
-1.
Changed paths:
engines/sci/engine/klists.cpp
engines/sci/engine/segment.h
diff --git a/engines/sci/engine/klists.cpp b/engines/sci/engine/klists.cpp
index 2f66e51..2ca5a65 100644
--- a/engines/sci/engine/klists.cpp
+++ b/engines/sci/engine/klists.cpp
@@ -859,19 +859,19 @@ reg_t kArrayFill(EngineState *s, int argc, reg_t *argv) {
reg_t kArrayCopy(EngineState *s, int argc, reg_t *argv) {
SciArray &target = *s->_segMan->lookupArray(argv[0]);
const uint16 targetIndex = argv[1].toUint16();
+ const uint16 sourceIndex = argv[3].toUint16();
+ const int16 count = argv[4].toSint16();
- SciArray source;
- // String copies may be made from static script data
if (!s->_segMan->isArray(argv[2])) {
+ // String copies may be made from static script data
+ SciArray source;
source.setType(kArrayTypeString);
source.fromString(s->_segMan->getString(argv[2]));
+ target.copy(source, sourceIndex, targetIndex, count);
} else {
- source = *s->_segMan->lookupArray(argv[2]);
+ target.copy(*s->_segMan->lookupArray(argv[2]), sourceIndex, targetIndex, count);
}
- const uint16 sourceIndex = argv[3].toUint16();
- const uint16 count = argv[4].toUint16();
- target.copy(source, sourceIndex, targetIndex, count);
return argv[0];
}
diff --git a/engines/sci/engine/segment.h b/engines/sci/engine/segment.h
index a0c9f7f..2b2e946 100644
--- a/engines/sci/engine/segment.h
+++ b/engines/sci/engine/segment.h
@@ -761,12 +761,12 @@ public:
* Copies values from the source array. Both arrays will be grown if needed
* to prevent out-of-bounds reads/writes.
*/
- void copy(SciArray &source, const uint16 sourceIndex, const uint16 targetIndex, uint16 count) {
- if (count == 65535 /* -1 */) {
+ void copy(SciArray &source, const uint16 sourceIndex, const uint16 targetIndex, int16 count) {
+ if (count == -1) {
count = source.size() - sourceIndex;
}
- if (!count) {
+ if (count < 1) {
return;
}
Commit: 643a572610377d36c3d43d44c7487dd15b36e5e2
https://github.com/scummvm/scummvm/commit/643a572610377d36c3d43d44c7487dd15b36e5e2
Author: Colin Snover (github.com at zetafleet.com)
Date: 2017-09-23T20:57:03-05:00
Commit Message:
Revert "SCI: Fix Warnings about Copy Constructor Failing to Call Base Class."
This reverts commit d2dd942ad79df0c4ac9d841fb5b0919d260bb79c.
The Serializable class is an interface class and has only the
empty default copy constructor. There is no reason to call this
explicitly. Whatever is warning about this should be fixed to stop
warning about it, rather than adding such unnecessary calls to the
engine code.
Changed paths:
engines/sci/engine/segment.h
diff --git a/engines/sci/engine/segment.h b/engines/sci/engine/segment.h
index 2b2e946..d6e2dd3 100644
--- a/engines/sci/engine/segment.h
+++ b/engines/sci/engine/segment.h
@@ -436,7 +436,7 @@ public:
_size(0),
_data(nullptr) {}
- SciArray(const SciArray &array) : Common::Serializable() {
+ SciArray(const SciArray &array) {
_type = array._type;
_size = array._size;
_elementSize = array._elementSize;
@@ -972,7 +972,7 @@ public:
inline SciBitmap() : _data(nullptr), _dataSize(0), _gc(true) {}
- inline SciBitmap(const SciBitmap &other) : Common::Serializable() {
+ inline SciBitmap(const SciBitmap &other) {
_dataSize = other._dataSize;
_data = (byte *)malloc(other._dataSize);
memcpy(_data, other._data, other._dataSize);
Commit: f83c61fcb62567f1a7d1916e018117eee619b322
https://github.com/scummvm/scummvm/commit/f83c61fcb62567f1a7d1916e018117eee619b322
Author: Colin Snover (github.com at zetafleet.com)
Date: 2017-09-23T20:57:03-05:00
Commit Message:
SCI32: Fix relocation of locals in SCI3
Somehow, up until trying to view an encrypted data cube in RAMA,
the missing relocation of locals did not seem to cause any trouble
in any of the other SCI3 games.
Changed paths:
engines/sci/engine/script.cpp
engines/sci/engine/script.h
diff --git a/engines/sci/engine/script.cpp b/engines/sci/engine/script.cpp
index a0243de..c1da3f2 100644
--- a/engines/sci/engine/script.cpp
+++ b/engines/sci/engine/script.cpp
@@ -698,9 +698,9 @@ int Script::relocateOffsetSci3(uint32 offset) const {
}
#endif
-bool Script::relocateLocal(SegmentId segment, int location) {
+bool Script::relocateLocal(SegmentId segment, int location, uint32 offset) {
if (_localsBlock)
- return relocateBlock(_localsBlock->_locals, _localsOffset, segment, location, getHeapOffset());
+ return relocateBlock(_localsBlock->_locals, _localsOffset, segment, location, offset);
else
return false;
}
@@ -804,7 +804,7 @@ void Script::relocateSci0Sci21(const SegmentId segmentId) {
// We only relocate locals and objects here, and ignore relocation of
// code blocks. In SCI1.1 and newer versions, only locals and objects
// are relocated.
- if (!relocateLocal(segmentId, pos)) {
+ if (!relocateLocal(segmentId, pos, getHeapOffset())) {
// Not a local? It's probably an object or code block. If it's an
// object, relocate it.
const ObjMap::iterator end = _objects.end();
@@ -817,19 +817,23 @@ void Script::relocateSci0Sci21(const SegmentId segmentId) {
#ifdef ENABLE_SCI32
void Script::relocateSci3(const SegmentId segmentId) {
- SciSpan<const byte> relocStart = _buf->subspan(_buf->getUint32SEAt(8));
+ SciSpan<const byte> relocEntry = _buf->subspan(_buf->getUint32SEAt(8));
const uint relocCount = _buf->getUint16SEAt(18);
- ObjMap::iterator it;
- for (it = _objects.begin(); it != _objects.end(); ++it) {
- SciSpan<const byte> seeker = relocStart;
- for (uint i = 0; i < relocCount; ++i) {
- it->_value.relocateSci3(segmentId,
- seeker.getUint32SEAt(0),
- seeker.getUint32SEAt(4),
- _script.size());
- seeker += 10;
+ for (uint i = 0; i < relocCount; ++i) {
+ const uint location = relocEntry.getUint32SEAt(0);
+ const uint offset = relocEntry.getUint32SEAt(4);
+
+ if (!relocateLocal(segmentId, location, offset)) {
+ const ObjMap::iterator end = _objects.end();
+ for (ObjMap::iterator it = _objects.begin(); it != end; ++it) {
+ if (it->_value.relocateSci3(segmentId, location, offset, _script.size())) {
+ break;
+ }
+ }
}
+
+ relocEntry += 10;
}
}
#endif
@@ -973,7 +977,7 @@ void Script::initializeLocals(SegManager *segMan) {
const SciSpan<const byte> base = _buf->subspan(getLocalsOffset());
for (uint16 i = 0; i < getLocalsCount(); i++)
- locals->_locals[i] = make_reg(0, base.getUint16SEAt(i * 2));
+ locals->_locals[i] = make_reg(0, base.getUint16SEAt(i * sizeof(uint16)));
} else {
// In SCI0 early, locals are set at run time, thus zero them all here
for (uint16 i = 0; i < getLocalsCount(); i++)
diff --git a/engines/sci/engine/script.h b/engines/sci/engine/script.h
index 1befef1..f4afca7 100644
--- a/engines/sci/engine/script.h
+++ b/engines/sci/engine/script.h
@@ -318,7 +318,7 @@ private:
void relocateSci3(const SegmentId segmentId);
#endif
- bool relocateLocal(SegmentId segment, int location);
+ bool relocateLocal(SegmentId segment, int location, uint32 offset);
#ifdef ENABLE_SCI32
/**
Commit: d2364cb9bb2cf846b35bf7a42dca47426fae2820
https://github.com/scummvm/scummvm/commit/d2364cb9bb2cf846b35bf7a42dca47426fae2820
Author: Colin Snover (github.com at zetafleet.com)
Date: 2017-09-23T20:57:04-05:00
Commit Message:
SCI32: Fix bad data cube text priority calculation in RAMA
Changed paths:
engines/sci/engine/script_patches.cpp
diff --git a/engines/sci/engine/script_patches.cpp b/engines/sci/engine/script_patches.cpp
index 0937e9f..78b51c1 100644
--- a/engines/sci/engine/script_patches.cpp
+++ b/engines/sci/engine/script_patches.cpp
@@ -140,6 +140,8 @@ static const char *const selectorNameTable[] = {
"select", // PQ4
"handle", // RAMA
"saveFilePtr", // RAMA
+ "priority", // RAMA
+ "plane", // RAMA
#endif
NULL
};
@@ -203,7 +205,9 @@ enum ScriptPatcherSelectors {
SELECTOR_flag,
SELECTOR_select,
SELECTOR_handle,
- SELECTOR_saveFilePtr
+ SELECTOR_saveFilePtr,
+ SELECTOR_priority,
+ SELECTOR_plane
#endif
};
@@ -6269,8 +6273,34 @@ static const uint16 ramaSerializeRegTPatch1[] = {
PATCH_END
};
+// When opening a datacube on the pocket computer, `DocReader::init` will try
+// to perform arithmetic on a pointer to `thighComputer::plane` and then use the
+// resulting value as the priority for the DocReader. This happened to work in
+// SSCI because the plane pointer would just be a high numeric value, but
+// ScummVM needs an actual number, not a pointer.
+// Applies to at least: US English
+static const uint16 ramaDocReaderInitSignature[] = {
+ 0x39, SIG_SELECTOR8(priority), // pushi $1a (priority)
+ 0x78, // push1
+ 0x39, SIG_SELECTOR8(plane), // pushi $19 (plane)
+ 0x76, // push0
+ 0x7a, // push2
+ SIG_MAGICDWORD,
+ 0x39, 0x2c, // pushi 44
+ 0x76, // push0
+ 0x43, 0x02, SIG_UINT16(0x04), // callk ScriptID, 4
+ SIG_END
+};
+
+static const uint16 ramaDocReaderInitPatch[] = {
+ PATCH_ADDTOOFFSET(+3), // pushi priority, push1
+ 0x39, PATCH_SELECTOR8(priority), // pushi priority
+ PATCH_END
+};
+
static const SciScriptPatcherEntry ramaSignatures[] = {
{ true, 0, "fix bad text resolution", 1, ramaTextResolutionSignature, ramaTextResolutionPatch },
+ { true, 55, "fix bad DocReader::init priority calculation", 1, ramaDocReaderInitSignature, ramaDocReaderInitPatch },
{ true, 85, "fix SaveManager to use normal readWord calls", 1, ramaSerializeRegTSignature1, ramaSerializeRegTPatch1 },
{ true, 64908, "disable video benchmarking", 1, ramaBenchmarkSignature, ramaBenchmarkPatch },
SCI_SIGNATUREENTRY_TERMINATOR
More information about the Scummvm-git-logs
mailing list