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) { struct VFile* GBAGetState(struct GBA* gba, struct VDir* dir, int slot, bool write) {
char suffix[5] = { '\0' }; char basename[PATH_MAX];
snprintf(suffix, sizeof(suffix), ".ss%d", slot); separatePath(gba->activeFile, 0, basename, 0);
return VDirOptionalOpenFile(dir, gba->activeFile, "savestate", suffix, write ? (O_CREAT | O_TRUNC | O_RDWR) : O_RDONLY); 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 #ifdef USE_PNG
@ -709,7 +711,9 @@ void GBATakeScreenshot(struct GBA* gba, struct VDir* dir) {
#ifdef USE_PNG #ifdef USE_PNG
unsigned stride; unsigned stride;
const void* pixels = 0; 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; bool success = false;
if (vf) { if (vf) {
gba->video.renderer->getPixels(gba->video.renderer, &stride, &pixels); 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; 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) { 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; 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 #endif

View File

@ -22,10 +22,39 @@
#include <signal.h> #include <signal.h>
static void _loadGameDir(struct GBAThread* threadContext);
static const float _defaultFPSTarget = 60.f; 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 #ifndef DISABLE_THREADING
#ifdef USE_PTHREADS #ifdef USE_PTHREADS
static pthread_key_t _contextKey; static pthread_key_t _contextKey;
@ -196,11 +225,9 @@ static THREAD_ENTRY _GBAThreadRun(void* context) {
if (threadContext->movie) { if (threadContext->movie) {
struct VDir* movieDir = VDirOpen(threadContext->movie); struct VDir* movieDir = VDirOpen(threadContext->movie);
#ifdef USE_LIBZIP
if (!movieDir) { if (!movieDir) {
movieDir = VDirOpenZip(threadContext->movie, 0); movieDir = VDirOpenArchive(threadContext->movie);
} }
#endif
if (movieDir) { if (movieDir) {
struct GBAMGMContext* mgm = malloc(sizeof(*mgm)); struct GBAMGMContext* mgm = malloc(sizeof(*mgm));
GBAMGMContextCreate(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) { 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->fname = args->fname;
threadContext->patch = VFileOpen(args->patch, O_RDONLY); threadContext->patch = VFileOpen(args->patch, O_RDONLY);
threadContext->cheatsFile = VFileOpen(args->cheatsFile, O_RDONLY); threadContext->cheatsFile = VFileOpen(args->cheatsFile, O_RDONLY);
@ -413,26 +435,13 @@ bool GBAThreadStart(struct GBAThread* threadContext) {
threadContext->rom = 0; threadContext->rom = 0;
} }
if (threadContext->gameDir) {
_loadGameDir(threadContext);
}
if (!threadContext->rom && !bootBios) { if (!threadContext->rom && !bootBios) {
threadContext->state = THREAD_SHUTDOWN; threadContext->state = THREAD_SHUTDOWN;
return false; return false;
} }
threadContext->save = VDirOptionalOpenFile(threadContext->stateDir, threadContext->fname, "sram", ".sav", O_CREAT | O_RDWR); GBADirectorySetInit(&threadContext->dirs);
_reloadDirectories(threadContext);
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);
}
MutexInit(&threadContext->stateMutex); MutexInit(&threadContext->stateMutex);
ConditionInit(&threadContext->stateCond); ConditionInit(&threadContext->stateCond);
@ -562,18 +571,7 @@ void GBAThreadJoin(struct GBAThread* threadContext) {
threadContext->patch = 0; threadContext->patch = 0;
} }
if (threadContext->gameDir) { GBADirectorySetDeinit(&threadContext->dirs);
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;
}
} }
bool GBAThreadIsActive(struct GBAThread* threadContext) { bool GBAThreadIsActive(struct GBAThread* threadContext) {
@ -687,31 +685,11 @@ void GBAThreadPauseFromThread(struct GBAThread* threadContext) {
} }
void GBAThreadLoadROM(struct GBAThread* threadContext, const char* fname) { void GBAThreadLoadROM(struct GBAThread* threadContext, const char* fname) {
threadContext->rom = VFileOpen(fname, O_RDONLY); threadContext->dirs.archive = VDirOpenArchive(fname);
threadContext->gameDir = 0; if (threadContext->dirs.archive) {
if (!threadContext->gameDir) { threadContext->rom = VDirFindFirst(threadContext->dirs.archive, GBAIsROM);
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 { } else {
vf->close(vf); threadContext->rom = VFileOpen(fname, O_RDONLY);
}
dirent = threadContext->gameDir->listNext(threadContext->gameDir);
} }
} }
@ -728,13 +706,15 @@ void GBAThreadReplaceROM(struct GBAThread* threadContext, const char* fname) {
threadContext->save = 0; threadContext->save = 0;
} }
GBAThreadLoadROM(threadContext, fname); if (threadContext->dirs.archive) {
if (threadContext->gameDir) { threadContext->dirs.archive->close(threadContext->dirs.archive);
_loadGameDir(threadContext); threadContext->dirs.archive = 0;
} }
GBAThreadLoadROM(threadContext, fname);
threadContext->fname = fname; threadContext->fname = fname;
threadContext->save = VDirOptionalOpenFile(threadContext->stateDir, threadContext->fname, "sram", ".sav", O_CREAT | O_RDWR); _reloadDirectories(threadContext);
GBARaiseIRQ(threadContext->gba, IRQ_GAMEPAK); GBARaiseIRQ(threadContext->gba, IRQ_GAMEPAK);
GBALoadROM(threadContext->gba, threadContext->rom, threadContext->save, threadContext->fname); GBALoadROM(threadContext->gba, threadContext->rom, threadContext->save, threadContext->fname);
@ -753,7 +733,7 @@ struct GBAThread* GBAThreadGetContext(void) {
#endif #endif
void GBAThreadTakeScreenshot(struct GBAThread* threadContext) { void GBAThreadTakeScreenshot(struct GBAThread* threadContext) {
GBATakeScreenshot(threadContext->gba, threadContext->stateDir); GBATakeScreenshot(threadContext->gba, threadContext->dirs.screenshot);
} }
#else #else

View File

@ -10,6 +10,7 @@
#include "gba/gba.h" #include "gba/gba.h"
#include "gba/input.h" #include "gba/input.h"
#include "gba/context/directories.h"
#include "gba/context/overrides.h" #include "gba/context/overrides.h"
#include "gba/context/sync.h" #include "gba/context/sync.h"
@ -47,8 +48,7 @@ struct GBAThread {
struct GBAVideoRenderer* renderer; struct GBAVideoRenderer* renderer;
struct GBASIODriverSet sioDrivers; struct GBASIODriverSet sioDrivers;
struct ARMDebugger* debugger; struct ARMDebugger* debugger;
struct VDir* gameDir; struct GBADirectorySet dirs;
struct VDir* stateDir;
struct VFile* rom; struct VFile* rom;
struct VFile* save; struct VFile* save;
struct VFile* bios; struct VFile* bios;

View File

@ -110,8 +110,8 @@ GameController::GameController(QObject* parent)
context->gba->video.renderer->disableOBJ = !controller->m_videoLayers[4]; context->gba->video.renderer->disableOBJ = !controller->m_videoLayers[4];
controller->m_fpsTarget = context->fpsTarget; controller->m_fpsTarget = context->fpsTarget;
if (GBALoadState(context, context->stateDir, 0, SAVESTATE_SCREENSHOT)) { if (GBALoadState(context, context->dirs.state, 0, SAVESTATE_SCREENSHOT)) {
VFile* vf = GBAGetState(context->gba, context->stateDir, 0, true); VFile* vf = GBAGetState(context->gba, context->dirs.state, 0, true);
if (vf) { if (vf) {
vf->truncate(vf, 0); vf->truncate(vf, 0);
} }
@ -139,7 +139,7 @@ GameController::GameController(QObject* parent)
return false; return false;
} }
GameController* controller = static_cast<GameController*>(context->userData); GameController* controller = static_cast<GameController*>(context->userData);
if (!GBASaveState(context, context->stateDir, 0, true)) { if (!GBASaveState(context, context->dirs.state, 0, true)) {
return false; return false;
} }
QMetaObject::invokeMethod(controller, "closeGame"); QMetaObject::invokeMethod(controller, "closeGame");
@ -317,19 +317,13 @@ void GameController::openGame(bool biosOnly) {
m_threadContext.sync.audioWait = m_audioSync; m_threadContext.sync.audioWait = m_audioSync;
} }
m_threadContext.gameDir = 0;
m_threadContext.bootBios = biosOnly; m_threadContext.bootBios = biosOnly;
if (biosOnly) { if (biosOnly) {
m_threadContext.fname = nullptr; m_threadContext.fname = nullptr;
} else { } else {
m_threadContext.fname = strdup(m_fname.toUtf8().constData()); 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) { if (!m_bios.isNull() && m_useBios) {
m_threadContext.bios = VFileDevice::open(m_bios, O_RDONLY); m_threadContext.bios = VFileDevice::open(m_bios, O_RDONLY);
@ -712,7 +706,7 @@ void GameController::loadState(int slot) {
controller->m_backupLoadState = new GBASerializedState; controller->m_backupLoadState = new GBASerializedState;
} }
GBASerialize(context->gba, controller->m_backupLoadState); 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->frameAvailable(controller->m_drawContext);
controller->stateLoaded(context); controller->stateLoaded(context);
} }
@ -725,13 +719,13 @@ void GameController::saveState(int slot) {
} }
GBARunOnThread(&m_threadContext, [](GBAThread* context) { GBARunOnThread(&m_threadContext, [](GBAThread* context) {
GameController* controller = static_cast<GameController*>(context->userData); 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) { if (vf) {
controller->m_backupSaveState.resize(vf->size(vf)); controller->m_backupSaveState.resize(vf->size(vf));
vf->read(vf, controller->m_backupSaveState.data(), controller->m_backupSaveState.size()); vf->read(vf, controller->m_backupSaveState.data(), controller->m_backupSaveState.size());
vf->close(vf); 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) { GBARunOnThread(&m_threadContext, [](GBAThread* context) {
GameController* controller = static_cast<GameController*>(context->userData); 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) { if (vf) {
vf->write(vf, controller->m_backupSaveState.constData(), controller->m_backupSaveState.size()); vf->write(vf, controller->m_backupSaveState.constData(), controller->m_backupSaveState.size());
vf->close(vf); vf->close(vf);

View File

@ -170,7 +170,7 @@ bool LoadSaveState::eventFilter(QObject* object, QEvent* event) {
void LoadSaveState::loadState(int slot) { void LoadSaveState::loadState(int slot) {
GBAThread* thread = m_controller->thread(); 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) { if (!vf) {
m_slots[slot - 1]->setText(tr("Empty")); m_slots[slot - 1]->setText(tr("Empty"));
return; return;

View File

@ -423,7 +423,7 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLPlayer
case SDLK_F8: case SDLK_F8:
case SDLK_F9: case SDLK_F9:
GBAThreadInterrupt(context); 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); GBAThreadContinue(context);
break; break;
default: default:
@ -441,7 +441,7 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLPlayer
case SDLK_F8: case SDLK_F8:
case SDLK_F9: case SDLK_F9:
GBAThreadInterrupt(context); 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); GBAThreadContinue(context);
break; break;
default: default:

View File

@ -157,6 +157,52 @@ ssize_t VFileRead16LE(struct VFile* vf, void* hword) {
return r; 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) { struct VFile* VDirOptionalOpenFile(struct VDir* dir, const char* realPath, const char* prefix, const char* suffix, int mode) {
char path[PATH_MAX]; char path[PATH_MAX];
path[PATH_MAX - 1] = '\0'; path[PATH_MAX - 1] = '\0';
@ -185,64 +231,37 @@ struct VFile* VDirOptionalOpenFile(struct VDir* dir, const char* realPath, const
return vf; return vf;
} }
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*)) {
char path[PATH_MAX]; dir->rewind(dir);
path[PATH_MAX - 1] = '\0'; struct VDirEntry* dirent = dir->listNext(dir);
char realPrefix[PATH_MAX]; while (dirent) {
realPrefix[PATH_MAX - 1] = '\0'; struct VFile* vf = dir->openFile(dir, dirent->name(dirent), O_RDONLY);
if (!vf) {
dirent = dir->listNext(dir);
continue;
}
if (filter(vf)) {
return vf;
}
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) { if (!dir) {
if (!realPath) {
return 0;
}
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 (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);
}
if (!dir) {
// This shouldn't be possible
return 0; return 0;
} }
dir->rewind(dir); dir->rewind(dir);
struct VDirEntry* dirent; struct VDirEntry* dirent;
size_t prefixLen = strlen(prefix); size_t prefixLen = strlen(basename);
size_t infixLen = strlen(infix); size_t infixLen = strlen(infix);
char path[PATH_MAX];
unsigned next = 0; unsigned next = 0;
while ((dirent = dir->listNext(dir))) { while ((dirent = dir->listNext(dir))) {
const char* filename = dirent->name(dirent); const char* filename = dirent->name(dirent);
char* dotPoint = strrchr(filename, '.'); const char* dotPoint = strrchr(filename, '.');
size_t len = strlen(filename); size_t len = strlen(filename);
if (dotPoint) { if (dotPoint) {
len = (dotPoint - filename); len = (dotPoint - filename);
@ -255,7 +274,7 @@ struct VFile* VDirOptionalOpenIncrementFile(struct VDir* dir, const char* realPa
if (len != prefixLen) { if (len != prefixLen) {
continue; continue;
} }
if (strncmp(filename, prefix, prefixLen) == 0) { if (strncmp(filename, basename, prefixLen) == 0) {
int nlen; int nlen;
separator += infixLen; separator += infixLen;
snprintf(path, PATH_MAX - 1, "%%u%s%%n", suffix); 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'; path[PATH_MAX - 1] = '\0';
return dir->openFile(dir, path, mode); return dir->openFile(dir, path, mode);
} }

View File

@ -82,10 +82,13 @@ struct VDir* VDirOpen7z(const char* path, int flags);
struct VDir* VDeviceList(void); 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, struct VFile* VDirOptionalOpenFile(struct VDir* dir, const char* realPath, const char* prefix, const char* suffix,
int mode); 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); ssize_t VFileReadline(struct VFile* vf, char* buffer, size_t size);