7#if defined(SDL_LIBRETRO_IMPLEMENTATION) && !defined(SDL_LIBRETRO_VFS_IMPL_ONCE)
8#define SDL_LIBRETRO_VFS_IMPL_ONCE
19#define SDL_LIBRETRO_DISABLE_VFS
22#ifndef SDL_LIBRETRO_DISABLE_VFS
33struct retro_vfs_file_handle {
42struct retro_vfs_dir_handle {
53static const char* SDL_Libretro_VFS_GetPath(
struct retro_vfs_file_handle* stream) {
54 return stream ? stream->path : NULL;
60static struct retro_vfs_file_handle* SDL_Libretro_VFS_Open(
const char* path,
unsigned mode,
unsigned hints) {
62 if (!path)
return NULL;
65 SDL_PathInfo pathInfo;
66 if (SDL_GetPathInfo(path, &pathInfo) && pathInfo.type == SDL_PATHTYPE_DIRECTORY)
return NULL;
69 bool update = (mode & RETRO_VFS_FILE_ACCESS_UPDATE_EXISTING) != 0;
70 bool read = (mode & RETRO_VFS_FILE_ACCESS_READ) != 0;
71 bool write = (mode & RETRO_VFS_FILE_ACCESS_WRITE) != 0;
74 sdlMode = update ?
"r+b" :
"w+b";
81 SDL_IOStream* io = SDL_IOFromFile(path, sdlMode);
84 struct retro_vfs_file_handle* h = (
struct retro_vfs_file_handle*)SDL_malloc(
sizeof(*h));
85 if (!h) { SDL_CloseIO(io);
return NULL; }
87 h->path = SDL_strdup(path);
95static int SDL_Libretro_VFS_Close(
struct retro_vfs_file_handle* stream) {
96 if (!stream)
return -1;
97 int ret = SDL_CloseIO(stream->io) ? 0 : -1;
98 SDL_free(stream->path);
106static int64_t SDL_Libretro_VFS_Size(
struct retro_vfs_file_handle* stream) {
107 if (!stream)
return -1;
108 int64_t sz = SDL_GetIOSize(stream->io);
109 return sz < 0 ? -1 : sz;
115static int64_t SDL_Libretro_VFS_Tell(
struct retro_vfs_file_handle* stream) {
116 if (!stream)
return -1;
117 return SDL_TellIO(stream->io);
123static int64_t SDL_Libretro_VFS_Seek(
struct retro_vfs_file_handle* stream, int64_t offset,
int seek_position) {
124 if (!stream)
return -1;
126 switch (seek_position) {
127 case RETRO_VFS_SEEK_POSITION_START: whence = SDL_IO_SEEK_SET;
break;
128 case RETRO_VFS_SEEK_POSITION_CURRENT: whence = SDL_IO_SEEK_CUR;
break;
129 case RETRO_VFS_SEEK_POSITION_END: whence = SDL_IO_SEEK_END;
break;
132 return SDL_SeekIO(stream->io, offset, whence);
138static int64_t SDL_Libretro_VFS_Read(
struct retro_vfs_file_handle* stream,
void* s, uint64_t len) {
139 if (!stream || !s)
return -1;
140 size_t n = SDL_ReadIO(stream->io, s, (
size_t)len);
141 if (n == 0 && SDL_GetIOStatus(stream->io) == SDL_IO_STATUS_ERROR)
return -1;
148static int64_t SDL_Libretro_VFS_Write(
struct retro_vfs_file_handle* stream,
const void* s, uint64_t len) {
149 if (!stream || !s)
return -1;
150 size_t n = SDL_WriteIO(stream->io, s, (
size_t)len);
151 if (n == 0 && SDL_GetIOStatus(stream->io) == SDL_IO_STATUS_ERROR)
return -1;
158static int SDL_Libretro_VFS_Flush(
struct retro_vfs_file_handle* stream) {
159 if (!stream)
return -1;
160 return SDL_FlushIO(stream->io) ? 0 : -1;
166static int SDL_Libretro_VFS_Remove(
const char* path) {
167 return (path && SDL_RemovePath(path)) ? 0 : -1;
173static int SDL_Libretro_VFS_Rename(
const char* old_path,
const char* new_path) {
174 return (old_path && new_path && SDL_RenamePath(old_path, new_path)) ? 0 : -1;
186static int64_t SDL_Libretro_VFS_Truncate(
struct retro_vfs_file_handle* stream, int64_t length) {
187 if (!stream || length < 0)
return -1;
189 int64_t cur_size = SDL_GetIOSize(stream->io);
190 if (cur_size < 0)
return -1;
193 int64_t orig_pos = SDL_TellIO(stream->io);
195 if (length >= cur_size) {
197 if (SDL_SeekIO(stream->io, 0, SDL_IO_SEEK_END) < 0)
return -1;
198 int64_t to_pad = length - cur_size;
201 SDL_memset(zeros, 0,
sizeof(zeros));
203 size_t chunk = (to_pad < (int64_t)
sizeof(zeros)) ? (
size_t)to_pad :
sizeof(zeros);
204 if (SDL_WriteIO(stream->io, zeros, chunk) != chunk)
return -1;
205 to_pad -= (int64_t)chunk;
208 if (orig_pos >= 0) SDL_SeekIO(stream->io, orig_pos, SDL_IO_SEEK_SET);
215 buf = (Uint8*)SDL_malloc((
size_t)length);
218 if (SDL_SeekIO(stream->io, 0, SDL_IO_SEEK_SET) < 0) { SDL_free(buf);
return -1; }
219 size_t got = SDL_ReadIO(stream->io, buf, (
size_t)length);
220 if ((int64_t)got != length) { SDL_free(buf);
return -1; }
224 bool canWrite = (stream->mode & RETRO_VFS_FILE_ACCESS_WRITE) != 0;
226 SDL_CloseIO(stream->io);
227 stream->io = SDL_IOFromFile(stream->path,
"wb");
228 if (!stream->io) { SDL_free(buf); stream->io = SDL_IOFromFile(stream->path, canWrite ?
"r+b" :
"rb");
return -1; }
230 if (length > 0 && (int64_t)SDL_WriteIO(stream->io, buf, (
size_t)length) != length) {
232 SDL_CloseIO(stream->io);
233 stream->io = SDL_IOFromFile(stream->path, canWrite ?
"r+b" :
"rb");
239 SDL_CloseIO(stream->io);
240 stream->io = SDL_IOFromFile(stream->path, canWrite ?
"r+b" :
"rb");
241 if (!stream->io)
return -1;
245 SDL_SeekIO(stream->io, orig_pos < length ? orig_pos : length, SDL_IO_SEEK_SET);
253static int SDL_Libretro_VFS_Stat64(
const char* path, int64_t* size) {
256 if (!SDL_GetPathInfo(path, &info)) {
260 int flags = RETRO_VFS_STAT_IS_VALID;
261 if (info.type == SDL_PATHTYPE_DIRECTORY) {
262 flags |= RETRO_VFS_STAT_IS_DIRECTORY;
274static int SDL_Libretro_VFS_Stat(
const char* path, int32_t* size) {
277 int out = SDL_Libretro_VFS_Stat64(path, &outSize);
279 if (outSize > SDL_MAX_SINT32) {
280 SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
281 "[SDL_Libretro] VFS stat size %" SDL_PRIs64
" for '%s' exceeds int32. Clamping to SDL_MAX_SINT32.",
283 *size = SDL_MAX_SINT32;
286 *size = (int32_t)outSize;
295static int SDL_Libretro_VFS_Mkdir(
const char* dir) {
298 if (SDL_GetPathInfo(dir, &info))
return -2;
299 return SDL_CreateDirectory(dir) ? 0 : -1;
312} SDL_Libretro_DirCollect;
317static SDL_EnumerationResult SDL_Libretro_DirCallback(
void* userdata,
const char* dirname,
const char* fname) {
319 SDL_Libretro_DirCollect* ctx = (SDL_Libretro_DirCollect*)userdata;
320 if (!ctx->includeHidden && fname[0] ==
'.')
return SDL_ENUM_CONTINUE;
322 if (ctx->count >= ctx->capacity) {
323 size_t newCap = ctx->capacity ? ctx->capacity * 2 : 32;
325 char** newNames = (
char**)SDL_realloc(ctx->names, newCap *
sizeof(
char*));
326 if (newNames) ctx->names = newNames;
327 bool* newIsDir = (
bool*)SDL_realloc(ctx->isDir, newCap *
sizeof(
bool));
328 if (newIsDir) ctx->isDir = newIsDir;
329 if (!newNames || !newIsDir)
return SDL_ENUM_FAILURE;
330 ctx->capacity = newCap;
333 ctx->names[ctx->count] = SDL_strdup(fname);
334 if (!ctx->names[ctx->count])
return SDL_ENUM_FAILURE;
337 char fullpath[SDL_LIBRETRO_MAX_PATH];
338 SDL_snprintf(fullpath,
sizeof(fullpath),
"%s/%s", ctx->dirPath, fname);
340 ctx->isDir[ctx->count] = SDL_GetPathInfo(fullpath, &info) && info.type == SDL_PATHTYPE_DIRECTORY;
342 return SDL_ENUM_CONTINUE;
348static struct retro_vfs_dir_handle* SDL_Libretro_VFS_Opendir(
const char* dir,
bool include_hidden) {
349 if (!dir)
return NULL;
351 SDL_Libretro_DirCollect ctx;
352 SDL_memset(&ctx, 0,
sizeof(ctx));
354 ctx.includeHidden = include_hidden;
356 if (!SDL_EnumerateDirectory(dir, SDL_Libretro_DirCallback, &ctx)) {
358 for (
size_t i = 0; i < ctx.count; i++) SDL_free(ctx.names[i]);
364 struct retro_vfs_dir_handle* h = (
struct retro_vfs_dir_handle*)SDL_malloc(
sizeof(*h));
366 for (
size_t i = 0; i < ctx.count; i++) SDL_free(ctx.names[i]);
371 h->dir = SDL_strdup(dir);
372 h->names = ctx.names;
373 h->isDir = ctx.isDir;
374 h->count = ctx.count;
375 h->index = (size_t)-1;
382static bool SDL_Libretro_VFS_Readdir(
struct retro_vfs_dir_handle* dirstream) {
383 if (!dirstream)
return false;
384 size_t next = (dirstream->index == (size_t)-1) ? 0 : dirstream->index + 1;
385 if (next >= dirstream->count)
return false;
386 dirstream->index = next;
393static const char* SDL_Libretro_VFS_DirentGetName(
struct retro_vfs_dir_handle* dirstream) {
394 if (!dirstream || dirstream->index >= dirstream->count)
return NULL;
395 return dirstream->names[dirstream->index];
401static bool SDL_Libretro_VFS_DirentIsDir(
struct retro_vfs_dir_handle* dirstream) {
402 if (!dirstream || dirstream->index >= dirstream->count)
return false;
403 return dirstream->isDir[dirstream->index];
409static int SDL_Libretro_VFS_Closedir(
struct retro_vfs_dir_handle* dirstream) {
410 if (!dirstream)
return -1;
411 for (
size_t i = 0; i < dirstream->count; i++) SDL_free(dirstream->names[i]);
412 SDL_free(dirstream->names);
413 SDL_free(dirstream->isDir);
414 SDL_free(dirstream->dir);
427void SDL_Libretro_SetVFS(SDL_Libretro* lr,
void* vfs_interface) {
429 struct retro_vfs_interface temp = {0};
430 struct retro_vfs_interface* vfs = (
struct retro_vfs_interface*)vfs_interface;
435 lr->vfs_interface.get_path = vfs->get_path != NULL ? vfs->get_path : &SDL_Libretro_VFS_GetPath;
436 lr->vfs_interface.open = vfs->open != NULL ? vfs->open : &SDL_Libretro_VFS_Open;
437 lr->vfs_interface.close = vfs->close != NULL ? vfs->close : &SDL_Libretro_VFS_Close;
438 lr->vfs_interface.size = vfs->size != NULL ? vfs->size : &SDL_Libretro_VFS_Size;
439 lr->vfs_interface.tell = vfs->tell != NULL ? vfs->tell : &SDL_Libretro_VFS_Tell;
440 lr->vfs_interface.seek = vfs->seek != NULL ? vfs->seek : &SDL_Libretro_VFS_Seek;
441 lr->vfs_interface.read = vfs->read != NULL ? vfs->read : &SDL_Libretro_VFS_Read;
442 lr->vfs_interface.write = vfs->write != NULL ? vfs->write : &SDL_Libretro_VFS_Write;
443 lr->vfs_interface.flush = vfs->flush != NULL ? vfs->flush : &SDL_Libretro_VFS_Flush;
444 lr->vfs_interface.remove = vfs->remove != NULL ? vfs->remove : &SDL_Libretro_VFS_Remove;
445 lr->vfs_interface.rename = vfs->rename != NULL ? vfs->rename : &SDL_Libretro_VFS_Rename;
446 lr->vfs_interface.truncate = vfs->truncate != NULL ? vfs->truncate : &SDL_Libretro_VFS_Truncate;
447 lr->vfs_interface.stat = vfs->stat != NULL ? vfs->stat : &SDL_Libretro_VFS_Stat;
448 lr->vfs_interface.mkdir = vfs->mkdir != NULL ? vfs->mkdir : &SDL_Libretro_VFS_Mkdir;
449 lr->vfs_interface.opendir = vfs->opendir != NULL ? vfs->opendir : &SDL_Libretro_VFS_Opendir;
450 lr->vfs_interface.readdir = vfs->readdir != NULL ? vfs->readdir : &SDL_Libretro_VFS_Readdir;
451 lr->vfs_interface.dirent_get_name = vfs->dirent_get_name != NULL ? vfs->dirent_get_name : &SDL_Libretro_VFS_DirentGetName;
452 lr->vfs_interface.dirent_is_dir = vfs->dirent_is_dir != NULL ? vfs->dirent_is_dir : &SDL_Libretro_VFS_DirentIsDir;
453 lr->vfs_interface.closedir = vfs->closedir != NULL ? vfs->closedir : &SDL_Libretro_VFS_Closedir;
454 lr->vfs_interface.stat_64 = vfs->stat_64 != NULL ? vfs->stat_64 : &SDL_Libretro_VFS_Stat64;