GBA Thread: Redo state directories for future expansion

This commit is contained in:
Jeffrey Pfau 2015-12-30 21:21:02 -08:00
parent 5147a5160f
commit 91cf3be128
11 changed files with 253 additions and 143 deletions

View File

@ -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;
}
}

View File

@ -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

View File

@ -198,9 +198,11 @@ bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) {
}
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);

View File

@ -106,7 +106,7 @@ static void _load(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
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 @@ static void _save(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
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

View File

@ -22,10 +22,39 @@
#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;
}
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
static pthread_key_t _contextKey;
@ -196,11 +225,9 @@ static THREAD_ENTRY _GBAThreadRun(void* context) {
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 @@ void GBAMapOptionsToContext(const struct GBAOptions* opts, struct GBAThread* thr
}
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 @@ bool GBAThreadStart(struct GBAThread* threadContext) {
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 @@ void GBAThreadJoin(struct GBAThread* threadContext) {
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 @@ void GBAThreadPauseFromThread(struct GBAThread* threadContext) {
}
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 @@ void GBAThreadReplaceROM(struct GBAThread* threadContext, const char* fname) {
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 @@ struct GBAThread* GBAThreadGetContext(void) {
#endif
void GBAThreadTakeScreenshot(struct GBAThread* threadContext) {
GBATakeScreenshot(threadContext->gba, threadContext->stateDir);
GBATakeScreenshot(threadContext->gba, threadContext->dirs.screenshot);
}
#else

View File

@ -10,6 +10,7 @@
#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 @@ struct GBAThread {
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;

View File

@ -110,8 +110,8 @@ GameController::GameController(QObject* parent)
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 @@ GameController::GameController(QObject* parent)
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 @@ void GameController::openGame(bool biosOnly) {
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 @@ void GameController::loadState(int slot) {
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 @@ void GameController::saveState(int 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 @@ void GameController::saveBackupState() {
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);

View File

@ -170,7 +170,7 @@ bool LoadSaveState::eventFilter(QObject* object, QEvent* event) {
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;

View File

@ -423,7 +423,7 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLPlayer
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 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLPlayer
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:

View File

@ -157,6 +157,52 @@ ssize_t VFileRead16LE(struct VFile* vf, void* hword) {
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 @@ struct VFile* VDirOptionalOpenFile(struct VDir* dir, const char* realPath, const
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';
prefix = realPrefix;
dir = VDirOpen(path);
vf->close(vf);
dirent = dir->listNext(dir);
}
return 0;
}
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 @@ struct VFile* VDirOptionalOpenIncrementFile(struct VDir* dir, const char* realPa
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 @@ struct VFile* VDirOptionalOpenIncrementFile(struct VDir* dir, const char* realPa
}
}
}
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);
}

View File

@ -82,10 +82,13 @@ struct VDir* VDirOpen7z(const char* path, int flags);
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);