From 74ea1c19a9f9f0b9381b02139ed130af99fb4c1b Mon Sep 17 00:00:00 2001 From: meepingsnesroms Date: Mon, 29 Oct 2018 19:12:42 -0700 Subject: [PATCH] RetroArch port now working again --- bugs/regressions.txt | 4 +- libretroBuildSystem/build/Makefile.common | 4 + .../libretro-common/compat/fopen_utf8.c | 60 ++ .../include/compat/fopen_utf8.h | 34 + .../{ => libretro-common/include}/libretro.h | 0 .../libretro-common/include/memmap.h | 52 ++ .../include/streams/file_stream.h | 111 ++++ .../include/vfs/vfs_implementation.h | 67 ++ .../libretro-common/memmap/memmap.c | 163 +++++ .../libretro-common/streams/file_stream.c | 613 ++++++++++++++++++ .../libretro-common/vfs/vfs_implementation.c | 610 +++++++++++++++++ libretroBuildSystem/libretro.c | 34 +- 12 files changed, 1734 insertions(+), 18 deletions(-) create mode 100644 libretroBuildSystem/libretro-common/compat/fopen_utf8.c create mode 100644 libretroBuildSystem/libretro-common/include/compat/fopen_utf8.h rename libretroBuildSystem/{ => libretro-common/include}/libretro.h (100%) create mode 100644 libretroBuildSystem/libretro-common/include/memmap.h create mode 100644 libretroBuildSystem/libretro-common/include/streams/file_stream.h create mode 100644 libretroBuildSystem/libretro-common/include/vfs/vfs_implementation.h create mode 100644 libretroBuildSystem/libretro-common/memmap/memmap.c create mode 100644 libretroBuildSystem/libretro-common/streams/file_stream.c create mode 100644 libretroBuildSystem/libretro-common/vfs/vfs_implementation.c diff --git a/bugs/regressions.txt b/bugs/regressions.txt index 3269710..47b9a04 100644 --- a/bugs/regressions.txt +++ b/bugs/regressions.txt @@ -1,2 +1,4 @@ -RetroArch port crashes on exit Endian compatibility is broken(the CPU state needs to be standardized) + +Fixed: +RetroArch port crashes on exit(needed to check if environ_cb returned true, switched to libretro-common filestreams too) diff --git a/libretroBuildSystem/build/Makefile.common b/libretroBuildSystem/build/Makefile.common index 1d2c5d3..1118f47 100644 --- a/libretroBuildSystem/build/Makefile.common +++ b/libretroBuildSystem/build/Makefile.common @@ -21,9 +21,13 @@ SOURCES_C := $(CORE_DIR)/libretro.c \ $(LIBRETRO_COMM_DIR)/compat/compat_posix_string.c \ $(LIBRETRO_COMM_DIR)/compat/compat_strcasestr.c \ $(LIBRETRO_COMM_DIR)/compat/compat_strl.c \ + $(LIBRETRO_COMM_DIR)/compat/fopen_utf8.c \ $(LIBRETRO_COMM_DIR)/encodings/encoding_utf.c \ $(LIBRETRO_COMM_DIR)/file/file_path.c \ + $(LIBRETRO_COMM_DIR)/memmap/memmap.c \ + $(LIBRETRO_COMM_DIR)/streams/file_stream.c \ $(LIBRETRO_COMM_DIR)/string/stdstring.c \ + $(LIBRETRO_COMM_DIR)/vfs/vfs_implementation.c \ $(EMU_SOURCES_C) SOURCES_ASM := $(EMU_SOURCES_ASM) diff --git a/libretroBuildSystem/libretro-common/compat/fopen_utf8.c b/libretroBuildSystem/libretro-common/compat/fopen_utf8.c new file mode 100644 index 0000000..52b481e --- /dev/null +++ b/libretroBuildSystem/libretro-common/compat/fopen_utf8.c @@ -0,0 +1,60 @@ +/* Copyright (C) 2010-2018 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (fopen_utf8.c). + * --------------------------------------------------------------------------------------- + * + * Permission is hereby granted, free of charge, + * to any person obtaining a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include + +#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 || defined(_XBOX) +#ifndef LEGACY_WIN32 +#define LEGACY_WIN32 +#endif +#endif + +#ifdef _WIN32 +#undef fopen + +void *fopen_utf8(const char * filename, const char * mode) +{ +#if defined(_XBOX) + return fopen(filename, mode); +#elif defined(LEGACY_WIN32) + FILE *ret = NULL; + char * filename_local = utf8_to_local_string_alloc(filename); + + if (!filename_local) + return NULL; + ret = fopen(filename_local, mode); + if (filename_local) + free(filename_local); + return ret; +#else + wchar_t * filename_w = utf8_to_utf16_string_alloc(filename); + wchar_t * mode_w = utf8_to_utf16_string_alloc(mode); + FILE* ret = _wfopen(filename_w, mode_w); + free(filename_w); + free(mode_w); + return ret; +#endif +} +#endif diff --git a/libretroBuildSystem/libretro-common/include/compat/fopen_utf8.h b/libretroBuildSystem/libretro-common/include/compat/fopen_utf8.h new file mode 100644 index 0000000..f59822a --- /dev/null +++ b/libretroBuildSystem/libretro-common/include/compat/fopen_utf8.h @@ -0,0 +1,34 @@ +/* Copyright (C) 2010-2018 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (fopen_utf8.h). + * --------------------------------------------------------------------------------------- + * + * Permission is hereby granted, free of charge, + * to any person obtaining a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __LIBRETRO_SDK_COMPAT_FOPEN_UTF8_H +#define __LIBRETRO_SDK_COMPAT_FOPEN_UTF8_H + +#ifdef _WIN32 +/* Defined to error rather than fopen_utf8, to make it clear to everyone reading the code that not worrying about utf16 is fine */ +/* TODO: enable */ +/* #define fopen (use fopen_utf8 instead) */ +void *fopen_utf8(const char * filename, const char * mode); +#else +#define fopen_utf8 fopen +#endif +#endif diff --git a/libretroBuildSystem/libretro.h b/libretroBuildSystem/libretro-common/include/libretro.h similarity index 100% rename from libretroBuildSystem/libretro.h rename to libretroBuildSystem/libretro-common/include/libretro.h diff --git a/libretroBuildSystem/libretro-common/include/memmap.h b/libretroBuildSystem/libretro-common/include/memmap.h new file mode 100644 index 0000000..2bedd5c --- /dev/null +++ b/libretroBuildSystem/libretro-common/include/memmap.h @@ -0,0 +1,52 @@ +/* Copyright (C) 2010-2018 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (memmap.h). + * --------------------------------------------------------------------------------------- + * + * Permission is hereby granted, free of charge, + * to any person obtaining a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _LIBRETRO_MEMMAP_H +#define _LIBRETRO_MEMMAP_H + +#include +#include + +#if defined(__CELLOS_LV2__) || defined(PSP) || defined(GEKKO) || defined(VITA) || defined(_XBOX) || defined(_3DS) || defined(WIIU) || defined(SWITCH) +/* No mman available */ +#elif defined(_WIN32) && !defined(_XBOX) +#include +#include +#include +#else +#define HAVE_MMAN +#include +#endif + +#if !defined(HAVE_MMAN) || defined(_WIN32) +void* mmap(void *addr, size_t len, int mmap_prot, int mmap_flags, int fildes, size_t off); + +int munmap(void *addr, size_t len); + +int mprotect(void *addr, size_t len, int prot); +#endif + +int memsync(void *start, void *end); + +int memprotect(void *addr, size_t len); + +#endif diff --git a/libretroBuildSystem/libretro-common/include/streams/file_stream.h b/libretroBuildSystem/libretro-common/include/streams/file_stream.h new file mode 100644 index 0000000..8e83f6d --- /dev/null +++ b/libretroBuildSystem/libretro-common/include/streams/file_stream.h @@ -0,0 +1,111 @@ +/* Copyright (C) 2010-2018 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (file_stream.h). + * --------------------------------------------------------------------------------------- + * + * Permission is hereby granted, free of charge, + * to any person obtaining a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __LIBRETRO_SDK_FILE_STREAM_H +#define __LIBRETRO_SDK_FILE_STREAM_H + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include + +#define FILESTREAM_REQUIRED_VFS_VERSION 2 + +RETRO_BEGIN_DECLS + +typedef struct RFILE RFILE; + +#define FILESTREAM_REQUIRED_VFS_VERSION 2 + +void filestream_vfs_init(const struct retro_vfs_interface_info* vfs_info); + +int64_t filestream_get_size(RFILE *stream); + +int64_t filestream_truncate(RFILE *stream, int64_t length); + +/** + * filestream_open: + * @path : path to file + * @mode : file mode to use when opening (read/write) + * @bufsize : optional buffer size (-1 or 0 to use default) + * + * Opens a file for reading or writing, depending on the requested mode. + * Returns a pointer to an RFILE if opened successfully, otherwise NULL. + **/ +RFILE *filestream_open(const char *path, unsigned mode, unsigned hints); + +int64_t filestream_seek(RFILE *stream, int64_t offset, int seek_position); + +int64_t filestream_read(RFILE *stream, void *data, int64_t len); + +int64_t filestream_write(RFILE *stream, const void *data, int64_t len); + +int64_t filestream_tell(RFILE *stream); + +void filestream_rewind(RFILE *stream); + +int filestream_close(RFILE *stream); + +int64_t filestream_read_file(const char *path, void **buf, int64_t *len); + +char *filestream_gets(RFILE *stream, char *s, size_t len); + +int filestream_getc(RFILE *stream); + +int filestream_scanf(RFILE *stream, const char* format, ...); + +int filestream_eof(RFILE *stream); + +bool filestream_write_file(const char *path, const void *data, int64_t size); + +int filestream_putc(RFILE *stream, int c); + +int filestream_vprintf(RFILE *stream, const char* format, va_list args); + +int filestream_printf(RFILE *stream, const char* format, ...); + +int filestream_error(RFILE *stream); + +int filestream_flush(RFILE *stream); + +int filestream_delete(const char *path); + +int filestream_rename(const char *old_path, const char *new_path); + +const char *filestream_get_path(RFILE *stream); + +bool filestream_exists(const char *path); + +char *filestream_getline(RFILE *stream); + +RETRO_END_DECLS + +#endif diff --git a/libretroBuildSystem/libretro-common/include/vfs/vfs_implementation.h b/libretroBuildSystem/libretro-common/include/vfs/vfs_implementation.h new file mode 100644 index 0000000..5a1e5c3 --- /dev/null +++ b/libretroBuildSystem/libretro-common/include/vfs/vfs_implementation.h @@ -0,0 +1,67 @@ +/* Copyright (C) 2010-2018 The RetroArch team +* +* --------------------------------------------------------------------------------------- +* The following license statement only applies to this file (vfs_implementation.h). +* --------------------------------------------------------------------------------------- +* +* Permission is hereby granted, free of charge, +* to any person obtaining a copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation the rights to +* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, +* and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef __LIBRETRO_SDK_VFS_IMPLEMENTATION_H +#define __LIBRETRO_SDK_VFS_IMPLEMENTATION_H + +#include +#include + +/* Replace the following symbol with something appropriate + * to signify the file is being compiled for a front end instead of a core. + * This allows the same code to act as reference implementation + * for VFS and as fallbacks for when the front end does not provide VFS functionality. + */ + +#ifdef VFS_FRONTEND +typedef struct retro_vfs_file_handle libretro_vfs_implementation_file; +#else +typedef struct libretro_vfs_implementation_file libretro_vfs_implementation_file; +#endif + +libretro_vfs_implementation_file *retro_vfs_file_open_impl(const char *path, unsigned mode, unsigned hints); + +int retro_vfs_file_close_impl(libretro_vfs_implementation_file *stream); + +int retro_vfs_file_error_impl(libretro_vfs_implementation_file *stream); + +int64_t retro_vfs_file_size_impl(libretro_vfs_implementation_file *stream); + +int64_t retro_vfs_file_truncate_impl(libretro_vfs_implementation_file *stream, int64_t length); + +int64_t retro_vfs_file_tell_impl(libretro_vfs_implementation_file *stream); + +int64_t retro_vfs_file_seek_impl(libretro_vfs_implementation_file *stream, int64_t offset, int seek_position); + +int64_t retro_vfs_file_read_impl(libretro_vfs_implementation_file *stream, void *s, uint64_t len); + +int64_t retro_vfs_file_write_impl(libretro_vfs_implementation_file *stream, const void *s, uint64_t len); + +int retro_vfs_file_flush_impl(libretro_vfs_implementation_file *stream); + +int retro_vfs_file_remove_impl(const char *path); + +int retro_vfs_file_rename_impl(const char *old_path, const char *new_path); + +const char *retro_vfs_file_get_path_impl(libretro_vfs_implementation_file *stream); + +#endif diff --git a/libretroBuildSystem/libretro-common/memmap/memmap.c b/libretroBuildSystem/libretro-common/memmap/memmap.c new file mode 100644 index 0000000..26d9ce0 --- /dev/null +++ b/libretroBuildSystem/libretro-common/memmap/memmap.c @@ -0,0 +1,163 @@ +/* Copyright (C) 2010-2018 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (memmap.c). + * --------------------------------------------------------------------------------------- + * + * Permission is hereby granted, free of charge, + * to any person obtaining a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include + +#ifndef PROT_READ +#define PROT_READ 0x1 /* Page can be read */ +#endif + +#ifndef PROT_WRITE +#define PROT_WRITE 0x2 /* Page can be written. */ +#endif + +#ifndef PROT_READWRITE +#define PROT_READWRITE 0x3 /* Page can be written to and read from. */ +#endif + +#ifndef PROT_EXEC +#define PROT_EXEC 0x4 /* Page can be executed. */ +#endif + +#ifndef PROT_NONE +#define PROT_NONE 0x0 /* Page can not be accessed. */ +#endif + +#ifndef MAP_FAILED +#define MAP_FAILED ((void *) -1) +#endif + +#ifdef _WIN32 +void* mmap(void *addr, size_t len, int prot, int flags, + int fildes, size_t offset) +{ + void *map = (void*)NULL; + HANDLE handle = INVALID_HANDLE_VALUE; + + switch (prot) + { + case PROT_READ: + default: + handle = CreateFileMapping((HANDLE) + _get_osfhandle(fildes), 0, PAGE_READONLY, 0, + len, 0); + if (!handle) + break; + map = (void*)MapViewOfFile(handle, FILE_MAP_READ, 0, 0, len); + CloseHandle(handle); + break; + case PROT_WRITE: + handle = CreateFileMapping((HANDLE) + _get_osfhandle(fildes),0,PAGE_READWRITE,0, + len, 0); + if (!handle) + break; + map = (void*)MapViewOfFile(handle, FILE_MAP_WRITE, 0, 0, len); + CloseHandle(handle); + break; + case PROT_READWRITE: + handle = CreateFileMapping((HANDLE) + _get_osfhandle(fildes),0,PAGE_READWRITE,0, + len, 0); + if (!handle) + break; + map = (void*)MapViewOfFile(handle, FILE_MAP_ALL_ACCESS, 0, 0, len); + CloseHandle(handle); + break; + } + + if (map == (void*)NULL) + return((void*)MAP_FAILED); + return((void*) ((int8_t*)map + offset)); +} + +int munmap(void *addr, size_t length) +{ + if (!UnmapViewOfFile(addr)) + return -1; + return 0; +} + +int mprotect(void *addr, size_t len, int prot) +{ + /* Incomplete, just assumes PAGE_EXECUTE_READWRITE right now + * instead of correctly handling prot */ + prot = 0; + if (prot & (PROT_READ | PROT_WRITE | PROT_EXEC)) + prot = PAGE_EXECUTE_READWRITE; + return VirtualProtect(addr, len, prot, 0); +} + +#elif !defined(HAVE_MMAN) +void* mmap(void *addr, size_t len, int prot, int flags, + int fildes, size_t offset) +{ + return malloc(len); +} + +int munmap(void *addr, size_t len) +{ + free(addr); + return 0; +} + +int mprotect(void *addr, size_t len, int prot) +{ + /* stub - not really needed at this point + * since this codepath has no dynarecs. */ + return 0; +} + +#endif + +#if defined(__MACH__) && defined(__arm__) +#include +#endif + +int memsync(void *start, void *end) +{ + size_t len = (char*)end - (char*)start; +#if defined(__MACH__) && defined(__arm__) + sys_dcache_flush(start ,len); + sys_icache_invalidate(start, len); + return 0; +#elif defined(__arm__) && !defined(__QNX__) + (void)len; + __clear_cache(start, end); + return 0; +#elif defined(HAVE_MMAN) + return msync(start, len, MS_SYNC | MS_INVALIDATE +#ifdef __QNX__ + MS_CACHE_ONLY +#endif + ); +#else + (void)len; + return 0; +#endif +} + +int memprotect(void *addr, size_t len) +{ + return mprotect(addr, len, PROT_READ | PROT_WRITE | PROT_EXEC); +} diff --git a/libretroBuildSystem/libretro-common/streams/file_stream.c b/libretroBuildSystem/libretro-common/streams/file_stream.c new file mode 100644 index 0000000..b24102f --- /dev/null +++ b/libretroBuildSystem/libretro-common/streams/file_stream.c @@ -0,0 +1,613 @@ +/* Copyright (C) 2010-2018 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (file_stream.c). + * --------------------------------------------------------------------------------------- + * + * Permission is hereby granted, free of charge, + * to any person obtaining a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#define VFS_FRONTEND +#include + +static const int64_t vfs_error_return_value = -1; + +static retro_vfs_get_path_t filestream_get_path_cb = NULL; +static retro_vfs_open_t filestream_open_cb = NULL; +static retro_vfs_close_t filestream_close_cb = NULL; +static retro_vfs_size_t filestream_size_cb = NULL; +static retro_vfs_truncate_t filestream_truncate_cb = NULL; +static retro_vfs_tell_t filestream_tell_cb = NULL; +static retro_vfs_seek_t filestream_seek_cb = NULL; +static retro_vfs_read_t filestream_read_cb = NULL; +static retro_vfs_write_t filestream_write_cb = NULL; +static retro_vfs_flush_t filestream_flush_cb = NULL; +static retro_vfs_remove_t filestream_remove_cb = NULL; +static retro_vfs_rename_t filestream_rename_cb = NULL; + +struct RFILE +{ + struct retro_vfs_file_handle *hfile; + bool error_flag; + bool eof_flag; +}; + +/* VFS Initialization */ + +void filestream_vfs_init(const struct retro_vfs_interface_info* vfs_info) +{ + const struct retro_vfs_interface* vfs_iface; + + filestream_get_path_cb = NULL; + filestream_open_cb = NULL; + filestream_close_cb = NULL; + filestream_tell_cb = NULL; + filestream_size_cb = NULL; + filestream_truncate_cb = NULL; + filestream_seek_cb = NULL; + filestream_read_cb = NULL; + filestream_write_cb = NULL; + filestream_flush_cb = NULL; + filestream_remove_cb = NULL; + filestream_rename_cb = NULL; + + vfs_iface = vfs_info->iface; + + if (vfs_info->required_interface_version < FILESTREAM_REQUIRED_VFS_VERSION + || !vfs_iface) + return; + + filestream_get_path_cb = vfs_iface->get_path; + filestream_open_cb = vfs_iface->open; + filestream_close_cb = vfs_iface->close; + filestream_size_cb = vfs_iface->size; + filestream_truncate_cb = vfs_iface->truncate; + filestream_tell_cb = vfs_iface->tell; + filestream_seek_cb = vfs_iface->seek; + filestream_read_cb = vfs_iface->read; + filestream_write_cb = vfs_iface->write; + filestream_flush_cb = vfs_iface->flush; + filestream_remove_cb = vfs_iface->remove; + filestream_rename_cb = vfs_iface->rename; +} + +/* Callback wrappers */ +bool filestream_exists(const char *path) +{ + RFILE *dummy = NULL; + + if (!path || !*path) + return false; + + dummy = filestream_open(path, + RETRO_VFS_FILE_ACCESS_READ, + RETRO_VFS_FILE_ACCESS_HINT_NONE); + + if (!dummy) + return false; + + filestream_close(dummy); + return true; +} + +int64_t filestream_get_size(RFILE *stream) +{ + int64_t output; + + if (filestream_size_cb != NULL) + output = filestream_size_cb(stream->hfile); + else + output = retro_vfs_file_size_impl((libretro_vfs_implementation_file*)stream->hfile); + + if (output == vfs_error_return_value) + stream->error_flag = true; + + return output; +} + +int64_t filestream_truncate(RFILE *stream, int64_t length) +{ + int64_t output; + + if (filestream_truncate_cb != NULL) + output = filestream_truncate_cb(stream->hfile, length); + else + output = retro_vfs_file_truncate_impl((libretro_vfs_implementation_file*)stream->hfile, length); + + if (output == vfs_error_return_value) + stream->error_flag = true; + + return output; +} + +/** + * filestream_open: + * @path : path to file + * @mode : file mode to use when opening (read/write) + * @hints : + * + * Opens a file for reading or writing, depending on the requested mode. + * Returns a pointer to an RFILE if opened successfully, otherwise NULL. + **/ +RFILE *filestream_open(const char *path, unsigned mode, unsigned hints) +{ + struct retro_vfs_file_handle *fp = NULL; + RFILE* output = NULL; + + if (filestream_open_cb != NULL) + fp = (struct retro_vfs_file_handle*) + filestream_open_cb(path, mode, hints); + else + fp = (struct retro_vfs_file_handle*) + retro_vfs_file_open_impl(path, mode, hints); + + if (!fp) + return NULL; + + output = (RFILE*)malloc(sizeof(RFILE)); + output->error_flag = false; + output->eof_flag = false; + output->hfile = fp; + return output; +} + +char *filestream_gets(RFILE *stream, char *s, size_t len) +{ + int c = 0; + char *p = s; + if (!stream) + return NULL; + + /* get max bytes or up to a newline */ + + for (len--; len > 0; len--) + { + if ((c = filestream_getc(stream)) == EOF) + break; + *p++ = c; + if (c == '\n') + break; + } + *p = 0; + + if (p == s && c == EOF) + return NULL; + return (s); +} + +int filestream_getc(RFILE *stream) +{ + char c = 0; + if (!stream) + return 0; + if(filestream_read(stream, &c, 1) == 1) + return (int)c; + return EOF; +} + +int filestream_scanf(RFILE *stream, const char* format, ...) +{ + char buf[4096]; + char subfmt[64]; + va_list args; + + const char * bufiter = buf; + int64_t startpos = filestream_tell(stream); + int ret = 0; + int64_t maxlen = filestream_read(stream, buf, sizeof(buf)-1); + + buf[maxlen] = '\0'; + + va_start(args, format); + + while (*format) + { + if (*format == '%') + { + int sublen; + + char* subfmtiter = subfmt; + bool asterisk = false; + + *subfmtiter++ = *format++; /* '%' */ + + /* %[*][width][length]specifier */ + + if (*format == '*') + { + asterisk = true; + *subfmtiter++ = *format++; + } + + while (isdigit(*format)) *subfmtiter++ = *format++; /* width */ + + /* length */ + if (*format == 'h' || *format == 'l') + { + if (format[1] == format[0]) *subfmtiter++ = *format++; + *subfmtiter++ = *format++; + } + else if (*format == 'j' || *format == 'z' || *format == 't' || *format == 'L') + { + *subfmtiter++ = *format++; + } + + /* specifier - always a single character (except ]) */ + if (*format == '[') + { + while (*format != ']') *subfmtiter++ = *format++; + *subfmtiter++ = *format++; + } + else *subfmtiter++ = *format++; + + *subfmtiter++ = '%'; + *subfmtiter++ = 'n'; + *subfmtiter++ = '\0'; + + if (sizeof(void*) != sizeof(long*)) abort(); /* all pointers must have the same size */ + if (asterisk) + { + if (sscanf(bufiter, subfmt, &sublen) != 0) break; + } + else + { + if (sscanf(bufiter, subfmt, va_arg(args, void*), &sublen) != 1) break; + } + + ret++; + bufiter += sublen; + } + else if (isspace(*format)) + { + while (isspace(*bufiter)) bufiter++; + format++; + } + else + { + if (*bufiter != *format) + break; + bufiter++; + format++; + } + } + + va_end(args); + filestream_seek(stream, startpos+(bufiter-buf), RETRO_VFS_SEEK_POSITION_START); + + return ret; +} + +int64_t filestream_seek(RFILE *stream, int64_t offset, int seek_position) +{ + int64_t output; + + if (filestream_seek_cb != NULL) + output = filestream_seek_cb(stream->hfile, offset, seek_position); + else + output = retro_vfs_file_seek_impl((libretro_vfs_implementation_file*)stream->hfile, offset, seek_position); + + if (output == vfs_error_return_value) + stream->error_flag = true; + stream->eof_flag = false; + + return output; +} + +int filestream_eof(RFILE *stream) +{ + return stream->eof_flag; +} + + +int64_t filestream_tell(RFILE *stream) +{ + int64_t output; + + if (filestream_size_cb != NULL) + output = filestream_tell_cb(stream->hfile); + else + output = retro_vfs_file_tell_impl((libretro_vfs_implementation_file*)stream->hfile); + + if (output == vfs_error_return_value) + stream->error_flag = true; + + return output; +} + +void filestream_rewind(RFILE *stream) +{ + if (!stream) + return; + filestream_seek(stream, 0L, RETRO_VFS_SEEK_POSITION_START); + stream->error_flag = false; + stream->eof_flag = false; +} + +int64_t filestream_read(RFILE *stream, void *s, int64_t len) +{ + int64_t output; + + if (filestream_read_cb != NULL) + output = filestream_read_cb(stream->hfile, s, len); + else + output = retro_vfs_file_read_impl( + (libretro_vfs_implementation_file*)stream->hfile, s, len); + + if (output == vfs_error_return_value) + stream->error_flag = true; + if (output < len) + stream->eof_flag = true; + + return output; +} + +int filestream_flush(RFILE *stream) +{ + int output; + + if (filestream_flush_cb != NULL) + output = filestream_flush_cb(stream->hfile); + else + output = retro_vfs_file_flush_impl((libretro_vfs_implementation_file*)stream->hfile); + + if (output == vfs_error_return_value) + stream->error_flag = true; + + return output; +} + +int filestream_delete(const char *path) +{ + if (filestream_remove_cb != NULL) + return filestream_remove_cb(path); + + return retro_vfs_file_remove_impl(path); +} + +int filestream_rename(const char *old_path, const char *new_path) +{ + if (filestream_rename_cb != NULL) + return filestream_rename_cb(old_path, new_path); + + return retro_vfs_file_rename_impl(old_path, new_path); +} + +const char *filestream_get_path(RFILE *stream) +{ + if (filestream_get_path_cb != NULL) + return filestream_get_path_cb(stream->hfile); + + return retro_vfs_file_get_path_impl((libretro_vfs_implementation_file*)stream->hfile); +} + +int64_t filestream_write(RFILE *stream, const void *s, int64_t len) +{ + int64_t output; + + if (filestream_write_cb != NULL) + output = filestream_write_cb(stream->hfile, s, len); + else + output = retro_vfs_file_write_impl((libretro_vfs_implementation_file*)stream->hfile, s, len); + + if (output == vfs_error_return_value) + stream->error_flag = true; + + return output; +} + +int filestream_putc(RFILE *stream, int c) +{ + char c_char = (char)c; + if (!stream) + return EOF; + return filestream_write(stream, &c_char, 1)==1 ? c : EOF; +} + +int filestream_vprintf(RFILE *stream, const char* format, va_list args) +{ + static char buffer[8 * 1024]; + int64_t num_chars = vsprintf(buffer, format, args); + + if (num_chars < 0) + return -1; + else if (num_chars == 0) + return 0; + + return (int)filestream_write(stream, buffer, num_chars); +} + +int filestream_printf(RFILE *stream, const char* format, ...) +{ + va_list vl; + int result; + va_start(vl, format); + result = filestream_vprintf(stream, format, vl); + va_end(vl); + return result; +} + +int filestream_error(RFILE *stream) +{ + if (stream && stream->error_flag) + return 1; + return 0; +} + +int filestream_close(RFILE *stream) +{ + int output; + struct retro_vfs_file_handle* fp = stream->hfile; + + if (filestream_close_cb != NULL) + output = filestream_close_cb(fp); + else + output = retro_vfs_file_close_impl((libretro_vfs_implementation_file*)fp); + + if (output == 0) + free(stream); + + return output; +} + +/** + * filestream_read_file: + * @path : path to file. + * @buf : buffer to allocate and read the contents of the + * file into. Needs to be freed manually. + * + * Read the contents of a file into @buf. + * + * Returns: 1 on success, 0 on failure + * In the error case, the dereferenced buf is set to NULL and the len is set to -1. + */ +int64_t filestream_read_file(const char *path, void **buf, int64_t *len) +{ + int64_t ret = 0; + int64_t content_buf_size = 0; + void *content_buf = NULL; + RFILE *file = filestream_open(path, + RETRO_VFS_FILE_ACCESS_READ, + RETRO_VFS_FILE_ACCESS_HINT_NONE); + + if (!file) + { + fprintf(stderr, "Failed to open %s: %s\n", path, strerror(errno)); + goto error; + } + + content_buf_size = filestream_get_size(file); + + if (content_buf_size < 0) + goto error; + + content_buf = malloc((size_t)(content_buf_size + 1)); + + if (!content_buf) + goto error; + if ((int64_t)(uint64_t)(content_buf_size + 1) != (content_buf_size + 1)) + goto error; + + ret = filestream_read(file, content_buf, (int64_t)content_buf_size); + if (ret < 0) + { + fprintf(stderr, "Failed to read %s: %s\n", path, strerror(errno)); + goto error; + } + + filestream_close(file); + + *buf = content_buf; + + /* Allow for easy reading of strings to be safe. + * Will only work with sane character formatting (Unix). */ + ((char*)content_buf)[ret] = '\0'; + + if (len) + *len = ret; + + return 1; + +error: + if (file) + filestream_close(file); + if (content_buf) + free(content_buf); + if (len) + *len = -1; + *buf = NULL; + return 0; +} + +/** + * filestream_write_file: + * @path : path to file. + * @data : contents to write to the file. + * @size : size of the contents. + * + * Writes data to a file. + * + * Returns: true (1) on success, false (0) otherwise. + */ +bool filestream_write_file(const char *path, const void *data, int64_t size) +{ + int64_t ret = 0; + RFILE *file = filestream_open(path, + RETRO_VFS_FILE_ACCESS_WRITE, + RETRO_VFS_FILE_ACCESS_HINT_NONE); + if (!file) + return false; + + ret = filestream_write(file, data, size); + filestream_close(file); + + if (ret != size) + return false; + + return true; +} + +char *filestream_getline(RFILE *stream) +{ + char* newline_tmp = NULL; + size_t cur_size = 8; + size_t idx = 0; + int in = 0; + char* newline = (char*)malloc(9); + + if (!stream || !newline) + { + if (newline) + free(newline); + return NULL; + } + + in = filestream_getc(stream); + + while (in != EOF && in != '\n') + { + if (idx == cur_size) + { + cur_size *= 2; + newline_tmp = (char*)realloc(newline, cur_size + 1); + + if (!newline_tmp) + { + free(newline); + return NULL; + } + + newline = newline_tmp; + } + + newline[idx++] = in; + in = filestream_getc(stream); + } + + newline[idx] = '\0'; + return newline; +} diff --git a/libretroBuildSystem/libretro-common/vfs/vfs_implementation.c b/libretroBuildSystem/libretro-common/vfs/vfs_implementation.c new file mode 100644 index 0000000..3443381 --- /dev/null +++ b/libretroBuildSystem/libretro-common/vfs/vfs_implementation.c @@ -0,0 +1,610 @@ +/* Copyright (C) 2010-2018 The RetroArch team +* +* --------------------------------------------------------------------------------------- +* The following license statement only applies to this file (vfs_implementation.c). +* --------------------------------------------------------------------------------------- +* +* Permission is hereby granted, free of charge, +* to any person obtaining a copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation the rights to +* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, +* and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include +#include +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if defined(_WIN32) +# ifdef _MSC_VER +# define setmode _setmode +# endif +# ifdef _XBOX +# include +# define INVALID_FILE_ATTRIBUTES -1 +# else +# include +# include +# include +# include +# endif +#else +# if defined(PSP) +# include +# endif +# include +# include +# if !defined(VITA) +# include +# endif +# include +#endif + +#ifdef __CELLOS_LV2__ +#include +#define O_RDONLY CELL_FS_O_RDONLY +#define O_WRONLY CELL_FS_O_WRONLY +#define O_CREAT CELL_FS_O_CREAT +#define O_TRUNC CELL_FS_O_TRUNC +#define O_RDWR CELL_FS_O_RDWR +#else +#include +#endif + +/* Assume W-functions do not work below Win2K and Xbox platforms */ +#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 || defined(_XBOX) + +#ifndef LEGACY_WIN32 +#define LEGACY_WIN32 +#endif + +#endif + +#if defined(_WIN32) && !defined(_XBOX) +#if !defined(_MSC_VER) || (defined(_MSC_VER) && _MSC_VER >= 1400) +#define ATLEAST_VC2005 +#endif +#endif + +#ifdef RARCH_INTERNAL +#ifndef VFS_FRONTEND +#define VFS_FRONTEND +#endif +#endif + +#include +#include +#include +#include +#include + +#define RFILE_HINT_UNBUFFERED (1 << 8) + +#ifdef VFS_FRONTEND +struct retro_vfs_file_handle +#else +struct libretro_vfs_implementation_file +#endif +{ + int fd; + unsigned hints; + int64_t size; + char *buf; + FILE *fp; + char* orig_path; +#if defined(HAVE_MMAP) + uint64_t mappos; + uint64_t mapsize; + uint8_t *mapped; +#endif +}; + +int64_t retro_vfs_file_seek_internal(libretro_vfs_implementation_file *stream, int64_t offset, int whence) +{ + if (!stream) + goto error; + + if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0) +/* VC2005 and up have a special 64-bit fseek */ +#ifdef ATLEAST_VC2005 + return _fseeki64(stream->fp, offset, whence); +#elif defined(__CELLOS_LV2__) || defined(_MSC_VER) && _MSC_VER <= 1310 + return fseek(stream->fp, (long)offset, whence); +#else + return fseeko(stream->fp, (off_t)offset, whence); +#endif + +#ifdef HAVE_MMAP + /* Need to check stream->mapped because this function is + * called in filestream_open() */ + if (stream->mapped && stream->hints & + RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS) + { + /* fseek() returns error on under/overflow but + * allows cursor > EOF for + read-only file descriptors. */ + switch (whence) + { + case SEEK_SET: + if (offset < 0) + goto error; + + stream->mappos = offset; + break; + + case SEEK_CUR: + if ((offset < 0 && stream->mappos + offset > stream->mappos) || + (offset > 0 && stream->mappos + offset < stream->mappos)) + goto error; + + stream->mappos += offset; + break; + + case SEEK_END: + if (stream->mapsize + offset < stream->mapsize) + goto error; + + stream->mappos = stream->mapsize + offset; + break; + } + return stream->mappos; + } +#endif + + if (lseek(stream->fd, offset, whence) < 0) + goto error; + + return 0; + +error: + return -1; +} + +/** + * retro_vfs_file_open_impl: + * @path : path to file + * @mode : file mode to use when opening (read/write) + * @hints : + * + * Opens a file for reading or writing, depending on the requested mode. + * Returns a pointer to an RFILE if opened successfully, otherwise NULL. + **/ + +libretro_vfs_implementation_file *retro_vfs_file_open_impl(const char *path, unsigned mode, unsigned hints) +{ + int flags = 0; + const char *mode_str = NULL; + libretro_vfs_implementation_file *stream = (libretro_vfs_implementation_file*)calloc(1, sizeof(*stream)); + +#ifdef VFS_FRONTEND + const char *dumb_prefix = "vfsonly://"; + int dumb_prefix_len = (int)strlen(dumb_prefix); + int path_len = (int)strlen(path); + + if (path_len >= dumb_prefix_len) + if (!memcmp(path, dumb_prefix, dumb_prefix_len)) + path += strlen(dumb_prefix); +#endif + + if (!stream) + return NULL; + + (void)flags; + + stream->hints = hints; + stream->orig_path = strdup(path); + +#ifdef HAVE_MMAP + if (stream->hints & RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS && mode == RETRO_VFS_FILE_ACCESS_READ) + stream->hints |= RFILE_HINT_UNBUFFERED; + else +#endif + stream->hints &= ~RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS; + + switch (mode) + { + case RETRO_VFS_FILE_ACCESS_READ: + mode_str = "rb"; + + flags = O_RDONLY; +#ifdef _WIN32 + flags |= O_BINARY; +#endif + break; + + case RETRO_VFS_FILE_ACCESS_WRITE: + mode_str = "wb"; + + flags = O_WRONLY | O_CREAT | O_TRUNC; +#ifndef _WIN32 + flags |= S_IRUSR | S_IWUSR; +#else + flags |= O_BINARY; +#endif + break; + + case RETRO_VFS_FILE_ACCESS_READ_WRITE: + mode_str = "w+b"; + + flags = O_RDWR | O_CREAT | O_TRUNC; +#ifndef _WIN32 + flags |= S_IRUSR | S_IWUSR; +#else + flags |= O_BINARY; +#endif + break; + + case RETRO_VFS_FILE_ACCESS_WRITE | RETRO_VFS_FILE_ACCESS_UPDATE_EXISTING: + case RETRO_VFS_FILE_ACCESS_READ_WRITE | RETRO_VFS_FILE_ACCESS_UPDATE_EXISTING: + mode_str = "r+b"; + + flags = O_RDWR; +#ifndef _WIN32 + flags |= S_IRUSR | S_IWUSR; +#else + flags |= O_BINARY; +#endif + break; + + default: + goto error; + } + + if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0) + { + FILE *fp = (FILE*)fopen_utf8(path, mode_str); + + if (!fp) + goto error; + + /* Regarding setvbuf: + * + * https://www.freebsd.org/cgi/man.cgi?query=setvbuf&apropos=0&sektion=0&manpath=FreeBSD+11.1-RELEASE&arch=default&format=html + * + * If the size argument is not zero but buf is NULL, a buffer of the given size will be allocated immediately, and + * released on close. This is an extension to ANSI C. + * + * Since C89 does not support specifying a null buffer with a non-zero size, we create and track our own buffer for it. + */ + /* TODO: this is only useful for a few platforms, find which and add ifdef */ + stream->fp = fp; + stream->buf = (char*)calloc(1, 0x4000); + setvbuf(stream->fp, stream->buf, _IOFBF, 0x4000); + } + else + { +#if defined(_WIN32) && !defined(_XBOX) +#if defined(LEGACY_WIN32) + char *path_local = utf8_to_local_string_alloc(path); + stream->fd = open(path_local, flags, 0); + if (path_local) + free(path_local); +#else + wchar_t * path_wide = utf8_to_utf16_string_alloc(path); + stream->fd = _wopen(path_wide, flags, 0); + if (path_wide) + free(path_wide); +#endif +#else + stream->fd = open(path, flags, 0); +#endif + + if (stream->fd == -1) + goto error; + +#ifdef HAVE_MMAP + if (stream->hints & RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS) + { + stream->mappos = 0; + stream->mapped = NULL; + stream->mapsize = retro_vfs_file_seek_internal(stream, 0, SEEK_END); + + if (stream->mapsize == (uint64_t)-1) + goto error; + + retro_vfs_file_seek_internal(stream, 0, SEEK_SET); + + stream->mapped = (uint8_t*)mmap((void*)0, + stream->mapsize, PROT_READ, MAP_SHARED, stream->fd, 0); + + if (stream->mapped == MAP_FAILED) + stream->hints &= ~RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS; + } +#endif + } + + retro_vfs_file_seek_internal(stream, 0, SEEK_SET); + retro_vfs_file_seek_internal(stream, 0, SEEK_END); + + stream->size = retro_vfs_file_tell_impl(stream); + + retro_vfs_file_seek_internal(stream, 0, SEEK_SET); + + return stream; + +error: + retro_vfs_file_close_impl(stream); + return NULL; +} + +int retro_vfs_file_close_impl(libretro_vfs_implementation_file *stream) +{ + if (!stream) + return -1; + + if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0) + { + if (stream->fp) + fclose(stream->fp); + } + else + { +#ifdef HAVE_MMAP + if (stream->hints & RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS) + munmap(stream->mapped, stream->mapsize); +#endif + } + + if (stream->fd > 0) + close(stream->fd); + if (stream->buf) + free(stream->buf); + if (stream->orig_path) + free(stream->orig_path); + free(stream); + + return 0; +} + +int retro_vfs_file_error_impl(libretro_vfs_implementation_file *stream) +{ + return ferror(stream->fp); +} + +int64_t retro_vfs_file_size_impl(libretro_vfs_implementation_file *stream) +{ + if (!stream) + return 0; + return stream->size; +} + +int64_t retro_vfs_file_truncate_impl(libretro_vfs_implementation_file *stream, int64_t length) +{ + if (!stream) + return -1; + +#ifdef _WIN32 + if(_chsize(_fileno(stream->fp), length) != 0) + return -1; +#elif !defined(VITA) && !defined(PSP) && (!defined(SWITCH) || defined(HAVE_LIBNX)) + if(ftruncate(fileno(stream->fp), length) != 0) + return -1; +#endif + + return 0; +} + +int64_t retro_vfs_file_tell_impl(libretro_vfs_implementation_file *stream) +{ + if (!stream) + return -1; + + if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0) +/* VC2005 and up have a special 64-bit ftell */ +#ifdef ATLEAST_VC2005 + return _ftelli64(stream->fp); +#else + return ftell(stream->fp); +#endif + +#ifdef HAVE_MMAP + /* Need to check stream->mapped because this function + * is called in filestream_open() */ + if (stream->mapped && stream->hints & RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS) + return stream->mappos; +#endif + if (lseek(stream->fd, 0, SEEK_CUR) < 0) + return -1; + + return 0; +} + +int64_t retro_vfs_file_seek_impl(libretro_vfs_implementation_file *stream, int64_t offset, int seek_position) +{ + int whence = -1; + switch (seek_position) + { + case RETRO_VFS_SEEK_POSITION_START: + whence = SEEK_SET; + break; + case RETRO_VFS_SEEK_POSITION_CURRENT: + whence = SEEK_CUR; + break; + case RETRO_VFS_SEEK_POSITION_END: + whence = SEEK_END; + break; + } + + return retro_vfs_file_seek_internal(stream, offset, whence); +} + +int64_t retro_vfs_file_read_impl(libretro_vfs_implementation_file *stream, void *s, uint64_t len) +{ + if (!stream || !s) + goto error; + + if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0) + return fread(s, 1, (size_t)len, stream->fp); + +#ifdef HAVE_MMAP + if (stream->hints & RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS) + { + if (stream->mappos > stream->mapsize) + goto error; + + if (stream->mappos + len > stream->mapsize) + len = stream->mapsize - stream->mappos; + + memcpy(s, &stream->mapped[stream->mappos], len); + stream->mappos += len; + + return len; + } +#endif + + return read(stream->fd, s, (size_t)len); + +error: + return -1; +} + +int64_t retro_vfs_file_write_impl(libretro_vfs_implementation_file *stream, const void *s, uint64_t len) +{ + if (!stream) + goto error; + + if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0) + return fwrite(s, 1, (size_t)len, stream->fp); + +#ifdef HAVE_MMAP + if (stream->hints & RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS) + goto error; +#endif + return write(stream->fd, s, (size_t)len); + +error: + return -1; +} + +int retro_vfs_file_flush_impl(libretro_vfs_implementation_file *stream) +{ + if (!stream) + return -1; + return fflush(stream->fp)==0 ? 0 : -1; +} + +int retro_vfs_file_remove_impl(const char *path) +{ + char *path_local = NULL; + wchar_t *path_wide = NULL; + + if (!path || !*path) + return -1; + + (void)path_local; + (void)path_wide; + +#if defined(_WIN32) && !defined(_XBOX) +#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 + path_local = utf8_to_local_string_alloc(path); + + if (path_local) + { + int ret = remove(path_local); + free(path_local); + + if (ret == 0) + return 0; + } +#else + path_wide = utf8_to_utf16_string_alloc(path); + + if (path_wide) + { + int ret = _wremove(path_wide); + free(path_wide); + + if (ret == 0) + return 0; + } +#endif +#else + if (remove(path) == 0) + return 0; +#endif + return -1; +} + +int retro_vfs_file_rename_impl(const char *old_path, const char *new_path) +{ + char *old_path_local = NULL; + char *new_path_local = NULL; + wchar_t *old_path_wide = NULL; + wchar_t *new_path_wide = NULL; + + if (!old_path || !*old_path || !new_path || !*new_path) + return -1; + + (void)old_path_local; + (void)new_path_local; + (void)old_path_wide; + (void)new_path_wide; + +#if defined(_WIN32) && !defined(_XBOX) +#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 + old_path_local = utf8_to_local_string_alloc(old_path); + new_path_local = utf8_to_local_string_alloc(new_path); + + if (old_path_local) + { + if (new_path_local) + { + int ret = rename(old_path_local, new_path_local); + free(old_path_local); + free(new_path_local); + return ret==0 ? 0 : -1; + } + + free(old_path_local); + } + + if (new_path_local) + free(new_path_local); +#else + old_path_wide = utf8_to_utf16_string_alloc(old_path); + new_path_wide = utf8_to_utf16_string_alloc(new_path); + + if (old_path_wide) + { + if (new_path_wide) + { + int ret = _wrename(old_path_wide, new_path_wide); + free(old_path_wide); + free(new_path_wide); + return ret==0 ? 0 : -1; + } + + free(old_path_wide); + } + + if (new_path_wide) + free(new_path_wide); +#endif + return -1; +#else + return rename(old_path, new_path)==0 ? 0 : -1; +#endif +} + +const char *retro_vfs_file_get_path_impl(libretro_vfs_implementation_file *stream) +{ + /* should never happen, do something noisy so caller can be fixed */ + if (!stream) + abort(); + return stream->orig_path; +} diff --git a/libretroBuildSystem/libretro.c b/libretroBuildSystem/libretro.c index 72e09e1..dc1c0e8 100644 --- a/libretroBuildSystem/libretro.c +++ b/libretroBuildSystem/libretro.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "../src/emulator.h" #include "../src/portability.h" @@ -27,7 +28,6 @@ static retro_audio_sample_batch_t audio_cb; static retro_environment_t environ_cb; static retro_input_poll_t input_poll_cb; static retro_input_state_t input_state_cb; -static struct retro_vfs_interface* vfs_interface; static bool screenHires; static uint16_t screenWidth; @@ -184,8 +184,8 @@ void retro_set_environment(retro_environment_t cb){ else log_cb = fallback_log; - environ_cb(RETRO_ENVIRONMENT_GET_VFS_INTERFACE, &vfs_getter); - vfs_interface = vfs_getter.iface; + if(environ_cb(RETRO_ENVIRONMENT_GET_VFS_INTERFACE, &vfs_getter)) + filestream_vfs_init(&vfs_getter); struct retro_variable vars[] = { { "palm_emu_feature_ram_huge", "Extra RAM Hack; disabled|enabled" }, @@ -309,8 +309,8 @@ bool retro_load_game(const struct retro_game_info *info){ buffer_t bootloader; char bootloaderPath[PATH_MAX]; char saveRamPath[PATH_MAX]; - struct retro_vfs_file_handle* bootloaderFile; - struct retro_vfs_file_handle* saveRamFile; + struct RFILE* bootloaderFile; + struct RFILE* saveRamFile; const char* systemDir; const char* saveDir; time_t rawTime; @@ -328,17 +328,17 @@ bool retro_load_game(const struct retro_game_info *info){ //bootloader strlcpy(bootloaderPath, systemDir, PATH_MAX); strlcat(bootloaderPath, "/bootloader-en-m515.rom", PATH_MAX); - bootloaderFile = vfs_interface->open(bootloaderPath, RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE); + bootloaderFile = filestream_open(bootloaderPath, RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE); if(bootloaderFile){ - bootloader.size = vfs_interface->size(bootloaderFile); + bootloader.size = filestream_get_size(bootloaderFile); bootloader.data = malloc(bootloader.size); if(bootloader.data) - vfs_interface->read(bootloaderFile, bootloader.data, bootloader.size); + filestream_read(bootloaderFile, bootloader.data, bootloader.size); else bootloader.size = 0; - vfs_interface->close(bootloaderFile); + filestream_close(bootloaderFile); } else{ bootloader.data = NULL; @@ -358,14 +358,14 @@ bool retro_load_game(const struct retro_game_info *info){ //save RAM strlcpy(saveRamPath, saveDir, PATH_MAX); strlcat(saveRamPath, "/userdata-en-m515.ram", PATH_MAX); - saveRamFile = vfs_interface->open(saveRamPath, RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE); + saveRamFile = filestream_open(saveRamPath, RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE); if(saveRamFile){ - if(vfs_interface->size(saveRamFile) == emulatorGetRamSize()){ - vfs_interface->read(saveRamFile, palmRam, emulatorGetRamSize()); + if(filestream_get_size(saveRamFile) == emulatorGetRamSize()){ + filestream_read(saveRamFile, palmRam, emulatorGetRamSize()); swap16_buffer_if_little(palmRam, emulatorGetRamSize() / sizeof(uint16_t)); } - vfs_interface->close(saveRamFile); + filestream_close(saveRamFile); } //set RTC @@ -385,19 +385,19 @@ bool retro_load_game(const struct retro_game_info *info){ void retro_unload_game(void){ const char* saveDir; char saveRamPath[PATH_MAX]; - struct retro_vfs_file_handle* saveRamFile; + struct RFILE* saveRamFile; environ_cb(RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY, &saveDir); //save RAM strlcpy(saveRamPath, saveDir, PATH_MAX); strlcat(saveRamPath, "/userdata-en-m515.ram", PATH_MAX); - saveRamFile = vfs_interface->open(saveRamPath, RETRO_VFS_FILE_ACCESS_WRITE, RETRO_VFS_FILE_ACCESS_HINT_NONE); + saveRamFile = filestream_open(saveRamPath, RETRO_VFS_FILE_ACCESS_WRITE, RETRO_VFS_FILE_ACCESS_HINT_NONE); if(saveRamFile){ swap16_buffer_if_little(palmRam, emulatorGetRamSize() / sizeof(uint16_t));//this will no longer be used, so its ok to destroy it when swapping - vfs_interface->write(saveRamFile, palmRam, emulatorGetRamSize()); - vfs_interface->close(saveRamFile); + filestream_write(saveRamFile, palmRam, emulatorGetRamSize()); + filestream_close(saveRamFile); } emulatorExit();