Files
dosbox-x/src/misc/savestates.cpp
2021-11-25 05:42:03 -05:00

1724 lines
53 KiB
C++

#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <string>
#include <cstring>
#include <fstream>
#include "SDL.h"
#include "menu.h"
#include "shell.h"
#include "cross.h"
#include "render.h"
#include "mapper.h"
#include "control.h"
#include "logging.h"
#include "build_timestamp.h"
#ifdef WIN32
#include "direct.h"
#endif
#if defined (__APPLE__)
#else
#include <malloc.h>
#endif
#if defined(unix) || defined(__APPLE__)
# include <utime.h>
#endif
#define MAXU32 0xffffffff
#include "zip.h"
#include "unzip.h"
#include "ioapi.h"
#include "vs/zlib/contrib/minizip/zip.c"
#include "vs/zlib/contrib/minizip/unzip.c"
#include "vs/zlib/contrib/minizip/ioapi.c"
#if !defined(HX_DOS)
#include "../libs/tinyfiledialogs/tinyfiledialogs.h"
#endif
extern unsigned int page;
extern int autosave_last[10], autosave_count;
extern std::string autosave_name[10], savefilename;
extern bool use_save_file, clearline, dos_kernel_disabled;
extern const char* RunningProgram;
bool auto_save_state=false;
bool noremark_save_state = false;
bool force_load_state = false;
std::string saveloaderr="";
void refresh_slots(void);
void GFX_LosingFocus(void), GFX_ReleaseMouse(void), MAPPER_ReleaseAllKeys(void), resetFontSize(void);
bool systemmessagebox(char const * aTitle, char const * aMessage, char const * aDialogType, char const * aIconType, int aDefaultButton);
namespace
{
std::string getTime(bool date=false)
{
const time_t current = time(NULL);
tm* timeinfo;
timeinfo = localtime(&current); //convert to local time
char buffer[80];
if (date)
::strftime(buffer, 80, "%Y-%m-%d %H:%M", timeinfo);
else
::strftime(buffer, 50, "%H:%M:%S", timeinfo);
return buffer;
}
std::string getType() {
switch (machine) {
case MCH_HERC:
return "MCH_HERC";
case MCH_CGA:
return "MCH_CGA";
case MCH_TANDY:
return "MCH_TANDY";
case MCH_PCJR:
return "MCH_PCJR";
case MCH_EGA:
return "MCH_EGA";
case MCH_VGA:
return "MCH_VGA";
case MCH_AMSTRAD:
return "MCH_AMSTRAD";
case MCH_PC98:
return "MCH_PC98";
case MCH_FM_TOWNS:
return "MCH_FM_TOWNS";
case MCH_MCGA:
return "MCH_MCGA";
case MCH_MDA:
return "MCH_MDA";
default:
return "MCH_OTHER";
}
}
size_t GetGameState();
class SlotPos
{
public:
SlotPos() : slot(0) {}
void next()
{
++slot;
slot %= SaveState::SLOT_COUNT*SaveState::MAX_PAGE;
if (page!=GetGameState()/SaveState::SLOT_COUNT) {
page=(unsigned int)GetGameState()/SaveState::SLOT_COUNT;
refresh_slots();
}
}
void previous()
{
slot += SaveState::SLOT_COUNT*SaveState::MAX_PAGE - 1;
slot %= SaveState::SLOT_COUNT*SaveState::MAX_PAGE;
if (page!=GetGameState()/SaveState::SLOT_COUNT) {
page=(unsigned int)GetGameState()/SaveState::SLOT_COUNT;
refresh_slots();
}
}
void set(int value)
{
slot = value;
}
operator size_t() const
{
return slot;
}
private:
size_t slot;
};
SlotPos currentSlot;
void notifyError(const std::string& message, bool log=true)
{
if (log) LOG_MSG("%s",message.c_str());
systemmessagebox("Error",message.c_str(),"ok","error", 1);
}
size_t GetGameState(void) {
return currentSlot;
}
void SetGameState(int value) {
char name[6]="slot0";
name[4]='0'+(char)(currentSlot%SaveState::SLOT_COUNT);
mainMenu.get_item(name).check(false).refresh_item(mainMenu);
currentSlot.set(value);
if (page!=currentSlot/SaveState::SLOT_COUNT) {
page=(unsigned int)(currentSlot/SaveState::SLOT_COUNT);
refresh_slots();
}
name[4]='0'+(char)(currentSlot%SaveState::SLOT_COUNT);
mainMenu.get_item(name).check(true).refresh_item(mainMenu);
const bool emptySlot = SaveState::instance().isEmpty(currentSlot);
LOG_MSG("Active save slot: %d %s", (int)currentSlot + 1, emptySlot ? "[Empty]" : "");
}
void SaveGameState(bool pressed) {
if (!pressed) return;
try
{
LOG_MSG("Saving state to slot: %d", (int)currentSlot + 1);
SaveState::instance().save(currentSlot);
if (page!=GetGameState()/SaveState::SLOT_COUNT)
SetGameState((int)currentSlot);
else
refresh_slots();
}
catch (const SaveState::Error& err)
{
notifyError(err);
}
}
void LoadGameState(bool pressed) {
if (!pressed) return;
// if (SaveState::instance().isEmpty(currentSlot))
// {
// LOG_MSG("[%s]: State %d is empty!", getTime().c_str(), currentSlot + 1);
// return;
// }
if (!GFX_IsFullscreen()&&render.aspect) GFX_LosingFocus();
try
{
LOG_MSG("Loading state from slot: %d", (int)currentSlot + 1);
SaveState::instance().load(currentSlot);
#if defined(USE_TTF)
if (ttf.inUse) resetFontSize();
#endif
}
catch (const SaveState::Error& err)
{
notifyError(err);
}
}
void NextSaveSlot(bool pressed) {
if (!pressed) return;
char name[6]="slot0";
name[4]='0'+(char)(currentSlot%SaveState::SLOT_COUNT);
mainMenu.get_item(name).check(false).refresh_item(mainMenu);
currentSlot.next();
if (currentSlot/SaveState::SLOT_COUNT==page) {
name[4]='0'+(char)(currentSlot%SaveState::SLOT_COUNT);
mainMenu.get_item(name).check(true).refresh_item(mainMenu);
}
const bool emptySlot = SaveState::instance().isEmpty(currentSlot);
LOG_MSG("Active save slot: %d %s", (int)currentSlot + 1, emptySlot ? "[Empty]" : "");
}
void PreviousSaveSlot(bool pressed) {
if (!pressed) return;
char name[6]="slot0";
name[4]='0'+(char)(currentSlot%SaveState::SLOT_COUNT);
mainMenu.get_item(name).check(false).refresh_item(mainMenu);
currentSlot.previous();
if (currentSlot/SaveState::SLOT_COUNT==page) {
name[4]='0'+(char)(currentSlot%SaveState::SLOT_COUNT);
mainMenu.get_item(name).check(true).refresh_item(mainMenu);
}
const bool emptySlot = SaveState::instance().isEmpty(currentSlot);
LOG_MSG("Active save slot: %d %s", (int)currentSlot + 1, emptySlot ? "[Empty]" : "");
}
void LastAutoSaveSlot(bool pressed) {
if (!pressed) return;
int index=0;
for (int i=1; i<10&&i<=autosave_count; i++) if (autosave_name[i].size()&&!strcasecmp(RunningProgram, autosave_name[i].c_str())) index=i;
if (autosave_last[index]<1) return;
char name[6]="slot0";
name[4]='0'+(char)(currentSlot%SaveState::SLOT_COUNT);
mainMenu.get_item(name).check(false).refresh_item(mainMenu);
currentSlot.set(autosave_last[index]-1);
if (page!=currentSlot/SaveState::SLOT_COUNT) {
page=(unsigned int)(currentSlot/SaveState::SLOT_COUNT);
refresh_slots();
}
name[4]='0'+(char)(currentSlot%SaveState::SLOT_COUNT);
mainMenu.get_item(name).check(true).refresh_item(mainMenu);
const bool emptySlot = SaveState::instance().isEmpty(currentSlot);
LOG_MSG("Active save slot: %d %s", (int)currentSlot + 1, emptySlot ? "[Empty]" : "");
}
}
std::string GetPlatform(bool save) {
char platform[30];
strcpy(platform,
#if defined(HX_DOS)
"DOS "
#elif defined(__MINGW32__)
"MinGW "
#elif defined(WIN32)
"Windows "
#elif defined(LINUX)
"Linux "
#elif unix
"Unix "
#elif defined(MACOSX)
"macOS "
#elif defined(OS2)
"OS/2 "
#else
save?"Other ":""
#endif
);
if (!save) strcat(platform, (std::string(SDL_STRING)+", ").c_str());
#if defined(_M_X64) || defined (_M_AMD64) || defined (_M_ARM64) || defined (_M_IA64) || defined(__ia64__) || defined(__LP64__) || defined(_WIN64) || defined(__x86_64__) || defined(__aarch64__) || defined(__powerpc64__)
strcat(platform, "64");
#else
strcat(platform, "32");
#endif
strcat(platform, save?"-bit build":"-bit");
return std::string(platform);
}
size_t GetGameState_Run(void) { return GetGameState(); }
void SetGameState_Run(int value) { SetGameState(value); }
void SaveGameState_Run(void) { SaveGameState(true); }
void LoadGameState_Run(void) { LoadGameState(true); }
void NextSaveSlot_Run(void) { NextSaveSlot(true); }
void PreviousSaveSlot_Run(void) { PreviousSaveSlot(true); }
void LastAutoSaveSlot_Run(void) { LastAutoSaveSlot(true); }
void ShowStateInfo(bool pressed) {
if (!pressed) return;
std::string message = "Save to: "+(use_save_file&&savefilename.size()?"File "+savefilename:"Slot "+std::to_string(GetGameState_Run()+1))+"\n"+SaveState::instance().getName(GetGameState_Run(), true);
systemmessagebox("Saved state information", message.c_str(), "ok","info", 1);
}
void AddSaveStateMapper() {
DOSBoxMenu::item *item;
MAPPER_AddHandler(SaveGameState, MK_s, MMODHOST,"savestate","Save state", &item);
item->set_text("Save state");
MAPPER_AddHandler(LoadGameState, MK_l, MMODHOST,"loadstate","Load state", &item);
item->set_text("Load state");
MAPPER_AddHandler(ShowStateInfo, MK_nothing, 0,"showstate","Display state info", &item);
item->set_text("Display state information");
MAPPER_AddHandler(PreviousSaveSlot, MK_comma, MMODHOST,"prevslot","Previous save slot", &item);
item->set_text("Select previous slot");
MAPPER_AddHandler(NextSaveSlot, MK_period, MMODHOST,"nextslot","Next save slot", &item);
item->set_text("Select next slot");
}
#ifndef WIN32
char* itoa(int value, char* str, int radix) {
/**
* C++ version 0.4 char* style "itoa":
* Written by Lukás Chmela
* Released under GPLv3.
*/
// check that the radix if valid
if (radix < 2 || radix > 36) { *str = '\0'; return str; }
char* ptr = str, *ptr1 = str, tmp_char;
int tmp_value;
do {
tmp_value = value;
value /= radix;
*ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz" [35 + (tmp_value - value * radix)];
} while ( value );
// Apply negative sign
if (tmp_value < 0) *ptr++ = '-';
*ptr-- = '\0';
while(ptr1 < ptr) {
tmp_char = *ptr;
*ptr--= *ptr1;
*ptr1++ = tmp_char;
}
return str;
}
#endif
SaveState& SaveState::instance() {
static SaveState singleton;
return singleton;
}
void SaveState::registerComponent(const std::string& uniqueName, Component& comp) {
components.insert(std::make_pair(uniqueName, CompData(comp)));
}
namespace Util {
std::string compress(const std::string& input) { //throw (SaveState::Error)
if (input.empty())
return input;
const uLong bufferSize = ::compressBound((uLong)input.size());
std::string output;
output.resize(bufferSize);
uLongf actualSize = bufferSize;
if (::compress2(reinterpret_cast<Bytef*>(&output[0]), &actualSize,
reinterpret_cast<const Bytef*>(input.c_str()), (uLong)input.size(), Z_BEST_SPEED) != Z_OK)
throw SaveState::Error("Compression failed!");
output.resize(actualSize);
//save size of uncompressed data
const size_t uncompressedSize = input.size(); //save size of uncompressed data
output.resize(output.size() + sizeof(uncompressedSize)); //won't trigger a reallocation
::memcpy(&output[0] + output.size() - sizeof(uncompressedSize), &uncompressedSize, sizeof(uncompressedSize));
return std::string(&output[0], output.size()); //strip reserved space
}
std::string decompress(const std::string& input) { //throw (SaveState::Error)
if (input.empty())
return input;
//retrieve size of uncompressed data
size_t uncompressedSize = 0;
::memcpy(&uncompressedSize, &input[0] + input.size() - sizeof(uncompressedSize), sizeof(uncompressedSize));
std::string output;
output.resize(uncompressedSize);
uLongf actualSize = (uLongf)uncompressedSize;
if (::uncompress(reinterpret_cast<Bytef*>(&output[0]), &actualSize,
reinterpret_cast<const Bytef*>(input.c_str()), (uLong)(input.size() - sizeof(uncompressedSize))) != Z_OK)
throw SaveState::Error("Decompression failed!");
output.resize(actualSize); //should be superfluous!
return output;
}
}
inline void SaveState::RawBytes::set(const std::string& stream) {
bytes = stream;
isCompressed = false;
dataExists = true;
}
inline std::string SaveState::RawBytes::get() const { //throw (Error){
if (isCompressed)
(Util::decompress(bytes)).swap(bytes);
isCompressed = false;
return bytes;
}
inline void SaveState::RawBytes::compress() const { //throw (Error)
if (!isCompressed)
(Util::compress(bytes)).swap(bytes);
isCompressed = true;
}
inline bool SaveState::RawBytes::dataAvailable() const {
return dataExists;
}
#define CASESENSITIVITY (0)
#define MAXFILENAME (256)
int mymkdir(const char* dirname)
{
int ret=0;
#ifdef _WIN32
ret = _mkdir(dirname);
#elif unix
ret = mkdir (dirname,0775);
#elif __APPLE__
ret = mkdir (dirname,0775);
#endif
return ret;
}
int makedir(const char *newdir)
{
char *buffer ;
char *p;
int len = (int)strlen(newdir);
if (len <= 0)
return 0;
buffer = (char*)malloc(len+1);
if (buffer==NULL)
{
LOG_MSG("Error allocating memory\n");
return UNZ_INTERNALERROR;
}
strcpy(buffer,newdir);
if (buffer[len-1] == '/') {
buffer[len-1] = '\0';
}
if (mymkdir(buffer) == 0)
{
free(buffer);
return 1;
}
p = buffer+1;
while (1)
{
char hold;
while(*p && *p != '\\' && *p != '/')
p++;
hold = *p;
*p = 0;
if ((mymkdir(buffer) == -1) && (errno == ENOENT))
{
LOG_MSG("Could not create directory %s\n",buffer);
free(buffer);
return 0;
}
if (hold == 0)
break;
*p++ = hold;
}
free(buffer);
return 1;
}
void change_file_date(const char *filename, uLong dosdate, tm_unz tmu_date)
{
(void)dosdate;
#ifdef _WIN32
HANDLE hFile;
FILETIME ftm,ftLocal,ftCreate,ftLastAcc,ftLastWrite;
hFile = CreateFileA(filename,GENERIC_READ | GENERIC_WRITE,
0,NULL,OPEN_EXISTING,0,NULL);
GetFileTime(hFile,&ftCreate,&ftLastAcc,&ftLastWrite);
DosDateTimeToFileTime((WORD)(dosdate>>16),(WORD)dosdate,&ftLocal);
LocalFileTimeToFileTime(&ftLocal,&ftm);
SetFileTime(hFile,&ftm,&ftLastAcc,&ftm);
CloseHandle(hFile);
#else
#if defined(unix) || defined(__APPLE__)
struct utimbuf ut;
struct tm newdate;
newdate.tm_sec = tmu_date.tm_sec;
newdate.tm_min=tmu_date.tm_min;
newdate.tm_hour=tmu_date.tm_hour;
newdate.tm_mday=tmu_date.tm_mday;
newdate.tm_mon=tmu_date.tm_mon;
if (tmu_date.tm_year > 1900)
newdate.tm_year=tmu_date.tm_year - 1900;
else
newdate.tm_year=tmu_date.tm_year ;
newdate.tm_isdst=-1;
ut.actime=ut.modtime=mktime(&newdate);
utime(filename,&ut);
#endif
#endif
}
int do_extract_currentfile(unzFile uf, const int* popt_extract_without_path, int* popt_overwrite, const char* password, const char *savename=NULL)
{
char filename_inzip[256];
char* filename_withoutpath;
char* p;
int err=UNZ_OK;
FILE *fout=NULL;
void* buf;
uInt size_buf;
unz_file_info64 file_info;
uLong ratio=0;
err = unzGetCurrentFileInfo64(uf,&file_info,filename_inzip,sizeof(filename_inzip),NULL,0,NULL,0);
(void)ratio;
if (err!=UNZ_OK)
{
LOG_MSG("Error %d with zipfile in unzGetCurrentFileInfo\n",err);
return err;
}
size_buf = 8192;
buf = malloc(size_buf);
if (buf==NULL)
{
LOG_MSG("Error allocating memory\n");
return UNZ_INTERNALERROR;
}
p = filename_withoutpath = filename_inzip;
while ((*p) != '\0')
{
if (((*p)=='/') || ((*p)=='\\'))
filename_withoutpath = p+1;
p++;
}
if ((*filename_withoutpath)=='\0')
{
if ((*popt_extract_without_path)==0)
{
LOG_MSG("Creating directory: %s\n",filename_inzip);
mymkdir(filename_inzip);
}
}
else
{
const char* write_filename;
int skip=0;
if (savename!=NULL)
write_filename = savename;
else if ((*popt_extract_without_path)==0)
write_filename = filename_inzip;
else
write_filename = filename_withoutpath;
err = unzOpenCurrentFilePassword(uf,password);
if (err!=UNZ_OK)
{
LOG_MSG("Error %d with zipfile in unzOpenCurrentFilePassword\n",err);
}
if (((*popt_overwrite)==0) && (err==UNZ_OK))
{
char rep=0;
FILE* ftestexist;
ftestexist = FOPEN_FUNC(write_filename,"rb");
if (ftestexist!=NULL)
{
fclose(ftestexist);
do
{
char answer[128];
int ret;
LOG_MSG("The file %s exists. Overwrite ? [y]es, [n]o, [A]ll: ",write_filename);
ret = scanf("%1s",answer);
if (ret != 1)
{
exit(EXIT_FAILURE);
}
rep = answer[0] ;
if ((rep>='a') && (rep<='z'))
rep -= 0x20;
}
while ((rep!='Y') && (rep!='N') && (rep!='A'));
}
if (rep == 'N')
skip = 1;
if (rep == 'A')
*popt_overwrite=1;
}
if ((skip==0) && (err==UNZ_OK))
{
fout=FOPEN_FUNC(write_filename,"wb");
/* some zipfile don't contain directory alone before file */
if ((fout==NULL) && ((*popt_extract_without_path)==0) &&
(filename_withoutpath!=(char*)filename_inzip))
{
char c=*(filename_withoutpath-1);
*(filename_withoutpath-1)='\0';
makedir(write_filename);
*(filename_withoutpath-1)=c;
fout=FOPEN_FUNC(write_filename,"wb");
}
if (fout==NULL)
{
LOG_MSG("error opening %s\n",write_filename);
}
}
if (fout!=NULL)
{
LOG_MSG(" Extracting: %s\n",write_filename);
do
{
err = unzReadCurrentFile(uf,buf,size_buf);
if (err<0)
{
LOG_MSG("Error %d with zipfile in unzReadCurrentFile\n",err);
break;
}
if (err>0)
if (fwrite(buf,err,1,fout)!=1)
{
LOG_MSG("Error in writing extracted file\n");
err=UNZ_ERRNO;
break;
}
}
while (err>0);
if (fout)
fclose(fout);
if (err==0)
change_file_date(write_filename,file_info.dosDate,
file_info.tmu_date);
}
if (err==UNZ_OK)
{
err = unzCloseCurrentFile (uf);
if (err!=UNZ_OK)
{
LOG_MSG("Error %d with zipfile in unzCloseCurrentFile\n",err);
}
}
else
unzCloseCurrentFile(uf); /* don't lose the error */
}
free(buf);
return err;
}
int do_extract(unzFile uf, int opt_extract_without_path, int opt_overwrite, const char* password)
{
uLong i;
unz_global_info64 gi;
int err;
FILE* fout=NULL;
(void)fout;
err = unzGetGlobalInfo64(uf,&gi);
if (err!=UNZ_OK) {
LOG_MSG("Error %d with zipfile in unzGetGlobalInfo \n",err);
return 0;
}
for (i=0;i<gi.number_entry;i++)
{
if (do_extract_currentfile(uf,&opt_extract_without_path,
&opt_overwrite,
password) != UNZ_OK)
break;
if ((i+1)<gi.number_entry)
{
err = unzGoToNextFile(uf);
if (err!=UNZ_OK)
{
LOG_MSG("Error %d with zipfile in unzGoToNextFile\n",err);
break;
}
}
}
return 0;
}
int do_extract_onefile(unzFile uf, const char* filename, int opt_extract_without_path, int opt_overwrite, const char* password, const char *savename=NULL)
{
int err = UNZ_OK;
(void)err;
if (unzLocateFile(uf,filename,CASESENSITIVITY)!=UNZ_OK)
{
LOG_MSG("File %s not found in the zipfile\n",filename);
return 2;
}
if (do_extract_currentfile(uf,&opt_extract_without_path,
&opt_overwrite,
password, savename) == UNZ_OK)
return 0;
else
return 1;
}
int my_miniunz(char ** savefile, const char * savefile2, const char * savedir, char* savename = NULL) {
const char *zipfilename=NULL;
const char *filename_to_extract=NULL;
const char *password=NULL;
char filename_try[MAXFILENAME+16] = "";
int ret_value=0;
int opt_do_extract=1;
int opt_do_extract_withoutpath=0;
int opt_overwrite=0;
int opt_extractdir=0;
const char *dirname=NULL;
unzFile uf=NULL;
opt_do_extract = opt_do_extract_withoutpath = 1;
opt_overwrite=1;
opt_extractdir=1;
dirname=savedir;
zipfilename = (const char *)savefile;
filename_to_extract = savefile2;
if (zipfilename!=NULL)
{
# ifdef USEWIN32IOAPI
zlib_filefunc64_def ffunc;
# endif
strncpy(filename_try, zipfilename,MAXFILENAME-1);
/* strncpy doesnt append the trailing NULL, of the string is too long. */
filename_try[ MAXFILENAME ] = '\0';
# ifdef USEWIN32IOAPI
fill_win32_filefunc64A(&ffunc);
uf = unzOpen2_64(zipfilename,&ffunc);
# else
uf = unzOpen64(zipfilename);
# endif
}
if (uf==NULL)
{
//LOG_MSG("Cannot open %s\n",zipfilename,zipfilename);
return 1;
}
//LOG_MSG("%s opened\n",filename_try);
if (opt_do_extract==1)
{
char cCurrentPath[FILENAME_MAX];
char *ret=getcwd(cCurrentPath, sizeof(cCurrentPath));
if (opt_extractdir && chdir(dirname))
{
LOG_MSG("Error changing into %s, aborting\n", dirname);
exit(-1);
}
if (filename_to_extract == NULL)
ret_value = do_extract(uf, opt_do_extract_withoutpath, opt_overwrite, password);
else
ret_value = do_extract_onefile(uf, filename_to_extract, opt_do_extract_withoutpath, opt_overwrite, password, savename);
if (ret!=NULL) chdir(cCurrentPath);
}
unzClose(uf);
return ret_value;
}
#ifdef _WIN32
uLong filetime(char *f, tm_zip *tmzip, uLong *dt)
{
int ret = 0;
{
FILETIME ftLocal;
HANDLE hFind;
WIN32_FIND_DATAA ff32;
hFind = FindFirstFileA(f,&ff32);
if (hFind != INVALID_HANDLE_VALUE)
{
FileTimeToLocalFileTime(&(ff32.ftLastWriteTime),&ftLocal);
FileTimeToDosDateTime(&ftLocal,((LPWORD)dt)+1,((LPWORD)dt)+0);
FindClose(hFind);
ret = 1;
}
}
return ret;
}
#else
#if defined(unix) || defined(__APPLE__)
uLong filetime(char *f, tm_zip *tmzip, uLong *dt)
{
(void)dt;
int ret=0;
struct stat s; /* results of stat() */
struct tm* filedate;
time_t tm_t=0;
if (strcmp(f,"-")!=0)
{
char name[MAXFILENAME+1];
int len = strlen(f);
if (len > MAXFILENAME)
len = MAXFILENAME;
strncpy(name, f,MAXFILENAME-1);
/* strncpy doesnt append the trailing NULL, of the string is too long. */
name[ MAXFILENAME ] = '\0';
if (name[len - 1] == '/')
name[len - 1] = '\0';
/* not all systems allow stat'ing a file with / appended */
if (stat(name,&s)==0)
{
tm_t = s.st_mtime;
ret = 1;
}
}
filedate = localtime(&tm_t);
tmzip->tm_sec = filedate->tm_sec;
tmzip->tm_min = filedate->tm_min;
tmzip->tm_hour = filedate->tm_hour;
tmzip->tm_mday = filedate->tm_mday;
tmzip->tm_mon = filedate->tm_mon ;
tmzip->tm_year = filedate->tm_year;
return ret;
}
#else
uLong filetime(char *f, tm_zip *tmzip, uLong *dt)
{
return 0;
}
#endif
#endif
#ifdef __APPLE__
// In darwin and perhaps other BSD variants off_t is a 64 bit value, hence no need for specific 64 bit functions
#define FOPEN_FUNC(filename, mode) fopen(filename, mode)
#define FTELLO_FUNC(stream) ftello(stream)
#define FSEEKO_FUNC(stream, offset, origin) fseeko(stream, offset, origin)
#else
#define FOPEN_FUNC(filename, mode) fopen64(filename, mode)
#define FTELLO_FUNC(stream) ftello64(stream)
#define FSEEKO_FUNC(stream, offset, origin) fseeko64(stream, offset, origin)
#endif
int getFileCrc(const char* filenameinzip,void*buf,unsigned long size_buf,unsigned long* result_crc)
{
unsigned long calculate_crc=0;
int err=ZIP_OK;
FILE * fin = FOPEN_FUNC(filenameinzip,"rb");
unsigned long size_read = 0;
unsigned long total_read = 0;
if (fin==NULL)
err = ZIP_ERRNO;
if (err == ZIP_OK)
do
{
err = ZIP_OK;
size_read = (int)fread(buf,1,size_buf,fin);
if (size_read < size_buf)
if (feof(fin)==0)
{
LOG_MSG("Error in reading %s\n",filenameinzip);
err = ZIP_ERRNO;
}
if (size_read>0)
calculate_crc = crc32(calculate_crc,(const Bytef*)buf,size_read);
total_read += size_read;
} while ((err == ZIP_OK) && (size_read>0));
if (fin)
fclose(fin);
*result_crc=calculate_crc;
LOG_MSG("File %s crc %lx\n", filenameinzip, calculate_crc);
return err;
}
int isLargeFile(const char* filename)
{
int largeFile = 0;
ZPOS64_T pos = 0;
FILE* pFile = FOPEN_FUNC(filename, "rb");
if(pFile != NULL)
{
int n = FSEEKO_FUNC(pFile, 0, SEEK_END);
pos = FTELLO_FUNC(pFile);
(void)n;
LOG_MSG("File : %s is %lld bytes\n", filename, pos);
if(pos >= 0xffffffff)
largeFile = 1;
fclose(pFile);
}
return largeFile;
}
int my_minizip(char ** savefile, char ** savefile2, char* savename=NULL) {
int opt_overwrite=0;
int opt_compress_level=Z_DEFAULT_COMPRESSION;
int opt_exclude_path=savename==NULL?1:0;
int zipfilenamearg = 0;
(void)zipfilenamearg;
//char filename_try[MAXFILENAME16];
int err=0;
int size_buf=0;
void* buf=NULL;
const char* password=NULL;
opt_overwrite = 2;
opt_compress_level = 9;
size_buf = 16384;
buf = malloc(size_buf);
if (buf==NULL)
{
//LOG_MSG("Error allocating memory\n");
return ZIP_INTERNALERROR;
}
{
zipFile zf;
int errclose;
# ifdef USEWIN32IOAPI
zlib_filefunc64_def ffunc;
fill_win32_filefunc64A(&ffunc);
zf = zipOpen2_64(savefile,(opt_overwrite==2) ? 2 : 0,NULL,&ffunc);
# else
zf = zipOpen64(savefile,(opt_overwrite==2) ? 2 : 0);
# endif
if (zf == NULL)
{
//LOG_MSG("Error opening %s\n",savefile);
err= ZIP_ERRNO;
}
else
//LOG_MSG("Creating %s\n",savefile);
{
FILE *fin = NULL;
int size_read;
char* filenameinzip = (char *)savefile2;
const char *savefilenameinzip;
zip_fileinfo zi;
unsigned long crcFile=0;
int zip64 = 0;
zi.tmz_date.tm_sec = zi.tmz_date.tm_min = zi.tmz_date.tm_hour =
zi.tmz_date.tm_mday = zi.tmz_date.tm_mon = zi.tmz_date.tm_year = 0;
zi.dosDate = 0;
zi.internal_fa = 0;
zi.external_fa = 0;
filetime(filenameinzip,&zi.tmz_date,&zi.dosDate);
if ((password != NULL) && (err==ZIP_OK))
err = getFileCrc(filenameinzip,buf,size_buf,&crcFile);
zip64 = isLargeFile(filenameinzip);
/* The path name saved, should not include a leading slash. */
/*if it did, windows/xp and dynazip couldn't read the zip file. */
savefilenameinzip = savename == NULL ? filenameinzip : savename;
while( savefilenameinzip[0] == '\\' || savefilenameinzip[0] == '/' )
{
savefilenameinzip++;
}
/*should the zip file contain any path at all?*/
if( opt_exclude_path )
{
const char *tmpptr;
const char *lastslash = 0;
for( tmpptr = savefilenameinzip; *tmpptr; tmpptr++)
{
if( *tmpptr == '\\' || *tmpptr == '/')
{
lastslash = tmpptr;
}
}
if( lastslash != NULL )
{
savefilenameinzip = lastslash+1; // base filename follows last slash.
}
}
/**/
err = zipOpenNewFileInZip3_64(zf,savefilenameinzip,&zi,
NULL,0,NULL,0,NULL /* comment*/,
(opt_compress_level != 0) ? Z_DEFLATED : 0,
opt_compress_level,0,
/* -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, */
-MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY,
password,crcFile, zip64);
if (err != ZIP_OK) {
//LOG_MSG("Error in opening %s in zipfile\n",filenameinzip);
}
else
{
fin = fopen64(filenameinzip,"rb");
if (fin==NULL)
{
err=ZIP_ERRNO;
//LOG_MSG("Error in opening %s for reading\n",filenameinzip);
}
}
if (err == ZIP_OK)
do
{
err = ZIP_OK;
size_read = (int)fread(buf,1,size_buf,fin);
if (size_read < size_buf)
if (feof(fin)==0)
{
//LOG_MSG("Error in reading %s\n",filenameinzip);
err = ZIP_ERRNO;
}
if (size_read>0)
{
err = zipWriteInFileInZip (zf,buf,size_read);
if (err<0)
{
//LOG_MSG("Error in writing %s in the zipfile\n",
// filenameinzip);
}
}
} while ((err == ZIP_OK) && (size_read>0));
if (fin)
fclose(fin);
if (err<0)
err=ZIP_ERRNO;
else
{
err = zipCloseFileInZip(zf);
if (err!=ZIP_OK) {
//LOG_MSG("Error in closing %s in the zipfile\n",
// filenameinzip);
}
}
}
errclose = zipClose(zf,NULL);
if (errclose != ZIP_OK) {
//LOG_MSG("Error in closing %s\n",savefile);
}
}
free(buf);
return 0;
}
int flagged_backup(char *zip);
int flagged_restore(char* zip);
void SaveState::save(size_t slot) { //throw (Error)
if (slot >= SLOT_COUNT*MAX_PAGE) return;
SDL_PauseAudio(0);
bool save_err=false;
if((MEM_TotalPages()*4096/1024/1024)>1024) {
LOG_MSG("Stopped. 1 GB is the maximum memory size for saving/loading states.");
notifyError("Unsupported memory size for saving states.", false);
return;
}
bool compresssaveparts = static_cast<Section_prop *>(control->GetSection("dosbox"))->Get_bool("compresssaveparts");
const char *save_remark = "";
#if !defined(HX_DOS)
if (auto_save_state)
save_remark = "Auto-save";
else if (!noremark_save_state) {
/* NTS: tinyfd_inputBox() returns a string from an internal statically declared char array.
* It is not necessary to free the return string, but it is important to understand that
* the next call to tinyfd_inputBox() will obliterate the previously returned string.
* See src/libs/tinyfiledialogs/tinyfiledialogs.c line 5069 --J.C. */
/* NTS: The code was originally written to declare save_remark as char* default assigned to string
* constant "", but GCC (rightfully so) complains you're pointing char* at something that
* is stored const by the compiler. "save_remark" is not modified past this point, so it
* has been changed to const char* and the return value of tinyfd_inputBox() is given to
* a local temporary char* string where the modification can be made, and *then* assigned
* to the const char* string for the rest of this function. */
bool fs=GFX_IsFullscreen();
if (fs) GFX_SwitchFullScreen();
MAPPER_ReleaseAllKeys();
GFX_LosingFocus();
GFX_ReleaseMouse();
char *new_remark = tinyfd_inputBox("Save state", "Please enter remark for the state (optional; 30 characters maximum). Click the Cancel button to cancel the saving.", " ");
MAPPER_ReleaseAllKeys();
GFX_LosingFocus();
if (fs&&!GFX_IsFullscreen()) GFX_SwitchFullScreen();
if (new_remark==NULL) return;
new_remark=trim(new_remark);
if (strlen(new_remark)>30) new_remark[30]=0;
save_remark = new_remark;
}
#endif
bool create_version=false;
bool create_title=false;
bool create_memorysize=false;
bool create_machinetype=false;
bool create_timestamp=false;
bool create_saveremark=false;
std::string path;
bool Get_Custom_SaveDir(std::string& savedir);
if(Get_Custom_SaveDir(path)) {
path+=CROSS_FILESPLIT;
} else {
extern std::string capturedir;
const size_t last_slash_idx = capturedir.find_last_of("\\/");
if (std::string::npos != last_slash_idx) {
path = capturedir.substr(0, last_slash_idx);
} else {
path = ".";
}
path+=CROSS_FILESPLIT;
path+="save";
Cross::CreateDir(path);
path+=CROSS_FILESPLIT;
}
std::string temp, save2;
std::stringstream slotname;
slotname << slot+1;
temp=path;
std::string save=use_save_file&&savefilename.size()?savefilename:temp+slotname.str()+".sav";
remove(save.c_str());
std::ofstream file (save.c_str());
file << "";
file.close();
try {
for (CompEntry::iterator i = components.begin(); i != components.end(); ++i) {
std::ostringstream ss;
i->second.comp.getBytes(ss);
i->second.rawBytes[slot].set(ss.str());
//LOG_MSG("Component is %s",i->first.c_str());
if(!create_version) {
std::string tempname = temp+"DOSBox-X_Version";
std::ofstream emulatorversion (tempname.c_str(), std::ofstream::binary);
emulatorversion << "DOSBox-X " << VERSION << " (" << SDL_STRING << ")" << std::endl << GetPlatform(true) << std::endl << UPDATED_STR;
if (!compresssaveparts) emulatorversion << std::endl << "No compression";
create_version=true;
emulatorversion.close();
}
if(!create_title) {
std::string tempname = temp+"Program_Name";
std::ofstream programname (tempname.c_str(), std::ofstream::binary);
programname << RunningProgram;
create_title=true;
programname.close();
}
if(!create_memorysize) {
std::string tempname = temp+"Memory_Size";
std::ofstream memorysize (tempname.c_str(), std::ofstream::binary);
memorysize << MEM_TotalPages();
create_memorysize=true;
memorysize.close();
}
if(!create_machinetype) {
std::string tempname = temp+"Machine_type";
std::ofstream machinetype (tempname.c_str(), std::ofstream::binary);
machinetype << getType();
create_machinetype=true;
machinetype.close();
}
if(!create_timestamp) {
std::string tempname = temp+"Time_Stamp";
std::ofstream timestamp (tempname.c_str(), std::ofstream::binary);
timestamp << getTime(true);
create_timestamp=true;
timestamp.close();
}
if(!create_saveremark) {
std::string tempname = temp+"Save_Remark";
std::ofstream saveremark (tempname.c_str(), std::ofstream::binary);
saveremark << std::string(save_remark);
create_saveremark=true;
saveremark.close();
}
std::string realtemp;
realtemp = temp + i->first;
std::ofstream outfile (realtemp.c_str(), std::ofstream::binary);
outfile << (compresssaveparts?Util::compress(ss.str()):ss.str());
//compress all other saved states except position "slot"
//const std::vector<RawBytes>& rb = i->second.rawBytes;
//std::for_each(rb.begin(), rb.begin() + slot, std::mem_fun_ref(&RawBytes::compress));
//std::for_each(rb.begin() + slot + 1, rb.end(), std::mem_fun_ref(&RawBytes::compress));
outfile.close();
ss.clear();
if(outfile.fail()) {
LOG_MSG("Save failed! - %s", realtemp.c_str());
save_err=true;
remove(save.c_str());
goto delete_all;
}
}
}
catch (const std::bad_alloc&) {
LOG_MSG("Save failed! Out of Memory!");
save_err=true;
remove(save.c_str());
goto delete_all;
}
for (CompEntry::iterator i = components.begin(); i != components.end(); ++i) {
save2=temp+i->first;
my_minizip((char **)save.c_str(), (char **)save2.c_str());
}
save2=temp+"DOSBox-X_Version";
my_minizip((char **)save.c_str(), (char **)save2.c_str());
save2=temp+"Program_Name";
my_minizip((char **)save.c_str(), (char **)save2.c_str());
save2=temp+"Memory_Size";
my_minizip((char **)save.c_str(), (char **)save2.c_str());
save2=temp+"Machine_Type";
my_minizip((char **)save.c_str(), (char **)save2.c_str());
save2=temp+"Time_Stamp";
my_minizip((char **)save.c_str(), (char **)save2.c_str());
save2=temp+"Save_Remark";
my_minizip((char **)save.c_str(), (char **)save2.c_str());
if (!dos_kernel_disabled) flagged_backup((char *)save.c_str());
delete_all:
for (CompEntry::iterator i = components.begin(); i != components.end(); ++i) {
save2=temp+i->first;
remove(save2.c_str());
}
save2=temp+"DOSBox-X_Version";
remove(save2.c_str());
save2=temp+"Program_Name";
remove(save2.c_str());
save2=temp+"Memory_Size";
remove(save2.c_str());
save2=temp+"Machine_Type";
remove(save2.c_str());
save2=temp+"Time_Stamp";
remove(save2.c_str());
save2=temp+"Save_Remark";
remove(save2.c_str());
if (save_err)
notifyError("Failed to save the current state.");
else
LOG_MSG("[%s]: Saved. (Slot %d)", getTime().c_str(), (int)slot+1);
}
void savestatecorrupt(const char* part) {
LOG_MSG("Save state corrupted! Program in inconsistent state! - %s", part);
systemmessagebox("Error","Save state corrupted! Program may not work.","ok","error", 1);
}
bool confres=false;
bool loadstateconfirm(int ind) {
if (ind<0||ind>4) return false;
confres=false;
MAPPER_ReleaseAllKeys();
GFX_LosingFocus();
GUI_Shortcut(23+ind);
MAPPER_ReleaseAllKeys();
GFX_LosingFocus();
bool ret=confres;
confres=false;
return ret;
}
void SaveState::load(size_t slot) const { //throw (Error)
// if (isEmpty(slot)) return;
bool load_err=false;
if((MEM_TotalPages()*4096/1024/1024)>1024) {
LOG_MSG("Stopped. 1 GB is the maximum memory size for saving/loading states.");
notifyError("Unsupported memory size for loading states.", false);
return;
}
SDL_PauseAudio(0);
extern const char* RunningProgram;
bool read_version=false;
bool read_title=false;
bool read_memorysize=false;
bool read_machinetype=false;
bool decompressparts=true;
std::string path;
bool Get_Custom_SaveDir(std::string& savedir);
if(Get_Custom_SaveDir(path)) {
path+=CROSS_FILESPLIT;
} else {
extern std::string capturedir;
const size_t last_slash_idx = capturedir.find_last_of("\\/");
if (std::string::npos != last_slash_idx) {
path = capturedir.substr(0, last_slash_idx);
} else {
path = ".";
}
path += CROSS_FILESPLIT;
path +="save";
path += CROSS_FILESPLIT;
}
std::string temp;
temp = path;
std::stringstream slotname;
slotname << slot+1;
std::string save=use_save_file&&savefilename.size()?savefilename:temp+slotname.str()+".sav";
std::ifstream check_slot;
check_slot.open(save.c_str(), std::ifstream::in);
if(check_slot.fail()) {
LOG_MSG("No saved slot - %d (%s)",(int)slot+1,save.c_str());
notifyError(use_save_file&&savefilename.size()?"The selected save file is currently empty.":"The selected save slot is an empty slot.", false);
load_err=true;
return;
}
for (CompEntry::const_iterator i = components.begin(); i != components.end(); ++i) {
std::filebuf * fb;
std::ifstream ss;
std::ifstream check_file;
fb = ss.rdbuf();
//LOG_MSG("Component is %s",i->first.c_str());
my_miniunz((char **)save.c_str(),i->first.c_str(),temp.c_str());
if(!read_version) {
my_miniunz((char **)save.c_str(),"DOSBox-X_Version",temp.c_str());
std::ifstream check_version;
int length = 8;
std::string tempname = temp+"DOSBox-X_Version";
check_version.open(tempname.c_str(), std::ifstream::in);
if(check_version.fail()) {
savestatecorrupt("DOSBox-X_Version");
load_err=true;
goto delete_all;
}
check_version.seekg (0, std::ios::end);
length = (int)check_version.tellg();
check_version.seekg (0, std::ios::beg);
char * const buffer = (char*)alloca( (length+1) * sizeof(char)); // char buffer[length];
check_version.read (buffer, length);
check_version.close();
buffer[length]='\0';
char *p;
if (strstr(buffer, "\nNo compression") != NULL) {
decompressparts = false;
p=strrchr(buffer, '\n');
if (p!=NULL) *p=0;
}
p=strrchr(buffer, '\n');
if (p!=NULL) *p=0;
std::string emulatorversion = std::string("DOSBox-X ") + VERSION + std::string(" (") + SDL_STRING + std::string(")\n") + GetPlatform(true);
if (p==NULL||strcasecmp(buffer,emulatorversion.c_str())) {
if(!force_load_state&&!loadstateconfirm(0)) {
LOG_MSG("Aborted. Check your DOSBox-X version: %s",buffer);
load_err=true;
goto delete_all;
}
}
read_version=true;
}
if(!read_title) {
my_miniunz((char **)save.c_str(),"Program_Name",temp.c_str());
std::ifstream check_title;
int length = 8;
std::string tempname = temp+"Program_Name";
check_title.open(tempname.c_str(), std::ifstream::in);
if(check_title.fail()) {
savestatecorrupt("Program_Name");
load_err=true;
goto delete_all;
}
check_title.seekg (0, std::ios::end);
length = (int)check_title.tellg();
check_title.seekg (0, std::ios::beg);
char * const buffer = (char*)alloca( (length+1) * sizeof(char)); // char buffer[length];
check_title.read (buffer, length);
check_title.close();
if (!length||(size_t)length!=strlen(RunningProgram)||strncmp(buffer,RunningProgram,length)) {
if(!force_load_state&&!loadstateconfirm(1)) {
buffer[length]='\0';
LOG_MSG("Aborted. Check your program name: %s",buffer);
load_err=true;
goto delete_all;
}
if (length<9) {
static char pname[9];
if (length) {
strncpy(pname,buffer,length);
pname[length]=0;
} else
strcpy(pname, "DOSBOX-X");
RunningProgram=pname;
void GFX_SetTitle(int32_t cycles, int frameskip, Bits timing, bool paused);
GFX_SetTitle(-1,-1,-1,false);
}
}
read_title=true;
}
if(!read_memorysize) {
my_miniunz((char **)save.c_str(),"Memory_Size",temp.c_str());
std::fstream check_memorysize;
int length = 8;
std::string tempname = temp+"Memory_Size";
check_memorysize.open(tempname.c_str(), std::ifstream::in);
if(check_memorysize.fail()) {
savestatecorrupt("Memory_Size");
load_err=true;
goto delete_all;
}
check_memorysize.seekg (0, std::ios::end);
length = (int)check_memorysize.tellg();
check_memorysize.seekg (0, std::ios::beg);
char * const buffer = (char*)alloca( (length+1) * sizeof(char)); // char buffer[length];
check_memorysize.read (buffer, length);
check_memorysize.close();
char str[10];
itoa((int)MEM_TotalPages(), str, 10);
if(!length||(size_t)length!=strlen(str)||strncmp(buffer,str,length)) {
if(!force_load_state&&!loadstateconfirm(2)) {
buffer[length]='\0';
int size=atoi(buffer)*4096/1024/1024;
LOG_MSG("Aborted. Check your memory size: %d MB", size);
load_err=true;
goto delete_all;
}
}
read_memorysize=true;
}
if(!read_machinetype) {
my_miniunz((char **)save.c_str(),"Machine_Type",temp.c_str());
std::ifstream check_machinetype;
int length = 8;
std::string tempname = temp+"Machine_Type";
check_machinetype.open(tempname.c_str(), std::ifstream::in);
if(check_machinetype.fail()) {
savestatecorrupt("Machine_Type");
load_err=true;
goto delete_all;
}
check_machinetype.seekg (0, std::ios::end);
length = (int)check_machinetype.tellg();
check_machinetype.seekg (0, std::ios::beg);
char * const buffer = (char*)alloca( (length+1) * sizeof(char)); // char buffer[length];
check_machinetype.read (buffer, length);
check_machinetype.close();
char str[20];
strcpy(str, getType().c_str());
if(!length||(size_t)length!=strlen(str)||strncmp(buffer,str,length)) {
if(!force_load_state&&!loadstateconfirm(3)) {
LOG_MSG("Aborted. Check your machine type: %s",buffer);
load_err=true;
goto delete_all;
}
}
read_machinetype=true;
}
std::string realtemp;
realtemp = temp + i->first;
check_file.open(realtemp.c_str(), std::ifstream::in);
check_file.close();
if(check_file.fail()) {
savestatecorrupt(i->first.c_str());
load_err=true;
goto delete_all;
}
clearline=true;
fb->open(realtemp.c_str(),std::ios::in | std::ios::binary);
std::string str((std::istreambuf_iterator<char>(ss)), std::istreambuf_iterator<char>());
std::stringstream mystream;
mystream << (decompressparts?Util::decompress(str):str);
i->second.comp.setBytes(mystream);
if (mystream.rdbuf()->in_avail() != 0 || mystream.eof()) { //basic consistency check
savestatecorrupt(i->first.c_str());
load_err=true;
goto delete_all;
}
//compress all other saved states except position "slot"
//const std::vector<RawBytes>& rb = i->second.rawBytes;
//std::for_each(rb.begin(), rb.begin() + slot, std::mem_fun_ref(&RawBytes::compress));
//std::for_each(rb.begin() + slot + 1, rb.end(), std::mem_fun_ref(&RawBytes::compress));
fb->close();
mystream.clear();
if (!dos_kernel_disabled) flagged_restore((char *)save.c_str());
}
delete_all:
std::string save2;
for (CompEntry::const_iterator i = components.begin(); i != components.end(); ++i) {
save2=temp+i->first;
remove(save2.c_str());
}
save2=temp+"DOSBox-X_Version";
remove(save2.c_str());
save2=temp+"Program_Name";
remove(save2.c_str());
save2=temp+"Memory_Size";
remove(save2.c_str());
save2=temp+"Machine_Type";
remove(save2.c_str());
if (!load_err) LOG_MSG("[%s]: Loaded. (Slot %d)", getTime().c_str(), (int)slot+1);
}
bool SaveState::isEmpty(size_t slot) const {
if (slot >= SLOT_COUNT*MAX_PAGE) return true;
std::string path;
bool Get_Custom_SaveDir(std::string& savedir);
if(Get_Custom_SaveDir(path)) {
path+=CROSS_FILESPLIT;
} else {
extern std::string capturedir;
const size_t last_slash_idx = capturedir.find_last_of("\\/");
if (std::string::npos != last_slash_idx) {
path = capturedir.substr(0, last_slash_idx);
} else {
path = ".";
}
path += CROSS_FILESPLIT;
path +="save";
path += CROSS_FILESPLIT;
}
std::string temp;
temp = path;
std::stringstream slotname;
slotname << slot+1;
std::string save=temp+slotname.str()+".sav";
std::ifstream check_slot;
check_slot.open(save.c_str(), std::ifstream::in);
return check_slot.fail();
}
void SaveState::removeState(size_t slot) const {
if (slot >= SLOT_COUNT*MAX_PAGE) return;
std::string path;
bool Get_Custom_SaveDir(std::string& savedir);
if(Get_Custom_SaveDir(path)) {
path+=CROSS_FILESPLIT;
} else {
extern std::string capturedir;
const size_t last_slash_idx = capturedir.find_last_of("\\/");
if (std::string::npos != last_slash_idx) {
path = capturedir.substr(0, last_slash_idx);
} else {
path = ".";
}
path += CROSS_FILESPLIT;
path +="save";
path += CROSS_FILESPLIT;
}
std::string temp;
temp = path;
std::stringstream slotname;
slotname << slot+1;
std::string save=temp+slotname.str()+".sav";
std::ifstream check_slot;
check_slot.open(save.c_str(), std::ifstream::in);
if(check_slot.fail()) {
LOG_MSG("No saved slot - %d (%s)",(int)slot+1,save.c_str());
notifyError("The selected save slot is an empty slot.", false);
return;
}
if (loadstateconfirm(4)) {
check_slot.close();
remove(save.c_str());
check_slot.open(save.c_str(), std::ifstream::in);
if (!check_slot.fail()) notifyError("Failed to remove the state in the save slot.");
if (page!=GetGameState()/SaveState::SLOT_COUNT)
SetGameState((int)slot);
else
refresh_slots();
}
}
std::string SaveState::getName(size_t slot, bool nl) const {
if (slot >= SLOT_COUNT*MAX_PAGE) return "["+std::string(MSG_Get("EMPTY_SLOT"))+"]";
std::string path;
bool Get_Custom_SaveDir(std::string& savedir);
if(Get_Custom_SaveDir(path)) {
path+=CROSS_FILESPLIT;
} else {
extern std::string capturedir;
const size_t last_slash_idx = capturedir.find_last_of("\\/");
if (std::string::npos != last_slash_idx) {
path = capturedir.substr(0, last_slash_idx);
} else {
path = ".";
}
path += CROSS_FILESPLIT;
path +="save";
path += CROSS_FILESPLIT;
}
std::string temp;
temp = path;
std::stringstream slotname;
slotname << slot+1;
std::string save=nl&&use_save_file&&savefilename.size()?savefilename:temp+slotname.str()+".sav";
std::ifstream check_slot;
check_slot.open(save.c_str(), std::ifstream::in);
if (check_slot.fail()) return nl?"(Empty state)":"["+std::string(MSG_Get("EMPTY_SLOT"))+"]";
my_miniunz((char **)save.c_str(),"Program_Name",temp.c_str());
std::ifstream check_title;
int length = 8;
std::string tempname = temp+"Program_Name";
check_title.open(tempname.c_str(), std::ifstream::in);
if (check_title.fail()) {
remove(tempname.c_str());
return "";
}
check_title.seekg (0, std::ios::end);
length = (int)check_title.tellg();
check_title.seekg (0, std::ios::beg);
char * const buffer1 = (char*)alloca( (length+1) * sizeof(char));
check_title.read (buffer1, length);
check_title.close();
remove(tempname.c_str());
buffer1[length]='\0';
std::string ret=nl?"Program: "+(!strlen(buffer1)?"-":std::string(buffer1))+"\n":"[Program: "+std::string(buffer1)+"]";
my_miniunz((char **)save.c_str(),"Time_Stamp",temp.c_str());
length=18;
tempname = temp+"Time_Stamp";
check_title.open(tempname.c_str(), std::ifstream::in);
if (check_title.fail()) {
remove(tempname.c_str());
return ret;
}
check_title.seekg (0, std::ios::end);
length = (int)check_title.tellg();
check_title.seekg (0, std::ios::beg);
char * const buffer2 = (char*)alloca( (length+1) * sizeof(char));
check_title.read (buffer2, length);
check_title.close();
remove(tempname.c_str());
buffer2[length]='\0';
if (strlen(buffer2)) ret+=nl?"Timestamp: "+(!strlen(buffer2)?"-":std::string(buffer2))+"\n":" ("+std::string(buffer2);
my_miniunz((char **)save.c_str(),"Save_Remark",temp.c_str());
length=30;
tempname = temp+"Save_Remark";
check_title.open(tempname.c_str(), std::ifstream::in);
if (check_title.fail()) {
remove(tempname.c_str());
return ret+(!nl?")":"");
}
check_title.seekg (0, std::ios::end);
length = (int)check_title.tellg();
check_title.seekg (0, std::ios::beg);
char * const buffer3 = (char*)alloca( (length+1) * sizeof(char));
check_title.read (buffer3, length);
check_title.close();
remove(tempname.c_str());
buffer3[length]='\0';
if (strlen(buffer3)) ret+=nl?"Remark: "+(!strlen(buffer3)?"-":std::string(buffer3))+"\n":" - "+std::string(buffer3)+")";
else if (!nl) ret+=")";
return ret;
}