1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "base/plugins.h"
24
25 #include "engines/advancedDetector.h"
26
27 #include "common/system.h"
28 #include "common/textconsole.h"
29 #include "common/translation.h"
30 #include "common/util.h"
31
32 #include "cine/cine.h"
33 #include "cine/various.h"
34
35 #include "cine/detection.h"
36
37 namespace Cine {
38
39 #define MAX_SAVEGAMES (ARRAYSIZE(Cine::currentSaveName))
40 #define SAVEGAME_NAME_LEN (sizeof(Cine::currentSaveName[0]))
41 #define SAVELIST_SIZE (MAX_SAVEGAMES * SAVEGAME_NAME_LEN)
42
mayHave256Colors() const43 bool CineEngine::mayHave256Colors() const { return getGameType() == Cine::GType_OS && getPlatform() == Common::kPlatformDOS; }
getGameType() const44 int CineEngine::getGameType() const { return _gameDescription->gameType; }
getFeatures() const45 uint32 CineEngine::getFeatures() const { return _gameDescription->features; }
getLanguage() const46 Common::Language CineEngine::getLanguage() const { return _gameDescription->desc.language; }
getPlatform() const47 Common::Platform CineEngine::getPlatform() const { return _gameDescription->desc.platform; }
48
49 } // End of namespace Cine
50
51 class CineMetaEngine : public AdvancedMetaEngine {
52 public:
getName() const53 const char *getName() const override {
54 return "cine";
55 }
56
57 Common::Error createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const override;
58
59 bool hasFeature(MetaEngineFeature f) const override;
60 SaveStateList listSaves(const char *target) const override;
61 int getMaximumSaveSlot() const override;
62 void removeSaveState(const char *target, int slot) const override;
63 Common::String getSavegameFile(int saveGameIdx, const char *target = nullptr) const override;
64 SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const override;
65 };
66
hasFeature(MetaEngineFeature f) const67 bool CineMetaEngine::hasFeature(MetaEngineFeature f) const {
68 return
69 (f == kSupportsListSaves) ||
70 (f == kSupportsLoadingDuringStartup) ||
71 (f == kSupportsDeleteSave) ||
72 (f == kSavesSupportMetaInfo) ||
73 (f == kSavesSupportThumbnail) ||
74 (f == kSavesSupportCreationDate) ||
75 (f == kSavesSupportPlayTime) ||
76 (f == kSavesUseExtendedFormat);
77 }
78
hasFeature(EngineFeature f) const79 bool Cine::CineEngine::hasFeature(EngineFeature f) const {
80 return
81 (f == kSupportsReturnToLauncher) ||
82 (f == kSupportsLoadingDuringRuntime) ||
83 (f == kSupportsSavingDuringRuntime);
84 }
85
createInstance(OSystem * syst,Engine ** engine,const ADGameDescription * desc) const86 Common::Error CineMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
87 *engine = new Cine::CineEngine(syst, (const Cine::CINEGameDescription *)desc);
88 return Common::kNoError;
89 }
90
listSaves(const char * target) const91 SaveStateList CineMetaEngine::listSaves(const char *target) const {
92 Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
93 SaveStateList saveList;
94
95 Common::String pattern;
96
97 Common::StringArray::const_iterator file;
98
99 Common::String filename = target;
100 filename += ".dir";
101 Common::InSaveFile *in = saveFileMan->openForLoading(filename);
102 bool foundAutosave = false;
103 if (in) {
104 typedef char CommandeType[SAVEGAME_NAME_LEN];
105 CommandeType saveNames[MAX_SAVEGAMES];
106
107 // Initialize all savegames' descriptions to empty strings
108 // so that if the savegames' descriptions can only be partially read from file
109 // then the missing ones are correctly set to empty strings.
110 memset(saveNames, 0, sizeof(saveNames));
111
112 in->read(saveNames, SAVELIST_SIZE);
113 CommandeType saveDesc;
114
115 pattern = target;
116 pattern += ".#*";
117 Common::StringArray filenames = saveFileMan->listSavefiles(pattern);
118
119 for (file = filenames.begin(); file != filenames.end(); ++file) {
120 // Obtain the extension part of the filename, since it corresponds to the save slot number
121 Common::String ext = Common::lastPathComponent(*file, '.');
122 int slotNum = (int)ext.asUint64();
123
124 if (ext.equals(Common::String::format("%d", slotNum)) &&
125 slotNum >= 0 && slotNum < MAX_SAVEGAMES) {
126 // Copy the savegame description making sure it ends with a trailing zero
127 strncpy(saveDesc, saveNames[slotNum], SAVEGAME_NAME_LEN);
128 saveDesc[sizeof(CommandeType) - 1] = 0;
129
130 SaveStateDescriptor saveStateDesc(this, slotNum, saveDesc);
131
132 if (saveStateDesc.getDescription().empty()) {
133 if (saveStateDesc.isAutosave()) {
134 saveStateDesc.setDescription(_("Unnamed autosave"));
135 } else {
136 saveStateDesc.setDescription(_("Unnamed savegame"));
137 }
138 }
139
140 if (saveStateDesc.isAutosave()) {
141 foundAutosave = true;
142 }
143
144 saveList.push_back(saveStateDesc);
145 }
146 }
147 }
148
149 delete in;
150
151 // No saving on empty autosave slot
152 if (!foundAutosave) {
153 SaveStateDescriptor desc(this, getAutosaveSlot(), _("Empty autosave"));
154 saveList.push_back(desc);
155 }
156
157 // Sort saves based on slot number.
158 Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
159 return saveList;
160 }
161
getMaximumSaveSlot() const162 int CineMetaEngine::getMaximumSaveSlot() const { return MAX_SAVEGAMES - 1; }
163
getSavegameFile(int saveGameIdx,const char * target) const164 Common::String CineMetaEngine::getSavegameFile(int saveGameIdx, const char *target) const {
165 return Common::String::format("%s.%d", target == nullptr ? getEngineId() : target, saveGameIdx);
166 }
167
querySaveMetaInfos(const char * target,int slot) const168 SaveStateDescriptor CineMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
169 if (slot < 0 || slot > getMaximumSaveSlot()) {
170 // HACK: Try to make SaveLoadChooserGrid::open() not use save slot
171 // numbers over the maximum save slot number for "New save".
172 SaveStateDescriptor desc;
173 desc.setWriteProtectedFlag(true);
174 return desc;
175 }
176
177 Common::ScopedPtr<Common::InSaveFile> f(g_system->getSavefileManager()->openForLoading(
178 getSavegameFile(slot, target)));
179
180 if (f) {
181 // Create the return descriptor
182 SaveStateDescriptor desc(this, slot, Common::U32String());
183
184 ExtendedSavegameHeader header;
185 if (readSavegameHeader(f.get(), &header, false)) {
186 parseSavegameHeader(&header, &desc);
187 desc.setThumbnail(header.thumbnail);
188 } else {
189 // Load savegame descriptions from index file
190 typedef char CommandeType[SAVEGAME_NAME_LEN];
191 CommandeType saveNames[MAX_SAVEGAMES];
192 memset(saveNames, 0, sizeof(saveNames));
193
194 Common::InSaveFile *in;
195 in = g_system->getSavefileManager()->openForLoading(Common::String::format("%s.dir", target));
196
197 if (in) {
198 in->read(saveNames, SAVELIST_SIZE);
199 delete in;
200 }
201
202 saveNames[slot][SAVEGAME_NAME_LEN - 1] = 0;
203 Common::String saveNameStr((const char *)saveNames[slot]);
204 desc.setDescription(saveNameStr);
205 }
206
207 if (desc.getDescription().empty()) {
208 desc.setDescription(_("Unnamed savegame"));
209 }
210
211 return desc;
212 }
213
214 // No saving on empty autosave slot
215 if (slot == getAutosaveSlot()) {
216 return SaveStateDescriptor(this, slot, _("Empty autosave"));
217 }
218
219 return SaveStateDescriptor();
220 }
221
removeSaveState(const char * target,int slot) const222 void CineMetaEngine::removeSaveState(const char *target, int slot) const {
223 if (slot < 0 || slot >= MAX_SAVEGAMES) {
224 return;
225 }
226
227 // Load savegame descriptions from index file
228 typedef char CommandeType[SAVEGAME_NAME_LEN];
229 CommandeType saveNames[MAX_SAVEGAMES];
230
231 // Initialize all savegames' descriptions to empty strings
232 // so that if the savegames' descriptions can only be partially read from file
233 // then the missing ones are correctly set to empty strings.
234 memset(saveNames, 0, sizeof(saveNames));
235
236 Common::InSaveFile *in;
237 in = g_system->getSavefileManager()->openForLoading(Common::String::format("%s.dir", target));
238
239 if (!in)
240 return;
241
242 in->read(saveNames, SAVELIST_SIZE);
243 delete in;
244
245 // Set description for selected slot
246 char slotName[SAVEGAME_NAME_LEN];
247 slotName[0] = 0;
248 Common::strlcpy(saveNames[slot], slotName, SAVEGAME_NAME_LEN);
249
250 // Update savegame descriptions
251 Common::String indexFile = Common::String::format("%s.dir", target);
252 Common::OutSaveFile *out = g_system->getSavefileManager()->openForSaving(indexFile);
253 if (!out) {
254 warning("Unable to open file %s for saving", indexFile.c_str());
255 return;
256 }
257
258 out->write(saveNames, SAVELIST_SIZE);
259 delete out;
260
261 // Delete save file
262 Common::String saveFileName = getSavegameFile(slot, target);
263
264 g_system->getSavefileManager()->removeSavefile(saveFileName);
265 }
266
267 #if PLUGIN_ENABLED_DYNAMIC(CINE)
268 REGISTER_PLUGIN_DYNAMIC(CINE, PLUGIN_TYPE_ENGINE, CineMetaEngine);
269 #else
270 REGISTER_PLUGIN_STATIC(CINE, PLUGIN_TYPE_ENGINE, CineMetaEngine);
271 #endif
272
273 namespace Cine {
274
loadGameState(int slot)275 Common::Error CineEngine::loadGameState(int slot) {
276 bool gameLoaded = makeLoad(getSaveStateName(slot));
277
278 return gameLoaded ? Common::kNoError : Common::kUnknownError;
279 }
280
saveGameState(int slot,const Common::String & desc,bool isAutosave)281 Common::Error CineEngine::saveGameState(int slot, const Common::String &desc, bool isAutosave) {
282 if (slot < 0 || slot >= MAX_SAVEGAMES) {
283 return Common::kCreatingFileFailed;
284 }
285
286 // Load savegame descriptions from index file
287 loadSaveDirectory();
288
289 // Set description for selected slot making sure it ends with a trailing zero
290 strncpy(currentSaveName[slot], desc.c_str(), sizeof(CommandeType));
291 currentSaveName[slot][sizeof(CommandeType) - 1] = 0;
292
293 // Update savegame descriptions
294 Common::String indexFile = _targetName + ".dir";
295
296 Common::OutSaveFile *fHandle = _saveFileMan->openForSaving(indexFile);
297 if (!fHandle) {
298 warning("Unable to open file %s for saving", indexFile.c_str());
299 return Common::kUnknownError;
300 }
301
302 fHandle->write(currentSaveName, SAVELIST_SIZE);
303 delete fHandle;
304
305 // Save game
306 makeSave(getSaveStateName(slot), getTotalPlayTime() / 1000, desc, isAutosave);
307
308 checkDataDisk(-1);
309
310 return Common::kNoError;
311 }
312
getSaveStateName(int slot) const313 Common::String CineEngine::getSaveStateName(int slot) const {
314 return getMetaEngine()->getSavegameFile(slot, _targetName.c_str());
315 }
316
canLoadGameStateCurrently()317 bool CineEngine::canLoadGameStateCurrently() {
318 return (!disableSystemMenu && !inMenu);
319 }
320
canSaveGameStateCurrently()321 bool CineEngine::canSaveGameStateCurrently() {
322 return (allowPlayerInput && !disableSystemMenu && !inMenu);
323 }
324
325 } // End of namespace Cine
326
327