/* 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 #include #include #include #include #include #include #include #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; }