mirror of
https://github.com/libretro/scummvm.git
synced 2025-02-28 15:26:12 +00:00
744 lines
19 KiB
C++
744 lines
19 KiB
C++
/* ScummVM - Scumm Interpreter
|
|
* Copyright (C) 2005-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* $Header$
|
|
*
|
|
*/
|
|
|
|
#include <tamtypes.h>
|
|
#include <kernel.h>
|
|
#include <sifrpc.h>
|
|
#include <loadfile.h>
|
|
#include <fileio.h>
|
|
#include <malloc.h>
|
|
#include <ucl/ucl.h>
|
|
#include <libmc.h>
|
|
#include "backends/ps2/savefile.h"
|
|
#include "backends/ps2/Gs2dScreen.h"
|
|
#include "backends/ps2/systemps2.h"
|
|
#include "common/scummsys.h"
|
|
|
|
#define UCL_MAGIC 0x314C4355
|
|
|
|
#define PORT 0
|
|
#define SLOT 0
|
|
// port 0, slot 0: memory card in first slot.
|
|
|
|
void sioprintf(const char *zFormat, ...);
|
|
|
|
class McAccess {
|
|
public:
|
|
McAccess(int port, int slot);
|
|
~McAccess(void);
|
|
int open(const char *name, int mode);
|
|
int close(int fd);
|
|
int size(int fd);
|
|
int read(int fd, void *buf, int size);
|
|
int write(int fd, const void *buf, int size);
|
|
int mkDir(const char *name);
|
|
int getDir(const char *name, unsigned int mode, int max, void *dest);
|
|
int getInfo(int *type, int *free, int *format);
|
|
private:
|
|
int _sema;
|
|
int _port, _slot;
|
|
};
|
|
|
|
McAccess::McAccess(int port, int slot) {
|
|
_port = port;
|
|
_slot = slot;
|
|
ee_sema_t newSema;
|
|
newSema.init_count = 1;
|
|
newSema.max_count = 1;
|
|
_sema = CreateSema(&newSema);
|
|
|
|
assert(mcInit(MC_TYPE_MC) >= 0);
|
|
}
|
|
|
|
McAccess::~McAccess(void) {
|
|
DeleteSema(_sema);
|
|
}
|
|
|
|
int McAccess::open(const char *name, int mode) {
|
|
int res;
|
|
WaitSema(_sema);
|
|
mcOpen(_port, _slot, name, mode);
|
|
mcSync(0, NULL, &res);
|
|
SignalSema(_sema);
|
|
return res;
|
|
}
|
|
|
|
int McAccess::close(int fd) {
|
|
int res;
|
|
WaitSema(_sema);
|
|
mcClose(fd);
|
|
mcSync(0, NULL, &res);
|
|
SignalSema(_sema);
|
|
return res;
|
|
}
|
|
|
|
int McAccess::size(int fd) {
|
|
int res, size;
|
|
WaitSema(_sema);
|
|
mcSeek(fd, 0, SEEK_END);
|
|
mcSync(0, NULL, &size);
|
|
mcSeek(fd, 0, SEEK_SET);
|
|
mcSync(0, NULL, &res);
|
|
SignalSema(_sema);
|
|
assert(res == 0);
|
|
return size;
|
|
}
|
|
|
|
int McAccess::read(int fd, void *buf, int size) {
|
|
int res;
|
|
WaitSema(_sema);
|
|
mcRead(fd, buf, size);
|
|
mcSync(0, NULL, &res);
|
|
SignalSema(_sema);
|
|
return res;
|
|
}
|
|
|
|
int McAccess::write(int fd, const void *buf, int size) {
|
|
int res;
|
|
WaitSema(_sema);
|
|
mcWrite(fd, buf, size);
|
|
mcSync(0, NULL, &res);
|
|
SignalSema(_sema);
|
|
return res;
|
|
}
|
|
|
|
int McAccess::mkDir(const char *name) {
|
|
int res;
|
|
WaitSema(_sema);
|
|
mcMkDir(_port, _slot, name);
|
|
mcSync(0, NULL, &res);
|
|
SignalSema(_sema);
|
|
return res;
|
|
}
|
|
|
|
int McAccess::getDir(const char *name, unsigned int mode, int max, void *dest) {
|
|
int res;
|
|
WaitSema(_sema);
|
|
mcGetDir(_port, _slot, name, mode, max, (mcTable*)dest);
|
|
mcSync(0, NULL, &res);
|
|
SignalSema(_sema);
|
|
return res;
|
|
}
|
|
|
|
int McAccess::getInfo(int *type, int *free, int *format) {
|
|
int res;
|
|
WaitSema(_sema);
|
|
mcGetInfo(_port, _slot, type, free, format);
|
|
mcSync(0, NULL, &res);
|
|
SignalSema(_sema);
|
|
return res;
|
|
}
|
|
|
|
class UclOutSaveFile : public Common::OutSaveFile {
|
|
public:
|
|
UclOutSaveFile(const char *filename, OSystem_PS2 *system, Gs2dScreen *screen, McAccess *mc);
|
|
virtual ~UclOutSaveFile(void);
|
|
virtual uint32 write(const void *ptr, uint32 size);
|
|
virtual void flush(void);
|
|
virtual bool ioFailed(void);
|
|
virtual void clearIOFailed(void);
|
|
private:
|
|
OSystem_PS2 *_system;
|
|
Gs2dScreen *_screen;
|
|
McAccess *_mc;
|
|
int _fd;
|
|
uint8 *_buf;
|
|
uint32 _bufSize, _bufPos;
|
|
bool _ioFailed, _wasFlushed;
|
|
};
|
|
|
|
class UclInSaveFile : public Common::InSaveFile {
|
|
public:
|
|
UclInSaveFile(const char *filename, Gs2dScreen *screen, McAccess *mc);
|
|
virtual ~UclInSaveFile(void);
|
|
virtual bool eos(void) const;
|
|
virtual uint32 read(void *ptr, uint32 size);
|
|
virtual bool ioFailed(void);
|
|
virtual void clearIOFailed(void);
|
|
virtual void skip(uint32 offset);
|
|
private:
|
|
Gs2dScreen *_screen;
|
|
McAccess *_mc;
|
|
uint8 *_buf;
|
|
uint32 _bufSize, _bufPos;
|
|
bool _ioFailed;
|
|
};
|
|
|
|
class AutoSaveFile : public Common::OutSaveFile {
|
|
public:
|
|
AutoSaveFile(Ps2SaveFileManager *saveMan, const char *filename);
|
|
~AutoSaveFile(void);
|
|
virtual uint32 write(const void *ptr, uint32 size);
|
|
virtual void flush(void) { };
|
|
virtual bool ioFailed(void) { return false; };
|
|
virtual void clearIOFailed(void) {};
|
|
private:
|
|
Ps2SaveFileManager *_saveMan;
|
|
char _fileName[256];
|
|
uint8 *_buf;
|
|
uint32 _bufSize, _bufPos;
|
|
};
|
|
|
|
AutoSaveFile::AutoSaveFile(Ps2SaveFileManager *saveMan, const char *filename) {
|
|
strcpy(_fileName, filename);
|
|
_saveMan = saveMan;
|
|
_bufSize = 65536;
|
|
_buf = (uint8*)memalign(64, _bufSize);
|
|
_bufPos = 0;
|
|
}
|
|
|
|
AutoSaveFile::~AutoSaveFile(void) {
|
|
_saveMan->writeSaveNonblocking(_fileName, _buf, _bufPos);
|
|
free(_buf);
|
|
}
|
|
|
|
uint32 AutoSaveFile::write(const void *ptr, uint32 size) {
|
|
uint32 bytesFree = _bufSize - _bufPos;
|
|
if (bytesFree < size) {
|
|
uint32 allocBytes = (size > 32 * 1024) ? size : 32 * 1024;
|
|
_bufSize += allocBytes;
|
|
_buf = (uint8*)realloc(_buf, _bufSize);
|
|
bytesFree = _bufSize - _bufPos;
|
|
}
|
|
memcpy(_buf + _bufPos, ptr, size);
|
|
_bufPos += size;
|
|
return size;
|
|
}
|
|
|
|
#define MAX_MC_ENTRIES 16
|
|
|
|
void runSaveThread(Ps2SaveFileManager *param);
|
|
|
|
Ps2SaveFileManager::Ps2SaveFileManager(OSystem_PS2 *system, Gs2dScreen *screen) {
|
|
_system = system;
|
|
_screen = screen;
|
|
_mc = new McAccess(0, 0);
|
|
|
|
_mcDirList = (mcTable*)memalign(64, MAX_MC_ENTRIES * sizeof(mcTable));
|
|
_mcDirName[0] = '\0';
|
|
_mcCheckTime = 0;
|
|
_mcNeedsUpdate = true;
|
|
|
|
for (int mcCheckCount = 0; mcCheckCount < 3; mcCheckCount++) {
|
|
/* retry mcGetInfo 3 times. It slows down startup without mc considerably,
|
|
but cheap 3rd party memory cards apparently fail to get detected once in a while */
|
|
|
|
int mcType, mcFree, mcFormat;
|
|
int res = _mc->getInfo(&mcType, &mcFree, &mcFormat);
|
|
|
|
if ((res == 0) || (res == -1)) { // mc okay
|
|
_mcPresent = true;
|
|
printf("MC okay, result = %d. Type %d, Free %d, Format %d\n", res, mcType, mcFree, mcFormat);
|
|
checkMainDirectory();
|
|
break;
|
|
} else {
|
|
_mcPresent = false;
|
|
printf("MC failed, not present or not formatted, code %d\n", res);
|
|
}
|
|
}
|
|
|
|
// create save thread
|
|
ee_sema_t newSema;
|
|
newSema.init_count = 0;
|
|
newSema.max_count = 1;
|
|
_autoSaveSignal = CreateSema(&newSema);
|
|
_autoSaveBuf = NULL;
|
|
_autoSaveSize = 0;
|
|
_systemQuit = false;
|
|
|
|
ee_thread_t saveThread, thisThread;
|
|
ReferThreadStatus(GetThreadId(), &thisThread);
|
|
|
|
saveThread.initial_priority = thisThread.current_priority + 1;
|
|
saveThread.stack_size = 8 * 1024;
|
|
_autoSaveStack = malloc(saveThread.stack_size);
|
|
saveThread.stack = _autoSaveStack;
|
|
saveThread.func = (void *)runSaveThread;
|
|
asm("move %0, $gp\n": "=r"(saveThread.gp_reg));
|
|
|
|
_autoSaveTid = CreateThread(&saveThread);
|
|
assert(_autoSaveTid >= 0);
|
|
StartThread(_autoSaveTid, this);
|
|
}
|
|
|
|
Ps2SaveFileManager::~Ps2SaveFileManager(void) {
|
|
}
|
|
|
|
void Ps2SaveFileManager::checkMainDirectory(void) {
|
|
// verify that the main directory (scummvm config + icon) exists
|
|
int ret, fd;
|
|
_mcNeedsUpdate = true;
|
|
ret = _mc->getDir("/ScummVM/*", 0, MAX_MC_ENTRIES, _mcDirList);
|
|
printf("/ScummVM/* res = %d\n", ret);
|
|
if (ret <= 0) { // assume directory doesn't exist
|
|
printf("Dir doesn't exist\n");
|
|
ret = _mc->mkDir("/ScummVM");
|
|
if (ret >= 0) {
|
|
fd = _mc->open("/ScummVM/scummvm.icn", O_WRONLY | O_CREAT);
|
|
if (fd >= 0) {
|
|
uint16 icoSize;
|
|
uint16 *icoBuf = decompressIconData(&icoSize);
|
|
ret = _mc->write(fd, icoBuf, icoSize * 2);
|
|
_mc->close(fd);
|
|
free(icoBuf);
|
|
|
|
printf(".icn written\n");
|
|
setupIcon("/ScummVM/icon.sys", "scummvm.icn", "ScummVM", "Configuration");
|
|
} else
|
|
printf("Can't create icon file: %d\n", fd);
|
|
} else
|
|
printf("can't create scummvm directory: %d\n", ret);
|
|
}
|
|
}
|
|
|
|
void Ps2SaveFileManager::splitPath(const char *fileName, char *dir, char *name) {
|
|
strcpy(dir, fileName);
|
|
char *ext = strchr(dir, '.');
|
|
if (ext) {
|
|
*ext = '\0';
|
|
ext++;
|
|
}
|
|
if (ext && *ext)
|
|
sprintf(name, "%s.ucl", ext);
|
|
else
|
|
strcpy(name, "save.ucl");
|
|
}
|
|
|
|
bool Ps2SaveFileManager::mcReadyForDir(const char *dir) {
|
|
if (_mcNeedsUpdate || ((_system->getMillis() - _mcCheckTime) > 2000) || !_mcPresent) {
|
|
// check if memory card was exchanged/removed in the meantime
|
|
int mcType, mcFree, mcFormat, mcResult;
|
|
mcResult = _mc->getInfo(&mcType, &mcFree, &mcFormat);
|
|
if (mcResult != 0) { // memory card was exchanged
|
|
_mcNeedsUpdate = true;
|
|
if (mcResult == -1) { // yes, it was exchanged
|
|
checkMainDirectory(); // make sure ScummVM dir and icon are there
|
|
} else { // no memorycard in slot or not formatted or something like that
|
|
_mcPresent = false;
|
|
printf("MC not found, error code %d\n", mcResult);
|
|
return false;
|
|
}
|
|
}
|
|
_mcPresent = true;
|
|
_mcCheckTime = _system->getMillis();
|
|
}
|
|
if (_mcNeedsUpdate || strcmp(_mcDirName, dir)) {
|
|
strcpy(_mcDirName, dir);
|
|
char dirStr[256];
|
|
sprintf(dirStr, "/ScummVM-%s/*", dir);
|
|
_mcEntries = _mc->getDir(dirStr, 0, MAX_MC_ENTRIES, _mcDirList);
|
|
_mcNeedsUpdate = false;
|
|
}
|
|
return (_mcEntries >= 0);
|
|
}
|
|
|
|
Common::InSaveFile *Ps2SaveFileManager::openForLoading(const char *filename) {
|
|
_screen->wantAnim(true);
|
|
|
|
char dir[256], name[256];
|
|
splitPath(filename, dir, name);
|
|
if (mcReadyForDir(dir)) {
|
|
bool fileExists = false;
|
|
for (int i = 0; i < _mcEntries; i++)
|
|
if (strcmp(name, (char*)_mcDirList[i].name) == 0)
|
|
fileExists = true;
|
|
if (fileExists) {
|
|
char fullName[256];
|
|
sprintf(fullName, "/ScummVM-%s/%s", dir, name);
|
|
UclInSaveFile *file = new UclInSaveFile(fullName, _screen, _mc);
|
|
if (file) {
|
|
if (!file->ioFailed())
|
|
return file;
|
|
else
|
|
delete file;
|
|
}
|
|
} else
|
|
printf("file %s (%s) doesn't exist\n", filename, name);
|
|
}
|
|
_screen->wantAnim(false);
|
|
return NULL;
|
|
}
|
|
|
|
Common::OutSaveFile *Ps2SaveFileManager::openForSaving(const char *filename) {
|
|
int res;
|
|
char dir[256], name[256];
|
|
|
|
_screen->wantAnim(true);
|
|
splitPath(filename, dir, name);
|
|
|
|
if (!mcReadyForDir(dir)) {
|
|
if (_mcPresent) { // directory doesn't seem to exist yet
|
|
char fullPath[256];
|
|
sprintf(fullPath, "/ScummVM-%s", dir);
|
|
res = _mc->mkDir(fullPath);
|
|
|
|
char icoSysDest[256], saveDesc[256];
|
|
sprintf(icoSysDest, "%s/icon.sys", fullPath);
|
|
strcpy(saveDesc, dir);
|
|
if ((saveDesc[0] >= 'a') && (saveDesc[0] <= 'z'))
|
|
saveDesc[0] += 'A' - 'a';
|
|
setupIcon(icoSysDest, "../ScummVM/scummvm.icn", saveDesc, "Savegames");
|
|
}
|
|
}
|
|
|
|
if (_mcPresent) {
|
|
char fullPath[256];
|
|
sprintf(fullPath, "/ScummVM-%s/%s", dir, name);
|
|
if (strstr(filename, ".s00") || strstr(filename, ".ASD") || strstr(filename, ".asd")) {
|
|
// this is an autosave
|
|
AutoSaveFile *file = new AutoSaveFile(this, fullPath);
|
|
return file;
|
|
} else {
|
|
UclOutSaveFile *file = new UclOutSaveFile(fullPath, _system, _screen, _mc);
|
|
if (!file->ioFailed()) {
|
|
// we're creating a file, mc will have to be updated next time
|
|
_mcNeedsUpdate = true;
|
|
return file;
|
|
} else
|
|
delete file;
|
|
}
|
|
}
|
|
|
|
_screen->wantAnim(false);
|
|
return NULL;
|
|
}
|
|
|
|
void Ps2SaveFileManager::listSavefiles(const char *prefix, bool *marks, int num) {
|
|
_screen->wantAnim(true);
|
|
|
|
int mcType, mcFree, mcFormat, mcResult;
|
|
mcResult = _mc->getInfo(&mcType, &mcFree, &mcFormat);
|
|
|
|
memset(marks, false, num * sizeof(bool));
|
|
|
|
if ((mcResult == 0) || (mcResult == -1)) {
|
|
// there's a memory card in the slot.
|
|
if (mcResult == -1)
|
|
_mcNeedsUpdate = true;
|
|
|
|
mcTable *mcEntries = (mcTable*)memalign(64, sizeof(mcTable) * MAX_MC_ENTRIES);
|
|
|
|
char dirStr[256], ext[256], mcSearchStr[256];
|
|
strcpy(dirStr, prefix);
|
|
char *pos = strchr(dirStr, '.');
|
|
if (pos) {
|
|
strcpy(ext, pos + 1);
|
|
*pos = '\0';
|
|
} else
|
|
ext[0] = '\0';
|
|
sprintf(mcSearchStr, "/ScummVM-%s/%s*", dirStr, ext);
|
|
|
|
int numEntries = _mc->getDir(mcSearchStr, 0, MAX_MC_ENTRIES, mcEntries);
|
|
|
|
int searchLen = strlen(ext);
|
|
for (int i = 0; i < numEntries; i++)
|
|
if ((((char*)mcEntries[i].name)[0] != '.') && stricmp((char*)mcEntries[i].name, "icon.sys")) {
|
|
char *stopCh;
|
|
int destNum = (int)strtoul((char*)mcEntries[i].name + searchLen, &stopCh, 10);
|
|
if ((!stopCh) || strcmp(stopCh, ".ucl"))
|
|
printf("unexpected end %s in name %s, search %s\n", stopCh, (char*)mcEntries[i].name, prefix);
|
|
if (destNum < num)
|
|
marks[destNum] = true;
|
|
}
|
|
free(mcEntries);
|
|
}
|
|
_screen->wantAnim(false);
|
|
}
|
|
|
|
const char *Ps2SaveFileManager::getSavePath(void) const {
|
|
return "mc0:";
|
|
}
|
|
|
|
bool Ps2SaveFileManager::setupIcon(const char *dest, const char *ico, const char *descr1, const char *descr2) {
|
|
mcIcon icon_sys;
|
|
memset(&icon_sys, 0, sizeof(mcIcon));
|
|
memcpy(icon_sys.head, "PS2D", 4);
|
|
char title[256];
|
|
if (!stricmp("SAVEGAME", descr1)) { // these are broken sword 1 savegames
|
|
sprintf(title, "BSword1\n%s", descr2);
|
|
icon_sys.nlOffset = 8;
|
|
} else {
|
|
sprintf(title, "%s\n%s", descr1, descr2);
|
|
icon_sys.nlOffset = strlen(descr1) + 1;
|
|
}
|
|
strcpy_sjis((short*)&(icon_sys.title), title);
|
|
icon_sys.trans = 0x10;
|
|
memcpy(icon_sys.bgCol, _bgcolor, sizeof(_bgcolor));
|
|
memcpy(icon_sys.lightDir, _lightdir, sizeof(_lightdir));
|
|
memcpy(icon_sys.lightCol, _lightcol, sizeof(_lightcol));
|
|
memcpy(icon_sys.lightAmbient, _ambient, sizeof(_ambient));
|
|
strcpy((char*)icon_sys.view, ico);
|
|
strcpy((char*)icon_sys.copy, ico);
|
|
strcpy((char*)icon_sys.del, ico);
|
|
|
|
int fd, res;
|
|
fd = _mc->open(dest, O_WRONLY | O_CREAT);
|
|
if (fd >= 0) {
|
|
res = _mc->write(fd, &icon_sys, sizeof(icon_sys));
|
|
_mc->close(fd);
|
|
return (res == sizeof(icon_sys));
|
|
} else
|
|
return false;
|
|
}
|
|
|
|
uint16 *Ps2SaveFileManager::decompressIconData(uint16 *size) {
|
|
uint16 inPos = 1;
|
|
uint16 *rleData = (uint16*)_rleIcoData;
|
|
uint16 resSize = rleData[0];
|
|
uint16 *resData = (uint16*)malloc(resSize * sizeof(uint16));
|
|
uint16 outPos = 0;
|
|
while (outPos < resSize) {
|
|
uint16 len = rleData[inPos++];
|
|
while (len--)
|
|
resData[outPos++] = 0x7FFF;
|
|
len = rleData[inPos++];
|
|
while (len--)
|
|
resData[outPos++] = rleData[inPos++];
|
|
}
|
|
*size = resSize;
|
|
assert(outPos == resSize);
|
|
return resData;
|
|
}
|
|
|
|
void runSaveThread(Ps2SaveFileManager *param) {
|
|
param->saveThread();
|
|
}
|
|
|
|
void Ps2SaveFileManager::writeSaveNonblocking(char *name, void *buf, uint32 size) {
|
|
if (buf && size && !_systemQuit) {
|
|
strcpy(_autoSaveName, name);
|
|
assert(!_autoSaveBuf);
|
|
_autoSaveBuf = (uint8*)malloc(size);
|
|
memcpy(_autoSaveBuf, buf, size);
|
|
_autoSaveSize = size;
|
|
SignalSema(_autoSaveSignal);
|
|
}
|
|
}
|
|
|
|
void Ps2SaveFileManager::saveThread(void) {
|
|
while (!_systemQuit) {
|
|
WaitSema(_autoSaveSignal);
|
|
if (_autoSaveBuf && _autoSaveSize) {
|
|
UclOutSaveFile *outSave = new UclOutSaveFile(_autoSaveName, _system, _screen, _mc);
|
|
if (!outSave->ioFailed()) {
|
|
outSave->write(_autoSaveBuf, _autoSaveSize);
|
|
outSave->flush();
|
|
}
|
|
if (outSave->ioFailed())
|
|
_system->msgPrintf(5000, "Writing autosave to %s failed", _autoSaveName);
|
|
delete outSave;
|
|
free(_autoSaveBuf);
|
|
_autoSaveBuf = NULL;
|
|
_autoSaveSize = 0;
|
|
_mcNeedsUpdate = true; // we've created a file, mc will have to be updated
|
|
_screen->wantAnim(false);
|
|
}
|
|
}
|
|
ExitThread();
|
|
}
|
|
|
|
void Ps2SaveFileManager::quit(void) {
|
|
_systemQuit = true;
|
|
ee_thread_t statSave, statThis;
|
|
ReferThreadStatus(GetThreadId(), &statThis);
|
|
int res = ChangeThreadPriority(_autoSaveTid, statThis.current_priority - 1);
|
|
sioprintf("SaveThread prio res: %d", res);
|
|
|
|
do { // wait until thread called ExitThread()
|
|
SignalSema(_autoSaveSignal);
|
|
ReferThreadStatus(_autoSaveTid, &statSave);
|
|
} while (statSave.status != 0x10);
|
|
sioprintf("wait done");
|
|
DeleteThread(_autoSaveTid);
|
|
free(_autoSaveStack);
|
|
}
|
|
|
|
UclInSaveFile::UclInSaveFile(const char *filename, Gs2dScreen *screen, McAccess *mc) {
|
|
_screen = screen;
|
|
_mc = mc;
|
|
int fd = _mc->open(filename, O_RDONLY);
|
|
_buf = NULL;
|
|
_bufSize = _bufPos = 0;
|
|
_ioFailed = false;
|
|
|
|
if (fd >= 0) {
|
|
int srcSize = _mc->size(fd);
|
|
if (srcSize > 8) {
|
|
int res;
|
|
uint8 *tmpBuf = (uint8*)memalign(64, srcSize);
|
|
res = _mc->read(fd, tmpBuf, srcSize);
|
|
if ((res == srcSize) && (*(uint32*)tmpBuf == UCL_MAGIC)) {
|
|
uint32 resLen = _bufSize = *(uint32*)(tmpBuf + 4);
|
|
_buf = (uint8*)malloc(_bufSize + 2048);
|
|
res = ucl_nrv2e_decompress_8(tmpBuf + 8, srcSize - 8, _buf, &resLen, NULL);
|
|
if ((res < 0) || (resLen != _bufSize)) {
|
|
printf("Unable to decompress file %s (%d -> %d) error code %d\n", filename, srcSize, _bufSize, res);
|
|
free(_buf);
|
|
_buf = NULL;
|
|
_bufSize = 0;
|
|
}
|
|
}
|
|
free(tmpBuf);
|
|
}
|
|
_mc->close(fd);
|
|
}
|
|
if (!_buf) {
|
|
printf("Invalid savegame %s\n", filename);
|
|
_ioFailed = true;
|
|
}
|
|
}
|
|
|
|
UclInSaveFile::~UclInSaveFile(void) {
|
|
if (_buf)
|
|
free(_buf);
|
|
_screen->wantAnim(false);
|
|
}
|
|
|
|
bool UclInSaveFile::ioFailed(void) {
|
|
return _ioFailed;
|
|
}
|
|
|
|
void UclInSaveFile::clearIOFailed(void) {
|
|
_ioFailed = false;
|
|
}
|
|
|
|
bool UclInSaveFile::eos(void) const {
|
|
return _bufPos == _bufSize;
|
|
}
|
|
|
|
uint32 UclInSaveFile::read(void *ptr, uint32 size) {
|
|
if (_buf) {
|
|
uint32 bytesRemain = _bufSize - _bufPos;
|
|
if (size > bytesRemain) {
|
|
size = bytesRemain;
|
|
_ioFailed = true;
|
|
}
|
|
memcpy(ptr, _buf + _bufPos, size);
|
|
_bufPos += size;
|
|
return size;
|
|
} else {
|
|
_ioFailed = true;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
void UclInSaveFile::skip(uint32 offset) {
|
|
if (_buf) {
|
|
if (_bufPos + offset <= _bufSize)
|
|
_bufPos += offset;
|
|
else
|
|
_bufPos = _bufSize;
|
|
}
|
|
}
|
|
|
|
UclOutSaveFile::UclOutSaveFile(const char *filename, OSystem_PS2 *system, Gs2dScreen *screen, McAccess *mc) {
|
|
_screen = screen;
|
|
_system = system;
|
|
_mc = mc;
|
|
_bufPos = 0;
|
|
_fd = _mc->open(filename, O_WRONLY | O_CREAT);
|
|
if (_fd >= 0) {
|
|
_bufSize = 65536;
|
|
_buf = (uint8*)malloc(_bufSize);
|
|
_ioFailed = false;
|
|
} else {
|
|
_ioFailed = true;
|
|
_bufSize = 0;
|
|
_buf = NULL;
|
|
}
|
|
_wasFlushed = false;
|
|
}
|
|
|
|
UclOutSaveFile::~UclOutSaveFile(void) {
|
|
if (_buf) {
|
|
if (_bufPos) {
|
|
printf("Engine didn't call SaveFile::flush()\n");
|
|
flush();
|
|
if (ioFailed()) {
|
|
// unable to save to memory card and it's too late to return an error code to the engine
|
|
_system->msgPrintf(5000, "!WARNING!\nCan't write to memory card.\nGame was NOT saved.");
|
|
printf("~UclOutSaveFile: Flush failed!\n");
|
|
}
|
|
}
|
|
free(_buf);
|
|
}
|
|
if (_fd >= 0)
|
|
_mc->close(_fd);
|
|
_screen->wantAnim(false);
|
|
}
|
|
|
|
bool UclOutSaveFile::ioFailed(void) {
|
|
return _ioFailed;
|
|
}
|
|
|
|
void UclOutSaveFile::clearIOFailed(void) {
|
|
_ioFailed = false;
|
|
}
|
|
|
|
void UclOutSaveFile::flush(void) {
|
|
int res = 0;
|
|
|
|
if (_bufPos) {
|
|
if (_wasFlushed) {
|
|
// the engine flushed this file and afterwards wrote more data.
|
|
// this is unsupported because it results in savefiles that consist
|
|
// of two or more compressed segments.
|
|
printf("Error: 2nd call to UclOutSaveFile::flush!\n");
|
|
res = -1;
|
|
} else {
|
|
uint32 compSize = _bufPos * 2;
|
|
uint8 *compBuf = (uint8*)memalign(64, compSize + 8);
|
|
*(uint32*)(compBuf + 0) = UCL_MAGIC;
|
|
*(uint32*)(compBuf + 4) = _bufPos; // uncompressed size
|
|
res = ucl_nrv2e_99_compress(_buf, _bufPos, compBuf + 8, &compSize, NULL, 10, NULL, NULL);
|
|
if (res >= 0) {
|
|
res = _mc->write(_fd, compBuf, compSize + 8);
|
|
if (res != (int)compSize + 8)
|
|
res = -1;
|
|
} else
|
|
printf("Unable to compress %d bytes of savedata, errorcode %d\n", _bufPos, res);
|
|
free(compBuf);
|
|
_bufPos = 0;
|
|
}
|
|
}
|
|
if (res < 0) {
|
|
_ioFailed = true;
|
|
printf("UclOutSaveFile::flush failed!\n");
|
|
}
|
|
}
|
|
|
|
uint32 UclOutSaveFile::write(const void *ptr, uint32 size) {
|
|
assert(_bufPos <= _bufSize);
|
|
uint32 bytesFree = _bufSize - _bufPos;
|
|
if (bytesFree < size) {
|
|
uint32 allocBytes = (size > 32 * 1024) ? size : 32 * 1024;
|
|
_bufSize += allocBytes;
|
|
_buf = (uint8*)realloc(_buf, _bufSize);
|
|
bytesFree = _bufSize - _bufPos;
|
|
}
|
|
assert(bytesFree >= size);
|
|
memcpy(_buf + _bufPos, ptr, size);
|
|
_bufPos += size;
|
|
return size;
|
|
}
|
|
|
|
|