diff --git a/src/gba/context/directories.c b/src/gba/context/directories.c new file mode 100644 index 000000000..224b96d32 --- /dev/null +++ b/src/gba/context/directories.c @@ -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; + } +} diff --git a/src/gba/context/directories.h b/src/gba/context/directories.h new file mode 100644 index 000000000..3a2044157 --- /dev/null +++ b/src/gba/context/directories.h @@ -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 diff --git a/src/gba/serialize.c b/src/gba/serialize.c index 6d00b46a8..119bdaed1 100644 --- a/src/gba/serialize.c +++ b/src/gba/serialize.c @@ -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); diff --git a/src/gba/supervisor/cli.c b/src/gba/supervisor/cli.c index 58de422b3..60d8839e8 100644 --- a/src/gba/supervisor/cli.c +++ b/src/gba/supervisor/cli.c @@ -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 diff --git a/src/gba/supervisor/thread.c b/src/gba/supervisor/thread.c index e9950b4cb..88696b693 100644 --- a/src/gba/supervisor/thread.c +++ b/src/gba/supervisor/thread.c @@ -22,10 +22,39 @@ #include -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 diff --git a/src/gba/supervisor/thread.h b/src/gba/supervisor/thread.h index c9b86bd5e..2a70d233c 100644 --- a/src/gba/supervisor/thread.h +++ b/src/gba/supervisor/thread.h @@ -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; diff --git a/src/platform/qt/GameController.cpp b/src/platform/qt/GameController.cpp index e36108a47..401964cd4 100644 --- a/src/platform/qt/GameController.cpp +++ b/src/platform/qt/GameController.cpp @@ -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(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(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(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); diff --git a/src/platform/qt/LoadSaveState.cpp b/src/platform/qt/LoadSaveState.cpp index c974680bd..af37615a3 100644 --- a/src/platform/qt/LoadSaveState.cpp +++ b/src/platform/qt/LoadSaveState.cpp @@ -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; diff --git a/src/platform/sdl/sdl-events.c b/src/platform/sdl/sdl-events.c index a98385f1f..42b14b209 100644 --- a/src/platform/sdl/sdl-events.c +++ b/src/platform/sdl/sdl-events.c @@ -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: diff --git a/src/util/vfs.c b/src/util/vfs.c index da098dfe3..80d201384 100644 --- a/src/util/vfs.c +++ b/src/util/vfs.c @@ -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); } diff --git a/src/util/vfs.h b/src/util/vfs.h index 7ab637881..81dddc9c1 100644 --- a/src/util/vfs.h +++ b/src/util/vfs.h @@ -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);