GBA Thread: Redo state directories for future expansion
@@ -0,0 +1,82 @@
+/* Copyright (c) 2013-2015 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "directories.h" + +#include "util/vfs.h" + +void GBADirectorySetInit(struct GBADirectorySet* dirs) { + dirs->base = 0; + dirs->archive = 0; + dirs->save = 0; + dirs->patch = 0; + dirs->state = 0; + dirs->screenshot = 0; +} + +void GBADirectorySetDeinit(struct GBADirectorySet* dirs) { + GBADirectorySetDetachBase(dirs); + + if (dirs->archive) { + dirs->archive->close(dirs->archive); + dirs->archive = 0; + } + + if (dirs->save) { + dirs->save->close(dirs->save); + dirs->save = 0; + } + + if (dirs->patch) { + dirs->patch->close(dirs->patch); + dirs->patch = 0; + } + + if (dirs->state) { + dirs->state->close(dirs->state); + dirs->state = 0; + } + + if (dirs->screenshot) { + dirs->screenshot->close(dirs->screenshot); + dirs->screenshot = 0; + } +} + +void GBADirectorySetAttachBase(struct GBADirectorySet* dirs, struct VDir* base) { + dirs->base = base; + if (!dirs->save) { + dirs->save = dirs->base; + } + if (!dirs->patch) { + dirs->patch = dirs->base; + } + if (!dirs->state) { + dirs->state = dirs->base; + } + if (!dirs->screenshot) { + dirs->screenshot = dirs->base; + } +} + +void GBADirectorySetDetachBase(struct GBADirectorySet* dirs) { + if (dirs->save == dirs->base) { + dirs->save = 0; + } + if (dirs->patch == dirs->base) { + dirs->patch = 0; + } + if (dirs->state == dirs->base) { + dirs->state = 0; + } + if (dirs->screenshot == dirs->base) { + dirs->screenshot = 0; + } + + if (dirs->base) { + dirs->base->close(dirs->base); + dirs->base = 0; + } +}
@@ -0,0 +1,28 @@
+/* Copyright (c) 2013-2015 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef DIRECTORIES_H +#define DIRECTORIES_H + +#include "util/common.h" + +struct VDir; + +struct GBADirectorySet { + struct VDir* base; + struct VDir* archive; + struct VDir* save; + struct VDir* patch; + struct VDir* state; + struct VDir* screenshot; +}; + +void GBADirectorySetInit(struct GBADirectorySet* dirs); +void GBADirectorySetDeinit(struct GBADirectorySet* dirs); + +void GBADirectorySetAttachBase(struct GBADirectorySet* dirs, struct VDir* base); +void GBADirectorySetDetachBase(struct GBADirectorySet* dirs); + +#endif
@@ -198,9 +198,11 @@ return true;
} struct VFile* GBAGetState(struct GBA* gba, struct VDir* dir, int slot, bool write) { - char suffix[5] = { '\0' }; - snprintf(suffix, sizeof(suffix), ".ss%d", slot); - return VDirOptionalOpenFile(dir, gba->activeFile, "savestate", suffix, write ? (O_CREAT | O_TRUNC | O_RDWR) : O_RDONLY); + char basename[PATH_MAX]; + separatePath(gba->activeFile, 0, basename, 0); + char path[PATH_MAX]; + snprintf(path, sizeof(path), "%s.ss%i", basename, slot); + return dir->openFile(dir, path, write ? (O_CREAT | O_TRUNC | O_RDWR) : O_RDONLY); } #ifdef USE_PNG@@ -709,7 +711,9 @@ void GBATakeScreenshot(struct GBA* gba, struct VDir* dir) {
#ifdef USE_PNG unsigned stride; const void* pixels = 0; - struct VFile* vf = VDirOptionalOpenIncrementFile(dir, gba->activeFile, "screenshot", "-", ".png", O_CREAT | O_TRUNC | O_WRONLY); + char basename[PATH_MAX]; + separatePath(gba->activeFile, 0, basename, 0); + struct VFile* vf = VDirFindNextAvailable(dir, basename, "-", ".png", O_CREAT | O_TRUNC | O_WRONLY); bool success = false; if (vf) { gba->video.renderer->getPixels(gba->video.renderer, &stride, &pixels);
@@ -106,7 +106,7 @@ }
struct GBACLIDebugger* gbaDebugger = (struct GBACLIDebugger*) debugger->system; - GBALoadState(gbaDebugger->context, gbaDebugger->context->stateDir, dv->intValue, SAVESTATE_SCREENSHOT); + GBALoadState(gbaDebugger->context, gbaDebugger->context->dirs.state, dv->intValue, SAVESTATE_SCREENSHOT); } static void _rewind(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {@@ -133,6 +133,6 @@ }
struct GBACLIDebugger* gbaDebugger = (struct GBACLIDebugger*) debugger->system; - GBASaveState(gbaDebugger->context, gbaDebugger->context->stateDir, dv->intValue, SAVESTATE_SCREENSHOT); + GBASaveState(gbaDebugger->context, gbaDebugger->context->dirs.state, dv->intValue, SAVESTATE_SCREENSHOT); } #endif
@@ -22,9 +22,38 @@ #include "platform/commandline.h"
#include <signal.h> -static void _loadGameDir(struct GBAThread* threadContext); +static const float _defaultFPSTarget = 60.f; + +bool _reloadDirectories(struct GBAThread* threadContext) { + GBADirectorySetDetachBase(&threadContext->dirs); + + char basename[PATH_MAX]; + if (threadContext->fname) { + char dirname[PATH_MAX]; + separatePath(threadContext->fname, dirname, basename, 0); + GBADirectorySetAttachBase(&threadContext->dirs, VDirOpen(dirname)); + } else { + return false; + } -static const float _defaultFPSTarget = 60.f; + char path[PATH_MAX]; + snprintf(path, sizeof(path), "%s.sav", basename); + threadContext->save = threadContext->dirs.save->openFile(threadContext->dirs.save, path, O_CREAT | O_RDWR); + + if (!threadContext->patch) { + snprintf(path, sizeof(path), "%s.ups", basename); + threadContext->patch = threadContext->dirs.patch->openFile(threadContext->dirs.patch, path, O_RDONLY); + } + if (!threadContext->patch) { + snprintf(path, sizeof(path), "%s.ips", basename); + threadContext->patch = threadContext->dirs.patch->openFile(threadContext->dirs.patch, path, O_RDONLY); + } + if (!threadContext->patch) { + snprintf(path, sizeof(path), "%s.bps", basename); + threadContext->patch = threadContext->dirs.patch->openFile(threadContext->dirs.patch, path, O_RDONLY); + } + return true; +} #ifndef DISABLE_THREADING #ifdef USE_PTHREADS@@ -196,11 +225,9 @@ }
if (threadContext->movie) { struct VDir* movieDir = VDirOpen(threadContext->movie); -#ifdef USE_LIBZIP if (!movieDir) { - movieDir = VDirOpenZip(threadContext->movie, 0); + movieDir = VDirOpenArchive(threadContext->movie); } -#endif if (movieDir) { struct GBAMGMContext* mgm = malloc(sizeof(*mgm)); GBAMGMContextCreate(mgm);@@ -376,12 +403,7 @@ threadContext->idleOptimization = opts->idleOptimization;
} void GBAMapArgumentsToContext(const struct GBAArguments* args, struct GBAThread* threadContext) { - if (args->dirmode) { - threadContext->gameDir = VDirOpen(args->fname); - threadContext->stateDir = threadContext->gameDir; - } else { - GBAThreadLoadROM(threadContext, args->fname); - } + GBAThreadLoadROM(threadContext, args->fname); threadContext->fname = args->fname; threadContext->patch = VFileOpen(args->patch, O_RDONLY); threadContext->cheatsFile = VFileOpen(args->cheatsFile, O_RDONLY);@@ -413,26 +435,13 @@ threadContext->rom->close(threadContext->rom);
threadContext->rom = 0; } - if (threadContext->gameDir) { - _loadGameDir(threadContext); - } - if (!threadContext->rom && !bootBios) { threadContext->state = THREAD_SHUTDOWN; return false; } - threadContext->save = VDirOptionalOpenFile(threadContext->stateDir, threadContext->fname, "sram", ".sav", O_CREAT | O_RDWR); - - if (!threadContext->patch) { - threadContext->patch = VDirOptionalOpenFile(threadContext->stateDir, threadContext->fname, "patch", ".ups", O_RDONLY); - } - if (!threadContext->patch) { - threadContext->patch = VDirOptionalOpenFile(threadContext->stateDir, threadContext->fname, "patch", ".ips", O_RDONLY); - } - if (!threadContext->patch) { - threadContext->patch = VDirOptionalOpenFile(threadContext->stateDir, threadContext->fname, "patch", ".bps", O_RDONLY); - } + GBADirectorySetInit(&threadContext->dirs); + _reloadDirectories(threadContext); MutexInit(&threadContext->stateMutex); ConditionInit(&threadContext->stateCond);@@ -562,18 +571,7 @@ threadContext->patch->close(threadContext->patch);
threadContext->patch = 0; } - if (threadContext->gameDir) { - if (threadContext->stateDir == threadContext->gameDir) { - threadContext->stateDir = 0; - } - threadContext->gameDir->close(threadContext->gameDir); - threadContext->gameDir = 0; - } - - if (threadContext->stateDir) { - threadContext->stateDir->close(threadContext->stateDir); - threadContext->stateDir = 0; - } + GBADirectorySetDeinit(&threadContext->dirs); } bool GBAThreadIsActive(struct GBAThread* threadContext) {@@ -687,31 +685,11 @@ GBASyncSetVideoSync(&threadContext->sync, frameOn);
} void GBAThreadLoadROM(struct GBAThread* threadContext, const char* fname) { - threadContext->rom = VFileOpen(fname, O_RDONLY); - threadContext->gameDir = 0; - if (!threadContext->gameDir) { - threadContext->gameDir = VDirOpenArchive(fname); - } -} - -static void _loadGameDir(struct GBAThread* threadContext) { - threadContext->gameDir->rewind(threadContext->gameDir); - struct VDirEntry* dirent = threadContext->gameDir->listNext(threadContext->gameDir); - while (dirent) { - struct Patch patchTemp; - struct VFile* vf = threadContext->gameDir->openFile(threadContext->gameDir, dirent->name(dirent), O_RDONLY); - if (!vf) { - dirent = threadContext->gameDir->listNext(threadContext->gameDir); - continue; - } - if (!threadContext->rom && GBAIsROM(vf)) { - threadContext->rom = vf; - } else if (!threadContext->patch && loadPatch(vf, &patchTemp)) { - threadContext->patch = vf; - } else { - vf->close(vf); - } - dirent = threadContext->gameDir->listNext(threadContext->gameDir); + threadContext->dirs.archive = VDirOpenArchive(fname); + if (threadContext->dirs.archive) { + threadContext->rom = VDirFindFirst(threadContext->dirs.archive, GBAIsROM); + } else { + threadContext->rom = VFileOpen(fname, O_RDONLY); } }@@ -728,13 +706,15 @@ threadContext->save->close(threadContext->save);
threadContext->save = 0; } - GBAThreadLoadROM(threadContext, fname); - if (threadContext->gameDir) { - _loadGameDir(threadContext); + if (threadContext->dirs.archive) { + threadContext->dirs.archive->close(threadContext->dirs.archive); + threadContext->dirs.archive = 0; } + + GBAThreadLoadROM(threadContext, fname); threadContext->fname = fname; - threadContext->save = VDirOptionalOpenFile(threadContext->stateDir, threadContext->fname, "sram", ".sav", O_CREAT | O_RDWR); + _reloadDirectories(threadContext); GBARaiseIRQ(threadContext->gba, IRQ_GAMEPAK); GBALoadROM(threadContext->gba, threadContext->rom, threadContext->save, threadContext->fname);@@ -753,7 +733,7 @@ }
#endif void GBAThreadTakeScreenshot(struct GBAThread* threadContext) { - GBATakeScreenshot(threadContext->gba, threadContext->stateDir); + GBATakeScreenshot(threadContext->gba, threadContext->dirs.screenshot); } #else
@@ -10,6 +10,7 @@ #include "util/common.h"
#include "gba/gba.h" #include "gba/input.h" +#include "gba/context/directories.h" #include "gba/context/overrides.h" #include "gba/context/sync.h"@@ -47,8 +48,7 @@ // Input
struct GBAVideoRenderer* renderer; struct GBASIODriverSet sioDrivers; struct ARMDebugger* debugger; - struct VDir* gameDir; - struct VDir* stateDir; + struct GBADirectorySet dirs; struct VFile* rom; struct VFile* save; struct VFile* bios;
@@ -110,8 +110,8 @@ context->gba->video.renderer->disableBG[3] = !controller->m_videoLayers[3];
context->gba->video.renderer->disableOBJ = !controller->m_videoLayers[4]; controller->m_fpsTarget = context->fpsTarget; - if (GBALoadState(context, context->stateDir, 0, SAVESTATE_SCREENSHOT)) { - VFile* vf = GBAGetState(context->gba, context->stateDir, 0, true); + if (GBALoadState(context, context->dirs.state, 0, SAVESTATE_SCREENSHOT)) { + VFile* vf = GBAGetState(context->gba, context->dirs.state, 0, true); if (vf) { vf->truncate(vf, 0); }@@ -139,7 +139,7 @@ if (!context) {
return false; } GameController* controller = static_cast<GameController*>(context->userData); - if (!GBASaveState(context, context->stateDir, 0, true)) { + if (!GBASaveState(context, context->dirs.state, 0, true)) { return false; } QMetaObject::invokeMethod(controller, "closeGame");@@ -317,18 +317,12 @@ m_threadContext.sync.videoFrameWait = m_videoSync;
m_threadContext.sync.audioWait = m_audioSync; } - m_threadContext.gameDir = 0; m_threadContext.bootBios = biosOnly; if (biosOnly) { m_threadContext.fname = nullptr; } else { m_threadContext.fname = strdup(m_fname.toUtf8().constData()); - if (m_dirmode) { - m_threadContext.gameDir = VDirOpen(m_threadContext.fname); - m_threadContext.stateDir = m_threadContext.gameDir; - } else { - GBAThreadLoadROM(&m_threadContext, m_threadContext.fname); - } + GBAThreadLoadROM(&m_threadContext, m_threadContext.fname); } if (!m_bios.isNull() && m_useBios) {@@ -712,7 +706,7 @@ if (!controller->m_backupLoadState) {
controller->m_backupLoadState = new GBASerializedState; } GBASerialize(context->gba, controller->m_backupLoadState); - if (GBALoadState(context, context->stateDir, controller->m_stateSlot, SAVESTATE_SCREENSHOT)) { + if (GBALoadState(context, context->dirs.state, controller->m_stateSlot, SAVESTATE_SCREENSHOT)) { controller->frameAvailable(controller->m_drawContext); controller->stateLoaded(context); }@@ -725,13 +719,13 @@ m_stateSlot = slot;
} GBARunOnThread(&m_threadContext, [](GBAThread* context) { GameController* controller = static_cast<GameController*>(context->userData); - VFile* vf = GBAGetState(context->gba, context->stateDir, controller->m_stateSlot, false); + VFile* vf = GBAGetState(context->gba, context->dirs.state, controller->m_stateSlot, false); if (vf) { controller->m_backupSaveState.resize(vf->size(vf)); vf->read(vf, controller->m_backupSaveState.data(), controller->m_backupSaveState.size()); vf->close(vf); } - GBASaveState(context, context->stateDir, controller->m_stateSlot, SAVESTATE_SCREENSHOT | EXTDATA_SAVEDATA); + GBASaveState(context, context->dirs.state, controller->m_stateSlot, SAVESTATE_SCREENSHOT | EXTDATA_SAVEDATA); }); }@@ -759,7 +753,7 @@ }
GBARunOnThread(&m_threadContext, [](GBAThread* context) { GameController* controller = static_cast<GameController*>(context->userData); - VFile* vf = GBAGetState(context->gba, context->stateDir, controller->m_stateSlot, true); + VFile* vf = GBAGetState(context->gba, context->dirs.state, controller->m_stateSlot, true); if (vf) { vf->write(vf, controller->m_backupSaveState.constData(), controller->m_backupSaveState.size()); vf->close(vf);
@@ -170,7 +170,7 @@ }
void LoadSaveState::loadState(int slot) { GBAThread* thread = m_controller->thread(); - VFile* vf = GBAGetState(thread->gba, thread->stateDir, slot, false); + VFile* vf = GBAGetState(thread->gba, thread->dirs.state, slot, false); if (!vf) { m_slots[slot - 1]->setText(tr("Empty")); return;
@@ -423,7 +423,7 @@ case SDLK_F7:
case SDLK_F8: case SDLK_F9: GBAThreadInterrupt(context); - GBASaveState(context, context->stateDir, event->keysym.sym - SDLK_F1 + 1, SAVESTATE_SCREENSHOT); + GBASaveState(context, context->dirs.state, event->keysym.sym - SDLK_F1 + 1, SAVESTATE_SCREENSHOT); GBAThreadContinue(context); break; default:@@ -441,7 +441,7 @@ case SDLK_F7:
case SDLK_F8: case SDLK_F9: GBAThreadInterrupt(context); - GBALoadState(context, context->stateDir, event->keysym.sym - SDLK_F1 + 1, SAVESTATE_SCREENSHOT); + GBALoadState(context, context->dirs.state, event->keysym.sym - SDLK_F1 + 1, SAVESTATE_SCREENSHOT); GBAThreadContinue(context); break; default:
@@ -157,6 +157,52 @@ }
return r; } +void separatePath(const char* path, char* dirname, char* basename, char* extension) { + if (!path) { + return; + } + char* dotPoint = strrchr(path, '.'); + char* separatorPoint = strnrstr(path, PATH_SEP, strlen(path)); + if (separatorPoint) { + if (dirname) { + ptrdiff_t len = separatorPoint - path; + if (PATH_MAX <= len) { + len = PATH_MAX - 1; + } + strncpy(dirname, path, len); + dirname[len] = '\0'; + } + path = separatorPoint + 1; + + } + if (basename) { + size_t len; + if (dotPoint) { + len = dotPoint - path; + } else { + len = strlen(path); + } + if (PATH_MAX <= len) { + len = PATH_MAX - 1; + } + strncpy(basename, path, len); + basename[len] = '\0'; + } + if (extension) { + if (dotPoint) { + ++dotPoint; + size_t len = strlen(dotPoint); + if (PATH_MAX <= len) { + len = PATH_MAX - 1; + } + strncpy(extension, dotPoint, len); + extension[len] = '\0'; + } else { + extension[0] = '\0'; + } + } +} + struct VFile* VDirOptionalOpenFile(struct VDir* dir, const char* realPath, const char* prefix, const char* suffix, int mode) { char path[PATH_MAX]; path[PATH_MAX - 1] = '\0';@@ -185,64 +231,37 @@ }
return vf; } -struct VFile* VDirOptionalOpenIncrementFile(struct VDir* dir, const char* realPath, const char* prefix, const char* infix, const char* suffix, int mode) { - char path[PATH_MAX]; - path[PATH_MAX - 1] = '\0'; - char realPrefix[PATH_MAX]; - realPrefix[PATH_MAX - 1] = '\0'; - if (!dir) { - if (!realPath) { - return 0; +struct VFile* VDirFindFirst(struct VDir* dir, bool (*filter)(struct VFile*)) { + dir->rewind(dir); + struct VDirEntry* dirent = dir->listNext(dir); + while (dirent) { + struct VFile* vf = dir->openFile(dir, dirent->name(dirent), O_RDONLY); + if (!vf) { + dirent = dir->listNext(dir); + continue; } - const char* separatorPoint = strrchr(realPath, '/'); - const char* dotPoint; - size_t len; - if (!separatorPoint) { - strcpy(path, "./"); - separatorPoint = realPath; - dotPoint = strrchr(realPath, '.'); - } else { - path[0] = '\0'; - dotPoint = strrchr(separatorPoint, '.'); - - if (separatorPoint - realPath + 1 >= PATH_MAX - 1) { - return 0; - } - - len = separatorPoint - realPath; - strncat(path, realPath, len); - path[len] = '\0'; - ++separatorPoint; + if (filter(vf)) { + return vf; } - - if (dotPoint - realPath + 1 >= PATH_MAX - 1) { - return 0; - } - - if (dotPoint >= separatorPoint) { - len = dotPoint - separatorPoint; - } else { - len = PATH_MAX - 1; - } - - strncpy(realPrefix, separatorPoint, len); - realPrefix[len] = '\0'; + vf->close(vf); + dirent = dir->listNext(dir); + } + return 0; +} - prefix = realPrefix; - dir = VDirOpen(path); - } +struct VFile* VDirFindNextAvailable(struct VDir* dir, const char* basename, const char* infix, const char* suffix, int mode) { if (!dir) { - // This shouldn't be possible return 0; } dir->rewind(dir); struct VDirEntry* dirent; - size_t prefixLen = strlen(prefix); + size_t prefixLen = strlen(basename); size_t infixLen = strlen(infix); + char path[PATH_MAX]; unsigned next = 0; while ((dirent = dir->listNext(dir))) { const char* filename = dirent->name(dirent); - char* dotPoint = strrchr(filename, '.'); + const char* dotPoint = strrchr(filename, '.'); size_t len = strlen(filename); if (dotPoint) { len = (dotPoint - filename);@@ -255,7 +274,7 @@ len = separator - filename;
if (len != prefixLen) { continue; } - if (strncmp(filename, prefix, prefixLen) == 0) { + if (strncmp(filename, basename, prefixLen) == 0) { int nlen; separator += infixLen; snprintf(path, PATH_MAX - 1, "%%u%s%%n", suffix);@@ -272,7 +291,7 @@ next = increment + 1;
} } } - snprintf(path, PATH_MAX - 1, "%s%s%u%s", prefix, infix, next, suffix); + snprintf(path, PATH_MAX - 1, "%s%s%u%s", basename, infix, next, suffix); path[PATH_MAX - 1] = '\0'; return dir->openFile(dir, path, mode); }
@@ -82,10 +82,13 @@ #endif
struct VDir* VDeviceList(void); +void separatePath(const char* path, char* dirname, char* basename, char* extension); + struct VFile* VDirOptionalOpenFile(struct VDir* dir, const char* realPath, const char* prefix, const char* suffix, int mode); -struct VFile* VDirOptionalOpenIncrementFile(struct VDir* dir, const char* realPath, const char* prefix, - const char* infix, const char* suffix, int mode); + +struct VFile* VDirFindFirst(struct VDir* dir, bool (*filter)(struct VFile*)); +struct VFile* VDirFindNextAvailable(struct VDir*, const char* basename, const char* infix, const char* suffix, int mode); ssize_t VFileReadline(struct VFile* vf, char* buffer, size_t size);