2009-02-17 15:02:16 +00:00
|
|
|
/* ScummVM - Graphic Adventure Engine
|
|
|
|
*
|
|
|
|
* ScummVM is the legal property of its developers, whose names
|
|
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
|
|
* file distributed with this source distribution.
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
2014-02-18 02:34:24 +01:00
|
|
|
*
|
2009-02-17 15:02:16 +00:00
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
2014-02-18 02:34:24 +01:00
|
|
|
*
|
2009-02-17 15:02:16 +00:00
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
*
|
|
|
|
*/
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-02-23 04:37:35 +00:00
|
|
|
#include "common/archive.h"
|
2010-08-23 19:10:06 +00:00
|
|
|
#include "common/config-manager.h"
|
2011-10-29 18:31:53 +03:00
|
|
|
#include "common/debug-channels.h"
|
2009-02-23 04:37:35 +00:00
|
|
|
#include "common/file.h"
|
2012-03-08 08:03:50 -05:00
|
|
|
#include "common/macresman.h"
|
2009-02-17 20:26:57 +00:00
|
|
|
#include "common/str.h"
|
2009-02-20 23:41:15 +00:00
|
|
|
#include "common/savefile.h"
|
2011-04-24 11:34:27 +03:00
|
|
|
#include "common/system.h"
|
2010-09-12 21:56:37 +00:00
|
|
|
#include "common/translation.h"
|
2016-07-02 21:19:29 +02:00
|
|
|
#include "common/memstream.h"
|
2009-02-17 20:26:57 +00:00
|
|
|
|
2010-08-23 23:04:07 +00:00
|
|
|
#include "gui/saveload.h"
|
2010-08-23 19:10:06 +00:00
|
|
|
|
2009-02-20 23:41:15 +00:00
|
|
|
#include "sci/sci.h"
|
2012-06-13 11:00:58 +03:00
|
|
|
#include "sci/engine/file.h"
|
2009-02-27 02:23:40 +00:00
|
|
|
#include "sci/engine/state.h"
|
2009-02-24 05:51:55 +00:00
|
|
|
#include "sci/engine/kernel.h"
|
2009-03-12 03:26:21 +00:00
|
|
|
#include "sci/engine/savegame.h"
|
2014-03-28 02:16:54 +02:00
|
|
|
#include "sci/sound/audio.h"
|
2010-01-02 14:08:26 +00:00
|
|
|
#include "sci/console.h"
|
2016-08-19 08:59:55 -05:00
|
|
|
#ifdef ENABLE_SCI32
|
|
|
|
#include "sci/resource.h"
|
|
|
|
#endif
|
2009-02-17 20:26:57 +00:00
|
|
|
|
2009-02-23 04:37:35 +00:00
|
|
|
namespace Sci {
|
|
|
|
|
2012-06-13 11:29:14 +03:00
|
|
|
extern reg_t file_open(EngineState *s, const Common::String &filename, int 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);
|
2016-09-12 11:08:02 -05:00
|
|
|
extern bool fillSavegameDesc(const Common::String &filename, SavegameDesc *desc);
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-02-27 01:17:24 +00:00
|
|
|
/**
|
|
|
|
* Writes the cwd to the supplied address and returns the address in acc.
|
|
|
|
*/
|
- Changed the unimplemented debug SCI kernel functions (InspectObj, ShowSends, ShowObjs, ShowFree, StackUsage and Profiler) to be dummy functions - we have our own debugger, and don't use these functions for debugging
- Removed the function number parameter from all kernel functions, as it's no longer needed, and removed the FAKE_FUNCT_NR hack
- Removed kUnknown() and kStub()
- Dummy/unknown kernel functions are no longer invoked, and a warning is shown instead, with the paremeters passed to them
Note: there is an evil hack used for debugging scripts in invoke_selector(), which probably no longer works now
svn-id: r44461
2009-09-29 14:24:07 +00:00
|
|
|
reg_t kGetCWD(EngineState *s, int argc, reg_t *argv) {
|
2009-02-27 01:17:24 +00:00
|
|
|
// We do not let the scripts see the file system, instead pretending
|
|
|
|
// we are always in the same directory.
|
|
|
|
// TODO/FIXME: Is "/" a good value? Maybe "" or "." or "C:\" are better?
|
2009-10-04 18:38:18 +00:00
|
|
|
s->_segMan->strcpy(argv[0], "/");
|
2009-02-23 04:48:29 +00:00
|
|
|
|
2011-01-01 12:48:12 +00:00
|
|
|
debugC(kDebugLevelFile, "kGetCWD() -> %s", "/");
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
return argv[0];
|
|
|
|
}
|
|
|
|
|
2009-02-27 01:17:24 +00:00
|
|
|
enum {
|
|
|
|
K_DEVICE_INFO_GET_DEVICE = 0,
|
|
|
|
K_DEVICE_INFO_GET_CURRENT_DEVICE = 1,
|
|
|
|
K_DEVICE_INFO_PATHS_EQUAL = 2,
|
|
|
|
K_DEVICE_INFO_IS_FLOPPY = 3,
|
2010-01-31 21:11:36 +00:00
|
|
|
K_DEVICE_INFO_GET_CONFIG_PATH = 5,
|
2009-02-27 01:17:24 +00:00
|
|
|
K_DEVICE_INFO_GET_SAVECAT_NAME = 7,
|
|
|
|
K_DEVICE_INFO_GET_SAVEFILE_NAME = 8
|
|
|
|
};
|
2009-02-15 06:10:59 +00:00
|
|
|
|
- Changed the unimplemented debug SCI kernel functions (InspectObj, ShowSends, ShowObjs, ShowFree, StackUsage and Profiler) to be dummy functions - we have our own debugger, and don't use these functions for debugging
- Removed the function number parameter from all kernel functions, as it's no longer needed, and removed the FAKE_FUNCT_NR hack
- Removed kUnknown() and kStub()
- Dummy/unknown kernel functions are no longer invoked, and a warning is shown instead, with the paremeters passed to them
Note: there is an evil hack used for debugging scripts in invoke_selector(), which probably no longer works now
svn-id: r44461
2009-09-29 14:24:07 +00:00
|
|
|
reg_t kDeviceInfo(EngineState *s, int argc, reg_t *argv) {
|
2010-07-30 18:45:28 +00:00
|
|
|
if (g_sci->getGameId() == GID_FANMADE && argc == 1) {
|
|
|
|
// WORKAROUND: The fan game script library calls kDeviceInfo with one parameter.
|
|
|
|
// According to the scripts, it wants to call CurDevice. However, it fails to
|
|
|
|
// provide the subop to the function.
|
|
|
|
s->_segMan->strcpy(argv[0], "/");
|
|
|
|
return s->r_acc;
|
|
|
|
}
|
|
|
|
|
2009-06-07 15:53:30 +00:00
|
|
|
int mode = argv[0].toUint16();
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-02-15 22:28:12 +00:00
|
|
|
switch (mode) {
|
2009-09-27 01:50:26 +00:00
|
|
|
case K_DEVICE_INFO_GET_DEVICE: {
|
2009-10-04 18:38:18 +00:00
|
|
|
Common::String input_str = s->_segMan->getString(argv[1]);
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-10-04 18:38:18 +00:00
|
|
|
s->_segMan->strcpy(argv[2], "/");
|
2009-09-27 01:50:26 +00:00
|
|
|
debug(3, "K_DEVICE_INFO_GET_DEVICE(%s) -> %s", input_str.c_str(), "/");
|
2009-02-23 03:51:22 +00:00
|
|
|
break;
|
2009-09-27 01:50:26 +00:00
|
|
|
}
|
2009-02-23 03:51:22 +00:00
|
|
|
case K_DEVICE_INFO_GET_CURRENT_DEVICE:
|
2009-10-04 18:38:18 +00:00
|
|
|
s->_segMan->strcpy(argv[1], "/");
|
2009-09-27 01:50:26 +00:00
|
|
|
debug(3, "K_DEVICE_INFO_GET_CURRENT_DEVICE() -> %s", "/");
|
2009-02-23 03:51:22 +00:00
|
|
|
break;
|
2009-02-15 06:10:59 +00:00
|
|
|
|
|
|
|
case K_DEVICE_INFO_PATHS_EQUAL: {
|
2009-10-04 18:38:18 +00:00
|
|
|
Common::String path1_s = s->_segMan->getString(argv[1]);
|
|
|
|
Common::String path2_s = s->_segMan->getString(argv[2]);
|
2009-09-27 01:50:26 +00:00
|
|
|
debug(3, "K_DEVICE_INFO_PATHS_EQUAL(%s,%s)", path1_s.c_str(), path2_s.c_str());
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-09-27 01:50:26 +00:00
|
|
|
return make_reg(0, Common::matchString(path2_s.c_str(), path1_s.c_str(), false, true));
|
2009-02-23 03:51:22 +00:00
|
|
|
}
|
|
|
|
break;
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-09-27 01:50:26 +00:00
|
|
|
case K_DEVICE_INFO_IS_FLOPPY: {
|
2009-10-04 18:38:18 +00:00
|
|
|
Common::String input_str = s->_segMan->getString(argv[1]);
|
2009-09-27 01:50:26 +00:00
|
|
|
debug(3, "K_DEVICE_INFO_IS_FLOPPY(%s)", input_str.c_str());
|
2009-02-15 06:10:59 +00:00
|
|
|
return NULL_REG; /* Never */
|
2009-09-27 01:50:26 +00:00
|
|
|
}
|
2010-01-31 21:11:36 +00:00
|
|
|
case K_DEVICE_INFO_GET_CONFIG_PATH: {
|
|
|
|
// Early versions return drive letter, later versions a path string
|
|
|
|
// FIXME: Implement if needed, for now return NULL_REG
|
|
|
|
return NULL_REG;
|
2010-01-31 19:46:46 +00:00
|
|
|
}
|
2009-02-15 22:28:12 +00:00
|
|
|
/* SCI uses these in a less-than-portable way to delete savegames.
|
|
|
|
** Read http://www-plan.cs.colorado.edu/creichen/freesci-logs/2005.10/log20051019.html
|
|
|
|
** for more information on our workaround for this.
|
|
|
|
*/
|
2009-02-15 06:10:59 +00:00
|
|
|
case K_DEVICE_INFO_GET_SAVECAT_NAME: {
|
2009-10-04 18:38:18 +00:00
|
|
|
Common::String game_prefix = s->_segMan->getString(argv[2]);
|
|
|
|
s->_segMan->strcpy(argv[1], "__throwaway");
|
2009-09-27 01:50:26 +00:00
|
|
|
debug(3, "K_DEVICE_INFO_GET_SAVECAT_NAME(%s) -> %s", game_prefix.c_str(), "__throwaway");
|
2009-02-23 03:51:22 +00:00
|
|
|
}
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-02-15 22:28:12 +00:00
|
|
|
break;
|
2009-02-15 06:10:59 +00:00
|
|
|
case K_DEVICE_INFO_GET_SAVEFILE_NAME: {
|
2009-10-04 18:38:18 +00:00
|
|
|
Common::String game_prefix = s->_segMan->getString(argv[2]);
|
2010-07-12 22:26:48 +00:00
|
|
|
uint virtualId = argv[3].toUint16();
|
2009-10-04 18:38:18 +00:00
|
|
|
s->_segMan->strcpy(argv[1], "__throwaway");
|
2010-07-12 22:26:48 +00:00
|
|
|
debug(3, "K_DEVICE_INFO_GET_SAVEFILE_NAME(%s,%d) -> %s", game_prefix.c_str(), virtualId, "__throwaway");
|
|
|
|
if ((virtualId < SAVEGAMEID_OFFICIALRANGE_START) || (virtualId > SAVEGAMEID_OFFICIALRANGE_END))
|
2012-05-14 11:14:10 +03:00
|
|
|
error("kDeviceInfo(deleteSave): invalid savegame ID specified");
|
2010-07-12 22:26:48 +00:00
|
|
|
uint savegameId = virtualId - SAVEGAMEID_OFFICIALRANGE_START;
|
2009-10-29 09:58:36 +00:00
|
|
|
Common::Array<SavegameDesc> saves;
|
|
|
|
listSavegames(saves);
|
2010-07-12 22:26:48 +00:00
|
|
|
if (findSavegame(saves, savegameId) != -1) {
|
|
|
|
// Confirmed that this id still lives...
|
|
|
|
Common::String filename = g_sci->getSavegameName(savegameId);
|
2011-06-03 18:21:58 +02:00
|
|
|
Common::SaveFileManager *saveFileMan = g_sci->getSaveFileManager();
|
2010-07-12 22:26:48 +00:00
|
|
|
saveFileMan->removeSavefile(filename);
|
2009-02-23 03:51:22 +00:00
|
|
|
}
|
|
|
|
break;
|
2010-07-12 22:26:48 +00:00
|
|
|
}
|
2009-02-23 03:51:22 +00:00
|
|
|
|
|
|
|
default:
|
2010-06-18 02:09:12 +00:00
|
|
|
error("Unknown DeviceInfo() sub-command: %d", mode);
|
2009-02-23 03:51:22 +00:00
|
|
|
break;
|
2009-02-15 06:10:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return s->r_acc;
|
|
|
|
}
|
|
|
|
|
- Changed the unimplemented debug SCI kernel functions (InspectObj, ShowSends, ShowObjs, ShowFree, StackUsage and Profiler) to be dummy functions - we have our own debugger, and don't use these functions for debugging
- Removed the function number parameter from all kernel functions, as it's no longer needed, and removed the FAKE_FUNCT_NR hack
- Removed kUnknown() and kStub()
- Dummy/unknown kernel functions are no longer invoked, and a warning is shown instead, with the paremeters passed to them
Note: there is an evil hack used for debugging scripts in invoke_selector(), which probably no longer works now
svn-id: r44461
2009-09-29 14:24:07 +00:00
|
|
|
reg_t kCheckFreeSpace(EngineState *s, int argc, reg_t *argv) {
|
2016-09-10 14:30:23 -05:00
|
|
|
// A file path to test is also passed to this function as a separate
|
|
|
|
// argument, but we do not actually check anything, so it is unused
|
|
|
|
|
|
|
|
enum {
|
|
|
|
kSaveGameSize = 0,
|
|
|
|
kFreeDiskSpace = 1,
|
|
|
|
kEnoughSpaceToSave = 2
|
|
|
|
};
|
|
|
|
|
|
|
|
int16 subop;
|
|
|
|
// In SCI2.1mid, the call is moved into kFileIO and the arguments are
|
|
|
|
// flipped
|
|
|
|
if (getSciVersion() >= SCI_VERSION_2_1_MIDDLE) {
|
|
|
|
subop = argc > 0 ? argv[0].toSint16() : 2;
|
|
|
|
} else {
|
|
|
|
subop = argc > 1 ? argv[1].toSint16() : 2;
|
2010-07-27 18:45:32 +00:00
|
|
|
}
|
2010-01-30 02:03:59 +00:00
|
|
|
|
2016-09-10 14:30:23 -05:00
|
|
|
switch (subop) {
|
|
|
|
case kSaveGameSize:
|
|
|
|
return make_reg(0, 0);
|
2009-02-15 22:28:12 +00:00
|
|
|
|
2016-09-10 14:30:23 -05:00
|
|
|
case kFreeDiskSpace: // in KiB; up to 32MiB maximum
|
|
|
|
return make_reg(0, 0x7fff);
|
|
|
|
|
|
|
|
case kEnoughSpaceToSave:
|
|
|
|
return make_reg(0, 1);
|
|
|
|
|
|
|
|
default:
|
|
|
|
error("kCheckFreeSpace: called with unknown sub-op %d", subop);
|
|
|
|
}
|
2009-02-15 06:10:59 +00:00
|
|
|
}
|
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
reg_t kValidPath(EngineState *s, int argc, reg_t *argv) {
|
|
|
|
Common::String path = s->_segMan->getString(argv[0]);
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2012-06-18 05:21:59 +03:00
|
|
|
debug(3, "kValidPath(%s) -> %d", path.c_str(), s->r_acc.getOffset());
|
2009-05-29 14:36:56 +00:00
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
// Always return true
|
|
|
|
return make_reg(0, 1);
|
|
|
|
}
|
2010-08-02 21:44:41 +00:00
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
#ifdef ENABLE_SCI32
|
2010-06-04 10:36:49 +00:00
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
reg_t kCD(EngineState *s, int argc, reg_t *argv) {
|
2016-08-19 08:59:55 -05:00
|
|
|
if (!s)
|
|
|
|
return make_reg(0, getSciVersion());
|
|
|
|
error("not supposed to call this");
|
|
|
|
}
|
|
|
|
|
|
|
|
reg_t kCheckCD(EngineState *s, int argc, reg_t *argv) {
|
|
|
|
const int16 cdNo = argc > 0 ? argv[0].toSint16() : 0;
|
|
|
|
|
|
|
|
if (cdNo) {
|
|
|
|
g_sci->getResMan()->findDisc(cdNo);
|
2012-06-13 12:54:40 +03:00
|
|
|
}
|
2010-06-04 10:36:49 +00:00
|
|
|
|
2016-08-19 08:59:55 -05:00
|
|
|
return make_reg(0, g_sci->getResMan()->getCurrentDiscNo());
|
|
|
|
}
|
|
|
|
|
|
|
|
reg_t kGetSavedCD(EngineState *s, int argc, reg_t *argv) {
|
|
|
|
// TODO: This is wrong, CD number needs to be available prior to
|
|
|
|
// the save game being loaded
|
|
|
|
return make_reg(0, g_sci->getResMan()->getCurrentDiscNo());
|
2009-02-15 06:10:59 +00:00
|
|
|
}
|
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
#endif
|
2009-05-29 14:36:56 +00:00
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
// ---- FileIO operations -----------------------------------------------------
|
2010-07-12 22:26:48 +00:00
|
|
|
|
2016-09-15 09:02:55 -05:00
|
|
|
#ifdef ENABLE_SCI32
|
|
|
|
static bool isSaveCatalogue(const Common::String &name) {
|
|
|
|
return name == "autosave.cat" || name.hasSuffix("sg.cat");
|
|
|
|
}
|
|
|
|
|
|
|
|
// SCI32 save game scripts check for, and write directly to, the save game
|
|
|
|
// catalogue. Since ScummVM does not use these catalogues, when looking for a
|
|
|
|
// catalogue, we instead check for save games within ScummVM that are logically
|
|
|
|
// equivalent to the behaviour of SSCI.
|
|
|
|
static bool saveCatalogueExists(const Common::String &name) {
|
|
|
|
bool exists = false;
|
|
|
|
Common::SaveFileManager *saveFileMan = g_sci->getSaveFileManager();
|
|
|
|
|
|
|
|
// There will always be one save game in some games, the "new game"
|
|
|
|
// game, which should be ignored when deciding if there are any save
|
|
|
|
// games available
|
|
|
|
uint numPermanentSaves;
|
|
|
|
switch (g_sci->getGameId()) {
|
|
|
|
case GID_TORIN:
|
|
|
|
case GID_LSL7:
|
|
|
|
case GID_LIGHTHOUSE:
|
|
|
|
numPermanentSaves = 1;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
numPermanentSaves = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Torin uses autosave.cat; LSL7 uses autosvsg.cat
|
|
|
|
if (name == "autosave.cat" || name == "autosvsg.cat") {
|
|
|
|
exists = !saveFileMan->listSavefiles(g_sci->getSavegameName(0)).empty();
|
|
|
|
} else {
|
|
|
|
exists = saveFileMan->listSavefiles(g_sci->getSavegamePattern()).size() > numPermanentSaves;
|
|
|
|
}
|
|
|
|
|
|
|
|
return exists;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
reg_t kFileIO(EngineState *s, int argc, reg_t *argv) {
|
|
|
|
if (!s)
|
|
|
|
return make_reg(0, getSciVersion());
|
|
|
|
error("not supposed to call this");
|
|
|
|
}
|
2009-02-20 23:41:15 +00:00
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
reg_t kFileIOOpen(EngineState *s, int argc, reg_t *argv) {
|
|
|
|
Common::String name = s->_segMan->getString(argv[0]);
|
2009-02-20 23:41:15 +00:00
|
|
|
|
2016-09-20 21:07:20 -05:00
|
|
|
if (name.empty()) {
|
|
|
|
// Happens many times during KQ1 (e.g. when typing something)
|
|
|
|
debugC(kDebugLevelFile, "Attempted to open a file with an empty filename");
|
|
|
|
return SIGNAL_REG;
|
|
|
|
}
|
2016-09-13 10:44:18 -05:00
|
|
|
|
2016-09-10 11:36:57 -05:00
|
|
|
int mode = argv[1].toUint16();
|
2012-06-13 12:54:40 +03:00
|
|
|
bool unwrapFilename = true;
|
2009-02-20 23:41:15 +00:00
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
// SQ4 floppy prepends /\ to the filenames
|
|
|
|
if (name.hasPrefix("/\\")) {
|
|
|
|
name.deleteChar(0);
|
|
|
|
name.deleteChar(0);
|
2009-02-15 06:10:59 +00:00
|
|
|
}
|
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
// SQ4 floppy attempts to update the savegame index file sq4sg.dir when
|
|
|
|
// deleting saved games. We don't use an index file for saving or loading,
|
|
|
|
// so just stop the game from modifying the file here in order to avoid
|
|
|
|
// having it saved in the ScummVM save directory.
|
|
|
|
if (name == "sq4sg.dir") {
|
|
|
|
debugC(kDebugLevelFile, "Not opening unused file sq4sg.dir");
|
|
|
|
return SIGNAL_REG;
|
|
|
|
}
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2016-09-13 10:32:41 -05:00
|
|
|
#ifdef ENABLE_SCI32
|
2016-09-20 21:07:20 -05:00
|
|
|
// GK1, GK2, KQ7, LSL6hires, Phant1, PQ4, PQ:SWAT, and SQ6 read in
|
|
|
|
// their game version from the VERSION file
|
|
|
|
if (name.compareToIgnoreCase("version") == 0) {
|
|
|
|
unwrapFilename = false;
|
2016-01-02 21:50:35 +01:00
|
|
|
}
|
|
|
|
|
2012-06-13 22:55:27 +03:00
|
|
|
if (g_sci->getGameId() == GID_SHIVERS && name.hasSuffix(".SG")) {
|
2016-09-21 12:10:06 -05:00
|
|
|
// Shivers stores the name and score of save games in separate %d.SG
|
|
|
|
// files, which are used by the save/load screen
|
2012-06-13 22:55:27 +03:00
|
|
|
if (mode == _K_FILE_MODE_OPEN_OR_CREATE || mode == _K_FILE_MODE_CREATE) {
|
2016-09-15 21:08:44 -05:00
|
|
|
// Suppress creation of the SG file, since it is not necessary
|
2012-06-13 22:55:27 +03:00
|
|
|
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.
|
2016-09-15 21:08:44 -05:00
|
|
|
int saveNo;
|
|
|
|
sscanf(name.c_str(), "%d.SG", &saveNo);
|
2016-09-20 21:07:20 -05:00
|
|
|
saveNo += kSaveIdShift;
|
2016-09-15 21:08:44 -05:00
|
|
|
|
|
|
|
SavegameDesc save;
|
|
|
|
fillSavegameDesc(g_sci->getSavegameName(saveNo), &save);
|
|
|
|
|
2016-09-21 16:16:21 -05:00
|
|
|
Common::String score;
|
|
|
|
if (!save.highScore) {
|
|
|
|
score = Common::String::format("%u", save.lowScore);
|
2016-09-15 21:08:44 -05:00
|
|
|
} else {
|
2016-09-21 16:16:21 -05:00
|
|
|
score = Common::String::format("%u%03u", save.highScore, save.lowScore);
|
2016-09-15 21:08:44 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
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());
|
|
|
|
|
|
|
|
const uint handle = findFreeFileHandle(s);
|
|
|
|
|
|
|
|
s->_fileHandles[handle]._in = new Common::MemoryReadStream((byte *)buffer, size, DisposeAfterUse::YES);
|
2016-07-02 21:19:29 +02:00
|
|
|
s->_fileHandles[handle]._out = nullptr;
|
|
|
|
s->_fileHandles[handle]._name = "";
|
|
|
|
|
|
|
|
return make_reg(0, handle);
|
2012-06-13 22:55:27 +03:00
|
|
|
}
|
2016-09-21 12:10:06 -05:00
|
|
|
} 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) {
|
|
|
|
// 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) {
|
|
|
|
// Create a virtual file containing the save game description
|
|
|
|
// and slot number, as the game scripts expect.
|
|
|
|
int saveNo;
|
|
|
|
sscanf(name.c_str(), "%d.DTA", &saveNo);
|
|
|
|
saveNo += kSaveIdShift;
|
|
|
|
|
|
|
|
SavegameDesc save;
|
|
|
|
fillSavegameDesc(g_sci->getSavegameName(saveNo), &save);
|
|
|
|
|
|
|
|
const Common::String avatarId = Common::String::format("%02d", save.avatarId);
|
|
|
|
const uint nameLength = strlen(save.name);
|
|
|
|
const uint size = nameLength + /* \r\n */ 2 + avatarId.size() + 1;
|
|
|
|
char *buffer = (char *)malloc(size);
|
|
|
|
memcpy(buffer, save.name, nameLength);
|
|
|
|
buffer[nameLength] = '\r';
|
|
|
|
buffer[nameLength + 1] = '\n';
|
|
|
|
memcpy(buffer + nameLength + 2, avatarId.c_str(), avatarId.size() + 1);
|
|
|
|
|
|
|
|
const uint handle = findFreeFileHandle(s);
|
2016-09-20 21:07:20 -05:00
|
|
|
|
2016-09-21 12:10:06 -05:00
|
|
|
s->_fileHandles[handle]._in = new Common::MemoryReadStream((byte *)buffer, size, DisposeAfterUse::YES);
|
|
|
|
s->_fileHandles[handle]._out = nullptr;
|
|
|
|
s->_fileHandles[handle]._name = "";
|
|
|
|
|
|
|
|
return make_reg(0, handle);
|
|
|
|
}
|
|
|
|
} else if (g_sci->getGameId() == GID_KQ7) {
|
2016-09-20 21:07:20 -05:00
|
|
|
// KQ7 creates a temp.tmp file to perform an atomic rewrite of the
|
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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) {
|
|
|
|
// 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) {
|
|
|
|
Common::Array<SavegameDesc> saves;
|
|
|
|
listSavegames(saves);
|
|
|
|
|
|
|
|
const uint recordSize = sizeof(int16) + SCI_MAX_SAVENAME_LENGTH;
|
|
|
|
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) {
|
2016-09-21 10:45:16 -05:00
|
|
|
WRITE_UINT16(out, saves[i].id - kSaveIdShift);
|
2016-09-20 21:07:20 -05:00
|
|
|
Common::strlcpy((char *)out + sizeof(int16), saves[i].name, SCI_MAX_SAVENAME_LENGTH);
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// See kMakeSaveCatName
|
|
|
|
if (name == "fake.cat") {
|
|
|
|
return make_reg(0, VIRTUALFILE_HANDLE_SCI32SAVE);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isSaveCatalogue(name)) {
|
|
|
|
const bool exists = saveCatalogueExists(name);
|
|
|
|
if (exists) {
|
|
|
|
// Dummy handle is used to represent the catalogue and ignore any
|
|
|
|
// direct game script writes
|
|
|
|
return make_reg(0, VIRTUALFILE_HANDLE_SCI32SAVE);
|
|
|
|
} else {
|
|
|
|
return SIGNAL_REG;
|
|
|
|
}
|
|
|
|
}
|
2012-06-13 22:55:27 +03:00
|
|
|
#endif
|
|
|
|
|
2016-09-20 21:07:20 -05:00
|
|
|
debugC(kDebugLevelFile, "kFileIO(open): %s, 0x%x", name.c_str(), mode);
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
// QFG import rooms get a virtual filelisting instead of an actual one
|
|
|
|
if (g_sci->inQfGImportRoom()) {
|
|
|
|
// We need to find out what the user actually selected, "savedHeroes" is
|
|
|
|
// already destroyed when we get here. That's why we need to remember
|
|
|
|
// selection via kDrawControl.
|
|
|
|
name = s->_dirseeker.getVirtualFilename(s->_chosenQfGImportItem);
|
|
|
|
unwrapFilename = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return file_open(s, name, mode, unwrapFilename);
|
2009-02-15 06:10:59 +00:00
|
|
|
}
|
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
reg_t kFileIOClose(EngineState *s, int argc, reg_t *argv) {
|
|
|
|
debugC(kDebugLevelFile, "kFileIO(close): %d", argv[0].toUint16());
|
2010-08-24 14:40:18 +00:00
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
if (argv[0] == SIGNAL_REG)
|
|
|
|
return s->r_acc;
|
2012-09-26 04:17:31 +02:00
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
uint16 handle = argv[0].toUint16();
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2016-01-02 21:50:35 +01:00
|
|
|
if (handle >= VIRTUALFILE_HANDLE_START) {
|
|
|
|
// it's a virtual handle? ignore it
|
2016-09-13 10:44:18 -05:00
|
|
|
return getSciVersion() >= SCI_VERSION_2 ? TRUE_REG : SIGNAL_REG;
|
2016-01-02 21:50:35 +01:00
|
|
|
}
|
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
FileHandle *f = getFileFromHandle(s, handle);
|
|
|
|
if (f) {
|
|
|
|
f->close();
|
|
|
|
if (getSciVersion() <= SCI_VERSION_0_LATE)
|
|
|
|
return s->r_acc; // SCI0 semantics: no value returned
|
2016-09-13 10:44:18 -05:00
|
|
|
return getSciVersion() >= SCI_VERSION_2 ? TRUE_REG : SIGNAL_REG;
|
2012-06-13 12:54:40 +03:00
|
|
|
}
|
2010-08-24 14:40:18 +00:00
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
if (getSciVersion() <= SCI_VERSION_0_LATE)
|
|
|
|
return s->r_acc; // SCI0 semantics: no value returned
|
|
|
|
return NULL_REG;
|
|
|
|
}
|
2010-08-24 14:40:18 +00:00
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
reg_t kFileIOReadRaw(EngineState *s, int argc, reg_t *argv) {
|
|
|
|
uint16 handle = argv[0].toUint16();
|
|
|
|
uint16 size = argv[2].toUint16();
|
|
|
|
int bytesRead = 0;
|
|
|
|
char *buf = new char[size];
|
|
|
|
debugC(kDebugLevelFile, "kFileIO(readRaw): %d, %d", handle, size);
|
2010-08-24 14:40:18 +00:00
|
|
|
|
2016-07-02 21:19:29 +02:00
|
|
|
FileHandle *f = getFileFromHandle(s, handle);
|
|
|
|
if (f)
|
|
|
|
bytesRead = f->_in->read(buf, size);
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
// TODO: What happens if less bytes are read than what has
|
|
|
|
// been requested? (i.e. if bytesRead is non-zero, but still
|
|
|
|
// less than size)
|
|
|
|
if (bytesRead > 0)
|
|
|
|
s->_segMan->memcpy(argv[1], (const byte*)buf, size);
|
2010-07-12 22:26:48 +00:00
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
delete[] buf;
|
|
|
|
return make_reg(0, bytesRead);
|
|
|
|
}
|
2011-02-10 13:39:01 +00:00
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
reg_t kFileIOWriteRaw(EngineState *s, int argc, reg_t *argv) {
|
|
|
|
uint16 handle = argv[0].toUint16();
|
|
|
|
uint16 size = argv[2].toUint16();
|
2016-09-13 10:44:18 -05:00
|
|
|
|
|
|
|
#ifdef ENABLE_SCI32
|
|
|
|
if (handle == VIRTUALFILE_HANDLE_SCI32SAVE) {
|
|
|
|
return make_reg(0, size);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
char *buf = new char[size];
|
2016-09-13 10:44:18 -05:00
|
|
|
uint bytesWritten = 0;
|
2012-06-13 12:54:40 +03:00
|
|
|
bool success = false;
|
|
|
|
s->_segMan->memcpy((byte *)buf, argv[1], size);
|
|
|
|
debugC(kDebugLevelFile, "kFileIO(writeRaw): %d, %d", handle, size);
|
2010-08-24 09:00:53 +00:00
|
|
|
|
2016-07-02 21:19:29 +02:00
|
|
|
FileHandle *f = getFileFromHandle(s, handle);
|
|
|
|
if (f) {
|
2016-09-13 10:44:18 -05:00
|
|
|
bytesWritten = f->_out->write(buf, size);
|
|
|
|
success = !f->_out->err();
|
2009-02-15 06:10:59 +00:00
|
|
|
}
|
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
delete[] buf;
|
2016-09-13 10:44:18 -05:00
|
|
|
|
|
|
|
#ifdef ENABLE_SCI32
|
|
|
|
if (getSciVersion() >= SCI_VERSION_2) {
|
|
|
|
if (!success) {
|
2016-09-30 13:09:12 -05:00
|
|
|
return SIGNAL_REG;
|
2016-09-13 10:44:18 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
return make_reg(0, bytesWritten);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
if (success)
|
|
|
|
return NULL_REG;
|
|
|
|
return make_reg(0, 6); // DOS - invalid handle
|
2010-07-14 22:07:36 +00:00
|
|
|
}
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
reg_t kFileIOUnlink(EngineState *s, int argc, reg_t *argv) {
|
2010-07-14 22:07:36 +00:00
|
|
|
Common::String name = s->_segMan->getString(argv[0]);
|
2012-06-13 12:54:40 +03:00
|
|
|
Common::SaveFileManager *saveFileMan = g_sci->getSaveFileManager();
|
|
|
|
bool result;
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2010-07-14 22:07:36 +00:00
|
|
|
// SQ4 floppy prepends /\ to the filenames
|
|
|
|
if (name.hasPrefix("/\\")) {
|
|
|
|
name.deleteChar(0);
|
|
|
|
name.deleteChar(0);
|
|
|
|
}
|
2009-10-29 20:31:35 +00:00
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
// Special case for SQ4 floppy: This game has hardcoded names for all of
|
|
|
|
// its savegames, and they are all named "sq4sg.xxx", where xxx is the
|
|
|
|
// slot. We just take the slot number here, and delete the appropriate
|
|
|
|
// save game.
|
|
|
|
if (name.hasPrefix("sq4sg.")) {
|
|
|
|
// Special handling for SQ4... get the slot number and construct the
|
|
|
|
// save game name.
|
|
|
|
int slotNum = atoi(name.c_str() + name.size() - 3);
|
|
|
|
Common::Array<SavegameDesc> saves;
|
|
|
|
listSavegames(saves);
|
|
|
|
int savedir_nr = saves[slotNum].id;
|
|
|
|
name = g_sci->getSavegameName(savedir_nr);
|
|
|
|
result = saveFileMan->removeSavefile(name);
|
2016-09-13 10:44:18 -05:00
|
|
|
#ifdef ENABLE_SCI32
|
2012-06-13 12:54:40 +03:00
|
|
|
} else if (getSciVersion() >= SCI_VERSION_2) {
|
2016-09-21 10:45:16 -05:00
|
|
|
// Special case for KQ7, basically identical to the SQ4 case above,
|
|
|
|
// where the game hardcodes its save game names
|
|
|
|
if (name.hasPrefix("kq7cdsg.")) {
|
|
|
|
int saveNo = atoi(name.c_str() + name.size() - 3);
|
|
|
|
name = g_sci->getSavegameName(saveNo + kSaveIdShift);
|
|
|
|
}
|
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
// The file name may be already wrapped, so check both cases
|
|
|
|
result = saveFileMan->removeSavefile(name);
|
|
|
|
if (!result) {
|
|
|
|
const Common::String wrappedName = g_sci->wrapFilename(name);
|
|
|
|
result = saveFileMan->removeSavefile(wrappedName);
|
|
|
|
}
|
2016-09-13 10:44:18 -05:00
|
|
|
#endif
|
2012-06-13 12:54:40 +03:00
|
|
|
} else {
|
|
|
|
const Common::String wrappedName = g_sci->wrapFilename(name);
|
|
|
|
result = saveFileMan->removeSavefile(wrappedName);
|
2010-08-29 00:17:56 +00:00
|
|
|
}
|
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
debugC(kDebugLevelFile, "kFileIO(unlink): %s", name.c_str());
|
2016-09-13 10:44:18 -05:00
|
|
|
|
|
|
|
#ifdef ENABLE_SCI32
|
|
|
|
if (getSciVersion() >= SCI_VERSION_2) {
|
|
|
|
return make_reg(0, result);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
if (result)
|
|
|
|
return NULL_REG;
|
|
|
|
return make_reg(0, 2); // DOS - file not found error code
|
2010-07-14 22:07:36 +00:00
|
|
|
}
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
reg_t kFileIOReadString(EngineState *s, int argc, reg_t *argv) {
|
|
|
|
uint16 maxsize = argv[1].toUint16();
|
|
|
|
char *buf = new char[maxsize];
|
|
|
|
uint16 handle = argv[2].toUint16();
|
|
|
|
debugC(kDebugLevelFile, "kFileIO(readString): %d, %d", handle, maxsize);
|
|
|
|
uint32 bytesRead;
|
2010-07-14 22:07:36 +00:00
|
|
|
|
2016-07-02 21:19:29 +02:00
|
|
|
bytesRead = fgets_wrapper(s, buf, maxsize, handle);
|
2012-06-13 12:54:40 +03:00
|
|
|
|
|
|
|
s->_segMan->memcpy(argv[0], (const byte*)buf, maxsize);
|
|
|
|
delete[] buf;
|
|
|
|
return bytesRead ? argv[0] : NULL_REG;
|
|
|
|
}
|
|
|
|
|
|
|
|
reg_t kFileIOWriteString(EngineState *s, int argc, reg_t *argv) {
|
|
|
|
int handle = argv[0].toUint16();
|
|
|
|
Common::String str = s->_segMan->getString(argv[1]);
|
|
|
|
debugC(kDebugLevelFile, "kFileIO(writeString): %d", handle);
|
2012-06-13 11:00:58 +03:00
|
|
|
|
2014-03-28 02:16:54 +02:00
|
|
|
// Handle sciAudio calls in fanmade games here. sciAudio is an
|
|
|
|
// external .NET library for playing MP3 files in fanmade games.
|
|
|
|
// It runs in the background, and obtains sound commands from the
|
|
|
|
// currently running game via text files (called "conductor files").
|
|
|
|
// 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.
|
2016-01-02 21:50:35 +01:00
|
|
|
if (handle == VIRTUALFILE_HANDLE_SCIAUDIO) {
|
2014-03-28 02:16:54 +02:00
|
|
|
Common::List<ExecStack>::const_iterator iter = s->_executionStack.reverse_begin();
|
|
|
|
iter--; // sciAudio
|
|
|
|
iter--; // sciAudio child
|
|
|
|
g_sci->_audio->handleFanmadeSciAudio(iter->sendp, s->_segMan);
|
|
|
|
return NULL_REG;
|
|
|
|
}
|
|
|
|
|
2012-06-13 11:00:58 +03:00
|
|
|
FileHandle *f = getFileFromHandle(s, handle);
|
2012-06-13 12:54:40 +03:00
|
|
|
|
2010-07-14 22:07:36 +00:00
|
|
|
if (f) {
|
2012-06-13 12:54:40 +03:00
|
|
|
f->_out->write(str.c_str(), str.size());
|
2012-06-13 12:19:18 +03:00
|
|
|
if (getSciVersion() <= SCI_VERSION_0_LATE)
|
|
|
|
return s->r_acc; // SCI0 semantics: no value returned
|
2012-06-13 12:54:40 +03:00
|
|
|
return NULL_REG;
|
2009-02-15 22:28:12 +00:00
|
|
|
}
|
2012-06-13 12:19:18 +03:00
|
|
|
|
|
|
|
if (getSciVersion() <= SCI_VERSION_0_LATE)
|
|
|
|
return s->r_acc; // SCI0 semantics: no value returned
|
2012-06-13 12:54:40 +03:00
|
|
|
return make_reg(0, 6); // DOS - invalid handle
|
2010-07-14 22:07:36 +00:00
|
|
|
}
|
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
reg_t kFileIOSeek(EngineState *s, int argc, reg_t *argv) {
|
2012-06-13 11:00:58 +03:00
|
|
|
uint16 handle = argv[0].toUint16();
|
2012-06-13 12:54:40 +03:00
|
|
|
uint16 offset = ABS<int16>(argv[1].toSint16()); // can be negative
|
|
|
|
uint16 whence = argv[2].toUint16();
|
|
|
|
debugC(kDebugLevelFile, "kFileIO(seek): %d, %d, %d", handle, offset, whence);
|
2011-06-20 00:59:48 +02:00
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
FileHandle *f = getFileFromHandle(s, handle);
|
|
|
|
|
|
|
|
if (f && f->_in) {
|
|
|
|
// Backward seeking isn't supported in zip file streams, thus adapt the
|
|
|
|
// parameters accordingly if games ask for such a seek mode. A known
|
|
|
|
// case where this is requested is the save file manager in Phantasmagoria
|
|
|
|
if (whence == SEEK_END) {
|
|
|
|
whence = SEEK_SET;
|
|
|
|
offset = f->_in->size() - offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
return make_reg(0, f->_in->seek(offset, whence));
|
|
|
|
} else if (f && f->_out) {
|
|
|
|
error("kFileIOSeek: Unsupported seek operation on a writeable stream (offset: %d, whence: %d)", offset, whence);
|
2010-07-14 22:07:36 +00:00
|
|
|
}
|
2012-06-13 11:00:58 +03:00
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
return SIGNAL_REG;
|
|
|
|
}
|
2010-01-27 04:48:50 +00:00
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
reg_t kFileIOFindFirst(EngineState *s, int argc, reg_t *argv) {
|
|
|
|
Common::String mask = s->_segMan->getString(argv[0]);
|
|
|
|
reg_t buf = argv[1];
|
|
|
|
int attr = argv[2].toUint16(); // We won't use this, Win32 might, though...
|
|
|
|
debugC(kDebugLevelFile, "kFileIO(findFirst): %s, 0x%x", mask.c_str(), attr);
|
|
|
|
|
|
|
|
// We remove ".*". mask will get prefixed, so we will return all additional files for that gameid
|
|
|
|
if (mask == "*.*")
|
|
|
|
mask = "*";
|
|
|
|
return s->_dirseeker.firstFile(mask, buf, s->_segMan);
|
2010-07-14 22:07:36 +00:00
|
|
|
}
|
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
reg_t kFileIOFindNext(EngineState *s, int argc, reg_t *argv) {
|
|
|
|
debugC(kDebugLevelFile, "kFileIO(findNext)");
|
|
|
|
return s->_dirseeker.nextFile(s->_segMan);
|
|
|
|
}
|
|
|
|
|
|
|
|
reg_t kFileIOExists(EngineState *s, int argc, reg_t *argv) {
|
|
|
|
Common::String name = s->_segMan->getString(argv[0]);
|
2010-07-14 22:07:36 +00:00
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
bool exists = false;
|
|
|
|
|
2016-02-22 14:22:59 +01:00
|
|
|
if (g_sci->getGameId() == GID_PEPPER) {
|
|
|
|
// HACK: Special case for Pepper's Adventure in Time
|
|
|
|
// The game checks like crazy for the file CDAUDIO when entering the game menu.
|
|
|
|
// On at least Windows that makes the engine slow down to a crawl and takes at least 1 second.
|
|
|
|
// Should get solved properly by changing the code below. This here is basically for 1.8.0 release.
|
|
|
|
// TODO: Fix this properly.
|
|
|
|
if (name == "CDAUDIO")
|
|
|
|
return NULL_REG;
|
|
|
|
}
|
|
|
|
|
2016-09-15 09:02:55 -05:00
|
|
|
#ifdef ENABLE_SCI32
|
|
|
|
if (isSaveCatalogue(name)) {
|
|
|
|
return saveCatalogueExists(name) ? TRUE_REG : NULL_REG;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2016-07-02 21:19:29 +02:00
|
|
|
// TODO: It may apparently be worth caching the existence of
|
|
|
|
// phantsg.dir, and possibly even keeping it open persistently
|
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
// Check for regular file
|
|
|
|
exists = Common::File::exists(name);
|
|
|
|
|
|
|
|
// Check for a savegame with the name
|
|
|
|
Common::SaveFileManager *saveFileMan = g_sci->getSaveFileManager();
|
|
|
|
if (!exists)
|
|
|
|
exists = !saveFileMan->listSavefiles(name).empty();
|
|
|
|
|
|
|
|
// Try searching for the file prepending "target-"
|
|
|
|
const Common::String wrappedName = g_sci->wrapFilename(name);
|
|
|
|
if (!exists) {
|
|
|
|
exists = !saveFileMan->listSavefiles(wrappedName).empty();
|
2009-02-15 22:28:12 +00:00
|
|
|
}
|
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
// SCI2+ debug mode
|
|
|
|
if (DebugMan.isDebugChannelEnabled(kDebugLevelDebugMode)) {
|
|
|
|
if (!exists && name == "1.scr") // PQ4
|
|
|
|
exists = true;
|
|
|
|
if (!exists && name == "18.scr") // QFG4
|
|
|
|
exists = true;
|
|
|
|
if (!exists && name == "99.scr") // GK1, KQ7
|
|
|
|
exists = true;
|
|
|
|
if (!exists && name == "classes") // GK2, SQ6, LSL7
|
|
|
|
exists = true;
|
|
|
|
}
|
2010-01-27 04:48:50 +00:00
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
// Special case for non-English versions of LSL5: The English version of
|
|
|
|
// LSL5 calls kFileIO(), case K_FILEIO_OPEN for reading to check if
|
|
|
|
// memory.drv exists (which is where the game's password is stored). If
|
|
|
|
// it's not found, it calls kFileIO() again, case K_FILEIO_OPEN for
|
|
|
|
// writing and creates a new file. Non-English versions call kFileIO(),
|
|
|
|
// case K_FILEIO_FILE_EXISTS instead, and fail if memory.drv can't be
|
|
|
|
// found. We create a default memory.drv file with no password, so that
|
|
|
|
// the game can continue.
|
|
|
|
if (!exists && name == "memory.drv") {
|
|
|
|
// Create a new file, and write the bytes for the empty password
|
|
|
|
// string inside
|
|
|
|
byte defaultContent[] = { 0xE9, 0xE9, 0xEB, 0xE1, 0x0D, 0x0A, 0x31, 0x30, 0x30, 0x30 };
|
|
|
|
Common::WriteStream *outFile = saveFileMan->openForSaving(wrappedName);
|
|
|
|
for (int i = 0; i < 10; i++)
|
|
|
|
outFile->writeByte(defaultContent[i]);
|
|
|
|
outFile->finalize();
|
|
|
|
exists = !outFile->err(); // check whether we managed to create the file.
|
|
|
|
delete outFile;
|
|
|
|
}
|
2012-06-13 11:02:00 +03:00
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
// Special case for KQ6 Mac: The game checks for two video files to see
|
|
|
|
// if they exist before it plays them. Since we support multiple naming
|
|
|
|
// schemes for resource fork files, we also need to support that here in
|
2012-09-26 04:17:31 +02:00
|
|
|
// case someone has a "HalfDome.bin" file, etc.
|
2012-06-13 12:54:40 +03:00
|
|
|
if (!exists && g_sci->getGameId() == GID_KQ6 && g_sci->getPlatform() == Common::kPlatformMacintosh &&
|
|
|
|
(name == "HalfDome" || name == "Kq6Movie"))
|
|
|
|
exists = Common::MacResManager::exists(name);
|
2012-06-13 11:02:00 +03:00
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
debugC(kDebugLevelFile, "kFileIO(fileExists) %s -> %d", name.c_str(), exists);
|
|
|
|
return make_reg(0, exists);
|
2012-06-13 11:02:00 +03:00
|
|
|
}
|
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
reg_t kFileIORename(EngineState *s, int argc, reg_t *argv) {
|
|
|
|
Common::String oldName = s->_segMan->getString(argv[0]);
|
|
|
|
Common::String newName = s->_segMan->getString(argv[1]);
|
|
|
|
|
2016-08-25 22:05:24 +02:00
|
|
|
// We don't fully implement all cases that could occur here, and
|
|
|
|
// assume the file to be renamed is a wrapped filename.
|
|
|
|
// Known usage: In Phant1 and KQ7 while deleting savegames.
|
|
|
|
// The scripts rewrite the dir file as a temporary file, and then
|
|
|
|
// rename it to the actual dir file.
|
|
|
|
|
|
|
|
oldName = g_sci->wrapFilename(oldName);
|
|
|
|
newName = g_sci->wrapFilename(newName);
|
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
// SCI1.1 returns 0 on success and a DOS error code on fail. SCI32
|
|
|
|
// returns -1 on fail. We just return -1 for all versions.
|
|
|
|
if (g_sci->getSaveFileManager()->renameSavefile(oldName, newName))
|
|
|
|
return NULL_REG;
|
|
|
|
else
|
|
|
|
return SIGNAL_REG;
|
|
|
|
}
|
2012-06-13 11:02:00 +03:00
|
|
|
|
|
|
|
#ifdef ENABLE_SCI32
|
2012-06-13 12:54:40 +03:00
|
|
|
reg_t kFileIOReadByte(EngineState *s, int argc, reg_t *argv) {
|
|
|
|
// Read the byte into the low byte of the accumulator
|
|
|
|
FileHandle *f = getFileFromHandle(s, argv[0].toUint16());
|
|
|
|
if (!f)
|
2012-06-13 11:02:00 +03:00
|
|
|
return NULL_REG;
|
2012-06-13 12:54:40 +03:00
|
|
|
return make_reg(0, (s->r_acc.toUint16() & 0xff00) | f->_in->readByte());
|
|
|
|
}
|
2012-06-13 11:02:00 +03:00
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
reg_t kFileIOWriteByte(EngineState *s, int argc, reg_t *argv) {
|
|
|
|
FileHandle *f = getFileFromHandle(s, argv[0].toUint16());
|
|
|
|
if (f)
|
|
|
|
f->_out->writeByte(argv[1].toUint16() & 0xff);
|
2016-09-10 14:30:23 -05:00
|
|
|
return s->r_acc;
|
2012-06-13 12:54:40 +03:00
|
|
|
}
|
2012-06-13 11:02:00 +03:00
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
reg_t kFileIOReadWord(EngineState *s, int argc, reg_t *argv) {
|
|
|
|
FileHandle *f = getFileFromHandle(s, argv[0].toUint16());
|
|
|
|
if (!f)
|
2012-06-13 11:02:00 +03:00
|
|
|
return NULL_REG;
|
2012-06-13 12:54:40 +03:00
|
|
|
return make_reg(0, f->_in->readUint16LE());
|
|
|
|
}
|
2012-06-13 11:02:00 +03:00
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
reg_t kFileIOWriteWord(EngineState *s, int argc, reg_t *argv) {
|
|
|
|
FileHandle *f = getFileFromHandle(s, argv[0].toUint16());
|
|
|
|
if (f)
|
|
|
|
f->_out->writeUint16LE(argv[1].toUint16());
|
2016-09-10 14:30:23 -05:00
|
|
|
return s->r_acc;
|
2012-06-13 11:02:00 +03:00
|
|
|
}
|
|
|
|
|
2016-09-10 14:30:23 -05:00
|
|
|
reg_t kFileIOGetCWD(EngineState *s, int argc, reg_t *argv) {
|
|
|
|
SciArray &fileName = *s->_segMan->lookupArray(argv[0]);
|
|
|
|
fileName.fromString("C:\\SIERRA\\");
|
|
|
|
return argv[0];
|
2010-07-14 22:07:36 +00:00
|
|
|
}
|
2009-10-29 18:07:39 +00:00
|
|
|
|
2012-07-04 00:59:55 +03:00
|
|
|
reg_t kFileIOIsValidDirectory(EngineState *s, int argc, reg_t *argv) {
|
|
|
|
// Used in Torin's Passage and LSL7 to determine if the directory passed as
|
|
|
|
// a parameter (usually the save directory) is valid. We always return true
|
|
|
|
// here.
|
|
|
|
return TRUE_REG;
|
|
|
|
}
|
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
#endif
|
|
|
|
|
|
|
|
// ---- Save operations -------------------------------------------------------
|
2011-06-20 00:59:48 +02:00
|
|
|
|
2012-06-13 11:00:58 +03:00
|
|
|
#ifdef ENABLE_SCI32
|
2012-06-13 12:54:40 +03:00
|
|
|
|
|
|
|
reg_t kSave(EngineState *s, int argc, reg_t *argv) {
|
|
|
|
if (!s)
|
|
|
|
return make_reg(0, getSciVersion());
|
|
|
|
error("not supposed to call this");
|
|
|
|
}
|
|
|
|
|
2012-06-13 11:00:58 +03:00
|
|
|
#endif
|
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
reg_t kSaveGame(EngineState *s, int argc, reg_t *argv) {
|
2016-09-09 14:21:33 -05:00
|
|
|
// slot 0 is the ScummVM auto-save slot, which is not used by us, but is
|
|
|
|
// still reserved
|
|
|
|
enum {
|
|
|
|
SAVEGAMESLOT_FIRST = 1,
|
|
|
|
SAVEGAMESLOT_LAST = 99
|
|
|
|
};
|
|
|
|
|
2016-07-01 14:57:03 +02:00
|
|
|
Common::String game_id = !argv[0].isNull() ? s->_segMan->getString(argv[0]) : "";
|
2013-11-23 11:02:24 +01:00
|
|
|
int16 virtualId = argv[1].toSint16();
|
2012-06-13 12:54:40 +03:00
|
|
|
int16 savegameId = -1;
|
|
|
|
Common::String game_description;
|
|
|
|
Common::String version;
|
2010-08-22 03:50:42 +00:00
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
if (argc > 3)
|
|
|
|
version = s->_segMan->getString(argv[3]);
|
|
|
|
|
|
|
|
// We check here, we don't want to delete a users save in case we are within a kernel function
|
|
|
|
if (s->executionStackBase) {
|
|
|
|
warning("kSaveGame - won't save from within kernel function");
|
|
|
|
return NULL_REG;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (argv[0].isNull()) {
|
|
|
|
// Direct call, from a patched Game::save
|
|
|
|
if ((argv[1] != SIGNAL_REG) || (!argv[2].isNull()))
|
|
|
|
error("kSaveGame: assumed patched call isn't accurate");
|
|
|
|
|
|
|
|
// we are supposed to show a dialog for the user and let him choose where to save
|
|
|
|
g_sci->_soundCmd->pauseAll(true); // pause music
|
|
|
|
GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Save game:"), _("Save"), true);
|
|
|
|
savegameId = dialog->runModalWithCurrentTarget();
|
|
|
|
game_description = dialog->getResultString();
|
|
|
|
if (game_description.empty()) {
|
2014-02-17 11:52:49 +02:00
|
|
|
// create our own description for the saved game, the user didn't enter it
|
2012-06-13 12:54:40 +03:00
|
|
|
game_description = dialog->createDefaultSaveDescription(savegameId);
|
2012-06-13 11:00:58 +03:00
|
|
|
}
|
2012-06-13 12:54:40 +03:00
|
|
|
delete dialog;
|
2014-02-17 11:52:49 +02:00
|
|
|
g_sci->_soundCmd->pauseAll(false); // unpause music (we can't have it paused during save)
|
2012-06-13 12:54:40 +03:00
|
|
|
if (savegameId < 0)
|
|
|
|
return NULL_REG;
|
|
|
|
} else {
|
|
|
|
// Real call from script
|
|
|
|
if (argv[2].isNull())
|
|
|
|
error("kSaveGame: called with description being NULL");
|
|
|
|
game_description = s->_segMan->getString(argv[2]);
|
|
|
|
|
|
|
|
debug(3, "kSaveGame(%s,%d,%s,%s)", game_id.c_str(), virtualId, game_description.c_str(), version.c_str());
|
|
|
|
|
|
|
|
Common::Array<SavegameDesc> saves;
|
|
|
|
listSavegames(saves);
|
|
|
|
|
|
|
|
if ((virtualId >= SAVEGAMEID_OFFICIALRANGE_START) && (virtualId <= SAVEGAMEID_OFFICIALRANGE_END)) {
|
|
|
|
// savegameId is an actual Id, so search for it just to make sure
|
|
|
|
savegameId = virtualId - SAVEGAMEID_OFFICIALRANGE_START;
|
|
|
|
if (findSavegame(saves, savegameId) == -1)
|
|
|
|
return NULL_REG;
|
|
|
|
} else if (virtualId < SAVEGAMEID_OFFICIALRANGE_START) {
|
|
|
|
// virtualId is low, we assume that scripts expect us to create new slot
|
2016-06-25 23:58:16 +02:00
|
|
|
switch (g_sci->getGameId()) {
|
|
|
|
case GID_JONES:
|
2013-03-21 21:05:28 +02:00
|
|
|
// Jones has one save slot only
|
|
|
|
savegameId = 0;
|
2016-06-25 23:58:16 +02:00
|
|
|
break;
|
|
|
|
case GID_FANMADE: {
|
|
|
|
// Fanmade game, try to identify the game
|
|
|
|
const char *gameName = g_sci->getGameObjectName();
|
|
|
|
|
|
|
|
if (strcmp(gameName, "CascadeQuest") == 0) {
|
|
|
|
// Cascade Quest calls us directly to auto-save and uses slot 99,
|
|
|
|
// put that save into slot 0 (ScummVM auto-save slot) see bug #7007
|
|
|
|
if (virtualId == (SAVEGAMEID_OFFICIALRANGE_START - 1)) {
|
|
|
|
savegameId = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (savegameId < 0) {
|
|
|
|
// savegameId not set yet
|
|
|
|
if (virtualId == s->_lastSaveVirtualId) {
|
|
|
|
// if last virtual id is the same as this one, we assume that caller wants to overwrite last save
|
|
|
|
savegameId = s->_lastSaveNewId;
|
|
|
|
} else {
|
|
|
|
uint savegameNr;
|
|
|
|
// savegameId is in lower range, scripts expect us to create a new slot
|
|
|
|
for (savegameId = SAVEGAMESLOT_FIRST; savegameId <= SAVEGAMESLOT_LAST; savegameId++) {
|
|
|
|
for (savegameNr = 0; savegameNr < saves.size(); savegameNr++) {
|
|
|
|
if (savegameId == saves[savegameNr].id)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (savegameNr == saves.size()) // Slot not found, seems to be good to go
|
2012-06-13 12:54:40 +03:00
|
|
|
break;
|
|
|
|
}
|
2016-06-25 23:58:16 +02:00
|
|
|
if (savegameId > SAVEGAMESLOT_LAST)
|
|
|
|
error("kSavegame: no more savegame slots available");
|
2012-06-13 12:54:40 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
error("kSaveGame: invalid savegameId used");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Save in case caller wants to overwrite last newly created save
|
|
|
|
s->_lastSaveVirtualId = virtualId;
|
|
|
|
s->_lastSaveNewId = savegameId;
|
2012-06-13 11:00:58 +03:00
|
|
|
}
|
2010-08-22 03:50:42 +00:00
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
s->r_acc = NULL_REG;
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
Common::String filename = g_sci->getSavegameName(savegameId);
|
|
|
|
Common::SaveFileManager *saveFileMan = g_sci->getSaveFileManager();
|
|
|
|
Common::OutSaveFile *out;
|
|
|
|
|
|
|
|
out = saveFileMan->openForSaving(filename);
|
|
|
|
if (!out) {
|
|
|
|
warning("Error opening savegame \"%s\" for writing", filename.c_str());
|
|
|
|
} else {
|
|
|
|
if (!gamestate_save(s, out, game_description, version)) {
|
|
|
|
warning("Saving the game failed");
|
|
|
|
} else {
|
|
|
|
s->r_acc = TRUE_REG; // save successful
|
|
|
|
}
|
|
|
|
|
|
|
|
out->finalize();
|
|
|
|
if (out->err()) {
|
|
|
|
warning("Writing the savegame failed");
|
|
|
|
s->r_acc = NULL_REG; // write failure
|
|
|
|
}
|
|
|
|
delete out;
|
|
|
|
}
|
2009-02-27 01:17:24 +00:00
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
return s->r_acc;
|
2010-07-14 22:07:36 +00:00
|
|
|
}
|
2009-10-29 16:05:24 +00:00
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
reg_t kRestoreGame(EngineState *s, int argc, reg_t *argv) {
|
|
|
|
Common::String game_id = !argv[0].isNull() ? s->_segMan->getString(argv[0]) : "";
|
|
|
|
int16 savegameId = argv[1].toSint16();
|
|
|
|
bool pausedMusic = false;
|
2009-10-29 16:05:24 +00:00
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
debug(3, "kRestoreGame(%s,%d)", game_id.c_str(), savegameId);
|
2009-05-19 11:30:51 +00:00
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
if (argv[0].isNull()) {
|
|
|
|
// Direct call, either from launcher or from a patched Game::restore
|
|
|
|
if (savegameId == -1) {
|
|
|
|
// we are supposed to show a dialog for the user and let him choose a saved game
|
|
|
|
g_sci->_soundCmd->pauseAll(true); // pause music
|
|
|
|
GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Restore game:"), _("Restore"), false);
|
|
|
|
savegameId = dialog->runModalWithCurrentTarget();
|
|
|
|
delete dialog;
|
|
|
|
if (savegameId < 0) {
|
|
|
|
g_sci->_soundCmd->pauseAll(false); // unpause music
|
|
|
|
return s->r_acc;
|
|
|
|
}
|
|
|
|
pausedMusic = true;
|
|
|
|
}
|
|
|
|
// don't adjust ID of the saved game, it's already correct
|
2016-09-15 09:02:55 -05:00
|
|
|
} else {
|
2013-03-21 21:05:28 +02:00
|
|
|
if (g_sci->getGameId() == GID_JONES) {
|
|
|
|
// Jones has one save slot only
|
|
|
|
savegameId = 0;
|
|
|
|
} else {
|
|
|
|
// Real call from script, we need to adjust ID
|
|
|
|
if ((savegameId < SAVEGAMEID_OFFICIALRANGE_START) || (savegameId > SAVEGAMEID_OFFICIALRANGE_END)) {
|
|
|
|
warning("Savegame ID %d is not allowed", savegameId);
|
|
|
|
return TRUE_REG;
|
|
|
|
}
|
|
|
|
savegameId -= SAVEGAMEID_OFFICIALRANGE_START;
|
2012-06-13 12:54:40 +03:00
|
|
|
}
|
|
|
|
}
|
2012-06-13 11:00:58 +03:00
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
s->r_acc = NULL_REG; // signals success
|
2011-02-10 13:39:01 +00:00
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
Common::Array<SavegameDesc> saves;
|
2016-09-15 09:02:55 -05:00
|
|
|
listSavegames(saves);
|
|
|
|
if (findSavegame(saves, savegameId) == -1) {
|
2012-06-13 12:54:40 +03:00
|
|
|
s->r_acc = TRUE_REG;
|
|
|
|
warning("Savegame ID %d not found", savegameId);
|
|
|
|
} else {
|
|
|
|
Common::SaveFileManager *saveFileMan = g_sci->getSaveFileManager();
|
|
|
|
Common::String filename = g_sci->getSavegameName(savegameId);
|
|
|
|
Common::SeekableReadStream *in;
|
2010-07-14 22:07:36 +00:00
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
in = saveFileMan->openForLoading(filename);
|
|
|
|
if (in) {
|
|
|
|
// found a savegame file
|
|
|
|
gamestate_restore(s, in);
|
|
|
|
delete in;
|
2010-07-14 22:07:36 +00:00
|
|
|
|
2016-02-09 01:28:08 +01:00
|
|
|
gamestate_afterRestoreFixUp(s, savegameId);
|
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
} else {
|
|
|
|
s->r_acc = TRUE_REG;
|
|
|
|
warning("Savegame #%d not found", savegameId);
|
|
|
|
}
|
2011-10-29 18:31:53 +03:00
|
|
|
}
|
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
if (!s->r_acc.isNull()) {
|
|
|
|
// no success?
|
|
|
|
if (pausedMusic)
|
|
|
|
g_sci->_soundCmd->pauseAll(false); // unpause music
|
2010-07-14 22:07:36 +00:00
|
|
|
}
|
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
return s->r_acc;
|
|
|
|
}
|
2012-03-08 08:03:50 -05:00
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
reg_t kGetSaveDir(EngineState *s, int argc, reg_t *argv) {
|
|
|
|
#ifdef ENABLE_SCI32
|
|
|
|
// SCI32 uses a parameter here. It is used to modify a string, stored in a
|
|
|
|
// global variable, so that game scripts store the save directory. We
|
|
|
|
// don't really set a save game directory, thus not setting the string to
|
|
|
|
// anything is the correct thing to do here.
|
|
|
|
//if (argc > 0)
|
|
|
|
// warning("kGetSaveDir called with %d parameter(s): %04x:%04x", argc, PRINT_REG(argv[0]));
|
|
|
|
#endif
|
|
|
|
return s->_segMan->getSaveDirPtr();
|
2010-07-14 22:07:36 +00:00
|
|
|
}
|
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
reg_t kCheckSaveGame(EngineState *s, int argc, reg_t *argv) {
|
|
|
|
Common::String game_id = s->_segMan->getString(argv[0]);
|
|
|
|
uint16 virtualId = argv[1].toUint16();
|
2010-07-14 22:07:36 +00:00
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
debug(3, "kCheckSaveGame(%s, %d)", game_id.c_str(), virtualId);
|
2009-12-24 02:43:07 +00:00
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
Common::Array<SavegameDesc> saves;
|
|
|
|
listSavegames(saves);
|
|
|
|
|
|
|
|
// we allow 0 (happens in QfG2 when trying to restore from an empty saved game list) and return false in that case
|
|
|
|
if (virtualId == 0)
|
2010-07-14 22:07:36 +00:00
|
|
|
return NULL_REG;
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2013-03-21 21:05:28 +02:00
|
|
|
uint savegameId = 0;
|
|
|
|
if (g_sci->getGameId() == GID_JONES) {
|
|
|
|
// Jones has one save slot only
|
|
|
|
} else {
|
|
|
|
// Find saved game
|
|
|
|
if ((virtualId < SAVEGAMEID_OFFICIALRANGE_START) || (virtualId > SAVEGAMEID_OFFICIALRANGE_END))
|
|
|
|
error("kCheckSaveGame: called with invalid savegame ID (%d)", virtualId);
|
|
|
|
savegameId = virtualId - SAVEGAMEID_OFFICIALRANGE_START;
|
|
|
|
}
|
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
int savegameNr = findSavegame(saves, savegameId);
|
|
|
|
if (savegameNr == -1)
|
|
|
|
return NULL_REG;
|
2009-02-21 10:23:36 +00:00
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
// Check for compatible savegame version
|
|
|
|
int ver = saves[savegameNr].version;
|
|
|
|
if (ver < MINIMUM_SAVEGAME_VERSION || ver > CURRENT_SAVEGAME_VERSION)
|
2010-07-14 22:07:36 +00:00
|
|
|
return NULL_REG;
|
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
// Otherwise we assume the savegame is OK
|
|
|
|
return TRUE_REG;
|
2010-07-14 22:07:36 +00:00
|
|
|
}
|
2010-07-21 21:18:21 +00:00
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
reg_t kGetSaveFiles(EngineState *s, int argc, reg_t *argv) {
|
|
|
|
// Scripts ask for current save files, we can assume that if afterwards they ask us to create a new slot they really
|
|
|
|
// mean new slot instead of overwriting the old one
|
|
|
|
s->_lastSaveVirtualId = SAVEGAMEID_OFFICIALRANGE_START;
|
2011-01-08 10:23:27 +00:00
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
Common::Array<SavegameDesc> saves;
|
|
|
|
listSavegames(saves);
|
|
|
|
uint totalSaves = MIN<uint>(saves.size(), MAX_SAVEGAME_NR);
|
2011-01-08 10:23:27 +00:00
|
|
|
|
2016-09-09 14:21:33 -05:00
|
|
|
Common::String game_id = s->_segMan->getString(argv[0]);
|
|
|
|
|
|
|
|
debug(3, "kGetSaveFiles(%s)", game_id.c_str());
|
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
reg_t *slot = s->_segMan->derefRegPtr(argv[2], totalSaves);
|
2011-01-08 10:23:27 +00:00
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
if (!slot) {
|
|
|
|
warning("kGetSaveFiles: %04X:%04X invalid or too small to hold slot data", PRINT_REG(argv[2]));
|
|
|
|
totalSaves = 0;
|
|
|
|
}
|
2011-01-08 10:23:27 +00:00
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
const uint bufSize = (totalSaves * SCI_MAX_SAVENAME_LENGTH) + 1;
|
|
|
|
char *saveNames = new char[bufSize];
|
|
|
|
char *saveNamePtr = saveNames;
|
|
|
|
|
|
|
|
for (uint i = 0; i < totalSaves; i++) {
|
2013-04-27 14:02:49 +03:00
|
|
|
*slot++ = make_reg(0, saves[i].id + SAVEGAMEID_OFFICIALRANGE_START); // Store the virtual savegame ID (see above)
|
2012-06-13 12:54:40 +03:00
|
|
|
strcpy(saveNamePtr, saves[i].name);
|
|
|
|
saveNamePtr += SCI_MAX_SAVENAME_LENGTH;
|
2010-07-21 21:18:21 +00:00
|
|
|
}
|
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
*saveNamePtr = 0; // Terminate list
|
|
|
|
|
|
|
|
s->_segMan->memcpy(argv[1], (byte *)saveNames, bufSize);
|
|
|
|
delete[] saveNames;
|
|
|
|
|
|
|
|
return make_reg(0, totalSaves);
|
2010-07-21 21:18:21 +00:00
|
|
|
}
|
|
|
|
|
2012-06-13 12:54:40 +03:00
|
|
|
#ifdef ENABLE_SCI32
|
2016-09-20 21:07:20 -05:00
|
|
|
|
2016-09-13 10:32:41 -05:00
|
|
|
reg_t kSaveGame32(EngineState *s, int argc, reg_t *argv) {
|
2016-09-20 21:07:20 -05:00
|
|
|
const bool isScummVMSave = argv[0].isNull();
|
2016-09-15 08:54:10 -05:00
|
|
|
Common::String gameName = "";
|
|
|
|
int16 saveNo;
|
|
|
|
Common::String saveDescription;
|
|
|
|
Common::String gameVersion = (argc <= 3 || argv[3].isNull()) ? "" : s->_segMan->getString(argv[3]);
|
|
|
|
|
2016-09-20 21:07:20 -05:00
|
|
|
if (isScummVMSave) {
|
2016-09-15 08:54:10 -05:00
|
|
|
// ScummVM call, from a patched Game::save
|
|
|
|
g_sci->_soundCmd->pauseAll(true);
|
|
|
|
GUI::SaveLoadChooser dialog(_("Save game:"), _("Save"), true);
|
|
|
|
saveNo = dialog.runModalWithCurrentTarget();
|
|
|
|
g_sci->_soundCmd->pauseAll(false);
|
|
|
|
|
|
|
|
if (saveNo < 0) {
|
2016-09-20 21:07:20 -05:00
|
|
|
// User cancelled save
|
2016-09-15 08:54:10 -05:00
|
|
|
return NULL_REG;
|
|
|
|
}
|
|
|
|
|
|
|
|
saveDescription = dialog.getResultString();
|
|
|
|
if (saveDescription.empty()) {
|
|
|
|
saveDescription = dialog.createDefaultSaveDescription(saveNo);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Native script call
|
|
|
|
gameName = s->_segMan->getString(argv[0]);
|
|
|
|
saveNo = argv[1].toSint16();
|
|
|
|
saveDescription = argv[2].isNull() ? "" : s->_segMan->getString(argv[2]);
|
|
|
|
}
|
2016-09-09 14:21:33 -05:00
|
|
|
|
2016-09-20 21:07:20 -05:00
|
|
|
debugC(kDebugLevelFile, "Game name %s save %d desc %s ver %s", gameName.c_str(), saveNo, saveDescription.c_str(), gameVersion.c_str());
|
|
|
|
|
2016-09-09 14:21:33 -05:00
|
|
|
// Auto-save system used by Torin and LSL7
|
|
|
|
if (gameName == "Autosave" || gameName == "Autosv") {
|
|
|
|
if (saveNo == 0) {
|
|
|
|
// Autosave slot 0 is the autosave
|
|
|
|
} else {
|
|
|
|
// Autosave slot 1 is a "new game" save
|
|
|
|
saveNo = kNewGameId;
|
|
|
|
}
|
2016-09-20 21:07:20 -05:00
|
|
|
} else if (!isScummVMSave) {
|
|
|
|
// ScummVM save screen will give a pre-corrected save number, but native
|
|
|
|
// save-load will not
|
|
|
|
saveNo += kSaveIdShift;
|
2016-09-12 11:08:02 -05:00
|
|
|
}
|
2016-09-09 14:21:33 -05:00
|
|
|
|
|
|
|
Common::SaveFileManager *saveFileMan = g_sci->getSaveFileManager();
|
|
|
|
const Common::String filename = g_sci->getSavegameName(saveNo);
|
|
|
|
Common::OutSaveFile *saveStream = saveFileMan->openForSaving(filename);
|
|
|
|
|
|
|
|
if (saveStream == nullptr) {
|
|
|
|
warning("Error opening savegame \"%s\" for writing", filename.c_str());
|
|
|
|
return NULL_REG;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!gamestate_save(s, saveStream, saveDescription, gameVersion)) {
|
|
|
|
warning("Saving the game failed");
|
|
|
|
saveStream->finalize();
|
|
|
|
delete saveStream;
|
|
|
|
return NULL_REG;
|
|
|
|
}
|
|
|
|
|
|
|
|
saveStream->finalize();
|
|
|
|
if (saveStream->err()) {
|
|
|
|
warning("Writing the savegame failed");
|
|
|
|
delete saveStream;
|
|
|
|
return NULL_REG;
|
|
|
|
}
|
|
|
|
|
|
|
|
delete saveStream;
|
|
|
|
return TRUE_REG;
|
|
|
|
}
|
|
|
|
|
2016-09-13 10:32:41 -05:00
|
|
|
reg_t kRestoreGame32(EngineState *s, int argc, reg_t *argv) {
|
2016-09-20 21:07:20 -05:00
|
|
|
const bool isScummVMRestore = argv[0].isNull();
|
2016-09-15 08:54:10 -05:00
|
|
|
Common::String gameName = "";
|
2016-09-09 14:21:33 -05:00
|
|
|
int16 saveNo = argv[1].toSint16();
|
|
|
|
const Common::String gameVersion = argv[2].isNull() ? "" : s->_segMan->getString(argv[2]);
|
|
|
|
|
2016-09-20 21:07:20 -05:00
|
|
|
if (isScummVMRestore && saveNo == -1) {
|
2016-09-15 08:54:10 -05:00
|
|
|
// ScummVM call, either from lancher or a patched Game::restore
|
|
|
|
g_sci->_soundCmd->pauseAll(true);
|
|
|
|
GUI::SaveLoadChooser dialog(_("Restore game:"), _("Restore"), false);
|
|
|
|
saveNo = dialog.runModalWithCurrentTarget();
|
|
|
|
g_sci->_soundCmd->pauseAll(false);
|
2016-09-20 21:07:20 -05:00
|
|
|
|
2016-09-15 08:54:10 -05:00
|
|
|
if (saveNo < 0) {
|
2016-09-20 21:07:20 -05:00
|
|
|
// User cancelled restore
|
2016-09-15 08:54:10 -05:00
|
|
|
return s->r_acc;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
gameName = s->_segMan->getString(argv[0]);
|
|
|
|
}
|
|
|
|
|
2016-09-09 14:21:33 -05:00
|
|
|
if (gameName == "Autosave" || gameName == "Autosv") {
|
|
|
|
if (saveNo == 0) {
|
|
|
|
// Autosave slot 0 is the autosave
|
|
|
|
} else {
|
|
|
|
// Autosave slot 1 is a "new game" save
|
|
|
|
saveNo = kNewGameId;
|
|
|
|
}
|
2016-09-20 21:07:20 -05:00
|
|
|
} else if (!isScummVMRestore) {
|
|
|
|
// ScummVM save screen will give a pre-corrected save number, but native
|
|
|
|
// save-load will not
|
|
|
|
saveNo += kSaveIdShift;
|
2016-09-09 14:21:33 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
Common::SaveFileManager *saveFileMan = g_sci->getSaveFileManager();
|
|
|
|
const Common::String filename = g_sci->getSavegameName(saveNo);
|
|
|
|
Common::SeekableReadStream *saveStream = saveFileMan->openForLoading(filename);
|
|
|
|
|
|
|
|
if (saveStream == nullptr) {
|
|
|
|
warning("Savegame #%d not found", saveNo);
|
|
|
|
return NULL_REG;
|
|
|
|
}
|
|
|
|
|
|
|
|
gamestate_restore(s, saveStream);
|
|
|
|
delete saveStream;
|
|
|
|
|
|
|
|
gamestate_afterRestoreFixUp(s, saveNo);
|
|
|
|
return TRUE_REG;
|
|
|
|
}
|
|
|
|
|
2016-09-13 10:32:41 -05:00
|
|
|
reg_t kCheckSaveGame32(EngineState *s, int argc, reg_t *argv) {
|
2016-09-09 14:21:33 -05:00
|
|
|
const Common::String gameName = s->_segMan->getString(argv[0]);
|
|
|
|
int16 saveNo = argv[1].toSint16();
|
|
|
|
const Common::String gameVersion = argv[2].isNull() ? "" : s->_segMan->getString(argv[2]);
|
|
|
|
|
|
|
|
Common::Array<SavegameDesc> saves;
|
|
|
|
listSavegames(saves);
|
|
|
|
|
2016-09-20 21:07:20 -05:00
|
|
|
if (gameName == "Autosave" || gameName == "Autosv") {
|
|
|
|
if (saveNo == 1) {
|
|
|
|
saveNo = kNewGameId;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
saveNo += kSaveIdShift;
|
2016-09-09 14:21:33 -05:00
|
|
|
}
|
|
|
|
|
2016-09-12 11:08:02 -05:00
|
|
|
SavegameDesc save;
|
|
|
|
if (!fillSavegameDesc(g_sci->getSavegameName(saveNo), &save)) {
|
|
|
|
return NULL_REG;
|
|
|
|
}
|
2016-09-09 14:21:33 -05:00
|
|
|
|
|
|
|
if (save.version < MINIMUM_SAVEGAME_VERSION ||
|
|
|
|
save.version > CURRENT_SAVEGAME_VERSION ||
|
|
|
|
save.gameVersion != gameVersion) {
|
|
|
|
|
|
|
|
return NULL_REG;
|
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE_REG;
|
|
|
|
}
|
|
|
|
|
2016-09-13 10:32:41 -05:00
|
|
|
reg_t kGetSaveFiles32(EngineState *s, int argc, reg_t *argv) {
|
2016-09-09 14:21:33 -05:00
|
|
|
// argv[0] is gameName, used in SSCI as the name of the save game catalogue
|
|
|
|
// but unused here since ScummVM does not support multiple catalogues
|
|
|
|
SciArray &descriptions = *s->_segMan->lookupArray(argv[1]);
|
|
|
|
SciArray &saveIds = *s->_segMan->lookupArray(argv[2]);
|
|
|
|
|
|
|
|
Common::Array<SavegameDesc> saves;
|
|
|
|
listSavegames(saves);
|
|
|
|
|
|
|
|
// Normally SSCI limits to 20 games per directory, but ScummVM allows more
|
2016-09-20 21:07:20 -05:00
|
|
|
// than that with games that use the standard save-load dialogue
|
2016-09-13 10:32:41 -05:00
|
|
|
descriptions.resize(SCI_MAX_SAVENAME_LENGTH * saves.size() + 1, true);
|
2016-09-15 09:03:29 -05:00
|
|
|
saveIds.resize(saves.size() + 1, true);
|
2016-09-09 14:21:33 -05:00
|
|
|
|
|
|
|
for (uint i = 0; i < saves.size(); ++i) {
|
|
|
|
const SavegameDesc &save = saves[i];
|
|
|
|
char *target = &descriptions.charAt(SCI_MAX_SAVENAME_LENGTH * i);
|
|
|
|
Common::strlcpy(target, save.name, SCI_MAX_SAVENAME_LENGTH);
|
2016-09-20 21:07:20 -05:00
|
|
|
saveIds.int16At(i) = save.id - kSaveIdShift;
|
2016-09-09 14:21:33 -05:00
|
|
|
}
|
|
|
|
|
2016-09-13 10:32:41 -05:00
|
|
|
descriptions.charAt(SCI_MAX_SAVENAME_LENGTH * saves.size()) = '\0';
|
2016-09-15 09:03:29 -05:00
|
|
|
saveIds.int16At(saves.size()) = 0;
|
2016-09-13 10:32:41 -05:00
|
|
|
|
2016-09-09 14:21:33 -05:00
|
|
|
return make_reg(0, saves.size());
|
|
|
|
}
|
2012-06-13 12:54:40 +03:00
|
|
|
|
2012-05-14 11:04:58 +03:00
|
|
|
reg_t kMakeSaveCatName(EngineState *s, int argc, reg_t *argv) {
|
2016-09-13 10:32:41 -05:00
|
|
|
// ScummVM does not use SCI catalogues for save games, but game scripts try
|
|
|
|
// to write out catalogues manually after a save game is deleted, so we need
|
|
|
|
// to be able to identify and ignore these IO operations by always giving
|
|
|
|
// back a fixed catalogue name and then ignoring it in kFileIO
|
|
|
|
SciArray &outCatName = *s->_segMan->lookupArray(argv[0]);
|
|
|
|
outCatName.fromString("fake.cat");
|
2012-05-14 11:04:58 +03:00
|
|
|
return argv[0];
|
|
|
|
}
|
|
|
|
|
2016-09-13 10:32:41 -05:00
|
|
|
reg_t kMakeSaveFileName(EngineState *s, int argc, reg_t *argv) {
|
2016-09-12 11:08:02 -05:00
|
|
|
SciArray &outFileName = *s->_segMan->lookupArray(argv[0]);
|
|
|
|
// argv[1] is the game name, which is not used by ScummVM
|
2016-09-20 21:07:20 -05:00
|
|
|
const int16 saveNo = argv[2].toSint16();
|
2016-09-12 11:08:02 -05:00
|
|
|
outFileName.fromString(g_sci->getSavegameName(saveNo));
|
2012-05-14 11:04:58 +03:00
|
|
|
return argv[0];
|
|
|
|
}
|
|
|
|
|
2010-07-14 22:07:36 +00:00
|
|
|
#endif
|
|
|
|
|
2009-02-21 10:23:36 +00:00
|
|
|
} // End of namespace Sci
|