Applied a slightly modified version of my patch #2307224 - "BS1: Save/load overhaul"

svn-id: r35111
This commit is contained in:
Filippos Karapetis 2008-11-18 16:31:55 +00:00
parent bdbae740a6
commit 7650b61d27
5 changed files with 385 additions and 131 deletions

View file

@ -23,6 +23,7 @@
*
*/
#include <time.h> // for extended infos
#include "common/file.h"
#include "common/util.h"
@ -31,6 +32,7 @@
#include "common/system.h"
#include "common/config-manager.h"
#include "graphics/thumbnail.h"
#include "gui/message.h"
#include "sword1/control.h"
@ -509,6 +511,8 @@ void Control::setupMainPanel(void) {
}
void Control::setupSaveRestorePanel(bool saving) {
readSavegameDescriptions();
FrameHeader *savePanel = _resMan->fetchFrame(_resMan->openFetchRes(SR_WINDOW), 0);
uint16 panelX = (640 - _resMan->getUint16(savePanel->width)) / 2;
uint16 panelY = (480 - _resMan->getUint16(savePanel->height)) / 2;
@ -690,22 +694,20 @@ bool Control::keyAccepted(uint16 ascii) {
void Control::handleSaveKey(Common::KeyState kbd) {
if (_selectedSavegame < 255) {
uint8 len = strlen((char*)_saveNames[_selectedSavegame]);
uint8 len = _saveNames[_selectedSavegame].size();
if ((kbd.keycode == Common::KEYCODE_BACKSPACE) && len) // backspace
_saveNames[_selectedSavegame][len - 1] = '\0';
_saveNames[_selectedSavegame].deleteLastChar();
else if (keyAccepted(kbd.ascii) && (len < 31)) {
_saveNames[_selectedSavegame][len] = kbd.ascii;
_saveNames[_selectedSavegame][len + 1] = '\0';
_saveNames[_selectedSavegame].insertChar(kbd.ascii, len);
}
showSavegameNames();
}
}
bool Control::saveToFile(void) {
if ((_selectedSavegame == 255) || !strlen((char*)_saveNames[_selectedSavegame]))
if ((_selectedSavegame == 255) || _saveNames[_selectedSavegame].size() == 0)
return false; // no saveslot selected or no name entered
saveGameToFile(_selectedSavegame);
writeSavegameDescriptions();
return true;
}
@ -717,33 +719,40 @@ bool Control::restoreFromFile(void) {
}
void Control::readSavegameDescriptions(void) {
Common::InSaveFile *inf;
inf = _saveFileMan->openForLoading("SAVEGAME.INF");
_saveScrollPos = _saveFiles = 0;
char saveName[40];
Common::String pattern = ConfMan.get("gameid") + ".???";
Common::StringList filenames = _saveFileMan->listSavefiles(pattern.c_str());
sort(filenames.begin(), filenames.end()); // Sort (hopefully ensuring we are sorted numerically..)
int num = 0;
int slotNum = 0;
for (Common::StringList::const_iterator file = filenames.begin(); file != filenames.end(); ++file) {
// Obtain the last 3 digits of the filename, since they correspond to the save slot
slotNum = atoi(file->c_str() + file->size() - 3);
while (num < slotNum) {
_saveNames.push_back("");
num++;
}
if (slotNum >= 0 && slotNum <= 999) {
num++;
Common::InSaveFile *in = _saveFileMan->openForLoading(file->c_str());
if (in) {
in->readUint32LE(); // header
in->read(saveName, 40);
_saveNames.push_back(saveName);
delete in;
}
}
}
for (int i = _saveNames.size(); i < 1000; i++)
_saveNames.push_back("");
_saveScrollPos = 0;
_selectedSavegame = 255;
for (uint8 cnt = 0; cnt < 64; cnt++) {
memset(_saveNames[cnt], 0, sizeof(_saveNames[cnt]));
}
if (inf) {
uint8 curFileNum = 0;
uint8 ch;
do {
uint8 pos = 0;
do {
ch = inf->readByte();
if (pos < sizeof(_saveNames[curFileNum]) - 1) {
if ((ch == 10) || (ch == 255) || (inf->eos()))
_saveNames[curFileNum][pos++] = '\0';
else if (ch >= 32)
_saveNames[curFileNum][pos++] = ch;
}
} while ((ch != 10) && (ch != 255) && (!inf->eos()));
curFileNum++;
} while ((ch != 255) && (!inf->eos()));
_saveFiles = curFileNum;
_numSaves = _saveFiles;
}
delete inf;
_saveFiles = _numSaves = _saveNames.size();
}
int Control::displayMessage(const char *altButton, const char *message, ...) {
@ -760,43 +769,59 @@ int Control::displayMessage(const char *altButton, const char *message, ...) {
return result;
}
void Control::writeSavegameDescriptions(void) {
Common::OutSaveFile *outf;
outf = _saveFileMan->openForSaving("SAVEGAME.INF");
bool Control::savegamesExist(void) {
Common::String pattern = ConfMan.get("gameid") + ".???";
Common::StringList saveNames = _saveFileMan->listSavefiles(pattern.c_str());
return saveNames.size() > 0;
}
if (!outf) {
// Display an error message, and do nothing
displayMessage(0, "Can't create SAVEGAME.INF. (%s)", _saveFileMan->popErrorDesc().c_str());
void Control::checkForOldSaveGames() {
Common::InSaveFile *inf = _saveFileMan->openForLoading("SAVEGAME.INF");
if (!inf) {
delete inf;
return;
}
// if the player accidently clicked the last slot and then deselected it again,
// we'd still have _saveFiles == 64, so get rid of the empty end.
while (strlen((char*)_saveNames[_saveFiles - 1]) == 0)
_saveFiles--;
for (uint8 cnt = 0; cnt < _saveFiles; cnt++) {
int len = strlen((char*)_saveNames[cnt]);
if (len > 0)
outf->write(_saveNames[cnt], len);
if (cnt < _saveFiles - 1)
outf->writeByte(10);
else
outf->writeByte(255);
}
outf->finalize();
if (outf->ioFailed())
displayMessage(0, "Can't write to SAVEGAME.INF. Device full? (%s)", _saveFileMan->popErrorDesc().c_str());
delete outf;
}
GUI::MessageDialog dialog0(
"ScummVM found that you have old savefiles for Broken Sword 1 that should be converted.\n"
"The old save game format is no longer supported, so you will not be able to load your games if you don't convert them.\n\n"
"Press OK to convert them now, otherwise you will be asked again the next time you start the game.\n", "OK", "Cancel");
bool Control::savegamesExist(void) {
bool retVal = false;
Common::InSaveFile *inf;
inf = _saveFileMan->openForLoading("SAVEGAME.INF");
if (inf)
retVal = true;
delete inf;
return retVal;
int choice = dialog0.runModal();
if (choice == GUI::kMessageCancel) {
// user pressed cancel
return;
}
// Convert every save slot we find in the index file to the new format
uint8 saveName[32];
uint8 slot = 0;
uint8 ch;
memset(saveName, 0, sizeof(saveName));
do {
uint8 pos = 0;
do {
ch = inf->readByte();
if (pos < sizeof(saveName) - 1) {
if ((ch == 10) || (ch == 255) || (inf->eos()))
saveName[pos++] = '\0';
else if (ch >= 32)
saveName[pos++] = ch;
}
} while ((ch != 10) && (ch != 255) && (!inf->eos()));
if (pos > 1) // if the slot has a description
convertSaveGame(slot, (char*)saveName);
slot++;
} while ((ch != 255) && (!inf->eos()));
delete inf;
// Delete index file
_saveFileMan->removeSavefile("SAVEGAME.INF");
}
void Control::showSavegameNames(void) {
@ -805,7 +830,7 @@ void Control::showSavegameNames(void) {
uint8 textMode = TEXT_LEFT_ALIGN;
uint16 ycoord = _saveButtons[cnt].y + 2;
uint8 str[40];
sprintf((char*)str, "%d. %s", cnt + _saveScrollPos + 1, _saveNames[cnt + _saveScrollPos]);
sprintf((char*)str, "%d. %s", cnt + _saveScrollPos + 1, _saveNames[cnt + _saveScrollPos].c_str());
if (cnt + _saveScrollPos == _selectedSavegame) {
textMode |= TEXT_RED_FONT;
ycoord += 2;
@ -821,10 +846,10 @@ void Control::saveNameSelect(uint8 id, bool saving) {
_buttons[id - BUTTON_SAVE_SELECT1]->setSelected(1);
uint8 num = (id - BUTTON_SAVE_SELECT1) + _saveScrollPos;
if (saving && (_selectedSavegame != 255)) // the player may have entered something, clear it again
strcpy((char*)_saveNames[_selectedSavegame], (char*)_oldName);
_saveNames[_selectedSavegame] = _oldName;
if (num < _saveFiles) {
_selectedSavegame = num;
strcpy((char*)_oldName, (char*)_saveNames[num]); // save for later
_oldName = _saveNames[num]; // save for later
} else {
if (!saving)
_buttons[id - BUTTON_SAVE_SELECT1]->setSelected(0); // no save in slot, deselect it
@ -832,7 +857,7 @@ void Control::saveNameSelect(uint8 id, bool saving) {
if (_saveFiles <= num)
_saveFiles = num + 1;
_selectedSavegame = num;
_oldName[0] = '\0';
_oldName.clear();
}
}
if (_selectedSavegame < 255)
@ -950,7 +975,7 @@ void Control::renderVolumeBar(uint8 id, uint8 volL, uint8 volR) {
void Control::saveGameToFile(uint8 slot) {
char fName[15];
uint16 cnt;
sprintf(fName, "SAVEGAME.%03d", slot);
sprintf(fName, "%s.%03d", ConfMan.get("gameid").c_str(), slot);
uint16 liveBuf[TOTAL_SECTIONS];
Common::OutSaveFile *outf;
outf = _saveFileMan->openForSaving(fName);
@ -960,6 +985,31 @@ void Control::saveGameToFile(uint8 slot) {
return;
}
outf->writeUint32LE(SAVEGAME_HEADER);
outf->write(_saveNames[slot].c_str(), 40);
outf->writeByte(SAVEGAME_VERSION);
// FIXME: at this point, we can't save a thumbnail of the game screen, as the save menu is shown
#if 0
outf->writeByte(HAS_THUMBNAIL);
// Thumbnail
Graphics::saveThumbnail(*outf);
#else
outf->writeByte(NO_THUMBNAIL);
#endif
// Date / time
tm curTime;
_system->getTimeAndDate(curTime);
uint32 saveDate = (curTime.tm_mday & 0xFF) << 24 | ((curTime.tm_mon + 1) & 0xFF) << 16 | (curTime.tm_year + 1900) & 0xFFFF;
uint16 saveTime = (curTime.tm_hour & 0xFF) << 8 | (curTime.tm_min) & 0xFF;
outf->writeUint32BE(saveDate);
outf->writeUint16BE(saveTime);
// TODO: played time
_objMan->saveLiveList(liveBuf);
for (cnt = 0; cnt < TOTAL_SECTIONS; cnt++)
outf->writeUint16LE(liveBuf[cnt]);
@ -987,7 +1037,7 @@ void Control::saveGameToFile(uint8 slot) {
bool Control::restoreGameFromFile(uint8 slot) {
char fName[15];
uint16 cnt;
sprintf(fName, "SAVEGAME.%03d", slot);
sprintf(fName, "%s.%03d", ConfMan.get("gameid").c_str(), slot);
Common::InSaveFile *inf;
inf = _saveFileMan->openForLoading(fName);
if (!inf) {
@ -996,6 +1046,36 @@ bool Control::restoreGameFromFile(uint8 slot) {
return false;
}
uint saveHeader = inf->readUint32LE();
if (saveHeader != SAVEGAME_HEADER) {
// Display an error message, and do nothing
displayMessage(0, "Save game '%s' is corrupt", fName);
return false;
}
inf->skip(40); // skip description
uint8 saveVersion = inf->readByte();
if (saveVersion != SAVEGAME_VERSION) {
warning("Different save game version");
return false;
}
bool hasThumbnail = inf->readByte();
if (hasThumbnail) {
// We don't need the thumbnail here, so just read it and discard it
Graphics::Surface *thumbnail = new Graphics::Surface();
assert(thumbnail);
Graphics::loadThumbnail(*inf, *thumbnail);
delete thumbnail;
thumbnail = 0;
}
inf->readUint32BE(); // save date
inf->readUint16BE(); // save time
// TODO: played time
_restoreBuf = (uint8*)malloc(
TOTAL_SECTIONS * 2 +
NUM_SCRIPT_VARS * 4 +
@ -1026,6 +1106,90 @@ bool Control::restoreGameFromFile(uint8 slot) {
return true;
}
bool Control::convertSaveGame(uint8 slot, char* desc) {
char oldFileName[15];
char newFileName[40];
sprintf(oldFileName, "SAVEGAME.%03d", slot);
sprintf(newFileName, "%s.%03d", ConfMan.get("gameid").c_str(), slot);
uint8 *saveData;
int dataSize;
// Check if the new file already exists
Common::InSaveFile *testSave = _saveFileMan->openForLoading(newFileName);
if (testSave) {
delete testSave;
char msg[200];
sprintf(msg, "Target new save game already exists!\n"
"Would you like to keep the old save game (%s) or the new one (%s)?\n",
oldFileName, newFileName);
GUI::MessageDialog dialog0(msg, "Keep the old one", "Keep the new one");
int choice = dialog0.runModal();
if (choice == GUI::kMessageCancel) {
// User chose to keep the new game, so delete the old one
_saveFileMan->removeSavefile(oldFileName);
return true;
}
}
Common::InSaveFile *oldSave = _saveFileMan->openForLoading(oldFileName);
if (!oldSave) {
// Display a warning message and do nothing
warning("Can't open file '%s'", oldFileName);
return false;
}
// Read data from old type of save game
dataSize = oldSave->size();
saveData = new uint8[dataSize];
oldSave->read(saveData, dataSize);
delete oldSave;
// Now write the save data to a new type of save game
Common::OutSaveFile *newSave;
newSave = _saveFileMan->openForSaving(newFileName);
if (!newSave) {
// Display a warning message and do nothing
warning("Unable to create file '%s'. (%s)", newFileName, _saveFileMan->popErrorDesc().c_str());
free(saveData);
saveData = NULL;
return false;
}
newSave->writeUint32LE(SAVEGAME_HEADER);
newSave->write(desc, 40);
newSave->writeByte(SAVEGAME_VERSION);
newSave->writeByte(NO_THUMBNAIL);
// Date / time
tm curTime;
_system->getTimeAndDate(curTime);
uint32 saveDate = (curTime.tm_mday & 0xFF) << 24 | ((curTime.tm_mon + 1) & 0xFF) << 16 | (curTime.tm_year + 1900) & 0xFFFF;
uint16 saveTime = (curTime.tm_hour & 0xFF) << 8 | (curTime.tm_min) & 0xFF;
newSave->writeUint32BE(saveDate);
newSave->writeUint16BE(saveTime);
// TODO: played time
newSave->write(saveData, dataSize);
newSave->finalize();
if (newSave->ioFailed())
warning("Couldn't write to file '%s'. Device full?", newFileName);
delete newSave;
// Delete old save
_saveFileMan->removeSavefile(oldFileName);
// Cleanup
free(saveData);
saveData = NULL;
return true;
}
void Control::doRestore(void) {
uint8 *bufPos = _restoreBuf;
_objMan->loadLiveList((uint16*)bufPos);

View file

@ -43,6 +43,11 @@ class Mouse;
class Music;
class Sound;
#define SAVEGAME_HEADER MKID_BE('BS_1')
#define SAVEGAME_VERSION 1
#define HAS_THUMBNAIL 1
#define NO_THUMBNAIL 0
#define MAX_BUTTONS 16
#define CONTROL_NOTHING_DONE 0
@ -87,13 +92,19 @@ public:
void doRestore(void);
void askForCd(void);
bool savegamesExist(void);
void readSavegameDescriptions(void);
void saveGameToFile(uint8 slot);
bool restoreGameFromFile(uint8 slot);
void checkForOldSaveGames();
void setSaveDescription(int slot, const char *desc) {
_saveNames[slot] = desc;
}
private:
int displayMessage(const char *altButton, const char *message, ...);
void saveGameToFile(uint8 slot);
void readSavegameDescriptions(void);
void writeSavegameDescriptions(void);
bool convertSaveGame(uint8 slot, char* desc);
void showSavegameNames(void);
void deselectSaveslots(void);
uint8 *_restoreBuf;
@ -101,8 +112,8 @@ private:
uint8 _numSaves;
uint8 _saveScrollPos;
uint8 _selectedSavegame;
uint8 _saveNames[64][32];
uint8 _oldName[32];
Common::StringList _saveNames;
Common::String _oldName;
uint8 _cursorTick;
bool _cursorVisible;

View file

@ -24,12 +24,14 @@
*/
#include "sword1/sword1.h"
#include "sword1/control.h"
#include "base/plugins.h"
#include "common/config-manager.h"
#include "common/file.h"
#include "common/fs.h"
#include "common/savefile.h"
#include "graphics/thumbnail.h"
#include "engines/metaengine.h"
@ -81,6 +83,8 @@ public:
virtual GameList detectGames(const Common::FSList &fslist) const;
virtual SaveStateList listSaves(const char *target) const;
virtual int getMaximumSaveSlot() const;
virtual void removeSaveState(const char *target, int slot) const;
SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
virtual Common::Error createInstance(OSystem *syst, Engine **engine) const;
};
@ -88,7 +92,11 @@ public:
bool SwordMetaEngine::hasFeature(MetaEngineFeature f) const {
return
(f == kSupportsListSaves) ||
(f == kSupportsLoadingDuringStartup);
(f == kSupportsLoadingDuringStartup) ||
(f == kSupportsDeleteSave) ||
(f == kSavesSupportMetaInfo) ||
(f == kSavesSupportThumbnail) ||
(f == kSavesSupportCreationDate);
}
bool Sword1::SwordEngine::hasFeature(EngineFeature f) const {
@ -184,76 +192,130 @@ Common::Error SwordMetaEngine::createInstance(OSystem *syst, Engine **engine) co
SaveStateList SwordMetaEngine::listSaves(const char *target) const {
Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
Common::String pattern = target;
pattern += ".???";
SaveStateList saveList;
char saveName[40];
Common::String pattern = "SAVEGAME.???";
Common::StringList filenames = saveFileMan->listSavefiles(pattern.c_str());
sort(filenames.begin(), filenames.end());
Common::StringList::const_iterator file = filenames.begin();
sort(filenames.begin(), filenames.end()); // Sort (hopefully ensuring we are sorted numerically..)
Common::InSaveFile *in = saveFileMan->openForLoading("SAVEGAME.INF");
if (in) {
Common::Array<uint32> offsets;
uint8 stop = 0;
int slotsInFile = 0;
// Find the offset for each savegame name in the file.
while (stop != 255 && !in->eos()) {
offsets.push_back(in->pos());
slotsInFile++;
stop = 0;
while (stop != 10 && stop != 255 && !in->eos())
stop = in->readByte();
}
// Match the savegames to the save slot names.
while (file != filenames.end()) {
char saveDesc[32];
if (file->compareToIgnoreCase("SAVEGAME.INF") == 0) {
file++;
continue;
int slotNum = 0;
for (Common::StringList::const_iterator file = filenames.begin(); file != filenames.end(); ++file) {
// Obtain the last 3 digits of the filename, since they correspond to the save slot
slotNum = atoi(file->c_str() + file->size() - 3);
if (slotNum >= 0 && slotNum <= 999) {
Common::InSaveFile *in = saveFileMan->openForLoading(file->c_str());
if (in) {
in->readUint32LE(); // header
in->read(saveName, 40);
saveList.push_back(SaveStateDescriptor(slotNum, saveName));
delete in;
}
// Obtain the last 3 digits of the filename, since they correspond to the save slot
int slotNum = atoi(file->c_str() + file->size() - 3);
if (slotNum >= 0 && slotNum < slotsInFile) {
in->seek(offsets[slotNum]);
uint pos = 0;
do {
stop = in->readByte();
if (pos < sizeof(saveDesc) - 1) {
if (stop == 10 || stop == 255 || in->eos())
saveDesc[pos++] = '\0';
else if (stop >= 32)
saveDesc[pos++] = stop;
}
} while (stop != 10 && stop != 255 && !in->eos());
}
if (saveDesc[0] == 0)
strcpy(saveDesc, "Unnamed savegame");
// FIXME: The in-game dialog shows the first save slot as 1, not 0,
// but if we change the numbering here, the launcher won?t set
// "save_slot" correctly.
saveList.push_back(SaveStateDescriptor(slotNum, saveDesc));
file++;
}
}
delete in;
return saveList;
}
int SwordMetaEngine::getMaximumSaveSlot() const { return 999; }
void SwordMetaEngine::removeSaveState(const char *target, int slot) const {
char extension[6];
snprintf(extension, sizeof(extension), ".%03d", slot);
Common::String filename = target;
filename += extension;
g_system->getSavefileManager()->removeSavefile(filename.c_str());
}
SaveStateDescriptor SwordMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
static char fileName[40];
sprintf(fileName, "%s.%03d", target, slot);
char name[40];
Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(fileName);
if (in) {
in->skip(4); // header
in->read(name, sizeof(name));
in->skip(1); // version
SaveStateDescriptor desc(slot, name);
desc.setDeletableFlag(true);
desc.setWriteProtectedFlag(false);
bool hasThumbnail = in->readByte();
if (hasThumbnail) {
Graphics::Surface *thumbnail = new Graphics::Surface();
assert(thumbnail);
if (!Graphics::loadThumbnail(*in, *thumbnail)) {
delete thumbnail;
thumbnail = 0;
}
desc.setThumbnail(thumbnail);
}
uint32 saveDate = in->readUint32BE();
uint16 saveTime = in->readUint16BE();
int day = (saveDate >> 24) & 0xFF;
int month = (saveDate >> 16) & 0xFF;
int year = saveDate & 0xFFFF;
desc.setSaveDate(year, month, day);
int hour = (saveTime >> 8) & 0xFF;
int minutes = saveTime & 0xFF;
desc.setSaveTime(hour, minutes);
// TODO: played time
delete in;
return desc;
}
return SaveStateDescriptor();
}
#if PLUGIN_ENABLED_DYNAMIC(SWORD1)
REGISTER_PLUGIN_DYNAMIC(SWORD1, PLUGIN_TYPE_ENGINE, SwordMetaEngine);
#else
REGISTER_PLUGIN_STATIC(SWORD1, PLUGIN_TYPE_ENGINE, SwordMetaEngine);
#endif
namespace Sword1 {
// FIXME: Loading a game through the GMM crashes the game
#if 0
Common::Error SwordEngine::loadGameState(int slot) {
_systemVars.forceRestart = false;
_systemVars.controlPanelMode = CP_NORMAL;
_control->restoreGameFromFile(slot);
reinitialize();
_control->doRestore();
return Common::kNoError; // TODO: return success/failure
}
Common::Error SwordEngine::saveGameState(int slot, const char *desc) {
_control->setSaveDescription(slot, desc);
_control->saveGameToFile(slot);
return Common::kNoError; // TODO: return success/failure
}
bool SwordEngine::canLoadGameStateCurrently() {
return mouseIsActive();
}
bool SwordEngine::canSaveGameStateCurrently() {
return mouseIsActive();
}
#endif
} // End of namespace Sword1

View file

@ -484,7 +484,10 @@ void SwordEngine::checkCdFiles(void) { // check if we're running from cd, hdd or
}
Common::Error SwordEngine::go() {
_control->checkForOldSaveGames();
uint16 startPos = ConfMan.getInt("boot_param");
_control->readSavegameDescriptions();
if (startPos) {
_logic->startPositions(startPos);
} else {
@ -660,4 +663,8 @@ void SwordEngine::delay(int32 amount) { //copied and mutilated from sky.cpp
} while (_system->getMillis() < start + amount);
}
bool SwordEngine::mouseIsActive() {
return Logic::_scriptVars[MOUSE_STATUS] & 1;
}
} // End of namespace Sword1

View file

@ -79,12 +79,22 @@ public:
void reinitialize(void);
uint32 _features;
bool mouseIsActive();
protected:
// Engine APIs
virtual Common::Error init();
virtual Common::Error go();
virtual bool hasFeature(EngineFeature f) const;
virtual void syncSoundSettings();
// FIXME: Loading a game through the GMM crashes the game
#if 0
Common::Error loadGameState(int slot);
Common::Error saveGameState(int slot, const char *desc);
bool canLoadGameStateCurrently();
bool canSaveGameStateCurrently();
#endif
private:
void delay(int32 amount);