New savefile backend system (bye bye NONSTANDARD_SAVE...)

svn-id: r6007
This commit is contained in:
Marcus Comstedt 2002-12-17 01:15:13 +00:00
parent fadf55aad0
commit 83da387eef
12 changed files with 314 additions and 132 deletions

View file

@ -6,7 +6,7 @@ VPATH = ../..
CXX = sh-elf-g++ -ml -m4-single-only CXX = sh-elf-g++ -ml -m4-single-only
CXXFLAGS= -O1 -Wno-multichar CXXFLAGS= -O1 -Wno-multichar
DEFINES = -D__DC__ -DNONSTANDARD_PORT -DNONSTANDARD_SAVE DEFINES = -D__DC__ -DNONSTANDARD_PORT
LDFLAGS := -Wl,-Ttext,0x8c010000 -nostartfiles ronin/crt0.o LDFLAGS := -Wl,-Ttext,0x8c010000 -nostartfiles ronin/crt0.o
INCLUDES:= -I./ -I../.. -I../../common INCLUDES:= -I./ -I../.. -I../../common
CPPFLAGS= $(DEFINES) $(INCLUDES) CPPFLAGS= $(DEFINES) $(INCLUDES)

View file

@ -84,6 +84,9 @@ class OSystem_Dreamcast : public OSystem {
virtual void unlock_mutex(void *mutex); virtual void unlock_mutex(void *mutex);
virtual void delete_mutex(void *mutex); virtual void delete_mutex(void *mutex);
// Savefile handling
virtual SaveFileManager *get_savefile_manager();
static OSystem *create(); static OSystem *create();

View file

@ -155,6 +155,33 @@ static bool tryLoad(char *&buffer, int &size, const char *filename, int vm)
return false; return false;
} }
static void tryList(const char *prefix, bool *marks, int num, int vm)
{
struct vmsinfo info;
struct superblock super;
struct dir_iterator iter;
struct dir_entry de;
int pl = strlen(prefix);
if(!vmsfs_check_unit(vm, 0, &info))
return;
if(!vmsfs_get_superblock(&info, &super))
return;
vmsfs_open_dir(&super, &iter);
while(vmsfs_next_dir_entry(&iter, &de))
if(de.entry[0]) {
char buf[16], *endp = NULL;
strncpy(buf, (char *)de.entry+4, 12);
buf[12] = 0;
int l = strlen(buf);
long i = 42;
if(l > pl && !strncmp(buf, prefix, pl) &&
(i = strtol(buf+pl, &endp, 10))>=0 && i<num &&
(endp - buf) == l)
marks[i] = true;
}
}
vmsaveResult writeSaveGame(const char *gamename, const char *data, int size, vmsaveResult writeSaveGame(const char *gamename, const char *data, int size,
const char *filename, class Icon &icon) const char *filename, class Icon &icon)
{ {
@ -190,104 +217,130 @@ bool readSaveGame(char *&buffer, int &size, const char *filename)
} }
struct vmStreamContext { class VMSave : public SaveFile {
private:
bool issave; bool issave;
char *buffer; char *buffer;
int pos, size; int pos, size;
char filename[16]; char filename[16];
};
bool SerializerStream::fopen(const char *filename, const char *mode) public:
{ VMSave(const char *_filename, bool _saveOrLoad)
vmStreamContext *c = new vmStreamContext; : issave(_saveOrLoad), pos(0), buffer(NULL)
context = c; {
if(strchr(mode, 'w')) { strncpy(filename, _filename, 16);
c->issave = true; if(issave)
strncpy(c->filename, filename, 16); buffer = new char[size = MAX_SAVE_SIZE];
c->pos = 0; }
c->buffer = new char[c->size = MAX_SAVE_SIZE];
return true; ~VMSave();
} else if(readSaveGame(c->buffer, c->size, filename)) {
if(c->size > 0 && c->buffer[0] != 'S') { virtual int fread(void *buf, int size, int cnt);
virtual int fwrite(void *buf, int size, int cnt);
bool readSaveGame()
{ return ::readSaveGame(buffer, size, filename); }
void tryUncompress()
{
if(size > 0 && buffer[0] != 'S') {
// Data does not start with "SCVM". Maybe compressed? // Data does not start with "SCVM". Maybe compressed?
char *expbuf = new char[MAX_SAVE_SIZE]; char *expbuf = new char[MAX_SAVE_SIZE];
unsigned long destlen = MAX_SAVE_SIZE; unsigned long destlen = MAX_SAVE_SIZE;
if(!uncompress((Bytef*)expbuf, &destlen, (Bytef*)c->buffer, c->size)) { if(!uncompress((Bytef*)expbuf, &destlen, (Bytef*)buffer, size)) {
delete(c->buffer); delete(buffer);
c->buffer = expbuf; buffer = expbuf;
c->size = destlen; size = destlen;
} else delete expbuf; } else delete expbuf;
} }
c->issave = false; }
c->pos = 0; };
return true;
class VMSaveManager : public SaveFileManager {
virtual SaveFile *open_savefile(const char *filename, bool saveOrLoad);
virtual void list_savefiles(const char *prefix, bool *marks, int num);
};
SaveFile *VMSaveManager::open_savefile(const char *filename,
bool saveOrLoad)
{
VMSave *s = new VMSave(filename, saveOrLoad);
if(saveOrLoad)
return s;
else if(s->readSaveGame()) {
s->tryUncompress();
return s;
} else { } else {
delete c; delete s;
context = NULL; return NULL;
return false;
} }
} }
void SerializerStream::fclose() VMSave::~VMSave()
{ {
extern const char *gGameName; extern const char *gGameName;
extern Icon icon; extern Icon icon;
if(context) { if(issave) {
vmStreamContext *c = (vmStreamContext *)context; if(pos) {
if(c->issave) { // Try compression
if(c->pos) { char *compbuf = new char[pos];
// Try compression unsigned long destlen = pos;
char *compbuf = new char[c->pos]; if(!compress((Bytef*)compbuf, &destlen, (Bytef*)buffer, pos)) {
unsigned long destlen = c->pos; delete buffer;
if(!compress((Bytef*)compbuf, &destlen, (Bytef*)c->buffer, c->pos)) { buffer = compbuf;
delete c->buffer; pos = destlen;
c->buffer = compbuf; } else delete compbuf;
c->pos = destlen;
} else delete compbuf;
}
displaySaveResult(writeSaveGame(gGameName, c->buffer,
c->pos, c->filename, icon));
} }
delete c->buffer; displaySaveResult(writeSaveGame(gGameName, buffer,
delete c; pos, filename, icon));
context = NULL;
} }
delete buffer;
} }
int SerializerStream::fread(void *buf, int size, int cnt) int VMSave::fread(void *buf, int sz, int cnt)
{ {
vmStreamContext *c = (vmStreamContext *)context; if (issave)
if (!c || c->issave)
return -1; return -1;
int nbyt = size*cnt; int nbyt = sz*cnt;
if (c->pos + nbyt > c->size) { if (pos + nbyt > size) {
cnt = (c->size - c->pos)/size; cnt = (size - pos)/sz;
nbyt = size*cnt; nbyt = sz*cnt;
} }
if (nbyt) if (nbyt)
memcpy(buf, c->buffer + c->pos, nbyt); memcpy(buf, buffer + pos, nbyt);
c->pos += nbyt; pos += nbyt;
return cnt; return cnt;
} }
int SerializerStream::fwrite(void *buf, int size, int cnt) int VMSave::fwrite(void *buf, int sz, int cnt)
{ {
vmStreamContext *c = (vmStreamContext *)context; if (!issave)
if (!c || !c->issave)
return -1; return -1;
int nbyt = size*cnt; int nbyt = sz*cnt;
if (c->pos + nbyt > c->size) { if (pos + nbyt > size) {
cnt = (c->size - c->pos)/size; cnt = (size - pos)/sz;
nbyt = size*cnt; nbyt = sz*cnt;
} }
if (nbyt) if (nbyt)
memcpy(c->buffer + c->pos, buf, nbyt); memcpy(buffer + pos, buf, nbyt);
c->pos += nbyt; pos += nbyt;
return cnt; return cnt;
} }
void VMSaveManager::list_savefiles(const char *prefix,
bool *marks, int num)
{
memset(marks, false, num*sizeof(bool));
for(int i=0; i<24; i++)
tryList(prefix, marks, num, i);
}
SaveFileManager *OSystem_Dreamcast::get_savefile_manager()
{
return new VMSaveManager();
}

View file

@ -1221,26 +1221,47 @@ extern "C" int write(int fd, void *p, size_t n);
int write(int fd, void *p, size_t n) { return 0; } //ph0x hack! int write(int fd, void *p, size_t n) { return 0; } //ph0x hack!
// fixme - unnecessary? // fixme - unnecessary?
int SerializerStream::fwrite(void *buf, int size, int cnt) { class GP32SaveFile : public SaveFile {
// implement me private:
return ::fwrite(buf, size, cnt, (FILE*)context); FILE *fh;
public:
GP32SaveFile(FILE *f) : fh(f) { }
~GP32SaveFile();
int fread(void *buf, int size, int cnt);
int fwrite(void *buf, int size, int cnt);
} }
bool SerializerStream::fopen(const char *filename, const char *mode) { class GP32SaveFileManager : public SaveFileManager {
// implement me SaveFile *open_savefile(const char *filename, bool saveOrLoad);
(FILE*)context = ::fopen(filename, mode);
//if (tolower(mode[0])=='w') error("Autosaving..");
return context != NULL;
} }
void SerializerStream::fclose() { int GP32SaveFile::fwrite(void *buf, int size, int cnt) {
// implement me // implement me
::fclose((FILE*)context); return ::fwrite(buf, size, cnt, fh);
} }
int SerializerStream::fread(void *buf, int size, int cnt) {
// implement me
return ::fread(buf, size, cnt, (FILE*)context);
SaveFile GP32SaveFileManager::open_savefile(const char *filename,
bool saveOrLoad) {
// implement me
FILE *fh = ::fopen(filename, (saveOrLoad? "wb":"rb"));
//if (saveOrLoad) error("Autosaving..");
return fh? new GP32SaveFile(fh) : NULL;
}
void GP32SaveFile::~GP32SaveFile() {
// implement me
::fclose(fh);
}
int GP32SaveFile::fread(void *buf, int size, int cnt) {
// implement me
return ::fread(buf, size, cnt, fh);
}
SaveFileManager *OSystem_GP32::get_savefile_manager() {
return new GP32SaveFileManager();
} }
// Converts 8bit rgb values to a GP32 palette value // Converts 8bit rgb values to a GP32 palette value

View file

@ -115,6 +115,10 @@ public:
void grab_overlay(int16 *buf, int pitch); void grab_overlay(int16 *buf, int pitch);
void copy_rect_overlay(const int16 *buf, int pitch, int x, int y, int w, int h); void copy_rect_overlay(const int16 *buf, int pitch, int x, int y, int w, int h);
// Savefiles
SaveFileManager *get_savefile_manager();
static OSystem *create(int gfx_mode, bool full_screen); static OSystem *create(int gfx_mode, bool full_screen);
private: private:
typedef void ScalerProc(uint8 *srcPtr, uint32 srcPitch, uint8 *deltaPtr, typedef void ScalerProc(uint8 *srcPtr, uint32 srcPitch, uint8 *deltaPtr,

90
common/savefile.h Normal file
View file

@ -0,0 +1,90 @@
/* ScummVM - Scumm Interpreter
* Copyright (C) 2001 Ludvig Strigeus
* Copyright (C) 2001/2002 The ScummVM project
*
* 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.
* 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.
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* $Header$
*
*/
#ifndef COMMON_SAVEFILE_H
#define COMMON_SAVEFILE_H
#include <stdio.h>
class SaveFile {
public:
virtual ~SaveFile() {}
virtual int fread(void *buf, int size, int cnt) = 0;
virtual int fwrite(void *buf, int size, int cnt) = 0;
#ifdef _WIN32_WCE
//Should go away. See scumm/saveload.cpp and scumm/imuse.cpp
virtual int fseek(long offs, int whence) = 0;
virtual int feof() = 0;
#endif
};
class StdioSaveFile : public SaveFile {
private:
FILE *fh;
public:
StdioSaveFile(const char *filename, const char *mode)
{ fh = ::fopen(filename, mode); }
~StdioSaveFile()
{ if(fh) ::fclose(fh); }
bool is_open() { return fh != NULL; }
int fread(void *buf, int size, int cnt)
{ return ::fread(buf, size, cnt, fh); }
int fwrite(void *buf, int size, int cnt)
{ return ::fwrite(buf, size, cnt, fh); }
#ifdef _WIN32_WCE
int fseek(long offs, int whence)
{ return ::fseek(fh, offs, whence); }
int feof()
{ return ::feof(fh); }
#endif
};
class SaveFileManager {
public:
virtual ~SaveFileManager() {}
virtual SaveFile *open_savefile(const char *filename,
bool saveOrLoad)
{
StdioSaveFile *sf = new StdioSaveFile(filename,
(saveOrLoad? "wb":"rb"));
if(!sf->is_open()) {
delete sf;
sf = NULL;
}
return sf;
}
virtual void list_savefiles(const char *prefix,
bool *marks, int num)
{
memset(marks, true, num*sizeof(bool));
}
};
#endif

View file

@ -240,7 +240,6 @@
#define CDECL #define CDECL
#define SCUMM_NEED_ALIGNMENT #define SCUMM_NEED_ALIGNMENT
#define SCUMM_LITTLE_ENDIAN #define SCUMM_LITTLE_ENDIAN
#define NONSTANDARD_SAVE
#define scumm_stricmp stricmp #define scumm_stricmp stricmp
#define CHECK_HEAP #define CHECK_HEAP

View file

@ -24,6 +24,7 @@
#define COMMON_SYSTEM_H #define COMMON_SYSTEM_H
#include "scummsys.h" #include "scummsys.h"
#include "savefile.h"
class Timer; class Timer;
@ -180,6 +181,12 @@ public:
g = (((color>>5)&0x3F) << 2); g = (((color>>5)&0x3F) << 2);
b = ((color&0x1F) << 3); b = ((color&0x1F) << 3);
} }
// Savefile management
virtual SaveFileManager *get_savefile_manager()
{
return new SaveFileManager();
}
}; };

View file

@ -406,12 +406,20 @@ void SaveLoadDialog::fillList()
ScummVM::StringList l; ScummVM::StringList l;
char name[32]; char name[32];
int i = _saveMode ? 1 : 0; int i = _saveMode ? 1 : 0;
bool avail_saves[81];
SaveFileManager *mgr = _scumm->_system->get_savefile_manager();
_scumm->listSavegames(avail_saves, 81, mgr);
for (; i <= 80; i++) { // 80 - got this value from the old GUI for (; i <= 80; i++) { // 80 - got this value from the old GUI
_scumm->getSavegameName(i, name); if(avail_saves[i])
_scumm->getSavegameName(i, name, mgr);
else
name[0] = 0;
l.push_back(name); l.push_back(name);
} }
delete mgr;
_savegameList->setList(l); _savegameList->setList(l);
_savegameList->setNumberingMode(_saveMode ? kListNumberingOne : kListNumberingZero); _savegameList->setNumberingMode(_saveMode ? kListNumberingOne : kListNumberingZero);
} }

View file

@ -39,15 +39,15 @@ struct SaveGameHeader {
}; };
bool Scumm::saveState(int slot, bool compat) bool Scumm::saveState(int slot, bool compat, SaveFileManager *mgr)
{ {
char filename[256]; char filename[256];
SerializerStream out; SaveFile *out;
SaveGameHeader hdr; SaveGameHeader hdr;
makeSavegameName(filename, slot, compat); makeSavegameName(filename, slot, compat);
if (!out.fopen(filename, "wb")) if (!(out = mgr->open_savefile(filename, true)))
return false; return false;
memcpy(hdr.name, _saveLoadName, sizeof(hdr.name)); memcpy(hdr.name, _saveLoadName, sizeof(hdr.name));
@ -56,32 +56,32 @@ bool Scumm::saveState(int slot, bool compat)
hdr.size = 0; hdr.size = 0;
hdr.ver = TO_LE_32(CURRENT_VER); hdr.ver = TO_LE_32(CURRENT_VER);
out.fwrite(&hdr, sizeof(hdr), 1); out->fwrite(&hdr, sizeof(hdr), 1);
Serializer ser(out, true, CURRENT_VER); Serializer ser(out, true, CURRENT_VER);
saveOrLoad(&ser, CURRENT_VER); saveOrLoad(&ser, CURRENT_VER);
out.fclose(); delete out;
debug(1, "State saved as '%s'", filename); debug(1, "State saved as '%s'", filename);
return true; return true;
} }
bool Scumm::loadState(int slot, bool compat) bool Scumm::loadState(int slot, bool compat, SaveFileManager *mgr)
{ {
char filename[256]; char filename[256];
SerializerStream out; SaveFile *out;
int i, j; int i, j;
SaveGameHeader hdr; SaveGameHeader hdr;
int sb, sh; int sb, sh;
makeSavegameName(filename, slot, compat); makeSavegameName(filename, slot, compat);
if (!out.fopen(filename, "rb")) if (!(out = mgr->open_savefile(filename, false)))
return false; return false;
out.fread(&hdr, sizeof(hdr), 1); out->fread(&hdr, sizeof(hdr), 1);
if (hdr.type != MKID('SCVM')) { if (hdr.type != MKID('SCVM')) {
warning("Invalid savegame '%s'", filename); warning("Invalid savegame '%s'", filename);
out.fclose(); delete out;
return false; return false;
} }
@ -92,7 +92,7 @@ bool Scumm::loadState(int slot, bool compat)
if (hdr.ver < VER_V7 || hdr.ver > CURRENT_VER) if (hdr.ver < VER_V7 || hdr.ver > CURRENT_VER)
{ {
warning("Invalid version of '%s'", filename); warning("Invalid version of '%s'", filename);
out.fclose(); delete out;
return false; return false;
} }
@ -126,7 +126,7 @@ bool Scumm::loadState(int slot, bool compat)
Serializer ser(out, false, hdr.ver); Serializer ser(out, false, hdr.ver);
saveOrLoad(&ser, hdr.ver); saveOrLoad(&ser, hdr.ver);
out.fclose(); delete out;
sb = _screenB; sb = _screenB;
sh = _screenH; sh = _screenH;
@ -174,20 +174,28 @@ void Scumm::makeSavegameName(char *out, int slot, bool compatible)
sprintf(out, "%s%s.%c%.2d", dir, _game_name, compatible ? 'c' : 's', slot); sprintf(out, "%s%s.%c%.2d", dir, _game_name, compatible ? 'c' : 's', slot);
} }
bool Scumm::getSavegameName(int slot, char *desc) void Scumm::listSavegames(bool *marks, int num, SaveFileManager *mgr)
{
char prefix[256];
makeSavegameName(prefix, 99, false);
prefix[strlen(prefix)-2] = 0;
mgr->list_savefiles(prefix, marks, num);
}
bool Scumm::getSavegameName(int slot, char *desc, SaveFileManager *mgr)
{ {
char filename[256]; char filename[256];
SerializerStream out; SaveFile *out;
SaveGameHeader hdr; SaveGameHeader hdr;
int len; int len;
makeSavegameName(filename, slot, false); makeSavegameName(filename, slot, false);
if (!out.fopen(filename, "rb")) { if (!(out = mgr->open_savefile(filename, false))) {
strcpy(desc, ""); strcpy(desc, "");
return false; return false;
} }
len = out.fread(&hdr, sizeof(hdr), 1); len = out->fread(&hdr, sizeof(hdr), 1);
out.fclose(); delete out;
if (len != 1 || hdr.type != MKID('SCVM')) { if (len != 1 || hdr.type != MKID('SCVM')) {
strcpy(desc, "Invalid savegame"); strcpy(desc, "Invalid savegame");
@ -652,12 +660,12 @@ void Scumm::saveLoadResource(Serializer *ser, int type, int idx)
void Serializer::saveBytes(void *b, int len) void Serializer::saveBytes(void *b, int len)
{ {
_saveLoadStream.fwrite(b, 1, len); _saveLoadStream->fwrite(b, 1, len);
} }
void Serializer::loadBytes(void *b, int len) void Serializer::loadBytes(void *b, int len)
{ {
_saveLoadStream.fread(b, 1, len); _saveLoadStream->fread(b, 1, len);
} }
#ifdef _WIN32_WCE #ifdef _WIN32_WCE
@ -666,11 +674,11 @@ void Serializer::loadBytes(void *b, int len)
bool Serializer::checkEOFLoadStream() bool Serializer::checkEOFLoadStream()
{ {
if (!fseek(_saveLoadStream.out, 1, SEEK_CUR)) if (!_saveLoadStream->fseek(1, SEEK_CUR))
return true; return true;
if (feof(_saveLoadStream.out)) if (_saveLoadStream->feof())
return true; return true;
fseek(_saveLoadStream.out, -1, SEEK_CUR); _saveLoadStream->fseek(-1, SEEK_CUR);
return false; return false;
} }

View file

@ -85,38 +85,12 @@ struct SaveLoadEntry {
uint8 maxVersion; uint8 maxVersion;
}; };
struct SerializerStream {
#ifdef NONSTANDARD_SAVE
void *context;
bool fopen(const char *filename, const char *mode);
void fclose();
int fread(void *buf, int size, int cnt);
int fwrite(void *buf, int size, int cnt);
#else
FILE *out;
FILE *fopen(const char *filename, const char *mode) {
return out = ::fopen(filename, mode);
}
void fclose() {
::fclose(out);
}
int fread(void *buf, int size, int cnt) {
return ::fread(buf, size, cnt, out);
}
int fwrite(void *buf, int size, int cnt) {
return ::fwrite(buf, size, cnt, out);
}
#endif
};
typedef int SerializerSaveReference(void *me, byte type, void *ref); typedef int SerializerSaveReference(void *me, byte type, void *ref);
typedef void *SerializerLoadReference(void *me, byte type, int ref); typedef void *SerializerLoadReference(void *me, byte type, int ref);
class Serializer { class Serializer {
public: public:
Serializer(SerializerStream stream, bool saveOrLoad, uint32 savegameVersion) Serializer(SaveFile *stream, bool saveOrLoad, uint32 savegameVersion)
: _save_ref(0), _load_ref(0), _ref_me(0), : _save_ref(0), _load_ref(0), _ref_me(0),
_saveLoadStream(stream), _saveOrLoad(saveOrLoad), _saveLoadStream(stream), _saveOrLoad(saveOrLoad),
_savegameVersion(savegameVersion) _savegameVersion(savegameVersion)
@ -146,7 +120,7 @@ public:
void loadBytes(void *b, int len); void loadBytes(void *b, int len);
protected: protected:
SerializerStream _saveLoadStream; SaveFile *_saveLoadStream;
bool _saveOrLoad; bool _saveOrLoad;
uint32 _savegameVersion; uint32 _savegameVersion;

View file

@ -439,13 +439,28 @@ public:
bool _saveLoadCompatible; bool _saveLoadCompatible;
char _saveLoadName[32]; char _saveLoadName[32];
bool saveState(int slot, bool compat); bool saveState(int slot, bool compat, SaveFileManager *mgr);
bool loadState(int slot, bool compat); bool loadState(int slot, bool compat, SaveFileManager *mgr);
bool saveState(int slot, bool compat)
{
SaveFileManager *mgr = _system->get_savefile_manager();
bool result = saveState(slot, compat, mgr);
delete mgr;
return result;
}
bool loadState(int slot, bool compat)
{
SaveFileManager *mgr = _system->get_savefile_manager();
bool result = loadState(slot, compat, mgr);
delete mgr;
return result;
}
void saveOrLoad(Serializer *s, uint32 savegameVersion); void saveOrLoad(Serializer *s, uint32 savegameVersion);
bool getSavegameName(int slot, char *desc); bool getSavegameName(int slot, char *desc, SaveFileManager *mgr);
void makeSavegameName(char *out, int slot, bool compatible); void makeSavegameName(char *out, int slot, bool compatible);
void saveLoadResource(Serializer *ser, int type, int index); void saveLoadResource(Serializer *ser, int type, int index);
void listSavegames(bool *marks, int num, SaveFileManager *mgr);
/* Heap and memory management */ /* Heap and memory management */
uint32 _maxHeapThreshold, _minHeapThreshold; uint32 _maxHeapThreshold, _minHeapThreshold;