diff options
Diffstat (limited to 'backends/platform/ps2/savefilemgr.cpp')
-rw-r--r-- | backends/platform/ps2/savefilemgr.cpp | 572 |
1 files changed, 572 insertions, 0 deletions
diff --git a/backends/platform/ps2/savefilemgr.cpp b/backends/platform/ps2/savefilemgr.cpp new file mode 100644 index 0000000000..8fbd98e78a --- /dev/null +++ b/backends/platform/ps2/savefilemgr.cpp @@ -0,0 +1,572 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * 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. + * + * $URL$ + * $Id$ + * + */ + +#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/platform/ps2/savefile.h" +#include "backends/platform/ps2/Gs2dScreen.h" +#include "backends/platform/ps2/systemps2.h" +#include "backends/fs/abstract-fs.h" +#include "backends/platform/ps2/ps2debug.h" + +#include "common/scummsys.h" + +#include "backends/platform/ps2/savefilemgr.h" +#include "backends/platform/ps2/savefile.h" + +extern void *_gp; + +#define PORT 0 +#define SLOT 0 +// port 0, slot 0: memory card in first 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::remove(const char *name) { + int res; + WaitSema(_sema); + mcDelete(_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; +} + +#define MAX_MC_ENTRIES 16 + +void runSaveThread(Ps2SaveFileManager *param); + +Ps2SaveFileManager::Ps2SaveFileManager(OSystem_PS2 *system, Gs2dScreen *screen) { + _system = system; + _screen = screen; + _mc = new McAccess(PORT, SLOT); + + _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; + saveThread.gp_reg = &_gp; + + _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); + printf("openForLoading: \"%s\" => \"%s\" + \"%s\"\n", filename, dir, name); + if (mcReadyForDir(dir)) { + printf("Ready\n"); + bool fileExists = false; + for (int i = 0; i < _mcEntries; i++) + if (strcmp(name, (char*)_mcDirList[i].name) == 0) + fileExists = true; + if (fileExists) { + printf("Found!\n"); + 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 { + printf("IoFailed\n"); + 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 { + printf("UCL out create failed!\n"); + 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); +} + +Common::StringList Ps2SaveFileManager::listSavefiles(const char *regex) { + _screen->wantAnim(true); + Common::StringList results; + int mcType, mcFree, mcFormat, mcResult; + + printf("listSavefiles -> regex=%s\n", regex); + + mcResult = _mc->getInfo(&mcType, &mcFree, &mcFormat); + + 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 temp[256], mcSearchStr[256], *dir, *ext; + strcpy(temp, regex); + dir = strdup(strtok(temp, ".")); + ext = strdup(strtok(NULL, "*")); + + printf("dir = %s - ext = %s\n", dir, ext); + + sprintf(mcSearchStr, "/ScummVM-%s/%s*", dir, ext); + + int numEntries = _mc->getDir(mcSearchStr, 0, MAX_MC_ENTRIES, mcEntries); + char *name; + for (int i = 0; i < numEntries; i++) { + name = (char*)mcEntries[i].name; + + if ((name[0] != '.') && stricmp(name, "icon.sys")) { + printf(" name = %s\n", (char*)mcEntries[i].name); + if (Common::matchString(name, "s*.ucl")) { + sprintf(temp, "%s.%s%c%c", dir, ext, name[1], name[2]); + results.push_back(temp); + printf(" -> match [%s] ;-)\n", temp); + } + else { + results.push_back(name); // ;-) + printf(" -> no match :-(\n"); + } + } + } + free(mcEntries); + free(dir); + free(ext); + } + + _screen->wantAnim(false); + + return results; +} + +bool Ps2SaveFileManager::removeSavefile(const char *filename) { + + char dir[64], name[64], fullPath[128]; + + splitPath(filename, dir, name); + sprintf(fullPath, "/ScummVM-%s/%s", dir, name); + + int res = _mc->remove(fullPath); + return (res == 0); +} + +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); + ChangeThreadPriority(_autoSaveTid, statThis.current_priority - 1); + + do { // wait until thread called ExitThread() + SignalSema(_autoSaveSignal); + ReferThreadStatus(_autoSaveTid, &statSave); + } while (statSave.status != 0x10); + + DeleteThread(_autoSaveTid); + free(_autoSaveStack); +} + +McAccess *Ps2SaveFileManager::getMcAccess(void) { + return _mc; +} + + |