Skip to content

Commit

Permalink
SCI32: Emulate Shivers 1 game score metadata
Browse files Browse the repository at this point in the history
  • Loading branch information
csnover committed Sep 30, 2016
1 parent 25c874b commit 2629269
Show file tree
Hide file tree
Showing 6 changed files with 50 additions and 36 deletions.
1 change: 1 addition & 0 deletions engines/sci/engine/file.cpp
Expand Up @@ -335,6 +335,7 @@ bool fillSavegameDesc(const Common::String &filename, SavegameDesc *desc) {
desc->time = meta.saveTime;
desc->version = meta.version;
desc->gameVersion = meta.gameVersion;
desc->score = meta.score;

if (meta.name.lastChar() == '\n')
meta.name.deleteLastChar();
Expand Down
4 changes: 4 additions & 0 deletions engines/sci/engine/file.h
Expand Up @@ -59,6 +59,10 @@ struct SavegameDesc {
int version;
char name[SCI_MAX_SAVENAME_LENGTH];
Common::String gameVersion;
#ifdef ENABLE_SCI32
// Used by Shivers 1
uint32 score;
#endif
};

class FileHandle {
Expand Down
60 changes: 26 additions & 34 deletions engines/sci/engine/kfile.cpp
Expand Up @@ -328,50 +328,42 @@ reg_t kFileIOOpen(EngineState *s, int argc, reg_t *argv) {
}

#ifdef ENABLE_SCI32
// Shivers is trying to store savegame descriptions and current spots in
// separate .SG files, which are hardcoded in the scripts.
// Essentially, there is a normal save file, created by the executable
// and an extra hardcoded save file, created by the game scripts, probably
// because they didn't want to modify the save/load code to add the extra
// information.
// Each slot in the book then has two strings, the save description and a
// description of the current spot that the player is at.
// For now, we don't allow the creation of these files, which means that
// all the spot descriptions next to each slot description will be empty.
// Until a viable solution is found to handle these
// extra files and until the spot description strings are initialized
// correctly, we resort to virtual files in order to make the load screen
// useable. Without this code it is unusable, as the extra information is
// always saved to 0.SG for some reason, but on restore the correct file is
// used. Perhaps the virtual ID is not taken into account when saving.
//
// Future TODO: maintain spot descriptions and show them too, ideally without
// having to return to this logic of extra hardcoded files.
// Shivers stores the name and score of save games in separate %d.SG files,
// which are used by the save/load screen
if (g_sci->getGameId() == GID_SHIVERS && name.hasSuffix(".SG")) {
if (mode == _K_FILE_MODE_OPEN_OR_CREATE || mode == _K_FILE_MODE_CREATE) {
// Game scripts are trying to create a file with the save
// description, stop them here
// 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) {
// Create a virtual file containing the save game description
// and slot number, as the game scripts expect.
int slotNumber;
sscanf(name.c_str(), "%d.SG", &slotNumber);

Common::Array<SavegameDesc> saves;
listSavegames(saves);
int savegameNr = findSavegame(saves, slotNumber);
assert(savegameNr >= 0);
int saveNo;
sscanf(name.c_str(), "%d.SG", &saveNo);

SavegameDesc save;
fillSavegameDesc(g_sci->getSavegameName(saveNo), &save);
Common::String score;
const uint16 lowScore = save.score & 0xFFFF;
const uint16 highScore = save.score >> 16;

if (!highScore) {
score = Common::String::format("%u", lowScore);
} else {
score = Common::String::format("%u%03u", highScore, lowScore);
}

int size = strlen(saves[savegameNr].name) + 2;
char *buf = (char *)malloc(size);
strcpy(buf, saves[savegameNr].name);
buf[size - 1] = 0; // Spot description (empty)
const uint nameLength = strlen(save.name);
const uint size = nameLength + /* \r\n */ 2 + score.size();
char *buffer = (char *)malloc(size);
memcpy(buffer, save.name, nameLength);
buffer[nameLength] = '\r';
buffer[nameLength + 1] = '\n';
memcpy(buffer + nameLength + 2, score.c_str(), score.size());

uint handle = findFreeFileHandle(s);
const uint handle = findFreeFileHandle(s);

s->_fileHandles[handle]._in = new Common::MemoryReadStream((byte *)buf, size, DisposeAfterUse::YES);
s->_fileHandles[handle]._in = new Common::MemoryReadStream((byte *)buffer, size, DisposeAfterUse::YES);
s->_fileHandles[handle]._out = nullptr;
s->_fileHandles[handle]._name = "";

Expand Down
11 changes: 11 additions & 0 deletions engines/sci/engine/savegame.cpp
Expand Up @@ -330,6 +330,17 @@ static void sync_SavegameMetadata(Common::Serializer &s, SavegameMetadata &obj)
}
s.syncAsUint32LE(obj.playTime);
}

if (s.getVersion() >= 38) {
if (s.isSaving()) {
obj.score = g_sci->getEngineState()->variables[VAR_GLOBAL][kScore].toUint16();
if (g_sci->getGameId() == GID_SHIVERS) {
obj.score |= g_sci->getEngineState()->variables[VAR_GLOBAL][kShivers1Score].toUint16() << 16;
}
}

s.syncAsUint32LE(obj.score);
}
}

void EngineState::saveLoadWithSerializer(Common::Serializer &s) {
Expand Down
6 changes: 5 additions & 1 deletion engines/sci/engine/savegame.h
Expand Up @@ -37,7 +37,7 @@ struct EngineState;
*
* Version - new/changed feature
* =============================
* 38 - SCI32 cursor, accurate SCI32 arrays/strings
* 38 - SCI32 cursor, accurate SCI32 arrays/strings, score metadata
* 37 - Segment entry data changed to pointers
* 36 - SCI32 bitmap segment
* 35 - SCI32 remap
Expand Down Expand Up @@ -77,6 +77,10 @@ struct SavegameMetadata {
uint32 playTime;
uint16 gameObjectOffset;
uint16 script0Size;
#ifdef ENABLE_SCI32
// Used by Shivers 1
uint32 score;
#endif
};

/**
Expand Down
4 changes: 3 additions & 1 deletion engines/sci/engine/vm.h
Expand Up @@ -147,8 +147,10 @@ enum GlobalVar {
kCurrentRoomNo = 11,
kPreviousRoomNo = 12,
kNewRoomNo = 13,
kScore = 15,
kFastCast = 84, // SCI16
kMessageType = 90
kMessageType = 90,
kShivers1Score = 349
};

/** Number of kernel calls in between gcs; should be < 50000 */
Expand Down

0 comments on commit 2629269

Please sign in to comment.